]>
Commit | Line | Data |
---|---|---|
c942fddf | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
98d8fc6c ML |
2 | /* |
3 | * hdac_i915.c - routines for sync between HD-A core and i915 display driver | |
98d8fc6c ML |
4 | */ |
5 | ||
6 | #include <linux/init.h> | |
7 | #include <linux/module.h> | |
8 | #include <linux/pci.h> | |
98d8fc6c ML |
9 | #include <sound/core.h> |
10 | #include <sound/hdaudio.h> | |
11 | #include <sound/hda_i915.h> | |
bb03ed21 | 12 | #include <sound/hda_register.h> |
98d8fc6c | 13 | |
f9b54e19 TI |
14 | static struct completion bind_complete; |
15 | ||
bb03ed21 TI |
16 | #define CONTROLLER_IN_GPU(pci) (((pci)->device == 0x0a0c) || \ |
17 | ((pci)->device == 0x0c0c) || \ | |
18 | ((pci)->device == 0x0d0c) || \ | |
19 | ((pci)->device == 0x160c)) | |
20 | ||
78dd5e21 | 21 | /** |
bb03ed21 | 22 | * snd_hdac_i915_set_bclk - Reprogram BCLK for HSW/BDW |
78dd5e21 TI |
23 | * @bus: HDA core bus |
24 | * | |
bb03ed21 TI |
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. | |
78dd5e21 | 31 | * |
bb03ed21 TI |
32 | * Call this function at initializing and changing power well, as well as |
33 | * at ELD notifier for the hotplug. | |
78dd5e21 | 34 | */ |
bb03ed21 | 35 | void snd_hdac_i915_set_bclk(struct hdac_bus *bus) |
98d8fc6c | 36 | { |
ae891abe | 37 | struct drm_audio_component *acomp = bus->audio_component; |
bb03ed21 TI |
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 | } | |
98d8fc6c | 70 | |
bb03ed21 TI |
71 | snd_hdac_chip_writew(bus, HSW_EM4, bclk_m); |
72 | snd_hdac_chip_writew(bus, HSW_EM5, bclk_n); | |
98d8fc6c | 73 | } |
bb03ed21 | 74 | EXPORT_SYMBOL_GPL(snd_hdac_i915_set_bclk); |
98d8fc6c | 75 | |
8857c7d0 SV |
76 | static int i915_component_master_match(struct device *dev, int subcomponent, |
77 | void *data) | |
e2dc7d7d | 78 | { |
8857c7d0 SV |
79 | return !strcmp(dev->driver->name, "i915") && |
80 | subcomponent == I915_COMPONENT_AUDIO; | |
98d8fc6c ML |
81 | } |
82 | ||
bfa5fb14 TI |
83 | /* check whether intel graphics is present */ |
84 | static bool i915_gfx_present(void) | |
85 | { | |
6c5a2660 | 86 | static const struct pci_device_id ids[] = { |
bfa5fb14 TI |
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 | ||
f9b54e19 TI |
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 | ||
78dd5e21 TI |
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 | */ | |
98d8fc6c ML |
120 | int snd_hdac_i915_init(struct hdac_bus *bus) |
121 | { | |
ae891abe | 122 | struct drm_audio_component *acomp; |
a57942bf | 123 | int err; |
d745f5e7 | 124 | |
bfa5fb14 TI |
125 | if (!i915_gfx_present()) |
126 | return -ENODEV; | |
127 | ||
f9b54e19 TI |
128 | init_completion(&bind_complete); |
129 | ||
130 | err = snd_hdac_acomp_init(bus, &i915_init_ops, | |
a57942bf TI |
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; | |
f9b54e19 | 138 | if (!acomp->ops) { |
74bf71ed ST |
139 | if (!IS_ENABLED(CONFIG_MODULES) || |
140 | !request_module("i915")) { | |
141 | /* 60s timeout */ | |
142 | wait_for_completion_timeout(&bind_complete, | |
143 | msecs_to_jiffies(60 * 1000)); | |
144 | } | |
f9b54e19 | 145 | } |
98d8fc6c | 146 | if (!acomp->ops) { |
b3a5402c | 147 | dev_info(bus->dev, "couldn't bind with audio component\n"); |
a57942bf TI |
148 | snd_hdac_acomp_exit(bus); |
149 | return -ENODEV; | |
98d8fc6c | 150 | } |
98d8fc6c | 151 | return 0; |
98d8fc6c ML |
152 | } |
153 | EXPORT_SYMBOL_GPL(snd_hdac_i915_init); |