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 | |
| 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>
| -rw-r--r-- | Documentation/laptops/thinkpad-acpi.txt | 7 | ||||
| -rw-r--r-- | drivers/misc/thinkpad_acpi.c | 79 |
2 files changed, 75 insertions, 11 deletions
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index 606af1628382..76cb428435da 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt | |||
| @@ -313,6 +313,13 @@ sysfs notes: | |||
| 313 | 313 | ||
| 314 | This attribute has poll()/select() support. | 314 | This attribute has poll()/select() support. |
| 315 | 315 | ||
| 316 | hotkey_tablet_mode: | ||
| 317 | If the ThinkPad has tablet capabilities, this attribute | ||
| 318 | will read 0 if the ThinkPad is in normal mode, and | ||
| 319 | 1 if the ThinkPad is in tablet mode. | ||
| 320 | |||
| 321 | This attribute has poll()/select() support. | ||
| 322 | |||
| 316 | hotkey_report_mode: | 323 | hotkey_report_mode: |
| 317 | Returns the state of the procfs ACPI event report mode | 324 | Returns the state of the procfs ACPI event report mode |
| 318 | filter for hot keys. If it is set to 1 (the default), | 325 | filter for hot keys. If it is set to 1 (the default), |
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); |
