All of lore.kernel.org
 help / color / mirror / Atom feed
* From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001
@ 2023-01-27 12:00 Peter Ujfalusi
  2023-01-27 12:00 ` [PATCH 01/18] ASoC: SOF: ipc4-topology: No need to unbind routes within a pipeline Peter Ujfalusi
                   ` (19 more replies)
  0 siblings, 20 replies; 21+ messages in thread
From: Peter Ujfalusi @ 2023-01-27 12:00 UTC (permalink / raw)
  To: lgirdwood, broonie
  Cc: alsa-devel, kai.vehmanen, pierre-louis.bossart, rander.wang,
	ranjani.sridharan, yung-chuan.liao

Hi,

The following series will enable multi-stream support for playback and capture
streams.
Currently only a single PCM can be connected to a DAI, with the multi-stream
support it is possible to connect multiple PCMs to a single DAI.

To achieve this we need to make sure that DAIs/AIF are only set up once since
other stream could be connected to it later.

We also need to introduce reference or use counting for widgets to make sure
that they are not going to be destroyed while other streams are still using
them.

With the multi-stream support we also need to extend our current locking scheme
which worked well for simple paths.

The first patch was sent to the mailing list, but it is not yet applied and I
have included in this series as it is a direct dependency:
https://lore.kernel.org/alsa-devel/20230117121615.25690-1-peter.ujfalusi@linux.intel.com/

Regards,
Peter
---
Peter Ujfalusi (3):
  ASoC: SOF: Avoid double decrementing use_count in sof_widget_setup on
    error
  ASoC: SOF: Protect swidget->use_count with mutex for kcontrol access
    race
  ASoC: SOF: ipc4-pcm: Do not run the trigger pipelines if no spipe is
    stored

Ranjani Sridharan (15):
  ASoC: SOF: ipc4-topology: No need to unbind routes within a pipeline
  ASoC: soc-pcm: Export widget_in_list()
  ASoC: SOF: sof-audio: Set up/free DAI/AIF widgets only once
  ASoC: SOF: sof-audio: Only process widgets in the connected widget
    list
  ASoC: SOF: pcm: do not free widgets during suspend trigger
  ASoC: SOF: topology: Set IPC-specific trigger order for DAI links
  ASoC: SOF: Introduce PCM setup/free PCM IPC ops
  ASoC: SOF: ipc4-pcm: Define pcm_setup/free ops
  ASoC: SOF: ipc4: Add flag to skip triggering pipelines during FE DAI
    trigger
  ASoC: SOF: sof-audio: Populate the PCM stream pipeline_info
  ASoC: SOF: ipc4-pcm: Use the PCM stream's pipeline_info during trigger
  ASoC: SOF: Introduce struct snd_sof_pipeline
  ASoC: SOF: ipc4-pcm: Rename 'data' variable to trigger_list
  ASoC: SOF: ipc4-pcm: Implement pipeline trigger reference counting
  ASoC: SOF: ipc4-topology: Protect pipeline free with mutex

 include/sound/soc-dpcm.h        |   2 +
 include/sound/sof/ipc4/header.h |   3 +
 sound/soc/soc-pcm.c             |   3 +-
 sound/soc/sof/core.c            |   1 +
 sound/soc/sof/intel/hda-dai.c   |  92 ++-------
 sound/soc/sof/ipc3-control.c    |  46 +++--
 sound/soc/sof/ipc3-topology.c   |  32 ++-
 sound/soc/sof/ipc4-control.c    |  33 ++-
 sound/soc/sof/ipc4-pcm.c        | 343 +++++++++++++++++++++++++-------
 sound/soc/sof/ipc4-priv.h       |   2 +
 sound/soc/sof/ipc4-topology.c   |  48 ++++-
 sound/soc/sof/ipc4-topology.h   |  12 ++
 sound/soc/sof/ipc4.c            |   2 +
 sound/soc/sof/pcm.c             |   5 +-
 sound/soc/sof/sof-audio.c       | 226 ++++++++++++++-------
 sound/soc/sof/sof-audio.h       |  54 ++++-
 sound/soc/sof/sof-priv.h        |   1 +
 sound/soc/sof/topology.c        | 114 +++++++----
 18 files changed, 714 insertions(+), 305 deletions(-)

-- 
2.39.1


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 01/18] ASoC: SOF: ipc4-topology: No need to unbind routes within a pipeline
  2023-01-27 12:00 From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Peter Ujfalusi
@ 2023-01-27 12:00 ` Peter Ujfalusi
  2023-01-27 12:00 ` [PATCH 02/18] ASoC: soc-pcm: Export widget_in_list() Peter Ujfalusi
                   ` (18 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Peter Ujfalusi @ 2023-01-27 12:00 UTC (permalink / raw)
  To: lgirdwood, broonie
  Cc: alsa-devel, kai.vehmanen, pierre-louis.bossart, rander.wang,
	ranjani.sridharan, yung-chuan.liao

From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>

The FW currently ignores unbinding routes if the source and sink widgets
belong to the same pipeline. So no need to send the IPC at all in the
first place.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Rander Wang <rander.wang@intel.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
---
 sound/soc/sof/ipc4-topology.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index ba99114e86a9..ae8ec98bb4eb 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -1805,12 +1805,19 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s
 	struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info;
 	struct sof_ipc4_msg msg = {{ 0 }};
 	u32 header, extension;
-	int ret;
+	int ret = 0;
 
 	dev_dbg(sdev->dev, "unbind modules %s:%d -> %s:%d\n",
 		src_widget->widget->name, sroute->src_queue_id,
 		sink_widget->widget->name, sroute->dst_queue_id);
 
+	/*
+	 * routes belonging to the same pipeline will be disconnected by the FW when the pipeline
+	 * is freed. So avoid sending this IPC which will be ignored by the FW anyway.
+	 */
+	if (src_widget->pipe_widget == sink_widget->pipe_widget)
+		goto out;
+
 	header = src_fw_module->man4_module_entry.id;
 	header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id);
 	header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_UNBIND);
@@ -1829,7 +1836,7 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s
 	if (ret < 0)
 		dev_err(sdev->dev, "failed to unbind modules %s -> %s\n",
 			src_widget->widget->name, sink_widget->widget->name);
-
+out:
 	sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_SINK);
 	sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_SOURCE);
 
-- 
2.39.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 02/18] ASoC: soc-pcm: Export widget_in_list()
  2023-01-27 12:00 From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Peter Ujfalusi
  2023-01-27 12:00 ` [PATCH 01/18] ASoC: SOF: ipc4-topology: No need to unbind routes within a pipeline Peter Ujfalusi
@ 2023-01-27 12:00 ` Peter Ujfalusi
  2023-01-27 12:00 ` [PATCH 03/18] ASoC: SOF: sof-audio: Set up/free DAI/AIF widgets only once Peter Ujfalusi
                   ` (17 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Peter Ujfalusi @ 2023-01-27 12:00 UTC (permalink / raw)
  To: lgirdwood, broonie
  Cc: alsa-devel, kai.vehmanen, pierre-louis.bossart, rander.wang,
	ranjani.sridharan, yung-chuan.liao

From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>

Export the widget_in_list() function to be used by other modules.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Bard Liao <yung-chuan.liao@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>
---
 include/sound/soc-dpcm.h | 2 ++
 sound/soc/soc-pcm.c      | 3 ++-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h
index 2864aed72998..1e7d09556fe3 100644
--- a/include/sound/soc-dpcm.h
+++ b/include/sound/soc-dpcm.h
@@ -162,6 +162,8 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream);
 int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir,
 	int event);
 bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, enum snd_soc_dapm_direction dir);
+int widget_in_list(struct snd_soc_dapm_widget_list *list,
+		   struct snd_soc_dapm_widget *widget);
 
 #define dpcm_be_dai_startup_rollback(fe, stream, last)	\
 						dpcm_be_dai_stop(fe, stream, 0, last)
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 579a44d81d9a..f6caa55ef322 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -1337,7 +1337,7 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
 	return NULL;
 }
 
-static int widget_in_list(struct snd_soc_dapm_widget_list *list,
+int widget_in_list(struct snd_soc_dapm_widget_list *list,
 		struct snd_soc_dapm_widget *widget)
 {
 	struct snd_soc_dapm_widget *w;
@@ -1349,6 +1349,7 @@ static int widget_in_list(struct snd_soc_dapm_widget_list *list,
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(widget_in_list);
 
 bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, enum snd_soc_dapm_direction dir)
 {
-- 
2.39.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 03/18] ASoC: SOF: sof-audio: Set up/free DAI/AIF widgets only once
  2023-01-27 12:00 From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Peter Ujfalusi
  2023-01-27 12:00 ` [PATCH 01/18] ASoC: SOF: ipc4-topology: No need to unbind routes within a pipeline Peter Ujfalusi
  2023-01-27 12:00 ` [PATCH 02/18] ASoC: soc-pcm: Export widget_in_list() Peter Ujfalusi
@ 2023-01-27 12:00 ` Peter Ujfalusi
  2023-01-27 12:00 ` [PATCH 04/18] ASoC: SOF: sof-audio: Only process widgets in the connected widget list Peter Ujfalusi
                   ` (16 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Peter Ujfalusi @ 2023-01-27 12:00 UTC (permalink / raw)
  To: lgirdwood, broonie
  Cc: alsa-devel, kai.vehmanen, pierre-louis.bossart, rander.wang,
	ranjani.sridharan, yung-chuan.liao

From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>

Calling the sof_widget_setup/free() for the DAI/AIF widgets inside the
snd_soc_dapm_widget_for_each_sink_path() loop will end up setting up or
freeing the widget multiple times if there are multiple paths leaving
the widget. Fix this by moving the widget setup/free for the starting
widget in each path outside the loop.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Bard Liao <yung-chuan.liao@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>
---
 sound/soc/sof/sof-audio.c | 48 +++++++++++++++------------------------
 1 file changed, 18 insertions(+), 30 deletions(-)

diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index 2dff3ae25d27..572ac6a0c9ac 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -358,19 +358,16 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap
 	int err;
 	int ret = 0;
 
-	/* free all widgets even in case of error to keep use counts balanced */
+	if (widget->dobj.private) {
+		err = sof_widget_free(sdev, widget->dobj.private);
+		if (err < 0)
+			ret = err;
+	}
+
+	/* free all widgets in the sink paths even in case of error to keep use counts balanced */
 	snd_soc_dapm_widget_for_each_sink_path(widget, p) {
-		if (!p->walking && p->sink->dobj.private && widget->dobj.private) {
+		if (!p->walking) {
 			p->walking = true;
-			if (WIDGET_IS_AIF_OR_DAI(widget->id)) {
-				err = sof_widget_free(sdev, widget->dobj.private);
-				if (err < 0)
-					ret = err;
-			}
-
-			err = sof_widget_free(sdev, p->sink->dobj.private);
-			if (err < 0)
-				ret = err;
 
 			err = sof_free_widgets_in_path(sdev, p->sink, dir);
 			if (err < 0)
@@ -393,32 +390,23 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d
 	struct snd_soc_dapm_path *p;
 	int ret;
 
+	if (widget->dobj.private) {
+		ret = sof_widget_setup(sdev, widget->dobj.private);
+		if (ret < 0)
+			return ret;
+	}
+
 	snd_soc_dapm_widget_for_each_sink_path(widget, p) {
-		if (!p->walking && p->sink->dobj.private && widget->dobj.private) {
+		if (!p->walking) {
 			p->walking = true;
-			if (WIDGET_IS_AIF_OR_DAI(widget->id)) {
-				ret = sof_widget_setup(sdev, widget->dobj.private);
-				if (ret < 0)
-					goto out;
-			}
-
-			ret = sof_widget_setup(sdev, p->sink->dobj.private);
-			if (ret < 0) {
-				if (WIDGET_IS_AIF_OR_DAI(widget->id))
-					sof_widget_free(sdev, widget->dobj.private);
-				goto out;
-			}
 
 			ret = sof_set_up_widgets_in_path(sdev, p->sink, dir);
+			p->walking = false;
 			if (ret < 0) {
-				if (WIDGET_IS_AIF_OR_DAI(widget->id))
+				if (widget->dobj.private)
 					sof_widget_free(sdev, widget->dobj.private);
-				sof_widget_free(sdev, p->sink->dobj.private);
-			}
-out:
-			p->walking = false;
-			if (ret < 0)
 				return ret;
+			}
 		}
 	}
 
-- 
2.39.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 04/18] ASoC: SOF: sof-audio: Only process widgets in the connected widget list
  2023-01-27 12:00 From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Peter Ujfalusi
                   ` (2 preceding siblings ...)
  2023-01-27 12:00 ` [PATCH 03/18] ASoC: SOF: sof-audio: Set up/free DAI/AIF widgets only once Peter Ujfalusi
@ 2023-01-27 12:00 ` Peter Ujfalusi
  2023-01-27 12:00 ` [PATCH 05/18] ASoC: SOF: pcm: do not free widgets during suspend trigger Peter Ujfalusi
                   ` (15 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Peter Ujfalusi @ 2023-01-27 12:00 UTC (permalink / raw)
  To: lgirdwood, broonie
  Cc: alsa-devel, kai.vehmanen, pierre-louis.bossart, rander.wang,
	ranjani.sridharan, yung-chuan.liao

From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>

When walking the list of the widgets from the source to the sink, we
accidentally also end up preparing/setting up the widgets that are not
in the list of connected DAPM widgets associated with the PCM. Avoid
this by checking if a widget is part of the connected DAPM widget list
during widget prepare, unprepare, setup or free.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Bard Liao <yung-chuan.liao@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>
---
 sound/soc/sof/sof-audio.c | 51 +++++++++++++++++++++++++++------------
 1 file changed, 36 insertions(+), 15 deletions(-)

diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index 572ac6a0c9ac..b127b304298c 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -241,24 +241,32 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
 			if (!widget->dobj.private)
 				continue;
 
-			snd_soc_dapm_widget_for_each_sink_path(widget, p)
+			snd_soc_dapm_widget_for_each_sink_path(widget, p) {
+				if (!widget_in_list(list, p->sink))
+					continue;
+
 				if (p->sink->dobj.private) {
 					ret = sof_route_setup(sdev, widget, p->sink);
 					if (ret < 0)
 						return ret;
 				}
+			}
 		}
 	} else {
 		for_each_dapm_widgets(list, i, widget) {
 			if (!widget->dobj.private)
 				continue;
 
-			snd_soc_dapm_widget_for_each_source_path(widget, p)
+			snd_soc_dapm_widget_for_each_source_path(widget, p) {
+				if (!widget_in_list(list, p->source))
+					continue;
+
 				if (p->source->dobj.private) {
 					ret = sof_route_setup(sdev, p->source, widget);
 					if (ret < 0)
 						return ret;
 				}
+			}
 		}
 	}
 
@@ -266,7 +274,8 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
 }
 
 static void
-sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget)
+sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
+			      struct snd_soc_dapm_widget_list *list)
 {
 	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
 	struct snd_sof_widget *swidget = widget->dobj.private;
@@ -287,9 +296,11 @@ sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widg
 sink_unprepare:
 	/* unprepare all widgets in the sink paths */
 	snd_soc_dapm_widget_for_each_sink_path(widget, p) {
+		if (!widget_in_list(list, p->sink))
+			continue;
 		if (!p->walking && p->sink->dobj.private) {
 			p->walking = true;
-			sof_unprepare_widgets_in_path(sdev, p->sink);
+			sof_unprepare_widgets_in_path(sdev, p->sink, list);
 			p->walking = false;
 		}
 	}
@@ -299,7 +310,8 @@ static int
 sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
 			    struct snd_pcm_hw_params *fe_params,
 			    struct snd_sof_platform_stream_params *platform_params,
-			    struct snd_pcm_hw_params *pipeline_params, int dir)
+			    struct snd_pcm_hw_params *pipeline_params, int dir,
+			    struct snd_soc_dapm_widget_list *list)
 {
 	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
 	struct snd_sof_widget *swidget = widget->dobj.private;
@@ -327,10 +339,13 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget
 sink_prepare:
 	/* prepare all widgets in the sink paths */
 	snd_soc_dapm_widget_for_each_sink_path(widget, p) {
+		if (!widget_in_list(list, p->sink))
+			continue;
 		if (!p->walking && p->sink->dobj.private) {
 			p->walking = true;
 			ret = sof_prepare_widgets_in_path(sdev, p->sink,  fe_params,
-							  platform_params, pipeline_params, dir);
+							  platform_params, pipeline_params, dir,
+							  list);
 			p->walking = false;
 			if (ret < 0) {
 				/* unprepare the source widget */
@@ -352,7 +367,7 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget
  * (DAI type for capture, AIF type for playback)
  */
 static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
-				    int dir)
+				    int dir, struct snd_soc_dapm_widget_list *list)
 {
 	struct snd_soc_dapm_path *p;
 	int err;
@@ -367,9 +382,12 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap
 	/* free all widgets in the sink paths even in case of error to keep use counts balanced */
 	snd_soc_dapm_widget_for_each_sink_path(widget, p) {
 		if (!p->walking) {
+			if (!widget_in_list(list, p->sink))
+				continue;
+
 			p->walking = true;
 
-			err = sof_free_widgets_in_path(sdev, p->sink, dir);
+			err = sof_free_widgets_in_path(sdev, p->sink, dir, list);
 			if (err < 0)
 				ret = err;
 			p->walking = false;
@@ -385,7 +403,7 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap
  * The error path in this function ensures that all successfully set up widgets getting freed.
  */
 static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
-				      int dir)
+				      int dir, struct snd_soc_dapm_widget_list *list)
 {
 	struct snd_soc_dapm_path *p;
 	int ret;
@@ -398,9 +416,12 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d
 
 	snd_soc_dapm_widget_for_each_sink_path(widget, p) {
 		if (!p->walking) {
+			if (!widget_in_list(list, p->sink))
+				continue;
+
 			p->walking = true;
 
-			ret = sof_set_up_widgets_in_path(sdev, p->sink, dir);
+			ret = sof_set_up_widgets_in_path(sdev, p->sink, dir, list);
 			p->walking = false;
 			if (ret < 0) {
 				if (widget->dobj.private)
@@ -435,11 +456,11 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_l
 
 		switch (op) {
 		case SOF_WIDGET_SETUP:
-			ret = sof_set_up_widgets_in_path(sdev, widget, dir);
+			ret = sof_set_up_widgets_in_path(sdev, widget, dir, list);
 			str = "set up";
 			break;
 		case SOF_WIDGET_FREE:
-			ret = sof_free_widgets_in_path(sdev, widget, dir);
+			ret = sof_free_widgets_in_path(sdev, widget, dir, list);
 			str = "free";
 			break;
 		case SOF_WIDGET_PREPARE:
@@ -455,12 +476,12 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_l
 			 */
 			memcpy(&pipeline_params, fe_params, sizeof(*fe_params));
 
-			ret = sof_prepare_widgets_in_path(sdev, widget, fe_params,
-							  platform_params, &pipeline_params, dir);
+			ret = sof_prepare_widgets_in_path(sdev, widget, fe_params, platform_params,
+							  &pipeline_params, dir, list);
 			break;
 		}
 		case SOF_WIDGET_UNPREPARE:
-			sof_unprepare_widgets_in_path(sdev, widget);
+			sof_unprepare_widgets_in_path(sdev, widget, list);
 			break;
 		default:
 			dev_err(sdev->dev, "Invalid widget op %d\n", op);
-- 
2.39.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 05/18] ASoC: SOF: pcm: do not free widgets during suspend trigger
  2023-01-27 12:00 From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Peter Ujfalusi
                   ` (3 preceding siblings ...)
  2023-01-27 12:00 ` [PATCH 04/18] ASoC: SOF: sof-audio: Only process widgets in the connected widget list Peter Ujfalusi
@ 2023-01-27 12:00 ` Peter Ujfalusi
  2023-01-27 12:00 ` [PATCH 06/18] ASoC: SOF: topology: Set IPC-specific trigger order for DAI links Peter Ujfalusi
                   ` (14 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Peter Ujfalusi @ 2023-01-27 12:00 UTC (permalink / raw)
  To: lgirdwood, broonie
  Cc: alsa-devel, kai.vehmanen, pierre-louis.bossart, rander.wang,
	ranjani.sridharan, yung-chuan.liao

From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>

IPC3 and IPC4 have different requirements for the order in which the FE
CPU and BE CPU DAI trigger callbacks must be invoked. With a regular PCM
start/stop, pipeline widgets are set up during hw_params and freed
during hw_free.

But when the system is suspended when a PCM is running,
pipeline widgets are freed during the SUSPEND trigger callback for the
FE CPU DAI. In order to avoid freeing the pipeline widgets before the BE
CPU DAI trigger is executed, the trigger order was modified in previous
contributions in the PCM dai_link_fixup callback to make sure that the BE
CPU DAI trigger stop/suspend is always invoked before the FE CPU DAI
trigger. But this contradicts the firmware requirement for IPC4 w.r.t.
ordering of pipeline triggers.

So, remove the freeing of pipeline widgets during FE CPU DAI suspend
trigger and handle it during system suspend when the
tear_down_all_pipelines() IPC op is invoked. This will be followed up
with a patch to fix the trigger order for IPC4.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
---
 sound/soc/sof/ipc3-topology.c |  2 +-
 sound/soc/sof/ipc4-pcm.c      | 12 ------------
 sound/soc/sof/ipc4-topology.c |  2 +-
 sound/soc/sof/pcm.c           |  5 +----
 4 files changed, 3 insertions(+), 18 deletions(-)

diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c
index 989395999d6e..72ac1725af0d 100644
--- a/sound/soc/sof/ipc3-topology.c
+++ b/sound/soc/sof/ipc3-topology.c
@@ -2264,7 +2264,7 @@ static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev)
 		for_each_pcm_streams(dir) {
 			struct snd_pcm_substream *substream = spcm->stream[dir].substream;
 
-			if (!substream || !substream->runtime)
+			if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored)
 				continue;
 
 			if (spcm->stream[dir].list) {
diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index 96941bebc1f1..23de58d7d06b 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -183,7 +183,6 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
 	struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
 	struct sof_ipc4_copier *ipc4_copier;
-	struct snd_soc_dpcm *dpcm;
 
 	if (!dai) {
 		dev_err(component->dev, "%s: No DAI found with name %s\n", __func__,
@@ -205,17 +204,6 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
 	rate->min = ipc4_copier->available_fmt.base_config->audio_fmt.sampling_frequency;
 	rate->max = rate->min;
 
-	/*
-	 * Set trigger order for capture to SND_SOC_DPCM_TRIGGER_PRE. This is required
-	 * to ensure that the BE DAI pipeline gets stopped/suspended before the FE DAI
-	 * pipeline gets triggered and the pipeline widgets are freed.
-	 */
-	for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_CAPTURE, dpcm) {
-		struct snd_soc_pcm_runtime *fe = dpcm->fe;
-
-		fe->dai_link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_PRE;
-	}
-
 	switch (ipc4_copier->dai_type) {
 	case SOF_DAI_INTEL_SSP:
 		ipc4_ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params);
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index ae8ec98bb4eb..3938ff2d998b 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -2025,7 +2025,7 @@ static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif
 		for_each_pcm_streams(dir) {
 			struct snd_pcm_substream *substream = spcm->stream[dir].substream;
 
-			if (!substream || !substream->runtime)
+			if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored)
 				continue;
 
 			if (spcm->stream[dir].list) {
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index 952fc698a586..34d40c5c629a 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -282,7 +282,6 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
 	const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
 	struct snd_sof_pcm *spcm;
 	bool reset_hw_params = false;
-	bool free_widget_list = false;
 	bool ipc_first = false;
 	int ret = 0;
 
@@ -326,7 +325,6 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
 			spcm->stream[substream->stream].suspend_ignored = true;
 			return 0;
 		}
-		free_widget_list = true;
 		fallthrough;
 	case SNDRV_PCM_TRIGGER_STOP:
 		ipc_first = true;
@@ -353,8 +351,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
 
 	/* free PCM if reset_hw_params is set and the STOP IPC is successful */
 	if (!ret && reset_hw_params)
-		ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream,
-					  free_widget_list);
+		ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, false);
 
 	return ret;
 }
-- 
2.39.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 06/18] ASoC: SOF: topology: Set IPC-specific trigger order for DAI links
  2023-01-27 12:00 From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Peter Ujfalusi
                   ` (4 preceding siblings ...)
  2023-01-27 12:00 ` [PATCH 05/18] ASoC: SOF: pcm: do not free widgets during suspend trigger Peter Ujfalusi
@ 2023-01-27 12:00 ` Peter Ujfalusi
  2023-01-27 12:00 ` [PATCH 07/18] ASoC: SOF: Introduce PCM setup/free PCM IPC ops Peter Ujfalusi
                   ` (13 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Peter Ujfalusi @ 2023-01-27 12:00 UTC (permalink / raw)
  To: lgirdwood, broonie
  Cc: alsa-devel, kai.vehmanen, pierre-louis.bossart, rander.wang,
	ranjani.sridharan, yung-chuan.liao

From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>

Add a new topology IPC op to set up DAI links and set the link trigger
order to match the expectation based on the IPC type. Note that the
link_setup op implementations for IPC3 and IPC4 are not identical and
have contrasting trigger orders for playback and capture.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
---
 sound/soc/sof/ipc3-topology.c | 19 +++++++++++++++++++
 sound/soc/sof/ipc4-topology.c | 19 +++++++++++++++++++
 sound/soc/sof/sof-audio.h     |  2 ++
 sound/soc/sof/topology.c      | 25 +++++++------------------
 4 files changed, 47 insertions(+), 18 deletions(-)

diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c
index 72ac1725af0d..3f52dfb19e01 100644
--- a/sound/soc/sof/ipc3-topology.c
+++ b/sound/soc/sof/ipc3-topology.c
@@ -2426,6 +2426,24 @@ static int sof_ipc3_parse_manifest(struct snd_soc_component *scomp, int index,
 	return 0;
 }
 
+static int sof_ipc3_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link)
+{
+	if (link->no_pcm)
+		return 0;
+
+	/*
+	 * set default trigger order for all links. Exceptions to
+	 * the rule will be handled in sof_pcm_dai_link_fixup()
+	 * For playback, the sequence is the following: start FE,
+	 * start BE, stop BE, stop FE; for Capture the sequence is
+	 * inverted start BE, start FE, stop FE, stop BE
+	 */
+	link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = SND_SOC_DPCM_TRIGGER_PRE;
+	link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_POST;
+
+	return 0;
+}
+
 /* token list for each topology object */
 static enum sof_tokens host_token_list[] = {
 	SOF_CORE_TOKENS,
@@ -2537,4 +2555,5 @@ const struct sof_ipc_tplg_ops ipc3_tplg_ops = {
 	.set_up_all_pipelines = sof_ipc3_set_up_all_pipelines,
 	.tear_down_all_pipelines = sof_ipc3_tear_down_all_pipelines,
 	.parse_manifest = sof_ipc3_parse_manifest,
+	.link_setup = sof_ipc3_link_setup,
 };
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index 3938ff2d998b..b07a405516b1 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -2038,6 +2038,24 @@ static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif
 	return 0;
 }
 
+static int sof_ipc4_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link)
+{
+	if (link->no_pcm)
+		return 0;
+
+	/*
+	 * set default trigger order for all links. Exceptions to
+	 * the rule will be handled in sof_pcm_dai_link_fixup()
+	 * For playback, the sequence is the following: start BE,
+	 * start FE, stop FE, stop BE; for Capture the sequence is
+	 * inverted start FE, start BE, stop BE, stop FE
+	 */
+	link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = SND_SOC_DPCM_TRIGGER_POST;
+	link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_PRE;
+
+	return 0;
+}
+
 static enum sof_tokens common_copier_token_list[] = {
 	SOF_COMP_TOKENS,
 	SOF_AUDIO_FMT_NUM_TOKENS,
@@ -2144,4 +2162,5 @@ const struct sof_ipc_tplg_ops ipc4_tplg_ops = {
 	.parse_manifest = sof_ipc4_parse_manifest,
 	.dai_get_clk = sof_ipc4_dai_get_clk,
 	.tear_down_all_pipelines = sof_ipc4_tear_down_all_pipelines,
+	.link_setup = sof_ipc4_link_setup,
 };
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index 8e4abb1f5f73..28062a0c3a43 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -180,6 +180,7 @@ struct sof_ipc_tplg_widget_ops {
  * @set_up_all_pipelines: Function pointer for setting up all topology pipelines
  * @tear_down_all_pipelines: Function pointer for tearing down all topology pipelines
  * @parse_manifest: Function pointer for ipc4 specific parsing of topology manifest
+ * @link_setup: Function pointer for IPC-specific DAI link set up
  *
  * Note: function pointers (ops) are optional
  */
@@ -201,6 +202,7 @@ struct sof_ipc_tplg_ops {
 	int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify);
 	int (*parse_manifest)(struct snd_soc_component *scomp, int index,
 			      struct snd_soc_tplg_manifest *man);
+	int (*link_setup)(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link);
 };
 
 /** struct snd_sof_tuple - Tuple info
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index 560771ba8fb9..f67c39c47930 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -1813,26 +1813,15 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_
 	}
 	link->platforms->name = dev_name(scomp->dev);
 
-	/*
-	 * Set nonatomic property for FE dai links as their trigger action
-	 * involves IPC's.
-	 */
+	if (tplg_ops && tplg_ops->link_setup) {
+		ret = tplg_ops->link_setup(sdev, link);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* Set nonatomic property for FE dai links as their trigger action involves IPC's */
 	if (!link->no_pcm) {
 		link->nonatomic = true;
-
-		/*
-		 * set default trigger order for all links. Exceptions to
-		 * the rule will be handled in sof_pcm_dai_link_fixup()
-		 * For playback, the sequence is the following: start FE,
-		 * start BE, stop BE, stop FE; for Capture the sequence is
-		 * inverted start BE, start FE, stop FE, stop BE
-		 */
-		link->trigger[SNDRV_PCM_STREAM_PLAYBACK] =
-					SND_SOC_DPCM_TRIGGER_PRE;
-		link->trigger[SNDRV_PCM_STREAM_CAPTURE] =
-					SND_SOC_DPCM_TRIGGER_POST;
-
-		/* nothing more to do for FE dai links */
 		return 0;
 	}
 
-- 
2.39.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 07/18] ASoC: SOF: Introduce PCM setup/free PCM IPC ops
  2023-01-27 12:00 From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Peter Ujfalusi
                   ` (5 preceding siblings ...)
  2023-01-27 12:00 ` [PATCH 06/18] ASoC: SOF: topology: Set IPC-specific trigger order for DAI links Peter Ujfalusi
@ 2023-01-27 12:00 ` Peter Ujfalusi
  2023-01-27 12:00 ` [PATCH 08/18] ASoC: SOF: ipc4-pcm: Define pcm_setup/free ops Peter Ujfalusi
                   ` (12 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Peter Ujfalusi @ 2023-01-27 12:00 UTC (permalink / raw)
  To: lgirdwood, broonie
  Cc: alsa-devel, kai.vehmanen, pierre-louis.bossart, rander.wang,
	ranjani.sridharan, yung-chuan.liao

From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>

These will be used to perform IPC-specific PCM setup/free.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Libin Yang <libin.yang@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>
---
 sound/soc/sof/sof-audio.h |  7 +++++++
 sound/soc/sof/topology.c  | 14 ++++++++++++++
 2 files changed, 21 insertions(+)

diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index 28062a0c3a43..bcde2ebaf022 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -85,6 +85,7 @@ struct snd_sof_widget;
 struct snd_sof_route;
 struct snd_sof_control;
 struct snd_sof_dai;
+struct snd_sof_pcm;
 
 struct snd_sof_dai_config_data {
 	int dai_index;
@@ -97,6 +98,10 @@ struct snd_sof_dai_config_data {
  * @hw_free: Function pointer for hw_free
  * @trigger: Function pointer for trigger
  * @dai_link_fixup: Function pointer for DAI link fixup
+ * @pcm_setup: Function pointer for IPC-specific PCM set up that can be used for allocating
+ *	       additional memory in the SOF PCM stream structure
+ * @pcm_free: Function pointer for PCM free that can be used for freeing any
+ *	       additional memory in the SOF PCM stream structure
  */
 struct sof_ipc_pcm_ops {
 	int (*hw_params)(struct snd_soc_component *component, struct snd_pcm_substream *substream,
@@ -106,6 +111,8 @@ struct sof_ipc_pcm_ops {
 	int (*trigger)(struct snd_soc_component *component,  struct snd_pcm_substream *substream,
 		       int cmd);
 	int (*dai_link_fixup)(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params);
+	int (*pcm_setup)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm);
+	void (*pcm_free)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm);
 };
 
 /**
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index f67c39c47930..51f6fed45ae7 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -1669,6 +1669,7 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index,
 			struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai)
 {
 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+	const struct sof_ipc_pcm_ops *ipc_pcm_ops = sof_ipc_get_ops(sdev, pcm);
 	struct snd_soc_tplg_stream_caps *caps;
 	struct snd_soc_tplg_private *private = &pcm->priv;
 	struct snd_sof_pcm *spcm;
@@ -1696,6 +1697,13 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index,
 	spcm->pcm = *pcm;
 	dev_dbg(scomp->dev, "tplg: load pcm %s\n", pcm->dai_name);
 
+	/* perform pcm set op */
+	if (ipc_pcm_ops && ipc_pcm_ops->pcm_setup) {
+		ret = ipc_pcm_ops->pcm_setup(sdev, spcm);
+		if (ret < 0)
+			return ret;
+	}
+
 	dai_drv->dobj.private = spcm;
 	list_add(&spcm->list, &sdev->pcm_list);
 
@@ -1773,6 +1781,8 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index,
 static int sof_dai_unload(struct snd_soc_component *scomp,
 			  struct snd_soc_dobj *dobj)
 {
+	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+	const struct sof_ipc_pcm_ops *ipc_pcm_ops = sof_ipc_get_ops(sdev, pcm);
 	struct snd_sof_pcm *spcm = dobj->private;
 
 	/* free PCM DMA pages */
@@ -1782,6 +1792,10 @@ static int sof_dai_unload(struct snd_soc_component *scomp,
 	if (spcm->pcm.capture)
 		snd_dma_free_pages(&spcm->stream[SNDRV_PCM_STREAM_CAPTURE].page_table);
 
+	/* perform pcm free op */
+	if (ipc_pcm_ops && ipc_pcm_ops->pcm_free)
+		ipc_pcm_ops->pcm_free(sdev, spcm);
+
 	/* remove from list and free spcm */
 	list_del(&spcm->list);
 	kfree(spcm);
-- 
2.39.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 08/18] ASoC: SOF: ipc4-pcm: Define pcm_setup/free ops
  2023-01-27 12:00 From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Peter Ujfalusi
                   ` (6 preceding siblings ...)
  2023-01-27 12:00 ` [PATCH 07/18] ASoC: SOF: Introduce PCM setup/free PCM IPC ops Peter Ujfalusi
@ 2023-01-27 12:00 ` Peter Ujfalusi
  2023-01-27 12:00 ` [PATCH 09/18] ASoC: SOF: ipc4: Add flag to skip triggering pipelines during FE DAI trigger Peter Ujfalusi
                   ` (11 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Peter Ujfalusi @ 2023-01-27 12:00 UTC (permalink / raw)
  To: lgirdwood, broonie
  Cc: alsa-devel, kai.vehmanen, pierre-louis.bossart, rander.wang,
	ranjani.sridharan, yung-chuan.liao

From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>

Define the pcm_setup/pcm_free ops for IPC4. Define a new struct
snd_sof_pcm_stream_trigger_info and add a new field trigger_info of this
type to struct snd_sof_pcm_stream. This will be used to save the list of
pipelines that need to be triggered.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Libin Yang <libin.yang@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>
---
 sound/soc/sof/ipc4-pcm.c  | 36 ++++++++++++++++++++++++++++++++++++
 sound/soc/sof/sof-audio.h | 11 +++++++++++
 2 files changed, 47 insertions(+)

diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index 23de58d7d06b..05515e8e6f57 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -215,8 +215,44 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
 	return 0;
 }
 
+static void sof_ipc4_pcm_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm)
+{
+	struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
+	int stream;
+
+	for_each_pcm_streams(stream) {
+		pipeline_list = &spcm->stream[stream].pipeline_list;
+		kfree(pipeline_list->pipe_widgets);
+		pipeline_list->pipe_widgets = NULL;
+	}
+}
+
+static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm)
+{
+	struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
+	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+	int stream;
+
+	for_each_pcm_streams(stream) {
+		pipeline_list = &spcm->stream[stream].pipeline_list;
+
+		/* allocate memory for max number of pipeline IDs */
+		pipeline_list->pipe_widgets = kcalloc(ipc4_data->max_num_pipelines,
+						      sizeof(struct snd_sof_widget *),
+						      GFP_KERNEL);
+		if (!pipeline_list->pipe_widgets) {
+			sof_ipc4_pcm_free(sdev, spcm);
+			return -ENOMEM;
+		}
+	}
+
+	return 0;
+}
+
 const struct sof_ipc_pcm_ops ipc4_pcm_ops = {
 	.trigger = sof_ipc4_pcm_trigger,
 	.hw_free = sof_ipc4_pcm_hw_free,
 	.dai_link_fixup = sof_ipc4_pcm_dai_link_fixup,
+	.pcm_setup = sof_ipc4_pcm_setup,
+	.pcm_free = sof_ipc4_pcm_free,
 };
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index bcde2ebaf022..bb5c61dd9b1e 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -285,6 +285,16 @@ struct sof_token_info {
 	int count;
 };
 
+/**
+ * struct snd_sof_pcm_stream_pipeline_list - List of pipelines associated with a PCM stream
+ * @count: number of pipeline widgets in the @pipe_widgets array
+ * @pipe_widgets: array of pipeline widgets
+ */
+struct snd_sof_pcm_stream_pipeline_list {
+	u32 count;
+	struct snd_sof_widget **pipe_widgets;
+};
+
 /* PCM stream, mapped to FW component  */
 struct snd_sof_pcm_stream {
 	u32 comp_id;
@@ -300,6 +310,7 @@ struct snd_sof_pcm_stream {
 	 * active or not while suspending the stream
 	 */
 	bool suspend_ignored;
+	struct snd_sof_pcm_stream_pipeline_list pipeline_list;
 };
 
 /* ALSA SOF PCM device */
-- 
2.39.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 09/18] ASoC: SOF: ipc4: Add flag to skip triggering pipelines during FE DAI trigger
  2023-01-27 12:00 From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Peter Ujfalusi
                   ` (7 preceding siblings ...)
  2023-01-27 12:00 ` [PATCH 08/18] ASoC: SOF: ipc4-pcm: Define pcm_setup/free ops Peter Ujfalusi
@ 2023-01-27 12:00 ` Peter Ujfalusi
  2023-01-27 12:00 ` [PATCH 10/18] ASoC: SOF: sof-audio: Populate the PCM stream pipeline_info Peter Ujfalusi
                   ` (10 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Peter Ujfalusi @ 2023-01-27 12:00 UTC (permalink / raw)
  To: lgirdwood, broonie
  Cc: alsa-devel, kai.vehmanen, pierre-louis.bossart, rander.wang,
	ranjani.sridharan, yung-chuan.liao

From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>

Add a new flag, skip_during_fe_trigger, to struct sof_ipc4_pipeline to
skip triggering pipelines in the FE DAI trigger. Set this flag for the
HDA DAI BE pipelines so that their BE pipeline will not be triggered in
the FE DAI trigger. Also, move the trigger handling for all commands
include START/PAUSE_RELEASE for the HDA DAI's to the backend DAI trigger
ops.

For the SSP/DMIC/SDW cases, remove the BE DAI trigger as they involve no
DMA operations and can be triggered in the FE DAI trigger. This is in
preparation to perform batch triggering of all pipelines for the non-HDA
case.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Libin Yang <libin.yang@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>
---
 sound/soc/sof/intel/hda-dai.c | 92 +++++++----------------------------
 sound/soc/sof/ipc4-pcm.c      | 17 +------
 sound/soc/sof/ipc4-topology.c |  1 +
 sound/soc/sof/ipc4-topology.h |  2 +
 4 files changed, 21 insertions(+), 91 deletions(-)

diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index 1c3d4887aa30..98eebb4b07e6 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -450,6 +450,8 @@ static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream,
 {
 	struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(dai, substream);
 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
+	struct snd_sof_widget *pipe_widget;
+	struct sof_ipc4_pipeline *pipeline;
 	struct snd_soc_pcm_runtime *rtd;
 	struct snd_sof_widget *swidget;
 	struct snd_soc_dapm_widget *w;
@@ -466,18 +468,30 @@ static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream,
 
 	w = snd_soc_dai_get_widget(dai, substream->stream);
 	swidget = w->dobj.private;
+	pipe_widget = swidget->pipe_widget;
+	pipeline = pipe_widget->private;
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 		snd_hdac_ext_stream_start(hext_stream);
+		if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
+			ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
+							  SOF_IPC4_PIPE_PAUSED);
+			if (ret < 0)
+				return ret;
+			pipeline->state = SOF_IPC4_PIPE_PAUSED;
+		}
+
+		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
+						  SOF_IPC4_PIPE_RUNNING);
+		if (ret < 0)
+			return ret;
+		pipeline->state = SOF_IPC4_PIPE_RUNNING;
 		break;
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_STOP:
 	{
-		struct snd_sof_widget *pipe_widget = swidget->pipe_widget;
-		struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
-
 		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
 						  SOF_IPC4_PIPE_PAUSED);
 		if (ret < 0)
@@ -503,9 +517,6 @@ static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream,
 	}
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 	{
-		struct snd_sof_widget *pipe_widget = swidget->pipe_widget;
-		struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
-
 		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
 						  SOF_IPC4_PIPE_PAUSED);
 		if (ret < 0)
@@ -703,64 +714,6 @@ static const struct snd_soc_dai_ops ipc3_ssp_dai_ops = {
 	.shutdown = ssp_dai_shutdown,
 };
 
-static int ipc4_be_dai_common_trigger(struct snd_soc_dai *dai, int cmd, int stream)
-{
-	struct snd_sof_widget *pipe_widget;
-	struct sof_ipc4_pipeline *pipeline;
-	struct snd_sof_widget *swidget;
-	struct snd_soc_dapm_widget *w;
-	struct snd_sof_dev *sdev;
-	int ret;
-
-	w = snd_soc_dai_get_widget(dai, stream);
-	swidget = w->dobj.private;
-	pipe_widget = swidget->pipe_widget;
-	pipeline = pipe_widget->private;
-	sdev = snd_soc_component_get_drvdata(swidget->scomp);
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-	case SNDRV_PCM_TRIGGER_STOP:
-		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
-						  SOF_IPC4_PIPE_PAUSED);
-		if (ret < 0)
-			return ret;
-		pipeline->state = SOF_IPC4_PIPE_PAUSED;
-
-		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
-						  SOF_IPC4_PIPE_RESET);
-		if (ret < 0)
-			return ret;
-		pipeline->state = SOF_IPC4_PIPE_RESET;
-		break;
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
-						  SOF_IPC4_PIPE_PAUSED);
-		if (ret < 0)
-			return ret;
-		pipeline->state = SOF_IPC4_PIPE_PAUSED;
-		break;
-	default:
-		break;
-	}
-
-	return 0;
-}
-
-static int ipc4_be_dai_trigger(struct snd_pcm_substream *substream,
-			       int cmd, struct snd_soc_dai *dai)
-{
-	return ipc4_be_dai_common_trigger(dai, cmd, substream->stream);
-}
-
-static const struct snd_soc_dai_ops ipc4_dmic_dai_ops = {
-	.trigger = ipc4_be_dai_trigger,
-};
-
-static const struct snd_soc_dai_ops ipc4_ssp_dai_ops = {
-	.trigger = ipc4_be_dai_trigger,
-};
-
 void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
 {
 	int i;
@@ -785,14 +738,6 @@ void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
 		struct sof_ipc4_fw_data *ipc4_data = sdev->private;
 
 		for (i = 0; i < ops->num_drv; i++) {
-			if (strstr(ops->drv[i].name, "DMIC")) {
-				ops->drv[i].ops = &ipc4_dmic_dai_ops;
-				continue;
-			}
-			if (strstr(ops->drv[i].name, "SSP")) {
-				ops->drv[i].ops = &ipc4_ssp_dai_ops;
-				continue;
-			}
 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
 			if (strstr(ops->drv[i].name, "iDisp") ||
 			    strstr(ops->drv[i].name, "Analog") ||
@@ -804,9 +749,6 @@ void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
 		if (!hda_use_tplg_nhlt)
 			ipc4_data->nhlt = intel_nhlt_init(sdev->dev);
 
-		if (IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE))
-			sdw_callback.trigger = ipc4_be_dai_common_trigger;
-
 		break;
 	}
 	default:
diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index 05515e8e6f57..db9d0adb2717 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -58,25 +58,10 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 		if (!swidget)
 			continue;
 
-		/*
-		 * set pipeline state for both FE and BE pipelines for RUNNING state.
-		 * For PAUSE/RESET, set the pipeline state only for the FE pipeline.
-		 */
-		switch (state) {
-		case SOF_IPC4_PIPE_PAUSED:
-		case SOF_IPC4_PIPE_RESET:
-			if (!WIDGET_IS_AIF(swidget->id))
-				continue;
-			break;
-		default:
-			break;
-		}
-
-		/* find pipeline widget for the pipeline that this widget belongs to */
 		pipeline_widget = swidget->pipe_widget;
 		pipeline = (struct sof_ipc4_pipeline *)pipeline_widget->private;
 
-		if (pipeline->state == state)
+		if (pipeline->state == state || pipeline->skip_during_fe_trigger)
 			continue;
 
 		/* first set the pipeline to PAUSED state */
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index b07a405516b1..f3b1a7f81216 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -1869,6 +1869,7 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
 	case SOF_DAI_INTEL_HDA:
 		gtw_attr = ipc4_copier->gtw_attr;
 		gtw_attr->lp_buffer_alloc = pipeline->lp_mode;
+		pipeline->skip_during_fe_trigger = true;
 		fallthrough;
 	case SOF_DAI_INTEL_ALH:
 		copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h
index 8dbbf69b0eb7..028b5d91b9db 100644
--- a/sound/soc/sof/ipc4-topology.h
+++ b/sound/soc/sof/ipc4-topology.h
@@ -73,6 +73,7 @@
  * @mem_usage: Memory usage
  * @state: Pipeline state
  * @msg: message structure for pipeline
+ * @skip_during_fe_trigger: skip triggering this pipeline during the FE DAI trigger
  */
 struct sof_ipc4_pipeline {
 	uint32_t priority;
@@ -80,6 +81,7 @@ struct sof_ipc4_pipeline {
 	uint32_t mem_usage;
 	int state;
 	struct sof_ipc4_msg msg;
+	bool skip_during_fe_trigger;
 };
 
 /**
-- 
2.39.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 10/18] ASoC: SOF: sof-audio: Populate the PCM stream pipeline_info
  2023-01-27 12:00 From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Peter Ujfalusi
                   ` (8 preceding siblings ...)
  2023-01-27 12:00 ` [PATCH 09/18] ASoC: SOF: ipc4: Add flag to skip triggering pipelines during FE DAI trigger Peter Ujfalusi
@ 2023-01-27 12:00 ` Peter Ujfalusi
  2023-01-27 12:00 ` [PATCH 11/18] ASoC: SOF: ipc4-pcm: Use the PCM stream's pipeline_info during trigger Peter Ujfalusi
                   ` (9 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Peter Ujfalusi @ 2023-01-27 12:00 UTC (permalink / raw)
  To: lgirdwood, broonie
  Cc: alsa-devel, kai.vehmanen, pierre-louis.bossart, rander.wang,
	ranjani.sridharan, yung-chuan.liao

From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>

Populate the pipeline_info for the PCM stream with the list of pipeline
widgets that need to be handled during the PCM trigger. This will be
used in the IPC-specific PCM trigger op to trigger the pipelines.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Libin Yang <libin.yang@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>
---
 sound/soc/sof/sof-audio.c | 69 +++++++++++++++++++++++++++++----------
 1 file changed, 52 insertions(+), 17 deletions(-)

diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index b127b304298c..e6796c59e04b 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -367,8 +367,9 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget
  * (DAI type for capture, AIF type for playback)
  */
 static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
-				    int dir, struct snd_soc_dapm_widget_list *list)
+				    int dir, struct snd_sof_pcm *spcm)
 {
+	struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
 	struct snd_soc_dapm_path *p;
 	int err;
 	int ret = 0;
@@ -387,7 +388,7 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap
 
 			p->walking = true;
 
-			err = sof_free_widgets_in_path(sdev, p->sink, dir, list);
+			err = sof_free_widgets_in_path(sdev, p->sink, dir, spcm);
 			if (err < 0)
 				ret = err;
 			p->walking = false;
@@ -403,17 +404,44 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap
  * The error path in this function ensures that all successfully set up widgets getting freed.
  */
 static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
-				      int dir, struct snd_soc_dapm_widget_list *list)
+				      int dir, struct snd_sof_pcm *spcm)
 {
+	struct snd_sof_pcm_stream_pipeline_list *pipeline_list = &spcm->stream[dir].pipeline_list;
+	struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
+	struct snd_sof_widget *swidget = widget->dobj.private;
+	struct snd_sof_widget *pipe_widget;
 	struct snd_soc_dapm_path *p;
 	int ret;
 
-	if (widget->dobj.private) {
+	if (swidget) {
+		int i;
+
 		ret = sof_widget_setup(sdev, widget->dobj.private);
 		if (ret < 0)
 			return ret;
+
+		/* skip populating the pipe_widgets array if it is NULL */
+		if (!pipeline_list->pipe_widgets)
+			goto sink_setup;
+
+		/*
+		 * Add the widget's pipe_widget to the list of pipelines to be triggered if not
+		 * already in the list. This will result in the pipelines getting added in the
+		 * order source to sink.
+		 */
+		for (i = 0; i < pipeline_list->count; i++) {
+			pipe_widget = pipeline_list->pipe_widgets[i];
+			if (pipe_widget == swidget->pipe_widget)
+				break;
+		}
+
+		if (i == pipeline_list->count) {
+			pipeline_list->count++;
+			pipeline_list->pipe_widgets[i] = swidget->pipe_widget;
+		}
 	}
 
+sink_setup:
 	snd_soc_dapm_widget_for_each_sink_path(widget, p) {
 		if (!p->walking) {
 			if (!widget_in_list(list, p->sink))
@@ -421,11 +449,11 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d
 
 			p->walking = true;
 
-			ret = sof_set_up_widgets_in_path(sdev, p->sink, dir, list);
+			ret = sof_set_up_widgets_in_path(sdev, p->sink, dir, spcm);
 			p->walking = false;
 			if (ret < 0) {
-				if (widget->dobj.private)
-					sof_widget_free(sdev, widget->dobj.private);
+				if (swidget)
+					sof_widget_free(sdev, swidget);
 				return ret;
 			}
 		}
@@ -435,16 +463,20 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d
 }
 
 static int
-sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_list *list,
+sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
 			  struct snd_pcm_hw_params *fe_params,
 			  struct snd_sof_platform_stream_params *platform_params, int dir,
 			  enum sof_widget_op op)
 {
+	struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
 	struct snd_soc_dapm_widget *widget;
 	char *str;
 	int ret = 0;
 	int i;
 
+	if (!list)
+		return 0;
+
 	for_each_dapm_widgets(list, i, widget) {
 		/* starting widget for playback is AIF type */
 		if (dir == SNDRV_PCM_STREAM_PLAYBACK && widget->id != snd_soc_dapm_aif_in)
@@ -456,11 +488,11 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_l
 
 		switch (op) {
 		case SOF_WIDGET_SETUP:
-			ret = sof_set_up_widgets_in_path(sdev, widget, dir, list);
+			ret = sof_set_up_widgets_in_path(sdev, widget, dir, spcm);
 			str = "set up";
 			break;
 		case SOF_WIDGET_FREE:
-			ret = sof_free_widgets_in_path(sdev, widget, dir, list);
+			ret = sof_free_widgets_in_path(sdev, widget, dir, spcm);
 			str = "free";
 			break;
 		case SOF_WIDGET_PREPARE:
@@ -514,16 +546,16 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
 	 * Prepare widgets for set up. The prepare step is used to allocate memory, assign
 	 * instance ID and pick the widget configuration based on the runtime PCM params.
 	 */
-	ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
+	ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
 					dir, SOF_WIDGET_PREPARE);
 	if (ret < 0)
 		return ret;
 
 	/* Set up is used to send the IPC to the DSP to create the widget */
-	ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
+	ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
 					dir, SOF_WIDGET_SETUP);
 	if (ret < 0) {
-		ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
+		ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
 						dir, SOF_WIDGET_UNPREPARE);
 		return ret;
 	}
@@ -567,15 +599,16 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
 	return 0;
 
 widget_free:
-	sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, dir,
+	sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, dir,
 				  SOF_WIDGET_FREE);
-	sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
+	sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
 
 	return ret;
 }
 
 int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
 {
+	struct snd_sof_pcm_stream_pipeline_list *pipeline_list = &spcm->stream[dir].pipeline_list;
 	struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
 	int ret;
 
@@ -584,14 +617,16 @@ int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int
 		return 0;
 
 	/* send IPC to free widget in the DSP */
-	ret = sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_FREE);
+	ret = sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_FREE);
 
 	/* unprepare the widget */
-	sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
+	sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
 
 	snd_soc_dapm_dai_free_widgets(&list);
 	spcm->stream[dir].list = NULL;
 
+	pipeline_list->count = 0;
+
 	return ret;
 }
 
-- 
2.39.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 11/18] ASoC: SOF: ipc4-pcm: Use the PCM stream's pipeline_info during trigger
  2023-01-27 12:00 From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Peter Ujfalusi
                   ` (9 preceding siblings ...)
  2023-01-27 12:00 ` [PATCH 10/18] ASoC: SOF: sof-audio: Populate the PCM stream pipeline_info Peter Ujfalusi
@ 2023-01-27 12:00 ` Peter Ujfalusi
  2023-01-27 12:00 ` [PATCH 12/18] ASoC: SOF: Introduce struct snd_sof_pipeline Peter Ujfalusi
                   ` (8 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Peter Ujfalusi @ 2023-01-27 12:00 UTC (permalink / raw)
  To: lgirdwood, broonie
  Cc: alsa-devel, kai.vehmanen, pierre-louis.bossart, rander.wang,
	ranjani.sridharan, yung-chuan.liao

From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>

Use the list of pipelines in the PCM stream's pipeline info to trigger
the pipelines in the right order. Add a helper for triggering pipelines
in batch mode that will be used to trigger multiple pipelines at the
same time.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Libin Yang <libin.yang@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>
---
 include/sound/sof/ipc4/header.h |   3 +
 sound/soc/sof/ipc4-pcm.c        | 137 ++++++++++++++++++++++++--------
 sound/soc/sof/ipc4-topology.h   |  10 +++
 3 files changed, 115 insertions(+), 35 deletions(-)

diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h
index 622193be7ac4..d31349bf011d 100644
--- a/include/sound/sof/ipc4/header.h
+++ b/include/sound/sof/ipc4/header.h
@@ -185,6 +185,9 @@ enum sof_ipc4_pipeline_state {
 #define SOF_IPC4_GLB_PIPE_STATE_MASK		GENMASK(15, 0)
 #define SOF_IPC4_GLB_PIPE_STATE(x)		((x) << SOF_IPC4_GLB_PIPE_STATE_SHIFT)
 
+/* pipeline set state IPC msg extension */
+#define SOF_IPC4_GLB_PIPE_STATE_EXT_MULTI	BIT(0)
+
 /* load library ipc msg */
 #define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT	16
 #define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID(x)	((x) << SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT)
diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index db9d0adb2717..a5482185cd6c 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -13,6 +13,33 @@
 #include "ipc4-priv.h"
 #include "ipc4-topology.h"
 
+static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state,
+					     struct ipc4_pipeline_set_state_data *data)
+{
+	struct sof_ipc4_msg msg = {{ 0 }};
+	u32 primary, ipc_size;
+
+	/* trigger a single pipeline */
+	if (data->count == 1)
+		return sof_ipc4_set_pipeline_state(sdev, data->pipeline_ids[0], state);
+
+	primary = state;
+	primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_SET_PIPELINE_STATE);
+	primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+	primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
+	msg.primary = primary;
+
+	/* trigger multiple pipelines with a single IPC */
+	msg.extension = SOF_IPC4_GLB_PIPE_STATE_EXT_MULTI;
+
+	/* ipc_size includes the count and the pipeline IDs for the number of pipelines */
+	ipc_size = sizeof(u32) * (data->count + 1);
+	msg.data_size = ipc_size;
+	msg.data_ptr = data;
+
+	return sof_ipc_tx_message(sdev->ipc, &msg, ipc_size, NULL, 0);
+}
+
 int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state)
 {
 	struct sof_ipc4_msg msg = {{ 0 }};
@@ -37,60 +64,100 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 {
 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
-	struct snd_sof_widget *pipeline_widget;
-	struct snd_soc_dapm_widget_list *list;
-	struct snd_soc_dapm_widget *widget;
+	struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
+	struct ipc4_pipeline_set_state_data *data;
+	struct snd_sof_widget *pipe_widget;
 	struct sof_ipc4_pipeline *pipeline;
-	struct snd_sof_widget *swidget;
 	struct snd_sof_pcm *spcm;
-	int ret = 0;
-	int num_widgets;
+	int ret;
+	int i, j;
 
 	spcm = snd_sof_find_spcm_dai(component, rtd);
 	if (!spcm)
 		return -EINVAL;
 
-	list = spcm->stream[substream->stream].list;
-
-	for_each_dapm_widgets(list, num_widgets, widget) {
-		swidget = widget->dobj.private;
+	pipeline_list = &spcm->stream[substream->stream].pipeline_list;
+
+	/* nothing to trigger if the list is empty */
+	if (!pipeline_list->pipe_widgets)
+		return 0;
+
+	/* allocate memory for the pipeline data */
+	data = kzalloc(struct_size(data, pipeline_ids, pipeline_list->count), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	/*
+	 * IPC4 requires pipelines to be triggered in order starting at the sink and
+	 * walking all the way to the source. So traverse the pipeline_list in the reverse order.
+	 * Skip the pipelines that have their skip_during_fe_trigger flag set or if they're already
+	 * in the requested state. If there is a fork in the pipeline, the order of triggering
+	 * between the left/right paths will be indeterministic. But the sink->source trigger order
+	 * sink->source would still be guaranteed for each fork independently.
+	 */
+	for (i = pipeline_list->count - 1; i >= 0; i--) {
+		pipe_widget = pipeline_list->pipe_widgets[i];
+		pipeline = pipe_widget->private;
+		if (pipeline->state != state && !pipeline->skip_during_fe_trigger)
+			data->pipeline_ids[data->count++] = pipe_widget->instance_id;
+	}
 
-		if (!swidget)
-			continue;
+	/* return if all pipelines are in the requested state already */
+	if (!data->count) {
+		kfree(data);
+		return 0;
+	}
 
-		pipeline_widget = swidget->pipe_widget;
-		pipeline = (struct sof_ipc4_pipeline *)pipeline_widget->private;
+	/*
+	 * Pause all pipelines. This could result in an extra IPC to pause all pipelines even if
+	 * they are already paused. But it helps keep the logic simpler and the firmware handles
+	 * the repeated pause gracefully. This can be optimized in the future if needed.
+	 */
+	ret = sof_ipc4_set_multi_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, data);
+	if (ret < 0) {
+		dev_err(sdev->dev, "failed to pause all pipelines\n");
+		goto free;
+	}
 
-		if (pipeline->state == state || pipeline->skip_during_fe_trigger)
-			continue;
+	/* update PAUSED state for all pipelines that were just triggered */
+	for (i = 0; i < data->count; i++) {
+		for (j = 0; j < pipeline_list->count; j++) {
+			pipe_widget = pipeline_list->pipe_widgets[j];
+			pipeline = pipe_widget->private;
 
-		/* first set the pipeline to PAUSED state */
-		if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
-			ret = sof_ipc4_set_pipeline_state(sdev, pipeline_widget->instance_id,
-							  SOF_IPC4_PIPE_PAUSED);
-			if (ret < 0) {
-				dev_err(sdev->dev, "failed to pause pipeline %d\n",
-					swidget->pipeline_id);
-				return ret;
+			if (data->pipeline_ids[i] == pipe_widget->instance_id) {
+				pipeline->state = SOF_IPC4_PIPE_PAUSED;
+				break;
 			}
 		}
+	}
 
-		pipeline->state = SOF_IPC4_PIPE_PAUSED;
+	/* return if this is the final state */
+	if (state == SOF_IPC4_PIPE_PAUSED)
+		goto free;
 
-		if (pipeline->state == state)
-			continue;
+	/* else set the final state in the DSP */
+	ret = sof_ipc4_set_multi_pipeline_state(sdev, state, data);
+	if (ret < 0) {
+		dev_err(sdev->dev, "failed to set final state %d for all pipelines\n", state);
+		goto free;
+	}
 
-		/* then set the final state */
-		ret = sof_ipc4_set_pipeline_state(sdev, pipeline_widget->instance_id, state);
-		if (ret < 0) {
-			dev_err(sdev->dev, "failed to set state %d for pipeline %d\n",
-				state, swidget->pipeline_id);
-			break;
-		}
+	/* update final state for all pipelines that were just triggered */
+	for (i = 0; i < data->count; i++) {
+		for (j = 0; j < pipeline_list->count; j++) {
+			pipe_widget = pipeline_list->pipe_widgets[j];
+			pipeline = pipe_widget->private;
 
-		pipeline->state = state;
+			if (data->pipeline_ids[i] == pipe_widget->instance_id) {
+				pipeline->state = state;
+				break;
+			}
+		}
 	}
 
+free:
+	kfree(data);
 	return ret;
 }
 
diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h
index 028b5d91b9db..ee5d31e68a77 100644
--- a/sound/soc/sof/ipc4-topology.h
+++ b/sound/soc/sof/ipc4-topology.h
@@ -84,6 +84,16 @@ struct sof_ipc4_pipeline {
 	bool skip_during_fe_trigger;
 };
 
+/**
+ * struct sof_ipc4_multi_pipeline_data - multi pipeline trigger IPC data
+ * @count: Number of pipelines to be triggered
+ * @pipeline_ids: Flexible array of IDs of the pipelines to be triggered
+ */
+struct ipc4_pipeline_set_state_data {
+	u32 count;
+	DECLARE_FLEX_ARRAY(u32, pipeline_ids);
+} __packed;
+
 /**
  * struct sof_ipc4_available_audio_format - Available audio formats
  * @base_config: Available base config
-- 
2.39.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 12/18] ASoC: SOF: Introduce struct snd_sof_pipeline
  2023-01-27 12:00 From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Peter Ujfalusi
                   ` (10 preceding siblings ...)
  2023-01-27 12:00 ` [PATCH 11/18] ASoC: SOF: ipc4-pcm: Use the PCM stream's pipeline_info during trigger Peter Ujfalusi
@ 2023-01-27 12:00 ` Peter Ujfalusi
  2023-01-27 12:00 ` [PATCH 13/18] ASoC: SOF: ipc4-pcm: Rename 'data' variable to trigger_list Peter Ujfalusi
                   ` (7 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Peter Ujfalusi @ 2023-01-27 12:00 UTC (permalink / raw)
  To: lgirdwood, broonie
  Cc: alsa-devel, kai.vehmanen, pierre-louis.bossart, rander.wang,
	ranjani.sridharan, yung-chuan.liao

From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>

Introduce struct snd_sof_pipeline to save the information about
pipelines including the pipeline widget, their status wrt how many PCM's
are using them and whether they are complete or not.

In struct snd_sof_widget, replace pipe_widget with spipe and remove
complete. In struct snd_sof_pcm_stream_pipeline_list, replace
pipe_widgets with pipelines.

Update all users accordingly.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Rander Wang <rander.wang@intel.com>
Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
---
 sound/soc/sof/core.c          |  1 +
 sound/soc/sof/intel/hda-dai.c |  2 +-
 sound/soc/sof/ipc3-topology.c |  9 +++--
 sound/soc/sof/ipc4-pcm.c      | 23 ++++++-----
 sound/soc/sof/ipc4-topology.c | 12 +++---
 sound/soc/sof/sof-audio.c     | 48 +++++++++++++++--------
 sound/soc/sof/sof-audio.h     | 27 +++++++++----
 sound/soc/sof/sof-priv.h      |  1 +
 sound/soc/sof/topology.c      | 73 +++++++++++++++++++++++------------
 9 files changed, 126 insertions(+), 70 deletions(-)

diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c
index 26a3f7c8c914..7de8673a01ce 100644
--- a/sound/soc/sof/core.c
+++ b/sound/soc/sof/core.c
@@ -390,6 +390,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
 	INIT_LIST_HEAD(&sdev->pcm_list);
 	INIT_LIST_HEAD(&sdev->kcontrol_list);
 	INIT_LIST_HEAD(&sdev->widget_list);
+	INIT_LIST_HEAD(&sdev->pipeline_list);
 	INIT_LIST_HEAD(&sdev->dai_list);
 	INIT_LIST_HEAD(&sdev->dai_link_list);
 	INIT_LIST_HEAD(&sdev->route_list);
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index 98eebb4b07e6..193b3e74820a 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -468,7 +468,7 @@ static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream,
 
 	w = snd_soc_dai_get_widget(dai, substream->stream);
 	swidget = w->dobj.private;
-	pipe_widget = swidget->pipe_widget;
+	pipe_widget = swidget->spipe->pipe_widget;
 	pipeline = pipe_widget->private;
 
 	switch (cmd) {
diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c
index 3f52dfb19e01..88b9b9507231 100644
--- a/sound/soc/sof/ipc3-topology.c
+++ b/sound/soc/sof/ipc3-topology.c
@@ -2233,9 +2233,9 @@ static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify)
 					return ret;
 			}
 
-			swidget->complete = sof_ipc3_complete_pipeline(sdev, swidget);
-			if (swidget->complete < 0)
-				return swidget->complete;
+			swidget->spipe->complete = sof_ipc3_complete_pipeline(sdev, swidget);
+			if (swidget->spipe->complete < 0)
+				return swidget->spipe->complete;
 			break;
 		default:
 			break;
@@ -2317,7 +2317,8 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif
 		if (!verify && !swidget->dynamic_pipeline_widget &&
 		    SOF_FW_VER(v->major, v->minor, v->micro) < SOF_FW_VER(2, 2, 0)) {
 			swidget->use_count = 0;
-			swidget->complete = 0;
+			if (swidget->spipe)
+				swidget->spipe->complete = 0;
 			continue;
 		}
 
diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index a5482185cd6c..17a116e8c47c 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -68,6 +68,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 	struct ipc4_pipeline_set_state_data *data;
 	struct snd_sof_widget *pipe_widget;
 	struct sof_ipc4_pipeline *pipeline;
+	struct snd_sof_pipeline *spipe;
 	struct snd_sof_pcm *spcm;
 	int ret;
 	int i, j;
@@ -79,7 +80,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 	pipeline_list = &spcm->stream[substream->stream].pipeline_list;
 
 	/* nothing to trigger if the list is empty */
-	if (!pipeline_list->pipe_widgets)
+	if (!pipeline_list->pipelines)
 		return 0;
 
 	/* allocate memory for the pipeline data */
@@ -96,7 +97,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 	 * sink->source would still be guaranteed for each fork independently.
 	 */
 	for (i = pipeline_list->count - 1; i >= 0; i--) {
-		pipe_widget = pipeline_list->pipe_widgets[i];
+		spipe = pipeline_list->pipelines[i];
+		pipe_widget = spipe->pipe_widget;
 		pipeline = pipe_widget->private;
 		if (pipeline->state != state && !pipeline->skip_during_fe_trigger)
 			data->pipeline_ids[data->count++] = pipe_widget->instance_id;
@@ -122,7 +124,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 	/* update PAUSED state for all pipelines that were just triggered */
 	for (i = 0; i < data->count; i++) {
 		for (j = 0; j < pipeline_list->count; j++) {
-			pipe_widget = pipeline_list->pipe_widgets[j];
+			spipe = pipeline_list->pipelines[j];
+			pipe_widget = spipe->pipe_widget;
 			pipeline = pipe_widget->private;
 
 			if (data->pipeline_ids[i] == pipe_widget->instance_id) {
@@ -146,7 +149,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 	/* update final state for all pipelines that were just triggered */
 	for (i = 0; i < data->count; i++) {
 		for (j = 0; j < pipeline_list->count; j++) {
-			pipe_widget = pipeline_list->pipe_widgets[j];
+			spipe = pipeline_list->pipelines[j];
+			pipe_widget = spipe->pipe_widget;
 			pipeline = pipe_widget->private;
 
 			if (data->pipeline_ids[i] == pipe_widget->instance_id) {
@@ -274,8 +278,8 @@ static void sof_ipc4_pcm_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm
 
 	for_each_pcm_streams(stream) {
 		pipeline_list = &spcm->stream[stream].pipeline_list;
-		kfree(pipeline_list->pipe_widgets);
-		pipeline_list->pipe_widgets = NULL;
+		kfree(pipeline_list->pipelines);
+		pipeline_list->pipelines = NULL;
 	}
 }
 
@@ -289,10 +293,9 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm
 		pipeline_list = &spcm->stream[stream].pipeline_list;
 
 		/* allocate memory for max number of pipeline IDs */
-		pipeline_list->pipe_widgets = kcalloc(ipc4_data->max_num_pipelines,
-						      sizeof(struct snd_sof_widget *),
-						      GFP_KERNEL);
-		if (!pipeline_list->pipe_widgets) {
+		pipeline_list->pipelines = kcalloc(ipc4_data->max_num_pipelines,
+						   sizeof(struct snd_sof_widget *), GFP_KERNEL);
+		if (!pipeline_list->pipelines) {
 			sof_ipc4_pcm_free(sdev, spcm);
 			return -ENOMEM;
 		}
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index f3b1a7f81216..2f82b5e02a27 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -855,7 +855,7 @@ sof_ipc4_update_pipeline_mem_usage(struct snd_sof_dev *sdev, struct snd_sof_widg
 
 	total = SOF_IPC4_FW_PAGE(task_mem + queue_mem);
 
-	pipe_widget = swidget->pipe_widget;
+	pipe_widget = swidget->spipe->pipe_widget;
 	pipeline = pipe_widget->private;
 	pipeline->mem_usage += total;
 }
@@ -969,7 +969,7 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget)
 	struct sof_ipc4_pipeline *pipeline;
 
 	/* reset pipeline memory usage */
-	pipe_widget = swidget->pipe_widget;
+	pipe_widget = swidget->spipe->pipe_widget;
 	pipeline = pipe_widget->private;
 	pipeline->mem_usage = 0;
 
@@ -1136,7 +1136,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
 		struct snd_sof_widget *pipe_widget;
 		struct sof_ipc4_pipeline *pipeline;
 
-		pipe_widget = swidget->pipe_widget;
+		pipe_widget = swidget->spipe->pipe_widget;
 		pipeline = pipe_widget->private;
 		ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
 		gtw_attr = ipc4_copier->gtw_attr;
@@ -1495,7 +1495,7 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr
 
 static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
 {
-	struct snd_sof_widget *pipe_widget = swidget->pipe_widget;
+	struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
 	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
 	struct sof_ipc4_pipeline *pipeline;
 	struct sof_ipc4_msg *msg;
@@ -1815,7 +1815,7 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s
 	 * routes belonging to the same pipeline will be disconnected by the FW when the pipeline
 	 * is freed. So avoid sending this IPC which will be ignored by the FW anyway.
 	 */
-	if (src_widget->pipe_widget == sink_widget->pipe_widget)
+	if (src_widget->spipe->pipe_widget == sink_widget->spipe->pipe_widget)
 		goto out;
 
 	header = src_fw_module->man4_module_entry.id;
@@ -1846,7 +1846,7 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s
 static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
 			       unsigned int flags, struct snd_sof_dai_config_data *data)
 {
-	struct snd_sof_widget *pipe_widget = swidget->pipe_widget;
+	struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
 	struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
 	struct snd_sof_dai *dai = swidget->private;
 	struct sof_ipc4_gtw_attributes *gtw_attr;
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index e6796c59e04b..158f7b03fbc2 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -31,6 +31,7 @@ static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_so
 int sof_widget_free(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_widget *pipe_widget;
 	int err = 0;
 	int ret;
 
@@ -43,6 +44,8 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
 	if (--swidget->use_count)
 		return 0;
 
+	pipe_widget = swidget->spipe->pipe_widget;
+
 	/* reset route setup status for all routes that contain this widget */
 	sof_reset_route_setup_status(sdev, swidget);
 
@@ -67,12 +70,15 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
 	 * skip for static pipelines
 	 */
 	if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) {
-		ret = sof_widget_free(sdev, swidget->pipe_widget);
+		ret = sof_widget_free(sdev, pipe_widget);
 		if (ret < 0 && !err)
 			err = ret;
-		swidget->pipe_widget->complete = 0;
 	}
 
+	/* 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);
 
@@ -103,14 +109,13 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
 	 * widget in the pipeline is freed. Skip setting up scheduler widget for static pipelines.
 	 */
 	if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) {
-		if (!swidget->pipe_widget) {
-			dev_err(sdev->dev, "No scheduler widget set for %s\n",
-				swidget->widget->name);
+		if (!swidget->spipe || !swidget->spipe->pipe_widget) {
+			dev_err(sdev->dev, "No pipeline set for %s\n", swidget->widget->name);
 			ret = -EINVAL;
 			goto use_count_dec;
 		}
 
-		ret = sof_widget_setup(sdev, swidget->pipe_widget);
+		ret = sof_widget_setup(sdev, swidget->spipe->pipe_widget);
 		if (ret < 0)
 			goto use_count_dec;
 	}
@@ -159,7 +164,7 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
 	snd_sof_dsp_core_put(sdev, swidget->core);
 pipe_widget_free:
 	if (swidget->id != snd_soc_dapm_scheduler)
-		sof_widget_free(sdev, swidget->pipe_widget);
+		sof_widget_free(sdev, swidget->spipe->pipe_widget);
 use_count_dec:
 	swidget->use_count--;
 	return ret;
@@ -409,7 +414,7 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d
 	struct snd_sof_pcm_stream_pipeline_list *pipeline_list = &spcm->stream[dir].pipeline_list;
 	struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
 	struct snd_sof_widget *swidget = widget->dobj.private;
-	struct snd_sof_widget *pipe_widget;
+	struct snd_sof_pipeline *spipe;
 	struct snd_soc_dapm_path *p;
 	int ret;
 
@@ -421,7 +426,7 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d
 			return ret;
 
 		/* skip populating the pipe_widgets array if it is NULL */
-		if (!pipeline_list->pipe_widgets)
+		if (!pipeline_list->pipelines)
 			goto sink_setup;
 
 		/*
@@ -430,14 +435,14 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d
 		 * order source to sink.
 		 */
 		for (i = 0; i < pipeline_list->count; i++) {
-			pipe_widget = pipeline_list->pipe_widgets[i];
-			if (pipe_widget == swidget->pipe_widget)
+			spipe = pipeline_list->pipelines[i];
+			if (spipe == swidget->spipe)
 				break;
 		}
 
 		if (i == pipeline_list->count) {
 			pipeline_list->count++;
-			pipeline_list->pipe_widgets[i] = swidget->pipe_widget;
+			pipeline_list->pipelines[i] = swidget->spipe;
 		}
 	}
 
@@ -572,11 +577,20 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
 	for_each_dapm_widgets(list, i, widget) {
 		struct snd_sof_widget *swidget = widget->dobj.private;
 		struct snd_sof_widget *pipe_widget;
+		struct snd_sof_pipeline *spipe;
 
 		if (!swidget)
 			continue;
 
-		pipe_widget = swidget->pipe_widget;
+		spipe = swidget->spipe;
+		if (!spipe) {
+			dev_err(sdev->dev, "no pipeline found for %s\n",
+				swidget->widget->name);
+			ret = -EINVAL;
+			goto widget_free;
+		}
+
+		pipe_widget = spipe->pipe_widget;
 		if (!pipe_widget) {
 			dev_err(sdev->dev, "error: no pipeline widget found for %s\n",
 				swidget->widget->name);
@@ -584,13 +598,13 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
 			goto widget_free;
 		}
 
-		if (pipe_widget->complete)
+		if (spipe->complete)
 			continue;
 
 		if (tplg_ops && tplg_ops->pipeline_complete) {
-			pipe_widget->complete = tplg_ops->pipeline_complete(sdev, pipe_widget);
-			if (pipe_widget->complete < 0) {
-				ret = pipe_widget->complete;
+			spipe->complete = tplg_ops->pipeline_complete(sdev, pipe_widget);
+			if (spipe->complete < 0) {
+				ret = spipe->complete;
 				goto widget_free;
 			}
 		}
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index bb5c61dd9b1e..f1bbd1adc8b6 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -288,11 +288,11 @@ struct sof_token_info {
 /**
  * struct snd_sof_pcm_stream_pipeline_list - List of pipelines associated with a PCM stream
  * @count: number of pipeline widgets in the @pipe_widgets array
- * @pipe_widgets: array of pipeline widgets
+ * @pipelines: array of pipelines
  */
 struct snd_sof_pcm_stream_pipeline_list {
 	u32 count;
-	struct snd_sof_widget **pipe_widgets;
+	struct snd_sof_pipeline **pipelines;
 };
 
 /* PCM stream, mapped to FW component  */
@@ -382,11 +382,6 @@ struct snd_sof_widget {
 	struct snd_soc_component *scomp;
 	int comp_id;
 	int pipeline_id;
-	/*
-	 * complete flag is used to indicate that pipeline set up is complete for scheduler type
-	 * widgets. It is unused for all other widget types.
-	 */
-	int complete;
 	/*
 	 * the prepared flag is used to indicate that a widget has been prepared for getting set
 	 * up in the DSP.
@@ -413,7 +408,7 @@ struct snd_sof_widget {
 
 	struct snd_soc_dapm_widget *widget;
 	struct list_head list;	/* list in sdev widget list */
-	struct snd_sof_widget *pipe_widget;
+	struct snd_sof_pipeline *spipe;
 	void *module_info;
 
 	const guid_t uuid;
@@ -451,6 +446,22 @@ struct snd_sof_widget {
 	void *private;		/* core does not touch this */
 };
 
+/** struct snd_sof_pipeline - ASoC SOF pipeline
+ * @pipe_widget: Pointer to the pipeline widget
+ * @started_count: Count of number of PCM's that have started this pipeline
+ * @paused_count: Count of number of PCM's that have started and have currently paused this
+		  pipeline
+ * @complete: flag used to indicate that pipeline set up is complete.
+ * @list: List item in sdev pipeline_list
+ */
+struct snd_sof_pipeline {
+	struct snd_sof_widget *pipe_widget;
+	int started_count;
+	int paused_count;
+	int complete;
+	struct list_head list;
+};
+
 /* ASoC SOF DAPM route */
 struct snd_sof_route {
 	struct snd_soc_component *scomp;
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index 86fc5c6a9c39..208a30ff3db9 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -578,6 +578,7 @@ struct snd_sof_dev {
 	struct list_head pcm_list;
 	struct list_head kcontrol_list;
 	struct list_head widget_list;
+	struct list_head pipeline_list;
 	struct list_head dai_list;
 	struct list_head dai_link_list;
 	struct list_head route_list;
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index 51f6fed45ae7..33ca3067262b 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -1402,7 +1402,6 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
 	swidget->scomp = scomp;
 	swidget->widget = w;
 	swidget->comp_id = sdev->next_comp_id++;
-	swidget->complete = 0;
 	swidget->id = w->id;
 	swidget->pipeline_id = index;
 	swidget->private = NULL;
@@ -1553,6 +1552,23 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
 		}
 	}
 
+	/* create and add pipeline for scheduler type widgets */
+	if (w->id == snd_soc_dapm_scheduler) {
+		struct snd_sof_pipeline *spipe;
+
+		spipe = kzalloc(sizeof(*spipe), GFP_KERNEL);
+		if (!spipe) {
+			kfree(swidget->private);
+			kfree(swidget->tuples);
+			kfree(swidget);
+			return -ENOMEM;
+		}
+
+		spipe->pipe_widget = swidget;
+		swidget->spipe = spipe;
+		list_add(&spipe->list, &sdev->pipeline_list);
+	}
+
 	w->dobj.private = swidget;
 	list_add(&swidget->list, &sdev->widget_list);
 	return ret;
@@ -1608,6 +1624,15 @@ static int sof_widget_unload(struct snd_soc_component *scomp,
 		sof_disconnect_dai_widget(scomp, widget);
 
 		break;
+	case snd_soc_dapm_scheduler:
+	{
+		struct snd_sof_pipeline *spipe = swidget->spipe;
+
+		list_del(&spipe->list);
+		kfree(spipe);
+		swidget->spipe = NULL;
+		break;
+	}
 	default:
 		break;
 	}
@@ -2082,18 +2107,19 @@ static int sof_route_load(struct snd_soc_component *scomp, int index,
 }
 
 /**
- * sof_set_pipe_widget - Set pipe_widget for a component
+ * sof_set_widget_pipeline - Set pipeline for a component
  * @sdev: pointer to struct snd_sof_dev
- * @pipe_widget: pointer to struct snd_sof_widget of type snd_soc_dapm_scheduler
+ * @spipe: pointer to struct snd_sof_pipeline
  * @swidget: pointer to struct snd_sof_widget that has the same pipeline ID as @pipe_widget
  *
  * Return: 0 if successful, -EINVAL on error.
  * The function checks if @swidget is associated with any volatile controls. If so, setting
  * the dynamic_pipeline_widget is disallowed.
  */
-static int sof_set_pipe_widget(struct snd_sof_dev *sdev, struct snd_sof_widget *pipe_widget,
-			       struct snd_sof_widget *swidget)
+static int sof_set_widget_pipeline(struct snd_sof_dev *sdev, struct snd_sof_pipeline *spipe,
+				   struct snd_sof_widget *swidget)
 {
+	struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
 	struct snd_sof_control *scontrol;
 
 	if (pipe_widget->dynamic_pipeline_widget) {
@@ -2108,8 +2134,8 @@ static int sof_set_pipe_widget(struct snd_sof_dev *sdev, struct snd_sof_widget *
 			}
 	}
 
-	/* set the pipe_widget and apply the dynamic_pipeline_widget_flag */
-	swidget->pipe_widget = pipe_widget;
+	/* set the pipeline and apply the dynamic_pipeline_widget_flag */
+	swidget->spipe = spipe;
 	swidget->dynamic_pipeline_widget = pipe_widget->dynamic_pipeline_widget;
 
 	return 0;
@@ -2123,6 +2149,7 @@ static int sof_complete(struct snd_soc_component *scomp)
 	struct snd_sof_widget *swidget, *comp_swidget;
 	const struct sof_ipc_tplg_widget_ops *widget_ops;
 	struct snd_sof_control *scontrol;
+	struct snd_sof_pipeline *spipe;
 	int ret;
 
 	widget_ops = tplg_ops ? tplg_ops->widget : NULL;
@@ -2155,23 +2182,21 @@ static int sof_complete(struct snd_soc_component *scomp)
 	}
 
 	/* set the pipe_widget and apply the dynamic_pipeline_widget_flag */
-	list_for_each_entry(swidget, &sdev->widget_list, list) {
-		switch (swidget->id) {
-		case snd_soc_dapm_scheduler:
-			/*
-			 * Apply the dynamic_pipeline_widget flag and set the pipe_widget field
-			 * for all widgets that have the same pipeline ID as the scheduler widget
-			 */
-			list_for_each_entry(comp_swidget, &sdev->widget_list, list)
-				if (comp_swidget->pipeline_id == swidget->pipeline_id) {
-					ret = sof_set_pipe_widget(sdev, swidget, comp_swidget);
-					if (ret < 0)
-						return ret;
-				}
-			break;
-		default:
-			break;
-		}
+	list_for_each_entry(spipe, &sdev->pipeline_list, list) {
+		struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
+
+		/*
+		 * Apply the dynamic_pipeline_widget flag and set the pipe_widget field
+		 * for all widgets that have the same pipeline ID as the scheduler widget.
+		 * Skip the scheduler widgets as they have their pipeline set during widget_ready
+		 */
+		list_for_each_entry(comp_swidget, &sdev->widget_list, list)
+			if (comp_swidget->widget->id != snd_soc_dapm_scheduler &&
+			    comp_swidget->pipeline_id == pipe_widget->pipeline_id) {
+				ret = sof_set_widget_pipeline(sdev, spipe, comp_swidget);
+				if (ret < 0)
+					return ret;
+			}
 	}
 
 	/* verify topology components loading including dynamic pipelines */
-- 
2.39.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 13/18] ASoC: SOF: ipc4-pcm: Rename 'data' variable to trigger_list
  2023-01-27 12:00 From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Peter Ujfalusi
                   ` (11 preceding siblings ...)
  2023-01-27 12:00 ` [PATCH 12/18] ASoC: SOF: Introduce struct snd_sof_pipeline Peter Ujfalusi
@ 2023-01-27 12:00 ` Peter Ujfalusi
  2023-01-27 12:00 ` [PATCH 14/18] ASoC: SOF: ipc4-pcm: Implement pipeline trigger reference counting Peter Ujfalusi
                   ` (6 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Peter Ujfalusi @ 2023-01-27 12:00 UTC (permalink / raw)
  To: lgirdwood, broonie
  Cc: alsa-devel, kai.vehmanen, pierre-louis.bossart, rander.wang,
	ranjani.sridharan, yung-chuan.liao

From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>

For more clarity, rename the struct ipc4_pipeline_set_state_data
variable to trigger_list instead of data. No functionality change.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Rander Wang <rander.wang@intel.com>
Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
---
 sound/soc/sof/ipc4-pcm.c | 38 ++++++++++++++++++++------------------
 1 file changed, 20 insertions(+), 18 deletions(-)

diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index 17a116e8c47c..284e402709be 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -14,14 +14,14 @@
 #include "ipc4-topology.h"
 
 static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state,
-					     struct ipc4_pipeline_set_state_data *data)
+					     struct ipc4_pipeline_set_state_data *trigger_list)
 {
 	struct sof_ipc4_msg msg = {{ 0 }};
 	u32 primary, ipc_size;
 
 	/* trigger a single pipeline */
-	if (data->count == 1)
-		return sof_ipc4_set_pipeline_state(sdev, data->pipeline_ids[0], state);
+	if (trigger_list->count == 1)
+		return sof_ipc4_set_pipeline_state(sdev, trigger_list->pipeline_ids[0], state);
 
 	primary = state;
 	primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_SET_PIPELINE_STATE);
@@ -33,9 +33,9 @@ static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state
 	msg.extension = SOF_IPC4_GLB_PIPE_STATE_EXT_MULTI;
 
 	/* ipc_size includes the count and the pipeline IDs for the number of pipelines */
-	ipc_size = sizeof(u32) * (data->count + 1);
+	ipc_size = sizeof(u32) * (trigger_list->count + 1);
 	msg.data_size = ipc_size;
-	msg.data_ptr = data;
+	msg.data_ptr = trigger_list;
 
 	return sof_ipc_tx_message(sdev->ipc, &msg, ipc_size, NULL, 0);
 }
@@ -65,7 +65,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
 	struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
-	struct ipc4_pipeline_set_state_data *data;
+	struct ipc4_pipeline_set_state_data *trigger_list;
 	struct snd_sof_widget *pipe_widget;
 	struct sof_ipc4_pipeline *pipeline;
 	struct snd_sof_pipeline *spipe;
@@ -84,8 +84,9 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 		return 0;
 
 	/* allocate memory for the pipeline data */
-	data = kzalloc(struct_size(data, pipeline_ids, pipeline_list->count), GFP_KERNEL);
-	if (!data)
+	trigger_list = kzalloc(struct_size(trigger_list, pipeline_ids, pipeline_list->count),
+			       GFP_KERNEL);
+	if (!trigger_list)
 		return -ENOMEM;
 
 	/*
@@ -101,12 +102,13 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 		pipe_widget = spipe->pipe_widget;
 		pipeline = pipe_widget->private;
 		if (pipeline->state != state && !pipeline->skip_during_fe_trigger)
-			data->pipeline_ids[data->count++] = pipe_widget->instance_id;
+			trigger_list->pipeline_ids[trigger_list->count++] =
+				pipe_widget->instance_id;
 	}
 
 	/* return if all pipelines are in the requested state already */
-	if (!data->count) {
-		kfree(data);
+	if (!trigger_list->count) {
+		kfree(trigger_list);
 		return 0;
 	}
 
@@ -115,20 +117,20 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 	 * they are already paused. But it helps keep the logic simpler and the firmware handles
 	 * the repeated pause gracefully. This can be optimized in the future if needed.
 	 */
-	ret = sof_ipc4_set_multi_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, data);
+	ret = sof_ipc4_set_multi_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, trigger_list);
 	if (ret < 0) {
 		dev_err(sdev->dev, "failed to pause all pipelines\n");
 		goto free;
 	}
 
 	/* update PAUSED state for all pipelines that were just triggered */
-	for (i = 0; i < data->count; i++) {
+	for (i = 0; i < trigger_list->count; i++) {
 		for (j = 0; j < pipeline_list->count; j++) {
 			spipe = pipeline_list->pipelines[j];
 			pipe_widget = spipe->pipe_widget;
 			pipeline = pipe_widget->private;
 
-			if (data->pipeline_ids[i] == pipe_widget->instance_id) {
+			if (trigger_list->pipeline_ids[i] == pipe_widget->instance_id) {
 				pipeline->state = SOF_IPC4_PIPE_PAUSED;
 				break;
 			}
@@ -140,20 +142,20 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 		goto free;
 
 	/* else set the final state in the DSP */
-	ret = sof_ipc4_set_multi_pipeline_state(sdev, state, data);
+	ret = sof_ipc4_set_multi_pipeline_state(sdev, state, trigger_list);
 	if (ret < 0) {
 		dev_err(sdev->dev, "failed to set final state %d for all pipelines\n", state);
 		goto free;
 	}
 
 	/* update final state for all pipelines that were just triggered */
-	for (i = 0; i < data->count; i++) {
+	for (i = 0; i < trigger_list->count; i++) {
 		for (j = 0; j < pipeline_list->count; j++) {
 			spipe = pipeline_list->pipelines[j];
 			pipe_widget = spipe->pipe_widget;
 			pipeline = pipe_widget->private;
 
-			if (data->pipeline_ids[i] == pipe_widget->instance_id) {
+			if (trigger_list->pipeline_ids[i] == pipe_widget->instance_id) {
 				pipeline->state = state;
 				break;
 			}
@@ -161,7 +163,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 	}
 
 free:
-	kfree(data);
+	kfree(trigger_list);
 	return ret;
 }
 
-- 
2.39.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 14/18] ASoC: SOF: ipc4-pcm: Implement pipeline trigger reference counting
  2023-01-27 12:00 From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Peter Ujfalusi
                   ` (12 preceding siblings ...)
  2023-01-27 12:00 ` [PATCH 13/18] ASoC: SOF: ipc4-pcm: Rename 'data' variable to trigger_list Peter Ujfalusi
@ 2023-01-27 12:00 ` Peter Ujfalusi
  2023-01-27 12:00 ` [PATCH 15/18] ASoC: SOF: ipc4-topology: Protect pipeline free with mutex Peter Ujfalusi
                   ` (5 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Peter Ujfalusi @ 2023-01-27 12:00 UTC (permalink / raw)
  To: lgirdwood, broonie
  Cc: alsa-devel, kai.vehmanen, pierre-louis.bossart, rander.wang,
	ranjani.sridharan, yung-chuan.liao

From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>

Use the started_count and paused_count to implement reference counting
when making decisions to start/stop/pause pipelines during the FE DAI
trigger. This is necessary to trigger the shared pipelines in the FE DAI
trigger properly.

With IPC4, the FE trigger will issue multiple pipeline state changes,
and the triggers are propagated downstream to connected pipelines by
the SOF driver - not the firmware. This creates a window for race
conditions where an FE trigger preempts another one, which results
in inconsistent pipeline states and refcounts.

This patch introduces a mutex lock for the pcm trigger that guarantees
that IPC4 state and resources are accessed in a serialized manner.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Rander Wang <rander.wang@intel.com>
Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
---
 sound/soc/sof/ipc4-pcm.c  | 228 +++++++++++++++++++++++++++++---------
 sound/soc/sof/ipc4-priv.h |   2 +
 sound/soc/sof/ipc4.c      |   2 +
 3 files changed, 182 insertions(+), 50 deletions(-)

diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index 284e402709be..ababa29d6eac 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -59,19 +59,152 @@ int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state)
 }
 EXPORT_SYMBOL(sof_ipc4_set_pipeline_state);
 
+static void
+sof_ipc4_add_pipeline_to_trigger_list(struct snd_sof_dev *sdev, int state,
+				      struct snd_sof_pipeline *spipe,
+				      struct ipc4_pipeline_set_state_data *trigger_list)
+{
+	struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
+	struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+
+	if (pipeline->skip_during_fe_trigger)
+		return;
+
+	switch (state) {
+	case SOF_IPC4_PIPE_RUNNING:
+		/*
+		 * Trigger pipeline if all PCMs containing it are paused or if it is RUNNING
+		 * for the first time
+		 */
+		if (spipe->started_count == spipe->paused_count)
+			trigger_list->pipeline_ids[trigger_list->count++] =
+				pipe_widget->instance_id;
+		break;
+	case SOF_IPC4_PIPE_RESET:
+		/* RESET if the pipeline is neither running nor paused */
+		if (!spipe->started_count && !spipe->paused_count)
+			trigger_list->pipeline_ids[trigger_list->count++] =
+				pipe_widget->instance_id;
+		break;
+	case SOF_IPC4_PIPE_PAUSED:
+		/* Pause the pipeline only when its started_count is 1 more than paused_count */
+		if (spipe->paused_count == (spipe->started_count - 1))
+			trigger_list->pipeline_ids[trigger_list->count++] =
+				pipe_widget->instance_id;
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd,
+			       struct snd_sof_pipeline *spipe,
+			       struct ipc4_pipeline_set_state_data *trigger_list)
+{
+	struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
+	struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+	int i;
+
+	if (pipeline->skip_during_fe_trigger)
+		return;
+
+	/* set state for pipeline if it was just triggered */
+	for (i = 0; i < trigger_list->count; i++) {
+		if (trigger_list->pipeline_ids[i] == pipe_widget->instance_id) {
+			pipeline->state = state;
+			break;
+		}
+	}
+
+	switch (state) {
+	case SOF_IPC4_PIPE_PAUSED:
+		switch (cmd) {
+		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+			/*
+			 * increment paused_count if the PAUSED is the final state during
+			 * the PAUSE trigger
+			 */
+			spipe->paused_count++;
+			break;
+		case SNDRV_PCM_TRIGGER_STOP:
+		case SNDRV_PCM_TRIGGER_SUSPEND:
+			/*
+			 * decrement started_count if PAUSED is the final state during the
+			 * STOP trigger
+			 */
+			spipe->started_count--;
+			break;
+		default:
+			break;
+		}
+		break;
+	case SOF_IPC4_PIPE_RUNNING:
+		switch (cmd) {
+		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+			/* decrement paused_count for RELEASE */
+			spipe->paused_count--;
+			break;
+		case SNDRV_PCM_TRIGGER_START:
+		case SNDRV_PCM_TRIGGER_RESUME:
+			/* increment started_count for START/RESUME */
+			spipe->started_count++;
+			break;
+		default:
+			break;
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+/*
+ * The picture below represents the pipeline state machine wrt PCM actions corresponding to the
+ * triggers and ioctls
+ *				+---------------+
+ *				|               |
+ *				|    INIT       |
+ *				|               |
+ *				+-------+-------+
+ *					|
+ *					|
+ *					| START
+ *					|
+ *					|
+ * +----------------+		   +------v-------+		  +-------------+
+ * |                |   START     |              |   HW_FREE	  |             |
+ * |   RUNNING      <-------------+  PAUSED      +--------------> +   RESET     |
+ * |                |   PAUSE     |              |		  |             |
+ * +------+---------+   RELEASE   +---------+----+		  +-------------+
+ *	  |				     ^
+ *	  |				     |
+ *	  |				     |
+ *	  |				     |
+ *	  |		PAUSE		     |
+ *	  +---------------------------------+
+ *			STOP/SUSPEND
+ *
+ * Note that during system suspend, the suspend trigger is followed by a hw_free in
+ * sof_pcm_trigger(). So, the final state during suspend would be RESET.
+ * Also, since the SOF driver doesn't support full resume, streams would be restarted with the
+ * prepare ioctl before the START trigger.
+ */
+
 static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
-				      struct snd_pcm_substream *substream, int state)
+				      struct snd_pcm_substream *substream, int state, int cmd)
 {
 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
 	struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
+	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
 	struct ipc4_pipeline_set_state_data *trigger_list;
-	struct snd_sof_widget *pipe_widget;
-	struct sof_ipc4_pipeline *pipeline;
 	struct snd_sof_pipeline *spipe;
 	struct snd_sof_pcm *spcm;
 	int ret;
-	int i, j;
+	int i;
+
+	dev_dbg(sdev->dev, "trigger cmd: %d state: %d\n", cmd, state);
 
 	spcm = snd_sof_find_spcm_dai(component, rtd);
 	if (!spcm)
@@ -89,33 +222,41 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 	if (!trigger_list)
 		return -ENOMEM;
 
+	mutex_lock(&ipc4_data->trigger_mutex);
+
 	/*
 	 * IPC4 requires pipelines to be triggered in order starting at the sink and
-	 * walking all the way to the source. So traverse the pipeline_list in the reverse order.
-	 * Skip the pipelines that have their skip_during_fe_trigger flag set or if they're already
-	 * in the requested state. If there is a fork in the pipeline, the order of triggering
-	 * between the left/right paths will be indeterministic. But the sink->source trigger order
-	 * sink->source would still be guaranteed for each fork independently.
+	 * walking all the way to the source. So traverse the pipeline_list in the order
+	 * sink->source when starting PCM's and in the reverse order to pause/stop PCM's.
+	 * Skip the pipelines that have their skip_during_fe_trigger flag set. If there is a fork
+	 * in the pipeline, the order of triggering between the left/right paths will be
+	 * indeterministic. But the sink->source trigger order sink->source would still be
+	 * guaranteed for each fork independently.
 	 */
-	for (i = pipeline_list->count - 1; i >= 0; i--) {
-		spipe = pipeline_list->pipelines[i];
-		pipe_widget = spipe->pipe_widget;
-		pipeline = pipe_widget->private;
-		if (pipeline->state != state && !pipeline->skip_during_fe_trigger)
-			trigger_list->pipeline_ids[trigger_list->count++] =
-				pipe_widget->instance_id;
-	}
+	if (state == SOF_IPC4_PIPE_RUNNING || state == SOF_IPC4_PIPE_RESET)
+		for (i = pipeline_list->count - 1; i >= 0; i--) {
+			spipe = pipeline_list->pipelines[i];
+			sof_ipc4_add_pipeline_to_trigger_list(sdev, state, spipe, trigger_list);
+		}
+	else
+		for (i = 0; i < pipeline_list->count; i++) {
+			spipe = pipeline_list->pipelines[i];
+			sof_ipc4_add_pipeline_to_trigger_list(sdev, state, spipe, trigger_list);
+		}
 
 	/* return if all pipelines are in the requested state already */
 	if (!trigger_list->count) {
-		kfree(trigger_list);
-		return 0;
+		ret = 0;
+		goto free;
 	}
 
+	/* no need to pause before reset or before pause release */
+	if (state == SOF_IPC4_PIPE_RESET || cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE)
+		goto skip_pause_transition;
+
 	/*
-	 * Pause all pipelines. This could result in an extra IPC to pause all pipelines even if
-	 * they are already paused. But it helps keep the logic simpler and the firmware handles
-	 * the repeated pause gracefully. This can be optimized in the future if needed.
+	 * set paused state for pipelines if the final state is PAUSED or when the pipeline
+	 * is set to RUNNING for the first time after the PCM is started.
 	 */
 	ret = sof_ipc4_set_multi_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, trigger_list);
 	if (ret < 0) {
@@ -123,46 +264,32 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 		goto free;
 	}
 
-	/* update PAUSED state for all pipelines that were just triggered */
-	for (i = 0; i < trigger_list->count; i++) {
-		for (j = 0; j < pipeline_list->count; j++) {
-			spipe = pipeline_list->pipelines[j];
-			pipe_widget = spipe->pipe_widget;
-			pipeline = pipe_widget->private;
-
-			if (trigger_list->pipeline_ids[i] == pipe_widget->instance_id) {
-				pipeline->state = SOF_IPC4_PIPE_PAUSED;
-				break;
-			}
-		}
+	/* update PAUSED state for all pipelines just triggered */
+	for (i = 0; i < pipeline_list->count ; i++) {
+		spipe = pipeline_list->pipelines[i];
+		sof_ipc4_update_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, cmd, spipe,
+					       trigger_list);
 	}
 
 	/* return if this is the final state */
 	if (state == SOF_IPC4_PIPE_PAUSED)
 		goto free;
-
-	/* else set the final state in the DSP */
+skip_pause_transition:
+	/* else set the RUNNING/RESET state in the DSP */
 	ret = sof_ipc4_set_multi_pipeline_state(sdev, state, trigger_list);
 	if (ret < 0) {
 		dev_err(sdev->dev, "failed to set final state %d for all pipelines\n", state);
 		goto free;
 	}
 
-	/* update final state for all pipelines that were just triggered */
-	for (i = 0; i < trigger_list->count; i++) {
-		for (j = 0; j < pipeline_list->count; j++) {
-			spipe = pipeline_list->pipelines[j];
-			pipe_widget = spipe->pipe_widget;
-			pipeline = pipe_widget->private;
-
-			if (trigger_list->pipeline_ids[i] == pipe_widget->instance_id) {
-				pipeline->state = state;
-				break;
-			}
-		}
+	/* update RUNNING/RESET state for all pipelines that were just triggered */
+	for (i = 0; i < pipeline_list->count; i++) {
+		spipe = pipeline_list->pipelines[i];
+		sof_ipc4_update_pipeline_state(sdev, state, cmd, spipe, trigger_list);
 	}
 
 free:
+	mutex_unlock(&ipc4_data->trigger_mutex);
 	kfree(trigger_list);
 	return ret;
 }
@@ -192,13 +319,14 @@ static int sof_ipc4_pcm_trigger(struct snd_soc_component *component,
 	}
 
 	/* set the pipeline state */
-	return sof_ipc4_trigger_pipelines(component, substream, state);
+	return sof_ipc4_trigger_pipelines(component, substream, state, cmd);
 }
 
 static int sof_ipc4_pcm_hw_free(struct snd_soc_component *component,
 				struct snd_pcm_substream *substream)
 {
-	return sof_ipc4_trigger_pipelines(component, substream, SOF_IPC4_PIPE_RESET);
+	/* command is not relevant with RESET, so just pass 0 */
+	return sof_ipc4_trigger_pipelines(component, substream, SOF_IPC4_PIPE_RESET, 0);
 }
 
 static void ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name,
diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h
index fc9efdce67e0..0c0d48376045 100644
--- a/sound/soc/sof/ipc4-priv.h
+++ b/sound/soc/sof/ipc4-priv.h
@@ -70,6 +70,7 @@ struct sof_ipc4_fw_library {
  *		    base firmware
  *
  * @load_library: Callback function for platform dependent library loading
+ * @trigger_mutex: Mutex to protect pipeline triggers, ref counts and states
  */
 struct sof_ipc4_fw_data {
 	u32 manifest_fw_hdr_offset;
@@ -82,6 +83,7 @@ struct sof_ipc4_fw_data {
 
 	int (*load_library)(struct snd_sof_dev *sdev,
 			    struct sof_ipc4_fw_library *fw_lib, bool reload);
+	struct mutex trigger_mutex; /* protect pipeline triggers, ref counts and states */
 };
 
 extern const struct sof_ipc_fw_loader_ops ipc4_loader_ops;
diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c
index 74cd7e956019..fb4760ae593f 100644
--- a/sound/soc/sof/ipc4.c
+++ b/sound/soc/sof/ipc4.c
@@ -662,6 +662,8 @@ static int sof_ipc4_init(struct snd_sof_dev *sdev)
 {
 	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
 
+	mutex_init(&ipc4_data->trigger_mutex);
+
 	xa_init_flags(&ipc4_data->fw_lib_xa, XA_FLAGS_ALLOC);
 
 	return 0;
-- 
2.39.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 15/18] ASoC: SOF: ipc4-topology: Protect pipeline free with mutex
  2023-01-27 12:00 From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Peter Ujfalusi
                   ` (13 preceding siblings ...)
  2023-01-27 12:00 ` [PATCH 14/18] ASoC: SOF: ipc4-pcm: Implement pipeline trigger reference counting Peter Ujfalusi
@ 2023-01-27 12:00 ` Peter Ujfalusi
  2023-01-27 12:00 ` [PATCH 16/18] ASoC: SOF: Avoid double decrementing use_count in sof_widget_setup on error Peter Ujfalusi
                   ` (4 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Peter Ujfalusi @ 2023-01-27 12:00 UTC (permalink / raw)
  To: lgirdwood, broonie
  Cc: alsa-devel, kai.vehmanen, pierre-louis.bossart, rander.wang,
	ranjani.sridharan, yung-chuan.liao

From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>

When starting/stopping multiple streams in parallel, pipeline triggers
and pipeline frees can get interleaved. So use the same mutex used for
pipeline trigger to protect the pipeline frees as well. Rename the
trigger_mutex to pipeline_state_mutex for more clarity.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
---
 sound/soc/sof/ipc4-pcm.c      | 4 ++--
 sound/soc/sof/ipc4-priv.h     | 4 ++--
 sound/soc/sof/ipc4-topology.c | 5 +++++
 sound/soc/sof/ipc4.c          | 2 +-
 4 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index ababa29d6eac..2d89d3708ed0 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -222,7 +222,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 	if (!trigger_list)
 		return -ENOMEM;
 
-	mutex_lock(&ipc4_data->trigger_mutex);
+	mutex_lock(&ipc4_data->pipeline_state_mutex);
 
 	/*
 	 * IPC4 requires pipelines to be triggered in order starting at the sink and
@@ -289,7 +289,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 	}
 
 free:
-	mutex_unlock(&ipc4_data->trigger_mutex);
+	mutex_unlock(&ipc4_data->pipeline_state_mutex);
 	kfree(trigger_list);
 	return ret;
 }
diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h
index 0c0d48376045..38bb3d7df42e 100644
--- a/sound/soc/sof/ipc4-priv.h
+++ b/sound/soc/sof/ipc4-priv.h
@@ -70,7 +70,7 @@ struct sof_ipc4_fw_library {
  *		    base firmware
  *
  * @load_library: Callback function for platform dependent library loading
- * @trigger_mutex: Mutex to protect pipeline triggers, ref counts and states
+ * @pipeline_state_mutex: Mutex to protect pipeline triggers, ref counts, states and deletion
  */
 struct sof_ipc4_fw_data {
 	u32 manifest_fw_hdr_offset;
@@ -83,7 +83,7 @@ struct sof_ipc4_fw_data {
 
 	int (*load_library)(struct snd_sof_dev *sdev,
 			    struct sof_ipc4_fw_library *fw_lib, bool reload);
-	struct mutex trigger_mutex; /* protect pipeline triggers, ref counts and states */
+	struct mutex pipeline_state_mutex; /* protect pipeline triggers, ref counts and states */
 };
 
 extern const struct sof_ipc_fw_loader_ops ipc4_loader_ops;
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index 2f82b5e02a27..43340c8917b7 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -1625,8 +1625,11 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
 static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
 {
 	struct sof_ipc4_fw_module *fw_module = swidget->module_info;
+	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
 	int ret = 0;
 
+	mutex_lock(&ipc4_data->pipeline_state_mutex);
+
 	/* freeing a pipeline frees all the widgets associated with it */
 	if (swidget->id == snd_soc_dapm_scheduler) {
 		struct sof_ipc4_pipeline *pipeline = swidget->private;
@@ -1652,6 +1655,8 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget
 		ida_free(&fw_module->m_ida, swidget->instance_id);
 	}
 
+	mutex_unlock(&ipc4_data->pipeline_state_mutex);
+
 	return ret;
 }
 
diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c
index fb4760ae593f..35c9f3913d9a 100644
--- a/sound/soc/sof/ipc4.c
+++ b/sound/soc/sof/ipc4.c
@@ -662,7 +662,7 @@ static int sof_ipc4_init(struct snd_sof_dev *sdev)
 {
 	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
 
-	mutex_init(&ipc4_data->trigger_mutex);
+	mutex_init(&ipc4_data->pipeline_state_mutex);
 
 	xa_init_flags(&ipc4_data->fw_lib_xa, XA_FLAGS_ALLOC);
 
-- 
2.39.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 16/18] ASoC: SOF: Avoid double decrementing use_count in sof_widget_setup on error
  2023-01-27 12:00 From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Peter Ujfalusi
                   ` (14 preceding siblings ...)
  2023-01-27 12:00 ` [PATCH 15/18] ASoC: SOF: ipc4-topology: Protect pipeline free with mutex Peter Ujfalusi
@ 2023-01-27 12:00 ` Peter Ujfalusi
  2023-01-27 12:00 ` [PATCH 17/18] ASoC: SOF: Protect swidget->use_count with mutex for kcontrol access race Peter Ujfalusi
                   ` (3 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Peter Ujfalusi @ 2023-01-27 12:00 UTC (permalink / raw)
  To: lgirdwood, broonie
  Cc: alsa-devel, kai.vehmanen, pierre-louis.bossart, rander.wang,
	ranjani.sridharan, yung-chuan.liao

The sof_widget_free() on the error path will decrement the use count and if
we jump to widget_free: then the use_count will be decremented by two,
which is not correct as we only incremented once with 1.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
---
 sound/soc/sof/sof-audio.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index 158f7b03fbc2..14b4b949d081 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -89,6 +89,7 @@ EXPORT_SYMBOL(sof_widget_free);
 int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
 {
 	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
+	bool use_count_decremented = false;
 	int ret;
 
 	/* skip if there is no private data */
@@ -160,13 +161,16 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
 widget_free:
 	/* widget use_count and core ref_count will both be decremented by sof_widget_free() */
 	sof_widget_free(sdev, swidget);
+	use_count_decremented = true;
 core_put:
 	snd_sof_dsp_core_put(sdev, swidget->core);
 pipe_widget_free:
 	if (swidget->id != snd_soc_dapm_scheduler)
 		sof_widget_free(sdev, swidget->spipe->pipe_widget);
 use_count_dec:
-	swidget->use_count--;
+	if (!use_count_decremented)
+		swidget->use_count--;
+
 	return ret;
 }
 EXPORT_SYMBOL(sof_widget_setup);
-- 
2.39.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 17/18] ASoC: SOF: Protect swidget->use_count with mutex for kcontrol access race
  2023-01-27 12:00 From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Peter Ujfalusi
                   ` (15 preceding siblings ...)
  2023-01-27 12:00 ` [PATCH 16/18] ASoC: SOF: Avoid double decrementing use_count in sof_widget_setup on error Peter Ujfalusi
@ 2023-01-27 12:00 ` Peter Ujfalusi
  2023-01-27 12:00 ` [PATCH 18/18] ASoC: SOF: ipc4-pcm: Do not run the trigger pipelines if no spipe is stored Peter Ujfalusi
                   ` (2 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Peter Ujfalusi @ 2023-01-27 12:00 UTC (permalink / raw)
  To: lgirdwood, broonie
  Cc: alsa-devel, kai.vehmanen, pierre-louis.bossart, rander.wang,
	ranjani.sridharan, yung-chuan.liao

The use_count of the swidget is protect by ALSA core PCM locking with the
exception when an associated kcontrol is changed.

It has been observed that a rightly timed kcontrol access during stream
stop can result of an attempt to send a control update to a widget which
has been freed up between the check of the use_count and the message
sending.

We need to protect the entire sof_widget_setup() and sof_widget_free()
execution to make it safe to rely on the use_count.
Move the code under an _unlocked() function and use a mutex to protect
the execution of the functions for concurrency.
On the control path we need to use the lock only for the kcontrol access,
the widget_kcontrol_setup() op is called with the lock already held.

Reported-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
---
 sound/soc/sof/ipc3-control.c  | 46 +++++++++++++++++++++++------------
 sound/soc/sof/ipc3-topology.c |  2 ++
 sound/soc/sof/ipc4-control.c  | 33 +++++++++++++++++--------
 sound/soc/sof/sof-audio.c     | 36 ++++++++++++++++++++++-----
 sound/soc/sof/sof-audio.h     | 11 ++++++++-
 sound/soc/sof/topology.c      |  2 ++
 6 files changed, 97 insertions(+), 33 deletions(-)

diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c
index 3fdc0d854e65..217ac5501a98 100644
--- a/sound/soc/sof/ipc3-control.c
+++ b/sound/soc/sof/ipc3-control.c
@@ -12,7 +12,8 @@
 #include "ipc3-priv.h"
 
 /* IPC set()/get() for kcontrols. */
-static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set)
+static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol,
+					  bool set, bool lock)
 {
 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scontrol->scomp);
 	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
@@ -21,6 +22,7 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
 	struct snd_sof_widget *swidget;
 	bool widget_found = false;
 	u32 ipc_cmd, msg_bytes;
+	int ret = 0;
 
 	list_for_each_entry(swidget, &sdev->widget_list, list) {
 		if (swidget->comp_id == scontrol->comp_id) {
@@ -35,13 +37,18 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
 		return -EINVAL;
 	}
 
+	if (lock)
+		mutex_lock(&swidget->setup_mutex);
+	else
+		lockdep_assert_held(&swidget->setup_mutex);
+
 	/*
-	 * Volatile controls should always be part of static pipelines and the widget use_count
-	 * would always be > 0 in this case. For the others, just return the cached value if the
-	 * widget is not set up.
+	 * Volatile controls should always be part of static pipelines and the
+	 * widget use_count would always be > 0 in this case. For the others,
+	 * just return the cached value if the widget is not set up.
 	 */
 	if (!swidget->use_count)
-		return 0;
+		goto unlock;
 
 	/*
 	 * Select the IPC cmd and the ctrl_type based on the ctrl_cmd and the
@@ -81,13 +88,20 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
 			     sizeof(struct sof_abi_hdr);
 		break;
 	default:
-		return -EINVAL;
+		ret = -EINVAL;
+		goto unlock;
 	}
 
 	cdata->rhdr.hdr.size = msg_bytes;
 	cdata->elems_remaining = 0;
 
-	return iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set);
+	ret = iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set);
+
+unlock:
+	if (lock)
+		mutex_unlock(&swidget->setup_mutex);
+
+	return ret;
 }
 
 static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
@@ -108,7 +122,7 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
 
 	/* refresh the component data from DSP */
 	scontrol->comp_data_dirty = false;
-	ret = sof_ipc3_set_get_kcontrol_data(scontrol, false);
+	ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true);
 	if (ret < 0) {
 		dev_err(scomp->dev, "Failed to get control data: %d\n", ret);
 
@@ -156,7 +170,7 @@ static bool sof_ipc3_volume_put(struct snd_sof_control *scontrol,
 
 	/* notify DSP of mixer updates */
 	if (pm_runtime_active(scomp->dev)) {
-		int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
+		int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true);
 
 		if (ret < 0) {
 			dev_err(scomp->dev, "Failed to set mixer updates for %s\n",
@@ -204,7 +218,7 @@ static bool sof_ipc3_switch_put(struct snd_sof_control *scontrol,
 
 	/* notify DSP of mixer updates */
 	if (pm_runtime_active(scomp->dev)) {
-		int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
+		int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true);
 
 		if (ret < 0) {
 			dev_err(scomp->dev, "Failed to set mixer updates for %s\n",
@@ -252,7 +266,7 @@ static bool sof_ipc3_enum_put(struct snd_sof_control *scontrol,
 
 	/* notify DSP of enum updates */
 	if (pm_runtime_active(scomp->dev)) {
-		int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
+		int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true);
 
 		if (ret < 0) {
 			dev_err(scomp->dev, "Failed to set enum updates for %s\n",
@@ -324,7 +338,7 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol,
 
 	/* notify DSP of byte control updates */
 	if (pm_runtime_active(scomp->dev))
-		return sof_ipc3_set_get_kcontrol_data(scontrol, true);
+		return sof_ipc3_set_get_kcontrol_data(scontrol, true, true);
 
 	return 0;
 }
@@ -438,7 +452,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
 
 	/* notify DSP of byte control updates */
 	if (pm_runtime_active(scomp->dev))
-		return sof_ipc3_set_get_kcontrol_data(scontrol, true);
+		return sof_ipc3_set_get_kcontrol_data(scontrol, true, true);
 
 	return 0;
 }
@@ -468,7 +482,7 @@ static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol,
 	cdata->data->abi = SOF_ABI_VERSION;
 
 	/* get all the component data from DSP */
-	ret = sof_ipc3_set_get_kcontrol_data(scontrol, false);
+	ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true);
 	if (ret < 0)
 		return ret;
 
@@ -647,7 +661,7 @@ static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev,
 	list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
 		if (scontrol->comp_id == swidget->comp_id) {
 			/* set kcontrol data in DSP */
-			ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
+			ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, false);
 			if (ret < 0) {
 				dev_err(sdev->dev,
 					"kcontrol %d set up failed for widget %s\n",
@@ -664,7 +678,7 @@ static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev,
 			if (swidget->dynamic_pipeline_widget)
 				continue;
 
-			ret = sof_ipc3_set_get_kcontrol_data(scontrol, false);
+			ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, false);
 			if (ret < 0)
 				dev_warn(sdev->dev,
 					 "kcontrol %d read failed for widget %s\n",
diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c
index 88b9b9507231..dceb78bfe17c 100644
--- a/sound/soc/sof/ipc3-topology.c
+++ b/sound/soc/sof/ipc3-topology.c
@@ -2316,7 +2316,9 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif
 		/* Do not free widgets for static pipelines with FW older than SOF2.2 */
 		if (!verify && !swidget->dynamic_pipeline_widget &&
 		    SOF_FW_VER(v->major, v->minor, v->micro) < SOF_FW_VER(2, 2, 0)) {
+			mutex_lock(&swidget->setup_mutex);
 			swidget->use_count = 0;
+			mutex_unlock(&swidget->setup_mutex);
 			if (swidget->spipe)
 				swidget->spipe->complete = 0;
 			continue;
diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c
index 0d5a578c3496..67bd2233fd9a 100644
--- a/sound/soc/sof/ipc4-control.c
+++ b/sound/soc/sof/ipc4-control.c
@@ -12,7 +12,8 @@
 #include "ipc4-priv.h"
 #include "ipc4-topology.h"
 
-static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set)
+static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol,
+					  bool set, bool lock)
 {
 	struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
 	struct snd_soc_component *scomp = scontrol->scomp;
@@ -21,6 +22,7 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
 	struct sof_ipc4_msg *msg = &cdata->msg;
 	struct snd_sof_widget *swidget;
 	bool widget_found = false;
+	int ret = 0;
 
 	/* find widget associated with the control */
 	list_for_each_entry(swidget, &sdev->widget_list, list) {
@@ -35,23 +37,34 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
 		return -ENOENT;
 	}
 
+	if (lock)
+		mutex_lock(&swidget->setup_mutex);
+	else
+		lockdep_assert_held(&swidget->setup_mutex);
+
 	/*
-	 * Volatile controls should always be part of static pipelines and the widget use_count
-	 * would always be > 0 in this case. For the others, just return the cached value if the
-	 * widget is not set up.
+	 * Volatile controls should always be part of static pipelines and the
+	 * widget use_count would always be > 0 in this case. For the others,
+	 * just return the cached value if the widget is not set up.
 	 */
 	if (!swidget->use_count)
-		return 0;
+		goto unlock;
 
 	msg->primary &= ~SOF_IPC4_MOD_INSTANCE_MASK;
 	msg->primary |= SOF_IPC4_MOD_INSTANCE(swidget->instance_id);
 
-	return iops->set_get_data(sdev, msg, msg->data_size, set);
+	ret = iops->set_get_data(sdev, msg, msg->data_size, set);
+
+unlock:
+	if (lock)
+		mutex_unlock(&swidget->setup_mutex);
+
+	return ret;
 }
 
 static int
 sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
-			 struct snd_sof_control *scontrol)
+			 struct snd_sof_control *scontrol, bool lock)
 {
 	struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
 	struct sof_ipc4_gain *gain = swidget->private;
@@ -90,7 +103,7 @@ sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidge
 		msg->data_ptr = &data;
 		msg->data_size = sizeof(data);
 
-		ret = sof_ipc4_set_get_kcontrol_data(scontrol, true);
+		ret = sof_ipc4_set_get_kcontrol_data(scontrol, true, lock);
 		msg->data_ptr = NULL;
 		msg->data_size = 0;
 		if (ret < 0) {
@@ -145,7 +158,7 @@ static bool sof_ipc4_volume_put(struct snd_sof_control *scontrol,
 		return false;
 	}
 
-	ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol);
+	ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, true);
 	if (ret < 0)
 		return false;
 
@@ -175,7 +188,7 @@ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_s
 
 	list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
 		if (scontrol->comp_id == swidget->comp_id) {
-			ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol);
+			ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, false);
 			if (ret < 0) {
 				dev_err(sdev->dev, "%s: kcontrol %d set up failed for widget %s\n",
 					__func__, scontrol->comp_id, swidget->widget->name);
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index 14b4b949d081..760621bfc802 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -28,7 +28,8 @@ static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_so
 		}
 }
 
-int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+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_widget *pipe_widget;
@@ -70,7 +71,7 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
 	 * skip for static pipelines
 	 */
 	if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) {
-		ret = sof_widget_free(sdev, pipe_widget);
+		ret = sof_widget_free_unlocked(sdev, pipe_widget);
 		if (ret < 0 && !err)
 			err = ret;
 	}
@@ -84,9 +85,21 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
 
 	return err;
 }
+
+int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+{
+	int ret;
+
+	mutex_lock(&swidget->setup_mutex);
+	ret = sof_widget_free_unlocked(sdev, swidget);
+	mutex_unlock(&swidget->setup_mutex);
+
+	return ret;
+}
 EXPORT_SYMBOL(sof_widget_free);
 
-int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+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);
 	bool use_count_decremented = false;
@@ -116,7 +129,7 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
 			goto use_count_dec;
 		}
 
-		ret = sof_widget_setup(sdev, swidget->spipe->pipe_widget);
+		ret = sof_widget_setup_unlocked(sdev, swidget->spipe->pipe_widget);
 		if (ret < 0)
 			goto use_count_dec;
 	}
@@ -160,19 +173,30 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
 
 widget_free:
 	/* widget use_count and core ref_count will both be decremented by sof_widget_free() */
-	sof_widget_free(sdev, swidget);
+	sof_widget_free_unlocked(sdev, swidget);
 	use_count_decremented = true;
 core_put:
 	snd_sof_dsp_core_put(sdev, swidget->core);
 pipe_widget_free:
 	if (swidget->id != snd_soc_dapm_scheduler)
-		sof_widget_free(sdev, swidget->spipe->pipe_widget);
+		sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget);
 use_count_dec:
 	if (!use_count_decremented)
 		swidget->use_count--;
 
 	return ret;
 }
+
+int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+{
+	int ret;
+
+	mutex_lock(&swidget->setup_mutex);
+	ret = sof_widget_setup_unlocked(sdev, swidget);
+	mutex_unlock(&swidget->setup_mutex);
+
+	return ret;
+}
 EXPORT_SYMBOL(sof_widget_setup);
 
 int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource,
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index f1bbd1adc8b6..b0593b46d477 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -387,7 +387,16 @@ struct snd_sof_widget {
 	 * up in the DSP.
 	 */
 	bool prepared;
-	int use_count; /* use_count will be protected by the PCM mutex held by the core */
+
+	struct mutex setup_mutex; /* to protect the swidget setup and free operations */
+
+	/*
+	 * use_count is protected by the PCM mutex held by the core and the
+	 * setup_mutex against non stream domain races (kcontrol access for
+	 * example)
+	 */
+	int use_count;
+
 	int core;
 	int id; /* id is the DAPM widget type */
 	/*
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index 33ca3067262b..e2f8cd9e278e 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -1405,6 +1405,8 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
 	swidget->id = w->id;
 	swidget->pipeline_id = index;
 	swidget->private = NULL;
+	mutex_init(&swidget->setup_mutex);
+
 	ida_init(&swidget->src_queue_ida);
 	ida_init(&swidget->sink_queue_ida);
 
-- 
2.39.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 18/18] ASoC: SOF: ipc4-pcm: Do not run the trigger pipelines if no spipe is stored
  2023-01-27 12:00 From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Peter Ujfalusi
                   ` (16 preceding siblings ...)
  2023-01-27 12:00 ` [PATCH 17/18] ASoC: SOF: Protect swidget->use_count with mutex for kcontrol access race Peter Ujfalusi
@ 2023-01-27 12:00 ` Peter Ujfalusi
  2023-01-27 12:07 ` [PATCH 00/18] ASoC: SOF: ipc4: Multi-stream playback and capture support Péter Ujfalusi
  2023-01-28 10:48 ` From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Mark Brown
  19 siblings, 0 replies; 21+ messages in thread
From: Peter Ujfalusi @ 2023-01-27 12:00 UTC (permalink / raw)
  To: lgirdwood, broonie
  Cc: alsa-devel, kai.vehmanen, pierre-louis.bossart, rander.wang,
	ranjani.sridharan, yung-chuan.liao

If the pipeline setup fails at the first widget/pipeline then we will have
no spipe stored under the pipeline_list->pipelines, the
pipeline_list->count is 0.

If this is the case we would have a NULL pointer dereference if the
execution is allowed to proceed.

Check for this condition along with the pipeline_list->pipelines check

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
---
 sound/soc/sof/ipc4-pcm.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index 2d89d3708ed0..521090d4498d 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -213,7 +213,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
 	pipeline_list = &spcm->stream[substream->stream].pipeline_list;
 
 	/* nothing to trigger if the list is empty */
-	if (!pipeline_list->pipelines)
+	if (!pipeline_list->pipelines || !pipeline_list->count)
 		return 0;
 
 	/* allocate memory for the pipeline data */
-- 
2.39.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH 00/18] ASoC: SOF: ipc4: Multi-stream playback and capture support
  2023-01-27 12:00 From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Peter Ujfalusi
                   ` (17 preceding siblings ...)
  2023-01-27 12:00 ` [PATCH 18/18] ASoC: SOF: ipc4-pcm: Do not run the trigger pipelines if no spipe is stored Peter Ujfalusi
@ 2023-01-27 12:07 ` Péter Ujfalusi
  2023-01-28 10:48 ` From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Mark Brown
  19 siblings, 0 replies; 21+ messages in thread
From: Péter Ujfalusi @ 2023-01-27 12:07 UTC (permalink / raw)
  To: lgirdwood, broonie
  Cc: alsa-devel, kai.vehmanen, ranjani.sridharan,
	pierre-louis.bossart, rander.wang, yung-chuan.liao

Hi,

wow, something messed up the subject line, which should have been:

[PATCH 00/18] ASoC: SOF: ipc4: Multi-stream playback and capture support

-- 
Péter

On 27/01/2023 14:00, Peter Ujfalusi wrote:
> Hi,
> 
> The following series will enable multi-stream support for playback and capture
> streams.
> Currently only a single PCM can be connected to a DAI, with the multi-stream
> support it is possible to connect multiple PCMs to a single DAI.
> 
> To achieve this we need to make sure that DAIs/AIF are only set up once since
> other stream could be connected to it later.
> 
> We also need to introduce reference or use counting for widgets to make sure
> that they are not going to be destroyed while other streams are still using
> them.
> 
> With the multi-stream support we also need to extend our current locking scheme
> which worked well for simple paths.
> 
> The first patch was sent to the mailing list, but it is not yet applied and I
> have included in this series as it is a direct dependency:
> https://lore.kernel.org/alsa-devel/20230117121615.25690-1-peter.ujfalusi@linux.intel.com/
> 
> Regards,
> Peter
> ---
> Peter Ujfalusi (3):
>   ASoC: SOF: Avoid double decrementing use_count in sof_widget_setup on
>     error
>   ASoC: SOF: Protect swidget->use_count with mutex for kcontrol access
>     race
>   ASoC: SOF: ipc4-pcm: Do not run the trigger pipelines if no spipe is
>     stored
> 
> Ranjani Sridharan (15):
>   ASoC: SOF: ipc4-topology: No need to unbind routes within a pipeline
>   ASoC: soc-pcm: Export widget_in_list()
>   ASoC: SOF: sof-audio: Set up/free DAI/AIF widgets only once
>   ASoC: SOF: sof-audio: Only process widgets in the connected widget
>     list
>   ASoC: SOF: pcm: do not free widgets during suspend trigger
>   ASoC: SOF: topology: Set IPC-specific trigger order for DAI links
>   ASoC: SOF: Introduce PCM setup/free PCM IPC ops
>   ASoC: SOF: ipc4-pcm: Define pcm_setup/free ops
>   ASoC: SOF: ipc4: Add flag to skip triggering pipelines during FE DAI
>     trigger
>   ASoC: SOF: sof-audio: Populate the PCM stream pipeline_info
>   ASoC: SOF: ipc4-pcm: Use the PCM stream's pipeline_info during trigger
>   ASoC: SOF: Introduce struct snd_sof_pipeline
>   ASoC: SOF: ipc4-pcm: Rename 'data' variable to trigger_list
>   ASoC: SOF: ipc4-pcm: Implement pipeline trigger reference counting
>   ASoC: SOF: ipc4-topology: Protect pipeline free with mutex
> 
>  include/sound/soc-dpcm.h        |   2 +
>  include/sound/sof/ipc4/header.h |   3 +
>  sound/soc/soc-pcm.c             |   3 +-
>  sound/soc/sof/core.c            |   1 +
>  sound/soc/sof/intel/hda-dai.c   |  92 ++-------
>  sound/soc/sof/ipc3-control.c    |  46 +++--
>  sound/soc/sof/ipc3-topology.c   |  32 ++-
>  sound/soc/sof/ipc4-control.c    |  33 ++-
>  sound/soc/sof/ipc4-pcm.c        | 343 +++++++++++++++++++++++++-------
>  sound/soc/sof/ipc4-priv.h       |   2 +
>  sound/soc/sof/ipc4-topology.c   |  48 ++++-
>  sound/soc/sof/ipc4-topology.h   |  12 ++
>  sound/soc/sof/ipc4.c            |   2 +
>  sound/soc/sof/pcm.c             |   5 +-
>  sound/soc/sof/sof-audio.c       | 226 ++++++++++++++-------
>  sound/soc/sof/sof-audio.h       |  54 ++++-
>  sound/soc/sof/sof-priv.h        |   1 +
>  sound/soc/sof/topology.c        | 114 +++++++----
>  18 files changed, 714 insertions(+), 305 deletions(-)
> 


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001
  2023-01-27 12:00 From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Peter Ujfalusi
                   ` (18 preceding siblings ...)
  2023-01-27 12:07 ` [PATCH 00/18] ASoC: SOF: ipc4: Multi-stream playback and capture support Péter Ujfalusi
@ 2023-01-28 10:48 ` Mark Brown
  19 siblings, 0 replies; 21+ messages in thread
From: Mark Brown @ 2023-01-28 10:48 UTC (permalink / raw)
  To: lgirdwood, Peter Ujfalusi
  Cc: alsa-devel, kai.vehmanen, ranjani.sridharan,
	pierre-louis.bossart, rander.wang, yung-chuan.liao

On Fri, 27 Jan 2023 14:00:13 +0200, Peter Ujfalusi wrote:
> The following series will enable multi-stream support for playback and capture
> streams.
> Currently only a single PCM can be connected to a DAI, with the multi-stream
> support it is possible to connect multiple PCMs to a single DAI.
> 
> To achieve this we need to make sure that DAIs/AIF are only set up once since
> other stream could be connected to it later.
> 
> [...]

Applied to

   broonie/sound.git for-next

Thanks!

[01/18] ASoC: SOF: ipc4-topology: No need to unbind routes within a pipeline
        commit: 9a62d87acee94919af1fe92f2412fff83dcbcda0
[02/18] ASoC: soc-pcm: Export widget_in_list()
        commit: 5edcf2a3aad41ee398ac011cda7bccca400b56f0
[03/18] ASoC: SOF: sof-audio: Set up/free DAI/AIF widgets only once
        commit: 73ea660947b52969214473434396a33d283c5ac8
[04/18] ASoC: SOF: sof-audio: Only process widgets in the connected widget list
        commit: 4639029b046bcab11bd566afa2979c68edeb338a
[05/18] ASoC: SOF: pcm: do not free widgets during suspend trigger
        commit: 82b18242ae68214c44ccb13e993c2bc925f28428
[06/18] ASoC: SOF: topology: Set IPC-specific trigger order for DAI links
        commit: e380c9071032b89ea2e77b871792d908d0f15512
[07/18] ASoC: SOF: Introduce PCM setup/free PCM IPC ops
        commit: 7201a3d47e8a6a0b3a55125e70a9c650afabe7b0
[08/18] ASoC: SOF: ipc4-pcm: Define pcm_setup/free ops
        commit: ba223b3ad0b9f1753f0822c5c441a925cc82b63a
[09/18] ASoC: SOF: ipc4: Add flag to skip triggering pipelines during FE DAI trigger
        commit: 37a26eec53b09b7054234b77200ce729601b0ccb
[10/18] ASoC: SOF: sof-audio: Populate the PCM stream pipeline_info
        commit: 19137532dbe32ff2c8b5b1442c077bf3abff86f3
[11/18] ASoC: SOF: ipc4-pcm: Use the PCM stream's pipeline_info during trigger
        commit: 2d271af1af241e64726ada07c6bef6572f1be6a5
[12/18] ASoC: SOF: Introduce struct snd_sof_pipeline
        commit: 9c04363d222bc94d49d883458b2854334a848b5f
[13/18] ASoC: SOF: ipc4-pcm: Rename 'data' variable to trigger_list
        commit: 6f9eb19a33d608ba36162a9ccbd34a77249fcc2e
[14/18] ASoC: SOF: ipc4-pcm: Implement pipeline trigger reference counting
        commit: 32c4b69872e5fe5fd9517826be31dbf2c3dd917a
[15/18] ASoC: SOF: ipc4-topology: Protect pipeline free with mutex
        commit: 6bc4d1b714aafc0ee3c7649c36aa19998b4c11f9
[16/18] ASoC: SOF: Avoid double decrementing use_count in sof_widget_setup on error
        commit: 955a6f131a50c3685c560ef7b75880d272337b33
[17/18] ASoC: SOF: Protect swidget->use_count with mutex for kcontrol access race
        commit: f94f3915274d22d5cd8b253e0533822b934f5f41
[18/18] ASoC: SOF: ipc4-pcm: Do not run the trigger pipelines if no spipe is stored
        commit: 251a2b11851531526260db1dbc5673a7d6177895

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


^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2023-01-28 10:49 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-27 12:00 From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Peter Ujfalusi
2023-01-27 12:00 ` [PATCH 01/18] ASoC: SOF: ipc4-topology: No need to unbind routes within a pipeline Peter Ujfalusi
2023-01-27 12:00 ` [PATCH 02/18] ASoC: soc-pcm: Export widget_in_list() Peter Ujfalusi
2023-01-27 12:00 ` [PATCH 03/18] ASoC: SOF: sof-audio: Set up/free DAI/AIF widgets only once Peter Ujfalusi
2023-01-27 12:00 ` [PATCH 04/18] ASoC: SOF: sof-audio: Only process widgets in the connected widget list Peter Ujfalusi
2023-01-27 12:00 ` [PATCH 05/18] ASoC: SOF: pcm: do not free widgets during suspend trigger Peter Ujfalusi
2023-01-27 12:00 ` [PATCH 06/18] ASoC: SOF: topology: Set IPC-specific trigger order for DAI links Peter Ujfalusi
2023-01-27 12:00 ` [PATCH 07/18] ASoC: SOF: Introduce PCM setup/free PCM IPC ops Peter Ujfalusi
2023-01-27 12:00 ` [PATCH 08/18] ASoC: SOF: ipc4-pcm: Define pcm_setup/free ops Peter Ujfalusi
2023-01-27 12:00 ` [PATCH 09/18] ASoC: SOF: ipc4: Add flag to skip triggering pipelines during FE DAI trigger Peter Ujfalusi
2023-01-27 12:00 ` [PATCH 10/18] ASoC: SOF: sof-audio: Populate the PCM stream pipeline_info Peter Ujfalusi
2023-01-27 12:00 ` [PATCH 11/18] ASoC: SOF: ipc4-pcm: Use the PCM stream's pipeline_info during trigger Peter Ujfalusi
2023-01-27 12:00 ` [PATCH 12/18] ASoC: SOF: Introduce struct snd_sof_pipeline Peter Ujfalusi
2023-01-27 12:00 ` [PATCH 13/18] ASoC: SOF: ipc4-pcm: Rename 'data' variable to trigger_list Peter Ujfalusi
2023-01-27 12:00 ` [PATCH 14/18] ASoC: SOF: ipc4-pcm: Implement pipeline trigger reference counting Peter Ujfalusi
2023-01-27 12:00 ` [PATCH 15/18] ASoC: SOF: ipc4-topology: Protect pipeline free with mutex Peter Ujfalusi
2023-01-27 12:00 ` [PATCH 16/18] ASoC: SOF: Avoid double decrementing use_count in sof_widget_setup on error Peter Ujfalusi
2023-01-27 12:00 ` [PATCH 17/18] ASoC: SOF: Protect swidget->use_count with mutex for kcontrol access race Peter Ujfalusi
2023-01-27 12:00 ` [PATCH 18/18] ASoC: SOF: ipc4-pcm: Do not run the trigger pipelines if no spipe is stored Peter Ujfalusi
2023-01-27 12:07 ` [PATCH 00/18] ASoC: SOF: ipc4: Multi-stream playback and capture support Péter Ujfalusi
2023-01-28 10:48 ` From 644473b181f0f310e428301a2ed459f912eec7ea Mon Sep 17 00:00:00 2001 Mark Brown

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.