.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * thinkpad_acpi.c - ThinkPad ACPI Extras |
---|
3 | 4 | * |
---|
4 | | - * |
---|
5 | 5 | * Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net> |
---|
6 | 6 | * Copyright (C) 2006-2009 Henrique de Moraes Holschuh <hmh@hmh.eng.br> |
---|
7 | | - * |
---|
8 | | - * This program is free software; you can redistribute it and/or modify |
---|
9 | | - * it under the terms of the GNU General Public License as published by |
---|
10 | | - * the Free Software Foundation; either version 2 of the License, or |
---|
11 | | - * (at your option) any later version. |
---|
12 | | - * |
---|
13 | | - * This program is distributed in the hope that it will be useful, |
---|
14 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
15 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
16 | | - * GNU General Public License for more details. |
---|
17 | | - * |
---|
18 | | - * You should have received a copy of the GNU General Public License |
---|
19 | | - * along with this program; if not, write to the Free Software |
---|
20 | | - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
---|
21 | | - * 02110-1301, USA. |
---|
22 | 7 | */ |
---|
23 | 8 | |
---|
24 | 9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
---|
.. | .. |
---|
81 | 66 | #include <linux/acpi.h> |
---|
82 | 67 | #include <linux/pci.h> |
---|
83 | 68 | #include <linux/power_supply.h> |
---|
84 | | -#include <linux/thinkpad_acpi.h> |
---|
85 | 69 | #include <sound/core.h> |
---|
86 | 70 | #include <sound/control.h> |
---|
87 | 71 | #include <sound/initval.h> |
---|
.. | .. |
---|
334 | 318 | u32 uwb:1; |
---|
335 | 319 | u32 fan_ctrl_status_undef:1; |
---|
336 | 320 | u32 second_fan:1; |
---|
| 321 | + u32 second_fan_ctl:1; |
---|
337 | 322 | u32 beep_needs_two_args:1; |
---|
338 | 323 | u32 mixer_no_level_control:1; |
---|
339 | 324 | u32 battery_force_primary:1; |
---|
.. | .. |
---|
476 | 461 | { .vendor = PCI_VENDOR_ID_LENOVO, \ |
---|
477 | 462 | .bios = TPID3(__id1, __id2, __id3), \ |
---|
478 | 463 | .ec = TPACPI_MATCH_ANY, \ |
---|
| 464 | + .quirks = (__quirk) } |
---|
| 465 | + |
---|
| 466 | +#define TPACPI_QEC_IBM(__id1, __id2, __quirk) \ |
---|
| 467 | + { .vendor = PCI_VENDOR_ID_IBM, \ |
---|
| 468 | + .bios = TPACPI_MATCH_ANY, \ |
---|
| 469 | + .ec = TPID(__id1, __id2), \ |
---|
479 | 470 | .quirks = (__quirk) } |
---|
480 | 471 | |
---|
481 | 472 | #define TPACPI_QEC_LNV(__id1, __id2, __quirk) \ |
---|
.. | .. |
---|
894 | 885 | |
---|
895 | 886 | if (!ibm || !ibm->write) |
---|
896 | 887 | return -EINVAL; |
---|
897 | | - if (count > PAGE_SIZE - 2) |
---|
| 888 | + if (count > PAGE_SIZE - 1) |
---|
898 | 889 | return -EINVAL; |
---|
899 | 890 | |
---|
900 | | - kernbuf = kmalloc(count + 2, GFP_KERNEL); |
---|
| 891 | + kernbuf = kmalloc(count + 1, GFP_KERNEL); |
---|
901 | 892 | if (!kernbuf) |
---|
902 | 893 | return -ENOMEM; |
---|
903 | 894 | |
---|
.. | .. |
---|
907 | 898 | } |
---|
908 | 899 | |
---|
909 | 900 | kernbuf[count] = 0; |
---|
910 | | - strcat(kernbuf, ","); |
---|
911 | 901 | ret = ibm->write(kernbuf); |
---|
912 | 902 | if (ret == 0) |
---|
913 | 903 | ret = count; |
---|
.. | .. |
---|
917 | 907 | return ret; |
---|
918 | 908 | } |
---|
919 | 909 | |
---|
920 | | -static const struct file_operations dispatch_proc_fops = { |
---|
921 | | - .owner = THIS_MODULE, |
---|
922 | | - .open = dispatch_proc_open, |
---|
923 | | - .read = seq_read, |
---|
924 | | - .llseek = seq_lseek, |
---|
925 | | - .release = single_release, |
---|
926 | | - .write = dispatch_proc_write, |
---|
| 910 | +static const struct proc_ops dispatch_proc_ops = { |
---|
| 911 | + .proc_open = dispatch_proc_open, |
---|
| 912 | + .proc_read = seq_read, |
---|
| 913 | + .proc_lseek = seq_lseek, |
---|
| 914 | + .proc_release = single_release, |
---|
| 915 | + .proc_write = dispatch_proc_write, |
---|
927 | 916 | }; |
---|
928 | | - |
---|
929 | | -static char *next_cmd(char **cmds) |
---|
930 | | -{ |
---|
931 | | - char *start = *cmds; |
---|
932 | | - char *end; |
---|
933 | | - |
---|
934 | | - while ((end = strchr(start, ',')) && end == start) |
---|
935 | | - start = end + 1; |
---|
936 | | - |
---|
937 | | - if (!end) |
---|
938 | | - return NULL; |
---|
939 | | - |
---|
940 | | - *end = 0; |
---|
941 | | - *cmds = end + 1; |
---|
942 | | - return start; |
---|
943 | | -} |
---|
944 | | - |
---|
945 | 917 | |
---|
946 | 918 | /**************************************************************************** |
---|
947 | 919 | **************************************************************************** |
---|
.. | .. |
---|
1424 | 1396 | if (id >= TPACPI_RFK_SW_MAX) |
---|
1425 | 1397 | return -ENODEV; |
---|
1426 | 1398 | |
---|
1427 | | - while ((cmd = next_cmd(&buf))) { |
---|
| 1399 | + while ((cmd = strsep(&buf, ","))) { |
---|
1428 | 1400 | if (strlencmp(cmd, "enable") == 0) |
---|
1429 | 1401 | status = TPACPI_RFK_RADIO_ON; |
---|
1430 | 1402 | else if (strlencmp(cmd, "disable") == 0) |
---|
.. | .. |
---|
1932 | 1904 | TP_ACPI_HOTKEYSCAN_CALCULATOR, |
---|
1933 | 1905 | TP_ACPI_HOTKEYSCAN_BLUETOOTH, |
---|
1934 | 1906 | TP_ACPI_HOTKEYSCAN_KEYBOARD, |
---|
| 1907 | + TP_ACPI_HOTKEYSCAN_FN_RIGHT_SHIFT, /* Used by "Lenovo Quick Clean" */ |
---|
| 1908 | + TP_ACPI_HOTKEYSCAN_NOTIFICATION_CENTER, |
---|
| 1909 | + TP_ACPI_HOTKEYSCAN_PICKUP_PHONE, |
---|
| 1910 | + TP_ACPI_HOTKEYSCAN_HANGUP_PHONE, |
---|
1935 | 1911 | |
---|
1936 | 1912 | /* Hotkey keymap size */ |
---|
1937 | 1913 | TPACPI_HOTKEY_MAP_LEN |
---|
.. | .. |
---|
3452 | 3428 | KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, |
---|
3453 | 3429 | KEY_UNKNOWN, |
---|
3454 | 3430 | |
---|
3455 | | - KEY_FAVORITES, /* Favorite app, 0x311 */ |
---|
3456 | | - KEY_RESERVED, /* Clipping tool */ |
---|
3457 | | - KEY_CALC, /* Calculator (above numpad, P52) */ |
---|
3458 | | - KEY_BLUETOOTH, /* Bluetooth */ |
---|
3459 | | - KEY_KEYBOARD /* Keyboard, 0x315 */ |
---|
| 3431 | + KEY_BOOKMARKS, /* Favorite app, 0x311 */ |
---|
| 3432 | + KEY_SELECTIVE_SCREENSHOT, /* Clipping tool */ |
---|
| 3433 | + KEY_CALC, /* Calculator (above numpad, P52) */ |
---|
| 3434 | + KEY_BLUETOOTH, /* Bluetooth */ |
---|
| 3435 | + KEY_KEYBOARD, /* Keyboard, 0x315 */ |
---|
| 3436 | + KEY_FN_RIGHT_SHIFT, /* Fn + right Shift */ |
---|
| 3437 | + KEY_NOTIFICATION_CENTER, /* Notification Center */ |
---|
| 3438 | + KEY_PICKUP_PHONE, /* Answer incoming call */ |
---|
| 3439 | + KEY_HANGUP_PHONE, /* Decline incoming call */ |
---|
3460 | 3440 | }, |
---|
3461 | 3441 | }; |
---|
3462 | 3442 | |
---|
.. | .. |
---|
3652 | 3632 | goto err_exit; |
---|
3653 | 3633 | |
---|
3654 | 3634 | /* Set up key map */ |
---|
3655 | | - hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE, |
---|
3656 | | - GFP_KERNEL); |
---|
3657 | | - if (!hotkey_keycode_map) { |
---|
3658 | | - pr_err("failed to allocate memory for key map\n"); |
---|
3659 | | - res = -ENOMEM; |
---|
3660 | | - goto err_exit; |
---|
3661 | | - } |
---|
3662 | | - |
---|
3663 | 3635 | keymap_id = tpacpi_check_quirks(tpacpi_keymap_qtable, |
---|
3664 | 3636 | ARRAY_SIZE(tpacpi_keymap_qtable)); |
---|
3665 | 3637 | BUG_ON(keymap_id >= ARRAY_SIZE(tpacpi_keymaps)); |
---|
3666 | 3638 | dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, |
---|
3667 | 3639 | "using keymap number %lu\n", keymap_id); |
---|
3668 | 3640 | |
---|
3669 | | - memcpy(hotkey_keycode_map, &tpacpi_keymaps[keymap_id], |
---|
3670 | | - TPACPI_HOTKEY_MAP_SIZE); |
---|
| 3641 | + hotkey_keycode_map = kmemdup(&tpacpi_keymaps[keymap_id], |
---|
| 3642 | + TPACPI_HOTKEY_MAP_SIZE, GFP_KERNEL); |
---|
| 3643 | + if (!hotkey_keycode_map) { |
---|
| 3644 | + pr_err("failed to allocate memory for key map\n"); |
---|
| 3645 | + res = -ENOMEM; |
---|
| 3646 | + goto err_exit; |
---|
| 3647 | + } |
---|
3671 | 3648 | |
---|
3672 | 3649 | input_set_capability(tpacpi_inputdev, EV_MSC, MSC_SCAN); |
---|
3673 | 3650 | tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE; |
---|
.. | .. |
---|
4056 | 4033 | return true; |
---|
4057 | 4034 | case TP_HKEY_EV_THM_CSM_COMPLETED: |
---|
4058 | 4035 | pr_debug("EC reports: Thermal Control Command set completed (DYTC)\n"); |
---|
4059 | | - /* recommended action: do nothing, we don't have |
---|
4060 | | - * Lenovo ATM information */ |
---|
| 4036 | + /* Thermal event - pass on to event handler */ |
---|
| 4037 | + tpacpi_driver_event(hkey); |
---|
4061 | 4038 | return true; |
---|
4062 | 4039 | case TP_HKEY_EV_THM_TRANSFM_CHANGED: |
---|
4063 | 4040 | pr_debug("EC reports: Thermal Transformation changed (GMTS)\n"); |
---|
.. | .. |
---|
4086 | 4063 | * AC status changed; can be triggered by plugging or |
---|
4087 | 4064 | * unplugging AC adapter, docking or undocking. */ |
---|
4088 | 4065 | |
---|
4089 | | - /* fallthrough */ |
---|
| 4066 | + fallthrough; |
---|
4090 | 4067 | |
---|
4091 | 4068 | case TP_HKEY_EV_KEY_NUMLOCK: |
---|
4092 | 4069 | case TP_HKEY_EV_KEY_FN: |
---|
.. | .. |
---|
4208 | 4185 | known_ev = true; |
---|
4209 | 4186 | break; |
---|
4210 | 4187 | } |
---|
4211 | | - /* fallthrough to default */ |
---|
| 4188 | + fallthrough; /* to default */ |
---|
4212 | 4189 | default: |
---|
4213 | 4190 | known_ev = false; |
---|
4214 | 4191 | } |
---|
.. | .. |
---|
4321 | 4298 | mask = hotkey_user_mask; |
---|
4322 | 4299 | |
---|
4323 | 4300 | res = 0; |
---|
4324 | | - while ((cmd = next_cmd(&buf))) { |
---|
| 4301 | + while ((cmd = strsep(&buf, ","))) { |
---|
4325 | 4302 | if (strlencmp(cmd, "enable") == 0) { |
---|
4326 | 4303 | hotkey_enabledisable_warn(1); |
---|
4327 | 4304 | } else if (strlencmp(cmd, "disable") == 0) { |
---|
.. | .. |
---|
5248 | 5225 | enable = 0; |
---|
5249 | 5226 | disable = 0; |
---|
5250 | 5227 | |
---|
5251 | | - while ((cmd = next_cmd(&buf))) { |
---|
| 5228 | + while ((cmd = strsep(&buf, ","))) { |
---|
5252 | 5229 | if (strlencmp(cmd, "lcd_enable") == 0) { |
---|
5253 | 5230 | enable |= TP_ACPI_VIDEO_S_LCD; |
---|
5254 | 5231 | } else if (strlencmp(cmd, "lcd_disable") == 0) { |
---|
.. | .. |
---|
5449 | 5426 | |
---|
5450 | 5427 | static void kbdlight_exit(void) |
---|
5451 | 5428 | { |
---|
5452 | | - if (tp_features.kbdlight) |
---|
5453 | | - led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev); |
---|
| 5429 | + led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev); |
---|
5454 | 5430 | } |
---|
5455 | 5431 | |
---|
5456 | 5432 | static int kbdlight_set_level_and_update(int level) |
---|
.. | .. |
---|
5488 | 5464 | static int kbdlight_write(char *buf) |
---|
5489 | 5465 | { |
---|
5490 | 5466 | char *cmd; |
---|
5491 | | - int level = -1; |
---|
| 5467 | + int res, level = -EINVAL; |
---|
5492 | 5468 | |
---|
5493 | 5469 | if (!tp_features.kbdlight) |
---|
5494 | 5470 | return -ENODEV; |
---|
5495 | 5471 | |
---|
5496 | | - while ((cmd = next_cmd(&buf))) { |
---|
5497 | | - if (strlencmp(cmd, "0") == 0) |
---|
5498 | | - level = 0; |
---|
5499 | | - else if (strlencmp(cmd, "1") == 0) |
---|
5500 | | - level = 1; |
---|
5501 | | - else if (strlencmp(cmd, "2") == 0) |
---|
5502 | | - level = 2; |
---|
5503 | | - else |
---|
5504 | | - return -EINVAL; |
---|
| 5472 | + while ((cmd = strsep(&buf, ","))) { |
---|
| 5473 | + res = kstrtoint(cmd, 10, &level); |
---|
| 5474 | + if (res < 0) |
---|
| 5475 | + return res; |
---|
5505 | 5476 | } |
---|
5506 | 5477 | |
---|
5507 | | - if (level == -1) |
---|
| 5478 | + if (level >= 3 || level < 0) |
---|
5508 | 5479 | return -EINVAL; |
---|
5509 | 5480 | |
---|
5510 | 5481 | return kbdlight_set_level_and_update(level); |
---|
.. | .. |
---|
5673 | 5644 | if (!tp_features.light) |
---|
5674 | 5645 | return -ENODEV; |
---|
5675 | 5646 | |
---|
5676 | | - while ((cmd = next_cmd(&buf))) { |
---|
| 5647 | + while ((cmd = strsep(&buf, ","))) { |
---|
5677 | 5648 | if (strlencmp(cmd, "on") == 0) { |
---|
5678 | 5649 | newstatus = 1; |
---|
5679 | 5650 | } else if (strlencmp(cmd, "off") == 0) { |
---|
.. | .. |
---|
5758 | 5729 | char *cmd; |
---|
5759 | 5730 | int cmos_cmd, res; |
---|
5760 | 5731 | |
---|
5761 | | - while ((cmd = next_cmd(&buf))) { |
---|
| 5732 | + while ((cmd = strsep(&buf, ","))) { |
---|
5762 | 5733 | if (sscanf(cmd, "%u", &cmos_cmd) == 1 && |
---|
5763 | 5734 | cmos_cmd >= 0 && cmos_cmd <= 21) { |
---|
5764 | 5735 | /* cmos_cmd set */ |
---|
.. | .. |
---|
5873 | 5844 | return -EPERM; |
---|
5874 | 5845 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", |
---|
5875 | 5846 | (1 << led), led_sled_arg1[ledstatus])) |
---|
5876 | | - rc = -EIO; |
---|
| 5847 | + return -EIO; |
---|
5877 | 5848 | break; |
---|
5878 | 5849 | case TPACPI_LED_OLD: |
---|
5879 | 5850 | /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ |
---|
.. | .. |
---|
5897 | 5868 | return -EPERM; |
---|
5898 | 5869 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", |
---|
5899 | 5870 | led, led_led_arg1[ledstatus])) |
---|
5900 | | - rc = -EIO; |
---|
| 5871 | + return -EIO; |
---|
5901 | 5872 | break; |
---|
5902 | 5873 | default: |
---|
5903 | | - rc = -ENXIO; |
---|
| 5874 | + return -ENXIO; |
---|
5904 | 5875 | } |
---|
5905 | 5876 | |
---|
5906 | 5877 | if (!rc) |
---|
.. | .. |
---|
5964 | 5935 | { |
---|
5965 | 5936 | unsigned int i; |
---|
5966 | 5937 | |
---|
5967 | | - for (i = 0; i < TPACPI_LED_NUMLEDS; i++) { |
---|
5968 | | - if (tpacpi_leds[i].led_classdev.name) |
---|
5969 | | - led_classdev_unregister(&tpacpi_leds[i].led_classdev); |
---|
5970 | | - } |
---|
| 5938 | + for (i = 0; i < TPACPI_LED_NUMLEDS; i++) |
---|
| 5939 | + led_classdev_unregister(&tpacpi_leds[i].led_classdev); |
---|
5971 | 5940 | |
---|
5972 | 5941 | kfree(tpacpi_leds); |
---|
5973 | 5942 | } |
---|
5974 | 5943 | |
---|
5975 | 5944 | static int __init tpacpi_init_led(unsigned int led) |
---|
5976 | 5945 | { |
---|
5977 | | - int rc; |
---|
5978 | | - |
---|
5979 | | - tpacpi_leds[led].led = led; |
---|
5980 | | - |
---|
5981 | 5946 | /* LEDs with no name don't get registered */ |
---|
5982 | 5947 | if (!tpacpi_led_names[led]) |
---|
5983 | 5948 | return 0; |
---|
.. | .. |
---|
5985 | 5950 | tpacpi_leds[led].led_classdev.brightness_set_blocking = &led_sysfs_set; |
---|
5986 | 5951 | tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set; |
---|
5987 | 5952 | if (led_supported == TPACPI_LED_570) |
---|
5988 | | - tpacpi_leds[led].led_classdev.brightness_get = |
---|
5989 | | - &led_sysfs_get; |
---|
| 5953 | + tpacpi_leds[led].led_classdev.brightness_get = &led_sysfs_get; |
---|
5990 | 5954 | |
---|
5991 | 5955 | tpacpi_leds[led].led_classdev.name = tpacpi_led_names[led]; |
---|
| 5956 | + tpacpi_leds[led].led = led; |
---|
5992 | 5957 | |
---|
5993 | | - rc = led_classdev_register(&tpacpi_pdev->dev, |
---|
5994 | | - &tpacpi_leds[led].led_classdev); |
---|
5995 | | - if (rc < 0) |
---|
5996 | | - tpacpi_leds[led].led_classdev.name = NULL; |
---|
5997 | | - |
---|
5998 | | - return rc; |
---|
| 5958 | + return led_classdev_register(&tpacpi_pdev->dev, &tpacpi_leds[led].led_classdev); |
---|
5999 | 5959 | } |
---|
6000 | 5960 | |
---|
6001 | 5961 | static const struct tpacpi_quirk led_useful_qtable[] __initconst = { |
---|
.. | .. |
---|
6042 | 6002 | .quirks = 0x00bfU, |
---|
6043 | 6003 | }, |
---|
6044 | 6004 | }; |
---|
6045 | | - |
---|
6046 | | -#undef TPACPI_LEDQ_IBM |
---|
6047 | | -#undef TPACPI_LEDQ_LNV |
---|
6048 | 6005 | |
---|
6049 | 6006 | static enum led_access_mode __init led_init_detect_mode(void) |
---|
6050 | 6007 | { |
---|
.. | .. |
---|
6108 | 6065 | for (i = 0; i < TPACPI_LED_NUMLEDS; i++) { |
---|
6109 | 6066 | tpacpi_leds[i].led = -1; |
---|
6110 | 6067 | |
---|
6111 | | - if (!tpacpi_is_led_restricted(i) && |
---|
6112 | | - test_bit(i, &useful_leds)) { |
---|
| 6068 | + if (!tpacpi_is_led_restricted(i) && test_bit(i, &useful_leds)) { |
---|
6113 | 6069 | rc = tpacpi_init_led(i); |
---|
6114 | 6070 | if (rc < 0) { |
---|
6115 | 6071 | led_exit(); |
---|
.. | .. |
---|
6162 | 6118 | if (!led_supported) |
---|
6163 | 6119 | return -ENODEV; |
---|
6164 | 6120 | |
---|
6165 | | - while ((cmd = next_cmd(&buf))) { |
---|
| 6121 | + while ((cmd = strsep(&buf, ","))) { |
---|
6166 | 6122 | if (sscanf(cmd, "%d", &led) != 1) |
---|
6167 | 6123 | return -EINVAL; |
---|
6168 | 6124 | |
---|
6169 | | - if (led < 0 || led > (TPACPI_LED_NUMLEDS - 1) || |
---|
6170 | | - tpacpi_leds[led].led < 0) |
---|
| 6125 | + if (led < 0 || led > (TPACPI_LED_NUMLEDS - 1)) |
---|
| 6126 | + return -ENODEV; |
---|
| 6127 | + |
---|
| 6128 | + if (tpacpi_leds[led].led < 0) |
---|
6171 | 6129 | return -ENODEV; |
---|
6172 | 6130 | |
---|
6173 | 6131 | if (strstr(cmd, "off")) { |
---|
.. | .. |
---|
6247 | 6205 | if (!beep_handle) |
---|
6248 | 6206 | return -ENODEV; |
---|
6249 | 6207 | |
---|
6250 | | - while ((cmd = next_cmd(&buf))) { |
---|
| 6208 | + while ((cmd = strsep(&buf, ","))) { |
---|
6251 | 6209 | if (sscanf(cmd, "%u", &beep_cmd) == 1 && |
---|
6252 | 6210 | beep_cmd >= 0 && beep_cmd <= 17) { |
---|
6253 | 6211 | /* beep_cmd set */ |
---|
.. | .. |
---|
6318 | 6276 | t = TP_EC_THERMAL_TMP8; |
---|
6319 | 6277 | idx -= 8; |
---|
6320 | 6278 | } |
---|
6321 | | - /* fallthrough */ |
---|
6322 | 6279 | #endif |
---|
| 6280 | + fallthrough; |
---|
6323 | 6281 | case TPACPI_THERMAL_TPEC_8: |
---|
6324 | 6282 | if (idx <= 7) { |
---|
6325 | 6283 | if (!acpi_ec_read(t + idx, &tmp)) |
---|
.. | .. |
---|
7030 | 6988 | pr_warn("Cannot enable backlight brightness support, ACPI is already handling it. Refer to the acpi_backlight kernel parameter.\n"); |
---|
7031 | 6989 | return 1; |
---|
7032 | 6990 | } |
---|
7033 | | - } else if (tp_features.bright_acpimode && brightness_enable > 1) { |
---|
7034 | | - pr_notice("Standard ACPI backlight interface not available, thinkpad_acpi native brightness control enabled\n"); |
---|
| 6991 | + } else if (!tp_features.bright_acpimode) { |
---|
| 6992 | + pr_notice("ACPI backlight interface not available\n"); |
---|
| 6993 | + return 1; |
---|
7035 | 6994 | } |
---|
| 6995 | + |
---|
| 6996 | + pr_notice("ACPI native brightness control enabled\n"); |
---|
7036 | 6997 | |
---|
7037 | 6998 | /* |
---|
7038 | 6999 | * Check for module parameter bogosity, note that we |
---|
.. | .. |
---|
7150 | 7111 | if (level < 0) |
---|
7151 | 7112 | return level; |
---|
7152 | 7113 | |
---|
7153 | | - while ((cmd = next_cmd(&buf))) { |
---|
| 7114 | + while ((cmd = strsep(&buf, ","))) { |
---|
7154 | 7115 | if (strlencmp(cmd, "up") == 0) { |
---|
7155 | 7116 | if (level < bright_maxlvl) |
---|
7156 | 7117 | level++; |
---|
.. | .. |
---|
7902 | 7863 | new_level = s & TP_EC_AUDIO_LVL_MSK; |
---|
7903 | 7864 | new_mute = s & TP_EC_AUDIO_MUTESW_MSK; |
---|
7904 | 7865 | |
---|
7905 | | - while ((cmd = next_cmd(&buf))) { |
---|
| 7866 | + while ((cmd = strsep(&buf, ","))) { |
---|
7906 | 7867 | if (!tp_features.mixer_no_level_control) { |
---|
7907 | 7868 | if (strlencmp(cmd, "up") == 0) { |
---|
7908 | 7869 | if (new_mute) |
---|
.. | .. |
---|
8032 | 7993 | * does so, its initial value is meaningless (0x07). |
---|
8033 | 7994 | * |
---|
8034 | 7995 | * For firmware bugs, refer to: |
---|
8035 | | - * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues |
---|
| 7996 | + * https://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues |
---|
8036 | 7997 | * |
---|
8037 | 7998 | * ---- |
---|
8038 | 7999 | * |
---|
.. | .. |
---|
8057 | 8018 | * mode. |
---|
8058 | 8019 | * |
---|
8059 | 8020 | * For firmware bugs, refer to: |
---|
8060 | | - * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues |
---|
| 8021 | + * https://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues |
---|
8061 | 8022 | * |
---|
8062 | 8023 | * ---- |
---|
8063 | 8024 | * |
---|
.. | .. |
---|
8358 | 8319 | |
---|
8359 | 8320 | switch (fan_control_access_mode) { |
---|
8360 | 8321 | case TPACPI_FAN_WR_ACPI_SFAN: |
---|
8361 | | - if (level >= 0 && level <= 7) { |
---|
8362 | | - if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level)) |
---|
8363 | | - return -EIO; |
---|
8364 | | - } else |
---|
| 8322 | + if ((level < 0) || (level > 7)) |
---|
8365 | 8323 | return -EINVAL; |
---|
| 8324 | + |
---|
| 8325 | + if (tp_features.second_fan_ctl) { |
---|
| 8326 | + if (!fan_select_fan2() || |
---|
| 8327 | + !acpi_evalf(sfan_handle, NULL, NULL, "vd", level)) { |
---|
| 8328 | + pr_warn("Couldn't set 2nd fan level, disabling support\n"); |
---|
| 8329 | + tp_features.second_fan_ctl = 0; |
---|
| 8330 | + } |
---|
| 8331 | + fan_select_fan1(); |
---|
| 8332 | + } |
---|
| 8333 | + if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level)) |
---|
| 8334 | + return -EIO; |
---|
8366 | 8335 | break; |
---|
8367 | 8336 | |
---|
8368 | 8337 | case TPACPI_FAN_WR_ACPI_FANS: |
---|
.. | .. |
---|
8379 | 8348 | else if (level & TP_EC_FAN_AUTO) |
---|
8380 | 8349 | level |= 4; /* safety min speed 4 */ |
---|
8381 | 8350 | |
---|
| 8351 | + if (tp_features.second_fan_ctl) { |
---|
| 8352 | + if (!fan_select_fan2() || |
---|
| 8353 | + !acpi_ec_write(fan_status_offset, level)) { |
---|
| 8354 | + pr_warn("Couldn't set 2nd fan level, disabling support\n"); |
---|
| 8355 | + tp_features.second_fan_ctl = 0; |
---|
| 8356 | + } |
---|
| 8357 | + fan_select_fan1(); |
---|
| 8358 | + |
---|
| 8359 | + } |
---|
8382 | 8360 | if (!acpi_ec_write(fan_status_offset, level)) |
---|
8383 | 8361 | return -EIO; |
---|
8384 | 8362 | else |
---|
.. | .. |
---|
8795 | 8773 | .attrs = fan_attributes, |
---|
8796 | 8774 | }; |
---|
8797 | 8775 | |
---|
8798 | | -#define TPACPI_FAN_Q1 0x0001 /* Unitialized HFSP */ |
---|
| 8776 | +#define TPACPI_FAN_Q1 0x0001 /* Unitialized HFSP */ |
---|
8799 | 8777 | #define TPACPI_FAN_2FAN 0x0002 /* EC 0x31 bit 0 selects fan2 */ |
---|
8800 | | - |
---|
8801 | | -#define TPACPI_FAN_QI(__id1, __id2, __quirks) \ |
---|
8802 | | - { .vendor = PCI_VENDOR_ID_IBM, \ |
---|
8803 | | - .bios = TPACPI_MATCH_ANY, \ |
---|
8804 | | - .ec = TPID(__id1, __id2), \ |
---|
8805 | | - .quirks = __quirks } |
---|
8806 | | - |
---|
8807 | | -#define TPACPI_FAN_QL(__id1, __id2, __quirks) \ |
---|
8808 | | - { .vendor = PCI_VENDOR_ID_LENOVO, \ |
---|
8809 | | - .bios = TPACPI_MATCH_ANY, \ |
---|
8810 | | - .ec = TPID(__id1, __id2), \ |
---|
8811 | | - .quirks = __quirks } |
---|
8812 | | - |
---|
8813 | | -#define TPACPI_FAN_QB(__id1, __id2, __quirks) \ |
---|
8814 | | - { .vendor = PCI_VENDOR_ID_LENOVO, \ |
---|
8815 | | - .bios = TPID(__id1, __id2), \ |
---|
8816 | | - .ec = TPACPI_MATCH_ANY, \ |
---|
8817 | | - .quirks = __quirks } |
---|
| 8778 | +#define TPACPI_FAN_2CTL 0x0004 /* selects fan2 control */ |
---|
8818 | 8779 | |
---|
8819 | 8780 | static const struct tpacpi_quirk fan_quirk_table[] __initconst = { |
---|
8820 | | - TPACPI_FAN_QI('1', 'Y', TPACPI_FAN_Q1), |
---|
8821 | | - TPACPI_FAN_QI('7', '8', TPACPI_FAN_Q1), |
---|
8822 | | - TPACPI_FAN_QI('7', '6', TPACPI_FAN_Q1), |
---|
8823 | | - TPACPI_FAN_QI('7', '0', TPACPI_FAN_Q1), |
---|
8824 | | - TPACPI_FAN_QL('7', 'M', TPACPI_FAN_2FAN), |
---|
8825 | | - TPACPI_FAN_QB('N', '1', TPACPI_FAN_2FAN), |
---|
| 8781 | + TPACPI_QEC_IBM('1', 'Y', TPACPI_FAN_Q1), |
---|
| 8782 | + TPACPI_QEC_IBM('7', '8', TPACPI_FAN_Q1), |
---|
| 8783 | + TPACPI_QEC_IBM('7', '6', TPACPI_FAN_Q1), |
---|
| 8784 | + TPACPI_QEC_IBM('7', '0', TPACPI_FAN_Q1), |
---|
| 8785 | + TPACPI_QEC_LNV('7', 'M', TPACPI_FAN_2FAN), |
---|
| 8786 | + TPACPI_Q_LNV('N', '1', TPACPI_FAN_2FAN), |
---|
| 8787 | + TPACPI_Q_LNV3('N', '1', 'D', TPACPI_FAN_2CTL), /* P70 */ |
---|
| 8788 | + TPACPI_Q_LNV3('N', '1', 'E', TPACPI_FAN_2CTL), /* P50 */ |
---|
| 8789 | + TPACPI_Q_LNV3('N', '1', 'T', TPACPI_FAN_2CTL), /* P71 */ |
---|
| 8790 | + TPACPI_Q_LNV3('N', '1', 'U', TPACPI_FAN_2CTL), /* P51 */ |
---|
| 8791 | + TPACPI_Q_LNV3('N', '2', 'C', TPACPI_FAN_2CTL), /* P52 / P72 */ |
---|
| 8792 | + TPACPI_Q_LNV3('N', '2', 'N', TPACPI_FAN_2CTL), /* P53 / P73 */ |
---|
| 8793 | + TPACPI_Q_LNV3('N', '2', 'E', TPACPI_FAN_2CTL), /* P1 / X1 Extreme (1st gen) */ |
---|
| 8794 | + TPACPI_Q_LNV3('N', '2', 'O', TPACPI_FAN_2CTL), /* P1 / X1 Extreme (2nd gen) */ |
---|
| 8795 | + TPACPI_Q_LNV3('N', '2', 'V', TPACPI_FAN_2CTL), /* P1 / X1 Extreme (3nd gen) */ |
---|
| 8796 | + TPACPI_Q_LNV3('N', '4', '0', TPACPI_FAN_2CTL), /* P1 / X1 Extreme (4nd gen) */ |
---|
| 8797 | + TPACPI_Q_LNV3('N', '3', '0', TPACPI_FAN_2CTL), /* P15 (1st gen) / P15v (1st gen) */ |
---|
| 8798 | + TPACPI_Q_LNV3('N', '3', '2', TPACPI_FAN_2CTL), /* X1 Carbon (9th gen) */ |
---|
8826 | 8799 | }; |
---|
8827 | | - |
---|
8828 | | -#undef TPACPI_FAN_QL |
---|
8829 | | -#undef TPACPI_FAN_QI |
---|
8830 | | -#undef TPACPI_FAN_QB |
---|
8831 | 8800 | |
---|
8832 | 8801 | static int __init fan_init(struct ibm_init_struct *iibm) |
---|
8833 | 8802 | { |
---|
.. | .. |
---|
8844 | 8813 | fan_watchdog_maxinterval = 0; |
---|
8845 | 8814 | tp_features.fan_ctrl_status_undef = 0; |
---|
8846 | 8815 | tp_features.second_fan = 0; |
---|
| 8816 | + tp_features.second_fan_ctl = 0; |
---|
8847 | 8817 | fan_control_desired_level = 7; |
---|
8848 | 8818 | |
---|
8849 | 8819 | if (tpacpi_is_ibm()) { |
---|
.. | .. |
---|
8868 | 8838 | fan_quirk1_setup(); |
---|
8869 | 8839 | if (quirks & TPACPI_FAN_2FAN) { |
---|
8870 | 8840 | tp_features.second_fan = 1; |
---|
8871 | | - dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN, |
---|
8872 | | - "secondary fan support enabled\n"); |
---|
| 8841 | + pr_info("secondary fan support enabled\n"); |
---|
| 8842 | + } |
---|
| 8843 | + if (quirks & TPACPI_FAN_2CTL) { |
---|
| 8844 | + tp_features.second_fan = 1; |
---|
| 8845 | + tp_features.second_fan_ctl = 1; |
---|
| 8846 | + pr_info("secondary fan control enabled\n"); |
---|
8873 | 8847 | } |
---|
8874 | 8848 | } else { |
---|
8875 | 8849 | pr_err("ThinkPad ACPI EC access misbehaving, fan status and control unavailable\n"); |
---|
.. | .. |
---|
9204 | 9178 | char *cmd; |
---|
9205 | 9179 | int rc = 0; |
---|
9206 | 9180 | |
---|
9207 | | - while (!rc && (cmd = next_cmd(&buf))) { |
---|
| 9181 | + while (!rc && (cmd = strsep(&buf, ","))) { |
---|
9208 | 9182 | if (!((fan_control_commands & TPACPI_FAN_CMD_LEVEL) && |
---|
9209 | 9183 | fan_write_cmd_level(cmd, &rc)) && |
---|
9210 | 9184 | !((fan_control_commands & TPACPI_FAN_CMD_ENABLE) && |
---|
.. | .. |
---|
9235 | 9209 | * Mute LED subdriver |
---|
9236 | 9210 | */ |
---|
9237 | 9211 | |
---|
| 9212 | +#define TPACPI_LED_MAX 2 |
---|
9238 | 9213 | |
---|
9239 | 9214 | struct tp_led_table { |
---|
9240 | 9215 | acpi_string name; |
---|
.. | .. |
---|
9243 | 9218 | int state; |
---|
9244 | 9219 | }; |
---|
9245 | 9220 | |
---|
9246 | | -static struct tp_led_table led_tables[] = { |
---|
9247 | | - [TPACPI_LED_MUTE] = { |
---|
| 9221 | +static struct tp_led_table led_tables[TPACPI_LED_MAX] = { |
---|
| 9222 | + [LED_AUDIO_MUTE] = { |
---|
9248 | 9223 | .name = "SSMS", |
---|
9249 | 9224 | .on_value = 1, |
---|
9250 | 9225 | .off_value = 0, |
---|
9251 | 9226 | }, |
---|
9252 | | - [TPACPI_LED_MICMUTE] = { |
---|
| 9227 | + [LED_AUDIO_MICMUTE] = { |
---|
9253 | 9228 | .name = "MMTS", |
---|
9254 | 9229 | .on_value = 2, |
---|
9255 | 9230 | .off_value = 0, |
---|
.. | .. |
---|
9274 | 9249 | return state; |
---|
9275 | 9250 | } |
---|
9276 | 9251 | |
---|
9277 | | -int tpacpi_led_set(int whichled, bool on) |
---|
| 9252 | +static int tpacpi_led_set(int whichled, bool on) |
---|
9278 | 9253 | { |
---|
9279 | 9254 | struct tp_led_table *t; |
---|
9280 | | - |
---|
9281 | | - if (whichled < 0 || whichled >= TPACPI_LED_MAX) |
---|
9282 | | - return -EINVAL; |
---|
9283 | 9255 | |
---|
9284 | 9256 | t = &led_tables[whichled]; |
---|
9285 | 9257 | if (t->state < 0 || t->state == on) |
---|
9286 | 9258 | return t->state; |
---|
9287 | 9259 | return mute_led_on_off(t, on); |
---|
9288 | 9260 | } |
---|
9289 | | -EXPORT_SYMBOL_GPL(tpacpi_led_set); |
---|
| 9261 | + |
---|
| 9262 | +static int tpacpi_led_mute_set(struct led_classdev *led_cdev, |
---|
| 9263 | + enum led_brightness brightness) |
---|
| 9264 | +{ |
---|
| 9265 | + return tpacpi_led_set(LED_AUDIO_MUTE, brightness != LED_OFF); |
---|
| 9266 | +} |
---|
| 9267 | + |
---|
| 9268 | +static int tpacpi_led_micmute_set(struct led_classdev *led_cdev, |
---|
| 9269 | + enum led_brightness brightness) |
---|
| 9270 | +{ |
---|
| 9271 | + return tpacpi_led_set(LED_AUDIO_MICMUTE, brightness != LED_OFF); |
---|
| 9272 | +} |
---|
| 9273 | + |
---|
| 9274 | +static struct led_classdev mute_led_cdev[TPACPI_LED_MAX] = { |
---|
| 9275 | + [LED_AUDIO_MUTE] = { |
---|
| 9276 | + .name = "platform::mute", |
---|
| 9277 | + .max_brightness = 1, |
---|
| 9278 | + .brightness_set_blocking = tpacpi_led_mute_set, |
---|
| 9279 | + .default_trigger = "audio-mute", |
---|
| 9280 | + }, |
---|
| 9281 | + [LED_AUDIO_MICMUTE] = { |
---|
| 9282 | + .name = "platform::micmute", |
---|
| 9283 | + .max_brightness = 1, |
---|
| 9284 | + .brightness_set_blocking = tpacpi_led_micmute_set, |
---|
| 9285 | + .default_trigger = "audio-micmute", |
---|
| 9286 | + }, |
---|
| 9287 | +}; |
---|
9290 | 9288 | |
---|
9291 | 9289 | static int mute_led_init(struct ibm_init_struct *iibm) |
---|
9292 | 9290 | { |
---|
9293 | 9291 | acpi_handle temp; |
---|
9294 | | - int i; |
---|
| 9292 | + int i, err; |
---|
9295 | 9293 | |
---|
9296 | 9294 | for (i = 0; i < TPACPI_LED_MAX; i++) { |
---|
9297 | 9295 | struct tp_led_table *t = &led_tables[i]; |
---|
9298 | | - if (ACPI_SUCCESS(acpi_get_handle(hkey_handle, t->name, &temp))) |
---|
9299 | | - mute_led_on_off(t, false); |
---|
9300 | | - else |
---|
| 9296 | + if (ACPI_FAILURE(acpi_get_handle(hkey_handle, t->name, &temp))) { |
---|
9301 | 9297 | t->state = -ENODEV; |
---|
| 9298 | + continue; |
---|
| 9299 | + } |
---|
| 9300 | + |
---|
| 9301 | + mute_led_cdev[i].brightness = ledtrig_audio_get(i); |
---|
| 9302 | + err = led_classdev_register(&tpacpi_pdev->dev, &mute_led_cdev[i]); |
---|
| 9303 | + if (err < 0) { |
---|
| 9304 | + while (i--) |
---|
| 9305 | + led_classdev_unregister(&mute_led_cdev[i]); |
---|
| 9306 | + return err; |
---|
| 9307 | + } |
---|
9302 | 9308 | } |
---|
9303 | 9309 | return 0; |
---|
9304 | 9310 | } |
---|
.. | .. |
---|
9307 | 9313 | { |
---|
9308 | 9314 | int i; |
---|
9309 | 9315 | |
---|
9310 | | - for (i = 0; i < TPACPI_LED_MAX; i++) |
---|
| 9316 | + for (i = 0; i < TPACPI_LED_MAX; i++) { |
---|
| 9317 | + led_classdev_unregister(&mute_led_cdev[i]); |
---|
9311 | 9318 | tpacpi_led_set(i, false); |
---|
| 9319 | + } |
---|
9312 | 9320 | } |
---|
9313 | 9321 | |
---|
9314 | 9322 | static void mute_led_resume(void) |
---|
.. | .. |
---|
9339 | 9347 | #define SET_START "BCCS" |
---|
9340 | 9348 | #define GET_STOP "BCSG" |
---|
9341 | 9349 | #define SET_STOP "BCSS" |
---|
9342 | | - |
---|
9343 | | -#define START_ATTR "charge_start_threshold" |
---|
9344 | | -#define STOP_ATTR "charge_stop_threshold" |
---|
9345 | 9350 | |
---|
9346 | 9351 | enum { |
---|
9347 | 9352 | BAT_ANY = 0, |
---|
.. | .. |
---|
9566 | 9571 | if (!battery_info.batteries[battery].start_support) |
---|
9567 | 9572 | return -ENODEV; |
---|
9568 | 9573 | /* valid values are [0, 99] */ |
---|
9569 | | - if (value < 0 || value > 99) |
---|
| 9574 | + if (value > 99) |
---|
9570 | 9575 | return -EINVAL; |
---|
9571 | 9576 | if (value > battery_info.batteries[battery].charge_stop) |
---|
9572 | 9577 | return -EINVAL; |
---|
.. | .. |
---|
9628 | 9633 | return sprintf(buf, "%d\n", ret); |
---|
9629 | 9634 | } |
---|
9630 | 9635 | |
---|
9631 | | -static ssize_t charge_start_threshold_show(struct device *device, |
---|
| 9636 | +static ssize_t charge_control_start_threshold_show(struct device *device, |
---|
9632 | 9637 | struct device_attribute *attr, |
---|
9633 | 9638 | char *buf) |
---|
9634 | 9639 | { |
---|
9635 | 9640 | return tpacpi_battery_show(THRESHOLD_START, device, buf); |
---|
9636 | 9641 | } |
---|
9637 | 9642 | |
---|
9638 | | -static ssize_t charge_stop_threshold_show(struct device *device, |
---|
| 9643 | +static ssize_t charge_control_end_threshold_show(struct device *device, |
---|
9639 | 9644 | struct device_attribute *attr, |
---|
9640 | 9645 | char *buf) |
---|
9641 | 9646 | { |
---|
9642 | 9647 | return tpacpi_battery_show(THRESHOLD_STOP, device, buf); |
---|
9643 | 9648 | } |
---|
9644 | 9649 | |
---|
9645 | | -static ssize_t charge_start_threshold_store(struct device *dev, |
---|
| 9650 | +static ssize_t charge_control_start_threshold_store(struct device *dev, |
---|
9646 | 9651 | struct device_attribute *attr, |
---|
9647 | 9652 | const char *buf, size_t count) |
---|
9648 | 9653 | { |
---|
9649 | 9654 | return tpacpi_battery_store(THRESHOLD_START, dev, buf, count); |
---|
9650 | 9655 | } |
---|
9651 | 9656 | |
---|
9652 | | -static ssize_t charge_stop_threshold_store(struct device *dev, |
---|
| 9657 | +static ssize_t charge_control_end_threshold_store(struct device *dev, |
---|
9653 | 9658 | struct device_attribute *attr, |
---|
9654 | 9659 | const char *buf, size_t count) |
---|
9655 | 9660 | { |
---|
9656 | 9661 | return tpacpi_battery_store(THRESHOLD_STOP, dev, buf, count); |
---|
9657 | 9662 | } |
---|
9658 | 9663 | |
---|
9659 | | -static DEVICE_ATTR_RW(charge_start_threshold); |
---|
9660 | | -static DEVICE_ATTR_RW(charge_stop_threshold); |
---|
| 9664 | +static DEVICE_ATTR_RW(charge_control_start_threshold); |
---|
| 9665 | +static DEVICE_ATTR_RW(charge_control_end_threshold); |
---|
| 9666 | +static struct device_attribute dev_attr_charge_start_threshold = __ATTR( |
---|
| 9667 | + charge_start_threshold, |
---|
| 9668 | + 0644, |
---|
| 9669 | + charge_control_start_threshold_show, |
---|
| 9670 | + charge_control_start_threshold_store |
---|
| 9671 | +); |
---|
| 9672 | +static struct device_attribute dev_attr_charge_stop_threshold = __ATTR( |
---|
| 9673 | + charge_stop_threshold, |
---|
| 9674 | + 0644, |
---|
| 9675 | + charge_control_end_threshold_show, |
---|
| 9676 | + charge_control_end_threshold_store |
---|
| 9677 | +); |
---|
9661 | 9678 | |
---|
9662 | 9679 | static struct attribute *tpacpi_battery_attrs[] = { |
---|
| 9680 | + &dev_attr_charge_control_start_threshold.attr, |
---|
| 9681 | + &dev_attr_charge_control_end_threshold.attr, |
---|
9663 | 9682 | &dev_attr_charge_start_threshold.attr, |
---|
9664 | 9683 | &dev_attr_charge_stop_threshold.attr, |
---|
9665 | 9684 | NULL, |
---|
.. | .. |
---|
9729 | 9748 | .exit = tpacpi_battery_exit, |
---|
9730 | 9749 | }; |
---|
9731 | 9750 | |
---|
| 9751 | +/************************************************************************* |
---|
| 9752 | + * LCD Shadow subdriver, for the Lenovo PrivacyGuard feature |
---|
| 9753 | + */ |
---|
| 9754 | + |
---|
| 9755 | +static int lcdshadow_state; |
---|
| 9756 | + |
---|
| 9757 | +static int lcdshadow_on_off(bool state) |
---|
| 9758 | +{ |
---|
| 9759 | + acpi_handle set_shadow_handle; |
---|
| 9760 | + int output; |
---|
| 9761 | + |
---|
| 9762 | + if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "SSSS", &set_shadow_handle))) { |
---|
| 9763 | + pr_warn("Thinkpad ACPI has no %s interface.\n", "SSSS"); |
---|
| 9764 | + return -EIO; |
---|
| 9765 | + } |
---|
| 9766 | + |
---|
| 9767 | + if (!acpi_evalf(set_shadow_handle, &output, NULL, "dd", (int)state)) |
---|
| 9768 | + return -EIO; |
---|
| 9769 | + |
---|
| 9770 | + lcdshadow_state = state; |
---|
| 9771 | + return 0; |
---|
| 9772 | +} |
---|
| 9773 | + |
---|
| 9774 | +static int lcdshadow_set(bool on) |
---|
| 9775 | +{ |
---|
| 9776 | + if (lcdshadow_state < 0) |
---|
| 9777 | + return lcdshadow_state; |
---|
| 9778 | + if (lcdshadow_state == on) |
---|
| 9779 | + return 0; |
---|
| 9780 | + return lcdshadow_on_off(on); |
---|
| 9781 | +} |
---|
| 9782 | + |
---|
| 9783 | +static int tpacpi_lcdshadow_init(struct ibm_init_struct *iibm) |
---|
| 9784 | +{ |
---|
| 9785 | + acpi_handle get_shadow_handle; |
---|
| 9786 | + int output; |
---|
| 9787 | + |
---|
| 9788 | + if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "GSSS", &get_shadow_handle))) { |
---|
| 9789 | + lcdshadow_state = -ENODEV; |
---|
| 9790 | + return 0; |
---|
| 9791 | + } |
---|
| 9792 | + |
---|
| 9793 | + if (!acpi_evalf(get_shadow_handle, &output, NULL, "dd", 0)) { |
---|
| 9794 | + lcdshadow_state = -EIO; |
---|
| 9795 | + return -EIO; |
---|
| 9796 | + } |
---|
| 9797 | + if (!(output & 0x10000)) { |
---|
| 9798 | + lcdshadow_state = -ENODEV; |
---|
| 9799 | + return 0; |
---|
| 9800 | + } |
---|
| 9801 | + lcdshadow_state = output & 0x1; |
---|
| 9802 | + |
---|
| 9803 | + return 0; |
---|
| 9804 | +} |
---|
| 9805 | + |
---|
| 9806 | +static void lcdshadow_resume(void) |
---|
| 9807 | +{ |
---|
| 9808 | + if (lcdshadow_state >= 0) |
---|
| 9809 | + lcdshadow_on_off(lcdshadow_state); |
---|
| 9810 | +} |
---|
| 9811 | + |
---|
| 9812 | +static int lcdshadow_read(struct seq_file *m) |
---|
| 9813 | +{ |
---|
| 9814 | + if (lcdshadow_state < 0) { |
---|
| 9815 | + seq_puts(m, "status:\t\tnot supported\n"); |
---|
| 9816 | + } else { |
---|
| 9817 | + seq_printf(m, "status:\t\t%d\n", lcdshadow_state); |
---|
| 9818 | + seq_puts(m, "commands:\t0, 1\n"); |
---|
| 9819 | + } |
---|
| 9820 | + |
---|
| 9821 | + return 0; |
---|
| 9822 | +} |
---|
| 9823 | + |
---|
| 9824 | +static int lcdshadow_write(char *buf) |
---|
| 9825 | +{ |
---|
| 9826 | + char *cmd; |
---|
| 9827 | + int res, state = -EINVAL; |
---|
| 9828 | + |
---|
| 9829 | + if (lcdshadow_state < 0) |
---|
| 9830 | + return -ENODEV; |
---|
| 9831 | + |
---|
| 9832 | + while ((cmd = strsep(&buf, ","))) { |
---|
| 9833 | + res = kstrtoint(cmd, 10, &state); |
---|
| 9834 | + if (res < 0) |
---|
| 9835 | + return res; |
---|
| 9836 | + } |
---|
| 9837 | + |
---|
| 9838 | + if (state >= 2 || state < 0) |
---|
| 9839 | + return -EINVAL; |
---|
| 9840 | + |
---|
| 9841 | + return lcdshadow_set(state); |
---|
| 9842 | +} |
---|
| 9843 | + |
---|
| 9844 | +static struct ibm_struct lcdshadow_driver_data = { |
---|
| 9845 | + .name = "lcdshadow", |
---|
| 9846 | + .resume = lcdshadow_resume, |
---|
| 9847 | + .read = lcdshadow_read, |
---|
| 9848 | + .write = lcdshadow_write, |
---|
| 9849 | +}; |
---|
| 9850 | + |
---|
| 9851 | +/************************************************************************* |
---|
| 9852 | + * DYTC subdriver, for the Lenovo lapmode feature |
---|
| 9853 | + */ |
---|
| 9854 | + |
---|
| 9855 | +#define DYTC_CMD_GET 2 /* To get current IC function and mode */ |
---|
| 9856 | +#define DYTC_GET_LAPMODE_BIT 17 /* Set when in lapmode */ |
---|
| 9857 | + |
---|
| 9858 | +static bool dytc_lapmode; |
---|
| 9859 | + |
---|
| 9860 | +static void dytc_lapmode_notify_change(void) |
---|
| 9861 | +{ |
---|
| 9862 | + sysfs_notify(&tpacpi_pdev->dev.kobj, NULL, "dytc_lapmode"); |
---|
| 9863 | +} |
---|
| 9864 | + |
---|
| 9865 | +static int dytc_command(int command, int *output) |
---|
| 9866 | +{ |
---|
| 9867 | + acpi_handle dytc_handle; |
---|
| 9868 | + |
---|
| 9869 | + if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "DYTC", &dytc_handle))) { |
---|
| 9870 | + /* Platform doesn't support DYTC */ |
---|
| 9871 | + return -ENODEV; |
---|
| 9872 | + } |
---|
| 9873 | + if (!acpi_evalf(dytc_handle, output, NULL, "dd", command)) |
---|
| 9874 | + return -EIO; |
---|
| 9875 | + return 0; |
---|
| 9876 | +} |
---|
| 9877 | + |
---|
| 9878 | +static int dytc_lapmode_get(bool *state) |
---|
| 9879 | +{ |
---|
| 9880 | + int output, err; |
---|
| 9881 | + |
---|
| 9882 | + err = dytc_command(DYTC_CMD_GET, &output); |
---|
| 9883 | + if (err) |
---|
| 9884 | + return err; |
---|
| 9885 | + *state = output & BIT(DYTC_GET_LAPMODE_BIT) ? true : false; |
---|
| 9886 | + return 0; |
---|
| 9887 | +} |
---|
| 9888 | + |
---|
| 9889 | +static void dytc_lapmode_refresh(void) |
---|
| 9890 | +{ |
---|
| 9891 | + bool new_state; |
---|
| 9892 | + int err; |
---|
| 9893 | + |
---|
| 9894 | + err = dytc_lapmode_get(&new_state); |
---|
| 9895 | + if (err || (new_state == dytc_lapmode)) |
---|
| 9896 | + return; |
---|
| 9897 | + |
---|
| 9898 | + dytc_lapmode = new_state; |
---|
| 9899 | + dytc_lapmode_notify_change(); |
---|
| 9900 | +} |
---|
| 9901 | + |
---|
| 9902 | +/* sysfs lapmode entry */ |
---|
| 9903 | +static ssize_t dytc_lapmode_show(struct device *dev, |
---|
| 9904 | + struct device_attribute *attr, |
---|
| 9905 | + char *buf) |
---|
| 9906 | +{ |
---|
| 9907 | + return snprintf(buf, PAGE_SIZE, "%d\n", dytc_lapmode); |
---|
| 9908 | +} |
---|
| 9909 | + |
---|
| 9910 | +static DEVICE_ATTR_RO(dytc_lapmode); |
---|
| 9911 | + |
---|
| 9912 | +static struct attribute *dytc_attributes[] = { |
---|
| 9913 | + &dev_attr_dytc_lapmode.attr, |
---|
| 9914 | + NULL, |
---|
| 9915 | +}; |
---|
| 9916 | + |
---|
| 9917 | +static const struct attribute_group dytc_attr_group = { |
---|
| 9918 | + .attrs = dytc_attributes, |
---|
| 9919 | +}; |
---|
| 9920 | + |
---|
| 9921 | +static int tpacpi_dytc_init(struct ibm_init_struct *iibm) |
---|
| 9922 | +{ |
---|
| 9923 | + int err; |
---|
| 9924 | + |
---|
| 9925 | + err = dytc_lapmode_get(&dytc_lapmode); |
---|
| 9926 | + /* If support isn't available (ENODEV) then don't return an error |
---|
| 9927 | + * but just don't create the sysfs group |
---|
| 9928 | + */ |
---|
| 9929 | + if (err == -ENODEV) |
---|
| 9930 | + return 0; |
---|
| 9931 | + /* For all other errors we can flag the failure */ |
---|
| 9932 | + if (err) |
---|
| 9933 | + return err; |
---|
| 9934 | + |
---|
| 9935 | + /* Platform supports this feature - create the group */ |
---|
| 9936 | + err = sysfs_create_group(&tpacpi_pdev->dev.kobj, &dytc_attr_group); |
---|
| 9937 | + return err; |
---|
| 9938 | +} |
---|
| 9939 | + |
---|
| 9940 | +static void dytc_exit(void) |
---|
| 9941 | +{ |
---|
| 9942 | + sysfs_remove_group(&tpacpi_pdev->dev.kobj, &dytc_attr_group); |
---|
| 9943 | +} |
---|
| 9944 | + |
---|
| 9945 | +static struct ibm_struct dytc_driver_data = { |
---|
| 9946 | + .name = "dytc", |
---|
| 9947 | + .exit = dytc_exit, |
---|
| 9948 | +}; |
---|
| 9949 | + |
---|
9732 | 9950 | /**************************************************************************** |
---|
9733 | 9951 | **************************************************************************** |
---|
9734 | 9952 | * |
---|
.. | .. |
---|
9776 | 9994 | |
---|
9777 | 9995 | mutex_unlock(&kbdlight_mutex); |
---|
9778 | 9996 | } |
---|
| 9997 | + |
---|
| 9998 | + if (hkey_event == TP_HKEY_EV_THM_CSM_COMPLETED) |
---|
| 9999 | + dytc_lapmode_refresh(); |
---|
| 10000 | + |
---|
9779 | 10001 | } |
---|
9780 | 10002 | |
---|
9781 | 10003 | static void hotkey_driver_event(const unsigned int scancode) |
---|
.. | .. |
---|
9901 | 10123 | if (ibm->write) |
---|
9902 | 10124 | mode |= S_IWUSR; |
---|
9903 | 10125 | entry = proc_create_data(ibm->name, mode, proc_dir, |
---|
9904 | | - &dispatch_proc_fops, ibm); |
---|
| 10126 | + &dispatch_proc_ops, ibm); |
---|
9905 | 10127 | if (!entry) { |
---|
9906 | 10128 | pr_err("unable to create proc entry %s\n", ibm->name); |
---|
9907 | 10129 | ret = -ENODEV; |
---|
.. | .. |
---|
9958 | 10180 | return '\0'; |
---|
9959 | 10181 | } |
---|
9960 | 10182 | |
---|
| 10183 | +static void find_new_ec_fwstr(const struct dmi_header *dm, void *private) |
---|
| 10184 | +{ |
---|
| 10185 | + char *ec_fw_string = (char *) private; |
---|
| 10186 | + const char *dmi_data = (const char *)dm; |
---|
| 10187 | + /* |
---|
| 10188 | + * ThinkPad Embedded Controller Program Table on newer models |
---|
| 10189 | + * |
---|
| 10190 | + * Offset | Name | Width | Description |
---|
| 10191 | + * ---------------------------------------------------- |
---|
| 10192 | + * 0x00 | Type | BYTE | 0x8C |
---|
| 10193 | + * 0x01 | Length | BYTE | |
---|
| 10194 | + * 0x02 | Handle | WORD | Varies |
---|
| 10195 | + * 0x04 | Signature | BYTEx6 | ASCII for "LENOVO" |
---|
| 10196 | + * 0x0A | OEM struct offset | BYTE | 0x0B |
---|
| 10197 | + * 0x0B | OEM struct number | BYTE | 0x07, for this structure |
---|
| 10198 | + * 0x0C | OEM struct revision | BYTE | 0x01, for this format |
---|
| 10199 | + * 0x0D | ECP version ID | STR ID | |
---|
| 10200 | + * 0x0E | ECP release date | STR ID | |
---|
| 10201 | + */ |
---|
| 10202 | + |
---|
| 10203 | + /* Return if data structure not match */ |
---|
| 10204 | + if (dm->type != 140 || dm->length < 0x0F || |
---|
| 10205 | + memcmp(dmi_data + 4, "LENOVO", 6) != 0 || |
---|
| 10206 | + dmi_data[0x0A] != 0x0B || dmi_data[0x0B] != 0x07 || |
---|
| 10207 | + dmi_data[0x0C] != 0x01) |
---|
| 10208 | + return; |
---|
| 10209 | + |
---|
| 10210 | + /* fwstr is the first 8byte string */ |
---|
| 10211 | + strncpy(ec_fw_string, dmi_data + 0x0F, 8); |
---|
| 10212 | +} |
---|
| 10213 | + |
---|
9961 | 10214 | /* returns 0 - probe ok, or < 0 - probe error. |
---|
9962 | 10215 | * Probe ok doesn't mean thinkpad found. |
---|
9963 | 10216 | * On error, kfree() cleanup on tp->* is not performed, caller must do it */ |
---|
.. | .. |
---|
9965 | 10218 | struct thinkpad_id_data *tp) |
---|
9966 | 10219 | { |
---|
9967 | 10220 | const struct dmi_device *dev = NULL; |
---|
9968 | | - char ec_fw_string[18]; |
---|
| 10221 | + char ec_fw_string[18] = {0}; |
---|
9969 | 10222 | char const *s; |
---|
9970 | 10223 | char t; |
---|
9971 | 10224 | |
---|
.. | .. |
---|
9997 | 10250 | * X32 or newer, all Z series; Some models must have an |
---|
9998 | 10251 | * up-to-date BIOS or they will not be detected. |
---|
9999 | 10252 | * |
---|
10000 | | - * See http://thinkwiki.org/wiki/List_of_DMI_IDs |
---|
| 10253 | + * See https://thinkwiki.org/wiki/List_of_DMI_IDs |
---|
10001 | 10254 | */ |
---|
10002 | 10255 | while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { |
---|
10003 | 10256 | if (sscanf(dev->name, |
---|
.. | .. |
---|
10005 | 10258 | ec_fw_string) == 1) { |
---|
10006 | 10259 | ec_fw_string[sizeof(ec_fw_string) - 1] = 0; |
---|
10007 | 10260 | ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; |
---|
10008 | | - |
---|
10009 | | - tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL); |
---|
10010 | | - if (!tp->ec_version_str) |
---|
10011 | | - return -ENOMEM; |
---|
10012 | | - |
---|
10013 | | - t = tpacpi_parse_fw_id(ec_fw_string, |
---|
10014 | | - &tp->ec_model, &tp->ec_release); |
---|
10015 | | - if (t != 'H') { |
---|
10016 | | - pr_notice("ThinkPad firmware release %s doesn't match the known patterns\n", |
---|
10017 | | - ec_fw_string); |
---|
10018 | | - pr_notice("please report this to %s\n", |
---|
10019 | | - TPACPI_MAIL); |
---|
10020 | | - } |
---|
10021 | 10261 | break; |
---|
| 10262 | + } |
---|
| 10263 | + } |
---|
| 10264 | + |
---|
| 10265 | + /* Newer ThinkPads have different EC program info table */ |
---|
| 10266 | + if (!ec_fw_string[0]) |
---|
| 10267 | + dmi_walk(find_new_ec_fwstr, &ec_fw_string); |
---|
| 10268 | + |
---|
| 10269 | + if (ec_fw_string[0]) { |
---|
| 10270 | + tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL); |
---|
| 10271 | + if (!tp->ec_version_str) |
---|
| 10272 | + return -ENOMEM; |
---|
| 10273 | + |
---|
| 10274 | + t = tpacpi_parse_fw_id(ec_fw_string, |
---|
| 10275 | + &tp->ec_model, &tp->ec_release); |
---|
| 10276 | + if (t != 'H') { |
---|
| 10277 | + pr_notice("ThinkPad firmware release %s doesn't match the known patterns\n", |
---|
| 10278 | + ec_fw_string); |
---|
| 10279 | + pr_notice("please report this to %s\n", TPACPI_MAIL); |
---|
10022 | 10280 | } |
---|
10023 | 10281 | } |
---|
10024 | 10282 | |
---|
.. | .. |
---|
10174 | 10432 | .init = tpacpi_battery_init, |
---|
10175 | 10433 | .data = &battery_driver_data, |
---|
10176 | 10434 | }, |
---|
| 10435 | + { |
---|
| 10436 | + .init = tpacpi_lcdshadow_init, |
---|
| 10437 | + .data = &lcdshadow_driver_data, |
---|
| 10438 | + }, |
---|
| 10439 | + { |
---|
| 10440 | + .init = tpacpi_dytc_init, |
---|
| 10441 | + .data = &dytc_driver_data, |
---|
| 10442 | + }, |
---|
10177 | 10443 | }; |
---|
10178 | 10444 | |
---|
10179 | 10445 | static int __init set_ibm_param(const char *val, const struct kernel_param *kp) |
---|
.. | .. |
---|
10192 | 10458 | continue; |
---|
10193 | 10459 | |
---|
10194 | 10460 | if (strcmp(ibm->name, kp->name) == 0 && ibm->write) { |
---|
10195 | | - if (strlen(val) > sizeof(ibms_init[i].param) - 2) |
---|
| 10461 | + if (strlen(val) > sizeof(ibms_init[i].param) - 1) |
---|
10196 | 10462 | return -ENOSPC; |
---|
10197 | 10463 | strcpy(ibms_init[i].param, val); |
---|
10198 | | - strcat(ibms_init[i].param, ","); |
---|
10199 | 10464 | return 0; |
---|
10200 | 10465 | } |
---|
10201 | 10466 | } |
---|
.. | .. |
---|
10233 | 10498 | |
---|
10234 | 10499 | module_param_named(volume_capabilities, volume_capabilities, uint, 0444); |
---|
10235 | 10500 | MODULE_PARM_DESC(volume_capabilities, |
---|
10236 | | - "Selects the mixer capabilites: 0=auto, 1=volume and mute, 2=mute only"); |
---|
| 10501 | + "Selects the mixer capabilities: 0=auto, 1=volume and mute, 2=mute only"); |
---|
10237 | 10502 | |
---|
10238 | 10503 | module_param_named(volume_control, volume_control_allowed, bool, 0444); |
---|
10239 | 10504 | MODULE_PARM_DESC(volume_control, |
---|
.. | .. |
---|
10508 | 10773 | /* |
---|
10509 | 10774 | * DMI matching for module autoloading |
---|
10510 | 10775 | * |
---|
10511 | | - * See http://thinkwiki.org/wiki/List_of_DMI_IDs |
---|
10512 | | - * See http://thinkwiki.org/wiki/BIOS_Upgrade_Downloads |
---|
| 10776 | + * See https://thinkwiki.org/wiki/List_of_DMI_IDs |
---|
| 10777 | + * See https://thinkwiki.org/wiki/BIOS_Upgrade_Downloads |
---|
10513 | 10778 | * |
---|
10514 | 10779 | * Only models listed in thinkwiki will be supported, so add yours |
---|
10515 | 10780 | * if it is not there yet. |
---|