aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHenrique de Moraes Holschuh <hmh@hmh.eng.br>2007-10-30 15:46:20 -0400
committerLen Brown <len.brown@intel.com>2007-11-05 13:07:11 -0500
commita3f104c02ab842574e699186cf953551aafe2ca9 (patch)
tree8cee0bbd37ac14b8a9bb9a88e594a65b424e8ff9
parente927c08da53e5c87ca07f7a828d4a0048e7bacf0 (diff)
ACPI: thinkpad-acpi: support 16 levels of brightness (v3)
Lenovo ThinkPads often have 16 brightness levels in EC, and not just eight levels like older ThinkPads. They also have standard ACPI backlight brightness control. We detect the number of brightness levels by the presence of a BCLL package with 16 entries. If BCLL is not there, we assume eight levels (Z6*). If it is there, but it doesn't have 16 entries, we assume eight levels (T60). Otherwise we assume sixteen levels (T61, X61, etc). We don't use _BCL because it can have side-effects in thinkpads. Thanks to Thomas Renninger <trenn@suse.de> for notifying me of this potential problem. Using the standard ACPI backlight brightness control *instead* of the native thinkpad backlight control is a better idea, though. A different patch will take care of this. Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br> Cc: Thomas Renninger <trenn@suse.de> Signed-off-by: Len Brown <len.brown@intel.com>
-rw-r--r--Documentation/thinkpad-acpi.txt61
-rw-r--r--drivers/misc/thinkpad_acpi.c91
-rw-r--r--drivers/misc/thinkpad_acpi.h3
3 files changed, 118 insertions, 37 deletions
diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt
index ec499265deca..a743bfa62b95 100644
--- a/Documentation/thinkpad-acpi.txt
+++ b/Documentation/thinkpad-acpi.txt
@@ -923,19 +923,26 @@ sysfs backlight device "thinkpad_screen"
923This feature allows software control of the LCD brightness on ThinkPad 923This feature allows software control of the LCD brightness on ThinkPad
924models which don't have a hardware brightness slider. 924models which don't have a hardware brightness slider.
925 925
926It has some limitations: the LCD backlight cannot be actually turned on or off 926It has some limitations: the LCD backlight cannot be actually turned on or
927by this interface, and in many ThinkPad models, the "dim while on battery" 927off by this interface, and in many ThinkPad models, the "dim while on
928functionality will be enabled by the BIOS when this interface is used, and 928battery" functionality will be enabled by the BIOS when this interface is
929cannot be controlled. 929used, and cannot be controlled.
930 930
931The backlight control has eight levels, ranging from 0 to 7. Some of the 931On IBM (and some of the earlier Lenovo) ThinkPads, the backlight control
932levels may not be distinct. 932has eight brightness levels, ranging from 0 to 7. Some of the levels
933 933may not be distinct. Later Lenovo models that implement the ACPI
934There are two interfaces to the firmware for brightness control, EC and CMOS. 934display backlight brightness control methods have 16 levels, ranging
935To select which one should be used, use the brightness_mode module parameter: 935from 0 to 15.
936brightness_mode=1 selects EC mode, brightness_mode=2 selects CMOS mode, 936
937brightness_mode=3 selects both EC and CMOS. The driver tries to autodetect 937There are two interfaces to the firmware for direct brightness control,
938which interface to use. 938EC and CMOS. To select which one should be used, use the
939brightness_mode module parameter: brightness_mode=1 selects EC mode,
940brightness_mode=2 selects CMOS mode, brightness_mode=3 selects both EC
941and CMOS. The driver tries to autodetect which interface to use.
942
943When display backlight brightness controls are available through the
944standard ACPI interface, it is best to use it instead of this direct
945ThinkPad-specific interface.
939 946
940Procfs notes: 947Procfs notes:
941 948
@@ -947,11 +954,11 @@ Procfs notes:
947 954
948Sysfs notes: 955Sysfs notes:
949 956
950The interface is implemented through the backlight sysfs class, which is poorly 957The interface is implemented through the backlight sysfs class, which is
951documented at this time. 958poorly documented at this time.
952 959
953Locate the thinkpad_screen device under /sys/class/backlight, and inside it 960Locate the thinkpad_screen device under /sys/class/backlight, and inside
954there will be the following attributes: 961it there will be the following attributes:
955 962
956 max_brightness: 963 max_brightness:
957 Reads the maximum brightness the hardware can be set to. 964 Reads the maximum brightness the hardware can be set to.
@@ -961,17 +968,19 @@ there will be the following attributes:
961 Reads what brightness the screen is set to at this instant. 968 Reads what brightness the screen is set to at this instant.
962 969
963 brightness: 970 brightness:
964 Writes request the driver to change brightness to the given 971 Writes request the driver to change brightness to the
965 value. Reads will tell you what brightness the driver is trying 972 given value. Reads will tell you what brightness the
966 to set the display to when "power" is set to zero and the display 973 driver is trying to set the display to when "power" is set
967 has not been dimmed by a kernel power management event. 974 to zero and the display has not been dimmed by a kernel
975 power management event.
968 976
969 power: 977 power:
970 power management mode, where 0 is "display on", and 1 to 3 will 978 power management mode, where 0 is "display on", and 1 to 3
971 dim the display backlight to brightness level 0 because 979 will dim the display backlight to brightness level 0
972 thinkpad-acpi cannot really turn the backlight off. Kernel 980 because thinkpad-acpi cannot really turn the backlight
973 power management events can temporarily increase the current 981 off. Kernel power management events can temporarily
974 power management level, i.e. they can dim the display. 982 increase the current power management level, i.e. they can
983 dim the display.
975 984
976 985
977Volume control -- /proc/acpi/ibm/volume 986Volume control -- /proc/acpi/ibm/volume
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index 63b1e2610c60..322ba25b4798 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -3114,6 +3114,66 @@ static struct backlight_ops ibm_backlight_data = {
3114 3114
3115static struct mutex brightness_mutex; 3115static struct mutex brightness_mutex;
3116 3116
3117static int __init tpacpi_query_bcll_levels(acpi_handle handle)
3118{
3119 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
3120 union acpi_object *obj;
3121 int rc;
3122
3123 if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) {
3124 obj = (union acpi_object *)buffer.pointer;
3125 if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
3126 printk(IBM_ERR "Unknown BCLL data, "
3127 "please report this to %s\n", IBM_MAIL);
3128 rc = 0;
3129 } else {
3130 rc = obj->package.count;
3131 }
3132 } else {
3133 return 0;
3134 }
3135
3136 kfree(buffer.pointer);
3137 return rc;
3138}
3139
3140static acpi_status __init brightness_find_bcll(acpi_handle handle, u32 lvl,
3141 void *context, void **rv)
3142{
3143 char name[ACPI_PATH_SEGMENT_LENGTH];
3144 struct acpi_buffer buffer = { sizeof(name), &name };
3145
3146 if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) &&
3147 !strncmp("BCLL", name, sizeof(name) - 1)) {
3148 if (tpacpi_query_bcll_levels(handle) == 16) {
3149 *rv = handle;
3150 return AE_CTRL_TERMINATE;
3151 } else {
3152 return AE_OK;
3153 }
3154 } else {
3155 return AE_OK;
3156 }
3157}
3158
3159static int __init brightness_check_levels(void)
3160{
3161 int status;
3162 void *found_node = NULL;
3163
3164 if (!vid_handle) {
3165 IBM_ACPIHANDLE_INIT(vid);
3166 }
3167 if (!vid_handle)
3168 return 0;
3169
3170 /* Search for a BCLL package with 16 levels */
3171 status = acpi_walk_namespace(ACPI_TYPE_PACKAGE, vid_handle, 3,
3172 brightness_find_bcll, NULL, &found_node);
3173
3174 return (ACPI_SUCCESS(status) && found_node != NULL);
3175}
3176
3117static int __init brightness_init(struct ibm_init_struct *iibm) 3177static int __init brightness_init(struct ibm_init_struct *iibm)
3118{ 3178{
3119 int b; 3179 int b;
@@ -3135,10 +3195,17 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
3135 if (brightness_mode > 3) 3195 if (brightness_mode > 3)
3136 return -EINVAL; 3196 return -EINVAL;
3137 3197
3198 tp_features.bright_16levels =
3199 thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO &&
3200 brightness_check_levels();
3201
3138 b = brightness_get(NULL); 3202 b = brightness_get(NULL);
3139 if (b < 0) 3203 if (b < 0)
3140 return 1; 3204 return 1;
3141 3205
3206 if (tp_features.bright_16levels)
3207 printk(IBM_INFO "detected a 16-level brightness capable ThinkPad\n");
3208
3142 ibm_backlight_device = backlight_device_register( 3209 ibm_backlight_device = backlight_device_register(
3143 TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL, 3210 TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL,
3144 &ibm_backlight_data); 3211 &ibm_backlight_data);
@@ -3148,7 +3215,8 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
3148 } 3215 }
3149 vdbg_printk(TPACPI_DBG_INIT, "brightness is supported\n"); 3216 vdbg_printk(TPACPI_DBG_INIT, "brightness is supported\n");
3150 3217
3151 ibm_backlight_device->props.max_brightness = 7; 3218 ibm_backlight_device->props.max_brightness =
3219 (tp_features.bright_16levels)? 15 : 7;
3152 ibm_backlight_device->props.brightness = b; 3220 ibm_backlight_device->props.brightness = b;
3153 backlight_update_status(ibm_backlight_device); 3221 backlight_update_status(ibm_backlight_device);
3154 3222
@@ -3184,13 +3252,14 @@ static int brightness_get(struct backlight_device *bd)
3184 if (brightness_mode & 1) { 3252 if (brightness_mode & 1) {
3185 if (!acpi_ec_read(brightness_offset, &lec)) 3253 if (!acpi_ec_read(brightness_offset, &lec))
3186 return -EIO; 3254 return -EIO;
3187 lec &= 7; 3255 lec &= (tp_features.bright_16levels)? 0x0f : 0x07;
3188 level = lec; 3256 level = lec;
3189 }; 3257 };
3190 if (brightness_mode & 2) { 3258 if (brightness_mode & 2) {
3191 lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS) 3259 lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS)
3192 & TP_NVRAM_MASK_LEVEL_BRIGHTNESS) 3260 & TP_NVRAM_MASK_LEVEL_BRIGHTNESS)
3193 >> TP_NVRAM_POS_LEVEL_BRIGHTNESS; 3261 >> TP_NVRAM_POS_LEVEL_BRIGHTNESS;
3262 lcmos &= (tp_features.bright_16levels)? 0x0f : 0x07;
3194 level = lcmos; 3263 level = lcmos;
3195 } 3264 }
3196 3265
@@ -3211,7 +3280,7 @@ static int brightness_set(int value)
3211 int cmos_cmd, inc, i, res; 3280 int cmos_cmd, inc, i, res;
3212 int current_value; 3281 int current_value;
3213 3282
3214 if (value > 7) 3283 if (value > ((tp_features.bright_16levels)? 15 : 7))
3215 return -EINVAL; 3284 return -EINVAL;
3216 3285
3217 res = mutex_lock_interruptible(&brightness_mutex); 3286 res = mutex_lock_interruptible(&brightness_mutex);
@@ -3227,7 +3296,7 @@ static int brightness_set(int value)
3227 cmos_cmd = value > current_value ? 3296 cmos_cmd = value > current_value ?
3228 TP_CMOS_BRIGHTNESS_UP : 3297 TP_CMOS_BRIGHTNESS_UP :
3229 TP_CMOS_BRIGHTNESS_DOWN; 3298 TP_CMOS_BRIGHTNESS_DOWN;
3230 inc = value > current_value ? 1 : -1; 3299 inc = (value > current_value)? 1 : -1;
3231 3300
3232 res = 0; 3301 res = 0;
3233 for (i = current_value; i != value; i += inc) { 3302 for (i = current_value; i != value; i += inc) {
@@ -3256,10 +3325,11 @@ static int brightness_read(char *p)
3256 if ((level = brightness_get(NULL)) < 0) { 3325 if ((level = brightness_get(NULL)) < 0) {
3257 len += sprintf(p + len, "level:\t\tunreadable\n"); 3326 len += sprintf(p + len, "level:\t\tunreadable\n");
3258 } else { 3327 } else {
3259 len += sprintf(p + len, "level:\t\t%d\n", level & 0x7); 3328 len += sprintf(p + len, "level:\t\t%d\n", level);
3260 len += sprintf(p + len, "commands:\tup, down\n"); 3329 len += sprintf(p + len, "commands:\tup, down\n");
3261 len += sprintf(p + len, "commands:\tlevel <level>" 3330 len += sprintf(p + len, "commands:\tlevel <level>"
3262 " (<level> is 0-7)\n"); 3331 " (<level> is 0-%d)\n",
3332 (tp_features.bright_16levels) ? 15 : 7);
3263 } 3333 }
3264 3334
3265 return len; 3335 return len;
@@ -3270,18 +3340,19 @@ static int brightness_write(char *buf)
3270 int level; 3340 int level;
3271 int new_level; 3341 int new_level;
3272 char *cmd; 3342 char *cmd;
3343 int max_level = (tp_features.bright_16levels) ? 15 : 7;
3273 3344
3274 while ((cmd = next_cmd(&buf))) { 3345 while ((cmd = next_cmd(&buf))) {
3275 if ((level = brightness_get(NULL)) < 0) 3346 if ((level = brightness_get(NULL)) < 0)
3276 return level; 3347 return level;
3277 level &= 7;
3278 3348
3279 if (strlencmp(cmd, "up") == 0) { 3349 if (strlencmp(cmd, "up") == 0) {
3280 new_level = level == 7 ? 7 : level + 1; 3350 new_level = level == (max_level)?
3351 max_level : level + 1;
3281 } else if (strlencmp(cmd, "down") == 0) { 3352 } else if (strlencmp(cmd, "down") == 0) {
3282 new_level = level == 0 ? 0 : level - 1; 3353 new_level = level == 0? 0 : level - 1;
3283 } else if (sscanf(cmd, "level %d", &new_level) == 1 && 3354 } else if (sscanf(cmd, "level %d", &new_level) == 1 &&
3284 new_level >= 0 && new_level <= 7) { 3355 new_level >= 0 && new_level <= max_level) {
3285 /* new_level set */ 3356 /* new_level set */
3286 } else 3357 } else
3287 return -EINVAL; 3358 return -EINVAL;
diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h
index 3abcc8120634..8ca19c333727 100644
--- a/drivers/misc/thinkpad_acpi.h
+++ b/drivers/misc/thinkpad_acpi.h
@@ -84,7 +84,7 @@
84 84
85/* ThinkPad CMOS NVRAM constants */ 85/* ThinkPad CMOS NVRAM constants */
86#define TP_NVRAM_ADDR_BRIGHTNESS 0x5e 86#define TP_NVRAM_ADDR_BRIGHTNESS 0x5e
87#define TP_NVRAM_MASK_LEVEL_BRIGHTNESS 0x07 87#define TP_NVRAM_MASK_LEVEL_BRIGHTNESS 0x0f
88#define TP_NVRAM_POS_LEVEL_BRIGHTNESS 0 88#define TP_NVRAM_POS_LEVEL_BRIGHTNESS 0
89 89
90#define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off") 90#define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off")
@@ -246,6 +246,7 @@ static struct {
246 u32 hotkey_wlsw:1; 246 u32 hotkey_wlsw:1;
247 u32 light:1; 247 u32 light:1;
248 u32 light_status:1; 248 u32 light_status:1;
249 u32 bright_16levels:1;
249 u32 wan:1; 250 u32 wan:1;
250 u32 fan_ctrl_status_undef:1; 251 u32 fan_ctrl_status_undef:1;
251 u32 input_device_registered:1; 252 u32 input_device_registered:1;