ALSA: hda: track CIRB/CORB command/response states for each codec
Wu Fengguang [Sat, 1 Aug 2009 10:45:16 +0000 (18:45 +0800)]
Recently we hit a bug in our dev board, whose HDMI codec#3 may emit
redundant/spurious responses, which were then taken as responses to
command for another onboard Realtek codec#2, and mess up both codecs.

Extend the azx_rb.cmds and azx_rb.res to array and track each codec's
commands/responses separately. This helps keep good codec safe from
broken ones.

Signed-off-by: Wu Fengguang <fengguang.wu@intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_intel.c

index 88480c0..c7df01b 100644 (file)
@@ -174,7 +174,7 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
        mutex_lock(&bus->cmd_mutex);
        err = bus->ops.command(bus, cmd);
        if (!err && res)
-               *res = bus->ops.get_response(bus);
+               *res = bus->ops.get_response(bus, codec->addr);
        mutex_unlock(&bus->cmd_mutex);
        snd_hda_power_down(codec);
        if (res && *res == -1 && bus->rirb_error) {
index cad79ef..1b75f28 100644 (file)
@@ -568,7 +568,7 @@ struct hda_bus_ops {
        /* send a single command */
        int (*command)(struct hda_bus *bus, unsigned int cmd);
        /* get a response from the last command */
-       unsigned int (*get_response)(struct hda_bus *bus);
+       unsigned int (*get_response)(struct hda_bus *bus, unsigned int addr);
        /* free the private data */
        void (*private_free)(struct hda_bus *);
        /* attach a PCM stream */
index 77c1b84..19e67a1 100644 (file)
@@ -253,7 +253,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
 
 /* STATESTS int mask: S3,SD2,SD1,SD0 */
 #define AZX_MAX_CODECS         4
-#define STATESTS_INT_MASK      0x0f
+#define STATESTS_INT_MASK      ((1 << AZX_MAX_CODECS) - 1)
 
 /* SD_CTL bits */
 #define SD_CTL_STREAM_RESET    0x01    /* stream reset bit */
@@ -361,8 +361,8 @@ struct azx_rb {
        dma_addr_t addr;        /* physical address of CORB/RIRB buffer */
        /* for RIRB */
        unsigned short rp, wp;  /* read/write pointers */
-       int cmds;               /* number of pending requests */
-       u32 res;                /* last read value */
+       int cmds[AZX_MAX_CODECS];       /* number of pending requests */
+       u32 res[AZX_MAX_CODECS];        /* last read value */
 };
 
 struct azx {
@@ -531,7 +531,8 @@ static void azx_init_cmd_io(struct azx *chip)
        /* RIRB set up */
        chip->rirb.addr = chip->rb.addr + 2048;
        chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
-       chip->rirb.wp = chip->rirb.rp = chip->rirb.cmds = 0;
+       chip->rirb.wp = chip->rirb.rp = 0;
+       memset(chip->rirb.cmds, 0, sizeof(chip->rirb.cmds));
        azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr);
        azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr));
 
@@ -552,10 +553,35 @@ static void azx_free_cmd_io(struct azx *chip)
        azx_writeb(chip, CORBCTL, 0);
 }
 
+static unsigned int azx_command_addr(u32 cmd)
+{
+       unsigned int addr = cmd >> 28;
+
+       if (addr >= AZX_MAX_CODECS) {
+               snd_BUG();
+               addr = 0;
+       }
+
+       return addr;
+}
+
+static unsigned int azx_response_addr(u32 res)
+{
+       unsigned int addr = res & 0xf;
+
+       if (addr >= AZX_MAX_CODECS) {
+               snd_BUG();
+               addr = 0;
+       }
+
+       return addr;
+}
+
 /* send a command */
 static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
 {
        struct azx *chip = bus->private_data;
+       unsigned int addr = azx_command_addr(val);
        unsigned int wp;
 
        /* add command to corb */
@@ -564,7 +590,7 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
        wp %= ICH6_MAX_CORB_ENTRIES;
 
        spin_lock_irq(&chip->reg_lock);
-       chip->rirb.cmds++;
+       chip->rirb.cmds[addr]++;
        chip->corb.buf[wp] = cpu_to_le32(val);
        azx_writel(chip, CORBWP, wp);
        spin_unlock_irq(&chip->reg_lock);
@@ -578,13 +604,14 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
 static void azx_update_rirb(struct azx *chip)
 {
        unsigned int rp, wp;
+       unsigned int addr;
        u32 res, res_ex;
 
        wp = azx_readb(chip, RIRBWP);
        if (wp == chip->rirb.wp)
                return;
        chip->rirb.wp = wp;
-               
+
        while (chip->rirb.rp != wp) {
                chip->rirb.rp++;
                chip->rirb.rp %= ICH6_MAX_RIRB_ENTRIES;
@@ -592,18 +619,20 @@ static void azx_update_rirb(struct azx *chip)
                rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */
                res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]);
                res = le32_to_cpu(chip->rirb.buf[rp]);
+               addr = azx_response_addr(res_ex);
                if (res_ex & ICH6_RIRB_EX_UNSOL_EV)
                        snd_hda_queue_unsol_event(chip->bus, res, res_ex);
-               else if (chip->rirb.cmds) {
-                       chip->rirb.res = res;
+               else if (chip->rirb.cmds[addr]) {
+                       chip->rirb.res[addr] = res;
                        smp_wmb();
-                       chip->rirb.cmds--;
+                       chip->rirb.cmds[addr]--;
                }
        }
 }
 
 /* receive a response */
-static unsigned int azx_rirb_get_response(struct hda_bus *bus)
+static unsigned int azx_rirb_get_response(struct hda_bus *bus,
+                                         unsigned int addr)
 {
        struct azx *chip = bus->private_data;
        unsigned long timeout;
@@ -616,10 +645,10 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
                        azx_update_rirb(chip);
                        spin_unlock_irq(&chip->reg_lock);
                }
-               if (!chip->rirb.cmds) {
+               if (!chip->rirb.cmds[addr]) {
                        smp_rmb();
                        bus->rirb_error = 0;
-                       return chip->rirb.res; /* the last value */
+                       return chip->rirb.res[addr]; /* the last value */
                }
                if (time_after(jiffies, timeout))
                        break;
@@ -692,7 +721,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
  */
 
 /* receive a response */
-static int azx_single_wait_for_response(struct azx *chip)
+static int azx_single_wait_for_response(struct azx *chip, unsigned int addr)
 {
        int timeout = 50;
 
@@ -700,7 +729,7 @@ static int azx_single_wait_for_response(struct azx *chip)
                /* check IRV busy bit */
                if (azx_readw(chip, IRS) & ICH6_IRS_VALID) {
                        /* reuse rirb.res as the response return value */
-                       chip->rirb.res = azx_readl(chip, IR);
+                       chip->rirb.res[addr] = azx_readl(chip, IR);
                        return 0;
                }
                udelay(1);
@@ -708,7 +737,7 @@ static int azx_single_wait_for_response(struct azx *chip)
        if (printk_ratelimit())
                snd_printd(SFX "get_response timeout: IRS=0x%x\n",
                           azx_readw(chip, IRS));
-       chip->rirb.res = -1;
+       chip->rirb.res[addr] = -1;
        return -EIO;
 }
 
@@ -716,6 +745,7 @@ static int azx_single_wait_for_response(struct azx *chip)
 static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
 {
        struct azx *chip = bus->private_data;
+       unsigned int addr = azx_command_addr(val);
        int timeout = 50;
 
        bus->rirb_error = 0;
@@ -728,7 +758,7 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
                        azx_writel(chip, IC, val);
                        azx_writew(chip, IRS, azx_readw(chip, IRS) |
                                   ICH6_IRS_BUSY);
-                       return azx_single_wait_for_response(chip);
+                       return azx_single_wait_for_response(chip, addr);
                }
                udelay(1);
        }
@@ -739,10 +769,11 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
 }
 
 /* receive a response */
-static unsigned int azx_single_get_response(struct hda_bus *bus)
+static unsigned int azx_single_get_response(struct hda_bus *bus,
+                                           unsigned int addr)
 {
        struct azx *chip = bus->private_data;
-       return chip->rirb.res;
+       return chip->rirb.res[addr];
 }
 
 /*
@@ -765,13 +796,14 @@ static int azx_send_cmd(struct hda_bus *bus, unsigned int val)
 }
 
 /* get a response */
-static unsigned int azx_get_response(struct hda_bus *bus)
+static unsigned int azx_get_response(struct hda_bus *bus,
+                                    unsigned int addr)
 {
        struct azx *chip = bus->private_data;
        if (chip->single_cmd)
-               return azx_single_get_response(bus);
+               return azx_single_get_response(bus, addr);
        else
-               return azx_rirb_get_response(bus);
+               return azx_rirb_get_response(bus, addr);
 }
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -1245,7 +1277,7 @@ static int probe_codec(struct azx *chip, int addr)
 
        chip->probing = 1;
        azx_send_cmd(chip->bus, cmd);
-       res = azx_get_response(chip->bus);
+       res = azx_get_response(chip->bus, addr);
        chip->probing = 0;
        if (res == -1)
                return -EIO;