aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/sound/soc/sof/sof-audio.c
diff options
context:
space:
mode:
authorRanjani Sridharan <ranjani.sridharan@linux.intel.com>2021-09-27 15:05:14 +0300
committerMark Brown <broonie@kernel.org>2021-10-01 20:48:28 +0100
commit8b0014169254513bda914ba5d49a09458a919488 (patch)
treeb26ae4c89a7e597fcc9f1d77a872700abe08b4f3 /sound/soc/sof/sof-audio.c
parentASoC: SOF: Don't set up widgets during topology parsing (diff)
downloadwireguard-linux-8b0014169254513bda914ba5d49a09458a919488.tar.xz
wireguard-linux-8b0014169254513bda914ba5d49a09458a919488.zip
ASoC: SOF: Introduce widget use_count
Add a new field, use_count to struct snd_sof_widget to keep track of the usage count for each widget. Since widgets can belong to multiple pipelines, this field will ensure that the widget is setup only when the first pipeline that needs it is started and freed when the last pipeline that needs it is stopped. There is no need to protect the widget use_count access as the core already handles mutual exclusion at the PCM level. Add a new helper sof_widget_free() to handle freeing the SOF widgets and export the sof_widget_setup/free() functions. Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> Reviewed-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com> Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com> Signed-off-by: Daniel Baluta <daniel.baluta@nxp.com> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com> Link: https://lore.kernel.org/r/20210927120517.20505-10-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound/soc/sof/sof-audio.c')
-rw-r--r--sound/soc/sof/sof-audio.c77
1 files changed, 71 insertions, 6 deletions
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index 4bed50847f1d..7a4aaabf091e 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -83,7 +83,53 @@ static int sof_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_wi
return 0;
}
-static int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+{
+ struct sof_ipc_free ipc_free = {
+ .hdr = {
+ .size = sizeof(ipc_free),
+ .cmd = SOF_IPC_GLB_TPLG_MSG,
+ },
+ .id = swidget->comp_id,
+ };
+ struct sof_ipc_reply reply;
+ int ret;
+
+ if (!swidget->private)
+ return 0;
+
+ /* only free when use_count is 0 */
+ if (--swidget->use_count)
+ return 0;
+
+ switch (swidget->id) {
+ case snd_soc_dapm_scheduler:
+ ipc_free.hdr.cmd |= SOF_IPC_TPLG_PIPE_FREE;
+ break;
+ case snd_soc_dapm_buffer:
+ ipc_free.hdr.cmd |= SOF_IPC_TPLG_BUFFER_FREE;
+ break;
+ default:
+ ipc_free.hdr.cmd |= SOF_IPC_TPLG_COMP_FREE;
+ break;
+ }
+
+ ret = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free),
+ &reply, sizeof(reply));
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to free widget %s\n", swidget->widget->name);
+ swidget->use_count++;
+ return ret;
+ }
+
+ swidget->complete = 0;
+ dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name);
+
+ return 0;
+}
+EXPORT_SYMBOL(sof_widget_free);
+
+int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{
struct sof_ipc_pipe_new *pipeline;
struct sof_ipc_comp_reply r;
@@ -97,11 +143,15 @@ static int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swi
if (!swidget->private)
return 0;
+ /* widget already set up */
+ if (++swidget->use_count > 1)
+ return 0;
+
ret = sof_pipeline_core_enable(sdev, swidget);
if (ret < 0) {
dev_err(sdev->dev, "error: failed to enable target core: %d for widget %s\n",
ret, swidget->widget->name);
- return ret;
+ goto use_count_dec;
}
switch (swidget->id) {
@@ -134,7 +184,7 @@ static int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swi
}
if (ret < 0) {
dev_err(sdev->dev, "error: failed to load widget %s\n", swidget->widget->name);
- return ret;
+ goto use_count_dec;
}
/* restore kcontrols for widget */
@@ -147,8 +197,13 @@ static int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swi
dev_dbg(sdev->dev, "widget %s setup complete\n", swidget->widget->name);
+ return 0;
+
+use_count_dec:
+ swidget->use_count--;
return ret;
}
+EXPORT_SYMBOL(sof_widget_setup);
/*
* helper to determine if there are only D0i3 compatible
@@ -258,6 +313,9 @@ int sof_set_up_pipelines(struct device *dev)
/* restore pipeline components */
list_for_each_entry_reverse(swidget, &sdev->widget_list, list) {
+ /* reset widget use_count after resuming */
+ swidget->use_count = 0;
+
ret = sof_widget_setup(sdev, swidget);
if (ret < 0)
return ret;
@@ -325,16 +383,23 @@ int sof_set_up_pipelines(struct device *dev)
return 0;
}
-/* This function doesn't free widgets. It only resets the set up status for all routes */
+/*
+ * This function doesn't free widgets. It only resets the set up status for all routes and
+ * use_count for all widgets.
+ */
void sof_tear_down_pipelines(struct device *dev)
{
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
+ struct snd_sof_widget *swidget;
struct snd_sof_route *sroute;
/*
- * No need to protect sroute->setup as this function is called only during the suspend
- * callback and all streams should be suspended by then
+ * No need to protect swidget->use_count and sroute->setup as this function is called only
+ * during the suspend callback and all streams should be suspended by then
*/
+ list_for_each_entry(swidget, &sdev->widget_list, list)
+ swidget->use_count = 0;
+
list_for_each_entry(sroute, &sdev->route_list, list)
sroute->setup = false;
}