sound/soc/sof/intel/hda-stream.c

Source file repositories/reference/linux-study-clean/sound/soc/sof/intel/hda-stream.c

File Facts

System
Linux kernel
Corpus path
sound/soc/sof/intel/hda-stream.c
Extension
.c
Size
39109 bytes
Lines
1366
Domain
Driver Families
Bucket
sound/soc
Inferred role
Driver Families: exported/initcall integration point
Status
integration 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

if (hstream->frags >= HDA_DSP_MAX_BDL_ENTRIES) {
			dev_err(sdev->dev, "error: stream frags exceeded\n");
			return -EINVAL;
		}

		addr = snd_sgbuf_get_addr(dmab, offset);
		/* program BDL addr */
		bdl->addr_l = cpu_to_le32(lower_32_bits(addr));
		bdl->addr_h = cpu_to_le32(upper_32_bits(addr));
		/* program BDL size */
		chunk = snd_sgbuf_get_chunk_size(dmab, offset, size);
		/* one BDLE should not cross 4K boundary */
		if (bus->align_bdle_4k) {
			u32 remain = 0x1000 - (offset & 0xfff);

			if (chunk > remain)
				chunk = remain;
		}
		bdl->size = cpu_to_le32(chunk);
		/* only program IOC when the whole segment is processed */
		size -= chunk;
		bdl->ioc = (size || !ioc) ? 0 : cpu_to_le32(0x01);
		bdl++;
		hstream->frags++;
		offset += chunk;
	}

	*bdlp = bdl;
	return offset;
}

/*
 * set up Buffer Descriptor List (BDL) for host memory transfer
 * BDL describes the location of the individual buffers and is little endian.
 */
int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev,
			     struct snd_dma_buffer *dmab,
			     struct hdac_stream *hstream)
{
	struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
	struct sof_intel_dsp_bdl *bdl;
	int i, offset, period_bytes, periods;
	int remain, ioc;

	period_bytes = hstream->period_bytes;
	dev_dbg(sdev->dev, "period_bytes: %#x, bufsize: %#x\n", period_bytes,
		hstream->bufsize);

	if (!period_bytes) {
		unsigned int chunk_size;

		chunk_size = snd_sgbuf_get_chunk_size(dmab, 0, hstream->bufsize);

		period_bytes = hstream->bufsize;

		/*
		 * HDA spec demands that the LVI value must be at least one
		 * before the DMA operation can begin. This means that there
		 * must be at least two BDLE present for the transfer.
		 *
		 * If the buffer is not a single continuous area then the
		 * hda_setup_bdle() will create multiple BDLEs for each segment.
		 * If the memory is a single continuous area, force it to be
		 * split into two 'periods', otherwise the transfer will be
		 * split to multiple BDLE for each chunk in hda_setup_bdle()
		 *
		 * Note: period_bytes == 0 can only happen for firmware or
		 * library loading. The data size is 4K aligned, which ensures
		 * that the second chunk's start address will be 128-byte
		 * aligned.
		 */
		if (chunk_size == hstream->bufsize)
			period_bytes /= 2;
	}

	periods = hstream->bufsize / period_bytes;

	dev_dbg(sdev->dev, "periods: %d\n", periods);

	remain = hstream->bufsize % period_bytes;
	if (remain)
		periods++;

	/* program the initial BDL entries */
	bdl = (struct sof_intel_dsp_bdl *)hstream->bdl.area;
	offset = 0;
	hstream->frags = 0;

	/*
	 * set IOC if don't use position IPC

Annotation

Implementation Notes