xref: /linux/drivers/gpu/drm/nouveau/nouveau_acpi.c (revision cba97805cb69d5b1a1d3bb108872c73b5bf0e205)
16ee73861SBen Skeggs #include <linux/pci.h>
26ee73861SBen Skeggs #include <linux/acpi.h>
35a0e3ad6STejun Heo #include <linux/slab.h>
48116188fSDave Airlie #include <linux/mxm-wmi.h>
56a9ee8afSDave Airlie #include <linux/vga_switcheroo.h>
6612a9aabSLinus Torvalds #include <drm/drm_edid.h>
78b48463fSLv Zheng #include <acpi/video.h>
8c0077061SBen Skeggs 
94dc28134SBen Skeggs #include "nouveau_drv.h"
10c0077061SBen Skeggs #include "nouveau_acpi.h"
11c0077061SBen Skeggs 
126ee73861SBen Skeggs #define NOUVEAU_DSM_LED 0x02
136ee73861SBen Skeggs #define NOUVEAU_DSM_LED_STATE 0x00
146ee73861SBen Skeggs #define NOUVEAU_DSM_LED_OFF 0x10
156ee73861SBen Skeggs #define NOUVEAU_DSM_LED_STAMINA 0x11
166ee73861SBen Skeggs #define NOUVEAU_DSM_LED_SPEED 0x12
176ee73861SBen Skeggs 
186ee73861SBen Skeggs #define NOUVEAU_DSM_POWER 0x03
196ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_STATE 0x00
206ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_SPEED 0x01
216ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_STAMINA 0x02
226ee73861SBen Skeggs 
235addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_CAPS 0x1A
245addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_FLAGS 0x1B
255addcf0aSDave Airlie 
265addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 (3 << 24)
275addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_NO_POWERDOWN_PS3 (2 << 24)
285addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED (1)
295addcf0aSDave Airlie 
305addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN (NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 | NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED)
315addcf0aSDave Airlie 
325addcf0aSDave Airlie /* result of the optimus caps function */
335addcf0aSDave Airlie #define OPTIMUS_ENABLED (1 << 0)
345addcf0aSDave Airlie #define OPTIMUS_STATUS_MASK (3 << 3)
355addcf0aSDave Airlie #define OPTIMUS_STATUS_OFF  (0 << 3)
365addcf0aSDave Airlie #define OPTIMUS_STATUS_ON_ENABLED  (1 << 3)
375addcf0aSDave Airlie #define OPTIMUS_STATUS_PWR_STABLE  (3 << 3)
385addcf0aSDave Airlie #define OPTIMUS_DISPLAY_HOTPLUG (1 << 6)
395addcf0aSDave Airlie #define OPTIMUS_CAPS_MASK (7 << 24)
405addcf0aSDave Airlie #define OPTIMUS_DYNAMIC_PWR_CAP (1 << 24)
415addcf0aSDave Airlie 
425addcf0aSDave Airlie #define OPTIMUS_AUDIO_CAPS_MASK (3 << 27)
435addcf0aSDave Airlie #define OPTIMUS_HDA_CODEC_MASK (2 << 27) /* hda bios control */
44d099230cSPeter Lekensteyn 
456a9ee8afSDave Airlie static struct nouveau_dsm_priv {
466a9ee8afSDave Airlie 	bool dsm_detected;
47f19467c5SDave Airlie 	bool optimus_detected;
48*cba97805SPeter Wu 	bool optimus_flags_detected;
496a9ee8afSDave Airlie 	acpi_handle dhandle;
50afeb3e11SDave Airlie 	acpi_handle rom_handle;
516a9ee8afSDave Airlie } nouveau_dsm_priv;
526a9ee8afSDave Airlie 
53c839d748SDave Airlie bool nouveau_is_optimus(void) {
54c839d748SDave Airlie 	return nouveau_dsm_priv.optimus_detected;
55c839d748SDave Airlie }
56c839d748SDave Airlie 
57c839d748SDave Airlie bool nouveau_is_v1_dsm(void) {
58c839d748SDave Airlie 	return nouveau_dsm_priv.dsm_detected;
59c839d748SDave Airlie }
60c839d748SDave Airlie 
61d0ce7b85SJeff Mahoney #ifdef CONFIG_VGA_SWITCHEROO
626a9ee8afSDave Airlie static const char nouveau_dsm_muid[] = {
636ee73861SBen Skeggs 	0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
646ee73861SBen Skeggs 	0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4,
656ee73861SBen Skeggs };
666ee73861SBen Skeggs 
67f19467c5SDave Airlie static const char nouveau_op_dsm_muid[] = {
68f19467c5SDave Airlie 	0xF8, 0xD8, 0x86, 0xA4, 0xDA, 0x0B, 0x1B, 0x47,
69f19467c5SDave Airlie 	0xA7, 0x2B, 0x60, 0x42, 0xA6, 0xB5, 0xBE, 0xE0,
70f19467c5SDave Airlie };
71f19467c5SDave Airlie 
72f19467c5SDave Airlie static int nouveau_optimus_dsm(acpi_handle handle, int func, int arg, uint32_t *result)
73f19467c5SDave Airlie {
74b072e53bSJiang Liu 	int i;
75f19467c5SDave Airlie 	union acpi_object *obj;
76d099230cSPeter Lekensteyn 	char args_buff[4];
77b072e53bSJiang Liu 	union acpi_object argv4 = {
78b072e53bSJiang Liu 		.buffer.type = ACPI_TYPE_BUFFER,
79b072e53bSJiang Liu 		.buffer.length = 4,
80b072e53bSJiang Liu 		.buffer.pointer = args_buff
81b072e53bSJiang Liu 	};
82f19467c5SDave Airlie 
83d099230cSPeter Lekensteyn 	/* ACPI is little endian, AABBCCDD becomes {DD,CC,BB,AA} */
84d099230cSPeter Lekensteyn 	for (i = 0; i < 4; i++)
85d099230cSPeter Lekensteyn 		args_buff[i] = (arg >> i * 8) & 0xFF;
86f19467c5SDave Airlie 
87f19467c5SDave Airlie 	*result = 0;
88b072e53bSJiang Liu 	obj = acpi_evaluate_dsm_typed(handle, nouveau_op_dsm_muid, 0x00000100,
89b072e53bSJiang Liu 				      func, &argv4, ACPI_TYPE_BUFFER);
90b072e53bSJiang Liu 	if (!obj) {
91b072e53bSJiang Liu 		acpi_handle_info(handle, "failed to evaluate _DSM\n");
92b072e53bSJiang Liu 		return AE_ERROR;
93b072e53bSJiang Liu 	} else {
94b072e53bSJiang Liu 		if (obj->buffer.length == 4) {
95f19467c5SDave Airlie 			*result |= obj->buffer.pointer[0];
96f19467c5SDave Airlie 			*result |= (obj->buffer.pointer[1] << 8);
97f19467c5SDave Airlie 			*result |= (obj->buffer.pointer[2] << 16);
98f19467c5SDave Airlie 			*result |= (obj->buffer.pointer[3] << 24);
99f19467c5SDave Airlie 		}
100b072e53bSJiang Liu 		ACPI_FREE(obj);
101f19467c5SDave Airlie 	}
102f19467c5SDave Airlie 
103f19467c5SDave Airlie 	return 0;
104f19467c5SDave Airlie }
105f19467c5SDave Airlie 
106e284175aSJiang Liu /*
107e284175aSJiang Liu  * On some platforms, _DSM(nouveau_op_dsm_muid, func0) has special
108e284175aSJiang Liu  * requirements on the fourth parameter, so a private implementation
109e284175aSJiang Liu  * instead of using acpi_check_dsm().
110e284175aSJiang Liu  */
111a12e78ddSPeter Wu static int nouveau_dsm_get_optimus_functions(acpi_handle handle)
112e284175aSJiang Liu {
113e284175aSJiang Liu 	int result;
114e284175aSJiang Liu 
115e284175aSJiang Liu 	/*
116e284175aSJiang Liu 	 * Function 0 returns a Buffer containing available functions.
117e284175aSJiang Liu 	 * The args parameter is ignored for function 0, so just put 0 in it
118e284175aSJiang Liu 	 */
119e284175aSJiang Liu 	if (nouveau_optimus_dsm(handle, 0, 0, &result))
120e284175aSJiang Liu 		return 0;
121e284175aSJiang Liu 
122e284175aSJiang Liu 	/*
123e284175aSJiang Liu 	 * ACPI Spec v4 9.14.1: if bit 0 is zero, no function is supported.
124e284175aSJiang Liu 	 * If the n-th bit is enabled, function n is supported
125e284175aSJiang Liu 	 */
126a12e78ddSPeter Wu 	if (result & 1 && result & (1 << NOUVEAU_DSM_OPTIMUS_CAPS))
127a12e78ddSPeter Wu 		return result;
128a12e78ddSPeter Wu 	return 0;
129e284175aSJiang Liu }
130e284175aSJiang Liu 
131b072e53bSJiang Liu static int nouveau_dsm(acpi_handle handle, int func, int arg)
1326a9ee8afSDave Airlie {
133b072e53bSJiang Liu 	int ret = 0;
1346ee73861SBen Skeggs 	union acpi_object *obj;
135b072e53bSJiang Liu 	union acpi_object argv4 = {
136b072e53bSJiang Liu 		.integer.type = ACPI_TYPE_INTEGER,
137b072e53bSJiang Liu 		.integer.value = arg,
138b072e53bSJiang Liu 	};
1396ee73861SBen Skeggs 
140b072e53bSJiang Liu 	obj = acpi_evaluate_dsm_typed(handle, nouveau_dsm_muid, 0x00000102,
141b072e53bSJiang Liu 				      func, &argv4, ACPI_TYPE_INTEGER);
142b072e53bSJiang Liu 	if (!obj) {
143b072e53bSJiang Liu 		acpi_handle_info(handle, "failed to evaluate _DSM\n");
144b072e53bSJiang Liu 		return AE_ERROR;
145b072e53bSJiang Liu 	} else {
1466ee73861SBen Skeggs 		if (obj->integer.value == 0x80000002)
147b072e53bSJiang Liu 			ret = -ENODEV;
148b072e53bSJiang Liu 		ACPI_FREE(obj);
1496ee73861SBen Skeggs 	}
1506ee73861SBen Skeggs 
151b072e53bSJiang Liu 	return ret;
1529075e85fSPeter Lekensteyn }
1539075e85fSPeter Lekensteyn 
1546a9ee8afSDave Airlie static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id)
1556ee73861SBen Skeggs {
156000703f4SDave Airlie 	mxm_wmi_call_mxmx(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0);
1578116188fSDave Airlie 	mxm_wmi_call_mxds(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0);
158b072e53bSJiang Liu 	return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id);
1596ee73861SBen Skeggs }
1606ee73861SBen Skeggs 
1616a9ee8afSDave Airlie static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state)
1626a9ee8afSDave Airlie {
1636a9ee8afSDave Airlie 	int arg;
1646a9ee8afSDave Airlie 	if (state == VGA_SWITCHEROO_ON)
1656a9ee8afSDave Airlie 		arg = NOUVEAU_DSM_POWER_SPEED;
1666a9ee8afSDave Airlie 	else
1676a9ee8afSDave Airlie 		arg = NOUVEAU_DSM_POWER_STAMINA;
168b072e53bSJiang Liu 	nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg);
1696ee73861SBen Skeggs 	return 0;
1706ee73861SBen Skeggs }
1716ee73861SBen Skeggs 
1726a9ee8afSDave Airlie static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id)
1736ee73861SBen Skeggs {
174c839d748SDave Airlie 	if (!nouveau_dsm_priv.dsm_detected)
175d099230cSPeter Lekensteyn 		return 0;
1766a9ee8afSDave Airlie 	if (id == VGA_SWITCHEROO_IGD)
177fc5ea29dSDave Airlie 		return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA);
1786a9ee8afSDave Airlie 	else
179fc5ea29dSDave Airlie 		return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_SPEED);
1806a9ee8afSDave Airlie }
1816ee73861SBen Skeggs 
1826a9ee8afSDave Airlie static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id,
1836a9ee8afSDave Airlie 				   enum vga_switcheroo_state state)
1846a9ee8afSDave Airlie {
1856a9ee8afSDave Airlie 	if (id == VGA_SWITCHEROO_IGD)
1866a9ee8afSDave Airlie 		return 0;
1876a9ee8afSDave Airlie 
188d099230cSPeter Lekensteyn 	/* Optimus laptops have the card already disabled in
189d099230cSPeter Lekensteyn 	 * nouveau_switcheroo_set_state */
190c839d748SDave Airlie 	if (!nouveau_dsm_priv.dsm_detected)
191d099230cSPeter Lekensteyn 		return 0;
192d099230cSPeter Lekensteyn 
193fc5ea29dSDave Airlie 	return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state);
1946a9ee8afSDave Airlie }
1956a9ee8afSDave Airlie 
1966a9ee8afSDave Airlie static int nouveau_dsm_get_client_id(struct pci_dev *pdev)
1976a9ee8afSDave Airlie {
198d1fbd923SDave Airlie 	/* easy option one - intel vendor ID means Integrated */
199d1fbd923SDave Airlie 	if (pdev->vendor == PCI_VENDOR_ID_INTEL)
2006a9ee8afSDave Airlie 		return VGA_SWITCHEROO_IGD;
201d1fbd923SDave Airlie 
202d1fbd923SDave Airlie 	/* is this device on Bus 0? - this may need improving */
203d1fbd923SDave Airlie 	if (pdev->bus->number == 0)
204d1fbd923SDave Airlie 		return VGA_SWITCHEROO_IGD;
205d1fbd923SDave Airlie 
2066a9ee8afSDave Airlie 	return VGA_SWITCHEROO_DIS;
2076a9ee8afSDave Airlie }
2086a9ee8afSDave Airlie 
2095d170139SLukas Wunner static const struct vga_switcheroo_handler nouveau_dsm_handler = {
2106a9ee8afSDave Airlie 	.switchto = nouveau_dsm_switchto,
2116a9ee8afSDave Airlie 	.power_state = nouveau_dsm_power_state,
2126a9ee8afSDave Airlie 	.get_client_id = nouveau_dsm_get_client_id,
2136a9ee8afSDave Airlie };
2146a9ee8afSDave Airlie 
215df42194aSPeter Wu static void nouveau_dsm_pci_probe(struct pci_dev *pdev, acpi_handle *dhandle_out,
216*cba97805SPeter Wu 				  bool *has_mux, bool *has_opt,
217*cba97805SPeter Wu 				  bool *has_opt_flags)
2186a9ee8afSDave Airlie {
219187b5b5dSZhang Rui 	acpi_handle dhandle;
220df42194aSPeter Wu 	bool supports_mux;
221a12e78ddSPeter Wu 	int optimus_funcs;
2226a9ee8afSDave Airlie 
2233a83f992SRafael J. Wysocki 	dhandle = ACPI_HANDLE(&pdev->dev);
2246a9ee8afSDave Airlie 	if (!dhandle)
225df42194aSPeter Wu 		return;
226afeb3e11SDave Airlie 
227f91ce35eSBjorn Helgaas 	if (!acpi_has_method(dhandle, "_DSM"))
228df42194aSPeter Wu 		return;
229f91ce35eSBjorn Helgaas 
230df42194aSPeter Wu 	supports_mux = acpi_check_dsm(dhandle, nouveau_dsm_muid, 0x00000102,
231df42194aSPeter Wu 				      1 << NOUVEAU_DSM_POWER);
232a12e78ddSPeter Wu 	optimus_funcs = nouveau_dsm_get_optimus_functions(dhandle);
2336ee73861SBen Skeggs 
234df42194aSPeter Wu 	/* Does not look like a Nvidia device. */
235a12e78ddSPeter Wu 	if (!supports_mux && !optimus_funcs)
236df42194aSPeter Wu 		return;
237f19467c5SDave Airlie 
238df42194aSPeter Wu 	*dhandle_out = dhandle;
239df42194aSPeter Wu 	*has_mux = supports_mux;
240a12e78ddSPeter Wu 	*has_opt = !!optimus_funcs;
241*cba97805SPeter Wu 	*has_opt_flags = optimus_funcs & (1 << NOUVEAU_DSM_OPTIMUS_FLAGS);
242df42194aSPeter Wu 
243a12e78ddSPeter Wu 	if (optimus_funcs) {
2445addcf0aSDave Airlie 		uint32_t result;
2455addcf0aSDave Airlie 		nouveau_optimus_dsm(dhandle, NOUVEAU_DSM_OPTIMUS_CAPS, 0,
2465addcf0aSDave Airlie 				    &result);
2475addcf0aSDave Airlie 		dev_info(&pdev->dev, "optimus capabilities: %s, status %s%s\n",
2485addcf0aSDave Airlie 			 (result & OPTIMUS_ENABLED) ? "enabled" : "disabled",
2495addcf0aSDave Airlie 			 (result & OPTIMUS_DYNAMIC_PWR_CAP) ? "dynamic power, " : "",
2505addcf0aSDave Airlie 			 (result & OPTIMUS_HDA_CODEC_MASK) ? "hda bios codec supported" : "");
2515addcf0aSDave Airlie 	}
2526ee73861SBen Skeggs }
2536a9ee8afSDave Airlie 
2546a9ee8afSDave Airlie static bool nouveau_dsm_detect(void)
2556a9ee8afSDave Airlie {
2566a9ee8afSDave Airlie 	char acpi_method_name[255] = { 0 };
2576a9ee8afSDave Airlie 	struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
2586a9ee8afSDave Airlie 	struct pci_dev *pdev = NULL;
259df42194aSPeter Wu 	acpi_handle dhandle = NULL;
260df42194aSPeter Wu 	bool has_mux = false;
261df42194aSPeter Wu 	bool has_optimus = false;
262*cba97805SPeter Wu 	bool has_optimus_flags = false;
2636a9ee8afSDave Airlie 	int vga_count = 0;
2648116188fSDave Airlie 	bool guid_valid;
265f19467c5SDave Airlie 	bool ret = false;
2668116188fSDave Airlie 
267f19467c5SDave Airlie 	/* lookup the MXM GUID */
2688116188fSDave Airlie 	guid_valid = mxm_wmi_supported();
2698116188fSDave Airlie 
270f19467c5SDave Airlie 	if (guid_valid)
271f19467c5SDave Airlie 		printk("MXM: GUID detected in BIOS\n");
272afeb3e11SDave Airlie 
273f19467c5SDave Airlie 	/* now do DSM detection */
2746a9ee8afSDave Airlie 	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
2756a9ee8afSDave Airlie 		vga_count++;
2766a9ee8afSDave Airlie 
277*cba97805SPeter Wu 		nouveau_dsm_pci_probe(pdev, &dhandle, &has_mux, &has_optimus,
278*cba97805SPeter Wu 				      &has_optimus_flags);
2796a9ee8afSDave Airlie 	}
2806a9ee8afSDave Airlie 
2814c60fac1SEmil Velikov 	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_3D << 8, pdev)) != NULL) {
2824c60fac1SEmil Velikov 		vga_count++;
2834c60fac1SEmil Velikov 
284*cba97805SPeter Wu 		nouveau_dsm_pci_probe(pdev, &dhandle, &has_mux, &has_optimus,
285*cba97805SPeter Wu 				      &has_optimus_flags);
2864c60fac1SEmil Velikov 	}
2874c60fac1SEmil Velikov 
288c839d748SDave Airlie 	/* find the optimus DSM or the old v1 DSM */
289df42194aSPeter Wu 	if (has_optimus) {
290df42194aSPeter Wu 		nouveau_dsm_priv.dhandle = dhandle;
291c839d748SDave Airlie 		acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME,
292c839d748SDave Airlie 			&buffer);
293c839d748SDave Airlie 		printk(KERN_INFO "VGA switcheroo: detected Optimus DSM method %s handle\n",
294c839d748SDave Airlie 			acpi_method_name);
295c839d748SDave Airlie 		nouveau_dsm_priv.optimus_detected = true;
296*cba97805SPeter Wu 		nouveau_dsm_priv.optimus_flags_detected = has_optimus_flags;
297c839d748SDave Airlie 		ret = true;
298df42194aSPeter Wu 	} else if (vga_count == 2 && has_mux && guid_valid) {
299df42194aSPeter Wu 		nouveau_dsm_priv.dhandle = dhandle;
300d099230cSPeter Lekensteyn 		acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME,
301d099230cSPeter Lekensteyn 			&buffer);
3026a9ee8afSDave Airlie 		printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n",
3036a9ee8afSDave Airlie 			acpi_method_name);
3046a9ee8afSDave Airlie 		nouveau_dsm_priv.dsm_detected = true;
305f19467c5SDave Airlie 		ret = true;
3066a9ee8afSDave Airlie 	}
307f19467c5SDave Airlie 
308f19467c5SDave Airlie 
309f19467c5SDave Airlie 	return ret;
3106a9ee8afSDave Airlie }
3116a9ee8afSDave Airlie 
3126a9ee8afSDave Airlie void nouveau_register_dsm_handler(void)
3136a9ee8afSDave Airlie {
3146a9ee8afSDave Airlie 	bool r;
3156a9ee8afSDave Airlie 
3166a9ee8afSDave Airlie 	r = nouveau_dsm_detect();
3176a9ee8afSDave Airlie 	if (!r)
3186a9ee8afSDave Airlie 		return;
3196a9ee8afSDave Airlie 
320156d7d41SLukas Wunner 	vga_switcheroo_register_handler(&nouveau_dsm_handler, 0);
3216a9ee8afSDave Airlie }
3226a9ee8afSDave Airlie 
323d099230cSPeter Lekensteyn /* Must be called for Optimus models before the card can be turned off */
324d099230cSPeter Lekensteyn void nouveau_switcheroo_optimus_dsm(void)
325d099230cSPeter Lekensteyn {
326d099230cSPeter Lekensteyn 	u32 result = 0;
327d099230cSPeter Lekensteyn 	if (!nouveau_dsm_priv.optimus_detected)
328d099230cSPeter Lekensteyn 		return;
329d099230cSPeter Lekensteyn 
330*cba97805SPeter Wu 	if (nouveau_dsm_priv.optimus_flags_detected)
3315addcf0aSDave Airlie 		nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_FLAGS,
3325addcf0aSDave Airlie 				    0x3, &result);
3335addcf0aSDave Airlie 
3345addcf0aSDave Airlie 	nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_CAPS,
3355addcf0aSDave Airlie 		NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN, &result);
3365addcf0aSDave Airlie 
337d099230cSPeter Lekensteyn }
338d099230cSPeter Lekensteyn 
3396a9ee8afSDave Airlie void nouveau_unregister_dsm_handler(void)
3406a9ee8afSDave Airlie {
3412f3787aaSAndreas Heider 	if (nouveau_dsm_priv.optimus_detected || nouveau_dsm_priv.dsm_detected)
3426a9ee8afSDave Airlie 		vga_switcheroo_unregister_handler();
3436a9ee8afSDave Airlie }
344d0ce7b85SJeff Mahoney #else
345d0ce7b85SJeff Mahoney void nouveau_register_dsm_handler(void) {}
346d0ce7b85SJeff Mahoney void nouveau_unregister_dsm_handler(void) {}
347d0ce7b85SJeff Mahoney void nouveau_switcheroo_optimus_dsm(void) {}
348d0ce7b85SJeff Mahoney #endif
349afeb3e11SDave Airlie 
350afeb3e11SDave Airlie /* retrieve the ROM in 4k blocks */
351afeb3e11SDave Airlie static int nouveau_rom_call(acpi_handle rom_handle, uint8_t *bios,
352afeb3e11SDave Airlie 			    int offset, int len)
353afeb3e11SDave Airlie {
354afeb3e11SDave Airlie 	acpi_status status;
355afeb3e11SDave Airlie 	union acpi_object rom_arg_elements[2], *obj;
356afeb3e11SDave Airlie 	struct acpi_object_list rom_arg;
357afeb3e11SDave Airlie 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL};
358afeb3e11SDave Airlie 
359afeb3e11SDave Airlie 	rom_arg.count = 2;
360afeb3e11SDave Airlie 	rom_arg.pointer = &rom_arg_elements[0];
361afeb3e11SDave Airlie 
362afeb3e11SDave Airlie 	rom_arg_elements[0].type = ACPI_TYPE_INTEGER;
363afeb3e11SDave Airlie 	rom_arg_elements[0].integer.value = offset;
364afeb3e11SDave Airlie 
365afeb3e11SDave Airlie 	rom_arg_elements[1].type = ACPI_TYPE_INTEGER;
366afeb3e11SDave Airlie 	rom_arg_elements[1].integer.value = len;
367afeb3e11SDave Airlie 
368afeb3e11SDave Airlie 	status = acpi_evaluate_object(rom_handle, NULL, &rom_arg, &buffer);
369afeb3e11SDave Airlie 	if (ACPI_FAILURE(status)) {
370afeb3e11SDave Airlie 		printk(KERN_INFO "failed to evaluate ROM got %s\n", acpi_format_exception(status));
371afeb3e11SDave Airlie 		return -ENODEV;
372afeb3e11SDave Airlie 	}
373afeb3e11SDave Airlie 	obj = (union acpi_object *)buffer.pointer;
37495432941SBen Skeggs 	len = min(len, (int)obj->buffer.length);
375afeb3e11SDave Airlie 	memcpy(bios+offset, obj->buffer.pointer, len);
376afeb3e11SDave Airlie 	kfree(buffer.pointer);
377afeb3e11SDave Airlie 	return len;
378afeb3e11SDave Airlie }
379afeb3e11SDave Airlie 
38026c9e8efSBen Skeggs bool nouveau_acpi_rom_supported(struct device *dev)
381afeb3e11SDave Airlie {
382afeb3e11SDave Airlie 	acpi_status status;
383afeb3e11SDave Airlie 	acpi_handle dhandle, rom_handle;
384afeb3e11SDave Airlie 
38526c9e8efSBen Skeggs 	dhandle = ACPI_HANDLE(dev);
386afeb3e11SDave Airlie 	if (!dhandle)
387afeb3e11SDave Airlie 		return false;
388afeb3e11SDave Airlie 
389afeb3e11SDave Airlie 	status = acpi_get_handle(dhandle, "_ROM", &rom_handle);
390afeb3e11SDave Airlie 	if (ACPI_FAILURE(status))
391afeb3e11SDave Airlie 		return false;
392afeb3e11SDave Airlie 
393afeb3e11SDave Airlie 	nouveau_dsm_priv.rom_handle = rom_handle;
394afeb3e11SDave Airlie 	return true;
395afeb3e11SDave Airlie }
396afeb3e11SDave Airlie 
397afeb3e11SDave Airlie int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len)
398afeb3e11SDave Airlie {
399afeb3e11SDave Airlie 	return nouveau_rom_call(nouveau_dsm_priv.rom_handle, bios, offset, len);
400afeb3e11SDave Airlie }
401a6ed76d7SBen Skeggs 
402c0077061SBen Skeggs void *
403a6ed76d7SBen Skeggs nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector)
404a6ed76d7SBen Skeggs {
405a6ed76d7SBen Skeggs 	struct acpi_device *acpidev;
406a6ed76d7SBen Skeggs 	acpi_handle handle;
407a6ed76d7SBen Skeggs 	int type, ret;
408a6ed76d7SBen Skeggs 	void *edid;
409a6ed76d7SBen Skeggs 
410a6ed76d7SBen Skeggs 	switch (connector->connector_type) {
411a6ed76d7SBen Skeggs 	case DRM_MODE_CONNECTOR_LVDS:
412a6ed76d7SBen Skeggs 	case DRM_MODE_CONNECTOR_eDP:
413a6ed76d7SBen Skeggs 		type = ACPI_VIDEO_DISPLAY_LCD;
414a6ed76d7SBen Skeggs 		break;
415a6ed76d7SBen Skeggs 	default:
416c0077061SBen Skeggs 		return NULL;
417a6ed76d7SBen Skeggs 	}
418a6ed76d7SBen Skeggs 
4193a83f992SRafael J. Wysocki 	handle = ACPI_HANDLE(&dev->pdev->dev);
420a6ed76d7SBen Skeggs 	if (!handle)
421c0077061SBen Skeggs 		return NULL;
422a6ed76d7SBen Skeggs 
423a6ed76d7SBen Skeggs 	ret = acpi_bus_get_device(handle, &acpidev);
424a6ed76d7SBen Skeggs 	if (ret)
425c0077061SBen Skeggs 		return NULL;
426a6ed76d7SBen Skeggs 
427a6ed76d7SBen Skeggs 	ret = acpi_video_get_edid(acpidev, type, -1, &edid);
428a6ed76d7SBen Skeggs 	if (ret < 0)
429c0077061SBen Skeggs 		return NULL;
430a6ed76d7SBen Skeggs 
431c0077061SBen Skeggs 	return kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
432a6ed76d7SBen Skeggs }
433