]> git.ipfire.org Git - ipfire-2.x.git/blobdiff - src/patches/suse-2.6.27.31/patches.drivers/alsa-post-ga-hda-hwptr-stabilize
Move xen patchset to new version's subdir.
[ipfire-2.x.git] / src / patches / suse-2.6.27.31 / patches.drivers / alsa-post-ga-hda-hwptr-stabilize
diff --git a/src/patches/suse-2.6.27.31/patches.drivers/alsa-post-ga-hda-hwptr-stabilize b/src/patches/suse-2.6.27.31/patches.drivers/alsa-post-ga-hda-hwptr-stabilize
new file mode 100644 (file)
index 0000000..c00e20f
--- /dev/null
@@ -0,0 +1,165 @@
+From: Jaroslav Kysela <perex@perex.cz>
+Subject: [ALSA] hda_intel: fix unexpected ring buffer positions
+Patch-mainline: 2.6.30-rc3
+References: bnc#502733
+
+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>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+
+---
+ sound/pci/hda/hda_intel.c |   39 +++++++++++++++++++++++++--------------
+ 1 file changed, 25 insertions(+), 14 deletions(-)
+
+--- a/sound/pci/hda/hda_intel.c
++++ b/sound/pci/hda/hda_intel.c
+@@ -304,6 +304,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 */
+@@ -322,7 +325,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
+@@ -964,7 +966,7 @@ static irqreturn_t azx_interrupt(int irq
+       struct azx *chip = dev_id;
+       struct azx_dev *azx_dev;
+       u32 status;
+-      int i;
++      int i, ok;
+       spin_lock(&chip->reg_lock);
+@@ -980,18 +982,14 @@ static irqreturn_t azx_interrupt(int irq
+                       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,
+@@ -1080,7 +1078,6 @@ static int azx_setup_periods(struct azx
+       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;
+@@ -1101,7 +1098,6 @@ static int azx_setup_periods(struct azx
+                                        &bdl, ofs, pos_adj, 1);
+                       if (ofs < 0)
+                               goto error;
+-                      azx_dev->irq_ignore = 1;
+               }
+       } else
+               pos_adj = 0;
+@@ -1147,6 +1143,9 @@ static void azx_stream_reset(struct azx
+       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;
+ }
+ /*
+@@ -1396,7 +1395,6 @@ static int azx_pcm_open(struct snd_pcm_s
+       snd_pcm_set_sync(substream);
+       mutex_unlock(&chip->open_mutex);
+-      azx_stream_reset(chip, azx_dev);
+       return 0;
+ }
+@@ -1461,6 +1459,7 @@ static int azx_pcm_prepare(struct snd_pc
+       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,
+@@ -1489,6 +1488,8 @@ static int azx_pcm_prepare(struct snd_pc
+                       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;
+@@ -1505,13 +1506,14 @@ static int azx_pcm_trigger(struct snd_pc
+       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:
+@@ -1541,6 +1543,10 @@ static int azx_pcm_trigger(struct snd_pc
+               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
+@@ -1690,6 +1696,11 @@ static int azx_position_ok(struct azx *c
+ {
+       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) {