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};
  35
  36#define CMPC_ACCEL_SENSITIVITY_DEFAULT          5
  37
  38
  39#define CMPC_ACCEL_HID          "ACCE0000"
  40#define CMPC_TABLET_HID         "TBLT0000"
  41#define CMPC_IPML_HID   "IPML200"
  42#define CMPC_KEYS_HID           "FnBT0000"
  43
  44/*
  45 * Generic input device code.
  46 */
  47
  48typedef void (*input_device_init)(struct input_dev *dev);
  49
  50static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name,
  51                                       input_device_init idev_init)
  52{
  53        struct input_dev *inputdev;
  54        int error;
  55
  56        inputdev = input_allocate_device();
  57        if (!inputdev)
  58                return -ENOMEM;
  59        inputdev->name = name;
  60        inputdev->dev.parent = &acpi->dev;
  61        idev_init(inputdev);
  62        error = input_register_device(inputdev);
  63        if (error) {
  64                input_free_device(inputdev);
  65                return error;
  66        }
  67        dev_set_drvdata(&acpi->dev, inputdev);
  68        return 0;
  69}
  70
  71static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi)
  72{
  73        struct input_dev *inputdev = dev_get_drvdata(&acpi->dev);
  74        input_unregister_device(inputdev);
  75        return 0;
  76}
  77
  78/*
  79 * Accelerometer code.
  80 */
  81static acpi_status cmpc_start_accel(acpi_handle handle)
  82{
  83        union acpi_object param[2];
  84        struct acpi_object_list input;
  85        acpi_status status;
  86
  87        param[0].type = ACPI_TYPE_INTEGER;
  88        param[0].integer.value = 0x3;
  89        param[1].type = ACPI_TYPE_INTEGER;
  90        input.count = 2;
  91        input.pointer = param;
  92        status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
  93        return status;
  94}
  95
  96static acpi_status cmpc_stop_accel(acpi_handle handle)
  97{
  98        union acpi_object param[2];
  99        struct acpi_object_list input;
 100        acpi_status status;
 101
 102        param[0].type = ACPI_TYPE_INTEGER;
 103        param[0].integer.value = 0x4;
 104        param[1].type = ACPI_TYPE_INTEGER;
 105        input.count = 2;
 106        input.pointer = param;
 107        status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
 108        return status;
 109}
 110
 111static acpi_status cmpc_accel_set_sensitivity(acpi_handle handle, int val)
 112{
 113        union acpi_object param[2];
 114        struct acpi_object_list input;
 115
 116        param[0].type = ACPI_TYPE_INTEGER;
 117        param[0].integer.value = 0x02;
 118        param[1].type = ACPI_TYPE_INTEGER;
 119        param[1].integer.value = val;
 120        input.count = 2;
 121        input.pointer = param;
 122        return acpi_evaluate_object(handle, "ACMD", &input, NULL);
 123}
 124
 125static acpi_status cmpc_get_accel(acpi_handle handle,
 126                                  unsigned char *x,
 127                                  unsigned char *y,
 128                                  unsigned char *z)
 129{
 130        union acpi_object param[2];
 131        struct acpi_object_list input;
 132        struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, 0 };
 133        unsigned char *locs;
 134        acpi_status status;
 135
 136        param[0].type = ACPI_TYPE_INTEGER;
 137        param[0].integer.value = 0x01;
 138        param[1].type = ACPI_TYPE_INTEGER;
 139        input.count = 2;
 140        input.pointer = param;
 141        status = acpi_evaluate_object(handle, "ACMD", &input, &output);
 142        if (ACPI_SUCCESS(status)) {
 143                union acpi_object *obj;
 144                obj = output.pointer;
 145                locs = obj->buffer.pointer;
 146                *x = locs[0];
 147                *y = locs[1];
 148                *z = locs[2];
 149                kfree(output.pointer);
 150        }
 151        return status;
 152}
 153
 154static void cmpc_accel_handler(struct acpi_device *dev, u32 event)
 155{
 156        if (event == 0x81) {
 157                unsigned char x, y, z;
 158                acpi_status status;
 159
 160                status = cmpc_get_accel(dev->handle, &x, &y, &z);
 161                if (ACPI_SUCCESS(status)) {
 162                        struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
 163
 164                        input_report_abs(inputdev, ABS_X, x);
 165                        input_report_abs(inputdev, ABS_Y, y);
 166                        input_report_abs(inputdev, ABS_Z, z);
 167                        input_sync(inputdev);
 168                }
 169        }
 170}
 171
 172static ssize_t cmpc_accel_sensitivity_show(struct device *dev,
 173                                           struct device_attribute *attr,
 174                                           char *buf)
 175{
 176        struct acpi_device *acpi;
 177        struct input_dev *inputdev;
 178        struct cmpc_accel *accel;
 179
 180        acpi = to_acpi_device(dev);
 181        inputdev = dev_get_drvdata(&acpi->dev);
 182        accel = dev_get_drvdata(&inputdev->dev);
 183
 184        return sprintf(buf, "%d\n", accel->sensitivity);
 185}
 186
 187static ssize_t cmpc_accel_sensitivity_store(struct device *dev,
 188                                            struct device_attribute *attr,
 189                                            const char *buf, size_t count)
 190{
 191        struct acpi_device *acpi;
 192        struct input_dev *inputdev;
 193        struct cmpc_accel *accel;
 194        unsigned long sensitivity;
 195        int r;
 196
 197        acpi = to_acpi_device(dev);
 198        inputdev = dev_get_drvdata(&acpi->dev);
 199        accel = dev_get_drvdata(&inputdev->dev);
 200
 201        r = strict_strtoul(buf, 0, &sensitivity);
 202        if (r)
 203                return r;
 204
 205        accel->sensitivity = sensitivity;
 206        cmpc_accel_set_sensitivity(acpi->handle, sensitivity);
 207
 208        return strnlen(buf, count);
 209}
 210
 211static struct device_attribute cmpc_accel_sensitivity_attr = {
 212        .attr = { .name = "sensitivity", .mode = 0660 },
 213        .show = cmpc_accel_sensitivity_show,
 214        .store = cmpc_accel_sensitivity_store
 215};
 216
 217static int cmpc_accel_open(struct input_dev *input)
 218{
 219        struct acpi_device *acpi;
 220
 221        acpi = to_acpi_device(input->dev.parent);
 222        if (ACPI_SUCCESS(cmpc_start_accel(acpi->handle)))
 223                return 0;
 224        return -EIO;
 225}
 226
 227static void cmpc_accel_close(struct input_dev *input)
 228{
 229        struct acpi_device *acpi;
 230
 231        acpi = to_acpi_device(input->dev.parent);
 232        cmpc_stop_accel(acpi->handle);
 233}
 234
 235static void cmpc_accel_idev_init(struct input_dev *inputdev)
 236{
 237        set_bit(EV_ABS, inputdev->evbit);
 238        input_set_abs_params(inputdev, ABS_X, 0, 255, 8, 0);
 239        input_set_abs_params(inputdev, ABS_Y, 0, 255, 8, 0);
 240        input_set_abs_params(inputdev, ABS_Z, 0, 255, 8, 0);
 241        inputdev->open = cmpc_accel_open;
 242        inputdev->close = cmpc_accel_close;
 243}
 244
 245static int cmpc_accel_add(struct acpi_device *acpi)
 246{
 247        int error;
 248        struct input_dev *inputdev;
 249        struct cmpc_accel *accel;
 250
 251        accel = kmalloc(sizeof(*accel), GFP_KERNEL);
 252        if (!accel)
 253                return -ENOMEM;
 254
 255        accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
 256        cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity);
 257
 258        error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
 259        if (error)
 260                goto failed_file;
 261
 262        error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel",
 263                                            cmpc_accel_idev_init);
 264        if (error)
 265                goto failed_input;
 266
 267        inputdev = dev_get_drvdata(&acpi->dev);
 268        dev_set_drvdata(&inputdev->dev, accel);
 269
 270        return 0;
 271
 272failed_input:
 273        device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
 274failed_file:
 275        kfree(accel);
 276        return error;
 277}
 278
 279static int cmpc_accel_remove(struct acpi_device *acpi, int type)
 280{
 281        struct input_dev *inputdev;
 282        struct cmpc_accel *accel;
 283
 284        inputdev = dev_get_drvdata(&acpi->dev);
 285        accel = dev_get_drvdata(&inputdev->dev);
 286
 287        device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
 288        return cmpc_remove_acpi_notify_device(acpi);
 289}
 290
 291static const struct acpi_device_id cmpc_accel_device_ids[] = {
 292        {CMPC_ACCEL_HID, 0},
 293        {"", 0}
 294};
 295
 296static struct acpi_driver cmpc_accel_acpi_driver = {
 297        .owner = THIS_MODULE,
 298        .name = "cmpc_accel",
 299        .class = "cmpc_accel",
 300        .ids = cmpc_accel_device_ids,
 301        .ops = {
 302                .add = cmpc_accel_add,
 303                .remove = cmpc_accel_remove,
 304                .notify = cmpc_accel_handler,
 305        }
 306};
 307
 308
 309/*
 310 * Tablet mode code.
 311 */
 312static acpi_status cmpc_get_tablet(acpi_handle handle,
 313                                   unsigned long long *value)
 314{
 315        union acpi_object param;
 316        struct acpi_object_list input;
 317        unsigned long long output;
 318        acpi_status status;
 319
 320        param.type = ACPI_TYPE_INTEGER;
 321        param.integer.value = 0x01;
 322        input.count = 1;
 323        input.pointer = &param;
 324        status = acpi_evaluate_integer(handle, "TCMD", &input, &output);
 325        if (ACPI_SUCCESS(status))
 326                *value = output;
 327        return status;
 328}
 329
 330static void cmpc_tablet_handler(struct acpi_device *dev, u32 event)
 331{
 332        unsigned long long val = 0;
 333        struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
 334
 335        if (event == 0x81) {
 336                if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val)))
 337                        input_report_switch(inputdev, SW_TABLET_MODE, !val);
 338        }
 339}
 340
 341static void cmpc_tablet_idev_init(struct input_dev *inputdev)
 342{
 343        unsigned long long val = 0;
 344        struct acpi_device *acpi;
 345
 346        set_bit(EV_SW, inputdev->evbit);
 347        set_bit(SW_TABLET_MODE, inputdev->swbit);
 348
 349        acpi = to_acpi_device(inputdev->dev.parent);
 350        if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val)))
 351                input_report_switch(inputdev, SW_TABLET_MODE, !val);
 352}
 353
 354static int cmpc_tablet_add(struct acpi_device *acpi)
 355{
 356        return cmpc_add_acpi_notify_device(acpi, "cmpc_tablet",
 357                                           cmpc_tablet_idev_init);
 358}
 359
 360static int cmpc_tablet_remove(struct acpi_device *acpi, int type)
 361{
 362        return cmpc_remove_acpi_notify_device(acpi);
 363}
 364
 365static int cmpc_tablet_resume(struct acpi_device *acpi)
 366{
 367        struct input_dev *inputdev = dev_get_drvdata(&acpi->dev);
 368        unsigned long long val = 0;
 369        if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val)))
 370                input_report_switch(inputdev, SW_TABLET_MODE, !val);
 371        return 0;
 372}
 373
 374static const struct acpi_device_id cmpc_tablet_device_ids[] = {
 375        {CMPC_TABLET_HID, 0},
 376        {"", 0}
 377};
 378
 379static struct acpi_driver cmpc_tablet_acpi_driver = {
 380        .owner = THIS_MODULE,
 381        .name = "cmpc_tablet",
 382        .class = "cmpc_tablet",
 383        .ids = cmpc_tablet_device_ids,
 384        .ops = {
 385                .add = cmpc_tablet_add,
 386                .remove = cmpc_tablet_remove,
 387                .resume = cmpc_tablet_resume,
 388                .notify = cmpc_tablet_handler,
 389        }
 390};
 391
 392
 393/*
 394 * Backlight code.
 395 */
 396
 397static acpi_status cmpc_get_brightness(acpi_handle handle,
 398                                       unsigned long long *value)
 399{
 400        union acpi_object param;
 401        struct acpi_object_list input;
 402        unsigned long long output;
 403        acpi_status status;
 404
 405        param.type = ACPI_TYPE_INTEGER;
 406        param.integer.value = 0xC0;
 407        input.count = 1;
 408        input.pointer = &param;
 409        status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
 410        if (ACPI_SUCCESS(status))
 411                *value = output;
 412        return status;
 413}
 414
 415static acpi_status cmpc_set_brightness(acpi_handle handle,
 416                                       unsigned long long value)
 417{
 418        union acpi_object param[2];
 419        struct acpi_object_list input;
 420        acpi_status status;
 421        unsigned long long output;
 422
 423        param[0].type = ACPI_TYPE_INTEGER;
 424        param[0].integer.value = 0xC0;
 425        param[1].type = ACPI_TYPE_INTEGER;
 426        param[1].integer.value = value;
 427        input.count = 2;
 428        input.pointer = param;
 429        status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
 430        return status;
 431}
 432
 433static int cmpc_bl_get_brightness(struct backlight_device *bd)
 434{
 435        acpi_status status;
 436        acpi_handle handle;
 437        unsigned long long brightness;
 438
 439        handle = bl_get_data(bd);
 440        status = cmpc_get_brightness(handle, &brightness);
 441        if (ACPI_SUCCESS(status))
 442                return brightness;
 443        else
 444                return -1;
 445}
 446
 447static int cmpc_bl_update_status(struct backlight_device *bd)
 448{
 449        acpi_status status;
 450        acpi_handle handle;
 451
 452        handle = bl_get_data(bd);
 453        status = cmpc_set_brightness(handle, bd->props.brightness);
 454        if (ACPI_SUCCESS(status))
 455                return 0;
 456        else
 457                return -1;
 458}
 459
 460static const struct backlight_ops cmpc_bl_ops = {
 461        .get_brightness = cmpc_bl_get_brightness,
 462        .update_status = cmpc_bl_update_status
 463};
 464
 465/*
 466 * RFKILL code.
 467 */
 468
 469static acpi_status cmpc_get_rfkill_wlan(acpi_handle handle,
 470                                        unsigned long long *value)
 471{
 472        union acpi_object param;
 473        struct acpi_object_list input;
 474        unsigned long long output;
 475        acpi_status status;
 476
 477        param.type = ACPI_TYPE_INTEGER;
 478        param.integer.value = 0xC1;
 479        input.count = 1;
 480        input.pointer = &param;
 481        status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
 482        if (ACPI_SUCCESS(status))
 483                *value = output;
 484        return status;
 485}
 486
 487static acpi_status cmpc_set_rfkill_wlan(acpi_handle handle,
 488                                        unsigned long long value)
 489{
 490        union acpi_object param[2];
 491        struct acpi_object_list input;
 492        acpi_status status;
 493        unsigned long long output;
 494
 495        param[0].type = ACPI_TYPE_INTEGER;
 496        param[0].integer.value = 0xC1;
 497        param[1].type = ACPI_TYPE_INTEGER;
 498        param[1].integer.value = value;
 499        input.count = 2;
 500        input.pointer = param;
 501        status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
 502        return status;
 503}
 504
 505static void cmpc_rfkill_query(struct rfkill *rfkill, void *data)
 506{
 507        acpi_status status;
 508        acpi_handle handle;
 509        unsigned long long state;
 510        bool blocked;
 511
 512        handle = data;
 513        status = cmpc_get_rfkill_wlan(handle, &state);
 514        if (ACPI_SUCCESS(status)) {
 515                blocked = state & 1 ? false : true;
 516                rfkill_set_sw_state(rfkill, blocked);
 517        }
 518}
 519
 520static int cmpc_rfkill_block(void *data, bool blocked)
 521{
 522        acpi_status status;
 523        acpi_handle handle;
 524        unsigned long long state;
 525        bool is_blocked;
 526
 527        handle = data;
 528        status = cmpc_get_rfkill_wlan(handle, &state);
 529        if (ACPI_FAILURE(status))
 530                return -ENODEV;
 531        /* Check if we really need to call cmpc_set_rfkill_wlan */
 532        is_blocked = state & 1 ? false : true;
 533        if (is_blocked != blocked) {
 534                state = blocked ? 0 : 1;
 535                status = cmpc_set_rfkill_wlan(handle, state);
 536                if (ACPI_FAILURE(status))
 537                        return -ENODEV;
 538        }
 539        return 0;
 540}
 541
 542static const struct rfkill_ops cmpc_rfkill_ops = {
 543        .query = cmpc_rfkill_query,
 544        .set_block = cmpc_rfkill_block,
 545};
 546
 547/*
 548 * Common backlight and rfkill code.
 549 */
 550
 551struct ipml200_dev {
 552        struct backlight_device *bd;
 553        struct rfkill *rf;
 554};
 555
 556static int cmpc_ipml_add(struct acpi_device *acpi)
 557{
 558        int retval;
 559        struct ipml200_dev *ipml;
 560        struct backlight_properties props;
 561
 562        ipml = kmalloc(sizeof(*ipml), GFP_KERNEL);
 563        if (ipml == NULL)
 564                return -ENOMEM;
 565
 566        memset(&props, 0, sizeof(struct backlight_properties));
 567        props.type = BACKLIGHT_PLATFORM;
 568        props.max_brightness = 7;
 569        ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev,
 570                                             acpi->handle, &cmpc_bl_ops,
 571                                             &props);
 572        if (IS_ERR(ipml->bd)) {
 573                retval = PTR_ERR(ipml->bd);
 574                goto out_bd;
 575        }
 576
 577        ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN,
 578                                &cmpc_rfkill_ops, acpi->handle);
 579        /*
 580         * If RFKILL is disabled, rfkill_alloc will return ERR_PTR(-ENODEV).
 581         * This is OK, however, since all other uses of the device will not
 582         * derefence it.
 583         */
 584        if (ipml->rf) {
 585                retval = rfkill_register(ipml->rf);
 586                if (retval) {
 587                        rfkill_destroy(ipml->rf);
 588                        ipml->rf = NULL;
 589                }
 590        }
 591
 592        dev_set_drvdata(&acpi->dev, ipml);
 593        return 0;
 594
 595out_bd:
 596        kfree(ipml);
 597        return retval;
 598}
 599
 600static int cmpc_ipml_remove(struct acpi_device *acpi, int type)
 601{
 602        struct ipml200_dev *ipml;
 603
 604        ipml = dev_get_drvdata(&acpi->dev);
 605
 606        backlight_device_unregister(ipml->bd);
 607
 608        if (ipml->rf) {
 609                rfkill_unregister(ipml->rf);
 610                rfkill_destroy(ipml->rf);
 611        }
 612
 613        kfree(ipml);
 614
 615        return 0;
 616}
 617
 618static const struct acpi_device_id cmpc_ipml_device_ids[] = {
 619        {CMPC_IPML_HID, 0},
 620        {"", 0}
 621};
 622
 623static struct acpi_driver cmpc_ipml_acpi_driver = {
 624        .owner = THIS_MODULE,
 625        .name = "cmpc",
 626        .class = "cmpc",
 627        .ids = cmpc_ipml_device_ids,
 628        .ops = {
 629                .add = cmpc_ipml_add,
 630                .remove = cmpc_ipml_remove
 631        }
 632};
 633
 634
 635/*
 636 * Extra keys code.
 637 */
 638static int cmpc_keys_codes[] = {
 639        KEY_UNKNOWN,
 640        KEY_WLAN,
 641        KEY_SWITCHVIDEOMODE,
 642        KEY_BRIGHTNESSDOWN,
 643        KEY_BRIGHTNESSUP,
 644        KEY_VENDOR,
 645        KEY_UNKNOWN,
 646        KEY_CAMERA,
 647        KEY_BACK,
 648        KEY_FORWARD,
 649        KEY_MAX
 650};
 651
 652static void cmpc_keys_handler(struct acpi_device *dev, u32 event)
 653{
 654        struct input_dev *inputdev;
 655        int code = KEY_MAX;
 656
 657        if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes))
 658                code = cmpc_keys_codes[event & 0x0F];
 659        inputdev = dev_get_drvdata(&dev->dev);
 660        input_report_key(inputdev, code, !(event & 0x10));
 661        input_sync(inputdev);
 662}
 663
 664static void cmpc_keys_idev_init(struct input_dev *inputdev)
 665{
 666        int i;
 667
 668        set_bit(EV_KEY, inputdev->evbit);
 669        for (i = 0; cmpc_keys_codes[i] != KEY_MAX; i++)
 670                set_bit(cmpc_keys_codes[i], inputdev->keybit);
 671}
 672
 673static int cmpc_keys_add(struct acpi_device *acpi)
 674{
 675        return cmpc_add_acpi_notify_device(acpi, "cmpc_keys",
 676                                           cmpc_keys_idev_init);
 677}
 678
 679static int cmpc_keys_remove(struct acpi_device *acpi, int type)
 680{
 681        return cmpc_remove_acpi_notify_device(acpi);
 682}
 683
 684static const struct acpi_device_id cmpc_keys_device_ids[] = {
 685        {CMPC_KEYS_HID, 0},
 686        {"", 0}
 687};
 688
 689static struct acpi_driver cmpc_keys_acpi_driver = {
 690        .owner = THIS_MODULE,
 691        .name = "cmpc_keys",
 692        .class = "cmpc_keys",
 693        .ids = cmpc_keys_device_ids,
 694        .ops = {
 695                .add = cmpc_keys_add,
 696                .remove = cmpc_keys_remove,
 697                .notify = cmpc_keys_handler,
 698        }
 699};
 700
 701
 702/*
 703 * General init/exit code.
 704 */
 705
 706static int cmpc_init(void)
 707{
 708        int r;
 709
 710        r = acpi_bus_register_driver(&cmpc_keys_acpi_driver);
 711        if (r)
 712                goto failed_keys;
 713
 714        r = acpi_bus_register_driver(&cmpc_ipml_acpi_driver);
 715        if (r)
 716                goto failed_bl;
 717
 718        r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver);
 719        if (r)
 720                goto failed_tablet;
 721
 722        r = acpi_bus_register_driver(&cmpc_accel_acpi_driver);
 723        if (r)
 724                goto failed_accel;
 725
 726        return r;
 727
 728failed_accel:
 729        acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
 730
 731failed_tablet:
 732        acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
 733
 734failed_bl:
 735        acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
 736
 737failed_keys:
 738        return r;
 739}
 740
 741static void cmpc_exit(void)
 742{
 743        acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
 744        acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
 745        acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
 746        acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
 747}
 748
 749module_init(cmpc_init);
 750module_exit(cmpc_exit);
 751
 752static const struct acpi_device_id cmpc_device_ids[] = {
 753        {CMPC_ACCEL_HID, 0},
 754        {CMPC_TABLET_HID, 0},
 755        {CMPC_IPML_HID, 0},
 756        {CMPC_KEYS_HID, 0},
 757        {"", 0}
 758};
 759
 760MODULE_DEVICE_TABLE(acpi, cmpc_device_ids);
 761