| From 02751cd04c89621be9bdfc0d8e8dc5d4a724d32c Mon Sep 17 00:00:00 2001 |
| From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> |
| Date: Mon, 4 Nov 2019 14:48:11 -0800 |
| Subject: [PATCH] ASoC: pcm: update FE/BE trigger order based on the command |
| |
| commit acbf27746ecfa96b290b54cc7f05273482ea128a upstream. |
| |
| Currently, the trigger orders SND_SOC_DPCM_TRIGGER_PRE/POST |
| determine the order in which FE DAI and BE DAI are triggered. |
| In the case of SND_SOC_DPCM_TRIGGER_PRE, the FE DAI is |
| triggered before the BE DAI and in the case of |
| SND_SOC_DPCM_TRIGGER_POST, the BE DAI is triggered before |
| the FE DAI. And this order remains the same irrespective of the |
| trigger command. |
| |
| In the case of the SOF driver, during playback, the FW |
| expects the BE DAI to be triggered before the FE DAI during |
| the START trigger. The BE DAI trigger handles the starting of |
| Link DMA and so it must be started before the FE DAI is started |
| to prevent xruns during pause/release. This can be addressed |
| by setting the trigger order for the FE dai link to |
| SND_SOC_DPCM_TRIGGER_POST. But during the STOP trigger, |
| the FW expects the FE DAI to be triggered before the BE DAI. |
| Retaining the same order during the START and STOP commands, |
| results in FW error as the DAI component in the FW is still |
| active. |
| |
| The issue can be fixed by mirroring the trigger order of |
| FE and BE DAI's during the START and STOP trigger. So, with the |
| trigger order set to SND_SOC_DPCM_TRIGGER_PRE, the FE DAI will be |
| trigger first during SNDRV_PCM_TRIGGER_START/STOP/RESUME |
| and the BE DAI will be triggered first during the |
| STOP/SUSPEND/PAUSE commands. Conversely, with the trigger order |
| set to SND_SOC_DPCM_TRIGGER_POST, the BE DAI will be triggered |
| first during the SNDRV_PCM_TRIGGER_START/STOP/RESUME commands |
| and the FE DAI will be triggered first during the |
| SNDRV_PCM_TRIGGER_STOP/SUSPEND/PAUSE commands. |
| |
| Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> |
| Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> |
| Link: https://lore.kernel.org/r/20191104224812.3393-2-ranjani.sridharan@linux.intel.com |
| Signed-off-by: Mark Brown <broonie@kernel.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c |
| index b994d27478e2..e1526548cfe8 100644 |
| --- a/sound/soc/soc-pcm.c |
| +++ b/sound/soc/soc-pcm.c |
| @@ -2375,42 +2375,81 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, |
| } |
| EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger); |
| |
| +static int dpcm_dai_trigger_fe_be(struct snd_pcm_substream *substream, |
| + int cmd, bool fe_first) |
| +{ |
| + struct snd_soc_pcm_runtime *fe = substream->private_data; |
| + int ret; |
| + |
| + /* call trigger on the frontend before the backend. */ |
| + if (fe_first) { |
| + dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n", |
| + fe->dai_link->name, cmd); |
| + |
| + ret = soc_pcm_trigger(substream, cmd); |
| + if (ret < 0) |
| + return ret; |
| + |
| + ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); |
| + return ret; |
| + } |
| + |
| + /* call trigger on the frontend after the backend. */ |
| + ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); |
| + if (ret < 0) |
| + return ret; |
| + |
| + dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n", |
| + fe->dai_link->name, cmd); |
| + |
| + ret = soc_pcm_trigger(substream, cmd); |
| + |
| + return ret; |
| +} |
| + |
| static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd) |
| { |
| struct snd_soc_pcm_runtime *fe = substream->private_data; |
| - int stream = substream->stream, ret; |
| + int stream = substream->stream; |
| + int ret = 0; |
| enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; |
| |
| fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; |
| |
| switch (trigger) { |
| case SND_SOC_DPCM_TRIGGER_PRE: |
| - /* call trigger on the frontend before the backend. */ |
| - |
| - dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n", |
| - fe->dai_link->name, cmd); |
| - |
| - ret = soc_pcm_trigger(substream, cmd); |
| - if (ret < 0) { |
| - dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret); |
| - goto out; |
| + switch (cmd) { |
| + case SNDRV_PCM_TRIGGER_START: |
| + case SNDRV_PCM_TRIGGER_RESUME: |
| + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
| + ret = dpcm_dai_trigger_fe_be(substream, cmd, true); |
| + break; |
| + case SNDRV_PCM_TRIGGER_STOP: |
| + case SNDRV_PCM_TRIGGER_SUSPEND: |
| + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
| + ret = dpcm_dai_trigger_fe_be(substream, cmd, false); |
| + break; |
| + default: |
| + ret = -EINVAL; |
| + break; |
| } |
| - |
| - ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); |
| break; |
| case SND_SOC_DPCM_TRIGGER_POST: |
| - /* call trigger on the frontend after the backend. */ |
| - |
| - ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); |
| - if (ret < 0) { |
| - dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret); |
| - goto out; |
| + switch (cmd) { |
| + case SNDRV_PCM_TRIGGER_START: |
| + case SNDRV_PCM_TRIGGER_RESUME: |
| + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
| + ret = dpcm_dai_trigger_fe_be(substream, cmd, false); |
| + break; |
| + case SNDRV_PCM_TRIGGER_STOP: |
| + case SNDRV_PCM_TRIGGER_SUSPEND: |
| + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
| + ret = dpcm_dai_trigger_fe_be(substream, cmd, true); |
| + break; |
| + default: |
| + ret = -EINVAL; |
| + break; |
| } |
| - |
| - dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n", |
| - fe->dai_link->name, cmd); |
| - |
| - ret = soc_pcm_trigger(substream, cmd); |
| break; |
| case SND_SOC_DPCM_TRIGGER_BESPOKE: |
| /* bespoke trigger() - handles both FE and BEs */ |
| @@ -2419,10 +2458,6 @@ static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd) |
| fe->dai_link->name, cmd); |
| |
| ret = soc_pcm_bespoke_trigger(substream, cmd); |
| - if (ret < 0) { |
| - dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret); |
| - goto out; |
| - } |
| break; |
| default: |
| dev_err(fe->dev, "ASoC: invalid trigger cmd %d for %s\n", cmd, |
| @@ -2431,6 +2466,12 @@ static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd) |
| goto out; |
| } |
| |
| + if (ret < 0) { |
| + dev_err(fe->dev, "ASoC: trigger FE cmd: %d failed: %d\n", |
| + cmd, ret); |
| + goto out; |
| + } |
| + |
| switch (cmd) { |
| case SNDRV_PCM_TRIGGER_START: |
| case SNDRV_PCM_TRIGGER_RESUME: |
| -- |
| 2.7.4 |
| |