fujitsu-laptop.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  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. return fujitsu->brightness_level;
  100. }
  101. /* Backlight device stuff */
  102. static int bl_get_brightness(struct backlight_device *b)
  103. {
  104. return get_lcd_level();
  105. }
  106. static int bl_update_status(struct backlight_device *b)
  107. {
  108. return set_lcd_level(b->props.brightness);
  109. }
  110. static struct backlight_ops fujitsubl_ops = {
  111. .get_brightness = bl_get_brightness,
  112. .update_status = bl_update_status,
  113. };
  114. /* Platform device */
  115. static ssize_t show_lcd_level(struct device *dev,
  116. struct device_attribute *attr, char *buf)
  117. {
  118. int ret;
  119. ret = get_lcd_level();
  120. if (ret < 0)
  121. return ret;
  122. return sprintf(buf, "%i\n", ret);
  123. }
  124. static ssize_t store_lcd_level(struct device *dev,
  125. struct device_attribute *attr, const char *buf,
  126. size_t count)
  127. {
  128. int level, ret;
  129. if (sscanf(buf, "%i", &level) != 1
  130. || (level < 0 || level >= FUJITSU_LCD_N_LEVELS))
  131. return -EINVAL;
  132. ret = set_lcd_level(level);
  133. if (ret < 0)
  134. return ret;
  135. return count;
  136. }
  137. static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
  138. static struct attribute *fujitsupf_attributes[] = {
  139. &dev_attr_lcd_level.attr,
  140. NULL
  141. };
  142. static struct attribute_group fujitsupf_attribute_group = {
  143. .attrs = fujitsupf_attributes
  144. };
  145. static struct platform_driver fujitsupf_driver = {
  146. .driver = {
  147. .name = "fujitsu-laptop",
  148. .owner = THIS_MODULE,
  149. }
  150. };
  151. /* ACPI device */
  152. static int acpi_fujitsu_add(struct acpi_device *device)
  153. {
  154. int result = 0;
  155. int state = 0;
  156. ACPI_FUNCTION_TRACE("acpi_fujitsu_add");
  157. if (!device)
  158. return -EINVAL;
  159. fujitsu->acpi_handle = device->handle;
  160. sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_DEVICE_NAME);
  161. sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
  162. acpi_driver_data(device) = fujitsu;
  163. result = acpi_bus_get_power(fujitsu->acpi_handle, &state);
  164. if (result) {
  165. ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
  166. "Error reading power state\n"));
  167. goto end;
  168. }
  169. printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
  170. acpi_device_name(device), acpi_device_bid(device),
  171. !device->power.state ? "on" : "off");
  172. end:
  173. return result;
  174. }
  175. static int acpi_fujitsu_remove(struct acpi_device *device, int type)
  176. {
  177. ACPI_FUNCTION_TRACE("acpi_fujitsu_remove");
  178. if (!device || !acpi_driver_data(device))
  179. return -EINVAL;
  180. fujitsu->acpi_handle = 0;
  181. return 0;
  182. }
  183. static const struct acpi_device_id fujitsu_device_ids[] = {
  184. {ACPI_FUJITSU_HID, 0},
  185. {"", 0},
  186. };
  187. static struct acpi_driver acpi_fujitsu_driver = {
  188. .name = ACPI_FUJITSU_DRIVER_NAME,
  189. .class = ACPI_FUJITSU_CLASS,
  190. .ids = fujitsu_device_ids,
  191. .ops = {
  192. .add = acpi_fujitsu_add,
  193. .remove = acpi_fujitsu_remove,
  194. },
  195. };
  196. /* Initialization */
  197. static int __init fujitsu_init(void)
  198. {
  199. int ret, result;
  200. if (acpi_disabled)
  201. return -ENODEV;
  202. fujitsu = kmalloc(sizeof(struct fujitsu_t), GFP_KERNEL);
  203. if (!fujitsu)
  204. return -ENOMEM;
  205. memset(fujitsu, 0, sizeof(struct fujitsu_t));
  206. result = acpi_bus_register_driver(&acpi_fujitsu_driver);
  207. if (result < 0) {
  208. ret = -ENODEV;
  209. goto fail_acpi;
  210. }
  211. /* Register backlight stuff */
  212. fujitsu->bl_device =
  213. backlight_device_register("fujitsu-laptop", NULL, NULL,
  214. &fujitsubl_ops);
  215. if (IS_ERR(fujitsu->bl_device))
  216. return PTR_ERR(fujitsu->bl_device);
  217. fujitsu->bl_device->props.max_brightness = FUJITSU_LCD_N_LEVELS - 1;
  218. ret = platform_driver_register(&fujitsupf_driver);
  219. if (ret)
  220. goto fail_backlight;
  221. /* Register platform stuff */
  222. fujitsu->pf_device = platform_device_alloc("fujitsu-laptop", -1);
  223. if (!fujitsu->pf_device) {
  224. ret = -ENOMEM;
  225. goto fail_platform_driver;
  226. }
  227. ret = platform_device_add(fujitsu->pf_device);
  228. if (ret)
  229. goto fail_platform_device1;
  230. ret =
  231. sysfs_create_group(&fujitsu->pf_device->dev.kobj,
  232. &fujitsupf_attribute_group);
  233. if (ret)
  234. goto fail_platform_device2;
  235. printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION
  236. " successfully loaded.\n");
  237. return 0;
  238. fail_platform_device2:
  239. platform_device_del(fujitsu->pf_device);
  240. fail_platform_device1:
  241. platform_device_put(fujitsu->pf_device);
  242. fail_platform_driver:
  243. platform_driver_unregister(&fujitsupf_driver);
  244. fail_backlight:
  245. backlight_device_unregister(fujitsu->bl_device);
  246. fail_acpi:
  247. kfree(fujitsu);
  248. return ret;
  249. }
  250. static void __exit fujitsu_cleanup(void)
  251. {
  252. sysfs_remove_group(&fujitsu->pf_device->dev.kobj,
  253. &fujitsupf_attribute_group);
  254. platform_device_unregister(fujitsu->pf_device);
  255. platform_driver_unregister(&fujitsupf_driver);
  256. backlight_device_unregister(fujitsu->bl_device);
  257. acpi_bus_unregister_driver(&acpi_fujitsu_driver);
  258. kfree(fujitsu);
  259. printk(KERN_INFO "fujitsu-laptop: driver unloaded.\n");
  260. }
  261. module_init(fujitsu_init);
  262. module_exit(fujitsu_cleanup);
  263. MODULE_AUTHOR("Jonathan Woithe");
  264. MODULE_DESCRIPTION("Fujitsu laptop extras support");
  265. MODULE_VERSION(FUJITSU_DRIVER_VERSION);
  266. MODULE_LICENSE("GPL");