fujitsu-laptop.c 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070
  1. /*-*-linux-c-*-*/
  2. /*
  3. Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@just42.net>
  4. Copyright (C) 2008 Peter Gruber <nokos@gmx.net>
  5. Copyright (C) 2008 Tony Vroon <tony@linx.net>
  6. Based on earlier work:
  7. Copyright (C) 2003 Shane Spencer <shane@bogomip.com>
  8. Adrian Yee <brewt-fujitsu@brewt.org>
  9. Templated from msi-laptop.c and thinkpad_acpi.c which is copyright
  10. by its respective authors.
  11. This program is free software; you can redistribute it and/or modify
  12. it under the terms of the GNU General Public License as published by
  13. the Free Software Foundation; either version 2 of the License, or
  14. (at your option) any later version.
  15. This program is distributed in the hope that it will be useful, but
  16. WITHOUT ANY WARRANTY; without even the implied warranty of
  17. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  18. General Public License for more details.
  19. You should have received a copy of the GNU General Public License
  20. along with this program; if not, write to the Free Software
  21. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  22. 02110-1301, USA.
  23. */
  24. /*
  25. * fujitsu-laptop.c - Fujitsu laptop support, providing access to additional
  26. * features made available on a range of Fujitsu laptops including the
  27. * P2xxx/P5xxx/S6xxx/S7xxx series.
  28. *
  29. * This driver implements a vendor-specific backlight control interface for
  30. * Fujitsu laptops and provides support for hotkeys present on certain Fujitsu
  31. * laptops.
  32. *
  33. * This driver has been tested on a Fujitsu Lifebook S6410, S7020 and
  34. * P8010. It should work on most P-series and S-series Lifebooks, but
  35. * YMMV.
  36. *
  37. * The module parameter use_alt_lcd_levels switches between different ACPI
  38. * brightness controls which are used by different Fujitsu laptops. In most
  39. * cases the correct method is automatically detected. "use_alt_lcd_levels=1"
  40. * is applicable for a Fujitsu Lifebook S6410 if autodetection fails.
  41. *
  42. */
  43. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  44. #include <linux/module.h>
  45. #include <linux/kernel.h>
  46. #include <linux/init.h>
  47. #include <linux/acpi.h>
  48. #include <linux/dmi.h>
  49. #include <linux/backlight.h>
  50. #include <linux/fb.h>
  51. #include <linux/input.h>
  52. #include <linux/input/sparse-keymap.h>
  53. #include <linux/kfifo.h>
  54. #include <linux/leds.h>
  55. #include <linux/platform_device.h>
  56. #include <linux/slab.h>
  57. #include <acpi/video.h>
  58. #define FUJITSU_DRIVER_VERSION "0.6.0"
  59. #define FUJITSU_LCD_N_LEVELS 8
  60. #define ACPI_FUJITSU_CLASS "fujitsu"
  61. #define ACPI_FUJITSU_BL_HID "FUJ02B1"
  62. #define ACPI_FUJITSU_BL_DRIVER_NAME "Fujitsu laptop FUJ02B1 ACPI brightness driver"
  63. #define ACPI_FUJITSU_BL_DEVICE_NAME "Fujitsu FUJ02B1"
  64. #define ACPI_FUJITSU_LAPTOP_HID "FUJ02E3"
  65. #define ACPI_FUJITSU_LAPTOP_DRIVER_NAME "Fujitsu laptop FUJ02E3 ACPI hotkeys driver"
  66. #define ACPI_FUJITSU_LAPTOP_DEVICE_NAME "Fujitsu FUJ02E3"
  67. #define ACPI_FUJITSU_NOTIFY_CODE1 0x80
  68. /* FUNC interface - command values */
  69. #define FUNC_FLAGS 0x1000
  70. #define FUNC_LEDS 0x1001
  71. #define FUNC_BUTTONS 0x1002
  72. #define FUNC_BACKLIGHT 0x1004
  73. /* FUNC interface - responses */
  74. #define UNSUPPORTED_CMD 0x80000000
  75. /* FUNC interface - status flags */
  76. #define FLAG_RFKILL 0x020
  77. #define FLAG_LID 0x100
  78. #define FLAG_DOCK 0x200
  79. /* FUNC interface - LED control */
  80. #define FUNC_LED_OFF 0x1
  81. #define FUNC_LED_ON 0x30001
  82. #define KEYBOARD_LAMPS 0x100
  83. #define LOGOLAMP_POWERON 0x2000
  84. #define LOGOLAMP_ALWAYS 0x4000
  85. #define RADIO_LED_ON 0x20
  86. #define ECO_LED 0x10000
  87. #define ECO_LED_ON 0x80000
  88. /* Hotkey details */
  89. #define KEY1_CODE 0x410 /* codes for the keys in the GIRB register */
  90. #define KEY2_CODE 0x411
  91. #define KEY3_CODE 0x412
  92. #define KEY4_CODE 0x413
  93. #define KEY5_CODE 0x420
  94. #define MAX_HOTKEY_RINGBUFFER_SIZE 100
  95. #define RINGBUFFERSIZE 40
  96. /* Debugging */
  97. #define FUJLAPTOP_DBG_ERROR 0x0001
  98. #define FUJLAPTOP_DBG_WARN 0x0002
  99. #define FUJLAPTOP_DBG_INFO 0x0004
  100. #define FUJLAPTOP_DBG_TRACE 0x0008
  101. #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
  102. #define vdbg_printk(a_dbg_level, format, arg...) \
  103. do { if (dbg_level & a_dbg_level) \
  104. printk(KERN_DEBUG pr_fmt("%s: " format), __func__, ## arg); \
  105. } while (0)
  106. #else
  107. #define vdbg_printk(a_dbg_level, format, arg...) \
  108. do { } while (0)
  109. #endif
  110. /* Device controlling the backlight and associated keys */
  111. struct fujitsu_bl {
  112. acpi_handle acpi_handle;
  113. struct input_dev *input;
  114. char phys[32];
  115. struct backlight_device *bl_device;
  116. unsigned int max_brightness;
  117. unsigned int brightness_level;
  118. };
  119. static struct fujitsu_bl *fujitsu_bl;
  120. static int use_alt_lcd_levels = -1;
  121. static bool disable_brightness_adjust;
  122. /* Device used to access hotkeys and other features on the laptop */
  123. struct fujitsu_laptop {
  124. acpi_handle acpi_handle;
  125. struct acpi_device *dev;
  126. struct input_dev *input;
  127. char phys[32];
  128. struct platform_device *pf_device;
  129. struct kfifo fifo;
  130. spinlock_t fifo_lock;
  131. int flags_supported;
  132. int flags_state;
  133. };
  134. static struct fujitsu_laptop *fujitsu_laptop;
  135. #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
  136. static u32 dbg_level = 0x03;
  137. #endif
  138. /* Fujitsu ACPI interface function */
  139. static int call_fext_func(int func, int op, int feature, int state)
  140. {
  141. union acpi_object params[4] = {
  142. { .integer.type = ACPI_TYPE_INTEGER, .integer.value = func },
  143. { .integer.type = ACPI_TYPE_INTEGER, .integer.value = op },
  144. { .integer.type = ACPI_TYPE_INTEGER, .integer.value = feature },
  145. { .integer.type = ACPI_TYPE_INTEGER, .integer.value = state }
  146. };
  147. struct acpi_object_list arg_list = { 4, params };
  148. unsigned long long value;
  149. acpi_status status;
  150. status = acpi_evaluate_integer(fujitsu_laptop->acpi_handle, "FUNC",
  151. &arg_list, &value);
  152. if (ACPI_FAILURE(status)) {
  153. vdbg_printk(FUJLAPTOP_DBG_ERROR, "Failed to evaluate FUNC\n");
  154. return -ENODEV;
  155. }
  156. vdbg_printk(FUJLAPTOP_DBG_TRACE, "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n",
  157. func, op, feature, state, (int)value);
  158. return value;
  159. }
  160. /* Hardware access for LCD brightness control */
  161. static int set_lcd_level(int level)
  162. {
  163. acpi_status status;
  164. char *method;
  165. switch (use_alt_lcd_levels) {
  166. case -1:
  167. if (acpi_has_method(fujitsu_bl->acpi_handle, "SBL2"))
  168. method = "SBL2";
  169. else
  170. method = "SBLL";
  171. break;
  172. case 1:
  173. method = "SBL2";
  174. break;
  175. default:
  176. method = "SBLL";
  177. break;
  178. }
  179. vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via %s [%d]\n",
  180. method, level);
  181. if (level < 0 || level >= fujitsu_bl->max_brightness)
  182. return -EINVAL;
  183. status = acpi_execute_simple_method(fujitsu_bl->acpi_handle, method,
  184. level);
  185. if (ACPI_FAILURE(status)) {
  186. vdbg_printk(FUJLAPTOP_DBG_ERROR, "Failed to evaluate %s\n",
  187. method);
  188. return -ENODEV;
  189. }
  190. fujitsu_bl->brightness_level = level;
  191. return 0;
  192. }
  193. static int get_lcd_level(void)
  194. {
  195. unsigned long long state = 0;
  196. acpi_status status = AE_OK;
  197. vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLL\n");
  198. status = acpi_evaluate_integer(fujitsu_bl->acpi_handle, "GBLL", NULL,
  199. &state);
  200. if (ACPI_FAILURE(status))
  201. return 0;
  202. fujitsu_bl->brightness_level = state & 0x0fffffff;
  203. return fujitsu_bl->brightness_level;
  204. }
  205. static int get_max_brightness(void)
  206. {
  207. unsigned long long state = 0;
  208. acpi_status status = AE_OK;
  209. vdbg_printk(FUJLAPTOP_DBG_TRACE, "get max lcd level via RBLL\n");
  210. status = acpi_evaluate_integer(fujitsu_bl->acpi_handle, "RBLL", NULL,
  211. &state);
  212. if (ACPI_FAILURE(status))
  213. return -1;
  214. fujitsu_bl->max_brightness = state;
  215. return fujitsu_bl->max_brightness;
  216. }
  217. /* Backlight device stuff */
  218. static int bl_get_brightness(struct backlight_device *b)
  219. {
  220. return b->props.power == FB_BLANK_POWERDOWN ? 0 : get_lcd_level();
  221. }
  222. static int bl_update_status(struct backlight_device *b)
  223. {
  224. if (b->props.power == FB_BLANK_POWERDOWN)
  225. call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x3);
  226. else
  227. call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x0);
  228. return set_lcd_level(b->props.brightness);
  229. }
  230. static const struct backlight_ops fujitsu_bl_ops = {
  231. .get_brightness = bl_get_brightness,
  232. .update_status = bl_update_status,
  233. };
  234. static ssize_t lid_show(struct device *dev, struct device_attribute *attr,
  235. char *buf)
  236. {
  237. if (!(fujitsu_laptop->flags_supported & FLAG_LID))
  238. return sprintf(buf, "unknown\n");
  239. if (fujitsu_laptop->flags_state & FLAG_LID)
  240. return sprintf(buf, "open\n");
  241. else
  242. return sprintf(buf, "closed\n");
  243. }
  244. static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
  245. char *buf)
  246. {
  247. if (!(fujitsu_laptop->flags_supported & FLAG_DOCK))
  248. return sprintf(buf, "unknown\n");
  249. if (fujitsu_laptop->flags_state & FLAG_DOCK)
  250. return sprintf(buf, "docked\n");
  251. else
  252. return sprintf(buf, "undocked\n");
  253. }
  254. static ssize_t radios_show(struct device *dev, struct device_attribute *attr,
  255. char *buf)
  256. {
  257. if (!(fujitsu_laptop->flags_supported & FLAG_RFKILL))
  258. return sprintf(buf, "unknown\n");
  259. if (fujitsu_laptop->flags_state & FLAG_RFKILL)
  260. return sprintf(buf, "on\n");
  261. else
  262. return sprintf(buf, "killed\n");
  263. }
  264. static DEVICE_ATTR_RO(lid);
  265. static DEVICE_ATTR_RO(dock);
  266. static DEVICE_ATTR_RO(radios);
  267. static struct attribute *fujitsu_pf_attributes[] = {
  268. &dev_attr_lid.attr,
  269. &dev_attr_dock.attr,
  270. &dev_attr_radios.attr,
  271. NULL
  272. };
  273. static struct attribute_group fujitsu_pf_attribute_group = {
  274. .attrs = fujitsu_pf_attributes
  275. };
  276. static struct platform_driver fujitsu_pf_driver = {
  277. .driver = {
  278. .name = "fujitsu-laptop",
  279. }
  280. };
  281. /* ACPI device for LCD brightness control */
  282. static const struct key_entry keymap_backlight[] = {
  283. { KE_KEY, true, { KEY_BRIGHTNESSUP } },
  284. { KE_KEY, false, { KEY_BRIGHTNESSDOWN } },
  285. { KE_END, 0 }
  286. };
  287. static int acpi_fujitsu_bl_input_setup(struct acpi_device *device)
  288. {
  289. struct fujitsu_bl *fujitsu_bl = acpi_driver_data(device);
  290. int ret;
  291. fujitsu_bl->input = devm_input_allocate_device(&device->dev);
  292. if (!fujitsu_bl->input)
  293. return -ENOMEM;
  294. snprintf(fujitsu_bl->phys, sizeof(fujitsu_bl->phys),
  295. "%s/video/input0", acpi_device_hid(device));
  296. fujitsu_bl->input->name = acpi_device_name(device);
  297. fujitsu_bl->input->phys = fujitsu_bl->phys;
  298. fujitsu_bl->input->id.bustype = BUS_HOST;
  299. fujitsu_bl->input->id.product = 0x06;
  300. ret = sparse_keymap_setup(fujitsu_bl->input, keymap_backlight, NULL);
  301. if (ret)
  302. return ret;
  303. return input_register_device(fujitsu_bl->input);
  304. }
  305. static int fujitsu_backlight_register(struct acpi_device *device)
  306. {
  307. const struct backlight_properties props = {
  308. .brightness = fujitsu_bl->brightness_level,
  309. .max_brightness = fujitsu_bl->max_brightness - 1,
  310. .type = BACKLIGHT_PLATFORM
  311. };
  312. struct backlight_device *bd;
  313. bd = devm_backlight_device_register(&device->dev, "fujitsu-laptop",
  314. &device->dev, NULL,
  315. &fujitsu_bl_ops, &props);
  316. if (IS_ERR(bd))
  317. return PTR_ERR(bd);
  318. fujitsu_bl->bl_device = bd;
  319. return 0;
  320. }
  321. static int acpi_fujitsu_bl_add(struct acpi_device *device)
  322. {
  323. int state = 0;
  324. int error;
  325. if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
  326. return -ENODEV;
  327. if (!device)
  328. return -EINVAL;
  329. fujitsu_bl->acpi_handle = device->handle;
  330. sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_BL_DEVICE_NAME);
  331. sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
  332. device->driver_data = fujitsu_bl;
  333. error = acpi_fujitsu_bl_input_setup(device);
  334. if (error)
  335. return error;
  336. error = acpi_bus_update_power(fujitsu_bl->acpi_handle, &state);
  337. if (error) {
  338. pr_err("Error reading power state\n");
  339. return error;
  340. }
  341. pr_info("ACPI: %s [%s] (%s)\n",
  342. acpi_device_name(device), acpi_device_bid(device),
  343. !device->power.state ? "on" : "off");
  344. if (acpi_has_method(device->handle, METHOD_NAME__INI)) {
  345. vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
  346. if (ACPI_FAILURE
  347. (acpi_evaluate_object
  348. (device->handle, METHOD_NAME__INI, NULL, NULL)))
  349. pr_err("_INI Method failed\n");
  350. }
  351. if (get_max_brightness() <= 0)
  352. fujitsu_bl->max_brightness = FUJITSU_LCD_N_LEVELS;
  353. get_lcd_level();
  354. error = fujitsu_backlight_register(device);
  355. if (error)
  356. return error;
  357. return 0;
  358. }
  359. /* Brightness notify */
  360. static void acpi_fujitsu_bl_notify(struct acpi_device *device, u32 event)
  361. {
  362. struct input_dev *input;
  363. int oldb, newb;
  364. input = fujitsu_bl->input;
  365. if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
  366. vdbg_printk(FUJLAPTOP_DBG_WARN,
  367. "unsupported event [0x%x]\n", event);
  368. sparse_keymap_report_event(input, -1, 1, true);
  369. return;
  370. }
  371. oldb = fujitsu_bl->brightness_level;
  372. get_lcd_level();
  373. newb = fujitsu_bl->brightness_level;
  374. vdbg_printk(FUJLAPTOP_DBG_TRACE, "brightness button event [%i -> %i]\n",
  375. oldb, newb);
  376. if (oldb == newb)
  377. return;
  378. if (!disable_brightness_adjust)
  379. set_lcd_level(newb);
  380. sparse_keymap_report_event(input, oldb < newb, 1, true);
  381. }
  382. /* ACPI device for hotkey handling */
  383. static const struct key_entry keymap_default[] = {
  384. { KE_KEY, KEY1_CODE, { KEY_PROG1 } },
  385. { KE_KEY, KEY2_CODE, { KEY_PROG2 } },
  386. { KE_KEY, KEY3_CODE, { KEY_PROG3 } },
  387. { KE_KEY, KEY4_CODE, { KEY_PROG4 } },
  388. { KE_KEY, KEY5_CODE, { KEY_RFKILL } },
  389. { KE_KEY, BIT(26), { KEY_TOUCHPAD_TOGGLE } },
  390. { KE_END, 0 }
  391. };
  392. static const struct key_entry keymap_s64x0[] = {
  393. { KE_KEY, KEY1_CODE, { KEY_SCREENLOCK } }, /* "Lock" */
  394. { KE_KEY, KEY2_CODE, { KEY_HELP } }, /* "Mobility Center */
  395. { KE_KEY, KEY3_CODE, { KEY_PROG3 } },
  396. { KE_KEY, KEY4_CODE, { KEY_PROG4 } },
  397. { KE_END, 0 }
  398. };
  399. static const struct key_entry keymap_p8010[] = {
  400. { KE_KEY, KEY1_CODE, { KEY_HELP } }, /* "Support" */
  401. { KE_KEY, KEY2_CODE, { KEY_PROG2 } },
  402. { KE_KEY, KEY3_CODE, { KEY_SWITCHVIDEOMODE } }, /* "Presentation" */
  403. { KE_KEY, KEY4_CODE, { KEY_WWW } }, /* "WWW" */
  404. { KE_END, 0 }
  405. };
  406. static const struct key_entry *keymap = keymap_default;
  407. static int fujitsu_laptop_dmi_keymap_override(const struct dmi_system_id *id)
  408. {
  409. pr_info("Identified laptop model '%s'\n", id->ident);
  410. keymap = id->driver_data;
  411. return 1;
  412. }
  413. static const struct dmi_system_id fujitsu_laptop_dmi_table[] = {
  414. {
  415. .callback = fujitsu_laptop_dmi_keymap_override,
  416. .ident = "Fujitsu Siemens S6410",
  417. .matches = {
  418. DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
  419. DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6410"),
  420. },
  421. .driver_data = (void *)keymap_s64x0
  422. },
  423. {
  424. .callback = fujitsu_laptop_dmi_keymap_override,
  425. .ident = "Fujitsu Siemens S6420",
  426. .matches = {
  427. DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
  428. DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6420"),
  429. },
  430. .driver_data = (void *)keymap_s64x0
  431. },
  432. {
  433. .callback = fujitsu_laptop_dmi_keymap_override,
  434. .ident = "Fujitsu LifeBook P8010",
  435. .matches = {
  436. DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
  437. DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P8010"),
  438. },
  439. .driver_data = (void *)keymap_p8010
  440. },
  441. {}
  442. };
  443. static int acpi_fujitsu_laptop_input_setup(struct acpi_device *device)
  444. {
  445. struct fujitsu_laptop *fujitsu_laptop = acpi_driver_data(device);
  446. int ret;
  447. fujitsu_laptop->input = devm_input_allocate_device(&device->dev);
  448. if (!fujitsu_laptop->input)
  449. return -ENOMEM;
  450. snprintf(fujitsu_laptop->phys, sizeof(fujitsu_laptop->phys),
  451. "%s/video/input0", acpi_device_hid(device));
  452. fujitsu_laptop->input->name = acpi_device_name(device);
  453. fujitsu_laptop->input->phys = fujitsu_laptop->phys;
  454. fujitsu_laptop->input->id.bustype = BUS_HOST;
  455. fujitsu_laptop->input->id.product = 0x06;
  456. dmi_check_system(fujitsu_laptop_dmi_table);
  457. ret = sparse_keymap_setup(fujitsu_laptop->input, keymap, NULL);
  458. if (ret)
  459. return ret;
  460. return input_register_device(fujitsu_laptop->input);
  461. }
  462. static int fujitsu_laptop_platform_add(void)
  463. {
  464. int ret;
  465. fujitsu_laptop->pf_device = platform_device_alloc("fujitsu-laptop", -1);
  466. if (!fujitsu_laptop->pf_device)
  467. return -ENOMEM;
  468. ret = platform_device_add(fujitsu_laptop->pf_device);
  469. if (ret)
  470. goto err_put_platform_device;
  471. ret = sysfs_create_group(&fujitsu_laptop->pf_device->dev.kobj,
  472. &fujitsu_pf_attribute_group);
  473. if (ret)
  474. goto err_del_platform_device;
  475. return 0;
  476. err_del_platform_device:
  477. platform_device_del(fujitsu_laptop->pf_device);
  478. err_put_platform_device:
  479. platform_device_put(fujitsu_laptop->pf_device);
  480. return ret;
  481. }
  482. static void fujitsu_laptop_platform_remove(void)
  483. {
  484. sysfs_remove_group(&fujitsu_laptop->pf_device->dev.kobj,
  485. &fujitsu_pf_attribute_group);
  486. platform_device_unregister(fujitsu_laptop->pf_device);
  487. }
  488. static int logolamp_set(struct led_classdev *cdev,
  489. enum led_brightness brightness)
  490. {
  491. int poweron = FUNC_LED_ON, always = FUNC_LED_ON;
  492. int ret;
  493. if (brightness < LED_HALF)
  494. poweron = FUNC_LED_OFF;
  495. if (brightness < LED_FULL)
  496. always = FUNC_LED_OFF;
  497. ret = call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron);
  498. if (ret < 0)
  499. return ret;
  500. return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always);
  501. }
  502. static enum led_brightness logolamp_get(struct led_classdev *cdev)
  503. {
  504. int ret;
  505. ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0);
  506. if (ret == FUNC_LED_ON)
  507. return LED_FULL;
  508. ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0);
  509. if (ret == FUNC_LED_ON)
  510. return LED_HALF;
  511. return LED_OFF;
  512. }
  513. static struct led_classdev logolamp_led = {
  514. .name = "fujitsu::logolamp",
  515. .brightness_set_blocking = logolamp_set,
  516. .brightness_get = logolamp_get
  517. };
  518. static int kblamps_set(struct led_classdev *cdev,
  519. enum led_brightness brightness)
  520. {
  521. if (brightness >= LED_FULL)
  522. return call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS,
  523. FUNC_LED_ON);
  524. else
  525. return call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS,
  526. FUNC_LED_OFF);
  527. }
  528. static enum led_brightness kblamps_get(struct led_classdev *cdev)
  529. {
  530. enum led_brightness brightness = LED_OFF;
  531. if (call_fext_func(FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON)
  532. brightness = LED_FULL;
  533. return brightness;
  534. }
  535. static struct led_classdev kblamps_led = {
  536. .name = "fujitsu::kblamps",
  537. .brightness_set_blocking = kblamps_set,
  538. .brightness_get = kblamps_get
  539. };
  540. static int radio_led_set(struct led_classdev *cdev,
  541. enum led_brightness brightness)
  542. {
  543. if (brightness >= LED_FULL)
  544. return call_fext_func(FUNC_FLAGS, 0x5, RADIO_LED_ON,
  545. RADIO_LED_ON);
  546. else
  547. return call_fext_func(FUNC_FLAGS, 0x5, RADIO_LED_ON, 0x0);
  548. }
  549. static enum led_brightness radio_led_get(struct led_classdev *cdev)
  550. {
  551. enum led_brightness brightness = LED_OFF;
  552. if (call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0) & RADIO_LED_ON)
  553. brightness = LED_FULL;
  554. return brightness;
  555. }
  556. static struct led_classdev radio_led = {
  557. .name = "fujitsu::radio_led",
  558. .brightness_set_blocking = radio_led_set,
  559. .brightness_get = radio_led_get,
  560. .default_trigger = "rfkill-any"
  561. };
  562. static int eco_led_set(struct led_classdev *cdev,
  563. enum led_brightness brightness)
  564. {
  565. int curr;
  566. curr = call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0);
  567. if (brightness >= LED_FULL)
  568. return call_fext_func(FUNC_LEDS, 0x1, ECO_LED,
  569. curr | ECO_LED_ON);
  570. else
  571. return call_fext_func(FUNC_LEDS, 0x1, ECO_LED,
  572. curr & ~ECO_LED_ON);
  573. }
  574. static enum led_brightness eco_led_get(struct led_classdev *cdev)
  575. {
  576. enum led_brightness brightness = LED_OFF;
  577. if (call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON)
  578. brightness = LED_FULL;
  579. return brightness;
  580. }
  581. static struct led_classdev eco_led = {
  582. .name = "fujitsu::eco_led",
  583. .brightness_set_blocking = eco_led_set,
  584. .brightness_get = eco_led_get
  585. };
  586. static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
  587. {
  588. int result;
  589. if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) {
  590. result = devm_led_classdev_register(&device->dev,
  591. &logolamp_led);
  592. if (result)
  593. return result;
  594. }
  595. if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) &&
  596. (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) {
  597. result = devm_led_classdev_register(&device->dev, &kblamps_led);
  598. if (result)
  599. return result;
  600. }
  601. /*
  602. * BTNI bit 24 seems to indicate the presence of a radio toggle
  603. * button in place of a slide switch, and all such machines appear
  604. * to also have an RF LED. Therefore use bit 24 as an indicator
  605. * that an RF LED is present.
  606. */
  607. if (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) {
  608. result = devm_led_classdev_register(&device->dev, &radio_led);
  609. if (result)
  610. return result;
  611. }
  612. /* Support for eco led is not always signaled in bit corresponding
  613. * to the bit used to control the led. According to the DSDT table,
  614. * bit 14 seems to indicate presence of said led as well.
  615. * Confirm by testing the status.
  616. */
  617. if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) &&
  618. (call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) {
  619. result = devm_led_classdev_register(&device->dev, &eco_led);
  620. if (result)
  621. return result;
  622. }
  623. return 0;
  624. }
  625. static int acpi_fujitsu_laptop_add(struct acpi_device *device)
  626. {
  627. int state = 0;
  628. int error;
  629. int i;
  630. if (!device)
  631. return -EINVAL;
  632. fujitsu_laptop->acpi_handle = device->handle;
  633. sprintf(acpi_device_name(device), "%s",
  634. ACPI_FUJITSU_LAPTOP_DEVICE_NAME);
  635. sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
  636. device->driver_data = fujitsu_laptop;
  637. /* kfifo */
  638. spin_lock_init(&fujitsu_laptop->fifo_lock);
  639. error = kfifo_alloc(&fujitsu_laptop->fifo, RINGBUFFERSIZE * sizeof(int),
  640. GFP_KERNEL);
  641. if (error) {
  642. pr_err("kfifo_alloc failed\n");
  643. goto err_stop;
  644. }
  645. error = acpi_fujitsu_laptop_input_setup(device);
  646. if (error)
  647. goto err_free_fifo;
  648. error = acpi_bus_update_power(fujitsu_laptop->acpi_handle, &state);
  649. if (error) {
  650. pr_err("Error reading power state\n");
  651. goto err_free_fifo;
  652. }
  653. pr_info("ACPI: %s [%s] (%s)\n",
  654. acpi_device_name(device), acpi_device_bid(device),
  655. !device->power.state ? "on" : "off");
  656. fujitsu_laptop->dev = device;
  657. if (acpi_has_method(device->handle, METHOD_NAME__INI)) {
  658. vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
  659. if (ACPI_FAILURE
  660. (acpi_evaluate_object
  661. (device->handle, METHOD_NAME__INI, NULL, NULL)))
  662. pr_err("_INI Method failed\n");
  663. }
  664. i = 0;
  665. while (call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0
  666. && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE)
  667. ; /* No action, result is discarded */
  668. vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i);
  669. fujitsu_laptop->flags_supported =
  670. call_fext_func(FUNC_FLAGS, 0x0, 0x0, 0x0);
  671. /* Make sure our bitmask of supported functions is cleared if the
  672. RFKILL function block is not implemented, like on the S7020. */
  673. if (fujitsu_laptop->flags_supported == UNSUPPORTED_CMD)
  674. fujitsu_laptop->flags_supported = 0;
  675. if (fujitsu_laptop->flags_supported)
  676. fujitsu_laptop->flags_state =
  677. call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0);
  678. /* Suspect this is a keymap of the application panel, print it */
  679. pr_info("BTNI: [0x%x]\n", call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0));
  680. /* Sync backlight power status */
  681. if (fujitsu_bl->bl_device &&
  682. acpi_video_get_backlight_type() == acpi_backlight_vendor) {
  683. if (call_fext_func(FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3)
  684. fujitsu_bl->bl_device->props.power = FB_BLANK_POWERDOWN;
  685. else
  686. fujitsu_bl->bl_device->props.power = FB_BLANK_UNBLANK;
  687. }
  688. error = acpi_fujitsu_laptop_leds_register(device);
  689. if (error)
  690. goto err_free_fifo;
  691. error = fujitsu_laptop_platform_add();
  692. if (error)
  693. goto err_free_fifo;
  694. return 0;
  695. err_free_fifo:
  696. kfifo_free(&fujitsu_laptop->fifo);
  697. err_stop:
  698. return error;
  699. }
  700. static int acpi_fujitsu_laptop_remove(struct acpi_device *device)
  701. {
  702. struct fujitsu_laptop *fujitsu_laptop = acpi_driver_data(device);
  703. fujitsu_laptop_platform_remove();
  704. kfifo_free(&fujitsu_laptop->fifo);
  705. return 0;
  706. }
  707. static void acpi_fujitsu_laptop_press(int scancode)
  708. {
  709. struct input_dev *input = fujitsu_laptop->input;
  710. int status;
  711. status = kfifo_in_locked(&fujitsu_laptop->fifo,
  712. (unsigned char *)&scancode, sizeof(scancode),
  713. &fujitsu_laptop->fifo_lock);
  714. if (status != sizeof(scancode)) {
  715. vdbg_printk(FUJLAPTOP_DBG_WARN,
  716. "Could not push scancode [0x%x]\n", scancode);
  717. return;
  718. }
  719. sparse_keymap_report_event(input, scancode, 1, false);
  720. vdbg_printk(FUJLAPTOP_DBG_TRACE,
  721. "Push scancode into ringbuffer [0x%x]\n", scancode);
  722. }
  723. static void acpi_fujitsu_laptop_release(void)
  724. {
  725. struct input_dev *input = fujitsu_laptop->input;
  726. int scancode, status;
  727. while (true) {
  728. status = kfifo_out_locked(&fujitsu_laptop->fifo,
  729. (unsigned char *)&scancode,
  730. sizeof(scancode),
  731. &fujitsu_laptop->fifo_lock);
  732. if (status != sizeof(scancode))
  733. return;
  734. sparse_keymap_report_event(input, scancode, 0, false);
  735. vdbg_printk(FUJLAPTOP_DBG_TRACE,
  736. "Pop scancode from ringbuffer [0x%x]\n", scancode);
  737. }
  738. }
  739. static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event)
  740. {
  741. struct input_dev *input;
  742. int scancode, i = 0;
  743. unsigned int irb;
  744. input = fujitsu_laptop->input;
  745. if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
  746. vdbg_printk(FUJLAPTOP_DBG_WARN,
  747. "Unsupported event [0x%x]\n", event);
  748. sparse_keymap_report_event(input, -1, 1, true);
  749. return;
  750. }
  751. if (fujitsu_laptop->flags_supported)
  752. fujitsu_laptop->flags_state =
  753. call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0);
  754. while ((irb = call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 &&
  755. i++ < MAX_HOTKEY_RINGBUFFER_SIZE) {
  756. scancode = irb & 0x4ff;
  757. if (sparse_keymap_entry_from_scancode(input, scancode))
  758. acpi_fujitsu_laptop_press(scancode);
  759. else if (scancode == 0)
  760. acpi_fujitsu_laptop_release();
  761. else
  762. vdbg_printk(FUJLAPTOP_DBG_WARN,
  763. "Unknown GIRB result [%x]\n", irb);
  764. }
  765. /* On some models (first seen on the Skylake-based Lifebook
  766. * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is
  767. * handled in software; its state is queried using FUNC_FLAGS
  768. */
  769. if ((fujitsu_laptop->flags_supported & BIT(26)) &&
  770. (call_fext_func(FUNC_FLAGS, 0x1, 0x0, 0x0) & BIT(26)))
  771. sparse_keymap_report_event(input, BIT(26), 1, true);
  772. }
  773. /* Initialization */
  774. static const struct acpi_device_id fujitsu_bl_device_ids[] = {
  775. {ACPI_FUJITSU_BL_HID, 0},
  776. {"", 0},
  777. };
  778. static struct acpi_driver acpi_fujitsu_bl_driver = {
  779. .name = ACPI_FUJITSU_BL_DRIVER_NAME,
  780. .class = ACPI_FUJITSU_CLASS,
  781. .ids = fujitsu_bl_device_ids,
  782. .ops = {
  783. .add = acpi_fujitsu_bl_add,
  784. .notify = acpi_fujitsu_bl_notify,
  785. },
  786. };
  787. static const struct acpi_device_id fujitsu_laptop_device_ids[] = {
  788. {ACPI_FUJITSU_LAPTOP_HID, 0},
  789. {"", 0},
  790. };
  791. static struct acpi_driver acpi_fujitsu_laptop_driver = {
  792. .name = ACPI_FUJITSU_LAPTOP_DRIVER_NAME,
  793. .class = ACPI_FUJITSU_CLASS,
  794. .ids = fujitsu_laptop_device_ids,
  795. .ops = {
  796. .add = acpi_fujitsu_laptop_add,
  797. .remove = acpi_fujitsu_laptop_remove,
  798. .notify = acpi_fujitsu_laptop_notify,
  799. },
  800. };
  801. static const struct acpi_device_id fujitsu_ids[] __used = {
  802. {ACPI_FUJITSU_BL_HID, 0},
  803. {ACPI_FUJITSU_LAPTOP_HID, 0},
  804. {"", 0}
  805. };
  806. MODULE_DEVICE_TABLE(acpi, fujitsu_ids);
  807. static int __init fujitsu_init(void)
  808. {
  809. int ret;
  810. if (acpi_disabled)
  811. return -ENODEV;
  812. fujitsu_bl = kzalloc(sizeof(struct fujitsu_bl), GFP_KERNEL);
  813. if (!fujitsu_bl)
  814. return -ENOMEM;
  815. ret = acpi_bus_register_driver(&acpi_fujitsu_bl_driver);
  816. if (ret)
  817. goto err_free_fujitsu_bl;
  818. /* Register platform stuff */
  819. ret = platform_driver_register(&fujitsu_pf_driver);
  820. if (ret)
  821. goto err_unregister_acpi;
  822. /* Register laptop driver */
  823. fujitsu_laptop = kzalloc(sizeof(struct fujitsu_laptop), GFP_KERNEL);
  824. if (!fujitsu_laptop) {
  825. ret = -ENOMEM;
  826. goto err_unregister_platform_driver;
  827. }
  828. ret = acpi_bus_register_driver(&acpi_fujitsu_laptop_driver);
  829. if (ret)
  830. goto err_free_fujitsu_laptop;
  831. pr_info("driver " FUJITSU_DRIVER_VERSION " successfully loaded\n");
  832. return 0;
  833. err_free_fujitsu_laptop:
  834. kfree(fujitsu_laptop);
  835. err_unregister_platform_driver:
  836. platform_driver_unregister(&fujitsu_pf_driver);
  837. err_unregister_acpi:
  838. acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver);
  839. err_free_fujitsu_bl:
  840. kfree(fujitsu_bl);
  841. return ret;
  842. }
  843. static void __exit fujitsu_cleanup(void)
  844. {
  845. acpi_bus_unregister_driver(&acpi_fujitsu_laptop_driver);
  846. kfree(fujitsu_laptop);
  847. platform_driver_unregister(&fujitsu_pf_driver);
  848. acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver);
  849. kfree(fujitsu_bl);
  850. pr_info("driver unloaded\n");
  851. }
  852. module_init(fujitsu_init);
  853. module_exit(fujitsu_cleanup);
  854. module_param(use_alt_lcd_levels, int, 0644);
  855. MODULE_PARM_DESC(use_alt_lcd_levels, "Interface used for setting LCD brightness level (-1 = auto, 0 = force SBLL, 1 = force SBL2)");
  856. module_param(disable_brightness_adjust, bool, 0644);
  857. MODULE_PARM_DESC(disable_brightness_adjust, "Disable LCD brightness adjustment");
  858. #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
  859. module_param_named(debug, dbg_level, uint, 0644);
  860. MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
  861. #endif
  862. MODULE_AUTHOR("Jonathan Woithe, Peter Gruber, Tony Vroon");
  863. MODULE_DESCRIPTION("Fujitsu laptop extras support");
  864. MODULE_VERSION(FUJITSU_DRIVER_VERSION);
  865. MODULE_LICENSE("GPL");