Skip to content

Commit

Permalink
nouveau: add optimus detection to DSM code.
Browse files Browse the repository at this point in the history
optimus has another DSM GUID, so we check for its existance,
also allow the BIOS stuff is we find it.

Signed-off-by: Dave Airlie <[email protected]>
  • Loading branch information
airlied committed May 4, 2011
1 parent 3448a19 commit f19467c
Showing 1 changed file with 89 additions and 15 deletions.
104 changes: 89 additions & 15 deletions drivers/gpu/drm/nouveau/nouveau_acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down Expand Up @@ -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);
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit f19467c

Please sign in to comment.