Re: [PATCH v2] ASoC: fsl_sai: Fix spurious BCLK on resume by clearing BYP

From: Shengjiu Wang

Date: Sun Jul 12 2026 - 23:22:03 EST


On Fri, Jul 10, 2026 at 3:08 PM Chancel Liu <chancel.liu@xxxxxxxxxxx> wrote:
>
> From: Chancel Liu <chancel.liu@xxxxxxx>
>
> 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@xxxxxxxxxxxxxxx
> Signed-off-by: Chancel Liu <chancel.liu@xxxxxxx>

Thanks for the update.

Reviewed-by: Shengjiu Wang <shengjiu.wang@xxxxxxxxx>

Best regards
Shengjiu Wang
> ---
> Changes in v2:
> - Move BYP/BCI clearing from fsl_sai_config_disable() (trigger path)
> to fsl_sai_hw_free() to avoid breaking pause/suspend-resume cycles
> where hw_params() is not called again on unpause/resume.
>
> sound/soc/fsl/fsl_sai.c | 29 +++++++++++++++++++++++++----
> 1 file changed, 25 insertions(+), 4 deletions(-)
>
> diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
> index 9661602b53c5..d232c8f53061 100644
> --- a/sound/soc/fsl/fsl_sai.c
> +++ b/sound/soc/fsl/fsl_sai.c
> @@ -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;
> --
> 2.50.1
>