diff options
Diffstat (limited to 'drivers/platform/x86/thinkpad_acpi.c')
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 320 |
1 files changed, 216 insertions, 104 deletions
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 3b8ceee7c5cb..7769575345d8 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -319,6 +319,7 @@ static struct { u32 sensors_pdrv_attrs_registered:1; u32 sensors_pdev_attrs_registered:1; u32 hotkey_poll_active:1; + u32 has_adaptive_kbd:1; } tp_features; static struct { @@ -1911,6 +1912,27 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */ TP_ACPI_HOTKEYSCAN_UNK7, TP_ACPI_HOTKEYSCAN_UNK8, + TP_ACPI_HOTKEYSCAN_MUTE2, + TP_ACPI_HOTKEYSCAN_BRIGHTNESS_ZERO, + TP_ACPI_HOTKEYSCAN_CLIPPING_TOOL, + TP_ACPI_HOTKEYSCAN_CLOUD, + TP_ACPI_HOTKEYSCAN_UNK9, + TP_ACPI_HOTKEYSCAN_VOICE, + TP_ACPI_HOTKEYSCAN_UNK10, + TP_ACPI_HOTKEYSCAN_GESTURES, + TP_ACPI_HOTKEYSCAN_UNK11, + TP_ACPI_HOTKEYSCAN_UNK12, + TP_ACPI_HOTKEYSCAN_UNK13, + TP_ACPI_HOTKEYSCAN_CONFIG, + TP_ACPI_HOTKEYSCAN_NEW_TAB, + TP_ACPI_HOTKEYSCAN_RELOAD, + TP_ACPI_HOTKEYSCAN_BACK, + TP_ACPI_HOTKEYSCAN_MIC_DOWN, + TP_ACPI_HOTKEYSCAN_MIC_UP, + TP_ACPI_HOTKEYSCAN_MIC_CANCELLATION, + TP_ACPI_HOTKEYSCAN_CAMERA_MODE, + TP_ACPI_HOTKEYSCAN_ROTATE_DISPLAY, + /* Hotkey keymap size */ TPACPI_HOTKEY_MAP_LEN }; @@ -2647,9 +2669,7 @@ static ssize_t hotkey_enable_store(struct device *dev, return count; } -static struct device_attribute dev_attr_hotkey_enable = - __ATTR(hotkey_enable, S_IWUSR | S_IRUGO, - hotkey_enable_show, hotkey_enable_store); +static DEVICE_ATTR_RW(hotkey_enable); /* sysfs hotkey mask --------------------------------------------------- */ static ssize_t hotkey_mask_show(struct device *dev, @@ -2685,9 +2705,7 @@ static ssize_t hotkey_mask_store(struct device *dev, return (res) ? res : count; } -static struct device_attribute dev_attr_hotkey_mask = - __ATTR(hotkey_mask, S_IWUSR | S_IRUGO, - hotkey_mask_show, hotkey_mask_store); +static DEVICE_ATTR_RW(hotkey_mask); /* sysfs hotkey bios_enabled ------------------------------------------- */ static ssize_t hotkey_bios_enabled_show(struct device *dev, @@ -2697,8 +2715,7 @@ static ssize_t hotkey_bios_enabled_show(struct device *dev, return sprintf(buf, "0\n"); } -static struct device_attribute dev_attr_hotkey_bios_enabled = - __ATTR(hotkey_bios_enabled, S_IRUGO, hotkey_bios_enabled_show, NULL); +static DEVICE_ATTR_RO(hotkey_bios_enabled); /* sysfs hotkey bios_mask ---------------------------------------------- */ static ssize_t hotkey_bios_mask_show(struct device *dev, @@ -2710,8 +2727,7 @@ static ssize_t hotkey_bios_mask_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_orig_mask); } -static struct device_attribute dev_attr_hotkey_bios_mask = - __ATTR(hotkey_bios_mask, S_IRUGO, hotkey_bios_mask_show, NULL); +static DEVICE_ATTR_RO(hotkey_bios_mask); /* sysfs hotkey all_mask ----------------------------------------------- */ static ssize_t hotkey_all_mask_show(struct device *dev, @@ -2722,8 +2738,7 @@ static ssize_t hotkey_all_mask_show(struct device *dev, hotkey_all_mask | hotkey_source_mask); } -static struct device_attribute dev_attr_hotkey_all_mask = - __ATTR(hotkey_all_mask, S_IRUGO, hotkey_all_mask_show, NULL); +static DEVICE_ATTR_RO(hotkey_all_mask); /* sysfs hotkey recommended_mask --------------------------------------- */ static ssize_t hotkey_recommended_mask_show(struct device *dev, @@ -2735,9 +2750,7 @@ static ssize_t hotkey_recommended_mask_show(struct device *dev, & ~hotkey_reserved_mask); } -static struct device_attribute dev_attr_hotkey_recommended_mask = - __ATTR(hotkey_recommended_mask, S_IRUGO, - hotkey_recommended_mask_show, NULL); +static DEVICE_ATTR_RO(hotkey_recommended_mask); #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL @@ -2792,9 +2805,7 @@ static ssize_t hotkey_source_mask_store(struct device *dev, return (rc < 0) ? rc : count; } -static struct device_attribute dev_attr_hotkey_source_mask = - __ATTR(hotkey_source_mask, S_IWUSR | S_IRUGO, - hotkey_source_mask_show, hotkey_source_mask_store); +static DEVICE_ATTR_RW(hotkey_source_mask); /* sysfs hotkey hotkey_poll_freq --------------------------------------- */ static ssize_t hotkey_poll_freq_show(struct device *dev, @@ -2826,9 +2837,7 @@ static ssize_t hotkey_poll_freq_store(struct device *dev, return count; } -static struct device_attribute dev_attr_hotkey_poll_freq = - __ATTR(hotkey_poll_freq, S_IWUSR | S_IRUGO, - hotkey_poll_freq_show, hotkey_poll_freq_store); +static DEVICE_ATTR_RW(hotkey_poll_freq); #endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ @@ -2849,8 +2858,7 @@ static ssize_t hotkey_radio_sw_show(struct device *dev, (res == TPACPI_RFK_RADIO_OFF) ? 0 : 1); } -static struct device_attribute dev_attr_hotkey_radio_sw = - __ATTR(hotkey_radio_sw, S_IRUGO, hotkey_radio_sw_show, NULL); +static DEVICE_ATTR_RO(hotkey_radio_sw); static void hotkey_radio_sw_notify_change(void) { @@ -2872,8 +2880,7 @@ static ssize_t hotkey_tablet_mode_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", !!s); } -static struct device_attribute dev_attr_hotkey_tablet_mode = - __ATTR(hotkey_tablet_mode, S_IRUGO, hotkey_tablet_mode_show, NULL); +static DEVICE_ATTR_RO(hotkey_tablet_mode); static void hotkey_tablet_mode_notify_change(void) { @@ -2890,8 +2897,7 @@ static ssize_t hotkey_wakeup_reason_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_wakeup_reason); } -static struct device_attribute dev_attr_hotkey_wakeup_reason = - __ATTR(wakeup_reason, S_IRUGO, hotkey_wakeup_reason_show, NULL); +static DEVICE_ATTR_RO(hotkey_wakeup_reason); static void hotkey_wakeup_reason_notify_change(void) { @@ -2907,9 +2913,7 @@ static ssize_t hotkey_wakeup_hotunplug_complete_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_autosleep_ack); } -static struct device_attribute dev_attr_hotkey_wakeup_hotunplug_complete = - __ATTR(wakeup_hotunplug_complete, S_IRUGO, - hotkey_wakeup_hotunplug_complete_show, NULL); +static DEVICE_ATTR_RO(hotkey_wakeup_hotunplug_complete); static void hotkey_wakeup_hotunplug_complete_notify_change(void) { @@ -2917,6 +2921,57 @@ static void hotkey_wakeup_hotunplug_complete_notify_change(void) "wakeup_hotunplug_complete"); } +/* sysfs adaptive kbd mode --------------------------------------------- */ + +static int adaptive_keyboard_get_mode(void); +static int adaptive_keyboard_set_mode(int new_mode); + +enum ADAPTIVE_KEY_MODE { + HOME_MODE, + WEB_BROWSER_MODE, + WEB_CONFERENCE_MODE, + FUNCTION_MODE, + LAYFLAT_MODE +}; + +static ssize_t adaptive_kbd_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int current_mode; + + current_mode = adaptive_keyboard_get_mode(); + if (current_mode < 0) + return current_mode; + + return snprintf(buf, PAGE_SIZE, "%d\n", current_mode); +} + +static ssize_t adaptive_kbd_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long t; + int res; + + if (parse_strtoul(buf, LAYFLAT_MODE, &t)) + return -EINVAL; + + res = adaptive_keyboard_set_mode(t); + return (res < 0) ? res : count; +} + +static DEVICE_ATTR_RW(adaptive_kbd_mode); + +static struct attribute *adaptive_kbd_attributes[] = { + &dev_attr_adaptive_kbd_mode.attr, + NULL +}; + +static const struct attribute_group adaptive_kbd_attr_group = { + .attrs = adaptive_kbd_attributes, +}; + /* --------------------------------------------------------------------- */ static struct attribute *hotkey_attributes[] __initdata = { @@ -3118,6 +3173,13 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) /* (assignments unknown, please report if found) */ KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, + + /* No assignments, only used for Adaptive keyboards. */ + KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, + KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, + KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, + KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, + KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, }, /* Generic keymap for Lenovo ThinkPads */ @@ -3174,6 +3236,35 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) /* Extra keys in use since the X240 / T440 / T540 */ KEY_CONFIG, KEY_SEARCH, KEY_SCALE, KEY_FILE, + + /* + * These are the adaptive keyboard keycodes for Carbon X1 2014. + * The first item in this list is the Mute button which is + * emitted with 0x103 through + * adaptive_keyboard_hotkey_notify_hotkey() when the sound + * symbol is held. + * We'll need to offset those by 0x20. + */ + KEY_RESERVED, /* Mute held, 0x103 */ + KEY_BRIGHTNESS_MIN, /* Backlight off */ + KEY_RESERVED, /* Clipping tool */ + KEY_RESERVED, /* Cloud */ + KEY_RESERVED, + KEY_VOICECOMMAND, /* Voice */ + KEY_RESERVED, + KEY_RESERVED, /* Gestures */ + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_CONFIG, /* Settings */ + KEY_RESERVED, /* New tab */ + KEY_REFRESH, /* Reload */ + KEY_BACK, /* Back */ + KEY_RESERVED, /* Microphone down */ + KEY_RESERVED, /* Microphone up */ + KEY_RESERVED, /* Microphone cancellation */ + KEY_RESERVED, /* Camera mode */ + KEY_RESERVED, /* Rotate display, 0x116 */ }, }; @@ -3227,6 +3318,20 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) if (!tp_features.hotkey) return 1; + /* + * Check if we have an adaptive keyboard, like on the + * Lenovo Carbon X1 2014 (2nd Gen). + */ + if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { + if ((hkeyv >> 8) == 2) { + tp_features.has_adaptive_kbd = true; + res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + &adaptive_kbd_attr_group); + if (res) + goto err_exit; + } + } + quirks = tpacpi_check_quirks(tpacpi_hotkey_qtable, ARRAY_SIZE(tpacpi_hotkey_qtable)); @@ -3437,6 +3542,9 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) err_exit: delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj); + sysfs_remove_group(&tpacpi_pdev->dev.kobj, + &adaptive_kbd_attr_group); + hotkey_dev_attributes = NULL; return (res < 0) ? res : 1; @@ -3449,14 +3557,6 @@ err_exit: * Will consider support rest of modes in future. * */ -enum ADAPTIVE_KEY_MODE { - HOME_MODE, - WEB_BROWSER_MODE, - WEB_CONFERENCE_MODE, - FUNCTION_MODE, - LAYFLAT_MODE -}; - static const int adaptive_keyboard_modes[] = { HOME_MODE, /* WEB_BROWSER_MODE = 2, @@ -3466,6 +3566,8 @@ static const int adaptive_keyboard_modes[] = { #define DFR_CHANGE_ROW 0x101 #define DFR_SHOW_QUICKVIEW_ROW 0x102 +#define FIRST_ADAPTIVE_KEY 0x103 +#define ADAPTIVE_KEY_OFFSET 0x020 /* press Fn key a while second, it will switch to Function Mode. Then * release Fn key, previous mode be restored. @@ -3473,6 +3575,32 @@ static const int adaptive_keyboard_modes[] = { static bool adaptive_keyboard_mode_is_saved; static int adaptive_keyboard_prev_mode; +static int adaptive_keyboard_get_mode(void) +{ + int mode = 0; + + if (!acpi_evalf(hkey_handle, &mode, "GTRW", "dd", 0)) { + pr_err("Cannot read adaptive keyboard mode\n"); + return -EIO; + } + + return mode; +} + +static int adaptive_keyboard_set_mode(int new_mode) +{ + if (new_mode < 0 || + new_mode > LAYFLAT_MODE) + return -EINVAL; + + if (!acpi_evalf(hkey_handle, NULL, "STRW", "vd", new_mode)) { + pr_err("Cannot set adaptive keyboard mode\n"); + return -EIO; + } + + return 0; +} + static int adaptive_keyboard_get_next_mode(int mode) { size_t i; @@ -3493,8 +3621,9 @@ static int adaptive_keyboard_get_next_mode(int mode) static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode) { - u32 current_mode = 0; + int current_mode = 0; int new_mode = 0; + int keycode; switch (scancode) { case DFR_CHANGE_ROW: @@ -3502,43 +3631,51 @@ static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode) new_mode = adaptive_keyboard_prev_mode; adaptive_keyboard_mode_is_saved = false; } else { - if (!acpi_evalf( - hkey_handle, ¤t_mode, - "GTRW", "dd", 0)) { - pr_err("Cannot read adaptive keyboard mode\n"); + current_mode = adaptive_keyboard_get_mode(); + if (current_mode < 0) return false; - } else { - new_mode = adaptive_keyboard_get_next_mode( - current_mode); - } + new_mode = adaptive_keyboard_get_next_mode( + current_mode); } - if (!acpi_evalf(hkey_handle, NULL, "STRW", "vd", new_mode)) { - pr_err("Cannot set adaptive keyboard mode\n"); + if (adaptive_keyboard_set_mode(new_mode) < 0) return false; - } return true; case DFR_SHOW_QUICKVIEW_ROW: - if (!acpi_evalf(hkey_handle, - &adaptive_keyboard_prev_mode, - "GTRW", "dd", 0)) { - pr_err("Cannot read adaptive keyboard mode\n"); + current_mode = adaptive_keyboard_get_mode(); + if (current_mode < 0) return false; - } else { - adaptive_keyboard_mode_is_saved = true; - if (!acpi_evalf(hkey_handle, - NULL, "STRW", "vd", FUNCTION_MODE)) { - pr_err("Cannot set adaptive keyboard mode\n"); - return false; - } - } + adaptive_keyboard_prev_mode = current_mode; + adaptive_keyboard_mode_is_saved = true; + + if (adaptive_keyboard_set_mode (FUNCTION_MODE) < 0) + return false; return true; default: - return false; + if (scancode < FIRST_ADAPTIVE_KEY || + scancode >= FIRST_ADAPTIVE_KEY + TPACPI_HOTKEY_MAP_LEN - + ADAPTIVE_KEY_OFFSET) { + pr_info("Unhandled adaptive keyboard key: 0x%x\n", + scancode); + return false; + } + keycode = hotkey_keycode_map[scancode - FIRST_ADAPTIVE_KEY + ADAPTIVE_KEY_OFFSET]; + if (keycode != KEY_RESERVED) { + mutex_lock(&tpacpi_inputdev_send_mutex); + + input_report_key(tpacpi_inputdev, keycode, 1); + input_sync(tpacpi_inputdev); + + input_report_key(tpacpi_inputdev, keycode, 0); + input_sync(tpacpi_inputdev); + + mutex_unlock(&tpacpi_inputdev_send_mutex); + } + return true; } } @@ -3836,28 +3973,21 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) static void hotkey_suspend(void) { - int hkeyv; - /* Do these on suspend, we get the events on early resume! */ hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE; hotkey_autosleep_ack = 0; /* save previous mode of adaptive keyboard of X1 Carbon */ - if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { - if ((hkeyv >> 8) == 2) { - if (!acpi_evalf(hkey_handle, - &adaptive_keyboard_prev_mode, - "GTRW", "dd", 0)) { - pr_err("Cannot read adaptive keyboard mode.\n"); - } + if (tp_features.has_adaptive_kbd) { + if (!acpi_evalf(hkey_handle, &adaptive_keyboard_prev_mode, + "GTRW", "dd", 0)) { + pr_err("Cannot read adaptive keyboard mode.\n"); } } } static void hotkey_resume(void) { - int hkeyv; - tpacpi_disable_brightness_delay(); if (hotkey_status_set(true) < 0 || @@ -3872,14 +4002,10 @@ static void hotkey_resume(void) hotkey_poll_setup_safe(false); /* restore previous mode of adapive keyboard of X1 Carbon */ - if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { - if ((hkeyv >> 8) == 2) { - if (!acpi_evalf(hkey_handle, - NULL, - "STRW", "vd", - adaptive_keyboard_prev_mode)) { - pr_err("Cannot set adaptive keyboard mode.\n"); - } + if (tp_features.has_adaptive_kbd) { + if (!acpi_evalf(hkey_handle, NULL, "STRW", "vd", + adaptive_keyboard_prev_mode)) { + pr_err("Cannot set adaptive keyboard mode.\n"); } } } @@ -4079,9 +4205,7 @@ static ssize_t bluetooth_enable_store(struct device *dev, attr, buf, count); } -static struct device_attribute dev_attr_bluetooth_enable = - __ATTR(bluetooth_enable, S_IWUSR | S_IRUGO, - bluetooth_enable_show, bluetooth_enable_store); +static DEVICE_ATTR_RW(bluetooth_enable); /* --------------------------------------------------------------------- */ @@ -4269,9 +4393,7 @@ static ssize_t wan_enable_store(struct device *dev, attr, buf, count); } -static struct device_attribute dev_attr_wan_enable = - __ATTR(wwan_enable, S_IWUSR | S_IRUGO, - wan_enable_show, wan_enable_store); +static DEVICE_ATTR_RW(wan_enable); /* --------------------------------------------------------------------- */ @@ -5048,8 +5170,7 @@ static ssize_t cmos_command_store(struct device *dev, return (res) ? res : count; } -static struct device_attribute dev_attr_cmos_command = - __ATTR(cmos_command, S_IWUSR, NULL, cmos_command_store); +static DEVICE_ATTR_WO(cmos_command); /* --------------------------------------------------------------------- */ @@ -8017,9 +8138,7 @@ static ssize_t fan_pwm1_enable_store(struct device *dev, return count; } -static struct device_attribute dev_attr_fan_pwm1_enable = - __ATTR(pwm1_enable, S_IWUSR | S_IRUGO, - fan_pwm1_enable_show, fan_pwm1_enable_store); +static DEVICE_ATTR_RW(fan_pwm1_enable); /* sysfs fan pwm1 ------------------------------------------------------ */ static ssize_t fan_pwm1_show(struct device *dev, @@ -8079,9 +8198,7 @@ static ssize_t fan_pwm1_store(struct device *dev, return (rc) ? rc : count; } -static struct device_attribute dev_attr_fan_pwm1 = - __ATTR(pwm1, S_IWUSR | S_IRUGO, - fan_pwm1_show, fan_pwm1_store); +static DEVICE_ATTR_RW(fan_pwm1); /* sysfs fan fan1_input ------------------------------------------------ */ static ssize_t fan_fan1_input_show(struct device *dev, @@ -8098,9 +8215,7 @@ static ssize_t fan_fan1_input_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%u\n", speed); } -static struct device_attribute dev_attr_fan_fan1_input = - __ATTR(fan1_input, S_IRUGO, - fan_fan1_input_show, NULL); +static DEVICE_ATTR_RO(fan_fan1_input); /* sysfs fan fan2_input ------------------------------------------------ */ static ssize_t fan_fan2_input_show(struct device *dev, @@ -8117,9 +8232,7 @@ static ssize_t fan_fan2_input_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%u\n", speed); } -static struct device_attribute dev_attr_fan_fan2_input = - __ATTR(fan2_input, S_IRUGO, - fan_fan2_input_show, NULL); +static DEVICE_ATTR_RO(fan_fan2_input); /* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */ static ssize_t fan_fan_watchdog_show(struct device_driver *drv, @@ -8735,8 +8848,7 @@ static ssize_t thinkpad_acpi_pdev_name_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%s\n", TPACPI_NAME); } -static struct device_attribute dev_attr_thinkpad_acpi_pdev_name = - __ATTR(name, S_IRUGO, thinkpad_acpi_pdev_name_show, NULL); +static DEVICE_ATTR_RO(thinkpad_acpi_pdev_name); /* --------------------------------------------------------------------- */ |