From 8dd783304e6d0f7c2830365d63f75f08aa343e10 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 01:16:07 +0200 Subject: 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 --- sound/pci/hda/hda_codec.c | 32 ++++++++++++++++---------------- sound/pci/hda/hda_codec.h | 6 +++++- sound/pci/hda/hda_intel.c | 40 ++++++++++++++++++++++++++++++++++------ 3 files changed, 55 insertions(+), 23 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index d1d5fb9d7afb..aa0e1c18b606 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, unsigned int *res) { struct hda_bus *bus = codec->bus; - int err, repeated = 0; + int err; if (res) *res = -1; + again: snd_hda_power_up(codec); mutex_lock(&bus->cmd_mutex); - again: err = bus->ops.command(bus, cmd); - if (!err) { - if (res) { - *res = bus->ops.get_response(bus); - if (*res == -1 && bus->rirb_error) { - if (repeated++ < 1) { - snd_printd(KERN_WARNING "hda_codec: " - "Trying verb 0x%08x again\n", cmd); - goto again; - } - } - } - } + if (!err && res) + *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; + } + /* clear reset-flag when the communication gets recovered */ + if (!err) + bus->response_reset = 0; return err; } @@ -3894,11 +3895,10 @@ EXPORT_SYMBOL_HDA(auto_pin_cfg_labels); /** * 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; diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index c5bd40f77bb9..f5fa5d1f223c 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -574,6 +574,8 @@ struct hda_bus_ops { /* attach a PCM stream */ int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec, struct hda_pcm *pcm); + /* 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); @@ -624,6 +626,8 @@ struct hda_bus { unsigned int needs_damn_long_delay :1; unsigned int shutdown :1; /* being unloaded */ 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 */ }; /* @@ -907,7 +911,7 @@ void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen); * 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 diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index b063d0e3d325..44f9a0aa20c5 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) 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 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val) 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 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, 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 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model, 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 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state) 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); -- cgit v1.2.3 From b20f3b834673be9ead83a3c6f07fa3881d1a990f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Jun 2009 01:20:22 +0200 Subject: ALSA: hda - Limit codec-verb retry to limited hardwares The reset of a BUS controller during operations is somehow risky and shouldn't be done inevitably for devices that have apparently no such codec-communication problems. This patch adds the check of the hardware and limits the bus-reset capability. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 12 ++---------- sound/pci/hda/hda_codec.h | 3 +++ sound/pci/hda/hda_intel.c | 2 +- sound/pci/hda/patch_sigmatel.c | 9 +++++++++ 4 files changed, 15 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index aa0e1c18b606..562403a23488 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -214,11 +214,6 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_codec_read); -/* Define the below to send and receive verbs synchronously. - * If you often get any codec communication errors, this is worth to try. - */ -/* #define SND_HDA_SUPPORT_SYNC_WRITE */ - /** * snd_hda_codec_write - send a single command without waiting for response * @codec: the HDA codec @@ -235,12 +230,9 @@ int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm) { unsigned int cmd = make_codec_cmd(codec, nid, direct, verb, parm); -#ifdef SND_HDA_SUPPORT_SYNC_WRITE unsigned int res; - return codec_exec_verb(codec, cmd, &res); -#else - return codec_exec_verb(codec, cmd, NULL); -#endif + return codec_exec_verb(codec, cmd, + codec->bus->sync_write ? &res : NULL); } EXPORT_SYMBOL_HDA(snd_hda_codec_write); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index f5fa5d1f223c..cad79efaabc9 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -624,6 +624,9 @@ struct hda_bus { /* misc op flags */ unsigned int needs_damn_long_delay :1; + unsigned int allow_bus_reset:1; /* allow bus reset at fatal error */ + unsigned int sync_write:1; /* sync after verb write */ + /* status for codec/controller */ unsigned int shutdown :1; /* being unloaded */ unsigned int rirb_error:1; /* error in codec communication */ unsigned int response_reset:1; /* controller was reset */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 44f9a0aa20c5..9f44645a1d04 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -665,7 +665,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus) * to the single_cmd mode */ bus->rirb_error = 1; - if (!bus->response_reset && !bus->in_reset) { + if (bus->allow_bus_reset && !bus->response_reset && !bus->in_reset) { bus->response_reset = 1; return -1; /* give a chance to retry */ } 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: if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) snd_hda_sequence_write_cache(codec, unmute_init); + /* Some HP machines seem to have unstable codec communications + * especially with ATI fglrx driver. For recovering from the + * CORB/RIRB stall, allow the BUS reset and keep always sync + */ + if (spec->board_config == STAC_HP_DV5) { + codec->bus->sync_write = 1; + codec->bus->allow_bus_reset = 1; + } + spec->aloopback_ctl = stac92hd71bxx_loopback; spec->aloopback_mask = 0x50; spec->aloopback_shift = 0; -- cgit v1.2.3