]>
Commit | Line | Data |
---|---|---|
00e5a55c BS |
1 | From: Takashi Iwai <tiwai@suse.de> |
2 | Subject: ALSA: Add codec bus reset and verb-retry at critical errors | |
3 | Patch-mainline: | |
4 | References: bnc#502903 | |
5 | ||
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 | |
8 | reset. | |
9 | ||
10 | This patch allows the bus controller reset at critical errors so | |
11 | that the communication gets recovered again. | |
12 | ||
13 | Signed-off-by: Takashi Iwai <tiwai@suse.de> | |
14 | ||
15 | --- | |
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(-) | |
20 | ||
21 | --- a/sound/pci/hda/hda_codec.h | |
22 | +++ b/sound/pci/hda/hda_codec.h | |
23 | @@ -541,6 +541,8 @@ | |
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); | |
32 | @@ -586,6 +588,9 @@ | |
33 | ||
34 | /* misc op flags */ | |
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 */ | |
39 | }; | |
40 | ||
41 | /* | |
42 | @@ -827,7 +832,7 @@ | |
43 | * power management | |
44 | */ | |
45 | #ifdef CONFIG_PM | |
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); | |
49 | #endif | |
50 | ||
51 | --- a/sound/pci/hda/hda_codec.c | |
52 | +++ b/sound/pci/hda/hda_codec.c | |
53 | @@ -187,6 +187,7 @@ | |
54 | ||
55 | if (res) | |
56 | *res = -1; | |
57 | + again: | |
58 | snd_hda_power_up(codec); | |
59 | mutex_lock(&bus->cmd_mutex); | |
60 | err = bus->ops.command(bus, cmd); | |
61 | @@ -194,6 +195,15 @@ | |
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); | |
70 | + } | |
71 | + goto again; | |
72 | + } | |
73 | + bus->response_reset = 0; | |
74 | return err; | |
75 | } | |
76 | ||
77 | @@ -3279,11 +3289,10 @@ | |
78 | /** | |
79 | * snd_hda_suspend - suspend the codecs | |
80 | * @bus: the HDA bus | |
81 | - * @state: suspsend state | |
82 | * | |
83 | * Returns 0 if successful. | |
84 | */ | |
85 | -int snd_hda_suspend(struct hda_bus *bus, pm_message_t state) | |
86 | +int snd_hda_suspend(struct hda_bus *bus) | |
87 | { | |
88 | struct hda_codec *codec; | |
89 | ||
90 | --- a/sound/pci/hda/hda_intel.c | |
91 | +++ b/sound/pci/hda/hda_intel.c | |
92 | @@ -600,6 +600,7 @@ | |
93 | } | |
94 | if (!chip->rirb.cmds) { | |
95 | smp_rmb(); | |
96 | + bus->rirb_error = 0; | |
97 | return chip->rirb.res; /* the last value */ | |
98 | } | |
99 | if (time_after(jiffies, timeout)) | |
100 | @@ -640,14 +641,23 @@ | |
101 | return -1; | |
102 | } | |
103 | ||
104 | + /* a fatal communication error; need either to reset or to fallback | |
105 | + * to the single_cmd mode | |
106 | + */ | |
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 */ | |
111 | + } | |
112 | + | |
113 | snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, " | |
114 | "switching to single_cmd mode: last cmd=0x%08x\n", | |
115 | chip->last_cmd); | |
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); | |
124 | return -1; | |
125 | } | |
126 | ||
127 | @@ -688,6 +698,7 @@ | |
128 | struct azx *chip = bus->private_data; | |
129 | int timeout = 50; | |
130 | ||
131 | + bus->rirb_error = 0; | |
132 | while (timeout--) { | |
133 | /* check ICB busy bit */ | |
134 | if (!((azx_readw(chip, IRS) & ICH6_IRS_BUSY))) { | |
135 | @@ -1227,6 +1238,23 @@ | |
136 | ||
137 | static void azx_stop_chip(struct azx *chip); | |
138 | ||
139 | +static void azx_bus_reset(struct hda_bus *bus) | |
140 | +{ | |
141 | + struct azx *chip = bus->private_data; | |
142 | + int i; | |
143 | + | |
144 | + bus->in_reset = 1; | |
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); | |
152 | + } | |
153 | + bus->in_reset = 0; | |
154 | +} | |
155 | + | |
156 | /* | |
157 | * Codec initialization | |
158 | */ | |
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; | |
166 | #endif | |
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); | |
173 | azx_stop_chip(chip); | |
174 | if (chip->irq >= 0) { | |
175 | free_irq(chip->irq, chip); |