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> 6*a6ed76d7SBen Skeggs #include <acpi/video.h> 76ee73861SBen Skeggs 86ee73861SBen Skeggs #include "drmP.h" 96ee73861SBen Skeggs #include "drm.h" 106ee73861SBen Skeggs #include "drm_sarea.h" 116ee73861SBen Skeggs #include "drm_crtc_helper.h" 126ee73861SBen Skeggs #include "nouveau_drv.h" 136ee73861SBen Skeggs #include "nouveau_drm.h" 146ee73861SBen Skeggs #include "nv50_display.h" 15*a6ed76d7SBen Skeggs #include "nouveau_connector.h" 166ee73861SBen Skeggs 176a9ee8afSDave Airlie #include <linux/vga_switcheroo.h> 186a9ee8afSDave Airlie 196ee73861SBen Skeggs #define NOUVEAU_DSM_SUPPORTED 0x00 206ee73861SBen Skeggs #define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x00 216ee73861SBen Skeggs 226ee73861SBen Skeggs #define NOUVEAU_DSM_ACTIVE 0x01 236ee73861SBen Skeggs #define NOUVEAU_DSM_ACTIVE_QUERY 0x00 246ee73861SBen Skeggs 256ee73861SBen Skeggs #define NOUVEAU_DSM_LED 0x02 266ee73861SBen Skeggs #define NOUVEAU_DSM_LED_STATE 0x00 276ee73861SBen Skeggs #define NOUVEAU_DSM_LED_OFF 0x10 286ee73861SBen Skeggs #define NOUVEAU_DSM_LED_STAMINA 0x11 296ee73861SBen Skeggs #define NOUVEAU_DSM_LED_SPEED 0x12 306ee73861SBen Skeggs 316ee73861SBen Skeggs #define NOUVEAU_DSM_POWER 0x03 326ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_STATE 0x00 336ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_SPEED 0x01 346ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_STAMINA 0x02 356ee73861SBen Skeggs 366a9ee8afSDave Airlie static struct nouveau_dsm_priv { 376a9ee8afSDave Airlie bool dsm_detected; 386a9ee8afSDave Airlie acpi_handle dhandle; 39afeb3e11SDave Airlie acpi_handle rom_handle; 406a9ee8afSDave Airlie } nouveau_dsm_priv; 416a9ee8afSDave Airlie 426a9ee8afSDave Airlie static const char nouveau_dsm_muid[] = { 436ee73861SBen Skeggs 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, 446ee73861SBen Skeggs 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, 456ee73861SBen Skeggs }; 466ee73861SBen Skeggs 476e86e041SFrancisco Jerez static int nouveau_dsm(acpi_handle handle, int func, int arg, uint32_t *result) 486a9ee8afSDave Airlie { 496ee73861SBen Skeggs struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 506ee73861SBen Skeggs struct acpi_object_list input; 516ee73861SBen Skeggs union acpi_object params[4]; 526ee73861SBen Skeggs union acpi_object *obj; 536ee73861SBen Skeggs int err; 546ee73861SBen Skeggs 556ee73861SBen Skeggs input.count = 4; 566ee73861SBen Skeggs input.pointer = params; 576ee73861SBen Skeggs params[0].type = ACPI_TYPE_BUFFER; 586a9ee8afSDave Airlie params[0].buffer.length = sizeof(nouveau_dsm_muid); 596a9ee8afSDave Airlie params[0].buffer.pointer = (char *)nouveau_dsm_muid; 606ee73861SBen Skeggs params[1].type = ACPI_TYPE_INTEGER; 616ee73861SBen Skeggs params[1].integer.value = 0x00000102; 626ee73861SBen Skeggs params[2].type = ACPI_TYPE_INTEGER; 636ee73861SBen Skeggs params[2].integer.value = func; 646ee73861SBen Skeggs params[3].type = ACPI_TYPE_INTEGER; 656ee73861SBen Skeggs params[3].integer.value = arg; 666ee73861SBen Skeggs 676ee73861SBen Skeggs err = acpi_evaluate_object(handle, "_DSM", &input, &output); 686ee73861SBen Skeggs if (err) { 696a9ee8afSDave Airlie printk(KERN_INFO "failed to evaluate _DSM: %d\n", err); 706ee73861SBen Skeggs return err; 716ee73861SBen Skeggs } 726ee73861SBen Skeggs 736ee73861SBen Skeggs obj = (union acpi_object *)output.pointer; 746ee73861SBen Skeggs 756ee73861SBen Skeggs if (obj->type == ACPI_TYPE_INTEGER) 766ee73861SBen Skeggs if (obj->integer.value == 0x80000002) 776ee73861SBen Skeggs return -ENODEV; 786ee73861SBen Skeggs 796ee73861SBen Skeggs if (obj->type == ACPI_TYPE_BUFFER) { 806ee73861SBen Skeggs if (obj->buffer.length == 4 && result) { 816ee73861SBen Skeggs *result = 0; 826ee73861SBen Skeggs *result |= obj->buffer.pointer[0]; 836ee73861SBen Skeggs *result |= (obj->buffer.pointer[1] << 8); 846ee73861SBen Skeggs *result |= (obj->buffer.pointer[2] << 16); 856ee73861SBen Skeggs *result |= (obj->buffer.pointer[3] << 24); 866ee73861SBen Skeggs } 876ee73861SBen Skeggs } 886ee73861SBen Skeggs 896ee73861SBen Skeggs kfree(output.pointer); 906ee73861SBen Skeggs return 0; 916ee73861SBen Skeggs } 926ee73861SBen Skeggs 936a9ee8afSDave Airlie static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id) 946ee73861SBen Skeggs { 956a9ee8afSDave Airlie return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id, NULL); 966ee73861SBen Skeggs } 976ee73861SBen Skeggs 986a9ee8afSDave Airlie static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state) 996a9ee8afSDave Airlie { 1006a9ee8afSDave Airlie int arg; 1016a9ee8afSDave Airlie if (state == VGA_SWITCHEROO_ON) 1026a9ee8afSDave Airlie arg = NOUVEAU_DSM_POWER_SPEED; 1036a9ee8afSDave Airlie else 1046a9ee8afSDave Airlie arg = NOUVEAU_DSM_POWER_STAMINA; 1056a9ee8afSDave Airlie nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg, NULL); 1066ee73861SBen Skeggs return 0; 1076ee73861SBen Skeggs } 1086ee73861SBen Skeggs 1096a9ee8afSDave Airlie static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id) 1106ee73861SBen Skeggs { 1116a9ee8afSDave Airlie if (id == VGA_SWITCHEROO_IGD) 112fc5ea29dSDave Airlie return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA); 1136a9ee8afSDave Airlie else 114fc5ea29dSDave Airlie return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_SPEED); 1156a9ee8afSDave Airlie } 1166ee73861SBen Skeggs 1176a9ee8afSDave Airlie static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id, 1186a9ee8afSDave Airlie enum vga_switcheroo_state state) 1196a9ee8afSDave Airlie { 1206a9ee8afSDave Airlie if (id == VGA_SWITCHEROO_IGD) 1216a9ee8afSDave Airlie return 0; 1226a9ee8afSDave Airlie 123fc5ea29dSDave Airlie return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state); 1246a9ee8afSDave Airlie } 1256a9ee8afSDave Airlie 1266a9ee8afSDave Airlie static int nouveau_dsm_init(void) 1276a9ee8afSDave Airlie { 1286a9ee8afSDave Airlie return 0; 1296a9ee8afSDave Airlie } 1306a9ee8afSDave Airlie 1316a9ee8afSDave Airlie static int nouveau_dsm_get_client_id(struct pci_dev *pdev) 1326a9ee8afSDave Airlie { 1336a9ee8afSDave Airlie if (nouveau_dsm_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev)) 1346a9ee8afSDave Airlie return VGA_SWITCHEROO_IGD; 1356a9ee8afSDave Airlie else 1366a9ee8afSDave Airlie return VGA_SWITCHEROO_DIS; 1376a9ee8afSDave Airlie } 1386a9ee8afSDave Airlie 1396a9ee8afSDave Airlie static struct vga_switcheroo_handler nouveau_dsm_handler = { 1406a9ee8afSDave Airlie .switchto = nouveau_dsm_switchto, 1416a9ee8afSDave Airlie .power_state = nouveau_dsm_power_state, 1426a9ee8afSDave Airlie .init = nouveau_dsm_init, 1436a9ee8afSDave Airlie .get_client_id = nouveau_dsm_get_client_id, 1446a9ee8afSDave Airlie }; 1456a9ee8afSDave Airlie 1466a9ee8afSDave Airlie static bool nouveau_dsm_pci_probe(struct pci_dev *pdev) 1476a9ee8afSDave Airlie { 1486a9ee8afSDave Airlie acpi_handle dhandle, nvidia_handle; 1496a9ee8afSDave Airlie acpi_status status; 1506a9ee8afSDave Airlie int ret; 1516a9ee8afSDave Airlie uint32_t result; 1526a9ee8afSDave Airlie 1536a9ee8afSDave Airlie dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); 1546a9ee8afSDave Airlie if (!dhandle) 1556a9ee8afSDave Airlie return false; 156afeb3e11SDave Airlie 1576a9ee8afSDave Airlie status = acpi_get_handle(dhandle, "_DSM", &nvidia_handle); 1586a9ee8afSDave Airlie if (ACPI_FAILURE(status)) { 1596a9ee8afSDave Airlie return false; 1606a9ee8afSDave Airlie } 1616a9ee8afSDave Airlie 162fc5ea29dSDave Airlie ret = nouveau_dsm(dhandle, NOUVEAU_DSM_SUPPORTED, 1636a9ee8afSDave Airlie NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &result); 1646a9ee8afSDave Airlie if (ret < 0) 1656ee73861SBen Skeggs return false; 1666ee73861SBen Skeggs 1676a9ee8afSDave Airlie nouveau_dsm_priv.dhandle = dhandle; 1686ee73861SBen Skeggs return true; 1696ee73861SBen Skeggs } 1706a9ee8afSDave Airlie 1716a9ee8afSDave Airlie static bool nouveau_dsm_detect(void) 1726a9ee8afSDave Airlie { 1736a9ee8afSDave Airlie char acpi_method_name[255] = { 0 }; 1746a9ee8afSDave Airlie struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; 1756a9ee8afSDave Airlie struct pci_dev *pdev = NULL; 1766a9ee8afSDave Airlie int has_dsm = 0; 1776a9ee8afSDave Airlie int vga_count = 0; 178afeb3e11SDave Airlie 1796a9ee8afSDave Airlie while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { 1806a9ee8afSDave Airlie vga_count++; 1816a9ee8afSDave Airlie 1826a9ee8afSDave Airlie has_dsm |= (nouveau_dsm_pci_probe(pdev) == true); 1836a9ee8afSDave Airlie } 1846a9ee8afSDave Airlie 1856a9ee8afSDave Airlie if (vga_count == 2 && has_dsm) { 186fc5ea29dSDave Airlie acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, &buffer); 1876a9ee8afSDave Airlie printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", 1886a9ee8afSDave Airlie acpi_method_name); 1896a9ee8afSDave Airlie nouveau_dsm_priv.dsm_detected = true; 1906a9ee8afSDave Airlie return true; 1916a9ee8afSDave Airlie } 1926a9ee8afSDave Airlie return false; 1936a9ee8afSDave Airlie } 1946a9ee8afSDave Airlie 1956a9ee8afSDave Airlie void nouveau_register_dsm_handler(void) 1966a9ee8afSDave Airlie { 1976a9ee8afSDave Airlie bool r; 1986a9ee8afSDave Airlie 1996a9ee8afSDave Airlie r = nouveau_dsm_detect(); 2006a9ee8afSDave Airlie if (!r) 2016a9ee8afSDave Airlie return; 2026a9ee8afSDave Airlie 2036a9ee8afSDave Airlie vga_switcheroo_register_handler(&nouveau_dsm_handler); 2046a9ee8afSDave Airlie } 2056a9ee8afSDave Airlie 2066a9ee8afSDave Airlie void nouveau_unregister_dsm_handler(void) 2076a9ee8afSDave Airlie { 2086a9ee8afSDave Airlie vga_switcheroo_unregister_handler(); 2096a9ee8afSDave Airlie } 210afeb3e11SDave Airlie 211afeb3e11SDave Airlie /* retrieve the ROM in 4k blocks */ 212afeb3e11SDave Airlie static int nouveau_rom_call(acpi_handle rom_handle, uint8_t *bios, 213afeb3e11SDave Airlie int offset, int len) 214afeb3e11SDave Airlie { 215afeb3e11SDave Airlie acpi_status status; 216afeb3e11SDave Airlie union acpi_object rom_arg_elements[2], *obj; 217afeb3e11SDave Airlie struct acpi_object_list rom_arg; 218afeb3e11SDave Airlie struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL}; 219afeb3e11SDave Airlie 220afeb3e11SDave Airlie rom_arg.count = 2; 221afeb3e11SDave Airlie rom_arg.pointer = &rom_arg_elements[0]; 222afeb3e11SDave Airlie 223afeb3e11SDave Airlie rom_arg_elements[0].type = ACPI_TYPE_INTEGER; 224afeb3e11SDave Airlie rom_arg_elements[0].integer.value = offset; 225afeb3e11SDave Airlie 226afeb3e11SDave Airlie rom_arg_elements[1].type = ACPI_TYPE_INTEGER; 227afeb3e11SDave Airlie rom_arg_elements[1].integer.value = len; 228afeb3e11SDave Airlie 229afeb3e11SDave Airlie status = acpi_evaluate_object(rom_handle, NULL, &rom_arg, &buffer); 230afeb3e11SDave Airlie if (ACPI_FAILURE(status)) { 231afeb3e11SDave Airlie printk(KERN_INFO "failed to evaluate ROM got %s\n", acpi_format_exception(status)); 232afeb3e11SDave Airlie return -ENODEV; 233afeb3e11SDave Airlie } 234afeb3e11SDave Airlie obj = (union acpi_object *)buffer.pointer; 235afeb3e11SDave Airlie memcpy(bios+offset, obj->buffer.pointer, len); 236afeb3e11SDave Airlie kfree(buffer.pointer); 237afeb3e11SDave Airlie return len; 238afeb3e11SDave Airlie } 239afeb3e11SDave Airlie 240afeb3e11SDave Airlie bool nouveau_acpi_rom_supported(struct pci_dev *pdev) 241afeb3e11SDave Airlie { 242afeb3e11SDave Airlie acpi_status status; 243afeb3e11SDave Airlie acpi_handle dhandle, rom_handle; 244afeb3e11SDave Airlie 245afeb3e11SDave Airlie if (!nouveau_dsm_priv.dsm_detected) 246afeb3e11SDave Airlie return false; 247afeb3e11SDave Airlie 248afeb3e11SDave Airlie dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); 249afeb3e11SDave Airlie if (!dhandle) 250afeb3e11SDave Airlie return false; 251afeb3e11SDave Airlie 252afeb3e11SDave Airlie status = acpi_get_handle(dhandle, "_ROM", &rom_handle); 253afeb3e11SDave Airlie if (ACPI_FAILURE(status)) 254afeb3e11SDave Airlie return false; 255afeb3e11SDave Airlie 256afeb3e11SDave Airlie nouveau_dsm_priv.rom_handle = rom_handle; 257afeb3e11SDave Airlie return true; 258afeb3e11SDave Airlie } 259afeb3e11SDave Airlie 260afeb3e11SDave Airlie int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len) 261afeb3e11SDave Airlie { 262afeb3e11SDave Airlie return nouveau_rom_call(nouveau_dsm_priv.rom_handle, bios, offset, len); 263afeb3e11SDave Airlie } 264*a6ed76d7SBen Skeggs 265*a6ed76d7SBen Skeggs int 266*a6ed76d7SBen Skeggs nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) 267*a6ed76d7SBen Skeggs { 268*a6ed76d7SBen Skeggs struct nouveau_connector *nv_connector = nouveau_connector(connector); 269*a6ed76d7SBen Skeggs struct acpi_device *acpidev; 270*a6ed76d7SBen Skeggs acpi_handle handle; 271*a6ed76d7SBen Skeggs int type, ret; 272*a6ed76d7SBen Skeggs void *edid; 273*a6ed76d7SBen Skeggs 274*a6ed76d7SBen Skeggs switch (connector->connector_type) { 275*a6ed76d7SBen Skeggs case DRM_MODE_CONNECTOR_LVDS: 276*a6ed76d7SBen Skeggs case DRM_MODE_CONNECTOR_eDP: 277*a6ed76d7SBen Skeggs type = ACPI_VIDEO_DISPLAY_LCD; 278*a6ed76d7SBen Skeggs break; 279*a6ed76d7SBen Skeggs default: 280*a6ed76d7SBen Skeggs return -EINVAL; 281*a6ed76d7SBen Skeggs } 282*a6ed76d7SBen Skeggs 283*a6ed76d7SBen Skeggs handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev); 284*a6ed76d7SBen Skeggs if (!handle) 285*a6ed76d7SBen Skeggs return -ENODEV; 286*a6ed76d7SBen Skeggs 287*a6ed76d7SBen Skeggs ret = acpi_bus_get_device(handle, &acpidev); 288*a6ed76d7SBen Skeggs if (ret) 289*a6ed76d7SBen Skeggs return -ENODEV; 290*a6ed76d7SBen Skeggs 291*a6ed76d7SBen Skeggs ret = acpi_video_get_edid(acpidev, type, -1, &edid); 292*a6ed76d7SBen Skeggs if (ret < 0) 293*a6ed76d7SBen Skeggs return ret; 294*a6ed76d7SBen Skeggs 295*a6ed76d7SBen Skeggs nv_connector->edid = edid; 296*a6ed76d7SBen Skeggs return 0; 297*a6ed76d7SBen Skeggs } 298