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