aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/thinkpad_acpi.c
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 /drivers/misc/thinkpad_acpi.c
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>
Diffstat (limited to 'drivers/misc/thinkpad_acpi.c')
-rw-r--r--drivers/misc/thinkpad_acpi.c91
1 files changed, 81 insertions, 10 deletions
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;