1296548Sdumbbell/*
2296548Sdumbbell * Intel ACPI functions
3296548Sdumbbell *
4296548Sdumbbell * _DSM related code stolen from nouveau_acpi.c.
5296548Sdumbbell */
6296548Sdumbbell
7296548Sdumbbell#include <sys/cdefs.h>
8296548Sdumbbell__FBSDID("$FreeBSD$");
9296548Sdumbbell
10296548Sdumbbell#include <dev/drm2/drmP.h>
11296548Sdumbbell#include <dev/drm2/i915/i915_drv.h>
12296548Sdumbbell#include <contrib/dev/acpica/include/acpi.h>
13296548Sdumbbell#include <contrib/dev/acpica/include/accommon.h>
14296548Sdumbbell#include <dev/acpica/acpivar.h>
15296548Sdumbbell
16296548Sdumbbell#define INTEL_DSM_REVISION_ID 1 /* For Calpella anyway... */
17296548Sdumbbell
18296548Sdumbbell#define INTEL_DSM_FN_SUPPORTED_FUNCTIONS 0 /* No args */
19296548Sdumbbell#define INTEL_DSM_FN_PLATFORM_MUX_INFO 1 /* No args */
20296548Sdumbbell
21296548Sdumbbellstatic struct intel_dsm_priv {
22296548Sdumbbell	ACPI_HANDLE dhandle;
23296548Sdumbbell} intel_dsm_priv;
24296548Sdumbbell
25296548Sdumbbellstatic const u8 intel_dsm_guid[] = {
26296548Sdumbbell	0xd3, 0x73, 0xd8, 0x7e,
27296548Sdumbbell	0xd0, 0xc2,
28296548Sdumbbell	0x4f, 0x4e,
29296548Sdumbbell	0xa8, 0x54,
30296548Sdumbbell	0x0f, 0x13, 0x17, 0xb0, 0x1c, 0x2c
31296548Sdumbbell};
32296548Sdumbbell
33296548Sdumbbellstatic int intel_dsm(ACPI_HANDLE handle, int func, int arg)
34296548Sdumbbell{
35296548Sdumbbell	ACPI_BUFFER output = { ACPI_ALLOCATE_BUFFER, NULL };
36296548Sdumbbell	ACPI_OBJECT_LIST input;
37296548Sdumbbell	ACPI_OBJECT params[4];
38296548Sdumbbell	ACPI_OBJECT *obj;
39296548Sdumbbell	u32 result;
40296548Sdumbbell	int ret = 0;
41296548Sdumbbell
42296548Sdumbbell	input.Count = 4;
43296548Sdumbbell	input.Pointer = params;
44296548Sdumbbell	params[0].Type = ACPI_TYPE_BUFFER;
45296548Sdumbbell	params[0].Buffer.Length = sizeof(intel_dsm_guid);
46296548Sdumbbell	params[0].Buffer.Pointer = __DECONST(char *, intel_dsm_guid);
47296548Sdumbbell	params[1].Type = ACPI_TYPE_INTEGER;
48296548Sdumbbell	params[1].Integer.Value = INTEL_DSM_REVISION_ID;
49296548Sdumbbell	params[2].Type = ACPI_TYPE_INTEGER;
50296548Sdumbbell	params[2].Integer.Value = func;
51296548Sdumbbell	params[3].Type = ACPI_TYPE_INTEGER;
52296548Sdumbbell	params[3].Integer.Value = arg;
53296548Sdumbbell
54296548Sdumbbell	ret = AcpiEvaluateObject(handle, "_DSM", &input, &output);
55296548Sdumbbell	if (ret) {
56296548Sdumbbell		DRM_DEBUG_DRIVER("failed to evaluate _DSM: %d\n", ret);
57296548Sdumbbell		return ret;
58296548Sdumbbell	}
59296548Sdumbbell
60296548Sdumbbell	obj = (ACPI_OBJECT *)output.Pointer;
61296548Sdumbbell
62296548Sdumbbell	result = 0;
63296548Sdumbbell	switch (obj->Type) {
64296548Sdumbbell	case ACPI_TYPE_INTEGER:
65296548Sdumbbell		result = obj->Integer.Value;
66296548Sdumbbell		break;
67296548Sdumbbell
68296548Sdumbbell	case ACPI_TYPE_BUFFER:
69296548Sdumbbell		if (obj->Buffer.Length == 4) {
70296548Sdumbbell			result = (obj->Buffer.Pointer[0] |
71296548Sdumbbell				(obj->Buffer.Pointer[1] <<  8) |
72296548Sdumbbell				(obj->Buffer.Pointer[2] << 16) |
73296548Sdumbbell				(obj->Buffer.Pointer[3] << 24));
74296548Sdumbbell			break;
75296548Sdumbbell		}
76296548Sdumbbell	default:
77296548Sdumbbell		ret = -EINVAL;
78296548Sdumbbell		break;
79296548Sdumbbell	}
80296548Sdumbbell	if (result == 0x80000002)
81296548Sdumbbell		ret = -ENODEV;
82296548Sdumbbell
83296548Sdumbbell	AcpiOsFree(output.Pointer);
84296548Sdumbbell	return ret;
85296548Sdumbbell}
86296548Sdumbbell
87296548Sdumbbellstatic char *intel_dsm_port_name(u8 id)
88296548Sdumbbell{
89296548Sdumbbell	switch (id) {
90296548Sdumbbell	case 0:
91296548Sdumbbell		return "Reserved";
92296548Sdumbbell	case 1:
93296548Sdumbbell		return "Analog VGA";
94296548Sdumbbell	case 2:
95296548Sdumbbell		return "LVDS";
96296548Sdumbbell	case 3:
97296548Sdumbbell		return "Reserved";
98296548Sdumbbell	case 4:
99296548Sdumbbell		return "HDMI/DVI_B";
100296548Sdumbbell	case 5:
101296548Sdumbbell		return "HDMI/DVI_C";
102296548Sdumbbell	case 6:
103296548Sdumbbell		return "HDMI/DVI_D";
104296548Sdumbbell	case 7:
105296548Sdumbbell		return "DisplayPort_A";
106296548Sdumbbell	case 8:
107296548Sdumbbell		return "DisplayPort_B";
108296548Sdumbbell	case 9:
109296548Sdumbbell		return "DisplayPort_C";
110296548Sdumbbell	case 0xa:
111296548Sdumbbell		return "DisplayPort_D";
112296548Sdumbbell	case 0xb:
113296548Sdumbbell	case 0xc:
114296548Sdumbbell	case 0xd:
115296548Sdumbbell		return "Reserved";
116296548Sdumbbell	case 0xe:
117296548Sdumbbell		return "WiDi";
118296548Sdumbbell	default:
119296548Sdumbbell		return "bad type";
120296548Sdumbbell	}
121296548Sdumbbell}
122296548Sdumbbell
123296548Sdumbbellstatic char *intel_dsm_mux_type(u8 type)
124296548Sdumbbell{
125296548Sdumbbell	switch (type) {
126296548Sdumbbell	case 0:
127296548Sdumbbell		return "unknown";
128296548Sdumbbell	case 1:
129296548Sdumbbell		return "No MUX, iGPU only";
130296548Sdumbbell	case 2:
131296548Sdumbbell		return "No MUX, dGPU only";
132296548Sdumbbell	case 3:
133296548Sdumbbell		return "MUXed between iGPU and dGPU";
134296548Sdumbbell	default:
135296548Sdumbbell		return "bad type";
136296548Sdumbbell	}
137296548Sdumbbell}
138296548Sdumbbell
139296548Sdumbbellstatic void intel_dsm_platform_mux_info(void)
140296548Sdumbbell{
141296548Sdumbbell	ACPI_BUFFER output = { ACPI_ALLOCATE_BUFFER, NULL };
142296548Sdumbbell	ACPI_OBJECT_LIST input;
143296548Sdumbbell	ACPI_OBJECT params[4];
144296548Sdumbbell	ACPI_OBJECT *pkg;
145296548Sdumbbell	int i, ret;
146296548Sdumbbell
147296548Sdumbbell	input.Count = 4;
148296548Sdumbbell	input.Pointer = params;
149296548Sdumbbell	params[0].Type = ACPI_TYPE_BUFFER;
150296548Sdumbbell	params[0].Buffer.Length = sizeof(intel_dsm_guid);
151296548Sdumbbell	params[0].Buffer.Pointer = __DECONST(char *, intel_dsm_guid);
152296548Sdumbbell	params[1].Type = ACPI_TYPE_INTEGER;
153296548Sdumbbell	params[1].Integer.Value = INTEL_DSM_REVISION_ID;
154296548Sdumbbell	params[2].Type = ACPI_TYPE_INTEGER;
155296548Sdumbbell	params[2].Integer.Value = INTEL_DSM_FN_PLATFORM_MUX_INFO;
156296548Sdumbbell	params[3].Type = ACPI_TYPE_INTEGER;
157296548Sdumbbell	params[3].Integer.Value = 0;
158296548Sdumbbell
159296548Sdumbbell	ret = AcpiEvaluateObject(intel_dsm_priv.dhandle, "_DSM", &input,
160296548Sdumbbell				   &output);
161296548Sdumbbell	if (ret) {
162296548Sdumbbell		DRM_DEBUG_DRIVER("failed to evaluate _DSM: %d\n", ret);
163296548Sdumbbell		goto out;
164296548Sdumbbell	}
165296548Sdumbbell
166296548Sdumbbell	pkg = (ACPI_OBJECT *)output.Pointer;
167296548Sdumbbell
168296548Sdumbbell	if (pkg->Type == ACPI_TYPE_PACKAGE) {
169296548Sdumbbell		ACPI_OBJECT *connector_count = &pkg->Package.Elements[0];
170296548Sdumbbell		DRM_DEBUG_DRIVER("MUX info connectors: %lld\n",
171296548Sdumbbell			  (unsigned long long)connector_count->Integer.Value);
172296548Sdumbbell		for (i = 1; i < pkg->Package.Count; i++) {
173296548Sdumbbell			ACPI_OBJECT *obj = &pkg->Package.Elements[i];
174296548Sdumbbell			ACPI_OBJECT *connector_id =
175296548Sdumbbell				&obj->Package.Elements[0];
176296548Sdumbbell			ACPI_OBJECT *info = &obj->Package.Elements[1];
177296548Sdumbbell			DRM_DEBUG_DRIVER("Connector id: 0x%016llx\n",
178296548Sdumbbell				  (unsigned long long)connector_id->Integer.Value);
179296548Sdumbbell			DRM_DEBUG_DRIVER("  port id: %s\n",
180296548Sdumbbell			       intel_dsm_port_name(info->Buffer.Pointer[0]));
181296548Sdumbbell			DRM_DEBUG_DRIVER("  display mux info: %s\n",
182296548Sdumbbell			       intel_dsm_mux_type(info->Buffer.Pointer[1]));
183296548Sdumbbell			DRM_DEBUG_DRIVER("  aux/dc mux info: %s\n",
184296548Sdumbbell			       intel_dsm_mux_type(info->Buffer.Pointer[2]));
185296548Sdumbbell			DRM_DEBUG_DRIVER("  hpd mux info: %s\n",
186296548Sdumbbell			       intel_dsm_mux_type(info->Buffer.Pointer[3]));
187296548Sdumbbell		}
188296548Sdumbbell	}
189296548Sdumbbell
190296548Sdumbbellout:
191296548Sdumbbell	AcpiOsFree(output.Pointer);
192296548Sdumbbell}
193296548Sdumbbell
194296548Sdumbbellstatic bool intel_dsm_pci_probe(device_t dev)
195296548Sdumbbell{
196296548Sdumbbell	ACPI_HANDLE dhandle, intel_handle;
197296548Sdumbbell	ACPI_STATUS status;
198296548Sdumbbell	int ret;
199296548Sdumbbell
200296548Sdumbbell	dhandle = acpi_get_handle(dev);
201296548Sdumbbell	if (!dhandle)
202296548Sdumbbell		return false;
203296548Sdumbbell
204296548Sdumbbell	status = AcpiGetHandle(dhandle, "_DSM", &intel_handle);
205296548Sdumbbell	if (ACPI_FAILURE(status)) {
206296548Sdumbbell		DRM_DEBUG_KMS("no _DSM method for intel device\n");
207296548Sdumbbell		return false;
208296548Sdumbbell	}
209296548Sdumbbell
210296548Sdumbbell	ret = intel_dsm(dhandle, INTEL_DSM_FN_SUPPORTED_FUNCTIONS, 0);
211296548Sdumbbell	if (ret < 0) {
212296548Sdumbbell		DRM_DEBUG_KMS("failed to get supported _DSM functions\n");
213296548Sdumbbell		return false;
214296548Sdumbbell	}
215296548Sdumbbell
216296548Sdumbbell	intel_dsm_priv.dhandle = dhandle;
217296548Sdumbbell
218296548Sdumbbell	intel_dsm_platform_mux_info();
219296548Sdumbbell	return true;
220296548Sdumbbell}
221296548Sdumbbell
222296548Sdumbbellstatic bool intel_dsm_detect(void)
223296548Sdumbbell{
224296548Sdumbbell	char acpi_method_name[255] = { 0 };
225296548Sdumbbell	ACPI_BUFFER buffer = {sizeof(acpi_method_name), acpi_method_name};
226296548Sdumbbell	device_t dev = NULL;
227296548Sdumbbell	bool has_dsm = false;
228296548Sdumbbell	int vga_count = 0;
229296548Sdumbbell
230296548Sdumbbell#ifdef FREEBSD_WIP
231296548Sdumbbell	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
232296548Sdumbbell#endif /* FREEBSD_WIP */
233296548Sdumbbell	if ((dev = pci_find_class(PCIC_DISPLAY, PCIS_DISPLAY_VGA)) != NULL) {
234296548Sdumbbell		vga_count++;
235296548Sdumbbell		has_dsm |= intel_dsm_pci_probe(dev);
236296548Sdumbbell	}
237296548Sdumbbell
238296548Sdumbbell	if (vga_count == 2 && has_dsm) {
239296548Sdumbbell		AcpiGetName(intel_dsm_priv.dhandle, ACPI_FULL_PATHNAME, &buffer);
240296548Sdumbbell		DRM_DEBUG_DRIVER("VGA switcheroo: detected DSM switching method %s handle\n",
241296548Sdumbbell				 acpi_method_name);
242296548Sdumbbell		return true;
243296548Sdumbbell	}
244296548Sdumbbell
245296548Sdumbbell	return false;
246296548Sdumbbell}
247296548Sdumbbell
248296548Sdumbbellvoid intel_register_dsm_handler(void)
249296548Sdumbbell{
250296548Sdumbbell	if (!intel_dsm_detect())
251296548Sdumbbell		return;
252296548Sdumbbell}
253296548Sdumbbell
254296548Sdumbbellvoid intel_unregister_dsm_handler(void)
255296548Sdumbbell{
256296548Sdumbbell}
257