summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>2012-10-22 16:42:15 -0500
committerTakashi Iwai <tiwai@suse.de>2012-10-23 16:13:48 +0200
commit4eeaaeaea1cec60a25979678182720dc91308550 (patch)
treeef6895d3ff86454cc6348e2efde2313eceb24471 /sound
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>
Diffstat (limited to 'sound')
-rw-r--r--sound/core/pcm_compat.c19
-rw-r--r--sound/core/pcm_lib.c32
-rw-r--r--sound/core/pcm_native.c2
3 files changed, 43 insertions, 10 deletions
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 {
u32 avail_max;
u32 overrange;
s32 suspended_state;
- unsigned char reserved[60];
+ struct compat_timespec audio_tstamp;
+ unsigned char reserved[60-sizeof(struct compat_timespec)];
} __attribute__((packed));
@@ -205,17 +206,16 @@ static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
return err;
if (put_user(status.state, &src->state) ||
- put_user(status.trigger_tstamp.tv_sec, &src->trigger_tstamp.tv_sec) ||
- put_user(status.trigger_tstamp.tv_nsec, &src->trigger_tstamp.tv_nsec) ||
- put_user(status.tstamp.tv_sec, &src->tstamp.tv_sec) ||
- put_user(status.tstamp.tv_nsec, &src->tstamp.tv_nsec) ||
+ compat_put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) ||
+ compat_put_timespec(&status.tstamp, &src->tstamp) ||
put_user(status.appl_ptr, &src->appl_ptr) ||
put_user(status.hw_ptr, &src->hw_ptr) ||
put_user(status.delay, &src->delay) ||
put_user(status.avail, &src->avail) ||
put_user(status.avail_max, &src->avail_max) ||
put_user(status.overrange, &src->overrange) ||
- put_user(status.suspended_state, &src->suspended_state))
+ put_user(status.suspended_state, &src->suspended_state) ||
+ compat_put_timespec(&status.audio_tstamp, &src->audio_tstamp))
return -EFAULT;
return err;
@@ -364,6 +364,7 @@ struct snd_pcm_mmap_status32 {
u32 hw_ptr;
struct compat_timespec tstamp;
s32 suspended_state;
+ struct compat_timespec audio_tstamp;
} __attribute__((packed));
struct snd_pcm_mmap_control32 {
@@ -426,12 +427,14 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
sstatus.hw_ptr = status->hw_ptr % boundary;
sstatus.tstamp = status->tstamp;
sstatus.suspended_state = status->suspended_state;
+ sstatus.audio_tstamp = status->audio_tstamp;
snd_pcm_stream_unlock_irq(substream);
if (put_user(sstatus.state, &src->s.status.state) ||
put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
- put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp.tv_sec) ||
- put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp.tv_nsec) ||
+ compat_put_timespec(&sstatus.tstamp, &src->s.status.tstamp) ||
put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
+ compat_put_timespec(&sstatus.audio_tstamp,
+ &src->s.status.audio_tstamp) ||
put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
put_user(scontrol.avail_min, &src->c.control.avail_min))
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,
unsigned long jdelta;
unsigned long curr_jiffies;
struct timespec curr_tstamp;
+ struct timespec audio_tstamp;
int crossed_boundary = 0;
old_hw_ptr = runtime->status->hw_ptr;
@@ -328,9 +329,14 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
*/
pos = substream->ops->pointer(substream);
curr_jiffies = jiffies;
- if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
+ if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp);
+ if ((runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK) &&
+ (substream->ops->wall_clock))
+ substream->ops->wall_clock(substream, &audio_tstamp);
+ }
+
if (pos == SNDRV_PCM_POS_XRUN) {
xrun(substream);
return -EPIPE;
@@ -520,9 +526,31 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
snd_BUG_ON(crossed_boundary != 1);
runtime->hw_ptr_wrap += runtime->boundary;
}
- if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
+ if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
runtime->status->tstamp = curr_tstamp;
+ if (!(runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK)) {
+ /*
+ * no wall clock available, provide audio timestamp
+ * derived from pointer position+delay
+ */
+ u64 audio_frames, audio_nsecs;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ audio_frames = runtime->hw_ptr_wrap
+ + runtime->status->hw_ptr
+ - runtime->delay;
+ else
+ audio_frames = runtime->hw_ptr_wrap
+ + runtime->status->hw_ptr
+ + runtime->delay;
+ audio_nsecs = div_u64(audio_frames * 1000000000LL,
+ runtime->rate);
+ audio_tstamp = ns_to_timespec(audio_nsecs);
+ }
+ runtime->status->audio_tstamp = audio_tstamp;
+ }
+
return snd_pcm_update_state(substream, runtime);
}
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,
snd_pcm_update_hw_ptr(substream);
if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
status->tstamp = runtime->status->tstamp;
+ status->audio_tstamp =
+ runtime->status->audio_tstamp;
goto _tstamp_end;
}
}