Re: new fixes for sony-acpi

Matthew Garrett
2009-03-31T15:20:57Z

[-- Attachment #1: Type: text/plain, Size: 710 bytes --]

I've done some more work on this and here's the current state of things.
It's a module that loads, checks what state the firmware thinks the
hardware is in and then ensures that the hardware is actually in that
state. I believe that it gets the choice between internal and discrete
GPUs correct. It doesn't support runtime switching right now since
there's no way to do anything useful with it, but it's a trivial sysfs
attribute.

I've done this outside sony-laptop since it's not specific to Sonys in
any way. It would be wonderful if anyone with any of the other hybrid
nvidias (except the Apples, which seem to be entirely different) could
give it a go.

--
Matthew Garrett | mjg59@srcf.ucam.org

[-- Attachment #2: nvidia-control.c --]
[-- Type: text/x-csrc, Size: 2852 bytes --]

#include <linux/acpi.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acpi_bus.h>
#include <linux/pci.h>

static int nvidia_dsm(struct pci_dev *dev, int func, int arg, int *result)
{
static char muid[] = {
0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4,
};

struct acpi_handle *handle;
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
struct acpi_object_list input;
union acpi_object params[4];
union acpi_object *obj;
int err;

handle = DEVICE_ACPI_HANDLE(&dev->dev);

if (!handle)
return -ENODEV;

input.count = 4;
input.pointer = params;
params[0].type = ACPI_TYPE_BUFFER;
params[0].buffer.length = sizeof(muid);
params[0].buffer.pointer = (char *)muid;
params[1].type = ACPI_TYPE_INTEGER;
params[1].integer.value = 0x00000102;
params[2].type = ACPI_TYPE_INTEGER;
params[2].integer.value = func;
params[3].type = ACPI_TYPE_INTEGER;
params[3].integer.value = arg;

err = acpi_evaluate_object(handle, "_DSM", &input, &output);
if (err) {
printk(KERN_ERR "nvidia-control: 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;
}

int nvidia_control_setup(struct pci_dev *dev)
{
int result;

if (nvidia_dsm(dev, 1, 0, &result))
return -ENODEV;

printk(KERN_INFO "nvidia-control: hardware status gave 0x%x\n", result);

if (result &= 0x1) { /* Stamina mode - disable the external GPU */
nvidia_dsm(dev, 0x2, 0x11, NULL);
nvidia_dsm(dev, 0x3, 0x02, NULL);
} else { /* Ensure that the external GPU is enabled */
nvidia_dsm(dev, 0x2, 0x12, NULL);
nvidia_dsm(dev, 0x3, 0x01, NULL);
}

return 0;
}

int nvidia_control_probe(struct pci_dev *dev)
{
int support = 0;

if (nvidia_dsm(dev, 0, 0, &support))
return -ENODEV;

if (!support)
return -ENODEV;

return 0;
}

static int __init nvidia_control_init(void)
{
struct pci_dev *dev = NULL;
while ((dev = pci_get_device(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, dev))) {
if (dev->class == 0x30000)
if (!nvidia_control_probe(dev))
nvidia_control_setup(dev);
}
return 0;
}

static void __exit nvidia_control_exit(void)
{
return;
}

static const struct pci_device_id pci_ids [] = {
{ PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
(PCI_CLASS_DISPLAY_VGA << 8), 0xffff00},
{ },
};

MODULE_DEVICE_TABLE(pci, pci_ids);

module_init(nvidia_control_init);
module_exit(nvidia_control_exit);

MODULE_LICENSE("GPL");

[-- Attachment #3: Makefile --]
[-- Type: text/plain, Size: 79 bytes --]

obj-m += nvidia-control.o

all:
make -C /lib/modules/`uname -r`/build M=`pwd`