diff options
author | Takashi Iwai <tiwai@suse.de> | 2009-06-10 01:26:41 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-06-10 01:26:41 -0400 |
commit | 3b88bc522986ae853670fcba71bb3761c84f7867 (patch) | |
tree | 149a6239c963dfa1111e1ffa736e52edbeaa07bd /sound/core/pcm_lib.c | |
parent | eabaf0634a9034f2e487b0be347edc1460c026a4 (diff) | |
parent | c00701101b82f2bc61dfc259748ec6e5288af6a9 (diff) |
Merge branch 'topic/pcm-jiffies-check' into for-linus
* topic/pcm-jiffies-check:
ALSA: pcm - A helper function to compose PCM stream name for debug prints
ALSA: pcm - Fix update of runtime->hw_ptr_interrupt
ALSA: pcm - Fix a typo in hw_ptr update check
ALSA: PCM midlevel: lower jiffies check margin using runtime->delay value
ALSA: PCM midlevel: Do not update hw_ptr_jiffies when hw_ptr is not changed
ALSA: PCM midlevel: introduce mask for xrun_debug() macro
ALSA: PCM midlevel: improve fifo_size handling
Diffstat (limited to 'sound/core/pcm_lib.c')
-rw-r--r-- | sound/core/pcm_lib.c | 89 |
1 files changed, 70 insertions, 19 deletions
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index a7482874c451..333e4dd29450 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c | |||
@@ -127,24 +127,37 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram | |||
127 | } | 127 | } |
128 | 128 | ||
129 | #ifdef CONFIG_SND_PCM_XRUN_DEBUG | 129 | #ifdef CONFIG_SND_PCM_XRUN_DEBUG |
130 | #define xrun_debug(substream) ((substream)->pstr->xrun_debug) | 130 | #define xrun_debug(substream, mask) ((substream)->pstr->xrun_debug & (mask)) |
131 | #else | 131 | #else |
132 | #define xrun_debug(substream) 0 | 132 | #define xrun_debug(substream, mask) 0 |
133 | #endif | 133 | #endif |
134 | 134 | ||
135 | #define dump_stack_on_xrun(substream) do { \ | 135 | #define dump_stack_on_xrun(substream) do { \ |
136 | if (xrun_debug(substream) > 1) \ | 136 | if (xrun_debug(substream, 2)) \ |
137 | dump_stack(); \ | 137 | dump_stack(); \ |
138 | } while (0) | 138 | } while (0) |
139 | 139 | ||
140 | static void pcm_debug_name(struct snd_pcm_substream *substream, | ||
141 | char *name, size_t len) | ||
142 | { | ||
143 | snprintf(name, len, "pcmC%dD%d%c:%d", | ||
144 | substream->pcm->card->number, | ||
145 | substream->pcm->device, | ||
146 | substream->stream ? 'c' : 'p', | ||
147 | substream->number); | ||
148 | } | ||
149 | |||
140 | static void xrun(struct snd_pcm_substream *substream) | 150 | static void xrun(struct snd_pcm_substream *substream) |
141 | { | 151 | { |
152 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
153 | |||
154 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) | ||
155 | snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); | ||
142 | snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); | 156 | snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); |
143 | if (xrun_debug(substream)) { | 157 | if (xrun_debug(substream, 1)) { |
144 | snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n", | 158 | char name[16]; |
145 | substream->pcm->card->number, | 159 | pcm_debug_name(substream, name, sizeof(name)); |
146 | substream->pcm->device, | 160 | snd_printd(KERN_DEBUG "XRUN: %s\n", name); |
147 | substream->stream ? 'c' : 'p'); | ||
148 | dump_stack_on_xrun(substream); | 161 | dump_stack_on_xrun(substream); |
149 | } | 162 | } |
150 | } | 163 | } |
@@ -155,16 +168,16 @@ snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, | |||
155 | { | 168 | { |
156 | snd_pcm_uframes_t pos; | 169 | snd_pcm_uframes_t pos; |
157 | 170 | ||
158 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) | ||
159 | snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); | ||
160 | pos = substream->ops->pointer(substream); | 171 | pos = substream->ops->pointer(substream); |
161 | if (pos == SNDRV_PCM_POS_XRUN) | 172 | if (pos == SNDRV_PCM_POS_XRUN) |
162 | return pos; /* XRUN */ | 173 | return pos; /* XRUN */ |
163 | if (pos >= runtime->buffer_size) { | 174 | if (pos >= runtime->buffer_size) { |
164 | if (printk_ratelimit()) { | 175 | if (printk_ratelimit()) { |
165 | snd_printd(KERN_ERR "BUG: stream = %i, pos = 0x%lx, " | 176 | char name[16]; |
177 | pcm_debug_name(substream, name, sizeof(name)); | ||
178 | snd_printd(KERN_ERR "BUG: %s, pos = 0x%lx, " | ||
166 | "buffer size = 0x%lx, period size = 0x%lx\n", | 179 | "buffer size = 0x%lx, period size = 0x%lx\n", |
167 | substream->stream, pos, runtime->buffer_size, | 180 | name, pos, runtime->buffer_size, |
168 | runtime->period_size); | 181 | runtime->period_size); |
169 | } | 182 | } |
170 | pos = 0; | 183 | pos = 0; |
@@ -198,7 +211,7 @@ static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, | |||
198 | 211 | ||
199 | #define hw_ptr_error(substream, fmt, args...) \ | 212 | #define hw_ptr_error(substream, fmt, args...) \ |
200 | do { \ | 213 | do { \ |
201 | if (xrun_debug(substream)) { \ | 214 | if (xrun_debug(substream, 1)) { \ |
202 | if (printk_ratelimit()) { \ | 215 | if (printk_ratelimit()) { \ |
203 | snd_printd("PCM: " fmt, ##args); \ | 216 | snd_printd("PCM: " fmt, ##args); \ |
204 | } \ | 217 | } \ |
@@ -252,7 +265,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) | |||
252 | } | 265 | } |
253 | 266 | ||
254 | /* Do jiffies check only in xrun_debug mode */ | 267 | /* Do jiffies check only in xrun_debug mode */ |
255 | if (!xrun_debug(substream)) | 268 | if (!xrun_debug(substream, 4)) |
256 | goto no_jiffies_check; | 269 | goto no_jiffies_check; |
257 | 270 | ||
258 | /* Skip the jiffies check for hardwares with BATCH flag. | 271 | /* Skip the jiffies check for hardwares with BATCH flag. |
@@ -262,6 +275,9 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) | |||
262 | if (runtime->hw.info & SNDRV_PCM_INFO_BATCH) | 275 | if (runtime->hw.info & SNDRV_PCM_INFO_BATCH) |
263 | goto no_jiffies_check; | 276 | goto no_jiffies_check; |
264 | hdelta = new_hw_ptr - old_hw_ptr; | 277 | hdelta = new_hw_ptr - old_hw_ptr; |
278 | if (hdelta < runtime->delay) | ||
279 | goto no_jiffies_check; | ||
280 | hdelta -= runtime->delay; | ||
265 | jdelta = jiffies - runtime->hw_ptr_jiffies; | 281 | jdelta = jiffies - runtime->hw_ptr_jiffies; |
266 | if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) { | 282 | if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) { |
267 | delta = jdelta / | 283 | delta = jdelta / |
@@ -295,14 +311,20 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) | |||
295 | hw_ptr_interrupt = | 311 | hw_ptr_interrupt = |
296 | new_hw_ptr - new_hw_ptr % runtime->period_size; | 312 | new_hw_ptr - new_hw_ptr % runtime->period_size; |
297 | } | 313 | } |
314 | runtime->hw_ptr_interrupt = hw_ptr_interrupt; | ||
315 | |||
298 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && | 316 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && |
299 | runtime->silence_size > 0) | 317 | runtime->silence_size > 0) |
300 | snd_pcm_playback_silence(substream, new_hw_ptr); | 318 | snd_pcm_playback_silence(substream, new_hw_ptr); |
301 | 319 | ||
320 | if (runtime->status->hw_ptr == new_hw_ptr) | ||
321 | return 0; | ||
322 | |||
302 | runtime->hw_ptr_base = hw_base; | 323 | runtime->hw_ptr_base = hw_base; |
303 | runtime->status->hw_ptr = new_hw_ptr; | 324 | runtime->status->hw_ptr = new_hw_ptr; |
304 | runtime->hw_ptr_jiffies = jiffies; | 325 | runtime->hw_ptr_jiffies = jiffies; |
305 | runtime->hw_ptr_interrupt = hw_ptr_interrupt; | 326 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) |
327 | snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); | ||
306 | 328 | ||
307 | return snd_pcm_update_hw_ptr_post(substream, runtime); | 329 | return snd_pcm_update_hw_ptr_post(substream, runtime); |
308 | } | 330 | } |
@@ -343,8 +365,12 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) | |||
343 | new_hw_ptr = hw_base + pos; | 365 | new_hw_ptr = hw_base + pos; |
344 | } | 366 | } |
345 | /* Do jiffies check only in xrun_debug mode */ | 367 | /* Do jiffies check only in xrun_debug mode */ |
346 | if (xrun_debug(substream) && | 368 | if (!xrun_debug(substream, 4)) |
347 | ((delta * HZ) / runtime->rate) > jdelta + HZ/100) { | 369 | goto no_jiffies_check; |
370 | if (delta < runtime->delay) | ||
371 | goto no_jiffies_check; | ||
372 | delta -= runtime->delay; | ||
373 | if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) { | ||
348 | hw_ptr_error(substream, | 374 | hw_ptr_error(substream, |
349 | "hw_ptr skipping! " | 375 | "hw_ptr skipping! " |
350 | "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n", | 376 | "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n", |
@@ -353,13 +379,19 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) | |||
353 | ((delta * HZ) / runtime->rate)); | 379 | ((delta * HZ) / runtime->rate)); |
354 | return 0; | 380 | return 0; |
355 | } | 381 | } |
382 | no_jiffies_check: | ||
356 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && | 383 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && |
357 | runtime->silence_size > 0) | 384 | runtime->silence_size > 0) |
358 | snd_pcm_playback_silence(substream, new_hw_ptr); | 385 | snd_pcm_playback_silence(substream, new_hw_ptr); |
359 | 386 | ||
387 | if (runtime->status->hw_ptr == new_hw_ptr) | ||
388 | return 0; | ||
389 | |||
360 | runtime->hw_ptr_base = hw_base; | 390 | runtime->hw_ptr_base = hw_base; |
361 | runtime->status->hw_ptr = new_hw_ptr; | 391 | runtime->status->hw_ptr = new_hw_ptr; |
362 | runtime->hw_ptr_jiffies = jiffies; | 392 | runtime->hw_ptr_jiffies = jiffies; |
393 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) | ||
394 | snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); | ||
363 | 395 | ||
364 | return snd_pcm_update_hw_ptr_post(substream, runtime); | 396 | return snd_pcm_update_hw_ptr_post(substream, runtime); |
365 | } | 397 | } |
@@ -1525,6 +1557,23 @@ static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream, | |||
1525 | return 0; | 1557 | return 0; |
1526 | } | 1558 | } |
1527 | 1559 | ||
1560 | static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream, | ||
1561 | void *arg) | ||
1562 | { | ||
1563 | struct snd_pcm_hw_params *params = arg; | ||
1564 | snd_pcm_format_t format; | ||
1565 | int channels, width; | ||
1566 | |||
1567 | params->fifo_size = substream->runtime->hw.fifo_size; | ||
1568 | if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_FIFO_IN_FRAMES)) { | ||
1569 | format = params_format(params); | ||
1570 | channels = params_channels(params); | ||
1571 | width = snd_pcm_format_physical_width(format); | ||
1572 | params->fifo_size /= width * channels; | ||
1573 | } | ||
1574 | return 0; | ||
1575 | } | ||
1576 | |||
1528 | /** | 1577 | /** |
1529 | * snd_pcm_lib_ioctl - a generic PCM ioctl callback | 1578 | * snd_pcm_lib_ioctl - a generic PCM ioctl callback |
1530 | * @substream: the pcm substream instance | 1579 | * @substream: the pcm substream instance |
@@ -1546,6 +1595,8 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, | |||
1546 | return snd_pcm_lib_ioctl_reset(substream, arg); | 1595 | return snd_pcm_lib_ioctl_reset(substream, arg); |
1547 | case SNDRV_PCM_IOCTL1_CHANNEL_INFO: | 1596 | case SNDRV_PCM_IOCTL1_CHANNEL_INFO: |
1548 | return snd_pcm_lib_ioctl_channel_info(substream, arg); | 1597 | return snd_pcm_lib_ioctl_channel_info(substream, arg); |
1598 | case SNDRV_PCM_IOCTL1_FIFO_SIZE: | ||
1599 | return snd_pcm_lib_ioctl_fifo_size(substream, arg); | ||
1549 | } | 1600 | } |
1550 | return -ENXIO; | 1601 | return -ENXIO; |
1551 | } | 1602 | } |