@@ -4049,6 +4049,7 @@ static bool hotkey_notify_6xxx(const u32 hkey,
pr_debug("EC reports: Thermal Control Command set completed (DYTC)\n");
/* recommended action: do nothing, we don't have
* Lenovo ATM information */
+ tpacpi_driver_event(hkey);
return true;
case TP_HKEY_EV_THM_TRANSFM_CHANGED:
pr_debug("EC reports: Thermal Transformation changed (GMTS)\n");
@@ -9811,6 +9812,107 @@ 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 int dytc_lapmode;
+static void dytc_lapmode_notify_change(void)
+{
+ sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
+ "dytc_lapmode");
+}
+
+static int dytc_command(int command)
+{
+ acpi_handle dytc_handle;
+ int output;
+
+ 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 output;
+}
+
+static int dytc_lapmode_get(void)
+{
+ int output;
+
+ output = dytc_command(DYTC_CMD_GET);
+ if ((output == -ENODEV) || (output == -EIO))
+ return output;
+
+ return (output & BIT(DYTC_GET_LAPMODE_BIT) ? 1 : 0);
+}
+
+static void dytc_lapmode_refresh(void)
+{
+ int new_state;
+
+ new_state = dytc_lapmode_get();
+ if ((new_state == -ENODEV) || (new_state == -EIO) || (new_state == dytc_lapmode))
+ return;
+
+ if (dytc_lapmode != new_state) {
+ 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)
+{
+ if (dytc_lapmode < 0)
+ return dytc_lapmode;
+
+ 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 res;
+
+ dytc_lapmode = dytc_lapmode_get();
+
+ if (dytc_lapmode < 0 && dytc_lapmode != -ENODEV)
+ return dytc_lapmode;
+
+ res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
+ &dytc_attr_group);
+
+ return res;
+}
+
+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
+};
+
/****************************************************************************
****************************************************************************
*
@@ -9858,6 +9960,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)
@@ -10296,6 +10402,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)