diff options
author | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
---|---|---|
committer | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
commit | ada47b5fe13d89735805b566185f4885f5a3f750 (patch) | |
tree | 644b88f8a71896307d71438e9b3af49126ffb22b /drivers/platform/x86/thinkpad_acpi.c | |
parent | 43e98717ad40a4ae64545b5ba047c7b86aa44f4f (diff) | |
parent | 3280f21d43ee541f97f8cda5792150d2dbec20d5 (diff) |
Merge branch 'wip-2.6.34' into old-private-masterarchived-private-master
Diffstat (limited to 'drivers/platform/x86/thinkpad_acpi.c')
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 1313 |
1 files changed, 1003 insertions, 310 deletions
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index a848c7e20aeb..63290b33c879 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
@@ -21,8 +21,8 @@ | |||
21 | * 02110-1301, USA. | 21 | * 02110-1301, USA. |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #define TPACPI_VERSION "0.23" | 24 | #define TPACPI_VERSION "0.24" |
25 | #define TPACPI_SYSFS_VERSION 0x020500 | 25 | #define TPACPI_SYSFS_VERSION 0x020700 |
26 | 26 | ||
27 | /* | 27 | /* |
28 | * Changelog: | 28 | * Changelog: |
@@ -58,9 +58,11 @@ | |||
58 | #include <linux/kthread.h> | 58 | #include <linux/kthread.h> |
59 | #include <linux/freezer.h> | 59 | #include <linux/freezer.h> |
60 | #include <linux/delay.h> | 60 | #include <linux/delay.h> |
61 | #include <linux/slab.h> | ||
61 | 62 | ||
62 | #include <linux/nvram.h> | 63 | #include <linux/nvram.h> |
63 | #include <linux/proc_fs.h> | 64 | #include <linux/proc_fs.h> |
65 | #include <linux/seq_file.h> | ||
64 | #include <linux/sysfs.h> | 66 | #include <linux/sysfs.h> |
65 | #include <linux/backlight.h> | 67 | #include <linux/backlight.h> |
66 | #include <linux/fb.h> | 68 | #include <linux/fb.h> |
@@ -76,6 +78,10 @@ | |||
76 | #include <linux/jiffies.h> | 78 | #include <linux/jiffies.h> |
77 | #include <linux/workqueue.h> | 79 | #include <linux/workqueue.h> |
78 | 80 | ||
81 | #include <sound/core.h> | ||
82 | #include <sound/control.h> | ||
83 | #include <sound/initval.h> | ||
84 | |||
79 | #include <acpi/acpi_drivers.h> | 85 | #include <acpi/acpi_drivers.h> |
80 | 86 | ||
81 | #include <linux/pci_ids.h> | 87 | #include <linux/pci_ids.h> |
@@ -231,6 +237,7 @@ enum tpacpi_hkey_event_t { | |||
231 | #define TPACPI_DBG_HKEY 0x0008 | 237 | #define TPACPI_DBG_HKEY 0x0008 |
232 | #define TPACPI_DBG_FAN 0x0010 | 238 | #define TPACPI_DBG_FAN 0x0010 |
233 | #define TPACPI_DBG_BRGHT 0x0020 | 239 | #define TPACPI_DBG_BRGHT 0x0020 |
240 | #define TPACPI_DBG_MIXER 0x0040 | ||
234 | 241 | ||
235 | #define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off") | 242 | #define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off") |
236 | #define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") | 243 | #define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") |
@@ -256,7 +263,7 @@ struct tp_acpi_drv_struct { | |||
256 | struct ibm_struct { | 263 | struct ibm_struct { |
257 | char *name; | 264 | char *name; |
258 | 265 | ||
259 | int (*read) (char *); | 266 | int (*read) (struct seq_file *); |
260 | int (*write) (char *); | 267 | int (*write) (char *); |
261 | void (*exit) (void); | 268 | void (*exit) (void); |
262 | void (*resume) (void); | 269 | void (*resume) (void); |
@@ -280,6 +287,7 @@ struct ibm_init_struct { | |||
280 | char param[32]; | 287 | char param[32]; |
281 | 288 | ||
282 | int (*init) (struct ibm_init_struct *); | 289 | int (*init) (struct ibm_init_struct *); |
290 | mode_t base_procfs_mode; | ||
283 | struct ibm_struct *data; | 291 | struct ibm_struct *data; |
284 | }; | 292 | }; |
285 | 293 | ||
@@ -298,6 +306,7 @@ static struct { | |||
298 | u32 fan_ctrl_status_undef:1; | 306 | u32 fan_ctrl_status_undef:1; |
299 | u32 second_fan:1; | 307 | u32 second_fan:1; |
300 | u32 beep_needs_two_args:1; | 308 | u32 beep_needs_two_args:1; |
309 | u32 mixer_no_level_control:1; | ||
301 | u32 input_device_registered:1; | 310 | u32 input_device_registered:1; |
302 | u32 platform_drv_registered:1; | 311 | u32 platform_drv_registered:1; |
303 | u32 platform_drv_attrs_registered:1; | 312 | u32 platform_drv_attrs_registered:1; |
@@ -309,6 +318,7 @@ static struct { | |||
309 | 318 | ||
310 | static struct { | 319 | static struct { |
311 | u16 hotkey_mask_ff:1; | 320 | u16 hotkey_mask_ff:1; |
321 | u16 volume_ctrl_forbidden:1; | ||
312 | } tp_warned; | 322 | } tp_warned; |
313 | 323 | ||
314 | struct thinkpad_id_data { | 324 | struct thinkpad_id_data { |
@@ -425,6 +435,12 @@ static void tpacpi_log_usertask(const char * const what) | |||
425 | .ec = TPACPI_MATCH_ANY, \ | 435 | .ec = TPACPI_MATCH_ANY, \ |
426 | .quirks = (__quirk) } | 436 | .quirks = (__quirk) } |
427 | 437 | ||
438 | #define TPACPI_QEC_LNV(__id1, __id2, __quirk) \ | ||
439 | { .vendor = PCI_VENDOR_ID_LENOVO, \ | ||
440 | .bios = TPACPI_MATCH_ANY, \ | ||
441 | .ec = TPID(__id1, __id2), \ | ||
442 | .quirks = (__quirk) } | ||
443 | |||
428 | struct tpacpi_quirk { | 444 | struct tpacpi_quirk { |
429 | unsigned int vendor; | 445 | unsigned int vendor; |
430 | u16 bios; | 446 | u16 bios; |
@@ -776,36 +792,25 @@ static int __init register_tpacpi_subdriver(struct ibm_struct *ibm) | |||
776 | **************************************************************************** | 792 | **************************************************************************** |
777 | ****************************************************************************/ | 793 | ****************************************************************************/ |
778 | 794 | ||
779 | static int dispatch_procfs_read(char *page, char **start, off_t off, | 795 | static int dispatch_proc_show(struct seq_file *m, void *v) |
780 | int count, int *eof, void *data) | ||
781 | { | 796 | { |
782 | struct ibm_struct *ibm = data; | 797 | struct ibm_struct *ibm = m->private; |
783 | int len; | ||
784 | 798 | ||
785 | if (!ibm || !ibm->read) | 799 | if (!ibm || !ibm->read) |
786 | return -EINVAL; | 800 | return -EINVAL; |
801 | return ibm->read(m); | ||
802 | } | ||
787 | 803 | ||
788 | len = ibm->read(page); | 804 | static int dispatch_proc_open(struct inode *inode, struct file *file) |
789 | if (len < 0) | 805 | { |
790 | return len; | 806 | return single_open(file, dispatch_proc_show, PDE(inode)->data); |
791 | |||
792 | if (len <= off + count) | ||
793 | *eof = 1; | ||
794 | *start = page + off; | ||
795 | len -= off; | ||
796 | if (len > count) | ||
797 | len = count; | ||
798 | if (len < 0) | ||
799 | len = 0; | ||
800 | |||
801 | return len; | ||
802 | } | 807 | } |
803 | 808 | ||
804 | static int dispatch_procfs_write(struct file *file, | 809 | static ssize_t dispatch_proc_write(struct file *file, |
805 | const char __user *userbuf, | 810 | const char __user *userbuf, |
806 | unsigned long count, void *data) | 811 | size_t count, loff_t *pos) |
807 | { | 812 | { |
808 | struct ibm_struct *ibm = data; | 813 | struct ibm_struct *ibm = PDE(file->f_path.dentry->d_inode)->data; |
809 | char *kernbuf; | 814 | char *kernbuf; |
810 | int ret; | 815 | int ret; |
811 | 816 | ||
@@ -834,6 +839,15 @@ static int dispatch_procfs_write(struct file *file, | |||
834 | return ret; | 839 | return ret; |
835 | } | 840 | } |
836 | 841 | ||
842 | static const struct file_operations dispatch_proc_fops = { | ||
843 | .owner = THIS_MODULE, | ||
844 | .open = dispatch_proc_open, | ||
845 | .read = seq_read, | ||
846 | .llseek = seq_lseek, | ||
847 | .release = single_release, | ||
848 | .write = dispatch_proc_write, | ||
849 | }; | ||
850 | |||
837 | static char *next_cmd(char **cmds) | 851 | static char *next_cmd(char **cmds) |
838 | { | 852 | { |
839 | char *start = *cmds; | 853 | char *start = *cmds; |
@@ -1006,11 +1020,8 @@ static int parse_strtoul(const char *buf, | |||
1006 | { | 1020 | { |
1007 | char *endp; | 1021 | char *endp; |
1008 | 1022 | ||
1009 | while (*buf && isspace(*buf)) | 1023 | *value = simple_strtoul(skip_spaces(buf), &endp, 0); |
1010 | buf++; | 1024 | endp = skip_spaces(endp); |
1011 | *value = simple_strtoul(buf, &endp, 0); | ||
1012 | while (*endp && isspace(*endp)) | ||
1013 | endp++; | ||
1014 | if (*endp || *value > max) | 1025 | if (*endp || *value > max) |
1015 | return -EINVAL; | 1026 | return -EINVAL; |
1016 | 1027 | ||
@@ -1087,7 +1098,7 @@ static int __init tpacpi_check_std_acpi_brightness_support(void) | |||
1087 | */ | 1098 | */ |
1088 | 1099 | ||
1089 | status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3, | 1100 | status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3, |
1090 | tpacpi_acpi_walk_find_bcl, NULL, | 1101 | tpacpi_acpi_walk_find_bcl, NULL, NULL, |
1091 | &bcl_ptr); | 1102 | &bcl_ptr); |
1092 | 1103 | ||
1093 | if (ACPI_SUCCESS(status) && bcl_levels > 2) { | 1104 | if (ACPI_SUCCESS(status) && bcl_levels > 2) { |
@@ -1264,6 +1275,7 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, | |||
1264 | struct tpacpi_rfk *atp_rfk; | 1275 | struct tpacpi_rfk *atp_rfk; |
1265 | int res; | 1276 | int res; |
1266 | bool sw_state = false; | 1277 | bool sw_state = false; |
1278 | bool hw_state; | ||
1267 | int sw_status; | 1279 | int sw_status; |
1268 | 1280 | ||
1269 | BUG_ON(id >= TPACPI_RFK_SW_MAX || tpacpi_rfkill_switches[id]); | 1281 | BUG_ON(id >= TPACPI_RFK_SW_MAX || tpacpi_rfkill_switches[id]); |
@@ -1298,7 +1310,8 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, | |||
1298 | rfkill_init_sw_state(atp_rfk->rfkill, sw_state); | 1310 | rfkill_init_sw_state(atp_rfk->rfkill, sw_state); |
1299 | } | 1311 | } |
1300 | } | 1312 | } |
1301 | rfkill_set_hw_state(atp_rfk->rfkill, tpacpi_rfk_check_hwblock_state()); | 1313 | hw_state = tpacpi_rfk_check_hwblock_state(); |
1314 | rfkill_set_hw_state(atp_rfk->rfkill, hw_state); | ||
1302 | 1315 | ||
1303 | res = rfkill_register(atp_rfk->rfkill); | 1316 | res = rfkill_register(atp_rfk->rfkill); |
1304 | if (res < 0) { | 1317 | if (res < 0) { |
@@ -1311,6 +1324,9 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, | |||
1311 | } | 1324 | } |
1312 | 1325 | ||
1313 | tpacpi_rfkill_switches[id] = atp_rfk; | 1326 | tpacpi_rfkill_switches[id] = atp_rfk; |
1327 | |||
1328 | printk(TPACPI_INFO "rfkill switch %s: radio is %sblocked\n", | ||
1329 | name, (sw_state || hw_state) ? "" : "un"); | ||
1314 | return 0; | 1330 | return 0; |
1315 | } | 1331 | } |
1316 | 1332 | ||
@@ -1383,12 +1399,10 @@ static ssize_t tpacpi_rfk_sysfs_enable_store(const enum tpacpi_rfk_id id, | |||
1383 | } | 1399 | } |
1384 | 1400 | ||
1385 | /* procfs -------------------------------------------------------------- */ | 1401 | /* procfs -------------------------------------------------------------- */ |
1386 | static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, char *p) | 1402 | static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, struct seq_file *m) |
1387 | { | 1403 | { |
1388 | int len = 0; | ||
1389 | |||
1390 | if (id >= TPACPI_RFK_SW_MAX) | 1404 | if (id >= TPACPI_RFK_SW_MAX) |
1391 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 1405 | seq_printf(m, "status:\t\tnot supported\n"); |
1392 | else { | 1406 | else { |
1393 | int status; | 1407 | int status; |
1394 | 1408 | ||
@@ -1402,13 +1416,13 @@ static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, char *p) | |||
1402 | return status; | 1416 | return status; |
1403 | } | 1417 | } |
1404 | 1418 | ||
1405 | len += sprintf(p + len, "status:\t\t%s\n", | 1419 | seq_printf(m, "status:\t\t%s\n", |
1406 | (status == TPACPI_RFK_RADIO_ON) ? | 1420 | (status == TPACPI_RFK_RADIO_ON) ? |
1407 | "enabled" : "disabled"); | 1421 | "enabled" : "disabled"); |
1408 | len += sprintf(p + len, "commands:\tenable, disable\n"); | 1422 | seq_printf(m, "commands:\tenable, disable\n"); |
1409 | } | 1423 | } |
1410 | 1424 | ||
1411 | return len; | 1425 | return 0; |
1412 | } | 1426 | } |
1413 | 1427 | ||
1414 | static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf) | 1428 | static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf) |
@@ -1655,7 +1669,7 @@ static void tpacpi_remove_driver_attributes(struct device_driver *drv) | |||
1655 | * Table of recommended minimum BIOS versions | 1669 | * Table of recommended minimum BIOS versions |
1656 | * | 1670 | * |
1657 | * Reasons for listing: | 1671 | * Reasons for listing: |
1658 | * 1. Stable BIOS, listed because the unknown ammount of | 1672 | * 1. Stable BIOS, listed because the unknown amount of |
1659 | * bugs and bad ACPI behaviour on older versions | 1673 | * bugs and bad ACPI behaviour on older versions |
1660 | * | 1674 | * |
1661 | * 2. BIOS or EC fw with known bugs that trigger on Linux | 1675 | * 2. BIOS or EC fw with known bugs that trigger on Linux |
@@ -1779,7 +1793,7 @@ static const struct tpacpi_quirk tpacpi_bios_version_qtable[] __initconst = { | |||
1779 | 1793 | ||
1780 | TPV_QL1('7', '9', 'E', '3', '5', '0'), /* T60/p */ | 1794 | TPV_QL1('7', '9', 'E', '3', '5', '0'), /* T60/p */ |
1781 | TPV_QL1('7', 'C', 'D', '2', '2', '2'), /* R60, R60i */ | 1795 | TPV_QL1('7', 'C', 'D', '2', '2', '2'), /* R60, R60i */ |
1782 | TPV_QL0('7', 'E', 'D', '0'), /* R60e, R60i */ | 1796 | TPV_QL1('7', 'E', 'D', '0', '1', '5'), /* R60e, R60i */ |
1783 | 1797 | ||
1784 | /* BIOS FW BIOS VERS EC FW EC VERS */ | 1798 | /* BIOS FW BIOS VERS EC FW EC VERS */ |
1785 | TPV_QI2('1', 'W', '9', '0', '1', 'V', '2', '8'), /* R50e (1) */ | 1799 | TPV_QI2('1', 'W', '9', '0', '1', 'V', '2', '8'), /* R50e (1) */ |
@@ -1795,8 +1809,8 @@ static const struct tpacpi_quirk tpacpi_bios_version_qtable[] __initconst = { | |||
1795 | TPV_QI1('7', '4', '6', '4', '2', '7'), /* X41 (0) */ | 1809 | TPV_QI1('7', '4', '6', '4', '2', '7'), /* X41 (0) */ |
1796 | TPV_QI1('7', '5', '6', '0', '2', '0'), /* X41t (0) */ | 1810 | TPV_QI1('7', '5', '6', '0', '2', '0'), /* X41t (0) */ |
1797 | 1811 | ||
1798 | TPV_QL0('7', 'B', 'D', '7'), /* X60/s */ | 1812 | TPV_QL1('7', 'B', 'D', '7', '4', '0'), /* X60/s */ |
1799 | TPV_QL0('7', 'J', '3', '0'), /* X60t */ | 1813 | TPV_QL1('7', 'J', '3', '0', '1', '3'), /* X60t */ |
1800 | 1814 | ||
1801 | /* (0) - older versions lack DMI EC fw string and functionality */ | 1815 | /* (0) - older versions lack DMI EC fw string and functionality */ |
1802 | /* (1) - older versions known to lack functionality */ | 1816 | /* (1) - older versions known to lack functionality */ |
@@ -1886,14 +1900,11 @@ static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm) | |||
1886 | return 0; | 1900 | return 0; |
1887 | } | 1901 | } |
1888 | 1902 | ||
1889 | static int thinkpad_acpi_driver_read(char *p) | 1903 | static int thinkpad_acpi_driver_read(struct seq_file *m) |
1890 | { | 1904 | { |
1891 | int len = 0; | 1905 | seq_printf(m, "driver:\t\t%s\n", TPACPI_DESC); |
1892 | 1906 | seq_printf(m, "version:\t%s\n", TPACPI_VERSION); | |
1893 | len += sprintf(p + len, "driver:\t\t%s\n", TPACPI_DESC); | 1907 | return 0; |
1894 | len += sprintf(p + len, "version:\t%s\n", TPACPI_VERSION); | ||
1895 | |||
1896 | return len; | ||
1897 | } | 1908 | } |
1898 | 1909 | ||
1899 | static struct ibm_struct thinkpad_acpi_driver_data = { | 1910 | static struct ibm_struct thinkpad_acpi_driver_data = { |
@@ -2073,6 +2084,7 @@ static struct attribute_set *hotkey_dev_attributes; | |||
2073 | 2084 | ||
2074 | static void tpacpi_driver_event(const unsigned int hkey_event); | 2085 | static void tpacpi_driver_event(const unsigned int hkey_event); |
2075 | static void hotkey_driver_event(const unsigned int scancode); | 2086 | static void hotkey_driver_event(const unsigned int scancode); |
2087 | static void hotkey_poll_setup(const bool may_warn); | ||
2076 | 2088 | ||
2077 | /* HKEY.MHKG() return bits */ | 2089 | /* HKEY.MHKG() return bits */ |
2078 | #define TP_HOTKEY_TABLET_MASK (1 << 3) | 2090 | #define TP_HOTKEY_TABLET_MASK (1 << 3) |
@@ -2189,7 +2201,8 @@ static int hotkey_mask_set(u32 mask) | |||
2189 | fwmask, hotkey_acpi_mask); | 2201 | fwmask, hotkey_acpi_mask); |
2190 | } | 2202 | } |
2191 | 2203 | ||
2192 | hotkey_mask_warn_incomplete_mask(); | 2204 | if (tpacpi_lifecycle != TPACPI_LIFE_EXITING) |
2205 | hotkey_mask_warn_incomplete_mask(); | ||
2193 | 2206 | ||
2194 | return rc; | 2207 | return rc; |
2195 | } | 2208 | } |
@@ -2254,6 +2267,8 @@ static int tpacpi_hotkey_driver_mask_set(const u32 mask) | |||
2254 | 2267 | ||
2255 | rc = hotkey_mask_set((hotkey_acpi_mask | hotkey_driver_mask) & | 2268 | rc = hotkey_mask_set((hotkey_acpi_mask | hotkey_driver_mask) & |
2256 | ~hotkey_source_mask); | 2269 | ~hotkey_source_mask); |
2270 | hotkey_poll_setup(true); | ||
2271 | |||
2257 | mutex_unlock(&hotkey_mutex); | 2272 | mutex_unlock(&hotkey_mutex); |
2258 | 2273 | ||
2259 | return rc; | 2274 | return rc; |
@@ -2538,7 +2553,7 @@ static void hotkey_poll_stop_sync(void) | |||
2538 | } | 2553 | } |
2539 | 2554 | ||
2540 | /* call with hotkey_mutex held */ | 2555 | /* call with hotkey_mutex held */ |
2541 | static void hotkey_poll_setup(bool may_warn) | 2556 | static void hotkey_poll_setup(const bool may_warn) |
2542 | { | 2557 | { |
2543 | const u32 poll_driver_mask = hotkey_driver_mask & hotkey_source_mask; | 2558 | const u32 poll_driver_mask = hotkey_driver_mask & hotkey_source_mask; |
2544 | const u32 poll_user_mask = hotkey_user_mask & hotkey_source_mask; | 2559 | const u32 poll_user_mask = hotkey_user_mask & hotkey_source_mask; |
@@ -2569,7 +2584,7 @@ static void hotkey_poll_setup(bool may_warn) | |||
2569 | } | 2584 | } |
2570 | } | 2585 | } |
2571 | 2586 | ||
2572 | static void hotkey_poll_setup_safe(bool may_warn) | 2587 | static void hotkey_poll_setup_safe(const bool may_warn) |
2573 | { | 2588 | { |
2574 | mutex_lock(&hotkey_mutex); | 2589 | mutex_lock(&hotkey_mutex); |
2575 | hotkey_poll_setup(may_warn); | 2590 | hotkey_poll_setup(may_warn); |
@@ -2587,7 +2602,11 @@ static void hotkey_poll_set_freq(unsigned int freq) | |||
2587 | 2602 | ||
2588 | #else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ | 2603 | #else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ |
2589 | 2604 | ||
2590 | static void hotkey_poll_setup_safe(bool __unused) | 2605 | static void hotkey_poll_setup(const bool __unused) |
2606 | { | ||
2607 | } | ||
2608 | |||
2609 | static void hotkey_poll_setup_safe(const bool __unused) | ||
2591 | { | 2610 | { |
2592 | } | 2611 | } |
2593 | 2612 | ||
@@ -2597,16 +2616,11 @@ static int hotkey_inputdev_open(struct input_dev *dev) | |||
2597 | { | 2616 | { |
2598 | switch (tpacpi_lifecycle) { | 2617 | switch (tpacpi_lifecycle) { |
2599 | case TPACPI_LIFE_INIT: | 2618 | case TPACPI_LIFE_INIT: |
2600 | /* | ||
2601 | * hotkey_init will call hotkey_poll_setup_safe | ||
2602 | * at the appropriate moment | ||
2603 | */ | ||
2604 | return 0; | ||
2605 | case TPACPI_LIFE_EXITING: | ||
2606 | return -EBUSY; | ||
2607 | case TPACPI_LIFE_RUNNING: | 2619 | case TPACPI_LIFE_RUNNING: |
2608 | hotkey_poll_setup_safe(false); | 2620 | hotkey_poll_setup_safe(false); |
2609 | return 0; | 2621 | return 0; |
2622 | case TPACPI_LIFE_EXITING: | ||
2623 | return -EBUSY; | ||
2610 | } | 2624 | } |
2611 | 2625 | ||
2612 | /* Should only happen if tpacpi_lifecycle is corrupt */ | 2626 | /* Should only happen if tpacpi_lifecycle is corrupt */ |
@@ -2617,7 +2631,7 @@ static int hotkey_inputdev_open(struct input_dev *dev) | |||
2617 | static void hotkey_inputdev_close(struct input_dev *dev) | 2631 | static void hotkey_inputdev_close(struct input_dev *dev) |
2618 | { | 2632 | { |
2619 | /* disable hotkey polling when possible */ | 2633 | /* disable hotkey polling when possible */ |
2620 | if (tpacpi_lifecycle == TPACPI_LIFE_RUNNING && | 2634 | if (tpacpi_lifecycle != TPACPI_LIFE_EXITING && |
2621 | !(hotkey_source_mask & hotkey_driver_mask)) | 2635 | !(hotkey_source_mask & hotkey_driver_mask)) |
2622 | hotkey_poll_setup_safe(false); | 2636 | hotkey_poll_setup_safe(false); |
2623 | } | 2637 | } |
@@ -3185,6 +3199,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3185 | int res, i; | 3199 | int res, i; |
3186 | int status; | 3200 | int status; |
3187 | int hkeyv; | 3201 | int hkeyv; |
3202 | bool radiosw_state = false; | ||
3203 | bool tabletsw_state = false; | ||
3188 | 3204 | ||
3189 | unsigned long quirks; | 3205 | unsigned long quirks; |
3190 | 3206 | ||
@@ -3290,6 +3306,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3290 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 3306 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
3291 | if (dbg_wlswemul) { | 3307 | if (dbg_wlswemul) { |
3292 | tp_features.hotkey_wlsw = 1; | 3308 | tp_features.hotkey_wlsw = 1; |
3309 | radiosw_state = !!tpacpi_wlsw_emulstate; | ||
3293 | printk(TPACPI_INFO | 3310 | printk(TPACPI_INFO |
3294 | "radio switch emulation enabled\n"); | 3311 | "radio switch emulation enabled\n"); |
3295 | } else | 3312 | } else |
@@ -3297,6 +3314,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3297 | /* Not all thinkpads have a hardware radio switch */ | 3314 | /* Not all thinkpads have a hardware radio switch */ |
3298 | if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) { | 3315 | if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) { |
3299 | tp_features.hotkey_wlsw = 1; | 3316 | tp_features.hotkey_wlsw = 1; |
3317 | radiosw_state = !!status; | ||
3300 | printk(TPACPI_INFO | 3318 | printk(TPACPI_INFO |
3301 | "radio switch found; radios are %s\n", | 3319 | "radio switch found; radios are %s\n", |
3302 | enabled(status, 0)); | 3320 | enabled(status, 0)); |
@@ -3308,11 +3326,11 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3308 | /* For X41t, X60t, X61t Tablets... */ | 3326 | /* For X41t, X60t, X61t Tablets... */ |
3309 | if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) { | 3327 | if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) { |
3310 | tp_features.hotkey_tablet = 1; | 3328 | tp_features.hotkey_tablet = 1; |
3329 | tabletsw_state = !!(status & TP_HOTKEY_TABLET_MASK); | ||
3311 | printk(TPACPI_INFO | 3330 | printk(TPACPI_INFO |
3312 | "possible tablet mode switch found; " | 3331 | "possible tablet mode switch found; " |
3313 | "ThinkPad in %s mode\n", | 3332 | "ThinkPad in %s mode\n", |
3314 | (status & TP_HOTKEY_TABLET_MASK)? | 3333 | (tabletsw_state) ? "tablet" : "laptop"); |
3315 | "tablet" : "laptop"); | ||
3316 | res = add_to_attr_set(hotkey_dev_attributes, | 3334 | res = add_to_attr_set(hotkey_dev_attributes, |
3317 | &dev_attr_hotkey_tablet_mode.attr); | 3335 | &dev_attr_hotkey_tablet_mode.attr); |
3318 | } | 3336 | } |
@@ -3347,16 +3365,14 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3347 | TPACPI_HOTKEY_MAP_SIZE); | 3365 | TPACPI_HOTKEY_MAP_SIZE); |
3348 | } | 3366 | } |
3349 | 3367 | ||
3350 | set_bit(EV_KEY, tpacpi_inputdev->evbit); | 3368 | input_set_capability(tpacpi_inputdev, EV_MSC, MSC_SCAN); |
3351 | set_bit(EV_MSC, tpacpi_inputdev->evbit); | ||
3352 | set_bit(MSC_SCAN, tpacpi_inputdev->mscbit); | ||
3353 | tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE; | 3369 | tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE; |
3354 | tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN; | 3370 | tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN; |
3355 | tpacpi_inputdev->keycode = hotkey_keycode_map; | 3371 | tpacpi_inputdev->keycode = hotkey_keycode_map; |
3356 | for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) { | 3372 | for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) { |
3357 | if (hotkey_keycode_map[i] != KEY_RESERVED) { | 3373 | if (hotkey_keycode_map[i] != KEY_RESERVED) { |
3358 | set_bit(hotkey_keycode_map[i], | 3374 | input_set_capability(tpacpi_inputdev, EV_KEY, |
3359 | tpacpi_inputdev->keybit); | 3375 | hotkey_keycode_map[i]); |
3360 | } else { | 3376 | } else { |
3361 | if (i < sizeof(hotkey_reserved_mask)*8) | 3377 | if (i < sizeof(hotkey_reserved_mask)*8) |
3362 | hotkey_reserved_mask |= 1 << i; | 3378 | hotkey_reserved_mask |= 1 << i; |
@@ -3364,12 +3380,14 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3364 | } | 3380 | } |
3365 | 3381 | ||
3366 | if (tp_features.hotkey_wlsw) { | 3382 | if (tp_features.hotkey_wlsw) { |
3367 | set_bit(EV_SW, tpacpi_inputdev->evbit); | 3383 | input_set_capability(tpacpi_inputdev, EV_SW, SW_RFKILL_ALL); |
3368 | set_bit(SW_RFKILL_ALL, tpacpi_inputdev->swbit); | 3384 | input_report_switch(tpacpi_inputdev, |
3385 | SW_RFKILL_ALL, radiosw_state); | ||
3369 | } | 3386 | } |
3370 | if (tp_features.hotkey_tablet) { | 3387 | if (tp_features.hotkey_tablet) { |
3371 | set_bit(EV_SW, tpacpi_inputdev->evbit); | 3388 | input_set_capability(tpacpi_inputdev, EV_SW, SW_TABLET_MODE); |
3372 | set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit); | 3389 | input_report_switch(tpacpi_inputdev, |
3390 | SW_TABLET_MODE, tabletsw_state); | ||
3373 | } | 3391 | } |
3374 | 3392 | ||
3375 | /* Do not issue duplicate brightness change events to | 3393 | /* Do not issue duplicate brightness change events to |
@@ -3436,8 +3454,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3436 | tpacpi_inputdev->close = &hotkey_inputdev_close; | 3454 | tpacpi_inputdev->close = &hotkey_inputdev_close; |
3437 | 3455 | ||
3438 | hotkey_poll_setup_safe(true); | 3456 | hotkey_poll_setup_safe(true); |
3439 | tpacpi_send_radiosw_update(); | ||
3440 | tpacpi_input_send_tabletsw(); | ||
3441 | 3457 | ||
3442 | return 0; | 3458 | return 0; |
3443 | 3459 | ||
@@ -3545,49 +3561,57 @@ static bool hotkey_notify_usrevent(const u32 hkey, | |||
3545 | } | 3561 | } |
3546 | } | 3562 | } |
3547 | 3563 | ||
3564 | static void thermal_dump_all_sensors(void); | ||
3565 | |||
3548 | static bool hotkey_notify_thermal(const u32 hkey, | 3566 | static bool hotkey_notify_thermal(const u32 hkey, |
3549 | bool *send_acpi_ev, | 3567 | bool *send_acpi_ev, |
3550 | bool *ignore_acpi_ev) | 3568 | bool *ignore_acpi_ev) |
3551 | { | 3569 | { |
3570 | bool known = true; | ||
3571 | |||
3552 | /* 0x6000-0x6FFF: thermal alarms */ | 3572 | /* 0x6000-0x6FFF: thermal alarms */ |
3553 | *send_acpi_ev = true; | 3573 | *send_acpi_ev = true; |
3554 | *ignore_acpi_ev = false; | 3574 | *ignore_acpi_ev = false; |
3555 | 3575 | ||
3556 | switch (hkey) { | 3576 | switch (hkey) { |
3577 | case TP_HKEY_EV_THM_TABLE_CHANGED: | ||
3578 | printk(TPACPI_INFO | ||
3579 | "EC reports that Thermal Table has changed\n"); | ||
3580 | /* recommended action: do nothing, we don't have | ||
3581 | * Lenovo ATM information */ | ||
3582 | return true; | ||
3557 | case TP_HKEY_EV_ALARM_BAT_HOT: | 3583 | case TP_HKEY_EV_ALARM_BAT_HOT: |
3558 | printk(TPACPI_CRIT | 3584 | printk(TPACPI_CRIT |
3559 | "THERMAL ALARM: battery is too hot!\n"); | 3585 | "THERMAL ALARM: battery is too hot!\n"); |
3560 | /* recommended action: warn user through gui */ | 3586 | /* recommended action: warn user through gui */ |
3561 | return true; | 3587 | break; |
3562 | case TP_HKEY_EV_ALARM_BAT_XHOT: | 3588 | case TP_HKEY_EV_ALARM_BAT_XHOT: |
3563 | printk(TPACPI_ALERT | 3589 | printk(TPACPI_ALERT |
3564 | "THERMAL EMERGENCY: battery is extremely hot!\n"); | 3590 | "THERMAL EMERGENCY: battery is extremely hot!\n"); |
3565 | /* recommended action: immediate sleep/hibernate */ | 3591 | /* recommended action: immediate sleep/hibernate */ |
3566 | return true; | 3592 | break; |
3567 | case TP_HKEY_EV_ALARM_SENSOR_HOT: | 3593 | case TP_HKEY_EV_ALARM_SENSOR_HOT: |
3568 | printk(TPACPI_CRIT | 3594 | printk(TPACPI_CRIT |
3569 | "THERMAL ALARM: " | 3595 | "THERMAL ALARM: " |
3570 | "a sensor reports something is too hot!\n"); | 3596 | "a sensor reports something is too hot!\n"); |
3571 | /* recommended action: warn user through gui, that */ | 3597 | /* recommended action: warn user through gui, that */ |
3572 | /* some internal component is too hot */ | 3598 | /* some internal component is too hot */ |
3573 | return true; | 3599 | break; |
3574 | case TP_HKEY_EV_ALARM_SENSOR_XHOT: | 3600 | case TP_HKEY_EV_ALARM_SENSOR_XHOT: |
3575 | printk(TPACPI_ALERT | 3601 | printk(TPACPI_ALERT |
3576 | "THERMAL EMERGENCY: " | 3602 | "THERMAL EMERGENCY: " |
3577 | "a sensor reports something is extremely hot!\n"); | 3603 | "a sensor reports something is extremely hot!\n"); |
3578 | /* recommended action: immediate sleep/hibernate */ | 3604 | /* recommended action: immediate sleep/hibernate */ |
3579 | return true; | 3605 | break; |
3580 | case TP_HKEY_EV_THM_TABLE_CHANGED: | ||
3581 | printk(TPACPI_INFO | ||
3582 | "EC reports that Thermal Table has changed\n"); | ||
3583 | /* recommended action: do nothing, we don't have | ||
3584 | * Lenovo ATM information */ | ||
3585 | return true; | ||
3586 | default: | 3606 | default: |
3587 | printk(TPACPI_ALERT | 3607 | printk(TPACPI_ALERT |
3588 | "THERMAL ALERT: unknown thermal alarm received\n"); | 3608 | "THERMAL ALERT: unknown thermal alarm received\n"); |
3589 | return false; | 3609 | known = false; |
3590 | } | 3610 | } |
3611 | |||
3612 | thermal_dump_all_sensors(); | ||
3613 | |||
3614 | return known; | ||
3591 | } | 3615 | } |
3592 | 3616 | ||
3593 | static void hotkey_notify(struct ibm_struct *ibm, u32 event) | 3617 | static void hotkey_notify(struct ibm_struct *ibm, u32 event) |
@@ -3635,13 +3659,19 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
3635 | break; | 3659 | break; |
3636 | case 3: | 3660 | case 3: |
3637 | /* 0x3000-0x3FFF: bay-related wakeups */ | 3661 | /* 0x3000-0x3FFF: bay-related wakeups */ |
3638 | if (hkey == TP_HKEY_EV_BAYEJ_ACK) { | 3662 | switch (hkey) { |
3663 | case TP_HKEY_EV_BAYEJ_ACK: | ||
3639 | hotkey_autosleep_ack = 1; | 3664 | hotkey_autosleep_ack = 1; |
3640 | printk(TPACPI_INFO | 3665 | printk(TPACPI_INFO |
3641 | "bay ejected\n"); | 3666 | "bay ejected\n"); |
3642 | hotkey_wakeup_hotunplug_complete_notify_change(); | 3667 | hotkey_wakeup_hotunplug_complete_notify_change(); |
3643 | known_ev = true; | 3668 | known_ev = true; |
3644 | } else { | 3669 | break; |
3670 | case TP_HKEY_EV_OPTDRV_EJ: | ||
3671 | /* FIXME: kick libata if SATA link offline */ | ||
3672 | known_ev = true; | ||
3673 | break; | ||
3674 | default: | ||
3645 | known_ev = false; | 3675 | known_ev = false; |
3646 | } | 3676 | } |
3647 | break; | 3677 | break; |
@@ -3730,14 +3760,13 @@ static void hotkey_resume(void) | |||
3730 | } | 3760 | } |
3731 | 3761 | ||
3732 | /* procfs -------------------------------------------------------------- */ | 3762 | /* procfs -------------------------------------------------------------- */ |
3733 | static int hotkey_read(char *p) | 3763 | static int hotkey_read(struct seq_file *m) |
3734 | { | 3764 | { |
3735 | int res, status; | 3765 | int res, status; |
3736 | int len = 0; | ||
3737 | 3766 | ||
3738 | if (!tp_features.hotkey) { | 3767 | if (!tp_features.hotkey) { |
3739 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 3768 | seq_printf(m, "status:\t\tnot supported\n"); |
3740 | return len; | 3769 | return 0; |
3741 | } | 3770 | } |
3742 | 3771 | ||
3743 | if (mutex_lock_killable(&hotkey_mutex)) | 3772 | if (mutex_lock_killable(&hotkey_mutex)) |
@@ -3749,17 +3778,16 @@ static int hotkey_read(char *p) | |||
3749 | if (res) | 3778 | if (res) |
3750 | return res; | 3779 | return res; |
3751 | 3780 | ||
3752 | len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0)); | 3781 | seq_printf(m, "status:\t\t%s\n", enabled(status, 0)); |
3753 | if (hotkey_all_mask) { | 3782 | if (hotkey_all_mask) { |
3754 | len += sprintf(p + len, "mask:\t\t0x%08x\n", hotkey_user_mask); | 3783 | seq_printf(m, "mask:\t\t0x%08x\n", hotkey_user_mask); |
3755 | len += sprintf(p + len, | 3784 | seq_printf(m, "commands:\tenable, disable, reset, <mask>\n"); |
3756 | "commands:\tenable, disable, reset, <mask>\n"); | ||
3757 | } else { | 3785 | } else { |
3758 | len += sprintf(p + len, "mask:\t\tnot supported\n"); | 3786 | seq_printf(m, "mask:\t\tnot supported\n"); |
3759 | len += sprintf(p + len, "commands:\tenable, disable, reset\n"); | 3787 | seq_printf(m, "commands:\tenable, disable, reset\n"); |
3760 | } | 3788 | } |
3761 | 3789 | ||
3762 | return len; | 3790 | return 0; |
3763 | } | 3791 | } |
3764 | 3792 | ||
3765 | static void hotkey_enabledisable_warn(bool enable) | 3793 | static void hotkey_enabledisable_warn(bool enable) |
@@ -3852,7 +3880,7 @@ enum { | |||
3852 | TP_ACPI_BLUETOOTH_HWPRESENT = 0x01, /* Bluetooth hw available */ | 3880 | TP_ACPI_BLUETOOTH_HWPRESENT = 0x01, /* Bluetooth hw available */ |
3853 | TP_ACPI_BLUETOOTH_RADIOSSW = 0x02, /* Bluetooth radio enabled */ | 3881 | TP_ACPI_BLUETOOTH_RADIOSSW = 0x02, /* Bluetooth radio enabled */ |
3854 | TP_ACPI_BLUETOOTH_RESUMECTRL = 0x04, /* Bluetooth state at resume: | 3882 | TP_ACPI_BLUETOOTH_RESUMECTRL = 0x04, /* Bluetooth state at resume: |
3855 | off / last state */ | 3883 | 0 = disable, 1 = enable */ |
3856 | }; | 3884 | }; |
3857 | 3885 | ||
3858 | enum { | 3886 | enum { |
@@ -3866,15 +3894,6 @@ enum { | |||
3866 | 3894 | ||
3867 | #define TPACPI_RFK_BLUETOOTH_SW_NAME "tpacpi_bluetooth_sw" | 3895 | #define TPACPI_RFK_BLUETOOTH_SW_NAME "tpacpi_bluetooth_sw" |
3868 | 3896 | ||
3869 | static void bluetooth_suspend(pm_message_t state) | ||
3870 | { | ||
3871 | /* Try to make sure radio will resume powered off */ | ||
3872 | if (!acpi_evalf(NULL, NULL, "\\BLTH", "vd", | ||
3873 | TP_ACPI_BLTH_PWR_OFF_ON_RESUME)) | ||
3874 | vdbg_printk(TPACPI_DBG_RFKILL, | ||
3875 | "bluetooth power down on resume request failed\n"); | ||
3876 | } | ||
3877 | |||
3878 | static int bluetooth_get_status(void) | 3897 | static int bluetooth_get_status(void) |
3879 | { | 3898 | { |
3880 | int status; | 3899 | int status; |
@@ -3907,9 +3926,9 @@ static int bluetooth_set_status(enum tpacpi_rfkill_state state) | |||
3907 | } | 3926 | } |
3908 | #endif | 3927 | #endif |
3909 | 3928 | ||
3910 | /* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */ | ||
3911 | if (state == TPACPI_RFK_RADIO_ON) | 3929 | if (state == TPACPI_RFK_RADIO_ON) |
3912 | status = TP_ACPI_BLUETOOTH_RADIOSSW; | 3930 | status = TP_ACPI_BLUETOOTH_RADIOSSW |
3931 | | TP_ACPI_BLUETOOTH_RESUMECTRL; | ||
3913 | else | 3932 | else |
3914 | status = 0; | 3933 | status = 0; |
3915 | 3934 | ||
@@ -4035,9 +4054,9 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) | |||
4035 | } | 4054 | } |
4036 | 4055 | ||
4037 | /* procfs -------------------------------------------------------------- */ | 4056 | /* procfs -------------------------------------------------------------- */ |
4038 | static int bluetooth_read(char *p) | 4057 | static int bluetooth_read(struct seq_file *m) |
4039 | { | 4058 | { |
4040 | return tpacpi_rfk_procfs_read(TPACPI_RFK_BLUETOOTH_SW_ID, p); | 4059 | return tpacpi_rfk_procfs_read(TPACPI_RFK_BLUETOOTH_SW_ID, m); |
4041 | } | 4060 | } |
4042 | 4061 | ||
4043 | static int bluetooth_write(char *buf) | 4062 | static int bluetooth_write(char *buf) |
@@ -4050,7 +4069,6 @@ static struct ibm_struct bluetooth_driver_data = { | |||
4050 | .read = bluetooth_read, | 4069 | .read = bluetooth_read, |
4051 | .write = bluetooth_write, | 4070 | .write = bluetooth_write, |
4052 | .exit = bluetooth_exit, | 4071 | .exit = bluetooth_exit, |
4053 | .suspend = bluetooth_suspend, | ||
4054 | .shutdown = bluetooth_shutdown, | 4072 | .shutdown = bluetooth_shutdown, |
4055 | }; | 4073 | }; |
4056 | 4074 | ||
@@ -4063,20 +4081,11 @@ enum { | |||
4063 | TP_ACPI_WANCARD_HWPRESENT = 0x01, /* Wan hw available */ | 4081 | TP_ACPI_WANCARD_HWPRESENT = 0x01, /* Wan hw available */ |
4064 | TP_ACPI_WANCARD_RADIOSSW = 0x02, /* Wan radio enabled */ | 4082 | TP_ACPI_WANCARD_RADIOSSW = 0x02, /* Wan radio enabled */ |
4065 | TP_ACPI_WANCARD_RESUMECTRL = 0x04, /* Wan state at resume: | 4083 | TP_ACPI_WANCARD_RESUMECTRL = 0x04, /* Wan state at resume: |
4066 | off / last state */ | 4084 | 0 = disable, 1 = enable */ |
4067 | }; | 4085 | }; |
4068 | 4086 | ||
4069 | #define TPACPI_RFK_WWAN_SW_NAME "tpacpi_wwan_sw" | 4087 | #define TPACPI_RFK_WWAN_SW_NAME "tpacpi_wwan_sw" |
4070 | 4088 | ||
4071 | static void wan_suspend(pm_message_t state) | ||
4072 | { | ||
4073 | /* Try to make sure radio will resume powered off */ | ||
4074 | if (!acpi_evalf(NULL, NULL, "\\WGSV", "qvd", | ||
4075 | TP_ACPI_WGSV_PWR_OFF_ON_RESUME)) | ||
4076 | vdbg_printk(TPACPI_DBG_RFKILL, | ||
4077 | "WWAN power down on resume request failed\n"); | ||
4078 | } | ||
4079 | |||
4080 | static int wan_get_status(void) | 4089 | static int wan_get_status(void) |
4081 | { | 4090 | { |
4082 | int status; | 4091 | int status; |
@@ -4109,9 +4118,9 @@ static int wan_set_status(enum tpacpi_rfkill_state state) | |||
4109 | } | 4118 | } |
4110 | #endif | 4119 | #endif |
4111 | 4120 | ||
4112 | /* We make sure to keep TP_ACPI_WANCARD_RESUMECTRL off */ | ||
4113 | if (state == TPACPI_RFK_RADIO_ON) | 4121 | if (state == TPACPI_RFK_RADIO_ON) |
4114 | status = TP_ACPI_WANCARD_RADIOSSW; | 4122 | status = TP_ACPI_WANCARD_RADIOSSW |
4123 | | TP_ACPI_WANCARD_RESUMECTRL; | ||
4115 | else | 4124 | else |
4116 | status = 0; | 4125 | status = 0; |
4117 | 4126 | ||
@@ -4236,9 +4245,9 @@ static int __init wan_init(struct ibm_init_struct *iibm) | |||
4236 | } | 4245 | } |
4237 | 4246 | ||
4238 | /* procfs -------------------------------------------------------------- */ | 4247 | /* procfs -------------------------------------------------------------- */ |
4239 | static int wan_read(char *p) | 4248 | static int wan_read(struct seq_file *m) |
4240 | { | 4249 | { |
4241 | return tpacpi_rfk_procfs_read(TPACPI_RFK_WWAN_SW_ID, p); | 4250 | return tpacpi_rfk_procfs_read(TPACPI_RFK_WWAN_SW_ID, m); |
4242 | } | 4251 | } |
4243 | 4252 | ||
4244 | static int wan_write(char *buf) | 4253 | static int wan_write(char *buf) |
@@ -4251,7 +4260,6 @@ static struct ibm_struct wan_driver_data = { | |||
4251 | .read = wan_read, | 4260 | .read = wan_read, |
4252 | .write = wan_write, | 4261 | .write = wan_write, |
4253 | .exit = wan_exit, | 4262 | .exit = wan_exit, |
4254 | .suspend = wan_suspend, | ||
4255 | .shutdown = wan_shutdown, | 4263 | .shutdown = wan_shutdown, |
4256 | }; | 4264 | }; |
4257 | 4265 | ||
@@ -4614,16 +4622,19 @@ static int video_expand_toggle(void) | |||
4614 | /* not reached */ | 4622 | /* not reached */ |
4615 | } | 4623 | } |
4616 | 4624 | ||
4617 | static int video_read(char *p) | 4625 | static int video_read(struct seq_file *m) |
4618 | { | 4626 | { |
4619 | int status, autosw; | 4627 | int status, autosw; |
4620 | int len = 0; | ||
4621 | 4628 | ||
4622 | if (video_supported == TPACPI_VIDEO_NONE) { | 4629 | if (video_supported == TPACPI_VIDEO_NONE) { |
4623 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 4630 | seq_printf(m, "status:\t\tnot supported\n"); |
4624 | return len; | 4631 | return 0; |
4625 | } | 4632 | } |
4626 | 4633 | ||
4634 | /* Even reads can crash X.org, so... */ | ||
4635 | if (!capable(CAP_SYS_ADMIN)) | ||
4636 | return -EPERM; | ||
4637 | |||
4627 | status = video_outputsw_get(); | 4638 | status = video_outputsw_get(); |
4628 | if (status < 0) | 4639 | if (status < 0) |
4629 | return status; | 4640 | return status; |
@@ -4632,20 +4643,20 @@ static int video_read(char *p) | |||
4632 | if (autosw < 0) | 4643 | if (autosw < 0) |
4633 | return autosw; | 4644 | return autosw; |
4634 | 4645 | ||
4635 | len += sprintf(p + len, "status:\t\tsupported\n"); | 4646 | seq_printf(m, "status:\t\tsupported\n"); |
4636 | len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0)); | 4647 | seq_printf(m, "lcd:\t\t%s\n", enabled(status, 0)); |
4637 | len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1)); | 4648 | seq_printf(m, "crt:\t\t%s\n", enabled(status, 1)); |
4638 | if (video_supported == TPACPI_VIDEO_NEW) | 4649 | if (video_supported == TPACPI_VIDEO_NEW) |
4639 | len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3)); | 4650 | seq_printf(m, "dvi:\t\t%s\n", enabled(status, 3)); |
4640 | len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0)); | 4651 | seq_printf(m, "auto:\t\t%s\n", enabled(autosw, 0)); |
4641 | len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n"); | 4652 | seq_printf(m, "commands:\tlcd_enable, lcd_disable\n"); |
4642 | len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n"); | 4653 | seq_printf(m, "commands:\tcrt_enable, crt_disable\n"); |
4643 | if (video_supported == TPACPI_VIDEO_NEW) | 4654 | if (video_supported == TPACPI_VIDEO_NEW) |
4644 | len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n"); | 4655 | seq_printf(m, "commands:\tdvi_enable, dvi_disable\n"); |
4645 | len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n"); | 4656 | seq_printf(m, "commands:\tauto_enable, auto_disable\n"); |
4646 | len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n"); | 4657 | seq_printf(m, "commands:\tvideo_switch, expand_toggle\n"); |
4647 | 4658 | ||
4648 | return len; | 4659 | return 0; |
4649 | } | 4660 | } |
4650 | 4661 | ||
4651 | static int video_write(char *buf) | 4662 | static int video_write(char *buf) |
@@ -4657,6 +4668,10 @@ static int video_write(char *buf) | |||
4657 | if (video_supported == TPACPI_VIDEO_NONE) | 4668 | if (video_supported == TPACPI_VIDEO_NONE) |
4658 | return -ENODEV; | 4669 | return -ENODEV; |
4659 | 4670 | ||
4671 | /* Even reads can crash X.org, let alone writes... */ | ||
4672 | if (!capable(CAP_SYS_ADMIN)) | ||
4673 | return -EPERM; | ||
4674 | |||
4660 | enable = 0; | 4675 | enable = 0; |
4661 | disable = 0; | 4676 | disable = 0; |
4662 | 4677 | ||
@@ -4837,25 +4852,24 @@ static void light_exit(void) | |||
4837 | flush_workqueue(tpacpi_wq); | 4852 | flush_workqueue(tpacpi_wq); |
4838 | } | 4853 | } |
4839 | 4854 | ||
4840 | static int light_read(char *p) | 4855 | static int light_read(struct seq_file *m) |
4841 | { | 4856 | { |
4842 | int len = 0; | ||
4843 | int status; | 4857 | int status; |
4844 | 4858 | ||
4845 | if (!tp_features.light) { | 4859 | if (!tp_features.light) { |
4846 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 4860 | seq_printf(m, "status:\t\tnot supported\n"); |
4847 | } else if (!tp_features.light_status) { | 4861 | } else if (!tp_features.light_status) { |
4848 | len += sprintf(p + len, "status:\t\tunknown\n"); | 4862 | seq_printf(m, "status:\t\tunknown\n"); |
4849 | len += sprintf(p + len, "commands:\ton, off\n"); | 4863 | seq_printf(m, "commands:\ton, off\n"); |
4850 | } else { | 4864 | } else { |
4851 | status = light_get_status(); | 4865 | status = light_get_status(); |
4852 | if (status < 0) | 4866 | if (status < 0) |
4853 | return status; | 4867 | return status; |
4854 | len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0)); | 4868 | seq_printf(m, "status:\t\t%s\n", onoff(status, 0)); |
4855 | len += sprintf(p + len, "commands:\ton, off\n"); | 4869 | seq_printf(m, "commands:\ton, off\n"); |
4856 | } | 4870 | } |
4857 | 4871 | ||
4858 | return len; | 4872 | return 0; |
4859 | } | 4873 | } |
4860 | 4874 | ||
4861 | static int light_write(char *buf) | 4875 | static int light_write(char *buf) |
@@ -4933,20 +4947,18 @@ static void cmos_exit(void) | |||
4933 | device_remove_file(&tpacpi_pdev->dev, &dev_attr_cmos_command); | 4947 | device_remove_file(&tpacpi_pdev->dev, &dev_attr_cmos_command); |
4934 | } | 4948 | } |
4935 | 4949 | ||
4936 | static int cmos_read(char *p) | 4950 | static int cmos_read(struct seq_file *m) |
4937 | { | 4951 | { |
4938 | int len = 0; | ||
4939 | |||
4940 | /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, | 4952 | /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, |
4941 | R30, R31, T20-22, X20-21 */ | 4953 | R30, R31, T20-22, X20-21 */ |
4942 | if (!cmos_handle) | 4954 | if (!cmos_handle) |
4943 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 4955 | seq_printf(m, "status:\t\tnot supported\n"); |
4944 | else { | 4956 | else { |
4945 | len += sprintf(p + len, "status:\t\tsupported\n"); | 4957 | seq_printf(m, "status:\t\tsupported\n"); |
4946 | len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-21)\n"); | 4958 | seq_printf(m, "commands:\t<cmd> (<cmd> is 0-21)\n"); |
4947 | } | 4959 | } |
4948 | 4960 | ||
4949 | return len; | 4961 | return 0; |
4950 | } | 4962 | } |
4951 | 4963 | ||
4952 | static int cmos_write(char *buf) | 4964 | static int cmos_write(char *buf) |
@@ -5321,15 +5333,13 @@ static int __init led_init(struct ibm_init_struct *iibm) | |||
5321 | ((s) == TPACPI_LED_OFF ? "off" : \ | 5333 | ((s) == TPACPI_LED_OFF ? "off" : \ |
5322 | ((s) == TPACPI_LED_ON ? "on" : "blinking")) | 5334 | ((s) == TPACPI_LED_ON ? "on" : "blinking")) |
5323 | 5335 | ||
5324 | static int led_read(char *p) | 5336 | static int led_read(struct seq_file *m) |
5325 | { | 5337 | { |
5326 | int len = 0; | ||
5327 | |||
5328 | if (!led_supported) { | 5338 | if (!led_supported) { |
5329 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 5339 | seq_printf(m, "status:\t\tnot supported\n"); |
5330 | return len; | 5340 | return 0; |
5331 | } | 5341 | } |
5332 | len += sprintf(p + len, "status:\t\tsupported\n"); | 5342 | seq_printf(m, "status:\t\tsupported\n"); |
5333 | 5343 | ||
5334 | if (led_supported == TPACPI_LED_570) { | 5344 | if (led_supported == TPACPI_LED_570) { |
5335 | /* 570 */ | 5345 | /* 570 */ |
@@ -5338,15 +5348,15 @@ static int led_read(char *p) | |||
5338 | status = led_get_status(i); | 5348 | status = led_get_status(i); |
5339 | if (status < 0) | 5349 | if (status < 0) |
5340 | return -EIO; | 5350 | return -EIO; |
5341 | len += sprintf(p + len, "%d:\t\t%s\n", | 5351 | seq_printf(m, "%d:\t\t%s\n", |
5342 | i, str_led_status(status)); | 5352 | i, str_led_status(status)); |
5343 | } | 5353 | } |
5344 | } | 5354 | } |
5345 | 5355 | ||
5346 | len += sprintf(p + len, "commands:\t" | 5356 | seq_printf(m, "commands:\t" |
5347 | "<led> on, <led> off, <led> blink (<led> is 0-15)\n"); | 5357 | "<led> on, <led> off, <led> blink (<led> is 0-15)\n"); |
5348 | 5358 | ||
5349 | return len; | 5359 | return 0; |
5350 | } | 5360 | } |
5351 | 5361 | ||
5352 | static int led_write(char *buf) | 5362 | static int led_write(char *buf) |
@@ -5419,18 +5429,16 @@ static int __init beep_init(struct ibm_init_struct *iibm) | |||
5419 | return (beep_handle)? 0 : 1; | 5429 | return (beep_handle)? 0 : 1; |
5420 | } | 5430 | } |
5421 | 5431 | ||
5422 | static int beep_read(char *p) | 5432 | static int beep_read(struct seq_file *m) |
5423 | { | 5433 | { |
5424 | int len = 0; | ||
5425 | |||
5426 | if (!beep_handle) | 5434 | if (!beep_handle) |
5427 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 5435 | seq_printf(m, "status:\t\tnot supported\n"); |
5428 | else { | 5436 | else { |
5429 | len += sprintf(p + len, "status:\t\tsupported\n"); | 5437 | seq_printf(m, "status:\t\tsupported\n"); |
5430 | len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-17)\n"); | 5438 | seq_printf(m, "commands:\t<cmd> (<cmd> is 0-17)\n"); |
5431 | } | 5439 | } |
5432 | 5440 | ||
5433 | return len; | 5441 | return 0; |
5434 | } | 5442 | } |
5435 | 5443 | ||
5436 | static int beep_write(char *buf) | 5444 | static int beep_write(char *buf) |
@@ -5483,8 +5491,11 @@ enum { /* TPACPI_THERMAL_TPEC_* */ | |||
5483 | TP_EC_THERMAL_TMP0 = 0x78, /* ACPI EC regs TMP 0..7 */ | 5491 | TP_EC_THERMAL_TMP0 = 0x78, /* ACPI EC regs TMP 0..7 */ |
5484 | TP_EC_THERMAL_TMP8 = 0xC0, /* ACPI EC regs TMP 8..15 */ | 5492 | TP_EC_THERMAL_TMP8 = 0xC0, /* ACPI EC regs TMP 8..15 */ |
5485 | TP_EC_THERMAL_TMP_NA = -128, /* ACPI EC sensor not available */ | 5493 | TP_EC_THERMAL_TMP_NA = -128, /* ACPI EC sensor not available */ |
5494 | |||
5495 | TPACPI_THERMAL_SENSOR_NA = -128000, /* Sensor not available */ | ||
5486 | }; | 5496 | }; |
5487 | 5497 | ||
5498 | |||
5488 | #define TPACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */ | 5499 | #define TPACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */ |
5489 | struct ibm_thermal_sensors_struct { | 5500 | struct ibm_thermal_sensors_struct { |
5490 | s32 temp[TPACPI_MAX_THERMAL_SENSORS]; | 5501 | s32 temp[TPACPI_MAX_THERMAL_SENSORS]; |
@@ -5574,6 +5585,28 @@ static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s) | |||
5574 | return n; | 5585 | return n; |
5575 | } | 5586 | } |
5576 | 5587 | ||
5588 | static void thermal_dump_all_sensors(void) | ||
5589 | { | ||
5590 | int n, i; | ||
5591 | struct ibm_thermal_sensors_struct t; | ||
5592 | |||
5593 | n = thermal_get_sensors(&t); | ||
5594 | if (n <= 0) | ||
5595 | return; | ||
5596 | |||
5597 | printk(TPACPI_NOTICE | ||
5598 | "temperatures (Celsius):"); | ||
5599 | |||
5600 | for (i = 0; i < n; i++) { | ||
5601 | if (t.temp[i] != TPACPI_THERMAL_SENSOR_NA) | ||
5602 | printk(KERN_CONT " %d", (int)(t.temp[i] / 1000)); | ||
5603 | else | ||
5604 | printk(KERN_CONT " N/A"); | ||
5605 | } | ||
5606 | |||
5607 | printk(KERN_CONT "\n"); | ||
5608 | } | ||
5609 | |||
5577 | /* sysfs temp##_input -------------------------------------------------- */ | 5610 | /* sysfs temp##_input -------------------------------------------------- */ |
5578 | 5611 | ||
5579 | static ssize_t thermal_temp_input_show(struct device *dev, | 5612 | static ssize_t thermal_temp_input_show(struct device *dev, |
@@ -5589,7 +5622,7 @@ static ssize_t thermal_temp_input_show(struct device *dev, | |||
5589 | res = thermal_get_sensor(idx, &value); | 5622 | res = thermal_get_sensor(idx, &value); |
5590 | if (res) | 5623 | if (res) |
5591 | return res; | 5624 | return res; |
5592 | if (value == TP_EC_THERMAL_TMP_NA * 1000) | 5625 | if (value == TPACPI_THERMAL_SENSOR_NA) |
5593 | return -ENXIO; | 5626 | return -ENXIO; |
5594 | 5627 | ||
5595 | return snprintf(buf, PAGE_SIZE, "%d\n", value); | 5628 | return snprintf(buf, PAGE_SIZE, "%d\n", value); |
@@ -5758,7 +5791,7 @@ static void thermal_exit(void) | |||
5758 | case TPACPI_THERMAL_ACPI_TMP07: | 5791 | case TPACPI_THERMAL_ACPI_TMP07: |
5759 | case TPACPI_THERMAL_ACPI_UPDT: | 5792 | case TPACPI_THERMAL_ACPI_UPDT: |
5760 | sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj, | 5793 | sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj, |
5761 | &thermal_temp_input16_group); | 5794 | &thermal_temp_input8_group); |
5762 | break; | 5795 | break; |
5763 | case TPACPI_THERMAL_NONE: | 5796 | case TPACPI_THERMAL_NONE: |
5764 | default: | 5797 | default: |
@@ -5766,9 +5799,8 @@ static void thermal_exit(void) | |||
5766 | } | 5799 | } |
5767 | } | 5800 | } |
5768 | 5801 | ||
5769 | static int thermal_read(char *p) | 5802 | static int thermal_read(struct seq_file *m) |
5770 | { | 5803 | { |
5771 | int len = 0; | ||
5772 | int n, i; | 5804 | int n, i; |
5773 | struct ibm_thermal_sensors_struct t; | 5805 | struct ibm_thermal_sensors_struct t; |
5774 | 5806 | ||
@@ -5776,16 +5808,16 @@ static int thermal_read(char *p) | |||
5776 | if (unlikely(n < 0)) | 5808 | if (unlikely(n < 0)) |
5777 | return n; | 5809 | return n; |
5778 | 5810 | ||
5779 | len += sprintf(p + len, "temperatures:\t"); | 5811 | seq_printf(m, "temperatures:\t"); |
5780 | 5812 | ||
5781 | if (n > 0) { | 5813 | if (n > 0) { |
5782 | for (i = 0; i < (n - 1); i++) | 5814 | for (i = 0; i < (n - 1); i++) |
5783 | len += sprintf(p + len, "%d ", t.temp[i] / 1000); | 5815 | seq_printf(m, "%d ", t.temp[i] / 1000); |
5784 | len += sprintf(p + len, "%d\n", t.temp[i] / 1000); | 5816 | seq_printf(m, "%d\n", t.temp[i] / 1000); |
5785 | } else | 5817 | } else |
5786 | len += sprintf(p + len, "not supported\n"); | 5818 | seq_printf(m, "not supported\n"); |
5787 | 5819 | ||
5788 | return len; | 5820 | return 0; |
5789 | } | 5821 | } |
5790 | 5822 | ||
5791 | static struct ibm_struct thermal_driver_data = { | 5823 | static struct ibm_struct thermal_driver_data = { |
@@ -5800,39 +5832,38 @@ static struct ibm_struct thermal_driver_data = { | |||
5800 | 5832 | ||
5801 | static u8 ecdump_regs[256]; | 5833 | static u8 ecdump_regs[256]; |
5802 | 5834 | ||
5803 | static int ecdump_read(char *p) | 5835 | static int ecdump_read(struct seq_file *m) |
5804 | { | 5836 | { |
5805 | int len = 0; | ||
5806 | int i, j; | 5837 | int i, j; |
5807 | u8 v; | 5838 | u8 v; |
5808 | 5839 | ||
5809 | len += sprintf(p + len, "EC " | 5840 | seq_printf(m, "EC " |
5810 | " +00 +01 +02 +03 +04 +05 +06 +07" | 5841 | " +00 +01 +02 +03 +04 +05 +06 +07" |
5811 | " +08 +09 +0a +0b +0c +0d +0e +0f\n"); | 5842 | " +08 +09 +0a +0b +0c +0d +0e +0f\n"); |
5812 | for (i = 0; i < 256; i += 16) { | 5843 | for (i = 0; i < 256; i += 16) { |
5813 | len += sprintf(p + len, "EC 0x%02x:", i); | 5844 | seq_printf(m, "EC 0x%02x:", i); |
5814 | for (j = 0; j < 16; j++) { | 5845 | for (j = 0; j < 16; j++) { |
5815 | if (!acpi_ec_read(i + j, &v)) | 5846 | if (!acpi_ec_read(i + j, &v)) |
5816 | break; | 5847 | break; |
5817 | if (v != ecdump_regs[i + j]) | 5848 | if (v != ecdump_regs[i + j]) |
5818 | len += sprintf(p + len, " *%02x", v); | 5849 | seq_printf(m, " *%02x", v); |
5819 | else | 5850 | else |
5820 | len += sprintf(p + len, " %02x", v); | 5851 | seq_printf(m, " %02x", v); |
5821 | ecdump_regs[i + j] = v; | 5852 | ecdump_regs[i + j] = v; |
5822 | } | 5853 | } |
5823 | len += sprintf(p + len, "\n"); | 5854 | seq_putc(m, '\n'); |
5824 | if (j != 16) | 5855 | if (j != 16) |
5825 | break; | 5856 | break; |
5826 | } | 5857 | } |
5827 | 5858 | ||
5828 | /* These are way too dangerous to advertise openly... */ | 5859 | /* These are way too dangerous to advertise openly... */ |
5829 | #if 0 | 5860 | #if 0 |
5830 | len += sprintf(p + len, "commands:\t0x<offset> 0x<value>" | 5861 | seq_printf(m, "commands:\t0x<offset> 0x<value>" |
5831 | " (<offset> is 00-ff, <value> is 00-ff)\n"); | 5862 | " (<offset> is 00-ff, <value> is 00-ff)\n"); |
5832 | len += sprintf(p + len, "commands:\t0x<offset> <value> " | 5863 | seq_printf(m, "commands:\t0x<offset> <value> " |
5833 | " (<offset> is 00-ff, <value> is 0-255)\n"); | 5864 | " (<offset> is 00-ff, <value> is 0-255)\n"); |
5834 | #endif | 5865 | #endif |
5835 | return len; | 5866 | return 0; |
5836 | } | 5867 | } |
5837 | 5868 | ||
5838 | static int ecdump_write(char *buf) | 5869 | static int ecdump_write(char *buf) |
@@ -6095,6 +6126,12 @@ static int brightness_get(struct backlight_device *bd) | |||
6095 | return status & TP_EC_BACKLIGHT_LVLMSK; | 6126 | return status & TP_EC_BACKLIGHT_LVLMSK; |
6096 | } | 6127 | } |
6097 | 6128 | ||
6129 | static void tpacpi_brightness_notify_change(void) | ||
6130 | { | ||
6131 | backlight_force_update(ibm_backlight_device, | ||
6132 | BACKLIGHT_UPDATE_HOTKEY); | ||
6133 | } | ||
6134 | |||
6098 | static struct backlight_ops ibm_backlight_data = { | 6135 | static struct backlight_ops ibm_backlight_data = { |
6099 | .get_brightness = brightness_get, | 6136 | .get_brightness = brightness_get, |
6100 | .update_status = brightness_update_status, | 6137 | .update_status = brightness_update_status, |
@@ -6116,15 +6153,15 @@ static const struct tpacpi_quirk brightness_quirk_table[] __initconst = { | |||
6116 | TPACPI_Q_IBM('1', 'Y', TPACPI_BRGHT_Q_EC), /* T43/p ATI */ | 6153 | TPACPI_Q_IBM('1', 'Y', TPACPI_BRGHT_Q_EC), /* T43/p ATI */ |
6117 | 6154 | ||
6118 | /* Models with ATI GPUs that can use ECNVRAM */ | 6155 | /* Models with ATI GPUs that can use ECNVRAM */ |
6119 | TPACPI_Q_IBM('1', 'R', TPACPI_BRGHT_Q_EC), | 6156 | TPACPI_Q_IBM('1', 'R', TPACPI_BRGHT_Q_EC), /* R50,51 T40-42 */ |
6120 | TPACPI_Q_IBM('1', 'Q', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), | 6157 | TPACPI_Q_IBM('1', 'Q', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), |
6121 | TPACPI_Q_IBM('7', '6', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), | 6158 | TPACPI_Q_IBM('7', '6', TPACPI_BRGHT_Q_EC), /* R52 */ |
6122 | TPACPI_Q_IBM('7', '8', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), | 6159 | TPACPI_Q_IBM('7', '8', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), |
6123 | 6160 | ||
6124 | /* Models with Intel Extreme Graphics 2 */ | 6161 | /* Models with Intel Extreme Graphics 2 */ |
6125 | TPACPI_Q_IBM('1', 'U', TPACPI_BRGHT_Q_NOEC), | 6162 | TPACPI_Q_IBM('1', 'U', TPACPI_BRGHT_Q_NOEC), /* X40 */ |
6126 | TPACPI_Q_IBM('1', 'V', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC), | 6163 | TPACPI_Q_IBM('1', 'V', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), |
6127 | TPACPI_Q_IBM('1', 'W', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC), | 6164 | TPACPI_Q_IBM('1', 'W', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), |
6128 | 6165 | ||
6129 | /* Models with Intel GMA900 */ | 6166 | /* Models with Intel GMA900 */ |
6130 | TPACPI_Q_IBM('7', '0', TPACPI_BRGHT_Q_NOEC), /* T43, R52 */ | 6167 | TPACPI_Q_IBM('7', '0', TPACPI_BRGHT_Q_NOEC), /* T43, R52 */ |
@@ -6134,6 +6171,7 @@ static const struct tpacpi_quirk brightness_quirk_table[] __initconst = { | |||
6134 | 6171 | ||
6135 | static int __init brightness_init(struct ibm_init_struct *iibm) | 6172 | static int __init brightness_init(struct ibm_init_struct *iibm) |
6136 | { | 6173 | { |
6174 | struct backlight_properties props; | ||
6137 | int b; | 6175 | int b; |
6138 | unsigned long quirks; | 6176 | unsigned long quirks; |
6139 | 6177 | ||
@@ -6223,9 +6261,12 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
6223 | printk(TPACPI_INFO | 6261 | printk(TPACPI_INFO |
6224 | "detected a 16-level brightness capable ThinkPad\n"); | 6262 | "detected a 16-level brightness capable ThinkPad\n"); |
6225 | 6263 | ||
6226 | ibm_backlight_device = backlight_device_register( | 6264 | memset(&props, 0, sizeof(struct backlight_properties)); |
6227 | TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL, | 6265 | props.max_brightness = (tp_features.bright_16levels) ? 15 : 7; |
6228 | &ibm_backlight_data); | 6266 | ibm_backlight_device = backlight_device_register(TPACPI_BACKLIGHT_DEV_NAME, |
6267 | NULL, NULL, | ||
6268 | &ibm_backlight_data, | ||
6269 | &props); | ||
6229 | if (IS_ERR(ibm_backlight_device)) { | 6270 | if (IS_ERR(ibm_backlight_device)) { |
6230 | int rc = PTR_ERR(ibm_backlight_device); | 6271 | int rc = PTR_ERR(ibm_backlight_device); |
6231 | ibm_backlight_device = NULL; | 6272 | ibm_backlight_device = NULL; |
@@ -6244,11 +6285,15 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
6244 | "or not on your ThinkPad\n", TPACPI_MAIL); | 6285 | "or not on your ThinkPad\n", TPACPI_MAIL); |
6245 | } | 6286 | } |
6246 | 6287 | ||
6247 | ibm_backlight_device->props.max_brightness = | ||
6248 | (tp_features.bright_16levels)? 15 : 7; | ||
6249 | ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK; | 6288 | ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK; |
6250 | backlight_update_status(ibm_backlight_device); | 6289 | backlight_update_status(ibm_backlight_device); |
6251 | 6290 | ||
6291 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, | ||
6292 | "brightness: registering brightness hotkeys " | ||
6293 | "as change notification\n"); | ||
6294 | tpacpi_hotkey_driver_mask_set(hotkey_driver_mask | ||
6295 | | TP_ACPI_HKEY_BRGHTUP_MASK | ||
6296 | | TP_ACPI_HKEY_BRGHTDWN_MASK);; | ||
6252 | return 0; | 6297 | return 0; |
6253 | } | 6298 | } |
6254 | 6299 | ||
@@ -6273,23 +6318,22 @@ static void brightness_exit(void) | |||
6273 | tpacpi_brightness_checkpoint_nvram(); | 6318 | tpacpi_brightness_checkpoint_nvram(); |
6274 | } | 6319 | } |
6275 | 6320 | ||
6276 | static int brightness_read(char *p) | 6321 | static int brightness_read(struct seq_file *m) |
6277 | { | 6322 | { |
6278 | int len = 0; | ||
6279 | int level; | 6323 | int level; |
6280 | 6324 | ||
6281 | level = brightness_get(NULL); | 6325 | level = brightness_get(NULL); |
6282 | if (level < 0) { | 6326 | if (level < 0) { |
6283 | len += sprintf(p + len, "level:\t\tunreadable\n"); | 6327 | seq_printf(m, "level:\t\tunreadable\n"); |
6284 | } else { | 6328 | } else { |
6285 | len += sprintf(p + len, "level:\t\t%d\n", level); | 6329 | seq_printf(m, "level:\t\t%d\n", level); |
6286 | len += sprintf(p + len, "commands:\tup, down\n"); | 6330 | seq_printf(m, "commands:\tup, down\n"); |
6287 | len += sprintf(p + len, "commands:\tlevel <level>" | 6331 | seq_printf(m, "commands:\tlevel <level>" |
6288 | " (<level> is 0-%d)\n", | 6332 | " (<level> is 0-%d)\n", |
6289 | (tp_features.bright_16levels) ? 15 : 7); | 6333 | (tp_features.bright_16levels) ? 15 : 7); |
6290 | } | 6334 | } |
6291 | 6335 | ||
6292 | return len; | 6336 | return 0; |
6293 | } | 6337 | } |
6294 | 6338 | ||
6295 | static int brightness_write(char *buf) | 6339 | static int brightness_write(char *buf) |
@@ -6325,6 +6369,9 @@ static int brightness_write(char *buf) | |||
6325 | * Doing it this way makes the syscall restartable in case of EINTR | 6369 | * Doing it this way makes the syscall restartable in case of EINTR |
6326 | */ | 6370 | */ |
6327 | rc = brightness_set(level); | 6371 | rc = brightness_set(level); |
6372 | if (!rc && ibm_backlight_device) | ||
6373 | backlight_force_update(ibm_backlight_device, | ||
6374 | BACKLIGHT_UPDATE_SYSFS); | ||
6328 | return (rc == -EINTR)? -ERESTARTSYS : rc; | 6375 | return (rc == -EINTR)? -ERESTARTSYS : rc; |
6329 | } | 6376 | } |
6330 | 6377 | ||
@@ -6341,101 +6388,704 @@ static struct ibm_struct brightness_driver_data = { | |||
6341 | * Volume subdriver | 6388 | * Volume subdriver |
6342 | */ | 6389 | */ |
6343 | 6390 | ||
6344 | static int volume_offset = 0x30; | 6391 | /* |
6392 | * IBM ThinkPads have a simple volume controller with MUTE gating. | ||
6393 | * Very early Lenovo ThinkPads follow the IBM ThinkPad spec. | ||
6394 | * | ||
6395 | * Since the *61 series (and probably also the later *60 series), Lenovo | ||
6396 | * ThinkPads only implement the MUTE gate. | ||
6397 | * | ||
6398 | * EC register 0x30 | ||
6399 | * Bit 6: MUTE (1 mutes sound) | ||
6400 | * Bit 3-0: Volume | ||
6401 | * Other bits should be zero as far as we know. | ||
6402 | * | ||
6403 | * This is also stored in CMOS NVRAM, byte 0x60, bit 6 (MUTE), and | ||
6404 | * bits 3-0 (volume). Other bits in NVRAM may have other functions, | ||
6405 | * such as bit 7 which is used to detect repeated presses of MUTE, | ||
6406 | * and we leave them unchanged. | ||
6407 | */ | ||
6408 | |||
6409 | #ifdef CONFIG_THINKPAD_ACPI_ALSA_SUPPORT | ||
6410 | |||
6411 | #define TPACPI_ALSA_DRVNAME "ThinkPad EC" | ||
6412 | #define TPACPI_ALSA_SHRTNAME "ThinkPad Console Audio Control" | ||
6413 | #define TPACPI_ALSA_MIXERNAME TPACPI_ALSA_SHRTNAME | ||
6345 | 6414 | ||
6346 | static int volume_read(char *p) | 6415 | static int alsa_index = ~((1 << (SNDRV_CARDS - 3)) - 1); /* last three slots */ |
6416 | static char *alsa_id = "ThinkPadEC"; | ||
6417 | static int alsa_enable = SNDRV_DEFAULT_ENABLE1; | ||
6418 | |||
6419 | struct tpacpi_alsa_data { | ||
6420 | struct snd_card *card; | ||
6421 | struct snd_ctl_elem_id *ctl_mute_id; | ||
6422 | struct snd_ctl_elem_id *ctl_vol_id; | ||
6423 | }; | ||
6424 | |||
6425 | static struct snd_card *alsa_card; | ||
6426 | |||
6427 | enum { | ||
6428 | TP_EC_AUDIO = 0x30, | ||
6429 | |||
6430 | /* TP_EC_AUDIO bits */ | ||
6431 | TP_EC_AUDIO_MUTESW = 6, | ||
6432 | |||
6433 | /* TP_EC_AUDIO bitmasks */ | ||
6434 | TP_EC_AUDIO_LVL_MSK = 0x0F, | ||
6435 | TP_EC_AUDIO_MUTESW_MSK = (1 << TP_EC_AUDIO_MUTESW), | ||
6436 | |||
6437 | /* Maximum volume */ | ||
6438 | TP_EC_VOLUME_MAX = 14, | ||
6439 | }; | ||
6440 | |||
6441 | enum tpacpi_volume_access_mode { | ||
6442 | TPACPI_VOL_MODE_AUTO = 0, /* Not implemented yet */ | ||
6443 | TPACPI_VOL_MODE_EC, /* Pure EC control */ | ||
6444 | TPACPI_VOL_MODE_UCMS_STEP, /* UCMS step-based control: N/A */ | ||
6445 | TPACPI_VOL_MODE_ECNVRAM, /* EC control w/ NVRAM store */ | ||
6446 | TPACPI_VOL_MODE_MAX | ||
6447 | }; | ||
6448 | |||
6449 | enum tpacpi_volume_capabilities { | ||
6450 | TPACPI_VOL_CAP_AUTO = 0, /* Use white/blacklist */ | ||
6451 | TPACPI_VOL_CAP_VOLMUTE, /* Output vol and mute */ | ||
6452 | TPACPI_VOL_CAP_MUTEONLY, /* Output mute only */ | ||
6453 | TPACPI_VOL_CAP_MAX | ||
6454 | }; | ||
6455 | |||
6456 | static enum tpacpi_volume_access_mode volume_mode = | ||
6457 | TPACPI_VOL_MODE_MAX; | ||
6458 | |||
6459 | static enum tpacpi_volume_capabilities volume_capabilities; | ||
6460 | static int volume_control_allowed; | ||
6461 | |||
6462 | /* | ||
6463 | * Used to syncronize writers to TP_EC_AUDIO and | ||
6464 | * TP_NVRAM_ADDR_MIXER, as we need to do read-modify-write | ||
6465 | */ | ||
6466 | static struct mutex volume_mutex; | ||
6467 | |||
6468 | static void tpacpi_volume_checkpoint_nvram(void) | ||
6347 | { | 6469 | { |
6348 | int len = 0; | 6470 | u8 lec = 0; |
6349 | u8 level; | 6471 | u8 b_nvram; |
6472 | u8 ec_mask; | ||
6473 | |||
6474 | if (volume_mode != TPACPI_VOL_MODE_ECNVRAM) | ||
6475 | return; | ||
6476 | if (!volume_control_allowed) | ||
6477 | return; | ||
6478 | |||
6479 | vdbg_printk(TPACPI_DBG_MIXER, | ||
6480 | "trying to checkpoint mixer state to NVRAM...\n"); | ||
6481 | |||
6482 | if (tp_features.mixer_no_level_control) | ||
6483 | ec_mask = TP_EC_AUDIO_MUTESW_MSK; | ||
6484 | else | ||
6485 | ec_mask = TP_EC_AUDIO_MUTESW_MSK | TP_EC_AUDIO_LVL_MSK; | ||
6486 | |||
6487 | if (mutex_lock_killable(&volume_mutex) < 0) | ||
6488 | return; | ||
6350 | 6489 | ||
6351 | if (!acpi_ec_read(volume_offset, &level)) { | 6490 | if (unlikely(!acpi_ec_read(TP_EC_AUDIO, &lec))) |
6352 | len += sprintf(p + len, "level:\t\tunreadable\n"); | 6491 | goto unlock; |
6492 | lec &= ec_mask; | ||
6493 | b_nvram = nvram_read_byte(TP_NVRAM_ADDR_MIXER); | ||
6494 | |||
6495 | if (lec != (b_nvram & ec_mask)) { | ||
6496 | /* NVRAM needs update */ | ||
6497 | b_nvram &= ~ec_mask; | ||
6498 | b_nvram |= lec; | ||
6499 | nvram_write_byte(b_nvram, TP_NVRAM_ADDR_MIXER); | ||
6500 | dbg_printk(TPACPI_DBG_MIXER, | ||
6501 | "updated NVRAM mixer status to 0x%02x (0x%02x)\n", | ||
6502 | (unsigned int) lec, (unsigned int) b_nvram); | ||
6353 | } else { | 6503 | } else { |
6354 | len += sprintf(p + len, "level:\t\t%d\n", level & 0xf); | 6504 | vdbg_printk(TPACPI_DBG_MIXER, |
6355 | len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6)); | 6505 | "NVRAM mixer status already is 0x%02x (0x%02x)\n", |
6356 | len += sprintf(p + len, "commands:\tup, down, mute\n"); | 6506 | (unsigned int) lec, (unsigned int) b_nvram); |
6357 | len += sprintf(p + len, "commands:\tlevel <level>" | ||
6358 | " (<level> is 0-15)\n"); | ||
6359 | } | 6507 | } |
6360 | 6508 | ||
6361 | return len; | 6509 | unlock: |
6510 | mutex_unlock(&volume_mutex); | ||
6362 | } | 6511 | } |
6363 | 6512 | ||
6364 | static int volume_write(char *buf) | 6513 | static int volume_get_status_ec(u8 *status) |
6365 | { | 6514 | { |
6366 | int cmos_cmd, inc, i; | 6515 | u8 s; |
6367 | u8 level, mute; | ||
6368 | int new_level, new_mute; | ||
6369 | char *cmd; | ||
6370 | 6516 | ||
6371 | while ((cmd = next_cmd(&buf))) { | 6517 | if (!acpi_ec_read(TP_EC_AUDIO, &s)) |
6372 | if (!acpi_ec_read(volume_offset, &level)) | 6518 | return -EIO; |
6373 | return -EIO; | ||
6374 | new_mute = mute = level & 0x40; | ||
6375 | new_level = level = level & 0xf; | ||
6376 | 6519 | ||
6377 | if (strlencmp(cmd, "up") == 0) { | 6520 | *status = s; |
6378 | if (mute) | ||
6379 | new_mute = 0; | ||
6380 | else | ||
6381 | new_level = level == 15 ? 15 : level + 1; | ||
6382 | } else if (strlencmp(cmd, "down") == 0) { | ||
6383 | if (mute) | ||
6384 | new_mute = 0; | ||
6385 | else | ||
6386 | new_level = level == 0 ? 0 : level - 1; | ||
6387 | } else if (sscanf(cmd, "level %d", &new_level) == 1 && | ||
6388 | new_level >= 0 && new_level <= 15) { | ||
6389 | /* new_level set */ | ||
6390 | } else if (strlencmp(cmd, "mute") == 0) { | ||
6391 | new_mute = 0x40; | ||
6392 | } else | ||
6393 | return -EINVAL; | ||
6394 | 6521 | ||
6395 | if (new_level != level) { | 6522 | dbg_printk(TPACPI_DBG_MIXER, "status 0x%02x\n", s); |
6396 | /* mute doesn't change */ | ||
6397 | 6523 | ||
6398 | cmos_cmd = (new_level > level) ? | 6524 | return 0; |
6399 | TP_CMOS_VOLUME_UP : TP_CMOS_VOLUME_DOWN; | 6525 | } |
6400 | inc = new_level > level ? 1 : -1; | ||
6401 | 6526 | ||
6402 | if (mute && (issue_thinkpad_cmos_command(cmos_cmd) || | 6527 | static int volume_get_status(u8 *status) |
6403 | !acpi_ec_write(volume_offset, level))) | 6528 | { |
6404 | return -EIO; | 6529 | return volume_get_status_ec(status); |
6530 | } | ||
6405 | 6531 | ||
6406 | for (i = level; i != new_level; i += inc) | 6532 | static int volume_set_status_ec(const u8 status) |
6407 | if (issue_thinkpad_cmos_command(cmos_cmd) || | 6533 | { |
6408 | !acpi_ec_write(volume_offset, i + inc)) | 6534 | if (!acpi_ec_write(TP_EC_AUDIO, status)) |
6409 | return -EIO; | 6535 | return -EIO; |
6410 | 6536 | ||
6411 | if (mute && | 6537 | dbg_printk(TPACPI_DBG_MIXER, "set EC mixer to 0x%02x\n", status); |
6412 | (issue_thinkpad_cmos_command(TP_CMOS_VOLUME_MUTE) || | 6538 | |
6413 | !acpi_ec_write(volume_offset, new_level + mute))) { | 6539 | return 0; |
6414 | return -EIO; | 6540 | } |
6415 | } | 6541 | |
6542 | static int volume_set_status(const u8 status) | ||
6543 | { | ||
6544 | return volume_set_status_ec(status); | ||
6545 | } | ||
6546 | |||
6547 | /* returns < 0 on error, 0 on no change, 1 on change */ | ||
6548 | static int __volume_set_mute_ec(const bool mute) | ||
6549 | { | ||
6550 | int rc; | ||
6551 | u8 s, n; | ||
6552 | |||
6553 | if (mutex_lock_killable(&volume_mutex) < 0) | ||
6554 | return -EINTR; | ||
6555 | |||
6556 | rc = volume_get_status_ec(&s); | ||
6557 | if (rc) | ||
6558 | goto unlock; | ||
6559 | |||
6560 | n = (mute) ? s | TP_EC_AUDIO_MUTESW_MSK : | ||
6561 | s & ~TP_EC_AUDIO_MUTESW_MSK; | ||
6562 | |||
6563 | if (n != s) { | ||
6564 | rc = volume_set_status_ec(n); | ||
6565 | if (!rc) | ||
6566 | rc = 1; | ||
6567 | } | ||
6568 | |||
6569 | unlock: | ||
6570 | mutex_unlock(&volume_mutex); | ||
6571 | return rc; | ||
6572 | } | ||
6573 | |||
6574 | static int volume_alsa_set_mute(const bool mute) | ||
6575 | { | ||
6576 | dbg_printk(TPACPI_DBG_MIXER, "ALSA: trying to %smute\n", | ||
6577 | (mute) ? "" : "un"); | ||
6578 | return __volume_set_mute_ec(mute); | ||
6579 | } | ||
6580 | |||
6581 | static int volume_set_mute(const bool mute) | ||
6582 | { | ||
6583 | int rc; | ||
6584 | |||
6585 | dbg_printk(TPACPI_DBG_MIXER, "trying to %smute\n", | ||
6586 | (mute) ? "" : "un"); | ||
6587 | |||
6588 | rc = __volume_set_mute_ec(mute); | ||
6589 | return (rc < 0) ? rc : 0; | ||
6590 | } | ||
6591 | |||
6592 | /* returns < 0 on error, 0 on no change, 1 on change */ | ||
6593 | static int __volume_set_volume_ec(const u8 vol) | ||
6594 | { | ||
6595 | int rc; | ||
6596 | u8 s, n; | ||
6597 | |||
6598 | if (vol > TP_EC_VOLUME_MAX) | ||
6599 | return -EINVAL; | ||
6600 | |||
6601 | if (mutex_lock_killable(&volume_mutex) < 0) | ||
6602 | return -EINTR; | ||
6603 | |||
6604 | rc = volume_get_status_ec(&s); | ||
6605 | if (rc) | ||
6606 | goto unlock; | ||
6607 | |||
6608 | n = (s & ~TP_EC_AUDIO_LVL_MSK) | vol; | ||
6609 | |||
6610 | if (n != s) { | ||
6611 | rc = volume_set_status_ec(n); | ||
6612 | if (!rc) | ||
6613 | rc = 1; | ||
6614 | } | ||
6615 | |||
6616 | unlock: | ||
6617 | mutex_unlock(&volume_mutex); | ||
6618 | return rc; | ||
6619 | } | ||
6620 | |||
6621 | static int volume_alsa_set_volume(const u8 vol) | ||
6622 | { | ||
6623 | dbg_printk(TPACPI_DBG_MIXER, | ||
6624 | "ALSA: trying to set volume level to %hu\n", vol); | ||
6625 | return __volume_set_volume_ec(vol); | ||
6626 | } | ||
6627 | |||
6628 | static void volume_alsa_notify_change(void) | ||
6629 | { | ||
6630 | struct tpacpi_alsa_data *d; | ||
6631 | |||
6632 | if (alsa_card && alsa_card->private_data) { | ||
6633 | d = alsa_card->private_data; | ||
6634 | if (d->ctl_mute_id) | ||
6635 | snd_ctl_notify(alsa_card, | ||
6636 | SNDRV_CTL_EVENT_MASK_VALUE, | ||
6637 | d->ctl_mute_id); | ||
6638 | if (d->ctl_vol_id) | ||
6639 | snd_ctl_notify(alsa_card, | ||
6640 | SNDRV_CTL_EVENT_MASK_VALUE, | ||
6641 | d->ctl_vol_id); | ||
6642 | } | ||
6643 | } | ||
6644 | |||
6645 | static int volume_alsa_vol_info(struct snd_kcontrol *kcontrol, | ||
6646 | struct snd_ctl_elem_info *uinfo) | ||
6647 | { | ||
6648 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
6649 | uinfo->count = 1; | ||
6650 | uinfo->value.integer.min = 0; | ||
6651 | uinfo->value.integer.max = TP_EC_VOLUME_MAX; | ||
6652 | return 0; | ||
6653 | } | ||
6654 | |||
6655 | static int volume_alsa_vol_get(struct snd_kcontrol *kcontrol, | ||
6656 | struct snd_ctl_elem_value *ucontrol) | ||
6657 | { | ||
6658 | u8 s; | ||
6659 | int rc; | ||
6660 | |||
6661 | rc = volume_get_status(&s); | ||
6662 | if (rc < 0) | ||
6663 | return rc; | ||
6664 | |||
6665 | ucontrol->value.integer.value[0] = s & TP_EC_AUDIO_LVL_MSK; | ||
6666 | return 0; | ||
6667 | } | ||
6668 | |||
6669 | static int volume_alsa_vol_put(struct snd_kcontrol *kcontrol, | ||
6670 | struct snd_ctl_elem_value *ucontrol) | ||
6671 | { | ||
6672 | return volume_alsa_set_volume(ucontrol->value.integer.value[0]); | ||
6673 | } | ||
6674 | |||
6675 | #define volume_alsa_mute_info snd_ctl_boolean_mono_info | ||
6676 | |||
6677 | static int volume_alsa_mute_get(struct snd_kcontrol *kcontrol, | ||
6678 | struct snd_ctl_elem_value *ucontrol) | ||
6679 | { | ||
6680 | u8 s; | ||
6681 | int rc; | ||
6682 | |||
6683 | rc = volume_get_status(&s); | ||
6684 | if (rc < 0) | ||
6685 | return rc; | ||
6686 | |||
6687 | ucontrol->value.integer.value[0] = | ||
6688 | (s & TP_EC_AUDIO_MUTESW_MSK) ? 0 : 1; | ||
6689 | return 0; | ||
6690 | } | ||
6691 | |||
6692 | static int volume_alsa_mute_put(struct snd_kcontrol *kcontrol, | ||
6693 | struct snd_ctl_elem_value *ucontrol) | ||
6694 | { | ||
6695 | return volume_alsa_set_mute(!ucontrol->value.integer.value[0]); | ||
6696 | } | ||
6697 | |||
6698 | static struct snd_kcontrol_new volume_alsa_control_vol __devinitdata = { | ||
6699 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
6700 | .name = "Console Playback Volume", | ||
6701 | .index = 0, | ||
6702 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
6703 | .info = volume_alsa_vol_info, | ||
6704 | .get = volume_alsa_vol_get, | ||
6705 | }; | ||
6706 | |||
6707 | static struct snd_kcontrol_new volume_alsa_control_mute __devinitdata = { | ||
6708 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
6709 | .name = "Console Playback Switch", | ||
6710 | .index = 0, | ||
6711 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
6712 | .info = volume_alsa_mute_info, | ||
6713 | .get = volume_alsa_mute_get, | ||
6714 | }; | ||
6715 | |||
6716 | static void volume_suspend(pm_message_t state) | ||
6717 | { | ||
6718 | tpacpi_volume_checkpoint_nvram(); | ||
6719 | } | ||
6720 | |||
6721 | static void volume_resume(void) | ||
6722 | { | ||
6723 | volume_alsa_notify_change(); | ||
6724 | } | ||
6725 | |||
6726 | static void volume_shutdown(void) | ||
6727 | { | ||
6728 | tpacpi_volume_checkpoint_nvram(); | ||
6729 | } | ||
6730 | |||
6731 | static void volume_exit(void) | ||
6732 | { | ||
6733 | if (alsa_card) { | ||
6734 | snd_card_free(alsa_card); | ||
6735 | alsa_card = NULL; | ||
6736 | } | ||
6737 | |||
6738 | tpacpi_volume_checkpoint_nvram(); | ||
6739 | } | ||
6740 | |||
6741 | static int __init volume_create_alsa_mixer(void) | ||
6742 | { | ||
6743 | struct snd_card *card; | ||
6744 | struct tpacpi_alsa_data *data; | ||
6745 | struct snd_kcontrol *ctl_vol; | ||
6746 | struct snd_kcontrol *ctl_mute; | ||
6747 | int rc; | ||
6748 | |||
6749 | rc = snd_card_create(alsa_index, alsa_id, THIS_MODULE, | ||
6750 | sizeof(struct tpacpi_alsa_data), &card); | ||
6751 | if (rc < 0 || !card) { | ||
6752 | printk(TPACPI_ERR | ||
6753 | "Failed to create ALSA card structures: %d\n", rc); | ||
6754 | return 1; | ||
6755 | } | ||
6756 | |||
6757 | BUG_ON(!card->private_data); | ||
6758 | data = card->private_data; | ||
6759 | data->card = card; | ||
6760 | |||
6761 | strlcpy(card->driver, TPACPI_ALSA_DRVNAME, | ||
6762 | sizeof(card->driver)); | ||
6763 | strlcpy(card->shortname, TPACPI_ALSA_SHRTNAME, | ||
6764 | sizeof(card->shortname)); | ||
6765 | snprintf(card->mixername, sizeof(card->mixername), "ThinkPad EC %s", | ||
6766 | (thinkpad_id.ec_version_str) ? | ||
6767 | thinkpad_id.ec_version_str : "(unknown)"); | ||
6768 | snprintf(card->longname, sizeof(card->longname), | ||
6769 | "%s at EC reg 0x%02x, fw %s", card->shortname, TP_EC_AUDIO, | ||
6770 | (thinkpad_id.ec_version_str) ? | ||
6771 | thinkpad_id.ec_version_str : "unknown"); | ||
6772 | |||
6773 | if (volume_control_allowed) { | ||
6774 | volume_alsa_control_vol.put = volume_alsa_vol_put; | ||
6775 | volume_alsa_control_vol.access = | ||
6776 | SNDRV_CTL_ELEM_ACCESS_READWRITE; | ||
6777 | |||
6778 | volume_alsa_control_mute.put = volume_alsa_mute_put; | ||
6779 | volume_alsa_control_mute.access = | ||
6780 | SNDRV_CTL_ELEM_ACCESS_READWRITE; | ||
6781 | } | ||
6782 | |||
6783 | if (!tp_features.mixer_no_level_control) { | ||
6784 | ctl_vol = snd_ctl_new1(&volume_alsa_control_vol, NULL); | ||
6785 | rc = snd_ctl_add(card, ctl_vol); | ||
6786 | if (rc < 0) { | ||
6787 | printk(TPACPI_ERR | ||
6788 | "Failed to create ALSA volume control: %d\n", | ||
6789 | rc); | ||
6790 | goto err_exit; | ||
6416 | } | 6791 | } |
6792 | data->ctl_vol_id = &ctl_vol->id; | ||
6793 | } | ||
6417 | 6794 | ||
6418 | if (new_mute != mute) { | 6795 | ctl_mute = snd_ctl_new1(&volume_alsa_control_mute, NULL); |
6419 | /* level doesn't change */ | 6796 | rc = snd_ctl_add(card, ctl_mute); |
6797 | if (rc < 0) { | ||
6798 | printk(TPACPI_ERR "Failed to create ALSA mute control: %d\n", | ||
6799 | rc); | ||
6800 | goto err_exit; | ||
6801 | } | ||
6802 | data->ctl_mute_id = &ctl_mute->id; | ||
6420 | 6803 | ||
6421 | cmos_cmd = (new_mute) ? | 6804 | snd_card_set_dev(card, &tpacpi_pdev->dev); |
6422 | TP_CMOS_VOLUME_MUTE : TP_CMOS_VOLUME_UP; | 6805 | rc = snd_card_register(card); |
6806 | if (rc < 0) { | ||
6807 | printk(TPACPI_ERR "Failed to register ALSA card: %d\n", rc); | ||
6808 | goto err_exit; | ||
6809 | } | ||
6423 | 6810 | ||
6424 | if (issue_thinkpad_cmos_command(cmos_cmd) || | 6811 | alsa_card = card; |
6425 | !acpi_ec_write(volume_offset, level + new_mute)) | 6812 | return 0; |
6426 | return -EIO; | 6813 | |
6814 | err_exit: | ||
6815 | snd_card_free(card); | ||
6816 | return 1; | ||
6817 | } | ||
6818 | |||
6819 | #define TPACPI_VOL_Q_MUTEONLY 0x0001 /* Mute-only control available */ | ||
6820 | #define TPACPI_VOL_Q_LEVEL 0x0002 /* Volume control available */ | ||
6821 | |||
6822 | static const struct tpacpi_quirk volume_quirk_table[] __initconst = { | ||
6823 | /* Whitelist volume level on all IBM by default */ | ||
6824 | { .vendor = PCI_VENDOR_ID_IBM, | ||
6825 | .bios = TPACPI_MATCH_ANY, | ||
6826 | .ec = TPACPI_MATCH_ANY, | ||
6827 | .quirks = TPACPI_VOL_Q_LEVEL }, | ||
6828 | |||
6829 | /* Lenovo models with volume control (needs confirmation) */ | ||
6830 | TPACPI_QEC_LNV('7', 'C', TPACPI_VOL_Q_LEVEL), /* R60/i */ | ||
6831 | TPACPI_QEC_LNV('7', 'E', TPACPI_VOL_Q_LEVEL), /* R60e/i */ | ||
6832 | TPACPI_QEC_LNV('7', '9', TPACPI_VOL_Q_LEVEL), /* T60/p */ | ||
6833 | TPACPI_QEC_LNV('7', 'B', TPACPI_VOL_Q_LEVEL), /* X60/s */ | ||
6834 | TPACPI_QEC_LNV('7', 'J', TPACPI_VOL_Q_LEVEL), /* X60t */ | ||
6835 | TPACPI_QEC_LNV('7', '7', TPACPI_VOL_Q_LEVEL), /* Z60 */ | ||
6836 | TPACPI_QEC_LNV('7', 'F', TPACPI_VOL_Q_LEVEL), /* Z61 */ | ||
6837 | |||
6838 | /* Whitelist mute-only on all Lenovo by default */ | ||
6839 | { .vendor = PCI_VENDOR_ID_LENOVO, | ||
6840 | .bios = TPACPI_MATCH_ANY, | ||
6841 | .ec = TPACPI_MATCH_ANY, | ||
6842 | .quirks = TPACPI_VOL_Q_MUTEONLY } | ||
6843 | }; | ||
6844 | |||
6845 | static int __init volume_init(struct ibm_init_struct *iibm) | ||
6846 | { | ||
6847 | unsigned long quirks; | ||
6848 | int rc; | ||
6849 | |||
6850 | vdbg_printk(TPACPI_DBG_INIT, "initializing volume subdriver\n"); | ||
6851 | |||
6852 | mutex_init(&volume_mutex); | ||
6853 | |||
6854 | /* | ||
6855 | * Check for module parameter bogosity, note that we | ||
6856 | * init volume_mode to TPACPI_VOL_MODE_MAX in order to be | ||
6857 | * able to detect "unspecified" | ||
6858 | */ | ||
6859 | if (volume_mode > TPACPI_VOL_MODE_MAX) | ||
6860 | return -EINVAL; | ||
6861 | |||
6862 | if (volume_mode == TPACPI_VOL_MODE_UCMS_STEP) { | ||
6863 | printk(TPACPI_ERR | ||
6864 | "UCMS step volume mode not implemented, " | ||
6865 | "please contact %s\n", TPACPI_MAIL); | ||
6866 | return 1; | ||
6867 | } | ||
6868 | |||
6869 | if (volume_capabilities >= TPACPI_VOL_CAP_MAX) | ||
6870 | return -EINVAL; | ||
6871 | |||
6872 | /* | ||
6873 | * The ALSA mixer is our primary interface. | ||
6874 | * When disabled, don't install the subdriver at all | ||
6875 | */ | ||
6876 | if (!alsa_enable) { | ||
6877 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, | ||
6878 | "ALSA mixer disabled by parameter, " | ||
6879 | "not loading volume subdriver...\n"); | ||
6880 | return 1; | ||
6881 | } | ||
6882 | |||
6883 | quirks = tpacpi_check_quirks(volume_quirk_table, | ||
6884 | ARRAY_SIZE(volume_quirk_table)); | ||
6885 | |||
6886 | switch (volume_capabilities) { | ||
6887 | case TPACPI_VOL_CAP_AUTO: | ||
6888 | if (quirks & TPACPI_VOL_Q_MUTEONLY) | ||
6889 | tp_features.mixer_no_level_control = 1; | ||
6890 | else if (quirks & TPACPI_VOL_Q_LEVEL) | ||
6891 | tp_features.mixer_no_level_control = 0; | ||
6892 | else | ||
6893 | return 1; /* no mixer */ | ||
6894 | break; | ||
6895 | case TPACPI_VOL_CAP_VOLMUTE: | ||
6896 | tp_features.mixer_no_level_control = 0; | ||
6897 | break; | ||
6898 | case TPACPI_VOL_CAP_MUTEONLY: | ||
6899 | tp_features.mixer_no_level_control = 1; | ||
6900 | break; | ||
6901 | default: | ||
6902 | return 1; | ||
6903 | } | ||
6904 | |||
6905 | if (volume_capabilities != TPACPI_VOL_CAP_AUTO) | ||
6906 | dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, | ||
6907 | "using user-supplied volume_capabilities=%d\n", | ||
6908 | volume_capabilities); | ||
6909 | |||
6910 | if (volume_mode == TPACPI_VOL_MODE_AUTO || | ||
6911 | volume_mode == TPACPI_VOL_MODE_MAX) { | ||
6912 | volume_mode = TPACPI_VOL_MODE_ECNVRAM; | ||
6913 | |||
6914 | dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, | ||
6915 | "driver auto-selected volume_mode=%d\n", | ||
6916 | volume_mode); | ||
6917 | } else { | ||
6918 | dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, | ||
6919 | "using user-supplied volume_mode=%d\n", | ||
6920 | volume_mode); | ||
6921 | } | ||
6922 | |||
6923 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, | ||
6924 | "mute is supported, volume control is %s\n", | ||
6925 | str_supported(!tp_features.mixer_no_level_control)); | ||
6926 | |||
6927 | rc = volume_create_alsa_mixer(); | ||
6928 | if (rc) { | ||
6929 | printk(TPACPI_ERR | ||
6930 | "Could not create the ALSA mixer interface\n"); | ||
6931 | return rc; | ||
6932 | } | ||
6933 | |||
6934 | printk(TPACPI_INFO | ||
6935 | "Console audio control enabled, mode: %s\n", | ||
6936 | (volume_control_allowed) ? | ||
6937 | "override (read/write)" : | ||
6938 | "monitor (read only)"); | ||
6939 | |||
6940 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, | ||
6941 | "registering volume hotkeys as change notification\n"); | ||
6942 | tpacpi_hotkey_driver_mask_set(hotkey_driver_mask | ||
6943 | | TP_ACPI_HKEY_VOLUP_MASK | ||
6944 | | TP_ACPI_HKEY_VOLDWN_MASK | ||
6945 | | TP_ACPI_HKEY_MUTE_MASK); | ||
6946 | |||
6947 | return 0; | ||
6948 | } | ||
6949 | |||
6950 | static int volume_read(struct seq_file *m) | ||
6951 | { | ||
6952 | u8 status; | ||
6953 | |||
6954 | if (volume_get_status(&status) < 0) { | ||
6955 | seq_printf(m, "level:\t\tunreadable\n"); | ||
6956 | } else { | ||
6957 | if (tp_features.mixer_no_level_control) | ||
6958 | seq_printf(m, "level:\t\tunsupported\n"); | ||
6959 | else | ||
6960 | seq_printf(m, "level:\t\t%d\n", | ||
6961 | status & TP_EC_AUDIO_LVL_MSK); | ||
6962 | |||
6963 | seq_printf(m, "mute:\t\t%s\n", | ||
6964 | onoff(status, TP_EC_AUDIO_MUTESW)); | ||
6965 | |||
6966 | if (volume_control_allowed) { | ||
6967 | seq_printf(m, "commands:\tunmute, mute\n"); | ||
6968 | if (!tp_features.mixer_no_level_control) { | ||
6969 | seq_printf(m, | ||
6970 | "commands:\tup, down\n"); | ||
6971 | seq_printf(m, | ||
6972 | "commands:\tlevel <level>" | ||
6973 | " (<level> is 0-%d)\n", | ||
6974 | TP_EC_VOLUME_MAX); | ||
6975 | } | ||
6427 | } | 6976 | } |
6428 | } | 6977 | } |
6429 | 6978 | ||
6430 | return 0; | 6979 | return 0; |
6431 | } | 6980 | } |
6432 | 6981 | ||
6982 | static int volume_write(char *buf) | ||
6983 | { | ||
6984 | u8 s; | ||
6985 | u8 new_level, new_mute; | ||
6986 | int l; | ||
6987 | char *cmd; | ||
6988 | int rc; | ||
6989 | |||
6990 | /* | ||
6991 | * We do allow volume control at driver startup, so that the | ||
6992 | * user can set initial state through the volume=... parameter hack. | ||
6993 | */ | ||
6994 | if (!volume_control_allowed && tpacpi_lifecycle != TPACPI_LIFE_INIT) { | ||
6995 | if (unlikely(!tp_warned.volume_ctrl_forbidden)) { | ||
6996 | tp_warned.volume_ctrl_forbidden = 1; | ||
6997 | printk(TPACPI_NOTICE | ||
6998 | "Console audio control in monitor mode, " | ||
6999 | "changes are not allowed.\n"); | ||
7000 | printk(TPACPI_NOTICE | ||
7001 | "Use the volume_control=1 module parameter " | ||
7002 | "to enable volume control\n"); | ||
7003 | } | ||
7004 | return -EPERM; | ||
7005 | } | ||
7006 | |||
7007 | rc = volume_get_status(&s); | ||
7008 | if (rc < 0) | ||
7009 | return rc; | ||
7010 | |||
7011 | new_level = s & TP_EC_AUDIO_LVL_MSK; | ||
7012 | new_mute = s & TP_EC_AUDIO_MUTESW_MSK; | ||
7013 | |||
7014 | while ((cmd = next_cmd(&buf))) { | ||
7015 | if (!tp_features.mixer_no_level_control) { | ||
7016 | if (strlencmp(cmd, "up") == 0) { | ||
7017 | if (new_mute) | ||
7018 | new_mute = 0; | ||
7019 | else if (new_level < TP_EC_VOLUME_MAX) | ||
7020 | new_level++; | ||
7021 | continue; | ||
7022 | } else if (strlencmp(cmd, "down") == 0) { | ||
7023 | if (new_mute) | ||
7024 | new_mute = 0; | ||
7025 | else if (new_level > 0) | ||
7026 | new_level--; | ||
7027 | continue; | ||
7028 | } else if (sscanf(cmd, "level %u", &l) == 1 && | ||
7029 | l >= 0 && l <= TP_EC_VOLUME_MAX) { | ||
7030 | new_level = l; | ||
7031 | continue; | ||
7032 | } | ||
7033 | } | ||
7034 | if (strlencmp(cmd, "mute") == 0) | ||
7035 | new_mute = TP_EC_AUDIO_MUTESW_MSK; | ||
7036 | else if (strlencmp(cmd, "unmute") == 0) | ||
7037 | new_mute = 0; | ||
7038 | else | ||
7039 | return -EINVAL; | ||
7040 | } | ||
7041 | |||
7042 | if (tp_features.mixer_no_level_control) { | ||
7043 | tpacpi_disclose_usertask("procfs volume", "%smute\n", | ||
7044 | new_mute ? "" : "un"); | ||
7045 | rc = volume_set_mute(!!new_mute); | ||
7046 | } else { | ||
7047 | tpacpi_disclose_usertask("procfs volume", | ||
7048 | "%smute and set level to %d\n", | ||
7049 | new_mute ? "" : "un", new_level); | ||
7050 | rc = volume_set_status(new_mute | new_level); | ||
7051 | } | ||
7052 | volume_alsa_notify_change(); | ||
7053 | |||
7054 | return (rc == -EINTR) ? -ERESTARTSYS : rc; | ||
7055 | } | ||
7056 | |||
6433 | static struct ibm_struct volume_driver_data = { | 7057 | static struct ibm_struct volume_driver_data = { |
6434 | .name = "volume", | 7058 | .name = "volume", |
6435 | .read = volume_read, | 7059 | .read = volume_read, |
6436 | .write = volume_write, | 7060 | .write = volume_write, |
7061 | .exit = volume_exit, | ||
7062 | .suspend = volume_suspend, | ||
7063 | .resume = volume_resume, | ||
7064 | .shutdown = volume_shutdown, | ||
7065 | }; | ||
7066 | |||
7067 | #else /* !CONFIG_THINKPAD_ACPI_ALSA_SUPPORT */ | ||
7068 | |||
7069 | #define alsa_card NULL | ||
7070 | |||
7071 | static void inline volume_alsa_notify_change(void) | ||
7072 | { | ||
7073 | } | ||
7074 | |||
7075 | static int __init volume_init(struct ibm_init_struct *iibm) | ||
7076 | { | ||
7077 | printk(TPACPI_INFO | ||
7078 | "volume: disabled as there is no ALSA support in this kernel\n"); | ||
7079 | |||
7080 | return 1; | ||
7081 | } | ||
7082 | |||
7083 | static struct ibm_struct volume_driver_data = { | ||
7084 | .name = "volume", | ||
6437 | }; | 7085 | }; |
6438 | 7086 | ||
7087 | #endif /* CONFIG_THINKPAD_ACPI_ALSA_SUPPORT */ | ||
7088 | |||
6439 | /************************************************************************* | 7089 | /************************************************************************* |
6440 | * Fan subdriver | 7090 | * Fan subdriver |
6441 | */ | 7091 | */ |
@@ -6461,7 +7111,7 @@ static struct ibm_struct volume_driver_data = { | |||
6461 | * | 7111 | * |
6462 | * Fan speed changes of any sort (including those caused by the | 7112 | * Fan speed changes of any sort (including those caused by the |
6463 | * disengaged mode) are usually done slowly by the firmware as the | 7113 | * disengaged mode) are usually done slowly by the firmware as the |
6464 | * maximum ammount of fan duty cycle change per second seems to be | 7114 | * maximum amount of fan duty cycle change per second seems to be |
6465 | * limited. | 7115 | * limited. |
6466 | * | 7116 | * |
6467 | * Reading is not available if GFAN exists. | 7117 | * Reading is not available if GFAN exists. |
@@ -6545,7 +7195,7 @@ static struct ibm_struct volume_driver_data = { | |||
6545 | * The speeds are stored on handles | 7195 | * The speeds are stored on handles |
6546 | * (FANA:FAN9), (FANC:FANB), (FANE:FAND). | 7196 | * (FANA:FAN9), (FANC:FANB), (FANE:FAND). |
6547 | * | 7197 | * |
6548 | * There are three default speed sets, acessible as handles: | 7198 | * There are three default speed sets, accessible as handles: |
6549 | * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H | 7199 | * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H |
6550 | * | 7200 | * |
6551 | * ACPI DSDT switches which set is in use depending on various | 7201 | * ACPI DSDT switches which set is in use depending on various |
@@ -7510,9 +8160,8 @@ static void fan_resume(void) | |||
7510 | } | 8160 | } |
7511 | } | 8161 | } |
7512 | 8162 | ||
7513 | static int fan_read(char *p) | 8163 | static int fan_read(struct seq_file *m) |
7514 | { | 8164 | { |
7515 | int len = 0; | ||
7516 | int rc; | 8165 | int rc; |
7517 | u8 status; | 8166 | u8 status; |
7518 | unsigned int speed = 0; | 8167 | unsigned int speed = 0; |
@@ -7524,7 +8173,7 @@ static int fan_read(char *p) | |||
7524 | if (rc < 0) | 8173 | if (rc < 0) |
7525 | return rc; | 8174 | return rc; |
7526 | 8175 | ||
7527 | len += sprintf(p + len, "status:\t\t%s\n" | 8176 | seq_printf(m, "status:\t\t%s\n" |
7528 | "level:\t\t%d\n", | 8177 | "level:\t\t%d\n", |
7529 | (status != 0) ? "enabled" : "disabled", status); | 8178 | (status != 0) ? "enabled" : "disabled", status); |
7530 | break; | 8179 | break; |
@@ -7535,54 +8184,54 @@ static int fan_read(char *p) | |||
7535 | if (rc < 0) | 8184 | if (rc < 0) |
7536 | return rc; | 8185 | return rc; |
7537 | 8186 | ||
7538 | len += sprintf(p + len, "status:\t\t%s\n", | 8187 | seq_printf(m, "status:\t\t%s\n", |
7539 | (status != 0) ? "enabled" : "disabled"); | 8188 | (status != 0) ? "enabled" : "disabled"); |
7540 | 8189 | ||
7541 | rc = fan_get_speed(&speed); | 8190 | rc = fan_get_speed(&speed); |
7542 | if (rc < 0) | 8191 | if (rc < 0) |
7543 | return rc; | 8192 | return rc; |
7544 | 8193 | ||
7545 | len += sprintf(p + len, "speed:\t\t%d\n", speed); | 8194 | seq_printf(m, "speed:\t\t%d\n", speed); |
7546 | 8195 | ||
7547 | if (status & TP_EC_FAN_FULLSPEED) | 8196 | if (status & TP_EC_FAN_FULLSPEED) |
7548 | /* Disengaged mode takes precedence */ | 8197 | /* Disengaged mode takes precedence */ |
7549 | len += sprintf(p + len, "level:\t\tdisengaged\n"); | 8198 | seq_printf(m, "level:\t\tdisengaged\n"); |
7550 | else if (status & TP_EC_FAN_AUTO) | 8199 | else if (status & TP_EC_FAN_AUTO) |
7551 | len += sprintf(p + len, "level:\t\tauto\n"); | 8200 | seq_printf(m, "level:\t\tauto\n"); |
7552 | else | 8201 | else |
7553 | len += sprintf(p + len, "level:\t\t%d\n", status); | 8202 | seq_printf(m, "level:\t\t%d\n", status); |
7554 | break; | 8203 | break; |
7555 | 8204 | ||
7556 | case TPACPI_FAN_NONE: | 8205 | case TPACPI_FAN_NONE: |
7557 | default: | 8206 | default: |
7558 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 8207 | seq_printf(m, "status:\t\tnot supported\n"); |
7559 | } | 8208 | } |
7560 | 8209 | ||
7561 | if (fan_control_commands & TPACPI_FAN_CMD_LEVEL) { | 8210 | if (fan_control_commands & TPACPI_FAN_CMD_LEVEL) { |
7562 | len += sprintf(p + len, "commands:\tlevel <level>"); | 8211 | seq_printf(m, "commands:\tlevel <level>"); |
7563 | 8212 | ||
7564 | switch (fan_control_access_mode) { | 8213 | switch (fan_control_access_mode) { |
7565 | case TPACPI_FAN_WR_ACPI_SFAN: | 8214 | case TPACPI_FAN_WR_ACPI_SFAN: |
7566 | len += sprintf(p + len, " (<level> is 0-7)\n"); | 8215 | seq_printf(m, " (<level> is 0-7)\n"); |
7567 | break; | 8216 | break; |
7568 | 8217 | ||
7569 | default: | 8218 | default: |
7570 | len += sprintf(p + len, " (<level> is 0-7, " | 8219 | seq_printf(m, " (<level> is 0-7, " |
7571 | "auto, disengaged, full-speed)\n"); | 8220 | "auto, disengaged, full-speed)\n"); |
7572 | break; | 8221 | break; |
7573 | } | 8222 | } |
7574 | } | 8223 | } |
7575 | 8224 | ||
7576 | if (fan_control_commands & TPACPI_FAN_CMD_ENABLE) | 8225 | if (fan_control_commands & TPACPI_FAN_CMD_ENABLE) |
7577 | len += sprintf(p + len, "commands:\tenable, disable\n" | 8226 | seq_printf(m, "commands:\tenable, disable\n" |
7578 | "commands:\twatchdog <timeout> (<timeout> " | 8227 | "commands:\twatchdog <timeout> (<timeout> " |
7579 | "is 0 (off), 1-120 (seconds))\n"); | 8228 | "is 0 (off), 1-120 (seconds))\n"); |
7580 | 8229 | ||
7581 | if (fan_control_commands & TPACPI_FAN_CMD_SPEED) | 8230 | if (fan_control_commands & TPACPI_FAN_CMD_SPEED) |
7582 | len += sprintf(p + len, "commands:\tspeed <speed>" | 8231 | seq_printf(m, "commands:\tspeed <speed>" |
7583 | " (<speed> is 0-65535)\n"); | 8232 | " (<speed> is 0-65535)\n"); |
7584 | 8233 | ||
7585 | return len; | 8234 | return 0; |
7586 | } | 8235 | } |
7587 | 8236 | ||
7588 | static int fan_write_cmd_level(const char *cmd, int *rc) | 8237 | static int fan_write_cmd_level(const char *cmd, int *rc) |
@@ -7724,10 +8373,23 @@ static struct ibm_struct fan_driver_data = { | |||
7724 | */ | 8373 | */ |
7725 | static void tpacpi_driver_event(const unsigned int hkey_event) | 8374 | static void tpacpi_driver_event(const unsigned int hkey_event) |
7726 | { | 8375 | { |
8376 | if (ibm_backlight_device) { | ||
8377 | switch (hkey_event) { | ||
8378 | case TP_HKEY_EV_BRGHT_UP: | ||
8379 | case TP_HKEY_EV_BRGHT_DOWN: | ||
8380 | tpacpi_brightness_notify_change(); | ||
8381 | } | ||
8382 | } | ||
8383 | if (alsa_card) { | ||
8384 | switch (hkey_event) { | ||
8385 | case TP_HKEY_EV_VOL_UP: | ||
8386 | case TP_HKEY_EV_VOL_DOWN: | ||
8387 | case TP_HKEY_EV_VOL_MUTE: | ||
8388 | volume_alsa_notify_change(); | ||
8389 | } | ||
8390 | } | ||
7727 | } | 8391 | } |
7728 | 8392 | ||
7729 | |||
7730 | |||
7731 | static void hotkey_driver_event(const unsigned int scancode) | 8393 | static void hotkey_driver_event(const unsigned int scancode) |
7732 | { | 8394 | { |
7733 | tpacpi_driver_event(TP_HKEY_EV_HOTKEY_BASE + scancode); | 8395 | tpacpi_driver_event(TP_HKEY_EV_HOTKEY_BASE + scancode); |
@@ -7856,19 +8518,20 @@ static int __init ibm_init(struct ibm_init_struct *iibm) | |||
7856 | "%s installed\n", ibm->name); | 8518 | "%s installed\n", ibm->name); |
7857 | 8519 | ||
7858 | if (ibm->read) { | 8520 | if (ibm->read) { |
7859 | entry = create_proc_entry(ibm->name, | 8521 | mode_t mode = iibm->base_procfs_mode; |
7860 | S_IFREG | S_IRUGO | S_IWUSR, | 8522 | |
7861 | proc_dir); | 8523 | if (!mode) |
8524 | mode = S_IRUGO; | ||
8525 | if (ibm->write) | ||
8526 | mode |= S_IWUSR; | ||
8527 | entry = proc_create_data(ibm->name, mode, proc_dir, | ||
8528 | &dispatch_proc_fops, ibm); | ||
7862 | if (!entry) { | 8529 | if (!entry) { |
7863 | printk(TPACPI_ERR "unable to create proc entry %s\n", | 8530 | printk(TPACPI_ERR "unable to create proc entry %s\n", |
7864 | ibm->name); | 8531 | ibm->name); |
7865 | ret = -ENODEV; | 8532 | ret = -ENODEV; |
7866 | goto err_out; | 8533 | goto err_out; |
7867 | } | 8534 | } |
7868 | entry->data = ibm; | ||
7869 | entry->read_proc = &dispatch_procfs_read; | ||
7870 | if (ibm->write) | ||
7871 | entry->write_proc = &dispatch_procfs_write; | ||
7872 | ibm->flags.proc_created = 1; | 8535 | ibm->flags.proc_created = 1; |
7873 | } | 8536 | } |
7874 | 8537 | ||
@@ -8049,6 +8712,7 @@ static struct ibm_init_struct ibms_init[] __initdata = { | |||
8049 | #ifdef CONFIG_THINKPAD_ACPI_VIDEO | 8712 | #ifdef CONFIG_THINKPAD_ACPI_VIDEO |
8050 | { | 8713 | { |
8051 | .init = video_init, | 8714 | .init = video_init, |
8715 | .base_procfs_mode = S_IRUSR, | ||
8052 | .data = &video_driver_data, | 8716 | .data = &video_driver_data, |
8053 | }, | 8717 | }, |
8054 | #endif | 8718 | #endif |
@@ -8080,6 +8744,7 @@ static struct ibm_init_struct ibms_init[] __initdata = { | |||
8080 | .data = &brightness_driver_data, | 8744 | .data = &brightness_driver_data, |
8081 | }, | 8745 | }, |
8082 | { | 8746 | { |
8747 | .init = volume_init, | ||
8083 | .data = &volume_driver_data, | 8748 | .data = &volume_driver_data, |
8084 | }, | 8749 | }, |
8085 | { | 8750 | { |
@@ -8115,36 +8780,61 @@ static int __init set_ibm_param(const char *val, struct kernel_param *kp) | |||
8115 | return -EINVAL; | 8780 | return -EINVAL; |
8116 | } | 8781 | } |
8117 | 8782 | ||
8118 | module_param(experimental, int, 0); | 8783 | module_param(experimental, int, 0444); |
8119 | MODULE_PARM_DESC(experimental, | 8784 | MODULE_PARM_DESC(experimental, |
8120 | "Enables experimental features when non-zero"); | 8785 | "Enables experimental features when non-zero"); |
8121 | 8786 | ||
8122 | module_param_named(debug, dbg_level, uint, 0); | 8787 | module_param_named(debug, dbg_level, uint, 0); |
8123 | MODULE_PARM_DESC(debug, "Sets debug level bit-mask"); | 8788 | MODULE_PARM_DESC(debug, "Sets debug level bit-mask"); |
8124 | 8789 | ||
8125 | module_param(force_load, bool, 0); | 8790 | module_param(force_load, bool, 0444); |
8126 | MODULE_PARM_DESC(force_load, | 8791 | MODULE_PARM_DESC(force_load, |
8127 | "Attempts to load the driver even on a " | 8792 | "Attempts to load the driver even on a " |
8128 | "mis-identified ThinkPad when true"); | 8793 | "mis-identified ThinkPad when true"); |
8129 | 8794 | ||
8130 | module_param_named(fan_control, fan_control_allowed, bool, 0); | 8795 | module_param_named(fan_control, fan_control_allowed, bool, 0444); |
8131 | MODULE_PARM_DESC(fan_control, | 8796 | MODULE_PARM_DESC(fan_control, |
8132 | "Enables setting fan parameters features when true"); | 8797 | "Enables setting fan parameters features when true"); |
8133 | 8798 | ||
8134 | module_param_named(brightness_mode, brightness_mode, uint, 0); | 8799 | module_param_named(brightness_mode, brightness_mode, uint, 0444); |
8135 | MODULE_PARM_DESC(brightness_mode, | 8800 | MODULE_PARM_DESC(brightness_mode, |
8136 | "Selects brightness control strategy: " | 8801 | "Selects brightness control strategy: " |
8137 | "0=auto, 1=EC, 2=UCMS, 3=EC+NVRAM"); | 8802 | "0=auto, 1=EC, 2=UCMS, 3=EC+NVRAM"); |
8138 | 8803 | ||
8139 | module_param(brightness_enable, uint, 0); | 8804 | module_param(brightness_enable, uint, 0444); |
8140 | MODULE_PARM_DESC(brightness_enable, | 8805 | MODULE_PARM_DESC(brightness_enable, |
8141 | "Enables backlight control when 1, disables when 0"); | 8806 | "Enables backlight control when 1, disables when 0"); |
8142 | 8807 | ||
8143 | module_param(hotkey_report_mode, uint, 0); | 8808 | module_param(hotkey_report_mode, uint, 0444); |
8144 | MODULE_PARM_DESC(hotkey_report_mode, | 8809 | MODULE_PARM_DESC(hotkey_report_mode, |
8145 | "used for backwards compatibility with userspace, " | 8810 | "used for backwards compatibility with userspace, " |
8146 | "see documentation"); | 8811 | "see documentation"); |
8147 | 8812 | ||
8813 | #ifdef CONFIG_THINKPAD_ACPI_ALSA_SUPPORT | ||
8814 | module_param_named(volume_mode, volume_mode, uint, 0444); | ||
8815 | MODULE_PARM_DESC(volume_mode, | ||
8816 | "Selects volume control strategy: " | ||
8817 | "0=auto, 1=EC, 2=N/A, 3=EC+NVRAM"); | ||
8818 | |||
8819 | module_param_named(volume_capabilities, volume_capabilities, uint, 0444); | ||
8820 | MODULE_PARM_DESC(volume_capabilities, | ||
8821 | "Selects the mixer capabilites: " | ||
8822 | "0=auto, 1=volume and mute, 2=mute only"); | ||
8823 | |||
8824 | module_param_named(volume_control, volume_control_allowed, bool, 0444); | ||
8825 | MODULE_PARM_DESC(volume_control, | ||
8826 | "Enables software override for the console audio " | ||
8827 | "control when true"); | ||
8828 | |||
8829 | /* ALSA module API parameters */ | ||
8830 | module_param_named(index, alsa_index, int, 0444); | ||
8831 | MODULE_PARM_DESC(index, "ALSA index for the ACPI EC Mixer"); | ||
8832 | module_param_named(id, alsa_id, charp, 0444); | ||
8833 | MODULE_PARM_DESC(id, "ALSA id for the ACPI EC Mixer"); | ||
8834 | module_param_named(enable, alsa_enable, bool, 0444); | ||
8835 | MODULE_PARM_DESC(enable, "Enable the ALSA interface for the ACPI EC Mixer"); | ||
8836 | #endif /* CONFIG_THINKPAD_ACPI_ALSA_SUPPORT */ | ||
8837 | |||
8148 | #define TPACPI_PARAM(feature) \ | 8838 | #define TPACPI_PARAM(feature) \ |
8149 | module_param_call(feature, set_ibm_param, NULL, NULL, 0); \ | 8839 | module_param_call(feature, set_ibm_param, NULL, NULL, 0); \ |
8150 | MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \ | 8840 | MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \ |
@@ -8163,25 +8853,25 @@ TPACPI_PARAM(volume); | |||
8163 | TPACPI_PARAM(fan); | 8853 | TPACPI_PARAM(fan); |
8164 | 8854 | ||
8165 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 8855 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
8166 | module_param(dbg_wlswemul, uint, 0); | 8856 | module_param(dbg_wlswemul, uint, 0444); |
8167 | MODULE_PARM_DESC(dbg_wlswemul, "Enables WLSW emulation"); | 8857 | MODULE_PARM_DESC(dbg_wlswemul, "Enables WLSW emulation"); |
8168 | module_param_named(wlsw_state, tpacpi_wlsw_emulstate, bool, 0); | 8858 | module_param_named(wlsw_state, tpacpi_wlsw_emulstate, bool, 0); |
8169 | MODULE_PARM_DESC(wlsw_state, | 8859 | MODULE_PARM_DESC(wlsw_state, |
8170 | "Initial state of the emulated WLSW switch"); | 8860 | "Initial state of the emulated WLSW switch"); |
8171 | 8861 | ||
8172 | module_param(dbg_bluetoothemul, uint, 0); | 8862 | module_param(dbg_bluetoothemul, uint, 0444); |
8173 | MODULE_PARM_DESC(dbg_bluetoothemul, "Enables bluetooth switch emulation"); | 8863 | MODULE_PARM_DESC(dbg_bluetoothemul, "Enables bluetooth switch emulation"); |
8174 | module_param_named(bluetooth_state, tpacpi_bluetooth_emulstate, bool, 0); | 8864 | module_param_named(bluetooth_state, tpacpi_bluetooth_emulstate, bool, 0); |
8175 | MODULE_PARM_DESC(bluetooth_state, | 8865 | MODULE_PARM_DESC(bluetooth_state, |
8176 | "Initial state of the emulated bluetooth switch"); | 8866 | "Initial state of the emulated bluetooth switch"); |
8177 | 8867 | ||
8178 | module_param(dbg_wwanemul, uint, 0); | 8868 | module_param(dbg_wwanemul, uint, 0444); |
8179 | MODULE_PARM_DESC(dbg_wwanemul, "Enables WWAN switch emulation"); | 8869 | MODULE_PARM_DESC(dbg_wwanemul, "Enables WWAN switch emulation"); |
8180 | module_param_named(wwan_state, tpacpi_wwan_emulstate, bool, 0); | 8870 | module_param_named(wwan_state, tpacpi_wwan_emulstate, bool, 0); |
8181 | MODULE_PARM_DESC(wwan_state, | 8871 | MODULE_PARM_DESC(wwan_state, |
8182 | "Initial state of the emulated WWAN switch"); | 8872 | "Initial state of the emulated WWAN switch"); |
8183 | 8873 | ||
8184 | module_param(dbg_uwbemul, uint, 0); | 8874 | module_param(dbg_uwbemul, uint, 0444); |
8185 | MODULE_PARM_DESC(dbg_uwbemul, "Enables UWB switch emulation"); | 8875 | MODULE_PARM_DESC(dbg_uwbemul, "Enables UWB switch emulation"); |
8186 | module_param_named(uwb_state, tpacpi_uwb_emulstate, bool, 0); | 8876 | module_param_named(uwb_state, tpacpi_uwb_emulstate, bool, 0); |
8187 | MODULE_PARM_DESC(uwb_state, | 8877 | MODULE_PARM_DESC(uwb_state, |
@@ -8374,6 +9064,7 @@ static int __init thinkpad_acpi_module_init(void) | |||
8374 | PCI_VENDOR_ID_IBM; | 9064 | PCI_VENDOR_ID_IBM; |
8375 | tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT; | 9065 | tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT; |
8376 | tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION; | 9066 | tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION; |
9067 | tpacpi_inputdev->dev.parent = &tpacpi_pdev->dev; | ||
8377 | } | 9068 | } |
8378 | for (i = 0; i < ARRAY_SIZE(ibms_init); i++) { | 9069 | for (i = 0; i < ARRAY_SIZE(ibms_init); i++) { |
8379 | ret = ibm_init(&ibms_init[i]); | 9070 | ret = ibm_init(&ibms_init[i]); |
@@ -8384,6 +9075,9 @@ static int __init thinkpad_acpi_module_init(void) | |||
8384 | return ret; | 9075 | return ret; |
8385 | } | 9076 | } |
8386 | } | 9077 | } |
9078 | |||
9079 | tpacpi_lifecycle = TPACPI_LIFE_RUNNING; | ||
9080 | |||
8387 | ret = input_register_device(tpacpi_inputdev); | 9081 | ret = input_register_device(tpacpi_inputdev); |
8388 | if (ret < 0) { | 9082 | if (ret < 0) { |
8389 | printk(TPACPI_ERR "unable to register input device\n"); | 9083 | printk(TPACPI_ERR "unable to register input device\n"); |
@@ -8393,7 +9087,6 @@ static int __init thinkpad_acpi_module_init(void) | |||
8393 | tp_features.input_device_registered = 1; | 9087 | tp_features.input_device_registered = 1; |
8394 | } | 9088 | } |
8395 | 9089 | ||
8396 | tpacpi_lifecycle = TPACPI_LIFE_RUNNING; | ||
8397 | return 0; | 9090 | return 0; |
8398 | } | 9091 | } |
8399 | 9092 | ||