ALSA: hda - Fix CORB reset to follow specification
authorDavid Henningsson <david.henningsson@canonical.com>
Fri, 28 Feb 2014 06:56:58 +0000 (07:56 +0100)
committerTakashi Iwai <tiwai@suse.de>
Fri, 28 Feb 2014 13:03:30 +0000 (14:03 +0100)
According to the HDA spec, we must write 1 to bit 15 on a CORBRP
reset, read back 1, then write 0, then read back 0. This must be
done while the DMA is not running.

We accidentaly ended up writing back the 0 by using a writel
instead of a writew to CORBWP.

This caused occasional controller failure on Bay Trail hardware.

[replaced error messages with dev_err() by tiwai]

Signed-off-by: David Henningsson <david.henningsson@canonical.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/hda/hda_intel.c

index 6eb09418d08ef1c86bdc9bc503b6fafb6798bc8c..e8a9e87ac07477e90557e5b94416a880f7ddf831 100644 (file)
@@ -769,6 +769,8 @@ static int azx_alloc_cmd_io(struct azx *chip)
 
 static void azx_init_cmd_io(struct azx *chip)
 {
+       int timeout;
+
        spin_lock_irq(&chip->reg_lock);
        /* CORB set up */
        chip->corb.addr = chip->rb.addr;
@@ -780,8 +782,28 @@ static void azx_init_cmd_io(struct azx *chip)
        azx_writeb(chip, CORBSIZE, 0x02);
        /* set the corb write pointer to 0 */
        azx_writew(chip, CORBWP, 0);
+
        /* reset the corb hw read pointer */
        azx_writew(chip, CORBRP, ICH6_CORBRP_RST);
+       for (timeout = 1000; timeout > 0; timeout--) {
+               if ((azx_readw(chip, CORBRP) & ICH6_CORBRP_RST) == ICH6_CORBRP_RST)
+                       break;
+               udelay(1);
+       }
+       if (timeout <= 0)
+               dev_err(chip->card->dev, "CORB reset timeout#1, CORBRP = %d\n",
+                       azx_readw(chip, CORBRP));
+
+       azx_writew(chip, CORBRP, 0);
+       for (timeout = 1000; timeout > 0; timeout--) {
+               if (azx_readw(chip, CORBRP) == 0)
+                       break;
+               udelay(1);
+       }
+       if (timeout <= 0)
+               dev_err(chip->card->dev, "CORB reset timeout#2, CORBRP = %d\n",
+                       azx_readw(chip, CORBRP));
+
        /* enable corb dma */
        azx_writeb(chip, CORBCTL, ICH6_CORBCTL_RUN);
 
@@ -856,7 +878,7 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
 
        chip->rirb.cmds[addr]++;
        chip->corb.buf[wp] = cpu_to_le32(val);
-       azx_writel(chip, CORBWP, wp);
+       azx_writew(chip, CORBWP, wp);
 
        spin_unlock_irq(&chip->reg_lock);