Commit fa00e046 authored by Jaroslav Kysela's avatar Jaroslav Kysela

[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
......@@ -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) {
......
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