brintos

brintos / linux-shallow public Read only

0
0
Text · 57.6 KiB · c908f52 Raw
2348 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  ideapad-laptop.c - Lenovo IdeaPad ACPI Extras4 *5 *  Copyright © 2010 Intel Corporation6 *  Copyright © 2010 David Woodhouse <dwmw2@infradead.org>7 */8 9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt10 11#include <linux/acpi.h>12#include <linux/backlight.h>13#include <linux/bitfield.h>14#include <linux/bitops.h>15#include <linux/bug.h>16#include <linux/cleanup.h>17#include <linux/debugfs.h>18#include <linux/device.h>19#include <linux/dmi.h>20#include <linux/i8042.h>21#include <linux/init.h>22#include <linux/input.h>23#include <linux/input/sparse-keymap.h>24#include <linux/jiffies.h>25#include <linux/kernel.h>26#include <linux/leds.h>27#include <linux/module.h>28#include <linux/platform_device.h>29#include <linux/platform_profile.h>30#include <linux/rfkill.h>31#include <linux/seq_file.h>32#include <linux/sysfs.h>33#include <linux/types.h>34#include <linux/wmi.h>35#include "ideapad-laptop.h"36 37#include <acpi/video.h>38 39#include <dt-bindings/leds/common.h>40 41#define IDEAPAD_RFKILL_DEV_NUM	342 43enum {44	CFG_CAP_BT_BIT       = 16,45	CFG_CAP_3G_BIT       = 17,46	CFG_CAP_WIFI_BIT     = 18,47	CFG_CAP_CAM_BIT      = 19,48 49	/*50	 * These are OnScreenDisplay support bits that can be useful to determine51	 * whether a hotkey exists/should show OSD. But they aren't particularly52	 * meaningful since they were introduced later, i.e. 2010 IdeaPads53	 * don't have these, but they still have had OSD for hotkeys.54	 */55	CFG_OSD_NUMLK_BIT    = 27,56	CFG_OSD_CAPSLK_BIT   = 28,57	CFG_OSD_MICMUTE_BIT  = 29,58	CFG_OSD_TOUCHPAD_BIT = 30,59	CFG_OSD_CAM_BIT      = 31,60};61 62enum {63	GBMD_CONSERVATION_STATE_BIT = 5,64};65 66enum {67	SBMC_CONSERVATION_ON  = 3,68	SBMC_CONSERVATION_OFF = 5,69};70 71enum {72	HALS_KBD_BL_SUPPORT_BIT       = 4,73	HALS_KBD_BL_STATE_BIT         = 5,74	HALS_USB_CHARGING_SUPPORT_BIT = 6,75	HALS_USB_CHARGING_STATE_BIT   = 7,76	HALS_FNLOCK_SUPPORT_BIT       = 9,77	HALS_FNLOCK_STATE_BIT         = 10,78	HALS_HOTKEYS_PRIMARY_BIT      = 11,79};80 81enum {82	SALS_KBD_BL_ON        = 0x8,83	SALS_KBD_BL_OFF       = 0x9,84	SALS_USB_CHARGING_ON  = 0xa,85	SALS_USB_CHARGING_OFF = 0xb,86	SALS_FNLOCK_ON        = 0xe,87	SALS_FNLOCK_OFF       = 0xf,88};89 90enum {91	VPCCMD_R_VPC1 = 0x10,92	VPCCMD_R_BL_MAX,93	VPCCMD_R_BL,94	VPCCMD_W_BL,95	VPCCMD_R_WIFI,96	VPCCMD_W_WIFI,97	VPCCMD_R_BT,98	VPCCMD_W_BT,99	VPCCMD_R_BL_POWER,100	VPCCMD_R_NOVO,101	VPCCMD_R_VPC2,102	VPCCMD_R_TOUCHPAD,103	VPCCMD_W_TOUCHPAD,104	VPCCMD_R_CAMERA,105	VPCCMD_W_CAMERA,106	VPCCMD_R_3G,107	VPCCMD_W_3G,108	VPCCMD_R_ODD, /* 0x21 */109	VPCCMD_W_FAN,110	VPCCMD_R_RF,111	VPCCMD_W_RF,112	VPCCMD_W_YMC = 0x2A,113	VPCCMD_R_FAN = 0x2B,114	VPCCMD_R_SPECIAL_BUTTONS = 0x31,115	VPCCMD_W_BL_POWER = 0x33,116};117 118/*119 * These correspond to the number of supported states - 1120 * Future keyboard types may need a new system, if there's a collision121 * KBD_BL_TRISTATE_AUTO has no way to report or set the auto state122 * so it effectively has 3 states, but needs to handle 4123 */124enum {125	KBD_BL_STANDARD      = 1,126	KBD_BL_TRISTATE      = 2,127	KBD_BL_TRISTATE_AUTO = 3,128};129 130#define KBD_BL_QUERY_TYPE		0x1131#define KBD_BL_TRISTATE_TYPE		0x5132#define KBD_BL_TRISTATE_AUTO_TYPE	0x7133 134#define KBD_BL_COMMAND_GET		0x2135#define KBD_BL_COMMAND_SET		0x3136#define KBD_BL_COMMAND_TYPE		GENMASK(7, 4)137 138#define KBD_BL_GET_BRIGHTNESS		GENMASK(15, 1)139#define KBD_BL_SET_BRIGHTNESS		GENMASK(19, 16)140 141#define KBD_BL_KBLC_CHANGED_EVENT	12142 143struct ideapad_dytc_priv {144	enum platform_profile_option current_profile;145	struct platform_profile_handler pprof;146	struct mutex mutex; /* protects the DYTC interface */147	struct ideapad_private *priv;148};149 150struct ideapad_rfk_priv {151	int dev;152	struct ideapad_private *priv;153};154 155struct ideapad_private {156	struct acpi_device *adev;157	struct mutex vpc_mutex; /* protects the VPC calls */158	struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];159	struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM];160	struct platform_device *platform_device;161	struct input_dev *inputdev;162	struct backlight_device *blightdev;163	struct ideapad_dytc_priv *dytc;164	struct dentry *debug;165	unsigned long cfg;166	unsigned long r_touchpad_val;167	struct {168		bool conservation_mode    : 1;169		bool dytc                 : 1;170		bool fan_mode             : 1;171		bool fn_lock              : 1;172		bool set_fn_lock_led      : 1;173		bool hw_rfkill_switch     : 1;174		bool kbd_bl               : 1;175		bool touchpad_ctrl_via_ec : 1;176		bool ctrl_ps2_aux_port    : 1;177		bool usb_charging         : 1;178		bool ymc_ec_trigger       : 1;179	} features;180	struct {181		bool initialized;182		int type;183		struct led_classdev led;184		unsigned int last_brightness;185	} kbd_bl;186	struct {187		bool initialized;188		struct led_classdev led;189		unsigned int last_brightness;190	} fn_lock;191};192 193static bool no_bt_rfkill;194module_param(no_bt_rfkill, bool, 0444);195MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");196 197static bool allow_v4_dytc;198module_param(allow_v4_dytc, bool, 0444);199MODULE_PARM_DESC(allow_v4_dytc,200	"Enable DYTC version 4 platform-profile support. "201	"If you need this please report this to: platform-driver-x86@vger.kernel.org");202 203static bool hw_rfkill_switch;204module_param(hw_rfkill_switch, bool, 0444);205MODULE_PARM_DESC(hw_rfkill_switch,206	"Enable rfkill support for laptops with a hw on/off wifi switch/slider. "207	"If you need this please report this to: platform-driver-x86@vger.kernel.org");208 209static bool set_fn_lock_led;210module_param(set_fn_lock_led, bool, 0444);211MODULE_PARM_DESC(set_fn_lock_led,212	"Enable driver based updates of the fn-lock LED on fn-lock changes. "213	"If you need this please report this to: platform-driver-x86@vger.kernel.org");214 215static bool ctrl_ps2_aux_port;216module_param(ctrl_ps2_aux_port, bool, 0444);217MODULE_PARM_DESC(ctrl_ps2_aux_port,218	"Enable driver based PS/2 aux port en-/dis-abling on touchpad on/off toggle. "219	"If you need this please report this to: platform-driver-x86@vger.kernel.org");220 221static bool touchpad_ctrl_via_ec;222module_param(touchpad_ctrl_via_ec, bool, 0444);223MODULE_PARM_DESC(touchpad_ctrl_via_ec,224	"Enable registering a 'touchpad' sysfs-attribute which can be used to manually "225	"tell the EC to enable/disable the touchpad. This may not work on all models.");226 227static bool ymc_ec_trigger __read_mostly;228module_param(ymc_ec_trigger, bool, 0444);229MODULE_PARM_DESC(ymc_ec_trigger,230	"Enable EC triggering work-around to force emitting tablet mode events. "231	"If you need this please report this to: platform-driver-x86@vger.kernel.org");232 233/*234 * shared data235 */236 237static struct ideapad_private *ideapad_shared;238static DEFINE_MUTEX(ideapad_shared_mutex);239 240static int ideapad_shared_init(struct ideapad_private *priv)241{242	int ret;243 244	guard(mutex)(&ideapad_shared_mutex);245 246	if (!ideapad_shared) {247		ideapad_shared = priv;248		ret = 0;249	} else {250		dev_warn(&priv->adev->dev, "found multiple platform devices\n");251		ret = -EINVAL;252	}253 254	return ret;255}256 257static void ideapad_shared_exit(struct ideapad_private *priv)258{259	guard(mutex)(&ideapad_shared_mutex);260 261	if (ideapad_shared == priv)262		ideapad_shared = NULL;263}264 265/*266 * ACPI Helpers267 */268#define IDEAPAD_EC_TIMEOUT 200 /* in ms */269 270static int eval_int(acpi_handle handle, const char *name, unsigned long *res)271{272	unsigned long long result;273	acpi_status status;274 275	status = acpi_evaluate_integer(handle, (char *)name, NULL, &result);276	if (ACPI_FAILURE(status))277		return -EIO;278 279	*res = result;280 281	return 0;282}283 284static int eval_int_with_arg(acpi_handle handle, const char *name, unsigned long arg,285			     unsigned long *res)286{287	struct acpi_object_list params;288	unsigned long long result;289	union acpi_object in_obj;290	acpi_status status;291 292	params.count = 1;293	params.pointer = &in_obj;294	in_obj.type = ACPI_TYPE_INTEGER;295	in_obj.integer.value = arg;296 297	status = acpi_evaluate_integer(handle, (char *)name, &params, &result);298	if (ACPI_FAILURE(status))299		return -EIO;300 301	if (res)302		*res = result;303 304	return 0;305}306 307static int exec_simple_method(acpi_handle handle, const char *name, unsigned long arg)308{309	acpi_status status = acpi_execute_simple_method(handle, (char *)name, arg);310 311	return ACPI_FAILURE(status) ? -EIO : 0;312}313 314static int eval_gbmd(acpi_handle handle, unsigned long *res)315{316	return eval_int(handle, "GBMD", res);317}318 319static int exec_sbmc(acpi_handle handle, unsigned long arg)320{321	return exec_simple_method(handle, "SBMC", arg);322}323 324static int eval_hals(acpi_handle handle, unsigned long *res)325{326	return eval_int(handle, "HALS", res);327}328 329static int exec_sals(acpi_handle handle, unsigned long arg)330{331	return exec_simple_method(handle, "SALS", arg);332}333 334static int exec_kblc(acpi_handle handle, unsigned long arg)335{336	return exec_simple_method(handle, "KBLC", arg);337}338 339static int eval_kblc(acpi_handle handle, unsigned long cmd, unsigned long *res)340{341	return eval_int_with_arg(handle, "KBLC", cmd, res);342}343 344static int eval_dytc(acpi_handle handle, unsigned long cmd, unsigned long *res)345{346	return eval_int_with_arg(handle, "DYTC", cmd, res);347}348 349static int eval_vpcr(acpi_handle handle, unsigned long cmd, unsigned long *res)350{351	return eval_int_with_arg(handle, "VPCR", cmd, res);352}353 354static int eval_vpcw(acpi_handle handle, unsigned long cmd, unsigned long data)355{356	struct acpi_object_list params;357	union acpi_object in_obj[2];358	acpi_status status;359 360	params.count = 2;361	params.pointer = in_obj;362	in_obj[0].type = ACPI_TYPE_INTEGER;363	in_obj[0].integer.value = cmd;364	in_obj[1].type = ACPI_TYPE_INTEGER;365	in_obj[1].integer.value = data;366 367	status = acpi_evaluate_object(handle, "VPCW", &params, NULL);368	if (ACPI_FAILURE(status))369		return -EIO;370 371	return 0;372}373 374static int read_ec_data(acpi_handle handle, unsigned long cmd, unsigned long *data)375{376	unsigned long end_jiffies, val;377	int err;378 379	err = eval_vpcw(handle, 1, cmd);380	if (err)381		return err;382 383	end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1;384 385	while (time_before(jiffies, end_jiffies)) {386		schedule();387 388		err = eval_vpcr(handle, 1, &val);389		if (err)390			return err;391 392		if (val == 0)393			return eval_vpcr(handle, 0, data);394	}395 396	acpi_handle_err(handle, "timeout in %s\n", __func__);397 398	return -ETIMEDOUT;399}400 401static int write_ec_cmd(acpi_handle handle, unsigned long cmd, unsigned long data)402{403	unsigned long end_jiffies, val;404	int err;405 406	err = eval_vpcw(handle, 0, data);407	if (err)408		return err;409 410	err = eval_vpcw(handle, 1, cmd);411	if (err)412		return err;413 414	end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1;415 416	while (time_before(jiffies, end_jiffies)) {417		schedule();418 419		err = eval_vpcr(handle, 1, &val);420		if (err)421			return err;422 423		if (val == 0)424			return 0;425	}426 427	acpi_handle_err(handle, "timeout in %s\n", __func__);428 429	return -ETIMEDOUT;430}431 432/*433 * debugfs434 */435static int debugfs_status_show(struct seq_file *s, void *data)436{437	struct ideapad_private *priv = s->private;438	unsigned long value;439 440	guard(mutex)(&priv->vpc_mutex);441 442	if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &value))443		seq_printf(s, "Backlight max:  %lu\n", value);444	if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL, &value))445		seq_printf(s, "Backlight now:  %lu\n", value);446	if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &value))447		seq_printf(s, "BL power value: %s (%lu)\n", value ? "on" : "off", value);448 449	seq_puts(s, "=====================\n");450 451	if (!read_ec_data(priv->adev->handle, VPCCMD_R_RF, &value))452		seq_printf(s, "Radio status: %s (%lu)\n", value ? "on" : "off", value);453	if (!read_ec_data(priv->adev->handle, VPCCMD_R_WIFI, &value))454		seq_printf(s, "Wifi status:  %s (%lu)\n", value ? "on" : "off", value);455	if (!read_ec_data(priv->adev->handle, VPCCMD_R_BT, &value))456		seq_printf(s, "BT status:    %s (%lu)\n", value ? "on" : "off", value);457	if (!read_ec_data(priv->adev->handle, VPCCMD_R_3G, &value))458		seq_printf(s, "3G status:    %s (%lu)\n", value ? "on" : "off", value);459 460	seq_puts(s, "=====================\n");461 462	if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value))463		seq_printf(s, "Touchpad status: %s (%lu)\n", value ? "on" : "off", value);464	if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value))465		seq_printf(s, "Camera status:   %s (%lu)\n", value ? "on" : "off", value);466 467	seq_puts(s, "=====================\n");468 469	if (!eval_gbmd(priv->adev->handle, &value))470		seq_printf(s, "GBMD: %#010lx\n", value);471	if (!eval_hals(priv->adev->handle, &value))472		seq_printf(s, "HALS: %#010lx\n", value);473 474	return 0;475}476DEFINE_SHOW_ATTRIBUTE(debugfs_status);477 478static int debugfs_cfg_show(struct seq_file *s, void *data)479{480	struct ideapad_private *priv = s->private;481 482	seq_printf(s, "_CFG: %#010lx\n\n", priv->cfg);483 484	seq_puts(s, "Capabilities:");485	if (test_bit(CFG_CAP_BT_BIT, &priv->cfg))486		seq_puts(s, " bluetooth");487	if (test_bit(CFG_CAP_3G_BIT, &priv->cfg))488		seq_puts(s, " 3G");489	if (test_bit(CFG_CAP_WIFI_BIT, &priv->cfg))490		seq_puts(s, " wifi");491	if (test_bit(CFG_CAP_CAM_BIT, &priv->cfg))492		seq_puts(s, " camera");493	seq_puts(s, "\n");494 495	seq_puts(s, "OSD support:");496	if (test_bit(CFG_OSD_NUMLK_BIT, &priv->cfg))497		seq_puts(s, " num-lock");498	if (test_bit(CFG_OSD_CAPSLK_BIT, &priv->cfg))499		seq_puts(s, " caps-lock");500	if (test_bit(CFG_OSD_MICMUTE_BIT, &priv->cfg))501		seq_puts(s, " mic-mute");502	if (test_bit(CFG_OSD_TOUCHPAD_BIT, &priv->cfg))503		seq_puts(s, " touchpad");504	if (test_bit(CFG_OSD_CAM_BIT, &priv->cfg))505		seq_puts(s, " camera");506	seq_puts(s, "\n");507 508	seq_puts(s, "Graphics: ");509	switch (priv->cfg & 0x700) {510	case 0x100:511		seq_puts(s, "Intel");512		break;513	case 0x200:514		seq_puts(s, "ATI");515		break;516	case 0x300:517		seq_puts(s, "Nvidia");518		break;519	case 0x400:520		seq_puts(s, "Intel and ATI");521		break;522	case 0x500:523		seq_puts(s, "Intel and Nvidia");524		break;525	}526	seq_puts(s, "\n");527 528	return 0;529}530DEFINE_SHOW_ATTRIBUTE(debugfs_cfg);531 532static void ideapad_debugfs_init(struct ideapad_private *priv)533{534	struct dentry *dir;535 536	dir = debugfs_create_dir("ideapad", NULL);537	priv->debug = dir;538 539	debugfs_create_file("cfg", 0444, dir, priv, &debugfs_cfg_fops);540	debugfs_create_file("status", 0444, dir, priv, &debugfs_status_fops);541}542 543static void ideapad_debugfs_exit(struct ideapad_private *priv)544{545	debugfs_remove_recursive(priv->debug);546	priv->debug = NULL;547}548 549/*550 * sysfs551 */552static ssize_t camera_power_show(struct device *dev,553				 struct device_attribute *attr,554				 char *buf)555{556	struct ideapad_private *priv = dev_get_drvdata(dev);557	unsigned long result = 0;558	int err;559 560	scoped_guard(mutex, &priv->vpc_mutex) {561		err = read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &result);562		if (err)563			return err;564	}565 566	return sysfs_emit(buf, "%d\n", !!result);567}568 569static ssize_t camera_power_store(struct device *dev,570				  struct device_attribute *attr,571				  const char *buf, size_t count)572{573	struct ideapad_private *priv = dev_get_drvdata(dev);574	bool state;575	int err;576 577	err = kstrtobool(buf, &state);578	if (err)579		return err;580 581	scoped_guard(mutex, &priv->vpc_mutex) {582		err = write_ec_cmd(priv->adev->handle, VPCCMD_W_CAMERA, state);583		if (err)584			return err;585	}586 587	return count;588}589 590static DEVICE_ATTR_RW(camera_power);591 592static ssize_t conservation_mode_show(struct device *dev,593				      struct device_attribute *attr,594				      char *buf)595{596	struct ideapad_private *priv = dev_get_drvdata(dev);597	unsigned long result;598	int err;599 600	err = eval_gbmd(priv->adev->handle, &result);601	if (err)602		return err;603 604	return sysfs_emit(buf, "%d\n", !!test_bit(GBMD_CONSERVATION_STATE_BIT, &result));605}606 607static ssize_t conservation_mode_store(struct device *dev,608				       struct device_attribute *attr,609				       const char *buf, size_t count)610{611	struct ideapad_private *priv = dev_get_drvdata(dev);612	bool state;613	int err;614 615	err = kstrtobool(buf, &state);616	if (err)617		return err;618 619	err = exec_sbmc(priv->adev->handle, state ? SBMC_CONSERVATION_ON : SBMC_CONSERVATION_OFF);620	if (err)621		return err;622 623	return count;624}625 626static DEVICE_ATTR_RW(conservation_mode);627 628static ssize_t fan_mode_show(struct device *dev,629			     struct device_attribute *attr,630			     char *buf)631{632	struct ideapad_private *priv = dev_get_drvdata(dev);633	unsigned long result = 0;634	int err;635 636	scoped_guard(mutex, &priv->vpc_mutex) {637		err = read_ec_data(priv->adev->handle, VPCCMD_R_FAN, &result);638		if (err)639			return err;640	}641 642	return sysfs_emit(buf, "%lu\n", result);643}644 645static ssize_t fan_mode_store(struct device *dev,646			      struct device_attribute *attr,647			      const char *buf, size_t count)648{649	struct ideapad_private *priv = dev_get_drvdata(dev);650	unsigned int state;651	int err;652 653	err = kstrtouint(buf, 0, &state);654	if (err)655		return err;656 657	if (state > 4 || state == 3)658		return -EINVAL;659 660	scoped_guard(mutex, &priv->vpc_mutex) {661		err = write_ec_cmd(priv->adev->handle, VPCCMD_W_FAN, state);662		if (err)663			return err;664	}665 666	return count;667}668 669static DEVICE_ATTR_RW(fan_mode);670 671static int ideapad_fn_lock_get(struct ideapad_private *priv)672{673	unsigned long hals;674	int err;675 676	err = eval_hals(priv->adev->handle, &hals);677	if (err)678		return err;679 680	return !!test_bit(HALS_FNLOCK_STATE_BIT, &hals);681}682 683static int ideapad_fn_lock_set(struct ideapad_private *priv, bool state)684{685	return exec_sals(priv->adev->handle,686		state ? SALS_FNLOCK_ON : SALS_FNLOCK_OFF);687}688 689static void ideapad_fn_lock_led_notify(struct ideapad_private *priv, int brightness)690{691	if (!priv->fn_lock.initialized)692		return;693 694	if (brightness == priv->fn_lock.last_brightness)695		return;696 697	priv->fn_lock.last_brightness = brightness;698 699	led_classdev_notify_brightness_hw_changed(&priv->fn_lock.led, brightness);700}701 702static ssize_t fn_lock_show(struct device *dev,703			    struct device_attribute *attr,704			    char *buf)705{706	struct ideapad_private *priv = dev_get_drvdata(dev);707	int brightness;708 709	brightness = ideapad_fn_lock_get(priv);710	if (brightness < 0)711		return brightness;712 713	return sysfs_emit(buf, "%d\n", brightness);714}715 716static ssize_t fn_lock_store(struct device *dev,717			     struct device_attribute *attr,718			     const char *buf, size_t count)719{720	struct ideapad_private *priv = dev_get_drvdata(dev);721	bool state;722	int err;723 724	err = kstrtobool(buf, &state);725	if (err)726		return err;727 728	err = ideapad_fn_lock_set(priv, state);729	if (err)730		return err;731 732	ideapad_fn_lock_led_notify(priv, state);733 734	return count;735}736 737static DEVICE_ATTR_RW(fn_lock);738 739static ssize_t touchpad_show(struct device *dev,740			     struct device_attribute *attr,741			     char *buf)742{743	struct ideapad_private *priv = dev_get_drvdata(dev);744	unsigned long result = 0;745	int err;746 747	scoped_guard(mutex, &priv->vpc_mutex) {748		err = read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &result);749		if (err)750			return err;751	}752 753	priv->r_touchpad_val = result;754 755	return sysfs_emit(buf, "%d\n", !!result);756}757 758static ssize_t touchpad_store(struct device *dev,759			      struct device_attribute *attr,760			      const char *buf, size_t count)761{762	struct ideapad_private *priv = dev_get_drvdata(dev);763	bool state;764	int err;765 766	err = kstrtobool(buf, &state);767	if (err)768		return err;769 770	scoped_guard(mutex, &priv->vpc_mutex) {771		err = write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, state);772		if (err)773			return err;774	}775 776	priv->r_touchpad_val = state;777 778	return count;779}780 781static DEVICE_ATTR_RW(touchpad);782 783static ssize_t usb_charging_show(struct device *dev,784				 struct device_attribute *attr,785				 char *buf)786{787	struct ideapad_private *priv = dev_get_drvdata(dev);788	unsigned long hals;789	int err;790 791	err = eval_hals(priv->adev->handle, &hals);792	if (err)793		return err;794 795	return sysfs_emit(buf, "%d\n", !!test_bit(HALS_USB_CHARGING_STATE_BIT, &hals));796}797 798static ssize_t usb_charging_store(struct device *dev,799				  struct device_attribute *attr,800				  const char *buf, size_t count)801{802	struct ideapad_private *priv = dev_get_drvdata(dev);803	bool state;804	int err;805 806	err = kstrtobool(buf, &state);807	if (err)808		return err;809 810	err = exec_sals(priv->adev->handle, state ? SALS_USB_CHARGING_ON : SALS_USB_CHARGING_OFF);811	if (err)812		return err;813 814	return count;815}816 817static DEVICE_ATTR_RW(usb_charging);818 819static struct attribute *ideapad_attributes[] = {820	&dev_attr_camera_power.attr,821	&dev_attr_conservation_mode.attr,822	&dev_attr_fan_mode.attr,823	&dev_attr_fn_lock.attr,824	&dev_attr_touchpad.attr,825	&dev_attr_usb_charging.attr,826	NULL827};828 829static umode_t ideapad_is_visible(struct kobject *kobj,830				  struct attribute *attr,831				  int idx)832{833	struct device *dev = kobj_to_dev(kobj);834	struct ideapad_private *priv = dev_get_drvdata(dev);835	bool supported = true;836 837	if (attr == &dev_attr_camera_power.attr)838		supported = test_bit(CFG_CAP_CAM_BIT, &priv->cfg);839	else if (attr == &dev_attr_conservation_mode.attr)840		supported = priv->features.conservation_mode;841	else if (attr == &dev_attr_fan_mode.attr)842		supported = priv->features.fan_mode;843	else if (attr == &dev_attr_fn_lock.attr)844		supported = priv->features.fn_lock;845	else if (attr == &dev_attr_touchpad.attr)846		supported = priv->features.touchpad_ctrl_via_ec;847	else if (attr == &dev_attr_usb_charging.attr)848		supported = priv->features.usb_charging;849 850	return supported ? attr->mode : 0;851}852 853static const struct attribute_group ideapad_attribute_group = {854	.is_visible = ideapad_is_visible,855	.attrs = ideapad_attributes856};857 858/*859 * DYTC Platform profile860 */861#define DYTC_CMD_QUERY        0 /* To get DYTC status - enable/revision */862#define DYTC_CMD_SET          1 /* To enable/disable IC function mode */863#define DYTC_CMD_GET          2 /* To get current IC function and mode */864#define DYTC_CMD_RESET    0x1ff /* To reset back to default */865 866#define DYTC_QUERY_ENABLE_BIT 8  /* Bit        8 - 0 = disabled, 1 = enabled */867#define DYTC_QUERY_SUBREV_BIT 16 /* Bits 16 - 27 - sub revision */868#define DYTC_QUERY_REV_BIT    28 /* Bits 28 - 31 - revision */869 870#define DYTC_GET_FUNCTION_BIT 8  /* Bits  8-11 - function setting */871#define DYTC_GET_MODE_BIT     12 /* Bits 12-15 - mode setting */872 873#define DYTC_SET_FUNCTION_BIT 12 /* Bits 12-15 - function setting */874#define DYTC_SET_MODE_BIT     16 /* Bits 16-19 - mode setting */875#define DYTC_SET_VALID_BIT    20 /* Bit     20 - 1 = on, 0 = off */876 877#define DYTC_FUNCTION_STD     0  /* Function = 0, standard mode */878#define DYTC_FUNCTION_CQL     1  /* Function = 1, lap mode */879#define DYTC_FUNCTION_MMC     11 /* Function = 11, desk mode */880 881#define DYTC_MODE_PERFORM     2  /* High power mode aka performance */882#define DYTC_MODE_LOW_POWER       3  /* Low power mode aka quiet */883#define DYTC_MODE_BALANCE   0xF  /* Default mode aka balanced */884 885#define DYTC_SET_COMMAND(function, mode, on) \886	(DYTC_CMD_SET | (function) << DYTC_SET_FUNCTION_BIT | \887	 (mode) << DYTC_SET_MODE_BIT | \888	 (on) << DYTC_SET_VALID_BIT)889 890#define DYTC_DISABLE_CQL DYTC_SET_COMMAND(DYTC_FUNCTION_CQL, DYTC_MODE_BALANCE, 0)891 892#define DYTC_ENABLE_CQL DYTC_SET_COMMAND(DYTC_FUNCTION_CQL, DYTC_MODE_BALANCE, 1)893 894static int convert_dytc_to_profile(int dytcmode, enum platform_profile_option *profile)895{896	switch (dytcmode) {897	case DYTC_MODE_LOW_POWER:898		*profile = PLATFORM_PROFILE_LOW_POWER;899		break;900	case DYTC_MODE_BALANCE:901		*profile =  PLATFORM_PROFILE_BALANCED;902		break;903	case DYTC_MODE_PERFORM:904		*profile =  PLATFORM_PROFILE_PERFORMANCE;905		break;906	default: /* Unknown mode */907		return -EINVAL;908	}909 910	return 0;911}912 913static int convert_profile_to_dytc(enum platform_profile_option profile, int *perfmode)914{915	switch (profile) {916	case PLATFORM_PROFILE_LOW_POWER:917		*perfmode = DYTC_MODE_LOW_POWER;918		break;919	case PLATFORM_PROFILE_BALANCED:920		*perfmode = DYTC_MODE_BALANCE;921		break;922	case PLATFORM_PROFILE_PERFORMANCE:923		*perfmode = DYTC_MODE_PERFORM;924		break;925	default: /* Unknown profile */926		return -EOPNOTSUPP;927	}928 929	return 0;930}931 932/*933 * dytc_profile_get: Function to register with platform_profile934 * handler. Returns current platform profile.935 */936static int dytc_profile_get(struct platform_profile_handler *pprof,937			    enum platform_profile_option *profile)938{939	struct ideapad_dytc_priv *dytc = container_of(pprof, struct ideapad_dytc_priv, pprof);940 941	*profile = dytc->current_profile;942	return 0;943}944 945/*946 * Helper function - check if we are in CQL mode and if we are947 *  - disable CQL,948 *  - run the command949 *  - enable CQL950 *  If not in CQL mode, just run the command951 */952static int dytc_cql_command(struct ideapad_private *priv, unsigned long cmd,953			    unsigned long *output)954{955	int err, cmd_err, cur_funcmode;956 957	/* Determine if we are in CQL mode. This alters the commands we do */958	err = eval_dytc(priv->adev->handle, DYTC_CMD_GET, output);959	if (err)960		return err;961 962	cur_funcmode = (*output >> DYTC_GET_FUNCTION_BIT) & 0xF;963	/* Check if we're OK to return immediately */964	if (cmd == DYTC_CMD_GET && cur_funcmode != DYTC_FUNCTION_CQL)965		return 0;966 967	if (cur_funcmode == DYTC_FUNCTION_CQL) {968		err = eval_dytc(priv->adev->handle, DYTC_DISABLE_CQL, NULL);969		if (err)970			return err;971	}972 973	cmd_err = eval_dytc(priv->adev->handle, cmd, output);974	/* Check return condition after we've restored CQL state */975 976	if (cur_funcmode == DYTC_FUNCTION_CQL) {977		err = eval_dytc(priv->adev->handle, DYTC_ENABLE_CQL, NULL);978		if (err)979			return err;980	}981 982	return cmd_err;983}984 985/*986 * dytc_profile_set: Function to register with platform_profile987 * handler. Sets current platform profile.988 */989static int dytc_profile_set(struct platform_profile_handler *pprof,990			    enum platform_profile_option profile)991{992	struct ideapad_dytc_priv *dytc = container_of(pprof, struct ideapad_dytc_priv, pprof);993	struct ideapad_private *priv = dytc->priv;994	unsigned long output;995	int err;996 997	scoped_guard(mutex_intr, &dytc->mutex) {998		if (profile == PLATFORM_PROFILE_BALANCED) {999			/* To get back to balanced mode we just issue a reset command */1000			err = eval_dytc(priv->adev->handle, DYTC_CMD_RESET, NULL);1001			if (err)1002				return err;1003		} else {1004			int perfmode;1005 1006			err = convert_profile_to_dytc(profile, &perfmode);1007			if (err)1008				return err;1009 1010			/* Determine if we are in CQL mode. This alters the commands we do */1011			err = dytc_cql_command(priv,1012					       DYTC_SET_COMMAND(DYTC_FUNCTION_MMC, perfmode, 1),1013					       &output);1014			if (err)1015				return err;1016		}1017 1018		/* Success - update current profile */1019		dytc->current_profile = profile;1020		return 0;1021	}1022 1023	return -EINTR;1024}1025 1026static void dytc_profile_refresh(struct ideapad_private *priv)1027{1028	enum platform_profile_option profile;1029	unsigned long output;1030	int err, perfmode;1031 1032	scoped_guard(mutex, &priv->dytc->mutex)1033		err = dytc_cql_command(priv, DYTC_CMD_GET, &output);1034	if (err)1035		return;1036 1037	perfmode = (output >> DYTC_GET_MODE_BIT) & 0xF;1038 1039	if (convert_dytc_to_profile(perfmode, &profile))1040		return;1041 1042	if (profile != priv->dytc->current_profile) {1043		priv->dytc->current_profile = profile;1044		platform_profile_notify();1045	}1046}1047 1048static const struct dmi_system_id ideapad_dytc_v4_allow_table[] = {1049	{1050		/* Ideapad 5 Pro 16ACH6 */1051		.matches = {1052			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),1053			DMI_MATCH(DMI_PRODUCT_NAME, "82L5")1054		}1055	},1056	{1057		/* Ideapad 5 15ITL05 */1058		.matches = {1059			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),1060			DMI_MATCH(DMI_PRODUCT_VERSION, "IdeaPad 5 15ITL05")1061		}1062	},1063	{}1064};1065 1066static int ideapad_dytc_profile_init(struct ideapad_private *priv)1067{1068	int err, dytc_version;1069	unsigned long output;1070 1071	if (!priv->features.dytc)1072		return -ENODEV;1073 1074	err = eval_dytc(priv->adev->handle, DYTC_CMD_QUERY, &output);1075	/* For all other errors we can flag the failure */1076	if (err)1077		return err;1078 1079	/* Check DYTC is enabled and supports mode setting */1080	if (!test_bit(DYTC_QUERY_ENABLE_BIT, &output)) {1081		dev_info(&priv->platform_device->dev, "DYTC_QUERY_ENABLE_BIT returned false\n");1082		return -ENODEV;1083	}1084 1085	dytc_version = (output >> DYTC_QUERY_REV_BIT) & 0xF;1086 1087	if (dytc_version < 4) {1088		dev_info(&priv->platform_device->dev, "DYTC_VERSION < 4 is not supported\n");1089		return -ENODEV;1090	}1091 1092	if (dytc_version < 5 &&1093	    !(allow_v4_dytc || dmi_check_system(ideapad_dytc_v4_allow_table))) {1094		dev_info(&priv->platform_device->dev,1095			 "DYTC_VERSION 4 support may not work. Pass ideapad_laptop.allow_v4_dytc=Y on the kernel commandline to enable\n");1096		return -ENODEV;1097	}1098 1099	priv->dytc = kzalloc(sizeof(*priv->dytc), GFP_KERNEL);1100	if (!priv->dytc)1101		return -ENOMEM;1102 1103	mutex_init(&priv->dytc->mutex);1104 1105	priv->dytc->priv = priv;1106	priv->dytc->pprof.profile_get = dytc_profile_get;1107	priv->dytc->pprof.profile_set = dytc_profile_set;1108 1109	/* Setup supported modes */1110	set_bit(PLATFORM_PROFILE_LOW_POWER, priv->dytc->pprof.choices);1111	set_bit(PLATFORM_PROFILE_BALANCED, priv->dytc->pprof.choices);1112	set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->dytc->pprof.choices);1113 1114	/* Create platform_profile structure and register */1115	err = platform_profile_register(&priv->dytc->pprof);1116	if (err)1117		goto pp_reg_failed;1118 1119	/* Ensure initial values are correct */1120	dytc_profile_refresh(priv);1121 1122	return 0;1123 1124pp_reg_failed:1125	mutex_destroy(&priv->dytc->mutex);1126	kfree(priv->dytc);1127	priv->dytc = NULL;1128 1129	return err;1130}1131 1132static void ideapad_dytc_profile_exit(struct ideapad_private *priv)1133{1134	if (!priv->dytc)1135		return;1136 1137	platform_profile_remove();1138	mutex_destroy(&priv->dytc->mutex);1139	kfree(priv->dytc);1140 1141	priv->dytc = NULL;1142}1143 1144/*1145 * Rfkill1146 */1147struct ideapad_rfk_data {1148	char *name;1149	int cfgbit;1150	int opcode;1151	int type;1152};1153 1154static const struct ideapad_rfk_data ideapad_rfk_data[] = {1155	{ "ideapad_wlan",      CFG_CAP_WIFI_BIT, VPCCMD_W_WIFI, RFKILL_TYPE_WLAN },1156	{ "ideapad_bluetooth", CFG_CAP_BT_BIT,   VPCCMD_W_BT,   RFKILL_TYPE_BLUETOOTH },1157	{ "ideapad_3g",        CFG_CAP_3G_BIT,   VPCCMD_W_3G,   RFKILL_TYPE_WWAN },1158};1159 1160static int ideapad_rfk_set(void *data, bool blocked)1161{1162	struct ideapad_rfk_priv *priv = data;1163	int opcode = ideapad_rfk_data[priv->dev].opcode;1164 1165	guard(mutex)(&priv->priv->vpc_mutex);1166 1167	return write_ec_cmd(priv->priv->adev->handle, opcode, !blocked);1168}1169 1170static const struct rfkill_ops ideapad_rfk_ops = {1171	.set_block = ideapad_rfk_set,1172};1173 1174static void ideapad_sync_rfk_state(struct ideapad_private *priv)1175{1176	unsigned long hw_blocked = 0;1177	int i;1178 1179	if (priv->features.hw_rfkill_switch) {1180		guard(mutex)(&priv->vpc_mutex);1181 1182		if (read_ec_data(priv->adev->handle, VPCCMD_R_RF, &hw_blocked))1183			return;1184		hw_blocked = !hw_blocked;1185	}1186 1187	for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)1188		if (priv->rfk[i])1189			rfkill_set_hw_state(priv->rfk[i], hw_blocked);1190}1191 1192static int ideapad_register_rfkill(struct ideapad_private *priv, int dev)1193{1194	unsigned long rf_enabled;1195	int err;1196 1197	if (no_bt_rfkill && ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH) {1198		/* Force to enable bluetooth when no_bt_rfkill=1 */1199		write_ec_cmd(priv->adev->handle, ideapad_rfk_data[dev].opcode, 1);1200		return 0;1201	}1202 1203	priv->rfk_priv[dev].dev = dev;1204	priv->rfk_priv[dev].priv = priv;1205 1206	priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name,1207				      &priv->platform_device->dev,1208				      ideapad_rfk_data[dev].type,1209				      &ideapad_rfk_ops,1210				      &priv->rfk_priv[dev]);1211	if (!priv->rfk[dev])1212		return -ENOMEM;1213 1214	err = read_ec_data(priv->adev->handle, ideapad_rfk_data[dev].opcode - 1, &rf_enabled);1215	if (err)1216		rf_enabled = 1;1217 1218	rfkill_init_sw_state(priv->rfk[dev], !rf_enabled);1219 1220	err = rfkill_register(priv->rfk[dev]);1221	if (err)1222		rfkill_destroy(priv->rfk[dev]);1223 1224	return err;1225}1226 1227static void ideapad_unregister_rfkill(struct ideapad_private *priv, int dev)1228{1229	if (!priv->rfk[dev])1230		return;1231 1232	rfkill_unregister(priv->rfk[dev]);1233	rfkill_destroy(priv->rfk[dev]);1234}1235 1236/*1237 * Platform device1238 */1239static int ideapad_sysfs_init(struct ideapad_private *priv)1240{1241	return device_add_group(&priv->platform_device->dev,1242				&ideapad_attribute_group);1243}1244 1245static void ideapad_sysfs_exit(struct ideapad_private *priv)1246{1247	device_remove_group(&priv->platform_device->dev,1248			    &ideapad_attribute_group);1249}1250 1251/*1252 * input device1253 */1254#define IDEAPAD_WMI_KEY 0x1001255 1256static const struct key_entry ideapad_keymap[] = {1257	{ KE_KEY,   6, { KEY_SWITCHVIDEOMODE } },1258	{ KE_KEY,   7, { KEY_CAMERA } },1259	{ KE_KEY,   8, { KEY_MICMUTE } },1260	{ KE_KEY,  11, { KEY_F16 } },1261	{ KE_KEY,  13, { KEY_WLAN } },1262	{ KE_KEY,  16, { KEY_PROG1 } },1263	{ KE_KEY,  17, { KEY_PROG2 } },1264	{ KE_KEY,  64, { KEY_PROG3 } },1265	{ KE_KEY,  65, { KEY_PROG4 } },1266	{ KE_KEY,  66, { KEY_TOUCHPAD_OFF } },1267	{ KE_KEY,  67, { KEY_TOUCHPAD_ON } },1268	{ KE_KEY, 128, { KEY_ESC } },1269 1270	/*1271	 * WMI keys1272	 */1273 1274	/* FnLock (handled by the firmware) */1275	{ KE_IGNORE,	0x02 | IDEAPAD_WMI_KEY },1276	/* Esc (handled by the firmware) */1277	{ KE_IGNORE,	0x03 | IDEAPAD_WMI_KEY },1278	/* Customizable Lenovo Hotkey ("star" with 'S' inside) */1279	{ KE_KEY,	0x01 | IDEAPAD_WMI_KEY, { KEY_FAVORITES } },1280	{ KE_KEY,	0x04 | IDEAPAD_WMI_KEY, { KEY_SELECTIVE_SCREENSHOT } },1281	/* Lenovo Support */1282	{ KE_KEY,	0x07 | IDEAPAD_WMI_KEY, { KEY_HELP } },1283	{ KE_KEY,	0x0e | IDEAPAD_WMI_KEY, { KEY_PICKUP_PHONE } },1284	{ KE_KEY,	0x0f | IDEAPAD_WMI_KEY, { KEY_HANGUP_PHONE } },1285	/* Refresh Rate Toggle (Fn+R) */1286	{ KE_KEY,	0x10 | IDEAPAD_WMI_KEY, { KEY_REFRESH_RATE_TOGGLE } },1287	/* Dark mode toggle */1288	{ KE_KEY,	0x13 | IDEAPAD_WMI_KEY, { KEY_PROG1 } },1289	/* Sound profile switch */1290	{ KE_KEY,	0x12 | IDEAPAD_WMI_KEY, { KEY_PROG2 } },1291	/* Lenovo Virtual Background application */1292	{ KE_KEY,	0x28 | IDEAPAD_WMI_KEY, { KEY_PROG3 } },1293	/* Lenovo Support */1294	{ KE_KEY,	0x27 | IDEAPAD_WMI_KEY, { KEY_HELP } },1295	/* Refresh Rate Toggle */1296	{ KE_KEY,	0x0a | IDEAPAD_WMI_KEY, { KEY_REFRESH_RATE_TOGGLE } },1297	/* Specific to some newer models */1298	{ KE_KEY,	0x3e | IDEAPAD_WMI_KEY, { KEY_MICMUTE } },1299	{ KE_KEY,	0x3f | IDEAPAD_WMI_KEY, { KEY_RFKILL } },1300 1301	{ KE_END },1302};1303 1304static int ideapad_input_init(struct ideapad_private *priv)1305{1306	struct input_dev *inputdev;1307	int err;1308 1309	inputdev = input_allocate_device();1310	if (!inputdev)1311		return -ENOMEM;1312 1313	inputdev->name = "Ideapad extra buttons";1314	inputdev->phys = "ideapad/input0";1315	inputdev->id.bustype = BUS_HOST;1316	inputdev->dev.parent = &priv->platform_device->dev;1317 1318	err = sparse_keymap_setup(inputdev, ideapad_keymap, NULL);1319	if (err) {1320		dev_err(&priv->platform_device->dev,1321			"Could not set up input device keymap: %d\n", err);1322		goto err_free_dev;1323	}1324 1325	err = input_register_device(inputdev);1326	if (err) {1327		dev_err(&priv->platform_device->dev,1328			"Could not register input device: %d\n", err);1329		goto err_free_dev;1330	}1331 1332	priv->inputdev = inputdev;1333 1334	return 0;1335 1336err_free_dev:1337	input_free_device(inputdev);1338 1339	return err;1340}1341 1342static void ideapad_input_exit(struct ideapad_private *priv)1343{1344	input_unregister_device(priv->inputdev);1345	priv->inputdev = NULL;1346}1347 1348static void ideapad_input_report(struct ideapad_private *priv,1349				 unsigned long scancode)1350{1351	sparse_keymap_report_event(priv->inputdev, scancode, 1, true);1352}1353 1354static void ideapad_input_novokey(struct ideapad_private *priv)1355{1356	unsigned long long_pressed;1357 1358	scoped_guard(mutex, &priv->vpc_mutex)1359		if (read_ec_data(priv->adev->handle, VPCCMD_R_NOVO, &long_pressed))1360			return;1361 1362	if (long_pressed)1363		ideapad_input_report(priv, 17);1364	else1365		ideapad_input_report(priv, 16);1366}1367 1368static void ideapad_check_special_buttons(struct ideapad_private *priv)1369{1370	unsigned long bit, value;1371 1372	scoped_guard(mutex, &priv->vpc_mutex)1373		if (read_ec_data(priv->adev->handle, VPCCMD_R_SPECIAL_BUTTONS, &value))1374			return;1375 1376	for_each_set_bit (bit, &value, 16) {1377		switch (bit) {1378		case 6:	/* Z570 */1379		case 0:	/* Z580 */1380			/* Thermal Management / Performance Mode button */1381			if (priv->dytc)1382				platform_profile_cycle();1383			else1384				ideapad_input_report(priv, 65);1385			break;1386		case 1:1387			/* OneKey Theater button */1388			ideapad_input_report(priv, 64);1389			break;1390		default:1391			dev_info(&priv->platform_device->dev,1392				 "Unknown special button: %lu\n", bit);1393			break;1394		}1395	}1396}1397 1398/*1399 * backlight1400 */1401static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)1402{1403	struct ideapad_private *priv = bl_get_data(blightdev);1404	unsigned long now;1405	int err;1406 1407	guard(mutex)(&priv->vpc_mutex);1408 1409	err = read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now);1410	if (err)1411		return err;1412 1413	return now;1414}1415 1416static int ideapad_backlight_update_status(struct backlight_device *blightdev)1417{1418	struct ideapad_private *priv = bl_get_data(blightdev);1419	int err;1420 1421	guard(mutex)(&priv->vpc_mutex);1422 1423	err = write_ec_cmd(priv->adev->handle, VPCCMD_W_BL,1424			   blightdev->props.brightness);1425	if (err)1426		return err;1427 1428	err = write_ec_cmd(priv->adev->handle, VPCCMD_W_BL_POWER,1429			   blightdev->props.power != BACKLIGHT_POWER_OFF);1430	if (err)1431		return err;1432 1433	return 0;1434}1435 1436static const struct backlight_ops ideapad_backlight_ops = {1437	.get_brightness = ideapad_backlight_get_brightness,1438	.update_status = ideapad_backlight_update_status,1439};1440 1441static int ideapad_backlight_init(struct ideapad_private *priv)1442{1443	struct backlight_device *blightdev;1444	struct backlight_properties props;1445	unsigned long max, now, power;1446	int err;1447 1448	err = read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &max);1449	if (err)1450		return err;1451 1452	err = read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now);1453	if (err)1454		return err;1455 1456	err = read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power);1457	if (err)1458		return err;1459 1460	memset(&props, 0, sizeof(props));1461 1462	props.max_brightness = max;1463	props.type = BACKLIGHT_PLATFORM;1464 1465	blightdev = backlight_device_register("ideapad",1466					      &priv->platform_device->dev,1467					      priv,1468					      &ideapad_backlight_ops,1469					      &props);1470	if (IS_ERR(blightdev)) {1471		err = PTR_ERR(blightdev);1472		dev_err(&priv->platform_device->dev,1473			"Could not register backlight device: %d\n", err);1474		return err;1475	}1476 1477	priv->blightdev = blightdev;1478	blightdev->props.brightness = now;1479	blightdev->props.power = power ? BACKLIGHT_POWER_ON : BACKLIGHT_POWER_OFF;1480 1481	backlight_update_status(blightdev);1482 1483	return 0;1484}1485 1486static void ideapad_backlight_exit(struct ideapad_private *priv)1487{1488	backlight_device_unregister(priv->blightdev);1489	priv->blightdev = NULL;1490}1491 1492static void ideapad_backlight_notify_power(struct ideapad_private *priv)1493{1494	struct backlight_device *blightdev = priv->blightdev;1495	unsigned long power;1496 1497	if (!blightdev)1498		return;1499 1500	guard(mutex)(&priv->vpc_mutex);1501 1502	if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))1503		return;1504 1505	blightdev->props.power = power ? BACKLIGHT_POWER_ON : BACKLIGHT_POWER_OFF;1506}1507 1508static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)1509{1510	unsigned long now;1511 1512	/* if we control brightness via acpi video driver */1513	if (!priv->blightdev)1514		scoped_guard(mutex, &priv->vpc_mutex)1515			read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now);1516	else1517		backlight_force_update(priv->blightdev, BACKLIGHT_UPDATE_HOTKEY);1518}1519 1520/*1521 * keyboard backlight1522 */1523static int ideapad_kbd_bl_check_tristate(int type)1524{1525	return (type == KBD_BL_TRISTATE) || (type == KBD_BL_TRISTATE_AUTO);1526}1527 1528static int ideapad_kbd_bl_brightness_get(struct ideapad_private *priv)1529{1530	unsigned long value;1531	int err;1532 1533	if (ideapad_kbd_bl_check_tristate(priv->kbd_bl.type)) {1534		err = eval_kblc(priv->adev->handle,1535				FIELD_PREP(KBD_BL_COMMAND_TYPE, priv->kbd_bl.type) |1536				KBD_BL_COMMAND_GET,1537				&value);1538 1539		if (err)1540			return err;1541 1542		/* Convert returned value to brightness level */1543		value = FIELD_GET(KBD_BL_GET_BRIGHTNESS, value);1544 1545		/* Off, low or high */1546		if (value <= priv->kbd_bl.led.max_brightness)1547			return value;1548 1549		/* Auto, report as off */1550		if (value == priv->kbd_bl.led.max_brightness + 1)1551			return 0;1552 1553		/* Unknown value */1554		dev_warn(&priv->platform_device->dev,1555			 "Unknown keyboard backlight value: %lu", value);1556		return -EINVAL;1557	}1558 1559	err = eval_hals(priv->adev->handle, &value);1560	if (err)1561		return err;1562 1563	return !!test_bit(HALS_KBD_BL_STATE_BIT, &value);1564}1565 1566static enum led_brightness ideapad_kbd_bl_led_cdev_brightness_get(struct led_classdev *led_cdev)1567{1568	struct ideapad_private *priv = container_of(led_cdev, struct ideapad_private, kbd_bl.led);1569 1570	return ideapad_kbd_bl_brightness_get(priv);1571}1572 1573static int ideapad_kbd_bl_brightness_set(struct ideapad_private *priv, unsigned int brightness)1574{1575	int err;1576	unsigned long value;1577	int type = priv->kbd_bl.type;1578 1579	if (ideapad_kbd_bl_check_tristate(type)) {1580		if (brightness > priv->kbd_bl.led.max_brightness)1581			return -EINVAL;1582 1583		value = FIELD_PREP(KBD_BL_SET_BRIGHTNESS, brightness) |1584			FIELD_PREP(KBD_BL_COMMAND_TYPE, type) |1585			KBD_BL_COMMAND_SET;1586		err = exec_kblc(priv->adev->handle, value);1587	} else {1588		err = exec_sals(priv->adev->handle, brightness ? SALS_KBD_BL_ON : SALS_KBD_BL_OFF);1589	}1590 1591	if (err)1592		return err;1593 1594	priv->kbd_bl.last_brightness = brightness;1595 1596	return 0;1597}1598 1599static int ideapad_kbd_bl_led_cdev_brightness_set(struct led_classdev *led_cdev,1600						  enum led_brightness brightness)1601{1602	struct ideapad_private *priv = container_of(led_cdev, struct ideapad_private, kbd_bl.led);1603 1604	return ideapad_kbd_bl_brightness_set(priv, brightness);1605}1606 1607static void ideapad_kbd_bl_notify(struct ideapad_private *priv)1608{1609	int brightness;1610 1611	if (!priv->kbd_bl.initialized)1612		return;1613 1614	brightness = ideapad_kbd_bl_brightness_get(priv);1615	if (brightness < 0)1616		return;1617 1618	if (brightness == priv->kbd_bl.last_brightness)1619		return;1620 1621	priv->kbd_bl.last_brightness = brightness;1622 1623	led_classdev_notify_brightness_hw_changed(&priv->kbd_bl.led, brightness);1624}1625 1626static int ideapad_kbd_bl_init(struct ideapad_private *priv)1627{1628	int brightness, err;1629 1630	if (!priv->features.kbd_bl)1631		return -ENODEV;1632 1633	if (WARN_ON(priv->kbd_bl.initialized))1634		return -EEXIST;1635 1636	if (ideapad_kbd_bl_check_tristate(priv->kbd_bl.type)) {1637		priv->kbd_bl.led.max_brightness = 2;1638	} else {1639		priv->kbd_bl.led.max_brightness = 1;1640	}1641 1642	brightness = ideapad_kbd_bl_brightness_get(priv);1643	if (brightness < 0)1644		return brightness;1645 1646	priv->kbd_bl.last_brightness = brightness;1647	priv->kbd_bl.led.name                    = "platform::" LED_FUNCTION_KBD_BACKLIGHT;1648	priv->kbd_bl.led.brightness_get          = ideapad_kbd_bl_led_cdev_brightness_get;1649	priv->kbd_bl.led.brightness_set_blocking = ideapad_kbd_bl_led_cdev_brightness_set;1650	priv->kbd_bl.led.flags                   = LED_BRIGHT_HW_CHANGED;1651 1652	err = led_classdev_register(&priv->platform_device->dev, &priv->kbd_bl.led);1653	if (err)1654		return err;1655 1656	priv->kbd_bl.initialized = true;1657 1658	return 0;1659}1660 1661static void ideapad_kbd_bl_exit(struct ideapad_private *priv)1662{1663	if (!priv->kbd_bl.initialized)1664		return;1665 1666	priv->kbd_bl.initialized = false;1667 1668	led_classdev_unregister(&priv->kbd_bl.led);1669}1670 1671/*1672 * FnLock LED1673 */1674static enum led_brightness ideapad_fn_lock_led_cdev_get(struct led_classdev *led_cdev)1675{1676	struct ideapad_private *priv = container_of(led_cdev, struct ideapad_private, fn_lock.led);1677 1678	return ideapad_fn_lock_get(priv);1679}1680 1681static int ideapad_fn_lock_led_cdev_set(struct led_classdev *led_cdev,1682	enum led_brightness brightness)1683{1684	struct ideapad_private *priv = container_of(led_cdev, struct ideapad_private, fn_lock.led);1685 1686	return ideapad_fn_lock_set(priv, brightness);1687}1688 1689static int ideapad_fn_lock_led_init(struct ideapad_private *priv)1690{1691	int brightness, err;1692 1693	if (!priv->features.fn_lock)1694		return -ENODEV;1695 1696	if (WARN_ON(priv->fn_lock.initialized))1697		return -EEXIST;1698 1699	priv->fn_lock.led.max_brightness = 1;1700 1701	brightness = ideapad_fn_lock_get(priv);1702	if (brightness < 0)1703		return brightness;1704 1705	priv->fn_lock.last_brightness = brightness;1706	priv->fn_lock.led.name                    = "platform::" LED_FUNCTION_FNLOCK;1707	priv->fn_lock.led.brightness_get          = ideapad_fn_lock_led_cdev_get;1708	priv->fn_lock.led.brightness_set_blocking = ideapad_fn_lock_led_cdev_set;1709	priv->fn_lock.led.flags                   = LED_BRIGHT_HW_CHANGED;1710 1711	err = led_classdev_register(&priv->platform_device->dev, &priv->fn_lock.led);1712	if (err)1713		return err;1714 1715	priv->fn_lock.initialized = true;1716 1717	return 0;1718}1719 1720static void ideapad_fn_lock_led_exit(struct ideapad_private *priv)1721{1722	if (!priv->fn_lock.initialized)1723		return;1724 1725	priv->fn_lock.initialized = false;1726 1727	led_classdev_unregister(&priv->fn_lock.led);1728}1729 1730/*1731 * module init/exit1732 */1733static void ideapad_sync_touchpad_state(struct ideapad_private *priv, bool send_events)1734{1735	unsigned long value;1736	unsigned char param;1737	int ret;1738 1739	/* Without reading from EC touchpad LED doesn't switch state */1740	scoped_guard(mutex, &priv->vpc_mutex)1741		ret = read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value);1742	if (ret)1743		return;1744 1745	/*1746	 * Some IdeaPads don't really turn off touchpad - they only1747	 * switch the LED state. We (de)activate KBC AUX port to turn1748	 * touchpad off and on. We send KEY_TOUCHPAD_OFF and1749	 * KEY_TOUCHPAD_ON to not to get out of sync with LED1750	 */1751	if (priv->features.ctrl_ps2_aux_port)1752		i8042_command(&param, value ? I8042_CMD_AUX_ENABLE : I8042_CMD_AUX_DISABLE);1753 1754	/*1755	 * On older models the EC controls the touchpad and toggles it on/off1756	 * itself, in this case we report KEY_TOUCHPAD_ON/_OFF. Some models do1757	 * an acpi-notify with VPC bit 5 set on resume, so this function get1758	 * called with send_events=true on every resume. Therefor if the EC did1759	 * not toggle, do nothing to avoid sending spurious KEY_TOUCHPAD_TOGGLE.1760	 */1761	if (send_events && value != priv->r_touchpad_val) {1762		ideapad_input_report(priv, value ? 67 : 66);1763		sysfs_notify(&priv->platform_device->dev.kobj, NULL, "touchpad");1764	}1765 1766	priv->r_touchpad_val = value;1767}1768 1769static const struct dmi_system_id ymc_ec_trigger_quirk_dmi_table[] = {1770	{1771		/* Lenovo Yoga 7 14ARB7 */1772		.matches = {1773			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),1774			DMI_MATCH(DMI_PRODUCT_NAME, "82QF"),1775		},1776	},1777	{1778		/* Lenovo Yoga 7 14ACN6 */1779		.matches = {1780			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),1781			DMI_MATCH(DMI_PRODUCT_NAME, "82N7"),1782		},1783	},1784	{ }1785};1786 1787static void ideapad_laptop_trigger_ec(void)1788{1789	struct ideapad_private *priv;1790	int ret;1791 1792	guard(mutex)(&ideapad_shared_mutex);1793 1794	priv = ideapad_shared;1795	if (!priv)1796		return;1797 1798	if (!priv->features.ymc_ec_trigger)1799		return;1800 1801	scoped_guard(mutex, &priv->vpc_mutex)1802		ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_YMC, 1);1803	if (ret)1804		dev_warn(&priv->platform_device->dev, "Could not write YMC: %d\n", ret);1805}1806 1807static int ideapad_laptop_nb_notify(struct notifier_block *nb,1808				    unsigned long action, void *data)1809{1810	switch (action) {1811	case IDEAPAD_LAPTOP_YMC_EVENT:1812		ideapad_laptop_trigger_ec();1813		break;1814	}1815 1816	return 0;1817}1818 1819static struct notifier_block ideapad_laptop_notifier = {1820	.notifier_call = ideapad_laptop_nb_notify,1821};1822 1823static BLOCKING_NOTIFIER_HEAD(ideapad_laptop_chain_head);1824 1825int ideapad_laptop_register_notifier(struct notifier_block *nb)1826{1827	return blocking_notifier_chain_register(&ideapad_laptop_chain_head, nb);1828}1829EXPORT_SYMBOL_NS_GPL(ideapad_laptop_register_notifier, IDEAPAD_LAPTOP);1830 1831int ideapad_laptop_unregister_notifier(struct notifier_block *nb)1832{1833	return blocking_notifier_chain_unregister(&ideapad_laptop_chain_head, nb);1834}1835EXPORT_SYMBOL_NS_GPL(ideapad_laptop_unregister_notifier, IDEAPAD_LAPTOP);1836 1837void ideapad_laptop_call_notifier(unsigned long action, void *data)1838{1839	blocking_notifier_call_chain(&ideapad_laptop_chain_head, action, data);1840}1841EXPORT_SYMBOL_NS_GPL(ideapad_laptop_call_notifier, IDEAPAD_LAPTOP);1842 1843static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)1844{1845	struct ideapad_private *priv = data;1846	unsigned long vpc1, vpc2, bit;1847 1848	scoped_guard(mutex, &priv->vpc_mutex) {1849		if (read_ec_data(handle, VPCCMD_R_VPC1, &vpc1))1850			return;1851 1852		if (read_ec_data(handle, VPCCMD_R_VPC2, &vpc2))1853			return;1854	}1855 1856	vpc1 = (vpc2 << 8) | vpc1;1857 1858	for_each_set_bit (bit, &vpc1, 16) {1859		switch (bit) {1860		case 13:1861		case 11:1862		case 8:1863		case 7:1864		case 6:1865			ideapad_input_report(priv, bit);1866			break;1867		case 10:1868			/*1869			 * This event gets send on a Yoga 300-11IBR when the EC1870			 * believes that the device has changed between laptop/1871			 * tent/stand/tablet mode. The EC relies on getting1872			 * angle info from 2 accelerometers through a special1873			 * windows service calling a DSM on the DUAL250E ACPI-1874			 * device. Linux does not do this, making the laptop/1875			 * tent/stand/tablet mode info unreliable, so we simply1876			 * ignore these events.1877			 */1878			break;1879		case 9:1880			ideapad_sync_rfk_state(priv);1881			break;1882		case 5:1883			ideapad_sync_touchpad_state(priv, true);1884			break;1885		case 4:1886			ideapad_backlight_notify_brightness(priv);1887			break;1888		case 3:1889			ideapad_input_novokey(priv);1890			break;1891		case 2:1892			ideapad_backlight_notify_power(priv);1893			break;1894		case KBD_BL_KBLC_CHANGED_EVENT:1895		case 1:1896			/*1897			 * Some IdeaPads report event 1 every ~201898			 * seconds while on battery power; some1899			 * report this when changing to/from tablet1900			 * mode; some report this when the keyboard1901			 * backlight has changed.1902			 */1903			ideapad_kbd_bl_notify(priv);1904			break;1905		case 0:1906			ideapad_check_special_buttons(priv);1907			break;1908		default:1909			dev_info(&priv->platform_device->dev,1910				 "Unknown event: %lu\n", bit);1911		}1912	}1913}1914 1915/* On some models we need to call exec_sals(SALS_FNLOCK_ON/OFF) to set the LED */1916static const struct dmi_system_id set_fn_lock_led_list[] = {1917	{1918		/* https://bugzilla.kernel.org/show_bug.cgi?id=212671 */1919		.matches = {1920			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),1921			DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Legion R7000P2020H"),1922		}1923	},1924	{1925		.matches = {1926			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),1927			DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Legion 5 15ARH05"),1928		}1929	},1930	{}1931};1932 1933/*1934 * Some ideapads have a hardware rfkill switch, but most do not have one.1935 * Reading VPCCMD_R_RF always results in 0 on models without a hardware rfkill,1936 * switch causing ideapad_laptop to wrongly report all radios as hw-blocked.1937 * There used to be a long list of DMI ids for models without a hw rfkill1938 * switch here, but that resulted in playing whack a mole.1939 * More importantly wrongly reporting the wifi radio as hw-blocked, results in1940 * non working wifi. Whereas not reporting it hw-blocked, when it actually is1941 * hw-blocked results in an empty SSID list, which is a much more benign1942 * failure mode.1943 * So the default now is the much safer option of assuming there is no1944 * hardware rfkill switch. This default also actually matches most hardware,1945 * since having a hw rfkill switch is quite rare on modern hardware, so this1946 * also leads to a much shorter list.1947 */1948static const struct dmi_system_id hw_rfkill_list[] = {1949	{}1950};1951 1952/*1953 * On some models the EC toggles the touchpad muted LED on touchpad toggle1954 * hotkey presses, but the EC does not actually disable the touchpad itself.1955 * On these models the driver needs to explicitly enable/disable the i80421956 * (PS/2) aux port.1957 */1958static const struct dmi_system_id ctrl_ps2_aux_port_list[] = {1959	{1960	/* Lenovo Ideapad Z570 */1961	.matches = {1962		DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),1963		DMI_MATCH(DMI_PRODUCT_VERSION, "Ideapad Z570"),1964		},1965	},1966	{}1967};1968 1969static void ideapad_check_features(struct ideapad_private *priv)1970{1971	acpi_handle handle = priv->adev->handle;1972	unsigned long val;1973 1974	priv->features.set_fn_lock_led =1975		set_fn_lock_led || dmi_check_system(set_fn_lock_led_list);1976	priv->features.hw_rfkill_switch =1977		hw_rfkill_switch || dmi_check_system(hw_rfkill_list);1978	priv->features.ctrl_ps2_aux_port =1979		ctrl_ps2_aux_port || dmi_check_system(ctrl_ps2_aux_port_list);1980	priv->features.touchpad_ctrl_via_ec = touchpad_ctrl_via_ec;1981	priv->features.ymc_ec_trigger =1982		ymc_ec_trigger || dmi_check_system(ymc_ec_trigger_quirk_dmi_table);1983 1984	if (!read_ec_data(handle, VPCCMD_R_FAN, &val))1985		priv->features.fan_mode = true;1986 1987	if (acpi_has_method(handle, "GBMD") && acpi_has_method(handle, "SBMC"))1988		priv->features.conservation_mode = true;1989 1990	if (acpi_has_method(handle, "DYTC"))1991		priv->features.dytc = true;1992 1993	if (acpi_has_method(handle, "HALS") && acpi_has_method(handle, "SALS")) {1994		if (!eval_hals(handle, &val)) {1995			if (test_bit(HALS_FNLOCK_SUPPORT_BIT, &val))1996				priv->features.fn_lock = true;1997 1998			if (test_bit(HALS_KBD_BL_SUPPORT_BIT, &val)) {1999				priv->features.kbd_bl = true;2000				priv->kbd_bl.type = KBD_BL_STANDARD;2001			}2002 2003			if (test_bit(HALS_USB_CHARGING_SUPPORT_BIT, &val))2004				priv->features.usb_charging = true;2005		}2006	}2007 2008	if (acpi_has_method(handle, "KBLC")) {2009		if (!eval_kblc(priv->adev->handle, KBD_BL_QUERY_TYPE, &val)) {2010			if (val == KBD_BL_TRISTATE_TYPE) {2011				priv->features.kbd_bl = true;2012				priv->kbd_bl.type = KBD_BL_TRISTATE;2013			} else if (val == KBD_BL_TRISTATE_AUTO_TYPE) {2014				priv->features.kbd_bl = true;2015				priv->kbd_bl.type = KBD_BL_TRISTATE_AUTO;2016			} else {2017				dev_warn(&priv->platform_device->dev,2018					 "Unknown keyboard type: %lu",2019					 val);2020			}2021		}2022	}2023}2024 2025#if IS_ENABLED(CONFIG_ACPI_WMI)2026/*2027 * WMI driver2028 */2029enum ideapad_wmi_event_type {2030	IDEAPAD_WMI_EVENT_ESC,2031	IDEAPAD_WMI_EVENT_FN_KEYS,2032};2033 2034struct ideapad_wmi_private {2035	enum ideapad_wmi_event_type event;2036};2037 2038static int ideapad_wmi_probe(struct wmi_device *wdev, const void *context)2039{2040	struct ideapad_wmi_private *wpriv;2041 2042	wpriv = devm_kzalloc(&wdev->dev, sizeof(*wpriv), GFP_KERNEL);2043	if (!wpriv)2044		return -ENOMEM;2045 2046	*wpriv = *(const struct ideapad_wmi_private *)context;2047 2048	dev_set_drvdata(&wdev->dev, wpriv);2049	return 0;2050}2051 2052static void ideapad_wmi_notify(struct wmi_device *wdev, union acpi_object *data)2053{2054	struct ideapad_wmi_private *wpriv = dev_get_drvdata(&wdev->dev);2055	struct ideapad_private *priv;2056 2057	guard(mutex)(&ideapad_shared_mutex);2058 2059	priv = ideapad_shared;2060	if (!priv)2061		return;2062 2063	switch (wpriv->event) {2064	case IDEAPAD_WMI_EVENT_ESC:2065		ideapad_input_report(priv, 128);2066		break;2067	case IDEAPAD_WMI_EVENT_FN_KEYS:2068		if (priv->features.set_fn_lock_led) {2069			int brightness = ideapad_fn_lock_get(priv);2070 2071			if (brightness >= 0) {2072				ideapad_fn_lock_set(priv, brightness);2073				ideapad_fn_lock_led_notify(priv, brightness);2074			}2075		}2076 2077		if (data->type != ACPI_TYPE_INTEGER) {2078			dev_warn(&wdev->dev,2079				 "WMI event data is not an integer\n");2080			break;2081		}2082 2083		dev_dbg(&wdev->dev, "WMI fn-key event: 0x%llx\n",2084			data->integer.value);2085 2086		/* 0x02 FnLock, 0x03 Esc */2087		if (data->integer.value == 0x02 || data->integer.value == 0x03)2088			ideapad_fn_lock_led_notify(priv, data->integer.value == 0x02);2089 2090		ideapad_input_report(priv,2091				     data->integer.value | IDEAPAD_WMI_KEY);2092 2093		break;2094	}2095}2096 2097static const struct ideapad_wmi_private ideapad_wmi_context_esc = {2098	.event = IDEAPAD_WMI_EVENT_ESC2099};2100 2101static const struct ideapad_wmi_private ideapad_wmi_context_fn_keys = {2102	.event = IDEAPAD_WMI_EVENT_FN_KEYS2103};2104 2105static const struct wmi_device_id ideapad_wmi_ids[] = {2106	{ "26CAB2E5-5CF1-46AE-AAC3-4A12B6BA50E6", &ideapad_wmi_context_esc }, /* Yoga 3 */2107	{ "56322276-8493-4CE8-A783-98C991274F5E", &ideapad_wmi_context_esc }, /* Yoga 700 */2108	{ "8FC0DE0C-B4E4-43FD-B0F3-8871711C1294", &ideapad_wmi_context_fn_keys }, /* Legion 5 */2109	{},2110};2111MODULE_DEVICE_TABLE(wmi, ideapad_wmi_ids);2112 2113static struct wmi_driver ideapad_wmi_driver = {2114	.driver = {2115		.name = "ideapad_wmi",2116	},2117	.id_table = ideapad_wmi_ids,2118	.probe = ideapad_wmi_probe,2119	.notify = ideapad_wmi_notify,2120};2121 2122static int ideapad_wmi_driver_register(void)2123{2124	return wmi_driver_register(&ideapad_wmi_driver);2125}2126 2127static void ideapad_wmi_driver_unregister(void)2128{2129	return wmi_driver_unregister(&ideapad_wmi_driver);2130}2131 2132#else2133static inline int ideapad_wmi_driver_register(void) { return 0; }2134static inline void ideapad_wmi_driver_unregister(void) { }2135#endif2136 2137/*2138 * ACPI driver2139 */2140static int ideapad_acpi_add(struct platform_device *pdev)2141{2142	struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);2143	struct ideapad_private *priv;2144	acpi_status status;2145	unsigned long cfg;2146	int err, i;2147 2148	if (!adev || eval_int(adev->handle, "_CFG", &cfg))2149		return -ENODEV;2150 2151	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);2152	if (!priv)2153		return -ENOMEM;2154 2155	dev_set_drvdata(&pdev->dev, priv);2156 2157	priv->cfg = cfg;2158	priv->adev = adev;2159	priv->platform_device = pdev;2160 2161	err = devm_mutex_init(&pdev->dev, &priv->vpc_mutex);2162	if (err)2163		return err;2164 2165	ideapad_check_features(priv);2166 2167	err = ideapad_sysfs_init(priv);2168	if (err)2169		return err;2170 2171	ideapad_debugfs_init(priv);2172 2173	err = ideapad_input_init(priv);2174	if (err)2175		goto input_failed;2176 2177	err = ideapad_kbd_bl_init(priv);2178	if (err) {2179		if (err != -ENODEV)2180			dev_warn(&pdev->dev, "Could not set up keyboard backlight LED: %d\n", err);2181		else2182			dev_info(&pdev->dev, "Keyboard backlight control not available\n");2183	}2184 2185	err = ideapad_fn_lock_led_init(priv);2186	if (err) {2187		if (err != -ENODEV)2188			dev_warn(&pdev->dev, "Could not set up FnLock LED: %d\n", err);2189		else2190			dev_info(&pdev->dev, "FnLock control not available\n");2191	}2192 2193	/*2194	 * On some models without a hw-switch (the yoga 2 13 at least)2195	 * VPCCMD_W_RF must be explicitly set to 1 for the wifi to work.2196	 */2197	if (!priv->features.hw_rfkill_switch)2198		write_ec_cmd(priv->adev->handle, VPCCMD_W_RF, 1);2199 2200	for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)2201		if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg))2202			ideapad_register_rfkill(priv, i);2203 2204	ideapad_sync_rfk_state(priv);2205	ideapad_sync_touchpad_state(priv, false);2206 2207	err = ideapad_dytc_profile_init(priv);2208	if (err) {2209		if (err != -ENODEV)2210			dev_warn(&pdev->dev, "Could not set up DYTC interface: %d\n", err);2211		else2212			dev_info(&pdev->dev, "DYTC interface is not available\n");2213	}2214 2215	if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {2216		err = ideapad_backlight_init(priv);2217		if (err && err != -ENODEV)2218			goto backlight_failed;2219	}2220 2221	status = acpi_install_notify_handler(adev->handle,2222					     ACPI_DEVICE_NOTIFY,2223					     ideapad_acpi_notify, priv);2224	if (ACPI_FAILURE(status)) {2225		err = -EIO;2226		goto notification_failed;2227	}2228 2229	err = ideapad_shared_init(priv);2230	if (err)2231		goto shared_init_failed;2232 2233	ideapad_laptop_register_notifier(&ideapad_laptop_notifier);2234 2235	return 0;2236 2237shared_init_failed:2238	acpi_remove_notify_handler(priv->adev->handle,2239				   ACPI_DEVICE_NOTIFY,2240				   ideapad_acpi_notify);2241 2242notification_failed:2243	ideapad_backlight_exit(priv);2244 2245backlight_failed:2246	ideapad_dytc_profile_exit(priv);2247 2248	for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)2249		ideapad_unregister_rfkill(priv, i);2250 2251	ideapad_fn_lock_led_exit(priv);2252	ideapad_kbd_bl_exit(priv);2253	ideapad_input_exit(priv);2254 2255input_failed:2256	ideapad_debugfs_exit(priv);2257	ideapad_sysfs_exit(priv);2258 2259	return err;2260}2261 2262static void ideapad_acpi_remove(struct platform_device *pdev)2263{2264	struct ideapad_private *priv = dev_get_drvdata(&pdev->dev);2265	int i;2266 2267	ideapad_laptop_unregister_notifier(&ideapad_laptop_notifier);2268 2269	ideapad_shared_exit(priv);2270 2271	acpi_remove_notify_handler(priv->adev->handle,2272				   ACPI_DEVICE_NOTIFY,2273				   ideapad_acpi_notify);2274 2275	ideapad_backlight_exit(priv);2276	ideapad_dytc_profile_exit(priv);2277 2278	for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)2279		ideapad_unregister_rfkill(priv, i);2280 2281	ideapad_fn_lock_led_exit(priv);2282	ideapad_kbd_bl_exit(priv);2283	ideapad_input_exit(priv);2284	ideapad_debugfs_exit(priv);2285	ideapad_sysfs_exit(priv);2286}2287 2288#ifdef CONFIG_PM_SLEEP2289static int ideapad_acpi_resume(struct device *dev)2290{2291	struct ideapad_private *priv = dev_get_drvdata(dev);2292 2293	ideapad_sync_rfk_state(priv);2294	ideapad_sync_touchpad_state(priv, false);2295 2296	if (priv->dytc)2297		dytc_profile_refresh(priv);2298 2299	return 0;2300}2301#endif2302static SIMPLE_DEV_PM_OPS(ideapad_pm, NULL, ideapad_acpi_resume);2303 2304static const struct acpi_device_id ideapad_device_ids[] = {2305	{"VPC2004", 0},2306	{"", 0},2307};2308MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);2309 2310static struct platform_driver ideapad_acpi_driver = {2311	.probe = ideapad_acpi_add,2312	.remove_new = ideapad_acpi_remove,2313	.driver = {2314		.name   = "ideapad_acpi",2315		.pm     = &ideapad_pm,2316		.acpi_match_table = ACPI_PTR(ideapad_device_ids),2317	},2318};2319 2320static int __init ideapad_laptop_init(void)2321{2322	int err;2323 2324	err = ideapad_wmi_driver_register();2325	if (err)2326		return err;2327 2328	err = platform_driver_register(&ideapad_acpi_driver);2329	if (err) {2330		ideapad_wmi_driver_unregister();2331		return err;2332	}2333 2334	return 0;2335}2336module_init(ideapad_laptop_init)2337 2338static void __exit ideapad_laptop_exit(void)2339{2340	ideapad_wmi_driver_unregister();2341	platform_driver_unregister(&ideapad_acpi_driver);2342}2343module_exit(ideapad_laptop_exit)2344 2345MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");2346MODULE_DESCRIPTION("IdeaPad ACPI Extras");2347MODULE_LICENSE("GPL");2348