diff options
-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; |