diff options
author | Takashi Iwai <tiwai@suse.de> | 2009-06-02 06:15:48 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-06-02 06:15:48 -0400 |
commit | 3c4dbda003b5941b351bec8d1954e8eaa6e7bc09 (patch) | |
tree | e082b5f735b3c43ad9e19e39e3ce8119f1a7d69c | |
parent | 3b315d70b094e8b439358756a9084438fd7a71c2 (diff) | |
parent | b20f3b834673be9ead83a3c6f07fa3881d1a990f (diff) |
Merge branch 'topic/hda-ctl-reset' into topic/hda
-rw-r--r-- | sound/pci/hda/hda_codec.c | 44 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 9 | ||||
-rw-r--r-- | sound/pci/hda/hda_intel.c | 40 | ||||
-rw-r--r-- | sound/pci/hda/patch_sigmatel.c | 9 |
4 files changed, 69 insertions, 33 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index d1d5fb9d7afb..562403a23488 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c | |||
@@ -165,28 +165,29 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, | |||
165 | unsigned int *res) | 165 | unsigned int *res) |
166 | { | 166 | { |
167 | struct hda_bus *bus = codec->bus; | 167 | struct hda_bus *bus = codec->bus; |
168 | int err, repeated = 0; | 168 | int err; |
169 | 169 | ||
170 | if (res) | 170 | if (res) |
171 | *res = -1; | 171 | *res = -1; |
172 | again: | ||
172 | snd_hda_power_up(codec); | 173 | snd_hda_power_up(codec); |
173 | mutex_lock(&bus->cmd_mutex); | 174 | mutex_lock(&bus->cmd_mutex); |
174 | again: | ||
175 | err = bus->ops.command(bus, cmd); | 175 | err = bus->ops.command(bus, cmd); |
176 | if (!err) { | 176 | if (!err && res) |
177 | if (res) { | 177 | *res = bus->ops.get_response(bus); |
178 | *res = bus->ops.get_response(bus); | ||
179 | if (*res == -1 && bus->rirb_error) { | ||
180 | if (repeated++ < 1) { | ||
181 | snd_printd(KERN_WARNING "hda_codec: " | ||
182 | "Trying verb 0x%08x again\n", cmd); | ||
183 | goto again; | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | } | ||
188 | mutex_unlock(&bus->cmd_mutex); | 178 | mutex_unlock(&bus->cmd_mutex); |
189 | snd_hda_power_down(codec); | 179 | snd_hda_power_down(codec); |
180 | if (res && *res == -1 && bus->rirb_error) { | ||
181 | if (bus->response_reset) { | ||
182 | snd_printd("hda_codec: resetting BUS due to " | ||
183 | "fatal communication error\n"); | ||
184 | bus->ops.bus_reset(bus); | ||
185 | } | ||
186 | goto again; | ||
187 | } | ||
188 | /* clear reset-flag when the communication gets recovered */ | ||
189 | if (!err) | ||
190 | bus->response_reset = 0; | ||
190 | return err; | 191 | return err; |
191 | } | 192 | } |
192 | 193 | ||
@@ -213,11 +214,6 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, | |||
213 | } | 214 | } |
214 | EXPORT_SYMBOL_HDA(snd_hda_codec_read); | 215 | EXPORT_SYMBOL_HDA(snd_hda_codec_read); |
215 | 216 | ||
216 | /* Define the below to send and receive verbs synchronously. | ||
217 | * If you often get any codec communication errors, this is worth to try. | ||
218 | */ | ||
219 | /* #define SND_HDA_SUPPORT_SYNC_WRITE */ | ||
220 | |||
221 | /** | 217 | /** |
222 | * snd_hda_codec_write - send a single command without waiting for response | 218 | * snd_hda_codec_write - send a single command without waiting for response |
223 | * @codec: the HDA codec | 219 | * @codec: the HDA codec |
@@ -234,12 +230,9 @@ int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct, | |||
234 | unsigned int verb, unsigned int parm) | 230 | unsigned int verb, unsigned int parm) |
235 | { | 231 | { |
236 | unsigned int cmd = make_codec_cmd(codec, nid, direct, verb, parm); | 232 | unsigned int cmd = make_codec_cmd(codec, nid, direct, verb, parm); |
237 | #ifdef SND_HDA_SUPPORT_SYNC_WRITE | ||
238 | unsigned int res; | 233 | unsigned int res; |
239 | return codec_exec_verb(codec, cmd, &res); | 234 | return codec_exec_verb(codec, cmd, |
240 | #else | 235 | codec->bus->sync_write ? &res : NULL); |
241 | return codec_exec_verb(codec, cmd, NULL); | ||
242 | #endif | ||
243 | } | 236 | } |
244 | EXPORT_SYMBOL_HDA(snd_hda_codec_write); | 237 | EXPORT_SYMBOL_HDA(snd_hda_codec_write); |
245 | 238 | ||
@@ -3894,11 +3887,10 @@ EXPORT_SYMBOL_HDA(auto_pin_cfg_labels); | |||
3894 | /** | 3887 | /** |
3895 | * snd_hda_suspend - suspend the codecs | 3888 | * snd_hda_suspend - suspend the codecs |
3896 | * @bus: the HDA bus | 3889 | * @bus: the HDA bus |
3897 | * @state: suspsend state | ||
3898 | * | 3890 | * |
3899 | * Returns 0 if successful. | 3891 | * Returns 0 if successful. |
3900 | */ | 3892 | */ |
3901 | int snd_hda_suspend(struct hda_bus *bus, pm_message_t state) | 3893 | int snd_hda_suspend(struct hda_bus *bus) |
3902 | { | 3894 | { |
3903 | struct hda_codec *codec; | 3895 | struct hda_codec *codec; |
3904 | 3896 | ||
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index c5bd40f77bb9..cad79efaabc9 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h | |||
@@ -574,6 +574,8 @@ struct hda_bus_ops { | |||
574 | /* attach a PCM stream */ | 574 | /* attach a PCM stream */ |
575 | int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec, | 575 | int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec, |
576 | struct hda_pcm *pcm); | 576 | struct hda_pcm *pcm); |
577 | /* reset bus for retry verb */ | ||
578 | void (*bus_reset)(struct hda_bus *bus); | ||
577 | #ifdef CONFIG_SND_HDA_POWER_SAVE | 579 | #ifdef CONFIG_SND_HDA_POWER_SAVE |
578 | /* notify power-up/down from codec to controller */ | 580 | /* notify power-up/down from codec to controller */ |
579 | void (*pm_notify)(struct hda_bus *bus); | 581 | void (*pm_notify)(struct hda_bus *bus); |
@@ -622,8 +624,13 @@ struct hda_bus { | |||
622 | 624 | ||
623 | /* misc op flags */ | 625 | /* misc op flags */ |
624 | unsigned int needs_damn_long_delay :1; | 626 | unsigned int needs_damn_long_delay :1; |
627 | unsigned int allow_bus_reset:1; /* allow bus reset at fatal error */ | ||
628 | unsigned int sync_write:1; /* sync after verb write */ | ||
629 | /* status for codec/controller */ | ||
625 | unsigned int shutdown :1; /* being unloaded */ | 630 | unsigned int shutdown :1; /* being unloaded */ |
626 | unsigned int rirb_error:1; /* error in codec communication */ | 631 | unsigned int rirb_error:1; /* error in codec communication */ |
632 | unsigned int response_reset:1; /* controller was reset */ | ||
633 | unsigned int in_reset:1; /* during reset operation */ | ||
627 | }; | 634 | }; |
628 | 635 | ||
629 | /* | 636 | /* |
@@ -907,7 +914,7 @@ void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen); | |||
907 | * power management | 914 | * power management |
908 | */ | 915 | */ |
909 | #ifdef CONFIG_PM | 916 | #ifdef CONFIG_PM |
910 | int snd_hda_suspend(struct hda_bus *bus, pm_message_t state); | 917 | int snd_hda_suspend(struct hda_bus *bus); |
911 | int snd_hda_resume(struct hda_bus *bus); | 918 | int snd_hda_resume(struct hda_bus *bus); |
912 | #endif | 919 | #endif |
913 | 920 | ||
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index b063d0e3d325..9f44645a1d04 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c | |||
@@ -661,14 +661,23 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus) | |||
661 | return -1; | 661 | return -1; |
662 | } | 662 | } |
663 | 663 | ||
664 | snd_printk(KERN_ERR SFX "azx_get_response timeout (ERROR): " | 664 | /* a fatal communication error; need either to reset or to fallback |
665 | "last cmd=0x%08x\n", chip->last_cmd); | 665 | * to the single_cmd mode |
666 | /* re-initialize CORB/RIRB */ | 666 | */ |
667 | spin_lock_irq(&chip->reg_lock); | ||
668 | bus->rirb_error = 1; | 667 | bus->rirb_error = 1; |
668 | if (bus->allow_bus_reset && !bus->response_reset && !bus->in_reset) { | ||
669 | bus->response_reset = 1; | ||
670 | return -1; /* give a chance to retry */ | ||
671 | } | ||
672 | |||
673 | snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, " | ||
674 | "switching to single_cmd mode: last cmd=0x%08x\n", | ||
675 | chip->last_cmd); | ||
676 | chip->single_cmd = 1; | ||
677 | bus->response_reset = 0; | ||
678 | /* re-initialize CORB/RIRB */ | ||
669 | azx_free_cmd_io(chip); | 679 | azx_free_cmd_io(chip); |
670 | azx_init_cmd_io(chip); | 680 | azx_init_cmd_io(chip); |
671 | spin_unlock_irq(&chip->reg_lock); | ||
672 | return -1; | 681 | return -1; |
673 | } | 682 | } |
674 | 683 | ||
@@ -709,6 +718,7 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val) | |||
709 | struct azx *chip = bus->private_data; | 718 | struct azx *chip = bus->private_data; |
710 | int timeout = 50; | 719 | int timeout = 50; |
711 | 720 | ||
721 | bus->rirb_error = 0; | ||
712 | while (timeout--) { | 722 | while (timeout--) { |
713 | /* check ICB busy bit */ | 723 | /* check ICB busy bit */ |
714 | if (!((azx_readw(chip, IRS) & ICH6_IRS_BUSY))) { | 724 | if (!((azx_readw(chip, IRS) & ICH6_IRS_BUSY))) { |
@@ -1247,6 +1257,23 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, | |||
1247 | struct hda_pcm *cpcm); | 1257 | struct hda_pcm *cpcm); |
1248 | static void azx_stop_chip(struct azx *chip); | 1258 | static void azx_stop_chip(struct azx *chip); |
1249 | 1259 | ||
1260 | static void azx_bus_reset(struct hda_bus *bus) | ||
1261 | { | ||
1262 | struct azx *chip = bus->private_data; | ||
1263 | int i; | ||
1264 | |||
1265 | bus->in_reset = 1; | ||
1266 | azx_stop_chip(chip); | ||
1267 | azx_init_chip(chip); | ||
1268 | if (chip->initialized) { | ||
1269 | for (i = 0; i < AZX_MAX_PCMS; i++) | ||
1270 | snd_pcm_suspend_all(chip->pcm[i]); | ||
1271 | snd_hda_suspend(chip->bus); | ||
1272 | snd_hda_resume(chip->bus); | ||
1273 | } | ||
1274 | bus->in_reset = 0; | ||
1275 | } | ||
1276 | |||
1250 | /* | 1277 | /* |
1251 | * Codec initialization | 1278 | * Codec initialization |
1252 | */ | 1279 | */ |
@@ -1270,6 +1297,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model, | |||
1270 | bus_temp.ops.command = azx_send_cmd; | 1297 | bus_temp.ops.command = azx_send_cmd; |
1271 | bus_temp.ops.get_response = azx_get_response; | 1298 | bus_temp.ops.get_response = azx_get_response; |
1272 | bus_temp.ops.attach_pcm = azx_attach_pcm_stream; | 1299 | bus_temp.ops.attach_pcm = azx_attach_pcm_stream; |
1300 | bus_temp.ops.bus_reset = azx_bus_reset; | ||
1273 | #ifdef CONFIG_SND_HDA_POWER_SAVE | 1301 | #ifdef CONFIG_SND_HDA_POWER_SAVE |
1274 | bus_temp.power_save = &power_save; | 1302 | bus_temp.power_save = &power_save; |
1275 | bus_temp.ops.pm_notify = azx_power_notify; | 1303 | bus_temp.ops.pm_notify = azx_power_notify; |
@@ -1997,7 +2025,7 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state) | |||
1997 | for (i = 0; i < AZX_MAX_PCMS; i++) | 2025 | for (i = 0; i < AZX_MAX_PCMS; i++) |
1998 | snd_pcm_suspend_all(chip->pcm[i]); | 2026 | snd_pcm_suspend_all(chip->pcm[i]); |
1999 | if (chip->initialized) | 2027 | if (chip->initialized) |
2000 | snd_hda_suspend(chip->bus, state); | 2028 | snd_hda_suspend(chip->bus); |
2001 | azx_stop_chip(chip); | 2029 | azx_stop_chip(chip); |
2002 | if (chip->irq >= 0) { | 2030 | if (chip->irq >= 0) { |
2003 | free_irq(chip->irq, chip); | 2031 | free_irq(chip->irq, chip); |
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 48f4a36c4813..42f944bb641d 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c | |||
@@ -5375,6 +5375,15 @@ again: | |||
5375 | if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) | 5375 | if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) |
5376 | snd_hda_sequence_write_cache(codec, unmute_init); | 5376 | snd_hda_sequence_write_cache(codec, unmute_init); |
5377 | 5377 | ||
5378 | /* Some HP machines seem to have unstable codec communications | ||
5379 | * especially with ATI fglrx driver. For recovering from the | ||
5380 | * CORB/RIRB stall, allow the BUS reset and keep always sync | ||
5381 | */ | ||
5382 | if (spec->board_config == STAC_HP_DV5) { | ||
5383 | codec->bus->sync_write = 1; | ||
5384 | codec->bus->allow_bus_reset = 1; | ||
5385 | } | ||
5386 | |||
5378 | spec->aloopback_ctl = stac92hd71bxx_loopback; | 5387 | spec->aloopback_ctl = stac92hd71bxx_loopback; |
5379 | spec->aloopback_mask = 0x50; | 5388 | spec->aloopback_mask = 0x50; |
5380 | spec->aloopback_shift = 0; | 5389 | spec->aloopback_shift = 0; |