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/workqueue.h> 23 #include <acpi/acpi_drivers.h> 24 #include <linux/backlight.h> 25 #include <linux/input.h> 26 27 MODULE_LICENSE("GPL"); 28 29 30 struct cmpc_accel { 31 int sensitivity; 32 }; 33 34 #define CMPC_ACCEL_SENSITIVITY_DEFAULT 5 35 36 37 /* 38 * Generic input device code. 39 */ 40 41 typedef void (*input_device_init)(struct input_dev *dev); 42 43 static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name, 44 input_device_init idev_init) 45 { 46 struct input_dev *inputdev; 47 int error; 48 49 inputdev = input_allocate_device(); 50 if (!inputdev) 51 return -ENOMEM; 52 inputdev->name = name; 53 inputdev->dev.parent = &acpi->dev; 54 idev_init(inputdev); 55 error = input_register_device(inputdev); 56 if (error) { 57 input_free_device(inputdev); 58 return error; 59 } 60 dev_set_drvdata(&acpi->dev, inputdev); 61 return 0; 62 } 63 64 static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi) 65 { 66 struct input_dev *inputdev = dev_get_drvdata(&acpi->dev); 67 input_unregister_device(inputdev); 68 return 0; 69 } 70 71 /* 72 * Accelerometer code. 73 */ 74 static acpi_status cmpc_start_accel(acpi_handle handle) 75 { 76 union acpi_object param[2]; 77 struct acpi_object_list input; 78 acpi_status status; 79 80 param[0].type = ACPI_TYPE_INTEGER; 81 param[0].integer.value = 0x3; 82 param[1].type = ACPI_TYPE_INTEGER; 83 input.count = 2; 84 input.pointer = param; 85 status = acpi_evaluate_object(handle, "ACMD", &input, NULL); 86 return status; 87 } 88 89 static acpi_status cmpc_stop_accel(acpi_handle handle) 90 { 91 union acpi_object param[2]; 92 struct acpi_object_list input; 93 acpi_status status; 94 95 param[0].type = ACPI_TYPE_INTEGER; 96 param[0].integer.value = 0x4; 97 param[1].type = ACPI_TYPE_INTEGER; 98 input.count = 2; 99 input.pointer = param; 100 status = acpi_evaluate_object(handle, "ACMD", &input, NULL); 101 return status; 102 } 103 104 static acpi_status cmpc_accel_set_sensitivity(acpi_handle handle, int val) 105 { 106 union acpi_object param[2]; 107 struct acpi_object_list input; 108 109 param[0].type = ACPI_TYPE_INTEGER; 110 param[0].integer.value = 0x02; 111 param[1].type = ACPI_TYPE_INTEGER; 112 param[1].integer.value = val; 113 input.count = 2; 114 input.pointer = param; 115 return acpi_evaluate_object(handle, "ACMD", &input, NULL); 116 } 117 118 static acpi_status cmpc_get_accel(acpi_handle handle, 119 unsigned char *x, 120 unsigned char *y, 121 unsigned char *z) 122 { 123 union acpi_object param[2]; 124 struct acpi_object_list input; 125 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, 0 }; 126 unsigned char *locs; 127 acpi_status status; 128 129 param[0].type = ACPI_TYPE_INTEGER; 130 param[0].integer.value = 0x01; 131 param[1].type = ACPI_TYPE_INTEGER; 132 input.count = 2; 133 input.pointer = param; 134 status = acpi_evaluate_object(handle, "ACMD", &input, &output); 135 if (ACPI_SUCCESS(status)) { 136 union acpi_object *obj; 137 obj = output.pointer; 138 locs = obj->buffer.pointer; 139 *x = locs[0]; 140 *y = locs[1]; 141 *z = locs[2]; 142 kfree(output.pointer); 143 } 144 return status; 145 } 146 147 static void cmpc_accel_handler(struct acpi_device *dev, u32 event) 148 { 149 if (event == 0x81) { 150 unsigned char x, y, z; 151 acpi_status status; 152 153 status = cmpc_get_accel(dev->handle, &x, &y, &z); 154 if (ACPI_SUCCESS(status)) { 155 struct input_dev *inputdev = dev_get_drvdata(&dev->dev); 156 157 input_report_abs(inputdev, ABS_X, x); 158 input_report_abs(inputdev, ABS_Y, y); 159 input_report_abs(inputdev, ABS_Z, z); 160 input_sync(inputdev); 161 } 162 } 163 } 164 165 static ssize_t cmpc_accel_sensitivity_show(struct device *dev, 166 struct device_attribute *attr, 167 char *buf) 168 { 169 struct acpi_device *acpi; 170 struct input_dev *inputdev; 171 struct cmpc_accel *accel; 172 173 acpi = to_acpi_device(dev); 174 inputdev = dev_get_drvdata(&acpi->dev); 175 accel = dev_get_drvdata(&inputdev->dev); 176 177 return sprintf(buf, "%d\n", accel->sensitivity); 178 } 179 180 static ssize_t cmpc_accel_sensitivity_store(struct device *dev, 181 struct device_attribute *attr, 182 const char *buf, size_t count) 183 { 184 struct acpi_device *acpi; 185 struct input_dev *inputdev; 186 struct cmpc_accel *accel; 187 unsigned long sensitivity; 188 int r; 189 190 acpi = to_acpi_device(dev); 191 inputdev = dev_get_drvdata(&acpi->dev); 192 accel = dev_get_drvdata(&inputdev->dev); 193 194 r = strict_strtoul(buf, 0, &sensitivity); 195 if (r) 196 return r; 197 198 accel->sensitivity = sensitivity; 199 cmpc_accel_set_sensitivity(acpi->handle, sensitivity); 200 201 return strnlen(buf, count); 202 } 203 204 struct device_attribute cmpc_accel_sensitivity_attr = { 205 .attr = { .name = "sensitivity", .mode = 0660 }, 206 .show = cmpc_accel_sensitivity_show, 207 .store = cmpc_accel_sensitivity_store 208 }; 209 210 static int cmpc_accel_open(struct input_dev *input) 211 { 212 struct acpi_device *acpi; 213 214 acpi = to_acpi_device(input->dev.parent); 215 if (ACPI_SUCCESS(cmpc_start_accel(acpi->handle))) 216 return 0; 217 return -EIO; 218 } 219 220 static void cmpc_accel_close(struct input_dev *input) 221 { 222 struct acpi_device *acpi; 223 224 acpi = to_acpi_device(input->dev.parent); 225 cmpc_stop_accel(acpi->handle); 226 } 227 228 static void cmpc_accel_idev_init(struct input_dev *inputdev) 229 { 230 set_bit(EV_ABS, inputdev->evbit); 231 input_set_abs_params(inputdev, ABS_X, 0, 255, 8, 0); 232 input_set_abs_params(inputdev, ABS_Y, 0, 255, 8, 0); 233 input_set_abs_params(inputdev, ABS_Z, 0, 255, 8, 0); 234 inputdev->open = cmpc_accel_open; 235 inputdev->close = cmpc_accel_close; 236 } 237 238 static int cmpc_accel_add(struct acpi_device *acpi) 239 { 240 int error; 241 struct input_dev *inputdev; 242 struct cmpc_accel *accel; 243 244 accel = kmalloc(sizeof(*accel), GFP_KERNEL); 245 if (!accel) 246 return -ENOMEM; 247 248 accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT; 249 cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity); 250 251 error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr); 252 if (error) 253 goto failed_file; 254 255 error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel", 256 cmpc_accel_idev_init); 257 if (error) 258 goto failed_input; 259 260 inputdev = dev_get_drvdata(&acpi->dev); 261 dev_set_drvdata(&inputdev->dev, accel); 262 263 return 0; 264 265 failed_input: 266 device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); 267 failed_file: 268 kfree(accel); 269 return error; 270 } 271 272 static int cmpc_accel_remove(struct acpi_device *acpi, int type) 273 { 274 struct input_dev *inputdev; 275 struct cmpc_accel *accel; 276 277 inputdev = dev_get_drvdata(&acpi->dev); 278 accel = dev_get_drvdata(&inputdev->dev); 279 280 device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); 281 return cmpc_remove_acpi_notify_device(acpi); 282 } 283 284 static const struct acpi_device_id cmpc_accel_device_ids[] = { 285 {"ACCE0000", 0}, 286 {"", 0} 287 }; 288 MODULE_DEVICE_TABLE(acpi, cmpc_accel_device_ids); 289 290 static struct acpi_driver cmpc_accel_acpi_driver = { 291 .owner = THIS_MODULE, 292 .name = "cmpc_accel", 293 .class = "cmpc_accel", 294 .ids = cmpc_accel_device_ids, 295 .ops = { 296 .add = cmpc_accel_add, 297 .remove = cmpc_accel_remove, 298 .notify = cmpc_accel_handler, 299 } 300 }; 301 302 303 /* 304 * Tablet mode code. 305 */ 306 static acpi_status cmpc_get_tablet(acpi_handle handle, 307 unsigned long long *value) 308 { 309 union acpi_object param; 310 struct acpi_object_list input; 311 unsigned long long output; 312 acpi_status status; 313 314 param.type = ACPI_TYPE_INTEGER; 315 param.integer.value = 0x01; 316 input.count = 1; 317 input.pointer = ¶m; 318 status = acpi_evaluate_integer(handle, "TCMD", &input, &output); 319 if (ACPI_SUCCESS(status)) 320 *value = output; 321 return status; 322 } 323 324 static void cmpc_tablet_handler(struct acpi_device *dev, u32 event) 325 { 326 unsigned long long val = 0; 327 struct input_dev *inputdev = dev_get_drvdata(&dev->dev); 328 329 if (event == 0x81) { 330 if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val))) 331 input_report_switch(inputdev, SW_TABLET_MODE, !val); 332 } 333 } 334 335 static void cmpc_tablet_idev_init(struct input_dev *inputdev) 336 { 337 unsigned long long val = 0; 338 struct acpi_device *acpi; 339 340 set_bit(EV_SW, inputdev->evbit); 341 set_bit(SW_TABLET_MODE, inputdev->swbit); 342 343 acpi = to_acpi_device(inputdev->dev.parent); 344 if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) 345 input_report_switch(inputdev, SW_TABLET_MODE, !val); 346 } 347 348 static int cmpc_tablet_add(struct acpi_device *acpi) 349 { 350 return cmpc_add_acpi_notify_device(acpi, "cmpc_tablet", 351 cmpc_tablet_idev_init); 352 } 353 354 static int cmpc_tablet_remove(struct acpi_device *acpi, int type) 355 { 356 return cmpc_remove_acpi_notify_device(acpi); 357 } 358 359 static int cmpc_tablet_resume(struct acpi_device *acpi) 360 { 361 struct input_dev *inputdev = dev_get_drvdata(&acpi->dev); 362 unsigned long long val = 0; 363 if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) 364 input_report_switch(inputdev, SW_TABLET_MODE, !val); 365 return 0; 366 } 367 368 static const struct acpi_device_id cmpc_tablet_device_ids[] = { 369 {"TBLT0000", 0}, 370 {"", 0} 371 }; 372 MODULE_DEVICE_TABLE(acpi, cmpc_tablet_device_ids); 373 374 static struct acpi_driver cmpc_tablet_acpi_driver = { 375 .owner = THIS_MODULE, 376 .name = "cmpc_tablet", 377 .class = "cmpc_tablet", 378 .ids = cmpc_tablet_device_ids, 379 .ops = { 380 .add = cmpc_tablet_add, 381 .remove = cmpc_tablet_remove, 382 .resume = cmpc_tablet_resume, 383 .notify = cmpc_tablet_handler, 384 } 385 }; 386 387 388 /* 389 * Backlight code. 390 */ 391 392 static acpi_status cmpc_get_brightness(acpi_handle handle, 393 unsigned long long *value) 394 { 395 union acpi_object param; 396 struct acpi_object_list input; 397 unsigned long long output; 398 acpi_status status; 399 400 param.type = ACPI_TYPE_INTEGER; 401 param.integer.value = 0xC0; 402 input.count = 1; 403 input.pointer = ¶m; 404 status = acpi_evaluate_integer(handle, "GRDI", &input, &output); 405 if (ACPI_SUCCESS(status)) 406 *value = output; 407 return status; 408 } 409 410 static acpi_status cmpc_set_brightness(acpi_handle handle, 411 unsigned long long value) 412 { 413 union acpi_object param[2]; 414 struct acpi_object_list input; 415 acpi_status status; 416 unsigned long long output; 417 418 param[0].type = ACPI_TYPE_INTEGER; 419 param[0].integer.value = 0xC0; 420 param[1].type = ACPI_TYPE_INTEGER; 421 param[1].integer.value = value; 422 input.count = 2; 423 input.pointer = param; 424 status = acpi_evaluate_integer(handle, "GWRI", &input, &output); 425 return status; 426 } 427 428 static int cmpc_bl_get_brightness(struct backlight_device *bd) 429 { 430 acpi_status status; 431 acpi_handle handle; 432 unsigned long long brightness; 433 434 handle = bl_get_data(bd); 435 status = cmpc_get_brightness(handle, &brightness); 436 if (ACPI_SUCCESS(status)) 437 return brightness; 438 else 439 return -1; 440 } 441 442 static int cmpc_bl_update_status(struct backlight_device *bd) 443 { 444 acpi_status status; 445 acpi_handle handle; 446 447 handle = bl_get_data(bd); 448 status = cmpc_set_brightness(handle, bd->props.brightness); 449 if (ACPI_SUCCESS(status)) 450 return 0; 451 else 452 return -1; 453 } 454 455 static struct backlight_ops cmpc_bl_ops = { 456 .get_brightness = cmpc_bl_get_brightness, 457 .update_status = cmpc_bl_update_status 458 }; 459 460 static int cmpc_bl_add(struct acpi_device *acpi) 461 { 462 struct backlight_device *bd; 463 464 bd = backlight_device_register("cmpc_bl", &acpi->dev, 465 acpi->handle, &cmpc_bl_ops); 466 bd->props.max_brightness = 7; 467 dev_set_drvdata(&acpi->dev, bd); 468 return 0; 469 } 470 471 static int cmpc_bl_remove(struct acpi_device *acpi, int type) 472 { 473 struct backlight_device *bd; 474 475 bd = dev_get_drvdata(&acpi->dev); 476 backlight_device_unregister(bd); 477 return 0; 478 } 479 480 static const struct acpi_device_id cmpc_device_ids[] = { 481 {"IPML200", 0}, 482 {"", 0} 483 }; 484 MODULE_DEVICE_TABLE(acpi, cmpc_device_ids); 485 486 static struct acpi_driver cmpc_bl_acpi_driver = { 487 .owner = THIS_MODULE, 488 .name = "cmpc", 489 .class = "cmpc", 490 .ids = cmpc_device_ids, 491 .ops = { 492 .add = cmpc_bl_add, 493 .remove = cmpc_bl_remove 494 } 495 }; 496 497 498 /* 499 * Extra keys code. 500 */ 501 static int cmpc_keys_codes[] = { 502 KEY_UNKNOWN, 503 KEY_WLAN, 504 KEY_SWITCHVIDEOMODE, 505 KEY_BRIGHTNESSDOWN, 506 KEY_BRIGHTNESSUP, 507 KEY_VENDOR, 508 KEY_MAX 509 }; 510 511 static void cmpc_keys_handler(struct acpi_device *dev, u32 event) 512 { 513 struct input_dev *inputdev; 514 int code = KEY_MAX; 515 516 if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes)) 517 code = cmpc_keys_codes[event & 0x0F]; 518 inputdev = dev_get_drvdata(&dev->dev);; 519 input_report_key(inputdev, code, !(event & 0x10)); 520 } 521 522 static void cmpc_keys_idev_init(struct input_dev *inputdev) 523 { 524 int i; 525 526 set_bit(EV_KEY, inputdev->evbit); 527 for (i = 0; cmpc_keys_codes[i] != KEY_MAX; i++) 528 set_bit(cmpc_keys_codes[i], inputdev->keybit); 529 } 530 531 static int cmpc_keys_add(struct acpi_device *acpi) 532 { 533 return cmpc_add_acpi_notify_device(acpi, "cmpc_keys", 534 cmpc_keys_idev_init); 535 } 536 537 static int cmpc_keys_remove(struct acpi_device *acpi, int type) 538 { 539 return cmpc_remove_acpi_notify_device(acpi); 540 } 541 542 static const struct acpi_device_id cmpc_keys_device_ids[] = { 543 {"FnBT0000", 0}, 544 {"", 0} 545 }; 546 MODULE_DEVICE_TABLE(acpi, cmpc_keys_device_ids); 547 548 static struct acpi_driver cmpc_keys_acpi_driver = { 549 .owner = THIS_MODULE, 550 .name = "cmpc_keys", 551 .class = "cmpc_keys", 552 .ids = cmpc_keys_device_ids, 553 .ops = { 554 .add = cmpc_keys_add, 555 .remove = cmpc_keys_remove, 556 .notify = cmpc_keys_handler, 557 } 558 }; 559 560 561 /* 562 * General init/exit code. 563 */ 564 565 static int cmpc_init(void) 566 { 567 int r; 568 569 r = acpi_bus_register_driver(&cmpc_keys_acpi_driver); 570 if (r) 571 goto failed_keys; 572 573 r = acpi_bus_register_driver(&cmpc_bl_acpi_driver); 574 if (r) 575 goto failed_bl; 576 577 r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver); 578 if (r) 579 goto failed_tablet; 580 581 r = acpi_bus_register_driver(&cmpc_accel_acpi_driver); 582 if (r) 583 goto failed_accel; 584 585 return r; 586 587 failed_accel: 588 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); 589 590 failed_tablet: 591 acpi_bus_unregister_driver(&cmpc_bl_acpi_driver); 592 593 failed_bl: 594 acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); 595 596 failed_keys: 597 return r; 598 } 599 600 static void cmpc_exit(void) 601 { 602 acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); 603 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); 604 acpi_bus_unregister_driver(&cmpc_bl_acpi_driver); 605 acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); 606 } 607 608 module_init(cmpc_init); 609 module_exit(cmpc_exit); 610