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