Merge tag 'v5.9' into next
[linux-block.git] / drivers / platform / x86 / thinkpad_acpi.c
index fd93df4ac293e1cceb60c7854420d6b9dec6b2a2..e3810675090ac93f205a0d2025ec4389cc94cf52 100644 (file)
@@ -318,6 +318,7 @@ static struct {
        u32 uwb:1;
        u32 fan_ctrl_status_undef:1;
        u32 second_fan:1;
+       u32 second_fan_ctl:1;
        u32 beep_needs_two_args:1;
        u32 mixer_no_level_control:1;
        u32 battery_force_primary:1;
@@ -884,10 +885,10 @@ static ssize_t dispatch_proc_write(struct file *file,
 
        if (!ibm || !ibm->write)
                return -EINVAL;
-       if (count > PAGE_SIZE - 2)
+       if (count > PAGE_SIZE - 1)
                return -EINVAL;
 
-       kernbuf = kmalloc(count + 2, GFP_KERNEL);
+       kernbuf = kmalloc(count + 1, GFP_KERNEL);
        if (!kernbuf)
                return -ENOMEM;
 
@@ -897,7 +898,6 @@ static ssize_t dispatch_proc_write(struct file *file,
        }
 
        kernbuf[count] = 0;
-       strcat(kernbuf, ",");
        ret = ibm->write(kernbuf);
        if (ret == 0)
                ret = count;
@@ -915,23 +915,6 @@ static const struct proc_ops dispatch_proc_ops = {
        .proc_write     = dispatch_proc_write,
 };
 
-static char *next_cmd(char **cmds)
-{
-       char *start = *cmds;
-       char *end;
-
-       while ((end = strchr(start, ',')) && end == start)
-               start = end + 1;
-
-       if (!end)
-               return NULL;
-
-       *end = 0;
-       *cmds = end + 1;
-       return start;
-}
-
-
 /****************************************************************************
  ****************************************************************************
  *
@@ -1422,7 +1405,7 @@ static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf)
        if (id >= TPACPI_RFK_SW_MAX)
                return -ENODEV;
 
-       while ((cmd = next_cmd(&buf))) {
+       while ((cmd = strsep(&buf, ","))) {
                if (strlencmp(cmd, "enable") == 0)
                        status = TPACPI_RFK_RADIO_ON;
                else if (strlencmp(cmd, "disable") == 0)
@@ -2590,7 +2573,7 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
  */
 static int hotkey_kthread(void *data)
 {
-       struct tp_nvram_state s[2];
+       struct tp_nvram_state s[2] = { 0 };
        u32 poll_mask, event_mask;
        unsigned int si, so;
        unsigned long t;
@@ -4055,8 +4038,8 @@ static bool hotkey_notify_6xxx(const u32 hkey,
                return true;
        case TP_HKEY_EV_THM_CSM_COMPLETED:
                pr_debug("EC reports: Thermal Control Command set completed (DYTC)\n");
-               /* recommended action: do nothing, we don't have
-                * Lenovo ATM information */
+               /* Thermal event - pass on to event handler */
+               tpacpi_driver_event(hkey);
                return true;
        case TP_HKEY_EV_THM_TRANSFM_CHANGED:
                pr_debug("EC reports: Thermal Transformation changed (GMTS)\n");
@@ -4085,7 +4068,7 @@ static bool hotkey_notify_6xxx(const u32 hkey,
                 * AC status changed; can be triggered by plugging or
                 * unplugging AC adapter, docking or undocking. */
 
-               /* fallthrough */
+               fallthrough;
 
        case TP_HKEY_EV_KEY_NUMLOCK:
        case TP_HKEY_EV_KEY_FN:
@@ -4201,7 +4184,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
                                known_ev = true;
                                break;
                        }
-                       /* fallthrough - to default */
+                       fallthrough;    /* to default */
                default:
                        known_ev = false;
                }
@@ -4313,7 +4296,7 @@ static int hotkey_write(char *buf)
        mask = hotkey_user_mask;
 
        res = 0;
-       while ((cmd = next_cmd(&buf))) {
+       while ((cmd = strsep(&buf, ","))) {
                if (strlencmp(cmd, "enable") == 0) {
                        hotkey_enabledisable_warn(1);
                } else if (strlencmp(cmd, "disable") == 0) {
@@ -5240,7 +5223,7 @@ static int video_write(char *buf)
        enable = 0;
        disable = 0;
 
-       while ((cmd = next_cmd(&buf))) {
+       while ((cmd = strsep(&buf, ","))) {
                if (strlencmp(cmd, "lcd_enable") == 0) {
                        enable |= TP_ACPI_VIDEO_S_LCD;
                } else if (strlencmp(cmd, "lcd_disable") == 0) {
@@ -5441,8 +5424,7 @@ static int __init kbdlight_init(struct ibm_init_struct *iibm)
 
 static void kbdlight_exit(void)
 {
-       if (tp_features.kbdlight)
-               led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev);
+       led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev);
 }
 
 static int kbdlight_set_level_and_update(int level)
@@ -5480,23 +5462,18 @@ static int kbdlight_read(struct seq_file *m)
 static int kbdlight_write(char *buf)
 {
        char *cmd;
-       int level = -1;
+       int res, level = -EINVAL;
 
        if (!tp_features.kbdlight)
                return -ENODEV;
 
-       while ((cmd = next_cmd(&buf))) {
-               if (strlencmp(cmd, "0") == 0)
-                       level = 0;
-               else if (strlencmp(cmd, "1") == 0)
-                       level = 1;
-               else if (strlencmp(cmd, "2") == 0)
-                       level = 2;
-               else
-                       return -EINVAL;
+       while ((cmd = strsep(&buf, ","))) {
+               res = kstrtoint(cmd, 10, &level);
+               if (res < 0)
+                       return res;
        }
 
-       if (level == -1)
+       if (level >= 3 || level < 0)
                return -EINVAL;
 
        return kbdlight_set_level_and_update(level);
@@ -5665,7 +5642,7 @@ static int light_write(char *buf)
        if (!tp_features.light)
                return -ENODEV;
 
-       while ((cmd = next_cmd(&buf))) {
+       while ((cmd = strsep(&buf, ","))) {
                if (strlencmp(cmd, "on") == 0) {
                        newstatus = 1;
                } else if (strlencmp(cmd, "off") == 0) {
@@ -5750,7 +5727,7 @@ static int cmos_write(char *buf)
        char *cmd;
        int cmos_cmd, res;
 
-       while ((cmd = next_cmd(&buf))) {
+       while ((cmd = strsep(&buf, ","))) {
                if (sscanf(cmd, "%u", &cmos_cmd) == 1 &&
                    cmos_cmd >= 0 && cmos_cmd <= 21) {
                        /* cmos_cmd set */
@@ -5956,20 +5933,14 @@ static void led_exit(void)
 {
        unsigned int i;
 
-       for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
-               if (tpacpi_leds[i].led_classdev.name)
-                       led_classdev_unregister(&tpacpi_leds[i].led_classdev);
-       }
+       for (i = 0; i < TPACPI_LED_NUMLEDS; i++)
+               led_classdev_unregister(&tpacpi_leds[i].led_classdev);
 
        kfree(tpacpi_leds);
 }
 
 static int __init tpacpi_init_led(unsigned int led)
 {
-       int rc;
-
-       tpacpi_leds[led].led = led;
-
        /* LEDs with no name don't get registered */
        if (!tpacpi_led_names[led])
                return 0;
@@ -5977,17 +5948,12 @@ static int __init tpacpi_init_led(unsigned int led)
        tpacpi_leds[led].led_classdev.brightness_set_blocking = &led_sysfs_set;
        tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set;
        if (led_supported == TPACPI_LED_570)
-               tpacpi_leds[led].led_classdev.brightness_get =
-                                               &led_sysfs_get;
+               tpacpi_leds[led].led_classdev.brightness_get = &led_sysfs_get;
 
        tpacpi_leds[led].led_classdev.name = tpacpi_led_names[led];
+       tpacpi_leds[led].led = led;
 
-       rc = led_classdev_register(&tpacpi_pdev->dev,
-                               &tpacpi_leds[led].led_classdev);
-       if (rc < 0)
-               tpacpi_leds[led].led_classdev.name = NULL;
-
-       return rc;
+       return led_classdev_register(&tpacpi_pdev->dev, &tpacpi_leds[led].led_classdev);
 }
 
 static const struct tpacpi_quirk led_useful_qtable[] __initconst = {
@@ -6097,8 +6063,7 @@ static int __init led_init(struct ibm_init_struct *iibm)
        for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
                tpacpi_leds[i].led = -1;
 
-               if (!tpacpi_is_led_restricted(i) &&
-                   test_bit(i, &useful_leds)) {
+               if (!tpacpi_is_led_restricted(i) && test_bit(i, &useful_leds)) {
                        rc = tpacpi_init_led(i);
                        if (rc < 0) {
                                led_exit();
@@ -6151,12 +6116,14 @@ static int led_write(char *buf)
        if (!led_supported)
                return -ENODEV;
 
-       while ((cmd = next_cmd(&buf))) {
+       while ((cmd = strsep(&buf, ","))) {
                if (sscanf(cmd, "%d", &led) != 1)
                        return -EINVAL;
 
-               if (led < 0 || led > (TPACPI_LED_NUMLEDS - 1) ||
-                               tpacpi_leds[led].led < 0)
+               if (led < 0 || led > (TPACPI_LED_NUMLEDS - 1))
+                       return -ENODEV;
+
+               if (tpacpi_leds[led].led < 0)
                        return -ENODEV;
 
                if (strstr(cmd, "off")) {
@@ -6236,7 +6203,7 @@ static int beep_write(char *buf)
        if (!beep_handle)
                return -ENODEV;
 
-       while ((cmd = next_cmd(&buf))) {
+       while ((cmd = strsep(&buf, ","))) {
                if (sscanf(cmd, "%u", &beep_cmd) == 1 &&
                    beep_cmd >= 0 && beep_cmd <= 17) {
                        /* beep_cmd set */
@@ -6307,7 +6274,7 @@ static int thermal_get_sensor(int idx, s32 *value)
                        idx -= 8;
                }
 #endif
-               /* fallthrough */
+               fallthrough;
        case TPACPI_THERMAL_TPEC_8:
                if (idx <= 7) {
                        if (!acpi_ec_read(t + idx, &tmp))
@@ -6870,8 +6837,10 @@ static int __init tpacpi_query_bcl_levels(acpi_handle handle)
        list_for_each_entry(child, &device->children, node) {
                acpi_status status = acpi_evaluate_object(child->handle, "_BCL",
                                                          NULL, &buffer);
-               if (ACPI_FAILURE(status))
+               if (ACPI_FAILURE(status)) {
+                       buffer.length = ACPI_ALLOCATE_BUFFER;
                        continue;
+               }
 
                obj = (union acpi_object *)buffer.pointer;
                if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
@@ -7004,10 +6973,13 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
                        pr_warn("Cannot enable backlight brightness support, ACPI is already handling it.  Refer to the acpi_backlight kernel parameter.\n");
                        return 1;
                }
-       } else if (tp_features.bright_acpimode && brightness_enable > 1) {
-               pr_notice("Standard ACPI backlight interface not available, thinkpad_acpi native brightness control enabled\n");
+       } else if (!tp_features.bright_acpimode) {
+               pr_notice("ACPI backlight interface not available\n");
+               return 1;
        }
 
+       pr_notice("ACPI native brightness control enabled\n");
+
        /*
         * Check for module parameter bogosity, note that we
         * init brightness_mode to TPACPI_BRGHT_MODE_MAX in order to be
@@ -7124,7 +7096,7 @@ static int brightness_write(char *buf)
        if (level < 0)
                return level;
 
-       while ((cmd = next_cmd(&buf))) {
+       while ((cmd = strsep(&buf, ","))) {
                if (strlencmp(cmd, "up") == 0) {
                        if (level < bright_maxlvl)
                                level++;
@@ -7876,7 +7848,7 @@ static int volume_write(char *buf)
        new_level = s & TP_EC_AUDIO_LVL_MSK;
        new_mute  = s & TP_EC_AUDIO_MUTESW_MSK;
 
-       while ((cmd = next_cmd(&buf))) {
+       while ((cmd = strsep(&buf, ","))) {
                if (!tp_features.mixer_no_level_control) {
                        if (strlencmp(cmd, "up") == 0) {
                                if (new_mute)
@@ -8006,7 +7978,7 @@ static struct ibm_struct volume_driver_data = {
  *     does so, its initial value is meaningless (0x07).
  *
  *     For firmware bugs, refer to:
- *     http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
+ *     https://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
  *
  *     ----
  *
@@ -8031,7 +8003,7 @@ static struct ibm_struct volume_driver_data = {
  *     mode.
  *
  *     For firmware bugs, refer to:
- *     http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
+ *     https://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
  *
  *     ----
  *
@@ -8332,11 +8304,19 @@ static int fan_set_level(int level)
 
        switch (fan_control_access_mode) {
        case TPACPI_FAN_WR_ACPI_SFAN:
-               if (level >= 0 && level <= 7) {
-                       if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
-                               return -EIO;
-               } else
+               if ((level < 0) || (level > 7))
                        return -EINVAL;
+
+               if (tp_features.second_fan_ctl) {
+                       if (!fan_select_fan2() ||
+                           !acpi_evalf(sfan_handle, NULL, NULL, "vd", level)) {
+                               pr_warn("Couldn't set 2nd fan level, disabling support\n");
+                               tp_features.second_fan_ctl = 0;
+                       }
+                       fan_select_fan1();
+               }
+               if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
+                       return -EIO;
                break;
 
        case TPACPI_FAN_WR_ACPI_FANS:
@@ -8353,6 +8333,15 @@ static int fan_set_level(int level)
                else if (level & TP_EC_FAN_AUTO)
                        level |= 4;     /* safety min speed 4 */
 
+               if (tp_features.second_fan_ctl) {
+                       if (!fan_select_fan2() ||
+                           !acpi_ec_write(fan_status_offset, level)) {
+                               pr_warn("Couldn't set 2nd fan level, disabling support\n");
+                               tp_features.second_fan_ctl = 0;
+                       }
+                       fan_select_fan1();
+
+               }
                if (!acpi_ec_write(fan_status_offset, level))
                        return -EIO;
                else
@@ -8771,6 +8760,7 @@ static const struct attribute_group fan_attr_group = {
 
 #define TPACPI_FAN_Q1  0x0001          /* Unitialized HFSP */
 #define TPACPI_FAN_2FAN        0x0002          /* EC 0x31 bit 0 selects fan2 */
+#define TPACPI_FAN_2CTL        0x0004          /* selects fan2 control */
 
 static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
        TPACPI_QEC_IBM('1', 'Y', TPACPI_FAN_Q1),
@@ -8779,6 +8769,13 @@ static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
        TPACPI_QEC_IBM('7', '0', TPACPI_FAN_Q1),
        TPACPI_QEC_LNV('7', 'M', TPACPI_FAN_2FAN),
        TPACPI_Q_LNV('N', '1', TPACPI_FAN_2FAN),
+       TPACPI_Q_LNV3('N', '1', 'D', TPACPI_FAN_2CTL),  /* P70 */
+       TPACPI_Q_LNV3('N', '1', 'E', TPACPI_FAN_2CTL),  /* P50 */
+       TPACPI_Q_LNV3('N', '1', 'T', TPACPI_FAN_2CTL),  /* P71 */
+       TPACPI_Q_LNV3('N', '1', 'U', TPACPI_FAN_2CTL),  /* P51 */
+       TPACPI_Q_LNV3('N', '2', 'C', TPACPI_FAN_2CTL),  /* P52 / P72 */
+       TPACPI_Q_LNV3('N', '2', 'E', TPACPI_FAN_2CTL),  /* P1 / X1 Extreme (1st gen) */
+       TPACPI_Q_LNV3('N', '2', 'O', TPACPI_FAN_2CTL),  /* P1 / X1 Extreme (2nd gen) */
 };
 
 static int __init fan_init(struct ibm_init_struct *iibm)
@@ -8796,6 +8793,7 @@ static int __init fan_init(struct ibm_init_struct *iibm)
        fan_watchdog_maxinterval = 0;
        tp_features.fan_ctrl_status_undef = 0;
        tp_features.second_fan = 0;
+       tp_features.second_fan_ctl = 0;
        fan_control_desired_level = 7;
 
        if (tpacpi_is_ibm()) {
@@ -8820,8 +8818,12 @@ static int __init fan_init(struct ibm_init_struct *iibm)
                                fan_quirk1_setup();
                        if (quirks & TPACPI_FAN_2FAN) {
                                tp_features.second_fan = 1;
-                               dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN,
-                                       "secondary fan support enabled\n");
+                               pr_info("secondary fan support enabled\n");
+                       }
+                       if (quirks & TPACPI_FAN_2CTL) {
+                               tp_features.second_fan = 1;
+                               tp_features.second_fan_ctl = 1;
+                               pr_info("secondary fan control enabled\n");
                        }
                } else {
                        pr_err("ThinkPad ACPI EC access misbehaving, fan status and control unavailable\n");
@@ -9156,7 +9158,7 @@ static int fan_write(char *buf)
        char *cmd;
        int rc = 0;
 
-       while (!rc && (cmd = next_cmd(&buf))) {
+       while (!rc && (cmd = strsep(&buf, ","))) {
                if (!((fan_control_commands & TPACPI_FAN_CMD_LEVEL) &&
                      fan_write_cmd_level(cmd, &rc)) &&
                    !((fan_control_commands & TPACPI_FAN_CMD_ENABLE) &&
@@ -9279,10 +9281,8 @@ static int mute_led_init(struct ibm_init_struct *iibm)
                mute_led_cdev[i].brightness = ledtrig_audio_get(i);
                err = led_classdev_register(&tpacpi_pdev->dev, &mute_led_cdev[i]);
                if (err < 0) {
-                       while (i--) {
-                               if (led_tables[i].state >= 0)
-                                       led_classdev_unregister(&mute_led_cdev[i]);
-                       }
+                       while (i--)
+                               led_classdev_unregister(&mute_led_cdev[i]);
                        return err;
                }
        }
@@ -9294,10 +9294,8 @@ static void mute_led_exit(void)
        int i;
 
        for (i = 0; i < TPACPI_LED_MAX; i++) {
-               if (led_tables[i].state >= 0) {
-                       led_classdev_unregister(&mute_led_cdev[i]);
-                       tpacpi_led_set(i, false);
-               }
+               led_classdev_unregister(&mute_led_cdev[i]);
+               tpacpi_led_set(i, false);
        }
 }
 
@@ -9330,9 +9328,6 @@ static struct ibm_struct mute_led_driver_data = {
 #define GET_STOP       "BCSG"
 #define SET_STOP       "BCSS"
 
-#define START_ATTR "charge_start_threshold"
-#define STOP_ATTR  "charge_stop_threshold"
-
 enum {
        BAT_ANY = 0,
        BAT_PRIMARY = 1,
@@ -9618,38 +9613,52 @@ static ssize_t tpacpi_battery_show(int what,
        return sprintf(buf, "%d\n", ret);
 }
 
-static ssize_t charge_start_threshold_show(struct device *device,
+static ssize_t charge_control_start_threshold_show(struct device *device,
                                struct device_attribute *attr,
                                char *buf)
 {
        return tpacpi_battery_show(THRESHOLD_START, device, buf);
 }
 
-static ssize_t charge_stop_threshold_show(struct device *device,
+static ssize_t charge_control_end_threshold_show(struct device *device,
                                struct device_attribute *attr,
                                char *buf)
 {
        return tpacpi_battery_show(THRESHOLD_STOP, device, buf);
 }
 
-static ssize_t charge_start_threshold_store(struct device *dev,
+static ssize_t charge_control_start_threshold_store(struct device *dev,
                                struct device_attribute *attr,
                                const char *buf, size_t count)
 {
        return tpacpi_battery_store(THRESHOLD_START, dev, buf, count);
 }
 
-static ssize_t charge_stop_threshold_store(struct device *dev,
+static ssize_t charge_control_end_threshold_store(struct device *dev,
                                struct device_attribute *attr,
                                const char *buf, size_t count)
 {
        return tpacpi_battery_store(THRESHOLD_STOP, dev, buf, count);
 }
 
-static DEVICE_ATTR_RW(charge_start_threshold);
-static DEVICE_ATTR_RW(charge_stop_threshold);
+static DEVICE_ATTR_RW(charge_control_start_threshold);
+static DEVICE_ATTR_RW(charge_control_end_threshold);
+static struct device_attribute dev_attr_charge_start_threshold = __ATTR(
+       charge_start_threshold,
+       0644,
+       charge_control_start_threshold_show,
+       charge_control_start_threshold_store
+);
+static struct device_attribute dev_attr_charge_stop_threshold = __ATTR(
+       charge_stop_threshold,
+       0644,
+       charge_control_end_threshold_show,
+       charge_control_end_threshold_store
+);
 
 static struct attribute *tpacpi_battery_attrs[] = {
+       &dev_attr_charge_control_start_threshold.attr,
+       &dev_attr_charge_control_end_threshold.attr,
        &dev_attr_charge_start_threshold.attr,
        &dev_attr_charge_stop_threshold.attr,
        NULL,
@@ -9794,19 +9803,18 @@ static int lcdshadow_read(struct seq_file *m)
 static int lcdshadow_write(char *buf)
 {
        char *cmd;
-       int state = -1;
+       int res, state = -EINVAL;
 
        if (lcdshadow_state < 0)
                return -ENODEV;
 
-       while ((cmd = next_cmd(&buf))) {
-               if (strlencmp(cmd, "0") == 0)
-                       state = 0;
-               else if (strlencmp(cmd, "1") == 0)
-                       state = 1;
+       while ((cmd = strsep(&buf, ","))) {
+               res = kstrtoint(cmd, 10, &state);
+               if (res < 0)
+                       return res;
        }
 
-       if (state == -1)
+       if (state >= 2 || state < 0)
                return -EINVAL;
 
        return lcdshadow_set(state);
@@ -9819,6 +9827,105 @@ static struct ibm_struct lcdshadow_driver_data = {
        .write = lcdshadow_write,
 };
 
+/*************************************************************************
+ * DYTC subdriver, for the Lenovo lapmode feature
+ */
+
+#define DYTC_CMD_GET          2 /* To get current IC function and mode */
+#define DYTC_GET_LAPMODE_BIT 17 /* Set when in lapmode */
+
+static bool dytc_lapmode;
+
+static void dytc_lapmode_notify_change(void)
+{
+       sysfs_notify(&tpacpi_pdev->dev.kobj, NULL, "dytc_lapmode");
+}
+
+static int dytc_command(int command, int *output)
+{
+       acpi_handle dytc_handle;
+
+       if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "DYTC", &dytc_handle))) {
+               /* Platform doesn't support DYTC */
+               return -ENODEV;
+       }
+       if (!acpi_evalf(dytc_handle, output, NULL, "dd", command))
+               return -EIO;
+       return 0;
+}
+
+static int dytc_lapmode_get(bool *state)
+{
+       int output, err;
+
+       err = dytc_command(DYTC_CMD_GET, &output);
+       if (err)
+               return err;
+       *state = output & BIT(DYTC_GET_LAPMODE_BIT) ? true : false;
+       return 0;
+}
+
+static void dytc_lapmode_refresh(void)
+{
+       bool new_state;
+       int err;
+
+       err = dytc_lapmode_get(&new_state);
+       if (err || (new_state == dytc_lapmode))
+               return;
+
+       dytc_lapmode = new_state;
+       dytc_lapmode_notify_change();
+}
+
+/* sysfs lapmode entry */
+static ssize_t dytc_lapmode_show(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", dytc_lapmode);
+}
+
+static DEVICE_ATTR_RO(dytc_lapmode);
+
+static struct attribute *dytc_attributes[] = {
+       &dev_attr_dytc_lapmode.attr,
+       NULL,
+};
+
+static const struct attribute_group dytc_attr_group = {
+       .attrs = dytc_attributes,
+};
+
+static int tpacpi_dytc_init(struct ibm_init_struct *iibm)
+{
+       int err;
+
+       err = dytc_lapmode_get(&dytc_lapmode);
+       /* If support isn't available (ENODEV) then don't return an error
+        * but just don't create the sysfs group
+        */
+       if (err == -ENODEV)
+               return 0;
+       /* For all other errors we can flag the failure */
+       if (err)
+               return err;
+
+       /* Platform supports this feature - create the group */
+       err = sysfs_create_group(&tpacpi_pdev->dev.kobj, &dytc_attr_group);
+       return err;
+}
+
+static void dytc_exit(void)
+{
+       sysfs_remove_group(&tpacpi_pdev->dev.kobj, &dytc_attr_group);
+}
+
+static struct ibm_struct dytc_driver_data = {
+       .name = "dytc",
+       .exit = dytc_exit,
+};
+
 /****************************************************************************
  ****************************************************************************
  *
@@ -9866,6 +9973,10 @@ static void tpacpi_driver_event(const unsigned int hkey_event)
 
                mutex_unlock(&kbdlight_mutex);
        }
+
+       if (hkey_event == TP_HKEY_EV_THM_CSM_COMPLETED)
+               dytc_lapmode_refresh();
+
 }
 
 static void hotkey_driver_event(const unsigned int scancode)
@@ -10118,7 +10229,7 @@ static int __must_check __init get_thinkpad_model_data(
         * X32 or newer, all Z series;  Some models must have an
         * up-to-date BIOS or they will not be detected.
         *
-        * See http://thinkwiki.org/wiki/List_of_DMI_IDs
+        * See https://thinkwiki.org/wiki/List_of_DMI_IDs
         */
        while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
                if (sscanf(dev->name,
@@ -10304,6 +10415,10 @@ static struct ibm_init_struct ibms_init[] __initdata = {
                .init = tpacpi_lcdshadow_init,
                .data = &lcdshadow_driver_data,
        },
+       {
+               .init = tpacpi_dytc_init,
+               .data = &dytc_driver_data,
+       },
 };
 
 static int __init set_ibm_param(const char *val, const struct kernel_param *kp)
@@ -10322,10 +10437,9 @@ static int __init set_ibm_param(const char *val, const struct kernel_param *kp)
                        continue;
 
                if (strcmp(ibm->name, kp->name) == 0 && ibm->write) {
-                       if (strlen(val) > sizeof(ibms_init[i].param) - 2)
+                       if (strlen(val) > sizeof(ibms_init[i].param) - 1)
                                return -ENOSPC;
                        strcpy(ibms_init[i].param, val);
-                       strcat(ibms_init[i].param, ",");
                        return 0;
                }
        }
@@ -10638,8 +10752,8 @@ MODULE_DEVICE_TABLE(acpi, ibm_htk_device_ids);
 /*
  * DMI matching for module autoloading
  *
- * See http://thinkwiki.org/wiki/List_of_DMI_IDs
- * See http://thinkwiki.org/wiki/BIOS_Upgrade_Downloads
+ * See https://thinkwiki.org/wiki/List_of_DMI_IDs
+ * See https://thinkwiki.org/wiki/BIOS_Upgrade_Downloads
  *
  * Only models listed in thinkwiki will be supported, so add yours
  * if it is not there yet.