aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/thinkpad_acpi.c
diff options
context:
space:
mode:
authorHenrique de Moraes Holschuh <hmh@hmh.eng.br>2008-04-26 00:02:17 -0400
committerLen Brown <len.brown@intel.com>2008-04-29 09:46:59 -0400
commitb59727965d7f286489206c292e2788d4835a8a23 (patch)
tree644211bb3c0cd4e3e2451df97ea11b0b80c08315 /drivers/misc/thinkpad_acpi.c
parenta01e035ebb552223c03f2d9138ffc73f2d4d3965 (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.c238
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
811static 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
834static 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 */
853static 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
4276static 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
4299static 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
4318static 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
4337static 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
4352static 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
4370static int __init brightness_init(struct ibm_init_struct *iibm) 4380static 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;