compal-laptop.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. /*-*-linux-c-*-*/
  2. /*
  3. Copyright (C) 2008 Cezary Jackiewicz <cezary.jackiewicz (at) gmail.com>
  4. based on MSI driver
  5. Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
  6. This program is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful, but
  11. WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program; if not, write to the Free Software
  16. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  17. 02110-1301, USA.
  18. */
  19. /*
  20. * comapl-laptop.c - Compal laptop support.
  21. *
  22. * The driver registers itself with the rfkill subsystem and
  23. * the Linux backlight control subsystem.
  24. *
  25. * This driver might work on other laptops produced by Compal. If you
  26. * want to try it you can pass force=1 as argument to the module which
  27. * will force it to load even when the DMI data doesn't identify the
  28. * laptop as FL9x.
  29. */
  30. #include <linux/module.h>
  31. #include <linux/kernel.h>
  32. #include <linux/init.h>
  33. #include <linux/acpi.h>
  34. #include <linux/dmi.h>
  35. #include <linux/backlight.h>
  36. #include <linux/platform_device.h>
  37. #include <linux/rfkill.h>
  38. #define COMPAL_DRIVER_VERSION "0.2.6"
  39. #define COMPAL_LCD_LEVEL_MAX 8
  40. #define COMPAL_EC_COMMAND_WIRELESS 0xBB
  41. #define COMPAL_EC_COMMAND_LCD_LEVEL 0xB9
  42. #define KILLSWITCH_MASK 0x10
  43. #define WLAN_MASK 0x01
  44. #define BT_MASK 0x02
  45. static struct rfkill *wifi_rfkill;
  46. static struct rfkill *bt_rfkill;
  47. static struct platform_device *compal_device;
  48. static int force;
  49. module_param(force, bool, 0);
  50. MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
  51. /* Hardware access */
  52. static int set_lcd_level(int level)
  53. {
  54. if (level < 0 || level >= COMPAL_LCD_LEVEL_MAX)
  55. return -EINVAL;
  56. ec_write(COMPAL_EC_COMMAND_LCD_LEVEL, level);
  57. return 0;
  58. }
  59. static int get_lcd_level(void)
  60. {
  61. u8 result;
  62. ec_read(COMPAL_EC_COMMAND_LCD_LEVEL, &result);
  63. return (int) result;
  64. }
  65. static int compal_rfkill_set(void *data, bool blocked)
  66. {
  67. unsigned long radio = (unsigned long) data;
  68. u8 result, value;
  69. ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
  70. if (!blocked)
  71. value = (u8) (result | radio);
  72. else
  73. value = (u8) (result & ~radio);
  74. ec_write(COMPAL_EC_COMMAND_WIRELESS, value);
  75. return 0;
  76. }
  77. static void compal_rfkill_poll(struct rfkill *rfkill, void *data)
  78. {
  79. u8 result;
  80. bool hw_blocked;
  81. ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
  82. hw_blocked = !(result & KILLSWITCH_MASK);
  83. rfkill_set_hw_state(rfkill, hw_blocked);
  84. }
  85. static const struct rfkill_ops compal_rfkill_ops = {
  86. .poll = compal_rfkill_poll,
  87. .set_block = compal_rfkill_set,
  88. };
  89. static int setup_rfkill(void)
  90. {
  91. int ret;
  92. wifi_rfkill = rfkill_alloc("compal-wifi", &compal_device->dev,
  93. RFKILL_TYPE_WLAN, &compal_rfkill_ops,
  94. (void *) WLAN_MASK);
  95. if (!wifi_rfkill)
  96. return -ENOMEM;
  97. ret = rfkill_register(wifi_rfkill);
  98. if (ret)
  99. goto err_wifi;
  100. bt_rfkill = rfkill_alloc("compal-bluetooth", &compal_device->dev,
  101. RFKILL_TYPE_BLUETOOTH, &compal_rfkill_ops,
  102. (void *) BT_MASK);
  103. if (!bt_rfkill) {
  104. ret = -ENOMEM;
  105. goto err_allocate_bt;
  106. }
  107. ret = rfkill_register(bt_rfkill);
  108. if (ret)
  109. goto err_register_bt;
  110. return 0;
  111. err_register_bt:
  112. rfkill_destroy(bt_rfkill);
  113. err_allocate_bt:
  114. rfkill_unregister(wifi_rfkill);
  115. err_wifi:
  116. rfkill_destroy(wifi_rfkill);
  117. return ret;
  118. }
  119. /* Backlight device stuff */
  120. static int bl_get_brightness(struct backlight_device *b)
  121. {
  122. return get_lcd_level();
  123. }
  124. static int bl_update_status(struct backlight_device *b)
  125. {
  126. return set_lcd_level(b->props.brightness);
  127. }
  128. static struct backlight_ops compalbl_ops = {
  129. .get_brightness = bl_get_brightness,
  130. .update_status = bl_update_status,
  131. };
  132. static struct backlight_device *compalbl_device;
  133. static struct platform_driver compal_driver = {
  134. .driver = {
  135. .name = "compal-laptop",
  136. .owner = THIS_MODULE,
  137. }
  138. };
  139. /* Initialization */
  140. static int dmi_check_cb(const struct dmi_system_id *id)
  141. {
  142. printk(KERN_INFO "compal-laptop: Identified laptop model '%s'.\n",
  143. id->ident);
  144. return 0;
  145. }
  146. static struct dmi_system_id __initdata compal_dmi_table[] = {
  147. {
  148. .ident = "FL90/IFL90",
  149. .matches = {
  150. DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
  151. DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
  152. },
  153. .callback = dmi_check_cb
  154. },
  155. {
  156. .ident = "FL90/IFL90",
  157. .matches = {
  158. DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
  159. DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
  160. },
  161. .callback = dmi_check_cb
  162. },
  163. {
  164. .ident = "FL91/IFL91",
  165. .matches = {
  166. DMI_MATCH(DMI_BOARD_NAME, "IFL91"),
  167. DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
  168. },
  169. .callback = dmi_check_cb
  170. },
  171. {
  172. .ident = "FL92/JFL92",
  173. .matches = {
  174. DMI_MATCH(DMI_BOARD_NAME, "JFL92"),
  175. DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
  176. },
  177. .callback = dmi_check_cb
  178. },
  179. {
  180. .ident = "FT00/IFT00",
  181. .matches = {
  182. DMI_MATCH(DMI_BOARD_NAME, "IFT00"),
  183. DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
  184. },
  185. .callback = dmi_check_cb
  186. },
  187. {
  188. .ident = "Dell Mini 9",
  189. .matches = {
  190. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  191. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"),
  192. },
  193. .callback = dmi_check_cb
  194. },
  195. {
  196. .ident = "Dell Mini 10",
  197. .matches = {
  198. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  199. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"),
  200. },
  201. .callback = dmi_check_cb
  202. },
  203. {
  204. .ident = "Dell Mini 10v",
  205. .matches = {
  206. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  207. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"),
  208. },
  209. .callback = dmi_check_cb
  210. },
  211. {
  212. .ident = "Dell Inspiron 11z",
  213. .matches = {
  214. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  215. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"),
  216. },
  217. .callback = dmi_check_cb
  218. },
  219. {
  220. .ident = "Dell Mini 12",
  221. .matches = {
  222. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  223. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"),
  224. },
  225. .callback = dmi_check_cb
  226. },
  227. { }
  228. };
  229. static int __init compal_init(void)
  230. {
  231. int ret;
  232. if (acpi_disabled)
  233. return -ENODEV;
  234. if (!force && !dmi_check_system(compal_dmi_table))
  235. return -ENODEV;
  236. /* Register backlight stuff */
  237. if (!acpi_video_backlight_support()) {
  238. compalbl_device = backlight_device_register("compal-laptop", NULL, NULL,
  239. &compalbl_ops);
  240. if (IS_ERR(compalbl_device))
  241. return PTR_ERR(compalbl_device);
  242. compalbl_device->props.max_brightness = COMPAL_LCD_LEVEL_MAX-1;
  243. }
  244. ret = platform_driver_register(&compal_driver);
  245. if (ret)
  246. goto fail_backlight;
  247. /* Register platform stuff */
  248. compal_device = platform_device_alloc("compal-laptop", -1);
  249. if (!compal_device) {
  250. ret = -ENOMEM;
  251. goto fail_platform_driver;
  252. }
  253. ret = platform_device_add(compal_device);
  254. if (ret)
  255. goto fail_platform_device;
  256. ret = setup_rfkill();
  257. if (ret)
  258. goto fail_rfkill;
  259. printk(KERN_INFO "compal-laptop: driver "COMPAL_DRIVER_VERSION
  260. " successfully loaded.\n");
  261. return 0;
  262. fail_rfkill:
  263. platform_device_del(compal_device);
  264. fail_platform_device:
  265. platform_device_put(compal_device);
  266. fail_platform_driver:
  267. platform_driver_unregister(&compal_driver);
  268. fail_backlight:
  269. backlight_device_unregister(compalbl_device);
  270. return ret;
  271. }
  272. static void __exit compal_cleanup(void)
  273. {
  274. platform_device_unregister(compal_device);
  275. platform_driver_unregister(&compal_driver);
  276. backlight_device_unregister(compalbl_device);
  277. rfkill_unregister(wifi_rfkill);
  278. rfkill_destroy(wifi_rfkill);
  279. rfkill_unregister(bt_rfkill);
  280. rfkill_destroy(bt_rfkill);
  281. printk(KERN_INFO "compal-laptop: driver unloaded.\n");
  282. }
  283. module_init(compal_init);
  284. module_exit(compal_cleanup);
  285. MODULE_AUTHOR("Cezary Jackiewicz");
  286. MODULE_DESCRIPTION("Compal Laptop Support");
  287. MODULE_VERSION(COMPAL_DRIVER_VERSION);
  288. MODULE_LICENSE("GPL");
  289. MODULE_ALIAS("dmi:*:rnIFL90:rvrIFT00:*");
  290. MODULE_ALIAS("dmi:*:rnIFL90:rvrREFERENCE:*");
  291. MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*");
  292. MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*");
  293. MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*");
  294. MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron910:*");
  295. MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1010:*");
  296. MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1011:*");
  297. MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1110:*");
  298. MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1210:*");