ALSA: hda - Check CORB overflow
authorTakashi Iwai <tiwai@suse.de>
Thu, 20 Dec 2012 10:17:17 +0000 (11:17 +0100)
committerTakashi Iwai <tiwai@suse.de>
Sat, 12 Jan 2013 07:33:56 +0000 (08:33 +0100)
Add an overflow check of CORB in HD-audio controller and codec drivers
so that flood of sequential writes would work properly.
In the controller side, add a check of CORB read-pointer to make
returning -EAGAIN when it's full.  Meanwhile in the codec side, when
-EAGAIN error is received, it retries the write after flushing the
pending verbs (calling get_response() essentially does it).

Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_intel.c

index 3207e5c571416b6ac3f7344677aff93d504ba74a..afc3ccd998f84cf7bd286829b804c0eed1f8d9aa 100644 (file)
@@ -222,8 +222,14 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
  again:
        snd_hda_power_up(codec);
        mutex_lock(&bus->cmd_mutex);
-       trace_hda_send_cmd(codec, cmd);
-       err = bus->ops.command(bus, cmd);
+       for (;;) {
+               trace_hda_send_cmd(codec, cmd);
+               err = bus->ops.command(bus, cmd);
+               if (err != -EAGAIN)
+                       break;
+               /* process pending verbs */
+               bus->ops.get_response(bus, codec->addr);
+       }
        if (!err && res) {
                *res = bus->ops.get_response(bus, codec->addr);
                trace_hda_get_response(codec, *res);
index 0b6aebacc56b6bb2df187dbaf3604e2c28aef784..0430436b003d0924ac93e0b824a58e494e8279ab 100644 (file)
@@ -797,7 +797,7 @@ 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;
+       unsigned int wp, rp;
 
        spin_lock_irq(&chip->reg_lock);
 
@@ -806,11 +806,18 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
        if (wp == 0xffff) {
                /* something wrong, controller likely turned to D3 */
                spin_unlock_irq(&chip->reg_lock);
-               return -1;
+               return -EIO;
        }
        wp++;
        wp %= ICH6_MAX_CORB_ENTRIES;
 
+       rp = azx_readw(chip, CORBRP);
+       if (wp == rp) {
+               /* oops, it's full */
+               spin_unlock_irq(&chip->reg_lock);
+               return -EAGAIN;
+       }
+
        chip->rirb.cmds[addr]++;
        chip->corb.buf[wp] = cpu_to_le32(val);
        azx_writel(chip, CORBWP, wp);