diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h index bc88d6f964da..5ff6b96cfb6a 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 f34ce564d92c..201b98587871 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;