1 /* 2 * Copyright (C) 2009 Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License along 15 * with this program; if not, write to the Free Software Foundation, Inc., 16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 */ 18 19 20 #include <linux/init.h> 21 #include <linux/module.h> 22 #include <linux/slab.h> 23 #include <linux/workqueue.h> 24 #include <acpi/acpi_drivers.h> 25 #include <linux/backlight.h> 26 #include <linux/input.h> 27 #include <linux/rfkill.h> 28 29 MODULE_LICENSE("GPL"); 30 31 32 struct cmpc_accel { 33 int sensitivity; 34 }; 35 36 #define CMPC_ACCEL_SENSITIVITY_DEFAULT 5 37 38 39 #define CMPC_ACCEL_HID "ACCE0000" 40 #define CMPC_TABLET_HID "TBLT0000" 41 #define CMPC_IPML_HID "IPML200" 42 #define CMPC_KEYS_HID "FnBT0000" 43 44 /* 45 * Generic input device code. 46 */ 47 48 typedef void (*input_device_init)(struct input_dev *dev); 49 50 static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name, 51 input_device_init idev_init) 52 { 53 struct input_dev *inputdev; 54 int error; 55 56 inputdev = input_allocate_device(); 57 if (!inputdev) 58 return -ENOMEM; 59 inputdev->name = name; 60 inputdev->dev.parent = &acpi->dev; 61 idev_init(inputdev); 62 error = input_register_device(inputdev); 63 if (error) { 64 input_free_device(inputdev); 65 return error; 66 } 67 dev_set_drvdata(&acpi->dev, inputdev); 68 return 0; 69 } 70 71 static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi) 72 { 73 struct input_dev *inputdev = dev_get_drvdata(&acpi->dev); 74 input_unregister_device(inputdev); 75 return 0; 76 } 77 78 /* 79 * Accelerometer code. 80 */ 81 static acpi_status cmpc_start_accel(acpi_handle handle) 82 { 83 union acpi_object param[2]; 84 struct acpi_object_list input; 85 acpi_status status; 86 87 param[0].type = ACPI_TYPE_INTEGER; 88 param[0].integer.value = 0x3; 89 param[1].type = ACPI_TYPE_INTEGER; 90 input.count = 2; 91 input.pointer = param; 92 status = acpi_evaluate_object(handle, "ACMD", &input, NULL); 93 return status; 94 } 95 96 static acpi_status cmpc_stop_accel(acpi_handle handle) 97 { 98 union acpi_object param[2]; 99 struct acpi_object_list input; 100 acpi_status status; 101 102 param[0].type = ACPI_TYPE_INTEGER; 103 param[0].integer.value = 0x4; 104 param[1].type = ACPI_TYPE_INTEGER; 105 input.count = 2; 106 input.pointer = param; 107 status = acpi_evaluate_object(handle, "ACMD", &input, NULL); 108 return status; 109 } 110 111 static acpi_status cmpc_accel_set_sensitivity(acpi_handle handle, int val) 112 { 113 union acpi_object param[2]; 114 struct acpi_object_list input; 115 116 param[0].type = ACPI_TYPE_INTEGER; 117 param[0].integer.value = 0x02; 118 param[1].type = ACPI_TYPE_INTEGER; 119 param[1].integer.value = val; 120 input.count = 2; 121 input.pointer = param; 122 return acpi_evaluate_object(handle, "ACMD", &input, NULL); 123 } 124 125 static acpi_status cmpc_get_accel(acpi_handle handle, 126 unsigned char *x, 127 unsigned char *y, 128 unsigned char *z) 129 { 130 union acpi_object param[2]; 131 struct acpi_object_list input; 132 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, 0 }; 133 unsigned char *locs; 134 acpi_status status; 135 136 param[0].type = ACPI_TYPE_INTEGER; 137 param[0].integer.value = 0x01; 138 param[1].type = ACPI_TYPE_INTEGER; 139 input.count = 2; 140 input.pointer = param; 141 status = acpi_evaluate_object(handle, "ACMD", &input, &output); 142 if (ACPI_SUCCESS(status)) { 143 union acpi_object *obj; 144 obj = output.pointer; 145 locs = obj->buffer.pointer; 146 *x = locs[0]; 147 *y = locs[1]; 148 *z = locs[2]; 149 kfree(output.pointer); 150 } 151 return status; 152 } 153 154 static void cmpc_accel_handler(struct acpi_device *dev, u32 event) 155 { 156 if (event == 0x81) { 157 unsigned char x, y, z; 158 acpi_status status; 159 160 status = cmpc_get_accel(dev->handle, &x, &y, &z); 161 if (ACPI_SUCCESS(status)) { 162 struct input_dev *inputdev = dev_get_drvdata(&dev->dev); 163 164 input_report_abs(inputdev, ABS_X, x); 165 input_report_abs(inputdev, ABS_Y, y); 166 input_report_abs(inputdev, ABS_Z, z); 167 input_sync(inputdev); 168 } 169 } 170 } 171 172 static ssize_t cmpc_accel_sensitivity_show(struct device *dev, 173 struct device_attribute *attr, 174 char *buf) 175 { 176 struct acpi_device *acpi; 177 struct input_dev *inputdev; 178 struct cmpc_accel *accel; 179 180 acpi = to_acpi_device(dev); 181 inputdev = dev_get_drvdata(&acpi->dev); 182 accel = dev_get_drvdata(&inputdev->dev); 183 184 return sprintf(buf, "%d\n", accel->sensitivity); 185 } 186 187 static ssize_t cmpc_accel_sensitivity_store(struct device *dev, 188 struct device_attribute *attr, 189 const char *buf, size_t count) 190 { 191 struct acpi_device *acpi; 192 struct input_dev *inputdev; 193 struct cmpc_accel *accel; 194 unsigned long sensitivity; 195 int r; 196 197 acpi = to_acpi_device(dev); 198 inputdev = dev_get_drvdata(&acpi->dev); 199 accel = dev_get_drvdata(&inputdev->dev); 200 201 r = strict_strtoul(buf, 0, &sensitivity); 202 if (r) 203 return r; 204 205 accel->sensitivity = sensitivity; 206 cmpc_accel_set_sensitivity(acpi->handle, sensitivity); 207 208 return strnlen(buf, count); 209 } 210 211 static struct device_attribute cmpc_accel_sensitivity_attr = { 212 .attr = { .name = "sensitivity", .mode = 0660 }, 213 .show = cmpc_accel_sensitivity_show, 214 .store = cmpc_accel_sensitivity_store 215 }; 216 217 static int cmpc_accel_open(struct input_dev *input) 218 { 219 struct acpi_device *acpi; 220 221 acpi = to_acpi_device(input->dev.parent); 222 if (ACPI_SUCCESS(cmpc_start_accel(acpi->handle))) 223 return 0; 224 return -EIO; 225 } 226 227 static void cmpc_accel_close(struct input_dev *input) 228 { 229 struct acpi_device *acpi; 230 231 acpi = to_acpi_device(input->dev.parent); 232 cmpc_stop_accel(acpi->handle); 233 } 234 235 static void cmpc_accel_idev_init(struct input_dev *inputdev) 236 { 237 set_bit(EV_ABS, inputdev->evbit); 238 input_set_abs_params(inputdev, ABS_X, 0, 255, 8, 0); 239 input_set_abs_params(inputdev, ABS_Y, 0, 255, 8, 0); 240 input_set_abs_params(inputdev, ABS_Z, 0, 255, 8, 0); 241 inputdev->open = cmpc_accel_open; 242 inputdev->close = cmpc_accel_close; 243 } 244 245 static int cmpc_accel_add(struct acpi_device *acpi) 246 { 247 int error; 248 struct input_dev *inputdev; 249 struct cmpc_accel *accel; 250 251 accel = kmalloc(sizeof(*accel), GFP_KERNEL); 252 if (!accel) 253 return -ENOMEM; 254 255 accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT; 256 cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity); 257 258 error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr); 259 if (error) 260 goto failed_file; 261 262 error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel", 263 cmpc_accel_idev_init); 264 if (error) 265 goto failed_input; 266 267 inputdev = dev_get_drvdata(&acpi->dev); 268 dev_set_drvdata(&inputdev->dev, accel); 269 270 return 0; 271 272 failed_input: 273 device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); 274 failed_file: 275 kfree(accel); 276 return error; 277 } 278 279 static int cmpc_accel_remove(struct acpi_device *acpi, int type) 280 { 281 struct input_dev *inputdev; 282 struct cmpc_accel *accel; 283 284 inputdev = dev_get_drvdata(&acpi->dev); 285 accel = dev_get_drvdata(&inputdev->dev); 286 287 device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); 288 return cmpc_remove_acpi_notify_device(acpi); 289 } 290 291 static const struct acpi_device_id cmpc_accel_device_ids[] = { 292 {CMPC_ACCEL_HID, 0}, 293 {"", 0} 294 }; 295 296 static struct acpi_driver cmpc_accel_acpi_driver = { 297 .owner = THIS_MODULE, 298 .name = "cmpc_accel", 299 .class = "cmpc_accel", 300 .ids = cmpc_accel_device_ids, 301 .ops = { 302 .add = cmpc_accel_add, 303 .remove = cmpc_accel_remove, 304 .notify = cmpc_accel_handler, 305 } 306 }; 307 308 309 /* 310 * Tablet mode code. 311 */ 312 static acpi_status cmpc_get_tablet(acpi_handle handle, 313 unsigned long long *value) 314 { 315 union acpi_object param; 316 struct acpi_object_list input; 317 unsigned long long output; 318 acpi_status status; 319 320 param.type = ACPI_TYPE_INTEGER; 321 param.integer.value = 0x01; 322 input.count = 1; 323 input.pointer = ¶m; 324 status = acpi_evaluate_integer(handle, "TCMD", &input, &output); 325 if (ACPI_SUCCESS(status)) 326 *value = output; 327 return status; 328 } 329 330 static void cmpc_tablet_handler(struct acpi_device *dev, u32 event) 331 { 332 unsigned long long val = 0; 333 struct input_dev *inputdev = dev_get_drvdata(&dev->dev); 334 335 if (event == 0x81) { 336 if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val))) 337 input_report_switch(inputdev, SW_TABLET_MODE, !val); 338 } 339 } 340 341 static void cmpc_tablet_idev_init(struct input_dev *inputdev) 342 { 343 unsigned long long val = 0; 344 struct acpi_device *acpi; 345 346 set_bit(EV_SW, inputdev->evbit); 347 set_bit(SW_TABLET_MODE, inputdev->swbit); 348 349 acpi = to_acpi_device(inputdev->dev.parent); 350 if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) 351 input_report_switch(inputdev, SW_TABLET_MODE, !val); 352 } 353 354 static int cmpc_tablet_add(struct acpi_device *acpi) 355 { 356 return cmpc_add_acpi_notify_device(acpi, "cmpc_tablet", 357 cmpc_tablet_idev_init); 358 } 359 360 static int cmpc_tablet_remove(struct acpi_device *acpi, int type) 361 { 362 return cmpc_remove_acpi_notify_device(acpi); 363 } 364 365 static int cmpc_tablet_resume(struct acpi_device *acpi) 366 { 367 struct input_dev *inputdev = dev_get_drvdata(&acpi->dev); 368 unsigned long long val = 0; 369 if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) 370 input_report_switch(inputdev, SW_TABLET_MODE, !val); 371 return 0; 372 } 373 374 static const struct acpi_device_id cmpc_tablet_device_ids[] = { 375 {CMPC_TABLET_HID, 0}, 376 {"", 0} 377 }; 378 379 static struct acpi_driver cmpc_tablet_acpi_driver = { 380 .owner = THIS_MODULE, 381 .name = "cmpc_tablet", 382 .class = "cmpc_tablet", 383 .ids = cmpc_tablet_device_ids, 384 .ops = { 385 .add = cmpc_tablet_add, 386 .remove = cmpc_tablet_remove, 387 .resume = cmpc_tablet_resume, 388 .notify = cmpc_tablet_handler, 389 } 390 }; 391 392 393 /* 394 * Backlight code. 395 */ 396 397 static acpi_status cmpc_get_brightness(acpi_handle handle, 398 unsigned long long *value) 399 { 400 union acpi_object param; 401 struct acpi_object_list input; 402 unsigned long long output; 403 acpi_status status; 404 405 param.type = ACPI_TYPE_INTEGER; 406 param.integer.value = 0xC0; 407 input.count = 1; 408 input.pointer = ¶m; 409 status = acpi_evaluate_integer(handle, "GRDI", &input, &output); 410 if (ACPI_SUCCESS(status)) 411 *value = output; 412 return status; 413 } 414 415 static acpi_status cmpc_set_brightness(acpi_handle handle, 416 unsigned long long value) 417 { 418 union acpi_object param[2]; 419 struct acpi_object_list input; 420 acpi_status status; 421 unsigned long long output; 422 423 param[0].type = ACPI_TYPE_INTEGER; 424 param[0].integer.value = 0xC0; 425 param[1].type = ACPI_TYPE_INTEGER; 426 param[1].integer.value = value; 427 input.count = 2; 428 input.pointer = param; 429 status = acpi_evaluate_integer(handle, "GWRI", &input, &output); 430 return status; 431 } 432 433 static int cmpc_bl_get_brightness(struct backlight_device *bd) 434 { 435 acpi_status status; 436 acpi_handle handle; 437 unsigned long long brightness; 438 439 handle = bl_get_data(bd); 440 status = cmpc_get_brightness(handle, &brightness); 441 if (ACPI_SUCCESS(status)) 442 return brightness; 443 else 444 return -1; 445 } 446 447 static int cmpc_bl_update_status(struct backlight_device *bd) 448 { 449 acpi_status status; 450 acpi_handle handle; 451 452 handle = bl_get_data(bd); 453 status = cmpc_set_brightness(handle, bd->props.brightness); 454 if (ACPI_SUCCESS(status)) 455 return 0; 456 else 457 return -1; 458 } 459 460 static const struct backlight_ops cmpc_bl_ops = { 461 .get_brightness = cmpc_bl_get_brightness, 462 .update_status = cmpc_bl_update_status 463 }; 464 465 /* 466 * RFKILL code. 467 */ 468 469 static acpi_status cmpc_get_rfkill_wlan(acpi_handle handle, 470 unsigned long long *value) 471 { 472 union acpi_object param; 473 struct acpi_object_list input; 474 unsigned long long output; 475 acpi_status status; 476 477 param.type = ACPI_TYPE_INTEGER; 478 param.integer.value = 0xC1; 479 input.count = 1; 480 input.pointer = ¶m; 481 status = acpi_evaluate_integer(handle, "GRDI", &input, &output); 482 if (ACPI_SUCCESS(status)) 483 *value = output; 484 return status; 485 } 486 487 static acpi_status cmpc_set_rfkill_wlan(acpi_handle handle, 488 unsigned long long value) 489 { 490 union acpi_object param[2]; 491 struct acpi_object_list input; 492 acpi_status status; 493 unsigned long long output; 494 495 param[0].type = ACPI_TYPE_INTEGER; 496 param[0].integer.value = 0xC1; 497 param[1].type = ACPI_TYPE_INTEGER; 498 param[1].integer.value = value; 499 input.count = 2; 500 input.pointer = param; 501 status = acpi_evaluate_integer(handle, "GWRI", &input, &output); 502 return status; 503 } 504 505 static void cmpc_rfkill_query(struct rfkill *rfkill, void *data) 506 { 507 acpi_status status; 508 acpi_handle handle; 509 unsigned long long state; 510 bool blocked; 511 512 handle = data; 513 status = cmpc_get_rfkill_wlan(handle, &state); 514 if (ACPI_SUCCESS(status)) { 515 blocked = state & 1 ? false : true; 516 rfkill_set_sw_state(rfkill, blocked); 517 } 518 } 519 520 static int cmpc_rfkill_block(void *data, bool blocked) 521 { 522 acpi_status status; 523 acpi_handle handle; 524 unsigned long long state; 525 bool is_blocked; 526 527 handle = data; 528 status = cmpc_get_rfkill_wlan(handle, &state); 529 if (ACPI_FAILURE(status)) 530 return -ENODEV; 531 /* Check if we really need to call cmpc_set_rfkill_wlan */ 532 is_blocked = state & 1 ? false : true; 533 if (is_blocked != blocked) { 534 state = blocked ? 0 : 1; 535 status = cmpc_set_rfkill_wlan(handle, state); 536 if (ACPI_FAILURE(status)) 537 return -ENODEV; 538 } 539 return 0; 540 } 541 542 static const struct rfkill_ops cmpc_rfkill_ops = { 543 .query = cmpc_rfkill_query, 544 .set_block = cmpc_rfkill_block, 545 }; 546 547 /* 548 * Common backlight and rfkill code. 549 */ 550 551 struct ipml200_dev { 552 struct backlight_device *bd; 553 struct rfkill *rf; 554 }; 555 556 static int cmpc_ipml_add(struct acpi_device *acpi) 557 { 558 int retval; 559 struct ipml200_dev *ipml; 560 struct backlight_properties props; 561 562 ipml = kmalloc(sizeof(*ipml), GFP_KERNEL); 563 if (ipml == NULL) 564 return -ENOMEM; 565 566 memset(&props, 0, sizeof(struct backlight_properties)); 567 props.type = BACKLIGHT_PLATFORM; 568 props.max_brightness = 7; 569 ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev, 570 acpi->handle, &cmpc_bl_ops, 571 &props); 572 if (IS_ERR(ipml->bd)) { 573 retval = PTR_ERR(ipml->bd); 574 goto out_bd; 575 } 576 577 ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN, 578 &cmpc_rfkill_ops, acpi->handle); 579 /* 580 * If RFKILL is disabled, rfkill_alloc will return ERR_PTR(-ENODEV). 581 * This is OK, however, since all other uses of the device will not 582 * derefence it. 583 */ 584 if (ipml->rf) { 585 retval = rfkill_register(ipml->rf); 586 if (retval) { 587 rfkill_destroy(ipml->rf); 588 ipml->rf = NULL; 589 } 590 } 591 592 dev_set_drvdata(&acpi->dev, ipml); 593 return 0; 594 595 out_bd: 596 kfree(ipml); 597 return retval; 598 } 599 600 static int cmpc_ipml_remove(struct acpi_device *acpi, int type) 601 { 602 struct ipml200_dev *ipml; 603 604 ipml = dev_get_drvdata(&acpi->dev); 605 606 backlight_device_unregister(ipml->bd); 607 608 if (ipml->rf) { 609 rfkill_unregister(ipml->rf); 610 rfkill_destroy(ipml->rf); 611 } 612 613 kfree(ipml); 614 615 return 0; 616 } 617 618 static const struct acpi_device_id cmpc_ipml_device_ids[] = { 619 {CMPC_IPML_HID, 0}, 620 {"", 0} 621 }; 622 623 static struct acpi_driver cmpc_ipml_acpi_driver = { 624 .owner = THIS_MODULE, 625 .name = "cmpc", 626 .class = "cmpc", 627 .ids = cmpc_ipml_device_ids, 628 .ops = { 629 .add = cmpc_ipml_add, 630 .remove = cmpc_ipml_remove 631 } 632 }; 633 634 635 /* 636 * Extra keys code. 637 */ 638 static int cmpc_keys_codes[] = { 639 KEY_UNKNOWN, 640 KEY_WLAN, 641 KEY_SWITCHVIDEOMODE, 642 KEY_BRIGHTNESSDOWN, 643 KEY_BRIGHTNESSUP, 644 KEY_VENDOR, 645 KEY_UNKNOWN, 646 KEY_CAMERA, 647 KEY_BACK, 648 KEY_FORWARD, 649 KEY_MAX 650 }; 651 652 static void cmpc_keys_handler(struct acpi_device *dev, u32 event) 653 { 654 struct input_dev *inputdev; 655 int code = KEY_MAX; 656 657 if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes)) 658 code = cmpc_keys_codes[event & 0x0F]; 659 inputdev = dev_get_drvdata(&dev->dev); 660 input_report_key(inputdev, code, !(event & 0x10)); 661 input_sync(inputdev); 662 } 663 664 static void cmpc_keys_idev_init(struct input_dev *inputdev) 665 { 666 int i; 667 668 set_bit(EV_KEY, inputdev->evbit); 669 for (i = 0; cmpc_keys_codes[i] != KEY_MAX; i++) 670 set_bit(cmpc_keys_codes[i], inputdev->keybit); 671 } 672 673 static int cmpc_keys_add(struct acpi_device *acpi) 674 { 675 return cmpc_add_acpi_notify_device(acpi, "cmpc_keys", 676 cmpc_keys_idev_init); 677 } 678 679 static int cmpc_keys_remove(struct acpi_device *acpi, int type) 680 { 681 return cmpc_remove_acpi_notify_device(acpi); 682 } 683 684 static const struct acpi_device_id cmpc_keys_device_ids[] = { 685 {CMPC_KEYS_HID, 0}, 686 {"", 0} 687 }; 688 689 static struct acpi_driver cmpc_keys_acpi_driver = { 690 .owner = THIS_MODULE, 691 .name = "cmpc_keys", 692 .class = "cmpc_keys", 693 .ids = cmpc_keys_device_ids, 694 .ops = { 695 .add = cmpc_keys_add, 696 .remove = cmpc_keys_remove, 697 .notify = cmpc_keys_handler, 698 } 699 }; 700 701 702 /* 703 * General init/exit code. 704 */ 705 706 static int cmpc_init(void) 707 { 708 int r; 709 710 r = acpi_bus_register_driver(&cmpc_keys_acpi_driver); 711 if (r) 712 goto failed_keys; 713 714 r = acpi_bus_register_driver(&cmpc_ipml_acpi_driver); 715 if (r) 716 goto failed_bl; 717 718 r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver); 719 if (r) 720 goto failed_tablet; 721 722 r = acpi_bus_register_driver(&cmpc_accel_acpi_driver); 723 if (r) 724 goto failed_accel; 725 726 return r; 727 728 failed_accel: 729 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); 730 731 failed_tablet: 732 acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); 733 734 failed_bl: 735 acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); 736 737 failed_keys: 738 return r; 739 } 740 741 static void cmpc_exit(void) 742 { 743 acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); 744 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); 745 acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); 746 acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); 747 } 748 749 module_init(cmpc_init); 750 module_exit(cmpc_exit); 751 752 static const struct acpi_device_id cmpc_device_ids[] = { 753 {CMPC_ACCEL_HID, 0}, 754 {CMPC_TABLET_HID, 0}, 755 {CMPC_IPML_HID, 0}, 756 {CMPC_KEYS_HID, 0}, 757 {"", 0} 758 }; 759 760 MODULE_DEVICE_TABLE(acpi, cmpc_device_ids); 761