Pull thinkpad-2.6.24 into release branch
authorLen Brown <len.brown@intel.com>
Tue, 20 Nov 2007 06:20:42 +0000 (01:20 -0500)
committerLen Brown <len.brown@intel.com>
Tue, 20 Nov 2007 06:20:42 +0000 (01:20 -0500)
Documentation/thinkpad-acpi.txt
drivers/misc/thinkpad_acpi.c
drivers/misc/thinkpad_acpi.h

index ec499265decaa45b7c2495e51bdc660f1959dc6f..10c041ca13c70c8f0e8d9c44a1e3118f34e9d793 100644 (file)
@@ -1,7 +1,7 @@
                     ThinkPad ACPI Extras Driver
 
-                            Version 0.16
-                          August 2nd, 2007
+                            Version 0.17
+                         October 04th, 2007
 
                Borislav Deianov <borislav@users.sf.net>
              Henrique de Moraes Holschuh <hmh@hmh.eng.br>
@@ -923,19 +923,34 @@ sysfs backlight device "thinkpad_screen"
 This feature allows software control of the LCD brightness on ThinkPad
 models which don't have a hardware brightness slider.
 
-It has some limitations: the LCD backlight cannot be actually turned on or off
-by this interface, and in many ThinkPad models, the "dim while on battery"
-functionality will be enabled by the BIOS when this interface is used, and
-cannot be controlled.
-
-The backlight control has eight levels, ranging from 0 to 7.  Some of the
-levels may not be distinct.
-
-There are two interfaces to the firmware for brightness control, EC and CMOS.
-To select which one should be used, use the brightness_mode module parameter:
-brightness_mode=1 selects EC mode, brightness_mode=2 selects CMOS mode,
-brightness_mode=3 selects both EC and CMOS.  The driver tries to autodetect
-which interface to use.
+It has some limitations: the LCD backlight cannot be actually turned on or
+off by this interface, and in many ThinkPad models, the "dim while on
+battery" functionality will be enabled by the BIOS when this interface is
+used, and cannot be controlled.
+
+On IBM (and some of the earlier Lenovo) ThinkPads, the backlight control
+has eight brightness levels, ranging from 0 to 7.  Some of the levels
+may not be distinct.  Later Lenovo models that implement the ACPI
+display backlight brightness control methods have 16 levels, ranging
+from 0 to 15.
+
+There are two interfaces to the firmware for direct brightness control,
+EC and CMOS.  To select which one should be used, use the
+brightness_mode module parameter: brightness_mode=1 selects EC mode,
+brightness_mode=2 selects CMOS mode, brightness_mode=3 selects both EC
+and CMOS.  The driver tries to autodetect which interface to use.
+
+When display backlight brightness controls are available through the
+standard ACPI interface, it is best to use it instead of this direct
+ThinkPad-specific interface.  The driver will disable its native
+backlight brightness control interface if it detects that the standard
+ACPI interface is available in the ThinkPad.
+
+The brightness_enable module parameter can be used to control whether
+the LCD brightness control feature will be enabled when available.
+brightness_enable=0 forces it to be disabled.  brightness_enable=1
+forces it to be enabled when available, even if the standard ACPI
+interface is also available.
 
 Procfs notes:
 
@@ -947,11 +962,11 @@ Procfs notes:
 
 Sysfs notes:
 
-The interface is implemented through the backlight sysfs class, which is poorly
-documented at this time.
+The interface is implemented through the backlight sysfs class, which is
+poorly documented at this time.
 
-Locate the thinkpad_screen device under /sys/class/backlight, and inside it
-there will be the following attributes:
+Locate the thinkpad_screen device under /sys/class/backlight, and inside
+it there will be the following attributes:
 
        max_brightness:
                Reads the maximum brightness the hardware can be set to.
@@ -961,17 +976,19 @@ there will be the following attributes:
                Reads what brightness the screen is set to at this instant.
 
        brightness:
-               Writes request the driver to change brightness to the given
-               value.  Reads will tell you what brightness the driver is trying
-               to set the display to when "power" is set to zero and the display
-               has not been dimmed by a kernel power management event.
+               Writes request the driver to change brightness to the
+               given value.  Reads will tell you what brightness the
+               driver is trying to set the display to when "power" is set
+               to zero and the display has not been dimmed by a kernel
+               power management event.
 
        power:
-               power management mode, where 0 is "display on", and 1 to 3 will
-               dim the display backlight to brightness level 0 because
-               thinkpad-acpi cannot really turn the backlight off.  Kernel
-               power management events can temporarily increase the current
-               power management level, i.e. they can dim the display.
+               power management mode, where 0 is "display on", and 1 to 3
+               will dim the display backlight to brightness level 0
+               because thinkpad-acpi cannot really turn the backlight
+               off.  Kernel power management events can temporarily
+               increase the current power management level, i.e. they can
+               dim the display.
 
 
 Volume control -- /proc/acpi/ibm/volume
index e953276664a0f77aef6a0a997452488d75a294c3..ab23a3221585302fec8a33d79ceade142b93f7dc 100644 (file)
@@ -21,7 +21,7 @@
  *  02110-1301, USA.
  */
 
-#define IBM_VERSION "0.16"
+#define IBM_VERSION "0.17"
 #define TPACPI_SYSFS_VERSION 0x020000
 
 /*
@@ -964,15 +964,15 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
                KEY_UNKNOWN,    /* 0x0C: FN+BACKSPACE */
                KEY_UNKNOWN,    /* 0x0D: FN+INSERT */
                KEY_UNKNOWN,    /* 0x0E: FN+DELETE */
-               KEY_BRIGHTNESSUP,       /* 0x0F: FN+HOME (brightness up) */
+               KEY_RESERVED,   /* 0x0F: FN+HOME (brightness up) */
                /* Scan codes 0x10 to 0x1F: Extended ACPI HKEY hot keys */
-               KEY_BRIGHTNESSDOWN,     /* 0x10: FN+END (brightness down) */
+               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_VOLUMEUP,   /* 0x14: VOLUME UP */
-               KEY_VOLUMEDOWN, /* 0x15: VOLUME DOWN */
-               KEY_MUTE,       /* 0x16: MUTE */
+               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,
@@ -993,9 +993,9 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
                KEY_RESERVED,   /* 0x11: FN+PGUP (thinklight toggle) */
                KEY_UNKNOWN,    /* 0x12: FN+PGDOWN */
                KEY_ZOOM,       /* 0x13: FN+SPACE (zoom) */
-               KEY_VOLUMEUP,   /* 0x14: VOLUME UP */
-               KEY_VOLUMEDOWN, /* 0x15: VOLUME DOWN */
-               KEY_MUTE,       /* 0x16: MUTE */
+               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,
@@ -1342,9 +1342,8 @@ static int hotkey_read(char *p)
                return len;
        }
 
-       res = mutex_lock_interruptible(&hotkey_mutex);
-       if (res < 0)
-               return res;
+       if (mutex_lock_interruptible(&hotkey_mutex))
+               return -ERESTARTSYS;
        res = hotkey_get(&status, &mask);
        mutex_unlock(&hotkey_mutex);
        if (res)
@@ -1373,9 +1372,8 @@ static int hotkey_write(char *buf)
        if (!tp_features.hotkey)
                return -ENODEV;
 
-       res = mutex_lock_interruptible(&hotkey_mutex);
-       if (res < 0)
-               return res;
+       if (mutex_lock_interruptible(&hotkey_mutex))
+               return -ERESTARTSYS;
 
        res = hotkey_get(&status, &mask);
        if (res)
@@ -3114,6 +3112,99 @@ static struct backlight_ops ibm_backlight_data = {
 
 static struct mutex brightness_mutex;
 
+static int __init tpacpi_query_bcll_levels(acpi_handle handle)
+{
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object *obj;
+       int rc;
+
+       if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) {
+               obj = (union acpi_object *)buffer.pointer;
+               if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
+                       printk(IBM_ERR "Unknown BCLL data, "
+                              "please report this to %s\n", IBM_MAIL);
+                       rc = 0;
+               } else {
+                       rc = obj->package.count;
+               }
+       } else {
+               return 0;
+       }
+
+       kfree(buffer.pointer);
+       return rc;
+}
+
+static acpi_status __init brightness_find_bcll(acpi_handle handle, u32 lvl,
+                                       void *context, void **rv)
+{
+       char name[ACPI_PATH_SEGMENT_LENGTH];
+       struct acpi_buffer buffer = { sizeof(name), &name };
+
+       if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) &&
+           !strncmp("BCLL", name, sizeof(name) - 1)) {
+               if (tpacpi_query_bcll_levels(handle) == 16) {
+                       *rv = handle;
+                       return AE_CTRL_TERMINATE;
+               } else {
+                       return AE_OK;
+               }
+       } else {
+               return AE_OK;
+       }
+}
+
+static int __init brightness_check_levels(void)
+{
+       int status;
+       void *found_node = NULL;
+
+       if (!vid_handle) {
+               IBM_ACPIHANDLE_INIT(vid);
+       }
+       if (!vid_handle)
+               return 0;
+
+       /* Search for a BCLL package with 16 levels */
+       status = acpi_walk_namespace(ACPI_TYPE_PACKAGE, vid_handle, 3,
+                                       brightness_find_bcll, NULL, &found_node);
+
+       return (ACPI_SUCCESS(status) && found_node != NULL);
+}
+
+static acpi_status __init brightness_find_bcl(acpi_handle handle, u32 lvl,
+                                       void *context, void **rv)
+{
+       char name[ACPI_PATH_SEGMENT_LENGTH];
+       struct acpi_buffer buffer = { sizeof(name), &name };
+
+       if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) &&
+           !strncmp("_BCL", name, sizeof(name) - 1)) {
+               *rv = handle;
+               return AE_CTRL_TERMINATE;
+       } else {
+               return AE_OK;
+       }
+}
+
+static int __init brightness_check_std_acpi_support(void)
+{
+       int status;
+       void *found_node = NULL;
+
+       if (!vid_handle) {
+               IBM_ACPIHANDLE_INIT(vid);
+       }
+       if (!vid_handle)
+               return 0;
+
+       /* Search for a _BCL method, but don't execute it */
+       status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3,
+                                    brightness_find_bcl, NULL, &found_node);
+
+       return (ACPI_SUCCESS(status) && found_node != NULL);
+}
+
 static int __init brightness_init(struct ibm_init_struct *iibm)
 {
        int b;
@@ -3122,6 +3213,18 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
 
        mutex_init(&brightness_mutex);
 
+       if (!brightness_enable) {
+               dbg_printk(TPACPI_DBG_INIT,
+                          "brightness support disabled by module parameter\n");
+               return 1;
+       } else if (brightness_enable > 1) {
+               if (brightness_check_std_acpi_support()) {
+                       printk(IBM_NOTICE
+                              "standard ACPI backlight interface available, not loading native one...\n");
+                       return 1;
+               }
+       }
+
        if (!brightness_mode) {
                if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO)
                        brightness_mode = 2;
@@ -3135,10 +3238,17 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
        if (brightness_mode > 3)
                return -EINVAL;
 
+       tp_features.bright_16levels =
+                       thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO &&
+                       brightness_check_levels();
+
        b = brightness_get(NULL);
        if (b < 0)
                return 1;
 
+       if (tp_features.bright_16levels)
+               printk(IBM_INFO "detected a 16-level brightness capable ThinkPad\n");
+
        ibm_backlight_device = backlight_device_register(
                                        TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL,
                                        &ibm_backlight_data);
@@ -3148,7 +3258,8 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
        }
        vdbg_printk(TPACPI_DBG_INIT, "brightness is supported\n");
 
-       ibm_backlight_device->props.max_brightness = 7;
+       ibm_backlight_device->props.max_brightness =
+                               (tp_features.bright_16levels)? 15 : 7;
        ibm_backlight_device->props.brightness = b;
        backlight_update_status(ibm_backlight_device);
 
@@ -3167,6 +3278,8 @@ static void brightness_exit(void)
 
 static int brightness_update_status(struct backlight_device *bd)
 {
+       /* it is the backlight class's job (caller) to handle
+        * EINTR and other errors properly */
        return brightness_set(
                (bd->props.fb_blank == FB_BLANK_UNBLANK &&
                 bd->props.power == FB_BLANK_UNBLANK) ?
@@ -3184,13 +3297,14 @@ static int brightness_get(struct backlight_device *bd)
        if (brightness_mode & 1) {
                if (!acpi_ec_read(brightness_offset, &lec))
                        return -EIO;
-               lec &= 7;
+               lec &= (tp_features.bright_16levels)? 0x0f : 0x07;
                level = lec;
        };
        if (brightness_mode & 2) {
                lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS)
                         & TP_NVRAM_MASK_LEVEL_BRIGHTNESS)
                        >> TP_NVRAM_POS_LEVEL_BRIGHTNESS;
+               lcmos &= (tp_features.bright_16levels)? 0x0f : 0x07;
                level = lcmos;
        }
 
@@ -3206,12 +3320,13 @@ static int brightness_get(struct backlight_device *bd)
        return level;
 }
 
+/* May return EINTR which can always be mapped to ERESTARTSYS */
 static int brightness_set(int value)
 {
        int cmos_cmd, inc, i, res;
        int current_value;
 
-       if (value > 7)
+       if (value > ((tp_features.bright_16levels)? 15 : 7))
                return -EINVAL;
 
        res = mutex_lock_interruptible(&brightness_mutex);
@@ -3227,7 +3342,7 @@ static int brightness_set(int value)
        cmos_cmd = value > current_value ?
                        TP_CMOS_BRIGHTNESS_UP :
                        TP_CMOS_BRIGHTNESS_DOWN;
-       inc = value > current_value ? 1 : -1;
+       inc = (value > current_value)? 1 : -1;
 
        res = 0;
        for (i = current_value; i != value; i += inc) {
@@ -3256,10 +3371,11 @@ static int brightness_read(char *p)
        if ((level = brightness_get(NULL)) < 0) {
                len += sprintf(p + len, "level:\t\tunreadable\n");
        } else {
-               len += sprintf(p + len, "level:\t\t%d\n", level & 0x7);
+               len += sprintf(p + len, "level:\t\t%d\n", level);
                len += sprintf(p + len, "commands:\tup, down\n");
                len += sprintf(p + len, "commands:\tlevel <level>"
-                              " (<level> is 0-7)\n");
+                              " (<level> is 0-%d)\n",
+                              (tp_features.bright_16levels) ? 15 : 7);
        }
 
        return len;
@@ -3268,28 +3384,34 @@ static int brightness_read(char *p)
 static int brightness_write(char *buf)
 {
        int level;
-       int new_level;
+       int rc;
        char *cmd;
+       int max_level = (tp_features.bright_16levels) ? 15 : 7;
 
-       while ((cmd = next_cmd(&buf))) {
-               if ((level = brightness_get(NULL)) < 0)
-                       return level;
-               level &= 7;
+       level = brightness_get(NULL);
+       if (level < 0)
+               return level;
 
+       while ((cmd = next_cmd(&buf))) {
                if (strlencmp(cmd, "up") == 0) {
-                       new_level = level == 7 ? 7 : level + 1;
+                       if (level < max_level)
+                               level++;
                } else if (strlencmp(cmd, "down") == 0) {
-                       new_level = level == 0 ? 0 : level - 1;
-               } else if (sscanf(cmd, "level %d", &new_level) == 1 &&
-                          new_level >= 0 && new_level <= 7) {
-                       /* new_level set */
+                       if (level > 0)
+                               level--;
+               } else if (sscanf(cmd, "level %d", &level) == 1 &&
+                          level >= 0 && level <= max_level) {
+                       /* new level set */
                } else
                        return -EINVAL;
-
-               brightness_set(new_level);
        }
 
-       return 0;
+       /*
+        * Now we know what the final level should be, so we try to set it.
+        * Doing it this way makes the syscall restartable in case of EINTR
+        */
+       rc = brightness_set(level);
+       return (rc == -EINTR)? ERESTARTSYS : rc;
 }
 
 static struct ibm_struct brightness_driver_data = {
@@ -3652,9 +3774,8 @@ static ssize_t fan_pwm1_store(struct device *dev,
        /* scale down from 0-255 to 0-7 */
        newlevel = (s >> 5) & 0x07;
 
-       rc = mutex_lock_interruptible(&fan_mutex);
-       if (rc < 0)
-               return rc;
+       if (mutex_lock_interruptible(&fan_mutex))
+               return -ERESTARTSYS;
 
        rc = fan_get_status(&status);
        if (!rc && (status &
@@ -3904,9 +4025,8 @@ static int fan_get_status_safe(u8 *status)
        int rc;
        u8 s;
 
-       rc = mutex_lock_interruptible(&fan_mutex);
-       if (rc < 0)
-               return rc;
+       if (mutex_lock_interruptible(&fan_mutex))
+               return -ERESTARTSYS;
        rc = fan_get_status(&s);
        if (!rc)
                fan_update_desired_level(s);
@@ -4040,9 +4160,8 @@ static int fan_set_level_safe(int level)
        if (!fan_control_allowed)
                return -EPERM;
 
-       rc = mutex_lock_interruptible(&fan_mutex);
-       if (rc < 0)
-               return rc;
+       if (mutex_lock_interruptible(&fan_mutex))
+               return -ERESTARTSYS;
 
        if (level == TPACPI_FAN_LAST_LEVEL)
                level = fan_control_desired_level;
@@ -4063,9 +4182,8 @@ static int fan_set_enable(void)
        if (!fan_control_allowed)
                return -EPERM;
 
-       rc = mutex_lock_interruptible(&fan_mutex);
-       if (rc < 0)
-               return rc;
+       if (mutex_lock_interruptible(&fan_mutex))
+               return -ERESTARTSYS;
 
        switch (fan_control_access_mode) {
        case TPACPI_FAN_WR_ACPI_FANS:
@@ -4119,9 +4237,8 @@ static int fan_set_disable(void)
        if (!fan_control_allowed)
                return -EPERM;
 
-       rc = mutex_lock_interruptible(&fan_mutex);
-       if (rc < 0)
-               return rc;
+       if (mutex_lock_interruptible(&fan_mutex))
+               return -ERESTARTSYS;
 
        rc = 0;
        switch (fan_control_access_mode) {
@@ -4158,9 +4275,8 @@ static int fan_set_speed(int speed)
        if (!fan_control_allowed)
                return -EPERM;
 
-       rc = mutex_lock_interruptible(&fan_mutex);
-       if (rc < 0)
-               return rc;
+       if (mutex_lock_interruptible(&fan_mutex))
+               return -ERESTARTSYS;
 
        rc = 0;
        switch (fan_control_access_mode) {
@@ -4701,9 +4817,15 @@ static int __init set_ibm_param(const char *val, struct kernel_param *kp)
        unsigned int i;
        struct ibm_struct *ibm;
 
+       if (!kp || !kp->name || !val)
+               return -EINVAL;
+
        for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
                ibm = ibms_init[i].data;
-               BUG_ON(ibm == NULL);
+               WARN_ON(ibm == NULL);
+
+               if (!ibm || !ibm->name)
+                       continue;
 
                if (strcmp(ibm->name, kp->name) == 0 && ibm->write) {
                        if (strlen(val) > sizeof(ibms_init[i].param) - 2)
@@ -4732,6 +4854,9 @@ module_param_named(fan_control, fan_control_allowed, bool, 0);
 static int brightness_mode;
 module_param_named(brightness_mode, brightness_mode, int, 0);
 
+static unsigned int brightness_enable = 2; /* 2 = auto, 0 = no, 1 = yes */
+module_param(brightness_enable, uint, 0);
+
 static unsigned int hotkey_report_mode;
 module_param(hotkey_report_mode, uint, 0);
 
index 3abcc812063433e664a23df7d5275918d6906351..8fba2bbe345ee3ad46808d7e522058d2983e8a97 100644 (file)
@@ -84,7 +84,7 @@
 
 /* ThinkPad CMOS NVRAM constants */
 #define TP_NVRAM_ADDR_BRIGHTNESS       0x5e
-#define TP_NVRAM_MASK_LEVEL_BRIGHTNESS 0x07
+#define TP_NVRAM_MASK_LEVEL_BRIGHTNESS 0x0f
 #define TP_NVRAM_POS_LEVEL_BRIGHTNESS 0
 
 #define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off")
@@ -246,6 +246,7 @@ static struct {
        u32 hotkey_wlsw:1;
        u32 light:1;
        u32 light_status:1;
+       u32 bright_16levels:1;
        u32 wan:1;
        u32 fan_ctrl_status_undef:1;
        u32 input_device_registered:1;
@@ -338,6 +339,7 @@ static int bluetooth_write(char *buf);
 static struct backlight_device *ibm_backlight_device;
 static int brightness_offset = 0x31;
 static int brightness_mode;
+static unsigned int brightness_enable; /* 0 = no, 1 = yes, 2 = auto */
 
 static int brightness_init(struct ibm_init_struct *iibm);
 static void brightness_exit(void);