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