Skip to content

Commit

Permalink
ALSA: Rewrite hw_ptr updaters
Browse files Browse the repository at this point in the history
Clean up and improve snd_pcm_update_hw_ptr*() functions.

snd_pcm_update_hw_ptr() tries to detect the unexpected hwptr jumps
more strictly to avoid the position mess-up, which often results in
the bad quality I/O with pulseaudio.

The hw-ptr skip error messages are printed when xrun proc is set to
non-zero.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
  • Loading branch information
Takashi Iwai committed Mar 9, 2009
1 parent 2450cf5 commit ed3da3d
Showing 1 changed file with 83 additions and 45 deletions.
128 changes: 83 additions & 45 deletions sound/core/pcm_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,19 +125,27 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
}
}

#ifdef CONFIG_SND_PCM_XRUN_DEBUG
#define xrun_debug(substream) ((substream)->pstr->xrun_debug)
#else
#define xrun_debug(substream) 0
#endif

#define dump_stack_on_xrun(substream) do { \
if (xrun_debug(substream) > 1) \
dump_stack(); \
} while (0)

static void xrun(struct snd_pcm_substream *substream)
{
snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
if (substream->pstr->xrun_debug) {
if (xrun_debug(substream)) {
snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n",
substream->pcm->card->number,
substream->pcm->device,
substream->stream ? 'c' : 'p');
if (substream->pstr->xrun_debug > 1)
dump_stack();
dump_stack_on_xrun(substream);
}
#endif
}

static inline snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream,
Expand Down Expand Up @@ -182,48 +190,69 @@ static inline int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream
return 0;
}

#define hw_ptr_error(substream, fmt, args...) \
do { \
if (xrun_debug(substream)) { \
if (printk_ratelimit()) { \
snd_printd("hda_codec: " fmt, ##args); \
} \
dump_stack_on_xrun(substream); \
} \
} while (0)

static inline int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t pos;
snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt;
snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt, hw_base;
snd_pcm_sframes_t delta;

pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
if (pos == SNDRV_PCM_POS_XRUN) {
xrun(substream);
return -EPIPE;
}
if (runtime->period_size == runtime->buffer_size)
goto __next_buf;
new_hw_ptr = runtime->hw_ptr_base + pos;
hw_base = runtime->hw_ptr_base;
new_hw_ptr = hw_base + pos;
hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size;

delta = hw_ptr_interrupt - new_hw_ptr;
if (delta > 0) {
if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) {
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
if (runtime->periods > 1 && substream->pstr->xrun_debug) {
snd_printd(KERN_ERR "Unexpected hw_pointer value [1] (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2);
if (substream->pstr->xrun_debug > 1)
dump_stack();
}
#endif
return 0;
delta = new_hw_ptr - hw_ptr_interrupt;
if (hw_ptr_interrupt == runtime->boundary)
hw_ptr_interrupt = 0;
if (delta < 0) {
delta += runtime->buffer_size;
if (delta < 0) {
hw_ptr_error(substream,
"Unexpected hw_pointer value "
"(stream=%i, pos=%ld, intr_ptr=%ld)\n",
substream->stream, (long)pos,
(long)hw_ptr_interrupt);
/* rebase to interrupt position */
hw_base = new_hw_ptr = hw_ptr_interrupt;
delta = 0;
} else {
hw_base += runtime->buffer_size;
if (hw_base == runtime->boundary)
hw_base = 0;
new_hw_ptr = hw_base + pos;
}
__next_buf:
runtime->hw_ptr_base += runtime->buffer_size;
if (runtime->hw_ptr_base == runtime->boundary)
runtime->hw_ptr_base = 0;
new_hw_ptr = runtime->hw_ptr_base + pos;
}

if (delta > runtime->period_size) {
hw_ptr_error(substream,
"Lost interrupts? "
"(stream=%i, delta=%ld, intr_ptr=%ld)\n",
substream->stream, (long)delta,
(long)hw_ptr_interrupt);
/* rebase hw_ptr_interrupt */
hw_ptr_interrupt =
new_hw_ptr - new_hw_ptr % runtime->period_size;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0)
snd_pcm_playback_silence(substream, new_hw_ptr);

runtime->hw_ptr_base = hw_base;
runtime->status->hw_ptr = new_hw_ptr;
runtime->hw_ptr_interrupt = new_hw_ptr - new_hw_ptr % runtime->period_size;
runtime->hw_ptr_interrupt = hw_ptr_interrupt;

return snd_pcm_update_hw_ptr_post(substream, runtime);
}
Expand All @@ -233,7 +262,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t pos;
snd_pcm_uframes_t old_hw_ptr, new_hw_ptr;
snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
snd_pcm_sframes_t delta;

old_hw_ptr = runtime->status->hw_ptr;
Expand All @@ -242,29 +271,38 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
xrun(substream);
return -EPIPE;
}
new_hw_ptr = runtime->hw_ptr_base + pos;

delta = old_hw_ptr - new_hw_ptr;
if (delta > 0) {
if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) {
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
if (runtime->periods > 2 && substream->pstr->xrun_debug) {
snd_printd(KERN_ERR "Unexpected hw_pointer value [2] (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2);
if (substream->pstr->xrun_debug > 1)
dump_stack();
}
#endif
hw_base = runtime->hw_ptr_base;
new_hw_ptr = hw_base + pos;

delta = new_hw_ptr - old_hw_ptr;
if (delta < 0) {
delta += runtime->buffer_size;
if (delta < 0) {
hw_ptr_error(substream,
"Unexpected hw_pointer value [2] "
"(stream=%i, pos=%ld, old_ptr=%ld)\n",
substream->stream, (long)pos,
(long)old_hw_ptr);
return 0;
}
runtime->hw_ptr_base += runtime->buffer_size;
if (runtime->hw_ptr_base == runtime->boundary)
runtime->hw_ptr_base = 0;
new_hw_ptr = runtime->hw_ptr_base + pos;
hw_base += runtime->buffer_size;
if (hw_base == runtime->boundary)
hw_base = 0;
new_hw_ptr = hw_base + pos;
}
if (delta > runtime->period_size && runtime->periods > 1) {
hw_ptr_error(substream,
"hw_ptr skipping! "
"(pos=%ld, delta=%ld, period=%ld)\n",
(long)pos, (long)delta,
(long)runtime->period_size);
return 0;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0)
snd_pcm_playback_silence(substream, new_hw_ptr);

runtime->hw_ptr_base = hw_base;
runtime->status->hw_ptr = new_hw_ptr;

return snd_pcm_update_hw_ptr_post(substream, runtime);
Expand Down

0 comments on commit ed3da3d

Please sign in to comment.