diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c index 85e545aac523..5ec3fe8c1f33 100644 --- a/sound/soc/rockchip/rockchip_pdm.c +++ b/sound/soc/rockchip/rockchip_pdm.c @@ -229,13 +229,46 @@ static void rockchip_pdm_rxctrl(struct rk_pdm_dev *pdm, int on) } } +static int rockchip_pdm_clk_set_rate(struct rk_pdm_dev *pdm, + struct clk *clk, unsigned long rate, + int ppm) +{ + unsigned long rate_target; + int delta, ret; + + if (ppm == pdm->clk_ppm) + return 0; + + ret = rockchip_pll_clk_compensation(clk, ppm); + if (ret != -ENOSYS) + goto out; + + delta = (ppm < 0) ? -1 : 1; + delta *= (int)div64_u64((uint64_t)rate * (uint64_t)abs(ppm) + 500000, 1000000); + + rate_target = rate + delta; + + if (!rate_target) + return -EINVAL; + + ret = clk_set_rate(clk, rate_target); + if (ret) + return ret; +out: + if (!ret) + pdm->clk_ppm = ppm; + + return ret; +} + static int rockchip_pdm_set_samplerate(struct rk_pdm_dev *pdm, unsigned int samplerate) { unsigned int val = 0, div = 0; - unsigned int clk_rate, clk_div, rate; + unsigned int clk_rate, clk_div, rate, delta; unsigned int clk_src = 0, clk_out = 0, signoff = PDM_SIGNOFF_CLK_100M; unsigned long m, n; + uint64_t ppm; bool change; int ret; @@ -250,14 +283,21 @@ static int rockchip_pdm_set_samplerate(struct rk_pdm_dev *pdm, unsigned int samp if (ret) return ret; + ret = rockchip_pdm_clk_set_rate(pdm, pdm->clk_root, + pdm->clk_root_rate, 0); + if (ret) + return ret; + rate = pdm->clk_root_rate; - if (rate % clk_src) { + delta = abs(rate % clk_src - clk_src); + ppm = div64_u64((uint64_t)delta * 1000000, (uint64_t)rate); + + if (ppm) { div = DIV_ROUND_CLOSEST(pdm->clk_root_initial_rate, clk_src); if (!div) return -EINVAL; rate = clk_src * round_up(div, 2); - pdm->clk_ppm = 0; ret = clk_set_rate(pdm->clk_root, rate); if (ret) return ret; @@ -530,29 +570,6 @@ static const struct snd_kcontrol_new rockchip_pdm_controls[] = { }, }; -static int rockchip_pdm_clk_set_rate(struct rk_pdm_dev *pdm, - struct clk *clk, unsigned long rate) -{ - unsigned long rate_target; - int delta, ret; - - ret = rockchip_pll_clk_compensation(clk, pdm->clk_ppm); - if (!ret) - return ret; - - delta = (pdm->clk_ppm < 0) ? -1 : 1; - delta *= (int)div64_u64((uint64_t)rate * (uint64_t)abs(pdm->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_pdm_clk_compensation_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -578,22 +595,19 @@ static int rockchip_pdm_clk_compensation_get(struct snd_kcontrol *kcontrol, return 0; } - static int rockchip_pdm_clk_compensation_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) - { struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); struct rk_pdm_dev *pdm = snd_soc_dai_get_drvdata(dai); + int ppm = ucontrol->value.integer.value[0]; if ((ucontrol->value.integer.value[0] < CLK_PPM_MIN) || (ucontrol->value.integer.value[0] > CLK_PPM_MAX)) return -EINVAL; - pdm->clk_ppm = ucontrol->value.integer.value[0]; - - return rockchip_pdm_clk_set_rate(pdm, pdm->clk_root, pdm->clk_root_rate); + return rockchip_pdm_clk_set_rate(pdm, pdm->clk_root, pdm->clk_root_rate, ppm); } static struct snd_kcontrol_new rockchip_pdm_compensation_control = {