Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6c231bd5 authored by Henrique de Moraes Holschuh's avatar Henrique de Moraes Holschuh Committed by Len Brown
Browse files

ACPI: thinkpad-acpi: add tablet-mode reporting



A quick study of the 0x5009/0x500A HKEY event on the X61t DSDT revealed the
existence of the EC HTAB register (EC 0x0f, bit 7), and a compare with the
X41t DSDT shows that HKEY.MHKG can be used to verify if the ThinkPad is
tablet-capable (MHKG present), and in tablet mode (bit 3 of MHKG return is
set).

Add an attribute to report this information, "hotkey_tablet_mode".  This
attribute has poll()/select() support, and can be used along with EV_SW
SW_TABLET_MODE to hook userspace to tablet events.

Signed-off-by: default avatarHenrique de Moraes Holschuh <hmh@hmh.eng.br>
Signed-off-by: default avatarLen Brown <len.brown@intel.com>
parent d147da73
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -313,6 +313,13 @@ sysfs notes:

		This attribute has poll()/select() support.

	hotkey_tablet_mode:
		If the ThinkPad has tablet capabilities, this attribute
		will read 0 if the ThinkPad is in normal mode, and
		1 if the ThinkPad is in tablet mode.

		This attribute has poll()/select() support.

	hotkey_report_mode:
		Returns the state of the procfs ACPI event report mode
		filter for hot keys.  If it is set to 1 (the default),
+68 −11
Original line number Diff line number Diff line
@@ -221,6 +221,7 @@ static struct {
	u32 hotkey:1;
	u32 hotkey_mask:1;
	u32 hotkey_wlsw:1;
	u32 hotkey_tablet:1;
	u32 light:1;
	u32 light_status:1;
	u32 bright_16levels:1;
@@ -1060,6 +1061,9 @@ static struct attribute_set *hotkey_dev_attributes;
#define HOTKEY_CONFIG_CRITICAL_END
#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */

/* HKEY.MHKG() return bits */
#define TP_HOTKEY_TABLET_MASK (1 << 3)

static int hotkey_get_wlsw(int *status)
{
	if (!acpi_evalf(hkey_handle, status, "WLSW", "d"))
@@ -1067,6 +1071,16 @@ static int hotkey_get_wlsw(int *status)
	return 0;
}

static int hotkey_get_tablet_mode(int *status)
{
	int s;

	if (!acpi_evalf(hkey_handle, &s, "MHKG", "d"))
		return -EIO;

	return ((s & TP_HOTKEY_TABLET_MASK) != 0);
}

/*
 * Call with hotkey_mutex held
 */
@@ -1172,8 +1186,12 @@ static void tpacpi_input_send_radiosw(void)
	}
}

static void tpacpi_input_send_tabletsw(unsigned int state)
static void tpacpi_input_send_tabletsw(void)
{
	int state;

	if (tp_features.hotkey_tablet &&
	    !hotkey_get_tablet_mode(&state)) {
		mutex_lock(&tpacpi_inputdev_send_mutex);

		input_report_switch(tpacpi_inputdev,
@@ -1182,6 +1200,7 @@ static void tpacpi_input_send_tabletsw(unsigned int state)

		mutex_unlock(&tpacpi_inputdev_send_mutex);
	}
}

static void tpacpi_input_send_key(unsigned int scancode)
{
@@ -1691,6 +1710,29 @@ static void hotkey_radio_sw_notify_change(void)
			     "hotkey_radio_sw");
}

/* sysfs hotkey tablet mode (pollable) --------------------------------- */
static ssize_t hotkey_tablet_mode_show(struct device *dev,
			   struct device_attribute *attr,
			   char *buf)
{
	int res, s;
	res = hotkey_get_tablet_mode(&s);
	if (res < 0)
		return res;

	return snprintf(buf, PAGE_SIZE, "%d\n", !!s);
}

static struct device_attribute dev_attr_hotkey_tablet_mode =
	__ATTR(hotkey_tablet_mode, S_IRUGO, hotkey_tablet_mode_show, NULL);

static void hotkey_tablet_mode_notify_change(void)
{
	if (tp_features.hotkey_tablet)
		sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
			     "hotkey_tablet_mode");
}

/* sysfs hotkey report_mode -------------------------------------------- */
static ssize_t hotkey_report_mode_show(struct device *dev,
			   struct device_attribute *attr,
@@ -1903,7 +1945,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
		str_supported(tp_features.hotkey));

	if (tp_features.hotkey) {
		hotkey_dev_attributes = create_attr_set(12, NULL);
		hotkey_dev_attributes = create_attr_set(13, NULL);
		if (!hotkey_dev_attributes)
			return -ENOMEM;
		res = add_many_to_attr_set(hotkey_dev_attributes,
@@ -1982,6 +2024,18 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
					&dev_attr_hotkey_radio_sw.attr);
		}

		/* For X41t, X60t, X61t Tablets... */
		if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) {
			tp_features.hotkey_tablet = 1;
			printk(TPACPI_INFO
				"possible tablet mode switch found; "
				"ThinkPad in %s mode\n",
				(status & TP_HOTKEY_TABLET_MASK)?
					"tablet" : "laptop");
			res = add_to_attr_set(hotkey_dev_attributes,
					&dev_attr_hotkey_tablet_mode.attr);
		}

		if (!res)
			res = register_attr_set_with_sysfs(
					hotkey_dev_attributes,
@@ -2031,7 +2085,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
			set_bit(EV_SW, tpacpi_inputdev->evbit);
			set_bit(SW_RADIO, tpacpi_inputdev->swbit);
		}
		if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) {
		if (tp_features.hotkey_tablet) {
			set_bit(EV_SW, tpacpi_inputdev->evbit);
			set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit);
		}
@@ -2057,6 +2111,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)

		hotkey_poll_setup_safe(1);
		tpacpi_input_send_radiosw();
		tpacpi_input_send_tabletsw();
	}

	return (tp_features.hotkey)? 0 : 1;
@@ -2187,9 +2242,10 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
			case 0x500b: /* X61t: tablet pen inserted into bay */
			case 0x500c: /* X61t: tablet pen removed from bay */
				break;
			case 0x5009: /* X61t: swivel up (tablet mode) */
			case 0x500a: /* X61t: swivel down (normal mode) */
				tpacpi_input_send_tabletsw((hkey == 0x5009));
			case 0x5009: /* X41t-X61t: swivel up (tablet mode) */
			case 0x500a: /* X41t-X61t: swivel down (normal mode) */
				tpacpi_input_send_tabletsw();
				hotkey_tablet_mode_notify_change();
				send_acpi_ev = 0;
				break;
			case 0x5001:
@@ -2250,6 +2306,7 @@ static void hotkey_resume(void)
		       "from firmware\n");
	tpacpi_input_send_radiosw();
	hotkey_radio_sw_notify_change();
	hotkey_tablet_mode_notify_change();
	hotkey_wakeup_reason_notify_change();
	hotkey_wakeup_hotunplug_complete_notify_change();
	hotkey_poll_setup_safe(0);