From 85c69ce13f1e97246b11f64a2e3af1d1b36f925c Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 7 Dec 2024 12:09:17 -0800 Subject: [PATCH 1/6] ASoC: SOF: sof-audio: Add a new op in struct sof_ipc_tplg_ops Add a new host_config op in struct sof_ipc_tplg_ops and define it for IPC4. This will be used to configure the host widget during prepare after a suspend/resume or after an xrun. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/ipc4-topology.c | 20 ++++++++++++++++++++ sound/soc/sof/sof-audio.h | 3 +++ 2 files changed, 23 insertions(+) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 27979a99e283a9..02a2c075bbc70f 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1796,6 +1796,25 @@ sof_ipc4_prepare_dai_copier(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, return ret; } +static void sof_ipc4_host_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, + struct snd_sof_platform_stream_params *platform_params) +{ + struct sof_ipc4_copier *ipc4_copier = (struct sof_ipc4_copier *)swidget->private; + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; + struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; + u32 host_dma_id = platform_params->stream_tag - 1; + + if (pipeline->use_chain_dma) { + pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_MASK; + pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(host_dma_id); + return; + } + + copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK; + copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(host_dma_id); +} + static int sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, struct snd_pcm_hw_params *fe_params, @@ -3552,4 +3571,5 @@ const struct sof_ipc_tplg_ops ipc4_tplg_ops = { .dai_get_param = sof_ipc4_dai_get_param, .tear_down_all_pipelines = sof_ipc4_tear_down_all_pipelines, .link_setup = sof_ipc4_link_setup, + .host_config = sof_ipc4_host_config, }; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 36ab75e11779d2..fafd967e207080 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -209,6 +209,7 @@ struct sof_ipc_tplg_widget_ops { * @widget_setup: Function pointer for setting up setup in the DSP * @widget_free: Function pointer for freeing widget in the DSP * @dai_config: Function pointer for sending DAI config IPC to the DSP + * @host_config: Function pointer for setting the DMA ID for host widgets * @dai_get_param: Function pointer for getting the DAI parameter * @set_up_all_pipelines: Function pointer for setting up all topology pipelines * @tear_down_all_pipelines: Function pointer for tearing down all topology pipelines @@ -230,6 +231,8 @@ struct sof_ipc_tplg_ops { int (*widget_free)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); int (*dai_config)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, unsigned int flags, struct snd_sof_dai_config_data *data); + void (*host_config)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, + struct snd_sof_platform_stream_params *platform_params); int (*dai_get_param)(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int param_type); int (*set_up_all_pipelines)(struct snd_sof_dev *sdev, bool verify); int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify); From d5ba3157c676eeea29e9363b8850eb3c59620e40 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 3 Dec 2024 14:17:33 -0800 Subject: [PATCH 2/6] ASoC: SOF: pcm: Split up widget prepare and setup In preparation for refacting pipeline management, split the widget prepare and set up between the hw_params and prepare ioctls. This is required to ensure that the BE pipeline widgets can be set up during the BE DAI prepare and the remaining widgets will be set up during the FE DAI prepare. The widget's ipc_prepare op for all widgets in both the BE and FE pipelines are handled during the FE DAI hw_params to make sure that the pipeline params can be propagated all the way from the source widget to the sink widget. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/pcm.c | 81 +++++++++++++++++++++++++++++++-------- sound/soc/sof/sof-audio.c | 39 +++++++++++-------- sound/soc/sof/sof-audio.h | 6 +++ 3 files changed, 95 insertions(+), 31 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index d584a72e6f52fd..5ee1d30b1f53dd 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -106,9 +106,9 @@ sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_run spcm->stream[dir].list = list; - ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir); + ret = sof_widget_list_prepare(sdev, spcm, params, platform_params, dir); if (ret < 0) { - spcm_err(spcm, dir, "Widget list set up failed\n"); + spcm_err(spcm, dir, "widget list prepare failed\n"); spcm->stream[dir].list = NULL; snd_soc_dapm_dai_free_widgets(&list); return ret; @@ -118,15 +118,30 @@ sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_run return 0; } +static struct snd_sof_widget *snd_sof_find_swidget_by_comp_id(struct snd_sof_dev *sdev, + int comp_id) +{ + struct snd_sof_widget *swidget; + + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (comp_id == swidget->comp_id) + return swidget; + } + + return NULL; +} + static int sof_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); - struct snd_sof_platform_stream_params platform_params = { 0 }; + struct snd_sof_platform_stream_params *platform_params; struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_sof_widget *host_widget; struct snd_sof_pcm *spcm; int ret; @@ -152,7 +167,8 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, spcm->prepared[substream->stream] = false; } - ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params); + platform_params = &spcm->platform_params[substream->stream]; + ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, platform_params); if (ret < 0) { spcm_err(spcm, substream->stream, "platform hw params failed\n"); return ret; @@ -160,12 +176,27 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, /* if this is a repeated hw_params without hw_free, skip setting up widgets */ if (!spcm->stream[substream->stream].list) { - ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, params, &platform_params, + ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, params, platform_params, substream->stream); if (ret < 0) return ret; } + if (!sdev->dspless_mode_selected) { + int host_comp_id = spcm->stream[substream->stream].comp_id; + + host_widget = snd_sof_find_swidget_by_comp_id(sdev, host_comp_id); + if (!host_widget) { + spcm_err(spcm, substream->stream, + "failed to find host widget with comp_id %d\n", host_comp_id); + return -EINVAL; + } + + /* set the host DMA ID */ + if (tplg_ops && tplg_ops->host_config) + tplg_ops->host_config(sdev, host_widget, platform_params); + } + /* create compressed page table for audio firmware */ if (runtime->buffer_changed) { ret = create_page_table(component, substream, runtime->dma_area, @@ -175,14 +206,6 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, return ret; } - if (pcm_ops && pcm_ops->hw_params) { - ret = pcm_ops->hw_params(component, substream, params, &platform_params); - if (ret < 0) - return ret; - } - - spcm->prepared[substream->stream] = true; - /* save pcm hw_params */ memcpy(&spcm->params[substream->stream], params, sizeof(*params)); @@ -287,6 +310,9 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, true); + /* unprepare and free the list of DAPM widgets */ + sof_widget_list_unprepare(sdev, spcm, substream->stream); + cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work); return ret; @@ -297,7 +323,12 @@ static int sof_pcm_prepare(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); + struct snd_sof_platform_stream_params *platform_params; + struct snd_soc_dapm_widget_list *list; + struct snd_pcm_hw_params *params; struct snd_sof_pcm *spcm; + int dir = substream->stream; int ret; /* nothing to do for BE */ @@ -323,15 +354,33 @@ static int sof_pcm_prepare(struct snd_soc_component *component, return ret; } - /* set hw_params */ - ret = sof_pcm_hw_params(component, - substream, &spcm->params[substream->stream]); + ret = sof_pcm_hw_params(component, substream, &spcm->params[substream->stream]); if (ret < 0) { spcm_err(spcm, substream->stream, "failed to set hw_params after resume\n"); return ret; } + list = spcm->stream[dir].list; + params = &spcm->params[substream->stream]; + platform_params = &spcm->platform_params[substream->stream]; + ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir); + if (ret < 0) { + dev_err(sdev->dev, "failed widget list set up for pcm %d dir %d\n", + spcm->pcm.pcm_id, dir); + spcm->stream[dir].list = NULL; + snd_soc_dapm_dai_free_widgets(&list); + return ret; + } + + if (pcm_ops && pcm_ops->hw_params) { + ret = pcm_ops->hw_params(component, substream, params, platform_params); + if (ret < 0) + return ret; + } + + spcm->prepared[substream->stream] = true; + return 0; } diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index a9664b4cf43f90..b21818942d84e6 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -672,6 +672,30 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, return 0; } +int sof_widget_list_prepare(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) +{ + /* + * 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. + */ + return sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, + dir, SOF_WIDGET_PREPARE); +} + +void sof_widget_list_unprepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir) +{ + struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; + + /* unprepare the widget */ + 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; +} + int sof_widget_list_setup(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, @@ -686,15 +710,6 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, if (!list) return 0; - /* - * 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, 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, spcm, fe_params, platform_params, dir, SOF_WIDGET_SETUP); @@ -772,12 +787,6 @@ int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int /* send IPC to free widget in the DSP */ ret = sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_FREE); - /* unprepare the widget */ - 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; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index fafd967e207080..fab6027a95d805 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -354,6 +354,7 @@ struct snd_sof_pcm { struct snd_sof_pcm_stream stream[2]; struct list_head list; /* list in sdev pcm list */ struct snd_pcm_hw_params params[2]; + struct snd_sof_platform_stream_params platform_params[2]; bool prepared[2]; /* PCM_PARAMS set successfully */ bool pending_stop[2]; /* only used if (!pcm_ops->platform_stop_during_hw_free) */ @@ -663,6 +664,11 @@ int sof_widget_list_setup(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); +int sof_widget_list_prepare(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); +void sof_widget_list_unprepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm); From 524fcf2055a6c3f0cf800e38bbe034beda6c49b7 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 3 Dec 2024 17:35:38 -0800 Subject: [PATCH 3/6] ASoC: SOF: sof-audio: Add helper functions for BE managed pipelines Define 3 new helper functions for setting up/freeing/unpreparing BE pipelines. These will be used when the pipeline management is refactored to set up/free widgets, routes and pipelines during BE DAI prepare or hw_free. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/intel/hda.h | 8 -- sound/soc/sof/sof-audio.c | 246 ++++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-audio.h | 14 +++ 3 files changed, 260 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index ee4ccc1a549001..2105a1e5c99332 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -1039,12 +1039,4 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags, struct snd_sof_dai_config_data *data); -static inline struct snd_sof_dev *widget_to_sdev(struct snd_soc_dapm_widget *w) -{ - struct snd_sof_widget *swidget = w->dobj.private; - struct snd_soc_component *component = swidget->scomp; - - return snd_soc_component_get_drvdata(component); -} - #endif diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index b21818942d84e6..fc7daa4e9aa325 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -986,3 +986,249 @@ int sof_dai_get_tdm_slots(struct snd_soc_pcm_runtime *rtd) return sof_dai_get_param(rtd, SOF_DAI_PARAM_INTEL_SSP_TDM_SLOTS); } EXPORT_SYMBOL(sof_dai_get_tdm_slots); + +static int snd_sof_set_up_widgets_in_pipeline(struct snd_soc_dapm_widget *w, + struct snd_sof_pipeline *spipe, int dir) +{ + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_sof_dev *sdev = widget_to_sdev(w); + struct snd_soc_dapm_path *p; + int ret; + + /* set up the widget if it belongs to the same pipeline as the DAI widget */ + if (swidget->spipe != spipe || !swidget->prepared) + return 0; + + ret = sof_widget_setup(sdev, swidget); + if (ret < 0) + return ret; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (!p->walking) { + p->walking = true; + + ret = snd_sof_set_up_widgets_in_pipeline(p->source, spipe, dir); + p->walking = false; + if (ret < 0) + goto err; + } + } + } else { + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (!p->walking) { + p->walking = true; + + ret = snd_sof_set_up_widgets_in_pipeline(p->sink, spipe, dir); + p->walking = false; + if (ret < 0) + goto err; + } + } + } + + return 0; +err: + if (swidget) + sof_widget_free(sdev, swidget); + return ret; +} + +void snd_sof_unprepare_widgets_in_pipeline(struct snd_soc_dapm_widget *w, + struct snd_sof_pipeline *spipe, int dir) +{ + struct snd_sof_widget *swidget = w->dobj.private; + const struct sof_ipc_tplg_widget_ops *widget_ops; + struct snd_sof_dev *sdev = widget_to_sdev(w); + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + struct snd_soc_dapm_path *p; + + /* unprepare the widget only if it belongs to the same pipeline as the DAI widget */ + if (!swidget || swidget->spipe != spipe) + return; + + /* skip if the widget is in use or if it is already unprepared */ + if (!swidget->prepared || swidget->use_count > 0) + goto unprepare; + + widget_ops = tplg_ops ? tplg_ops->widget : NULL; + if (widget_ops && widget_ops[w->id].ipc_unprepare) + /* unprepare the source widget */ + widget_ops[w->id].ipc_unprepare(swidget); + + swidget->prepared = false; + +unprepare: + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (!p->walking) { + p->walking = true; + snd_sof_unprepare_widgets_in_pipeline(p->source, spipe, dir); + p->walking = false; + } + } + } else { + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (!p->walking) { + p->walking = true; + snd_sof_unprepare_widgets_in_pipeline(p->sink, spipe, dir); + p->walking = false; + } + } + } +} +EXPORT_SYMBOL(snd_sof_unprepare_widgets_in_pipeline); + +static int snd_sof_free_widgets_in_pipeline(struct snd_soc_dapm_widget *w, + struct snd_sof_pipeline *spipe, int dir) +{ + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_sof_dev *sdev = widget_to_sdev(w); + struct snd_soc_dapm_path *p; + int ret = 0; + int err; + + /* free the widget if it belongs to the same pipeline as the DAI widget */ + if (swidget->spipe != spipe || !swidget->prepared) + return 0; + + err = sof_widget_free(sdev, swidget); + if (err < 0) + ret = err; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (!p->walking) { + p->walking = true; + + err = snd_sof_free_widgets_in_pipeline(p->source, spipe, dir); + p->walking = false; + if (err < 0) + ret = err; + } + } + } else { + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (!p->walking) { + p->walking = true; + + err = snd_sof_free_widgets_in_pipeline(p->sink, spipe, dir); + p->walking = false; + if (err < 0) + ret = err; + } + } + } + + return ret; +} + +static int snd_sof_set_up_routes_in_pipeline(struct snd_soc_dapm_widget *w, + struct snd_sof_pipeline *spipe, int dir) +{ + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_sof_dev *sdev = widget_to_sdev(w); + struct snd_soc_dapm_path *p; + int ret; + + if (swidget->spipe != spipe) + return 0; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (!p->walking) { + struct snd_soc_dapm_widget *wsource = p->source; + struct snd_sof_widget *source_swidget = wsource->dobj.private; + + if (source_swidget->spipe != spipe) + continue; + + p->walking = true; + + ret = sof_route_setup(sdev, wsource, w); + if (ret < 0) { + p->walking = false; + return ret; + } + + ret = snd_sof_set_up_routes_in_pipeline(wsource, spipe, dir); + p->walking = false; + if (ret < 0) + return ret; + } + } + } else { + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (!p->walking) { + struct snd_soc_dapm_widget *wsink = p->sink; + struct snd_sof_widget *sink_swidget = wsink->dobj.private; + + if (sink_swidget->spipe != spipe) + continue; + + p->walking = true; + + ret = sof_route_setup(sdev, w, wsink); + if (ret < 0) { + p->walking = false; + return ret; + } + + ret = snd_sof_set_up_routes_in_pipeline(wsink, spipe, dir); + p->walking = false; + if (ret < 0) + return ret; + } + } + } + + return 0; +} + +int snd_sof_set_up_be_pipeline(struct snd_soc_dapm_widget *w, int dir) +{ + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_sof_dev *sdev = widget_to_sdev(w); + int ret; + + /* set up the widgets in the BE pipeline */ + ret = snd_sof_set_up_widgets_in_pipeline(w, swidget->spipe, dir); + if (ret < 0) { + dev_err(sdev->dev, "failed to set up widgets in the BE pipeline with DAI: %s\n", + w->name); + return ret; + } + + /* set up the routes */ + ret = snd_sof_set_up_routes_in_pipeline(w, swidget->spipe, dir); + if (ret < 0) { + dev_err(sdev->dev, "failed to set up routes in the BE pipeline with DAI: %s\n", + w->name); + return ret; + } + + swidget->spipe->complete = 1; + return 0; +} +EXPORT_SYMBOL(snd_sof_set_up_be_pipeline); + +int snd_sof_free_be_pipeline(struct snd_soc_dapm_widget *w, int dir) +{ + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_sof_dev *sdev = widget_to_sdev(w); + int ret; + + /* + * free the widgets in the BE pipeline. Routes are automatically disconnected when + * widgets are freed + */ + ret = snd_sof_free_widgets_in_pipeline(w, swidget->spipe, dir); + if (ret < 0) + dev_err(sdev->dev, "failed to free widgets in the BE pipeline with DAI: %s\n", + w->name); + + swidget->spipe->complete = 0; + + return ret; +} +EXPORT_SYMBOL(snd_sof_free_be_pipeline); diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index fab6027a95d805..d078f535abdb6b 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -683,4 +683,18 @@ int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum so struct snd_sof_tuple *tuples, int num_tuples, size_t object_size, int token_instance_num); u32 vol_compute_gain(u32 value, int *tlv); + +/* pipeline management */ +int snd_sof_set_up_be_pipeline(struct snd_soc_dapm_widget *w, int dir); +int snd_sof_free_be_pipeline(struct snd_soc_dapm_widget *w, int dir); +void snd_sof_unprepare_widgets_in_pipeline(struct snd_soc_dapm_widget *w, + struct snd_sof_pipeline *spipe, int dir); + +static inline struct snd_sof_dev *widget_to_sdev(struct snd_soc_dapm_widget *w) +{ + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_soc_component *component = swidget->scomp; + + return snd_soc_component_get_drvdata(component); +} #endif From 12cdbf445ecd26ef01059e58abd393fa775b1d4c Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 4 Dec 2024 19:29:29 -0800 Subject: [PATCH 4/6] drivers: soundwire: Add new prepare op in struct sdw_intel_ops Add and define the prepare op for Intel ACE2.x platforms. For the moment, the prepare_stream op is exactly the same as the params_stream op. But it will be modified in the following patch when the pipeline management is refactored. Signed-off-by: Ranjani Sridharan --- drivers/soundwire/intel_ace2x.c | 25 +++++++++++++++++++++++-- include/linux/soundwire/sdw_intel.h | 2 ++ sound/soc/sof/intel/hda-dai.c | 7 +++++++ sound/soc/sof/intel/hda.c | 9 +++++++++ sound/soc/sof/intel/hda.h | 3 +++ 5 files changed, 44 insertions(+), 2 deletions(-) diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index e305c6258ca967..4a2e01ec2ed644 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -286,6 +286,27 @@ static int intel_params_stream(struct sdw_intel *sdw, return -EIO; } +static int intel_prepare_stream(struct sdw_intel *sdw, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, + struct snd_pcm_hw_params *hw_params, + int link_id, int alh_stream_id) +{ + struct sdw_intel_link_res *res = sdw->link_res; + struct sdw_intel_stream_params_data params_data; + + params_data.substream = substream; + params_data.dai = dai; + params_data.hw_params = hw_params; + params_data.link_id = link_id; + params_data.alh_stream_id = alh_stream_id; + + if (res->ops && res->ops->prepare_stream && res->dev) + return res->ops->prepare_stream(res->dev, ¶ms_data); + + return -EIO; +} + static int intel_free_stream(struct sdw_intel *sdw, struct snd_pcm_substream *substream, struct snd_soc_dai *dai, @@ -422,8 +443,8 @@ static int intel_prepare(struct snd_pcm_substream *substream, } /* Inform DSP about PDI stream number */ - return intel_params_stream(sdw, substream, dai, hw_params, sdw->instance, - dai_runtime->pdi->intel_alh_id); + return intel_prepare_stream(sdw, substream, dai, hw_params, sdw->instance, + dai_runtime->pdi->intel_alh_id); } static int diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h index 580086417e4b0e..cedfcd3257b16b 100644 --- a/include/linux/soundwire/sdw_intel.h +++ b/include/linux/soundwire/sdw_intel.h @@ -220,6 +220,8 @@ struct sdw_intel_stream_free_data { struct sdw_intel_ops { int (*params_stream)(struct device *dev, struct sdw_intel_stream_params_data *params_data); + int (*prepare_stream)(struct device *dev, + struct sdw_intel_stream_params_data *params_data); int (*free_stream)(struct device *dev, struct sdw_intel_stream_free_data *free_data); int (*trigger)(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai); diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index da12aabc1bb856..858402857f1a53 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -638,6 +638,13 @@ int sdw_hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, } EXPORT_SYMBOL_NS(sdw_hda_dai_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON"); +int sdw_hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai, int link_id, int intel_alh_id) +{ + return sdw_hda_dai_hw_params(substream, params, cpu_dai, link_id, intel_alh_id); +} +EXPORT_SYMBOL_NS(sdw_hda_dai_prepare, "SND_SOC_SOF_INTEL_HDA_COMMON"); + static int hda_dai_suspend(struct hdac_bus *bus) { struct snd_soc_pcm_runtime *rtd; diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 720803dadee83a..8b24e408f05dae 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -119,6 +119,14 @@ static int sdw_ace2x_params_stream(struct device *dev, params_data->alh_stream_id); } +static int sdw_ace2x_prepare_stream(struct device *dev, + struct sdw_intel_stream_params_data *params_data) +{ + return sdw_hda_dai_prepare(params_data->substream, params_data->hw_params, + params_data->dai, params_data->link_id, + params_data->alh_stream_id); +} + static int sdw_ace2x_free_stream(struct device *dev, struct sdw_intel_stream_free_data *free_data) { @@ -134,6 +142,7 @@ static int sdw_ace2x_trigger(struct snd_pcm_substream *substream, int cmd, struc static struct sdw_intel_ops sdw_ace2x_callback = { .params_stream = sdw_ace2x_params_stream, + .prepare_stream = sdw_ace2x_prepare_stream, .free_stream = sdw_ace2x_free_stream, .trigger = sdw_ace2x_trigger, }; diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 2105a1e5c99332..feb9b2c622c4fc 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -894,6 +894,9 @@ int sdw_hda_dai_hw_free(struct snd_pcm_substream *substream, int sdw_hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *cpu_dai); +int sdw_hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai, int link_id, int intel_alh_id); + /* common dai driver */ extern struct snd_soc_dai_driver skl_dai[]; int hda_dsp_dais_suspend(struct snd_sof_dev *sdev); From b47c706fae218f7ee663b4603e804ff8dcd4e803 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 3 Dec 2024 18:54:52 -0800 Subject: [PATCH 5/6] ASoC: SOF: Refactor pipeline management Invoke the BE pipeline helper functions in the BE DAI prepare and hw_free to ensure that all the widgets and routes belonging to the BE pipeline are handled in the BE DAI ops. Add a new field in struct snd_sof_pipeline to identify BE managed pipelines i.e. those that will managed by the BE DAI ops. Lastly, modify the FE ops to make sure that the widgets/routes belonging to these pipelines are skipped during setup/prepare/unprepare. Note that the widget_ipc_prepare op handles all pipelines including those managed by the BE DAI ops. This is because in order to prepare all the widgets, the hw_params needs to be propagated along the list of connected DAPM widgets in order from the source to the sink. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/intel/hda-dai-ops.c | 19 +++-- sound/soc/sof/intel/hda-dai.c | 125 +++++++++++++++++++++++++++--- sound/soc/sof/ipc4-pcm.c | 4 +- sound/soc/sof/ipc4-topology.c | 1 - sound/soc/sof/sof-audio.c | 40 ++++++++-- sound/soc/sof/sof-audio.h | 3 + 6 files changed, 170 insertions(+), 22 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index 92681ca7f24def..41297a4d69ba2d 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -416,10 +416,11 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: - /* - * STOP/SUSPEND trigger is invoked only once when all users of this pipeline have - * been stopped. So, clear the started_count so that the pipeline can be reset - */ + ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, + SOF_IPC4_PIPE_RESET); + if (ret < 0) + goto out; + pipeline->state = SOF_IPC4_PIPE_RESET; swidget->spipe->started_count = 0; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: @@ -623,7 +624,8 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg } case SOF_IPC_TYPE_4: { - struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; + struct snd_sof_pipeline *spipe = swidget->spipe; + struct snd_sof_widget *pipe_widget = spipe->pipe_widget; struct sof_ipc4_pipeline *pipeline = pipe_widget->private; switch (sdai->type) { @@ -631,20 +633,27 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg if (pipeline->use_chain_dma) return &hda_ipc4_chain_dma_ops; + spipe->be_managed_pipeline = true; return &hda_ipc4_dma_ops; case SOF_DAI_INTEL_SSP: if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) return NULL; + + spipe->be_managed_pipeline = true; return &ssp_ipc4_dma_ops; case SOF_DAI_INTEL_DMIC: if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) return NULL; + + spipe->be_managed_pipeline = true; return &dmic_ipc4_dma_ops; case SOF_DAI_INTEL_ALH: if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) return NULL; if (pipeline->use_chain_dma) return &sdw_ipc4_chain_dma_ops; + + spipe->be_managed_pipeline = true; return &sdw_ipc4_dma_ops; default: diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 858402857f1a53..041ec72f1bfab8 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -211,9 +211,12 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream, static int __maybe_unused hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai); struct hdac_ext_stream *hext_stream; struct snd_sof_dev *sdev = dai_to_sdev(substream, cpu_dai); + struct snd_sof_widget *swidget = w->dobj.private; + int ret; if (!ops) { dev_err(cpu_dai->dev, "DAI widget ops not set\n"); @@ -222,9 +225,23 @@ static int __maybe_unused hda_dai_hw_free(struct snd_pcm_substream *substream, hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream); if (!hext_stream) - return 0; + goto free; + + ret = hda_link_dma_cleanup(substream, hext_stream, cpu_dai, true); + if (ret < 0) + return ret; + +free: + if (sof_is_widget_pipeline_be_managed(w)) { + ret = snd_sof_free_be_pipeline(w, substream->stream); + if (ret < 0) + return ret; + } + + if (sof_is_widget_pipeline_be_managed(w)) + snd_sof_unprepare_widgets_in_pipeline(w, swidget->spipe, substream->stream); - return hda_link_dma_cleanup(substream, hext_stream, cpu_dai, true); + return 0; } static int __maybe_unused hda_dai_hw_params_data(struct snd_pcm_substream *substream, @@ -316,14 +333,31 @@ static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, i switch (cmd) { case SNDRV_PCM_TRIGGER_STOP: + ret = hda_link_dma_cleanup(substream, hext_stream, dai, false); + if (ret < 0) { + dev_err(sdev->dev, "%s: failed to clean up link DMA during stop\n", + __func__); + return ret; + } + break; case SNDRV_PCM_TRIGGER_SUSPEND: - ret = hda_link_dma_cleanup(substream, hext_stream, dai, - cmd == SNDRV_PCM_TRIGGER_STOP ? false : true); + { + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream); + + ret = hda_link_dma_cleanup(substream, hext_stream, dai, true); if (ret < 0) { - dev_err(sdev->dev, "%s: failed to clean up link DMA\n", __func__); + dev_err(sdev->dev, "%s: failed to clean up link DMA during suspend\n", + __func__); return ret; } + + if (sof_is_widget_pipeline_be_managed(w)) { + ret = snd_sof_free_be_pipeline(w, substream->stream); + if (ret < 0) + return ret; + } break; + } default: break; } @@ -336,9 +370,33 @@ static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, i static int hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai); + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream); + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_sof_dev *sdev = widget_to_sdev(w); int stream = substream->stream; + int ret; + + if (!ops) { + dev_err(sdev->dev, "DAI widget ops not set\n"); + return -EINVAL; + } + + /* if this is a prepare without a hw_free or a suspend, free the BE pipeline */ + if (swidget->spipe->complete && sof_is_widget_pipeline_be_managed(w)) { + ret = snd_sof_free_be_pipeline(w, stream); + if (ret < 0) + return ret; + } + + ret = hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, dai); + if (ret < 0) + return ret; + + if (sof_is_widget_pipeline_be_managed(w)) + return snd_sof_set_up_be_pipeline(w, stream); - return hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, dai); + return 0; } static const struct snd_soc_dai_ops hda_dai_ops = { @@ -460,10 +518,27 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream, static int non_hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_sof_widget *swidget = w->dobj.private; int stream = substream->stream; + int ret; + + /* if this is a prepare without a hw_free or a suspend, free the BE pipeline */ + if (swidget->spipe->complete && sof_is_widget_pipeline_be_managed(w)) { + ret = snd_sof_free_be_pipeline(w, stream); + if (ret < 0) + return ret; + } - return non_hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, cpu_dai); + ret = non_hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, cpu_dai); + if (ret < 0) + return ret; + + if (sof_is_widget_pipeline_be_managed(w)) + return snd_sof_set_up_be_pipeline(w, stream); + + return 0; } static const struct snd_soc_dai_ops ssp_dai_ops = { @@ -641,7 +716,32 @@ EXPORT_SYMBOL_NS(sdw_hda_dai_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON"); int sdw_hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai, int link_id, int intel_alh_id) { - return sdw_hda_dai_hw_params(substream, params, cpu_dai, link_id, intel_alh_id); + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); + const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai); + struct snd_sof_widget *swidget = w->dobj.private; + int stream = substream->stream; + int ret; + + if (!ops) { + dev_err(cpu_dai->dev, "DAI widget ops not set\n"); + return -EINVAL; + } + + /* if this is a prepare without a hw_free or a suspend, free the BE pipeline */ + if (swidget->spipe->complete && sof_is_widget_pipeline_be_managed(w)) { + ret = snd_sof_free_be_pipeline(w, stream); + if (ret < 0) + return ret; + } + + ret = sdw_hda_dai_hw_params(substream, params, cpu_dai, link_id, intel_alh_id); + if (ret < 0) + return ret; + + if (sof_is_widget_pipeline_be_managed(w)) + return snd_sof_set_up_be_pipeline(w, stream); + + return 0; } EXPORT_SYMBOL_NS(sdw_hda_dai_prepare, "SND_SOC_SOF_INTEL_HDA_COMMON"); @@ -670,10 +770,11 @@ static int hda_dai_suspend(struct hdac_bus *bus) struct snd_soc_dai *cpu_dai; struct snd_sof_dev *sdev; struct snd_sof_dai *sdai; + int dir = s->direction; rtd = snd_soc_substream_to_rtd(hext_stream->link_substream); cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); - w = snd_soc_dai_get_widget(cpu_dai, hdac_stream(hext_stream)->direction); + w = snd_soc_dai_get_widget(cpu_dai, dir); swidget = w->dobj.private; sdev = widget_to_sdev(w); sdai = swidget->private; @@ -696,6 +797,12 @@ static int hda_dai_suspend(struct hdac_bus *bus) hext_stream, cpu_dai, true); if (ret < 0) return ret; + + if (sof_is_widget_pipeline_be_managed(w)) { + ret = snd_sof_free_be_pipeline(w, dir); + if (ret < 0) + return ret; + } } } diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 5ec10909403180..2b46fa9ccb3db4 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -138,7 +138,7 @@ sof_ipc4_add_pipeline_to_trigger_list(struct snd_sof_dev *sdev, int state, struct snd_sof_widget *pipe_widget = spipe->pipe_widget; struct sof_ipc4_pipeline *pipeline = pipe_widget->private; - if (pipeline->skip_during_fe_trigger && state != SOF_IPC4_PIPE_RESET) + if (pipeline->skip_during_fe_trigger) return; switch (state) { @@ -177,7 +177,7 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd, struct sof_ipc4_pipeline *pipeline = pipe_widget->private; int i; - if (pipeline->skip_during_fe_trigger && state != SOF_IPC4_PIPE_RESET) + if (pipeline->skip_during_fe_trigger) return; /* set state for pipeline if it was just triggered */ diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 02a2c075bbc70f..b9467c2c00c00e 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1444,7 +1444,6 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) /* reset pipeline memory usage */ pipe_widget = swidget->spipe->pipe_widget; pipeline = pipe_widget->private; - pipeline->mem_usage = 0; if (WIDGET_IS_AIF(swidget->id) || swidget->id == snd_soc_dapm_buffer) { if (pipeline->use_chain_dma) { diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index fc7daa4e9aa325..1338c1d9984904 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -292,6 +292,18 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc return 0; } +/* helper function to check if the widget's pipeline is BE managed */ +bool sof_is_widget_pipeline_be_managed(struct snd_soc_dapm_widget *w) +{ + struct snd_sof_widget *swidget = w->dobj.private; + + if (swidget->spipe->be_managed_pipeline) + return true; + + return false; +} +EXPORT_SYMBOL(sof_is_widget_pipeline_be_managed); + static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_list *list, int dir) { @@ -318,6 +330,12 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, continue; if (p->sink->dobj.private) { + /* + * skip routes between widgets belonging to the BE pipeline + */ + if (sof_is_widget_pipeline_be_managed(widget) && + sof_is_widget_pipeline_be_managed(p->sink)) + continue; ret = sof_route_setup(sdev, widget, p->sink); if (ret < 0) return ret; @@ -334,6 +352,12 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, continue; if (p->source->dobj.private) { + /* + * skip routes between widgets belonging to the BE pipeline + */ + if (sof_is_widget_pipeline_be_managed(widget) && + sof_is_widget_pipeline_be_managed(p->source)) + continue; ret = sof_route_setup(sdev, p->source, widget); if (ret < 0) return ret; @@ -414,8 +438,12 @@ sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widg if (is_virtual_widget(sdev, widget, __func__)) return; - /* skip if the widget is in use or if it is already unprepared */ - if (!swidget || !swidget->prepared || swidget->use_count > 0) + /* + * skip if the widget is in use or if it is already unprepared or + * if it belongs to a BE pipeline. + */ + if (!swidget || !swidget->prepared || swidget->use_count > 0 || + sof_is_widget_pipeline_be_managed(widget)) goto sink_unprepare; widget_ops = tplg_ops ? tplg_ops->widget : NULL; @@ -505,6 +533,7 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap int dir, struct snd_sof_pcm *spcm) { struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; + struct snd_sof_widget *swidget = widget->dobj.private; struct snd_soc_dapm_path *p; int err; int ret = 0; @@ -512,7 +541,8 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap if (is_virtual_widget(sdev, widget, __func__)) return 0; - if (widget->dobj.private) { + /* only free widgets that aren't part of the BE pipeline */ + if (swidget && !sof_is_widget_pipeline_be_managed(widget)) { err = sof_widget_free(sdev, widget->dobj.private); if (err < 0) ret = err; @@ -554,7 +584,7 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d if (is_virtual_widget(sdev, widget, __func__)) return 0; - if (swidget) { + if (swidget && !sof_is_widget_pipeline_be_managed(widget)) { int i; ret = sof_widget_setup(sdev, widget->dobj.private); @@ -593,7 +623,7 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d ret = sof_set_up_widgets_in_path(sdev, p->sink, dir, spcm); p->walking = false; if (ret < 0) { - if (swidget) + if (swidget && !sof_is_widget_pipeline_be_managed(widget)) sof_widget_free(sdev, swidget); return ret; } diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index d078f535abdb6b..a72de717f2d47e 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -502,6 +502,7 @@ struct snd_sof_widget { pipeline * @complete: flag used to indicate that pipeline set up is complete. * @core_mask: Mask containing target cores for all modules in the pipeline + * @be_managed_pipeline: Flag indicating that the pipeline is managed by the BE DAI ops * @list: List item in sdev pipeline_list */ struct snd_sof_pipeline { @@ -510,6 +511,7 @@ struct snd_sof_pipeline { int paused_count; int complete; unsigned long core_mask; + bool be_managed_pipeline; struct list_head list; }; @@ -685,6 +687,7 @@ int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum so u32 vol_compute_gain(u32 value, int *tlv); /* pipeline management */ +bool sof_is_widget_pipeline_be_managed(struct snd_soc_dapm_widget *w); int snd_sof_set_up_be_pipeline(struct snd_soc_dapm_widget *w, int dir); int snd_sof_free_be_pipeline(struct snd_soc_dapm_widget *w, int dir); void snd_sof_unprepare_widgets_in_pipeline(struct snd_soc_dapm_widget *w, From 3a625fbb1feff397049707580113b88189fc9486 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 3 Dec 2024 19:34:17 -0800 Subject: [PATCH 6/6] ASoC: SOF: ipc4-topology: Remove the skip_during_fe_trigger flag Now that all widgets/routes/pipelines are setup, triggered and freed in the BE DAI ops, we can reuse the be_pipeline flag in struct snd_sof_pipeline to skip triggering the BE pipelines in the PCM trigger. So remove the skip_during_fe_trigger flag from struct sof_ipc4_pipeline. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/intel/hda-dai-ops.c | 13 ------------- sound/soc/sof/ipc4-pcm.c | 12 +++++------- sound/soc/sof/ipc4-topology.h | 2 -- 3 files changed, 5 insertions(+), 22 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index 41297a4d69ba2d..7dc13e104c74fc 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -126,19 +126,6 @@ static struct hdac_ext_stream *hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream) { - struct snd_sof_widget *pipe_widget; - struct sof_ipc4_pipeline *pipeline; - struct snd_sof_widget *swidget; - struct snd_soc_dapm_widget *w; - - w = snd_soc_dai_get_widget(cpu_dai, substream->stream); - swidget = w->dobj.private; - pipe_widget = swidget->spipe->pipe_widget; - pipeline = pipe_widget->private; - - /* mark pipeline so that it can be skipped during FE trigger */ - pipeline->skip_during_fe_trigger = true; - return snd_soc_dai_get_dma_data(cpu_dai, substream); } diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 2b46fa9ccb3db4..b4446e8f2b462f 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -136,9 +136,8 @@ sof_ipc4_add_pipeline_to_trigger_list(struct snd_sof_dev *sdev, int state, s8 *pipe_priority) { struct snd_sof_widget *pipe_widget = spipe->pipe_widget; - struct sof_ipc4_pipeline *pipeline = pipe_widget->private; - if (pipeline->skip_during_fe_trigger) + if (spipe->be_managed_pipeline) return; switch (state) { @@ -177,7 +176,7 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd, struct sof_ipc4_pipeline *pipeline = pipe_widget->private; int i; - if (pipeline->skip_during_fe_trigger) + if (spipe->be_managed_pipeline) return; /* set state for pipeline if it was just triggered */ @@ -431,10 +430,9 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, * 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 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. + * Skip the backend pipelines. 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. */ if (state == SOF_IPC4_PIPE_RUNNING || state == SOF_IPC4_PIPE_RESET) for (i = pipeline_list->count - 1; i >= 0; i--) { diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index f4dc499c0ffe55..9914df1f577af7 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -129,7 +129,6 @@ struct sof_ipc4_copier_config_set_sink_format { * @state: Pipeline state * @use_chain_dma: flag to indicate if the firmware shall use chained DMA * @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; @@ -139,7 +138,6 @@ struct sof_ipc4_pipeline { int state; bool use_chain_dma; struct sof_ipc4_msg msg; - bool skip_during_fe_trigger; }; /**