From: Daniel Baluta daniel.baluta@nxp.com
There are two types of clocks: * DSP IP clocks * DAI clocks
This clocks are necessary in order to power up DSP and DAIs.
We choose to enable DAI clocks here because of the way i.MX8/i.MX8X design handles resources (including clocks).
All clocks are managed by a separate core (named SCU) which communicates with Linux managed ARM core via a well known API.
We parse and enable the clocks in probe function and disable them in remove function.
Future patches will introduce Power Management support so that we disable clocks while DSP is not used or system enters power save.
Signed-off-by: Daniel Baluta daniel.baluta@nxp.com --- sound/soc/sof/imx/imx-common.c | 77 ++++++++++++++++++++++++++++++++++ sound/soc/sof/imx/imx-common.h | 16 +++++++ sound/soc/sof/imx/imx8.c | 32 ++++++++++++++ sound/soc/sof/imx/imx8m.c | 33 +++++++++++++++ 4 files changed, 158 insertions(+)
diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c index 8826ef94f04a..20bcfab7b653 100644 --- a/sound/soc/sof/imx/imx-common.c +++ b/sound/soc/sof/imx/imx-common.c @@ -74,4 +74,81 @@ void imx8_dump(struct snd_sof_dev *sdev, u32 flags) } EXPORT_SYMBOL(imx8_dump);
+int imx8_parse_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks) +{ + int i; + + clks->dsp_clks = devm_kcalloc(sdev->dev, clks->num_dsp_clks, + sizeof(*clks->dsp_clks), GFP_KERNEL); + if (!clks->dsp_clks) + return -ENOMEM; + + clks->dai_clks = devm_kcalloc(sdev->dev, clks->num_dai_clks, + sizeof(*clks->dai_clks), GFP_KERNEL); + if (!clks->dai_clks) + return -ENOMEM; + + for (i = 0; i < clks->num_dsp_clks; i++) { + clks->dsp_clks[i] = devm_clk_get(sdev->dev, clks->dsp_clks_names[i]); + if (IS_ERR(clks->dsp_clks[i])) { + dev_err(sdev->dev, "Failed to get clk %s\n", clks->dsp_clks_names[i]); + return PTR_ERR(clks->dsp_clks[i]); + } + } + + for (i = 0; i < clks->num_dai_clks; i++) + clks->dai_clks[i] = devm_clk_get_optional(sdev->dev, clks->dai_clks_names[i]); + + return 0; +} +EXPORT_SYMBOL(imx8_parse_clocks); + +int imx8_enable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks) +{ + int i, j, ret; + + for (i = 0; i < clks->num_dsp_clks; i++) { + ret = clk_prepare_enable(clks->dsp_clks[i]); + if (ret < 0) { + dev_err(sdev->dev, "Failed to enable clk %s\n", + clks->dsp_clks_names[i]); + goto err_dsp_clks; + } + } + + for (j = 0; j < clks->num_dai_clks; j++) { + ret = clk_prepare_enable(clks->dai_clks[j]); + if (ret < 0) { + dev_err(sdev->dev, "Failed to enable clk %s\n", + clks->dai_clks_names[j]); + goto err_dai_clks; + } + } + + return 0; + +err_dai_clks: + while (--j >= 0) + clk_disable_unprepare(clks->dai_clks[j]); + +err_dsp_clks: + while (--i >= 0) + clk_disable_unprepare(clks->dsp_clks[i]); + + return ret; +} +EXPORT_SYMBOL(imx8_enable_clocks); + +void imx8_disable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks) +{ + int i; + + for (i = 0; i < clks->num_dsp_clks; i++) + clk_disable_unprepare(clks->dsp_clks[i]); + + for (i = 0; i < clks->num_dai_clks; i++) + clk_disable_unprepare(clks->dai_clks[i]); +} +EXPORT_SYMBOL(imx8_disable_clocks); + MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/imx/imx-common.h b/sound/soc/sof/imx/imx-common.h index 1cc7d6704182..a9bd16d61e8f 100644 --- a/sound/soc/sof/imx/imx-common.h +++ b/sound/soc/sof/imx/imx-common.h @@ -3,6 +3,8 @@ #ifndef __IMX_COMMON_H__ #define __IMX_COMMON_H__
+#include <linux/clk.h> + #define EXCEPT_MAX_HDR_SIZE 0x400 #define IMX8_STACK_DUMP_SIZE 32
@@ -13,4 +15,18 @@ void imx8_get_registers(struct snd_sof_dev *sdev,
void imx8_dump(struct snd_sof_dev *sdev, u32 flags);
+struct imx_clocks { + const char **dsp_clks_names; + struct clk **dsp_clks; + int num_dsp_clks; + + const char **dai_clks_names; + struct clk **dai_clks; + int num_dai_clks; +}; + +int imx8_parse_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks); +int imx8_enable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks); +void imx8_disable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks); + #endif diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index fc1720c211a3..2937300200b2 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -41,6 +41,20 @@ #define MBOX_OFFSET 0x800000 #define MBOX_SIZE 0x1000
+#define IMX8_DSP_CLK_NUM 3 +static const char *imx8_dsp_clks_names[IMX8_DSP_CLK_NUM] = { + /* DSP clocks */ + "ipg", "ocram", "core", +}; + +#define IMX8_DAI_CLK_NUM 9 +static const char *imx8_dai_clks_names[IMX8_DAI_CLK_NUM] = { + /* ESAI0 clocks */ + "esai0_core", "esai0_extal", "esai0_fsys", "esai0_spba", + /* SAI1 clocks */ + "sai1_bus", "sai1_mclk0", "sai1_mclk1", "sai1_mclk2", "sai1_mclk3", +}; + struct imx8_priv { struct device *dev; struct snd_sof_dev *sdev; @@ -57,6 +71,7 @@ struct imx8_priv { struct device **pd_dev; struct device_link **link;
+ struct imx_clocks *clks; };
static void imx8_get_reply(struct snd_sof_dev *sdev) @@ -223,6 +238,10 @@ static int imx8_probe(struct snd_sof_dev *sdev) if (!priv) return -ENOMEM;
+ priv->clks = devm_kzalloc(&pdev->dev, sizeof(*priv->clks), GFP_KERNEL); + if (!priv->clks) + return -ENOMEM; + sdev->num_cores = 1; sdev->pdata->hw_pdata = priv; priv->dev = sdev->dev; @@ -336,6 +355,18 @@ static int imx8_probe(struct snd_sof_dev *sdev) /* set default mailbox offset for FW ready message */ sdev->dsp_box.offset = MBOX_OFFSET;
+ /* init clocks info */ + priv->clks->dsp_clks_names = imx8_dsp_clks_names; + priv->clks->num_dsp_clks = IMX8_DSP_CLK_NUM; + priv->clks->dai_clks_names = imx8_dai_clks_names; + priv->clks->num_dai_clks = IMX8_DAI_CLK_NUM; + + ret = imx8_parse_clocks(sdev, priv->clks); + if (ret < 0) + goto exit_pdev_unregister; + + imx8_enable_clocks(sdev, priv->clks); + return 0;
exit_pdev_unregister: @@ -354,6 +385,7 @@ static int imx8_remove(struct snd_sof_dev *sdev) struct imx8_priv *priv = sdev->pdata->hw_pdata; int i;
+ imx8_disable_clocks(sdev, priv->clks); platform_device_unregister(priv->ipc_dev);
for (i = 0; i < priv->num_domains; i++) { diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index 30624fafc632..482c25ab15ce 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -23,6 +23,20 @@ #define MBOX_OFFSET 0x800000 #define MBOX_SIZE 0x1000
+#define IMX8M_DSP_CLK_NUM 3 +static const char *imx8m_dsp_clks_names[IMX8M_DSP_CLK_NUM] = { + /* DSP clocks */ + "ipg", "ocram", "core", +}; + +#define IMX8M_DAI_CLK_NUM 6 +static const char *imx8m_dai_clks_names[IMX8M_DAI_CLK_NUM] = { + /* SAI3 clocks */ + "sai3_bus", "sai3_mclk0", "sai3_mclk1", "sai3_mclk2", "sai3_mclk3", + /* DMA3 clocks */ + "sdma3_root", +}; + struct imx8m_priv { struct device *dev; struct snd_sof_dev *sdev; @@ -30,6 +44,8 @@ struct imx8m_priv { /* DSP IPC handler */ struct imx_dsp_ipc *dsp_ipc; struct platform_device *ipc_dev; + + struct imx_clocks *clks; };
static void imx8m_get_reply(struct snd_sof_dev *sdev) @@ -143,6 +159,10 @@ static int imx8m_probe(struct snd_sof_dev *sdev) if (!priv) return -ENOMEM;
+ priv->clks = devm_kzalloc(&pdev->dev, sizeof(*priv->clks), GFP_KERNEL); + if (!priv->clks) + return -ENOMEM; + sdev->num_cores = 1; sdev->pdata->hw_pdata = priv; priv->dev = sdev->dev; @@ -211,6 +231,18 @@ static int imx8m_probe(struct snd_sof_dev *sdev) /* set default mailbox offset for FW ready message */ sdev->dsp_box.offset = MBOX_OFFSET;
+ /* init clocks info */ + priv->clks->dsp_clks_names = imx8m_dsp_clks_names; + priv->clks->num_dsp_clks = IMX8M_DSP_CLK_NUM; + priv->clks->dai_clks_names = imx8m_dai_clks_names; + priv->clks->num_dai_clks = IMX8M_DAI_CLK_NUM; + + ret = imx8_parse_clocks(sdev, priv->clks); + if (ret < 0) + goto exit_pdev_unregister; + + imx8_enable_clocks(sdev, priv->clks); + return 0;
exit_pdev_unregister: @@ -222,6 +254,7 @@ static int imx8m_remove(struct snd_sof_dev *sdev) { struct imx8m_priv *priv = sdev->pdata->hw_pdata;
+ imx8_disable_clocks(sdev, priv->clks); platform_device_unregister(priv->ipc_dev);
return 0;