diff options
author | Henrique de Moraes Holschuh <hmh@hmh.eng.br> | 2008-02-15 23:17:58 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2008-02-16 00:34:06 -0500 |
commit | 6c231bd5eb07ce546517019f334652b9ecfc329a (patch) | |
tree | 4fcb81f506a0a6591e2a5870fce855bbc2582291 /drivers/misc | |
parent | d147da73c9a3f617e4685c6a7762961fe19833e7 (diff) |
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: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/thinkpad_acpi.c | 79 |
1 files changed, 68 insertions, 11 deletions
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index c119cf23e1ff..bb269d0c677e 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c | |||
@@ -221,6 +221,7 @@ static struct { | |||
221 | u32 hotkey:1; | 221 | u32 hotkey:1; |
222 | u32 hotkey_mask:1; | 222 | u32 hotkey_mask:1; |
223 | u32 hotkey_wlsw:1; | 223 | u32 hotkey_wlsw:1; |
224 | u32 hotkey_tablet:1; | ||
224 | u32 light:1; | 225 | u32 light:1; |
225 | u32 light_status:1; | 226 | u32 light_status:1; |
226 | u32 bright_16levels:1; | 227 | u32 bright_16levels:1; |
@@ -1060,6 +1061,9 @@ static struct attribute_set *hotkey_dev_attributes; | |||
1060 | #define HOTKEY_CONFIG_CRITICAL_END | 1061 | #define HOTKEY_CONFIG_CRITICAL_END |
1061 | #endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ | 1062 | #endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ |
1062 | 1063 | ||
1064 | /* HKEY.MHKG() return bits */ | ||
1065 | #define TP_HOTKEY_TABLET_MASK (1 << 3) | ||
1066 | |||
1063 | static int hotkey_get_wlsw(int *status) | 1067 | static int hotkey_get_wlsw(int *status) |
1064 | { | 1068 | { |
1065 | if (!acpi_evalf(hkey_handle, status, "WLSW", "d")) | 1069 | if (!acpi_evalf(hkey_handle, status, "WLSW", "d")) |
@@ -1067,6 +1071,16 @@ static int hotkey_get_wlsw(int *status) | |||
1067 | return 0; | 1071 | return 0; |
1068 | } | 1072 | } |
1069 | 1073 | ||
1074 | static int hotkey_get_tablet_mode(int *status) | ||
1075 | { | ||
1076 | int s; | ||
1077 | |||
1078 | if (!acpi_evalf(hkey_handle, &s, "MHKG", "d")) | ||
1079 | return -EIO; | ||
1080 | |||
1081 | return ((s & TP_HOTKEY_TABLET_MASK) != 0); | ||
1082 | } | ||
1083 | |||
1070 | /* | 1084 | /* |
1071 | * Call with hotkey_mutex held | 1085 | * Call with hotkey_mutex held |
1072 | */ | 1086 | */ |
@@ -1172,15 +1186,20 @@ static void tpacpi_input_send_radiosw(void) | |||
1172 | } | 1186 | } |
1173 | } | 1187 | } |
1174 | 1188 | ||
1175 | static void tpacpi_input_send_tabletsw(unsigned int state) | 1189 | static void tpacpi_input_send_tabletsw(void) |
1176 | { | 1190 | { |
1177 | mutex_lock(&tpacpi_inputdev_send_mutex); | 1191 | int state; |
1178 | 1192 | ||
1179 | input_report_switch(tpacpi_inputdev, | 1193 | if (tp_features.hotkey_tablet && |
1180 | SW_TABLET_MODE, !!state); | 1194 | !hotkey_get_tablet_mode(&state)) { |
1181 | input_sync(tpacpi_inputdev); | 1195 | mutex_lock(&tpacpi_inputdev_send_mutex); |
1182 | 1196 | ||
1183 | mutex_unlock(&tpacpi_inputdev_send_mutex); | 1197 | input_report_switch(tpacpi_inputdev, |
1198 | SW_TABLET_MODE, !!state); | ||
1199 | input_sync(tpacpi_inputdev); | ||
1200 | |||
1201 | mutex_unlock(&tpacpi_inputdev_send_mutex); | ||
1202 | } | ||
1184 | } | 1203 | } |
1185 | 1204 | ||
1186 | static void tpacpi_input_send_key(unsigned int scancode) | 1205 | static void tpacpi_input_send_key(unsigned int scancode) |
@@ -1691,6 +1710,29 @@ static void hotkey_radio_sw_notify_change(void) | |||
1691 | "hotkey_radio_sw"); | 1710 | "hotkey_radio_sw"); |
1692 | } | 1711 | } |
1693 | 1712 | ||
1713 | /* sysfs hotkey tablet mode (pollable) --------------------------------- */ | ||
1714 | static ssize_t hotkey_tablet_mode_show(struct device *dev, | ||
1715 | struct device_attribute *attr, | ||
1716 | char *buf) | ||
1717 | { | ||
1718 | int res, s; | ||
1719 | res = hotkey_get_tablet_mode(&s); | ||
1720 | if (res < 0) | ||
1721 | return res; | ||
1722 | |||
1723 | return snprintf(buf, PAGE_SIZE, "%d\n", !!s); | ||
1724 | } | ||
1725 | |||
1726 | static struct device_attribute dev_attr_hotkey_tablet_mode = | ||
1727 | __ATTR(hotkey_tablet_mode, S_IRUGO, hotkey_tablet_mode_show, NULL); | ||
1728 | |||
1729 | static void hotkey_tablet_mode_notify_change(void) | ||
1730 | { | ||
1731 | if (tp_features.hotkey_tablet) | ||
1732 | sysfs_notify(&tpacpi_pdev->dev.kobj, NULL, | ||
1733 | "hotkey_tablet_mode"); | ||
1734 | } | ||
1735 | |||
1694 | /* sysfs hotkey report_mode -------------------------------------------- */ | 1736 | /* sysfs hotkey report_mode -------------------------------------------- */ |
1695 | static ssize_t hotkey_report_mode_show(struct device *dev, | 1737 | static ssize_t hotkey_report_mode_show(struct device *dev, |
1696 | struct device_attribute *attr, | 1738 | struct device_attribute *attr, |
@@ -1903,7 +1945,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
1903 | str_supported(tp_features.hotkey)); | 1945 | str_supported(tp_features.hotkey)); |
1904 | 1946 | ||
1905 | if (tp_features.hotkey) { | 1947 | if (tp_features.hotkey) { |
1906 | hotkey_dev_attributes = create_attr_set(12, NULL); | 1948 | hotkey_dev_attributes = create_attr_set(13, NULL); |
1907 | if (!hotkey_dev_attributes) | 1949 | if (!hotkey_dev_attributes) |
1908 | return -ENOMEM; | 1950 | return -ENOMEM; |
1909 | res = add_many_to_attr_set(hotkey_dev_attributes, | 1951 | res = add_many_to_attr_set(hotkey_dev_attributes, |
@@ -1982,6 +2024,18 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
1982 | &dev_attr_hotkey_radio_sw.attr); | 2024 | &dev_attr_hotkey_radio_sw.attr); |
1983 | } | 2025 | } |
1984 | 2026 | ||
2027 | /* For X41t, X60t, X61t Tablets... */ | ||
2028 | if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) { | ||
2029 | tp_features.hotkey_tablet = 1; | ||
2030 | printk(TPACPI_INFO | ||
2031 | "possible tablet mode switch found; " | ||
2032 | "ThinkPad in %s mode\n", | ||
2033 | (status & TP_HOTKEY_TABLET_MASK)? | ||
2034 | "tablet" : "laptop"); | ||
2035 | res = add_to_attr_set(hotkey_dev_attributes, | ||
2036 | &dev_attr_hotkey_tablet_mode.attr); | ||
2037 | } | ||
2038 | |||
1985 | if (!res) | 2039 | if (!res) |
1986 | res = register_attr_set_with_sysfs( | 2040 | res = register_attr_set_with_sysfs( |
1987 | hotkey_dev_attributes, | 2041 | hotkey_dev_attributes, |
@@ -2031,7 +2085,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
2031 | set_bit(EV_SW, tpacpi_inputdev->evbit); | 2085 | set_bit(EV_SW, tpacpi_inputdev->evbit); |
2032 | set_bit(SW_RADIO, tpacpi_inputdev->swbit); | 2086 | set_bit(SW_RADIO, tpacpi_inputdev->swbit); |
2033 | } | 2087 | } |
2034 | if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) { | 2088 | if (tp_features.hotkey_tablet) { |
2035 | set_bit(EV_SW, tpacpi_inputdev->evbit); | 2089 | set_bit(EV_SW, tpacpi_inputdev->evbit); |
2036 | set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit); | 2090 | set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit); |
2037 | } | 2091 | } |
@@ -2057,6 +2111,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
2057 | 2111 | ||
2058 | hotkey_poll_setup_safe(1); | 2112 | hotkey_poll_setup_safe(1); |
2059 | tpacpi_input_send_radiosw(); | 2113 | tpacpi_input_send_radiosw(); |
2114 | tpacpi_input_send_tabletsw(); | ||
2060 | } | 2115 | } |
2061 | 2116 | ||
2062 | return (tp_features.hotkey)? 0 : 1; | 2117 | return (tp_features.hotkey)? 0 : 1; |
@@ -2187,9 +2242,10 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
2187 | case 0x500b: /* X61t: tablet pen inserted into bay */ | 2242 | case 0x500b: /* X61t: tablet pen inserted into bay */ |
2188 | case 0x500c: /* X61t: tablet pen removed from bay */ | 2243 | case 0x500c: /* X61t: tablet pen removed from bay */ |
2189 | break; | 2244 | break; |
2190 | case 0x5009: /* X61t: swivel up (tablet mode) */ | 2245 | case 0x5009: /* X41t-X61t: swivel up (tablet mode) */ |
2191 | case 0x500a: /* X61t: swivel down (normal mode) */ | 2246 | case 0x500a: /* X41t-X61t: swivel down (normal mode) */ |
2192 | tpacpi_input_send_tabletsw((hkey == 0x5009)); | 2247 | tpacpi_input_send_tabletsw(); |
2248 | hotkey_tablet_mode_notify_change(); | ||
2193 | send_acpi_ev = 0; | 2249 | send_acpi_ev = 0; |
2194 | break; | 2250 | break; |
2195 | case 0x5001: | 2251 | case 0x5001: |
@@ -2250,6 +2306,7 @@ static void hotkey_resume(void) | |||
2250 | "from firmware\n"); | 2306 | "from firmware\n"); |
2251 | tpacpi_input_send_radiosw(); | 2307 | tpacpi_input_send_radiosw(); |
2252 | hotkey_radio_sw_notify_change(); | 2308 | hotkey_radio_sw_notify_change(); |
2309 | hotkey_tablet_mode_notify_change(); | ||
2253 | hotkey_wakeup_reason_notify_change(); | 2310 | hotkey_wakeup_reason_notify_change(); |
2254 | hotkey_wakeup_hotunplug_complete_notify_change(); | 2311 | hotkey_wakeup_hotunplug_complete_notify_change(); |
2255 | hotkey_poll_setup_safe(0); | 2312 | hotkey_poll_setup_safe(0); |