fujitsu-laptop.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. /*-*-linux-c-*-*/
  2. /*
  3. Copyright (C) 2007 Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
  4. Based on earlier work:
  5. Copyright (C) 2003 Shane Spencer <shane@bogomip.com>
  6. Adrian Yee <brewt-fujitsu@brewt.org>
  7. Templated from msi-laptop.c which is copyright by its respective authors.
  8. This program is free software; you can redistribute it and/or modify
  9. it under the terms of the GNU General Public License as published by
  10. the Free Software Foundation; either version 2 of the License, or
  11. (at your option) any later version.
  12. This program is distributed in the hope that it will be useful, but
  13. WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. General Public License for more details.
  16. You should have received a copy of the GNU General Public License
  17. along with this program; if not, write to the Free Software
  18. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19. 02110-1301, USA.
  20. */
  21. /*
  22. * fujitsu-laptop.c - Fujitsu laptop support, providing access to additional
  23. * features made available on a range of Fujitsu laptops including the
  24. * P2xxx/P5xxx/S6xxx/S7xxx series.
  25. *
  26. * This driver exports a few files in /sys/devices/platform/fujitsu-laptop/;
  27. * others may be added at a later date.
  28. *
  29. * lcd_level - Screen brightness: contains a single integer in the
  30. * range 0..7. (rw)
  31. *
  32. * In addition to these platform device attributes the driver
  33. * registers itself in the Linux backlight control subsystem and is
  34. * available to userspace under /sys/class/backlight/fujitsu-laptop/.
  35. *
  36. * This driver has been tested on a Fujitsu Lifebook S7020. It should
  37. * work on most P-series and S-series Lifebooks, but YMMV.
  38. */
  39. #include <linux/module.h>
  40. #include <linux/kernel.h>
  41. #include <linux/init.h>
  42. #include <linux/acpi.h>
  43. #include <linux/dmi.h>
  44. #include <linux/backlight.h>
  45. #include <linux/platform_device.h>
  46. #include <linux/autoconf.h>
  47. #define FUJITSU_DRIVER_VERSION "0.3"
  48. #define FUJITSU_LCD_N_LEVELS 8
  49. #define ACPI_FUJITSU_CLASS "fujitsu"
  50. #define ACPI_FUJITSU_HID "FUJ02B1"
  51. #define ACPI_FUJITSU_DRIVER_NAME "Fujitsu laptop FUJ02B1 ACPI extras driver"
  52. #define ACPI_FUJITSU_DEVICE_NAME "Fujitsu FUJ02B1"
  53. struct fujitsu_t {
  54. acpi_handle acpi_handle;
  55. struct backlight_device *bl_device;
  56. struct platform_device *pf_device;
  57. unsigned long fuj02b1_state;
  58. unsigned int brightness_changed;
  59. unsigned int brightness_level;
  60. };
  61. static struct fujitsu_t *fujitsu;
  62. /* Hardware access */
  63. static int set_lcd_level(int level)
  64. {
  65. acpi_status status = AE_OK;
  66. union acpi_object arg0 = { ACPI_TYPE_INTEGER };
  67. struct acpi_object_list arg_list = { 1, &arg0 };
  68. acpi_handle handle = NULL;
  69. if (level < 0 || level >= FUJITSU_LCD_N_LEVELS)
  70. return -EINVAL;
  71. if (!fujitsu)
  72. return -EINVAL;
  73. status = acpi_get_handle(fujitsu->acpi_handle, "SBLL", &handle);
  74. if (ACPI_FAILURE(status)) {
  75. ACPI_DEBUG_PRINT((ACPI_DB_INFO, "SBLL not present\n"));
  76. return -ENODEV;
  77. }
  78. arg0.integer.value = level;
  79. status = acpi_evaluate_object(handle, NULL, &arg_list, NULL);
  80. if (ACPI_FAILURE(status))
  81. return -ENODEV;
  82. return 0;
  83. }
  84. static int get_lcd_level(void)
  85. {
  86. unsigned long state = 0;
  87. acpi_status status = AE_OK;
  88. // Get the Brightness
  89. status =
  90. acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, &state);
  91. if (status < 0)
  92. return status;
  93. fujitsu->fuj02b1_state = state;
  94. fujitsu->brightness_level = state & 0x0fffffff;
  95. if (state & 0x80000000)
  96. fujitsu->brightness_changed = 1;
  97. else
  98. fujitsu->brightness_changed = 0;
  99. if (status < 0)
  100. return status;
  101. return fujitsu->brightness_level;
  102. }
  103. /* Backlight device stuff */
  104. static int bl_get_brightness(struct backlight_device *b)
  105. {
  106. return get_lcd_level();
  107. }
  108. static int bl_update_status(struct backlight_device *b)
  109. {
  110. return set_lcd_level(b->props.brightness);
  111. }
  112. static struct backlight_ops fujitsubl_ops = {
  113. .get_brightness = bl_get_brightness,
  114. .update_status = bl_update_status,
  115. };
  116. /* Platform device */
  117. static ssize_t show_lcd_level(struct device *dev,
  118. struct device_attribute *attr, char *buf)
  119. {
  120. int ret;
  121. ret = get_lcd_level();
  122. if (ret < 0)
  123. return ret;
  124. return sprintf(buf, "%i\n", ret);
  125. }
  126. static ssize_t store_lcd_level(struct device *dev,
  127. struct device_attribute *attr, const char *buf,
  128. size_t count)
  129. {
  130. int level, ret;
  131. if (sscanf(buf, "%i", &level) != 1
  132. || (level < 0 || level >= FUJITSU_LCD_N_LEVELS))
  133. return -EINVAL;
  134. ret = set_lcd_level(level);
  135. if (ret < 0)
  136. return ret;
  137. return count;
  138. }
  139. static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
  140. static struct attribute *fujitsupf_attributes[] = {
  141. &dev_attr_lcd_level.attr,
  142. NULL
  143. };
  144. static struct attribute_group fujitsupf_attribute_group = {
  145. .attrs = fujitsupf_attributes
  146. };
  147. static struct platform_driver fujitsupf_driver = {
  148. .driver = {
  149. .name = "fujitsu-laptop",
  150. .owner = THIS_MODULE,
  151. }
  152. };
  153. /* ACPI device */
  154. int acpi_fujitsu_add(struct acpi_device *device)
  155. {
  156. int result = 0;
  157. int state = 0;
  158. ACPI_FUNCTION_TRACE("acpi_fujitsu_add");
  159. if (!device)
  160. return -EINVAL;
  161. fujitsu->acpi_handle = device->handle;
  162. sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_DEVICE_NAME);
  163. sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
  164. acpi_driver_data(device) = fujitsu;
  165. result = acpi_bus_get_power(fujitsu->acpi_handle, &state);
  166. if (result) {
  167. ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
  168. "Error reading power state\n"));
  169. goto end;
  170. }
  171. printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
  172. acpi_device_name(device), acpi_device_bid(device),
  173. !device->power.state ? "on" : "off");
  174. end:
  175. return result;
  176. }
  177. int acpi_fujitsu_remove(struct acpi_device *device, int type)
  178. {
  179. ACPI_FUNCTION_TRACE("acpi_fujitsu_remove");
  180. if (!device || !acpi_driver_data(device))
  181. return -EINVAL;
  182. fujitsu->acpi_handle = 0;
  183. return 0;
  184. }
  185. static const struct acpi_device_id fujitsu_device_ids[] = {
  186. {ACPI_FUJITSU_HID, 0},
  187. {"", 0},
  188. };
  189. static struct acpi_driver acpi_fujitsu_driver = {
  190. .name = ACPI_FUJITSU_DRIVER_NAME,
  191. .class = ACPI_FUJITSU_CLASS,
  192. .ids = fujitsu_device_ids,
  193. .ops = {
  194. .add = acpi_fujitsu_add,
  195. .remove = acpi_fujitsu_remove,
  196. },
  197. };
  198. /* Initialization */
  199. static int __init fujitsu_init(void)
  200. {
  201. int ret, result;
  202. if (acpi_disabled)
  203. return -ENODEV;
  204. fujitsu = kmalloc(sizeof(struct fujitsu_t), GFP_KERNEL);
  205. if (!fujitsu)
  206. return -ENOMEM;
  207. memset(fujitsu, 0, sizeof(struct fujitsu_t));
  208. result = acpi_bus_register_driver(&acpi_fujitsu_driver);
  209. if (result < 0) {
  210. ret = -ENODEV;
  211. goto fail_acpi;
  212. }
  213. /* Register backlight stuff */
  214. fujitsu->bl_device =
  215. backlight_device_register("fujitsu-laptop", NULL, NULL,
  216. &fujitsubl_ops);
  217. if (IS_ERR(fujitsu->bl_device))
  218. return PTR_ERR(fujitsu->bl_device);
  219. fujitsu->bl_device->props.max_brightness = FUJITSU_LCD_N_LEVELS - 1;
  220. ret = platform_driver_register(&fujitsupf_driver);
  221. if (ret)
  222. goto fail_backlight;
  223. /* Register platform stuff */
  224. fujitsu->pf_device = platform_device_alloc("fujitsu-laptop", -1);
  225. if (!fujitsu->pf_device) {
  226. ret = -ENOMEM;
  227. goto fail_platform_driver;
  228. }
  229. ret = platform_device_add(fujitsu->pf_device);
  230. if (ret)
  231. goto fail_platform_device1;
  232. ret =
  233. sysfs_create_group(&fujitsu->pf_device->dev.kobj,
  234. &fujitsupf_attribute_group);
  235. if (ret)
  236. goto fail_platform_device2;
  237. printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION
  238. " successfully loaded.\n");
  239. return 0;
  240. fail_platform_device2:
  241. platform_device_del(fujitsu->pf_device);
  242. fail_platform_device1:
  243. platform_device_put(fujitsu->pf_device);
  244. fail_platform_driver:
  245. platform_driver_unregister(&fujitsupf_driver);
  246. fail_backlight:
  247. backlight_device_unregister(fujitsu->bl_device);
  248. fail_acpi:
  249. kfree(fujitsu);
  250. return ret;
  251. }
  252. static void __exit fujitsu_cleanup(void)
  253. {
  254. sysfs_remove_group(&fujitsu->pf_device->dev.kobj,
  255. &fujitsupf_attribute_group);
  256. platform_device_unregister(fujitsu->pf_device);
  257. platform_driver_unregister(&fujitsupf_driver);
  258. backlight_device_unregister(fujitsu->bl_device);
  259. acpi_bus_unregister_driver(&acpi_fujitsu_driver);
  260. kfree(fujitsu);
  261. printk(KERN_INFO "fujitsu-laptop: driver unloaded.\n");
  262. }
  263. module_init(fujitsu_init);
  264. module_exit(fujitsu_cleanup);
  265. MODULE_AUTHOR("Jonathan Woithe");
  266. MODULE_DESCRIPTION("Fujitsu laptop extras support");
  267. MODULE_VERSION(FUJITSU_DRIVER_VERSION);
  268. MODULE_LICENSE("GPL");