From dd1853b3fbeb77ecbfd4e6ec2c37371614c605f1 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Tue, 21 May 2019 15:12:45 +0800 Subject: [PATCH 1/6] ASoC: SOF: add ref counts for DSP cores Add atomic ref counts core_refs for each audio DSP cores, for power management of each core based on usage on it, this is preparation for audio DSP multi-core support. Signed-off-by: Keyon Jie --- sound/soc/sof/sof-priv.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index b4243a4e877e55..e00441db15e164 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -36,6 +36,9 @@ /* max BARs mmaped devices can use */ #define SND_SOF_BARS 8 +/* max core number */ +#define SND_SOF_CORE_MAX 8 + /* time in ms for runtime suspend delay */ #define SND_SOF_SUSPEND_DELAY_MS 2000 @@ -412,6 +415,7 @@ struct snd_sof_dev { struct list_head route_list; struct snd_soc_component *component; u32 enabled_cores_mask; /* keep track of enabled cores */ + int core_refs[SND_SOF_CORE_MAX]; /* FW configuration */ struct sof_ipc_dma_buffer_data *info_buffer; From 0ebb026db597d19dc54e710192299c3d23f6fb30 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Mon, 13 May 2019 14:46:10 +0800 Subject: [PATCH 2/6] ASoC: SOF: Add cores_status_mutex to protect core status We are changing enabled_cores_mask dynamically during pipeline load/unload, there might be race for enabled_cores_mask when a pipeline loading and another pipeline(run on the same core) is unloading. Here introduces a mutex to protect this enabled_cores_mask and core_refs, and use it when never we need to read/write to it. Signed-off-by: Keyon Jie --- sound/soc/sof/core.c | 1 + sound/soc/sof/loader.c | 2 ++ sound/soc/sof/sof-priv.h | 2 ++ sound/soc/sof/topology.c | 7 +++++++ 4 files changed, 12 insertions(+) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 15200841a12a12..a8b5f95d20a3c4 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -477,6 +477,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) INIT_LIST_HEAD(&sdev->route_list); spin_lock_init(&sdev->ipc_lock); spin_lock_init(&sdev->hw_lock); + mutex_init(&sdev->cores_status_mutex); if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) INIT_WORK(&sdev->probe_work, sof_probe_work); diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index 93cb8fd0844fa6..8f486f9f2e30d7 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -394,8 +394,10 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) return ret; } + mutex_lock(&sdev->cores_status_mutex); /* fw boot is complete. Update the active cores mask */ sdev->enabled_cores_mask = init_core_mask; + mutex_unlock(&sdev->cores_status_mutex); return 0; } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index e00441db15e164..d2aa3e44b5e5d8 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -416,6 +416,8 @@ struct snd_sof_dev { struct snd_soc_component *component; u32 enabled_cores_mask; /* keep track of enabled cores */ int core_refs[SND_SOF_CORE_MAX]; + /* protects enabled_cores_mask & core_refs */ + struct mutex cores_status_mutex; /* FW configuration */ struct sof_ipc_dma_buffer_data *info_buffer; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 12b7d900b9c245..5e8b215a26ec1a 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1361,11 +1361,14 @@ int sof_load_pipeline_ipc(struct snd_sof_dev *sdev, return ret; } + mutex_lock(&sdev->cores_status_mutex); + /* power up the core that this pipeline is scheduled on */ ret = snd_sof_dsp_core_power_up(sdev, 1 << pipeline->core); if (ret < 0) { dev_err(sdev->dev, "error: powering up pipeline schedule core %d\n", pipeline->core); + mutex_unlock(&sdev->cores_status_mutex); return ret; } @@ -1390,6 +1393,8 @@ int sof_load_pipeline_ipc(struct snd_sof_dev *sdev, if (ret < 0) dev_err(sdev->dev, "error: core enable ipc failure\n"); + mutex_unlock(&sdev->cores_status_mutex); + return ret; } @@ -2149,6 +2154,7 @@ static int sof_widget_unload(struct snd_soc_component *scomp, } break; case snd_soc_dapm_scheduler: + mutex_lock(&sdev->cores_status_mutex); /* power down the pipeline schedule core */ pipeline = swidget->private; @@ -2160,6 +2166,7 @@ static int sof_widget_unload(struct snd_soc_component *scomp, /* update enabled cores mask */ sdev->enabled_cores_mask &= ~(1 << pipeline->core); + mutex_unlock(&sdev->cores_status_mutex); break; default: break; From 9939e848863d74cedd51782b8afff428f88c32a0 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Tue, 21 May 2019 15:17:02 +0800 Subject: [PATCH 3/6] ASoC: SOF: update ref counts of cores at probe, suspend, and FW booted Initialize ref counts to DSP cores to be 0s, at start of probing. Once FW booted, update the ref counts of dsp cores, that is, for each powered on core, set the ref count of it to be 1. Reset ref counts of DSP cores to be 0s at suspend, to align with the status before next FW boot at resume. Signed-off-by: Keyon Jie --- sound/soc/sof/core.c | 4 ++++ sound/soc/sof/loader.c | 9 ++++++++- sound/soc/sof/pm.c | 7 +++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index a8b5f95d20a3c4..e964541797507f 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -451,6 +451,7 @@ static void sof_probe_work(struct work_struct *work) int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) { struct snd_sof_dev *sdev; + int i; sdev = devm_kzalloc(dev, sizeof(*sdev), GFP_KERNEL); if (!sdev) @@ -479,6 +480,9 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) spin_lock_init(&sdev->hw_lock); mutex_init(&sdev->cores_status_mutex); + for (i = 0; i < ARRAY_SIZE(sdev->core_refs); i++) + sdev->core_refs[i] = 0; + if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) INIT_WORK(&sdev->probe_work, sof_probe_work); diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index 8f486f9f2e30d7..9d9a1459863c26 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -338,6 +338,7 @@ EXPORT_SYMBOL(snd_sof_load_firmware); int snd_sof_run_firmware(struct snd_sof_dev *sdev) { int ret; + int i = 0; int init_core_mask; init_waitqueue_head(&sdev->boot_wait); @@ -395,8 +396,14 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) } mutex_lock(&sdev->cores_status_mutex); - /* fw boot is complete. Update the active cores mask */ + /* fw booted, update the active cores mask and ref counts */ sdev->enabled_cores_mask = init_core_mask; + while (init_core_mask && (i < SND_SOF_CORE_MAX)) { + if (init_core_mask & BIT(0)) + sdev->core_refs[i] = 1; + init_core_mask >>= 1; + i++; + } mutex_unlock(&sdev->cores_status_mutex); return 0; diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index e23beaeefe0054..77e96682220375 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -333,6 +333,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); int ret; + int i; /* do nothing if dsp suspend callback is not set */ if (!sof_ops(sdev)->suspend) @@ -385,6 +386,12 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) "error: failed to power down DSP during suspend %d\n", ret); + mutex_lock(&sdev->cores_status_mutex); + /* reset ref counts of DSP cores */ + for (i = 0; i < ARRAY_SIZE(sdev->core_refs); i++) + sdev->core_refs[i] = 0; + mutex_unlock(&sdev->cores_status_mutex); + return ret; } From bc4c6081c4c344ceaeaf5c35fd9b3c00da50a1a0 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 24 May 2019 14:52:36 +0800 Subject: [PATCH 4/6] ASoC: SOF: ipc: add an interface for DSP core enable Add a general DSP core enable/disable IPC interface, this is now aligned with FW that sending the whole enable_mask, may consider changing to send IPC for specific core only with each IPC in the future when FW change is ready (ABI bump needed). Signed-off-by: Keyon Jie --- sound/soc/sof/ipc.c | 24 ++++++++++++++++++++++++ sound/soc/sof/sof-priv.h | 3 +++ 2 files changed, 27 insertions(+) diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index b2f359d2f7e530..3a11c6fe3cf04a 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -722,6 +722,30 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_ipc *ipc, } EXPORT_SYMBOL(snd_sof_ipc_set_get_comp_data); +/* DSP core enable/disable IPC according to enabled_cores_mask */ +int snd_sof_ipc_core_enable(struct snd_sof_dev *sdev) +{ + struct sof_ipc_pm_core_config pm_core_config; + int err; + + memset(&pm_core_config, 0, sizeof(pm_core_config)); + pm_core_config.enable_mask = sdev->enabled_cores_mask; + + /* configure CORE_ENABLE ipc message */ + pm_core_config.hdr.size = sizeof(pm_core_config); + pm_core_config.hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE; + + /* send ipc */ + err = sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd, + &pm_core_config, sizeof(pm_core_config), + &pm_core_config, sizeof(pm_core_config)); + if (err < 0) + dev_err(sdev->dev, "error: core enable ipc failure\n"); + + return err; +} +EXPORT_SYMBOL(snd_sof_ipc_core_enable); + /* * IPC layer enumeration. */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index d2aa3e44b5e5d8..65fc6ba3895708 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -541,6 +541,9 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_ipc *ipc, enum sof_ipc_ctrl_cmd ctrl_cmd, bool send); +/* DSP core enable/disable IPC */ +int snd_sof_ipc_core_enable(struct snd_sof_dev *sdev); + /* * Topology. * There is no snd_sof_free_topology since topology components will From afcf5da45b0a85f3c3ee06b50849e3c869d3d6ba Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 24 May 2019 15:00:01 +0800 Subject: [PATCH 5/6] ASoC: SOF: ops: add snd_sof_dsp_core_get/put interfaces We need get/put interfaces to manage the ref counts of DSP cores, call the real core power up/down, and send ipc to align the core power status with FW, the interfaces are typically used when new modules/pipelines which are assigned to run on a specific DSP core. Signed-off-by: Keyon Jie --- sound/soc/sof/ops.c | 80 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/ops.h | 4 +++ 2 files changed, 84 insertions(+) diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c index 7a27c3b719e772..b3b96d6d86261c 100644 --- a/sound/soc/sof/ops.c +++ b/sound/soc/sof/ops.c @@ -161,3 +161,83 @@ void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset) snd_sof_trace_notify_for_error(sdev); } EXPORT_SYMBOL(snd_sof_dsp_panic); + +/* This is for getting ref count for a DSP core and power on it if needed */ +int snd_sof_dsp_core_get(struct snd_sof_dev *sdev, u32 core_idx) +{ + int ret; + + mutex_lock(&sdev->cores_status_mutex); + + /* already powered on, return */ + if (sdev->core_refs[core_idx] > 0) { + sdev->core_refs[core_idx]++; + dev_vdbg(sdev->dev, "core_get: core_refs[%d] %d, no need power up\n", + core_idx, sdev->core_refs[core_idx]); + mutex_unlock(&sdev->cores_status_mutex); + return 0;/* core already enabled, return */ + } + + dev_vdbg(sdev->dev, "core_get: core_refs[%d] %d, powering it up...\n", + core_idx, sdev->core_refs[core_idx]); + /* power up the core that this pipeline is scheduled on */ + ret = snd_sof_dsp_core_power_up(sdev, BIT(core_idx)); + if (ret < 0) { + dev_err(sdev->dev, "error: powering up pipeline schedule core %d\n", + core_idx); + mutex_unlock(&sdev->cores_status_mutex); + return ret; + } + + /* update core ref count and enabled_cores_mask */ + sdev->core_refs[core_idx]++; + sdev->enabled_cores_mask |= BIT(core_idx); + + /* Now notify DSP that the core power status changed */ + snd_sof_ipc_core_enable(sdev); + + mutex_unlock(&sdev->cores_status_mutex); + + return 0; +} +EXPORT_SYMBOL(snd_sof_dsp_core_get); + +/* This is for putting ref count for a DSP core and power off it if needed */ +int snd_sof_dsp_core_put(struct snd_sof_dev *sdev, u32 core_idx) +{ + int ret; + + mutex_lock(&sdev->cores_status_mutex); + + /* return if the core is still in use */ + if (sdev->core_refs[core_idx] > 1) { + sdev->core_refs[core_idx]--; + dev_vdbg(sdev->dev, "core_put: core_refs[%d] %d, no need power down\n", + core_idx, sdev->core_refs[core_idx]); + mutex_unlock(&sdev->cores_status_mutex); + return 0; + } + + dev_vdbg(sdev->dev, "core_put: core_refs[%d] %d, powering it down...\n", + core_idx, sdev->core_refs[core_idx]); + /* power down the pipeline schedule core */ + ret = snd_sof_dsp_core_power_down(sdev, BIT(core_idx)); + if (ret < 0) { + dev_err(sdev->dev, "error: powering down pipeline schedule core %d\n", + core_idx); + mutex_unlock(&sdev->cores_status_mutex); + return ret; + } + + /* update core ref count and enabled_cores_mask */ + sdev->core_refs[core_idx]--; + sdev->enabled_cores_mask &= ~BIT(core_idx); + + /* Now notify DSP that the core power status changed */ + snd_sof_ipc_core_enable(sdev); + + mutex_unlock(&sdev->cores_status_mutex); + + return 0; +} +EXPORT_SYMBOL(snd_sof_dsp_core_put); diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 793c1aea0c53b3..a72a7ab14a8711 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -446,4 +446,8 @@ int snd_sof_dsp_register_poll(struct snd_sof_dev *sdev, u32 bar, u32 offset, u32 interval_us); void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset); + +int snd_sof_dsp_core_get(struct snd_sof_dev *sdev, u32 core_idx); +int snd_sof_dsp_core_put(struct snd_sof_dev *sdev, u32 core_idx); + #endif From 3c0f719d9f98ad1b843ac2fea035eba1eb99065a Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 24 May 2019 15:10:16 +0800 Subject: [PATCH 6/6] ASoC: SOF: topology: refine DSP multi-core support Refine DSP multi-core support to use the new defined APIs directlly. For pipeline loading, call snd_sof_dsp_core_get(), it will increase ref count of the specific DSP core, and power on the core when necessary (it is on powered off stage). For pipeline(scheduler widget) unloading, call snd_sof_dsp_core_put(), it will decrease ref count of the specific DSP core, and power off it when necessary (no more usage of it). Signed-off-by: Keyon Jie --- sound/soc/sof/topology.c | 51 ++++------------------------------------ 1 file changed, 4 insertions(+), 47 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 5e8b215a26ec1a..21ca62e7b49f3d 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1351,7 +1351,6 @@ int sof_load_pipeline_ipc(struct snd_sof_dev *sdev, struct sof_ipc_pipe_new *pipeline, struct sof_ipc_comp_reply *r) { - struct sof_ipc_pm_core_config pm_core_config; int ret; ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline, @@ -1361,41 +1360,8 @@ int sof_load_pipeline_ipc(struct snd_sof_dev *sdev, return ret; } - mutex_lock(&sdev->cores_status_mutex); - - /* power up the core that this pipeline is scheduled on */ - ret = snd_sof_dsp_core_power_up(sdev, 1 << pipeline->core); - if (ret < 0) { - dev_err(sdev->dev, "error: powering up pipeline schedule core %d\n", - pipeline->core); - mutex_unlock(&sdev->cores_status_mutex); - return ret; - } - - /* update enabled cores mask */ - sdev->enabled_cores_mask |= 1 << pipeline->core; - - /* - * Now notify DSP that the core that this pipeline is scheduled on - * has been powered up - */ - memset(&pm_core_config, 0, sizeof(pm_core_config)); - pm_core_config.enable_mask = sdev->enabled_cores_mask; - - /* configure CORE_ENABLE ipc message */ - pm_core_config.hdr.size = sizeof(pm_core_config); - pm_core_config.hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE; - - /* send ipc */ - ret = sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd, - &pm_core_config, sizeof(pm_core_config), - &pm_core_config, sizeof(pm_core_config)); - if (ret < 0) - dev_err(sdev->dev, "error: core enable ipc failure\n"); - - mutex_unlock(&sdev->cores_status_mutex); - - return ret; + /* increase ref count of the DSP core */ + return snd_sof_dsp_core_get(sdev, pipeline->core); } static int sof_widget_load_pipeline(struct snd_soc_component *scomp, @@ -2154,19 +2120,10 @@ static int sof_widget_unload(struct snd_soc_component *scomp, } break; case snd_soc_dapm_scheduler: - mutex_lock(&sdev->cores_status_mutex); - - /* power down the pipeline schedule core */ pipeline = swidget->private; - ret = snd_sof_dsp_core_power_down(sdev, 1 << pipeline->core); - if (ret < 0) - dev_err(sdev->dev, "error: powering down pipeline schedule core %d\n", - pipeline->core); - - /* update enabled cores mask */ - sdev->enabled_cores_mask &= ~(1 << pipeline->core); - mutex_unlock(&sdev->cores_status_mutex); + /* decrease ref count of the DSP core */ + ret = snd_sof_dsp_core_put(sdev, pipeline->core); break; default: break;