]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - drivers/misc/sony-laptop.c
sony-laptop: Invoke _INI for SNC devices that provide it
[linux-2.6.git] / drivers / misc / sony-laptop.c
index c15c1f61bd1bfad853b8c6ea343d6cdd47e98135..5300cad9cd7f919998c34b7175375dff9aea5bea 100644 (file)
@@ -63,7 +63,7 @@
 #include <asm/uaccess.h>
 #include <linux/sonypi.h>
 #include <linux/sony-laptop.h>
-#ifdef CONFIG_SONY_LAPTOP_OLD
+#ifdef CONFIG_SONYPI_COMPAT
 #include <linux/poll.h>
 #include <linux/miscdevice.h>
 #endif
@@ -114,7 +114,7 @@ MODULE_PARM_DESC(camera,
                 "set this to 1 to enable Motion Eye camera controls "
                 "(only use it if you have a C1VE or C1VN model)");
 
-#ifdef CONFIG_SONY_LAPTOP_OLD
+#ifdef CONFIG_SONYPI_COMPAT
 static int minor = -1;
 module_param(minor, int, 0);
 MODULE_PARM_DESC(minor,
@@ -177,6 +177,8 @@ static struct {
        { SONYPI_EVENT_BACK_PRESSED,            KEY_BACK },
        { SONYPI_EVENT_HELP_PRESSED,            KEY_HELP },
        { SONYPI_EVENT_ZOOM_PRESSED,            KEY_ZOOM },
+       { SONYPI_EVENT_WIRELESS_ON,             KEY_WLAN },
+       { SONYPI_EVENT_WIRELESS_OFF,            KEY_WLAN },
        { SONYPI_EVENT_THUMBPHRASE_PRESSED,     BTN_THUMB },
        { 0, 0 },
 };
@@ -487,6 +489,14 @@ SNC_HANDLE_NAMES(audiopower_set, "AZPW");
 SNC_HANDLE_NAMES(lanpower_get, "GLNP");
 SNC_HANDLE_NAMES(lanpower_set, "LNPW");
 
+SNC_HANDLE_NAMES(lidstate_get, "GLID");
+
+SNC_HANDLE_NAMES(indicatorlamp_get, "GILS");
+SNC_HANDLE_NAMES(indicatorlamp_set, "SILS");
+
+SNC_HANDLE_NAMES(gainbass_get, "GMGB");
+SNC_HANDLE_NAMES(gainbass_set, "CMGB");
+
 SNC_HANDLE_NAMES(PID_get, "GPID");
 
 SNC_HANDLE_NAMES(CTR_get, "GCTR");
@@ -507,6 +517,12 @@ static struct sony_nc_value sony_nc_values[] = {
                        boolean_validate, 0),
        SNC_HANDLE(lanpower, snc_lanpower_get, snc_lanpower_set,
                        boolean_validate, 1),
+       SNC_HANDLE(lidstate, snc_lidstate_get, NULL,
+                       boolean_validate, 0),
+       SNC_HANDLE(indicatorlamp, snc_indicatorlamp_get, snc_indicatorlamp_set,
+                       boolean_validate, 0),
+       SNC_HANDLE(gainbass, snc_gainbass_get, snc_gainbass_set,
+                       boolean_validate, 0),
        /* unknown methods */
        SNC_HANDLE(PID, snc_PID_get, NULL, NULL, 1),
        SNC_HANDLE(CTR, snc_CTR_get, snc_CTR_set, NULL, 1),
@@ -688,14 +704,108 @@ static struct backlight_ops sony_backlight_ops = {
        .get_brightness = sony_backlight_get_brightness,
 };
 
+/*
+ * New SNC-only Vaios event mapping to driver known keys
+ */
+struct sony_nc_event {
+       u8      data;
+       u8      event;
+};
+
+static struct sony_nc_event *sony_nc_events;
+
+/* Vaio C* --maybe also FE*, N* and AR* ?-- special init sequence
+ * for Fn keys
+ */
+static int sony_nc_C_enable(struct dmi_system_id *id)
+{
+       int result = 0;
+
+       printk(KERN_NOTICE DRV_PFX "detected %s\n", id->ident);
+
+       sony_nc_events = id->driver_data;
+
+       if (acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0x4, &result) < 0
+                       || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x2, &result) < 0
+                       || acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0x10, &result) < 0
+                       || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x0, &result) < 0
+                       || acpi_callsetfunc(sony_nc_acpi_handle, "SN03", 0x2, &result) < 0
+                       || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x101, &result) < 0) {
+               printk(KERN_WARNING DRV_PFX "failed to initialize SNC, some "
+                               "functionalities may be missing\n");
+               return 1;
+       }
+       return 0;
+}
+
+static struct sony_nc_event sony_C_events[] = {
+       { 0x81, SONYPI_EVENT_FNKEY_F1 },
+       { 0x01, SONYPI_EVENT_FNKEY_RELEASED },
+       { 0x85, SONYPI_EVENT_FNKEY_F5 },
+       { 0x05, SONYPI_EVENT_FNKEY_RELEASED },
+       { 0x86, SONYPI_EVENT_FNKEY_F6 },
+       { 0x06, SONYPI_EVENT_FNKEY_RELEASED },
+       { 0x87, SONYPI_EVENT_FNKEY_F7 },
+       { 0x07, SONYPI_EVENT_FNKEY_RELEASED },
+       { 0x8A, SONYPI_EVENT_FNKEY_F10 },
+       { 0x0A, SONYPI_EVENT_FNKEY_RELEASED },
+       { 0x8C, SONYPI_EVENT_FNKEY_F12 },
+       { 0x0C, SONYPI_EVENT_FNKEY_RELEASED },
+       { 0, 0 },
+};
+
+/* SNC-only model map */
+struct dmi_system_id sony_nc_ids[] = {
+               {
+                       .ident = "Sony Vaio C Series",
+                       .callback = sony_nc_C_enable,
+                       .driver_data = sony_C_events,
+                       .matches = {
+                               DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+                               DMI_MATCH(DMI_PRODUCT_NAME, "VGN-C"),
+                       },
+               },
+               { }
+};
+
 /*
  * ACPI callbacks
  */
 static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
 {
-       dprintk("sony_acpi_notify, event: %d\n", event);
-       sony_laptop_report_input_event(event);
-       acpi_bus_generate_event(sony_nc_acpi_device, 1, event);
+       struct sony_nc_event *evmap;
+       u32 ev = event;
+       int result;
+
+       if (ev == 0x92) {
+               /* read the key pressed from EC.GECR
+                * A call to SN07 with 0x0202 will do it as well respecting
+                * the current protocol on different OSes
+                *
+                * Note: the path for GECR may be
+                *   \_SB.PCI0.LPCB.EC (C, FE, AR, N and friends)
+                *   \_SB.PCI0.PIB.EC0 (VGN-FR notifications are sent directly, no GECR)
+                *
+                * TODO: we may want to do the same for the older GHKE -need
+                *       dmi list- so this snippet may become one more callback.
+                */
+               if (acpi_callsetfunc(handle, "SN07", 0x0202, &result) < 0)
+                       dprintk("sony_acpi_notify, unable to decode event 0x%.2x\n", ev);
+               else
+                       ev = result & 0xFF;
+       }
+
+       if (sony_nc_events)
+               for (evmap = sony_nc_events; evmap->event; evmap++) {
+                       if (evmap->data == ev) {
+                               ev = evmap->event;
+                               break;
+                       }
+               }
+
+       dprintk("sony_acpi_notify, event: 0x%.2x\n", ev);
+       sony_laptop_report_input_event(ev);
+       acpi_bus_generate_event(sony_nc_acpi_device, 1, ev);
 }
 
 static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
@@ -732,6 +842,10 @@ static int sony_nc_resume(struct acpi_device *device)
                        break;
                }
        }
+
+       /* re-initialize models with specific requirements */
+       dmi_check_system(sony_nc_ids);
+
        return 0;
 }
 
@@ -750,6 +864,15 @@ static int sony_nc_add(struct acpi_device *device)
 
        sony_nc_acpi_handle = device->handle;
 
+       /* read device status */
+       result = acpi_bus_get_status(device);
+       /* bail IFF the above call was successful and the device is not present */
+       if (!result && !device->status.present) {
+               dprintk("Device not present\n");
+               result = -ENODEV;
+               goto outwalk;
+       }
+
        if (debug) {
                status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle,
                                             1, sony_walk_callback, NULL, NULL);
@@ -760,6 +883,15 @@ static int sony_nc_add(struct acpi_device *device)
                }
        }
 
+       /* try to _INI the device if such method exists (ACPI spec 3.0-6.5.1
+        * should be respected as we already checked for the device presence above */
+       if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, METHOD_NAME__INI, &handle))) {
+               dprintk("Invoking _INI\n");
+               if (ACPI_FAILURE(acpi_evaluate_object(sony_nc_acpi_handle, METHOD_NAME__INI,
+                                               NULL, NULL)))
+                       dprintk("_INI Method failed\n");
+       }
+
        /* setup input devices and helper fifo */
        result = sony_laptop_setup_input();
        if (result) {
@@ -772,7 +904,7 @@ static int sony_nc_add(struct acpi_device *device)
                                             ACPI_DEVICE_NOTIFY,
                                             sony_acpi_notify, NULL);
        if (ACPI_FAILURE(status)) {
-               printk(KERN_WARNING DRV_PFX "unable to install notify handler\n");
+               printk(KERN_WARNING DRV_PFX "unable to install notify handler (%u)\n", status);
                result = -ENODEV;
                goto outinput;
        }
@@ -795,6 +927,9 @@ static int sony_nc_add(struct acpi_device *device)
 
        }
 
+       /* initialize models with specific requirements */
+       dmi_check_system(sony_nc_ids);
+
        result = sony_pf_add();
        if (result)
                goto outbacklight;
@@ -1504,7 +1639,7 @@ static struct attribute_group spic_attribute_group = {
 };
 
 /******** SONYPI compatibility **********/
-#ifdef CONFIG_SONY_LAPTOP_OLD
+#ifdef CONFIG_SONYPI_COMPAT
 
 /* battery / brightness / temperature  addresses */
 #define SONYPI_BAT_FLAGS       0x81
@@ -1798,7 +1933,7 @@ static void sonypi_compat_exit(void)
 static int sonypi_compat_init(void) { return 0; }
 static void sonypi_compat_exit(void) { }
 static void sonypi_compat_report_event(u8 event) { }
-#endif /* CONFIG_SONY_LAPTOP_OLD */
+#endif /* CONFIG_SONYPI_COMPAT */
 
 /*
  * ACPI callbacks
@@ -1917,7 +2052,8 @@ end:
  */
 static int sony_pic_disable(struct acpi_device *device)
 {
-       if (ACPI_FAILURE(acpi_evaluate_object(device->handle, "_DIS", 0, NULL)))
+       if (ACPI_FAILURE(acpi_evaluate_object(device->handle,
+                       "_DIS", NULL, NULL)))
                return -ENXIO;
 
        dprintk("Device disabled\n");