]> err.no Git - linux-2.6/blobdiff - drivers/misc/thinkpad_acpi.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/roland...
[linux-2.6] / drivers / misc / thinkpad_acpi.c
index 99500af651c1ca5eb3a16fa1dca36a18ee9ef158..f15a58f7403fd4b231ddd267ebb02f098d22957b 100644 (file)
@@ -21,7 +21,7 @@
  *  02110-1301, USA.
  */
 
-#define IBM_VERSION "0.14"
+#define IBM_VERSION "0.15"
 #define TPACPI_SYSFS_VERSION 0x010000
 
 /*
@@ -758,29 +758,7 @@ static u32 hotkey_orig_mask;
 static u32 hotkey_all_mask;
 static u32 hotkey_reserved_mask;
 
-static u16 hotkey_keycode_map[] = {
-       /* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */
-       KEY_FN_F1,      KEY_FN_F2,      KEY_FN_F3,      KEY_SLEEP,
-       KEY_FN_F5,      KEY_FN_F6,      KEY_FN_F7,      KEY_FN_F8,
-       KEY_FN_F9,      KEY_FN_F10,     KEY_FN_F11,     KEY_SUSPEND,
-       /* Scan codes 0x0C to 0x0F: Other ACPI HKEY hot keys */
-       KEY_UNKNOWN,    /* 0x0C: FN+BACKSPACE */
-       KEY_UNKNOWN,    /* 0x0D: FN+INSERT */
-       KEY_UNKNOWN,    /* 0x0E: FN+DELETE */
-       KEY_RESERVED,   /* 0x0F: FN+HOME (brightness up) */
-       /* Scan codes 0x10 to 0x1F: Extended ACPI HKEY hot keys */
-       KEY_RESERVED,   /* 0x10: FN+END (brightness down) */
-       KEY_RESERVED,   /* 0x11: FN+PGUP (thinklight toggle) */
-       KEY_UNKNOWN,    /* 0x12: FN+PGDOWN */
-       KEY_ZOOM,       /* 0x13: FN+SPACE (zoom) */
-       KEY_RESERVED,   /* 0x14: VOLUME UP */
-       KEY_RESERVED,   /* 0x15: VOLUME DOWN */
-       KEY_RESERVED,   /* 0x16: MUTE */
-       KEY_VENDOR,     /* 0x17: Thinkpad/AccessIBM/Lenovo */
-       /* (assignments unknown, please report if found) */
-       KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
-       KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
-};
+static u16 *hotkey_keycode_map;
 
 static struct attribute_set *hotkey_dev_attributes;
 
@@ -939,6 +917,58 @@ static struct attribute *hotkey_mask_attributes[] = {
 
 static int __init hotkey_init(struct ibm_init_struct *iibm)
 {
+
+       static u16 ibm_keycode_map[] __initdata = {
+               /* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */
+               KEY_FN_F1,      KEY_FN_F2,      KEY_COFFEE,     KEY_SLEEP,
+               KEY_WLAN,       KEY_FN_F6, KEY_SWITCHVIDEOMODE, KEY_FN_F8,
+               KEY_FN_F9,      KEY_FN_F10,     KEY_FN_F11,     KEY_SUSPEND,
+               /* Scan codes 0x0C to 0x0F: Other ACPI HKEY hot keys */
+               KEY_UNKNOWN,    /* 0x0C: FN+BACKSPACE */
+               KEY_UNKNOWN,    /* 0x0D: FN+INSERT */
+               KEY_UNKNOWN,    /* 0x0E: FN+DELETE */
+               KEY_RESERVED,   /* 0x0F: FN+HOME (brightness up) */
+               /* Scan codes 0x10 to 0x1F: Extended ACPI HKEY hot keys */
+               KEY_RESERVED,   /* 0x10: FN+END (brightness down) */
+               KEY_RESERVED,   /* 0x11: FN+PGUP (thinklight toggle) */
+               KEY_UNKNOWN,    /* 0x12: FN+PGDOWN */
+               KEY_ZOOM,       /* 0x13: FN+SPACE (zoom) */
+               KEY_RESERVED,   /* 0x14: VOLUME UP */
+               KEY_RESERVED,   /* 0x15: VOLUME DOWN */
+               KEY_RESERVED,   /* 0x16: MUTE */
+               KEY_VENDOR,     /* 0x17: Thinkpad/AccessIBM/Lenovo */
+               /* (assignments unknown, please report if found) */
+               KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
+               KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
+       };
+       static u16 lenovo_keycode_map[] __initdata = {
+               /* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */
+               KEY_FN_F1,      KEY_COFFEE,     KEY_BATTERY,    KEY_SLEEP,
+               KEY_WLAN,       KEY_FN_F6, KEY_SWITCHVIDEOMODE, KEY_FN_F8,
+               KEY_FN_F9,      KEY_FN_F10,     KEY_FN_F11,     KEY_SUSPEND,
+               /* Scan codes 0x0C to 0x0F: Other ACPI HKEY hot keys */
+               KEY_UNKNOWN,    /* 0x0C: FN+BACKSPACE */
+               KEY_UNKNOWN,    /* 0x0D: FN+INSERT */
+               KEY_UNKNOWN,    /* 0x0E: FN+DELETE */
+               KEY_BRIGHTNESSUP,       /* 0x0F: FN+HOME (brightness up) */
+               /* Scan codes 0x10 to 0x1F: Extended ACPI HKEY hot keys */
+               KEY_BRIGHTNESSDOWN,     /* 0x10: FN+END (brightness down) */
+               KEY_RESERVED,   /* 0x11: FN+PGUP (thinklight toggle) */
+               KEY_UNKNOWN,    /* 0x12: FN+PGDOWN */
+               KEY_ZOOM,       /* 0x13: FN+SPACE (zoom) */
+               KEY_RESERVED,   /* 0x14: VOLUME UP */
+               KEY_RESERVED,   /* 0x15: VOLUME DOWN */
+               KEY_RESERVED,   /* 0x16: MUTE */
+               KEY_VENDOR,     /* 0x17: Thinkpad/AccessIBM/Lenovo */
+               /* (assignments unknown, please report if found) */
+               KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
+               KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
+       };
+
+#define TPACPI_HOTKEY_MAP_LEN          ARRAY_SIZE(ibm_keycode_map)
+#define TPACPI_HOTKEY_MAP_SIZE         sizeof(ibm_keycode_map)
+#define TPACPI_HOTKEY_MAP_TYPESIZE     sizeof(ibm_keycode_map[0])
+
        int res, i;
        int status;
 
@@ -1003,6 +1033,27 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
                if (res)
                        return res;
 
+               /* Set up key map */
+
+               hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE,
+                                               GFP_KERNEL);
+               if (!hotkey_keycode_map) {
+                       printk(IBM_ERR "failed to allocate memory for key map\n");
+                       return -ENOMEM;
+               }
+
+               if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) {
+                       dbg_printk(TPACPI_DBG_INIT,
+                                  "using Lenovo default hot key map\n");
+                       memcpy(hotkey_keycode_map, &lenovo_keycode_map,
+                               TPACPI_HOTKEY_MAP_SIZE);
+               } else {
+                       dbg_printk(TPACPI_DBG_INIT,
+                                  "using IBM default hot key map\n");
+                       memcpy(hotkey_keycode_map, &ibm_keycode_map,
+                               TPACPI_HOTKEY_MAP_SIZE);
+               }
+
 #ifndef CONFIG_THINKPAD_ACPI_INPUT_ENABLED
                for (i = 0; i < 12; i++)
                        hotkey_keycode_map[i] = KEY_UNKNOWN;
@@ -1011,10 +1062,10 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
                set_bit(EV_KEY, tpacpi_inputdev->evbit);
                set_bit(EV_MSC, tpacpi_inputdev->evbit);
                set_bit(MSC_SCAN, tpacpi_inputdev->mscbit);
-               tpacpi_inputdev->keycodesize = sizeof(hotkey_keycode_map[0]);
-               tpacpi_inputdev->keycodemax = ARRAY_SIZE(hotkey_keycode_map);
-               tpacpi_inputdev->keycode = &hotkey_keycode_map;
-               for (i = 0; i < ARRAY_SIZE(hotkey_keycode_map); i++) {
+               tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE;
+               tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN;
+               tpacpi_inputdev->keycode = hotkey_keycode_map;
+               for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) {
                        if (hotkey_keycode_map[i] != KEY_RESERVED) {
                                set_bit(hotkey_keycode_map[i],
                                        tpacpi_inputdev->keybit);
@@ -2658,7 +2709,7 @@ static int __init thermal_init(struct ibm_init_struct *iibm)
 
        acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv");
 
-       if (thinkpad_id.ec_model && experimental) {
+       if (thinkpad_id.ec_model) {
                /*
                 * Direct EC access mode: sensors at registers
                 * 0x78-0x7F, 0xC0-0xC7.  Registers return 0x00 for
@@ -2802,6 +2853,8 @@ static int thermal_get_sensor(int idx, s32 *value)
                        snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx);
                        if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
                                return -EIO;
+                       if (t > 127 || t < -127)
+                               t = TP_EC_THERMAL_TMP_NA;
                        *value = t * 1000;
                        return 0;
                }
@@ -2947,15 +3000,32 @@ static struct backlight_ops ibm_backlight_data = {
         .update_status  = brightness_update_status,
 };
 
+static struct mutex brightness_mutex;
+
 static int __init brightness_init(struct ibm_init_struct *iibm)
 {
        int b;
 
        vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n");
 
+       mutex_init(&brightness_mutex);
+
+       if (!brightness_mode) {
+               if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO)
+                       brightness_mode = 2;
+               else
+                       brightness_mode = 3;
+
+               dbg_printk(TPACPI_DBG_INIT, "selected brightness_mode=%d\n",
+                       brightness_mode);
+       }
+
+       if (brightness_mode > 3)
+               return -EINVAL;
+
        b = brightness_get(NULL);
        if (b < 0)
-               return b;
+               return 1;
 
        ibm_backlight_device = backlight_device_register(
                                        TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL,
@@ -2991,34 +3061,79 @@ static int brightness_update_status(struct backlight_device *bd)
                                bd->props.brightness : 0);
 }
 
+/*
+ * ThinkPads can read brightness from two places: EC 0x31, or
+ * CMOS NVRAM byte 0x5E, bits 0-3.
+ */
 static int brightness_get(struct backlight_device *bd)
 {
-       u8 level;
-       if (!acpi_ec_read(brightness_offset, &level))
-               return -EIO;
+       u8 lec = 0, lcmos = 0, level = 0;
+
+       if (brightness_mode & 1) {
+               if (!acpi_ec_read(brightness_offset, &lec))
+                       return -EIO;
+               lec &= 7;
+               level = lec;
+       };
+       if (brightness_mode & 2) {
+               lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS)
+                        & TP_NVRAM_MASK_LEVEL_BRIGHTNESS)
+                       >> TP_NVRAM_POS_LEVEL_BRIGHTNESS;
+               level = lcmos;
+       }
 
-       level &= 0x7;
+       if (brightness_mode == 3 && lec != lcmos) {
+               printk(IBM_ERR
+                       "CMOS NVRAM (%u) and EC (%u) do not agree "
+                       "on display brightness level\n",
+                       (unsigned int) lcmos,
+                       (unsigned int) lec);
+               return -EIO;
+       }
 
        return level;
 }
 
 static int brightness_set(int value)
 {
-       int cmos_cmd, inc, i;
-       int current_value = brightness_get(NULL);
+       int cmos_cmd, inc, i, res;
+       int current_value;
+
+       if (value > 7)
+               return -EINVAL;
 
-       value &= 7;
+       res = mutex_lock_interruptible(&brightness_mutex);
+       if (res < 0)
+               return res;
 
-       cmos_cmd = value > current_value ? TP_CMOS_BRIGHTNESS_UP : TP_CMOS_BRIGHTNESS_DOWN;
+       current_value = brightness_get(NULL);
+       if (current_value < 0) {
+               res = current_value;
+               goto errout;
+       }
+
+       cmos_cmd = value > current_value ?
+                       TP_CMOS_BRIGHTNESS_UP :
+                       TP_CMOS_BRIGHTNESS_DOWN;
        inc = value > current_value ? 1 : -1;
+
+       res = 0;
        for (i = current_value; i != value; i += inc) {
-               if (issue_thinkpad_cmos_command(cmos_cmd))
-                       return -EIO;
-               if (!acpi_ec_write(brightness_offset, i + inc))
-                       return -EIO;
+               if ((brightness_mode & 2) &&
+                   issue_thinkpad_cmos_command(cmos_cmd)) {
+                       res = -EIO;
+                       goto errout;
+               }
+               if ((brightness_mode & 1) &&
+                   !acpi_ec_write(brightness_offset, i + inc)) {
+                       res = -EIO;
+                       goto errout;;
+               }
        }
 
-       return 0;
+errout:
+       mutex_unlock(&brightness_mutex);
+       return res;
 }
 
 static int brightness_read(char *p)
@@ -4485,6 +4600,9 @@ module_param(force_load, bool, 0);
 static int fan_control_allowed;
 module_param_named(fan_control, fan_control_allowed, bool, 0);
 
+static int brightness_mode;
+module_param_named(brightness_mode, brightness_mode, int, 0);
+
 #define IBM_PARAM(feature) \
        module_param_call(feature, set_ibm_param, NULL, NULL, 0)
 
@@ -4574,7 +4692,9 @@ static int __init thinkpad_acpi_module_init(void)
                tpacpi_inputdev->name = "ThinkPad Extra Buttons";
                tpacpi_inputdev->phys = IBM_DRVR_NAME "/input0";
                tpacpi_inputdev->id.bustype = BUS_HOST;
-               tpacpi_inputdev->id.vendor = TPACPI_HKEY_INPUT_VENDOR;
+               tpacpi_inputdev->id.vendor = (thinkpad_id.vendor) ?
+                                               thinkpad_id.vendor :
+                                               PCI_VENDOR_ID_IBM;
                tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT;
                tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION;
        }