16ee73861SBen Skeggs #include <linux/pci.h> 26ee73861SBen Skeggs #include <linux/acpi.h> 36ee73861SBen Skeggs #include <acpi/acpi_drivers.h> 46ee73861SBen Skeggs #include <acpi/acpi_bus.h> 56ee73861SBen Skeggs 66ee73861SBen Skeggs #include "drmP.h" 76ee73861SBen Skeggs #include "drm.h" 86ee73861SBen Skeggs #include "drm_sarea.h" 96ee73861SBen Skeggs #include "drm_crtc_helper.h" 106ee73861SBen Skeggs #include "nouveau_drv.h" 116ee73861SBen Skeggs #include "nouveau_drm.h" 126ee73861SBen Skeggs #include "nv50_display.h" 136ee73861SBen Skeggs 14*6a9ee8afSDave Airlie #include <linux/vga_switcheroo.h> 15*6a9ee8afSDave Airlie 166ee73861SBen Skeggs #define NOUVEAU_DSM_SUPPORTED 0x00 176ee73861SBen Skeggs #define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x00 186ee73861SBen Skeggs 196ee73861SBen Skeggs #define NOUVEAU_DSM_ACTIVE 0x01 206ee73861SBen Skeggs #define NOUVEAU_DSM_ACTIVE_QUERY 0x00 216ee73861SBen Skeggs 226ee73861SBen Skeggs #define NOUVEAU_DSM_LED 0x02 236ee73861SBen Skeggs #define NOUVEAU_DSM_LED_STATE 0x00 246ee73861SBen Skeggs #define NOUVEAU_DSM_LED_OFF 0x10 256ee73861SBen Skeggs #define NOUVEAU_DSM_LED_STAMINA 0x11 266ee73861SBen Skeggs #define NOUVEAU_DSM_LED_SPEED 0x12 276ee73861SBen Skeggs 286ee73861SBen Skeggs #define NOUVEAU_DSM_POWER 0x03 296ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_STATE 0x00 306ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_SPEED 0x01 316ee73861SBen Skeggs #define NOUVEAU_DSM_POWER_STAMINA 0x02 326ee73861SBen Skeggs 33*6a9ee8afSDave Airlie static struct nouveau_dsm_priv { 34*6a9ee8afSDave Airlie bool dsm_detected; 35*6a9ee8afSDave Airlie acpi_handle dhandle; 36*6a9ee8afSDave Airlie acpi_handle dsm_handle; 37*6a9ee8afSDave Airlie } nouveau_dsm_priv; 38*6a9ee8afSDave Airlie 39*6a9ee8afSDave Airlie static const char nouveau_dsm_muid[] = { 406ee73861SBen Skeggs 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, 416ee73861SBen Skeggs 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, 426ee73861SBen Skeggs }; 436ee73861SBen Skeggs 44*6a9ee8afSDave Airlie static int nouveau_dsm(acpi_handle handle, int func, int arg, int *result) 45*6a9ee8afSDave Airlie { 466ee73861SBen Skeggs struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 476ee73861SBen Skeggs struct acpi_object_list input; 486ee73861SBen Skeggs union acpi_object params[4]; 496ee73861SBen Skeggs union acpi_object *obj; 506ee73861SBen Skeggs int err; 516ee73861SBen Skeggs 526ee73861SBen Skeggs input.count = 4; 536ee73861SBen Skeggs input.pointer = params; 546ee73861SBen Skeggs params[0].type = ACPI_TYPE_BUFFER; 55*6a9ee8afSDave Airlie params[0].buffer.length = sizeof(nouveau_dsm_muid); 56*6a9ee8afSDave Airlie params[0].buffer.pointer = (char *)nouveau_dsm_muid; 576ee73861SBen Skeggs params[1].type = ACPI_TYPE_INTEGER; 586ee73861SBen Skeggs params[1].integer.value = 0x00000102; 596ee73861SBen Skeggs params[2].type = ACPI_TYPE_INTEGER; 606ee73861SBen Skeggs params[2].integer.value = func; 616ee73861SBen Skeggs params[3].type = ACPI_TYPE_INTEGER; 626ee73861SBen Skeggs params[3].integer.value = arg; 636ee73861SBen Skeggs 646ee73861SBen Skeggs err = acpi_evaluate_object(handle, "_DSM", &input, &output); 656ee73861SBen Skeggs if (err) { 66*6a9ee8afSDave Airlie printk(KERN_INFO "failed to evaluate _DSM: %d\n", err); 676ee73861SBen Skeggs return err; 686ee73861SBen Skeggs } 696ee73861SBen Skeggs 706ee73861SBen Skeggs obj = (union acpi_object *)output.pointer; 716ee73861SBen Skeggs 726ee73861SBen Skeggs if (obj->type == ACPI_TYPE_INTEGER) 736ee73861SBen Skeggs if (obj->integer.value == 0x80000002) 746ee73861SBen Skeggs return -ENODEV; 756ee73861SBen Skeggs 766ee73861SBen Skeggs if (obj->type == ACPI_TYPE_BUFFER) { 776ee73861SBen Skeggs if (obj->buffer.length == 4 && result) { 786ee73861SBen Skeggs *result = 0; 796ee73861SBen Skeggs *result |= obj->buffer.pointer[0]; 806ee73861SBen Skeggs *result |= (obj->buffer.pointer[1] << 8); 816ee73861SBen Skeggs *result |= (obj->buffer.pointer[2] << 16); 826ee73861SBen Skeggs *result |= (obj->buffer.pointer[3] << 24); 836ee73861SBen Skeggs } 846ee73861SBen Skeggs } 856ee73861SBen Skeggs 866ee73861SBen Skeggs kfree(output.pointer); 876ee73861SBen Skeggs return 0; 886ee73861SBen Skeggs } 896ee73861SBen Skeggs 90*6a9ee8afSDave Airlie static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id) 916ee73861SBen Skeggs { 92*6a9ee8afSDave Airlie return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id, NULL); 936ee73861SBen Skeggs } 946ee73861SBen Skeggs 95*6a9ee8afSDave Airlie static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state) 96*6a9ee8afSDave Airlie { 97*6a9ee8afSDave Airlie int arg; 98*6a9ee8afSDave Airlie if (state == VGA_SWITCHEROO_ON) 99*6a9ee8afSDave Airlie arg = NOUVEAU_DSM_POWER_SPEED; 100*6a9ee8afSDave Airlie else 101*6a9ee8afSDave Airlie arg = NOUVEAU_DSM_POWER_STAMINA; 102*6a9ee8afSDave Airlie nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg, NULL); 1036ee73861SBen Skeggs return 0; 1046ee73861SBen Skeggs } 1056ee73861SBen Skeggs 106*6a9ee8afSDave Airlie static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id) 1076ee73861SBen Skeggs { 108*6a9ee8afSDave Airlie if (id == VGA_SWITCHEROO_IGD) 109*6a9ee8afSDave Airlie return nouveau_dsm_switch_mux(nouveau_dsm_priv.dsm_handle, NOUVEAU_DSM_LED_STAMINA); 110*6a9ee8afSDave Airlie else 111*6a9ee8afSDave Airlie return nouveau_dsm_switch_mux(nouveau_dsm_priv.dsm_handle, NOUVEAU_DSM_LED_SPEED); 112*6a9ee8afSDave Airlie } 1136ee73861SBen Skeggs 114*6a9ee8afSDave Airlie static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id, 115*6a9ee8afSDave Airlie enum vga_switcheroo_state state) 116*6a9ee8afSDave Airlie { 117*6a9ee8afSDave Airlie if (id == VGA_SWITCHEROO_IGD) 118*6a9ee8afSDave Airlie return 0; 119*6a9ee8afSDave Airlie 120*6a9ee8afSDave Airlie return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dsm_handle, state); 121*6a9ee8afSDave Airlie } 122*6a9ee8afSDave Airlie 123*6a9ee8afSDave Airlie static int nouveau_dsm_init(void) 124*6a9ee8afSDave Airlie { 125*6a9ee8afSDave Airlie return 0; 126*6a9ee8afSDave Airlie } 127*6a9ee8afSDave Airlie 128*6a9ee8afSDave Airlie static int nouveau_dsm_get_client_id(struct pci_dev *pdev) 129*6a9ee8afSDave Airlie { 130*6a9ee8afSDave Airlie if (nouveau_dsm_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev)) 131*6a9ee8afSDave Airlie return VGA_SWITCHEROO_IGD; 132*6a9ee8afSDave Airlie else 133*6a9ee8afSDave Airlie return VGA_SWITCHEROO_DIS; 134*6a9ee8afSDave Airlie } 135*6a9ee8afSDave Airlie 136*6a9ee8afSDave Airlie static struct vga_switcheroo_handler nouveau_dsm_handler = { 137*6a9ee8afSDave Airlie .switchto = nouveau_dsm_switchto, 138*6a9ee8afSDave Airlie .power_state = nouveau_dsm_power_state, 139*6a9ee8afSDave Airlie .init = nouveau_dsm_init, 140*6a9ee8afSDave Airlie .get_client_id = nouveau_dsm_get_client_id, 141*6a9ee8afSDave Airlie }; 142*6a9ee8afSDave Airlie 143*6a9ee8afSDave Airlie static bool nouveau_dsm_pci_probe(struct pci_dev *pdev) 144*6a9ee8afSDave Airlie { 145*6a9ee8afSDave Airlie acpi_handle dhandle, nvidia_handle; 146*6a9ee8afSDave Airlie acpi_status status; 147*6a9ee8afSDave Airlie int ret; 148*6a9ee8afSDave Airlie uint32_t result; 149*6a9ee8afSDave Airlie 150*6a9ee8afSDave Airlie dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); 151*6a9ee8afSDave Airlie if (!dhandle) 152*6a9ee8afSDave Airlie return false; 153*6a9ee8afSDave Airlie status = acpi_get_handle(dhandle, "_DSM", &nvidia_handle); 154*6a9ee8afSDave Airlie if (ACPI_FAILURE(status)) { 155*6a9ee8afSDave Airlie return false; 156*6a9ee8afSDave Airlie } 157*6a9ee8afSDave Airlie 158*6a9ee8afSDave Airlie ret= nouveau_dsm(nvidia_handle, NOUVEAU_DSM_SUPPORTED, 159*6a9ee8afSDave Airlie NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &result); 160*6a9ee8afSDave Airlie if (ret < 0) 1616ee73861SBen Skeggs return false; 1626ee73861SBen Skeggs 163*6a9ee8afSDave Airlie nouveau_dsm_priv.dhandle = dhandle; 164*6a9ee8afSDave Airlie nouveau_dsm_priv.dsm_handle = nvidia_handle; 1656ee73861SBen Skeggs return true; 1666ee73861SBen Skeggs } 167*6a9ee8afSDave Airlie 168*6a9ee8afSDave Airlie static bool nouveau_dsm_detect(void) 169*6a9ee8afSDave Airlie { 170*6a9ee8afSDave Airlie char acpi_method_name[255] = { 0 }; 171*6a9ee8afSDave Airlie struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; 172*6a9ee8afSDave Airlie struct pci_dev *pdev = NULL; 173*6a9ee8afSDave Airlie int has_dsm = 0; 174*6a9ee8afSDave Airlie int vga_count = 0; 175*6a9ee8afSDave Airlie while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { 176*6a9ee8afSDave Airlie vga_count++; 177*6a9ee8afSDave Airlie 178*6a9ee8afSDave Airlie has_dsm |= (nouveau_dsm_pci_probe(pdev) == true); 179*6a9ee8afSDave Airlie } 180*6a9ee8afSDave Airlie 181*6a9ee8afSDave Airlie if (vga_count == 2 && has_dsm) { 182*6a9ee8afSDave Airlie acpi_get_name(nouveau_dsm_priv.dsm_handle, ACPI_FULL_PATHNAME, &buffer); 183*6a9ee8afSDave Airlie printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", 184*6a9ee8afSDave Airlie acpi_method_name); 185*6a9ee8afSDave Airlie nouveau_dsm_priv.dsm_detected = true; 186*6a9ee8afSDave Airlie return true; 187*6a9ee8afSDave Airlie } 188*6a9ee8afSDave Airlie return false; 189*6a9ee8afSDave Airlie } 190*6a9ee8afSDave Airlie 191*6a9ee8afSDave Airlie void nouveau_register_dsm_handler(void) 192*6a9ee8afSDave Airlie { 193*6a9ee8afSDave Airlie bool r; 194*6a9ee8afSDave Airlie 195*6a9ee8afSDave Airlie r = nouveau_dsm_detect(); 196*6a9ee8afSDave Airlie if (!r) 197*6a9ee8afSDave Airlie return; 198*6a9ee8afSDave Airlie 199*6a9ee8afSDave Airlie vga_switcheroo_register_handler(&nouveau_dsm_handler); 200*6a9ee8afSDave Airlie } 201*6a9ee8afSDave Airlie 202*6a9ee8afSDave Airlie void nouveau_unregister_dsm_handler(void) 203*6a9ee8afSDave Airlie { 204*6a9ee8afSDave Airlie vga_switcheroo_unregister_handler(); 205*6a9ee8afSDave Airlie } 206