diff options
| -rw-r--r-- | include/sound/pcm.h | 3 | ||||
| -rw-r--r-- | sound/core/pcm_lib.c | 47 |
2 files changed, 41 insertions, 9 deletions
diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 8904b1900d7f..c17296891617 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h | |||
| @@ -268,7 +268,8 @@ struct snd_pcm_runtime { | |||
| 268 | int overrange; | 268 | int overrange; |
| 269 | snd_pcm_uframes_t avail_max; | 269 | snd_pcm_uframes_t avail_max; |
| 270 | snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */ | 270 | snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */ |
| 271 | snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time*/ | 271 | snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time */ |
| 272 | unsigned long hw_ptr_jiffies; /* Time when hw_ptr is updated */ | ||
| 272 | 273 | ||
| 273 | /* -- HW params -- */ | 274 | /* -- HW params -- */ |
| 274 | snd_pcm_access_t access; /* access mode */ | 275 | snd_pcm_access_t access; /* access mode */ |
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index fbb2e391591e..63d088f2265f 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c | |||
| @@ -209,9 +209,11 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) | |||
| 209 | { | 209 | { |
| 210 | struct snd_pcm_runtime *runtime = substream->runtime; | 210 | struct snd_pcm_runtime *runtime = substream->runtime; |
| 211 | snd_pcm_uframes_t pos; | 211 | snd_pcm_uframes_t pos; |
| 212 | snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt, hw_base; | 212 | snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_ptr_interrupt, hw_base; |
| 213 | snd_pcm_sframes_t delta; | 213 | snd_pcm_sframes_t hdelta, delta; |
| 214 | unsigned long jdelta; | ||
| 214 | 215 | ||
| 216 | old_hw_ptr = runtime->status->hw_ptr; | ||
| 215 | pos = snd_pcm_update_hw_ptr_pos(substream, runtime); | 217 | pos = snd_pcm_update_hw_ptr_pos(substream, runtime); |
| 216 | if (pos == SNDRV_PCM_POS_XRUN) { | 218 | if (pos == SNDRV_PCM_POS_XRUN) { |
| 217 | xrun(substream); | 219 | xrun(substream); |
| @@ -247,7 +249,30 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) | |||
| 247 | new_hw_ptr = hw_base + pos; | 249 | new_hw_ptr = hw_base + pos; |
| 248 | } | 250 | } |
| 249 | } | 251 | } |
| 250 | if (delta > runtime->period_size) { | 252 | hdelta = new_hw_ptr - old_hw_ptr; |
| 253 | jdelta = jiffies - runtime->hw_ptr_jiffies; | ||
| 254 | if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) { | ||
| 255 | delta = jdelta / | ||
| 256 | (((runtime->period_size * HZ) / runtime->rate) | ||
| 257 | + HZ/100); | ||
| 258 | hw_ptr_error(substream, | ||
| 259 | "hw_ptr skipping! [Q] " | ||
| 260 | "(pos=%ld, delta=%ld, period=%ld, " | ||
| 261 | "jdelta=%lu/%lu/%lu)\n", | ||
| 262 | (long)pos, (long)hdelta, | ||
| 263 | (long)runtime->period_size, jdelta, | ||
| 264 | ((hdelta * HZ) / runtime->rate), delta); | ||
| 265 | hw_ptr_interrupt = runtime->hw_ptr_interrupt + | ||
| 266 | runtime->period_size * delta; | ||
| 267 | if (hw_ptr_interrupt >= runtime->boundary) | ||
| 268 | hw_ptr_interrupt -= runtime->boundary; | ||
| 269 | /* rebase to interrupt position */ | ||
| 270 | hw_base = new_hw_ptr = hw_ptr_interrupt; | ||
| 271 | /* align hw_base to buffer_size */ | ||
| 272 | hw_base -= hw_base % runtime->buffer_size; | ||
| 273 | delta = 0; | ||
| 274 | } | ||
| 275 | if (delta > runtime->period_size + runtime->period_size / 2) { | ||
| 251 | hw_ptr_error(substream, | 276 | hw_ptr_error(substream, |
| 252 | "Lost interrupts? " | 277 | "Lost interrupts? " |
| 253 | "(stream=%i, delta=%ld, intr_ptr=%ld)\n", | 278 | "(stream=%i, delta=%ld, intr_ptr=%ld)\n", |
| @@ -263,6 +288,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) | |||
| 263 | 288 | ||
| 264 | runtime->hw_ptr_base = hw_base; | 289 | runtime->hw_ptr_base = hw_base; |
| 265 | runtime->status->hw_ptr = new_hw_ptr; | 290 | runtime->status->hw_ptr = new_hw_ptr; |
| 291 | runtime->hw_ptr_jiffies = jiffies; | ||
| 266 | runtime->hw_ptr_interrupt = hw_ptr_interrupt; | 292 | runtime->hw_ptr_interrupt = hw_ptr_interrupt; |
| 267 | 293 | ||
| 268 | return snd_pcm_update_hw_ptr_post(substream, runtime); | 294 | return snd_pcm_update_hw_ptr_post(substream, runtime); |
| @@ -275,6 +301,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) | |||
| 275 | snd_pcm_uframes_t pos; | 301 | snd_pcm_uframes_t pos; |
| 276 | snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base; | 302 | snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base; |
| 277 | snd_pcm_sframes_t delta; | 303 | snd_pcm_sframes_t delta; |
| 304 | unsigned long jdelta; | ||
| 278 | 305 | ||
| 279 | old_hw_ptr = runtime->status->hw_ptr; | 306 | old_hw_ptr = runtime->status->hw_ptr; |
| 280 | pos = snd_pcm_update_hw_ptr_pos(substream, runtime); | 307 | pos = snd_pcm_update_hw_ptr_pos(substream, runtime); |
| @@ -286,14 +313,15 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) | |||
| 286 | new_hw_ptr = hw_base + pos; | 313 | new_hw_ptr = hw_base + pos; |
| 287 | 314 | ||
| 288 | delta = new_hw_ptr - old_hw_ptr; | 315 | delta = new_hw_ptr - old_hw_ptr; |
| 316 | jdelta = jiffies - runtime->hw_ptr_jiffies; | ||
| 289 | if (delta < 0) { | 317 | if (delta < 0) { |
| 290 | delta += runtime->buffer_size; | 318 | delta += runtime->buffer_size; |
| 291 | if (delta < 0) { | 319 | if (delta < 0) { |
| 292 | hw_ptr_error(substream, | 320 | hw_ptr_error(substream, |
| 293 | "Unexpected hw_pointer value [2] " | 321 | "Unexpected hw_pointer value [2] " |
| 294 | "(stream=%i, pos=%ld, old_ptr=%ld)\n", | 322 | "(stream=%i, pos=%ld, old_ptr=%ld, jdelta=%li)\n", |
| 295 | substream->stream, (long)pos, | 323 | substream->stream, (long)pos, |
| 296 | (long)old_hw_ptr); | 324 | (long)old_hw_ptr, jdelta); |
| 297 | return 0; | 325 | return 0; |
| 298 | } | 326 | } |
| 299 | hw_base += runtime->buffer_size; | 327 | hw_base += runtime->buffer_size; |
| @@ -301,12 +329,13 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) | |||
| 301 | hw_base = 0; | 329 | hw_base = 0; |
| 302 | new_hw_ptr = hw_base + pos; | 330 | new_hw_ptr = hw_base + pos; |
| 303 | } | 331 | } |
| 304 | if (delta > runtime->period_size && runtime->periods > 1) { | 332 | if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) { |
| 305 | hw_ptr_error(substream, | 333 | hw_ptr_error(substream, |
| 306 | "hw_ptr skipping! " | 334 | "hw_ptr skipping! " |
| 307 | "(pos=%ld, delta=%ld, period=%ld)\n", | 335 | "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n", |
| 308 | (long)pos, (long)delta, | 336 | (long)pos, (long)delta, |
| 309 | (long)runtime->period_size); | 337 | (long)runtime->period_size, jdelta, |
| 338 | ((delta * HZ) / runtime->rate)); | ||
| 310 | return 0; | 339 | return 0; |
| 311 | } | 340 | } |
| 312 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && | 341 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && |
| @@ -315,6 +344,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) | |||
| 315 | 344 | ||
| 316 | runtime->hw_ptr_base = hw_base; | 345 | runtime->hw_ptr_base = hw_base; |
| 317 | runtime->status->hw_ptr = new_hw_ptr; | 346 | runtime->status->hw_ptr = new_hw_ptr; |
| 347 | runtime->hw_ptr_jiffies = jiffies; | ||
| 318 | 348 | ||
| 319 | return snd_pcm_update_hw_ptr_post(substream, runtime); | 349 | return snd_pcm_update_hw_ptr_post(substream, runtime); |
| 320 | } | 350 | } |
| @@ -1441,6 +1471,7 @@ static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream, | |||
| 1441 | runtime->status->hw_ptr %= runtime->buffer_size; | 1471 | runtime->status->hw_ptr %= runtime->buffer_size; |
| 1442 | else | 1472 | else |
| 1443 | runtime->status->hw_ptr = 0; | 1473 | runtime->status->hw_ptr = 0; |
| 1474 | runtime->hw_ptr_jiffies = jiffies; | ||
| 1444 | snd_pcm_stream_unlock_irqrestore(substream, flags); | 1475 | snd_pcm_stream_unlock_irqrestore(substream, flags); |
| 1445 | return 0; | 1476 | return 0; |
| 1446 | } | 1477 | } |
