diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index e305c6258ca967..4a2e01ec2ed644 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -286,6 +286,27 @@ static int intel_params_stream(struct sdw_intel *sdw, return -EIO; } +static int intel_prepare_stream(struct sdw_intel *sdw, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, + struct snd_pcm_hw_params *hw_params, + int link_id, int alh_stream_id) +{ + struct sdw_intel_link_res *res = sdw->link_res; + struct sdw_intel_stream_params_data params_data; + + params_data.substream = substream; + params_data.dai = dai; + params_data.hw_params = hw_params; + params_data.link_id = link_id; + params_data.alh_stream_id = alh_stream_id; + + if (res->ops && res->ops->prepare_stream && res->dev) + return res->ops->prepare_stream(res->dev, ¶ms_data); + + return -EIO; +} + static int intel_free_stream(struct sdw_intel *sdw, struct snd_pcm_substream *substream, struct snd_soc_dai *dai, @@ -422,8 +443,8 @@ static int intel_prepare(struct snd_pcm_substream *substream, } /* Inform DSP about PDI stream number */ - return intel_params_stream(sdw, substream, dai, hw_params, sdw->instance, - dai_runtime->pdi->intel_alh_id); + return intel_prepare_stream(sdw, substream, dai, hw_params, sdw->instance, + dai_runtime->pdi->intel_alh_id); } static int diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h index 580086417e4b0e..cedfcd3257b16b 100644 --- a/include/linux/soundwire/sdw_intel.h +++ b/include/linux/soundwire/sdw_intel.h @@ -220,6 +220,8 @@ struct sdw_intel_stream_free_data { struct sdw_intel_ops { int (*params_stream)(struct device *dev, struct sdw_intel_stream_params_data *params_data); + int (*prepare_stream)(struct device *dev, + struct sdw_intel_stream_params_data *params_data); int (*free_stream)(struct device *dev, struct sdw_intel_stream_free_data *free_data); int (*trigger)(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai); diff --git a/sound/soc/sof/intel/hda-dai-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 da12aabc1bb856..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; + } - 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, @@ -316,14 +333,31 @@ static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, i switch (cmd) { case SNDRV_PCM_TRIGGER_STOP: + ret = hda_link_dma_cleanup(substream, hext_stream, dai, false); + if (ret < 0) { + dev_err(sdev->dev, "%s: failed to clean up link DMA during stop\n", + __func__); + return ret; + } + break; case SNDRV_PCM_TRIGGER_SUSPEND: - ret = hda_link_dma_cleanup(substream, hext_stream, dai, - cmd == SNDRV_PCM_TRIGGER_STOP ? false : true); + { + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream); + + ret = hda_link_dma_cleanup(substream, hext_stream, dai, true); if (ret < 0) { - dev_err(sdev->dev, "%s: failed to clean up link DMA\n", __func__); + dev_err(sdev->dev, "%s: failed to clean up link DMA during suspend\n", + __func__); return ret; } + + if (sof_is_widget_pipeline_be_managed(w)) { + ret = snd_sof_free_be_pipeline(w, substream->stream); + if (ret < 0) + return ret; + } break; + } default: break; } @@ -336,9 +370,33 @@ static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, i static int hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai); + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream); + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_sof_dev *sdev = widget_to_sdev(w); int stream = substream->stream; + int ret; + + if (!ops) { + dev_err(sdev->dev, "DAI widget ops not set\n"); + return -EINVAL; + } + + /* if this is a prepare without a hw_free or a suspend, free the BE pipeline */ + if (swidget->spipe->complete && sof_is_widget_pipeline_be_managed(w)) { + ret = snd_sof_free_be_pipeline(w, stream); + if (ret < 0) + return ret; + } + + ret = hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, dai); + if (ret < 0) + return ret; + + if (sof_is_widget_pipeline_be_managed(w)) + return snd_sof_set_up_be_pipeline(w, stream); - return hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, dai); + return 0; } static const struct snd_soc_dai_ops hda_dai_ops = { @@ -460,10 +518,27 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream, static int non_hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_sof_widget *swidget = w->dobj.private; int stream = substream->stream; + int ret; + + /* if this is a prepare without a hw_free or a suspend, free the BE pipeline */ + if (swidget->spipe->complete && sof_is_widget_pipeline_be_managed(w)) { + ret = snd_sof_free_be_pipeline(w, stream); + if (ret < 0) + return ret; + } + + 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 non_hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, cpu_dai); + return 0; } static const struct snd_soc_dai_ops ssp_dai_ops = { @@ -638,6 +713,38 @@ 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; + + 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"); + static int hda_dai_suspend(struct hdac_bus *bus) { struct snd_soc_pcm_runtime *rtd; @@ -663,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; @@ -689,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/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 ee4ccc1a549001..feb9b2c622c4fc 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -894,6 +894,9 @@ int sdw_hda_dai_hw_free(struct snd_pcm_substream *substream, int sdw_hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *cpu_dai); +int sdw_hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai, int link_id, int intel_alh_id); + /* common dai driver */ extern struct snd_soc_dai_driver skl_dai[]; int hda_dsp_dais_suspend(struct snd_sof_dev *sdev); @@ -1039,12 +1042,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 5ec10909403180..b4446e8f2b462f 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -136,9 +136,8 @@ sof_ipc4_add_pipeline_to_trigger_list(struct snd_sof_dev *sdev, int state, s8 *pipe_priority) { struct snd_sof_widget *pipe_widget = spipe->pipe_widget; - struct sof_ipc4_pipeline *pipeline = pipe_widget->private; - if (pipeline->skip_during_fe_trigger && 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 27979a99e283a9..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) { @@ -1796,6 +1795,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 +3570,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/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; }; /** 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..1338c1d9984904 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -292,6 +292,18 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc return 0; } +/* helper function to check if the widget's pipeline is BE managed */ +bool sof_is_widget_pipeline_be_managed(struct snd_soc_dapm_widget *w) +{ + struct snd_sof_widget *swidget = w->dobj.private; + + if (swidget->spipe->be_managed_pipeline) + return true; + + return false; +} +EXPORT_SYMBOL(sof_is_widget_pipeline_be_managed); + static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_list *list, int dir) { @@ -318,6 +330,12 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, continue; if (p->sink->dobj.private) { + /* + * skip routes between widgets belonging to the BE pipeline + */ + if (sof_is_widget_pipeline_be_managed(widget) && + sof_is_widget_pipeline_be_managed(p->sink)) + continue; ret = sof_route_setup(sdev, widget, p->sink); if (ret < 0) return ret; @@ -334,6 +352,12 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, continue; if (p->source->dobj.private) { + /* + * skip routes between widgets belonging to the BE pipeline + */ + if (sof_is_widget_pipeline_be_managed(widget) && + sof_is_widget_pipeline_be_managed(p->source)) + continue; ret = sof_route_setup(sdev, p->source, widget); if (ret < 0) return ret; @@ -414,8 +438,12 @@ sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widg if (is_virtual_widget(sdev, widget, __func__)) return; - /* skip if the widget is in use or if it is already unprepared */ - if (!swidget || !swidget->prepared || swidget->use_count > 0) + /* + * skip if the widget is in use or if it is already unprepared or + * if it belongs to a BE pipeline. + */ + if (!swidget || !swidget->prepared || swidget->use_count > 0 || + sof_is_widget_pipeline_be_managed(widget)) goto sink_unprepare; widget_ops = tplg_ops ? tplg_ops->widget : NULL; @@ -505,6 +533,7 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap int dir, struct snd_sof_pcm *spcm) { struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; + struct snd_sof_widget *swidget = widget->dobj.private; struct snd_soc_dapm_path *p; int err; int ret = 0; @@ -512,7 +541,8 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap if (is_virtual_widget(sdev, widget, __func__)) return 0; - if (widget->dobj.private) { + /* only free widgets that aren't part of the BE pipeline */ + if (swidget && !sof_is_widget_pipeline_be_managed(widget)) { err = sof_widget_free(sdev, widget->dobj.private); if (err < 0) ret = err; @@ -554,7 +584,7 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d if (is_virtual_widget(sdev, widget, __func__)) return 0; - if (swidget) { + if (swidget && !sof_is_widget_pipeline_be_managed(widget)) { int i; ret = sof_widget_setup(sdev, widget->dobj.private); @@ -593,7 +623,7 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d ret = sof_set_up_widgets_in_path(sdev, p->sink, dir, spcm); p->walking = false; if (ret < 0) { - if (swidget) + if (swidget && !sof_is_widget_pipeline_be_managed(widget)) sof_widget_free(sdev, swidget); return ret; } @@ -672,6 +702,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 +740,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 +817,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; @@ -977,3 +1016,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 36ab75e11779d2..a72de717f2d47e 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); @@ -351,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) */ @@ -498,6 +502,7 @@ struct snd_sof_widget { pipeline * @complete: flag used to indicate that pipeline set up is complete. * @core_mask: Mask containing target cores for all modules in the pipeline + * @be_managed_pipeline: Flag indicating that the pipeline is managed by the BE DAI ops * @list: List item in sdev pipeline_list */ struct snd_sof_pipeline { @@ -506,6 +511,7 @@ struct snd_sof_pipeline { int paused_count; int complete; unsigned long core_mask; + bool be_managed_pipeline; struct list_head list; }; @@ -660,6 +666,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); @@ -674,4 +685,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