diff options
author | vijay mali <vmali@nvidia.com> | 2010-10-04 18:32:41 +0530 |
---|---|---|
committer | Bharat Nihalani <bnihalani@nvidia.com> | 2010-10-05 02:33:48 -0700 |
commit | fd2d97158a28206e49dd0b40334fa486283ea451 (patch) | |
tree | 945d7b9ceb1145ff4991be641a53b594113cde04 /sound | |
parent | aae38757d090a317782df6247ff7fe0f802a21ff (diff) |
[tegra ALSA] Fix delay in terminating playback
aplay app and dmix plugin are behaving differently with ALSA
kernel. aplay app modifies appl_ptr on every new buffer.
dmix plugin does not update appl_ptr for new buffers. To
handle these cases play_thread function is split in to three
small functions and checks are added to identify if appl_ptr
gets updated.
For Bug 741242
Change-Id: I24f621a98ba1948dd3f4ae047f7ab407ff473c7b
Reviewed-on: http://git-master/r/7751
Tested-by: Vijay Mali <vmali@nvidia.com>
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/tegra/tegra_pcm_rpc.c | 237 |
1 files changed, 144 insertions, 93 deletions
diff --git a/sound/soc/tegra/tegra_pcm_rpc.c b/sound/soc/tegra/tegra_pcm_rpc.c index 02cdafebab24..b15244018f29 100644 --- a/sound/soc/tegra/tegra_pcm_rpc.c +++ b/sound/soc/tegra/tegra_pcm_rpc.c @@ -59,87 +59,167 @@ static int tegra_pcm_prepare(struct snd_pcm_substream *substream) return 0; } +static inline NvAudioFxState play_state(struct pcm_runtime_data *prtd, + NvAudioFxState cur_state) +{ + NvAudioFxState state = cur_state; + + switch (prtd->state) { + case SNDRV_PCM_TRIGGER_START: + if (state != NvAudioFxState_Run) { + state = NvAudioFxState_Run; + tegra_snd_cx->xrt_fxn.SetProperty( + prtd->stdoutpath->Stream, + NvAudioFxProperty_State, + sizeof(NvAudioFxState), + &state); + } + break; + case SNDRV_PCM_TRIGGER_STOP: + if (state != NvAudioFxState_Stop) { + state = NvAudioFxState_Stop; + tegra_snd_cx->xrt_fxn.SetProperty( + prtd->stdoutpath->Stream, + NvAudioFxProperty_State, + sizeof(NvAudioFxState), + &state); + down(&prtd->stop_done_sem); + } + break; + default: + ; + } + + return state; +} + +static inline int queue_next_buffer(void *arg, int cur_offset) +{ + struct snd_pcm_substream *substream = (struct snd_pcm_substream *)arg; + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_runtime_data *prtd = substream->runtime->private_data; + int offset = cur_offset; + int size, rtbuffersize; + NvAudioFxBufferDescriptor abd; + + rtbuffersize = frames_to_bytes(runtime, runtime->buffer_size); + memset(&abd, 0, sizeof(NvAudioFxBufferDescriptor)); + size = TEGRA_DEFAULT_BUFFER_SIZE; + if ((offset + size) > rtbuffersize) { + size = rtbuffersize - offset; + } + + abd.hMixBuffer = prtd->mixer_buffer; + abd.Offset = offset; + abd.Size = size; + abd.Format.FormatTag = 1; + abd.Format.SampleRate = runtime->rate; + abd.Format.BitsPerSample = runtime->sample_bits; + abd.Format.Channels = runtime->channels; + abd.Format.ChannelMask = 0; + abd.Format.ValidBitsPerSample = 0; + + tegra_snd_cx->xrt_fxn.StreamAddBuffer( + (NvAudioFxStreamHandle)prtd->stdoutpath->Stream, + &abd); + + offset += size; + if (offset >= rtbuffersize) + offset =0; + + prtd->audiofx_frames += bytes_to_frames(runtime, size); + + return offset; +} + +static inline void play_buffer_done (void *arg) +{ + struct snd_pcm_substream *substream = (struct snd_pcm_substream *)arg; + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_runtime_data *prtd = substream->runtime->private_data; + int size, period_offset, rtbuffersize; + + rtbuffersize = frames_to_bytes(runtime, runtime->buffer_size); + down(&prtd->buf_done_sem); + + if ((frames_to_bytes(runtime, prtd->cur_pos) + + TEGRA_DEFAULT_BUFFER_SIZE) > rtbuffersize) { + size = rtbuffersize - + frames_to_bytes(runtime, prtd->cur_pos); + } else { + size = TEGRA_DEFAULT_BUFFER_SIZE; + } + + prtd->cur_pos += bytes_to_frames(runtime, size); + + if (prtd->cur_pos < prtd->last_pos) { + period_offset = (runtime->buffer_size + + prtd->cur_pos) - prtd->last_pos; + } else { + period_offset = prtd->cur_pos - prtd->last_pos; + } + + if (period_offset >= runtime->period_size) { + prtd->last_pos = prtd->cur_pos; + snd_pcm_period_elapsed(substream); + } + + if (prtd->cur_pos >= runtime->buffer_size) { + prtd->cur_pos -= runtime->buffer_size; + } +} + static int play_thread( void *arg) { - struct snd_pcm_substream *substream = arg; + struct snd_pcm_substream *substream = (struct snd_pcm_substream *)arg; struct snd_pcm_runtime *runtime = substream->runtime; struct pcm_runtime_data *prtd = substream->runtime->private_data; - NvError e; - int size = 0; int offset = 0; - int period_offset = 0; int rtbuffersize = 0; int buffer_to_prime = 0, buffer_in_queue = 0; - NvAudioFxBufferDescriptor abd; NvAudioFxState state = NVALSA_INVALID_STATE; wait_for_completion(&prtd->thread_comp); - rtbuffersize = frames_to_bytes(runtime, runtime->buffer_size); buffer_to_prime = (rtbuffersize / TEGRA_DEFAULT_BUFFER_SIZE); + if (runtime->control->appl_ptr) for (;;) { - switch (prtd->state) { - case SNDRV_PCM_TRIGGER_START: - if (state != NvAudioFxState_Run) { - state = NvAudioFxState_Run; - tegra_snd_cx->xrt_fxn.SetProperty( - prtd->stdoutpath->Stream, - NvAudioFxProperty_State, - sizeof(NvAudioFxState), - &state); - } - break; - case SNDRV_PCM_TRIGGER_STOP: - if (state != NvAudioFxState_Stop) { - state = NvAudioFxState_Stop; - tegra_snd_cx->xrt_fxn.SetProperty( - prtd->stdoutpath->Stream, - NvAudioFxProperty_State, - sizeof(NvAudioFxState), - &state); - down(&prtd->stop_done_sem); - buffer_in_queue = 0; - } - break; - default: - ; + state = play_state(prtd, state); + if (state == SNDRV_PCM_TRIGGER_STOP) + buffer_in_queue = 0; + if (kthread_should_stop()) + return 0; + if ((prtd->audiofx_frames < runtime->control->appl_ptr) && + (state != NvAudioFxState_Stop)) { + offset = queue_next_buffer(substream, offset); + buffer_in_queue++; + } + if (buffer_in_queue == 0) { + DEFINE_WAIT(wq); + prepare_to_wait(&prtd->buf_wait, &wq, TASK_INTERRUPTIBLE); + schedule(); + finish_wait(&prtd->buf_wait, &wq); + continue; } + if ((buffer_to_prime == buffer_in_queue) || + (prtd->audiofx_frames >= runtime->control->appl_ptr)) { + play_buffer_done(substream); + buffer_in_queue--; + } + } + for (;;) { + state = play_state(prtd, state); + if (state == SNDRV_PCM_TRIGGER_STOP) + buffer_in_queue = 0; if (kthread_should_stop()) break; - if ((prtd->state != SNDRV_PCM_TRIGGER_STOP) && - (runtime->status->state != SNDRV_PCM_STATE_DRAINING )) { - memset(&abd, 0, sizeof(NvAudioFxBufferDescriptor)); - - size = TEGRA_DEFAULT_BUFFER_SIZE; - if ((offset + size) > rtbuffersize) { - size = rtbuffersize - offset; - } - - abd.hMixBuffer = prtd->mixer_buffer; - abd.Offset = offset; - abd.Size = size; - abd.Format.FormatTag = 1; - abd.Format.SampleRate = runtime->rate; - abd.Format.BitsPerSample = runtime->sample_bits; - abd.Format.Channels = runtime->channels; - abd.Format.ChannelMask = 0; - abd.Format.ValidBitsPerSample = 0; - - e = tegra_snd_cx->xrt_fxn.StreamAddBuffer( - (NvAudioFxStreamHandle)prtd->stdoutpath->Stream, - &abd); + (runtime->status->state != SNDRV_PCM_STATE_DRAINING)) { + offset = queue_next_buffer(substream, offset); buffer_in_queue++; - offset += size; - if (offset >= rtbuffersize) - offset =0; - - prtd->audiofx_frames += bytes_to_frames(runtime, - size); } - if (buffer_in_queue == 0) { DEFINE_WAIT(wq); prepare_to_wait(&prtd->buf_wait, &wq, TASK_INTERRUPTIBLE); @@ -147,39 +227,10 @@ static int play_thread( void *arg) finish_wait(&prtd->buf_wait, &wq); continue; } - if ((buffer_to_prime == buffer_in_queue) && - (prtd->state != SNDRV_PCM_TRIGGER_STOP)) { - down(&prtd->buf_done_sem); - + (prtd->state != SNDRV_PCM_TRIGGER_STOP)) { + play_buffer_done(substream); buffer_in_queue--; - - if ((frames_to_bytes(runtime, prtd->cur_pos) + - TEGRA_DEFAULT_BUFFER_SIZE) > rtbuffersize) { - size = rtbuffersize - - frames_to_bytes(runtime, prtd->cur_pos); - } else { - size = TEGRA_DEFAULT_BUFFER_SIZE; - } - - prtd->cur_pos += bytes_to_frames(runtime, size); - - if (prtd->cur_pos < prtd->last_pos) { - period_offset = (runtime->buffer_size + - prtd->cur_pos) - prtd->last_pos; - - } else { - period_offset = prtd->cur_pos - prtd->last_pos; - } - - if (period_offset >= runtime->period_size) { - prtd->last_pos = prtd->cur_pos; - snd_pcm_period_elapsed(substream); - } - - if (prtd->cur_pos >= runtime->buffer_size) { - prtd->cur_pos -= runtime->buffer_size; - } } } return 0; @@ -353,7 +404,7 @@ static int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { prtd->timeout = PLAY_TIMEOUT; - }else{ + } else { prtd->timeout = REC_TIMEOUT; } break; @@ -416,7 +467,7 @@ static int pcm_common_close(struct snd_pcm_substream *substream) memset(&message, 0, sizeof(NvAudioFxMessage)); message.Event = NvAudioFxEventAll; message.pContext = NULL; - if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) message.hFx = (NvAudioFxHandle)prtd->stdoutpath->Stream; else message.hFx = (NvAudioFxHandle)prtd->stdinpath->Stream; |