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 <gt82.lee@samsung.com> Acked-by: Vinod Koul <vkoul@kernel.org> Link: https://lore.kernel.org/r/000101d6c3f0$89b312b0$9d193810$@samsung.com Signed-off-by: Takashi Iwai <tiwai@suse.de> (cherry picked from commit 9be9f2d3d073ef42127475f4fb6a392ab133f629) [quic_c_smanag@quicinc.com: ported patch in abi safe way] Signed-off-by: Soumya Managoli <quic_c_smanag@quicinc.com>
This commit is contained in:
parent
a8427caea3
commit
42ae17e6f4
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user