From a1d073c9f5573e6eb492f705d4232cdab050b70e Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 3 Dec 2024 18:54:52 -0800 Subject: [PATCH] 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 | 118 ++++++++++++++++++++++++++++-- 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, 159 insertions(+), 17 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index 40a23d75e6fb02..ea8baa66511cc2 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 3089c1019d2b47..f5f2f832d11745 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -198,9 +198,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"); @@ -209,9 +212,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); + 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); + return 0; } static int __maybe_unused hda_dai_hw_params_data(struct snd_pcm_substream *substream, @@ -303,13 +320,29 @@ 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); + if (ret < 0) { + dev_err(sdev->dev, "%s: failed to clean up link DMA\n", __func__); + return ret; + } + break; case SNDRV_PCM_TRIGGER_SUSPEND: + { + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream); + ret = hda_link_dma_cleanup(substream, hext_stream, dai); if (ret < 0) { dev_err(sdev->dev, "%s: failed to clean up link DMA\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; } @@ -322,9 +355,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 = { @@ -446,10 +503,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; + } + + 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 non_hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, cpu_dai); + return ret; } static const struct snd_soc_dai_ops ssp_dai_ops = { @@ -612,10 +687,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); @@ -671,6 +772,13 @@ static int hda_dai_suspend(struct hdac_bus *bus) cpu_dai); 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 18fff2df76f97e..b1629addb999d7 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 d20c5aafbdeac5..e7eef0c52b4727 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1445,7 +1445,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 7f375c8eada980..ee4512ba04b721 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 || + swidget->spipe->be_pipeline) 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 a8d164c5903d2b..b52353d31ae7b4 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; };