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.
 
 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.
                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
 
 
 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 int __init brightness_init(struct ibm_init_struct *iibm)
 {
        int b;
        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);
        }
        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);
 
        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;
        }
 
        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);
        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) {
        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;
        int level;
        int new_level;
        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;
 
                if (strlencmp(cmd, "up") == 0) {
-                       new_level = level == 7 ? 7 : level + 1;
+                       new_level = level == (max_level)?
+                                               max_level : level + 1;
                } else if (strlencmp(cmd, "down") == 0) {
-                       new_level = level == 0 ? 0 : level - 1;
+                       new_level = level == 0? 0 : level - 1;
                } else if (sscanf(cmd, "level %d", &new_level) == 1 &&
-                          new_level >= 0 && new_level <= 7) {
+                          new_level >= 0 && new_level <= max_level) {
                        /* new_level set */
                } else
                        return -EINVAL;