aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform/x86/ideapad-laptop.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86/ideapad-laptop.c')
-rw-r--r--drivers/platform/x86/ideapad-laptop.c251
1 files changed, 217 insertions, 34 deletions
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index 0c595410e788..a36addf106a0 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -34,6 +34,8 @@
34#include <linux/input/sparse-keymap.h> 34#include <linux/input/sparse-keymap.h>
35#include <linux/backlight.h> 35#include <linux/backlight.h>
36#include <linux/fb.h> 36#include <linux/fb.h>
37#include <linux/debugfs.h>
38#include <linux/seq_file.h>
37 39
38#define IDEAPAD_RFKILL_DEV_NUM (3) 40#define IDEAPAD_RFKILL_DEV_NUM (3)
39 41
@@ -42,15 +44,41 @@
42#define CFG_WIFI_BIT (18) 44#define CFG_WIFI_BIT (18)
43#define CFG_CAMERA_BIT (19) 45#define CFG_CAMERA_BIT (19)
44 46
47enum {
48 VPCCMD_R_VPC1 = 0x10,
49 VPCCMD_R_BL_MAX,
50 VPCCMD_R_BL,
51 VPCCMD_W_BL,
52 VPCCMD_R_WIFI,
53 VPCCMD_W_WIFI,
54 VPCCMD_R_BT,
55 VPCCMD_W_BT,
56 VPCCMD_R_BL_POWER,
57 VPCCMD_R_NOVO,
58 VPCCMD_R_VPC2,
59 VPCCMD_R_TOUCHPAD,
60 VPCCMD_W_TOUCHPAD,
61 VPCCMD_R_CAMERA,
62 VPCCMD_W_CAMERA,
63 VPCCMD_R_3G,
64 VPCCMD_W_3G,
65 VPCCMD_R_ODD, /* 0x21 */
66 VPCCMD_R_RF = 0x23,
67 VPCCMD_W_RF,
68 VPCCMD_W_BL_POWER = 0x33,
69};
70
45struct ideapad_private { 71struct ideapad_private {
46 struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM]; 72 struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
47 struct platform_device *platform_device; 73 struct platform_device *platform_device;
48 struct input_dev *inputdev; 74 struct input_dev *inputdev;
49 struct backlight_device *blightdev; 75 struct backlight_device *blightdev;
76 struct dentry *debug;
50 unsigned long cfg; 77 unsigned long cfg;
51}; 78};
52 79
53static acpi_handle ideapad_handle; 80static acpi_handle ideapad_handle;
81static struct ideapad_private *ideapad_priv;
54static bool no_bt_rfkill; 82static bool no_bt_rfkill;
55module_param(no_bt_rfkill, bool, 0444); 83module_param(no_bt_rfkill, bool, 0444);
56MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth."); 84MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
@@ -164,6 +192,146 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
164} 192}
165 193
166/* 194/*
195 * debugfs
196 */
197#define DEBUGFS_EVENT_LEN (4096)
198static int debugfs_status_show(struct seq_file *s, void *data)
199{
200 unsigned long value;
201
202 if (!read_ec_data(ideapad_handle, VPCCMD_R_BL_MAX, &value))
203 seq_printf(s, "Backlight max:\t%lu\n", value);
204 if (!read_ec_data(ideapad_handle, VPCCMD_R_BL, &value))
205 seq_printf(s, "Backlight now:\t%lu\n", value);
206 if (!read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &value))
207 seq_printf(s, "BL power value:\t%s\n", value ? "On" : "Off");
208 seq_printf(s, "=====================\n");
209
210 if (!read_ec_data(ideapad_handle, VPCCMD_R_RF, &value))
211 seq_printf(s, "Radio status:\t%s(%lu)\n",
212 value ? "On" : "Off", value);
213 if (!read_ec_data(ideapad_handle, VPCCMD_R_WIFI, &value))
214 seq_printf(s, "Wifi status:\t%s(%lu)\n",
215 value ? "On" : "Off", value);
216 if (!read_ec_data(ideapad_handle, VPCCMD_R_BT, &value))
217 seq_printf(s, "BT status:\t%s(%lu)\n",
218 value ? "On" : "Off", value);
219 if (!read_ec_data(ideapad_handle, VPCCMD_R_3G, &value))
220 seq_printf(s, "3G status:\t%s(%lu)\n",
221 value ? "On" : "Off", value);
222 seq_printf(s, "=====================\n");
223
224 if (!read_ec_data(ideapad_handle, VPCCMD_R_TOUCHPAD, &value))
225 seq_printf(s, "Touchpad status:%s(%lu)\n",
226 value ? "On" : "Off", value);
227 if (!read_ec_data(ideapad_handle, VPCCMD_R_CAMERA, &value))
228 seq_printf(s, "Camera status:\t%s(%lu)\n",
229 value ? "On" : "Off", value);
230
231 return 0;
232}
233
234static int debugfs_status_open(struct inode *inode, struct file *file)
235{
236 return single_open(file, debugfs_status_show, NULL);
237}
238
239static const struct file_operations debugfs_status_fops = {
240 .owner = THIS_MODULE,
241 .open = debugfs_status_open,
242 .read = seq_read,
243 .llseek = seq_lseek,
244 .release = single_release,
245};
246
247static int debugfs_cfg_show(struct seq_file *s, void *data)
248{
249 if (!ideapad_priv) {
250 seq_printf(s, "cfg: N/A\n");
251 } else {
252 seq_printf(s, "cfg: 0x%.8lX\n\nCapability: ",
253 ideapad_priv->cfg);
254 if (test_bit(CFG_BT_BIT, &ideapad_priv->cfg))
255 seq_printf(s, "Bluetooth ");
256 if (test_bit(CFG_3G_BIT, &ideapad_priv->cfg))
257 seq_printf(s, "3G ");
258 if (test_bit(CFG_WIFI_BIT, &ideapad_priv->cfg))
259 seq_printf(s, "Wireless ");
260 if (test_bit(CFG_CAMERA_BIT, &ideapad_priv->cfg))
261 seq_printf(s, "Camera ");
262 seq_printf(s, "\nGraphic: ");
263 switch ((ideapad_priv->cfg)&0x700) {
264 case 0x100:
265 seq_printf(s, "Intel");
266 break;
267 case 0x200:
268 seq_printf(s, "ATI");
269 break;
270 case 0x300:
271 seq_printf(s, "Nvidia");
272 break;
273 case 0x400:
274 seq_printf(s, "Intel and ATI");
275 break;
276 case 0x500:
277 seq_printf(s, "Intel and Nvidia");
278 break;
279 }
280 seq_printf(s, "\n");
281 }
282 return 0;
283}
284
285static int debugfs_cfg_open(struct inode *inode, struct file *file)
286{
287 return single_open(file, debugfs_cfg_show, NULL);
288}
289
290static const struct file_operations debugfs_cfg_fops = {
291 .owner = THIS_MODULE,
292 .open = debugfs_cfg_open,
293 .read = seq_read,
294 .llseek = seq_lseek,
295 .release = single_release,
296};
297
298static int __devinit ideapad_debugfs_init(struct ideapad_private *priv)
299{
300 struct dentry *node;
301
302 priv->debug = debugfs_create_dir("ideapad", NULL);
303 if (priv->debug == NULL) {
304 pr_err("failed to create debugfs directory");
305 goto errout;
306 }
307
308 node = debugfs_create_file("cfg", S_IRUGO, priv->debug, NULL,
309 &debugfs_cfg_fops);
310 if (!node) {
311 pr_err("failed to create cfg in debugfs");
312 goto errout;
313 }
314
315 node = debugfs_create_file("status", S_IRUGO, priv->debug, NULL,
316 &debugfs_status_fops);
317 if (!node) {
318 pr_err("failed to create event in debugfs");
319 goto errout;
320 }
321
322 return 0;
323
324errout:
325 return -ENOMEM;
326}
327
328static void ideapad_debugfs_exit(struct ideapad_private *priv)
329{
330 debugfs_remove_recursive(priv->debug);
331 priv->debug = NULL;
332}
333
334/*
167 * sysfs 335 * sysfs
168 */ 336 */
169static ssize_t show_ideapad_cam(struct device *dev, 337static ssize_t show_ideapad_cam(struct device *dev,
@@ -172,7 +340,7 @@ static ssize_t show_ideapad_cam(struct device *dev,
172{ 340{
173 unsigned long result; 341 unsigned long result;
174 342
175 if (read_ec_data(ideapad_handle, 0x1D, &result)) 343 if (read_ec_data(ideapad_handle, VPCCMD_R_CAMERA, &result))
176 return sprintf(buf, "-1\n"); 344 return sprintf(buf, "-1\n");
177 return sprintf(buf, "%lu\n", result); 345 return sprintf(buf, "%lu\n", result);
178} 346}
@@ -187,7 +355,7 @@ static ssize_t store_ideapad_cam(struct device *dev,
187 return 0; 355 return 0;
188 if (sscanf(buf, "%i", &state) != 1) 356 if (sscanf(buf, "%i", &state) != 1)
189 return -EINVAL; 357 return -EINVAL;
190 ret = write_ec_cmd(ideapad_handle, 0x1E, state); 358 ret = write_ec_cmd(ideapad_handle, VPCCMD_W_CAMERA, state);
191 if (ret < 0) 359 if (ret < 0)
192 return ret; 360 return ret;
193 return count; 361 return count;
@@ -195,20 +363,8 @@ static ssize_t store_ideapad_cam(struct device *dev,
195 363
196static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam); 364static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
197 365
198static ssize_t show_ideapad_cfg(struct device *dev,
199 struct device_attribute *attr,
200 char *buf)
201{
202 struct ideapad_private *priv = dev_get_drvdata(dev);
203
204 return sprintf(buf, "0x%.8lX\n", priv->cfg);
205}
206
207static DEVICE_ATTR(cfg, 0444, show_ideapad_cfg, NULL);
208
209static struct attribute *ideapad_attributes[] = { 366static struct attribute *ideapad_attributes[] = {
210 &dev_attr_camera_power.attr, 367 &dev_attr_camera_power.attr,
211 &dev_attr_cfg.attr,
212 NULL 368 NULL
213}; 369};
214 370
@@ -244,9 +400,9 @@ struct ideapad_rfk_data {
244}; 400};
245 401
246const struct ideapad_rfk_data ideapad_rfk_data[] = { 402const struct ideapad_rfk_data ideapad_rfk_data[] = {
247 { "ideapad_wlan", CFG_WIFI_BIT, 0x15, RFKILL_TYPE_WLAN }, 403 { "ideapad_wlan", CFG_WIFI_BIT, VPCCMD_W_WIFI, RFKILL_TYPE_WLAN },
248 { "ideapad_bluetooth", CFG_BT_BIT, 0x17, RFKILL_TYPE_BLUETOOTH }, 404 { "ideapad_bluetooth", CFG_BT_BIT, VPCCMD_W_BT, RFKILL_TYPE_BLUETOOTH },
249 { "ideapad_3g", CFG_3G_BIT, 0x20, RFKILL_TYPE_WWAN }, 405 { "ideapad_3g", CFG_3G_BIT, VPCCMD_W_3G, RFKILL_TYPE_WWAN },
250}; 406};
251 407
252static int ideapad_rfk_set(void *data, bool blocked) 408static int ideapad_rfk_set(void *data, bool blocked)
@@ -260,13 +416,12 @@ static struct rfkill_ops ideapad_rfk_ops = {
260 .set_block = ideapad_rfk_set, 416 .set_block = ideapad_rfk_set,
261}; 417};
262 418
263static void ideapad_sync_rfk_state(struct acpi_device *adevice) 419static void ideapad_sync_rfk_state(struct ideapad_private *priv)
264{ 420{
265 struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
266 unsigned long hw_blocked; 421 unsigned long hw_blocked;
267 int i; 422 int i;
268 423
269 if (read_ec_data(ideapad_handle, 0x23, &hw_blocked)) 424 if (read_ec_data(ideapad_handle, VPCCMD_R_RF, &hw_blocked))
270 return; 425 return;
271 hw_blocked = !hw_blocked; 426 hw_blocked = !hw_blocked;
272 427
@@ -363,8 +518,10 @@ static void ideapad_platform_exit(struct ideapad_private *priv)
363 * input device 518 * input device
364 */ 519 */
365static const struct key_entry ideapad_keymap[] = { 520static const struct key_entry ideapad_keymap[] = {
366 { KE_KEY, 0x06, { KEY_SWITCHVIDEOMODE } }, 521 { KE_KEY, 6, { KEY_SWITCHVIDEOMODE } },
367 { KE_KEY, 0x0D, { KEY_WLAN } }, 522 { KE_KEY, 13, { KEY_WLAN } },
523 { KE_KEY, 16, { KEY_PROG1 } },
524 { KE_KEY, 17, { KEY_PROG2 } },
368 { KE_END, 0 }, 525 { KE_END, 0 },
369}; 526};
370 527
@@ -419,6 +576,18 @@ static void ideapad_input_report(struct ideapad_private *priv,
419 sparse_keymap_report_event(priv->inputdev, scancode, 1, true); 576 sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
420} 577}
421 578
579static void ideapad_input_novokey(struct ideapad_private *priv)
580{
581 unsigned long long_pressed;
582
583 if (read_ec_data(ideapad_handle, VPCCMD_R_NOVO, &long_pressed))
584 return;
585 if (long_pressed)
586 ideapad_input_report(priv, 17);
587 else
588 ideapad_input_report(priv, 16);
589}
590
422/* 591/*
423 * backlight 592 * backlight
424 */ 593 */
@@ -426,16 +595,17 @@ static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)
426{ 595{
427 unsigned long now; 596 unsigned long now;
428 597
429 if (read_ec_data(ideapad_handle, 0x12, &now)) 598 if (read_ec_data(ideapad_handle, VPCCMD_R_BL, &now))
430 return -EIO; 599 return -EIO;
431 return now; 600 return now;
432} 601}
433 602
434static int ideapad_backlight_update_status(struct backlight_device *blightdev) 603static int ideapad_backlight_update_status(struct backlight_device *blightdev)
435{ 604{
436 if (write_ec_cmd(ideapad_handle, 0x13, blightdev->props.brightness)) 605 if (write_ec_cmd(ideapad_handle, VPCCMD_W_BL,
606 blightdev->props.brightness))
437 return -EIO; 607 return -EIO;
438 if (write_ec_cmd(ideapad_handle, 0x33, 608 if (write_ec_cmd(ideapad_handle, VPCCMD_W_BL_POWER,
439 blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1)) 609 blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1))
440 return -EIO; 610 return -EIO;
441 611
@@ -453,11 +623,11 @@ static int ideapad_backlight_init(struct ideapad_private *priv)
453 struct backlight_properties props; 623 struct backlight_properties props;
454 unsigned long max, now, power; 624 unsigned long max, now, power;
455 625
456 if (read_ec_data(ideapad_handle, 0x11, &max)) 626 if (read_ec_data(ideapad_handle, VPCCMD_R_BL_MAX, &max))
457 return -EIO; 627 return -EIO;
458 if (read_ec_data(ideapad_handle, 0x12, &now)) 628 if (read_ec_data(ideapad_handle, VPCCMD_R_BL, &now))
459 return -EIO; 629 return -EIO;
460 if (read_ec_data(ideapad_handle, 0x18, &power)) 630 if (read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &power))
461 return -EIO; 631 return -EIO;
462 632
463 memset(&props, 0, sizeof(struct backlight_properties)); 633 memset(&props, 0, sizeof(struct backlight_properties));
@@ -493,7 +663,9 @@ static void ideapad_backlight_notify_power(struct ideapad_private *priv)
493 unsigned long power; 663 unsigned long power;
494 struct backlight_device *blightdev = priv->blightdev; 664 struct backlight_device *blightdev = priv->blightdev;
495 665
496 if (read_ec_data(ideapad_handle, 0x18, &power)) 666 if (!blightdev)
667 return;
668 if (read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &power))
497 return; 669 return;
498 blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; 670 blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
499} 671}
@@ -504,7 +676,7 @@ static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
504 676
505 /* if we control brightness via acpi video driver */ 677 /* if we control brightness via acpi video driver */
506 if (priv->blightdev == NULL) { 678 if (priv->blightdev == NULL) {
507 read_ec_data(ideapad_handle, 0x12, &now); 679 read_ec_data(ideapad_handle, VPCCMD_R_BL, &now);
508 return; 680 return;
509 } 681 }
510 682
@@ -533,6 +705,7 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
533 if (!priv) 705 if (!priv)
534 return -ENOMEM; 706 return -ENOMEM;
535 dev_set_drvdata(&adevice->dev, priv); 707 dev_set_drvdata(&adevice->dev, priv);
708 ideapad_priv = priv;
536 ideapad_handle = adevice->handle; 709 ideapad_handle = adevice->handle;
537 priv->cfg = cfg; 710 priv->cfg = cfg;
538 711
@@ -540,6 +713,10 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
540 if (ret) 713 if (ret)
541 goto platform_failed; 714 goto platform_failed;
542 715
716 ret = ideapad_debugfs_init(priv);
717 if (ret)
718 goto debugfs_failed;
719
543 ret = ideapad_input_init(priv); 720 ret = ideapad_input_init(priv);
544 if (ret) 721 if (ret)
545 goto input_failed; 722 goto input_failed;
@@ -550,7 +727,7 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
550 else 727 else
551 priv->rfk[i] = NULL; 728 priv->rfk[i] = NULL;
552 } 729 }
553 ideapad_sync_rfk_state(adevice); 730 ideapad_sync_rfk_state(priv);
554 731
555 if (!acpi_video_backlight_support()) { 732 if (!acpi_video_backlight_support()) {
556 ret = ideapad_backlight_init(priv); 733 ret = ideapad_backlight_init(priv);
@@ -565,6 +742,8 @@ backlight_failed:
565 ideapad_unregister_rfkill(adevice, i); 742 ideapad_unregister_rfkill(adevice, i);
566 ideapad_input_exit(priv); 743 ideapad_input_exit(priv);
567input_failed: 744input_failed:
745 ideapad_debugfs_exit(priv);
746debugfs_failed:
568 ideapad_platform_exit(priv); 747 ideapad_platform_exit(priv);
569platform_failed: 748platform_failed:
570 kfree(priv); 749 kfree(priv);
@@ -580,6 +759,7 @@ static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type)
580 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) 759 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
581 ideapad_unregister_rfkill(adevice, i); 760 ideapad_unregister_rfkill(adevice, i);
582 ideapad_input_exit(priv); 761 ideapad_input_exit(priv);
762 ideapad_debugfs_exit(priv);
583 ideapad_platform_exit(priv); 763 ideapad_platform_exit(priv);
584 dev_set_drvdata(&adevice->dev, NULL); 764 dev_set_drvdata(&adevice->dev, NULL);
585 kfree(priv); 765 kfree(priv);
@@ -593,9 +773,9 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
593 acpi_handle handle = adevice->handle; 773 acpi_handle handle = adevice->handle;
594 unsigned long vpc1, vpc2, vpc_bit; 774 unsigned long vpc1, vpc2, vpc_bit;
595 775
596 if (read_ec_data(handle, 0x10, &vpc1)) 776 if (read_ec_data(handle, VPCCMD_R_VPC1, &vpc1))
597 return; 777 return;
598 if (read_ec_data(handle, 0x1A, &vpc2)) 778 if (read_ec_data(handle, VPCCMD_R_VPC2, &vpc2))
599 return; 779 return;
600 780
601 vpc1 = (vpc2 << 8) | vpc1; 781 vpc1 = (vpc2 << 8) | vpc1;
@@ -603,11 +783,14 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
603 if (test_bit(vpc_bit, &vpc1)) { 783 if (test_bit(vpc_bit, &vpc1)) {
604 switch (vpc_bit) { 784 switch (vpc_bit) {
605 case 9: 785 case 9:
606 ideapad_sync_rfk_state(adevice); 786 ideapad_sync_rfk_state(priv);
607 break; 787 break;
608 case 4: 788 case 4:
609 ideapad_backlight_notify_brightness(priv); 789 ideapad_backlight_notify_brightness(priv);
610 break; 790 break;
791 case 3:
792 ideapad_input_novokey(priv);
793 break;
611 case 2: 794 case 2:
612 ideapad_backlight_notify_power(priv); 795 ideapad_backlight_notify_power(priv);
613 break; 796 break;