diff mbox

[v5,1/6] ASoC: Intel: Skylake: Add ssp clock driver

Message ID 1512978390-8848-2-git-send-email-sriramx.periyasamy@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Sriram Periyasamy Dec. 11, 2017, 7:46 a.m. UTC
For certain platforms, it is required to start the clocks (mclk/sclk/fs)
before the stream start. Example: for few chrome systems, codec needs the
mclk/sclk to be enabled early for a successful clock synchronization and
for few IVI platforms, clock need to be enabled at boot and should be ON
always.

Add the required structures and create set_dma_control ipc to enable or
disable the clock. To enable sclk without fs, mclk ipc structure is used,
else sclkfs ipc structure is used.

Clock prepare/unprepare are used to enable/disable the clock as the IPC
will be sent in non-atomic context. The clk set_dma_control IPC
structures are populated during the set_rate callback and IPC is sent
to enable the clock during prepare callback.

This patch creates virtual clock driver, which allows the machine driver
to use the clock interface to send IPCs to DSP to enable/disable the
clocks.

Signed-off-by: Sriram Periyasamy <sriramx.periyasamy@intel.com>
Signed-off-by: Jaikrishna Nemallapudi <jaikrishnax.nemallapudi@intel.com>
Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
---
 sound/soc/intel/Kconfig                |   3 +
 sound/soc/intel/skylake/Makefile       |   5 +
 sound/soc/intel/skylake/skl-messages.c |   1 +
 sound/soc/intel/skylake/skl-ssp-clk.c  | 473 +++++++++++++++++++++++++++++++++
 sound/soc/intel/skylake/skl-ssp-clk.h  |  38 +++
 sound/soc/intel/skylake/skl.h          |   6 +
 6 files changed, 526 insertions(+)
 create mode 100644 sound/soc/intel/skylake/skl-ssp-clk.c

Comments

Stephen Boyd Dec. 13, 2017, 10:30 p.m. UTC | #1
On 12/11, Sriram Periyasamy wrote:
> +
> +static int skl_clk_set_rate(struct clk_hw *hw, unsigned long rate,
> +					unsigned long parent_rate)
> +{
> +	struct skl_clk *clkdev = to_skl_clk(hw);
> +	struct skl_clk_rate_cfg_table *rcfg;
> +	int clk_type;
> +
> +	if (!clkdev)
> +		return -ENODEV;

These checks don't make sense. container_of() on clk_hw
structures returning NULL wouldn't happen.

> +
> +	if (!rate)
> +		return -EINVAL;
> +
> +	if (__clk_is_enabled(hw->clk) && (clkdev->rate != rate))

Any chance you can directly read the hardware instead of going
through the framework to find out if the clk is enabled? Seems
circular to do it this way.

> +		return -EBUSY;
> +
> +	rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
> +							rate);
> +	if (!rcfg)
> +		return -EINVAL;
> +
> +	clk_type = skl_get_clk_type(clkdev->id);
> +	if (clk_type < 0)
> +		return clk_type;
> +
> +	skl_fill_clk_ipc(rcfg, clk_type);
> +	clkdev->rate = rate;
> +
> +	return 0;
> +}
> +
> +static unsigned long skl_clk_recalc_rate(struct clk_hw *hw,
> +				unsigned long parent_rate)
> +{
> +	struct skl_clk *clkdev = to_skl_clk(hw);
> +	struct skl_clk_rate_cfg_table *rcfg;
> +	int clk_type;
> +
> +	if (!clkdev)
> +		return 0;
> +
> +	if (clkdev->rate)
> +		return clkdev->rate;

Why is the rate being cached? We should always be able to
calculate the rate based on parent_rate that gets passed to this
function?

> +
> +	rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
> +					parent_rate);
> +	if (!rcfg)
> +		return 0;
> +
> +	clk_type = skl_get_clk_type(clkdev->id);
> +	if (clk_type < 0)
> +		return 0;
> +
> +	skl_fill_clk_ipc(rcfg, clk_type);
> +	clkdev->rate = rcfg->rate;
> +
> +	return clkdev->rate;
> +}
> +
> +/* Not supported by clk driver. Implemented to satisfy clk fw */
> +long skl_clk_round_rate(struct clk_hw *hw, unsigned long rate,
> +				unsigned long *parent_rate)
> +{
> +	return rate;
> +}
Subhransu S. Prusty Dec. 18, 2017, 3:57 a.m. UTC | #2
On Wed, Dec 13, 2017 at 02:30:32PM -0800, Stephen Boyd wrote:
> On 12/11, Sriram Periyasamy wrote:
> > +
> > +static int skl_clk_set_rate(struct clk_hw *hw, unsigned long rate,
> > +					unsigned long parent_rate)
> > +{
> > +	struct skl_clk *clkdev = to_skl_clk(hw);
> > +	struct skl_clk_rate_cfg_table *rcfg;
> > +	int clk_type;
> > +
> > +	if (!clkdev)
> > +		return -ENODEV;
> 
> These checks don't make sense. container_of() on clk_hw
> structures returning NULL wouldn't happen.

Sure. will remove.

> 
> > +
> > +	if (!rate)
> > +		return -EINVAL;
> > +
> > +	if (__clk_is_enabled(hw->clk) && (clkdev->rate != rate))
> 
> Any chance you can directly read the hardware instead of going
> through the framework to find out if the clk is enabled? Seems

No. This involves sending an IPC to DSP to enable clock and interpreting the
return error code. I would like to avoid doing this here in set_rate.

> circular to do it this way.
> 
> > +		return -EBUSY;
> > +
> > +	rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
> > +							rate);
> > +	if (!rcfg)
> > +		return -EINVAL;
> > +
> > +	clk_type = skl_get_clk_type(clkdev->id);
> > +	if (clk_type < 0)
> > +		return clk_type;
> > +
> > +	skl_fill_clk_ipc(rcfg, clk_type);
> > +	clkdev->rate = rate;
> > +
> > +	return 0;
> > +}
> > +
> > +static unsigned long skl_clk_recalc_rate(struct clk_hw *hw,
> > +				unsigned long parent_rate)
> > +{
> > +	struct skl_clk *clkdev = to_skl_clk(hw);
> > +	struct skl_clk_rate_cfg_table *rcfg;
> > +	int clk_type;
> > +
> > +	if (!clkdev)
> > +		return 0;
> > +
> > +	if (clkdev->rate)
> > +		return clkdev->rate;
> 
> Why is the rate being cached? We should always be able to
> calculate the rate based on parent_rate that gets passed to this
> function?

Will check and get back.

Regards,
Subhransu

> 
> > +
> > +	rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
> > +					parent_rate);
> > +	if (!rcfg)
> > +		return 0;
> > +
> > +	clk_type = skl_get_clk_type(clkdev->id);
> > +	if (clk_type < 0)
> > +		return 0;
> > +
> > +	skl_fill_clk_ipc(rcfg, clk_type);
> > +	clkdev->rate = rcfg->rate;
> > +
> > +	return clkdev->rate;
> > +}
> > +
> > +/* Not supported by clk driver. Implemented to satisfy clk fw */
> > +long skl_clk_round_rate(struct clk_hw *hw, unsigned long rate,
> > +				unsigned long *parent_rate)
> > +{
> > +	return rate;
> > +}
> 
> -- 
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> a Linux Foundation Collaborative Project
Subhransu S. Prusty Dec. 18, 2017, 5:01 a.m. UTC | #3
On Mon, Dec 18, 2017 at 09:27:16AM +0530, Subhransu S. Prusty wrote:
> On Wed, Dec 13, 2017 at 02:30:32PM -0800, Stephen Boyd wrote:
> > On 12/11, Sriram Periyasamy wrote:
> > > +
> > > +static int skl_clk_set_rate(struct clk_hw *hw, unsigned long rate,
> > > +					unsigned long parent_rate)
> > > +{
> > > +	struct skl_clk *clkdev = to_skl_clk(hw);
> > > +	struct skl_clk_rate_cfg_table *rcfg;
> > > +	int clk_type;
> > > +
> > > +	if (!clkdev)
> > > +		return -ENODEV;
> > 
> > These checks don't make sense. container_of() on clk_hw
> > structures returning NULL wouldn't happen.
> 
> Sure. will remove.
> 
> > 
> > > +
> > > +	if (!rate)
> > > +		return -EINVAL;
> > > +
> > > +	if (__clk_is_enabled(hw->clk) && (clkdev->rate != rate))
> > 
> > Any chance you can directly read the hardware instead of going
> > through the framework to find out if the clk is enabled? Seems
> 
> No. This involves sending an IPC to DSP to enable clock and interpreting the
> return error code. I would like to avoid doing this here in set_rate.
> 
> > circular to do it this way.
> > 
> > > +		return -EBUSY;
> > > +
> > > +	rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
> > > +							rate);
> > > +	if (!rcfg)
> > > +		return -EINVAL;
> > > +
> > > +	clk_type = skl_get_clk_type(clkdev->id);
> > > +	if (clk_type < 0)
> > > +		return clk_type;
> > > +
> > > +	skl_fill_clk_ipc(rcfg, clk_type);
> > > +	clkdev->rate = rate;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static unsigned long skl_clk_recalc_rate(struct clk_hw *hw,
> > > +				unsigned long parent_rate)
> > > +{
> > > +	struct skl_clk *clkdev = to_skl_clk(hw);
> > > +	struct skl_clk_rate_cfg_table *rcfg;
> > > +	int clk_type;
> > > +
> > > +	if (!clkdev)
> > > +		return 0;
> > > +
> > > +	if (clkdev->rate)
> > > +		return clkdev->rate;
> > 
> > Why is the rate being cached? We should always be able to
> > calculate the rate based on parent_rate that gets passed to this
> > function?
> 
> Will check and get back.

If I understand correctly, you refer to deriving the rate from parent_rate
using ratios. But since only the DSP is aware of the ratios and not the
driver, the driver can't derive the rate from the parent_rate and thus
cached.

> 
> Regards,
> Subhransu
> 
> > 
> > > +
> > > +	rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
> > > +					parent_rate);
> > > +	if (!rcfg)
> > > +		return 0;
> > > +
> > > +	clk_type = skl_get_clk_type(clkdev->id);
> > > +	if (clk_type < 0)
> > > +		return 0;
> > > +
> > > +	skl_fill_clk_ipc(rcfg, clk_type);
> > > +	clkdev->rate = rcfg->rate;
> > > +
> > > +	return clkdev->rate;
> > > +}
> > > +
> > > +/* Not supported by clk driver. Implemented to satisfy clk fw */
> > > +long skl_clk_round_rate(struct clk_hw *hw, unsigned long rate,
> > > +				unsigned long *parent_rate)
> > > +{
> > > +	return rate;
> > > +}
> > 
> > -- 
> > Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> > a Linux Foundation Collaborative Project
> 
> --
Stephen Boyd Dec. 18, 2017, 7:10 p.m. UTC | #4
On 12/18, Subhransu S. Prusty wrote:
> On Mon, Dec 18, 2017 at 09:27:16AM +0530, Subhransu S. Prusty wrote:
> > On Wed, Dec 13, 2017 at 02:30:32PM -0800, Stephen Boyd wrote:
> > > On 12/11, Sriram Periyasamy wrote:
> > > 
> > > > +
> > > > +	if (!rate)
> > > > +		return -EINVAL;
> > > > +
> > > > +	if (__clk_is_enabled(hw->clk) && (clkdev->rate != rate))
> > > 
> > > Any chance you can directly read the hardware instead of going
> > > through the framework to find out if the clk is enabled? Seems
> > 
> > No. This involves sending an IPC to DSP to enable clock and interpreting the
> > return error code. I would like to avoid doing this here in set_rate.
> > 

Ok. So we're checking to see if software has already enabled the
clk and then checking to see if the rate the consumer is
requesting is the same as the rate it previously requested? I'm
still confused what's going on here. Does skl_fill_clk_ipc()
change the rate of the clk? Is there any way to ask the DSP what
the rate would be if we were to use some rate configuration?

> > > circular to do it this way.
> > > 
> > > > +		return -EBUSY;
> > > > +
> > > > +	rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
> > > > +							rate);
> > > > +	if (!rcfg)
> > > > +		return -EINVAL;
> > > > +
> > > > +	clk_type = skl_get_clk_type(clkdev->id);
> > > > +	if (clk_type < 0)
> > > > +		return clk_type;
> > > > +
> > > > +	skl_fill_clk_ipc(rcfg, clk_type);
> > > > +	clkdev->rate = rate;
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +static unsigned long skl_clk_recalc_rate(struct clk_hw *hw,
> > > > +				unsigned long parent_rate)
> > > > +{
> > > > +	struct skl_clk *clkdev = to_skl_clk(hw);
> > > > +	struct skl_clk_rate_cfg_table *rcfg;
> > > > +	int clk_type;
> > > > +
> > > > +	if (!clkdev)
> > > > +		return 0;
> > > > +
> > > > +	if (clkdev->rate)
> > > > +		return clkdev->rate;
> > > 
> > > Why is the rate being cached? We should always be able to
> > > calculate the rate based on parent_rate that gets passed to this
> > > function?
> > 
> > Will check and get back.
> 
> If I understand correctly, you refer to deriving the rate from parent_rate
> using ratios. But since only the DSP is aware of the ratios and not the
> driver, the driver can't derive the rate from the parent_rate and thus
> cached.
> 

I was thinking the code would do what's below all the time.

> > 
> > > 
> > > > +
> > > > +	rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
> > > > +					parent_rate);
> > > > +	if (!rcfg)
> > > > +		return 0;
> > > > +
> > > > +	clk_type = skl_get_clk_type(clkdev->id);
> > > > +	if (clk_type < 0)
> > > > +		return 0;
> > > > +
> > > > +	skl_fill_clk_ipc(rcfg, clk_type);
> > > > +	clkdev->rate = rcfg->rate;
> > > > +
> > > > +	return clkdev->rate;
> > > > +}
> > > > +

I guess that means doing an IPC to the DSP to figure out the
ratio and how that relates to the parent rate? recalc_rate() can
be called many times with different things when the framework is
speculating on the tree. We don't want clk providers to rely on
the order of this op being called with respect to clk_set_rate().
Subhransu S. Prusty Dec. 19, 2017, 5:41 a.m. UTC | #5
On Mon, Dec 18, 2017 at 11:10:40AM -0800, Stephen Boyd wrote:
> On 12/18, Subhransu S. Prusty wrote:
> > On Mon, Dec 18, 2017 at 09:27:16AM +0530, Subhransu S. Prusty wrote:
> > > On Wed, Dec 13, 2017 at 02:30:32PM -0800, Stephen Boyd wrote:
> > > > On 12/11, Sriram Periyasamy wrote:
> > > > 
> > > > > +
> > > > > +	if (!rate)
> > > > > +		return -EINVAL;
> > > > > +
> > > > > +	if (__clk_is_enabled(hw->clk) && (clkdev->rate != rate))
> > > > 
> > > > Any chance you can directly read the hardware instead of going
> > > > through the framework to find out if the clk is enabled? Seems
> > > 
> > > No. This involves sending an IPC to DSP to enable clock and interpreting the
> > > return error code. I would like to avoid doing this here in set_rate.
> > > 
> 
> Ok. So we're checking to see if software has already enabled the
> clk and then checking to see if the rate the consumer is
> requesting is the same as the rate it previously requested? I'm

The second check is not required, will remove it.

> still confused what's going on here. Does skl_fill_clk_ipc()
> change the rate of the clk? Is there any way to ask the DSP what

skl_fill_clk_ipc() prepares the IPC message based on the rate request by the
consumer. This IPC will be sent to DSP during a call to clock enable.

> the rate would be if we were to use some rate configuration?

No. If the clock is already running, reconfiguration is not allowed. So the
above check is invalid.

> 
> > > > circular to do it this way.
> > > > 
> > > > > +		return -EBUSY;
> > > > > +
> > > > > +	rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
> > > > > +							rate);
> > > > > +	if (!rcfg)
> > > > > +		return -EINVAL;
> > > > > +
> > > > > +	clk_type = skl_get_clk_type(clkdev->id);
> > > > > +	if (clk_type < 0)
> > > > > +		return clk_type;
> > > > > +
> > > > > +	skl_fill_clk_ipc(rcfg, clk_type);
> > > > > +	clkdev->rate = rate;
> > > > > +
> > > > > +	return 0;
> > > > > +}
> > > > > +
> > > > > +static unsigned long skl_clk_recalc_rate(struct clk_hw *hw,
> > > > > +				unsigned long parent_rate)
> > > > > +{
> > > > > +	struct skl_clk *clkdev = to_skl_clk(hw);
> > > > > +	struct skl_clk_rate_cfg_table *rcfg;
> > > > > +	int clk_type;
> > > > > +
> > > > > +	if (!clkdev)
> > > > > +		return 0;
> > > > > +
> > > > > +	if (clkdev->rate)
> > > > > +		return clkdev->rate;
> > > > 
> > > > Why is the rate being cached? We should always be able to
> > > > calculate the rate based on parent_rate that gets passed to this
> > > > function?
> > > 
> > > Will check and get back.
> > 
> > If I understand correctly, you refer to deriving the rate from parent_rate
> > using ratios. But since only the DSP is aware of the ratios and not the
> > driver, the driver can't derive the rate from the parent_rate and thus
> > cached.
> > 
> 
> I was thinking the code would do what's below all the time.

I think we interpreted incorrectly. As recalc_rate is meant to be used only
when parent_rate changes, so this can be removed as the set_parent is not
supported for this driver. Please let me know if I understand correctly.


Regards,
Subhransu

> 
> > > 
> > > > 
> > > > > +
> > > > > +	rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
> > > > > +					parent_rate);
> > > > > +	if (!rcfg)
> > > > > +		return 0;
> > > > > +
> > > > > +	clk_type = skl_get_clk_type(clkdev->id);
> > > > > +	if (clk_type < 0)
> > > > > +		return 0;
> > > > > +
> > > > > +	skl_fill_clk_ipc(rcfg, clk_type);
> > > > > +	clkdev->rate = rcfg->rate;
> > > > > +
> > > > > +	return clkdev->rate;
> > > > > +}
> > > > > +
> 
> I guess that means doing an IPC to the DSP to figure out the
> ratio and how that relates to the parent rate? recalc_rate() can
> be called many times with different things when the framework is
> speculating on the tree. We don't want clk providers to rely on
> the order of this op being called with respect to clk_set_rate().
> 
> -- 
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> a Linux Foundation Collaborative Project
Stephen Boyd Dec. 19, 2017, 7:17 p.m. UTC | #6
On 12/19, Subhransu S. Prusty wrote:
> On Mon, Dec 18, 2017 at 11:10:40AM -0800, Stephen Boyd wrote:
> > On 12/18, Subhransu S. Prusty wrote:
> > > On Mon, Dec 18, 2017 at 09:27:16AM +0530, Subhransu S. Prusty wrote:
> > > > On Wed, Dec 13, 2017 at 02:30:32PM -0800, Stephen Boyd wrote:
> > > > > On 12/11, Sriram Periyasamy wrote:
> > > > > 
> > > > > > +
> > > > > > +	if (!rate)
> > > > > > +		return -EINVAL;
> > > > > > +
> > > > > > +	if (__clk_is_enabled(hw->clk) && (clkdev->rate != rate))
> > > > > 
> > > > > Any chance you can directly read the hardware instead of going
> > > > > through the framework to find out if the clk is enabled? Seems
> > > > 
> > > > No. This involves sending an IPC to DSP to enable clock and interpreting the
> > > > return error code. I would like to avoid doing this here in set_rate.
> > > > 
> > 
> > Ok. So we're checking to see if software has already enabled the
> > clk and then checking to see if the rate the consumer is
> > requesting is the same as the rate it previously requested? I'm
> 
> The second check is not required, will remove it.
> 
> > still confused what's going on here. Does skl_fill_clk_ipc()
> > change the rate of the clk? Is there any way to ask the DSP what
> 
> skl_fill_clk_ipc() prepares the IPC message based on the rate request by the
> consumer. This IPC will be sent to DSP during a call to clock enable.
> 
> > the rate would be if we were to use some rate configuration?
> 
> No. If the clock is already running, reconfiguration is not allowed. So the
> above check is invalid.

But we can't figure out if the clk is already running when we
probe this driver, correct? It seems that we're relying on
knowing if the clk is already running by looking at the software
enable count that relates to if the clk is enabled in software by
some linux consumer.

> 
> > 
> > > > > circular to do it this way.
> > > > > 
> > > > > > +		return -EBUSY;
> > > > > > +
> > > > > > +	rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
> > > > > > +							rate);
> > > > > > +	if (!rcfg)
> > > > > > +		return -EINVAL;
> > > > > > +
> > > > > > +	clk_type = skl_get_clk_type(clkdev->id);
> > > > > > +	if (clk_type < 0)
> > > > > > +		return clk_type;
> > > > > > +
> > > > > > +	skl_fill_clk_ipc(rcfg, clk_type);
> > > > > > +	clkdev->rate = rate;
> > > > > > +
> > > > > > +	return 0;
> > > > > > +}
> > > > > > +
> > > > > > +static unsigned long skl_clk_recalc_rate(struct clk_hw *hw,
> > > > > > +				unsigned long parent_rate)
> > > > > > +{
> > > > > > +	struct skl_clk *clkdev = to_skl_clk(hw);
> > > > > > +	struct skl_clk_rate_cfg_table *rcfg;
> > > > > > +	int clk_type;
> > > > > > +
> > > > > > +	if (!clkdev)
> > > > > > +		return 0;
> > > > > > +
> > > > > > +	if (clkdev->rate)
> > > > > > +		return clkdev->rate;
> > > > > 
> > > > > Why is the rate being cached? We should always be able to
> > > > > calculate the rate based on parent_rate that gets passed to this
> > > > > function?
> > > > 
> > > > Will check and get back.
> > > 
> > > If I understand correctly, you refer to deriving the rate from parent_rate
> > > using ratios. But since only the DSP is aware of the ratios and not the
> > > driver, the driver can't derive the rate from the parent_rate and thus
> > > cached.
> > > 
> > 
> > I was thinking the code would do what's below all the time.
> 
> I think we interpreted incorrectly. As recalc_rate is meant to be used only
> when parent_rate changes, so this can be removed as the set_parent is not
> supported for this driver. Please let me know if I understand correctly.

recalc_rate() is called whenever the clk rate could change. It
could be that clk_set_rate() is called directly on this clk, and
then recalc_rate() would be called. Or it could be that the
parent of this clk has its rate change, and then again
recalc_rate() would be called on this clk. set_parent is about
changing the parent of the clk, which also would cause the
framework to call recalc_rate() on a clk that gets a new parent.
Subhransu S. Prusty Dec. 20, 2017, 3:33 a.m. UTC | #7
On Tue, Dec 19, 2017 at 11:17:27AM -0800, Stephen Boyd wrote:
> On 12/19, Subhransu S. Prusty wrote:
> > On Mon, Dec 18, 2017 at 11:10:40AM -0800, Stephen Boyd wrote:
> > > On 12/18, Subhransu S. Prusty wrote:
> > > > On Mon, Dec 18, 2017 at 09:27:16AM +0530, Subhransu S. Prusty wrote:
> > > > > On Wed, Dec 13, 2017 at 02:30:32PM -0800, Stephen Boyd wrote:
> > > > > > On 12/11, Sriram Periyasamy wrote:
> > > > > > 
> > > > > > > +
> > > > > > > +	if (!rate)
> > > > > > > +		return -EINVAL;
> > > > > > > +
> > > > > > > +	if (__clk_is_enabled(hw->clk) && (clkdev->rate != rate))
> > > > > > 
> > > > > > Any chance you can directly read the hardware instead of going
> > > > > > through the framework to find out if the clk is enabled? Seems
> > > > > 
> > > > > No. This involves sending an IPC to DSP to enable clock and interpreting the
> > > > > return error code. I would like to avoid doing this here in set_rate.
> > > > > 
> > > 
> > > Ok. So we're checking to see if software has already enabled the
> > > clk and then checking to see if the rate the consumer is
> > > requesting is the same as the rate it previously requested? I'm
> > 
> > The second check is not required, will remove it.
> > 
> > > still confused what's going on here. Does skl_fill_clk_ipc()
> > > change the rate of the clk? Is there any way to ask the DSP what
> > 
> > skl_fill_clk_ipc() prepares the IPC message based on the rate request by the
> > consumer. This IPC will be sent to DSP during a call to clock enable.
> > 
> > > the rate would be if we were to use some rate configuration?
> > 
> > No. If the clock is already running, reconfiguration is not allowed. So the
> > above check is invalid.
> 
> But we can't figure out if the clk is already running when we
> probe this driver, correct? It seems that we're relying on
> knowing if the clk is already running by looking at the software
> enable count that relates to if the clk is enabled in software by
> some linux consumer.

So here are the details:
 - clock is turned ON, when we send the IPC. At probe we don't send, so the
   clock will be OFF.
 - The clock is configured by DSP firmware and it will need an IPC to
   trigger that. By default power up of HW and DSP fw bootup ensures clk
   is OFF

> 
> > 
> > > 
> > > > > > circular to do it this way.
> > > > > > 
> > > > > > > +		return -EBUSY;
> > > > > > > +
> > > > > > > +	rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
> > > > > > > +							rate);
> > > > > > > +	if (!rcfg)
> > > > > > > +		return -EINVAL;
> > > > > > > +
> > > > > > > +	clk_type = skl_get_clk_type(clkdev->id);
> > > > > > > +	if (clk_type < 0)
> > > > > > > +		return clk_type;
> > > > > > > +
> > > > > > > +	skl_fill_clk_ipc(rcfg, clk_type);
> > > > > > > +	clkdev->rate = rate;
> > > > > > > +
> > > > > > > +	return 0;
> > > > > > > +}
> > > > > > > +
> > > > > > > +static unsigned long skl_clk_recalc_rate(struct clk_hw *hw,
> > > > > > > +				unsigned long parent_rate)
> > > > > > > +{
> > > > > > > +	struct skl_clk *clkdev = to_skl_clk(hw);
> > > > > > > +	struct skl_clk_rate_cfg_table *rcfg;
> > > > > > > +	int clk_type;
> > > > > > > +
> > > > > > > +	if (!clkdev)
> > > > > > > +		return 0;
> > > > > > > +
> > > > > > > +	if (clkdev->rate)
> > > > > > > +		return clkdev->rate;
> > > > > > 
> > > > > > Why is the rate being cached? We should always be able to
> > > > > > calculate the rate based on parent_rate that gets passed to this
> > > > > > function?
> > > > > 
> > > > > Will check and get back.
> > > > 
> > > > If I understand correctly, you refer to deriving the rate from parent_rate
> > > > using ratios. But since only the DSP is aware of the ratios and not the
> > > > driver, the driver can't derive the rate from the parent_rate and thus
> > > > cached.
> > > > 
> > > 
> > > I was thinking the code would do what's below all the time.
> > 
> > I think we interpreted incorrectly. As recalc_rate is meant to be used only
> > when parent_rate changes, so this can be removed as the set_parent is not
> > supported for this driver. Please let me know if I understand correctly.
> 
> recalc_rate() is called whenever the clk rate could change. It
> could be that clk_set_rate() is called directly on this clk, and
> then recalc_rate() would be called. Or it could be that the
> parent of this clk has its rate change, and then again
> recalc_rate() would be called on this clk. set_parent is about
> changing the parent of the clk, which also would cause the
> framework to call recalc_rate() on a clk that gets a new parent.

Thanks for the explanation.

So, we have a parent of the clk which is fixed. so change of parent is not
applicable here.

For us, recalc_rate() doesn't mean much as we can only return current rate,
if it is same otherwise 0. Pls do advise in this case if the behaviour needs
to change, if so how?

Regards,
Subhransu

> 
> -- 
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> a Linux Foundation Collaborative Project
Stephen Boyd Dec. 22, 2017, 2:04 a.m. UTC | #8
On 12/20, Subhransu S. Prusty wrote:
> On Tue, Dec 19, 2017 at 11:17:27AM -0800, Stephen Boyd wrote:
> >
> > But we can't figure out if the clk is already running when we
> > probe this driver, correct? It seems that we're relying on
> > knowing if the clk is already running by looking at the software
> > enable count that relates to if the clk is enabled in software by
> > some linux consumer.
> 
> So here are the details:
>  - clock is turned ON, when we send the IPC. At probe we don't send, so the
>    clock will be OFF.
>  - The clock is configured by DSP firmware and it will need an IPC to
>    trigger that. By default power up of HW and DSP fw bootup ensures clk
>    is OFF
> 

Ok great.

> > 
> > recalc_rate() is called whenever the clk rate could change. It
> > could be that clk_set_rate() is called directly on this clk, and
> > then recalc_rate() would be called. Or it could be that the
> > parent of this clk has its rate change, and then again
> > recalc_rate() would be called on this clk. set_parent is about
> > changing the parent of the clk, which also would cause the
> > framework to call recalc_rate() on a clk that gets a new parent.
> 
> Thanks for the explanation.
> 
> So, we have a parent of the clk which is fixed. so change of parent is not
> applicable here.

Yeah, let's ignore a changing parent frequency. recalc_rate() is
also called when *this* clk rate is changed. The parent rate is
passed in because that's usually helpful to calculate the rate
that this op is supposed to return.

> 
> For us, recalc_rate() doesn't mean much as we can only return current rate,
> if it is same otherwise 0. Pls do advise in this case if the behaviour needs
> to change, if so how?
> 

Can the DSP tell us what the rate of the clk is? Or what the rate
of the clk is configured for? What is that configuration out of
boot when it's OFF? Typically, recalc_rate() can tell us what the
rate of the clk is, even when its OFF, because we can read the
hardware and calculate the rate of the clk given the parent
frequency. We do have clk drivers out there that are like this
DSP and don't tell anything about the rate and we can't even ask.
In that case, we return 0 and cache the rate in the set_rate op.

Ideally, recalc_rate would always return the frequency of the clk
that's been configured in the hardware on the DSP side. If that
can't be done, I suppose faking it and caching the rate that the
set_rate op figures out would work. Or if enabling the clk let's
us know the rate we should cache it there too. But definitely
don't do any sort of rate caching in recalc_rate. It should just
blindly return the cached value if it can't read hardware.
Subhransu S. Prusty Dec. 22, 2017, 4:52 a.m. UTC | #9
On Thu, Dec 21, 2017 at 06:04:36PM -0800, Stephen Boyd wrote:
> On 12/20, Subhransu S. Prusty wrote:
> > On Tue, Dec 19, 2017 at 11:17:27AM -0800, Stephen Boyd wrote:
> > applicable here.
> 
> Yeah, let's ignore a changing parent frequency. recalc_rate() is
> also called when *this* clk rate is changed. The parent rate is
> passed in because that's usually helpful to calculate the rate
> that this op is supposed to return.
> 
> > 
> > For us, recalc_rate() doesn't mean much as we can only return current rate,
> > if it is same otherwise 0. Pls do advise in this case if the behaviour needs
> > to change, if so how?
> > 
> 
> Can the DSP tell us what the rate of the clk is? Or what the rate
> of the clk is configured for? What is that configuration out of
> boot when it's OFF? Typically, recalc_rate() can tell us what the

No, the DSP clock is programmed using configuration parameters which are
sent from driver. DSP doesn't have any mechanism to report the clock
configuration back to driver. So the rate is cached.

> rate of the clk is, even when its OFF, because we can read the
> hardware and calculate the rate of the clk given the parent
> frequency. We do have clk drivers out there that are like this
> DSP and don't tell anything about the rate and we can't even ask.
> In that case, we return 0 and cache the rate in the set_rate op.
> 
> Ideally, recalc_rate would always return the frequency of the clk
> that's been configured in the hardware on the DSP side. If that
> can't be done, I suppose faking it and caching the rate that the
> set_rate op figures out would work. Or if enabling the clk let's
> us know the rate we should cache it there too. But definitely

Yes, this behavior is applicable for this driver. I will remove the
recalculation part and resubmit the patch by returning the cached rate
already done in set_rate.

Regards,
Subhransu

> don't do any sort of rate caching in recalc_rate. It should just
> blindly return the cached value if it can't read hardware.
> 
> -- 
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> a Linux Foundation Collaborative Project
diff mbox

Patch

diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index e18118209b75..826a30f76249 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -53,6 +53,9 @@  config SND_SST_ATOM_HIFI2_PLATFORM
 	depends on SND_SOC_INTEL_SST_TOPLEVEL && X86
 	select SND_SOC_COMPRESS
 
+config SND_SOC_INTEL_SKYLAKE_SSP_CLK
+	tristate
+
 config SND_SOC_INTEL_SKYLAKE
 	tristate "Intel ASoC SST driver for SKL/BXT/KBL/GLK/CNL"
 	depends on SND_SOC_INTEL_SST_TOPLEVEL && PCI && ACPI
diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile
index 3380deb81015..9131c35ad4bb 100644
--- a/sound/soc/intel/skylake/Makefile
+++ b/sound/soc/intel/skylake/Makefile
@@ -13,3 +13,8 @@  snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o cnl-sst-dsp.o \
 		skl-sst-utils.o
 
 obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o
+
+#Skylake Clock device support
+snd-soc-skl-ssp-clk-objs := skl-ssp-clk.o
+
+obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE_SSP_CLK) += snd-soc-skl-ssp-clk.o
diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c
index 4e63213a8d55..05de2b8bd7a9 100644
--- a/sound/soc/intel/skylake/skl-messages.c
+++ b/sound/soc/intel/skylake/skl-messages.c
@@ -671,6 +671,7 @@  int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps,
 	kfree(dma_ctrl);
 	return err;
 }
+EXPORT_SYMBOL_GPL(skl_dsp_set_dma_control);
 
 static void skl_setup_out_format(struct skl_sst *ctx,
 			struct skl_module_cfg *mconfig,
diff --git a/sound/soc/intel/skylake/skl-ssp-clk.c b/sound/soc/intel/skylake/skl-ssp-clk.c
new file mode 100644
index 000000000000..385ecd560a8a
--- /dev/null
+++ b/sound/soc/intel/skylake/skl-ssp-clk.c
@@ -0,0 +1,473 @@ 
+/*
+ *  skl-ssp-clk.c - ASoC skylake ssp clock driver
+ *
+ *  Copyright (C) 2017 Intel Corp
+ *  Author: Jaikrishna Nemallapudi <jaikrishnax.nemallapudi@intel.com>
+ *  Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
+ *
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include "skl.h"
+#include "skl-ssp-clk.h"
+#include "skl-topology.h"
+
+#define to_skl_clk(_hw)	container_of(_hw, struct skl_clk, hw)
+
+struct skl_clk_parent {
+	struct clk_hw *hw;
+	struct clk_lookup *lookup;
+};
+
+struct skl_clk {
+	struct clk_hw hw;
+	struct clk_lookup *lookup;
+	unsigned long rate;
+	struct skl_clk_pdata *pdata;
+	u32 id;
+};
+
+struct skl_clk_data {
+	struct skl_clk_parent parent[SKL_MAX_CLK_SRC];
+	struct skl_clk *clk[SKL_MAX_CLK_CNT];
+	u8 avail_clk_cnt;
+};
+
+static int skl_get_clk_type(u32 index)
+{
+	switch (index) {
+	case 0 ... (SKL_SCLK_OFS - 1):
+		return SKL_MCLK;
+
+	case SKL_SCLK_OFS ... (SKL_SCLKFS_OFS - 1):
+		return SKL_SCLK;
+
+	case SKL_SCLKFS_OFS ... (SKL_MAX_CLK_CNT - 1):
+		return SKL_SCLK_FS;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int skl_get_vbus_id(u32 index, u8 clk_type)
+{
+	switch (clk_type) {
+	case SKL_MCLK:
+		return index;
+
+	case SKL_SCLK:
+		return index - SKL_SCLK_OFS;
+
+	case SKL_SCLK_FS:
+		return index - SKL_SCLKFS_OFS;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static void skl_fill_clk_ipc(struct skl_clk_rate_cfg_table *rcfg, u8 clk_type)
+{
+	struct nhlt_fmt_cfg *fmt_cfg;
+	union skl_clk_ctrl_ipc *ipc;
+	struct wav_fmt *wfmt;
+
+	if (!rcfg)
+		return;
+
+	ipc = &rcfg->dma_ctl_ipc;
+	if (clk_type == SKL_SCLK_FS) {
+		fmt_cfg = (struct nhlt_fmt_cfg *)rcfg->config;
+		wfmt = &fmt_cfg->fmt_ext.fmt;
+
+		/* Remove TLV Header size */
+		ipc->sclk_fs.hdr.size = sizeof(struct skl_dmactrl_sclkfs_cfg) -
+						sizeof(struct skl_tlv_hdr);
+		ipc->sclk_fs.sampling_frequency = wfmt->samples_per_sec;
+		ipc->sclk_fs.bit_depth = wfmt->bits_per_sample;
+		ipc->sclk_fs.valid_bit_depth =
+			fmt_cfg->fmt_ext.sample.valid_bits_per_sample;
+		ipc->sclk_fs.number_of_channels = wfmt->channels;
+	} else {
+		ipc->mclk.hdr.type = DMA_CLK_CONTROLS;
+		/* Remove TLV Header size */
+		ipc->mclk.hdr.size = sizeof(struct skl_dmactrl_mclk_cfg) -
+						sizeof(struct skl_tlv_hdr);
+	}
+}
+
+/* Sends dma control IPC to turn the clock ON/OFF */
+static int skl_send_clk_dma_control(struct skl *skl,
+				struct skl_clk_rate_cfg_table *rcfg,
+				u32 vbus_id, u8 clk_type,
+				bool enable)
+{
+	struct nhlt_specific_cfg *sp_cfg;
+	u32 i2s_config_size, node_id = 0;
+	struct nhlt_fmt_cfg *fmt_cfg;
+	union skl_clk_ctrl_ipc *ipc;
+	void *i2s_config = NULL;
+	u8 *data, size;
+	int ret;
+
+	if (!rcfg)
+		return -EIO;
+
+	ipc = &rcfg->dma_ctl_ipc;
+	fmt_cfg = (struct nhlt_fmt_cfg *)rcfg->config;
+	sp_cfg = &fmt_cfg->config;
+
+	if (clk_type == SKL_SCLK_FS) {
+		ipc->sclk_fs.hdr.type =
+			enable ? DMA_TRANSMITION_START : DMA_TRANSMITION_STOP;
+		data = (u8 *)&ipc->sclk_fs;
+		size = sizeof(struct skl_dmactrl_sclkfs_cfg);
+	} else {
+		/* 1 to enable mclk, 0 to enable sclk */
+		if (clk_type == SKL_SCLK)
+			ipc->mclk.mclk = 0;
+		else
+			ipc->mclk.mclk = 1;
+
+		ipc->mclk.keep_running = enable;
+		ipc->mclk.warm_up_over = enable;
+		ipc->mclk.clk_stop_over = !enable;
+		data = (u8 *)&ipc->mclk;
+		size = sizeof(struct skl_dmactrl_mclk_cfg);
+	}
+
+	i2s_config_size = sp_cfg->size + size;
+	i2s_config = kzalloc(i2s_config_size, GFP_KERNEL);
+	if (!i2s_config)
+		return -ENOMEM;
+
+	/* copy blob */
+	memcpy(i2s_config, sp_cfg->caps, sp_cfg->size);
+
+	/* copy additional dma controls information */
+	memcpy(i2s_config + sp_cfg->size, data, size);
+
+	node_id = ((SKL_DMA_I2S_LINK_INPUT_CLASS << 8) | (vbus_id << 4));
+	ret = skl_dsp_set_dma_control(skl->skl_sst, (u32 *)i2s_config,
+					i2s_config_size, node_id);
+	kfree(i2s_config);
+
+	return ret;
+}
+
+static struct skl_clk_rate_cfg_table *skl_get_rate_cfg(
+		struct skl_clk_rate_cfg_table *rcfg,
+				unsigned long rate)
+{
+	int i;
+
+	for (i = 0; (i < SKL_MAX_CLK_RATES) && rcfg[i].rate; i++) {
+		if (rcfg[i].rate == rate)
+			return &rcfg[i];
+	}
+
+	return NULL;
+}
+
+static int skl_clk_change_status(struct skl_clk *clkdev,
+				bool enable)
+{
+	struct skl_clk_rate_cfg_table *rcfg;
+	int vbus_id, clk_type;
+
+	clk_type = skl_get_clk_type(clkdev->id);
+	if (clk_type < 0)
+		return clk_type;
+
+	vbus_id = skl_get_vbus_id(clkdev->id, clk_type);
+	if (vbus_id < 0)
+		return vbus_id;
+
+	rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
+						clkdev->rate);
+	if (!rcfg)
+		return -EINVAL;
+
+	return skl_send_clk_dma_control(clkdev->pdata->pvt_data, rcfg,
+					vbus_id, clk_type, enable);
+}
+
+static int skl_clk_prepare(struct clk_hw *hw)
+{
+	struct skl_clk *clkdev = to_skl_clk(hw);
+
+	if (!clkdev)
+		return -ENODEV;
+
+	return skl_clk_change_status(clkdev, true);
+}
+
+static void skl_clk_unprepare(struct clk_hw *hw)
+{
+	struct skl_clk *clkdev = to_skl_clk(hw);
+
+	if (!clkdev)
+		return;
+
+	skl_clk_change_status(clkdev, false);
+}
+
+static int skl_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+					unsigned long parent_rate)
+{
+	struct skl_clk *clkdev = to_skl_clk(hw);
+	struct skl_clk_rate_cfg_table *rcfg;
+	int clk_type;
+
+	if (!clkdev)
+		return -ENODEV;
+
+	if (!rate)
+		return -EINVAL;
+
+	if (__clk_is_enabled(hw->clk) && (clkdev->rate != rate))
+		return -EBUSY;
+
+	rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
+							rate);
+	if (!rcfg)
+		return -EINVAL;
+
+	clk_type = skl_get_clk_type(clkdev->id);
+	if (clk_type < 0)
+		return clk_type;
+
+	skl_fill_clk_ipc(rcfg, clk_type);
+	clkdev->rate = rate;
+
+	return 0;
+}
+
+static unsigned long skl_clk_recalc_rate(struct clk_hw *hw,
+				unsigned long parent_rate)
+{
+	struct skl_clk *clkdev = to_skl_clk(hw);
+	struct skl_clk_rate_cfg_table *rcfg;
+	int clk_type;
+
+	if (!clkdev)
+		return 0;
+
+	if (clkdev->rate)
+		return clkdev->rate;
+
+	rcfg = skl_get_rate_cfg(clkdev->pdata->ssp_clks[clkdev->id].rate_cfg,
+					parent_rate);
+	if (!rcfg)
+		return 0;
+
+	clk_type = skl_get_clk_type(clkdev->id);
+	if (clk_type < 0)
+		return 0;
+
+	skl_fill_clk_ipc(rcfg, clk_type);
+	clkdev->rate = rcfg->rate;
+
+	return clkdev->rate;
+}
+
+/* Not supported by clk driver. Implemented to satisfy clk fw */
+long skl_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long *parent_rate)
+{
+	return rate;
+}
+
+/*
+ * prepare/unprepare are used instead of enable/disable as IPC will be sent
+ * in non-atomic context.
+ */
+static const struct clk_ops skl_clk_ops = {
+	.prepare = skl_clk_prepare,
+	.unprepare = skl_clk_unprepare,
+	.set_rate = skl_clk_set_rate,
+	.round_rate = skl_clk_round_rate,
+	.recalc_rate = skl_clk_recalc_rate,
+};
+
+static void unregister_parent_src_clk(struct skl_clk_parent *pclk,
+					unsigned int id)
+{
+	while (id--) {
+		clkdev_drop(pclk[id].lookup);
+		clk_hw_unregister_fixed_rate(pclk[id].hw);
+	}
+}
+
+static void unregister_src_clk(struct skl_clk_data *dclk)
+{
+	u8 cnt = dclk->avail_clk_cnt;
+
+	while (cnt--)
+		clkdev_drop(dclk->clk[cnt]->lookup);
+}
+
+static int skl_register_parent_clks(struct device *dev,
+			struct skl_clk_parent *parent,
+			struct skl_clk_parent_src *pclk)
+{
+	int i, ret;
+
+	for (i = 0; i < SKL_MAX_CLK_SRC; i++) {
+
+		/* Register Parent clock */
+		parent[i].hw = clk_hw_register_fixed_rate(dev, pclk[i].name,
+				pclk[i].parent_name, 0, pclk[i].rate);
+		if (IS_ERR(parent[i].hw)) {
+			ret = PTR_ERR(parent[i].hw);
+			goto err;
+		}
+
+		parent[i].lookup = clkdev_hw_create(parent[i].hw, pclk[i].name,
+									NULL);
+		if (!parent[i].lookup) {
+			clk_hw_unregister_fixed_rate(parent[i].hw);
+			ret = -ENOMEM;
+			goto err;
+		}
+	}
+
+	return 0;
+err:
+	unregister_parent_src_clk(parent, i);
+	return ret;
+}
+
+/* Assign fmt_config to clk_data */
+static struct skl_clk *register_skl_clk(struct device *dev,
+			struct skl_ssp_clk *clk,
+			struct skl_clk_pdata *clk_pdata, int id)
+{
+	struct clk_init_data init;
+	struct skl_clk *clkdev;
+	int ret;
+
+	clkdev = devm_kzalloc(dev, sizeof(*clkdev), GFP_KERNEL);
+	if (!clkdev)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = clk->name;
+	init.ops = &skl_clk_ops;
+	init.flags = 0;
+	init.parent_names = &clk->parent_name;
+	init.num_parents = 1;
+	clkdev->hw.init = &init;
+	clkdev->pdata = clk_pdata;
+
+	clkdev->id = id;
+	ret = devm_clk_hw_register(dev, &clkdev->hw);
+	if (ret) {
+		clkdev = ERR_PTR(ret);
+		return clkdev;
+	}
+
+	clkdev->lookup = clkdev_hw_create(&clkdev->hw, init.name, NULL);
+	if (!clkdev->lookup)
+		clkdev = ERR_PTR(-ENOMEM);
+
+	return clkdev;
+}
+
+static int skl_clk_dev_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device *parent_dev = dev->parent;
+	struct skl_clk_parent_src *parent_clks;
+	struct skl_clk_pdata *clk_pdata;
+	struct skl_clk_data *data;
+	struct skl_ssp_clk *clks;
+	int ret, i;
+
+	clk_pdata = dev_get_platdata(&pdev->dev);
+	parent_clks = clk_pdata->parent_clks;
+	clks = clk_pdata->ssp_clks;
+	if (!parent_clks || !clks)
+		return -EIO;
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	/* Register Parent clock */
+	ret = skl_register_parent_clks(parent_dev, data->parent, parent_clks);
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < clk_pdata->num_clks; i++) {
+		/*
+		 * Only register valid clocks
+		 * i.e. for which nhlt entry is present.
+		 */
+		if (clks[i].rate_cfg[0].rate == 0)
+			continue;
+
+		data->clk[i] = register_skl_clk(dev, &clks[i], clk_pdata, i);
+		if (IS_ERR(data->clk[i])) {
+			ret = PTR_ERR(data->clk[i]);
+			goto err_unreg_skl_clk;
+		}
+
+		data->avail_clk_cnt++;
+	}
+
+	platform_set_drvdata(pdev, data);
+
+	return 0;
+
+err_unreg_skl_clk:
+	unregister_src_clk(data);
+	unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC);
+
+	return ret;
+}
+
+static int skl_clk_dev_remove(struct platform_device *pdev)
+{
+	struct skl_clk_data *data;
+
+	data = platform_get_drvdata(pdev);
+	unregister_src_clk(data);
+	unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC);
+
+	return 0;
+}
+
+static struct platform_driver skl_clk_driver = {
+	.driver = {
+		.name = "skl-ssp-clk",
+	},
+	.probe = skl_clk_dev_probe,
+	.remove = skl_clk_dev_remove,
+};
+
+module_platform_driver(skl_clk_driver);
+
+MODULE_DESCRIPTION("Skylake clock driver");
+MODULE_AUTHOR("Jaikrishna Nemallapudi <jaikrishnax.nemallapudi@intel.com>");
+MODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:skl-ssp-clk");
diff --git a/sound/soc/intel/skylake/skl-ssp-clk.h b/sound/soc/intel/skylake/skl-ssp-clk.h
index c9ea84004260..d1be50f96c05 100644
--- a/sound/soc/intel/skylake/skl-ssp-clk.h
+++ b/sound/soc/intel/skylake/skl-ssp-clk.h
@@ -54,8 +54,46 @@  struct skl_clk_parent_src {
 	const char *parent_name;
 };
 
+struct skl_tlv_hdr {
+	u32 type;
+	u32 size;
+};
+
+struct skl_dmactrl_mclk_cfg {
+	struct skl_tlv_hdr hdr;
+	/* DMA Clk TLV params */
+	u32 clk_warm_up:16;
+	u32 mclk:1;
+	u32 warm_up_over:1;
+	u32 rsvd0:14;
+	u32 clk_stop_delay:16;
+	u32 keep_running:1;
+	u32 clk_stop_over:1;
+	u32 rsvd1:14;
+};
+
+struct skl_dmactrl_sclkfs_cfg {
+	struct skl_tlv_hdr hdr;
+	/* DMA SClk&FS  TLV params */
+	u32 sampling_frequency;
+	u32 bit_depth;
+	u32 channel_map;
+	u32 channel_config;
+	u32 interleaving_style;
+	u32 number_of_channels : 8;
+	u32 valid_bit_depth : 8;
+	u32 sample_type : 8;
+	u32 reserved : 8;
+};
+
+union skl_clk_ctrl_ipc {
+	struct skl_dmactrl_mclk_cfg mclk;
+	struct skl_dmactrl_sclkfs_cfg sclk_fs;
+};
+
 struct skl_clk_rate_cfg_table {
 	unsigned long rate;
+	union skl_clk_ctrl_ipc dma_ctl_ipc;
 	void *config;
 };
 
diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h
index 46dda88ba139..bb73068d2a21 100644
--- a/sound/soc/intel/skylake/skl.h
+++ b/sound/soc/intel/skylake/skl.h
@@ -38,6 +38,10 @@ 
 /* D0I3C Register fields */
 #define AZX_REG_VS_D0I3C_CIP      0x1 /* Command in progress */
 #define AZX_REG_VS_D0I3C_I3       0x4 /* D0i3 enable */
+#define SKL_MAX_DMACTRL_CFG	18
+#define DMA_CLK_CONTROLS	1
+#define DMA_TRANSMITION_START	2
+#define DMA_TRANSMITION_STOP	3
 
 struct skl_dsp_resource {
 	u32 max_mcps;
@@ -146,6 +150,8 @@  int skl_nhlt_create_sysfs(struct skl *skl);
 void skl_nhlt_remove_sysfs(struct skl *skl);
 void skl_get_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks);
 struct skl_clk_parent_src *skl_get_parent_clk(u8 clk_id);
+int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps,
+				u32 caps_size, u32 node_id);
 
 struct skl_module_cfg;