Merge tag 'platform-drivers-x86-v6.4-1' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-block.git] / drivers / platform / x86 / ideapad-laptop.c
CommitLineData
16216333 1// SPDX-License-Identifier: GPL-2.0-or-later
58ac7aa0 2/*
a4b5a279 3 * ideapad-laptop.c - Lenovo IdeaPad ACPI Extras
58ac7aa0
DW
4 *
5 * Copyright © 2010 Intel Corporation
6 * Copyright © 2010 David Woodhouse <dwmw2@infradead.org>
58ac7aa0
DW
7 */
8
9ab23989
JP
9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10
7d38f034
BP
11#include <linux/acpi.h>
12#include <linux/backlight.h>
0c4915b6 13#include <linux/bitops.h>
503325f8 14#include <linux/bug.h>
7d38f034
BP
15#include <linux/debugfs.h>
16#include <linux/device.h>
17#include <linux/dmi.h>
18#include <linux/fb.h>
19#include <linux/i8042.h>
20#include <linux/init.h>
21#include <linux/input.h>
22#include <linux/input/sparse-keymap.h>
58ac7aa0 23#include <linux/kernel.h>
503325f8 24#include <linux/leds.h>
58ac7aa0 25#include <linux/module.h>
98ee6919 26#include <linux/platform_device.h>
eabe5339 27#include <linux/platform_profile.h>
7d38f034 28#include <linux/rfkill.h>
773e3206 29#include <linux/seq_file.h>
d6b50889 30#include <linux/sysfs.h>
7d38f034 31#include <linux/types.h>
f32e0241 32#include <linux/wmi.h>
0de0ab9a 33#include "ideapad-laptop.h"
7d38f034 34
26bff5f0 35#include <acpi/video.h>
58ac7aa0 36
503325f8
BP
37#include <dt-bindings/leds/common.h>
38
65c7713a 39#define IDEAPAD_RFKILL_DEV_NUM 3
58ac7aa0 40
ade50296 41enum {
be5dd7d8
EO
42 CFG_CAP_BT_BIT = 16,
43 CFG_CAP_3G_BIT = 17,
44 CFG_CAP_WIFI_BIT = 18,
45 CFG_CAP_CAM_BIT = 19,
46
47 /*
48 * These are OnScreenDisplay support bits that can be useful to determine
49 * whether a hotkey exists/should show OSD. But they aren't particularly
50 * meaningful since they were introduced later, i.e. 2010 IdeaPads
51 * don't have these, but they still have had OSD for hotkeys.
52 */
53 CFG_OSD_NUMLK_BIT = 27,
54 CFG_OSD_CAPSLK_BIT = 28,
55 CFG_OSD_MICMUTE_BIT = 29,
56 CFG_OSD_TOUCHPAD_BIT = 30,
57 CFG_OSD_CAM_BIT = 31,
0b765671
BP
58};
59
60enum {
61 GBMD_CONSERVATION_STATE_BIT = 5,
62};
63
64enum {
b09aaa3f
BP
65 SBMC_CONSERVATION_ON = 3,
66 SBMC_CONSERVATION_OFF = 5,
0b765671
BP
67};
68
69enum {
6b49dea4
BP
70 HALS_KBD_BL_SUPPORT_BIT = 4,
71 HALS_KBD_BL_STATE_BIT = 5,
72 HALS_USB_CHARGING_SUPPORT_BIT = 6,
73 HALS_USB_CHARGING_STATE_BIT = 7,
74 HALS_FNLOCK_SUPPORT_BIT = 9,
75 HALS_FNLOCK_STATE_BIT = 10,
76 HALS_HOTKEYS_PRIMARY_BIT = 11,
0b765671
BP
77};
78
79enum {
6b49dea4
BP
80 SALS_KBD_BL_ON = 0x8,
81 SALS_KBD_BL_OFF = 0x9,
82 SALS_USB_CHARGING_ON = 0xa,
83 SALS_USB_CHARGING_OFF = 0xb,
84 SALS_FNLOCK_ON = 0xe,
85 SALS_FNLOCK_OFF = 0xf,
ade50296
HWT
86};
87
eabe5339
JY
88struct ideapad_dytc_priv {
89 enum platform_profile_option current_profile;
90 struct platform_profile_handler pprof;
65c7713a 91 struct mutex mutex; /* protects the DYTC interface */
eabe5339
JY
92 struct ideapad_private *priv;
93};
94
331e0ea2
ZR
95struct ideapad_rfk_priv {
96 int dev;
97 struct ideapad_private *priv;
98};
99
ce326329 100struct ideapad_private {
469f6434 101 struct acpi_device *adev;
c1f73658 102 struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
331e0ea2 103 struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM];
98ee6919 104 struct platform_device *platform_device;
f63409ae 105 struct input_dev *inputdev;
a4ecbb8a 106 struct backlight_device *blightdev;
eabe5339 107 struct ideapad_dytc_priv *dytc;
773e3206 108 struct dentry *debug;
3371f481 109 unsigned long cfg;
5829f8a8 110 unsigned long r_touchpad_val;
1c59de4a
BP
111 struct {
112 bool conservation_mode : 1;
113 bool dytc : 1;
114 bool fan_mode : 1;
115 bool fn_lock : 1;
81a5603a 116 bool set_fn_lock_led : 1;
1c59de4a 117 bool hw_rfkill_switch : 1;
503325f8 118 bool kbd_bl : 1;
1c59de4a 119 bool touchpad_ctrl_via_ec : 1;
c69e7d84 120 bool ctrl_ps2_aux_port : 1;
6b49dea4 121 bool usb_charging : 1;
1c59de4a 122 } features;
503325f8
BP
123 struct {
124 bool initialized;
125 struct led_classdev led;
126 unsigned int last_brightness;
127 } kbd_bl;
58ac7aa0
DW
128};
129
bfa97b7d
IP
130static bool no_bt_rfkill;
131module_param(no_bt_rfkill, bool, 0444);
132MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
133
a27a1e35
HG
134static bool allow_v4_dytc;
135module_param(allow_v4_dytc, bool, 0444);
b44fd994
HG
136MODULE_PARM_DESC(allow_v4_dytc,
137 "Enable DYTC version 4 platform-profile support. "
138 "If you need this please report this to: platform-driver-x86@vger.kernel.org");
139
140static bool hw_rfkill_switch;
141module_param(hw_rfkill_switch, bool, 0444);
142MODULE_PARM_DESC(hw_rfkill_switch,
143 "Enable rfkill support for laptops with a hw on/off wifi switch/slider. "
144 "If you need this please report this to: platform-driver-x86@vger.kernel.org");
145
146static bool set_fn_lock_led;
147module_param(set_fn_lock_led, bool, 0444);
148MODULE_PARM_DESC(set_fn_lock_led,
149 "Enable driver based updates of the fn-lock LED on fn-lock changes. "
150 "If you need this please report this to: platform-driver-x86@vger.kernel.org");
a27a1e35 151
c69e7d84
HG
152static bool ctrl_ps2_aux_port;
153module_param(ctrl_ps2_aux_port, bool, 0444);
154MODULE_PARM_DESC(ctrl_ps2_aux_port,
155 "Enable driver based PS/2 aux port en-/dis-abling on touchpad on/off toggle. "
156 "If you need this please report this to: platform-driver-x86@vger.kernel.org");
157
301e0d76
HG
158static bool touchpad_ctrl_via_ec;
159module_param(touchpad_ctrl_via_ec, bool, 0444);
160MODULE_PARM_DESC(touchpad_ctrl_via_ec,
161 "Enable registering a 'touchpad' sysfs-attribute which can be used to manually "
162 "tell the EC to enable/disable the touchpad. This may not work on all models.");
163
f32e0241
PJ
164/*
165 * shared data
166 */
167
168static struct ideapad_private *ideapad_shared;
169static DEFINE_MUTEX(ideapad_shared_mutex);
170
171static int ideapad_shared_init(struct ideapad_private *priv)
172{
173 int ret;
174
175 mutex_lock(&ideapad_shared_mutex);
176
177 if (!ideapad_shared) {
178 ideapad_shared = priv;
179 ret = 0;
180 } else {
181 dev_warn(&priv->adev->dev, "found multiple platform devices\n");
182 ret = -EINVAL;
183 }
184
185 mutex_unlock(&ideapad_shared_mutex);
186
187 return ret;
188}
189
190static void ideapad_shared_exit(struct ideapad_private *priv)
191{
192 mutex_lock(&ideapad_shared_mutex);
193
194 if (ideapad_shared == priv)
195 ideapad_shared = NULL;
196
197 mutex_unlock(&ideapad_shared_mutex);
198}
199
6a09f21d
IP
200/*
201 * ACPI Helpers
202 */
6a09f21d 203
ff36b0d9 204static int eval_int(acpi_handle handle, const char *name, unsigned long *res)
6a09f21d 205{
6a09f21d 206 unsigned long long result;
ff36b0d9 207 acpi_status status;
6a09f21d 208
ff36b0d9
BP
209 status = acpi_evaluate_integer(handle, (char *)name, NULL, &result);
210 if (ACPI_FAILURE(status))
7be193e3 211 return -EIO;
65c7713a 212
ff36b0d9 213 *res = result;
65c7713a 214
ba3a3387 215 return 0;
6a09f21d
IP
216}
217
ff36b0d9 218static int exec_simple_method(acpi_handle handle, const char *name, unsigned long arg)
ade50296 219{
ff36b0d9 220 acpi_status status = acpi_execute_simple_method(handle, (char *)name, arg);
ade50296 221
ff36b0d9 222 return ACPI_FAILURE(status) ? -EIO : 0;
ade50296
HWT
223}
224
ff36b0d9 225static int eval_gbmd(acpi_handle handle, unsigned long *res)
ade50296 226{
ff36b0d9 227 return eval_int(handle, "GBMD", res);
ade50296
HWT
228}
229
b09aaa3f 230static int exec_sbmc(acpi_handle handle, unsigned long arg)
eabe5339 231{
b09aaa3f 232 return exec_simple_method(handle, "SBMC", arg);
ff36b0d9 233}
eabe5339 234
ff36b0d9
BP
235static int eval_hals(acpi_handle handle, unsigned long *res)
236{
237 return eval_int(handle, "HALS", res);
238}
eabe5339 239
ff36b0d9
BP
240static int exec_sals(acpi_handle handle, unsigned long arg)
241{
242 return exec_simple_method(handle, "SALS", arg);
eabe5339
JY
243}
244
ff36b0d9
BP
245static int eval_dytc(acpi_handle handle, unsigned long cmd, unsigned long *res)
246{
247 return eval_int_with_arg(handle, "DYTC", cmd, res);
6a09f21d
IP
248}
249
773e3206
IP
250/*
251 * debugfs
252 */
773e3206
IP
253static int debugfs_status_show(struct seq_file *s, void *data)
254{
331e0ea2 255 struct ideapad_private *priv = s->private;
773e3206
IP
256 unsigned long value;
257
331e0ea2 258 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &value))
7553390d 259 seq_printf(s, "Backlight max: %lu\n", value);
331e0ea2 260 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL, &value))
7553390d 261 seq_printf(s, "Backlight now: %lu\n", value);
331e0ea2 262 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &value))
7553390d 263 seq_printf(s, "BL power value: %s (%lu)\n", value ? "on" : "off", value);
65c7713a 264
7553390d 265 seq_puts(s, "=====================\n");
773e3206 266
331e0ea2 267 if (!read_ec_data(priv->adev->handle, VPCCMD_R_RF, &value))
7553390d 268 seq_printf(s, "Radio status: %s (%lu)\n", value ? "on" : "off", value);
331e0ea2 269 if (!read_ec_data(priv->adev->handle, VPCCMD_R_WIFI, &value))
7553390d 270 seq_printf(s, "Wifi status: %s (%lu)\n", value ? "on" : "off", value);
331e0ea2 271 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BT, &value))
7553390d 272 seq_printf(s, "BT status: %s (%lu)\n", value ? "on" : "off", value);
331e0ea2 273 if (!read_ec_data(priv->adev->handle, VPCCMD_R_3G, &value))
7553390d 274 seq_printf(s, "3G status: %s (%lu)\n", value ? "on" : "off", value);
65c7713a 275
7553390d 276 seq_puts(s, "=====================\n");
773e3206 277
331e0ea2 278 if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value))
7553390d 279 seq_printf(s, "Touchpad status: %s (%lu)\n", value ? "on" : "off", value);
331e0ea2 280 if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value))
7553390d 281 seq_printf(s, "Camera status: %s (%lu)\n", value ? "on" : "off", value);
65c7713a 282
ade50296
HWT
283 seq_puts(s, "=====================\n");
284
7553390d
BP
285 if (!eval_gbmd(priv->adev->handle, &value))
286 seq_printf(s, "GBMD: %#010lx\n", value);
287 if (!eval_hals(priv->adev->handle, &value))
288 seq_printf(s, "HALS: %#010lx\n", value);
773e3206
IP
289
290 return 0;
291}
334c4efd 292DEFINE_SHOW_ATTRIBUTE(debugfs_status);
773e3206
IP
293
294static int debugfs_cfg_show(struct seq_file *s, void *data)
295{
331e0ea2
ZR
296 struct ideapad_private *priv = s->private;
297
18227424
BP
298 seq_printf(s, "_CFG: %#010lx\n\n", priv->cfg);
299
300 seq_puts(s, "Capabilities:");
0b765671 301 if (test_bit(CFG_CAP_BT_BIT, &priv->cfg))
18227424 302 seq_puts(s, " bluetooth");
0b765671 303 if (test_bit(CFG_CAP_3G_BIT, &priv->cfg))
18227424 304 seq_puts(s, " 3G");
0b765671 305 if (test_bit(CFG_CAP_WIFI_BIT, &priv->cfg))
18227424 306 seq_puts(s, " wifi");
0b765671 307 if (test_bit(CFG_CAP_CAM_BIT, &priv->cfg))
18227424 308 seq_puts(s, " camera");
18227424
BP
309 seq_puts(s, "\n");
310
be5dd7d8
EO
311 seq_puts(s, "OSD support:");
312 if (test_bit(CFG_OSD_NUMLK_BIT, &priv->cfg))
313 seq_puts(s, " num-lock");
314 if (test_bit(CFG_OSD_CAPSLK_BIT, &priv->cfg))
315 seq_puts(s, " caps-lock");
316 if (test_bit(CFG_OSD_MICMUTE_BIT, &priv->cfg))
317 seq_puts(s, " mic-mute");
318 if (test_bit(CFG_OSD_TOUCHPAD_BIT, &priv->cfg))
319 seq_puts(s, " touchpad");
320 if (test_bit(CFG_OSD_CAM_BIT, &priv->cfg))
321 seq_puts(s, " camera");
322 seq_puts(s, "\n");
323
18227424
BP
324 seq_puts(s, "Graphics: ");
325 switch (priv->cfg & 0x700) {
e1a39a44 326 case 0x100:
18227424 327 seq_puts(s, "Intel");
e1a39a44
BP
328 break;
329 case 0x200:
18227424 330 seq_puts(s, "ATI");
e1a39a44
BP
331 break;
332 case 0x300:
18227424 333 seq_puts(s, "Nvidia");
e1a39a44
BP
334 break;
335 case 0x400:
18227424 336 seq_puts(s, "Intel and ATI");
e1a39a44
BP
337 break;
338 case 0x500:
18227424 339 seq_puts(s, "Intel and Nvidia");
e1a39a44 340 break;
773e3206 341 }
18227424 342 seq_puts(s, "\n");
e1a39a44 343
773e3206
IP
344 return 0;
345}
334c4efd 346DEFINE_SHOW_ATTRIBUTE(debugfs_cfg);
773e3206 347
17f1bf38 348static void ideapad_debugfs_init(struct ideapad_private *priv)
773e3206 349{
17f1bf38 350 struct dentry *dir;
773e3206 351
17f1bf38
GKH
352 dir = debugfs_create_dir("ideapad", NULL);
353 priv->debug = dir;
773e3206 354
65c7713a
BP
355 debugfs_create_file("cfg", 0444, dir, priv, &debugfs_cfg_fops);
356 debugfs_create_file("status", 0444, dir, priv, &debugfs_status_fops);
773e3206
IP
357}
358
359static void ideapad_debugfs_exit(struct ideapad_private *priv)
360{
361 debugfs_remove_recursive(priv->debug);
362 priv->debug = NULL;
363}
364
a4b5a279 365/*
3371f481 366 * sysfs
a4b5a279 367 */
65c7713a
BP
368static ssize_t camera_power_show(struct device *dev,
369 struct device_attribute *attr,
370 char *buf)
58ac7aa0 371{
331e0ea2 372 struct ideapad_private *priv = dev_get_drvdata(dev);
65c7713a 373 unsigned long result;
c81f2410 374 int err;
58ac7aa0 375
c81f2410
BP
376 err = read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &result);
377 if (err)
378 return err;
65c7713a 379
00641c08 380 return sysfs_emit(buf, "%d\n", !!result);
58ac7aa0
DW
381}
382
65c7713a
BP
383static ssize_t camera_power_store(struct device *dev,
384 struct device_attribute *attr,
385 const char *buf, size_t count)
58ac7aa0 386{
331e0ea2 387 struct ideapad_private *priv = dev_get_drvdata(dev);
00641c08 388 bool state;
65c7713a
BP
389 int err;
390
391 err = kstrtobool(buf, &state);
392 if (err)
393 return err;
394
395 err = write_ec_cmd(priv->adev->handle, VPCCMD_W_CAMERA, state);
396 if (err)
397 return err;
398
58ac7aa0
DW
399 return count;
400}
401
65c7713a 402static DEVICE_ATTR_RW(camera_power);
58ac7aa0 403
65c7713a
BP
404static ssize_t conservation_mode_show(struct device *dev,
405 struct device_attribute *attr,
406 char *buf)
0c7bbeb9 407{
331e0ea2 408 struct ideapad_private *priv = dev_get_drvdata(dev);
65c7713a 409 unsigned long result;
c81f2410 410 int err;
0c7bbeb9 411
65c7713a 412 err = eval_gbmd(priv->adev->handle, &result);
c81f2410
BP
413 if (err)
414 return err;
65c7713a
BP
415
416 return sysfs_emit(buf, "%d\n", !!test_bit(GBMD_CONSERVATION_STATE_BIT, &result));
0c7bbeb9
MM
417}
418
65c7713a
BP
419static ssize_t conservation_mode_store(struct device *dev,
420 struct device_attribute *attr,
421 const char *buf, size_t count)
0c7bbeb9 422{
331e0ea2 423 struct ideapad_private *priv = dev_get_drvdata(dev);
65c7713a
BP
424 bool state;
425 int err;
426
427 err = kstrtobool(buf, &state);
428 if (err)
429 return err;
430
b09aaa3f 431 err = exec_sbmc(priv->adev->handle, state ? SBMC_CONSERVATION_ON : SBMC_CONSERVATION_OFF);
65c7713a
BP
432 if (err)
433 return err;
0c7bbeb9 434
0c7bbeb9
MM
435 return count;
436}
437
65c7713a 438static DEVICE_ATTR_RW(conservation_mode);
0c7bbeb9 439
65c7713a 440static ssize_t fan_mode_show(struct device *dev,
36ac0d43
RRS
441 struct device_attribute *attr,
442 char *buf)
443{
444 struct ideapad_private *priv = dev_get_drvdata(dev);
445 unsigned long result;
c81f2410 446 int err;
36ac0d43 447
65c7713a 448 err = read_ec_data(priv->adev->handle, VPCCMD_R_FAN, &result);
c81f2410
BP
449 if (err)
450 return err;
65c7713a
BP
451
452 return sysfs_emit(buf, "%lu\n", result);
36ac0d43
RRS
453}
454
65c7713a 455static ssize_t fan_mode_store(struct device *dev,
921f70ff
BP
456 struct device_attribute *attr,
457 const char *buf, size_t count)
36ac0d43
RRS
458{
459 struct ideapad_private *priv = dev_get_drvdata(dev);
65c7713a
BP
460 unsigned int state;
461 int err;
462
463 err = kstrtouint(buf, 0, &state);
464 if (err)
465 return err;
466
467 if (state > 4 || state == 3)
468 return -EINVAL;
36ac0d43 469
65c7713a
BP
470 err = write_ec_cmd(priv->adev->handle, VPCCMD_W_FAN, state);
471 if (err)
472 return err;
36ac0d43 473
36ac0d43
RRS
474 return count;
475}
476
65c7713a 477static DEVICE_ATTR_RW(fan_mode);
36ac0d43 478
65c7713a
BP
479static ssize_t fn_lock_show(struct device *dev,
480 struct device_attribute *attr,
481 char *buf)
ade50296
HWT
482{
483 struct ideapad_private *priv = dev_get_drvdata(dev);
65c7713a 484 unsigned long hals;
c81f2410 485 int err;
ade50296 486
65c7713a 487 err = eval_hals(priv->adev->handle, &hals);
c81f2410
BP
488 if (err)
489 return err;
65c7713a
BP
490
491 return sysfs_emit(buf, "%d\n", !!test_bit(HALS_FNLOCK_STATE_BIT, &hals));
ade50296
HWT
492}
493
65c7713a
BP
494static ssize_t fn_lock_store(struct device *dev,
495 struct device_attribute *attr,
496 const char *buf, size_t count)
ade50296
HWT
497{
498 struct ideapad_private *priv = dev_get_drvdata(dev);
499 bool state;
65c7713a 500 int err;
ade50296 501
65c7713a
BP
502 err = kstrtobool(buf, &state);
503 if (err)
504 return err;
505
506 err = exec_sals(priv->adev->handle, state ? SALS_FNLOCK_ON : SALS_FNLOCK_OFF);
507 if (err)
508 return err;
ade50296 509
ade50296
HWT
510 return count;
511}
512
65c7713a 513static DEVICE_ATTR_RW(fn_lock);
ade50296 514
65c7713a
BP
515static ssize_t touchpad_show(struct device *dev,
516 struct device_attribute *attr,
517 char *buf)
40760717
OK
518{
519 struct ideapad_private *priv = dev_get_drvdata(dev);
65c7713a
BP
520 unsigned long result;
521 int err;
40760717 522
65c7713a
BP
523 err = read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &result);
524 if (err)
525 return err;
40760717 526
5829f8a8
HG
527 priv->r_touchpad_val = result;
528
65c7713a 529 return sysfs_emit(buf, "%d\n", !!result);
40760717
OK
530}
531
65c7713a
BP
532static ssize_t touchpad_store(struct device *dev,
533 struct device_attribute *attr,
534 const char *buf, size_t count)
40760717
OK
535{
536 struct ideapad_private *priv = dev_get_drvdata(dev);
537 bool state;
65c7713a
BP
538 int err;
539
540 err = kstrtobool(buf, &state);
541 if (err)
542 return err;
40760717 543
65c7713a
BP
544 err = write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, state);
545 if (err)
546 return err;
40760717 547
5829f8a8
HG
548 priv->r_touchpad_val = state;
549
40760717
OK
550 return count;
551}
552
65c7713a 553static DEVICE_ATTR_RW(touchpad);
40760717 554
6b49dea4
BP
555static ssize_t usb_charging_show(struct device *dev,
556 struct device_attribute *attr,
557 char *buf)
558{
559 struct ideapad_private *priv = dev_get_drvdata(dev);
560 unsigned long hals;
561 int err;
562
563 err = eval_hals(priv->adev->handle, &hals);
564 if (err)
565 return err;
566
567 return sysfs_emit(buf, "%d\n", !!test_bit(HALS_USB_CHARGING_STATE_BIT, &hals));
568}
569
570static ssize_t usb_charging_store(struct device *dev,
571 struct device_attribute *attr,
572 const char *buf, size_t count)
573{
574 struct ideapad_private *priv = dev_get_drvdata(dev);
575 bool state;
576 int err;
577
578 err = kstrtobool(buf, &state);
579 if (err)
580 return err;
581
582 err = exec_sals(priv->adev->handle, state ? SALS_USB_CHARGING_ON : SALS_USB_CHARGING_OFF);
583 if (err)
584 return err;
585
586 return count;
587}
588
589static DEVICE_ATTR_RW(usb_charging);
590
3371f481
IP
591static struct attribute *ideapad_attributes[] = {
592 &dev_attr_camera_power.attr,
ade50296 593 &dev_attr_conservation_mode.attr,
65c7713a 594 &dev_attr_fan_mode.attr,
40760717 595 &dev_attr_fn_lock.attr,
65c7713a 596 &dev_attr_touchpad.attr,
6b49dea4 597 &dev_attr_usb_charging.attr,
3371f481
IP
598 NULL
599};
600
587a1f16 601static umode_t ideapad_is_visible(struct kobject *kobj,
65c7713a
BP
602 struct attribute *attr,
603 int idx)
a84511f7 604{
708086b2 605 struct device *dev = kobj_to_dev(kobj);
a84511f7 606 struct ideapad_private *priv = dev_get_drvdata(dev);
1c59de4a 607 bool supported = true;
a84511f7
IP
608
609 if (attr == &dev_attr_camera_power.attr)
0b765671 610 supported = test_bit(CFG_CAP_CAM_BIT, &priv->cfg);
1c59de4a
BP
611 else if (attr == &dev_attr_conservation_mode.attr)
612 supported = priv->features.conservation_mode;
613 else if (attr == &dev_attr_fan_mode.attr)
614 supported = priv->features.fan_mode;
615 else if (attr == &dev_attr_fn_lock.attr)
616 supported = priv->features.fn_lock;
617 else if (attr == &dev_attr_touchpad.attr)
58318828 618 supported = priv->features.touchpad_ctrl_via_ec;
6b49dea4
BP
619 else if (attr == &dev_attr_usb_charging.attr)
620 supported = priv->features.usb_charging;
a84511f7
IP
621
622 return supported ? attr->mode : 0;
623}
624
49458e83 625static const struct attribute_group ideapad_attribute_group = {
a84511f7 626 .is_visible = ideapad_is_visible,
3371f481
IP
627 .attrs = ideapad_attributes
628};
629
eabe5339
JY
630/*
631 * DYTC Platform profile
632 */
633#define DYTC_CMD_QUERY 0 /* To get DYTC status - enable/revision */
634#define DYTC_CMD_SET 1 /* To enable/disable IC function mode */
635#define DYTC_CMD_GET 2 /* To get current IC function and mode */
636#define DYTC_CMD_RESET 0x1ff /* To reset back to default */
637
638#define DYTC_QUERY_ENABLE_BIT 8 /* Bit 8 - 0 = disabled, 1 = enabled */
639#define DYTC_QUERY_SUBREV_BIT 16 /* Bits 16 - 27 - sub revision */
640#define DYTC_QUERY_REV_BIT 28 /* Bits 28 - 31 - revision */
641
642#define DYTC_GET_FUNCTION_BIT 8 /* Bits 8-11 - function setting */
643#define DYTC_GET_MODE_BIT 12 /* Bits 12-15 - mode setting */
644
645#define DYTC_SET_FUNCTION_BIT 12 /* Bits 12-15 - function setting */
646#define DYTC_SET_MODE_BIT 16 /* Bits 16-19 - mode setting */
647#define DYTC_SET_VALID_BIT 20 /* Bit 20 - 1 = on, 0 = off */
648
649#define DYTC_FUNCTION_STD 0 /* Function = 0, standard mode */
650#define DYTC_FUNCTION_CQL 1 /* Function = 1, lap mode */
651#define DYTC_FUNCTION_MMC 11 /* Function = 11, desk mode */
652
653#define DYTC_MODE_PERFORM 2 /* High power mode aka performance */
654#define DYTC_MODE_LOW_POWER 3 /* Low power mode aka quiet */
655#define DYTC_MODE_BALANCE 0xF /* Default mode aka balanced */
656
657#define DYTC_SET_COMMAND(function, mode, on) \
658 (DYTC_CMD_SET | (function) << DYTC_SET_FUNCTION_BIT | \
659 (mode) << DYTC_SET_MODE_BIT | \
660 (on) << DYTC_SET_VALID_BIT)
661
662#define DYTC_DISABLE_CQL DYTC_SET_COMMAND(DYTC_FUNCTION_CQL, DYTC_MODE_BALANCE, 0)
663
664#define DYTC_ENABLE_CQL DYTC_SET_COMMAND(DYTC_FUNCTION_CQL, DYTC_MODE_BALANCE, 1)
665
666static int convert_dytc_to_profile(int dytcmode, enum platform_profile_option *profile)
667{
668 switch (dytcmode) {
669 case DYTC_MODE_LOW_POWER:
670 *profile = PLATFORM_PROFILE_LOW_POWER;
671 break;
672 case DYTC_MODE_BALANCE:
673 *profile = PLATFORM_PROFILE_BALANCED;
674 break;
675 case DYTC_MODE_PERFORM:
676 *profile = PLATFORM_PROFILE_PERFORMANCE;
677 break;
678 default: /* Unknown mode */
679 return -EINVAL;
680 }
65c7713a 681
eabe5339
JY
682 return 0;
683}
684
685static int convert_profile_to_dytc(enum platform_profile_option profile, int *perfmode)
686{
687 switch (profile) {
688 case PLATFORM_PROFILE_LOW_POWER:
689 *perfmode = DYTC_MODE_LOW_POWER;
690 break;
691 case PLATFORM_PROFILE_BALANCED:
692 *perfmode = DYTC_MODE_BALANCE;
693 break;
694 case PLATFORM_PROFILE_PERFORMANCE:
695 *perfmode = DYTC_MODE_PERFORM;
696 break;
697 default: /* Unknown profile */
698 return -EOPNOTSUPP;
699 }
65c7713a 700
eabe5339
JY
701 return 0;
702}
703
704/*
705 * dytc_profile_get: Function to register with platform_profile
706 * handler. Returns current platform profile.
707 */
65c7713a
BP
708static int dytc_profile_get(struct platform_profile_handler *pprof,
709 enum platform_profile_option *profile)
eabe5339 710{
65c7713a 711 struct ideapad_dytc_priv *dytc = container_of(pprof, struct ideapad_dytc_priv, pprof);
eabe5339 712
eabe5339
JY
713 *profile = dytc->current_profile;
714 return 0;
715}
716
717/*
718 * Helper function - check if we are in CQL mode and if we are
65c7713a 719 * - disable CQL,
eabe5339
JY
720 * - run the command
721 * - enable CQL
722 * If not in CQL mode, just run the command
723 */
65c7713a
BP
724static int dytc_cql_command(struct ideapad_private *priv, unsigned long cmd,
725 unsigned long *output)
eabe5339 726{
ff36b0d9 727 int err, cmd_err, cur_funcmode;
eabe5339
JY
728
729 /* Determine if we are in CQL mode. This alters the commands we do */
ff36b0d9 730 err = eval_dytc(priv->adev->handle, DYTC_CMD_GET, output);
eabe5339
JY
731 if (err)
732 return err;
733
734 cur_funcmode = (*output >> DYTC_GET_FUNCTION_BIT) & 0xF;
735 /* Check if we're OK to return immediately */
ff36b0d9 736 if (cmd == DYTC_CMD_GET && cur_funcmode != DYTC_FUNCTION_CQL)
eabe5339
JY
737 return 0;
738
739 if (cur_funcmode == DYTC_FUNCTION_CQL) {
ff36b0d9 740 err = eval_dytc(priv->adev->handle, DYTC_DISABLE_CQL, NULL);
eabe5339
JY
741 if (err)
742 return err;
743 }
744
ff36b0d9 745 cmd_err = eval_dytc(priv->adev->handle, cmd, output);
eabe5339
JY
746 /* Check return condition after we've restored CQL state */
747
748 if (cur_funcmode == DYTC_FUNCTION_CQL) {
ff36b0d9 749 err = eval_dytc(priv->adev->handle, DYTC_ENABLE_CQL, NULL);
eabe5339
JY
750 if (err)
751 return err;
752 }
753
754 return cmd_err;
755}
756
757/*
758 * dytc_profile_set: Function to register with platform_profile
759 * handler. Sets current platform profile.
760 */
65c7713a
BP
761static int dytc_profile_set(struct platform_profile_handler *pprof,
762 enum platform_profile_option profile)
eabe5339 763{
65c7713a
BP
764 struct ideapad_dytc_priv *dytc = container_of(pprof, struct ideapad_dytc_priv, pprof);
765 struct ideapad_private *priv = dytc->priv;
ff67dbd5 766 unsigned long output;
eabe5339
JY
767 int err;
768
eabe5339
JY
769 err = mutex_lock_interruptible(&dytc->mutex);
770 if (err)
771 return err;
772
773 if (profile == PLATFORM_PROFILE_BALANCED) {
774 /* To get back to balanced mode we just issue a reset command */
ff36b0d9 775 err = eval_dytc(priv->adev->handle, DYTC_CMD_RESET, NULL);
eabe5339
JY
776 if (err)
777 goto unlock;
778 } else {
779 int perfmode;
780
781 err = convert_profile_to_dytc(profile, &perfmode);
782 if (err)
783 goto unlock;
784
785 /* Determine if we are in CQL mode. This alters the commands we do */
65c7713a 786 err = dytc_cql_command(priv, DYTC_SET_COMMAND(DYTC_FUNCTION_MMC, perfmode, 1),
ff67dbd5 787 &output);
eabe5339
JY
788 if (err)
789 goto unlock;
790 }
65c7713a 791
eabe5339
JY
792 /* Success - update current profile */
793 dytc->current_profile = profile;
65c7713a 794
eabe5339
JY
795unlock:
796 mutex_unlock(&dytc->mutex);
65c7713a 797
eabe5339
JY
798 return err;
799}
800
801static void dytc_profile_refresh(struct ideapad_private *priv)
802{
803 enum platform_profile_option profile;
ff36b0d9
BP
804 unsigned long output;
805 int err, perfmode;
eabe5339
JY
806
807 mutex_lock(&priv->dytc->mutex);
808 err = dytc_cql_command(priv, DYTC_CMD_GET, &output);
809 mutex_unlock(&priv->dytc->mutex);
810 if (err)
811 return;
812
813 perfmode = (output >> DYTC_GET_MODE_BIT) & 0xF;
65c7713a
BP
814
815 if (convert_dytc_to_profile(perfmode, &profile))
816 return;
817
eabe5339
JY
818 if (profile != priv->dytc->current_profile) {
819 priv->dytc->current_profile = profile;
820 platform_profile_notify();
821 }
822}
823
599482c5
KA
824static const struct dmi_system_id ideapad_dytc_v4_allow_table[] = {
825 {
826 /* Ideapad 5 Pro 16ACH6 */
599482c5
KA
827 .matches = {
828 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
829 DMI_MATCH(DMI_PRODUCT_NAME, "82L5")
830 }
831 },
8853e8ce
HG
832 {
833 /* Ideapad 5 15ITL05 */
834 .matches = {
835 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
836 DMI_MATCH(DMI_PRODUCT_VERSION, "IdeaPad 5 15ITL05")
837 }
838 },
599482c5
KA
839 {}
840};
841
eabe5339
JY
842static int ideapad_dytc_profile_init(struct ideapad_private *priv)
843{
ff36b0d9
BP
844 int err, dytc_version;
845 unsigned long output;
eabe5339 846
1c59de4a
BP
847 if (!priv->features.dytc)
848 return -ENODEV;
849
ff36b0d9 850 err = eval_dytc(priv->adev->handle, DYTC_CMD_QUERY, &output);
eabe5339
JY
851 /* For all other errors we can flag the failure */
852 if (err)
853 return err;
854
855 /* Check DYTC is enabled and supports mode setting */
599482c5
KA
856 if (!test_bit(DYTC_QUERY_ENABLE_BIT, &output)) {
857 dev_info(&priv->platform_device->dev, "DYTC_QUERY_ENABLE_BIT returned false\n");
eabe5339 858 return -ENODEV;
599482c5 859 }
eabe5339
JY
860
861 dytc_version = (output >> DYTC_QUERY_REV_BIT) & 0xF;
599482c5 862
a27a1e35
HG
863 if (dytc_version < 4) {
864 dev_info(&priv->platform_device->dev, "DYTC_VERSION < 4 is not supported\n");
865 return -ENODEV;
866 }
867
868 if (dytc_version < 5 &&
869 !(allow_v4_dytc || dmi_check_system(ideapad_dytc_v4_allow_table))) {
870 dev_info(&priv->platform_device->dev,
871 "DYTC_VERSION 4 support may not work. Pass ideapad_laptop.allow_v4_dytc=Y on the kernel commandline to enable\n");
872 return -ENODEV;
599482c5 873 }
eabe5339 874
65c7713a 875 priv->dytc = kzalloc(sizeof(*priv->dytc), GFP_KERNEL);
eabe5339
JY
876 if (!priv->dytc)
877 return -ENOMEM;
878
879 mutex_init(&priv->dytc->mutex);
880
881 priv->dytc->priv = priv;
882 priv->dytc->pprof.profile_get = dytc_profile_get;
883 priv->dytc->pprof.profile_set = dytc_profile_set;
884
885 /* Setup supported modes */
886 set_bit(PLATFORM_PROFILE_LOW_POWER, priv->dytc->pprof.choices);
887 set_bit(PLATFORM_PROFILE_BALANCED, priv->dytc->pprof.choices);
888 set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->dytc->pprof.choices);
889
890 /* Create platform_profile structure and register */
891 err = platform_profile_register(&priv->dytc->pprof);
892 if (err)
65c7713a 893 goto pp_reg_failed;
eabe5339
JY
894
895 /* Ensure initial values are correct */
896 dytc_profile_refresh(priv);
897
898 return 0;
899
65c7713a 900pp_reg_failed:
eabe5339
JY
901 mutex_destroy(&priv->dytc->mutex);
902 kfree(priv->dytc);
903 priv->dytc = NULL;
65c7713a 904
eabe5339
JY
905 return err;
906}
907
908static void ideapad_dytc_profile_exit(struct ideapad_private *priv)
909{
910 if (!priv->dytc)
911 return;
912
913 platform_profile_remove();
914 mutex_destroy(&priv->dytc->mutex);
915 kfree(priv->dytc);
65c7713a 916
eabe5339
JY
917 priv->dytc = NULL;
918}
919
a4b5a279
IP
920/*
921 * Rfkill
922 */
c1f73658
IP
923struct ideapad_rfk_data {
924 char *name;
925 int cfgbit;
926 int opcode;
927 int type;
928};
929
b3d94d70 930static const struct ideapad_rfk_data ideapad_rfk_data[] = {
0b765671
BP
931 { "ideapad_wlan", CFG_CAP_WIFI_BIT, VPCCMD_W_WIFI, RFKILL_TYPE_WLAN },
932 { "ideapad_bluetooth", CFG_CAP_BT_BIT, VPCCMD_W_BT, RFKILL_TYPE_BLUETOOTH },
933 { "ideapad_3g", CFG_CAP_3G_BIT, VPCCMD_W_3G, RFKILL_TYPE_WWAN },
c1f73658
IP
934};
935
58ac7aa0
DW
936static int ideapad_rfk_set(void *data, bool blocked)
937{
331e0ea2 938 struct ideapad_rfk_priv *priv = data;
4b200b46 939 int opcode = ideapad_rfk_data[priv->dev].opcode;
fa08359e 940
4b200b46 941 return write_ec_cmd(priv->priv->adev->handle, opcode, !blocked);
58ac7aa0
DW
942}
943
3d59dfcd 944static const struct rfkill_ops ideapad_rfk_ops = {
58ac7aa0
DW
945 .set_block = ideapad_rfk_set,
946};
947
923de84a 948static void ideapad_sync_rfk_state(struct ideapad_private *priv)
58ac7aa0 949{
ce363c2b 950 unsigned long hw_blocked = 0;
58ac7aa0
DW
951 int i;
952
1c59de4a 953 if (priv->features.hw_rfkill_switch) {
ce363c2b
HG
954 if (read_ec_data(priv->adev->handle, VPCCMD_R_RF, &hw_blocked))
955 return;
956 hw_blocked = !hw_blocked;
957 }
58ac7aa0 958
c1f73658 959 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
ce326329 960 if (priv->rfk[i])
2b7266bd 961 rfkill_set_hw_state(priv->rfk[i], hw_blocked);
58ac7aa0
DW
962}
963
75a11f11 964static int ideapad_register_rfkill(struct ideapad_private *priv, int dev)
58ac7aa0 965{
65c7713a
BP
966 unsigned long rf_enabled;
967 int err;
58ac7aa0 968
65c7713a 969 if (no_bt_rfkill && ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH) {
bfa97b7d 970 /* Force to enable bluetooth when no_bt_rfkill=1 */
65c7713a 971 write_ec_cmd(priv->adev->handle, ideapad_rfk_data[dev].opcode, 1);
bfa97b7d
IP
972 return 0;
973 }
65c7713a 974
331e0ea2
ZR
975 priv->rfk_priv[dev].dev = dev;
976 priv->rfk_priv[dev].priv = priv;
bfa97b7d 977
75a11f11 978 priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name,
b5c37b79 979 &priv->platform_device->dev,
75a11f11
ZR
980 ideapad_rfk_data[dev].type,
981 &ideapad_rfk_ops,
331e0ea2 982 &priv->rfk_priv[dev]);
ce326329 983 if (!priv->rfk[dev])
58ac7aa0
DW
984 return -ENOMEM;
985
65c7713a
BP
986 err = read_ec_data(priv->adev->handle, ideapad_rfk_data[dev].opcode - 1, &rf_enabled);
987 if (err)
988 rf_enabled = 1;
989
990 rfkill_init_sw_state(priv->rfk[dev], !rf_enabled);
2b7266bd 991
65c7713a
BP
992 err = rfkill_register(priv->rfk[dev]);
993 if (err)
ce326329 994 rfkill_destroy(priv->rfk[dev]);
65c7713a
BP
995
996 return err;
58ac7aa0
DW
997}
998
75a11f11 999static void ideapad_unregister_rfkill(struct ideapad_private *priv, int dev)
58ac7aa0 1000{
ce326329 1001 if (!priv->rfk[dev])
58ac7aa0
DW
1002 return;
1003
ce326329
DW
1004 rfkill_unregister(priv->rfk[dev]);
1005 rfkill_destroy(priv->rfk[dev]);
58ac7aa0
DW
1006}
1007
98ee6919
IP
1008/*
1009 * Platform device
1010 */
b5c37b79 1011static int ideapad_sysfs_init(struct ideapad_private *priv)
98ee6919 1012{
8782d8d7
BP
1013 return device_add_group(&priv->platform_device->dev,
1014 &ideapad_attribute_group);
98ee6919
IP
1015}
1016
b5c37b79 1017static void ideapad_sysfs_exit(struct ideapad_private *priv)
98ee6919 1018{
8782d8d7
BP
1019 device_remove_group(&priv->platform_device->dev,
1020 &ideapad_attribute_group);
98ee6919 1021}
98ee6919 1022
f63409ae
IP
1023/*
1024 * input device
1025 */
f32e0241
PJ
1026#define IDEAPAD_WMI_KEY 0x100
1027
f63409ae 1028static const struct key_entry ideapad_keymap[] = {
65c7713a
BP
1029 { KE_KEY, 6, { KEY_SWITCHVIDEOMODE } },
1030 { KE_KEY, 7, { KEY_CAMERA } },
1031 { KE_KEY, 8, { KEY_MICMUTE } },
1032 { KE_KEY, 11, { KEY_F16 } },
1033 { KE_KEY, 13, { KEY_WLAN } },
1034 { KE_KEY, 16, { KEY_PROG1 } },
1035 { KE_KEY, 17, { KEY_PROG2 } },
1036 { KE_KEY, 64, { KEY_PROG3 } },
1037 { KE_KEY, 65, { KEY_PROG4 } },
1038 { KE_KEY, 66, { KEY_TOUCHPAD_OFF } },
1039 { KE_KEY, 67, { KEY_TOUCHPAD_ON } },
74caab99 1040 { KE_KEY, 128, { KEY_ESC } },
f32e0241
PJ
1041
1042 /*
1043 * WMI keys
1044 */
1045
1046 /* FnLock (handled by the firmware) */
1047 { KE_IGNORE, 0x02 | IDEAPAD_WMI_KEY },
1048 /* Esc (handled by the firmware) */
1049 { KE_IGNORE, 0x03 | IDEAPAD_WMI_KEY },
1050 /* Customizable Lenovo Hotkey ("star" with 'S' inside) */
1051 { KE_KEY, 0x01 | IDEAPAD_WMI_KEY, { KEY_FAVORITES } },
1052 /* Dark mode toggle */
1053 { KE_KEY, 0x13 | IDEAPAD_WMI_KEY, { KEY_PROG1 } },
1054 /* Sound profile switch */
1055 { KE_KEY, 0x12 | IDEAPAD_WMI_KEY, { KEY_PROG2 } },
1056 /* Lenovo Virtual Background application */
1057 { KE_KEY, 0x28 | IDEAPAD_WMI_KEY, { KEY_PROG3 } },
1058 /* Lenovo Support */
1059 { KE_KEY, 0x27 | IDEAPAD_WMI_KEY, { KEY_HELP } },
1060 /* Refresh Rate Toggle */
1061 { KE_KEY, 0x0a | IDEAPAD_WMI_KEY, { KEY_DISPLAYTOGGLE } },
1062
65c7713a 1063 { KE_END },
f63409ae
IP
1064};
1065
b859f159 1066static int ideapad_input_init(struct ideapad_private *priv)
f63409ae
IP
1067{
1068 struct input_dev *inputdev;
65c7713a 1069 int err;
f63409ae
IP
1070
1071 inputdev = input_allocate_device();
b222cca6 1072 if (!inputdev)
f63409ae 1073 return -ENOMEM;
f63409ae
IP
1074
1075 inputdev->name = "Ideapad extra buttons";
1076 inputdev->phys = "ideapad/input0";
1077 inputdev->id.bustype = BUS_HOST;
8693ae84 1078 inputdev->dev.parent = &priv->platform_device->dev;
f63409ae 1079
65c7713a
BP
1080 err = sparse_keymap_setup(inputdev, ideapad_keymap, NULL);
1081 if (err) {
654324c4 1082 dev_err(&priv->platform_device->dev,
65c7713a 1083 "Could not set up input device keymap: %d\n", err);
f63409ae
IP
1084 goto err_free_dev;
1085 }
1086
65c7713a
BP
1087 err = input_register_device(inputdev);
1088 if (err) {
654324c4 1089 dev_err(&priv->platform_device->dev,
65c7713a 1090 "Could not register input device: %d\n", err);
c973d4b5 1091 goto err_free_dev;
f63409ae
IP
1092 }
1093
8693ae84 1094 priv->inputdev = inputdev;
65c7713a 1095
f63409ae
IP
1096 return 0;
1097
f63409ae
IP
1098err_free_dev:
1099 input_free_device(inputdev);
65c7713a
BP
1100
1101 return err;
f63409ae
IP
1102}
1103
7451a55a 1104static void ideapad_input_exit(struct ideapad_private *priv)
f63409ae 1105{
8693ae84
IP
1106 input_unregister_device(priv->inputdev);
1107 priv->inputdev = NULL;
f63409ae
IP
1108}
1109
8693ae84
IP
1110static void ideapad_input_report(struct ideapad_private *priv,
1111 unsigned long scancode)
f63409ae 1112{
8693ae84 1113 sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
f63409ae 1114}
f63409ae 1115
f43d9ec0
IP
1116static void ideapad_input_novokey(struct ideapad_private *priv)
1117{
1118 unsigned long long_pressed;
1119
331e0ea2 1120 if (read_ec_data(priv->adev->handle, VPCCMD_R_NOVO, &long_pressed))
f43d9ec0 1121 return;
65c7713a 1122
f43d9ec0
IP
1123 if (long_pressed)
1124 ideapad_input_report(priv, 17);
1125 else
1126 ideapad_input_report(priv, 16);
1127}
1128
296f9fe0
MM
1129static void ideapad_check_special_buttons(struct ideapad_private *priv)
1130{
1131 unsigned long bit, value;
1132
7be193e3
BP
1133 if (read_ec_data(priv->adev->handle, VPCCMD_R_SPECIAL_BUTTONS, &value))
1134 return;
296f9fe0 1135
0c4915b6
BP
1136 for_each_set_bit (bit, &value, 16) {
1137 switch (bit) {
0c4915b6 1138 case 6: /* Z570 */
65c7713a 1139 case 0: /* Z580 */
0c4915b6
BP
1140 /* Thermal Management button */
1141 ideapad_input_report(priv, 65);
1142 break;
1143 case 1:
1144 /* OneKey Theater button */
1145 ideapad_input_report(priv, 64);
1146 break;
1147 default:
654324c4
BP
1148 dev_info(&priv->platform_device->dev,
1149 "Unknown special button: %lu\n", bit);
0c4915b6 1150 break;
296f9fe0
MM
1151 }
1152 }
1153}
1154
a4ecbb8a
IP
1155/*
1156 * backlight
1157 */
1158static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)
1159{
331e0ea2 1160 struct ideapad_private *priv = bl_get_data(blightdev);
a4ecbb8a 1161 unsigned long now;
7be193e3 1162 int err;
a4ecbb8a 1163
7be193e3
BP
1164 err = read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now);
1165 if (err)
1166 return err;
65c7713a 1167
a4ecbb8a
IP
1168 return now;
1169}
1170
1171static int ideapad_backlight_update_status(struct backlight_device *blightdev)
1172{
331e0ea2 1173 struct ideapad_private *priv = bl_get_data(blightdev);
7be193e3 1174 int err;
331e0ea2 1175
7be193e3
BP
1176 err = write_ec_cmd(priv->adev->handle, VPCCMD_W_BL,
1177 blightdev->props.brightness);
1178 if (err)
1179 return err;
65c7713a 1180
7be193e3
BP
1181 err = write_ec_cmd(priv->adev->handle, VPCCMD_W_BL_POWER,
1182 blightdev->props.power != FB_BLANK_POWERDOWN);
1183 if (err)
1184 return err;
a4ecbb8a
IP
1185
1186 return 0;
1187}
1188
1189static const struct backlight_ops ideapad_backlight_ops = {
1190 .get_brightness = ideapad_backlight_get_brightness,
1191 .update_status = ideapad_backlight_update_status,
1192};
1193
1194static int ideapad_backlight_init(struct ideapad_private *priv)
1195{
1196 struct backlight_device *blightdev;
1197 struct backlight_properties props;
1198 unsigned long max, now, power;
7be193e3 1199 int err;
a4ecbb8a 1200
7be193e3
BP
1201 err = read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &max);
1202 if (err)
1203 return err;
65c7713a 1204
7be193e3
BP
1205 err = read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now);
1206 if (err)
1207 return err;
65c7713a 1208
7be193e3
BP
1209 err = read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power);
1210 if (err)
1211 return err;
a4ecbb8a 1212
65c7713a
BP
1213 memset(&props, 0, sizeof(props));
1214
a4ecbb8a
IP
1215 props.max_brightness = max;
1216 props.type = BACKLIGHT_PLATFORM;
65c7713a 1217
a4ecbb8a
IP
1218 blightdev = backlight_device_register("ideapad",
1219 &priv->platform_device->dev,
1220 priv,
1221 &ideapad_backlight_ops,
1222 &props);
1223 if (IS_ERR(blightdev)) {
65c7713a 1224 err = PTR_ERR(blightdev);
654324c4 1225 dev_err(&priv->platform_device->dev,
65c7713a
BP
1226 "Could not register backlight device: %d\n", err);
1227 return err;
a4ecbb8a
IP
1228 }
1229
1230 priv->blightdev = blightdev;
1231 blightdev->props.brightness = now;
1232 blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
65c7713a 1233
a4ecbb8a
IP
1234 backlight_update_status(blightdev);
1235
1236 return 0;
1237}
1238
1239static void ideapad_backlight_exit(struct ideapad_private *priv)
1240{
00981810 1241 backlight_device_unregister(priv->blightdev);
a4ecbb8a
IP
1242 priv->blightdev = NULL;
1243}
1244
1245static void ideapad_backlight_notify_power(struct ideapad_private *priv)
1246{
a4ecbb8a 1247 struct backlight_device *blightdev = priv->blightdev;
65c7713a 1248 unsigned long power;
a4ecbb8a 1249
d4afc775
RB
1250 if (!blightdev)
1251 return;
65c7713a 1252
331e0ea2 1253 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))
a4ecbb8a 1254 return;
65c7713a 1255
a4ecbb8a
IP
1256 blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
1257}
1258
1259static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
1260{
1261 unsigned long now;
1262
1263 /* if we control brightness via acpi video driver */
65c7713a 1264 if (!priv->blightdev)
331e0ea2 1265 read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now);
65c7713a
BP
1266 else
1267 backlight_force_update(priv->blightdev, BACKLIGHT_UPDATE_HOTKEY);
a4ecbb8a
IP
1268}
1269
503325f8
BP
1270/*
1271 * keyboard backlight
1272 */
1273static int ideapad_kbd_bl_brightness_get(struct ideapad_private *priv)
1274{
1275 unsigned long hals;
1276 int err;
1277
1278 err = eval_hals(priv->adev->handle, &hals);
1279 if (err)
1280 return err;
1281
1282 return !!test_bit(HALS_KBD_BL_STATE_BIT, &hals);
1283}
1284
1285static enum led_brightness ideapad_kbd_bl_led_cdev_brightness_get(struct led_classdev *led_cdev)
1286{
1287 struct ideapad_private *priv = container_of(led_cdev, struct ideapad_private, kbd_bl.led);
1288
1289 return ideapad_kbd_bl_brightness_get(priv);
1290}
1291
1292static int ideapad_kbd_bl_brightness_set(struct ideapad_private *priv, unsigned int brightness)
1293{
1294 int err = exec_sals(priv->adev->handle, brightness ? SALS_KBD_BL_ON : SALS_KBD_BL_OFF);
1295
1296 if (err)
1297 return err;
1298
1299 priv->kbd_bl.last_brightness = brightness;
1300
1301 return 0;
1302}
1303
1304static int ideapad_kbd_bl_led_cdev_brightness_set(struct led_classdev *led_cdev,
1305 enum led_brightness brightness)
1306{
1307 struct ideapad_private *priv = container_of(led_cdev, struct ideapad_private, kbd_bl.led);
1308
1309 return ideapad_kbd_bl_brightness_set(priv, brightness);
1310}
1311
1312static void ideapad_kbd_bl_notify(struct ideapad_private *priv)
1313{
1314 int brightness;
1315
1316 if (!priv->kbd_bl.initialized)
1317 return;
1318
1319 brightness = ideapad_kbd_bl_brightness_get(priv);
1320 if (brightness < 0)
1321 return;
1322
1323 if (brightness == priv->kbd_bl.last_brightness)
1324 return;
1325
1326 priv->kbd_bl.last_brightness = brightness;
1327
1328 led_classdev_notify_brightness_hw_changed(&priv->kbd_bl.led, brightness);
1329}
1330
1331static int ideapad_kbd_bl_init(struct ideapad_private *priv)
1332{
1333 int brightness, err;
1334
1335 if (!priv->features.kbd_bl)
1336 return -ENODEV;
1337
1338 if (WARN_ON(priv->kbd_bl.initialized))
1339 return -EEXIST;
1340
1341 brightness = ideapad_kbd_bl_brightness_get(priv);
1342 if (brightness < 0)
1343 return brightness;
1344
1345 priv->kbd_bl.last_brightness = brightness;
1346
1347 priv->kbd_bl.led.name = "platform::" LED_FUNCTION_KBD_BACKLIGHT;
1348 priv->kbd_bl.led.max_brightness = 1;
1349 priv->kbd_bl.led.brightness_get = ideapad_kbd_bl_led_cdev_brightness_get;
1350 priv->kbd_bl.led.brightness_set_blocking = ideapad_kbd_bl_led_cdev_brightness_set;
1351 priv->kbd_bl.led.flags = LED_BRIGHT_HW_CHANGED;
1352
1353 err = led_classdev_register(&priv->platform_device->dev, &priv->kbd_bl.led);
1354 if (err)
1355 return err;
1356
1357 priv->kbd_bl.initialized = true;
1358
1359 return 0;
1360}
1361
1362static void ideapad_kbd_bl_exit(struct ideapad_private *priv)
1363{
1364 if (!priv->kbd_bl.initialized)
1365 return;
1366
1367 priv->kbd_bl.initialized = false;
1368
1369 led_classdev_unregister(&priv->kbd_bl.led);
1370}
1371
a4b5a279
IP
1372/*
1373 * module init/exit
1374 */
f4dd8c44 1375static void ideapad_sync_touchpad_state(struct ideapad_private *priv, bool send_events)
07a4a4fc 1376{
07a4a4fc 1377 unsigned long value;
289a5989
HG
1378 unsigned char param;
1379 int ret;
07a4a4fc
MM
1380
1381 /* Without reading from EC touchpad LED doesn't switch state */
289a5989
HG
1382 ret = read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value);
1383 if (ret)
1384 return;
1385
1386 /*
1387 * Some IdeaPads don't really turn off touchpad - they only
1388 * switch the LED state. We (de)activate KBC AUX port to turn
1389 * touchpad off and on. We send KEY_TOUCHPAD_OFF and
1390 * KEY_TOUCHPAD_ON to not to get out of sync with LED
1391 */
c69e7d84
HG
1392 if (priv->features.ctrl_ps2_aux_port)
1393 i8042_command(&param, value ? I8042_CMD_AUX_ENABLE : I8042_CMD_AUX_DISABLE);
f4dd8c44 1394
e3271a59
HG
1395 /*
1396 * On older models the EC controls the touchpad and toggles it on/off
1397 * itself, in this case we report KEY_TOUCHPAD_ON/_OFF. Some models do
1398 * an acpi-notify with VPC bit 5 set on resume, so this function get
1399 * called with send_events=true on every resume. Therefor if the EC did
1400 * not toggle, do nothing to avoid sending spurious KEY_TOUCHPAD_TOGGLE.
1401 */
1402 if (send_events && value != priv->r_touchpad_val) {
1403 ideapad_input_report(priv, value ? 67 : 66);
1404 sysfs_notify(&priv->platform_device->dev.kobj, NULL, "touchpad");
f4dd8c44 1405 }
5829f8a8
HG
1406
1407 priv->r_touchpad_val = value;
07a4a4fc
MM
1408}
1409
b5c37b79
ZR
1410static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)
1411{
1412 struct ideapad_private *priv = data;
0c4915b6 1413 unsigned long vpc1, vpc2, bit;
b5c37b79
ZR
1414
1415 if (read_ec_data(handle, VPCCMD_R_VPC1, &vpc1))
1416 return;
65c7713a 1417
b5c37b79
ZR
1418 if (read_ec_data(handle, VPCCMD_R_VPC2, &vpc2))
1419 return;
1420
1421 vpc1 = (vpc2 << 8) | vpc1;
0c4915b6
BP
1422
1423 for_each_set_bit (bit, &vpc1, 16) {
1424 switch (bit) {
0c4915b6
BP
1425 case 13:
1426 case 11:
1427 case 8:
1428 case 7:
1429 case 6:
1430 ideapad_input_report(priv, bit);
1431 break;
ab66724a
HG
1432 case 10:
1433 /*
1434 * This event gets send on a Yoga 300-11IBR when the EC
1435 * believes that the device has changed between laptop/
1436 * tent/stand/tablet mode. The EC relies on getting
1437 * angle info from 2 accelerometers through a special
1438 * windows service calling a DSM on the DUAL250E ACPI-
1439 * device. Linux does not do this, making the laptop/
1440 * tent/stand/tablet mode info unreliable, so we simply
1441 * ignore these events.
1442 */
1443 break;
65c7713a
BP
1444 case 9:
1445 ideapad_sync_rfk_state(priv);
1446 break;
0c4915b6 1447 case 5:
f4dd8c44 1448 ideapad_sync_touchpad_state(priv, true);
0c4915b6
BP
1449 break;
1450 case 4:
1451 ideapad_backlight_notify_brightness(priv);
1452 break;
1453 case 3:
1454 ideapad_input_novokey(priv);
1455 break;
1456 case 2:
1457 ideapad_backlight_notify_power(priv);
1458 break;
0c4915b6 1459 case 1:
65c7713a
BP
1460 /*
1461 * Some IdeaPads report event 1 every ~20
0c4915b6
BP
1462 * seconds while on battery power; some
1463 * report this when changing to/from tablet
503325f8
BP
1464 * mode; some report this when the keyboard
1465 * backlight has changed.
0c4915b6 1466 */
503325f8 1467 ideapad_kbd_bl_notify(priv);
0c4915b6 1468 break;
65c7713a
BP
1469 case 0:
1470 ideapad_check_special_buttons(priv);
1471 break;
0c4915b6 1472 default:
654324c4
BP
1473 dev_info(&priv->platform_device->dev,
1474 "Unknown event: %lu\n", bit);
b5c37b79
ZR
1475 }
1476 }
1477}
1478
81a5603a
AR
1479/* On some models we need to call exec_sals(SALS_FNLOCK_ON/OFF) to set the LED */
1480static const struct dmi_system_id set_fn_lock_led_list[] = {
1481 {
1482 /* https://bugzilla.kernel.org/show_bug.cgi?id=212671 */
1483 .matches = {
1484 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1485 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Legion R7000P2020H"),
1486 }
1487 },
f4b7f8fe
HG
1488 {
1489 .matches = {
1490 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1491 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Legion 5 15ARH05"),
1492 }
1493 },
81a5603a
AR
1494 {}
1495};
1496
ce363c2b 1497/*
5105e78e
HG
1498 * Some ideapads have a hardware rfkill switch, but most do not have one.
1499 * Reading VPCCMD_R_RF always results in 0 on models without a hardware rfkill,
1500 * switch causing ideapad_laptop to wrongly report all radios as hw-blocked.
1501 * There used to be a long list of DMI ids for models without a hw rfkill
1502 * switch here, but that resulted in playing whack a mole.
1503 * More importantly wrongly reporting the wifi radio as hw-blocked, results in
1504 * non working wifi. Whereas not reporting it hw-blocked, when it actually is
1505 * hw-blocked results in an empty SSID list, which is a much more benign
1506 * failure mode.
1507 * So the default now is the much safer option of assuming there is no
1508 * hardware rfkill switch. This default also actually matches most hardware,
1509 * since having a hw rfkill switch is quite rare on modern hardware, so this
1510 * also leads to a much shorter list.
ce363c2b 1511 */
5105e78e 1512static const struct dmi_system_id hw_rfkill_list[] = {
85093f79
HG
1513 {}
1514};
1515
c69e7d84
HG
1516/*
1517 * On some models the EC toggles the touchpad muted LED on touchpad toggle
1518 * hotkey presses, but the EC does not actually disable the touchpad itself.
1519 * On these models the driver needs to explicitly enable/disable the i8042
1520 * (PS/2) aux port.
1521 */
1522static const struct dmi_system_id ctrl_ps2_aux_port_list[] = {
1523 {
1524 /* Lenovo Ideapad Z570 */
1525 .matches = {
1526 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1527 DMI_MATCH(DMI_PRODUCT_VERSION, "Ideapad Z570"),
1528 },
1529 },
1530 {}
1531};
1532
1c59de4a
BP
1533static void ideapad_check_features(struct ideapad_private *priv)
1534{
1535 acpi_handle handle = priv->adev->handle;
1536 unsigned long val;
1537
b44fd994
HG
1538 priv->features.set_fn_lock_led =
1539 set_fn_lock_led || dmi_check_system(set_fn_lock_led_list);
1540 priv->features.hw_rfkill_switch =
1541 hw_rfkill_switch || dmi_check_system(hw_rfkill_list);
c69e7d84
HG
1542 priv->features.ctrl_ps2_aux_port =
1543 ctrl_ps2_aux_port || dmi_check_system(ctrl_ps2_aux_port_list);
301e0d76 1544 priv->features.touchpad_ctrl_via_ec = touchpad_ctrl_via_ec;
1c59de4a
BP
1545
1546 if (!read_ec_data(handle, VPCCMD_R_FAN, &val))
1547 priv->features.fan_mode = true;
1548
1549 if (acpi_has_method(handle, "GBMD") && acpi_has_method(handle, "SBMC"))
1550 priv->features.conservation_mode = true;
1551
1552 if (acpi_has_method(handle, "DYTC"))
1553 priv->features.dytc = true;
1554
392cbf0a
BP
1555 if (acpi_has_method(handle, "HALS") && acpi_has_method(handle, "SALS")) {
1556 if (!eval_hals(handle, &val)) {
1557 if (test_bit(HALS_FNLOCK_SUPPORT_BIT, &val))
1558 priv->features.fn_lock = true;
503325f8
BP
1559
1560 if (test_bit(HALS_KBD_BL_SUPPORT_BIT, &val))
1561 priv->features.kbd_bl = true;
6b49dea4
BP
1562
1563 if (test_bit(HALS_USB_CHARGING_SUPPORT_BIT, &val))
1564 priv->features.usb_charging = true;
392cbf0a
BP
1565 }
1566 }
1c59de4a
BP
1567}
1568
f32e0241
PJ
1569#if IS_ENABLED(CONFIG_ACPI_WMI)
1570/*
1571 * WMI driver
1572 */
1573enum ideapad_wmi_event_type {
1574 IDEAPAD_WMI_EVENT_ESC,
1575 IDEAPAD_WMI_EVENT_FN_KEYS,
1576};
1577
1578struct ideapad_wmi_private {
1579 enum ideapad_wmi_event_type event;
1580};
1581
1582static int ideapad_wmi_probe(struct wmi_device *wdev, const void *context)
1583{
1584 struct ideapad_wmi_private *wpriv;
1585
1586 wpriv = devm_kzalloc(&wdev->dev, sizeof(*wpriv), GFP_KERNEL);
1587 if (!wpriv)
1588 return -ENOMEM;
1589
1590 *wpriv = *(const struct ideapad_wmi_private *)context;
1591
1592 dev_set_drvdata(&wdev->dev, wpriv);
1593 return 0;
1594}
1595
1596static void ideapad_wmi_notify(struct wmi_device *wdev, union acpi_object *data)
1597{
1598 struct ideapad_wmi_private *wpriv = dev_get_drvdata(&wdev->dev);
1599 struct ideapad_private *priv;
1600 unsigned long result;
1601
1602 mutex_lock(&ideapad_shared_mutex);
1603
1604 priv = ideapad_shared;
1605 if (!priv)
1606 goto unlock;
1607
1608 switch (wpriv->event) {
1609 case IDEAPAD_WMI_EVENT_ESC:
1610 ideapad_input_report(priv, 128);
1611 break;
1612 case IDEAPAD_WMI_EVENT_FN_KEYS:
1613 if (priv->features.set_fn_lock_led &&
1614 !eval_hals(priv->adev->handle, &result)) {
1615 bool state = test_bit(HALS_FNLOCK_STATE_BIT, &result);
1616
1617 exec_sals(priv->adev->handle, state ? SALS_FNLOCK_ON : SALS_FNLOCK_OFF);
1618 }
1619
1620 if (data->type != ACPI_TYPE_INTEGER) {
1621 dev_warn(&wdev->dev,
1622 "WMI event data is not an integer\n");
1623 break;
1624 }
1625
1626 dev_dbg(&wdev->dev, "WMI fn-key event: 0x%llx\n",
1627 data->integer.value);
1628
1629 ideapad_input_report(priv,
1630 data->integer.value | IDEAPAD_WMI_KEY);
1631
1632 break;
1633 }
1634unlock:
1635 mutex_unlock(&ideapad_shared_mutex);
1636}
1637
1638static const struct ideapad_wmi_private ideapad_wmi_context_esc = {
1639 .event = IDEAPAD_WMI_EVENT_ESC
1640};
1641
1642static const struct ideapad_wmi_private ideapad_wmi_context_fn_keys = {
1643 .event = IDEAPAD_WMI_EVENT_FN_KEYS
1644};
1645
1646static const struct wmi_device_id ideapad_wmi_ids[] = {
1647 { "26CAB2E5-5CF1-46AE-AAC3-4A12B6BA50E6", &ideapad_wmi_context_esc }, /* Yoga 3 */
1648 { "56322276-8493-4CE8-A783-98C991274F5E", &ideapad_wmi_context_esc }, /* Yoga 700 */
1649 { "8FC0DE0C-B4E4-43FD-B0F3-8871711C1294", &ideapad_wmi_context_fn_keys }, /* Legion 5 */
1650 {},
1651};
1652MODULE_DEVICE_TABLE(wmi, ideapad_wmi_ids);
1653
1654static struct wmi_driver ideapad_wmi_driver = {
1655 .driver = {
1656 .name = "ideapad_wmi",
1657 },
1658 .id_table = ideapad_wmi_ids,
1659 .probe = ideapad_wmi_probe,
1660 .notify = ideapad_wmi_notify,
1661};
1662
1663static int ideapad_wmi_driver_register(void)
1664{
1665 return wmi_driver_register(&ideapad_wmi_driver);
1666}
1667
1668static void ideapad_wmi_driver_unregister(void)
1669{
1670 return wmi_driver_unregister(&ideapad_wmi_driver);
1671}
1672
1673#else
1674static inline int ideapad_wmi_driver_register(void) { return 0; }
1675static inline void ideapad_wmi_driver_unregister(void) { }
1676#endif
1677
1678/*
1679 * ACPI driver
1680 */
b5c37b79 1681static int ideapad_acpi_add(struct platform_device *pdev)
58ac7aa0 1682{
043449e7 1683 struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
ce326329 1684 struct ideapad_private *priv;
803be832 1685 acpi_status status;
ff36b0d9 1686 unsigned long cfg;
65c7713a 1687 int err, i;
b5c37b79 1688
043449e7 1689 if (!adev || eval_int(adev->handle, "_CFG", &cfg))
6f8371c0
IP
1690 return -ENODEV;
1691
b3facd7b 1692 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
ce326329
DW
1693 if (!priv)
1694 return -ENOMEM;
b5c37b79
ZR
1695
1696 dev_set_drvdata(&pdev->dev, priv);
65c7713a 1697
3371f481 1698 priv->cfg = cfg;
469f6434 1699 priv->adev = adev;
b5c37b79 1700 priv->platform_device = pdev;
98ee6919 1701
1c59de4a 1702 ideapad_check_features(priv);
d69cd7ee 1703
65c7713a
BP
1704 err = ideapad_sysfs_init(priv);
1705 if (err)
1706 return err;
ce326329 1707
17f1bf38 1708 ideapad_debugfs_init(priv);
773e3206 1709
65c7713a
BP
1710 err = ideapad_input_init(priv);
1711 if (err)
f63409ae
IP
1712 goto input_failed;
1713
503325f8
BP
1714 err = ideapad_kbd_bl_init(priv);
1715 if (err) {
1716 if (err != -ENODEV)
1717 dev_warn(&pdev->dev, "Could not set up keyboard backlight LED: %d\n", err);
1718 else
1719 dev_info(&pdev->dev, "Keyboard backlight control not available\n");
1720 }
1721
ce363c2b
HG
1722 /*
1723 * On some models without a hw-switch (the yoga 2 13 at least)
1724 * VPCCMD_W_RF must be explicitly set to 1 for the wifi to work.
1725 */
1c59de4a 1726 if (!priv->features.hw_rfkill_switch)
ce363c2b
HG
1727 write_ec_cmd(priv->adev->handle, VPCCMD_W_RF, 1);
1728
1729 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
1730 if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg))
1731 ideapad_register_rfkill(priv, i);
1732
923de84a 1733 ideapad_sync_rfk_state(priv);
f4dd8c44 1734 ideapad_sync_touchpad_state(priv, false);
c9f718d0 1735
65c7713a
BP
1736 err = ideapad_dytc_profile_init(priv);
1737 if (err) {
1738 if (err != -ENODEV)
1739 dev_warn(&pdev->dev, "Could not set up DYTC interface: %d\n", err);
1c59de4a
BP
1740 else
1741 dev_info(&pdev->dev, "DYTC interface is not available\n");
1742 }
eabe5339 1743
26bff5f0 1744 if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
65c7713a
BP
1745 err = ideapad_backlight_init(priv);
1746 if (err && err != -ENODEV)
a4ecbb8a
IP
1747 goto backlight_failed;
1748 }
65c7713a 1749
803be832
BP
1750 status = acpi_install_notify_handler(adev->handle,
1751 ACPI_DEVICE_NOTIFY,
1752 ideapad_acpi_notify, priv);
1753 if (ACPI_FAILURE(status)) {
65c7713a 1754 err = -EIO;
b5c37b79 1755 goto notification_failed;
803be832 1756 }
2d98e0b9 1757
f32e0241
PJ
1758 err = ideapad_shared_init(priv);
1759 if (err)
1760 goto shared_init_failed;
a4ecbb8a 1761
58ac7aa0 1762 return 0;
65c7713a 1763
f32e0241 1764shared_init_failed:
74caab99 1765 acpi_remove_notify_handler(priv->adev->handle,
65c7713a
BP
1766 ACPI_DEVICE_NOTIFY,
1767 ideapad_acpi_notify);
65c7713a 1768
b5c37b79
ZR
1769notification_failed:
1770 ideapad_backlight_exit(priv);
65c7713a 1771
a4ecbb8a 1772backlight_failed:
caa315b8 1773 ideapad_dytc_profile_exit(priv);
65c7713a 1774
a4ecbb8a 1775 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
75a11f11 1776 ideapad_unregister_rfkill(priv, i);
65c7713a 1777
503325f8 1778 ideapad_kbd_bl_exit(priv);
7451a55a 1779 ideapad_input_exit(priv);
65c7713a 1780
f63409ae 1781input_failed:
773e3206 1782 ideapad_debugfs_exit(priv);
b5c37b79 1783 ideapad_sysfs_exit(priv);
65c7713a
BP
1784
1785 return err;
58ac7aa0
DW
1786}
1787
d21c474c 1788static void ideapad_acpi_remove(struct platform_device *pdev)
58ac7aa0 1789{
b5c37b79 1790 struct ideapad_private *priv = dev_get_drvdata(&pdev->dev);
58ac7aa0 1791 int i;
ce326329 1792
f32e0241 1793 ideapad_shared_exit(priv);
65c7713a 1794
b5c37b79 1795 acpi_remove_notify_handler(priv->adev->handle,
65c7713a
BP
1796 ACPI_DEVICE_NOTIFY,
1797 ideapad_acpi_notify);
1798
a4ecbb8a 1799 ideapad_backlight_exit(priv);
eabe5339 1800 ideapad_dytc_profile_exit(priv);
65c7713a 1801
c1f73658 1802 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
75a11f11 1803 ideapad_unregister_rfkill(priv, i);
65c7713a 1804
503325f8 1805 ideapad_kbd_bl_exit(priv);
8693ae84 1806 ideapad_input_exit(priv);
773e3206 1807 ideapad_debugfs_exit(priv);
b5c37b79 1808 ideapad_sysfs_exit(priv);
58ac7aa0
DW
1809}
1810
11fa8da5 1811#ifdef CONFIG_PM_SLEEP
e1a39a44 1812static int ideapad_acpi_resume(struct device *dev)
07a4a4fc 1813{
e1a39a44 1814 struct ideapad_private *priv = dev_get_drvdata(dev);
75a11f11
ZR
1815
1816 ideapad_sync_rfk_state(priv);
f4dd8c44 1817 ideapad_sync_touchpad_state(priv, false);
eabe5339
JY
1818
1819 if (priv->dytc)
1820 dytc_profile_refresh(priv);
1821
07a4a4fc
MM
1822 return 0;
1823}
11fa8da5 1824#endif
b5c37b79 1825static SIMPLE_DEV_PM_OPS(ideapad_pm, NULL, ideapad_acpi_resume);
07a4a4fc 1826
b5c37b79 1827static const struct acpi_device_id ideapad_device_ids[] = {
65c7713a
BP
1828 {"VPC2004", 0},
1829 {"", 0},
58ac7aa0 1830};
b5c37b79
ZR
1831MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
1832
1833static struct platform_driver ideapad_acpi_driver = {
1834 .probe = ideapad_acpi_add,
d21c474c 1835 .remove_new = ideapad_acpi_remove,
b5c37b79
ZR
1836 .driver = {
1837 .name = "ideapad_acpi",
b5c37b79
ZR
1838 .pm = &ideapad_pm,
1839 .acpi_match_table = ACPI_PTR(ideapad_device_ids),
1840 },
1841};
1842
f32e0241
PJ
1843static int __init ideapad_laptop_init(void)
1844{
1845 int err;
1846
1847 err = ideapad_wmi_driver_register();
1848 if (err)
1849 return err;
1850
1851 err = platform_driver_register(&ideapad_acpi_driver);
1852 if (err) {
1853 ideapad_wmi_driver_unregister();
1854 return err;
1855 }
1856
1857 return 0;
1858}
1859module_init(ideapad_laptop_init)
1860
1861static void __exit ideapad_laptop_exit(void)
1862{
1863 ideapad_wmi_driver_unregister();
1864 platform_driver_unregister(&ideapad_acpi_driver);
1865}
1866module_exit(ideapad_laptop_exit)
58ac7aa0
DW
1867
1868MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
1869MODULE_DESCRIPTION("IdeaPad ACPI Extras");
1870MODULE_LICENSE("GPL");