@@ -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");
@@ -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
@@ -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++) {
@@ -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;