]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ASoC: fsl_asrc_dma: fix eDMA maxburst misalignment with channel count
authorShengjiu Wang <shengjiu.wang@nxp.com>
Thu, 25 Jun 2026 10:24:16 +0000 (18:24 +0800)
committerMark Brown <broonie@kernel.org>
Thu, 25 Jun 2026 13:56:27 +0000 (14:56 +0100)
The back-end consumes data in units of the number of channels. When the
maxburst value is not evenly divisible by the channel count, the DMA
transfer length does not align with the FIFO frame boundary, causing
wrong data to be copied and audible noise at the end of the stream.

This is specific to eDMA: eDMA only responds to DMA requests from the
back-end, whereas SDMA handles requests from both the front-end and the
back-end and is not affected.

For eDMA, when the back-end maxburst is not evenly divisible by the
channel count, align it to the nearest valid boundary:
- If maxburst >= channel count, override to the channel count so each
  transfer corresponds to exactly one audio frame.
- If maxburst < channel count, override to 1 to avoid partial-frame
  transfers.

Retain the original maxburst for SDMA or when it already aligns with
the channel count.

Fixes: c05f10f28ef6 ("ASoC: fsl_asrc: Add support for imx8qm & imx8qxp")
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Link: https://patch.msgid.link/20260625102416.424911-1-shengjiu.wang@oss.nxp.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/fsl/fsl_asrc_dma.c

index 5aa96af994c41a46a70a297b160a10ef6f24a803..38f2b7c63133abb99ad27c4453ec2ada2b2d5eb1 100644 (file)
@@ -288,6 +288,26 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
        config_be.dst_addr_width = buswidth;
        config_be.dst_maxburst = dma_params_be->maxburst;
 
+       /*
+        * For eDMA, the back-end may report a maxburst size that is not evenly
+        * divisible by the channel count. This causes the DMA transfer length
+        * to misalign with the FIFO boundary, resulting in wrong data and
+        * audible noise. Align maxburst to the nearest valid boundary:
+        * - If maxburst >= channel count, override to the channel count so
+        *   each transfer equals exactly one audio frame.
+        * - If maxburst < channel count, override to 1 to avoid partial-frame
+        *   transfers.
+        */
+       if (asrc->use_edma && (dma_params_be->maxburst % params_channels(params))) {
+               if (dma_params_be->maxburst >= params_channels(params)) {
+                       config_be.src_maxburst = params_channels(params);
+                       config_be.dst_maxburst = params_channels(params);
+               } else {
+                       config_be.src_maxburst = 1;
+                       config_be.dst_maxburst = 1;
+               }
+       }
+
        memset(&audio_config, 0, sizeof(audio_config));
        config_be.peripheral_config = &audio_config;
        config_be.peripheral_size  = sizeof(audio_config);