Merge branch 'afs-dh' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[linux-2.6-block.git] / drivers / platform / x86 / fujitsu-laptop.c
index 2cfbd3fa5136002362bdc558c14d00e40ed56e22..cd95b6f3a06405835edadfc6ea92a5b46bab591f 100644 (file)
@@ -53,6 +53,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/acpi.h>
+#include <linux/bitops.h>
 #include <linux/dmi.h>
 #include <linux/backlight.h>
 #include <linux/fb.h>
 #include <linux/kfifo.h>
 #include <linux/leds.h>
 #include <linux/platform_device.h>
-#include <linux/slab.h>
 #include <acpi/video.h>
 
-#define FUJITSU_DRIVER_VERSION "0.6.0"
+#define FUJITSU_DRIVER_VERSION         "0.6.0"
 
-#define FUJITSU_LCD_N_LEVELS 8
+#define FUJITSU_LCD_N_LEVELS           8
 
 #define ACPI_FUJITSU_CLASS             "fujitsu"
 #define ACPI_FUJITSU_BL_HID            "FUJ02B1"
 #define ACPI_FUJITSU_LAPTOP_DRIVER_NAME        "Fujitsu laptop FUJ02E3 ACPI hotkeys driver"
 #define ACPI_FUJITSU_LAPTOP_DEVICE_NAME        "Fujitsu FUJ02E3"
 
-#define ACPI_FUJITSU_NOTIFY_CODE1     0x80
+#define ACPI_FUJITSU_NOTIFY_CODE       0x80
 
 /* FUNC interface - command values */
-#define FUNC_FLAGS     0x1000
-#define FUNC_LEDS      0x1001
-#define FUNC_BUTTONS   0x1002
-#define FUNC_BACKLIGHT  0x1004
+#define FUNC_FLAGS                     BIT(12)
+#define FUNC_LEDS                      (BIT(12) | BIT(0))
+#define FUNC_BUTTONS                   (BIT(12) | BIT(1))
+#define FUNC_BACKLIGHT                 (BIT(12) | BIT(2))
 
 /* FUNC interface - responses */
-#define UNSUPPORTED_CMD 0x80000000
+#define UNSUPPORTED_CMD                        0x80000000
 
 /* FUNC interface - status flags */
-#define FLAG_RFKILL    0x020
-#define FLAG_LID       0x100
-#define FLAG_DOCK      0x200
+#define FLAG_RFKILL                    BIT(5)
+#define FLAG_LID                       BIT(8)
+#define FLAG_DOCK                      BIT(9)
 
 /* FUNC interface - LED control */
-#define FUNC_LED_OFF   0x1
-#define FUNC_LED_ON    0x30001
-#define KEYBOARD_LAMPS 0x100
-#define LOGOLAMP_POWERON 0x2000
-#define LOGOLAMP_ALWAYS  0x4000
-#define RADIO_LED_ON   0x20
-#define ECO_LED        0x10000
-#define ECO_LED_ON     0x80000
-
-/* Hotkey details */
-#define KEY1_CODE      0x410   /* codes for the keys in the GIRB register */
-#define KEY2_CODE      0x411
-#define KEY3_CODE      0x412
-#define KEY4_CODE      0x413
-#define KEY5_CODE      0x420
-
-#define MAX_HOTKEY_RINGBUFFER_SIZE 100
-#define RINGBUFFERSIZE 40
+#define FUNC_LED_OFF                   BIT(0)
+#define FUNC_LED_ON                    (BIT(0) | BIT(16) | BIT(17))
+#define LOGOLAMP_POWERON               BIT(13)
+#define LOGOLAMP_ALWAYS                        BIT(14)
+#define KEYBOARD_LAMPS                 BIT(8)
+#define RADIO_LED_ON                   BIT(5)
+#define ECO_LED                                BIT(16)
+#define ECO_LED_ON                     BIT(19)
+
+/* FUNC interface - backlight power control */
+#define BACKLIGHT_PARAM_POWER          BIT(2)
+#define BACKLIGHT_OFF                  (BIT(0) | BIT(1))
+#define BACKLIGHT_ON                   0
+
+/* Scancodes read from the GIRB register */
+#define KEY1_CODE                      0x410
+#define KEY2_CODE                      0x411
+#define KEY3_CODE                      0x412
+#define KEY4_CODE                      0x413
+#define KEY5_CODE                      0x420
+
+/* Hotkey ringbuffer limits */
+#define MAX_HOTKEY_RINGBUFFER_SIZE     100
+#define RINGBUFFERSIZE                 40
+
+/* Module parameters */
+static int use_alt_lcd_levels = -1;
+static bool disable_brightness_adjust;
 
 /* Device controlling the backlight and associated keys */
 struct fujitsu_bl {
@@ -122,8 +132,6 @@ struct fujitsu_bl {
 };
 
 static struct fujitsu_bl *fujitsu_bl;
-static int use_alt_lcd_levels = -1;
-static bool disable_brightness_adjust;
 
 /* Device used to access hotkeys and other features on the laptop */
 struct fujitsu_laptop {
@@ -256,9 +264,11 @@ static int bl_update_status(struct backlight_device *b)
 
        if (fext) {
                if (b->props.power == FB_BLANK_POWERDOWN)
-                       call_fext_func(fext, FUNC_BACKLIGHT, 0x1, 0x4, 0x3);
+                       call_fext_func(fext, FUNC_BACKLIGHT, 0x1,
+                                      BACKLIGHT_PARAM_POWER, BACKLIGHT_OFF);
                else
-                       call_fext_func(fext, FUNC_BACKLIGHT, 0x1, 0x4, 0x0);
+                       call_fext_func(fext, FUNC_BACKLIGHT, 0x1,
+                                      BACKLIGHT_PARAM_POWER, BACKLIGHT_ON);
        }
 
        return set_lcd_level(device, b->props.brightness);
@@ -385,7 +395,7 @@ static int fujitsu_backlight_register(struct acpi_device *device)
 static int acpi_fujitsu_bl_add(struct acpi_device *device)
 {
        struct fujitsu_bl *priv;
-       int error;
+       int ret;
 
        if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
                return -ENODEV;
@@ -399,10 +409,6 @@ static int acpi_fujitsu_bl_add(struct acpi_device *device)
        strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS);
        device->driver_data = priv;
 
-       error = acpi_fujitsu_bl_input_setup(device);
-       if (error)
-               return error;
-
        pr_info("ACPI: %s [%s]\n",
                acpi_device_name(device), acpi_device_bid(device));
 
@@ -410,11 +416,11 @@ static int acpi_fujitsu_bl_add(struct acpi_device *device)
                priv->max_brightness = FUJITSU_LCD_N_LEVELS;
        get_lcd_level(device);
 
-       error = fujitsu_backlight_register(device);
-       if (error)
-               return error;
+       ret = acpi_fujitsu_bl_input_setup(device);
+       if (ret)
+               return ret;
 
-       return 0;
+       return fujitsu_backlight_register(device);
 }
 
 /* Brightness notify */
@@ -424,7 +430,7 @@ static void acpi_fujitsu_bl_notify(struct acpi_device *device, u32 event)
        struct fujitsu_bl *priv = acpi_driver_data(device);
        int oldb, newb;
 
-       if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
+       if (event != ACPI_FUJITSU_NOTIFY_CODE) {
                acpi_handle_info(device->handle, "unsupported event [0x%x]\n",
                                 event);
                sparse_keymap_report_event(priv->input, -1, 1, true);
@@ -455,7 +461,9 @@ static const struct key_entry keymap_default[] = {
        { KE_KEY, KEY3_CODE, { KEY_PROG3 } },
        { KE_KEY, KEY4_CODE, { KEY_PROG4 } },
        { KE_KEY, KEY5_CODE, { KEY_RFKILL } },
+       { KE_KEY, BIT(5),    { KEY_RFKILL } },
        { KE_KEY, BIT(26),   { KEY_TOUCHPAD_TOGGLE } },
+       { KE_KEY, BIT(29),   { KEY_MICMUTE } },
        { KE_END, 0 }
 };
 
@@ -693,7 +701,7 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
 {
        struct fujitsu_laptop *priv = acpi_driver_data(device);
        struct led_classdev *led;
-       int result;
+       int ret;
 
        if (call_fext_func(device,
                           FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) {
@@ -704,9 +712,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
                led->name = "fujitsu::logolamp";
                led->brightness_set_blocking = logolamp_set;
                led->brightness_get = logolamp_get;
-               result = devm_led_classdev_register(&device->dev, led);
-               if (result)
-                       return result;
+               ret = devm_led_classdev_register(&device->dev, led);
+               if (ret)
+                       return ret;
        }
 
        if ((call_fext_func(device,
@@ -719,9 +727,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
                led->name = "fujitsu::kblamps";
                led->brightness_set_blocking = kblamps_set;
                led->brightness_get = kblamps_get;
-               result = devm_led_classdev_register(&device->dev, led);
-               if (result)
-                       return result;
+               ret = devm_led_classdev_register(&device->dev, led);
+               if (ret)
+                       return ret;
        }
 
        /*
@@ -742,9 +750,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
                led->brightness_set_blocking = radio_led_set;
                led->brightness_get = radio_led_get;
                led->default_trigger = "rfkill-any";
-               result = devm_led_classdev_register(&device->dev, led);
-               if (result)
-                       return result;
+               ret = devm_led_classdev_register(&device->dev, led);
+               if (ret)
+                       return ret;
        }
 
        /* Support for eco led is not always signaled in bit corresponding
@@ -762,9 +770,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
                led->name = "fujitsu::eco_led";
                led->brightness_set_blocking = eco_led_set;
                led->brightness_get = eco_led_get;
-               result = devm_led_classdev_register(&device->dev, led);
-               if (result)
-                       return result;
+               ret = devm_led_classdev_register(&device->dev, led);
+               if (ret)
+                       return ret;
        }
 
        return 0;
@@ -773,8 +781,7 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
 static int acpi_fujitsu_laptop_add(struct acpi_device *device)
 {
        struct fujitsu_laptop *priv;
-       int error;
-       int i;
+       int ret, i = 0;
 
        priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
@@ -789,23 +796,16 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device)
 
        /* kfifo */
        spin_lock_init(&priv->fifo_lock);
-       error = kfifo_alloc(&priv->fifo, RINGBUFFERSIZE * sizeof(int),
-                           GFP_KERNEL);
-       if (error) {
-               pr_err("kfifo_alloc failed\n");
-               goto err_stop;
-       }
-
-       error = acpi_fujitsu_laptop_input_setup(device);
-       if (error)
-               goto err_free_fifo;
+       ret = kfifo_alloc(&priv->fifo, RINGBUFFERSIZE * sizeof(int),
+                         GFP_KERNEL);
+       if (ret)
+               return ret;
 
        pr_info("ACPI: %s [%s]\n",
                acpi_device_name(device), acpi_device_bid(device));
 
-       i = 0;
-       while (call_fext_func(device, FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0
-               && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE)
+       while (call_fext_func(device, FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0 &&
+              i++ < MAX_HOTKEY_RINGBUFFER_SIZE)
                ; /* No action, result is discarded */
        acpi_handle_debug(device->handle, "Discarded %i ringbuffer entries\n",
                          i);
@@ -829,26 +829,31 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device)
        /* Sync backlight power status */
        if (fujitsu_bl && fujitsu_bl->bl_device &&
            acpi_video_get_backlight_type() == acpi_backlight_vendor) {
-               if (call_fext_func(fext, FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3)
+               if (call_fext_func(fext, FUNC_BACKLIGHT, 0x2,
+                                  BACKLIGHT_PARAM_POWER, 0x0) == BACKLIGHT_OFF)
                        fujitsu_bl->bl_device->props.power = FB_BLANK_POWERDOWN;
                else
                        fujitsu_bl->bl_device->props.power = FB_BLANK_UNBLANK;
        }
 
-       error = acpi_fujitsu_laptop_leds_register(device);
-       if (error)
+       ret = acpi_fujitsu_laptop_input_setup(device);
+       if (ret)
+               goto err_free_fifo;
+
+       ret = acpi_fujitsu_laptop_leds_register(device);
+       if (ret)
                goto err_free_fifo;
 
-       error = fujitsu_laptop_platform_add(device);
-       if (error)
+       ret = fujitsu_laptop_platform_add(device);
+       if (ret)
                goto err_free_fifo;
 
        return 0;
 
 err_free_fifo:
        kfifo_free(&priv->fifo);
-err_stop:
-       return error;
+
+       return ret;
 }
 
 static int acpi_fujitsu_laptop_remove(struct acpi_device *device)
@@ -865,11 +870,11 @@ static int acpi_fujitsu_laptop_remove(struct acpi_device *device)
 static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode)
 {
        struct fujitsu_laptop *priv = acpi_driver_data(device);
-       int status;
+       int ret;
 
-       status = kfifo_in_locked(&priv->fifo, (unsigned char *)&scancode,
-                                sizeof(scancode), &priv->fifo_lock);
-       if (status != sizeof(scancode)) {
+       ret = kfifo_in_locked(&priv->fifo, (unsigned char *)&scancode,
+                             sizeof(scancode), &priv->fifo_lock);
+       if (ret != sizeof(scancode)) {
                dev_info(&priv->input->dev, "Could not push scancode [0x%x]\n",
                         scancode);
                return;
@@ -882,13 +887,12 @@ static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode)
 static void acpi_fujitsu_laptop_release(struct acpi_device *device)
 {
        struct fujitsu_laptop *priv = acpi_driver_data(device);
-       int scancode, status;
+       int scancode, ret;
 
        while (true) {
-               status = kfifo_out_locked(&priv->fifo,
-                                         (unsigned char *)&scancode,
-                                         sizeof(scancode), &priv->fifo_lock);
-               if (status != sizeof(scancode))
+               ret = kfifo_out_locked(&priv->fifo, (unsigned char *)&scancode,
+                                      sizeof(scancode), &priv->fifo_lock);
+               if (ret != sizeof(scancode))
                        return;
                sparse_keymap_report_event(priv->input, scancode, 0, false);
                dev_dbg(&priv->input->dev,
@@ -899,10 +903,10 @@ static void acpi_fujitsu_laptop_release(struct acpi_device *device)
 static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event)
 {
        struct fujitsu_laptop *priv = acpi_driver_data(device);
-       int scancode, i = 0;
+       int scancode, i = 0, ret;
        unsigned int irb;
 
-       if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
+       if (event != ACPI_FUJITSU_NOTIFY_CODE) {
                acpi_handle_info(device->handle, "Unsupported event [0x%x]\n",
                                 event);
                sparse_keymap_report_event(priv->input, -1, 1, true);
@@ -930,9 +934,18 @@ static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event)
         * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is
         * handled in software; its state is queried using FUNC_FLAGS
         */
-       if ((priv->flags_supported & BIT(26)) &&
-           (call_fext_func(device, FUNC_FLAGS, 0x1, 0x0, 0x0) & BIT(26)))
-               sparse_keymap_report_event(priv->input, BIT(26), 1, true);
+       if (priv->flags_supported & (BIT(5) | BIT(26) | BIT(29))) {
+               ret = call_fext_func(device, FUNC_FLAGS, 0x1, 0x0, 0x0);
+               if (ret & BIT(5))
+                       sparse_keymap_report_event(priv->input,
+                                                  BIT(5), 1, true);
+               if (ret & BIT(26))
+                       sparse_keymap_report_event(priv->input,
+                                                  BIT(26), 1, true);
+               if (ret & BIT(29))
+                       sparse_keymap_report_event(priv->input,
+                                                  BIT(29), 1, true);
+       }
 }
 
 /* Initialization */