Merge branch 'for-linus' of git://git.kernel.dk/linux-block
[pandora-kernel.git] / drivers / platform / x86 / dell-laptop.c
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
39 struct calling_interface_buffer {
40         u16 class;
41         u16 select;
42         volatile u32 input[4];
43         volatile u32 output[4];
44 } __packed;
45
46 struct calling_interface_token {
47         u16 tokenID;
48         u16 location;
49         union {
50                 u16 value;
51                 u16 stringlength;
52         };
53 };
54
55 struct 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
63 struct quirk_entry {
64         u8 touchpad_led;
65 };
66
67 static struct quirk_entry *quirks;
68
69 static struct quirk_entry quirk_dell_vostro_v130 = {
70         .touchpad_led = 1,
71 };
72
73 static int dmi_matched(const struct dmi_system_id *dmi)
74 {
75         quirks = dmi->driver_data;
76         return 1;
77 }
78
79 static int da_command_address;
80 static int da_command_code;
81 static int da_num_tokens;
82 static struct calling_interface_token *da_tokens;
83
84 static struct platform_driver platform_driver = {
85         .driver = {
86                 .name = "dell-laptop",
87                 .owner = THIS_MODULE,
88         }
89 };
90
91 static struct platform_device *platform_device;
92 static struct backlight_device *dell_backlight_device;
93 static struct rfkill *wifi_rfkill;
94 static struct rfkill *bluetooth_rfkill;
95 static struct rfkill *wwan_rfkill;
96
97 static 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
121 static 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
168 static 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
189 static struct calling_interface_buffer *buffer;
190 static struct page *bufferpage;
191 static DEFINE_MUTEX(buffer_mutex);
192
193 static int hwswitch_state;
194
195 static void get_buffer(void)
196 {
197         mutex_lock(&buffer_mutex);
198         memset(buffer, 0, sizeof(struct calling_interface_buffer));
199 }
200
201 static void release_buffer(void)
202 {
203         mutex_unlock(&buffer_mutex);
204 }
205
206 static 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
235 static 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
250 static 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
261 static struct calling_interface_buffer *
262 dell_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
321 static 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
342 out:
343         release_buffer();
344         return ret;
345 }
346
347 static 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
364 static const struct rfkill_ops dell_rfkill_ops = {
365         .set_block = dell_rfkill_set,
366         .query = dell_rfkill_query,
367 };
368
369 static struct dentry *dell_laptop_dir;
370
371 static 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
425 static int dell_debugfs_open(struct inode *inode, struct file *file)
426 {
427         return single_open(file, dell_debugfs_show, inode->i_private);
428 }
429
430 static 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
438 static 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 }
447 static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
448
449
450 static 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;
510 err_wwan:
511         rfkill_destroy(wwan_rfkill);
512         if (bluetooth_rfkill)
513                 rfkill_unregister(bluetooth_rfkill);
514 err_bluetooth:
515         rfkill_destroy(bluetooth_rfkill);
516         if (wifi_rfkill)
517                 rfkill_unregister(wifi_rfkill);
518 err_wifi:
519         rfkill_destroy(wifi_rfkill);
520
521         return ret;
522 }
523
524 static 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
540 static 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
558 out:
559         release_buffer();
560         return 0;
561 }
562
563 static 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
582 out:
583         release_buffer();
584         return ret;
585 }
586
587 static const struct backlight_ops dell_ops = {
588         .get_brightness = dell_get_intensity,
589         .update_status  = dell_send_intensity,
590 };
591
592 static void touchpad_led_on(void)
593 {
594         int command = 0x97;
595         char data = 1;
596         i8042_command(&data, command | 1 << 12);
597 }
598
599 static void touchpad_led_off(void)
600 {
601         int command = 0x97;
602         char data = 2;
603         i8042_command(&data, command | 1 << 12);
604 }
605
606 static 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
615 static struct led_classdev touchpad_led = {
616         .name = "dell-laptop::touchpad",
617         .brightness_set = touchpad_led_set,
618 };
619
620 static int __devinit touchpad_led_init(struct device *dev)
621 {
622         return led_classdev_register(dev, &touchpad_led);
623 }
624
625 static void touchpad_led_exit(void)
626 {
627         led_classdev_unregister(&touchpad_led);
628 }
629
630 static 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
654 static 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
756 fail_backlight:
757         i8042_remove_filter(dell_laptop_i8042_filter);
758         cancel_delayed_work_sync(&dell_rfkill_work);
759 fail_filter:
760         dell_cleanup_rfkill();
761 fail_rfkill:
762         free_page((unsigned long)bufferpage);
763 fail_buffer:
764         platform_device_del(platform_device);
765 fail_platform_device2:
766         platform_device_put(platform_device);
767 fail_platform_device1:
768         platform_driver_unregister(&platform_driver);
769 fail_platform_driver:
770         kfree(da_tokens);
771         return ret;
772 }
773
774 static 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
791 module_init(dell_init);
792 module_exit(dell_exit);
793
794 MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
795 MODULE_DESCRIPTION("Dell laptop driver");
796 MODULE_LICENSE("GPL");
797 MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*");
798 MODULE_ALIAS("dmi:*svnDellInc.:*:ct9:*");
799 MODULE_ALIAS("dmi:*svnDellComputerCorporation.:*:ct8:*");