intel_acpi.c revision 1.4 1 /* $NetBSD: intel_acpi.c,v 1.4 2021/12/19 11:38:03 riastradh Exp $ */
2
3 // SPDX-License-Identifier: GPL-2.0
4 /*
5 * Intel ACPI functions
6 *
7 * _DSM related code stolen from nouveau_acpi.c.
8 */
9
10 #include <sys/cdefs.h>
11 __KERNEL_RCSID(0, "$NetBSD: intel_acpi.c,v 1.4 2021/12/19 11:38:03 riastradh Exp $");
12
13 #include <linux/pci.h>
14 #include <linux/acpi.h>
15
16 #include "i915_drv.h"
17 #include "intel_acpi.h"
18
19 #ifdef __NetBSD__
20
21 #include <dev/acpi/acpireg.h>
22 #define _COMPONENT ACPI_BUTTON_COMPONENT
23 ACPI_MODULE_NAME("acpi_intel_brightness")
24
25 #include <dev/acpi/acpi_pci.h>
26
27 #define acpi_handle ACPI_HANDLE
28 #define buffer Buffer
29 #define count Count
30 #define elements Elements
31 #define integer Integer
32 #define package Package
33 #define pointer Pointer
34 #define value Value
35
36 static ACPI_OBJECT *
37 acpi_evaluate_dsm(ACPI_HANDLE handle, const guid_t *uuid, int rev, int func,
38 ACPI_OBJECT *argv4)
39 {
40 ACPI_OBJECT_LIST arg;
41 ACPI_OBJECT params[4];
42 ACPI_BUFFER buf;
43 ACPI_STATUS rv;
44
45 if (handle == NULL)
46 handle = ACPI_ROOT_OBJECT;
47
48 arg.Count = 4;
49 arg.Pointer = params;
50 params[0].Type = ACPI_TYPE_BUFFER;
51 params[0].Buffer.Length = 16;
52 params[0].Buffer.Pointer = (char *)__UNCONST(uuid);
53 params[1].Type = ACPI_TYPE_INTEGER;
54 params[1].Integer.Value = rev;
55 params[2].Type = ACPI_TYPE_INTEGER;
56 params[2].Integer.Value = func;
57 if (argv4 != NULL) {
58 params[3] = *argv4;
59 } else {
60 params[3].Type = ACPI_TYPE_PACKAGE;
61 params[3].Package.Count = 0;
62 params[3].Package.Elements = NULL;
63 }
64
65 buf.Pointer = NULL;
66 buf.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
67
68 rv = AcpiEvaluateObject(handle, "_DSM", &arg, &buf);
69 if (ACPI_SUCCESS(rv))
70 return (ACPI_OBJECT *)buf.Pointer;
71 return NULL;
72 }
73
74 static inline ACPI_OBJECT *
75 acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const guid_t *uuid, int rev,
76 int func, ACPI_OBJECT *argv4, ACPI_OBJECT_TYPE type)
77 {
78 ACPI_OBJECT *obj;
79
80 obj = acpi_evaluate_dsm(handle, uuid, rev, func, argv4);
81 if (obj != NULL && obj->Type != type) {
82 ACPI_FREE(obj);
83 obj = NULL;
84 }
85 return obj;
86 }
87
88 #define ACPI_INIT_DSM_ARGV4(cnt, eles) \
89 { \
90 .Package.Type = ACPI_TYPE_PACKAGE, \
91 .Package.Count = (cnt), \
92 .Package.Elements = (eles) \
93 }
94
95 static bool
96 acpi_check_dsm(ACPI_HANDLE handle, const guid_t *uuid, int rev, uint64_t funcs)
97 {
98 ACPI_OBJECT *obj;
99 uint64_t mask = 0;
100 int i;
101
102 if (funcs == 0)
103 return false;
104
105 obj = acpi_evaluate_dsm(handle, uuid, rev, 0, NULL);
106 if (obj == NULL)
107 return false;
108
109 if (obj->Type == ACPI_TYPE_INTEGER)
110 mask = obj->Integer.Value;
111 else if (obj->Type == ACPI_TYPE_BUFFER)
112 for (i = 0; i < obj->Buffer.Length && i < 8; i++)
113 mask |= (uint64_t)obj->Buffer.Pointer[i] << (i * 8);
114 ACPI_FREE(obj);
115
116 if ((mask & 0x1) == 0x1 && (mask & funcs) == funcs)
117 return true;
118 return false;
119 }
120 #endif
121
122 #define INTEL_DSM_REVISION_ID 1 /* For Calpella anyway... */
123 #define INTEL_DSM_FN_PLATFORM_MUX_INFO 1 /* No args */
124
125 static const guid_t intel_dsm_guid =
126 GUID_INIT(0x7ed873d3, 0xc2d0, 0x4e4f,
127 0xa8, 0x54, 0x0f, 0x13, 0x17, 0xb0, 0x1c, 0x2c);
128
129 static const char *intel_dsm_port_name(u8 id)
130 {
131 switch (id) {
132 case 0:
133 return "Reserved";
134 case 1:
135 return "Analog VGA";
136 case 2:
137 return "LVDS";
138 case 3:
139 return "Reserved";
140 case 4:
141 return "HDMI/DVI_B";
142 case 5:
143 return "HDMI/DVI_C";
144 case 6:
145 return "HDMI/DVI_D";
146 case 7:
147 return "DisplayPort_A";
148 case 8:
149 return "DisplayPort_B";
150 case 9:
151 return "DisplayPort_C";
152 case 0xa:
153 return "DisplayPort_D";
154 case 0xb:
155 case 0xc:
156 case 0xd:
157 return "Reserved";
158 case 0xe:
159 return "WiDi";
160 default:
161 return "bad type";
162 }
163 }
164
165 static const char *intel_dsm_mux_type(u8 type)
166 {
167 switch (type) {
168 case 0:
169 return "unknown";
170 case 1:
171 return "No MUX, iGPU only";
172 case 2:
173 return "No MUX, dGPU only";
174 case 3:
175 return "MUXed between iGPU and dGPU";
176 default:
177 return "bad type";
178 }
179 }
180
181 static void intel_dsm_platform_mux_info(acpi_handle dhandle)
182 {
183 int i;
184 #ifdef __NetBSD__
185 ACPI_OBJECT *pkg, *connector_count;
186 #else
187 union acpi_object *pkg, *connector_count;
188 #endif
189
190 pkg = acpi_evaluate_dsm_typed(dhandle, &intel_dsm_guid,
191 INTEL_DSM_REVISION_ID, INTEL_DSM_FN_PLATFORM_MUX_INFO,
192 NULL, ACPI_TYPE_PACKAGE);
193 if (!pkg) {
194 DRM_DEBUG_DRIVER("failed to evaluate _DSM\n");
195 return;
196 }
197
198 connector_count = &pkg->package.elements[0];
199 DRM_DEBUG_DRIVER("MUX info connectors: %lld\n",
200 (unsigned long long)connector_count->integer.value);
201 for (i = 1; i < pkg->package.count; i++) {
202 #ifdef __NetBSD__
203 ACPI_OBJECT *obj = &pkg->package.elements[i];
204 ACPI_OBJECT *connector_id = &obj->package.elements[0];
205 ACPI_OBJECT *info = &obj->package.elements[1];
206 #else
207 union acpi_object *obj = &pkg->package.elements[i];
208 union acpi_object *connector_id = &obj->package.elements[0];
209 union acpi_object *info = &obj->package.elements[1];
210 #endif
211 DRM_DEBUG_DRIVER("Connector id: 0x%016llx\n",
212 (unsigned long long)connector_id->integer.value);
213 DRM_DEBUG_DRIVER(" port id: %s\n",
214 intel_dsm_port_name(info->buffer.pointer[0]));
215 DRM_DEBUG_DRIVER(" display mux info: %s\n",
216 intel_dsm_mux_type(info->buffer.pointer[1]));
217 DRM_DEBUG_DRIVER(" aux/dc mux info: %s\n",
218 intel_dsm_mux_type(info->buffer.pointer[2]));
219 DRM_DEBUG_DRIVER(" hpd mux info: %s\n",
220 intel_dsm_mux_type(info->buffer.pointer[3]));
221 }
222
223 ACPI_FREE(pkg);
224 }
225
226 #ifdef __NetBSD__
227 static ACPI_HANDLE intel_dsm_pci_probe(ACPI_HANDLE dhandle)
228 #else
229 static acpi_handle intel_dsm_pci_probe(struct pci_dev *pdev)
230 #endif
231 {
232 #ifndef __NetBSD__
233 acpi_handle dhandle;
234
235 dhandle = ACPI_HANDLE(&pdev->dev);
236 if (!dhandle)
237 return NULL;
238 #endif
239
240 if (!acpi_check_dsm(dhandle, &intel_dsm_guid, INTEL_DSM_REVISION_ID,
241 1 << INTEL_DSM_FN_PLATFORM_MUX_INFO)) {
242 DRM_DEBUG_KMS("no _DSM method for intel device\n");
243 return NULL;
244 }
245
246 intel_dsm_platform_mux_info(dhandle);
247
248 return dhandle;
249 }
250
251 #ifdef __NetBSD__
252
253 static int vga_count;
254 static ACPI_HANDLE intel_dsm_handle;
255
256 /* XXX from sys/dev/pci/vga_pcivar.h */
257 #define DEVICE_IS_VGA_PCI(class, id) \
258 (((PCI_CLASS(class) == PCI_CLASS_DISPLAY && \
259 PCI_SUBCLASS(class) == PCI_SUBCLASS_DISPLAY_VGA) || \
260 (PCI_CLASS(class) == PCI_CLASS_PREHISTORIC && \
261 PCI_SUBCLASS(class) == PCI_SUBCLASS_PREHISTORIC_VGA)) ? 1 : 0)
262
263 static int
264 intel_dsm_vga_match(const struct pci_attach_args *pa)
265 {
266
267 if (!DEVICE_IS_VGA_PCI(pa->pa_class, pa->pa_id))
268 return 0;
269
270 vga_count++;
271 struct acpi_devnode *node = acpi_pcidev_find(0 /*XXX segment*/,
272 pa->pa_bus, pa->pa_device, pa->pa_function);
273 if (node != NULL && intel_dsm_handle == NULL)
274 intel_dsm_handle = intel_dsm_pci_probe(node->ad_handle);
275 return 0;
276 }
277
278 static bool intel_dsm_detect(struct drm_device *dev)
279 {
280 char acpi_method_name[255] = { 0 };
281
282 vga_count = 0;
283 pci_find_device(&dev->pdev->pd_pa, intel_dsm_vga_match);
284
285 if (vga_count == 2 && intel_dsm_handle) {
286 const char *name = acpi_name(intel_dsm_handle);
287 strlcpy(acpi_method_name, name, sizeof(acpi_method_name));
288 DRM_DEBUG_DRIVER("VGA switcheroo: detected DSM switching method %s handle\n",
289 acpi_method_name);
290 return true;
291 }
292
293 return false;
294 }
295 #else
296 static bool intel_dsm_detect(void)
297 {
298 acpi_handle dhandle = NULL;
299 char acpi_method_name[255] = { 0 };
300 struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
301 struct pci_dev *pdev = NULL;
302 int vga_count = 0;
303
304 while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
305 vga_count++;
306 dhandle = intel_dsm_pci_probe(pdev) ?: dhandle;
307 }
308
309 if (vga_count == 2 && dhandle) {
310 acpi_get_name(dhandle, ACPI_FULL_PATHNAME, &buffer);
311 DRM_DEBUG_DRIVER("vga_switcheroo: detected DSM switching method %s handle\n",
312 acpi_method_name);
313 return true;
314 }
315
316 return false;
317 }
318 #endif
319
320 #ifdef __NetBSD__
321 void intel_register_dsm_handler(struct drm_i915_private *i915)
322 {
323 if (!intel_dsm_detect(&i915->drm))
324 return;
325 }
326 #else
327 void intel_register_dsm_handler(void)
328 {
329 if (!intel_dsm_detect())
330 return;
331 }
332 #endif
333
334 void intel_unregister_dsm_handler(void)
335 {
336 }
337