diff options
author | Henrique de Moraes Holschuh <hmh@hmh.eng.br> | 2007-10-30 15:46:20 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2007-11-05 13:07:11 -0500 |
commit | a3f104c02ab842574e699186cf953551aafe2ca9 (patch) | |
tree | 8cee0bbd37ac14b8a9bb9a88e594a65b424e8ff9 /drivers/misc/thinkpad_acpi.c | |
parent | e927c08da53e5c87ca07f7a828d4a0048e7bacf0 (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.c | 91 |
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 | ||
3115 | static struct mutex brightness_mutex; | 3115 | static struct mutex brightness_mutex; |
3116 | 3116 | ||
3117 | static 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 | |||
3140 | static 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 | |||
3159 | static 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 | |||
3117 | static int __init brightness_init(struct ibm_init_struct *iibm) | 3177 | static 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; |