diff options
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_acpi.c | 104 |
1 files changed, 89 insertions, 15 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c index 1aa33d96d5d6..e0a885b72e0e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c @@ -37,15 +37,71 @@ static struct nouveau_dsm_priv { bool dsm_detected; + bool optimus_detected; acpi_handle dhandle; acpi_handle rom_handle; } nouveau_dsm_priv; +#define NOUVEAU_DSM_HAS_MUX 0x1 +#define NOUVEAU_DSM_HAS_OPT 0x2 + static const char nouveau_dsm_muid[] = { 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, }; +static const char nouveau_op_dsm_muid[] = { + 0xF8, 0xD8, 0x86, 0xA4, 0xDA, 0x0B, 0x1B, 0x47, + 0xA7, 0x2B, 0x60, 0x42, 0xA6, 0xB5, 0xBE, 0xE0, +}; + +static int nouveau_optimus_dsm(acpi_handle handle, int func, int arg, uint32_t *result) +{ + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_object_list input; + union acpi_object params[4]; + union acpi_object *obj; + int err; + + input.count = 4; + input.pointer = params; + params[0].type = ACPI_TYPE_BUFFER; + params[0].buffer.length = sizeof(nouveau_op_dsm_muid); + params[0].buffer.pointer = (char *)nouveau_op_dsm_muid; + params[1].type = ACPI_TYPE_INTEGER; + params[1].integer.value = 0x00000100; + params[2].type = ACPI_TYPE_INTEGER; + params[2].integer.value = func; + params[3].type = ACPI_TYPE_BUFFER; + params[3].buffer.length = 0; + + err = acpi_evaluate_object(handle, "_DSM", &input, &output); + if (err) { + printk(KERN_INFO "failed to evaluate _DSM: %d\n", err); + return err; + } + + obj = (union acpi_object *)output.pointer; + + if (obj->type == ACPI_TYPE_INTEGER) + if (obj->integer.value == 0x80000002) { + return -ENODEV; + } + + if (obj->type == ACPI_TYPE_BUFFER) { + if (obj->buffer.length == 4 && result) { + *result = 0; + *result |= obj->buffer.pointer[0]; + *result |= (obj->buffer.pointer[1] << 8); + *result |= (obj->buffer.pointer[2] << 16); + *result |= (obj->buffer.pointer[3] << 24); + } + } + + kfree(output.pointer); + return 0; +} + static int nouveau_dsm(acpi_handle handle, int func, int arg, uint32_t *result) { struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -151,11 +207,11 @@ static struct vga_switcheroo_handler nouveau_dsm_handler = { .get_client_id = nouveau_dsm_get_client_id, }; -static bool nouveau_dsm_pci_probe(struct pci_dev *pdev) +static int nouveau_dsm_pci_probe(struct pci_dev *pdev) { acpi_handle dhandle, nvidia_handle; acpi_status status; - int ret; + int ret, retval = 0; uint32_t result; dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); @@ -169,11 +225,17 @@ static bool nouveau_dsm_pci_probe(struct pci_dev *pdev) ret = nouveau_dsm(dhandle, NOUVEAU_DSM_SUPPORTED, NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &result); - if (ret < 0) - return false; + if (ret == 0) + retval |= NOUVEAU_DSM_HAS_MUX; - nouveau_dsm_priv.dhandle = dhandle; - return true; + ret = nouveau_optimus_dsm(dhandle, 0, 0, &result); + if (ret == 0) + retval |= NOUVEAU_DSM_HAS_OPT; + + if (retval) + nouveau_dsm_priv.dhandle = dhandle; + + return retval; } static bool nouveau_dsm_detect(void) @@ -182,30 +244,42 @@ static bool nouveau_dsm_detect(void) struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; struct pci_dev *pdev = NULL; int has_dsm = 0; + int has_optimus; int vga_count = 0; bool guid_valid; + int retval; + bool ret = false; - /* lookup the GUID */ + /* lookup the MXM GUID */ guid_valid = mxm_wmi_supported(); - if (!guid_valid) - return false; - printk("MXM GUID detected in BIOS\n"); + if (guid_valid) + printk("MXM: GUID detected in BIOS\n"); + /* now do DSM detection */ while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { vga_count++; - has_dsm |= (nouveau_dsm_pci_probe(pdev) == true); + retval = nouveau_dsm_pci_probe(pdev); + printk("ret val is %d\n", retval); + if (retval & NOUVEAU_DSM_HAS_MUX) + has_dsm |= 1; + if (retval & NOUVEAU_DSM_HAS_OPT) + has_optimus = 1; } - if (vga_count == 2 && has_dsm) { + if (vga_count == 2 && has_dsm && guid_valid) { acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, &buffer); printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", acpi_method_name); nouveau_dsm_priv.dsm_detected = true; - return true; + ret = true; } - return false; + + if (has_optimus == 1) + nouveau_dsm_priv.optimus_detected = true; + + return ret; } void nouveau_register_dsm_handler(void) @@ -258,7 +332,7 @@ bool nouveau_acpi_rom_supported(struct pci_dev *pdev) acpi_status status; acpi_handle dhandle, rom_handle; - if (!nouveau_dsm_priv.dsm_detected) + if (!nouveau_dsm_priv.dsm_detected && !nouveau_dsm_priv.optimus_detected) return false; dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); |