diff options
author | Henrique de Moraes Holschuh <hmh@hmh.eng.br> | 2008-04-26 00:02:17 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2008-04-29 09:46:59 -0400 |
commit | b59727965d7f286489206c292e2788d4835a8a23 (patch) | |
tree | 644211bb3c0cd4e3e2451df97ea11b0b80c08315 /drivers/misc/thinkpad_acpi.c | |
parent | a01e035ebb552223c03f2d9138ffc73f2d4d3965 (diff) |
ACPI: thinkpad-acpi: BIOS backlight mode helper (v2.1)
Lenovo ThinkPads with generic ACPI backlight level control can be easily
set to react to keyboard brightness key presses in a more predictable way
than what they do when in "DOS / bootloader" mode after Linux brings
up the ACPI interface.
The switch to the ACPI backlight mode in the firmware is designed to be
safe to use only as an one way trapdoor. One is not to force the firmware
to switch back to "DOS/bootloader" mode except by rebooting. The mode
switch itself is performed by calling any of the ACPI _BCL methods at least
once.
When in ACPI mode, the backlight firmware just issues (standard) events for
the brightness up/down hot key presses along with the non-standard HKEY
events which thinkpad-acpi traps, and doesn't touch the hardware.
thinkpad-acpi will:
1. Place the ThinkPad firmware in ACPI backlight control mode
if one is available
2. Suppress HKEY backlight change notifications by default
to avoid double-reporting when ACPI video is loaded when
the ThinkPad is in ACPI backlight control mode
3. Urge the user to load the ACPI video driver
The user is free to use either the ACPI video driver to get the brightness
key events, or to override the thinkpad-acpi default hotkey mask to get
them from thinkpad-acpi as well (this will result in duplicate events if
ACPI video is loaded, so let's hope distros won't screw this up).
Provided userspace is sane, all should work (and *keep* working), which is
more that can be said about the non-ACPI mode of the new Lenovo ThinkPad
BIOSes when coupled to current userspace and X.org drivers.
Full guidelines for backlight hot key reporting and use of the
thinkpad-acpi backlight interface have been added to the documentation.
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Cc: Matthew Garrett <mjg59@srcf.ucam.org>
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 | 238 |
1 files changed, 133 insertions, 105 deletions
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; |