| 1 |
From: Takashi Iwai <tiwai@suse.de>
|
| 2 |
Date: Tue, 3 Mar 2009 16:00:15 +0000 (+0100)
|
| 3 |
Subject: ALSA: Rewrite hw_ptr updaters
|
| 4 |
X-Git-Url: http://git.alsa-project.org/?p=alsa-kernel.git;a=commitdiff_plain;h=070397989dc29a6067cb51989ab3d5efc82a6790
|
| 5 |
|
| 6 |
ALSA: Rewrite hw_ptr updaters
|
| 7 |
|
| 8 |
Clean up and improve snd_pcm_update_hw_ptr*() functions.
|
| 9 |
|
| 10 |
snd_pcm_update_hw_ptr() tries to detect the unexpected hwptr jumps
|
| 11 |
more strictly to avoid the position mess-up, which often results in
|
| 12 |
the bad quality I/O with pulseaudio.
|
| 13 |
|
| 14 |
The hw-ptr skip error messages are printed when xrun proc is set to
|
| 15 |
non-zero.
|
| 16 |
|
| 17 |
Signed-off-by: Takashi Iwai <tiwai@suse.de>
|
| 18 |
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
|
| 19 |
---
|
| 20 |
|
| 21 |
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
|
| 22 |
index 9216910..86ac9ae 100644
|
| 23 |
--- a/sound/core/pcm_lib.c
|
| 24 |
+++ b/sound/core/pcm_lib.c
|
| 25 |
@@ -125,19 +125,27 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
|
| 26 |
}
|
| 27 |
}
|
| 28 |
|
| 29 |
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
|
| 30 |
+#define xrun_debug(substream) ((substream)->pstr->xrun_debug)
|
| 31 |
+#else
|
| 32 |
+#define xrun_debug(substream) 0
|
| 33 |
+#endif
|
| 34 |
+
|
| 35 |
+#define dump_stack_on_xrun(substream) do { \
|
| 36 |
+ if (xrun_debug(substream) > 1) \
|
| 37 |
+ dump_stack(); \
|
| 38 |
+ } while (0)
|
| 39 |
+
|
| 40 |
static void xrun(struct snd_pcm_substream *substream)
|
| 41 |
{
|
| 42 |
snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
|
| 43 |
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
|
| 44 |
- if (substream->pstr->xrun_debug) {
|
| 45 |
+ if (xrun_debug(substream)) {
|
| 46 |
snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n",
|
| 47 |
substream->pcm->card->number,
|
| 48 |
substream->pcm->device,
|
| 49 |
substream->stream ? 'c' : 'p');
|
| 50 |
- if (substream->pstr->xrun_debug > 1)
|
| 51 |
- dump_stack();
|
| 52 |
+ dump_stack_on_xrun(substream);
|
| 53 |
}
|
| 54 |
-#endif
|
| 55 |
}
|
| 56 |
|
| 57 |
static inline snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream,
|
| 58 |
@@ -182,11 +190,21 @@ static inline int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream
|
| 59 |
return 0;
|
| 60 |
}
|
| 61 |
|
| 62 |
+#define hw_ptr_error(substream, fmt, args...) \
|
| 63 |
+ do { \
|
| 64 |
+ if (xrun_debug(substream)) { \
|
| 65 |
+ if (printk_ratelimit()) { \
|
| 66 |
+ snd_printd("hda_codec: " fmt, ##args); \
|
| 67 |
+ } \
|
| 68 |
+ dump_stack_on_xrun(substream); \
|
| 69 |
+ } \
|
| 70 |
+ } while (0)
|
| 71 |
+
|
| 72 |
static inline int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
|
| 73 |
{
|
| 74 |
struct snd_pcm_runtime *runtime = substream->runtime;
|
| 75 |
snd_pcm_uframes_t pos;
|
| 76 |
- snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt;
|
| 77 |
+ snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt, hw_base;
|
| 78 |
snd_pcm_sframes_t delta;
|
| 79 |
|
| 80 |
pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
|
| 81 |
@@ -194,36 +212,47 @@ static inline int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *subs
|
| 82 |
xrun(substream);
|
| 83 |
return -EPIPE;
|
| 84 |
}
|
| 85 |
- if (runtime->period_size == runtime->buffer_size)
|
| 86 |
- goto __next_buf;
|
| 87 |
- new_hw_ptr = runtime->hw_ptr_base + pos;
|
| 88 |
+ hw_base = runtime->hw_ptr_base;
|
| 89 |
+ new_hw_ptr = hw_base + pos;
|
| 90 |
hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size;
|
| 91 |
-
|
| 92 |
- delta = hw_ptr_interrupt - new_hw_ptr;
|
| 93 |
- if (delta > 0) {
|
| 94 |
- if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) {
|
| 95 |
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
|
| 96 |
- if (runtime->periods > 1 && substream->pstr->xrun_debug) {
|
| 97 |
- 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);
|
| 98 |
- if (substream->pstr->xrun_debug > 1)
|
| 99 |
- dump_stack();
|
| 100 |
- }
|
| 101 |
-#endif
|
| 102 |
- return 0;
|
| 103 |
+ delta = new_hw_ptr - hw_ptr_interrupt;
|
| 104 |
+ if (hw_ptr_interrupt == runtime->boundary)
|
| 105 |
+ hw_ptr_interrupt = 0;
|
| 106 |
+ if (delta < 0) {
|
| 107 |
+ delta += runtime->buffer_size;
|
| 108 |
+ if (delta < 0) {
|
| 109 |
+ hw_ptr_error(substream,
|
| 110 |
+ "Unexpected hw_pointer value "
|
| 111 |
+ "(stream=%i, pos=%ld, intr_ptr=%ld)\n",
|
| 112 |
+ substream->stream, (long)pos,
|
| 113 |
+ (long)hw_ptr_interrupt);
|
| 114 |
+ /* rebase to interrupt position */
|
| 115 |
+ hw_base = new_hw_ptr = hw_ptr_interrupt;
|
| 116 |
+ delta = 0;
|
| 117 |
+ } else {
|
| 118 |
+ hw_base += runtime->buffer_size;
|
| 119 |
+ if (hw_base == runtime->boundary)
|
| 120 |
+ hw_base = 0;
|
| 121 |
+ new_hw_ptr = hw_base + pos;
|
| 122 |
}
|
| 123 |
- __next_buf:
|
| 124 |
- runtime->hw_ptr_base += runtime->buffer_size;
|
| 125 |
- if (runtime->hw_ptr_base == runtime->boundary)
|
| 126 |
- runtime->hw_ptr_base = 0;
|
| 127 |
- new_hw_ptr = runtime->hw_ptr_base + pos;
|
| 128 |
}
|
| 129 |
-
|
| 130 |
+ if (delta > runtime->period_size) {
|
| 131 |
+ hw_ptr_error(substream,
|
| 132 |
+ "Lost interrupts? "
|
| 133 |
+ "(stream=%i, delta=%ld, intr_ptr=%ld)\n",
|
| 134 |
+ substream->stream, (long)delta,
|
| 135 |
+ (long)hw_ptr_interrupt);
|
| 136 |
+ /* rebase hw_ptr_interrupt */
|
| 137 |
+ hw_ptr_interrupt =
|
| 138 |
+ new_hw_ptr - new_hw_ptr % runtime->period_size;
|
| 139 |
+ }
|
| 140 |
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
|
| 141 |
runtime->silence_size > 0)
|
| 142 |
snd_pcm_playback_silence(substream, new_hw_ptr);
|
| 143 |
|
| 144 |
+ runtime->hw_ptr_base = hw_base;
|
| 145 |
runtime->status->hw_ptr = new_hw_ptr;
|
| 146 |
- runtime->hw_ptr_interrupt = new_hw_ptr - new_hw_ptr % runtime->period_size;
|
| 147 |
+ runtime->hw_ptr_interrupt = hw_ptr_interrupt;
|
| 148 |
|
| 149 |
return snd_pcm_update_hw_ptr_post(substream, runtime);
|
| 150 |
}
|
| 151 |
@@ -233,7 +262,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
|
| 152 |
{
|
| 153 |
struct snd_pcm_runtime *runtime = substream->runtime;
|
| 154 |
snd_pcm_uframes_t pos;
|
| 155 |
- snd_pcm_uframes_t old_hw_ptr, new_hw_ptr;
|
| 156 |
+ snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
|
| 157 |
snd_pcm_sframes_t delta;
|
| 158 |
|
| 159 |
old_hw_ptr = runtime->status->hw_ptr;
|
| 160 |
@@ -242,29 +271,38 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
|
| 161 |
xrun(substream);
|
| 162 |
return -EPIPE;
|
| 163 |
}
|
| 164 |
- new_hw_ptr = runtime->hw_ptr_base + pos;
|
| 165 |
-
|
| 166 |
- delta = old_hw_ptr - new_hw_ptr;
|
| 167 |
- if (delta > 0) {
|
| 168 |
- if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) {
|
| 169 |
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
|
| 170 |
- if (runtime->periods > 2 && substream->pstr->xrun_debug) {
|
| 171 |
- 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);
|
| 172 |
- if (substream->pstr->xrun_debug > 1)
|
| 173 |
- dump_stack();
|
| 174 |
- }
|
| 175 |
-#endif
|
| 176 |
+ hw_base = runtime->hw_ptr_base;
|
| 177 |
+ new_hw_ptr = hw_base + pos;
|
| 178 |
+
|
| 179 |
+ delta = new_hw_ptr - old_hw_ptr;
|
| 180 |
+ if (delta < 0) {
|
| 181 |
+ delta += runtime->buffer_size;
|
| 182 |
+ if (delta < 0) {
|
| 183 |
+ hw_ptr_error(substream,
|
| 184 |
+ "Unexpected hw_pointer value [2] "
|
| 185 |
+ "(stream=%i, pos=%ld, old_ptr=%ld)\n",
|
| 186 |
+ substream->stream, (long)pos,
|
| 187 |
+ (long)old_hw_ptr);
|
| 188 |
return 0;
|
| 189 |
}
|
| 190 |
- runtime->hw_ptr_base += runtime->buffer_size;
|
| 191 |
- if (runtime->hw_ptr_base == runtime->boundary)
|
| 192 |
- runtime->hw_ptr_base = 0;
|
| 193 |
- new_hw_ptr = runtime->hw_ptr_base + pos;
|
| 194 |
+ hw_base += runtime->buffer_size;
|
| 195 |
+ if (hw_base == runtime->boundary)
|
| 196 |
+ hw_base = 0;
|
| 197 |
+ new_hw_ptr = hw_base + pos;
|
| 198 |
+ }
|
| 199 |
+ if (delta > runtime->period_size && runtime->periods > 1) {
|
| 200 |
+ hw_ptr_error(substream,
|
| 201 |
+ "hw_ptr skipping! "
|
| 202 |
+ "(pos=%ld, delta=%ld, period=%ld)\n",
|
| 203 |
+ (long)pos, (long)delta,
|
| 204 |
+ (long)runtime->period_size);
|
| 205 |
+ return 0;
|
| 206 |
}
|
| 207 |
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
|
| 208 |
runtime->silence_size > 0)
|
| 209 |
snd_pcm_playback_silence(substream, new_hw_ptr);
|
| 210 |
|
| 211 |
+ runtime->hw_ptr_base = hw_base;
|
| 212 |
runtime->status->hw_ptr = new_hw_ptr;
|
| 213 |
|
| 214 |
return snd_pcm_update_hw_ptr_post(substream, runtime);
|