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