From 42568b67f302146ddc2eacd5b828d05ebc69ad60 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Sat, 20 Jul 2024 11:55:10 +0800 Subject: [PATCH] ASoC: rockchip: i2s-tdm: Fix the clk glitch on Calibrate case BACKGROUND A short clk giltch (~200us) found on the mclk calibrate case in kernel 5.10. mclk-12.288M - mclk-49.152M (~200us) - mclk-12.288M The root cause is: clk_set_rate(mclk_parent, 49.152M) clk_set_rate(mclk, 12.288M) Prefer to get the mclk rate div from mclk_parent rate, Unfortunately, the latter will change the mclk_parent rate again in kernel 5.10. So, do not change mclk parent's rate when it is multiple mclk already, let clk framework handle it. e.g. mclk: clk_i2s1_8ch_tx mclk_parent: clk_i2s_8ch_tx_src clk_i2s1_8ch_tx_src 0 0 0 12288000 clk_i2s1_8ch_tx_mux 0 0 0 12288000 clk_i2s1_8ch_rx 0 0 0 12288000 Signed-off-by: Sugar Zhang Change-Id: Ie9e869a48b65e287e60545626e92c9090ab02229 --- sound/soc/rockchip/rockchip_i2s_tdm.c | 32 ++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index 0370e01a0cba..1f08e0043065 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -67,6 +67,7 @@ #define MULTIPLEX_CH_MAX 10 #define CLK_PPM_MIN (-1000) #define CLK_PPM_MAX (1000) +#define CLK_SHIFT_RATE_HZ_MAX 5 #define MAXBURST_PER_FIFO 8 #define WAIT_TIME_MS_MAX 10000 @@ -1448,14 +1449,29 @@ static int rockchip_i2s_tdm_calibrate_mclk(struct rk_i2s_tdm_dev *i2s_tdm, unsigned int mclk_root_freq; unsigned int mclk_root_initial_freq; unsigned int mclk_parent_freq; + unsigned int mclk_freq, freq, freq_req; unsigned int div, delta; u64 ppm; int ret; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { mclk_parent = i2s_tdm->mclk_tx_src; - else + mclk_freq = i2s_tdm->mclk_tx_freq; + } else { mclk_parent = i2s_tdm->mclk_rx_src; + mclk_freq = i2s_tdm->mclk_rx_freq; + } + + switch (i2s_tdm->clk_trcm) { + case I2S_CKR_TRCM_TXONLY: + mclk_parent = i2s_tdm->mclk_tx_src; + mclk_freq = i2s_tdm->mclk_tx_freq; + break; + case I2S_CKR_TRCM_RXONLY: + mclk_parent = i2s_tdm->mclk_rx_src; + mclk_freq = i2s_tdm->mclk_rx_freq; + break; + } switch (lrck_freq) { case 8000: @@ -1514,7 +1530,17 @@ static int rockchip_i2s_tdm_calibrate_mclk(struct rk_i2s_tdm_dev *i2s_tdm, i2s_tdm->mclk_root1_freq = clk_get_rate(i2s_tdm->mclk_root1); } - return clk_set_rate(mclk_parent, mclk_parent_freq); + freq = clk_get_rate(mclk_parent); + div = DIV_ROUND_CLOSEST(freq, mclk_freq); + freq_req = mclk_freq * div; + if (freq < freq_req - CLK_SHIFT_RATE_HZ_MAX || + freq > freq_req + CLK_SHIFT_RATE_HZ_MAX) { + dev_dbg(i2s_tdm->dev, "Change mclk parent freq from %d to %d\n", + freq, mclk_parent_freq); + ret = clk_set_rate(mclk_parent, mclk_parent_freq); + } + + return ret; } static int rockchip_i2s_tdm_mclk_reparent(struct rk_i2s_tdm_dev *i2s_tdm)