ALSA: hda - Add codec bus reset and verb-retry at critical errors

Some machines machine cause a severe CORB/RIRB stall in certain
weird conditions, such as PA access at the start up together with
fglrx driver.  This seems unable to be recovered without the controller
reset.

This patch allows the bus controller reset at critical errors so
that the communication gets recovered again.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index b063d0e..44f9a0a 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -661,14 +661,23 @@
 		return -1;
 	}
 
-	snd_printk(KERN_ERR SFX "azx_get_response timeout (ERROR): "
-		   "last cmd=0x%08x\n", chip->last_cmd);
-	/* re-initialize CORB/RIRB */
-	spin_lock_irq(&chip->reg_lock);
+	/* a fatal communication error; need either to reset or to fallback
+	 * to the single_cmd mode
+	 */
 	bus->rirb_error = 1;
+	if (!bus->response_reset && !bus->in_reset) {
+		bus->response_reset = 1;
+		return -1; /* give a chance to retry */
+	}
+
+	snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
+		   "switching to single_cmd mode: last cmd=0x%08x\n",
+		   chip->last_cmd);
+	chip->single_cmd = 1;
+	bus->response_reset = 0;
+	/* re-initialize CORB/RIRB */
 	azx_free_cmd_io(chip);
 	azx_init_cmd_io(chip);
-	spin_unlock_irq(&chip->reg_lock);
 	return -1;
 }
 
@@ -709,6 +718,7 @@
 	struct azx *chip = bus->private_data;
 	int timeout = 50;
 
+	bus->rirb_error = 0;
 	while (timeout--) {
 		/* check ICB busy bit */
 		if (!((azx_readw(chip, IRS) & ICH6_IRS_BUSY))) {
@@ -1247,6 +1257,23 @@
 				 struct hda_pcm *cpcm);
 static void azx_stop_chip(struct azx *chip);
 
+static void azx_bus_reset(struct hda_bus *bus)
+{
+	struct azx *chip = bus->private_data;
+	int i;
+
+	bus->in_reset = 1;
+	azx_stop_chip(chip);
+	azx_init_chip(chip);
+	if (chip->initialized) {
+		for (i = 0; i < AZX_MAX_PCMS; i++)
+			snd_pcm_suspend_all(chip->pcm[i]);
+		snd_hda_suspend(chip->bus);
+		snd_hda_resume(chip->bus);
+	}
+	bus->in_reset = 0;
+}
+
 /*
  * Codec initialization
  */
@@ -1270,6 +1297,7 @@
 	bus_temp.ops.command = azx_send_cmd;
 	bus_temp.ops.get_response = azx_get_response;
 	bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
+	bus_temp.ops.bus_reset = azx_bus_reset;
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	bus_temp.power_save = &power_save;
 	bus_temp.ops.pm_notify = azx_power_notify;
@@ -1997,7 +2025,7 @@
 	for (i = 0; i < AZX_MAX_PCMS; i++)
 		snd_pcm_suspend_all(chip->pcm[i]);
 	if (chip->initialized)
-		snd_hda_suspend(chip->bus, state);
+		snd_hda_suspend(chip->bus);
 	azx_stop_chip(chip);
 	if (chip->irq >= 0) {
 		free_irq(chip->irq, chip);