xref: /linux/drivers/gpu/drm/nouveau/nouveau_acpi.c (revision b072e53b0a27a885d8be3d08c8d8758292762f39)
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 
12612a9aabSLinus 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 
285addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_CAPS 0x1A
295addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_FLAGS 0x1B
305addcf0aSDave Airlie 
315addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 (3 << 24)
325addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_NO_POWERDOWN_PS3 (2 << 24)
335addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED (1)
345addcf0aSDave Airlie 
355addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN (NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 | NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED)
365addcf0aSDave Airlie 
375addcf0aSDave Airlie /* result of the optimus caps function */
385addcf0aSDave Airlie #define OPTIMUS_ENABLED (1 << 0)
395addcf0aSDave Airlie #define OPTIMUS_STATUS_MASK (3 << 3)
405addcf0aSDave Airlie #define OPTIMUS_STATUS_OFF  (0 << 3)
415addcf0aSDave Airlie #define OPTIMUS_STATUS_ON_ENABLED  (1 << 3)
425addcf0aSDave Airlie #define OPTIMUS_STATUS_PWR_STABLE  (3 << 3)
435addcf0aSDave Airlie #define OPTIMUS_DISPLAY_HOTPLUG (1 << 6)
445addcf0aSDave Airlie #define OPTIMUS_CAPS_MASK (7 << 24)
455addcf0aSDave Airlie #define OPTIMUS_DYNAMIC_PWR_CAP (1 << 24)
465addcf0aSDave Airlie 
475addcf0aSDave Airlie #define OPTIMUS_AUDIO_CAPS_MASK (3 << 27)
485addcf0aSDave Airlie #define OPTIMUS_HDA_CODEC_MASK (2 << 27) /* hda bios control */
49d099230cSPeter Lekensteyn 
506a9ee8afSDave Airlie static struct nouveau_dsm_priv {
516a9ee8afSDave Airlie 	bool dsm_detected;
52f19467c5SDave Airlie 	bool optimus_detected;
536a9ee8afSDave Airlie 	acpi_handle dhandle;
54f244d8b6SRafael J. Wysocki 	acpi_handle other_handle;
55afeb3e11SDave Airlie 	acpi_handle rom_handle;
566a9ee8afSDave Airlie } nouveau_dsm_priv;
576a9ee8afSDave Airlie 
58c839d748SDave Airlie bool nouveau_is_optimus(void) {
59c839d748SDave Airlie 	return nouveau_dsm_priv.optimus_detected;
60c839d748SDave Airlie }
61c839d748SDave Airlie 
62c839d748SDave Airlie bool nouveau_is_v1_dsm(void) {
63c839d748SDave Airlie 	return nouveau_dsm_priv.dsm_detected;
64c839d748SDave Airlie }
65c839d748SDave Airlie 
66f19467c5SDave Airlie #define NOUVEAU_DSM_HAS_MUX 0x1
67f19467c5SDave Airlie #define NOUVEAU_DSM_HAS_OPT 0x2
68f19467c5SDave Airlie 
696a9ee8afSDave Airlie static const char nouveau_dsm_muid[] = {
706ee73861SBen Skeggs 	0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
716ee73861SBen Skeggs 	0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4,
726ee73861SBen Skeggs };
736ee73861SBen Skeggs 
74f19467c5SDave Airlie static const char nouveau_op_dsm_muid[] = {
75f19467c5SDave Airlie 	0xF8, 0xD8, 0x86, 0xA4, 0xDA, 0x0B, 0x1B, 0x47,
76f19467c5SDave Airlie 	0xA7, 0x2B, 0x60, 0x42, 0xA6, 0xB5, 0xBE, 0xE0,
77f19467c5SDave Airlie };
78f19467c5SDave Airlie 
79f19467c5SDave Airlie static int nouveau_optimus_dsm(acpi_handle handle, int func, int arg, uint32_t *result)
80f19467c5SDave Airlie {
81*b072e53bSJiang Liu 	int i;
82f19467c5SDave Airlie 	union acpi_object *obj;
83d099230cSPeter Lekensteyn 	char args_buff[4];
84*b072e53bSJiang Liu 	union acpi_object argv4 = {
85*b072e53bSJiang Liu 		.buffer.type = ACPI_TYPE_BUFFER,
86*b072e53bSJiang Liu 		.buffer.length = 4,
87*b072e53bSJiang Liu 		.buffer.pointer = args_buff
88*b072e53bSJiang Liu 	};
89f19467c5SDave Airlie 
90d099230cSPeter Lekensteyn 	/* ACPI is little endian, AABBCCDD becomes {DD,CC,BB,AA} */
91d099230cSPeter Lekensteyn 	for (i = 0; i < 4; i++)
92d099230cSPeter Lekensteyn 		args_buff[i] = (arg >> i * 8) & 0xFF;
93f19467c5SDave Airlie 
94f19467c5SDave Airlie 	*result = 0;
95*b072e53bSJiang Liu 	obj = acpi_evaluate_dsm_typed(handle, nouveau_op_dsm_muid, 0x00000100,
96*b072e53bSJiang Liu 				      func, &argv4, ACPI_TYPE_BUFFER);
97*b072e53bSJiang Liu 	if (!obj) {
98*b072e53bSJiang Liu 		acpi_handle_info(handle, "failed to evaluate _DSM\n");
99*b072e53bSJiang Liu 		return AE_ERROR;
100*b072e53bSJiang Liu 	} else {
101*b072e53bSJiang Liu 		if (obj->buffer.length == 4) {
102f19467c5SDave Airlie 			*result |= obj->buffer.pointer[0];
103f19467c5SDave Airlie 			*result |= (obj->buffer.pointer[1] << 8);
104f19467c5SDave Airlie 			*result |= (obj->buffer.pointer[2] << 16);
105f19467c5SDave Airlie 			*result |= (obj->buffer.pointer[3] << 24);
106f19467c5SDave Airlie 		}
107*b072e53bSJiang Liu 		ACPI_FREE(obj);
108f19467c5SDave Airlie 	}
109f19467c5SDave Airlie 
110f19467c5SDave Airlie 	return 0;
111f19467c5SDave Airlie }
112f19467c5SDave Airlie 
113*b072e53bSJiang Liu static int nouveau_dsm(acpi_handle handle, int func, int arg)
1146a9ee8afSDave Airlie {
115*b072e53bSJiang Liu 	int ret = 0;
1166ee73861SBen Skeggs 	union acpi_object *obj;
117*b072e53bSJiang Liu 	union acpi_object argv4 = {
118*b072e53bSJiang Liu 		.integer.type = ACPI_TYPE_INTEGER,
119*b072e53bSJiang Liu 		.integer.value = arg,
120*b072e53bSJiang Liu 	};
1216ee73861SBen Skeggs 
122*b072e53bSJiang Liu 	obj = acpi_evaluate_dsm_typed(handle, nouveau_dsm_muid, 0x00000102,
123*b072e53bSJiang Liu 				      func, &argv4, ACPI_TYPE_INTEGER);
124*b072e53bSJiang Liu 	if (!obj) {
125*b072e53bSJiang Liu 		acpi_handle_info(handle, "failed to evaluate _DSM\n");
126*b072e53bSJiang Liu 		return AE_ERROR;
127*b072e53bSJiang Liu 	} else {
128*b072e53bSJiang Liu 		if (obj->integer.value == 0x80000002)
129*b072e53bSJiang Liu 			ret = -ENODEV;
130*b072e53bSJiang Liu 		ACPI_FREE(obj);
1316ee73861SBen Skeggs 	}
1326ee73861SBen Skeggs 
133*b072e53bSJiang Liu 	return ret;
1349075e85fSPeter Lekensteyn }
1359075e85fSPeter Lekensteyn 
1366a9ee8afSDave Airlie static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id)
1376ee73861SBen Skeggs {
138000703f4SDave Airlie 	mxm_wmi_call_mxmx(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0);
1398116188fSDave Airlie 	mxm_wmi_call_mxds(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0);
140*b072e53bSJiang Liu 	return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id);
1416ee73861SBen Skeggs }
1426ee73861SBen Skeggs 
1436a9ee8afSDave Airlie static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state)
1446a9ee8afSDave Airlie {
1456a9ee8afSDave Airlie 	int arg;
1466a9ee8afSDave Airlie 	if (state == VGA_SWITCHEROO_ON)
1476a9ee8afSDave Airlie 		arg = NOUVEAU_DSM_POWER_SPEED;
1486a9ee8afSDave Airlie 	else
1496a9ee8afSDave Airlie 		arg = NOUVEAU_DSM_POWER_STAMINA;
150*b072e53bSJiang Liu 	nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg);
1516ee73861SBen Skeggs 	return 0;
1526ee73861SBen Skeggs }
1536ee73861SBen Skeggs 
1546a9ee8afSDave Airlie static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id)
1556ee73861SBen Skeggs {
156c839d748SDave Airlie 	if (!nouveau_dsm_priv.dsm_detected)
157d099230cSPeter Lekensteyn 		return 0;
1586a9ee8afSDave Airlie 	if (id == VGA_SWITCHEROO_IGD)
159fc5ea29dSDave Airlie 		return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA);
1606a9ee8afSDave Airlie 	else
161fc5ea29dSDave Airlie 		return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_SPEED);
1626a9ee8afSDave Airlie }
1636ee73861SBen Skeggs 
1646a9ee8afSDave Airlie static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id,
1656a9ee8afSDave Airlie 				   enum vga_switcheroo_state state)
1666a9ee8afSDave Airlie {
1676a9ee8afSDave Airlie 	if (id == VGA_SWITCHEROO_IGD)
1686a9ee8afSDave Airlie 		return 0;
1696a9ee8afSDave Airlie 
170d099230cSPeter Lekensteyn 	/* Optimus laptops have the card already disabled in
171d099230cSPeter Lekensteyn 	 * nouveau_switcheroo_set_state */
172c839d748SDave Airlie 	if (!nouveau_dsm_priv.dsm_detected)
173d099230cSPeter Lekensteyn 		return 0;
174d099230cSPeter Lekensteyn 
175fc5ea29dSDave Airlie 	return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state);
1766a9ee8afSDave Airlie }
1776a9ee8afSDave Airlie 
1786a9ee8afSDave Airlie static int nouveau_dsm_get_client_id(struct pci_dev *pdev)
1796a9ee8afSDave Airlie {
180d1fbd923SDave Airlie 	/* easy option one - intel vendor ID means Integrated */
181d1fbd923SDave Airlie 	if (pdev->vendor == PCI_VENDOR_ID_INTEL)
1826a9ee8afSDave Airlie 		return VGA_SWITCHEROO_IGD;
183d1fbd923SDave Airlie 
184d1fbd923SDave Airlie 	/* is this device on Bus 0? - this may need improving */
185d1fbd923SDave Airlie 	if (pdev->bus->number == 0)
186d1fbd923SDave Airlie 		return VGA_SWITCHEROO_IGD;
187d1fbd923SDave Airlie 
1886a9ee8afSDave Airlie 	return VGA_SWITCHEROO_DIS;
1896a9ee8afSDave Airlie }
1906a9ee8afSDave Airlie 
1916a9ee8afSDave Airlie static struct vga_switcheroo_handler nouveau_dsm_handler = {
1926a9ee8afSDave Airlie 	.switchto = nouveau_dsm_switchto,
1936a9ee8afSDave Airlie 	.power_state = nouveau_dsm_power_state,
1946a9ee8afSDave Airlie 	.get_client_id = nouveau_dsm_get_client_id,
1956a9ee8afSDave Airlie };
1966a9ee8afSDave Airlie 
197f19467c5SDave Airlie static int nouveau_dsm_pci_probe(struct pci_dev *pdev)
1986a9ee8afSDave Airlie {
199187b5b5dSZhang Rui 	acpi_handle dhandle;
2009075e85fSPeter Lekensteyn 	int retval = 0;
2016a9ee8afSDave Airlie 
2023a83f992SRafael J. Wysocki 	dhandle = ACPI_HANDLE(&pdev->dev);
2036a9ee8afSDave Airlie 	if (!dhandle)
2046a9ee8afSDave Airlie 		return false;
205afeb3e11SDave Airlie 
206f244d8b6SRafael J. Wysocki 	if (!acpi_has_method(dhandle, "_DSM")) {
207f244d8b6SRafael J. Wysocki 		nouveau_dsm_priv.other_handle = dhandle;
2086a9ee8afSDave Airlie 		return false;
209f244d8b6SRafael J. Wysocki 	}
210*b072e53bSJiang Liu 	if (acpi_check_dsm(dhandle, nouveau_dsm_muid, 0x00000102,
211*b072e53bSJiang Liu 			   1 << NOUVEAU_DSM_POWER))
212f19467c5SDave Airlie 		retval |= NOUVEAU_DSM_HAS_MUX;
2136ee73861SBen Skeggs 
214*b072e53bSJiang Liu 	if (acpi_check_dsm(dhandle, nouveau_op_dsm_muid, 0x00000100,
215*b072e53bSJiang Liu 			   1 << NOUVEAU_DSM_OPTIMUS_CAPS))
216f19467c5SDave Airlie 		retval |= NOUVEAU_DSM_HAS_OPT;
217f19467c5SDave Airlie 
2185addcf0aSDave Airlie 	if (retval & NOUVEAU_DSM_HAS_OPT) {
2195addcf0aSDave Airlie 		uint32_t result;
2205addcf0aSDave Airlie 		nouveau_optimus_dsm(dhandle, NOUVEAU_DSM_OPTIMUS_CAPS, 0,
2215addcf0aSDave Airlie 				    &result);
2225addcf0aSDave Airlie 		dev_info(&pdev->dev, "optimus capabilities: %s, status %s%s\n",
2235addcf0aSDave Airlie 			 (result & OPTIMUS_ENABLED) ? "enabled" : "disabled",
2245addcf0aSDave Airlie 			 (result & OPTIMUS_DYNAMIC_PWR_CAP) ? "dynamic power, " : "",
2255addcf0aSDave Airlie 			 (result & OPTIMUS_HDA_CODEC_MASK) ? "hda bios codec supported" : "");
2265addcf0aSDave Airlie 	}
227f19467c5SDave Airlie 	if (retval)
2286a9ee8afSDave Airlie 		nouveau_dsm_priv.dhandle = dhandle;
229f19467c5SDave Airlie 
230f19467c5SDave Airlie 	return retval;
2316ee73861SBen Skeggs }
2326a9ee8afSDave Airlie 
2336a9ee8afSDave Airlie static bool nouveau_dsm_detect(void)
2346a9ee8afSDave Airlie {
2356a9ee8afSDave Airlie 	char acpi_method_name[255] = { 0 };
2366a9ee8afSDave Airlie 	struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
2376a9ee8afSDave Airlie 	struct pci_dev *pdev = NULL;
2386a9ee8afSDave Airlie 	int has_dsm = 0;
239addde4ecSDave Airlie 	int has_optimus = 0;
2406a9ee8afSDave Airlie 	int vga_count = 0;
2418116188fSDave Airlie 	bool guid_valid;
242f19467c5SDave Airlie 	int retval;
243f19467c5SDave Airlie 	bool ret = false;
2448116188fSDave Airlie 
245f19467c5SDave Airlie 	/* lookup the MXM GUID */
2468116188fSDave Airlie 	guid_valid = mxm_wmi_supported();
2478116188fSDave Airlie 
248f19467c5SDave Airlie 	if (guid_valid)
249f19467c5SDave Airlie 		printk("MXM: GUID detected in BIOS\n");
250afeb3e11SDave Airlie 
251f19467c5SDave Airlie 	/* now do DSM detection */
2526a9ee8afSDave Airlie 	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
2536a9ee8afSDave Airlie 		vga_count++;
2546a9ee8afSDave Airlie 
255f19467c5SDave Airlie 		retval = nouveau_dsm_pci_probe(pdev);
256f19467c5SDave Airlie 		if (retval & NOUVEAU_DSM_HAS_MUX)
257f19467c5SDave Airlie 			has_dsm |= 1;
258f19467c5SDave Airlie 		if (retval & NOUVEAU_DSM_HAS_OPT)
259f19467c5SDave Airlie 			has_optimus = 1;
2606a9ee8afSDave Airlie 	}
2616a9ee8afSDave Airlie 
2624c60fac1SEmil Velikov 	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_3D << 8, pdev)) != NULL) {
2634c60fac1SEmil Velikov 		vga_count++;
2644c60fac1SEmil Velikov 
2654c60fac1SEmil Velikov 		retval = nouveau_dsm_pci_probe(pdev);
2664c60fac1SEmil Velikov 		if (retval & NOUVEAU_DSM_HAS_MUX)
2674c60fac1SEmil Velikov 			has_dsm |= 1;
2684c60fac1SEmil Velikov 		if (retval & NOUVEAU_DSM_HAS_OPT)
2694c60fac1SEmil Velikov 			has_optimus = 1;
2704c60fac1SEmil Velikov 	}
2714c60fac1SEmil Velikov 
272c839d748SDave Airlie 	/* find the optimus DSM or the old v1 DSM */
273c839d748SDave Airlie 	if (has_optimus == 1) {
274c839d748SDave Airlie 		acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME,
275c839d748SDave Airlie 			&buffer);
276c839d748SDave Airlie 		printk(KERN_INFO "VGA switcheroo: detected Optimus DSM method %s handle\n",
277c839d748SDave Airlie 			acpi_method_name);
278c839d748SDave Airlie 		nouveau_dsm_priv.optimus_detected = true;
279c839d748SDave Airlie 		ret = true;
280c839d748SDave Airlie 	} else if (vga_count == 2 && has_dsm && guid_valid) {
281d099230cSPeter Lekensteyn 		acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME,
282d099230cSPeter Lekensteyn 			&buffer);
2836a9ee8afSDave Airlie 		printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n",
2846a9ee8afSDave Airlie 			acpi_method_name);
2856a9ee8afSDave Airlie 		nouveau_dsm_priv.dsm_detected = true;
286f244d8b6SRafael J. Wysocki 		/*
287f244d8b6SRafael J. Wysocki 		 * On some systems hotplug events are generated for the device
288f244d8b6SRafael J. Wysocki 		 * being switched off when _DSM is executed.  They cause ACPI
289f244d8b6SRafael J. Wysocki 		 * hotplug to trigger and attempt to remove the device from
290f244d8b6SRafael J. Wysocki 		 * the system, which causes it to break down.  Prevent that from
291f244d8b6SRafael J. Wysocki 		 * happening by setting the no_hotplug flag for the involved
292f244d8b6SRafael J. Wysocki 		 * ACPI device objects.
293f244d8b6SRafael J. Wysocki 		 */
294f244d8b6SRafael J. Wysocki 		acpi_bus_no_hotplug(nouveau_dsm_priv.dhandle);
295f244d8b6SRafael J. Wysocki 		acpi_bus_no_hotplug(nouveau_dsm_priv.other_handle);
296f19467c5SDave Airlie 		ret = true;
2976a9ee8afSDave Airlie 	}
298f19467c5SDave Airlie 
299f19467c5SDave Airlie 
300f19467c5SDave Airlie 	return ret;
3016a9ee8afSDave Airlie }
3026a9ee8afSDave Airlie 
3036a9ee8afSDave Airlie void nouveau_register_dsm_handler(void)
3046a9ee8afSDave Airlie {
3056a9ee8afSDave Airlie 	bool r;
3066a9ee8afSDave Airlie 
3076a9ee8afSDave Airlie 	r = nouveau_dsm_detect();
3086a9ee8afSDave Airlie 	if (!r)
3096a9ee8afSDave Airlie 		return;
3106a9ee8afSDave Airlie 
3116a9ee8afSDave Airlie 	vga_switcheroo_register_handler(&nouveau_dsm_handler);
3126a9ee8afSDave Airlie }
3136a9ee8afSDave Airlie 
314d099230cSPeter Lekensteyn /* Must be called for Optimus models before the card can be turned off */
315d099230cSPeter Lekensteyn void nouveau_switcheroo_optimus_dsm(void)
316d099230cSPeter Lekensteyn {
317d099230cSPeter Lekensteyn 	u32 result = 0;
318d099230cSPeter Lekensteyn 	if (!nouveau_dsm_priv.optimus_detected)
319d099230cSPeter Lekensteyn 		return;
320d099230cSPeter Lekensteyn 
3215addcf0aSDave Airlie 	nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_FLAGS,
3225addcf0aSDave Airlie 			    0x3, &result);
3235addcf0aSDave Airlie 
3245addcf0aSDave Airlie 	nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_CAPS,
3255addcf0aSDave Airlie 		NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN, &result);
3265addcf0aSDave Airlie 
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 
3723a83f992SRafael J. Wysocki 	dhandle = 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 
4063a83f992SRafael J. Wysocki 	handle = 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