116216333SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 257ac3b05SIke Panhc /* 3a4b5a279SIke Panhc * ideapad-laptop.c - Lenovo IdeaPad ACPI Extras 457ac3b05SIke Panhc * 557ac3b05SIke Panhc * Copyright © 2010 Intel Corporation 657ac3b05SIke Panhc * Copyright © 2010 David Woodhouse <dwmw2@infradead.org> 757ac3b05SIke Panhc */ 857ac3b05SIke Panhc 99ab23989SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 109ab23989SJoe Perches 118b48463fSLv Zheng #include <linux/acpi.h> 127d38f034SBarnabás Pőcze #include <linux/backlight.h> 13*ecaa1867SStuart Hayhurst #include <linux/bitfield.h> 140c4915b6SBarnabás Pőcze #include <linux/bitops.h> 15503325f8SBarnabás Pőcze #include <linux/bug.h> 167d38f034SBarnabás Pőcze #include <linux/debugfs.h> 177d38f034SBarnabás Pőcze #include <linux/device.h> 187d38f034SBarnabás Pőcze #include <linux/dmi.h> 197d38f034SBarnabás Pőcze #include <linux/fb.h> 207d38f034SBarnabás Pőcze #include <linux/i8042.h> 217d38f034SBarnabás Pőcze #include <linux/init.h> 22f63409aeSIke Panhc #include <linux/input.h> 23f63409aeSIke Panhc #include <linux/input/sparse-keymap.h> 247d38f034SBarnabás Pőcze #include <linux/kernel.h> 25503325f8SBarnabás Pőcze #include <linux/leds.h> 267d38f034SBarnabás Pőcze #include <linux/module.h> 277d38f034SBarnabás Pőcze #include <linux/platform_device.h> 287d38f034SBarnabás Pőcze #include <linux/platform_profile.h> 297d38f034SBarnabás Pőcze #include <linux/rfkill.h> 30773e3206SIke Panhc #include <linux/seq_file.h> 31d6b50889SBarnabás Pőcze #include <linux/sysfs.h> 327d38f034SBarnabás Pőcze #include <linux/types.h> 33f32e0241SPhilipp Jungkamp #include <linux/wmi.h> 340de0ab9aSAndrew Kallmeyer #include "ideapad-laptop.h" 357d38f034SBarnabás Pőcze 3626bff5f0SHans de Goede #include <acpi/video.h> 3757ac3b05SIke Panhc 38503325f8SBarnabás Pőcze #include <dt-bindings/leds/common.h> 39503325f8SBarnabás Pőcze 4065c7713aSBarnabás Pőcze #define IDEAPAD_RFKILL_DEV_NUM 3 4157ac3b05SIke Panhc 422be1dc21SIke Panhc enum { 430b765671SBarnabás Pőcze CFG_CAP_BT_BIT = 16, 440b765671SBarnabás Pőcze CFG_CAP_3G_BIT = 17, 450b765671SBarnabás Pőcze CFG_CAP_WIFI_BIT = 18, 460b765671SBarnabás Pőcze CFG_CAP_CAM_BIT = 19, 47be5dd7d8SEray Orçunus 48be5dd7d8SEray Orçunus /* 49be5dd7d8SEray Orçunus * These are OnScreenDisplay support bits that can be useful to determine 50be5dd7d8SEray Orçunus * whether a hotkey exists/should show OSD. But they aren't particularly 51be5dd7d8SEray Orçunus * meaningful since they were introduced later, i.e. 2010 IdeaPads 52be5dd7d8SEray Orçunus * don't have these, but they still have had OSD for hotkeys. 53be5dd7d8SEray Orçunus */ 54be5dd7d8SEray Orçunus CFG_OSD_NUMLK_BIT = 27, 55be5dd7d8SEray Orçunus CFG_OSD_CAPSLK_BIT = 28, 56be5dd7d8SEray Orçunus CFG_OSD_MICMUTE_BIT = 29, 57be5dd7d8SEray Orçunus CFG_OSD_TOUCHPAD_BIT = 30, 58be5dd7d8SEray Orçunus CFG_OSD_CAM_BIT = 31, 590b765671SBarnabás Pőcze }; 600b765671SBarnabás Pőcze 610b765671SBarnabás Pőcze enum { 620b765671SBarnabás Pőcze GBMD_CONSERVATION_STATE_BIT = 5, 630b765671SBarnabás Pőcze }; 640b765671SBarnabás Pőcze 650b765671SBarnabás Pőcze enum { 66b09aaa3fSBarnabás Pőcze SBMC_CONSERVATION_ON = 3, 67b09aaa3fSBarnabás Pőcze SBMC_CONSERVATION_OFF = 5, 680b765671SBarnabás Pőcze }; 690b765671SBarnabás Pőcze 700b765671SBarnabás Pőcze enum { 71503325f8SBarnabás Pőcze HALS_KBD_BL_SUPPORT_BIT = 4, 72503325f8SBarnabás Pőcze HALS_KBD_BL_STATE_BIT = 5, 736b49dea4SBarnabás Pőcze HALS_USB_CHARGING_SUPPORT_BIT = 6, 746b49dea4SBarnabás Pőcze HALS_USB_CHARGING_STATE_BIT = 7, 75392cbf0aSBarnabás Pőcze HALS_FNLOCK_SUPPORT_BIT = 9, 760b765671SBarnabás Pőcze HALS_FNLOCK_STATE_BIT = 10, 77392cbf0aSBarnabás Pőcze HALS_HOTKEYS_PRIMARY_BIT = 11, 780b765671SBarnabás Pőcze }; 790b765671SBarnabás Pőcze 800b765671SBarnabás Pőcze enum { 81503325f8SBarnabás Pőcze SALS_KBD_BL_ON = 0x8, 82503325f8SBarnabás Pőcze SALS_KBD_BL_OFF = 0x9, 836b49dea4SBarnabás Pőcze SALS_USB_CHARGING_ON = 0xa, 846b49dea4SBarnabás Pőcze SALS_USB_CHARGING_OFF = 0xb, 850b765671SBarnabás Pőcze SALS_FNLOCK_ON = 0xe, 860b765671SBarnabás Pőcze SALS_FNLOCK_OFF = 0xf, 87ade50296SHao Wei Tee }; 88ade50296SHao Wei Tee 89*ecaa1867SStuart Hayhurst /* 90*ecaa1867SStuart Hayhurst * These correspond to the number of supported states - 1 91*ecaa1867SStuart Hayhurst * Future keyboard types may need a new system, if there's a collision 92*ecaa1867SStuart Hayhurst * KBD_BL_TRISTATE_AUTO has no way to report or set the auto state 93*ecaa1867SStuart Hayhurst * so it effectively has 3 states, but needs to handle 4 94*ecaa1867SStuart Hayhurst */ 95*ecaa1867SStuart Hayhurst enum { 96*ecaa1867SStuart Hayhurst KBD_BL_STANDARD = 1, 97*ecaa1867SStuart Hayhurst KBD_BL_TRISTATE = 2, 98*ecaa1867SStuart Hayhurst KBD_BL_TRISTATE_AUTO = 3, 99*ecaa1867SStuart Hayhurst }; 100*ecaa1867SStuart Hayhurst 101*ecaa1867SStuart Hayhurst #define KBD_BL_QUERY_TYPE 0x1 102*ecaa1867SStuart Hayhurst #define KBD_BL_TRISTATE_TYPE 0x5 103*ecaa1867SStuart Hayhurst #define KBD_BL_TRISTATE_AUTO_TYPE 0x7 104*ecaa1867SStuart Hayhurst 105*ecaa1867SStuart Hayhurst #define KBD_BL_COMMAND_GET 0x2 106*ecaa1867SStuart Hayhurst #define KBD_BL_COMMAND_SET 0x3 107*ecaa1867SStuart Hayhurst #define KBD_BL_COMMAND_TYPE GENMASK(7, 4) 108*ecaa1867SStuart Hayhurst 109*ecaa1867SStuart Hayhurst #define KBD_BL_GET_BRIGHTNESS GENMASK(15, 1) 110*ecaa1867SStuart Hayhurst #define KBD_BL_SET_BRIGHTNESS GENMASK(19, 16) 111*ecaa1867SStuart Hayhurst 112*ecaa1867SStuart Hayhurst #define KBD_BL_KBLC_CHANGED_EVENT 12 113*ecaa1867SStuart Hayhurst 114eabe5339SJiaxun Yang struct ideapad_dytc_priv { 115eabe5339SJiaxun Yang enum platform_profile_option current_profile; 116eabe5339SJiaxun Yang struct platform_profile_handler pprof; 11765c7713aSBarnabás Pőcze struct mutex mutex; /* protects the DYTC interface */ 118eabe5339SJiaxun Yang struct ideapad_private *priv; 119eabe5339SJiaxun Yang }; 120eabe5339SJiaxun Yang 121331e0ea2SZhang Rui struct ideapad_rfk_priv { 122331e0ea2SZhang Rui int dev; 123331e0ea2SZhang Rui struct ideapad_private *priv; 124331e0ea2SZhang Rui }; 125331e0ea2SZhang Rui 12657ac3b05SIke Panhc struct ideapad_private { 127469f6434SZhang Rui struct acpi_device *adev; 128c1f73658SIke Panhc struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM]; 129331e0ea2SZhang Rui struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM]; 13098ee6919SIke Panhc struct platform_device *platform_device; 131f63409aeSIke Panhc struct input_dev *inputdev; 132a4ecbb8aSIke Panhc struct backlight_device *blightdev; 133eabe5339SJiaxun Yang struct ideapad_dytc_priv *dytc; 134773e3206SIke Panhc struct dentry *debug; 1353371f481SIke Panhc unsigned long cfg; 1365829f8a8SHans de Goede unsigned long r_touchpad_val; 1371c59de4aSBarnabás Pőcze struct { 1381c59de4aSBarnabás Pőcze bool conservation_mode : 1; 1391c59de4aSBarnabás Pőcze bool dytc : 1; 1401c59de4aSBarnabás Pőcze bool fan_mode : 1; 1411c59de4aSBarnabás Pőcze bool fn_lock : 1; 14281a5603aSArnav Rawat bool set_fn_lock_led : 1; 1431c59de4aSBarnabás Pőcze bool hw_rfkill_switch : 1; 144503325f8SBarnabás Pőcze bool kbd_bl : 1; 1451c59de4aSBarnabás Pőcze bool touchpad_ctrl_via_ec : 1; 146c69e7d84SHans de Goede bool ctrl_ps2_aux_port : 1; 1476b49dea4SBarnabás Pőcze bool usb_charging : 1; 1481c59de4aSBarnabás Pőcze } features; 149503325f8SBarnabás Pőcze struct { 150503325f8SBarnabás Pőcze bool initialized; 151*ecaa1867SStuart Hayhurst int type; 152503325f8SBarnabás Pőcze struct led_classdev led; 153503325f8SBarnabás Pőcze unsigned int last_brightness; 154503325f8SBarnabás Pőcze } kbd_bl; 15557ac3b05SIke Panhc }; 15657ac3b05SIke Panhc 157bfa97b7dSIke Panhc static bool no_bt_rfkill; 158bfa97b7dSIke Panhc module_param(no_bt_rfkill, bool, 0444); 159bfa97b7dSIke Panhc MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth."); 160bfa97b7dSIke Panhc 161a27a1e35SHans de Goede static bool allow_v4_dytc; 162a27a1e35SHans de Goede module_param(allow_v4_dytc, bool, 0444); 163b44fd994SHans de Goede MODULE_PARM_DESC(allow_v4_dytc, 164b44fd994SHans de Goede "Enable DYTC version 4 platform-profile support. " 165b44fd994SHans de Goede "If you need this please report this to: platform-driver-x86@vger.kernel.org"); 166b44fd994SHans de Goede 167b44fd994SHans de Goede static bool hw_rfkill_switch; 168b44fd994SHans de Goede module_param(hw_rfkill_switch, bool, 0444); 169b44fd994SHans de Goede MODULE_PARM_DESC(hw_rfkill_switch, 170b44fd994SHans de Goede "Enable rfkill support for laptops with a hw on/off wifi switch/slider. " 171b44fd994SHans de Goede "If you need this please report this to: platform-driver-x86@vger.kernel.org"); 172b44fd994SHans de Goede 173b44fd994SHans de Goede static bool set_fn_lock_led; 174b44fd994SHans de Goede module_param(set_fn_lock_led, bool, 0444); 175b44fd994SHans de Goede MODULE_PARM_DESC(set_fn_lock_led, 176b44fd994SHans de Goede "Enable driver based updates of the fn-lock LED on fn-lock changes. " 177b44fd994SHans de Goede "If you need this please report this to: platform-driver-x86@vger.kernel.org"); 178a27a1e35SHans de Goede 179c69e7d84SHans de Goede static bool ctrl_ps2_aux_port; 180c69e7d84SHans de Goede module_param(ctrl_ps2_aux_port, bool, 0444); 181c69e7d84SHans de Goede MODULE_PARM_DESC(ctrl_ps2_aux_port, 182c69e7d84SHans de Goede "Enable driver based PS/2 aux port en-/dis-abling on touchpad on/off toggle. " 183c69e7d84SHans de Goede "If you need this please report this to: platform-driver-x86@vger.kernel.org"); 184c69e7d84SHans de Goede 185301e0d76SHans de Goede static bool touchpad_ctrl_via_ec; 186301e0d76SHans de Goede module_param(touchpad_ctrl_via_ec, bool, 0444); 187301e0d76SHans de Goede MODULE_PARM_DESC(touchpad_ctrl_via_ec, 188301e0d76SHans de Goede "Enable registering a 'touchpad' sysfs-attribute which can be used to manually " 189301e0d76SHans de Goede "tell the EC to enable/disable the touchpad. This may not work on all models."); 190301e0d76SHans de Goede 19157ac3b05SIke Panhc /* 192f32e0241SPhilipp Jungkamp * shared data 193f32e0241SPhilipp Jungkamp */ 194f32e0241SPhilipp Jungkamp 195f32e0241SPhilipp Jungkamp static struct ideapad_private *ideapad_shared; 196f32e0241SPhilipp Jungkamp static DEFINE_MUTEX(ideapad_shared_mutex); 197f32e0241SPhilipp Jungkamp 198f32e0241SPhilipp Jungkamp static int ideapad_shared_init(struct ideapad_private *priv) 199f32e0241SPhilipp Jungkamp { 200f32e0241SPhilipp Jungkamp int ret; 201f32e0241SPhilipp Jungkamp 202f32e0241SPhilipp Jungkamp mutex_lock(&ideapad_shared_mutex); 203f32e0241SPhilipp Jungkamp 204f32e0241SPhilipp Jungkamp if (!ideapad_shared) { 205f32e0241SPhilipp Jungkamp ideapad_shared = priv; 206f32e0241SPhilipp Jungkamp ret = 0; 207f32e0241SPhilipp Jungkamp } else { 208f32e0241SPhilipp Jungkamp dev_warn(&priv->adev->dev, "found multiple platform devices\n"); 209f32e0241SPhilipp Jungkamp ret = -EINVAL; 210f32e0241SPhilipp Jungkamp } 211f32e0241SPhilipp Jungkamp 212f32e0241SPhilipp Jungkamp mutex_unlock(&ideapad_shared_mutex); 213f32e0241SPhilipp Jungkamp 214f32e0241SPhilipp Jungkamp return ret; 215f32e0241SPhilipp Jungkamp } 216f32e0241SPhilipp Jungkamp 217f32e0241SPhilipp Jungkamp static void ideapad_shared_exit(struct ideapad_private *priv) 218f32e0241SPhilipp Jungkamp { 219f32e0241SPhilipp Jungkamp mutex_lock(&ideapad_shared_mutex); 220f32e0241SPhilipp Jungkamp 221f32e0241SPhilipp Jungkamp if (ideapad_shared == priv) 222f32e0241SPhilipp Jungkamp ideapad_shared = NULL; 223f32e0241SPhilipp Jungkamp 224f32e0241SPhilipp Jungkamp mutex_unlock(&ideapad_shared_mutex); 225f32e0241SPhilipp Jungkamp } 226f32e0241SPhilipp Jungkamp 227f32e0241SPhilipp Jungkamp /* 22857ac3b05SIke Panhc * ACPI Helpers 22957ac3b05SIke Panhc */ 23057ac3b05SIke Panhc 231ff36b0d9SBarnabás Pőcze static int eval_int(acpi_handle handle, const char *name, unsigned long *res) 23257ac3b05SIke Panhc { 23357ac3b05SIke Panhc unsigned long long result; 234ade50296SHao Wei Tee acpi_status status; 235ade50296SHao Wei Tee 236ff36b0d9SBarnabás Pőcze status = acpi_evaluate_integer(handle, (char *)name, NULL, &result); 237ff36b0d9SBarnabás Pőcze if (ACPI_FAILURE(status)) 238ff36b0d9SBarnabás Pőcze return -EIO; 23965c7713aSBarnabás Pőcze 240ff36b0d9SBarnabás Pőcze *res = result; 24165c7713aSBarnabás Pőcze 242ff36b0d9SBarnabás Pőcze return 0; 243ff36b0d9SBarnabás Pőcze } 244ff36b0d9SBarnabás Pőcze 245ff36b0d9SBarnabás Pőcze static int exec_simple_method(acpi_handle handle, const char *name, unsigned long arg) 246ff36b0d9SBarnabás Pőcze { 247ff36b0d9SBarnabás Pőcze acpi_status status = acpi_execute_simple_method(handle, (char *)name, arg); 248ff36b0d9SBarnabás Pőcze 2497be193e3SBarnabás Pőcze return ACPI_FAILURE(status) ? -EIO : 0; 250ade50296SHao Wei Tee } 251ade50296SHao Wei Tee 252ff36b0d9SBarnabás Pőcze static int eval_gbmd(acpi_handle handle, unsigned long *res) 253eabe5339SJiaxun Yang { 254ff36b0d9SBarnabás Pőcze return eval_int(handle, "GBMD", res); 255ff36b0d9SBarnabás Pőcze } 256ff36b0d9SBarnabás Pőcze 257b09aaa3fSBarnabás Pőcze static int exec_sbmc(acpi_handle handle, unsigned long arg) 258ff36b0d9SBarnabás Pőcze { 259b09aaa3fSBarnabás Pőcze return exec_simple_method(handle, "SBMC", arg); 260ff36b0d9SBarnabás Pőcze } 261ff36b0d9SBarnabás Pőcze 262ff36b0d9SBarnabás Pőcze static int eval_hals(acpi_handle handle, unsigned long *res) 263ff36b0d9SBarnabás Pőcze { 264ff36b0d9SBarnabás Pőcze return eval_int(handle, "HALS", res); 265ff36b0d9SBarnabás Pőcze } 266ff36b0d9SBarnabás Pőcze 267ff36b0d9SBarnabás Pőcze static int exec_sals(acpi_handle handle, unsigned long arg) 268ff36b0d9SBarnabás Pőcze { 269ff36b0d9SBarnabás Pőcze return exec_simple_method(handle, "SALS", arg); 270ff36b0d9SBarnabás Pőcze } 271ff36b0d9SBarnabás Pőcze 272*ecaa1867SStuart Hayhurst static int exec_kblc(acpi_handle handle, unsigned long arg) 273*ecaa1867SStuart Hayhurst { 274*ecaa1867SStuart Hayhurst return exec_simple_method(handle, "KBLC", arg); 275*ecaa1867SStuart Hayhurst } 276*ecaa1867SStuart Hayhurst 277*ecaa1867SStuart Hayhurst static int eval_kblc(acpi_handle handle, unsigned long cmd, unsigned long *res) 278*ecaa1867SStuart Hayhurst { 279*ecaa1867SStuart Hayhurst return eval_int_with_arg(handle, "KBLC", cmd, res); 280*ecaa1867SStuart Hayhurst } 281*ecaa1867SStuart Hayhurst 282ff36b0d9SBarnabás Pőcze static int eval_dytc(acpi_handle handle, unsigned long cmd, unsigned long *res) 28357ac3b05SIke Panhc { 284ff36b0d9SBarnabás Pőcze return eval_int_with_arg(handle, "DYTC", cmd, res); 28557ac3b05SIke Panhc } 28657ac3b05SIke Panhc 287a4b5a279SIke Panhc /* 288773e3206SIke Panhc * debugfs 289773e3206SIke Panhc */ 290773e3206SIke Panhc static int debugfs_status_show(struct seq_file *s, void *data) 291773e3206SIke Panhc { 292331e0ea2SZhang Rui struct ideapad_private *priv = s->private; 293773e3206SIke Panhc unsigned long value; 294773e3206SIke Panhc 295331e0ea2SZhang Rui if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &value)) 2967553390dSBarnabás Pőcze seq_printf(s, "Backlight max: %lu\n", value); 297331e0ea2SZhang Rui if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL, &value)) 2987553390dSBarnabás Pőcze seq_printf(s, "Backlight now: %lu\n", value); 299331e0ea2SZhang Rui if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &value)) 3007553390dSBarnabás Pőcze seq_printf(s, "BL power value: %s (%lu)\n", value ? "on" : "off", value); 30165c7713aSBarnabás Pőcze 302ade50296SHao Wei Tee seq_puts(s, "=====================\n"); 303ade50296SHao Wei Tee 3047553390dSBarnabás Pőcze if (!read_ec_data(priv->adev->handle, VPCCMD_R_RF, &value)) 3057553390dSBarnabás Pőcze seq_printf(s, "Radio status: %s (%lu)\n", value ? "on" : "off", value); 3067553390dSBarnabás Pőcze if (!read_ec_data(priv->adev->handle, VPCCMD_R_WIFI, &value)) 3077553390dSBarnabás Pőcze seq_printf(s, "Wifi status: %s (%lu)\n", value ? "on" : "off", value); 3087553390dSBarnabás Pőcze if (!read_ec_data(priv->adev->handle, VPCCMD_R_BT, &value)) 3097553390dSBarnabás Pőcze seq_printf(s, "BT status: %s (%lu)\n", value ? "on" : "off", value); 3107553390dSBarnabás Pőcze if (!read_ec_data(priv->adev->handle, VPCCMD_R_3G, &value)) 3117553390dSBarnabás Pőcze seq_printf(s, "3G status: %s (%lu)\n", value ? "on" : "off", value); 31265c7713aSBarnabás Pőcze 3137553390dSBarnabás Pőcze seq_puts(s, "=====================\n"); 3147553390dSBarnabás Pőcze 3157553390dSBarnabás Pőcze if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value)) 3167553390dSBarnabás Pőcze seq_printf(s, "Touchpad status: %s (%lu)\n", value ? "on" : "off", value); 3177553390dSBarnabás Pőcze if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value)) 3187553390dSBarnabás Pőcze seq_printf(s, "Camera status: %s (%lu)\n", value ? "on" : "off", value); 31965c7713aSBarnabás Pőcze 3207553390dSBarnabás Pőcze seq_puts(s, "=====================\n"); 3217553390dSBarnabás Pőcze 3227553390dSBarnabás Pőcze if (!eval_gbmd(priv->adev->handle, &value)) 3237553390dSBarnabás Pőcze seq_printf(s, "GBMD: %#010lx\n", value); 3247553390dSBarnabás Pőcze if (!eval_hals(priv->adev->handle, &value)) 3257553390dSBarnabás Pőcze seq_printf(s, "HALS: %#010lx\n", value); 326773e3206SIke Panhc 327773e3206SIke Panhc return 0; 328773e3206SIke Panhc } 329334c4efdSAndy Shevchenko DEFINE_SHOW_ATTRIBUTE(debugfs_status); 330773e3206SIke Panhc 331773e3206SIke Panhc static int debugfs_cfg_show(struct seq_file *s, void *data) 332773e3206SIke Panhc { 333331e0ea2SZhang Rui struct ideapad_private *priv = s->private; 334331e0ea2SZhang Rui 33518227424SBarnabás Pőcze seq_printf(s, "_CFG: %#010lx\n\n", priv->cfg); 33618227424SBarnabás Pőcze 33718227424SBarnabás Pőcze seq_puts(s, "Capabilities:"); 3380b765671SBarnabás Pőcze if (test_bit(CFG_CAP_BT_BIT, &priv->cfg)) 33918227424SBarnabás Pőcze seq_puts(s, " bluetooth"); 3400b765671SBarnabás Pőcze if (test_bit(CFG_CAP_3G_BIT, &priv->cfg)) 34118227424SBarnabás Pőcze seq_puts(s, " 3G"); 3420b765671SBarnabás Pőcze if (test_bit(CFG_CAP_WIFI_BIT, &priv->cfg)) 34318227424SBarnabás Pőcze seq_puts(s, " wifi"); 3440b765671SBarnabás Pőcze if (test_bit(CFG_CAP_CAM_BIT, &priv->cfg)) 34518227424SBarnabás Pőcze seq_puts(s, " camera"); 34618227424SBarnabás Pőcze seq_puts(s, "\n"); 34718227424SBarnabás Pőcze 348be5dd7d8SEray Orçunus seq_puts(s, "OSD support:"); 349be5dd7d8SEray Orçunus if (test_bit(CFG_OSD_NUMLK_BIT, &priv->cfg)) 350be5dd7d8SEray Orçunus seq_puts(s, " num-lock"); 351be5dd7d8SEray Orçunus if (test_bit(CFG_OSD_CAPSLK_BIT, &priv->cfg)) 352be5dd7d8SEray Orçunus seq_puts(s, " caps-lock"); 353be5dd7d8SEray Orçunus if (test_bit(CFG_OSD_MICMUTE_BIT, &priv->cfg)) 354be5dd7d8SEray Orçunus seq_puts(s, " mic-mute"); 355be5dd7d8SEray Orçunus if (test_bit(CFG_OSD_TOUCHPAD_BIT, &priv->cfg)) 356be5dd7d8SEray Orçunus seq_puts(s, " touchpad"); 357be5dd7d8SEray Orçunus if (test_bit(CFG_OSD_CAM_BIT, &priv->cfg)) 358be5dd7d8SEray Orçunus seq_puts(s, " camera"); 359be5dd7d8SEray Orçunus seq_puts(s, "\n"); 360be5dd7d8SEray Orçunus 36118227424SBarnabás Pőcze seq_puts(s, "Graphics: "); 36218227424SBarnabás Pőcze switch (priv->cfg & 0x700) { 363773e3206SIke Panhc case 0x100: 36418227424SBarnabás Pőcze seq_puts(s, "Intel"); 365773e3206SIke Panhc break; 366773e3206SIke Panhc case 0x200: 36718227424SBarnabás Pőcze seq_puts(s, "ATI"); 368773e3206SIke Panhc break; 369773e3206SIke Panhc case 0x300: 37018227424SBarnabás Pőcze seq_puts(s, "Nvidia"); 371773e3206SIke Panhc break; 372773e3206SIke Panhc case 0x400: 37318227424SBarnabás Pőcze seq_puts(s, "Intel and ATI"); 374773e3206SIke Panhc break; 375773e3206SIke Panhc case 0x500: 37618227424SBarnabás Pőcze seq_puts(s, "Intel and Nvidia"); 377773e3206SIke Panhc break; 378773e3206SIke Panhc } 37918227424SBarnabás Pőcze seq_puts(s, "\n"); 380e1a39a44SBarnabás Pőcze 381773e3206SIke Panhc return 0; 382773e3206SIke Panhc } 383334c4efdSAndy Shevchenko DEFINE_SHOW_ATTRIBUTE(debugfs_cfg); 384773e3206SIke Panhc 38517f1bf38SGreg Kroah-Hartman static void ideapad_debugfs_init(struct ideapad_private *priv) 386773e3206SIke Panhc { 38717f1bf38SGreg Kroah-Hartman struct dentry *dir; 388773e3206SIke Panhc 38917f1bf38SGreg Kroah-Hartman dir = debugfs_create_dir("ideapad", NULL); 39017f1bf38SGreg Kroah-Hartman priv->debug = dir; 391773e3206SIke Panhc 39265c7713aSBarnabás Pőcze debugfs_create_file("cfg", 0444, dir, priv, &debugfs_cfg_fops); 39365c7713aSBarnabás Pőcze debugfs_create_file("status", 0444, dir, priv, &debugfs_status_fops); 394773e3206SIke Panhc } 395773e3206SIke Panhc 396773e3206SIke Panhc static void ideapad_debugfs_exit(struct ideapad_private *priv) 397773e3206SIke Panhc { 398773e3206SIke Panhc debugfs_remove_recursive(priv->debug); 399773e3206SIke Panhc priv->debug = NULL; 400773e3206SIke Panhc } 401773e3206SIke Panhc 402773e3206SIke Panhc /* 4033371f481SIke Panhc * sysfs 404a4b5a279SIke Panhc */ 40565c7713aSBarnabás Pőcze static ssize_t camera_power_show(struct device *dev, 40657ac3b05SIke Panhc struct device_attribute *attr, 40757ac3b05SIke Panhc char *buf) 40857ac3b05SIke Panhc { 409331e0ea2SZhang Rui struct ideapad_private *priv = dev_get_drvdata(dev); 41065c7713aSBarnabás Pőcze unsigned long result; 411c81f2410SBarnabás Pőcze int err; 41257ac3b05SIke Panhc 413c81f2410SBarnabás Pőcze err = read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &result); 414c81f2410SBarnabás Pőcze if (err) 415c81f2410SBarnabás Pőcze return err; 41665c7713aSBarnabás Pőcze 41700641c08SBarnabás Pőcze return sysfs_emit(buf, "%d\n", !!result); 41857ac3b05SIke Panhc } 41957ac3b05SIke Panhc 42065c7713aSBarnabás Pőcze static ssize_t camera_power_store(struct device *dev, 42157ac3b05SIke Panhc struct device_attribute *attr, 42257ac3b05SIke Panhc const char *buf, size_t count) 42357ac3b05SIke Panhc { 424331e0ea2SZhang Rui struct ideapad_private *priv = dev_get_drvdata(dev); 42500641c08SBarnabás Pőcze bool state; 426c81f2410SBarnabás Pőcze int err; 4270c7bbeb9SMaxim Mikityanskiy 42865c7713aSBarnabás Pőcze err = kstrtobool(buf, &state); 429c81f2410SBarnabás Pőcze if (err) 430c81f2410SBarnabás Pőcze return err; 4310c7bbeb9SMaxim Mikityanskiy 43265c7713aSBarnabás Pőcze err = write_ec_cmd(priv->adev->handle, VPCCMD_W_CAMERA, state); 43365c7713aSBarnabás Pőcze if (err) 43465c7713aSBarnabás Pőcze return err; 4350c7bbeb9SMaxim Mikityanskiy 4360c7bbeb9SMaxim Mikityanskiy return count; 4370c7bbeb9SMaxim Mikityanskiy } 4380c7bbeb9SMaxim Mikityanskiy 43965c7713aSBarnabás Pőcze static DEVICE_ATTR_RW(camera_power); 44036ac0d43SRitesh Raj Sarraf 441ade50296SHao Wei Tee static ssize_t conservation_mode_show(struct device *dev, 442ade50296SHao Wei Tee struct device_attribute *attr, 443ade50296SHao Wei Tee char *buf) 444ade50296SHao Wei Tee { 445ade50296SHao Wei Tee struct ideapad_private *priv = dev_get_drvdata(dev); 446ade50296SHao Wei Tee unsigned long result; 447c81f2410SBarnabás Pőcze int err; 448ade50296SHao Wei Tee 449ff36b0d9SBarnabás Pőcze err = eval_gbmd(priv->adev->handle, &result); 450c81f2410SBarnabás Pőcze if (err) 451c81f2410SBarnabás Pőcze return err; 45265c7713aSBarnabás Pőcze 4530b765671SBarnabás Pőcze return sysfs_emit(buf, "%d\n", !!test_bit(GBMD_CONSERVATION_STATE_BIT, &result)); 454ade50296SHao Wei Tee } 455ade50296SHao Wei Tee 456ade50296SHao Wei Tee static ssize_t conservation_mode_store(struct device *dev, 457ade50296SHao Wei Tee struct device_attribute *attr, 458ade50296SHao Wei Tee const char *buf, size_t count) 459ade50296SHao Wei Tee { 460ade50296SHao Wei Tee struct ideapad_private *priv = dev_get_drvdata(dev); 461ade50296SHao Wei Tee bool state; 46265c7713aSBarnabás Pőcze int err; 463ade50296SHao Wei Tee 46465c7713aSBarnabás Pőcze err = kstrtobool(buf, &state); 46565c7713aSBarnabás Pőcze if (err) 46665c7713aSBarnabás Pőcze return err; 467ade50296SHao Wei Tee 468b09aaa3fSBarnabás Pőcze err = exec_sbmc(priv->adev->handle, state ? SBMC_CONSERVATION_ON : SBMC_CONSERVATION_OFF); 46965c7713aSBarnabás Pőcze if (err) 47065c7713aSBarnabás Pőcze return err; 47165c7713aSBarnabás Pőcze 472ade50296SHao Wei Tee return count; 473ade50296SHao Wei Tee } 474ade50296SHao Wei Tee 475ade50296SHao Wei Tee static DEVICE_ATTR_RW(conservation_mode); 476ade50296SHao Wei Tee 47765c7713aSBarnabás Pőcze static ssize_t fan_mode_show(struct device *dev, 47865c7713aSBarnabás Pőcze struct device_attribute *attr, 47965c7713aSBarnabás Pőcze char *buf) 48065c7713aSBarnabás Pőcze { 48165c7713aSBarnabás Pőcze struct ideapad_private *priv = dev_get_drvdata(dev); 48265c7713aSBarnabás Pőcze unsigned long result; 48365c7713aSBarnabás Pőcze int err; 48465c7713aSBarnabás Pőcze 48565c7713aSBarnabás Pőcze err = read_ec_data(priv->adev->handle, VPCCMD_R_FAN, &result); 48665c7713aSBarnabás Pőcze if (err) 48765c7713aSBarnabás Pőcze return err; 48865c7713aSBarnabás Pőcze 48965c7713aSBarnabás Pőcze return sysfs_emit(buf, "%lu\n", result); 49065c7713aSBarnabás Pőcze } 49165c7713aSBarnabás Pőcze 49265c7713aSBarnabás Pőcze static ssize_t fan_mode_store(struct device *dev, 49365c7713aSBarnabás Pőcze struct device_attribute *attr, 49465c7713aSBarnabás Pőcze const char *buf, size_t count) 49565c7713aSBarnabás Pőcze { 49665c7713aSBarnabás Pőcze struct ideapad_private *priv = dev_get_drvdata(dev); 49765c7713aSBarnabás Pőcze unsigned int state; 49865c7713aSBarnabás Pőcze int err; 49965c7713aSBarnabás Pőcze 50065c7713aSBarnabás Pőcze err = kstrtouint(buf, 0, &state); 50165c7713aSBarnabás Pőcze if (err) 50265c7713aSBarnabás Pőcze return err; 50365c7713aSBarnabás Pőcze 50465c7713aSBarnabás Pőcze if (state > 4 || state == 3) 50565c7713aSBarnabás Pőcze return -EINVAL; 50665c7713aSBarnabás Pőcze 50765c7713aSBarnabás Pőcze err = write_ec_cmd(priv->adev->handle, VPCCMD_W_FAN, state); 50865c7713aSBarnabás Pőcze if (err) 50965c7713aSBarnabás Pőcze return err; 51065c7713aSBarnabás Pőcze 51165c7713aSBarnabás Pőcze return count; 51265c7713aSBarnabás Pőcze } 51365c7713aSBarnabás Pőcze 51465c7713aSBarnabás Pőcze static DEVICE_ATTR_RW(fan_mode); 51565c7713aSBarnabás Pőcze 51640760717SOleg Keri static ssize_t fn_lock_show(struct device *dev, 51740760717SOleg Keri struct device_attribute *attr, 51840760717SOleg Keri char *buf) 51940760717SOleg Keri { 52040760717SOleg Keri struct ideapad_private *priv = dev_get_drvdata(dev); 521ff36b0d9SBarnabás Pőcze unsigned long hals; 52265c7713aSBarnabás Pőcze int err; 52340760717SOleg Keri 52465c7713aSBarnabás Pőcze err = eval_hals(priv->adev->handle, &hals); 52565c7713aSBarnabás Pőcze if (err) 52665c7713aSBarnabás Pőcze return err; 52740760717SOleg Keri 528ff36b0d9SBarnabás Pőcze return sysfs_emit(buf, "%d\n", !!test_bit(HALS_FNLOCK_STATE_BIT, &hals)); 52940760717SOleg Keri } 53040760717SOleg Keri 53140760717SOleg Keri static ssize_t fn_lock_store(struct device *dev, 53240760717SOleg Keri struct device_attribute *attr, 53340760717SOleg Keri const char *buf, size_t count) 53440760717SOleg Keri { 53540760717SOleg Keri struct ideapad_private *priv = dev_get_drvdata(dev); 53640760717SOleg Keri bool state; 53765c7713aSBarnabás Pőcze int err; 53840760717SOleg Keri 53965c7713aSBarnabás Pőcze err = kstrtobool(buf, &state); 54065c7713aSBarnabás Pőcze if (err) 54165c7713aSBarnabás Pőcze return err; 54240760717SOleg Keri 54365c7713aSBarnabás Pőcze err = exec_sals(priv->adev->handle, state ? SALS_FNLOCK_ON : SALS_FNLOCK_OFF); 54465c7713aSBarnabás Pőcze if (err) 54565c7713aSBarnabás Pőcze return err; 54665c7713aSBarnabás Pőcze 54740760717SOleg Keri return count; 54840760717SOleg Keri } 54940760717SOleg Keri 55040760717SOleg Keri static DEVICE_ATTR_RW(fn_lock); 55140760717SOleg Keri 55265c7713aSBarnabás Pőcze static ssize_t touchpad_show(struct device *dev, 55365c7713aSBarnabás Pőcze struct device_attribute *attr, 55465c7713aSBarnabás Pőcze char *buf) 55565c7713aSBarnabás Pőcze { 55665c7713aSBarnabás Pőcze struct ideapad_private *priv = dev_get_drvdata(dev); 55765c7713aSBarnabás Pőcze unsigned long result; 55865c7713aSBarnabás Pőcze int err; 55965c7713aSBarnabás Pőcze 56065c7713aSBarnabás Pőcze err = read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &result); 56165c7713aSBarnabás Pőcze if (err) 56265c7713aSBarnabás Pőcze return err; 56365c7713aSBarnabás Pőcze 5645829f8a8SHans de Goede priv->r_touchpad_val = result; 5655829f8a8SHans de Goede 56665c7713aSBarnabás Pőcze return sysfs_emit(buf, "%d\n", !!result); 56765c7713aSBarnabás Pőcze } 56865c7713aSBarnabás Pőcze 56965c7713aSBarnabás Pőcze static ssize_t touchpad_store(struct device *dev, 57065c7713aSBarnabás Pőcze struct device_attribute *attr, 57165c7713aSBarnabás Pőcze const char *buf, size_t count) 57265c7713aSBarnabás Pőcze { 57365c7713aSBarnabás Pőcze struct ideapad_private *priv = dev_get_drvdata(dev); 57465c7713aSBarnabás Pőcze bool state; 57565c7713aSBarnabás Pőcze int err; 57665c7713aSBarnabás Pőcze 57765c7713aSBarnabás Pőcze err = kstrtobool(buf, &state); 57865c7713aSBarnabás Pőcze if (err) 57965c7713aSBarnabás Pőcze return err; 58065c7713aSBarnabás Pőcze 58165c7713aSBarnabás Pőcze err = write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, state); 58265c7713aSBarnabás Pőcze if (err) 58365c7713aSBarnabás Pőcze return err; 58465c7713aSBarnabás Pőcze 5855829f8a8SHans de Goede priv->r_touchpad_val = state; 5865829f8a8SHans de Goede 58765c7713aSBarnabás Pőcze return count; 58865c7713aSBarnabás Pőcze } 58965c7713aSBarnabás Pőcze 59065c7713aSBarnabás Pőcze static DEVICE_ATTR_RW(touchpad); 59140760717SOleg Keri 5926b49dea4SBarnabás Pőcze static ssize_t usb_charging_show(struct device *dev, 5936b49dea4SBarnabás Pőcze struct device_attribute *attr, 5946b49dea4SBarnabás Pőcze char *buf) 5956b49dea4SBarnabás Pőcze { 5966b49dea4SBarnabás Pőcze struct ideapad_private *priv = dev_get_drvdata(dev); 5976b49dea4SBarnabás Pőcze unsigned long hals; 5986b49dea4SBarnabás Pőcze int err; 5996b49dea4SBarnabás Pőcze 6006b49dea4SBarnabás Pőcze err = eval_hals(priv->adev->handle, &hals); 6016b49dea4SBarnabás Pőcze if (err) 6026b49dea4SBarnabás Pőcze return err; 6036b49dea4SBarnabás Pőcze 6046b49dea4SBarnabás Pőcze return sysfs_emit(buf, "%d\n", !!test_bit(HALS_USB_CHARGING_STATE_BIT, &hals)); 6056b49dea4SBarnabás Pőcze } 6066b49dea4SBarnabás Pőcze 6076b49dea4SBarnabás Pőcze static ssize_t usb_charging_store(struct device *dev, 6086b49dea4SBarnabás Pőcze struct device_attribute *attr, 6096b49dea4SBarnabás Pőcze const char *buf, size_t count) 6106b49dea4SBarnabás Pőcze { 6116b49dea4SBarnabás Pőcze struct ideapad_private *priv = dev_get_drvdata(dev); 6126b49dea4SBarnabás Pőcze bool state; 6136b49dea4SBarnabás Pőcze int err; 6146b49dea4SBarnabás Pőcze 6156b49dea4SBarnabás Pőcze err = kstrtobool(buf, &state); 6166b49dea4SBarnabás Pőcze if (err) 6176b49dea4SBarnabás Pőcze return err; 6186b49dea4SBarnabás Pőcze 6196b49dea4SBarnabás Pőcze err = exec_sals(priv->adev->handle, state ? SALS_USB_CHARGING_ON : SALS_USB_CHARGING_OFF); 6206b49dea4SBarnabás Pőcze if (err) 6216b49dea4SBarnabás Pőcze return err; 6226b49dea4SBarnabás Pőcze 6236b49dea4SBarnabás Pőcze return count; 6246b49dea4SBarnabás Pőcze } 6256b49dea4SBarnabás Pőcze 6266b49dea4SBarnabás Pőcze static DEVICE_ATTR_RW(usb_charging); 6276b49dea4SBarnabás Pőcze 6283371f481SIke Panhc static struct attribute *ideapad_attributes[] = { 6293371f481SIke Panhc &dev_attr_camera_power.attr, 630ade50296SHao Wei Tee &dev_attr_conservation_mode.attr, 63165c7713aSBarnabás Pőcze &dev_attr_fan_mode.attr, 63240760717SOleg Keri &dev_attr_fn_lock.attr, 63365c7713aSBarnabás Pőcze &dev_attr_touchpad.attr, 6346b49dea4SBarnabás Pőcze &dev_attr_usb_charging.attr, 6353371f481SIke Panhc NULL 6363371f481SIke Panhc }; 6373371f481SIke Panhc 638587a1f16SAl Viro static umode_t ideapad_is_visible(struct kobject *kobj, 639a84511f7SIke Panhc struct attribute *attr, 640a84511f7SIke Panhc int idx) 641a84511f7SIke Panhc { 642708086b2SBarnabás Pőcze struct device *dev = kobj_to_dev(kobj); 643a84511f7SIke Panhc struct ideapad_private *priv = dev_get_drvdata(dev); 6441c59de4aSBarnabás Pőcze bool supported = true; 645a84511f7SIke Panhc 646a84511f7SIke Panhc if (attr == &dev_attr_camera_power.attr) 6470b765671SBarnabás Pőcze supported = test_bit(CFG_CAP_CAM_BIT, &priv->cfg); 6481c59de4aSBarnabás Pőcze else if (attr == &dev_attr_conservation_mode.attr) 6491c59de4aSBarnabás Pőcze supported = priv->features.conservation_mode; 6501c59de4aSBarnabás Pőcze else if (attr == &dev_attr_fan_mode.attr) 6511c59de4aSBarnabás Pőcze supported = priv->features.fan_mode; 6521c59de4aSBarnabás Pőcze else if (attr == &dev_attr_fn_lock.attr) 6531c59de4aSBarnabás Pőcze supported = priv->features.fn_lock; 6541c59de4aSBarnabás Pőcze else if (attr == &dev_attr_touchpad.attr) 65558318828SEray Orçunus supported = priv->features.touchpad_ctrl_via_ec; 6566b49dea4SBarnabás Pőcze else if (attr == &dev_attr_usb_charging.attr) 6576b49dea4SBarnabás Pőcze supported = priv->features.usb_charging; 658a84511f7SIke Panhc 659a84511f7SIke Panhc return supported ? attr->mode : 0; 660a84511f7SIke Panhc } 661a84511f7SIke Panhc 66249458e83SMathias Krause static const struct attribute_group ideapad_attribute_group = { 663a84511f7SIke Panhc .is_visible = ideapad_is_visible, 6643371f481SIke Panhc .attrs = ideapad_attributes 6653371f481SIke Panhc }; 6663371f481SIke Panhc 667a4b5a279SIke Panhc /* 668eabe5339SJiaxun Yang * DYTC Platform profile 669eabe5339SJiaxun Yang */ 670eabe5339SJiaxun Yang #define DYTC_CMD_QUERY 0 /* To get DYTC status - enable/revision */ 671eabe5339SJiaxun Yang #define DYTC_CMD_SET 1 /* To enable/disable IC function mode */ 672eabe5339SJiaxun Yang #define DYTC_CMD_GET 2 /* To get current IC function and mode */ 673eabe5339SJiaxun Yang #define DYTC_CMD_RESET 0x1ff /* To reset back to default */ 674eabe5339SJiaxun Yang 675eabe5339SJiaxun Yang #define DYTC_QUERY_ENABLE_BIT 8 /* Bit 8 - 0 = disabled, 1 = enabled */ 676eabe5339SJiaxun Yang #define DYTC_QUERY_SUBREV_BIT 16 /* Bits 16 - 27 - sub revision */ 677eabe5339SJiaxun Yang #define DYTC_QUERY_REV_BIT 28 /* Bits 28 - 31 - revision */ 678eabe5339SJiaxun Yang 679eabe5339SJiaxun Yang #define DYTC_GET_FUNCTION_BIT 8 /* Bits 8-11 - function setting */ 680eabe5339SJiaxun Yang #define DYTC_GET_MODE_BIT 12 /* Bits 12-15 - mode setting */ 681eabe5339SJiaxun Yang 682eabe5339SJiaxun Yang #define DYTC_SET_FUNCTION_BIT 12 /* Bits 12-15 - function setting */ 683eabe5339SJiaxun Yang #define DYTC_SET_MODE_BIT 16 /* Bits 16-19 - mode setting */ 684eabe5339SJiaxun Yang #define DYTC_SET_VALID_BIT 20 /* Bit 20 - 1 = on, 0 = off */ 685eabe5339SJiaxun Yang 686eabe5339SJiaxun Yang #define DYTC_FUNCTION_STD 0 /* Function = 0, standard mode */ 687eabe5339SJiaxun Yang #define DYTC_FUNCTION_CQL 1 /* Function = 1, lap mode */ 688eabe5339SJiaxun Yang #define DYTC_FUNCTION_MMC 11 /* Function = 11, desk mode */ 689eabe5339SJiaxun Yang 690eabe5339SJiaxun Yang #define DYTC_MODE_PERFORM 2 /* High power mode aka performance */ 691eabe5339SJiaxun Yang #define DYTC_MODE_LOW_POWER 3 /* Low power mode aka quiet */ 692eabe5339SJiaxun Yang #define DYTC_MODE_BALANCE 0xF /* Default mode aka balanced */ 693eabe5339SJiaxun Yang 694eabe5339SJiaxun Yang #define DYTC_SET_COMMAND(function, mode, on) \ 695eabe5339SJiaxun Yang (DYTC_CMD_SET | (function) << DYTC_SET_FUNCTION_BIT | \ 696eabe5339SJiaxun Yang (mode) << DYTC_SET_MODE_BIT | \ 697eabe5339SJiaxun Yang (on) << DYTC_SET_VALID_BIT) 698eabe5339SJiaxun Yang 699eabe5339SJiaxun Yang #define DYTC_DISABLE_CQL DYTC_SET_COMMAND(DYTC_FUNCTION_CQL, DYTC_MODE_BALANCE, 0) 700eabe5339SJiaxun Yang 701eabe5339SJiaxun Yang #define DYTC_ENABLE_CQL DYTC_SET_COMMAND(DYTC_FUNCTION_CQL, DYTC_MODE_BALANCE, 1) 702eabe5339SJiaxun Yang 703eabe5339SJiaxun Yang static int convert_dytc_to_profile(int dytcmode, enum platform_profile_option *profile) 704eabe5339SJiaxun Yang { 705eabe5339SJiaxun Yang switch (dytcmode) { 706eabe5339SJiaxun Yang case DYTC_MODE_LOW_POWER: 707eabe5339SJiaxun Yang *profile = PLATFORM_PROFILE_LOW_POWER; 708eabe5339SJiaxun Yang break; 709eabe5339SJiaxun Yang case DYTC_MODE_BALANCE: 710eabe5339SJiaxun Yang *profile = PLATFORM_PROFILE_BALANCED; 711eabe5339SJiaxun Yang break; 712eabe5339SJiaxun Yang case DYTC_MODE_PERFORM: 713eabe5339SJiaxun Yang *profile = PLATFORM_PROFILE_PERFORMANCE; 714eabe5339SJiaxun Yang break; 715eabe5339SJiaxun Yang default: /* Unknown mode */ 716eabe5339SJiaxun Yang return -EINVAL; 717eabe5339SJiaxun Yang } 71865c7713aSBarnabás Pőcze 719eabe5339SJiaxun Yang return 0; 720eabe5339SJiaxun Yang } 721eabe5339SJiaxun Yang 722eabe5339SJiaxun Yang static int convert_profile_to_dytc(enum platform_profile_option profile, int *perfmode) 723eabe5339SJiaxun Yang { 724eabe5339SJiaxun Yang switch (profile) { 725eabe5339SJiaxun Yang case PLATFORM_PROFILE_LOW_POWER: 726eabe5339SJiaxun Yang *perfmode = DYTC_MODE_LOW_POWER; 727eabe5339SJiaxun Yang break; 728eabe5339SJiaxun Yang case PLATFORM_PROFILE_BALANCED: 729eabe5339SJiaxun Yang *perfmode = DYTC_MODE_BALANCE; 730eabe5339SJiaxun Yang break; 731eabe5339SJiaxun Yang case PLATFORM_PROFILE_PERFORMANCE: 732eabe5339SJiaxun Yang *perfmode = DYTC_MODE_PERFORM; 733eabe5339SJiaxun Yang break; 734eabe5339SJiaxun Yang default: /* Unknown profile */ 735eabe5339SJiaxun Yang return -EOPNOTSUPP; 736eabe5339SJiaxun Yang } 73765c7713aSBarnabás Pőcze 738eabe5339SJiaxun Yang return 0; 739eabe5339SJiaxun Yang } 740eabe5339SJiaxun Yang 741eabe5339SJiaxun Yang /* 742eabe5339SJiaxun Yang * dytc_profile_get: Function to register with platform_profile 743eabe5339SJiaxun Yang * handler. Returns current platform profile. 744eabe5339SJiaxun Yang */ 74565c7713aSBarnabás Pőcze static int dytc_profile_get(struct platform_profile_handler *pprof, 746eabe5339SJiaxun Yang enum platform_profile_option *profile) 747eabe5339SJiaxun Yang { 74865c7713aSBarnabás Pőcze struct ideapad_dytc_priv *dytc = container_of(pprof, struct ideapad_dytc_priv, pprof); 749eabe5339SJiaxun Yang 750eabe5339SJiaxun Yang *profile = dytc->current_profile; 751eabe5339SJiaxun Yang return 0; 752eabe5339SJiaxun Yang } 753eabe5339SJiaxun Yang 754eabe5339SJiaxun Yang /* 755eabe5339SJiaxun Yang * Helper function - check if we are in CQL mode and if we are 756eabe5339SJiaxun Yang * - disable CQL, 757eabe5339SJiaxun Yang * - run the command 758eabe5339SJiaxun Yang * - enable CQL 759eabe5339SJiaxun Yang * If not in CQL mode, just run the command 760eabe5339SJiaxun Yang */ 76165c7713aSBarnabás Pőcze static int dytc_cql_command(struct ideapad_private *priv, unsigned long cmd, 76265c7713aSBarnabás Pőcze unsigned long *output) 763eabe5339SJiaxun Yang { 764ff36b0d9SBarnabás Pőcze int err, cmd_err, cur_funcmode; 765eabe5339SJiaxun Yang 766eabe5339SJiaxun Yang /* Determine if we are in CQL mode. This alters the commands we do */ 767ff36b0d9SBarnabás Pőcze err = eval_dytc(priv->adev->handle, DYTC_CMD_GET, output); 768eabe5339SJiaxun Yang if (err) 769eabe5339SJiaxun Yang return err; 770eabe5339SJiaxun Yang 771eabe5339SJiaxun Yang cur_funcmode = (*output >> DYTC_GET_FUNCTION_BIT) & 0xF; 772eabe5339SJiaxun Yang /* Check if we're OK to return immediately */ 773ff36b0d9SBarnabás Pőcze if (cmd == DYTC_CMD_GET && cur_funcmode != DYTC_FUNCTION_CQL) 774eabe5339SJiaxun Yang return 0; 775eabe5339SJiaxun Yang 776eabe5339SJiaxun Yang if (cur_funcmode == DYTC_FUNCTION_CQL) { 777ff36b0d9SBarnabás Pőcze err = eval_dytc(priv->adev->handle, DYTC_DISABLE_CQL, NULL); 778eabe5339SJiaxun Yang if (err) 779eabe5339SJiaxun Yang return err; 780eabe5339SJiaxun Yang } 781eabe5339SJiaxun Yang 782ff36b0d9SBarnabás Pőcze cmd_err = eval_dytc(priv->adev->handle, cmd, output); 783eabe5339SJiaxun Yang /* Check return condition after we've restored CQL state */ 784eabe5339SJiaxun Yang 785eabe5339SJiaxun Yang if (cur_funcmode == DYTC_FUNCTION_CQL) { 786ff36b0d9SBarnabás Pőcze err = eval_dytc(priv->adev->handle, DYTC_ENABLE_CQL, NULL); 787eabe5339SJiaxun Yang if (err) 788eabe5339SJiaxun Yang return err; 789eabe5339SJiaxun Yang } 790eabe5339SJiaxun Yang 791eabe5339SJiaxun Yang return cmd_err; 792eabe5339SJiaxun Yang } 793eabe5339SJiaxun Yang 794eabe5339SJiaxun Yang /* 795eabe5339SJiaxun Yang * dytc_profile_set: Function to register with platform_profile 796eabe5339SJiaxun Yang * handler. Sets current platform profile. 797eabe5339SJiaxun Yang */ 79865c7713aSBarnabás Pőcze static int dytc_profile_set(struct platform_profile_handler *pprof, 799eabe5339SJiaxun Yang enum platform_profile_option profile) 800eabe5339SJiaxun Yang { 80165c7713aSBarnabás Pőcze struct ideapad_dytc_priv *dytc = container_of(pprof, struct ideapad_dytc_priv, pprof); 80265c7713aSBarnabás Pőcze struct ideapad_private *priv = dytc->priv; 803ff67dbd5SQiu Wenbo unsigned long output; 804eabe5339SJiaxun Yang int err; 805eabe5339SJiaxun Yang 806eabe5339SJiaxun Yang err = mutex_lock_interruptible(&dytc->mutex); 807eabe5339SJiaxun Yang if (err) 808eabe5339SJiaxun Yang return err; 809eabe5339SJiaxun Yang 810eabe5339SJiaxun Yang if (profile == PLATFORM_PROFILE_BALANCED) { 811eabe5339SJiaxun Yang /* To get back to balanced mode we just issue a reset command */ 812ff36b0d9SBarnabás Pőcze err = eval_dytc(priv->adev->handle, DYTC_CMD_RESET, NULL); 813eabe5339SJiaxun Yang if (err) 814eabe5339SJiaxun Yang goto unlock; 815eabe5339SJiaxun Yang } else { 816eabe5339SJiaxun Yang int perfmode; 817eabe5339SJiaxun Yang 818eabe5339SJiaxun Yang err = convert_profile_to_dytc(profile, &perfmode); 819eabe5339SJiaxun Yang if (err) 820eabe5339SJiaxun Yang goto unlock; 821eabe5339SJiaxun Yang 822eabe5339SJiaxun Yang /* Determine if we are in CQL mode. This alters the commands we do */ 82365c7713aSBarnabás Pőcze err = dytc_cql_command(priv, DYTC_SET_COMMAND(DYTC_FUNCTION_MMC, perfmode, 1), 824ff67dbd5SQiu Wenbo &output); 825eabe5339SJiaxun Yang if (err) 826eabe5339SJiaxun Yang goto unlock; 827eabe5339SJiaxun Yang } 82865c7713aSBarnabás Pőcze 829eabe5339SJiaxun Yang /* Success - update current profile */ 830eabe5339SJiaxun Yang dytc->current_profile = profile; 83165c7713aSBarnabás Pőcze 832eabe5339SJiaxun Yang unlock: 833eabe5339SJiaxun Yang mutex_unlock(&dytc->mutex); 83465c7713aSBarnabás Pőcze 835eabe5339SJiaxun Yang return err; 836eabe5339SJiaxun Yang } 837eabe5339SJiaxun Yang 838eabe5339SJiaxun Yang static void dytc_profile_refresh(struct ideapad_private *priv) 839eabe5339SJiaxun Yang { 840eabe5339SJiaxun Yang enum platform_profile_option profile; 841ff36b0d9SBarnabás Pőcze unsigned long output; 842ff36b0d9SBarnabás Pőcze int err, perfmode; 843eabe5339SJiaxun Yang 844eabe5339SJiaxun Yang mutex_lock(&priv->dytc->mutex); 845eabe5339SJiaxun Yang err = dytc_cql_command(priv, DYTC_CMD_GET, &output); 846eabe5339SJiaxun Yang mutex_unlock(&priv->dytc->mutex); 847eabe5339SJiaxun Yang if (err) 848eabe5339SJiaxun Yang return; 849eabe5339SJiaxun Yang 850eabe5339SJiaxun Yang perfmode = (output >> DYTC_GET_MODE_BIT) & 0xF; 85165c7713aSBarnabás Pőcze 85265c7713aSBarnabás Pőcze if (convert_dytc_to_profile(perfmode, &profile)) 85365c7713aSBarnabás Pőcze return; 85465c7713aSBarnabás Pőcze 855eabe5339SJiaxun Yang if (profile != priv->dytc->current_profile) { 856eabe5339SJiaxun Yang priv->dytc->current_profile = profile; 857eabe5339SJiaxun Yang platform_profile_notify(); 858eabe5339SJiaxun Yang } 859eabe5339SJiaxun Yang } 860eabe5339SJiaxun Yang 861599482c5SKelly Anderson static const struct dmi_system_id ideapad_dytc_v4_allow_table[] = { 862599482c5SKelly Anderson { 863599482c5SKelly Anderson /* Ideapad 5 Pro 16ACH6 */ 864599482c5SKelly Anderson .matches = { 865599482c5SKelly Anderson DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 866599482c5SKelly Anderson DMI_MATCH(DMI_PRODUCT_NAME, "82L5") 867599482c5SKelly Anderson } 868599482c5SKelly Anderson }, 8698853e8ceSHans de Goede { 8708853e8ceSHans de Goede /* Ideapad 5 15ITL05 */ 8718853e8ceSHans de Goede .matches = { 8728853e8ceSHans de Goede DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 8738853e8ceSHans de Goede DMI_MATCH(DMI_PRODUCT_VERSION, "IdeaPad 5 15ITL05") 8748853e8ceSHans de Goede } 8758853e8ceSHans de Goede }, 876599482c5SKelly Anderson {} 877599482c5SKelly Anderson }; 878599482c5SKelly Anderson 879eabe5339SJiaxun Yang static int ideapad_dytc_profile_init(struct ideapad_private *priv) 880eabe5339SJiaxun Yang { 881ff36b0d9SBarnabás Pőcze int err, dytc_version; 882ff36b0d9SBarnabás Pőcze unsigned long output; 883eabe5339SJiaxun Yang 8841c59de4aSBarnabás Pőcze if (!priv->features.dytc) 8851c59de4aSBarnabás Pőcze return -ENODEV; 8861c59de4aSBarnabás Pőcze 887ff36b0d9SBarnabás Pőcze err = eval_dytc(priv->adev->handle, DYTC_CMD_QUERY, &output); 888eabe5339SJiaxun Yang /* For all other errors we can flag the failure */ 889eabe5339SJiaxun Yang if (err) 890eabe5339SJiaxun Yang return err; 891eabe5339SJiaxun Yang 892eabe5339SJiaxun Yang /* Check DYTC is enabled and supports mode setting */ 893599482c5SKelly Anderson if (!test_bit(DYTC_QUERY_ENABLE_BIT, &output)) { 894599482c5SKelly Anderson dev_info(&priv->platform_device->dev, "DYTC_QUERY_ENABLE_BIT returned false\n"); 895eabe5339SJiaxun Yang return -ENODEV; 896599482c5SKelly Anderson } 897eabe5339SJiaxun Yang 898eabe5339SJiaxun Yang dytc_version = (output >> DYTC_QUERY_REV_BIT) & 0xF; 899599482c5SKelly Anderson 900a27a1e35SHans de Goede if (dytc_version < 4) { 901a27a1e35SHans de Goede dev_info(&priv->platform_device->dev, "DYTC_VERSION < 4 is not supported\n"); 902eabe5339SJiaxun Yang return -ENODEV; 903599482c5SKelly Anderson } 904a27a1e35SHans de Goede 905a27a1e35SHans de Goede if (dytc_version < 5 && 906a27a1e35SHans de Goede !(allow_v4_dytc || dmi_check_system(ideapad_dytc_v4_allow_table))) { 907a27a1e35SHans de Goede dev_info(&priv->platform_device->dev, 908a27a1e35SHans de Goede "DYTC_VERSION 4 support may not work. Pass ideapad_laptop.allow_v4_dytc=Y on the kernel commandline to enable\n"); 909a27a1e35SHans de Goede return -ENODEV; 910599482c5SKelly Anderson } 911eabe5339SJiaxun Yang 91265c7713aSBarnabás Pőcze priv->dytc = kzalloc(sizeof(*priv->dytc), GFP_KERNEL); 913eabe5339SJiaxun Yang if (!priv->dytc) 914eabe5339SJiaxun Yang return -ENOMEM; 915eabe5339SJiaxun Yang 916eabe5339SJiaxun Yang mutex_init(&priv->dytc->mutex); 917eabe5339SJiaxun Yang 918eabe5339SJiaxun Yang priv->dytc->priv = priv; 919eabe5339SJiaxun Yang priv->dytc->pprof.profile_get = dytc_profile_get; 920eabe5339SJiaxun Yang priv->dytc->pprof.profile_set = dytc_profile_set; 921eabe5339SJiaxun Yang 922eabe5339SJiaxun Yang /* Setup supported modes */ 923eabe5339SJiaxun Yang set_bit(PLATFORM_PROFILE_LOW_POWER, priv->dytc->pprof.choices); 924eabe5339SJiaxun Yang set_bit(PLATFORM_PROFILE_BALANCED, priv->dytc->pprof.choices); 925eabe5339SJiaxun Yang set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->dytc->pprof.choices); 926eabe5339SJiaxun Yang 927eabe5339SJiaxun Yang /* Create platform_profile structure and register */ 928eabe5339SJiaxun Yang err = platform_profile_register(&priv->dytc->pprof); 929eabe5339SJiaxun Yang if (err) 93065c7713aSBarnabás Pőcze goto pp_reg_failed; 931eabe5339SJiaxun Yang 932eabe5339SJiaxun Yang /* Ensure initial values are correct */ 933eabe5339SJiaxun Yang dytc_profile_refresh(priv); 934eabe5339SJiaxun Yang 935eabe5339SJiaxun Yang return 0; 936eabe5339SJiaxun Yang 93765c7713aSBarnabás Pőcze pp_reg_failed: 938eabe5339SJiaxun Yang mutex_destroy(&priv->dytc->mutex); 939eabe5339SJiaxun Yang kfree(priv->dytc); 940eabe5339SJiaxun Yang priv->dytc = NULL; 94165c7713aSBarnabás Pőcze 942eabe5339SJiaxun Yang return err; 943eabe5339SJiaxun Yang } 944eabe5339SJiaxun Yang 945eabe5339SJiaxun Yang static void ideapad_dytc_profile_exit(struct ideapad_private *priv) 946eabe5339SJiaxun Yang { 947eabe5339SJiaxun Yang if (!priv->dytc) 948eabe5339SJiaxun Yang return; 949eabe5339SJiaxun Yang 950eabe5339SJiaxun Yang platform_profile_remove(); 951eabe5339SJiaxun Yang mutex_destroy(&priv->dytc->mutex); 952eabe5339SJiaxun Yang kfree(priv->dytc); 95365c7713aSBarnabás Pőcze 954eabe5339SJiaxun Yang priv->dytc = NULL; 955eabe5339SJiaxun Yang } 956eabe5339SJiaxun Yang 957eabe5339SJiaxun Yang /* 958a4b5a279SIke Panhc * Rfkill 959a4b5a279SIke Panhc */ 960c1f73658SIke Panhc struct ideapad_rfk_data { 961c1f73658SIke Panhc char *name; 962c1f73658SIke Panhc int cfgbit; 963c1f73658SIke Panhc int opcode; 964c1f73658SIke Panhc int type; 965c1f73658SIke Panhc }; 966c1f73658SIke Panhc 967b3d94d70SMathias Krause static const struct ideapad_rfk_data ideapad_rfk_data[] = { 9680b765671SBarnabás Pőcze { "ideapad_wlan", CFG_CAP_WIFI_BIT, VPCCMD_W_WIFI, RFKILL_TYPE_WLAN }, 9690b765671SBarnabás Pőcze { "ideapad_bluetooth", CFG_CAP_BT_BIT, VPCCMD_W_BT, RFKILL_TYPE_BLUETOOTH }, 9700b765671SBarnabás Pőcze { "ideapad_3g", CFG_CAP_3G_BIT, VPCCMD_W_3G, RFKILL_TYPE_WWAN }, 971c1f73658SIke Panhc }; 972c1f73658SIke Panhc 97357ac3b05SIke Panhc static int ideapad_rfk_set(void *data, bool blocked) 97457ac3b05SIke Panhc { 975331e0ea2SZhang Rui struct ideapad_rfk_priv *priv = data; 9764b200b46SArnd Bergmann int opcode = ideapad_rfk_data[priv->dev].opcode; 97757ac3b05SIke Panhc 9784b200b46SArnd Bergmann return write_ec_cmd(priv->priv->adev->handle, opcode, !blocked); 97957ac3b05SIke Panhc } 98057ac3b05SIke Panhc 9813d59dfcdSBhumika Goyal static const struct rfkill_ops ideapad_rfk_ops = { 98257ac3b05SIke Panhc .set_block = ideapad_rfk_set, 98357ac3b05SIke Panhc }; 98457ac3b05SIke Panhc 985923de84aSIke Panhc static void ideapad_sync_rfk_state(struct ideapad_private *priv) 98657ac3b05SIke Panhc { 987ce363c2bSHans de Goede unsigned long hw_blocked = 0; 98857ac3b05SIke Panhc int i; 98957ac3b05SIke Panhc 9901c59de4aSBarnabás Pőcze if (priv->features.hw_rfkill_switch) { 991331e0ea2SZhang Rui if (read_ec_data(priv->adev->handle, VPCCMD_R_RF, &hw_blocked)) 99257ac3b05SIke Panhc return; 99357ac3b05SIke Panhc hw_blocked = !hw_blocked; 994ce363c2bSHans de Goede } 99557ac3b05SIke Panhc 996c1f73658SIke Panhc for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) 99757ac3b05SIke Panhc if (priv->rfk[i]) 99857ac3b05SIke Panhc rfkill_set_hw_state(priv->rfk[i], hw_blocked); 99957ac3b05SIke Panhc } 100057ac3b05SIke Panhc 100175a11f11SZhang Rui static int ideapad_register_rfkill(struct ideapad_private *priv, int dev) 100257ac3b05SIke Panhc { 100365c7713aSBarnabás Pőcze unsigned long rf_enabled; 100465c7713aSBarnabás Pőcze int err; 100557ac3b05SIke Panhc 100665c7713aSBarnabás Pőcze if (no_bt_rfkill && ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH) { 1007bfa97b7dSIke Panhc /* Force to enable bluetooth when no_bt_rfkill=1 */ 100865c7713aSBarnabás Pőcze write_ec_cmd(priv->adev->handle, ideapad_rfk_data[dev].opcode, 1); 1009bfa97b7dSIke Panhc return 0; 1010bfa97b7dSIke Panhc } 101165c7713aSBarnabás Pőcze 1012331e0ea2SZhang Rui priv->rfk_priv[dev].dev = dev; 1013331e0ea2SZhang Rui priv->rfk_priv[dev].priv = priv; 1014bfa97b7dSIke Panhc 101575a11f11SZhang Rui priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name, 1016b5c37b79SZhang Rui &priv->platform_device->dev, 101775a11f11SZhang Rui ideapad_rfk_data[dev].type, 101875a11f11SZhang Rui &ideapad_rfk_ops, 1019331e0ea2SZhang Rui &priv->rfk_priv[dev]); 102057ac3b05SIke Panhc if (!priv->rfk[dev]) 102157ac3b05SIke Panhc return -ENOMEM; 102257ac3b05SIke Panhc 102365c7713aSBarnabás Pőcze err = read_ec_data(priv->adev->handle, ideapad_rfk_data[dev].opcode - 1, &rf_enabled); 102465c7713aSBarnabás Pőcze if (err) 102565c7713aSBarnabás Pőcze rf_enabled = 1; 102657ac3b05SIke Panhc 102765c7713aSBarnabás Pőcze rfkill_init_sw_state(priv->rfk[dev], !rf_enabled); 102865c7713aSBarnabás Pőcze 102965c7713aSBarnabás Pőcze err = rfkill_register(priv->rfk[dev]); 103065c7713aSBarnabás Pőcze if (err) 103157ac3b05SIke Panhc rfkill_destroy(priv->rfk[dev]); 103265c7713aSBarnabás Pőcze 103365c7713aSBarnabás Pőcze return err; 103457ac3b05SIke Panhc } 103557ac3b05SIke Panhc 103675a11f11SZhang Rui static void ideapad_unregister_rfkill(struct ideapad_private *priv, int dev) 103757ac3b05SIke Panhc { 103857ac3b05SIke Panhc if (!priv->rfk[dev]) 103957ac3b05SIke Panhc return; 104057ac3b05SIke Panhc 104157ac3b05SIke Panhc rfkill_unregister(priv->rfk[dev]); 104257ac3b05SIke Panhc rfkill_destroy(priv->rfk[dev]); 104357ac3b05SIke Panhc } 104457ac3b05SIke Panhc 104598ee6919SIke Panhc /* 104698ee6919SIke Panhc * Platform device 104798ee6919SIke Panhc */ 1048b5c37b79SZhang Rui static int ideapad_sysfs_init(struct ideapad_private *priv) 104998ee6919SIke Panhc { 10508782d8d7SBarnabás Pőcze return device_add_group(&priv->platform_device->dev, 1051c9f718d0SIke Panhc &ideapad_attribute_group); 105298ee6919SIke Panhc } 105398ee6919SIke Panhc 1054b5c37b79SZhang Rui static void ideapad_sysfs_exit(struct ideapad_private *priv) 105598ee6919SIke Panhc { 10568782d8d7SBarnabás Pőcze device_remove_group(&priv->platform_device->dev, 1057c9f718d0SIke Panhc &ideapad_attribute_group); 105898ee6919SIke Panhc } 105998ee6919SIke Panhc 1060f63409aeSIke Panhc /* 1061f63409aeSIke Panhc * input device 1062f63409aeSIke Panhc */ 1063f32e0241SPhilipp Jungkamp #define IDEAPAD_WMI_KEY 0x100 1064f32e0241SPhilipp Jungkamp 1065f63409aeSIke Panhc static const struct key_entry ideapad_keymap[] = { 1066f43d9ec0SIke Panhc { KE_KEY, 6, { KEY_SWITCHVIDEOMODE } }, 1067296f9fe0SMaxim Mikityanskiy { KE_KEY, 7, { KEY_CAMERA } }, 106848f67d62SAlex Hung { KE_KEY, 8, { KEY_MICMUTE } }, 1069296f9fe0SMaxim Mikityanskiy { KE_KEY, 11, { KEY_F16 } }, 1070f43d9ec0SIke Panhc { KE_KEY, 13, { KEY_WLAN } }, 1071f43d9ec0SIke Panhc { KE_KEY, 16, { KEY_PROG1 } }, 1072f43d9ec0SIke Panhc { KE_KEY, 17, { KEY_PROG2 } }, 1073296f9fe0SMaxim Mikityanskiy { KE_KEY, 64, { KEY_PROG3 } }, 1074296f9fe0SMaxim Mikityanskiy { KE_KEY, 65, { KEY_PROG4 } }, 107507a4a4fcSMaxim Mikityanskiy { KE_KEY, 66, { KEY_TOUCHPAD_OFF } }, 107607a4a4fcSMaxim Mikityanskiy { KE_KEY, 67, { KEY_TOUCHPAD_ON } }, 107774caab99SArnd Bergmann { KE_KEY, 128, { KEY_ESC } }, 1078f32e0241SPhilipp Jungkamp 1079f32e0241SPhilipp Jungkamp /* 1080f32e0241SPhilipp Jungkamp * WMI keys 1081f32e0241SPhilipp Jungkamp */ 1082f32e0241SPhilipp Jungkamp 1083f32e0241SPhilipp Jungkamp /* FnLock (handled by the firmware) */ 1084f32e0241SPhilipp Jungkamp { KE_IGNORE, 0x02 | IDEAPAD_WMI_KEY }, 1085f32e0241SPhilipp Jungkamp /* Esc (handled by the firmware) */ 1086f32e0241SPhilipp Jungkamp { KE_IGNORE, 0x03 | IDEAPAD_WMI_KEY }, 1087f32e0241SPhilipp Jungkamp /* Customizable Lenovo Hotkey ("star" with 'S' inside) */ 1088f32e0241SPhilipp Jungkamp { KE_KEY, 0x01 | IDEAPAD_WMI_KEY, { KEY_FAVORITES } }, 1089a260f7d7SAndré Apitzsch { KE_KEY, 0x04 | IDEAPAD_WMI_KEY, { KEY_SELECTIVE_SCREENSHOT } }, 1090a260f7d7SAndré Apitzsch /* Lenovo Support */ 1091a260f7d7SAndré Apitzsch { KE_KEY, 0x07 | IDEAPAD_WMI_KEY, { KEY_HELP } }, 1092a260f7d7SAndré Apitzsch { KE_KEY, 0x0e | IDEAPAD_WMI_KEY, { KEY_PICKUP_PHONE } }, 1093a260f7d7SAndré Apitzsch { KE_KEY, 0x0f | IDEAPAD_WMI_KEY, { KEY_HANGUP_PHONE } }, 1094f32e0241SPhilipp Jungkamp /* Dark mode toggle */ 1095f32e0241SPhilipp Jungkamp { KE_KEY, 0x13 | IDEAPAD_WMI_KEY, { KEY_PROG1 } }, 1096f32e0241SPhilipp Jungkamp /* Sound profile switch */ 1097f32e0241SPhilipp Jungkamp { KE_KEY, 0x12 | IDEAPAD_WMI_KEY, { KEY_PROG2 } }, 1098f32e0241SPhilipp Jungkamp /* Lenovo Virtual Background application */ 1099f32e0241SPhilipp Jungkamp { KE_KEY, 0x28 | IDEAPAD_WMI_KEY, { KEY_PROG3 } }, 1100f32e0241SPhilipp Jungkamp /* Lenovo Support */ 1101f32e0241SPhilipp Jungkamp { KE_KEY, 0x27 | IDEAPAD_WMI_KEY, { KEY_HELP } }, 1102f32e0241SPhilipp Jungkamp /* Refresh Rate Toggle */ 1103f32e0241SPhilipp Jungkamp { KE_KEY, 0x0a | IDEAPAD_WMI_KEY, { KEY_DISPLAYTOGGLE } }, 1104f32e0241SPhilipp Jungkamp 110565c7713aSBarnabás Pőcze { KE_END }, 1106f63409aeSIke Panhc }; 1107f63409aeSIke Panhc 1108b859f159SGreg Kroah-Hartman static int ideapad_input_init(struct ideapad_private *priv) 1109f63409aeSIke Panhc { 1110f63409aeSIke Panhc struct input_dev *inputdev; 111165c7713aSBarnabás Pőcze int err; 1112f63409aeSIke Panhc 1113f63409aeSIke Panhc inputdev = input_allocate_device(); 1114b222cca6SJoe Perches if (!inputdev) 1115f63409aeSIke Panhc return -ENOMEM; 1116f63409aeSIke Panhc 1117f63409aeSIke Panhc inputdev->name = "Ideapad extra buttons"; 1118f63409aeSIke Panhc inputdev->phys = "ideapad/input0"; 1119f63409aeSIke Panhc inputdev->id.bustype = BUS_HOST; 11208693ae84SIke Panhc inputdev->dev.parent = &priv->platform_device->dev; 1121f63409aeSIke Panhc 112265c7713aSBarnabás Pőcze err = sparse_keymap_setup(inputdev, ideapad_keymap, NULL); 112365c7713aSBarnabás Pőcze if (err) { 1124654324c4SBarnabás Pőcze dev_err(&priv->platform_device->dev, 112565c7713aSBarnabás Pőcze "Could not set up input device keymap: %d\n", err); 1126f63409aeSIke Panhc goto err_free_dev; 1127f63409aeSIke Panhc } 1128f63409aeSIke Panhc 112965c7713aSBarnabás Pőcze err = input_register_device(inputdev); 113065c7713aSBarnabás Pőcze if (err) { 1131654324c4SBarnabás Pőcze dev_err(&priv->platform_device->dev, 113265c7713aSBarnabás Pőcze "Could not register input device: %d\n", err); 1133c973d4b5SMichał Kępień goto err_free_dev; 1134f63409aeSIke Panhc } 1135f63409aeSIke Panhc 11368693ae84SIke Panhc priv->inputdev = inputdev; 113765c7713aSBarnabás Pőcze 1138f63409aeSIke Panhc return 0; 1139f63409aeSIke Panhc 1140f63409aeSIke Panhc err_free_dev: 1141f63409aeSIke Panhc input_free_device(inputdev); 114265c7713aSBarnabás Pőcze 114365c7713aSBarnabás Pőcze return err; 1144f63409aeSIke Panhc } 1145f63409aeSIke Panhc 11467451a55aSAxel Lin static void ideapad_input_exit(struct ideapad_private *priv) 1147f63409aeSIke Panhc { 11488693ae84SIke Panhc input_unregister_device(priv->inputdev); 11498693ae84SIke Panhc priv->inputdev = NULL; 1150f63409aeSIke Panhc } 1151f63409aeSIke Panhc 11528693ae84SIke Panhc static void ideapad_input_report(struct ideapad_private *priv, 11538693ae84SIke Panhc unsigned long scancode) 1154f63409aeSIke Panhc { 11558693ae84SIke Panhc sparse_keymap_report_event(priv->inputdev, scancode, 1, true); 1156f63409aeSIke Panhc } 1157f63409aeSIke Panhc 1158f43d9ec0SIke Panhc static void ideapad_input_novokey(struct ideapad_private *priv) 1159f43d9ec0SIke Panhc { 1160f43d9ec0SIke Panhc unsigned long long_pressed; 1161f43d9ec0SIke Panhc 1162331e0ea2SZhang Rui if (read_ec_data(priv->adev->handle, VPCCMD_R_NOVO, &long_pressed)) 1163f43d9ec0SIke Panhc return; 116465c7713aSBarnabás Pőcze 1165f43d9ec0SIke Panhc if (long_pressed) 1166f43d9ec0SIke Panhc ideapad_input_report(priv, 17); 1167f43d9ec0SIke Panhc else 1168f43d9ec0SIke Panhc ideapad_input_report(priv, 16); 1169f43d9ec0SIke Panhc } 1170f43d9ec0SIke Panhc 1171296f9fe0SMaxim Mikityanskiy static void ideapad_check_special_buttons(struct ideapad_private *priv) 1172296f9fe0SMaxim Mikityanskiy { 1173296f9fe0SMaxim Mikityanskiy unsigned long bit, value; 1174296f9fe0SMaxim Mikityanskiy 11757be193e3SBarnabás Pőcze if (read_ec_data(priv->adev->handle, VPCCMD_R_SPECIAL_BUTTONS, &value)) 11767be193e3SBarnabás Pőcze return; 1177296f9fe0SMaxim Mikityanskiy 11780c4915b6SBarnabás Pőcze for_each_set_bit (bit, &value, 16) { 1179296f9fe0SMaxim Mikityanskiy switch (bit) { 1180a1ec56edSMaxim Mikityanskiy case 6: /* Z570 */ 118165c7713aSBarnabás Pőcze case 0: /* Z580 */ 1182296f9fe0SMaxim Mikityanskiy /* Thermal Management button */ 1183296f9fe0SMaxim Mikityanskiy ideapad_input_report(priv, 65); 1184296f9fe0SMaxim Mikityanskiy break; 1185296f9fe0SMaxim Mikityanskiy case 1: 1186296f9fe0SMaxim Mikityanskiy /* OneKey Theater button */ 1187296f9fe0SMaxim Mikityanskiy ideapad_input_report(priv, 64); 1188296f9fe0SMaxim Mikityanskiy break; 1189a1ec56edSMaxim Mikityanskiy default: 1190654324c4SBarnabás Pőcze dev_info(&priv->platform_device->dev, 1191654324c4SBarnabás Pőcze "Unknown special button: %lu\n", bit); 1192a1ec56edSMaxim Mikityanskiy break; 1193296f9fe0SMaxim Mikityanskiy } 1194296f9fe0SMaxim Mikityanskiy } 1195296f9fe0SMaxim Mikityanskiy } 1196296f9fe0SMaxim Mikityanskiy 1197a4b5a279SIke Panhc /* 1198a4ecbb8aSIke Panhc * backlight 1199a4ecbb8aSIke Panhc */ 1200a4ecbb8aSIke Panhc static int ideapad_backlight_get_brightness(struct backlight_device *blightdev) 1201a4ecbb8aSIke Panhc { 1202331e0ea2SZhang Rui struct ideapad_private *priv = bl_get_data(blightdev); 1203a4ecbb8aSIke Panhc unsigned long now; 12047be193e3SBarnabás Pőcze int err; 1205a4ecbb8aSIke Panhc 12067be193e3SBarnabás Pőcze err = read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now); 12077be193e3SBarnabás Pőcze if (err) 12087be193e3SBarnabás Pőcze return err; 120965c7713aSBarnabás Pőcze 1210a4ecbb8aSIke Panhc return now; 1211a4ecbb8aSIke Panhc } 1212a4ecbb8aSIke Panhc 1213a4ecbb8aSIke Panhc static int ideapad_backlight_update_status(struct backlight_device *blightdev) 1214a4ecbb8aSIke Panhc { 1215331e0ea2SZhang Rui struct ideapad_private *priv = bl_get_data(blightdev); 12167be193e3SBarnabás Pőcze int err; 1217331e0ea2SZhang Rui 12187be193e3SBarnabás Pőcze err = write_ec_cmd(priv->adev->handle, VPCCMD_W_BL, 12197be193e3SBarnabás Pőcze blightdev->props.brightness); 12207be193e3SBarnabás Pőcze if (err) 12217be193e3SBarnabás Pőcze return err; 122265c7713aSBarnabás Pőcze 12237be193e3SBarnabás Pőcze err = write_ec_cmd(priv->adev->handle, VPCCMD_W_BL_POWER, 12247be193e3SBarnabás Pőcze blightdev->props.power != FB_BLANK_POWERDOWN); 12257be193e3SBarnabás Pőcze if (err) 12267be193e3SBarnabás Pőcze return err; 1227a4ecbb8aSIke Panhc 1228a4ecbb8aSIke Panhc return 0; 1229a4ecbb8aSIke Panhc } 1230a4ecbb8aSIke Panhc 1231a4ecbb8aSIke Panhc static const struct backlight_ops ideapad_backlight_ops = { 1232a4ecbb8aSIke Panhc .get_brightness = ideapad_backlight_get_brightness, 1233a4ecbb8aSIke Panhc .update_status = ideapad_backlight_update_status, 1234a4ecbb8aSIke Panhc }; 1235a4ecbb8aSIke Panhc 1236a4ecbb8aSIke Panhc static int ideapad_backlight_init(struct ideapad_private *priv) 1237a4ecbb8aSIke Panhc { 1238a4ecbb8aSIke Panhc struct backlight_device *blightdev; 1239a4ecbb8aSIke Panhc struct backlight_properties props; 1240a4ecbb8aSIke Panhc unsigned long max, now, power; 12417be193e3SBarnabás Pőcze int err; 1242a4ecbb8aSIke Panhc 12437be193e3SBarnabás Pőcze err = read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &max); 12447be193e3SBarnabás Pőcze if (err) 12457be193e3SBarnabás Pőcze return err; 124665c7713aSBarnabás Pőcze 12477be193e3SBarnabás Pőcze err = read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now); 12487be193e3SBarnabás Pőcze if (err) 12497be193e3SBarnabás Pőcze return err; 125065c7713aSBarnabás Pőcze 12517be193e3SBarnabás Pőcze err = read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power); 12527be193e3SBarnabás Pőcze if (err) 12537be193e3SBarnabás Pőcze return err; 1254a4ecbb8aSIke Panhc 125565c7713aSBarnabás Pőcze memset(&props, 0, sizeof(props)); 125665c7713aSBarnabás Pőcze 1257a4ecbb8aSIke Panhc props.max_brightness = max; 1258a4ecbb8aSIke Panhc props.type = BACKLIGHT_PLATFORM; 125965c7713aSBarnabás Pőcze 1260a4ecbb8aSIke Panhc blightdev = backlight_device_register("ideapad", 1261a4ecbb8aSIke Panhc &priv->platform_device->dev, 1262a4ecbb8aSIke Panhc priv, 1263a4ecbb8aSIke Panhc &ideapad_backlight_ops, 1264a4ecbb8aSIke Panhc &props); 1265a4ecbb8aSIke Panhc if (IS_ERR(blightdev)) { 126665c7713aSBarnabás Pőcze err = PTR_ERR(blightdev); 1267654324c4SBarnabás Pőcze dev_err(&priv->platform_device->dev, 126865c7713aSBarnabás Pőcze "Could not register backlight device: %d\n", err); 126965c7713aSBarnabás Pőcze return err; 1270a4ecbb8aSIke Panhc } 1271a4ecbb8aSIke Panhc 1272a4ecbb8aSIke Panhc priv->blightdev = blightdev; 1273a4ecbb8aSIke Panhc blightdev->props.brightness = now; 1274a4ecbb8aSIke Panhc blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; 127565c7713aSBarnabás Pőcze 1276a4ecbb8aSIke Panhc backlight_update_status(blightdev); 1277a4ecbb8aSIke Panhc 1278a4ecbb8aSIke Panhc return 0; 1279a4ecbb8aSIke Panhc } 1280a4ecbb8aSIke Panhc 1281a4ecbb8aSIke Panhc static void ideapad_backlight_exit(struct ideapad_private *priv) 1282a4ecbb8aSIke Panhc { 1283a4ecbb8aSIke Panhc backlight_device_unregister(priv->blightdev); 1284a4ecbb8aSIke Panhc priv->blightdev = NULL; 1285a4ecbb8aSIke Panhc } 1286a4ecbb8aSIke Panhc 1287a4ecbb8aSIke Panhc static void ideapad_backlight_notify_power(struct ideapad_private *priv) 1288a4ecbb8aSIke Panhc { 1289a4ecbb8aSIke Panhc struct backlight_device *blightdev = priv->blightdev; 129065c7713aSBarnabás Pőcze unsigned long power; 1291a4ecbb8aSIke Panhc 1292d4afc775SRene Bollford if (!blightdev) 1293d4afc775SRene Bollford return; 129465c7713aSBarnabás Pőcze 1295331e0ea2SZhang Rui if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power)) 1296a4ecbb8aSIke Panhc return; 129765c7713aSBarnabás Pőcze 1298a4ecbb8aSIke Panhc blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; 1299a4ecbb8aSIke Panhc } 1300a4ecbb8aSIke Panhc 1301a4ecbb8aSIke Panhc static void ideapad_backlight_notify_brightness(struct ideapad_private *priv) 1302a4ecbb8aSIke Panhc { 1303a4ecbb8aSIke Panhc unsigned long now; 1304a4ecbb8aSIke Panhc 1305a4ecbb8aSIke Panhc /* if we control brightness via acpi video driver */ 130665c7713aSBarnabás Pőcze if (!priv->blightdev) 1307331e0ea2SZhang Rui read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now); 130865c7713aSBarnabás Pőcze else 1309a4ecbb8aSIke Panhc backlight_force_update(priv->blightdev, BACKLIGHT_UPDATE_HOTKEY); 1310a4ecbb8aSIke Panhc } 1311a4ecbb8aSIke Panhc 1312a4ecbb8aSIke Panhc /* 1313503325f8SBarnabás Pőcze * keyboard backlight 1314503325f8SBarnabás Pőcze */ 1315*ecaa1867SStuart Hayhurst static int ideapad_kbd_bl_check_tristate(int type) 1316*ecaa1867SStuart Hayhurst { 1317*ecaa1867SStuart Hayhurst return (type == KBD_BL_TRISTATE) || (type == KBD_BL_TRISTATE_AUTO); 1318*ecaa1867SStuart Hayhurst } 1319*ecaa1867SStuart Hayhurst 1320503325f8SBarnabás Pőcze static int ideapad_kbd_bl_brightness_get(struct ideapad_private *priv) 1321503325f8SBarnabás Pőcze { 1322*ecaa1867SStuart Hayhurst unsigned long value; 1323503325f8SBarnabás Pőcze int err; 1324503325f8SBarnabás Pőcze 1325*ecaa1867SStuart Hayhurst if (ideapad_kbd_bl_check_tristate(priv->kbd_bl.type)) { 1326*ecaa1867SStuart Hayhurst err = eval_kblc(priv->adev->handle, 1327*ecaa1867SStuart Hayhurst FIELD_PREP(KBD_BL_COMMAND_TYPE, priv->kbd_bl.type) | 1328*ecaa1867SStuart Hayhurst KBD_BL_COMMAND_GET, 1329*ecaa1867SStuart Hayhurst &value); 1330*ecaa1867SStuart Hayhurst 1331503325f8SBarnabás Pőcze if (err) 1332503325f8SBarnabás Pőcze return err; 1333503325f8SBarnabás Pőcze 1334*ecaa1867SStuart Hayhurst /* Convert returned value to brightness level */ 1335*ecaa1867SStuart Hayhurst value = FIELD_GET(KBD_BL_GET_BRIGHTNESS, value); 1336*ecaa1867SStuart Hayhurst 1337*ecaa1867SStuart Hayhurst /* Off, low or high */ 1338*ecaa1867SStuart Hayhurst if (value <= priv->kbd_bl.led.max_brightness) 1339*ecaa1867SStuart Hayhurst return value; 1340*ecaa1867SStuart Hayhurst 1341*ecaa1867SStuart Hayhurst /* Auto, report as off */ 1342*ecaa1867SStuart Hayhurst if (value == priv->kbd_bl.led.max_brightness + 1) 1343*ecaa1867SStuart Hayhurst return 0; 1344*ecaa1867SStuart Hayhurst 1345*ecaa1867SStuart Hayhurst /* Unknown value */ 1346*ecaa1867SStuart Hayhurst dev_warn(&priv->platform_device->dev, 1347*ecaa1867SStuart Hayhurst "Unknown keyboard backlight value: %lu", value); 1348*ecaa1867SStuart Hayhurst return -EINVAL; 1349*ecaa1867SStuart Hayhurst } 1350*ecaa1867SStuart Hayhurst 1351*ecaa1867SStuart Hayhurst err = eval_hals(priv->adev->handle, &value); 1352*ecaa1867SStuart Hayhurst if (err) 1353*ecaa1867SStuart Hayhurst return err; 1354*ecaa1867SStuart Hayhurst 1355*ecaa1867SStuart Hayhurst return !!test_bit(HALS_KBD_BL_STATE_BIT, &value); 1356503325f8SBarnabás Pőcze } 1357503325f8SBarnabás Pőcze 1358503325f8SBarnabás Pőcze static enum led_brightness ideapad_kbd_bl_led_cdev_brightness_get(struct led_classdev *led_cdev) 1359503325f8SBarnabás Pőcze { 1360503325f8SBarnabás Pőcze struct ideapad_private *priv = container_of(led_cdev, struct ideapad_private, kbd_bl.led); 1361503325f8SBarnabás Pőcze 1362503325f8SBarnabás Pőcze return ideapad_kbd_bl_brightness_get(priv); 1363503325f8SBarnabás Pőcze } 1364503325f8SBarnabás Pőcze 1365503325f8SBarnabás Pőcze static int ideapad_kbd_bl_brightness_set(struct ideapad_private *priv, unsigned int brightness) 1366503325f8SBarnabás Pőcze { 1367*ecaa1867SStuart Hayhurst int err; 1368*ecaa1867SStuart Hayhurst unsigned long value; 1369*ecaa1867SStuart Hayhurst int type = priv->kbd_bl.type; 1370*ecaa1867SStuart Hayhurst 1371*ecaa1867SStuart Hayhurst if (ideapad_kbd_bl_check_tristate(type)) { 1372*ecaa1867SStuart Hayhurst if (brightness > priv->kbd_bl.led.max_brightness) 1373*ecaa1867SStuart Hayhurst return -EINVAL; 1374*ecaa1867SStuart Hayhurst 1375*ecaa1867SStuart Hayhurst value = FIELD_PREP(KBD_BL_SET_BRIGHTNESS, brightness) | 1376*ecaa1867SStuart Hayhurst FIELD_PREP(KBD_BL_COMMAND_TYPE, type) | 1377*ecaa1867SStuart Hayhurst KBD_BL_COMMAND_SET; 1378*ecaa1867SStuart Hayhurst err = exec_kblc(priv->adev->handle, value); 1379*ecaa1867SStuart Hayhurst } else { 1380*ecaa1867SStuart Hayhurst err = exec_sals(priv->adev->handle, brightness ? SALS_KBD_BL_ON : SALS_KBD_BL_OFF); 1381*ecaa1867SStuart Hayhurst } 1382503325f8SBarnabás Pőcze 1383503325f8SBarnabás Pőcze if (err) 1384503325f8SBarnabás Pőcze return err; 1385503325f8SBarnabás Pőcze 1386503325f8SBarnabás Pőcze priv->kbd_bl.last_brightness = brightness; 1387503325f8SBarnabás Pőcze 1388503325f8SBarnabás Pőcze return 0; 1389503325f8SBarnabás Pőcze } 1390503325f8SBarnabás Pőcze 1391503325f8SBarnabás Pőcze static int ideapad_kbd_bl_led_cdev_brightness_set(struct led_classdev *led_cdev, 1392503325f8SBarnabás Pőcze enum led_brightness brightness) 1393503325f8SBarnabás Pőcze { 1394503325f8SBarnabás Pőcze struct ideapad_private *priv = container_of(led_cdev, struct ideapad_private, kbd_bl.led); 1395503325f8SBarnabás Pőcze 1396503325f8SBarnabás Pőcze return ideapad_kbd_bl_brightness_set(priv, brightness); 1397503325f8SBarnabás Pőcze } 1398503325f8SBarnabás Pőcze 1399503325f8SBarnabás Pőcze static void ideapad_kbd_bl_notify(struct ideapad_private *priv) 1400503325f8SBarnabás Pőcze { 1401503325f8SBarnabás Pőcze int brightness; 1402503325f8SBarnabás Pőcze 1403503325f8SBarnabás Pőcze if (!priv->kbd_bl.initialized) 1404503325f8SBarnabás Pőcze return; 1405503325f8SBarnabás Pőcze 1406503325f8SBarnabás Pőcze brightness = ideapad_kbd_bl_brightness_get(priv); 1407503325f8SBarnabás Pőcze if (brightness < 0) 1408503325f8SBarnabás Pőcze return; 1409503325f8SBarnabás Pőcze 1410503325f8SBarnabás Pőcze if (brightness == priv->kbd_bl.last_brightness) 1411503325f8SBarnabás Pőcze return; 1412503325f8SBarnabás Pőcze 1413503325f8SBarnabás Pőcze priv->kbd_bl.last_brightness = brightness; 1414503325f8SBarnabás Pőcze 1415503325f8SBarnabás Pőcze led_classdev_notify_brightness_hw_changed(&priv->kbd_bl.led, brightness); 1416503325f8SBarnabás Pőcze } 1417503325f8SBarnabás Pőcze 1418503325f8SBarnabás Pőcze static int ideapad_kbd_bl_init(struct ideapad_private *priv) 1419503325f8SBarnabás Pőcze { 1420503325f8SBarnabás Pőcze int brightness, err; 1421503325f8SBarnabás Pőcze 1422503325f8SBarnabás Pőcze if (!priv->features.kbd_bl) 1423503325f8SBarnabás Pőcze return -ENODEV; 1424503325f8SBarnabás Pőcze 1425503325f8SBarnabás Pőcze if (WARN_ON(priv->kbd_bl.initialized)) 1426503325f8SBarnabás Pőcze return -EEXIST; 1427503325f8SBarnabás Pőcze 1428503325f8SBarnabás Pőcze brightness = ideapad_kbd_bl_brightness_get(priv); 1429503325f8SBarnabás Pőcze if (brightness < 0) 1430503325f8SBarnabás Pőcze return brightness; 1431503325f8SBarnabás Pőcze 1432503325f8SBarnabás Pőcze priv->kbd_bl.last_brightness = brightness; 1433503325f8SBarnabás Pőcze 1434*ecaa1867SStuart Hayhurst if (ideapad_kbd_bl_check_tristate(priv->kbd_bl.type)) { 1435*ecaa1867SStuart Hayhurst priv->kbd_bl.led.max_brightness = 2; 1436*ecaa1867SStuart Hayhurst } else { 1437503325f8SBarnabás Pőcze priv->kbd_bl.led.max_brightness = 1; 1438*ecaa1867SStuart Hayhurst } 1439*ecaa1867SStuart Hayhurst 1440*ecaa1867SStuart Hayhurst priv->kbd_bl.led.name = "platform::" LED_FUNCTION_KBD_BACKLIGHT; 1441503325f8SBarnabás Pőcze priv->kbd_bl.led.brightness_get = ideapad_kbd_bl_led_cdev_brightness_get; 1442503325f8SBarnabás Pőcze priv->kbd_bl.led.brightness_set_blocking = ideapad_kbd_bl_led_cdev_brightness_set; 1443503325f8SBarnabás Pőcze priv->kbd_bl.led.flags = LED_BRIGHT_HW_CHANGED; 1444503325f8SBarnabás Pőcze 1445503325f8SBarnabás Pőcze err = led_classdev_register(&priv->platform_device->dev, &priv->kbd_bl.led); 1446503325f8SBarnabás Pőcze if (err) 1447503325f8SBarnabás Pőcze return err; 1448503325f8SBarnabás Pőcze 1449503325f8SBarnabás Pőcze priv->kbd_bl.initialized = true; 1450503325f8SBarnabás Pőcze 1451503325f8SBarnabás Pőcze return 0; 1452503325f8SBarnabás Pőcze } 1453503325f8SBarnabás Pőcze 1454503325f8SBarnabás Pőcze static void ideapad_kbd_bl_exit(struct ideapad_private *priv) 1455503325f8SBarnabás Pőcze { 1456503325f8SBarnabás Pőcze if (!priv->kbd_bl.initialized) 1457503325f8SBarnabás Pőcze return; 1458503325f8SBarnabás Pőcze 1459503325f8SBarnabás Pőcze priv->kbd_bl.initialized = false; 1460503325f8SBarnabás Pőcze 1461503325f8SBarnabás Pőcze led_classdev_unregister(&priv->kbd_bl.led); 1462503325f8SBarnabás Pőcze } 1463503325f8SBarnabás Pőcze 1464503325f8SBarnabás Pőcze /* 1465a4b5a279SIke Panhc * module init/exit 1466a4b5a279SIke Panhc */ 1467f4dd8c44SHans de Goede static void ideapad_sync_touchpad_state(struct ideapad_private *priv, bool send_events) 146807a4a4fcSMaxim Mikityanskiy { 146907a4a4fcSMaxim Mikityanskiy unsigned long value; 1470289a5989SHans de Goede unsigned char param; 1471289a5989SHans de Goede int ret; 147207a4a4fcSMaxim Mikityanskiy 147307a4a4fcSMaxim Mikityanskiy /* Without reading from EC touchpad LED doesn't switch state */ 1474289a5989SHans de Goede ret = read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value); 1475289a5989SHans de Goede if (ret) 1476289a5989SHans de Goede return; 1477289a5989SHans de Goede 147865c7713aSBarnabás Pőcze /* 147965c7713aSBarnabás Pőcze * Some IdeaPads don't really turn off touchpad - they only 148007a4a4fcSMaxim Mikityanskiy * switch the LED state. We (de)activate KBC AUX port to turn 148107a4a4fcSMaxim Mikityanskiy * touchpad off and on. We send KEY_TOUCHPAD_OFF and 148265c7713aSBarnabás Pőcze * KEY_TOUCHPAD_ON to not to get out of sync with LED 148365c7713aSBarnabás Pőcze */ 1484c69e7d84SHans de Goede if (priv->features.ctrl_ps2_aux_port) 148565c7713aSBarnabás Pőcze i8042_command(¶m, value ? I8042_CMD_AUX_ENABLE : I8042_CMD_AUX_DISABLE); 1486f4dd8c44SHans de Goede 14875829f8a8SHans de Goede /* 1488e3271a59SHans de Goede * On older models the EC controls the touchpad and toggles it on/off 1489e3271a59SHans de Goede * itself, in this case we report KEY_TOUCHPAD_ON/_OFF. Some models do 1490e3271a59SHans de Goede * an acpi-notify with VPC bit 5 set on resume, so this function get 1491e3271a59SHans de Goede * called with send_events=true on every resume. Therefor if the EC did 1492e3271a59SHans de Goede * not toggle, do nothing to avoid sending spurious KEY_TOUCHPAD_TOGGLE. 14935829f8a8SHans de Goede */ 1494e3271a59SHans de Goede if (send_events && value != priv->r_touchpad_val) { 149507a4a4fcSMaxim Mikityanskiy ideapad_input_report(priv, value ? 67 : 66); 1496c6795746SBarnabás Pőcze sysfs_notify(&priv->platform_device->dev.kobj, NULL, "touchpad"); 1497f4dd8c44SHans de Goede } 149807a4a4fcSMaxim Mikityanskiy 14995829f8a8SHans de Goede priv->r_touchpad_val = value; 15005829f8a8SHans de Goede } 15015829f8a8SHans de Goede 1502b5c37b79SZhang Rui static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data) 150357ac3b05SIke Panhc { 1504b5c37b79SZhang Rui struct ideapad_private *priv = data; 15050c4915b6SBarnabás Pőcze unsigned long vpc1, vpc2, bit; 150657ac3b05SIke Panhc 15072be1dc21SIke Panhc if (read_ec_data(handle, VPCCMD_R_VPC1, &vpc1)) 150857ac3b05SIke Panhc return; 150965c7713aSBarnabás Pőcze 15102be1dc21SIke Panhc if (read_ec_data(handle, VPCCMD_R_VPC2, &vpc2)) 151157ac3b05SIke Panhc return; 151257ac3b05SIke Panhc 151357ac3b05SIke Panhc vpc1 = (vpc2 << 8) | vpc1; 15140c4915b6SBarnabás Pőcze 15150c4915b6SBarnabás Pőcze for_each_set_bit (bit, &vpc1, 16) { 15160c4915b6SBarnabás Pőcze switch (bit) { 151720a769c1SIke Panhc case 13: 1518296f9fe0SMaxim Mikityanskiy case 11: 151948f67d62SAlex Hung case 8: 1520296f9fe0SMaxim Mikityanskiy case 7: 152120a769c1SIke Panhc case 6: 15220c4915b6SBarnabás Pőcze ideapad_input_report(priv, bit); 152320a769c1SIke Panhc break; 1524ab66724aSHans de Goede case 10: 1525ab66724aSHans de Goede /* 1526ab66724aSHans de Goede * This event gets send on a Yoga 300-11IBR when the EC 1527ab66724aSHans de Goede * believes that the device has changed between laptop/ 1528ab66724aSHans de Goede * tent/stand/tablet mode. The EC relies on getting 1529ab66724aSHans de Goede * angle info from 2 accelerometers through a special 1530ab66724aSHans de Goede * windows service calling a DSM on the DUAL250E ACPI- 1531ab66724aSHans de Goede * device. Linux does not do this, making the laptop/ 1532ab66724aSHans de Goede * tent/stand/tablet mode info unreliable, so we simply 1533ab66724aSHans de Goede * ignore these events. 1534ab66724aSHans de Goede */ 1535ab66724aSHans de Goede break; 153665c7713aSBarnabás Pőcze case 9: 153765c7713aSBarnabás Pőcze ideapad_sync_rfk_state(priv); 153865c7713aSBarnabás Pőcze break; 153907a4a4fcSMaxim Mikityanskiy case 5: 1540f4dd8c44SHans de Goede ideapad_sync_touchpad_state(priv, true); 154107a4a4fcSMaxim Mikityanskiy break; 1542a4ecbb8aSIke Panhc case 4: 1543a4ecbb8aSIke Panhc ideapad_backlight_notify_brightness(priv); 1544a4ecbb8aSIke Panhc break; 1545f43d9ec0SIke Panhc case 3: 1546f43d9ec0SIke Panhc ideapad_input_novokey(priv); 1547f43d9ec0SIke Panhc break; 1548a4ecbb8aSIke Panhc case 2: 1549a4ecbb8aSIke Panhc ideapad_backlight_notify_power(priv); 1550a4ecbb8aSIke Panhc break; 1551*ecaa1867SStuart Hayhurst case KBD_BL_KBLC_CHANGED_EVENT: 15523cfd956bSHao Wei Tee case 1: 155365c7713aSBarnabás Pőcze /* 155465c7713aSBarnabás Pőcze * Some IdeaPads report event 1 every ~20 15553cfd956bSHao Wei Tee * seconds while on battery power; some 15563cfd956bSHao Wei Tee * report this when changing to/from tablet 1557503325f8SBarnabás Pőcze * mode; some report this when the keyboard 1558503325f8SBarnabás Pőcze * backlight has changed. 15593cfd956bSHao Wei Tee */ 1560503325f8SBarnabás Pőcze ideapad_kbd_bl_notify(priv); 15613cfd956bSHao Wei Tee break; 156265c7713aSBarnabás Pőcze case 0: 156365c7713aSBarnabás Pőcze ideapad_check_special_buttons(priv); 156465c7713aSBarnabás Pőcze break; 1565a4ecbb8aSIke Panhc default: 1566654324c4SBarnabás Pőcze dev_info(&priv->platform_device->dev, 1567654324c4SBarnabás Pőcze "Unknown event: %lu\n", bit); 156857ac3b05SIke Panhc } 156957ac3b05SIke Panhc } 1570a4ecbb8aSIke Panhc } 157157ac3b05SIke Panhc 157281a5603aSArnav Rawat /* On some models we need to call exec_sals(SALS_FNLOCK_ON/OFF) to set the LED */ 157381a5603aSArnav Rawat static const struct dmi_system_id set_fn_lock_led_list[] = { 157481a5603aSArnav Rawat { 157581a5603aSArnav Rawat /* https://bugzilla.kernel.org/show_bug.cgi?id=212671 */ 157681a5603aSArnav Rawat .matches = { 157781a5603aSArnav Rawat DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 157881a5603aSArnav Rawat DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Legion R7000P2020H"), 157981a5603aSArnav Rawat } 158081a5603aSArnav Rawat }, 1581f4b7f8feSHans de Goede { 1582f4b7f8feSHans de Goede .matches = { 1583f4b7f8feSHans de Goede DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 1584f4b7f8feSHans de Goede DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Legion 5 15ARH05"), 1585f4b7f8feSHans de Goede } 1586f4b7f8feSHans de Goede }, 158781a5603aSArnav Rawat {} 158881a5603aSArnav Rawat }; 158981a5603aSArnav Rawat 1590ce363c2bSHans de Goede /* 15915105e78eSHans de Goede * Some ideapads have a hardware rfkill switch, but most do not have one. 15925105e78eSHans de Goede * Reading VPCCMD_R_RF always results in 0 on models without a hardware rfkill, 15935105e78eSHans de Goede * switch causing ideapad_laptop to wrongly report all radios as hw-blocked. 15945105e78eSHans de Goede * There used to be a long list of DMI ids for models without a hw rfkill 15955105e78eSHans de Goede * switch here, but that resulted in playing whack a mole. 15965105e78eSHans de Goede * More importantly wrongly reporting the wifi radio as hw-blocked, results in 15975105e78eSHans de Goede * non working wifi. Whereas not reporting it hw-blocked, when it actually is 15985105e78eSHans de Goede * hw-blocked results in an empty SSID list, which is a much more benign 15995105e78eSHans de Goede * failure mode. 16005105e78eSHans de Goede * So the default now is the much safer option of assuming there is no 16015105e78eSHans de Goede * hardware rfkill switch. This default also actually matches most hardware, 16025105e78eSHans de Goede * since having a hw rfkill switch is quite rare on modern hardware, so this 16035105e78eSHans de Goede * also leads to a much shorter list. 1604ce363c2bSHans de Goede */ 16055105e78eSHans de Goede static const struct dmi_system_id hw_rfkill_list[] = { 160685093f79SHans de Goede {} 160785093f79SHans de Goede }; 160885093f79SHans de Goede 1609c69e7d84SHans de Goede /* 1610c69e7d84SHans de Goede * On some models the EC toggles the touchpad muted LED on touchpad toggle 1611c69e7d84SHans de Goede * hotkey presses, but the EC does not actually disable the touchpad itself. 1612c69e7d84SHans de Goede * On these models the driver needs to explicitly enable/disable the i8042 1613c69e7d84SHans de Goede * (PS/2) aux port. 1614c69e7d84SHans de Goede */ 1615c69e7d84SHans de Goede static const struct dmi_system_id ctrl_ps2_aux_port_list[] = { 1616c69e7d84SHans de Goede { 1617c69e7d84SHans de Goede /* Lenovo Ideapad Z570 */ 1618c69e7d84SHans de Goede .matches = { 1619c69e7d84SHans de Goede DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 1620c69e7d84SHans de Goede DMI_MATCH(DMI_PRODUCT_VERSION, "Ideapad Z570"), 1621c69e7d84SHans de Goede }, 1622c69e7d84SHans de Goede }, 1623c69e7d84SHans de Goede {} 1624c69e7d84SHans de Goede }; 1625c69e7d84SHans de Goede 16261c59de4aSBarnabás Pőcze static void ideapad_check_features(struct ideapad_private *priv) 16271c59de4aSBarnabás Pőcze { 16281c59de4aSBarnabás Pőcze acpi_handle handle = priv->adev->handle; 16291c59de4aSBarnabás Pőcze unsigned long val; 16301c59de4aSBarnabás Pőcze 1631b44fd994SHans de Goede priv->features.set_fn_lock_led = 1632b44fd994SHans de Goede set_fn_lock_led || dmi_check_system(set_fn_lock_led_list); 1633b44fd994SHans de Goede priv->features.hw_rfkill_switch = 1634b44fd994SHans de Goede hw_rfkill_switch || dmi_check_system(hw_rfkill_list); 1635c69e7d84SHans de Goede priv->features.ctrl_ps2_aux_port = 1636c69e7d84SHans de Goede ctrl_ps2_aux_port || dmi_check_system(ctrl_ps2_aux_port_list); 1637301e0d76SHans de Goede priv->features.touchpad_ctrl_via_ec = touchpad_ctrl_via_ec; 16381c59de4aSBarnabás Pőcze 16391c59de4aSBarnabás Pőcze if (!read_ec_data(handle, VPCCMD_R_FAN, &val)) 16401c59de4aSBarnabás Pőcze priv->features.fan_mode = true; 16411c59de4aSBarnabás Pőcze 16421c59de4aSBarnabás Pőcze if (acpi_has_method(handle, "GBMD") && acpi_has_method(handle, "SBMC")) 16431c59de4aSBarnabás Pőcze priv->features.conservation_mode = true; 16441c59de4aSBarnabás Pőcze 16451c59de4aSBarnabás Pőcze if (acpi_has_method(handle, "DYTC")) 16461c59de4aSBarnabás Pőcze priv->features.dytc = true; 16471c59de4aSBarnabás Pőcze 1648392cbf0aSBarnabás Pőcze if (acpi_has_method(handle, "HALS") && acpi_has_method(handle, "SALS")) { 1649392cbf0aSBarnabás Pőcze if (!eval_hals(handle, &val)) { 1650392cbf0aSBarnabás Pőcze if (test_bit(HALS_FNLOCK_SUPPORT_BIT, &val)) 16511c59de4aSBarnabás Pőcze priv->features.fn_lock = true; 1652503325f8SBarnabás Pőcze 1653*ecaa1867SStuart Hayhurst if (test_bit(HALS_KBD_BL_SUPPORT_BIT, &val)) { 1654503325f8SBarnabás Pőcze priv->features.kbd_bl = true; 1655*ecaa1867SStuart Hayhurst priv->kbd_bl.type = KBD_BL_STANDARD; 1656*ecaa1867SStuart Hayhurst } 16576b49dea4SBarnabás Pőcze 16586b49dea4SBarnabás Pőcze if (test_bit(HALS_USB_CHARGING_SUPPORT_BIT, &val)) 16596b49dea4SBarnabás Pőcze priv->features.usb_charging = true; 16601c59de4aSBarnabás Pőcze } 1661392cbf0aSBarnabás Pőcze } 1662*ecaa1867SStuart Hayhurst 1663*ecaa1867SStuart Hayhurst if (acpi_has_method(handle, "KBLC")) { 1664*ecaa1867SStuart Hayhurst if (!eval_kblc(priv->adev->handle, KBD_BL_QUERY_TYPE, &val)) { 1665*ecaa1867SStuart Hayhurst if (val == KBD_BL_TRISTATE_TYPE) { 1666*ecaa1867SStuart Hayhurst priv->features.kbd_bl = true; 1667*ecaa1867SStuart Hayhurst priv->kbd_bl.type = KBD_BL_TRISTATE; 1668*ecaa1867SStuart Hayhurst } else if (val == KBD_BL_TRISTATE_AUTO_TYPE) { 1669*ecaa1867SStuart Hayhurst priv->features.kbd_bl = true; 1670*ecaa1867SStuart Hayhurst priv->kbd_bl.type = KBD_BL_TRISTATE_AUTO; 1671*ecaa1867SStuart Hayhurst } else { 1672*ecaa1867SStuart Hayhurst dev_warn(&priv->platform_device->dev, 1673*ecaa1867SStuart Hayhurst "Unknown keyboard type: %lu", 1674*ecaa1867SStuart Hayhurst val); 1675*ecaa1867SStuart Hayhurst } 1676*ecaa1867SStuart Hayhurst } 1677*ecaa1867SStuart Hayhurst } 1678392cbf0aSBarnabás Pőcze } 16791c59de4aSBarnabás Pőcze 1680f32e0241SPhilipp Jungkamp #if IS_ENABLED(CONFIG_ACPI_WMI) 1681f32e0241SPhilipp Jungkamp /* 1682f32e0241SPhilipp Jungkamp * WMI driver 1683f32e0241SPhilipp Jungkamp */ 1684f32e0241SPhilipp Jungkamp enum ideapad_wmi_event_type { 1685f32e0241SPhilipp Jungkamp IDEAPAD_WMI_EVENT_ESC, 1686f32e0241SPhilipp Jungkamp IDEAPAD_WMI_EVENT_FN_KEYS, 1687f32e0241SPhilipp Jungkamp }; 1688f32e0241SPhilipp Jungkamp 1689f32e0241SPhilipp Jungkamp struct ideapad_wmi_private { 1690f32e0241SPhilipp Jungkamp enum ideapad_wmi_event_type event; 1691f32e0241SPhilipp Jungkamp }; 1692f32e0241SPhilipp Jungkamp 1693f32e0241SPhilipp Jungkamp static int ideapad_wmi_probe(struct wmi_device *wdev, const void *context) 1694f32e0241SPhilipp Jungkamp { 1695f32e0241SPhilipp Jungkamp struct ideapad_wmi_private *wpriv; 1696f32e0241SPhilipp Jungkamp 1697f32e0241SPhilipp Jungkamp wpriv = devm_kzalloc(&wdev->dev, sizeof(*wpriv), GFP_KERNEL); 1698f32e0241SPhilipp Jungkamp if (!wpriv) 1699f32e0241SPhilipp Jungkamp return -ENOMEM; 1700f32e0241SPhilipp Jungkamp 1701f32e0241SPhilipp Jungkamp *wpriv = *(const struct ideapad_wmi_private *)context; 1702f32e0241SPhilipp Jungkamp 1703f32e0241SPhilipp Jungkamp dev_set_drvdata(&wdev->dev, wpriv); 1704f32e0241SPhilipp Jungkamp return 0; 1705f32e0241SPhilipp Jungkamp } 1706f32e0241SPhilipp Jungkamp 1707f32e0241SPhilipp Jungkamp static void ideapad_wmi_notify(struct wmi_device *wdev, union acpi_object *data) 1708f32e0241SPhilipp Jungkamp { 1709f32e0241SPhilipp Jungkamp struct ideapad_wmi_private *wpriv = dev_get_drvdata(&wdev->dev); 1710f32e0241SPhilipp Jungkamp struct ideapad_private *priv; 1711f32e0241SPhilipp Jungkamp unsigned long result; 1712f32e0241SPhilipp Jungkamp 1713f32e0241SPhilipp Jungkamp mutex_lock(&ideapad_shared_mutex); 1714f32e0241SPhilipp Jungkamp 1715f32e0241SPhilipp Jungkamp priv = ideapad_shared; 1716f32e0241SPhilipp Jungkamp if (!priv) 1717f32e0241SPhilipp Jungkamp goto unlock; 1718f32e0241SPhilipp Jungkamp 1719f32e0241SPhilipp Jungkamp switch (wpriv->event) { 1720f32e0241SPhilipp Jungkamp case IDEAPAD_WMI_EVENT_ESC: 1721f32e0241SPhilipp Jungkamp ideapad_input_report(priv, 128); 1722f32e0241SPhilipp Jungkamp break; 1723f32e0241SPhilipp Jungkamp case IDEAPAD_WMI_EVENT_FN_KEYS: 1724f32e0241SPhilipp Jungkamp if (priv->features.set_fn_lock_led && 1725f32e0241SPhilipp Jungkamp !eval_hals(priv->adev->handle, &result)) { 1726f32e0241SPhilipp Jungkamp bool state = test_bit(HALS_FNLOCK_STATE_BIT, &result); 1727f32e0241SPhilipp Jungkamp 1728f32e0241SPhilipp Jungkamp exec_sals(priv->adev->handle, state ? SALS_FNLOCK_ON : SALS_FNLOCK_OFF); 1729f32e0241SPhilipp Jungkamp } 1730f32e0241SPhilipp Jungkamp 1731f32e0241SPhilipp Jungkamp if (data->type != ACPI_TYPE_INTEGER) { 1732f32e0241SPhilipp Jungkamp dev_warn(&wdev->dev, 1733f32e0241SPhilipp Jungkamp "WMI event data is not an integer\n"); 1734f32e0241SPhilipp Jungkamp break; 1735f32e0241SPhilipp Jungkamp } 1736f32e0241SPhilipp Jungkamp 1737f32e0241SPhilipp Jungkamp dev_dbg(&wdev->dev, "WMI fn-key event: 0x%llx\n", 1738f32e0241SPhilipp Jungkamp data->integer.value); 1739f32e0241SPhilipp Jungkamp 1740f32e0241SPhilipp Jungkamp ideapad_input_report(priv, 1741f32e0241SPhilipp Jungkamp data->integer.value | IDEAPAD_WMI_KEY); 1742f32e0241SPhilipp Jungkamp 1743f32e0241SPhilipp Jungkamp break; 1744f32e0241SPhilipp Jungkamp } 1745f32e0241SPhilipp Jungkamp unlock: 1746f32e0241SPhilipp Jungkamp mutex_unlock(&ideapad_shared_mutex); 1747f32e0241SPhilipp Jungkamp } 1748f32e0241SPhilipp Jungkamp 1749f32e0241SPhilipp Jungkamp static const struct ideapad_wmi_private ideapad_wmi_context_esc = { 1750f32e0241SPhilipp Jungkamp .event = IDEAPAD_WMI_EVENT_ESC 1751f32e0241SPhilipp Jungkamp }; 1752f32e0241SPhilipp Jungkamp 1753f32e0241SPhilipp Jungkamp static const struct ideapad_wmi_private ideapad_wmi_context_fn_keys = { 1754f32e0241SPhilipp Jungkamp .event = IDEAPAD_WMI_EVENT_FN_KEYS 1755f32e0241SPhilipp Jungkamp }; 1756f32e0241SPhilipp Jungkamp 1757f32e0241SPhilipp Jungkamp static const struct wmi_device_id ideapad_wmi_ids[] = { 1758f32e0241SPhilipp Jungkamp { "26CAB2E5-5CF1-46AE-AAC3-4A12B6BA50E6", &ideapad_wmi_context_esc }, /* Yoga 3 */ 1759f32e0241SPhilipp Jungkamp { "56322276-8493-4CE8-A783-98C991274F5E", &ideapad_wmi_context_esc }, /* Yoga 700 */ 1760f32e0241SPhilipp Jungkamp { "8FC0DE0C-B4E4-43FD-B0F3-8871711C1294", &ideapad_wmi_context_fn_keys }, /* Legion 5 */ 1761f32e0241SPhilipp Jungkamp {}, 1762f32e0241SPhilipp Jungkamp }; 1763f32e0241SPhilipp Jungkamp MODULE_DEVICE_TABLE(wmi, ideapad_wmi_ids); 1764f32e0241SPhilipp Jungkamp 1765f32e0241SPhilipp Jungkamp static struct wmi_driver ideapad_wmi_driver = { 1766f32e0241SPhilipp Jungkamp .driver = { 1767f32e0241SPhilipp Jungkamp .name = "ideapad_wmi", 1768f32e0241SPhilipp Jungkamp }, 1769f32e0241SPhilipp Jungkamp .id_table = ideapad_wmi_ids, 1770f32e0241SPhilipp Jungkamp .probe = ideapad_wmi_probe, 1771f32e0241SPhilipp Jungkamp .notify = ideapad_wmi_notify, 1772f32e0241SPhilipp Jungkamp }; 1773f32e0241SPhilipp Jungkamp 1774f32e0241SPhilipp Jungkamp static int ideapad_wmi_driver_register(void) 1775f32e0241SPhilipp Jungkamp { 1776f32e0241SPhilipp Jungkamp return wmi_driver_register(&ideapad_wmi_driver); 1777f32e0241SPhilipp Jungkamp } 1778f32e0241SPhilipp Jungkamp 1779f32e0241SPhilipp Jungkamp static void ideapad_wmi_driver_unregister(void) 1780f32e0241SPhilipp Jungkamp { 1781f32e0241SPhilipp Jungkamp return wmi_driver_unregister(&ideapad_wmi_driver); 1782f32e0241SPhilipp Jungkamp } 1783f32e0241SPhilipp Jungkamp 1784f32e0241SPhilipp Jungkamp #else 1785f32e0241SPhilipp Jungkamp static inline int ideapad_wmi_driver_register(void) { return 0; } 1786f32e0241SPhilipp Jungkamp static inline void ideapad_wmi_driver_unregister(void) { } 1787f32e0241SPhilipp Jungkamp #endif 1788f32e0241SPhilipp Jungkamp 1789f32e0241SPhilipp Jungkamp /* 1790f32e0241SPhilipp Jungkamp * ACPI driver 1791f32e0241SPhilipp Jungkamp */ 1792b5c37b79SZhang Rui static int ideapad_acpi_add(struct platform_device *pdev) 1793b5c37b79SZhang Rui { 1794043449e7SRafael J. Wysocki struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); 1795b5c37b79SZhang Rui struct ideapad_private *priv; 1796803be832SBarnabás Pőcze acpi_status status; 1797ff36b0d9SBarnabás Pőcze unsigned long cfg; 179865c7713aSBarnabás Pőcze int err, i; 1799b5c37b79SZhang Rui 1800043449e7SRafael J. Wysocki if (!adev || eval_int(adev->handle, "_CFG", &cfg)) 1801b5c37b79SZhang Rui return -ENODEV; 1802b5c37b79SZhang Rui 1803b3facd7bSHimangi Saraogi priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 1804b5c37b79SZhang Rui if (!priv) 1805b5c37b79SZhang Rui return -ENOMEM; 1806b5c37b79SZhang Rui 1807b5c37b79SZhang Rui dev_set_drvdata(&pdev->dev, priv); 180865c7713aSBarnabás Pőcze 1809b5c37b79SZhang Rui priv->cfg = cfg; 1810b5c37b79SZhang Rui priv->adev = adev; 1811b5c37b79SZhang Rui priv->platform_device = pdev; 1812b5c37b79SZhang Rui 18131c59de4aSBarnabás Pőcze ideapad_check_features(priv); 1814d69cd7eeSJiaxun Yang 181565c7713aSBarnabás Pőcze err = ideapad_sysfs_init(priv); 181665c7713aSBarnabás Pőcze if (err) 181765c7713aSBarnabás Pőcze return err; 1818b5c37b79SZhang Rui 181917f1bf38SGreg Kroah-Hartman ideapad_debugfs_init(priv); 1820b5c37b79SZhang Rui 182165c7713aSBarnabás Pőcze err = ideapad_input_init(priv); 182265c7713aSBarnabás Pőcze if (err) 1823b5c37b79SZhang Rui goto input_failed; 1824b5c37b79SZhang Rui 1825503325f8SBarnabás Pőcze err = ideapad_kbd_bl_init(priv); 1826503325f8SBarnabás Pőcze if (err) { 1827503325f8SBarnabás Pőcze if (err != -ENODEV) 1828503325f8SBarnabás Pőcze dev_warn(&pdev->dev, "Could not set up keyboard backlight LED: %d\n", err); 1829503325f8SBarnabás Pőcze else 1830503325f8SBarnabás Pőcze dev_info(&pdev->dev, "Keyboard backlight control not available\n"); 1831503325f8SBarnabás Pőcze } 1832503325f8SBarnabás Pőcze 1833ce363c2bSHans de Goede /* 1834ce363c2bSHans de Goede * On some models without a hw-switch (the yoga 2 13 at least) 1835ce363c2bSHans de Goede * VPCCMD_W_RF must be explicitly set to 1 for the wifi to work. 1836ce363c2bSHans de Goede */ 18371c59de4aSBarnabás Pőcze if (!priv->features.hw_rfkill_switch) 1838ce363c2bSHans de Goede write_ec_cmd(priv->adev->handle, VPCCMD_W_RF, 1); 1839ce363c2bSHans de Goede 184085093f79SHans de Goede for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) 1841b5c37b79SZhang Rui if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg)) 1842b5c37b79SZhang Rui ideapad_register_rfkill(priv, i); 1843ce363c2bSHans de Goede 1844b5c37b79SZhang Rui ideapad_sync_rfk_state(priv); 1845f4dd8c44SHans de Goede ideapad_sync_touchpad_state(priv, false); 1846b5c37b79SZhang Rui 184765c7713aSBarnabás Pőcze err = ideapad_dytc_profile_init(priv); 184865c7713aSBarnabás Pőcze if (err) { 184965c7713aSBarnabás Pőcze if (err != -ENODEV) 185065c7713aSBarnabás Pőcze dev_warn(&pdev->dev, "Could not set up DYTC interface: %d\n", err); 18511c59de4aSBarnabás Pőcze else 18521c59de4aSBarnabás Pőcze dev_info(&pdev->dev, "DYTC interface is not available\n"); 18531c59de4aSBarnabás Pőcze } 1854eabe5339SJiaxun Yang 185526bff5f0SHans de Goede if (acpi_video_get_backlight_type() == acpi_backlight_vendor) { 185665c7713aSBarnabás Pőcze err = ideapad_backlight_init(priv); 185765c7713aSBarnabás Pőcze if (err && err != -ENODEV) 1858b5c37b79SZhang Rui goto backlight_failed; 1859b5c37b79SZhang Rui } 186065c7713aSBarnabás Pőcze 1861803be832SBarnabás Pőcze status = acpi_install_notify_handler(adev->handle, 1862803be832SBarnabás Pőcze ACPI_DEVICE_NOTIFY, 1863803be832SBarnabás Pőcze ideapad_acpi_notify, priv); 1864803be832SBarnabás Pőcze if (ACPI_FAILURE(status)) { 186565c7713aSBarnabás Pőcze err = -EIO; 1866b5c37b79SZhang Rui goto notification_failed; 1867803be832SBarnabás Pőcze } 18682d98e0b9SArnd Bergmann 1869f32e0241SPhilipp Jungkamp err = ideapad_shared_init(priv); 1870f32e0241SPhilipp Jungkamp if (err) 1871f32e0241SPhilipp Jungkamp goto shared_init_failed; 1872b5c37b79SZhang Rui 1873b5c37b79SZhang Rui return 0; 187465c7713aSBarnabás Pőcze 1875f32e0241SPhilipp Jungkamp shared_init_failed: 187674caab99SArnd Bergmann acpi_remove_notify_handler(priv->adev->handle, 187765c7713aSBarnabás Pőcze ACPI_DEVICE_NOTIFY, 187865c7713aSBarnabás Pőcze ideapad_acpi_notify); 187965c7713aSBarnabás Pőcze 1880b5c37b79SZhang Rui notification_failed: 1881b5c37b79SZhang Rui ideapad_backlight_exit(priv); 188265c7713aSBarnabás Pőcze 1883b5c37b79SZhang Rui backlight_failed: 1884caa315b8SBarnabás Pőcze ideapad_dytc_profile_exit(priv); 188565c7713aSBarnabás Pőcze 1886b5c37b79SZhang Rui for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) 1887b5c37b79SZhang Rui ideapad_unregister_rfkill(priv, i); 188865c7713aSBarnabás Pőcze 1889503325f8SBarnabás Pőcze ideapad_kbd_bl_exit(priv); 1890b5c37b79SZhang Rui ideapad_input_exit(priv); 189165c7713aSBarnabás Pőcze 1892b5c37b79SZhang Rui input_failed: 1893b5c37b79SZhang Rui ideapad_debugfs_exit(priv); 1894b5c37b79SZhang Rui ideapad_sysfs_exit(priv); 189565c7713aSBarnabás Pőcze 189665c7713aSBarnabás Pőcze return err; 1897b5c37b79SZhang Rui } 1898b5c37b79SZhang Rui 1899d21c474cSUwe Kleine-König static void ideapad_acpi_remove(struct platform_device *pdev) 1900b5c37b79SZhang Rui { 1901b5c37b79SZhang Rui struct ideapad_private *priv = dev_get_drvdata(&pdev->dev); 1902b5c37b79SZhang Rui int i; 1903b5c37b79SZhang Rui 1904f32e0241SPhilipp Jungkamp ideapad_shared_exit(priv); 190565c7713aSBarnabás Pőcze 1906b5c37b79SZhang Rui acpi_remove_notify_handler(priv->adev->handle, 190765c7713aSBarnabás Pőcze ACPI_DEVICE_NOTIFY, 190865c7713aSBarnabás Pőcze ideapad_acpi_notify); 190965c7713aSBarnabás Pőcze 1910b5c37b79SZhang Rui ideapad_backlight_exit(priv); 1911eabe5339SJiaxun Yang ideapad_dytc_profile_exit(priv); 191265c7713aSBarnabás Pőcze 1913b5c37b79SZhang Rui for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) 1914b5c37b79SZhang Rui ideapad_unregister_rfkill(priv, i); 191565c7713aSBarnabás Pőcze 1916503325f8SBarnabás Pőcze ideapad_kbd_bl_exit(priv); 1917b5c37b79SZhang Rui ideapad_input_exit(priv); 1918b5c37b79SZhang Rui ideapad_debugfs_exit(priv); 1919b5c37b79SZhang Rui ideapad_sysfs_exit(priv); 1920b5c37b79SZhang Rui } 1921b5c37b79SZhang Rui 192211fa8da5SZhang Rui #ifdef CONFIG_PM_SLEEP 1923e1a39a44SBarnabás Pőcze static int ideapad_acpi_resume(struct device *dev) 192407a4a4fcSMaxim Mikityanskiy { 1925e1a39a44SBarnabás Pőcze struct ideapad_private *priv = dev_get_drvdata(dev); 192675a11f11SZhang Rui 192775a11f11SZhang Rui ideapad_sync_rfk_state(priv); 1928f4dd8c44SHans de Goede ideapad_sync_touchpad_state(priv, false); 1929eabe5339SJiaxun Yang 1930eabe5339SJiaxun Yang if (priv->dytc) 1931eabe5339SJiaxun Yang dytc_profile_refresh(priv); 1932eabe5339SJiaxun Yang 193307a4a4fcSMaxim Mikityanskiy return 0; 193407a4a4fcSMaxim Mikityanskiy } 1935b5c37b79SZhang Rui #endif 193607a4a4fcSMaxim Mikityanskiy static SIMPLE_DEV_PM_OPS(ideapad_pm, NULL, ideapad_acpi_resume); 193707a4a4fcSMaxim Mikityanskiy 1938b5c37b79SZhang Rui static const struct acpi_device_id ideapad_device_ids[] = { 1939b5c37b79SZhang Rui {"VPC2004", 0}, 1940b5c37b79SZhang Rui {"", 0}, 194157ac3b05SIke Panhc }; 1942b5c37b79SZhang Rui MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); 1943b5c37b79SZhang Rui 1944b5c37b79SZhang Rui static struct platform_driver ideapad_acpi_driver = { 1945b5c37b79SZhang Rui .probe = ideapad_acpi_add, 1946d21c474cSUwe Kleine-König .remove_new = ideapad_acpi_remove, 1947b5c37b79SZhang Rui .driver = { 1948b5c37b79SZhang Rui .name = "ideapad_acpi", 1949b5c37b79SZhang Rui .pm = &ideapad_pm, 1950b5c37b79SZhang Rui .acpi_match_table = ACPI_PTR(ideapad_device_ids), 1951b5c37b79SZhang Rui }, 1952b5c37b79SZhang Rui }; 1953b5c37b79SZhang Rui 1954f32e0241SPhilipp Jungkamp static int __init ideapad_laptop_init(void) 1955f32e0241SPhilipp Jungkamp { 1956f32e0241SPhilipp Jungkamp int err; 1957f32e0241SPhilipp Jungkamp 1958f32e0241SPhilipp Jungkamp err = ideapad_wmi_driver_register(); 1959f32e0241SPhilipp Jungkamp if (err) 1960f32e0241SPhilipp Jungkamp return err; 1961f32e0241SPhilipp Jungkamp 1962f32e0241SPhilipp Jungkamp err = platform_driver_register(&ideapad_acpi_driver); 1963f32e0241SPhilipp Jungkamp if (err) { 1964f32e0241SPhilipp Jungkamp ideapad_wmi_driver_unregister(); 1965f32e0241SPhilipp Jungkamp return err; 1966f32e0241SPhilipp Jungkamp } 1967f32e0241SPhilipp Jungkamp 1968f32e0241SPhilipp Jungkamp return 0; 1969f32e0241SPhilipp Jungkamp } 1970f32e0241SPhilipp Jungkamp module_init(ideapad_laptop_init) 1971f32e0241SPhilipp Jungkamp 1972f32e0241SPhilipp Jungkamp static void __exit ideapad_laptop_exit(void) 1973f32e0241SPhilipp Jungkamp { 1974f32e0241SPhilipp Jungkamp ideapad_wmi_driver_unregister(); 1975f32e0241SPhilipp Jungkamp platform_driver_unregister(&ideapad_acpi_driver); 1976f32e0241SPhilipp Jungkamp } 1977f32e0241SPhilipp Jungkamp module_exit(ideapad_laptop_exit) 197857ac3b05SIke Panhc 197957ac3b05SIke Panhc MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); 198057ac3b05SIke Panhc MODULE_DESCRIPTION("IdeaPad ACPI Extras"); 198157ac3b05SIke Panhc MODULE_LICENSE("GPL"); 1982