^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * ideapad-laptop.c - Lenovo IdeaPad ACPI Extras
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright © 2010 Intel Corporation
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Copyright © 2010 David Woodhouse <dwmw2@infradead.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/types.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/acpi.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/rfkill.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/input.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/input/sparse-keymap.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/backlight.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <linux/fb.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #include <linux/debugfs.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #include <linux/seq_file.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #include <linux/i8042.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #include <linux/dmi.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #include <linux/device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #include <acpi/video.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #define IDEAPAD_RFKILL_DEV_NUM (3)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) #define BM_CONSERVATION_BIT (5)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) #define HA_FNLOCK_BIT (10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) #define CFG_BT_BIT (16)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) #define CFG_3G_BIT (17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) #define CFG_WIFI_BIT (18)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) #define CFG_CAMERA_BIT (19)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) #if IS_ENABLED(CONFIG_ACPI_WMI)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) static const char *const ideapad_wmi_fnesc_events[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) "26CAB2E5-5CF1-46AE-AAC3-4A12B6BA50E6", /* Yoga 3 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) "56322276-8493-4CE8-A783-98C991274F5E", /* Yoga 700 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) enum {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) BMCMD_CONSERVATION_ON = 3,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) BMCMD_CONSERVATION_OFF = 5,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) HACMD_FNLOCK_ON = 0xe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) HACMD_FNLOCK_OFF = 0xf,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) enum {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) VPCCMD_R_VPC1 = 0x10,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) VPCCMD_R_BL_MAX,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) VPCCMD_R_BL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) VPCCMD_W_BL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) VPCCMD_R_WIFI,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) VPCCMD_W_WIFI,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) VPCCMD_R_BT,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) VPCCMD_W_BT,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) VPCCMD_R_BL_POWER,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) VPCCMD_R_NOVO,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) VPCCMD_R_VPC2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) VPCCMD_R_TOUCHPAD,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) VPCCMD_W_TOUCHPAD,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) VPCCMD_R_CAMERA,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) VPCCMD_W_CAMERA,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) VPCCMD_R_3G,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) VPCCMD_W_3G,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) VPCCMD_R_ODD, /* 0x21 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) VPCCMD_W_FAN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) VPCCMD_R_RF,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) VPCCMD_W_RF,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) VPCCMD_R_FAN = 0x2B,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) VPCCMD_R_SPECIAL_BUTTONS = 0x31,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) VPCCMD_W_BL_POWER = 0x33,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) struct ideapad_rfk_priv {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) int dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) struct ideapad_private *priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) struct ideapad_private {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) struct acpi_device *adev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) struct platform_device *platform_device;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) struct input_dev *inputdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) struct backlight_device *blightdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) struct dentry *debug;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) unsigned long cfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) bool has_hw_rfkill_switch;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) bool has_touchpad_switch;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) const char *fnesc_guid;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) static bool no_bt_rfkill;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) module_param(no_bt_rfkill, bool, 0444);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) * ACPI Helpers
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) #define IDEAPAD_EC_TIMEOUT (200) /* in ms */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) static int read_method_int(acpi_handle handle, const char *method, int *val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) acpi_status status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) unsigned long long result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) if (ACPI_FAILURE(status)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) *val = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) *val = result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) static int method_gbmd(acpi_handle handle, unsigned long *ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) int result, val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) result = read_method_int(handle, "GBMD", &val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) *ret = val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) return result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) static int method_int1(acpi_handle handle, char *method, int cmd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) acpi_status status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) status = acpi_execute_simple_method(handle, method, cmd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) return ACPI_FAILURE(status) ? -1 : 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) static int method_vpcr(acpi_handle handle, int cmd, int *ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) acpi_status status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) unsigned long long result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) struct acpi_object_list params;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) union acpi_object in_obj;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) params.count = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) params.pointer = &in_obj;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) in_obj.type = ACPI_TYPE_INTEGER;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) in_obj.integer.value = cmd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) status = acpi_evaluate_integer(handle, "VPCR", ¶ms, &result);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) if (ACPI_FAILURE(status)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) *ret = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) *ret = result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) static int method_vpcw(acpi_handle handle, int cmd, int data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) struct acpi_object_list params;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) union acpi_object in_obj[2];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) acpi_status status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) params.count = 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) params.pointer = in_obj;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) in_obj[0].type = ACPI_TYPE_INTEGER;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) in_obj[0].integer.value = cmd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) in_obj[1].type = ACPI_TYPE_INTEGER;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) in_obj[1].integer.value = data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) status = acpi_evaluate_object(handle, "VPCW", ¶ms, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) if (status != AE_OK)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) int val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) unsigned long int end_jiffies;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) if (method_vpcw(handle, 1, cmd))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) time_before(jiffies, end_jiffies);) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) schedule();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) if (method_vpcr(handle, 1, &val))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) if (val == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) if (method_vpcr(handle, 0, &val))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) *data = val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) pr_err("timeout in %s\n", __func__);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) int val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) unsigned long int end_jiffies;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) if (method_vpcw(handle, 0, data))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) if (method_vpcw(handle, 1, cmd))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) time_before(jiffies, end_jiffies);) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) schedule();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) if (method_vpcr(handle, 1, &val))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) if (val == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) pr_err("timeout in %s\n", __func__);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) * debugfs
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) static int debugfs_status_show(struct seq_file *s, void *data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) struct ideapad_private *priv = s->private;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) unsigned long value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) if (!priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &value))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) seq_printf(s, "Backlight max:\t%lu\n", value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL, &value))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) seq_printf(s, "Backlight now:\t%lu\n", value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &value))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) seq_printf(s, "BL power value:\t%s\n", value ? "On" : "Off");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) seq_printf(s, "=====================\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) if (!read_ec_data(priv->adev->handle, VPCCMD_R_RF, &value))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) seq_printf(s, "Radio status:\t%s(%lu)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) value ? "On" : "Off", value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) if (!read_ec_data(priv->adev->handle, VPCCMD_R_WIFI, &value))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) seq_printf(s, "Wifi status:\t%s(%lu)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) value ? "On" : "Off", value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) if (!read_ec_data(priv->adev->handle, VPCCMD_R_BT, &value))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) seq_printf(s, "BT status:\t%s(%lu)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) value ? "On" : "Off", value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) if (!read_ec_data(priv->adev->handle, VPCCMD_R_3G, &value))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) seq_printf(s, "3G status:\t%s(%lu)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) value ? "On" : "Off", value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) seq_printf(s, "=====================\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) seq_printf(s, "Touchpad status:%s(%lu)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) value ? "On" : "Off", value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) seq_printf(s, "Camera status:\t%s(%lu)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) value ? "On" : "Off", value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) seq_puts(s, "=====================\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) if (!method_gbmd(priv->adev->handle, &value)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) seq_printf(s, "Conservation mode:\t%s(%lu)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) test_bit(BM_CONSERVATION_BIT, &value) ? "On" : "Off",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) DEFINE_SHOW_ATTRIBUTE(debugfs_status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) static int debugfs_cfg_show(struct seq_file *s, void *data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) struct ideapad_private *priv = s->private;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) if (!priv) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) seq_printf(s, "cfg: N/A\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) seq_printf(s, "cfg: 0x%.8lX\n\nCapability: ",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) priv->cfg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) if (test_bit(CFG_BT_BIT, &priv->cfg))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) seq_printf(s, "Bluetooth ");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) if (test_bit(CFG_3G_BIT, &priv->cfg))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) seq_printf(s, "3G ");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) if (test_bit(CFG_WIFI_BIT, &priv->cfg))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) seq_printf(s, "Wireless ");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) if (test_bit(CFG_CAMERA_BIT, &priv->cfg))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) seq_printf(s, "Camera ");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) seq_printf(s, "\nGraphic: ");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) switch ((priv->cfg)&0x700) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) case 0x100:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) seq_printf(s, "Intel");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) case 0x200:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) seq_printf(s, "ATI");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) case 0x300:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) seq_printf(s, "Nvidia");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) case 0x400:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) seq_printf(s, "Intel and ATI");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) case 0x500:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) seq_printf(s, "Intel and Nvidia");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) seq_printf(s, "\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) DEFINE_SHOW_ATTRIBUTE(debugfs_cfg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) static void ideapad_debugfs_init(struct ideapad_private *priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) struct dentry *dir;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) dir = debugfs_create_dir("ideapad", NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) priv->debug = dir;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) debugfs_create_file("cfg", S_IRUGO, dir, priv, &debugfs_cfg_fops);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) debugfs_create_file("status", S_IRUGO, dir, priv, &debugfs_status_fops);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) static void ideapad_debugfs_exit(struct ideapad_private *priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) debugfs_remove_recursive(priv->debug);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) priv->debug = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) * sysfs
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) static ssize_t show_ideapad_cam(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) unsigned long result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) struct ideapad_private *priv = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) if (read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &result))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) return sprintf(buf, "-1\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) return sprintf(buf, "%lu\n", result);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) static ssize_t store_ideapad_cam(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) const char *buf, size_t count)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) int ret, state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) struct ideapad_private *priv = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) if (!count)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) if (sscanf(buf, "%i", &state) != 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_CAMERA, state);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) return -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) return count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) static ssize_t show_ideapad_fan(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) unsigned long result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) struct ideapad_private *priv = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) if (read_ec_data(priv->adev->handle, VPCCMD_R_FAN, &result))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) return sprintf(buf, "-1\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) return sprintf(buf, "%lu\n", result);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) static ssize_t store_ideapad_fan(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) const char *buf, size_t count)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) int ret, state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) struct ideapad_private *priv = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) if (!count)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) if (sscanf(buf, "%i", &state) != 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) if (state < 0 || state > 4 || state == 3)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_FAN, state);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) return -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) return count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) static DEVICE_ATTR(fan_mode, 0644, show_ideapad_fan, store_ideapad_fan);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) static ssize_t touchpad_show(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) struct ideapad_private *priv = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) unsigned long result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) if (read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &result))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) return sprintf(buf, "-1\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) return sprintf(buf, "%lu\n", result);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) /* Switch to RO for now: It might be revisited in the future */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) static ssize_t __maybe_unused touchpad_store(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) const char *buf, size_t count)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) struct ideapad_private *priv = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) bool state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) ret = kstrtobool(buf, &state);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, state);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) return -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) return count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) static DEVICE_ATTR_RO(touchpad);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) static ssize_t conservation_mode_show(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) struct ideapad_private *priv = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) unsigned long result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) if (method_gbmd(priv->adev->handle, &result))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) return sprintf(buf, "-1\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) return sprintf(buf, "%u\n", test_bit(BM_CONSERVATION_BIT, &result));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) static ssize_t conservation_mode_store(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) const char *buf, size_t count)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) struct ideapad_private *priv = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) bool state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) ret = kstrtobool(buf, &state);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461) ret = method_int1(priv->adev->handle, "SBMC", state ?
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462) BMCMD_CONSERVATION_ON :
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) BMCMD_CONSERVATION_OFF);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465) return -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) return count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469) static DEVICE_ATTR_RW(conservation_mode);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471) static ssize_t fn_lock_show(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473) char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475) struct ideapad_private *priv = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) unsigned long result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477) int hals;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478) int fail = read_method_int(priv->adev->handle, "HALS", &hals);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) if (fail)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481) return sprintf(buf, "-1\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483) result = hals;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484) return sprintf(buf, "%u\n", test_bit(HA_FNLOCK_BIT, &result));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487) static ssize_t fn_lock_store(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489) const char *buf, size_t count)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491) struct ideapad_private *priv = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492) bool state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495) ret = kstrtobool(buf, &state);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499) ret = method_int1(priv->adev->handle, "SALS", state ?
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500) HACMD_FNLOCK_ON :
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501) HACMD_FNLOCK_OFF);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503) return -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) return count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) static DEVICE_ATTR_RW(fn_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510) static struct attribute *ideapad_attributes[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511) &dev_attr_camera_power.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512) &dev_attr_fan_mode.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513) &dev_attr_touchpad.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514) &dev_attr_conservation_mode.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) &dev_attr_fn_lock.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516) NULL
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 519) static umode_t ideapad_is_visible(struct kobject *kobj,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 520) struct attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 521) int idx)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 522) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 523) struct device *dev = container_of(kobj, struct device, kobj);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 524) struct ideapad_private *priv = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 525) bool supported;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 526)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 527) if (attr == &dev_attr_camera_power.attr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 528) supported = test_bit(CFG_CAMERA_BIT, &(priv->cfg));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 529) else if (attr == &dev_attr_fan_mode.attr) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 530) unsigned long value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 531) supported = !read_ec_data(priv->adev->handle, VPCCMD_R_FAN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 532) &value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 533) } else if (attr == &dev_attr_conservation_mode.attr) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 534) supported = acpi_has_method(priv->adev->handle, "GBMD") &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 535) acpi_has_method(priv->adev->handle, "SBMC");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 536) } else if (attr == &dev_attr_fn_lock.attr) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 537) supported = acpi_has_method(priv->adev->handle, "HALS") &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 538) acpi_has_method(priv->adev->handle, "SALS");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 539) } else if (attr == &dev_attr_touchpad.attr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 540) supported = priv->has_touchpad_switch;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 541) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 542) supported = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 543)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 544) return supported ? attr->mode : 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 545) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 546)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 547) static const struct attribute_group ideapad_attribute_group = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 548) .is_visible = ideapad_is_visible,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 549) .attrs = ideapad_attributes
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 550) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 551)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 552) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 553) * Rfkill
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 554) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 555) struct ideapad_rfk_data {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 556) char *name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 557) int cfgbit;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 558) int opcode;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 559) int type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 560) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 561)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 562) static const struct ideapad_rfk_data ideapad_rfk_data[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 563) { "ideapad_wlan", CFG_WIFI_BIT, VPCCMD_W_WIFI, RFKILL_TYPE_WLAN },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 564) { "ideapad_bluetooth", CFG_BT_BIT, VPCCMD_W_BT, RFKILL_TYPE_BLUETOOTH },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 565) { "ideapad_3g", CFG_3G_BIT, VPCCMD_W_3G, RFKILL_TYPE_WWAN },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 566) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 567)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 568) static int ideapad_rfk_set(void *data, bool blocked)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 569) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 570) struct ideapad_rfk_priv *priv = data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 571) int opcode = ideapad_rfk_data[priv->dev].opcode;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 572)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 573) return write_ec_cmd(priv->priv->adev->handle, opcode, !blocked);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 574) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 575)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 576) static const struct rfkill_ops ideapad_rfk_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 577) .set_block = ideapad_rfk_set,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 578) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 579)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 580) static void ideapad_sync_rfk_state(struct ideapad_private *priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 581) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 582) unsigned long hw_blocked = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 583) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 584)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 585) if (priv->has_hw_rfkill_switch) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 586) if (read_ec_data(priv->adev->handle, VPCCMD_R_RF, &hw_blocked))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 587) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 588) hw_blocked = !hw_blocked;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 589) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 590)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 591) for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 592) if (priv->rfk[i])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 593) rfkill_set_hw_state(priv->rfk[i], hw_blocked);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 594) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 595)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 596) static int ideapad_register_rfkill(struct ideapad_private *priv, int dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 597) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 598) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 599) unsigned long sw_blocked;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 600)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 601) if (no_bt_rfkill &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 602) (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 603) /* Force to enable bluetooth when no_bt_rfkill=1 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 604) write_ec_cmd(priv->adev->handle,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 605) ideapad_rfk_data[dev].opcode, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 606) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 607) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 608) priv->rfk_priv[dev].dev = dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 609) priv->rfk_priv[dev].priv = priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 610)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 611) priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 612) &priv->platform_device->dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 613) ideapad_rfk_data[dev].type,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 614) &ideapad_rfk_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 615) &priv->rfk_priv[dev]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 616) if (!priv->rfk[dev])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 617) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 618)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 619) if (read_ec_data(priv->adev->handle, ideapad_rfk_data[dev].opcode-1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 620) &sw_blocked)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 621) rfkill_init_sw_state(priv->rfk[dev], 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 622) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 623) sw_blocked = !sw_blocked;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 624) rfkill_init_sw_state(priv->rfk[dev], sw_blocked);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 625) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 626)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 627) ret = rfkill_register(priv->rfk[dev]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 628) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 629) rfkill_destroy(priv->rfk[dev]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 630) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 631) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 632) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 633) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 634)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 635) static void ideapad_unregister_rfkill(struct ideapad_private *priv, int dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 636) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 637) if (!priv->rfk[dev])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 638) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 639)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 640) rfkill_unregister(priv->rfk[dev]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 641) rfkill_destroy(priv->rfk[dev]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 642) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 643)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 644) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 645) * Platform device
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 646) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 647) static int ideapad_sysfs_init(struct ideapad_private *priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 648) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 649) return sysfs_create_group(&priv->platform_device->dev.kobj,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 650) &ideapad_attribute_group);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 651) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 652)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 653) static void ideapad_sysfs_exit(struct ideapad_private *priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 654) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 655) sysfs_remove_group(&priv->platform_device->dev.kobj,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 656) &ideapad_attribute_group);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 657) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 658)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 659) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 660) * input device
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 661) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 662) static const struct key_entry ideapad_keymap[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 663) { KE_KEY, 6, { KEY_SWITCHVIDEOMODE } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 664) { KE_KEY, 7, { KEY_CAMERA } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 665) { KE_KEY, 8, { KEY_MICMUTE } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 666) { KE_KEY, 11, { KEY_F16 } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 667) { KE_KEY, 13, { KEY_WLAN } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 668) { KE_KEY, 16, { KEY_PROG1 } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 669) { KE_KEY, 17, { KEY_PROG2 } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 670) { KE_KEY, 64, { KEY_PROG3 } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 671) { KE_KEY, 65, { KEY_PROG4 } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 672) { KE_KEY, 66, { KEY_TOUCHPAD_OFF } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 673) { KE_KEY, 67, { KEY_TOUCHPAD_ON } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 674) { KE_KEY, 128, { KEY_ESC } },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 675)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 676) { KE_END, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 677) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 678)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 679) static int ideapad_input_init(struct ideapad_private *priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 680) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 681) struct input_dev *inputdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 682) int error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 683)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 684) inputdev = input_allocate_device();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 685) if (!inputdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 686) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 687)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 688) inputdev->name = "Ideapad extra buttons";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 689) inputdev->phys = "ideapad/input0";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 690) inputdev->id.bustype = BUS_HOST;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 691) inputdev->dev.parent = &priv->platform_device->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 692)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 693) error = sparse_keymap_setup(inputdev, ideapad_keymap, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 694) if (error) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 695) pr_err("Unable to setup input device keymap\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 696) goto err_free_dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 697) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 698)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 699) error = input_register_device(inputdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 700) if (error) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 701) pr_err("Unable to register input device\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 702) goto err_free_dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 703) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 704)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 705) priv->inputdev = inputdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 706) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 707)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 708) err_free_dev:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 709) input_free_device(inputdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 710) return error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 711) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 712)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 713) static void ideapad_input_exit(struct ideapad_private *priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 714) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 715) input_unregister_device(priv->inputdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 716) priv->inputdev = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 717) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 718)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 719) static void ideapad_input_report(struct ideapad_private *priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 720) unsigned long scancode)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 721) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 722) sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 723) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 724)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 725) static void ideapad_input_novokey(struct ideapad_private *priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 726) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 727) unsigned long long_pressed;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 728)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 729) if (read_ec_data(priv->adev->handle, VPCCMD_R_NOVO, &long_pressed))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 730) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 731) if (long_pressed)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 732) ideapad_input_report(priv, 17);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 733) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 734) ideapad_input_report(priv, 16);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 735) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 736)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 737) static void ideapad_check_special_buttons(struct ideapad_private *priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 738) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 739) unsigned long bit, value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 740)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 741) read_ec_data(priv->adev->handle, VPCCMD_R_SPECIAL_BUTTONS, &value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 742)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 743) for (bit = 0; bit < 16; bit++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 744) if (test_bit(bit, &value)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 745) switch (bit) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 746) case 0: /* Z580 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 747) case 6: /* Z570 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 748) /* Thermal Management button */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 749) ideapad_input_report(priv, 65);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 750) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 751) case 1:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 752) /* OneKey Theater button */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 753) ideapad_input_report(priv, 64);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 754) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 755) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 756) pr_info("Unknown special button: %lu\n", bit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 757) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 758) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 759) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 760) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 761) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 762)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 763) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 764) * backlight
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 765) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 766) static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 767) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 768) struct ideapad_private *priv = bl_get_data(blightdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 769) unsigned long now;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 770)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 771) if (!priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 772) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 773)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 774) if (read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 775) return -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 776) return now;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 777) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 778)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 779) static int ideapad_backlight_update_status(struct backlight_device *blightdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 780) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 781) struct ideapad_private *priv = bl_get_data(blightdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 782)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 783) if (!priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 784) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 785)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 786) if (write_ec_cmd(priv->adev->handle, VPCCMD_W_BL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 787) blightdev->props.brightness))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 788) return -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 789) if (write_ec_cmd(priv->adev->handle, VPCCMD_W_BL_POWER,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 790) blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 791) return -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 792)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 793) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 794) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 795)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 796) static const struct backlight_ops ideapad_backlight_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 797) .get_brightness = ideapad_backlight_get_brightness,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 798) .update_status = ideapad_backlight_update_status,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 799) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 800)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 801) static int ideapad_backlight_init(struct ideapad_private *priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 802) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 803) struct backlight_device *blightdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 804) struct backlight_properties props;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 805) unsigned long max, now, power;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 806)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 807) if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &max))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 808) return -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 809) if (read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 810) return -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 811) if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 812) return -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 813)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 814) memset(&props, 0, sizeof(struct backlight_properties));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 815) props.max_brightness = max;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 816) props.type = BACKLIGHT_PLATFORM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 817) blightdev = backlight_device_register("ideapad",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 818) &priv->platform_device->dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 819) priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 820) &ideapad_backlight_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 821) &props);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 822) if (IS_ERR(blightdev)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 823) pr_err("Could not register backlight device\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 824) return PTR_ERR(blightdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 825) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 826)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 827) priv->blightdev = blightdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 828) blightdev->props.brightness = now;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 829) blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 830) backlight_update_status(blightdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 831)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 832) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 833) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 834)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 835) static void ideapad_backlight_exit(struct ideapad_private *priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 836) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 837) backlight_device_unregister(priv->blightdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 838) priv->blightdev = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 839) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 840)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 841) static void ideapad_backlight_notify_power(struct ideapad_private *priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 842) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 843) unsigned long power;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 844) struct backlight_device *blightdev = priv->blightdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 845)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 846) if (!blightdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 847) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 848) if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 849) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 850) blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 851) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 852)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 853) static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 854) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 855) unsigned long now;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 856)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 857) /* if we control brightness via acpi video driver */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 858) if (priv->blightdev == NULL) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 859) read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 860) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 861) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 862)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 863) backlight_force_update(priv->blightdev, BACKLIGHT_UPDATE_HOTKEY);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 864) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 865)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 866) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 867) * module init/exit
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 868) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 869) static void ideapad_sync_touchpad_state(struct ideapad_private *priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 870) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 871) unsigned long value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 872)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 873) if (!priv->has_touchpad_switch)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 874) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 875)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 876) /* Without reading from EC touchpad LED doesn't switch state */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 877) if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 878) /* Some IdeaPads don't really turn off touchpad - they only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 879) * switch the LED state. We (de)activate KBC AUX port to turn
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 880) * touchpad off and on. We send KEY_TOUCHPAD_OFF and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 881) * KEY_TOUCHPAD_ON to not to get out of sync with LED */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 882) unsigned char param;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 883) i8042_command(¶m, value ? I8042_CMD_AUX_ENABLE :
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 884) I8042_CMD_AUX_DISABLE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 885) ideapad_input_report(priv, value ? 67 : 66);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 886) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 887) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 888)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 889) static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 890) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 891) struct ideapad_private *priv = data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 892) unsigned long vpc1, vpc2, vpc_bit;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 893)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 894) if (read_ec_data(handle, VPCCMD_R_VPC1, &vpc1))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 895) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 896) if (read_ec_data(handle, VPCCMD_R_VPC2, &vpc2))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 897) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 898)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 899) vpc1 = (vpc2 << 8) | vpc1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 900) for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 901) if (test_bit(vpc_bit, &vpc1)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 902) switch (vpc_bit) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 903) case 9:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 904) ideapad_sync_rfk_state(priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 905) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 906) case 13:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 907) case 11:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 908) case 8:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 909) case 7:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 910) case 6:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 911) ideapad_input_report(priv, vpc_bit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 912) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 913) case 5:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 914) ideapad_sync_touchpad_state(priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 915) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 916) case 4:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 917) ideapad_backlight_notify_brightness(priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 918) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 919) case 3:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 920) ideapad_input_novokey(priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 921) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 922) case 2:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 923) ideapad_backlight_notify_power(priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 924) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 925) case 0:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 926) ideapad_check_special_buttons(priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 927) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 928) case 1:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 929) /* Some IdeaPads report event 1 every ~20
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 930) * seconds while on battery power; some
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 931) * report this when changing to/from tablet
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 932) * mode. Squelch this event.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 933) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 934) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 935) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 936) pr_info("Unknown event: %lu\n", vpc_bit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 937) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 938) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 939) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 940) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 941)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 942) #if IS_ENABLED(CONFIG_ACPI_WMI)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 943) static void ideapad_wmi_notify(u32 value, void *context)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 944) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 945) switch (value) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 946) case 128:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 947) ideapad_input_report(context, value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 948) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 949) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 950) pr_info("Unknown WMI event %u\n", value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 951) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 952) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 953) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 954)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 955) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 956) * Some ideapads have a hardware rfkill switch, but most do not have one.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 957) * Reading VPCCMD_R_RF always results in 0 on models without a hardware rfkill,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 958) * switch causing ideapad_laptop to wrongly report all radios as hw-blocked.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 959) * There used to be a long list of DMI ids for models without a hw rfkill
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 960) * switch here, but that resulted in playing whack a mole.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 961) * More importantly wrongly reporting the wifi radio as hw-blocked, results in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 962) * non working wifi. Whereas not reporting it hw-blocked, when it actually is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 963) * hw-blocked results in an empty SSID list, which is a much more benign
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 964) * failure mode.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 965) * So the default now is the much safer option of assuming there is no
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 966) * hardware rfkill switch. This default also actually matches most hardware,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 967) * since having a hw rfkill switch is quite rare on modern hardware, so this
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 968) * also leads to a much shorter list.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 969) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 970) static const struct dmi_system_id hw_rfkill_list[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 971) {}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 972) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 973)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 974) static int ideapad_acpi_add(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 975) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 976) int ret, i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 977) int cfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 978) struct ideapad_private *priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 979) struct acpi_device *adev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 980)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 981) ret = acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 982) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 983) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 984)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 985) if (read_method_int(adev->handle, "_CFG", &cfg))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 986) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 987)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 988) priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 989) if (!priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 990) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 991)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 992) dev_set_drvdata(&pdev->dev, priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 993) priv->cfg = cfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 994) priv->adev = adev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 995) priv->platform_device = pdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 996) priv->has_hw_rfkill_switch = dmi_check_system(hw_rfkill_list);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 997)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 998) /* Most ideapads with ELAN0634 touchpad don't use EC touchpad switch */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 999) priv->has_touchpad_switch = !acpi_dev_present("ELAN0634", NULL, -1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1000)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1001) ret = ideapad_sysfs_init(priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1002) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1003) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1004)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1005) ideapad_debugfs_init(priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1006)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1007) ret = ideapad_input_init(priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1008) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1009) goto input_failed;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1010)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1011) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1012) * On some models without a hw-switch (the yoga 2 13 at least)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1013) * VPCCMD_W_RF must be explicitly set to 1 for the wifi to work.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1014) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1015) if (!priv->has_hw_rfkill_switch)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1016) write_ec_cmd(priv->adev->handle, VPCCMD_W_RF, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1017)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1018) /* The same for Touchpad */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1019) if (!priv->has_touchpad_switch)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1020) write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1021)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1022) for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1023) if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1024) ideapad_register_rfkill(priv, i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1025)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1026) ideapad_sync_rfk_state(priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1027) ideapad_sync_touchpad_state(priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1028)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1029) if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1030) ret = ideapad_backlight_init(priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1031) if (ret && ret != -ENODEV)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1032) goto backlight_failed;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1033) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1034) ret = acpi_install_notify_handler(adev->handle,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1035) ACPI_DEVICE_NOTIFY, ideapad_acpi_notify, priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1036) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1037) goto notification_failed;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1038)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1039) #if IS_ENABLED(CONFIG_ACPI_WMI)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1040) for (i = 0; i < ARRAY_SIZE(ideapad_wmi_fnesc_events); i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1041) ret = wmi_install_notify_handler(ideapad_wmi_fnesc_events[i],
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1042) ideapad_wmi_notify, priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1043) if (ret == AE_OK) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1044) priv->fnesc_guid = ideapad_wmi_fnesc_events[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1045) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1046) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1047) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1048) if (ret != AE_OK && ret != AE_NOT_EXIST)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1049) goto notification_failed_wmi;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1050) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1051)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1052) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1053) #if IS_ENABLED(CONFIG_ACPI_WMI)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1054) notification_failed_wmi:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1055) acpi_remove_notify_handler(priv->adev->handle,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1056) ACPI_DEVICE_NOTIFY, ideapad_acpi_notify);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1057) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1058) notification_failed:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1059) ideapad_backlight_exit(priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1060) backlight_failed:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1061) for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1062) ideapad_unregister_rfkill(priv, i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1063) ideapad_input_exit(priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1064) input_failed:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1065) ideapad_debugfs_exit(priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1066) ideapad_sysfs_exit(priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1067) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1068) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1069)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1070) static int ideapad_acpi_remove(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1071) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1072) struct ideapad_private *priv = dev_get_drvdata(&pdev->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1073) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1074)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1075) #if IS_ENABLED(CONFIG_ACPI_WMI)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1076) if (priv->fnesc_guid)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1077) wmi_remove_notify_handler(priv->fnesc_guid);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1078) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1079) acpi_remove_notify_handler(priv->adev->handle,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1080) ACPI_DEVICE_NOTIFY, ideapad_acpi_notify);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1081) ideapad_backlight_exit(priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1082) for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1083) ideapad_unregister_rfkill(priv, i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1084) ideapad_input_exit(priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1085) ideapad_debugfs_exit(priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1086) ideapad_sysfs_exit(priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1087) dev_set_drvdata(&pdev->dev, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1088)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1089) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1090) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1091)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1092) #ifdef CONFIG_PM_SLEEP
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1093) static int ideapad_acpi_resume(struct device *device)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1094) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1095) struct ideapad_private *priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1096)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1097) if (!device)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1098) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1099) priv = dev_get_drvdata(device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1101) ideapad_sync_rfk_state(priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1102) ideapad_sync_touchpad_state(priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1103) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1104) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1105) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1106) static SIMPLE_DEV_PM_OPS(ideapad_pm, NULL, ideapad_acpi_resume);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1108) static const struct acpi_device_id ideapad_device_ids[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1109) { "VPC2004", 0},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1110) { "", 0},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1111) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1112) MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1113)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1114) static struct platform_driver ideapad_acpi_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1115) .probe = ideapad_acpi_add,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1116) .remove = ideapad_acpi_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1117) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1118) .name = "ideapad_acpi",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1119) .pm = &ideapad_pm,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1120) .acpi_match_table = ACPI_PTR(ideapad_device_ids),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1121) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1122) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1123)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1124) module_platform_driver(ideapad_acpi_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1125)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1126) MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1127) MODULE_DESCRIPTION("IdeaPad ACPI Extras");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1128) MODULE_LICENSE("GPL");