From e636c8d336489bf3eed5878299e6cc045bbad077 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Tue, 20 Feb 2024 01:17:29 +0000
Subject: [PATCH] debug lk
---
kernel/drivers/platform/x86/thinkpad_acpi.c | 657 +++++++++++++++++++++++++++++++++++++++++-----------------
1 files changed, 461 insertions(+), 196 deletions(-)
diff --git a/kernel/drivers/platform/x86/thinkpad_acpi.c b/kernel/drivers/platform/x86/thinkpad_acpi.c
index 912ce5c..d8d2413 100644
--- a/kernel/drivers/platform/x86/thinkpad_acpi.c
+++ b/kernel/drivers/platform/x86/thinkpad_acpi.c
@@ -1,24 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* thinkpad_acpi.c - ThinkPad ACPI Extras
*
- *
* Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net>
* Copyright (C) 2006-2009 Henrique de Moraes Holschuh <hmh@hmh.eng.br>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -81,7 +66,6 @@
#include <linux/acpi.h>
#include <linux/pci.h>
#include <linux/power_supply.h>
-#include <linux/thinkpad_acpi.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/initval.h>
@@ -334,6 +318,7 @@
u32 uwb:1;
u32 fan_ctrl_status_undef:1;
u32 second_fan:1;
+ u32 second_fan_ctl:1;
u32 beep_needs_two_args:1;
u32 mixer_no_level_control:1;
u32 battery_force_primary:1;
@@ -476,6 +461,12 @@
{ .vendor = PCI_VENDOR_ID_LENOVO, \
.bios = TPID3(__id1, __id2, __id3), \
.ec = TPACPI_MATCH_ANY, \
+ .quirks = (__quirk) }
+
+#define TPACPI_QEC_IBM(__id1, __id2, __quirk) \
+ { .vendor = PCI_VENDOR_ID_IBM, \
+ .bios = TPACPI_MATCH_ANY, \
+ .ec = TPID(__id1, __id2), \
.quirks = (__quirk) }
#define TPACPI_QEC_LNV(__id1, __id2, __quirk) \
@@ -894,10 +885,10 @@
if (!ibm || !ibm->write)
return -EINVAL;
- if (count > PAGE_SIZE - 2)
+ if (count > PAGE_SIZE - 1)
return -EINVAL;
- kernbuf = kmalloc(count + 2, GFP_KERNEL);
+ kernbuf = kmalloc(count + 1, GFP_KERNEL);
if (!kernbuf)
return -ENOMEM;
@@ -907,7 +898,6 @@
}
kernbuf[count] = 0;
- strcat(kernbuf, ",");
ret = ibm->write(kernbuf);
if (ret == 0)
ret = count;
@@ -917,31 +907,13 @@
return ret;
}
-static const struct file_operations dispatch_proc_fops = {
- .owner = THIS_MODULE,
- .open = dispatch_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .write = dispatch_proc_write,
+static const struct proc_ops dispatch_proc_ops = {
+ .proc_open = dispatch_proc_open,
+ .proc_read = seq_read,
+ .proc_lseek = seq_lseek,
+ .proc_release = single_release,
+ .proc_write = dispatch_proc_write,
};
-
-static char *next_cmd(char **cmds)
-{
- char *start = *cmds;
- char *end;
-
- while ((end = strchr(start, ',')) && end == start)
- start = end + 1;
-
- if (!end)
- return NULL;
-
- *end = 0;
- *cmds = end + 1;
- return start;
-}
-
/****************************************************************************
****************************************************************************
@@ -1424,7 +1396,7 @@
if (id >= TPACPI_RFK_SW_MAX)
return -ENODEV;
- while ((cmd = next_cmd(&buf))) {
+ while ((cmd = strsep(&buf, ","))) {
if (strlencmp(cmd, "enable") == 0)
status = TPACPI_RFK_RADIO_ON;
else if (strlencmp(cmd, "disable") == 0)
@@ -1932,6 +1904,10 @@
TP_ACPI_HOTKEYSCAN_CALCULATOR,
TP_ACPI_HOTKEYSCAN_BLUETOOTH,
TP_ACPI_HOTKEYSCAN_KEYBOARD,
+ TP_ACPI_HOTKEYSCAN_FN_RIGHT_SHIFT, /* Used by "Lenovo Quick Clean" */
+ TP_ACPI_HOTKEYSCAN_NOTIFICATION_CENTER,
+ TP_ACPI_HOTKEYSCAN_PICKUP_PHONE,
+ TP_ACPI_HOTKEYSCAN_HANGUP_PHONE,
/* Hotkey keymap size */
TPACPI_HOTKEY_MAP_LEN
@@ -3452,11 +3428,15 @@
KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
KEY_UNKNOWN,
- KEY_FAVORITES, /* Favorite app, 0x311 */
- KEY_RESERVED, /* Clipping tool */
- KEY_CALC, /* Calculator (above numpad, P52) */
- KEY_BLUETOOTH, /* Bluetooth */
- KEY_KEYBOARD /* Keyboard, 0x315 */
+ KEY_BOOKMARKS, /* Favorite app, 0x311 */
+ KEY_SELECTIVE_SCREENSHOT, /* Clipping tool */
+ KEY_CALC, /* Calculator (above numpad, P52) */
+ KEY_BLUETOOTH, /* Bluetooth */
+ KEY_KEYBOARD, /* Keyboard, 0x315 */
+ KEY_FN_RIGHT_SHIFT, /* Fn + right Shift */
+ KEY_NOTIFICATION_CENTER, /* Notification Center */
+ KEY_PICKUP_PHONE, /* Answer incoming call */
+ KEY_HANGUP_PHONE, /* Decline incoming call */
},
};
@@ -3652,22 +3632,19 @@
goto err_exit;
/* Set up key map */
- hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE,
- GFP_KERNEL);
- if (!hotkey_keycode_map) {
- pr_err("failed to allocate memory for key map\n");
- res = -ENOMEM;
- goto err_exit;
- }
-
keymap_id = tpacpi_check_quirks(tpacpi_keymap_qtable,
ARRAY_SIZE(tpacpi_keymap_qtable));
BUG_ON(keymap_id >= ARRAY_SIZE(tpacpi_keymaps));
dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY,
"using keymap number %lu\n", keymap_id);
- memcpy(hotkey_keycode_map, &tpacpi_keymaps[keymap_id],
- TPACPI_HOTKEY_MAP_SIZE);
+ hotkey_keycode_map = kmemdup(&tpacpi_keymaps[keymap_id],
+ TPACPI_HOTKEY_MAP_SIZE, GFP_KERNEL);
+ if (!hotkey_keycode_map) {
+ pr_err("failed to allocate memory for key map\n");
+ res = -ENOMEM;
+ goto err_exit;
+ }
input_set_capability(tpacpi_inputdev, EV_MSC, MSC_SCAN);
tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE;
@@ -4056,8 +4033,8 @@
return true;
case TP_HKEY_EV_THM_CSM_COMPLETED:
pr_debug("EC reports: Thermal Control Command set completed (DYTC)\n");
- /* recommended action: do nothing, we don't have
- * Lenovo ATM information */
+ /* Thermal event - pass on to event handler */
+ tpacpi_driver_event(hkey);
return true;
case TP_HKEY_EV_THM_TRANSFM_CHANGED:
pr_debug("EC reports: Thermal Transformation changed (GMTS)\n");
@@ -4086,7 +4063,7 @@
* AC status changed; can be triggered by plugging or
* unplugging AC adapter, docking or undocking. */
- /* fallthrough */
+ fallthrough;
case TP_HKEY_EV_KEY_NUMLOCK:
case TP_HKEY_EV_KEY_FN:
@@ -4208,7 +4185,7 @@
known_ev = true;
break;
}
- /* fallthrough to default */
+ fallthrough; /* to default */
default:
known_ev = false;
}
@@ -4321,7 +4298,7 @@
mask = hotkey_user_mask;
res = 0;
- while ((cmd = next_cmd(&buf))) {
+ while ((cmd = strsep(&buf, ","))) {
if (strlencmp(cmd, "enable") == 0) {
hotkey_enabledisable_warn(1);
} else if (strlencmp(cmd, "disable") == 0) {
@@ -5248,7 +5225,7 @@
enable = 0;
disable = 0;
- while ((cmd = next_cmd(&buf))) {
+ while ((cmd = strsep(&buf, ","))) {
if (strlencmp(cmd, "lcd_enable") == 0) {
enable |= TP_ACPI_VIDEO_S_LCD;
} else if (strlencmp(cmd, "lcd_disable") == 0) {
@@ -5449,8 +5426,7 @@
static void kbdlight_exit(void)
{
- if (tp_features.kbdlight)
- led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev);
+ led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev);
}
static int kbdlight_set_level_and_update(int level)
@@ -5488,23 +5464,18 @@
static int kbdlight_write(char *buf)
{
char *cmd;
- int level = -1;
+ int res, level = -EINVAL;
if (!tp_features.kbdlight)
return -ENODEV;
- while ((cmd = next_cmd(&buf))) {
- if (strlencmp(cmd, "0") == 0)
- level = 0;
- else if (strlencmp(cmd, "1") == 0)
- level = 1;
- else if (strlencmp(cmd, "2") == 0)
- level = 2;
- else
- return -EINVAL;
+ while ((cmd = strsep(&buf, ","))) {
+ res = kstrtoint(cmd, 10, &level);
+ if (res < 0)
+ return res;
}
- if (level == -1)
+ if (level >= 3 || level < 0)
return -EINVAL;
return kbdlight_set_level_and_update(level);
@@ -5673,7 +5644,7 @@
if (!tp_features.light)
return -ENODEV;
- while ((cmd = next_cmd(&buf))) {
+ while ((cmd = strsep(&buf, ","))) {
if (strlencmp(cmd, "on") == 0) {
newstatus = 1;
} else if (strlencmp(cmd, "off") == 0) {
@@ -5758,7 +5729,7 @@
char *cmd;
int cmos_cmd, res;
- while ((cmd = next_cmd(&buf))) {
+ while ((cmd = strsep(&buf, ","))) {
if (sscanf(cmd, "%u", &cmos_cmd) == 1 &&
cmos_cmd >= 0 && cmos_cmd <= 21) {
/* cmos_cmd set */
@@ -5873,7 +5844,7 @@
return -EPERM;
if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
(1 << led), led_sled_arg1[ledstatus]))
- rc = -EIO;
+ return -EIO;
break;
case TPACPI_LED_OLD:
/* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
@@ -5897,10 +5868,10 @@
return -EPERM;
if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
led, led_led_arg1[ledstatus]))
- rc = -EIO;
+ return -EIO;
break;
default:
- rc = -ENXIO;
+ return -ENXIO;
}
if (!rc)
@@ -5964,20 +5935,14 @@
{
unsigned int i;
- for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
- if (tpacpi_leds[i].led_classdev.name)
- led_classdev_unregister(&tpacpi_leds[i].led_classdev);
- }
+ for (i = 0; i < TPACPI_LED_NUMLEDS; i++)
+ led_classdev_unregister(&tpacpi_leds[i].led_classdev);
kfree(tpacpi_leds);
}
static int __init tpacpi_init_led(unsigned int led)
{
- int rc;
-
- tpacpi_leds[led].led = led;
-
/* LEDs with no name don't get registered */
if (!tpacpi_led_names[led])
return 0;
@@ -5985,17 +5950,12 @@
tpacpi_leds[led].led_classdev.brightness_set_blocking = &led_sysfs_set;
tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set;
if (led_supported == TPACPI_LED_570)
- tpacpi_leds[led].led_classdev.brightness_get =
- &led_sysfs_get;
+ tpacpi_leds[led].led_classdev.brightness_get = &led_sysfs_get;
tpacpi_leds[led].led_classdev.name = tpacpi_led_names[led];
+ tpacpi_leds[led].led = led;
- rc = led_classdev_register(&tpacpi_pdev->dev,
- &tpacpi_leds[led].led_classdev);
- if (rc < 0)
- tpacpi_leds[led].led_classdev.name = NULL;
-
- return rc;
+ return led_classdev_register(&tpacpi_pdev->dev, &tpacpi_leds[led].led_classdev);
}
static const struct tpacpi_quirk led_useful_qtable[] __initconst = {
@@ -6042,9 +6002,6 @@
.quirks = 0x00bfU,
},
};
-
-#undef TPACPI_LEDQ_IBM
-#undef TPACPI_LEDQ_LNV
static enum led_access_mode __init led_init_detect_mode(void)
{
@@ -6108,8 +6065,7 @@
for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
tpacpi_leds[i].led = -1;
- if (!tpacpi_is_led_restricted(i) &&
- test_bit(i, &useful_leds)) {
+ if (!tpacpi_is_led_restricted(i) && test_bit(i, &useful_leds)) {
rc = tpacpi_init_led(i);
if (rc < 0) {
led_exit();
@@ -6162,12 +6118,14 @@
if (!led_supported)
return -ENODEV;
- while ((cmd = next_cmd(&buf))) {
+ while ((cmd = strsep(&buf, ","))) {
if (sscanf(cmd, "%d", &led) != 1)
return -EINVAL;
- if (led < 0 || led > (TPACPI_LED_NUMLEDS - 1) ||
- tpacpi_leds[led].led < 0)
+ if (led < 0 || led > (TPACPI_LED_NUMLEDS - 1))
+ return -ENODEV;
+
+ if (tpacpi_leds[led].led < 0)
return -ENODEV;
if (strstr(cmd, "off")) {
@@ -6247,7 +6205,7 @@
if (!beep_handle)
return -ENODEV;
- while ((cmd = next_cmd(&buf))) {
+ while ((cmd = strsep(&buf, ","))) {
if (sscanf(cmd, "%u", &beep_cmd) == 1 &&
beep_cmd >= 0 && beep_cmd <= 17) {
/* beep_cmd set */
@@ -6318,8 +6276,8 @@
t = TP_EC_THERMAL_TMP8;
idx -= 8;
}
- /* fallthrough */
#endif
+ fallthrough;
case TPACPI_THERMAL_TPEC_8:
if (idx <= 7) {
if (!acpi_ec_read(t + idx, &tmp))
@@ -7030,9 +6988,12 @@
pr_warn("Cannot enable backlight brightness support, ACPI is already handling it. Refer to the acpi_backlight kernel parameter.\n");
return 1;
}
- } else if (tp_features.bright_acpimode && brightness_enable > 1) {
- pr_notice("Standard ACPI backlight interface not available, thinkpad_acpi native brightness control enabled\n");
+ } else if (!tp_features.bright_acpimode) {
+ pr_notice("ACPI backlight interface not available\n");
+ return 1;
}
+
+ pr_notice("ACPI native brightness control enabled\n");
/*
* Check for module parameter bogosity, note that we
@@ -7150,7 +7111,7 @@
if (level < 0)
return level;
- while ((cmd = next_cmd(&buf))) {
+ while ((cmd = strsep(&buf, ","))) {
if (strlencmp(cmd, "up") == 0) {
if (level < bright_maxlvl)
level++;
@@ -7902,7 +7863,7 @@
new_level = s & TP_EC_AUDIO_LVL_MSK;
new_mute = s & TP_EC_AUDIO_MUTESW_MSK;
- while ((cmd = next_cmd(&buf))) {
+ while ((cmd = strsep(&buf, ","))) {
if (!tp_features.mixer_no_level_control) {
if (strlencmp(cmd, "up") == 0) {
if (new_mute)
@@ -8032,7 +7993,7 @@
* does so, its initial value is meaningless (0x07).
*
* For firmware bugs, refer to:
- * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
+ * https://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
*
* ----
*
@@ -8057,7 +8018,7 @@
* mode.
*
* For firmware bugs, refer to:
- * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
+ * https://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
*
* ----
*
@@ -8358,11 +8319,19 @@
switch (fan_control_access_mode) {
case TPACPI_FAN_WR_ACPI_SFAN:
- if (level >= 0 && level <= 7) {
- if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
- return -EIO;
- } else
+ if ((level < 0) || (level > 7))
return -EINVAL;
+
+ if (tp_features.second_fan_ctl) {
+ if (!fan_select_fan2() ||
+ !acpi_evalf(sfan_handle, NULL, NULL, "vd", level)) {
+ pr_warn("Couldn't set 2nd fan level, disabling support\n");
+ tp_features.second_fan_ctl = 0;
+ }
+ fan_select_fan1();
+ }
+ if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
+ return -EIO;
break;
case TPACPI_FAN_WR_ACPI_FANS:
@@ -8379,6 +8348,15 @@
else if (level & TP_EC_FAN_AUTO)
level |= 4; /* safety min speed 4 */
+ if (tp_features.second_fan_ctl) {
+ if (!fan_select_fan2() ||
+ !acpi_ec_write(fan_status_offset, level)) {
+ pr_warn("Couldn't set 2nd fan level, disabling support\n");
+ tp_features.second_fan_ctl = 0;
+ }
+ fan_select_fan1();
+
+ }
if (!acpi_ec_write(fan_status_offset, level))
return -EIO;
else
@@ -8795,39 +8773,30 @@
.attrs = fan_attributes,
};
-#define TPACPI_FAN_Q1 0x0001 /* Unitialized HFSP */
+#define TPACPI_FAN_Q1 0x0001 /* Unitialized HFSP */
#define TPACPI_FAN_2FAN 0x0002 /* EC 0x31 bit 0 selects fan2 */
-
-#define TPACPI_FAN_QI(__id1, __id2, __quirks) \
- { .vendor = PCI_VENDOR_ID_IBM, \
- .bios = TPACPI_MATCH_ANY, \
- .ec = TPID(__id1, __id2), \
- .quirks = __quirks }
-
-#define TPACPI_FAN_QL(__id1, __id2, __quirks) \
- { .vendor = PCI_VENDOR_ID_LENOVO, \
- .bios = TPACPI_MATCH_ANY, \
- .ec = TPID(__id1, __id2), \
- .quirks = __quirks }
-
-#define TPACPI_FAN_QB(__id1, __id2, __quirks) \
- { .vendor = PCI_VENDOR_ID_LENOVO, \
- .bios = TPID(__id1, __id2), \
- .ec = TPACPI_MATCH_ANY, \
- .quirks = __quirks }
+#define TPACPI_FAN_2CTL 0x0004 /* selects fan2 control */
static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
- TPACPI_FAN_QI('1', 'Y', TPACPI_FAN_Q1),
- TPACPI_FAN_QI('7', '8', TPACPI_FAN_Q1),
- TPACPI_FAN_QI('7', '6', TPACPI_FAN_Q1),
- TPACPI_FAN_QI('7', '0', TPACPI_FAN_Q1),
- TPACPI_FAN_QL('7', 'M', TPACPI_FAN_2FAN),
- TPACPI_FAN_QB('N', '1', TPACPI_FAN_2FAN),
+ TPACPI_QEC_IBM('1', 'Y', TPACPI_FAN_Q1),
+ TPACPI_QEC_IBM('7', '8', TPACPI_FAN_Q1),
+ TPACPI_QEC_IBM('7', '6', TPACPI_FAN_Q1),
+ TPACPI_QEC_IBM('7', '0', TPACPI_FAN_Q1),
+ TPACPI_QEC_LNV('7', 'M', TPACPI_FAN_2FAN),
+ TPACPI_Q_LNV('N', '1', TPACPI_FAN_2FAN),
+ TPACPI_Q_LNV3('N', '1', 'D', TPACPI_FAN_2CTL), /* P70 */
+ TPACPI_Q_LNV3('N', '1', 'E', TPACPI_FAN_2CTL), /* P50 */
+ TPACPI_Q_LNV3('N', '1', 'T', TPACPI_FAN_2CTL), /* P71 */
+ TPACPI_Q_LNV3('N', '1', 'U', TPACPI_FAN_2CTL), /* P51 */
+ TPACPI_Q_LNV3('N', '2', 'C', TPACPI_FAN_2CTL), /* P52 / P72 */
+ TPACPI_Q_LNV3('N', '2', 'N', TPACPI_FAN_2CTL), /* P53 / P73 */
+ TPACPI_Q_LNV3('N', '2', 'E', TPACPI_FAN_2CTL), /* P1 / X1 Extreme (1st gen) */
+ TPACPI_Q_LNV3('N', '2', 'O', TPACPI_FAN_2CTL), /* P1 / X1 Extreme (2nd gen) */
+ TPACPI_Q_LNV3('N', '2', 'V', TPACPI_FAN_2CTL), /* P1 / X1 Extreme (3nd gen) */
+ TPACPI_Q_LNV3('N', '4', '0', TPACPI_FAN_2CTL), /* P1 / X1 Extreme (4nd gen) */
+ TPACPI_Q_LNV3('N', '3', '0', TPACPI_FAN_2CTL), /* P15 (1st gen) / P15v (1st gen) */
+ TPACPI_Q_LNV3('N', '3', '2', TPACPI_FAN_2CTL), /* X1 Carbon (9th gen) */
};
-
-#undef TPACPI_FAN_QL
-#undef TPACPI_FAN_QI
-#undef TPACPI_FAN_QB
static int __init fan_init(struct ibm_init_struct *iibm)
{
@@ -8844,6 +8813,7 @@
fan_watchdog_maxinterval = 0;
tp_features.fan_ctrl_status_undef = 0;
tp_features.second_fan = 0;
+ tp_features.second_fan_ctl = 0;
fan_control_desired_level = 7;
if (tpacpi_is_ibm()) {
@@ -8868,8 +8838,12 @@
fan_quirk1_setup();
if (quirks & TPACPI_FAN_2FAN) {
tp_features.second_fan = 1;
- dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN,
- "secondary fan support enabled\n");
+ pr_info("secondary fan support enabled\n");
+ }
+ if (quirks & TPACPI_FAN_2CTL) {
+ tp_features.second_fan = 1;
+ tp_features.second_fan_ctl = 1;
+ pr_info("secondary fan control enabled\n");
}
} else {
pr_err("ThinkPad ACPI EC access misbehaving, fan status and control unavailable\n");
@@ -9204,7 +9178,7 @@
char *cmd;
int rc = 0;
- while (!rc && (cmd = next_cmd(&buf))) {
+ while (!rc && (cmd = strsep(&buf, ","))) {
if (!((fan_control_commands & TPACPI_FAN_CMD_LEVEL) &&
fan_write_cmd_level(cmd, &rc)) &&
!((fan_control_commands & TPACPI_FAN_CMD_ENABLE) &&
@@ -9235,6 +9209,7 @@
* Mute LED subdriver
*/
+#define TPACPI_LED_MAX 2
struct tp_led_table {
acpi_string name;
@@ -9243,13 +9218,13 @@
int state;
};
-static struct tp_led_table led_tables[] = {
- [TPACPI_LED_MUTE] = {
+static struct tp_led_table led_tables[TPACPI_LED_MAX] = {
+ [LED_AUDIO_MUTE] = {
.name = "SSMS",
.on_value = 1,
.off_value = 0,
},
- [TPACPI_LED_MICMUTE] = {
+ [LED_AUDIO_MICMUTE] = {
.name = "MMTS",
.on_value = 2,
.off_value = 0,
@@ -9274,31 +9249,62 @@
return state;
}
-int tpacpi_led_set(int whichled, bool on)
+static int tpacpi_led_set(int whichled, bool on)
{
struct tp_led_table *t;
-
- if (whichled < 0 || whichled >= TPACPI_LED_MAX)
- return -EINVAL;
t = &led_tables[whichled];
if (t->state < 0 || t->state == on)
return t->state;
return mute_led_on_off(t, on);
}
-EXPORT_SYMBOL_GPL(tpacpi_led_set);
+
+static int tpacpi_led_mute_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ return tpacpi_led_set(LED_AUDIO_MUTE, brightness != LED_OFF);
+}
+
+static int tpacpi_led_micmute_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ return tpacpi_led_set(LED_AUDIO_MICMUTE, brightness != LED_OFF);
+}
+
+static struct led_classdev mute_led_cdev[TPACPI_LED_MAX] = {
+ [LED_AUDIO_MUTE] = {
+ .name = "platform::mute",
+ .max_brightness = 1,
+ .brightness_set_blocking = tpacpi_led_mute_set,
+ .default_trigger = "audio-mute",
+ },
+ [LED_AUDIO_MICMUTE] = {
+ .name = "platform::micmute",
+ .max_brightness = 1,
+ .brightness_set_blocking = tpacpi_led_micmute_set,
+ .default_trigger = "audio-micmute",
+ },
+};
static int mute_led_init(struct ibm_init_struct *iibm)
{
acpi_handle temp;
- int i;
+ int i, err;
for (i = 0; i < TPACPI_LED_MAX; i++) {
struct tp_led_table *t = &led_tables[i];
- if (ACPI_SUCCESS(acpi_get_handle(hkey_handle, t->name, &temp)))
- mute_led_on_off(t, false);
- else
+ if (ACPI_FAILURE(acpi_get_handle(hkey_handle, t->name, &temp))) {
t->state = -ENODEV;
+ continue;
+ }
+
+ mute_led_cdev[i].brightness = ledtrig_audio_get(i);
+ err = led_classdev_register(&tpacpi_pdev->dev, &mute_led_cdev[i]);
+ if (err < 0) {
+ while (i--)
+ led_classdev_unregister(&mute_led_cdev[i]);
+ return err;
+ }
}
return 0;
}
@@ -9307,8 +9313,10 @@
{
int i;
- for (i = 0; i < TPACPI_LED_MAX; i++)
+ for (i = 0; i < TPACPI_LED_MAX; i++) {
+ led_classdev_unregister(&mute_led_cdev[i]);
tpacpi_led_set(i, false);
+ }
}
static void mute_led_resume(void)
@@ -9339,9 +9347,6 @@
#define SET_START "BCCS"
#define GET_STOP "BCSG"
#define SET_STOP "BCSS"
-
-#define START_ATTR "charge_start_threshold"
-#define STOP_ATTR "charge_stop_threshold"
enum {
BAT_ANY = 0,
@@ -9566,7 +9571,7 @@
if (!battery_info.batteries[battery].start_support)
return -ENODEV;
/* valid values are [0, 99] */
- if (value < 0 || value > 99)
+ if (value > 99)
return -EINVAL;
if (value > battery_info.batteries[battery].charge_stop)
return -EINVAL;
@@ -9628,38 +9633,52 @@
return sprintf(buf, "%d\n", ret);
}
-static ssize_t charge_start_threshold_show(struct device *device,
+static ssize_t charge_control_start_threshold_show(struct device *device,
struct device_attribute *attr,
char *buf)
{
return tpacpi_battery_show(THRESHOLD_START, device, buf);
}
-static ssize_t charge_stop_threshold_show(struct device *device,
+static ssize_t charge_control_end_threshold_show(struct device *device,
struct device_attribute *attr,
char *buf)
{
return tpacpi_battery_show(THRESHOLD_STOP, device, buf);
}
-static ssize_t charge_start_threshold_store(struct device *dev,
+static ssize_t charge_control_start_threshold_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
return tpacpi_battery_store(THRESHOLD_START, dev, buf, count);
}
-static ssize_t charge_stop_threshold_store(struct device *dev,
+static ssize_t charge_control_end_threshold_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
return tpacpi_battery_store(THRESHOLD_STOP, dev, buf, count);
}
-static DEVICE_ATTR_RW(charge_start_threshold);
-static DEVICE_ATTR_RW(charge_stop_threshold);
+static DEVICE_ATTR_RW(charge_control_start_threshold);
+static DEVICE_ATTR_RW(charge_control_end_threshold);
+static struct device_attribute dev_attr_charge_start_threshold = __ATTR(
+ charge_start_threshold,
+ 0644,
+ charge_control_start_threshold_show,
+ charge_control_start_threshold_store
+);
+static struct device_attribute dev_attr_charge_stop_threshold = __ATTR(
+ charge_stop_threshold,
+ 0644,
+ charge_control_end_threshold_show,
+ charge_control_end_threshold_store
+);
static struct attribute *tpacpi_battery_attrs[] = {
+ &dev_attr_charge_control_start_threshold.attr,
+ &dev_attr_charge_control_end_threshold.attr,
&dev_attr_charge_start_threshold.attr,
&dev_attr_charge_stop_threshold.attr,
NULL,
@@ -9729,6 +9748,205 @@
.exit = tpacpi_battery_exit,
};
+/*************************************************************************
+ * LCD Shadow subdriver, for the Lenovo PrivacyGuard feature
+ */
+
+static int lcdshadow_state;
+
+static int lcdshadow_on_off(bool state)
+{
+ acpi_handle set_shadow_handle;
+ int output;
+
+ if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "SSSS", &set_shadow_handle))) {
+ pr_warn("Thinkpad ACPI has no %s interface.\n", "SSSS");
+ return -EIO;
+ }
+
+ if (!acpi_evalf(set_shadow_handle, &output, NULL, "dd", (int)state))
+ return -EIO;
+
+ lcdshadow_state = state;
+ return 0;
+}
+
+static int lcdshadow_set(bool on)
+{
+ if (lcdshadow_state < 0)
+ return lcdshadow_state;
+ if (lcdshadow_state == on)
+ return 0;
+ return lcdshadow_on_off(on);
+}
+
+static int tpacpi_lcdshadow_init(struct ibm_init_struct *iibm)
+{
+ acpi_handle get_shadow_handle;
+ int output;
+
+ if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "GSSS", &get_shadow_handle))) {
+ lcdshadow_state = -ENODEV;
+ return 0;
+ }
+
+ if (!acpi_evalf(get_shadow_handle, &output, NULL, "dd", 0)) {
+ lcdshadow_state = -EIO;
+ return -EIO;
+ }
+ if (!(output & 0x10000)) {
+ lcdshadow_state = -ENODEV;
+ return 0;
+ }
+ lcdshadow_state = output & 0x1;
+
+ return 0;
+}
+
+static void lcdshadow_resume(void)
+{
+ if (lcdshadow_state >= 0)
+ lcdshadow_on_off(lcdshadow_state);
+}
+
+static int lcdshadow_read(struct seq_file *m)
+{
+ if (lcdshadow_state < 0) {
+ seq_puts(m, "status:\t\tnot supported\n");
+ } else {
+ seq_printf(m, "status:\t\t%d\n", lcdshadow_state);
+ seq_puts(m, "commands:\t0, 1\n");
+ }
+
+ return 0;
+}
+
+static int lcdshadow_write(char *buf)
+{
+ char *cmd;
+ int res, state = -EINVAL;
+
+ if (lcdshadow_state < 0)
+ return -ENODEV;
+
+ while ((cmd = strsep(&buf, ","))) {
+ res = kstrtoint(cmd, 10, &state);
+ if (res < 0)
+ return res;
+ }
+
+ if (state >= 2 || state < 0)
+ return -EINVAL;
+
+ return lcdshadow_set(state);
+}
+
+static struct ibm_struct lcdshadow_driver_data = {
+ .name = "lcdshadow",
+ .resume = lcdshadow_resume,
+ .read = lcdshadow_read,
+ .write = lcdshadow_write,
+};
+
+/*************************************************************************
+ * DYTC subdriver, for the Lenovo lapmode feature
+ */
+
+#define DYTC_CMD_GET 2 /* To get current IC function and mode */
+#define DYTC_GET_LAPMODE_BIT 17 /* Set when in lapmode */
+
+static bool dytc_lapmode;
+
+static void dytc_lapmode_notify_change(void)
+{
+ sysfs_notify(&tpacpi_pdev->dev.kobj, NULL, "dytc_lapmode");
+}
+
+static int dytc_command(int command, int *output)
+{
+ acpi_handle dytc_handle;
+
+ if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "DYTC", &dytc_handle))) {
+ /* Platform doesn't support DYTC */
+ return -ENODEV;
+ }
+ if (!acpi_evalf(dytc_handle, output, NULL, "dd", command))
+ return -EIO;
+ return 0;
+}
+
+static int dytc_lapmode_get(bool *state)
+{
+ int output, err;
+
+ err = dytc_command(DYTC_CMD_GET, &output);
+ if (err)
+ return err;
+ *state = output & BIT(DYTC_GET_LAPMODE_BIT) ? true : false;
+ return 0;
+}
+
+static void dytc_lapmode_refresh(void)
+{
+ bool new_state;
+ int err;
+
+ err = dytc_lapmode_get(&new_state);
+ if (err || (new_state == dytc_lapmode))
+ return;
+
+ dytc_lapmode = new_state;
+ dytc_lapmode_notify_change();
+}
+
+/* sysfs lapmode entry */
+static ssize_t dytc_lapmode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", dytc_lapmode);
+}
+
+static DEVICE_ATTR_RO(dytc_lapmode);
+
+static struct attribute *dytc_attributes[] = {
+ &dev_attr_dytc_lapmode.attr,
+ NULL,
+};
+
+static const struct attribute_group dytc_attr_group = {
+ .attrs = dytc_attributes,
+};
+
+static int tpacpi_dytc_init(struct ibm_init_struct *iibm)
+{
+ int err;
+
+ err = dytc_lapmode_get(&dytc_lapmode);
+ /* If support isn't available (ENODEV) then don't return an error
+ * but just don't create the sysfs group
+ */
+ if (err == -ENODEV)
+ return 0;
+ /* For all other errors we can flag the failure */
+ if (err)
+ return err;
+
+ /* Platform supports this feature - create the group */
+ err = sysfs_create_group(&tpacpi_pdev->dev.kobj, &dytc_attr_group);
+ return err;
+}
+
+static void dytc_exit(void)
+{
+ sysfs_remove_group(&tpacpi_pdev->dev.kobj, &dytc_attr_group);
+}
+
+static struct ibm_struct dytc_driver_data = {
+ .name = "dytc",
+ .exit = dytc_exit,
+};
+
/****************************************************************************
****************************************************************************
*
@@ -9776,6 +9994,10 @@
mutex_unlock(&kbdlight_mutex);
}
+
+ if (hkey_event == TP_HKEY_EV_THM_CSM_COMPLETED)
+ dytc_lapmode_refresh();
+
}
static void hotkey_driver_event(const unsigned int scancode)
@@ -9901,7 +10123,7 @@
if (ibm->write)
mode |= S_IWUSR;
entry = proc_create_data(ibm->name, mode, proc_dir,
- &dispatch_proc_fops, ibm);
+ &dispatch_proc_ops, ibm);
if (!entry) {
pr_err("unable to create proc entry %s\n", ibm->name);
ret = -ENODEV;
@@ -9958,6 +10180,37 @@
return '\0';
}
+static void find_new_ec_fwstr(const struct dmi_header *dm, void *private)
+{
+ char *ec_fw_string = (char *) private;
+ const char *dmi_data = (const char *)dm;
+ /*
+ * ThinkPad Embedded Controller Program Table on newer models
+ *
+ * Offset | Name | Width | Description
+ * ----------------------------------------------------
+ * 0x00 | Type | BYTE | 0x8C
+ * 0x01 | Length | BYTE |
+ * 0x02 | Handle | WORD | Varies
+ * 0x04 | Signature | BYTEx6 | ASCII for "LENOVO"
+ * 0x0A | OEM struct offset | BYTE | 0x0B
+ * 0x0B | OEM struct number | BYTE | 0x07, for this structure
+ * 0x0C | OEM struct revision | BYTE | 0x01, for this format
+ * 0x0D | ECP version ID | STR ID |
+ * 0x0E | ECP release date | STR ID |
+ */
+
+ /* Return if data structure not match */
+ if (dm->type != 140 || dm->length < 0x0F ||
+ memcmp(dmi_data + 4, "LENOVO", 6) != 0 ||
+ dmi_data[0x0A] != 0x0B || dmi_data[0x0B] != 0x07 ||
+ dmi_data[0x0C] != 0x01)
+ return;
+
+ /* fwstr is the first 8byte string */
+ strncpy(ec_fw_string, dmi_data + 0x0F, 8);
+}
+
/* returns 0 - probe ok, or < 0 - probe error.
* Probe ok doesn't mean thinkpad found.
* On error, kfree() cleanup on tp->* is not performed, caller must do it */
@@ -9965,7 +10218,7 @@
struct thinkpad_id_data *tp)
{
const struct dmi_device *dev = NULL;
- char ec_fw_string[18];
+ char ec_fw_string[18] = {0};
char const *s;
char t;
@@ -9997,7 +10250,7 @@
* X32 or newer, all Z series; Some models must have an
* up-to-date BIOS or they will not be detected.
*
- * See http://thinkwiki.org/wiki/List_of_DMI_IDs
+ * See https://thinkwiki.org/wiki/List_of_DMI_IDs
*/
while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
if (sscanf(dev->name,
@@ -10005,20 +10258,25 @@
ec_fw_string) == 1) {
ec_fw_string[sizeof(ec_fw_string) - 1] = 0;
ec_fw_string[strcspn(ec_fw_string, " ]")] = 0;
-
- tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL);
- if (!tp->ec_version_str)
- return -ENOMEM;
-
- t = tpacpi_parse_fw_id(ec_fw_string,
- &tp->ec_model, &tp->ec_release);
- if (t != 'H') {
- pr_notice("ThinkPad firmware release %s doesn't match the known patterns\n",
- ec_fw_string);
- pr_notice("please report this to %s\n",
- TPACPI_MAIL);
- }
break;
+ }
+ }
+
+ /* Newer ThinkPads have different EC program info table */
+ if (!ec_fw_string[0])
+ dmi_walk(find_new_ec_fwstr, &ec_fw_string);
+
+ if (ec_fw_string[0]) {
+ tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL);
+ if (!tp->ec_version_str)
+ return -ENOMEM;
+
+ t = tpacpi_parse_fw_id(ec_fw_string,
+ &tp->ec_model, &tp->ec_release);
+ if (t != 'H') {
+ pr_notice("ThinkPad firmware release %s doesn't match the known patterns\n",
+ ec_fw_string);
+ pr_notice("please report this to %s\n", TPACPI_MAIL);
}
}
@@ -10174,6 +10432,14 @@
.init = tpacpi_battery_init,
.data = &battery_driver_data,
},
+ {
+ .init = tpacpi_lcdshadow_init,
+ .data = &lcdshadow_driver_data,
+ },
+ {
+ .init = tpacpi_dytc_init,
+ .data = &dytc_driver_data,
+ },
};
static int __init set_ibm_param(const char *val, const struct kernel_param *kp)
@@ -10192,10 +10458,9 @@
continue;
if (strcmp(ibm->name, kp->name) == 0 && ibm->write) {
- if (strlen(val) > sizeof(ibms_init[i].param) - 2)
+ if (strlen(val) > sizeof(ibms_init[i].param) - 1)
return -ENOSPC;
strcpy(ibms_init[i].param, val);
- strcat(ibms_init[i].param, ",");
return 0;
}
}
@@ -10233,7 +10498,7 @@
module_param_named(volume_capabilities, volume_capabilities, uint, 0444);
MODULE_PARM_DESC(volume_capabilities,
- "Selects the mixer capabilites: 0=auto, 1=volume and mute, 2=mute only");
+ "Selects the mixer capabilities: 0=auto, 1=volume and mute, 2=mute only");
module_param_named(volume_control, volume_control_allowed, bool, 0444);
MODULE_PARM_DESC(volume_control,
@@ -10508,8 +10773,8 @@
/*
* DMI matching for module autoloading
*
- * See http://thinkwiki.org/wiki/List_of_DMI_IDs
- * See http://thinkwiki.org/wiki/BIOS_Upgrade_Downloads
+ * See https://thinkwiki.org/wiki/List_of_DMI_IDs
+ * See https://thinkwiki.org/wiki/BIOS_Upgrade_Downloads
*
* Only models listed in thinkwiki will be supported, so add yours
* if it is not there yet.
--
Gitblit v1.6.2