[PATCH v2 0/2] Fixes for McASP and dmaengine_pcm
This series fixes two patches:
1. Fix the dmaengine API usage by calling dmaengine_synchronize() after dmaengine_terminate_async() when xrun events occur in application 2. Use the McASP AFIFO property from DT to refine the period size, instead of hardcoding minimum to 64 samples
Signed-off-by: Jai Luthra j-luthra@ti.com --- Changes in v2: - Fix compiler warning for prepare callback by marking it static - Pass numevt directly to hw_rule_min_periodsize() - Link to v1: https://lore.kernel.org/r/20240604-asoc_next-v1-0-e895c88e744d@ti.com
--- Jai Luthra (2): ALSA: dmaengine: Synchronize dma channel in prepare() ASoC: ti: davinci-mcasp: Set min period size using FIFO config
include/sound/dmaengine_pcm.h | 1 + sound/core/pcm_dmaengine.c | 10 ++++++++++ sound/soc/soc-generic-dmaengine-pcm.c | 8 ++++++++ sound/soc/ti/davinci-mcasp.c | 9 +++++++-- 4 files changed, 26 insertions(+), 2 deletions(-) --- base-commit: d97496ca23a2d4ee80b7302849404859d9058bcd change-id: 20240604-asoc_next-c063fcc190c6
Best regards,
Sometimes the stream may be stopped due to XRUN events, in which case the userspace can call snd_pcm_drop() and snd_pcm_prepare() to stop and start the stream again.
In these cases, we must wait for the DMA channel to synchronize before marking the stream as prepared for playback, as the DMA channel gets stopped by snd_pcm_drop() without any synchronization.
Reviewed-by: Peter Ujfalusi peter.ujfalusi@gmail.com Signed-off-by: Jai Luthra j-luthra@ti.com --- include/sound/dmaengine_pcm.h | 1 + sound/core/pcm_dmaengine.c | 10 ++++++++++ sound/soc/soc-generic-dmaengine-pcm.c | 8 ++++++++ 3 files changed, 19 insertions(+)
diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index c11aaf8079fb..9c5800e5659f 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -36,6 +36,7 @@ snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, struct dma_chan *chan); int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream); +int snd_dmaengine_pcm_prepare(struct snd_pcm_substream *substream);
int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream, dma_filter_fn filter_fn, void *filter_data); diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index 12aa1cef11a1..dbf5c6136d68 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -349,6 +349,16 @@ int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream, } EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open_request_chan);
+int snd_dmaengine_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + + dmaengine_synchronize(prtd->dma_chan); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_prepare); + /** * snd_dmaengine_pcm_close - Close a dmaengine based PCM substream * @substream: PCM substream diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index ea3bc9318412..078fcb0ba8a2 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -318,6 +318,12 @@ static int dmaengine_copy(struct snd_soc_component *component, return 0; }
+static int dmaengine_pcm_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + return snd_dmaengine_pcm_prepare(substream); +} + static const struct snd_soc_component_driver dmaengine_pcm_component = { .name = SND_DMAENGINE_PCM_DRV_NAME, .probe_order = SND_SOC_COMP_ORDER_LATE, @@ -327,6 +333,7 @@ static const struct snd_soc_component_driver dmaengine_pcm_component = { .trigger = dmaengine_pcm_trigger, .pointer = dmaengine_pcm_pointer, .pcm_construct = dmaengine_pcm_new, + .prepare = dmaengine_pcm_prepare, };
static const struct snd_soc_component_driver dmaengine_pcm_component_process = { @@ -339,6 +346,7 @@ static const struct snd_soc_component_driver dmaengine_pcm_component_process = { .pointer = dmaengine_pcm_pointer, .copy = dmaengine_copy, .pcm_construct = dmaengine_pcm_new, + .prepare = dmaengine_pcm_prepare, };
static const char * const dmaengine_pcm_dma_channel_names[] = {
The minimum period size was enforced to 64 as older devices integrating McASP with EDMA used an internal FIFO of 64 samples.
With UDMA based platforms this internal McASP FIFO is optional, as the DMA engine internally does some buffering which is already accounted for when registering the platform. So we should read the actual FIFO configuration (txnumevt/rxnumevt) instead of hardcoding frames.min to 64.
Signed-off-by: Jai Luthra j-luthra@ti.com --- sound/soc/ti/davinci-mcasp.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 1e760c315521..2b1ed91a736c 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -1472,10 +1472,11 @@ static int davinci_mcasp_hw_rule_min_periodsize( { struct snd_interval *period_size = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + u8 numevt = *((u8 *)rule->private); struct snd_interval frames;
snd_interval_any(&frames); - frames.min = 64; + frames.min = numevt; frames.integer = 1;
return snd_interval_refine(period_size, &frames); @@ -1490,6 +1491,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, u32 max_channels = 0; int i, dir, ret; int tdm_slots = mcasp->tdm_slots; + u8 *numevt;
/* Do not allow more then one stream per direction */ if (mcasp->substreams[substream->stream]) @@ -1589,9 +1591,12 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, return ret; }
+ numevt = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + &mcasp->txnumevt : + &mcasp->rxnumevt; snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - davinci_mcasp_hw_rule_min_periodsize, NULL, + davinci_mcasp_hw_rule_min_periodsize, numevt, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
return 0;
Hi,
On 6/10/24 1:56 PM, Jai Luthra wrote:
The minimum period size was enforced to 64 as older devices integrating McASP with EDMA used an internal FIFO of 64 samples.
With UDMA based platforms this internal McASP FIFO is optional, as the DMA engine internally does some buffering which is already accounted for when registering the platform. So we should read the actual FIFO configuration (txnumevt/rxnumevt) instead of hardcoding frames.min to 64.
Thank you the patch and the fix, Acked-by: Peter Ujfalusi peter.ujfalusi@gmail.com
Signed-off-by: Jai Luthra j-luthra@ti.com
sound/soc/ti/davinci-mcasp.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 1e760c315521..2b1ed91a736c 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -1472,10 +1472,11 @@ static int davinci_mcasp_hw_rule_min_periodsize( { struct snd_interval *period_size = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
u8 numevt = *((u8 *)rule->private); struct snd_interval frames;
snd_interval_any(&frames);
- frames.min = 64;
frames.min = numevt; frames.integer = 1;
return snd_interval_refine(period_size, &frames);
@@ -1490,6 +1491,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, u32 max_channels = 0; int i, dir, ret; int tdm_slots = mcasp->tdm_slots;
u8 *numevt;
/* Do not allow more then one stream per direction */ if (mcasp->substreams[substream->stream])
@@ -1589,9 +1591,12 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, return ret; }
- numevt = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
&mcasp->txnumevt :
snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,&mcasp->rxnumevt;
davinci_mcasp_hw_rule_min_periodsize, NULL,
davinci_mcasp_hw_rule_min_periodsize, numevt, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
return 0;
On 6/10/24 03:56, Jai Luthra wrote:
Sometimes the stream may be stopped due to XRUN events, in which case the userspace can call snd_pcm_drop() and snd_pcm_prepare() to stop and start the stream again.
In these cases, we must wait for the DMA channel to synchronize before marking the stream as prepared for playback, as the DMA channel gets stopped by snd_pcm_drop() without any synchronization.
We should really implement the sync_stop() PCM callback and let the ALSA core let care of the sync.
Reviewed-by: Peter Ujfalusi peter.ujfalusi@gmail.com Signed-off-by: Jai Luthra j-luthra@ti.com
include/sound/dmaengine_pcm.h | 1 + sound/core/pcm_dmaengine.c | 10 ++++++++++ sound/soc/soc-generic-dmaengine-pcm.c | 8 ++++++++ 3 files changed, 19 insertions(+)
diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index c11aaf8079fb..9c5800e5659f 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -36,6 +36,7 @@ snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, struct dma_chan *chan); int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream); +int snd_dmaengine_pcm_prepare(struct snd_pcm_substream *substream);
int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream, dma_filter_fn filter_fn, void *filter_data); diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index 12aa1cef11a1..dbf5c6136d68 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -349,6 +349,16 @@ int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream, } EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open_request_chan);
+int snd_dmaengine_pcm_prepare(struct snd_pcm_substream *substream) +{
- struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
- dmaengine_synchronize(prtd->dma_chan);
- return 0;
+} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_prepare);
- /**
- snd_dmaengine_pcm_close - Close a dmaengine based PCM substream
- @substream: PCM substream
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index ea3bc9318412..078fcb0ba8a2 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -318,6 +318,12 @@ static int dmaengine_copy(struct snd_soc_component *component, return 0; }
+static int dmaengine_pcm_prepare(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
+{
- return snd_dmaengine_pcm_prepare(substream);
+}
- static const struct snd_soc_component_driver dmaengine_pcm_component = { .name = SND_DMAENGINE_PCM_DRV_NAME, .probe_order = SND_SOC_COMP_ORDER_LATE,
@@ -327,6 +333,7 @@ static const struct snd_soc_component_driver dmaengine_pcm_component = { .trigger = dmaengine_pcm_trigger, .pointer = dmaengine_pcm_pointer, .pcm_construct = dmaengine_pcm_new,
.prepare = dmaengine_pcm_prepare, };
static const struct snd_soc_component_driver dmaengine_pcm_component_process = {
@@ -339,6 +346,7 @@ static const struct snd_soc_component_driver dmaengine_pcm_component_process = { .pointer = dmaengine_pcm_pointer, .copy = dmaengine_copy, .pcm_construct = dmaengine_pcm_new,
.prepare = dmaengine_pcm_prepare, };
static const char * const dmaengine_pcm_dma_channel_names[] = {
On Mon, Jun 10, 2024 at 05:45:52PM -0700, Lars-Peter Clausen wrote:
On 6/10/24 03:56, Jai Luthra wrote:
Sometimes the stream may be stopped due to XRUN events, in which case the userspace can call snd_pcm_drop() and snd_pcm_prepare() to stop and start the stream again.
In these cases, we must wait for the DMA channel to synchronize before marking the stream as prepared for playback, as the DMA channel gets stopped by snd_pcm_drop() without any synchronization.
We should really implement the sync_stop() PCM callback and let the ALSA core let care of the sync.
Good point, that's a better idea.
Hi Lars, Mark,
On Jun 11, 2024 at 11:39:12 +0100, Mark Brown wrote:
On Mon, Jun 10, 2024 at 05:45:52PM -0700, Lars-Peter Clausen wrote:
On 6/10/24 03:56, Jai Luthra wrote:
Sometimes the stream may be stopped due to XRUN events, in which case the userspace can call snd_pcm_drop() and snd_pcm_prepare() to stop and start the stream again.
In these cases, we must wait for the DMA channel to synchronize before marking the stream as prepared for playback, as the DMA channel gets stopped by snd_pcm_drop() without any synchronization.
We should really implement the sync_stop() PCM callback and let the ALSA core let care of the sync.
Good point, that's a better idea.
Thanks for the suggestion, sending a v3 with the change.
Peter, I've kept your R-by intact as it is a minor change.
On Mon, 10 Jun 2024 16:25:59 +0530, Jai Luthra wrote:
This series fixes two patches:
- Fix the dmaengine API usage by calling dmaengine_synchronize() after dmaengine_terminate_async() when xrun events occur in application
- Use the McASP AFIFO property from DT to refine the period size, instead of hardcoding minimum to 64 samples
[...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
Thanks!
[1/2] ALSA: dmaengine: Synchronize dma channel in prepare() (no commit info) [2/2] ASoC: ti: davinci-mcasp: Set min period size using FIFO config commit: c5dcf8ab10606e76c1d8a0ec77f27d84a392e874
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
participants (4)
-
Jai Luthra
-
Lars-Peter Clausen
-
Mark Brown
-
Péter Ujfalusi