diff options
Diffstat (limited to 'sound/core/pcm_lib.c')
-rw-r--r-- | sound/core/pcm_lib.c | 92 |
1 files changed, 72 insertions, 20 deletions
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index d659995ac3a..333e4dd2945 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c | |||
@@ -22,6 +22,7 @@ | |||
22 | 22 | ||
23 | #include <linux/slab.h> | 23 | #include <linux/slab.h> |
24 | #include <linux/time.h> | 24 | #include <linux/time.h> |
25 | #include <linux/math64.h> | ||
25 | #include <sound/core.h> | 26 | #include <sound/core.h> |
26 | #include <sound/control.h> | 27 | #include <sound/control.h> |
27 | #include <sound/info.h> | 28 | #include <sound/info.h> |
@@ -126,24 +127,37 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram | |||
126 | } | 127 | } |
127 | 128 | ||
128 | #ifdef CONFIG_SND_PCM_XRUN_DEBUG | 129 | #ifdef CONFIG_SND_PCM_XRUN_DEBUG |
129 | #define xrun_debug(substream) ((substream)->pstr->xrun_debug) | 130 | #define xrun_debug(substream, mask) ((substream)->pstr->xrun_debug & (mask)) |
130 | #else | 131 | #else |
131 | #define xrun_debug(substream) 0 | 132 | #define xrun_debug(substream, mask) 0 |
132 | #endif | 133 | #endif |
133 | 134 | ||
134 | #define dump_stack_on_xrun(substream) do { \ | 135 | #define dump_stack_on_xrun(substream) do { \ |
135 | if (xrun_debug(substream) > 1) \ | 136 | if (xrun_debug(substream, 2)) \ |
136 | dump_stack(); \ | 137 | dump_stack(); \ |
137 | } while (0) | 138 | } while (0) |
138 | 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 | |||
139 | static void xrun(struct snd_pcm_substream *substream) | 150 | static void xrun(struct snd_pcm_substream *substream) |
140 | { | 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); | ||
141 | snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); | 156 | snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); |
142 | if (xrun_debug(substream)) { | 157 | if (xrun_debug(substream, 1)) { |
143 | snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n", | 158 | char name[16]; |
144 | substream->pcm->card->number, | 159 | pcm_debug_name(substream, name, sizeof(name)); |
145 | substream->pcm->device, | 160 | snd_printd(KERN_DEBUG "XRUN: %s\n", name); |
146 | substream->stream ? 'c' : 'p'); | ||
147 | dump_stack_on_xrun(substream); | 161 | dump_stack_on_xrun(substream); |
148 | } | 162 | } |
149 | } | 163 | } |
@@ -154,16 +168,16 @@ snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, | |||
154 | { | 168 | { |
155 | snd_pcm_uframes_t pos; | 169 | snd_pcm_uframes_t pos; |
156 | 170 | ||
157 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) | ||
158 | snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); | ||
159 | pos = substream->ops->pointer(substream); | 171 | pos = substream->ops->pointer(substream); |
160 | if (pos == SNDRV_PCM_POS_XRUN) | 172 | if (pos == SNDRV_PCM_POS_XRUN) |
161 | return pos; /* XRUN */ | 173 | return pos; /* XRUN */ |
162 | if (pos >= runtime->buffer_size) { | 174 | if (pos >= runtime->buffer_size) { |
163 | if (printk_ratelimit()) { | 175 | if (printk_ratelimit()) { |
164 | 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, " | ||
165 | "buffer size = 0x%lx, period size = 0x%lx\n", | 179 | "buffer size = 0x%lx, period size = 0x%lx\n", |
166 | substream->stream, pos, runtime->buffer_size, | 180 | name, pos, runtime->buffer_size, |
167 | runtime->period_size); | 181 | runtime->period_size); |
168 | } | 182 | } |
169 | pos = 0; | 183 | pos = 0; |
@@ -197,7 +211,7 @@ static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, | |||
197 | 211 | ||
198 | #define hw_ptr_error(substream, fmt, args...) \ | 212 | #define hw_ptr_error(substream, fmt, args...) \ |
199 | do { \ | 213 | do { \ |
200 | if (xrun_debug(substream)) { \ | 214 | if (xrun_debug(substream, 1)) { \ |
201 | if (printk_ratelimit()) { \ | 215 | if (printk_ratelimit()) { \ |
202 | snd_printd("PCM: " fmt, ##args); \ | 216 | snd_printd("PCM: " fmt, ##args); \ |
203 | } \ | 217 | } \ |
@@ -251,7 +265,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) | |||
251 | } | 265 | } |
252 | 266 | ||
253 | /* Do jiffies check only in xrun_debug mode */ | 267 | /* Do jiffies check only in xrun_debug mode */ |
254 | if (!xrun_debug(substream)) | 268 | if (!xrun_debug(substream, 4)) |
255 | goto no_jiffies_check; | 269 | goto no_jiffies_check; |
256 | 270 | ||
257 | /* Skip the jiffies check for hardwares with BATCH flag. | 271 | /* Skip the jiffies check for hardwares with BATCH flag. |
@@ -261,6 +275,9 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) | |||
261 | if (runtime->hw.info & SNDRV_PCM_INFO_BATCH) | 275 | if (runtime->hw.info & SNDRV_PCM_INFO_BATCH) |
262 | goto no_jiffies_check; | 276 | goto no_jiffies_check; |
263 | 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; | ||
264 | jdelta = jiffies - runtime->hw_ptr_jiffies; | 281 | jdelta = jiffies - runtime->hw_ptr_jiffies; |
265 | if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) { | 282 | if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) { |
266 | delta = jdelta / | 283 | delta = jdelta / |
@@ -294,14 +311,20 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) | |||
294 | hw_ptr_interrupt = | 311 | hw_ptr_interrupt = |
295 | new_hw_ptr - new_hw_ptr % runtime->period_size; | 312 | new_hw_ptr - new_hw_ptr % runtime->period_size; |
296 | } | 313 | } |
314 | runtime->hw_ptr_interrupt = hw_ptr_interrupt; | ||
315 | |||
297 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && | 316 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && |
298 | runtime->silence_size > 0) | 317 | runtime->silence_size > 0) |
299 | snd_pcm_playback_silence(substream, new_hw_ptr); | 318 | snd_pcm_playback_silence(substream, new_hw_ptr); |
300 | 319 | ||
320 | if (runtime->status->hw_ptr == new_hw_ptr) | ||
321 | return 0; | ||
322 | |||
301 | runtime->hw_ptr_base = hw_base; | 323 | runtime->hw_ptr_base = hw_base; |
302 | runtime->status->hw_ptr = new_hw_ptr; | 324 | runtime->status->hw_ptr = new_hw_ptr; |
303 | runtime->hw_ptr_jiffies = jiffies; | 325 | runtime->hw_ptr_jiffies = jiffies; |
304 | 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); | ||
305 | 328 | ||
306 | return snd_pcm_update_hw_ptr_post(substream, runtime); | 329 | return snd_pcm_update_hw_ptr_post(substream, runtime); |
307 | } | 330 | } |
@@ -342,8 +365,12 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) | |||
342 | new_hw_ptr = hw_base + pos; | 365 | new_hw_ptr = hw_base + pos; |
343 | } | 366 | } |
344 | /* Do jiffies check only in xrun_debug mode */ | 367 | /* Do jiffies check only in xrun_debug mode */ |
345 | if (xrun_debug(substream) && | 368 | if (!xrun_debug(substream, 4)) |
346 | ((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) { | ||
347 | hw_ptr_error(substream, | 374 | hw_ptr_error(substream, |
348 | "hw_ptr skipping! " | 375 | "hw_ptr skipping! " |
349 | "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n", | 376 | "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n", |
@@ -352,13 +379,19 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) | |||
352 | ((delta * HZ) / runtime->rate)); | 379 | ((delta * HZ) / runtime->rate)); |
353 | return 0; | 380 | return 0; |
354 | } | 381 | } |
382 | no_jiffies_check: | ||
355 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && | 383 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && |
356 | runtime->silence_size > 0) | 384 | runtime->silence_size > 0) |
357 | snd_pcm_playback_silence(substream, new_hw_ptr); | 385 | snd_pcm_playback_silence(substream, new_hw_ptr); |
358 | 386 | ||
387 | if (runtime->status->hw_ptr == new_hw_ptr) | ||
388 | return 0; | ||
389 | |||
359 | runtime->hw_ptr_base = hw_base; | 390 | runtime->hw_ptr_base = hw_base; |
360 | runtime->status->hw_ptr = new_hw_ptr; | 391 | runtime->status->hw_ptr = new_hw_ptr; |
361 | 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); | ||
362 | 395 | ||
363 | return snd_pcm_update_hw_ptr_post(substream, runtime); | 396 | return snd_pcm_update_hw_ptr_post(substream, runtime); |
364 | } | 397 | } |
@@ -452,7 +485,7 @@ static inline unsigned int muldiv32(unsigned int a, unsigned int b, | |||
452 | *r = 0; | 485 | *r = 0; |
453 | return UINT_MAX; | 486 | return UINT_MAX; |
454 | } | 487 | } |
455 | div64_32(&n, c, r); | 488 | n = div_u64_rem(n, c, r); |
456 | if (n >= UINT_MAX) { | 489 | if (n >= UINT_MAX) { |
457 | *r = 0; | 490 | *r = 0; |
458 | return UINT_MAX; | 491 | return UINT_MAX; |
@@ -1524,6 +1557,23 @@ static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream, | |||
1524 | return 0; | 1557 | return 0; |
1525 | } | 1558 | } |
1526 | 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 | |||
1527 | /** | 1577 | /** |
1528 | * snd_pcm_lib_ioctl - a generic PCM ioctl callback | 1578 | * snd_pcm_lib_ioctl - a generic PCM ioctl callback |
1529 | * @substream: the pcm substream instance | 1579 | * @substream: the pcm substream instance |
@@ -1545,6 +1595,8 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, | |||
1545 | return snd_pcm_lib_ioctl_reset(substream, arg); | 1595 | return snd_pcm_lib_ioctl_reset(substream, arg); |
1546 | case SNDRV_PCM_IOCTL1_CHANNEL_INFO: | 1596 | case SNDRV_PCM_IOCTL1_CHANNEL_INFO: |
1547 | 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); | ||
1548 | } | 1600 | } |
1549 | return -ENXIO; | 1601 | return -ENXIO; |
1550 | } | 1602 | } |