diff mbox series

[RESEND] ALSA: dmaengine_pcm: terminate dmaengine before synchronize

Message ID 1718851218-27803-1-git-send-email-shengjiu.wang@nxp.com
State Accepted
Commit 6a7db25aad8ce6512b366d2ce1d0e60bac00a09d
Headers show
Series [RESEND] ALSA: dmaengine_pcm: terminate dmaengine before synchronize | expand

Commit Message

Shengjiu Wang June 20, 2024, 2:40 a.m. UTC
When dmaengine supports pause function, in suspend state,
dmaengine_pause() is called instead of dmaengine_terminate_async(),

In end of playback stream, the runtime->state will go to
SNDRV_PCM_STATE_DRAINING, if system suspend & resume happen
at this time, application will not resume playback stream, the
stream will be closed directly, the dmaengine_terminate_async()
will not be called before the dmaengine_synchronize(), which
violates the call sequence for dmaengine_synchronize().

This behavior also happens for capture streams, but there is no
SNDRV_PCM_STATE_DRAINING state for capture. So use
dmaengine_tx_status() to check the DMA status if the status is
DMA_PAUSED, then call dmaengine_terminate_async() to terminate
dmaengine before dmaengine_synchronize().

Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
changes in resend:
- update the commit header

 sound/core/pcm_dmaengine.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

Comments

Takashi Iwai June 24, 2024, 12:39 p.m. UTC | #1
On Fri, 21 Jun 2024 04:21:19 +0200,
Shengjiu Wang wrote:
> 
> On Thu, Jun 20, 2024 at 3:56 PM Takashi Iwai <tiwai@suse.de> wrote:
> > But, this may need more rework, too; admittedly it imposes the
> > unnecessary resume of the stream although it shall be stopped and
> > closed immediately after that.  We may have some optimization in
> > addition.
> 
> The suspended_state is not cleared that the resume may be called again
> at the end of stream.

Right, that's the known side effect of this approach.

> Will you push the code?

I'm rethinking of this again, and I'm inclined rather to take your
patch for now.  The side-effect above would be much higher impact in
theory, so I'm not quite sure whether the behavior is acceptable.

Basically, a missing piece is the shortcut state change from SUSPENDED
to CLOSED.  Most drivers don't care as the SUSPENDED state is almost
equivalent with STOPPED state, and they don't support RESUME (hence
the application needs to re-initialize via PREPARE).  But a case like
dmaengine, there can be inconsistency as you pointed out.

By putting snd_pcm_resume() at the beginning of close procedure like
my patch, the state change itself is corrected.  However, it imposes
unnecessary resume, which should be avoided.

Ultimately, we may need some flag or conditional trigger for clearing
this pending SUSPENDED state.  But it needs further consideration.


thanks,

Takashi
Takashi Iwai June 25, 2024, 11:26 a.m. UTC | #2
On Mon, 24 Jun 2024 14:39:12 +0200,
Takashi Iwai wrote:
> I'm rethinking of this again, and I'm inclined rather to take your
> patch for now.

Now I merged the patch to for-linus branch as is.


thanks,

Takashi
diff mbox series

Patch

diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c
index ed07fa5693d2..cc5db93b9132 100644
--- a/sound/core/pcm_dmaengine.c
+++ b/sound/core/pcm_dmaengine.c
@@ -368,6 +368,12 @@  EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_sync_stop);
 int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream)
 {
 	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+	struct dma_tx_state state;
+	enum dma_status status;
+
+	status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state);
+	if (status == DMA_PAUSED)
+		dmaengine_terminate_async(prtd->dma_chan);
 
 	dmaengine_synchronize(prtd->dma_chan);
 	kfree(prtd);
@@ -388,6 +394,12 @@  EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close);
 int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream)
 {
 	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+	struct dma_tx_state state;
+	enum dma_status status;
+
+	status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state);
+	if (status == DMA_PAUSED)
+		dmaengine_terminate_async(prtd->dma_chan);
 
 	dmaengine_synchronize(prtd->dma_chan);
 	dma_release_channel(prtd->dma_chan);