xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/nouveau/nouveau_acpi.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: MIT
2*4882a593Smuzhiyun #include <linux/pci.h>
3*4882a593Smuzhiyun #include <linux/acpi.h>
4*4882a593Smuzhiyun #include <linux/slab.h>
5*4882a593Smuzhiyun #include <linux/mxm-wmi.h>
6*4882a593Smuzhiyun #include <linux/vga_switcheroo.h>
7*4882a593Smuzhiyun #include <drm/drm_edid.h>
8*4882a593Smuzhiyun #include <acpi/video.h>
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include "nouveau_drv.h"
11*4882a593Smuzhiyun #include "nouveau_acpi.h"
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun #define NOUVEAU_DSM_LED 0x02
14*4882a593Smuzhiyun #define NOUVEAU_DSM_LED_STATE 0x00
15*4882a593Smuzhiyun #define NOUVEAU_DSM_LED_OFF 0x10
16*4882a593Smuzhiyun #define NOUVEAU_DSM_LED_STAMINA 0x11
17*4882a593Smuzhiyun #define NOUVEAU_DSM_LED_SPEED 0x12
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun #define NOUVEAU_DSM_POWER 0x03
20*4882a593Smuzhiyun #define NOUVEAU_DSM_POWER_STATE 0x00
21*4882a593Smuzhiyun #define NOUVEAU_DSM_POWER_SPEED 0x01
22*4882a593Smuzhiyun #define NOUVEAU_DSM_POWER_STAMINA 0x02
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #define NOUVEAU_DSM_OPTIMUS_CAPS 0x1A
25*4882a593Smuzhiyun #define NOUVEAU_DSM_OPTIMUS_FLAGS 0x1B
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun #define NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 (3 << 24)
28*4882a593Smuzhiyun #define NOUVEAU_DSM_OPTIMUS_NO_POWERDOWN_PS3 (2 << 24)
29*4882a593Smuzhiyun #define NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED (1)
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun #define NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN (NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 | NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED)
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun /* result of the optimus caps function */
34*4882a593Smuzhiyun #define OPTIMUS_ENABLED (1 << 0)
35*4882a593Smuzhiyun #define OPTIMUS_STATUS_MASK (3 << 3)
36*4882a593Smuzhiyun #define OPTIMUS_STATUS_OFF  (0 << 3)
37*4882a593Smuzhiyun #define OPTIMUS_STATUS_ON_ENABLED  (1 << 3)
38*4882a593Smuzhiyun #define OPTIMUS_STATUS_PWR_STABLE  (3 << 3)
39*4882a593Smuzhiyun #define OPTIMUS_DISPLAY_HOTPLUG (1 << 6)
40*4882a593Smuzhiyun #define OPTIMUS_CAPS_MASK (7 << 24)
41*4882a593Smuzhiyun #define OPTIMUS_DYNAMIC_PWR_CAP (1 << 24)
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun #define OPTIMUS_AUDIO_CAPS_MASK (3 << 27)
44*4882a593Smuzhiyun #define OPTIMUS_HDA_CODEC_MASK (2 << 27) /* hda bios control */
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun static struct nouveau_dsm_priv {
47*4882a593Smuzhiyun 	bool dsm_detected;
48*4882a593Smuzhiyun 	bool optimus_detected;
49*4882a593Smuzhiyun 	bool optimus_flags_detected;
50*4882a593Smuzhiyun 	bool optimus_skip_dsm;
51*4882a593Smuzhiyun 	acpi_handle dhandle;
52*4882a593Smuzhiyun } nouveau_dsm_priv;
53*4882a593Smuzhiyun 
nouveau_is_optimus(void)54*4882a593Smuzhiyun bool nouveau_is_optimus(void) {
55*4882a593Smuzhiyun 	return nouveau_dsm_priv.optimus_detected;
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun 
nouveau_is_v1_dsm(void)58*4882a593Smuzhiyun bool nouveau_is_v1_dsm(void) {
59*4882a593Smuzhiyun 	return nouveau_dsm_priv.dsm_detected;
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun #ifdef CONFIG_VGA_SWITCHEROO
63*4882a593Smuzhiyun static const guid_t nouveau_dsm_muid =
64*4882a593Smuzhiyun 	GUID_INIT(0x9D95A0A0, 0x0060, 0x4D48,
65*4882a593Smuzhiyun 		  0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4);
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun static const guid_t nouveau_op_dsm_muid =
68*4882a593Smuzhiyun 	GUID_INIT(0xA486D8F8, 0x0BDA, 0x471B,
69*4882a593Smuzhiyun 		  0xA7, 0x2B, 0x60, 0x42, 0xA6, 0xB5, 0xBE, 0xE0);
70*4882a593Smuzhiyun 
nouveau_optimus_dsm(acpi_handle handle,int func,int arg,uint32_t * result)71*4882a593Smuzhiyun static int nouveau_optimus_dsm(acpi_handle handle, int func, int arg, uint32_t *result)
72*4882a593Smuzhiyun {
73*4882a593Smuzhiyun 	int i;
74*4882a593Smuzhiyun 	union acpi_object *obj;
75*4882a593Smuzhiyun 	char args_buff[4];
76*4882a593Smuzhiyun 	union acpi_object argv4 = {
77*4882a593Smuzhiyun 		.buffer.type = ACPI_TYPE_BUFFER,
78*4882a593Smuzhiyun 		.buffer.length = 4,
79*4882a593Smuzhiyun 		.buffer.pointer = args_buff
80*4882a593Smuzhiyun 	};
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 	/* ACPI is little endian, AABBCCDD becomes {DD,CC,BB,AA} */
83*4882a593Smuzhiyun 	for (i = 0; i < 4; i++)
84*4882a593Smuzhiyun 		args_buff[i] = (arg >> i * 8) & 0xFF;
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 	*result = 0;
87*4882a593Smuzhiyun 	obj = acpi_evaluate_dsm_typed(handle, &nouveau_op_dsm_muid, 0x00000100,
88*4882a593Smuzhiyun 				      func, &argv4, ACPI_TYPE_BUFFER);
89*4882a593Smuzhiyun 	if (!obj) {
90*4882a593Smuzhiyun 		acpi_handle_info(handle, "failed to evaluate _DSM\n");
91*4882a593Smuzhiyun 		return AE_ERROR;
92*4882a593Smuzhiyun 	} else {
93*4882a593Smuzhiyun 		if (obj->buffer.length == 4) {
94*4882a593Smuzhiyun 			*result |= obj->buffer.pointer[0];
95*4882a593Smuzhiyun 			*result |= (obj->buffer.pointer[1] << 8);
96*4882a593Smuzhiyun 			*result |= (obj->buffer.pointer[2] << 16);
97*4882a593Smuzhiyun 			*result |= (obj->buffer.pointer[3] << 24);
98*4882a593Smuzhiyun 		}
99*4882a593Smuzhiyun 		ACPI_FREE(obj);
100*4882a593Smuzhiyun 	}
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun 	return 0;
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun /*
106*4882a593Smuzhiyun  * On some platforms, _DSM(nouveau_op_dsm_muid, func0) has special
107*4882a593Smuzhiyun  * requirements on the fourth parameter, so a private implementation
108*4882a593Smuzhiyun  * instead of using acpi_check_dsm().
109*4882a593Smuzhiyun  */
nouveau_dsm_get_optimus_functions(acpi_handle handle)110*4882a593Smuzhiyun static int nouveau_dsm_get_optimus_functions(acpi_handle handle)
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun 	int result;
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	/*
115*4882a593Smuzhiyun 	 * Function 0 returns a Buffer containing available functions.
116*4882a593Smuzhiyun 	 * The args parameter is ignored for function 0, so just put 0 in it
117*4882a593Smuzhiyun 	 */
118*4882a593Smuzhiyun 	if (nouveau_optimus_dsm(handle, 0, 0, &result))
119*4882a593Smuzhiyun 		return 0;
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	/*
122*4882a593Smuzhiyun 	 * ACPI Spec v4 9.14.1: if bit 0 is zero, no function is supported.
123*4882a593Smuzhiyun 	 * If the n-th bit is enabled, function n is supported
124*4882a593Smuzhiyun 	 */
125*4882a593Smuzhiyun 	if (result & 1 && result & (1 << NOUVEAU_DSM_OPTIMUS_CAPS))
126*4882a593Smuzhiyun 		return result;
127*4882a593Smuzhiyun 	return 0;
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun 
nouveau_dsm(acpi_handle handle,int func,int arg)130*4882a593Smuzhiyun static int nouveau_dsm(acpi_handle handle, int func, int arg)
131*4882a593Smuzhiyun {
132*4882a593Smuzhiyun 	int ret = 0;
133*4882a593Smuzhiyun 	union acpi_object *obj;
134*4882a593Smuzhiyun 	union acpi_object argv4 = {
135*4882a593Smuzhiyun 		.integer.type = ACPI_TYPE_INTEGER,
136*4882a593Smuzhiyun 		.integer.value = arg,
137*4882a593Smuzhiyun 	};
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	obj = acpi_evaluate_dsm_typed(handle, &nouveau_dsm_muid, 0x00000102,
140*4882a593Smuzhiyun 				      func, &argv4, ACPI_TYPE_INTEGER);
141*4882a593Smuzhiyun 	if (!obj) {
142*4882a593Smuzhiyun 		acpi_handle_info(handle, "failed to evaluate _DSM\n");
143*4882a593Smuzhiyun 		return AE_ERROR;
144*4882a593Smuzhiyun 	} else {
145*4882a593Smuzhiyun 		if (obj->integer.value == 0x80000002)
146*4882a593Smuzhiyun 			ret = -ENODEV;
147*4882a593Smuzhiyun 		ACPI_FREE(obj);
148*4882a593Smuzhiyun 	}
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	return ret;
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun 
nouveau_dsm_switch_mux(acpi_handle handle,int mux_id)153*4882a593Smuzhiyun static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id)
154*4882a593Smuzhiyun {
155*4882a593Smuzhiyun 	mxm_wmi_call_mxmx(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0);
156*4882a593Smuzhiyun 	mxm_wmi_call_mxds(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0);
157*4882a593Smuzhiyun 	return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id);
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun 
nouveau_dsm_set_discrete_state(acpi_handle handle,enum vga_switcheroo_state state)160*4882a593Smuzhiyun static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state)
161*4882a593Smuzhiyun {
162*4882a593Smuzhiyun 	int arg;
163*4882a593Smuzhiyun 	if (state == VGA_SWITCHEROO_ON)
164*4882a593Smuzhiyun 		arg = NOUVEAU_DSM_POWER_SPEED;
165*4882a593Smuzhiyun 	else
166*4882a593Smuzhiyun 		arg = NOUVEAU_DSM_POWER_STAMINA;
167*4882a593Smuzhiyun 	nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg);
168*4882a593Smuzhiyun 	return 0;
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun 
nouveau_dsm_switchto(enum vga_switcheroo_client_id id)171*4882a593Smuzhiyun static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id)
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun 	if (!nouveau_dsm_priv.dsm_detected)
174*4882a593Smuzhiyun 		return 0;
175*4882a593Smuzhiyun 	if (id == VGA_SWITCHEROO_IGD)
176*4882a593Smuzhiyun 		return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA);
177*4882a593Smuzhiyun 	else
178*4882a593Smuzhiyun 		return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_SPEED);
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun 
nouveau_dsm_power_state(enum vga_switcheroo_client_id id,enum vga_switcheroo_state state)181*4882a593Smuzhiyun static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id,
182*4882a593Smuzhiyun 				   enum vga_switcheroo_state state)
183*4882a593Smuzhiyun {
184*4882a593Smuzhiyun 	if (id == VGA_SWITCHEROO_IGD)
185*4882a593Smuzhiyun 		return 0;
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 	/* Optimus laptops have the card already disabled in
188*4882a593Smuzhiyun 	 * nouveau_switcheroo_set_state */
189*4882a593Smuzhiyun 	if (!nouveau_dsm_priv.dsm_detected)
190*4882a593Smuzhiyun 		return 0;
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state);
193*4882a593Smuzhiyun }
194*4882a593Smuzhiyun 
nouveau_dsm_get_client_id(struct pci_dev * pdev)195*4882a593Smuzhiyun static enum vga_switcheroo_client_id nouveau_dsm_get_client_id(struct pci_dev *pdev)
196*4882a593Smuzhiyun {
197*4882a593Smuzhiyun 	/* easy option one - intel vendor ID means Integrated */
198*4882a593Smuzhiyun 	if (pdev->vendor == PCI_VENDOR_ID_INTEL)
199*4882a593Smuzhiyun 		return VGA_SWITCHEROO_IGD;
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	/* is this device on Bus 0? - this may need improving */
202*4882a593Smuzhiyun 	if (pdev->bus->number == 0)
203*4882a593Smuzhiyun 		return VGA_SWITCHEROO_IGD;
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	return VGA_SWITCHEROO_DIS;
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun static const struct vga_switcheroo_handler nouveau_dsm_handler = {
209*4882a593Smuzhiyun 	.switchto = nouveau_dsm_switchto,
210*4882a593Smuzhiyun 	.power_state = nouveau_dsm_power_state,
211*4882a593Smuzhiyun 	.get_client_id = nouveau_dsm_get_client_id,
212*4882a593Smuzhiyun };
213*4882a593Smuzhiyun 
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)214*4882a593Smuzhiyun static void nouveau_dsm_pci_probe(struct pci_dev *pdev, acpi_handle *dhandle_out,
215*4882a593Smuzhiyun 				  bool *has_mux, bool *has_opt,
216*4882a593Smuzhiyun 				  bool *has_opt_flags, bool *has_pr3)
217*4882a593Smuzhiyun {
218*4882a593Smuzhiyun 	acpi_handle dhandle;
219*4882a593Smuzhiyun 	bool supports_mux;
220*4882a593Smuzhiyun 	int optimus_funcs;
221*4882a593Smuzhiyun 	struct pci_dev *parent_pdev;
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	*has_pr3 = false;
224*4882a593Smuzhiyun 	parent_pdev = pci_upstream_bridge(pdev);
225*4882a593Smuzhiyun 	if (parent_pdev) {
226*4882a593Smuzhiyun 		if (parent_pdev->bridge_d3)
227*4882a593Smuzhiyun 			*has_pr3 = pci_pr3_present(parent_pdev);
228*4882a593Smuzhiyun 		else
229*4882a593Smuzhiyun 			pci_d3cold_disable(pdev);
230*4882a593Smuzhiyun 	}
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	dhandle = ACPI_HANDLE(&pdev->dev);
233*4882a593Smuzhiyun 	if (!dhandle)
234*4882a593Smuzhiyun 		return;
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 	if (!acpi_has_method(dhandle, "_DSM"))
237*4882a593Smuzhiyun 		return;
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	supports_mux = acpi_check_dsm(dhandle, &nouveau_dsm_muid, 0x00000102,
240*4882a593Smuzhiyun 				      1 << NOUVEAU_DSM_POWER);
241*4882a593Smuzhiyun 	optimus_funcs = nouveau_dsm_get_optimus_functions(dhandle);
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 	/* Does not look like a Nvidia device. */
244*4882a593Smuzhiyun 	if (!supports_mux && !optimus_funcs)
245*4882a593Smuzhiyun 		return;
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 	*dhandle_out = dhandle;
248*4882a593Smuzhiyun 	*has_mux = supports_mux;
249*4882a593Smuzhiyun 	*has_opt = !!optimus_funcs;
250*4882a593Smuzhiyun 	*has_opt_flags = optimus_funcs & (1 << NOUVEAU_DSM_OPTIMUS_FLAGS);
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 	if (optimus_funcs) {
253*4882a593Smuzhiyun 		uint32_t result;
254*4882a593Smuzhiyun 		nouveau_optimus_dsm(dhandle, NOUVEAU_DSM_OPTIMUS_CAPS, 0,
255*4882a593Smuzhiyun 				    &result);
256*4882a593Smuzhiyun 		dev_info(&pdev->dev, "optimus capabilities: %s, status %s%s\n",
257*4882a593Smuzhiyun 			 (result & OPTIMUS_ENABLED) ? "enabled" : "disabled",
258*4882a593Smuzhiyun 			 (result & OPTIMUS_DYNAMIC_PWR_CAP) ? "dynamic power, " : "",
259*4882a593Smuzhiyun 			 (result & OPTIMUS_HDA_CODEC_MASK) ? "hda bios codec supported" : "");
260*4882a593Smuzhiyun 	}
261*4882a593Smuzhiyun }
262*4882a593Smuzhiyun 
nouveau_dsm_detect(void)263*4882a593Smuzhiyun static bool nouveau_dsm_detect(void)
264*4882a593Smuzhiyun {
265*4882a593Smuzhiyun 	char acpi_method_name[255] = { 0 };
266*4882a593Smuzhiyun 	struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
267*4882a593Smuzhiyun 	struct pci_dev *pdev = NULL;
268*4882a593Smuzhiyun 	acpi_handle dhandle = NULL;
269*4882a593Smuzhiyun 	bool has_mux = false;
270*4882a593Smuzhiyun 	bool has_optimus = false;
271*4882a593Smuzhiyun 	bool has_optimus_flags = false;
272*4882a593Smuzhiyun 	bool has_power_resources = false;
273*4882a593Smuzhiyun 	int vga_count = 0;
274*4882a593Smuzhiyun 	bool guid_valid;
275*4882a593Smuzhiyun 	bool ret = false;
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 	/* lookup the MXM GUID */
278*4882a593Smuzhiyun 	guid_valid = mxm_wmi_supported();
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun 	if (guid_valid)
281*4882a593Smuzhiyun 		printk("MXM: GUID detected in BIOS\n");
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 	/* now do DSM detection */
284*4882a593Smuzhiyun 	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
285*4882a593Smuzhiyun 		vga_count++;
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 		nouveau_dsm_pci_probe(pdev, &dhandle, &has_mux, &has_optimus,
288*4882a593Smuzhiyun 				      &has_optimus_flags, &has_power_resources);
289*4882a593Smuzhiyun 	}
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_3D << 8, pdev)) != NULL) {
292*4882a593Smuzhiyun 		vga_count++;
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun 		nouveau_dsm_pci_probe(pdev, &dhandle, &has_mux, &has_optimus,
295*4882a593Smuzhiyun 				      &has_optimus_flags, &has_power_resources);
296*4882a593Smuzhiyun 	}
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 	/* find the optimus DSM or the old v1 DSM */
299*4882a593Smuzhiyun 	if (has_optimus) {
300*4882a593Smuzhiyun 		nouveau_dsm_priv.dhandle = dhandle;
301*4882a593Smuzhiyun 		acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME,
302*4882a593Smuzhiyun 			&buffer);
303*4882a593Smuzhiyun 		pr_info("VGA switcheroo: detected Optimus DSM method %s handle\n",
304*4882a593Smuzhiyun 			acpi_method_name);
305*4882a593Smuzhiyun 		if (has_power_resources)
306*4882a593Smuzhiyun 			pr_info("nouveau: detected PR support, will not use DSM\n");
307*4882a593Smuzhiyun 		nouveau_dsm_priv.optimus_detected = true;
308*4882a593Smuzhiyun 		nouveau_dsm_priv.optimus_flags_detected = has_optimus_flags;
309*4882a593Smuzhiyun 		nouveau_dsm_priv.optimus_skip_dsm = has_power_resources;
310*4882a593Smuzhiyun 		ret = true;
311*4882a593Smuzhiyun 	} else if (vga_count == 2 && has_mux && guid_valid) {
312*4882a593Smuzhiyun 		nouveau_dsm_priv.dhandle = dhandle;
313*4882a593Smuzhiyun 		acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME,
314*4882a593Smuzhiyun 			&buffer);
315*4882a593Smuzhiyun 		pr_info("VGA switcheroo: detected DSM switching method %s handle\n",
316*4882a593Smuzhiyun 			acpi_method_name);
317*4882a593Smuzhiyun 		nouveau_dsm_priv.dsm_detected = true;
318*4882a593Smuzhiyun 		ret = true;
319*4882a593Smuzhiyun 	}
320*4882a593Smuzhiyun 
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	return ret;
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun 
nouveau_register_dsm_handler(void)325*4882a593Smuzhiyun void nouveau_register_dsm_handler(void)
326*4882a593Smuzhiyun {
327*4882a593Smuzhiyun 	bool r;
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	r = nouveau_dsm_detect();
330*4882a593Smuzhiyun 	if (!r)
331*4882a593Smuzhiyun 		return;
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun 	vga_switcheroo_register_handler(&nouveau_dsm_handler, 0);
334*4882a593Smuzhiyun }
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun /* Must be called for Optimus models before the card can be turned off */
nouveau_switcheroo_optimus_dsm(void)337*4882a593Smuzhiyun void nouveau_switcheroo_optimus_dsm(void)
338*4882a593Smuzhiyun {
339*4882a593Smuzhiyun 	u32 result = 0;
340*4882a593Smuzhiyun 	if (!nouveau_dsm_priv.optimus_detected || nouveau_dsm_priv.optimus_skip_dsm)
341*4882a593Smuzhiyun 		return;
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun 	if (nouveau_dsm_priv.optimus_flags_detected)
344*4882a593Smuzhiyun 		nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_FLAGS,
345*4882a593Smuzhiyun 				    0x3, &result);
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun 	nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_CAPS,
348*4882a593Smuzhiyun 		NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN, &result);
349*4882a593Smuzhiyun 
350*4882a593Smuzhiyun }
351*4882a593Smuzhiyun 
nouveau_unregister_dsm_handler(void)352*4882a593Smuzhiyun void nouveau_unregister_dsm_handler(void)
353*4882a593Smuzhiyun {
354*4882a593Smuzhiyun 	if (nouveau_dsm_priv.optimus_detected || nouveau_dsm_priv.dsm_detected)
355*4882a593Smuzhiyun 		vga_switcheroo_unregister_handler();
356*4882a593Smuzhiyun }
357*4882a593Smuzhiyun #else
nouveau_register_dsm_handler(void)358*4882a593Smuzhiyun void nouveau_register_dsm_handler(void) {}
nouveau_unregister_dsm_handler(void)359*4882a593Smuzhiyun void nouveau_unregister_dsm_handler(void) {}
nouveau_switcheroo_optimus_dsm(void)360*4882a593Smuzhiyun void nouveau_switcheroo_optimus_dsm(void) {}
361*4882a593Smuzhiyun #endif
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun void *
nouveau_acpi_edid(struct drm_device * dev,struct drm_connector * connector)364*4882a593Smuzhiyun nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector)
365*4882a593Smuzhiyun {
366*4882a593Smuzhiyun 	struct acpi_device *acpidev;
367*4882a593Smuzhiyun 	acpi_handle handle;
368*4882a593Smuzhiyun 	int type, ret;
369*4882a593Smuzhiyun 	void *edid;
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	switch (connector->connector_type) {
372*4882a593Smuzhiyun 	case DRM_MODE_CONNECTOR_LVDS:
373*4882a593Smuzhiyun 	case DRM_MODE_CONNECTOR_eDP:
374*4882a593Smuzhiyun 		type = ACPI_VIDEO_DISPLAY_LCD;
375*4882a593Smuzhiyun 		break;
376*4882a593Smuzhiyun 	default:
377*4882a593Smuzhiyun 		return NULL;
378*4882a593Smuzhiyun 	}
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun 	handle = ACPI_HANDLE(&dev->pdev->dev);
381*4882a593Smuzhiyun 	if (!handle)
382*4882a593Smuzhiyun 		return NULL;
383*4882a593Smuzhiyun 
384*4882a593Smuzhiyun 	ret = acpi_bus_get_device(handle, &acpidev);
385*4882a593Smuzhiyun 	if (ret)
386*4882a593Smuzhiyun 		return NULL;
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 	ret = acpi_video_get_edid(acpidev, type, -1, &edid);
389*4882a593Smuzhiyun 	if (ret < 0)
390*4882a593Smuzhiyun 		return NULL;
391*4882a593Smuzhiyun 
392*4882a593Smuzhiyun 	return kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
393*4882a593Smuzhiyun }
394