diff options
| author | Dave Airlie <airlied@redhat.com> | 2011-03-22 14:10:27 +1000 | 
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2011-05-04 13:39:24 +1000 | 
| commit | f19467c509e36e5ba3498efd7d4072d3581a1d6c (patch) | |
| tree | c6a20a27a5b0438a7f5b1863ca0c2c80050c1ad8 | |
| parent | 3448a19da479b6bd1e28e2a2be9fa16c6a6feb39 (diff) | |
| download | olio-linux-3.10-f19467c509e36e5ba3498efd7d4072d3581a1d6c.tar.xz olio-linux-3.10-f19467c509e36e5ba3498efd7d4072d3581a1d6c.zip  | |
nouveau: add optimus detection to DSM code.
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 <airlied@redhat.com>
| -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 1aa33d96d5d..e0a885b72e0 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);  |