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