diff options
| -rw-r--r-- | Documentation/sound/alsa/Procfile.txt | 5 | ||||
| -rw-r--r-- | sound/core/pcm_lib.c | 36 |
2 files changed, 40 insertions, 1 deletions
diff --git a/Documentation/sound/alsa/Procfile.txt b/Documentation/sound/alsa/Procfile.txt index 381908d8ca42..719a819f8cc2 100644 --- a/Documentation/sound/alsa/Procfile.txt +++ b/Documentation/sound/alsa/Procfile.txt | |||
| @@ -101,6 +101,8 @@ card*/pcm*/xrun_debug | |||
| 101 | bit 0 = Enable XRUN/jiffies debug messages | 101 | bit 0 = Enable XRUN/jiffies debug messages |
| 102 | bit 1 = Show stack trace at XRUN / jiffies check | 102 | bit 1 = Show stack trace at XRUN / jiffies check |
| 103 | bit 2 = Enable additional jiffies check | 103 | bit 2 = Enable additional jiffies check |
| 104 | bit 3 = Log hwptr update at each period interrupt | ||
| 105 | bit 4 = Log hwptr update at each snd_pcm_update_hw_ptr() | ||
| 104 | 106 | ||
| 105 | When the bit 0 is set, the driver will show the messages to | 107 | When the bit 0 is set, the driver will show the messages to |
| 106 | kernel log when an xrun is detected. The debug message is | 108 | kernel log when an xrun is detected. The debug message is |
| @@ -117,6 +119,9 @@ card*/pcm*/xrun_debug | |||
| 117 | buggy) hardware that doesn't give smooth pointer updates. | 119 | buggy) hardware that doesn't give smooth pointer updates. |
| 118 | This feature is enabled via the bit 2. | 120 | This feature is enabled via the bit 2. |
| 119 | 121 | ||
| 122 | Bits 3 and 4 are for logging the hwptr records. Note that | ||
| 123 | these will give flood of kernel messages. | ||
| 124 | |||
| 120 | card*/pcm*/sub*/info | 125 | card*/pcm*/sub*/info |
| 121 | The general information of this PCM sub-stream. | 126 | The general information of this PCM sub-stream. |
| 122 | 127 | ||
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 333e4dd29450..72cfd47af6b8 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c | |||
| @@ -233,6 +233,18 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) | |||
| 233 | xrun(substream); | 233 | xrun(substream); |
| 234 | return -EPIPE; | 234 | return -EPIPE; |
| 235 | } | 235 | } |
| 236 | if (xrun_debug(substream, 8)) { | ||
| 237 | char name[16]; | ||
| 238 | pcm_debug_name(substream, name, sizeof(name)); | ||
| 239 | snd_printd("period_update: %s: pos=0x%x/0x%x/0x%x, " | ||
| 240 | "hwptr=0x%lx, hw_base=0x%lx, hw_intr=0x%lx\n", | ||
| 241 | name, (unsigned int)pos, | ||
| 242 | (unsigned int)runtime->period_size, | ||
| 243 | (unsigned int)runtime->buffer_size, | ||
| 244 | (unsigned long)old_hw_ptr, | ||
| 245 | (unsigned long)runtime->hw_ptr_base, | ||
| 246 | (unsigned long)runtime->hw_ptr_interrupt); | ||
| 247 | } | ||
| 236 | hw_base = runtime->hw_ptr_base; | 248 | hw_base = runtime->hw_ptr_base; |
| 237 | new_hw_ptr = hw_base + pos; | 249 | new_hw_ptr = hw_base + pos; |
| 238 | hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size; | 250 | hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size; |
| @@ -244,18 +256,27 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) | |||
| 244 | delta = new_hw_ptr - hw_ptr_interrupt; | 256 | delta = new_hw_ptr - hw_ptr_interrupt; |
| 245 | } | 257 | } |
| 246 | if (delta < 0) { | 258 | if (delta < 0) { |
| 247 | delta += runtime->buffer_size; | 259 | if (runtime->periods == 1 || new_hw_ptr < old_hw_ptr) |
| 260 | delta += runtime->buffer_size; | ||
| 248 | if (delta < 0) { | 261 | if (delta < 0) { |
| 249 | hw_ptr_error(substream, | 262 | hw_ptr_error(substream, |
| 250 | "Unexpected hw_pointer value " | 263 | "Unexpected hw_pointer value " |
| 251 | "(stream=%i, pos=%ld, intr_ptr=%ld)\n", | 264 | "(stream=%i, pos=%ld, intr_ptr=%ld)\n", |
| 252 | substream->stream, (long)pos, | 265 | substream->stream, (long)pos, |
| 253 | (long)hw_ptr_interrupt); | 266 | (long)hw_ptr_interrupt); |
| 267 | #if 1 | ||
| 268 | /* simply skipping the hwptr update seems more | ||
| 269 | * robust in some cases, e.g. on VMware with | ||
| 270 | * inaccurate timer source | ||
| 271 | */ | ||
| 272 | return 0; /* skip this update */ | ||
| 273 | #else | ||
| 254 | /* rebase to interrupt position */ | 274 | /* rebase to interrupt position */ |
| 255 | hw_base = new_hw_ptr = hw_ptr_interrupt; | 275 | hw_base = new_hw_ptr = hw_ptr_interrupt; |
| 256 | /* align hw_base to buffer_size */ | 276 | /* align hw_base to buffer_size */ |
| 257 | hw_base -= hw_base % runtime->buffer_size; | 277 | hw_base -= hw_base % runtime->buffer_size; |
| 258 | delta = 0; | 278 | delta = 0; |
| 279 | #endif | ||
| 259 | } else { | 280 | } else { |
| 260 | hw_base += runtime->buffer_size; | 281 | hw_base += runtime->buffer_size; |
| 261 | if (hw_base >= runtime->boundary) | 282 | if (hw_base >= runtime->boundary) |
| @@ -344,6 +365,19 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) | |||
| 344 | xrun(substream); | 365 | xrun(substream); |
| 345 | return -EPIPE; | 366 | return -EPIPE; |
| 346 | } | 367 | } |
| 368 | if (xrun_debug(substream, 16)) { | ||
| 369 | char name[16]; | ||
| 370 | pcm_debug_name(substream, name, sizeof(name)); | ||
| 371 | snd_printd("hw_update: %s: pos=0x%x/0x%x/0x%x, " | ||
| 372 | "hwptr=0x%lx, hw_base=0x%lx, hw_intr=0x%lx\n", | ||
| 373 | name, (unsigned int)pos, | ||
| 374 | (unsigned int)runtime->period_size, | ||
| 375 | (unsigned int)runtime->buffer_size, | ||
| 376 | (unsigned long)old_hw_ptr, | ||
| 377 | (unsigned long)runtime->hw_ptr_base, | ||
| 378 | (unsigned long)runtime->hw_ptr_interrupt); | ||
| 379 | } | ||
| 380 | |||
| 347 | hw_base = runtime->hw_ptr_base; | 381 | hw_base = runtime->hw_ptr_base; |
| 348 | new_hw_ptr = hw_base + pos; | 382 | new_hw_ptr = hw_base + pos; |
| 349 | 383 | ||
