From 6611d377ddc96e79b1e33ddf83b06cf0a6895f63 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 3 Dec 2024 17:35:38 -0800 Subject: [PATCH 1/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 108cad04879eac..fc1999fae695a2 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -1043,12 +1043,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 afb0acb4e3dc4e..b9f90af9e9ca1d 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -989,3 +989,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 41456d28f62817..a6a151d18a136f 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -684,4 +684,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 c84ba9aae8c5d19c8ce1a81f4181e11adf279ef7 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 4 Dec 2024 19:29:29 -0800 Subject: [PATCH 2/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 5b31e1f69591da..823547a1b01784 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -594,6 +594,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, @@ -730,8 +751,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 9c943500953712..e76a4e2814cf52 100644 --- a/include/linux/soundwire/sdw_intel.h +++ b/include/linux/soundwire/sdw_intel.h @@ -223,6 +223,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 883d0d3bae9ec2..4a5c9c037f368f 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 96f00adc89091f..179b5bd915c8a4 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -113,6 +113,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) { @@ -128,6 +136,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 fc1999fae695a2..9ff1dfc4896f51 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -902,6 +902,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 bec412359f130b2e87fd30de73736d0ed12146f0 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 3 Dec 2024 18:54:52 -0800 Subject: [PATCH 3/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 4a5c9c037f368f..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); + { + 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 c09b424ab863d7..096ad4069b91a9 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 f292e50db85945..3175440998cdfa 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1510,7 +1510,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 b9f90af9e9ca1d..7d0dd62bf6b463 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 a6a151d18a136f..00089df68b668f 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -503,6 +503,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 { @@ -511,6 +512,7 @@ struct snd_sof_pipeline { int paused_count; int complete; unsigned long core_mask; + bool be_managed_pipeline; struct list_head list; }; @@ -686,6 +688,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 49d1bf64333f1a50b0a532251855a6b157174192 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 3 Dec 2024 19:34:17 -0800 Subject: [PATCH 4/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 096ad4069b91a9..6f265779e89e27 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 9e98cac19532b1..b2aefe434ede8f 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; }; /** From 9be436d9a2eb23b52f2eaaa2be9b308e4138d8f9 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Fri, 11 Apr 2025 09:24:19 -0700 Subject: [PATCH 5/6] ASoC: SOF: ipc4-topology: reset pipeline mem_usage during unprepare The mem_usage for a pipeline is updated when the widgets in the pipeline are prepare. So, reset it when a widget in the pipeline is unprepared. The ipc_unprepare for the pipeline widget might be called multiple times when each widget associated with the pipeline is unprepared and it is harmless. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/ipc4-topology.c | 11 +++++++++-- sound/soc/sof/sof-audio.c | 16 ++++++++++++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 3175440998cdfa..a7e78687ce5c4f 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1501,6 +1501,14 @@ static int sof_ipc4_init_input_audio_fmt(struct snd_sof_dev *sdev, return i; } +static void sof_ipc4_unprepare_pipeline_module(struct snd_sof_widget *swidget) +{ + struct sof_ipc4_pipeline *pipeline = swidget->private; + + /* reset pipeline memory usage */ + pipeline->mem_usage = 0; +} + static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) { struct sof_ipc4_copier *ipc4_copier = NULL; @@ -3011,7 +3019,6 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget dev_err(sdev->dev, "failed to free pipeline widget %s\n", swidget->widget->name); - pipeline->mem_usage = 0; pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED; ida_free(&pipeline_ida, swidget->instance_id); swidget->instance_id = -EINVAL; @@ -3637,7 +3644,7 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TY [snd_soc_dapm_scheduler] = {sof_ipc4_widget_setup_comp_pipeline, sof_ipc4_widget_free_comp_pipeline, pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL, - NULL, NULL}, + NULL, sof_ipc4_unprepare_pipeline_module}, [snd_soc_dapm_pga] = {sof_ipc4_widget_setup_comp_pga, sof_ipc4_widget_free_comp_pga, pga_token_list, ARRAY_SIZE(pga_token_list), NULL, sof_ipc4_prepare_gain_module, diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 7d0dd62bf6b463..b0a1a9d2faf7eb 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -447,10 +447,16 @@ sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widg goto sink_unprepare; widget_ops = tplg_ops ? tplg_ops->widget : NULL; - if (widget_ops && widget_ops[widget->id].ipc_unprepare) + if (widget_ops && widget_ops[widget->id].ipc_unprepare) { + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; + /* unprepare the source widget */ widget_ops[widget->id].ipc_unprepare(swidget); + /* unprepare the widget's pipeline widget */ + widget_ops[pipe_widget->id].ipc_unprepare(pipe_widget); + } + swidget->prepared = false; sink_unprepare: @@ -1085,10 +1091,16 @@ void snd_sof_unprepare_widgets_in_pipeline(struct snd_soc_dapm_widget *w, goto unprepare; widget_ops = tplg_ops ? tplg_ops->widget : NULL; - if (widget_ops && widget_ops[w->id].ipc_unprepare) + if (widget_ops && widget_ops[w->id].ipc_unprepare) { + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; + /* unprepare the source widget */ widget_ops[w->id].ipc_unprepare(swidget); + /* unprepare the widget's pipeline widget */ + widget_ops[pipe_widget->id].ipc_unprepare(pipe_widget); + } + swidget->prepared = false; unprepare: From 734dfed154e2b1abdf14b763500273fdb883b01c Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 15 Apr 2025 17:17:31 -0700 Subject: [PATCH 6/6] Fix for IPC timeouts squash me with earlier commits Signed-off-by: Ranjani Sridharan --- sound/soc/sof/intel/hda-dai.c | 27 ++++++++---- sound/soc/sof/sof-audio.c | 83 +++++++++++++++++++++++------------ 2 files changed, 74 insertions(+), 36 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 041ec72f1bfab8..897c0c5a3d76c7 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -232,7 +232,7 @@ static int __maybe_unused hda_dai_hw_free(struct snd_pcm_substream *substream, return ret; free: - if (sof_is_widget_pipeline_be_managed(w)) { + if (swidget->spipe->complete && sof_is_widget_pipeline_be_managed(w)) { ret = snd_sof_free_be_pipeline(w, substream->stream); if (ret < 0) return ret; @@ -294,6 +294,7 @@ static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream, static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream); const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai); struct hdac_ext_stream *hext_stream; struct snd_sof_dev *sdev; @@ -309,6 +310,14 @@ static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, i sdev = dai_to_sdev(substream, dai); + /* For playback, set up the widgets first */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + cmd == SNDRV_PCM_TRIGGER_START) { + ret = snd_sof_set_up_be_pipeline(w, substream->stream); + if (ret < 0) + return ret; + } + hext_stream = ops->get_hext_stream(sdev, dai, substream); if (!hext_stream) return -EINVAL; @@ -393,8 +402,9 @@ static int hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_d if (ret < 0) return ret; - if (sof_is_widget_pipeline_be_managed(w)) - return snd_sof_set_up_be_pipeline(w, stream); + /* set up the widgets in the case of capture */ + if (stream == SNDRV_PCM_STREAM_CAPTURE && sof_is_widget_pipeline_be_managed(w)) + return snd_sof_set_up_be_pipeline(w, stream); return 0; } @@ -535,9 +545,9 @@ static int non_hda_dai_prepare(struct snd_pcm_substream *substream, if (ret < 0) return ret; - if (sof_is_widget_pipeline_be_managed(w)) - return snd_sof_set_up_be_pipeline(w, stream); - + /* set up the widgets in the case of capture */ + if (stream == SNDRV_PCM_STREAM_CAPTURE && sof_is_widget_pipeline_be_managed(w)) + return snd_sof_set_up_be_pipeline(w, stream); return 0; } @@ -738,8 +748,9 @@ int sdw_hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_pcm_hw_p if (ret < 0) return ret; - if (sof_is_widget_pipeline_be_managed(w)) - return snd_sof_set_up_be_pipeline(w, stream); + /* set up the widgets in the case of capture */ + if (stream == SNDRV_PCM_STREAM_CAPTURE && sof_is_widget_pipeline_be_managed(w)) + return snd_sof_set_up_be_pipeline(w, stream); return 0; } diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index b0a1a9d2faf7eb..ffa202fc4495b5 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -331,9 +331,9 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, if (p->sink->dobj.private) { /* - * skip routes between widgets belonging to the BE pipeline + * skip routes with widgets belonging to the BE pipeline */ - if (sof_is_widget_pipeline_be_managed(widget) && + 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); @@ -1026,6 +1026,32 @@ int sof_dai_get_tdm_slots(struct snd_soc_pcm_runtime *rtd) } EXPORT_SYMBOL(sof_dai_get_tdm_slots); +static struct snd_soc_dapm_widget *snd_sof_get_pipeline_source(struct snd_sof_pipeline *spipe, + struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_path *p; + + 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; + struct snd_soc_dapm_widget *source_widget; + + p->walking = true; + if (source_swidget->spipe != spipe) { + p->walking = false; + break; + } + + source_widget = snd_sof_get_pipeline_source(spipe, wsource); + p->walking = false; + return source_widget; + } + } + + return w; +} + static int snd_sof_set_up_widgets_in_pipeline(struct snd_soc_dapm_widget *w, struct snd_sof_pipeline *spipe, int dir) { @@ -1042,27 +1068,17 @@ static int snd_sof_set_up_widgets_in_pipeline(struct snd_soc_dapm_widget *w, 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; + if (dir == SNDRV_PCM_STREAM_PLAYBACK && WIDGET_IS_DAI(swidget->id)) + return 0; - 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; + 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; - } + ret = snd_sof_set_up_widgets_in_pipeline(p->sink, spipe, dir); + p->walking = false; + if (ret < 0) + goto err; } } @@ -1185,15 +1201,19 @@ static int snd_sof_set_up_routes_in_pipeline(struct snd_soc_dapm_widget *w, 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) { + if (source_swidget->use_count > 0) { + ret = sof_route_setup(sdev, wsource, w); + if (ret < 0) { + p->walking = false; + return ret; + } + } + + if (source_swidget->spipe != spipe) { p->walking = false; - return ret; + continue; } ret = snd_sof_set_up_routes_in_pipeline(wsource, spipe, dir); @@ -1234,10 +1254,17 @@ 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); + struct snd_soc_dapm_widget *wsource; int ret; + /* get the source widget for the BE pipeline */ + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + wsource = snd_sof_get_pipeline_source(swidget->spipe, w); + else + wsource = w; + /* set up the widgets in the BE pipeline */ - ret = snd_sof_set_up_widgets_in_pipeline(w, swidget->spipe, dir); + ret = snd_sof_set_up_widgets_in_pipeline(wsource, 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);