1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Intel ACPI functions |
4 | * |
5 | * _DSM related code stolen from nouveau_acpi.c. |
6 | */ |
7 | |
8 | #include <linux/pci.h> |
9 | #include <linux/acpi.h> |
10 | #include <acpi/video.h> |
11 | |
12 | #include "i915_drv.h" |
13 | #include "intel_acpi.h" |
14 | #include "intel_display_types.h" |
15 | |
16 | #define INTEL_DSM_REVISION_ID 1 /* For Calpella anyway... */ |
17 | #define INTEL_DSM_FN_PLATFORM_MUX_INFO 1 /* No args */ |
18 | |
19 | static const guid_t intel_dsm_guid = |
20 | GUID_INIT(0x7ed873d3, 0xc2d0, 0x4e4f, |
21 | 0xa8, 0x54, 0x0f, 0x13, 0x17, 0xb0, 0x1c, 0x2c); |
22 | |
23 | #define INTEL_DSM_FN_GET_BIOS_DATA_FUNCS_SUPPORTED 0 /* No args */ |
24 | |
25 | static const guid_t intel_dsm_guid2 = |
26 | GUID_INIT(0x3e5b41c6, 0xeb1d, 0x4260, |
27 | 0x9d, 0x15, 0xc7, 0x1f, 0xba, 0xda, 0xe4, 0x14); |
28 | |
29 | static char *intel_dsm_port_name(u8 id) |
30 | { |
31 | switch (id) { |
32 | case 0: |
33 | return "Reserved" ; |
34 | case 1: |
35 | return "Analog VGA" ; |
36 | case 2: |
37 | return "LVDS" ; |
38 | case 3: |
39 | return "Reserved" ; |
40 | case 4: |
41 | return "HDMI/DVI_B" ; |
42 | case 5: |
43 | return "HDMI/DVI_C" ; |
44 | case 6: |
45 | return "HDMI/DVI_D" ; |
46 | case 7: |
47 | return "DisplayPort_A" ; |
48 | case 8: |
49 | return "DisplayPort_B" ; |
50 | case 9: |
51 | return "DisplayPort_C" ; |
52 | case 0xa: |
53 | return "DisplayPort_D" ; |
54 | case 0xb: |
55 | case 0xc: |
56 | case 0xd: |
57 | return "Reserved" ; |
58 | case 0xe: |
59 | return "WiDi" ; |
60 | default: |
61 | return "bad type" ; |
62 | } |
63 | } |
64 | |
65 | static char *intel_dsm_mux_type(u8 type) |
66 | { |
67 | switch (type) { |
68 | case 0: |
69 | return "unknown" ; |
70 | case 1: |
71 | return "No MUX, iGPU only" ; |
72 | case 2: |
73 | return "No MUX, dGPU only" ; |
74 | case 3: |
75 | return "MUXed between iGPU and dGPU" ; |
76 | default: |
77 | return "bad type" ; |
78 | } |
79 | } |
80 | |
81 | static void intel_dsm_platform_mux_info(acpi_handle dhandle) |
82 | { |
83 | int i; |
84 | union acpi_object *pkg, *connector_count; |
85 | |
86 | pkg = acpi_evaluate_dsm_typed(handle: dhandle, guid: &intel_dsm_guid, |
87 | INTEL_DSM_REVISION_ID, INTEL_DSM_FN_PLATFORM_MUX_INFO, |
88 | NULL, ACPI_TYPE_PACKAGE); |
89 | if (!pkg) { |
90 | DRM_DEBUG_DRIVER("failed to evaluate _DSM\n" ); |
91 | return; |
92 | } |
93 | |
94 | if (!pkg->package.count) { |
95 | DRM_DEBUG_DRIVER("no connection in _DSM\n" ); |
96 | return; |
97 | } |
98 | |
99 | connector_count = &pkg->package.elements[0]; |
100 | DRM_DEBUG_DRIVER("MUX info connectors: %lld\n" , |
101 | (unsigned long long)connector_count->integer.value); |
102 | for (i = 1; i < pkg->package.count; i++) { |
103 | union acpi_object *obj = &pkg->package.elements[i]; |
104 | union acpi_object *connector_id; |
105 | union acpi_object *info; |
106 | |
107 | if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count < 2) { |
108 | DRM_DEBUG_DRIVER("Invalid object for MUX #%d\n" , i); |
109 | continue; |
110 | } |
111 | |
112 | connector_id = &obj->package.elements[0]; |
113 | info = &obj->package.elements[1]; |
114 | if (info->type != ACPI_TYPE_BUFFER || info->buffer.length < 4) { |
115 | DRM_DEBUG_DRIVER("Invalid info for MUX obj #%d\n" , i); |
116 | continue; |
117 | } |
118 | |
119 | DRM_DEBUG_DRIVER("Connector id: 0x%016llx\n" , |
120 | (unsigned long long)connector_id->integer.value); |
121 | DRM_DEBUG_DRIVER(" port id: %s\n" , |
122 | intel_dsm_port_name(info->buffer.pointer[0])); |
123 | DRM_DEBUG_DRIVER(" display mux info: %s\n" , |
124 | intel_dsm_mux_type(info->buffer.pointer[1])); |
125 | DRM_DEBUG_DRIVER(" aux/dc mux info: %s\n" , |
126 | intel_dsm_mux_type(info->buffer.pointer[2])); |
127 | DRM_DEBUG_DRIVER(" hpd mux info: %s\n" , |
128 | intel_dsm_mux_type(info->buffer.pointer[3])); |
129 | } |
130 | |
131 | ACPI_FREE(pkg); |
132 | } |
133 | |
134 | static acpi_handle intel_dsm_pci_probe(struct pci_dev *pdev) |
135 | { |
136 | acpi_handle dhandle; |
137 | |
138 | dhandle = ACPI_HANDLE(&pdev->dev); |
139 | if (!dhandle) |
140 | return NULL; |
141 | |
142 | if (!acpi_check_dsm(handle: dhandle, guid: &intel_dsm_guid, INTEL_DSM_REVISION_ID, |
143 | funcs: 1 << INTEL_DSM_FN_PLATFORM_MUX_INFO)) { |
144 | DRM_DEBUG_KMS("no _DSM method for intel device\n" ); |
145 | return NULL; |
146 | } |
147 | |
148 | intel_dsm_platform_mux_info(dhandle); |
149 | |
150 | return dhandle; |
151 | } |
152 | |
153 | static bool intel_dsm_detect(void) |
154 | { |
155 | acpi_handle dhandle = NULL; |
156 | char acpi_method_name[255] = {}; |
157 | struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; |
158 | struct pci_dev *pdev = NULL; |
159 | int vga_count = 0; |
160 | |
161 | while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, from: pdev)) != NULL) { |
162 | vga_count++; |
163 | dhandle = intel_dsm_pci_probe(pdev) ?: dhandle; |
164 | } |
165 | |
166 | if (vga_count == 2 && dhandle) { |
167 | acpi_get_name(object: dhandle, ACPI_FULL_PATHNAME, ret_path_ptr: &buffer); |
168 | DRM_DEBUG_DRIVER("vga_switcheroo: detected DSM switching method %s handle\n" , |
169 | acpi_method_name); |
170 | return true; |
171 | } |
172 | |
173 | return false; |
174 | } |
175 | |
176 | void intel_register_dsm_handler(void) |
177 | { |
178 | if (!intel_dsm_detect()) |
179 | return; |
180 | } |
181 | |
182 | void intel_unregister_dsm_handler(void) |
183 | { |
184 | } |
185 | |
186 | void intel_dsm_get_bios_data_funcs_supported(struct drm_i915_private *i915) |
187 | { |
188 | struct pci_dev *pdev = to_pci_dev(i915->drm.dev); |
189 | acpi_handle dhandle; |
190 | union acpi_object *obj; |
191 | |
192 | dhandle = ACPI_HANDLE(&pdev->dev); |
193 | if (!dhandle) |
194 | return; |
195 | |
196 | obj = acpi_evaluate_dsm(handle: dhandle, guid: &intel_dsm_guid2, INTEL_DSM_REVISION_ID, |
197 | INTEL_DSM_FN_GET_BIOS_DATA_FUNCS_SUPPORTED, NULL); |
198 | if (obj) |
199 | ACPI_FREE(obj); |
200 | } |
201 | |
202 | /* |
203 | * ACPI Specification, Revision 5.0, Appendix B.3.2 _DOD (Enumerate All Devices |
204 | * Attached to the Display Adapter). |
205 | */ |
206 | #define ACPI_DISPLAY_INDEX_SHIFT 0 |
207 | #define ACPI_DISPLAY_INDEX_MASK (0xf << 0) |
208 | #define ACPI_DISPLAY_PORT_ATTACHMENT_SHIFT 4 |
209 | #define ACPI_DISPLAY_PORT_ATTACHMENT_MASK (0xf << 4) |
210 | #define ACPI_DISPLAY_TYPE_SHIFT 8 |
211 | #define ACPI_DISPLAY_TYPE_MASK (0xf << 8) |
212 | #define ACPI_DISPLAY_TYPE_OTHER (0 << 8) |
213 | #define ACPI_DISPLAY_TYPE_VGA (1 << 8) |
214 | #define ACPI_DISPLAY_TYPE_TV (2 << 8) |
215 | #define ACPI_DISPLAY_TYPE_EXTERNAL_DIGITAL (3 << 8) |
216 | #define ACPI_DISPLAY_TYPE_INTERNAL_DIGITAL (4 << 8) |
217 | #define ACPI_VENDOR_SPECIFIC_SHIFT 12 |
218 | #define ACPI_VENDOR_SPECIFIC_MASK (0xf << 12) |
219 | #define ACPI_BIOS_CAN_DETECT (1 << 16) |
220 | #define ACPI_DEPENDS_ON_VGA (1 << 17) |
221 | #define ACPI_PIPE_ID_SHIFT 18 |
222 | #define ACPI_PIPE_ID_MASK (7 << 18) |
223 | #define ACPI_DEVICE_ID_SCHEME (1ULL << 31) |
224 | |
225 | static u32 acpi_display_type(struct intel_connector *connector) |
226 | { |
227 | u32 display_type; |
228 | |
229 | switch (connector->base.connector_type) { |
230 | case DRM_MODE_CONNECTOR_VGA: |
231 | case DRM_MODE_CONNECTOR_DVIA: |
232 | display_type = ACPI_DISPLAY_TYPE_VGA; |
233 | break; |
234 | case DRM_MODE_CONNECTOR_Composite: |
235 | case DRM_MODE_CONNECTOR_SVIDEO: |
236 | case DRM_MODE_CONNECTOR_Component: |
237 | case DRM_MODE_CONNECTOR_9PinDIN: |
238 | case DRM_MODE_CONNECTOR_TV: |
239 | display_type = ACPI_DISPLAY_TYPE_TV; |
240 | break; |
241 | case DRM_MODE_CONNECTOR_DVII: |
242 | case DRM_MODE_CONNECTOR_DVID: |
243 | case DRM_MODE_CONNECTOR_DisplayPort: |
244 | case DRM_MODE_CONNECTOR_HDMIA: |
245 | case DRM_MODE_CONNECTOR_HDMIB: |
246 | display_type = ACPI_DISPLAY_TYPE_EXTERNAL_DIGITAL; |
247 | break; |
248 | case DRM_MODE_CONNECTOR_LVDS: |
249 | case DRM_MODE_CONNECTOR_eDP: |
250 | case DRM_MODE_CONNECTOR_DSI: |
251 | display_type = ACPI_DISPLAY_TYPE_INTERNAL_DIGITAL; |
252 | break; |
253 | case DRM_MODE_CONNECTOR_Unknown: |
254 | case DRM_MODE_CONNECTOR_VIRTUAL: |
255 | display_type = ACPI_DISPLAY_TYPE_OTHER; |
256 | break; |
257 | default: |
258 | MISSING_CASE(connector->base.connector_type); |
259 | display_type = ACPI_DISPLAY_TYPE_OTHER; |
260 | break; |
261 | } |
262 | |
263 | return display_type; |
264 | } |
265 | |
266 | void intel_acpi_device_id_update(struct drm_i915_private *dev_priv) |
267 | { |
268 | struct drm_device *drm_dev = &dev_priv->drm; |
269 | struct intel_connector *connector; |
270 | struct drm_connector_list_iter conn_iter; |
271 | u8 display_index[16] = {}; |
272 | |
273 | /* Populate the ACPI IDs for all connectors for a given drm_device */ |
274 | drm_connector_list_iter_begin(dev: drm_dev, iter: &conn_iter); |
275 | for_each_intel_connector_iter(connector, &conn_iter) { |
276 | u32 device_id, type; |
277 | |
278 | device_id = acpi_display_type(connector); |
279 | |
280 | /* Use display type specific display index. */ |
281 | type = (device_id & ACPI_DISPLAY_TYPE_MASK) |
282 | >> ACPI_DISPLAY_TYPE_SHIFT; |
283 | device_id |= display_index[type]++ << ACPI_DISPLAY_INDEX_SHIFT; |
284 | |
285 | connector->acpi_device_id = device_id; |
286 | } |
287 | drm_connector_list_iter_end(iter: &conn_iter); |
288 | } |
289 | |
290 | /* NOTE: The connector order must be final before this is called. */ |
291 | void intel_acpi_assign_connector_fwnodes(struct drm_i915_private *i915) |
292 | { |
293 | struct drm_connector_list_iter conn_iter; |
294 | struct drm_device *drm_dev = &i915->drm; |
295 | struct fwnode_handle *fwnode = NULL; |
296 | struct drm_connector *connector; |
297 | struct acpi_device *adev; |
298 | |
299 | drm_connector_list_iter_begin(dev: drm_dev, iter: &conn_iter); |
300 | drm_for_each_connector_iter(connector, &conn_iter) { |
301 | /* Always getting the next, even when the last was not used. */ |
302 | fwnode = device_get_next_child_node(dev: drm_dev->dev, child: fwnode); |
303 | if (!fwnode) |
304 | break; |
305 | |
306 | switch (connector->connector_type) { |
307 | case DRM_MODE_CONNECTOR_LVDS: |
308 | case DRM_MODE_CONNECTOR_eDP: |
309 | case DRM_MODE_CONNECTOR_DSI: |
310 | /* |
311 | * Integrated displays have a specific address 0x1f on |
312 | * most Intel platforms, but not on all of them. |
313 | */ |
314 | adev = acpi_find_child_device(ACPI_COMPANION(drm_dev->dev), |
315 | address: 0x1f, check_children: 0); |
316 | if (adev) { |
317 | connector->fwnode = |
318 | fwnode_handle_get(fwnode: acpi_fwnode_handle(adev)); |
319 | break; |
320 | } |
321 | fallthrough; |
322 | default: |
323 | connector->fwnode = fwnode_handle_get(fwnode); |
324 | break; |
325 | } |
326 | } |
327 | drm_connector_list_iter_end(iter: &conn_iter); |
328 | /* |
329 | * device_get_next_child_node() takes a reference on the fwnode, if |
330 | * we stopped iterating because we are out of connectors we need to |
331 | * put this, otherwise fwnode is NULL and the put is a no-op. |
332 | */ |
333 | fwnode_handle_put(fwnode); |
334 | } |
335 | |
336 | void intel_acpi_video_register(struct drm_i915_private *i915) |
337 | { |
338 | struct drm_connector_list_iter conn_iter; |
339 | struct drm_connector *connector; |
340 | |
341 | acpi_video_register(); |
342 | |
343 | /* |
344 | * If i915 is driving an internal panel without registering its native |
345 | * backlight handler try to register the acpi_video backlight. |
346 | * For panels not driven by i915 another GPU driver may still register |
347 | * a native backlight later and acpi_video_register_backlight() should |
348 | * only be called after any native backlights have been registered. |
349 | */ |
350 | drm_connector_list_iter_begin(dev: &i915->drm, iter: &conn_iter); |
351 | drm_for_each_connector_iter(connector, &conn_iter) { |
352 | struct intel_panel *panel = &to_intel_connector(connector)->panel; |
353 | |
354 | if (panel->backlight.funcs && !panel->backlight.device) { |
355 | acpi_video_register_backlight(); |
356 | break; |
357 | } |
358 | } |
359 | drm_connector_list_iter_end(iter: &conn_iter); |
360 | } |
361 | |