16ee73861SBen Skeggs #include <linux/pci.h> 26ee73861SBen Skeggs #include <linux/acpi.h> 35a0e3ad6STejun Heo #include <linux/slab.h> 48116188fSDave Airlie #include <linux/mxm-wmi.h> 56a9ee8afSDave Airlie #include <linux/vga_switcheroo.h> 6612a9aabSLinus Torvalds #include <drm/drm_edid.h> 78b48463fSLv Zheng #include <acpi/video.h> 8c0077061SBen Skeggs 94dc28134SBen Skeggs #include "nouveau_drv.h" 10c0077061SBen Skeggs #include "nouveau_acpi.h" 11c0077061SBen Skeggs 126ee73861SBen Skeggs #define NOUVEAU_DSM_LED 0x02 136ee73861SBen Skeggs #define NOUVEAU_DSM_LED_STATE 0x00 146ee73861SBen Skeggs #define NOUVEAU_DSM_LED_OFF 0x10 156ee73861SBen Skeggs #define NOUVEAU_DSM_LED_STAMINA 0x11 166ee73861SBen Skeggs #define NOUVEAU_DSM_LED_SPEED 0x12 176ee73861SBen Skeggs 186ee73861SBen Skeggs #define NOUVEAU_DSM_POWER 0x03 196ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_STATE 0x00 206ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_SPEED 0x01 216ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_STAMINA 0x02 226ee73861SBen Skeggs 235addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_CAPS 0x1A 245addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_FLAGS 0x1B 255addcf0aSDave Airlie 265addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 (3 << 24) 275addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_NO_POWERDOWN_PS3 (2 << 24) 285addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED (1) 295addcf0aSDave Airlie 305addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN (NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 | NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED) 315addcf0aSDave Airlie 325addcf0aSDave Airlie /* result of the optimus caps function */ 335addcf0aSDave Airlie #define OPTIMUS_ENABLED (1 << 0) 345addcf0aSDave Airlie #define OPTIMUS_STATUS_MASK (3 << 3) 355addcf0aSDave Airlie #define OPTIMUS_STATUS_OFF (0 << 3) 365addcf0aSDave Airlie #define OPTIMUS_STATUS_ON_ENABLED (1 << 3) 375addcf0aSDave Airlie #define OPTIMUS_STATUS_PWR_STABLE (3 << 3) 385addcf0aSDave Airlie #define OPTIMUS_DISPLAY_HOTPLUG (1 << 6) 395addcf0aSDave Airlie #define OPTIMUS_CAPS_MASK (7 << 24) 405addcf0aSDave Airlie #define OPTIMUS_DYNAMIC_PWR_CAP (1 << 24) 415addcf0aSDave Airlie 425addcf0aSDave Airlie #define OPTIMUS_AUDIO_CAPS_MASK (3 << 27) 435addcf0aSDave Airlie #define OPTIMUS_HDA_CODEC_MASK (2 << 27) /* hda bios control */ 44d099230cSPeter Lekensteyn 456a9ee8afSDave Airlie static struct nouveau_dsm_priv { 466a9ee8afSDave Airlie bool dsm_detected; 47f19467c5SDave Airlie bool optimus_detected; 48*cba97805SPeter Wu bool optimus_flags_detected; 496a9ee8afSDave Airlie acpi_handle dhandle; 50afeb3e11SDave Airlie acpi_handle rom_handle; 516a9ee8afSDave Airlie } nouveau_dsm_priv; 526a9ee8afSDave Airlie 53c839d748SDave Airlie bool nouveau_is_optimus(void) { 54c839d748SDave Airlie return nouveau_dsm_priv.optimus_detected; 55c839d748SDave Airlie } 56c839d748SDave Airlie 57c839d748SDave Airlie bool nouveau_is_v1_dsm(void) { 58c839d748SDave Airlie return nouveau_dsm_priv.dsm_detected; 59c839d748SDave Airlie } 60c839d748SDave Airlie 61d0ce7b85SJeff Mahoney #ifdef CONFIG_VGA_SWITCHEROO 626a9ee8afSDave Airlie static const char nouveau_dsm_muid[] = { 636ee73861SBen Skeggs 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, 646ee73861SBen Skeggs 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, 656ee73861SBen Skeggs }; 666ee73861SBen Skeggs 67f19467c5SDave Airlie static const char nouveau_op_dsm_muid[] = { 68f19467c5SDave Airlie 0xF8, 0xD8, 0x86, 0xA4, 0xDA, 0x0B, 0x1B, 0x47, 69f19467c5SDave Airlie 0xA7, 0x2B, 0x60, 0x42, 0xA6, 0xB5, 0xBE, 0xE0, 70f19467c5SDave Airlie }; 71f19467c5SDave Airlie 72f19467c5SDave Airlie static int nouveau_optimus_dsm(acpi_handle handle, int func, int arg, uint32_t *result) 73f19467c5SDave Airlie { 74b072e53bSJiang Liu int i; 75f19467c5SDave Airlie union acpi_object *obj; 76d099230cSPeter Lekensteyn char args_buff[4]; 77b072e53bSJiang Liu union acpi_object argv4 = { 78b072e53bSJiang Liu .buffer.type = ACPI_TYPE_BUFFER, 79b072e53bSJiang Liu .buffer.length = 4, 80b072e53bSJiang Liu .buffer.pointer = args_buff 81b072e53bSJiang Liu }; 82f19467c5SDave Airlie 83d099230cSPeter Lekensteyn /* ACPI is little endian, AABBCCDD becomes {DD,CC,BB,AA} */ 84d099230cSPeter Lekensteyn for (i = 0; i < 4; i++) 85d099230cSPeter Lekensteyn args_buff[i] = (arg >> i * 8) & 0xFF; 86f19467c5SDave Airlie 87f19467c5SDave Airlie *result = 0; 88b072e53bSJiang Liu obj = acpi_evaluate_dsm_typed(handle, nouveau_op_dsm_muid, 0x00000100, 89b072e53bSJiang Liu func, &argv4, ACPI_TYPE_BUFFER); 90b072e53bSJiang Liu if (!obj) { 91b072e53bSJiang Liu acpi_handle_info(handle, "failed to evaluate _DSM\n"); 92b072e53bSJiang Liu return AE_ERROR; 93b072e53bSJiang Liu } else { 94b072e53bSJiang Liu if (obj->buffer.length == 4) { 95f19467c5SDave Airlie *result |= obj->buffer.pointer[0]; 96f19467c5SDave Airlie *result |= (obj->buffer.pointer[1] << 8); 97f19467c5SDave Airlie *result |= (obj->buffer.pointer[2] << 16); 98f19467c5SDave Airlie *result |= (obj->buffer.pointer[3] << 24); 99f19467c5SDave Airlie } 100b072e53bSJiang Liu ACPI_FREE(obj); 101f19467c5SDave Airlie } 102f19467c5SDave Airlie 103f19467c5SDave Airlie return 0; 104f19467c5SDave Airlie } 105f19467c5SDave Airlie 106e284175aSJiang Liu /* 107e284175aSJiang Liu * On some platforms, _DSM(nouveau_op_dsm_muid, func0) has special 108e284175aSJiang Liu * requirements on the fourth parameter, so a private implementation 109e284175aSJiang Liu * instead of using acpi_check_dsm(). 110e284175aSJiang Liu */ 111a12e78ddSPeter Wu static int nouveau_dsm_get_optimus_functions(acpi_handle handle) 112e284175aSJiang Liu { 113e284175aSJiang Liu int result; 114e284175aSJiang Liu 115e284175aSJiang Liu /* 116e284175aSJiang Liu * Function 0 returns a Buffer containing available functions. 117e284175aSJiang Liu * The args parameter is ignored for function 0, so just put 0 in it 118e284175aSJiang Liu */ 119e284175aSJiang Liu if (nouveau_optimus_dsm(handle, 0, 0, &result)) 120e284175aSJiang Liu return 0; 121e284175aSJiang Liu 122e284175aSJiang Liu /* 123e284175aSJiang Liu * ACPI Spec v4 9.14.1: if bit 0 is zero, no function is supported. 124e284175aSJiang Liu * If the n-th bit is enabled, function n is supported 125e284175aSJiang Liu */ 126a12e78ddSPeter Wu if (result & 1 && result & (1 << NOUVEAU_DSM_OPTIMUS_CAPS)) 127a12e78ddSPeter Wu return result; 128a12e78ddSPeter Wu return 0; 129e284175aSJiang Liu } 130e284175aSJiang Liu 131b072e53bSJiang Liu static int nouveau_dsm(acpi_handle handle, int func, int arg) 1326a9ee8afSDave Airlie { 133b072e53bSJiang Liu int ret = 0; 1346ee73861SBen Skeggs union acpi_object *obj; 135b072e53bSJiang Liu union acpi_object argv4 = { 136b072e53bSJiang Liu .integer.type = ACPI_TYPE_INTEGER, 137b072e53bSJiang Liu .integer.value = arg, 138b072e53bSJiang Liu }; 1396ee73861SBen Skeggs 140b072e53bSJiang Liu obj = acpi_evaluate_dsm_typed(handle, nouveau_dsm_muid, 0x00000102, 141b072e53bSJiang Liu func, &argv4, ACPI_TYPE_INTEGER); 142b072e53bSJiang Liu if (!obj) { 143b072e53bSJiang Liu acpi_handle_info(handle, "failed to evaluate _DSM\n"); 144b072e53bSJiang Liu return AE_ERROR; 145b072e53bSJiang Liu } else { 1466ee73861SBen Skeggs if (obj->integer.value == 0x80000002) 147b072e53bSJiang Liu ret = -ENODEV; 148b072e53bSJiang Liu ACPI_FREE(obj); 1496ee73861SBen Skeggs } 1506ee73861SBen Skeggs 151b072e53bSJiang Liu return ret; 1529075e85fSPeter Lekensteyn } 1539075e85fSPeter Lekensteyn 1546a9ee8afSDave Airlie static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id) 1556ee73861SBen Skeggs { 156000703f4SDave Airlie mxm_wmi_call_mxmx(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0); 1578116188fSDave Airlie mxm_wmi_call_mxds(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0); 158b072e53bSJiang Liu return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id); 1596ee73861SBen Skeggs } 1606ee73861SBen Skeggs 1616a9ee8afSDave Airlie static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state) 1626a9ee8afSDave Airlie { 1636a9ee8afSDave Airlie int arg; 1646a9ee8afSDave Airlie if (state == VGA_SWITCHEROO_ON) 1656a9ee8afSDave Airlie arg = NOUVEAU_DSM_POWER_SPEED; 1666a9ee8afSDave Airlie else 1676a9ee8afSDave Airlie arg = NOUVEAU_DSM_POWER_STAMINA; 168b072e53bSJiang Liu nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg); 1696ee73861SBen Skeggs return 0; 1706ee73861SBen Skeggs } 1716ee73861SBen Skeggs 1726a9ee8afSDave Airlie static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id) 1736ee73861SBen Skeggs { 174c839d748SDave Airlie if (!nouveau_dsm_priv.dsm_detected) 175d099230cSPeter Lekensteyn return 0; 1766a9ee8afSDave Airlie if (id == VGA_SWITCHEROO_IGD) 177fc5ea29dSDave Airlie return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA); 1786a9ee8afSDave Airlie else 179fc5ea29dSDave Airlie return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_SPEED); 1806a9ee8afSDave Airlie } 1816ee73861SBen Skeggs 1826a9ee8afSDave Airlie static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id, 1836a9ee8afSDave Airlie enum vga_switcheroo_state state) 1846a9ee8afSDave Airlie { 1856a9ee8afSDave Airlie if (id == VGA_SWITCHEROO_IGD) 1866a9ee8afSDave Airlie return 0; 1876a9ee8afSDave Airlie 188d099230cSPeter Lekensteyn /* Optimus laptops have the card already disabled in 189d099230cSPeter Lekensteyn * nouveau_switcheroo_set_state */ 190c839d748SDave Airlie if (!nouveau_dsm_priv.dsm_detected) 191d099230cSPeter Lekensteyn return 0; 192d099230cSPeter Lekensteyn 193fc5ea29dSDave Airlie return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state); 1946a9ee8afSDave Airlie } 1956a9ee8afSDave Airlie 1966a9ee8afSDave Airlie static int nouveau_dsm_get_client_id(struct pci_dev *pdev) 1976a9ee8afSDave Airlie { 198d1fbd923SDave Airlie /* easy option one - intel vendor ID means Integrated */ 199d1fbd923SDave Airlie if (pdev->vendor == PCI_VENDOR_ID_INTEL) 2006a9ee8afSDave Airlie return VGA_SWITCHEROO_IGD; 201d1fbd923SDave Airlie 202d1fbd923SDave Airlie /* is this device on Bus 0? - this may need improving */ 203d1fbd923SDave Airlie if (pdev->bus->number == 0) 204d1fbd923SDave Airlie return VGA_SWITCHEROO_IGD; 205d1fbd923SDave Airlie 2066a9ee8afSDave Airlie return VGA_SWITCHEROO_DIS; 2076a9ee8afSDave Airlie } 2086a9ee8afSDave Airlie 2095d170139SLukas Wunner static const struct vga_switcheroo_handler nouveau_dsm_handler = { 2106a9ee8afSDave Airlie .switchto = nouveau_dsm_switchto, 2116a9ee8afSDave Airlie .power_state = nouveau_dsm_power_state, 2126a9ee8afSDave Airlie .get_client_id = nouveau_dsm_get_client_id, 2136a9ee8afSDave Airlie }; 2146a9ee8afSDave Airlie 215df42194aSPeter Wu static void nouveau_dsm_pci_probe(struct pci_dev *pdev, acpi_handle *dhandle_out, 216*cba97805SPeter Wu bool *has_mux, bool *has_opt, 217*cba97805SPeter Wu bool *has_opt_flags) 2186a9ee8afSDave Airlie { 219187b5b5dSZhang Rui acpi_handle dhandle; 220df42194aSPeter Wu bool supports_mux; 221a12e78ddSPeter Wu int optimus_funcs; 2226a9ee8afSDave Airlie 2233a83f992SRafael J. Wysocki dhandle = ACPI_HANDLE(&pdev->dev); 2246a9ee8afSDave Airlie if (!dhandle) 225df42194aSPeter Wu return; 226afeb3e11SDave Airlie 227f91ce35eSBjorn Helgaas if (!acpi_has_method(dhandle, "_DSM")) 228df42194aSPeter Wu return; 229f91ce35eSBjorn Helgaas 230df42194aSPeter Wu supports_mux = acpi_check_dsm(dhandle, nouveau_dsm_muid, 0x00000102, 231df42194aSPeter Wu 1 << NOUVEAU_DSM_POWER); 232a12e78ddSPeter Wu optimus_funcs = nouveau_dsm_get_optimus_functions(dhandle); 2336ee73861SBen Skeggs 234df42194aSPeter Wu /* Does not look like a Nvidia device. */ 235a12e78ddSPeter Wu if (!supports_mux && !optimus_funcs) 236df42194aSPeter Wu return; 237f19467c5SDave Airlie 238df42194aSPeter Wu *dhandle_out = dhandle; 239df42194aSPeter Wu *has_mux = supports_mux; 240a12e78ddSPeter Wu *has_opt = !!optimus_funcs; 241*cba97805SPeter Wu *has_opt_flags = optimus_funcs & (1 << NOUVEAU_DSM_OPTIMUS_FLAGS); 242df42194aSPeter Wu 243a12e78ddSPeter Wu if (optimus_funcs) { 2445addcf0aSDave Airlie uint32_t result; 2455addcf0aSDave Airlie nouveau_optimus_dsm(dhandle, NOUVEAU_DSM_OPTIMUS_CAPS, 0, 2465addcf0aSDave Airlie &result); 2475addcf0aSDave Airlie dev_info(&pdev->dev, "optimus capabilities: %s, status %s%s\n", 2485addcf0aSDave Airlie (result & OPTIMUS_ENABLED) ? "enabled" : "disabled", 2495addcf0aSDave Airlie (result & OPTIMUS_DYNAMIC_PWR_CAP) ? "dynamic power, " : "", 2505addcf0aSDave Airlie (result & OPTIMUS_HDA_CODEC_MASK) ? "hda bios codec supported" : ""); 2515addcf0aSDave Airlie } 2526ee73861SBen Skeggs } 2536a9ee8afSDave Airlie 2546a9ee8afSDave Airlie static bool nouveau_dsm_detect(void) 2556a9ee8afSDave Airlie { 2566a9ee8afSDave Airlie char acpi_method_name[255] = { 0 }; 2576a9ee8afSDave Airlie struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; 2586a9ee8afSDave Airlie struct pci_dev *pdev = NULL; 259df42194aSPeter Wu acpi_handle dhandle = NULL; 260df42194aSPeter Wu bool has_mux = false; 261df42194aSPeter Wu bool has_optimus = false; 262*cba97805SPeter Wu bool has_optimus_flags = false; 2636a9ee8afSDave Airlie int vga_count = 0; 2648116188fSDave Airlie bool guid_valid; 265f19467c5SDave Airlie bool ret = false; 2668116188fSDave Airlie 267f19467c5SDave Airlie /* lookup the MXM GUID */ 2688116188fSDave Airlie guid_valid = mxm_wmi_supported(); 2698116188fSDave Airlie 270f19467c5SDave Airlie if (guid_valid) 271f19467c5SDave Airlie printk("MXM: GUID detected in BIOS\n"); 272afeb3e11SDave Airlie 273f19467c5SDave Airlie /* now do DSM detection */ 2746a9ee8afSDave Airlie while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { 2756a9ee8afSDave Airlie vga_count++; 2766a9ee8afSDave Airlie 277*cba97805SPeter Wu nouveau_dsm_pci_probe(pdev, &dhandle, &has_mux, &has_optimus, 278*cba97805SPeter Wu &has_optimus_flags); 2796a9ee8afSDave Airlie } 2806a9ee8afSDave Airlie 2814c60fac1SEmil Velikov while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_3D << 8, pdev)) != NULL) { 2824c60fac1SEmil Velikov vga_count++; 2834c60fac1SEmil Velikov 284*cba97805SPeter Wu nouveau_dsm_pci_probe(pdev, &dhandle, &has_mux, &has_optimus, 285*cba97805SPeter Wu &has_optimus_flags); 2864c60fac1SEmil Velikov } 2874c60fac1SEmil Velikov 288c839d748SDave Airlie /* find the optimus DSM or the old v1 DSM */ 289df42194aSPeter Wu if (has_optimus) { 290df42194aSPeter Wu nouveau_dsm_priv.dhandle = dhandle; 291c839d748SDave Airlie acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, 292c839d748SDave Airlie &buffer); 293c839d748SDave Airlie printk(KERN_INFO "VGA switcheroo: detected Optimus DSM method %s handle\n", 294c839d748SDave Airlie acpi_method_name); 295c839d748SDave Airlie nouveau_dsm_priv.optimus_detected = true; 296*cba97805SPeter Wu nouveau_dsm_priv.optimus_flags_detected = has_optimus_flags; 297c839d748SDave Airlie ret = true; 298df42194aSPeter Wu } else if (vga_count == 2 && has_mux && guid_valid) { 299df42194aSPeter Wu nouveau_dsm_priv.dhandle = dhandle; 300d099230cSPeter Lekensteyn acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, 301d099230cSPeter Lekensteyn &buffer); 3026a9ee8afSDave Airlie printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", 3036a9ee8afSDave Airlie acpi_method_name); 3046a9ee8afSDave Airlie nouveau_dsm_priv.dsm_detected = true; 305f19467c5SDave Airlie ret = true; 3066a9ee8afSDave Airlie } 307f19467c5SDave Airlie 308f19467c5SDave Airlie 309f19467c5SDave Airlie return ret; 3106a9ee8afSDave Airlie } 3116a9ee8afSDave Airlie 3126a9ee8afSDave Airlie void nouveau_register_dsm_handler(void) 3136a9ee8afSDave Airlie { 3146a9ee8afSDave Airlie bool r; 3156a9ee8afSDave Airlie 3166a9ee8afSDave Airlie r = nouveau_dsm_detect(); 3176a9ee8afSDave Airlie if (!r) 3186a9ee8afSDave Airlie return; 3196a9ee8afSDave Airlie 320156d7d41SLukas Wunner vga_switcheroo_register_handler(&nouveau_dsm_handler, 0); 3216a9ee8afSDave Airlie } 3226a9ee8afSDave Airlie 323d099230cSPeter Lekensteyn /* Must be called for Optimus models before the card can be turned off */ 324d099230cSPeter Lekensteyn void nouveau_switcheroo_optimus_dsm(void) 325d099230cSPeter Lekensteyn { 326d099230cSPeter Lekensteyn u32 result = 0; 327d099230cSPeter Lekensteyn if (!nouveau_dsm_priv.optimus_detected) 328d099230cSPeter Lekensteyn return; 329d099230cSPeter Lekensteyn 330*cba97805SPeter Wu if (nouveau_dsm_priv.optimus_flags_detected) 3315addcf0aSDave Airlie nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_FLAGS, 3325addcf0aSDave Airlie 0x3, &result); 3335addcf0aSDave Airlie 3345addcf0aSDave Airlie nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_CAPS, 3355addcf0aSDave Airlie NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN, &result); 3365addcf0aSDave Airlie 337d099230cSPeter Lekensteyn } 338d099230cSPeter Lekensteyn 3396a9ee8afSDave Airlie void nouveau_unregister_dsm_handler(void) 3406a9ee8afSDave Airlie { 3412f3787aaSAndreas Heider if (nouveau_dsm_priv.optimus_detected || nouveau_dsm_priv.dsm_detected) 3426a9ee8afSDave Airlie vga_switcheroo_unregister_handler(); 3436a9ee8afSDave Airlie } 344d0ce7b85SJeff Mahoney #else 345d0ce7b85SJeff Mahoney void nouveau_register_dsm_handler(void) {} 346d0ce7b85SJeff Mahoney void nouveau_unregister_dsm_handler(void) {} 347d0ce7b85SJeff Mahoney void nouveau_switcheroo_optimus_dsm(void) {} 348d0ce7b85SJeff Mahoney #endif 349afeb3e11SDave Airlie 350afeb3e11SDave Airlie /* retrieve the ROM in 4k blocks */ 351afeb3e11SDave Airlie static int nouveau_rom_call(acpi_handle rom_handle, uint8_t *bios, 352afeb3e11SDave Airlie int offset, int len) 353afeb3e11SDave Airlie { 354afeb3e11SDave Airlie acpi_status status; 355afeb3e11SDave Airlie union acpi_object rom_arg_elements[2], *obj; 356afeb3e11SDave Airlie struct acpi_object_list rom_arg; 357afeb3e11SDave Airlie struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL}; 358afeb3e11SDave Airlie 359afeb3e11SDave Airlie rom_arg.count = 2; 360afeb3e11SDave Airlie rom_arg.pointer = &rom_arg_elements[0]; 361afeb3e11SDave Airlie 362afeb3e11SDave Airlie rom_arg_elements[0].type = ACPI_TYPE_INTEGER; 363afeb3e11SDave Airlie rom_arg_elements[0].integer.value = offset; 364afeb3e11SDave Airlie 365afeb3e11SDave Airlie rom_arg_elements[1].type = ACPI_TYPE_INTEGER; 366afeb3e11SDave Airlie rom_arg_elements[1].integer.value = len; 367afeb3e11SDave Airlie 368afeb3e11SDave Airlie status = acpi_evaluate_object(rom_handle, NULL, &rom_arg, &buffer); 369afeb3e11SDave Airlie if (ACPI_FAILURE(status)) { 370afeb3e11SDave Airlie printk(KERN_INFO "failed to evaluate ROM got %s\n", acpi_format_exception(status)); 371afeb3e11SDave Airlie return -ENODEV; 372afeb3e11SDave Airlie } 373afeb3e11SDave Airlie obj = (union acpi_object *)buffer.pointer; 37495432941SBen Skeggs len = min(len, (int)obj->buffer.length); 375afeb3e11SDave Airlie memcpy(bios+offset, obj->buffer.pointer, len); 376afeb3e11SDave Airlie kfree(buffer.pointer); 377afeb3e11SDave Airlie return len; 378afeb3e11SDave Airlie } 379afeb3e11SDave Airlie 38026c9e8efSBen Skeggs bool nouveau_acpi_rom_supported(struct device *dev) 381afeb3e11SDave Airlie { 382afeb3e11SDave Airlie acpi_status status; 383afeb3e11SDave Airlie acpi_handle dhandle, rom_handle; 384afeb3e11SDave Airlie 38526c9e8efSBen Skeggs dhandle = ACPI_HANDLE(dev); 386afeb3e11SDave Airlie if (!dhandle) 387afeb3e11SDave Airlie return false; 388afeb3e11SDave Airlie 389afeb3e11SDave Airlie status = acpi_get_handle(dhandle, "_ROM", &rom_handle); 390afeb3e11SDave Airlie if (ACPI_FAILURE(status)) 391afeb3e11SDave Airlie return false; 392afeb3e11SDave Airlie 393afeb3e11SDave Airlie nouveau_dsm_priv.rom_handle = rom_handle; 394afeb3e11SDave Airlie return true; 395afeb3e11SDave Airlie } 396afeb3e11SDave Airlie 397afeb3e11SDave Airlie int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len) 398afeb3e11SDave Airlie { 399afeb3e11SDave Airlie return nouveau_rom_call(nouveau_dsm_priv.rom_handle, bios, offset, len); 400afeb3e11SDave Airlie } 401a6ed76d7SBen Skeggs 402c0077061SBen Skeggs void * 403a6ed76d7SBen Skeggs nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) 404a6ed76d7SBen Skeggs { 405a6ed76d7SBen Skeggs struct acpi_device *acpidev; 406a6ed76d7SBen Skeggs acpi_handle handle; 407a6ed76d7SBen Skeggs int type, ret; 408a6ed76d7SBen Skeggs void *edid; 409a6ed76d7SBen Skeggs 410a6ed76d7SBen Skeggs switch (connector->connector_type) { 411a6ed76d7SBen Skeggs case DRM_MODE_CONNECTOR_LVDS: 412a6ed76d7SBen Skeggs case DRM_MODE_CONNECTOR_eDP: 413a6ed76d7SBen Skeggs type = ACPI_VIDEO_DISPLAY_LCD; 414a6ed76d7SBen Skeggs break; 415a6ed76d7SBen Skeggs default: 416c0077061SBen Skeggs return NULL; 417a6ed76d7SBen Skeggs } 418a6ed76d7SBen Skeggs 4193a83f992SRafael J. Wysocki handle = ACPI_HANDLE(&dev->pdev->dev); 420a6ed76d7SBen Skeggs if (!handle) 421c0077061SBen Skeggs return NULL; 422a6ed76d7SBen Skeggs 423a6ed76d7SBen Skeggs ret = acpi_bus_get_device(handle, &acpidev); 424a6ed76d7SBen Skeggs if (ret) 425c0077061SBen Skeggs return NULL; 426a6ed76d7SBen Skeggs 427a6ed76d7SBen Skeggs ret = acpi_video_get_edid(acpidev, type, -1, &edid); 428a6ed76d7SBen Skeggs if (ret < 0) 429c0077061SBen Skeggs return NULL; 430a6ed76d7SBen Skeggs 431c0077061SBen Skeggs return kmemdup(edid, EDID_LENGTH, GFP_KERNEL); 432a6ed76d7SBen Skeggs } 433