diff options
Diffstat (limited to 'sound/core/pcm_lib.c')
-rw-r--r-- | sound/core/pcm_lib.c | 88 |
1 files changed, 59 insertions, 29 deletions
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index ffd656012ab8..ac6b33f3779c 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c | |||
@@ -232,6 +232,49 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream, | |||
232 | return 0; | 232 | return 0; |
233 | } | 233 | } |
234 | 234 | ||
235 | static void update_audio_tstamp(struct snd_pcm_substream *substream, | ||
236 | struct timespec *curr_tstamp, | ||
237 | struct timespec *audio_tstamp) | ||
238 | { | ||
239 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
240 | u64 audio_frames, audio_nsecs; | ||
241 | struct timespec driver_tstamp; | ||
242 | |||
243 | if (runtime->tstamp_mode != SNDRV_PCM_TSTAMP_ENABLE) | ||
244 | return; | ||
245 | |||
246 | if (!(substream->ops->get_time_info) || | ||
247 | (runtime->audio_tstamp_report.actual_type == | ||
248 | SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) { | ||
249 | |||
250 | /* | ||
251 | * provide audio timestamp derived from pointer position | ||
252 | * add delay only if requested | ||
253 | */ | ||
254 | |||
255 | audio_frames = runtime->hw_ptr_wrap + runtime->status->hw_ptr; | ||
256 | |||
257 | if (runtime->audio_tstamp_config.report_delay) { | ||
258 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
259 | audio_frames -= runtime->delay; | ||
260 | else | ||
261 | audio_frames += runtime->delay; | ||
262 | } | ||
263 | audio_nsecs = div_u64(audio_frames * 1000000000LL, | ||
264 | runtime->rate); | ||
265 | *audio_tstamp = ns_to_timespec(audio_nsecs); | ||
266 | } | ||
267 | runtime->status->audio_tstamp = *audio_tstamp; | ||
268 | runtime->status->tstamp = *curr_tstamp; | ||
269 | |||
270 | /* | ||
271 | * re-take a driver timestamp to let apps detect if the reference tstamp | ||
272 | * read by low-level hardware was provided with a delay | ||
273 | */ | ||
274 | snd_pcm_gettime(substream->runtime, (struct timespec *)&driver_tstamp); | ||
275 | runtime->driver_tstamp = driver_tstamp; | ||
276 | } | ||
277 | |||
235 | static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, | 278 | static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, |
236 | unsigned int in_interrupt) | 279 | unsigned int in_interrupt) |
237 | { | 280 | { |
@@ -256,11 +299,18 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, | |||
256 | pos = substream->ops->pointer(substream); | 299 | pos = substream->ops->pointer(substream); |
257 | curr_jiffies = jiffies; | 300 | curr_jiffies = jiffies; |
258 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { | 301 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { |
259 | snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); | 302 | if ((substream->ops->get_time_info) && |
260 | 303 | (runtime->audio_tstamp_config.type_requested != SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) { | |
261 | if ((runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK) && | 304 | substream->ops->get_time_info(substream, &curr_tstamp, |
262 | (substream->ops->wall_clock)) | 305 | &audio_tstamp, |
263 | substream->ops->wall_clock(substream, &audio_tstamp); | 306 | &runtime->audio_tstamp_config, |
307 | &runtime->audio_tstamp_report); | ||
308 | |||
309 | /* re-test in case tstamp type is not supported in hardware and was demoted to DEFAULT */ | ||
310 | if (runtime->audio_tstamp_report.actual_type == SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT) | ||
311 | snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); | ||
312 | } else | ||
313 | snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); | ||
264 | } | 314 | } |
265 | 315 | ||
266 | if (pos == SNDRV_PCM_POS_XRUN) { | 316 | if (pos == SNDRV_PCM_POS_XRUN) { |
@@ -403,8 +453,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, | |||
403 | } | 453 | } |
404 | 454 | ||
405 | no_delta_check: | 455 | no_delta_check: |
406 | if (runtime->status->hw_ptr == new_hw_ptr) | 456 | if (runtime->status->hw_ptr == new_hw_ptr) { |
457 | update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp); | ||
407 | return 0; | 458 | return 0; |
459 | } | ||
408 | 460 | ||
409 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && | 461 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && |
410 | runtime->silence_size > 0) | 462 | runtime->silence_size > 0) |
@@ -426,30 +478,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, | |||
426 | snd_BUG_ON(crossed_boundary != 1); | 478 | snd_BUG_ON(crossed_boundary != 1); |
427 | runtime->hw_ptr_wrap += runtime->boundary; | 479 | runtime->hw_ptr_wrap += runtime->boundary; |
428 | } | 480 | } |
429 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { | ||
430 | runtime->status->tstamp = curr_tstamp; | ||
431 | 481 | ||
432 | if (!(runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK)) { | 482 | update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp); |
433 | /* | ||
434 | * no wall clock available, provide audio timestamp | ||
435 | * derived from pointer position+delay | ||
436 | */ | ||
437 | u64 audio_frames, audio_nsecs; | ||
438 | |||
439 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
440 | audio_frames = runtime->hw_ptr_wrap | ||
441 | + runtime->status->hw_ptr | ||
442 | - runtime->delay; | ||
443 | else | ||
444 | audio_frames = runtime->hw_ptr_wrap | ||
445 | + runtime->status->hw_ptr | ||
446 | + runtime->delay; | ||
447 | audio_nsecs = div_u64(audio_frames * 1000000000LL, | ||
448 | runtime->rate); | ||
449 | audio_tstamp = ns_to_timespec(audio_nsecs); | ||
450 | } | ||
451 | runtime->status->audio_tstamp = audio_tstamp; | ||
452 | } | ||
453 | 483 | ||
454 | return snd_pcm_update_state(substream, runtime); | 484 | return snd_pcm_update_state(substream, runtime); |
455 | } | 485 | } |