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

Commit fa00e046 authored by Jaroslav Kysela's avatar Jaroslav Kysela
Browse files

[ALSA] hda_intel: fix unexpected ring buffer positions



I found two issues with ICH7-M (it should be related to other HDA chipsets
as well):

- the ring buffer position is not reset when stream restarts (after xrun) -
  solved by moving azx_stream_reset() call from open() to prepare() callback
  and reset posbuf to zero (it might be filled with hw later than position()
  callback is called)
- irq_ignore flag should be set also when ring buffer memory area is not
  changed in prepare() callback - this patch replaces irq_ignore with
  more universal check based on jiffies clock

Signed-off-by: default avatarJaroslav Kysela <perex@perex.cz>
parent 577c9c45
Loading
Loading
Loading
Loading
+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) {