From: Shengjiu Wang <[email protected]>

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 <[email protected]>
---
 sound/soc/fsl/fsl_asrc_dma.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c
index 5aa96af994c4..38f2b7c63133 100644
--- a/sound/soc/fsl/fsl_asrc_dma.c
+++ b/sound/soc/fsl/fsl_asrc_dma.c
@@ -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);
-- 
2.34.1


Reply via email to