| From jejb@kernel.org Tue Nov 4 11:38:02 2008 |
| From: Takashi Iwai <tiwai@suse.de> |
| Date: Thu, 30 Oct 2008 19:10:15 GMT |
| Subject: ALSA: hda - Add reboot notifier |
| To: jejb@kernel.org, stable@kernel.org |
| Message-ID: <200810301910.m9UJAFgE013161@hera.kernel.org> |
| |
| From: Takashi Iwai <tiwai@suse.de> |
| |
| commit 0cbf00980f0fc4cc064a15ab3dfce19b5fae9130 upstream |
| |
| The current snd-hda-intel driver seems blocking the power-off on some |
| devices like eeepc. Although this is likely a BIOS problem, we can add |
| a workaround by disabling IRQ lines before power-off operation. |
| This patch adds the reboot notifier to achieve it. |
| |
| The detailed problem description is found in bug#11889: |
| http://bugme.linux-foundation.org/show_bug.cgi?id=11889 |
| |
| Tested-by: Luiz Fernando N. Capitulino <lcapitulino@mandriva.com.br> |
| Signed-off-by: Takashi Iwai <tiwai@suse.de> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| sound/pci/hda/hda_intel.c | 29 +++++++++++++++++++++++++++++ |
| 1 file changed, 29 insertions(+) |
| |
| --- a/sound/pci/hda/hda_intel.c |
| +++ b/sound/pci/hda/hda_intel.c |
| @@ -45,6 +45,7 @@ |
| #include <linux/slab.h> |
| #include <linux/pci.h> |
| #include <linux/mutex.h> |
| +#include <linux/reboot.h> |
| #include <sound/core.h> |
| #include <sound/initval.h> |
| #include "hda_codec.h" |
| @@ -385,6 +386,9 @@ struct azx { |
| |
| /* for pending irqs */ |
| struct work_struct irq_pending_work; |
| + |
| + /* reboot notifier (for mysterious hangup problem at power-down) */ |
| + struct notifier_block reboot_notifier; |
| }; |
| |
| /* driver types */ |
| @@ -1890,12 +1894,36 @@ static int azx_resume(struct pci_dev *pc |
| |
| |
| /* |
| + * reboot notifier for hang-up problem at power-down |
| + */ |
| +static int azx_halt(struct notifier_block *nb, unsigned long event, void *buf) |
| +{ |
| + struct azx *chip = container_of(nb, struct azx, reboot_notifier); |
| + azx_stop_chip(chip); |
| + return NOTIFY_OK; |
| +} |
| + |
| +static void azx_notifier_register(struct azx *chip) |
| +{ |
| + chip->reboot_notifier.notifier_call = azx_halt; |
| + register_reboot_notifier(&chip->reboot_notifier); |
| +} |
| + |
| +static void azx_notifier_unregister(struct azx *chip) |
| +{ |
| + if (chip->reboot_notifier.notifier_call) |
| + unregister_reboot_notifier(&chip->reboot_notifier); |
| +} |
| + |
| +/* |
| * destructor |
| */ |
| static int azx_free(struct azx *chip) |
| { |
| int i; |
| |
| + azx_notifier_unregister(chip); |
| + |
| if (chip->initialized) { |
| azx_clear_irq_pending(chip); |
| for (i = 0; i < chip->num_streams; i++) |
| @@ -2250,6 +2278,7 @@ static int __devinit azx_probe(struct pc |
| pci_set_drvdata(pci, card); |
| chip->running = 1; |
| power_down_all_codecs(chip); |
| + azx_notifier_register(chip); |
| |
| dev++; |
| return err; |