Skip to content

Commit 6479f75

Browse files
plbossartbroonie
authored andcommitted
ASoC: soc-pcm: test refcount before triggering
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 <[email protected]> Message-Id: <[email protected]> Signed-off-by: Mark Brown <[email protected]>
1 parent 0c75fc7 commit 6479f75

File tree

2 files changed

+44
-4
lines changed

2 files changed

+44
-4
lines changed

include/sound/soc-dpcm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ struct snd_soc_dpcm_runtime {
101101
enum snd_soc_dpcm_state state;
102102

103103
int trigger_pending; /* trigger cmd + 1 if pending, 0 if not */
104+
105+
int be_start; /* refcount protected by dpcm_lock */
104106
};
105107

106108
#define for_each_dpcm_fe(be, stream, _dpcm) \

sound/soc/soc-pcm.c

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1534,7 +1534,7 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
15341534
be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
15351535
goto unwind;
15361536
}
1537-
1537+
be->dpcm[stream].be_start = 0;
15381538
be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
15391539
count++;
15401540
}
@@ -2001,6 +2001,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
20012001
int ret = 0;
20022002
unsigned long flags;
20032003
enum snd_soc_dpcm_state state;
2004+
bool do_trigger;
20042005

20052006
for_each_dpcm_be(fe, stream, dpcm) {
20062007
struct snd_pcm_substream *be_substream;
@@ -2015,6 +2016,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
20152016
dev_dbg(be->dev, "ASoC: trigger BE %s cmd %d\n",
20162017
be->dai_link->name, cmd);
20172018

2019+
do_trigger = false;
20182020
switch (cmd) {
20192021
case SNDRV_PCM_TRIGGER_START:
20202022
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,
20252027
continue;
20262028
}
20272029
state = be->dpcm[stream].state;
2030+
if (be->dpcm[stream].be_start == 0)
2031+
do_trigger = true;
2032+
be->dpcm[stream].be_start++;
20282033
be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
20292034
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20302035

2036+
if (!do_trigger)
2037+
continue;
2038+
20312039
ret = soc_pcm_trigger(be_substream, cmd);
20322040
if (ret) {
20332041
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
20342042
be->dpcm[stream].state = state;
2043+
be->dpcm[stream].be_start--;
20352044
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20362045
goto end;
20372046
}
@@ -2045,13 +2054,20 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
20452054
}
20462055

20472056
state = be->dpcm[stream].state;
2057+
if (be->dpcm[stream].be_start == 0)
2058+
do_trigger = true;
2059+
be->dpcm[stream].be_start++;
20482060
be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
20492061
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20502062

2063+
if (!do_trigger)
2064+
continue;
2065+
20512066
ret = soc_pcm_trigger(be_substream, cmd);
20522067
if (ret) {
20532068
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
20542069
be->dpcm[stream].state = state;
2070+
be->dpcm[stream].be_start--;
20552071
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20562072
goto end;
20572073
}
@@ -2065,13 +2081,20 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
20652081
}
20662082

20672083
state = be->dpcm[stream].state;
2084+
if (be->dpcm[stream].be_start == 0)
2085+
do_trigger = true;
2086+
be->dpcm[stream].be_start++;
20682087
be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
20692088
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20702089

2090+
if (!do_trigger)
2091+
continue;
2092+
20712093
ret = soc_pcm_trigger(be_substream, cmd);
20722094
if (ret) {
20732095
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
20742096
be->dpcm[stream].state = state;
2097+
be->dpcm[stream].be_start--;
20752098
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20762099
goto end;
20772100
}
@@ -2084,9 +2107,15 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
20842107
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20852108
continue;
20862109
}
2110+
if ((be->dpcm[stream].state == SND_SOC_DPCM_STATE_START &&
2111+
be->dpcm[stream].be_start == 1) ||
2112+
(be->dpcm[stream].state == SND_SOC_DPCM_STATE_PAUSED &&
2113+
be->dpcm[stream].be_start == 0))
2114+
do_trigger = true;
2115+
be->dpcm[stream].be_start--;
20872116
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20882117

2089-
if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
2118+
if (!do_trigger)
20902119
continue;
20912120

20922121
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,
20982127
if (ret) {
20992128
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
21002129
be->dpcm[stream].state = state;
2130+
be->dpcm[stream].be_start++;
21012131
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
21022132
goto end;
21032133
}
@@ -2109,9 +2139,12 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
21092139
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
21102140
continue;
21112141
}
2142+
if (be->dpcm[stream].be_start == 1)
2143+
do_trigger = true;
2144+
be->dpcm[stream].be_start--;
21122145
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
21132146

2114-
if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
2147+
if (!do_trigger)
21152148
continue;
21162149

21172150
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,
21232156
if (ret) {
21242157
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
21252158
be->dpcm[stream].state = state;
2159+
be->dpcm[stream].be_start++;
21262160
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
21272161
goto end;
21282162
}
@@ -2134,9 +2168,12 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
21342168
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
21352169
continue;
21362170
}
2171+
if (be->dpcm[stream].be_start == 1)
2172+
do_trigger = true;
2173+
be->dpcm[stream].be_start--;
21372174
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
21382175

2139-
if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
2176+
if (!do_trigger)
21402177
continue;
21412178

21422179
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,
21482185
if (ret) {
21492186
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
21502187
be->dpcm[stream].state = state;
2188+
be->dpcm[stream].be_start++;
21512189
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
21522190
goto end;
21532191
}

0 commit comments

Comments
 (0)