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