platform/x86: ideapad-laptop: use dev_{err,warn} or appropriate variant to display...
[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>
7d38f034
BP
14#include <linux/debugfs.h>
15#include <linux/device.h>
16#include <linux/dmi.h>
17#include <linux/fb.h>
18#include <linux/i8042.h>
19#include <linux/init.h>
20#include <linux/input.h>
21#include <linux/input/sparse-keymap.h>
40e0447d 22#include <linux/jiffies.h>
58ac7aa0
DW
23#include <linux/kernel.h>
24#include <linux/module.h>
98ee6919 25#include <linux/platform_device.h>
eabe5339 26#include <linux/platform_profile.h>
7d38f034 27#include <linux/rfkill.h>
773e3206 28#include <linux/seq_file.h>
d6b50889 29#include <linux/sysfs.h>
7d38f034
BP
30#include <linux/types.h>
31
26bff5f0 32#include <acpi/video.h>
58ac7aa0 33
c1f73658 34#define IDEAPAD_RFKILL_DEV_NUM (3)
58ac7aa0 35
ade50296 36#define BM_CONSERVATION_BIT (5)
40760717 37#define HA_FNLOCK_BIT (10)
ade50296 38
3371f481
IP
39#define CFG_BT_BIT (16)
40#define CFG_3G_BIT (17)
41#define CFG_WIFI_BIT (18)
a84511f7 42#define CFG_CAMERA_BIT (19)
3371f481 43
74caab99 44#if IS_ENABLED(CONFIG_ACPI_WMI)
2d98e0b9
AB
45static const char *const ideapad_wmi_fnesc_events[] = {
46 "26CAB2E5-5CF1-46AE-AAC3-4A12B6BA50E6", /* Yoga 3 */
47 "56322276-8493-4CE8-A783-98C991274F5E", /* Yoga 700 */
48};
74caab99
AB
49#endif
50
ade50296
HWT
51enum {
52 BMCMD_CONSERVATION_ON = 3,
53 BMCMD_CONSERVATION_OFF = 5,
40760717
OK
54 HACMD_FNLOCK_ON = 0xe,
55 HACMD_FNLOCK_OFF = 0xf,
ade50296
HWT
56};
57
2be1dc21
IP
58enum {
59 VPCCMD_R_VPC1 = 0x10,
60 VPCCMD_R_BL_MAX,
61 VPCCMD_R_BL,
62 VPCCMD_W_BL,
63 VPCCMD_R_WIFI,
64 VPCCMD_W_WIFI,
65 VPCCMD_R_BT,
66 VPCCMD_W_BT,
67 VPCCMD_R_BL_POWER,
68 VPCCMD_R_NOVO,
69 VPCCMD_R_VPC2,
70 VPCCMD_R_TOUCHPAD,
71 VPCCMD_W_TOUCHPAD,
72 VPCCMD_R_CAMERA,
73 VPCCMD_W_CAMERA,
74 VPCCMD_R_3G,
75 VPCCMD_W_3G,
76 VPCCMD_R_ODD, /* 0x21 */
0c7bbeb9
MM
77 VPCCMD_W_FAN,
78 VPCCMD_R_RF,
2be1dc21 79 VPCCMD_W_RF,
0c7bbeb9 80 VPCCMD_R_FAN = 0x2B,
296f9fe0 81 VPCCMD_R_SPECIAL_BUTTONS = 0x31,
2be1dc21
IP
82 VPCCMD_W_BL_POWER = 0x33,
83};
84
eabe5339
JY
85struct ideapad_dytc_priv {
86 enum platform_profile_option current_profile;
87 struct platform_profile_handler pprof;
88 struct mutex mutex;
89 struct ideapad_private *priv;
90};
91
331e0ea2
ZR
92struct ideapad_rfk_priv {
93 int dev;
94 struct ideapad_private *priv;
95};
96
ce326329 97struct ideapad_private {
469f6434 98 struct acpi_device *adev;
c1f73658 99 struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
331e0ea2 100 struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM];
98ee6919 101 struct platform_device *platform_device;
f63409ae 102 struct input_dev *inputdev;
a4ecbb8a 103 struct backlight_device *blightdev;
eabe5339 104 struct ideapad_dytc_priv *dytc;
773e3206 105 struct dentry *debug;
3371f481 106 unsigned long cfg;
ce363c2b 107 bool has_hw_rfkill_switch;
d69cd7ee 108 bool has_touchpad_switch;
2d98e0b9 109 const char *fnesc_guid;
58ac7aa0
DW
110};
111
bfa97b7d
IP
112static bool no_bt_rfkill;
113module_param(no_bt_rfkill, bool, 0444);
114MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
115
6a09f21d
IP
116/*
117 * ACPI Helpers
118 */
ed5b9ba7 119#define IDEAPAD_EC_TIMEOUT (200) /* in ms */
6a09f21d
IP
120
121static int read_method_int(acpi_handle handle, const char *method, int *val)
122{
123 acpi_status status;
124 unsigned long long result;
125
126 status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
127 if (ACPI_FAILURE(status)) {
128 *val = -1;
129 return -1;
6a09f21d 130 }
ba3a3387
JY
131 *val = result;
132 return 0;
133
6a09f21d
IP
134}
135
ade50296
HWT
136static int method_gbmd(acpi_handle handle, unsigned long *ret)
137{
138 int result, val;
139
140 result = read_method_int(handle, "GBMD", &val);
141 *ret = val;
142 return result;
143}
144
40760717 145static int method_int1(acpi_handle handle, char *method, int cmd)
ade50296
HWT
146{
147 acpi_status status;
148
40760717 149 status = acpi_execute_simple_method(handle, method, cmd);
ade50296
HWT
150 return ACPI_FAILURE(status) ? -1 : 0;
151}
152
eabe5339
JY
153static int method_dytc(acpi_handle handle, int cmd, int *ret)
154{
155 acpi_status status;
156 unsigned long long result;
157 struct acpi_object_list params;
158 union acpi_object in_obj;
159
160 params.count = 1;
161 params.pointer = &in_obj;
162 in_obj.type = ACPI_TYPE_INTEGER;
163 in_obj.integer.value = cmd;
164
165 status = acpi_evaluate_integer(handle, "DYTC", &params, &result);
166
167 if (ACPI_FAILURE(status)) {
168 *ret = -1;
169 return -1;
170 }
171 *ret = result;
172 return 0;
173}
174
6a09f21d
IP
175static int method_vpcr(acpi_handle handle, int cmd, int *ret)
176{
177 acpi_status status;
178 unsigned long long result;
179 struct acpi_object_list params;
180 union acpi_object in_obj;
181
182 params.count = 1;
183 params.pointer = &in_obj;
184 in_obj.type = ACPI_TYPE_INTEGER;
185 in_obj.integer.value = cmd;
186
187 status = acpi_evaluate_integer(handle, "VPCR", &params, &result);
188
189 if (ACPI_FAILURE(status)) {
190 *ret = -1;
191 return -1;
6a09f21d 192 }
ba3a3387
JY
193 *ret = result;
194 return 0;
195
6a09f21d
IP
196}
197
198static int method_vpcw(acpi_handle handle, int cmd, int data)
199{
200 struct acpi_object_list params;
201 union acpi_object in_obj[2];
202 acpi_status status;
203
204 params.count = 2;
205 params.pointer = in_obj;
206 in_obj[0].type = ACPI_TYPE_INTEGER;
207 in_obj[0].integer.value = cmd;
208 in_obj[1].type = ACPI_TYPE_INTEGER;
209 in_obj[1].integer.value = data;
210
211 status = acpi_evaluate_object(handle, "VPCW", &params, NULL);
212 if (status != AE_OK)
213 return -1;
214 return 0;
215}
216
217static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data)
218{
219 int val;
220 unsigned long int end_jiffies;
221
222 if (method_vpcw(handle, 1, cmd))
223 return -1;
224
40e0447d
BP
225 end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1;
226
227 while (time_before(jiffies, end_jiffies)) {
6a09f21d
IP
228 schedule();
229 if (method_vpcr(handle, 1, &val))
230 return -1;
231 if (val == 0) {
232 if (method_vpcr(handle, 0, &val))
233 return -1;
234 *data = val;
235 return 0;
236 }
237 }
654324c4 238 acpi_handle_err(handle, "timeout in %s\n", __func__);
6a09f21d
IP
239 return -1;
240}
241
242static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
243{
244 int val;
245 unsigned long int end_jiffies;
246
247 if (method_vpcw(handle, 0, data))
248 return -1;
249 if (method_vpcw(handle, 1, cmd))
250 return -1;
251
40e0447d
BP
252 end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1;
253
254 while (time_before(jiffies, end_jiffies)) {
6a09f21d
IP
255 schedule();
256 if (method_vpcr(handle, 1, &val))
257 return -1;
258 if (val == 0)
259 return 0;
260 }
654324c4 261 acpi_handle_err(handle, "timeout in %s\n", __func__);
6a09f21d
IP
262 return -1;
263}
6a09f21d 264
773e3206
IP
265/*
266 * debugfs
267 */
773e3206
IP
268static int debugfs_status_show(struct seq_file *s, void *data)
269{
331e0ea2 270 struct ideapad_private *priv = s->private;
773e3206
IP
271 unsigned long value;
272
331e0ea2 273 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &value))
773e3206 274 seq_printf(s, "Backlight max:\t%lu\n", value);
331e0ea2 275 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL, &value))
773e3206 276 seq_printf(s, "Backlight now:\t%lu\n", value);
331e0ea2 277 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &value))
773e3206
IP
278 seq_printf(s, "BL power value:\t%s\n", value ? "On" : "Off");
279 seq_printf(s, "=====================\n");
280
331e0ea2 281 if (!read_ec_data(priv->adev->handle, VPCCMD_R_RF, &value))
773e3206
IP
282 seq_printf(s, "Radio status:\t%s(%lu)\n",
283 value ? "On" : "Off", value);
331e0ea2 284 if (!read_ec_data(priv->adev->handle, VPCCMD_R_WIFI, &value))
773e3206
IP
285 seq_printf(s, "Wifi status:\t%s(%lu)\n",
286 value ? "On" : "Off", value);
331e0ea2 287 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BT, &value))
773e3206
IP
288 seq_printf(s, "BT status:\t%s(%lu)\n",
289 value ? "On" : "Off", value);
331e0ea2 290 if (!read_ec_data(priv->adev->handle, VPCCMD_R_3G, &value))
773e3206
IP
291 seq_printf(s, "3G status:\t%s(%lu)\n",
292 value ? "On" : "Off", value);
293 seq_printf(s, "=====================\n");
294
331e0ea2 295 if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value))
773e3206
IP
296 seq_printf(s, "Touchpad status:%s(%lu)\n",
297 value ? "On" : "Off", value);
331e0ea2 298 if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value))
773e3206
IP
299 seq_printf(s, "Camera status:\t%s(%lu)\n",
300 value ? "On" : "Off", value);
ade50296
HWT
301 seq_puts(s, "=====================\n");
302
303 if (!method_gbmd(priv->adev->handle, &value)) {
304 seq_printf(s, "Conservation mode:\t%s(%lu)\n",
305 test_bit(BM_CONSERVATION_BIT, &value) ? "On" : "Off",
306 value);
307 }
773e3206
IP
308
309 return 0;
310}
334c4efd 311DEFINE_SHOW_ATTRIBUTE(debugfs_status);
773e3206
IP
312
313static int debugfs_cfg_show(struct seq_file *s, void *data)
314{
331e0ea2
ZR
315 struct ideapad_private *priv = s->private;
316
e1a39a44
BP
317 seq_printf(s, "cfg: 0x%.8lX\n\nCapability: ",
318 priv->cfg);
319 if (test_bit(CFG_BT_BIT, &priv->cfg))
320 seq_printf(s, "Bluetooth ");
321 if (test_bit(CFG_3G_BIT, &priv->cfg))
322 seq_printf(s, "3G ");
323 if (test_bit(CFG_WIFI_BIT, &priv->cfg))
324 seq_printf(s, "Wireless ");
325 if (test_bit(CFG_CAMERA_BIT, &priv->cfg))
326 seq_printf(s, "Camera ");
327 seq_printf(s, "\nGraphic: ");
328 switch ((priv->cfg)&0x700) {
329 case 0x100:
330 seq_printf(s, "Intel");
331 break;
332 case 0x200:
333 seq_printf(s, "ATI");
334 break;
335 case 0x300:
336 seq_printf(s, "Nvidia");
337 break;
338 case 0x400:
339 seq_printf(s, "Intel and ATI");
340 break;
341 case 0x500:
342 seq_printf(s, "Intel and Nvidia");
343 break;
773e3206 344 }
e1a39a44
BP
345 seq_printf(s, "\n");
346
773e3206
IP
347 return 0;
348}
334c4efd 349DEFINE_SHOW_ATTRIBUTE(debugfs_cfg);
773e3206 350
17f1bf38 351static void ideapad_debugfs_init(struct ideapad_private *priv)
773e3206 352{
17f1bf38 353 struct dentry *dir;
773e3206 354
17f1bf38
GKH
355 dir = debugfs_create_dir("ideapad", NULL);
356 priv->debug = dir;
773e3206 357
17f1bf38
GKH
358 debugfs_create_file("cfg", S_IRUGO, dir, priv, &debugfs_cfg_fops);
359 debugfs_create_file("status", S_IRUGO, dir, priv, &debugfs_status_fops);
773e3206
IP
360}
361
362static void ideapad_debugfs_exit(struct ideapad_private *priv)
363{
364 debugfs_remove_recursive(priv->debug);
365 priv->debug = NULL;
366}
367
a4b5a279 368/*
3371f481 369 * sysfs
a4b5a279 370 */
58ac7aa0
DW
371static ssize_t show_ideapad_cam(struct device *dev,
372 struct device_attribute *attr,
373 char *buf)
374{
26c81d5c 375 unsigned long result;
331e0ea2 376 struct ideapad_private *priv = dev_get_drvdata(dev);
58ac7aa0 377
331e0ea2 378 if (read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &result))
d6b50889
BP
379 return sysfs_emit(buf, "-1\n");
380 return sysfs_emit(buf, "%lu\n", result);
58ac7aa0
DW
381}
382
383static ssize_t store_ideapad_cam(struct device *dev,
384 struct device_attribute *attr,
385 const char *buf, size_t count)
386{
387 int ret, state;
331e0ea2 388 struct ideapad_private *priv = dev_get_drvdata(dev);
58ac7aa0
DW
389
390 if (!count)
391 return 0;
392 if (sscanf(buf, "%i", &state) != 1)
393 return -EINVAL;
331e0ea2 394 ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_CAMERA, state);
58ac7aa0 395 if (ret < 0)
0c7bbeb9 396 return -EIO;
58ac7aa0
DW
397 return count;
398}
399
400static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
401
0c7bbeb9
MM
402static ssize_t show_ideapad_fan(struct device *dev,
403 struct device_attribute *attr,
404 char *buf)
405{
406 unsigned long result;
331e0ea2 407 struct ideapad_private *priv = dev_get_drvdata(dev);
0c7bbeb9 408
331e0ea2 409 if (read_ec_data(priv->adev->handle, VPCCMD_R_FAN, &result))
d6b50889
BP
410 return sysfs_emit(buf, "-1\n");
411 return sysfs_emit(buf, "%lu\n", result);
0c7bbeb9
MM
412}
413
414static ssize_t store_ideapad_fan(struct device *dev,
415 struct device_attribute *attr,
416 const char *buf, size_t count)
417{
418 int ret, state;
331e0ea2 419 struct ideapad_private *priv = dev_get_drvdata(dev);
0c7bbeb9
MM
420
421 if (!count)
422 return 0;
423 if (sscanf(buf, "%i", &state) != 1)
424 return -EINVAL;
425 if (state < 0 || state > 4 || state == 3)
426 return -EINVAL;
331e0ea2 427 ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_FAN, state);
0c7bbeb9
MM
428 if (ret < 0)
429 return -EIO;
430 return count;
431}
432
433static DEVICE_ATTR(fan_mode, 0644, show_ideapad_fan, store_ideapad_fan);
434
36ac0d43
RRS
435static ssize_t touchpad_show(struct device *dev,
436 struct device_attribute *attr,
437 char *buf)
438{
439 struct ideapad_private *priv = dev_get_drvdata(dev);
440 unsigned long result;
441
442 if (read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &result))
d6b50889
BP
443 return sysfs_emit(buf, "-1\n");
444 return sysfs_emit(buf, "%lu\n", result);
36ac0d43
RRS
445}
446
46936fd6
AB
447/* Switch to RO for now: It might be revisited in the future */
448static ssize_t __maybe_unused touchpad_store(struct device *dev,
449 struct device_attribute *attr,
450 const char *buf, size_t count)
36ac0d43
RRS
451{
452 struct ideapad_private *priv = dev_get_drvdata(dev);
453 bool state;
454 int ret;
455
456 ret = kstrtobool(buf, &state);
457 if (ret)
458 return ret;
459
460 ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, state);
461 if (ret < 0)
462 return -EIO;
463 return count;
464}
465
7f363145 466static DEVICE_ATTR_RO(touchpad);
36ac0d43 467
ade50296
HWT
468static ssize_t conservation_mode_show(struct device *dev,
469 struct device_attribute *attr,
470 char *buf)
471{
472 struct ideapad_private *priv = dev_get_drvdata(dev);
473 unsigned long result;
474
475 if (method_gbmd(priv->adev->handle, &result))
d6b50889
BP
476 return sysfs_emit(buf, "-1\n");
477 return sysfs_emit(buf, "%u\n", test_bit(BM_CONSERVATION_BIT, &result));
ade50296
HWT
478}
479
480static ssize_t conservation_mode_store(struct device *dev,
481 struct device_attribute *attr,
482 const char *buf, size_t count)
483{
484 struct ideapad_private *priv = dev_get_drvdata(dev);
485 bool state;
486 int ret;
487
488 ret = kstrtobool(buf, &state);
489 if (ret)
490 return ret;
491
40760717 492 ret = method_int1(priv->adev->handle, "SBMC", state ?
ade50296
HWT
493 BMCMD_CONSERVATION_ON :
494 BMCMD_CONSERVATION_OFF);
495 if (ret < 0)
496 return -EIO;
497 return count;
498}
499
500static DEVICE_ATTR_RW(conservation_mode);
501
40760717
OK
502static ssize_t fn_lock_show(struct device *dev,
503 struct device_attribute *attr,
504 char *buf)
505{
506 struct ideapad_private *priv = dev_get_drvdata(dev);
507 unsigned long result;
508 int hals;
509 int fail = read_method_int(priv->adev->handle, "HALS", &hals);
510
511 if (fail)
d6b50889 512 return sysfs_emit(buf, "-1\n");
40760717
OK
513
514 result = hals;
d6b50889 515 return sysfs_emit(buf, "%u\n", test_bit(HA_FNLOCK_BIT, &result));
40760717
OK
516}
517
518static ssize_t fn_lock_store(struct device *dev,
519 struct device_attribute *attr,
520 const char *buf, size_t count)
521{
522 struct ideapad_private *priv = dev_get_drvdata(dev);
523 bool state;
524 int ret;
525
526 ret = kstrtobool(buf, &state);
527 if (ret)
528 return ret;
529
530 ret = method_int1(priv->adev->handle, "SALS", state ?
531 HACMD_FNLOCK_ON :
532 HACMD_FNLOCK_OFF);
533 if (ret < 0)
534 return -EIO;
535 return count;
536}
537
538static DEVICE_ATTR_RW(fn_lock);
539
540
3371f481
IP
541static struct attribute *ideapad_attributes[] = {
542 &dev_attr_camera_power.attr,
0c7bbeb9 543 &dev_attr_fan_mode.attr,
36ac0d43 544 &dev_attr_touchpad.attr,
ade50296 545 &dev_attr_conservation_mode.attr,
40760717 546 &dev_attr_fn_lock.attr,
3371f481
IP
547 NULL
548};
549
587a1f16 550static umode_t ideapad_is_visible(struct kobject *kobj,
a84511f7
IP
551 struct attribute *attr,
552 int idx)
553{
708086b2 554 struct device *dev = kobj_to_dev(kobj);
a84511f7
IP
555 struct ideapad_private *priv = dev_get_drvdata(dev);
556 bool supported;
557
558 if (attr == &dev_attr_camera_power.attr)
559 supported = test_bit(CFG_CAMERA_BIT, &(priv->cfg));
0c7bbeb9
MM
560 else if (attr == &dev_attr_fan_mode.attr) {
561 unsigned long value;
331e0ea2
ZR
562 supported = !read_ec_data(priv->adev->handle, VPCCMD_R_FAN,
563 &value);
ade50296
HWT
564 } else if (attr == &dev_attr_conservation_mode.attr) {
565 supported = acpi_has_method(priv->adev->handle, "GBMD") &&
566 acpi_has_method(priv->adev->handle, "SBMC");
40760717
OK
567 } else if (attr == &dev_attr_fn_lock.attr) {
568 supported = acpi_has_method(priv->adev->handle, "HALS") &&
569 acpi_has_method(priv->adev->handle, "SALS");
d69cd7ee
JY
570 } else if (attr == &dev_attr_touchpad.attr)
571 supported = priv->has_touchpad_switch;
572 else
a84511f7
IP
573 supported = true;
574
575 return supported ? attr->mode : 0;
576}
577
49458e83 578static const struct attribute_group ideapad_attribute_group = {
a84511f7 579 .is_visible = ideapad_is_visible,
3371f481
IP
580 .attrs = ideapad_attributes
581};
582
eabe5339
JY
583/*
584 * DYTC Platform profile
585 */
586#define DYTC_CMD_QUERY 0 /* To get DYTC status - enable/revision */
587#define DYTC_CMD_SET 1 /* To enable/disable IC function mode */
588#define DYTC_CMD_GET 2 /* To get current IC function and mode */
589#define DYTC_CMD_RESET 0x1ff /* To reset back to default */
590
591#define DYTC_QUERY_ENABLE_BIT 8 /* Bit 8 - 0 = disabled, 1 = enabled */
592#define DYTC_QUERY_SUBREV_BIT 16 /* Bits 16 - 27 - sub revision */
593#define DYTC_QUERY_REV_BIT 28 /* Bits 28 - 31 - revision */
594
595#define DYTC_GET_FUNCTION_BIT 8 /* Bits 8-11 - function setting */
596#define DYTC_GET_MODE_BIT 12 /* Bits 12-15 - mode setting */
597
598#define DYTC_SET_FUNCTION_BIT 12 /* Bits 12-15 - function setting */
599#define DYTC_SET_MODE_BIT 16 /* Bits 16-19 - mode setting */
600#define DYTC_SET_VALID_BIT 20 /* Bit 20 - 1 = on, 0 = off */
601
602#define DYTC_FUNCTION_STD 0 /* Function = 0, standard mode */
603#define DYTC_FUNCTION_CQL 1 /* Function = 1, lap mode */
604#define DYTC_FUNCTION_MMC 11 /* Function = 11, desk mode */
605
606#define DYTC_MODE_PERFORM 2 /* High power mode aka performance */
607#define DYTC_MODE_LOW_POWER 3 /* Low power mode aka quiet */
608#define DYTC_MODE_BALANCE 0xF /* Default mode aka balanced */
609
610#define DYTC_SET_COMMAND(function, mode, on) \
611 (DYTC_CMD_SET | (function) << DYTC_SET_FUNCTION_BIT | \
612 (mode) << DYTC_SET_MODE_BIT | \
613 (on) << DYTC_SET_VALID_BIT)
614
615#define DYTC_DISABLE_CQL DYTC_SET_COMMAND(DYTC_FUNCTION_CQL, DYTC_MODE_BALANCE, 0)
616
617#define DYTC_ENABLE_CQL DYTC_SET_COMMAND(DYTC_FUNCTION_CQL, DYTC_MODE_BALANCE, 1)
618
619static int convert_dytc_to_profile(int dytcmode, enum platform_profile_option *profile)
620{
621 switch (dytcmode) {
622 case DYTC_MODE_LOW_POWER:
623 *profile = PLATFORM_PROFILE_LOW_POWER;
624 break;
625 case DYTC_MODE_BALANCE:
626 *profile = PLATFORM_PROFILE_BALANCED;
627 break;
628 case DYTC_MODE_PERFORM:
629 *profile = PLATFORM_PROFILE_PERFORMANCE;
630 break;
631 default: /* Unknown mode */
632 return -EINVAL;
633 }
634 return 0;
635}
636
637static int convert_profile_to_dytc(enum platform_profile_option profile, int *perfmode)
638{
639 switch (profile) {
640 case PLATFORM_PROFILE_LOW_POWER:
641 *perfmode = DYTC_MODE_LOW_POWER;
642 break;
643 case PLATFORM_PROFILE_BALANCED:
644 *perfmode = DYTC_MODE_BALANCE;
645 break;
646 case PLATFORM_PROFILE_PERFORMANCE:
647 *perfmode = DYTC_MODE_PERFORM;
648 break;
649 default: /* Unknown profile */
650 return -EOPNOTSUPP;
651 }
652 return 0;
653}
654
655/*
656 * dytc_profile_get: Function to register with platform_profile
657 * handler. Returns current platform profile.
658 */
659int dytc_profile_get(struct platform_profile_handler *pprof,
660 enum platform_profile_option *profile)
661{
662 struct ideapad_dytc_priv *dytc;
663
664 dytc = container_of(pprof, struct ideapad_dytc_priv, pprof);
665 *profile = dytc->current_profile;
666 return 0;
667}
668
669/*
670 * Helper function - check if we are in CQL mode and if we are
671 * - disable CQL,
672 * - run the command
673 * - enable CQL
674 * If not in CQL mode, just run the command
675 */
676int dytc_cql_command(struct ideapad_private *priv, int command, int *output)
677{
678 int err, cmd_err, dummy;
679 int cur_funcmode;
680
681 /* Determine if we are in CQL mode. This alters the commands we do */
682 err = method_dytc(priv->adev->handle, DYTC_CMD_GET, output);
683 if (err)
684 return err;
685
686 cur_funcmode = (*output >> DYTC_GET_FUNCTION_BIT) & 0xF;
687 /* Check if we're OK to return immediately */
688 if ((command == DYTC_CMD_GET) && (cur_funcmode != DYTC_FUNCTION_CQL))
689 return 0;
690
691 if (cur_funcmode == DYTC_FUNCTION_CQL) {
692 err = method_dytc(priv->adev->handle, DYTC_DISABLE_CQL, &dummy);
693 if (err)
694 return err;
695 }
696
697 cmd_err = method_dytc(priv->adev->handle, command, output);
698 /* Check return condition after we've restored CQL state */
699
700 if (cur_funcmode == DYTC_FUNCTION_CQL) {
701 err = method_dytc(priv->adev->handle, DYTC_ENABLE_CQL, &dummy);
702 if (err)
703 return err;
704 }
705
706 return cmd_err;
707}
708
709/*
710 * dytc_profile_set: Function to register with platform_profile
711 * handler. Sets current platform profile.
712 */
713int dytc_profile_set(struct platform_profile_handler *pprof,
714 enum platform_profile_option profile)
715{
716 struct ideapad_dytc_priv *dytc;
717 struct ideapad_private *priv;
718 int output;
719 int err;
720
721 dytc = container_of(pprof, struct ideapad_dytc_priv, pprof);
722 priv = dytc->priv;
723
724 err = mutex_lock_interruptible(&dytc->mutex);
725 if (err)
726 return err;
727
728 if (profile == PLATFORM_PROFILE_BALANCED) {
729 /* To get back to balanced mode we just issue a reset command */
730 err = method_dytc(priv->adev->handle, DYTC_CMD_RESET, &output);
731 if (err)
732 goto unlock;
733 } else {
734 int perfmode;
735
736 err = convert_profile_to_dytc(profile, &perfmode);
737 if (err)
738 goto unlock;
739
740 /* Determine if we are in CQL mode. This alters the commands we do */
741 err = dytc_cql_command(priv,
742 DYTC_SET_COMMAND(DYTC_FUNCTION_MMC, perfmode, 1),
743 &output);
744 if (err)
745 goto unlock;
746 }
747 /* Success - update current profile */
748 dytc->current_profile = profile;
749unlock:
750 mutex_unlock(&dytc->mutex);
751 return err;
752}
753
754static void dytc_profile_refresh(struct ideapad_private *priv)
755{
756 enum platform_profile_option profile;
757 int output, err;
758 int perfmode;
759
760 mutex_lock(&priv->dytc->mutex);
761 err = dytc_cql_command(priv, DYTC_CMD_GET, &output);
762 mutex_unlock(&priv->dytc->mutex);
763 if (err)
764 return;
765
766 perfmode = (output >> DYTC_GET_MODE_BIT) & 0xF;
767 convert_dytc_to_profile(perfmode, &profile);
768 if (profile != priv->dytc->current_profile) {
769 priv->dytc->current_profile = profile;
770 platform_profile_notify();
771 }
772}
773
774static int ideapad_dytc_profile_init(struct ideapad_private *priv)
775{
776 int err, output, dytc_version;
777
778 err = method_dytc(priv->adev->handle, DYTC_CMD_QUERY, &output);
779 /* For all other errors we can flag the failure */
780 if (err)
781 return err;
782
783 /* Check DYTC is enabled and supports mode setting */
784 if (!(output & BIT(DYTC_QUERY_ENABLE_BIT)))
785 return -ENODEV;
786
787 dytc_version = (output >> DYTC_QUERY_REV_BIT) & 0xF;
788 if (dytc_version < 5)
789 return -ENODEV;
790
791 priv->dytc = kzalloc(sizeof(struct ideapad_dytc_priv), GFP_KERNEL);
792 if (!priv->dytc)
793 return -ENOMEM;
794
795 mutex_init(&priv->dytc->mutex);
796
797 priv->dytc->priv = priv;
798 priv->dytc->pprof.profile_get = dytc_profile_get;
799 priv->dytc->pprof.profile_set = dytc_profile_set;
800
801 /* Setup supported modes */
802 set_bit(PLATFORM_PROFILE_LOW_POWER, priv->dytc->pprof.choices);
803 set_bit(PLATFORM_PROFILE_BALANCED, priv->dytc->pprof.choices);
804 set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->dytc->pprof.choices);
805
806 /* Create platform_profile structure and register */
807 err = platform_profile_register(&priv->dytc->pprof);
808 if (err)
809 goto mutex_destroy;
810
811 /* Ensure initial values are correct */
812 dytc_profile_refresh(priv);
813
814 return 0;
815
816mutex_destroy:
817 mutex_destroy(&priv->dytc->mutex);
818 kfree(priv->dytc);
819 priv->dytc = NULL;
820 return err;
821}
822
823static void ideapad_dytc_profile_exit(struct ideapad_private *priv)
824{
825 if (!priv->dytc)
826 return;
827
828 platform_profile_remove();
829 mutex_destroy(&priv->dytc->mutex);
830 kfree(priv->dytc);
831 priv->dytc = NULL;
832}
833
a4b5a279
IP
834/*
835 * Rfkill
836 */
c1f73658
IP
837struct ideapad_rfk_data {
838 char *name;
839 int cfgbit;
840 int opcode;
841 int type;
842};
843
b3d94d70 844static const struct ideapad_rfk_data ideapad_rfk_data[] = {
2be1dc21
IP
845 { "ideapad_wlan", CFG_WIFI_BIT, VPCCMD_W_WIFI, RFKILL_TYPE_WLAN },
846 { "ideapad_bluetooth", CFG_BT_BIT, VPCCMD_W_BT, RFKILL_TYPE_BLUETOOTH },
847 { "ideapad_3g", CFG_3G_BIT, VPCCMD_W_3G, RFKILL_TYPE_WWAN },
c1f73658
IP
848};
849
58ac7aa0
DW
850static int ideapad_rfk_set(void *data, bool blocked)
851{
331e0ea2 852 struct ideapad_rfk_priv *priv = data;
4b200b46 853 int opcode = ideapad_rfk_data[priv->dev].opcode;
fa08359e 854
4b200b46 855 return write_ec_cmd(priv->priv->adev->handle, opcode, !blocked);
58ac7aa0
DW
856}
857
3d59dfcd 858static const struct rfkill_ops ideapad_rfk_ops = {
58ac7aa0
DW
859 .set_block = ideapad_rfk_set,
860};
861
923de84a 862static void ideapad_sync_rfk_state(struct ideapad_private *priv)
58ac7aa0 863{
ce363c2b 864 unsigned long hw_blocked = 0;
58ac7aa0
DW
865 int i;
866
ce363c2b
HG
867 if (priv->has_hw_rfkill_switch) {
868 if (read_ec_data(priv->adev->handle, VPCCMD_R_RF, &hw_blocked))
869 return;
870 hw_blocked = !hw_blocked;
871 }
58ac7aa0 872
c1f73658 873 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
ce326329 874 if (priv->rfk[i])
2b7266bd 875 rfkill_set_hw_state(priv->rfk[i], hw_blocked);
58ac7aa0
DW
876}
877
75a11f11 878static int ideapad_register_rfkill(struct ideapad_private *priv, int dev)
58ac7aa0
DW
879{
880 int ret;
2b7266bd 881 unsigned long sw_blocked;
58ac7aa0 882
bfa97b7d
IP
883 if (no_bt_rfkill &&
884 (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) {
885 /* Force to enable bluetooth when no_bt_rfkill=1 */
331e0ea2 886 write_ec_cmd(priv->adev->handle,
bfa97b7d
IP
887 ideapad_rfk_data[dev].opcode, 1);
888 return 0;
889 }
331e0ea2
ZR
890 priv->rfk_priv[dev].dev = dev;
891 priv->rfk_priv[dev].priv = priv;
bfa97b7d 892
75a11f11 893 priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name,
b5c37b79 894 &priv->platform_device->dev,
75a11f11
ZR
895 ideapad_rfk_data[dev].type,
896 &ideapad_rfk_ops,
331e0ea2 897 &priv->rfk_priv[dev]);
ce326329 898 if (!priv->rfk[dev])
58ac7aa0
DW
899 return -ENOMEM;
900
331e0ea2 901 if (read_ec_data(priv->adev->handle, ideapad_rfk_data[dev].opcode-1,
2b7266bd
IP
902 &sw_blocked)) {
903 rfkill_init_sw_state(priv->rfk[dev], 0);
904 } else {
905 sw_blocked = !sw_blocked;
906 rfkill_init_sw_state(priv->rfk[dev], sw_blocked);
907 }
908
ce326329 909 ret = rfkill_register(priv->rfk[dev]);
58ac7aa0 910 if (ret) {
ce326329 911 rfkill_destroy(priv->rfk[dev]);
58ac7aa0
DW
912 return ret;
913 }
914 return 0;
915}
916
75a11f11 917static void ideapad_unregister_rfkill(struct ideapad_private *priv, int dev)
58ac7aa0 918{
ce326329 919 if (!priv->rfk[dev])
58ac7aa0
DW
920 return;
921
ce326329
DW
922 rfkill_unregister(priv->rfk[dev]);
923 rfkill_destroy(priv->rfk[dev]);
58ac7aa0
DW
924}
925
98ee6919
IP
926/*
927 * Platform device
928 */
b5c37b79 929static int ideapad_sysfs_init(struct ideapad_private *priv)
98ee6919 930{
8782d8d7
BP
931 return device_add_group(&priv->platform_device->dev,
932 &ideapad_attribute_group);
98ee6919
IP
933}
934
b5c37b79 935static void ideapad_sysfs_exit(struct ideapad_private *priv)
98ee6919 936{
8782d8d7
BP
937 device_remove_group(&priv->platform_device->dev,
938 &ideapad_attribute_group);
98ee6919 939}
98ee6919 940
f63409ae
IP
941/*
942 * input device
943 */
944static const struct key_entry ideapad_keymap[] = {
f43d9ec0 945 { KE_KEY, 6, { KEY_SWITCHVIDEOMODE } },
296f9fe0 946 { KE_KEY, 7, { KEY_CAMERA } },
48f67d62 947 { KE_KEY, 8, { KEY_MICMUTE } },
296f9fe0 948 { KE_KEY, 11, { KEY_F16 } },
f43d9ec0
IP
949 { KE_KEY, 13, { KEY_WLAN } },
950 { KE_KEY, 16, { KEY_PROG1 } },
951 { KE_KEY, 17, { KEY_PROG2 } },
296f9fe0
MM
952 { KE_KEY, 64, { KEY_PROG3 } },
953 { KE_KEY, 65, { KEY_PROG4 } },
07a4a4fc
MM
954 { KE_KEY, 66, { KEY_TOUCHPAD_OFF } },
955 { KE_KEY, 67, { KEY_TOUCHPAD_ON } },
74caab99
AB
956 { KE_KEY, 128, { KEY_ESC } },
957
f63409ae
IP
958 { KE_END, 0 },
959};
960
b859f159 961static int ideapad_input_init(struct ideapad_private *priv)
f63409ae
IP
962{
963 struct input_dev *inputdev;
964 int error;
965
966 inputdev = input_allocate_device();
b222cca6 967 if (!inputdev)
f63409ae 968 return -ENOMEM;
f63409ae
IP
969
970 inputdev->name = "Ideapad extra buttons";
971 inputdev->phys = "ideapad/input0";
972 inputdev->id.bustype = BUS_HOST;
8693ae84 973 inputdev->dev.parent = &priv->platform_device->dev;
f63409ae
IP
974
975 error = sparse_keymap_setup(inputdev, ideapad_keymap, NULL);
976 if (error) {
654324c4
BP
977 dev_err(&priv->platform_device->dev,
978 "Unable to setup input device keymap\n");
f63409ae
IP
979 goto err_free_dev;
980 }
981
982 error = input_register_device(inputdev);
983 if (error) {
654324c4
BP
984 dev_err(&priv->platform_device->dev,
985 "Unable to register input device\n");
c973d4b5 986 goto err_free_dev;
f63409ae
IP
987 }
988
8693ae84 989 priv->inputdev = inputdev;
f63409ae
IP
990 return 0;
991
f63409ae
IP
992err_free_dev:
993 input_free_device(inputdev);
994 return error;
995}
996
7451a55a 997static void ideapad_input_exit(struct ideapad_private *priv)
f63409ae 998{
8693ae84
IP
999 input_unregister_device(priv->inputdev);
1000 priv->inputdev = NULL;
f63409ae
IP
1001}
1002
8693ae84
IP
1003static void ideapad_input_report(struct ideapad_private *priv,
1004 unsigned long scancode)
f63409ae 1005{
8693ae84 1006 sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
f63409ae 1007}
f63409ae 1008
f43d9ec0
IP
1009static void ideapad_input_novokey(struct ideapad_private *priv)
1010{
1011 unsigned long long_pressed;
1012
331e0ea2 1013 if (read_ec_data(priv->adev->handle, VPCCMD_R_NOVO, &long_pressed))
f43d9ec0
IP
1014 return;
1015 if (long_pressed)
1016 ideapad_input_report(priv, 17);
1017 else
1018 ideapad_input_report(priv, 16);
1019}
1020
296f9fe0
MM
1021static void ideapad_check_special_buttons(struct ideapad_private *priv)
1022{
1023 unsigned long bit, value;
1024
331e0ea2 1025 read_ec_data(priv->adev->handle, VPCCMD_R_SPECIAL_BUTTONS, &value);
296f9fe0 1026
0c4915b6
BP
1027 for_each_set_bit (bit, &value, 16) {
1028 switch (bit) {
1029 case 0: /* Z580 */
1030 case 6: /* Z570 */
1031 /* Thermal Management button */
1032 ideapad_input_report(priv, 65);
1033 break;
1034 case 1:
1035 /* OneKey Theater button */
1036 ideapad_input_report(priv, 64);
1037 break;
1038 default:
654324c4
BP
1039 dev_info(&priv->platform_device->dev,
1040 "Unknown special button: %lu\n", bit);
0c4915b6 1041 break;
296f9fe0
MM
1042 }
1043 }
1044}
1045
a4ecbb8a
IP
1046/*
1047 * backlight
1048 */
1049static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)
1050{
331e0ea2 1051 struct ideapad_private *priv = bl_get_data(blightdev);
a4ecbb8a
IP
1052 unsigned long now;
1053
331e0ea2 1054 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now))
a4ecbb8a
IP
1055 return -EIO;
1056 return now;
1057}
1058
1059static int ideapad_backlight_update_status(struct backlight_device *blightdev)
1060{
331e0ea2
ZR
1061 struct ideapad_private *priv = bl_get_data(blightdev);
1062
331e0ea2 1063 if (write_ec_cmd(priv->adev->handle, VPCCMD_W_BL,
2be1dc21 1064 blightdev->props.brightness))
a4ecbb8a 1065 return -EIO;
331e0ea2 1066 if (write_ec_cmd(priv->adev->handle, VPCCMD_W_BL_POWER,
a4ecbb8a
IP
1067 blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1))
1068 return -EIO;
1069
1070 return 0;
1071}
1072
1073static const struct backlight_ops ideapad_backlight_ops = {
1074 .get_brightness = ideapad_backlight_get_brightness,
1075 .update_status = ideapad_backlight_update_status,
1076};
1077
1078static int ideapad_backlight_init(struct ideapad_private *priv)
1079{
1080 struct backlight_device *blightdev;
1081 struct backlight_properties props;
1082 unsigned long max, now, power;
1083
331e0ea2 1084 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &max))
a4ecbb8a 1085 return -EIO;
331e0ea2 1086 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now))
a4ecbb8a 1087 return -EIO;
331e0ea2 1088 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))
a4ecbb8a
IP
1089 return -EIO;
1090
1091 memset(&props, 0, sizeof(struct backlight_properties));
1092 props.max_brightness = max;
1093 props.type = BACKLIGHT_PLATFORM;
1094 blightdev = backlight_device_register("ideapad",
1095 &priv->platform_device->dev,
1096 priv,
1097 &ideapad_backlight_ops,
1098 &props);
1099 if (IS_ERR(blightdev)) {
654324c4
BP
1100 dev_err(&priv->platform_device->dev,
1101 "Could not register backlight device\n");
a4ecbb8a
IP
1102 return PTR_ERR(blightdev);
1103 }
1104
1105 priv->blightdev = blightdev;
1106 blightdev->props.brightness = now;
1107 blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
1108 backlight_update_status(blightdev);
1109
1110 return 0;
1111}
1112
1113static void ideapad_backlight_exit(struct ideapad_private *priv)
1114{
00981810 1115 backlight_device_unregister(priv->blightdev);
a4ecbb8a
IP
1116 priv->blightdev = NULL;
1117}
1118
1119static void ideapad_backlight_notify_power(struct ideapad_private *priv)
1120{
1121 unsigned long power;
1122 struct backlight_device *blightdev = priv->blightdev;
1123
d4afc775
RB
1124 if (!blightdev)
1125 return;
331e0ea2 1126 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))
a4ecbb8a
IP
1127 return;
1128 blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
1129}
1130
1131static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
1132{
1133 unsigned long now;
1134
1135 /* if we control brightness via acpi video driver */
1136 if (priv->blightdev == NULL) {
331e0ea2 1137 read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now);
a4ecbb8a
IP
1138 return;
1139 }
1140
1141 backlight_force_update(priv->blightdev, BACKLIGHT_UPDATE_HOTKEY);
1142}
1143
a4b5a279
IP
1144/*
1145 * module init/exit
1146 */
75a11f11 1147static void ideapad_sync_touchpad_state(struct ideapad_private *priv)
07a4a4fc 1148{
07a4a4fc
MM
1149 unsigned long value;
1150
d69cd7ee
JY
1151 if (!priv->has_touchpad_switch)
1152 return;
1153
07a4a4fc 1154 /* Without reading from EC touchpad LED doesn't switch state */
75a11f11 1155 if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value)) {
07a4a4fc
MM
1156 /* Some IdeaPads don't really turn off touchpad - they only
1157 * switch the LED state. We (de)activate KBC AUX port to turn
1158 * touchpad off and on. We send KEY_TOUCHPAD_OFF and
1159 * KEY_TOUCHPAD_ON to not to get out of sync with LED */
1160 unsigned char param;
1161 i8042_command(&param, value ? I8042_CMD_AUX_ENABLE :
1162 I8042_CMD_AUX_DISABLE);
1163 ideapad_input_report(priv, value ? 67 : 66);
1164 }
1165}
1166
b5c37b79
ZR
1167static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)
1168{
1169 struct ideapad_private *priv = data;
0c4915b6 1170 unsigned long vpc1, vpc2, bit;
b5c37b79
ZR
1171
1172 if (read_ec_data(handle, VPCCMD_R_VPC1, &vpc1))
1173 return;
1174 if (read_ec_data(handle, VPCCMD_R_VPC2, &vpc2))
1175 return;
1176
1177 vpc1 = (vpc2 << 8) | vpc1;
0c4915b6
BP
1178
1179 for_each_set_bit (bit, &vpc1, 16) {
1180 switch (bit) {
1181 case 9:
1182 ideapad_sync_rfk_state(priv);
1183 break;
1184 case 13:
1185 case 11:
1186 case 8:
1187 case 7:
1188 case 6:
1189 ideapad_input_report(priv, bit);
1190 break;
1191 case 5:
1192 ideapad_sync_touchpad_state(priv);
1193 break;
1194 case 4:
1195 ideapad_backlight_notify_brightness(priv);
1196 break;
1197 case 3:
1198 ideapad_input_novokey(priv);
1199 break;
1200 case 2:
1201 ideapad_backlight_notify_power(priv);
1202 break;
1203 case 0:
1204 ideapad_check_special_buttons(priv);
1205 break;
1206 case 1:
1207 /* Some IdeaPads report event 1 every ~20
1208 * seconds while on battery power; some
1209 * report this when changing to/from tablet
1210 * mode. Squelch this event.
1211 */
1212 break;
1213 default:
654324c4
BP
1214 dev_info(&priv->platform_device->dev,
1215 "Unknown event: %lu\n", bit);
b5c37b79
ZR
1216 }
1217 }
1218}
1219
74caab99
AB
1220#if IS_ENABLED(CONFIG_ACPI_WMI)
1221static void ideapad_wmi_notify(u32 value, void *context)
1222{
654324c4
BP
1223 struct ideapad_private *priv = context;
1224
74caab99
AB
1225 switch (value) {
1226 case 128:
654324c4 1227 ideapad_input_report(priv, value);
74caab99
AB
1228 break;
1229 default:
654324c4
BP
1230 dev_info(&priv->platform_device->dev,
1231 "Unknown WMI event: %u\n", value);
74caab99
AB
1232 }
1233}
1234#endif
1235
ce363c2b 1236/*
5105e78e
HG
1237 * Some ideapads have a hardware rfkill switch, but most do not have one.
1238 * Reading VPCCMD_R_RF always results in 0 on models without a hardware rfkill,
1239 * switch causing ideapad_laptop to wrongly report all radios as hw-blocked.
1240 * There used to be a long list of DMI ids for models without a hw rfkill
1241 * switch here, but that resulted in playing whack a mole.
1242 * More importantly wrongly reporting the wifi radio as hw-blocked, results in
1243 * non working wifi. Whereas not reporting it hw-blocked, when it actually is
1244 * hw-blocked results in an empty SSID list, which is a much more benign
1245 * failure mode.
1246 * So the default now is the much safer option of assuming there is no
1247 * hardware rfkill switch. This default also actually matches most hardware,
1248 * since having a hw rfkill switch is quite rare on modern hardware, so this
1249 * also leads to a much shorter list.
ce363c2b 1250 */
5105e78e 1251static const struct dmi_system_id hw_rfkill_list[] = {
85093f79
HG
1252 {}
1253};
1254
b5c37b79 1255static int ideapad_acpi_add(struct platform_device *pdev)
58ac7aa0 1256{
3371f481 1257 int ret, i;
57f9616b 1258 int cfg;
ce326329 1259 struct ideapad_private *priv;
b5c37b79 1260 struct acpi_device *adev;
803be832 1261 acpi_status status;
b5c37b79
ZR
1262
1263 ret = acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev);
1264 if (ret)
1265 return -ENODEV;
58ac7aa0 1266
469f6434 1267 if (read_method_int(adev->handle, "_CFG", &cfg))
6f8371c0
IP
1268 return -ENODEV;
1269
b3facd7b 1270 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
ce326329
DW
1271 if (!priv)
1272 return -ENOMEM;
b5c37b79
ZR
1273
1274 dev_set_drvdata(&pdev->dev, priv);
3371f481 1275 priv->cfg = cfg;
469f6434 1276 priv->adev = adev;
b5c37b79 1277 priv->platform_device = pdev;
5105e78e 1278 priv->has_hw_rfkill_switch = dmi_check_system(hw_rfkill_list);
98ee6919 1279
d69cd7ee
JY
1280 /* Most ideapads with ELAN0634 touchpad don't use EC touchpad switch */
1281 priv->has_touchpad_switch = !acpi_dev_present("ELAN0634", NULL, -1);
1282
b5c37b79 1283 ret = ideapad_sysfs_init(priv);
98ee6919 1284 if (ret)
b3facd7b 1285 return ret;
ce326329 1286
17f1bf38 1287 ideapad_debugfs_init(priv);
773e3206 1288
8693ae84 1289 ret = ideapad_input_init(priv);
f63409ae
IP
1290 if (ret)
1291 goto input_failed;
1292
ce363c2b
HG
1293 /*
1294 * On some models without a hw-switch (the yoga 2 13 at least)
1295 * VPCCMD_W_RF must be explicitly set to 1 for the wifi to work.
1296 */
1297 if (!priv->has_hw_rfkill_switch)
1298 write_ec_cmd(priv->adev->handle, VPCCMD_W_RF, 1);
1299
d69cd7ee
JY
1300 /* The same for Touchpad */
1301 if (!priv->has_touchpad_switch)
1302 write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, 1);
1303
ce363c2b
HG
1304 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
1305 if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg))
1306 ideapad_register_rfkill(priv, i);
1307
923de84a 1308 ideapad_sync_rfk_state(priv);
75a11f11 1309 ideapad_sync_touchpad_state(priv);
c9f718d0 1310
eabe5339
JY
1311 ideapad_dytc_profile_init(priv);
1312
26bff5f0 1313 if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
a4ecbb8a
IP
1314 ret = ideapad_backlight_init(priv);
1315 if (ret && ret != -ENODEV)
1316 goto backlight_failed;
1317 }
803be832
BP
1318 status = acpi_install_notify_handler(adev->handle,
1319 ACPI_DEVICE_NOTIFY,
1320 ideapad_acpi_notify, priv);
1321 if (ACPI_FAILURE(status)) {
1322 ret = -EIO;
b5c37b79 1323 goto notification_failed;
803be832 1324 }
2d98e0b9 1325
74caab99 1326#if IS_ENABLED(CONFIG_ACPI_WMI)
2d98e0b9 1327 for (i = 0; i < ARRAY_SIZE(ideapad_wmi_fnesc_events); i++) {
803be832
BP
1328 status = wmi_install_notify_handler(ideapad_wmi_fnesc_events[i],
1329 ideapad_wmi_notify, priv);
1330 if (ACPI_SUCCESS(status)) {
2d98e0b9
AB
1331 priv->fnesc_guid = ideapad_wmi_fnesc_events[i];
1332 break;
1333 }
1334 }
803be832
BP
1335 if (ACPI_FAILURE(status) && status != AE_NOT_EXIST) {
1336 ret = -EIO;
74caab99 1337 goto notification_failed_wmi;
803be832 1338 }
74caab99 1339#endif
a4ecbb8a 1340
58ac7aa0 1341 return 0;
74caab99
AB
1342#if IS_ENABLED(CONFIG_ACPI_WMI)
1343notification_failed_wmi:
1344 acpi_remove_notify_handler(priv->adev->handle,
1345 ACPI_DEVICE_NOTIFY, ideapad_acpi_notify);
1346#endif
b5c37b79
ZR
1347notification_failed:
1348 ideapad_backlight_exit(priv);
a4ecbb8a 1349backlight_failed:
caa315b8 1350 ideapad_dytc_profile_exit(priv);
a4ecbb8a 1351 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
75a11f11 1352 ideapad_unregister_rfkill(priv, i);
7451a55a 1353 ideapad_input_exit(priv);
f63409ae 1354input_failed:
773e3206 1355 ideapad_debugfs_exit(priv);
b5c37b79 1356 ideapad_sysfs_exit(priv);
98ee6919 1357 return ret;
58ac7aa0
DW
1358}
1359
b5c37b79 1360static int ideapad_acpi_remove(struct platform_device *pdev)
58ac7aa0 1361{
b5c37b79 1362 struct ideapad_private *priv = dev_get_drvdata(&pdev->dev);
58ac7aa0 1363 int i;
ce326329 1364
74caab99 1365#if IS_ENABLED(CONFIG_ACPI_WMI)
2d98e0b9
AB
1366 if (priv->fnesc_guid)
1367 wmi_remove_notify_handler(priv->fnesc_guid);
74caab99 1368#endif
b5c37b79
ZR
1369 acpi_remove_notify_handler(priv->adev->handle,
1370 ACPI_DEVICE_NOTIFY, ideapad_acpi_notify);
a4ecbb8a 1371 ideapad_backlight_exit(priv);
eabe5339 1372 ideapad_dytc_profile_exit(priv);
c1f73658 1373 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
75a11f11 1374 ideapad_unregister_rfkill(priv, i);
8693ae84 1375 ideapad_input_exit(priv);
773e3206 1376 ideapad_debugfs_exit(priv);
b5c37b79 1377 ideapad_sysfs_exit(priv);
c9f718d0 1378
58ac7aa0
DW
1379 return 0;
1380}
1381
11fa8da5 1382#ifdef CONFIG_PM_SLEEP
e1a39a44 1383static int ideapad_acpi_resume(struct device *dev)
07a4a4fc 1384{
e1a39a44 1385 struct ideapad_private *priv = dev_get_drvdata(dev);
75a11f11
ZR
1386
1387 ideapad_sync_rfk_state(priv);
1388 ideapad_sync_touchpad_state(priv);
eabe5339
JY
1389
1390 if (priv->dytc)
1391 dytc_profile_refresh(priv);
1392
07a4a4fc
MM
1393 return 0;
1394}
11fa8da5 1395#endif
b5c37b79 1396static SIMPLE_DEV_PM_OPS(ideapad_pm, NULL, ideapad_acpi_resume);
07a4a4fc 1397
b5c37b79
ZR
1398static const struct acpi_device_id ideapad_device_ids[] = {
1399 { "VPC2004", 0},
1400 { "", 0},
58ac7aa0 1401};
b5c37b79
ZR
1402MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
1403
1404static struct platform_driver ideapad_acpi_driver = {
1405 .probe = ideapad_acpi_add,
1406 .remove = ideapad_acpi_remove,
1407 .driver = {
1408 .name = "ideapad_acpi",
b5c37b79
ZR
1409 .pm = &ideapad_pm,
1410 .acpi_match_table = ACPI_PTR(ideapad_device_ids),
1411 },
1412};
1413
1414module_platform_driver(ideapad_acpi_driver);
58ac7aa0
DW
1415
1416MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
1417MODULE_DESCRIPTION("IdeaPad ACPI Extras");
1418MODULE_LICENSE("GPL");