16ee73861SBen Skeggs #include <linux/pci.h> 26ee73861SBen Skeggs #include <linux/acpi.h> 35a0e3ad6STejun Heo #include <linux/slab.h> 46ee73861SBen Skeggs #include <acpi/acpi_drivers.h> 56ee73861SBen Skeggs #include <acpi/acpi_bus.h> 6a6ed76d7SBen Skeggs #include <acpi/video.h> 78116188fSDave Airlie #include <acpi/acpi.h> 88116188fSDave Airlie #include <linux/mxm-wmi.h> 96ee73861SBen Skeggs 106a9ee8afSDave Airlie #include <linux/vga_switcheroo.h> 116a9ee8afSDave Airlie 12*612a9aabSLinus Torvalds #include <drm/drm_edid.h> 13c0077061SBen Skeggs 14c0077061SBen Skeggs #include "nouveau_drm.h" 15c0077061SBen Skeggs #include "nouveau_acpi.h" 16c0077061SBen Skeggs 176ee73861SBen Skeggs #define NOUVEAU_DSM_LED 0x02 186ee73861SBen Skeggs #define NOUVEAU_DSM_LED_STATE 0x00 196ee73861SBen Skeggs #define NOUVEAU_DSM_LED_OFF 0x10 206ee73861SBen Skeggs #define NOUVEAU_DSM_LED_STAMINA 0x11 216ee73861SBen Skeggs #define NOUVEAU_DSM_LED_SPEED 0x12 226ee73861SBen Skeggs 236ee73861SBen Skeggs #define NOUVEAU_DSM_POWER 0x03 246ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_STATE 0x00 256ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_SPEED 0x01 266ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_STAMINA 0x02 276ee73861SBen Skeggs 289075e85fSPeter Lekensteyn #define NOUVEAU_DSM_OPTIMUS_FN 0x1A 29d099230cSPeter Lekensteyn #define NOUVEAU_DSM_OPTIMUS_ARGS 0x03000001 30d099230cSPeter Lekensteyn 316a9ee8afSDave Airlie static struct nouveau_dsm_priv { 326a9ee8afSDave Airlie bool dsm_detected; 33f19467c5SDave Airlie bool optimus_detected; 346a9ee8afSDave Airlie acpi_handle dhandle; 35afeb3e11SDave Airlie acpi_handle rom_handle; 366a9ee8afSDave Airlie } nouveau_dsm_priv; 376a9ee8afSDave Airlie 38f19467c5SDave Airlie #define NOUVEAU_DSM_HAS_MUX 0x1 39f19467c5SDave Airlie #define NOUVEAU_DSM_HAS_OPT 0x2 40f19467c5SDave Airlie 416a9ee8afSDave Airlie static const char nouveau_dsm_muid[] = { 426ee73861SBen Skeggs 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, 436ee73861SBen Skeggs 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, 446ee73861SBen Skeggs }; 456ee73861SBen Skeggs 46f19467c5SDave Airlie static const char nouveau_op_dsm_muid[] = { 47f19467c5SDave Airlie 0xF8, 0xD8, 0x86, 0xA4, 0xDA, 0x0B, 0x1B, 0x47, 48f19467c5SDave Airlie 0xA7, 0x2B, 0x60, 0x42, 0xA6, 0xB5, 0xBE, 0xE0, 49f19467c5SDave Airlie }; 50f19467c5SDave Airlie 51f19467c5SDave Airlie static int nouveau_optimus_dsm(acpi_handle handle, int func, int arg, uint32_t *result) 52f19467c5SDave Airlie { 53f19467c5SDave Airlie struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 54f19467c5SDave Airlie struct acpi_object_list input; 55f19467c5SDave Airlie union acpi_object params[4]; 56f19467c5SDave Airlie union acpi_object *obj; 57d099230cSPeter Lekensteyn int i, err; 58d099230cSPeter Lekensteyn char args_buff[4]; 59f19467c5SDave Airlie 60f19467c5SDave Airlie input.count = 4; 61f19467c5SDave Airlie input.pointer = params; 62f19467c5SDave Airlie params[0].type = ACPI_TYPE_BUFFER; 63f19467c5SDave Airlie params[0].buffer.length = sizeof(nouveau_op_dsm_muid); 64f19467c5SDave Airlie params[0].buffer.pointer = (char *)nouveau_op_dsm_muid; 65f19467c5SDave Airlie params[1].type = ACPI_TYPE_INTEGER; 66f19467c5SDave Airlie params[1].integer.value = 0x00000100; 67f19467c5SDave Airlie params[2].type = ACPI_TYPE_INTEGER; 68f19467c5SDave Airlie params[2].integer.value = func; 69f19467c5SDave Airlie params[3].type = ACPI_TYPE_BUFFER; 70d099230cSPeter Lekensteyn params[3].buffer.length = 4; 71d099230cSPeter Lekensteyn /* ACPI is little endian, AABBCCDD becomes {DD,CC,BB,AA} */ 72d099230cSPeter Lekensteyn for (i = 0; i < 4; i++) 73d099230cSPeter Lekensteyn args_buff[i] = (arg >> i * 8) & 0xFF; 74d099230cSPeter Lekensteyn params[3].buffer.pointer = args_buff; 75f19467c5SDave Airlie 76f19467c5SDave Airlie err = acpi_evaluate_object(handle, "_DSM", &input, &output); 77f19467c5SDave Airlie if (err) { 78f19467c5SDave Airlie printk(KERN_INFO "failed to evaluate _DSM: %d\n", err); 79f19467c5SDave Airlie return err; 80f19467c5SDave Airlie } 81f19467c5SDave Airlie 82f19467c5SDave Airlie obj = (union acpi_object *)output.pointer; 83f19467c5SDave Airlie 84f19467c5SDave Airlie if (obj->type == ACPI_TYPE_INTEGER) 85f19467c5SDave Airlie if (obj->integer.value == 0x80000002) { 86f19467c5SDave Airlie return -ENODEV; 87f19467c5SDave Airlie } 88f19467c5SDave Airlie 89f19467c5SDave Airlie if (obj->type == ACPI_TYPE_BUFFER) { 90f19467c5SDave Airlie if (obj->buffer.length == 4 && result) { 91f19467c5SDave Airlie *result = 0; 92f19467c5SDave Airlie *result |= obj->buffer.pointer[0]; 93f19467c5SDave Airlie *result |= (obj->buffer.pointer[1] << 8); 94f19467c5SDave Airlie *result |= (obj->buffer.pointer[2] << 16); 95f19467c5SDave Airlie *result |= (obj->buffer.pointer[3] << 24); 96f19467c5SDave Airlie } 97f19467c5SDave Airlie } 98f19467c5SDave Airlie 99f19467c5SDave Airlie kfree(output.pointer); 100f19467c5SDave Airlie return 0; 101f19467c5SDave Airlie } 102f19467c5SDave Airlie 1036e86e041SFrancisco Jerez static int nouveau_dsm(acpi_handle handle, int func, int arg, uint32_t *result) 1046a9ee8afSDave Airlie { 1056ee73861SBen Skeggs struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 1066ee73861SBen Skeggs struct acpi_object_list input; 1076ee73861SBen Skeggs union acpi_object params[4]; 1086ee73861SBen Skeggs union acpi_object *obj; 1096ee73861SBen Skeggs int err; 1106ee73861SBen Skeggs 1116ee73861SBen Skeggs input.count = 4; 1126ee73861SBen Skeggs input.pointer = params; 1136ee73861SBen Skeggs params[0].type = ACPI_TYPE_BUFFER; 1146a9ee8afSDave Airlie params[0].buffer.length = sizeof(nouveau_dsm_muid); 1156a9ee8afSDave Airlie params[0].buffer.pointer = (char *)nouveau_dsm_muid; 1166ee73861SBen Skeggs params[1].type = ACPI_TYPE_INTEGER; 1176ee73861SBen Skeggs params[1].integer.value = 0x00000102; 1186ee73861SBen Skeggs params[2].type = ACPI_TYPE_INTEGER; 1196ee73861SBen Skeggs params[2].integer.value = func; 1206ee73861SBen Skeggs params[3].type = ACPI_TYPE_INTEGER; 1216ee73861SBen Skeggs params[3].integer.value = arg; 1226ee73861SBen Skeggs 1236ee73861SBen Skeggs err = acpi_evaluate_object(handle, "_DSM", &input, &output); 1246ee73861SBen Skeggs if (err) { 1256a9ee8afSDave Airlie printk(KERN_INFO "failed to evaluate _DSM: %d\n", err); 1266ee73861SBen Skeggs return err; 1276ee73861SBen Skeggs } 1286ee73861SBen Skeggs 1296ee73861SBen Skeggs obj = (union acpi_object *)output.pointer; 1306ee73861SBen Skeggs 1316ee73861SBen Skeggs if (obj->type == ACPI_TYPE_INTEGER) 1326ee73861SBen Skeggs if (obj->integer.value == 0x80000002) 1336ee73861SBen Skeggs return -ENODEV; 1346ee73861SBen Skeggs 1356ee73861SBen Skeggs if (obj->type == ACPI_TYPE_BUFFER) { 1366ee73861SBen Skeggs if (obj->buffer.length == 4 && result) { 1376ee73861SBen Skeggs *result = 0; 1386ee73861SBen Skeggs *result |= obj->buffer.pointer[0]; 1396ee73861SBen Skeggs *result |= (obj->buffer.pointer[1] << 8); 1406ee73861SBen Skeggs *result |= (obj->buffer.pointer[2] << 16); 1416ee73861SBen Skeggs *result |= (obj->buffer.pointer[3] << 24); 1426ee73861SBen Skeggs } 1436ee73861SBen Skeggs } 1446ee73861SBen Skeggs 1456ee73861SBen Skeggs kfree(output.pointer); 1466ee73861SBen Skeggs return 0; 1476ee73861SBen Skeggs } 1486ee73861SBen Skeggs 1499075e85fSPeter Lekensteyn /* Returns 1 if a DSM function is usable and 0 otherwise */ 1509075e85fSPeter Lekensteyn static int nouveau_test_dsm(acpi_handle test_handle, 1519075e85fSPeter Lekensteyn int (*dsm_func)(acpi_handle, int, int, uint32_t *), 1529075e85fSPeter Lekensteyn int sfnc) 1539075e85fSPeter Lekensteyn { 1549075e85fSPeter Lekensteyn u32 result = 0; 1559075e85fSPeter Lekensteyn 1569075e85fSPeter Lekensteyn /* Function 0 returns a Buffer containing available functions. The args 1579075e85fSPeter Lekensteyn * parameter is ignored for function 0, so just put 0 in it */ 1589075e85fSPeter Lekensteyn if (dsm_func(test_handle, 0, 0, &result)) 1599075e85fSPeter Lekensteyn return 0; 1609075e85fSPeter Lekensteyn 1619075e85fSPeter Lekensteyn /* ACPI Spec v4 9.14.1: if bit 0 is zero, no function is supported. If 1629075e85fSPeter Lekensteyn * the n-th bit is enabled, function n is supported */ 1639075e85fSPeter Lekensteyn return result & 1 && result & (1 << sfnc); 1649075e85fSPeter Lekensteyn } 1659075e85fSPeter Lekensteyn 1666a9ee8afSDave Airlie static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id) 1676ee73861SBen Skeggs { 168000703f4SDave Airlie mxm_wmi_call_mxmx(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0); 1698116188fSDave Airlie mxm_wmi_call_mxds(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0); 1706a9ee8afSDave Airlie return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id, NULL); 1716ee73861SBen Skeggs } 1726ee73861SBen Skeggs 1736a9ee8afSDave Airlie static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state) 1746a9ee8afSDave Airlie { 1756a9ee8afSDave Airlie int arg; 1766a9ee8afSDave Airlie if (state == VGA_SWITCHEROO_ON) 1776a9ee8afSDave Airlie arg = NOUVEAU_DSM_POWER_SPEED; 1786a9ee8afSDave Airlie else 1796a9ee8afSDave Airlie arg = NOUVEAU_DSM_POWER_STAMINA; 1806a9ee8afSDave Airlie nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg, NULL); 1816ee73861SBen Skeggs return 0; 1826ee73861SBen Skeggs } 1836ee73861SBen Skeggs 1846a9ee8afSDave Airlie static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id) 1856ee73861SBen Skeggs { 186d099230cSPeter Lekensteyn /* perhaps the _DSM functions are mutually exclusive, but prepare for 187d099230cSPeter Lekensteyn * the future */ 188d099230cSPeter Lekensteyn if (!nouveau_dsm_priv.dsm_detected && nouveau_dsm_priv.optimus_detected) 189d099230cSPeter Lekensteyn return 0; 1906a9ee8afSDave Airlie if (id == VGA_SWITCHEROO_IGD) 191fc5ea29dSDave Airlie return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA); 1926a9ee8afSDave Airlie else 193fc5ea29dSDave Airlie return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_SPEED); 1946a9ee8afSDave Airlie } 1956ee73861SBen Skeggs 1966a9ee8afSDave Airlie static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id, 1976a9ee8afSDave Airlie enum vga_switcheroo_state state) 1986a9ee8afSDave Airlie { 1996a9ee8afSDave Airlie if (id == VGA_SWITCHEROO_IGD) 2006a9ee8afSDave Airlie return 0; 2016a9ee8afSDave Airlie 202d099230cSPeter Lekensteyn /* Optimus laptops have the card already disabled in 203d099230cSPeter Lekensteyn * nouveau_switcheroo_set_state */ 204d099230cSPeter Lekensteyn if (!nouveau_dsm_priv.dsm_detected && nouveau_dsm_priv.optimus_detected) 205d099230cSPeter Lekensteyn return 0; 206d099230cSPeter Lekensteyn 207fc5ea29dSDave Airlie return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state); 2086a9ee8afSDave Airlie } 2096a9ee8afSDave Airlie 2106a9ee8afSDave Airlie static int nouveau_dsm_get_client_id(struct pci_dev *pdev) 2116a9ee8afSDave Airlie { 212d1fbd923SDave Airlie /* easy option one - intel vendor ID means Integrated */ 213d1fbd923SDave Airlie if (pdev->vendor == PCI_VENDOR_ID_INTEL) 2146a9ee8afSDave Airlie return VGA_SWITCHEROO_IGD; 215d1fbd923SDave Airlie 216d1fbd923SDave Airlie /* is this device on Bus 0? - this may need improving */ 217d1fbd923SDave Airlie if (pdev->bus->number == 0) 218d1fbd923SDave Airlie return VGA_SWITCHEROO_IGD; 219d1fbd923SDave Airlie 2206a9ee8afSDave Airlie return VGA_SWITCHEROO_DIS; 2216a9ee8afSDave Airlie } 2226a9ee8afSDave Airlie 2236a9ee8afSDave Airlie static struct vga_switcheroo_handler nouveau_dsm_handler = { 2246a9ee8afSDave Airlie .switchto = nouveau_dsm_switchto, 2256a9ee8afSDave Airlie .power_state = nouveau_dsm_power_state, 2266a9ee8afSDave Airlie .get_client_id = nouveau_dsm_get_client_id, 2276a9ee8afSDave Airlie }; 2286a9ee8afSDave Airlie 229f19467c5SDave Airlie static int nouveau_dsm_pci_probe(struct pci_dev *pdev) 2306a9ee8afSDave Airlie { 2316a9ee8afSDave Airlie acpi_handle dhandle, nvidia_handle; 2326a9ee8afSDave Airlie acpi_status status; 2339075e85fSPeter Lekensteyn int retval = 0; 2346a9ee8afSDave Airlie 2356a9ee8afSDave Airlie dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); 2366a9ee8afSDave Airlie if (!dhandle) 2376a9ee8afSDave Airlie return false; 238afeb3e11SDave Airlie 2396a9ee8afSDave Airlie status = acpi_get_handle(dhandle, "_DSM", &nvidia_handle); 2406a9ee8afSDave Airlie if (ACPI_FAILURE(status)) { 2416a9ee8afSDave Airlie return false; 2426a9ee8afSDave Airlie } 2436a9ee8afSDave Airlie 2449075e85fSPeter Lekensteyn if (nouveau_test_dsm(dhandle, nouveau_dsm, NOUVEAU_DSM_POWER)) 245f19467c5SDave Airlie retval |= NOUVEAU_DSM_HAS_MUX; 2466ee73861SBen Skeggs 2479075e85fSPeter Lekensteyn if (nouveau_test_dsm(dhandle, nouveau_optimus_dsm, 2489075e85fSPeter Lekensteyn NOUVEAU_DSM_OPTIMUS_FN)) 249f19467c5SDave Airlie retval |= NOUVEAU_DSM_HAS_OPT; 250f19467c5SDave Airlie 251f19467c5SDave Airlie if (retval) 2526a9ee8afSDave Airlie nouveau_dsm_priv.dhandle = dhandle; 253f19467c5SDave Airlie 254f19467c5SDave Airlie return retval; 2556ee73861SBen Skeggs } 2566a9ee8afSDave Airlie 2576a9ee8afSDave Airlie static bool nouveau_dsm_detect(void) 2586a9ee8afSDave Airlie { 2596a9ee8afSDave Airlie char acpi_method_name[255] = { 0 }; 2606a9ee8afSDave Airlie struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; 2616a9ee8afSDave Airlie struct pci_dev *pdev = NULL; 2626a9ee8afSDave Airlie int has_dsm = 0; 263addde4ecSDave Airlie int has_optimus = 0; 2646a9ee8afSDave Airlie int vga_count = 0; 2658116188fSDave Airlie bool guid_valid; 266f19467c5SDave Airlie int retval; 267f19467c5SDave Airlie bool ret = false; 2688116188fSDave Airlie 269f19467c5SDave Airlie /* lookup the MXM GUID */ 2708116188fSDave Airlie guid_valid = mxm_wmi_supported(); 2718116188fSDave Airlie 272f19467c5SDave Airlie if (guid_valid) 273f19467c5SDave Airlie printk("MXM: GUID detected in BIOS\n"); 274afeb3e11SDave Airlie 275f19467c5SDave Airlie /* now do DSM detection */ 2766a9ee8afSDave Airlie while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { 2776a9ee8afSDave Airlie vga_count++; 2786a9ee8afSDave Airlie 279f19467c5SDave Airlie retval = nouveau_dsm_pci_probe(pdev); 280f19467c5SDave Airlie if (retval & NOUVEAU_DSM_HAS_MUX) 281f19467c5SDave Airlie has_dsm |= 1; 282f19467c5SDave Airlie if (retval & NOUVEAU_DSM_HAS_OPT) 283f19467c5SDave Airlie has_optimus = 1; 2846a9ee8afSDave Airlie } 2856a9ee8afSDave Airlie 286f19467c5SDave Airlie if (vga_count == 2 && has_dsm && guid_valid) { 287d099230cSPeter Lekensteyn acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, 288d099230cSPeter Lekensteyn &buffer); 2896a9ee8afSDave Airlie printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", 2906a9ee8afSDave Airlie acpi_method_name); 2916a9ee8afSDave Airlie nouveau_dsm_priv.dsm_detected = true; 292f19467c5SDave Airlie ret = true; 2936a9ee8afSDave Airlie } 294f19467c5SDave Airlie 295d099230cSPeter Lekensteyn if (has_optimus == 1) { 296d099230cSPeter Lekensteyn acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, 297d099230cSPeter Lekensteyn &buffer); 298d099230cSPeter Lekensteyn printk(KERN_INFO "VGA switcheroo: detected Optimus DSM method %s handle\n", 299d099230cSPeter Lekensteyn acpi_method_name); 300f19467c5SDave Airlie nouveau_dsm_priv.optimus_detected = true; 301d099230cSPeter Lekensteyn ret = true; 302d099230cSPeter Lekensteyn } 303f19467c5SDave Airlie 304f19467c5SDave Airlie return ret; 3056a9ee8afSDave Airlie } 3066a9ee8afSDave Airlie 3076a9ee8afSDave Airlie void nouveau_register_dsm_handler(void) 3086a9ee8afSDave Airlie { 3096a9ee8afSDave Airlie bool r; 3106a9ee8afSDave Airlie 3116a9ee8afSDave Airlie r = nouveau_dsm_detect(); 3126a9ee8afSDave Airlie if (!r) 3136a9ee8afSDave Airlie return; 3146a9ee8afSDave Airlie 3156a9ee8afSDave Airlie vga_switcheroo_register_handler(&nouveau_dsm_handler); 3166a9ee8afSDave Airlie } 3176a9ee8afSDave Airlie 318d099230cSPeter Lekensteyn /* Must be called for Optimus models before the card can be turned off */ 319d099230cSPeter Lekensteyn void nouveau_switcheroo_optimus_dsm(void) 320d099230cSPeter Lekensteyn { 321d099230cSPeter Lekensteyn u32 result = 0; 322d099230cSPeter Lekensteyn if (!nouveau_dsm_priv.optimus_detected) 323d099230cSPeter Lekensteyn return; 324d099230cSPeter Lekensteyn 325d099230cSPeter Lekensteyn nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_FN, 326d099230cSPeter Lekensteyn NOUVEAU_DSM_OPTIMUS_ARGS, &result); 327d099230cSPeter Lekensteyn } 328d099230cSPeter Lekensteyn 3296a9ee8afSDave Airlie void nouveau_unregister_dsm_handler(void) 3306a9ee8afSDave Airlie { 3312f3787aaSAndreas Heider if (nouveau_dsm_priv.optimus_detected || nouveau_dsm_priv.dsm_detected) 3326a9ee8afSDave Airlie vga_switcheroo_unregister_handler(); 3336a9ee8afSDave Airlie } 334afeb3e11SDave Airlie 335afeb3e11SDave Airlie /* retrieve the ROM in 4k blocks */ 336afeb3e11SDave Airlie static int nouveau_rom_call(acpi_handle rom_handle, uint8_t *bios, 337afeb3e11SDave Airlie int offset, int len) 338afeb3e11SDave Airlie { 339afeb3e11SDave Airlie acpi_status status; 340afeb3e11SDave Airlie union acpi_object rom_arg_elements[2], *obj; 341afeb3e11SDave Airlie struct acpi_object_list rom_arg; 342afeb3e11SDave Airlie struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL}; 343afeb3e11SDave Airlie 344afeb3e11SDave Airlie rom_arg.count = 2; 345afeb3e11SDave Airlie rom_arg.pointer = &rom_arg_elements[0]; 346afeb3e11SDave Airlie 347afeb3e11SDave Airlie rom_arg_elements[0].type = ACPI_TYPE_INTEGER; 348afeb3e11SDave Airlie rom_arg_elements[0].integer.value = offset; 349afeb3e11SDave Airlie 350afeb3e11SDave Airlie rom_arg_elements[1].type = ACPI_TYPE_INTEGER; 351afeb3e11SDave Airlie rom_arg_elements[1].integer.value = len; 352afeb3e11SDave Airlie 353afeb3e11SDave Airlie status = acpi_evaluate_object(rom_handle, NULL, &rom_arg, &buffer); 354afeb3e11SDave Airlie if (ACPI_FAILURE(status)) { 355afeb3e11SDave Airlie printk(KERN_INFO "failed to evaluate ROM got %s\n", acpi_format_exception(status)); 356afeb3e11SDave Airlie return -ENODEV; 357afeb3e11SDave Airlie } 358afeb3e11SDave Airlie obj = (union acpi_object *)buffer.pointer; 359afeb3e11SDave Airlie memcpy(bios+offset, obj->buffer.pointer, len); 360afeb3e11SDave Airlie kfree(buffer.pointer); 361afeb3e11SDave Airlie return len; 362afeb3e11SDave Airlie } 363afeb3e11SDave Airlie 364afeb3e11SDave Airlie bool nouveau_acpi_rom_supported(struct pci_dev *pdev) 365afeb3e11SDave Airlie { 366afeb3e11SDave Airlie acpi_status status; 367afeb3e11SDave Airlie acpi_handle dhandle, rom_handle; 368afeb3e11SDave Airlie 369f19467c5SDave Airlie if (!nouveau_dsm_priv.dsm_detected && !nouveau_dsm_priv.optimus_detected) 370afeb3e11SDave Airlie return false; 371afeb3e11SDave Airlie 372afeb3e11SDave Airlie dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); 373afeb3e11SDave Airlie if (!dhandle) 374afeb3e11SDave Airlie return false; 375afeb3e11SDave Airlie 376afeb3e11SDave Airlie status = acpi_get_handle(dhandle, "_ROM", &rom_handle); 377afeb3e11SDave Airlie if (ACPI_FAILURE(status)) 378afeb3e11SDave Airlie return false; 379afeb3e11SDave Airlie 380afeb3e11SDave Airlie nouveau_dsm_priv.rom_handle = rom_handle; 381afeb3e11SDave Airlie return true; 382afeb3e11SDave Airlie } 383afeb3e11SDave Airlie 384afeb3e11SDave Airlie int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len) 385afeb3e11SDave Airlie { 386afeb3e11SDave Airlie return nouveau_rom_call(nouveau_dsm_priv.rom_handle, bios, offset, len); 387afeb3e11SDave Airlie } 388a6ed76d7SBen Skeggs 389c0077061SBen Skeggs void * 390a6ed76d7SBen Skeggs nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) 391a6ed76d7SBen Skeggs { 392a6ed76d7SBen Skeggs struct acpi_device *acpidev; 393a6ed76d7SBen Skeggs acpi_handle handle; 394a6ed76d7SBen Skeggs int type, ret; 395a6ed76d7SBen Skeggs void *edid; 396a6ed76d7SBen Skeggs 397a6ed76d7SBen Skeggs switch (connector->connector_type) { 398a6ed76d7SBen Skeggs case DRM_MODE_CONNECTOR_LVDS: 399a6ed76d7SBen Skeggs case DRM_MODE_CONNECTOR_eDP: 400a6ed76d7SBen Skeggs type = ACPI_VIDEO_DISPLAY_LCD; 401a6ed76d7SBen Skeggs break; 402a6ed76d7SBen Skeggs default: 403c0077061SBen Skeggs return NULL; 404a6ed76d7SBen Skeggs } 405a6ed76d7SBen Skeggs 406a6ed76d7SBen Skeggs handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev); 407a6ed76d7SBen Skeggs if (!handle) 408c0077061SBen Skeggs return NULL; 409a6ed76d7SBen Skeggs 410a6ed76d7SBen Skeggs ret = acpi_bus_get_device(handle, &acpidev); 411a6ed76d7SBen Skeggs if (ret) 412c0077061SBen Skeggs return NULL; 413a6ed76d7SBen Skeggs 414a6ed76d7SBen Skeggs ret = acpi_video_get_edid(acpidev, type, -1, &edid); 415a6ed76d7SBen Skeggs if (ret < 0) 416c0077061SBen Skeggs return NULL; 417a6ed76d7SBen Skeggs 418c0077061SBen Skeggs return kmemdup(edid, EDID_LENGTH, GFP_KERNEL); 419a6ed76d7SBen Skeggs } 420