]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ASoC: fsl_asrc_m2m: Add option to start ASRC before DMA device for M2M
authorShengjiu Wang <shengjiu.wang@nxp.com>
Fri, 6 Feb 2026 01:48:03 +0000 (09:48 +0800)
committerMark Brown <broonie@kernel.org>
Fri, 6 Feb 2026 17:22:35 +0000 (17:22 +0000)
There is a limitation on i.MX952 that dma request is not cleared at the
end of conversion with dma slave mode. Which causes sample is dropped
from the input fifo on the second time if dma is triggered before the
client device and EDMA may copy wrong data from output fifo as the output
fifo is not ready in the beginning.

The solution is to trigger asrc before dma on i.MX952, and add delay to
wait output data is generated then start the EDMA for output, otherwise
the m2m function has noise issues.

So add an option to start ASRC first for M2M before ASRC is enabled on
i.MX952.

Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/20260206014805.3897764-3-shengjiu.wang@nxp.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/fsl/fsl_asrc.c
sound/soc/fsl/fsl_asrc.h
sound/soc/fsl/fsl_asrc_common.h
sound/soc/fsl/fsl_asrc_m2m.c

index 92fb16f7be4563ea24baef5778ef672f73da89b1..2fe25667c888e087bd4138f710fd1d809928267f 100644 (file)
@@ -1078,6 +1078,26 @@ static unsigned int fsl_asrc_get_output_fifo_size(struct fsl_asrc_pair *pair)
        return val >> ASRFSTi_OUTPUT_FIFO_SHIFT;
 }
 
+static bool fsl_asrc_m2m_output_ready(struct fsl_asrc_pair *pair)
+{
+       struct fsl_asrc *asrc = pair->asrc;
+       enum asrc_pair_index index = pair->index;
+       u32 val;
+       int ret;
+
+       /* Check output fifo status if it exceeds the watermark. */
+       ret = regmap_read_poll_timeout(asrc->regmap, REG_ASRFST(index), val,
+                                      (ASRFSTi_OUTPUT_FIFO_FILL(val) >= ASRC_M2M_OUTPUTFIFO_WML),
+                                      1, 1000);
+
+       if (ret) {
+               pair_warn("output is not ready\n");
+               return false;
+       }
+
+       return true;
+}
+
 static int fsl_asrc_m2m_prepare(struct fsl_asrc_pair *pair)
 {
        struct fsl_asrc_pair_priv *pair_priv = pair->private;
@@ -1275,6 +1295,7 @@ static int fsl_asrc_probe(struct platform_device *pdev)
 
        asrc_priv->soc = of_device_get_match_data(&pdev->dev);
        asrc->use_edma = asrc_priv->soc->use_edma;
+       asrc->start_before_dma = asrc_priv->soc->start_before_dma;
        asrc->get_dma_channel = fsl_asrc_get_dma_channel;
        asrc->request_pair = fsl_asrc_request_pair;
        asrc->release_pair = fsl_asrc_release_pair;
@@ -1289,6 +1310,7 @@ static int fsl_asrc_probe(struct platform_device *pdev)
        asrc->m2m_get_maxburst = fsl_asrc_m2m_get_maxburst;
        asrc->m2m_pair_resume = fsl_asrc_m2m_pair_resume;
        asrc->m2m_get_cap = fsl_asrc_m2m_get_cap;
+       asrc->m2m_output_ready = fsl_asrc_m2m_output_ready;
 
        if (of_device_is_compatible(np, "fsl,imx35-asrc")) {
                asrc_priv->clk_map[IN] = input_clk_map_imx35;
index 1c492eb237f5f413a1c8a95dd7ca2ad95b1f586f..60b6865ca95226458569daa4ee7334af61d46014 100644 (file)
 #define ASRFSTi_OUTPUT_FIFO_WIDTH      7
 #define ASRFSTi_OUTPUT_FIFO_SHIFT      12
 #define ASRFSTi_OUTPUT_FIFO_MASK       (((1 << ASRFSTi_OUTPUT_FIFO_WIDTH) - 1) << ASRFSTi_OUTPUT_FIFO_SHIFT)
+#define ASRFSTi_OUTPUT_FIFO_FILL(v)    \
+       (((v) & ASRFSTi_OUTPUT_FIFO_MASK) >> ASRFSTi_OUTPUT_FIFO_SHIFT)
 #define ASRFSTi_IAEi_SHIFT             11
 #define ASRFSTi_IAEi_MASK              (1 << ASRFSTi_IAEi_SHIFT)
 #define ASRFSTi_IAEi                   (1 << ASRFSTi_IAEi_SHIFT)
@@ -432,10 +434,12 @@ struct dma_block {
  *
  * @use_edma: using edma as dma device or not
  * @channel_bits: width of ASRCNCR register for each pair
+ * @start_before_dma: start asrc before dma
  */
 struct fsl_asrc_soc_data {
        bool use_edma;
        unsigned int channel_bits;
+       bool start_before_dma;
 };
 
 /**
index 0cd595b0f629dbc8a7e827586b35943038e48c38..c8a1a2b5915d37e70ab202b86645f46eee6f25d4 100644 (file)
@@ -107,6 +107,7 @@ struct fsl_asrc_pair {
  * @asrc_rate: default sample rate for ASoC Back-Ends
  * @asrc_format: default sample format for ASoC Back-Ends
  * @use_edma: edma is used
+ * @start_before_dma: start asrc before dma
  * @get_dma_channel: function pointer
  * @request_pair: function pointer
  * @release_pair: function pointer
@@ -116,6 +117,7 @@ struct fsl_asrc_pair {
  * @m2m_start: function pointer
  * @m2m_unprepare: function pointer
  * @m2m_stop: function pointer
+ * @m2m_output_ready: function pointer, check output fifo ready or not
  * @m2m_calc_out_len: function pointer
  * @m2m_get_maxburst: function pointer
  * @m2m_pair_suspend: function pointer
@@ -143,6 +145,7 @@ struct fsl_asrc {
        int asrc_rate;
        snd_pcm_format_t asrc_format;
        bool use_edma;
+       bool start_before_dma;
 
        struct dma_chan *(*get_dma_channel)(struct fsl_asrc_pair *pair, bool dir);
        int (*request_pair)(int channels, struct fsl_asrc_pair *pair);
@@ -154,6 +157,7 @@ struct fsl_asrc {
        int (*m2m_start)(struct fsl_asrc_pair *pair);
        int (*m2m_unprepare)(struct fsl_asrc_pair *pair);
        int (*m2m_stop)(struct fsl_asrc_pair *pair);
+       bool (*m2m_output_ready)(struct fsl_asrc_pair *pair);
 
        int (*m2m_calc_out_len)(struct fsl_asrc_pair *pair, int input_buffer_length);
        int (*m2m_get_maxburst)(u8 dir, struct fsl_asrc_pair *pair);
index f46881f71e43079b327687aa9c1f0fb01ce5e8ab..77999526dd9eaa540fb3a0e309855bc5d82ded55 100644 (file)
@@ -253,15 +253,21 @@ static int asrc_m2m_device_run(struct fsl_asrc_pair *pair, struct snd_compr_task
        reinit_completion(&pair->complete[IN]);
        reinit_completion(&pair->complete[OUT]);
 
+       if (asrc->start_before_dma)
+               asrc->m2m_start(pair);
+
        /* Submit DMA request */
        dmaengine_submit(pair->desc[IN]);
        dma_async_issue_pending(pair->desc[IN]->chan);
        if (out_dma_len > 0) {
+               if (asrc->start_before_dma && asrc->m2m_output_ready)
+                       asrc->m2m_output_ready(pair);
                dmaengine_submit(pair->desc[OUT]);
                dma_async_issue_pending(pair->desc[OUT]->chan);
        }
 
-       asrc->m2m_start(pair);
+       if (!asrc->start_before_dma)
+               asrc->m2m_start(pair);
 
        if (!wait_for_completion_interruptible_timeout(&pair->complete[IN], 10 * HZ)) {
                dev_err(dev, "out DMA task timeout\n");