diff options
-rw-r--r-- | Documentation/laptops/thinkpad-acpi.txt | 50 | ||||
-rw-r--r-- | drivers/misc/thinkpad_acpi.c | 238 |
2 files changed, 183 insertions, 105 deletions
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index 76cb428435da..a77da28a6f8e 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt | |||
@@ -571,6 +571,47 @@ netlink interface and the input layer interface, and don't bother at all | |||
571 | with hotkey_report_mode. | 571 | with hotkey_report_mode. |
572 | 572 | ||
573 | 573 | ||
574 | Brightness hotkey notes: | ||
575 | |||
576 | These are the current sane choices for brightness key mapping in | ||
577 | thinkpad-acpi: | ||
578 | |||
579 | For IBM and Lenovo models *without* ACPI backlight control (the ones on | ||
580 | which thinkpad-acpi will autoload its backlight interface by default, | ||
581 | and on which ACPI video does not export a backlight interface): | ||
582 | |||
583 | 1. Don't enable or map the brightness hotkeys in thinkpad-acpi, as | ||
584 | these older firmware versions unfortunately won't respect the hotkey | ||
585 | mask for brightness keys anyway, and always reacts to them. This | ||
586 | usually work fine, unless X.org drivers are doing something to block | ||
587 | the BIOS. In that case, use (3) below. This is the default mode of | ||
588 | operation. | ||
589 | |||
590 | 2. Enable the hotkeys, but map them to something else that is NOT | ||
591 | KEY_BRIGHTNESS_UP/DOWN or any other keycode that would cause | ||
592 | userspace to try to change the backlight level, and use that as an | ||
593 | on-screen-display hint. | ||
594 | |||
595 | 3. IF AND ONLY IF X.org drivers find a way to block the firmware from | ||
596 | automatically changing the brightness, enable the hotkeys and map | ||
597 | them to KEY_BRIGHTNESS_UP and KEY_BRIGHTNESS_DOWN, and feed that to | ||
598 | something that calls xbacklight. thinkpad-acpi will not be able to | ||
599 | change brightness in that case either, so you should disable its | ||
600 | backlight interface. | ||
601 | |||
602 | For Lenovo models *with* ACPI backlight control: | ||
603 | |||
604 | 1. Load up ACPI video and use that. ACPI video will report ACPI | ||
605 | events for brightness change keys. Do not mess with thinkpad-acpi | ||
606 | defaults in this case. thinkpad-acpi should not have anything to do | ||
607 | with backlight events in a scenario where ACPI video is loaded: | ||
608 | brightness hotkeys must be disabled, and the backlight interface is | ||
609 | to be kept disabled as well. This is the default mode of operation. | ||
610 | |||
611 | 2. Do *NOT* load up ACPI video, enable the hotkeys in thinkpad-acpi, | ||
612 | and map them to KEY_BRIGHTNESS_UP and KEY_BRIGHTNESS_DOWN. Process | ||
613 | these keys on userspace somehow (e.g. by calling xbacklight). | ||
614 | |||
574 | Bluetooth | 615 | Bluetooth |
575 | --------- | 616 | --------- |
576 | 617 | ||
@@ -1090,6 +1131,15 @@ it there will be the following attributes: | |||
1090 | dim the display. | 1131 | dim the display. |
1091 | 1132 | ||
1092 | 1133 | ||
1134 | WARNING: | ||
1135 | |||
1136 | Whatever you do, do NOT ever call thinkpad-acpi backlight-level change | ||
1137 | interface and the ACPI-based backlight level change interface | ||
1138 | (available on newer BIOSes, and driven by the Linux ACPI video driver) | ||
1139 | at the same time. The two will interact in bad ways, do funny things, | ||
1140 | and maybe reduce the life of the backlight lamps by needlessly kicking | ||
1141 | its level up and down at every change. | ||
1142 | |||
1093 | Volume control -- /proc/acpi/ibm/volume | 1143 | Volume control -- /proc/acpi/ibm/volume |
1094 | --------------------------------------- | 1144 | --------------------------------------- |
1095 | 1145 | ||
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 6cb781262f94..2c85a2e10a25 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c | |||
@@ -225,6 +225,7 @@ static struct { | |||
225 | u32 light:1; | 225 | u32 light:1; |
226 | u32 light_status:1; | 226 | u32 light_status:1; |
227 | u32 bright_16levels:1; | 227 | u32 bright_16levels:1; |
228 | u32 bright_acpimode:1; | ||
228 | u32 wan:1; | 229 | u32 wan:1; |
229 | u32 fan_ctrl_status_undef:1; | 230 | u32 fan_ctrl_status_undef:1; |
230 | u32 input_device_registered:1; | 231 | u32 input_device_registered:1; |
@@ -807,6 +808,80 @@ static int parse_strtoul(const char *buf, | |||
807 | return 0; | 808 | return 0; |
808 | } | 809 | } |
809 | 810 | ||
811 | static int __init tpacpi_query_bcl_levels(acpi_handle handle) | ||
812 | { | ||
813 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
814 | union acpi_object *obj; | ||
815 | int rc; | ||
816 | |||
817 | if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) { | ||
818 | obj = (union acpi_object *)buffer.pointer; | ||
819 | if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { | ||
820 | printk(TPACPI_ERR "Unknown _BCL data, " | ||
821 | "please report this to %s\n", TPACPI_MAIL); | ||
822 | rc = 0; | ||
823 | } else { | ||
824 | rc = obj->package.count; | ||
825 | } | ||
826 | } else { | ||
827 | return 0; | ||
828 | } | ||
829 | |||
830 | kfree(buffer.pointer); | ||
831 | return rc; | ||
832 | } | ||
833 | |||
834 | static acpi_status __init tpacpi_acpi_walk_find_bcl(acpi_handle handle, | ||
835 | u32 lvl, void *context, void **rv) | ||
836 | { | ||
837 | char name[ACPI_PATH_SEGMENT_LENGTH]; | ||
838 | struct acpi_buffer buffer = { sizeof(name), &name }; | ||
839 | |||
840 | if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) && | ||
841 | !strncmp("_BCL", name, sizeof(name) - 1)) { | ||
842 | BUG_ON(!rv || !*rv); | ||
843 | **(int **)rv = tpacpi_query_bcl_levels(handle); | ||
844 | return AE_CTRL_TERMINATE; | ||
845 | } else { | ||
846 | return AE_OK; | ||
847 | } | ||
848 | } | ||
849 | |||
850 | /* | ||
851 | * Returns 0 (no ACPI _BCL or _BCL invalid), or size of brightness map | ||
852 | */ | ||
853 | static int __init tpacpi_check_std_acpi_brightness_support(void) | ||
854 | { | ||
855 | int status; | ||
856 | int bcl_levels = 0; | ||
857 | void *bcl_ptr = &bcl_levels; | ||
858 | |||
859 | if (!vid_handle) { | ||
860 | TPACPI_ACPIHANDLE_INIT(vid); | ||
861 | } | ||
862 | if (!vid_handle) | ||
863 | return 0; | ||
864 | |||
865 | /* | ||
866 | * Search for a _BCL method, and execute it. This is safe on all | ||
867 | * ThinkPads, and as a side-effect, _BCL will place a Lenovo Vista | ||
868 | * BIOS in ACPI backlight control mode. We do NOT have to care | ||
869 | * about calling the _BCL method in an enabled video device, any | ||
870 | * will do for our purposes. | ||
871 | */ | ||
872 | |||
873 | status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3, | ||
874 | tpacpi_acpi_walk_find_bcl, NULL, | ||
875 | &bcl_ptr); | ||
876 | |||
877 | if (ACPI_SUCCESS(status) && bcl_levels > 2) { | ||
878 | tp_features.bright_acpimode = 1; | ||
879 | return (bcl_levels - 2); | ||
880 | } | ||
881 | |||
882 | return 0; | ||
883 | } | ||
884 | |||
810 | /************************************************************************* | 885 | /************************************************************************* |
811 | * thinkpad-acpi driver attributes | 886 | * thinkpad-acpi driver attributes |
812 | */ | 887 | */ |
@@ -1887,6 +1962,9 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
1887 | KEY_UNKNOWN, /* 0x0D: FN+INSERT */ | 1962 | KEY_UNKNOWN, /* 0x0D: FN+INSERT */ |
1888 | KEY_UNKNOWN, /* 0x0E: FN+DELETE */ | 1963 | KEY_UNKNOWN, /* 0x0E: FN+DELETE */ |
1889 | 1964 | ||
1965 | /* These either have to go through ACPI video, or | ||
1966 | * act like in the IBM ThinkPads, so don't ever | ||
1967 | * enable them by default */ | ||
1890 | KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */ | 1968 | KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */ |
1891 | KEY_RESERVED, /* 0x10: FN+END (brightness down) */ | 1969 | KEY_RESERVED, /* 0x10: FN+END (brightness down) */ |
1892 | 1970 | ||
@@ -2091,6 +2169,32 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
2091 | set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit); | 2169 | set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit); |
2092 | } | 2170 | } |
2093 | 2171 | ||
2172 | /* Do not issue duplicate brightness change events to | ||
2173 | * userspace */ | ||
2174 | if (!tp_features.bright_acpimode) | ||
2175 | /* update bright_acpimode... */ | ||
2176 | tpacpi_check_std_acpi_brightness_support(); | ||
2177 | |||
2178 | if (tp_features.bright_acpimode) { | ||
2179 | printk(TPACPI_INFO | ||
2180 | "This ThinkPad has standard ACPI backlight " | ||
2181 | "brightness control, supported by the ACPI " | ||
2182 | "video driver\n"); | ||
2183 | printk(TPACPI_NOTICE | ||
2184 | "Disabling thinkpad-acpi brightness events " | ||
2185 | "by default...\n"); | ||
2186 | |||
2187 | /* The hotkey_reserved_mask change below is not | ||
2188 | * necessary while the keys are at KEY_RESERVED in the | ||
2189 | * default map, but better safe than sorry, leave it | ||
2190 | * here as a marker of what we have to do, especially | ||
2191 | * when we finally become able to set this at runtime | ||
2192 | * on response to X.org requests */ | ||
2193 | hotkey_reserved_mask |= | ||
2194 | (1 << TP_ACPI_HOTKEYSCAN_FNHOME) | ||
2195 | | (1 << TP_ACPI_HOTKEYSCAN_FNEND); | ||
2196 | } | ||
2197 | |||
2094 | dbg_printk(TPACPI_DBG_INIT, | 2198 | dbg_printk(TPACPI_DBG_INIT, |
2095 | "enabling hot key handling\n"); | 2199 | "enabling hot key handling\n"); |
2096 | res = hotkey_status_set(1); | 2200 | res = hotkey_status_set(1); |
@@ -4273,100 +4377,6 @@ static struct backlight_ops ibm_backlight_data = { | |||
4273 | 4377 | ||
4274 | /* --------------------------------------------------------------------- */ | 4378 | /* --------------------------------------------------------------------- */ |
4275 | 4379 | ||
4276 | static int __init tpacpi_query_bcll_levels(acpi_handle handle) | ||
4277 | { | ||
4278 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
4279 | union acpi_object *obj; | ||
4280 | int rc; | ||
4281 | |||
4282 | if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) { | ||
4283 | obj = (union acpi_object *)buffer.pointer; | ||
4284 | if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { | ||
4285 | printk(TPACPI_ERR "Unknown BCLL data, " | ||
4286 | "please report this to %s\n", TPACPI_MAIL); | ||
4287 | rc = 0; | ||
4288 | } else { | ||
4289 | rc = obj->package.count; | ||
4290 | } | ||
4291 | } else { | ||
4292 | return 0; | ||
4293 | } | ||
4294 | |||
4295 | kfree(buffer.pointer); | ||
4296 | return rc; | ||
4297 | } | ||
4298 | |||
4299 | static acpi_status __init brightness_find_bcll(acpi_handle handle, u32 lvl, | ||
4300 | void *context, void **rv) | ||
4301 | { | ||
4302 | char name[ACPI_PATH_SEGMENT_LENGTH]; | ||
4303 | struct acpi_buffer buffer = { sizeof(name), &name }; | ||
4304 | |||
4305 | if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) && | ||
4306 | !strncmp("BCLL", name, sizeof(name) - 1)) { | ||
4307 | if (tpacpi_query_bcll_levels(handle) == 16) { | ||
4308 | *rv = handle; | ||
4309 | return AE_CTRL_TERMINATE; | ||
4310 | } else { | ||
4311 | return AE_OK; | ||
4312 | } | ||
4313 | } else { | ||
4314 | return AE_OK; | ||
4315 | } | ||
4316 | } | ||
4317 | |||
4318 | static int __init brightness_check_levels(void) | ||
4319 | { | ||
4320 | int status; | ||
4321 | void *found_node = NULL; | ||
4322 | |||
4323 | if (!vid_handle) { | ||
4324 | TPACPI_ACPIHANDLE_INIT(vid); | ||
4325 | } | ||
4326 | if (!vid_handle) | ||
4327 | return 0; | ||
4328 | |||
4329 | /* Search for a BCLL package with 16 levels */ | ||
4330 | status = acpi_walk_namespace(ACPI_TYPE_PACKAGE, vid_handle, 3, | ||
4331 | brightness_find_bcll, NULL, | ||
4332 | &found_node); | ||
4333 | |||
4334 | return (ACPI_SUCCESS(status) && found_node != NULL); | ||
4335 | } | ||
4336 | |||
4337 | static acpi_status __init brightness_find_bcl(acpi_handle handle, u32 lvl, | ||
4338 | void *context, void **rv) | ||
4339 | { | ||
4340 | char name[ACPI_PATH_SEGMENT_LENGTH]; | ||
4341 | struct acpi_buffer buffer = { sizeof(name), &name }; | ||
4342 | |||
4343 | if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) && | ||
4344 | !strncmp("_BCL", name, sizeof(name) - 1)) { | ||
4345 | *rv = handle; | ||
4346 | return AE_CTRL_TERMINATE; | ||
4347 | } else { | ||
4348 | return AE_OK; | ||
4349 | } | ||
4350 | } | ||
4351 | |||
4352 | static int __init brightness_check_std_acpi_support(void) | ||
4353 | { | ||
4354 | int status; | ||
4355 | void *found_node = NULL; | ||
4356 | |||
4357 | if (!vid_handle) { | ||
4358 | TPACPI_ACPIHANDLE_INIT(vid); | ||
4359 | } | ||
4360 | if (!vid_handle) | ||
4361 | return 0; | ||
4362 | |||
4363 | /* Search for a _BCL method, but don't execute it */ | ||
4364 | status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3, | ||
4365 | brightness_find_bcl, NULL, &found_node); | ||
4366 | |||
4367 | return (ACPI_SUCCESS(status) && found_node != NULL); | ||
4368 | } | ||
4369 | |||
4370 | static int __init brightness_init(struct ibm_init_struct *iibm) | 4380 | static int __init brightness_init(struct ibm_init_struct *iibm) |
4371 | { | 4381 | { |
4372 | int b; | 4382 | int b; |
@@ -4375,13 +4385,19 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
4375 | 4385 | ||
4376 | mutex_init(&brightness_mutex); | 4386 | mutex_init(&brightness_mutex); |
4377 | 4387 | ||
4378 | if (!brightness_enable) { | 4388 | /* |
4379 | dbg_printk(TPACPI_DBG_INIT, | 4389 | * We always attempt to detect acpi support, so as to switch |
4380 | "brightness support disabled by " | 4390 | * Lenovo Vista BIOS to ACPI brightness mode even if we are not |
4381 | "module parameter\n"); | 4391 | * going to publish a backlight interface |
4382 | return 1; | 4392 | */ |
4383 | } else if (brightness_enable > 1) { | 4393 | b = tpacpi_check_std_acpi_brightness_support(); |
4384 | if (brightness_check_std_acpi_support()) { | 4394 | if (b > 0) { |
4395 | if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) { | ||
4396 | printk(TPACPI_NOTICE | ||
4397 | "Lenovo BIOS switched to ACPI backlight " | ||
4398 | "control mode\n"); | ||
4399 | } | ||
4400 | if (brightness_enable > 1) { | ||
4385 | printk(TPACPI_NOTICE | 4401 | printk(TPACPI_NOTICE |
4386 | "standard ACPI backlight interface " | 4402 | "standard ACPI backlight interface " |
4387 | "available, not loading native one...\n"); | 4403 | "available, not loading native one...\n"); |
@@ -4389,6 +4405,22 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
4389 | } | 4405 | } |
4390 | } | 4406 | } |
4391 | 4407 | ||
4408 | if (!brightness_enable) { | ||
4409 | dbg_printk(TPACPI_DBG_INIT, | ||
4410 | "brightness support disabled by " | ||
4411 | "module parameter\n"); | ||
4412 | return 1; | ||
4413 | } | ||
4414 | |||
4415 | if (b > 16) { | ||
4416 | printk(TPACPI_ERR | ||
4417 | "Unsupported brightness interface, " | ||
4418 | "please contact %s\n", TPACPI_MAIL); | ||
4419 | return 1; | ||
4420 | } | ||
4421 | if (b == 16) | ||
4422 | tp_features.bright_16levels = 1; | ||
4423 | |||
4392 | if (!brightness_mode) { | 4424 | if (!brightness_mode) { |
4393 | if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) | 4425 | if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) |
4394 | brightness_mode = 2; | 4426 | brightness_mode = 2; |
@@ -4402,10 +4434,6 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
4402 | if (brightness_mode > 3) | 4434 | if (brightness_mode > 3) |
4403 | return -EINVAL; | 4435 | return -EINVAL; |
4404 | 4436 | ||
4405 | tp_features.bright_16levels = | ||
4406 | thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO && | ||
4407 | brightness_check_levels(); | ||
4408 | |||
4409 | b = brightness_get(NULL); | 4437 | b = brightness_get(NULL); |
4410 | if (b < 0) | 4438 | if (b < 0) |
4411 | return 1; | 4439 | return 1; |