intel_acpi.c revision 296548
148734Siwasaki/*
248734Siwasaki * Intel ACPI functions
350472Speter *
448734Siwasaki * _DSM related code stolen from nouveau_acpi.c.
548734Siwasaki */
648734Siwasaki
748734Siwasaki#include <sys/cdefs.h>
848734Siwasaki__FBSDID("$FreeBSD: head/sys/dev/drm2/i915/intel_acpi.c 296548 2016-03-08 20:33:02Z dumbbell $");
948734Siwasaki
1048734Siwasaki#include <dev/drm2/drmP.h>
1148734Siwasaki#include <dev/drm2/i915/i915_drv.h>
1248734Siwasaki#include <contrib/dev/acpica/include/acpi.h>
1348734Siwasaki#include <contrib/dev/acpica/include/accommon.h>
1448734Siwasaki#include <dev/acpica/acpivar.h>
1548734Siwasaki
1648734Siwasaki#define INTEL_DSM_REVISION_ID 1 /* For Calpella anyway... */
1748734Siwasaki
1848734Siwasaki#define INTEL_DSM_FN_SUPPORTED_FUNCTIONS 0 /* No args */
1948734Siwasaki#define INTEL_DSM_FN_PLATFORM_MUX_INFO 1 /* No args */
2048734Siwasaki
2148734Siwasakistatic struct intel_dsm_priv {
2248734Siwasaki	ACPI_HANDLE dhandle;
2348734Siwasaki} intel_dsm_priv;
2448734Siwasaki
2548734Siwasakistatic const u8 intel_dsm_guid[] = {
2648734Siwasaki	0xd3, 0x73, 0xd8, 0x7e,
2748734Siwasaki	0xd0, 0xc2,
2848734Siwasaki	0x4f, 0x4e,
2948734Siwasaki	0xa8, 0x54,
3048734Siwasaki	0x0f, 0x13, 0x17, 0xb0, 0x1c, 0x2c
3148734Siwasaki};
3248734Siwasaki
3348734Siwasakistatic int intel_dsm(ACPI_HANDLE handle, int func, int arg)
3448734Siwasaki{
3548734Siwasaki	ACPI_BUFFER output = { ACPI_ALLOCATE_BUFFER, NULL };
3648734Siwasaki	ACPI_OBJECT_LIST input;
3748734Siwasaki	ACPI_OBJECT params[4];
3848734Siwasaki	ACPI_OBJECT *obj;
39	u32 result;
40	int ret = 0;
41
42	input.Count = 4;
43	input.Pointer = params;
44	params[0].Type = ACPI_TYPE_BUFFER;
45	params[0].Buffer.Length = sizeof(intel_dsm_guid);
46	params[0].Buffer.Pointer = __DECONST(char *, intel_dsm_guid);
47	params[1].Type = ACPI_TYPE_INTEGER;
48	params[1].Integer.Value = INTEL_DSM_REVISION_ID;
49	params[2].Type = ACPI_TYPE_INTEGER;
50	params[2].Integer.Value = func;
51	params[3].Type = ACPI_TYPE_INTEGER;
52	params[3].Integer.Value = arg;
53
54	ret = AcpiEvaluateObject(handle, "_DSM", &input, &output);
55	if (ret) {
56		DRM_DEBUG_DRIVER("failed to evaluate _DSM: %d\n", ret);
57		return ret;
58	}
59
60	obj = (ACPI_OBJECT *)output.Pointer;
61
62	result = 0;
63	switch (obj->Type) {
64	case ACPI_TYPE_INTEGER:
65		result = obj->Integer.Value;
66		break;
67
68	case ACPI_TYPE_BUFFER:
69		if (obj->Buffer.Length == 4) {
70			result = (obj->Buffer.Pointer[0] |
71				(obj->Buffer.Pointer[1] <<  8) |
72				(obj->Buffer.Pointer[2] << 16) |
73				(obj->Buffer.Pointer[3] << 24));
74			break;
75		}
76	default:
77		ret = -EINVAL;
78		break;
79	}
80	if (result == 0x80000002)
81		ret = -ENODEV;
82
83	AcpiOsFree(output.Pointer);
84	return ret;
85}
86
87static char *intel_dsm_port_name(u8 id)
88{
89	switch (id) {
90	case 0:
91		return "Reserved";
92	case 1:
93		return "Analog VGA";
94	case 2:
95		return "LVDS";
96	case 3:
97		return "Reserved";
98	case 4:
99		return "HDMI/DVI_B";
100	case 5:
101		return "HDMI/DVI_C";
102	case 6:
103		return "HDMI/DVI_D";
104	case 7:
105		return "DisplayPort_A";
106	case 8:
107		return "DisplayPort_B";
108	case 9:
109		return "DisplayPort_C";
110	case 0xa:
111		return "DisplayPort_D";
112	case 0xb:
113	case 0xc:
114	case 0xd:
115		return "Reserved";
116	case 0xe:
117		return "WiDi";
118	default:
119		return "bad type";
120	}
121}
122
123static char *intel_dsm_mux_type(u8 type)
124{
125	switch (type) {
126	case 0:
127		return "unknown";
128	case 1:
129		return "No MUX, iGPU only";
130	case 2:
131		return "No MUX, dGPU only";
132	case 3:
133		return "MUXed between iGPU and dGPU";
134	default:
135		return "bad type";
136	}
137}
138
139static void intel_dsm_platform_mux_info(void)
140{
141	ACPI_BUFFER output = { ACPI_ALLOCATE_BUFFER, NULL };
142	ACPI_OBJECT_LIST input;
143	ACPI_OBJECT params[4];
144	ACPI_OBJECT *pkg;
145	int i, ret;
146
147	input.Count = 4;
148	input.Pointer = params;
149	params[0].Type = ACPI_TYPE_BUFFER;
150	params[0].Buffer.Length = sizeof(intel_dsm_guid);
151	params[0].Buffer.Pointer = __DECONST(char *, intel_dsm_guid);
152	params[1].Type = ACPI_TYPE_INTEGER;
153	params[1].Integer.Value = INTEL_DSM_REVISION_ID;
154	params[2].Type = ACPI_TYPE_INTEGER;
155	params[2].Integer.Value = INTEL_DSM_FN_PLATFORM_MUX_INFO;
156	params[3].Type = ACPI_TYPE_INTEGER;
157	params[3].Integer.Value = 0;
158
159	ret = AcpiEvaluateObject(intel_dsm_priv.dhandle, "_DSM", &input,
160				   &output);
161	if (ret) {
162		DRM_DEBUG_DRIVER("failed to evaluate _DSM: %d\n", ret);
163		goto out;
164	}
165
166	pkg = (ACPI_OBJECT *)output.Pointer;
167
168	if (pkg->Type == ACPI_TYPE_PACKAGE) {
169		ACPI_OBJECT *connector_count = &pkg->Package.Elements[0];
170		DRM_DEBUG_DRIVER("MUX info connectors: %lld\n",
171			  (unsigned long long)connector_count->Integer.Value);
172		for (i = 1; i < pkg->Package.Count; i++) {
173			ACPI_OBJECT *obj = &pkg->Package.Elements[i];
174			ACPI_OBJECT *connector_id =
175				&obj->Package.Elements[0];
176			ACPI_OBJECT *info = &obj->Package.Elements[1];
177			DRM_DEBUG_DRIVER("Connector id: 0x%016llx\n",
178				  (unsigned long long)connector_id->Integer.Value);
179			DRM_DEBUG_DRIVER("  port id: %s\n",
180			       intel_dsm_port_name(info->Buffer.Pointer[0]));
181			DRM_DEBUG_DRIVER("  display mux info: %s\n",
182			       intel_dsm_mux_type(info->Buffer.Pointer[1]));
183			DRM_DEBUG_DRIVER("  aux/dc mux info: %s\n",
184			       intel_dsm_mux_type(info->Buffer.Pointer[2]));
185			DRM_DEBUG_DRIVER("  hpd mux info: %s\n",
186			       intel_dsm_mux_type(info->Buffer.Pointer[3]));
187		}
188	}
189
190out:
191	AcpiOsFree(output.Pointer);
192}
193
194static bool intel_dsm_pci_probe(device_t dev)
195{
196	ACPI_HANDLE dhandle, intel_handle;
197	ACPI_STATUS status;
198	int ret;
199
200	dhandle = acpi_get_handle(dev);
201	if (!dhandle)
202		return false;
203
204	status = AcpiGetHandle(dhandle, "_DSM", &intel_handle);
205	if (ACPI_FAILURE(status)) {
206		DRM_DEBUG_KMS("no _DSM method for intel device\n");
207		return false;
208	}
209
210	ret = intel_dsm(dhandle, INTEL_DSM_FN_SUPPORTED_FUNCTIONS, 0);
211	if (ret < 0) {
212		DRM_DEBUG_KMS("failed to get supported _DSM functions\n");
213		return false;
214	}
215
216	intel_dsm_priv.dhandle = dhandle;
217
218	intel_dsm_platform_mux_info();
219	return true;
220}
221
222static bool intel_dsm_detect(void)
223{
224	char acpi_method_name[255] = { 0 };
225	ACPI_BUFFER buffer = {sizeof(acpi_method_name), acpi_method_name};
226	device_t dev = NULL;
227	bool has_dsm = false;
228	int vga_count = 0;
229
230#ifdef FREEBSD_WIP
231	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
232#endif /* FREEBSD_WIP */
233	if ((dev = pci_find_class(PCIC_DISPLAY, PCIS_DISPLAY_VGA)) != NULL) {
234		vga_count++;
235		has_dsm |= intel_dsm_pci_probe(dev);
236	}
237
238	if (vga_count == 2 && has_dsm) {
239		AcpiGetName(intel_dsm_priv.dhandle, ACPI_FULL_PATHNAME, &buffer);
240		DRM_DEBUG_DRIVER("VGA switcheroo: detected DSM switching method %s handle\n",
241				 acpi_method_name);
242		return true;
243	}
244
245	return false;
246}
247
248void intel_register_dsm_handler(void)
249{
250	if (!intel_dsm_detect())
251		return;
252}
253
254void intel_unregister_dsm_handler(void)
255{
256}
257