linux/drivers/platform/x86/dell-laptop.c
<<
>>
Prefs
   1/*
   2 *  Driver for Dell laptop extras
   3 *
   4 *  Copyright (c) Red Hat <mjg@redhat.com>
   5 *
   6 *  Based on documentation in the libsmbios package, Copyright (C) 2005 Dell
   7 *  Inc.
   8 *
   9 *  This program is free software; you can redistribute it and/or modify
  10 *  it under the terms of the GNU General Public License version 2 as
  11 *  published by the Free Software Foundation.
  12 */
  13
  14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  15
  16#include <linux/module.h>
  17#include <linux/kernel.h>
  18#include <linux/init.h>
  19#include <linux/platform_device.h>
  20#include <linux/backlight.h>
  21#include <linux/err.h>
  22#include <linux/dmi.h>
  23#include <linux/io.h>
  24#include <linux/rfkill.h>
  25#include <linux/power_supply.h>
  26#include <linux/acpi.h>
  27#include <linux/mm.h>
  28#include <linux/i8042.h>
  29#include <linux/slab.h>
  30#include <linux/debugfs.h>
  31#include <linux/seq_file.h>
  32#include "../../firmware/dcdbas.h"
  33
  34#define BRIGHTNESS_TOKEN 0x7d
  35
  36/* This structure will be modified by the firmware when we enter
  37 * system management mode, hence the volatiles */
  38
  39struct calling_interface_buffer {
  40        u16 class;
  41        u16 select;
  42        volatile u32 input[4];
  43        volatile u32 output[4];
  44} __packed;
  45
  46struct calling_interface_token {
  47        u16 tokenID;
  48        u16 location;
  49        union {
  50                u16 value;
  51                u16 stringlength;
  52        };
  53};
  54
  55struct calling_interface_structure {
  56        struct dmi_header header;
  57        u16 cmdIOAddress;
  58        u8 cmdIOCode;
  59        u32 supportedCmds;
  60        struct calling_interface_token tokens[];
  61} __packed;
  62
  63struct quirk_entry {
  64        u8 touchpad_led;
  65};
  66
  67static struct quirk_entry *quirks;
  68
  69static struct quirk_entry quirk_dell_vostro_v130 = {
  70        .touchpad_led = 1,
  71};
  72
  73static int dmi_matched(const struct dmi_system_id *dmi)
  74{
  75        quirks = dmi->driver_data;
  76        return 1;
  77}
  78
  79static int da_command_address;
  80static int da_command_code;
  81static int da_num_tokens;
  82static struct calling_interface_token *da_tokens;
  83
  84static struct platform_driver platform_driver = {
  85        .driver = {
  86                .name = "dell-laptop",
  87                .owner = THIS_MODULE,
  88        }
  89};
  90
  91static struct platform_device *platform_device;
  92static struct backlight_device *dell_backlight_device;
  93static struct rfkill *wifi_rfkill;
  94static struct rfkill *bluetooth_rfkill;
  95static struct rfkill *wwan_rfkill;
  96
  97static const struct dmi_system_id __initdata dell_device_table[] = {
  98        {
  99                .ident = "Dell laptop",
 100                .matches = {
 101                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
 102                        DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
 103                },
 104        },
 105        {
 106                .matches = {
 107                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
 108                        DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/
 109                },
 110        },
 111        {
 112                .ident = "Dell Computer Corporation",
 113                .matches = {
 114                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
 115                        DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
 116                },
 117        },
 118        { }
 119};
 120
 121static struct dmi_system_id __devinitdata dell_blacklist[] = {
 122        /* Supported by compal-laptop */
 123        {
 124                .ident = "Dell Mini 9",
 125                .matches = {
 126                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
 127                        DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"),
 128                },
 129        },
 130        {
 131                .ident = "Dell Mini 10",
 132                .matches = {
 133                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
 134                        DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"),
 135                },
 136        },
 137        {
 138                .ident = "Dell Mini 10v",
 139                .matches = {
 140                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
 141                        DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"),
 142                },
 143        },
 144        {
 145                .ident = "Dell Mini 1012",
 146                .matches = {
 147                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
 148                        DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1012"),
 149                },
 150        },
 151        {
 152                .ident = "Dell Inspiron 11z",
 153                .matches = {
 154                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
 155                        DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"),
 156                },
 157        },
 158        {
 159                .ident = "Dell Mini 12",
 160                .matches = {
 161                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
 162                        DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"),
 163                },
 164        },
 165        {}
 166};
 167
 168static struct dmi_system_id __devinitdata dell_quirks[] = {
 169        {
 170                .callback = dmi_matched,
 171                .ident = "Dell Vostro V130",
 172                .matches = {
 173                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
 174                        DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V130"),
 175                },
 176                .driver_data = &quirk_dell_vostro_v130,
 177        },
 178        {
 179                .callback = dmi_matched,
 180                .ident = "Dell Vostro V131",
 181                .matches = {
 182                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
 183                        DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"),
 184                },
 185                .driver_data = &quirk_dell_vostro_v130,
 186        },
 187};
 188
 189static struct calling_interface_buffer *buffer;
 190static struct page *bufferpage;
 191static DEFINE_MUTEX(buffer_mutex);
 192
 193static int hwswitch_state;
 194
 195static void get_buffer(void)
 196{
 197        mutex_lock(&buffer_mutex);
 198        memset(buffer, 0, sizeof(struct calling_interface_buffer));
 199}
 200
 201static void release_buffer(void)
 202{
 203        mutex_unlock(&buffer_mutex);
 204}
 205
 206static void __init parse_da_table(const struct dmi_header *dm)
 207{
 208        /* Final token is a terminator, so we don't want to copy it */
 209        int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1;
 210        struct calling_interface_structure *table =
 211                container_of(dm, struct calling_interface_structure, header);
 212
 213        /* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
 214           6 bytes of entry */
 215
 216        if (dm->length < 17)
 217                return;
 218
 219        da_command_address = table->cmdIOAddress;
 220        da_command_code = table->cmdIOCode;
 221
 222        da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) *
 223                             sizeof(struct calling_interface_token),
 224                             GFP_KERNEL);
 225
 226        if (!da_tokens)
 227                return;
 228
 229        memcpy(da_tokens+da_num_tokens, table->tokens,
 230               sizeof(struct calling_interface_token) * tokens);
 231
 232        da_num_tokens += tokens;
 233}
 234
 235static void __init find_tokens(const struct dmi_header *dm, void *dummy)
 236{
 237        switch (dm->type) {
 238        case 0xd4: /* Indexed IO */
 239                break;
 240        case 0xd5: /* Protected Area Type 1 */
 241                break;
 242        case 0xd6: /* Protected Area Type 2 */
 243                break;
 244        case 0xda: /* Calling interface */
 245                parse_da_table(dm);
 246                break;
 247        }
 248}
 249
 250static int find_token_location(int tokenid)
 251{
 252        int i;
 253        for (i = 0; i < da_num_tokens; i++) {
 254                if (da_tokens[i].tokenID == tokenid)
 255                        return da_tokens[i].location;
 256        }
 257
 258        return -1;
 259}
 260
 261static struct calling_interface_buffer *
 262dell_send_request(struct calling_interface_buffer *buffer, int class,
 263                  int select)
 264{
 265        struct smi_cmd command;
 266
 267        command.magic = SMI_CMD_MAGIC;
 268        command.command_address = da_command_address;
 269        command.command_code = da_command_code;
 270        command.ebx = virt_to_phys(buffer);
 271        command.ecx = 0x42534931;
 272
 273        buffer->class = class;
 274        buffer->select = select;
 275
 276        dcdbas_smi_request(&command);
 277
 278        return buffer;
 279}
 280
 281/* Derived from information in DellWirelessCtl.cpp:
 282   Class 17, select 11 is radio control. It returns an array of 32-bit values.
 283
 284   Input byte 0 = 0: Wireless information
 285
 286   result[0]: return code
 287   result[1]:
 288     Bit 0:      Hardware switch supported
 289     Bit 1:      Wifi locator supported
 290     Bit 2:      Wifi is supported
 291     Bit 3:      Bluetooth is supported
 292     Bit 4:      WWAN is supported
 293     Bit 5:      Wireless keyboard supported
 294     Bits 6-7:   Reserved
 295     Bit 8:      Wifi is installed
 296     Bit 9:      Bluetooth is installed
 297     Bit 10:     WWAN is installed
 298     Bits 11-15: Reserved
 299     Bit 16:     Hardware switch is on
 300     Bit 17:     Wifi is blocked
 301     Bit 18:     Bluetooth is blocked
 302     Bit 19:     WWAN is blocked
 303     Bits 20-31: Reserved
 304   result[2]: NVRAM size in bytes
 305   result[3]: NVRAM format version number
 306
 307   Input byte 0 = 2: Wireless switch configuration
 308   result[0]: return code
 309   result[1]:
 310     Bit 0:      Wifi controlled by switch
 311     Bit 1:      Bluetooth controlled by switch
 312     Bit 2:      WWAN controlled by switch
 313     Bits 3-6:   Reserved
 314     Bit 7:      Wireless switch config locked
 315     Bit 8:      Wifi locator enabled
 316     Bits 9-14:  Reserved
 317     Bit 15:     Wifi locator setting locked
 318     Bits 16-31: Reserved
 319*/
 320
 321static int dell_rfkill_set(void *data, bool blocked)
 322{
 323        int disable = blocked ? 1 : 0;
 324        unsigned long radio = (unsigned long)data;
 325        int hwswitch_bit = (unsigned long)data - 1;
 326        int ret = 0;
 327
 328        get_buffer();
 329        dell_send_request(buffer, 17, 11);
 330
 331        /* If the hardware switch controls this radio, and the hardware
 332           switch is disabled, don't allow changing the software state */
 333        if ((hwswitch_state & BIT(hwswitch_bit)) &&
 334            !(buffer->output[1] & BIT(16))) {
 335                ret = -EINVAL;
 336                goto out;
 337        }
 338
 339        buffer->input[0] = (1 | (radio<<8) | (disable << 16));
 340        dell_send_request(buffer, 17, 11);
 341
 342out:
 343        release_buffer();
 344        return ret;
 345}
 346
 347static void dell_rfkill_query(struct rfkill *rfkill, void *data)
 348{
 349        int status;
 350        int bit = (unsigned long)data + 16;
 351        int hwswitch_bit = (unsigned long)data - 1;
 352
 353        get_buffer();
 354        dell_send_request(buffer, 17, 11);
 355        status = buffer->output[1];
 356        release_buffer();
 357
 358        rfkill_set_sw_state(rfkill, !!(status & BIT(bit)));
 359
 360        if (hwswitch_state & (BIT(hwswitch_bit)))
 361                rfkill_set_hw_state(rfkill, !(status & BIT(16)));
 362}
 363
 364static const struct rfkill_ops dell_rfkill_ops = {
 365        .set_block = dell_rfkill_set,
 366        .query = dell_rfkill_query,
 367};
 368
 369static struct dentry *dell_laptop_dir;
 370
 371static int dell_debugfs_show(struct seq_file *s, void *data)
 372{
 373        int status;
 374
 375        get_buffer();
 376        dell_send_request(buffer, 17, 11);
 377        status = buffer->output[1];
 378        release_buffer();
 379
 380        seq_printf(s, "status:\t0x%X\n", status);
 381        seq_printf(s, "Bit 0 : Hardware switch supported:   %lu\n",
 382                   status & BIT(0));
 383        seq_printf(s, "Bit 1 : Wifi locator supported:      %lu\n",
 384                  (status & BIT(1)) >> 1);
 385        seq_printf(s, "Bit 2 : Wifi is supported:           %lu\n",
 386                  (status & BIT(2)) >> 2);
 387        seq_printf(s, "Bit 3 : Bluetooth is supported:      %lu\n",
 388                  (status & BIT(3)) >> 3);
 389        seq_printf(s, "Bit 4 : WWAN is supported:           %lu\n",
 390                  (status & BIT(4)) >> 4);
 391        seq_printf(s, "Bit 5 : Wireless keyboard supported: %lu\n",
 392                  (status & BIT(5)) >> 5);
 393        seq_printf(s, "Bit 8 : Wifi is installed:           %lu\n",
 394                  (status & BIT(8)) >> 8);
 395        seq_printf(s, "Bit 9 : Bluetooth is installed:      %lu\n",
 396                  (status & BIT(9)) >> 9);
 397        seq_printf(s, "Bit 10: WWAN is installed:           %lu\n",
 398                  (status & BIT(10)) >> 10);
 399        seq_printf(s, "Bit 16: Hardware switch is on:       %lu\n",
 400                  (status & BIT(16)) >> 16);
 401        seq_printf(s, "Bit 17: Wifi is blocked:             %lu\n",
 402                  (status & BIT(17)) >> 17);
 403        seq_printf(s, "Bit 18: Bluetooth is blocked:        %lu\n",
 404                  (status & BIT(18)) >> 18);
 405        seq_printf(s, "Bit 19: WWAN is blocked:             %lu\n",
 406                  (status & BIT(19)) >> 19);
 407
 408        seq_printf(s, "\nhwswitch_state:\t0x%X\n", hwswitch_state);
 409        seq_printf(s, "Bit 0 : Wifi controlled by switch:      %lu\n",
 410                   hwswitch_state & BIT(0));
 411        seq_printf(s, "Bit 1 : Bluetooth controlled by switch: %lu\n",
 412                   (hwswitch_state & BIT(1)) >> 1);
 413        seq_printf(s, "Bit 2 : WWAN controlled by switch:      %lu\n",
 414                   (hwswitch_state & BIT(2)) >> 2);
 415        seq_printf(s, "Bit 7 : Wireless switch config locked:  %lu\n",
 416                   (hwswitch_state & BIT(7)) >> 7);
 417        seq_printf(s, "Bit 8 : Wifi locator enabled:           %lu\n",
 418                   (hwswitch_state & BIT(8)) >> 8);
 419        seq_printf(s, "Bit 15: Wifi locator setting locked:    %lu\n",
 420                   (hwswitch_state & BIT(15)) >> 15);
 421
 422        return 0;
 423}
 424
 425static int dell_debugfs_open(struct inode *inode, struct file *file)
 426{
 427        return single_open(file, dell_debugfs_show, inode->i_private);
 428}
 429
 430static const struct file_operations dell_debugfs_fops = {
 431        .owner = THIS_MODULE,
 432        .open = dell_debugfs_open,
 433        .read = seq_read,
 434        .llseek = seq_lseek,
 435        .release = single_release,
 436};
 437
 438static void dell_update_rfkill(struct work_struct *ignored)
 439{
 440        if (wifi_rfkill)
 441                dell_rfkill_query(wifi_rfkill, (void *)1);
 442        if (bluetooth_rfkill)
 443                dell_rfkill_query(bluetooth_rfkill, (void *)2);
 444        if (wwan_rfkill)
 445                dell_rfkill_query(wwan_rfkill, (void *)3);
 446}
 447static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
 448
 449
 450static int __init dell_setup_rfkill(void)
 451{
 452        int status;
 453        int ret;
 454
 455        if (dmi_check_system(dell_blacklist)) {
 456                pr_info("Blacklisted hardware detected - not enabling rfkill\n");
 457                return 0;
 458        }
 459
 460        get_buffer();
 461        dell_send_request(buffer, 17, 11);
 462        status = buffer->output[1];
 463        buffer->input[0] = 0x2;
 464        dell_send_request(buffer, 17, 11);
 465        hwswitch_state = buffer->output[1];
 466        release_buffer();
 467
 468        if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
 469                wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev,
 470                                           RFKILL_TYPE_WLAN,
 471                                           &dell_rfkill_ops, (void *) 1);
 472                if (!wifi_rfkill) {
 473                        ret = -ENOMEM;
 474                        goto err_wifi;
 475                }
 476                ret = rfkill_register(wifi_rfkill);
 477                if (ret)
 478                        goto err_wifi;
 479        }
 480
 481        if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) {
 482                bluetooth_rfkill = rfkill_alloc("dell-bluetooth",
 483                                                &platform_device->dev,
 484                                                RFKILL_TYPE_BLUETOOTH,
 485                                                &dell_rfkill_ops, (void *) 2);
 486                if (!bluetooth_rfkill) {
 487                        ret = -ENOMEM;
 488                        goto err_bluetooth;
 489                }
 490                ret = rfkill_register(bluetooth_rfkill);
 491                if (ret)
 492                        goto err_bluetooth;
 493        }
 494
 495        if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) {
 496                wwan_rfkill = rfkill_alloc("dell-wwan",
 497                                           &platform_device->dev,
 498                                           RFKILL_TYPE_WWAN,
 499                                           &dell_rfkill_ops, (void *) 3);
 500                if (!wwan_rfkill) {
 501                        ret = -ENOMEM;
 502                        goto err_wwan;
 503                }
 504                ret = rfkill_register(wwan_rfkill);
 505                if (ret)
 506                        goto err_wwan;
 507        }
 508
 509        return 0;
 510err_wwan:
 511        rfkill_destroy(wwan_rfkill);
 512        if (bluetooth_rfkill)
 513                rfkill_unregister(bluetooth_rfkill);
 514err_bluetooth:
 515        rfkill_destroy(bluetooth_rfkill);
 516        if (wifi_rfkill)
 517                rfkill_unregister(wifi_rfkill);
 518err_wifi:
 519        rfkill_destroy(wifi_rfkill);
 520
 521        return ret;
 522}
 523
 524static void dell_cleanup_rfkill(void)
 525{
 526        if (wifi_rfkill) {
 527                rfkill_unregister(wifi_rfkill);
 528                rfkill_destroy(wifi_rfkill);
 529        }
 530        if (bluetooth_rfkill) {
 531                rfkill_unregister(bluetooth_rfkill);
 532                rfkill_destroy(bluetooth_rfkill);
 533        }
 534        if (wwan_rfkill) {
 535                rfkill_unregister(wwan_rfkill);
 536                rfkill_destroy(wwan_rfkill);
 537        }
 538}
 539
 540static int dell_send_intensity(struct backlight_device *bd)
 541{
 542        int ret = 0;
 543
 544        get_buffer();
 545        buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
 546        buffer->input[1] = bd->props.brightness;
 547
 548        if (buffer->input[0] == -1) {
 549                ret = -ENODEV;
 550                goto out;
 551        }
 552
 553        if (power_supply_is_system_supplied() > 0)
 554                dell_send_request(buffer, 1, 2);
 555        else
 556                dell_send_request(buffer, 1, 1);
 557
 558out:
 559        release_buffer();
 560        return 0;
 561}
 562
 563static int dell_get_intensity(struct backlight_device *bd)
 564{
 565        int ret = 0;
 566
 567        get_buffer();
 568        buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
 569
 570        if (buffer->input[0] == -1) {
 571                ret = -ENODEV;
 572                goto out;
 573        }
 574
 575        if (power_supply_is_system_supplied() > 0)
 576                dell_send_request(buffer, 0, 2);
 577        else
 578                dell_send_request(buffer, 0, 1);
 579
 580        ret = buffer->output[1];
 581
 582out:
 583        release_buffer();
 584        return ret;
 585}
 586
 587static const struct backlight_ops dell_ops = {
 588        .get_brightness = dell_get_intensity,
 589        .update_status  = dell_send_intensity,
 590};
 591
 592static void touchpad_led_on(void)
 593{
 594        int command = 0x97;
 595        char data = 1;
 596        i8042_command(&data, command | 1 << 12);
 597}
 598
 599static void touchpad_led_off(void)
 600{
 601        int command = 0x97;
 602        char data = 2;
 603        i8042_command(&data, command | 1 << 12);
 604}
 605
 606static void touchpad_led_set(struct led_classdev *led_cdev,
 607        enum led_brightness value)
 608{
 609        if (value > 0)
 610                touchpad_led_on();
 611        else
 612                touchpad_led_off();
 613}
 614
 615static struct led_classdev touchpad_led = {
 616        .name = "dell-laptop::touchpad",
 617        .brightness_set = touchpad_led_set,
 618};
 619
 620static int __devinit touchpad_led_init(struct device *dev)
 621{
 622        return led_classdev_register(dev, &touchpad_led);
 623}
 624
 625static void touchpad_led_exit(void)
 626{
 627        led_classdev_unregister(&touchpad_led);
 628}
 629
 630static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
 631                              struct serio *port)
 632{
 633        static bool extended;
 634
 635        if (str & 0x20)
 636                return false;
 637
 638        if (unlikely(data == 0xe0)) {
 639                extended = true;
 640                return false;
 641        } else if (unlikely(extended)) {
 642                switch (data) {
 643                case 0x8:
 644                        schedule_delayed_work(&dell_rfkill_work,
 645                                              round_jiffies_relative(HZ));
 646                        break;
 647                }
 648                extended = false;
 649        }
 650
 651        return false;
 652}
 653
 654static int __init dell_init(void)
 655{
 656        int max_intensity = 0;
 657        int ret;
 658
 659        if (!dmi_check_system(dell_device_table))
 660                return -ENODEV;
 661
 662        quirks = NULL;
 663        /* find if this machine support other functions */
 664        dmi_check_system(dell_quirks);
 665
 666        dmi_walk(find_tokens, NULL);
 667
 668        if (!da_tokens)  {
 669                pr_info("Unable to find dmi tokens\n");
 670                return -ENODEV;
 671        }
 672
 673        ret = platform_driver_register(&platform_driver);
 674        if (ret)
 675                goto fail_platform_driver;
 676        platform_device = platform_device_alloc("dell-laptop", -1);
 677        if (!platform_device) {
 678                ret = -ENOMEM;
 679                goto fail_platform_device1;
 680        }
 681        ret = platform_device_add(platform_device);
 682        if (ret)
 683                goto fail_platform_device2;
 684
 685        /*
 686         * Allocate buffer below 4GB for SMI data--only 32-bit physical addr
 687         * is passed to SMI handler.
 688         */
 689        bufferpage = alloc_page(GFP_KERNEL | GFP_DMA32);
 690
 691        if (!bufferpage)
 692                goto fail_buffer;
 693        buffer = page_address(bufferpage);
 694
 695        ret = dell_setup_rfkill();
 696
 697        if (ret) {
 698                pr_warn("Unable to setup rfkill\n");
 699                goto fail_rfkill;
 700        }
 701
 702        ret = i8042_install_filter(dell_laptop_i8042_filter);
 703        if (ret) {
 704                pr_warn("Unable to install key filter\n");
 705                goto fail_filter;
 706        }
 707
 708        if (quirks && quirks->touchpad_led)
 709                touchpad_led_init(&platform_device->dev);
 710
 711        dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
 712        if (dell_laptop_dir != NULL)
 713                debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
 714                                    &dell_debugfs_fops);
 715
 716#ifdef CONFIG_ACPI
 717        /* In the event of an ACPI backlight being available, don't
 718         * register the platform controller.
 719         */
 720        if (acpi_video_backlight_support())
 721                return 0;
 722#endif
 723
 724        get_buffer();
 725        buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
 726        if (buffer->input[0] != -1) {
 727                dell_send_request(buffer, 0, 2);
 728                max_intensity = buffer->output[3];
 729        }
 730        release_buffer();
 731
 732        if (max_intensity) {
 733                struct backlight_properties props;
 734                memset(&props, 0, sizeof(struct backlight_properties));
 735                props.type = BACKLIGHT_PLATFORM;
 736                props.max_brightness = max_intensity;
 737                dell_backlight_device = backlight_device_register("dell_backlight",
 738                                                                  &platform_device->dev,
 739                                                                  NULL,
 740                                                                  &dell_ops,
 741                                                                  &props);
 742
 743                if (IS_ERR(dell_backlight_device)) {
 744                        ret = PTR_ERR(dell_backlight_device);
 745                        dell_backlight_device = NULL;
 746                        goto fail_backlight;
 747                }
 748
 749                dell_backlight_device->props.brightness =
 750                        dell_get_intensity(dell_backlight_device);
 751                backlight_update_status(dell_backlight_device);
 752        }
 753
 754        return 0;
 755
 756fail_backlight:
 757        i8042_remove_filter(dell_laptop_i8042_filter);
 758        cancel_delayed_work_sync(&dell_rfkill_work);
 759fail_filter:
 760        dell_cleanup_rfkill();
 761fail_rfkill:
 762        free_page((unsigned long)bufferpage);
 763fail_buffer:
 764        platform_device_del(platform_device);
 765fail_platform_device2:
 766        platform_device_put(platform_device);
 767fail_platform_device1:
 768        platform_driver_unregister(&platform_driver);
 769fail_platform_driver:
 770        kfree(da_tokens);
 771        return ret;
 772}
 773
 774static void __exit dell_exit(void)
 775{
 776        debugfs_remove_recursive(dell_laptop_dir);
 777        if (quirks && quirks->touchpad_led)
 778                touchpad_led_exit();
 779        i8042_remove_filter(dell_laptop_i8042_filter);
 780        cancel_delayed_work_sync(&dell_rfkill_work);
 781        backlight_device_unregister(dell_backlight_device);
 782        dell_cleanup_rfkill();
 783        if (platform_device) {
 784                platform_device_unregister(platform_device);
 785                platform_driver_unregister(&platform_driver);
 786        }
 787        kfree(da_tokens);
 788        free_page((unsigned long)buffer);
 789}
 790
 791module_init(dell_init);
 792module_exit(dell_exit);
 793
 794MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
 795MODULE_DESCRIPTION("Dell laptop driver");
 796MODULE_LICENSE("GPL");
 797MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*");
 798MODULE_ALIAS("dmi:*svnDellInc.:*:ct9:*");
 799MODULE_ALIAS("dmi:*svnDellComputerCorporation.:*:ct8:*");
 800
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.