Re: [PATCH v3 08/15] ASoC: tegra: Add audio mclk control through clk_out_1 and extern1

From: Dmitry Osipenko
Date: Tue Dec 17 2019 - 10:37:09 EST


17.12.2019 04:29, Sowjanya Komatineni ÐÐÑÐÑ:
>
> On 12/7/19 11:20 AM, Sowjanya Komatineni wrote:
>>
>> On 12/7/19 6:58 AM, Dmitry Osipenko wrote:
>>> 06.12.2019 05:48, Sowjanya Komatineni ÐÐÑÐÑ:
>>>> Current ASoC driver uses extern1 as cdev1 clock from Tegra30 onwards
>>>> through device tree.
>>>>
>>>> Actual audio mclk is clk_out_1 and to use PLLA for mclk rate control,
>>>> need to clk_out_1_mux parent to extern1 and extern1 parent to
>>>> PLLA_OUT0.
>>>>
>>>> Currently Tegra clock driver init sets the parents and enables both
>>>> clk_out_1 and extern1 clocks. But these clocks parent and enables
>>>> should
>>>> be controlled by ASoC driver.
>>>>
>>>> Clock parents can be specified in device tree using assigned-clocks
>>>> and assigned-clock-parents.
>>>>
>>>> To enable audio mclk, both clk_out_1 and extern1 clocks need to be
>>>> enabled.
>>>>
>>>> This patch configures parents for clk_out_1 and extern1 clocks if
>>>> device
>>>> tree does not specify clock parents inorder to support old device tree
>>>> and controls mclk using both clk_out_1 and extern1 clocks.
>>>>
>>>> Signed-off-by: Sowjanya Komatineni <skomatineni@xxxxxxxxxx>
>>>> ---
>>>> Â sound/soc/tegra/tegra_asoc_utils.c | 66
>>>> ++++++++++++++++++++++++++++++++++++++
>>>> Â sound/soc/tegra/tegra_asoc_utils.h |Â 1 +
>>>> Â 2 files changed, 67 insertions(+)
>>>>
>>>> diff --git a/sound/soc/tegra/tegra_asoc_utils.c
>>>> b/sound/soc/tegra/tegra_asoc_utils.c
>>>> index 536a578e9512..8e3a3740df7c 100644
>>>> --- a/sound/soc/tegra/tegra_asoc_utils.c
>>>> +++ b/sound/soc/tegra/tegra_asoc_utils.c
>>>> @@ -60,6 +60,7 @@ int tegra_asoc_utils_set_rate(struct
>>>> tegra_asoc_utils_data *data, int srate,
>>>> ÂÂÂÂÂ data->set_mclk = 0;
>>>> Â ÂÂÂÂÂ clk_disable_unprepare(data->clk_cdev1);
>>>> +ÂÂÂ clk_disable_unprepare(data->clk_extern1);
>>>> ÂÂÂÂÂ clk_disable_unprepare(data->clk_pll_a_out0);
>>>> ÂÂÂÂÂ clk_disable_unprepare(data->clk_pll_a);
>>>> Â @@ -89,6 +90,14 @@ int tegra_asoc_utils_set_rate(struct
>>>> tegra_asoc_utils_data *data, int srate,
>>>> ÂÂÂÂÂÂÂÂÂ return err;
>>>> ÂÂÂÂÂ }
>>>> Â +ÂÂÂ if (!IS_ERR_OR_NULL(data->clk_extern1)) {
>>>> +ÂÂÂÂÂÂÂ err = clk_prepare_enable(data->clk_extern1);
>>>> +ÂÂÂÂÂÂÂ if (err) {
>>>> +ÂÂÂÂÂÂÂÂÂÂÂ dev_err(data->dev, "Can't enable extern1: %d\n", err);
>>>> +ÂÂÂÂÂÂÂÂÂÂÂ return err;
>>>> +ÂÂÂÂÂÂÂ }
>>>> +ÂÂÂ }
>>>> +
>>>> ÂÂÂÂÂ err = clk_prepare_enable(data->clk_cdev1);
>>>> ÂÂÂÂÂ if (err) {
>>>> ÂÂÂÂÂÂÂÂÂ dev_err(data->dev, "Can't enable cdev1: %d\n", err);
>>>> @@ -109,6 +118,7 @@ int tegra_asoc_utils_set_ac97_rate(struct
>>>> tegra_asoc_utils_data *data)
>>>> ÂÂÂÂÂ int err;
>>>> Â ÂÂÂÂÂ clk_disable_unprepare(data->clk_cdev1);
>>>> +ÂÂÂ clk_disable_unprepare(data->clk_extern1);
>>>> ÂÂÂÂÂ clk_disable_unprepare(data->clk_pll_a_out0);
>>>> ÂÂÂÂÂ clk_disable_unprepare(data->clk_pll_a);
>>>> Â @@ -142,6 +152,14 @@ int tegra_asoc_utils_set_ac97_rate(struct
>>>> tegra_asoc_utils_data *data)
>>>> ÂÂÂÂÂÂÂÂÂ return err;
>>>> ÂÂÂÂÂ }
>>>> Â +ÂÂÂ if (!IS_ERR_OR_NULL(data->clk_extern1)) {
>>>> +ÂÂÂÂÂÂÂ err = clk_prepare_enable(data->clk_extern1);
>>>> +ÂÂÂÂÂÂÂ if (err) {
>>>> +ÂÂÂÂÂÂÂÂÂÂÂ dev_err(data->dev, "Can't enable extern1: %d\n", err);
>>>> +ÂÂÂÂÂÂÂÂÂÂÂ return err;
>>>> +ÂÂÂÂÂÂÂ }
>>>> +ÂÂÂ }
>>> Why this is needed given that clk_extern1 is either a child of MCLK or
>>> MCLK itself (on T20)? The child clocks are enabled when the parent is
>>> enabled.
>>
>> For T30 and later, clk_extern1 is one of the source for clk_out_1_mux.
>> clk_extern1 is in CAR and it has its own gate and mux.
>>
>> As audio mclk related clocks (clk_out_1, clk_out_1_mux, and extern1)
>> are moved into ASoC driver from clock driver
>>
>> need to enable extern1 gate as well along with clk_out1 for T30
>> through T210.
>>
>> Just FYI, extern1 enable here happens only when data->clk_extern1 is
>> available which is for T30 onwards.
>>
>>>> ÂÂÂÂÂ err = clk_prepare_enable(data->clk_cdev1);
>>>> ÂÂÂÂÂ if (err) {
>>>> ÂÂÂÂÂÂÂÂÂ dev_err(data->dev, "Can't enable cdev1: %d\n", err);
>>>> @@ -158,6 +176,7 @@ EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_ac97_rate);
>>>> Â int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
>>>> ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ struct device *dev)
>>>> Â {
>>>> +ÂÂÂ struct clk *clk_out_1_mux;
>>>> ÂÂÂÂÂ int ret;
>>>> Â ÂÂÂÂÂ data->dev = dev;
>>>> @@ -196,6 +215,51 @@ int tegra_asoc_utils_init(struct
>>>> tegra_asoc_utils_data *data,
>>>> ÂÂÂÂÂÂÂÂÂ goto err_put_pll_a_out0;
>>>> ÂÂÂÂÂ }
>>> In a previous patch you added fallback to EXTPERIPH when clk_get(MCLK)
>>> fails. This will work perfectly fine for the older kernels which have
>>> all clocks in the same single CaR driver, but this may not work that
>>> great for the newer kernels because PMC driver isn't registered early
>>> during boot and thus it is possible to get a legit -EPROBE_DEFER which
>>> shouldn't be ignored. In other words, you need to add into this patch a
>>> check for the error code returned by clk_get(MCLK) and fallback only for
>>> -EINVAL.
>> yeah right, will add check in next version.
>>>> +ÂÂÂ /*
>>>> +ÂÂÂÂ * If clock parents are not set in DT, configure here to use
>>>> clk_out_1
>>>> +ÂÂÂÂ * as mclk and extern1 as parent for Tegra30 and higher.
>>>> +ÂÂÂÂ */
>>>> +ÂÂÂ if (!of_find_property(dev->of_node, "assigned-clock-parents",
>>>> NULL) &&
>>>> +ÂÂÂÂÂÂÂ data->soc > TEGRA_ASOC_UTILS_SOC_TEGRA20) {
>>>> +ÂÂÂÂÂÂÂ data->clk_extern1 = clk_get_sys("clk_out_1", "extern1");
>>>> +ÂÂÂÂÂÂÂ if (IS_ERR(data->clk_extern1)) {
>>>> +ÂÂÂÂÂÂÂÂÂÂÂ dev_err(data->dev, "Can't retrieve clk extern1\n");
>>>> +ÂÂÂÂÂÂÂÂÂÂÂ ret = PTR_ERR(data->clk_extern1);
>>>> +ÂÂÂÂÂÂÂÂÂÂÂ goto err_put_cdev1;
>>>> +ÂÂÂÂÂÂÂ }
>>>> +
>>>> +ÂÂÂÂÂÂÂ ret = clk_set_parent(data->clk_extern1, data->clk_pll_a_out0);
>>>> +ÂÂÂÂÂÂÂ if (ret < 0) {
>>>> +ÂÂÂÂÂÂÂÂÂÂÂ dev_err(data->dev,
>>>> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ "Set parent failed for clk extern1: %d\n",
>>>> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ ret);
>>>> +ÂÂÂÂÂÂÂÂÂÂÂ goto err_put_cdev1;
>>>> +ÂÂÂÂÂÂÂ }
>>>> +
>>>> +ÂÂÂÂÂÂÂ clk_out_1_mux = clk_get_sys(NULL, "clk_out_1_mux");
>>> Note1: clk_get(dev, "clk_out_1_mux") should work here by letting clk
>>> core to fall back to the clk_get_sys() by itself. Either way should
>>> be good.
>
> clk_get uses device rather and dev_id will be name of this device and
> when clk_get fall back to __clk_get_sys() it still will use dev id of
> this device rather than actual dev_id that pmc clocks are added to the
> lookup. So clk_get_sys() seems to be correct to use as we can specify
> exact dev_id and con_id.

It should be better to use something "resource managed", thus
devm_clk_get() should be a better choice.

> Also, clk_find retrieves clock from lookup only when it finds matching
> clock with both dev_id and con_id as pmc clocks are registered with both
> dev_id and con_id.
>
> I see existing clock driver adds both extern and pmc clocks (clk_out) to
> lookup with same dev_id of clk_out_1/2/3 and con_id of extern1/2/3 and
> with this always extern clock will be retrieved and this is probably
> because old DT and audio driver always uses extern1 rather than actual
> clk_out_1
>
> But this need to be fixed now as we changed to use clk_out directly
> rather than extern (even for other pmc clocks) to match actual hw design.
>
> Will fix this as well to register pmc clocks using con_id as
> clk_out_1/2/3 in pmc driver and extern clocks using con_id of
> extern1/2/3 with dev_id being NULL so we can retrieve these clocks by
> just using con_id only using clk_get_sys as we switched to use clk_out_1
> directly as pmc clock rather than extern from DT and no longer need to
> pair pmc clocks to extern clocks.

I'm not sure it's worth the effort to care about con_ids if implicit
fallback to clk_get_sys(NULL, "...") does the right thing for the audio
driver.

IIRC, CCF uses variant of matching clocks by names, although I'm not
sure whether that applies to older stable kernels.

[snip]