aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>2012-10-22 17:42:15 -0400
committerTakashi Iwai <tiwai@suse.de>2012-10-23 10:13:48 -0400
commit4eeaaeaea1cec60a25979678182720dc91308550 (patch)
treeef6895d3ff86454cc6348e2efde2313eceb24471
parent0e8014d772a7639f48d234b23dc4ce97335cce7f (diff)
ALSA: core: add hooks for audio timestamps
ALSA did not provide any direct means to infer the audio time for A/V sync and system/audio time correlations (eg. PulseAudio). Applications had to track the number of samples read/written and add/subtract the number of samples queued in the ring buffer. This accounting led to small errors, typically several samples, due to the two-step process. Computing the audio time in the kernel is more direct, as all the information is available in the same routines. Also add new .audio_wallclock routine to enable fine-grain synchronization between monotonic system time and audio hardware time. Using the wallclock, if supported in hardware, allows for a much better sub-microsecond precision and a common drift tracking for all devices sharing the same wall clock (master clock). Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--include/sound/pcm.h2
-rw-r--r--include/uapi/sound/asound.h7
-rw-r--r--sound/core/pcm_compat.c19
-rw-r--r--sound/core/pcm_lib.c32
-rw-r--r--sound/core/pcm_native.c2
5 files changed, 50 insertions, 12 deletions
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 28fd9f95f9ba..45c1981c9ca2 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -71,6 +71,8 @@ struct snd_pcm_ops {
71 int (*prepare)(struct snd_pcm_substream *substream); 71 int (*prepare)(struct snd_pcm_substream *substream);
72 int (*trigger)(struct snd_pcm_substream *substream, int cmd); 72 int (*trigger)(struct snd_pcm_substream *substream, int cmd);
73 snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream); 73 snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream);
74 int (*wall_clock)(struct snd_pcm_substream *substream,
75 struct timespec *audio_ts);
74 int (*copy)(struct snd_pcm_substream *substream, int channel, 76 int (*copy)(struct snd_pcm_substream *substream, int channel,
75 snd_pcm_uframes_t pos, 77 snd_pcm_uframes_t pos,
76 void __user *buf, snd_pcm_uframes_t count); 78 void __user *buf, snd_pcm_uframes_t count);
diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 92b104e496b5..85b2e4dde883 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -136,7 +136,7 @@ struct snd_hwdep_dsp_image {
136 * * 136 * *
137 *****************************************************************************/ 137 *****************************************************************************/
138 138
139#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 10) 139#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 11)
140 140
141typedef unsigned long snd_pcm_uframes_t; 141typedef unsigned long snd_pcm_uframes_t;
142typedef signed long snd_pcm_sframes_t; 142typedef signed long snd_pcm_sframes_t;
@@ -258,6 +258,7 @@ typedef int __bitwise snd_pcm_subformat_t;
258#define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 /* playback and capture stream are somewhat correlated */ 258#define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 /* playback and capture stream are somewhat correlated */
259#define SNDRV_PCM_INFO_SYNC_START 0x00400000 /* pcm support some kind of sync go */ 259#define SNDRV_PCM_INFO_SYNC_START 0x00400000 /* pcm support some kind of sync go */
260#define SNDRV_PCM_INFO_NO_PERIOD_WAKEUP 0x00800000 /* period wakeup can be disabled */ 260#define SNDRV_PCM_INFO_NO_PERIOD_WAKEUP 0x00800000 /* period wakeup can be disabled */
261#define SNDRV_PCM_INFO_HAS_WALL_CLOCK 0x01000000 /* has audio wall clock for audio/system time sync */
261#define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */ 262#define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */
262 263
263typedef int __bitwise snd_pcm_state_t; 264typedef int __bitwise snd_pcm_state_t;
@@ -406,7 +407,8 @@ struct snd_pcm_status {
406 snd_pcm_uframes_t avail_max; /* max frames available on hw since last status */ 407 snd_pcm_uframes_t avail_max; /* max frames available on hw since last status */
407 snd_pcm_uframes_t overrange; /* count of ADC (capture) overrange detections from last status */ 408 snd_pcm_uframes_t overrange; /* count of ADC (capture) overrange detections from last status */
408 snd_pcm_state_t suspended_state; /* suspended stream state */ 409 snd_pcm_state_t suspended_state; /* suspended stream state */
409 unsigned char reserved[60]; /* must be filled with zero */ 410 struct timespec audio_tstamp; /* from sample counter or wall clock */
411 unsigned char reserved[60-sizeof(struct timespec)]; /* must be filled with zero */
410}; 412};
411 413
412struct snd_pcm_mmap_status { 414struct snd_pcm_mmap_status {
@@ -415,6 +417,7 @@ struct snd_pcm_mmap_status {
415 snd_pcm_uframes_t hw_ptr; /* RO: hw ptr (0...boundary-1) */ 417 snd_pcm_uframes_t hw_ptr; /* RO: hw ptr (0...boundary-1) */
416 struct timespec tstamp; /* Timestamp */ 418 struct timespec tstamp; /* Timestamp */
417 snd_pcm_state_t suspended_state; /* RO: suspended stream state */ 419 snd_pcm_state_t suspended_state; /* RO: suspended stream state */
420 struct timespec audio_tstamp; /* from sample counter or wall clock */
418}; 421};
419 422
420struct snd_pcm_mmap_control { 423struct snd_pcm_mmap_control {
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c
index 91cdf9435fec..af2a3fdb8828 100644
--- a/sound/core/pcm_compat.c
+++ b/sound/core/pcm_compat.c
@@ -190,7 +190,8 @@ struct snd_pcm_status32 {
190 u32 avail_max; 190 u32 avail_max;
191 u32 overrange; 191 u32 overrange;
192 s32 suspended_state; 192 s32 suspended_state;
193 unsigned char reserved[60]; 193 struct compat_timespec audio_tstamp;
194 unsigned char reserved[60-sizeof(struct compat_timespec)];
194} __attribute__((packed)); 195} __attribute__((packed));
195 196
196 197
@@ -205,17 +206,16 @@ static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
205 return err; 206 return err;
206 207
207 if (put_user(status.state, &src->state) || 208 if (put_user(status.state, &src->state) ||
208 put_user(status.trigger_tstamp.tv_sec, &src->trigger_tstamp.tv_sec) || 209 compat_put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) ||
209 put_user(status.trigger_tstamp.tv_nsec, &src->trigger_tstamp.tv_nsec) || 210 compat_put_timespec(&status.tstamp, &src->tstamp) ||
210 put_user(status.tstamp.tv_sec, &src->tstamp.tv_sec) ||
211 put_user(status.tstamp.tv_nsec, &src->tstamp.tv_nsec) ||
212 put_user(status.appl_ptr, &src->appl_ptr) || 211 put_user(status.appl_ptr, &src->appl_ptr) ||
213 put_user(status.hw_ptr, &src->hw_ptr) || 212 put_user(status.hw_ptr, &src->hw_ptr) ||
214 put_user(status.delay, &src->delay) || 213 put_user(status.delay, &src->delay) ||
215 put_user(status.avail, &src->avail) || 214 put_user(status.avail, &src->avail) ||
216 put_user(status.avail_max, &src->avail_max) || 215 put_user(status.avail_max, &src->avail_max) ||
217 put_user(status.overrange, &src->overrange) || 216 put_user(status.overrange, &src->overrange) ||
218 put_user(status.suspended_state, &src->suspended_state)) 217 put_user(status.suspended_state, &src->suspended_state) ||
218 compat_put_timespec(&status.audio_tstamp, &src->audio_tstamp))
219 return -EFAULT; 219 return -EFAULT;
220 220
221 return err; 221 return err;
@@ -364,6 +364,7 @@ struct snd_pcm_mmap_status32 {
364 u32 hw_ptr; 364 u32 hw_ptr;
365 struct compat_timespec tstamp; 365 struct compat_timespec tstamp;
366 s32 suspended_state; 366 s32 suspended_state;
367 struct compat_timespec audio_tstamp;
367} __attribute__((packed)); 368} __attribute__((packed));
368 369
369struct snd_pcm_mmap_control32 { 370struct snd_pcm_mmap_control32 {
@@ -426,12 +427,14 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
426 sstatus.hw_ptr = status->hw_ptr % boundary; 427 sstatus.hw_ptr = status->hw_ptr % boundary;
427 sstatus.tstamp = status->tstamp; 428 sstatus.tstamp = status->tstamp;
428 sstatus.suspended_state = status->suspended_state; 429 sstatus.suspended_state = status->suspended_state;
430 sstatus.audio_tstamp = status->audio_tstamp;
429 snd_pcm_stream_unlock_irq(substream); 431 snd_pcm_stream_unlock_irq(substream);
430 if (put_user(sstatus.state, &src->s.status.state) || 432 if (put_user(sstatus.state, &src->s.status.state) ||
431 put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) || 433 put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
432 put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp.tv_sec) || 434 compat_put_timespec(&sstatus.tstamp, &src->s.status.tstamp) ||
433 put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp.tv_nsec) ||
434 put_user(sstatus.suspended_state, &src->s.status.suspended_state) || 435 put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
436 compat_put_timespec(&sstatus.audio_tstamp,
437 &src->s.status.audio_tstamp) ||
435 put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || 438 put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
436 put_user(scontrol.avail_min, &src->c.control.avail_min)) 439 put_user(scontrol.avail_min, &src->c.control.avail_min))
437 return -EFAULT; 440 return -EFAULT;
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 3dc029e106a2..c4840ff75d00 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -316,6 +316,7 @@ 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;
319 int crossed_boundary = 0; 320 int crossed_boundary = 0;
320 321
321 old_hw_ptr = runtime->status->hw_ptr; 322 old_hw_ptr = runtime->status->hw_ptr;
@@ -328,9 +329,14 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
328 */ 329 */
329 pos = substream->ops->pointer(substream); 330 pos = substream->ops->pointer(substream);
330 curr_jiffies = jiffies; 331 curr_jiffies = jiffies;
331 if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) 332 if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
332 snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); 333 snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp);
333 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
334 if (pos == SNDRV_PCM_POS_XRUN) { 340 if (pos == SNDRV_PCM_POS_XRUN) {
335 xrun(substream); 341 xrun(substream);
336 return -EPIPE; 342 return -EPIPE;
@@ -520,9 +526,31 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
520 snd_BUG_ON(crossed_boundary != 1); 526 snd_BUG_ON(crossed_boundary != 1);
521 runtime->hw_ptr_wrap += runtime->boundary; 527 runtime->hw_ptr_wrap += runtime->boundary;
522 } 528 }
523 if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) 529 if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
524 runtime->status->tstamp = curr_tstamp; 530 runtime->status->tstamp = curr_tstamp;
525 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
526 return snd_pcm_update_state(substream, runtime); 554 return snd_pcm_update_state(substream, runtime);
527} 555}
528 556
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 5e12e5bacbba..7c800012fff9 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -594,6 +594,8 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
594 snd_pcm_update_hw_ptr(substream); 594 snd_pcm_update_hw_ptr(substream);
595 if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { 595 if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
596 status->tstamp = runtime->status->tstamp; 596 status->tstamp = runtime->status->tstamp;
597 status->audio_tstamp =
598 runtime->status->audio_tstamp;
597 goto _tstamp_end; 599 goto _tstamp_end;
598 } 600 }
599 } 601 }