lg-laptop.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * lg-laptop.c - LG Gram ACPI features and hotkeys Driver
  4. *
  5. * Copyright (C) 2018 Matan Ziv-Av <[email protected]>
  6. */
  7. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  8. #include <linux/acpi.h>
  9. #include <linux/dmi.h>
  10. #include <linux/input.h>
  11. #include <linux/input/sparse-keymap.h>
  12. #include <linux/kernel.h>
  13. #include <linux/leds.h>
  14. #include <linux/module.h>
  15. #include <linux/platform_device.h>
  16. #include <linux/types.h>
  17. #include <acpi/battery.h>
  18. #define LED_DEVICE(_name, max, flag) struct led_classdev _name = { \
  19. .name = __stringify(_name), \
  20. .max_brightness = max, \
  21. .brightness_set = _name##_set, \
  22. .brightness_get = _name##_get, \
  23. .flags = flag, \
  24. }
  25. MODULE_AUTHOR("Matan Ziv-Av");
  26. MODULE_DESCRIPTION("LG WMI Hotkey Driver");
  27. MODULE_LICENSE("GPL");
  28. #define WMI_EVENT_GUID0 "E4FB94F9-7F2B-4173-AD1A-CD1D95086248"
  29. #define WMI_EVENT_GUID1 "023B133E-49D1-4E10-B313-698220140DC2"
  30. #define WMI_EVENT_GUID2 "37BE1AC0-C3F2-4B1F-BFBE-8FDEAF2814D6"
  31. #define WMI_EVENT_GUID3 "911BAD44-7DF8-4FBB-9319-BABA1C4B293B"
  32. #define WMI_METHOD_WMAB "C3A72B38-D3EF-42D3-8CBB-D5A57049F66D"
  33. #define WMI_METHOD_WMBB "2B4F501A-BD3C-4394-8DCF-00A7D2BC8210"
  34. #define WMI_EVENT_GUID WMI_EVENT_GUID0
  35. #define WMAB_METHOD "\\XINI.WMAB"
  36. #define WMBB_METHOD "\\XINI.WMBB"
  37. #define SB_GGOV_METHOD "\\_SB.GGOV"
  38. #define GOV_TLED 0x2020008
  39. #define WM_GET 1
  40. #define WM_SET 2
  41. #define WM_KEY_LIGHT 0x400
  42. #define WM_TLED 0x404
  43. #define WM_FN_LOCK 0x407
  44. #define WM_BATT_LIMIT 0x61
  45. #define WM_READER_MODE 0xBF
  46. #define WM_FAN_MODE 0x33
  47. #define WMBB_USB_CHARGE 0x10B
  48. #define WMBB_BATT_LIMIT 0x10C
  49. #define PLATFORM_NAME "lg-laptop"
  50. MODULE_ALIAS("wmi:" WMI_EVENT_GUID0);
  51. MODULE_ALIAS("wmi:" WMI_EVENT_GUID1);
  52. MODULE_ALIAS("wmi:" WMI_EVENT_GUID2);
  53. MODULE_ALIAS("wmi:" WMI_EVENT_GUID3);
  54. MODULE_ALIAS("wmi:" WMI_METHOD_WMAB);
  55. MODULE_ALIAS("wmi:" WMI_METHOD_WMBB);
  56. static struct platform_device *pf_device;
  57. static struct input_dev *wmi_input_dev;
  58. static u32 inited;
  59. #define INIT_INPUT_WMI_0 0x01
  60. #define INIT_INPUT_WMI_2 0x02
  61. #define INIT_INPUT_ACPI 0x04
  62. #define INIT_SPARSE_KEYMAP 0x80
  63. static int battery_limit_use_wmbb;
  64. static struct led_classdev kbd_backlight;
  65. static enum led_brightness get_kbd_backlight_level(void);
  66. static const struct key_entry wmi_keymap[] = {
  67. {KE_KEY, 0x70, {KEY_F15} }, /* LG control panel (F1) */
  68. {KE_KEY, 0x74, {KEY_F21} }, /* Touchpad toggle (F5) */
  69. {KE_KEY, 0xf020000, {KEY_F14} }, /* Read mode (F9) */
  70. {KE_KEY, 0x10000000, {KEY_F16} },/* Keyboard backlight (F8) - pressing
  71. * this key both sends an event and
  72. * changes backlight level.
  73. */
  74. {KE_KEY, 0x80, {KEY_RFKILL} },
  75. {KE_END, 0}
  76. };
  77. static int ggov(u32 arg0)
  78. {
  79. union acpi_object args[1];
  80. union acpi_object *r;
  81. acpi_status status;
  82. acpi_handle handle;
  83. struct acpi_object_list arg;
  84. struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
  85. int res;
  86. args[0].type = ACPI_TYPE_INTEGER;
  87. args[0].integer.value = arg0;
  88. status = acpi_get_handle(NULL, (acpi_string) SB_GGOV_METHOD, &handle);
  89. if (ACPI_FAILURE(status)) {
  90. pr_err("Cannot get handle");
  91. return -ENODEV;
  92. }
  93. arg.count = 1;
  94. arg.pointer = args;
  95. status = acpi_evaluate_object(handle, NULL, &arg, &buffer);
  96. if (ACPI_FAILURE(status)) {
  97. acpi_handle_err(handle, "GGOV: call failed.\n");
  98. return -EINVAL;
  99. }
  100. r = buffer.pointer;
  101. if (r->type != ACPI_TYPE_INTEGER) {
  102. kfree(r);
  103. return -EINVAL;
  104. }
  105. res = r->integer.value;
  106. kfree(r);
  107. return res;
  108. }
  109. static union acpi_object *lg_wmab(u32 method, u32 arg1, u32 arg2)
  110. {
  111. union acpi_object args[3];
  112. acpi_status status;
  113. acpi_handle handle;
  114. struct acpi_object_list arg;
  115. struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
  116. args[0].type = ACPI_TYPE_INTEGER;
  117. args[0].integer.value = method;
  118. args[1].type = ACPI_TYPE_INTEGER;
  119. args[1].integer.value = arg1;
  120. args[2].type = ACPI_TYPE_INTEGER;
  121. args[2].integer.value = arg2;
  122. status = acpi_get_handle(NULL, (acpi_string) WMAB_METHOD, &handle);
  123. if (ACPI_FAILURE(status)) {
  124. pr_err("Cannot get handle");
  125. return NULL;
  126. }
  127. arg.count = 3;
  128. arg.pointer = args;
  129. status = acpi_evaluate_object(handle, NULL, &arg, &buffer);
  130. if (ACPI_FAILURE(status)) {
  131. acpi_handle_err(handle, "WMAB: call failed.\n");
  132. return NULL;
  133. }
  134. return buffer.pointer;
  135. }
  136. static union acpi_object *lg_wmbb(u32 method_id, u32 arg1, u32 arg2)
  137. {
  138. union acpi_object args[3];
  139. acpi_status status;
  140. acpi_handle handle;
  141. struct acpi_object_list arg;
  142. struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
  143. u8 buf[32];
  144. *(u32 *)buf = method_id;
  145. *(u32 *)(buf + 4) = arg1;
  146. *(u32 *)(buf + 16) = arg2;
  147. args[0].type = ACPI_TYPE_INTEGER;
  148. args[0].integer.value = 0; /* ignored */
  149. args[1].type = ACPI_TYPE_INTEGER;
  150. args[1].integer.value = 1; /* Must be 1 or 2. Does not matter which */
  151. args[2].type = ACPI_TYPE_BUFFER;
  152. args[2].buffer.length = 32;
  153. args[2].buffer.pointer = buf;
  154. status = acpi_get_handle(NULL, (acpi_string)WMBB_METHOD, &handle);
  155. if (ACPI_FAILURE(status)) {
  156. pr_err("Cannot get handle");
  157. return NULL;
  158. }
  159. arg.count = 3;
  160. arg.pointer = args;
  161. status = acpi_evaluate_object(handle, NULL, &arg, &buffer);
  162. if (ACPI_FAILURE(status)) {
  163. acpi_handle_err(handle, "WMAB: call failed.\n");
  164. return NULL;
  165. }
  166. return (union acpi_object *)buffer.pointer;
  167. }
  168. static void wmi_notify(u32 value, void *context)
  169. {
  170. struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
  171. union acpi_object *obj;
  172. acpi_status status;
  173. long data = (long)context;
  174. pr_debug("event guid %li\n", data);
  175. status = wmi_get_event_data(value, &response);
  176. if (ACPI_FAILURE(status)) {
  177. pr_err("Bad event status 0x%x\n", status);
  178. return;
  179. }
  180. obj = (union acpi_object *)response.pointer;
  181. if (!obj)
  182. return;
  183. if (obj->type == ACPI_TYPE_INTEGER) {
  184. int eventcode = obj->integer.value;
  185. struct key_entry *key;
  186. if (eventcode == 0x10000000) {
  187. led_classdev_notify_brightness_hw_changed(
  188. &kbd_backlight, get_kbd_backlight_level());
  189. } else {
  190. key = sparse_keymap_entry_from_scancode(
  191. wmi_input_dev, eventcode);
  192. if (key && key->type == KE_KEY)
  193. sparse_keymap_report_entry(wmi_input_dev,
  194. key, 1, true);
  195. }
  196. }
  197. pr_debug("Type: %i Eventcode: 0x%llx\n", obj->type,
  198. obj->integer.value);
  199. kfree(response.pointer);
  200. }
  201. static void wmi_input_setup(void)
  202. {
  203. acpi_status status;
  204. wmi_input_dev = input_allocate_device();
  205. if (wmi_input_dev) {
  206. wmi_input_dev->name = "LG WMI hotkeys";
  207. wmi_input_dev->phys = "wmi/input0";
  208. wmi_input_dev->id.bustype = BUS_HOST;
  209. if (sparse_keymap_setup(wmi_input_dev, wmi_keymap, NULL) ||
  210. input_register_device(wmi_input_dev)) {
  211. pr_info("Cannot initialize input device");
  212. input_free_device(wmi_input_dev);
  213. return;
  214. }
  215. inited |= INIT_SPARSE_KEYMAP;
  216. status = wmi_install_notify_handler(WMI_EVENT_GUID0, wmi_notify,
  217. (void *)0);
  218. if (ACPI_SUCCESS(status))
  219. inited |= INIT_INPUT_WMI_0;
  220. status = wmi_install_notify_handler(WMI_EVENT_GUID2, wmi_notify,
  221. (void *)2);
  222. if (ACPI_SUCCESS(status))
  223. inited |= INIT_INPUT_WMI_2;
  224. } else {
  225. pr_info("Cannot allocate input device");
  226. }
  227. }
  228. static void acpi_notify(struct acpi_device *device, u32 event)
  229. {
  230. struct key_entry *key;
  231. acpi_handle_debug(device->handle, "notify: %d\n", event);
  232. if (inited & INIT_SPARSE_KEYMAP) {
  233. key = sparse_keymap_entry_from_scancode(wmi_input_dev, 0x80);
  234. if (key && key->type == KE_KEY)
  235. sparse_keymap_report_entry(wmi_input_dev, key, 1, true);
  236. }
  237. }
  238. static ssize_t fan_mode_store(struct device *dev,
  239. struct device_attribute *attr,
  240. const char *buffer, size_t count)
  241. {
  242. bool value;
  243. union acpi_object *r;
  244. u32 m;
  245. int ret;
  246. ret = kstrtobool(buffer, &value);
  247. if (ret)
  248. return ret;
  249. r = lg_wmab(WM_FAN_MODE, WM_GET, 0);
  250. if (!r)
  251. return -EIO;
  252. if (r->type != ACPI_TYPE_INTEGER) {
  253. kfree(r);
  254. return -EIO;
  255. }
  256. m = r->integer.value;
  257. kfree(r);
  258. r = lg_wmab(WM_FAN_MODE, WM_SET, (m & 0xffffff0f) | (value << 4));
  259. kfree(r);
  260. r = lg_wmab(WM_FAN_MODE, WM_SET, (m & 0xfffffff0) | value);
  261. kfree(r);
  262. return count;
  263. }
  264. static ssize_t fan_mode_show(struct device *dev,
  265. struct device_attribute *attr, char *buffer)
  266. {
  267. unsigned int status;
  268. union acpi_object *r;
  269. r = lg_wmab(WM_FAN_MODE, WM_GET, 0);
  270. if (!r)
  271. return -EIO;
  272. if (r->type != ACPI_TYPE_INTEGER) {
  273. kfree(r);
  274. return -EIO;
  275. }
  276. status = r->integer.value & 0x01;
  277. kfree(r);
  278. return sysfs_emit(buffer, "%d\n", status);
  279. }
  280. static ssize_t usb_charge_store(struct device *dev,
  281. struct device_attribute *attr,
  282. const char *buffer, size_t count)
  283. {
  284. bool value;
  285. union acpi_object *r;
  286. int ret;
  287. ret = kstrtobool(buffer, &value);
  288. if (ret)
  289. return ret;
  290. r = lg_wmbb(WMBB_USB_CHARGE, WM_SET, value);
  291. if (!r)
  292. return -EIO;
  293. kfree(r);
  294. return count;
  295. }
  296. static ssize_t usb_charge_show(struct device *dev,
  297. struct device_attribute *attr, char *buffer)
  298. {
  299. unsigned int status;
  300. union acpi_object *r;
  301. r = lg_wmbb(WMBB_USB_CHARGE, WM_GET, 0);
  302. if (!r)
  303. return -EIO;
  304. if (r->type != ACPI_TYPE_BUFFER) {
  305. kfree(r);
  306. return -EIO;
  307. }
  308. status = !!r->buffer.pointer[0x10];
  309. kfree(r);
  310. return sysfs_emit(buffer, "%d\n", status);
  311. }
  312. static ssize_t reader_mode_store(struct device *dev,
  313. struct device_attribute *attr,
  314. const char *buffer, size_t count)
  315. {
  316. bool value;
  317. union acpi_object *r;
  318. int ret;
  319. ret = kstrtobool(buffer, &value);
  320. if (ret)
  321. return ret;
  322. r = lg_wmab(WM_READER_MODE, WM_SET, value);
  323. if (!r)
  324. return -EIO;
  325. kfree(r);
  326. return count;
  327. }
  328. static ssize_t reader_mode_show(struct device *dev,
  329. struct device_attribute *attr, char *buffer)
  330. {
  331. unsigned int status;
  332. union acpi_object *r;
  333. r = lg_wmab(WM_READER_MODE, WM_GET, 0);
  334. if (!r)
  335. return -EIO;
  336. if (r->type != ACPI_TYPE_INTEGER) {
  337. kfree(r);
  338. return -EIO;
  339. }
  340. status = !!r->integer.value;
  341. kfree(r);
  342. return sysfs_emit(buffer, "%d\n", status);
  343. }
  344. static ssize_t fn_lock_store(struct device *dev,
  345. struct device_attribute *attr,
  346. const char *buffer, size_t count)
  347. {
  348. bool value;
  349. union acpi_object *r;
  350. int ret;
  351. ret = kstrtobool(buffer, &value);
  352. if (ret)
  353. return ret;
  354. r = lg_wmab(WM_FN_LOCK, WM_SET, value);
  355. if (!r)
  356. return -EIO;
  357. kfree(r);
  358. return count;
  359. }
  360. static ssize_t fn_lock_show(struct device *dev,
  361. struct device_attribute *attr, char *buffer)
  362. {
  363. unsigned int status;
  364. union acpi_object *r;
  365. r = lg_wmab(WM_FN_LOCK, WM_GET, 0);
  366. if (!r)
  367. return -EIO;
  368. if (r->type != ACPI_TYPE_BUFFER) {
  369. kfree(r);
  370. return -EIO;
  371. }
  372. status = !!r->buffer.pointer[0];
  373. kfree(r);
  374. return sysfs_emit(buffer, "%d\n", status);
  375. }
  376. static ssize_t charge_control_end_threshold_store(struct device *dev,
  377. struct device_attribute *attr,
  378. const char *buf, size_t count)
  379. {
  380. unsigned long value;
  381. int ret;
  382. ret = kstrtoul(buf, 10, &value);
  383. if (ret)
  384. return ret;
  385. if (value == 100 || value == 80) {
  386. union acpi_object *r;
  387. if (battery_limit_use_wmbb)
  388. r = lg_wmbb(WMBB_BATT_LIMIT, WM_SET, value);
  389. else
  390. r = lg_wmab(WM_BATT_LIMIT, WM_SET, value);
  391. if (!r)
  392. return -EIO;
  393. kfree(r);
  394. return count;
  395. }
  396. return -EINVAL;
  397. }
  398. static ssize_t charge_control_end_threshold_show(struct device *device,
  399. struct device_attribute *attr,
  400. char *buf)
  401. {
  402. unsigned int status;
  403. union acpi_object *r;
  404. if (battery_limit_use_wmbb) {
  405. r = lg_wmbb(WMBB_BATT_LIMIT, WM_GET, 0);
  406. if (!r)
  407. return -EIO;
  408. if (r->type != ACPI_TYPE_BUFFER) {
  409. kfree(r);
  410. return -EIO;
  411. }
  412. status = r->buffer.pointer[0x10];
  413. } else {
  414. r = lg_wmab(WM_BATT_LIMIT, WM_GET, 0);
  415. if (!r)
  416. return -EIO;
  417. if (r->type != ACPI_TYPE_INTEGER) {
  418. kfree(r);
  419. return -EIO;
  420. }
  421. status = r->integer.value;
  422. }
  423. kfree(r);
  424. if (status != 80 && status != 100)
  425. status = 0;
  426. return sysfs_emit(buf, "%d\n", status);
  427. }
  428. static ssize_t battery_care_limit_show(struct device *dev,
  429. struct device_attribute *attr,
  430. char *buffer)
  431. {
  432. return charge_control_end_threshold_show(dev, attr, buffer);
  433. }
  434. static ssize_t battery_care_limit_store(struct device *dev,
  435. struct device_attribute *attr,
  436. const char *buffer, size_t count)
  437. {
  438. return charge_control_end_threshold_store(dev, attr, buffer, count);
  439. }
  440. static DEVICE_ATTR_RW(fan_mode);
  441. static DEVICE_ATTR_RW(usb_charge);
  442. static DEVICE_ATTR_RW(reader_mode);
  443. static DEVICE_ATTR_RW(fn_lock);
  444. static DEVICE_ATTR_RW(charge_control_end_threshold);
  445. static DEVICE_ATTR_RW(battery_care_limit);
  446. static int lg_battery_add(struct power_supply *battery)
  447. {
  448. if (device_create_file(&battery->dev,
  449. &dev_attr_charge_control_end_threshold))
  450. return -ENODEV;
  451. return 0;
  452. }
  453. static int lg_battery_remove(struct power_supply *battery)
  454. {
  455. device_remove_file(&battery->dev,
  456. &dev_attr_charge_control_end_threshold);
  457. return 0;
  458. }
  459. static struct acpi_battery_hook battery_hook = {
  460. .add_battery = lg_battery_add,
  461. .remove_battery = lg_battery_remove,
  462. .name = "LG Battery Extension",
  463. };
  464. static struct attribute *dev_attributes[] = {
  465. &dev_attr_fan_mode.attr,
  466. &dev_attr_usb_charge.attr,
  467. &dev_attr_reader_mode.attr,
  468. &dev_attr_fn_lock.attr,
  469. &dev_attr_battery_care_limit.attr,
  470. NULL
  471. };
  472. static const struct attribute_group dev_attribute_group = {
  473. .attrs = dev_attributes,
  474. };
  475. static void tpad_led_set(struct led_classdev *cdev,
  476. enum led_brightness brightness)
  477. {
  478. union acpi_object *r;
  479. r = lg_wmab(WM_TLED, WM_SET, brightness > LED_OFF);
  480. kfree(r);
  481. }
  482. static enum led_brightness tpad_led_get(struct led_classdev *cdev)
  483. {
  484. return ggov(GOV_TLED) > 0 ? LED_ON : LED_OFF;
  485. }
  486. static LED_DEVICE(tpad_led, 1, 0);
  487. static void kbd_backlight_set(struct led_classdev *cdev,
  488. enum led_brightness brightness)
  489. {
  490. u32 val;
  491. union acpi_object *r;
  492. val = 0x22;
  493. if (brightness <= LED_OFF)
  494. val = 0;
  495. if (brightness >= LED_FULL)
  496. val = 0x24;
  497. r = lg_wmab(WM_KEY_LIGHT, WM_SET, val);
  498. kfree(r);
  499. }
  500. static enum led_brightness get_kbd_backlight_level(void)
  501. {
  502. union acpi_object *r;
  503. int val;
  504. r = lg_wmab(WM_KEY_LIGHT, WM_GET, 0);
  505. if (!r)
  506. return LED_OFF;
  507. if (r->type != ACPI_TYPE_BUFFER || r->buffer.pointer[1] != 0x05) {
  508. kfree(r);
  509. return LED_OFF;
  510. }
  511. switch (r->buffer.pointer[0] & 0x27) {
  512. case 0x24:
  513. val = LED_FULL;
  514. break;
  515. case 0x22:
  516. val = LED_HALF;
  517. break;
  518. default:
  519. val = LED_OFF;
  520. }
  521. kfree(r);
  522. return val;
  523. }
  524. static enum led_brightness kbd_backlight_get(struct led_classdev *cdev)
  525. {
  526. return get_kbd_backlight_level();
  527. }
  528. static LED_DEVICE(kbd_backlight, 255, LED_BRIGHT_HW_CHANGED);
  529. static void wmi_input_destroy(void)
  530. {
  531. if (inited & INIT_INPUT_WMI_2)
  532. wmi_remove_notify_handler(WMI_EVENT_GUID2);
  533. if (inited & INIT_INPUT_WMI_0)
  534. wmi_remove_notify_handler(WMI_EVENT_GUID0);
  535. if (inited & INIT_SPARSE_KEYMAP)
  536. input_unregister_device(wmi_input_dev);
  537. inited &= ~(INIT_INPUT_WMI_0 | INIT_INPUT_WMI_2 | INIT_SPARSE_KEYMAP);
  538. }
  539. static struct platform_driver pf_driver = {
  540. .driver = {
  541. .name = PLATFORM_NAME,
  542. }
  543. };
  544. static int acpi_add(struct acpi_device *device)
  545. {
  546. int ret;
  547. const char *product;
  548. int year = 2017;
  549. if (pf_device)
  550. return 0;
  551. ret = platform_driver_register(&pf_driver);
  552. if (ret)
  553. return ret;
  554. pf_device = platform_device_register_simple(PLATFORM_NAME,
  555. PLATFORM_DEVID_NONE,
  556. NULL, 0);
  557. if (IS_ERR(pf_device)) {
  558. ret = PTR_ERR(pf_device);
  559. pf_device = NULL;
  560. pr_err("unable to register platform device\n");
  561. goto out_platform_registered;
  562. }
  563. product = dmi_get_system_info(DMI_PRODUCT_NAME);
  564. if (product && strlen(product) > 4)
  565. switch (product[4]) {
  566. case '5':
  567. if (strlen(product) > 5)
  568. switch (product[5]) {
  569. case 'N':
  570. year = 2021;
  571. break;
  572. case '0':
  573. year = 2016;
  574. break;
  575. default:
  576. year = 2022;
  577. }
  578. break;
  579. case '6':
  580. year = 2016;
  581. break;
  582. case '7':
  583. year = 2017;
  584. break;
  585. case '8':
  586. year = 2018;
  587. break;
  588. case '9':
  589. year = 2019;
  590. break;
  591. case '0':
  592. if (strlen(product) > 5)
  593. switch (product[5]) {
  594. case 'N':
  595. year = 2020;
  596. break;
  597. case 'P':
  598. year = 2021;
  599. break;
  600. default:
  601. year = 2022;
  602. }
  603. break;
  604. default:
  605. year = 2019;
  606. }
  607. pr_info("product: %s year: %d\n", product, year);
  608. if (year >= 2019)
  609. battery_limit_use_wmbb = 1;
  610. ret = sysfs_create_group(&pf_device->dev.kobj, &dev_attribute_group);
  611. if (ret)
  612. goto out_platform_device;
  613. /* LEDs are optional */
  614. led_classdev_register(&pf_device->dev, &kbd_backlight);
  615. led_classdev_register(&pf_device->dev, &tpad_led);
  616. wmi_input_setup();
  617. battery_hook_register(&battery_hook);
  618. return 0;
  619. out_platform_device:
  620. platform_device_unregister(pf_device);
  621. out_platform_registered:
  622. platform_driver_unregister(&pf_driver);
  623. return ret;
  624. }
  625. static int acpi_remove(struct acpi_device *device)
  626. {
  627. sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group);
  628. led_classdev_unregister(&tpad_led);
  629. led_classdev_unregister(&kbd_backlight);
  630. battery_hook_unregister(&battery_hook);
  631. wmi_input_destroy();
  632. platform_device_unregister(pf_device);
  633. pf_device = NULL;
  634. platform_driver_unregister(&pf_driver);
  635. return 0;
  636. }
  637. static const struct acpi_device_id device_ids[] = {
  638. {"LGEX0815", 0},
  639. {"", 0}
  640. };
  641. MODULE_DEVICE_TABLE(acpi, device_ids);
  642. static struct acpi_driver acpi_driver = {
  643. .name = "LG Gram Laptop Support",
  644. .class = "lg-laptop",
  645. .ids = device_ids,
  646. .ops = {
  647. .add = acpi_add,
  648. .remove = acpi_remove,
  649. .notify = acpi_notify,
  650. },
  651. .owner = THIS_MODULE,
  652. };
  653. static int __init acpi_init(void)
  654. {
  655. int result;
  656. result = acpi_bus_register_driver(&acpi_driver);
  657. if (result < 0) {
  658. pr_debug("Error registering driver\n");
  659. return -ENODEV;
  660. }
  661. return 0;
  662. }
  663. static void __exit acpi_exit(void)
  664. {
  665. acpi_bus_unregister_driver(&acpi_driver);
  666. }
  667. module_init(acpi_init);
  668. module_exit(acpi_exit);