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 <acpi/acpi_bus.h> 30 #include <acpi/acpi_drivers.h> 31 #include <linux/rfkill.h> 32 #include <linux/platform_device.h> 33 #include <linux/input.h> 34 #include <linux/input/sparse-keymap.h> 35 #include <linux/backlight.h> 36 #include <linux/fb.h> 37 38 #define IDEAPAD_RFKILL_DEV_NUM (3) 39 40 #define CFG_BT_BIT (16) 41 #define CFG_3G_BIT (17) 42 #define CFG_WIFI_BIT (18) 43 #define CFG_CAMERA_BIT (19) 44 45 struct ideapad_private { 46 struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM]; 47 struct platform_device *platform_device; 48 struct input_dev *inputdev; 49 struct backlight_device *blightdev; 50 unsigned long cfg; 51 }; 52 53 static acpi_handle ideapad_handle; 54 static bool no_bt_rfkill; 55 module_param(no_bt_rfkill, bool, 0444); 56 MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth."); 57 58 /* 59 * ACPI Helpers 60 */ 61 #define IDEAPAD_EC_TIMEOUT (100) /* in ms */ 62 63 static int read_method_int(acpi_handle handle, const char *method, int *val) 64 { 65 acpi_status status; 66 unsigned long long result; 67 68 status = acpi_evaluate_integer(handle, (char *)method, NULL, &result); 69 if (ACPI_FAILURE(status)) { 70 *val = -1; 71 return -1; 72 } else { 73 *val = result; 74 return 0; 75 } 76 } 77 78 static int method_vpcr(acpi_handle handle, int cmd, int *ret) 79 { 80 acpi_status status; 81 unsigned long long result; 82 struct acpi_object_list params; 83 union acpi_object in_obj; 84 85 params.count = 1; 86 params.pointer = &in_obj; 87 in_obj.type = ACPI_TYPE_INTEGER; 88 in_obj.integer.value = cmd; 89 90 status = acpi_evaluate_integer(handle, "VPCR", ¶ms, &result); 91 92 if (ACPI_FAILURE(status)) { 93 *ret = -1; 94 return -1; 95 } else { 96 *ret = result; 97 return 0; 98 } 99 } 100 101 static int method_vpcw(acpi_handle handle, int cmd, int data) 102 { 103 struct acpi_object_list params; 104 union acpi_object in_obj[2]; 105 acpi_status status; 106 107 params.count = 2; 108 params.pointer = in_obj; 109 in_obj[0].type = ACPI_TYPE_INTEGER; 110 in_obj[0].integer.value = cmd; 111 in_obj[1].type = ACPI_TYPE_INTEGER; 112 in_obj[1].integer.value = data; 113 114 status = acpi_evaluate_object(handle, "VPCW", ¶ms, NULL); 115 if (status != AE_OK) 116 return -1; 117 return 0; 118 } 119 120 static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data) 121 { 122 int val; 123 unsigned long int end_jiffies; 124 125 if (method_vpcw(handle, 1, cmd)) 126 return -1; 127 128 for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1; 129 time_before(jiffies, end_jiffies);) { 130 schedule(); 131 if (method_vpcr(handle, 1, &val)) 132 return -1; 133 if (val == 0) { 134 if (method_vpcr(handle, 0, &val)) 135 return -1; 136 *data = val; 137 return 0; 138 } 139 } 140 pr_err("timeout in read_ec_cmd\n"); 141 return -1; 142 } 143 144 static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data) 145 { 146 int val; 147 unsigned long int end_jiffies; 148 149 if (method_vpcw(handle, 0, data)) 150 return -1; 151 if (method_vpcw(handle, 1, cmd)) 152 return -1; 153 154 for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1; 155 time_before(jiffies, end_jiffies);) { 156 schedule(); 157 if (method_vpcr(handle, 1, &val)) 158 return -1; 159 if (val == 0) 160 return 0; 161 } 162 pr_err("timeout in write_ec_cmd\n"); 163 return -1; 164 } 165 166 /* 167 * sysfs 168 */ 169 static ssize_t show_ideapad_cam(struct device *dev, 170 struct device_attribute *attr, 171 char *buf) 172 { 173 unsigned long result; 174 175 if (read_ec_data(ideapad_handle, 0x1D, &result)) 176 return sprintf(buf, "-1\n"); 177 return sprintf(buf, "%lu\n", result); 178 } 179 180 static ssize_t store_ideapad_cam(struct device *dev, 181 struct device_attribute *attr, 182 const char *buf, size_t count) 183 { 184 int ret, state; 185 186 if (!count) 187 return 0; 188 if (sscanf(buf, "%i", &state) != 1) 189 return -EINVAL; 190 ret = write_ec_cmd(ideapad_handle, 0x1E, state); 191 if (ret < 0) 192 return ret; 193 return count; 194 } 195 196 static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam); 197 198 static 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 207 static DEVICE_ATTR(cfg, 0444, show_ideapad_cfg, NULL); 208 209 static struct attribute *ideapad_attributes[] = { 210 &dev_attr_camera_power.attr, 211 &dev_attr_cfg.attr, 212 NULL 213 }; 214 215 static mode_t ideapad_is_visible(struct kobject *kobj, 216 struct attribute *attr, 217 int idx) 218 { 219 struct device *dev = container_of(kobj, struct device, kobj); 220 struct ideapad_private *priv = dev_get_drvdata(dev); 221 bool supported; 222 223 if (attr == &dev_attr_camera_power.attr) 224 supported = test_bit(CFG_CAMERA_BIT, &(priv->cfg)); 225 else 226 supported = true; 227 228 return supported ? attr->mode : 0; 229 } 230 231 static struct attribute_group ideapad_attribute_group = { 232 .is_visible = ideapad_is_visible, 233 .attrs = ideapad_attributes 234 }; 235 236 /* 237 * Rfkill 238 */ 239 struct ideapad_rfk_data { 240 char *name; 241 int cfgbit; 242 int opcode; 243 int type; 244 }; 245 246 const struct ideapad_rfk_data ideapad_rfk_data[] = { 247 { "ideapad_wlan", CFG_WIFI_BIT, 0x15, RFKILL_TYPE_WLAN }, 248 { "ideapad_bluetooth", CFG_BT_BIT, 0x17, RFKILL_TYPE_BLUETOOTH }, 249 { "ideapad_3g", CFG_3G_BIT, 0x20, RFKILL_TYPE_WWAN }, 250 }; 251 252 static int ideapad_rfk_set(void *data, bool blocked) 253 { 254 unsigned long opcode = (unsigned long)data; 255 256 return write_ec_cmd(ideapad_handle, opcode, !blocked); 257 } 258 259 static struct rfkill_ops ideapad_rfk_ops = { 260 .set_block = ideapad_rfk_set, 261 }; 262 263 static void ideapad_sync_rfk_state(struct acpi_device *adevice) 264 { 265 struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); 266 unsigned long hw_blocked; 267 int i; 268 269 if (read_ec_data(ideapad_handle, 0x23, &hw_blocked)) 270 return; 271 hw_blocked = !hw_blocked; 272 273 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) 274 if (priv->rfk[i]) 275 rfkill_set_hw_state(priv->rfk[i], hw_blocked); 276 } 277 278 static int __devinit ideapad_register_rfkill(struct acpi_device *adevice, 279 int dev) 280 { 281 struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); 282 int ret; 283 unsigned long sw_blocked; 284 285 if (no_bt_rfkill && 286 (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) { 287 /* Force to enable bluetooth when no_bt_rfkill=1 */ 288 write_ec_cmd(ideapad_handle, 289 ideapad_rfk_data[dev].opcode, 1); 290 return 0; 291 } 292 293 priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name, &adevice->dev, 294 ideapad_rfk_data[dev].type, &ideapad_rfk_ops, 295 (void *)(long)dev); 296 if (!priv->rfk[dev]) 297 return -ENOMEM; 298 299 if (read_ec_data(ideapad_handle, ideapad_rfk_data[dev].opcode-1, 300 &sw_blocked)) { 301 rfkill_init_sw_state(priv->rfk[dev], 0); 302 } else { 303 sw_blocked = !sw_blocked; 304 rfkill_init_sw_state(priv->rfk[dev], sw_blocked); 305 } 306 307 ret = rfkill_register(priv->rfk[dev]); 308 if (ret) { 309 rfkill_destroy(priv->rfk[dev]); 310 return ret; 311 } 312 return 0; 313 } 314 315 static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev) 316 { 317 struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); 318 319 if (!priv->rfk[dev]) 320 return; 321 322 rfkill_unregister(priv->rfk[dev]); 323 rfkill_destroy(priv->rfk[dev]); 324 } 325 326 /* 327 * Platform device 328 */ 329 static int __devinit ideapad_platform_init(struct ideapad_private *priv) 330 { 331 int result; 332 333 priv->platform_device = platform_device_alloc("ideapad", -1); 334 if (!priv->platform_device) 335 return -ENOMEM; 336 platform_set_drvdata(priv->platform_device, priv); 337 338 result = platform_device_add(priv->platform_device); 339 if (result) 340 goto fail_platform_device; 341 342 result = sysfs_create_group(&priv->platform_device->dev.kobj, 343 &ideapad_attribute_group); 344 if (result) 345 goto fail_sysfs; 346 return 0; 347 348 fail_sysfs: 349 platform_device_del(priv->platform_device); 350 fail_platform_device: 351 platform_device_put(priv->platform_device); 352 return result; 353 } 354 355 static void ideapad_platform_exit(struct ideapad_private *priv) 356 { 357 sysfs_remove_group(&priv->platform_device->dev.kobj, 358 &ideapad_attribute_group); 359 platform_device_unregister(priv->platform_device); 360 } 361 362 /* 363 * input device 364 */ 365 static const struct key_entry ideapad_keymap[] = { 366 { KE_KEY, 0x06, { KEY_SWITCHVIDEOMODE } }, 367 { KE_KEY, 0x0D, { KEY_WLAN } }, 368 { KE_END, 0 }, 369 }; 370 371 static int __devinit ideapad_input_init(struct ideapad_private *priv) 372 { 373 struct input_dev *inputdev; 374 int error; 375 376 inputdev = input_allocate_device(); 377 if (!inputdev) { 378 pr_info("Unable to allocate input device\n"); 379 return -ENOMEM; 380 } 381 382 inputdev->name = "Ideapad extra buttons"; 383 inputdev->phys = "ideapad/input0"; 384 inputdev->id.bustype = BUS_HOST; 385 inputdev->dev.parent = &priv->platform_device->dev; 386 387 error = sparse_keymap_setup(inputdev, ideapad_keymap, NULL); 388 if (error) { 389 pr_err("Unable to setup input device keymap\n"); 390 goto err_free_dev; 391 } 392 393 error = input_register_device(inputdev); 394 if (error) { 395 pr_err("Unable to register input device\n"); 396 goto err_free_keymap; 397 } 398 399 priv->inputdev = inputdev; 400 return 0; 401 402 err_free_keymap: 403 sparse_keymap_free(inputdev); 404 err_free_dev: 405 input_free_device(inputdev); 406 return error; 407 } 408 409 static void ideapad_input_exit(struct ideapad_private *priv) 410 { 411 sparse_keymap_free(priv->inputdev); 412 input_unregister_device(priv->inputdev); 413 priv->inputdev = NULL; 414 } 415 416 static void ideapad_input_report(struct ideapad_private *priv, 417 unsigned long scancode) 418 { 419 sparse_keymap_report_event(priv->inputdev, scancode, 1, true); 420 } 421 422 /* 423 * backlight 424 */ 425 static int ideapad_backlight_get_brightness(struct backlight_device *blightdev) 426 { 427 unsigned long now; 428 429 if (read_ec_data(ideapad_handle, 0x12, &now)) 430 return -EIO; 431 return now; 432 } 433 434 static int ideapad_backlight_update_status(struct backlight_device *blightdev) 435 { 436 if (write_ec_cmd(ideapad_handle, 0x13, blightdev->props.brightness)) 437 return -EIO; 438 if (write_ec_cmd(ideapad_handle, 0x33, 439 blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1)) 440 return -EIO; 441 442 return 0; 443 } 444 445 static const struct backlight_ops ideapad_backlight_ops = { 446 .get_brightness = ideapad_backlight_get_brightness, 447 .update_status = ideapad_backlight_update_status, 448 }; 449 450 static int ideapad_backlight_init(struct ideapad_private *priv) 451 { 452 struct backlight_device *blightdev; 453 struct backlight_properties props; 454 unsigned long max, now, power; 455 456 if (read_ec_data(ideapad_handle, 0x11, &max)) 457 return -EIO; 458 if (read_ec_data(ideapad_handle, 0x12, &now)) 459 return -EIO; 460 if (read_ec_data(ideapad_handle, 0x18, &power)) 461 return -EIO; 462 463 memset(&props, 0, sizeof(struct backlight_properties)); 464 props.max_brightness = max; 465 props.type = BACKLIGHT_PLATFORM; 466 blightdev = backlight_device_register("ideapad", 467 &priv->platform_device->dev, 468 priv, 469 &ideapad_backlight_ops, 470 &props); 471 if (IS_ERR(blightdev)) { 472 pr_err("Could not register backlight device\n"); 473 return PTR_ERR(blightdev); 474 } 475 476 priv->blightdev = blightdev; 477 blightdev->props.brightness = now; 478 blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; 479 backlight_update_status(blightdev); 480 481 return 0; 482 } 483 484 static void ideapad_backlight_exit(struct ideapad_private *priv) 485 { 486 if (priv->blightdev) 487 backlight_device_unregister(priv->blightdev); 488 priv->blightdev = NULL; 489 } 490 491 static void ideapad_backlight_notify_power(struct ideapad_private *priv) 492 { 493 unsigned long power; 494 struct backlight_device *blightdev = priv->blightdev; 495 496 if (read_ec_data(ideapad_handle, 0x18, &power)) 497 return; 498 blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; 499 } 500 501 static void ideapad_backlight_notify_brightness(struct ideapad_private *priv) 502 { 503 unsigned long now; 504 505 /* if we control brightness via acpi video driver */ 506 if (priv->blightdev == NULL) { 507 read_ec_data(ideapad_handle, 0x12, &now); 508 return; 509 } 510 511 backlight_force_update(priv->blightdev, BACKLIGHT_UPDATE_HOTKEY); 512 } 513 514 /* 515 * module init/exit 516 */ 517 static const struct acpi_device_id ideapad_device_ids[] = { 518 { "VPC2004", 0}, 519 { "", 0}, 520 }; 521 MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); 522 523 static int __devinit ideapad_acpi_add(struct acpi_device *adevice) 524 { 525 int ret, i; 526 unsigned long cfg; 527 struct ideapad_private *priv; 528 529 if (read_method_int(adevice->handle, "_CFG", (int *)&cfg)) 530 return -ENODEV; 531 532 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 533 if (!priv) 534 return -ENOMEM; 535 dev_set_drvdata(&adevice->dev, priv); 536 ideapad_handle = adevice->handle; 537 priv->cfg = cfg; 538 539 ret = ideapad_platform_init(priv); 540 if (ret) 541 goto platform_failed; 542 543 ret = ideapad_input_init(priv); 544 if (ret) 545 goto input_failed; 546 547 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) { 548 if (test_bit(ideapad_rfk_data[i].cfgbit, &cfg)) 549 ideapad_register_rfkill(adevice, i); 550 else 551 priv->rfk[i] = NULL; 552 } 553 ideapad_sync_rfk_state(adevice); 554 555 if (!acpi_video_backlight_support()) { 556 ret = ideapad_backlight_init(priv); 557 if (ret && ret != -ENODEV) 558 goto backlight_failed; 559 } 560 561 return 0; 562 563 backlight_failed: 564 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) 565 ideapad_unregister_rfkill(adevice, i); 566 ideapad_input_exit(priv); 567 input_failed: 568 ideapad_platform_exit(priv); 569 platform_failed: 570 kfree(priv); 571 return ret; 572 } 573 574 static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type) 575 { 576 struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); 577 int i; 578 579 ideapad_backlight_exit(priv); 580 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) 581 ideapad_unregister_rfkill(adevice, i); 582 ideapad_input_exit(priv); 583 ideapad_platform_exit(priv); 584 dev_set_drvdata(&adevice->dev, NULL); 585 kfree(priv); 586 587 return 0; 588 } 589 590 static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) 591 { 592 struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); 593 acpi_handle handle = adevice->handle; 594 unsigned long vpc1, vpc2, vpc_bit; 595 596 if (read_ec_data(handle, 0x10, &vpc1)) 597 return; 598 if (read_ec_data(handle, 0x1A, &vpc2)) 599 return; 600 601 vpc1 = (vpc2 << 8) | vpc1; 602 for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) { 603 if (test_bit(vpc_bit, &vpc1)) { 604 switch (vpc_bit) { 605 case 9: 606 ideapad_sync_rfk_state(adevice); 607 break; 608 case 4: 609 ideapad_backlight_notify_brightness(priv); 610 break; 611 case 2: 612 ideapad_backlight_notify_power(priv); 613 break; 614 default: 615 ideapad_input_report(priv, vpc_bit); 616 } 617 } 618 } 619 } 620 621 static struct acpi_driver ideapad_acpi_driver = { 622 .name = "ideapad_acpi", 623 .class = "IdeaPad", 624 .ids = ideapad_device_ids, 625 .ops.add = ideapad_acpi_add, 626 .ops.remove = ideapad_acpi_remove, 627 .ops.notify = ideapad_acpi_notify, 628 .owner = THIS_MODULE, 629 }; 630 631 static int __init ideapad_acpi_module_init(void) 632 { 633 return acpi_bus_register_driver(&ideapad_acpi_driver); 634 } 635 636 static void __exit ideapad_acpi_module_exit(void) 637 { 638 acpi_bus_unregister_driver(&ideapad_acpi_driver); 639 } 640 641 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); 642 MODULE_DESCRIPTION("IdeaPad ACPI Extras"); 643 MODULE_LICENSE("GPL"); 644 645 module_init(ideapad_acpi_module_init); 646 module_exit(ideapad_acpi_module_exit); 647