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