[RFC PATCH 2/4] ASoC: soc-generic-dmaengine-pcm: Add custom prepare and submit function

Sit, Michael Wei Hong michael.wei.hong.sit at intel.com
Sat Dec 5 01:55:06 CET 2020



> > -----Original Message-----
> > From: Lars-Peter Clausen <lars at metafoo.de>
> > Sent: 01 December 2020 4:22 PM
> > To: Shevchenko, Andriy <andriy.shevchenko at intel.com>
> > Cc: Sit, Michael Wei Hong <michael.wei.hong.sit at intel.com>;
> Sia, Jee
> > Heng <jee.heng.sia at intel.com>; pierre-
> louis.bossart at linux.intel.com;
> > Rojewski, Cezary <cezary.rojewski at intel.com>;
> > liam.r.girdwood at linux.intel.com; vkoul at kernel.org;
> tiwai at suse.com;
> > broonie at kernel.org; alsa-devel at alsa-project.org
> > Subject: Re: [RFC PATCH 2/4] ASoC: soc-generic-dmaengine-
> pcm: Add
> > custom prepare and submit function
> >
...
> > > Why should we split than resplit if we may do it in one go?
> > > Why then we have DMA capabilities returned to the
> consumers.
> > >
> > > So, I have that we need to optimize DMA SG list preparation
> in a way
> > > that consumer gets SG list cooked in accordance with DMA
> > limitations / requirements.
> >
> > The API that the audio drivers use request a period DMA
> transfer for
> > length N split into M segments with the callback being called
> after
> > each segment.
> >
> > How that is implemented internally in the DMA does not matter
> as long
> > as it matches those semantics. E.g. if the segment is longer than
> what
> > the DMA can handle it can split it into two segments internally
> and
> > call the callback every second segment.
> >
> > The way this API works there isn't even really a possibility for
> the
> > client side to split periodic transfers.
> >
> > Btw. 1024 beats per segment seems excessively small, I don't
> > understand how somebody would design such an DMA. Was
> the assumption
> > that the DMA will never transfer more than PAGE_SIZE of
> contiguous
> > memory? Or do we not understand the documentation
> correctly?
> [>>] The segment size is 1024 items. The item size could be 8bits,
> 16bits or 32bits. So, set_max_seg_size() is set to 1024*4bytes.
> 
> 
> >
> > - Lars

Hi All,
 
In order to fulfil Andy request on optimizing the linked-list at the DMA client side, we proposed a few changes to the soc-generic- dmaengine and DMAENGINE API due to the AxiDMA's nature operation in number of items.
 
Add New DMAENGINE API:
// For DMA driver to set the max items in a segment 1. dma_set_max_seg_items(struct device *dev, unsigned int size)

// For DMA client to retrieve the max items supported in a segment 2. dma_get_max_seg_items(struct device *dev)

#1 and #2 above are the critical API needed to be exposed to the DMA Clients so that DMA Clients can use it to calculate the appropriate segment length before pass it to the DMA driver.
If #1 and #2 are No Go, then splitting linked-list in DMA client can't be achieve.

ASoC changes:
1. Adding variable to snd_pcm_hardware to store DMA limitation information.
2. soc-generic-dmaengine-pcm to register DMA limitation exposed by DMA driver using API provided above.
3. dmaengine_pcm_prepare_and_submit in pcm_dmaengine.c to check the number of items needed calculated from userspace and configure the dma accordingly if the number of items exceeds the DMA items limitation.
4. dmaengine_pcm_dma_complete in pcm_dmaengine.c to check the number of items needed calculated from userspace and update position according to DMA limitation, to trigger period_elapse appropriately.

All of the above are needed in order to facilitate storing of the DMA limitation info and using the info to configure the DMA appropriately within the DMA limits.
#3 and #4 implements a check against the number of items needed based on userspace provided information and the DMA item limit, if the limit is exceeded, the maximum limit of the DMA is used to configure the DMA transfers.
e.g.
bytes_to_samples returns a value higher than the maximum item limit of the DMA, the driver will pass the maximum usable limit of the DMA using samples_to_bytes to the DMA driver. This will make the DMA driver to use longer linked-list and would not need to do the resplitting at the DMA driver.

Below is the snapshot of the code showing how soc-generic- dmaengine make use of the new API to calculate the segment length.
---------------------------------------------------------------------------------------------------------------------------------------------
Code change in pcm.h

struct snd_pcm_hardware {
	......

	size_t buffer_bytes_max;	/* max buffer size */
	size_t period_bytes_min;	/* min period size */
	size_t period_bytes_max;	/* max period size */
	unsigned int periods_min;	/* min # of periods */
	unsigned int periods_max;	/* max # of periods */
	size_t fifo_size;		/* fifo size in bytes */

--> Add variable for dma max item numbers
	e.g: unsigned int dma_item_max;	/* max # of dma items */

	......
};


---------------------------------------------------------------------------------------------------------------------------------------------
Code change in soc-generic-dmaengine-pcm.c

static int
dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component,
				   struct snd_pcm_substream *substream) {
	......

	memset(&hw, 0, sizeof(hw));
	hw.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
			SNDRV_PCM_INFO_INTERLEAVED;
	hw.periods_min = 2;
	hw.periods_max = UINT_MAX;
	hw.period_bytes_min = 256;
	hw.period_bytes_max = dma_get_max_seg_size(dma_dev);
	hw.buffer_bytes_max = SIZE_MAX;
	hw.fifo_size = dma_data->fifo_size;

--> Add code to register MAX_DMA_Items limitation using API exposed by 
--> dma
	e.g: hw.dma_item_max = dma_get_max_item_number(dma_dev);

	......
}

---------------------------------------------------------------------------------------------------------------------------------------------
Code change in pcm_dmaengine.c

static void dmaengine_pcm_dma_complete(void *arg) {
	......

	struct snd_pcm_runtime *runtime = substream->runtime;
	int blocks;

-->Add code to convert period bytes to number of DMA items passed down 
-->from user space
	e.g : blocks = bytes_to_samples(runtime, snd_pcm_lib_period_bytes(substream));
--> Add code to check number of DMA items from user space vs DMA 
--> limitation and update position accordingly
	e.g:

	if (blocks > hw.dma_item_max)
		prtd->pos += samples_to_bytes(runtime, MAX_DMA_BLOCKS);
	else
		prtd->pos += snd_pcm_lib_period_bytes(substream);

	......
}

static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream) {
	......

	struct snd_pcm_runtime *runtime = substream->runtime;
	int blocks;

--> Add code to convert period bytes to number of DMA items passed down 
--> from user space
	e.g: blocks = bytes_to_samples(runtime, snd_pcm_lib_period_bytes(substream));
	......

--> Add code to check if the number of blocks used exceed the DMA 
--> limitation and prepare DMA according to limitation instead of 
--> information taken from userspace
	e.g:
	if (blocks > hw.dma_item_max)
		desc = dmaengine_prep_dma_cyclic(chan,
			substream->runtime->dma_addr,
			snd_pcm_lib_buffer_bytes(substream),
			samples_to_bytes(runtime, MAX_DMA_BLOCKS), direction, flags);
	else
		desc = dmaengine_prep_dma_cyclic(chan,
			substream->runtime->dma_addr,
			snd_pcm_lib_buffer_bytes(substream),
			snd_pcm_lib_period_bytes(substream), direction, flags);

	......
}



More information about the Alsa-devel mailing list