18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  ideapad-laptop.c - Lenovo IdeaPad ACPI Extras
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright © 2010 Intel Corporation
68c2ecf20Sopenharmony_ci *  Copyright © 2010 David Woodhouse <dwmw2@infradead.org>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/types.h>
158c2ecf20Sopenharmony_ci#include <linux/acpi.h>
168c2ecf20Sopenharmony_ci#include <linux/rfkill.h>
178c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
188c2ecf20Sopenharmony_ci#include <linux/input.h>
198c2ecf20Sopenharmony_ci#include <linux/input/sparse-keymap.h>
208c2ecf20Sopenharmony_ci#include <linux/backlight.h>
218c2ecf20Sopenharmony_ci#include <linux/fb.h>
228c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
238c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
248c2ecf20Sopenharmony_ci#include <linux/i8042.h>
258c2ecf20Sopenharmony_ci#include <linux/dmi.h>
268c2ecf20Sopenharmony_ci#include <linux/device.h>
278c2ecf20Sopenharmony_ci#include <acpi/video.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define IDEAPAD_RFKILL_DEV_NUM	(3)
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define BM_CONSERVATION_BIT (5)
328c2ecf20Sopenharmony_ci#define HA_FNLOCK_BIT       (10)
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define CFG_BT_BIT	(16)
358c2ecf20Sopenharmony_ci#define CFG_3G_BIT	(17)
368c2ecf20Sopenharmony_ci#define CFG_WIFI_BIT	(18)
378c2ecf20Sopenharmony_ci#define CFG_CAMERA_BIT	(19)
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_ACPI_WMI)
408c2ecf20Sopenharmony_cistatic const char *const ideapad_wmi_fnesc_events[] = {
418c2ecf20Sopenharmony_ci	"26CAB2E5-5CF1-46AE-AAC3-4A12B6BA50E6", /* Yoga 3 */
428c2ecf20Sopenharmony_ci	"56322276-8493-4CE8-A783-98C991274F5E", /* Yoga 700 */
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci#endif
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cienum {
478c2ecf20Sopenharmony_ci	BMCMD_CONSERVATION_ON = 3,
488c2ecf20Sopenharmony_ci	BMCMD_CONSERVATION_OFF = 5,
498c2ecf20Sopenharmony_ci	HACMD_FNLOCK_ON = 0xe,
508c2ecf20Sopenharmony_ci	HACMD_FNLOCK_OFF = 0xf,
518c2ecf20Sopenharmony_ci};
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cienum {
548c2ecf20Sopenharmony_ci	VPCCMD_R_VPC1 = 0x10,
558c2ecf20Sopenharmony_ci	VPCCMD_R_BL_MAX,
568c2ecf20Sopenharmony_ci	VPCCMD_R_BL,
578c2ecf20Sopenharmony_ci	VPCCMD_W_BL,
588c2ecf20Sopenharmony_ci	VPCCMD_R_WIFI,
598c2ecf20Sopenharmony_ci	VPCCMD_W_WIFI,
608c2ecf20Sopenharmony_ci	VPCCMD_R_BT,
618c2ecf20Sopenharmony_ci	VPCCMD_W_BT,
628c2ecf20Sopenharmony_ci	VPCCMD_R_BL_POWER,
638c2ecf20Sopenharmony_ci	VPCCMD_R_NOVO,
648c2ecf20Sopenharmony_ci	VPCCMD_R_VPC2,
658c2ecf20Sopenharmony_ci	VPCCMD_R_TOUCHPAD,
668c2ecf20Sopenharmony_ci	VPCCMD_W_TOUCHPAD,
678c2ecf20Sopenharmony_ci	VPCCMD_R_CAMERA,
688c2ecf20Sopenharmony_ci	VPCCMD_W_CAMERA,
698c2ecf20Sopenharmony_ci	VPCCMD_R_3G,
708c2ecf20Sopenharmony_ci	VPCCMD_W_3G,
718c2ecf20Sopenharmony_ci	VPCCMD_R_ODD, /* 0x21 */
728c2ecf20Sopenharmony_ci	VPCCMD_W_FAN,
738c2ecf20Sopenharmony_ci	VPCCMD_R_RF,
748c2ecf20Sopenharmony_ci	VPCCMD_W_RF,
758c2ecf20Sopenharmony_ci	VPCCMD_R_FAN = 0x2B,
768c2ecf20Sopenharmony_ci	VPCCMD_R_SPECIAL_BUTTONS = 0x31,
778c2ecf20Sopenharmony_ci	VPCCMD_W_BL_POWER = 0x33,
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistruct ideapad_rfk_priv {
818c2ecf20Sopenharmony_ci	int dev;
828c2ecf20Sopenharmony_ci	struct ideapad_private *priv;
838c2ecf20Sopenharmony_ci};
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistruct ideapad_private {
868c2ecf20Sopenharmony_ci	struct acpi_device *adev;
878c2ecf20Sopenharmony_ci	struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
888c2ecf20Sopenharmony_ci	struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM];
898c2ecf20Sopenharmony_ci	struct platform_device *platform_device;
908c2ecf20Sopenharmony_ci	struct input_dev *inputdev;
918c2ecf20Sopenharmony_ci	struct backlight_device *blightdev;
928c2ecf20Sopenharmony_ci	struct dentry *debug;
938c2ecf20Sopenharmony_ci	unsigned long cfg;
948c2ecf20Sopenharmony_ci	bool has_hw_rfkill_switch;
958c2ecf20Sopenharmony_ci	bool has_touchpad_switch;
968c2ecf20Sopenharmony_ci	const char *fnesc_guid;
978c2ecf20Sopenharmony_ci};
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic bool no_bt_rfkill;
1008c2ecf20Sopenharmony_cimodule_param(no_bt_rfkill, bool, 0444);
1018c2ecf20Sopenharmony_ciMODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci/*
1048c2ecf20Sopenharmony_ci * ACPI Helpers
1058c2ecf20Sopenharmony_ci */
1068c2ecf20Sopenharmony_ci#define IDEAPAD_EC_TIMEOUT (200) /* in ms */
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic int read_method_int(acpi_handle handle, const char *method, int *val)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	acpi_status status;
1118c2ecf20Sopenharmony_ci	unsigned long long result;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
1148c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status)) {
1158c2ecf20Sopenharmony_ci		*val = -1;
1168c2ecf20Sopenharmony_ci		return -1;
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci	*val = result;
1198c2ecf20Sopenharmony_ci	return 0;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic int method_gbmd(acpi_handle handle, unsigned long *ret)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	int result, val;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	result = read_method_int(handle, "GBMD", &val);
1288c2ecf20Sopenharmony_ci	*ret = val;
1298c2ecf20Sopenharmony_ci	return result;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic int method_int1(acpi_handle handle, char *method, int cmd)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	acpi_status status;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	status = acpi_execute_simple_method(handle, method, cmd);
1378c2ecf20Sopenharmony_ci	return ACPI_FAILURE(status) ? -1 : 0;
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic int method_vpcr(acpi_handle handle, int cmd, int *ret)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	acpi_status status;
1438c2ecf20Sopenharmony_ci	unsigned long long result;
1448c2ecf20Sopenharmony_ci	struct acpi_object_list params;
1458c2ecf20Sopenharmony_ci	union acpi_object in_obj;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	params.count = 1;
1488c2ecf20Sopenharmony_ci	params.pointer = &in_obj;
1498c2ecf20Sopenharmony_ci	in_obj.type = ACPI_TYPE_INTEGER;
1508c2ecf20Sopenharmony_ci	in_obj.integer.value = cmd;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	status = acpi_evaluate_integer(handle, "VPCR", &params, &result);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status)) {
1558c2ecf20Sopenharmony_ci		*ret = -1;
1568c2ecf20Sopenharmony_ci		return -1;
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci	*ret = result;
1598c2ecf20Sopenharmony_ci	return 0;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic int method_vpcw(acpi_handle handle, int cmd, int data)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	struct acpi_object_list params;
1668c2ecf20Sopenharmony_ci	union acpi_object in_obj[2];
1678c2ecf20Sopenharmony_ci	acpi_status status;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	params.count = 2;
1708c2ecf20Sopenharmony_ci	params.pointer = in_obj;
1718c2ecf20Sopenharmony_ci	in_obj[0].type = ACPI_TYPE_INTEGER;
1728c2ecf20Sopenharmony_ci	in_obj[0].integer.value = cmd;
1738c2ecf20Sopenharmony_ci	in_obj[1].type = ACPI_TYPE_INTEGER;
1748c2ecf20Sopenharmony_ci	in_obj[1].integer.value = data;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	status = acpi_evaluate_object(handle, "VPCW", &params, NULL);
1778c2ecf20Sopenharmony_ci	if (status != AE_OK)
1788c2ecf20Sopenharmony_ci		return -1;
1798c2ecf20Sopenharmony_ci	return 0;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic int read_ec_data(acpi_handle handle, int cmd, unsigned long *data)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	int val;
1858c2ecf20Sopenharmony_ci	unsigned long int end_jiffies;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (method_vpcw(handle, 1, cmd))
1888c2ecf20Sopenharmony_ci		return -1;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
1918c2ecf20Sopenharmony_ci	     time_before(jiffies, end_jiffies);) {
1928c2ecf20Sopenharmony_ci		schedule();
1938c2ecf20Sopenharmony_ci		if (method_vpcr(handle, 1, &val))
1948c2ecf20Sopenharmony_ci			return -1;
1958c2ecf20Sopenharmony_ci		if (val == 0) {
1968c2ecf20Sopenharmony_ci			if (method_vpcr(handle, 0, &val))
1978c2ecf20Sopenharmony_ci				return -1;
1988c2ecf20Sopenharmony_ci			*data = val;
1998c2ecf20Sopenharmony_ci			return 0;
2008c2ecf20Sopenharmony_ci		}
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci	pr_err("timeout in %s\n", __func__);
2038c2ecf20Sopenharmony_ci	return -1;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	int val;
2098c2ecf20Sopenharmony_ci	unsigned long int end_jiffies;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (method_vpcw(handle, 0, data))
2128c2ecf20Sopenharmony_ci		return -1;
2138c2ecf20Sopenharmony_ci	if (method_vpcw(handle, 1, cmd))
2148c2ecf20Sopenharmony_ci		return -1;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
2178c2ecf20Sopenharmony_ci	     time_before(jiffies, end_jiffies);) {
2188c2ecf20Sopenharmony_ci		schedule();
2198c2ecf20Sopenharmony_ci		if (method_vpcr(handle, 1, &val))
2208c2ecf20Sopenharmony_ci			return -1;
2218c2ecf20Sopenharmony_ci		if (val == 0)
2228c2ecf20Sopenharmony_ci			return 0;
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci	pr_err("timeout in %s\n", __func__);
2258c2ecf20Sopenharmony_ci	return -1;
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci/*
2298c2ecf20Sopenharmony_ci * debugfs
2308c2ecf20Sopenharmony_ci */
2318c2ecf20Sopenharmony_cistatic int debugfs_status_show(struct seq_file *s, void *data)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	struct ideapad_private *priv = s->private;
2348c2ecf20Sopenharmony_ci	unsigned long value;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	if (!priv)
2378c2ecf20Sopenharmony_ci		return -EINVAL;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &value))
2408c2ecf20Sopenharmony_ci		seq_printf(s, "Backlight max:\t%lu\n", value);
2418c2ecf20Sopenharmony_ci	if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL, &value))
2428c2ecf20Sopenharmony_ci		seq_printf(s, "Backlight now:\t%lu\n", value);
2438c2ecf20Sopenharmony_ci	if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &value))
2448c2ecf20Sopenharmony_ci		seq_printf(s, "BL power value:\t%s\n", value ? "On" : "Off");
2458c2ecf20Sopenharmony_ci	seq_printf(s, "=====================\n");
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	if (!read_ec_data(priv->adev->handle, VPCCMD_R_RF, &value))
2488c2ecf20Sopenharmony_ci		seq_printf(s, "Radio status:\t%s(%lu)\n",
2498c2ecf20Sopenharmony_ci			   value ? "On" : "Off", value);
2508c2ecf20Sopenharmony_ci	if (!read_ec_data(priv->adev->handle, VPCCMD_R_WIFI, &value))
2518c2ecf20Sopenharmony_ci		seq_printf(s, "Wifi status:\t%s(%lu)\n",
2528c2ecf20Sopenharmony_ci			   value ? "On" : "Off", value);
2538c2ecf20Sopenharmony_ci	if (!read_ec_data(priv->adev->handle, VPCCMD_R_BT, &value))
2548c2ecf20Sopenharmony_ci		seq_printf(s, "BT status:\t%s(%lu)\n",
2558c2ecf20Sopenharmony_ci			   value ? "On" : "Off", value);
2568c2ecf20Sopenharmony_ci	if (!read_ec_data(priv->adev->handle, VPCCMD_R_3G, &value))
2578c2ecf20Sopenharmony_ci		seq_printf(s, "3G status:\t%s(%lu)\n",
2588c2ecf20Sopenharmony_ci			   value ? "On" : "Off", value);
2598c2ecf20Sopenharmony_ci	seq_printf(s, "=====================\n");
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value))
2628c2ecf20Sopenharmony_ci		seq_printf(s, "Touchpad status:%s(%lu)\n",
2638c2ecf20Sopenharmony_ci			   value ? "On" : "Off", value);
2648c2ecf20Sopenharmony_ci	if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value))
2658c2ecf20Sopenharmony_ci		seq_printf(s, "Camera status:\t%s(%lu)\n",
2668c2ecf20Sopenharmony_ci			   value ? "On" : "Off", value);
2678c2ecf20Sopenharmony_ci	seq_puts(s, "=====================\n");
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if (!method_gbmd(priv->adev->handle, &value)) {
2708c2ecf20Sopenharmony_ci		seq_printf(s, "Conservation mode:\t%s(%lu)\n",
2718c2ecf20Sopenharmony_ci			   test_bit(BM_CONSERVATION_BIT, &value) ? "On" : "Off",
2728c2ecf20Sopenharmony_ci			   value);
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	return 0;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(debugfs_status);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic int debugfs_cfg_show(struct seq_file *s, void *data)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	struct ideapad_private *priv = s->private;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (!priv) {
2848c2ecf20Sopenharmony_ci		seq_printf(s, "cfg: N/A\n");
2858c2ecf20Sopenharmony_ci	} else {
2868c2ecf20Sopenharmony_ci		seq_printf(s, "cfg: 0x%.8lX\n\nCapability: ",
2878c2ecf20Sopenharmony_ci			   priv->cfg);
2888c2ecf20Sopenharmony_ci		if (test_bit(CFG_BT_BIT, &priv->cfg))
2898c2ecf20Sopenharmony_ci			seq_printf(s, "Bluetooth ");
2908c2ecf20Sopenharmony_ci		if (test_bit(CFG_3G_BIT, &priv->cfg))
2918c2ecf20Sopenharmony_ci			seq_printf(s, "3G ");
2928c2ecf20Sopenharmony_ci		if (test_bit(CFG_WIFI_BIT, &priv->cfg))
2938c2ecf20Sopenharmony_ci			seq_printf(s, "Wireless ");
2948c2ecf20Sopenharmony_ci		if (test_bit(CFG_CAMERA_BIT, &priv->cfg))
2958c2ecf20Sopenharmony_ci			seq_printf(s, "Camera ");
2968c2ecf20Sopenharmony_ci		seq_printf(s, "\nGraphic: ");
2978c2ecf20Sopenharmony_ci		switch ((priv->cfg)&0x700) {
2988c2ecf20Sopenharmony_ci		case 0x100:
2998c2ecf20Sopenharmony_ci			seq_printf(s, "Intel");
3008c2ecf20Sopenharmony_ci			break;
3018c2ecf20Sopenharmony_ci		case 0x200:
3028c2ecf20Sopenharmony_ci			seq_printf(s, "ATI");
3038c2ecf20Sopenharmony_ci			break;
3048c2ecf20Sopenharmony_ci		case 0x300:
3058c2ecf20Sopenharmony_ci			seq_printf(s, "Nvidia");
3068c2ecf20Sopenharmony_ci			break;
3078c2ecf20Sopenharmony_ci		case 0x400:
3088c2ecf20Sopenharmony_ci			seq_printf(s, "Intel and ATI");
3098c2ecf20Sopenharmony_ci			break;
3108c2ecf20Sopenharmony_ci		case 0x500:
3118c2ecf20Sopenharmony_ci			seq_printf(s, "Intel and Nvidia");
3128c2ecf20Sopenharmony_ci			break;
3138c2ecf20Sopenharmony_ci		}
3148c2ecf20Sopenharmony_ci		seq_printf(s, "\n");
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci	return 0;
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(debugfs_cfg);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic void ideapad_debugfs_init(struct ideapad_private *priv)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	struct dentry *dir;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	dir = debugfs_create_dir("ideapad", NULL);
3258c2ecf20Sopenharmony_ci	priv->debug = dir;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	debugfs_create_file("cfg", S_IRUGO, dir, priv, &debugfs_cfg_fops);
3288c2ecf20Sopenharmony_ci	debugfs_create_file("status", S_IRUGO, dir, priv, &debugfs_status_fops);
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistatic void ideapad_debugfs_exit(struct ideapad_private *priv)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	debugfs_remove_recursive(priv->debug);
3348c2ecf20Sopenharmony_ci	priv->debug = NULL;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci/*
3388c2ecf20Sopenharmony_ci * sysfs
3398c2ecf20Sopenharmony_ci */
3408c2ecf20Sopenharmony_cistatic ssize_t show_ideapad_cam(struct device *dev,
3418c2ecf20Sopenharmony_ci				struct device_attribute *attr,
3428c2ecf20Sopenharmony_ci				char *buf)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	unsigned long result;
3458c2ecf20Sopenharmony_ci	struct ideapad_private *priv = dev_get_drvdata(dev);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &result))
3488c2ecf20Sopenharmony_ci		return sprintf(buf, "-1\n");
3498c2ecf20Sopenharmony_ci	return sprintf(buf, "%lu\n", result);
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic ssize_t store_ideapad_cam(struct device *dev,
3538c2ecf20Sopenharmony_ci				 struct device_attribute *attr,
3548c2ecf20Sopenharmony_ci				 const char *buf, size_t count)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	int ret, state;
3578c2ecf20Sopenharmony_ci	struct ideapad_private *priv = dev_get_drvdata(dev);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if (!count)
3608c2ecf20Sopenharmony_ci		return 0;
3618c2ecf20Sopenharmony_ci	if (sscanf(buf, "%i", &state) != 1)
3628c2ecf20Sopenharmony_ci		return -EINVAL;
3638c2ecf20Sopenharmony_ci	ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_CAMERA, state);
3648c2ecf20Sopenharmony_ci	if (ret < 0)
3658c2ecf20Sopenharmony_ci		return -EIO;
3668c2ecf20Sopenharmony_ci	return count;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic ssize_t show_ideapad_fan(struct device *dev,
3728c2ecf20Sopenharmony_ci				struct device_attribute *attr,
3738c2ecf20Sopenharmony_ci				char *buf)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	unsigned long result;
3768c2ecf20Sopenharmony_ci	struct ideapad_private *priv = dev_get_drvdata(dev);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	if (read_ec_data(priv->adev->handle, VPCCMD_R_FAN, &result))
3798c2ecf20Sopenharmony_ci		return sprintf(buf, "-1\n");
3808c2ecf20Sopenharmony_ci	return sprintf(buf, "%lu\n", result);
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic ssize_t store_ideapad_fan(struct device *dev,
3848c2ecf20Sopenharmony_ci				 struct device_attribute *attr,
3858c2ecf20Sopenharmony_ci				 const char *buf, size_t count)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	int ret, state;
3888c2ecf20Sopenharmony_ci	struct ideapad_private *priv = dev_get_drvdata(dev);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	if (!count)
3918c2ecf20Sopenharmony_ci		return 0;
3928c2ecf20Sopenharmony_ci	if (sscanf(buf, "%i", &state) != 1)
3938c2ecf20Sopenharmony_ci		return -EINVAL;
3948c2ecf20Sopenharmony_ci	if (state < 0 || state > 4 || state == 3)
3958c2ecf20Sopenharmony_ci		return -EINVAL;
3968c2ecf20Sopenharmony_ci	ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_FAN, state);
3978c2ecf20Sopenharmony_ci	if (ret < 0)
3988c2ecf20Sopenharmony_ci		return -EIO;
3998c2ecf20Sopenharmony_ci	return count;
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_cistatic DEVICE_ATTR(fan_mode, 0644, show_ideapad_fan, store_ideapad_fan);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic ssize_t touchpad_show(struct device *dev,
4058c2ecf20Sopenharmony_ci			     struct device_attribute *attr,
4068c2ecf20Sopenharmony_ci			     char *buf)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	struct ideapad_private *priv = dev_get_drvdata(dev);
4098c2ecf20Sopenharmony_ci	unsigned long result;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	if (read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &result))
4128c2ecf20Sopenharmony_ci		return sprintf(buf, "-1\n");
4138c2ecf20Sopenharmony_ci	return sprintf(buf, "%lu\n", result);
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci/* Switch to RO for now: It might be revisited in the future */
4178c2ecf20Sopenharmony_cistatic ssize_t __maybe_unused touchpad_store(struct device *dev,
4188c2ecf20Sopenharmony_ci					     struct device_attribute *attr,
4198c2ecf20Sopenharmony_ci					     const char *buf, size_t count)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	struct ideapad_private *priv = dev_get_drvdata(dev);
4228c2ecf20Sopenharmony_ci	bool state;
4238c2ecf20Sopenharmony_ci	int ret;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	ret = kstrtobool(buf, &state);
4268c2ecf20Sopenharmony_ci	if (ret)
4278c2ecf20Sopenharmony_ci		return ret;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, state);
4308c2ecf20Sopenharmony_ci	if (ret < 0)
4318c2ecf20Sopenharmony_ci		return -EIO;
4328c2ecf20Sopenharmony_ci	return count;
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(touchpad);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_cistatic ssize_t conservation_mode_show(struct device *dev,
4388c2ecf20Sopenharmony_ci				struct device_attribute *attr,
4398c2ecf20Sopenharmony_ci				char *buf)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	struct ideapad_private *priv = dev_get_drvdata(dev);
4428c2ecf20Sopenharmony_ci	unsigned long result;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	if (method_gbmd(priv->adev->handle, &result))
4458c2ecf20Sopenharmony_ci		return sprintf(buf, "-1\n");
4468c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", test_bit(BM_CONSERVATION_BIT, &result));
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_cistatic ssize_t conservation_mode_store(struct device *dev,
4508c2ecf20Sopenharmony_ci				 struct device_attribute *attr,
4518c2ecf20Sopenharmony_ci				 const char *buf, size_t count)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	struct ideapad_private *priv = dev_get_drvdata(dev);
4548c2ecf20Sopenharmony_ci	bool state;
4558c2ecf20Sopenharmony_ci	int ret;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	ret = kstrtobool(buf, &state);
4588c2ecf20Sopenharmony_ci	if (ret)
4598c2ecf20Sopenharmony_ci		return ret;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	ret = method_int1(priv->adev->handle, "SBMC", state ?
4628c2ecf20Sopenharmony_ci					      BMCMD_CONSERVATION_ON :
4638c2ecf20Sopenharmony_ci					      BMCMD_CONSERVATION_OFF);
4648c2ecf20Sopenharmony_ci	if (ret < 0)
4658c2ecf20Sopenharmony_ci		return -EIO;
4668c2ecf20Sopenharmony_ci	return count;
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(conservation_mode);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_cistatic ssize_t fn_lock_show(struct device *dev,
4728c2ecf20Sopenharmony_ci			    struct device_attribute *attr,
4738c2ecf20Sopenharmony_ci			    char *buf)
4748c2ecf20Sopenharmony_ci{
4758c2ecf20Sopenharmony_ci	struct ideapad_private *priv = dev_get_drvdata(dev);
4768c2ecf20Sopenharmony_ci	unsigned long result;
4778c2ecf20Sopenharmony_ci	int hals;
4788c2ecf20Sopenharmony_ci	int fail = read_method_int(priv->adev->handle, "HALS", &hals);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	if (fail)
4818c2ecf20Sopenharmony_ci		return sprintf(buf, "-1\n");
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	result = hals;
4848c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", test_bit(HA_FNLOCK_BIT, &result));
4858c2ecf20Sopenharmony_ci}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_cistatic ssize_t fn_lock_store(struct device *dev,
4888c2ecf20Sopenharmony_ci			     struct device_attribute *attr,
4898c2ecf20Sopenharmony_ci			     const char *buf, size_t count)
4908c2ecf20Sopenharmony_ci{
4918c2ecf20Sopenharmony_ci	struct ideapad_private *priv = dev_get_drvdata(dev);
4928c2ecf20Sopenharmony_ci	bool state;
4938c2ecf20Sopenharmony_ci	int ret;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	ret = kstrtobool(buf, &state);
4968c2ecf20Sopenharmony_ci	if (ret)
4978c2ecf20Sopenharmony_ci		return ret;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	ret = method_int1(priv->adev->handle, "SALS", state ?
5008c2ecf20Sopenharmony_ci			  HACMD_FNLOCK_ON :
5018c2ecf20Sopenharmony_ci			  HACMD_FNLOCK_OFF);
5028c2ecf20Sopenharmony_ci	if (ret < 0)
5038c2ecf20Sopenharmony_ci		return -EIO;
5048c2ecf20Sopenharmony_ci	return count;
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(fn_lock);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_cistatic struct attribute *ideapad_attributes[] = {
5118c2ecf20Sopenharmony_ci	&dev_attr_camera_power.attr,
5128c2ecf20Sopenharmony_ci	&dev_attr_fan_mode.attr,
5138c2ecf20Sopenharmony_ci	&dev_attr_touchpad.attr,
5148c2ecf20Sopenharmony_ci	&dev_attr_conservation_mode.attr,
5158c2ecf20Sopenharmony_ci	&dev_attr_fn_lock.attr,
5168c2ecf20Sopenharmony_ci	NULL
5178c2ecf20Sopenharmony_ci};
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cistatic umode_t ideapad_is_visible(struct kobject *kobj,
5208c2ecf20Sopenharmony_ci				 struct attribute *attr,
5218c2ecf20Sopenharmony_ci				 int idx)
5228c2ecf20Sopenharmony_ci{
5238c2ecf20Sopenharmony_ci	struct device *dev = container_of(kobj, struct device, kobj);
5248c2ecf20Sopenharmony_ci	struct ideapad_private *priv = dev_get_drvdata(dev);
5258c2ecf20Sopenharmony_ci	bool supported;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	if (attr == &dev_attr_camera_power.attr)
5288c2ecf20Sopenharmony_ci		supported = test_bit(CFG_CAMERA_BIT, &(priv->cfg));
5298c2ecf20Sopenharmony_ci	else if (attr == &dev_attr_fan_mode.attr) {
5308c2ecf20Sopenharmony_ci		unsigned long value;
5318c2ecf20Sopenharmony_ci		supported = !read_ec_data(priv->adev->handle, VPCCMD_R_FAN,
5328c2ecf20Sopenharmony_ci					  &value);
5338c2ecf20Sopenharmony_ci	} else if (attr == &dev_attr_conservation_mode.attr) {
5348c2ecf20Sopenharmony_ci		supported = acpi_has_method(priv->adev->handle, "GBMD") &&
5358c2ecf20Sopenharmony_ci			    acpi_has_method(priv->adev->handle, "SBMC");
5368c2ecf20Sopenharmony_ci	} else if (attr == &dev_attr_fn_lock.attr) {
5378c2ecf20Sopenharmony_ci		supported = acpi_has_method(priv->adev->handle, "HALS") &&
5388c2ecf20Sopenharmony_ci			acpi_has_method(priv->adev->handle, "SALS");
5398c2ecf20Sopenharmony_ci	} else if (attr == &dev_attr_touchpad.attr)
5408c2ecf20Sopenharmony_ci		supported = priv->has_touchpad_switch;
5418c2ecf20Sopenharmony_ci	else
5428c2ecf20Sopenharmony_ci		supported = true;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	return supported ? attr->mode : 0;
5458c2ecf20Sopenharmony_ci}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_cistatic const struct attribute_group ideapad_attribute_group = {
5488c2ecf20Sopenharmony_ci	.is_visible = ideapad_is_visible,
5498c2ecf20Sopenharmony_ci	.attrs = ideapad_attributes
5508c2ecf20Sopenharmony_ci};
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci/*
5538c2ecf20Sopenharmony_ci * Rfkill
5548c2ecf20Sopenharmony_ci */
5558c2ecf20Sopenharmony_cistruct ideapad_rfk_data {
5568c2ecf20Sopenharmony_ci	char *name;
5578c2ecf20Sopenharmony_ci	int cfgbit;
5588c2ecf20Sopenharmony_ci	int opcode;
5598c2ecf20Sopenharmony_ci	int type;
5608c2ecf20Sopenharmony_ci};
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_cistatic const struct ideapad_rfk_data ideapad_rfk_data[] = {
5638c2ecf20Sopenharmony_ci	{ "ideapad_wlan",    CFG_WIFI_BIT, VPCCMD_W_WIFI, RFKILL_TYPE_WLAN },
5648c2ecf20Sopenharmony_ci	{ "ideapad_bluetooth", CFG_BT_BIT, VPCCMD_W_BT, RFKILL_TYPE_BLUETOOTH },
5658c2ecf20Sopenharmony_ci	{ "ideapad_3g",        CFG_3G_BIT, VPCCMD_W_3G, RFKILL_TYPE_WWAN },
5668c2ecf20Sopenharmony_ci};
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_cistatic int ideapad_rfk_set(void *data, bool blocked)
5698c2ecf20Sopenharmony_ci{
5708c2ecf20Sopenharmony_ci	struct ideapad_rfk_priv *priv = data;
5718c2ecf20Sopenharmony_ci	int opcode = ideapad_rfk_data[priv->dev].opcode;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	return write_ec_cmd(priv->priv->adev->handle, opcode, !blocked);
5748c2ecf20Sopenharmony_ci}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_cistatic const struct rfkill_ops ideapad_rfk_ops = {
5778c2ecf20Sopenharmony_ci	.set_block = ideapad_rfk_set,
5788c2ecf20Sopenharmony_ci};
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_cistatic void ideapad_sync_rfk_state(struct ideapad_private *priv)
5818c2ecf20Sopenharmony_ci{
5828c2ecf20Sopenharmony_ci	unsigned long hw_blocked = 0;
5838c2ecf20Sopenharmony_ci	int i;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	if (priv->has_hw_rfkill_switch) {
5868c2ecf20Sopenharmony_ci		if (read_ec_data(priv->adev->handle, VPCCMD_R_RF, &hw_blocked))
5878c2ecf20Sopenharmony_ci			return;
5888c2ecf20Sopenharmony_ci		hw_blocked = !hw_blocked;
5898c2ecf20Sopenharmony_ci	}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
5928c2ecf20Sopenharmony_ci		if (priv->rfk[i])
5938c2ecf20Sopenharmony_ci			rfkill_set_hw_state(priv->rfk[i], hw_blocked);
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_cistatic int ideapad_register_rfkill(struct ideapad_private *priv, int dev)
5978c2ecf20Sopenharmony_ci{
5988c2ecf20Sopenharmony_ci	int ret;
5998c2ecf20Sopenharmony_ci	unsigned long sw_blocked;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	if (no_bt_rfkill &&
6028c2ecf20Sopenharmony_ci	    (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) {
6038c2ecf20Sopenharmony_ci		/* Force to enable bluetooth when no_bt_rfkill=1 */
6048c2ecf20Sopenharmony_ci		write_ec_cmd(priv->adev->handle,
6058c2ecf20Sopenharmony_ci			     ideapad_rfk_data[dev].opcode, 1);
6068c2ecf20Sopenharmony_ci		return 0;
6078c2ecf20Sopenharmony_ci	}
6088c2ecf20Sopenharmony_ci	priv->rfk_priv[dev].dev = dev;
6098c2ecf20Sopenharmony_ci	priv->rfk_priv[dev].priv = priv;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name,
6128c2ecf20Sopenharmony_ci				      &priv->platform_device->dev,
6138c2ecf20Sopenharmony_ci				      ideapad_rfk_data[dev].type,
6148c2ecf20Sopenharmony_ci				      &ideapad_rfk_ops,
6158c2ecf20Sopenharmony_ci				      &priv->rfk_priv[dev]);
6168c2ecf20Sopenharmony_ci	if (!priv->rfk[dev])
6178c2ecf20Sopenharmony_ci		return -ENOMEM;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	if (read_ec_data(priv->adev->handle, ideapad_rfk_data[dev].opcode-1,
6208c2ecf20Sopenharmony_ci			 &sw_blocked)) {
6218c2ecf20Sopenharmony_ci		rfkill_init_sw_state(priv->rfk[dev], 0);
6228c2ecf20Sopenharmony_ci	} else {
6238c2ecf20Sopenharmony_ci		sw_blocked = !sw_blocked;
6248c2ecf20Sopenharmony_ci		rfkill_init_sw_state(priv->rfk[dev], sw_blocked);
6258c2ecf20Sopenharmony_ci	}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	ret = rfkill_register(priv->rfk[dev]);
6288c2ecf20Sopenharmony_ci	if (ret) {
6298c2ecf20Sopenharmony_ci		rfkill_destroy(priv->rfk[dev]);
6308c2ecf20Sopenharmony_ci		return ret;
6318c2ecf20Sopenharmony_ci	}
6328c2ecf20Sopenharmony_ci	return 0;
6338c2ecf20Sopenharmony_ci}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_cistatic void ideapad_unregister_rfkill(struct ideapad_private *priv, int dev)
6368c2ecf20Sopenharmony_ci{
6378c2ecf20Sopenharmony_ci	if (!priv->rfk[dev])
6388c2ecf20Sopenharmony_ci		return;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	rfkill_unregister(priv->rfk[dev]);
6418c2ecf20Sopenharmony_ci	rfkill_destroy(priv->rfk[dev]);
6428c2ecf20Sopenharmony_ci}
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci/*
6458c2ecf20Sopenharmony_ci * Platform device
6468c2ecf20Sopenharmony_ci */
6478c2ecf20Sopenharmony_cistatic int ideapad_sysfs_init(struct ideapad_private *priv)
6488c2ecf20Sopenharmony_ci{
6498c2ecf20Sopenharmony_ci	return sysfs_create_group(&priv->platform_device->dev.kobj,
6508c2ecf20Sopenharmony_ci				    &ideapad_attribute_group);
6518c2ecf20Sopenharmony_ci}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_cistatic void ideapad_sysfs_exit(struct ideapad_private *priv)
6548c2ecf20Sopenharmony_ci{
6558c2ecf20Sopenharmony_ci	sysfs_remove_group(&priv->platform_device->dev.kobj,
6568c2ecf20Sopenharmony_ci			   &ideapad_attribute_group);
6578c2ecf20Sopenharmony_ci}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci/*
6608c2ecf20Sopenharmony_ci * input device
6618c2ecf20Sopenharmony_ci */
6628c2ecf20Sopenharmony_cistatic const struct key_entry ideapad_keymap[] = {
6638c2ecf20Sopenharmony_ci	{ KE_KEY, 6,  { KEY_SWITCHVIDEOMODE } },
6648c2ecf20Sopenharmony_ci	{ KE_KEY, 7,  { KEY_CAMERA } },
6658c2ecf20Sopenharmony_ci	{ KE_KEY, 8,  { KEY_MICMUTE } },
6668c2ecf20Sopenharmony_ci	{ KE_KEY, 11, { KEY_F16 } },
6678c2ecf20Sopenharmony_ci	{ KE_KEY, 13, { KEY_WLAN } },
6688c2ecf20Sopenharmony_ci	{ KE_KEY, 16, { KEY_PROG1 } },
6698c2ecf20Sopenharmony_ci	{ KE_KEY, 17, { KEY_PROG2 } },
6708c2ecf20Sopenharmony_ci	{ KE_KEY, 64, { KEY_PROG3 } },
6718c2ecf20Sopenharmony_ci	{ KE_KEY, 65, { KEY_PROG4 } },
6728c2ecf20Sopenharmony_ci	{ KE_KEY, 66, { KEY_TOUCHPAD_OFF } },
6738c2ecf20Sopenharmony_ci	{ KE_KEY, 67, { KEY_TOUCHPAD_ON } },
6748c2ecf20Sopenharmony_ci	{ KE_KEY, 128, { KEY_ESC } },
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	{ KE_END, 0 },
6778c2ecf20Sopenharmony_ci};
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_cistatic int ideapad_input_init(struct ideapad_private *priv)
6808c2ecf20Sopenharmony_ci{
6818c2ecf20Sopenharmony_ci	struct input_dev *inputdev;
6828c2ecf20Sopenharmony_ci	int error;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	inputdev = input_allocate_device();
6858c2ecf20Sopenharmony_ci	if (!inputdev)
6868c2ecf20Sopenharmony_ci		return -ENOMEM;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	inputdev->name = "Ideapad extra buttons";
6898c2ecf20Sopenharmony_ci	inputdev->phys = "ideapad/input0";
6908c2ecf20Sopenharmony_ci	inputdev->id.bustype = BUS_HOST;
6918c2ecf20Sopenharmony_ci	inputdev->dev.parent = &priv->platform_device->dev;
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	error = sparse_keymap_setup(inputdev, ideapad_keymap, NULL);
6948c2ecf20Sopenharmony_ci	if (error) {
6958c2ecf20Sopenharmony_ci		pr_err("Unable to setup input device keymap\n");
6968c2ecf20Sopenharmony_ci		goto err_free_dev;
6978c2ecf20Sopenharmony_ci	}
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	error = input_register_device(inputdev);
7008c2ecf20Sopenharmony_ci	if (error) {
7018c2ecf20Sopenharmony_ci		pr_err("Unable to register input device\n");
7028c2ecf20Sopenharmony_ci		goto err_free_dev;
7038c2ecf20Sopenharmony_ci	}
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	priv->inputdev = inputdev;
7068c2ecf20Sopenharmony_ci	return 0;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_cierr_free_dev:
7098c2ecf20Sopenharmony_ci	input_free_device(inputdev);
7108c2ecf20Sopenharmony_ci	return error;
7118c2ecf20Sopenharmony_ci}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_cistatic void ideapad_input_exit(struct ideapad_private *priv)
7148c2ecf20Sopenharmony_ci{
7158c2ecf20Sopenharmony_ci	input_unregister_device(priv->inputdev);
7168c2ecf20Sopenharmony_ci	priv->inputdev = NULL;
7178c2ecf20Sopenharmony_ci}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_cistatic void ideapad_input_report(struct ideapad_private *priv,
7208c2ecf20Sopenharmony_ci				 unsigned long scancode)
7218c2ecf20Sopenharmony_ci{
7228c2ecf20Sopenharmony_ci	sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
7238c2ecf20Sopenharmony_ci}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_cistatic void ideapad_input_novokey(struct ideapad_private *priv)
7268c2ecf20Sopenharmony_ci{
7278c2ecf20Sopenharmony_ci	unsigned long long_pressed;
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	if (read_ec_data(priv->adev->handle, VPCCMD_R_NOVO, &long_pressed))
7308c2ecf20Sopenharmony_ci		return;
7318c2ecf20Sopenharmony_ci	if (long_pressed)
7328c2ecf20Sopenharmony_ci		ideapad_input_report(priv, 17);
7338c2ecf20Sopenharmony_ci	else
7348c2ecf20Sopenharmony_ci		ideapad_input_report(priv, 16);
7358c2ecf20Sopenharmony_ci}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_cistatic void ideapad_check_special_buttons(struct ideapad_private *priv)
7388c2ecf20Sopenharmony_ci{
7398c2ecf20Sopenharmony_ci	unsigned long bit, value;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	read_ec_data(priv->adev->handle, VPCCMD_R_SPECIAL_BUTTONS, &value);
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	for (bit = 0; bit < 16; bit++) {
7448c2ecf20Sopenharmony_ci		if (test_bit(bit, &value)) {
7458c2ecf20Sopenharmony_ci			switch (bit) {
7468c2ecf20Sopenharmony_ci			case 0:	/* Z580 */
7478c2ecf20Sopenharmony_ci			case 6:	/* Z570 */
7488c2ecf20Sopenharmony_ci				/* Thermal Management button */
7498c2ecf20Sopenharmony_ci				ideapad_input_report(priv, 65);
7508c2ecf20Sopenharmony_ci				break;
7518c2ecf20Sopenharmony_ci			case 1:
7528c2ecf20Sopenharmony_ci				/* OneKey Theater button */
7538c2ecf20Sopenharmony_ci				ideapad_input_report(priv, 64);
7548c2ecf20Sopenharmony_ci				break;
7558c2ecf20Sopenharmony_ci			default:
7568c2ecf20Sopenharmony_ci				pr_info("Unknown special button: %lu\n", bit);
7578c2ecf20Sopenharmony_ci				break;
7588c2ecf20Sopenharmony_ci			}
7598c2ecf20Sopenharmony_ci		}
7608c2ecf20Sopenharmony_ci	}
7618c2ecf20Sopenharmony_ci}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci/*
7648c2ecf20Sopenharmony_ci * backlight
7658c2ecf20Sopenharmony_ci */
7668c2ecf20Sopenharmony_cistatic int ideapad_backlight_get_brightness(struct backlight_device *blightdev)
7678c2ecf20Sopenharmony_ci{
7688c2ecf20Sopenharmony_ci	struct ideapad_private *priv = bl_get_data(blightdev);
7698c2ecf20Sopenharmony_ci	unsigned long now;
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	if (!priv)
7728c2ecf20Sopenharmony_ci		return -EINVAL;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	if (read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now))
7758c2ecf20Sopenharmony_ci		return -EIO;
7768c2ecf20Sopenharmony_ci	return now;
7778c2ecf20Sopenharmony_ci}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_cistatic int ideapad_backlight_update_status(struct backlight_device *blightdev)
7808c2ecf20Sopenharmony_ci{
7818c2ecf20Sopenharmony_ci	struct ideapad_private *priv = bl_get_data(blightdev);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	if (!priv)
7848c2ecf20Sopenharmony_ci		return -EINVAL;
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	if (write_ec_cmd(priv->adev->handle, VPCCMD_W_BL,
7878c2ecf20Sopenharmony_ci			 blightdev->props.brightness))
7888c2ecf20Sopenharmony_ci		return -EIO;
7898c2ecf20Sopenharmony_ci	if (write_ec_cmd(priv->adev->handle, VPCCMD_W_BL_POWER,
7908c2ecf20Sopenharmony_ci			 blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1))
7918c2ecf20Sopenharmony_ci		return -EIO;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	return 0;
7948c2ecf20Sopenharmony_ci}
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_cistatic const struct backlight_ops ideapad_backlight_ops = {
7978c2ecf20Sopenharmony_ci	.get_brightness = ideapad_backlight_get_brightness,
7988c2ecf20Sopenharmony_ci	.update_status = ideapad_backlight_update_status,
7998c2ecf20Sopenharmony_ci};
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_cistatic int ideapad_backlight_init(struct ideapad_private *priv)
8028c2ecf20Sopenharmony_ci{
8038c2ecf20Sopenharmony_ci	struct backlight_device *blightdev;
8048c2ecf20Sopenharmony_ci	struct backlight_properties props;
8058c2ecf20Sopenharmony_ci	unsigned long max, now, power;
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &max))
8088c2ecf20Sopenharmony_ci		return -EIO;
8098c2ecf20Sopenharmony_ci	if (read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now))
8108c2ecf20Sopenharmony_ci		return -EIO;
8118c2ecf20Sopenharmony_ci	if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))
8128c2ecf20Sopenharmony_ci		return -EIO;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	memset(&props, 0, sizeof(struct backlight_properties));
8158c2ecf20Sopenharmony_ci	props.max_brightness = max;
8168c2ecf20Sopenharmony_ci	props.type = BACKLIGHT_PLATFORM;
8178c2ecf20Sopenharmony_ci	blightdev = backlight_device_register("ideapad",
8188c2ecf20Sopenharmony_ci					      &priv->platform_device->dev,
8198c2ecf20Sopenharmony_ci					      priv,
8208c2ecf20Sopenharmony_ci					      &ideapad_backlight_ops,
8218c2ecf20Sopenharmony_ci					      &props);
8228c2ecf20Sopenharmony_ci	if (IS_ERR(blightdev)) {
8238c2ecf20Sopenharmony_ci		pr_err("Could not register backlight device\n");
8248c2ecf20Sopenharmony_ci		return PTR_ERR(blightdev);
8258c2ecf20Sopenharmony_ci	}
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	priv->blightdev = blightdev;
8288c2ecf20Sopenharmony_ci	blightdev->props.brightness = now;
8298c2ecf20Sopenharmony_ci	blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
8308c2ecf20Sopenharmony_ci	backlight_update_status(blightdev);
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	return 0;
8338c2ecf20Sopenharmony_ci}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_cistatic void ideapad_backlight_exit(struct ideapad_private *priv)
8368c2ecf20Sopenharmony_ci{
8378c2ecf20Sopenharmony_ci	backlight_device_unregister(priv->blightdev);
8388c2ecf20Sopenharmony_ci	priv->blightdev = NULL;
8398c2ecf20Sopenharmony_ci}
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_cistatic void ideapad_backlight_notify_power(struct ideapad_private *priv)
8428c2ecf20Sopenharmony_ci{
8438c2ecf20Sopenharmony_ci	unsigned long power;
8448c2ecf20Sopenharmony_ci	struct backlight_device *blightdev = priv->blightdev;
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	if (!blightdev)
8478c2ecf20Sopenharmony_ci		return;
8488c2ecf20Sopenharmony_ci	if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))
8498c2ecf20Sopenharmony_ci		return;
8508c2ecf20Sopenharmony_ci	blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
8518c2ecf20Sopenharmony_ci}
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_cistatic void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
8548c2ecf20Sopenharmony_ci{
8558c2ecf20Sopenharmony_ci	unsigned long now;
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	/* if we control brightness via acpi video driver */
8588c2ecf20Sopenharmony_ci	if (priv->blightdev == NULL) {
8598c2ecf20Sopenharmony_ci		read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now);
8608c2ecf20Sopenharmony_ci		return;
8618c2ecf20Sopenharmony_ci	}
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	backlight_force_update(priv->blightdev, BACKLIGHT_UPDATE_HOTKEY);
8648c2ecf20Sopenharmony_ci}
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci/*
8678c2ecf20Sopenharmony_ci * module init/exit
8688c2ecf20Sopenharmony_ci */
8698c2ecf20Sopenharmony_cistatic void ideapad_sync_touchpad_state(struct ideapad_private *priv)
8708c2ecf20Sopenharmony_ci{
8718c2ecf20Sopenharmony_ci	unsigned long value;
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	if (!priv->has_touchpad_switch)
8748c2ecf20Sopenharmony_ci		return;
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	/* Without reading from EC touchpad LED doesn't switch state */
8778c2ecf20Sopenharmony_ci	if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value)) {
8788c2ecf20Sopenharmony_ci		/* Some IdeaPads don't really turn off touchpad - they only
8798c2ecf20Sopenharmony_ci		 * switch the LED state. We (de)activate KBC AUX port to turn
8808c2ecf20Sopenharmony_ci		 * touchpad off and on. We send KEY_TOUCHPAD_OFF and
8818c2ecf20Sopenharmony_ci		 * KEY_TOUCHPAD_ON to not to get out of sync with LED */
8828c2ecf20Sopenharmony_ci		unsigned char param;
8838c2ecf20Sopenharmony_ci		i8042_command(&param, value ? I8042_CMD_AUX_ENABLE :
8848c2ecf20Sopenharmony_ci			      I8042_CMD_AUX_DISABLE);
8858c2ecf20Sopenharmony_ci		ideapad_input_report(priv, value ? 67 : 66);
8868c2ecf20Sopenharmony_ci	}
8878c2ecf20Sopenharmony_ci}
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_cistatic void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)
8908c2ecf20Sopenharmony_ci{
8918c2ecf20Sopenharmony_ci	struct ideapad_private *priv = data;
8928c2ecf20Sopenharmony_ci	unsigned long vpc1, vpc2, vpc_bit;
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	if (read_ec_data(handle, VPCCMD_R_VPC1, &vpc1))
8958c2ecf20Sopenharmony_ci		return;
8968c2ecf20Sopenharmony_ci	if (read_ec_data(handle, VPCCMD_R_VPC2, &vpc2))
8978c2ecf20Sopenharmony_ci		return;
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	vpc1 = (vpc2 << 8) | vpc1;
9008c2ecf20Sopenharmony_ci	for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) {
9018c2ecf20Sopenharmony_ci		if (test_bit(vpc_bit, &vpc1)) {
9028c2ecf20Sopenharmony_ci			switch (vpc_bit) {
9038c2ecf20Sopenharmony_ci			case 9:
9048c2ecf20Sopenharmony_ci				ideapad_sync_rfk_state(priv);
9058c2ecf20Sopenharmony_ci				break;
9068c2ecf20Sopenharmony_ci			case 13:
9078c2ecf20Sopenharmony_ci			case 11:
9088c2ecf20Sopenharmony_ci			case 8:
9098c2ecf20Sopenharmony_ci			case 7:
9108c2ecf20Sopenharmony_ci			case 6:
9118c2ecf20Sopenharmony_ci				ideapad_input_report(priv, vpc_bit);
9128c2ecf20Sopenharmony_ci				break;
9138c2ecf20Sopenharmony_ci			case 5:
9148c2ecf20Sopenharmony_ci				ideapad_sync_touchpad_state(priv);
9158c2ecf20Sopenharmony_ci				break;
9168c2ecf20Sopenharmony_ci			case 4:
9178c2ecf20Sopenharmony_ci				ideapad_backlight_notify_brightness(priv);
9188c2ecf20Sopenharmony_ci				break;
9198c2ecf20Sopenharmony_ci			case 3:
9208c2ecf20Sopenharmony_ci				ideapad_input_novokey(priv);
9218c2ecf20Sopenharmony_ci				break;
9228c2ecf20Sopenharmony_ci			case 2:
9238c2ecf20Sopenharmony_ci				ideapad_backlight_notify_power(priv);
9248c2ecf20Sopenharmony_ci				break;
9258c2ecf20Sopenharmony_ci			case 0:
9268c2ecf20Sopenharmony_ci				ideapad_check_special_buttons(priv);
9278c2ecf20Sopenharmony_ci				break;
9288c2ecf20Sopenharmony_ci			case 1:
9298c2ecf20Sopenharmony_ci				/* Some IdeaPads report event 1 every ~20
9308c2ecf20Sopenharmony_ci				 * seconds while on battery power; some
9318c2ecf20Sopenharmony_ci				 * report this when changing to/from tablet
9328c2ecf20Sopenharmony_ci				 * mode. Squelch this event.
9338c2ecf20Sopenharmony_ci				 */
9348c2ecf20Sopenharmony_ci				break;
9358c2ecf20Sopenharmony_ci			default:
9368c2ecf20Sopenharmony_ci				pr_info("Unknown event: %lu\n", vpc_bit);
9378c2ecf20Sopenharmony_ci			}
9388c2ecf20Sopenharmony_ci		}
9398c2ecf20Sopenharmony_ci	}
9408c2ecf20Sopenharmony_ci}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_ACPI_WMI)
9438c2ecf20Sopenharmony_cistatic void ideapad_wmi_notify(u32 value, void *context)
9448c2ecf20Sopenharmony_ci{
9458c2ecf20Sopenharmony_ci	switch (value) {
9468c2ecf20Sopenharmony_ci	case 128:
9478c2ecf20Sopenharmony_ci		ideapad_input_report(context, value);
9488c2ecf20Sopenharmony_ci		break;
9498c2ecf20Sopenharmony_ci	default:
9508c2ecf20Sopenharmony_ci		pr_info("Unknown WMI event %u\n", value);
9518c2ecf20Sopenharmony_ci	}
9528c2ecf20Sopenharmony_ci}
9538c2ecf20Sopenharmony_ci#endif
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci/*
9568c2ecf20Sopenharmony_ci * Some ideapads have a hardware rfkill switch, but most do not have one.
9578c2ecf20Sopenharmony_ci * Reading VPCCMD_R_RF always results in 0 on models without a hardware rfkill,
9588c2ecf20Sopenharmony_ci * switch causing ideapad_laptop to wrongly report all radios as hw-blocked.
9598c2ecf20Sopenharmony_ci * There used to be a long list of DMI ids for models without a hw rfkill
9608c2ecf20Sopenharmony_ci * switch here, but that resulted in playing whack a mole.
9618c2ecf20Sopenharmony_ci * More importantly wrongly reporting the wifi radio as hw-blocked, results in
9628c2ecf20Sopenharmony_ci * non working wifi. Whereas not reporting it hw-blocked, when it actually is
9638c2ecf20Sopenharmony_ci * hw-blocked results in an empty SSID list, which is a much more benign
9648c2ecf20Sopenharmony_ci * failure mode.
9658c2ecf20Sopenharmony_ci * So the default now is the much safer option of assuming there is no
9668c2ecf20Sopenharmony_ci * hardware rfkill switch. This default also actually matches most hardware,
9678c2ecf20Sopenharmony_ci * since having a hw rfkill switch is quite rare on modern hardware, so this
9688c2ecf20Sopenharmony_ci * also leads to a much shorter list.
9698c2ecf20Sopenharmony_ci */
9708c2ecf20Sopenharmony_cistatic const struct dmi_system_id hw_rfkill_list[] = {
9718c2ecf20Sopenharmony_ci	{}
9728c2ecf20Sopenharmony_ci};
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_cistatic int ideapad_acpi_add(struct platform_device *pdev)
9758c2ecf20Sopenharmony_ci{
9768c2ecf20Sopenharmony_ci	int ret, i;
9778c2ecf20Sopenharmony_ci	int cfg;
9788c2ecf20Sopenharmony_ci	struct ideapad_private *priv;
9798c2ecf20Sopenharmony_ci	struct acpi_device *adev;
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	ret = acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev);
9828c2ecf20Sopenharmony_ci	if (ret)
9838c2ecf20Sopenharmony_ci		return -ENODEV;
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	if (read_method_int(adev->handle, "_CFG", &cfg))
9868c2ecf20Sopenharmony_ci		return -ENODEV;
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
9898c2ecf20Sopenharmony_ci	if (!priv)
9908c2ecf20Sopenharmony_ci		return -ENOMEM;
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	dev_set_drvdata(&pdev->dev, priv);
9938c2ecf20Sopenharmony_ci	priv->cfg = cfg;
9948c2ecf20Sopenharmony_ci	priv->adev = adev;
9958c2ecf20Sopenharmony_ci	priv->platform_device = pdev;
9968c2ecf20Sopenharmony_ci	priv->has_hw_rfkill_switch = dmi_check_system(hw_rfkill_list);
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	/* Most ideapads with ELAN0634 touchpad don't use EC touchpad switch */
9998c2ecf20Sopenharmony_ci	priv->has_touchpad_switch = !acpi_dev_present("ELAN0634", NULL, -1);
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	ret = ideapad_sysfs_init(priv);
10028c2ecf20Sopenharmony_ci	if (ret)
10038c2ecf20Sopenharmony_ci		return ret;
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	ideapad_debugfs_init(priv);
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	ret = ideapad_input_init(priv);
10088c2ecf20Sopenharmony_ci	if (ret)
10098c2ecf20Sopenharmony_ci		goto input_failed;
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	/*
10128c2ecf20Sopenharmony_ci	 * On some models without a hw-switch (the yoga 2 13 at least)
10138c2ecf20Sopenharmony_ci	 * VPCCMD_W_RF must be explicitly set to 1 for the wifi to work.
10148c2ecf20Sopenharmony_ci	 */
10158c2ecf20Sopenharmony_ci	if (!priv->has_hw_rfkill_switch)
10168c2ecf20Sopenharmony_ci		write_ec_cmd(priv->adev->handle, VPCCMD_W_RF, 1);
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	/* The same for Touchpad */
10198c2ecf20Sopenharmony_ci	if (!priv->has_touchpad_switch)
10208c2ecf20Sopenharmony_ci		write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, 1);
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
10238c2ecf20Sopenharmony_ci		if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg))
10248c2ecf20Sopenharmony_ci			ideapad_register_rfkill(priv, i);
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	ideapad_sync_rfk_state(priv);
10278c2ecf20Sopenharmony_ci	ideapad_sync_touchpad_state(priv);
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
10308c2ecf20Sopenharmony_ci		ret = ideapad_backlight_init(priv);
10318c2ecf20Sopenharmony_ci		if (ret && ret != -ENODEV)
10328c2ecf20Sopenharmony_ci			goto backlight_failed;
10338c2ecf20Sopenharmony_ci	}
10348c2ecf20Sopenharmony_ci	ret = acpi_install_notify_handler(adev->handle,
10358c2ecf20Sopenharmony_ci		ACPI_DEVICE_NOTIFY, ideapad_acpi_notify, priv);
10368c2ecf20Sopenharmony_ci	if (ret)
10378c2ecf20Sopenharmony_ci		goto notification_failed;
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_ACPI_WMI)
10408c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ideapad_wmi_fnesc_events); i++) {
10418c2ecf20Sopenharmony_ci		ret = wmi_install_notify_handler(ideapad_wmi_fnesc_events[i],
10428c2ecf20Sopenharmony_ci						 ideapad_wmi_notify, priv);
10438c2ecf20Sopenharmony_ci		if (ret == AE_OK) {
10448c2ecf20Sopenharmony_ci			priv->fnesc_guid = ideapad_wmi_fnesc_events[i];
10458c2ecf20Sopenharmony_ci			break;
10468c2ecf20Sopenharmony_ci		}
10478c2ecf20Sopenharmony_ci	}
10488c2ecf20Sopenharmony_ci	if (ret != AE_OK && ret != AE_NOT_EXIST)
10498c2ecf20Sopenharmony_ci		goto notification_failed_wmi;
10508c2ecf20Sopenharmony_ci#endif
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	return 0;
10538c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_ACPI_WMI)
10548c2ecf20Sopenharmony_cinotification_failed_wmi:
10558c2ecf20Sopenharmony_ci	acpi_remove_notify_handler(priv->adev->handle,
10568c2ecf20Sopenharmony_ci		ACPI_DEVICE_NOTIFY, ideapad_acpi_notify);
10578c2ecf20Sopenharmony_ci#endif
10588c2ecf20Sopenharmony_cinotification_failed:
10598c2ecf20Sopenharmony_ci	ideapad_backlight_exit(priv);
10608c2ecf20Sopenharmony_cibacklight_failed:
10618c2ecf20Sopenharmony_ci	for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
10628c2ecf20Sopenharmony_ci		ideapad_unregister_rfkill(priv, i);
10638c2ecf20Sopenharmony_ci	ideapad_input_exit(priv);
10648c2ecf20Sopenharmony_ciinput_failed:
10658c2ecf20Sopenharmony_ci	ideapad_debugfs_exit(priv);
10668c2ecf20Sopenharmony_ci	ideapad_sysfs_exit(priv);
10678c2ecf20Sopenharmony_ci	return ret;
10688c2ecf20Sopenharmony_ci}
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_cistatic int ideapad_acpi_remove(struct platform_device *pdev)
10718c2ecf20Sopenharmony_ci{
10728c2ecf20Sopenharmony_ci	struct ideapad_private *priv = dev_get_drvdata(&pdev->dev);
10738c2ecf20Sopenharmony_ci	int i;
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_ACPI_WMI)
10768c2ecf20Sopenharmony_ci	if (priv->fnesc_guid)
10778c2ecf20Sopenharmony_ci		wmi_remove_notify_handler(priv->fnesc_guid);
10788c2ecf20Sopenharmony_ci#endif
10798c2ecf20Sopenharmony_ci	acpi_remove_notify_handler(priv->adev->handle,
10808c2ecf20Sopenharmony_ci		ACPI_DEVICE_NOTIFY, ideapad_acpi_notify);
10818c2ecf20Sopenharmony_ci	ideapad_backlight_exit(priv);
10828c2ecf20Sopenharmony_ci	for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
10838c2ecf20Sopenharmony_ci		ideapad_unregister_rfkill(priv, i);
10848c2ecf20Sopenharmony_ci	ideapad_input_exit(priv);
10858c2ecf20Sopenharmony_ci	ideapad_debugfs_exit(priv);
10868c2ecf20Sopenharmony_ci	ideapad_sysfs_exit(priv);
10878c2ecf20Sopenharmony_ci	dev_set_drvdata(&pdev->dev, NULL);
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	return 0;
10908c2ecf20Sopenharmony_ci}
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
10938c2ecf20Sopenharmony_cistatic int ideapad_acpi_resume(struct device *device)
10948c2ecf20Sopenharmony_ci{
10958c2ecf20Sopenharmony_ci	struct ideapad_private *priv;
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	if (!device)
10988c2ecf20Sopenharmony_ci		return -EINVAL;
10998c2ecf20Sopenharmony_ci	priv = dev_get_drvdata(device);
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	ideapad_sync_rfk_state(priv);
11028c2ecf20Sopenharmony_ci	ideapad_sync_touchpad_state(priv);
11038c2ecf20Sopenharmony_ci	return 0;
11048c2ecf20Sopenharmony_ci}
11058c2ecf20Sopenharmony_ci#endif
11068c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(ideapad_pm, NULL, ideapad_acpi_resume);
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_cistatic const struct acpi_device_id ideapad_device_ids[] = {
11098c2ecf20Sopenharmony_ci	{ "VPC2004", 0},
11108c2ecf20Sopenharmony_ci	{ "", 0},
11118c2ecf20Sopenharmony_ci};
11128c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_cistatic struct platform_driver ideapad_acpi_driver = {
11158c2ecf20Sopenharmony_ci	.probe = ideapad_acpi_add,
11168c2ecf20Sopenharmony_ci	.remove = ideapad_acpi_remove,
11178c2ecf20Sopenharmony_ci	.driver = {
11188c2ecf20Sopenharmony_ci		.name   = "ideapad_acpi",
11198c2ecf20Sopenharmony_ci		.pm     = &ideapad_pm,
11208c2ecf20Sopenharmony_ci		.acpi_match_table = ACPI_PTR(ideapad_device_ids),
11218c2ecf20Sopenharmony_ci	},
11228c2ecf20Sopenharmony_ci};
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_cimodule_platform_driver(ideapad_acpi_driver);
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
11278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IdeaPad ACPI Extras");
11288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1129