[PATCH v2 0/5] ASoC: SOF: Add PM support for i.MX8/i.MX8X/i.MX8M
From: Daniel Baluta daniel.baluta@nxp.com
This patch series adds support for System PM and Runtime PM with SOF for i.MX8 platforms.
First patch adds common code to manage clocks, patch 2/3 adds the actual PM support, patch 4/5 implement the DSP start / reset callbacks for i.mx8m.
Changes since v1: * rebased on top of Mark's for-next branch
Daniel Baluta (5): ASoC: SOF: imx: Add code to manage DSP related clocks ASoC: SOF: imx8: Add runtime PM / System PM support ASoC: SOF: imx8m: Add runtime PM / System PM support ASoC: SOF: imx8m: Implement DSP start ASoC: SOF: imx8m: Implement reset callback
sound/soc/sof/imx/imx-common.c | 24 +++++ sound/soc/sof/imx/imx-common.h | 11 ++ sound/soc/sof/imx/imx8.c | 137 +++++++++++++++++++++++ sound/soc/sof/imx/imx8m.c | 191 ++++++++++++++++++++++++++++++++- 4 files changed, 362 insertions(+), 1 deletion(-)
From: Daniel Baluta daniel.baluta@nxp.com
We need at least 3 clocks in order to power up and access DSP core registers found on i.MX8QM, i.MX8QXP and i.MX8MP platforms.
Add code to request these clocks and enable them at probe. Next patches will add PM support which will only activate clocks when DSP is used.
DSP clocks are already documented in Documentation/devicetree/bindings/dsp/fsl,dsp.yaml
We choose to add: * imx8_parse_clocks * imx8_enable_clocks * imx8_disable_clocks
wrappers because in the future DSP will need to take care about the clocks of other related Audio IPs (e.g SAI, ESAI).
Signed-off-by: Daniel Baluta daniel.baluta@nxp.com Reviewed-by: Paul Olaru paul.olaru@oss.nxp.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/imx/imx-common.c | 24 ++++++++++++++++++++++++ sound/soc/sof/imx/imx-common.h | 11 +++++++++++ sound/soc/sof/imx/imx8.c | 23 +++++++++++++++++++++++ sound/soc/sof/imx/imx8m.c | 23 +++++++++++++++++++++++ 4 files changed, 81 insertions(+)
diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c index 8826ef94f04a..9371e9062cb1 100644 --- a/sound/soc/sof/imx/imx-common.c +++ b/sound/soc/sof/imx/imx-common.c @@ -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"); diff --git a/sound/soc/sof/imx/imx-common.h b/sound/soc/sof/imx/imx-common.h index 1cc7d6704182..ec4b3a5c7496 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,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 diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index 0aeb44d0acc7..32f852cbba30 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -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; @@ -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) @@ -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->pdata->hw_pdata = priv; priv->dev = sdev->dev; priv->sdev = sdev; @@ -300,6 +312,16 @@ 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; + + imx8_enable_clocks(sdev, priv->clks); + return 0;
exit_pdev_unregister: @@ -318,6 +340,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 f454a5d0a87e..ab40c0bdf796 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -23,6 +23,12 @@ #define MBOX_OFFSET 0x800000 #define MBOX_SIZE 0x1000
+static struct clk_bulk_data imx8m_dsp_clks[] = { + { .id = "ipg" }, + { .id = "ocram" }, + { .id = "core" }, +}; + struct imx8m_priv { struct device *dev; struct snd_sof_dev *sdev; @@ -30,6 +36,8 @@ struct imx8m_priv { /* DSP IPC handler */ struct imx_dsp_ipc *dsp_ipc; struct platform_device *ipc_dev; + + struct imx_clocks *clks; };
static int imx8m_get_mailbox_offset(struct snd_sof_dev *sdev) @@ -108,6 +116,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->pdata->hw_pdata = priv; priv->dev = sdev->dev; priv->sdev = sdev; @@ -175,6 +187,16 @@ 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 = 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; + + imx8_enable_clocks(sdev, priv->clks); + return 0;
exit_pdev_unregister: @@ -186,6 +208,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;
From: Daniel Baluta daniel.baluta@nxp.com
Handle clocks and mailbox channels at runtime suspend/resume in order to save power.
DSP runtime PM uses a timeout of 2s. If device is idle for 2s system will enter runtime suspend.
Because SOF state machine assumes that even if the DSP wasn't previously active at a System resume, will re-load the firmware we need to make sure that all needed resources are active.
Kernel core will take care of enabling the PD, we need to make sure that we request the MU channels.
Signed-off-by: Daniel Baluta daniel.baluta@nxp.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/imx/imx8.c | 116 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-)
diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index 32f852cbba30..c4755c88d492 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -320,7 +320,9 @@ static int imx8_probe(struct snd_sof_dev *sdev) if (ret < 0) goto exit_pdev_unregister;
- imx8_enable_clocks(sdev, priv->clks); + ret = imx8_enable_clocks(sdev, priv->clks); + if (ret < 0) + goto exit_pdev_unregister;
return 0;
@@ -364,6 +366,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", @@ -389,6 +477,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 */ @@ -441,6 +537,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);
@@ -490,6 +595,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 |
From: Daniel Baluta daniel.baluta@nxp.com
We make use of common imx8m_suspend / imx8m_resume functions for both system PM and runtime PM.
imx8m_suspend: - frees the MU channels - disables the clocks
imx8m_resume - enables the clocks - requests the MU channels
On i.MX8MP there is no dedicated functionality to put the DSP in reset. The only way of doing this is to POWER DOWN the Audiomix domain.
We are able to do this because turning off the clocks and freeing the channels makes the Audiomix to have no users thus PM kernel core turns it down.
SOF core will not call system PM suspend handler if the DSP is already down, but at resume it will call the system PM resume. So, we need to keep track of the state via snd_sof_dsp_set_power_state
Few insights on how SOF core handles the PM: - SOF core uses PM runtime autosuspend (with a timeout of 2 secs) - at probe, SOF core boots the DSP and lets the PM runtime suspend to turn it off, if there is no activity - when someone opens the ALSA sound card (aplay/arecord, etc) ALSA core calls PM runtime resume to turn on the DSP - when the ALSA sound card is closed SOF core make use of PM subsystem to call PM runtime suspend and thus turning off the DSP.
Signed-off-by: Daniel Baluta daniel.baluta@nxp.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/imx/imx8m.c | 106 +++++++++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 1 deletion(-)
diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index ab40c0bdf796..b050d4cf9cd5 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -195,7 +195,9 @@ static int imx8m_probe(struct snd_sof_dev *sdev) if (ret < 0) goto exit_pdev_unregister;
- imx8_enable_clocks(sdev, priv->clks); + ret = imx8_enable_clocks(sdev, priv->clks); + if (ret < 0) + goto exit_pdev_unregister;
return 0;
@@ -252,6 +254,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 */ @@ -297,6 +393,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 |
From: Daniel Baluta daniel.baluta@nxp.com
On i.MX8M DSP is controlled via a set of registers from Audio MIX. This patches gets a reference (via regmap) to Audio Mix registers and implements DSP start.
Signed-off-by: Daniel Baluta daniel.baluta@nxp.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/imx/imx8m.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index b050d4cf9cd5..9972ca8e6ec6 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -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> @@ -29,6 +32,14 @@ static struct clk_bulk_data imx8m_dsp_clks[] = { { .id = "core" }, };
+/* 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; @@ -38,6 +49,8 @@ struct imx8m_priv { struct platform_device *ipc_dev;
struct imx_clocks *clks; + + struct regmap *regmap; };
static int imx8m_get_mailbox_offset(struct snd_sof_dev *sdev) @@ -96,7 +109,10 @@ 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; }
@@ -187,6 +203,13 @@ 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);
From: Daniel Baluta daniel.baluta@nxp.com
Resume common flow (System PM / Runtime PM) is like this:
sof_resume -> specific device resume -> snd_sof_load_firmware -> snd_sof_dsp_reset (1) -> load_modules() -> snd_sof_run_firmware (2)
We need to implement dsp_reset callback (1) that will actually reset the DSP but keep it stalled.
In order to implement this we do the following: -> put DSP into reset (assert CoreReset bit from PWRCTL) -> stall the DSP using RunStall bit from AudioDSP_REG2 mix -> take DSP out of reset (de-assert CoreReset bit from PWRCTL)
At this moment the DSP is taken out of reset and Stalled! This means that we can load the firmware and then start the DSP (2).
Until now we resetted the DSP by turning down the Audiomix PD. This doesn't work for Runtime PM if another IP is keeping Audiomix PD up.
By introducing dsp_reset() we no longer rely on turning off the audiomix to reset the DSP.
Signed-off-by: Daniel Baluta daniel.baluta@nxp.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Kai Vehmanen kai.vehmanen@linux.intel.com --- sound/soc/sof/imx/imx8m.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+)
diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index 9972ca8e6ec6..8f24c6db7f5b 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -32,6 +32,12 @@ static struct clk_bulk_data imx8m_dsp_clks[] = { { .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 @@ -50,6 +56,7 @@ struct imx8m_priv {
struct imx_clocks *clks;
+ void __iomem *dap; struct regmap *regmap; };
@@ -116,6 +123,30 @@ static int imx8m_run(struct snd_sof_dev *sdev) 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; +} + static int imx8m_probe(struct snd_sof_dev *sdev) { struct platform_device *pdev = @@ -168,6 +199,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", @@ -378,6 +416,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,
On Fri, 19 Nov 2021 11:43:14 +0200, Daniel Baluta wrote:
From: Daniel Baluta daniel.baluta@nxp.com
This patch series adds support for System PM and Runtime PM with SOF for i.MX8 platforms.
First patch adds common code to manage clocks, patch 2/3 adds the actual PM support, patch 4/5 implement the DSP start / reset callbacks for i.mx8m.
[...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
Thanks!
[1/5] ASoC: SOF: imx: Add code to manage DSP related clocks commit: 8253aa4700b37cef1ca3bbda0d986349357608d3 [2/5] ASoC: SOF: imx8: Add runtime PM / System PM support commit: 6fc8515806dfd5b7d3198c189b51e7624aadafdc [3/5] ASoC: SOF: imx8m: Add runtime PM / System PM support commit: a73b493d8e1b37acad686c15321d2eaab45567ce [4/5] ASoC: SOF: imx8m: Implement DSP start commit: 9ba23717b2927071ddb49f3d6719244e3fe8f4c9 [5/5] ASoC: SOF: imx8m: Implement reset callback commit: 3bf4cd8b747a222f0f454f3220199c99f1c03da6
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
participants (2)
-
Daniel Baluta
-
Mark Brown