Repository navigation
ASoC: DPCM fixes - #3188
ASoC: DPCM fixes#3188plbossart wants to merge 6 commits into
Conversation
This function is not used anywhere, including soc-pcm.c Remove dead code. Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
No one uses the following functions outside of soc-pcm.c snd_soc_dpcm_can_be_free_stop() snd_soc_dpcm_can_be_params() snd_soc_dpcm_be_can_update() In preparation for locking changes, move them to static functions and remove the EXPORT_SYMBOL_GPL() Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
It's not clear why a spinlock was used, and even less why the 'dpcm_lock' is used with the irqsave/irqrestore: DPCM functions are typically not used in interrupt context. Move to a mutex which will allow us to sleep for longer periods of time and handle non-atomic triggers. Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
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. The code carefully checks when the BE can be stopped or hw_free'ed, but the trigger code does not use any mutual exclusion. This can be fixed by using the dpcm_mutex. As discussed on the alsa-mailing list [1], there is an additional issue that the BEs can be disconnected dynamically, leading to potential accesses to freed memory. This patch suggests a general protection of the for_each_dpcm_be() loop with the dpcm_mutex, to solve both the problem of multiple triggers and BE dynamic addition/removal. [1] https://lore.kernel.org/alsa-devel/002f01d7b4f5$c030f4a0$4092dde0$@samsung.com/ Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
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 mutex. Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
A BE connected to more than one FE, e.g. in a mixer case, can go through the following transitions. play FE1 -> BE state is START pause FE1 -> BE state is PAUSED play FE2 -> BE state is START stop FE2 -> BE state is STOP (see note [1] below) release FE1 -> BE state is START stop FE1 -> BE state is STOP play FE1 -> BE state is START pause FE1 -> BE state is PAUSED play FE2 -> BE state is START release FE1 -> BE state is START stop FE2 -> BE state is START stop FE1 -> BE state is STOP The existing code for PAUSE_RELEASE only allows for the case where the BE is paused, which clearly would not work in the sequences above. Extend the allowed states to restart the BE when PAUSE_RELEASE is received. [1] the existing logic does not move the BE state back to PAUSED when the FE2 is stopped. This patch does not change the logic; it would be painful to keep a history of changes on the FE side, the state machine is already rather complicated with transitions based on the last BE state and the trigger type. Reported-by: Bard Liao <bard.liao@intel.com> Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
b9f58b0 to
2b96dc2
Compare
|
sent as upstream RFC - no issues detected in validation so far, apart from alsa-bat settings that need to be updated for the new topologies. |
| be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; | ||
| break; | ||
| case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
| if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) |
There was a problem hiding this comment.
how about
if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED) || be->dpcm[stream].be_start)
continue
and remove " if (be->dpcm[stream].be_start != 1) "
There was a problem hiding this comment.
@RanderWang your proposal mixes state handling and refcount handling. I think it's better to keep the two parts separate for clarity. The goal is not to have minimalistic code, it's really to make the state management clearer.
There was a problem hiding this comment.
sure. I don't like
if (be->dpcm[stream].be_start != 1)
how about
if (be->dpcm[stream].be_start > 1) // multiple call
continue;
There was a problem hiding this comment.
@RanderWang IMHO, I like if (be->dpcm[stream].be_start != 0) and if (be->dpcm[stream].be_start != 1). which says that the code is only executed when be_start is 0 or 1 exactly.
|
replaced by PR #3209 |
This version is a lot simpler/consistent than previous attempts, let's start reviewing it.
I need to double-check results with SoundWire/HDaudio in deep-buffer mode, but it's ready for internal reviews.