Commit b2ae8066 authored by Takashi Iwai's avatar Takashi Iwai Committed by Mark Brown

ASoC: soc-pcm: serialize BE triggers

When more than one FE is connected to a BE, e.g. in a mixing use case,
the BE can be triggered multiple times when the FE are opened/started
concurrently. This race condition is problematic in the case of
SoundWire BE dailinks, and this is not desirable in a general
case.

This patch relies on the existing BE PCM lock, which takes atomicity into
account. The locking model assumes that all interactions start with
the FE, so that there is no deadlock between FE and BE locks.
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
[test, checkpatch fix and clarification of commit message by plbossart]
Signed-off-by: default avatarPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: default avatarKai Vehmanen <kai.vehmanen@linux.intel.com>
Reviewed-by: default avatarBard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: default avatarRanjani Sridharan <ranjani.sridharan@linux.intel.com>
Link: https://lore.kernel.org/r/20211207173745.15850-5-pierre-louis.bossart@linux.intel.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent b7898396
...@@ -46,12 +46,18 @@ static inline void snd_soc_dpcm_stream_lock_irq(struct snd_soc_pcm_runtime *rtd, ...@@ -46,12 +46,18 @@ static inline void snd_soc_dpcm_stream_lock_irq(struct snd_soc_pcm_runtime *rtd,
snd_pcm_stream_lock_irq(snd_soc_dpcm_get_substream(rtd, stream)); snd_pcm_stream_lock_irq(snd_soc_dpcm_get_substream(rtd, stream));
} }
#define snd_soc_dpcm_stream_lock_irqsave(rtd, stream, flags) \
snd_pcm_stream_lock_irqsave(snd_soc_dpcm_get_substream(rtd, stream), flags)
static inline void snd_soc_dpcm_stream_unlock_irq(struct snd_soc_pcm_runtime *rtd, static inline void snd_soc_dpcm_stream_unlock_irq(struct snd_soc_pcm_runtime *rtd,
int stream) int stream)
{ {
snd_pcm_stream_unlock_irq(snd_soc_dpcm_get_substream(rtd, stream)); snd_pcm_stream_unlock_irq(snd_soc_dpcm_get_substream(rtd, stream));
} }
#define snd_soc_dpcm_stream_unlock_irqrestore(rtd, stream, flags) \
snd_pcm_stream_unlock_irqrestore(snd_soc_dpcm_get_substream(rtd, stream), flags)
#define DPCM_MAX_BE_USERS 8 #define DPCM_MAX_BE_USERS 8
static inline const char *soc_cpu_dai_name(struct snd_soc_pcm_runtime *rtd) static inline const char *soc_cpu_dai_name(struct snd_soc_pcm_runtime *rtd)
...@@ -2079,6 +2085,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, ...@@ -2079,6 +2085,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
{ {
struct snd_soc_pcm_runtime *be; struct snd_soc_pcm_runtime *be;
struct snd_soc_dpcm *dpcm; struct snd_soc_dpcm *dpcm;
unsigned long flags;
int ret = 0; int ret = 0;
for_each_dpcm_be(fe, stream, dpcm) { for_each_dpcm_be(fe, stream, dpcm) {
...@@ -2087,9 +2094,11 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, ...@@ -2087,9 +2094,11 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
be = dpcm->be; be = dpcm->be;
be_substream = snd_soc_dpcm_get_substream(be, stream); be_substream = snd_soc_dpcm_get_substream(be, stream);
snd_soc_dpcm_stream_lock_irqsave(be, stream, flags);
/* is this op for this BE ? */ /* is this op for this BE ? */
if (!snd_soc_dpcm_be_can_update(fe, be, stream)) if (!snd_soc_dpcm_be_can_update(fe, be, stream))
continue; goto next;
dev_dbg(be->dev, "ASoC: trigger BE %s cmd %d\n", dev_dbg(be->dev, "ASoC: trigger BE %s cmd %d\n",
be->dai_link->name, cmd); be->dai_link->name, cmd);
...@@ -2099,77 +2108,80 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, ...@@ -2099,77 +2108,80 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
continue; goto next;
ret = soc_pcm_trigger(be_substream, cmd); ret = soc_pcm_trigger(be_substream, cmd);
if (ret) if (ret)
goto end; goto next;
be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
break; break;
case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_RESUME:
if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND)) if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND))
continue; goto next;
ret = soc_pcm_trigger(be_substream, cmd); ret = soc_pcm_trigger(be_substream, cmd);
if (ret) if (ret)
goto end; goto next;
be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
break; break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
continue; goto next;
ret = soc_pcm_trigger(be_substream, cmd); ret = soc_pcm_trigger(be_substream, cmd);
if (ret) if (ret)
goto end; goto next;
be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
break; break;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) && if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
continue; goto next;
if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
continue; goto next;
ret = soc_pcm_trigger(be_substream, cmd); ret = soc_pcm_trigger(be_substream, cmd);
if (ret) if (ret)
goto end; goto next;
be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
break; break;
case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_SUSPEND:
if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
continue; goto next;
if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
continue; goto next;
ret = soc_pcm_trigger(be_substream, cmd); ret = soc_pcm_trigger(be_substream, cmd);
if (ret) if (ret)
goto end; goto next;
be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND; be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND;
break; break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
continue; goto next;
if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
continue; goto next;
ret = soc_pcm_trigger(be_substream, cmd); ret = soc_pcm_trigger(be_substream, cmd);
if (ret) if (ret)
goto end; goto next;
be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED;
break; break;
} }
next:
snd_soc_dpcm_stream_unlock_irqrestore(be, stream, flags);
if (ret)
break;
} }
end:
if (ret < 0) if (ret < 0)
dev_err(fe->dev, "ASoC: %s() failed at %s (%d)\n", dev_err(fe->dev, "ASoC: %s() failed at %s (%d)\n",
__func__, be->dai_link->name, ret); __func__, be->dai_link->name, ret);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment