]> git.karo-electronics.de Git - mv-sheeva.git/commitdiff
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platf...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 10 Jan 2011 23:39:48 +0000 (15:39 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 10 Jan 2011 23:39:48 +0000 (15:39 -0800)
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86: (36 commits)
  sony-laptop: support new hotkeys on the P, Z and EC series
  platform/x86: Consistently select LEDS Kconfig options
  sony-laptop: fix sparse non-ANSI function warning
  intel_ips: fix sparse non-ANSI function warning
  Support KHLB2 in the compal laptop driver
  acer-wmi: Enabled Acer Launch Manager mode
  [PATCH] intel_pmic_gpio: modify EOI handling following change of kernel irq subsystem
  ACPI Thinkpad: We must always call va_end() after va_start() but do not do so in thinkpad_acpi.c::acpi_evalf()
  acer-wmi: Initialize wlan/bluetooth/wwan rfkill software block state
  acer-wmi: Detect the WiFi/Bluetooth/3G devices available
  acer-wmi: Add 3G rfkill sysfs file
  acer-wmi: Add acer wmi hotkey events support
  platform/x86: Kconfig: Replace select by depends on ACPI_WMI
  ideapad: pass ideapad_priv as argument (part 2)
  ideapad: pass ideapad_priv as argument (part 1)
  ideapad: add markups, unify comments and return result when init
  ideapad: add hotkey support
  ideapad: let camera power control entry under platform driver
  ideapad: add platform driver for ideapad
  fujitsu-laptop: fix compiler warning on pnp_ids
  ...

19 files changed:
Documentation/ABI/testing/sysfs-platform-ideapad-laptop [new file with mode: 0644]
MAINTAINERS
drivers/platform/x86/Kconfig
drivers/platform/x86/Makefile
drivers/platform/x86/acer-wmi.c
drivers/platform/x86/classmate-laptop.c
drivers/platform/x86/compal-laptop.c
drivers/platform/x86/eeepc-laptop.c
drivers/platform/x86/eeepc-wmi.c
drivers/platform/x86/fujitsu-laptop.c
drivers/platform/x86/ideapad-laptop.c
drivers/platform/x86/intel_ips.c
drivers/platform/x86/intel_pmic_gpio.c
drivers/platform/x86/intel_scu_ipc.c
drivers/platform/x86/intel_scu_ipcutil.c [new file with mode: 0644]
drivers/platform/x86/sony-laptop.c
drivers/platform/x86/thinkpad_acpi.c
drivers/platform/x86/wmi.c
include/linux/sonypi.h

diff --git a/Documentation/ABI/testing/sysfs-platform-ideapad-laptop b/Documentation/ABI/testing/sysfs-platform-ideapad-laptop
new file mode 100644 (file)
index 0000000..807fca2
--- /dev/null
@@ -0,0 +1,6 @@
+What:          /sys/devices/platform/ideapad/camera_power
+Date:          Dec 2010
+KernelVersion: 2.6.37
+Contact:       "Ike Panhc <ike.pan@canonical.com>"
+Description:
+               Control the power of camera module. 1 means on, 0 means off.
index 9e4d4ca690e2633bd7b1f6d507b28f26c288a6f2..5181828012ce93e98424898083475d9b801712e6 100644 (file)
@@ -2271,6 +2271,14 @@ W:       http://acpi4asus.sf.net
 S:     Maintained
 F:     drivers/platform/x86/eeepc-laptop.c
 
+EEEPC WMI EXTRAS DRIVER
+M:     Corentin Chary <corentincj@iksaif.net>
+L:     acpi4asus-user@lists.sourceforge.net
+L:     platform-driver-x86@vger.kernel.org
+W:     http://acpi4asus.sf.net
+S:     Maintained
+F:     drivers/platform/x86/eeepc-wmi.c
+
 EFIFB FRAMEBUFFER DRIVER
 L:     linux-fbdev@vger.kernel.org
 M:     Peter Jones <pjones@redhat.com>
index faec777b1ed433de38514c479fc17dc2ebf96c00..d163bc2e2b9e43b76e5bf9a93e7ed374a4e794a8 100644 (file)
@@ -18,12 +18,14 @@ if X86_PLATFORM_DEVICES
 config ACER_WMI
        tristate "Acer WMI Laptop Extras"
        depends on ACPI
-       depends on LEDS_CLASS
-       depends on NEW_LEDS
+       select LEDS_CLASS
+       select NEW_LEDS
        depends on BACKLIGHT_CLASS_DEVICE
        depends on SERIO_I8042
+       depends on INPUT
        depends on RFKILL || RFKILL = n
-       select ACPI_WMI
+       depends on ACPI_WMI
+       select INPUT_SPARSEKMAP
        ---help---
          This is a driver for newer Acer (and Wistron) laptops. It adds
          wireless radio and bluetooth control, and on some laptops,
@@ -131,7 +133,7 @@ config TC1100_WMI
        depends on !X86_64
        depends on EXPERIMENTAL
        depends on ACPI
-       select ACPI_WMI
+       depends on ACPI_WMI
        ---help---
          This is a driver for the WMI extensions (wireless and bluetooth power
          control) of the HP Compaq TC1100 tablet.
@@ -226,6 +228,7 @@ config IDEAPAD_LAPTOP
        tristate "Lenovo IdeaPad Laptop Extras"
        depends on ACPI
        depends on RFKILL
+       select INPUT_SPARSEKMAP
        help
          This is a driver for the rfkill switches on Lenovo IdeaPad netbooks.
 
@@ -425,7 +428,10 @@ config EEEPC_WMI
        depends on INPUT
        depends on EXPERIMENTAL
        depends on BACKLIGHT_CLASS_DEVICE
+       depends on RFKILL || RFKILL = n
        select INPUT_SPARSEKMAP
+       select LEDS_CLASS
+       select NEW_LEDS
        ---help---
          Say Y here if you want to support WMI-based hotkeys on Eee PC laptops.
 
@@ -510,8 +516,8 @@ config TOPSTAR_LAPTOP
 config ACPI_TOSHIBA
        tristate "Toshiba Laptop Extras"
        depends on ACPI
-       depends on LEDS_CLASS
-       depends on NEW_LEDS
+       select LEDS_CLASS
+       select NEW_LEDS
        depends on BACKLIGHT_CLASS_DEVICE
        depends on INPUT
        depends on RFKILL || RFKILL = n
@@ -576,6 +582,15 @@ config INTEL_SCU_IPC
          some embedded Intel x86 platforms. This is not needed for PC-type
          machines.
 
+config INTEL_SCU_IPC_UTIL
+       tristate "Intel SCU IPC utility driver"
+       depends on INTEL_SCU_IPC
+       default y
+       ---help---
+         The IPC Util driver provides an interface with the SCU enabling
+         low level access for debug work and updating the firmware. Say
+         N unless you will be doing this on an Intel MID platform.
+
 config GPIO_INTEL_PMIC
        bool "Intel PMIC GPIO support"
        depends on INTEL_SCU_IPC && GPIOLIB
index 9950ccc940b5b926ad86a50c07a9651432dbea4c..4ec4ff8f918240e657153d7bdd0a11202ba8d498 100644 (file)
@@ -28,6 +28,7 @@ obj-$(CONFIG_TOPSTAR_LAPTOP)  += topstar-laptop.o
 obj-$(CONFIG_ACPI_TOSHIBA)     += toshiba_acpi.o
 obj-$(CONFIG_TOSHIBA_BT_RFKILL)        += toshiba_bluetooth.o
 obj-$(CONFIG_INTEL_SCU_IPC)    += intel_scu_ipc.o
+obj-$(CONFIG_INTEL_SCU_IPC_UTIL)+= intel_scu_ipcutil.o
 obj-$(CONFIG_RAR_REGISTER)     += intel_rar_register.o
 obj-$(CONFIG_INTEL_IPS)                += intel_ips.o
 obj-$(CONFIG_GPIO_INTEL_PMIC)  += intel_pmic_gpio.o
index c8c65375bfe2cf61e83276c97ad3eed86453a0a0..ee40d681edd0044bf278f15840825f437209914a 100644 (file)
@@ -37,6 +37,9 @@
 #include <linux/workqueue.h>
 #include <linux/debugfs.h>
 #include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
+#include <linux/dmi.h>
 
 #include <acpi/acpi_drivers.h>
 
@@ -48,6 +51,7 @@ MODULE_LICENSE("GPL");
 #define ACER_ERR KERN_ERR ACER_LOGPREFIX
 #define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX
 #define ACER_INFO KERN_INFO ACER_LOGPREFIX
+#define ACER_WARNING KERN_WARNING ACER_LOGPREFIX
 
 /*
  * Magic Number
@@ -82,9 +86,82 @@ MODULE_LICENSE("GPL");
 #define AMW0_GUID2             "431F16ED-0C2B-444C-B267-27DEB140CF9C"
 #define WMID_GUID1             "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"
 #define WMID_GUID2             "95764E09-FB56-4e83-B31A-37761F60994A"
+#define WMID_GUID3             "61EF69EA-865C-4BC3-A502-A0DEBA0CB531"
+
+/*
+ * Acer ACPI event GUIDs
+ */
+#define ACERWMID_EVENT_GUID "676AA15E-6A47-4D9F-A2CC-1E6D18D14026"
 
 MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB");
 MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3");
+MODULE_ALIAS("wmi:676AA15E-6A47-4D9F-A2CC-1E6D18D14026");
+
+enum acer_wmi_event_ids {
+       WMID_HOTKEY_EVENT = 0x1,
+};
+
+static const struct key_entry acer_wmi_keymap[] = {
+       {KE_KEY, 0x01, {KEY_WLAN} },     /* WiFi */
+       {KE_KEY, 0x12, {KEY_BLUETOOTH} },       /* BT */
+       {KE_KEY, 0x21, {KEY_PROG1} },    /* Backup */
+       {KE_KEY, 0x22, {KEY_PROG2} },    /* Arcade */
+       {KE_KEY, 0x23, {KEY_PROG3} },    /* P_Key */
+       {KE_KEY, 0x24, {KEY_PROG4} },    /* Social networking_Key */
+       {KE_KEY, 0x64, {KEY_SWITCHVIDEOMODE} }, /* Display Switch */
+       {KE_KEY, 0x82, {KEY_F22} },      /* Touch Pad On/Off */
+       {KE_END, 0}
+};
+
+static struct input_dev *acer_wmi_input_dev;
+
+struct event_return_value {
+       u8 function;
+       u8 key_num;
+       u16 device_state;
+       u32 reserved;
+} __attribute__((packed));
+
+/*
+ * GUID3 Get Device Status device flags
+ */
+#define ACER_WMID3_GDS_WIRELESS                (1<<0)  /* WiFi */
+#define ACER_WMID3_GDS_THREEG          (1<<6)  /* 3G */
+#define ACER_WMID3_GDS_BLUETOOTH       (1<<11) /* BT */
+
+struct lm_input_params {
+       u8 function_num;        /* Function Number */
+       u16 commun_devices;     /* Communication type devices default status */
+       u16 devices;            /* Other type devices default status */
+       u8 lm_status;           /* Launch Manager Status */
+       u16 reserved;
+} __attribute__((packed));
+
+struct lm_return_value {
+       u8 error_code;          /* Error Code */
+       u8 ec_return_value;     /* EC Return Value */
+       u16 reserved;
+} __attribute__((packed));
+
+struct wmid3_gds_input_param { /* Get Device Status input parameter */
+       u8 function_num;        /* Function Number */
+       u8 hotkey_number;       /* Hotkey Number */
+       u16 devices;            /* Get Device */
+} __attribute__((packed));
+
+struct wmid3_gds_return_value {        /* Get Device Status return value*/
+       u8 error_code;          /* Error Code */
+       u8 ec_return_value;     /* EC Return Value */
+       u16 devices;            /* Current Device Status */
+       u32 reserved;
+} __attribute__((packed));
+
+struct hotkey_function_type_aa {
+       u8 type;
+       u8 length;
+       u16 handle;
+       u16 commun_func_bitmap;
+} __attribute__((packed));
 
 /*
  * Interface capability flags
@@ -116,15 +193,19 @@ static int mailled = -1;
 static int brightness = -1;
 static int threeg = -1;
 static int force_series;
+static bool ec_raw_mode;
+static bool has_type_aa;
 
 module_param(mailled, int, 0444);
 module_param(brightness, int, 0444);
 module_param(threeg, int, 0444);
 module_param(force_series, int, 0444);
+module_param(ec_raw_mode, bool, 0444);
 MODULE_PARM_DESC(mailled, "Set initial state of Mail LED");
 MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness");
 MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware");
 MODULE_PARM_DESC(force_series, "Force a different laptop series");
+MODULE_PARM_DESC(ec_raw_mode, "Enable EC raw mode");
 
 struct acer_data {
        int mailled;
@@ -140,6 +221,7 @@ struct acer_debug {
 
 static struct rfkill *wireless_rfkill;
 static struct rfkill *bluetooth_rfkill;
+static struct rfkill *threeg_rfkill;
 
 /* Each low-level interface must define at least some of the following */
 struct wmi_interface {
@@ -753,6 +835,28 @@ static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
        return WMI_execute_u32(method_id, (u32)value, NULL);
 }
 
+static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy)
+{
+       struct hotkey_function_type_aa *type_aa;
+
+       /* We are looking for OEM-specific Type AAh */
+       if (header->type != 0xAA)
+               return;
+
+       has_type_aa = true;
+       type_aa = (struct hotkey_function_type_aa *) header;
+
+       printk(ACER_INFO "Function bitmap for Communication Button: 0x%x\n",
+               type_aa->commun_func_bitmap);
+
+       if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_WIRELESS)
+               interface->capability |= ACER_CAP_WIRELESS;
+       if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_THREEG)
+               interface->capability |= ACER_CAP_THREEG;
+       if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_BLUETOOTH)
+               interface->capability |= ACER_CAP_BLUETOOTH;
+}
+
 static acpi_status WMID_set_capabilities(void)
 {
        struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
@@ -773,16 +877,17 @@ static acpi_status WMID_set_capabilities(void)
                return AE_ERROR;
        }
 
-       /* Not sure on the meaning of the relevant bits yet to detect these */
-       interface->capability |= ACER_CAP_WIRELESS;
-       interface->capability |= ACER_CAP_THREEG;
+       dmi_walk(type_aa_dmi_decode, NULL);
+       if (!has_type_aa) {
+               interface->capability |= ACER_CAP_WIRELESS;
+               interface->capability |= ACER_CAP_THREEG;
+               if (devices & 0x10)
+                       interface->capability |= ACER_CAP_BLUETOOTH;
+       }
 
        /* WMID always provides brightness methods */
        interface->capability |= ACER_CAP_BRIGHTNESS;
 
-       if (devices & 0x10)
-               interface->capability |= ACER_CAP_BLUETOOTH;
-
        if (!(devices & 0x20))
                max_brightness = 0x9;
 
@@ -861,7 +966,8 @@ static void __init acer_commandline_init(void)
         * capability isn't available on the given interface
         */
        set_u32(mailled, ACER_CAP_MAILLED);
-       set_u32(threeg, ACER_CAP_THREEG);
+       if (!has_type_aa)
+               set_u32(threeg, ACER_CAP_THREEG);
        set_u32(brightness, ACER_CAP_BRIGHTNESS);
 }
 
@@ -948,6 +1054,79 @@ static void acer_backlight_exit(void)
        backlight_device_unregister(acer_backlight_device);
 }
 
+static acpi_status wmid3_get_device_status(u32 *value, u16 device)
+{
+       struct wmid3_gds_return_value return_value;
+       acpi_status status;
+       union acpi_object *obj;
+       struct wmid3_gds_input_param params = {
+               .function_num = 0x1,
+               .hotkey_number = 0x01,
+               .devices = device,
+       };
+       struct acpi_buffer input = {
+               sizeof(struct wmid3_gds_input_param),
+               &params
+       };
+       struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+
+       status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output);
+       if (ACPI_FAILURE(status))
+               return status;
+
+       obj = output.pointer;
+
+       if (!obj)
+               return AE_ERROR;
+       else if (obj->type != ACPI_TYPE_BUFFER) {
+               kfree(obj);
+               return AE_ERROR;
+       }
+       if (obj->buffer.length != 8) {
+               printk(ACER_WARNING "Unknown buffer length %d\n",
+                       obj->buffer.length);
+               kfree(obj);
+               return AE_ERROR;
+       }
+
+       return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
+       kfree(obj);
+
+       if (return_value.error_code || return_value.ec_return_value)
+               printk(ACER_WARNING "Get Device Status failed: "
+                       "0x%x - 0x%x\n", return_value.error_code,
+                       return_value.ec_return_value);
+       else
+               *value = !!(return_value.devices & device);
+
+       return status;
+}
+
+static acpi_status get_device_status(u32 *value, u32 cap)
+{
+       if (wmi_has_guid(WMID_GUID3)) {
+               u16 device;
+
+               switch (cap) {
+               case ACER_CAP_WIRELESS:
+                       device = ACER_WMID3_GDS_WIRELESS;
+                       break;
+               case ACER_CAP_BLUETOOTH:
+                       device = ACER_WMID3_GDS_BLUETOOTH;
+                       break;
+               case ACER_CAP_THREEG:
+                       device = ACER_WMID3_GDS_THREEG;
+                       break;
+               default:
+                       return AE_ERROR;
+               }
+               return wmid3_get_device_status(value, device);
+
+       } else {
+               return get_u32(value, cap);
+       }
+}
+
 /*
  * Rfkill devices
  */
@@ -968,6 +1147,13 @@ static void acer_rfkill_update(struct work_struct *ignored)
                        rfkill_set_sw_state(bluetooth_rfkill, !state);
        }
 
+       if (has_cap(ACER_CAP_THREEG) && wmi_has_guid(WMID_GUID3)) {
+               status = wmid3_get_device_status(&state,
+                               ACER_WMID3_GDS_THREEG);
+               if (ACPI_SUCCESS(status))
+                       rfkill_set_sw_state(threeg_rfkill, !state);
+       }
+
        schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
 }
 
@@ -991,6 +1177,8 @@ static struct rfkill *acer_rfkill_register(struct device *dev,
 {
        int err;
        struct rfkill *rfkill_dev;
+       u32 state;
+       acpi_status status;
 
        rfkill_dev = rfkill_alloc(name, dev, type,
                                  &acer_rfkill_ops,
@@ -998,6 +1186,10 @@ static struct rfkill *acer_rfkill_register(struct device *dev,
        if (!rfkill_dev)
                return ERR_PTR(-ENOMEM);
 
+       status = get_device_status(&state, cap);
+       if (ACPI_SUCCESS(status))
+               rfkill_init_sw_state(rfkill_dev, !state);
+
        err = rfkill_register(rfkill_dev);
        if (err) {
                rfkill_destroy(rfkill_dev);
@@ -1024,6 +1216,19 @@ static int acer_rfkill_init(struct device *dev)
                }
        }
 
+       if (has_cap(ACER_CAP_THREEG)) {
+               threeg_rfkill = acer_rfkill_register(dev,
+                       RFKILL_TYPE_WWAN, "acer-threeg",
+                       ACER_CAP_THREEG);
+               if (IS_ERR(threeg_rfkill)) {
+                       rfkill_unregister(wireless_rfkill);
+                       rfkill_destroy(wireless_rfkill);
+                       rfkill_unregister(bluetooth_rfkill);
+                       rfkill_destroy(bluetooth_rfkill);
+                       return PTR_ERR(threeg_rfkill);
+               }
+       }
+
        schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
 
        return 0;
@@ -1040,6 +1245,11 @@ static void acer_rfkill_exit(void)
                rfkill_unregister(bluetooth_rfkill);
                rfkill_destroy(bluetooth_rfkill);
        }
+
+       if (has_cap(ACER_CAP_THREEG)) {
+               rfkill_unregister(threeg_rfkill);
+               rfkill_destroy(threeg_rfkill);
+       }
        return;
 }
 
@@ -1050,7 +1260,12 @@ static ssize_t show_bool_threeg(struct device *dev,
        struct device_attribute *attr, char *buf)
 {
        u32 result; \
-       acpi_status status = get_u32(&result, ACER_CAP_THREEG);
+       acpi_status status;
+       if (wmi_has_guid(WMID_GUID3))
+               status = wmid3_get_device_status(&result,
+                               ACER_WMID3_GDS_THREEG);
+       else
+               status = get_u32(&result, ACER_CAP_THREEG);
        if (ACPI_SUCCESS(status))
                return sprintf(buf, "%u\n", result);
        return sprintf(buf, "Read error\n");
@@ -1085,6 +1300,178 @@ static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
 
 static DEVICE_ATTR(interface, S_IRUGO, show_interface, NULL);
 
+static void acer_wmi_notify(u32 value, void *context)
+{
+       struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object *obj;
+       struct event_return_value return_value;
+       acpi_status status;
+
+       status = wmi_get_event_data(value, &response);
+       if (status != AE_OK) {
+               printk(ACER_WARNING "bad event status 0x%x\n", status);
+               return;
+       }
+
+       obj = (union acpi_object *)response.pointer;
+
+       if (!obj)
+               return;
+       if (obj->type != ACPI_TYPE_BUFFER) {
+               printk(ACER_WARNING "Unknown response received %d\n",
+                       obj->type);
+               kfree(obj);
+               return;
+       }
+       if (obj->buffer.length != 8) {
+               printk(ACER_WARNING "Unknown buffer length %d\n",
+                       obj->buffer.length);
+               kfree(obj);
+               return;
+       }
+
+       return_value = *((struct event_return_value *)obj->buffer.pointer);
+       kfree(obj);
+
+       switch (return_value.function) {
+       case WMID_HOTKEY_EVENT:
+               if (!sparse_keymap_report_event(acer_wmi_input_dev,
+                               return_value.key_num, 1, true))
+                       printk(ACER_WARNING "Unknown key number - 0x%x\n",
+                               return_value.key_num);
+               break;
+       default:
+               printk(ACER_WARNING "Unknown function number - %d - %d\n",
+                       return_value.function, return_value.key_num);
+               break;
+       }
+}
+
+static acpi_status
+wmid3_set_lm_mode(struct lm_input_params *params,
+                 struct lm_return_value *return_value)
+{
+       acpi_status status;
+       union acpi_object *obj;
+
+       struct acpi_buffer input = { sizeof(struct lm_input_params), params };
+       struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+
+       status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output);
+       if (ACPI_FAILURE(status))
+               return status;
+
+       obj = output.pointer;
+
+       if (!obj)
+               return AE_ERROR;
+       else if (obj->type != ACPI_TYPE_BUFFER) {
+               kfree(obj);
+               return AE_ERROR;
+       }
+       if (obj->buffer.length != 4) {
+               printk(ACER_WARNING "Unknown buffer length %d\n",
+                      obj->buffer.length);
+               kfree(obj);
+               return AE_ERROR;
+       }
+
+       *return_value = *((struct lm_return_value *)obj->buffer.pointer);
+       kfree(obj);
+
+       return status;
+}
+
+static int acer_wmi_enable_ec_raw(void)
+{
+       struct lm_return_value return_value;
+       acpi_status status;
+       struct lm_input_params params = {
+               .function_num = 0x1,
+               .commun_devices = 0xFFFF,
+               .devices = 0xFFFF,
+               .lm_status = 0x00,            /* Launch Manager Deactive */
+       };
+
+       status = wmid3_set_lm_mode(&params, &return_value);
+
+       if (return_value.error_code || return_value.ec_return_value)
+               printk(ACER_WARNING "Enabling EC raw mode failed: "
+                      "0x%x - 0x%x\n", return_value.error_code,
+                      return_value.ec_return_value);
+       else
+               printk(ACER_INFO "Enabled EC raw mode");
+
+       return status;
+}
+
+static int acer_wmi_enable_lm(void)
+{
+       struct lm_return_value return_value;
+       acpi_status status;
+       struct lm_input_params params = {
+               .function_num = 0x1,
+               .commun_devices = 0xFFFF,
+               .devices = 0xFFFF,
+               .lm_status = 0x01,            /* Launch Manager Active */
+       };
+
+       status = wmid3_set_lm_mode(&params, &return_value);
+
+       if (return_value.error_code || return_value.ec_return_value)
+               printk(ACER_WARNING "Enabling Launch Manager failed: "
+                      "0x%x - 0x%x\n", return_value.error_code,
+                      return_value.ec_return_value);
+
+       return status;
+}
+
+static int __init acer_wmi_input_setup(void)
+{
+       acpi_status status;
+       int err;
+
+       acer_wmi_input_dev = input_allocate_device();
+       if (!acer_wmi_input_dev)
+               return -ENOMEM;
+
+       acer_wmi_input_dev->name = "Acer WMI hotkeys";
+       acer_wmi_input_dev->phys = "wmi/input0";
+       acer_wmi_input_dev->id.bustype = BUS_HOST;
+
+       err = sparse_keymap_setup(acer_wmi_input_dev, acer_wmi_keymap, NULL);
+       if (err)
+               goto err_free_dev;
+
+       status = wmi_install_notify_handler(ACERWMID_EVENT_GUID,
+                                               acer_wmi_notify, NULL);
+       if (ACPI_FAILURE(status)) {
+               err = -EIO;
+               goto err_free_keymap;
+       }
+
+       err = input_register_device(acer_wmi_input_dev);
+       if (err)
+               goto err_uninstall_notifier;
+
+       return 0;
+
+err_uninstall_notifier:
+       wmi_remove_notify_handler(ACERWMID_EVENT_GUID);
+err_free_keymap:
+       sparse_keymap_free(acer_wmi_input_dev);
+err_free_dev:
+       input_free_device(acer_wmi_input_dev);
+       return err;
+}
+
+static void acer_wmi_input_destroy(void)
+{
+       wmi_remove_notify_handler(ACERWMID_EVENT_GUID);
+       sparse_keymap_free(acer_wmi_input_dev);
+       input_unregister_device(acer_wmi_input_dev);
+}
+
 /*
  * debugfs functions
  */
@@ -1327,6 +1714,26 @@ static int __init acer_wmi_init(void)
                       "generic video driver\n");
        }
 
+       if (wmi_has_guid(WMID_GUID3)) {
+               if (ec_raw_mode) {
+                       if (ACPI_FAILURE(acer_wmi_enable_ec_raw())) {
+                               printk(ACER_ERR "Cannot enable EC raw mode\n");
+                               return -ENODEV;
+                       }
+               } else if (ACPI_FAILURE(acer_wmi_enable_lm())) {
+                       printk(ACER_ERR "Cannot enable Launch Manager mode\n");
+                       return -ENODEV;
+               }
+       } else if (ec_raw_mode) {
+               printk(ACER_INFO "No WMID EC raw mode enable method\n");
+       }
+
+       if (wmi_has_guid(ACERWMID_EVENT_GUID)) {
+               err = acer_wmi_input_setup();
+               if (err)
+                       return err;
+       }
+
        err = platform_driver_register(&acer_platform_driver);
        if (err) {
                printk(ACER_ERR "Unable to register platform driver.\n");
@@ -1368,11 +1775,17 @@ error_device_add:
 error_device_alloc:
        platform_driver_unregister(&acer_platform_driver);
 error_platform_register:
+       if (wmi_has_guid(ACERWMID_EVENT_GUID))
+               acer_wmi_input_destroy();
+
        return err;
 }
 
 static void __exit acer_wmi_exit(void)
 {
+       if (wmi_has_guid(ACERWMID_EVENT_GUID))
+               acer_wmi_input_destroy();
+
        remove_sysfs(acer_platform_device);
        remove_debugfs();
        platform_device_unregister(acer_platform_device);
index 341cbfef93eee6d4e973be27ca989c279f8d9b4a..911135425224b4e8aab137d9b696500c794aaa71 100644 (file)
@@ -522,18 +522,20 @@ static int cmpc_rfkill_block(void *data, bool blocked)
        acpi_status status;
        acpi_handle handle;
        unsigned long long state;
+       bool is_blocked;
 
        handle = data;
        status = cmpc_get_rfkill_wlan(handle, &state);
        if (ACPI_FAILURE(status))
                return -ENODEV;
-       if (blocked)
-               state &= ~1;
-       else
-               state |= 1;
-       status = cmpc_set_rfkill_wlan(handle, state);
-       if (ACPI_FAILURE(status))
-               return -ENODEV;
+       /* Check if we really need to call cmpc_set_rfkill_wlan */
+       is_blocked = state & 1 ? false : true;
+       if (is_blocked != blocked) {
+               state = blocked ? 0 : 1;
+               status = cmpc_set_rfkill_wlan(handle, state);
+               if (ACPI_FAILURE(status))
+                       return -ENODEV;
+       }
        return 0;
 }
 
@@ -653,8 +655,9 @@ static void cmpc_keys_handler(struct acpi_device *dev, u32 event)
 
        if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes))
                code = cmpc_keys_codes[event & 0x0F];
-       inputdev = dev_get_drvdata(&dev->dev);;
+       inputdev = dev_get_drvdata(&dev->dev);
        input_report_key(inputdev, code, !(event & 0x10));
+       input_sync(inputdev);
 }
 
 static void cmpc_keys_idev_init(struct input_dev *inputdev)
index 097083cac4135c7d413cc9d5932dd6cf55c7f269..034572b980c99cb102e0aad04719bf6279baa75a 100644 (file)
@@ -872,6 +872,14 @@ static struct dmi_system_id __initdata compal_dmi_table[] = {
                },
                .callback = dmi_check_cb_extra
        },
+       {
+               .ident = "KHLB2",
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_NAME, "KHLB2"),
+                       DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
+               },
+               .callback = dmi_check_cb_extra
+       },
        { }
 };
 
index b2edfdcdcb84fd09d6eee096c56ef146351393be..e9fc530e7dc29135d2da0c7618fb6f6a7960dbd1 100644 (file)
@@ -529,6 +529,15 @@ static void tpd_led_set(struct led_classdev *led_cdev,
        queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work);
 }
 
+static enum led_brightness tpd_led_get(struct led_classdev *led_cdev)
+{
+       struct eeepc_laptop *eeepc;
+
+       eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led);
+
+       return get_acpi(eeepc, CM_ASL_TPD);
+}
+
 static int eeepc_led_init(struct eeepc_laptop *eeepc)
 {
        int rv;
@@ -543,6 +552,8 @@ static int eeepc_led_init(struct eeepc_laptop *eeepc)
 
        eeepc->tpd_led.name = "eeepc::touchpad";
        eeepc->tpd_led.brightness_set = tpd_led_set;
+       if (get_acpi(eeepc, CM_ASL_TPD) >= 0) /* if method is available */
+         eeepc->tpd_led.brightness_get = tpd_led_get;
        eeepc->tpd_led.max_brightness = 1;
 
        rv = led_classdev_register(&eeepc->platform_device->dev,
index 0d50fbbe24784e86e3eef1522134ac585d6b6a2c..4d38f98aa97657b0b13262327eccc757d6f93066 100644 (file)
@@ -2,6 +2,7 @@
  * Eee PC WMI hotkey driver
  *
  * Copyright(C) 2010 Intel Corporation.
+ * Copyright(C) 2010 Corentin Chary <corentin.chary@gmail.com>
  *
  * Portions based on wistron_btns.c:
  * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
 #include <linux/input/sparse-keymap.h>
 #include <linux/fb.h>
 #include <linux/backlight.h>
+#include <linux/leds.h>
+#include <linux/rfkill.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
 #include <linux/platform_device.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
@@ -44,6 +49,8 @@ MODULE_AUTHOR("Yong Wang <yong.y.wang@intel.com>");
 MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver");
 MODULE_LICENSE("GPL");
 
+#define EEEPC_ACPI_HID         "ASUS010" /* old _HID used in eeepc-laptop */
+
 #define EEEPC_WMI_EVENT_GUID   "ABBC0F72-8EA1-11D1-00A0-C90629100000"
 #define EEEPC_WMI_MGMT_GUID    "97845ED0-4E6D-11DE-8A39-0800200C9A66"
 
@@ -60,6 +67,10 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID);
 #define EEEPC_WMI_METHODID_CFVS        0x53564643
 
 #define EEEPC_WMI_DEVID_BACKLIGHT      0x00050012
+#define EEEPC_WMI_DEVID_TPDLED         0x00100011
+#define EEEPC_WMI_DEVID_WLAN           0x00010011
+#define EEEPC_WMI_DEVID_BLUETOOTH      0x00010013
+#define EEEPC_WMI_DEVID_WWAN3G         0x00010019
 
 static const struct key_entry eeepc_wmi_keymap[] = {
        /* Sleep already handled via generic ACPI code */
@@ -83,11 +94,37 @@ struct bios_args {
        u32     ctrl_param;
 };
 
+/*
+ * eeepc-wmi/    - debugfs root directory
+ *   dev_id      - current dev_id
+ *   ctrl_param  - current ctrl_param
+ *   devs        - call DEVS(dev_id, ctrl_param) and print result
+ *   dsts        - call DSTS(dev_id)  and print result
+ */
+struct eeepc_wmi_debug {
+       struct dentry *root;
+       u32 dev_id;
+       u32 ctrl_param;
+};
+
 struct eeepc_wmi {
        struct input_dev *inputdev;
        struct backlight_device *backlight_device;
+       struct platform_device *platform_device;
+
+       struct led_classdev tpd_led;
+       int tpd_led_wk;
+       struct workqueue_struct *led_workqueue;
+       struct work_struct tpd_led_work;
+
+       struct rfkill *wlan_rfkill;
+       struct rfkill *bluetooth_rfkill;
+       struct rfkill *wwan3g_rfkill;
+
+       struct eeepc_wmi_debug debug;
 };
 
+/* Only used in eeepc_wmi_init() and eeepc_wmi_exit() */
 static struct platform_device *platform_device;
 
 static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc)
@@ -101,7 +138,7 @@ static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc)
        eeepc->inputdev->name = "Eee PC WMI hotkeys";
        eeepc->inputdev->phys = EEEPC_WMI_FILE "/input0";
        eeepc->inputdev->id.bustype = BUS_HOST;
-       eeepc->inputdev->dev.parent = &platform_device->dev;
+       eeepc->inputdev->dev.parent = &eeepc->platform_device->dev;
 
        err = sparse_keymap_setup(eeepc->inputdev, eeepc_wmi_keymap, NULL);
        if (err)
@@ -130,7 +167,7 @@ static void eeepc_wmi_input_exit(struct eeepc_wmi *eeepc)
        eeepc->inputdev = NULL;
 }
 
-static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param)
+static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *retval)
 {
        struct acpi_buffer input = { (acpi_size)sizeof(u32), &dev_id };
        struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -150,8 +187,8 @@ static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param)
        else
                tmp = 0;
 
-       if (ctrl_param)
-               *ctrl_param = tmp;
+       if (retval)
+               *retval = tmp;
 
        kfree(obj);
 
@@ -159,7 +196,8 @@ static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param)
 
 }
 
-static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param)
+static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
+                                         u32 *retval)
 {
        struct bios_args args = {
                .dev_id = dev_id,
@@ -168,34 +206,281 @@ static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param)
        struct acpi_buffer input = { (acpi_size)sizeof(args), &args };
        acpi_status status;
 
-       status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID,
-                       1, EEEPC_WMI_METHODID_DEVS, &input, NULL);
+       if (!retval) {
+               status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1,
+                                            EEEPC_WMI_METHODID_DEVS,
+                                            &input, NULL);
+       } else {
+               struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+               union acpi_object *obj;
+               u32 tmp;
+
+               status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1,
+                                            EEEPC_WMI_METHODID_DEVS,
+                                            &input, &output);
+
+               if (ACPI_FAILURE(status))
+                       return status;
+
+               obj = (union acpi_object *)output.pointer;
+               if (obj && obj->type == ACPI_TYPE_INTEGER)
+                       tmp = (u32)obj->integer.value;
+               else
+                       tmp = 0;
+
+               *retval = tmp;
+
+               kfree(obj);
+       }
 
        return status;
 }
 
+/*
+ * LEDs
+ */
+/*
+ * These functions actually update the LED's, and are called from a
+ * workqueue. By doing this as separate work rather than when the LED
+ * subsystem asks, we avoid messing with the Eeepc ACPI stuff during a
+ * potentially bad time, such as a timer interrupt.
+ */
+static void tpd_led_update(struct work_struct *work)
+{
+       int ctrl_param;
+       struct eeepc_wmi *eeepc;
+
+       eeepc = container_of(work, struct eeepc_wmi, tpd_led_work);
+
+       ctrl_param = eeepc->tpd_led_wk;
+       eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_TPDLED, ctrl_param, NULL);
+}
+
+static void tpd_led_set(struct led_classdev *led_cdev,
+                       enum led_brightness value)
+{
+       struct eeepc_wmi *eeepc;
+
+       eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led);
+
+       eeepc->tpd_led_wk = !!value;
+       queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work);
+}
+
+static int read_tpd_state(struct eeepc_wmi *eeepc)
+{
+       u32 retval;
+       acpi_status status;
+
+       status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_TPDLED, &retval);
+
+       if (ACPI_FAILURE(status))
+               return -1;
+       else if (!retval || retval == 0x00060000)
+               /*
+                * if touchpad led is present, DSTS will set some bits,
+                * usually 0x00020000.
+                * 0x00060000 means that the device is not supported
+                */
+               return -ENODEV;
+       else
+               /* Status is stored in the first bit */
+               return retval & 0x1;
+}
+
+static enum led_brightness tpd_led_get(struct led_classdev *led_cdev)
+{
+       struct eeepc_wmi *eeepc;
+
+       eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led);
+
+       return read_tpd_state(eeepc);
+}
+
+static int eeepc_wmi_led_init(struct eeepc_wmi *eeepc)
+{
+       int rv;
+
+       if (read_tpd_state(eeepc) < 0)
+               return 0;
+
+       eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue");
+       if (!eeepc->led_workqueue)
+               return -ENOMEM;
+       INIT_WORK(&eeepc->tpd_led_work, tpd_led_update);
+
+       eeepc->tpd_led.name = "eeepc::touchpad";
+       eeepc->tpd_led.brightness_set = tpd_led_set;
+       eeepc->tpd_led.brightness_get = tpd_led_get;
+       eeepc->tpd_led.max_brightness = 1;
+
+       rv = led_classdev_register(&eeepc->platform_device->dev,
+                                  &eeepc->tpd_led);
+       if (rv) {
+               destroy_workqueue(eeepc->led_workqueue);
+               return rv;
+       }
+
+       return 0;
+}
+
+static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc)
+{
+       if (eeepc->tpd_led.dev)
+               led_classdev_unregister(&eeepc->tpd_led);
+       if (eeepc->led_workqueue)
+               destroy_workqueue(eeepc->led_workqueue);
+}
+
+/*
+ * Rfkill devices
+ */
+static int eeepc_rfkill_set(void *data, bool blocked)
+{
+       int dev_id = (unsigned long)data;
+       u32 ctrl_param = !blocked;
+
+       return eeepc_wmi_set_devstate(dev_id, ctrl_param, NULL);
+}
+
+static void eeepc_rfkill_query(struct rfkill *rfkill, void *data)
+{
+       int dev_id = (unsigned long)data;
+       u32 retval;
+       acpi_status status;
+
+       status = eeepc_wmi_get_devstate(dev_id, &retval);
+
+       if (ACPI_FAILURE(status))
+               return ;
+
+       rfkill_set_sw_state(rfkill, !(retval & 0x1));
+}
+
+static const struct rfkill_ops eeepc_rfkill_ops = {
+       .set_block = eeepc_rfkill_set,
+       .query = eeepc_rfkill_query,
+};
+
+static int eeepc_new_rfkill(struct eeepc_wmi *eeepc,
+                           struct rfkill **rfkill,
+                           const char *name,
+                           enum rfkill_type type, int dev_id)
+{
+       int result;
+       u32 retval;
+       acpi_status status;
+
+       status = eeepc_wmi_get_devstate(dev_id, &retval);
+
+       if (ACPI_FAILURE(status))
+               return -1;
+
+       /* If the device is present, DSTS will always set some bits
+        * 0x00070000 - 1110000000000000000 - device supported
+        * 0x00060000 - 1100000000000000000 - not supported
+        * 0x00020000 - 0100000000000000000 - device supported
+        * 0x00010000 - 0010000000000000000 - not supported / special mode ?
+        */
+       if (!retval || retval == 0x00060000)
+               return -ENODEV;
+
+       *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type,
+                              &eeepc_rfkill_ops, (void *)(long)dev_id);
+
+       if (!*rfkill)
+               return -EINVAL;
+
+       rfkill_init_sw_state(*rfkill, !(retval & 0x1));
+       result = rfkill_register(*rfkill);
+       if (result) {
+               rfkill_destroy(*rfkill);
+               *rfkill = NULL;
+               return result;
+       }
+       return 0;
+}
+
+static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc)
+{
+       if (eeepc->wlan_rfkill) {
+               rfkill_unregister(eeepc->wlan_rfkill);
+               rfkill_destroy(eeepc->wlan_rfkill);
+               eeepc->wlan_rfkill = NULL;
+       }
+       if (eeepc->bluetooth_rfkill) {
+               rfkill_unregister(eeepc->bluetooth_rfkill);
+               rfkill_destroy(eeepc->bluetooth_rfkill);
+               eeepc->bluetooth_rfkill = NULL;
+       }
+       if (eeepc->wwan3g_rfkill) {
+               rfkill_unregister(eeepc->wwan3g_rfkill);
+               rfkill_destroy(eeepc->wwan3g_rfkill);
+               eeepc->wwan3g_rfkill = NULL;
+       }
+}
+
+static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc)
+{
+       int result = 0;
+
+       result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill,
+                                 "eeepc-wlan", RFKILL_TYPE_WLAN,
+                                 EEEPC_WMI_DEVID_WLAN);
+
+       if (result && result != -ENODEV)
+               goto exit;
+
+       result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill,
+                                 "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH,
+                                 EEEPC_WMI_DEVID_BLUETOOTH);
+
+       if (result && result != -ENODEV)
+               goto exit;
+
+       result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill,
+                                 "eeepc-wwan3g", RFKILL_TYPE_WWAN,
+                                 EEEPC_WMI_DEVID_WWAN3G);
+
+       if (result && result != -ENODEV)
+               goto exit;
+
+exit:
+       if (result && result != -ENODEV)
+               eeepc_wmi_rfkill_exit(eeepc);
+
+       if (result == -ENODEV)
+               result = 0;
+
+       return result;
+}
+
+/*
+ * Backlight
+ */
 static int read_brightness(struct backlight_device *bd)
 {
-       static u32 ctrl_param;
+       u32 retval;
        acpi_status status;
 
-       status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &ctrl_param);
+       status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &retval);
 
        if (ACPI_FAILURE(status))
                return -1;
        else
-               return ctrl_param & 0xFF;
+               return retval & 0xFF;
 }
 
 static int update_bl_status(struct backlight_device *bd)
 {
 
-       static u32 ctrl_param;
+       u32 ctrl_param;
        acpi_status status;
 
        ctrl_param = bd->props.brightness;
 
-       status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT, ctrl_param);
+       status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT,
+                                       ctrl_param, NULL);
 
        if (ACPI_FAILURE(status))
                return -1;
@@ -234,7 +519,7 @@ static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc)
        memset(&props, 0, sizeof(struct backlight_properties));
        props.max_brightness = 15;
        bd = backlight_device_register(EEEPC_WMI_FILE,
-                                      &platform_device->dev, eeepc,
+                                      &eeepc->platform_device->dev, eeepc,
                                       &eeepc_wmi_bl_ops, &props);
        if (IS_ERR(bd)) {
                pr_err("Could not register backlight device\n");
@@ -321,65 +606,240 @@ static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr,
 
 static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv);
 
+static struct attribute *platform_attributes[] = {
+       &dev_attr_cpufv.attr,
+       NULL
+};
+
+static struct attribute_group platform_attribute_group = {
+       .attrs = platform_attributes
+};
+
 static void eeepc_wmi_sysfs_exit(struct platform_device *device)
 {
-       device_remove_file(&device->dev, &dev_attr_cpufv);
+       sysfs_remove_group(&device->dev.kobj, &platform_attribute_group);
 }
 
 static int eeepc_wmi_sysfs_init(struct platform_device *device)
 {
-       int retval = -ENOMEM;
+       return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
+}
 
-       retval = device_create_file(&device->dev, &dev_attr_cpufv);
-       if (retval)
-               goto error_sysfs;
+/*
+ * Platform device
+ */
+static int __init eeepc_wmi_platform_init(struct eeepc_wmi *eeepc)
+{
+       int err;
 
+       eeepc->platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1);
+       if (!eeepc->platform_device)
+               return -ENOMEM;
+       platform_set_drvdata(eeepc->platform_device, eeepc);
+
+       err = platform_device_add(eeepc->platform_device);
+       if (err)
+               goto fail_platform_device;
+
+       err = eeepc_wmi_sysfs_init(eeepc->platform_device);
+       if (err)
+               goto fail_sysfs;
        return 0;
 
-error_sysfs:
-       eeepc_wmi_sysfs_exit(platform_device);
-       return retval;
+fail_sysfs:
+       platform_device_del(eeepc->platform_device);
+fail_platform_device:
+       platform_device_put(eeepc->platform_device);
+       return err;
 }
 
-static int __devinit eeepc_wmi_platform_probe(struct platform_device *device)
+static void eeepc_wmi_platform_exit(struct eeepc_wmi *eeepc)
 {
+       eeepc_wmi_sysfs_exit(eeepc->platform_device);
+       platform_device_unregister(eeepc->platform_device);
+}
+
+/*
+ * debugfs
+ */
+struct eeepc_wmi_debugfs_node {
        struct eeepc_wmi *eeepc;
-       int err;
+       char *name;
+       int (*show)(struct seq_file *m, void *data);
+};
+
+static int show_dsts(struct seq_file *m, void *data)
+{
+       struct eeepc_wmi *eeepc = m->private;
        acpi_status status;
+       u32 retval = -1;
 
-       eeepc = platform_get_drvdata(device);
+       status = eeepc_wmi_get_devstate(eeepc->debug.dev_id, &retval);
+
+       if (ACPI_FAILURE(status))
+               return -EIO;
+
+       seq_printf(m, "DSTS(%x) = %x\n", eeepc->debug.dev_id, retval);
+
+       return 0;
+}
+
+static int show_devs(struct seq_file *m, void *data)
+{
+       struct eeepc_wmi *eeepc = m->private;
+       acpi_status status;
+       u32 retval = -1;
+
+       status = eeepc_wmi_set_devstate(eeepc->debug.dev_id,
+                                       eeepc->debug.ctrl_param, &retval);
+       if (ACPI_FAILURE(status))
+               return -EIO;
+
+       seq_printf(m, "DEVS(%x, %x) = %x\n", eeepc->debug.dev_id,
+                  eeepc->debug.ctrl_param, retval);
+
+       return 0;
+}
+
+static struct eeepc_wmi_debugfs_node eeepc_wmi_debug_files[] = {
+       { NULL, "devs", show_devs },
+       { NULL, "dsts", show_dsts },
+};
+
+static int eeepc_wmi_debugfs_open(struct inode *inode, struct file *file)
+{
+       struct eeepc_wmi_debugfs_node *node = inode->i_private;
+
+       return single_open(file, node->show, node->eeepc);
+}
+
+static const struct file_operations eeepc_wmi_debugfs_io_ops = {
+       .owner = THIS_MODULE,
+       .open  = eeepc_wmi_debugfs_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+static void eeepc_wmi_debugfs_exit(struct eeepc_wmi *eeepc)
+{
+       debugfs_remove_recursive(eeepc->debug.root);
+}
+
+static int eeepc_wmi_debugfs_init(struct eeepc_wmi *eeepc)
+{
+       struct dentry *dent;
+       int i;
+
+       eeepc->debug.root = debugfs_create_dir(EEEPC_WMI_FILE, NULL);
+       if (!eeepc->debug.root) {
+               pr_err("failed to create debugfs directory");
+               goto error_debugfs;
+       }
+
+       dent = debugfs_create_x32("dev_id", S_IRUGO|S_IWUSR,
+                                 eeepc->debug.root, &eeepc->debug.dev_id);
+       if (!dent)
+               goto error_debugfs;
+
+       dent = debugfs_create_x32("ctrl_param", S_IRUGO|S_IWUSR,
+                                 eeepc->debug.root, &eeepc->debug.ctrl_param);
+       if (!dent)
+               goto error_debugfs;
+
+       for (i = 0; i < ARRAY_SIZE(eeepc_wmi_debug_files); i++) {
+               struct eeepc_wmi_debugfs_node *node = &eeepc_wmi_debug_files[i];
+
+               node->eeepc = eeepc;
+               dent = debugfs_create_file(node->name, S_IFREG | S_IRUGO,
+                                          eeepc->debug.root, node,
+                                          &eeepc_wmi_debugfs_io_ops);
+               if (!dent) {
+                       pr_err("failed to create debug file: %s\n", node->name);
+                       goto error_debugfs;
+               }
+       }
+
+       return 0;
+
+error_debugfs:
+       eeepc_wmi_debugfs_exit(eeepc);
+       return -ENOMEM;
+}
+
+/*
+ * WMI Driver
+ */
+static struct platform_device * __init eeepc_wmi_add(void)
+{
+       struct eeepc_wmi *eeepc;
+       acpi_status status;
+       int err;
+
+       eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL);
+       if (!eeepc)
+               return ERR_PTR(-ENOMEM);
+
+       /*
+        * Register the platform device first.  It is used as a parent for the
+        * sub-devices below.
+        */
+       err = eeepc_wmi_platform_init(eeepc);
+       if (err)
+               goto fail_platform;
 
        err = eeepc_wmi_input_init(eeepc);
        if (err)
-               goto error_input;
+               goto fail_input;
+
+       err = eeepc_wmi_led_init(eeepc);
+       if (err)
+               goto fail_leds;
+
+       err = eeepc_wmi_rfkill_init(eeepc);
+       if (err)
+               goto fail_rfkill;
 
        if (!acpi_video_backlight_support()) {
                err = eeepc_wmi_backlight_init(eeepc);
                if (err)
-                       goto error_backlight;
+                       goto fail_backlight;
        } else
                pr_info("Backlight controlled by ACPI video driver\n");
 
        status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID,
-                                       eeepc_wmi_notify, eeepc);
+                                           eeepc_wmi_notify, eeepc);
        if (ACPI_FAILURE(status)) {
                pr_err("Unable to register notify handler - %d\n",
                        status);
                err = -ENODEV;
-               goto error_wmi;
+               goto fail_wmi_handler;
        }
 
-       return 0;
+       err = eeepc_wmi_debugfs_init(eeepc);
+       if (err)
+               goto fail_debugfs;
 
-error_wmi:
+       return eeepc->platform_device;
+
+fail_debugfs:
+       wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID);
+fail_wmi_handler:
        eeepc_wmi_backlight_exit(eeepc);
-error_backlight:
+fail_backlight:
+       eeepc_wmi_rfkill_exit(eeepc);
+fail_rfkill:
+       eeepc_wmi_led_exit(eeepc);
+fail_leds:
        eeepc_wmi_input_exit(eeepc);
-error_input:
-       return err;
+fail_input:
+       eeepc_wmi_platform_exit(eeepc);
+fail_platform:
+       kfree(eeepc);
+       return ERR_PTR(err);
 }
 
-static int __devexit eeepc_wmi_platform_remove(struct platform_device *device)
+static int eeepc_wmi_remove(struct platform_device *device)
 {
        struct eeepc_wmi *eeepc;
 
@@ -387,7 +847,12 @@ static int __devexit eeepc_wmi_platform_remove(struct platform_device *device)
        wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID);
        eeepc_wmi_backlight_exit(eeepc);
        eeepc_wmi_input_exit(eeepc);
+       eeepc_wmi_led_exit(eeepc);
+       eeepc_wmi_rfkill_exit(eeepc);
+       eeepc_wmi_debugfs_exit(eeepc);
+       eeepc_wmi_platform_exit(eeepc);
 
+       kfree(eeepc);
        return 0;
 }
 
@@ -396,13 +861,31 @@ static struct platform_driver platform_driver = {
                .name = EEEPC_WMI_FILE,
                .owner = THIS_MODULE,
        },
-       .probe = eeepc_wmi_platform_probe,
-       .remove = __devexit_p(eeepc_wmi_platform_remove),
 };
 
+static acpi_status __init eeepc_wmi_parse_device(acpi_handle handle, u32 level,
+                                                void *context, void **retval)
+{
+       pr_warning("Found legacy ATKD device (%s)", EEEPC_ACPI_HID);
+       *(bool *)context = true;
+       return AE_CTRL_TERMINATE;
+}
+
+static int __init eeepc_wmi_check_atkd(void)
+{
+       acpi_status status;
+       bool found = false;
+
+       status = acpi_get_devices(EEEPC_ACPI_HID, eeepc_wmi_parse_device,
+                                 &found, NULL);
+
+       if (ACPI_FAILURE(status) || !found)
+               return 0;
+       return -1;
+}
+
 static int __init eeepc_wmi_init(void)
 {
-       struct eeepc_wmi *eeepc;
        int err;
 
        if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) ||
@@ -411,58 +894,40 @@ static int __init eeepc_wmi_init(void)
                return -ENODEV;
        }
 
-       eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL);
-       if (!eeepc)
-               return -ENOMEM;
-
-       platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1);
-       if (!platform_device) {
-               pr_warning("Unable to allocate platform device\n");
-               err = -ENOMEM;
-               goto fail_platform;
+       if (eeepc_wmi_check_atkd()) {
+               pr_warning("WMI device present, but legacy ATKD device is also "
+                          "present and enabled.");
+               pr_warning("You probably booted with acpi_osi=\"Linux\" or "
+                          "acpi_osi=\"!Windows 2009\"");
+               pr_warning("Can't load eeepc-wmi, use default acpi_osi "
+                          "(preferred) or eeepc-laptop");
+               return -ENODEV;
        }
 
-       err = platform_device_add(platform_device);
-       if (err) {
-               pr_warning("Unable to add platform device\n");
-               goto put_dev;
+       platform_device = eeepc_wmi_add();
+       if (IS_ERR(platform_device)) {
+               err = PTR_ERR(platform_device);
+               goto fail_eeepc_wmi;
        }
 
-       platform_set_drvdata(platform_device, eeepc);
-
        err = platform_driver_register(&platform_driver);
        if (err) {
                pr_warning("Unable to register platform driver\n");
-               goto del_dev;
+               goto fail_platform_driver;
        }
 
-       err = eeepc_wmi_sysfs_init(platform_device);
-       if (err)
-               goto del_sysfs;
-
        return 0;
 
-del_sysfs:
-       eeepc_wmi_sysfs_exit(platform_device);
-del_dev:
-       platform_device_del(platform_device);
-put_dev:
-       platform_device_put(platform_device);
-fail_platform:
-       kfree(eeepc);
-
+fail_platform_driver:
+       eeepc_wmi_remove(platform_device);
+fail_eeepc_wmi:
        return err;
 }
 
 static void __exit eeepc_wmi_exit(void)
 {
-       struct eeepc_wmi *eeepc;
-
-       eeepc_wmi_sysfs_exit(platform_device);
-       eeepc = platform_get_drvdata(platform_device);
+       eeepc_wmi_remove(platform_device);
        platform_driver_unregister(&platform_driver);
-       platform_device_unregister(platform_device);
-       kfree(eeepc);
 }
 
 module_init(eeepc_wmi_init);
index f44cd2620ff9e123fab32ee1ce7355b9d477d1cc..ad88b2ec34a158e9996e81860414b5949f7c6103 100644 (file)
@@ -1240,7 +1240,7 @@ MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*");
 MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1E6:*:cvrS6420:*");
 MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*");
 
-static struct pnp_device_id pnp_ids[] = {
+static struct pnp_device_id pnp_ids[] __used = {
        {.id = "FUJ02bf"},
        {.id = "FUJ02B1"},
        {.id = "FUJ02E3"},
index 5ff12205aa6bb905b503f64829d90729d10d1b27..114d95247cdf8f6ad3c07d14a6a88496d6f63f43 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  ideapad_acpi.c - Lenovo IdeaPad ACPI Extras
+ *  ideapad-laptop.c - Lenovo IdeaPad ACPI Extras
  *
  *  Copyright Â© 2010 Intel Corporation
  *  Copyright Â© 2010 David Woodhouse <dwmw2@infradead.org>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
 #include <linux/rfkill.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
 
-#define IDEAPAD_DEV_CAMERA     0
-#define IDEAPAD_DEV_WLAN       1
-#define IDEAPAD_DEV_BLUETOOTH  2
-#define IDEAPAD_DEV_3G         3
-#define IDEAPAD_DEV_KILLSW     4
+#define IDEAPAD_RFKILL_DEV_NUM (3)
 
 struct ideapad_private {
-       acpi_handle handle;
-       struct rfkill *rfk[5];
-} *ideapad_priv;
-
-static struct {
-       char *name;
-       int cfgbit;
-       int opcode;
-       int type;
-} ideapad_rfk_data[] = {
-       { "ideapad_camera",     19, 0x1E, NUM_RFKILL_TYPES },
-       { "ideapad_wlan",       18, 0x15, RFKILL_TYPE_WLAN },
-       { "ideapad_bluetooth",  16, 0x17, RFKILL_TYPE_BLUETOOTH },
-       { "ideapad_3g",         17, 0x20, RFKILL_TYPE_WWAN },
-       { "ideapad_killsw",     0,  0,    RFKILL_TYPE_WLAN }
+       struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
+       struct platform_device *platform_device;
+       struct input_dev *inputdev;
 };
 
+static acpi_handle ideapad_handle;
 static bool no_bt_rfkill;
 module_param(no_bt_rfkill, bool, 0444);
 MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
@@ -163,17 +151,17 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
        pr_err("timeout in write_ec_cmd\n");
        return -1;
 }
-/* the above is ACPI helpers */
 
+/*
+ * camera power
+ */
 static ssize_t show_ideapad_cam(struct device *dev,
                                struct device_attribute *attr,
                                char *buf)
 {
-       struct ideapad_private *priv = dev_get_drvdata(dev);
-       acpi_handle handle = priv->handle;
        unsigned long result;
 
-       if (read_ec_data(handle, 0x1D, &result))
+       if (read_ec_data(ideapad_handle, 0x1D, &result))
                return sprintf(buf, "-1\n");
        return sprintf(buf, "%lu\n", result);
 }
@@ -182,15 +170,13 @@ static ssize_t store_ideapad_cam(struct device *dev,
                                 struct device_attribute *attr,
                                 const char *buf, size_t count)
 {
-       struct ideapad_private *priv = dev_get_drvdata(dev);
-       acpi_handle handle = priv->handle;
        int ret, state;
 
        if (!count)
                return 0;
        if (sscanf(buf, "%i", &state) != 1)
                return -EINVAL;
-       ret = write_ec_cmd(handle, 0x1E, state);
+       ret = write_ec_cmd(ideapad_handle, 0x1E, state);
        if (ret < 0)
                return ret;
        return count;
@@ -198,16 +184,27 @@ static ssize_t store_ideapad_cam(struct device *dev,
 
 static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
 
+/*
+ * Rfkill
+ */
+struct ideapad_rfk_data {
+       char *name;
+       int cfgbit;
+       int opcode;
+       int type;
+};
+
+const struct ideapad_rfk_data ideapad_rfk_data[] = {
+       { "ideapad_wlan",       18, 0x15, RFKILL_TYPE_WLAN },
+       { "ideapad_bluetooth",  16, 0x17, RFKILL_TYPE_BLUETOOTH },
+       { "ideapad_3g",         17, 0x20, RFKILL_TYPE_WWAN },
+};
+
 static int ideapad_rfk_set(void *data, bool blocked)
 {
-       int device = (unsigned long)data;
+       unsigned long opcode = (unsigned long)data;
 
-       if (device == IDEAPAD_DEV_KILLSW)
-               return -EINVAL;
-
-       return write_ec_cmd(ideapad_priv->handle,
-                           ideapad_rfk_data[device].opcode,
-                           !blocked);
+       return write_ec_cmd(ideapad_handle, opcode, !blocked);
 }
 
 static struct rfkill_ops ideapad_rfk_ops = {
@@ -217,20 +214,20 @@ static struct rfkill_ops ideapad_rfk_ops = {
 static void ideapad_sync_rfk_state(struct acpi_device *adevice)
 {
        struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
-       acpi_handle handle = priv->handle;
        unsigned long hw_blocked;
        int i;
 
-       if (read_ec_data(handle, 0x23, &hw_blocked))
+       if (read_ec_data(ideapad_handle, 0x23, &hw_blocked))
                return;
        hw_blocked = !hw_blocked;
 
-       for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++)
+       for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
                if (priv->rfk[i])
                        rfkill_set_hw_state(priv->rfk[i], hw_blocked);
 }
 
-static int ideapad_register_rfkill(struct acpi_device *adevice, int dev)
+static int __devinit ideapad_register_rfkill(struct acpi_device *adevice,
+                                            int dev)
 {
        struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
        int ret;
@@ -239,7 +236,7 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev)
        if (no_bt_rfkill &&
            (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) {
                /* Force to enable bluetooth when no_bt_rfkill=1 */
-               write_ec_cmd(ideapad_priv->handle,
+               write_ec_cmd(ideapad_handle,
                             ideapad_rfk_data[dev].opcode, 1);
                return 0;
        }
@@ -250,7 +247,7 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev)
        if (!priv->rfk[dev])
                return -ENOMEM;
 
-       if (read_ec_data(ideapad_priv->handle, ideapad_rfk_data[dev].opcode-1,
+       if (read_ec_data(ideapad_handle, ideapad_rfk_data[dev].opcode-1,
                         &sw_blocked)) {
                rfkill_init_sw_state(priv->rfk[dev], 0);
        } else {
@@ -266,7 +263,8 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev)
        return 0;
 }
 
-static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev)
+static void __devexit ideapad_unregister_rfkill(struct acpi_device *adevice,
+                                               int dev)
 {
        struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
 
@@ -277,73 +275,177 @@ static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev)
        rfkill_destroy(priv->rfk[dev]);
 }
 
+/*
+ * Platform device
+ */
+static struct attribute *ideapad_attributes[] = {
+       &dev_attr_camera_power.attr,
+       NULL
+};
+
+static struct attribute_group ideapad_attribute_group = {
+       .attrs = ideapad_attributes
+};
+
+static int __devinit ideapad_platform_init(struct ideapad_private *priv)
+{
+       int result;
+
+       priv->platform_device = platform_device_alloc("ideapad", -1);
+       if (!priv->platform_device)
+               return -ENOMEM;
+       platform_set_drvdata(priv->platform_device, priv);
+
+       result = platform_device_add(priv->platform_device);
+       if (result)
+               goto fail_platform_device;
+
+       result = sysfs_create_group(&priv->platform_device->dev.kobj,
+                                   &ideapad_attribute_group);
+       if (result)
+               goto fail_sysfs;
+       return 0;
+
+fail_sysfs:
+       platform_device_del(priv->platform_device);
+fail_platform_device:
+       platform_device_put(priv->platform_device);
+       return result;
+}
+
+static void ideapad_platform_exit(struct ideapad_private *priv)
+{
+       sysfs_remove_group(&priv->platform_device->dev.kobj,
+                          &ideapad_attribute_group);
+       platform_device_unregister(priv->platform_device);
+}
+
+/*
+ * input device
+ */
+static const struct key_entry ideapad_keymap[] = {
+       { KE_KEY, 0x06, { KEY_SWITCHVIDEOMODE } },
+       { KE_KEY, 0x0D, { KEY_WLAN } },
+       { KE_END, 0 },
+};
+
+static int __devinit ideapad_input_init(struct ideapad_private *priv)
+{
+       struct input_dev *inputdev;
+       int error;
+
+       inputdev = input_allocate_device();
+       if (!inputdev) {
+               pr_info("Unable to allocate input device\n");
+               return -ENOMEM;
+       }
+
+       inputdev->name = "Ideapad extra buttons";
+       inputdev->phys = "ideapad/input0";
+       inputdev->id.bustype = BUS_HOST;
+       inputdev->dev.parent = &priv->platform_device->dev;
+
+       error = sparse_keymap_setup(inputdev, ideapad_keymap, NULL);
+       if (error) {
+               pr_err("Unable to setup input device keymap\n");
+               goto err_free_dev;
+       }
+
+       error = input_register_device(inputdev);
+       if (error) {
+               pr_err("Unable to register input device\n");
+               goto err_free_keymap;
+       }
+
+       priv->inputdev = inputdev;
+       return 0;
+
+err_free_keymap:
+       sparse_keymap_free(inputdev);
+err_free_dev:
+       input_free_device(inputdev);
+       return error;
+}
+
+static void __devexit ideapad_input_exit(struct ideapad_private *priv)
+{
+       sparse_keymap_free(priv->inputdev);
+       input_unregister_device(priv->inputdev);
+       priv->inputdev = NULL;
+}
+
+static void ideapad_input_report(struct ideapad_private *priv,
+                                unsigned long scancode)
+{
+       sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
+}
+
+/*
+ * module init/exit
+ */
 static const struct acpi_device_id ideapad_device_ids[] = {
        { "VPC2004", 0},
        { "", 0},
 };
 MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
 
-static int ideapad_acpi_add(struct acpi_device *adevice)
+static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
 {
-       int i, cfg;
-       int devs_present[5];
+       int ret, i, cfg;
        struct ideapad_private *priv;
 
        if (read_method_int(adevice->handle, "_CFG", &cfg))
                return -ENODEV;
 
-       for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) {
-               if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg))
-                       devs_present[i] = 1;
-               else
-                       devs_present[i] = 0;
-       }
-
-       /* The hardware switch is always present */
-       devs_present[IDEAPAD_DEV_KILLSW] = 1;
-
        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
+       dev_set_drvdata(&adevice->dev, priv);
+       ideapad_handle = adevice->handle;
 
-       if (devs_present[IDEAPAD_DEV_CAMERA]) {
-               int ret = device_create_file(&adevice->dev, &dev_attr_camera_power);
-               if (ret) {
-                       kfree(priv);
-                       return ret;
-               }
-       }
+       ret = ideapad_platform_init(priv);
+       if (ret)
+               goto platform_failed;
 
-       priv->handle = adevice->handle;
-       dev_set_drvdata(&adevice->dev, priv);
-       ideapad_priv = priv;
-       for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) {
-               if (!devs_present[i])
-                       continue;
+       ret = ideapad_input_init(priv);
+       if (ret)
+               goto input_failed;
 
-               ideapad_register_rfkill(adevice, i);
+       for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) {
+               if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg))
+                       ideapad_register_rfkill(adevice, i);
+               else
+                       priv->rfk[i] = NULL;
        }
        ideapad_sync_rfk_state(adevice);
+
        return 0;
+
+input_failed:
+       ideapad_platform_exit(priv);
+platform_failed:
+       kfree(priv);
+       return ret;
 }
 
-static int ideapad_acpi_remove(struct acpi_device *adevice, int type)
+static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type)
 {
        struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
        int i;
 
-       device_remove_file(&adevice->dev, &dev_attr_camera_power);
-
-       for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++)
+       for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
                ideapad_unregister_rfkill(adevice, i);
-
+       ideapad_input_exit(priv);
+       ideapad_platform_exit(priv);
        dev_set_drvdata(&adevice->dev, NULL);
        kfree(priv);
+
        return 0;
 }
 
 static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
 {
+       struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
        acpi_handle handle = adevice->handle;
        unsigned long vpc1, vpc2, vpc_bit;
 
@@ -357,6 +459,8 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
                if (test_bit(vpc_bit, &vpc1)) {
                        if (vpc_bit == 9)
                                ideapad_sync_rfk_state(adevice);
+                       else
+                               ideapad_input_report(priv, vpc_bit);
                }
        }
 }
@@ -371,19 +475,14 @@ static struct acpi_driver ideapad_acpi_driver = {
        .owner = THIS_MODULE,
 };
 
-
 static int __init ideapad_acpi_module_init(void)
 {
-       acpi_bus_register_driver(&ideapad_acpi_driver);
-
-       return 0;
+       return acpi_bus_register_driver(&ideapad_acpi_driver);
 }
 
-
 static void __exit ideapad_acpi_module_exit(void)
 {
        acpi_bus_unregister_driver(&ideapad_acpi_driver);
-
 }
 
 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
index f0b3ad13c273fb73394cc78ca8fb36521a8a4c90..1294a39373bab688fbc8ce14a777a8811c66c7b2 100644 (file)
@@ -1474,7 +1474,7 @@ ips_gpu_turbo_enabled(struct ips_driver *ips)
 }
 
 void
-ips_link_to_i915_driver()
+ips_link_to_i915_driver(void)
 {
        /* We can't cleanly get at the various ips_driver structs from
         * this caller (the i915 driver), so just set a flag saying
index e61db9dfebefd2382c65cbbecf8bdb57d3e63f28..930e62762365ab262155baf814ad6f74fa261733 100644 (file)
@@ -244,7 +244,11 @@ static void pmic_irq_handler(unsigned irq, struct irq_desc *desc)
                        generic_handle_irq(pg->irq_base + gpio);
                }
        }
-       desc->chip->eoi(irq);
+
+       if (desc->chip->irq_eoi)
+               desc->chip->irq_eoi(irq_get_irq_data(irq));
+       else
+               dev_warn(pg->chip.dev, "missing EOI handler for irq %d\n", irq);
 }
 
 static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev)
index ca35b0ce944a58ca017ff5b5678b73a468d7a42a..1752ef006d26e44f2154293ba9c6f052cf2480e0 100644 (file)
@@ -497,7 +497,7 @@ int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data)
                        "intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd);
 
                mutex_unlock(&ipclock);
-               return -1;
+               return -EIO;
        }
        mutex_unlock(&ipclock);
        return 0;
@@ -642,7 +642,7 @@ update_end:
 
        if (status == IPC_FW_UPDATE_SUCCESS)
                return 0;
-       return -1;
+       return -EIO;
 }
 EXPORT_SYMBOL(intel_scu_ipc_fw_update);
 
diff --git a/drivers/platform/x86/intel_scu_ipcutil.c b/drivers/platform/x86/intel_scu_ipcutil.c
new file mode 100644 (file)
index 0000000..ba3231d
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * intel_scu_ipc.c: Driver for the Intel SCU IPC mechanism
+ *
+ * (C) Copyright 2008-2010 Intel Corporation
+ * Author: Sreedhara DS (sreedhara.ds@intel.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ *
+ * This driver provides ioctl interfaces to call intel scu ipc driver api
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <asm/intel_scu_ipc.h>
+
+static u32 major;
+
+#define MAX_FW_SIZE 264192
+
+/* ioctl commnds */
+#define        INTE_SCU_IPC_REGISTER_READ      0
+#define INTE_SCU_IPC_REGISTER_WRITE    1
+#define INTE_SCU_IPC_REGISTER_UPDATE   2
+#define INTE_SCU_IPC_FW_UPDATE         0xA2
+
+struct scu_ipc_data {
+       u32     count;  /* No. of registers */
+       u16     addr[5]; /* Register addresses */
+       u8      data[5]; /* Register data */
+       u8      mask; /* Valid for read-modify-write */
+};
+
+/**
+ *     scu_reg_access          -       implement register access ioctls
+ *     @cmd: command we are doing (read/write/update)
+ *     @data: kernel copy of ioctl data
+ *
+ *     Allow the user to perform register accesses on the SCU via the
+ *     kernel interface
+ */
+
+static int scu_reg_access(u32 cmd, struct scu_ipc_data  *data)
+{
+       int count = data->count;
+
+       if (count == 0 || count == 3 || count > 4)
+               return -EINVAL;
+
+       switch (cmd) {
+       case INTE_SCU_IPC_REGISTER_READ:
+               return intel_scu_ipc_readv(data->addr, data->data, count);
+       case INTE_SCU_IPC_REGISTER_WRITE:
+               return intel_scu_ipc_writev(data->addr, data->data, count);
+       case INTE_SCU_IPC_REGISTER_UPDATE:
+               return intel_scu_ipc_update_register(data->addr[0],
+                                                   data->data[0], data->mask);
+       default:
+               return -ENOTTY;
+       }
+}
+
+/**
+ *     scu_ipc_ioctl           -       control ioctls for the SCU
+ *     @fp: file handle of the SCU device
+ *     @cmd: ioctl coce
+ *     @arg: pointer to user passed structure
+ *
+ *     Support the I/O and firmware flashing interfaces of the SCU
+ */
+static long scu_ipc_ioctl(struct file *fp, unsigned int cmd,
+                                                       unsigned long arg)
+{
+       int ret;
+       struct scu_ipc_data  data;
+       void __user *argp = (void __user *)arg;
+
+       if (!capable(CAP_SYS_RAWIO))
+               return -EPERM;
+
+       if (cmd == INTE_SCU_IPC_FW_UPDATE) {
+                       u8 *fwbuf = kmalloc(MAX_FW_SIZE, GFP_KERNEL);
+                       if (fwbuf == NULL)
+                               return -ENOMEM;
+                       if (copy_from_user(fwbuf, (u8 *)arg, MAX_FW_SIZE)) {
+                               kfree(fwbuf);
+                               return -EFAULT;
+                       }
+                       ret = intel_scu_ipc_fw_update(fwbuf, MAX_FW_SIZE);
+                       kfree(fwbuf);
+                       return ret;
+       } else {
+               if (copy_from_user(&data, argp, sizeof(struct scu_ipc_data)))
+                       return -EFAULT;
+               ret = scu_reg_access(cmd, &data);
+               if (ret < 0)
+                       return ret;
+               if (copy_to_user(argp, &data, sizeof(struct scu_ipc_data)))
+                       return -EFAULT;
+               return 0;
+       }
+}
+
+static const struct file_operations scu_ipc_fops = {
+       .unlocked_ioctl = scu_ipc_ioctl,
+};
+
+static int __init ipc_module_init(void)
+{
+       return register_chrdev(0, "intel_mid_scu", &scu_ipc_fops);
+}
+
+static void __exit ipc_module_exit(void)
+{
+       unregister_chrdev(major, "intel_mid_scu");
+}
+
+module_init(ipc_module_init);
+module_exit(ipc_module_exit);
+
+MODULE_LICENSE("GPL V2");
+MODULE_DESCRIPTION("Utility driver for intel scu ipc");
+MODULE_AUTHOR("Sreedhara <sreedhara.ds@intel.com>");
index f200677851b8bcc508cdee1f350c3c829d31a12d..b4a95bb2f232a1ed907a2f5ccea9207412dcd380 100644 (file)
@@ -235,6 +235,7 @@ static int sony_laptop_input_index[] = {
        57,     /* 70 SONYPI_EVENT_VOLUME_DEC_PRESSED */
        -1,     /* 71 SONYPI_EVENT_BRIGHTNESS_PRESSED */
        58,     /* 72 SONYPI_EVENT_MEDIA_PRESSED */
+       59,     /* 72 SONYPI_EVENT_VENDOR_PRESSED */
 };
 
 static int sony_laptop_input_keycode_map[] = {
@@ -297,6 +298,7 @@ static int sony_laptop_input_keycode_map[] = {
        KEY_VOLUMEUP,   /* 56 SONYPI_EVENT_VOLUME_INC_PRESSED */
        KEY_VOLUMEDOWN, /* 57 SONYPI_EVENT_VOLUME_DEC_PRESSED */
        KEY_MEDIA,      /* 58 SONYPI_EVENT_MEDIA_PRESSED */
+       KEY_VENDOR,     /* 59 SONYPI_EVENT_VENDOR_PRESSED */
 };
 
 /* release buttons after a short delay if pressed */
@@ -894,10 +896,18 @@ static struct sony_nc_event sony_100_events[] = {
        { 0x0A, SONYPI_EVENT_FNKEY_RELEASED },
        { 0x8C, SONYPI_EVENT_FNKEY_F12 },
        { 0x0C, SONYPI_EVENT_FNKEY_RELEASED },
+       { 0x9d, SONYPI_EVENT_ZOOM_PRESSED },
+       { 0x1d, SONYPI_EVENT_ANYBUTTON_RELEASED },
        { 0x9f, SONYPI_EVENT_CD_EJECT_PRESSED },
        { 0x1f, SONYPI_EVENT_ANYBUTTON_RELEASED },
        { 0xa1, SONYPI_EVENT_MEDIA_PRESSED },
        { 0x21, SONYPI_EVENT_ANYBUTTON_RELEASED },
+       { 0xa4, SONYPI_EVENT_CD_EJECT_PRESSED },
+       { 0x24, SONYPI_EVENT_ANYBUTTON_RELEASED },
+       { 0xa5, SONYPI_EVENT_VENDOR_PRESSED },
+       { 0x25, SONYPI_EVENT_ANYBUTTON_RELEASED },
+       { 0xa6, SONYPI_EVENT_HELP_PRESSED },
+       { 0x26, SONYPI_EVENT_ANYBUTTON_RELEASED },
        { 0, 0 },
 };
 
@@ -1131,7 +1141,7 @@ static int sony_nc_setup_rfkill(struct acpi_device *device,
        return err;
 }
 
-static void sony_nc_rfkill_update()
+static void sony_nc_rfkill_update(void)
 {
        enum sony_nc_rfkill i;
        int result;
index e8c21994b36da2c1bb9eb71af883a597566f532c..a974ca383cb90fd1872e36c873006c52be1ab5e0 100644 (file)
@@ -589,6 +589,7 @@ static int acpi_evalf(acpi_handle handle,
                default:
                        printk(TPACPI_ERR "acpi_evalf() called "
                               "with invalid format character '%c'\n", c);
+                       va_end(ap);
                        return 0;
                }
        }
@@ -6345,7 +6346,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
                        "as change notification\n");
        tpacpi_hotkey_driver_mask_set(hotkey_driver_mask
                                | TP_ACPI_HKEY_BRGHTUP_MASK
-                               | TP_ACPI_HKEY_BRGHTDWN_MASK);;
+                               | TP_ACPI_HKEY_BRGHTDWN_MASK);
        return 0;
 }
 
index aecd9a9b549f004d692976ce2a911644e2c56eb7..05cc79672a8baf3e32b79db9400a950e4c6dcd81 100644 (file)
@@ -549,21 +549,34 @@ acpi_status wmi_install_notify_handler(const char *guid,
 wmi_notify_handler handler, void *data)
 {
        struct wmi_block *block;
-       acpi_status status;
+       acpi_status status = AE_NOT_EXIST;
+       char tmp[16], guid_input[16];
+       struct list_head *p;
 
        if (!guid || !handler)
                return AE_BAD_PARAMETER;
 
-       if (!find_guid(guid, &block))
-               return AE_NOT_EXIST;
+       wmi_parse_guid(guid, tmp);
+       wmi_swap_bytes(tmp, guid_input);
 
-       if (block->handler && block->handler != wmi_notify_debug)
-               return AE_ALREADY_ACQUIRED;
+       list_for_each(p, &wmi_block_list) {
+               acpi_status wmi_status;
+               block = list_entry(p, struct wmi_block, list);
 
-       block->handler = handler;
-       block->handler_data = data;
+               if (memcmp(block->gblock.guid, guid_input, 16) == 0) {
+                       if (block->handler &&
+                           block->handler != wmi_notify_debug)
+                               return AE_ALREADY_ACQUIRED;
 
-       status = wmi_method_enable(block, 1);
+                       block->handler = handler;
+                       block->handler_data = data;
+
+                       wmi_status = wmi_method_enable(block, 1);
+                       if ((wmi_status != AE_OK) ||
+                           ((wmi_status == AE_OK) && (status == AE_NOT_EXIST)))
+                               status = wmi_status;
+               }
+       }
 
        return status;
 }
@@ -577,24 +590,40 @@ EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
 acpi_status wmi_remove_notify_handler(const char *guid)
 {
        struct wmi_block *block;
-       acpi_status status = AE_OK;
+       acpi_status status = AE_NOT_EXIST;
+       char tmp[16], guid_input[16];
+       struct list_head *p;
 
        if (!guid)
                return AE_BAD_PARAMETER;
 
-       if (!find_guid(guid, &block))
-               return AE_NOT_EXIST;
+       wmi_parse_guid(guid, tmp);
+       wmi_swap_bytes(tmp, guid_input);
 
-       if (!block->handler || block->handler == wmi_notify_debug)
-               return AE_NULL_ENTRY;
+       list_for_each(p, &wmi_block_list) {
+               acpi_status wmi_status;
+               block = list_entry(p, struct wmi_block, list);
 
-       if (debug_event) {
-               block->handler = wmi_notify_debug;
-       } else {
-               status = wmi_method_enable(block, 0);
-               block->handler = NULL;
-               block->handler_data = NULL;
+               if (memcmp(block->gblock.guid, guid_input, 16) == 0) {
+                       if (!block->handler ||
+                           block->handler == wmi_notify_debug)
+                               return AE_NULL_ENTRY;
+
+                       if (debug_event) {
+                               block->handler = wmi_notify_debug;
+                               status = AE_OK;
+                       } else {
+                               wmi_status = wmi_method_enable(block, 0);
+                               block->handler = NULL;
+                               block->handler_data = NULL;
+                               if ((wmi_status != AE_OK) ||
+                                   ((wmi_status == AE_OK) &&
+                                    (status == AE_NOT_EXIST)))
+                                       status = wmi_status;
+                       }
+               }
        }
+
        return status;
 }
 EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
@@ -705,22 +734,11 @@ static struct class wmi_class = {
        .dev_attrs = wmi_dev_attrs,
 };
 
-static struct wmi_block *wmi_create_device(const struct guid_block *gblock,
-                                          acpi_handle handle)
+static int wmi_create_device(const struct guid_block *gblock,
+                            struct wmi_block *wblock, acpi_handle handle)
 {
-       struct wmi_block *wblock;
-       int error;
        char guid_string[37];
 
-       wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
-       if (!wblock) {
-               error = -ENOMEM;
-               goto err_out;
-       }
-
-       wblock->handle = handle;
-       wblock->gblock = *gblock;
-
        wblock->dev.class = &wmi_class;
 
        wmi_gtoa(gblock->guid, guid_string);
@@ -728,17 +746,7 @@ static struct wmi_block *wmi_create_device(const struct guid_block *gblock,
 
        dev_set_drvdata(&wblock->dev, wblock);
 
-       error = device_register(&wblock->dev);
-       if (error)
-               goto err_free;
-
-       list_add_tail(&wblock->list, &wmi_block_list);
-       return wblock;
-
-err_free:
-       kfree(wblock);
-err_out:
-       return ERR_PTR(error);
+       return device_register(&wblock->dev);
 }
 
 static void wmi_free_devices(void)
@@ -747,7 +755,8 @@ static void wmi_free_devices(void)
 
        /* Delete devices for all the GUIDs */
        list_for_each_entry_safe(wblock, next, &wmi_block_list, list)
-               device_unregister(&wblock->dev);
+               if (wblock->dev.class)
+                       device_unregister(&wblock->dev);
 }
 
 static bool guid_already_parsed(const char *guid_string)
@@ -770,7 +779,6 @@ static acpi_status parse_wdg(acpi_handle handle)
        union acpi_object *obj;
        const struct guid_block *gblock;
        struct wmi_block *wblock;
-       char guid_string[37];
        acpi_status status;
        int retval;
        u32 i, total;
@@ -792,28 +800,31 @@ static acpi_status parse_wdg(acpi_handle handle)
        total = obj->buffer.length / sizeof(struct guid_block);
 
        for (i = 0; i < total; i++) {
+               if (debug_dump_wdg)
+                       wmi_dump_wdg(&gblock[i]);
+
+               wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
+               if (!wblock)
+                       return AE_NO_MEMORY;
+
+               wblock->handle = handle;
+               wblock->gblock = gblock[i];
+
                /*
                  Some WMI devices, like those for nVidia hooks, have a
                  duplicate GUID. It's not clear what we should do in this
-                 case yet, so for now, we'll just ignore the duplicate.
-                 Anyone who wants to add support for that device can come
-                 up with a better workaround for the mess then.
+                 case yet, so for now, we'll just ignore the duplicate
+                 for device creation.
                */
-               if (guid_already_parsed(gblock[i].guid) == true) {
-                       wmi_gtoa(gblock[i].guid, guid_string);
-                       pr_info("Skipping duplicate GUID %s\n", guid_string);
-                       continue;
+               if (!guid_already_parsed(gblock[i].guid)) {
+                       retval = wmi_create_device(&gblock[i], wblock, handle);
+                       if (retval) {
+                               wmi_free_devices();
+                               goto out_free_pointer;
+                       }
                }
 
-               if (debug_dump_wdg)
-                       wmi_dump_wdg(&gblock[i]);
-
-               wblock = wmi_create_device(&gblock[i], handle);
-               if (IS_ERR(wblock)) {
-                       retval = PTR_ERR(wblock);
-                       wmi_free_devices();
-                       break;
-               }
+               list_add_tail(&wblock->list, &wmi_block_list);
 
                if (debug_event) {
                        wblock->handler = wmi_notify_debug;
index 4f95c1aac2fde62b3ddb6294d836243a23119146..0e6dc3891942e22b52457850fa246bf443749798 100644 (file)
 #define SONYPI_EVENT_VOLUME_DEC_PRESSED                70
 #define SONYPI_EVENT_BRIGHTNESS_PRESSED                71
 #define SONYPI_EVENT_MEDIA_PRESSED             72
+#define SONYPI_EVENT_VENDOR_PRESSED            73
 
 /* get/set brightness */
 #define SONYPI_IOCGBRT         _IOR('v', 0, __u8)