diff options
-rw-r--r-- | sound/soc/sof/pcm.c | 8 | ||||
-rw-r--r-- | sound/soc/sof/sof-audio.c | 139 | ||||
-rw-r--r-- | sound/soc/sof/sof-audio.h | 21 |
3 files changed, 154 insertions, 14 deletions
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 095ca0d9d5ae..a76d0b5b2ad9 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -84,7 +84,8 @@ EXPORT_SYMBOL(snd_sof_pcm_period_elapsed); static int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd, - struct snd_sof_pcm *spcm, int dir) + struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *params, + struct snd_sof_platform_stream_params *platform_params, int dir) { struct snd_soc_dai *dai; int ret, j; @@ -103,7 +104,7 @@ 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, dir); + ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir); if (ret < 0) { dev_err(sdev->dev, "error: failed widget list set up for pcm %d dir %d\n", spcm->pcm.pcm_id, dir); @@ -159,7 +160,8 @@ 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, substream->stream); + ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, params, &platform_params, + substream->stream); if (ret < 0) return ret; } diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 791d2454c1d5..8d740635a4bb 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -257,6 +257,79 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, return 0; } +static void +sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget) +{ + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget; + struct snd_sof_widget *swidget = widget->dobj.private; + struct snd_soc_dapm_path *p; + + if (!widget_ops[widget->id].ipc_unprepare || !swidget->prepared) + goto sink_unprepare; + + /* unprepare the source widget */ + widget_ops[widget->id].ipc_unprepare(swidget); + swidget->prepared = false; + +sink_unprepare: + /* unprepare all widgets in the sink paths */ + snd_soc_dapm_widget_for_each_sink_path(widget, p) { + if (!p->walking && p->sink->dobj.private) { + p->walking = true; + sof_unprepare_widgets_in_path(sdev, p->sink); + p->walking = false; + } + } +} + +static int +sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, + struct snd_pcm_hw_params *fe_params, + struct snd_sof_platform_stream_params *platform_params, + struct snd_pcm_hw_params *pipeline_params, int dir) +{ + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget; + struct snd_sof_widget *swidget = widget->dobj.private; + struct snd_soc_dapm_path *p; + int ret; + + if (!widget_ops[widget->id].ipc_prepare || swidget->prepared) + goto sink_prepare; + + /* prepare the source widget */ + ret = widget_ops[widget->id].ipc_prepare(swidget, fe_params, platform_params, + pipeline_params, dir); + if (ret < 0) { + dev_err(sdev->dev, "failed to prepare widget %s\n", widget->name); + return ret; + } + + swidget->prepared = true; + +sink_prepare: + /* prepare all widgets in the sink paths */ + snd_soc_dapm_widget_for_each_sink_path(widget, p) { + if (!p->walking && p->sink->dobj.private) { + p->walking = true; + ret = sof_prepare_widgets_in_path(sdev, p->sink, fe_params, + platform_params, pipeline_params, dir); + p->walking = false; + if (ret < 0) { + /* unprepare the source widget */ + if (!widget_ops[widget->id].ipc_unprepare && swidget->prepared) { + widget_ops[widget->id].ipc_unprepare(swidget); + swidget->prepared = false; + } + return ret; + } + } + } + + return 0; +} + /* * free all widgets in the sink path starting from the source widget * (DAI type for capture, AIF type for playback) @@ -336,11 +409,15 @@ out: } static int -sof_setup_or_free_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_list *list, - bool dir, enum sof_widget_op op) +sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_list *list, + struct snd_pcm_hw_params *fe_params, + struct snd_sof_platform_stream_params *platform_params, int dir, + enum sof_widget_op op) { struct snd_soc_dapm_widget *widget; - int ret, i; + char *str; + int ret = 0; + int i; for_each_dapm_widgets(list, i, widget) { /* starting widget for playback is AIF type */ @@ -354,17 +431,38 @@ sof_setup_or_free_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm switch (op) { case SOF_WIDGET_SETUP: ret = sof_set_up_widgets_in_path(sdev, widget, dir); + str = "set up"; break; case SOF_WIDGET_FREE: ret = sof_free_widgets_in_path(sdev, widget, dir); + str = "free"; + break; + case SOF_WIDGET_PREPARE: + { + struct snd_pcm_hw_params pipeline_params; + + str = "prepare"; + /* + * When walking the list of connected widgets, the pipeline_params for each + * widget is modified by the source widget in the path. Use a local + * copy of the runtime params as the pipeline_params so that the runtime + * params does not get overwritten. + */ + memcpy(&pipeline_params, fe_params, sizeof(*fe_params)); + + ret = sof_prepare_widgets_in_path(sdev, widget, fe_params, + platform_params, &pipeline_params, dir); + break; + } + case SOF_WIDGET_UNPREPARE: + sof_unprepare_widgets_in_path(sdev, widget); break; default: dev_err(sdev->dev, "Invalid widget op %d\n", op); return -EINVAL; } if (ret < 0) { - dev_err(sdev->dev, "Failed to %s connected widgets\n", - op == SOF_WIDGET_SETUP ? "set up" : "free"); + dev_err(sdev->dev, "Failed to %s connected widgets\n", str); return ret; } } @@ -372,7 +470,10 @@ sof_setup_or_free_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm return 0; } -int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir) +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) { const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; @@ -383,10 +484,24 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, in if (!list) return 0; - ret = sof_setup_or_free_widgets_in_order(sdev, list, dir, SOF_WIDGET_SETUP); + /* + * 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, list, 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, list, fe_params, platform_params, + dir, SOF_WIDGET_SETUP); + if (ret < 0) { + ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, + dir, SOF_WIDGET_UNPREPARE); + return ret; + } + /* * error in setting pipeline connections will result in route status being reset for * routes that were successfully set up when the widgets are freed. @@ -426,7 +541,9 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, in return 0; widget_free: - sof_setup_or_free_widgets_in_order(sdev, list, dir, SOF_WIDGET_FREE); + sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, dir, + SOF_WIDGET_FREE); + sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE); return ret; } @@ -440,7 +557,11 @@ int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int if (!list) return 0; - ret = sof_setup_or_free_widgets_in_order(sdev, list, dir, SOF_WIDGET_FREE); + /* send IPC to free widget in the DSP */ + ret = sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_FREE); + + /* unprepare the widget */ + sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE); snd_soc_dapm_dai_free_widgets(&list); spcm->stream[dir].list = NULL; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 4dbee02424fe..27cc5fb642e5 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -36,8 +36,10 @@ #define SOF_DAI_CLK_INTEL_SSP_BCLK 1 enum sof_widget_op { - SOF_WIDGET_FREE, + SOF_WIDGET_PREPARE, SOF_WIDGET_SETUP, + SOF_WIDGET_FREE, + SOF_WIDGET_UNPREPARE, }; /* @@ -129,6 +131,8 @@ struct sof_ipc_tplg_control_ops { * @token_list: List of token ID's that should be parsed for the widget * @token_list_size: number of elements in token_list * @bind_event: Function pointer for binding events to the widget + * @ipc_prepare: Optional op for preparing a widget for set up + * @ipc_unprepare: Optional op for unpreparing a widget */ struct sof_ipc_tplg_widget_ops { int (*ipc_setup)(struct snd_sof_widget *swidget); @@ -137,6 +141,11 @@ struct sof_ipc_tplg_widget_ops { int token_list_size; int (*bind_event)(struct snd_soc_component *scomp, struct snd_sof_widget *swidget, u16 event_type); + int (*ipc_prepare)(struct snd_sof_widget *swidget, + struct snd_pcm_hw_params *fe_params, + struct snd_sof_platform_stream_params *platform_params, + struct snd_pcm_hw_params *source_params, int dir); + void (*ipc_unprepare)(struct snd_sof_widget *swidget); }; /** @@ -332,6 +341,11 @@ struct snd_sof_widget { * widgets. It is unused for all other widget types. */ int complete; + /* + * the prepared flag is used to indicate that a widget has been prepared for getting set + * up in the DSP. + */ + bool prepared; int use_count; /* use_count will be protected by the PCM mutex held by the core */ int core; int id; /* id is the DAPM widget type */ @@ -491,7 +505,10 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc struct snd_soc_dapm_widget *wsink); /* PCM */ -int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); +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_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); |