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