From 42ae17e6f4910e12a82d6fc25ab33b32a88967f0 Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Wed, 27 Sep 2023 15:52:56 +0530 Subject: [PATCH] BACKPORT: ALSA: compress: Allow pause and resume during draining With a stream with low bitrate, user can't pause or resume the stream near the end of the stream because current ALSA doesn't allow it. If the stream has very low bitrate enough to store whole stream into the buffer, user can't do anything except stop the stream and then restart it from the first because most of applications call draining after sending last frame to the kernel. If pause, resume are allowed during draining, user experience can be enhanced. To prevent malfunction in HW drivers which don't support pause during draining, pause during draining will only work if HW driver enable this feature explicitly by calling snd_compr_use_pause_in_draining(). Bug: 307192739 Change-Id: Ie40e6131746f8ee780e38f7f876622b407b84a75 Signed-off-by: Gyeongtaek Lee Acked-by: Vinod Koul Link: https://lore.kernel.org/r/000101d6c3f0$89b312b0$9d193810$@samsung.com Signed-off-by: Takashi Iwai (cherry picked from commit 9be9f2d3d073ef42127475f4fb6a392ab133f629) [quic_c_smanag@quicinc.com: ported patch in abi safe way] Signed-off-by: Soumya Managoli --- include/sound/compress_driver.h | 1 + sound/core/compress_offload.c | 62 ++++++++++++++++++++++++++++----- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h index bc88d6f964da9..5ff6b96cfb6ac 100644 --- a/include/sound/compress_driver.h +++ b/include/sound/compress_driver.h @@ -198,4 +198,5 @@ static inline void snd_compr_set_runtime_buffer( int snd_compr_stop_error(struct snd_compr_stream *stream, snd_pcm_state_t state); +void snd_compr_use_pause_in_draining(struct snd_compr_stream *stream); #endif diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index f34ce564d92c4..201b98587871a 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -51,6 +51,8 @@ static DEFINE_MUTEX(device_mutex); struct snd_compr_file { unsigned long caps; + bool use_pause_in_draining; + bool pause_in_draining; struct snd_compr_stream stream; }; @@ -115,6 +117,8 @@ static int snd_compr_open(struct inode *inode, struct file *f) INIT_DELAYED_WORK(&data->stream.error_work, error_delayed_work); + data->use_pause_in_draining = false; + data->pause_in_draining = false; data->stream.ops = compr->ops; data->stream.direction = dirn; data->stream.private_data = compr->private_data; @@ -662,27 +666,67 @@ snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg) return ret; } +/** + * snd_compr_use_pause_in_draining - Allow pause and resume in draining state + * @stream: compress substream to set + * + * Allow pause and resume in draining state. + * Only HW driver supports this transition can call this API. + */ +void snd_compr_use_pause_in_draining(struct snd_compr_stream *stream) +{ + struct snd_compr_file *scf = container_of(stream, struct snd_compr_file, stream); + + scf->use_pause_in_draining = true; +} +EXPORT_SYMBOL(snd_compr_use_pause_in_draining); + static int snd_compr_pause(struct snd_compr_stream *stream) { int retval; + struct snd_compr_file *scf = container_of(stream, struct snd_compr_file, stream); - if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) + switch (stream->runtime->state) { + case SNDRV_PCM_STATE_RUNNING: + retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); + if (!retval) + stream->runtime->state = SNDRV_PCM_STATE_PAUSED; + break; + case SNDRV_PCM_STATE_DRAINING: + if (!scf->use_pause_in_draining) + return -EPERM; + + retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); + if (!retval) + scf->pause_in_draining = true; + break; + default: return -EPERM; - retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); - if (!retval) - stream->runtime->state = SNDRV_PCM_STATE_PAUSED; + } return retval; } static int snd_compr_resume(struct snd_compr_stream *stream) { int retval; + struct snd_compr_file *scf = container_of(stream, struct snd_compr_file, stream); - if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED) + switch (stream->runtime->state) { + case SNDRV_PCM_STATE_PAUSED: + retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE); + if (!retval) + stream->runtime->state = SNDRV_PCM_STATE_RUNNING; + break; + case SNDRV_PCM_STATE_DRAINING: + if (!scf->pause_in_draining) + return -EPERM; + retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE); + if (!retval) + scf->pause_in_draining = false; + break; + default: return -EPERM; - retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE); - if (!retval) - stream->runtime->state = SNDRV_PCM_STATE_RUNNING; + } return retval; } @@ -710,6 +754,7 @@ static int snd_compr_start(struct snd_compr_stream *stream) static int snd_compr_stop(struct snd_compr_stream *stream) { int retval; + struct snd_compr_file *scf = container_of(stream, struct snd_compr_file, stream); switch (stream->runtime->state) { case SNDRV_PCM_STATE_OPEN: @@ -722,6 +767,7 @@ static int snd_compr_stop(struct snd_compr_stream *stream) retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); if (!retval) { + scf->pause_in_draining = false; snd_compr_drain_notify(stream); stream->runtime->total_bytes_available = 0; stream->runtime->total_bytes_transferred = 0;