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-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index 92681ca7f24def..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); } @@ -416,10 +403,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 +611,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 +620,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 883d0d3bae9ec2..897c0c5a3d76c7 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 (swidget->spipe->complete && sof_is_widget_pipeline_be_managed(w)) { + ret = snd_sof_free_be_pipeline(w, substream->stream); + if (ret < 0) + return ret; + } - return hda_link_dma_cleanup(substream, hext_stream, cpu_dai, true); + if (sof_is_widget_pipeline_be_managed(w)) + snd_sof_unprepare_widgets_in_pipeline(w, swidget->spipe, substream->stream); + + return 0; } static int __maybe_unused hda_dai_hw_params_data(struct snd_pcm_substream *substream, @@ -277,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; @@ -292,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; @@ -316,14 +342,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 +379,34 @@ 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 && 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; + + /* 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; } static const struct snd_soc_dai_ops hda_dai_ops = { @@ -460,10 +528,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; + + /* 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; } static const struct snd_soc_dai_ops ssp_dai_ops = { @@ -638,6 +723,39 @@ 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) +{ + 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; + + /* 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; +} +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; @@ -663,10 +781,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; @@ -689,6 +808,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/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 108cad04879eac..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); @@ -1043,12 +1046,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/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index c09b424ab863d7..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 && state != SOF_IPC4_PIPE_RESET) + 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 && state != SOF_IPC4_PIPE_RESET) + 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.c b/sound/soc/sof/ipc4-topology.c index f292e50db85945..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; @@ -1510,7 +1518,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) { @@ -3012,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; @@ -3638,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/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; }; /** diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index afb0acb4e3dc4e..ffa202fc4495b5 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 with 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,15 +438,25 @@ 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; - 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: @@ -505,6 +539,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 +547,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 +590,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 +629,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; } @@ -989,3 +1025,282 @@ 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 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) +{ + 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 && WIDGET_IS_DAI(swidget->id)) + return 0; + + 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) { + 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: + 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; + + p->walking = true; + + 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; + continue; + } + + 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); + 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(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); + 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..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; }; @@ -684,4 +686,19 @@ 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 */ +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, + 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