diff options
author | Len Brown <len.brown@intel.com> | 2009-12-16 00:00:54 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2009-12-16 00:00:54 -0500 |
commit | 7458bbd044f0e799ad27c3fc71e1115ac30a9c25 (patch) | |
tree | 4c9a320613722421f49b53266abcc64f90471f91 /drivers/platform | |
parent | 1667e399a41be976c9d37bf02aa477979df111d2 (diff) | |
parent | 5d2eb14d36723eba0b31ae208bc346835751e944 (diff) |
Merge branch 'thinkpad-2.6.33' into release
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 1178 |
1 files changed, 899 insertions, 279 deletions
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index cf61d6a8ef6f..448c8aeb166b 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: |
@@ -61,6 +61,7 @@ | |||
61 | 61 | ||
62 | #include <linux/nvram.h> | 62 | #include <linux/nvram.h> |
63 | #include <linux/proc_fs.h> | 63 | #include <linux/proc_fs.h> |
64 | #include <linux/seq_file.h> | ||
64 | #include <linux/sysfs.h> | 65 | #include <linux/sysfs.h> |
65 | #include <linux/backlight.h> | 66 | #include <linux/backlight.h> |
66 | #include <linux/fb.h> | 67 | #include <linux/fb.h> |
@@ -76,6 +77,10 @@ | |||
76 | #include <linux/jiffies.h> | 77 | #include <linux/jiffies.h> |
77 | #include <linux/workqueue.h> | 78 | #include <linux/workqueue.h> |
78 | 79 | ||
80 | #include <sound/core.h> | ||
81 | #include <sound/control.h> | ||
82 | #include <sound/initval.h> | ||
83 | |||
79 | #include <acpi/acpi_drivers.h> | 84 | #include <acpi/acpi_drivers.h> |
80 | 85 | ||
81 | #include <linux/pci_ids.h> | 86 | #include <linux/pci_ids.h> |
@@ -231,6 +236,7 @@ enum tpacpi_hkey_event_t { | |||
231 | #define TPACPI_DBG_HKEY 0x0008 | 236 | #define TPACPI_DBG_HKEY 0x0008 |
232 | #define TPACPI_DBG_FAN 0x0010 | 237 | #define TPACPI_DBG_FAN 0x0010 |
233 | #define TPACPI_DBG_BRGHT 0x0020 | 238 | #define TPACPI_DBG_BRGHT 0x0020 |
239 | #define TPACPI_DBG_MIXER 0x0040 | ||
234 | 240 | ||
235 | #define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off") | 241 | #define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off") |
236 | #define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") | 242 | #define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") |
@@ -256,7 +262,7 @@ struct tp_acpi_drv_struct { | |||
256 | struct ibm_struct { | 262 | struct ibm_struct { |
257 | char *name; | 263 | char *name; |
258 | 264 | ||
259 | int (*read) (char *); | 265 | int (*read) (struct seq_file *); |
260 | int (*write) (char *); | 266 | int (*write) (char *); |
261 | void (*exit) (void); | 267 | void (*exit) (void); |
262 | void (*resume) (void); | 268 | void (*resume) (void); |
@@ -298,6 +304,7 @@ static struct { | |||
298 | u32 fan_ctrl_status_undef:1; | 304 | u32 fan_ctrl_status_undef:1; |
299 | u32 second_fan:1; | 305 | u32 second_fan:1; |
300 | u32 beep_needs_two_args:1; | 306 | u32 beep_needs_two_args:1; |
307 | u32 mixer_no_level_control:1; | ||
301 | u32 input_device_registered:1; | 308 | u32 input_device_registered:1; |
302 | u32 platform_drv_registered:1; | 309 | u32 platform_drv_registered:1; |
303 | u32 platform_drv_attrs_registered:1; | 310 | u32 platform_drv_attrs_registered:1; |
@@ -309,6 +316,7 @@ static struct { | |||
309 | 316 | ||
310 | static struct { | 317 | static struct { |
311 | u16 hotkey_mask_ff:1; | 318 | u16 hotkey_mask_ff:1; |
319 | u16 volume_ctrl_forbidden:1; | ||
312 | } tp_warned; | 320 | } tp_warned; |
313 | 321 | ||
314 | struct thinkpad_id_data { | 322 | struct thinkpad_id_data { |
@@ -425,6 +433,12 @@ static void tpacpi_log_usertask(const char * const what) | |||
425 | .ec = TPACPI_MATCH_ANY, \ | 433 | .ec = TPACPI_MATCH_ANY, \ |
426 | .quirks = (__quirk) } | 434 | .quirks = (__quirk) } |
427 | 435 | ||
436 | #define TPACPI_QEC_LNV(__id1, __id2, __quirk) \ | ||
437 | { .vendor = PCI_VENDOR_ID_LENOVO, \ | ||
438 | .bios = TPACPI_MATCH_ANY, \ | ||
439 | .ec = TPID(__id1, __id2), \ | ||
440 | .quirks = (__quirk) } | ||
441 | |||
428 | struct tpacpi_quirk { | 442 | struct tpacpi_quirk { |
429 | unsigned int vendor; | 443 | unsigned int vendor; |
430 | u16 bios; | 444 | u16 bios; |
@@ -776,36 +790,25 @@ static int __init register_tpacpi_subdriver(struct ibm_struct *ibm) | |||
776 | **************************************************************************** | 790 | **************************************************************************** |
777 | ****************************************************************************/ | 791 | ****************************************************************************/ |
778 | 792 | ||
779 | static int dispatch_procfs_read(char *page, char **start, off_t off, | 793 | static int dispatch_proc_show(struct seq_file *m, void *v) |
780 | int count, int *eof, void *data) | ||
781 | { | 794 | { |
782 | struct ibm_struct *ibm = data; | 795 | struct ibm_struct *ibm = m->private; |
783 | int len; | ||
784 | 796 | ||
785 | if (!ibm || !ibm->read) | 797 | if (!ibm || !ibm->read) |
786 | return -EINVAL; | 798 | return -EINVAL; |
799 | return ibm->read(m); | ||
800 | } | ||
787 | 801 | ||
788 | len = ibm->read(page); | 802 | static int dispatch_proc_open(struct inode *inode, struct file *file) |
789 | if (len < 0) | 803 | { |
790 | return len; | 804 | 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 | } | 805 | } |
803 | 806 | ||
804 | static int dispatch_procfs_write(struct file *file, | 807 | static ssize_t dispatch_proc_write(struct file *file, |
805 | const char __user *userbuf, | 808 | const char __user *userbuf, |
806 | unsigned long count, void *data) | 809 | size_t count, loff_t *pos) |
807 | { | 810 | { |
808 | struct ibm_struct *ibm = data; | 811 | struct ibm_struct *ibm = PDE(file->f_path.dentry->d_inode)->data; |
809 | char *kernbuf; | 812 | char *kernbuf; |
810 | int ret; | 813 | int ret; |
811 | 814 | ||
@@ -834,6 +837,15 @@ static int dispatch_procfs_write(struct file *file, | |||
834 | return ret; | 837 | return ret; |
835 | } | 838 | } |
836 | 839 | ||
840 | static const struct file_operations dispatch_proc_fops = { | ||
841 | .owner = THIS_MODULE, | ||
842 | .open = dispatch_proc_open, | ||
843 | .read = seq_read, | ||
844 | .llseek = seq_lseek, | ||
845 | .release = single_release, | ||
846 | .write = dispatch_proc_write, | ||
847 | }; | ||
848 | |||
837 | static char *next_cmd(char **cmds) | 849 | static char *next_cmd(char **cmds) |
838 | { | 850 | { |
839 | char *start = *cmds; | 851 | char *start = *cmds; |
@@ -1261,6 +1273,7 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, | |||
1261 | struct tpacpi_rfk *atp_rfk; | 1273 | struct tpacpi_rfk *atp_rfk; |
1262 | int res; | 1274 | int res; |
1263 | bool sw_state = false; | 1275 | bool sw_state = false; |
1276 | bool hw_state; | ||
1264 | int sw_status; | 1277 | int sw_status; |
1265 | 1278 | ||
1266 | BUG_ON(id >= TPACPI_RFK_SW_MAX || tpacpi_rfkill_switches[id]); | 1279 | BUG_ON(id >= TPACPI_RFK_SW_MAX || tpacpi_rfkill_switches[id]); |
@@ -1295,7 +1308,8 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, | |||
1295 | rfkill_init_sw_state(atp_rfk->rfkill, sw_state); | 1308 | rfkill_init_sw_state(atp_rfk->rfkill, sw_state); |
1296 | } | 1309 | } |
1297 | } | 1310 | } |
1298 | rfkill_set_hw_state(atp_rfk->rfkill, tpacpi_rfk_check_hwblock_state()); | 1311 | hw_state = tpacpi_rfk_check_hwblock_state(); |
1312 | rfkill_set_hw_state(atp_rfk->rfkill, hw_state); | ||
1299 | 1313 | ||
1300 | res = rfkill_register(atp_rfk->rfkill); | 1314 | res = rfkill_register(atp_rfk->rfkill); |
1301 | if (res < 0) { | 1315 | if (res < 0) { |
@@ -1308,6 +1322,9 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, | |||
1308 | } | 1322 | } |
1309 | 1323 | ||
1310 | tpacpi_rfkill_switches[id] = atp_rfk; | 1324 | tpacpi_rfkill_switches[id] = atp_rfk; |
1325 | |||
1326 | printk(TPACPI_INFO "rfkill switch %s: radio is %sblocked\n", | ||
1327 | name, (sw_state || hw_state) ? "" : "un"); | ||
1311 | return 0; | 1328 | return 0; |
1312 | } | 1329 | } |
1313 | 1330 | ||
@@ -1380,12 +1397,10 @@ static ssize_t tpacpi_rfk_sysfs_enable_store(const enum tpacpi_rfk_id id, | |||
1380 | } | 1397 | } |
1381 | 1398 | ||
1382 | /* procfs -------------------------------------------------------------- */ | 1399 | /* procfs -------------------------------------------------------------- */ |
1383 | static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, char *p) | 1400 | static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, struct seq_file *m) |
1384 | { | 1401 | { |
1385 | int len = 0; | ||
1386 | |||
1387 | if (id >= TPACPI_RFK_SW_MAX) | 1402 | if (id >= TPACPI_RFK_SW_MAX) |
1388 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 1403 | seq_printf(m, "status:\t\tnot supported\n"); |
1389 | else { | 1404 | else { |
1390 | int status; | 1405 | int status; |
1391 | 1406 | ||
@@ -1399,13 +1414,13 @@ static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, char *p) | |||
1399 | return status; | 1414 | return status; |
1400 | } | 1415 | } |
1401 | 1416 | ||
1402 | len += sprintf(p + len, "status:\t\t%s\n", | 1417 | seq_printf(m, "status:\t\t%s\n", |
1403 | (status == TPACPI_RFK_RADIO_ON) ? | 1418 | (status == TPACPI_RFK_RADIO_ON) ? |
1404 | "enabled" : "disabled"); | 1419 | "enabled" : "disabled"); |
1405 | len += sprintf(p + len, "commands:\tenable, disable\n"); | 1420 | seq_printf(m, "commands:\tenable, disable\n"); |
1406 | } | 1421 | } |
1407 | 1422 | ||
1408 | return len; | 1423 | return 0; |
1409 | } | 1424 | } |
1410 | 1425 | ||
1411 | static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf) | 1426 | static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf) |
@@ -1776,7 +1791,7 @@ static const struct tpacpi_quirk tpacpi_bios_version_qtable[] __initconst = { | |||
1776 | 1791 | ||
1777 | TPV_QL1('7', '9', 'E', '3', '5', '0'), /* T60/p */ | 1792 | TPV_QL1('7', '9', 'E', '3', '5', '0'), /* T60/p */ |
1778 | TPV_QL1('7', 'C', 'D', '2', '2', '2'), /* R60, R60i */ | 1793 | TPV_QL1('7', 'C', 'D', '2', '2', '2'), /* R60, R60i */ |
1779 | TPV_QL0('7', 'E', 'D', '0'), /* R60e, R60i */ | 1794 | TPV_QL1('7', 'E', 'D', '0', '1', '5'), /* R60e, R60i */ |
1780 | 1795 | ||
1781 | /* BIOS FW BIOS VERS EC FW EC VERS */ | 1796 | /* BIOS FW BIOS VERS EC FW EC VERS */ |
1782 | TPV_QI2('1', 'W', '9', '0', '1', 'V', '2', '8'), /* R50e (1) */ | 1797 | TPV_QI2('1', 'W', '9', '0', '1', 'V', '2', '8'), /* R50e (1) */ |
@@ -1792,8 +1807,8 @@ static const struct tpacpi_quirk tpacpi_bios_version_qtable[] __initconst = { | |||
1792 | TPV_QI1('7', '4', '6', '4', '2', '7'), /* X41 (0) */ | 1807 | TPV_QI1('7', '4', '6', '4', '2', '7'), /* X41 (0) */ |
1793 | TPV_QI1('7', '5', '6', '0', '2', '0'), /* X41t (0) */ | 1808 | TPV_QI1('7', '5', '6', '0', '2', '0'), /* X41t (0) */ |
1794 | 1809 | ||
1795 | TPV_QL0('7', 'B', 'D', '7'), /* X60/s */ | 1810 | TPV_QL1('7', 'B', 'D', '7', '4', '0'), /* X60/s */ |
1796 | TPV_QL0('7', 'J', '3', '0'), /* X60t */ | 1811 | TPV_QL1('7', 'J', '3', '0', '1', '3'), /* X60t */ |
1797 | 1812 | ||
1798 | /* (0) - older versions lack DMI EC fw string and functionality */ | 1813 | /* (0) - older versions lack DMI EC fw string and functionality */ |
1799 | /* (1) - older versions known to lack functionality */ | 1814 | /* (1) - older versions known to lack functionality */ |
@@ -1883,14 +1898,11 @@ static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm) | |||
1883 | return 0; | 1898 | return 0; |
1884 | } | 1899 | } |
1885 | 1900 | ||
1886 | static int thinkpad_acpi_driver_read(char *p) | 1901 | static int thinkpad_acpi_driver_read(struct seq_file *m) |
1887 | { | 1902 | { |
1888 | int len = 0; | 1903 | seq_printf(m, "driver:\t\t%s\n", TPACPI_DESC); |
1889 | 1904 | seq_printf(m, "version:\t%s\n", TPACPI_VERSION); | |
1890 | len += sprintf(p + len, "driver:\t\t%s\n", TPACPI_DESC); | 1905 | return 0; |
1891 | len += sprintf(p + len, "version:\t%s\n", TPACPI_VERSION); | ||
1892 | |||
1893 | return len; | ||
1894 | } | 1906 | } |
1895 | 1907 | ||
1896 | static struct ibm_struct thinkpad_acpi_driver_data = { | 1908 | static struct ibm_struct thinkpad_acpi_driver_data = { |
@@ -2186,7 +2198,8 @@ static int hotkey_mask_set(u32 mask) | |||
2186 | fwmask, hotkey_acpi_mask); | 2198 | fwmask, hotkey_acpi_mask); |
2187 | } | 2199 | } |
2188 | 2200 | ||
2189 | hotkey_mask_warn_incomplete_mask(); | 2201 | if (tpacpi_lifecycle != TPACPI_LIFE_EXITING) |
2202 | hotkey_mask_warn_incomplete_mask(); | ||
2190 | 2203 | ||
2191 | return rc; | 2204 | return rc; |
2192 | } | 2205 | } |
@@ -3182,6 +3195,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3182 | int res, i; | 3195 | int res, i; |
3183 | int status; | 3196 | int status; |
3184 | int hkeyv; | 3197 | int hkeyv; |
3198 | bool radiosw_state = false; | ||
3199 | bool tabletsw_state = false; | ||
3185 | 3200 | ||
3186 | unsigned long quirks; | 3201 | unsigned long quirks; |
3187 | 3202 | ||
@@ -3287,6 +3302,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3287 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 3302 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
3288 | if (dbg_wlswemul) { | 3303 | if (dbg_wlswemul) { |
3289 | tp_features.hotkey_wlsw = 1; | 3304 | tp_features.hotkey_wlsw = 1; |
3305 | radiosw_state = !!tpacpi_wlsw_emulstate; | ||
3290 | printk(TPACPI_INFO | 3306 | printk(TPACPI_INFO |
3291 | "radio switch emulation enabled\n"); | 3307 | "radio switch emulation enabled\n"); |
3292 | } else | 3308 | } else |
@@ -3294,6 +3310,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3294 | /* Not all thinkpads have a hardware radio switch */ | 3310 | /* Not all thinkpads have a hardware radio switch */ |
3295 | if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) { | 3311 | if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) { |
3296 | tp_features.hotkey_wlsw = 1; | 3312 | tp_features.hotkey_wlsw = 1; |
3313 | radiosw_state = !!status; | ||
3297 | printk(TPACPI_INFO | 3314 | printk(TPACPI_INFO |
3298 | "radio switch found; radios are %s\n", | 3315 | "radio switch found; radios are %s\n", |
3299 | enabled(status, 0)); | 3316 | enabled(status, 0)); |
@@ -3305,11 +3322,11 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3305 | /* For X41t, X60t, X61t Tablets... */ | 3322 | /* For X41t, X60t, X61t Tablets... */ |
3306 | if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) { | 3323 | if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) { |
3307 | tp_features.hotkey_tablet = 1; | 3324 | tp_features.hotkey_tablet = 1; |
3325 | tabletsw_state = !!(status & TP_HOTKEY_TABLET_MASK); | ||
3308 | printk(TPACPI_INFO | 3326 | printk(TPACPI_INFO |
3309 | "possible tablet mode switch found; " | 3327 | "possible tablet mode switch found; " |
3310 | "ThinkPad in %s mode\n", | 3328 | "ThinkPad in %s mode\n", |
3311 | (status & TP_HOTKEY_TABLET_MASK)? | 3329 | (tabletsw_state) ? "tablet" : "laptop"); |
3312 | "tablet" : "laptop"); | ||
3313 | res = add_to_attr_set(hotkey_dev_attributes, | 3330 | res = add_to_attr_set(hotkey_dev_attributes, |
3314 | &dev_attr_hotkey_tablet_mode.attr); | 3331 | &dev_attr_hotkey_tablet_mode.attr); |
3315 | } | 3332 | } |
@@ -3344,16 +3361,14 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3344 | TPACPI_HOTKEY_MAP_SIZE); | 3361 | TPACPI_HOTKEY_MAP_SIZE); |
3345 | } | 3362 | } |
3346 | 3363 | ||
3347 | set_bit(EV_KEY, tpacpi_inputdev->evbit); | 3364 | input_set_capability(tpacpi_inputdev, EV_MSC, MSC_SCAN); |
3348 | set_bit(EV_MSC, tpacpi_inputdev->evbit); | ||
3349 | set_bit(MSC_SCAN, tpacpi_inputdev->mscbit); | ||
3350 | tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE; | 3365 | tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE; |
3351 | tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN; | 3366 | tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN; |
3352 | tpacpi_inputdev->keycode = hotkey_keycode_map; | 3367 | tpacpi_inputdev->keycode = hotkey_keycode_map; |
3353 | for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) { | 3368 | for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) { |
3354 | if (hotkey_keycode_map[i] != KEY_RESERVED) { | 3369 | if (hotkey_keycode_map[i] != KEY_RESERVED) { |
3355 | set_bit(hotkey_keycode_map[i], | 3370 | input_set_capability(tpacpi_inputdev, EV_KEY, |
3356 | tpacpi_inputdev->keybit); | 3371 | hotkey_keycode_map[i]); |
3357 | } else { | 3372 | } else { |
3358 | if (i < sizeof(hotkey_reserved_mask)*8) | 3373 | if (i < sizeof(hotkey_reserved_mask)*8) |
3359 | hotkey_reserved_mask |= 1 << i; | 3374 | hotkey_reserved_mask |= 1 << i; |
@@ -3361,12 +3376,14 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3361 | } | 3376 | } |
3362 | 3377 | ||
3363 | if (tp_features.hotkey_wlsw) { | 3378 | if (tp_features.hotkey_wlsw) { |
3364 | set_bit(EV_SW, tpacpi_inputdev->evbit); | 3379 | input_set_capability(tpacpi_inputdev, EV_SW, SW_RFKILL_ALL); |
3365 | set_bit(SW_RFKILL_ALL, tpacpi_inputdev->swbit); | 3380 | input_report_switch(tpacpi_inputdev, |
3381 | SW_RFKILL_ALL, radiosw_state); | ||
3366 | } | 3382 | } |
3367 | if (tp_features.hotkey_tablet) { | 3383 | if (tp_features.hotkey_tablet) { |
3368 | set_bit(EV_SW, tpacpi_inputdev->evbit); | 3384 | input_set_capability(tpacpi_inputdev, EV_SW, SW_TABLET_MODE); |
3369 | set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit); | 3385 | input_report_switch(tpacpi_inputdev, |
3386 | SW_TABLET_MODE, tabletsw_state); | ||
3370 | } | 3387 | } |
3371 | 3388 | ||
3372 | /* Do not issue duplicate brightness change events to | 3389 | /* Do not issue duplicate brightness change events to |
@@ -3433,8 +3450,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3433 | tpacpi_inputdev->close = &hotkey_inputdev_close; | 3450 | tpacpi_inputdev->close = &hotkey_inputdev_close; |
3434 | 3451 | ||
3435 | hotkey_poll_setup_safe(true); | 3452 | hotkey_poll_setup_safe(true); |
3436 | tpacpi_send_radiosw_update(); | ||
3437 | tpacpi_input_send_tabletsw(); | ||
3438 | 3453 | ||
3439 | return 0; | 3454 | return 0; |
3440 | 3455 | ||
@@ -3542,49 +3557,57 @@ static bool hotkey_notify_usrevent(const u32 hkey, | |||
3542 | } | 3557 | } |
3543 | } | 3558 | } |
3544 | 3559 | ||
3560 | static void thermal_dump_all_sensors(void); | ||
3561 | |||
3545 | static bool hotkey_notify_thermal(const u32 hkey, | 3562 | static bool hotkey_notify_thermal(const u32 hkey, |
3546 | bool *send_acpi_ev, | 3563 | bool *send_acpi_ev, |
3547 | bool *ignore_acpi_ev) | 3564 | bool *ignore_acpi_ev) |
3548 | { | 3565 | { |
3566 | bool known = true; | ||
3567 | |||
3549 | /* 0x6000-0x6FFF: thermal alarms */ | 3568 | /* 0x6000-0x6FFF: thermal alarms */ |
3550 | *send_acpi_ev = true; | 3569 | *send_acpi_ev = true; |
3551 | *ignore_acpi_ev = false; | 3570 | *ignore_acpi_ev = false; |
3552 | 3571 | ||
3553 | switch (hkey) { | 3572 | switch (hkey) { |
3573 | case TP_HKEY_EV_THM_TABLE_CHANGED: | ||
3574 | printk(TPACPI_INFO | ||
3575 | "EC reports that Thermal Table has changed\n"); | ||
3576 | /* recommended action: do nothing, we don't have | ||
3577 | * Lenovo ATM information */ | ||
3578 | return true; | ||
3554 | case TP_HKEY_EV_ALARM_BAT_HOT: | 3579 | case TP_HKEY_EV_ALARM_BAT_HOT: |
3555 | printk(TPACPI_CRIT | 3580 | printk(TPACPI_CRIT |
3556 | "THERMAL ALARM: battery is too hot!\n"); | 3581 | "THERMAL ALARM: battery is too hot!\n"); |
3557 | /* recommended action: warn user through gui */ | 3582 | /* recommended action: warn user through gui */ |
3558 | return true; | 3583 | break; |
3559 | case TP_HKEY_EV_ALARM_BAT_XHOT: | 3584 | case TP_HKEY_EV_ALARM_BAT_XHOT: |
3560 | printk(TPACPI_ALERT | 3585 | printk(TPACPI_ALERT |
3561 | "THERMAL EMERGENCY: battery is extremely hot!\n"); | 3586 | "THERMAL EMERGENCY: battery is extremely hot!\n"); |
3562 | /* recommended action: immediate sleep/hibernate */ | 3587 | /* recommended action: immediate sleep/hibernate */ |
3563 | return true; | 3588 | break; |
3564 | case TP_HKEY_EV_ALARM_SENSOR_HOT: | 3589 | case TP_HKEY_EV_ALARM_SENSOR_HOT: |
3565 | printk(TPACPI_CRIT | 3590 | printk(TPACPI_CRIT |
3566 | "THERMAL ALARM: " | 3591 | "THERMAL ALARM: " |
3567 | "a sensor reports something is too hot!\n"); | 3592 | "a sensor reports something is too hot!\n"); |
3568 | /* recommended action: warn user through gui, that */ | 3593 | /* recommended action: warn user through gui, that */ |
3569 | /* some internal component is too hot */ | 3594 | /* some internal component is too hot */ |
3570 | return true; | 3595 | break; |
3571 | case TP_HKEY_EV_ALARM_SENSOR_XHOT: | 3596 | case TP_HKEY_EV_ALARM_SENSOR_XHOT: |
3572 | printk(TPACPI_ALERT | 3597 | printk(TPACPI_ALERT |
3573 | "THERMAL EMERGENCY: " | 3598 | "THERMAL EMERGENCY: " |
3574 | "a sensor reports something is extremely hot!\n"); | 3599 | "a sensor reports something is extremely hot!\n"); |
3575 | /* recommended action: immediate sleep/hibernate */ | 3600 | /* recommended action: immediate sleep/hibernate */ |
3576 | return true; | 3601 | break; |
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; | ||
3583 | default: | 3602 | default: |
3584 | printk(TPACPI_ALERT | 3603 | printk(TPACPI_ALERT |
3585 | "THERMAL ALERT: unknown thermal alarm received\n"); | 3604 | "THERMAL ALERT: unknown thermal alarm received\n"); |
3586 | return false; | 3605 | known = false; |
3587 | } | 3606 | } |
3607 | |||
3608 | thermal_dump_all_sensors(); | ||
3609 | |||
3610 | return known; | ||
3588 | } | 3611 | } |
3589 | 3612 | ||
3590 | static void hotkey_notify(struct ibm_struct *ibm, u32 event) | 3613 | static void hotkey_notify(struct ibm_struct *ibm, u32 event) |
@@ -3727,14 +3750,13 @@ static void hotkey_resume(void) | |||
3727 | } | 3750 | } |
3728 | 3751 | ||
3729 | /* procfs -------------------------------------------------------------- */ | 3752 | /* procfs -------------------------------------------------------------- */ |
3730 | static int hotkey_read(char *p) | 3753 | static int hotkey_read(struct seq_file *m) |
3731 | { | 3754 | { |
3732 | int res, status; | 3755 | int res, status; |
3733 | int len = 0; | ||
3734 | 3756 | ||
3735 | if (!tp_features.hotkey) { | 3757 | if (!tp_features.hotkey) { |
3736 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 3758 | seq_printf(m, "status:\t\tnot supported\n"); |
3737 | return len; | 3759 | return 0; |
3738 | } | 3760 | } |
3739 | 3761 | ||
3740 | if (mutex_lock_killable(&hotkey_mutex)) | 3762 | if (mutex_lock_killable(&hotkey_mutex)) |
@@ -3746,17 +3768,16 @@ static int hotkey_read(char *p) | |||
3746 | if (res) | 3768 | if (res) |
3747 | return res; | 3769 | return res; |
3748 | 3770 | ||
3749 | len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0)); | 3771 | seq_printf(m, "status:\t\t%s\n", enabled(status, 0)); |
3750 | if (hotkey_all_mask) { | 3772 | if (hotkey_all_mask) { |
3751 | len += sprintf(p + len, "mask:\t\t0x%08x\n", hotkey_user_mask); | 3773 | seq_printf(m, "mask:\t\t0x%08x\n", hotkey_user_mask); |
3752 | len += sprintf(p + len, | 3774 | seq_printf(m, "commands:\tenable, disable, reset, <mask>\n"); |
3753 | "commands:\tenable, disable, reset, <mask>\n"); | ||
3754 | } else { | 3775 | } else { |
3755 | len += sprintf(p + len, "mask:\t\tnot supported\n"); | 3776 | seq_printf(m, "mask:\t\tnot supported\n"); |
3756 | len += sprintf(p + len, "commands:\tenable, disable, reset\n"); | 3777 | seq_printf(m, "commands:\tenable, disable, reset\n"); |
3757 | } | 3778 | } |
3758 | 3779 | ||
3759 | return len; | 3780 | return 0; |
3760 | } | 3781 | } |
3761 | 3782 | ||
3762 | static void hotkey_enabledisable_warn(bool enable) | 3783 | static void hotkey_enabledisable_warn(bool enable) |
@@ -3863,15 +3884,6 @@ enum { | |||
3863 | 3884 | ||
3864 | #define TPACPI_RFK_BLUETOOTH_SW_NAME "tpacpi_bluetooth_sw" | 3885 | #define TPACPI_RFK_BLUETOOTH_SW_NAME "tpacpi_bluetooth_sw" |
3865 | 3886 | ||
3866 | static void bluetooth_suspend(pm_message_t state) | ||
3867 | { | ||
3868 | /* Try to make sure radio will resume powered off */ | ||
3869 | if (!acpi_evalf(NULL, NULL, "\\BLTH", "vd", | ||
3870 | TP_ACPI_BLTH_PWR_OFF_ON_RESUME)) | ||
3871 | vdbg_printk(TPACPI_DBG_RFKILL, | ||
3872 | "bluetooth power down on resume request failed\n"); | ||
3873 | } | ||
3874 | |||
3875 | static int bluetooth_get_status(void) | 3887 | static int bluetooth_get_status(void) |
3876 | { | 3888 | { |
3877 | int status; | 3889 | int status; |
@@ -3905,10 +3917,9 @@ static int bluetooth_set_status(enum tpacpi_rfkill_state state) | |||
3905 | #endif | 3917 | #endif |
3906 | 3918 | ||
3907 | /* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */ | 3919 | /* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */ |
3920 | status = TP_ACPI_BLUETOOTH_RESUMECTRL; | ||
3908 | if (state == TPACPI_RFK_RADIO_ON) | 3921 | if (state == TPACPI_RFK_RADIO_ON) |
3909 | status = TP_ACPI_BLUETOOTH_RADIOSSW; | 3922 | status |= TP_ACPI_BLUETOOTH_RADIOSSW; |
3910 | else | ||
3911 | status = 0; | ||
3912 | 3923 | ||
3913 | if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) | 3924 | if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) |
3914 | return -EIO; | 3925 | return -EIO; |
@@ -4032,9 +4043,9 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) | |||
4032 | } | 4043 | } |
4033 | 4044 | ||
4034 | /* procfs -------------------------------------------------------------- */ | 4045 | /* procfs -------------------------------------------------------------- */ |
4035 | static int bluetooth_read(char *p) | 4046 | static int bluetooth_read(struct seq_file *m) |
4036 | { | 4047 | { |
4037 | return tpacpi_rfk_procfs_read(TPACPI_RFK_BLUETOOTH_SW_ID, p); | 4048 | return tpacpi_rfk_procfs_read(TPACPI_RFK_BLUETOOTH_SW_ID, m); |
4038 | } | 4049 | } |
4039 | 4050 | ||
4040 | static int bluetooth_write(char *buf) | 4051 | static int bluetooth_write(char *buf) |
@@ -4047,7 +4058,6 @@ static struct ibm_struct bluetooth_driver_data = { | |||
4047 | .read = bluetooth_read, | 4058 | .read = bluetooth_read, |
4048 | .write = bluetooth_write, | 4059 | .write = bluetooth_write, |
4049 | .exit = bluetooth_exit, | 4060 | .exit = bluetooth_exit, |
4050 | .suspend = bluetooth_suspend, | ||
4051 | .shutdown = bluetooth_shutdown, | 4061 | .shutdown = bluetooth_shutdown, |
4052 | }; | 4062 | }; |
4053 | 4063 | ||
@@ -4065,15 +4075,6 @@ enum { | |||
4065 | 4075 | ||
4066 | #define TPACPI_RFK_WWAN_SW_NAME "tpacpi_wwan_sw" | 4076 | #define TPACPI_RFK_WWAN_SW_NAME "tpacpi_wwan_sw" |
4067 | 4077 | ||
4068 | static void wan_suspend(pm_message_t state) | ||
4069 | { | ||
4070 | /* Try to make sure radio will resume powered off */ | ||
4071 | if (!acpi_evalf(NULL, NULL, "\\WGSV", "qvd", | ||
4072 | TP_ACPI_WGSV_PWR_OFF_ON_RESUME)) | ||
4073 | vdbg_printk(TPACPI_DBG_RFKILL, | ||
4074 | "WWAN power down on resume request failed\n"); | ||
4075 | } | ||
4076 | |||
4077 | static int wan_get_status(void) | 4078 | static int wan_get_status(void) |
4078 | { | 4079 | { |
4079 | int status; | 4080 | int status; |
@@ -4106,11 +4107,10 @@ static int wan_set_status(enum tpacpi_rfkill_state state) | |||
4106 | } | 4107 | } |
4107 | #endif | 4108 | #endif |
4108 | 4109 | ||
4109 | /* We make sure to keep TP_ACPI_WANCARD_RESUMECTRL off */ | 4110 | /* We make sure to set TP_ACPI_WANCARD_RESUMECTRL */ |
4111 | status = TP_ACPI_WANCARD_RESUMECTRL; | ||
4110 | if (state == TPACPI_RFK_RADIO_ON) | 4112 | if (state == TPACPI_RFK_RADIO_ON) |
4111 | status = TP_ACPI_WANCARD_RADIOSSW; | 4113 | status |= TP_ACPI_WANCARD_RADIOSSW; |
4112 | else | ||
4113 | status = 0; | ||
4114 | 4114 | ||
4115 | if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) | 4115 | if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) |
4116 | return -EIO; | 4116 | return -EIO; |
@@ -4233,9 +4233,9 @@ static int __init wan_init(struct ibm_init_struct *iibm) | |||
4233 | } | 4233 | } |
4234 | 4234 | ||
4235 | /* procfs -------------------------------------------------------------- */ | 4235 | /* procfs -------------------------------------------------------------- */ |
4236 | static int wan_read(char *p) | 4236 | static int wan_read(struct seq_file *m) |
4237 | { | 4237 | { |
4238 | return tpacpi_rfk_procfs_read(TPACPI_RFK_WWAN_SW_ID, p); | 4238 | return tpacpi_rfk_procfs_read(TPACPI_RFK_WWAN_SW_ID, m); |
4239 | } | 4239 | } |
4240 | 4240 | ||
4241 | static int wan_write(char *buf) | 4241 | static int wan_write(char *buf) |
@@ -4248,7 +4248,6 @@ static struct ibm_struct wan_driver_data = { | |||
4248 | .read = wan_read, | 4248 | .read = wan_read, |
4249 | .write = wan_write, | 4249 | .write = wan_write, |
4250 | .exit = wan_exit, | 4250 | .exit = wan_exit, |
4251 | .suspend = wan_suspend, | ||
4252 | .shutdown = wan_shutdown, | 4251 | .shutdown = wan_shutdown, |
4253 | }; | 4252 | }; |
4254 | 4253 | ||
@@ -4611,14 +4610,13 @@ static int video_expand_toggle(void) | |||
4611 | /* not reached */ | 4610 | /* not reached */ |
4612 | } | 4611 | } |
4613 | 4612 | ||
4614 | static int video_read(char *p) | 4613 | static int video_read(struct seq_file *m) |
4615 | { | 4614 | { |
4616 | int status, autosw; | 4615 | int status, autosw; |
4617 | int len = 0; | ||
4618 | 4616 | ||
4619 | if (video_supported == TPACPI_VIDEO_NONE) { | 4617 | if (video_supported == TPACPI_VIDEO_NONE) { |
4620 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 4618 | seq_printf(m, "status:\t\tnot supported\n"); |
4621 | return len; | 4619 | return 0; |
4622 | } | 4620 | } |
4623 | 4621 | ||
4624 | status = video_outputsw_get(); | 4622 | status = video_outputsw_get(); |
@@ -4629,20 +4627,20 @@ static int video_read(char *p) | |||
4629 | if (autosw < 0) | 4627 | if (autosw < 0) |
4630 | return autosw; | 4628 | return autosw; |
4631 | 4629 | ||
4632 | len += sprintf(p + len, "status:\t\tsupported\n"); | 4630 | seq_printf(m, "status:\t\tsupported\n"); |
4633 | len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0)); | 4631 | seq_printf(m, "lcd:\t\t%s\n", enabled(status, 0)); |
4634 | len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1)); | 4632 | seq_printf(m, "crt:\t\t%s\n", enabled(status, 1)); |
4635 | if (video_supported == TPACPI_VIDEO_NEW) | 4633 | if (video_supported == TPACPI_VIDEO_NEW) |
4636 | len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3)); | 4634 | seq_printf(m, "dvi:\t\t%s\n", enabled(status, 3)); |
4637 | len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0)); | 4635 | seq_printf(m, "auto:\t\t%s\n", enabled(autosw, 0)); |
4638 | len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n"); | 4636 | seq_printf(m, "commands:\tlcd_enable, lcd_disable\n"); |
4639 | len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n"); | 4637 | seq_printf(m, "commands:\tcrt_enable, crt_disable\n"); |
4640 | if (video_supported == TPACPI_VIDEO_NEW) | 4638 | if (video_supported == TPACPI_VIDEO_NEW) |
4641 | len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n"); | 4639 | seq_printf(m, "commands:\tdvi_enable, dvi_disable\n"); |
4642 | len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n"); | 4640 | seq_printf(m, "commands:\tauto_enable, auto_disable\n"); |
4643 | len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n"); | 4641 | seq_printf(m, "commands:\tvideo_switch, expand_toggle\n"); |
4644 | 4642 | ||
4645 | return len; | 4643 | return 0; |
4646 | } | 4644 | } |
4647 | 4645 | ||
4648 | static int video_write(char *buf) | 4646 | static int video_write(char *buf) |
@@ -4834,25 +4832,24 @@ static void light_exit(void) | |||
4834 | flush_workqueue(tpacpi_wq); | 4832 | flush_workqueue(tpacpi_wq); |
4835 | } | 4833 | } |
4836 | 4834 | ||
4837 | static int light_read(char *p) | 4835 | static int light_read(struct seq_file *m) |
4838 | { | 4836 | { |
4839 | int len = 0; | ||
4840 | int status; | 4837 | int status; |
4841 | 4838 | ||
4842 | if (!tp_features.light) { | 4839 | if (!tp_features.light) { |
4843 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 4840 | seq_printf(m, "status:\t\tnot supported\n"); |
4844 | } else if (!tp_features.light_status) { | 4841 | } else if (!tp_features.light_status) { |
4845 | len += sprintf(p + len, "status:\t\tunknown\n"); | 4842 | seq_printf(m, "status:\t\tunknown\n"); |
4846 | len += sprintf(p + len, "commands:\ton, off\n"); | 4843 | seq_printf(m, "commands:\ton, off\n"); |
4847 | } else { | 4844 | } else { |
4848 | status = light_get_status(); | 4845 | status = light_get_status(); |
4849 | if (status < 0) | 4846 | if (status < 0) |
4850 | return status; | 4847 | return status; |
4851 | len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0)); | 4848 | seq_printf(m, "status:\t\t%s\n", onoff(status, 0)); |
4852 | len += sprintf(p + len, "commands:\ton, off\n"); | 4849 | seq_printf(m, "commands:\ton, off\n"); |
4853 | } | 4850 | } |
4854 | 4851 | ||
4855 | return len; | 4852 | return 0; |
4856 | } | 4853 | } |
4857 | 4854 | ||
4858 | static int light_write(char *buf) | 4855 | static int light_write(char *buf) |
@@ -4930,20 +4927,18 @@ static void cmos_exit(void) | |||
4930 | device_remove_file(&tpacpi_pdev->dev, &dev_attr_cmos_command); | 4927 | device_remove_file(&tpacpi_pdev->dev, &dev_attr_cmos_command); |
4931 | } | 4928 | } |
4932 | 4929 | ||
4933 | static int cmos_read(char *p) | 4930 | static int cmos_read(struct seq_file *m) |
4934 | { | 4931 | { |
4935 | int len = 0; | ||
4936 | |||
4937 | /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, | 4932 | /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, |
4938 | R30, R31, T20-22, X20-21 */ | 4933 | R30, R31, T20-22, X20-21 */ |
4939 | if (!cmos_handle) | 4934 | if (!cmos_handle) |
4940 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 4935 | seq_printf(m, "status:\t\tnot supported\n"); |
4941 | else { | 4936 | else { |
4942 | len += sprintf(p + len, "status:\t\tsupported\n"); | 4937 | seq_printf(m, "status:\t\tsupported\n"); |
4943 | len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-21)\n"); | 4938 | seq_printf(m, "commands:\t<cmd> (<cmd> is 0-21)\n"); |
4944 | } | 4939 | } |
4945 | 4940 | ||
4946 | return len; | 4941 | return 0; |
4947 | } | 4942 | } |
4948 | 4943 | ||
4949 | static int cmos_write(char *buf) | 4944 | static int cmos_write(char *buf) |
@@ -5318,15 +5313,13 @@ static int __init led_init(struct ibm_init_struct *iibm) | |||
5318 | ((s) == TPACPI_LED_OFF ? "off" : \ | 5313 | ((s) == TPACPI_LED_OFF ? "off" : \ |
5319 | ((s) == TPACPI_LED_ON ? "on" : "blinking")) | 5314 | ((s) == TPACPI_LED_ON ? "on" : "blinking")) |
5320 | 5315 | ||
5321 | static int led_read(char *p) | 5316 | static int led_read(struct seq_file *m) |
5322 | { | 5317 | { |
5323 | int len = 0; | ||
5324 | |||
5325 | if (!led_supported) { | 5318 | if (!led_supported) { |
5326 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 5319 | seq_printf(m, "status:\t\tnot supported\n"); |
5327 | return len; | 5320 | return 0; |
5328 | } | 5321 | } |
5329 | len += sprintf(p + len, "status:\t\tsupported\n"); | 5322 | seq_printf(m, "status:\t\tsupported\n"); |
5330 | 5323 | ||
5331 | if (led_supported == TPACPI_LED_570) { | 5324 | if (led_supported == TPACPI_LED_570) { |
5332 | /* 570 */ | 5325 | /* 570 */ |
@@ -5335,15 +5328,15 @@ static int led_read(char *p) | |||
5335 | status = led_get_status(i); | 5328 | status = led_get_status(i); |
5336 | if (status < 0) | 5329 | if (status < 0) |
5337 | return -EIO; | 5330 | return -EIO; |
5338 | len += sprintf(p + len, "%d:\t\t%s\n", | 5331 | seq_printf(m, "%d:\t\t%s\n", |
5339 | i, str_led_status(status)); | 5332 | i, str_led_status(status)); |
5340 | } | 5333 | } |
5341 | } | 5334 | } |
5342 | 5335 | ||
5343 | len += sprintf(p + len, "commands:\t" | 5336 | seq_printf(m, "commands:\t" |
5344 | "<led> on, <led> off, <led> blink (<led> is 0-15)\n"); | 5337 | "<led> on, <led> off, <led> blink (<led> is 0-15)\n"); |
5345 | 5338 | ||
5346 | return len; | 5339 | return 0; |
5347 | } | 5340 | } |
5348 | 5341 | ||
5349 | static int led_write(char *buf) | 5342 | static int led_write(char *buf) |
@@ -5416,18 +5409,16 @@ static int __init beep_init(struct ibm_init_struct *iibm) | |||
5416 | return (beep_handle)? 0 : 1; | 5409 | return (beep_handle)? 0 : 1; |
5417 | } | 5410 | } |
5418 | 5411 | ||
5419 | static int beep_read(char *p) | 5412 | static int beep_read(struct seq_file *m) |
5420 | { | 5413 | { |
5421 | int len = 0; | ||
5422 | |||
5423 | if (!beep_handle) | 5414 | if (!beep_handle) |
5424 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 5415 | seq_printf(m, "status:\t\tnot supported\n"); |
5425 | else { | 5416 | else { |
5426 | len += sprintf(p + len, "status:\t\tsupported\n"); | 5417 | seq_printf(m, "status:\t\tsupported\n"); |
5427 | len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-17)\n"); | 5418 | seq_printf(m, "commands:\t<cmd> (<cmd> is 0-17)\n"); |
5428 | } | 5419 | } |
5429 | 5420 | ||
5430 | return len; | 5421 | return 0; |
5431 | } | 5422 | } |
5432 | 5423 | ||
5433 | static int beep_write(char *buf) | 5424 | static int beep_write(char *buf) |
@@ -5480,8 +5471,11 @@ enum { /* TPACPI_THERMAL_TPEC_* */ | |||
5480 | TP_EC_THERMAL_TMP0 = 0x78, /* ACPI EC regs TMP 0..7 */ | 5471 | TP_EC_THERMAL_TMP0 = 0x78, /* ACPI EC regs TMP 0..7 */ |
5481 | TP_EC_THERMAL_TMP8 = 0xC0, /* ACPI EC regs TMP 8..15 */ | 5472 | TP_EC_THERMAL_TMP8 = 0xC0, /* ACPI EC regs TMP 8..15 */ |
5482 | TP_EC_THERMAL_TMP_NA = -128, /* ACPI EC sensor not available */ | 5473 | TP_EC_THERMAL_TMP_NA = -128, /* ACPI EC sensor not available */ |
5474 | |||
5475 | TPACPI_THERMAL_SENSOR_NA = -128000, /* Sensor not available */ | ||
5483 | }; | 5476 | }; |
5484 | 5477 | ||
5478 | |||
5485 | #define TPACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */ | 5479 | #define TPACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */ |
5486 | struct ibm_thermal_sensors_struct { | 5480 | struct ibm_thermal_sensors_struct { |
5487 | s32 temp[TPACPI_MAX_THERMAL_SENSORS]; | 5481 | s32 temp[TPACPI_MAX_THERMAL_SENSORS]; |
@@ -5571,6 +5565,28 @@ static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s) | |||
5571 | return n; | 5565 | return n; |
5572 | } | 5566 | } |
5573 | 5567 | ||
5568 | static void thermal_dump_all_sensors(void) | ||
5569 | { | ||
5570 | int n, i; | ||
5571 | struct ibm_thermal_sensors_struct t; | ||
5572 | |||
5573 | n = thermal_get_sensors(&t); | ||
5574 | if (n <= 0) | ||
5575 | return; | ||
5576 | |||
5577 | printk(TPACPI_NOTICE | ||
5578 | "temperatures (Celsius):"); | ||
5579 | |||
5580 | for (i = 0; i < n; i++) { | ||
5581 | if (t.temp[i] != TPACPI_THERMAL_SENSOR_NA) | ||
5582 | printk(KERN_CONT " %d", (int)(t.temp[i] / 1000)); | ||
5583 | else | ||
5584 | printk(KERN_CONT " N/A"); | ||
5585 | } | ||
5586 | |||
5587 | printk(KERN_CONT "\n"); | ||
5588 | } | ||
5589 | |||
5574 | /* sysfs temp##_input -------------------------------------------------- */ | 5590 | /* sysfs temp##_input -------------------------------------------------- */ |
5575 | 5591 | ||
5576 | static ssize_t thermal_temp_input_show(struct device *dev, | 5592 | static ssize_t thermal_temp_input_show(struct device *dev, |
@@ -5586,7 +5602,7 @@ static ssize_t thermal_temp_input_show(struct device *dev, | |||
5586 | res = thermal_get_sensor(idx, &value); | 5602 | res = thermal_get_sensor(idx, &value); |
5587 | if (res) | 5603 | if (res) |
5588 | return res; | 5604 | return res; |
5589 | if (value == TP_EC_THERMAL_TMP_NA * 1000) | 5605 | if (value == TPACPI_THERMAL_SENSOR_NA) |
5590 | return -ENXIO; | 5606 | return -ENXIO; |
5591 | 5607 | ||
5592 | return snprintf(buf, PAGE_SIZE, "%d\n", value); | 5608 | return snprintf(buf, PAGE_SIZE, "%d\n", value); |
@@ -5763,9 +5779,8 @@ static void thermal_exit(void) | |||
5763 | } | 5779 | } |
5764 | } | 5780 | } |
5765 | 5781 | ||
5766 | static int thermal_read(char *p) | 5782 | static int thermal_read(struct seq_file *m) |
5767 | { | 5783 | { |
5768 | int len = 0; | ||
5769 | int n, i; | 5784 | int n, i; |
5770 | struct ibm_thermal_sensors_struct t; | 5785 | struct ibm_thermal_sensors_struct t; |
5771 | 5786 | ||
@@ -5773,16 +5788,16 @@ static int thermal_read(char *p) | |||
5773 | if (unlikely(n < 0)) | 5788 | if (unlikely(n < 0)) |
5774 | return n; | 5789 | return n; |
5775 | 5790 | ||
5776 | len += sprintf(p + len, "temperatures:\t"); | 5791 | seq_printf(m, "temperatures:\t"); |
5777 | 5792 | ||
5778 | if (n > 0) { | 5793 | if (n > 0) { |
5779 | for (i = 0; i < (n - 1); i++) | 5794 | for (i = 0; i < (n - 1); i++) |
5780 | len += sprintf(p + len, "%d ", t.temp[i] / 1000); | 5795 | seq_printf(m, "%d ", t.temp[i] / 1000); |
5781 | len += sprintf(p + len, "%d\n", t.temp[i] / 1000); | 5796 | seq_printf(m, "%d\n", t.temp[i] / 1000); |
5782 | } else | 5797 | } else |
5783 | len += sprintf(p + len, "not supported\n"); | 5798 | seq_printf(m, "not supported\n"); |
5784 | 5799 | ||
5785 | return len; | 5800 | return 0; |
5786 | } | 5801 | } |
5787 | 5802 | ||
5788 | static struct ibm_struct thermal_driver_data = { | 5803 | static struct ibm_struct thermal_driver_data = { |
@@ -5797,39 +5812,38 @@ static struct ibm_struct thermal_driver_data = { | |||
5797 | 5812 | ||
5798 | static u8 ecdump_regs[256]; | 5813 | static u8 ecdump_regs[256]; |
5799 | 5814 | ||
5800 | static int ecdump_read(char *p) | 5815 | static int ecdump_read(struct seq_file *m) |
5801 | { | 5816 | { |
5802 | int len = 0; | ||
5803 | int i, j; | 5817 | int i, j; |
5804 | u8 v; | 5818 | u8 v; |
5805 | 5819 | ||
5806 | len += sprintf(p + len, "EC " | 5820 | seq_printf(m, "EC " |
5807 | " +00 +01 +02 +03 +04 +05 +06 +07" | 5821 | " +00 +01 +02 +03 +04 +05 +06 +07" |
5808 | " +08 +09 +0a +0b +0c +0d +0e +0f\n"); | 5822 | " +08 +09 +0a +0b +0c +0d +0e +0f\n"); |
5809 | for (i = 0; i < 256; i += 16) { | 5823 | for (i = 0; i < 256; i += 16) { |
5810 | len += sprintf(p + len, "EC 0x%02x:", i); | 5824 | seq_printf(m, "EC 0x%02x:", i); |
5811 | for (j = 0; j < 16; j++) { | 5825 | for (j = 0; j < 16; j++) { |
5812 | if (!acpi_ec_read(i + j, &v)) | 5826 | if (!acpi_ec_read(i + j, &v)) |
5813 | break; | 5827 | break; |
5814 | if (v != ecdump_regs[i + j]) | 5828 | if (v != ecdump_regs[i + j]) |
5815 | len += sprintf(p + len, " *%02x", v); | 5829 | seq_printf(m, " *%02x", v); |
5816 | else | 5830 | else |
5817 | len += sprintf(p + len, " %02x", v); | 5831 | seq_printf(m, " %02x", v); |
5818 | ecdump_regs[i + j] = v; | 5832 | ecdump_regs[i + j] = v; |
5819 | } | 5833 | } |
5820 | len += sprintf(p + len, "\n"); | 5834 | seq_putc(m, '\n'); |
5821 | if (j != 16) | 5835 | if (j != 16) |
5822 | break; | 5836 | break; |
5823 | } | 5837 | } |
5824 | 5838 | ||
5825 | /* These are way too dangerous to advertise openly... */ | 5839 | /* These are way too dangerous to advertise openly... */ |
5826 | #if 0 | 5840 | #if 0 |
5827 | len += sprintf(p + len, "commands:\t0x<offset> 0x<value>" | 5841 | seq_printf(m, "commands:\t0x<offset> 0x<value>" |
5828 | " (<offset> is 00-ff, <value> is 00-ff)\n"); | 5842 | " (<offset> is 00-ff, <value> is 00-ff)\n"); |
5829 | len += sprintf(p + len, "commands:\t0x<offset> <value> " | 5843 | seq_printf(m, "commands:\t0x<offset> <value> " |
5830 | " (<offset> is 00-ff, <value> is 0-255)\n"); | 5844 | " (<offset> is 00-ff, <value> is 0-255)\n"); |
5831 | #endif | 5845 | #endif |
5832 | return len; | 5846 | return 0; |
5833 | } | 5847 | } |
5834 | 5848 | ||
5835 | static int ecdump_write(char *buf) | 5849 | static int ecdump_write(char *buf) |
@@ -6092,6 +6106,12 @@ static int brightness_get(struct backlight_device *bd) | |||
6092 | return status & TP_EC_BACKLIGHT_LVLMSK; | 6106 | return status & TP_EC_BACKLIGHT_LVLMSK; |
6093 | } | 6107 | } |
6094 | 6108 | ||
6109 | static void tpacpi_brightness_notify_change(void) | ||
6110 | { | ||
6111 | backlight_force_update(ibm_backlight_device, | ||
6112 | BACKLIGHT_UPDATE_HOTKEY); | ||
6113 | } | ||
6114 | |||
6095 | static struct backlight_ops ibm_backlight_data = { | 6115 | static struct backlight_ops ibm_backlight_data = { |
6096 | .get_brightness = brightness_get, | 6116 | .get_brightness = brightness_get, |
6097 | .update_status = brightness_update_status, | 6117 | .update_status = brightness_update_status, |
@@ -6120,8 +6140,8 @@ static const struct tpacpi_quirk brightness_quirk_table[] __initconst = { | |||
6120 | 6140 | ||
6121 | /* Models with Intel Extreme Graphics 2 */ | 6141 | /* Models with Intel Extreme Graphics 2 */ |
6122 | TPACPI_Q_IBM('1', 'U', TPACPI_BRGHT_Q_NOEC), | 6142 | TPACPI_Q_IBM('1', 'U', TPACPI_BRGHT_Q_NOEC), |
6123 | TPACPI_Q_IBM('1', 'V', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC), | 6143 | TPACPI_Q_IBM('1', 'V', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), |
6124 | TPACPI_Q_IBM('1', 'W', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC), | 6144 | TPACPI_Q_IBM('1', 'W', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), |
6125 | 6145 | ||
6126 | /* Models with Intel GMA900 */ | 6146 | /* Models with Intel GMA900 */ |
6127 | TPACPI_Q_IBM('7', '0', TPACPI_BRGHT_Q_NOEC), /* T43, R52 */ | 6147 | TPACPI_Q_IBM('7', '0', TPACPI_BRGHT_Q_NOEC), /* T43, R52 */ |
@@ -6246,6 +6266,12 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
6246 | ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK; | 6266 | ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK; |
6247 | backlight_update_status(ibm_backlight_device); | 6267 | backlight_update_status(ibm_backlight_device); |
6248 | 6268 | ||
6269 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, | ||
6270 | "brightness: registering brightness hotkeys " | ||
6271 | "as change notification\n"); | ||
6272 | tpacpi_hotkey_driver_mask_set(hotkey_driver_mask | ||
6273 | | TP_ACPI_HKEY_BRGHTUP_MASK | ||
6274 | | TP_ACPI_HKEY_BRGHTDWN_MASK);; | ||
6249 | return 0; | 6275 | return 0; |
6250 | } | 6276 | } |
6251 | 6277 | ||
@@ -6270,23 +6296,22 @@ static void brightness_exit(void) | |||
6270 | tpacpi_brightness_checkpoint_nvram(); | 6296 | tpacpi_brightness_checkpoint_nvram(); |
6271 | } | 6297 | } |
6272 | 6298 | ||
6273 | static int brightness_read(char *p) | 6299 | static int brightness_read(struct seq_file *m) |
6274 | { | 6300 | { |
6275 | int len = 0; | ||
6276 | int level; | 6301 | int level; |
6277 | 6302 | ||
6278 | level = brightness_get(NULL); | 6303 | level = brightness_get(NULL); |
6279 | if (level < 0) { | 6304 | if (level < 0) { |
6280 | len += sprintf(p + len, "level:\t\tunreadable\n"); | 6305 | seq_printf(m, "level:\t\tunreadable\n"); |
6281 | } else { | 6306 | } else { |
6282 | len += sprintf(p + len, "level:\t\t%d\n", level); | 6307 | seq_printf(m, "level:\t\t%d\n", level); |
6283 | len += sprintf(p + len, "commands:\tup, down\n"); | 6308 | seq_printf(m, "commands:\tup, down\n"); |
6284 | len += sprintf(p + len, "commands:\tlevel <level>" | 6309 | seq_printf(m, "commands:\tlevel <level>" |
6285 | " (<level> is 0-%d)\n", | 6310 | " (<level> is 0-%d)\n", |
6286 | (tp_features.bright_16levels) ? 15 : 7); | 6311 | (tp_features.bright_16levels) ? 15 : 7); |
6287 | } | 6312 | } |
6288 | 6313 | ||
6289 | return len; | 6314 | return 0; |
6290 | } | 6315 | } |
6291 | 6316 | ||
6292 | static int brightness_write(char *buf) | 6317 | static int brightness_write(char *buf) |
@@ -6322,6 +6347,9 @@ static int brightness_write(char *buf) | |||
6322 | * Doing it this way makes the syscall restartable in case of EINTR | 6347 | * Doing it this way makes the syscall restartable in case of EINTR |
6323 | */ | 6348 | */ |
6324 | rc = brightness_set(level); | 6349 | rc = brightness_set(level); |
6350 | if (!rc && ibm_backlight_device) | ||
6351 | backlight_force_update(ibm_backlight_device, | ||
6352 | BACKLIGHT_UPDATE_SYSFS); | ||
6325 | return (rc == -EINTR)? -ERESTARTSYS : rc; | 6353 | return (rc == -EINTR)? -ERESTARTSYS : rc; |
6326 | } | 6354 | } |
6327 | 6355 | ||
@@ -6338,99 +6366,654 @@ static struct ibm_struct brightness_driver_data = { | |||
6338 | * Volume subdriver | 6366 | * Volume subdriver |
6339 | */ | 6367 | */ |
6340 | 6368 | ||
6341 | static int volume_offset = 0x30; | 6369 | /* |
6370 | * IBM ThinkPads have a simple volume controller with MUTE gating. | ||
6371 | * Very early Lenovo ThinkPads follow the IBM ThinkPad spec. | ||
6372 | * | ||
6373 | * Since the *61 series (and probably also the later *60 series), Lenovo | ||
6374 | * ThinkPads only implement the MUTE gate. | ||
6375 | * | ||
6376 | * EC register 0x30 | ||
6377 | * Bit 6: MUTE (1 mutes sound) | ||
6378 | * Bit 3-0: Volume | ||
6379 | * Other bits should be zero as far as we know. | ||
6380 | * | ||
6381 | * This is also stored in CMOS NVRAM, byte 0x60, bit 6 (MUTE), and | ||
6382 | * bits 3-0 (volume). Other bits in NVRAM may have other functions, | ||
6383 | * such as bit 7 which is used to detect repeated presses of MUTE, | ||
6384 | * and we leave them unchanged. | ||
6385 | */ | ||
6386 | |||
6387 | #define TPACPI_ALSA_DRVNAME "ThinkPad EC" | ||
6388 | #define TPACPI_ALSA_SHRTNAME "ThinkPad Console Audio Control" | ||
6389 | #define TPACPI_ALSA_MIXERNAME TPACPI_ALSA_SHRTNAME | ||
6390 | |||
6391 | static int alsa_index = SNDRV_DEFAULT_IDX1; | ||
6392 | static char *alsa_id = "ThinkPadEC"; | ||
6393 | static int alsa_enable = SNDRV_DEFAULT_ENABLE1; | ||
6394 | |||
6395 | struct tpacpi_alsa_data { | ||
6396 | struct snd_card *card; | ||
6397 | struct snd_ctl_elem_id *ctl_mute_id; | ||
6398 | struct snd_ctl_elem_id *ctl_vol_id; | ||
6399 | }; | ||
6400 | |||
6401 | static struct snd_card *alsa_card; | ||
6402 | |||
6403 | enum { | ||
6404 | TP_EC_AUDIO = 0x30, | ||
6405 | |||
6406 | /* TP_EC_AUDIO bits */ | ||
6407 | TP_EC_AUDIO_MUTESW = 6, | ||
6408 | |||
6409 | /* TP_EC_AUDIO bitmasks */ | ||
6410 | TP_EC_AUDIO_LVL_MSK = 0x0F, | ||
6411 | TP_EC_AUDIO_MUTESW_MSK = (1 << TP_EC_AUDIO_MUTESW), | ||
6412 | |||
6413 | /* Maximum volume */ | ||
6414 | TP_EC_VOLUME_MAX = 14, | ||
6415 | }; | ||
6416 | |||
6417 | enum tpacpi_volume_access_mode { | ||
6418 | TPACPI_VOL_MODE_AUTO = 0, /* Not implemented yet */ | ||
6419 | TPACPI_VOL_MODE_EC, /* Pure EC control */ | ||
6420 | TPACPI_VOL_MODE_UCMS_STEP, /* UCMS step-based control: N/A */ | ||
6421 | TPACPI_VOL_MODE_ECNVRAM, /* EC control w/ NVRAM store */ | ||
6422 | TPACPI_VOL_MODE_MAX | ||
6423 | }; | ||
6424 | |||
6425 | enum tpacpi_volume_capabilities { | ||
6426 | TPACPI_VOL_CAP_AUTO = 0, /* Use white/blacklist */ | ||
6427 | TPACPI_VOL_CAP_VOLMUTE, /* Output vol and mute */ | ||
6428 | TPACPI_VOL_CAP_MUTEONLY, /* Output mute only */ | ||
6429 | TPACPI_VOL_CAP_MAX | ||
6430 | }; | ||
6431 | |||
6432 | static enum tpacpi_volume_access_mode volume_mode = | ||
6433 | TPACPI_VOL_MODE_MAX; | ||
6434 | |||
6435 | static enum tpacpi_volume_capabilities volume_capabilities; | ||
6436 | static int volume_control_allowed; | ||
6342 | 6437 | ||
6343 | static int volume_read(char *p) | 6438 | /* |
6439 | * Used to syncronize writers to TP_EC_AUDIO and | ||
6440 | * TP_NVRAM_ADDR_MIXER, as we need to do read-modify-write | ||
6441 | */ | ||
6442 | static struct mutex volume_mutex; | ||
6443 | |||
6444 | static void tpacpi_volume_checkpoint_nvram(void) | ||
6344 | { | 6445 | { |
6345 | int len = 0; | 6446 | u8 lec = 0; |
6346 | u8 level; | 6447 | u8 b_nvram; |
6448 | u8 ec_mask; | ||
6449 | |||
6450 | if (volume_mode != TPACPI_VOL_MODE_ECNVRAM) | ||
6451 | return; | ||
6452 | if (!volume_control_allowed) | ||
6453 | return; | ||
6454 | |||
6455 | vdbg_printk(TPACPI_DBG_MIXER, | ||
6456 | "trying to checkpoint mixer state to NVRAM...\n"); | ||
6347 | 6457 | ||
6348 | if (!acpi_ec_read(volume_offset, &level)) { | 6458 | if (tp_features.mixer_no_level_control) |
6349 | len += sprintf(p + len, "level:\t\tunreadable\n"); | 6459 | ec_mask = TP_EC_AUDIO_MUTESW_MSK; |
6460 | else | ||
6461 | ec_mask = TP_EC_AUDIO_MUTESW_MSK | TP_EC_AUDIO_LVL_MSK; | ||
6462 | |||
6463 | if (mutex_lock_killable(&volume_mutex) < 0) | ||
6464 | return; | ||
6465 | |||
6466 | if (unlikely(!acpi_ec_read(TP_EC_AUDIO, &lec))) | ||
6467 | goto unlock; | ||
6468 | lec &= ec_mask; | ||
6469 | b_nvram = nvram_read_byte(TP_NVRAM_ADDR_MIXER); | ||
6470 | |||
6471 | if (lec != (b_nvram & ec_mask)) { | ||
6472 | /* NVRAM needs update */ | ||
6473 | b_nvram &= ~ec_mask; | ||
6474 | b_nvram |= lec; | ||
6475 | nvram_write_byte(b_nvram, TP_NVRAM_ADDR_MIXER); | ||
6476 | dbg_printk(TPACPI_DBG_MIXER, | ||
6477 | "updated NVRAM mixer status to 0x%02x (0x%02x)\n", | ||
6478 | (unsigned int) lec, (unsigned int) b_nvram); | ||
6350 | } else { | 6479 | } else { |
6351 | len += sprintf(p + len, "level:\t\t%d\n", level & 0xf); | 6480 | vdbg_printk(TPACPI_DBG_MIXER, |
6352 | len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6)); | 6481 | "NVRAM mixer status already is 0x%02x (0x%02x)\n", |
6353 | len += sprintf(p + len, "commands:\tup, down, mute\n"); | 6482 | (unsigned int) lec, (unsigned int) b_nvram); |
6354 | len += sprintf(p + len, "commands:\tlevel <level>" | ||
6355 | " (<level> is 0-15)\n"); | ||
6356 | } | 6483 | } |
6357 | 6484 | ||
6358 | return len; | 6485 | unlock: |
6486 | mutex_unlock(&volume_mutex); | ||
6359 | } | 6487 | } |
6360 | 6488 | ||
6361 | static int volume_write(char *buf) | 6489 | static int volume_get_status_ec(u8 *status) |
6362 | { | 6490 | { |
6363 | int cmos_cmd, inc, i; | 6491 | u8 s; |
6364 | u8 level, mute; | ||
6365 | int new_level, new_mute; | ||
6366 | char *cmd; | ||
6367 | 6492 | ||
6368 | while ((cmd = next_cmd(&buf))) { | 6493 | if (!acpi_ec_read(TP_EC_AUDIO, &s)) |
6369 | if (!acpi_ec_read(volume_offset, &level)) | 6494 | return -EIO; |
6370 | return -EIO; | ||
6371 | new_mute = mute = level & 0x40; | ||
6372 | new_level = level = level & 0xf; | ||
6373 | 6495 | ||
6374 | if (strlencmp(cmd, "up") == 0) { | 6496 | *status = s; |
6375 | if (mute) | ||
6376 | new_mute = 0; | ||
6377 | else | ||
6378 | new_level = level == 15 ? 15 : level + 1; | ||
6379 | } else if (strlencmp(cmd, "down") == 0) { | ||
6380 | if (mute) | ||
6381 | new_mute = 0; | ||
6382 | else | ||
6383 | new_level = level == 0 ? 0 : level - 1; | ||
6384 | } else if (sscanf(cmd, "level %d", &new_level) == 1 && | ||
6385 | new_level >= 0 && new_level <= 15) { | ||
6386 | /* new_level set */ | ||
6387 | } else if (strlencmp(cmd, "mute") == 0) { | ||
6388 | new_mute = 0x40; | ||
6389 | } else | ||
6390 | return -EINVAL; | ||
6391 | 6497 | ||
6392 | if (new_level != level) { | 6498 | dbg_printk(TPACPI_DBG_MIXER, "status 0x%02x\n", s); |
6393 | /* mute doesn't change */ | ||
6394 | 6499 | ||
6395 | cmos_cmd = (new_level > level) ? | 6500 | return 0; |
6396 | TP_CMOS_VOLUME_UP : TP_CMOS_VOLUME_DOWN; | 6501 | } |
6397 | inc = new_level > level ? 1 : -1; | ||
6398 | 6502 | ||
6399 | if (mute && (issue_thinkpad_cmos_command(cmos_cmd) || | 6503 | static int volume_get_status(u8 *status) |
6400 | !acpi_ec_write(volume_offset, level))) | 6504 | { |
6401 | return -EIO; | 6505 | return volume_get_status_ec(status); |
6506 | } | ||
6402 | 6507 | ||
6403 | for (i = level; i != new_level; i += inc) | 6508 | static int volume_set_status_ec(const u8 status) |
6404 | if (issue_thinkpad_cmos_command(cmos_cmd) || | 6509 | { |
6405 | !acpi_ec_write(volume_offset, i + inc)) | 6510 | if (!acpi_ec_write(TP_EC_AUDIO, status)) |
6406 | return -EIO; | 6511 | return -EIO; |
6407 | 6512 | ||
6408 | if (mute && | 6513 | dbg_printk(TPACPI_DBG_MIXER, "set EC mixer to 0x%02x\n", status); |
6409 | (issue_thinkpad_cmos_command(TP_CMOS_VOLUME_MUTE) || | 6514 | |
6410 | !acpi_ec_write(volume_offset, new_level + mute))) { | 6515 | return 0; |
6411 | return -EIO; | 6516 | } |
6412 | } | 6517 | |
6518 | static int volume_set_status(const u8 status) | ||
6519 | { | ||
6520 | return volume_set_status_ec(status); | ||
6521 | } | ||
6522 | |||
6523 | static int volume_set_mute_ec(const bool mute) | ||
6524 | { | ||
6525 | int rc; | ||
6526 | u8 s, n; | ||
6527 | |||
6528 | if (mutex_lock_killable(&volume_mutex) < 0) | ||
6529 | return -EINTR; | ||
6530 | |||
6531 | rc = volume_get_status_ec(&s); | ||
6532 | if (rc) | ||
6533 | goto unlock; | ||
6534 | |||
6535 | n = (mute) ? s | TP_EC_AUDIO_MUTESW_MSK : | ||
6536 | s & ~TP_EC_AUDIO_MUTESW_MSK; | ||
6537 | |||
6538 | if (n != s) | ||
6539 | rc = volume_set_status_ec(n); | ||
6540 | |||
6541 | unlock: | ||
6542 | mutex_unlock(&volume_mutex); | ||
6543 | return rc; | ||
6544 | } | ||
6545 | |||
6546 | static int volume_set_mute(const bool mute) | ||
6547 | { | ||
6548 | dbg_printk(TPACPI_DBG_MIXER, "trying to %smute\n", | ||
6549 | (mute) ? "" : "un"); | ||
6550 | return volume_set_mute_ec(mute); | ||
6551 | } | ||
6552 | |||
6553 | static int volume_set_volume_ec(const u8 vol) | ||
6554 | { | ||
6555 | int rc; | ||
6556 | u8 s, n; | ||
6557 | |||
6558 | if (vol > TP_EC_VOLUME_MAX) | ||
6559 | return -EINVAL; | ||
6560 | |||
6561 | if (mutex_lock_killable(&volume_mutex) < 0) | ||
6562 | return -EINTR; | ||
6563 | |||
6564 | rc = volume_get_status_ec(&s); | ||
6565 | if (rc) | ||
6566 | goto unlock; | ||
6567 | |||
6568 | n = (s & ~TP_EC_AUDIO_LVL_MSK) | vol; | ||
6569 | |||
6570 | if (n != s) | ||
6571 | rc = volume_set_status_ec(n); | ||
6572 | |||
6573 | unlock: | ||
6574 | mutex_unlock(&volume_mutex); | ||
6575 | return rc; | ||
6576 | } | ||
6577 | |||
6578 | static int volume_set_volume(const u8 vol) | ||
6579 | { | ||
6580 | dbg_printk(TPACPI_DBG_MIXER, | ||
6581 | "trying to set volume level to %hu\n", vol); | ||
6582 | return volume_set_volume_ec(vol); | ||
6583 | } | ||
6584 | |||
6585 | static void volume_alsa_notify_change(void) | ||
6586 | { | ||
6587 | struct tpacpi_alsa_data *d; | ||
6588 | |||
6589 | if (alsa_card && alsa_card->private_data) { | ||
6590 | d = alsa_card->private_data; | ||
6591 | if (d->ctl_mute_id) | ||
6592 | snd_ctl_notify(alsa_card, | ||
6593 | SNDRV_CTL_EVENT_MASK_VALUE, | ||
6594 | d->ctl_mute_id); | ||
6595 | if (d->ctl_vol_id) | ||
6596 | snd_ctl_notify(alsa_card, | ||
6597 | SNDRV_CTL_EVENT_MASK_VALUE, | ||
6598 | d->ctl_vol_id); | ||
6599 | } | ||
6600 | } | ||
6601 | |||
6602 | static int volume_alsa_vol_info(struct snd_kcontrol *kcontrol, | ||
6603 | struct snd_ctl_elem_info *uinfo) | ||
6604 | { | ||
6605 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
6606 | uinfo->count = 1; | ||
6607 | uinfo->value.integer.min = 0; | ||
6608 | uinfo->value.integer.max = TP_EC_VOLUME_MAX; | ||
6609 | return 0; | ||
6610 | } | ||
6611 | |||
6612 | static int volume_alsa_vol_get(struct snd_kcontrol *kcontrol, | ||
6613 | struct snd_ctl_elem_value *ucontrol) | ||
6614 | { | ||
6615 | u8 s; | ||
6616 | int rc; | ||
6617 | |||
6618 | rc = volume_get_status(&s); | ||
6619 | if (rc < 0) | ||
6620 | return rc; | ||
6621 | |||
6622 | ucontrol->value.integer.value[0] = s & TP_EC_AUDIO_LVL_MSK; | ||
6623 | return 0; | ||
6624 | } | ||
6625 | |||
6626 | static int volume_alsa_vol_put(struct snd_kcontrol *kcontrol, | ||
6627 | struct snd_ctl_elem_value *ucontrol) | ||
6628 | { | ||
6629 | return volume_set_volume(ucontrol->value.integer.value[0]); | ||
6630 | } | ||
6631 | |||
6632 | #define volume_alsa_mute_info snd_ctl_boolean_mono_info | ||
6633 | |||
6634 | static int volume_alsa_mute_get(struct snd_kcontrol *kcontrol, | ||
6635 | struct snd_ctl_elem_value *ucontrol) | ||
6636 | { | ||
6637 | u8 s; | ||
6638 | int rc; | ||
6639 | |||
6640 | rc = volume_get_status(&s); | ||
6641 | if (rc < 0) | ||
6642 | return rc; | ||
6643 | |||
6644 | ucontrol->value.integer.value[0] = | ||
6645 | (s & TP_EC_AUDIO_MUTESW_MSK) ? 0 : 1; | ||
6646 | return 0; | ||
6647 | } | ||
6648 | |||
6649 | static int volume_alsa_mute_put(struct snd_kcontrol *kcontrol, | ||
6650 | struct snd_ctl_elem_value *ucontrol) | ||
6651 | { | ||
6652 | return volume_set_mute(!ucontrol->value.integer.value[0]); | ||
6653 | } | ||
6654 | |||
6655 | static struct snd_kcontrol_new volume_alsa_control_vol __devinitdata = { | ||
6656 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
6657 | .name = "Console Playback Volume", | ||
6658 | .index = 0, | ||
6659 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
6660 | .info = volume_alsa_vol_info, | ||
6661 | .get = volume_alsa_vol_get, | ||
6662 | }; | ||
6663 | |||
6664 | static struct snd_kcontrol_new volume_alsa_control_mute __devinitdata = { | ||
6665 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
6666 | .name = "Console Playback Switch", | ||
6667 | .index = 0, | ||
6668 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
6669 | .info = volume_alsa_mute_info, | ||
6670 | .get = volume_alsa_mute_get, | ||
6671 | }; | ||
6672 | |||
6673 | static void volume_suspend(pm_message_t state) | ||
6674 | { | ||
6675 | tpacpi_volume_checkpoint_nvram(); | ||
6676 | } | ||
6677 | |||
6678 | static void volume_resume(void) | ||
6679 | { | ||
6680 | volume_alsa_notify_change(); | ||
6681 | } | ||
6682 | |||
6683 | static void volume_shutdown(void) | ||
6684 | { | ||
6685 | tpacpi_volume_checkpoint_nvram(); | ||
6686 | } | ||
6687 | |||
6688 | static void volume_exit(void) | ||
6689 | { | ||
6690 | if (alsa_card) { | ||
6691 | snd_card_free(alsa_card); | ||
6692 | alsa_card = NULL; | ||
6693 | } | ||
6694 | |||
6695 | tpacpi_volume_checkpoint_nvram(); | ||
6696 | } | ||
6697 | |||
6698 | static int __init volume_create_alsa_mixer(void) | ||
6699 | { | ||
6700 | struct snd_card *card; | ||
6701 | struct tpacpi_alsa_data *data; | ||
6702 | struct snd_kcontrol *ctl_vol; | ||
6703 | struct snd_kcontrol *ctl_mute; | ||
6704 | int rc; | ||
6705 | |||
6706 | rc = snd_card_create(alsa_index, alsa_id, THIS_MODULE, | ||
6707 | sizeof(struct tpacpi_alsa_data), &card); | ||
6708 | if (rc < 0) | ||
6709 | return rc; | ||
6710 | if (!card) | ||
6711 | return -ENOMEM; | ||
6712 | |||
6713 | BUG_ON(!card->private_data); | ||
6714 | data = card->private_data; | ||
6715 | data->card = card; | ||
6716 | |||
6717 | strlcpy(card->driver, TPACPI_ALSA_DRVNAME, | ||
6718 | sizeof(card->driver)); | ||
6719 | strlcpy(card->shortname, TPACPI_ALSA_SHRTNAME, | ||
6720 | sizeof(card->shortname)); | ||
6721 | snprintf(card->mixername, sizeof(card->mixername), "ThinkPad EC %s", | ||
6722 | (thinkpad_id.ec_version_str) ? | ||
6723 | thinkpad_id.ec_version_str : "(unknown)"); | ||
6724 | snprintf(card->longname, sizeof(card->longname), | ||
6725 | "%s at EC reg 0x%02x, fw %s", card->shortname, TP_EC_AUDIO, | ||
6726 | (thinkpad_id.ec_version_str) ? | ||
6727 | thinkpad_id.ec_version_str : "unknown"); | ||
6728 | |||
6729 | if (volume_control_allowed) { | ||
6730 | volume_alsa_control_vol.put = volume_alsa_vol_put; | ||
6731 | volume_alsa_control_vol.access = | ||
6732 | SNDRV_CTL_ELEM_ACCESS_READWRITE; | ||
6733 | |||
6734 | volume_alsa_control_mute.put = volume_alsa_mute_put; | ||
6735 | volume_alsa_control_mute.access = | ||
6736 | SNDRV_CTL_ELEM_ACCESS_READWRITE; | ||
6737 | } | ||
6738 | |||
6739 | if (!tp_features.mixer_no_level_control) { | ||
6740 | ctl_vol = snd_ctl_new1(&volume_alsa_control_vol, NULL); | ||
6741 | rc = snd_ctl_add(card, ctl_vol); | ||
6742 | if (rc < 0) { | ||
6743 | printk(TPACPI_ERR | ||
6744 | "Failed to create ALSA volume control\n"); | ||
6745 | goto err_out; | ||
6413 | } | 6746 | } |
6747 | data->ctl_vol_id = &ctl_vol->id; | ||
6748 | } | ||
6414 | 6749 | ||
6415 | if (new_mute != mute) { | 6750 | ctl_mute = snd_ctl_new1(&volume_alsa_control_mute, NULL); |
6416 | /* level doesn't change */ | 6751 | rc = snd_ctl_add(card, ctl_mute); |
6752 | if (rc < 0) { | ||
6753 | printk(TPACPI_ERR "Failed to create ALSA mute control\n"); | ||
6754 | goto err_out; | ||
6755 | } | ||
6756 | data->ctl_mute_id = &ctl_mute->id; | ||
6417 | 6757 | ||
6418 | cmos_cmd = (new_mute) ? | 6758 | snd_card_set_dev(card, &tpacpi_pdev->dev); |
6419 | TP_CMOS_VOLUME_MUTE : TP_CMOS_VOLUME_UP; | 6759 | rc = snd_card_register(card); |
6420 | 6760 | ||
6421 | if (issue_thinkpad_cmos_command(cmos_cmd) || | 6761 | err_out: |
6422 | !acpi_ec_write(volume_offset, level + new_mute)) | 6762 | if (rc < 0) { |
6423 | return -EIO; | 6763 | snd_card_free(card); |
6764 | card = NULL; | ||
6765 | } | ||
6766 | |||
6767 | alsa_card = card; | ||
6768 | return rc; | ||
6769 | } | ||
6770 | |||
6771 | #define TPACPI_VOL_Q_MUTEONLY 0x0001 /* Mute-only control available */ | ||
6772 | #define TPACPI_VOL_Q_LEVEL 0x0002 /* Volume control available */ | ||
6773 | |||
6774 | static const struct tpacpi_quirk volume_quirk_table[] __initconst = { | ||
6775 | /* Whitelist volume level on all IBM by default */ | ||
6776 | { .vendor = PCI_VENDOR_ID_IBM, | ||
6777 | .bios = TPACPI_MATCH_ANY, | ||
6778 | .ec = TPACPI_MATCH_ANY, | ||
6779 | .quirks = TPACPI_VOL_Q_LEVEL }, | ||
6780 | |||
6781 | /* Lenovo models with volume control (needs confirmation) */ | ||
6782 | TPACPI_QEC_LNV('7', 'C', TPACPI_VOL_Q_LEVEL), /* R60/i */ | ||
6783 | TPACPI_QEC_LNV('7', 'E', TPACPI_VOL_Q_LEVEL), /* R60e/i */ | ||
6784 | TPACPI_QEC_LNV('7', '9', TPACPI_VOL_Q_LEVEL), /* T60/p */ | ||
6785 | TPACPI_QEC_LNV('7', 'B', TPACPI_VOL_Q_LEVEL), /* X60/s */ | ||
6786 | TPACPI_QEC_LNV('7', 'J', TPACPI_VOL_Q_LEVEL), /* X60t */ | ||
6787 | TPACPI_QEC_LNV('7', '7', TPACPI_VOL_Q_LEVEL), /* Z60 */ | ||
6788 | TPACPI_QEC_LNV('7', 'F', TPACPI_VOL_Q_LEVEL), /* Z61 */ | ||
6789 | |||
6790 | /* Whitelist mute-only on all Lenovo by default */ | ||
6791 | { .vendor = PCI_VENDOR_ID_LENOVO, | ||
6792 | .bios = TPACPI_MATCH_ANY, | ||
6793 | .ec = TPACPI_MATCH_ANY, | ||
6794 | .quirks = TPACPI_VOL_Q_MUTEONLY } | ||
6795 | }; | ||
6796 | |||
6797 | static int __init volume_init(struct ibm_init_struct *iibm) | ||
6798 | { | ||
6799 | unsigned long quirks; | ||
6800 | int rc; | ||
6801 | |||
6802 | vdbg_printk(TPACPI_DBG_INIT, "initializing volume subdriver\n"); | ||
6803 | |||
6804 | mutex_init(&volume_mutex); | ||
6805 | |||
6806 | /* | ||
6807 | * Check for module parameter bogosity, note that we | ||
6808 | * init volume_mode to TPACPI_VOL_MODE_MAX in order to be | ||
6809 | * able to detect "unspecified" | ||
6810 | */ | ||
6811 | if (volume_mode > TPACPI_VOL_MODE_MAX) | ||
6812 | return -EINVAL; | ||
6813 | |||
6814 | if (volume_mode == TPACPI_VOL_MODE_UCMS_STEP) { | ||
6815 | printk(TPACPI_ERR | ||
6816 | "UCMS step volume mode not implemented, " | ||
6817 | "please contact %s\n", TPACPI_MAIL); | ||
6818 | return 1; | ||
6819 | } | ||
6820 | |||
6821 | if (volume_capabilities >= TPACPI_VOL_CAP_MAX) | ||
6822 | return -EINVAL; | ||
6823 | |||
6824 | /* | ||
6825 | * The ALSA mixer is our primary interface. | ||
6826 | * When disabled, don't install the subdriver at all | ||
6827 | */ | ||
6828 | if (!alsa_enable) { | ||
6829 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, | ||
6830 | "ALSA mixer disabled by parameter, " | ||
6831 | "not loading volume subdriver...\n"); | ||
6832 | return 1; | ||
6833 | } | ||
6834 | |||
6835 | quirks = tpacpi_check_quirks(volume_quirk_table, | ||
6836 | ARRAY_SIZE(volume_quirk_table)); | ||
6837 | |||
6838 | switch (volume_capabilities) { | ||
6839 | case TPACPI_VOL_CAP_AUTO: | ||
6840 | if (quirks & TPACPI_VOL_Q_MUTEONLY) | ||
6841 | tp_features.mixer_no_level_control = 1; | ||
6842 | else if (quirks & TPACPI_VOL_Q_LEVEL) | ||
6843 | tp_features.mixer_no_level_control = 0; | ||
6844 | else | ||
6845 | return 1; /* no mixer */ | ||
6846 | break; | ||
6847 | case TPACPI_VOL_CAP_VOLMUTE: | ||
6848 | tp_features.mixer_no_level_control = 0; | ||
6849 | break; | ||
6850 | case TPACPI_VOL_CAP_MUTEONLY: | ||
6851 | tp_features.mixer_no_level_control = 1; | ||
6852 | break; | ||
6853 | default: | ||
6854 | return 1; | ||
6855 | } | ||
6856 | |||
6857 | if (volume_capabilities != TPACPI_VOL_CAP_AUTO) | ||
6858 | dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, | ||
6859 | "using user-supplied volume_capabilities=%d\n", | ||
6860 | volume_capabilities); | ||
6861 | |||
6862 | if (volume_mode == TPACPI_VOL_MODE_AUTO || | ||
6863 | volume_mode == TPACPI_VOL_MODE_MAX) { | ||
6864 | volume_mode = TPACPI_VOL_MODE_ECNVRAM; | ||
6865 | |||
6866 | dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, | ||
6867 | "driver auto-selected volume_mode=%d\n", | ||
6868 | volume_mode); | ||
6869 | } else { | ||
6870 | dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, | ||
6871 | "using user-supplied volume_mode=%d\n", | ||
6872 | volume_mode); | ||
6873 | } | ||
6874 | |||
6875 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, | ||
6876 | "mute is supported, volume control is %s\n", | ||
6877 | str_supported(!tp_features.mixer_no_level_control)); | ||
6878 | |||
6879 | rc = volume_create_alsa_mixer(); | ||
6880 | if (rc) { | ||
6881 | printk(TPACPI_ERR | ||
6882 | "Could not create the ALSA mixer interface\n"); | ||
6883 | return rc; | ||
6884 | } | ||
6885 | |||
6886 | printk(TPACPI_INFO | ||
6887 | "Console audio control enabled, mode: %s\n", | ||
6888 | (volume_control_allowed) ? | ||
6889 | "override (read/write)" : | ||
6890 | "monitor (read only)"); | ||
6891 | |||
6892 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, | ||
6893 | "registering volume hotkeys as change notification\n"); | ||
6894 | tpacpi_hotkey_driver_mask_set(hotkey_driver_mask | ||
6895 | | TP_ACPI_HKEY_VOLUP_MASK | ||
6896 | | TP_ACPI_HKEY_VOLDWN_MASK | ||
6897 | | TP_ACPI_HKEY_MUTE_MASK); | ||
6898 | |||
6899 | return 0; | ||
6900 | } | ||
6901 | |||
6902 | static int volume_read(struct seq_file *m) | ||
6903 | { | ||
6904 | u8 status; | ||
6905 | |||
6906 | if (volume_get_status(&status) < 0) { | ||
6907 | seq_printf(m, "level:\t\tunreadable\n"); | ||
6908 | } else { | ||
6909 | if (tp_features.mixer_no_level_control) | ||
6910 | seq_printf(m, "level:\t\tunsupported\n"); | ||
6911 | else | ||
6912 | seq_printf(m, "level:\t\t%d\n", | ||
6913 | status & TP_EC_AUDIO_LVL_MSK); | ||
6914 | |||
6915 | seq_printf(m, "mute:\t\t%s\n", | ||
6916 | onoff(status, TP_EC_AUDIO_MUTESW)); | ||
6917 | |||
6918 | if (volume_control_allowed) { | ||
6919 | seq_printf(m, "commands:\tunmute, mute\n"); | ||
6920 | if (!tp_features.mixer_no_level_control) { | ||
6921 | seq_printf(m, | ||
6922 | "commands:\tup, down\n"); | ||
6923 | seq_printf(m, | ||
6924 | "commands:\tlevel <level>" | ||
6925 | " (<level> is 0-%d)\n", | ||
6926 | TP_EC_VOLUME_MAX); | ||
6927 | } | ||
6424 | } | 6928 | } |
6425 | } | 6929 | } |
6426 | 6930 | ||
6427 | return 0; | 6931 | return 0; |
6428 | } | 6932 | } |
6429 | 6933 | ||
6934 | static int volume_write(char *buf) | ||
6935 | { | ||
6936 | u8 s; | ||
6937 | u8 new_level, new_mute; | ||
6938 | int l; | ||
6939 | char *cmd; | ||
6940 | int rc; | ||
6941 | |||
6942 | /* | ||
6943 | * We do allow volume control at driver startup, so that the | ||
6944 | * user can set initial state through the volume=... parameter hack. | ||
6945 | */ | ||
6946 | if (!volume_control_allowed && tpacpi_lifecycle != TPACPI_LIFE_INIT) { | ||
6947 | if (unlikely(!tp_warned.volume_ctrl_forbidden)) { | ||
6948 | tp_warned.volume_ctrl_forbidden = 1; | ||
6949 | printk(TPACPI_NOTICE | ||
6950 | "Console audio control in monitor mode, " | ||
6951 | "changes are not allowed.\n"); | ||
6952 | printk(TPACPI_NOTICE | ||
6953 | "Use the volume_control=1 module parameter " | ||
6954 | "to enable volume control\n"); | ||
6955 | } | ||
6956 | return -EPERM; | ||
6957 | } | ||
6958 | |||
6959 | rc = volume_get_status(&s); | ||
6960 | if (rc < 0) | ||
6961 | return rc; | ||
6962 | |||
6963 | new_level = s & TP_EC_AUDIO_LVL_MSK; | ||
6964 | new_mute = s & TP_EC_AUDIO_MUTESW_MSK; | ||
6965 | |||
6966 | while ((cmd = next_cmd(&buf))) { | ||
6967 | if (!tp_features.mixer_no_level_control) { | ||
6968 | if (strlencmp(cmd, "up") == 0) { | ||
6969 | if (new_mute) | ||
6970 | new_mute = 0; | ||
6971 | else if (new_level < TP_EC_VOLUME_MAX) | ||
6972 | new_level++; | ||
6973 | continue; | ||
6974 | } else if (strlencmp(cmd, "down") == 0) { | ||
6975 | if (new_mute) | ||
6976 | new_mute = 0; | ||
6977 | else if (new_level > 0) | ||
6978 | new_level--; | ||
6979 | continue; | ||
6980 | } else if (sscanf(cmd, "level %u", &l) == 1 && | ||
6981 | l >= 0 && l <= TP_EC_VOLUME_MAX) { | ||
6982 | new_level = l; | ||
6983 | continue; | ||
6984 | } | ||
6985 | } | ||
6986 | if (strlencmp(cmd, "mute") == 0) | ||
6987 | new_mute = TP_EC_AUDIO_MUTESW_MSK; | ||
6988 | else if (strlencmp(cmd, "unmute") == 0) | ||
6989 | new_mute = 0; | ||
6990 | else | ||
6991 | return -EINVAL; | ||
6992 | } | ||
6993 | |||
6994 | if (tp_features.mixer_no_level_control) { | ||
6995 | tpacpi_disclose_usertask("procfs volume", "%smute\n", | ||
6996 | new_mute ? "" : "un"); | ||
6997 | rc = volume_set_mute(!!new_mute); | ||
6998 | } else { | ||
6999 | tpacpi_disclose_usertask("procfs volume", | ||
7000 | "%smute and set level to %d\n", | ||
7001 | new_mute ? "" : "un", new_level); | ||
7002 | rc = volume_set_status(new_mute | new_level); | ||
7003 | } | ||
7004 | volume_alsa_notify_change(); | ||
7005 | |||
7006 | return (rc == -EINTR) ? -ERESTARTSYS : rc; | ||
7007 | } | ||
7008 | |||
6430 | static struct ibm_struct volume_driver_data = { | 7009 | static struct ibm_struct volume_driver_data = { |
6431 | .name = "volume", | 7010 | .name = "volume", |
6432 | .read = volume_read, | 7011 | .read = volume_read, |
6433 | .write = volume_write, | 7012 | .write = volume_write, |
7013 | .exit = volume_exit, | ||
7014 | .suspend = volume_suspend, | ||
7015 | .resume = volume_resume, | ||
7016 | .shutdown = volume_shutdown, | ||
6434 | }; | 7017 | }; |
6435 | 7018 | ||
6436 | /************************************************************************* | 7019 | /************************************************************************* |
@@ -7507,9 +8090,8 @@ static void fan_resume(void) | |||
7507 | } | 8090 | } |
7508 | } | 8091 | } |
7509 | 8092 | ||
7510 | static int fan_read(char *p) | 8093 | static int fan_read(struct seq_file *m) |
7511 | { | 8094 | { |
7512 | int len = 0; | ||
7513 | int rc; | 8095 | int rc; |
7514 | u8 status; | 8096 | u8 status; |
7515 | unsigned int speed = 0; | 8097 | unsigned int speed = 0; |
@@ -7521,7 +8103,7 @@ static int fan_read(char *p) | |||
7521 | if (rc < 0) | 8103 | if (rc < 0) |
7522 | return rc; | 8104 | return rc; |
7523 | 8105 | ||
7524 | len += sprintf(p + len, "status:\t\t%s\n" | 8106 | seq_printf(m, "status:\t\t%s\n" |
7525 | "level:\t\t%d\n", | 8107 | "level:\t\t%d\n", |
7526 | (status != 0) ? "enabled" : "disabled", status); | 8108 | (status != 0) ? "enabled" : "disabled", status); |
7527 | break; | 8109 | break; |
@@ -7532,54 +8114,54 @@ static int fan_read(char *p) | |||
7532 | if (rc < 0) | 8114 | if (rc < 0) |
7533 | return rc; | 8115 | return rc; |
7534 | 8116 | ||
7535 | len += sprintf(p + len, "status:\t\t%s\n", | 8117 | seq_printf(m, "status:\t\t%s\n", |
7536 | (status != 0) ? "enabled" : "disabled"); | 8118 | (status != 0) ? "enabled" : "disabled"); |
7537 | 8119 | ||
7538 | rc = fan_get_speed(&speed); | 8120 | rc = fan_get_speed(&speed); |
7539 | if (rc < 0) | 8121 | if (rc < 0) |
7540 | return rc; | 8122 | return rc; |
7541 | 8123 | ||
7542 | len += sprintf(p + len, "speed:\t\t%d\n", speed); | 8124 | seq_printf(m, "speed:\t\t%d\n", speed); |
7543 | 8125 | ||
7544 | if (status & TP_EC_FAN_FULLSPEED) | 8126 | if (status & TP_EC_FAN_FULLSPEED) |
7545 | /* Disengaged mode takes precedence */ | 8127 | /* Disengaged mode takes precedence */ |
7546 | len += sprintf(p + len, "level:\t\tdisengaged\n"); | 8128 | seq_printf(m, "level:\t\tdisengaged\n"); |
7547 | else if (status & TP_EC_FAN_AUTO) | 8129 | else if (status & TP_EC_FAN_AUTO) |
7548 | len += sprintf(p + len, "level:\t\tauto\n"); | 8130 | seq_printf(m, "level:\t\tauto\n"); |
7549 | else | 8131 | else |
7550 | len += sprintf(p + len, "level:\t\t%d\n", status); | 8132 | seq_printf(m, "level:\t\t%d\n", status); |
7551 | break; | 8133 | break; |
7552 | 8134 | ||
7553 | case TPACPI_FAN_NONE: | 8135 | case TPACPI_FAN_NONE: |
7554 | default: | 8136 | default: |
7555 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 8137 | seq_printf(m, "status:\t\tnot supported\n"); |
7556 | } | 8138 | } |
7557 | 8139 | ||
7558 | if (fan_control_commands & TPACPI_FAN_CMD_LEVEL) { | 8140 | if (fan_control_commands & TPACPI_FAN_CMD_LEVEL) { |
7559 | len += sprintf(p + len, "commands:\tlevel <level>"); | 8141 | seq_printf(m, "commands:\tlevel <level>"); |
7560 | 8142 | ||
7561 | switch (fan_control_access_mode) { | 8143 | switch (fan_control_access_mode) { |
7562 | case TPACPI_FAN_WR_ACPI_SFAN: | 8144 | case TPACPI_FAN_WR_ACPI_SFAN: |
7563 | len += sprintf(p + len, " (<level> is 0-7)\n"); | 8145 | seq_printf(m, " (<level> is 0-7)\n"); |
7564 | break; | 8146 | break; |
7565 | 8147 | ||
7566 | default: | 8148 | default: |
7567 | len += sprintf(p + len, " (<level> is 0-7, " | 8149 | seq_printf(m, " (<level> is 0-7, " |
7568 | "auto, disengaged, full-speed)\n"); | 8150 | "auto, disengaged, full-speed)\n"); |
7569 | break; | 8151 | break; |
7570 | } | 8152 | } |
7571 | } | 8153 | } |
7572 | 8154 | ||
7573 | if (fan_control_commands & TPACPI_FAN_CMD_ENABLE) | 8155 | if (fan_control_commands & TPACPI_FAN_CMD_ENABLE) |
7574 | len += sprintf(p + len, "commands:\tenable, disable\n" | 8156 | seq_printf(m, "commands:\tenable, disable\n" |
7575 | "commands:\twatchdog <timeout> (<timeout> " | 8157 | "commands:\twatchdog <timeout> (<timeout> " |
7576 | "is 0 (off), 1-120 (seconds))\n"); | 8158 | "is 0 (off), 1-120 (seconds))\n"); |
7577 | 8159 | ||
7578 | if (fan_control_commands & TPACPI_FAN_CMD_SPEED) | 8160 | if (fan_control_commands & TPACPI_FAN_CMD_SPEED) |
7579 | len += sprintf(p + len, "commands:\tspeed <speed>" | 8161 | seq_printf(m, "commands:\tspeed <speed>" |
7580 | " (<speed> is 0-65535)\n"); | 8162 | " (<speed> is 0-65535)\n"); |
7581 | 8163 | ||
7582 | return len; | 8164 | return 0; |
7583 | } | 8165 | } |
7584 | 8166 | ||
7585 | static int fan_write_cmd_level(const char *cmd, int *rc) | 8167 | static int fan_write_cmd_level(const char *cmd, int *rc) |
@@ -7721,10 +8303,23 @@ static struct ibm_struct fan_driver_data = { | |||
7721 | */ | 8303 | */ |
7722 | static void tpacpi_driver_event(const unsigned int hkey_event) | 8304 | static void tpacpi_driver_event(const unsigned int hkey_event) |
7723 | { | 8305 | { |
8306 | if (ibm_backlight_device) { | ||
8307 | switch (hkey_event) { | ||
8308 | case TP_HKEY_EV_BRGHT_UP: | ||
8309 | case TP_HKEY_EV_BRGHT_DOWN: | ||
8310 | tpacpi_brightness_notify_change(); | ||
8311 | } | ||
8312 | } | ||
8313 | if (alsa_card) { | ||
8314 | switch (hkey_event) { | ||
8315 | case TP_HKEY_EV_VOL_UP: | ||
8316 | case TP_HKEY_EV_VOL_DOWN: | ||
8317 | case TP_HKEY_EV_VOL_MUTE: | ||
8318 | volume_alsa_notify_change(); | ||
8319 | } | ||
8320 | } | ||
7724 | } | 8321 | } |
7725 | 8322 | ||
7726 | |||
7727 | |||
7728 | static void hotkey_driver_event(const unsigned int scancode) | 8323 | static void hotkey_driver_event(const unsigned int scancode) |
7729 | { | 8324 | { |
7730 | tpacpi_driver_event(TP_HKEY_EV_HOTKEY_BASE + scancode); | 8325 | tpacpi_driver_event(TP_HKEY_EV_HOTKEY_BASE + scancode); |
@@ -7853,19 +8448,19 @@ static int __init ibm_init(struct ibm_init_struct *iibm) | |||
7853 | "%s installed\n", ibm->name); | 8448 | "%s installed\n", ibm->name); |
7854 | 8449 | ||
7855 | if (ibm->read) { | 8450 | if (ibm->read) { |
7856 | entry = create_proc_entry(ibm->name, | 8451 | mode_t mode; |
7857 | S_IFREG | S_IRUGO | S_IWUSR, | 8452 | |
7858 | proc_dir); | 8453 | mode = S_IRUGO; |
8454 | if (ibm->write) | ||
8455 | mode |= S_IWUSR; | ||
8456 | entry = proc_create_data(ibm->name, mode, proc_dir, | ||
8457 | &dispatch_proc_fops, ibm); | ||
7859 | if (!entry) { | 8458 | if (!entry) { |
7860 | printk(TPACPI_ERR "unable to create proc entry %s\n", | 8459 | printk(TPACPI_ERR "unable to create proc entry %s\n", |
7861 | ibm->name); | 8460 | ibm->name); |
7862 | ret = -ENODEV; | 8461 | ret = -ENODEV; |
7863 | goto err_out; | 8462 | goto err_out; |
7864 | } | 8463 | } |
7865 | entry->data = ibm; | ||
7866 | entry->read_proc = &dispatch_procfs_read; | ||
7867 | if (ibm->write) | ||
7868 | entry->write_proc = &dispatch_procfs_write; | ||
7869 | ibm->flags.proc_created = 1; | 8464 | ibm->flags.proc_created = 1; |
7870 | } | 8465 | } |
7871 | 8466 | ||
@@ -8077,6 +8672,7 @@ static struct ibm_init_struct ibms_init[] __initdata = { | |||
8077 | .data = &brightness_driver_data, | 8672 | .data = &brightness_driver_data, |
8078 | }, | 8673 | }, |
8079 | { | 8674 | { |
8675 | .init = volume_init, | ||
8080 | .data = &volume_driver_data, | 8676 | .data = &volume_driver_data, |
8081 | }, | 8677 | }, |
8082 | { | 8678 | { |
@@ -8112,36 +8708,59 @@ static int __init set_ibm_param(const char *val, struct kernel_param *kp) | |||
8112 | return -EINVAL; | 8708 | return -EINVAL; |
8113 | } | 8709 | } |
8114 | 8710 | ||
8115 | module_param(experimental, int, 0); | 8711 | module_param(experimental, int, 0444); |
8116 | MODULE_PARM_DESC(experimental, | 8712 | MODULE_PARM_DESC(experimental, |
8117 | "Enables experimental features when non-zero"); | 8713 | "Enables experimental features when non-zero"); |
8118 | 8714 | ||
8119 | module_param_named(debug, dbg_level, uint, 0); | 8715 | module_param_named(debug, dbg_level, uint, 0); |
8120 | MODULE_PARM_DESC(debug, "Sets debug level bit-mask"); | 8716 | MODULE_PARM_DESC(debug, "Sets debug level bit-mask"); |
8121 | 8717 | ||
8122 | module_param(force_load, bool, 0); | 8718 | module_param(force_load, bool, 0444); |
8123 | MODULE_PARM_DESC(force_load, | 8719 | MODULE_PARM_DESC(force_load, |
8124 | "Attempts to load the driver even on a " | 8720 | "Attempts to load the driver even on a " |
8125 | "mis-identified ThinkPad when true"); | 8721 | "mis-identified ThinkPad when true"); |
8126 | 8722 | ||
8127 | module_param_named(fan_control, fan_control_allowed, bool, 0); | 8723 | module_param_named(fan_control, fan_control_allowed, bool, 0444); |
8128 | MODULE_PARM_DESC(fan_control, | 8724 | MODULE_PARM_DESC(fan_control, |
8129 | "Enables setting fan parameters features when true"); | 8725 | "Enables setting fan parameters features when true"); |
8130 | 8726 | ||
8131 | module_param_named(brightness_mode, brightness_mode, uint, 0); | 8727 | module_param_named(brightness_mode, brightness_mode, uint, 0444); |
8132 | MODULE_PARM_DESC(brightness_mode, | 8728 | MODULE_PARM_DESC(brightness_mode, |
8133 | "Selects brightness control strategy: " | 8729 | "Selects brightness control strategy: " |
8134 | "0=auto, 1=EC, 2=UCMS, 3=EC+NVRAM"); | 8730 | "0=auto, 1=EC, 2=UCMS, 3=EC+NVRAM"); |
8135 | 8731 | ||
8136 | module_param(brightness_enable, uint, 0); | 8732 | module_param(brightness_enable, uint, 0444); |
8137 | MODULE_PARM_DESC(brightness_enable, | 8733 | MODULE_PARM_DESC(brightness_enable, |
8138 | "Enables backlight control when 1, disables when 0"); | 8734 | "Enables backlight control when 1, disables when 0"); |
8139 | 8735 | ||
8140 | module_param(hotkey_report_mode, uint, 0); | 8736 | module_param(hotkey_report_mode, uint, 0444); |
8141 | MODULE_PARM_DESC(hotkey_report_mode, | 8737 | MODULE_PARM_DESC(hotkey_report_mode, |
8142 | "used for backwards compatibility with userspace, " | 8738 | "used for backwards compatibility with userspace, " |
8143 | "see documentation"); | 8739 | "see documentation"); |
8144 | 8740 | ||
8741 | module_param_named(volume_mode, volume_mode, uint, 0444); | ||
8742 | MODULE_PARM_DESC(volume_mode, | ||
8743 | "Selects volume control strategy: " | ||
8744 | "0=auto, 1=EC, 2=N/A, 3=EC+NVRAM"); | ||
8745 | |||
8746 | module_param_named(volume_capabilities, volume_capabilities, uint, 0444); | ||
8747 | MODULE_PARM_DESC(volume_capabilities, | ||
8748 | "Selects the mixer capabilites: " | ||
8749 | "0=auto, 1=volume and mute, 2=mute only"); | ||
8750 | |||
8751 | module_param_named(volume_control, volume_control_allowed, bool, 0444); | ||
8752 | MODULE_PARM_DESC(volume_control, | ||
8753 | "Enables software override for the console audio " | ||
8754 | "control when true"); | ||
8755 | |||
8756 | /* ALSA module API parameters */ | ||
8757 | module_param_named(index, alsa_index, int, 0444); | ||
8758 | MODULE_PARM_DESC(index, "ALSA index for the ACPI EC Mixer"); | ||
8759 | module_param_named(id, alsa_id, charp, 0444); | ||
8760 | MODULE_PARM_DESC(id, "ALSA id for the ACPI EC Mixer"); | ||
8761 | module_param_named(enable, alsa_enable, bool, 0444); | ||
8762 | MODULE_PARM_DESC(enable, "Enable the ALSA interface for the ACPI EC Mixer"); | ||
8763 | |||
8145 | #define TPACPI_PARAM(feature) \ | 8764 | #define TPACPI_PARAM(feature) \ |
8146 | module_param_call(feature, set_ibm_param, NULL, NULL, 0); \ | 8765 | module_param_call(feature, set_ibm_param, NULL, NULL, 0); \ |
8147 | MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \ | 8766 | MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \ |
@@ -8160,25 +8779,25 @@ TPACPI_PARAM(volume); | |||
8160 | TPACPI_PARAM(fan); | 8779 | TPACPI_PARAM(fan); |
8161 | 8780 | ||
8162 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 8781 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
8163 | module_param(dbg_wlswemul, uint, 0); | 8782 | module_param(dbg_wlswemul, uint, 0444); |
8164 | MODULE_PARM_DESC(dbg_wlswemul, "Enables WLSW emulation"); | 8783 | MODULE_PARM_DESC(dbg_wlswemul, "Enables WLSW emulation"); |
8165 | module_param_named(wlsw_state, tpacpi_wlsw_emulstate, bool, 0); | 8784 | module_param_named(wlsw_state, tpacpi_wlsw_emulstate, bool, 0); |
8166 | MODULE_PARM_DESC(wlsw_state, | 8785 | MODULE_PARM_DESC(wlsw_state, |
8167 | "Initial state of the emulated WLSW switch"); | 8786 | "Initial state of the emulated WLSW switch"); |
8168 | 8787 | ||
8169 | module_param(dbg_bluetoothemul, uint, 0); | 8788 | module_param(dbg_bluetoothemul, uint, 0444); |
8170 | MODULE_PARM_DESC(dbg_bluetoothemul, "Enables bluetooth switch emulation"); | 8789 | MODULE_PARM_DESC(dbg_bluetoothemul, "Enables bluetooth switch emulation"); |
8171 | module_param_named(bluetooth_state, tpacpi_bluetooth_emulstate, bool, 0); | 8790 | module_param_named(bluetooth_state, tpacpi_bluetooth_emulstate, bool, 0); |
8172 | MODULE_PARM_DESC(bluetooth_state, | 8791 | MODULE_PARM_DESC(bluetooth_state, |
8173 | "Initial state of the emulated bluetooth switch"); | 8792 | "Initial state of the emulated bluetooth switch"); |
8174 | 8793 | ||
8175 | module_param(dbg_wwanemul, uint, 0); | 8794 | module_param(dbg_wwanemul, uint, 0444); |
8176 | MODULE_PARM_DESC(dbg_wwanemul, "Enables WWAN switch emulation"); | 8795 | MODULE_PARM_DESC(dbg_wwanemul, "Enables WWAN switch emulation"); |
8177 | module_param_named(wwan_state, tpacpi_wwan_emulstate, bool, 0); | 8796 | module_param_named(wwan_state, tpacpi_wwan_emulstate, bool, 0); |
8178 | MODULE_PARM_DESC(wwan_state, | 8797 | MODULE_PARM_DESC(wwan_state, |
8179 | "Initial state of the emulated WWAN switch"); | 8798 | "Initial state of the emulated WWAN switch"); |
8180 | 8799 | ||
8181 | module_param(dbg_uwbemul, uint, 0); | 8800 | module_param(dbg_uwbemul, uint, 0444); |
8182 | MODULE_PARM_DESC(dbg_uwbemul, "Enables UWB switch emulation"); | 8801 | MODULE_PARM_DESC(dbg_uwbemul, "Enables UWB switch emulation"); |
8183 | module_param_named(uwb_state, tpacpi_uwb_emulstate, bool, 0); | 8802 | module_param_named(uwb_state, tpacpi_uwb_emulstate, bool, 0); |
8184 | MODULE_PARM_DESC(uwb_state, | 8803 | MODULE_PARM_DESC(uwb_state, |
@@ -8371,6 +8990,7 @@ static int __init thinkpad_acpi_module_init(void) | |||
8371 | PCI_VENDOR_ID_IBM; | 8990 | PCI_VENDOR_ID_IBM; |
8372 | tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT; | 8991 | tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT; |
8373 | tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION; | 8992 | tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION; |
8993 | tpacpi_inputdev->dev.parent = &tpacpi_pdev->dev; | ||
8374 | } | 8994 | } |
8375 | for (i = 0; i < ARRAY_SIZE(ibms_init); i++) { | 8995 | for (i = 0; i < ARRAY_SIZE(ibms_init); i++) { |
8376 | ret = ibm_init(&ibms_init[i]); | 8996 | ret = ibm_init(&ibms_init[i]); |