Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 the BE PCM
stream lock.

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Link: https://lore.kernel.org/r/20211207173745.15850-6-pierre-louis.bossart@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Pierre-Louis Bossart and committed by
Mark Brown
848aedfd b2ae8066

+47 -14
+2
include/sound/soc-dpcm.h
··· 101 101 enum snd_soc_dpcm_state state; 102 102 103 103 int trigger_pending; /* trigger cmd + 1 if pending, 0 if not */ 104 + 105 + int be_start; /* refcount protected by BE stream pcm lock */ 104 106 }; 105 107 106 108 #define for_each_dpcm_fe(be, stream, _dpcm) \
+45 -14
sound/soc/soc-pcm.c
··· 1619 1619 be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; 1620 1620 goto unwind; 1621 1621 } 1622 - 1622 + be->dpcm[stream].be_start = 0; 1623 1623 be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; 1624 1624 count++; 1625 1625 } ··· 2105 2105 2106 2106 switch (cmd) { 2107 2107 case SNDRV_PCM_TRIGGER_START: 2108 - if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && 2108 + if (!be->dpcm[stream].be_start && 2109 + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && 2109 2110 (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) && 2110 2111 (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) 2111 2112 goto next; 2112 2113 2113 - ret = soc_pcm_trigger(be_substream, cmd); 2114 - if (ret) 2114 + be->dpcm[stream].be_start++; 2115 + if (be->dpcm[stream].be_start != 1) 2115 2116 goto next; 2117 + 2118 + ret = soc_pcm_trigger(be_substream, cmd); 2119 + if (ret) { 2120 + be->dpcm[stream].be_start--; 2121 + goto next; 2122 + } 2116 2123 2117 2124 be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; 2118 2125 break; ··· 2127 2120 if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND)) 2128 2121 goto next; 2129 2122 2130 - ret = soc_pcm_trigger(be_substream, cmd); 2131 - if (ret) 2123 + be->dpcm[stream].be_start++; 2124 + if (be->dpcm[stream].be_start != 1) 2132 2125 goto next; 2126 + 2127 + ret = soc_pcm_trigger(be_substream, cmd); 2128 + if (ret) { 2129 + be->dpcm[stream].be_start--; 2130 + goto next; 2131 + } 2133 2132 2134 2133 be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; 2135 2134 break; ··· 2143 2130 if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) 2144 2131 goto next; 2145 2132 2146 - ret = soc_pcm_trigger(be_substream, cmd); 2147 - if (ret) 2133 + be->dpcm[stream].be_start++; 2134 + if (be->dpcm[stream].be_start != 1) 2148 2135 goto next; 2136 + 2137 + ret = soc_pcm_trigger(be_substream, cmd); 2138 + if (ret) { 2139 + be->dpcm[stream].be_start--; 2140 + goto next; 2141 + } 2149 2142 2150 2143 be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; 2151 2144 break; ··· 2160 2141 (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) 2161 2142 goto next; 2162 2143 2163 - if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 2144 + if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_START) 2145 + be->dpcm[stream].be_start--; 2146 + 2147 + if (be->dpcm[stream].be_start != 0) 2164 2148 goto next; 2165 2149 2166 2150 ret = soc_pcm_trigger(be_substream, cmd); 2167 - if (ret) 2151 + if (ret) { 2152 + if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_START) 2153 + be->dpcm[stream].be_start++; 2168 2154 goto next; 2155 + } 2169 2156 2170 2157 be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; 2171 2158 break; ··· 2179 2154 if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) 2180 2155 goto next; 2181 2156 2182 - if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 2157 + be->dpcm[stream].be_start--; 2158 + if (be->dpcm[stream].be_start != 0) 2183 2159 goto next; 2184 2160 2185 2161 ret = soc_pcm_trigger(be_substream, cmd); 2186 - if (ret) 2162 + if (ret) { 2163 + be->dpcm[stream].be_start++; 2187 2164 goto next; 2165 + } 2188 2166 2189 2167 be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND; 2190 2168 break; ··· 2195 2167 if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) 2196 2168 goto next; 2197 2169 2198 - if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 2170 + be->dpcm[stream].be_start--; 2171 + if (be->dpcm[stream].be_start != 0) 2199 2172 goto next; 2200 2173 2201 2174 ret = soc_pcm_trigger(be_substream, cmd); 2202 - if (ret) 2175 + if (ret) { 2176 + be->dpcm[stream].be_start++; 2203 2177 goto next; 2178 + } 2204 2179 2205 2180 be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; 2206 2181 break;