summaryrefslogtreecommitdiff
path: root/sound/soc/sof/sof-audio.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sof/sof-audio.c')
-rw-r--r--sound/soc/sof/sof-audio.c85
1 files changed, 75 insertions, 10 deletions
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index 6de388a8d0b8..1cbda595c518 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -280,9 +280,11 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc
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;
- int ret;
+ int ret = 0;
int i;
/*
@@ -325,6 +327,63 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
}
}
+ /*
+ * The above loop handles connections between widgets that belong to the DAPM widget list.
+ * This is not sufficient to handle loopback cases between pipelines configured with
+ * different directions, e.g. a sidetone or an amplifier feedback connected to a speaker
+ * protection module.
+ */
+ 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;
+
+ src_widget_in_dapm_list = widget_in_list(list, sroute->src_widget->widget);
+ 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.
+ */
+ 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.
+ */
+ 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 (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
+ */
+ ret = tplg_ops->route_setup(sdev, sroute);
+ if (!ret)
+ sroute->setup = true;
+ }
+
+ mutex_unlock(&swidget->setup_mutex);
+
+ if (ret < 0)
+ return ret;
+ }
+
return 0;
}
@@ -629,7 +688,7 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
struct snd_sof_widget *pipe_widget;
struct snd_sof_pipeline *spipe;
- if (!swidget)
+ if (!swidget || sdev->dspless_mode_selected)
continue;
spipe = swidget->spipe;
@@ -746,16 +805,22 @@ int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *subs
const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
int ret;
- /* Send PCM_FREE IPC to reset pipeline */
- if (pcm_ops && pcm_ops->hw_free && spcm->prepared[substream->stream]) {
- ret = pcm_ops->hw_free(sdev->component, substream);
- if (ret < 0)
- return ret;
- }
+ 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);
- spcm->prepared[substream->stream] = false;
+ /* Send PCM_FREE IPC to reset pipeline */
+ if (pcm_ops && pcm_ops->hw_free) {
+ ret = pcm_ops->hw_free(sdev->component, substream);
+ if (ret < 0)
+ return ret;
+ }
+
+ spcm->prepared[substream->stream] = false;
+ }
- /* stop the DMA */
+ /* reset the DMA */
ret = snd_sof_pcm_platform_hw_free(sdev, substream);
if (ret < 0)
return ret;