--- /dev/null
+From: Takashi Iwai <tiwai@suse.de>
+Subject: ALSA: Add codec bus reset and verb-retry at critical errors
+Patch-mainline:
+References: bnc#502903
+
+The Volna machine causes a severe CORB/RIRB stall when PA is started
+together with fglrx. This cannot 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>
+
+---
+ sound/pci/hda/hda_codec.c | 13 +++++++++++--
+ sound/pci/hda/hda_codec.h | 7 ++++++-
+ sound/pci/hda/hda_intel.c | 37 +++++++++++++++++++++++++++++++++----
+ 3 files changed, 50 insertions(+), 7 deletions(-)
+
+--- a/sound/pci/hda/hda_codec.h
++++ b/sound/pci/hda/hda_codec.h
+@@ -541,6 +541,8 @@
+ unsigned int (*get_response)(struct hda_bus *bus);
+ /* free the private data */
+ void (*private_free)(struct hda_bus *);
++ /* reset bus for retry verb */
++ void (*bus_reset)(struct hda_bus *bus);
+ #ifdef CONFIG_SND_HDA_POWER_SAVE
+ /* notify power-up/down from codec to controller */
+ void (*pm_notify)(struct hda_bus *bus);
+@@ -586,6 +588,9 @@
+
+ /* misc op flags */
+ unsigned int needs_damn_long_delay :1;
++ unsigned int rirb_error:1; /* error in codec communication */
++ unsigned int response_reset:1; /* controller was reset */
++ unsigned int in_reset:1; /* during reset operation */
+ };
+
+ /*
+@@ -827,7 +832,7 @@
+ * power management
+ */
+ #ifdef CONFIG_PM
+-int snd_hda_suspend(struct hda_bus *bus, pm_message_t state);
++int snd_hda_suspend(struct hda_bus *bus);
+ int snd_hda_resume(struct hda_bus *bus);
+ #endif
+
+--- a/sound/pci/hda/hda_codec.c
++++ b/sound/pci/hda/hda_codec.c
+@@ -187,6 +187,7 @@
+
+ if (res)
+ *res = -1;
++ again:
+ snd_hda_power_up(codec);
+ mutex_lock(&bus->cmd_mutex);
+ err = bus->ops.command(bus, cmd);
+@@ -194,6 +195,15 @@
+ *res = bus->ops.get_response(bus);
+ mutex_unlock(&bus->cmd_mutex);
+ snd_hda_power_down(codec);
++ if (res && *res == -1 && bus->rirb_error) {
++ if (bus->response_reset) {
++ snd_printd("hda_codec: resetting BUS due to "
++ "fatal communication error\n");
++ bus->ops.bus_reset(bus);
++ }
++ goto again;
++ }
++ bus->response_reset = 0;
+ return err;
+ }
+
+@@ -3279,11 +3289,10 @@
+ /**
+ * snd_hda_suspend - suspend the codecs
+ * @bus: the HDA bus
+- * @state: suspsend state
+ *
+ * Returns 0 if successful.
+ */
+-int snd_hda_suspend(struct hda_bus *bus, pm_message_t state)
++int snd_hda_suspend(struct hda_bus *bus)
+ {
+ struct hda_codec *codec;
+
+--- a/sound/pci/hda/hda_intel.c
++++ b/sound/pci/hda/hda_intel.c
+@@ -600,6 +600,7 @@
+ }
+ if (!chip->rirb.cmds) {
+ smp_rmb();
++ bus->rirb_error = 0;
+ return chip->rirb.res; /* the last value */
+ }
+ if (time_after(jiffies, timeout))
+@@ -640,14 +641,23 @@
+ return -1;
+ }
+
++ /* 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->rirb.rp = azx_readb(chip, RIRBWP);
+- chip->rirb.cmds = 0;
+- /* switch to single_cmd mode */
+ chip->single_cmd = 1;
++ bus->response_reset = 0;
++ /* re-initialize CORB/RIRB */
+ azx_free_cmd_io(chip);
++ azx_init_cmd_io(chip);
+ return -1;
+ }
+
+@@ -688,6 +698,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))) {
+@@ -1227,6 +1238,23 @@
+
+ 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
+ */
+@@ -1248,6 +1276,7 @@
+ bus_temp.pci = chip->pci;
+ bus_temp.ops.command = azx_send_cmd;
+ bus_temp.ops.get_response = azx_get_response;
++ bus_temp.ops.bus_reset = azx_bus_reset;
+ #ifdef CONFIG_SND_HDA_POWER_SAVE
+ bus_temp.ops.pm_notify = azx_power_notify;
+ #endif
+@@ -2013,7 +2042,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);