[alsa-devel] [PATCH v4] ASoC: AMD: Enable/Disable auxiliary clock via common clock framework

Agrawal, Akshu Akshu.Agrawal at amd.com
Fri Mar 30 12:02:26 CEST 2018



On 3/27/2018 9:52 AM, Daniel Kurtz wrote:
> Hi Akshu
> 
> On Mon, Mar 26, 2018 at 3:12 AM Akshu Agrawal <akshu.agrawal at amd.com> wrote:
> 
>> This enables/disables and sets auxiliary clock at 25Mhz. It uses
>> common clock framework for proper ref counting. This approach will
>> save power in comparison to keeping it always On in firmware.
> 
>> V2: Correcting the pin to OSCOUT1 from OSCOUT2
>> V3: Fix error/warnings from kbuild test
>> V4: Fix build errors for platform which do not support CONFIG_COMMON_CLK
> 
>> TEST= aplay -vv <file>
>> check register to see clock enabled
>> kill aplay
>> check register to see clock disabled
> 
>> Signed-off-by: Akshu Agrawal <akshu.agrawal at amd.com>
>> Acked-by: Alex Deucher <alexander.deucher at amd.com>
>> ---
>>    sound/soc/amd/acp-da7219-max98357a.c | 144
> ++++++++++++++++++++++++++++++++++-
>>    1 file changed, 142 insertions(+), 2 deletions(-)
> 
>> diff --git a/sound/soc/amd/acp-da7219-max98357a.c
> b/sound/soc/amd/acp-da7219-max98357a.c
>> index 99c6b5c..3c77803 100644
>> --- a/sound/soc/amd/acp-da7219-max98357a.c
>> +++ b/sound/soc/amd/acp-da7219-max98357a.c
>> @@ -30,10 +30,13 @@
>>    #include <sound/soc-dapm.h>
>>    #include <sound/jack.h>
>>    #include <linux/clk.h>
>> +#include <linux/clkdev.h>
>> +#include <linux/clk-provider.h>
>>    #include <linux/gpio.h>
>>    #include <linux/module.h>
>>    #include <linux/i2c.h>
>>    #include <linux/acpi.h>
>> +#include <linux/types.h>
> 
>>    #include "../codecs/da7219.h"
>>    #include "../codecs/da7219-aad.h"
>> @@ -41,6 +44,20 @@
>>    #define CZ_PLAT_CLK 24000000
>>    #define MCLK_RATE 24576000
>>    #define DUAL_CHANNEL           2
>> +#define CLKDRVSTR2     0x28
>> +#define MISCCLKCNTL1   0x40
>> +#define OSCCLKENB      2
>> +#define OSCOUT1CLK25MHZ        16
> 
> These defines do not belong with the clocks above them, so please separate
> them.
> It might be helpful to add a comment describing what these addresses are,
> ie that they are registers of the SoC.
> 

Accepted.

>> +
>> +struct cz_clock {
>> +       const char* acp_clks_name;
> 
> Why store this in the struct?
> 

need to get res_base in clk_enable. Hence, better to keep them
in one struct

>> +#ifdef CONFIG_COMMON_CLK
>> +       struct clk_hw acp_clks_hw;
>> +#endif
>> +       struct clk_lookup *acp_clks_lookup;
> 
> Why store this in the struct?
> 
>> +       struct clk *acp_clks;
> 
> Why store this in the struct?
> 
>> +       void __iomem *res_base;
>> +};
> 
>>    static struct snd_soc_jack cz_jack;
>>    struct clk *da7219_dai_clk;
>> @@ -91,6 +108,8 @@ static int cz_da7219_hw_params(struct
> snd_pcm_substream *substream,
>>    {
>>           int ret = 0;
>>           struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> +       struct snd_soc_card *card = rtd->card;
>> +       struct clk *acpd7219_clk;
> 
>>           ret = clk_prepare_enable(da7219_dai_clk);
>>           if (ret < 0) {
>> @@ -98,13 +117,27 @@ static int cz_da7219_hw_params(struct
> snd_pcm_substream *substream,
>>                   return ret;
>>           }
> 
>> +       acpd7219_clk = clk_get(card->dev, "acpd7219-clks");
>> +       ret = clk_prepare_enable(acpd7219_clk);
>> +       if (ret < 0) {
>> +               dev_err(rtd->dev, "can't enable oscillator clock %d\n",
> ret);
>> +               return ret;
>> +       }
>> +
>>           return ret;
>>    }
> 
>>    static int cz_da7219_hw_free(struct snd_pcm_substream *substream)
>>    {
>> +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> +       struct snd_soc_card *card = rtd->card;
>> +       struct clk *acpd7219_clk;
>> +
>>           clk_disable_unprepare(da7219_dai_clk);
> 
>> +       acpd7219_clk = clk_get(card->dev, "acpd7219-clks");
>> +       clk_disable_unprepare(acpd7219_clk);
>> +
>>           return 0;
>>    }
> 
>> @@ -237,9 +270,113 @@ static int cz_fe_startup(struct snd_pcm_substream
> *substream)
>>           .num_controls = ARRAY_SIZE(cz_mc_controls),
>>    };
> 
>> +#ifdef CONFIG_COMMON_CLK
>> +static int acpd7219_clks_prepare(struct clk_hw *hw)
> 
> Should setting OSCCLKENB be the clock enable rather than prepare?
> 

Accepted.

>> +{
>> +       u32 reg_val;
>> +       struct cz_clock *cz_clock_obj =
>> +               container_of(hw, struct cz_clock, acp_clks_hw);
> 
> nit: just "cz_clock" should be enough for all of these.
>

build failure with just cz_clock

>> +       void __iomem *base = cz_clock_obj->res_base;
>> +
>> +       reg_val = readl(base + MISCCLKCNTL1);
>> +       reg_val &= ~(0x1 << OSCCLKENB);
>> +       writel(reg_val, base + MISCCLKCNTL1);
>> +       reg_val = readl(base + CLKDRVSTR2);
>> +       reg_val |= (0x1 << OSCOUT1CLK25MHZ);
>> +       writel(reg_val, base + CLKDRVSTR2);
> 
> Writing OSCOUT1CLK25MHZ sets the clock to 25 MHz (ie, instead of 48 MHz).
> So, technically this part should probably be in a set_rate() rather than
> prepare() [and reject other frequencies]?
>

We need to set it always at 25Mhz. We initialize sysclk and set pll on 
that frequency. Though we are setting rate of clock but parameters rate 
and parent_rate will be of no use. Its more like enabling a clock at as 
fixed rate.

>> +
>> +       return 0;
>> +}
>> +
>> +static void acpd7219_clks_unprepare(struct clk_hw *hw)
> 
> Similarly, can this be disable()?
> 

Accepted.

>> +{
>> +       u32 reg_val;
>> +       struct cz_clock *cz_clock_obj =
>> +               container_of(hw, struct cz_clock, acp_clks_hw);
>> +       void __iomem *base = cz_clock_obj->res_base;
>> +
>> +       reg_val = readl(base + MISCCLKCNTL1);
>> +       reg_val |= (0x1 << OSCCLKENB);
>> +       writel(reg_val, base + MISCCLKCNTL1);
>> +}
>> +
>> +static int acpd7219_clks_is_enabled(struct clk_hw *hw)
>> +{
>> +       u32 reg_val;
>> +       struct cz_clock *cz_clock_obj =
>> +               container_of(hw, struct cz_clock, acp_clks_hw);
>> +       void __iomem *base = cz_clock_obj->res_base;
>> +
>> +       reg_val = readl(base + MISCCLKCNTL1);
>> +
>> +       return !(reg_val & OSCCLKENB);
>> +}
>> +
>> +const struct clk_ops acpd7219_clks_ops = {
> 
> static const ...
> 

Accepted.

>> +       .prepare = acpd7219_clks_prepare,
>> +       .unprepare = acpd7219_clks_unprepare,
>> +       .is_enabled = acpd7219_clks_is_enabled,
>> +};
>> +
>> +static int register_acpd7219_clocks(struct platform_device *pdev)
> 
> Can this be:
> __init
> 

No, seems there is code calling code in this function.

>> +{
>> +       struct clk_init_data init = {};
>> +       struct clk *acp_clks;
>> +       struct clk_lookup *acp_clks_lookup;
>> +       struct cz_clock *cz_clock_obj;
>> +       struct resource *res;
>> +       struct device dev = pdev->dev;
>> +
>> +       cz_clock_obj = kzalloc(sizeof(struct cz_clock), GFP_KERNEL);
>> +       if (!cz_clock_obj)
>> +               return -ENOMEM;
>> +
>> +       cz_clock_obj->acp_clks_name = "acpd7219-clks";
>> +
>> +       init.parent_names = NULL;
>> +       init.num_parents = 0;
>> +       init.name = cz_clock_obj->acp_clks_name;
>> +       init.ops = &acpd7219_clks_ops;
> 
> I think you can just do this all during: "struct clk_init_data init = { }".
> In fact, can't this done like this instead:
> 
> static const struct clk_init_data __initconst = {
>     .name = "acpd7219-clks";
>     .ops = &acpd7219_clks_ops;
> };
>

Accepted.

>> +       cz_clock_obj->acp_clks_hw.init = &init;
>> +
>> +       acp_clks = devm_clk_register(&dev, &cz_clock_obj->acp_clks_hw);
>> +       if (IS_ERR(acp_clks))
>> +       {
>> +               dev_err(&dev, "Failed to register DAI clocks: %ld\n",
>> +                        PTR_ERR(acp_clks));
>> +               return -EINVAL;
> 
> propagate PTR_ERR(acp_clks), instead?
> 

Accepted.

>> +       }
>> +       cz_clock_obj->acp_clks = acp_clks;
>> +
>> +       acp_clks_lookup = clkdev_create(acp_clks,
> cz_clock_obj->acp_clks_name,
>> +                                       "%s", dev_name(&dev));
>> +       if (!acp_clks_lookup)
>> +               dev_warn(&dev, "Failed to create DAI clkdev");
>> +       else
>> +               cz_clock_obj->acp_clks_lookup =
> acp_clks_lookup;clkdev_create
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       if (!res) {
>> +               dev_err(&pdev->dev, "Failed to get misc io resource.\n");
>> +               return -EINVAL;
>> +       }
>> +       cz_clock_obj->res_base = devm_ioremap_nocache(&pdev->dev,
> res->start,
>> +                                       resource_size(res));
>> +       if (!cz_clock_obj->res_base)
>> +               return -ENOMEM;
>> +
>> +       return 0;
>> +}
>> +#else
>> +static int register_acpd7219_clocks(struct platform_device *pdev)
>> +{
>> +       return 0;
>> +}
>> +#endif /* CONFIG_COMMON_CLK */
>> +
>>    static int cz_probe(struct platform_device *pdev)
>>    {
>> -       int ret;
>> +       int ret = 0;
> 
> Do not initialize this before it is used.
>

Accepted.

>>           struct snd_soc_card *card;
> 
>>           card = &cz_card;
>> @@ -252,7 +389,10 @@ static int cz_probe(struct platform_device *pdev)
>>                                   cz_card.name, ret);
>>                   return ret;
>>           }
>> -       return 0;
>> +
>> +       ret = register_acpd7219_clocks(pdev);
>> +
>> +       return ret;
> 
> Just:
>     return register_acpd7219_clocks(pdev);
> 

Accepted.

> Thanks,
> -Dan
> 
>>    }
> 
>>    static const struct acpi_device_id cz_audio_acpi_match[] = {
>> --
>> 1.9.1


More information about the Alsa-devel mailing list