1/* 2 * ideapad-laptop.c - Lenovo IdeaPad ACPI Extras 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 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 42#define IDEAPAD_RFKILL_DEV_NUM (3) 43 44#define CFG_BT_BIT (16) 45#define CFG_3G_BIT (17) 46#define CFG_WIFI_BIT (18) 47#define CFG_CAMERA_BIT (19) 48 49enum { 50 VPCCMD_R_VPC1 = 0x10, 51 VPCCMD_R_BL_MAX, 52 VPCCMD_R_BL, 53 VPCCMD_W_BL, 54 VPCCMD_R_WIFI, 55 VPCCMD_W_WIFI, 56 VPCCMD_R_BT, 57 VPCCMD_W_BT, 58 VPCCMD_R_BL_POWER, 59 VPCCMD_R_NOVO, 60 VPCCMD_R_VPC2, 61 VPCCMD_R_TOUCHPAD, 62 VPCCMD_W_TOUCHPAD, 63 VPCCMD_R_CAMERA, 64 VPCCMD_W_CAMERA, 65 VPCCMD_R_3G, 66 VPCCMD_W_3G, 67 VPCCMD_R_ODD, /* 0x21 */ 68 VPCCMD_W_FAN, 69 VPCCMD_R_RF, 70 VPCCMD_W_RF, 71 VPCCMD_R_FAN = 0x2B, 72 VPCCMD_R_SPECIAL_BUTTONS = 0x31, 73 VPCCMD_W_BL_POWER = 0x33, 74}; 75 76struct ideapad_rfk_priv { 77 int dev; 78 struct ideapad_private *priv; 79}; 80 81struct ideapad_private { 82 struct acpi_device *adev; 83 struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM]; 84 struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM]; 85 struct platform_device *platform_device; 86 struct input_dev *inputdev; 87 struct backlight_device *blightdev; 88 struct dentry *debug; 89 unsigned long cfg; 90 bool has_hw_rfkill_switch; 91}; 92 93static bool no_bt_rfkill; 94module_param(no_bt_rfkill, bool, 0444); 95MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth."); 96 97/* 98 * ACPI Helpers 99 */ 100#define IDEAPAD_EC_TIMEOUT (100) /* in ms */ 101 102static int read_method_int(acpi_handle handle, const char *method, int *val) 103{ 104 acpi_status status; 105 unsigned long long result; 106 107 status = acpi_evaluate_integer(handle, (char *)method, NULL, &result); 108 if (ACPI_FAILURE(status)) { 109 *val = -1; 110 return -1; 111 } else { 112 *val = result; 113 return 0; 114 } 115} 116 117static int method_vpcr(acpi_handle handle, int cmd, int *ret) 118{ 119 acpi_status status; 120 unsigned long long result; 121 struct acpi_object_list params; 122 union acpi_object in_obj; 123 124 params.count = 1; 125 params.pointer = &in_obj; 126 in_obj.type = ACPI_TYPE_INTEGER; 127 in_obj.integer.value = cmd; 128 129 status = acpi_evaluate_integer(handle, "VPCR", ¶ms, &result); 130 131 if (ACPI_FAILURE(status)) { 132 *ret = -1; 133 return -1; 134 } else { 135 *ret = result; 136 return 0; 137 } 138} 139 140static int method_vpcw(acpi_handle handle, int cmd, int data) 141{ 142 struct acpi_object_list params; 143 union acpi_object in_obj[2]; 144 acpi_status status; 145 146 params.count = 2; 147 params.pointer = in_obj; 148 in_obj[0].type = ACPI_TYPE_INTEGER; 149 in_obj[0].integer.value = cmd; 150 in_obj[1].type = ACPI_TYPE_INTEGER; 151 in_obj[1].integer.value = data; 152 153 status = acpi_evaluate_object(handle, "VPCW", ¶ms, NULL); 154 if (status != AE_OK) 155 return -1; 156 return 0; 157} 158 159static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data) 160{ 161 int val; 162 unsigned long int end_jiffies; 163 164 if (method_vpcw(handle, 1, cmd)) 165 return -1; 166 167 for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1; 168 time_before(jiffies, end_jiffies);) { 169 schedule(); 170 if (method_vpcr(handle, 1, &val)) 171 return -1; 172 if (val == 0) { 173 if (method_vpcr(handle, 0, &val)) 174 return -1; 175 *data = val; 176 return 0; 177 } 178 } 179 pr_err("timeout in read_ec_cmd\n"); 180 return -1; 181} 182 183static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data) 184{ 185 int val; 186 unsigned long int end_jiffies; 187 188 if (method_vpcw(handle, 0, data)) 189 return -1; 190 if (method_vpcw(handle, 1, cmd)) 191 return -1; 192 193 for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1; 194 time_before(jiffies, end_jiffies);) { 195 schedule(); 196 if (method_vpcr(handle, 1, &val)) 197 return -1; 198 if (val == 0) 199 return 0; 200 } 201 pr_err("timeout in write_ec_cmd\n"); 202 return -1; 203} 204 205/* 206 * debugfs 207 */ 208static int debugfs_status_show(struct seq_file *s, void *data) 209{ 210 struct ideapad_private *priv = s->private; 211 unsigned long value; 212 213 if (!priv) 214 return -EINVAL; 215 216 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &value)) 217 seq_printf(s, "Backlight max:\t%lu\n", value); 218 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL, &value)) 219 seq_printf(s, "Backlight now:\t%lu\n", value); 220 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &value)) 221 seq_printf(s, "BL power value:\t%s\n", value ? "On" : "Off"); 222 seq_printf(s, "=====================\n"); 223 224 if (!read_ec_data(priv->adev->handle, VPCCMD_R_RF, &value)) 225 seq_printf(s, "Radio status:\t%s(%lu)\n", 226 value ? "On" : "Off", value); 227 if (!read_ec_data(priv->adev->handle, VPCCMD_R_WIFI, &value)) 228 seq_printf(s, "Wifi status:\t%s(%lu)\n", 229 value ? "On" : "Off", value); 230 if (!read_ec_data(priv->adev->handle, VPCCMD_R_BT, &value)) 231 seq_printf(s, "BT status:\t%s(%lu)\n", 232 value ? "On" : "Off", value); 233 if (!read_ec_data(priv->adev->handle, VPCCMD_R_3G, &value)) 234 seq_printf(s, "3G status:\t%s(%lu)\n", 235 value ? "On" : "Off", value); 236 seq_printf(s, "=====================\n"); 237 238 if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value)) 239 seq_printf(s, "Touchpad status:%s(%lu)\n", 240 value ? "On" : "Off", value); 241 if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value)) 242 seq_printf(s, "Camera status:\t%s(%lu)\n", 243 value ? "On" : "Off", value); 244 245 return 0; 246} 247 248static int debugfs_status_open(struct inode *inode, struct file *file) 249{ 250 return single_open(file, debugfs_status_show, inode->i_private); 251} 252 253static const struct file_operations debugfs_status_fops = { 254 .owner = THIS_MODULE, 255 .open = debugfs_status_open, 256 .read = seq_read, 257 .llseek = seq_lseek, 258 .release = single_release, 259}; 260 261static int debugfs_cfg_show(struct seq_file *s, void *data) 262{ 263 struct ideapad_private *priv = s->private; 264 265 if (!priv) { 266 seq_printf(s, "cfg: N/A\n"); 267 } else { 268 seq_printf(s, "cfg: 0x%.8lX\n\nCapability: ", 269 priv->cfg); 270 if (test_bit(CFG_BT_BIT, &priv->cfg)) 271 seq_printf(s, "Bluetooth "); 272 if (test_bit(CFG_3G_BIT, &priv->cfg)) 273 seq_printf(s, "3G "); 274 if (test_bit(CFG_WIFI_BIT, &priv->cfg)) 275 seq_printf(s, "Wireless "); 276 if (test_bit(CFG_CAMERA_BIT, &priv->cfg)) 277 seq_printf(s, "Camera "); 278 seq_printf(s, "\nGraphic: "); 279 switch ((priv->cfg)&0x700) { 280 case 0x100: 281 seq_printf(s, "Intel"); 282 break; 283 case 0x200: 284 seq_printf(s, "ATI"); 285 break; 286 case 0x300: 287 seq_printf(s, "Nvidia"); 288 break; 289 case 0x400: 290 seq_printf(s, "Intel and ATI"); 291 break; 292 case 0x500: 293 seq_printf(s, "Intel and Nvidia"); 294 break; 295 } 296 seq_printf(s, "\n"); 297 } 298 return 0; 299} 300 301static int debugfs_cfg_open(struct inode *inode, struct file *file) 302{ 303 return single_open(file, debugfs_cfg_show, inode->i_private); 304} 305 306static const struct file_operations debugfs_cfg_fops = { 307 .owner = THIS_MODULE, 308 .open = debugfs_cfg_open, 309 .read = seq_read, 310 .llseek = seq_lseek, 311 .release = single_release, 312}; 313 314static int ideapad_debugfs_init(struct ideapad_private *priv) 315{ 316 struct dentry *node; 317 318 priv->debug = debugfs_create_dir("ideapad", NULL); 319 if (priv->debug == NULL) { 320 pr_err("failed to create debugfs directory"); 321 goto errout; 322 } 323 324 node = debugfs_create_file("cfg", S_IRUGO, priv->debug, priv, 325 &debugfs_cfg_fops); 326 if (!node) { 327 pr_err("failed to create cfg in debugfs"); 328 goto errout; 329 } 330 331 node = debugfs_create_file("status", S_IRUGO, priv->debug, priv, 332 &debugfs_status_fops); 333 if (!node) { 334 pr_err("failed to create status in debugfs"); 335 goto errout; 336 } 337 338 return 0; 339 340errout: 341 return -ENOMEM; 342} 343 344static void ideapad_debugfs_exit(struct ideapad_private *priv) 345{ 346 debugfs_remove_recursive(priv->debug); 347 priv->debug = NULL; 348} 349 350/* 351 * sysfs 352 */ 353static ssize_t show_ideapad_cam(struct device *dev, 354 struct device_attribute *attr, 355 char *buf) 356{ 357 unsigned long result; 358 struct ideapad_private *priv = dev_get_drvdata(dev); 359 360 if (read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &result)) 361 return sprintf(buf, "-1\n"); 362 return sprintf(buf, "%lu\n", result); 363} 364 365static ssize_t store_ideapad_cam(struct device *dev, 366 struct device_attribute *attr, 367 const char *buf, size_t count) 368{ 369 int ret, state; 370 struct ideapad_private *priv = dev_get_drvdata(dev); 371 372 if (!count) 373 return 0; 374 if (sscanf(buf, "%i", &state) != 1) 375 return -EINVAL; 376 ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_CAMERA, state); 377 if (ret < 0) 378 return -EIO; 379 return count; 380} 381 382static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam); 383 384static ssize_t show_ideapad_fan(struct device *dev, 385 struct device_attribute *attr, 386 char *buf) 387{ 388 unsigned long result; 389 struct ideapad_private *priv = dev_get_drvdata(dev); 390 391 if (read_ec_data(priv->adev->handle, VPCCMD_R_FAN, &result)) 392 return sprintf(buf, "-1\n"); 393 return sprintf(buf, "%lu\n", result); 394} 395 396static ssize_t store_ideapad_fan(struct device *dev, 397 struct device_attribute *attr, 398 const char *buf, size_t count) 399{ 400 int ret, state; 401 struct ideapad_private *priv = dev_get_drvdata(dev); 402 403 if (!count) 404 return 0; 405 if (sscanf(buf, "%i", &state) != 1) 406 return -EINVAL; 407 if (state < 0 || state > 4 || state == 3) 408 return -EINVAL; 409 ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_FAN, state); 410 if (ret < 0) 411 return -EIO; 412 return count; 413} 414 415static DEVICE_ATTR(fan_mode, 0644, show_ideapad_fan, store_ideapad_fan); 416 417static struct attribute *ideapad_attributes[] = { 418 &dev_attr_camera_power.attr, 419 &dev_attr_fan_mode.attr, 420 NULL 421}; 422 423static umode_t ideapad_is_visible(struct kobject *kobj, 424 struct attribute *attr, 425 int idx) 426{ 427 struct device *dev = container_of(kobj, struct device, kobj); 428 struct ideapad_private *priv = dev_get_drvdata(dev); 429 bool supported; 430 431 if (attr == &dev_attr_camera_power.attr) 432 supported = test_bit(CFG_CAMERA_BIT, &(priv->cfg)); 433 else if (attr == &dev_attr_fan_mode.attr) { 434 unsigned long value; 435 supported = !read_ec_data(priv->adev->handle, VPCCMD_R_FAN, 436 &value); 437 } else 438 supported = true; 439 440 return supported ? attr->mode : 0; 441} 442 443static const struct attribute_group ideapad_attribute_group = { 444 .is_visible = ideapad_is_visible, 445 .attrs = ideapad_attributes 446}; 447 448/* 449 * Rfkill 450 */ 451struct ideapad_rfk_data { 452 char *name; 453 int cfgbit; 454 int opcode; 455 int type; 456}; 457 458static const struct ideapad_rfk_data ideapad_rfk_data[] = { 459 { "ideapad_wlan", CFG_WIFI_BIT, VPCCMD_W_WIFI, RFKILL_TYPE_WLAN }, 460 { "ideapad_bluetooth", CFG_BT_BIT, VPCCMD_W_BT, RFKILL_TYPE_BLUETOOTH }, 461 { "ideapad_3g", CFG_3G_BIT, VPCCMD_W_3G, RFKILL_TYPE_WWAN }, 462}; 463 464static int ideapad_rfk_set(void *data, bool blocked) 465{ 466 struct ideapad_rfk_priv *priv = data; 467 int opcode = ideapad_rfk_data[priv->dev].opcode; 468 469 return write_ec_cmd(priv->priv->adev->handle, opcode, !blocked); 470} 471 472static struct rfkill_ops ideapad_rfk_ops = { 473 .set_block = ideapad_rfk_set, 474}; 475 476static void ideapad_sync_rfk_state(struct ideapad_private *priv) 477{ 478 unsigned long hw_blocked = 0; 479 int i; 480 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 } 486 487 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) 488 if (priv->rfk[i]) 489 rfkill_set_hw_state(priv->rfk[i], hw_blocked); 490} 491 492static int ideapad_register_rfkill(struct ideapad_private *priv, int dev) 493{ 494 int ret; 495 unsigned long sw_blocked; 496 497 if (no_bt_rfkill && 498 (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) { 499 /* Force to enable bluetooth when no_bt_rfkill=1 */ 500 write_ec_cmd(priv->adev->handle, 501 ideapad_rfk_data[dev].opcode, 1); 502 return 0; 503 } 504 priv->rfk_priv[dev].dev = dev; 505 priv->rfk_priv[dev].priv = priv; 506 507 priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name, 508 &priv->platform_device->dev, 509 ideapad_rfk_data[dev].type, 510 &ideapad_rfk_ops, 511 &priv->rfk_priv[dev]); 512 if (!priv->rfk[dev]) 513 return -ENOMEM; 514 515 if (read_ec_data(priv->adev->handle, ideapad_rfk_data[dev].opcode-1, 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 523 ret = rfkill_register(priv->rfk[dev]); 524 if (ret) { 525 rfkill_destroy(priv->rfk[dev]); 526 return ret; 527 } 528 return 0; 529} 530 531static void ideapad_unregister_rfkill(struct ideapad_private *priv, int dev) 532{ 533 if (!priv->rfk[dev]) 534 return; 535 536 rfkill_unregister(priv->rfk[dev]); 537 rfkill_destroy(priv->rfk[dev]); 538} 539 540/* 541 * Platform device 542 */ 543static int ideapad_sysfs_init(struct ideapad_private *priv) 544{ 545 return sysfs_create_group(&priv->platform_device->dev.kobj, 546 &ideapad_attribute_group); 547} 548 549static void ideapad_sysfs_exit(struct ideapad_private *priv) 550{ 551 sysfs_remove_group(&priv->platform_device->dev.kobj, 552 &ideapad_attribute_group); 553} 554 555/* 556 * input device 557 */ 558static const struct key_entry ideapad_keymap[] = { 559 { KE_KEY, 6, { KEY_SWITCHVIDEOMODE } }, 560 { KE_KEY, 7, { KEY_CAMERA } }, 561 { KE_KEY, 11, { KEY_F16 } }, 562 { KE_KEY, 13, { KEY_WLAN } }, 563 { KE_KEY, 16, { KEY_PROG1 } }, 564 { KE_KEY, 17, { KEY_PROG2 } }, 565 { KE_KEY, 64, { KEY_PROG3 } }, 566 { KE_KEY, 65, { KEY_PROG4 } }, 567 { KE_KEY, 66, { KEY_TOUCHPAD_OFF } }, 568 { KE_KEY, 67, { KEY_TOUCHPAD_ON } }, 569 { KE_END, 0 }, 570}; 571 572static int ideapad_input_init(struct ideapad_private *priv) 573{ 574 struct input_dev *inputdev; 575 int error; 576 577 inputdev = input_allocate_device(); 578 if (!inputdev) 579 return -ENOMEM; 580 581 inputdev->name = "Ideapad extra buttons"; 582 inputdev->phys = "ideapad/input0"; 583 inputdev->id.bustype = BUS_HOST; 584 inputdev->dev.parent = &priv->platform_device->dev; 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 598 priv->inputdev = inputdev; 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 608static void ideapad_input_exit(struct ideapad_private *priv) 609{ 610 sparse_keymap_free(priv->inputdev); 611 input_unregister_device(priv->inputdev); 612 priv->inputdev = NULL; 613} 614 615static void ideapad_input_report(struct ideapad_private *priv, 616 unsigned long scancode) 617{ 618 sparse_keymap_report_event(priv->inputdev, scancode, 1, true); 619} 620 621static void ideapad_input_novokey(struct ideapad_private *priv) 622{ 623 unsigned long long_pressed; 624 625 if (read_ec_data(priv->adev->handle, VPCCMD_R_NOVO, &long_pressed)) 626 return; 627 if (long_pressed) 628 ideapad_input_report(priv, 17); 629 else 630 ideapad_input_report(priv, 16); 631} 632 633static void ideapad_check_special_buttons(struct ideapad_private *priv) 634{ 635 unsigned long bit, value; 636 637 read_ec_data(priv->adev->handle, VPCCMD_R_SPECIAL_BUTTONS, &value); 638 639 for (bit = 0; bit < 16; bit++) { 640 if (test_bit(bit, &value)) { 641 switch (bit) { 642 case 0: /* Z580 */ 643 case 6: /* Z570 */ 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; 651 default: 652 pr_info("Unknown special button: %lu\n", bit); 653 break; 654 } 655 } 656 } 657} 658 659/* 660 * backlight 661 */ 662static int ideapad_backlight_get_brightness(struct backlight_device *blightdev) 663{ 664 struct ideapad_private *priv = bl_get_data(blightdev); 665 unsigned long now; 666 667 if (!priv) 668 return -EINVAL; 669 670 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now)) 671 return -EIO; 672 return now; 673} 674 675static int ideapad_backlight_update_status(struct backlight_device *blightdev) 676{ 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, 683 blightdev->props.brightness)) 684 return -EIO; 685 if (write_ec_cmd(priv->adev->handle, VPCCMD_W_BL_POWER, 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 703 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &max)) 704 return -EIO; 705 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now)) 706 return -EIO; 707 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power)) 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{ 733 backlight_device_unregister(priv->blightdev); 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 742 if (!blightdev) 743 return; 744 if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power)) 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) { 755 read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now); 756 return; 757 } 758 759 backlight_force_update(priv->blightdev, BACKLIGHT_UPDATE_HOTKEY); 760} 761 762/* 763 * module init/exit 764 */ 765static void ideapad_sync_touchpad_state(struct ideapad_private *priv) 766{ 767 unsigned long value; 768 769 /* Without reading from EC touchpad LED doesn't switch state */ 770 if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value)) { 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(¶m, value ? I8042_CMD_AUX_ENABLE : 777 I8042_CMD_AUX_DISABLE); 778 ideapad_input_report(priv, value ? 67 : 66); 779 } 780} 781 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 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 */ 832static const struct dmi_system_id no_hw_rfkill_list[] = { 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 }, 840 { 841 .ident = "Lenovo G50-30", 842 .matches = { 843 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 844 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo G50-30"), 845 }, 846 }, 847 { 848 .ident = "Lenovo ideapad Y700-15ISK", 849 .matches = { 850 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 851 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-15ISK"), 852 }, 853 }, 854 { 855 .ident = "Lenovo ideapad Y700 Touch-15ISK", 856 .matches = { 857 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 858 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700 Touch-15ISK"), 859 }, 860 }, 861 { 862 .ident = "Lenovo ideapad Y700-17ISK", 863 .matches = { 864 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 865 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-17ISK"), 866 }, 867 }, 868 { 869 .ident = "Lenovo Yoga 2 11 / 13 / Pro", 870 .matches = { 871 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 872 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 2"), 873 }, 874 }, 875 { 876 .ident = "Lenovo Yoga 3 14", 877 .matches = { 878 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 879 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 3 14"), 880 }, 881 }, 882 { 883 .ident = "Lenovo Yoga 3 Pro 1370", 884 .matches = { 885 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 886 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 3 Pro-1370"), 887 }, 888 }, 889 { 890 .ident = "Lenovo Yoga 700", 891 .matches = { 892 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 893 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 700"), 894 }, 895 }, 896 { 897 .ident = "Lenovo Yoga 900", 898 .matches = { 899 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 900 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 900"), 901 }, 902 }, 903 {} 904}; 905 906static int ideapad_acpi_add(struct platform_device *pdev) 907{ 908 int ret, i; 909 int cfg; 910 struct ideapad_private *priv; 911 struct acpi_device *adev; 912 913 ret = acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev); 914 if (ret) 915 return -ENODEV; 916 917 if (read_method_int(adev->handle, "_CFG", &cfg)) 918 return -ENODEV; 919 920 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 921 if (!priv) 922 return -ENOMEM; 923 924 dev_set_drvdata(&pdev->dev, priv); 925 priv->cfg = cfg; 926 priv->adev = adev; 927 priv->platform_device = pdev; 928 priv->has_hw_rfkill_switch = !dmi_check_system(no_hw_rfkill_list); 929 930 ret = ideapad_sysfs_init(priv); 931 if (ret) 932 return ret; 933 934 ret = ideapad_debugfs_init(priv); 935 if (ret) 936 goto debugfs_failed; 937 938 ret = ideapad_input_init(priv); 939 if (ret) 940 goto input_failed; 941 942 /* 943 * On some models without a hw-switch (the yoga 2 13 at least) 944 * VPCCMD_W_RF must be explicitly set to 1 for the wifi to work. 945 */ 946 if (!priv->has_hw_rfkill_switch) 947 write_ec_cmd(priv->adev->handle, VPCCMD_W_RF, 1); 948 949 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) 950 if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg)) 951 ideapad_register_rfkill(priv, i); 952 953 ideapad_sync_rfk_state(priv); 954 ideapad_sync_touchpad_state(priv); 955 956 if (!acpi_video_backlight_support()) { 957 ret = ideapad_backlight_init(priv); 958 if (ret && ret != -ENODEV) 959 goto backlight_failed; 960 } 961 ret = acpi_install_notify_handler(adev->handle, 962 ACPI_DEVICE_NOTIFY, ideapad_acpi_notify, priv); 963 if (ret) 964 goto notification_failed; 965 966 return 0; 967notification_failed: 968 ideapad_backlight_exit(priv); 969backlight_failed: 970 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) 971 ideapad_unregister_rfkill(priv, i); 972 ideapad_input_exit(priv); 973input_failed: 974 ideapad_debugfs_exit(priv); 975debugfs_failed: 976 ideapad_sysfs_exit(priv); 977 return ret; 978} 979 980static int ideapad_acpi_remove(struct platform_device *pdev) 981{ 982 struct ideapad_private *priv = dev_get_drvdata(&pdev->dev); 983 int i; 984 985 acpi_remove_notify_handler(priv->adev->handle, 986 ACPI_DEVICE_NOTIFY, ideapad_acpi_notify); 987 ideapad_backlight_exit(priv); 988 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) 989 ideapad_unregister_rfkill(priv, i); 990 ideapad_input_exit(priv); 991 ideapad_debugfs_exit(priv); 992 ideapad_sysfs_exit(priv); 993 dev_set_drvdata(&pdev->dev, NULL); 994 995 return 0; 996} 997 998#ifdef CONFIG_PM_SLEEP 999static int ideapad_acpi_resume(struct device *device) 1000{ 1001 struct ideapad_private *priv; 1002 1003 if (!device) 1004 return -EINVAL; 1005 priv = dev_get_drvdata(device); 1006 1007 ideapad_sync_rfk_state(priv); 1008 ideapad_sync_touchpad_state(priv); 1009 return 0; 1010} 1011#endif 1012static SIMPLE_DEV_PM_OPS(ideapad_pm, NULL, ideapad_acpi_resume); 1013 1014static const struct acpi_device_id ideapad_device_ids[] = { 1015 { "VPC2004", 0}, 1016 { "", 0}, 1017}; 1018MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); 1019 1020static struct platform_driver ideapad_acpi_driver = { 1021 .probe = ideapad_acpi_add, 1022 .remove = ideapad_acpi_remove, 1023 .driver = { 1024 .name = "ideapad_acpi", 1025 .pm = &ideapad_pm, 1026 .acpi_match_table = ACPI_PTR(ideapad_device_ids), 1027 }, 1028}; 1029 1030module_platform_driver(ideapad_acpi_driver); 1031 1032MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); 1033MODULE_DESCRIPTION("IdeaPad ACPI Extras"); 1034MODULE_LICENSE("GPL"); 1035