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