diff options
author | Takashi Iwai | 2012-04-26 12:13:25 +0200 |
---|---|---|
committer | Takashi Iwai | 2012-05-14 14:49:17 +0200 |
commit | 9121947d696df7ea259c0102e449da9621b9cf92 (patch) | |
tree | d9924ab48c80ba91649c2e4bae8adc1b44ad11b2 /sound/pci | |
parent | d9bbb4756dbc05764cebd0e3e2f49a56c9504e4d (diff) |
ALSA: hda - Check the dead HDMI audio controller by vga-switcheroo
When a discrete-GPU is disabled by the VGA switcheroo, the
corresponding HD-audio controller for HDMI output is also disabled.
Such a dead controller still appears in the PCI device list, but you
can't access properly any longer (even calling pci_read_config_*()
triggers Oops!) which leads the stall of the whole communication of
the driver.
This patch adds a check of graphics controller at the probe time to
see whether it's disabled by vga-switcheroo. If disabled, skip the
whole initialization of this controller.
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=43155
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci')
-rw-r--r-- | sound/pci/hda/hda_intel.c | 54 |
1 files changed, 52 insertions, 2 deletions
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index a70d7e5443aa..06a4ad3e5cd2 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -53,6 +53,7 @@ #endif #include <sound/core.h> #include <sound/initval.h> +#include <linux/vgaarb.h> #include "hda_codec.h" @@ -2494,6 +2495,45 @@ static int azx_dev_free(struct snd_device *device) } /* + * Check of disabled HDMI controller by vga-switcheroo + */ +static struct pci_dev __devinit *get_bound_vga(struct pci_dev *pci) +{ + struct pci_dev *p; + + /* check only discrete GPU */ + switch (pci->vendor) { + case PCI_VENDOR_ID_ATI: + case PCI_VENDOR_ID_AMD: + case PCI_VENDOR_ID_NVIDIA: + if (pci->devfn == 1) { + p = pci_get_domain_bus_and_slot(pci_domain_nr(pci->bus), + pci->bus->number, 0); + if (p) { + if ((p->class >> 8) == PCI_CLASS_DISPLAY_VGA) + return p; + pci_dev_put(p); + } + } + break; + } + return NULL; +} + +static bool __devinit check_hdmi_disabled(struct pci_dev *pci) +{ + bool vga_inactive = false; + struct pci_dev *p = get_bound_vga(pci); + + if (p) { + if (vga_default_device() && p != vga_default_device()) + vga_inactive = true; + pci_dev_put(p); + } + return vga_inactive; +} + +/* * white/black-listing for position_fix */ static struct snd_pci_quirk position_fix_list[] __devinitdata = { @@ -2928,6 +2968,12 @@ static int __devinit azx_probe(struct pci_dev *pci, return -ENOENT; } + if (check_hdmi_disabled(pci)) { + snd_printk(KERN_INFO SFX + "Inactive VGA controller; disabled audio, too\n"); + goto out; + } + err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); if (err < 0) { snd_printk(KERN_ERR SFX "Error creating card!\n"); @@ -2984,8 +3030,10 @@ static int __devinit azx_probe(struct pci_dev *pci, power_down_all_codecs(chip); azx_notifier_register(chip); + out: dev++; - return err; + return 0; + out_free: snd_card_free(card); return err; @@ -2993,7 +3041,9 @@ out_free: static void __devexit azx_remove(struct pci_dev *pci) { - snd_card_free(pci_get_drvdata(pci)); + struct snd_card *card = pci_get_drvdata(pci); + if (card) + snd_card_free(card); pci_set_drvdata(pci, NULL); } |