diff options
Diffstat (limited to 'sound/hda/hdac_i915.c')
-rw-r--r-- | sound/hda/hdac_i915.c | 69 |
1 files changed, 45 insertions, 24 deletions
diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c index 3c2db3816029..454474ac5716 100644 --- a/sound/hda/hdac_i915.c +++ b/sound/hda/hdac_i915.c @@ -11,9 +11,7 @@ #include <sound/hda_i915.h> #include <sound/hda_register.h> -static struct completion bind_complete; - -#define CONTROLLER_IN_GPU(pci) (((pci)->device == 0x0a0c) || \ +#define IS_HSW_CONTROLLER(pci) (((pci)->device == 0x0a0c) || \ ((pci)->device == 0x0c0c) || \ ((pci)->device == 0x0d0c) || \ ((pci)->device == 0x160c)) @@ -41,7 +39,7 @@ void snd_hdac_i915_set_bclk(struct hdac_bus *bus) if (!acomp || !acomp->ops || !acomp->ops->get_cdclk_freq) return; /* only for i915 binding */ - if (!CONTROLLER_IN_GPU(pci)) + if (!IS_HSW_CONTROLLER(pci)) return; /* only HSW/BDW */ cdclk_freq = acomp->ops->get_cdclk_freq(acomp->dev); @@ -73,11 +71,49 @@ 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 */ @@ -92,19 +128,6 @@ static bool i915_gfx_present(void) return pci_dev_present(ids); } -static int i915_master_bind(struct device *dev, - struct drm_audio_component *acomp) -{ - complete_all(&bind_complete); - /* clear audio_ops here as it was needed only for completion call */ - acomp->audio_ops = NULL; - return 0; -} - -static const struct drm_audio_component_audio_ops i915_init_ops = { - .master_bind = i915_master_bind -}; - /** * snd_hdac_i915_init - Initialize i915 audio component * @bus: HDA core bus @@ -125,9 +148,7 @@ int snd_hdac_i915_init(struct hdac_bus *bus) if (!i915_gfx_present()) return -ENODEV; - init_completion(&bind_complete); - - err = snd_hdac_acomp_init(bus, &i915_init_ops, + err = snd_hdac_acomp_init(bus, NULL, i915_component_master_match, sizeof(struct i915_audio_component) - sizeof(*acomp)); if (err < 0) @@ -139,8 +160,8 @@ int snd_hdac_i915_init(struct hdac_bus *bus) if (!IS_ENABLED(CONFIG_MODULES) || !request_module("i915")) { /* 60s timeout */ - wait_for_completion_timeout(&bind_complete, - msecs_to_jiffies(60 * 1000)); + wait_for_completion_timeout(&acomp->master_bind_complete, + msecs_to_jiffies(60 * 1000)); } } if (!acomp->ops) { |