[alsa-devel] [PATCH 2/2] ASoC: AMD: Enable/Disable auxiliary clock via common clock framework
Daniel Kurtz
djkurtz at chromium.org
Wed Apr 11 20:14:06 CEST 2018
On Wed, Apr 11, 2018 at 4:08 AM Agrawal, Akshu <Akshu.Agrawal at amd.com>
wrote:
> On 4/10/2018 11:13 AM, Daniel Kurtz wrote:
> >> On 4/4/2018 12:27 AM, Daniel Kurtz wrote:
> >>> Hi Akshu,
> >>>
> >>> On Fri, Mar 30, 2018 at 4:05 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.
> >>>
> >>> I have some general concern with the approach in this patch:
> >>>
> >>> (1) The 25 MHz clock being created here is a general system clock
> >>> resource. I think it should be created in an SoC clock driver, rather
> > than
> >>> here in an audio machine driver. This has several advantages:
> >>> (a) the clock resource would be available for other boards that
use
> > it but
> >>> don't use this particular audio machine driver
> >>> (b) the clock would be instantiated at boot, rather than at
machine
> > driver
> >>> load.
> >>> (c) the machine driver would not then need the additional clkdev
> > code
> >>>
> >>> (2) The oscout1 clock is a pretty standard clk_gate with a clk_mux.
We
> > can
> >>> use some standard helpers to better model the clock tree rather than
> >>> rewriting them:
> >>>
> >>> static struct clk *clk_48m;
> >>> static struct clk *clk_25m;
> >>> static struct clk *clk_oscout1_mux;
> >>> static struct clk *clk_oscout1;
> >>>
> >>> static const char *clk_oscout1_parents[] = { "clk48MHz", "clk25MHz" };
> >>>
> >>> {
> >>> clk_48m = clk_register_fixed_rate(dev, "clk48MHz", NULL, 0,
> > 48000000);
> >>> clk_25m = clk_register_fixed_rate(dev, "clk25MHz", NULL, 0,
> > 25000000);
> >>>
> >>> res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >>> res_base = devm_ioremap_nocache(dev, res->start,
> > resource_size(res));
> >>>
> >>> clk_oscout1_mux = clk_register_mux(dev, "oscout1_mux",
> >>> clk_oscout1_parents, ARRAY_SIZE(clk_oscout1_parents),
> >>> 0, res_base + CLKDRVSTR2, OSCOUT1CLK25MHZ, 3, 0, NULL);
> >>>
> >>> clk_set_parent(clk_oscout1_mux, clk_25m);
> >>>
> >>> clk_oscout1 = clk_register_gate(dev, "oscout1", "oscout1_mux",
0,
> >>> res_base + MISCCLKCNTL1, OSCCLKENB, 0, NULL);
> >>>
> >>>
> >>> (3) IIUC, the 25 MHz clock (oscout1) is actually the da7219's mclk.
The
> >>> da7219 already
> >>> has infrastructure to enable/disable its mclk as needed. Once we
> > establish
> >>> the system clock,
> >>> we can then wire this clock up to the da7219 via ACPI / devicetree
> >>> properties.
> >>>
> >> The above design is something used in almost all ARM based platform
> >> where we define all mux, pll, divider and gate and then each device
uses
> >> them though the devicetree.
> >> The clock here are controlled in firmware. Enable/disable is handled in
> >> firmware, where this being the exception case. Hence, I don't think it
> >> will be of much value add.
> >
> > The value add to properly modeling the SoC's clock tree is highlighted
> > above.
> > In addition to making the code more understandable, and the SoC easier
to
> > use for other designs in the future, the clock tree as expressed via
> > debugfs (/sys/kernel/debug/clk/clk_summary) will make more sense.
> > In the particular case of the acp-da7219-max98357a.c machine driver and
> > da7219 codec driver, properly modeling the oscout1 clock tree as a
> > fixed_clks+mux+gate and then passing it to da7219 as its mclk will allow
> > the mclk driver to do what it expects using its existing code - set the
> > mclk rate (via oscout1's parent mux selector) and then enable/disable as
> > needed.
> >
> >> For ST/CZ we have other platforms which do not use this clock. They
have
> >> a dedicated Oscillator for the codec.
> >
> > Exactly, such platforms will just not use this machine driver and this
> > clock will be defined properly, but remain in the state programmed by
> > firmware.
> >
> >> If you think its beneficial for other platform based on ST/CZ then
maybe
> >> we can take this as a separate activity. Where we move the clocks to a
> >> common file and use the same framework.
> >
> > Moving the clock configuration out of acp-da7219-max98357a.c allows this
> > system clock to be available independent of when this machine driver
kernel
> > module is loaded.
> >
> I understand the advantages of having it moved to drivers/clk and also
> connecting it to d7219's mclk. This is good to have feature for other
> ST/CZ platforms. Would take it up this as a separate activity.
I don't think this is a separate activity. It is an integral part of the
structure of this patch. Let's fix it up now while we are actively
thinking about this driver.
> >>>
> >>> Enable on hw_params() and disable on hw_free() doesn't sound balanced?
> >>> Will there always be exactly one hw_free() for every hw_params()?
> >>>
> >> Yes, all resources allocated by hw_paramns are freed in hw_free.
> >
> > Right, but is it possible for hw_params() to get called mulitple times
> > *without* hw_free()?
> > This would cause more enables than disables.
> >
> > Documentation/sound/kernel-api/writing-an-alsa-driver.rst says:
> > """
> > Note that this and ``prepare`` callbacks may be called multiple times
> > per initialization. For example, the OSS emulation may call these
> > callbacks at each change via its ioctl.
> >
> > Thus, you need to be careful not to allocate the same buffers many
> > times, which will lead to memory leaks! Calling the helper function
> > above many times is OK. It will release the previous buffer
> > automatically when it was already allocated.
> > """
> >
> I get there is a remote possibility that hw_params maybe called multiple
> times without hw_free (though I haven't come across it in my testing).
> The other option was to use trigger. But, that is not possible as kernel
> crashes when we call clk_prepare_enable(da7219_dai_clk) from trigger
> registered to a non da7219 codec, like of max98357
Using trigger won't work because you are not guaranteed to have symetric
start/stop events. In fact , there is a comment in the definition of
struct snd_soc_dai_ops that specifically says this:
/*
* NOTE: Commands passed to the trigger function are not necessarily
* compatible with the current state of the dai. For example this
* sequence of commands is possible: START STOP STOP.
* So do not unconditionally use refcounting functions in the trigger
* function, e.g. clk_enable/disable.
*/
int (*trigger)(struct snd_pcm_substream *, int, struct snd_soc_dai *);
Instead, can you try to use startup() / shutdown()?
Those callbacks are non-atomic and should correspond to open()/close() on
the corresponding audio device, which I think is what we want.
-Dan
More information about the Alsa-devel
mailing list