From 714ad3fabc5e2397daf3160f09f94402538df1f1 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 7 Dec 2024 12:09:17 -0800 Subject: [PATCH 1/2] ASoC: SOF: sof-audio: Add a new op in struct sof_ipc_tplg_ops Add a new host_config op in struct sof_ipc_tplg_ops and define it for IPC4. This will be used to configure the host widget during prepare after a suspend/resume or after an xrun. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/ipc4-topology.c | 20 ++++++++++++++++++++ sound/soc/sof/sof-audio.h | 3 +++ 2 files changed, 23 insertions(+) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index dc9a331c577072..725c2dc58278fd 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1861,6 +1861,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, @@ -3648,4 +3667,5 @@ const struct sof_ipc_tplg_ops ipc4_tplg_ops = { .dai_get_param = sof_ipc4_dai_get_param, .tear_down_all_pipelines = sof_ipc4_tear_down_all_pipelines, .link_setup = sof_ipc4_link_setup, + .host_config = sof_ipc4_host_config, }; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 36ab75e11779d2..fafd967e207080 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -209,6 +209,7 @@ struct sof_ipc_tplg_widget_ops { * @widget_setup: Function pointer for setting up setup in the DSP * @widget_free: Function pointer for freeing widget in the DSP * @dai_config: Function pointer for sending DAI config IPC to the DSP + * @host_config: Function pointer for setting the DMA ID for host widgets * @dai_get_param: Function pointer for getting the DAI parameter * @set_up_all_pipelines: Function pointer for setting up all topology pipelines * @tear_down_all_pipelines: Function pointer for tearing down all topology pipelines @@ -230,6 +231,8 @@ struct sof_ipc_tplg_ops { int (*widget_free)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); int (*dai_config)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, unsigned int flags, struct snd_sof_dai_config_data *data); + void (*host_config)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, + struct snd_sof_platform_stream_params *platform_params); int (*dai_get_param)(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int param_type); int (*set_up_all_pipelines)(struct snd_sof_dev *sdev, bool verify); int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify); From ac18247539ea6979a4196ccf698d77ac22d8cdfd Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 3 Dec 2024 14:17:33 -0800 Subject: [PATCH 2/2] ASoC: SOF: pcm: Split up widget prepare and setup In preparation for refacting pipeline management, split the widget prepare and set up between the hw_params and prepare ioctls. This is required to ensure that the BE pipeline widgets can be set up during the BE DAI prepare and the remaining widgets will be set up during the FE DAI prepare. The widget's ipc_prepare op for all widgets in both the BE and FE pipelines are handled during the FE DAI hw_params to make sure that the pipeline params can be propagated all the way from the source widget to the sink widget. Signed-off-by: Ranjani Sridharan --- sound/soc/sof/pcm.c | 81 +++++++++++++++++++++++++++++++-------- sound/soc/sof/sof-audio.c | 48 ++++++++++++++--------- sound/soc/sof/sof-audio.h | 7 ++++ 3 files changed, 102 insertions(+), 34 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index d584a72e6f52fd..5ee1d30b1f53dd 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -106,9 +106,9 @@ sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_run spcm->stream[dir].list = list; - ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir); + ret = sof_widget_list_prepare(sdev, spcm, params, platform_params, dir); if (ret < 0) { - spcm_err(spcm, dir, "Widget list set up failed\n"); + spcm_err(spcm, dir, "widget list prepare failed\n"); spcm->stream[dir].list = NULL; snd_soc_dapm_dai_free_widgets(&list); return ret; @@ -118,15 +118,30 @@ sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_run return 0; } +static struct snd_sof_widget *snd_sof_find_swidget_by_comp_id(struct snd_sof_dev *sdev, + int comp_id) +{ + struct snd_sof_widget *swidget; + + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (comp_id == swidget->comp_id) + return swidget; + } + + return NULL; +} + static int sof_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); - struct snd_sof_platform_stream_params platform_params = { 0 }; + struct snd_sof_platform_stream_params *platform_params; struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_sof_widget *host_widget; struct snd_sof_pcm *spcm; int ret; @@ -152,7 +167,8 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, spcm->prepared[substream->stream] = false; } - ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params); + platform_params = &spcm->platform_params[substream->stream]; + ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, platform_params); if (ret < 0) { spcm_err(spcm, substream->stream, "platform hw params failed\n"); return ret; @@ -160,12 +176,27 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, /* if this is a repeated hw_params without hw_free, skip setting up widgets */ if (!spcm->stream[substream->stream].list) { - ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, params, &platform_params, + ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, params, platform_params, substream->stream); if (ret < 0) return ret; } + if (!sdev->dspless_mode_selected) { + int host_comp_id = spcm->stream[substream->stream].comp_id; + + host_widget = snd_sof_find_swidget_by_comp_id(sdev, host_comp_id); + if (!host_widget) { + spcm_err(spcm, substream->stream, + "failed to find host widget with comp_id %d\n", host_comp_id); + return -EINVAL; + } + + /* set the host DMA ID */ + if (tplg_ops && tplg_ops->host_config) + tplg_ops->host_config(sdev, host_widget, platform_params); + } + /* create compressed page table for audio firmware */ if (runtime->buffer_changed) { ret = create_page_table(component, substream, runtime->dma_area, @@ -175,14 +206,6 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, return ret; } - if (pcm_ops && pcm_ops->hw_params) { - ret = pcm_ops->hw_params(component, substream, params, &platform_params); - if (ret < 0) - return ret; - } - - spcm->prepared[substream->stream] = true; - /* save pcm hw_params */ memcpy(&spcm->params[substream->stream], params, sizeof(*params)); @@ -287,6 +310,9 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, true); + /* unprepare and free the list of DAPM widgets */ + sof_widget_list_unprepare(sdev, spcm, substream->stream); + cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work); return ret; @@ -297,7 +323,12 @@ static int sof_pcm_prepare(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); + struct snd_sof_platform_stream_params *platform_params; + struct snd_soc_dapm_widget_list *list; + struct snd_pcm_hw_params *params; struct snd_sof_pcm *spcm; + int dir = substream->stream; int ret; /* nothing to do for BE */ @@ -323,15 +354,33 @@ static int sof_pcm_prepare(struct snd_soc_component *component, return ret; } - /* set hw_params */ - ret = sof_pcm_hw_params(component, - substream, &spcm->params[substream->stream]); + ret = sof_pcm_hw_params(component, substream, &spcm->params[substream->stream]); if (ret < 0) { spcm_err(spcm, substream->stream, "failed to set hw_params after resume\n"); return ret; } + list = spcm->stream[dir].list; + params = &spcm->params[substream->stream]; + platform_params = &spcm->platform_params[substream->stream]; + ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir); + if (ret < 0) { + dev_err(sdev->dev, "failed widget list set up for pcm %d dir %d\n", + spcm->pcm.pcm_id, dir); + spcm->stream[dir].list = NULL; + snd_soc_dapm_dai_free_widgets(&list); + return ret; + } + + if (pcm_ops && pcm_ops->hw_params) { + ret = pcm_ops->hw_params(component, substream, params, platform_params); + if (ret < 0) + return ret; + } + + spcm->prepared[substream->stream] = true; + return 0; } diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index a9664b4cf43f90..afb0acb4e3dc4e 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -672,6 +672,30 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, return 0; } +int sof_widget_list_prepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, + struct snd_pcm_hw_params *fe_params, + struct snd_sof_platform_stream_params *platform_params, + int dir) +{ + /* + * Prepare widgets for set up. The prepare step is used to allocate memory, assign + * instance ID and pick the widget configuration based on the runtime PCM params. + */ + return sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, + dir, SOF_WIDGET_PREPARE); +} + +void sof_widget_list_unprepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir) +{ + struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; + + /* unprepare the widget */ + sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE); + + snd_soc_dapm_dai_free_widgets(&list); + spcm->stream[dir].list = NULL; +} + int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *fe_params, struct snd_sof_platform_stream_params *platform_params, @@ -682,19 +706,10 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, struct snd_soc_dapm_widget *widget; int i, ret; - /* nothing to set up */ - if (!list) + /* nothing to set up or setup has been already done */ + if (!list || spcm->setup_done[dir]) 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); @@ -749,6 +764,8 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, } } + spcm->setup_done[dir] = true; + return 0; widget_free: @@ -766,18 +783,13 @@ int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int int ret; /* nothing to free */ - if (!list) + if (!list || !spcm->setup_done[dir]) return 0; /* 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; - + spcm->setup_done[dir] = false; pipeline_list->count = 0; return ret; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index fafd967e207080..41456d28f62817 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -354,7 +354,9 @@ 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 setup_done[2]; /* the setup of the SOF PCM device is done */ bool pending_stop[2]; /* only used if (!pcm_ops->platform_stop_during_hw_free) */ /* Must be last - ends in a flex-array member. */ @@ -663,6 +665,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);