1 From: Takashi Iwai <tiwai@suse.de>
2 Subject: ALSA: Add codec bus reset and verb-retry at critical errors
6 The Volna machine causes a severe CORB/RIRB stall when PA is started
7 together with fglrx. This cannot be recovered without the controller
10 This patch allows the bus controller reset at critical errors so
11 that the communication gets recovered again.
13 Signed-off-by: Takashi Iwai <tiwai@suse.de>
16 sound/pci/hda/hda_codec.c | 13 +++++++++++--
17 sound/pci/hda/hda_codec.h | 7 ++++++-
18 sound/pci/hda/hda_intel.c | 37 +++++++++++++++++++++++++++++++++----
19 3 files changed, 50 insertions(+), 7 deletions(-)
21 --- a/sound/pci/hda/hda_codec.h
22 +++ b/sound/pci/hda/hda_codec.h
24 unsigned int (*get_response)(struct hda_bus *bus);
25 /* free the private data */
26 void (*private_free)(struct hda_bus *);
27 + /* reset bus for retry verb */
28 + void (*bus_reset)(struct hda_bus *bus);
29 #ifdef CONFIG_SND_HDA_POWER_SAVE
30 /* notify power-up/down from codec to controller */
31 void (*pm_notify)(struct hda_bus *bus);
35 unsigned int needs_damn_long_delay :1;
36 + unsigned int rirb_error:1; /* error in codec communication */
37 + unsigned int response_reset:1; /* controller was reset */
38 + unsigned int in_reset:1; /* during reset operation */
46 -int snd_hda_suspend(struct hda_bus *bus, pm_message_t state);
47 +int snd_hda_suspend(struct hda_bus *bus);
48 int snd_hda_resume(struct hda_bus *bus);
51 --- a/sound/pci/hda/hda_codec.c
52 +++ b/sound/pci/hda/hda_codec.c
58 snd_hda_power_up(codec);
59 mutex_lock(&bus->cmd_mutex);
60 err = bus->ops.command(bus, cmd);
62 *res = bus->ops.get_response(bus);
63 mutex_unlock(&bus->cmd_mutex);
64 snd_hda_power_down(codec);
65 + if (res && *res == -1 && bus->rirb_error) {
66 + if (bus->response_reset) {
67 + snd_printd("hda_codec: resetting BUS due to "
68 + "fatal communication error\n");
69 + bus->ops.bus_reset(bus);
73 + bus->response_reset = 0;
77 @@ -3279,11 +3289,10 @@
79 * snd_hda_suspend - suspend the codecs
81 - * @state: suspsend state
83 * Returns 0 if successful.
85 -int snd_hda_suspend(struct hda_bus *bus, pm_message_t state)
86 +int snd_hda_suspend(struct hda_bus *bus)
88 struct hda_codec *codec;
90 --- a/sound/pci/hda/hda_intel.c
91 +++ b/sound/pci/hda/hda_intel.c
94 if (!chip->rirb.cmds) {
96 + bus->rirb_error = 0;
97 return chip->rirb.res; /* the last value */
99 if (time_after(jiffies, timeout))
100 @@ -640,14 +641,23 @@
104 + /* a fatal communication error; need either to reset or to fallback
105 + * to the single_cmd mode
107 + bus->rirb_error = 1;
108 + if (!bus->response_reset && !bus->in_reset) {
109 + bus->response_reset = 1;
110 + return -1; /* give a chance to retry */
113 snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
114 "switching to single_cmd mode: last cmd=0x%08x\n",
116 - chip->rirb.rp = azx_readb(chip, RIRBWP);
117 - chip->rirb.cmds = 0;
118 - /* switch to single_cmd mode */
119 chip->single_cmd = 1;
120 + bus->response_reset = 0;
121 + /* re-initialize CORB/RIRB */
122 azx_free_cmd_io(chip);
123 + azx_init_cmd_io(chip);
128 struct azx *chip = bus->private_data;
131 + bus->rirb_error = 0;
133 /* check ICB busy bit */
134 if (!((azx_readw(chip, IRS) & ICH6_IRS_BUSY))) {
135 @@ -1227,6 +1238,23 @@
137 static void azx_stop_chip(struct azx *chip);
139 +static void azx_bus_reset(struct hda_bus *bus)
141 + struct azx *chip = bus->private_data;
145 + azx_stop_chip(chip);
146 + azx_init_chip(chip);
147 + if (chip->initialized) {
148 + for (i = 0; i < AZX_MAX_PCMS; i++)
149 + snd_pcm_suspend_all(chip->pcm[i]);
150 + snd_hda_suspend(chip->bus);
151 + snd_hda_resume(chip->bus);
157 * Codec initialization
159 @@ -1248,6 +1276,7 @@
160 bus_temp.pci = chip->pci;
161 bus_temp.ops.command = azx_send_cmd;
162 bus_temp.ops.get_response = azx_get_response;
163 + bus_temp.ops.bus_reset = azx_bus_reset;
164 #ifdef CONFIG_SND_HDA_POWER_SAVE
165 bus_temp.ops.pm_notify = azx_power_notify;
167 @@ -2013,7 +2042,7 @@
168 for (i = 0; i < AZX_MAX_PCMS; i++)
169 snd_pcm_suspend_all(chip->pcm[i]);
170 if (chip->initialized)
171 - snd_hda_suspend(chip->bus, state);
172 + snd_hda_suspend(chip->bus);
174 if (chip->irq >= 0) {
175 free_irq(chip->irq, chip);