1/*	$NetBSD: intel_acpi.c,v 1.8 2022/02/27 14:22:42 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.8 2022/02/27 14:22:42 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
23ACPI_MODULE_NAME("acpi_intel_brightness")
24
25#include <dev/acpi/acpi_pci.h>
26
27#include <linux/nbsd-namespace-acpi.h>
28#endif
29
30#define INTEL_DSM_REVISION_ID 1 /* For Calpella anyway... */
31#define INTEL_DSM_FN_PLATFORM_MUX_INFO 1 /* No args */
32
33static const guid_t intel_dsm_guid =
34	GUID_INIT(0x7ed873d3, 0xc2d0, 0x4e4f,
35		  0xa8, 0x54, 0x0f, 0x13, 0x17, 0xb0, 0x1c, 0x2c);
36
37static const char *intel_dsm_port_name(u8 id)
38{
39	switch (id) {
40	case 0:
41		return "Reserved";
42	case 1:
43		return "Analog VGA";
44	case 2:
45		return "LVDS";
46	case 3:
47		return "Reserved";
48	case 4:
49		return "HDMI/DVI_B";
50	case 5:
51		return "HDMI/DVI_C";
52	case 6:
53		return "HDMI/DVI_D";
54	case 7:
55		return "DisplayPort_A";
56	case 8:
57		return "DisplayPort_B";
58	case 9:
59		return "DisplayPort_C";
60	case 0xa:
61		return "DisplayPort_D";
62	case 0xb:
63	case 0xc:
64	case 0xd:
65		return "Reserved";
66	case 0xe:
67		return "WiDi";
68	default:
69		return "bad type";
70	}
71}
72
73static const char *intel_dsm_mux_type(u8 type)
74{
75	switch (type) {
76	case 0:
77		return "unknown";
78	case 1:
79		return "No MUX, iGPU only";
80	case 2:
81		return "No MUX, dGPU only";
82	case 3:
83		return "MUXed between iGPU and dGPU";
84	default:
85		return "bad type";
86	}
87}
88
89static void intel_dsm_platform_mux_info(acpi_handle dhandle)
90{
91	int i;
92	union acpi_object *pkg, *connector_count;
93
94	pkg = acpi_evaluate_dsm_typed(dhandle, &intel_dsm_guid,
95			INTEL_DSM_REVISION_ID, INTEL_DSM_FN_PLATFORM_MUX_INFO,
96			NULL, ACPI_TYPE_PACKAGE);
97	if (!pkg) {
98		DRM_DEBUG_DRIVER("failed to evaluate _DSM\n");
99		return;
100	}
101
102	connector_count = &pkg->package.elements[0];
103	DRM_DEBUG_DRIVER("MUX info connectors: %lld\n",
104		  (unsigned long long)connector_count->integer.value);
105	for (i = 1; i < pkg->package.count; i++) {
106		union acpi_object *obj = &pkg->package.elements[i];
107		union acpi_object *connector_id = &obj->package.elements[0];
108		union acpi_object *info = &obj->package.elements[1];
109		DRM_DEBUG_DRIVER("Connector id: 0x%016llx\n",
110			  (unsigned long long)connector_id->integer.value);
111		DRM_DEBUG_DRIVER("  port id: %s\n",
112		       intel_dsm_port_name(info->buffer.pointer[0]));
113		DRM_DEBUG_DRIVER("  display mux info: %s\n",
114		       intel_dsm_mux_type(info->buffer.pointer[1]));
115		DRM_DEBUG_DRIVER("  aux/dc mux info: %s\n",
116		       intel_dsm_mux_type(info->buffer.pointer[2]));
117		DRM_DEBUG_DRIVER("  hpd mux info: %s\n",
118		       intel_dsm_mux_type(info->buffer.pointer[3]));
119	}
120
121	ACPI_FREE(pkg);
122}
123
124#ifdef __NetBSD__
125static ACPI_HANDLE intel_dsm_pci_probe(ACPI_HANDLE dhandle)
126#else
127static acpi_handle intel_dsm_pci_probe(struct pci_dev *pdev)
128#endif
129{
130#ifndef __NetBSD__
131	acpi_handle dhandle;
132
133	dhandle = ACPI_HANDLE(&pdev->dev);
134	if (!dhandle)
135		return NULL;
136#endif
137
138	if (!acpi_check_dsm(dhandle, &intel_dsm_guid, INTEL_DSM_REVISION_ID,
139			    1 << INTEL_DSM_FN_PLATFORM_MUX_INFO)) {
140		DRM_DEBUG_KMS("no _DSM method for intel device\n");
141		return NULL;
142	}
143
144	intel_dsm_platform_mux_info(dhandle);
145
146	return dhandle;
147}
148
149#ifdef __NetBSD__
150
151static int vga_count;
152static ACPI_HANDLE intel_dsm_handle;
153
154/* XXX from sys/dev/pci/vga_pcivar.h */
155#define	DEVICE_IS_VGA_PCI(class, id)					\
156	    (((PCI_CLASS(class) == PCI_CLASS_DISPLAY &&			\
157	      PCI_SUBCLASS(class) == PCI_SUBCLASS_DISPLAY_VGA) ||	\
158	     (PCI_CLASS(class) == PCI_CLASS_PREHISTORIC &&		\
159	      PCI_SUBCLASS(class) == PCI_SUBCLASS_PREHISTORIC_VGA)) ? 1 : 0)
160
161static int
162intel_dsm_vga_match(const struct pci_attach_args *pa)
163{
164
165	if (!DEVICE_IS_VGA_PCI(pa->pa_class, pa->pa_id))
166		return 0;
167
168	vga_count++;
169	struct acpi_devnode *node =
170	    acpi_pcidev_find(pci_get_segment(pa->pa_pc),
171		pa->pa_bus, pa->pa_device, pa->pa_function);
172	if (node != NULL && intel_dsm_handle == NULL)
173		intel_dsm_handle = intel_dsm_pci_probe(node->ad_handle);
174	return 0;
175}
176
177static bool intel_dsm_detect(struct drm_device *dev)
178{
179	char acpi_method_name[255] = { 0 };
180
181	vga_count = 0;
182	pci_find_device(&dev->pdev->pd_pa, intel_dsm_vga_match);
183
184	if (vga_count == 2 && intel_dsm_handle) {
185		const char *name = acpi_name(intel_dsm_handle);
186		strlcpy(acpi_method_name, name, sizeof(acpi_method_name));
187		DRM_DEBUG_DRIVER("VGA switcheroo: detected DSM switching method %s handle\n",
188				 acpi_method_name);
189		return true;
190	}
191
192	return false;
193}
194#else
195static bool intel_dsm_detect(void)
196{
197	acpi_handle dhandle = NULL;
198	char acpi_method_name[255] = { 0 };
199	struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
200	struct pci_dev *pdev = NULL;
201	int vga_count = 0;
202
203	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
204		vga_count++;
205		dhandle = intel_dsm_pci_probe(pdev) ?: dhandle;
206	}
207
208	if (vga_count == 2 && dhandle) {
209		acpi_get_name(dhandle, ACPI_FULL_PATHNAME, &buffer);
210		DRM_DEBUG_DRIVER("vga_switcheroo: detected DSM switching method %s handle\n",
211				 acpi_method_name);
212		return true;
213	}
214
215	return false;
216}
217#endif
218
219#ifdef __NetBSD__
220void intel_register_dsm_handler(struct drm_i915_private *i915)
221{
222	if (!intel_dsm_detect(&i915->drm))
223		return;
224}
225#else
226void intel_register_dsm_handler(void)
227{
228	if (!intel_dsm_detect())
229		return;
230}
231#endif
232
233void intel_unregister_dsm_handler(void)
234{
235}
236