diff options
Diffstat (limited to 'sound/soc/sof/pcm.c')
-rw-r--r-- | sound/soc/sof/pcm.c | 58 |
1 files changed, 56 insertions, 2 deletions
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index b4280459e5db..374df2dfa816 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -116,6 +116,40 @@ static int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, return ret; } +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_soc_dai *dai; + int ret, j; + + /* query DAPM for list of connected widgets and set them up */ + for_each_rtd_cpu_dais(rtd, j, dai) { + struct snd_soc_dapm_widget_list *list; + + ret = snd_soc_dapm_dai_get_connected_widgets(dai, dir, &list, + dpcm_end_walk_at_be); + if (ret < 0) { + dev_err(sdev->dev, "error: dai %s has no valid %s path\n", dai->name, + dir == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture"); + return ret; + } + + spcm->stream[dir].list = list; + + ret = sof_widget_list_setup(sdev, spcm, 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); + spcm->stream[dir].list = NULL; + snd_soc_dapm_dai_free_widgets(&list); + return ret; + } + } + + return 0; +} + static int sof_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) @@ -213,7 +247,14 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, dev_dbg(component->dev, "stream_tag %d", pcm.params.stream_tag); - /* send IPC to the DSP */ + /* 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); + if (ret < 0) + return ret; + } + + /* send hw_params IPC to the DSP */ ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm), &ipc_params_reply, sizeof(ipc_params_reply)); if (ret < 0) { @@ -259,6 +300,10 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, err = ret; } + ret = sof_widget_list_free(sdev, spcm, substream->stream); + if (ret < 0) + err = ret; + cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work); ret = snd_sof_pcm_platform_hw_free(sdev, substream); @@ -316,6 +361,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component, struct sof_ipc_stream stream; struct sof_ipc_reply reply; bool reset_hw_params = false; + bool free_widget_list = false; bool ipc_first = false; int ret; @@ -386,6 +432,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component, spcm->stream[substream->stream].suspend_ignored = true; return 0; } + free_widget_list = true; fallthrough; case SNDRV_PCM_TRIGGER_STOP: stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP; @@ -414,8 +461,15 @@ static int sof_pcm_trigger(struct snd_soc_component *component, snd_sof_pcm_platform_trigger(sdev, substream, cmd); /* free PCM if reset_hw_params is set and the STOP IPC is successful */ - if (!ret && reset_hw_params) + if (!ret && reset_hw_params) { ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); + if (ret < 0) + return ret; + + /* free widget list only for SUSPEND trigger */ + if (free_widget_list) + ret = sof_widget_list_free(sdev, spcm, substream->stream); + } return ret; } |