fujitsu-laptop.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  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. #define FUJITSU_DRIVER_VERSION "0.3"
  47. #define FUJITSU_LCD_N_LEVELS 8
  48. #define ACPI_FUJITSU_CLASS "fujitsu"
  49. #define ACPI_FUJITSU_HID "FUJ02B1"
  50. #define ACPI_FUJITSU_DRIVER_NAME "Fujitsu laptop FUJ02B1 ACPI extras driver"
  51. #define ACPI_FUJITSU_DEVICE_NAME "Fujitsu FUJ02B1"
  52. struct fujitsu_t {
  53. acpi_handle acpi_handle;
  54. struct backlight_device *bl_device;
  55. struct platform_device *pf_device;
  56. unsigned long fuj02b1_state;
  57. unsigned int brightness_changed;
  58. unsigned int brightness_level;
  59. };
  60. static struct fujitsu_t *fujitsu;
  61. /* Hardware access */
  62. static int set_lcd_level(int level)
  63. {
  64. acpi_status status = AE_OK;
  65. union acpi_object arg0 = { ACPI_TYPE_INTEGER };
  66. struct acpi_object_list arg_list = { 1, &arg0 };
  67. acpi_handle handle = NULL;
  68. if (level < 0 || level >= FUJITSU_LCD_N_LEVELS)
  69. return -EINVAL;
  70. if (!fujitsu)
  71. return -EINVAL;
  72. status = acpi_get_handle(fujitsu->acpi_handle, "SBLL", &handle);
  73. if (ACPI_FAILURE(status)) {
  74. ACPI_DEBUG_PRINT((ACPI_DB_INFO, "SBLL not present\n"));
  75. return -ENODEV;
  76. }
  77. arg0.integer.value = level;
  78. status = acpi_evaluate_object(handle, NULL, &arg_list, NULL);
  79. if (ACPI_FAILURE(status))
  80. return -ENODEV;
  81. return 0;
  82. }
  83. static int get_lcd_level(void)
  84. {
  85. unsigned long state = 0;
  86. acpi_status status = AE_OK;
  87. // Get the Brightness
  88. status =
  89. acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, &state);
  90. if (status < 0)
  91. return status;
  92. fujitsu->fuj02b1_state = state;
  93. fujitsu->brightness_level = state & 0x0fffffff;
  94. if (state & 0x80000000)
  95. fujitsu->brightness_changed = 1;
  96. else
  97. fujitsu->brightness_changed = 0;
  98. return fujitsu->brightness_level;
  99. }
  100. /* Backlight device stuff */
  101. static int bl_get_brightness(struct backlight_device *b)
  102. {
  103. return get_lcd_level();
  104. }
  105. static int bl_update_status(struct backlight_device *b)
  106. {
  107. return set_lcd_level(b->props.brightness);
  108. }
  109. static struct backlight_ops fujitsubl_ops = {
  110. .get_brightness = bl_get_brightness,
  111. .update_status = bl_update_status,
  112. };
  113. /* Platform device */
  114. static ssize_t show_lcd_level(struct device *dev,
  115. struct device_attribute *attr, char *buf)
  116. {
  117. int ret;
  118. ret = get_lcd_level();
  119. if (ret < 0)
  120. return ret;
  121. return sprintf(buf, "%i\n", ret);
  122. }
  123. static ssize_t store_lcd_level(struct device *dev,
  124. struct device_attribute *attr, const char *buf,
  125. size_t count)
  126. {
  127. int level, ret;
  128. if (sscanf(buf, "%i", &level) != 1
  129. || (level < 0 || level >= FUJITSU_LCD_N_LEVELS))
  130. return -EINVAL;
  131. ret = set_lcd_level(level);
  132. if (ret < 0)
  133. return ret;
  134. return count;
  135. }
  136. static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
  137. static struct attribute *fujitsupf_attributes[] = {
  138. &dev_attr_lcd_level.attr,
  139. NULL
  140. };
  141. static struct attribute_group fujitsupf_attribute_group = {
  142. .attrs = fujitsupf_attributes
  143. };
  144. static struct platform_driver fujitsupf_driver = {
  145. .driver = {
  146. .name = "fujitsu-laptop",
  147. .owner = THIS_MODULE,
  148. }
  149. };
  150. /* ACPI device */
  151. static int acpi_fujitsu_add(struct acpi_device *device)
  152. {
  153. int result = 0;
  154. int state = 0;
  155. ACPI_FUNCTION_TRACE("acpi_fujitsu_add");
  156. if (!device)
  157. return -EINVAL;
  158. fujitsu->acpi_handle = device->handle;
  159. sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_DEVICE_NAME);
  160. sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
  161. acpi_driver_data(device) = fujitsu;
  162. result = acpi_bus_get_power(fujitsu->acpi_handle, &state);
  163. if (result) {
  164. ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
  165. "Error reading power state\n"));
  166. goto end;
  167. }
  168. printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
  169. acpi_device_name(device), acpi_device_bid(device),
  170. !device->power.state ? "on" : "off");
  171. end:
  172. return result;
  173. }
  174. static int acpi_fujitsu_remove(struct acpi_device *device, int type)
  175. {
  176. ACPI_FUNCTION_TRACE("acpi_fujitsu_remove");
  177. if (!device || !acpi_driver_data(device))
  178. return -EINVAL;
  179. fujitsu->acpi_handle = NULL;
  180. return 0;
  181. }
  182. static const struct acpi_device_id fujitsu_device_ids[] = {
  183. {ACPI_FUJITSU_HID, 0},
  184. {"", 0},
  185. };
  186. static struct acpi_driver acpi_fujitsu_driver = {
  187. .name = ACPI_FUJITSU_DRIVER_NAME,
  188. .class = ACPI_FUJITSU_CLASS,
  189. .ids = fujitsu_device_ids,
  190. .ops = {
  191. .add = acpi_fujitsu_add,
  192. .remove = acpi_fujitsu_remove,
  193. },
  194. };
  195. /* Initialization */
  196. static int __init fujitsu_init(void)
  197. {
  198. int ret, result;
  199. if (acpi_disabled)
  200. return -ENODEV;
  201. fujitsu = kmalloc(sizeof(struct fujitsu_t), GFP_KERNEL);
  202. if (!fujitsu)
  203. return -ENOMEM;
  204. memset(fujitsu, 0, sizeof(struct fujitsu_t));
  205. result = acpi_bus_register_driver(&acpi_fujitsu_driver);
  206. if (result < 0) {
  207. ret = -ENODEV;
  208. goto fail_acpi;
  209. }
  210. /* Register backlight stuff */
  211. fujitsu->bl_device =
  212. backlight_device_register("fujitsu-laptop", NULL, NULL,
  213. &fujitsubl_ops);
  214. if (IS_ERR(fujitsu->bl_device))
  215. return PTR_ERR(fujitsu->bl_device);
  216. fujitsu->bl_device->props.max_brightness = FUJITSU_LCD_N_LEVELS - 1;
  217. ret = platform_driver_register(&fujitsupf_driver);
  218. if (ret)
  219. goto fail_backlight;
  220. /* Register platform stuff */
  221. fujitsu->pf_device = platform_device_alloc("fujitsu-laptop", -1);
  222. if (!fujitsu->pf_device) {
  223. ret = -ENOMEM;
  224. goto fail_platform_driver;
  225. }
  226. ret = platform_device_add(fujitsu->pf_device);
  227. if (ret)
  228. goto fail_platform_device1;
  229. ret =
  230. sysfs_create_group(&fujitsu->pf_device->dev.kobj,
  231. &fujitsupf_attribute_group);
  232. if (ret)
  233. goto fail_platform_device2;
  234. printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION
  235. " successfully loaded.\n");
  236. return 0;
  237. fail_platform_device2:
  238. platform_device_del(fujitsu->pf_device);
  239. fail_platform_device1:
  240. platform_device_put(fujitsu->pf_device);
  241. fail_platform_driver:
  242. platform_driver_unregister(&fujitsupf_driver);
  243. fail_backlight:
  244. backlight_device_unregister(fujitsu->bl_device);
  245. fail_acpi:
  246. kfree(fujitsu);
  247. return ret;
  248. }
  249. static void __exit fujitsu_cleanup(void)
  250. {
  251. sysfs_remove_group(&fujitsu->pf_device->dev.kobj,
  252. &fujitsupf_attribute_group);
  253. platform_device_unregister(fujitsu->pf_device);
  254. platform_driver_unregister(&fujitsupf_driver);
  255. backlight_device_unregister(fujitsu->bl_device);
  256. acpi_bus_unregister_driver(&acpi_fujitsu_driver);
  257. kfree(fujitsu);
  258. printk(KERN_INFO "fujitsu-laptop: driver unloaded.\n");
  259. }
  260. module_init(fujitsu_init);
  261. module_exit(fujitsu_cleanup);
  262. MODULE_AUTHOR("Jonathan Woithe");
  263. MODULE_DESCRIPTION("Fujitsu laptop extras support");
  264. MODULE_VERSION(FUJITSU_DRIVER_VERSION);
  265. MODULE_LICENSE("GPL");
  266. static struct pnp_device_id pnp_ids[] = {
  267. { .id = "FUJ02bf" },
  268. { .id = "" }
  269. };
  270. MODULE_DEVICE_TABLE(pnp, pnp_ids);