1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
24
25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/init.h>
28#include <linux/types.h>
29#include <linux/acpi.h>
30#include <linux/rfkill.h>
31#include <linux/platform_device.h>
32#include <linux/input.h>
33#include <linux/input/sparse-keymap.h>
34#include <linux/backlight.h>
35#include <linux/fb.h>
36#include <linux/debugfs.h>
37#include <linux/seq_file.h>
38#include <linux/i8042.h>
39#include <linux/dmi.h>
40#include <linux/device.h>
41#include <acpi/video.h>
42
43#define IDEAPAD_RFKILL_DEV_NUM (3)
44
45#define BM_CONSERVATION_BIT (5)
46
47#define CFG_BT_BIT (16)
48#define CFG_3G_BIT (17)
49#define CFG_WIFI_BIT (18)
50#define CFG_CAMERA_BIT (19)
51
52#if IS_ENABLED(CONFIG_ACPI_WMI)
53static const char *const ideapad_wmi_fnesc_events[] = {
54 "26CAB2E5-5CF1-46AE-AAC3-4A12B6BA50E6",
55 "56322276-8493-4CE8-A783-98C991274F5E",
56};
57#endif
58
59enum {
60 BMCMD_CONSERVATION_ON = 3,
61 BMCMD_CONSERVATION_OFF = 5,
62};
63
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,
83 VPCCMD_W_FAN,
84 VPCCMD_R_RF,
85 VPCCMD_W_RF,
86 VPCCMD_R_FAN = 0x2B,
87 VPCCMD_R_SPECIAL_BUTTONS = 0x31,
88 VPCCMD_W_BL_POWER = 0x33,
89};
90
91struct ideapad_rfk_priv {
92 int dev;
93 struct ideapad_private *priv;
94};
95
96struct ideapad_private {
97 struct acpi_device *adev;
98 struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
99 struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM];
100 struct platform_device *platform_device;
101 struct input_dev *inputdev;
102 struct backlight_device *blightdev;
103 struct dentry *debug;
104 unsigned long cfg;
105 bool has_hw_rfkill_switch;
106 const char *fnesc_guid;
107};
108
109static bool no_bt_rfkill;
110module_param(no_bt_rfkill, bool, 0444);
111MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
112
113
114
115
116#define IDEAPAD_EC_TIMEOUT (100)
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
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
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", ¶ms, &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", ¶ms, 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}
237
238
239
240
241static int debugfs_status_show(struct seq_file *s, void *data)
242{
243 struct ideapad_private *priv = s->private;
244 unsigned long value;
245
246 if (!priv)
247 return -EINVAL;
248
249 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &value))
250 seq_printf(s, "Backlight max:\t%lu\n", value);
251 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL, &value))
252 seq_printf(s, "Backlight now:\t%lu\n", value);
253 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &value))
254 seq_printf(s, "BL power value:\t%s\n", value ? "On" : "Off");
255 seq_printf(s, "=====================\n");
256
257 if (!read_ec_data(priv->adev->handle, VPCCMD_R_RF, &value))
258 seq_printf(s, "Radio status:\t%s(%lu)\n",
259 value ? "On" : "Off", value);
260 if (!read_ec_data(priv->adev->handle, VPCCMD_R_WIFI, &value))
261 seq_printf(s, "Wifi status:\t%s(%lu)\n",
262 value ? "On" : "Off", value);
263 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BT, &value))
264 seq_printf(s, "BT status:\t%s(%lu)\n",
265 value ? "On" : "Off", value);
266 if (!read_ec_data(priv->adev->handle, VPCCMD_R_3G, &value))
267 seq_printf(s, "3G status:\t%s(%lu)\n",
268 value ? "On" : "Off", value);
269 seq_printf(s, "=====================\n");
270
271 if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value))
272 seq_printf(s, "Touchpad status:%s(%lu)\n",
273 value ? "On" : "Off", value);
274 if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value))
275 seq_printf(s, "Camera status:\t%s(%lu)\n",
276 value ? "On" : "Off", value);
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 }
284
285 return 0;
286}
287
288static int debugfs_status_open(struct inode *inode, struct file *file)
289{
290 return single_open(file, debugfs_status_show, inode->i_private);
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{
303 struct ideapad_private *priv = s->private;
304
305 if (!priv) {
306 seq_printf(s, "cfg: N/A\n");
307 } else {
308 seq_printf(s, "cfg: 0x%.8lX\n\nCapability: ",
309 priv->cfg);
310 if (test_bit(CFG_BT_BIT, &priv->cfg))
311 seq_printf(s, "Bluetooth ");
312 if (test_bit(CFG_3G_BIT, &priv->cfg))
313 seq_printf(s, "3G ");
314 if (test_bit(CFG_WIFI_BIT, &priv->cfg))
315 seq_printf(s, "Wireless ");
316 if (test_bit(CFG_CAMERA_BIT, &priv->cfg))
317 seq_printf(s, "Camera ");
318 seq_printf(s, "\nGraphic: ");
319 switch ((priv->cfg)&0x700) {
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{
343 return single_open(file, debugfs_cfg_show, inode->i_private);
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
354static int ideapad_debugfs_init(struct ideapad_private *priv)
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
364 node = debugfs_create_file("cfg", S_IRUGO, priv->debug, priv,
365 &debugfs_cfg_fops);
366 if (!node) {
367 pr_err("failed to create cfg in debugfs");
368 goto errout;
369 }
370
371 node = debugfs_create_file("status", S_IRUGO, priv->debug, priv,
372 &debugfs_status_fops);
373 if (!node) {
374 pr_err("failed to create status in debugfs");
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
390
391
392
393static ssize_t show_ideapad_cam(struct device *dev,
394 struct device_attribute *attr,
395 char *buf)
396{
397 unsigned long result;
398 struct ideapad_private *priv = dev_get_drvdata(dev);
399
400 if (read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &result))
401 return sprintf(buf, "-1\n");
402 return sprintf(buf, "%lu\n", result);
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;
410 struct ideapad_private *priv = dev_get_drvdata(dev);
411
412 if (!count)
413 return 0;
414 if (sscanf(buf, "%i", &state) != 1)
415 return -EINVAL;
416 ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_CAMERA, state);
417 if (ret < 0)
418 return -EIO;
419 return count;
420}
421
422static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
423
424static ssize_t show_ideapad_fan(struct device *dev,
425 struct device_attribute *attr,
426 char *buf)
427{
428 unsigned long result;
429 struct ideapad_private *priv = dev_get_drvdata(dev);
430
431 if (read_ec_data(priv->adev->handle, VPCCMD_R_FAN, &result))
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;
441 struct ideapad_private *priv = dev_get_drvdata(dev);
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;
449 ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_FAN, state);
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
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
469
470static ssize_t __maybe_unused touchpad_store(struct device *dev,
471 struct device_attribute *attr,
472 const char *buf, size_t count)
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
488static DEVICE_ATTR_RO(touchpad);
489
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
524static struct attribute *ideapad_attributes[] = {
525 &dev_attr_camera_power.attr,
526 &dev_attr_fan_mode.attr,
527 &dev_attr_touchpad.attr,
528 &dev_attr_conservation_mode.attr,
529 NULL
530};
531
532static umode_t ideapad_is_visible(struct kobject *kobj,
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));
542 else if (attr == &dev_attr_fan_mode.attr) {
543 unsigned long value;
544 supported = !read_ec_data(priv->adev->handle, VPCCMD_R_FAN,
545 &value);
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");
549 } else
550 supported = true;
551
552 return supported ? attr->mode : 0;
553}
554
555static const struct attribute_group ideapad_attribute_group = {
556 .is_visible = ideapad_is_visible,
557 .attrs = ideapad_attributes
558};
559
560
561
562
563struct ideapad_rfk_data {
564 char *name;
565 int cfgbit;
566 int opcode;
567 int type;
568};
569
570static const struct ideapad_rfk_data ideapad_rfk_data[] = {
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 },
574};
575
576static int ideapad_rfk_set(void *data, bool blocked)
577{
578 struct ideapad_rfk_priv *priv = data;
579 int opcode = ideapad_rfk_data[priv->dev].opcode;
580
581 return write_ec_cmd(priv->priv->adev->handle, opcode, !blocked);
582}
583
584static const struct rfkill_ops ideapad_rfk_ops = {
585 .set_block = ideapad_rfk_set,
586};
587
588static void ideapad_sync_rfk_state(struct ideapad_private *priv)
589{
590 unsigned long hw_blocked = 0;
591 int i;
592
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 }
598
599 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
600 if (priv->rfk[i])
601 rfkill_set_hw_state(priv->rfk[i], hw_blocked);
602}
603
604static int ideapad_register_rfkill(struct ideapad_private *priv, int dev)
605{
606 int ret;
607 unsigned long sw_blocked;
608
609 if (no_bt_rfkill &&
610 (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) {
611
612 write_ec_cmd(priv->adev->handle,
613 ideapad_rfk_data[dev].opcode, 1);
614 return 0;
615 }
616 priv->rfk_priv[dev].dev = dev;
617 priv->rfk_priv[dev].priv = priv;
618
619 priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name,
620 &priv->platform_device->dev,
621 ideapad_rfk_data[dev].type,
622 &ideapad_rfk_ops,
623 &priv->rfk_priv[dev]);
624 if (!priv->rfk[dev])
625 return -ENOMEM;
626
627 if (read_ec_data(priv->adev->handle, ideapad_rfk_data[dev].opcode-1,
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
635 ret = rfkill_register(priv->rfk[dev]);
636 if (ret) {
637 rfkill_destroy(priv->rfk[dev]);
638 return ret;
639 }
640 return 0;
641}
642
643static void ideapad_unregister_rfkill(struct ideapad_private *priv, int dev)
644{
645 if (!priv->rfk[dev])
646 return;
647
648 rfkill_unregister(priv->rfk[dev]);
649 rfkill_destroy(priv->rfk[dev]);
650}
651
652
653
654
655static int ideapad_sysfs_init(struct ideapad_private *priv)
656{
657 return sysfs_create_group(&priv->platform_device->dev.kobj,
658 &ideapad_attribute_group);
659}
660
661static void ideapad_sysfs_exit(struct ideapad_private *priv)
662{
663 sysfs_remove_group(&priv->platform_device->dev.kobj,
664 &ideapad_attribute_group);
665}
666
667
668
669
670static const struct key_entry ideapad_keymap[] = {
671 { KE_KEY, 6, { KEY_SWITCHVIDEOMODE } },
672 { KE_KEY, 7, { KEY_CAMERA } },
673 { KE_KEY, 8, { KEY_MICMUTE } },
674 { KE_KEY, 11, { KEY_F16 } },
675 { KE_KEY, 13, { KEY_WLAN } },
676 { KE_KEY, 16, { KEY_PROG1 } },
677 { KE_KEY, 17, { KEY_PROG2 } },
678 { KE_KEY, 64, { KEY_PROG3 } },
679 { KE_KEY, 65, { KEY_PROG4 } },
680 { KE_KEY, 66, { KEY_TOUCHPAD_OFF } },
681 { KE_KEY, 67, { KEY_TOUCHPAD_ON } },
682 { KE_KEY, 128, { KEY_ESC } },
683
684 { KE_END, 0 },
685};
686
687static int ideapad_input_init(struct ideapad_private *priv)
688{
689 struct input_dev *inputdev;
690 int error;
691
692 inputdev = input_allocate_device();
693 if (!inputdev)
694 return -ENOMEM;
695
696 inputdev->name = "Ideapad extra buttons";
697 inputdev->phys = "ideapad/input0";
698 inputdev->id.bustype = BUS_HOST;
699 inputdev->dev.parent = &priv->platform_device->dev;
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");
710 goto err_free_dev;
711 }
712
713 priv->inputdev = inputdev;
714 return 0;
715
716err_free_dev:
717 input_free_device(inputdev);
718 return error;
719}
720
721static void ideapad_input_exit(struct ideapad_private *priv)
722{
723 input_unregister_device(priv->inputdev);
724 priv->inputdev = NULL;
725}
726
727static void ideapad_input_report(struct ideapad_private *priv,
728 unsigned long scancode)
729{
730 sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
731}
732
733static void ideapad_input_novokey(struct ideapad_private *priv)
734{
735 unsigned long long_pressed;
736
737 if (read_ec_data(priv->adev->handle, VPCCMD_R_NOVO, &long_pressed))
738 return;
739 if (long_pressed)
740 ideapad_input_report(priv, 17);
741 else
742 ideapad_input_report(priv, 16);
743}
744
745static void ideapad_check_special_buttons(struct ideapad_private *priv)
746{
747 unsigned long bit, value;
748
749 read_ec_data(priv->adev->handle, VPCCMD_R_SPECIAL_BUTTONS, &value);
750
751 for (bit = 0; bit < 16; bit++) {
752 if (test_bit(bit, &value)) {
753 switch (bit) {
754 case 0:
755 case 6:
756
757 ideapad_input_report(priv, 65);
758 break;
759 case 1:
760
761 ideapad_input_report(priv, 64);
762 break;
763 default:
764 pr_info("Unknown special button: %lu\n", bit);
765 break;
766 }
767 }
768 }
769}
770
771
772
773
774static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)
775{
776 struct ideapad_private *priv = bl_get_data(blightdev);
777 unsigned long now;
778
779 if (!priv)
780 return -EINVAL;
781
782 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now))
783 return -EIO;
784 return now;
785}
786
787static int ideapad_backlight_update_status(struct backlight_device *blightdev)
788{
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,
795 blightdev->props.brightness))
796 return -EIO;
797 if (write_ec_cmd(priv->adev->handle, VPCCMD_W_BL_POWER,
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
815 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &max))
816 return -EIO;
817 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now))
818 return -EIO;
819 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))
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{
845 backlight_device_unregister(priv->blightdev);
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
854 if (!blightdev)
855 return;
856 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))
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
866 if (priv->blightdev == NULL) {
867 read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now);
868 return;
869 }
870
871 backlight_force_update(priv->blightdev, BACKLIGHT_UPDATE_HOTKEY);
872}
873
874
875
876
877static void ideapad_sync_touchpad_state(struct ideapad_private *priv)
878{
879 unsigned long value;
880
881
882 if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value)) {
883
884
885
886
887 unsigned char param;
888 i8042_command(¶m, value ? I8042_CMD_AUX_ENABLE :
889 I8042_CMD_AUX_DISABLE);
890 ideapad_input_report(priv, value ? 67 : 66);
891 }
892}
893
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:
913 case 8:
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;
933 case 1:
934
935
936
937
938
939 break;
940 default:
941 pr_info("Unknown event: %lu\n", vpc_bit);
942 }
943 }
944 }
945}
946
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
960
961
962
963
964
965static const struct dmi_system_id no_hw_rfkill_list[] = {
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 },
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 },
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 },
1001 {
1002 .ident = "Lenovo V310-15ISK",
1003 .matches = {
1004 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1005 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V310-15ISK"),
1006 },
1007 },
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 },
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 },
1050 {
1051 .ident = "Lenovo ideapad 310-15IKB",
1052 .matches = {
1053 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1054 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 310-15IKB"),
1055 },
1056 },
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 },
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 },
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 },
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 },
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 },
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 },
1113 {
1114 .ident = "Lenovo Yoga 2 11 / 13 / Pro",
1115 .matches = {
1116 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1117 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 2"),
1118 },
1119 },
1120 {
1121 .ident = "Lenovo Yoga 2 11 / 13 / Pro",
1122 .matches = {
1123 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1124 DMI_MATCH(DMI_BOARD_NAME, "Yoga2"),
1125 },
1126 },
1127 {
1128 .ident = "Lenovo Yoga 3 1170 / 1470",
1129 .matches = {
1130 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1131 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 3"),
1132 },
1133 },
1134 {
1135 .ident = "Lenovo Yoga 3 Pro 1370",
1136 .matches = {
1137 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1138 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 3"),
1139 },
1140 },
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 },
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 },
1155 {
1156 .ident = "Lenovo Yoga 900",
1157 .matches = {
1158 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
1159 DMI_MATCH(DMI_BOARD_NAME, "VIUU4"),
1160 },
1161 },
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 },
1169 {}
1170};
1171
1172static int ideapad_acpi_add(struct platform_device *pdev)
1173{
1174 int ret, i;
1175 int cfg;
1176 struct ideapad_private *priv;
1177 struct acpi_device *adev;
1178
1179 ret = acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev);
1180 if (ret)
1181 return -ENODEV;
1182
1183 if (read_method_int(adev->handle, "_CFG", &cfg))
1184 return -ENODEV;
1185
1186 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
1187 if (!priv)
1188 return -ENOMEM;
1189
1190 dev_set_drvdata(&pdev->dev, priv);
1191 priv->cfg = cfg;
1192 priv->adev = adev;
1193 priv->platform_device = pdev;
1194 priv->has_hw_rfkill_switch = !dmi_check_system(no_hw_rfkill_list);
1195
1196 ret = ideapad_sysfs_init(priv);
1197 if (ret)
1198 return ret;
1199
1200 ret = ideapad_debugfs_init(priv);
1201 if (ret)
1202 goto debugfs_failed;
1203
1204 ret = ideapad_input_init(priv);
1205 if (ret)
1206 goto input_failed;
1207
1208
1209
1210
1211
1212 if (!priv->has_hw_rfkill_switch)
1213 write_ec_cmd(priv->adev->handle, VPCCMD_W_RF, 1);
1214
1215 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
1216 if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg))
1217 ideapad_register_rfkill(priv, i);
1218
1219 ideapad_sync_rfk_state(priv);
1220 ideapad_sync_touchpad_state(priv);
1221
1222 if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
1223 ret = ideapad_backlight_init(priv);
1224 if (ret && ret != -ENODEV)
1225 goto backlight_failed;
1226 }
1227 ret = acpi_install_notify_handler(adev->handle,
1228 ACPI_DEVICE_NOTIFY, ideapad_acpi_notify, priv);
1229 if (ret)
1230 goto notification_failed;
1231
1232#if IS_ENABLED(CONFIG_ACPI_WMI)
1233 for (i = 0; i < ARRAY_SIZE(ideapad_wmi_fnesc_events); i++) {
1234 ret = wmi_install_notify_handler(ideapad_wmi_fnesc_events[i],
1235 ideapad_wmi_notify, priv);
1236 if (ret == AE_OK) {
1237 priv->fnesc_guid = ideapad_wmi_fnesc_events[i];
1238 break;
1239 }
1240 }
1241 if (ret != AE_OK && ret != AE_NOT_EXIST)
1242 goto notification_failed_wmi;
1243#endif
1244
1245 return 0;
1246#if IS_ENABLED(CONFIG_ACPI_WMI)
1247notification_failed_wmi:
1248 acpi_remove_notify_handler(priv->adev->handle,
1249 ACPI_DEVICE_NOTIFY, ideapad_acpi_notify);
1250#endif
1251notification_failed:
1252 ideapad_backlight_exit(priv);
1253backlight_failed:
1254 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
1255 ideapad_unregister_rfkill(priv, i);
1256 ideapad_input_exit(priv);
1257input_failed:
1258 ideapad_debugfs_exit(priv);
1259debugfs_failed:
1260 ideapad_sysfs_exit(priv);
1261 return ret;
1262}
1263
1264static int ideapad_acpi_remove(struct platform_device *pdev)
1265{
1266 struct ideapad_private *priv = dev_get_drvdata(&pdev->dev);
1267 int i;
1268
1269#if IS_ENABLED(CONFIG_ACPI_WMI)
1270 if (priv->fnesc_guid)
1271 wmi_remove_notify_handler(priv->fnesc_guid);
1272#endif
1273 acpi_remove_notify_handler(priv->adev->handle,
1274 ACPI_DEVICE_NOTIFY, ideapad_acpi_notify);
1275 ideapad_backlight_exit(priv);
1276 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
1277 ideapad_unregister_rfkill(priv, i);
1278 ideapad_input_exit(priv);
1279 ideapad_debugfs_exit(priv);
1280 ideapad_sysfs_exit(priv);
1281 dev_set_drvdata(&pdev->dev, NULL);
1282
1283 return 0;
1284}
1285
1286#ifdef CONFIG_PM_SLEEP
1287static int ideapad_acpi_resume(struct device *device)
1288{
1289 struct ideapad_private *priv;
1290
1291 if (!device)
1292 return -EINVAL;
1293 priv = dev_get_drvdata(device);
1294
1295 ideapad_sync_rfk_state(priv);
1296 ideapad_sync_touchpad_state(priv);
1297 return 0;
1298}
1299#endif
1300static SIMPLE_DEV_PM_OPS(ideapad_pm, NULL, ideapad_acpi_resume);
1301
1302static const struct acpi_device_id ideapad_device_ids[] = {
1303 { "VPC2004", 0},
1304 { "", 0},
1305};
1306MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
1307
1308static struct platform_driver ideapad_acpi_driver = {
1309 .probe = ideapad_acpi_add,
1310 .remove = ideapad_acpi_remove,
1311 .driver = {
1312 .name = "ideapad_acpi",
1313 .pm = &ideapad_pm,
1314 .acpi_match_table = ACPI_PTR(ideapad_device_ids),
1315 },
1316};
1317
1318module_platform_driver(ideapad_acpi_driver);
1319
1320MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
1321MODULE_DESCRIPTION("IdeaPad ACPI Extras");
1322MODULE_LICENSE("GPL");
1323