ASoC: soc-pcm: test refcount before triggering
authorPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Tue, 17 Aug 2021 16:40:54 +0000 (11:40 -0500)
committerMark Brown <broonie@kernel.org>
Thu, 26 Aug 2021 16:42:08 +0000 (17:42 +0100)
On start/pause_release/resume, when more than one FE is connected to
the same BE, it's possible that the trigger is sent more than
once. This is not desirable, we only want to trigger a BE once, which
is straightforward to implement with a refcount.

For stop/pause/suspend, the problem is more complicated: the check
implemented in snd_soc_dpcm_can_be_free_stop() may fail due to a
conceptual deadlock when we trigger the BE before the FE. In this
case, the FE states have not yet changed, so there are corner cases
where the TRIGGER_STOP is never sent - the dual case of start where
multiple triggers might be sent.

This patch suggests an unconditional trigger in all cases, without
checking the FE states, using a refcount protected by a spinlock.

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Message-Id: <20210817164054.250028-3-pierre-louis.bossart@linux.intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
include/sound/soc-dpcm.h
sound/soc/soc-pcm.c

index e296a3949b18bd6bf13e8bbbc92dc2e28601eda9..6cc751002da7a7e4bc129f258d62e1ffa793db3d 100644 (file)
@@ -101,6 +101,8 @@ struct snd_soc_dpcm_runtime {
        enum snd_soc_dpcm_state state;
 
        int trigger_pending; /* trigger cmd + 1 if pending, 0 if not */
+
+       int be_start; /* refcount protected by dpcm_lock */
 };
 
 #define for_each_dpcm_fe(be, stream, _dpcm)                            \
index 0717f39d2eec26ff7b4f5801bf2652d8f64e7d3c..b2440f2f9bf538c2578f63f6d4292704db78cee0 100644 (file)
@@ -1534,7 +1534,7 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
                        be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
                        goto unwind;
                }
-
+               be->dpcm[stream].be_start = 0;
                be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
                count++;
        }
@@ -2001,6 +2001,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
        int ret = 0;
        unsigned long flags;
        enum snd_soc_dpcm_state state;
+       bool do_trigger;
 
        for_each_dpcm_be(fe, stream, dpcm) {
                struct snd_pcm_substream *be_substream;
@@ -2015,6 +2016,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
                dev_dbg(be->dev, "ASoC: trigger BE %s cmd %d\n",
                        be->dai_link->name, cmd);
 
+               do_trigger = false;
                switch (cmd) {
                case SNDRV_PCM_TRIGGER_START:
                        spin_lock_irqsave(&fe->card->dpcm_lock, flags);
@@ -2025,13 +2027,20 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
                                continue;
                        }
                        state = be->dpcm[stream].state;
+                       if (be->dpcm[stream].be_start == 0)
+                               do_trigger = true;
+                       be->dpcm[stream].be_start++;
                        be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
                        spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
 
+                       if (!do_trigger)
+                               continue;
+
                        ret = soc_pcm_trigger(be_substream, cmd);
                        if (ret) {
                                spin_lock_irqsave(&fe->card->dpcm_lock, flags);
                                be->dpcm[stream].state = state;
+                               be->dpcm[stream].be_start--;
                                spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
                                goto end;
                        }
@@ -2045,13 +2054,20 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
                        }
 
                        state = be->dpcm[stream].state;
+                       if (be->dpcm[stream].be_start == 0)
+                               do_trigger = true;
+                       be->dpcm[stream].be_start++;
                        be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
                        spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
 
+                       if (!do_trigger)
+                               continue;
+
                        ret = soc_pcm_trigger(be_substream, cmd);
                        if (ret) {
                                spin_lock_irqsave(&fe->card->dpcm_lock, flags);
                                be->dpcm[stream].state = state;
+                               be->dpcm[stream].be_start--;
                                spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
                                goto end;
                        }
@@ -2065,13 +2081,20 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
                        }
 
                        state = be->dpcm[stream].state;
+                       if (be->dpcm[stream].be_start == 0)
+                               do_trigger = true;
+                       be->dpcm[stream].be_start++;
                        be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
                        spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
 
+                       if (!do_trigger)
+                               continue;
+
                        ret = soc_pcm_trigger(be_substream, cmd);
                        if (ret) {
                                spin_lock_irqsave(&fe->card->dpcm_lock, flags);
                                be->dpcm[stream].state = state;
+                               be->dpcm[stream].be_start--;
                                spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
                                goto end;
                        }
@@ -2084,9 +2107,15 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
                                spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
                                continue;
                        }
+                       if ((be->dpcm[stream].state == SND_SOC_DPCM_STATE_START &&
+                            be->dpcm[stream].be_start == 1) ||
+                           (be->dpcm[stream].state == SND_SOC_DPCM_STATE_PAUSED &&
+                            be->dpcm[stream].be_start == 0))
+                               do_trigger = true;
+                       be->dpcm[stream].be_start--;
                        spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
 
-                       if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
+                       if (!do_trigger)
                                continue;
 
                        spin_lock_irqsave(&fe->card->dpcm_lock, flags);
@@ -2098,6 +2127,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
                        if (ret) {
                                spin_lock_irqsave(&fe->card->dpcm_lock, flags);
                                be->dpcm[stream].state = state;
+                               be->dpcm[stream].be_start++;
                                spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
                                goto end;
                        }
@@ -2109,9 +2139,12 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
                                spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
                                continue;
                        }
+                       if (be->dpcm[stream].be_start == 1)
+                               do_trigger = true;
+                       be->dpcm[stream].be_start--;
                        spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
 
-                       if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
+                       if (!do_trigger)
                                continue;
 
                        spin_lock_irqsave(&fe->card->dpcm_lock, flags);
@@ -2123,6 +2156,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
                        if (ret) {
                                spin_lock_irqsave(&fe->card->dpcm_lock, flags);
                                be->dpcm[stream].state = state;
+                               be->dpcm[stream].be_start++;
                                spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
                                goto end;
                        }
@@ -2134,9 +2168,12 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
                                spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
                                continue;
                        }
+                       if (be->dpcm[stream].be_start == 1)
+                               do_trigger = true;
+                       be->dpcm[stream].be_start--;
                        spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
 
-                       if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
+                       if (!do_trigger)
                                continue;
 
                        spin_lock_irqsave(&fe->card->dpcm_lock, flags);
@@ -2148,6 +2185,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
                        if (ret) {
                                spin_lock_irqsave(&fe->card->dpcm_lock, flags);
                                be->dpcm[stream].state = state;
+                               be->dpcm[stream].be_start++;
                                spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
                                goto end;
                        }