All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/3] ALSA: core: keep track of boundary wrap-around
@ 2012-10-22 21:42 Pierre-Louis Bossart
  2012-10-22 21:42 ` [PATCH 2/3] ALSA: core: add hooks for audio timestamps Pierre-Louis Bossart
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Pierre-Louis Bossart @ 2012-10-22 21:42 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, Pierre-Louis Bossart

Keep track of boundary crossing when hw_ptr
exceeds boundary limit and wraps-around. This
will help keep track of total number
of frames played/received at the kernel level

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
---
 include/sound/pcm.h  |    1 +
 sound/core/pcm_lib.c |   25 ++++++++++++++++++++-----
 2 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 6268a41..28fd9f9 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -281,6 +281,7 @@ struct snd_pcm_runtime {
 	unsigned long hw_ptr_jiffies;	/* Time when hw_ptr is updated */
 	unsigned long hw_ptr_buffer_jiffies; /* buffer time in jiffies */
 	snd_pcm_sframes_t delay;	/* extra delay; typically FIFO size */
+	u64 hw_ptr_wrap;                /* offset for hw_ptr due to boundary wrap-around */
 
 	/* -- HW params -- */
 	snd_pcm_access_t access;	/* access mode */
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index f42c10a..3dc029e 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;
+	int crossed_boundary = 0;
 
 	old_hw_ptr = runtime->status->hw_ptr;
 
@@ -360,8 +361,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
 			hdelta = curr_jiffies - runtime->hw_ptr_jiffies;
 			if (hdelta > runtime->hw_ptr_buffer_jiffies/2) {
 				hw_base += runtime->buffer_size;
-				if (hw_base >= runtime->boundary)
+				if (hw_base >= runtime->boundary) {
 					hw_base = 0;
+					crossed_boundary++;
+				}
 				new_hw_ptr = hw_base + pos;
 				goto __delta;
 			}
@@ -371,8 +374,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
 	/* pointer crosses the end of the ring buffer */
 	if (new_hw_ptr < old_hw_ptr) {
 		hw_base += runtime->buffer_size;
-		if (hw_base >= runtime->boundary)
+		if (hw_base >= runtime->boundary) {
 			hw_base = 0;
+			crossed_boundary++;
+		}
 		new_hw_ptr = hw_base + pos;
 	}
       __delta:
@@ -410,8 +415,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
 		while (hdelta > xrun_threshold) {
 			delta += runtime->buffer_size;
 			hw_base += runtime->buffer_size;
-			if (hw_base >= runtime->boundary)
+			if (hw_base >= runtime->boundary) {
 				hw_base = 0;
+				crossed_boundary++;
+			}
 			new_hw_ptr = hw_base + pos;
 			hdelta -= runtime->hw_ptr_buffer_jiffies;
 		}
@@ -456,8 +463,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
 		/* the delta value is small or zero in most cases */
 		while (delta > 0) {
 			new_hw_ptr += runtime->period_size;
-			if (new_hw_ptr >= runtime->boundary)
+			if (new_hw_ptr >= runtime->boundary) {
 				new_hw_ptr -= runtime->boundary;
+				crossed_boundary--;
+			}
 			delta--;
 		}
 		/* align hw_base to buffer_size */
@@ -507,6 +516,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
 	runtime->hw_ptr_base = hw_base;
 	runtime->status->hw_ptr = new_hw_ptr;
 	runtime->hw_ptr_jiffies = curr_jiffies;
+	if (crossed_boundary) {
+		snd_BUG_ON(crossed_boundary != 1);
+		runtime->hw_ptr_wrap += runtime->boundary;
+	}
 	if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
 		runtime->status->tstamp = curr_tstamp;
 
@@ -1661,8 +1674,10 @@ static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream,
 	if (snd_pcm_running(substream) &&
 	    snd_pcm_update_hw_ptr(substream) >= 0)
 		runtime->status->hw_ptr %= runtime->buffer_size;
-	else
+	else {
 		runtime->status->hw_ptr = 0;
+		runtime->hw_ptr_wrap = 0;
+	}
 	snd_pcm_stream_unlock_irqrestore(substream, flags);
 	return 0;
 }
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 2/3] ALSA: core: add hooks for audio timestamps
  2012-10-22 21:42 [PATCH 1/3] ALSA: core: keep track of boundary wrap-around Pierre-Louis Bossart
@ 2012-10-22 21:42 ` Pierre-Louis Bossart
  2012-10-22 21:42 ` [PATCH 3/3] ALSA: hda: support for wallclock timestamps Pierre-Louis Bossart
  2012-10-23 14:43 ` [PATCH 1/3] ALSA: core: keep track of boundary wrap-around Takashi Iwai
  2 siblings, 0 replies; 4+ messages in thread
From: Pierre-Louis Bossart @ 2012-10-22 21:42 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, Pierre-Louis Bossart

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>
---
 include/sound/pcm.h         |    2 ++
 include/uapi/sound/asound.h |    7 +++++--
 sound/core/pcm_compat.c     |   19 +++++++++++--------
 sound/core/pcm_lib.c        |   32 ++++++++++++++++++++++++++++++--
 sound/core/pcm_native.c     |    2 ++
 5 files changed, 50 insertions(+), 12 deletions(-)

diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 28fd9f9..45c1981 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -71,6 +71,8 @@ struct snd_pcm_ops {
 	int (*prepare)(struct snd_pcm_substream *substream);
 	int (*trigger)(struct snd_pcm_substream *substream, int cmd);
 	snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream);
+	int (*wall_clock)(struct snd_pcm_substream *substream,
+			  struct timespec *audio_ts);
 	int (*copy)(struct snd_pcm_substream *substream, int channel,
 		    snd_pcm_uframes_t pos,
 		    void __user *buf, snd_pcm_uframes_t count);
diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 92b104e..85b2e4d 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -136,7 +136,7 @@ struct snd_hwdep_dsp_image {
  *                                                                           *
  *****************************************************************************/
 
-#define SNDRV_PCM_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 10)
+#define SNDRV_PCM_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 11)
 
 typedef unsigned long snd_pcm_uframes_t;
 typedef signed long snd_pcm_sframes_t;
@@ -258,6 +258,7 @@ typedef int __bitwise snd_pcm_subformat_t;
 #define SNDRV_PCM_INFO_JOINT_DUPLEX	0x00200000	/* playback and capture stream are somewhat correlated */
 #define SNDRV_PCM_INFO_SYNC_START	0x00400000	/* pcm support some kind of sync go */
 #define SNDRV_PCM_INFO_NO_PERIOD_WAKEUP	0x00800000	/* period wakeup can be disabled */
+#define SNDRV_PCM_INFO_HAS_WALL_CLOCK   0x01000000      /* has audio wall clock for audio/system time sync */
 #define SNDRV_PCM_INFO_FIFO_IN_FRAMES	0x80000000	/* internal kernel flag - FIFO size is in frames */
 
 typedef int __bitwise snd_pcm_state_t;
@@ -406,7 +407,8 @@ struct snd_pcm_status {
 	snd_pcm_uframes_t avail_max;	/* max frames available on hw since last status */
 	snd_pcm_uframes_t overrange;	/* count of ADC (capture) overrange detections from last status */
 	snd_pcm_state_t suspended_state; /* suspended stream state */
-	unsigned char reserved[60];	/* must be filled with zero */
+	struct timespec audio_tstamp;	/* from sample counter or wall clock */
+	unsigned char reserved[60-sizeof(struct timespec)]; /* must be filled with zero */
 };
 
 struct snd_pcm_mmap_status {
@@ -415,6 +417,7 @@ struct snd_pcm_mmap_status {
 	snd_pcm_uframes_t hw_ptr;	/* RO: hw ptr (0...boundary-1) */
 	struct timespec tstamp;		/* Timestamp */
 	snd_pcm_state_t suspended_state; /* RO: suspended stream state */
+	struct timespec audio_tstamp;	/* from sample counter or wall clock */
 };
 
 struct snd_pcm_mmap_control {
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c
index 91cdf94..af2a3fd 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 3dc029e..c4840ff 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 5e12e5b..7c80001 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;
 		}
 	}
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 3/3] ALSA: hda: support for wallclock timestamps
  2012-10-22 21:42 [PATCH 1/3] ALSA: core: keep track of boundary wrap-around Pierre-Louis Bossart
  2012-10-22 21:42 ` [PATCH 2/3] ALSA: core: add hooks for audio timestamps Pierre-Louis Bossart
@ 2012-10-22 21:42 ` Pierre-Louis Bossart
  2012-10-23 14:43 ` [PATCH 1/3] ALSA: core: keep track of boundary wrap-around Takashi Iwai
  2 siblings, 0 replies; 4+ messages in thread
From: Pierre-Louis Bossart @ 2012-10-22 21:42 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, Pierre-Louis Bossart

Reuse code from clocksource to handle wall clock counter.
Since wrapparound occurs, the audio timestamp is reinitialized
to zero on a trigger. Synchronized linked devices will
start counting from same reference to avoid any drift.

Max buffer time is limited to 178 seconds to make sure
wall clock counter does not overflow

Wallclock timestamps are disabled on capture streams
until we figure out how to handle digital inputs.

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
---
 sound/pci/hda/hda_intel.c |   94 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 94 insertions(+)

diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index b6ffdd8..d6cf418 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -47,6 +47,9 @@
 #include <linux/reboot.h>
 #include <linux/io.h>
 #include <linux/pm_runtime.h>
+#include <linux/clocksource.h>
+#include <linux/time.h>
+
 #ifdef CONFIG_X86
 /* for snoop control */
 #include <asm/pgtable.h>
@@ -419,6 +422,9 @@ struct azx_dev {
 	unsigned int insufficient :1;
 	unsigned int wc_marked:1;
 	unsigned int no_period_wakeup:1;
+
+	struct timecounter  azx_tc;
+	struct cyclecounter azx_cc;
 };
 
 /* CORB/RIRB */
@@ -1749,6 +1755,64 @@ static inline void azx_release_device(struct azx_dev *azx_dev)
 	azx_dev->opened = 0;
 }
 
+static cycle_t azx_cc_read(const struct cyclecounter *cc)
+{
+	struct azx_dev *azx_dev = container_of(cc, struct azx_dev, azx_cc);
+	struct snd_pcm_substream *substream = azx_dev->substream;
+	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+	struct azx *chip = apcm->chip;
+
+	return azx_readl(chip, WALLCLK);
+}
+
+static void azx_timecounter_init(struct snd_pcm_substream *substream,
+				bool force, cycle_t last)
+{
+	struct azx_dev *azx_dev = get_azx_dev(substream);
+	struct timecounter *tc = &azx_dev->azx_tc;
+	struct cyclecounter *cc = &azx_dev->azx_cc;
+	u64 nsec;
+
+	cc->read = azx_cc_read;
+	cc->mask = CLOCKSOURCE_MASK(32);
+
+	/*
+	 * Converting from 24 MHz to ns means applying a 125/3 factor.
+	 * To avoid any saturation issues in intermediate operations,
+	 * the 125 factor is applied first. The division is applied
+	 * last after reading the timecounter value.
+	 * Applying the 1/3 factor as part of the multiplication
+	 * requires at least 20 bits for a decent precision, however
+	 * overflows occur after about 4 hours or less, not a option.
+	 */
+
+	cc->mult = 125; /* saturation after 195 years */
+	cc->shift = 0;
+
+	nsec = 0; /* audio time is elapsed time since trigger */
+	timecounter_init(tc, cc, nsec);
+	if (force)
+		/*
+		 * force timecounter to use predefined value,
+		 * used for synchronized starts
+		 */
+		tc->cycle_last = last;
+}
+
+static int azx_get_wallclock_tstamp(struct snd_pcm_substream *substream,
+				struct timespec *ts)
+{
+	struct azx_dev *azx_dev = get_azx_dev(substream);
+	u64 nsec;
+
+	nsec = timecounter_read(&azx_dev->azx_tc);
+	nsec = div_u64(nsec, 3); /* can be optimized */
+
+	*ts = ns_to_timespec(nsec);
+
+	return 0;
+}
+
 static struct snd_pcm_hardware azx_pcm_hw = {
 	.info =			(SNDRV_PCM_INFO_MMAP |
 				 SNDRV_PCM_INFO_INTERLEAVED |
@@ -1758,6 +1822,7 @@ static struct snd_pcm_hardware azx_pcm_hw = {
 				 /* SNDRV_PCM_INFO_RESUME |*/
 				 SNDRV_PCM_INFO_PAUSE |
 				 SNDRV_PCM_INFO_SYNC_START |
+				 SNDRV_PCM_INFO_HAS_WALL_CLOCK |
 				 SNDRV_PCM_INFO_NO_PERIOD_WAKEUP),
 	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
 	.rates =		SNDRV_PCM_RATE_48000,
@@ -1797,6 +1862,12 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
 	runtime->hw.rates = hinfo->rates;
 	snd_pcm_limit_hw_rates(runtime);
 	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+
+	/* avoid wrap-around with wall-clock */
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME,
+				20,
+				178000000);
+
 	if (chip->align_buffer_size)
 		/* constrain buffer sizes to be multiple of 128
 		   bytes. This is more efficient in terms of memory
@@ -1836,6 +1907,12 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
 		mutex_unlock(&chip->open_mutex);
 		return -EINVAL;
 	}
+
+	/* disable WALLCLOCK timestamps for capture streams
+	   until we figure out how to handle digital inputs */
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK;
+
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	azx_dev->substream = substream;
 	azx_dev->running = 0;
@@ -2072,6 +2149,22 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 			azx_readl(chip, OLD_SSYNC) & ~sbits);
 	else
 		azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) & ~sbits);
+	if (start) {
+		azx_timecounter_init(substream, 0, 0);
+		if (nsync > 1) {
+			cycle_t cycle_last;
+
+			/* same start cycle for master and group */
+			azx_dev = get_azx_dev(substream);
+			cycle_last = azx_dev->azx_tc.cycle_last;
+
+			snd_pcm_group_for_each_entry(s, substream) {
+				if (s->pcm->card != substream->pcm->card)
+					continue;
+				azx_timecounter_init(s, 1, cycle_last);
+			}
+		}
+	}
 	spin_unlock(&chip->reg_lock);
 	return 0;
 }
@@ -2308,6 +2401,7 @@ static struct snd_pcm_ops azx_pcm_ops = {
 	.prepare = azx_pcm_prepare,
 	.trigger = azx_pcm_trigger,
 	.pointer = azx_pcm_pointer,
+	.wall_clock =  azx_get_wallclock_tstamp,
 	.mmap = azx_pcm_mmap,
 	.page = snd_pcm_sgbuf_ops_page,
 };
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH 1/3] ALSA: core: keep track of boundary wrap-around
  2012-10-22 21:42 [PATCH 1/3] ALSA: core: keep track of boundary wrap-around Pierre-Louis Bossart
  2012-10-22 21:42 ` [PATCH 2/3] ALSA: core: add hooks for audio timestamps Pierre-Louis Bossart
  2012-10-22 21:42 ` [PATCH 3/3] ALSA: hda: support for wallclock timestamps Pierre-Louis Bossart
@ 2012-10-23 14:43 ` Takashi Iwai
  2 siblings, 0 replies; 4+ messages in thread
From: Takashi Iwai @ 2012-10-23 14:43 UTC (permalink / raw)
  To: Pierre-Louis Bossart; +Cc: alsa-devel

At Mon, 22 Oct 2012 16:42:14 -0500,
Pierre-Louis Bossart wrote:
> 
> Keep track of boundary crossing when hw_ptr
> exceeds boundary limit and wraps-around. This
> will help keep track of total number
> of frames played/received at the kernel level
> 
> Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>

Applied all three patches now.  Thanks.


Takashi

> ---
>  include/sound/pcm.h  |    1 +
>  sound/core/pcm_lib.c |   25 ++++++++++++++++++++-----
>  2 files changed, 21 insertions(+), 5 deletions(-)
> 
> diff --git a/include/sound/pcm.h b/include/sound/pcm.h
> index 6268a41..28fd9f9 100644
> --- a/include/sound/pcm.h
> +++ b/include/sound/pcm.h
> @@ -281,6 +281,7 @@ struct snd_pcm_runtime {
>  	unsigned long hw_ptr_jiffies;	/* Time when hw_ptr is updated */
>  	unsigned long hw_ptr_buffer_jiffies; /* buffer time in jiffies */
>  	snd_pcm_sframes_t delay;	/* extra delay; typically FIFO size */
> +	u64 hw_ptr_wrap;                /* offset for hw_ptr due to boundary wrap-around */
>  
>  	/* -- HW params -- */
>  	snd_pcm_access_t access;	/* access mode */
> diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
> index f42c10a..3dc029e 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;
> +	int crossed_boundary = 0;
>  
>  	old_hw_ptr = runtime->status->hw_ptr;
>  
> @@ -360,8 +361,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
>  			hdelta = curr_jiffies - runtime->hw_ptr_jiffies;
>  			if (hdelta > runtime->hw_ptr_buffer_jiffies/2) {
>  				hw_base += runtime->buffer_size;
> -				if (hw_base >= runtime->boundary)
> +				if (hw_base >= runtime->boundary) {
>  					hw_base = 0;
> +					crossed_boundary++;
> +				}
>  				new_hw_ptr = hw_base + pos;
>  				goto __delta;
>  			}
> @@ -371,8 +374,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
>  	/* pointer crosses the end of the ring buffer */
>  	if (new_hw_ptr < old_hw_ptr) {
>  		hw_base += runtime->buffer_size;
> -		if (hw_base >= runtime->boundary)
> +		if (hw_base >= runtime->boundary) {
>  			hw_base = 0;
> +			crossed_boundary++;
> +		}
>  		new_hw_ptr = hw_base + pos;
>  	}
>        __delta:
> @@ -410,8 +415,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
>  		while (hdelta > xrun_threshold) {
>  			delta += runtime->buffer_size;
>  			hw_base += runtime->buffer_size;
> -			if (hw_base >= runtime->boundary)
> +			if (hw_base >= runtime->boundary) {
>  				hw_base = 0;
> +				crossed_boundary++;
> +			}
>  			new_hw_ptr = hw_base + pos;
>  			hdelta -= runtime->hw_ptr_buffer_jiffies;
>  		}
> @@ -456,8 +463,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
>  		/* the delta value is small or zero in most cases */
>  		while (delta > 0) {
>  			new_hw_ptr += runtime->period_size;
> -			if (new_hw_ptr >= runtime->boundary)
> +			if (new_hw_ptr >= runtime->boundary) {
>  				new_hw_ptr -= runtime->boundary;
> +				crossed_boundary--;
> +			}
>  			delta--;
>  		}
>  		/* align hw_base to buffer_size */
> @@ -507,6 +516,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
>  	runtime->hw_ptr_base = hw_base;
>  	runtime->status->hw_ptr = new_hw_ptr;
>  	runtime->hw_ptr_jiffies = curr_jiffies;
> +	if (crossed_boundary) {
> +		snd_BUG_ON(crossed_boundary != 1);
> +		runtime->hw_ptr_wrap += runtime->boundary;
> +	}
>  	if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
>  		runtime->status->tstamp = curr_tstamp;
>  
> @@ -1661,8 +1674,10 @@ static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream,
>  	if (snd_pcm_running(substream) &&
>  	    snd_pcm_update_hw_ptr(substream) >= 0)
>  		runtime->status->hw_ptr %= runtime->buffer_size;
> -	else
> +	else {
>  		runtime->status->hw_ptr = 0;
> +		runtime->hw_ptr_wrap = 0;
> +	}
>  	snd_pcm_stream_unlock_irqrestore(substream, flags);
>  	return 0;
>  }
> -- 
> 1.7.9.5
> 
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
> 

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2012-10-23 14:43 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-10-22 21:42 [PATCH 1/3] ALSA: core: keep track of boundary wrap-around Pierre-Louis Bossart
2012-10-22 21:42 ` [PATCH 2/3] ALSA: core: add hooks for audio timestamps Pierre-Louis Bossart
2012-10-22 21:42 ` [PATCH 3/3] ALSA: hda: support for wallclock timestamps Pierre-Louis Bossart
2012-10-23 14:43 ` [PATCH 1/3] ALSA: core: keep track of boundary wrap-around Takashi Iwai

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.