aboutsummaryrefslogtreecommitdiffstats
path: root/sound/core
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2009-03-03 11:00:15 -0500
committerTakashi Iwai <tiwai@suse.de>2009-03-09 07:56:49 -0400
commited3da3d9a0ef13c6fe1414ec73c9c1be12747b62 (patch)
treee79103738c1d30f8528791af2a1fb44cef2c2371 /sound/core
parent2450cf51a1bdba7037e91b1bcc494b01c58aaf66 (diff)
ALSA: Rewrite hw_ptr updaters
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>
Diffstat (limited to 'sound/core')
-rw-r--r--sound/core/pcm_lib.c128
1 files changed, 83 insertions, 45 deletions
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 921691080f35..86ac9ae9460e 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -125,19 +125,27 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
125 } 125 }
126} 126}
127 127
128#ifdef CONFIG_SND_PCM_XRUN_DEBUG
129#define xrun_debug(substream) ((substream)->pstr->xrun_debug)
130#else
131#define xrun_debug(substream) 0
132#endif
133
134#define dump_stack_on_xrun(substream) do { \
135 if (xrun_debug(substream) > 1) \
136 dump_stack(); \
137 } while (0)
138
128static void xrun(struct snd_pcm_substream *substream) 139static void xrun(struct snd_pcm_substream *substream)
129{ 140{
130 snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); 141 snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
131#ifdef CONFIG_SND_PCM_XRUN_DEBUG 142 if (xrun_debug(substream)) {
132 if (substream->pstr->xrun_debug) {
133 snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n", 143 snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n",
134 substream->pcm->card->number, 144 substream->pcm->card->number,
135 substream->pcm->device, 145 substream->pcm->device,
136 substream->stream ? 'c' : 'p'); 146 substream->stream ? 'c' : 'p');
137 if (substream->pstr->xrun_debug > 1) 147 dump_stack_on_xrun(substream);
138 dump_stack();
139 } 148 }
140#endif
141} 149}
142 150
143static inline snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, 151static inline snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream,
@@ -182,11 +190,21 @@ static inline int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream
182 return 0; 190 return 0;
183} 191}
184 192
193#define hw_ptr_error(substream, fmt, args...) \
194 do { \
195 if (xrun_debug(substream)) { \
196 if (printk_ratelimit()) { \
197 snd_printd("hda_codec: " fmt, ##args); \
198 } \
199 dump_stack_on_xrun(substream); \
200 } \
201 } while (0)
202
185static inline int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) 203static inline int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
186{ 204{
187 struct snd_pcm_runtime *runtime = substream->runtime; 205 struct snd_pcm_runtime *runtime = substream->runtime;
188 snd_pcm_uframes_t pos; 206 snd_pcm_uframes_t pos;
189 snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt; 207 snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt, hw_base;
190 snd_pcm_sframes_t delta; 208 snd_pcm_sframes_t delta;
191 209
192 pos = snd_pcm_update_hw_ptr_pos(substream, runtime); 210 pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
@@ -194,36 +212,47 @@ static inline int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *subs
194 xrun(substream); 212 xrun(substream);
195 return -EPIPE; 213 return -EPIPE;
196 } 214 }
197 if (runtime->period_size == runtime->buffer_size) 215 hw_base = runtime->hw_ptr_base;
198 goto __next_buf; 216 new_hw_ptr = hw_base + pos;
199 new_hw_ptr = runtime->hw_ptr_base + pos;
200 hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size; 217 hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size;
201 218 delta = new_hw_ptr - hw_ptr_interrupt;
202 delta = hw_ptr_interrupt - new_hw_ptr; 219 if (hw_ptr_interrupt == runtime->boundary)
203 if (delta > 0) { 220 hw_ptr_interrupt = 0;
204 if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) { 221 if (delta < 0) {
205#ifdef CONFIG_SND_PCM_XRUN_DEBUG 222 delta += runtime->buffer_size;
206 if (runtime->periods > 1 && substream->pstr->xrun_debug) { 223 if (delta < 0) {
207 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); 224 hw_ptr_error(substream,
208 if (substream->pstr->xrun_debug > 1) 225 "Unexpected hw_pointer value "
209 dump_stack(); 226 "(stream=%i, pos=%ld, intr_ptr=%ld)\n",
210 } 227 substream->stream, (long)pos,
211#endif 228 (long)hw_ptr_interrupt);
212 return 0; 229 /* rebase to interrupt position */
230 hw_base = new_hw_ptr = hw_ptr_interrupt;
231 delta = 0;
232 } else {
233 hw_base += runtime->buffer_size;
234 if (hw_base == runtime->boundary)
235 hw_base = 0;
236 new_hw_ptr = hw_base + pos;
213 } 237 }
214 __next_buf:
215 runtime->hw_ptr_base += runtime->buffer_size;
216 if (runtime->hw_ptr_base == runtime->boundary)
217 runtime->hw_ptr_base = 0;
218 new_hw_ptr = runtime->hw_ptr_base + pos;
219 } 238 }
220 239 if (delta > runtime->period_size) {
240 hw_ptr_error(substream,
241 "Lost interrupts? "
242 "(stream=%i, delta=%ld, intr_ptr=%ld)\n",
243 substream->stream, (long)delta,
244 (long)hw_ptr_interrupt);
245 /* rebase hw_ptr_interrupt */
246 hw_ptr_interrupt =
247 new_hw_ptr - new_hw_ptr % runtime->period_size;
248 }
221 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && 249 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
222 runtime->silence_size > 0) 250 runtime->silence_size > 0)
223 snd_pcm_playback_silence(substream, new_hw_ptr); 251 snd_pcm_playback_silence(substream, new_hw_ptr);
224 252
253 runtime->hw_ptr_base = hw_base;
225 runtime->status->hw_ptr = new_hw_ptr; 254 runtime->status->hw_ptr = new_hw_ptr;
226 runtime->hw_ptr_interrupt = new_hw_ptr - new_hw_ptr % runtime->period_size; 255 runtime->hw_ptr_interrupt = hw_ptr_interrupt;
227 256
228 return snd_pcm_update_hw_ptr_post(substream, runtime); 257 return snd_pcm_update_hw_ptr_post(substream, runtime);
229} 258}
@@ -233,7 +262,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
233{ 262{
234 struct snd_pcm_runtime *runtime = substream->runtime; 263 struct snd_pcm_runtime *runtime = substream->runtime;
235 snd_pcm_uframes_t pos; 264 snd_pcm_uframes_t pos;
236 snd_pcm_uframes_t old_hw_ptr, new_hw_ptr; 265 snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
237 snd_pcm_sframes_t delta; 266 snd_pcm_sframes_t delta;
238 267
239 old_hw_ptr = runtime->status->hw_ptr; 268 old_hw_ptr = runtime->status->hw_ptr;
@@ -242,29 +271,38 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
242 xrun(substream); 271 xrun(substream);
243 return -EPIPE; 272 return -EPIPE;
244 } 273 }
245 new_hw_ptr = runtime->hw_ptr_base + pos; 274 hw_base = runtime->hw_ptr_base;
246 275 new_hw_ptr = hw_base + pos;
247 delta = old_hw_ptr - new_hw_ptr; 276
248 if (delta > 0) { 277 delta = new_hw_ptr - old_hw_ptr;
249 if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) { 278 if (delta < 0) {
250#ifdef CONFIG_SND_PCM_XRUN_DEBUG 279 delta += runtime->buffer_size;
251 if (runtime->periods > 2 && substream->pstr->xrun_debug) { 280 if (delta < 0) {
252 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); 281 hw_ptr_error(substream,
253 if (substream->pstr->xrun_debug > 1) 282 "Unexpected hw_pointer value [2] "
254 dump_stack(); 283 "(stream=%i, pos=%ld, old_ptr=%ld)\n",
255 } 284 substream->stream, (long)pos,
256#endif 285 (long)old_hw_ptr);
257 return 0; 286 return 0;
258 } 287 }
259 runtime->hw_ptr_base += runtime->buffer_size; 288 hw_base += runtime->buffer_size;
260 if (runtime->hw_ptr_base == runtime->boundary) 289 if (hw_base == runtime->boundary)
261 runtime->hw_ptr_base = 0; 290 hw_base = 0;
262 new_hw_ptr = runtime->hw_ptr_base + pos; 291 new_hw_ptr = hw_base + pos;
292 }
293 if (delta > runtime->period_size && runtime->periods > 1) {
294 hw_ptr_error(substream,
295 "hw_ptr skipping! "
296 "(pos=%ld, delta=%ld, period=%ld)\n",
297 (long)pos, (long)delta,
298 (long)runtime->period_size);
299 return 0;
263 } 300 }
264 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && 301 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
265 runtime->silence_size > 0) 302 runtime->silence_size > 0)
266 snd_pcm_playback_silence(substream, new_hw_ptr); 303 snd_pcm_playback_silence(substream, new_hw_ptr);
267 304
305 runtime->hw_ptr_base = hw_base;
268 runtime->status->hw_ptr = new_hw_ptr; 306 runtime->status->hw_ptr = new_hw_ptr;
269 307
270 return snd_pcm_update_hw_ptr_post(substream, runtime); 308 return snd_pcm_update_hw_ptr_post(substream, runtime);