Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 342563
b: refs/heads/master
c: 5d890f5
h: refs/heads/master
i:
  342561: 3eb76fd
  342559: a14ec5f
v: v3
  • Loading branch information
Pierre-Louis Bossart authored and Takashi Iwai committed Oct 23, 2012
1 parent 225b8a3 commit ab0f3aa
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 1 deletion.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 4eeaaeaea1cec60a25979678182720dc91308550
refs/heads/master: 5d890f591d15d1161131546c2af371178c967734
94 changes: 94 additions & 0 deletions trunk/sound/pci/hda/hda_intel.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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 |
Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -2306,6 +2399,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,
};
Expand Down

0 comments on commit ab0f3aa

Please sign in to comment.