aboutsummaryrefslogtreecommitdiff
path: root/sound/pci
diff options
context:
space:
mode:
authorTakashi Iwai2012-04-26 12:13:25 +0200
committerTakashi Iwai2012-05-14 14:49:17 +0200
commit9121947d696df7ea259c0102e449da9621b9cf92 (patch)
treed9924ab48c80ba91649c2e4bae8adc1b44ad11b2 /sound/pci
parentd9bbb4756dbc05764cebd0e3e2f49a56c9504e4d (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.c54
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);
}