From 3bcce5c0d931bf623adc5974200e4d7636b10bef Mon Sep 17 00:00:00 2001
From: Takashi Iwai <tiwai@suse.de>
Date: Thu, 20 Dec 2012 11:17:17 +0100
Subject: [PATCH] ALSA: hda - Check CORB overflow
Git-commit: 3bcce5c0d931bf623adc5974200e4d7636b10bef
Patch-mainline: 3.9-rc1
References: bnc#823597

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 |   10 ++++++++--
 sound/pci/hda/hda_intel.c |   11 +++++++++--
 2 files changed, 17 insertions(+), 4 deletions(-)

--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -219,8 +219,14 @@ static int codec_exec_verb(struct hda_co
  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);
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -792,7 +792,7 @@ static int azx_corb_send_cmd(struct hda_
 {
 	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);
 
@@ -801,11 +801,18 @@ static int azx_corb_send_cmd(struct hda_
 	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);
