From 64d7cf41f32aa34e32e7285c26acb2b057228a04 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 18 Oct 2022 16:32:27 -0700 Subject: [PATCH 1/5] ASoC: SOF: Introduce PCM setup/free PCM IPC ops These will be used to perform IPC-specific PCM setup/free. Signed-off-by: Ranjani Sridharan --- 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 7bd7c18c4347bf..dc8bf0e0814035 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 c9a5462c850eac..0160d2cf0b7e55 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1651,6 +1651,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 = sdev->ipc->ops->pcm; struct snd_soc_tplg_stream_caps *caps; struct snd_soc_tplg_private *private = &pcm->priv; struct snd_sof_pcm *spcm; @@ -1678,6 +1679,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->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); @@ -1755,6 +1763,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 = sdev->ipc->ops->pcm; struct snd_sof_pcm *spcm = dobj->private; /* free PCM DMA pages */ @@ -1764,6 +1774,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->pcm_free) + ipc_pcm_ops->pcm_free(sdev, spcm); + /* remove from list and free spcm */ list_del(&spcm->list); kfree(spcm); From 1b3379e2d9d0d2b661ec9c788e0373b851a26267 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 18 Oct 2022 16:34:30 -0700 Subject: [PATCH 2/5] ASoC: SOF: ipc4-pcm: Define pcm_setup/free ops 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 --- sound/soc/sof/ipc4-pcm.c | 41 +++++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-audio.h | 11 +++++++++++ 2 files changed, 52 insertions(+) diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 23de58d7d06b38..623386773fad98 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -215,8 +215,49 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } +static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm) +{ + struct snd_sof_pcm_stream_trigger_info *trigger_info; + struct sof_ipc4_fw_data *ipc4_data = sdev->private; + int stream; + + for_each_pcm_streams(stream) { + trigger_info = &spcm->stream[stream].trigger_info; + + /* allocate memory for max number of pipeline IDs */ + trigger_info->pipeline_list = kcalloc(ipc4_data->max_num_pipelines, + sizeof(struct snd_sof_widget *), + GFP_KERNEL); + if (!trigger_info->pipeline_list) + goto err; + } + + return 0; +err: + for_each_pcm_streams(stream) { + trigger_info = &spcm->stream[stream].trigger_info; + kfree(trigger_info->pipeline_list); + trigger_info->pipeline_list = NULL; + } + return -ENOMEM; +} + +static void sof_ipc4_pcm_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm) +{ + struct snd_sof_pcm_stream_trigger_info *trigger_info; + int stream; + + for_each_pcm_streams(stream) { + trigger_info = &spcm->stream[stream].trigger_info; + kfree(trigger_info->pipeline_list); + trigger_info->pipeline_list = NULL; + } +} + 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 dc8bf0e0814035..8b9240b0406ccf 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -282,6 +282,16 @@ struct sof_token_info { int count; }; +/** + * struct snd_sof_pipeline_trigger_info - PCM stream trigger info + * @count: number of pipelines to be triggered + * @pipeline_list: array of pipeline widgets to be triggered + */ +struct snd_sof_pcm_stream_trigger_info { + uint32_t count; + struct snd_sof_widget **pipeline_list; +}; + /* PCM stream, mapped to FW component */ struct snd_sof_pcm_stream { u32 comp_id; @@ -297,6 +307,7 @@ struct snd_sof_pcm_stream { * active or not while suspending the stream */ bool suspend_ignored; + struct snd_sof_pcm_stream_trigger_info trigger_info; }; /* ALSA SOF PCM device */ From 55b013f2a3648e1bbd2954bfd80a79a34e862343 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 18 Oct 2022 16:41:56 -0700 Subject: [PATCH 3/5] ASoC: SOF: ipc4: Add flag to skip triggering pipelines during FE DAI trigger 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 --- 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 1c3d4887aa304c..98eebb4b07e6d3 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 623386773fad98..4c455cd9718e77 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 eac36424967473..84972902596a49 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1801,6 +1801,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 0aa87a8add5d39..c89566bd7a6b51 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -71,6 +71,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; @@ -78,6 +79,7 @@ struct sof_ipc4_pipeline { uint32_t mem_usage; int state; struct sof_ipc4_msg msg; + bool skip_during_fe_trigger; }; /** From 479e68d413be79c9a5ccfaee9308708dea92ad67 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 18 Oct 2022 16:55:31 -0700 Subject: [PATCH 4/5] ASoC: SOF: sof-audio: Populate the PCM stream pipeline_info 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 --- 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 9447b9ee388722..f1f9e230e5d183 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -358,8 +358,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; @@ -378,7 +379,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; @@ -394,17 +395,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_trigger_info *trigger_info = &spcm->stream[dir].trigger_info; + 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 pipeline_list array if it is NULL */ + if (!trigger_info->pipeline_list) + 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 < trigger_info->count; i++) { + pipe_widget = trigger_info->pipeline_list[i]; + if (pipe_widget == swidget->pipe_widget) + break; + } + + if (i == trigger_info->count) { + trigger_info->count++; + trigger_info->pipeline_list[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)) @@ -412,11 +440,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; } } @@ -426,16 +454,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_IS_AIF(widget->id)) @@ -447,11 +479,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: @@ -505,16 +537,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; } @@ -558,15 +590,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_trigger_info *trigger_info = &spcm->stream[dir].trigger_info; struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; int ret; @@ -575,14 +608,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; + trigger_info->count = 0; + return ret; } From 4abe6445f5ea1e3303d273adfa80cd1772014850 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 17 Oct 2022 14:35:25 -0700 Subject: [PATCH 5/5] ASoC: SOF: ipc4-pcm: Use the PCM stream's pipeline_info during trigger 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 --- 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 622193be7ac43d..d31349bf011d3a 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 4c455cd9718e77..41121afd3cca18 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_trigger_info *trigger_info; + 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; + trigger_info = &spcm->stream[substream->stream].trigger_info; + + /* nothing to trigger if the list is empty */ + if (!trigger_info->pipeline_list) + return 0; + + /* allocate data for the pipeline ID's and the count of pipelines to be triggered */ + data = kzalloc(sizeof(u32) * (trigger_info->count + 1), 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 = trigger_info->count - 1; i >= 0; i--) { + pipe_widget = trigger_info->pipeline_list[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 < trigger_info->count; j++) { + pipe_widget = trigger_info->pipeline_list[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 < trigger_info->count; j++) { + pipe_widget = trigger_info->pipeline_list[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 c89566bd7a6b51..39995c00a3f0f8 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -82,6 +82,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 { + uint32_t count; + DECLARE_FLEX_ARRAY(u32, pipeline_ids); +} __packed; + /** * struct sof_ipc4_available_audio_format - Available audio formats * @base_config: Available base config