Loading sound/soc/sof/imx/imx-common.c +24 −0 Original line number Diff line number Diff line Loading @@ -74,4 +74,28 @@ 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 ret; ret = devm_clk_bulk_get(sdev->dev, clks->num_dsp_clks, clks->dsp_clks); if (ret) dev_err(sdev->dev, "Failed to request DSP clocks\n"); return ret; } EXPORT_SYMBOL(imx8_parse_clocks); int imx8_enable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks) { return clk_bulk_prepare_enable(clks->num_dsp_clks, clks->dsp_clks); } EXPORT_SYMBOL(imx8_enable_clocks); void imx8_disable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks) { clk_bulk_disable_unprepare(clks->num_dsp_clks, clks->dsp_clks); } EXPORT_SYMBOL(imx8_disable_clocks); MODULE_LICENSE("Dual BSD/GPL"); sound/soc/sof/imx/imx-common.h +11 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -13,4 +15,13 @@ void imx8_get_registers(struct snd_sof_dev *sdev, void imx8_dump(struct snd_sof_dev *sdev, u32 flags); struct imx_clocks { struct clk_bulk_data *dsp_clks; int num_dsp_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 sound/soc/sof/imx/imx8.c +137 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,13 @@ #define MBOX_OFFSET 0x800000 #define MBOX_SIZE 0x1000 /* DSP clocks */ static struct clk_bulk_data imx8_dsp_clks[] = { { .id = "ipg" }, { .id = "ocram" }, { .id = "core" }, }; struct imx8_priv { struct device *dev; struct snd_sof_dev *sdev; Loading @@ -57,6 +64,7 @@ struct imx8_priv { struct device **pd_dev; struct device_link **link; struct imx_clocks *clks; }; static int imx8_get_mailbox_offset(struct snd_sof_dev *sdev) Loading Loading @@ -188,6 +196,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; Loading Loading @@ -301,6 +313,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 = imx8_dsp_clks; priv->clks->num_dsp_clks = ARRAY_SIZE(imx8_dsp_clks); ret = imx8_parse_clocks(sdev, priv->clks); if (ret < 0) goto exit_pdev_unregister; ret = imx8_enable_clocks(sdev, priv->clks); if (ret < 0) goto exit_pdev_unregister; return 0; exit_pdev_unregister: Loading @@ -319,6 +343,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++) { Loading @@ -342,6 +367,92 @@ static int imx8_get_bar_index(struct snd_sof_dev *sdev, u32 type) } } static void imx8_suspend(struct snd_sof_dev *sdev) { int i; struct imx8_priv *priv = (struct imx8_priv *)sdev->pdata->hw_pdata; for (i = 0; i < DSP_MU_CHAN_NUM; i++) imx_dsp_free_channel(priv->dsp_ipc, i); imx8_disable_clocks(sdev, priv->clks); } static int imx8_resume(struct snd_sof_dev *sdev) { struct imx8_priv *priv = (struct imx8_priv *)sdev->pdata->hw_pdata; int ret; int i; ret = imx8_enable_clocks(sdev, priv->clks); if (ret < 0) return ret; for (i = 0; i < DSP_MU_CHAN_NUM; i++) imx_dsp_request_channel(priv->dsp_ipc, i); return 0; } static int imx8_dsp_runtime_resume(struct snd_sof_dev *sdev) { int ret; const struct sof_dsp_power_state target_dsp_state = { .state = SOF_DSP_PM_D0, }; ret = imx8_resume(sdev); if (ret < 0) return ret; return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); } static int imx8_dsp_runtime_suspend(struct snd_sof_dev *sdev) { const struct sof_dsp_power_state target_dsp_state = { .state = SOF_DSP_PM_D3, }; imx8_suspend(sdev); return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); } static int imx8_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state) { const struct sof_dsp_power_state target_dsp_state = { .state = target_state, }; if (!pm_runtime_suspended(sdev->dev)) imx8_suspend(sdev); return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); } static int imx8_dsp_resume(struct snd_sof_dev *sdev) { int ret; const struct sof_dsp_power_state target_dsp_state = { .state = SOF_DSP_PM_D0, }; ret = imx8_resume(sdev); if (ret < 0) return ret; if (pm_runtime_suspended(sdev->dev)) { pm_runtime_disable(sdev->dev); pm_runtime_set_active(sdev->dev); pm_runtime_mark_last_busy(sdev->dev); pm_runtime_enable(sdev->dev); pm_runtime_idle(sdev->dev); } return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); } static struct snd_soc_dai_driver imx8_dai[] = { { .name = "esai0", Loading @@ -367,6 +478,14 @@ static struct snd_soc_dai_driver imx8_dai[] = { }, }; static int imx8_dsp_set_power_state(struct snd_sof_dev *sdev, const struct sof_dsp_power_state *target_state) { sdev->dsp_power_state = *target_state; return 0; } /* i.MX8 ops */ struct snd_sof_dsp_ops sof_imx8_ops = { /* probe and remove */ Loading Loading @@ -419,6 +538,15 @@ struct snd_sof_dsp_ops sof_imx8_ops = { SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, /* PM */ .runtime_suspend = imx8_dsp_runtime_suspend, .runtime_resume = imx8_dsp_runtime_resume, .suspend = imx8_dsp_suspend, .resume = imx8_dsp_resume, .set_power_state = imx8_dsp_set_power_state, }; EXPORT_SYMBOL(sof_imx8_ops); Loading Loading @@ -468,6 +596,15 @@ struct snd_sof_dsp_ops sof_imx8x_ops = { .drv = imx8_dai, .num_drv = ARRAY_SIZE(imx8_dai), /* PM */ .runtime_suspend = imx8_dsp_runtime_suspend, .runtime_resume = imx8_dsp_runtime_resume, .suspend = imx8_dsp_suspend, .resume = imx8_dsp_resume, .set_power_state = imx8_dsp_set_power_state, /* ALSA HW info flags */ .hw_info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | Loading sound/soc/sof/imx/imx8m.c +190 −1 Original line number Diff line number Diff line Loading @@ -6,10 +6,13 @@ // // Hardware interface for audio DSP on i.MX8M #include <linux/bits.h> #include <linux/firmware.h> #include <linux/mfd/syscon.h> #include <linux/of_platform.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/regmap.h> #include <linux/module.h> #include <sound/sof.h> Loading @@ -23,6 +26,26 @@ #define MBOX_OFFSET 0x800000 #define MBOX_SIZE 0x1000 static struct clk_bulk_data imx8m_dsp_clks[] = { { .id = "ipg" }, { .id = "ocram" }, { .id = "core" }, }; /* DAP registers */ #define IMX8M_DAP_DEBUG 0x28800000 #define IMX8M_DAP_DEBUG_SIZE (64 * 1024) #define IMX8M_DAP_PWRCTL (0x4000 + 0x3020) #define IMX8M_PWRCTL_CORERESET BIT(16) /* DSP audio mix registers */ #define AudioDSP_REG0 0x100 #define AudioDSP_REG1 0x104 #define AudioDSP_REG2 0x108 #define AudioDSP_REG3 0x10c #define AudioDSP_REG2_RUNSTALL BIT(5) struct imx8m_priv { struct device *dev; struct snd_sof_dev *sdev; Loading @@ -30,6 +53,11 @@ struct imx8m_priv { /* DSP IPC handler */ struct imx_dsp_ipc *dsp_ipc; struct platform_device *ipc_dev; struct imx_clocks *clks; void __iomem *dap; struct regmap *regmap; }; static int imx8m_get_mailbox_offset(struct snd_sof_dev *sdev) Loading Loading @@ -88,7 +116,34 @@ static int imx8m_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) */ static int imx8m_run(struct snd_sof_dev *sdev) { /* TODO: start DSP using Audio MIX bits */ struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; regmap_update_bits(priv->regmap, AudioDSP_REG2, AudioDSP_REG2_RUNSTALL, 0); return 0; } static int imx8m_reset(struct snd_sof_dev *sdev) { struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; u32 pwrctl; /* put DSP into reset and stall */ pwrctl = readl(priv->dap + IMX8M_DAP_PWRCTL); pwrctl |= IMX8M_PWRCTL_CORERESET; writel(pwrctl, priv->dap + IMX8M_DAP_PWRCTL); /* keep reset asserted for 10 cycles */ usleep_range(1, 2); regmap_update_bits(priv->regmap, AudioDSP_REG2, AudioDSP_REG2_RUNSTALL, AudioDSP_REG2_RUNSTALL); /* take the DSP out of reset and keep stalled for FW loading */ pwrctl = readl(priv->dap + IMX8M_DAP_PWRCTL); pwrctl &= ~IMX8M_PWRCTL_CORERESET; writel(pwrctl, priv->dap + IMX8M_DAP_PWRCTL); return 0; } Loading @@ -108,6 +163,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; Loading Loading @@ -141,6 +200,13 @@ static int imx8m_probe(struct snd_sof_dev *sdev) goto exit_pdev_unregister; } priv->dap = devm_ioremap(sdev->dev, IMX8M_DAP_DEBUG, IMX8M_DAP_DEBUG_SIZE); if (!priv->dap) { dev_err(sdev->dev, "error: failed to map DAP debug memory area"); ret = -ENODEV; goto exit_pdev_unregister; } sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size); if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) { dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n", Loading Loading @@ -176,6 +242,25 @@ static int imx8m_probe(struct snd_sof_dev *sdev) /* set default mailbox offset for FW ready message */ sdev->dsp_box.offset = MBOX_OFFSET; priv->regmap = syscon_regmap_lookup_by_compatible("fsl,dsp-ctrl"); if (IS_ERR(priv->regmap)) { dev_err(sdev->dev, "cannot find dsp-ctrl registers"); ret = PTR_ERR(priv->regmap); goto exit_pdev_unregister; } /* init clocks info */ priv->clks->dsp_clks = imx8m_dsp_clks; priv->clks->num_dsp_clks = ARRAY_SIZE(imx8m_dsp_clks); ret = imx8_parse_clocks(sdev, priv->clks); if (ret < 0) goto exit_pdev_unregister; ret = imx8_enable_clocks(sdev, priv->clks); if (ret < 0) goto exit_pdev_unregister; return 0; exit_pdev_unregister: Loading @@ -187,6 +272,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; Loading Loading @@ -230,6 +316,100 @@ static struct snd_soc_dai_driver imx8m_dai[] = { }, }; static int imx8m_dsp_set_power_state(struct snd_sof_dev *sdev, const struct sof_dsp_power_state *target_state) { sdev->dsp_power_state = *target_state; return 0; } static int imx8m_resume(struct snd_sof_dev *sdev) { struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; int ret; int i; ret = imx8_enable_clocks(sdev, priv->clks); if (ret < 0) return ret; for (i = 0; i < DSP_MU_CHAN_NUM; i++) imx_dsp_request_channel(priv->dsp_ipc, i); return 0; } static void imx8m_suspend(struct snd_sof_dev *sdev) { struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; int i; for (i = 0; i < DSP_MU_CHAN_NUM; i++) imx_dsp_free_channel(priv->dsp_ipc, i); imx8_disable_clocks(sdev, priv->clks); } static int imx8m_dsp_runtime_resume(struct snd_sof_dev *sdev) { int ret; const struct sof_dsp_power_state target_dsp_state = { .state = SOF_DSP_PM_D0, }; ret = imx8m_resume(sdev); if (ret < 0) return ret; return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); } static int imx8m_dsp_runtime_suspend(struct snd_sof_dev *sdev) { const struct sof_dsp_power_state target_dsp_state = { .state = SOF_DSP_PM_D3, }; imx8m_suspend(sdev); return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); } static int imx8m_dsp_resume(struct snd_sof_dev *sdev) { int ret; const struct sof_dsp_power_state target_dsp_state = { .state = SOF_DSP_PM_D0, }; ret = imx8m_resume(sdev); if (ret < 0) return ret; if (pm_runtime_suspended(sdev->dev)) { pm_runtime_disable(sdev->dev); pm_runtime_set_active(sdev->dev); pm_runtime_mark_last_busy(sdev->dev); pm_runtime_enable(sdev->dev); pm_runtime_idle(sdev->dev); } return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); } static int imx8m_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state) { const struct sof_dsp_power_state target_dsp_state = { .state = target_state, }; if (!pm_runtime_suspended(sdev->dev)) imx8m_suspend(sdev); return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); } /* i.MX8 ops */ struct snd_sof_dsp_ops sof_imx8m_ops = { /* probe and remove */ Loading @@ -237,6 +417,7 @@ struct snd_sof_dsp_ops sof_imx8m_ops = { .remove = imx8m_remove, /* DSP core boot */ .run = imx8m_run, .reset = imx8m_reset, /* Block IO */ .block_read = sof_block_read, Loading Loading @@ -275,6 +456,14 @@ struct snd_sof_dsp_ops sof_imx8m_ops = { .drv = imx8m_dai, .num_drv = ARRAY_SIZE(imx8m_dai), .suspend = imx8m_dsp_suspend, .resume = imx8m_dsp_resume, .runtime_suspend = imx8m_dsp_runtime_suspend, .runtime_resume = imx8m_dsp_runtime_resume, .set_power_state = imx8m_dsp_set_power_state, .hw_info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | Loading Loading
sound/soc/sof/imx/imx-common.c +24 −0 Original line number Diff line number Diff line Loading @@ -74,4 +74,28 @@ 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 ret; ret = devm_clk_bulk_get(sdev->dev, clks->num_dsp_clks, clks->dsp_clks); if (ret) dev_err(sdev->dev, "Failed to request DSP clocks\n"); return ret; } EXPORT_SYMBOL(imx8_parse_clocks); int imx8_enable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks) { return clk_bulk_prepare_enable(clks->num_dsp_clks, clks->dsp_clks); } EXPORT_SYMBOL(imx8_enable_clocks); void imx8_disable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks) { clk_bulk_disable_unprepare(clks->num_dsp_clks, clks->dsp_clks); } EXPORT_SYMBOL(imx8_disable_clocks); MODULE_LICENSE("Dual BSD/GPL");
sound/soc/sof/imx/imx-common.h +11 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -13,4 +15,13 @@ void imx8_get_registers(struct snd_sof_dev *sdev, void imx8_dump(struct snd_sof_dev *sdev, u32 flags); struct imx_clocks { struct clk_bulk_data *dsp_clks; int num_dsp_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
sound/soc/sof/imx/imx8.c +137 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,13 @@ #define MBOX_OFFSET 0x800000 #define MBOX_SIZE 0x1000 /* DSP clocks */ static struct clk_bulk_data imx8_dsp_clks[] = { { .id = "ipg" }, { .id = "ocram" }, { .id = "core" }, }; struct imx8_priv { struct device *dev; struct snd_sof_dev *sdev; Loading @@ -57,6 +64,7 @@ struct imx8_priv { struct device **pd_dev; struct device_link **link; struct imx_clocks *clks; }; static int imx8_get_mailbox_offset(struct snd_sof_dev *sdev) Loading Loading @@ -188,6 +196,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; Loading Loading @@ -301,6 +313,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 = imx8_dsp_clks; priv->clks->num_dsp_clks = ARRAY_SIZE(imx8_dsp_clks); ret = imx8_parse_clocks(sdev, priv->clks); if (ret < 0) goto exit_pdev_unregister; ret = imx8_enable_clocks(sdev, priv->clks); if (ret < 0) goto exit_pdev_unregister; return 0; exit_pdev_unregister: Loading @@ -319,6 +343,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++) { Loading @@ -342,6 +367,92 @@ static int imx8_get_bar_index(struct snd_sof_dev *sdev, u32 type) } } static void imx8_suspend(struct snd_sof_dev *sdev) { int i; struct imx8_priv *priv = (struct imx8_priv *)sdev->pdata->hw_pdata; for (i = 0; i < DSP_MU_CHAN_NUM; i++) imx_dsp_free_channel(priv->dsp_ipc, i); imx8_disable_clocks(sdev, priv->clks); } static int imx8_resume(struct snd_sof_dev *sdev) { struct imx8_priv *priv = (struct imx8_priv *)sdev->pdata->hw_pdata; int ret; int i; ret = imx8_enable_clocks(sdev, priv->clks); if (ret < 0) return ret; for (i = 0; i < DSP_MU_CHAN_NUM; i++) imx_dsp_request_channel(priv->dsp_ipc, i); return 0; } static int imx8_dsp_runtime_resume(struct snd_sof_dev *sdev) { int ret; const struct sof_dsp_power_state target_dsp_state = { .state = SOF_DSP_PM_D0, }; ret = imx8_resume(sdev); if (ret < 0) return ret; return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); } static int imx8_dsp_runtime_suspend(struct snd_sof_dev *sdev) { const struct sof_dsp_power_state target_dsp_state = { .state = SOF_DSP_PM_D3, }; imx8_suspend(sdev); return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); } static int imx8_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state) { const struct sof_dsp_power_state target_dsp_state = { .state = target_state, }; if (!pm_runtime_suspended(sdev->dev)) imx8_suspend(sdev); return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); } static int imx8_dsp_resume(struct snd_sof_dev *sdev) { int ret; const struct sof_dsp_power_state target_dsp_state = { .state = SOF_DSP_PM_D0, }; ret = imx8_resume(sdev); if (ret < 0) return ret; if (pm_runtime_suspended(sdev->dev)) { pm_runtime_disable(sdev->dev); pm_runtime_set_active(sdev->dev); pm_runtime_mark_last_busy(sdev->dev); pm_runtime_enable(sdev->dev); pm_runtime_idle(sdev->dev); } return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); } static struct snd_soc_dai_driver imx8_dai[] = { { .name = "esai0", Loading @@ -367,6 +478,14 @@ static struct snd_soc_dai_driver imx8_dai[] = { }, }; static int imx8_dsp_set_power_state(struct snd_sof_dev *sdev, const struct sof_dsp_power_state *target_state) { sdev->dsp_power_state = *target_state; return 0; } /* i.MX8 ops */ struct snd_sof_dsp_ops sof_imx8_ops = { /* probe and remove */ Loading Loading @@ -419,6 +538,15 @@ struct snd_sof_dsp_ops sof_imx8_ops = { SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, /* PM */ .runtime_suspend = imx8_dsp_runtime_suspend, .runtime_resume = imx8_dsp_runtime_resume, .suspend = imx8_dsp_suspend, .resume = imx8_dsp_resume, .set_power_state = imx8_dsp_set_power_state, }; EXPORT_SYMBOL(sof_imx8_ops); Loading Loading @@ -468,6 +596,15 @@ struct snd_sof_dsp_ops sof_imx8x_ops = { .drv = imx8_dai, .num_drv = ARRAY_SIZE(imx8_dai), /* PM */ .runtime_suspend = imx8_dsp_runtime_suspend, .runtime_resume = imx8_dsp_runtime_resume, .suspend = imx8_dsp_suspend, .resume = imx8_dsp_resume, .set_power_state = imx8_dsp_set_power_state, /* ALSA HW info flags */ .hw_info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | Loading
sound/soc/sof/imx/imx8m.c +190 −1 Original line number Diff line number Diff line Loading @@ -6,10 +6,13 @@ // // Hardware interface for audio DSP on i.MX8M #include <linux/bits.h> #include <linux/firmware.h> #include <linux/mfd/syscon.h> #include <linux/of_platform.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/regmap.h> #include <linux/module.h> #include <sound/sof.h> Loading @@ -23,6 +26,26 @@ #define MBOX_OFFSET 0x800000 #define MBOX_SIZE 0x1000 static struct clk_bulk_data imx8m_dsp_clks[] = { { .id = "ipg" }, { .id = "ocram" }, { .id = "core" }, }; /* DAP registers */ #define IMX8M_DAP_DEBUG 0x28800000 #define IMX8M_DAP_DEBUG_SIZE (64 * 1024) #define IMX8M_DAP_PWRCTL (0x4000 + 0x3020) #define IMX8M_PWRCTL_CORERESET BIT(16) /* DSP audio mix registers */ #define AudioDSP_REG0 0x100 #define AudioDSP_REG1 0x104 #define AudioDSP_REG2 0x108 #define AudioDSP_REG3 0x10c #define AudioDSP_REG2_RUNSTALL BIT(5) struct imx8m_priv { struct device *dev; struct snd_sof_dev *sdev; Loading @@ -30,6 +53,11 @@ struct imx8m_priv { /* DSP IPC handler */ struct imx_dsp_ipc *dsp_ipc; struct platform_device *ipc_dev; struct imx_clocks *clks; void __iomem *dap; struct regmap *regmap; }; static int imx8m_get_mailbox_offset(struct snd_sof_dev *sdev) Loading Loading @@ -88,7 +116,34 @@ static int imx8m_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) */ static int imx8m_run(struct snd_sof_dev *sdev) { /* TODO: start DSP using Audio MIX bits */ struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; regmap_update_bits(priv->regmap, AudioDSP_REG2, AudioDSP_REG2_RUNSTALL, 0); return 0; } static int imx8m_reset(struct snd_sof_dev *sdev) { struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; u32 pwrctl; /* put DSP into reset and stall */ pwrctl = readl(priv->dap + IMX8M_DAP_PWRCTL); pwrctl |= IMX8M_PWRCTL_CORERESET; writel(pwrctl, priv->dap + IMX8M_DAP_PWRCTL); /* keep reset asserted for 10 cycles */ usleep_range(1, 2); regmap_update_bits(priv->regmap, AudioDSP_REG2, AudioDSP_REG2_RUNSTALL, AudioDSP_REG2_RUNSTALL); /* take the DSP out of reset and keep stalled for FW loading */ pwrctl = readl(priv->dap + IMX8M_DAP_PWRCTL); pwrctl &= ~IMX8M_PWRCTL_CORERESET; writel(pwrctl, priv->dap + IMX8M_DAP_PWRCTL); return 0; } Loading @@ -108,6 +163,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; Loading Loading @@ -141,6 +200,13 @@ static int imx8m_probe(struct snd_sof_dev *sdev) goto exit_pdev_unregister; } priv->dap = devm_ioremap(sdev->dev, IMX8M_DAP_DEBUG, IMX8M_DAP_DEBUG_SIZE); if (!priv->dap) { dev_err(sdev->dev, "error: failed to map DAP debug memory area"); ret = -ENODEV; goto exit_pdev_unregister; } sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size); if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) { dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n", Loading Loading @@ -176,6 +242,25 @@ static int imx8m_probe(struct snd_sof_dev *sdev) /* set default mailbox offset for FW ready message */ sdev->dsp_box.offset = MBOX_OFFSET; priv->regmap = syscon_regmap_lookup_by_compatible("fsl,dsp-ctrl"); if (IS_ERR(priv->regmap)) { dev_err(sdev->dev, "cannot find dsp-ctrl registers"); ret = PTR_ERR(priv->regmap); goto exit_pdev_unregister; } /* init clocks info */ priv->clks->dsp_clks = imx8m_dsp_clks; priv->clks->num_dsp_clks = ARRAY_SIZE(imx8m_dsp_clks); ret = imx8_parse_clocks(sdev, priv->clks); if (ret < 0) goto exit_pdev_unregister; ret = imx8_enable_clocks(sdev, priv->clks); if (ret < 0) goto exit_pdev_unregister; return 0; exit_pdev_unregister: Loading @@ -187,6 +272,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; Loading Loading @@ -230,6 +316,100 @@ static struct snd_soc_dai_driver imx8m_dai[] = { }, }; static int imx8m_dsp_set_power_state(struct snd_sof_dev *sdev, const struct sof_dsp_power_state *target_state) { sdev->dsp_power_state = *target_state; return 0; } static int imx8m_resume(struct snd_sof_dev *sdev) { struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; int ret; int i; ret = imx8_enable_clocks(sdev, priv->clks); if (ret < 0) return ret; for (i = 0; i < DSP_MU_CHAN_NUM; i++) imx_dsp_request_channel(priv->dsp_ipc, i); return 0; } static void imx8m_suspend(struct snd_sof_dev *sdev) { struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; int i; for (i = 0; i < DSP_MU_CHAN_NUM; i++) imx_dsp_free_channel(priv->dsp_ipc, i); imx8_disable_clocks(sdev, priv->clks); } static int imx8m_dsp_runtime_resume(struct snd_sof_dev *sdev) { int ret; const struct sof_dsp_power_state target_dsp_state = { .state = SOF_DSP_PM_D0, }; ret = imx8m_resume(sdev); if (ret < 0) return ret; return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); } static int imx8m_dsp_runtime_suspend(struct snd_sof_dev *sdev) { const struct sof_dsp_power_state target_dsp_state = { .state = SOF_DSP_PM_D3, }; imx8m_suspend(sdev); return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); } static int imx8m_dsp_resume(struct snd_sof_dev *sdev) { int ret; const struct sof_dsp_power_state target_dsp_state = { .state = SOF_DSP_PM_D0, }; ret = imx8m_resume(sdev); if (ret < 0) return ret; if (pm_runtime_suspended(sdev->dev)) { pm_runtime_disable(sdev->dev); pm_runtime_set_active(sdev->dev); pm_runtime_mark_last_busy(sdev->dev); pm_runtime_enable(sdev->dev); pm_runtime_idle(sdev->dev); } return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); } static int imx8m_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state) { const struct sof_dsp_power_state target_dsp_state = { .state = target_state, }; if (!pm_runtime_suspended(sdev->dev)) imx8m_suspend(sdev); return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); } /* i.MX8 ops */ struct snd_sof_dsp_ops sof_imx8m_ops = { /* probe and remove */ Loading @@ -237,6 +417,7 @@ struct snd_sof_dsp_ops sof_imx8m_ops = { .remove = imx8m_remove, /* DSP core boot */ .run = imx8m_run, .reset = imx8m_reset, /* Block IO */ .block_read = sof_block_read, Loading Loading @@ -275,6 +456,14 @@ struct snd_sof_dsp_ops sof_imx8m_ops = { .drv = imx8m_dai, .num_drv = ARRAY_SIZE(imx8m_dai), .suspend = imx8m_dsp_suspend, .resume = imx8m_dsp_resume, .runtime_suspend = imx8m_dsp_runtime_suspend, .runtime_resume = imx8m_dsp_runtime_resume, .set_power_state = imx8m_dsp_set_power_state, .hw_info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | Loading