File Index
Files and directories
Sign in
FILE

ideapad-laptop.c

File preview with syntax highlighting.

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