aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform/x86/thinkpad_acpi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86/thinkpad_acpi.c')
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c227
1 files changed, 186 insertions, 41 deletions
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index 94bb6157c957..15e61c16736e 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -2321,53 +2321,55 @@ static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m)
2321 } 2321 }
2322} 2322}
2323 2323
2324static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
2325 struct tp_nvram_state *newn,
2326 const u32 event_mask)
2327{
2328
2329#define TPACPI_COMPARE_KEY(__scancode, __member) \ 2324#define TPACPI_COMPARE_KEY(__scancode, __member) \
2330 do { \ 2325do { \
2331 if ((event_mask & (1 << __scancode)) && \ 2326 if ((event_mask & (1 << __scancode)) && \
2332 oldn->__member != newn->__member) \ 2327 oldn->__member != newn->__member) \
2333 tpacpi_hotkey_send_key(__scancode); \ 2328 tpacpi_hotkey_send_key(__scancode); \
2334 } while (0) 2329} while (0)
2335 2330
2336#define TPACPI_MAY_SEND_KEY(__scancode) \ 2331#define TPACPI_MAY_SEND_KEY(__scancode) \
2337 do { \ 2332do { \
2338 if (event_mask & (1 << __scancode)) \ 2333 if (event_mask & (1 << __scancode)) \
2339 tpacpi_hotkey_send_key(__scancode); \ 2334 tpacpi_hotkey_send_key(__scancode); \
2340 } while (0) 2335} while (0)
2341 2336
2342 void issue_volchange(const unsigned int oldvol, 2337static void issue_volchange(const unsigned int oldvol,
2343 const unsigned int newvol) 2338 const unsigned int newvol,
2344 { 2339 const u32 event_mask)
2345 unsigned int i = oldvol; 2340{
2341 unsigned int i = oldvol;
2346 2342
2347 while (i > newvol) { 2343 while (i > newvol) {
2348 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN); 2344 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
2349 i--; 2345 i--;
2350 } 2346 }
2351 while (i < newvol) { 2347 while (i < newvol) {
2352 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); 2348 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
2353 i++; 2349 i++;
2354 }
2355 } 2350 }
2351}
2356 2352
2357 void issue_brightnesschange(const unsigned int oldbrt, 2353static void issue_brightnesschange(const unsigned int oldbrt,
2358 const unsigned int newbrt) 2354 const unsigned int newbrt,
2359 { 2355 const u32 event_mask)
2360 unsigned int i = oldbrt; 2356{
2357 unsigned int i = oldbrt;
2361 2358
2362 while (i > newbrt) { 2359 while (i > newbrt) {
2363 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND); 2360 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
2364 i--; 2361 i--;
2365 }
2366 while (i < newbrt) {
2367 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
2368 i++;
2369 }
2370 } 2362 }
2363 while (i < newbrt) {
2364 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
2365 i++;
2366 }
2367}
2368
2369static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
2370 struct tp_nvram_state *newn,
2371 const u32 event_mask)
2372{
2371 2373
2372 TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle); 2374 TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle);
2373 TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle); 2375 TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle);
@@ -2402,7 +2404,8 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
2402 oldn->volume_level != newn->volume_level) { 2404 oldn->volume_level != newn->volume_level) {
2403 /* recently muted, or repeated mute keypress, or 2405 /* recently muted, or repeated mute keypress, or
2404 * multiple presses ending in mute */ 2406 * multiple presses ending in mute */
2405 issue_volchange(oldn->volume_level, newn->volume_level); 2407 issue_volchange(oldn->volume_level, newn->volume_level,
2408 event_mask);
2406 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE); 2409 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE);
2407 } 2410 }
2408 } else { 2411 } else {
@@ -2412,7 +2415,8 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
2412 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); 2415 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
2413 } 2416 }
2414 if (oldn->volume_level != newn->volume_level) { 2417 if (oldn->volume_level != newn->volume_level) {
2415 issue_volchange(oldn->volume_level, newn->volume_level); 2418 issue_volchange(oldn->volume_level, newn->volume_level,
2419 event_mask);
2416 } else if (oldn->volume_toggle != newn->volume_toggle) { 2420 } else if (oldn->volume_toggle != newn->volume_toggle) {
2417 /* repeated vol up/down keypress at end of scale ? */ 2421 /* repeated vol up/down keypress at end of scale ? */
2418 if (newn->volume_level == 0) 2422 if (newn->volume_level == 0)
@@ -2425,7 +2429,7 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
2425 /* handle brightness */ 2429 /* handle brightness */
2426 if (oldn->brightness_level != newn->brightness_level) { 2430 if (oldn->brightness_level != newn->brightness_level) {
2427 issue_brightnesschange(oldn->brightness_level, 2431 issue_brightnesschange(oldn->brightness_level,
2428 newn->brightness_level); 2432 newn->brightness_level, event_mask);
2429 } else if (oldn->brightness_toggle != newn->brightness_toggle) { 2433 } else if (oldn->brightness_toggle != newn->brightness_toggle) {
2430 /* repeated key presses that didn't change state */ 2434 /* repeated key presses that didn't change state */
2431 if (newn->brightness_level == 0) 2435 if (newn->brightness_level == 0)
@@ -3437,6 +3441,106 @@ err_exit:
3437 return (res < 0)? res : 1; 3441 return (res < 0)? res : 1;
3438} 3442}
3439 3443
3444/* Thinkpad X1 Carbon support 5 modes including Home mode, Web browser
3445 * mode, Web conference mode, Function mode and Lay-flat mode.
3446 * We support Home mode and Function mode currently.
3447 *
3448 * Will consider support rest of modes in future.
3449 *
3450 */
3451enum ADAPTIVE_KEY_MODE {
3452 HOME_MODE,
3453 WEB_BROWSER_MODE,
3454 WEB_CONFERENCE_MODE,
3455 FUNCTION_MODE,
3456 LAYFLAT_MODE
3457};
3458
3459const int adaptive_keyboard_modes[] = {
3460 HOME_MODE,
3461/* WEB_BROWSER_MODE = 2,
3462 WEB_CONFERENCE_MODE = 3, */
3463 FUNCTION_MODE
3464};
3465
3466#define DFR_CHANGE_ROW 0x101
3467#define DFR_SHOW_QUICKVIEW_ROW 0x102
3468
3469/* press Fn key a while second, it will switch to Function Mode. Then
3470 * release Fn key, previous mode be restored.
3471 */
3472static bool adaptive_keyboard_mode_is_saved;
3473static int adaptive_keyboard_prev_mode;
3474
3475static int adaptive_keyboard_get_next_mode(int mode)
3476{
3477 size_t i;
3478 size_t max_mode = ARRAY_SIZE(adaptive_keyboard_modes) - 1;
3479
3480 for (i = 0; i <= max_mode; i++) {
3481 if (adaptive_keyboard_modes[i] == mode)
3482 break;
3483 }
3484
3485 if (i >= max_mode)
3486 i = 0;
3487 else
3488 i++;
3489
3490 return adaptive_keyboard_modes[i];
3491}
3492
3493static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode)
3494{
3495 u32 current_mode = 0;
3496 int new_mode = 0;
3497
3498 switch (scancode) {
3499 case DFR_CHANGE_ROW:
3500 if (adaptive_keyboard_mode_is_saved) {
3501 new_mode = adaptive_keyboard_prev_mode;
3502 adaptive_keyboard_mode_is_saved = false;
3503 } else {
3504 if (!acpi_evalf(
3505 hkey_handle, &current_mode,
3506 "GTRW", "dd", 0)) {
3507 pr_err("Cannot read adaptive keyboard mode\n");
3508 return false;
3509 } else {
3510 new_mode = adaptive_keyboard_get_next_mode(
3511 current_mode);
3512 }
3513 }
3514
3515 if (!acpi_evalf(hkey_handle, NULL, "STRW", "vd", new_mode)) {
3516 pr_err("Cannot set adaptive keyboard mode\n");
3517 return false;
3518 }
3519
3520 return true;
3521
3522 case DFR_SHOW_QUICKVIEW_ROW:
3523 if (!acpi_evalf(hkey_handle,
3524 &adaptive_keyboard_prev_mode,
3525 "GTRW", "dd", 0)) {
3526 pr_err("Cannot read adaptive keyboard mode\n");
3527 return false;
3528 } else {
3529 adaptive_keyboard_mode_is_saved = true;
3530
3531 if (!acpi_evalf(hkey_handle,
3532 NULL, "STRW", "vd", FUNCTION_MODE)) {
3533 pr_err("Cannot set adaptive keyboard mode\n");
3534 return false;
3535 }
3536 }
3537 return true;
3538
3539 default:
3540 return false;
3541 }
3542}
3543
3440static bool hotkey_notify_hotkey(const u32 hkey, 3544static bool hotkey_notify_hotkey(const u32 hkey,
3441 bool *send_acpi_ev, 3545 bool *send_acpi_ev,
3442 bool *ignore_acpi_ev) 3546 bool *ignore_acpi_ev)
@@ -3456,6 +3560,8 @@ static bool hotkey_notify_hotkey(const u32 hkey,
3456 *ignore_acpi_ev = true; 3560 *ignore_acpi_ev = true;
3457 } 3561 }
3458 return true; 3562 return true;
3563 } else {
3564 return adaptive_keyboard_hotkey_notify_hotkey(scancode);
3459 } 3565 }
3460 return false; 3566 return false;
3461} 3567}
@@ -3728,13 +3834,28 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
3728 3834
3729static void hotkey_suspend(void) 3835static void hotkey_suspend(void)
3730{ 3836{
3837 int hkeyv;
3838
3731 /* Do these on suspend, we get the events on early resume! */ 3839 /* Do these on suspend, we get the events on early resume! */
3732 hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE; 3840 hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE;
3733 hotkey_autosleep_ack = 0; 3841 hotkey_autosleep_ack = 0;
3842
3843 /* save previous mode of adaptive keyboard of X1 Carbon */
3844 if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
3845 if ((hkeyv >> 8) == 2) {
3846 if (!acpi_evalf(hkey_handle,
3847 &adaptive_keyboard_prev_mode,
3848 "GTRW", "dd", 0)) {
3849 pr_err("Cannot read adaptive keyboard mode.\n");
3850 }
3851 }
3852 }
3734} 3853}
3735 3854
3736static void hotkey_resume(void) 3855static void hotkey_resume(void)
3737{ 3856{
3857 int hkeyv;
3858
3738 tpacpi_disable_brightness_delay(); 3859 tpacpi_disable_brightness_delay();
3739 3860
3740 if (hotkey_status_set(true) < 0 || 3861 if (hotkey_status_set(true) < 0 ||
@@ -3747,6 +3868,18 @@ static void hotkey_resume(void)
3747 hotkey_wakeup_reason_notify_change(); 3868 hotkey_wakeup_reason_notify_change();
3748 hotkey_wakeup_hotunplug_complete_notify_change(); 3869 hotkey_wakeup_hotunplug_complete_notify_change();
3749 hotkey_poll_setup_safe(false); 3870 hotkey_poll_setup_safe(false);
3871
3872 /* restore previous mode of adapive keyboard of X1 Carbon */
3873 if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
3874 if ((hkeyv >> 8) == 2) {
3875 if (!acpi_evalf(hkey_handle,
3876 NULL,
3877 "STRW", "vd",
3878 adaptive_keyboard_prev_mode)) {
3879 pr_err("Cannot set adaptive keyboard mode.\n");
3880 }
3881 }
3882 }
3750} 3883}
3751 3884
3752/* procfs -------------------------------------------------------------- */ 3885/* procfs -------------------------------------------------------------- */
@@ -8447,9 +8580,21 @@ static void mute_led_exit(void)
8447 tpacpi_led_set(i, false); 8580 tpacpi_led_set(i, false);
8448} 8581}
8449 8582
8583static void mute_led_resume(void)
8584{
8585 int i;
8586
8587 for (i = 0; i < TPACPI_LED_MAX; i++) {
8588 struct tp_led_table *t = &led_tables[i];
8589 if (t->state >= 0)
8590 mute_led_on_off(t, t->state);
8591 }
8592}
8593
8450static struct ibm_struct mute_led_driver_data = { 8594static struct ibm_struct mute_led_driver_data = {
8451 .name = "mute_led", 8595 .name = "mute_led",
8452 .exit = mute_led_exit, 8596 .exit = mute_led_exit,
8597 .resume = mute_led_resume,
8453}; 8598};
8454 8599
8455/**************************************************************************** 8600/****************************************************************************