ALSA: hda - handle multiple i915 device instances
authorKai Vehmanen <kai.vehmanen@linux.intel.com>
Mon, 21 Sep 2020 14:17:40 +0000 (17:17 +0300)
committerTakashi Iwai <tiwai@suse.de>
Mon, 21 Sep 2020 15:57:30 +0000 (17:57 +0200)
Currently i915_component_master_match() will return the first matching
i915 instance. This does not work in case system has multiple i915
and HDA audio controller instances.

Add a new connectivity check that handles following cases:
 - i915 and HDA controller on same PCI bus
 - discrete GPU with embedded HDA audio controller connected
   via PCI bridge

Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Link: https://lore.kernel.org/r/20200921141741.2983072-4-kai.vehmanen@linux.intel.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/hda/hdac_i915.c

index 3c2db3816029810eac81193a6d86554a10cb56cb..50b2c1db429baef6857846705cb37e6a79e67efd 100644 (file)
@@ -73,11 +73,51 @@ void snd_hdac_i915_set_bclk(struct hdac_bus *bus)
 }
 EXPORT_SYMBOL_GPL(snd_hdac_i915_set_bclk);
 
+/**
+ * Returns true if the devices can be connected for audio.
+ */
+static bool connectivity_check(struct pci_dev *i915, struct pci_dev *hdac)
+{
+       struct pci_bus *bus_a = i915->bus, *bus_b = hdac->bus;
+
+       /* directly connected on the same bus */
+       if (bus_a == bus_b)
+               return true;
+
+       /*
+        * on i915 discrete GPUs with embedded HDA audio, the two
+        * devices are connected via 2nd level PCI bridge
+        */
+       bus_a = bus_a->parent;
+       bus_b = bus_b->parent;
+       if (!bus_a || !bus_b)
+               return false;
+       bus_a = bus_a->parent;
+       bus_b = bus_b->parent;
+       if (bus_a && bus_a == bus_b)
+               return true;
+
+       return false;
+}
+
 static int i915_component_master_match(struct device *dev, int subcomponent,
                                       void *data)
 {
-       return !strcmp(dev->driver->name, "i915") &&
-              subcomponent == I915_COMPONENT_AUDIO;
+       struct pci_dev *hdac_pci, *i915_pci;
+       struct hdac_bus *bus = data;
+
+       if (!dev_is_pci(dev))
+               return 0;
+
+       hdac_pci = to_pci_dev(bus->dev);
+       i915_pci = to_pci_dev(dev);
+
+       if (!strcmp(dev->driver->name, "i915") &&
+           subcomponent == I915_COMPONENT_AUDIO &&
+           connectivity_check(i915_pci, hdac_pci))
+               return 1;
+
+       return 0;
 }
 
 /* check whether intel graphics is present */