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 12612a9aabSLinus 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 285addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_CAPS 0x1A 295addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_FLAGS 0x1B 305addcf0aSDave Airlie 315addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 (3 << 24) 325addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_NO_POWERDOWN_PS3 (2 << 24) 335addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED (1) 345addcf0aSDave Airlie 355addcf0aSDave Airlie #define NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN (NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 | NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED) 365addcf0aSDave Airlie 375addcf0aSDave Airlie /* result of the optimus caps function */ 385addcf0aSDave Airlie #define OPTIMUS_ENABLED (1 << 0) 395addcf0aSDave Airlie #define OPTIMUS_STATUS_MASK (3 << 3) 405addcf0aSDave Airlie #define OPTIMUS_STATUS_OFF (0 << 3) 415addcf0aSDave Airlie #define OPTIMUS_STATUS_ON_ENABLED (1 << 3) 425addcf0aSDave Airlie #define OPTIMUS_STATUS_PWR_STABLE (3 << 3) 435addcf0aSDave Airlie #define OPTIMUS_DISPLAY_HOTPLUG (1 << 6) 445addcf0aSDave Airlie #define OPTIMUS_CAPS_MASK (7 << 24) 455addcf0aSDave Airlie #define OPTIMUS_DYNAMIC_PWR_CAP (1 << 24) 465addcf0aSDave Airlie 475addcf0aSDave Airlie #define OPTIMUS_AUDIO_CAPS_MASK (3 << 27) 485addcf0aSDave Airlie #define OPTIMUS_HDA_CODEC_MASK (2 << 27) /* hda bios control */ 49d099230cSPeter Lekensteyn 506a9ee8afSDave Airlie static struct nouveau_dsm_priv { 516a9ee8afSDave Airlie bool dsm_detected; 52f19467c5SDave Airlie bool optimus_detected; 536a9ee8afSDave Airlie acpi_handle dhandle; 54f244d8b6SRafael J. Wysocki acpi_handle other_handle; 55afeb3e11SDave Airlie acpi_handle rom_handle; 566a9ee8afSDave Airlie } nouveau_dsm_priv; 576a9ee8afSDave Airlie 58c839d748SDave Airlie bool nouveau_is_optimus(void) { 59c839d748SDave Airlie return nouveau_dsm_priv.optimus_detected; 60c839d748SDave Airlie } 61c839d748SDave Airlie 62c839d748SDave Airlie bool nouveau_is_v1_dsm(void) { 63c839d748SDave Airlie return nouveau_dsm_priv.dsm_detected; 64c839d748SDave Airlie } 65c839d748SDave Airlie 66f19467c5SDave Airlie #define NOUVEAU_DSM_HAS_MUX 0x1 67f19467c5SDave Airlie #define NOUVEAU_DSM_HAS_OPT 0x2 68f19467c5SDave Airlie 696a9ee8afSDave Airlie static const char nouveau_dsm_muid[] = { 706ee73861SBen Skeggs 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, 716ee73861SBen Skeggs 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, 726ee73861SBen Skeggs }; 736ee73861SBen Skeggs 74f19467c5SDave Airlie static const char nouveau_op_dsm_muid[] = { 75f19467c5SDave Airlie 0xF8, 0xD8, 0x86, 0xA4, 0xDA, 0x0B, 0x1B, 0x47, 76f19467c5SDave Airlie 0xA7, 0x2B, 0x60, 0x42, 0xA6, 0xB5, 0xBE, 0xE0, 77f19467c5SDave Airlie }; 78f19467c5SDave Airlie 79f19467c5SDave Airlie static int nouveau_optimus_dsm(acpi_handle handle, int func, int arg, uint32_t *result) 80f19467c5SDave Airlie { 81*b072e53bSJiang Liu int i; 82f19467c5SDave Airlie union acpi_object *obj; 83d099230cSPeter Lekensteyn char args_buff[4]; 84*b072e53bSJiang Liu union acpi_object argv4 = { 85*b072e53bSJiang Liu .buffer.type = ACPI_TYPE_BUFFER, 86*b072e53bSJiang Liu .buffer.length = 4, 87*b072e53bSJiang Liu .buffer.pointer = args_buff 88*b072e53bSJiang Liu }; 89f19467c5SDave Airlie 90d099230cSPeter Lekensteyn /* ACPI is little endian, AABBCCDD becomes {DD,CC,BB,AA} */ 91d099230cSPeter Lekensteyn for (i = 0; i < 4; i++) 92d099230cSPeter Lekensteyn args_buff[i] = (arg >> i * 8) & 0xFF; 93f19467c5SDave Airlie 94f19467c5SDave Airlie *result = 0; 95*b072e53bSJiang Liu obj = acpi_evaluate_dsm_typed(handle, nouveau_op_dsm_muid, 0x00000100, 96*b072e53bSJiang Liu func, &argv4, ACPI_TYPE_BUFFER); 97*b072e53bSJiang Liu if (!obj) { 98*b072e53bSJiang Liu acpi_handle_info(handle, "failed to evaluate _DSM\n"); 99*b072e53bSJiang Liu return AE_ERROR; 100*b072e53bSJiang Liu } else { 101*b072e53bSJiang Liu if (obj->buffer.length == 4) { 102f19467c5SDave Airlie *result |= obj->buffer.pointer[0]; 103f19467c5SDave Airlie *result |= (obj->buffer.pointer[1] << 8); 104f19467c5SDave Airlie *result |= (obj->buffer.pointer[2] << 16); 105f19467c5SDave Airlie *result |= (obj->buffer.pointer[3] << 24); 106f19467c5SDave Airlie } 107*b072e53bSJiang Liu ACPI_FREE(obj); 108f19467c5SDave Airlie } 109f19467c5SDave Airlie 110f19467c5SDave Airlie return 0; 111f19467c5SDave Airlie } 112f19467c5SDave Airlie 113*b072e53bSJiang Liu static int nouveau_dsm(acpi_handle handle, int func, int arg) 1146a9ee8afSDave Airlie { 115*b072e53bSJiang Liu int ret = 0; 1166ee73861SBen Skeggs union acpi_object *obj; 117*b072e53bSJiang Liu union acpi_object argv4 = { 118*b072e53bSJiang Liu .integer.type = ACPI_TYPE_INTEGER, 119*b072e53bSJiang Liu .integer.value = arg, 120*b072e53bSJiang Liu }; 1216ee73861SBen Skeggs 122*b072e53bSJiang Liu obj = acpi_evaluate_dsm_typed(handle, nouveau_dsm_muid, 0x00000102, 123*b072e53bSJiang Liu func, &argv4, ACPI_TYPE_INTEGER); 124*b072e53bSJiang Liu if (!obj) { 125*b072e53bSJiang Liu acpi_handle_info(handle, "failed to evaluate _DSM\n"); 126*b072e53bSJiang Liu return AE_ERROR; 127*b072e53bSJiang Liu } else { 128*b072e53bSJiang Liu if (obj->integer.value == 0x80000002) 129*b072e53bSJiang Liu ret = -ENODEV; 130*b072e53bSJiang Liu ACPI_FREE(obj); 1316ee73861SBen Skeggs } 1326ee73861SBen Skeggs 133*b072e53bSJiang Liu return ret; 1349075e85fSPeter Lekensteyn } 1359075e85fSPeter Lekensteyn 1366a9ee8afSDave Airlie static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id) 1376ee73861SBen Skeggs { 138000703f4SDave Airlie mxm_wmi_call_mxmx(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0); 1398116188fSDave Airlie mxm_wmi_call_mxds(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0); 140*b072e53bSJiang Liu return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id); 1416ee73861SBen Skeggs } 1426ee73861SBen Skeggs 1436a9ee8afSDave Airlie static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state) 1446a9ee8afSDave Airlie { 1456a9ee8afSDave Airlie int arg; 1466a9ee8afSDave Airlie if (state == VGA_SWITCHEROO_ON) 1476a9ee8afSDave Airlie arg = NOUVEAU_DSM_POWER_SPEED; 1486a9ee8afSDave Airlie else 1496a9ee8afSDave Airlie arg = NOUVEAU_DSM_POWER_STAMINA; 150*b072e53bSJiang Liu nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg); 1516ee73861SBen Skeggs return 0; 1526ee73861SBen Skeggs } 1536ee73861SBen Skeggs 1546a9ee8afSDave Airlie static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id) 1556ee73861SBen Skeggs { 156c839d748SDave Airlie if (!nouveau_dsm_priv.dsm_detected) 157d099230cSPeter Lekensteyn return 0; 1586a9ee8afSDave Airlie if (id == VGA_SWITCHEROO_IGD) 159fc5ea29dSDave Airlie return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA); 1606a9ee8afSDave Airlie else 161fc5ea29dSDave Airlie return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_SPEED); 1626a9ee8afSDave Airlie } 1636ee73861SBen Skeggs 1646a9ee8afSDave Airlie static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id, 1656a9ee8afSDave Airlie enum vga_switcheroo_state state) 1666a9ee8afSDave Airlie { 1676a9ee8afSDave Airlie if (id == VGA_SWITCHEROO_IGD) 1686a9ee8afSDave Airlie return 0; 1696a9ee8afSDave Airlie 170d099230cSPeter Lekensteyn /* Optimus laptops have the card already disabled in 171d099230cSPeter Lekensteyn * nouveau_switcheroo_set_state */ 172c839d748SDave Airlie if (!nouveau_dsm_priv.dsm_detected) 173d099230cSPeter Lekensteyn return 0; 174d099230cSPeter Lekensteyn 175fc5ea29dSDave Airlie return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state); 1766a9ee8afSDave Airlie } 1776a9ee8afSDave Airlie 1786a9ee8afSDave Airlie static int nouveau_dsm_get_client_id(struct pci_dev *pdev) 1796a9ee8afSDave Airlie { 180d1fbd923SDave Airlie /* easy option one - intel vendor ID means Integrated */ 181d1fbd923SDave Airlie if (pdev->vendor == PCI_VENDOR_ID_INTEL) 1826a9ee8afSDave Airlie return VGA_SWITCHEROO_IGD; 183d1fbd923SDave Airlie 184d1fbd923SDave Airlie /* is this device on Bus 0? - this may need improving */ 185d1fbd923SDave Airlie if (pdev->bus->number == 0) 186d1fbd923SDave Airlie return VGA_SWITCHEROO_IGD; 187d1fbd923SDave Airlie 1886a9ee8afSDave Airlie return VGA_SWITCHEROO_DIS; 1896a9ee8afSDave Airlie } 1906a9ee8afSDave Airlie 1916a9ee8afSDave Airlie static struct vga_switcheroo_handler nouveau_dsm_handler = { 1926a9ee8afSDave Airlie .switchto = nouveau_dsm_switchto, 1936a9ee8afSDave Airlie .power_state = nouveau_dsm_power_state, 1946a9ee8afSDave Airlie .get_client_id = nouveau_dsm_get_client_id, 1956a9ee8afSDave Airlie }; 1966a9ee8afSDave Airlie 197f19467c5SDave Airlie static int nouveau_dsm_pci_probe(struct pci_dev *pdev) 1986a9ee8afSDave Airlie { 199187b5b5dSZhang Rui acpi_handle dhandle; 2009075e85fSPeter Lekensteyn int retval = 0; 2016a9ee8afSDave Airlie 2023a83f992SRafael J. Wysocki dhandle = ACPI_HANDLE(&pdev->dev); 2036a9ee8afSDave Airlie if (!dhandle) 2046a9ee8afSDave Airlie return false; 205afeb3e11SDave Airlie 206f244d8b6SRafael J. Wysocki if (!acpi_has_method(dhandle, "_DSM")) { 207f244d8b6SRafael J. Wysocki nouveau_dsm_priv.other_handle = dhandle; 2086a9ee8afSDave Airlie return false; 209f244d8b6SRafael J. Wysocki } 210*b072e53bSJiang Liu if (acpi_check_dsm(dhandle, nouveau_dsm_muid, 0x00000102, 211*b072e53bSJiang Liu 1 << NOUVEAU_DSM_POWER)) 212f19467c5SDave Airlie retval |= NOUVEAU_DSM_HAS_MUX; 2136ee73861SBen Skeggs 214*b072e53bSJiang Liu if (acpi_check_dsm(dhandle, nouveau_op_dsm_muid, 0x00000100, 215*b072e53bSJiang Liu 1 << NOUVEAU_DSM_OPTIMUS_CAPS)) 216f19467c5SDave Airlie retval |= NOUVEAU_DSM_HAS_OPT; 217f19467c5SDave Airlie 2185addcf0aSDave Airlie if (retval & NOUVEAU_DSM_HAS_OPT) { 2195addcf0aSDave Airlie uint32_t result; 2205addcf0aSDave Airlie nouveau_optimus_dsm(dhandle, NOUVEAU_DSM_OPTIMUS_CAPS, 0, 2215addcf0aSDave Airlie &result); 2225addcf0aSDave Airlie dev_info(&pdev->dev, "optimus capabilities: %s, status %s%s\n", 2235addcf0aSDave Airlie (result & OPTIMUS_ENABLED) ? "enabled" : "disabled", 2245addcf0aSDave Airlie (result & OPTIMUS_DYNAMIC_PWR_CAP) ? "dynamic power, " : "", 2255addcf0aSDave Airlie (result & OPTIMUS_HDA_CODEC_MASK) ? "hda bios codec supported" : ""); 2265addcf0aSDave Airlie } 227f19467c5SDave Airlie if (retval) 2286a9ee8afSDave Airlie nouveau_dsm_priv.dhandle = dhandle; 229f19467c5SDave Airlie 230f19467c5SDave Airlie return retval; 2316ee73861SBen Skeggs } 2326a9ee8afSDave Airlie 2336a9ee8afSDave Airlie static bool nouveau_dsm_detect(void) 2346a9ee8afSDave Airlie { 2356a9ee8afSDave Airlie char acpi_method_name[255] = { 0 }; 2366a9ee8afSDave Airlie struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; 2376a9ee8afSDave Airlie struct pci_dev *pdev = NULL; 2386a9ee8afSDave Airlie int has_dsm = 0; 239addde4ecSDave Airlie int has_optimus = 0; 2406a9ee8afSDave Airlie int vga_count = 0; 2418116188fSDave Airlie bool guid_valid; 242f19467c5SDave Airlie int retval; 243f19467c5SDave Airlie bool ret = false; 2448116188fSDave Airlie 245f19467c5SDave Airlie /* lookup the MXM GUID */ 2468116188fSDave Airlie guid_valid = mxm_wmi_supported(); 2478116188fSDave Airlie 248f19467c5SDave Airlie if (guid_valid) 249f19467c5SDave Airlie printk("MXM: GUID detected in BIOS\n"); 250afeb3e11SDave Airlie 251f19467c5SDave Airlie /* now do DSM detection */ 2526a9ee8afSDave Airlie while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { 2536a9ee8afSDave Airlie vga_count++; 2546a9ee8afSDave Airlie 255f19467c5SDave Airlie retval = nouveau_dsm_pci_probe(pdev); 256f19467c5SDave Airlie if (retval & NOUVEAU_DSM_HAS_MUX) 257f19467c5SDave Airlie has_dsm |= 1; 258f19467c5SDave Airlie if (retval & NOUVEAU_DSM_HAS_OPT) 259f19467c5SDave Airlie has_optimus = 1; 2606a9ee8afSDave Airlie } 2616a9ee8afSDave Airlie 2624c60fac1SEmil Velikov while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_3D << 8, pdev)) != NULL) { 2634c60fac1SEmil Velikov vga_count++; 2644c60fac1SEmil Velikov 2654c60fac1SEmil Velikov retval = nouveau_dsm_pci_probe(pdev); 2664c60fac1SEmil Velikov if (retval & NOUVEAU_DSM_HAS_MUX) 2674c60fac1SEmil Velikov has_dsm |= 1; 2684c60fac1SEmil Velikov if (retval & NOUVEAU_DSM_HAS_OPT) 2694c60fac1SEmil Velikov has_optimus = 1; 2704c60fac1SEmil Velikov } 2714c60fac1SEmil Velikov 272c839d748SDave Airlie /* find the optimus DSM or the old v1 DSM */ 273c839d748SDave Airlie if (has_optimus == 1) { 274c839d748SDave Airlie acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, 275c839d748SDave Airlie &buffer); 276c839d748SDave Airlie printk(KERN_INFO "VGA switcheroo: detected Optimus DSM method %s handle\n", 277c839d748SDave Airlie acpi_method_name); 278c839d748SDave Airlie nouveau_dsm_priv.optimus_detected = true; 279c839d748SDave Airlie ret = true; 280c839d748SDave Airlie } else if (vga_count == 2 && has_dsm && guid_valid) { 281d099230cSPeter Lekensteyn acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, 282d099230cSPeter Lekensteyn &buffer); 2836a9ee8afSDave Airlie printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", 2846a9ee8afSDave Airlie acpi_method_name); 2856a9ee8afSDave Airlie nouveau_dsm_priv.dsm_detected = true; 286f244d8b6SRafael J. Wysocki /* 287f244d8b6SRafael J. Wysocki * On some systems hotplug events are generated for the device 288f244d8b6SRafael J. Wysocki * being switched off when _DSM is executed. They cause ACPI 289f244d8b6SRafael J. Wysocki * hotplug to trigger and attempt to remove the device from 290f244d8b6SRafael J. Wysocki * the system, which causes it to break down. Prevent that from 291f244d8b6SRafael J. Wysocki * happening by setting the no_hotplug flag for the involved 292f244d8b6SRafael J. Wysocki * ACPI device objects. 293f244d8b6SRafael J. Wysocki */ 294f244d8b6SRafael J. Wysocki acpi_bus_no_hotplug(nouveau_dsm_priv.dhandle); 295f244d8b6SRafael J. Wysocki acpi_bus_no_hotplug(nouveau_dsm_priv.other_handle); 296f19467c5SDave Airlie ret = true; 2976a9ee8afSDave Airlie } 298f19467c5SDave Airlie 299f19467c5SDave Airlie 300f19467c5SDave Airlie return ret; 3016a9ee8afSDave Airlie } 3026a9ee8afSDave Airlie 3036a9ee8afSDave Airlie void nouveau_register_dsm_handler(void) 3046a9ee8afSDave Airlie { 3056a9ee8afSDave Airlie bool r; 3066a9ee8afSDave Airlie 3076a9ee8afSDave Airlie r = nouveau_dsm_detect(); 3086a9ee8afSDave Airlie if (!r) 3096a9ee8afSDave Airlie return; 3106a9ee8afSDave Airlie 3116a9ee8afSDave Airlie vga_switcheroo_register_handler(&nouveau_dsm_handler); 3126a9ee8afSDave Airlie } 3136a9ee8afSDave Airlie 314d099230cSPeter Lekensteyn /* Must be called for Optimus models before the card can be turned off */ 315d099230cSPeter Lekensteyn void nouveau_switcheroo_optimus_dsm(void) 316d099230cSPeter Lekensteyn { 317d099230cSPeter Lekensteyn u32 result = 0; 318d099230cSPeter Lekensteyn if (!nouveau_dsm_priv.optimus_detected) 319d099230cSPeter Lekensteyn return; 320d099230cSPeter Lekensteyn 3215addcf0aSDave Airlie nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_FLAGS, 3225addcf0aSDave Airlie 0x3, &result); 3235addcf0aSDave Airlie 3245addcf0aSDave Airlie nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_CAPS, 3255addcf0aSDave Airlie NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN, &result); 3265addcf0aSDave Airlie 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 3723a83f992SRafael J. Wysocki dhandle = 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 4063a83f992SRafael J. Wysocki handle = 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