[alsa-devel] [PATCH 2/2] ALSA: hda: support for wallclock timestamps
Pierre-Louis Bossart
pierre-louis.bossart at linux.intel.com
Thu Jun 28 23:12:35 CEST 2012
reuse code from clocksource to handle wall clock counter.
Since wrapparound occurs, the timestamps are reinitialized with
monotonic time on a trigger.
TODO:
- only re-init timecounter if there was no device active.
- Keep the same timestamp for all devices on same chip.
- make sure no overflow occurs in the 125/3 scaling implementation
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart at linux.intel.com>
---
sound/pci/hda/hda_intel.c | 54 ++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 53 insertions(+), 1 deletions(-)
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 86758dd..9bb795b 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -46,6 +46,10 @@
#include <linux/mutex.h>
#include <linux/reboot.h>
#include <linux/io.h>
+
+#include <linux/clocksource.h>
+#include <linux/time.h>
+
#ifdef CONFIG_X86
/* for snoop control */
#include <asm/pgtable.h>
@@ -496,6 +500,10 @@ struct azx {
/* reboot notifier (for mysterious hangup problem at power-down) */
struct notifier_block reboot_notifier;
+
+ struct timecounter azx_tc;
+ struct cyclecounter azx_cc;
+
};
/* driver types */
@@ -1700,6 +1708,45 @@ 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 *chip = container_of(cc, struct azx, azx_cc);
+
+ return azx_readl(chip, WALLCLK);
+}
+
+static void azx_timecounter_init(struct azx *chip)
+{
+ struct timecounter *tc = &chip->azx_tc;
+ struct cyclecounter *cc = &chip->azx_cc;
+ struct timespec ts;
+ u64 nsec;
+
+ cc->read = azx_cc_read;
+ cc->mask = CLOCKSOURCE_MASK(32);
+
+#define NBITS_NS 12 /* accuracy issues in cyc2ns with lower number of bits */
+ cc->mult = clocksource_khz2mult(24000, NBITS_NS);
+ cc->shift = NBITS_NS;
+
+ ktime_get_ts(&ts);
+ nsec = timespec_to_ns(&ts);
+ timecounter_init(tc, cc, nsec);
+}
+
+static int azx_get_wallclock_tstamp(struct snd_pcm_substream *substream,
+ struct timespec *ts)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct azx *chip = apcm->chip;
+ u64 nsec;
+
+ nsec = timecounter_read(&chip->azx_tc);
+ *ts = ns_to_timespec(nsec);
+
+ return 0;
+}
+
static struct snd_pcm_hardware azx_pcm_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -1709,6 +1756,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,
@@ -1982,8 +2030,10 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
}
spin_unlock(&chip->reg_lock);
if (start) {
- if (nsync == 1)
+ if (nsync == 1) {
+ azx_timecounter_init(chip);
return 0;
+ }
/* wait until all FIFOs get ready */
for (timeout = 5000; timeout; timeout--) {
nwait = 0;
@@ -1999,6 +2049,7 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
break;
cpu_relax();
}
+ azx_timecounter_init(chip);
} else {
/* wait until all RUN bits are cleared */
for (timeout = 5000; timeout; timeout--) {
@@ -2240,6 +2291,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.6.5
More information about the Alsa-devel
mailing list