ASoC: SOF: sof-audio: Modify logic for enabling/disabling topology cores
authorRanjani Sridharan <ranjani.sridharan@linux.intel.com>
Fri, 24 Nov 2023 13:57:43 +0000 (15:57 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 20 Jan 2024 10:51:40 +0000 (11:51 +0100)
[ Upstream commit 31ed8da1c8e5e504710bb36863700e3389f8fc81 ]

In the current code, we enable a widget core when it is set up and
disable it when it is freed. This is problematic with IPC4 because
widget free is essentially a NOP and all widgets are freed in the
firmware when the pipeline is deleted. This results in a crash during
pipeline deletion when one of it's widgets is scheduled to run on a
secondary core and is powered off when widget is freed. So, change the
logic to enable all cores needed by all the modules in a pipeline when
the pipeline widget is set up and disable them after the pipeline
widget is freed.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Link: https://lore.kernel.org/r/20231124135743.24674-3-peter.ujfalusi@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
sound/soc/sof/sof-audio.c

index 563fe6f..77cc64a 100644 (file)
@@ -46,6 +46,7 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
                                    struct snd_sof_widget *swidget)
 {
        const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
+       struct snd_sof_pipeline *spipe = swidget->spipe;
        struct snd_sof_widget *pipe_widget;
        int err = 0;
        int ret;
@@ -87,15 +88,22 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
        }
 
        /*
-        * disable widget core. continue to route setup status and complete flag
-        * even if this fails and return the appropriate error
+        * decrement ref count for cores associated with all modules in the pipeline and clear
+        * the complete flag
         */
-       ret = snd_sof_dsp_core_put(sdev, swidget->core);
-       if (ret < 0) {
-               dev_err(sdev->dev, "error: failed to disable target core: %d for widget %s\n",
-                       swidget->core, swidget->widget->name);
-               if (!err)
-                       err = ret;
+       if (swidget->id == snd_soc_dapm_scheduler) {
+               int i;
+
+               for_each_set_bit(i, &spipe->core_mask, sdev->num_cores) {
+                       ret = snd_sof_dsp_core_put(sdev, i);
+                       if (ret < 0) {
+                               dev_err(sdev->dev, "failed to disable target core: %d for pipeline %s\n",
+                                       i, swidget->widget->name);
+                               if (!err)
+                                       err = ret;
+                       }
+               }
+               swidget->spipe->complete = 0;
        }
 
        /*
@@ -108,10 +116,6 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
                        err = ret;
        }
 
-       /* clear pipeline complete */
-       if (swidget->id == snd_soc_dapm_scheduler)
-               swidget->spipe->complete = 0;
-
        if (!err)
                dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name);
 
@@ -134,8 +138,10 @@ static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev,
                                     struct snd_sof_widget *swidget)
 {
        const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
+       struct snd_sof_pipeline *spipe = swidget->spipe;
        bool use_count_decremented = false;
        int ret;
+       int i;
 
        /* skip if there is no private data */
        if (!swidget->private)
@@ -166,19 +172,23 @@ static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev,
                        goto use_count_dec;
        }
 
-       /* enable widget core */
-       ret = snd_sof_dsp_core_get(sdev, swidget->core);
-       if (ret < 0) {
-               dev_err(sdev->dev, "error: failed to enable target core for widget %s\n",
-                       swidget->widget->name);
-               goto pipe_widget_free;
+       /* update ref count for cores associated with all modules in the pipeline */
+       if (swidget->id == snd_soc_dapm_scheduler) {
+               for_each_set_bit(i, &spipe->core_mask, sdev->num_cores) {
+                       ret = snd_sof_dsp_core_get(sdev, i);
+                       if (ret < 0) {
+                               dev_err(sdev->dev, "failed to enable target core %d for pipeline %s\n",
+                                       i, swidget->widget->name);
+                               goto pipe_widget_free;
+                       }
+               }
        }
 
        /* setup widget in the DSP */
        if (tplg_ops && tplg_ops->widget_setup) {
                ret = tplg_ops->widget_setup(sdev, swidget);
                if (ret < 0)
-                       goto core_put;
+                       goto pipe_widget_free;
        }
 
        /* send config for DAI components */
@@ -208,15 +218,22 @@ static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev,
        return 0;
 
 widget_free:
-       /* widget use_count and core ref_count will both be decremented by sof_widget_free() */
+       /* widget use_count will be decremented by sof_widget_free() */
        sof_widget_free_unlocked(sdev, swidget);
        use_count_decremented = true;
-core_put:
-       if (!use_count_decremented)
-               snd_sof_dsp_core_put(sdev, swidget->core);
 pipe_widget_free:
-       if (swidget->id != snd_soc_dapm_scheduler)
+       if (swidget->id != snd_soc_dapm_scheduler) {
                sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget);
+       } else {
+               int j;
+
+               /* decrement ref count for all cores that were updated previously */
+               for_each_set_bit(j, &spipe->core_mask, sdev->num_cores) {
+                       if (j >= i)
+                               break;
+                       snd_sof_dsp_core_put(sdev, j);
+               }
+       }
 use_count_dec:
        if (!use_count_decremented)
                swidget->use_count--;