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