diff options
Diffstat (limited to 'sound/core/pcm_lib.c')
| -rw-r--r-- | sound/core/pcm_lib.c | 57 |
1 files changed, 50 insertions, 7 deletions
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index f42c10a43315..c4840ff75d00 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c | |||
| @@ -316,6 +316,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, | |||
| 316 | unsigned long jdelta; | 316 | unsigned long jdelta; |
| 317 | unsigned long curr_jiffies; | 317 | unsigned long curr_jiffies; |
| 318 | struct timespec curr_tstamp; | 318 | struct timespec curr_tstamp; |
| 319 | struct timespec audio_tstamp; | ||
| 320 | int crossed_boundary = 0; | ||
| 319 | 321 | ||
| 320 | old_hw_ptr = runtime->status->hw_ptr; | 322 | old_hw_ptr = runtime->status->hw_ptr; |
| 321 | 323 | ||
| @@ -327,9 +329,14 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, | |||
| 327 | */ | 329 | */ |
| 328 | pos = substream->ops->pointer(substream); | 330 | pos = substream->ops->pointer(substream); |
| 329 | curr_jiffies = jiffies; | 331 | curr_jiffies = jiffies; |
| 330 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) | 332 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { |
| 331 | snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); | 333 | snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); |
| 332 | 334 | ||
| 335 | if ((runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK) && | ||
| 336 | (substream->ops->wall_clock)) | ||
| 337 | substream->ops->wall_clock(substream, &audio_tstamp); | ||
| 338 | } | ||
| 339 | |||
| 333 | if (pos == SNDRV_PCM_POS_XRUN) { | 340 | if (pos == SNDRV_PCM_POS_XRUN) { |
| 334 | xrun(substream); | 341 | xrun(substream); |
| 335 | return -EPIPE; | 342 | return -EPIPE; |
| @@ -360,8 +367,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, | |||
| 360 | hdelta = curr_jiffies - runtime->hw_ptr_jiffies; | 367 | hdelta = curr_jiffies - runtime->hw_ptr_jiffies; |
| 361 | if (hdelta > runtime->hw_ptr_buffer_jiffies/2) { | 368 | if (hdelta > runtime->hw_ptr_buffer_jiffies/2) { |
| 362 | hw_base += runtime->buffer_size; | 369 | hw_base += runtime->buffer_size; |
| 363 | if (hw_base >= runtime->boundary) | 370 | if (hw_base >= runtime->boundary) { |
| 364 | hw_base = 0; | 371 | hw_base = 0; |
| 372 | crossed_boundary++; | ||
| 373 | } | ||
| 365 | new_hw_ptr = hw_base + pos; | 374 | new_hw_ptr = hw_base + pos; |
| 366 | goto __delta; | 375 | goto __delta; |
| 367 | } | 376 | } |
| @@ -371,8 +380,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, | |||
| 371 | /* pointer crosses the end of the ring buffer */ | 380 | /* pointer crosses the end of the ring buffer */ |
| 372 | if (new_hw_ptr < old_hw_ptr) { | 381 | if (new_hw_ptr < old_hw_ptr) { |
| 373 | hw_base += runtime->buffer_size; | 382 | hw_base += runtime->buffer_size; |
| 374 | if (hw_base >= runtime->boundary) | 383 | if (hw_base >= runtime->boundary) { |
| 375 | hw_base = 0; | 384 | hw_base = 0; |
| 385 | crossed_boundary++; | ||
| 386 | } | ||
| 376 | new_hw_ptr = hw_base + pos; | 387 | new_hw_ptr = hw_base + pos; |
| 377 | } | 388 | } |
| 378 | __delta: | 389 | __delta: |
| @@ -410,8 +421,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, | |||
| 410 | while (hdelta > xrun_threshold) { | 421 | while (hdelta > xrun_threshold) { |
| 411 | delta += runtime->buffer_size; | 422 | delta += runtime->buffer_size; |
| 412 | hw_base += runtime->buffer_size; | 423 | hw_base += runtime->buffer_size; |
| 413 | if (hw_base >= runtime->boundary) | 424 | if (hw_base >= runtime->boundary) { |
| 414 | hw_base = 0; | 425 | hw_base = 0; |
| 426 | crossed_boundary++; | ||
| 427 | } | ||
| 415 | new_hw_ptr = hw_base + pos; | 428 | new_hw_ptr = hw_base + pos; |
| 416 | hdelta -= runtime->hw_ptr_buffer_jiffies; | 429 | hdelta -= runtime->hw_ptr_buffer_jiffies; |
| 417 | } | 430 | } |
| @@ -456,8 +469,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, | |||
| 456 | /* the delta value is small or zero in most cases */ | 469 | /* the delta value is small or zero in most cases */ |
| 457 | while (delta > 0) { | 470 | while (delta > 0) { |
| 458 | new_hw_ptr += runtime->period_size; | 471 | new_hw_ptr += runtime->period_size; |
| 459 | if (new_hw_ptr >= runtime->boundary) | 472 | if (new_hw_ptr >= runtime->boundary) { |
| 460 | new_hw_ptr -= runtime->boundary; | 473 | new_hw_ptr -= runtime->boundary; |
| 474 | crossed_boundary--; | ||
| 475 | } | ||
| 461 | delta--; | 476 | delta--; |
| 462 | } | 477 | } |
| 463 | /* align hw_base to buffer_size */ | 478 | /* align hw_base to buffer_size */ |
| @@ -507,9 +522,35 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, | |||
| 507 | runtime->hw_ptr_base = hw_base; | 522 | runtime->hw_ptr_base = hw_base; |
| 508 | runtime->status->hw_ptr = new_hw_ptr; | 523 | runtime->status->hw_ptr = new_hw_ptr; |
| 509 | runtime->hw_ptr_jiffies = curr_jiffies; | 524 | runtime->hw_ptr_jiffies = curr_jiffies; |
| 510 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) | 525 | if (crossed_boundary) { |
| 526 | snd_BUG_ON(crossed_boundary != 1); | ||
| 527 | runtime->hw_ptr_wrap += runtime->boundary; | ||
| 528 | } | ||
| 529 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { | ||
| 511 | runtime->status->tstamp = curr_tstamp; | 530 | runtime->status->tstamp = curr_tstamp; |
| 512 | 531 | ||
| 532 | if (!(runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK)) { | ||
| 533 | /* | ||
| 534 | * no wall clock available, provide audio timestamp | ||
| 535 | * derived from pointer position+delay | ||
| 536 | */ | ||
| 537 | u64 audio_frames, audio_nsecs; | ||
| 538 | |||
| 539 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
| 540 | audio_frames = runtime->hw_ptr_wrap | ||
| 541 | + runtime->status->hw_ptr | ||
| 542 | - runtime->delay; | ||
| 543 | else | ||
| 544 | audio_frames = runtime->hw_ptr_wrap | ||
| 545 | + runtime->status->hw_ptr | ||
| 546 | + runtime->delay; | ||
| 547 | audio_nsecs = div_u64(audio_frames * 1000000000LL, | ||
| 548 | runtime->rate); | ||
| 549 | audio_tstamp = ns_to_timespec(audio_nsecs); | ||
| 550 | } | ||
| 551 | runtime->status->audio_tstamp = audio_tstamp; | ||
| 552 | } | ||
| 553 | |||
| 513 | return snd_pcm_update_state(substream, runtime); | 554 | return snd_pcm_update_state(substream, runtime); |
| 514 | } | 555 | } |
| 515 | 556 | ||
| @@ -1661,8 +1702,10 @@ static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream, | |||
| 1661 | if (snd_pcm_running(substream) && | 1702 | if (snd_pcm_running(substream) && |
| 1662 | snd_pcm_update_hw_ptr(substream) >= 0) | 1703 | snd_pcm_update_hw_ptr(substream) >= 0) |
| 1663 | runtime->status->hw_ptr %= runtime->buffer_size; | 1704 | runtime->status->hw_ptr %= runtime->buffer_size; |
| 1664 | else | 1705 | else { |
| 1665 | runtime->status->hw_ptr = 0; | 1706 | runtime->status->hw_ptr = 0; |
| 1707 | runtime->hw_ptr_wrap = 0; | ||
| 1708 | } | ||
| 1666 | snd_pcm_stream_unlock_irqrestore(substream, flags); | 1709 | snd_pcm_stream_unlock_irqrestore(substream, flags); |
| 1667 | return 0; | 1710 | return 0; |
| 1668 | } | 1711 | } |
