xref: /linux/drivers/gpu/drm/nouveau/nouveau_acpi.c (revision 612a9aab56a93533e76e3ad91642db7033e03b69)
16ee73861SBen Skeggs #include <linux/pci.h>
26ee73861SBen Skeggs #include <linux/acpi.h>
35a0e3ad6STejun Heo #include <linux/slab.h>
46ee73861SBen Skeggs #include <acpi/acpi_drivers.h>
56ee73861SBen Skeggs #include <acpi/acpi_bus.h>
6a6ed76d7SBen Skeggs #include <acpi/video.h>
78116188fSDave Airlie #include <acpi/acpi.h>
88116188fSDave Airlie #include <linux/mxm-wmi.h>
96ee73861SBen Skeggs 
106a9ee8afSDave Airlie #include <linux/vga_switcheroo.h>
116a9ee8afSDave Airlie 
12*612a9aabSLinus Torvalds #include <drm/drm_edid.h>
13c0077061SBen Skeggs 
14c0077061SBen Skeggs #include "nouveau_drm.h"
15c0077061SBen Skeggs #include "nouveau_acpi.h"
16c0077061SBen Skeggs 
176ee73861SBen Skeggs #define NOUVEAU_DSM_LED 0x02
186ee73861SBen Skeggs #define NOUVEAU_DSM_LED_STATE 0x00
196ee73861SBen Skeggs #define NOUVEAU_DSM_LED_OFF 0x10
206ee73861SBen Skeggs #define NOUVEAU_DSM_LED_STAMINA 0x11
216ee73861SBen Skeggs #define NOUVEAU_DSM_LED_SPEED 0x12
226ee73861SBen Skeggs 
236ee73861SBen Skeggs #define NOUVEAU_DSM_POWER 0x03
246ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_STATE 0x00
256ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_SPEED 0x01
266ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_STAMINA 0x02
276ee73861SBen Skeggs 
289075e85fSPeter Lekensteyn #define NOUVEAU_DSM_OPTIMUS_FN 0x1A
29d099230cSPeter Lekensteyn #define NOUVEAU_DSM_OPTIMUS_ARGS 0x03000001
30d099230cSPeter Lekensteyn 
316a9ee8afSDave Airlie static struct nouveau_dsm_priv {
326a9ee8afSDave Airlie 	bool dsm_detected;
33f19467c5SDave Airlie 	bool optimus_detected;
346a9ee8afSDave Airlie 	acpi_handle dhandle;
35afeb3e11SDave Airlie 	acpi_handle rom_handle;
366a9ee8afSDave Airlie } nouveau_dsm_priv;
376a9ee8afSDave Airlie 
38f19467c5SDave Airlie #define NOUVEAU_DSM_HAS_MUX 0x1
39f19467c5SDave Airlie #define NOUVEAU_DSM_HAS_OPT 0x2
40f19467c5SDave Airlie 
416a9ee8afSDave Airlie static const char nouveau_dsm_muid[] = {
426ee73861SBen Skeggs 	0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
436ee73861SBen Skeggs 	0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4,
446ee73861SBen Skeggs };
456ee73861SBen Skeggs 
46f19467c5SDave Airlie static const char nouveau_op_dsm_muid[] = {
47f19467c5SDave Airlie 	0xF8, 0xD8, 0x86, 0xA4, 0xDA, 0x0B, 0x1B, 0x47,
48f19467c5SDave Airlie 	0xA7, 0x2B, 0x60, 0x42, 0xA6, 0xB5, 0xBE, 0xE0,
49f19467c5SDave Airlie };
50f19467c5SDave Airlie 
51f19467c5SDave Airlie static int nouveau_optimus_dsm(acpi_handle handle, int func, int arg, uint32_t *result)
52f19467c5SDave Airlie {
53f19467c5SDave Airlie 	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
54f19467c5SDave Airlie 	struct acpi_object_list input;
55f19467c5SDave Airlie 	union acpi_object params[4];
56f19467c5SDave Airlie 	union acpi_object *obj;
57d099230cSPeter Lekensteyn 	int i, err;
58d099230cSPeter Lekensteyn 	char args_buff[4];
59f19467c5SDave Airlie 
60f19467c5SDave Airlie 	input.count = 4;
61f19467c5SDave Airlie 	input.pointer = params;
62f19467c5SDave Airlie 	params[0].type = ACPI_TYPE_BUFFER;
63f19467c5SDave Airlie 	params[0].buffer.length = sizeof(nouveau_op_dsm_muid);
64f19467c5SDave Airlie 	params[0].buffer.pointer = (char *)nouveau_op_dsm_muid;
65f19467c5SDave Airlie 	params[1].type = ACPI_TYPE_INTEGER;
66f19467c5SDave Airlie 	params[1].integer.value = 0x00000100;
67f19467c5SDave Airlie 	params[2].type = ACPI_TYPE_INTEGER;
68f19467c5SDave Airlie 	params[2].integer.value = func;
69f19467c5SDave Airlie 	params[3].type = ACPI_TYPE_BUFFER;
70d099230cSPeter Lekensteyn 	params[3].buffer.length = 4;
71d099230cSPeter Lekensteyn 	/* ACPI is little endian, AABBCCDD becomes {DD,CC,BB,AA} */
72d099230cSPeter Lekensteyn 	for (i = 0; i < 4; i++)
73d099230cSPeter Lekensteyn 		args_buff[i] = (arg >> i * 8) & 0xFF;
74d099230cSPeter Lekensteyn 	params[3].buffer.pointer = args_buff;
75f19467c5SDave Airlie 
76f19467c5SDave Airlie 	err = acpi_evaluate_object(handle, "_DSM", &input, &output);
77f19467c5SDave Airlie 	if (err) {
78f19467c5SDave Airlie 		printk(KERN_INFO "failed to evaluate _DSM: %d\n", err);
79f19467c5SDave Airlie 		return err;
80f19467c5SDave Airlie 	}
81f19467c5SDave Airlie 
82f19467c5SDave Airlie 	obj = (union acpi_object *)output.pointer;
83f19467c5SDave Airlie 
84f19467c5SDave Airlie 	if (obj->type == ACPI_TYPE_INTEGER)
85f19467c5SDave Airlie 		if (obj->integer.value == 0x80000002) {
86f19467c5SDave Airlie 			return -ENODEV;
87f19467c5SDave Airlie 		}
88f19467c5SDave Airlie 
89f19467c5SDave Airlie 	if (obj->type == ACPI_TYPE_BUFFER) {
90f19467c5SDave Airlie 		if (obj->buffer.length == 4 && result) {
91f19467c5SDave Airlie 			*result = 0;
92f19467c5SDave Airlie 			*result |= obj->buffer.pointer[0];
93f19467c5SDave Airlie 			*result |= (obj->buffer.pointer[1] << 8);
94f19467c5SDave Airlie 			*result |= (obj->buffer.pointer[2] << 16);
95f19467c5SDave Airlie 			*result |= (obj->buffer.pointer[3] << 24);
96f19467c5SDave Airlie 		}
97f19467c5SDave Airlie 	}
98f19467c5SDave Airlie 
99f19467c5SDave Airlie 	kfree(output.pointer);
100f19467c5SDave Airlie 	return 0;
101f19467c5SDave Airlie }
102f19467c5SDave Airlie 
1036e86e041SFrancisco Jerez static int nouveau_dsm(acpi_handle handle, int func, int arg, uint32_t *result)
1046a9ee8afSDave Airlie {
1056ee73861SBen Skeggs 	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
1066ee73861SBen Skeggs 	struct acpi_object_list input;
1076ee73861SBen Skeggs 	union acpi_object params[4];
1086ee73861SBen Skeggs 	union acpi_object *obj;
1096ee73861SBen Skeggs 	int err;
1106ee73861SBen Skeggs 
1116ee73861SBen Skeggs 	input.count = 4;
1126ee73861SBen Skeggs 	input.pointer = params;
1136ee73861SBen Skeggs 	params[0].type = ACPI_TYPE_BUFFER;
1146a9ee8afSDave Airlie 	params[0].buffer.length = sizeof(nouveau_dsm_muid);
1156a9ee8afSDave Airlie 	params[0].buffer.pointer = (char *)nouveau_dsm_muid;
1166ee73861SBen Skeggs 	params[1].type = ACPI_TYPE_INTEGER;
1176ee73861SBen Skeggs 	params[1].integer.value = 0x00000102;
1186ee73861SBen Skeggs 	params[2].type = ACPI_TYPE_INTEGER;
1196ee73861SBen Skeggs 	params[2].integer.value = func;
1206ee73861SBen Skeggs 	params[3].type = ACPI_TYPE_INTEGER;
1216ee73861SBen Skeggs 	params[3].integer.value = arg;
1226ee73861SBen Skeggs 
1236ee73861SBen Skeggs 	err = acpi_evaluate_object(handle, "_DSM", &input, &output);
1246ee73861SBen Skeggs 	if (err) {
1256a9ee8afSDave Airlie 		printk(KERN_INFO "failed to evaluate _DSM: %d\n", err);
1266ee73861SBen Skeggs 		return err;
1276ee73861SBen Skeggs 	}
1286ee73861SBen Skeggs 
1296ee73861SBen Skeggs 	obj = (union acpi_object *)output.pointer;
1306ee73861SBen Skeggs 
1316ee73861SBen Skeggs 	if (obj->type == ACPI_TYPE_INTEGER)
1326ee73861SBen Skeggs 		if (obj->integer.value == 0x80000002)
1336ee73861SBen Skeggs 			return -ENODEV;
1346ee73861SBen Skeggs 
1356ee73861SBen Skeggs 	if (obj->type == ACPI_TYPE_BUFFER) {
1366ee73861SBen Skeggs 		if (obj->buffer.length == 4 && result) {
1376ee73861SBen Skeggs 			*result = 0;
1386ee73861SBen Skeggs 			*result |= obj->buffer.pointer[0];
1396ee73861SBen Skeggs 			*result |= (obj->buffer.pointer[1] << 8);
1406ee73861SBen Skeggs 			*result |= (obj->buffer.pointer[2] << 16);
1416ee73861SBen Skeggs 			*result |= (obj->buffer.pointer[3] << 24);
1426ee73861SBen Skeggs 		}
1436ee73861SBen Skeggs 	}
1446ee73861SBen Skeggs 
1456ee73861SBen Skeggs 	kfree(output.pointer);
1466ee73861SBen Skeggs 	return 0;
1476ee73861SBen Skeggs }
1486ee73861SBen Skeggs 
1499075e85fSPeter Lekensteyn /* Returns 1 if a DSM function is usable and 0 otherwise */
1509075e85fSPeter Lekensteyn static int nouveau_test_dsm(acpi_handle test_handle,
1519075e85fSPeter Lekensteyn 	int (*dsm_func)(acpi_handle, int, int, uint32_t *),
1529075e85fSPeter Lekensteyn 	int sfnc)
1539075e85fSPeter Lekensteyn {
1549075e85fSPeter Lekensteyn 	u32 result = 0;
1559075e85fSPeter Lekensteyn 
1569075e85fSPeter Lekensteyn 	/* Function 0 returns a Buffer containing available functions. The args
1579075e85fSPeter Lekensteyn 	 * parameter is ignored for function 0, so just put 0 in it */
1589075e85fSPeter Lekensteyn 	if (dsm_func(test_handle, 0, 0, &result))
1599075e85fSPeter Lekensteyn 		return 0;
1609075e85fSPeter Lekensteyn 
1619075e85fSPeter Lekensteyn 	/* ACPI Spec v4 9.14.1: if bit 0 is zero, no function is supported. If
1629075e85fSPeter Lekensteyn 	 * the n-th bit is enabled, function n is supported */
1639075e85fSPeter Lekensteyn 	return result & 1 && result & (1 << sfnc);
1649075e85fSPeter Lekensteyn }
1659075e85fSPeter Lekensteyn 
1666a9ee8afSDave Airlie static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id)
1676ee73861SBen Skeggs {
168000703f4SDave Airlie 	mxm_wmi_call_mxmx(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0);
1698116188fSDave Airlie 	mxm_wmi_call_mxds(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0);
1706a9ee8afSDave Airlie 	return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id, NULL);
1716ee73861SBen Skeggs }
1726ee73861SBen Skeggs 
1736a9ee8afSDave Airlie static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state)
1746a9ee8afSDave Airlie {
1756a9ee8afSDave Airlie 	int arg;
1766a9ee8afSDave Airlie 	if (state == VGA_SWITCHEROO_ON)
1776a9ee8afSDave Airlie 		arg = NOUVEAU_DSM_POWER_SPEED;
1786a9ee8afSDave Airlie 	else
1796a9ee8afSDave Airlie 		arg = NOUVEAU_DSM_POWER_STAMINA;
1806a9ee8afSDave Airlie 	nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg, NULL);
1816ee73861SBen Skeggs 	return 0;
1826ee73861SBen Skeggs }
1836ee73861SBen Skeggs 
1846a9ee8afSDave Airlie static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id)
1856ee73861SBen Skeggs {
186d099230cSPeter Lekensteyn 	/* perhaps the _DSM functions are mutually exclusive, but prepare for
187d099230cSPeter Lekensteyn 	 * the future */
188d099230cSPeter Lekensteyn 	if (!nouveau_dsm_priv.dsm_detected && nouveau_dsm_priv.optimus_detected)
189d099230cSPeter Lekensteyn 		return 0;
1906a9ee8afSDave Airlie 	if (id == VGA_SWITCHEROO_IGD)
191fc5ea29dSDave Airlie 		return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA);
1926a9ee8afSDave Airlie 	else
193fc5ea29dSDave Airlie 		return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_SPEED);
1946a9ee8afSDave Airlie }
1956ee73861SBen Skeggs 
1966a9ee8afSDave Airlie static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id,
1976a9ee8afSDave Airlie 				   enum vga_switcheroo_state state)
1986a9ee8afSDave Airlie {
1996a9ee8afSDave Airlie 	if (id == VGA_SWITCHEROO_IGD)
2006a9ee8afSDave Airlie 		return 0;
2016a9ee8afSDave Airlie 
202d099230cSPeter Lekensteyn 	/* Optimus laptops have the card already disabled in
203d099230cSPeter Lekensteyn 	 * nouveau_switcheroo_set_state */
204d099230cSPeter Lekensteyn 	if (!nouveau_dsm_priv.dsm_detected && nouveau_dsm_priv.optimus_detected)
205d099230cSPeter Lekensteyn 		return 0;
206d099230cSPeter Lekensteyn 
207fc5ea29dSDave Airlie 	return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state);
2086a9ee8afSDave Airlie }
2096a9ee8afSDave Airlie 
2106a9ee8afSDave Airlie static int nouveau_dsm_get_client_id(struct pci_dev *pdev)
2116a9ee8afSDave Airlie {
212d1fbd923SDave Airlie 	/* easy option one - intel vendor ID means Integrated */
213d1fbd923SDave Airlie 	if (pdev->vendor == PCI_VENDOR_ID_INTEL)
2146a9ee8afSDave Airlie 		return VGA_SWITCHEROO_IGD;
215d1fbd923SDave Airlie 
216d1fbd923SDave Airlie 	/* is this device on Bus 0? - this may need improving */
217d1fbd923SDave Airlie 	if (pdev->bus->number == 0)
218d1fbd923SDave Airlie 		return VGA_SWITCHEROO_IGD;
219d1fbd923SDave Airlie 
2206a9ee8afSDave Airlie 	return VGA_SWITCHEROO_DIS;
2216a9ee8afSDave Airlie }
2226a9ee8afSDave Airlie 
2236a9ee8afSDave Airlie static struct vga_switcheroo_handler nouveau_dsm_handler = {
2246a9ee8afSDave Airlie 	.switchto = nouveau_dsm_switchto,
2256a9ee8afSDave Airlie 	.power_state = nouveau_dsm_power_state,
2266a9ee8afSDave Airlie 	.get_client_id = nouveau_dsm_get_client_id,
2276a9ee8afSDave Airlie };
2286a9ee8afSDave Airlie 
229f19467c5SDave Airlie static int nouveau_dsm_pci_probe(struct pci_dev *pdev)
2306a9ee8afSDave Airlie {
2316a9ee8afSDave Airlie 	acpi_handle dhandle, nvidia_handle;
2326a9ee8afSDave Airlie 	acpi_status status;
2339075e85fSPeter Lekensteyn 	int retval = 0;
2346a9ee8afSDave Airlie 
2356a9ee8afSDave Airlie 	dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
2366a9ee8afSDave Airlie 	if (!dhandle)
2376a9ee8afSDave Airlie 		return false;
238afeb3e11SDave Airlie 
2396a9ee8afSDave Airlie 	status = acpi_get_handle(dhandle, "_DSM", &nvidia_handle);
2406a9ee8afSDave Airlie 	if (ACPI_FAILURE(status)) {
2416a9ee8afSDave Airlie 		return false;
2426a9ee8afSDave Airlie 	}
2436a9ee8afSDave Airlie 
2449075e85fSPeter Lekensteyn 	if (nouveau_test_dsm(dhandle, nouveau_dsm, NOUVEAU_DSM_POWER))
245f19467c5SDave Airlie 		retval |= NOUVEAU_DSM_HAS_MUX;
2466ee73861SBen Skeggs 
2479075e85fSPeter Lekensteyn 	if (nouveau_test_dsm(dhandle, nouveau_optimus_dsm,
2489075e85fSPeter Lekensteyn 		NOUVEAU_DSM_OPTIMUS_FN))
249f19467c5SDave Airlie 		retval |= NOUVEAU_DSM_HAS_OPT;
250f19467c5SDave Airlie 
251f19467c5SDave Airlie 	if (retval)
2526a9ee8afSDave Airlie 		nouveau_dsm_priv.dhandle = dhandle;
253f19467c5SDave Airlie 
254f19467c5SDave Airlie 	return retval;
2556ee73861SBen Skeggs }
2566a9ee8afSDave Airlie 
2576a9ee8afSDave Airlie static bool nouveau_dsm_detect(void)
2586a9ee8afSDave Airlie {
2596a9ee8afSDave Airlie 	char acpi_method_name[255] = { 0 };
2606a9ee8afSDave Airlie 	struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
2616a9ee8afSDave Airlie 	struct pci_dev *pdev = NULL;
2626a9ee8afSDave Airlie 	int has_dsm = 0;
263addde4ecSDave Airlie 	int has_optimus = 0;
2646a9ee8afSDave Airlie 	int vga_count = 0;
2658116188fSDave Airlie 	bool guid_valid;
266f19467c5SDave Airlie 	int retval;
267f19467c5SDave Airlie 	bool ret = false;
2688116188fSDave Airlie 
269f19467c5SDave Airlie 	/* lookup the MXM GUID */
2708116188fSDave Airlie 	guid_valid = mxm_wmi_supported();
2718116188fSDave Airlie 
272f19467c5SDave Airlie 	if (guid_valid)
273f19467c5SDave Airlie 		printk("MXM: GUID detected in BIOS\n");
274afeb3e11SDave Airlie 
275f19467c5SDave Airlie 	/* now do DSM detection */
2766a9ee8afSDave Airlie 	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
2776a9ee8afSDave Airlie 		vga_count++;
2786a9ee8afSDave Airlie 
279f19467c5SDave Airlie 		retval = nouveau_dsm_pci_probe(pdev);
280f19467c5SDave Airlie 		if (retval & NOUVEAU_DSM_HAS_MUX)
281f19467c5SDave Airlie 			has_dsm |= 1;
282f19467c5SDave Airlie 		if (retval & NOUVEAU_DSM_HAS_OPT)
283f19467c5SDave Airlie 			has_optimus = 1;
2846a9ee8afSDave Airlie 	}
2856a9ee8afSDave Airlie 
286f19467c5SDave Airlie 	if (vga_count == 2 && has_dsm && guid_valid) {
287d099230cSPeter Lekensteyn 		acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME,
288d099230cSPeter Lekensteyn 			&buffer);
2896a9ee8afSDave Airlie 		printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n",
2906a9ee8afSDave Airlie 			acpi_method_name);
2916a9ee8afSDave Airlie 		nouveau_dsm_priv.dsm_detected = true;
292f19467c5SDave Airlie 		ret = true;
2936a9ee8afSDave Airlie 	}
294f19467c5SDave Airlie 
295d099230cSPeter Lekensteyn 	if (has_optimus == 1) {
296d099230cSPeter Lekensteyn 		acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME,
297d099230cSPeter Lekensteyn 			&buffer);
298d099230cSPeter Lekensteyn 		printk(KERN_INFO "VGA switcheroo: detected Optimus DSM method %s handle\n",
299d099230cSPeter Lekensteyn 			acpi_method_name);
300f19467c5SDave Airlie 		nouveau_dsm_priv.optimus_detected = true;
301d099230cSPeter Lekensteyn 		ret = true;
302d099230cSPeter Lekensteyn 	}
303f19467c5SDave Airlie 
304f19467c5SDave Airlie 	return ret;
3056a9ee8afSDave Airlie }
3066a9ee8afSDave Airlie 
3076a9ee8afSDave Airlie void nouveau_register_dsm_handler(void)
3086a9ee8afSDave Airlie {
3096a9ee8afSDave Airlie 	bool r;
3106a9ee8afSDave Airlie 
3116a9ee8afSDave Airlie 	r = nouveau_dsm_detect();
3126a9ee8afSDave Airlie 	if (!r)
3136a9ee8afSDave Airlie 		return;
3146a9ee8afSDave Airlie 
3156a9ee8afSDave Airlie 	vga_switcheroo_register_handler(&nouveau_dsm_handler);
3166a9ee8afSDave Airlie }
3176a9ee8afSDave Airlie 
318d099230cSPeter Lekensteyn /* Must be called for Optimus models before the card can be turned off */
319d099230cSPeter Lekensteyn void nouveau_switcheroo_optimus_dsm(void)
320d099230cSPeter Lekensteyn {
321d099230cSPeter Lekensteyn 	u32 result = 0;
322d099230cSPeter Lekensteyn 	if (!nouveau_dsm_priv.optimus_detected)
323d099230cSPeter Lekensteyn 		return;
324d099230cSPeter Lekensteyn 
325d099230cSPeter Lekensteyn 	nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_FN,
326d099230cSPeter Lekensteyn 		NOUVEAU_DSM_OPTIMUS_ARGS, &result);
327d099230cSPeter Lekensteyn }
328d099230cSPeter Lekensteyn 
3296a9ee8afSDave Airlie void nouveau_unregister_dsm_handler(void)
3306a9ee8afSDave Airlie {
3312f3787aaSAndreas Heider 	if (nouveau_dsm_priv.optimus_detected || nouveau_dsm_priv.dsm_detected)
3326a9ee8afSDave Airlie 		vga_switcheroo_unregister_handler();
3336a9ee8afSDave Airlie }
334afeb3e11SDave Airlie 
335afeb3e11SDave Airlie /* retrieve the ROM in 4k blocks */
336afeb3e11SDave Airlie static int nouveau_rom_call(acpi_handle rom_handle, uint8_t *bios,
337afeb3e11SDave Airlie 			    int offset, int len)
338afeb3e11SDave Airlie {
339afeb3e11SDave Airlie 	acpi_status status;
340afeb3e11SDave Airlie 	union acpi_object rom_arg_elements[2], *obj;
341afeb3e11SDave Airlie 	struct acpi_object_list rom_arg;
342afeb3e11SDave Airlie 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL};
343afeb3e11SDave Airlie 
344afeb3e11SDave Airlie 	rom_arg.count = 2;
345afeb3e11SDave Airlie 	rom_arg.pointer = &rom_arg_elements[0];
346afeb3e11SDave Airlie 
347afeb3e11SDave Airlie 	rom_arg_elements[0].type = ACPI_TYPE_INTEGER;
348afeb3e11SDave Airlie 	rom_arg_elements[0].integer.value = offset;
349afeb3e11SDave Airlie 
350afeb3e11SDave Airlie 	rom_arg_elements[1].type = ACPI_TYPE_INTEGER;
351afeb3e11SDave Airlie 	rom_arg_elements[1].integer.value = len;
352afeb3e11SDave Airlie 
353afeb3e11SDave Airlie 	status = acpi_evaluate_object(rom_handle, NULL, &rom_arg, &buffer);
354afeb3e11SDave Airlie 	if (ACPI_FAILURE(status)) {
355afeb3e11SDave Airlie 		printk(KERN_INFO "failed to evaluate ROM got %s\n", acpi_format_exception(status));
356afeb3e11SDave Airlie 		return -ENODEV;
357afeb3e11SDave Airlie 	}
358afeb3e11SDave Airlie 	obj = (union acpi_object *)buffer.pointer;
359afeb3e11SDave Airlie 	memcpy(bios+offset, obj->buffer.pointer, len);
360afeb3e11SDave Airlie 	kfree(buffer.pointer);
361afeb3e11SDave Airlie 	return len;
362afeb3e11SDave Airlie }
363afeb3e11SDave Airlie 
364afeb3e11SDave Airlie bool nouveau_acpi_rom_supported(struct pci_dev *pdev)
365afeb3e11SDave Airlie {
366afeb3e11SDave Airlie 	acpi_status status;
367afeb3e11SDave Airlie 	acpi_handle dhandle, rom_handle;
368afeb3e11SDave Airlie 
369f19467c5SDave Airlie 	if (!nouveau_dsm_priv.dsm_detected && !nouveau_dsm_priv.optimus_detected)
370afeb3e11SDave Airlie 		return false;
371afeb3e11SDave Airlie 
372afeb3e11SDave Airlie 	dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
373afeb3e11SDave Airlie 	if (!dhandle)
374afeb3e11SDave Airlie 		return false;
375afeb3e11SDave Airlie 
376afeb3e11SDave Airlie 	status = acpi_get_handle(dhandle, "_ROM", &rom_handle);
377afeb3e11SDave Airlie 	if (ACPI_FAILURE(status))
378afeb3e11SDave Airlie 		return false;
379afeb3e11SDave Airlie 
380afeb3e11SDave Airlie 	nouveau_dsm_priv.rom_handle = rom_handle;
381afeb3e11SDave Airlie 	return true;
382afeb3e11SDave Airlie }
383afeb3e11SDave Airlie 
384afeb3e11SDave Airlie int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len)
385afeb3e11SDave Airlie {
386afeb3e11SDave Airlie 	return nouveau_rom_call(nouveau_dsm_priv.rom_handle, bios, offset, len);
387afeb3e11SDave Airlie }
388a6ed76d7SBen Skeggs 
389c0077061SBen Skeggs void *
390a6ed76d7SBen Skeggs nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector)
391a6ed76d7SBen Skeggs {
392a6ed76d7SBen Skeggs 	struct acpi_device *acpidev;
393a6ed76d7SBen Skeggs 	acpi_handle handle;
394a6ed76d7SBen Skeggs 	int type, ret;
395a6ed76d7SBen Skeggs 	void *edid;
396a6ed76d7SBen Skeggs 
397a6ed76d7SBen Skeggs 	switch (connector->connector_type) {
398a6ed76d7SBen Skeggs 	case DRM_MODE_CONNECTOR_LVDS:
399a6ed76d7SBen Skeggs 	case DRM_MODE_CONNECTOR_eDP:
400a6ed76d7SBen Skeggs 		type = ACPI_VIDEO_DISPLAY_LCD;
401a6ed76d7SBen Skeggs 		break;
402a6ed76d7SBen Skeggs 	default:
403c0077061SBen Skeggs 		return NULL;
404a6ed76d7SBen Skeggs 	}
405a6ed76d7SBen Skeggs 
406a6ed76d7SBen Skeggs 	handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev);
407a6ed76d7SBen Skeggs 	if (!handle)
408c0077061SBen Skeggs 		return NULL;
409a6ed76d7SBen Skeggs 
410a6ed76d7SBen Skeggs 	ret = acpi_bus_get_device(handle, &acpidev);
411a6ed76d7SBen Skeggs 	if (ret)
412c0077061SBen Skeggs 		return NULL;
413a6ed76d7SBen Skeggs 
414a6ed76d7SBen Skeggs 	ret = acpi_video_get_edid(acpidev, type, -1, &edid);
415a6ed76d7SBen Skeggs 	if (ret < 0)
416c0077061SBen Skeggs 		return NULL;
417a6ed76d7SBen Skeggs 
418c0077061SBen Skeggs 	return kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
419a6ed76d7SBen Skeggs }
420