Hi all, On Fr, 20 Mär 2009, Norbert Preining wrote: > > As far as we can tell, the handling of the handover is consistent across > > all dual-GPU nvidia laptops[1]. The right place for support to end up is > > in the kernel component of the nouveau drivers. > > Whatever the nouveau drivers are? You mean those based on the kernel > mode swching? Unfortunately that is far from prime time, and we need > power saving now. > > I will take a look over the weekend maybe I can add a patch ontop of > yours that only does that, taken from the code in the special vaio-Z > sony-laptop module. > Now it would be nice to merge over *only* the stuff concerning > speed/stamina into the in-kernel driver added with the patch for rfkill > support. Ok, here is the code. For those interested I Cc the sony-vaio-z-series@xxxxxxxxxxxxxxxxxxx group where I first found that modules. You need: kernel 2.6.29-rc8 (maybe it works with all from .28 on) the patch Matthew sent the attached patch Together you get full rfkill support for bluetooth/wwan/wifi, plus afais stamina-speed mode setting. Maybe Matthias the creator of the sony-laptop for vaio-zseries modules can take a look at the patch and see if I missed something. Matthias: sony_led_off and sony_dgpu_sta is never used, is that intentional? And also the #define SONY_WMMX_GUID I couldn't find being used anywhere. Maybe Matthew can take a look and fix the compile warnings in function ‘sony_ovga_dsm’ (there are some!). My intention is that we do not digress too far from the kernel code of sony-laptop making eventual merging possible. Best wishes Norbert ------------------------------------------------------------------------------- Dr. Norbert Preining <preining@xxxxxxxx> Vienna University of Technology Debian Developer <preining@xxxxxxxxxx> Debian TeX Group gpg DSA: 0x09C5B094 fp: 14DF 2E6C 0307 BE6D AD76 A9C0 D2BF 4AA3 09C5 B094 ------------------------------------------------------------------------------- OSBASTON (n.) A point made for the seventh time to somebody who insists that they know exactly what you mean but clearly hasn't got the faintest idea. --- Douglas Adams, The Meaning of Liff
--- /usr/src/linux-2.6.29-rc8/drivers/platform/x86/sony-laptop.c 2009-03-19 22:52:44.000000000 +0100 +++ sony-laptop.c 2009-03-20 01:58:42.000000000 +0100 @@ -116,6 +116,11 @@ "set this to 1 to enable Motion Eye camera controls " "(only use it if you have a C1VE or C1VN model)"); +static int speed_stamina; +module_param(speed_stamina, int, 0444); +MODULE_PARM_DESC(speed_stamina, + "Set this to 1 to enable SPEED mode on module load (EXPERIMENTAL)"); + #ifdef CONFIG_SONYPI_COMPAT static int minor = -1; module_param(minor, int, 0); @@ -482,8 +487,158 @@ /*********** Platform Device ***********/ +static int sony_ovga_dsm(int func, int arg) +{ + static const char *path = "\\_SB.PCI0.OVGA._DSM"; + static const char muid[] = { + /*00*/ 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, /* MUID */ + /*08*/ 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, + }; + + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_object_list input; + union acpi_object params[4]; + union acpi_object *obj; + int result; + + input.count = 4; + input.pointer = params; + params[0].type = ACPI_TYPE_BUFFER; + params[0].buffer.length = sizeof(muid); + params[0].buffer.pointer = 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; + + result = acpi_evaluate_object(NULL, path, &input, &output); + if (result) { + printk("%s failed: %d\n", path, result); + return -1; + } + + obj = (union acpi_object*)output.pointer; + printk("result type %d\n", obj->type); + if (obj->type == ACPI_TYPE_PACKAGE) { + int i; + printk("returned package sized %d\n", obj->package.count); + for (i = 0; i < obj->package.count; i++) + printk("%d %08x\n", i, obj->package.elements[i].integer.value); + } else + if (obj->type == ACPI_TYPE_INTEGER) { + printk("returned integer %08X\n", obj->integer.value); + } else + if (obj->type == ACPI_TYPE_BUFFER) { + int i; + printk("returned buffer sized %d\n", obj->buffer.length); + for (i = 0; i < obj->buffer.length; i++) + printk("%d %02x\n", i, obj->buffer.pointer[i]); + } + kfree(output.pointer); + + return 0; +} + +static int sony_led_stamina(void) +{ + return sony_ovga_dsm(2, 0x11); +} + +static int sony_led_speed(void) +{ + return sony_ovga_dsm(2, 0x12); +} + +static int sony_led_off(void) +{ + return sony_ovga_dsm(2, 0x13); +} + +static int sony_dgpu_sta(void) +{ + return sony_ovga_dsm(3, 0x00); +} + +static int sony_dgpu_off(void) +{ + return sony_ovga_dsm(3, 0x02); +} + +static int sony_dgpu_on(void) +{ + return sony_ovga_dsm(3, 0x01); +} + +static ssize_t sony_pf_store_speed_stamina(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + if (!strncmp(buffer, "speed", strlen("speed"))) { + sony_dgpu_on(); + sony_led_speed(); + speed_stamina = 1; + } else + if (!strncmp(buffer, "stamina", strlen("stamina"))) { + sony_dgpu_off(); + sony_led_stamina(); + speed_stamina = 0; + } else + return -EINVAL; + + return count; +} + +static ssize_t sony_pf_show_speed_stamina(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + return snprintf(buffer, PAGE_SIZE, "%s\n", speed_stamina ? "speed":"stamina"); +} + +static struct device_attribute sony_pf_speed_stamina_attr = + __ATTR(speed_stamina, S_IWUSR|S_IRUGO, + sony_pf_show_speed_stamina, sony_pf_store_speed_stamina); + +static int sony_pf_probe(struct platform_device *pdev) +{ + int result; + + result = device_create_file(&pdev->dev, &sony_pf_speed_stamina_attr); + if (result) + printk(KERN_DEBUG "sony_pf_probe: failed to add speed/stamina switch\n"); + + /* initialize default, look at module param speed_stamina */ + if (speed_stamina == 1) { + sony_dgpu_on(); + sony_led_speed(); + } else { + sony_dgpu_off(); + sony_led_stamina(); + } + + return 0; +} + +static int sony_pf_resume(struct platform_device *pdev) +{ + /* on resume, restore previous state */ + if (speed_stamina == 1) { + sony_dgpu_on(); + sony_led_speed(); + } else { + sony_dgpu_off(); + sony_led_stamina(); + } + return 0; +} + static atomic_t sony_pf_users = ATOMIC_INIT(0); static struct platform_driver sony_pf_driver = { + .probe = sony_pf_probe, +#ifdef CONFIG_PM + .resume_early = sony_pf_resume, +#endif .driver = { .name = "sony-laptop", .owner = THIS_MODULE, @@ -1169,6 +1324,12 @@ dprintk("_INI Method failed\n"); } +#if 0 + /* try to _INI the ECON variable */ + if (acpi_callsetfunc(sony_nc_acpi_handle, "ECON", 1, &result)) + dprintk("ECON Method failed\n"); +#endif + if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN07", &handle))) { dprintk("Doing SNC setup\n");