1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * hdac_i915.c - routines for sync between HD-A core and i915 display driver 4 */ 5 6 #include <linux/init.h> 7 #include <linux/module.h> 8 #include <linux/pci.h> 9 #include <sound/core.h> 10 #include <sound/hdaudio.h> 11 #include <sound/hda_i915.h> 12 #include <sound/hda_register.h> 13 14 static struct completion bind_complete; 15 16 #define CONTROLLER_IN_GPU(pci) (((pci)->device == 0x0a0c) || \ 17 ((pci)->device == 0x0c0c) || \ 18 ((pci)->device == 0x0d0c) || \ 19 ((pci)->device == 0x160c)) 20 21 /** 22 * snd_hdac_i915_set_bclk - Reprogram BCLK for HSW/BDW 23 * @bus: HDA core bus 24 * 25 * Intel HSW/BDW display HDA controller is in GPU. Both its power and link BCLK 26 * depends on GPU. Two Extended Mode registers EM4 (M value) and EM5 (N Value) 27 * are used to convert CDClk (Core Display Clock) to 24MHz BCLK: 28 * BCLK = CDCLK * M / N 29 * The values will be lost when the display power well is disabled and need to 30 * be restored to avoid abnormal playback speed. 31 * 32 * Call this function at initializing and changing power well, as well as 33 * at ELD notifier for the hotplug. 34 */ 35 void snd_hdac_i915_set_bclk(struct hdac_bus *bus) 36 { 37 struct drm_audio_component *acomp = bus->audio_component; 38 struct pci_dev *pci = to_pci_dev(bus->dev); 39 int cdclk_freq; 40 unsigned int bclk_m, bclk_n; 41 42 if (!acomp || !acomp->ops || !acomp->ops->get_cdclk_freq) 43 return; /* only for i915 binding */ 44 if (!CONTROLLER_IN_GPU(pci)) 45 return; /* only HSW/BDW */ 46 47 cdclk_freq = acomp->ops->get_cdclk_freq(acomp->dev); 48 switch (cdclk_freq) { 49 case 337500: 50 bclk_m = 16; 51 bclk_n = 225; 52 break; 53 54 case 450000: 55 default: /* default CDCLK 450MHz */ 56 bclk_m = 4; 57 bclk_n = 75; 58 break; 59 60 case 540000: 61 bclk_m = 4; 62 bclk_n = 90; 63 break; 64 65 case 675000: 66 bclk_m = 8; 67 bclk_n = 225; 68 break; 69 } 70 71 snd_hdac_chip_writew(bus, HSW_EM4, bclk_m); 72 snd_hdac_chip_writew(bus, HSW_EM5, bclk_n); 73 } 74 EXPORT_SYMBOL_GPL(snd_hdac_i915_set_bclk); 75 76 static int i915_component_master_match(struct device *dev, int subcomponent, 77 void *data) 78 { 79 return !strcmp(dev->driver->name, "i915") && 80 subcomponent == I915_COMPONENT_AUDIO; 81 } 82 83 /* check whether intel graphics is present */ 84 static bool i915_gfx_present(void) 85 { 86 static const struct pci_device_id ids[] = { 87 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID), 88 .class = PCI_BASE_CLASS_DISPLAY << 16, 89 .class_mask = 0xff << 16 }, 90 {} 91 }; 92 return pci_dev_present(ids); 93 } 94 95 static int i915_master_bind(struct device *dev, 96 struct drm_audio_component *acomp) 97 { 98 complete_all(&bind_complete); 99 /* clear audio_ops here as it was needed only for completion call */ 100 acomp->audio_ops = NULL; 101 return 0; 102 } 103 104 static const struct drm_audio_component_audio_ops i915_init_ops = { 105 .master_bind = i915_master_bind 106 }; 107 108 /** 109 * snd_hdac_i915_init - Initialize i915 audio component 110 * @bus: HDA core bus 111 * 112 * This function is supposed to be used only by a HD-audio controller 113 * driver that needs the interaction with i915 graphics. 114 * 115 * This function initializes and sets up the audio component to communicate 116 * with i915 graphics driver. 117 * 118 * Returns zero for success or a negative error code. 119 */ 120 int snd_hdac_i915_init(struct hdac_bus *bus) 121 { 122 struct drm_audio_component *acomp; 123 int err; 124 125 if (!i915_gfx_present()) 126 return -ENODEV; 127 128 init_completion(&bind_complete); 129 130 err = snd_hdac_acomp_init(bus, &i915_init_ops, 131 i915_component_master_match, 132 sizeof(struct i915_audio_component) - sizeof(*acomp)); 133 if (err < 0) 134 return err; 135 acomp = bus->audio_component; 136 if (!acomp) 137 return -ENODEV; 138 if (!acomp->ops) { 139 request_module("i915"); 140 /* 60s timeout */ 141 wait_for_completion_timeout(&bind_complete, 142 msecs_to_jiffies(60 * 1000)); 143 } 144 if (!acomp->ops) { 145 dev_info(bus->dev, "couldn't bind with audio component\n"); 146 snd_hdac_acomp_exit(bus); 147 return -ENODEV; 148 } 149 return 0; 150 } 151 EXPORT_SYMBOL_GPL(snd_hdac_i915_init); 152