dell-laptop.c 19 KB

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