[alsa-devel] [PATCH v2 3/3] ALSA: hda: support for wallclock timestamps

Takashi Iwai tiwai at suse.de
Tue Oct 9 11:40:25 CEST 2012


At Mon,  8 Oct 2012 12:11:58 -0500,
Pierre-Louis Bossart wrote:
> 
> 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 at linux.intel.com>
> ---
>  sound/pci/hda/hda_intel.c |   95 +++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 95 insertions(+)
> 
> diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
> index b682442..0245382 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>
> @@ -406,6 +410,9 @@ struct azx_dev {
>  	 */
>  	unsigned int insufficient :1;
>  	unsigned int wc_marked:1;
> +
> +	struct timecounter  azx_tc;
> +	struct cyclecounter azx_cc;
>  };
>  
>  /* CORB/RIRB */
> @@ -1703,6 +1710,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 /= 3; /* can be optimized */

Use div_u64() instead.


Takashi


More information about the Alsa-devel mailing list