]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - drivers/platform/x86/ideapad-laptop.c
Merge tag 'wireless-drivers-for-davem-2017-11-22' of git://git.kernel.org/pub/scm...
[mirror_ubuntu-bionic-kernel.git] / drivers / platform / x86 / ideapad-laptop.c
CommitLineData
58ac7aa0 1/*
a4b5a279 2 * ideapad-laptop.c - Lenovo IdeaPad ACPI Extras
58ac7aa0
DW
3 *
4 * Copyright © 2010 Intel Corporation
5 * Copyright © 2010 David Woodhouse <dwmw2@infradead.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301, USA.
21 */
22
9ab23989
JP
23#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
24
58ac7aa0
DW
25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/init.h>
28#include <linux/types.h>
8b48463f 29#include <linux/acpi.h>
58ac7aa0 30#include <linux/rfkill.h>
98ee6919 31#include <linux/platform_device.h>
f63409ae
IP
32#include <linux/input.h>
33#include <linux/input/sparse-keymap.h>
a4ecbb8a
IP
34#include <linux/backlight.h>
35#include <linux/fb.h>
773e3206
IP
36#include <linux/debugfs.h>
37#include <linux/seq_file.h>
07a4a4fc 38#include <linux/i8042.h>
85093f79 39#include <linux/dmi.h>
b3facd7b 40#include <linux/device.h>
26bff5f0 41#include <acpi/video.h>
58ac7aa0 42
c1f73658 43#define IDEAPAD_RFKILL_DEV_NUM (3)
58ac7aa0 44
ade50296
HWT
45#define BM_CONSERVATION_BIT (5)
46
3371f481
IP
47#define CFG_BT_BIT (16)
48#define CFG_3G_BIT (17)
49#define CFG_WIFI_BIT (18)
a84511f7 50#define CFG_CAMERA_BIT (19)
3371f481 51
74caab99 52#if IS_ENABLED(CONFIG_ACPI_WMI)
2d98e0b9
AB
53static const char *const ideapad_wmi_fnesc_events[] = {
54 "26CAB2E5-5CF1-46AE-AAC3-4A12B6BA50E6", /* Yoga 3 */
55 "56322276-8493-4CE8-A783-98C991274F5E", /* Yoga 700 */
56};
74caab99
AB
57#endif
58
ade50296
HWT
59enum {
60 BMCMD_CONSERVATION_ON = 3,
61 BMCMD_CONSERVATION_OFF = 5,
62};
63
2be1dc21
IP
64enum {
65 VPCCMD_R_VPC1 = 0x10,
66 VPCCMD_R_BL_MAX,
67 VPCCMD_R_BL,
68 VPCCMD_W_BL,
69 VPCCMD_R_WIFI,
70 VPCCMD_W_WIFI,
71 VPCCMD_R_BT,
72 VPCCMD_W_BT,
73 VPCCMD_R_BL_POWER,
74 VPCCMD_R_NOVO,
75 VPCCMD_R_VPC2,
76 VPCCMD_R_TOUCHPAD,
77 VPCCMD_W_TOUCHPAD,
78 VPCCMD_R_CAMERA,
79 VPCCMD_W_CAMERA,
80 VPCCMD_R_3G,
81 VPCCMD_W_3G,
82 VPCCMD_R_ODD, /* 0x21 */
0c7bbeb9
MM
83 VPCCMD_W_FAN,
84 VPCCMD_R_RF,
2be1dc21 85 VPCCMD_W_RF,
0c7bbeb9 86 VPCCMD_R_FAN = 0x2B,
296f9fe0 87 VPCCMD_R_SPECIAL_BUTTONS = 0x31,
2be1dc21
IP
88 VPCCMD_W_BL_POWER = 0x33,
89};
90
331e0ea2
ZR
91struct ideapad_rfk_priv {
92 int dev;
93 struct ideapad_private *priv;
94};
95
ce326329 96struct ideapad_private {
469f6434 97 struct acpi_device *adev;
c1f73658 98 struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
331e0ea2 99 struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM];
98ee6919 100 struct platform_device *platform_device;
f63409ae 101 struct input_dev *inputdev;
a4ecbb8a 102 struct backlight_device *blightdev;
773e3206 103 struct dentry *debug;
3371f481 104 unsigned long cfg;
ce363c2b 105 bool has_hw_rfkill_switch;
2d98e0b9 106 const char *fnesc_guid;
58ac7aa0
DW
107};
108
bfa97b7d
IP
109static bool no_bt_rfkill;
110module_param(no_bt_rfkill, bool, 0444);
111MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
112
6a09f21d
IP
113/*
114 * ACPI Helpers
115 */
116#define IDEAPAD_EC_TIMEOUT (100) /* in ms */
117
118static int read_method_int(acpi_handle handle, const char *method, int *val)
119{
120 acpi_status status;
121 unsigned long long result;
122
123 status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
124 if (ACPI_FAILURE(status)) {
125 *val = -1;
126 return -1;
127 } else {
128 *val = result;
129 return 0;
130 }
131}
132
ade50296
HWT
133static int method_gbmd(acpi_handle handle, unsigned long *ret)
134{
135 int result, val;
136
137 result = read_method_int(handle, "GBMD", &val);
138 *ret = val;
139 return result;
140}
141
142static int method_sbmc(acpi_handle handle, int cmd)
143{
144 acpi_status status;
145
146 status = acpi_execute_simple_method(handle, "SBMC", cmd);
147 return ACPI_FAILURE(status) ? -1 : 0;
148}
149
6a09f21d
IP
150static int method_vpcr(acpi_handle handle, int cmd, int *ret)
151{
152 acpi_status status;
153 unsigned long long result;
154 struct acpi_object_list params;
155 union acpi_object in_obj;
156
157 params.count = 1;
158 params.pointer = &in_obj;
159 in_obj.type = ACPI_TYPE_INTEGER;
160 in_obj.integer.value = cmd;
161
162 status = acpi_evaluate_integer(handle, "VPCR", &params, &result);
163
164 if (ACPI_FAILURE(status)) {
165 *ret = -1;
166 return -1;
167 } else {
168 *ret = result;
169 return 0;
170 }
171}
172
173static int method_vpcw(acpi_handle handle, int cmd, int data)
174{
175 struct acpi_object_list params;
176 union acpi_object in_obj[2];
177 acpi_status status;
178
179 params.count = 2;
180 params.pointer = in_obj;
181 in_obj[0].type = ACPI_TYPE_INTEGER;
182 in_obj[0].integer.value = cmd;
183 in_obj[1].type = ACPI_TYPE_INTEGER;
184 in_obj[1].integer.value = data;
185
186 status = acpi_evaluate_object(handle, "VPCW", &params, NULL);
187 if (status != AE_OK)
188 return -1;
189 return 0;
190}
191
192static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data)
193{
194 int val;
195 unsigned long int end_jiffies;
196
197 if (method_vpcw(handle, 1, cmd))
198 return -1;
199
200 for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
201 time_before(jiffies, end_jiffies);) {
202 schedule();
203 if (method_vpcr(handle, 1, &val))
204 return -1;
205 if (val == 0) {
206 if (method_vpcr(handle, 0, &val))
207 return -1;
208 *data = val;
209 return 0;
210 }
211 }
212 pr_err("timeout in read_ec_cmd\n");
213 return -1;
214}
215
216static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
217{
218 int val;
219 unsigned long int end_jiffies;
220
221 if (method_vpcw(handle, 0, data))
222 return -1;
223 if (method_vpcw(handle, 1, cmd))
224 return -1;
225
226 for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
227 time_before(jiffies, end_jiffies);) {
228 schedule();
229 if (method_vpcr(handle, 1, &val))
230 return -1;
231 if (val == 0)
232 return 0;
233 }
234 pr_err("timeout in write_ec_cmd\n");
235 return -1;
236}
6a09f21d 237
773e3206
IP
238/*
239 * debugfs
240 */
773e3206
IP
241static int debugfs_status_show(struct seq_file *s, void *data)
242{
331e0ea2 243 struct ideapad_private *priv = s->private;
773e3206
IP
244 unsigned long value;
245
331e0ea2
ZR
246 if (!priv)
247 return -EINVAL;
248
249 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &value))
773e3206 250 seq_printf(s, "Backlight max:\t%lu\n", value);
331e0ea2 251 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL, &value))
773e3206 252 seq_printf(s, "Backlight now:\t%lu\n", value);
331e0ea2 253 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &value))
773e3206
IP
254 seq_printf(s, "BL power value:\t%s\n", value ? "On" : "Off");
255 seq_printf(s, "=====================\n");
256
331e0ea2 257 if (!read_ec_data(priv->adev->handle, VPCCMD_R_RF, &value))
773e3206
IP
258 seq_printf(s, "Radio status:\t%s(%lu)\n",
259 value ? "On" : "Off", value);
331e0ea2 260 if (!read_ec_data(priv->adev->handle, VPCCMD_R_WIFI, &value))
773e3206
IP
261 seq_printf(s, "Wifi status:\t%s(%lu)\n",
262 value ? "On" : "Off", value);
331e0ea2 263 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BT, &value))
773e3206
IP
264 seq_printf(s, "BT status:\t%s(%lu)\n",
265 value ? "On" : "Off", value);
331e0ea2 266 if (!read_ec_data(priv->adev->handle, VPCCMD_R_3G, &value))
773e3206
IP
267 seq_printf(s, "3G status:\t%s(%lu)\n",
268 value ? "On" : "Off", value);
269 seq_printf(s, "=====================\n");
270
331e0ea2 271 if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value))
773e3206
IP
272 seq_printf(s, "Touchpad status:%s(%lu)\n",
273 value ? "On" : "Off", value);
331e0ea2 274 if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value))
773e3206
IP
275 seq_printf(s, "Camera status:\t%s(%lu)\n",
276 value ? "On" : "Off", value);
ade50296
HWT
277 seq_puts(s, "=====================\n");
278
279 if (!method_gbmd(priv->adev->handle, &value)) {
280 seq_printf(s, "Conservation mode:\t%s(%lu)\n",
281 test_bit(BM_CONSERVATION_BIT, &value) ? "On" : "Off",
282 value);
283 }
773e3206
IP
284
285 return 0;
286}
287
288static int debugfs_status_open(struct inode *inode, struct file *file)
289{
331e0ea2 290 return single_open(file, debugfs_status_show, inode->i_private);
773e3206
IP
291}
292
293static const struct file_operations debugfs_status_fops = {
294 .owner = THIS_MODULE,
295 .open = debugfs_status_open,
296 .read = seq_read,
297 .llseek = seq_lseek,
298 .release = single_release,
299};
300
301static int debugfs_cfg_show(struct seq_file *s, void *data)
302{
331e0ea2
ZR
303 struct ideapad_private *priv = s->private;
304
305 if (!priv) {
773e3206
IP
306 seq_printf(s, "cfg: N/A\n");
307 } else {
308 seq_printf(s, "cfg: 0x%.8lX\n\nCapability: ",
331e0ea2
ZR
309 priv->cfg);
310 if (test_bit(CFG_BT_BIT, &priv->cfg))
773e3206 311 seq_printf(s, "Bluetooth ");
331e0ea2 312 if (test_bit(CFG_3G_BIT, &priv->cfg))
773e3206 313 seq_printf(s, "3G ");
331e0ea2 314 if (test_bit(CFG_WIFI_BIT, &priv->cfg))
773e3206 315 seq_printf(s, "Wireless ");
331e0ea2 316 if (test_bit(CFG_CAMERA_BIT, &priv->cfg))
773e3206
IP
317 seq_printf(s, "Camera ");
318 seq_printf(s, "\nGraphic: ");
331e0ea2 319 switch ((priv->cfg)&0x700) {
773e3206
IP
320 case 0x100:
321 seq_printf(s, "Intel");
322 break;
323 case 0x200:
324 seq_printf(s, "ATI");
325 break;
326 case 0x300:
327 seq_printf(s, "Nvidia");
328 break;
329 case 0x400:
330 seq_printf(s, "Intel and ATI");
331 break;
332 case 0x500:
333 seq_printf(s, "Intel and Nvidia");
334 break;
335 }
336 seq_printf(s, "\n");
337 }
338 return 0;
339}
340
341static int debugfs_cfg_open(struct inode *inode, struct file *file)
342{
331e0ea2 343 return single_open(file, debugfs_cfg_show, inode->i_private);
773e3206
IP
344}
345
346static const struct file_operations debugfs_cfg_fops = {
347 .owner = THIS_MODULE,
348 .open = debugfs_cfg_open,
349 .read = seq_read,
350 .llseek = seq_lseek,
351 .release = single_release,
352};
353
b859f159 354static int ideapad_debugfs_init(struct ideapad_private *priv)
773e3206
IP
355{
356 struct dentry *node;
357
358 priv->debug = debugfs_create_dir("ideapad", NULL);
359 if (priv->debug == NULL) {
360 pr_err("failed to create debugfs directory");
361 goto errout;
362 }
363
331e0ea2 364 node = debugfs_create_file("cfg", S_IRUGO, priv->debug, priv,
773e3206
IP
365 &debugfs_cfg_fops);
366 if (!node) {
367 pr_err("failed to create cfg in debugfs");
368 goto errout;
369 }
370
331e0ea2 371 node = debugfs_create_file("status", S_IRUGO, priv->debug, priv,
773e3206
IP
372 &debugfs_status_fops);
373 if (!node) {
a5c3892f 374 pr_err("failed to create status in debugfs");
773e3206
IP
375 goto errout;
376 }
377
378 return 0;
379
380errout:
381 return -ENOMEM;
382}
383
384static void ideapad_debugfs_exit(struct ideapad_private *priv)
385{
386 debugfs_remove_recursive(priv->debug);
387 priv->debug = NULL;
388}
389
a4b5a279 390/*
3371f481 391 * sysfs
a4b5a279 392 */
58ac7aa0
DW
393static ssize_t show_ideapad_cam(struct device *dev,
394 struct device_attribute *attr,
395 char *buf)
396{
26c81d5c 397 unsigned long result;
331e0ea2 398 struct ideapad_private *priv = dev_get_drvdata(dev);
58ac7aa0 399
331e0ea2 400 if (read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &result))
26c81d5c
IP
401 return sprintf(buf, "-1\n");
402 return sprintf(buf, "%lu\n", result);
58ac7aa0
DW
403}
404
405static ssize_t store_ideapad_cam(struct device *dev,
406 struct device_attribute *attr,
407 const char *buf, size_t count)
408{
409 int ret, state;
331e0ea2 410 struct ideapad_private *priv = dev_get_drvdata(dev);
58ac7aa0
DW
411
412 if (!count)
413 return 0;
414 if (sscanf(buf, "%i", &state) != 1)
415 return -EINVAL;
331e0ea2 416 ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_CAMERA, state);
58ac7aa0 417 if (ret < 0)
0c7bbeb9 418 return -EIO;
58ac7aa0
DW
419 return count;
420}
421
422static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
423
0c7bbeb9
MM
424static ssize_t show_ideapad_fan(struct device *dev,
425 struct device_attribute *attr,
426 char *buf)
427{
428 unsigned long result;
331e0ea2 429 struct ideapad_private *priv = dev_get_drvdata(dev);
0c7bbeb9 430
331e0ea2 431 if (read_ec_data(priv->adev->handle, VPCCMD_R_FAN, &result))
0c7bbeb9
MM
432 return sprintf(buf, "-1\n");
433 return sprintf(buf, "%lu\n", result);
434}
435
436static ssize_t store_ideapad_fan(struct device *dev,
437 struct device_attribute *attr,
438 const char *buf, size_t count)
439{
440 int ret, state;
331e0ea2 441 struct ideapad_private *priv = dev_get_drvdata(dev);
0c7bbeb9
MM
442
443 if (!count)
444 return 0;
445 if (sscanf(buf, "%i", &state) != 1)
446 return -EINVAL;
447 if (state < 0 || state > 4 || state == 3)
448 return -EINVAL;
331e0ea2 449 ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_FAN, state);
0c7bbeb9
MM
450 if (ret < 0)
451 return -EIO;
452 return count;
453}
454
455static DEVICE_ATTR(fan_mode, 0644, show_ideapad_fan, store_ideapad_fan);
456
36ac0d43
RRS
457static ssize_t touchpad_show(struct device *dev,
458 struct device_attribute *attr,
459 char *buf)
460{
461 struct ideapad_private *priv = dev_get_drvdata(dev);
462 unsigned long result;
463
464 if (read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &result))
465 return sprintf(buf, "-1\n");
466 return sprintf(buf, "%lu\n", result);
467}
468
46936fd6
AB
469/* Switch to RO for now: It might be revisited in the future */
470static ssize_t __maybe_unused touchpad_store(struct device *dev,
471 struct device_attribute *attr,
472 const char *buf, size_t count)
36ac0d43
RRS
473{
474 struct ideapad_private *priv = dev_get_drvdata(dev);
475 bool state;
476 int ret;
477
478 ret = kstrtobool(buf, &state);
479 if (ret)
480 return ret;
481
482 ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, state);
483 if (ret < 0)
484 return -EIO;
485 return count;
486}
487
7f363145 488static DEVICE_ATTR_RO(touchpad);
36ac0d43 489
ade50296
HWT
490static ssize_t conservation_mode_show(struct device *dev,
491 struct device_attribute *attr,
492 char *buf)
493{
494 struct ideapad_private *priv = dev_get_drvdata(dev);
495 unsigned long result;
496
497 if (method_gbmd(priv->adev->handle, &result))
498 return sprintf(buf, "-1\n");
499 return sprintf(buf, "%u\n", test_bit(BM_CONSERVATION_BIT, &result));
500}
501
502static ssize_t conservation_mode_store(struct device *dev,
503 struct device_attribute *attr,
504 const char *buf, size_t count)
505{
506 struct ideapad_private *priv = dev_get_drvdata(dev);
507 bool state;
508 int ret;
509
510 ret = kstrtobool(buf, &state);
511 if (ret)
512 return ret;
513
514 ret = method_sbmc(priv->adev->handle, state ?
515 BMCMD_CONSERVATION_ON :
516 BMCMD_CONSERVATION_OFF);
517 if (ret < 0)
518 return -EIO;
519 return count;
520}
521
522static DEVICE_ATTR_RW(conservation_mode);
523
3371f481
IP
524static struct attribute *ideapad_attributes[] = {
525 &dev_attr_camera_power.attr,
0c7bbeb9 526 &dev_attr_fan_mode.attr,
36ac0d43 527 &dev_attr_touchpad.attr,
ade50296 528 &dev_attr_conservation_mode.attr,
3371f481
IP
529 NULL
530};
531
587a1f16 532static umode_t ideapad_is_visible(struct kobject *kobj,
a84511f7
IP
533 struct attribute *attr,
534 int idx)
535{
536 struct device *dev = container_of(kobj, struct device, kobj);
537 struct ideapad_private *priv = dev_get_drvdata(dev);
538 bool supported;
539
540 if (attr == &dev_attr_camera_power.attr)
541 supported = test_bit(CFG_CAMERA_BIT, &(priv->cfg));
0c7bbeb9
MM
542 else if (attr == &dev_attr_fan_mode.attr) {
543 unsigned long value;
331e0ea2
ZR
544 supported = !read_ec_data(priv->adev->handle, VPCCMD_R_FAN,
545 &value);
ade50296
HWT
546 } else if (attr == &dev_attr_conservation_mode.attr) {
547 supported = acpi_has_method(priv->adev->handle, "GBMD") &&
548 acpi_has_method(priv->adev->handle, "SBMC");
0c7bbeb9 549 } else
a84511f7
IP
550 supported = true;
551
552 return supported ? attr->mode : 0;
553}
554
49458e83 555static const struct attribute_group ideapad_attribute_group = {
a84511f7 556 .is_visible = ideapad_is_visible,
3371f481
IP
557 .attrs = ideapad_attributes
558};
559
a4b5a279
IP
560/*
561 * Rfkill
562 */
c1f73658
IP
563struct ideapad_rfk_data {
564 char *name;
565 int cfgbit;
566 int opcode;
567 int type;
568};
569
b3d94d70 570static const struct ideapad_rfk_data ideapad_rfk_data[] = {
2be1dc21
IP
571 { "ideapad_wlan", CFG_WIFI_BIT, VPCCMD_W_WIFI, RFKILL_TYPE_WLAN },
572 { "ideapad_bluetooth", CFG_BT_BIT, VPCCMD_W_BT, RFKILL_TYPE_BLUETOOTH },
573 { "ideapad_3g", CFG_3G_BIT, VPCCMD_W_3G, RFKILL_TYPE_WWAN },
c1f73658
IP
574};
575
58ac7aa0
DW
576static int ideapad_rfk_set(void *data, bool blocked)
577{
331e0ea2 578 struct ideapad_rfk_priv *priv = data;
4b200b46 579 int opcode = ideapad_rfk_data[priv->dev].opcode;
fa08359e 580
4b200b46 581 return write_ec_cmd(priv->priv->adev->handle, opcode, !blocked);
58ac7aa0
DW
582}
583
3d59dfcd 584static const struct rfkill_ops ideapad_rfk_ops = {
58ac7aa0
DW
585 .set_block = ideapad_rfk_set,
586};
587
923de84a 588static void ideapad_sync_rfk_state(struct ideapad_private *priv)
58ac7aa0 589{
ce363c2b 590 unsigned long hw_blocked = 0;
58ac7aa0
DW
591 int i;
592
ce363c2b
HG
593 if (priv->has_hw_rfkill_switch) {
594 if (read_ec_data(priv->adev->handle, VPCCMD_R_RF, &hw_blocked))
595 return;
596 hw_blocked = !hw_blocked;
597 }
58ac7aa0 598
c1f73658 599 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
ce326329 600 if (priv->rfk[i])
2b7266bd 601 rfkill_set_hw_state(priv->rfk[i], hw_blocked);
58ac7aa0
DW
602}
603
75a11f11 604static int ideapad_register_rfkill(struct ideapad_private *priv, int dev)
58ac7aa0
DW
605{
606 int ret;
2b7266bd 607 unsigned long sw_blocked;
58ac7aa0 608
bfa97b7d
IP
609 if (no_bt_rfkill &&
610 (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) {
611 /* Force to enable bluetooth when no_bt_rfkill=1 */
331e0ea2 612 write_ec_cmd(priv->adev->handle,
bfa97b7d
IP
613 ideapad_rfk_data[dev].opcode, 1);
614 return 0;
615 }
331e0ea2
ZR
616 priv->rfk_priv[dev].dev = dev;
617 priv->rfk_priv[dev].priv = priv;
bfa97b7d 618
75a11f11 619 priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name,
b5c37b79 620 &priv->platform_device->dev,
75a11f11
ZR
621 ideapad_rfk_data[dev].type,
622 &ideapad_rfk_ops,
331e0ea2 623 &priv->rfk_priv[dev]);
ce326329 624 if (!priv->rfk[dev])
58ac7aa0
DW
625 return -ENOMEM;
626
331e0ea2 627 if (read_ec_data(priv->adev->handle, ideapad_rfk_data[dev].opcode-1,
2b7266bd
IP
628 &sw_blocked)) {
629 rfkill_init_sw_state(priv->rfk[dev], 0);
630 } else {
631 sw_blocked = !sw_blocked;
632 rfkill_init_sw_state(priv->rfk[dev], sw_blocked);
633 }
634
ce326329 635 ret = rfkill_register(priv->rfk[dev]);
58ac7aa0 636 if (ret) {
ce326329 637 rfkill_destroy(priv->rfk[dev]);
58ac7aa0
DW
638 return ret;
639 }
640 return 0;
641}
642
75a11f11 643static void ideapad_unregister_rfkill(struct ideapad_private *priv, int dev)
58ac7aa0 644{
ce326329 645 if (!priv->rfk[dev])
58ac7aa0
DW
646 return;
647
ce326329
DW
648 rfkill_unregister(priv->rfk[dev]);
649 rfkill_destroy(priv->rfk[dev]);
58ac7aa0
DW
650}
651
98ee6919
IP
652/*
653 * Platform device
654 */
b5c37b79 655static int ideapad_sysfs_init(struct ideapad_private *priv)
98ee6919 656{
b5c37b79 657 return sysfs_create_group(&priv->platform_device->dev.kobj,
c9f718d0 658 &ideapad_attribute_group);
98ee6919
IP
659}
660
b5c37b79 661static void ideapad_sysfs_exit(struct ideapad_private *priv)
98ee6919 662{
8693ae84 663 sysfs_remove_group(&priv->platform_device->dev.kobj,
c9f718d0 664 &ideapad_attribute_group);
98ee6919 665}
98ee6919 666
f63409ae
IP
667/*
668 * input device
669 */
670static const struct key_entry ideapad_keymap[] = {
f43d9ec0 671 { KE_KEY, 6, { KEY_SWITCHVIDEOMODE } },
296f9fe0 672 { KE_KEY, 7, { KEY_CAMERA } },
48f67d62 673 { KE_KEY, 8, { KEY_MICMUTE } },
296f9fe0 674 { KE_KEY, 11, { KEY_F16 } },
f43d9ec0
IP
675 { KE_KEY, 13, { KEY_WLAN } },
676 { KE_KEY, 16, { KEY_PROG1 } },
677 { KE_KEY, 17, { KEY_PROG2 } },
296f9fe0
MM
678 { KE_KEY, 64, { KEY_PROG3 } },
679 { KE_KEY, 65, { KEY_PROG4 } },
07a4a4fc
MM
680 { KE_KEY, 66, { KEY_TOUCHPAD_OFF } },
681 { KE_KEY, 67, { KEY_TOUCHPAD_ON } },
74caab99
AB
682 { KE_KEY, 128, { KEY_ESC } },
683
f63409ae
IP
684 { KE_END, 0 },
685};
686
b859f159 687static int ideapad_input_init(struct ideapad_private *priv)
f63409ae
IP
688{
689 struct input_dev *inputdev;
690 int error;
691
692 inputdev = input_allocate_device();
b222cca6 693 if (!inputdev)
f63409ae 694 return -ENOMEM;
f63409ae
IP
695
696 inputdev->name = "Ideapad extra buttons";
697 inputdev->phys = "ideapad/input0";
698 inputdev->id.bustype = BUS_HOST;
8693ae84 699 inputdev->dev.parent = &priv->platform_device->dev;
f63409ae
IP
700
701 error = sparse_keymap_setup(inputdev, ideapad_keymap, NULL);
702 if (error) {
703 pr_err("Unable to setup input device keymap\n");
704 goto err_free_dev;
705 }
706
707 error = input_register_device(inputdev);
708 if (error) {
709 pr_err("Unable to register input device\n");
c973d4b5 710 goto err_free_dev;
f63409ae
IP
711 }
712
8693ae84 713 priv->inputdev = inputdev;
f63409ae
IP
714 return 0;
715
f63409ae
IP
716err_free_dev:
717 input_free_device(inputdev);
718 return error;
719}
720
7451a55a 721static void ideapad_input_exit(struct ideapad_private *priv)
f63409ae 722{
8693ae84
IP
723 input_unregister_device(priv->inputdev);
724 priv->inputdev = NULL;
f63409ae
IP
725}
726
8693ae84
IP
727static void ideapad_input_report(struct ideapad_private *priv,
728 unsigned long scancode)
f63409ae 729{
8693ae84 730 sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
f63409ae 731}
f63409ae 732
f43d9ec0
IP
733static void ideapad_input_novokey(struct ideapad_private *priv)
734{
735 unsigned long long_pressed;
736
331e0ea2 737 if (read_ec_data(priv->adev->handle, VPCCMD_R_NOVO, &long_pressed))
f43d9ec0
IP
738 return;
739 if (long_pressed)
740 ideapad_input_report(priv, 17);
741 else
742 ideapad_input_report(priv, 16);
743}
744
296f9fe0
MM
745static void ideapad_check_special_buttons(struct ideapad_private *priv)
746{
747 unsigned long bit, value;
748
331e0ea2 749 read_ec_data(priv->adev->handle, VPCCMD_R_SPECIAL_BUTTONS, &value);
296f9fe0
MM
750
751 for (bit = 0; bit < 16; bit++) {
752 if (test_bit(bit, &value)) {
753 switch (bit) {
a1ec56ed
MM
754 case 0: /* Z580 */
755 case 6: /* Z570 */
296f9fe0
MM
756 /* Thermal Management button */
757 ideapad_input_report(priv, 65);
758 break;
759 case 1:
760 /* OneKey Theater button */
761 ideapad_input_report(priv, 64);
762 break;
a1ec56ed
MM
763 default:
764 pr_info("Unknown special button: %lu\n", bit);
765 break;
296f9fe0
MM
766 }
767 }
768 }
769}
770
a4ecbb8a
IP
771/*
772 * backlight
773 */
774static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)
775{
331e0ea2 776 struct ideapad_private *priv = bl_get_data(blightdev);
a4ecbb8a
IP
777 unsigned long now;
778
331e0ea2
ZR
779 if (!priv)
780 return -EINVAL;
781
782 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now))
a4ecbb8a
IP
783 return -EIO;
784 return now;
785}
786
787static int ideapad_backlight_update_status(struct backlight_device *blightdev)
788{
331e0ea2
ZR
789 struct ideapad_private *priv = bl_get_data(blightdev);
790
791 if (!priv)
792 return -EINVAL;
793
794 if (write_ec_cmd(priv->adev->handle, VPCCMD_W_BL,
2be1dc21 795 blightdev->props.brightness))
a4ecbb8a 796 return -EIO;
331e0ea2 797 if (write_ec_cmd(priv->adev->handle, VPCCMD_W_BL_POWER,
a4ecbb8a
IP
798 blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1))
799 return -EIO;
800
801 return 0;
802}
803
804static const struct backlight_ops ideapad_backlight_ops = {
805 .get_brightness = ideapad_backlight_get_brightness,
806 .update_status = ideapad_backlight_update_status,
807};
808
809static int ideapad_backlight_init(struct ideapad_private *priv)
810{
811 struct backlight_device *blightdev;
812 struct backlight_properties props;
813 unsigned long max, now, power;
814
331e0ea2 815 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &max))
a4ecbb8a 816 return -EIO;
331e0ea2 817 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now))
a4ecbb8a 818 return -EIO;
331e0ea2 819 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))
a4ecbb8a
IP
820 return -EIO;
821
822 memset(&props, 0, sizeof(struct backlight_properties));
823 props.max_brightness = max;
824 props.type = BACKLIGHT_PLATFORM;
825 blightdev = backlight_device_register("ideapad",
826 &priv->platform_device->dev,
827 priv,
828 &ideapad_backlight_ops,
829 &props);
830 if (IS_ERR(blightdev)) {
831 pr_err("Could not register backlight device\n");
832 return PTR_ERR(blightdev);
833 }
834
835 priv->blightdev = blightdev;
836 blightdev->props.brightness = now;
837 blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
838 backlight_update_status(blightdev);
839
840 return 0;
841}
842
843static void ideapad_backlight_exit(struct ideapad_private *priv)
844{
00981810 845 backlight_device_unregister(priv->blightdev);
a4ecbb8a
IP
846 priv->blightdev = NULL;
847}
848
849static void ideapad_backlight_notify_power(struct ideapad_private *priv)
850{
851 unsigned long power;
852 struct backlight_device *blightdev = priv->blightdev;
853
d4afc775
RB
854 if (!blightdev)
855 return;
331e0ea2 856 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))
a4ecbb8a
IP
857 return;
858 blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
859}
860
861static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
862{
863 unsigned long now;
864
865 /* if we control brightness via acpi video driver */
866 if (priv->blightdev == NULL) {
331e0ea2 867 read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now);
a4ecbb8a
IP
868 return;
869 }
870
871 backlight_force_update(priv->blightdev, BACKLIGHT_UPDATE_HOTKEY);
872}
873
a4b5a279
IP
874/*
875 * module init/exit
876 */
75a11f11 877static void ideapad_sync_touchpad_state(struct ideapad_private *priv)
07a4a4fc 878{
07a4a4fc
MM
879 unsigned long value;
880
881 /* Without reading from EC touchpad LED doesn't switch state */
75a11f11 882 if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value)) {
07a4a4fc
MM
883 /* Some IdeaPads don't really turn off touchpad - they only
884 * switch the LED state. We (de)activate KBC AUX port to turn
885 * touchpad off and on. We send KEY_TOUCHPAD_OFF and
886 * KEY_TOUCHPAD_ON to not to get out of sync with LED */
887 unsigned char param;
888 i8042_command(&param, value ? I8042_CMD_AUX_ENABLE :
889 I8042_CMD_AUX_DISABLE);
890 ideapad_input_report(priv, value ? 67 : 66);
891 }
892}
893
b5c37b79
ZR
894static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)
895{
896 struct ideapad_private *priv = data;
897 unsigned long vpc1, vpc2, vpc_bit;
898
899 if (read_ec_data(handle, VPCCMD_R_VPC1, &vpc1))
900 return;
901 if (read_ec_data(handle, VPCCMD_R_VPC2, &vpc2))
902 return;
903
904 vpc1 = (vpc2 << 8) | vpc1;
905 for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) {
906 if (test_bit(vpc_bit, &vpc1)) {
907 switch (vpc_bit) {
908 case 9:
909 ideapad_sync_rfk_state(priv);
910 break;
911 case 13:
912 case 11:
48f67d62 913 case 8:
b5c37b79
ZR
914 case 7:
915 case 6:
916 ideapad_input_report(priv, vpc_bit);
917 break;
918 case 5:
919 ideapad_sync_touchpad_state(priv);
920 break;
921 case 4:
922 ideapad_backlight_notify_brightness(priv);
923 break;
924 case 3:
925 ideapad_input_novokey(priv);
926 break;
927 case 2:
928 ideapad_backlight_notify_power(priv);
929 break;
930 case 0:
931 ideapad_check_special_buttons(priv);
932 break;
3cfd956b
HWT
933 case 1:
934 /* Some IdeaPads report event 1 every ~20
935 * seconds while on battery power; some
936 * report this when changing to/from tablet
937 * mode. Squelch this event.
938 */
939 break;
b5c37b79
ZR
940 default:
941 pr_info("Unknown event: %lu\n", vpc_bit);
942 }
943 }
944 }
945}
946
74caab99
AB
947#if IS_ENABLED(CONFIG_ACPI_WMI)
948static void ideapad_wmi_notify(u32 value, void *context)
949{
950 switch (value) {
951 case 128:
952 ideapad_input_report(context, value);
953 break;
954 default:
955 pr_info("Unknown WMI event %u\n", value);
956 }
957}
958#endif
959
ce363c2b
HG
960/*
961 * Some ideapads don't have a hardware rfkill switch, reading VPCCMD_R_RF
962 * always results in 0 on these models, causing ideapad_laptop to wrongly
963 * report all radios as hardware-blocked.
964 */
b3d94d70 965static const struct dmi_system_id no_hw_rfkill_list[] = {
9b071a43
PC
966 {
967 .ident = "Lenovo G40-30",
968 .matches = {
969 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
970 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo G40-30"),
971 },
972 },
4fa9dabc
DT
973 {
974 .ident = "Lenovo G50-30",
975 .matches = {
976 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
977 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo G50-30"),
978 },
979 },
710c059c
YJ
980 {
981 .ident = "Lenovo V310-14IKB",
982 .matches = {
983 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
984 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V310-14IKB"),
985 },
986 },
987 {
988 .ident = "Lenovo V310-14ISK",
989 .matches = {
990 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
991 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V310-14ISK"),
992 },
993 },
994 {
995 .ident = "Lenovo V310-15IKB",
996 .matches = {
997 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
998 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V310-15IKB"),
999 },
1000 },
ccc7179f
AS
1001 {
1002 .ident = "Lenovo V310-15ISK",
1003 .matches = {
5e8f42aa
AS
1004 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1005 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V310-15ISK"),
ccc7179f
AS
1006 },
1007 },
0df4b805
SE
1008 {
1009 .ident = "Lenovo V510-15IKB",
1010 .matches = {
1011 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1012 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V510-15IKB"),
1013 },
1014 },
710c059c
YJ
1015 {
1016 .ident = "Lenovo ideapad 300-15IBR",
1017 .matches = {
1018 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1019 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 300-15IBR"),
1020 },
1021 },
1022 {
1023 .ident = "Lenovo ideapad 300-15IKB",
1024 .matches = {
1025 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1026 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 300-15IKB"),
1027 },
1028 },
1029 {
1030 .ident = "Lenovo ideapad 300S-11IBR",
1031 .matches = {
1032 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1033 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 300S-11BR"),
1034 },
1035 },
1036 {
1037 .ident = "Lenovo ideapad 310-15ABR",
1038 .matches = {
1039 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1040 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 310-15ABR"),
1041 },
1042 },
1043 {
1044 .ident = "Lenovo ideapad 310-15IAP",
1045 .matches = {
1046 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1047 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 310-15IAP"),
1048 },
1049 },
1f3bc53d
SR
1050 {
1051 .ident = "Lenovo ideapad 310-15IKB",
1052 .matches = {
5e8f42aa
AS
1053 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1054 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 310-15IKB"),
1f3bc53d
SR
1055 },
1056 },
710c059c
YJ
1057 {
1058 .ident = "Lenovo ideapad 310-15ISK",
1059 .matches = {
1060 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1061 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 310-15ISK"),
1062 },
1063 },
1064 {
1065 .ident = "Lenovo ideapad Y700-14ISK",
1066 .matches = {
1067 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1068 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-14ISK"),
1069 },
1070 },
e2970468 1071 {
1072 .ident = "Lenovo ideapad Y700-15ACZ",
1073 .matches = {
1074 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1075 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-15ACZ"),
1076 },
1077 },
4db9675d
JD
1078 {
1079 .ident = "Lenovo ideapad Y700-15ISK",
1080 .matches = {
1081 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1082 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-15ISK"),
1083 },
1084 },
1085 {
1086 .ident = "Lenovo ideapad Y700 Touch-15ISK",
1087 .matches = {
1088 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1089 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700 Touch-15ISK"),
1090 },
1091 },
edde316a
JB
1092 {
1093 .ident = "Lenovo ideapad Y700-17ISK",
1094 .matches = {
1095 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1096 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-17ISK"),
1097 },
1098 },
5d9f40b5
OL
1099 {
1100 .ident = "Lenovo Legion Y520-15IKBN",
1101 .matches = {
1102 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1103 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Y520-15IKBN"),
1104 },
1105 },
b2f2fe20
OL
1106 {
1107 .ident = "Lenovo Legion Y720-15IKBN",
1108 .matches = {
1109 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1110 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Y720-15IKBN"),
1111 },
1112 },
85093f79 1113 {
ce363c2b 1114 .ident = "Lenovo Yoga 2 11 / 13 / Pro",
85093f79
HG
1115 .matches = {
1116 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
ce363c2b 1117 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 2"),
85093f79
HG
1118 },
1119 },
fa92a31b 1120 {
c789fffc 1121 .ident = "Lenovo Yoga 2 11 / 13 / Pro",
fa92a31b
HG
1122 .matches = {
1123 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
c789fffc 1124 DMI_MATCH(DMI_BOARD_NAME, "Yoga2"),
fa92a31b
HG
1125 },
1126 },
6d212b8a 1127 {
c789fffc 1128 .ident = "Lenovo Yoga 3 1170 / 1470",
6d212b8a
SK
1129 .matches = {
1130 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
c789fffc 1131 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 3"),
6d212b8a
SK
1132 },
1133 },
725c7f61
SM
1134 {
1135 .ident = "Lenovo Yoga 3 Pro 1370",
1136 .matches = {
1137 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
c789fffc 1138 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 3"),
725c7f61
SM
1139 },
1140 },
6b31de3e
JB
1141 {
1142 .ident = "Lenovo Yoga 700",
1143 .matches = {
1144 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1145 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 700"),
1146 },
1147 },
f71c882d
HG
1148 {
1149 .ident = "Lenovo Yoga 900",
1150 .matches = {
1151 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1152 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 900"),
1153 },
1154 },
446647d4
MW
1155 {
1156 .ident = "Lenovo Yoga 900",
1157 .matches = {
1158 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1159 DMI_MATCH(DMI_BOARD_NAME, "VIUU4"),
1160 },
1161 },
40c30bbf
BM
1162 {
1163 .ident = "Lenovo YOGA 910-13IKB",
1164 .matches = {
1165 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1166 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 910-13IKB"),
1167 },
1168 },
b231669c
PH
1169 {
1170 .ident = "Lenovo YOGA 920-13IKB",
1171 .matches = {
1172 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1173 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 920-13IKB"),
1174 },
1175 },
85093f79
HG
1176 {}
1177};
1178
b5c37b79 1179static int ideapad_acpi_add(struct platform_device *pdev)
58ac7aa0 1180{
3371f481 1181 int ret, i;
57f9616b 1182 int cfg;
ce326329 1183 struct ideapad_private *priv;
b5c37b79
ZR
1184 struct acpi_device *adev;
1185
1186 ret = acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev);
1187 if (ret)
1188 return -ENODEV;
58ac7aa0 1189
469f6434 1190 if (read_method_int(adev->handle, "_CFG", &cfg))
6f8371c0
IP
1191 return -ENODEV;
1192
b3facd7b 1193 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
ce326329
DW
1194 if (!priv)
1195 return -ENOMEM;
b5c37b79
ZR
1196
1197 dev_set_drvdata(&pdev->dev, priv);
3371f481 1198 priv->cfg = cfg;
469f6434 1199 priv->adev = adev;
b5c37b79 1200 priv->platform_device = pdev;
ce363c2b 1201 priv->has_hw_rfkill_switch = !dmi_check_system(no_hw_rfkill_list);
98ee6919 1202
b5c37b79 1203 ret = ideapad_sysfs_init(priv);
98ee6919 1204 if (ret)
b3facd7b 1205 return ret;
ce326329 1206
773e3206
IP
1207 ret = ideapad_debugfs_init(priv);
1208 if (ret)
1209 goto debugfs_failed;
1210
8693ae84 1211 ret = ideapad_input_init(priv);
f63409ae
IP
1212 if (ret)
1213 goto input_failed;
1214
ce363c2b
HG
1215 /*
1216 * On some models without a hw-switch (the yoga 2 13 at least)
1217 * VPCCMD_W_RF must be explicitly set to 1 for the wifi to work.
1218 */
1219 if (!priv->has_hw_rfkill_switch)
1220 write_ec_cmd(priv->adev->handle, VPCCMD_W_RF, 1);
1221
1222 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
1223 if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg))
1224 ideapad_register_rfkill(priv, i);
1225
923de84a 1226 ideapad_sync_rfk_state(priv);
75a11f11 1227 ideapad_sync_touchpad_state(priv);
c9f718d0 1228
26bff5f0 1229 if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
a4ecbb8a
IP
1230 ret = ideapad_backlight_init(priv);
1231 if (ret && ret != -ENODEV)
1232 goto backlight_failed;
1233 }
b5c37b79
ZR
1234 ret = acpi_install_notify_handler(adev->handle,
1235 ACPI_DEVICE_NOTIFY, ideapad_acpi_notify, priv);
1236 if (ret)
1237 goto notification_failed;
2d98e0b9 1238
74caab99 1239#if IS_ENABLED(CONFIG_ACPI_WMI)
2d98e0b9
AB
1240 for (i = 0; i < ARRAY_SIZE(ideapad_wmi_fnesc_events); i++) {
1241 ret = wmi_install_notify_handler(ideapad_wmi_fnesc_events[i],
1242 ideapad_wmi_notify, priv);
1243 if (ret == AE_OK) {
1244 priv->fnesc_guid = ideapad_wmi_fnesc_events[i];
1245 break;
1246 }
1247 }
74caab99
AB
1248 if (ret != AE_OK && ret != AE_NOT_EXIST)
1249 goto notification_failed_wmi;
1250#endif
a4ecbb8a 1251
58ac7aa0 1252 return 0;
74caab99
AB
1253#if IS_ENABLED(CONFIG_ACPI_WMI)
1254notification_failed_wmi:
1255 acpi_remove_notify_handler(priv->adev->handle,
1256 ACPI_DEVICE_NOTIFY, ideapad_acpi_notify);
1257#endif
b5c37b79
ZR
1258notification_failed:
1259 ideapad_backlight_exit(priv);
a4ecbb8a
IP
1260backlight_failed:
1261 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
75a11f11 1262 ideapad_unregister_rfkill(priv, i);
7451a55a 1263 ideapad_input_exit(priv);
f63409ae 1264input_failed:
773e3206
IP
1265 ideapad_debugfs_exit(priv);
1266debugfs_failed:
b5c37b79 1267 ideapad_sysfs_exit(priv);
98ee6919 1268 return ret;
58ac7aa0
DW
1269}
1270
b5c37b79 1271static int ideapad_acpi_remove(struct platform_device *pdev)
58ac7aa0 1272{
b5c37b79 1273 struct ideapad_private *priv = dev_get_drvdata(&pdev->dev);
58ac7aa0 1274 int i;
ce326329 1275
74caab99 1276#if IS_ENABLED(CONFIG_ACPI_WMI)
2d98e0b9
AB
1277 if (priv->fnesc_guid)
1278 wmi_remove_notify_handler(priv->fnesc_guid);
74caab99 1279#endif
b5c37b79
ZR
1280 acpi_remove_notify_handler(priv->adev->handle,
1281 ACPI_DEVICE_NOTIFY, ideapad_acpi_notify);
a4ecbb8a 1282 ideapad_backlight_exit(priv);
c1f73658 1283 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
75a11f11 1284 ideapad_unregister_rfkill(priv, i);
8693ae84 1285 ideapad_input_exit(priv);
773e3206 1286 ideapad_debugfs_exit(priv);
b5c37b79
ZR
1287 ideapad_sysfs_exit(priv);
1288 dev_set_drvdata(&pdev->dev, NULL);
c9f718d0 1289
58ac7aa0
DW
1290 return 0;
1291}
1292
11fa8da5 1293#ifdef CONFIG_PM_SLEEP
07a4a4fc
MM
1294static int ideapad_acpi_resume(struct device *device)
1295{
75a11f11
ZR
1296 struct ideapad_private *priv;
1297
1298 if (!device)
1299 return -EINVAL;
1300 priv = dev_get_drvdata(device);
1301
1302 ideapad_sync_rfk_state(priv);
1303 ideapad_sync_touchpad_state(priv);
07a4a4fc
MM
1304 return 0;
1305}
11fa8da5 1306#endif
b5c37b79 1307static SIMPLE_DEV_PM_OPS(ideapad_pm, NULL, ideapad_acpi_resume);
07a4a4fc 1308
b5c37b79
ZR
1309static const struct acpi_device_id ideapad_device_ids[] = {
1310 { "VPC2004", 0},
1311 { "", 0},
58ac7aa0 1312};
b5c37b79
ZR
1313MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
1314
1315static struct platform_driver ideapad_acpi_driver = {
1316 .probe = ideapad_acpi_add,
1317 .remove = ideapad_acpi_remove,
1318 .driver = {
1319 .name = "ideapad_acpi",
b5c37b79
ZR
1320 .pm = &ideapad_pm,
1321 .acpi_match_table = ACPI_PTR(ideapad_device_ids),
1322 },
1323};
1324
1325module_platform_driver(ideapad_acpi_driver);
58ac7aa0
DW
1326
1327MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
1328MODULE_DESCRIPTION("IdeaPad ACPI Extras");
1329MODULE_LICENSE("GPL");