]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ASoC: fsl_sai: Fix spurious BCLK on resume by clearing BYP
authorChancel Liu <chancel.liu@nxp.com>
Fri, 10 Jul 2026 07:08:35 +0000 (16:08 +0900)
committerMark Brown <broonie@kernel.org>
Mon, 13 Jul 2026 12:41:11 +0000 (13:41 +0100)
When the BCLK divider ratio is 1:1, fsl_sai_set_bclk() enables bypass
mode by setting BYP, but never clears the bit. The BYP=1 value remains
in the regcache, and is restored by regcache_sync() on the next runtime
resume.

Since BYP=1 combined with BCD=1 immediately outputs the ungated MCLK
as BCLK without waiting for BCE/TE/RE to be enabled, the clock is
driven prematurely before the stream is fully configured, causing
noise on some codecs.

Fix this by clearing BYP and BCI in fsl_sai_hw_free() taking into
account sync mode and the opposite stream's state, so that the regcache
holds BYP=0 before runtime suspend and regcache_sync() on resume will
not restore bypass mode prematurely.

Fixes: a50b7926d015 ("ASoC: fsl_sai: implement 1:1 bclk:mclk ratio support")
Cc: stable@vger.kernel.org
Signed-off-by: Chancel Liu <chancel.liu@nxp.com>
Reviewed-by: Shengjiu Wang <shengjiu.wang@gmail.com>
Link: https://patch.msgid.link/20260710070835.3749817-1-chancel.liu@oss.nxp.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/fsl/fsl_sai.c

index 9661602b53c5b012ca48d660fe253983bca2fd3a..d232c8f5306154bccf6736a47ca4657d46c9bb76 100644 (file)
@@ -808,6 +808,8 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream,
        struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
        bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
        unsigned int ofs = sai->soc_data->reg_offset;
+       int adir = tx ? RX : TX;
+       int dir  = tx ? TX : RX;
 
        /* Clear xMR to avoid channel swap with mclk_with_tere enabled case */
        regmap_write(sai->regmap, FSL_SAI_xMR(tx), 0);
@@ -815,10 +817,29 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream,
        regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs),
                           FSL_SAI_CR3_TRCE_MASK, 0);
 
-       if (!sai->is_consumer_mode[tx] &&
-           sai->mclk_streams & BIT(substream->stream)) {
-               clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]);
-               sai->mclk_streams &= ~BIT(substream->stream);
+       if (!sai->is_consumer_mode[tx]) {
+               bool adir_active = !!(sai->mclk_streams & BIT(!substream->stream));
+               /*
+                * If opposite stream provides clocks for synchronous mode and
+                * it is inactive, Clear BYP and BCI
+                */
+               if (fsl_sai_dir_is_synced(sai, adir) && !adir_active)
+                       regmap_update_bits(sai->regmap, FSL_SAI_xCR2(!tx, ofs),
+                                          FSL_SAI_CR2_BCI | FSL_SAI_CR2_BYP, 0);
+               /*
+                * Clear BYP and BCI of current stream if either of:
+                * 1. current stream doesn't provide clocks for synchronous mode
+                * 2. current stream provides clocks for synchronous mode but no
+                *    more stream is active.
+                */
+               if (!fsl_sai_dir_is_synced(sai, dir) || !adir_active)
+                       regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
+                                          FSL_SAI_CR2_BCI | FSL_SAI_CR2_BYP, 0);
+
+               if (sai->mclk_streams & BIT(substream->stream)) {
+                       clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]);
+                       sai->mclk_streams &= ~BIT(substream->stream);
+               }
        }
 
        return 0;