diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index 015fc8185e93..2ede0f6fcf30 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,8 @@ #define DEFAULT_MCLK_FS 256 #define CH_GRP_MAX 4 /* The max channel 8 / 2 */ #define MULTIPLEX_CH_MAX 10 +#define CLK_PPM_MIN (-1000) +#define CLK_PPM_MAX (1000) struct txrx_config { u32 addr; @@ -82,10 +85,15 @@ struct rk_i2s_tdm_dev { bool tdm_mode; unsigned int mclk_rx_freq; unsigned int mclk_tx_freq; + unsigned int mclk_root0_freq; + unsigned int mclk_root1_freq; + unsigned int mclk_root0_initial_freq; + unsigned int mclk_root1_initial_freq; unsigned int bclk_fs; unsigned int clk_trcm; unsigned int i2s_sdis[CH_GRP_MAX]; unsigned int i2s_sdos[CH_GRP_MAX]; + int clk_ppm; int tx_reset_id; int rx_reset_id; atomic_t refcount; @@ -658,13 +666,39 @@ static void rockchip_i2s_tdm_xfer_resume(struct snd_pcm_substream *substream, I2S_XFER_RXS_START); } +static int rockchip_i2s_tdm_clk_set_rate(struct rk_i2s_tdm_dev *i2s_tdm, + struct clk *clk, unsigned long rate) +{ + unsigned long rate_target; + int delta, ret; + + ret = rockchip_pll_clk_compensation(clk, i2s_tdm->clk_ppm); + if (!ret) + return ret; + + delta = (i2s_tdm->clk_ppm < 0) ? -1 : 1; + delta *= (int)div64_u64((uint64_t)rate * (uint64_t)abs(i2s_tdm->clk_ppm) + 500000, 1000000); + + rate_target = rate + delta; + + if (!rate_target) + return -EINVAL; + + ret = clk_set_rate(clk, rate_target); + + return ret; +} + static int rockchip_i2s_tdm_calibrate_mclk(struct rk_i2s_tdm_dev *i2s_tdm, struct snd_pcm_substream *substream, unsigned int lrck_freq) { struct clk *mclk_root; struct clk *mclk_parent; + unsigned int mclk_root_freq; + unsigned int mclk_root_initial_freq; unsigned int mclk_parent_freq; + unsigned int div; int ret; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -682,6 +716,8 @@ static int rockchip_i2s_tdm_calibrate_mclk(struct rk_i2s_tdm_dev *i2s_tdm, case 96000: case 192000: mclk_root = i2s_tdm->mclk_root0; + mclk_root_freq = i2s_tdm->mclk_root0_freq; + mclk_root_initial_freq = i2s_tdm->mclk_root0_initial_freq; mclk_parent_freq = DEFAULT_MCLK_FS * 192000; break; case 11025: @@ -690,6 +726,8 @@ static int rockchip_i2s_tdm_calibrate_mclk(struct rk_i2s_tdm_dev *i2s_tdm, case 88200: case 176400: mclk_root = i2s_tdm->mclk_root1; + mclk_root_freq = i2s_tdm->mclk_root1_freq; + mclk_root_initial_freq = i2s_tdm->mclk_root1_initial_freq; mclk_parent_freq = DEFAULT_MCLK_FS * 176400; break; default: @@ -702,6 +740,21 @@ static int rockchip_i2s_tdm_calibrate_mclk(struct rk_i2s_tdm_dev *i2s_tdm, if (ret) goto out; + if (mclk_root_freq % mclk_parent_freq) { + div = DIV_ROUND_CLOSEST(mclk_root_initial_freq, mclk_parent_freq); + if (!div) + return -EINVAL; + + mclk_root_freq = mclk_parent_freq * round_up(div, 2); + i2s_tdm->clk_ppm = 0; + ret = clk_set_rate(mclk_root, mclk_root_freq); + if (ret) + goto out; + + i2s_tdm->mclk_root0_freq = clk_get_rate(i2s_tdm->mclk_root0); + i2s_tdm->mclk_root1_freq = clk_get_rate(i2s_tdm->mclk_root1); + } + ret = clk_set_rate(mclk_parent, mclk_parent_freq); if (ret) goto out; @@ -1037,6 +1090,64 @@ static int rockchip_i2s_tdm_set_sysclk(struct snd_soc_dai *cpu_dai, int stream, return 0; } +static int rockchip_i2s_tdm_clk_compensation_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = CLK_PPM_MIN; + uinfo->value.integer.max = CLK_PPM_MAX; + uinfo->value.integer.step = 1; + + return 0; +} + +static int rockchip_i2s_tdm_clk_compensation_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai); + + ucontrol->value.integer.value[0] = i2s_tdm->clk_ppm; + + return 0; +} + +static int rockchip_i2s_tdm_clk_compensation_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai); + int ret = 0; + + if ((ucontrol->value.integer.value[0] < CLK_PPM_MIN) || + (ucontrol->value.integer.value[0] > CLK_PPM_MAX)) + return -EINVAL; + + i2s_tdm->clk_ppm = ucontrol->value.integer.value[0]; + + ret = rockchip_i2s_tdm_clk_set_rate(i2s_tdm, i2s_tdm->mclk_root0, + i2s_tdm->mclk_root0_freq); + if (ret) + return ret; + + if (clk_is_match(i2s_tdm->mclk_root0, i2s_tdm->mclk_root1)) + return 0; + + ret = rockchip_i2s_tdm_clk_set_rate(i2s_tdm, i2s_tdm->mclk_root1, + i2s_tdm->mclk_root1_freq); + + return ret; +} + +static struct snd_kcontrol_new rockchip_i2s_tdm_compensation_control = { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "PCM Clk Compensation In PPM", + .info = rockchip_i2s_tdm_clk_compensation_info, + .get = rockchip_i2s_tdm_clk_compensation_get, + .put = rockchip_i2s_tdm_clk_compensation_put, +}; + static int rockchip_i2s_tdm_dai_probe(struct snd_soc_dai *dai) { struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai); @@ -1044,6 +1155,9 @@ static int rockchip_i2s_tdm_dai_probe(struct snd_soc_dai *dai) dai->capture_dma_data = &i2s_tdm->capture_dma_data; dai->playback_dma_data = &i2s_tdm->playback_dma_data; + if (i2s_tdm->mclk_calibrate) + snd_soc_add_dai_controls(dai, &rockchip_i2s_tdm_compensation_control, 1); + return 0; } @@ -1590,6 +1704,11 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) i2s_tdm->mclk_root1 = devm_clk_get(&pdev->dev, "mclk_root1"); if (IS_ERR(i2s_tdm->mclk_root1)) return PTR_ERR(i2s_tdm->mclk_root1); + + i2s_tdm->mclk_root0_initial_freq = clk_get_rate(i2s_tdm->mclk_root0); + i2s_tdm->mclk_root1_initial_freq = clk_get_rate(i2s_tdm->mclk_root1); + i2s_tdm->mclk_root0_freq = i2s_tdm->mclk_root0_initial_freq; + i2s_tdm->mclk_root1_freq = i2s_tdm->mclk_root1_initial_freq; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0);