Re: [PATCH v2 1/4] ASoC: rockchip: add support for i2s-tdm controller

From: Pierre-Louis Bossart
Date: Mon Aug 23 2021 - 11:02:51 EST




On 8/21/21 3:45 PM, Nicolas Frattaroli wrote:
> On Freitag, 20. August 2021 21:02:16 CEST Pierre-Louis Bossart wrote:
>>> + regmap_read(i2s_tdm->regmap, I2S_CLR, &val);
>>> + /* Wait on the clear operation to finish */
>>> + while (val) {
>>
>> delay needed here?
>>
>
> The rockchip_i2s.c code doesn't have a delay here either, but I can
> add one of 1 usec for good measure, it seems weird to retry the
> read as fast as it can.

yep.

>>> +static int rockchip_i2s_tdm_clk_set_rate(struct rk_i2s_tdm_dev *i2s_tdm,
>>> + struct clk *clk, unsigned long rate,
>>> + int ppm)
>>> +{
>>> + unsigned long rate_target;
>>> + int delta, ret;
>>> +
>>> + if (ppm == i2s_tdm->clk_ppm)
>>> + return 0;
>>> +
>>> + delta = (ppm < 0) ? -1 : 1;
>>> + delta *= (int)div64_u64((u64)rate * (u64)abs(ppm) + 500000,
>>> + 1000000);
>>
>> formula looks odd? looks like you are implementing a round to nearest
>> operation, but that shouldn't require this multiplication?
>>
>
> I believe the multiplication is there to compensate for clock drift.
> ppm is a value between -1000 and 1000 that specifies the clock drift
> in presumably parts per million, going by the variable name.

I meant using a signed division with lsb round-to-nearest, something like:

delta = (int)div64_u64((u64)rate * (u64)(ppm) + 500000,
1000000);

>
>>> + pm_runtime_enable(&pdev->dev);
>>> + if (!pm_runtime_enabled(&pdev->dev)) {
>>> + ret = i2s_tdm_runtime_resume(&pdev->dev);
>>
>> that looks like dead code? you've just enabled pm_runtime, why would
>> this fail?
>>
>
> I've had a look at the upstream rockchip_i2s.c code which does the
> same thing, and I believe the idea here is that we need to manually
> prepare and enable the master clocks (mclk_rx/mclk_tx) if pm_runtime
> is not available. Otherwise, pm_runtime will presumably call our
> resume callback at some point.
>
> If runtime power management is disabled in the kernel config then
> pm_runtime_enabled is always going to return false.

that seems very odd. why not enable the clocks by default and let them
stop in suspend.

>>> +err_suspend:
>>> + if (!pm_runtime_status_suspended(&pdev->dev))
>>> + i2s_tdm_runtime_suspend(&pdev->dev);
>>
>> why is this necessary?
>
> I believe this is the same kind of situation as before, and the
> other driver does this too: if pm_runtime is not available, we
> need to stop our clocks manually on probe failure.

then use pm_runtime_disable() and manually stop the clocks...

>>> +err_pm_disable:
>>> + pm_runtime_disable(&pdev->dev);>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static int rockchip_i2s_tdm_remove(struct platform_device *pdev)
>>> +{
>>> + struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(&pdev->dev);
>>> +
>>> + pm_runtime_disable(&pdev->dev);
>>> + if (!pm_runtime_status_suspended(&pdev->dev))
>>> + i2s_tdm_runtime_suspend(&pdev->dev);
>>
>> this looks backwards, if you disable pm_runtime first what is the
>> expectation for the rest.
>
> I'm not well versed in the PM code but if my theory of this being
> related to unavailable PM is correct, then my best guess is that
> pm_runtime_disable does suspend the device, so if it's not
> suspended then we don't have pm_runtime and therefore need to call
> it manually.

I think this is really doing things backwards. You want to
unconditionally enable all resources on probe, and let them go to idle
when no one needs them - or if pm_runtime is disabled.

>>> +
>>> + if (!IS_ERR(i2s_tdm->mclk_tx))
>>> + clk_prepare_enable(i2s_tdm->mclk_tx);
>>> + if (!IS_ERR(i2s_tdm->mclk_rx))
>>> + clk_prepare_enable(i2s_tdm->mclk_rx);
>>> + if (!IS_ERR(i2s_tdm->hclk))
>>> + clk_disable_unprepare(i2s_tdm->hclk);
>>> +
>>> + return 0;>>> +}
>>> +
>>> +#ifdef CONFIG_PM_SLEEP
>>
>> use __maybe_unused
>
> You mean instead of the ifdef stuff to just add this attribute to
> the following functions like this?
>
> static int rockchip_i2s_tdm_suspend(struct device *dev) __maybe_unused

yes

>
>>
>>> +static int rockchip_i2s_tdm_suspend(struct device *dev)
>>> +{
>>> + struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
>>> +
>>> + regcache_mark_dirty(i2s_tdm->regmap);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int rockchip_i2s_tdm_resume(struct device *dev)
>>> +{
>>> + struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
>>> + int ret;
>>> +
>>> + ret = pm_runtime_get_sync(dev);
>>> + if (ret < 0)
>>> + return ret;
>>> + ret = regcache_sync(i2s_tdm->regmap);
>>> + pm_runtime_put(dev);
>>> +
>>> + return ret;
>>> +}
>>> +#endif
>
> Thank you for your review!
>
> Regards,
> Nicolas Frattaroli
>
>
>