[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: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
parent
577c9c456f
commit
fa00e046b4
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue