ideapad-laptop: Port to new backlight interface selection API
[linux-2.6-block.git] / drivers / platform / x86 / ideapad-laptop.c
CommitLineData
58ac7aa0 1/*
a4b5a279 2 * ideapad-laptop.c - Lenovo IdeaPad ACPI Extras
58ac7aa0
DW
3 *
4 * Copyright © 2010 Intel Corporation
5 * Copyright © 2010 David Woodhouse <dwmw2@infradead.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301, USA.
21 */
22
9ab23989
JP
23#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
24
58ac7aa0
DW
25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/init.h>
28#include <linux/types.h>
8b48463f 29#include <linux/acpi.h>
58ac7aa0 30#include <linux/rfkill.h>
98ee6919 31#include <linux/platform_device.h>
f63409ae
IP
32#include <linux/input.h>
33#include <linux/input/sparse-keymap.h>
a4ecbb8a
IP
34#include <linux/backlight.h>
35#include <linux/fb.h>
773e3206
IP
36#include <linux/debugfs.h>
37#include <linux/seq_file.h>
07a4a4fc 38#include <linux/i8042.h>
85093f79 39#include <linux/dmi.h>
b3facd7b 40#include <linux/device.h>
26bff5f0 41#include <acpi/video.h>
58ac7aa0 42
c1f73658 43#define IDEAPAD_RFKILL_DEV_NUM (3)
58ac7aa0 44
3371f481
IP
45#define CFG_BT_BIT (16)
46#define CFG_3G_BIT (17)
47#define CFG_WIFI_BIT (18)
a84511f7 48#define CFG_CAMERA_BIT (19)
3371f481 49
2be1dc21
IP
50enum {
51 VPCCMD_R_VPC1 = 0x10,
52 VPCCMD_R_BL_MAX,
53 VPCCMD_R_BL,
54 VPCCMD_W_BL,
55 VPCCMD_R_WIFI,
56 VPCCMD_W_WIFI,
57 VPCCMD_R_BT,
58 VPCCMD_W_BT,
59 VPCCMD_R_BL_POWER,
60 VPCCMD_R_NOVO,
61 VPCCMD_R_VPC2,
62 VPCCMD_R_TOUCHPAD,
63 VPCCMD_W_TOUCHPAD,
64 VPCCMD_R_CAMERA,
65 VPCCMD_W_CAMERA,
66 VPCCMD_R_3G,
67 VPCCMD_W_3G,
68 VPCCMD_R_ODD, /* 0x21 */
0c7bbeb9
MM
69 VPCCMD_W_FAN,
70 VPCCMD_R_RF,
2be1dc21 71 VPCCMD_W_RF,
0c7bbeb9 72 VPCCMD_R_FAN = 0x2B,
296f9fe0 73 VPCCMD_R_SPECIAL_BUTTONS = 0x31,
2be1dc21
IP
74 VPCCMD_W_BL_POWER = 0x33,
75};
76
331e0ea2
ZR
77struct ideapad_rfk_priv {
78 int dev;
79 struct ideapad_private *priv;
80};
81
ce326329 82struct ideapad_private {
469f6434 83 struct acpi_device *adev;
c1f73658 84 struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
331e0ea2 85 struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM];
98ee6919 86 struct platform_device *platform_device;
f63409ae 87 struct input_dev *inputdev;
a4ecbb8a 88 struct backlight_device *blightdev;
773e3206 89 struct dentry *debug;
3371f481 90 unsigned long cfg;
ce363c2b 91 bool has_hw_rfkill_switch;
58ac7aa0
DW
92};
93
bfa97b7d
IP
94static bool no_bt_rfkill;
95module_param(no_bt_rfkill, bool, 0444);
96MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
97
6a09f21d
IP
98/*
99 * ACPI Helpers
100 */
101#define IDEAPAD_EC_TIMEOUT (100) /* in ms */
102
103static int read_method_int(acpi_handle handle, const char *method, int *val)
104{
105 acpi_status status;
106 unsigned long long result;
107
108 status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
109 if (ACPI_FAILURE(status)) {
110 *val = -1;
111 return -1;
112 } else {
113 *val = result;
114 return 0;
115 }
116}
117
118static int method_vpcr(acpi_handle handle, int cmd, int *ret)
119{
120 acpi_status status;
121 unsigned long long result;
122 struct acpi_object_list params;
123 union acpi_object in_obj;
124
125 params.count = 1;
126 params.pointer = &in_obj;
127 in_obj.type = ACPI_TYPE_INTEGER;
128 in_obj.integer.value = cmd;
129
130 status = acpi_evaluate_integer(handle, "VPCR", &params, &result);
131
132 if (ACPI_FAILURE(status)) {
133 *ret = -1;
134 return -1;
135 } else {
136 *ret = result;
137 return 0;
138 }
139}
140
141static int method_vpcw(acpi_handle handle, int cmd, int data)
142{
143 struct acpi_object_list params;
144 union acpi_object in_obj[2];
145 acpi_status status;
146
147 params.count = 2;
148 params.pointer = in_obj;
149 in_obj[0].type = ACPI_TYPE_INTEGER;
150 in_obj[0].integer.value = cmd;
151 in_obj[1].type = ACPI_TYPE_INTEGER;
152 in_obj[1].integer.value = data;
153
154 status = acpi_evaluate_object(handle, "VPCW", &params, NULL);
155 if (status != AE_OK)
156 return -1;
157 return 0;
158}
159
160static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data)
161{
162 int val;
163 unsigned long int end_jiffies;
164
165 if (method_vpcw(handle, 1, cmd))
166 return -1;
167
168 for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
169 time_before(jiffies, end_jiffies);) {
170 schedule();
171 if (method_vpcr(handle, 1, &val))
172 return -1;
173 if (val == 0) {
174 if (method_vpcr(handle, 0, &val))
175 return -1;
176 *data = val;
177 return 0;
178 }
179 }
180 pr_err("timeout in read_ec_cmd\n");
181 return -1;
182}
183
184static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
185{
186 int val;
187 unsigned long int end_jiffies;
188
189 if (method_vpcw(handle, 0, data))
190 return -1;
191 if (method_vpcw(handle, 1, cmd))
192 return -1;
193
194 for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
195 time_before(jiffies, end_jiffies);) {
196 schedule();
197 if (method_vpcr(handle, 1, &val))
198 return -1;
199 if (val == 0)
200 return 0;
201 }
202 pr_err("timeout in write_ec_cmd\n");
203 return -1;
204}
6a09f21d 205
773e3206
IP
206/*
207 * debugfs
208 */
773e3206
IP
209static int debugfs_status_show(struct seq_file *s, void *data)
210{
331e0ea2 211 struct ideapad_private *priv = s->private;
773e3206
IP
212 unsigned long value;
213
331e0ea2
ZR
214 if (!priv)
215 return -EINVAL;
216
217 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &value))
773e3206 218 seq_printf(s, "Backlight max:\t%lu\n", value);
331e0ea2 219 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL, &value))
773e3206 220 seq_printf(s, "Backlight now:\t%lu\n", value);
331e0ea2 221 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &value))
773e3206
IP
222 seq_printf(s, "BL power value:\t%s\n", value ? "On" : "Off");
223 seq_printf(s, "=====================\n");
224
331e0ea2 225 if (!read_ec_data(priv->adev->handle, VPCCMD_R_RF, &value))
773e3206
IP
226 seq_printf(s, "Radio status:\t%s(%lu)\n",
227 value ? "On" : "Off", value);
331e0ea2 228 if (!read_ec_data(priv->adev->handle, VPCCMD_R_WIFI, &value))
773e3206
IP
229 seq_printf(s, "Wifi status:\t%s(%lu)\n",
230 value ? "On" : "Off", value);
331e0ea2 231 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BT, &value))
773e3206
IP
232 seq_printf(s, "BT status:\t%s(%lu)\n",
233 value ? "On" : "Off", value);
331e0ea2 234 if (!read_ec_data(priv->adev->handle, VPCCMD_R_3G, &value))
773e3206
IP
235 seq_printf(s, "3G status:\t%s(%lu)\n",
236 value ? "On" : "Off", value);
237 seq_printf(s, "=====================\n");
238
331e0ea2 239 if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value))
773e3206
IP
240 seq_printf(s, "Touchpad status:%s(%lu)\n",
241 value ? "On" : "Off", value);
331e0ea2 242 if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value))
773e3206
IP
243 seq_printf(s, "Camera status:\t%s(%lu)\n",
244 value ? "On" : "Off", value);
245
246 return 0;
247}
248
249static int debugfs_status_open(struct inode *inode, struct file *file)
250{
331e0ea2 251 return single_open(file, debugfs_status_show, inode->i_private);
773e3206
IP
252}
253
254static const struct file_operations debugfs_status_fops = {
255 .owner = THIS_MODULE,
256 .open = debugfs_status_open,
257 .read = seq_read,
258 .llseek = seq_lseek,
259 .release = single_release,
260};
261
262static int debugfs_cfg_show(struct seq_file *s, void *data)
263{
331e0ea2
ZR
264 struct ideapad_private *priv = s->private;
265
266 if (!priv) {
773e3206
IP
267 seq_printf(s, "cfg: N/A\n");
268 } else {
269 seq_printf(s, "cfg: 0x%.8lX\n\nCapability: ",
331e0ea2
ZR
270 priv->cfg);
271 if (test_bit(CFG_BT_BIT, &priv->cfg))
773e3206 272 seq_printf(s, "Bluetooth ");
331e0ea2 273 if (test_bit(CFG_3G_BIT, &priv->cfg))
773e3206 274 seq_printf(s, "3G ");
331e0ea2 275 if (test_bit(CFG_WIFI_BIT, &priv->cfg))
773e3206 276 seq_printf(s, "Wireless ");
331e0ea2 277 if (test_bit(CFG_CAMERA_BIT, &priv->cfg))
773e3206
IP
278 seq_printf(s, "Camera ");
279 seq_printf(s, "\nGraphic: ");
331e0ea2 280 switch ((priv->cfg)&0x700) {
773e3206
IP
281 case 0x100:
282 seq_printf(s, "Intel");
283 break;
284 case 0x200:
285 seq_printf(s, "ATI");
286 break;
287 case 0x300:
288 seq_printf(s, "Nvidia");
289 break;
290 case 0x400:
291 seq_printf(s, "Intel and ATI");
292 break;
293 case 0x500:
294 seq_printf(s, "Intel and Nvidia");
295 break;
296 }
297 seq_printf(s, "\n");
298 }
299 return 0;
300}
301
302static int debugfs_cfg_open(struct inode *inode, struct file *file)
303{
331e0ea2 304 return single_open(file, debugfs_cfg_show, inode->i_private);
773e3206
IP
305}
306
307static const struct file_operations debugfs_cfg_fops = {
308 .owner = THIS_MODULE,
309 .open = debugfs_cfg_open,
310 .read = seq_read,
311 .llseek = seq_lseek,
312 .release = single_release,
313};
314
b859f159 315static int ideapad_debugfs_init(struct ideapad_private *priv)
773e3206
IP
316{
317 struct dentry *node;
318
319 priv->debug = debugfs_create_dir("ideapad", NULL);
320 if (priv->debug == NULL) {
321 pr_err("failed to create debugfs directory");
322 goto errout;
323 }
324
331e0ea2 325 node = debugfs_create_file("cfg", S_IRUGO, priv->debug, priv,
773e3206
IP
326 &debugfs_cfg_fops);
327 if (!node) {
328 pr_err("failed to create cfg in debugfs");
329 goto errout;
330 }
331
331e0ea2 332 node = debugfs_create_file("status", S_IRUGO, priv->debug, priv,
773e3206
IP
333 &debugfs_status_fops);
334 if (!node) {
a5c3892f 335 pr_err("failed to create status in debugfs");
773e3206
IP
336 goto errout;
337 }
338
339 return 0;
340
341errout:
342 return -ENOMEM;
343}
344
345static void ideapad_debugfs_exit(struct ideapad_private *priv)
346{
347 debugfs_remove_recursive(priv->debug);
348 priv->debug = NULL;
349}
350
a4b5a279 351/*
3371f481 352 * sysfs
a4b5a279 353 */
58ac7aa0
DW
354static ssize_t show_ideapad_cam(struct device *dev,
355 struct device_attribute *attr,
356 char *buf)
357{
26c81d5c 358 unsigned long result;
331e0ea2 359 struct ideapad_private *priv = dev_get_drvdata(dev);
58ac7aa0 360
331e0ea2 361 if (read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &result))
26c81d5c
IP
362 return sprintf(buf, "-1\n");
363 return sprintf(buf, "%lu\n", result);
58ac7aa0
DW
364}
365
366static ssize_t store_ideapad_cam(struct device *dev,
367 struct device_attribute *attr,
368 const char *buf, size_t count)
369{
370 int ret, state;
331e0ea2 371 struct ideapad_private *priv = dev_get_drvdata(dev);
58ac7aa0
DW
372
373 if (!count)
374 return 0;
375 if (sscanf(buf, "%i", &state) != 1)
376 return -EINVAL;
331e0ea2 377 ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_CAMERA, state);
58ac7aa0 378 if (ret < 0)
0c7bbeb9 379 return -EIO;
58ac7aa0
DW
380 return count;
381}
382
383static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
384
0c7bbeb9
MM
385static ssize_t show_ideapad_fan(struct device *dev,
386 struct device_attribute *attr,
387 char *buf)
388{
389 unsigned long result;
331e0ea2 390 struct ideapad_private *priv = dev_get_drvdata(dev);
0c7bbeb9 391
331e0ea2 392 if (read_ec_data(priv->adev->handle, VPCCMD_R_FAN, &result))
0c7bbeb9
MM
393 return sprintf(buf, "-1\n");
394 return sprintf(buf, "%lu\n", result);
395}
396
397static ssize_t store_ideapad_fan(struct device *dev,
398 struct device_attribute *attr,
399 const char *buf, size_t count)
400{
401 int ret, state;
331e0ea2 402 struct ideapad_private *priv = dev_get_drvdata(dev);
0c7bbeb9
MM
403
404 if (!count)
405 return 0;
406 if (sscanf(buf, "%i", &state) != 1)
407 return -EINVAL;
408 if (state < 0 || state > 4 || state == 3)
409 return -EINVAL;
331e0ea2 410 ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_FAN, state);
0c7bbeb9
MM
411 if (ret < 0)
412 return -EIO;
413 return count;
414}
415
416static DEVICE_ATTR(fan_mode, 0644, show_ideapad_fan, store_ideapad_fan);
417
3371f481
IP
418static struct attribute *ideapad_attributes[] = {
419 &dev_attr_camera_power.attr,
0c7bbeb9 420 &dev_attr_fan_mode.attr,
3371f481
IP
421 NULL
422};
423
587a1f16 424static umode_t ideapad_is_visible(struct kobject *kobj,
a84511f7
IP
425 struct attribute *attr,
426 int idx)
427{
428 struct device *dev = container_of(kobj, struct device, kobj);
429 struct ideapad_private *priv = dev_get_drvdata(dev);
430 bool supported;
431
432 if (attr == &dev_attr_camera_power.attr)
433 supported = test_bit(CFG_CAMERA_BIT, &(priv->cfg));
0c7bbeb9
MM
434 else if (attr == &dev_attr_fan_mode.attr) {
435 unsigned long value;
331e0ea2
ZR
436 supported = !read_ec_data(priv->adev->handle, VPCCMD_R_FAN,
437 &value);
0c7bbeb9 438 } else
a84511f7
IP
439 supported = true;
440
441 return supported ? attr->mode : 0;
442}
443
49458e83 444static const struct attribute_group ideapad_attribute_group = {
a84511f7 445 .is_visible = ideapad_is_visible,
3371f481
IP
446 .attrs = ideapad_attributes
447};
448
a4b5a279
IP
449/*
450 * Rfkill
451 */
c1f73658
IP
452struct ideapad_rfk_data {
453 char *name;
454 int cfgbit;
455 int opcode;
456 int type;
457};
458
b3d94d70 459static const struct ideapad_rfk_data ideapad_rfk_data[] = {
2be1dc21
IP
460 { "ideapad_wlan", CFG_WIFI_BIT, VPCCMD_W_WIFI, RFKILL_TYPE_WLAN },
461 { "ideapad_bluetooth", CFG_BT_BIT, VPCCMD_W_BT, RFKILL_TYPE_BLUETOOTH },
462 { "ideapad_3g", CFG_3G_BIT, VPCCMD_W_3G, RFKILL_TYPE_WWAN },
c1f73658
IP
463};
464
58ac7aa0
DW
465static int ideapad_rfk_set(void *data, bool blocked)
466{
331e0ea2 467 struct ideapad_rfk_priv *priv = data;
fa08359e 468
331e0ea2 469 return write_ec_cmd(priv->priv->adev->handle, priv->dev, !blocked);
58ac7aa0
DW
470}
471
472static struct rfkill_ops ideapad_rfk_ops = {
473 .set_block = ideapad_rfk_set,
474};
475
923de84a 476static void ideapad_sync_rfk_state(struct ideapad_private *priv)
58ac7aa0 477{
ce363c2b 478 unsigned long hw_blocked = 0;
58ac7aa0
DW
479 int i;
480
ce363c2b
HG
481 if (priv->has_hw_rfkill_switch) {
482 if (read_ec_data(priv->adev->handle, VPCCMD_R_RF, &hw_blocked))
483 return;
484 hw_blocked = !hw_blocked;
485 }
58ac7aa0 486
c1f73658 487 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
ce326329 488 if (priv->rfk[i])
2b7266bd 489 rfkill_set_hw_state(priv->rfk[i], hw_blocked);
58ac7aa0
DW
490}
491
75a11f11 492static int ideapad_register_rfkill(struct ideapad_private *priv, int dev)
58ac7aa0
DW
493{
494 int ret;
2b7266bd 495 unsigned long sw_blocked;
58ac7aa0 496
bfa97b7d
IP
497 if (no_bt_rfkill &&
498 (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) {
499 /* Force to enable bluetooth when no_bt_rfkill=1 */
331e0ea2 500 write_ec_cmd(priv->adev->handle,
bfa97b7d
IP
501 ideapad_rfk_data[dev].opcode, 1);
502 return 0;
503 }
331e0ea2
ZR
504 priv->rfk_priv[dev].dev = dev;
505 priv->rfk_priv[dev].priv = priv;
bfa97b7d 506
75a11f11 507 priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name,
b5c37b79 508 &priv->platform_device->dev,
75a11f11
ZR
509 ideapad_rfk_data[dev].type,
510 &ideapad_rfk_ops,
331e0ea2 511 &priv->rfk_priv[dev]);
ce326329 512 if (!priv->rfk[dev])
58ac7aa0
DW
513 return -ENOMEM;
514
331e0ea2 515 if (read_ec_data(priv->adev->handle, ideapad_rfk_data[dev].opcode-1,
2b7266bd
IP
516 &sw_blocked)) {
517 rfkill_init_sw_state(priv->rfk[dev], 0);
518 } else {
519 sw_blocked = !sw_blocked;
520 rfkill_init_sw_state(priv->rfk[dev], sw_blocked);
521 }
522
ce326329 523 ret = rfkill_register(priv->rfk[dev]);
58ac7aa0 524 if (ret) {
ce326329 525 rfkill_destroy(priv->rfk[dev]);
58ac7aa0
DW
526 return ret;
527 }
528 return 0;
529}
530
75a11f11 531static void ideapad_unregister_rfkill(struct ideapad_private *priv, int dev)
58ac7aa0 532{
ce326329 533 if (!priv->rfk[dev])
58ac7aa0
DW
534 return;
535
ce326329
DW
536 rfkill_unregister(priv->rfk[dev]);
537 rfkill_destroy(priv->rfk[dev]);
58ac7aa0
DW
538}
539
98ee6919
IP
540/*
541 * Platform device
542 */
b5c37b79 543static int ideapad_sysfs_init(struct ideapad_private *priv)
98ee6919 544{
b5c37b79 545 return sysfs_create_group(&priv->platform_device->dev.kobj,
c9f718d0 546 &ideapad_attribute_group);
98ee6919
IP
547}
548
b5c37b79 549static void ideapad_sysfs_exit(struct ideapad_private *priv)
98ee6919 550{
8693ae84 551 sysfs_remove_group(&priv->platform_device->dev.kobj,
c9f718d0 552 &ideapad_attribute_group);
98ee6919 553}
98ee6919 554
f63409ae
IP
555/*
556 * input device
557 */
558static const struct key_entry ideapad_keymap[] = {
f43d9ec0 559 { KE_KEY, 6, { KEY_SWITCHVIDEOMODE } },
296f9fe0
MM
560 { KE_KEY, 7, { KEY_CAMERA } },
561 { KE_KEY, 11, { KEY_F16 } },
f43d9ec0
IP
562 { KE_KEY, 13, { KEY_WLAN } },
563 { KE_KEY, 16, { KEY_PROG1 } },
564 { KE_KEY, 17, { KEY_PROG2 } },
296f9fe0
MM
565 { KE_KEY, 64, { KEY_PROG3 } },
566 { KE_KEY, 65, { KEY_PROG4 } },
07a4a4fc
MM
567 { KE_KEY, 66, { KEY_TOUCHPAD_OFF } },
568 { KE_KEY, 67, { KEY_TOUCHPAD_ON } },
f63409ae
IP
569 { KE_END, 0 },
570};
571
b859f159 572static int ideapad_input_init(struct ideapad_private *priv)
f63409ae
IP
573{
574 struct input_dev *inputdev;
575 int error;
576
577 inputdev = input_allocate_device();
b222cca6 578 if (!inputdev)
f63409ae 579 return -ENOMEM;
f63409ae
IP
580
581 inputdev->name = "Ideapad extra buttons";
582 inputdev->phys = "ideapad/input0";
583 inputdev->id.bustype = BUS_HOST;
8693ae84 584 inputdev->dev.parent = &priv->platform_device->dev;
f63409ae
IP
585
586 error = sparse_keymap_setup(inputdev, ideapad_keymap, NULL);
587 if (error) {
588 pr_err("Unable to setup input device keymap\n");
589 goto err_free_dev;
590 }
591
592 error = input_register_device(inputdev);
593 if (error) {
594 pr_err("Unable to register input device\n");
595 goto err_free_keymap;
596 }
597
8693ae84 598 priv->inputdev = inputdev;
f63409ae
IP
599 return 0;
600
601err_free_keymap:
602 sparse_keymap_free(inputdev);
603err_free_dev:
604 input_free_device(inputdev);
605 return error;
606}
607
7451a55a 608static void ideapad_input_exit(struct ideapad_private *priv)
f63409ae 609{
8693ae84
IP
610 sparse_keymap_free(priv->inputdev);
611 input_unregister_device(priv->inputdev);
612 priv->inputdev = NULL;
f63409ae
IP
613}
614
8693ae84
IP
615static void ideapad_input_report(struct ideapad_private *priv,
616 unsigned long scancode)
f63409ae 617{
8693ae84 618 sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
f63409ae 619}
f63409ae 620
f43d9ec0
IP
621static void ideapad_input_novokey(struct ideapad_private *priv)
622{
623 unsigned long long_pressed;
624
331e0ea2 625 if (read_ec_data(priv->adev->handle, VPCCMD_R_NOVO, &long_pressed))
f43d9ec0
IP
626 return;
627 if (long_pressed)
628 ideapad_input_report(priv, 17);
629 else
630 ideapad_input_report(priv, 16);
631}
632
296f9fe0
MM
633static void ideapad_check_special_buttons(struct ideapad_private *priv)
634{
635 unsigned long bit, value;
636
331e0ea2 637 read_ec_data(priv->adev->handle, VPCCMD_R_SPECIAL_BUTTONS, &value);
296f9fe0
MM
638
639 for (bit = 0; bit < 16; bit++) {
640 if (test_bit(bit, &value)) {
641 switch (bit) {
a1ec56ed
MM
642 case 0: /* Z580 */
643 case 6: /* Z570 */
296f9fe0
MM
644 /* Thermal Management button */
645 ideapad_input_report(priv, 65);
646 break;
647 case 1:
648 /* OneKey Theater button */
649 ideapad_input_report(priv, 64);
650 break;
a1ec56ed
MM
651 default:
652 pr_info("Unknown special button: %lu\n", bit);
653 break;
296f9fe0
MM
654 }
655 }
656 }
657}
658
a4ecbb8a
IP
659/*
660 * backlight
661 */
662static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)
663{
331e0ea2 664 struct ideapad_private *priv = bl_get_data(blightdev);
a4ecbb8a
IP
665 unsigned long now;
666
331e0ea2
ZR
667 if (!priv)
668 return -EINVAL;
669
670 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now))
a4ecbb8a
IP
671 return -EIO;
672 return now;
673}
674
675static int ideapad_backlight_update_status(struct backlight_device *blightdev)
676{
331e0ea2
ZR
677 struct ideapad_private *priv = bl_get_data(blightdev);
678
679 if (!priv)
680 return -EINVAL;
681
682 if (write_ec_cmd(priv->adev->handle, VPCCMD_W_BL,
2be1dc21 683 blightdev->props.brightness))
a4ecbb8a 684 return -EIO;
331e0ea2 685 if (write_ec_cmd(priv->adev->handle, VPCCMD_W_BL_POWER,
a4ecbb8a
IP
686 blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1))
687 return -EIO;
688
689 return 0;
690}
691
692static const struct backlight_ops ideapad_backlight_ops = {
693 .get_brightness = ideapad_backlight_get_brightness,
694 .update_status = ideapad_backlight_update_status,
695};
696
697static int ideapad_backlight_init(struct ideapad_private *priv)
698{
699 struct backlight_device *blightdev;
700 struct backlight_properties props;
701 unsigned long max, now, power;
702
331e0ea2 703 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &max))
a4ecbb8a 704 return -EIO;
331e0ea2 705 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now))
a4ecbb8a 706 return -EIO;
331e0ea2 707 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))
a4ecbb8a
IP
708 return -EIO;
709
710 memset(&props, 0, sizeof(struct backlight_properties));
711 props.max_brightness = max;
712 props.type = BACKLIGHT_PLATFORM;
713 blightdev = backlight_device_register("ideapad",
714 &priv->platform_device->dev,
715 priv,
716 &ideapad_backlight_ops,
717 &props);
718 if (IS_ERR(blightdev)) {
719 pr_err("Could not register backlight device\n");
720 return PTR_ERR(blightdev);
721 }
722
723 priv->blightdev = blightdev;
724 blightdev->props.brightness = now;
725 blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
726 backlight_update_status(blightdev);
727
728 return 0;
729}
730
731static void ideapad_backlight_exit(struct ideapad_private *priv)
732{
00981810 733 backlight_device_unregister(priv->blightdev);
a4ecbb8a
IP
734 priv->blightdev = NULL;
735}
736
737static void ideapad_backlight_notify_power(struct ideapad_private *priv)
738{
739 unsigned long power;
740 struct backlight_device *blightdev = priv->blightdev;
741
d4afc775
RB
742 if (!blightdev)
743 return;
331e0ea2 744 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))
a4ecbb8a
IP
745 return;
746 blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
747}
748
749static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
750{
751 unsigned long now;
752
753 /* if we control brightness via acpi video driver */
754 if (priv->blightdev == NULL) {
331e0ea2 755 read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now);
a4ecbb8a
IP
756 return;
757 }
758
759 backlight_force_update(priv->blightdev, BACKLIGHT_UPDATE_HOTKEY);
760}
761
a4b5a279
IP
762/*
763 * module init/exit
764 */
75a11f11 765static void ideapad_sync_touchpad_state(struct ideapad_private *priv)
07a4a4fc 766{
07a4a4fc
MM
767 unsigned long value;
768
769 /* Without reading from EC touchpad LED doesn't switch state */
75a11f11 770 if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value)) {
07a4a4fc
MM
771 /* Some IdeaPads don't really turn off touchpad - they only
772 * switch the LED state. We (de)activate KBC AUX port to turn
773 * touchpad off and on. We send KEY_TOUCHPAD_OFF and
774 * KEY_TOUCHPAD_ON to not to get out of sync with LED */
775 unsigned char param;
776 i8042_command(&param, value ? I8042_CMD_AUX_ENABLE :
777 I8042_CMD_AUX_DISABLE);
778 ideapad_input_report(priv, value ? 67 : 66);
779 }
780}
781
b5c37b79
ZR
782static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)
783{
784 struct ideapad_private *priv = data;
785 unsigned long vpc1, vpc2, vpc_bit;
786
787 if (read_ec_data(handle, VPCCMD_R_VPC1, &vpc1))
788 return;
789 if (read_ec_data(handle, VPCCMD_R_VPC2, &vpc2))
790 return;
791
792 vpc1 = (vpc2 << 8) | vpc1;
793 for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) {
794 if (test_bit(vpc_bit, &vpc1)) {
795 switch (vpc_bit) {
796 case 9:
797 ideapad_sync_rfk_state(priv);
798 break;
799 case 13:
800 case 11:
801 case 7:
802 case 6:
803 ideapad_input_report(priv, vpc_bit);
804 break;
805 case 5:
806 ideapad_sync_touchpad_state(priv);
807 break;
808 case 4:
809 ideapad_backlight_notify_brightness(priv);
810 break;
811 case 3:
812 ideapad_input_novokey(priv);
813 break;
814 case 2:
815 ideapad_backlight_notify_power(priv);
816 break;
817 case 0:
818 ideapad_check_special_buttons(priv);
819 break;
820 default:
821 pr_info("Unknown event: %lu\n", vpc_bit);
822 }
823 }
824 }
825}
826
ce363c2b
HG
827/*
828 * Some ideapads don't have a hardware rfkill switch, reading VPCCMD_R_RF
829 * always results in 0 on these models, causing ideapad_laptop to wrongly
830 * report all radios as hardware-blocked.
831 */
b3d94d70 832static const struct dmi_system_id no_hw_rfkill_list[] = {
9b071a43
PC
833 {
834 .ident = "Lenovo G40-30",
835 .matches = {
836 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
837 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo G40-30"),
838 },
839 },
85093f79 840 {
ce363c2b 841 .ident = "Lenovo Yoga 2 11 / 13 / Pro",
85093f79
HG
842 .matches = {
843 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
ce363c2b 844 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 2"),
85093f79
HG
845 },
846 },
725c7f61
SM
847 {
848 .ident = "Lenovo Yoga 3 Pro 1370",
849 .matches = {
850 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
851 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 3 Pro-1370"),
852 },
853 },
85093f79
HG
854 {}
855};
856
b5c37b79 857static int ideapad_acpi_add(struct platform_device *pdev)
58ac7aa0 858{
3371f481 859 int ret, i;
57f9616b 860 int cfg;
ce326329 861 struct ideapad_private *priv;
b5c37b79
ZR
862 struct acpi_device *adev;
863
864 ret = acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev);
865 if (ret)
866 return -ENODEV;
58ac7aa0 867
469f6434 868 if (read_method_int(adev->handle, "_CFG", &cfg))
6f8371c0
IP
869 return -ENODEV;
870
b3facd7b 871 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
ce326329
DW
872 if (!priv)
873 return -ENOMEM;
b5c37b79
ZR
874
875 dev_set_drvdata(&pdev->dev, priv);
3371f481 876 priv->cfg = cfg;
469f6434 877 priv->adev = adev;
b5c37b79 878 priv->platform_device = pdev;
ce363c2b 879 priv->has_hw_rfkill_switch = !dmi_check_system(no_hw_rfkill_list);
98ee6919 880
b5c37b79 881 ret = ideapad_sysfs_init(priv);
98ee6919 882 if (ret)
b3facd7b 883 return ret;
ce326329 884
773e3206
IP
885 ret = ideapad_debugfs_init(priv);
886 if (ret)
887 goto debugfs_failed;
888
8693ae84 889 ret = ideapad_input_init(priv);
f63409ae
IP
890 if (ret)
891 goto input_failed;
892
ce363c2b
HG
893 /*
894 * On some models without a hw-switch (the yoga 2 13 at least)
895 * VPCCMD_W_RF must be explicitly set to 1 for the wifi to work.
896 */
897 if (!priv->has_hw_rfkill_switch)
898 write_ec_cmd(priv->adev->handle, VPCCMD_W_RF, 1);
899
900 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
901 if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg))
902 ideapad_register_rfkill(priv, i);
903
923de84a 904 ideapad_sync_rfk_state(priv);
75a11f11 905 ideapad_sync_touchpad_state(priv);
c9f718d0 906
26bff5f0 907 if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
a4ecbb8a
IP
908 ret = ideapad_backlight_init(priv);
909 if (ret && ret != -ENODEV)
910 goto backlight_failed;
911 }
b5c37b79
ZR
912 ret = acpi_install_notify_handler(adev->handle,
913 ACPI_DEVICE_NOTIFY, ideapad_acpi_notify, priv);
914 if (ret)
915 goto notification_failed;
a4ecbb8a 916
58ac7aa0 917 return 0;
b5c37b79
ZR
918notification_failed:
919 ideapad_backlight_exit(priv);
a4ecbb8a
IP
920backlight_failed:
921 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
75a11f11 922 ideapad_unregister_rfkill(priv, i);
7451a55a 923 ideapad_input_exit(priv);
f63409ae 924input_failed:
773e3206
IP
925 ideapad_debugfs_exit(priv);
926debugfs_failed:
b5c37b79 927 ideapad_sysfs_exit(priv);
98ee6919 928 return ret;
58ac7aa0
DW
929}
930
b5c37b79 931static int ideapad_acpi_remove(struct platform_device *pdev)
58ac7aa0 932{
b5c37b79 933 struct ideapad_private *priv = dev_get_drvdata(&pdev->dev);
58ac7aa0 934 int i;
ce326329 935
b5c37b79
ZR
936 acpi_remove_notify_handler(priv->adev->handle,
937 ACPI_DEVICE_NOTIFY, ideapad_acpi_notify);
a4ecbb8a 938 ideapad_backlight_exit(priv);
c1f73658 939 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
75a11f11 940 ideapad_unregister_rfkill(priv, i);
8693ae84 941 ideapad_input_exit(priv);
773e3206 942 ideapad_debugfs_exit(priv);
b5c37b79
ZR
943 ideapad_sysfs_exit(priv);
944 dev_set_drvdata(&pdev->dev, NULL);
c9f718d0 945
58ac7aa0
DW
946 return 0;
947}
948
11fa8da5 949#ifdef CONFIG_PM_SLEEP
07a4a4fc
MM
950static int ideapad_acpi_resume(struct device *device)
951{
75a11f11
ZR
952 struct ideapad_private *priv;
953
954 if (!device)
955 return -EINVAL;
956 priv = dev_get_drvdata(device);
957
958 ideapad_sync_rfk_state(priv);
959 ideapad_sync_touchpad_state(priv);
07a4a4fc
MM
960 return 0;
961}
11fa8da5 962#endif
b5c37b79 963static SIMPLE_DEV_PM_OPS(ideapad_pm, NULL, ideapad_acpi_resume);
07a4a4fc 964
b5c37b79
ZR
965static const struct acpi_device_id ideapad_device_ids[] = {
966 { "VPC2004", 0},
967 { "", 0},
58ac7aa0 968};
b5c37b79
ZR
969MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
970
971static struct platform_driver ideapad_acpi_driver = {
972 .probe = ideapad_acpi_add,
973 .remove = ideapad_acpi_remove,
974 .driver = {
975 .name = "ideapad_acpi",
b5c37b79
ZR
976 .pm = &ideapad_pm,
977 .acpi_match_table = ACPI_PTR(ideapad_device_ids),
978 },
979};
980
981module_platform_driver(ideapad_acpi_driver);
58ac7aa0
DW
982
983MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
984MODULE_DESCRIPTION("IdeaPad ACPI Extras");
985MODULE_LICENSE("GPL");