diff options
-rw-r--r-- | include/sound/asound.h | 1 | ||||
-rw-r--r-- | include/sound/pcm.h | 1 | ||||
-rw-r--r-- | sound/core/pcm_lib.c | 89 | ||||
-rw-r--r-- | sound/core/pcm_native.c | 15 |
4 files changed, 84 insertions, 22 deletions
diff --git a/include/sound/asound.h b/include/sound/asound.h index 6add80fc2512..82aed3f47534 100644 --- a/include/sound/asound.h +++ b/include/sound/asound.h | |||
@@ -255,6 +255,7 @@ typedef int __bitwise snd_pcm_subformat_t; | |||
255 | #define SNDRV_PCM_INFO_HALF_DUPLEX 0x00100000 /* only half duplex */ | 255 | #define SNDRV_PCM_INFO_HALF_DUPLEX 0x00100000 /* only half duplex */ |
256 | #define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 /* playback and capture stream are somewhat correlated */ | 256 | #define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 /* playback and capture stream are somewhat correlated */ |
257 | #define SNDRV_PCM_INFO_SYNC_START 0x00400000 /* pcm support some kind of sync go */ | 257 | #define SNDRV_PCM_INFO_SYNC_START 0x00400000 /* pcm support some kind of sync go */ |
258 | #define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */ | ||
258 | 259 | ||
259 | typedef int __bitwise snd_pcm_state_t; | 260 | typedef int __bitwise snd_pcm_state_t; |
260 | #define SNDRV_PCM_STATE_OPEN ((__force snd_pcm_state_t) 0) /* stream is open */ | 261 | #define SNDRV_PCM_STATE_OPEN ((__force snd_pcm_state_t) 0) /* stream is open */ |
diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 2092274212b9..23893523dc8c 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h | |||
@@ -98,6 +98,7 @@ struct snd_pcm_ops { | |||
98 | #define SNDRV_PCM_IOCTL1_INFO 1 | 98 | #define SNDRV_PCM_IOCTL1_INFO 1 |
99 | #define SNDRV_PCM_IOCTL1_CHANNEL_INFO 2 | 99 | #define SNDRV_PCM_IOCTL1_CHANNEL_INFO 2 |
100 | #define SNDRV_PCM_IOCTL1_GSTATE 3 | 100 | #define SNDRV_PCM_IOCTL1_GSTATE 3 |
101 | #define SNDRV_PCM_IOCTL1_FIFO_SIZE 4 | ||
101 | 102 | ||
102 | #define SNDRV_PCM_TRIGGER_STOP 0 | 103 | #define SNDRV_PCM_TRIGGER_STOP 0 |
103 | #define SNDRV_PCM_TRIGGER_START 1 | 104 | #define SNDRV_PCM_TRIGGER_START 1 |
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 | } |
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 45dc53fcfa2f..84da3ba17c86 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c | |||
@@ -312,9 +312,18 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, | |||
312 | 312 | ||
313 | hw = &substream->runtime->hw; | 313 | hw = &substream->runtime->hw; |
314 | if (!params->info) | 314 | if (!params->info) |
315 | params->info = hw->info; | 315 | params->info = hw->info & ~SNDRV_PCM_INFO_FIFO_IN_FRAMES; |
316 | if (!params->fifo_size) | 316 | if (!params->fifo_size) { |
317 | params->fifo_size = hw->fifo_size; | 317 | if (snd_mask_min(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT]) == |
318 | snd_mask_max(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT]) && | ||
319 | snd_mask_min(¶ms->masks[SNDRV_PCM_HW_PARAM_CHANNELS]) == | ||
320 | snd_mask_max(¶ms->masks[SNDRV_PCM_HW_PARAM_CHANNELS])) { | ||
321 | changed = substream->ops->ioctl(substream, | ||
322 | SNDRV_PCM_IOCTL1_FIFO_SIZE, params); | ||
323 | if (params < 0) | ||
324 | return changed; | ||
325 | } | ||
326 | } | ||
318 | params->rmask = 0; | 327 | params->rmask = 0; |
319 | return 0; | 328 | return 0; |
320 | } | 329 | } |