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_intel.c | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda/hda_intel.c') 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