Commit 45c27fc8 authored by Takashi Iwai's avatar Takashi Iwai

Merge branch 'master' of git://git.alsa-project.org/alsa-kernel into for-linus

* 'master' of git://git.alsa-project.org/alsa-kernel:
  [ALSA] intel8x0: add one retry to the ac97_clock measurement routine
  [ALSA] intel8x0: fix wrong conditions in ac97_clock measure routine
  [ALSA] intel8x0: do not use zero value from PICB register
  [ALSA] intel8x0: an attempt to make ac97_clock measurement more reliable
  [ALSA] pcm-midlevel: Add more strict buffer position checks based on jiffies
  [ALSA] hda_intel: fix unexpected ring buffer positions
parents 0882e8dd 2ec775e7
...@@ -268,7 +268,8 @@ struct snd_pcm_runtime { ...@@ -268,7 +268,8 @@ struct snd_pcm_runtime {
int overrange; int overrange;
snd_pcm_uframes_t avail_max; snd_pcm_uframes_t avail_max;
snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */ snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */
snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time*/ snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time */
unsigned long hw_ptr_jiffies; /* Time when hw_ptr is updated */
/* -- HW params -- */ /* -- HW params -- */
snd_pcm_access_t access; /* access mode */ snd_pcm_access_t access; /* access mode */
......
...@@ -209,9 +209,11 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) ...@@ -209,9 +209,11 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
{ {
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t pos; snd_pcm_uframes_t pos;
snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt, hw_base; snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_ptr_interrupt, hw_base;
snd_pcm_sframes_t delta; snd_pcm_sframes_t hdelta, delta;
unsigned long jdelta;
old_hw_ptr = runtime->status->hw_ptr;
pos = snd_pcm_update_hw_ptr_pos(substream, runtime); pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
if (pos == SNDRV_PCM_POS_XRUN) { if (pos == SNDRV_PCM_POS_XRUN) {
xrun(substream); xrun(substream);
...@@ -247,7 +249,30 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) ...@@ -247,7 +249,30 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
new_hw_ptr = hw_base + pos; new_hw_ptr = hw_base + pos;
} }
} }
if (delta > runtime->period_size) { hdelta = new_hw_ptr - old_hw_ptr;
jdelta = jiffies - runtime->hw_ptr_jiffies;
if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) {
delta = jdelta /
(((runtime->period_size * HZ) / runtime->rate)
+ HZ/100);
hw_ptr_error(substream,
"hw_ptr skipping! [Q] "
"(pos=%ld, delta=%ld, period=%ld, "
"jdelta=%lu/%lu/%lu)\n",
(long)pos, (long)hdelta,
(long)runtime->period_size, jdelta,
((hdelta * HZ) / runtime->rate), delta);
hw_ptr_interrupt = runtime->hw_ptr_interrupt +
runtime->period_size * delta;
if (hw_ptr_interrupt >= runtime->boundary)
hw_ptr_interrupt -= runtime->boundary;
/* rebase to interrupt position */
hw_base = new_hw_ptr = hw_ptr_interrupt;
/* align hw_base to buffer_size */
hw_base -= hw_base % runtime->buffer_size;
delta = 0;
}
if (delta > runtime->period_size + runtime->period_size / 2) {
hw_ptr_error(substream, hw_ptr_error(substream,
"Lost interrupts? " "Lost interrupts? "
"(stream=%i, delta=%ld, intr_ptr=%ld)\n", "(stream=%i, delta=%ld, intr_ptr=%ld)\n",
...@@ -263,6 +288,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) ...@@ -263,6 +288,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
runtime->hw_ptr_base = hw_base; runtime->hw_ptr_base = hw_base;
runtime->status->hw_ptr = new_hw_ptr; runtime->status->hw_ptr = new_hw_ptr;
runtime->hw_ptr_jiffies = jiffies;
runtime->hw_ptr_interrupt = hw_ptr_interrupt; runtime->hw_ptr_interrupt = hw_ptr_interrupt;
return snd_pcm_update_hw_ptr_post(substream, runtime); return snd_pcm_update_hw_ptr_post(substream, runtime);
...@@ -275,6 +301,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) ...@@ -275,6 +301,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
snd_pcm_uframes_t pos; snd_pcm_uframes_t pos;
snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base; snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
snd_pcm_sframes_t delta; snd_pcm_sframes_t delta;
unsigned long jdelta;
old_hw_ptr = runtime->status->hw_ptr; old_hw_ptr = runtime->status->hw_ptr;
pos = snd_pcm_update_hw_ptr_pos(substream, runtime); pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
...@@ -286,14 +313,15 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) ...@@ -286,14 +313,15 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
new_hw_ptr = hw_base + pos; new_hw_ptr = hw_base + pos;
delta = new_hw_ptr - old_hw_ptr; delta = new_hw_ptr - old_hw_ptr;
jdelta = jiffies - runtime->hw_ptr_jiffies;
if (delta < 0) { if (delta < 0) {
delta += runtime->buffer_size; delta += runtime->buffer_size;
if (delta < 0) { if (delta < 0) {
hw_ptr_error(substream, hw_ptr_error(substream,
"Unexpected hw_pointer value [2] " "Unexpected hw_pointer value [2] "
"(stream=%i, pos=%ld, old_ptr=%ld)\n", "(stream=%i, pos=%ld, old_ptr=%ld, jdelta=%li)\n",
substream->stream, (long)pos, substream->stream, (long)pos,
(long)old_hw_ptr); (long)old_hw_ptr, jdelta);
return 0; return 0;
} }
hw_base += runtime->buffer_size; hw_base += runtime->buffer_size;
...@@ -301,12 +329,13 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) ...@@ -301,12 +329,13 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
hw_base = 0; hw_base = 0;
new_hw_ptr = hw_base + pos; new_hw_ptr = hw_base + pos;
} }
if (delta > runtime->period_size && runtime->periods > 1) { if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) {
hw_ptr_error(substream, hw_ptr_error(substream,
"hw_ptr skipping! " "hw_ptr skipping! "
"(pos=%ld, delta=%ld, period=%ld)\n", "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n",
(long)pos, (long)delta, (long)pos, (long)delta,
(long)runtime->period_size); (long)runtime->period_size, jdelta,
((delta * HZ) / runtime->rate));
return 0; return 0;
} }
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
...@@ -315,6 +344,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) ...@@ -315,6 +344,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
runtime->hw_ptr_base = hw_base; runtime->hw_ptr_base = hw_base;
runtime->status->hw_ptr = new_hw_ptr; runtime->status->hw_ptr = new_hw_ptr;
runtime->hw_ptr_jiffies = jiffies;
return snd_pcm_update_hw_ptr_post(substream, runtime); return snd_pcm_update_hw_ptr_post(substream, runtime);
} }
...@@ -1441,6 +1471,7 @@ static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream, ...@@ -1441,6 +1471,7 @@ static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream,
runtime->status->hw_ptr %= runtime->buffer_size; runtime->status->hw_ptr %= runtime->buffer_size;
else else
runtime->status->hw_ptr = 0; runtime->status->hw_ptr = 0;
runtime->hw_ptr_jiffies = jiffies;
snd_pcm_stream_unlock_irqrestore(substream, flags); snd_pcm_stream_unlock_irqrestore(substream, flags);
return 0; return 0;
} }
......
...@@ -312,6 +312,9 @@ struct azx_dev { ...@@ -312,6 +312,9 @@ struct azx_dev {
unsigned int period_bytes; /* size of the period in bytes */ unsigned int period_bytes; /* size of the period in bytes */
unsigned int frags; /* number for period in the play buffer */ unsigned int frags; /* number for period in the play buffer */
unsigned int fifo_size; /* FIFO size */ unsigned int fifo_size; /* FIFO size */
unsigned int start_flag: 1; /* stream full start flag */
unsigned long start_jiffies; /* start + minimum jiffies */
unsigned long min_jiffies; /* minimum jiffies before position is valid */
void __iomem *sd_addr; /* stream descriptor pointer */ void __iomem *sd_addr; /* stream descriptor pointer */
...@@ -330,7 +333,6 @@ struct azx_dev { ...@@ -330,7 +333,6 @@ struct azx_dev {
unsigned int opened :1; unsigned int opened :1;
unsigned int running :1; unsigned int running :1;
unsigned int irq_pending :1; unsigned int irq_pending :1;
unsigned int irq_ignore :1;
/* /*
* For VIA: * For VIA:
* A flag to ensure DMA position is 0 * A flag to ensure DMA position is 0
...@@ -975,7 +977,7 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id) ...@@ -975,7 +977,7 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
struct azx *chip = dev_id; struct azx *chip = dev_id;
struct azx_dev *azx_dev; struct azx_dev *azx_dev;
u32 status; u32 status;
int i; int i, ok;
spin_lock(&chip->reg_lock); spin_lock(&chip->reg_lock);
...@@ -991,18 +993,14 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id) ...@@ -991,18 +993,14 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
if (!azx_dev->substream || !azx_dev->running) if (!azx_dev->substream || !azx_dev->running)
continue; continue;
/* ignore the first dummy IRQ (due to pos_adj) */
if (azx_dev->irq_ignore) {
azx_dev->irq_ignore = 0;
continue;
}
/* check whether this IRQ is really acceptable */ /* check whether this IRQ is really acceptable */
if (azx_position_ok(chip, azx_dev)) { ok = azx_position_ok(chip, azx_dev);
if (ok == 1) {
azx_dev->irq_pending = 0; azx_dev->irq_pending = 0;
spin_unlock(&chip->reg_lock); spin_unlock(&chip->reg_lock);
snd_pcm_period_elapsed(azx_dev->substream); snd_pcm_period_elapsed(azx_dev->substream);
spin_lock(&chip->reg_lock); spin_lock(&chip->reg_lock);
} else if (chip->bus && chip->bus->workq) { } else if (ok == 0 && chip->bus && chip->bus->workq) {
/* bogus IRQ, process it later */ /* bogus IRQ, process it later */
azx_dev->irq_pending = 1; azx_dev->irq_pending = 1;
queue_work(chip->bus->workq, queue_work(chip->bus->workq,
...@@ -1088,7 +1086,6 @@ static int azx_setup_periods(struct azx *chip, ...@@ -1088,7 +1086,6 @@ static int azx_setup_periods(struct azx *chip,
bdl = (u32 *)azx_dev->bdl.area; bdl = (u32 *)azx_dev->bdl.area;
ofs = 0; ofs = 0;
azx_dev->frags = 0; azx_dev->frags = 0;
azx_dev->irq_ignore = 0;
pos_adj = bdl_pos_adj[chip->dev_index]; pos_adj = bdl_pos_adj[chip->dev_index];
if (pos_adj > 0) { if (pos_adj > 0) {
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
...@@ -1109,7 +1106,6 @@ static int azx_setup_periods(struct azx *chip, ...@@ -1109,7 +1106,6 @@ static int azx_setup_periods(struct azx *chip,
&bdl, ofs, pos_adj, 1); &bdl, ofs, pos_adj, 1);
if (ofs < 0) if (ofs < 0)
goto error; goto error;
azx_dev->irq_ignore = 1;
} }
} else } else
pos_adj = 0; pos_adj = 0;
...@@ -1155,6 +1151,9 @@ static void azx_stream_reset(struct azx *chip, struct azx_dev *azx_dev) ...@@ -1155,6 +1151,9 @@ static void azx_stream_reset(struct azx *chip, struct azx_dev *azx_dev)
while (((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) && while (((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) &&
--timeout) --timeout)
; ;
/* reset first position - may not be synced with hw at this time */
*azx_dev->posbuf = 0;
} }
/* /*
...@@ -1409,7 +1408,6 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) ...@@ -1409,7 +1408,6 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
snd_pcm_set_sync(substream); snd_pcm_set_sync(substream);
mutex_unlock(&chip->open_mutex); mutex_unlock(&chip->open_mutex);
azx_stream_reset(chip, azx_dev);
return 0; return 0;
} }
...@@ -1474,6 +1472,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) ...@@ -1474,6 +1472,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
unsigned int bufsize, period_bytes, format_val; unsigned int bufsize, period_bytes, format_val;
int err; int err;
azx_stream_reset(chip, azx_dev);
format_val = snd_hda_calc_stream_format(runtime->rate, format_val = snd_hda_calc_stream_format(runtime->rate,
runtime->channels, runtime->channels,
runtime->format, runtime->format,
...@@ -1502,6 +1501,8 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) ...@@ -1502,6 +1501,8 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
return err; return err;
} }
azx_dev->min_jiffies = (runtime->period_size * HZ) /
(runtime->rate * 2);
azx_setup_controller(chip, azx_dev); azx_setup_controller(chip, azx_dev);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1; azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1;
...@@ -1518,13 +1519,14 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) ...@@ -1518,13 +1519,14 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
struct azx *chip = apcm->chip; struct azx *chip = apcm->chip;
struct azx_dev *azx_dev; struct azx_dev *azx_dev;
struct snd_pcm_substream *s; struct snd_pcm_substream *s;
int start, nsync = 0, sbits = 0; int rstart = 0, start, nsync = 0, sbits = 0;
int nwait, timeout; int nwait, timeout;
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
rstart = 1;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_START:
start = 1; start = 1;
break; break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
...@@ -1554,6 +1556,10 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) ...@@ -1554,6 +1556,10 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
if (s->pcm->card != substream->pcm->card) if (s->pcm->card != substream->pcm->card)
continue; continue;
azx_dev = get_azx_dev(s); azx_dev = get_azx_dev(s);
if (rstart) {
azx_dev->start_flag = 1;
azx_dev->start_jiffies = jiffies + azx_dev->min_jiffies;
}
if (start) if (start)
azx_stream_start(chip, azx_dev); azx_stream_start(chip, azx_dev);
else else
...@@ -1703,6 +1709,11 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) ...@@ -1703,6 +1709,11 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
{ {
unsigned int pos; unsigned int pos;
if (azx_dev->start_flag &&
time_before_eq(jiffies, azx_dev->start_jiffies))
return -1; /* bogus (too early) interrupt */
azx_dev->start_flag = 0;
pos = azx_get_position(chip, azx_dev); pos = azx_get_position(chip, azx_dev);
if (chip->position_fix == POS_FIX_AUTO) { if (chip->position_fix == POS_FIX_AUTO) {
if (!pos) { if (!pos) {
......
...@@ -355,6 +355,9 @@ struct ichdev { ...@@ -355,6 +355,9 @@ struct ichdev {
unsigned int fragsize1; unsigned int fragsize1;
unsigned int position; unsigned int position;
unsigned int pos_shift; unsigned int pos_shift;
unsigned int last_pos;
unsigned long last_pos_jiffies;
unsigned int jiffy_to_bytes;
int frags; int frags;
int lvi; int lvi;
int lvi_frag; int lvi_frag;
...@@ -838,7 +841,10 @@ static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd ...@@ -838,7 +841,10 @@ static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd
ichdev->suspended = 0; ichdev->suspended = 0;
/* fallthru */ /* fallthru */
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
val = ICH_IOCE | ICH_STARTBM; val = ICH_IOCE | ICH_STARTBM;
ichdev->last_pos = ichdev->position;
ichdev->last_pos_jiffies = jiffies;
break; break;
case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_SUSPEND:
ichdev->suspended = 1; ichdev->suspended = 1;
...@@ -849,9 +855,6 @@ static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd ...@@ -849,9 +855,6 @@ static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
val = ICH_IOCE; val = ICH_IOCE;
break; break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
val = ICH_IOCE | ICH_STARTBM;
break;
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -1045,6 +1048,7 @@ static int snd_intel8x0_pcm_prepare(struct snd_pcm_substream *substream) ...@@ -1045,6 +1048,7 @@ static int snd_intel8x0_pcm_prepare(struct snd_pcm_substream *substream)
ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1; ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1;
} }
snd_intel8x0_setup_periods(chip, ichdev); snd_intel8x0_setup_periods(chip, ichdev);
ichdev->jiffy_to_bytes = (runtime->rate * 4 * ichdev->pos_shift) / HZ;
return 0; return 0;
} }
...@@ -1053,7 +1057,7 @@ static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(struct snd_pcm_substream *subs ...@@ -1053,7 +1057,7 @@ static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(struct snd_pcm_substream *subs
struct intel8x0 *chip = snd_pcm_substream_chip(substream); struct intel8x0 *chip = snd_pcm_substream_chip(substream);
struct ichdev *ichdev = get_ichdev(substream); struct ichdev *ichdev = get_ichdev(substream);
size_t ptr1, ptr; size_t ptr1, ptr;
int civ, timeout = 100; int civ, timeout = 10;
unsigned int position; unsigned int position;
spin_lock(&chip->reg_lock); spin_lock(&chip->reg_lock);
...@@ -1069,9 +1073,19 @@ static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(struct snd_pcm_substream *subs ...@@ -1069,9 +1073,19 @@ static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(struct snd_pcm_substream *subs
ptr1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb)) ptr1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb))
break; break;
} while (timeout--); } while (timeout--);
if (ptr1 != 0) {
ptr1 <<= ichdev->pos_shift; ptr1 <<= ichdev->pos_shift;
ptr = ichdev->fragsize1 - ptr1; ptr = ichdev->fragsize1 - ptr1;
ptr += position; ptr += position;
ichdev->last_pos = ptr;
ichdev->last_pos_jiffies = jiffies;
} else {
ptr1 = jiffies - ichdev->last_pos_jiffies;
if (ptr1)
ptr1 -= 1;
ptr = ichdev->last_pos + ptr1 * ichdev->jiffy_to_bytes;
ptr %= ichdev->size;
}
spin_unlock(&chip->reg_lock); spin_unlock(&chip->reg_lock);
if (ptr >= ichdev->size) if (ptr >= ichdev->size)
return 0; return 0;
...@@ -2661,12 +2675,14 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip) ...@@ -2661,12 +2675,14 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
struct snd_pcm_substream *subs; struct snd_pcm_substream *subs;
struct ichdev *ichdev; struct ichdev *ichdev;
unsigned long port; unsigned long port;
unsigned long pos, t; unsigned long pos, pos1, t;
struct timeval start_time, stop_time; int civ, timeout = 1000, attempt = 1;
struct timespec start_time, stop_time;
if (chip->ac97_bus->clock != 48000) if (chip->ac97_bus->clock != 48000)
return; /* specified in module option */ return; /* specified in module option */
__again:
subs = chip->pcm[0]->streams[0].substream; subs = chip->pcm[0]->streams[0].substream;
if (! subs || subs->dma_buffer.bytes < INTEL8X0_TESTBUF_SIZE) { if (! subs || subs->dma_buffer.bytes < INTEL8X0_TESTBUF_SIZE) {
snd_printk(KERN_WARNING "no playback buffer allocated - aborting measure ac97 clock\n"); snd_printk(KERN_WARNING "no playback buffer allocated - aborting measure ac97 clock\n");
...@@ -2674,7 +2690,7 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip) ...@@ -2674,7 +2690,7 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
} }
ichdev = &chip->ichd[ICHD_PCMOUT]; ichdev = &chip->ichd[ICHD_PCMOUT];
ichdev->physbuf = subs->dma_buffer.addr; ichdev->physbuf = subs->dma_buffer.addr;
ichdev->size = chip->ichd[ICHD_PCMOUT].fragsize = INTEL8X0_TESTBUF_SIZE; ichdev->size = ichdev->fragsize = INTEL8X0_TESTBUF_SIZE;
ichdev->substream = NULL; /* don't process interrupts */ ichdev->substream = NULL; /* don't process interrupts */
/* set rate */ /* set rate */
...@@ -2693,16 +2709,31 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip) ...@@ -2693,16 +2709,31 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE); iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE);
iputdword(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot); iputdword(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot);
} }
do_gettimeofday(&start_time); do_posix_clock_monotonic_gettime(&start_time);
spin_unlock_irq(&chip->reg_lock); spin_unlock_irq(&chip->reg_lock);
msleep(50); msleep(50);
spin_lock_irq(&chip->reg_lock); spin_lock_irq(&chip->reg_lock);
/* check the position */ /* check the position */
do {
civ = igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV);
pos1 = igetword(chip, ichdev->reg_offset + ichdev->roff_picb);
if (pos1 == 0) {
udelay(10);
continue;
}
if (civ == igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV) &&
pos1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb))
break;
} while (timeout--);
if (pos1 == 0) { /* oops, this value is not reliable */
pos = 0;
} else {
pos = ichdev->fragsize1; pos = ichdev->fragsize1;
pos -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << ichdev->pos_shift; pos -= pos1 << ichdev->pos_shift;
pos += ichdev->position; pos += ichdev->position;
}
chip->in_measurement = 0; chip->in_measurement = 0;
do_gettimeofday(&stop_time); do_posix_clock_monotonic_gettime(&stop_time);
/* stop */ /* stop */
if (chip->device_type == DEVICE_ALI) { if (chip->device_type == DEVICE_ALI) {
iputdword(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 16)); iputdword(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 16));
...@@ -2717,19 +2748,37 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip) ...@@ -2717,19 +2748,37 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS); iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
spin_unlock_irq(&chip->reg_lock); spin_unlock_irq(&chip->reg_lock);
if (pos == 0) {
snd_printk(KERN_ERR "intel8x0: measure - unreliable DMA position..\n");
__retry:
if (attempt < 2) {
attempt++;
goto __again;
}
return;
}
pos /= 4;
t = stop_time.tv_sec - start_time.tv_sec; t = stop_time.tv_sec - start_time.tv_sec;
t *= 1000000; t *= 1000000;
t += stop_time.tv_usec - start_time.tv_usec; t += (stop_time.tv_nsec - start_time.tv_nsec) / 1000;
printk(KERN_INFO "%s: measured %lu usecs\n", __func__, t); printk(KERN_INFO "%s: measured %lu usecs (%lu samples)\n", __func__, t, pos);
if (t == 0) { if (t == 0) {
snd_printk(KERN_ERR "?? calculation error..\n"); snd_printk(KERN_ERR "intel8x0: ?? calculation error..\n");
return; goto __retry;
} }
pos = (pos / 4) * 1000; pos *= 1000;
pos = (pos / t) * 1000 + ((pos % t) * 1000) / t; pos = (pos / t) * 1000 + ((pos % t) * 1000) / t;
if (pos < 40000 || pos >= 60000) if (pos < 40000 || pos >= 60000) {
/* abnormal value. hw problem? */ /* abnormal value. hw problem? */
printk(KERN_INFO "intel8x0: measured clock %ld rejected\n", pos); printk(KERN_INFO "intel8x0: measured clock %ld rejected\n", pos);
goto __retry;
} else if (pos > 40500 && pos < 41500)
/* first exception - 41000Hz reference clock */
chip->ac97_bus->clock = 41000;
else if (pos > 43600 && pos < 44600)
/* second exception - 44100HZ reference clock */
chip->ac97_bus->clock = 44100;
else if (pos < 47500 || pos > 48500) else if (pos < 47500 || pos > 48500)
/* not 48000Hz, tuning the clock.. */ /* not 48000Hz, tuning the clock.. */
chip->ac97_bus->clock = (chip->ac97_bus->clock * 48000) / pos; chip->ac97_bus->clock = (chip->ac97_bus->clock * 48000) / pos;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment