Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -269,6 +269,7 @@ struct snd_pcm_runtime {
	snd_pcm_uframes_t avail_max;
	snd_pcm_uframes_t hw_ptr_base;	/* Position at buffer restart */
	snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time */
	unsigned long hw_ptr_jiffies;	/* Time when hw_ptr is updated */

	/* -- HW params -- */
	snd_pcm_access_t access;	/* access mode */
+39 −8
Original line number Diff line number Diff line
@@ -209,9 +209,11 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
{
	struct snd_pcm_runtime *runtime = substream->runtime;
	snd_pcm_uframes_t pos;
	snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt, hw_base;
	snd_pcm_sframes_t delta;
	snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_ptr_interrupt, hw_base;
	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);
	if (pos == SNDRV_PCM_POS_XRUN) {
		xrun(substream);
@@ -247,7 +249,30 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
			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,
			     "Lost interrupts? "
			     "(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)

	runtime->hw_ptr_base = hw_base;
	runtime->status->hw_ptr = new_hw_ptr;
	runtime->hw_ptr_jiffies = jiffies;
	runtime->hw_ptr_interrupt = hw_ptr_interrupt;

	return snd_pcm_update_hw_ptr_post(substream, runtime);
@@ -275,6 +301,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
	snd_pcm_uframes_t pos;
	snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
	snd_pcm_sframes_t delta;
	unsigned long jdelta;

	old_hw_ptr = runtime->status->hw_ptr;
	pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
@@ -286,14 +313,15 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
	new_hw_ptr = hw_base + pos;

	delta = new_hw_ptr - old_hw_ptr;
	jdelta = jiffies - runtime->hw_ptr_jiffies;
	if (delta < 0) {
		delta += runtime->buffer_size;
		if (delta < 0) {
			hw_ptr_error(substream, 
				     "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,
				     (long)old_hw_ptr);
				     (long)old_hw_ptr, jdelta);
			return 0;
		}
		hw_base += runtime->buffer_size;
@@ -301,12 +329,13 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
			hw_base = 0;
		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 skipping! "
			     "(pos=%ld, delta=%ld, period=%ld)\n",
			     "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n",
			     (long)pos, (long)delta,
			     (long)runtime->period_size);
			     (long)runtime->period_size, jdelta,
			     ((delta * HZ) / runtime->rate));
		return 0;
	}
	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
@@ -315,6 +344,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)

	runtime->hw_ptr_base = hw_base;
	runtime->status->hw_ptr = new_hw_ptr;
	runtime->hw_ptr_jiffies = jiffies;

	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,
		runtime->status->hw_ptr %= runtime->buffer_size;
	else
		runtime->status->hw_ptr = 0;
	runtime->hw_ptr_jiffies = jiffies;
	snd_pcm_stream_unlock_irqrestore(substream, flags);
	return 0;
}
+25 −14
Original line number Diff line number Diff line
@@ -312,6 +312,9 @@ struct azx_dev {
	unsigned int period_bytes; /* size of the period in bytes */
	unsigned int frags;	/* number for period in the play buffer */
	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 */

@@ -330,7 +333,6 @@ struct azx_dev {
	unsigned int opened :1;
	unsigned int running :1;
	unsigned int irq_pending :1;
	unsigned int irq_ignore :1;
	/*
	 * For VIA:
	 *  A flag to ensure DMA position is 0
@@ -975,7 +977,7 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
	struct azx *chip = dev_id;
	struct azx_dev *azx_dev;
	u32 status;
	int i;
	int i, ok;

	spin_lock(&chip->reg_lock);

@@ -991,18 +993,14 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
			azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
			if (!azx_dev->substream || !azx_dev->running)
				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 */
			if (azx_position_ok(chip, azx_dev)) {
			ok = azx_position_ok(chip, azx_dev);
			if (ok == 1) {
				azx_dev->irq_pending = 0;
				spin_unlock(&chip->reg_lock);
				snd_pcm_period_elapsed(azx_dev->substream);
				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 */
				azx_dev->irq_pending = 1;
				queue_work(chip->bus->workq,
@@ -1088,7 +1086,6 @@ static int azx_setup_periods(struct azx *chip,
	bdl = (u32 *)azx_dev->bdl.area;
	ofs = 0;
	azx_dev->frags = 0;
	azx_dev->irq_ignore = 0;
	pos_adj = bdl_pos_adj[chip->dev_index];
	if (pos_adj > 0) {
		struct snd_pcm_runtime *runtime = substream->runtime;
@@ -1109,7 +1106,6 @@ static int azx_setup_periods(struct azx *chip,
					 &bdl, ofs, pos_adj, 1);
			if (ofs < 0)
				goto error;
			azx_dev->irq_ignore = 1;
		}
	} else
		pos_adj = 0;
@@ -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) &&
	       --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)
	snd_pcm_set_sync(substream);
	mutex_unlock(&chip->open_mutex);

	azx_stream_reset(chip, azx_dev);
	return 0;
}

@@ -1474,6 +1472,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
	unsigned int bufsize, period_bytes, format_val;
	int err;

	azx_stream_reset(chip, azx_dev);
	format_val = snd_hda_calc_stream_format(runtime->rate,
						runtime->channels,
						runtime->format,
@@ -1502,6 +1501,8 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
			return err;
	}

	azx_dev->min_jiffies = (runtime->period_size * HZ) /
						(runtime->rate * 2);
	azx_setup_controller(chip, azx_dev);
	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
		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)
	struct azx *chip = apcm->chip;
	struct azx_dev *azx_dev;
	struct snd_pcm_substream *s;
	int start, nsync = 0, sbits = 0;
	int rstart = 0, start, nsync = 0, sbits = 0;
	int nwait, timeout;

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
		rstart = 1;
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
	case SNDRV_PCM_TRIGGER_RESUME:
	case SNDRV_PCM_TRIGGER_START:
		start = 1;
		break;
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
@@ -1554,6 +1556,10 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
		if (s->pcm->card != substream->pcm->card)
			continue;
		azx_dev = get_azx_dev(s);
		if (rstart) {
			azx_dev->start_flag = 1;
			azx_dev->start_jiffies = jiffies + azx_dev->min_jiffies;
		}
		if (start)
			azx_stream_start(chip, azx_dev);
		else
@@ -1703,6 +1709,11 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
{
	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);
	if (chip->position_fix == POS_FIX_AUTO) {
		if (!pos) {
+70 −21
Original line number Diff line number Diff line
@@ -355,6 +355,9 @@ struct ichdev {
        unsigned int fragsize1;
        unsigned int position;
	unsigned int pos_shift;
	unsigned int last_pos;
	unsigned long last_pos_jiffies;
	unsigned int jiffy_to_bytes;
        int frags;
        int lvi;
        int lvi_frag;
@@ -838,7 +841,10 @@ static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd
		ichdev->suspended = 0;
		/* fallthru */
	case SNDRV_PCM_TRIGGER_START:
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
		val = ICH_IOCE | ICH_STARTBM;
		ichdev->last_pos = ichdev->position;
		ichdev->last_pos_jiffies = jiffies;
		break;
	case SNDRV_PCM_TRIGGER_SUSPEND:
		ichdev->suspended = 1;
@@ -849,9 +855,6 @@ static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
		val = ICH_IOCE;
		break;
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
		val = ICH_IOCE | ICH_STARTBM;
		break;
	default:
		return -EINVAL;
	}
@@ -1045,6 +1048,7 @@ static int snd_intel8x0_pcm_prepare(struct snd_pcm_substream *substream)
			ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1;
	}
	snd_intel8x0_setup_periods(chip, ichdev);
	ichdev->jiffy_to_bytes = (runtime->rate * 4 * ichdev->pos_shift) / HZ;
	return 0;
}

@@ -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 ichdev *ichdev = get_ichdev(substream);
	size_t ptr1, ptr;
	int civ, timeout = 100;
	int civ, timeout = 10;
	unsigned int position;

	spin_lock(&chip->reg_lock);
@@ -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))
			break;
	} while (timeout--);
	if (ptr1 != 0) {
		ptr1 <<= ichdev->pos_shift;
		ptr = ichdev->fragsize1 - ptr1;
		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);
	if (ptr >= ichdev->size)
		return 0;
@@ -2661,12 +2675,14 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
	struct snd_pcm_substream *subs;
	struct ichdev *ichdev;
	unsigned long port;
	unsigned long pos, t;
	struct timeval start_time, stop_time;
	unsigned long pos, pos1, t;
	int civ, timeout = 1000, attempt = 1;
	struct timespec start_time, stop_time;

	if (chip->ac97_bus->clock != 48000)
		return; /* specified in module option */

      __again:
	subs = chip->pcm[0]->streams[0].substream;
	if (! subs || subs->dma_buffer.bytes < INTEL8X0_TESTBUF_SIZE) {
		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)
	}
	ichdev = &chip->ichd[ICHD_PCMOUT];
	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 */

	/* set rate */
@@ -2693,16 +2709,31 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
		iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE);
		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);
	msleep(50);
	spin_lock_irq(&chip->reg_lock);
	/* 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 -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << ichdev->pos_shift;
		pos -= pos1 << ichdev->pos_shift;
		pos += ichdev->position;
	}
	chip->in_measurement = 0;
	do_gettimeofday(&stop_time);
	do_posix_clock_monotonic_gettime(&stop_time);
	/* stop */
	if (chip->device_type == DEVICE_ALI) {
		iputdword(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 16));
@@ -2717,19 +2748,37 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
	iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
	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 *= 1000000;
	t += stop_time.tv_usec - start_time.tv_usec;
	printk(KERN_INFO "%s: measured %lu usecs\n", __func__, t);
	t += (stop_time.tv_nsec - start_time.tv_nsec) / 1000;
	printk(KERN_INFO "%s: measured %lu usecs (%lu samples)\n", __func__, t, pos);
	if (t == 0) {
		snd_printk(KERN_ERR "?? calculation error..\n");
		return;
		snd_printk(KERN_ERR "intel8x0: ?? calculation error..\n");
		goto __retry;
	}
	pos = (pos / 4) * 1000;
	pos *= 1000;
	pos = (pos / t) * 1000 + ((pos % t) * 1000) / t;
	if (pos < 40000 || pos >= 60000) 
	if (pos < 40000 || pos >= 60000) {
		/* abnormal value. hw problem? */
		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)
		/* not 48000Hz, tuning the clock.. */
		chip->ac97_bus->clock = (chip->ac97_bus->clock * 48000) / pos;