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> 7*8116188fSDave Airlie #include <acpi/acpi.h> 8*8116188fSDave Airlie #include <linux/mxm-wmi.h> 96ee73861SBen Skeggs 106ee73861SBen Skeggs #include "drmP.h" 116ee73861SBen Skeggs #include "drm.h" 126ee73861SBen Skeggs #include "drm_sarea.h" 136ee73861SBen Skeggs #include "drm_crtc_helper.h" 146ee73861SBen Skeggs #include "nouveau_drv.h" 156ee73861SBen Skeggs #include "nouveau_drm.h" 166ee73861SBen Skeggs #include "nv50_display.h" 17a6ed76d7SBen Skeggs #include "nouveau_connector.h" 186ee73861SBen Skeggs 196a9ee8afSDave Airlie #include <linux/vga_switcheroo.h> 206a9ee8afSDave Airlie 216ee73861SBen Skeggs #define NOUVEAU_DSM_SUPPORTED 0x00 226ee73861SBen Skeggs #define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x00 236ee73861SBen Skeggs 246ee73861SBen Skeggs #define NOUVEAU_DSM_ACTIVE 0x01 256ee73861SBen Skeggs #define NOUVEAU_DSM_ACTIVE_QUERY 0x00 266ee73861SBen Skeggs 276ee73861SBen Skeggs #define NOUVEAU_DSM_LED 0x02 286ee73861SBen Skeggs #define NOUVEAU_DSM_LED_STATE 0x00 296ee73861SBen Skeggs #define NOUVEAU_DSM_LED_OFF 0x10 306ee73861SBen Skeggs #define NOUVEAU_DSM_LED_STAMINA 0x11 316ee73861SBen Skeggs #define NOUVEAU_DSM_LED_SPEED 0x12 326ee73861SBen Skeggs 336ee73861SBen Skeggs #define NOUVEAU_DSM_POWER 0x03 346ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_STATE 0x00 356ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_SPEED 0x01 366ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_STAMINA 0x02 376ee73861SBen Skeggs 386a9ee8afSDave Airlie static struct nouveau_dsm_priv { 396a9ee8afSDave Airlie bool dsm_detected; 406a9ee8afSDave Airlie acpi_handle dhandle; 41afeb3e11SDave Airlie acpi_handle rom_handle; 426a9ee8afSDave Airlie } nouveau_dsm_priv; 436a9ee8afSDave Airlie 446a9ee8afSDave Airlie static const char nouveau_dsm_muid[] = { 456ee73861SBen Skeggs 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, 466ee73861SBen Skeggs 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, 476ee73861SBen Skeggs }; 486ee73861SBen Skeggs 496e86e041SFrancisco Jerez static int nouveau_dsm(acpi_handle handle, int func, int arg, uint32_t *result) 506a9ee8afSDave Airlie { 516ee73861SBen Skeggs struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 526ee73861SBen Skeggs struct acpi_object_list input; 536ee73861SBen Skeggs union acpi_object params[4]; 546ee73861SBen Skeggs union acpi_object *obj; 556ee73861SBen Skeggs int err; 566ee73861SBen Skeggs 576ee73861SBen Skeggs input.count = 4; 586ee73861SBen Skeggs input.pointer = params; 596ee73861SBen Skeggs params[0].type = ACPI_TYPE_BUFFER; 606a9ee8afSDave Airlie params[0].buffer.length = sizeof(nouveau_dsm_muid); 616a9ee8afSDave Airlie params[0].buffer.pointer = (char *)nouveau_dsm_muid; 626ee73861SBen Skeggs params[1].type = ACPI_TYPE_INTEGER; 636ee73861SBen Skeggs params[1].integer.value = 0x00000102; 646ee73861SBen Skeggs params[2].type = ACPI_TYPE_INTEGER; 656ee73861SBen Skeggs params[2].integer.value = func; 666ee73861SBen Skeggs params[3].type = ACPI_TYPE_INTEGER; 676ee73861SBen Skeggs params[3].integer.value = arg; 686ee73861SBen Skeggs 696ee73861SBen Skeggs err = acpi_evaluate_object(handle, "_DSM", &input, &output); 706ee73861SBen Skeggs if (err) { 716a9ee8afSDave Airlie printk(KERN_INFO "failed to evaluate _DSM: %d\n", err); 726ee73861SBen Skeggs return err; 736ee73861SBen Skeggs } 746ee73861SBen Skeggs 756ee73861SBen Skeggs obj = (union acpi_object *)output.pointer; 766ee73861SBen Skeggs 776ee73861SBen Skeggs if (obj->type == ACPI_TYPE_INTEGER) 786ee73861SBen Skeggs if (obj->integer.value == 0x80000002) 796ee73861SBen Skeggs return -ENODEV; 806ee73861SBen Skeggs 816ee73861SBen Skeggs if (obj->type == ACPI_TYPE_BUFFER) { 826ee73861SBen Skeggs if (obj->buffer.length == 4 && result) { 836ee73861SBen Skeggs *result = 0; 846ee73861SBen Skeggs *result |= obj->buffer.pointer[0]; 856ee73861SBen Skeggs *result |= (obj->buffer.pointer[1] << 8); 866ee73861SBen Skeggs *result |= (obj->buffer.pointer[2] << 16); 876ee73861SBen Skeggs *result |= (obj->buffer.pointer[3] << 24); 886ee73861SBen Skeggs } 896ee73861SBen Skeggs } 906ee73861SBen Skeggs 916ee73861SBen Skeggs kfree(output.pointer); 926ee73861SBen Skeggs return 0; 936ee73861SBen Skeggs } 946ee73861SBen Skeggs 956a9ee8afSDave Airlie static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id) 966ee73861SBen Skeggs { 97*8116188fSDave Airlie mxm_wmi_call_mxds(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0); 986a9ee8afSDave Airlie return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id, NULL); 996ee73861SBen Skeggs } 1006ee73861SBen Skeggs 1016a9ee8afSDave Airlie static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state) 1026a9ee8afSDave Airlie { 1036a9ee8afSDave Airlie int arg; 1046a9ee8afSDave Airlie if (state == VGA_SWITCHEROO_ON) 1056a9ee8afSDave Airlie arg = NOUVEAU_DSM_POWER_SPEED; 1066a9ee8afSDave Airlie else 1076a9ee8afSDave Airlie arg = NOUVEAU_DSM_POWER_STAMINA; 1086a9ee8afSDave Airlie nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg, NULL); 1096ee73861SBen Skeggs return 0; 1106ee73861SBen Skeggs } 1116ee73861SBen Skeggs 1126a9ee8afSDave Airlie static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id) 1136ee73861SBen Skeggs { 1146a9ee8afSDave Airlie if (id == VGA_SWITCHEROO_IGD) 115fc5ea29dSDave Airlie return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA); 1166a9ee8afSDave Airlie else 117fc5ea29dSDave Airlie return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_SPEED); 1186a9ee8afSDave Airlie } 1196ee73861SBen Skeggs 1206a9ee8afSDave Airlie static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id, 1216a9ee8afSDave Airlie enum vga_switcheroo_state state) 1226a9ee8afSDave Airlie { 1236a9ee8afSDave Airlie if (id == VGA_SWITCHEROO_IGD) 1246a9ee8afSDave Airlie return 0; 1256a9ee8afSDave Airlie 126fc5ea29dSDave Airlie return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state); 1276a9ee8afSDave Airlie } 1286a9ee8afSDave Airlie 1296a9ee8afSDave Airlie static int nouveau_dsm_init(void) 1306a9ee8afSDave Airlie { 1316a9ee8afSDave Airlie return 0; 1326a9ee8afSDave Airlie } 1336a9ee8afSDave Airlie 1346a9ee8afSDave Airlie static int nouveau_dsm_get_client_id(struct pci_dev *pdev) 1356a9ee8afSDave Airlie { 136d1fbd923SDave Airlie /* easy option one - intel vendor ID means Integrated */ 137d1fbd923SDave Airlie if (pdev->vendor == PCI_VENDOR_ID_INTEL) 1386a9ee8afSDave Airlie return VGA_SWITCHEROO_IGD; 139d1fbd923SDave Airlie 140d1fbd923SDave Airlie /* is this device on Bus 0? - this may need improving */ 141d1fbd923SDave Airlie if (pdev->bus->number == 0) 142d1fbd923SDave Airlie return VGA_SWITCHEROO_IGD; 143d1fbd923SDave Airlie 1446a9ee8afSDave Airlie return VGA_SWITCHEROO_DIS; 1456a9ee8afSDave Airlie } 1466a9ee8afSDave Airlie 1476a9ee8afSDave Airlie static struct vga_switcheroo_handler nouveau_dsm_handler = { 1486a9ee8afSDave Airlie .switchto = nouveau_dsm_switchto, 1496a9ee8afSDave Airlie .power_state = nouveau_dsm_power_state, 1506a9ee8afSDave Airlie .init = nouveau_dsm_init, 1516a9ee8afSDave Airlie .get_client_id = nouveau_dsm_get_client_id, 1526a9ee8afSDave Airlie }; 1536a9ee8afSDave Airlie 1546a9ee8afSDave Airlie static bool nouveau_dsm_pci_probe(struct pci_dev *pdev) 1556a9ee8afSDave Airlie { 1566a9ee8afSDave Airlie acpi_handle dhandle, nvidia_handle; 1576a9ee8afSDave Airlie acpi_status status; 1586a9ee8afSDave Airlie int ret; 1596a9ee8afSDave Airlie uint32_t result; 1606a9ee8afSDave Airlie 1616a9ee8afSDave Airlie dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); 1626a9ee8afSDave Airlie if (!dhandle) 1636a9ee8afSDave Airlie return false; 164afeb3e11SDave Airlie 1656a9ee8afSDave Airlie status = acpi_get_handle(dhandle, "_DSM", &nvidia_handle); 1666a9ee8afSDave Airlie if (ACPI_FAILURE(status)) { 1676a9ee8afSDave Airlie return false; 1686a9ee8afSDave Airlie } 1696a9ee8afSDave Airlie 170fc5ea29dSDave Airlie ret = nouveau_dsm(dhandle, NOUVEAU_DSM_SUPPORTED, 1716a9ee8afSDave Airlie NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &result); 1726a9ee8afSDave Airlie if (ret < 0) 1736ee73861SBen Skeggs return false; 1746ee73861SBen Skeggs 1756a9ee8afSDave Airlie nouveau_dsm_priv.dhandle = dhandle; 1766ee73861SBen Skeggs return true; 1776ee73861SBen Skeggs } 1786a9ee8afSDave Airlie 1796a9ee8afSDave Airlie static bool nouveau_dsm_detect(void) 1806a9ee8afSDave Airlie { 1816a9ee8afSDave Airlie char acpi_method_name[255] = { 0 }; 1826a9ee8afSDave Airlie struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; 1836a9ee8afSDave Airlie struct pci_dev *pdev = NULL; 1846a9ee8afSDave Airlie int has_dsm = 0; 1856a9ee8afSDave Airlie int vga_count = 0; 186*8116188fSDave Airlie bool guid_valid; 187*8116188fSDave Airlie 188*8116188fSDave Airlie /* lookup the GUID */ 189*8116188fSDave Airlie guid_valid = mxm_wmi_supported(); 190*8116188fSDave Airlie if (!guid_valid) 191*8116188fSDave Airlie return false; 192*8116188fSDave Airlie 193*8116188fSDave Airlie printk("MXM GUID detected in BIOS\n"); 194afeb3e11SDave Airlie 1956a9ee8afSDave Airlie while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { 1966a9ee8afSDave Airlie vga_count++; 1976a9ee8afSDave Airlie 1986a9ee8afSDave Airlie has_dsm |= (nouveau_dsm_pci_probe(pdev) == true); 1996a9ee8afSDave Airlie } 2006a9ee8afSDave Airlie 2016a9ee8afSDave Airlie if (vga_count == 2 && has_dsm) { 202fc5ea29dSDave Airlie acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, &buffer); 2036a9ee8afSDave Airlie printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", 2046a9ee8afSDave Airlie acpi_method_name); 2056a9ee8afSDave Airlie nouveau_dsm_priv.dsm_detected = true; 2066a9ee8afSDave Airlie return true; 2076a9ee8afSDave Airlie } 2086a9ee8afSDave Airlie return false; 2096a9ee8afSDave Airlie } 2106a9ee8afSDave Airlie 2116a9ee8afSDave Airlie void nouveau_register_dsm_handler(void) 2126a9ee8afSDave Airlie { 2136a9ee8afSDave Airlie bool r; 2146a9ee8afSDave Airlie 2156a9ee8afSDave Airlie r = nouveau_dsm_detect(); 2166a9ee8afSDave Airlie if (!r) 2176a9ee8afSDave Airlie return; 2186a9ee8afSDave Airlie 2196a9ee8afSDave Airlie vga_switcheroo_register_handler(&nouveau_dsm_handler); 2206a9ee8afSDave Airlie } 2216a9ee8afSDave Airlie 2226a9ee8afSDave Airlie void nouveau_unregister_dsm_handler(void) 2236a9ee8afSDave Airlie { 2246a9ee8afSDave Airlie vga_switcheroo_unregister_handler(); 2256a9ee8afSDave Airlie } 226afeb3e11SDave Airlie 227afeb3e11SDave Airlie /* retrieve the ROM in 4k blocks */ 228afeb3e11SDave Airlie static int nouveau_rom_call(acpi_handle rom_handle, uint8_t *bios, 229afeb3e11SDave Airlie int offset, int len) 230afeb3e11SDave Airlie { 231afeb3e11SDave Airlie acpi_status status; 232afeb3e11SDave Airlie union acpi_object rom_arg_elements[2], *obj; 233afeb3e11SDave Airlie struct acpi_object_list rom_arg; 234afeb3e11SDave Airlie struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL}; 235afeb3e11SDave Airlie 236afeb3e11SDave Airlie rom_arg.count = 2; 237afeb3e11SDave Airlie rom_arg.pointer = &rom_arg_elements[0]; 238afeb3e11SDave Airlie 239afeb3e11SDave Airlie rom_arg_elements[0].type = ACPI_TYPE_INTEGER; 240afeb3e11SDave Airlie rom_arg_elements[0].integer.value = offset; 241afeb3e11SDave Airlie 242afeb3e11SDave Airlie rom_arg_elements[1].type = ACPI_TYPE_INTEGER; 243afeb3e11SDave Airlie rom_arg_elements[1].integer.value = len; 244afeb3e11SDave Airlie 245afeb3e11SDave Airlie status = acpi_evaluate_object(rom_handle, NULL, &rom_arg, &buffer); 246afeb3e11SDave Airlie if (ACPI_FAILURE(status)) { 247afeb3e11SDave Airlie printk(KERN_INFO "failed to evaluate ROM got %s\n", acpi_format_exception(status)); 248afeb3e11SDave Airlie return -ENODEV; 249afeb3e11SDave Airlie } 250afeb3e11SDave Airlie obj = (union acpi_object *)buffer.pointer; 251afeb3e11SDave Airlie memcpy(bios+offset, obj->buffer.pointer, len); 252afeb3e11SDave Airlie kfree(buffer.pointer); 253afeb3e11SDave Airlie return len; 254afeb3e11SDave Airlie } 255afeb3e11SDave Airlie 256afeb3e11SDave Airlie bool nouveau_acpi_rom_supported(struct pci_dev *pdev) 257afeb3e11SDave Airlie { 258afeb3e11SDave Airlie acpi_status status; 259afeb3e11SDave Airlie acpi_handle dhandle, rom_handle; 260afeb3e11SDave Airlie 261afeb3e11SDave Airlie if (!nouveau_dsm_priv.dsm_detected) 262afeb3e11SDave Airlie return false; 263afeb3e11SDave Airlie 264afeb3e11SDave Airlie dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); 265afeb3e11SDave Airlie if (!dhandle) 266afeb3e11SDave Airlie return false; 267afeb3e11SDave Airlie 268afeb3e11SDave Airlie status = acpi_get_handle(dhandle, "_ROM", &rom_handle); 269afeb3e11SDave Airlie if (ACPI_FAILURE(status)) 270afeb3e11SDave Airlie return false; 271afeb3e11SDave Airlie 272afeb3e11SDave Airlie nouveau_dsm_priv.rom_handle = rom_handle; 273afeb3e11SDave Airlie return true; 274afeb3e11SDave Airlie } 275afeb3e11SDave Airlie 276afeb3e11SDave Airlie int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len) 277afeb3e11SDave Airlie { 278afeb3e11SDave Airlie return nouveau_rom_call(nouveau_dsm_priv.rom_handle, bios, offset, len); 279afeb3e11SDave Airlie } 280a6ed76d7SBen Skeggs 281a6ed76d7SBen Skeggs int 282a6ed76d7SBen Skeggs nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) 283a6ed76d7SBen Skeggs { 284a6ed76d7SBen Skeggs struct nouveau_connector *nv_connector = nouveau_connector(connector); 285a6ed76d7SBen Skeggs struct acpi_device *acpidev; 286a6ed76d7SBen Skeggs acpi_handle handle; 287a6ed76d7SBen Skeggs int type, ret; 288a6ed76d7SBen Skeggs void *edid; 289a6ed76d7SBen Skeggs 290a6ed76d7SBen Skeggs switch (connector->connector_type) { 291a6ed76d7SBen Skeggs case DRM_MODE_CONNECTOR_LVDS: 292a6ed76d7SBen Skeggs case DRM_MODE_CONNECTOR_eDP: 293a6ed76d7SBen Skeggs type = ACPI_VIDEO_DISPLAY_LCD; 294a6ed76d7SBen Skeggs break; 295a6ed76d7SBen Skeggs default: 296a6ed76d7SBen Skeggs return -EINVAL; 297a6ed76d7SBen Skeggs } 298a6ed76d7SBen Skeggs 299a6ed76d7SBen Skeggs handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev); 300a6ed76d7SBen Skeggs if (!handle) 301a6ed76d7SBen Skeggs return -ENODEV; 302a6ed76d7SBen Skeggs 303a6ed76d7SBen Skeggs ret = acpi_bus_get_device(handle, &acpidev); 304a6ed76d7SBen Skeggs if (ret) 305a6ed76d7SBen Skeggs return -ENODEV; 306a6ed76d7SBen Skeggs 307a6ed76d7SBen Skeggs ret = acpi_video_get_edid(acpidev, type, -1, &edid); 308a6ed76d7SBen Skeggs if (ret < 0) 309a6ed76d7SBen Skeggs return ret; 310a6ed76d7SBen Skeggs 31124b102d3SBen Skeggs nv_connector->edid = kmemdup(edid, EDID_LENGTH, GFP_KERNEL); 312a6ed76d7SBen Skeggs return 0; 313a6ed76d7SBen Skeggs } 314