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

Pierre-Louis Bossart pierre-louis.bossart at linux.intel.com
Mon Aug 23 08:02:18 PDT 2021



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
> 
> 
> 



More information about the Linux-rockchip mailing list