From 85c69ce13f1e97246b11f64a2e3af1d1b36f925c Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 7 Dec 2024 12:09:17 -0800 Subject: [PATCH 1/7] 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/7] 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 736d252e8924a8c8fc0a891e529578e7c11ddcdb Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 3 Dec 2024 17:36:24 -0800 Subject: [PATCH 3/7] ASoC: SOF: sof-audio: expose sof_route_setup() This will be used in the BE DAI ops in the following patches. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/sof-audio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index b21818942d84e6..51dae081056bd3 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -291,6 +291,7 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc sroute->setup = true; return 0; } +EXPORT_SYMBOL(sof_route_setup); static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_list *list, int dir) From 0fc6909bf1e843893b1bcbf111e97de259bd57aa Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 3 Dec 2024 17:35:38 -0800 Subject: [PATCH 4/7] ASoC: SOF: Intel: hda-dai-ops: Add new BE DAI DMA ops Define 3 new ops in struct hda_dai_widget_dma_ops for setting up/freeing/unpreparing the BE pipeline and set them for IPC4 version of the ops. These ops 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-dai-ops.c | 254 ++++++++++++++++++++++++++++++ sound/soc/sof/intel/hda.h | 7 + 2 files changed, 261 insertions(+) diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index 92681ca7f24def..14a3f072594062 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -434,6 +434,248 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c return ret; } +static int hda_dai_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 = hda_dai_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 = hda_dai_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; +} + +static void +hda_dai_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; + hda_dai_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; + hda_dai_unprepare_widgets_in_pipeline(p->sink, spipe, dir); + p->walking = false; + } + } + } +} + +static int hda_dai_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 = hda_dai_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 = hda_dai_free_widgets_in_pipeline(p->sink, spipe, dir); + p->walking = false; + if (err < 0) + ret = err; + } + } + } + + return ret; +} + +static int hda_dai_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 = hda_dai_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 = hda_dai_set_up_routes_in_pipeline(wsink, spipe, dir); + p->walking = false; + if (ret < 0) + return ret; + } + } + } + + return 0; +} + +static int hda_ipc4_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 = hda_dai_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 = hda_dai_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); + + swidget->spipe->complete = 1; + return ret; +} + +static int hda_ipc4_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 = hda_dai_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; +} + static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = { .get_hext_stream = hda_ipc4_get_hext_stream, .assign_hext_stream = hda_assign_hext_stream, @@ -446,6 +688,9 @@ static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = { .codec_dai_set_stream = hda_codec_dai_set_stream, .calc_stream_format = hda_calc_stream_format, .get_hlink = hda_get_hlink, + .set_up_be_pipeline = hda_ipc4_set_up_be_pipeline, + .free_be_pipeline = hda_ipc4_free_be_pipeline, + .unprepare_be_pipeline = hda_dai_unprepare_widgets_in_pipeline, }; static const struct hda_dai_widget_dma_ops ssp_ipc4_dma_ops = { @@ -459,6 +704,9 @@ static const struct hda_dai_widget_dma_ops ssp_ipc4_dma_ops = { .post_trigger = hda_ipc4_post_trigger, .calc_stream_format = generic_calc_stream_format, .get_hlink = ssp_get_hlink, + .set_up_be_pipeline = hda_ipc4_set_up_be_pipeline, + .free_be_pipeline = hda_ipc4_free_be_pipeline, + .unprepare_be_pipeline = hda_dai_unprepare_widgets_in_pipeline, }; static const struct hda_dai_widget_dma_ops dmic_ipc4_dma_ops = { @@ -472,6 +720,9 @@ static const struct hda_dai_widget_dma_ops dmic_ipc4_dma_ops = { .post_trigger = hda_ipc4_post_trigger, .calc_stream_format = dmic_calc_stream_format, .get_hlink = dmic_get_hlink, + .set_up_be_pipeline = hda_ipc4_set_up_be_pipeline, + .free_be_pipeline = hda_ipc4_free_be_pipeline, + .unprepare_be_pipeline = hda_dai_unprepare_widgets_in_pipeline, }; static const struct hda_dai_widget_dma_ops sdw_ipc4_dma_ops = { @@ -485,6 +736,9 @@ static const struct hda_dai_widget_dma_ops sdw_ipc4_dma_ops = { .post_trigger = hda_ipc4_post_trigger, .calc_stream_format = generic_calc_stream_format, .get_hlink = sdw_get_hlink, + .set_up_be_pipeline = hda_ipc4_set_up_be_pipeline, + .free_be_pipeline = hda_ipc4_free_be_pipeline, + .unprepare_be_pipeline = hda_dai_unprepare_widgets_in_pipeline, }; static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = { diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index ee4ccc1a549001..21b103873cb38b 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -1005,6 +1005,9 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev, * for HDaudio codec DAI from the .sig bits * @get_hlink: Mandatory function pointer to retrieve hlink, mainly to program LOSIDV * for legacy HDaudio links or program HDaudio Extended Link registers. + * @set_up_be_pipeline: Function pointer to set up the widgets and routes in the BE pipeline + * @free_be_pipeline: Function pointer to free the widgets and routes in the BE pipeline + * @unprepare_be_pipeline: Function pointer to unprepare the widgets in the BE pipeline */ struct hda_dai_widget_dma_ops { struct hdac_ext_stream *(*get_hext_stream)(struct snd_sof_dev *sdev, @@ -1032,6 +1035,10 @@ struct hda_dai_widget_dma_ops { struct snd_pcm_hw_params *params); struct hdac_ext_link * (*get_hlink)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); + int (*set_up_be_pipeline)(struct snd_soc_dapm_widget *w, int dir); + int (*free_be_pipeline)(struct snd_soc_dapm_widget *w, int dir); + void (*unprepare_be_pipeline)(struct snd_soc_dapm_widget *w, + struct snd_sof_pipeline *spipe, int dir); }; const struct hda_dai_widget_dma_ops * From 37bbcbc2eb41b70c014cd835b11aebf37ade7688 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 4 Dec 2024 19:29:29 -0800 Subject: [PATCH 5/7] 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 prepare_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..a8d45266e6e3e3 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 21b103873cb38b..32c8aedf3450de 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 457b14d03c02340aadee42a7acc9bd7895730342 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 3 Dec 2024 18:54:52 -0800 Subject: [PATCH 6/7] ASoC: SOF: Refactor pipeline management Invoke the set_up_be_pipeline and free_be_pipeline ops 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 pipelines i.e. those that contain the DAI widget. Modify the FE ops to make sure that the widgets/routes belonging to these pipelines are skipped during setup/free/unprepare. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/intel/hda-dai-ops.c | 12 ++- 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 | 39 ++++++++-- sound/soc/sof/sof-audio.h | 2 + 6 files changed, 163 insertions(+), 20 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index 14a3f072594062..f20afdb4d7ca86 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: @@ -638,6 +639,9 @@ static int hda_ipc4_set_up_be_pipeline(struct snd_soc_dapm_widget *w, int dir) struct snd_sof_dev *sdev = widget_to_sdev(w); int ret; + /* set the be_pipeline flag true for the pipeline */ + swidget->spipe->be_pipeline = true; + /* set up the widgets in the BE pipeline */ ret = hda_dai_set_up_widgets_in_pipeline(w, swidget->spipe, dir); if (ret < 0) { diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 858402857f1a53..c7625b73d7132f 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 (ops->free_be_pipeline) { + ret = ops->free_be_pipeline(w, substream->stream); + if (ret < 0) + return ret; + } + + if (ops->unprepare_be_pipeline) + ops->unprepare_be_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 (ops->free_be_pipeline) { + ret = ops->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; - return hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, dai); + 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 && ops->free_be_pipeline) { + ret = ops->free_be_pipeline(w, substream->stream); + if (ret < 0) + return ret; + } + + ret = hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, dai); + if (ret < 0) + return ret; + + if (ops && ops->set_up_be_pipeline) + return ops->set_up_be_pipeline(w, substream->stream); + + return ret; } static const struct snd_soc_dai_ops hda_dai_ops = { @@ -460,10 +518,28 @@ 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); + const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai); 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 && ops->free_be_pipeline) { + ret = ops->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 (ops && ops->set_up_be_pipeline) + return ops->set_up_be_pipeline(w, stream); + + return ret; } static const struct snd_soc_dai_ops ssp_dai_ops = { @@ -638,10 +714,36 @@ 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); + 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 && ops->free_be_pipeline) { + ret = ops->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 (ops->set_up_be_pipeline) + return ops->set_up_be_pipeline(w, stream); + + return ret; } EXPORT_SYMBOL_NS(sdw_hda_dai_prepare, "SND_SOC_SOF_INTEL_HDA_COMMON"); @@ -696,6 +798,13 @@ static int hda_dai_suspend(struct hdac_bus *bus) hext_stream, cpu_dai, true); if (ret < 0) return ret; + + if (ops->free_be_pipeline) { + ret = ops->free_be_pipeline(w, + hdac_stream(hext_stream)->direction); + 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 51dae081056bd3..c7caa0e23613d1 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -293,6 +293,17 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc } EXPORT_SYMBOL(sof_route_setup); +/* helper function to check if the widget belongs to a BE pipeline */ +static bool sof_is_be_pipeline_widget(struct snd_soc_dapm_widget *w) +{ + struct snd_sof_widget *swidget = w->dobj.private; + + if (swidget->spipe->be_pipeline) + return true; + + return false; +} + static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_list *list, int dir) { @@ -319,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_be_pipeline_widget(widget) && + sof_is_be_pipeline_widget(p->sink)) + continue; ret = sof_route_setup(sdev, widget, p->sink); if (ret < 0) return ret; @@ -335,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_be_pipeline_widget(widget) && + sof_is_be_pipeline_widget(p->source)) + continue; ret = sof_route_setup(sdev, p->source, widget); if (ret < 0) return ret; @@ -415,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_be_pipeline_widget(widget)) goto sink_unprepare; widget_ops = tplg_ops ? tplg_ops->widget : NULL; @@ -506,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; @@ -513,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_be_pipeline_widget(widget)) { err = sof_widget_free(sdev, widget->dobj.private); if (err < 0) ret = err; @@ -555,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_be_pipeline_widget(widget)) { int i; ret = sof_widget_setup(sdev, widget->dobj.private); @@ -594,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_be_pipeline_widget(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 fab6027a95d805..3da4191ea4f650 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_pipeline: Flag indicating that the pipeline contains a BE DAI widget * @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_pipeline; struct list_head list; }; From f417fc7690334510d6a4de0eaaa534048808bc4a Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 3 Dec 2024 19:34:17 -0800 Subject: [PATCH 7/7] 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 f20afdb4d7ca86..07babae745a8f7 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..35f588c7dc1c5f 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_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_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; }; /**