sound/soc/intel/avs/cldma.c

Source file repositories/reference/linux-study-clean/sound/soc/intel/avs/cldma.c

File Facts

System
Linux kernel
Corpus path
sound/soc/intel/avs/cldma.c
Extension
.c
Size
7406 bytes
Lines
291
Domain
Driver Families
Bucket
sound/soc
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct hda_cldma {
	struct device *dev;
	struct hdac_bus *bus;
	void __iomem *dsp_ba;

	unsigned int buffer_size;
	unsigned int num_periods;
	unsigned char stream_tag;
	void __iomem *sd_addr;

	struct snd_dma_buffer dmab_data;
	struct snd_dma_buffer dmab_bdl;
	struct delayed_work memcpy_work;
	struct completion completion;

	/* runtime */
	void *position;
	unsigned int remaining;
	unsigned int sd_status;
};

static void cldma_memcpy_work(struct work_struct *work);

struct hda_cldma code_loader = {
	.stream_tag	= AVS_CL_STREAM_INDEX + 1,
	.memcpy_work	= __DELAYED_WORK_INITIALIZER(code_loader.memcpy_work, cldma_memcpy_work, 0),
	.completion	= COMPLETION_INITIALIZER(code_loader.completion),
};

void hda_cldma_fill(struct hda_cldma *cl)
{
	unsigned int size, offset;

	if (cl->remaining > cl->buffer_size)
		size = cl->buffer_size;
	else
		size = cl->remaining;

	offset = snd_hdac_stream_readl(cl, CL_SD_SPIB);
	if (offset + size > cl->buffer_size) {
		unsigned int ss;

		ss = cl->buffer_size - offset;
		memcpy(cl->dmab_data.area + offset, cl->position, ss);
		offset = 0;
		size -= ss;
		cl->position += ss;
		cl->remaining -= ss;
	}

	memcpy(cl->dmab_data.area + offset, cl->position, size);
	cl->position += size;
	cl->remaining -= size;

	snd_hdac_stream_writel(cl, CL_SD_SPIB, offset + size);
}

static void cldma_memcpy_work(struct work_struct *work)
{
	struct hda_cldma *cl = container_of(work, struct hda_cldma, memcpy_work.work);
	int ret;

	ret = hda_cldma_start(cl);
	if (ret < 0) {
		dev_err(cl->dev, "cldma set RUN failed: %d\n", ret);
		return;
	}

	while (true) {
		ret = wait_for_completion_timeout(&cl->completion,
						  msecs_to_jiffies(AVS_CL_IOC_TIMEOUT_MS));
		if (!ret) {
			dev_err(cl->dev, "cldma IOC timeout\n");
			break;
		}

		if (!(cl->sd_status & SD_INT_COMPLETE)) {
			dev_err(cl->dev, "cldma transfer error, SD status: 0x%08x\n",
				cl->sd_status);
			break;
		}

		if (!cl->remaining)
			break;

		reinit_completion(&cl->completion);
		hda_cldma_fill(cl);
		/* enable CLDMA interrupt */
		snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA,
				      AVS_ADSP_ADSPIC_CLDMA);

Annotation

Implementation Notes