FILE
classmate-laptop.c
File preview with syntax highlighting.
linux-6.19.14/drivers/platform/x86/classmate-laptop.c
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (C) 2009 Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com> 4 */ 5 6 7#include <linux/init.h> 8#include <linux/module.h> 9#include <linux/slab.h> 10#include <linux/workqueue.h> 11#include <linux/acpi.h> 12#include <linux/backlight.h> 13#include <linux/input.h> 14#include <linux/rfkill.h> 15#include <linux/sysfs.h> 16 17struct cmpc_accel { 18 int sensitivity; 19 int g_select; 20 int inputdev_state; 21}; 22 23#define CMPC_ACCEL_DEV_STATE_CLOSED 0 24#define CMPC_ACCEL_DEV_STATE_OPEN 1 25 26#define CMPC_ACCEL_SENSITIVITY_DEFAULT 5 27#define CMPC_ACCEL_G_SELECT_DEFAULT 0 28 29#define CMPC_ACCEL_HID "ACCE0000" 30#define CMPC_ACCEL_HID_V4 "ACCE0001" 31#define CMPC_TABLET_HID "TBLT0000" 32#define CMPC_IPML_HID "IPML200" 33#define CMPC_KEYS_HID "FNBT0000" 34 35/* 36 * Generic input device code. 37 */ 38 39typedef void (*input_device_init)(struct input_dev *dev); 40 41static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name, 42 input_device_init idev_init) 43{ 44 struct input_dev *inputdev; 45 int error; 46 47 inputdev = input_allocate_device(); 48 if (!inputdev) 49 return -ENOMEM; 50 inputdev->name = name; 51 inputdev->dev.parent = &acpi->dev; 52 idev_init(inputdev); 53 error = input_register_device(inputdev); 54 if (error) { 55 input_free_device(inputdev); 56 return error; 57 } 58 dev_set_drvdata(&acpi->dev, inputdev); 59 return 0; 60} 61 62static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi) 63{ 64 struct input_dev *inputdev = dev_get_drvdata(&acpi->dev); 65 input_unregister_device(inputdev); 66 return 0; 67} 68 69/* 70 * Accelerometer code for Classmate V4 71 */ 72static acpi_status cmpc_start_accel_v4(acpi_handle handle) 73{ 74 union acpi_object param[4]; 75 struct acpi_object_list input; 76 acpi_status status; 77 78 param[0].type = ACPI_TYPE_INTEGER; 79 param[0].integer.value = 0x3; 80 param[1].type = ACPI_TYPE_INTEGER; 81 param[1].integer.value = 0; 82 param[2].type = ACPI_TYPE_INTEGER; 83 param[2].integer.value = 0; 84 param[3].type = ACPI_TYPE_INTEGER; 85 param[3].integer.value = 0; 86 input.count = 4; 87 input.pointer = param; 88 status = acpi_evaluate_object(handle, "ACMD", &input, NULL); 89 return status; 90} 91 92static acpi_status cmpc_stop_accel_v4(acpi_handle handle) 93{ 94 union acpi_object param[4]; 95 struct acpi_object_list input; 96 acpi_status status; 97 98 param[0].type = ACPI_TYPE_INTEGER; 99 param[0].integer.value = 0x4; 100 param[1].type = ACPI_TYPE_INTEGER; 101 param[1].integer.value = 0; 102 param[2].type = ACPI_TYPE_INTEGER; 103 param[2].integer.value = 0; 104 param[3].type = ACPI_TYPE_INTEGER; 105 param[3].integer.value = 0; 106 input.count = 4; 107 input.pointer = param; 108 status = acpi_evaluate_object(handle, "ACMD", &input, NULL); 109 return status; 110} 111 112static acpi_status cmpc_accel_set_sensitivity_v4(acpi_handle handle, int val) 113{ 114 union acpi_object param[4]; 115 struct acpi_object_list input; 116 117 param[0].type = ACPI_TYPE_INTEGER; 118 param[0].integer.value = 0x02; 119 param[1].type = ACPI_TYPE_INTEGER; 120 param[1].integer.value = val; 121 param[2].type = ACPI_TYPE_INTEGER; 122 param[2].integer.value = 0; 123 param[3].type = ACPI_TYPE_INTEGER; 124 param[3].integer.value = 0; 125 input.count = 4; 126 input.pointer = param; 127 return acpi_evaluate_object(handle, "ACMD", &input, NULL); 128} 129 130static acpi_status cmpc_accel_set_g_select_v4(acpi_handle handle, int val) 131{ 132 union acpi_object param[4]; 133 struct acpi_object_list input; 134 135 param[0].type = ACPI_TYPE_INTEGER; 136 param[0].integer.value = 0x05; 137 param[1].type = ACPI_TYPE_INTEGER; 138 param[1].integer.value = val; 139 param[2].type = ACPI_TYPE_INTEGER; 140 param[2].integer.value = 0; 141 param[3].type = ACPI_TYPE_INTEGER; 142 param[3].integer.value = 0; 143 input.count = 4; 144 input.pointer = param; 145 return acpi_evaluate_object(handle, "ACMD", &input, NULL); 146} 147 148static acpi_status cmpc_get_accel_v4(acpi_handle handle, 149 int16_t *x, 150 int16_t *y, 151 int16_t *z) 152{ 153 union acpi_object param[4]; 154 struct acpi_object_list input; 155 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 156 int16_t *locs; 157 acpi_status status; 158 159 param[0].type = ACPI_TYPE_INTEGER; 160 param[0].integer.value = 0x01; 161 param[1].type = ACPI_TYPE_INTEGER; 162 param[1].integer.value = 0; 163 param[2].type = ACPI_TYPE_INTEGER; 164 param[2].integer.value = 0; 165 param[3].type = ACPI_TYPE_INTEGER; 166 param[3].integer.value = 0; 167 input.count = 4; 168 input.pointer = param; 169 status = acpi_evaluate_object(handle, "ACMD", &input, &output); 170 if (ACPI_SUCCESS(status)) { 171 union acpi_object *obj; 172 obj = output.pointer; 173 locs = (int16_t *) obj->buffer.pointer; 174 *x = locs[0]; 175 *y = locs[1]; 176 *z = locs[2]; 177 kfree(output.pointer); 178 } 179 return status; 180} 181 182static void cmpc_accel_handler_v4(struct acpi_device *dev, u32 event) 183{ 184 if (event == 0x81) { 185 int16_t x, y, z; 186 acpi_status status; 187 188 status = cmpc_get_accel_v4(dev->handle, &x, &y, &z); 189 if (ACPI_SUCCESS(status)) { 190 struct input_dev *inputdev = dev_get_drvdata(&dev->dev); 191 192 input_report_abs(inputdev, ABS_X, x); 193 input_report_abs(inputdev, ABS_Y, y); 194 input_report_abs(inputdev, ABS_Z, z); 195 input_sync(inputdev); 196 } 197 } 198} 199 200static ssize_t cmpc_accel_sensitivity_show_v4(struct device *dev, 201 struct device_attribute *attr, 202 char *buf) 203{ 204 struct acpi_device *acpi; 205 struct input_dev *inputdev; 206 struct cmpc_accel *accel; 207 208 acpi = to_acpi_device(dev); 209 inputdev = dev_get_drvdata(&acpi->dev); 210 if (!inputdev) 211 return -ENXIO; 212 213 accel = dev_get_drvdata(&inputdev->dev); 214 if (!accel) 215 return -ENXIO; 216 217 return sysfs_emit(buf, "%d\n", accel->sensitivity); 218} 219 220static ssize_t cmpc_accel_sensitivity_store_v4(struct device *dev, 221 struct device_attribute *attr, 222 const char *buf, size_t count) 223{ 224 struct acpi_device *acpi; 225 struct input_dev *inputdev; 226 struct cmpc_accel *accel; 227 unsigned long sensitivity; 228 int r; 229 230 acpi = to_acpi_device(dev); 231 inputdev = dev_get_drvdata(&acpi->dev); 232 if (!inputdev) 233 return -ENXIO; 234 235 accel = dev_get_drvdata(&inputdev->dev); 236 if (!accel) 237 return -ENXIO; 238 239 r = kstrtoul(buf, 0, &sensitivity); 240 if (r) 241 return r; 242 243 /* sensitivity must be between 1 and 127 */ 244 if (sensitivity < 1 || sensitivity > 127) 245 return -EINVAL; 246 247 accel->sensitivity = sensitivity; 248 cmpc_accel_set_sensitivity_v4(acpi->handle, sensitivity); 249 250 return strnlen(buf, count); 251} 252 253static struct device_attribute cmpc_accel_sensitivity_attr_v4 = { 254 .attr = { .name = "sensitivity", .mode = 0660 }, 255 .show = cmpc_accel_sensitivity_show_v4, 256 .store = cmpc_accel_sensitivity_store_v4 257}; 258 259static ssize_t cmpc_accel_g_select_show_v4(struct device *dev, 260 struct device_attribute *attr, 261 char *buf) 262{ 263 struct acpi_device *acpi; 264 struct input_dev *inputdev; 265 struct cmpc_accel *accel; 266 267 acpi = to_acpi_device(dev); 268 inputdev = dev_get_drvdata(&acpi->dev); 269 if (!inputdev) 270 return -ENXIO; 271 272 accel = dev_get_drvdata(&inputdev->dev); 273 if (!accel) 274 return -ENXIO; 275 276 return sysfs_emit(buf, "%d\n", accel->g_select); 277} 278 279static ssize_t cmpc_accel_g_select_store_v4(struct device *dev, 280 struct device_attribute *attr, 281 const char *buf, size_t count) 282{ 283 struct acpi_device *acpi; 284 struct input_dev *inputdev; 285 struct cmpc_accel *accel; 286 unsigned long g_select; 287 int r; 288 289 acpi = to_acpi_device(dev); 290 inputdev = dev_get_drvdata(&acpi->dev); 291 if (!inputdev) 292 return -ENXIO; 293 294 accel = dev_get_drvdata(&inputdev->dev); 295 if (!accel) 296 return -ENXIO; 297 298 r = kstrtoul(buf, 0, &g_select); 299 if (r) 300 return r; 301 302 /* 0 means 1.5g, 1 means 6g, everything else is wrong */ 303 if (g_select != 0 && g_select != 1) 304 return -EINVAL; 305 306 accel->g_select = g_select; 307 cmpc_accel_set_g_select_v4(acpi->handle, g_select); 308 309 return strnlen(buf, count); 310} 311 312static struct device_attribute cmpc_accel_g_select_attr_v4 = { 313 .attr = { .name = "g_select", .mode = 0660 }, 314 .show = cmpc_accel_g_select_show_v4, 315 .store = cmpc_accel_g_select_store_v4 316}; 317 318static int cmpc_accel_open_v4(struct input_dev *input) 319{ 320 struct acpi_device *acpi; 321 struct cmpc_accel *accel; 322 323 acpi = to_acpi_device(input->dev.parent); 324 accel = dev_get_drvdata(&input->dev); 325 if (!accel) 326 return -ENXIO; 327 328 cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity); 329 cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select); 330 331 if (ACPI_SUCCESS(cmpc_start_accel_v4(acpi->handle))) { 332 accel->inputdev_state = CMPC_ACCEL_DEV_STATE_OPEN; 333 return 0; 334 } 335 return -EIO; 336} 337 338static void cmpc_accel_close_v4(struct input_dev *input) 339{ 340 struct acpi_device *acpi; 341 struct cmpc_accel *accel; 342 343 acpi = to_acpi_device(input->dev.parent); 344 accel = dev_get_drvdata(&input->dev); 345 346 cmpc_stop_accel_v4(acpi->handle); 347 accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED; 348} 349 350static void cmpc_accel_idev_init_v4(struct input_dev *inputdev) 351{ 352 set_bit(EV_ABS, inputdev->evbit); 353 input_set_abs_params(inputdev, ABS_X, -255, 255, 16, 0); 354 input_set_abs_params(inputdev, ABS_Y, -255, 255, 16, 0); 355 input_set_abs_params(inputdev, ABS_Z, -255, 255, 16, 0); 356 inputdev->open = cmpc_accel_open_v4; 357 inputdev->close = cmpc_accel_close_v4; 358} 359 360#ifdef CONFIG_PM_SLEEP 361static int cmpc_accel_suspend_v4(struct device *dev) 362{ 363 struct input_dev *inputdev; 364 struct cmpc_accel *accel; 365 366 inputdev = dev_get_drvdata(dev); 367 accel = dev_get_drvdata(&inputdev->dev); 368 369 if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN) 370 return cmpc_stop_accel_v4(to_acpi_device(dev)->handle); 371 372 return 0; 373} 374 375static int cmpc_accel_resume_v4(struct device *dev) 376{ 377 struct input_dev *inputdev; 378 struct cmpc_accel *accel; 379 380 inputdev = dev_get_drvdata(dev); 381 accel = dev_get_drvdata(&inputdev->dev); 382 383 if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN) { 384 cmpc_accel_set_sensitivity_v4(to_acpi_device(dev)->handle, 385 accel->sensitivity); 386 cmpc_accel_set_g_select_v4(to_acpi_device(dev)->handle, 387 accel->g_select); 388 389 if (ACPI_FAILURE(cmpc_start_accel_v4(to_acpi_device(dev)->handle))) 390 return -EIO; 391 } 392 393 return 0; 394} 395#endif 396 397static int cmpc_accel_add_v4(struct acpi_device *acpi) 398{ 399 int error; 400 struct input_dev *inputdev; 401 struct cmpc_accel *accel; 402 403 accel = kmalloc(sizeof(*accel), GFP_KERNEL); 404 if (!accel) 405 return -ENOMEM; 406 407 accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED; 408 409 accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT; 410 cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity); 411 412 error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); 413 if (error) 414 goto failed_sensitivity; 415 416 accel->g_select = CMPC_ACCEL_G_SELECT_DEFAULT; 417 cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select); 418 419 error = device_create_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); 420 if (error) 421 goto failed_g_select; 422 423 error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel_v4", 424 cmpc_accel_idev_init_v4); 425 if (error) 426 goto failed_input; 427 428 inputdev = dev_get_drvdata(&acpi->dev); 429 dev_set_drvdata(&inputdev->dev, accel); 430 431 return 0; 432 433failed_input: 434 device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); 435failed_g_select: 436 device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); 437failed_sensitivity: 438 kfree(accel); 439 return error; 440} 441 442static void cmpc_accel_remove_v4(struct acpi_device *acpi) 443{ 444 device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); 445 device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); 446 cmpc_remove_acpi_notify_device(acpi); 447} 448 449static SIMPLE_DEV_PM_OPS(cmpc_accel_pm, cmpc_accel_suspend_v4, 450 cmpc_accel_resume_v4); 451 452static const struct acpi_device_id cmpc_accel_device_ids_v4[] = { 453 {CMPC_ACCEL_HID_V4, 0}, 454 {"", 0} 455}; 456 457static struct acpi_driver cmpc_accel_acpi_driver_v4 = { 458 .name = "cmpc_accel_v4", 459 .class = "cmpc_accel_v4", 460 .ids = cmpc_accel_device_ids_v4, 461 .ops = { 462 .add = cmpc_accel_add_v4, 463 .remove = cmpc_accel_remove_v4, 464 .notify = cmpc_accel_handler_v4, 465 }, 466 .drv.pm = &cmpc_accel_pm, 467}; 468 469 470/* 471 * Accelerometer code for Classmate versions prior to V4 472 */ 473static acpi_status cmpc_start_accel(acpi_handle handle) 474{ 475 union acpi_object param[2]; 476 struct acpi_object_list input; 477 acpi_status status; 478 479 param[0].type = ACPI_TYPE_INTEGER; 480 param[0].integer.value = 0x3; 481 param[1].type = ACPI_TYPE_INTEGER; 482 input.count = 2; 483 input.pointer = param; 484 status = acpi_evaluate_object(handle, "ACMD", &input, NULL); 485 return status; 486} 487 488static acpi_status cmpc_stop_accel(acpi_handle handle) 489{ 490 union acpi_object param[2]; 491 struct acpi_object_list input; 492 acpi_status status; 493 494 param[0].type = ACPI_TYPE_INTEGER; 495 param[0].integer.value = 0x4; 496 param[1].type = ACPI_TYPE_INTEGER; 497 input.count = 2; 498 input.pointer = param; 499 status = acpi_evaluate_object(handle, "ACMD", &input, NULL); 500 return status; 501} 502 503static acpi_status cmpc_accel_set_sensitivity(acpi_handle handle, int val) 504{ 505 union acpi_object param[2]; 506 struct acpi_object_list input; 507 508 param[0].type = ACPI_TYPE_INTEGER; 509 param[0].integer.value = 0x02; 510 param[1].type = ACPI_TYPE_INTEGER; 511 param[1].integer.value = val; 512 input.count = 2; 513 input.pointer = param; 514 return acpi_evaluate_object(handle, "ACMD", &input, NULL); 515} 516 517static acpi_status cmpc_get_accel(acpi_handle handle, 518 unsigned char *x, 519 unsigned char *y, 520 unsigned char *z) 521{ 522 union acpi_object param[2]; 523 struct acpi_object_list input; 524 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 525 unsigned char *locs; 526 acpi_status status; 527 528 param[0].type = ACPI_TYPE_INTEGER; 529 param[0].integer.value = 0x01; 530 param[1].type = ACPI_TYPE_INTEGER; 531 input.count = 2; 532 input.pointer = param; 533 status = acpi_evaluate_object(handle, "ACMD", &input, &output); 534 if (ACPI_SUCCESS(status)) { 535 union acpi_object *obj; 536 obj = output.pointer; 537 locs = obj->buffer.pointer; 538 *x = locs[0]; 539 *y = locs[1]; 540 *z = locs[2]; 541 kfree(output.pointer); 542 } 543 return status; 544} 545 546static void cmpc_accel_handler(struct acpi_device *dev, u32 event) 547{ 548 if (event == 0x81) { 549 unsigned char x, y, z; 550 acpi_status status; 551 552 status = cmpc_get_accel(dev->handle, &x, &y, &z); 553 if (ACPI_SUCCESS(status)) { 554 struct input_dev *inputdev = dev_get_drvdata(&dev->dev); 555 556 input_report_abs(inputdev, ABS_X, x); 557 input_report_abs(inputdev, ABS_Y, y); 558 input_report_abs(inputdev, ABS_Z, z); 559 input_sync(inputdev); 560 } 561 } 562} 563 564static ssize_t cmpc_accel_sensitivity_show(struct device *dev, 565 struct device_attribute *attr, 566 char *buf) 567{ 568 struct acpi_device *acpi; 569 struct input_dev *inputdev; 570 struct cmpc_accel *accel; 571 572 acpi = to_acpi_device(dev); 573 inputdev = dev_get_drvdata(&acpi->dev); 574 if (!inputdev) 575 return -ENXIO; 576 577 accel = dev_get_drvdata(&inputdev->dev); 578 if (!accel) 579 return -ENXIO; 580 581 return sysfs_emit(buf, "%d\n", accel->sensitivity); 582} 583 584static ssize_t cmpc_accel_sensitivity_store(struct device *dev, 585 struct device_attribute *attr, 586 const char *buf, size_t count) 587{ 588 struct acpi_device *acpi; 589 struct input_dev *inputdev; 590 struct cmpc_accel *accel; 591 unsigned long sensitivity; 592 int r; 593 594 acpi = to_acpi_device(dev); 595 inputdev = dev_get_drvdata(&acpi->dev); 596 if (!inputdev) 597 return -ENXIO; 598 599 accel = dev_get_drvdata(&inputdev->dev); 600 if (!accel) 601 return -ENXIO; 602 603 r = kstrtoul(buf, 0, &sensitivity); 604 if (r) 605 return r; 606 607 accel->sensitivity = sensitivity; 608 cmpc_accel_set_sensitivity(acpi->handle, sensitivity); 609 610 return strnlen(buf, count); 611} 612 613static struct device_attribute cmpc_accel_sensitivity_attr = { 614 .attr = { .name = "sensitivity", .mode = 0660 }, 615 .show = cmpc_accel_sensitivity_show, 616 .store = cmpc_accel_sensitivity_store 617}; 618 619static int cmpc_accel_open(struct input_dev *input) 620{ 621 struct acpi_device *acpi; 622 623 acpi = to_acpi_device(input->dev.parent); 624 if (ACPI_SUCCESS(cmpc_start_accel(acpi->handle))) 625 return 0; 626 return -EIO; 627} 628 629static void cmpc_accel_close(struct input_dev *input) 630{ 631 struct acpi_device *acpi; 632 633 acpi = to_acpi_device(input->dev.parent); 634 cmpc_stop_accel(acpi->handle); 635} 636 637static void cmpc_accel_idev_init(struct input_dev *inputdev) 638{ 639 set_bit(EV_ABS, inputdev->evbit); 640 input_set_abs_params(inputdev, ABS_X, 0, 255, 8, 0); 641 input_set_abs_params(inputdev, ABS_Y, 0, 255, 8, 0); 642 input_set_abs_params(inputdev, ABS_Z, 0, 255, 8, 0); 643 inputdev->open = cmpc_accel_open; 644 inputdev->close = cmpc_accel_close; 645} 646 647static int cmpc_accel_add(struct acpi_device *acpi) 648{ 649 int error; 650 struct input_dev *inputdev; 651 struct cmpc_accel *accel; 652 653 accel = kmalloc(sizeof(*accel), GFP_KERNEL); 654 if (!accel) 655 return -ENOMEM; 656 657 accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT; 658 cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity); 659 660 error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr); 661 if (error) 662 goto failed_file; 663 664 error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel", 665 cmpc_accel_idev_init); 666 if (error) 667 goto failed_input; 668 669 inputdev = dev_get_drvdata(&acpi->dev); 670 dev_set_drvdata(&inputdev->dev, accel); 671 672 return 0; 673 674failed_input: 675 device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); 676failed_file: 677 kfree(accel); 678 return error; 679} 680 681static void cmpc_accel_remove(struct acpi_device *acpi) 682{ 683 device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); 684 cmpc_remove_acpi_notify_device(acpi); 685} 686 687static const struct acpi_device_id cmpc_accel_device_ids[] = { 688 {CMPC_ACCEL_HID, 0}, 689 {"", 0} 690}; 691 692static struct acpi_driver cmpc_accel_acpi_driver = { 693 .name = "cmpc_accel", 694 .class = "cmpc_accel", 695 .ids = cmpc_accel_device_ids, 696 .ops = { 697 .add = cmpc_accel_add, 698 .remove = cmpc_accel_remove, 699 .notify = cmpc_accel_handler, 700 } 701}; 702 703 704/* 705 * Tablet mode code. 706 */ 707static acpi_status cmpc_get_tablet(acpi_handle handle, 708 unsigned long long *value) 709{ 710 union acpi_object param; 711 struct acpi_object_list input; 712 unsigned long long output; 713 acpi_status status; 714 715 param.type = ACPI_TYPE_INTEGER; 716 param.integer.value = 0x01; 717 input.count = 1; 718 input.pointer = ¶m; 719 status = acpi_evaluate_integer(handle, "TCMD", &input, &output); 720 if (ACPI_SUCCESS(status)) 721 *value = output; 722 return status; 723} 724 725static void cmpc_tablet_handler(struct acpi_device *dev, u32 event) 726{ 727 unsigned long long val = 0; 728 struct input_dev *inputdev = dev_get_drvdata(&dev->dev); 729 730 if (event == 0x81) { 731 if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val))) { 732 input_report_switch(inputdev, SW_TABLET_MODE, !val); 733 input_sync(inputdev); 734 } 735 } 736} 737 738static void cmpc_tablet_idev_init(struct input_dev *inputdev) 739{ 740 unsigned long long val = 0; 741 struct acpi_device *acpi; 742 743 set_bit(EV_SW, inputdev->evbit); 744 set_bit(SW_TABLET_MODE, inputdev->swbit); 745 746 acpi = to_acpi_device(inputdev->dev.parent); 747 if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) { 748 input_report_switch(inputdev, SW_TABLET_MODE, !val); 749 input_sync(inputdev); 750 } 751} 752 753static int cmpc_tablet_add(struct acpi_device *acpi) 754{ 755 return cmpc_add_acpi_notify_device(acpi, "cmpc_tablet", 756 cmpc_tablet_idev_init); 757} 758 759static void cmpc_tablet_remove(struct acpi_device *acpi) 760{ 761 cmpc_remove_acpi_notify_device(acpi); 762} 763 764#ifdef CONFIG_PM_SLEEP 765static int cmpc_tablet_resume(struct device *dev) 766{ 767 struct input_dev *inputdev = dev_get_drvdata(dev); 768 769 unsigned long long val = 0; 770 if (ACPI_SUCCESS(cmpc_get_tablet(to_acpi_device(dev)->handle, &val))) { 771 input_report_switch(inputdev, SW_TABLET_MODE, !val); 772 input_sync(inputdev); 773 } 774 return 0; 775} 776#endif 777 778static SIMPLE_DEV_PM_OPS(cmpc_tablet_pm, NULL, cmpc_tablet_resume); 779 780static const struct acpi_device_id cmpc_tablet_device_ids[] = { 781 {CMPC_TABLET_HID, 0}, 782 {"", 0} 783}; 784 785static struct acpi_driver cmpc_tablet_acpi_driver = { 786 .name = "cmpc_tablet", 787 .class = "cmpc_tablet", 788 .ids = cmpc_tablet_device_ids, 789 .ops = { 790 .add = cmpc_tablet_add, 791 .remove = cmpc_tablet_remove, 792 .notify = cmpc_tablet_handler, 793 }, 794 .drv.pm = &cmpc_tablet_pm, 795}; 796 797 798/* 799 * Backlight code. 800 */ 801 802static acpi_status cmpc_get_brightness(acpi_handle handle, 803 unsigned long long *value) 804{ 805 union acpi_object param; 806 struct acpi_object_list input; 807 unsigned long long output; 808 acpi_status status; 809 810 param.type = ACPI_TYPE_INTEGER; 811 param.integer.value = 0xC0; 812 input.count = 1; 813 input.pointer = ¶m; 814 status = acpi_evaluate_integer(handle, "GRDI", &input, &output); 815 if (ACPI_SUCCESS(status)) 816 *value = output; 817 return status; 818} 819 820static acpi_status cmpc_set_brightness(acpi_handle handle, 821 unsigned long long value) 822{ 823 union acpi_object param[2]; 824 struct acpi_object_list input; 825 acpi_status status; 826 unsigned long long output; 827 828 param[0].type = ACPI_TYPE_INTEGER; 829 param[0].integer.value = 0xC0; 830 param[1].type = ACPI_TYPE_INTEGER; 831 param[1].integer.value = value; 832 input.count = 2; 833 input.pointer = param; 834 status = acpi_evaluate_integer(handle, "GWRI", &input, &output); 835 return status; 836} 837 838static int cmpc_bl_get_brightness(struct backlight_device *bd) 839{ 840 acpi_status status; 841 acpi_handle handle; 842 unsigned long long brightness; 843 844 handle = bl_get_data(bd); 845 status = cmpc_get_brightness(handle, &brightness); 846 if (ACPI_SUCCESS(status)) 847 return brightness; 848 else 849 return -1; 850} 851 852static int cmpc_bl_update_status(struct backlight_device *bd) 853{ 854 acpi_status status; 855 acpi_handle handle; 856 857 handle = bl_get_data(bd); 858 status = cmpc_set_brightness(handle, bd->props.brightness); 859 if (ACPI_SUCCESS(status)) 860 return 0; 861 else 862 return -1; 863} 864 865static const struct backlight_ops cmpc_bl_ops = { 866 .get_brightness = cmpc_bl_get_brightness, 867 .update_status = cmpc_bl_update_status 868}; 869 870/* 871 * RFKILL code. 872 */ 873 874static acpi_status cmpc_get_rfkill_wlan(acpi_handle handle, 875 unsigned long long *value) 876{ 877 union acpi_object param; 878 struct acpi_object_list input; 879 unsigned long long output; 880 acpi_status status; 881 882 param.type = ACPI_TYPE_INTEGER; 883 param.integer.value = 0xC1; 884 input.count = 1; 885 input.pointer = ¶m; 886 status = acpi_evaluate_integer(handle, "GRDI", &input, &output); 887 if (ACPI_SUCCESS(status)) 888 *value = output; 889 return status; 890} 891 892static acpi_status cmpc_set_rfkill_wlan(acpi_handle handle, 893 unsigned long long value) 894{ 895 union acpi_object param[2]; 896 struct acpi_object_list input; 897 acpi_status status; 898 unsigned long long output; 899 900 param[0].type = ACPI_TYPE_INTEGER; 901 param[0].integer.value = 0xC1; 902 param[1].type = ACPI_TYPE_INTEGER; 903 param[1].integer.value = value; 904 input.count = 2; 905 input.pointer = param; 906 status = acpi_evaluate_integer(handle, "GWRI", &input, &output); 907 return status; 908} 909 910static void cmpc_rfkill_query(struct rfkill *rfkill, void *data) 911{ 912 acpi_status status; 913 acpi_handle handle; 914 unsigned long long state; 915 bool blocked; 916 917 handle = data; 918 status = cmpc_get_rfkill_wlan(handle, &state); 919 if (ACPI_SUCCESS(status)) { 920 blocked = state & 1 ? false : true; 921 rfkill_set_sw_state(rfkill, blocked); 922 } 923} 924 925static int cmpc_rfkill_block(void *data, bool blocked) 926{ 927 acpi_status status; 928 acpi_handle handle; 929 unsigned long long state; 930 bool is_blocked; 931 932 handle = data; 933 status = cmpc_get_rfkill_wlan(handle, &state); 934 if (ACPI_FAILURE(status)) 935 return -ENODEV; 936 /* Check if we really need to call cmpc_set_rfkill_wlan */ 937 is_blocked = state & 1 ? false : true; 938 if (is_blocked != blocked) { 939 state = blocked ? 0 : 1; 940 status = cmpc_set_rfkill_wlan(handle, state); 941 if (ACPI_FAILURE(status)) 942 return -ENODEV; 943 } 944 return 0; 945} 946 947static const struct rfkill_ops cmpc_rfkill_ops = { 948 .query = cmpc_rfkill_query, 949 .set_block = cmpc_rfkill_block, 950}; 951 952/* 953 * Common backlight and rfkill code. 954 */ 955 956struct ipml200_dev { 957 struct backlight_device *bd; 958 struct rfkill *rf; 959}; 960 961static int cmpc_ipml_add(struct acpi_device *acpi) 962{ 963 int retval; 964 struct ipml200_dev *ipml; 965 struct backlight_properties props; 966 967 ipml = kmalloc(sizeof(*ipml), GFP_KERNEL); 968 if (ipml == NULL) 969 return -ENOMEM; 970 971 memset(&props, 0, sizeof(struct backlight_properties)); 972 props.type = BACKLIGHT_PLATFORM; 973 props.max_brightness = 7; 974 ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev, 975 acpi->handle, &cmpc_bl_ops, 976 &props); 977 if (IS_ERR(ipml->bd)) { 978 retval = PTR_ERR(ipml->bd); 979 goto out_bd; 980 } 981 982 ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN, 983 &cmpc_rfkill_ops, acpi->handle); 984 /* 985 * If RFKILL is disabled, rfkill_alloc will return ERR_PTR(-ENODEV). 986 * This is OK, however, since all other uses of the device will not 987 * dereference it. 988 */ 989 if (ipml->rf) { 990 retval = rfkill_register(ipml->rf); 991 if (retval) { 992 rfkill_destroy(ipml->rf); 993 ipml->rf = NULL; 994 } 995 } 996 997 dev_set_drvdata(&acpi->dev, ipml); 998 return 0; 999 1000out_bd: 1001 kfree(ipml); 1002 return retval; 1003} 1004 1005static void cmpc_ipml_remove(struct acpi_device *acpi) 1006{ 1007 struct ipml200_dev *ipml; 1008 1009 ipml = dev_get_drvdata(&acpi->dev); 1010 1011 backlight_device_unregister(ipml->bd); 1012 1013 if (ipml->rf) { 1014 rfkill_unregister(ipml->rf); 1015 rfkill_destroy(ipml->rf); 1016 } 1017 1018 kfree(ipml); 1019} 1020 1021static const struct acpi_device_id cmpc_ipml_device_ids[] = { 1022 {CMPC_IPML_HID, 0}, 1023 {"", 0} 1024}; 1025 1026static struct acpi_driver cmpc_ipml_acpi_driver = { 1027 .name = "cmpc", 1028 .class = "cmpc", 1029 .ids = cmpc_ipml_device_ids, 1030 .ops = { 1031 .add = cmpc_ipml_add, 1032 .remove = cmpc_ipml_remove 1033 } 1034}; 1035 1036 1037/* 1038 * Extra keys code. 1039 */ 1040static int cmpc_keys_codes[] = { 1041 KEY_UNKNOWN, 1042 KEY_WLAN, 1043 KEY_SWITCHVIDEOMODE, 1044 KEY_BRIGHTNESSDOWN, 1045 KEY_BRIGHTNESSUP, 1046 KEY_VENDOR, 1047 KEY_UNKNOWN, 1048 KEY_CAMERA, 1049 KEY_BACK, 1050 KEY_FORWARD, 1051 KEY_UNKNOWN, 1052 KEY_WLAN, /* NL3: 0x8b (press), 0x9b (release) */ 1053 KEY_MAX 1054}; 1055 1056static void cmpc_keys_handler(struct acpi_device *dev, u32 event) 1057{ 1058 struct input_dev *inputdev; 1059 int code = KEY_MAX; 1060 1061 if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes)) 1062 code = cmpc_keys_codes[event & 0x0F]; 1063 inputdev = dev_get_drvdata(&dev->dev); 1064 input_report_key(inputdev, code, !(event & 0x10)); 1065 input_sync(inputdev); 1066} 1067 1068static void cmpc_keys_idev_init(struct input_dev *inputdev) 1069{ 1070 int i; 1071 1072 set_bit(EV_KEY, inputdev->evbit); 1073 for (i = 0; cmpc_keys_codes[i] != KEY_MAX; i++) 1074 set_bit(cmpc_keys_codes[i], inputdev->keybit); 1075} 1076 1077static int cmpc_keys_add(struct acpi_device *acpi) 1078{ 1079 return cmpc_add_acpi_notify_device(acpi, "cmpc_keys", 1080 cmpc_keys_idev_init); 1081} 1082 1083static void cmpc_keys_remove(struct acpi_device *acpi) 1084{ 1085 cmpc_remove_acpi_notify_device(acpi); 1086} 1087 1088static const struct acpi_device_id cmpc_keys_device_ids[] = { 1089 {CMPC_KEYS_HID, 0}, 1090 {"", 0} 1091}; 1092 1093static struct acpi_driver cmpc_keys_acpi_driver = { 1094 .name = "cmpc_keys", 1095 .class = "cmpc_keys", 1096 .ids = cmpc_keys_device_ids, 1097 .ops = { 1098 .add = cmpc_keys_add, 1099 .remove = cmpc_keys_remove, 1100 .notify = cmpc_keys_handler, 1101 } 1102}; 1103 1104 1105/* 1106 * General init/exit code. 1107 */ 1108 1109static int cmpc_init(void) 1110{ 1111 int r; 1112 1113 r = acpi_bus_register_driver(&cmpc_keys_acpi_driver); 1114 if (r) 1115 goto failed_keys; 1116 1117 r = acpi_bus_register_driver(&cmpc_ipml_acpi_driver); 1118 if (r) 1119 goto failed_bl; 1120 1121 r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver); 1122 if (r) 1123 goto failed_tablet; 1124 1125 r = acpi_bus_register_driver(&cmpc_accel_acpi_driver); 1126 if (r) 1127 goto failed_accel; 1128 1129 r = acpi_bus_register_driver(&cmpc_accel_acpi_driver_v4); 1130 if (r) 1131 goto failed_accel_v4; 1132 1133 return r; 1134 1135failed_accel_v4: 1136 acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); 1137 1138failed_accel: 1139 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); 1140 1141failed_tablet: 1142 acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); 1143 1144failed_bl: 1145 acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); 1146 1147failed_keys: 1148 return r; 1149} 1150 1151static void cmpc_exit(void) 1152{ 1153 acpi_bus_unregister_driver(&cmpc_accel_acpi_driver_v4); 1154 acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); 1155 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); 1156 acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); 1157 acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); 1158} 1159 1160module_init(cmpc_init); 1161module_exit(cmpc_exit); 1162 1163static const struct acpi_device_id cmpc_device_ids[] __maybe_unused = { 1164 {CMPC_ACCEL_HID, 0}, 1165 {CMPC_ACCEL_HID_V4, 0}, 1166 {CMPC_TABLET_HID, 0}, 1167 {CMPC_IPML_HID, 0}, 1168 {CMPC_KEYS_HID, 0}, 1169 {"", 0} 1170}; 1171 1172MODULE_DEVICE_TABLE(acpi, cmpc_device_ids); 1173MODULE_DESCRIPTION("Support for Intel Classmate PC ACPI devices"); 1174MODULE_LICENSE("GPL");
~/
Read-only file preview.