diff options
Diffstat (limited to 'drivers/platform/x86/thinkpad_acpi.c')
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 1375 |
1 files changed, 744 insertions, 631 deletions
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 5ecd9d33250d..1cc91173e012 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -45,6 +45,7 @@ #include <linux/hwmon-sysfs.h> #include <linux/init.h> #include <linux/input.h> +#include <linux/input/sparse-keymap.h> #include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/kthread.h> @@ -69,6 +70,7 @@ #include <linux/sysfs.h> #include <linux/types.h> #include <linux/uaccess.h> +#include <linux/units.h> #include <linux/workqueue.h> #include <acpi/battery.h> @@ -156,16 +158,34 @@ enum { /* HKEY events */ enum tpacpi_hkey_event_t { - /* Hotkey-related */ - TP_HKEY_EV_HOTKEY_BASE = 0x1001, /* first hotkey (FN+F1) */ + /* Original hotkeys */ + TP_HKEY_EV_ORIG_KEY_START = 0x1001, /* First hotkey (FN+F1) */ TP_HKEY_EV_BRGHT_UP = 0x1010, /* Brightness up */ TP_HKEY_EV_BRGHT_DOWN = 0x1011, /* Brightness down */ TP_HKEY_EV_KBD_LIGHT = 0x1012, /* Thinklight/kbd backlight */ TP_HKEY_EV_VOL_UP = 0x1015, /* Volume up or unmute */ TP_HKEY_EV_VOL_DOWN = 0x1016, /* Volume down or unmute */ TP_HKEY_EV_VOL_MUTE = 0x1017, /* Mixer output mute */ + TP_HKEY_EV_ORIG_KEY_END = 0x1020, /* Last original hotkey code */ + + /* Adaptive keyboard (2014 X1 Carbon) */ + TP_HKEY_EV_DFR_CHANGE_ROW = 0x1101, /* Change adaptive kbd Fn row mode */ + TP_HKEY_EV_DFR_S_QUICKVIEW_ROW = 0x1102, /* Set adap. kbd Fn row to function mode */ + TP_HKEY_EV_ADAPTIVE_KEY_START = 0x1103, /* First hotkey code on adaptive kbd */ + TP_HKEY_EV_ADAPTIVE_KEY_END = 0x1116, /* Last hotkey code on adaptive kbd */ + + /* Extended hotkey events in 2017+ models */ + TP_HKEY_EV_EXTENDED_KEY_START = 0x1300, /* First extended hotkey code */ TP_HKEY_EV_PRIVACYGUARD_TOGGLE = 0x130f, /* Toggle priv.guard on/off */ + TP_HKEY_EV_EXTENDED_KEY_END = 0x1319, /* Last extended hotkey code using + * hkey -> scancode translation for + * compat. Later codes are entered + * directly in the sparse-keymap. + */ TP_HKEY_EV_AMT_TOGGLE = 0x131a, /* Toggle AMT on/off */ + TP_HKEY_EV_DOUBLETAP_TOGGLE = 0x131c, /* Toggle trackpoint doubletap on/off */ + TP_HKEY_EV_PROFILE_TOGGLE = 0x131f, /* Toggle platform profile in 2024 systems */ + TP_HKEY_EV_PROFILE_TOGGLE2 = 0x1401, /* Toggle platform profile in 2025 + systems */ /* Reasons for waking up from S3/S4 */ TP_HKEY_EV_WKUP_S3_UNDOCK = 0x2304, /* undock requested, S3 */ @@ -230,6 +250,9 @@ enum tpacpi_hkey_event_t { /* Misc */ TP_HKEY_EV_RFKILL_CHANGED = 0x7000, /* rfkill switch changed */ + + /* Misc2 */ + TP_HKEY_EV_TRACK_DOUBLETAP = 0x8036, /* trackpoint doubletap */ }; /**************************************************************************** @@ -351,6 +374,7 @@ static struct { u32 hotkey_poll_active:1; u32 has_adaptive_kbd:1; u32 kbd_lang:1; + u32 trackpoint_doubletap:1; struct quirk_entry *quirks; } tp_features; @@ -939,6 +963,7 @@ static const struct proc_ops dispatch_proc_ops = { static struct platform_device *tpacpi_pdev; static struct platform_device *tpacpi_sensors_pdev; static struct device *tpacpi_hwmon; +static struct device *tpacpi_pprof; static struct input_dev *tpacpi_inputdev; static struct mutex tpacpi_inputdev_send_mutex; static LIST_HEAD(tpacpi_all_drivers); @@ -1742,15 +1767,15 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */ TP_ACPI_HOTKEYSCAN_THINKPAD, TP_ACPI_HOTKEYSCAN_UNK1, TP_ACPI_HOTKEYSCAN_UNK2, - TP_ACPI_HOTKEYSCAN_UNK3, + TP_ACPI_HOTKEYSCAN_MICMUTE, TP_ACPI_HOTKEYSCAN_UNK4, - TP_ACPI_HOTKEYSCAN_UNK5, - TP_ACPI_HOTKEYSCAN_UNK6, - TP_ACPI_HOTKEYSCAN_UNK7, - TP_ACPI_HOTKEYSCAN_UNK8, + TP_ACPI_HOTKEYSCAN_CONFIG, + TP_ACPI_HOTKEYSCAN_SEARCH, + TP_ACPI_HOTKEYSCAN_SCALE, + TP_ACPI_HOTKEYSCAN_FILE, /* Adaptive keyboard keycodes */ - TP_ACPI_HOTKEYSCAN_ADAPTIVE_START, + TP_ACPI_HOTKEYSCAN_ADAPTIVE_START, /* 32 / 0x20 */ TP_ACPI_HOTKEYSCAN_MUTE2 = TP_ACPI_HOTKEYSCAN_ADAPTIVE_START, TP_ACPI_HOTKEYSCAN_BRIGHTNESS_ZERO, TP_ACPI_HOTKEYSCAN_CLIPPING_TOOL, @@ -1762,7 +1787,7 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */ TP_ACPI_HOTKEYSCAN_UNK11, TP_ACPI_HOTKEYSCAN_UNK12, TP_ACPI_HOTKEYSCAN_UNK13, - TP_ACPI_HOTKEYSCAN_CONFIG, + TP_ACPI_HOTKEYSCAN_CONFIG2, TP_ACPI_HOTKEYSCAN_NEW_TAB, TP_ACPI_HOTKEYSCAN_RELOAD, TP_ACPI_HOTKEYSCAN_BACK, @@ -1773,7 +1798,7 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */ TP_ACPI_HOTKEYSCAN_ROTATE_DISPLAY, /* Lenovo extended keymap, starting at 0x1300 */ - TP_ACPI_HOTKEYSCAN_EXTENDED_START, + TP_ACPI_HOTKEYSCAN_EXTENDED_START, /* 52 / 0x34 */ /* first new observed key (star, favorites) is 0x1311 */ TP_ACPI_HOTKEYSCAN_STAR = 69, TP_ACPI_HOTKEYSCAN_CLIPPING_TOOL2, @@ -1784,9 +1809,6 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */ TP_ACPI_HOTKEYSCAN_NOTIFICATION_CENTER, TP_ACPI_HOTKEYSCAN_PICKUP_PHONE, TP_ACPI_HOTKEYSCAN_HANGUP_PHONE, - - /* Hotkey keymap size */ - TPACPI_HOTKEY_MAP_LEN }; enum { /* Keys/events available through NVRAM polling */ @@ -1899,10 +1921,7 @@ static u32 hotkey_driver_mask; /* events needed by the driver */ static u32 hotkey_user_mask; /* events visible to userspace */ static u32 hotkey_acpi_mask; /* events enabled in firmware */ -static u16 *hotkey_keycode_map; - -static void tpacpi_driver_event(const unsigned int hkey_event); -static void hotkey_driver_event(const unsigned int scancode); +static bool tpacpi_driver_event(const unsigned int hkey_event); static void hotkey_poll_setup(const bool may_warn); /* HKEY.MHKG() return bits */ @@ -2234,32 +2253,56 @@ static void tpacpi_input_send_tabletsw(void) } } -/* Do NOT call without validating scancode first */ -static void tpacpi_input_send_key(const unsigned int scancode) +static bool tpacpi_input_send_key(const u32 hkey, bool *send_acpi_ev) { - const unsigned int keycode = hotkey_keycode_map[scancode]; - - if (keycode != KEY_RESERVED) { - mutex_lock(&tpacpi_inputdev_send_mutex); + bool known_ev; + u32 scancode; - input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, scancode); - input_report_key(tpacpi_inputdev, keycode, 1); - input_sync(tpacpi_inputdev); + if (tpacpi_driver_event(hkey)) + return true; - input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, scancode); - input_report_key(tpacpi_inputdev, keycode, 0); - input_sync(tpacpi_inputdev); + /* + * Before the conversion to using the sparse-keymap helpers the driver used to + * map the hkey event codes to 0x00 - 0x4d scancodes so that a straight scancode + * indexed array could be used to map scancodes to keycodes: + * + * 0x1001 - 0x1020 -> 0x00 - 0x1f (Original ThinkPad events) + * 0x1103 - 0x1116 -> 0x20 - 0x33 (Adaptive keyboard, 2014 X1 Carbon) + * 0x1300 - 0x1319 -> 0x34 - 0x4d (Additional keys send in 2017+ models) + * + * The sparse-keymap tables still use these scancodes for these ranges to + * preserve userspace API compatibility (e.g. hwdb keymappings). + */ + if (hkey >= TP_HKEY_EV_ORIG_KEY_START && + hkey <= TP_HKEY_EV_ORIG_KEY_END) { + scancode = hkey - TP_HKEY_EV_ORIG_KEY_START; + if (!(hotkey_user_mask & (1 << scancode))) + return true; /* Not reported but still a known code */ + } else if (hkey >= TP_HKEY_EV_ADAPTIVE_KEY_START && + hkey <= TP_HKEY_EV_ADAPTIVE_KEY_END) { + scancode = hkey - TP_HKEY_EV_ADAPTIVE_KEY_START + + TP_ACPI_HOTKEYSCAN_ADAPTIVE_START; + } else if (hkey >= TP_HKEY_EV_EXTENDED_KEY_START && + hkey <= TP_HKEY_EV_EXTENDED_KEY_END) { + scancode = hkey - TP_HKEY_EV_EXTENDED_KEY_START + + TP_ACPI_HOTKEYSCAN_EXTENDED_START; + } else { + /* + * Do not send ACPI netlink events for unknown hotkeys, to + * avoid userspace starting to rely on them. Instead these + * should be added to the keymap to send evdev events. + */ + if (send_acpi_ev) + *send_acpi_ev = false; - mutex_unlock(&tpacpi_inputdev_send_mutex); + scancode = hkey; } -} -/* Do NOT call without validating scancode first */ -static void tpacpi_input_send_key_masked(const unsigned int scancode) -{ - hotkey_driver_event(scancode); - if (hotkey_user_mask & (1 << scancode)) - tpacpi_input_send_key(scancode); + mutex_lock(&tpacpi_inputdev_send_mutex); + known_ev = sparse_keymap_report_event(tpacpi_inputdev, scancode, 1, true); + mutex_unlock(&tpacpi_inputdev_send_mutex); + + return known_ev; } #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL @@ -2268,7 +2311,7 @@ static struct tp_acpi_drv_struct ibm_hotkey_acpidriver; /* Do NOT call without validating scancode first */ static void tpacpi_hotkey_send_key(unsigned int scancode) { - tpacpi_input_send_key_masked(scancode); + tpacpi_input_send_key(TP_HKEY_EV_ORIG_KEY_START + scancode, NULL); } static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m) @@ -2573,6 +2616,9 @@ static void hotkey_poll_setup_safe(const bool __unused) { } +static void hotkey_poll_stop_sync(void) +{ +} #endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ static int hotkey_inputdev_open(struct input_dev *dev) @@ -2677,7 +2723,7 @@ static ssize_t hotkey_bios_enabled_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "0\n"); + return sysfs_emit(buf, "0\n"); } static DEVICE_ATTR_RO(hotkey_bios_enabled); @@ -3042,11 +3088,8 @@ static void tpacpi_send_radiosw_update(void) static void hotkey_exit(void) { -#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL mutex_lock(&hotkey_mutex); hotkey_poll_stop_sync(); - mutex_unlock(&hotkey_mutex); -#endif dbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_HKEY, "restoring original HKEY status and mask\n"); /* yes, there is a bitwise or below, we want the @@ -3055,15 +3098,8 @@ static void hotkey_exit(void) hotkey_mask_set(hotkey_orig_mask)) | hotkey_status_set(false)) != 0) pr_err("failed to restore hot key mask to BIOS defaults\n"); -} -static void __init hotkey_unmap(const unsigned int scancode) -{ - if (hotkey_keycode_map[scancode] != KEY_RESERVED) { - clear_bit(hotkey_keycode_map[scancode], - tpacpi_inputdev->keybit); - hotkey_keycode_map[scancode] = KEY_RESERVED; - } + mutex_unlock(&hotkey_mutex); } /* @@ -3095,9 +3131,6 @@ static const struct tpacpi_quirk tpacpi_hotkey_qtable[] __initconst = { TPACPI_Q_IBM('1', 'D', TPACPI_HK_Q_INIMASK), /* X22, X23, X24 */ }; -typedef u16 tpacpi_keymap_entry_t; -typedef tpacpi_keymap_entry_t tpacpi_keymap_t[TPACPI_HOTKEY_MAP_LEN]; - static int hotkey_init_tablet_mode(void) { int in_tablet_mode = 0, res; @@ -3134,209 +3167,127 @@ static int hotkey_init_tablet_mode(void) return in_tablet_mode; } -static int __init hotkey_init(struct ibm_init_struct *iibm) -{ - /* Requirements for changing the default keymaps: - * - * 1. Many of the keys are mapped to KEY_RESERVED for very - * good reasons. Do not change them unless you have deep - * knowledge on the IBM and Lenovo ThinkPad firmware for - * the various ThinkPad models. The driver behaves - * differently for KEY_RESERVED: such keys have their - * hot key mask *unset* in mask_recommended, and also - * in the initial hot key mask programmed into the - * firmware at driver load time, which means the firm- - * ware may react very differently if you change them to - * something else; - * - * 2. You must be subscribed to the linux-thinkpad and - * ibm-acpi-devel mailing lists, and you should read the - * list archives since 2007 if you want to change the - * keymaps. This requirement exists so that you will - * know the past history of problems with the thinkpad- - * acpi driver keymaps, and also that you will be - * listening to any bug reports; - * - * 3. Do not send thinkpad-acpi specific patches directly to - * for merging, *ever*. Send them to the linux-acpi - * mailinglist for comments. Merging is to be done only - * through acpi-test and the ACPI maintainer. - * - * If the above is too much to ask, don't change the keymap. - * Ask the thinkpad-acpi maintainer to do it, instead. +static const struct key_entry keymap_ibm[] __initconst = { + /* Original hotkey mappings translated scancodes 0x00 - 0x1f */ + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF1, { KEY_FN_F1 } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF2, { KEY_BATTERY } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF3, { KEY_COFFEE } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF4, { KEY_SLEEP } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF5, { KEY_WLAN } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF6, { KEY_FN_F6 } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF7, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF8, { KEY_FN_F8 } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF9, { KEY_FN_F9 } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF10, { KEY_FN_F10 } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF11, { KEY_FN_F11 } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF12, { KEY_SUSPEND } }, + /* Brightness: firmware always reacts, suppressed through hotkey_reserved_mask. */ + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNHOME, { KEY_BRIGHTNESSUP } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNEND, { KEY_BRIGHTNESSDOWN } }, + /* Thinklight: firmware always reacts, suppressed through hotkey_reserved_mask. */ + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNPAGEUP, { KEY_KBDILLUMTOGGLE } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNSPACE, { KEY_ZOOM } }, + /* + * Volume: firmware always reacts and reprograms the built-in *extra* mixer. + * Suppressed by default through hotkey_reserved_mask. + */ + { KE_KEY, TP_ACPI_HOTKEYSCAN_VOLUMEUP, { KEY_VOLUMEUP } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_VOLUMEDOWN, { KEY_VOLUMEDOWN } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_MUTE, { KEY_MUTE } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_THINKPAD, { KEY_VENDOR } }, + { KE_END } +}; + +static const struct key_entry keymap_lenovo[] __initconst = { + /* Original hotkey mappings translated scancodes 0x00 - 0x1f */ + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF1, { KEY_FN_F1 } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF2, { KEY_COFFEE } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF3, { KEY_BATTERY } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF4, { KEY_SLEEP } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF5, { KEY_WLAN } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF6, { KEY_CAMERA, } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF7, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF8, { KEY_FN_F8 } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF9, { KEY_FN_F9 } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF10, { KEY_FN_F10 } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF11, { KEY_FN_F11 } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF12, { KEY_SUSPEND } }, + /* + * These should be enabled --only-- when ACPI video is disabled and + * are handled in a special way by the init code. + */ + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNHOME, { KEY_BRIGHTNESSUP } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNEND, { KEY_BRIGHTNESSDOWN } }, + /* Suppressed by default through hotkey_reserved_mask. */ + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNPAGEUP, { KEY_KBDILLUMTOGGLE } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FNSPACE, { KEY_ZOOM } }, + /* + * Volume: z60/z61, T60 (BIOS version?): firmware always reacts and + * reprograms the built-in *extra* mixer. + * T60?, T61, R60?, R61: firmware and EC tries to send these over + * the regular keyboard (not through tpacpi). There are still weird bugs + * re. MUTE. May cause the BIOS to interfere with the HDA mixer. + * Suppressed by default through hotkey_reserved_mask. */ + { KE_KEY, TP_ACPI_HOTKEYSCAN_VOLUMEUP, { KEY_VOLUMEUP } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_VOLUMEDOWN, { KEY_VOLUMEDOWN } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_MUTE, { KEY_MUTE } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_THINKPAD, { KEY_VENDOR } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_MICMUTE, { KEY_MICMUTE } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_CONFIG, { KEY_CONFIG } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_SEARCH, { KEY_SEARCH } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_SCALE, { KEY_SCALE } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_FILE, { KEY_FILE } }, + /* Adaptive keyboard mappings for Carbon X1 2014 translated scancodes 0x20 - 0x33 */ + { KE_KEY, TP_ACPI_HOTKEYSCAN_MUTE2, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_BRIGHTNESS_ZERO, { KEY_BRIGHTNESS_MIN } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_CLIPPING_TOOL, { KEY_SELECTIVE_SCREENSHOT } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_CLOUD, { KEY_XFER } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_UNK9, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_VOICE, { KEY_VOICECOMMAND } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_UNK10, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_GESTURES, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_UNK11, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_UNK12, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_UNK13, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_CONFIG2, { KEY_CONFIG } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_NEW_TAB, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_RELOAD, { KEY_REFRESH } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_BACK, { KEY_BACK } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_MIC_DOWN, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_MIC_UP, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_MIC_CANCELLATION, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_CAMERA_MODE, { KEY_RESERVED } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_ROTATE_DISPLAY, { KEY_RESERVED } }, + /* Extended hotkeys mappings translated scancodes 0x34 - 0x4d */ + { KE_KEY, TP_ACPI_HOTKEYSCAN_STAR, { KEY_BOOKMARKS } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_CLIPPING_TOOL2, { KEY_SELECTIVE_SCREENSHOT } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_CALCULATOR, { KEY_CALC } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_BLUETOOTH, { KEY_BLUETOOTH } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_KEYBOARD, { KEY_KEYBOARD } }, + /* Used by "Lenovo Quick Clean" */ + { KE_KEY, TP_ACPI_HOTKEYSCAN_FN_RIGHT_SHIFT, { KEY_FN_RIGHT_SHIFT } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_NOTIFICATION_CENTER, { KEY_NOTIFICATION_CENTER } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_PICKUP_PHONE, { KEY_PICKUP_PHONE } }, + { KE_KEY, TP_ACPI_HOTKEYSCAN_HANGUP_PHONE, { KEY_HANGUP_PHONE } }, + /* + * All mapping below are for raw untranslated hkey event codes mapped directly + * after switching to sparse keymap support. The mappings above use translated + * scancodes to preserve uAPI compatibility, see tpacpi_input_send_key(). + */ + { KE_KEY, 0x131d, { KEY_VENDOR } }, /* System debug info, similar to old ThinkPad key */ + { KE_KEY, 0x1320, { KEY_LINK_PHONE } }, + { KE_KEY, TP_HKEY_EV_TRACK_DOUBLETAP /* 0x8036 */, { KEY_PROG4 } }, + { KE_END } +}; +static int __init hotkey_init(struct ibm_init_struct *iibm) +{ enum keymap_index { TPACPI_KEYMAP_IBM_GENERIC = 0, TPACPI_KEYMAP_LENOVO_GENERIC, }; - static const tpacpi_keymap_t tpacpi_keymaps[] __initconst = { - /* Generic keymap for IBM ThinkPads */ - [TPACPI_KEYMAP_IBM_GENERIC] = { - /* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */ - KEY_FN_F1, KEY_BATTERY, KEY_COFFEE, KEY_SLEEP, - KEY_WLAN, KEY_FN_F6, KEY_SWITCHVIDEOMODE, KEY_FN_F8, - KEY_FN_F9, KEY_FN_F10, KEY_FN_F11, KEY_SUSPEND, - - /* Scan codes 0x0C to 0x1F: Other ACPI HKEY hot keys */ - KEY_UNKNOWN, /* 0x0C: FN+BACKSPACE */ - KEY_UNKNOWN, /* 0x0D: FN+INSERT */ - KEY_UNKNOWN, /* 0x0E: FN+DELETE */ - - /* brightness: firmware always reacts to them */ - KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */ - KEY_RESERVED, /* 0x10: FN+END (brightness down) */ - - /* Thinklight: firmware always react to it */ - KEY_RESERVED, /* 0x11: FN+PGUP (thinklight toggle) */ - - KEY_UNKNOWN, /* 0x12: FN+PGDOWN */ - KEY_ZOOM, /* 0x13: FN+SPACE (zoom) */ - - /* Volume: firmware always react to it and reprograms - * the built-in *extra* mixer. Never map it to control - * another mixer by default. */ - KEY_RESERVED, /* 0x14: VOLUME UP */ - KEY_RESERVED, /* 0x15: VOLUME DOWN */ - KEY_RESERVED, /* 0x16: MUTE */ - - KEY_VENDOR, /* 0x17: Thinkpad/AccessIBM/Lenovo */ - - /* (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, - - /* No assignment, used for newer Lenovo models */ - 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, KEY_UNKNOWN, - KEY_UNKNOWN, KEY_UNKNOWN - - }, - - /* Generic keymap for Lenovo ThinkPads */ - [TPACPI_KEYMAP_LENOVO_GENERIC] = { - /* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */ - KEY_FN_F1, KEY_COFFEE, KEY_BATTERY, KEY_SLEEP, - KEY_WLAN, KEY_CAMERA, KEY_SWITCHVIDEOMODE, KEY_FN_F8, - KEY_FN_F9, KEY_FN_F10, KEY_FN_F11, KEY_SUSPEND, - - /* Scan codes 0x0C to 0x1F: Other ACPI HKEY hot keys */ - KEY_UNKNOWN, /* 0x0C: FN+BACKSPACE */ - KEY_UNKNOWN, /* 0x0D: FN+INSERT */ - KEY_UNKNOWN, /* 0x0E: FN+DELETE */ - - /* These should be enabled --only-- when ACPI video - * is disabled (i.e. in "vendor" mode), and are handled - * in a special way by the init code */ - KEY_BRIGHTNESSUP, /* 0x0F: FN+HOME (brightness up) */ - KEY_BRIGHTNESSDOWN, /* 0x10: FN+END (brightness down) */ - - KEY_RESERVED, /* 0x11: FN+PGUP (thinklight toggle) */ - - KEY_UNKNOWN, /* 0x12: FN+PGDOWN */ - KEY_ZOOM, /* 0x13: FN+SPACE (zoom) */ - - /* Volume: z60/z61, T60 (BIOS version?): firmware always - * react to it and reprograms the built-in *extra* mixer. - * Never map it to control another mixer by default. - * - * T60?, T61, R60?, R61: firmware and EC tries to send - * these over the regular keyboard, so these are no-ops, - * but there are still weird bugs re. MUTE, so do not - * change unless you get test reports from all Lenovo - * models. May cause the BIOS to interfere with the - * HDA mixer. - */ - KEY_RESERVED, /* 0x14: VOLUME UP */ - KEY_RESERVED, /* 0x15: VOLUME DOWN */ - KEY_RESERVED, /* 0x16: MUTE */ - - KEY_VENDOR, /* 0x17: Thinkpad/AccessIBM/Lenovo */ - - /* (assignments unknown, please report if found) */ - KEY_UNKNOWN, KEY_UNKNOWN, - - /* - * The mic mute button only sends 0x1a. It does not - * automatically mute the mic or change the mute light. - */ - KEY_MICMUTE, /* 0x1a: Mic mute (since ?400 or so) */ - - /* (assignments unknown, please report if found) */ - KEY_UNKNOWN, - - /* 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 */ - - /* - * These are found in 2017 models (e.g. T470s, X270). - * The lowest known value is 0x311, which according to - * the manual should launch a user defined favorite - * application. - * - * The offset for these is TP_ACPI_HOTKEYSCAN_EXTENDED_START, - * corresponding to 0x34. - */ - - /* (assignments unknown, please report if found) */ - 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_BOOKMARKS, /* Favorite app, 0x311 */ - KEY_SELECTIVE_SCREENSHOT, /* Clipping tool */ - KEY_CALC, /* Calculator (above numpad, P52) */ - KEY_BLUETOOTH, /* Bluetooth */ - KEY_KEYBOARD, /* Keyboard, 0x315 */ - KEY_FN_RIGHT_SHIFT, /* Fn + right Shift */ - KEY_NOTIFICATION_CENTER, /* Notification Center */ - KEY_PICKUP_PHONE, /* Answer incoming call */ - KEY_HANGUP_PHONE, /* Decline incoming call */ - }, - }; - static const struct tpacpi_quirk tpacpi_keymap_qtable[] __initconst = { /* Generic maps (fallback) */ { @@ -3351,17 +3302,11 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) }, }; -#define TPACPI_HOTKEY_MAP_SIZE sizeof(tpacpi_keymap_t) -#define TPACPI_HOTKEY_MAP_TYPESIZE sizeof(tpacpi_keymap_entry_t) - - int res, i; - int status; - int hkeyv; + unsigned long keymap_id, quirks; + const struct key_entry *keymap; bool radiosw_state = false; bool tabletsw_state = false; - - unsigned long quirks; - unsigned long keymap_id; + int hkeyv, res, status; vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, "initializing hotkey subdriver\n"); @@ -3501,30 +3446,29 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) /* Set up key map */ keymap_id = tpacpi_check_quirks(tpacpi_keymap_qtable, ARRAY_SIZE(tpacpi_keymap_qtable)); - BUG_ON(keymap_id >= ARRAY_SIZE(tpacpi_keymaps)); dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, "using keymap number %lu\n", keymap_id); - hotkey_keycode_map = kmemdup(&tpacpi_keymaps[keymap_id], - TPACPI_HOTKEY_MAP_SIZE, GFP_KERNEL); - if (!hotkey_keycode_map) { - pr_err("failed to allocate memory for key map\n"); - return -ENOMEM; + /* Keys which should be reserved on both IBM and Lenovo models */ + hotkey_reserved_mask = TP_ACPI_HKEY_KBD_LIGHT_MASK | + TP_ACPI_HKEY_VOLUP_MASK | + TP_ACPI_HKEY_VOLDWN_MASK | + TP_ACPI_HKEY_MUTE_MASK; + /* + * Reserve brightness up/down unconditionally on IBM models, on Lenovo + * models these are disabled based on acpi_video_get_backlight_type(). + */ + if (keymap_id == TPACPI_KEYMAP_IBM_GENERIC) { + hotkey_reserved_mask |= TP_ACPI_HKEY_BRGHTUP_MASK | + TP_ACPI_HKEY_BRGHTDWN_MASK; + keymap = keymap_ibm; + } else { + keymap = keymap_lenovo; } - input_set_capability(tpacpi_inputdev, EV_MSC, MSC_SCAN); - tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE; - tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN; - tpacpi_inputdev->keycode = hotkey_keycode_map; - for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) { - if (hotkey_keycode_map[i] != KEY_RESERVED) { - input_set_capability(tpacpi_inputdev, EV_KEY, - hotkey_keycode_map[i]); - } else { - if (i < sizeof(hotkey_reserved_mask)*8) - hotkey_reserved_mask |= 1 << i; - } - } + res = sparse_keymap_setup(tpacpi_inputdev, keymap, NULL); + if (res) + return res; if (tp_features.hotkey_wlsw) { input_set_capability(tpacpi_inputdev, EV_SW, SW_RFKILL_ALL); @@ -3547,11 +3491,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) /* Disable brightness up/down on Lenovo thinkpads when * ACPI is handling them, otherwise it is plain impossible * for userspace to do something even remotely sane */ - hotkey_reserved_mask |= - (1 << TP_ACPI_HOTKEYSCAN_FNHOME) - | (1 << TP_ACPI_HOTKEYSCAN_FNEND); - hotkey_unmap(TP_ACPI_HOTKEYSCAN_FNHOME); - hotkey_unmap(TP_ACPI_HOTKEYSCAN_FNEND); + hotkey_reserved_mask |= TP_ACPI_HKEY_BRGHTUP_MASK | + TP_ACPI_HKEY_BRGHTDWN_MASK; } #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL @@ -3591,6 +3532,9 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) hotkey_poll_setup_safe(true); + /* Enable doubletap by default */ + tp_features.trackpoint_doubletap = 1; + return 0; } @@ -3608,10 +3552,6 @@ static const int adaptive_keyboard_modes[] = { FUNCTION_MODE }; -#define DFR_CHANGE_ROW 0x101 -#define DFR_SHOW_QUICKVIEW_ROW 0x102 -#define FIRST_ADAPTIVE_KEY 0x103 - /* press Fn key a while second, it will switch to Function Mode. Then * release Fn key, previous mode be restored. */ @@ -3662,152 +3602,67 @@ static int adaptive_keyboard_get_next_mode(int mode) return adaptive_keyboard_modes[i]; } -static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode) +static void adaptive_keyboard_change_row(void) { - int current_mode = 0; - int new_mode = 0; - int keycode; - - switch (scancode) { - case DFR_CHANGE_ROW: - if (adaptive_keyboard_mode_is_saved) { - new_mode = adaptive_keyboard_prev_mode; - adaptive_keyboard_mode_is_saved = false; - } else { - current_mode = adaptive_keyboard_get_mode(); - if (current_mode < 0) - return false; - new_mode = adaptive_keyboard_get_next_mode( - current_mode); - } - - if (adaptive_keyboard_set_mode(new_mode) < 0) - return false; - - return true; - - case DFR_SHOW_QUICKVIEW_ROW: - current_mode = adaptive_keyboard_get_mode(); - if (current_mode < 0) - 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: - if (scancode < FIRST_ADAPTIVE_KEY || - scancode >= FIRST_ADAPTIVE_KEY + - TP_ACPI_HOTKEYSCAN_EXTENDED_START - - TP_ACPI_HOTKEYSCAN_ADAPTIVE_START) { - pr_info("Unhandled adaptive keyboard key: 0x%x\n", - scancode); - return false; - } - keycode = hotkey_keycode_map[scancode - FIRST_ADAPTIVE_KEY + - TP_ACPI_HOTKEYSCAN_ADAPTIVE_START]; - 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); + int mode; - mutex_unlock(&tpacpi_inputdev_send_mutex); - } - return true; + if (adaptive_keyboard_mode_is_saved) { + mode = adaptive_keyboard_prev_mode; + adaptive_keyboard_mode_is_saved = false; + } else { + mode = adaptive_keyboard_get_mode(); + if (mode < 0) + return; + mode = adaptive_keyboard_get_next_mode(mode); } + + adaptive_keyboard_set_mode(mode); } -static bool hotkey_notify_extended_hotkey(const u32 hkey) +static void adaptive_keyboard_s_quickview_row(void) { - unsigned int scancode; + int mode; - switch (hkey) { - case TP_HKEY_EV_PRIVACYGUARD_TOGGLE: - case TP_HKEY_EV_AMT_TOGGLE: - tpacpi_driver_event(hkey); - return true; - } + mode = adaptive_keyboard_get_mode(); + if (mode < 0) + return; - /* Extended keycodes start at 0x300 and our offset into the map - * TP_ACPI_HOTKEYSCAN_EXTENDED_START. The calculated scancode - * will be positive, but might not be in the correct range. - */ - scancode = (hkey & 0xfff) - (0x300 - TP_ACPI_HOTKEYSCAN_EXTENDED_START); - if (scancode >= TP_ACPI_HOTKEYSCAN_EXTENDED_START && - scancode < TPACPI_HOTKEY_MAP_LEN) { - tpacpi_input_send_key(scancode); - return true; - } + adaptive_keyboard_prev_mode = mode; + adaptive_keyboard_mode_is_saved = true; - return false; + adaptive_keyboard_set_mode(FUNCTION_MODE); } -static bool hotkey_notify_hotkey(const u32 hkey, - bool *send_acpi_ev, - bool *ignore_acpi_ev) +/* 0x1000-0x1FFF: key presses */ +static bool hotkey_notify_hotkey(const u32 hkey, bool *send_acpi_ev) { - /* 0x1000-0x1FFF: key presses */ - unsigned int scancode = hkey & 0xfff; - *send_acpi_ev = true; - *ignore_acpi_ev = false; + /* Never send ACPI netlink events for original hotkeys (hkey: 0x1001 - 0x1020) */ + if (hkey >= TP_HKEY_EV_ORIG_KEY_START && hkey <= TP_HKEY_EV_ORIG_KEY_END) { + *send_acpi_ev = false; - /* - * Original events are in the 0x10XX range, the adaptive keyboard - * found in 2014 X1 Carbon emits events are of 0x11XX. In 2017 - * models, additional keys are emitted through 0x13XX. - */ - switch ((hkey >> 8) & 0xf) { - case 0: - if (scancode > 0 && - scancode <= TP_ACPI_HOTKEYSCAN_ADAPTIVE_START) { - /* HKEY event 0x1001 is scancode 0x00 */ - scancode--; - if (!(hotkey_source_mask & (1 << scancode))) { - tpacpi_input_send_key_masked(scancode); - *send_acpi_ev = false; - } else { - *ignore_acpi_ev = true; - } + /* Original hotkeys may be polled from NVRAM instead */ + unsigned int scancode = hkey - TP_HKEY_EV_ORIG_KEY_START; + if (hotkey_source_mask & (1 << scancode)) return true; - } - break; - - case 1: - return adaptive_keyboard_hotkey_notify_hotkey(scancode); - - case 3: - return hotkey_notify_extended_hotkey(hkey); } - return false; + return tpacpi_input_send_key(hkey, send_acpi_ev); } -static bool hotkey_notify_wakeup(const u32 hkey, - bool *send_acpi_ev, - bool *ignore_acpi_ev) +/* 0x2000-0x2FFF: Wakeup reason */ +static bool hotkey_notify_wakeup(const u32 hkey, bool *send_acpi_ev) { - /* 0x2000-0x2FFF: Wakeup reason */ - *send_acpi_ev = true; - *ignore_acpi_ev = false; - switch (hkey) { case TP_HKEY_EV_WKUP_S3_UNDOCK: /* suspend, undock */ case TP_HKEY_EV_WKUP_S4_UNDOCK: /* hibernation, undock */ hotkey_wakeup_reason = TP_ACPI_WAKEUP_UNDOCK; - *ignore_acpi_ev = true; + *send_acpi_ev = false; break; case TP_HKEY_EV_WKUP_S3_BAYEJ: /* suspend, bay eject */ case TP_HKEY_EV_WKUP_S4_BAYEJ: /* hibernation, bay eject */ hotkey_wakeup_reason = TP_ACPI_WAKEUP_BAYEJ; - *ignore_acpi_ev = true; + *send_acpi_ev = false; break; case TP_HKEY_EV_WKUP_S3_BATLOW: /* Battery on critical low level/S3 */ @@ -3829,14 +3684,9 @@ static bool hotkey_notify_wakeup(const u32 hkey, return true; } -static bool hotkey_notify_dockevent(const u32 hkey, - bool *send_acpi_ev, - bool *ignore_acpi_ev) +/* 0x4000-0x4FFF: dock-related events */ +static bool hotkey_notify_dockevent(const u32 hkey, bool *send_acpi_ev) { - /* 0x4000-0x4FFF: dock-related events */ - *send_acpi_ev = true; - *ignore_acpi_ev = false; - switch (hkey) { case TP_HKEY_EV_UNDOCK_ACK: /* ACPI undock operation completed after wakeup */ @@ -3866,7 +3716,6 @@ static bool hotkey_notify_dockevent(const u32 hkey, case TP_HKEY_EV_KBD_COVER_ATTACH: case TP_HKEY_EV_KBD_COVER_DETACH: *send_acpi_ev = false; - *ignore_acpi_ev = true; return true; default: @@ -3874,14 +3723,9 @@ static bool hotkey_notify_dockevent(const u32 hkey, } } -static bool hotkey_notify_usrevent(const u32 hkey, - bool *send_acpi_ev, - bool *ignore_acpi_ev) +/* 0x5000-0x5FFF: human interface helpers */ +static bool hotkey_notify_usrevent(const u32 hkey, bool *send_acpi_ev) { - /* 0x5000-0x5FFF: human interface helpers */ - *send_acpi_ev = true; - *ignore_acpi_ev = false; - switch (hkey) { case TP_HKEY_EV_PEN_INSERTED: /* X61t: tablet pen inserted into bay */ case TP_HKEY_EV_PEN_REMOVED: /* X61t: tablet pen removed from bay */ @@ -3898,7 +3742,7 @@ static bool hotkey_notify_usrevent(const u32 hkey, case TP_HKEY_EV_LID_OPEN: /* Lid opened */ case TP_HKEY_EV_BRGHT_CHANGED: /* brightness changed */ /* do not propagate these events */ - *ignore_acpi_ev = true; + *send_acpi_ev = false; return true; default: @@ -3909,14 +3753,9 @@ static bool hotkey_notify_usrevent(const u32 hkey, static void thermal_dump_all_sensors(void); static void palmsensor_refresh(void); -static bool hotkey_notify_6xxx(const u32 hkey, - bool *send_acpi_ev, - bool *ignore_acpi_ev) +/* 0x6000-0x6FFF: thermal alarms/notices and keyboard events */ +static bool hotkey_notify_6xxx(const u32 hkey, bool *send_acpi_ev) { - /* 0x6000-0x6FFF: thermal alarms/notices and keyboard events */ - *send_acpi_ev = true; - *ignore_acpi_ev = false; - switch (hkey) { case TP_HKEY_EV_THM_TABLE_CHANGED: pr_debug("EC reports: Thermal Table has changed\n"); @@ -3962,14 +3801,12 @@ static bool hotkey_notify_6xxx(const u32 hkey, /* key press events, we just ignore them as long as the EC * is still reporting them in the normal keyboard stream */ *send_acpi_ev = false; - *ignore_acpi_ev = true; return true; case TP_HKEY_EV_KEY_FN_ESC: /* Get the media key status to force the status LED to update */ acpi_evalf(hkey_handle, NULL, "GMKS", "v"); *send_acpi_ev = false; - *ignore_acpi_ev = true; return true; case TP_HKEY_EV_TABLET_CHANGED: @@ -3993,11 +3830,23 @@ static bool hotkey_notify_6xxx(const u32 hkey, return true; } +static bool hotkey_notify_8xxx(const u32 hkey, bool *send_acpi_ev) +{ + switch (hkey) { + case TP_HKEY_EV_TRACK_DOUBLETAP: + if (tp_features.trackpoint_doubletap) + tpacpi_input_send_key(hkey, send_acpi_ev); + + return true; + default: + return false; + } +} + static void hotkey_notify(struct ibm_struct *ibm, u32 event) { u32 hkey; bool send_acpi_ev; - bool ignore_acpi_ev; bool known_ev; if (event != 0x80) { @@ -4022,18 +3871,16 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) } send_acpi_ev = true; - ignore_acpi_ev = false; + known_ev = false; switch (hkey >> 12) { case 1: /* 0x1000-0x1FFF: key presses */ - known_ev = hotkey_notify_hotkey(hkey, &send_acpi_ev, - &ignore_acpi_ev); + known_ev = hotkey_notify_hotkey(hkey, &send_acpi_ev); break; case 2: /* 0x2000-0x2FFF: Wakeup reason */ - known_ev = hotkey_notify_wakeup(hkey, &send_acpi_ev, - &ignore_acpi_ev); + known_ev = hotkey_notify_wakeup(hkey, &send_acpi_ev); break; case 3: /* 0x3000-0x3FFF: bay-related wakeups */ @@ -4048,38 +3895,34 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) /* FIXME: kick libata if SATA link offline */ known_ev = true; break; - default: - known_ev = false; } break; case 4: /* 0x4000-0x4FFF: dock-related events */ - known_ev = hotkey_notify_dockevent(hkey, &send_acpi_ev, - &ignore_acpi_ev); + known_ev = hotkey_notify_dockevent(hkey, &send_acpi_ev); break; case 5: /* 0x5000-0x5FFF: human interface helpers */ - known_ev = hotkey_notify_usrevent(hkey, &send_acpi_ev, - &ignore_acpi_ev); + known_ev = hotkey_notify_usrevent(hkey, &send_acpi_ev); break; case 6: /* 0x6000-0x6FFF: thermal alarms/notices and * keyboard events */ - known_ev = hotkey_notify_6xxx(hkey, &send_acpi_ev, - &ignore_acpi_ev); + known_ev = hotkey_notify_6xxx(hkey, &send_acpi_ev); break; case 7: /* 0x7000-0x7FFF: misc */ if (tp_features.hotkey_wlsw && hkey == TP_HKEY_EV_RFKILL_CHANGED) { tpacpi_send_radiosw_update(); - send_acpi_ev = 0; + send_acpi_ev = false; known_ev = true; - break; } - fallthrough; /* to default */ - default: - known_ev = false; + break; + case 8: + /* 0x8000-0x8FFF: misc2 */ + known_ev = hotkey_notify_8xxx(hkey, &send_acpi_ev); + break; } if (!known_ev) { pr_notice("unhandled HKEY event 0x%04x\n", hkey); @@ -4088,7 +3931,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) } /* netlink events */ - if (!ignore_acpi_ev && send_acpi_ev) { + if (send_acpi_ev) { acpi_bus_generate_netlink_event( ibm->acpi->device->pnp.device_class, dev_name(&ibm->acpi->device->dev), @@ -6126,12 +5969,15 @@ enum thermal_access_mode { TPACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */ TPACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */ TPACPI_THERMAL_TPEC_8, /* Use ACPI EC regs, 8 sensors */ + TPACPI_THERMAL_TPEC_12, /* Use ACPI EC regs, 12 sensors */ TPACPI_THERMAL_TPEC_16, /* Use ACPI EC regs, 16 sensors */ }; enum { /* TPACPI_THERMAL_TPEC_* */ TP_EC_THERMAL_TMP0 = 0x78, /* ACPI EC regs TMP 0..7 */ TP_EC_THERMAL_TMP8 = 0xC0, /* ACPI EC regs TMP 8..15 */ + TP_EC_THERMAL_TMP0_NS = 0xA8, /* ACPI EC Non-Standard regs TMP 0..7 */ + TP_EC_THERMAL_TMP8_NS = 0xB8, /* ACPI EC Non-standard regs TMP 8..11 */ TP_EC_FUNCREV = 0xEF, /* ACPI EC Functional revision */ TP_EC_THERMAL_TMP_NA = -128, /* ACPI EC sensor not available */ @@ -6144,8 +5990,104 @@ struct ibm_thermal_sensors_struct { s32 temp[TPACPI_MAX_THERMAL_SENSORS]; }; +static const struct tpacpi_quirk thermal_quirk_table[] __initconst = { + /* Non-standard address for thermal registers on some ThinkPads */ + TPACPI_Q_LNV3('R', '1', 'F', true), /* L13 Yoga Gen 2 */ + TPACPI_Q_LNV3('N', '2', 'U', true), /* X13 Yoga Gen 2*/ + TPACPI_Q_LNV3('R', '0', 'R', true), /* L380 */ + TPACPI_Q_LNV3('R', '1', '5', true), /* L13 Yoga Gen 1*/ + TPACPI_Q_LNV3('R', '1', '0', true), /* L390 */ + TPACPI_Q_LNV3('N', '2', 'L', true), /* X13 Yoga Gen 1*/ + TPACPI_Q_LNV3('R', '0', 'T', true), /* 11e Gen5 GL*/ + TPACPI_Q_LNV3('R', '1', 'D', true), /* 11e Gen5 GL-R*/ + TPACPI_Q_LNV3('R', '0', 'V', true), /* 11e Gen5 KL-Y*/ +}; + static enum thermal_access_mode thermal_read_mode; static bool thermal_use_labels; +static bool thermal_with_ns_address; /* Non-standard thermal reg address */ + +/* Function to check thermal read mode */ +static enum thermal_access_mode __init thermal_read_mode_check(void) +{ + u8 t, ta1, ta2, ver = 0; + int i; + int acpi_tmp7; + + acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); + + if (thinkpad_id.ec_model) { + /* + * Direct EC access mode: sensors at registers 0x78-0x7F, + * 0xC0-0xC7. Registers return 0x00 for non-implemented, + * thermal sensors return 0x80 when not available. + * + * In some special cases (when Power Supply ID is 0xC2) + * above rule causes thermal control issues. Offset 0xEF + * determines EC version. 0xC0-0xC7 are not thermal registers + * in Ver 3. + */ + if (!acpi_ec_read(TP_EC_FUNCREV, &ver)) + pr_warn("Thinkpad ACPI EC unable to access EC version\n"); + + /* Quirks to check non-standard EC */ + thermal_with_ns_address = tpacpi_check_quirks(thermal_quirk_table, + ARRAY_SIZE(thermal_quirk_table)); + + /* Support for Thinkpads with non-standard address */ + if (thermal_with_ns_address) { + pr_info("ECFW with non-standard thermal registers found\n"); + return TPACPI_THERMAL_TPEC_12; + } + + ta1 = ta2 = 0; + for (i = 0; i < 8; i++) { + if (acpi_ec_read(TP_EC_THERMAL_TMP0 + i, &t)) { + ta1 |= t; + } else { + ta1 = 0; + break; + } + if (ver < 3) { + if (acpi_ec_read(TP_EC_THERMAL_TMP8 + i, &t)) { + ta2 |= t; + } else { + ta1 = 0; + break; + } + } + } + + if (ta1 == 0) { + /* This is sheer paranoia, but we handle it anyway */ + if (acpi_tmp7) { + pr_err("ThinkPad ACPI EC access misbehaving, falling back to ACPI TMPx access mode\n"); + return TPACPI_THERMAL_ACPI_TMP07; + } + pr_err("ThinkPad ACPI EC access misbehaving, disabling thermal sensors access\n"); + return TPACPI_THERMAL_NONE; + } + + if (ver >= 3) { + thermal_use_labels = true; + return TPACPI_THERMAL_TPEC_8; + } + + return (ta2 != 0) ? TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8; + } + + if (acpi_tmp7) { + if (tpacpi_is_ibm() && acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { + /* 600e/x, 770e, 770x */ + return TPACPI_THERMAL_ACPI_UPDT; + } + /* IBM/LENOVO DSDT EC.TMPx access, max 8 sensors */ + return TPACPI_THERMAL_ACPI_TMP07; + } + + /* temperatures not supported on 570, G4x, R30, R31, R32 */ + return TPACPI_THERMAL_NONE; +} /* idx is zero-based */ static int thermal_get_sensor(int idx, s32 *value) @@ -6174,6 +6116,20 @@ static int thermal_get_sensor(int idx, s32 *value) } break; + /* The Non-standard EC uses 12 Thermal areas */ + case TPACPI_THERMAL_TPEC_12: + if (idx >= 12) + return -EINVAL; + + t = idx < 8 ? TP_EC_THERMAL_TMP0_NS + idx : + TP_EC_THERMAL_TMP8_NS + (idx - 8); + + if (!acpi_ec_read(t, &tmp)) + return -EIO; + + *value = tmp * MILLIDEGREE_PER_DEGREE; + return 0; + case TPACPI_THERMAL_ACPI_UPDT: if (idx <= 7) { snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx); @@ -6208,17 +6164,17 @@ static int thermal_get_sensor(int idx, s32 *value) static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s) { - int res, i; - int n; - - n = 8; - i = 0; + int res, i, n; if (!s) return -EINVAL; if (thermal_read_mode == TPACPI_THERMAL_TPEC_16) n = 16; + else if (thermal_read_mode == TPACPI_THERMAL_TPEC_12) + n = 12; + else + n = 8; for (i = 0 ; i < n; i++) { res = thermal_get_sensor(i, &s->temp[i]); @@ -6317,18 +6273,36 @@ static struct attribute *thermal_temp_input_attr[] = { NULL }; +#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr) + static umode_t thermal_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) { - if (thermal_read_mode == TPACPI_THERMAL_NONE) + struct device_attribute *dev_attr = to_dev_attr(attr); + struct sensor_device_attribute *sensor_attr = + to_sensor_dev_attr(dev_attr); + + int idx = sensor_attr->index; + + switch (thermal_read_mode) { + case TPACPI_THERMAL_NONE: return 0; - if (attr == THERMAL_ATTRS(8) || attr == THERMAL_ATTRS(9) || - attr == THERMAL_ATTRS(10) || attr == THERMAL_ATTRS(11) || - attr == THERMAL_ATTRS(12) || attr == THERMAL_ATTRS(13) || - attr == THERMAL_ATTRS(14) || attr == THERMAL_ATTRS(15)) { - if (thermal_read_mode != TPACPI_THERMAL_TPEC_16) + case TPACPI_THERMAL_ACPI_TMP07: + case TPACPI_THERMAL_ACPI_UPDT: + case TPACPI_THERMAL_TPEC_8: + if (idx >= 8) + return 0; + break; + + case TPACPI_THERMAL_TPEC_12: + if (idx >= 12) return 0; + break; + + default: + break; + } return attr->mode; @@ -6375,78 +6349,9 @@ static const struct attribute_group temp_label_attr_group = { static int __init thermal_init(struct ibm_init_struct *iibm) { - u8 t, ta1, ta2, ver = 0; - int i; - int acpi_tmp7; - vdbg_printk(TPACPI_DBG_INIT, "initializing thermal subdriver\n"); - acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); - - if (thinkpad_id.ec_model) { - /* - * Direct EC access mode: sensors at registers - * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for - * non-implemented, thermal sensors return 0x80 when - * not available - * The above rule is unfortunately flawed. This has been seen with - * 0xC2 (power supply ID) causing thermal control problems. - * The EC version can be determined by offset 0xEF and at least for - * version 3 the Lenovo firmware team confirmed that registers 0xC0-0xC7 - * are not thermal registers. - */ - if (!acpi_ec_read(TP_EC_FUNCREV, &ver)) - pr_warn("Thinkpad ACPI EC unable to access EC version\n"); - - ta1 = ta2 = 0; - for (i = 0; i < 8; i++) { - if (acpi_ec_read(TP_EC_THERMAL_TMP0 + i, &t)) { - ta1 |= t; - } else { - ta1 = 0; - break; - } - if (ver < 3) { - if (acpi_ec_read(TP_EC_THERMAL_TMP8 + i, &t)) { - ta2 |= t; - } else { - ta1 = 0; - break; - } - } - } - if (ta1 == 0) { - /* This is sheer paranoia, but we handle it anyway */ - if (acpi_tmp7) { - pr_err("ThinkPad ACPI EC access misbehaving, falling back to ACPI TMPx access mode\n"); - thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07; - } else { - pr_err("ThinkPad ACPI EC access misbehaving, disabling thermal sensors access\n"); - thermal_read_mode = TPACPI_THERMAL_NONE; - } - } else { - if (ver >= 3) { - thermal_read_mode = TPACPI_THERMAL_TPEC_8; - thermal_use_labels = true; - } else { - thermal_read_mode = - (ta2 != 0) ? - TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8; - } - } - } else if (acpi_tmp7) { - if (tpacpi_is_ibm() && - acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { - /* 600e/x, 770e, 770x */ - thermal_read_mode = TPACPI_THERMAL_ACPI_UPDT; - } else { - /* IBM/LENOVO DSDT EC.TMPx access, max 8 sensors */ - thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07; - } - } else { - /* temperatures not supported on 570, G4x, R30, R31, R32 */ - thermal_read_mode = TPACPI_THERMAL_NONE; - } + thermal_read_mode = thermal_read_mode_check(); vdbg_printk(TPACPI_DBG_INIT, "thermal is %s, mode %d\n", str_supported(thermal_read_mode != TPACPI_THERMAL_NONE), @@ -7514,10 +7419,8 @@ static int __init volume_create_alsa_mixer(void) data = card->private_data; data->card = card; - strscpy(card->driver, TPACPI_ALSA_DRVNAME, - sizeof(card->driver)); - strscpy(card->shortname, TPACPI_ALSA_SHRTNAME, - sizeof(card->shortname)); + strscpy(card->driver, TPACPI_ALSA_DRVNAME); + strscpy(card->shortname, TPACPI_ALSA_SHRTNAME); snprintf(card->mixername, sizeof(card->mixername), "ThinkPad EC %s", (thinkpad_id.ec_version_str) ? thinkpad_id.ec_version_str : "(unknown)"); @@ -7849,6 +7752,28 @@ static struct ibm_struct volume_driver_data = { * EC 0x2f (HFSP) might be available *for reading*, but do not use * it for writing. * + * TPACPI_FAN_RD_ACPI_FANG: + * ACPI FANG method: returns fan control register + * + * Takes one parameter which is 0x8100 plus the offset to EC memory + * address 0xf500 and returns the byte at this address. + * + * 0xf500: + * When the value is less than 9 automatic mode is enabled + * 0xf502: + * Contains the current fan speed from 0-100% + * 0xf506: + * Bit 7 has to be set in order to enable manual control by + * writing a value >= 9 to 0xf500 + * + * TPACPI_FAN_WR_ACPI_FANW: + * ACPI FANW method: sets fan control registers + * + * Takes 0x8100 plus the offset to EC memory address 0xf500 and the + * value to be written there as parameters. + * + * see TPACPI_FAN_RD_ACPI_FANG + * * TPACPI_FAN_WR_TPEC: * ThinkPad EC register 0x2f (HFSP): fan control loop mode * Supported on almost all ThinkPads @@ -7960,6 +7885,7 @@ static struct ibm_struct volume_driver_data = { #define FAN_NS_CTRL_STATUS BIT(2) /* Bit which determines control is enabled or not */ #define FAN_NS_CTRL BIT(4) /* Bit which determines control is by host or EC */ +#define FAN_CLOCK_TPM (22500*60) /* Ticks per minute for a 22.5 kHz clock */ enum { /* Fan control constants */ fan_status_offset = 0x2f, /* EC register 0x2f */ @@ -7982,6 +7908,7 @@ enum { /* Fan control constants */ enum fan_status_access_mode { TPACPI_FAN_NONE = 0, /* No fan status or control */ TPACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */ + TPACPI_FAN_RD_ACPI_FANG, /* Use ACPI FANG */ TPACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */ TPACPI_FAN_RD_TPEC_NS, /* Use non-standard ACPI EC regs (eg: L13 Yoga gen2 etc.) */ }; @@ -7989,6 +7916,7 @@ enum fan_status_access_mode { enum fan_control_access_mode { TPACPI_FAN_WR_NONE = 0, /* No fan control */ TPACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */ + TPACPI_FAN_WR_ACPI_FANW, /* Use ACPI FANW */ TPACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */ TPACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */ }; @@ -8012,6 +7940,8 @@ static u8 fan_control_resume_level; static int fan_watchdog_maxinterval; static bool fan_with_ns_addr; +static bool ecfw_with_fan_dec_rpm; +static bool fan_speed_in_tpr; static struct mutex fan_mutex; @@ -8022,9 +7952,13 @@ TPACPI_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */ TPACPI_HANDLE(gfan, ec, "GFAN", /* 570 */ "\\FSPD", /* 600e/x, 770e, 770x */ ); /* all others */ +TPACPI_HANDLE(fang, ec, "FANG", /* E531 */ + ); /* all others */ TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */ "JFNS", /* 770x-JL */ ); /* all others */ +TPACPI_HANDLE(fanw, ec, "FANW", /* E531 */ + ); /* all others */ /* * Unitialized HFSP quirk: ACPI DSDT and EC fail to initialize the @@ -8131,6 +8065,23 @@ static int fan_get_status(u8 *status) break; } + case TPACPI_FAN_RD_ACPI_FANG: { + /* E531 */ + int mode, speed; + + if (unlikely(!acpi_evalf(fang_handle, &mode, NULL, "dd", 0x8100))) + return -EIO; + if (unlikely(!acpi_evalf(fang_handle, &speed, NULL, "dd", 0x8102))) + return -EIO; + + if (likely(status)) { + *status = speed * 7 / 100; + if (mode < 9) + *status |= TP_EC_FAN_AUTO; + } + + break; + } case TPACPI_FAN_RD_TPEC: /* all except 570, 600e/x, 770e, 770x */ if (unlikely(!acpi_ec_read(fan_status_offset, &s))) @@ -8193,8 +8144,11 @@ static int fan_get_speed(unsigned int *speed) !acpi_ec_read(fan_rpm_offset + 1, &hi))) return -EIO; - if (likely(speed)) + if (likely(speed)) { *speed = (hi << 8) | lo; + if (fan_speed_in_tpr && *speed != 0) + *speed = FAN_CLOCK_TPM / *speed; + } break; case TPACPI_FAN_RD_TPEC_NS: if (!acpi_ec_read(fan_rpm_status_ns, &lo)) @@ -8227,8 +8181,11 @@ static int fan2_get_speed(unsigned int *speed) if (rc) return -EIO; - if (likely(speed)) + if (likely(speed)) { *speed = (hi << 8) | lo; + if (fan_speed_in_tpr && *speed != 0) + *speed = FAN_CLOCK_TPM / *speed; + } break; case TPACPI_FAN_RD_TPEC_NS: @@ -8245,6 +8202,17 @@ static int fan2_get_speed(unsigned int *speed) if (speed) *speed = lo ? FAN_RPM_CAL_CONST / lo : 0; break; + case TPACPI_FAN_RD_ACPI_FANG: { + /* E531 */ + int speed_tmp; + + if (unlikely(!acpi_evalf(fang_handle, &speed_tmp, NULL, "dd", 0x8102))) + return -EIO; + + if (likely(speed)) + *speed = speed_tmp * 65535 / 100; + break; + } default: return -ENXIO; @@ -8304,6 +8272,32 @@ static int fan_set_level(int level) tp_features.fan_ctrl_status_undef = 0; break; + case TPACPI_FAN_WR_ACPI_FANW: + if (!(level & TP_EC_FAN_AUTO) && (level < 0 || level > 7)) + return -EINVAL; + if (level & TP_EC_FAN_FULLSPEED) + return -EINVAL; + + if (level & TP_EC_FAN_AUTO) { + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x05)) { + return -EIO; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0x00)) { + return -EIO; + } + } else { + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x45)) { + return -EIO; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0xff)) { + return -EIO; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8102, level * 100 / 7)) { + return -EIO; + } + } + break; + default: return -ENXIO; } @@ -8336,7 +8330,7 @@ static int fan_set_level_safe(int level) static int fan_set_enable(void) { - u8 s; + u8 s = 0; int rc; if (!fan_control_allowed) @@ -8382,6 +8376,19 @@ static int fan_set_enable(void) rc = 0; break; + case TPACPI_FAN_WR_ACPI_FANW: + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x05)) { + rc = -EIO; + break; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0x00)) { + rc = -EIO; + break; + } + + rc = 0; + break; + default: rc = -ENXIO; } @@ -8424,6 +8431,22 @@ static int fan_set_disable(void) fan_control_desired_level = 0; break; + case TPACPI_FAN_WR_ACPI_FANW: + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x45)) { + rc = -EIO; + break; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0xff)) { + rc = -EIO; + break; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8102, 0x00)) { + rc = -EIO; + break; + } + rc = 0; + break; + default: rc = -ENXIO; } @@ -8457,6 +8480,23 @@ static int fan_set_speed(int speed) rc = -EINVAL; break; + case TPACPI_FAN_WR_ACPI_FANW: + if (speed >= 0 && speed <= 65535) { + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x45)) { + rc = -EIO; + break; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0xff)) { + rc = -EIO; + break; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", + 0x8102, speed * 100 / 65535)) + rc = -EIO; + } else + rc = -EINVAL; + break; + default: rc = -ENXIO; } @@ -8654,7 +8694,11 @@ static ssize_t fan_fan1_input_show(struct device *dev, if (res < 0) return res; - return sysfs_emit(buf, "%u\n", speed); + /* Check for fan speeds displayed in hexadecimal */ + if (!ecfw_with_fan_dec_rpm) + return sysfs_emit(buf, "%u\n", speed); + else + return sysfs_emit(buf, "%x\n", speed); } static DEVICE_ATTR(fan1_input, S_IRUGO, fan_fan1_input_show, NULL); @@ -8671,7 +8715,11 @@ static ssize_t fan_fan2_input_show(struct device *dev, if (res < 0) return res; - return sysfs_emit(buf, "%u\n", speed); + /* Check for fan speeds displayed in hexadecimal */ + if (!ecfw_with_fan_dec_rpm) + return sysfs_emit(buf, "%u\n", speed); + else + return sysfs_emit(buf, "%x\n", speed); } static DEVICE_ATTR(fan2_input, S_IRUGO, fan_fan2_input_show, NULL); @@ -8747,6 +8795,8 @@ static const struct attribute_group fan_driver_attr_group = { #define TPACPI_FAN_2CTL 0x0004 /* selects fan2 control */ #define TPACPI_FAN_NOFAN 0x0008 /* no fan available */ #define TPACPI_FAN_NS 0x0010 /* For EC with non-Standard register addresses */ +#define TPACPI_FAN_DECRPM 0x0020 /* For ECFW's with RPM in register as decimal */ +#define TPACPI_FAN_TPR 0x0040 /* Fan speed is in Ticks Per Revolution */ static const struct tpacpi_quirk fan_quirk_table[] __initconst = { TPACPI_QEC_IBM('1', 'Y', TPACPI_FAN_Q1), @@ -8767,7 +8817,16 @@ static const struct tpacpi_quirk fan_quirk_table[] __initconst = { TPACPI_Q_LNV3('N', '3', '7', TPACPI_FAN_2CTL), /* T15g (2nd gen) */ TPACPI_Q_LNV3('R', '1', 'F', TPACPI_FAN_NS), /* L13 Yoga Gen 2 */ TPACPI_Q_LNV3('N', '2', 'U', TPACPI_FAN_NS), /* X13 Yoga Gen 2*/ + TPACPI_Q_LNV3('R', '0', 'R', TPACPI_FAN_NS), /* L380 */ + TPACPI_Q_LNV3('R', '1', '5', TPACPI_FAN_NS), /* L13 Yoga Gen 1 */ + TPACPI_Q_LNV3('R', '1', '0', TPACPI_FAN_NS), /* L390 */ + TPACPI_Q_LNV3('N', '2', 'L', TPACPI_FAN_NS), /* X13 Yoga Gen 1 */ + TPACPI_Q_LNV3('R', '0', 'T', TPACPI_FAN_NS), /* 11e Gen5 GL */ + TPACPI_Q_LNV3('R', '1', 'D', TPACPI_FAN_NS), /* 11e Gen5 GL-R */ + TPACPI_Q_LNV3('R', '0', 'V', TPACPI_FAN_NS), /* 11e Gen5 KL-Y */ TPACPI_Q_LNV3('N', '1', 'O', TPACPI_FAN_NOFAN), /* X1 Tablet (2nd gen) */ + TPACPI_Q_LNV3('R', '0', 'Q', TPACPI_FAN_DECRPM),/* L480 */ + TPACPI_Q_LNV('8', 'F', TPACPI_FAN_TPR), /* ThinkPad x120e */ }; static int __init fan_init(struct ibm_init_struct *iibm) @@ -8792,6 +8851,10 @@ static int __init fan_init(struct ibm_init_struct *iibm) TPACPI_ACPIHANDLE_INIT(gfan); TPACPI_ACPIHANDLE_INIT(sfan); } + if (tpacpi_is_lenovo()) { + TPACPI_ACPIHANDLE_INIT(fang); + TPACPI_ACPIHANDLE_INIT(fanw); + } quirks = tpacpi_check_quirks(fan_quirk_table, ARRAY_SIZE(fan_quirk_table)); @@ -8808,9 +8871,19 @@ static int __init fan_init(struct ibm_init_struct *iibm) tp_features.fan_ctrl_status_undef = 1; } + /* Check for the EC/BIOS with RPM reported in decimal*/ + if (quirks & TPACPI_FAN_DECRPM) { + pr_info("ECFW with fan RPM as decimal in EC register\n"); + ecfw_with_fan_dec_rpm = 1; + tp_features.fan_ctrl_status_undef = 1; + } + if (gfan_handle) { /* 570, 600e/x, 770e, 770x */ fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN; + } else if (fang_handle) { + /* E531 */ + fan_status_access_mode = TPACPI_FAN_RD_ACPI_FANG; } else { /* all other ThinkPads: note that even old-style * ThinkPad ECs supports the fan control register */ @@ -8824,6 +8897,8 @@ static int __init fan_init(struct ibm_init_struct *iibm) if (quirks & TPACPI_FAN_Q1) fan_quirk1_setup(); + if (quirks & TPACPI_FAN_TPR) + fan_speed_in_tpr = true; /* Try and probe the 2nd fan */ tp_features.second_fan = 1; /* needed for get_speed to work */ res = fan2_get_speed(&speed); @@ -8857,6 +8932,11 @@ static int __init fan_init(struct ibm_init_struct *iibm) fan_control_access_mode = TPACPI_FAN_WR_ACPI_SFAN; fan_control_commands |= TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_ENABLE; + } else if (fanw_handle) { + /* E531 */ + fan_control_access_mode = TPACPI_FAN_WR_ACPI_FANW; + fan_control_commands |= + TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_SPEED | TPACPI_FAN_CMD_ENABLE; } else { if (!gfan_handle) { /* gfan without sfan means no fan control */ @@ -9008,6 +9088,7 @@ static int fan_read(struct seq_file *m) case TPACPI_FAN_RD_TPEC_NS: case TPACPI_FAN_RD_TPEC: + case TPACPI_FAN_RD_ACPI_FANG: /* all except 570, 600e/x, 770e, 770x */ rc = fan_get_status_safe(&status); if (rc) @@ -9019,7 +9100,11 @@ static int fan_read(struct seq_file *m) if (rc < 0) return rc; - seq_printf(m, "speed:\t\t%d\n", speed); + /* Check for fan speeds displayed in hexadecimal */ + if (!ecfw_with_fan_dec_rpm) + seq_printf(m, "speed:\t\t%d\n", speed); + else + seq_printf(m, "speed:\t\t%x\n", speed); if (fan_status_access_mode == TPACPI_FAN_RD_TPEC_NS) { /* @@ -9028,7 +9113,7 @@ static int fan_read(struct seq_file *m) * No other levels settings available */ seq_printf(m, "level:\t\t%s\n", status & FAN_NS_CTRL ? "unknown" : "auto"); - } else { + } else if (fan_status_access_mode == TPACPI_FAN_RD_TPEC) { if (status & TP_EC_FAN_FULLSPEED) /* Disengaged mode takes precedence */ seq_printf(m, "level:\t\tdisengaged\n"); @@ -9285,7 +9370,6 @@ static int mute_led_init(struct ibm_init_struct *iibm) continue; } - mute_led_cdev[i].brightness = ledtrig_audio_get(i); err = led_classdev_register(&tpacpi_pdev->dev, &mute_led_cdev[i]); if (err < 0) { while (i--) @@ -9718,7 +9802,7 @@ static ssize_t tpacpi_battery_show(int what, battery = BAT_PRIMARY; if (tpacpi_battery_get(what, battery, &ret)) return -ENODEV; - return sprintf(buf, "%d\n", ret); + return sysfs_emit(buf, "%d\n", ret); } static ssize_t charge_control_start_threshold_show(struct device *device, @@ -9888,6 +9972,7 @@ static const struct tpacpi_quirk battery_quirk_table[] __initconst = { * Individual addressing is broken on models that expose the * primary battery as BAT1. */ + TPACPI_Q_LNV('G', '8', true), /* ThinkPad X131e */ TPACPI_Q_LNV('8', 'F', true), /* Thinkpad X120e */ TPACPI_Q_LNV('J', '7', true), /* B5400 */ TPACPI_Q_LNV('J', 'I', true), /* Thinkpad 11e */ @@ -10247,6 +10332,10 @@ static struct ibm_struct proxsensor_driver_data = { #define DYTC_MODE_PSC_BALANCE 5 /* Default mode aka balanced */ #define DYTC_MODE_PSC_PERFORM 7 /* High power mode aka performance */ +#define DYTC_MODE_PSCV9_LOWPOWER 1 /* Low power mode */ +#define DYTC_MODE_PSCV9_BALANCE 3 /* Default mode aka balanced */ +#define DYTC_MODE_PSCV9_PERFORM 4 /* High power mode aka performance */ + #define DYTC_ERR_MASK 0xF /* Bits 0-3 in cmd result are the error result */ #define DYTC_ERR_SUCCESS 1 /* CMD completed successful */ @@ -10267,6 +10356,10 @@ static int dytc_capabilities; static bool dytc_mmc_get_available; static int profile_force; +static int platform_psc_profile_lowpower = DYTC_MODE_PSC_LOWPOWER; +static int platform_psc_profile_balanced = DYTC_MODE_PSC_BALANCE; +static int platform_psc_profile_performance = DYTC_MODE_PSC_PERFORM; + static int convert_dytc_to_profile(int funcmode, int dytcmode, enum platform_profile_option *profile) { @@ -10288,19 +10381,15 @@ static int convert_dytc_to_profile(int funcmode, int dytcmode, } return 0; case DYTC_FUNCTION_PSC: - switch (dytcmode) { - case DYTC_MODE_PSC_LOWPOWER: + if (dytcmode == platform_psc_profile_lowpower) *profile = PLATFORM_PROFILE_LOW_POWER; - break; - case DYTC_MODE_PSC_BALANCE: + else if (dytcmode == platform_psc_profile_balanced) *profile = PLATFORM_PROFILE_BALANCED; - break; - case DYTC_MODE_PSC_PERFORM: + else if (dytcmode == platform_psc_profile_performance) *profile = PLATFORM_PROFILE_PERFORMANCE; - break; - default: /* Unknown mode */ + else return -EINVAL; - } + return 0; case DYTC_FUNCTION_AMT: /* For now return balanced. It's the closest we have to 'auto' */ @@ -10321,19 +10410,19 @@ static int convert_profile_to_dytc(enum platform_profile_option profile, int *pe if (dytc_capabilities & BIT(DYTC_FC_MMC)) *perfmode = DYTC_MODE_MMC_LOWPOWER; else if (dytc_capabilities & BIT(DYTC_FC_PSC)) - *perfmode = DYTC_MODE_PSC_LOWPOWER; + *perfmode = platform_psc_profile_lowpower; break; case PLATFORM_PROFILE_BALANCED: if (dytc_capabilities & BIT(DYTC_FC_MMC)) *perfmode = DYTC_MODE_MMC_BALANCE; else if (dytc_capabilities & BIT(DYTC_FC_PSC)) - *perfmode = DYTC_MODE_PSC_BALANCE; + *perfmode = platform_psc_profile_balanced; break; case PLATFORM_PROFILE_PERFORMANCE: if (dytc_capabilities & BIT(DYTC_FC_MMC)) *perfmode = DYTC_MODE_MMC_PERFORM; else if (dytc_capabilities & BIT(DYTC_FC_PSC)) - *perfmode = DYTC_MODE_PSC_PERFORM; + *perfmode = platform_psc_profile_performance; break; default: /* Unknown profile */ return -EOPNOTSUPP; @@ -10345,7 +10434,7 @@ static int convert_profile_to_dytc(enum platform_profile_option profile, int *pe * dytc_profile_get: Function to register with platform_profile * handler. Returns current platform profile. */ -static int dytc_profile_get(struct platform_profile_handler *pprof, +static int dytc_profile_get(struct device *dev, enum platform_profile_option *profile) { *profile = dytc_current_profile; @@ -10420,7 +10509,7 @@ static int dytc_cql_command(int command, int *output) * dytc_profile_set: Function to register with platform_profile * handler. Sets current platform profile. */ -static int dytc_profile_set(struct platform_profile_handler *pprof, +static int dytc_profile_set(struct device *dev, enum platform_profile_option profile) { int perfmode; @@ -10469,6 +10558,21 @@ unlock: return err; } +static int dytc_profile_probe(void *drvdata, unsigned long *choices) +{ + set_bit(PLATFORM_PROFILE_LOW_POWER, choices); + set_bit(PLATFORM_PROFILE_BALANCED, choices); + set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); + + return 0; +} + +static const struct platform_profile_ops dytc_profile_ops = { + .probe = dytc_profile_probe, + .profile_get = dytc_profile_get, + .profile_set = dytc_profile_set, +}; + static void dytc_profile_refresh(void) { enum platform_profile_option profile; @@ -10497,24 +10601,14 @@ static void dytc_profile_refresh(void) err = convert_dytc_to_profile(funcmode, perfmode, &profile); if (!err && profile != dytc_current_profile) { dytc_current_profile = profile; - platform_profile_notify(); + platform_profile_notify(tpacpi_pprof); } } -static struct platform_profile_handler dytc_profile = { - .profile_get = dytc_profile_get, - .profile_set = dytc_profile_set, -}; - static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) { int err, output; - /* Setup supported modes */ - set_bit(PLATFORM_PROFILE_LOW_POWER, dytc_profile.choices); - set_bit(PLATFORM_PROFILE_BALANCED, dytc_profile.choices); - set_bit(PLATFORM_PROFILE_PERFORMANCE, dytc_profile.choices); - err = dytc_command(DYTC_CMD_QUERY, &output); if (err) return err; @@ -10522,6 +10616,7 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) if (output & BIT(DYTC_QUERY_ENABLE_BIT)) dytc_version = (output >> DYTC_QUERY_REV_BIT) & 0xF; + dbg_printk(TPACPI_DBG_INIT, "DYTC version %d\n", dytc_version); /* Check DYTC is enabled and supports mode setting */ if (dytc_version < 5) return -ENODEV; @@ -10560,6 +10655,11 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) } } else if (dytc_capabilities & BIT(DYTC_FC_PSC)) { /* PSC MODE */ pr_debug("PSC is supported\n"); + if (dytc_version >= 9) { /* update profiles for DYTC 9 and up */ + platform_psc_profile_lowpower = DYTC_MODE_PSCV9_LOWPOWER; + platform_psc_profile_balanced = DYTC_MODE_PSCV9_BALANCE; + platform_psc_profile_performance = DYTC_MODE_PSCV9_PERFORM; + } } else { dbg_printk(TPACPI_DBG_INIT, "No DYTC support available\n"); return -ENODEV; @@ -10569,12 +10669,13 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) "DYTC version %d: thermal mode available\n", dytc_version); /* Create platform_profile structure and register */ - err = platform_profile_register(&dytc_profile); + tpacpi_pprof = platform_profile_register(&tpacpi_pdev->dev, "thinkpad-acpi-profile", + NULL, &dytc_profile_ops); /* * If for some reason platform_profiles aren't enabled * don't quit terminally. */ - if (err) + if (IS_ERR(tpacpi_pprof)) return -ENODEV; /* Ensure initial values are correct */ @@ -10589,7 +10690,8 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) static void dytc_profile_exit(void) { - platform_profile_remove(); + if (!IS_ERR_OR_NULL(tpacpi_pprof)) + platform_profile_remove(tpacpi_pprof); } static struct ibm_struct dytc_profile_driver_data = { @@ -10920,13 +11022,7 @@ static struct ibm_struct auxmac_data = { .name = "auxmac", }; -static ssize_t auxmac_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - return sysfs_emit(buf, "%s\n", auxmac); -} -static DEVICE_ATTR_RO(auxmac); +static DEVICE_STRING_ATTR_RO(auxmac, 0444, auxmac); static umode_t auxmac_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) @@ -10935,7 +11031,7 @@ static umode_t auxmac_attr_is_visible(struct kobject *kobj, } static struct attribute *auxmac_attributes[] = { - &dev_attr_auxmac.attr, + &dev_attr_auxmac.attr.attr, NULL }; @@ -11055,76 +11151,94 @@ static struct platform_driver tpacpi_hwmon_pdriver = { * HKEY event callout for other subdrivers go here * (yes, it is ugly, but it is quick, safe, and gets the job done */ -static void tpacpi_driver_event(const unsigned int hkey_event) +static bool tpacpi_driver_event(const unsigned int hkey_event) { - if (ibm_backlight_device) { - switch (hkey_event) { - case TP_HKEY_EV_BRGHT_UP: - case TP_HKEY_EV_BRGHT_DOWN: + switch (hkey_event) { + case TP_HKEY_EV_BRGHT_UP: + case TP_HKEY_EV_BRGHT_DOWN: + if (ibm_backlight_device) tpacpi_brightness_notify_change(); - } - } - if (alsa_card) { - switch (hkey_event) { - case TP_HKEY_EV_VOL_UP: - case TP_HKEY_EV_VOL_DOWN: - case TP_HKEY_EV_VOL_MUTE: + /* + * Key press events are suppressed by default hotkey_user_mask + * and should still be reported if explicitly requested. + */ + return false; + case TP_HKEY_EV_VOL_UP: + case TP_HKEY_EV_VOL_DOWN: + case TP_HKEY_EV_VOL_MUTE: + if (alsa_card) volume_alsa_notify_change(); - } - } - if (tp_features.kbdlight && hkey_event == TP_HKEY_EV_KBD_LIGHT) { - enum led_brightness brightness; - mutex_lock(&kbdlight_mutex); + /* Key events are suppressed by default hotkey_user_mask */ + return false; + case TP_HKEY_EV_KBD_LIGHT: + if (tp_features.kbdlight) { + enum led_brightness brightness; - /* - * Check the brightness actually changed, setting the brightness - * through kbdlight_set_level() also triggers this event. - */ - brightness = kbdlight_sysfs_get(NULL); - if (kbdlight_brightness != brightness) { - kbdlight_brightness = brightness; - led_classdev_notify_brightness_hw_changed( - &tpacpi_led_kbdlight.led_classdev, brightness); - } + mutex_lock(&kbdlight_mutex); - mutex_unlock(&kbdlight_mutex); - } + /* + * Check the brightness actually changed, setting the brightness + * through kbdlight_set_level() also triggers this event. + */ + brightness = kbdlight_sysfs_get(NULL); + if (kbdlight_brightness != brightness) { + kbdlight_brightness = brightness; + led_classdev_notify_brightness_hw_changed( + &tpacpi_led_kbdlight.led_classdev, brightness); + } - if (hkey_event == TP_HKEY_EV_THM_CSM_COMPLETED) { + mutex_unlock(&kbdlight_mutex); + } + /* Key events are suppressed by default hotkey_user_mask */ + return false; + case TP_HKEY_EV_DFR_CHANGE_ROW: + adaptive_keyboard_change_row(); + return true; + case TP_HKEY_EV_DFR_S_QUICKVIEW_ROW: + adaptive_keyboard_s_quickview_row(); + return true; + case TP_HKEY_EV_THM_CSM_COMPLETED: lapsensor_refresh(); /* If we are already accessing DYTC then skip dytc update */ if (!atomic_add_unless(&dytc_ignore_event, -1, 0)) dytc_profile_refresh(); - } - - if (lcdshadow_dev && hkey_event == TP_HKEY_EV_PRIVACYGUARD_TOGGLE) { - enum drm_privacy_screen_status old_hw_state; - bool changed; - - mutex_lock(&lcdshadow_dev->lock); - old_hw_state = lcdshadow_dev->hw_state; - lcdshadow_get_hw_state(lcdshadow_dev); - changed = lcdshadow_dev->hw_state != old_hw_state; - mutex_unlock(&lcdshadow_dev->lock); - if (changed) - drm_privacy_screen_call_notifier_chain(lcdshadow_dev); - } - if (hkey_event == TP_HKEY_EV_AMT_TOGGLE) { + return true; + case TP_HKEY_EV_PRIVACYGUARD_TOGGLE: + if (lcdshadow_dev) { + enum drm_privacy_screen_status old_hw_state; + bool changed; + + mutex_lock(&lcdshadow_dev->lock); + old_hw_state = lcdshadow_dev->hw_state; + lcdshadow_get_hw_state(lcdshadow_dev); + changed = lcdshadow_dev->hw_state != old_hw_state; + mutex_unlock(&lcdshadow_dev->lock); + + if (changed) + drm_privacy_screen_call_notifier_chain(lcdshadow_dev); + } + return true; + case TP_HKEY_EV_AMT_TOGGLE: /* If we're enabling AMT we need to force balanced mode */ if (!dytc_amt_active) /* This will also set AMT mode enabled */ dytc_profile_set(NULL, PLATFORM_PROFILE_BALANCED); else dytc_control_amt(!dytc_amt_active); - } -} + return true; + case TP_HKEY_EV_DOUBLETAP_TOGGLE: + tp_features.trackpoint_doubletap = !tp_features.trackpoint_doubletap; + return true; + case TP_HKEY_EV_PROFILE_TOGGLE: + case TP_HKEY_EV_PROFILE_TOGGLE2: + platform_profile_cycle(); + return true; + } -static void hotkey_driver_event(const unsigned int scancode) -{ - tpacpi_driver_event(TP_HKEY_EV_HOTKEY_BASE + scancode); + return false; } /* --------------------------------------------------------------------- */ @@ -11599,7 +11713,7 @@ static int __init set_ibm_param(const char *val, const struct kernel_param *kp) if (strcmp(ibm->name, kp->name) == 0 && ibm->write) { if (strlen(val) > sizeof(ibms_init[i].param) - 1) return -ENOSPC; - strcpy(ibms_init[i].param, val); + strscpy(ibms_init[i].param, val); return 0; } } @@ -11727,7 +11841,6 @@ static void thinkpad_acpi_module_exit(void) input_unregister_device(tpacpi_inputdev); else input_free_device(tpacpi_inputdev); - kfree(hotkey_keycode_map); } if (tpacpi_sensors_pdev) |