xref: /linux/drivers/gpu/drm/nouveau/nouveau_acpi.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
1b7019ac5SIlia Mirkin // SPDX-License-Identifier: MIT
26ee73861SBen Skeggs #include <linux/pci.h>
36ee73861SBen Skeggs #include <linux/acpi.h>
45a0e3ad6STejun Heo #include <linux/slab.h>
58116188fSDave Airlie #include <linux/mxm-wmi.h>
66a9ee8afSDave Airlie #include <linux/vga_switcheroo.h>
7612a9aabSLinus Torvalds #include <drm/drm_edid.h>
88b48463fSLv Zheng #include <acpi/video.h>
9c0077061SBen Skeggs 
104dc28134SBen Skeggs #include "nouveau_drv.h"
11c0077061SBen Skeggs #include "nouveau_acpi.h"
12c0077061SBen Skeggs 
136ee73861SBen Skeggs #define NOUVEAU_DSM_LED 0x02
146ee73861SBen Skeggs #define NOUVEAU_DSM_LED_STATE 0x00
156ee73861SBen Skeggs #define NOUVEAU_DSM_LED_OFF 0x10
166ee73861SBen Skeggs #define NOUVEAU_DSM_LED_STAMINA 0x11
176ee73861SBen Skeggs #define NOUVEAU_DSM_LED_SPEED 0x12
186ee73861SBen Skeggs 
196ee73861SBen Skeggs #define NOUVEAU_DSM_POWER 0x03
206ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_STATE 0x00
216ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_SPEED 0x01
226ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_STAMINA 0x02
236ee73861SBen Skeggs 
245addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_CAPS 0x1A
255addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_FLAGS 0x1B
265addcf0aSDave Airlie 
275addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 (3 << 24)
285addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_NO_POWERDOWN_PS3 (2 << 24)
295addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED (1)
305addcf0aSDave Airlie 
315addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN (NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 | NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED)
325addcf0aSDave Airlie 
335addcf0aSDave Airlie /* result of the optimus caps function */
345addcf0aSDave Airlie #define OPTIMUS_ENABLED (1 << 0)
355addcf0aSDave Airlie #define OPTIMUS_STATUS_MASK (3 << 3)
365addcf0aSDave Airlie #define OPTIMUS_STATUS_OFF  (0 << 3)
375addcf0aSDave Airlie #define OPTIMUS_STATUS_ON_ENABLED  (1 << 3)
385addcf0aSDave Airlie #define OPTIMUS_STATUS_PWR_STABLE  (3 << 3)
395addcf0aSDave Airlie #define OPTIMUS_DISPLAY_HOTPLUG (1 << 6)
405addcf0aSDave Airlie #define OPTIMUS_CAPS_MASK (7 << 24)
415addcf0aSDave Airlie #define OPTIMUS_DYNAMIC_PWR_CAP (1 << 24)
425addcf0aSDave Airlie 
435addcf0aSDave Airlie #define OPTIMUS_AUDIO_CAPS_MASK (3 << 27)
445addcf0aSDave Airlie #define OPTIMUS_HDA_CODEC_MASK (2 << 27) /* hda bios control */
45d099230cSPeter Lekensteyn 
466a9ee8afSDave Airlie static struct nouveau_dsm_priv {
476a9ee8afSDave Airlie 	bool dsm_detected;
48f19467c5SDave Airlie 	bool optimus_detected;
49cba97805SPeter Wu 	bool optimus_flags_detected;
50692a17dcSPeter Wu 	bool optimus_skip_dsm;
516a9ee8afSDave Airlie 	acpi_handle dhandle;
526a9ee8afSDave Airlie } nouveau_dsm_priv;
536a9ee8afSDave Airlie 
nouveau_is_optimus(void)54c839d748SDave Airlie bool nouveau_is_optimus(void) {
55c839d748SDave Airlie 	return nouveau_dsm_priv.optimus_detected;
56c839d748SDave Airlie }
57c839d748SDave Airlie 
nouveau_is_v1_dsm(void)58c839d748SDave Airlie bool nouveau_is_v1_dsm(void) {
59c839d748SDave Airlie 	return nouveau_dsm_priv.dsm_detected;
60c839d748SDave Airlie }
61c839d748SDave Airlie 
62d0ce7b85SJeff Mahoney #ifdef CONFIG_VGA_SWITCHEROO
6394116f81SAndy Shevchenko static const guid_t nouveau_dsm_muid =
6494116f81SAndy Shevchenko 	GUID_INIT(0x9D95A0A0, 0x0060, 0x4D48,
6594116f81SAndy Shevchenko 		  0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4);
666ee73861SBen Skeggs 
6794116f81SAndy Shevchenko static const guid_t nouveau_op_dsm_muid =
6894116f81SAndy Shevchenko 	GUID_INIT(0xA486D8F8, 0x0BDA, 0x471B,
6994116f81SAndy Shevchenko 		  0xA7, 0x2B, 0x60, 0x42, 0xA6, 0xB5, 0xBE, 0xE0);
70f19467c5SDave Airlie 
nouveau_optimus_dsm(acpi_handle handle,int func,int arg,uint32_t * result)71f19467c5SDave Airlie static int nouveau_optimus_dsm(acpi_handle handle, int func, int arg, uint32_t *result)
72f19467c5SDave Airlie {
73b072e53bSJiang Liu 	int i;
74f19467c5SDave Airlie 	union acpi_object *obj;
75d099230cSPeter Lekensteyn 	char args_buff[4];
76b072e53bSJiang Liu 	union acpi_object argv4 = {
77b072e53bSJiang Liu 		.buffer.type = ACPI_TYPE_BUFFER,
78b072e53bSJiang Liu 		.buffer.length = 4,
79b072e53bSJiang Liu 		.buffer.pointer = args_buff
80b072e53bSJiang Liu 	};
81f19467c5SDave Airlie 
82d099230cSPeter Lekensteyn 	/* ACPI is little endian, AABBCCDD becomes {DD,CC,BB,AA} */
83d099230cSPeter Lekensteyn 	for (i = 0; i < 4; i++)
84d099230cSPeter Lekensteyn 		args_buff[i] = (arg >> i * 8) & 0xFF;
85f19467c5SDave Airlie 
86f19467c5SDave Airlie 	*result = 0;
8794116f81SAndy Shevchenko 	obj = acpi_evaluate_dsm_typed(handle, &nouveau_op_dsm_muid, 0x00000100,
88b072e53bSJiang Liu 				      func, &argv4, ACPI_TYPE_BUFFER);
89b072e53bSJiang Liu 	if (!obj) {
90b072e53bSJiang Liu 		acpi_handle_info(handle, "failed to evaluate _DSM\n");
91b072e53bSJiang Liu 		return AE_ERROR;
92b072e53bSJiang Liu 	} else {
93b072e53bSJiang Liu 		if (obj->buffer.length == 4) {
94f19467c5SDave Airlie 			*result |= obj->buffer.pointer[0];
95f19467c5SDave Airlie 			*result |= (obj->buffer.pointer[1] << 8);
96f19467c5SDave Airlie 			*result |= (obj->buffer.pointer[2] << 16);
97f19467c5SDave Airlie 			*result |= (obj->buffer.pointer[3] << 24);
98f19467c5SDave Airlie 		}
99b072e53bSJiang Liu 		ACPI_FREE(obj);
100f19467c5SDave Airlie 	}
101f19467c5SDave Airlie 
102f19467c5SDave Airlie 	return 0;
103f19467c5SDave Airlie }
104f19467c5SDave Airlie 
105e284175aSJiang Liu /*
106e284175aSJiang Liu  * On some platforms, _DSM(nouveau_op_dsm_muid, func0) has special
107e284175aSJiang Liu  * requirements on the fourth parameter, so a private implementation
108e284175aSJiang Liu  * instead of using acpi_check_dsm().
109e284175aSJiang Liu  */
nouveau_dsm_get_optimus_functions(acpi_handle handle)110a12e78ddSPeter Wu static int nouveau_dsm_get_optimus_functions(acpi_handle handle)
111e284175aSJiang Liu {
112e284175aSJiang Liu 	int result;
113e284175aSJiang Liu 
114e284175aSJiang Liu 	/*
115e284175aSJiang Liu 	 * Function 0 returns a Buffer containing available functions.
116e284175aSJiang Liu 	 * The args parameter is ignored for function 0, so just put 0 in it
117e284175aSJiang Liu 	 */
118e284175aSJiang Liu 	if (nouveau_optimus_dsm(handle, 0, 0, &result))
119e284175aSJiang Liu 		return 0;
120e284175aSJiang Liu 
121e284175aSJiang Liu 	/*
122e284175aSJiang Liu 	 * ACPI Spec v4 9.14.1: if bit 0 is zero, no function is supported.
123e284175aSJiang Liu 	 * If the n-th bit is enabled, function n is supported
124e284175aSJiang Liu 	 */
125a12e78ddSPeter Wu 	if (result & 1 && result & (1 << NOUVEAU_DSM_OPTIMUS_CAPS))
126a12e78ddSPeter Wu 		return result;
127a12e78ddSPeter Wu 	return 0;
128e284175aSJiang Liu }
129e284175aSJiang Liu 
nouveau_dsm(acpi_handle handle,int func,int arg)130b072e53bSJiang Liu static int nouveau_dsm(acpi_handle handle, int func, int arg)
1316a9ee8afSDave Airlie {
132b072e53bSJiang Liu 	int ret = 0;
1336ee73861SBen Skeggs 	union acpi_object *obj;
134b072e53bSJiang Liu 	union acpi_object argv4 = {
135b072e53bSJiang Liu 		.integer.type = ACPI_TYPE_INTEGER,
136b072e53bSJiang Liu 		.integer.value = arg,
137b072e53bSJiang Liu 	};
1386ee73861SBen Skeggs 
13994116f81SAndy Shevchenko 	obj = acpi_evaluate_dsm_typed(handle, &nouveau_dsm_muid, 0x00000102,
140b072e53bSJiang Liu 				      func, &argv4, ACPI_TYPE_INTEGER);
141b072e53bSJiang Liu 	if (!obj) {
142b072e53bSJiang Liu 		acpi_handle_info(handle, "failed to evaluate _DSM\n");
143b072e53bSJiang Liu 		return AE_ERROR;
144b072e53bSJiang Liu 	} else {
1456ee73861SBen Skeggs 		if (obj->integer.value == 0x80000002)
146b072e53bSJiang Liu 			ret = -ENODEV;
147b072e53bSJiang Liu 		ACPI_FREE(obj);
1486ee73861SBen Skeggs 	}
1496ee73861SBen Skeggs 
150b072e53bSJiang Liu 	return ret;
1519075e85fSPeter Lekensteyn }
1529075e85fSPeter Lekensteyn 
nouveau_dsm_switch_mux(acpi_handle handle,int mux_id)1536a9ee8afSDave Airlie static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id)
1546ee73861SBen Skeggs {
155000703f4SDave Airlie 	mxm_wmi_call_mxmx(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0);
1568116188fSDave Airlie 	mxm_wmi_call_mxds(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0);
157b072e53bSJiang Liu 	return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id);
1586ee73861SBen Skeggs }
1596ee73861SBen Skeggs 
nouveau_dsm_set_discrete_state(acpi_handle handle,enum vga_switcheroo_state state)1606a9ee8afSDave Airlie static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state)
1616a9ee8afSDave Airlie {
1626a9ee8afSDave Airlie 	int arg;
1636a9ee8afSDave Airlie 	if (state == VGA_SWITCHEROO_ON)
1646a9ee8afSDave Airlie 		arg = NOUVEAU_DSM_POWER_SPEED;
1656a9ee8afSDave Airlie 	else
1666a9ee8afSDave Airlie 		arg = NOUVEAU_DSM_POWER_STAMINA;
167b072e53bSJiang Liu 	nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg);
1686ee73861SBen Skeggs 	return 0;
1696ee73861SBen Skeggs }
1706ee73861SBen Skeggs 
nouveau_dsm_switchto(enum vga_switcheroo_client_id id)1716a9ee8afSDave Airlie static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id)
1726ee73861SBen Skeggs {
173c839d748SDave Airlie 	if (!nouveau_dsm_priv.dsm_detected)
174d099230cSPeter Lekensteyn 		return 0;
1756a9ee8afSDave Airlie 	if (id == VGA_SWITCHEROO_IGD)
176fc5ea29dSDave Airlie 		return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA);
1776a9ee8afSDave Airlie 	else
178fc5ea29dSDave Airlie 		return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_SPEED);
1796a9ee8afSDave Airlie }
1806ee73861SBen Skeggs 
nouveau_dsm_power_state(enum vga_switcheroo_client_id id,enum vga_switcheroo_state state)1816a9ee8afSDave Airlie static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id,
1826a9ee8afSDave Airlie 				   enum vga_switcheroo_state state)
1836a9ee8afSDave Airlie {
1846a9ee8afSDave Airlie 	if (id == VGA_SWITCHEROO_IGD)
1856a9ee8afSDave Airlie 		return 0;
1866a9ee8afSDave Airlie 
187d099230cSPeter Lekensteyn 	/* Optimus laptops have the card already disabled in
188d099230cSPeter Lekensteyn 	 * nouveau_switcheroo_set_state */
189c839d748SDave Airlie 	if (!nouveau_dsm_priv.dsm_detected)
190d099230cSPeter Lekensteyn 		return 0;
191d099230cSPeter Lekensteyn 
192fc5ea29dSDave Airlie 	return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state);
1936a9ee8afSDave Airlie }
1946a9ee8afSDave Airlie 
nouveau_dsm_get_client_id(struct pci_dev * pdev)195f43cda5cSLuc Van Oostenryck static enum vga_switcheroo_client_id nouveau_dsm_get_client_id(struct pci_dev *pdev)
1966a9ee8afSDave Airlie {
197d1fbd923SDave Airlie 	/* easy option one - intel vendor ID means Integrated */
198d1fbd923SDave Airlie 	if (pdev->vendor == PCI_VENDOR_ID_INTEL)
1996a9ee8afSDave Airlie 		return VGA_SWITCHEROO_IGD;
200d1fbd923SDave Airlie 
201d1fbd923SDave Airlie 	/* is this device on Bus 0? - this may need improving */
202d1fbd923SDave Airlie 	if (pdev->bus->number == 0)
203d1fbd923SDave Airlie 		return VGA_SWITCHEROO_IGD;
204d1fbd923SDave Airlie 
2056a9ee8afSDave Airlie 	return VGA_SWITCHEROO_DIS;
2066a9ee8afSDave Airlie }
2076a9ee8afSDave Airlie 
2085d170139SLukas Wunner static const struct vga_switcheroo_handler nouveau_dsm_handler = {
2096a9ee8afSDave Airlie 	.switchto = nouveau_dsm_switchto,
2106a9ee8afSDave Airlie 	.power_state = nouveau_dsm_power_state,
2116a9ee8afSDave Airlie 	.get_client_id = nouveau_dsm_get_client_id,
2126a9ee8afSDave Airlie };
2136a9ee8afSDave Airlie 
nouveau_dsm_pci_probe(struct pci_dev * pdev,acpi_handle * dhandle_out,bool * has_mux,bool * has_opt,bool * has_opt_flags,bool * has_pr3)214df42194aSPeter Wu static void nouveau_dsm_pci_probe(struct pci_dev *pdev, acpi_handle *dhandle_out,
215cba97805SPeter Wu 				  bool *has_mux, bool *has_opt,
216692a17dcSPeter Wu 				  bool *has_opt_flags, bool *has_pr3)
2176a9ee8afSDave Airlie {
218187b5b5dSZhang Rui 	acpi_handle dhandle;
219df42194aSPeter Wu 	bool supports_mux;
220a12e78ddSPeter Wu 	int optimus_funcs;
221ccfc2d5cSKai-Heng Feng 	struct pci_dev *parent_pdev;
222ccfc2d5cSKai-Heng Feng 
22311d24327SRatchanan Srirattanamet 	if (pdev->vendor != PCI_VENDOR_ID_NVIDIA)
22411d24327SRatchanan Srirattanamet 		return;
22511d24327SRatchanan Srirattanamet 
226ccfc2d5cSKai-Heng Feng 	*has_pr3 = false;
227ccfc2d5cSKai-Heng Feng 	parent_pdev = pci_upstream_bridge(pdev);
228ccfc2d5cSKai-Heng Feng 	if (parent_pdev) {
229ccfc2d5cSKai-Heng Feng 		if (parent_pdev->bridge_d3)
230ccfc2d5cSKai-Heng Feng 			*has_pr3 = pci_pr3_present(parent_pdev);
231ccfc2d5cSKai-Heng Feng 		else
232ccfc2d5cSKai-Heng Feng 			pci_d3cold_disable(pdev);
233ccfc2d5cSKai-Heng Feng 	}
2346a9ee8afSDave Airlie 
2353a83f992SRafael J. Wysocki 	dhandle = ACPI_HANDLE(&pdev->dev);
2366a9ee8afSDave Airlie 	if (!dhandle)
237df42194aSPeter Wu 		return;
238afeb3e11SDave Airlie 
239f91ce35eSBjorn Helgaas 	if (!acpi_has_method(dhandle, "_DSM"))
240df42194aSPeter Wu 		return;
241f91ce35eSBjorn Helgaas 
24294116f81SAndy Shevchenko 	supports_mux = acpi_check_dsm(dhandle, &nouveau_dsm_muid, 0x00000102,
243df42194aSPeter Wu 				      1 << NOUVEAU_DSM_POWER);
244a12e78ddSPeter Wu 	optimus_funcs = nouveau_dsm_get_optimus_functions(dhandle);
2456ee73861SBen Skeggs 
246df42194aSPeter Wu 	/* Does not look like a Nvidia device. */
247a12e78ddSPeter Wu 	if (!supports_mux && !optimus_funcs)
248df42194aSPeter Wu 		return;
249f19467c5SDave Airlie 
250df42194aSPeter Wu 	*dhandle_out = dhandle;
251df42194aSPeter Wu 	*has_mux = supports_mux;
252a12e78ddSPeter Wu 	*has_opt = !!optimus_funcs;
253cba97805SPeter Wu 	*has_opt_flags = optimus_funcs & (1 << NOUVEAU_DSM_OPTIMUS_FLAGS);
254df42194aSPeter Wu 
255a12e78ddSPeter Wu 	if (optimus_funcs) {
2565addcf0aSDave Airlie 		uint32_t result;
2575addcf0aSDave Airlie 		nouveau_optimus_dsm(dhandle, NOUVEAU_DSM_OPTIMUS_CAPS, 0,
2585addcf0aSDave Airlie 				    &result);
2595addcf0aSDave Airlie 		dev_info(&pdev->dev, "optimus capabilities: %s, status %s%s\n",
2605addcf0aSDave Airlie 			 (result & OPTIMUS_ENABLED) ? "enabled" : "disabled",
2615addcf0aSDave Airlie 			 (result & OPTIMUS_DYNAMIC_PWR_CAP) ? "dynamic power, " : "",
2625addcf0aSDave Airlie 			 (result & OPTIMUS_HDA_CODEC_MASK) ? "hda bios codec supported" : "");
2635addcf0aSDave Airlie 	}
2646ee73861SBen Skeggs }
2656a9ee8afSDave Airlie 
nouveau_dsm_detect(void)2666a9ee8afSDave Airlie static bool nouveau_dsm_detect(void)
2676a9ee8afSDave Airlie {
2686a9ee8afSDave Airlie 	char acpi_method_name[255] = { 0 };
2696a9ee8afSDave Airlie 	struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
2706a9ee8afSDave Airlie 	struct pci_dev *pdev = NULL;
271df42194aSPeter Wu 	acpi_handle dhandle = NULL;
272df42194aSPeter Wu 	bool has_mux = false;
273df42194aSPeter Wu 	bool has_optimus = false;
274cba97805SPeter Wu 	bool has_optimus_flags = false;
275692a17dcSPeter Wu 	bool has_power_resources = false;
2766a9ee8afSDave Airlie 	int vga_count = 0;
2778116188fSDave Airlie 	bool guid_valid;
278f19467c5SDave Airlie 	bool ret = false;
2798116188fSDave Airlie 
280f19467c5SDave Airlie 	/* lookup the MXM GUID */
2818116188fSDave Airlie 	guid_valid = mxm_wmi_supported();
2828116188fSDave Airlie 
283f19467c5SDave Airlie 	if (guid_valid)
284f19467c5SDave Airlie 		printk("MXM: GUID detected in BIOS\n");
285afeb3e11SDave Airlie 
286f19467c5SDave Airlie 	/* now do DSM detection */
287*c213ed39SSui Jingfeng 	while ((pdev = pci_get_base_class(PCI_BASE_CLASS_DISPLAY, pdev))) {
288*c213ed39SSui Jingfeng 		if ((pdev->class != PCI_CLASS_DISPLAY_VGA << 8) &&
289*c213ed39SSui Jingfeng 		    (pdev->class != PCI_CLASS_DISPLAY_3D << 8))
290*c213ed39SSui Jingfeng 			continue;
2916a9ee8afSDave Airlie 
2924c60fac1SEmil Velikov 		vga_count++;
2934c60fac1SEmil Velikov 
294cba97805SPeter Wu 		nouveau_dsm_pci_probe(pdev, &dhandle, &has_mux, &has_optimus,
295692a17dcSPeter Wu 				      &has_optimus_flags, &has_power_resources);
2964c60fac1SEmil Velikov 	}
2974c60fac1SEmil Velikov 
298c839d748SDave Airlie 	/* find the optimus DSM or the old v1 DSM */
299df42194aSPeter Wu 	if (has_optimus) {
300df42194aSPeter Wu 		nouveau_dsm_priv.dhandle = dhandle;
301c839d748SDave Airlie 		acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME,
302c839d748SDave Airlie 			&buffer);
3038dfe162aSJoe Perches 		pr_info("VGA switcheroo: detected Optimus DSM method %s handle\n",
304c839d748SDave Airlie 			acpi_method_name);
305692a17dcSPeter Wu 		if (has_power_resources)
306692a17dcSPeter Wu 			pr_info("nouveau: detected PR support, will not use DSM\n");
307c839d748SDave Airlie 		nouveau_dsm_priv.optimus_detected = true;
308cba97805SPeter Wu 		nouveau_dsm_priv.optimus_flags_detected = has_optimus_flags;
309692a17dcSPeter Wu 		nouveau_dsm_priv.optimus_skip_dsm = has_power_resources;
310c839d748SDave Airlie 		ret = true;
311df42194aSPeter Wu 	} else if (vga_count == 2 && has_mux && guid_valid) {
312df42194aSPeter Wu 		nouveau_dsm_priv.dhandle = dhandle;
313d099230cSPeter Lekensteyn 		acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME,
314d099230cSPeter Lekensteyn 			&buffer);
3158dfe162aSJoe Perches 		pr_info("VGA switcheroo: detected DSM switching method %s handle\n",
3166a9ee8afSDave Airlie 			acpi_method_name);
3176a9ee8afSDave Airlie 		nouveau_dsm_priv.dsm_detected = true;
318f19467c5SDave Airlie 		ret = true;
3196a9ee8afSDave Airlie 	}
320f19467c5SDave Airlie 
321f19467c5SDave Airlie 
322f19467c5SDave Airlie 	return ret;
3236a9ee8afSDave Airlie }
3246a9ee8afSDave Airlie 
nouveau_register_dsm_handler(void)3256a9ee8afSDave Airlie void nouveau_register_dsm_handler(void)
3266a9ee8afSDave Airlie {
3276a9ee8afSDave Airlie 	bool r;
3286a9ee8afSDave Airlie 
3296a9ee8afSDave Airlie 	r = nouveau_dsm_detect();
3306a9ee8afSDave Airlie 	if (!r)
3316a9ee8afSDave Airlie 		return;
3326a9ee8afSDave Airlie 
333156d7d41SLukas Wunner 	vga_switcheroo_register_handler(&nouveau_dsm_handler, 0);
3346a9ee8afSDave Airlie }
3356a9ee8afSDave Airlie 
336d099230cSPeter Lekensteyn /* Must be called for Optimus models before the card can be turned off */
nouveau_switcheroo_optimus_dsm(void)337d099230cSPeter Lekensteyn void nouveau_switcheroo_optimus_dsm(void)
338d099230cSPeter Lekensteyn {
339d099230cSPeter Lekensteyn 	u32 result = 0;
340692a17dcSPeter Wu 	if (!nouveau_dsm_priv.optimus_detected || nouveau_dsm_priv.optimus_skip_dsm)
341d099230cSPeter Lekensteyn 		return;
342d099230cSPeter Lekensteyn 
343cba97805SPeter Wu 	if (nouveau_dsm_priv.optimus_flags_detected)
3445addcf0aSDave Airlie 		nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_FLAGS,
3455addcf0aSDave Airlie 				    0x3, &result);
3465addcf0aSDave Airlie 
3475addcf0aSDave Airlie 	nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_CAPS,
3485addcf0aSDave Airlie 		NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN, &result);
3495addcf0aSDave Airlie 
350d099230cSPeter Lekensteyn }
351d099230cSPeter Lekensteyn 
nouveau_unregister_dsm_handler(void)3526a9ee8afSDave Airlie void nouveau_unregister_dsm_handler(void)
3536a9ee8afSDave Airlie {
3542f3787aaSAndreas Heider 	if (nouveau_dsm_priv.optimus_detected || nouveau_dsm_priv.dsm_detected)
3556a9ee8afSDave Airlie 		vga_switcheroo_unregister_handler();
3566a9ee8afSDave Airlie }
357d0ce7b85SJeff Mahoney #else
nouveau_register_dsm_handler(void)358d0ce7b85SJeff Mahoney void nouveau_register_dsm_handler(void) {}
nouveau_unregister_dsm_handler(void)359d0ce7b85SJeff Mahoney void nouveau_unregister_dsm_handler(void) {}
nouveau_switcheroo_optimus_dsm(void)360d0ce7b85SJeff Mahoney void nouveau_switcheroo_optimus_dsm(void) {}
361d0ce7b85SJeff Mahoney #endif
362afeb3e11SDave Airlie 
363c0077061SBen Skeggs void *
nouveau_acpi_edid(struct drm_device * dev,struct drm_connector * connector)364a6ed76d7SBen Skeggs nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector)
365a6ed76d7SBen Skeggs {
366a6ed76d7SBen Skeggs 	struct acpi_device *acpidev;
367a6ed76d7SBen Skeggs 	int type, ret;
368a6ed76d7SBen Skeggs 	void *edid;
369a6ed76d7SBen Skeggs 
370a6ed76d7SBen Skeggs 	switch (connector->connector_type) {
371a6ed76d7SBen Skeggs 	case DRM_MODE_CONNECTOR_LVDS:
372a6ed76d7SBen Skeggs 	case DRM_MODE_CONNECTOR_eDP:
373a6ed76d7SBen Skeggs 		type = ACPI_VIDEO_DISPLAY_LCD;
374a6ed76d7SBen Skeggs 		break;
375a6ed76d7SBen Skeggs 	default:
376c0077061SBen Skeggs 		return NULL;
377a6ed76d7SBen Skeggs 	}
378a6ed76d7SBen Skeggs 
379ae364fd9SRafael J. Wysocki 	acpidev = ACPI_COMPANION(dev->dev);
380ae364fd9SRafael J. Wysocki 	if (!acpidev)
381c0077061SBen Skeggs 		return NULL;
382a6ed76d7SBen Skeggs 
383a6ed76d7SBen Skeggs 	ret = acpi_video_get_edid(acpidev, type, -1, &edid);
384a6ed76d7SBen Skeggs 	if (ret < 0)
385c0077061SBen Skeggs 		return NULL;
386a6ed76d7SBen Skeggs 
387c0077061SBen Skeggs 	return kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
388a6ed76d7SBen Skeggs }
3897f908d33SHans de Goede 
nouveau_acpi_video_backlight_use_native(void)3907f908d33SHans de Goede bool nouveau_acpi_video_backlight_use_native(void)
3917f908d33SHans de Goede {
3927f908d33SHans de Goede 	return acpi_video_backlight_use_native();
3937f908d33SHans de Goede }
394c0533838SHans de Goede 
nouveau_acpi_video_register_backlight(void)395c0533838SHans de Goede void nouveau_acpi_video_register_backlight(void)
396c0533838SHans de Goede {
397c0533838SHans de Goede 	acpi_video_register_backlight();
398c0533838SHans de Goede }
399