Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * lg-laptop.c - LG Gram ACPI features and hotkeys Driver
0004  *
0005  * Copyright (C) 2018 Matan Ziv-Av <matan@svgalib.org>
0006  */
0007 
0008 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0009 
0010 #include <linux/acpi.h>
0011 #include <linux/dmi.h>
0012 #include <linux/input.h>
0013 #include <linux/input/sparse-keymap.h>
0014 #include <linux/kernel.h>
0015 #include <linux/leds.h>
0016 #include <linux/module.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/types.h>
0019 
0020 #include <acpi/battery.h>
0021 
0022 #define LED_DEVICE(_name, max, flag) struct led_classdev _name = { \
0023     .name           = __stringify(_name),   \
0024     .max_brightness = max,                  \
0025     .brightness_set = _name##_set,          \
0026     .brightness_get = _name##_get,          \
0027     .flags = flag,                          \
0028 }
0029 
0030 MODULE_AUTHOR("Matan Ziv-Av");
0031 MODULE_DESCRIPTION("LG WMI Hotkey Driver");
0032 MODULE_LICENSE("GPL");
0033 
0034 #define WMI_EVENT_GUID0 "E4FB94F9-7F2B-4173-AD1A-CD1D95086248"
0035 #define WMI_EVENT_GUID1 "023B133E-49D1-4E10-B313-698220140DC2"
0036 #define WMI_EVENT_GUID2 "37BE1AC0-C3F2-4B1F-BFBE-8FDEAF2814D6"
0037 #define WMI_EVENT_GUID3 "911BAD44-7DF8-4FBB-9319-BABA1C4B293B"
0038 #define WMI_METHOD_WMAB "C3A72B38-D3EF-42D3-8CBB-D5A57049F66D"
0039 #define WMI_METHOD_WMBB "2B4F501A-BD3C-4394-8DCF-00A7D2BC8210"
0040 #define WMI_EVENT_GUID  WMI_EVENT_GUID0
0041 
0042 #define WMAB_METHOD     "\\XINI.WMAB"
0043 #define WMBB_METHOD     "\\XINI.WMBB"
0044 #define SB_GGOV_METHOD  "\\_SB.GGOV"
0045 #define GOV_TLED        0x2020008
0046 #define WM_GET          1
0047 #define WM_SET          2
0048 #define WM_KEY_LIGHT    0x400
0049 #define WM_TLED         0x404
0050 #define WM_FN_LOCK      0x407
0051 #define WM_BATT_LIMIT   0x61
0052 #define WM_READER_MODE  0xBF
0053 #define WM_FAN_MODE 0x33
0054 #define WMBB_USB_CHARGE 0x10B
0055 #define WMBB_BATT_LIMIT 0x10C
0056 
0057 #define PLATFORM_NAME   "lg-laptop"
0058 
0059 MODULE_ALIAS("wmi:" WMI_EVENT_GUID0);
0060 MODULE_ALIAS("wmi:" WMI_EVENT_GUID1);
0061 MODULE_ALIAS("wmi:" WMI_EVENT_GUID2);
0062 MODULE_ALIAS("wmi:" WMI_EVENT_GUID3);
0063 MODULE_ALIAS("wmi:" WMI_METHOD_WMAB);
0064 MODULE_ALIAS("wmi:" WMI_METHOD_WMBB);
0065 
0066 static struct platform_device *pf_device;
0067 static struct input_dev *wmi_input_dev;
0068 
0069 static u32 inited;
0070 #define INIT_INPUT_WMI_0        0x01
0071 #define INIT_INPUT_WMI_2        0x02
0072 #define INIT_INPUT_ACPI         0x04
0073 #define INIT_SPARSE_KEYMAP      0x80
0074 
0075 static int battery_limit_use_wmbb;
0076 static struct led_classdev kbd_backlight;
0077 static enum led_brightness get_kbd_backlight_level(void);
0078 
0079 static const struct key_entry wmi_keymap[] = {
0080     {KE_KEY, 0x70, {KEY_F15} },  /* LG control panel (F1) */
0081     {KE_KEY, 0x74, {KEY_F21} },  /* Touchpad toggle (F5) */
0082     {KE_KEY, 0xf020000, {KEY_F14} }, /* Read mode (F9) */
0083     {KE_KEY, 0x10000000, {KEY_F16} },/* Keyboard backlight (F8) - pressing
0084                       * this key both sends an event and
0085                       * changes backlight level.
0086                       */
0087     {KE_KEY, 0x80, {KEY_RFKILL} },
0088     {KE_END, 0}
0089 };
0090 
0091 static int ggov(u32 arg0)
0092 {
0093     union acpi_object args[1];
0094     union acpi_object *r;
0095     acpi_status status;
0096     acpi_handle handle;
0097     struct acpi_object_list arg;
0098     struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
0099     int res;
0100 
0101     args[0].type = ACPI_TYPE_INTEGER;
0102     args[0].integer.value = arg0;
0103 
0104     status = acpi_get_handle(NULL, (acpi_string) SB_GGOV_METHOD, &handle);
0105     if (ACPI_FAILURE(status)) {
0106         pr_err("Cannot get handle");
0107         return -ENODEV;
0108     }
0109 
0110     arg.count = 1;
0111     arg.pointer = args;
0112 
0113     status = acpi_evaluate_object(handle, NULL, &arg, &buffer);
0114     if (ACPI_FAILURE(status)) {
0115         acpi_handle_err(handle, "GGOV: call failed.\n");
0116         return -EINVAL;
0117     }
0118 
0119     r = buffer.pointer;
0120     if (r->type != ACPI_TYPE_INTEGER) {
0121         kfree(r);
0122         return -EINVAL;
0123     }
0124 
0125     res = r->integer.value;
0126     kfree(r);
0127 
0128     return res;
0129 }
0130 
0131 static union acpi_object *lg_wmab(u32 method, u32 arg1, u32 arg2)
0132 {
0133     union acpi_object args[3];
0134     acpi_status status;
0135     acpi_handle handle;
0136     struct acpi_object_list arg;
0137     struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
0138 
0139     args[0].type = ACPI_TYPE_INTEGER;
0140     args[0].integer.value = method;
0141     args[1].type = ACPI_TYPE_INTEGER;
0142     args[1].integer.value = arg1;
0143     args[2].type = ACPI_TYPE_INTEGER;
0144     args[2].integer.value = arg2;
0145 
0146     status = acpi_get_handle(NULL, (acpi_string) WMAB_METHOD, &handle);
0147     if (ACPI_FAILURE(status)) {
0148         pr_err("Cannot get handle");
0149         return NULL;
0150     }
0151 
0152     arg.count = 3;
0153     arg.pointer = args;
0154 
0155     status = acpi_evaluate_object(handle, NULL, &arg, &buffer);
0156     if (ACPI_FAILURE(status)) {
0157         acpi_handle_err(handle, "WMAB: call failed.\n");
0158         return NULL;
0159     }
0160 
0161     return buffer.pointer;
0162 }
0163 
0164 static union acpi_object *lg_wmbb(u32 method_id, u32 arg1, u32 arg2)
0165 {
0166     union acpi_object args[3];
0167     acpi_status status;
0168     acpi_handle handle;
0169     struct acpi_object_list arg;
0170     struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
0171     u8 buf[32];
0172 
0173     *(u32 *)buf = method_id;
0174     *(u32 *)(buf + 4) = arg1;
0175     *(u32 *)(buf + 16) = arg2;
0176     args[0].type = ACPI_TYPE_INTEGER;
0177     args[0].integer.value = 0; /* ignored */
0178     args[1].type = ACPI_TYPE_INTEGER;
0179     args[1].integer.value = 1; /* Must be 1 or 2. Does not matter which */
0180     args[2].type = ACPI_TYPE_BUFFER;
0181     args[2].buffer.length = 32;
0182     args[2].buffer.pointer = buf;
0183 
0184     status = acpi_get_handle(NULL, (acpi_string)WMBB_METHOD, &handle);
0185     if (ACPI_FAILURE(status)) {
0186         pr_err("Cannot get handle");
0187         return NULL;
0188     }
0189 
0190     arg.count = 3;
0191     arg.pointer = args;
0192 
0193     status = acpi_evaluate_object(handle, NULL, &arg, &buffer);
0194     if (ACPI_FAILURE(status)) {
0195         acpi_handle_err(handle, "WMAB: call failed.\n");
0196         return NULL;
0197     }
0198 
0199     return (union acpi_object *)buffer.pointer;
0200 }
0201 
0202 static void wmi_notify(u32 value, void *context)
0203 {
0204     struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
0205     union acpi_object *obj;
0206     acpi_status status;
0207     long data = (long)context;
0208 
0209     pr_debug("event guid %li\n", data);
0210     status = wmi_get_event_data(value, &response);
0211     if (ACPI_FAILURE(status)) {
0212         pr_err("Bad event status 0x%x\n", status);
0213         return;
0214     }
0215 
0216     obj = (union acpi_object *)response.pointer;
0217     if (!obj)
0218         return;
0219 
0220     if (obj->type == ACPI_TYPE_INTEGER) {
0221         int eventcode = obj->integer.value;
0222         struct key_entry *key;
0223 
0224         if (eventcode == 0x10000000) {
0225             led_classdev_notify_brightness_hw_changed(
0226                 &kbd_backlight, get_kbd_backlight_level());
0227         } else {
0228             key = sparse_keymap_entry_from_scancode(
0229                 wmi_input_dev, eventcode);
0230             if (key && key->type == KE_KEY)
0231                 sparse_keymap_report_entry(wmi_input_dev,
0232                                key, 1, true);
0233         }
0234     }
0235 
0236     pr_debug("Type: %i    Eventcode: 0x%llx\n", obj->type,
0237          obj->integer.value);
0238     kfree(response.pointer);
0239 }
0240 
0241 static void wmi_input_setup(void)
0242 {
0243     acpi_status status;
0244 
0245     wmi_input_dev = input_allocate_device();
0246     if (wmi_input_dev) {
0247         wmi_input_dev->name = "LG WMI hotkeys";
0248         wmi_input_dev->phys = "wmi/input0";
0249         wmi_input_dev->id.bustype = BUS_HOST;
0250 
0251         if (sparse_keymap_setup(wmi_input_dev, wmi_keymap, NULL) ||
0252             input_register_device(wmi_input_dev)) {
0253             pr_info("Cannot initialize input device");
0254             input_free_device(wmi_input_dev);
0255             return;
0256         }
0257 
0258         inited |= INIT_SPARSE_KEYMAP;
0259         status = wmi_install_notify_handler(WMI_EVENT_GUID0, wmi_notify,
0260                             (void *)0);
0261         if (ACPI_SUCCESS(status))
0262             inited |= INIT_INPUT_WMI_0;
0263 
0264         status = wmi_install_notify_handler(WMI_EVENT_GUID2, wmi_notify,
0265                             (void *)2);
0266         if (ACPI_SUCCESS(status))
0267             inited |= INIT_INPUT_WMI_2;
0268     } else {
0269         pr_info("Cannot allocate input device");
0270     }
0271 }
0272 
0273 static void acpi_notify(struct acpi_device *device, u32 event)
0274 {
0275     struct key_entry *key;
0276 
0277     acpi_handle_debug(device->handle, "notify: %d\n", event);
0278     if (inited & INIT_SPARSE_KEYMAP) {
0279         key = sparse_keymap_entry_from_scancode(wmi_input_dev, 0x80);
0280         if (key && key->type == KE_KEY)
0281             sparse_keymap_report_entry(wmi_input_dev, key, 1, true);
0282     }
0283 }
0284 
0285 static ssize_t fan_mode_store(struct device *dev,
0286                   struct device_attribute *attr,
0287                   const char *buffer, size_t count)
0288 {
0289     bool value;
0290     union acpi_object *r;
0291     u32 m;
0292     int ret;
0293 
0294     ret = kstrtobool(buffer, &value);
0295     if (ret)
0296         return ret;
0297 
0298     r = lg_wmab(WM_FAN_MODE, WM_GET, 0);
0299     if (!r)
0300         return -EIO;
0301 
0302     if (r->type != ACPI_TYPE_INTEGER) {
0303         kfree(r);
0304         return -EIO;
0305     }
0306 
0307     m = r->integer.value;
0308     kfree(r);
0309     r = lg_wmab(WM_FAN_MODE, WM_SET, (m & 0xffffff0f) | (value << 4));
0310     kfree(r);
0311     r = lg_wmab(WM_FAN_MODE, WM_SET, (m & 0xfffffff0) | value);
0312     kfree(r);
0313 
0314     return count;
0315 }
0316 
0317 static ssize_t fan_mode_show(struct device *dev,
0318                  struct device_attribute *attr, char *buffer)
0319 {
0320     unsigned int status;
0321     union acpi_object *r;
0322 
0323     r = lg_wmab(WM_FAN_MODE, WM_GET, 0);
0324     if (!r)
0325         return -EIO;
0326 
0327     if (r->type != ACPI_TYPE_INTEGER) {
0328         kfree(r);
0329         return -EIO;
0330     }
0331 
0332     status = r->integer.value & 0x01;
0333     kfree(r);
0334 
0335     return sysfs_emit(buffer, "%d\n", status);
0336 }
0337 
0338 static ssize_t usb_charge_store(struct device *dev,
0339                 struct device_attribute *attr,
0340                 const char *buffer, size_t count)
0341 {
0342     bool value;
0343     union acpi_object *r;
0344     int ret;
0345 
0346     ret = kstrtobool(buffer, &value);
0347     if (ret)
0348         return ret;
0349 
0350     r = lg_wmbb(WMBB_USB_CHARGE, WM_SET, value);
0351     if (!r)
0352         return -EIO;
0353 
0354     kfree(r);
0355     return count;
0356 }
0357 
0358 static ssize_t usb_charge_show(struct device *dev,
0359                    struct device_attribute *attr, char *buffer)
0360 {
0361     unsigned int status;
0362     union acpi_object *r;
0363 
0364     r = lg_wmbb(WMBB_USB_CHARGE, WM_GET, 0);
0365     if (!r)
0366         return -EIO;
0367 
0368     if (r->type != ACPI_TYPE_BUFFER) {
0369         kfree(r);
0370         return -EIO;
0371     }
0372 
0373     status = !!r->buffer.pointer[0x10];
0374 
0375     kfree(r);
0376 
0377     return sysfs_emit(buffer, "%d\n", status);
0378 }
0379 
0380 static ssize_t reader_mode_store(struct device *dev,
0381                  struct device_attribute *attr,
0382                  const char *buffer, size_t count)
0383 {
0384     bool value;
0385     union acpi_object *r;
0386     int ret;
0387 
0388     ret = kstrtobool(buffer, &value);
0389     if (ret)
0390         return ret;
0391 
0392     r = lg_wmab(WM_READER_MODE, WM_SET, value);
0393     if (!r)
0394         return -EIO;
0395 
0396     kfree(r);
0397     return count;
0398 }
0399 
0400 static ssize_t reader_mode_show(struct device *dev,
0401                 struct device_attribute *attr, char *buffer)
0402 {
0403     unsigned int status;
0404     union acpi_object *r;
0405 
0406     r = lg_wmab(WM_READER_MODE, WM_GET, 0);
0407     if (!r)
0408         return -EIO;
0409 
0410     if (r->type != ACPI_TYPE_INTEGER) {
0411         kfree(r);
0412         return -EIO;
0413     }
0414 
0415     status = !!r->integer.value;
0416 
0417     kfree(r);
0418 
0419     return sysfs_emit(buffer, "%d\n", status);
0420 }
0421 
0422 static ssize_t fn_lock_store(struct device *dev,
0423                  struct device_attribute *attr,
0424                  const char *buffer, size_t count)
0425 {
0426     bool value;
0427     union acpi_object *r;
0428     int ret;
0429 
0430     ret = kstrtobool(buffer, &value);
0431     if (ret)
0432         return ret;
0433 
0434     r = lg_wmab(WM_FN_LOCK, WM_SET, value);
0435     if (!r)
0436         return -EIO;
0437 
0438     kfree(r);
0439     return count;
0440 }
0441 
0442 static ssize_t fn_lock_show(struct device *dev,
0443                 struct device_attribute *attr, char *buffer)
0444 {
0445     unsigned int status;
0446     union acpi_object *r;
0447 
0448     r = lg_wmab(WM_FN_LOCK, WM_GET, 0);
0449     if (!r)
0450         return -EIO;
0451 
0452     if (r->type != ACPI_TYPE_BUFFER) {
0453         kfree(r);
0454         return -EIO;
0455     }
0456 
0457     status = !!r->buffer.pointer[0];
0458     kfree(r);
0459 
0460     return sysfs_emit(buffer, "%d\n", status);
0461 }
0462 
0463 static ssize_t charge_control_end_threshold_store(struct device *dev,
0464                           struct device_attribute *attr,
0465                           const char *buf, size_t count)
0466 {
0467     unsigned long value;
0468     int ret;
0469 
0470     ret = kstrtoul(buf, 10, &value);
0471     if (ret)
0472         return ret;
0473 
0474     if (value == 100 || value == 80) {
0475         union acpi_object *r;
0476 
0477         if (battery_limit_use_wmbb)
0478             r = lg_wmbb(WMBB_BATT_LIMIT, WM_SET, value);
0479         else
0480             r = lg_wmab(WM_BATT_LIMIT, WM_SET, value);
0481         if (!r)
0482             return -EIO;
0483 
0484         kfree(r);
0485         return count;
0486     }
0487 
0488     return -EINVAL;
0489 }
0490 
0491 static ssize_t charge_control_end_threshold_show(struct device *device,
0492                          struct device_attribute *attr,
0493                          char *buf)
0494 {
0495     unsigned int status;
0496     union acpi_object *r;
0497 
0498     if (battery_limit_use_wmbb) {
0499         r = lg_wmbb(WMBB_BATT_LIMIT, WM_GET, 0);
0500         if (!r)
0501             return -EIO;
0502 
0503         if (r->type != ACPI_TYPE_BUFFER) {
0504             kfree(r);
0505             return -EIO;
0506         }
0507 
0508         status = r->buffer.pointer[0x10];
0509     } else {
0510         r = lg_wmab(WM_BATT_LIMIT, WM_GET, 0);
0511         if (!r)
0512             return -EIO;
0513 
0514         if (r->type != ACPI_TYPE_INTEGER) {
0515             kfree(r);
0516             return -EIO;
0517         }
0518 
0519         status = r->integer.value;
0520     }
0521     kfree(r);
0522     if (status != 80 && status != 100)
0523         status = 0;
0524 
0525     return sysfs_emit(buf, "%d\n", status);
0526 }
0527 
0528 static ssize_t battery_care_limit_show(struct device *dev,
0529                        struct device_attribute *attr,
0530                        char *buffer)
0531 {
0532     return charge_control_end_threshold_show(dev, attr, buffer);
0533 }
0534 
0535 static ssize_t battery_care_limit_store(struct device *dev,
0536                     struct device_attribute *attr,
0537                     const char *buffer, size_t count)
0538 {
0539     return charge_control_end_threshold_store(dev, attr, buffer, count);
0540 }
0541 
0542 static DEVICE_ATTR_RW(fan_mode);
0543 static DEVICE_ATTR_RW(usb_charge);
0544 static DEVICE_ATTR_RW(reader_mode);
0545 static DEVICE_ATTR_RW(fn_lock);
0546 static DEVICE_ATTR_RW(charge_control_end_threshold);
0547 static DEVICE_ATTR_RW(battery_care_limit);
0548 
0549 static int lg_battery_add(struct power_supply *battery)
0550 {
0551     if (device_create_file(&battery->dev,
0552                    &dev_attr_charge_control_end_threshold))
0553         return -ENODEV;
0554 
0555     return 0;
0556 }
0557 
0558 static int lg_battery_remove(struct power_supply *battery)
0559 {
0560     device_remove_file(&battery->dev,
0561                &dev_attr_charge_control_end_threshold);
0562     return 0;
0563 }
0564 
0565 static struct acpi_battery_hook battery_hook = {
0566     .add_battery = lg_battery_add,
0567     .remove_battery = lg_battery_remove,
0568     .name = "LG Battery Extension",
0569 };
0570 
0571 static struct attribute *dev_attributes[] = {
0572     &dev_attr_fan_mode.attr,
0573     &dev_attr_usb_charge.attr,
0574     &dev_attr_reader_mode.attr,
0575     &dev_attr_fn_lock.attr,
0576     &dev_attr_battery_care_limit.attr,
0577     NULL
0578 };
0579 
0580 static const struct attribute_group dev_attribute_group = {
0581     .attrs = dev_attributes,
0582 };
0583 
0584 static void tpad_led_set(struct led_classdev *cdev,
0585              enum led_brightness brightness)
0586 {
0587     union acpi_object *r;
0588 
0589     r = lg_wmab(WM_TLED, WM_SET, brightness > LED_OFF);
0590     kfree(r);
0591 }
0592 
0593 static enum led_brightness tpad_led_get(struct led_classdev *cdev)
0594 {
0595     return ggov(GOV_TLED) > 0 ? LED_ON : LED_OFF;
0596 }
0597 
0598 static LED_DEVICE(tpad_led, 1, 0);
0599 
0600 static void kbd_backlight_set(struct led_classdev *cdev,
0601                   enum led_brightness brightness)
0602 {
0603     u32 val;
0604     union acpi_object *r;
0605 
0606     val = 0x22;
0607     if (brightness <= LED_OFF)
0608         val = 0;
0609     if (brightness >= LED_FULL)
0610         val = 0x24;
0611     r = lg_wmab(WM_KEY_LIGHT, WM_SET, val);
0612     kfree(r);
0613 }
0614 
0615 static enum led_brightness get_kbd_backlight_level(void)
0616 {
0617     union acpi_object *r;
0618     int val;
0619 
0620     r = lg_wmab(WM_KEY_LIGHT, WM_GET, 0);
0621 
0622     if (!r)
0623         return LED_OFF;
0624 
0625     if (r->type != ACPI_TYPE_BUFFER || r->buffer.pointer[1] != 0x05) {
0626         kfree(r);
0627         return LED_OFF;
0628     }
0629 
0630     switch (r->buffer.pointer[0] & 0x27) {
0631     case 0x24:
0632         val = LED_FULL;
0633         break;
0634     case 0x22:
0635         val = LED_HALF;
0636         break;
0637     default:
0638         val = LED_OFF;
0639     }
0640 
0641     kfree(r);
0642 
0643     return val;
0644 }
0645 
0646 static enum led_brightness kbd_backlight_get(struct led_classdev *cdev)
0647 {
0648     return get_kbd_backlight_level();
0649 }
0650 
0651 static LED_DEVICE(kbd_backlight, 255, LED_BRIGHT_HW_CHANGED);
0652 
0653 static void wmi_input_destroy(void)
0654 {
0655     if (inited & INIT_INPUT_WMI_2)
0656         wmi_remove_notify_handler(WMI_EVENT_GUID2);
0657 
0658     if (inited & INIT_INPUT_WMI_0)
0659         wmi_remove_notify_handler(WMI_EVENT_GUID0);
0660 
0661     if (inited & INIT_SPARSE_KEYMAP)
0662         input_unregister_device(wmi_input_dev);
0663 
0664     inited &= ~(INIT_INPUT_WMI_0 | INIT_INPUT_WMI_2 | INIT_SPARSE_KEYMAP);
0665 }
0666 
0667 static struct platform_driver pf_driver = {
0668     .driver = {
0669            .name = PLATFORM_NAME,
0670     }
0671 };
0672 
0673 static int acpi_add(struct acpi_device *device)
0674 {
0675     int ret;
0676     const char *product;
0677     int year = 2017;
0678 
0679     if (pf_device)
0680         return 0;
0681 
0682     ret = platform_driver_register(&pf_driver);
0683     if (ret)
0684         return ret;
0685 
0686     pf_device = platform_device_register_simple(PLATFORM_NAME,
0687                             PLATFORM_DEVID_NONE,
0688                             NULL, 0);
0689     if (IS_ERR(pf_device)) {
0690         ret = PTR_ERR(pf_device);
0691         pf_device = NULL;
0692         pr_err("unable to register platform device\n");
0693         goto out_platform_registered;
0694     }
0695     product = dmi_get_system_info(DMI_PRODUCT_NAME);
0696     if (product && strlen(product) > 4)
0697         switch (product[4]) {
0698         case '5':
0699             if (strlen(product) > 5)
0700                 switch (product[5]) {
0701                 case 'N':
0702                     year = 2021;
0703                     break;
0704                 case '0':
0705                     year = 2016;
0706                     break;
0707                 default:
0708                     year = 2022;
0709                 }
0710             break;
0711         case '6':
0712             year = 2016;
0713             break;
0714         case '7':
0715             year = 2017;
0716             break;
0717         case '8':
0718             year = 2018;
0719             break;
0720         case '9':
0721             year = 2019;
0722             break;
0723         case '0':
0724             if (strlen(product) > 5)
0725                 switch (product[5]) {
0726                 case 'N':
0727                     year = 2020;
0728                     break;
0729                 case 'P':
0730                     year = 2021;
0731                     break;
0732                 default:
0733                     year = 2022;
0734                 }
0735             break;
0736         default:
0737             year = 2019;
0738         }
0739     pr_info("product: %s  year: %d\n", product, year);
0740 
0741     if (year >= 2019)
0742         battery_limit_use_wmbb = 1;
0743 
0744     ret = sysfs_create_group(&pf_device->dev.kobj, &dev_attribute_group);
0745     if (ret)
0746         goto out_platform_device;
0747 
0748     /* LEDs are optional */
0749     led_classdev_register(&pf_device->dev, &kbd_backlight);
0750     led_classdev_register(&pf_device->dev, &tpad_led);
0751 
0752     wmi_input_setup();
0753     battery_hook_register(&battery_hook);
0754 
0755     return 0;
0756 
0757 out_platform_device:
0758     platform_device_unregister(pf_device);
0759 out_platform_registered:
0760     platform_driver_unregister(&pf_driver);
0761     return ret;
0762 }
0763 
0764 static int acpi_remove(struct acpi_device *device)
0765 {
0766     sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group);
0767 
0768     led_classdev_unregister(&tpad_led);
0769     led_classdev_unregister(&kbd_backlight);
0770 
0771     battery_hook_unregister(&battery_hook);
0772     wmi_input_destroy();
0773     platform_device_unregister(pf_device);
0774     pf_device = NULL;
0775     platform_driver_unregister(&pf_driver);
0776 
0777     return 0;
0778 }
0779 
0780 static const struct acpi_device_id device_ids[] = {
0781     {"LGEX0815", 0},
0782     {"", 0}
0783 };
0784 MODULE_DEVICE_TABLE(acpi, device_ids);
0785 
0786 static struct acpi_driver acpi_driver = {
0787     .name = "LG Gram Laptop Support",
0788     .class = "lg-laptop",
0789     .ids = device_ids,
0790     .ops = {
0791         .add = acpi_add,
0792         .remove = acpi_remove,
0793         .notify = acpi_notify,
0794         },
0795     .owner = THIS_MODULE,
0796 };
0797 
0798 static int __init acpi_init(void)
0799 {
0800     int result;
0801 
0802     result = acpi_bus_register_driver(&acpi_driver);
0803     if (result < 0) {
0804         pr_debug("Error registering driver\n");
0805         return -ENODEV;
0806     }
0807 
0808     return 0;
0809 }
0810 
0811 static void __exit acpi_exit(void)
0812 {
0813     acpi_bus_unregister_driver(&acpi_driver);
0814 }
0815 
0816 module_init(acpi_init);
0817 module_exit(acpi_exit);