diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 15200841a12a12..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) @@ -477,6 +478,10 @@ 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); + + 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/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/loader.c b/sound/soc/sof/loader.c index 93cb8fd0844fa6..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); @@ -394,8 +395,16 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) return ret; } - /* fw boot is complete. Update the active cores mask */ + mutex_lock(&sdev->cores_status_mutex); + /* 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/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 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; } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index b4243a4e877e55..65fc6ba3895708 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,9 @@ 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]; + /* protects enabled_cores_mask & core_refs */ + struct mutex cores_status_mutex; /* FW configuration */ struct sof_ipc_dma_buffer_data *info_buffer; @@ -535,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 diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 12b7d900b9c245..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,36 +1360,8 @@ int sof_load_pipeline_ipc(struct snd_sof_dev *sdev, return ret; } - /* 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); - 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"); - - 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, @@ -2149,17 +2120,10 @@ static int sof_widget_unload(struct snd_soc_component *scomp, } break; case snd_soc_dapm_scheduler: - - /* 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); + /* decrease ref count of the DSP core */ + ret = snd_sof_dsp_core_put(sdev, pipeline->core); break; default: break;