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> 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" 15a6ed76d7SBen 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 { 133*d1fbd923SDave Airlie /* easy option one - intel vendor ID means Integrated */ 134*d1fbd923SDave Airlie if (pdev->vendor == PCI_VENDOR_ID_INTEL) 1356a9ee8afSDave Airlie return VGA_SWITCHEROO_IGD; 136*d1fbd923SDave Airlie 137*d1fbd923SDave Airlie /* is this device on Bus 0? - this may need improving */ 138*d1fbd923SDave Airlie if (pdev->bus->number == 0) 139*d1fbd923SDave Airlie return VGA_SWITCHEROO_IGD; 140*d1fbd923SDave Airlie 1416a9ee8afSDave Airlie return VGA_SWITCHEROO_DIS; 1426a9ee8afSDave Airlie } 1436a9ee8afSDave Airlie 1446a9ee8afSDave Airlie static struct vga_switcheroo_handler nouveau_dsm_handler = { 1456a9ee8afSDave Airlie .switchto = nouveau_dsm_switchto, 1466a9ee8afSDave Airlie .power_state = nouveau_dsm_power_state, 1476a9ee8afSDave Airlie .init = nouveau_dsm_init, 1486a9ee8afSDave Airlie .get_client_id = nouveau_dsm_get_client_id, 1496a9ee8afSDave Airlie }; 1506a9ee8afSDave Airlie 1516a9ee8afSDave Airlie static bool nouveau_dsm_pci_probe(struct pci_dev *pdev) 1526a9ee8afSDave Airlie { 1536a9ee8afSDave Airlie acpi_handle dhandle, nvidia_handle; 1546a9ee8afSDave Airlie acpi_status status; 1556a9ee8afSDave Airlie int ret; 1566a9ee8afSDave Airlie uint32_t result; 1576a9ee8afSDave Airlie 1586a9ee8afSDave Airlie dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); 1596a9ee8afSDave Airlie if (!dhandle) 1606a9ee8afSDave Airlie return false; 161afeb3e11SDave Airlie 1626a9ee8afSDave Airlie status = acpi_get_handle(dhandle, "_DSM", &nvidia_handle); 1636a9ee8afSDave Airlie if (ACPI_FAILURE(status)) { 1646a9ee8afSDave Airlie return false; 1656a9ee8afSDave Airlie } 1666a9ee8afSDave Airlie 167fc5ea29dSDave Airlie ret = nouveau_dsm(dhandle, NOUVEAU_DSM_SUPPORTED, 1686a9ee8afSDave Airlie NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &result); 1696a9ee8afSDave Airlie if (ret < 0) 1706ee73861SBen Skeggs return false; 1716ee73861SBen Skeggs 1726a9ee8afSDave Airlie nouveau_dsm_priv.dhandle = dhandle; 1736ee73861SBen Skeggs return true; 1746ee73861SBen Skeggs } 1756a9ee8afSDave Airlie 1766a9ee8afSDave Airlie static bool nouveau_dsm_detect(void) 1776a9ee8afSDave Airlie { 1786a9ee8afSDave Airlie char acpi_method_name[255] = { 0 }; 1796a9ee8afSDave Airlie struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; 1806a9ee8afSDave Airlie struct pci_dev *pdev = NULL; 1816a9ee8afSDave Airlie int has_dsm = 0; 1826a9ee8afSDave Airlie int vga_count = 0; 183afeb3e11SDave Airlie 1846a9ee8afSDave Airlie while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { 1856a9ee8afSDave Airlie vga_count++; 1866a9ee8afSDave Airlie 1876a9ee8afSDave Airlie has_dsm |= (nouveau_dsm_pci_probe(pdev) == true); 1886a9ee8afSDave Airlie } 1896a9ee8afSDave Airlie 1906a9ee8afSDave Airlie if (vga_count == 2 && has_dsm) { 191fc5ea29dSDave Airlie acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, &buffer); 1926a9ee8afSDave Airlie printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", 1936a9ee8afSDave Airlie acpi_method_name); 1946a9ee8afSDave Airlie nouveau_dsm_priv.dsm_detected = true; 1956a9ee8afSDave Airlie return true; 1966a9ee8afSDave Airlie } 1976a9ee8afSDave Airlie return false; 1986a9ee8afSDave Airlie } 1996a9ee8afSDave Airlie 2006a9ee8afSDave Airlie void nouveau_register_dsm_handler(void) 2016a9ee8afSDave Airlie { 2026a9ee8afSDave Airlie bool r; 2036a9ee8afSDave Airlie 2046a9ee8afSDave Airlie r = nouveau_dsm_detect(); 2056a9ee8afSDave Airlie if (!r) 2066a9ee8afSDave Airlie return; 2076a9ee8afSDave Airlie 2086a9ee8afSDave Airlie vga_switcheroo_register_handler(&nouveau_dsm_handler); 2096a9ee8afSDave Airlie } 2106a9ee8afSDave Airlie 2116a9ee8afSDave Airlie void nouveau_unregister_dsm_handler(void) 2126a9ee8afSDave Airlie { 2136a9ee8afSDave Airlie vga_switcheroo_unregister_handler(); 2146a9ee8afSDave Airlie } 215afeb3e11SDave Airlie 216afeb3e11SDave Airlie /* retrieve the ROM in 4k blocks */ 217afeb3e11SDave Airlie static int nouveau_rom_call(acpi_handle rom_handle, uint8_t *bios, 218afeb3e11SDave Airlie int offset, int len) 219afeb3e11SDave Airlie { 220afeb3e11SDave Airlie acpi_status status; 221afeb3e11SDave Airlie union acpi_object rom_arg_elements[2], *obj; 222afeb3e11SDave Airlie struct acpi_object_list rom_arg; 223afeb3e11SDave Airlie struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL}; 224afeb3e11SDave Airlie 225afeb3e11SDave Airlie rom_arg.count = 2; 226afeb3e11SDave Airlie rom_arg.pointer = &rom_arg_elements[0]; 227afeb3e11SDave Airlie 228afeb3e11SDave Airlie rom_arg_elements[0].type = ACPI_TYPE_INTEGER; 229afeb3e11SDave Airlie rom_arg_elements[0].integer.value = offset; 230afeb3e11SDave Airlie 231afeb3e11SDave Airlie rom_arg_elements[1].type = ACPI_TYPE_INTEGER; 232afeb3e11SDave Airlie rom_arg_elements[1].integer.value = len; 233afeb3e11SDave Airlie 234afeb3e11SDave Airlie status = acpi_evaluate_object(rom_handle, NULL, &rom_arg, &buffer); 235afeb3e11SDave Airlie if (ACPI_FAILURE(status)) { 236afeb3e11SDave Airlie printk(KERN_INFO "failed to evaluate ROM got %s\n", acpi_format_exception(status)); 237afeb3e11SDave Airlie return -ENODEV; 238afeb3e11SDave Airlie } 239afeb3e11SDave Airlie obj = (union acpi_object *)buffer.pointer; 240afeb3e11SDave Airlie memcpy(bios+offset, obj->buffer.pointer, len); 241afeb3e11SDave Airlie kfree(buffer.pointer); 242afeb3e11SDave Airlie return len; 243afeb3e11SDave Airlie } 244afeb3e11SDave Airlie 245afeb3e11SDave Airlie bool nouveau_acpi_rom_supported(struct pci_dev *pdev) 246afeb3e11SDave Airlie { 247afeb3e11SDave Airlie acpi_status status; 248afeb3e11SDave Airlie acpi_handle dhandle, rom_handle; 249afeb3e11SDave Airlie 250afeb3e11SDave Airlie if (!nouveau_dsm_priv.dsm_detected) 251afeb3e11SDave Airlie return false; 252afeb3e11SDave Airlie 253afeb3e11SDave Airlie dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); 254afeb3e11SDave Airlie if (!dhandle) 255afeb3e11SDave Airlie return false; 256afeb3e11SDave Airlie 257afeb3e11SDave Airlie status = acpi_get_handle(dhandle, "_ROM", &rom_handle); 258afeb3e11SDave Airlie if (ACPI_FAILURE(status)) 259afeb3e11SDave Airlie return false; 260afeb3e11SDave Airlie 261afeb3e11SDave Airlie nouveau_dsm_priv.rom_handle = rom_handle; 262afeb3e11SDave Airlie return true; 263afeb3e11SDave Airlie } 264afeb3e11SDave Airlie 265afeb3e11SDave Airlie int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len) 266afeb3e11SDave Airlie { 267afeb3e11SDave Airlie return nouveau_rom_call(nouveau_dsm_priv.rom_handle, bios, offset, len); 268afeb3e11SDave Airlie } 269a6ed76d7SBen Skeggs 270a6ed76d7SBen Skeggs int 271a6ed76d7SBen Skeggs nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) 272a6ed76d7SBen Skeggs { 273a6ed76d7SBen Skeggs struct nouveau_connector *nv_connector = nouveau_connector(connector); 274a6ed76d7SBen Skeggs struct acpi_device *acpidev; 275a6ed76d7SBen Skeggs acpi_handle handle; 276a6ed76d7SBen Skeggs int type, ret; 277a6ed76d7SBen Skeggs void *edid; 278a6ed76d7SBen Skeggs 279a6ed76d7SBen Skeggs switch (connector->connector_type) { 280a6ed76d7SBen Skeggs case DRM_MODE_CONNECTOR_LVDS: 281a6ed76d7SBen Skeggs case DRM_MODE_CONNECTOR_eDP: 282a6ed76d7SBen Skeggs type = ACPI_VIDEO_DISPLAY_LCD; 283a6ed76d7SBen Skeggs break; 284a6ed76d7SBen Skeggs default: 285a6ed76d7SBen Skeggs return -EINVAL; 286a6ed76d7SBen Skeggs } 287a6ed76d7SBen Skeggs 288a6ed76d7SBen Skeggs handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev); 289a6ed76d7SBen Skeggs if (!handle) 290a6ed76d7SBen Skeggs return -ENODEV; 291a6ed76d7SBen Skeggs 292a6ed76d7SBen Skeggs ret = acpi_bus_get_device(handle, &acpidev); 293a6ed76d7SBen Skeggs if (ret) 294a6ed76d7SBen Skeggs return -ENODEV; 295a6ed76d7SBen Skeggs 296a6ed76d7SBen Skeggs ret = acpi_video_get_edid(acpidev, type, -1, &edid); 297a6ed76d7SBen Skeggs if (ret < 0) 298a6ed76d7SBen Skeggs return ret; 299a6ed76d7SBen Skeggs 30024b102d3SBen Skeggs nv_connector->edid = kmemdup(edid, EDID_LENGTH, GFP_KERNEL); 301a6ed76d7SBen Skeggs return 0; 302a6ed76d7SBen Skeggs } 303