diff options
Diffstat (limited to 'sound/soc/sof/sof-audio.c')
| -rw-r--r-- | sound/soc/sof/sof-audio.c | 280 |
1 files changed, 153 insertions, 127 deletions
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 9a52781bf8d8..acf56607bc9c 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -13,6 +13,21 @@ #include "sof-audio.h" #include "ops.h" +/* + * Check if a DAI widget is an aggregated DAI. Aggregated DAI's have names ending in numbers + * starting with 0. For example: in the case of a SDW speaker with 2 amps, the topology contains + * 2 DAI's names alh-copier.SDW1.Playback.0 and alh-copier-SDW1.Playback.1. In this case, only the + * DAI alh-copier.SDW1.Playback.0 is set up in the firmware. The other DAI, + * alh-copier.SDW1.Playback.1 in topology is for the sake of completeness to show aggregation for + * the speaker amp and does not need any firmware configuration. + */ +static bool is_aggregated_dai(struct snd_sof_widget *swidget) +{ + return (WIDGET_IS_DAI(swidget->id) && + isdigit(swidget->widget->name[strlen(swidget->widget->name) - 1]) && + swidget->widget->name[strlen(swidget->widget->name) - 1] != '0'); +} + static bool is_virtual_widget(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, const char *func) { @@ -121,13 +136,8 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { - int ret; - - mutex_lock(&swidget->setup_mutex); - ret = sof_widget_free_unlocked(sdev, swidget); - mutex_unlock(&swidget->setup_mutex); - - return ret; + guard(mutex)(&swidget->setup_mutex); + return sof_widget_free_unlocked(sdev, swidget); } EXPORT_SYMBOL(sof_widget_free); @@ -240,13 +250,8 @@ use_count_dec: int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { - int ret; - - mutex_lock(&swidget->setup_mutex); - ret = sof_widget_setup_unlocked(sdev, swidget); - mutex_unlock(&swidget->setup_mutex); - - return ret; + guard(mutex)(&swidget->setup_mutex); + return sof_widget_setup_unlocked(sdev, swidget); } EXPORT_SYMBOL(sof_widget_setup); @@ -264,6 +269,10 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc is_virtual_widget(sdev, sink_widget->widget, __func__)) return 0; + /* skip route if source/sink widget is not set up */ + if (!src_widget->use_count || !sink_widget->use_count) + return 0; + /* find route matching source and sink widgets */ list_for_each_entry(sroute, &sdev->route_list, list) if (sroute->src_widget == src_widget && sroute->sink_widget == sink_widget) { @@ -292,10 +301,34 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc return 0; } +static bool sof_widget_in_same_direction(struct snd_sof_widget *swidget, int dir) +{ + return swidget->spipe->direction == dir; +} + +static int sof_set_up_same_dir_widget_routes(struct snd_sof_dev *sdev, + struct snd_soc_dapm_widget *wsource, + struct snd_soc_dapm_widget *wsink) +{ + struct snd_sof_widget *src_widget = wsource->dobj.private; + struct snd_sof_widget *sink_widget = wsink->dobj.private; + + /* + * skip setting up route if source and sink are in different directions (ex. playback and + * echo ref) if the direction is set in topology. These will be set up later. It is enough + * to check if the direction_valid is set for one of the widgets as all widgets will have + * the direction set in topology if one is set. + */ + if (sink_widget->spipe && sink_widget->spipe->direction_valid && + !sof_widget_in_same_direction(sink_widget, src_widget->spipe->direction)) + return 0; + + return sof_route_setup(sdev, wsource, wsink); +} + static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_list *list, int dir) { - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_soc_dapm_widget *widget; struct snd_sof_route *sroute; struct snd_soc_dapm_path *p; @@ -318,7 +351,8 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, continue; if (p->sink->dobj.private) { - ret = sof_route_setup(sdev, widget, p->sink); + ret = sof_set_up_same_dir_widget_routes(sdev, widget, + p->sink); if (ret < 0) return ret; } @@ -334,7 +368,8 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, continue; if (p->source->dobj.private) { - ret = sof_route_setup(sdev, p->source, widget); + ret = sof_set_up_same_dir_widget_routes(sdev, p->source, + widget); if (ret < 0) return ret; } @@ -350,7 +385,6 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, */ list_for_each_entry(sroute, &sdev->route_list, list) { bool src_widget_in_dapm_list, sink_widget_in_dapm_list; - struct snd_sof_widget *swidget; if (sroute->setup) continue; @@ -359,41 +393,36 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, sink_widget_in_dapm_list = widget_in_list(list, sroute->sink_widget->widget); /* - * if both source and sink are in the DAPM list, the route must already have been - * set up above. And if neither are in the DAPM list, the route shouldn't be - * handled now. + * no need to set up the route if both the source and sink widgets are not in the + * DAPM list */ - if (src_widget_in_dapm_list == sink_widget_in_dapm_list) + if (!src_widget_in_dapm_list && !sink_widget_in_dapm_list) continue; /* - * At this point either the source widget or the sink widget is in the DAPM list - * with a route that might need to be set up. Check the use_count of the widget - * that is not in the DAPM list to confirm if it is in use currently before setting - * up the route. + * set up the route only if both the source and sink widgets are in the DAPM list + * but are in different directions. The ones in the same direction would already + * have been set up in the previous loop. */ - if (src_widget_in_dapm_list) - swidget = sroute->sink_widget; - else - swidget = sroute->src_widget; - - mutex_lock(&swidget->setup_mutex); - if (!swidget->use_count) { - mutex_unlock(&swidget->setup_mutex); - continue; - } + if (src_widget_in_dapm_list && sink_widget_in_dapm_list) { + struct snd_sof_widget *src_widget, *sink_widget; + + src_widget = sroute->src_widget->widget->dobj.private; + sink_widget = sroute->sink_widget->widget->dobj.private; - if (tplg_ops && tplg_ops->route_setup) { /* - * this route will get freed when either the source widget or the sink - * widget is freed during hw_free + * it is enough to check if the direction_valid is set for one of the + * widgets as all widgets will have the direction set in topology if one + * is set. */ - ret = tplg_ops->route_setup(sdev, sroute); - if (!ret) - sroute->setup = true; + if (src_widget && sink_widget && + src_widget->spipe && src_widget->spipe->direction_valid && + sof_widget_in_same_direction(sink_widget, src_widget->spipe->direction)) + continue; } - mutex_unlock(&swidget->setup_mutex); + ret = sof_route_setup(sdev, sroute->src_widget->widget, + sroute->sink_widget->widget); if (ret < 0) return ret; @@ -404,7 +433,7 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, static void sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, - struct snd_soc_dapm_widget_list *list) + struct snd_soc_dapm_widget_list *list, int dir) { const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_sof_widget *swidget = widget->dobj.private; @@ -414,8 +443,15 @@ 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) + if (!swidget) + goto sink_unprepare; + + if (swidget->spipe && swidget->spipe->direction_valid && + !sof_widget_in_same_direction(swidget, dir)) + return; + + /* skip widgets in use, those already unprepared or aggregated DAIs */ + if (!swidget->prepared || swidget->use_count > 0 || is_aggregated_dai(swidget)) goto sink_unprepare; widget_ops = tplg_ops ? tplg_ops->widget : NULL; @@ -430,9 +466,10 @@ sink_unprepare: snd_soc_dapm_widget_for_each_sink_path(widget, p) { if (!widget_in_list(list, p->sink)) continue; + if (!p->walking && p->sink->dobj.private) { p->walking = true; - sof_unprepare_widgets_in_path(sdev, p->sink, list); + sof_unprepare_widgets_in_path(sdev, p->sink, list, dir); p->walking = false; } } @@ -454,11 +491,20 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget if (is_virtual_widget(sdev, widget, __func__)) return 0; + if (!swidget) + goto sink_prepare; + widget_ops = tplg_ops ? tplg_ops->widget : NULL; if (!widget_ops) return 0; - if (!swidget || !widget_ops[widget->id].ipc_prepare || swidget->prepared) + if (swidget->spipe && swidget->spipe->direction_valid && + !sof_widget_in_same_direction(swidget, dir)) + return 0; + + /* skip widgets already prepared or aggregated DAI widgets*/ + if (!widget_ops[widget->id].ipc_prepare || swidget->prepared || + is_aggregated_dai(swidget)) goto sink_prepare; /* prepare the source widget */ @@ -476,6 +522,7 @@ sink_prepare: snd_soc_dapm_widget_for_each_sink_path(widget, p) { if (!widget_in_list(list, p->sink)) continue; + if (!p->walking && p->sink->dobj.private) { p->walking = true; ret = sof_prepare_widgets_in_path(sdev, p->sink, fe_params, @@ -505,6 +552,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,12 +560,21 @@ 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) { - err = sof_widget_free(sdev, widget->dobj.private); - if (err < 0) - ret = err; - } + if (!swidget) + goto sink_free; + + if (swidget->spipe && swidget->spipe->direction_valid && + !sof_widget_in_same_direction(swidget, dir)) + return 0; + /* skip aggregated DAIs */ + if (is_aggregated_dai(swidget)) + goto sink_free; + + err = sof_widget_free(sdev, widget->dobj.private); + if (err < 0) + ret = err; +sink_free: /* free all widgets in the sink paths even in case of error to keep use counts balanced */ snd_soc_dapm_widget_for_each_sink_path(widget, p) { if (!p->walking) { @@ -557,7 +614,15 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d if (swidget) { int i; - ret = sof_widget_setup(sdev, widget->dobj.private); + if (swidget->spipe && swidget->spipe->direction_valid && + !sof_widget_in_same_direction(swidget, dir)) + return 0; + + /* skip aggregated DAIs */ + if (is_aggregated_dai(swidget)) + goto sink_setup; + + ret = sof_widget_setup(sdev, swidget); if (ret < 0) return ret; @@ -619,15 +684,13 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, return 0; for_each_dapm_widgets(list, i, widget) { - if (is_virtual_widget(sdev, widget, __func__)) - continue; - - /* starting widget for playback is AIF type */ + /* starting widget for playback is of AIF type */ if (dir == SNDRV_PCM_STREAM_PLAYBACK && widget->id != snd_soc_dapm_aif_in) continue; /* starting widget for capture is DAI type */ - if (dir == SNDRV_PCM_STREAM_CAPTURE && widget->id != snd_soc_dapm_dai_out) + if (dir == SNDRV_PCM_STREAM_CAPTURE && widget->id != snd_soc_dapm_dai_out && + widget->id != snd_soc_dapm_output) continue; switch (op) { @@ -657,7 +720,7 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, break; } case SOF_WIDGET_UNPREPARE: - sof_unprepare_widgets_in_path(sdev, widget, list); + sof_unprepare_widgets_in_path(sdev, widget, list, dir); break; default: dev_err(sdev->dev, "Invalid widget op %d\n", op); @@ -672,6 +735,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 +769,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 +827,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 +846,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; @@ -829,55 +904,6 @@ bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev) return false; } -int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, - struct snd_sof_pcm *spcm, int dir, bool free_widget_list) -{ - const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); - int ret; - int err = 0; - - if (spcm->prepared[substream->stream]) { - /* stop DMA first if needed */ - if (pcm_ops && pcm_ops->platform_stop_during_hw_free) - snd_sof_pcm_platform_trigger(sdev, substream, SNDRV_PCM_TRIGGER_STOP); - - /* free PCM in the DSP */ - if (pcm_ops && pcm_ops->hw_free) { - ret = pcm_ops->hw_free(sdev->component, substream); - if (ret < 0) { - dev_err(sdev->dev, "%s: pcm_ops hw_free failed %d\n", - __func__, ret); - err = ret; - } - } - - spcm->prepared[substream->stream] = false; - spcm->pending_stop[substream->stream] = false; - } - - /* reset the DMA */ - ret = snd_sof_pcm_platform_hw_free(sdev, substream); - if (ret < 0) { - dev_err(sdev->dev, "%s: platform hw free failed %d\n", - __func__, ret); - if (!err) - err = ret; - } - - /* free widget list */ - if (free_widget_list) { - ret = sof_widget_list_free(sdev, spcm, dir); - if (ret < 0) { - dev_err(sdev->dev, "%s: sof_widget_list_free failed %d\n", - __func__, ret); - if (!err) - err = ret; - } - } - - return err; -} - /* * Generic object lookup APIs. */ |
