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 | |
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>
-rw-r--r-- | Documentation/thinkpad-acpi.txt | 61 | ||||
-rw-r--r-- | drivers/misc/thinkpad_acpi.c | 91 | ||||
-rw-r--r-- | drivers/misc/thinkpad_acpi.h | 3 |
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" | |||
923 | This feature allows software control of the LCD brightness on ThinkPad | 923 | This feature allows software control of the LCD brightness on ThinkPad |
924 | models which don't have a hardware brightness slider. | 924 | models which don't have a hardware brightness slider. |
925 | 925 | ||
926 | It has some limitations: the LCD backlight cannot be actually turned on or off | 926 | It has some limitations: the LCD backlight cannot be actually turned on or |
927 | by this interface, and in many ThinkPad models, the "dim while on battery" | 927 | off by this interface, and in many ThinkPad models, the "dim while on |
928 | functionality will be enabled by the BIOS when this interface is used, and | 928 | battery" functionality will be enabled by the BIOS when this interface is |
929 | cannot be controlled. | 929 | used, and cannot be controlled. |
930 | 930 | ||
931 | The backlight control has eight levels, ranging from 0 to 7. Some of the | 931 | On IBM (and some of the earlier Lenovo) ThinkPads, the backlight control |
932 | levels may not be distinct. | 932 | has eight brightness levels, ranging from 0 to 7. Some of the levels |
933 | 933 | may not be distinct. Later Lenovo models that implement the ACPI | |
934 | There are two interfaces to the firmware for brightness control, EC and CMOS. | 934 | display backlight brightness control methods have 16 levels, ranging |
935 | To select which one should be used, use the brightness_mode module parameter: | 935 | from 0 to 15. |
936 | brightness_mode=1 selects EC mode, brightness_mode=2 selects CMOS mode, | 936 | |
937 | brightness_mode=3 selects both EC and CMOS. The driver tries to autodetect | 937 | There are two interfaces to the firmware for direct brightness control, |
938 | which interface to use. | 938 | EC and CMOS. To select which one should be used, use the |
939 | brightness_mode module parameter: brightness_mode=1 selects EC mode, | ||
940 | brightness_mode=2 selects CMOS mode, brightness_mode=3 selects both EC | ||
941 | and CMOS. The driver tries to autodetect which interface to use. | ||
942 | |||
943 | When display backlight brightness controls are available through the | ||
944 | standard ACPI interface, it is best to use it instead of this direct | ||
945 | ThinkPad-specific interface. | ||
939 | 946 | ||
940 | Procfs notes: | 947 | Procfs notes: |
941 | 948 | ||
@@ -947,11 +954,11 @@ Procfs notes: | |||
947 | 954 | ||
948 | Sysfs notes: | 955 | Sysfs notes: |
949 | 956 | ||
950 | The interface is implemented through the backlight sysfs class, which is poorly | 957 | The interface is implemented through the backlight sysfs class, which is |
951 | documented at this time. | 958 | poorly documented at this time. |
952 | 959 | ||
953 | Locate the thinkpad_screen device under /sys/class/backlight, and inside it | 960 | Locate the thinkpad_screen device under /sys/class/backlight, and inside |
954 | there will be the following attributes: | 961 | it 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 | ||
977 | Volume control -- /proc/acpi/ibm/volume | 986 | Volume 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 | ||
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; |
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; |