aboutsummaryrefslogtreecommitdiff
path: root/drivers/platform/x86/thinkpad_acpi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86/thinkpad_acpi.c')
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c1943
1 files changed, 1124 insertions, 819 deletions
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index e67e4feb35c..d82f196e3cf 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -21,7 +21,9 @@
* 02110-1301, USA.
*/
-#define TPACPI_VERSION "0.24"
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define TPACPI_VERSION "0.25"
#define TPACPI_SYSFS_VERSION 0x020700
/*
@@ -58,7 +60,7 @@
#include <linux/kthread.h>
#include <linux/freezer.h>
#include <linux/delay.h>
-
+#include <linux/slab.h>
#include <linux/nvram.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
@@ -71,20 +73,16 @@
#include <linux/input.h>
#include <linux/leds.h>
#include <linux/rfkill.h>
-#include <asm/uaccess.h>
-
#include <linux/dmi.h>
#include <linux/jiffies.h>
#include <linux/workqueue.h>
-
+#include <linux/acpi.h>
+#include <linux/pci_ids.h>
+#include <linux/thinkpad_acpi.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/initval.h>
-
-#include <acpi/acpi_drivers.h>
-
-#include <linux/pci_ids.h>
-
+#include <asm/uaccess.h>
/* ThinkPad CMOS commands */
#define TP_CMOS_VOLUME_DOWN 0
@@ -121,8 +119,15 @@ enum {
TP_NVRAM_POS_LEVEL_VOLUME = 0,
};
+/* Misc NVRAM-related */
+enum {
+ TP_NVRAM_LEVEL_VOLUME_MAX = 14,
+};
+
/* ACPI HIDs */
-#define TPACPI_ACPI_HKEY_HID "IBM0068"
+#define TPACPI_ACPI_IBM_HKEY_HID "IBM0068"
+#define TPACPI_ACPI_LENOVO_HKEY_HID "LEN0068"
+#define TPACPI_ACPI_EC_HID "PNP0C09"
/* Input IDs */
#define TPACPI_HKEY_INPUT_PRODUCT 0x5054 /* "TP" */
@@ -174,6 +179,10 @@ enum tpacpi_hkey_event_t {
/* Misc bay events */
TP_HKEY_EV_OPTDRV_EJ = 0x3006, /* opt. drive tray ejected */
+ TP_HKEY_EV_HOTPLUG_DOCK = 0x4010, /* docked into hotplug dock
+ or port replicator */
+ TP_HKEY_EV_HOTPLUG_UNDOCK = 0x4011, /* undocked from hotplug
+ dock or port replicator */
/* User-interface events */
TP_HKEY_EV_LID_CLOSE = 0x5001, /* laptop lid closed */
@@ -184,6 +193,10 @@ enum tpacpi_hkey_event_t {
TP_HKEY_EV_PEN_REMOVED = 0x500c, /* tablet pen removed */
TP_HKEY_EV_BRGHT_CHANGED = 0x5010, /* backlight control event */
+ /* Key-related user-interface events */
+ TP_HKEY_EV_KEY_NUMLOCK = 0x6000, /* NumLock key pressed */
+ TP_HKEY_EV_KEY_FN = 0x6005, /* Fn key pressed? E420 */
+
/* Thermal events */
TP_HKEY_EV_ALARM_BAT_HOT = 0x6011, /* battery too hot */
TP_HKEY_EV_ALARM_BAT_XHOT = 0x6012, /* battery critically hot */
@@ -191,6 +204,9 @@ enum tpacpi_hkey_event_t {
TP_HKEY_EV_ALARM_SENSOR_XHOT = 0x6022, /* sensor critically hot */
TP_HKEY_EV_THM_TABLE_CHANGED = 0x6030, /* thermal table changed */
+ /* AC-related events */
+ TP_HKEY_EV_AC_CHANGED = 0x6040, /* AC status changed */
+
/* Misc */
TP_HKEY_EV_RFKILL_CHANGED = 0x7000, /* rfkill switch changed */
};
@@ -216,17 +232,6 @@ enum tpacpi_hkey_event_t {
#define TPACPI_MAX_ACPI_ARGS 3
-/* printk headers */
-#define TPACPI_LOG TPACPI_FILE ": "
-#define TPACPI_EMERG KERN_EMERG TPACPI_LOG
-#define TPACPI_ALERT KERN_ALERT TPACPI_LOG
-#define TPACPI_CRIT KERN_CRIT TPACPI_LOG
-#define TPACPI_ERR KERN_ERR TPACPI_LOG
-#define TPACPI_WARN KERN_WARNING TPACPI_LOG
-#define TPACPI_NOTICE KERN_NOTICE TPACPI_LOG
-#define TPACPI_INFO KERN_INFO TPACPI_LOG
-#define TPACPI_DEBUG KERN_DEBUG TPACPI_LOG
-
/* Debugging printk groups */
#define TPACPI_DBG_ALL 0xffff
#define TPACPI_DBG_DISCLOSETASK 0x8000
@@ -266,7 +271,7 @@ struct ibm_struct {
int (*write) (char *);
void (*exit) (void);
void (*resume) (void);
- void (*suspend) (pm_message_t state);
+ void (*suspend) (void);
void (*shutdown) (void);
struct list_head all_drivers;
@@ -286,6 +291,7 @@ struct ibm_init_struct {
char param[32];
int (*init) (struct ibm_init_struct *);
+ umode_t base_procfs_mode;
struct ibm_struct *data;
};
@@ -297,8 +303,8 @@ static struct {
u32 hotkey_tablet:1;
u32 light:1;
u32 light_status:1;
- u32 bright_16levels:1;
u32 bright_acpimode:1;
+ u32 bright_unkfw:1;
u32 wan:1;
u32 uwb:1;
u32 fan_ctrl_status_undef:1;
@@ -358,18 +364,21 @@ struct tpacpi_led_classdev {
struct led_classdev led_classdev;
struct work_struct work;
enum led_status_t new_state;
- unsigned int led;
+ int led;
};
+/* brightness level capabilities */
+static unsigned int bright_maxlvl; /* 0 = unknown */
+
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
static int dbg_wlswemul;
-static int tpacpi_wlsw_emulstate;
+static bool tpacpi_wlsw_emulstate;
static int dbg_bluetoothemul;
-static int tpacpi_bluetooth_emulstate;
+static bool tpacpi_bluetooth_emulstate;
static int dbg_wwanemul;
-static int tpacpi_wwan_emulstate;
+static bool tpacpi_wwan_emulstate;
static int dbg_uwbemul;
-static int tpacpi_uwb_emulstate;
+static bool tpacpi_uwb_emulstate;
#endif
@@ -377,34 +386,36 @@ static int tpacpi_uwb_emulstate;
* Debugging helpers
*/
-#define dbg_printk(a_dbg_level, format, arg...) \
- do { if (dbg_level & (a_dbg_level)) \
- printk(TPACPI_DEBUG "%s: " format, __func__ , ## arg); \
- } while (0)
+#define dbg_printk(a_dbg_level, format, arg...) \
+do { \
+ if (dbg_level & (a_dbg_level)) \
+ printk(KERN_DEBUG pr_fmt("%s: " format), \
+ __func__, ##arg); \
+} while (0)
#ifdef CONFIG_THINKPAD_ACPI_DEBUG
#define vdbg_printk dbg_printk
static const char *str_supported(int is_supported);
#else
-#define vdbg_printk(a_dbg_level, format, arg...) \
- do { } while (0)
+static inline const char *str_supported(int is_supported) { return ""; }
+#define vdbg_printk(a_dbg_level, format, arg...) \
+ no_printk(format, ##arg)
#endif
static void tpacpi_log_usertask(const char * const what)
{
- printk(TPACPI_DEBUG "%s: access by process with PID %d\n",
- what, task_tgid_vnr(current));
+ printk(KERN_DEBUG pr_fmt("%s: access by process with PID %d\n"),
+ what, task_tgid_vnr(current));
}
-#define tpacpi_disclose_usertask(what, format, arg...) \
- do { \
- if (unlikely( \
- (dbg_level & TPACPI_DBG_DISCLOSETASK) && \
- (tpacpi_lifecycle == TPACPI_LIFE_RUNNING))) { \
- printk(TPACPI_DEBUG "%s: PID %d: " format, \
- what, task_tgid_vnr(current), ## arg); \
- } \
- } while (0)
+#define tpacpi_disclose_usertask(what, format, arg...) \
+do { \
+ if (unlikely((dbg_level & TPACPI_DBG_DISCLOSETASK) && \
+ (tpacpi_lifecycle == TPACPI_LIFE_RUNNING))) { \
+ printk(KERN_DEBUG pr_fmt("%s: PID %d: " format), \
+ what, task_tgid_vnr(current), ## arg); \
+ } \
+} while (0)
/*
* Quirk handling helpers
@@ -478,6 +489,15 @@ static unsigned long __init tpacpi_check_quirks(
return 0;
}
+static inline bool __pure __init tpacpi_is_lenovo(void)
+{
+ return thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO;
+}
+
+static inline bool __pure __init tpacpi_is_ibm(void)
+{
+ return thinkpad_id.vendor == PCI_VENDOR_ID_IBM;
+}
/****************************************************************************
****************************************************************************
@@ -492,21 +512,13 @@ static unsigned long __init tpacpi_check_quirks(
*/
static acpi_handle root_handle;
+static acpi_handle ec_handle;
#define TPACPI_HANDLE(object, parent, paths...) \
static acpi_handle object##_handle; \
- static acpi_handle *object##_parent = &parent##_handle; \
- static char *object##_path; \
- static char *object##_paths[] = { paths }
-
-TPACPI_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */
- "\\_SB.PCI.ISA.EC", /* 570 */
- "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */
- "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */
- "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */
- "\\_SB.PCI0.ICH3.EC0", /* R31 */
- "\\_SB.PCI0.LPC.EC", /* all others */
- );
+ static const acpi_handle * const object##_parent __initconst = \
+ &parent##_handle; \
+ static char *object##_paths[] __initdata = { paths }
TPACPI_HANDLE(ecrd, ec, "ECRD"); /* 570 */
TPACPI_HANDLE(ecwr, ec, "ECWR"); /* 570 */
@@ -522,20 +534,12 @@ TPACPI_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */
"HKEY", /* all others */
); /* 570 */
-TPACPI_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */
- "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */
- "\\_SB.PCI0.VID0", /* 770e */
- "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */
- "\\_SB.PCI0.AGP.VID", /* all others */
- ); /* R30, R31 */
-
-
/*************************************************************************
* ACPI helpers
*/
static int acpi_evalf(acpi_handle handle,
- void *res, char *method, char *fmt, ...)
+ int *res, char *method, char *fmt, ...)
{
char *fmt0 = fmt;
struct acpi_object_list params;
@@ -549,7 +553,7 @@ static int acpi_evalf(acpi_handle handle,
int quiet;
if (!*fmt) {
- printk(TPACPI_ERR "acpi_evalf() called with empty format\n");
+ pr_err("acpi_evalf() called with empty format\n");
return 0;
}
@@ -574,8 +578,9 @@ static int acpi_evalf(acpi_handle handle,
break;
/* add more types as needed */
default:
- printk(TPACPI_ERR "acpi_evalf() called "
+ pr_err("acpi_evalf() called "
"with invalid format character '%c'\n", c);
+ va_end(ap);
return 0;
}
}
@@ -592,23 +597,24 @@ static int acpi_evalf(acpi_handle handle,
switch (res_type) {
case 'd': /* int */
- if (res)
- *(int *)res = out_obj.integer.value;
- success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER;
+ success = (status == AE_OK &&
+ out_obj.type == ACPI_TYPE_INTEGER);
+ if (success && res)
+ *res = out_obj.integer.value;
break;
case 'v': /* void */
success = status == AE_OK;
break;
/* add more types as needed */
default:
- printk(TPACPI_ERR "acpi_evalf() called "
+ pr_err("acpi_evalf() called "
"with invalid format character '%c'\n", res_type);
return 0;
}
if (!success && !quiet)
- printk(TPACPI_ERR "acpi_evalf(%s, %s, ...) failed: %d\n",
- method, fmt0, status);
+ pr_err("acpi_evalf(%s, %s, ...) failed: %s\n",
+ method, fmt0, acpi_format_exception(status));
return success;
}
@@ -659,11 +665,11 @@ static int issue_thinkpad_cmos_command(int cmos_cmd)
#define TPACPI_ACPIHANDLE_INIT(object) \
drv_acpi_handle_init(#object, &object##_handle, *object##_parent, \
- object##_paths, ARRAY_SIZE(object##_paths), &object##_path)
+ object##_paths, ARRAY_SIZE(object##_paths))
-static void drv_acpi_handle_init(char *name,
- acpi_handle *handle, acpi_handle parent,
- char **paths, int num_paths, char **path)
+static void __init drv_acpi_handle_init(const char *name,
+ acpi_handle *handle, const acpi_handle parent,
+ char **paths, const int num_paths)
{
int i;
acpi_status status;
@@ -674,10 +680,9 @@ static void drv_acpi_handle_init(char *name,
for (i = 0; i < num_paths; i++) {
status = acpi_get_handle(parent, paths[i], handle);
if (ACPI_SUCCESS(status)) {
- *path = paths[i];
dbg_printk(TPACPI_DBG_INIT,
"Found ACPI handle %s for %s\n",
- *path, name);
+ paths[i], name);
return;
}
}
@@ -687,6 +692,51 @@ static void drv_acpi_handle_init(char *name,
*handle = NULL;
}
+static acpi_status __init tpacpi_acpi_handle_locate_callback(acpi_handle handle,
+ u32 level, void *context, void **return_value)
+{
+ struct acpi_device *dev;
+ if (!strcmp(context, "video")) {
+ if (acpi_bus_get_device(handle, &dev))
+ return AE_OK;
+ if (strcmp(ACPI_VIDEO_HID, acpi_device_hid(dev)))
+ return AE_OK;
+ }
+
+ *(acpi_handle *)return_value = handle;
+
+ return AE_CTRL_TERMINATE;
+}
+
+static void __init tpacpi_acpi_handle_locate(const char *name,
+ const char *hid,
+ acpi_handle *handle)
+{
+ acpi_status status;
+ acpi_handle device_found;
+
+ BUG_ON(!name || !handle);
+ vdbg_printk(TPACPI_DBG_INIT,
+ "trying to locate ACPI handle for %s, using HID %s\n",
+ name, hid ? hid : "NULL");
+
+ memset(&device_found, 0, sizeof(device_found));
+ status = acpi_get_devices(hid, tpacpi_acpi_handle_locate_callback,
+ (void *)name, &device_found);
+
+ *handle = NULL;
+
+ if (ACPI_SUCCESS(status)) {
+ *handle = device_found;
+ dbg_printk(TPACPI_DBG_INIT,
+ "Found ACPI handle for %s\n", name);
+ } else {
+ vdbg_printk(TPACPI_DBG_INIT,
+ "Could not locate an ACPI handle for %s: %s\n",
+ name, acpi_format_exception(status));
+ }
+}
+
static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data)
{
struct ibm_struct *ibm = data;
@@ -715,8 +765,7 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm)
rc = acpi_bus_get_device(*ibm->acpi->handle, &ibm->acpi->device);
if (rc < 0) {
- printk(TPACPI_ERR "acpi_bus_get_device(%s) failed: %d\n",
- ibm->name, rc);
+ pr_err("acpi_bus_get_device(%s) failed: %d\n", ibm->name, rc);
return -ENODEV;
}
@@ -729,13 +778,11 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm)
ibm->acpi->type, dispatch_acpi_notify, ibm);
if (ACPI_FAILURE(status)) {
if (status == AE_ALREADY_EXISTS) {
- printk(TPACPI_NOTICE
- "another device driver is already "
- "handling %s events\n", ibm->name);
+ pr_notice("another device driver is already "
+ "handling %s events\n", ibm->name);
} else {
- printk(TPACPI_ERR
- "acpi_install_notify_handler(%s) failed: %d\n",
- ibm->name, status);
+ pr_err("acpi_install_notify_handler(%s) failed: %s\n",
+ ibm->name, acpi_format_exception(status));
}
return -ENODEV;
}
@@ -759,8 +806,7 @@ static int __init register_tpacpi_subdriver(struct ibm_struct *ibm)
ibm->acpi->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL);
if (!ibm->acpi->driver) {
- printk(TPACPI_ERR
- "failed to allocate memory for ibm->acpi->driver\n");
+ pr_err("failed to allocate memory for ibm->acpi->driver\n");
return -ENOMEM;
}
@@ -771,7 +817,7 @@ static int __init register_tpacpi_subdriver(struct ibm_struct *ibm)
rc = acpi_bus_register_driver(ibm->acpi->driver);
if (rc < 0) {
- printk(TPACPI_ERR "acpi_bus_register_driver(%s) failed: %d\n",
+ pr_err("acpi_bus_register_driver(%s) failed: %d\n",
ibm->name, rc);
kfree(ibm->acpi->driver);
ibm->acpi->driver = NULL;
@@ -801,14 +847,14 @@ static int dispatch_proc_show(struct seq_file *m, void *v)
static int dispatch_proc_open(struct inode *inode, struct file *file)
{
- return single_open(file, dispatch_proc_show, PDE(inode)->data);
+ return single_open(file, dispatch_proc_show, PDE_DATA(inode));
}
static ssize_t dispatch_proc_write(struct file *file,
const char __user *userbuf,
size_t count, loff_t *pos)
{
- struct ibm_struct *ibm = PDE(file->f_path.dentry->d_inode)->data;
+ struct ibm_struct *ibm = PDE_DATA(file_inode(file));
char *kernbuf;
int ret;
@@ -878,8 +924,8 @@ static struct input_dev *tpacpi_inputdev;
static struct mutex tpacpi_inputdev_send_mutex;
static LIST_HEAD(tpacpi_all_drivers);
-static int tpacpi_suspend_handler(struct platform_device *pdev,
- pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int tpacpi_suspend_handler(struct device *dev)
{
struct ibm_struct *ibm, *itmp;
@@ -887,13 +933,13 @@ static int tpacpi_suspend_handler(struct platform_device *pdev,
&tpacpi_all_drivers,
all_drivers) {
if (ibm->suspend)
- (ibm->suspend)(state);
+ (ibm->suspend)();
}
return 0;
}
-static int tpacpi_resume_handler(struct platform_device *pdev)
+static int tpacpi_resume_handler(struct device *dev)
{
struct ibm_struct *ibm, *itmp;
@@ -906,6 +952,10 @@ static int tpacpi_resume_handler(struct platform_device *pdev)
return 0;
}
+#endif
+
+static SIMPLE_DEV_PM_OPS(tpacpi_pm,
+ tpacpi_suspend_handler, tpacpi_resume_handler);
static void tpacpi_shutdown_handler(struct platform_device *pdev)
{
@@ -923,9 +973,8 @@ static struct platform_driver tpacpi_pdriver = {
.driver = {
.name = TPACPI_DRVR_NAME,
.owner = THIS_MODULE,
+ .pm = &tpacpi_pm,
},
- .suspend = tpacpi_suspend_handler,
- .resume = tpacpi_resume_handler,
.shutdown = tpacpi_shutdown_handler,
};
@@ -1029,89 +1078,14 @@ static int parse_strtoul(const char *buf,
static void tpacpi_disable_brightness_delay(void)
{
if (acpi_evalf(hkey_handle, NULL, "PWMS", "qvd", 0))
- printk(TPACPI_NOTICE
- "ACPI backlight control delay disabled\n");
-}
-
-static int __init tpacpi_query_bcl_levels(acpi_handle handle)
-{
- struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
- union acpi_object *obj;
- int rc;
-
- if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) {
- obj = (union acpi_object *)buffer.pointer;
- if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
- printk(TPACPI_ERR "Unknown _BCL data, "
- "please report this to %s\n", TPACPI_MAIL);
- rc = 0;
- } else {
- rc = obj->package.count;
- }
- } else {
- return 0;
- }
-
- kfree(buffer.pointer);
- return rc;
-}
-
-static acpi_status __init tpacpi_acpi_walk_find_bcl(acpi_handle handle,
- u32 lvl, void *context, void **rv)
-{
- char name[ACPI_PATH_SEGMENT_LENGTH];
- struct acpi_buffer buffer = { sizeof(name), &name };
-
- if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) &&
- !strncmp("_BCL", name, sizeof(name) - 1)) {
- BUG_ON(!rv || !*rv);
- **(int **)rv = tpacpi_query_bcl_levels(handle);
- return AE_CTRL_TERMINATE;
- } else {
- return AE_OK;
- }
-}
-
-/*
- * Returns 0 (no ACPI _BCL or _BCL invalid), or size of brightness map
- */
-static int __init tpacpi_check_std_acpi_brightness_support(void)
-{
- int status;
- int bcl_levels = 0;
- void *bcl_ptr = &bcl_levels;
-
- if (!vid_handle) {
- TPACPI_ACPIHANDLE_INIT(vid);
- }
- if (!vid_handle)
- return 0;
-
- /*
- * Search for a _BCL method, and execute it. This is safe on all
- * ThinkPads, and as a side-effect, _BCL will place a Lenovo Vista
- * BIOS in ACPI backlight control mode. We do NOT have to care
- * about calling the _BCL method in an enabled video device, any
- * will do for our purposes.
- */
-
- status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3,
- tpacpi_acpi_walk_find_bcl, NULL, NULL,
- &bcl_ptr);
-
- if (ACPI_SUCCESS(status) && bcl_levels > 2) {
- tp_features.bright_acpimode = 1;
- return (bcl_levels - 2);
- }
-
- return 0;
+ pr_notice("ACPI backlight control delay disabled\n");
}
static void printk_deprecated_attribute(const char * const what,
const char * const details)
{
tpacpi_log_usertask("deprecated sysfs attribute");
- printk(TPACPI_WARN "WARNING: sysfs attribute %s is deprecated and "
+ pr_warn("WARNING: sysfs attribute %s is deprecated and "
"will be removed. %s\n",
what, details);
}
@@ -1286,8 +1260,7 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id,
&tpacpi_rfk_rfkill_ops,
atp_rfk);
if (!atp_rfk || !atp_rfk->rfkill) {
- printk(TPACPI_ERR
- "failed to allocate memory for rfkill class\n");
+ pr_err("failed to allocate memory for rfkill class\n");
kfree(atp_rfk);
return -ENOMEM;
}
@@ -1297,9 +1270,8 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id,
sw_status = (tp_rfkops->get_status)();
if (sw_status < 0) {
- printk(TPACPI_ERR
- "failed to read initial state for %s, error %d\n",
- name, sw_status);
+ pr_err("failed to read initial state for %s, error %d\n",
+ name, sw_status);
} else {
sw_state = (sw_status == TPACPI_RFK_RADIO_OFF);
if (set_default) {
@@ -1313,9 +1285,7 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id,
res = rfkill_register(atp_rfk->rfkill);
if (res < 0) {
- printk(TPACPI_ERR
- "failed to register %s rfkill switch: %d\n",
- name, res);
+ pr_err("failed to register %s rfkill switch: %d\n", name, res);
rfkill_destroy(atp_rfk->rfkill);
kfree(atp_rfk);
return res;
@@ -1323,7 +1293,7 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id,
tpacpi_rfkill_switches[id] = atp_rfk;
- printk(TPACPI_INFO "rfkill switch %s: radio is %sblocked\n",
+ pr_info("rfkill switch %s: radio is %sblocked\n",
name, (sw_state || hw_state) ? "" : "un");
return 0;
}
@@ -1667,7 +1637,7 @@ static void tpacpi_remove_driver_attributes(struct device_driver *drv)
* Table of recommended minimum BIOS versions
*
* Reasons for listing:
- * 1. Stable BIOS, listed because the unknown ammount of
+ * 1. Stable BIOS, listed because the unknown amount of
* bugs and bad ACPI behaviour on older versions
*
* 2. BIOS or EC fw with known bugs that trigger on Linux
@@ -1847,10 +1817,8 @@ static void __init tpacpi_check_outdated_fw(void)
* broken, or really stable to begin with, so it is
* best if the user upgrades the firmware anyway.
*/
- printk(TPACPI_WARN
- "WARNING: Outdated ThinkPad BIOS/EC firmware\n");
- printk(TPACPI_WARN
- "WARNING: This firmware may be missing critical bug "
+ pr_warn("WARNING: Outdated ThinkPad BIOS/EC firmware\n");
+ pr_warn("WARNING: This firmware may be missing critical bug "
"fixes and/or important features\n");
}
}
@@ -1870,34 +1838,9 @@ static bool __init tpacpi_is_fw_known(void)
****************************************************************************/
/*************************************************************************
- * thinkpad-acpi init subdriver
+ * thinkpad-acpi metadata subdriver
*/
-static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm)
-{
- printk(TPACPI_INFO "%s v%s\n", TPACPI_DESC, TPACPI_VERSION);
- printk(TPACPI_INFO "%s\n", TPACPI_URL);
-
- printk(TPACPI_INFO "ThinkPad BIOS %s, EC %s\n",
- (thinkpad_id.bios_version_str) ?
- thinkpad_id.bios_version_str : "unknown",
- (thinkpad_id.ec_version_str) ?
- thinkpad_id.ec_version_str : "unknown");
-
- if (thinkpad_id.vendor && thinkpad_id.model_str)
- printk(TPACPI_INFO "%s %s, model %s\n",
- (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ?
- "IBM" : ((thinkpad_id.vendor ==
- PCI_VENDOR_ID_LENOVO) ?
- "Lenovo" : "Unknown vendor"),
- thinkpad_id.model_str,
- (thinkpad_id.nummodel_str) ?
- thinkpad_id.nummodel_str : "unknown");
-
- tpacpi_check_outdated_fw();
- return 0;
-}
-
static int thinkpad_acpi_driver_read(struct seq_file *m)
{
seq_printf(m, "driver:\t\t%s\n", TPACPI_DESC);
@@ -1960,6 +1903,17 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */
TP_ACPI_HOTKEYSCAN_VOLUMEDOWN,
TP_ACPI_HOTKEYSCAN_MUTE,
TP_ACPI_HOTKEYSCAN_THINKPAD,
+ TP_ACPI_HOTKEYSCAN_UNK1,
+ TP_ACPI_HOTKEYSCAN_UNK2,
+ TP_ACPI_HOTKEYSCAN_UNK3,
+ TP_ACPI_HOTKEYSCAN_UNK4,
+ TP_ACPI_HOTKEYSCAN_UNK5,
+ TP_ACPI_HOTKEYSCAN_UNK6,
+ TP_ACPI_HOTKEYSCAN_UNK7,
+ TP_ACPI_HOTKEYSCAN_UNK8,
+
+ /* Hotkey keymap size */
+ TPACPI_HOTKEY_MAP_LEN
};
enum { /* Keys/events available through NVRAM polling */
@@ -2013,9 +1967,6 @@ struct tp_nvram_state {
/* kthread for the hotkey poller */
static struct task_struct *tpacpi_hotkey_task;
-/* Acquired while the poller kthread is running, use to sync start/stop */
-static struct mutex hotkey_thread_mutex;
-
/*
* Acquire mutex to write poller control variables as an
* atomic block.
@@ -2074,14 +2025,13 @@ static u32 hotkey_driver_mask; /* events needed by the driver */
static u32 hotkey_user_mask; /* events visible to userspace */
static u32 hotkey_acpi_mask; /* events enabled in firmware */
-static unsigned int hotkey_report_mode;
-
static u16 *hotkey_keycode_map;
static struct attribute_set *hotkey_dev_attributes;
static void tpacpi_driver_event(const unsigned int hkey_event);
static void hotkey_driver_event(const unsigned int scancode);
+static void hotkey_poll_setup(const bool may_warn);
/* HKEY.MHKG() return bits */
#define TP_HOTKEY_TABLET_MASK (1 << 3)
@@ -2152,9 +2102,7 @@ void static hotkey_mask_warn_incomplete_mask(void)
(hotkey_all_mask | TPACPI_HKEY_NVRAM_KNOWN_MASK);
if (wantedmask)
- printk(TPACPI_NOTICE
- "required events 0x%08x not enabled!\n",
- wantedmask);
+ pr_notice("required events 0x%08x not enabled!\n", wantedmask);
}
/*
@@ -2192,10 +2140,9 @@ static int hotkey_mask_set(u32 mask)
* a given event.
*/
if (!hotkey_mask_get() && !rc && (fwmask & ~hotkey_acpi_mask)) {
- printk(TPACPI_NOTICE
- "asked for hotkey mask 0x%08x, but "
- "firmware forced it to 0x%08x\n",
- fwmask, hotkey_acpi_mask);
+ pr_notice("asked for hotkey mask 0x%08x, but "
+ "firmware forced it to 0x%08x\n",
+ fwmask, hotkey_acpi_mask);
}
if (tpacpi_lifecycle != TPACPI_LIFE_EXITING)
@@ -2219,13 +2166,11 @@ static int hotkey_user_mask_set(const u32 mask)
(mask == 0xffff || mask == 0xffffff ||
mask == 0xffffffff)) {
tp_warned.hotkey_mask_ff = 1;
- printk(TPACPI_NOTICE
- "setting the hotkey mask to 0x%08x is likely "
- "not the best way to go about it\n", mask);
- printk(TPACPI_NOTICE
- "please consider using the driver defaults, "
- "and refer to up-to-date thinkpad-acpi "
- "documentation\n");
+ pr_notice("setting the hotkey mask to 0x%08x is likely "
+ "not the best way to go about it\n", mask);
+ pr_notice("please consider using the driver defaults, "
+ "and refer to up-to-date thinkpad-acpi "
+ "documentation\n");
}
/* Try to enable what the user asked for, plus whatever we need.
@@ -2264,6 +2209,8 @@ static int tpacpi_hotkey_driver_mask_set(const u32 mask)
rc = hotkey_mask_set((hotkey_acpi_mask | hotkey_driver_mask) &
~hotkey_source_mask);
+ hotkey_poll_setup(true);
+
mutex_unlock(&hotkey_mutex);
return rc;
@@ -2309,16 +2256,12 @@ static void tpacpi_input_send_key(const unsigned int scancode)
if (keycode != KEY_RESERVED) {
mutex_lock(&tpacpi_inputdev_send_mutex);
+ input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, scancode);
input_report_key(tpacpi_inputdev, keycode, 1);
- if (keycode == KEY_UNKNOWN)
- input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN,
- scancode);
input_sync(tpacpi_inputdev);
+ input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, scancode);
input_report_key(tpacpi_inputdev, keycode, 0);
- if (keycode == KEY_UNKNOWN)
- input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN,
- scancode);
input_sync(tpacpi_inputdev);
mutex_unlock(&tpacpi_inputdev_send_mutex);
@@ -2340,10 +2283,6 @@ static struct tp_acpi_drv_struct ibm_hotkey_acpidriver;
static void tpacpi_hotkey_send_key(unsigned int scancode)
{
tpacpi_input_send_key_masked(scancode);
- if (hotkey_report_mode < 2) {
- acpi_bus_generate_proc_event(ibm_hotkey_acpidriver.device,
- 0x80, TP_HKEY_EV_HOTKEY_BASE + scancode);
- }
}
static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m)
@@ -2382,24 +2321,56 @@ static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m)
}
}
+#define TPACPI_COMPARE_KEY(__scancode, __member) \
+do { \
+ if ((event_mask & (1 << __scancode)) && \
+ oldn->__member != newn->__member) \
+ tpacpi_hotkey_send_key(__scancode); \
+} while (0)
+
+#define TPACPI_MAY_SEND_KEY(__scancode) \
+do { \
+ if (event_mask & (1 << __scancode)) \
+ tpacpi_hotkey_send_key(__scancode); \
+} while (0)
+
+static void issue_volchange(const unsigned int oldvol,
+ const unsigned int newvol,
+ const u32 event_mask)
+{
+ unsigned int i = oldvol;
+
+ while (i > newvol) {
+ TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
+ i--;
+ }
+ while (i < newvol) {
+ TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
+ i++;
+ }
+}
+
+static void issue_brightnesschange(const unsigned int oldbrt,
+ const unsigned int newbrt,
+ const u32 event_mask)
+{
+ unsigned int i = oldbrt;
+
+ while (i > newbrt) {
+ TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
+ i--;
+ }
+ while (i < newbrt) {
+ TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
+ i++;
+ }
+}
+
static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
struct tp_nvram_state *newn,
const u32 event_mask)
{
-#define TPACPI_COMPARE_KEY(__scancode, __member) \
- do { \
- if ((event_mask & (1 << __scancode)) && \
- oldn->__member != newn->__member) \
- tpacpi_hotkey_send_key(__scancode); \
- } while (0)
-
-#define TPACPI_MAY_SEND_KEY(__scancode) \
- do { \
- if (event_mask & (1 << __scancode)) \
- tpacpi_hotkey_send_key(__scancode); \
- } while (0)
-
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle);
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle);
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF7, display_toggle);
@@ -2409,41 +2380,63 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF8, displayexp_toggle);
- /* handle volume */
- if (oldn->volume_toggle != newn->volume_toggle) {
- if (oldn->mute != newn->mute) {
+ /*
+ * Handle volume
+ *
+ * This code is supposed to duplicate the IBM firmware behaviour:
+ * - Pressing MUTE issues mute hotkey message, even when already mute
+ * - Pressing Volume up/down issues volume up/down hotkey messages,
+ * even when already at maximum or minimum volume
+ * - The act of unmuting issues volume up/down notification,
+ * depending which key was used to unmute
+ *
+ * We are constrained to what the NVRAM can tell us, which is not much
+ * and certainly not enough if more than one volume hotkey was pressed
+ * since the last poll cycle.
+ *
+ * Just to make our life interesting, some newer Lenovo ThinkPads have
+ * bugs in the BIOS and may fail to update volume_toggle properly.
+ */
+ if (newn->mute) {
+ /* muted */
+ if (!oldn->mute ||
+ oldn->volume_toggle != newn->volume_toggle ||
+ oldn->volume_level != newn->volume_level) {
+ /* recently muted, or repeated mute keypress, or
+ * multiple presses ending in mute */
+ issue_volchange(oldn->volume_level, newn->volume_level,
+ event_mask);
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE);
}
- if (oldn->volume_level > newn->volume_level) {
- TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
- } else if (oldn->volume_level < newn->volume_level) {
+ } else {
+ /* unmute */
+ if (oldn->mute) {
+ /* recently unmuted, issue 'unmute' keypress */
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
- } else if (oldn->mute == newn->mute) {
- /* repeated key presses that didn't change state */
- if (newn->mute) {
- TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE);
- } else if (newn->volume_level != 0) {
- TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
- } else {
+ }
+ if (oldn->volume_level != newn->volume_level) {
+ issue_volchange(oldn->volume_level, newn->volume_level,
+ event_mask);
+ } else if (oldn->volume_toggle != newn->volume_toggle) {
+ /* repeated vol up/down keypress at end of scale ? */
+ if (newn->volume_level == 0)
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
- }
+ else if (newn->volume_level >= TP_NVRAM_LEVEL_VOLUME_MAX)
+ TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
}
}
/* handle brightness */
- if (oldn->brightness_toggle != newn->brightness_toggle) {
- if (oldn->brightness_level < newn->brightness_level) {
- TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
- } else if (oldn->brightness_level > newn->brightness_level) {
+ if (oldn->brightness_level != newn->brightness_level) {
+ issue_brightnesschange(oldn->brightness_level,
+ newn->brightness_level, event_mask);
+ } else if (oldn->brightness_toggle != newn->brightness_toggle) {
+ /* repeated key presses that didn't change state */
+ if (newn->brightness_level == 0)
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
- } else {
- /* repeated key presses that didn't change state */
- if (newn->brightness_level != 0) {
- TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
- } else {
- TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
- }
- }
+ else if (newn->brightness_level >= bright_maxlvl
+ && !tp_features.bright_unkfw)
+ TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
}
#undef TPACPI_COMPARE_KEY
@@ -2463,10 +2456,9 @@ static int hotkey_kthread(void *data)
u32 poll_mask, event_mask;
unsigned int si, so;
unsigned long t;
- unsigned int change_detector, must_reset;
+ unsigned int change_detector;
unsigned int poll_freq;
-
- mutex_lock(&hotkey_thread_mutex);
+ bool was_frozen;
if (tpacpi_lifecycle == TPACPI_LIFE_EXITING)
goto exit;
@@ -2495,14 +2487,14 @@ static int hotkey_kthread(void *data)
t = 100; /* should never happen... */
}
t = msleep_interruptible(t);
- if (unlikely(kthread_should_stop()))
+ if (unlikely(kthread_freezable_should_stop(&was_frozen)))
break;
- must_reset = try_to_freeze();
- if (t > 0 && !must_reset)
+
+ if (t > 0 && !was_frozen)
continue;
mutex_lock(&hotkey_thread_data_mutex);
- if (must_reset || hotkey_config_change != change_detector) {
+ if (was_frozen || hotkey_config_change != change_detector) {
/* forget old state on thaw or config change */
si = so;
t = 0;
@@ -2527,7 +2519,6 @@ static int hotkey_kthread(void *data)
}
exit:
- mutex_unlock(&hotkey_thread_mutex);
return 0;
}
@@ -2535,20 +2526,13 @@ exit:
static void hotkey_poll_stop_sync(void)
{
if (tpacpi_hotkey_task) {
- if (frozen(tpacpi_hotkey_task) ||
- freezing(tpacpi_hotkey_task))
- thaw_process(tpacpi_hotkey_task);
-
kthread_stop(tpacpi_hotkey_task);
tpacpi_hotkey_task = NULL;
- mutex_lock(&hotkey_thread_mutex);
- /* at this point, the thread did exit */
- mutex_unlock(&hotkey_thread_mutex);
}
}
/* call with hotkey_mutex held */
-static void hotkey_poll_setup(bool may_warn)
+static void hotkey_poll_setup(const bool may_warn)
{
const u32 poll_driver_mask = hotkey_driver_mask & hotkey_source_mask;
const u32 poll_user_mask = hotkey_user_mask & hotkey_source_mask;
@@ -2561,8 +2545,7 @@ static void hotkey_poll_setup(bool may_warn)
NULL, TPACPI_NVRAM_KTHREAD_NAME);
if (IS_ERR(tpacpi_hotkey_task)) {
tpacpi_hotkey_task = NULL;
- printk(TPACPI_ERR
- "could not create kernel thread "
+ pr_err("could not create kernel thread "
"for hotkey polling\n");
}
}
@@ -2570,16 +2553,15 @@ static void hotkey_poll_setup(bool may_warn)
hotkey_poll_stop_sync();
if (may_warn && (poll_driver_mask || poll_user_mask) &&
hotkey_poll_freq == 0) {
- printk(TPACPI_NOTICE
- "hot keys 0x%08x and/or events 0x%08x "
- "require polling, which is currently "
- "disabled\n",
- poll_user_mask, poll_driver_mask);
+ pr_notice("hot keys 0x%08x and/or events 0x%08x "
+ "require polling, which is currently "
+ "disabled\n",
+ poll_user_mask, poll_driver_mask);
}
}
}
-static void hotkey_poll_setup_safe(bool may_warn)
+static void hotkey_poll_setup_safe(const bool may_warn)
{
mutex_lock(&hotkey_mutex);
hotkey_poll_setup(may_warn);
@@ -2597,7 +2579,11 @@ static void hotkey_poll_set_freq(unsigned int freq)
#else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
-static void hotkey_poll_setup_safe(bool __unused)
+static void hotkey_poll_setup(const bool __unused)
+{
+}
+
+static void hotkey_poll_setup_safe(const bool __unused)
{
}
@@ -2607,16 +2593,11 @@ static int hotkey_inputdev_open(struct input_dev *dev)
{
switch (tpacpi_lifecycle) {
case TPACPI_LIFE_INIT:
- /*
- * hotkey_init will call hotkey_poll_setup_safe
- * at the appropriate moment
- */
- return 0;
- case TPACPI_LIFE_EXITING:
- return -EBUSY;
case TPACPI_LIFE_RUNNING:
hotkey_poll_setup_safe(false);
return 0;
+ case TPACPI_LIFE_EXITING:
+ return -EBUSY;
}
/* Should only happen if tpacpi_lifecycle is corrupt */
@@ -2627,7 +2608,7 @@ static int hotkey_inputdev_open(struct input_dev *dev)
static void hotkey_inputdev_close(struct input_dev *dev)
{
/* disable hotkey polling when possible */
- if (tpacpi_lifecycle == TPACPI_LIFE_RUNNING &&
+ if (tpacpi_lifecycle != TPACPI_LIFE_EXITING &&
!(hotkey_source_mask & hotkey_driver_mask))
hotkey_poll_setup_safe(false);
}
@@ -2799,13 +2780,13 @@ static ssize_t hotkey_source_mask_store(struct device *dev,
mutex_unlock(&hotkey_mutex);
if (rc < 0)
- printk(TPACPI_ERR "hotkey_source_mask: failed to update the"
- "firmware event mask!\n");
+ pr_err("hotkey_source_mask: "
+ "failed to update the firmware event mask!\n");
if (r_ev)
- printk(TPACPI_NOTICE "hotkey_source_mask: "
- "some important events were disabled: "
- "0x%04x\n", r_ev);
+ pr_notice("hotkey_source_mask: "
+ "some important events were disabled: 0x%04x\n",
+ r_ev);
tpacpi_disclose_usertask("hotkey_source_mask", "set to 0x%08lx\n", t);
@@ -2902,18 +2883,6 @@ static void hotkey_tablet_mode_notify_change(void)
"hotkey_tablet_mode");
}
-/* sysfs hotkey report_mode -------------------------------------------- */
-static ssize_t hotkey_report_mode_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%d\n",
- (hotkey_report_mode != 0) ? hotkey_report_mode : 1);
-}
-
-static struct device_attribute dev_attr_hotkey_report_mode =
- __ATTR(hotkey_report_mode, S_IRUGO, hotkey_report_mode_show, NULL);
-
/* sysfs wakeup reason (pollable) -------------------------------------- */
static ssize_t hotkey_wakeup_reason_show(struct device *dev,
struct device_attribute *attr,
@@ -2955,7 +2924,6 @@ static struct attribute *hotkey_attributes[] __initdata = {
&dev_attr_hotkey_enable.attr,
&dev_attr_hotkey_bios_enabled.attr,
&dev_attr_hotkey_bios_mask.attr,
- &dev_attr_hotkey_report_mode.attr,
&dev_attr_hotkey_wakeup_reason.attr,
&dev_attr_hotkey_wakeup_hotunplug_complete.attr,
&dev_attr_hotkey_mask.attr,
@@ -2979,7 +2947,7 @@ static void tpacpi_send_radiosw_update(void)
* rfkill input events, or we will race the rfkill core input
* handler.
*
- * tpacpi_inputdev_send_mutex works as a syncronization point
+ * tpacpi_inputdev_send_mutex works as a synchronization point
* for the above.
*
* We optimize to avoid numerous calls to hotkey_get_wlsw.
@@ -3027,8 +2995,6 @@ static void hotkey_exit(void)
if (hotkey_dev_attributes)
delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
- kfree(hotkey_keycode_map);
-
dbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_HKEY,
"restoring original HKEY status and mask\n");
/* yes, there is a bitwise or below, we want the
@@ -3036,8 +3002,7 @@ static void hotkey_exit(void)
if (((tp_features.hotkey_mask &&
hotkey_mask_set(hotkey_orig_mask)) |
hotkey_status_set(false)) != 0)
- printk(TPACPI_ERR
- "failed to restore hot key mask "
+ pr_err("failed to restore hot key mask "
"to BIOS defaults\n");
}
@@ -3079,6 +3044,9 @@ static const struct tpacpi_quirk tpacpi_hotkey_qtable[] __initconst = {
TPACPI_Q_IBM('1', 'D', TPACPI_HK_Q_INIMASK), /* X22, X23, X24 */
};
+typedef u16 tpacpi_keymap_entry_t;
+typedef tpacpi_keymap_entry_t tpacpi_keymap_t[TPACPI_HOTKEY_MAP_LEN];
+
static int __init hotkey_init(struct ibm_init_struct *iibm)
{
/* Requirements for changing the default keymaps:
@@ -3110,9 +3078,17 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
* If the above is too much to ask, don't change the keymap.
* Ask the thinkpad-acpi maintainer to do it, instead.
*/
- static u16 ibm_keycode_map[] __initdata = {
+
+ enum keymap_index {
+ TPACPI_KEYMAP_IBM_GENERIC = 0,
+ TPACPI_KEYMAP_LENOVO_GENERIC,
+ };
+
+ static const tpacpi_keymap_t tpacpi_keymaps[] __initconst = {
+ /* Generic keymap for IBM ThinkPads */
+ [TPACPI_KEYMAP_IBM_GENERIC] = {
/* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */
- KEY_FN_F1, KEY_FN_F2, KEY_COFFEE, KEY_SLEEP,
+ KEY_FN_F1, KEY_BATTERY, KEY_COFFEE, KEY_SLEEP,
KEY_WLAN, KEY_FN_F6, KEY_SWITCHVIDEOMODE, KEY_FN_F8,
KEY_FN_F9, KEY_FN_F10, KEY_FN_F11, KEY_SUSPEND,
@@ -3143,11 +3119,13 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
/* (assignments unknown, please report if found) */
KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
- };
- static u16 lenovo_keycode_map[] __initdata = {
+ },
+
+ /* Generic keymap for Lenovo ThinkPads */
+ [TPACPI_KEYMAP_LENOVO_GENERIC] = {
/* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */
KEY_FN_F1, KEY_COFFEE, KEY_BATTERY, KEY_SLEEP,
- KEY_WLAN, KEY_FN_F6, KEY_SWITCHVIDEOMODE, KEY_FN_F8,
+ KEY_WLAN, KEY_CAMERA, KEY_SWITCHVIDEOMODE, KEY_FN_F8,
KEY_FN_F9, KEY_FN_F10, KEY_FN_F11, KEY_SUSPEND,
/* Scan codes 0x0C to 0x1F: Other ACPI HKEY hot keys */
@@ -3184,13 +3162,38 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
KEY_VENDOR, /* 0x17: Thinkpad/AccessIBM/Lenovo */
/* (assignments unknown, please report if found) */
- KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
- KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
+ KEY_UNKNOWN, KEY_UNKNOWN,
+
+ /*
+ * The mic mute button only sends 0x1a. It does not
+ * automatically mute the mic or change the mute light.
+ */
+ KEY_MICMUTE, /* 0x1a: Mic mute (since ?400 or so) */
+
+ /* (assignments unknown, please report if found) */
+ KEY_UNKNOWN,
+
+ /* Extra keys in use since the X240 / T440 / T540 */
+ KEY_CONFIG, KEY_SEARCH, KEY_SCALE, KEY_COMPUTER,
+ },
};
-#define TPACPI_HOTKEY_MAP_LEN ARRAY_SIZE(ibm_keycode_map)
-#define TPACPI_HOTKEY_MAP_SIZE sizeof(ibm_keycode_map)
-#define TPACPI_HOTKEY_MAP_TYPESIZE sizeof(ibm_keycode_map[0])
+ static const struct tpacpi_quirk tpacpi_keymap_qtable[] __initconst = {
+ /* Generic maps (fallback) */
+ {
+ .vendor = PCI_VENDOR_ID_IBM,
+ .bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_ANY,
+ .quirks = TPACPI_KEYMAP_IBM_GENERIC,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_LENOVO,
+ .bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_ANY,
+ .quirks = TPACPI_KEYMAP_LENOVO_GENERIC,
+ },
+ };
+
+#define TPACPI_HOTKEY_MAP_SIZE sizeof(tpacpi_keymap_t)
+#define TPACPI_HOTKEY_MAP_TYPESIZE sizeof(tpacpi_keymap_entry_t)
int res, i;
int status;
@@ -3199,6 +3202,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
bool tabletsw_state = false;
unsigned long quirks;
+ unsigned long keymap_id;
vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY,
"initializing hotkey subdriver\n");
@@ -3211,7 +3215,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
mutex_init(&hotkey_mutex);
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
- mutex_init(&hotkey_thread_mutex);
mutex_init(&hotkey_thread_data_mutex);
#endif
@@ -3248,10 +3251,9 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
for HKEY interface version 0x100 */
if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
if ((hkeyv >> 8) != 1) {
- printk(TPACPI_ERR "unknown version of the "
- "HKEY interface: 0x%x\n", hkeyv);
- printk(TPACPI_ERR "please report this to %s\n",
- TPACPI_MAIL);
+ pr_err("unknown version of the HKEY interface: 0x%x\n",
+ hkeyv);
+ pr_err("please report this to %s\n", TPACPI_MAIL);
} else {
/*
* MHKV 0x100 in A31, R40, R40e,
@@ -3264,8 +3266,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
/* Paranoia check AND init hotkey_all_mask */
if (!acpi_evalf(hkey_handle, &hotkey_all_mask,
"MHKA", "qd")) {
- printk(TPACPI_ERR
- "missing MHKA handler, "
+ pr_err("missing MHKA handler, "
"please report this to %s\n",
TPACPI_MAIL);
/* Fallback: pre-init for FN+F3,F4,F12 */
@@ -3303,16 +3304,14 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
if (dbg_wlswemul) {
tp_features.hotkey_wlsw = 1;
radiosw_state = !!tpacpi_wlsw_emulstate;
- printk(TPACPI_INFO
- "radio switch emulation enabled\n");
+ pr_info("radio switch emulation enabled\n");
} else
#endif
/* Not all thinkpads have a hardware radio switch */
if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) {
tp_features.hotkey_wlsw = 1;
radiosw_state = !!status;
- printk(TPACPI_INFO
- "radio switch found; radios are %s\n",
+ pr_info("radio switch found; radios are %s\n",
enabled(status, 0));
}
if (tp_features.hotkey_wlsw)
@@ -3323,8 +3322,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) {
tp_features.hotkey_tablet = 1;
tabletsw_state = !!(status & TP_HOTKEY_TABLET_MASK);
- printk(TPACPI_INFO
- "possible tablet mode switch found; "
+ pr_info("possible tablet mode switch found; "
"ThinkPad in %s mode\n",
(tabletsw_state) ? "tablet" : "laptop");
res = add_to_attr_set(hotkey_dev_attributes,
@@ -3339,27 +3337,22 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
goto err_exit;
/* Set up key map */
-
hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE,
GFP_KERNEL);
if (!hotkey_keycode_map) {
- printk(TPACPI_ERR
- "failed to allocate memory for key map\n");
+ pr_err("failed to allocate memory for key map\n");
res = -ENOMEM;
goto err_exit;
}
- if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) {
- dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY,
- "using Lenovo default hot key map\n");
- memcpy(hotkey_keycode_map, &lenovo_keycode_map,
- TPACPI_HOTKEY_MAP_SIZE);
- } else {
- dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY,
- "using IBM default hot key map\n");
- memcpy(hotkey_keycode_map, &ibm_keycode_map,
- TPACPI_HOTKEY_MAP_SIZE);
- }
+ 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);
input_set_capability(tpacpi_inputdev, EV_MSC, MSC_SCAN);
tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE;
@@ -3387,19 +3380,14 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
}
/* Do not issue duplicate brightness change events to
- * userspace */
- if (!tp_features.bright_acpimode)
- /* update bright_acpimode... */
- tpacpi_check_std_acpi_brightness_support();
-
- if (tp_features.bright_acpimode && acpi_video_backlight_support()) {
- printk(TPACPI_INFO
- "This ThinkPad has standard ACPI backlight "
- "brightness control, supported by the ACPI "
- "video driver\n");
- printk(TPACPI_NOTICE
- "Disabling thinkpad-acpi brightness events "
- "by default...\n");
+ * userspace. tpacpi_detect_brightness_capabilities() must have
+ * been called before this point */
+ if (acpi_video_backlight_support()) {
+ pr_info("This ThinkPad has standard ACPI backlight "
+ "brightness control, supported by the ACPI "
+ "video driver\n");
+ pr_notice("Disabling thinkpad-acpi brightness events "
+ "by default...\n");
/* Disable brightness up/down on Lenovo thinkpads when
* ACPI is handling them, otherwise it is plain impossible
@@ -3441,11 +3429,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
"initial masks: user=0x%08x, fw=0x%08x, poll=0x%08x\n",
hotkey_user_mask, hotkey_acpi_mask, hotkey_source_mask);
- dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY,
- "legacy ibm/hotkey event reporting over procfs %s\n",
- (hotkey_report_mode < 2) ?
- "enabled" : "disabled");
-
tpacpi_inputdev->open = &hotkey_inputdev_open;
tpacpi_inputdev->close = &hotkey_inputdev_close;
@@ -3460,6 +3443,106 @@ err_exit:
return (res < 0)? res : 1;
}
+/* Thinkpad X1 Carbon support 5 modes including Home mode, Web browser
+ * mode, Web conference mode, Function mode and Lay-flat mode.
+ * We support Home mode and Function mode currently.
+ *
+ * Will consider support rest of modes in future.
+ *
+ */
+enum ADAPTIVE_KEY_MODE {
+ HOME_MODE,
+ WEB_BROWSER_MODE,
+ WEB_CONFERENCE_MODE,
+ FUNCTION_MODE,
+ LAYFLAT_MODE
+};
+
+const int adaptive_keyboard_modes[] = {
+ HOME_MODE,
+/* WEB_BROWSER_MODE = 2,
+ WEB_CONFERENCE_MODE = 3, */
+ FUNCTION_MODE
+};
+
+#define DFR_CHANGE_ROW 0x101
+#define DFR_SHOW_QUICKVIEW_ROW 0x102
+
+/* press Fn key a while second, it will switch to Function Mode. Then
+ * release Fn key, previous mode be restored.
+ */
+static bool adaptive_keyboard_mode_is_saved;
+static int adaptive_keyboard_prev_mode;
+
+static int adaptive_keyboard_get_next_mode(int mode)
+{
+ size_t i;
+ size_t max_mode = ARRAY_SIZE(adaptive_keyboard_modes) - 1;
+
+ for (i = 0; i <= max_mode; i++) {
+ if (adaptive_keyboard_modes[i] == mode)
+ break;
+ }
+
+ if (i >= max_mode)
+ i = 0;
+ else
+ i++;
+
+ return adaptive_keyboard_modes[i];
+}
+
+static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode)
+{
+ u32 current_mode = 0;
+ int new_mode = 0;
+
+ switch (scancode) {
+ case DFR_CHANGE_ROW:
+ if (adaptive_keyboard_mode_is_saved) {
+ new_mode = adaptive_keyboard_prev_mode;
+ adaptive_keyboard_mode_is_saved = false;
+ } else {
+ if (!acpi_evalf(
+ hkey_handle, &current_mode,
+ "GTRW", "dd", 0)) {
+ pr_err("Cannot read adaptive keyboard mode\n");
+ return false;
+ } else {
+ new_mode = adaptive_keyboard_get_next_mode(
+ current_mode);
+ }
+ }
+
+ if (!acpi_evalf(hkey_handle, NULL, "STRW", "vd", new_mode)) {
+ pr_err("Cannot set adaptive keyboard mode\n");
+ return false;
+ }
+
+ return true;
+
+ case DFR_SHOW_QUICKVIEW_ROW:
+ if (!acpi_evalf(hkey_handle,
+ &adaptive_keyboard_prev_mode,
+ "GTRW", "dd", 0)) {
+ pr_err("Cannot read adaptive keyboard mode\n");
+ return false;
+ } else {
+ adaptive_keyboard_mode_is_saved = true;
+
+ if (!acpi_evalf(hkey_handle,
+ NULL, "STRW", "vd", FUNCTION_MODE)) {
+ pr_err("Cannot set adaptive keyboard mode\n");
+ return false;
+ }
+ }
+ return true;
+
+ default:
+ return false;
+ }
+}
+
static bool hotkey_notify_hotkey(const u32 hkey,
bool *send_acpi_ev,
bool *ignore_acpi_ev)
@@ -3469,7 +3552,8 @@ static bool hotkey_notify_hotkey(const u32 hkey,
*send_acpi_ev = true;
*ignore_acpi_ev = false;
- if (scancode > 0 && scancode < 0x21) {
+ /* HKEY event 0x1001 is scancode 0x00 */
+ if (scancode > 0 && scancode <= TPACPI_HOTKEY_MAP_LEN) {
scancode--;
if (!(hotkey_source_mask & (1 << scancode))) {
tpacpi_input_send_key_masked(scancode);
@@ -3478,6 +3562,8 @@ static bool hotkey_notify_hotkey(const u32 hkey,
*ignore_acpi_ev = true;
}
return true;
+ } else {
+ return adaptive_keyboard_hotkey_notify_hotkey(scancode);
}
return false;
}
@@ -3505,8 +3591,7 @@ static bool hotkey_notify_wakeup(const u32 hkey,
case TP_HKEY_EV_WKUP_S3_BATLOW: /* Battery on critical low level/S3 */
case TP_HKEY_EV_WKUP_S4_BATLOW: /* Battery on critical low level/S4 */
- printk(TPACPI_ALERT
- "EMERGENCY WAKEUP: battery almost empty\n");
+ pr_alert("EMERGENCY WAKEUP: battery almost empty\n");
/* how to auto-heal: */
/* 2313: woke up from S3, go to S4/S5 */
/* 2413: woke up from S4, go to S5 */
@@ -3517,14 +3602,40 @@ static bool hotkey_notify_wakeup(const u32 hkey,
}
if (hotkey_wakeup_reason != TP_ACPI_WAKEUP_NONE) {
- printk(TPACPI_INFO
- "woke up due to a hot-unplug "
- "request...\n");
+ pr_info("woke up due to a hot-unplug request...\n");
hotkey_wakeup_reason_notify_change();
}
return true;
}
+static bool hotkey_notify_dockevent(const u32 hkey,
+ bool *send_acpi_ev,
+ bool *ignore_acpi_ev)
+{
+ /* 0x4000-0x4FFF: dock-related events */
+ *send_acpi_ev = true;
+ *ignore_acpi_ev = false;
+
+ switch (hkey) {
+ case TP_HKEY_EV_UNDOCK_ACK:
+ /* ACPI undock operation completed after wakeup */
+ hotkey_autosleep_ack = 1;
+ pr_info("undocked\n");
+ hotkey_wakeup_hotunplug_complete_notify_change();
+ return true;
+
+ case TP_HKEY_EV_HOTPLUG_DOCK: /* docked to port replicator */
+ pr_info("docked into hotplug port replicator\n");
+ return true;
+ case TP_HKEY_EV_HOTPLUG_UNDOCK: /* undocked from port replicator */
+ pr_info("undocked from hotplug port replicator\n");
+ return true;
+
+ default:
+ return false;
+ }
+}
+
static bool hotkey_notify_usrevent(const u32 hkey,
bool *send_acpi_ev,
bool *ignore_acpi_ev)
@@ -3559,49 +3670,58 @@ static bool hotkey_notify_usrevent(const u32 hkey,
static void thermal_dump_all_sensors(void);
-static bool hotkey_notify_thermal(const u32 hkey,
+static bool hotkey_notify_6xxx(const u32 hkey,
bool *send_acpi_ev,
bool *ignore_acpi_ev)
{
bool known = true;
- /* 0x6000-0x6FFF: thermal alarms */
+ /* 0x6000-0x6FFF: thermal alarms/notices and keyboard events */
*send_acpi_ev = true;
*ignore_acpi_ev = false;
switch (hkey) {
case TP_HKEY_EV_THM_TABLE_CHANGED:
- printk(TPACPI_INFO
- "EC reports that Thermal Table has changed\n");
+ pr_info("EC reports that Thermal Table has changed\n");
/* recommended action: do nothing, we don't have
* Lenovo ATM information */
return true;
case TP_HKEY_EV_ALARM_BAT_HOT:
- printk(TPACPI_CRIT
- "THERMAL ALARM: battery is too hot!\n");
+ pr_crit("THERMAL ALARM: battery is too hot!\n");
/* recommended action: warn user through gui */
break;
case TP_HKEY_EV_ALARM_BAT_XHOT:
- printk(TPACPI_ALERT
- "THERMAL EMERGENCY: battery is extremely hot!\n");
+ pr_alert("THERMAL EMERGENCY: battery is extremely hot!\n");
/* recommended action: immediate sleep/hibernate */
break;
case TP_HKEY_EV_ALARM_SENSOR_HOT:
- printk(TPACPI_CRIT
- "THERMAL ALARM: "
+ pr_crit("THERMAL ALARM: "
"a sensor reports something is too hot!\n");
/* recommended action: warn user through gui, that */
/* some internal component is too hot */
break;
case TP_HKEY_EV_ALARM_SENSOR_XHOT:
- printk(TPACPI_ALERT
- "THERMAL EMERGENCY: "
- "a sensor reports something is extremely hot!\n");
+ pr_alert("THERMAL EMERGENCY: "
+ "a sensor reports something is extremely hot!\n");
/* recommended action: immediate sleep/hibernate */
break;
+ case TP_HKEY_EV_AC_CHANGED:
+ /* X120e, X121e, X220, X220i, X220t, X230, T420, T420s, W520:
+ * AC status changed; can be triggered by plugging or
+ * unplugging AC adapter, docking or undocking. */
+
+ /* fallthrough */
+
+ case TP_HKEY_EV_KEY_NUMLOCK:
+ case TP_HKEY_EV_KEY_FN:
+ /* key press events, we just ignore them as long as the EC
+ * is still reporting them in the normal keyboard stream */
+ *send_acpi_ev = false;
+ *ignore_acpi_ev = true;
+ return true;
+
default:
- printk(TPACPI_ALERT
- "THERMAL ALERT: unknown thermal alarm received\n");
+ pr_warn("unknown possible thermal alarm or keyboard event received\n");
known = false;
}
@@ -3618,8 +3738,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
bool known_ev;
if (event != 0x80) {
- printk(TPACPI_ERR
- "unknown HKEY notification event %d\n", event);
+ pr_err("unknown HKEY notification event %d\n", event);
/* forward it to userspace, maybe it knows how to handle it */
acpi_bus_generate_netlink_event(
ibm->acpi->device->pnp.device_class,
@@ -3630,7 +3749,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
while (1) {
if (!acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) {
- printk(TPACPI_ERR "failed to retrieve HKEY event\n");
+ pr_err("failed to retrieve HKEY event\n");
return;
}
@@ -3655,27 +3774,25 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
break;
case 3:
/* 0x3000-0x3FFF: bay-related wakeups */
- if (hkey == TP_HKEY_EV_BAYEJ_ACK) {
+ switch (hkey) {
+ case TP_HKEY_EV_BAYEJ_ACK:
hotkey_autosleep_ack = 1;
- printk(TPACPI_INFO
- "bay ejected\n");
+ pr_info("bay ejected\n");
hotkey_wakeup_hotunplug_complete_notify_change();
known_ev = true;
- } else {
+ break;
+ case TP_HKEY_EV_OPTDRV_EJ:
+ /* FIXME: kick libata if SATA link offline */
+ known_ev = true;
+ break;
+ default:
known_ev = false;
}
break;
case 4:
- /* 0x4000-0x4FFF: dock-related wakeups */
- if (hkey == TP_HKEY_EV_UNDOCK_ACK) {
- hotkey_autosleep_ack = 1;
- printk(TPACPI_INFO
- "undocked\n");
- hotkey_wakeup_hotunplug_complete_notify_change();
- known_ev = true;
- } else {
- known_ev = false;
- }
+ /* 0x4000-0x4FFF: dock-related events */
+ known_ev = hotkey_notify_dockevent(hkey, &send_acpi_ev,
+ &ignore_acpi_ev);
break;
case 5:
/* 0x5000-0x5FFF: human interface helpers */
@@ -3683,8 +3800,9 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
&ignore_acpi_ev);
break;
case 6:
- /* 0x6000-0x6FFF: thermal alarms */
- known_ev = hotkey_notify_thermal(hkey, &send_acpi_ev,
+ /* 0x6000-0x6FFF: thermal alarms/notices and
+ * keyboard events */
+ known_ev = hotkey_notify_6xxx(hkey, &send_acpi_ev,
&ignore_acpi_ev);
break;
case 7:
@@ -3701,18 +3819,9 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
known_ev = false;
}
if (!known_ev) {
- printk(TPACPI_NOTICE
- "unhandled HKEY event 0x%04x\n", hkey);
- printk(TPACPI_NOTICE
- "please report the conditions when this "
- "event happened to %s\n", TPACPI_MAIL);
- }
-
- /* Legacy events */
- if (!ignore_acpi_ev &&
- (send_acpi_ev || hotkey_report_mode < 2)) {
- acpi_bus_generate_proc_event(ibm->acpi->device,
- event, hkey);
+ pr_notice("unhandled HKEY event 0x%04x\n", hkey);
+ pr_notice("please report the conditions when this "
+ "event happened to %s\n", TPACPI_MAIL);
}
/* netlink events */
@@ -3725,21 +3834,35 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
}
}
-static void hotkey_suspend(pm_message_t state)
+static void hotkey_suspend(void)
{
+ int hkeyv;
+
/* Do these on suspend, we get the events on early resume! */
hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE;
hotkey_autosleep_ack = 0;
+
+ /* save previous mode of adaptive keyboard of X1 Carbon */
+ if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
+ if ((hkeyv >> 8) == 2) {
+ if (!acpi_evalf(hkey_handle,
+ &adaptive_keyboard_prev_mode,
+ "GTRW", "dd", 0)) {
+ pr_err("Cannot read adaptive keyboard mode.\n");
+ }
+ }
+ }
}
static void hotkey_resume(void)
{
+ int hkeyv;
+
tpacpi_disable_brightness_delay();
if (hotkey_status_set(true) < 0 ||
hotkey_mask_set(hotkey_acpi_mask) < 0)
- printk(TPACPI_ERR
- "error while attempting to reset the event "
+ pr_err("error while attempting to reset the event "
"firmware interface\n");
tpacpi_send_radiosw_update();
@@ -3747,6 +3870,18 @@ static void hotkey_resume(void)
hotkey_wakeup_reason_notify_change();
hotkey_wakeup_hotunplug_complete_notify_change();
hotkey_poll_setup_safe(false);
+
+ /* restore previous mode of adapive keyboard of X1 Carbon */
+ if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
+ if ((hkeyv >> 8) == 2) {
+ if (!acpi_evalf(hkey_handle,
+ NULL,
+ "STRW", "vd",
+ adaptive_keyboard_prev_mode)) {
+ pr_err("Cannot set adaptive keyboard mode.\n");
+ }
+ }
+ }
}
/* procfs -------------------------------------------------------------- */
@@ -3784,14 +3919,12 @@ static void hotkey_enabledisable_warn(bool enable)
{
tpacpi_log_usertask("procfs hotkey enable/disable");
if (!WARN((tpacpi_lifecycle == TPACPI_LIFE_RUNNING || !enable),
- TPACPI_WARN
- "hotkey enable/disable functionality has been "
- "removed from the driver. Hotkeys are always "
- "enabled\n"))
- printk(TPACPI_ERR
- "Please remove the hotkey=enable module "
- "parameter, it is deprecated. Hotkeys are always "
- "enabled\n");
+ pr_fmt("hotkey enable/disable functionality has been "
+ "removed from the driver. "
+ "Hotkeys are always enabled.\n")))
+ pr_err("Please remove the hotkey=enable module "
+ "parameter, it is deprecated. "
+ "Hotkeys are always enabled.\n");
}
static int hotkey_write(char *buf)
@@ -3840,7 +3973,8 @@ errexit:
}
static const struct acpi_device_id ibm_htk_device_ids[] = {
- {TPACPI_ACPI_HKEY_HID, 0},
+ {TPACPI_ACPI_IBM_HKEY_HID, 0},
+ {TPACPI_ACPI_LENOVO_HKEY_HID, 0},
{"", 0},
};
@@ -3870,7 +4004,7 @@ enum {
TP_ACPI_BLUETOOTH_HWPRESENT = 0x01, /* Bluetooth hw available */
TP_ACPI_BLUETOOTH_RADIOSSW = 0x02, /* Bluetooth radio enabled */
TP_ACPI_BLUETOOTH_RESUMECTRL = 0x04, /* Bluetooth state at resume:
- off / last state */
+ 0 = disable, 1 = enable */
};
enum {
@@ -3916,10 +4050,11 @@ static int bluetooth_set_status(enum tpacpi_rfkill_state state)
}
#endif
- /* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */
- status = TP_ACPI_BLUETOOTH_RESUMECTRL;
if (state == TPACPI_RFK_RADIO_ON)
- status |= TP_ACPI_BLUETOOTH_RADIOSSW;
+ status = TP_ACPI_BLUETOOTH_RADIOSSW
+ | TP_ACPI_BLUETOOTH_RESUMECTRL;
+ else
+ status = 0;
if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
return -EIO;
@@ -3969,11 +4104,10 @@ static void bluetooth_shutdown(void)
/* Order firmware to save current state to NVRAM */
if (!acpi_evalf(NULL, NULL, "\\BLTH", "vd",
TP_ACPI_BLTH_SAVE_STATE))
- printk(TPACPI_NOTICE
- "failed to save bluetooth state to NVRAM\n");
+ pr_notice("failed to save bluetooth state to NVRAM\n");
else
vdbg_printk(TPACPI_DBG_RFKILL,
- "bluestooth state saved to NVRAM\n");
+ "bluetooth state saved to NVRAM\n");
}
static void bluetooth_exit(void)
@@ -4009,8 +4143,7 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm)
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if (dbg_bluetoothemul) {
tp_features.bluetooth = 1;
- printk(TPACPI_INFO
- "bluetooth switch emulation enabled\n");
+ pr_info("bluetooth switch emulation enabled\n");
} else
#endif
if (tp_features.bluetooth &&
@@ -4070,7 +4203,7 @@ enum {
TP_ACPI_WANCARD_HWPRESENT = 0x01, /* Wan hw available */
TP_ACPI_WANCARD_RADIOSSW = 0x02, /* Wan radio enabled */
TP_ACPI_WANCARD_RESUMECTRL = 0x04, /* Wan state at resume:
- off / last state */
+ 0 = disable, 1 = enable */
};
#define TPACPI_RFK_WWAN_SW_NAME "tpacpi_wwan_sw"
@@ -4107,10 +4240,11 @@ static int wan_set_status(enum tpacpi_rfkill_state state)
}
#endif
- /* We make sure to set TP_ACPI_WANCARD_RESUMECTRL */
- status = TP_ACPI_WANCARD_RESUMECTRL;
if (state == TPACPI_RFK_RADIO_ON)
- status |= TP_ACPI_WANCARD_RADIOSSW;
+ status = TP_ACPI_WANCARD_RADIOSSW
+ | TP_ACPI_WANCARD_RESUMECTRL;
+ else
+ status = 0;
if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
return -EIO;
@@ -4160,8 +4294,7 @@ static void wan_shutdown(void)
/* Order firmware to save current state to NVRAM */
if (!acpi_evalf(NULL, NULL, "\\WGSV", "vd",
TP_ACPI_WGSV_SAVE_STATE))
- printk(TPACPI_NOTICE
- "failed to save WWAN state to NVRAM\n");
+ pr_notice("failed to save WWAN state to NVRAM\n");
else
vdbg_printk(TPACPI_DBG_RFKILL,
"WWAN state saved to NVRAM\n");
@@ -4198,8 +4331,7 @@ static int __init wan_init(struct ibm_init_struct *iibm)
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if (dbg_wwanemul) {
tp_features.wan = 1;
- printk(TPACPI_INFO
- "wwan switch emulation enabled\n");
+ pr_info("wwan switch emulation enabled\n");
} else
#endif
if (tp_features.wan &&
@@ -4339,8 +4471,7 @@ static int __init uwb_init(struct ibm_init_struct *iibm)
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if (dbg_uwbemul) {
tp_features.uwb = 1;
- printk(TPACPI_INFO
- "uwb switch emulation enabled\n");
+ pr_info("uwb switch emulation enabled\n");
} else
#endif
if (tp_features.uwb &&
@@ -4401,6 +4532,15 @@ static int video_orig_autosw;
static int video_autosw_get(void);
static int video_autosw_set(int enable);
+TPACPI_HANDLE(vid, root,
+ "\\_SB.PCI.AGP.VGA", /* 570 */
+ "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */
+ "\\_SB.PCI0.VID0", /* 770e */
+ "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */
+ "\\_SB.PCI0.AGP.VGA", /* X100e and a few others */
+ "\\_SB.PCI0.AGP.VID", /* all others */
+ ); /* R30, R31 */
+
TPACPI_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID"); /* G41 */
static int __init video_init(struct ibm_init_struct *iibm)
@@ -4410,7 +4550,8 @@ static int __init video_init(struct ibm_init_struct *iibm)
vdbg_printk(TPACPI_DBG_INIT, "initializing video subdriver\n");
TPACPI_ACPIHANDLE_INIT(vid);
- TPACPI_ACPIHANDLE_INIT(vid2);
+ if (tpacpi_is_ibm())
+ TPACPI_ACPIHANDLE_INIT(vid2);
if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga)
/* G41, assume IVGA doesn't change */
@@ -4419,10 +4560,12 @@ static int __init video_init(struct ibm_init_struct *iibm)
if (!vid_handle)
/* video switching not supported on R30, R31 */
video_supported = TPACPI_VIDEO_NONE;
- else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd"))
+ else if (tpacpi_is_ibm() &&
+ acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd"))
/* 570 */
video_supported = TPACPI_VIDEO_570;
- else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd"))
+ else if (tpacpi_is_ibm() &&
+ acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd"))
/* 600e/x, 770e, 770x */
video_supported = TPACPI_VIDEO_770;
else
@@ -4441,7 +4584,7 @@ static void video_exit(void)
dbg_printk(TPACPI_DBG_EXIT,
"restoring original video autoswitch mode\n");
if (video_autosw_set(video_orig_autosw))
- printk(TPACPI_ERR "error while trying to restore original "
+ pr_err("error while trying to restore original "
"video autoswitch mode\n");
}
@@ -4514,8 +4657,7 @@ static int video_outputsw_set(int status)
res = acpi_evalf(vid_handle, NULL,
"ASWT", "vdd", status * 0x100, 0);
if (!autosw && video_autosw_set(autosw)) {
- printk(TPACPI_ERR
- "video auto-switch left enabled due to error\n");
+ pr_err("video auto-switch left enabled due to error\n");
return -EIO;
}
break;
@@ -4584,8 +4726,7 @@ static int video_outputsw_cycle(void)
return -ENOSYS;
}
if (!autosw && video_autosw_set(autosw)) {
- printk(TPACPI_ERR
- "video auto-switch left enabled due to error\n");
+ pr_err("video auto-switch left enabled due to error\n");
return -EIO;
}
@@ -4619,6 +4760,10 @@ static int video_read(struct seq_file *m)
return 0;
}
+ /* Even reads can crash X.org, so... */
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
status = video_outputsw_get();
if (status < 0)
return status;
@@ -4652,6 +4797,10 @@ static int video_write(char *buf)
if (video_supported == TPACPI_VIDEO_NONE)
return -ENODEV;
+ /* Even reads can crash X.org, let alone writes... */
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
enable = 0;
disable = 0;
@@ -4791,8 +4940,10 @@ static int __init light_init(struct ibm_init_struct *iibm)
vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n");
- TPACPI_ACPIHANDLE_INIT(ledb);
- TPACPI_ACPIHANDLE_INIT(lght);
+ if (tpacpi_is_ibm()) {
+ TPACPI_ACPIHANDLE_INIT(ledb);
+ TPACPI_ACPIHANDLE_INIT(lght);
+ }
TPACPI_ACPIHANDLE_INIT(cmos);
INIT_WORK(&tpacpi_led_thinklight.work, light_set_status_worker);
@@ -4828,8 +4979,7 @@ static int __init light_init(struct ibm_init_struct *iibm)
static void light_exit(void)
{
led_classdev_unregister(&tpacpi_led_thinklight.led_classdev);
- if (work_pending(&tpacpi_led_thinklight.work))
- flush_workqueue(tpacpi_wq);
+ flush_workqueue(tpacpi_wq);
}
static int light_read(struct seq_file *m)
@@ -4987,11 +5137,7 @@ enum { /* For TPACPI_LED_OLD */
static enum led_access_mode led_supported;
-TPACPI_HANDLE(led, ec, "SLED", /* 570 */
- "SYSL", /* 600e/x, 770e, 770x, A21e, A2xm/p, */
- /* T20-22, X20-21 */
- "LED", /* all others */
- ); /* R30, R31 */
+static acpi_handle led_handle;
#define TPACPI_LED_NUMLEDS 16
static struct tpacpi_led_classdev *tpacpi_leds;
@@ -5172,6 +5318,7 @@ static void led_exit(void)
led_classdev_unregister(&tpacpi_leds[i].led_classdev);
}
+ flush_workqueue(tpacpi_wq);
kfree(tpacpi_leds);
}
@@ -5251,6 +5398,32 @@ static const struct tpacpi_quirk led_useful_qtable[] __initconst = {
#undef TPACPI_LEDQ_IBM
#undef TPACPI_LEDQ_LNV
+static enum led_access_mode __init led_init_detect_mode(void)
+{
+ acpi_status status;
+
+ if (tpacpi_is_ibm()) {
+ /* 570 */
+ status = acpi_get_handle(ec_handle, "SLED", &led_handle);
+ if (ACPI_SUCCESS(status))
+ return TPACPI_LED_570;
+
+ /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
+ status = acpi_get_handle(ec_handle, "SYSL", &led_handle);
+ if (ACPI_SUCCESS(status))
+ return TPACPI_LED_OLD;
+ }
+
+ /* most others */
+ status = acpi_get_handle(ec_handle, "LED", &led_handle);
+ if (ACPI_SUCCESS(status))
+ return TPACPI_LED_NEW;
+
+ /* R30, R31, and unknown firmwares */
+ led_handle = NULL;
+ return TPACPI_LED_NONE;
+}
+
static int __init led_init(struct ibm_init_struct *iibm)
{
unsigned int i;
@@ -5259,20 +5432,17 @@ static int __init led_init(struct ibm_init_struct *iibm)
vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n");
- TPACPI_ACPIHANDLE_INIT(led);
+ led_supported = led_init_detect_mode();
- if (!led_handle)
- /* led not supported on R30, R31 */
- led_supported = TPACPI_LED_NONE;
- else if (strlencmp(led_path, "SLED") == 0)
- /* 570 */
- led_supported = TPACPI_LED_570;
- else if (strlencmp(led_path, "SYSL") == 0)
- /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
- led_supported = TPACPI_LED_OLD;
- else
- /* all others */
- led_supported = TPACPI_LED_NEW;
+ if (led_supported != TPACPI_LED_NONE) {
+ useful_leds = tpacpi_check_quirks(led_useful_qtable,
+ ARRAY_SIZE(led_useful_qtable));
+
+ if (!useful_leds) {
+ led_handle = NULL;
+ led_supported = TPACPI_LED_NONE;
+ }
+ }
vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n",
str_supported(led_supported), led_supported);
@@ -5283,14 +5453,13 @@ static int __init led_init(struct ibm_init_struct *iibm)
tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS,
GFP_KERNEL);
if (!tpacpi_leds) {
- printk(TPACPI_ERR "Out of memory for LED data\n");
+ pr_err("Out of memory for LED data\n");
return -ENOMEM;
}
- useful_leds = tpacpi_check_quirks(led_useful_qtable,
- ARRAY_SIZE(led_useful_qtable));
-
for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
+ tpacpi_leds[i].led = -1;
+
if (!tpacpi_is_led_restricted(i) &&
test_bit(i, &useful_leds)) {
rc = tpacpi_init_led(i);
@@ -5302,9 +5471,8 @@ static int __init led_init(struct ibm_init_struct *iibm)
}
#ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS
- printk(TPACPI_NOTICE
- "warning: userspace override of important "
- "firmware LEDs is enabled\n");
+ pr_notice("warning: userspace override of important "
+ "firmware LEDs is enabled\n");
#endif
return 0;
}
@@ -5349,9 +5517,13 @@ static int led_write(char *buf)
return -ENODEV;
while ((cmd = next_cmd(&buf))) {
- if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 15)
+ if (sscanf(cmd, "%d", &led) != 1)
return -EINVAL;
+ if (led < 0 || led > (TPACPI_LED_NUMLEDS - 1) ||
+ tpacpi_leds[led].led < 0)
+ return -ENODEV;
+
if (strstr(cmd, "off")) {
s = TPACPI_LED_OFF;
} else if (strstr(cmd, "on")) {
@@ -5574,17 +5746,16 @@ static void thermal_dump_all_sensors(void)
if (n <= 0)
return;
- printk(TPACPI_NOTICE
- "temperatures (Celsius):");
+ pr_notice("temperatures (Celsius):");
for (i = 0; i < n; i++) {
if (t.temp[i] != TPACPI_THERMAL_SENSOR_NA)
- printk(KERN_CONT " %d", (int)(t.temp[i] / 1000));
+ pr_cont(" %d", (int)(t.temp[i] / 1000));
else
- printk(KERN_CONT " N/A");
+ pr_cont(" N/A");
}
- printk(KERN_CONT "\n");
+ pr_cont("\n");
}
/* sysfs temp##_input -------------------------------------------------- */
@@ -5704,14 +5875,12 @@ static int __init thermal_init(struct ibm_init_struct *iibm)
if (ta1 == 0) {
/* This is sheer paranoia, but we handle it anyway */
if (acpi_tmp7) {
- printk(TPACPI_ERR
- "ThinkPad ACPI EC access misbehaving, "
+ pr_err("ThinkPad ACPI EC access misbehaving, "
"falling back to ACPI TMPx access "
"mode\n");
thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
} else {
- printk(TPACPI_ERR
- "ThinkPad ACPI EC access misbehaving, "
+ pr_err("ThinkPad ACPI EC access misbehaving, "
"disabling thermal sensors access\n");
thermal_read_mode = TPACPI_THERMAL_NONE;
}
@@ -5721,11 +5890,12 @@ static int __init thermal_init(struct ibm_init_struct *iibm)
TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8;
}
} else if (acpi_tmp7) {
- if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) {
+ if (tpacpi_is_ibm() &&
+ acpi_evalf(ec_handle, NULL, "UPDT", "qv")) {
/* 600e/x, 770e, 770x */
thermal_read_mode = TPACPI_THERMAL_ACPI_UPDT;
} else {
- /* Standard ACPI TMPx access, max 8 sensors */
+ /* IBM/LENOVO DSDT EC.TMPx access, max 8 sensors */
thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
}
} else {
@@ -5771,7 +5941,7 @@ static void thermal_exit(void)
case TPACPI_THERMAL_ACPI_TMP07:
case TPACPI_THERMAL_ACPI_UPDT:
sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
- &thermal_temp_input16_group);
+ &thermal_temp_input8_group);
break;
case TPACPI_THERMAL_NONE:
default:
@@ -5807,75 +5977,6 @@ static struct ibm_struct thermal_driver_data = {
};
/*************************************************************************
- * EC Dump subdriver
- */
-
-static u8 ecdump_regs[256];
-
-static int ecdump_read(struct seq_file *m)
-{
- int i, j;
- u8 v;
-
- seq_printf(m, "EC "
- " +00 +01 +02 +03 +04 +05 +06 +07"
- " +08 +09 +0a +0b +0c +0d +0e +0f\n");
- for (i = 0; i < 256; i += 16) {
- seq_printf(m, "EC 0x%02x:", i);
- for (j = 0; j < 16; j++) {
- if (!acpi_ec_read(i + j, &v))
- break;
- if (v != ecdump_regs[i + j])
- seq_printf(m, " *%02x", v);
- else
- seq_printf(m, " %02x", v);
- ecdump_regs[i + j] = v;
- }
- seq_putc(m, '\n');
- if (j != 16)
- break;
- }
-
- /* These are way too dangerous to advertise openly... */
-#if 0
- seq_printf(m, "commands:\t0x<offset> 0x<value>"
- " (<offset> is 00-ff, <value> is 00-ff)\n");
- seq_printf(m, "commands:\t0x<offset> <value> "
- " (<offset> is 00-ff, <value> is 0-255)\n");
-#endif
- return 0;
-}
-
-static int ecdump_write(char *buf)
-{
- char *cmd;
- int i, v;
-
- while ((cmd = next_cmd(&buf))) {
- if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) {
- /* i and v set */
- } else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) {
- /* i and v set */
- } else
- return -EINVAL;
- if (i >= 0 && i < 256 && v >= 0 && v < 256) {
- if (!acpi_ec_write(i, v))
- return -EIO;
- } else
- return -EINVAL;
- }
-
- return 0;
-}
-
-static struct ibm_struct ecdump_driver_data = {
- .name = "ecdump",
- .read = ecdump_read,
- .write = ecdump_write,
- .flags.experimental = 1,
-};
-
-/*************************************************************************
* Backlight/brightness subdriver
*/
@@ -5934,7 +6035,7 @@ static unsigned int tpacpi_brightness_nvram_get(void)
lnvram = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS)
& TP_NVRAM_MASK_LEVEL_BRIGHTNESS)
>> TP_NVRAM_POS_LEVEL_BRIGHTNESS;
- lnvram &= (tp_features.bright_16levels) ? 0x0f : 0x07;
+ lnvram &= bright_maxlvl;
return lnvram;
}
@@ -6043,8 +6144,7 @@ static int brightness_set(unsigned int value)
{
int res;
- if (value > ((tp_features.bright_16levels)? 15 : 7) ||
- value < 0)
+ if (value > bright_maxlvl || value < 0)
return -EINVAL;
vdbg_printk(TPACPI_DBG_BRGHT,
@@ -6112,7 +6212,7 @@ static void tpacpi_brightness_notify_change(void)
BACKLIGHT_UPDATE_HOTKEY);
}
-static struct backlight_ops ibm_backlight_data = {
+static const struct backlight_ops ibm_backlight_data = {
.get_brightness = brightness_get,
.update_status = brightness_update_status,
};
@@ -6120,6 +6220,61 @@ static struct backlight_ops ibm_backlight_data = {
/* --------------------------------------------------------------------- */
/*
+ * Call _BCL method of video device. On some ThinkPads this will
+ * switch the firmware to the ACPI brightness control mode.
+ */
+
+static int __init tpacpi_query_bcl_levels(acpi_handle handle)
+{
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *obj;
+ struct acpi_device *device, *child;
+ int rc;
+
+ if (acpi_bus_get_device(handle, &device))
+ return 0;
+
+ rc = 0;
+ list_for_each_entry(child, &device->children, node) {
+ acpi_status status = acpi_evaluate_object(child->handle, "_BCL",
+ NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ continue;
+
+ obj = (union acpi_object *)buffer.pointer;
+ if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
+ pr_err("Unknown _BCL data, please report this to %s\n",
+ TPACPI_MAIL);
+ rc = 0;
+ } else {
+ rc = obj->package.count;
+ }
+ break;
+ }
+
+ kfree(buffer.pointer);
+ return rc;
+}
+
+
+/*
+ * Returns 0 (no ACPI _BCL or _BCL invalid), or size of brightness map
+ */
+static unsigned int __init tpacpi_check_std_acpi_brightness_support(void)
+{
+ acpi_handle video_device;
+ int bcl_levels = 0;
+
+ tpacpi_acpi_handle_locate("video", NULL, &video_device);
+ if (video_device)
+ bcl_levels = tpacpi_query_bcl_levels(video_device);
+
+ tp_features.bright_acpimode = (bcl_levels > 0);
+
+ return (bcl_levels > 2) ? (bcl_levels - 2) : 0;
+}
+
+/*
* These are only useful for models that have only one possibility
* of GPU. If the BIOS model handles both ATI and Intel, don't use
* these quirks.
@@ -6133,13 +6288,13 @@ static const struct tpacpi_quirk brightness_quirk_table[] __initconst = {
TPACPI_Q_IBM('1', 'Y', TPACPI_BRGHT_Q_EC), /* T43/p ATI */
/* Models with ATI GPUs that can use ECNVRAM */
- TPACPI_Q_IBM('1', 'R', TPACPI_BRGHT_Q_EC),
+ TPACPI_Q_IBM('1', 'R', TPACPI_BRGHT_Q_EC), /* R50,51 T40-42 */
TPACPI_Q_IBM('1', 'Q', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC),
- TPACPI_Q_IBM('7', '6', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC),
+ TPACPI_Q_IBM('7', '6', TPACPI_BRGHT_Q_EC), /* R52 */
TPACPI_Q_IBM('7', '8', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC),
/* Models with Intel Extreme Graphics 2 */
- TPACPI_Q_IBM('1', 'U', TPACPI_BRGHT_Q_NOEC),
+ TPACPI_Q_IBM('1', 'U', TPACPI_BRGHT_Q_NOEC), /* X40 */
TPACPI_Q_IBM('1', 'V', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC),
TPACPI_Q_IBM('1', 'W', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC),
@@ -6149,8 +6304,47 @@ static const struct tpacpi_quirk brightness_quirk_table[] __initconst = {
TPACPI_Q_IBM('7', '5', TPACPI_BRGHT_Q_NOEC), /* X41 Tablet */
};
+/*
+ * Returns < 0 for error, otherwise sets tp_features.bright_*
+ * and bright_maxlvl.
+ */
+static void __init tpacpi_detect_brightness_capabilities(void)
+{
+ unsigned int b;
+
+ vdbg_printk(TPACPI_DBG_INIT,
+ "detecting firmware brightness interface capabilities\n");
+
+ /* we could run a quirks check here (same table used by
+ * brightness_init) if needed */
+
+ /*
+ * We always attempt to detect acpi support, so as to switch
+ * Lenovo Vista BIOS to ACPI brightness mode even if we are not
+ * going to publish a backlight interface
+ */
+ b = tpacpi_check_std_acpi_brightness_support();
+ switch (b) {
+ case 16:
+ bright_maxlvl = 15;
+ pr_info("detected a 16-level brightness capable ThinkPad\n");
+ break;
+ case 8:
+ case 0:
+ bright_maxlvl = 7;
+ pr_info("detected a 8-level brightness capable ThinkPad\n");
+ break;
+ default:
+ pr_err("Unsupported brightness interface, "
+ "please contact %s\n", TPACPI_MAIL);
+ tp_features.bright_unkfw = 1;
+ bright_maxlvl = b - 1;
+ }
+}
+
static int __init brightness_init(struct ibm_init_struct *iibm)
{
+ struct backlight_properties props;
int b;
unsigned long quirks;
@@ -6161,34 +6355,11 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
quirks = tpacpi_check_quirks(brightness_quirk_table,
ARRAY_SIZE(brightness_quirk_table));
- /*
- * We always attempt to detect acpi support, so as to switch
- * Lenovo Vista BIOS to ACPI brightness mode even if we are not
- * going to publish a backlight interface
- */
- b = tpacpi_check_std_acpi_brightness_support();
- if (b > 0) {
-
- if (acpi_video_backlight_support()) {
- if (brightness_enable > 1) {
- printk(TPACPI_NOTICE
- "Standard ACPI backlight interface "
- "available, not loading native one.\n");
- return 1;
- } else if (brightness_enable == 1) {
- printk(TPACPI_NOTICE
- "Backlight control force enabled, even if standard "
- "ACPI backlight interface is available\n");
- }
- } else {
- if (brightness_enable > 1) {
- printk(TPACPI_NOTICE
- "Standard ACPI backlight interface not "
- "available, thinkpad_acpi native "
- "brightness control enabled\n");
- }
- }
- }
+ /* tpacpi_detect_brightness_capabilities() must have run already */
+
+ /* if it is unknown, we don't handle it: it wouldn't be safe */
+ if (tp_features.bright_unkfw)
+ return 1;
if (!brightness_enable) {
dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT,
@@ -6197,14 +6368,22 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
return 1;
}
- if (b > 16) {
- printk(TPACPI_ERR
- "Unsupported brightness interface, "
- "please contact %s\n", TPACPI_MAIL);
- return 1;
+ if (acpi_video_backlight_support()) {
+ if (brightness_enable > 1) {
+ pr_info("Standard ACPI backlight interface "
+ "available, not loading native one\n");
+ return 1;
+ } else if (brightness_enable == 1) {
+ 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");
}
- if (b == 16)
- tp_features.bright_16levels = 1;
/*
* Check for module parameter bogosity, note that we
@@ -6228,7 +6407,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
}
/* Safety */
- if (thinkpad_id.vendor != PCI_VENDOR_ID_IBM &&
+ if (!tpacpi_is_ibm() &&
(brightness_mode == TPACPI_BRGHT_MODE_ECNVRAM ||
brightness_mode == TPACPI_BRGHT_MODE_EC))
return -EINVAL;
@@ -6236,34 +6415,34 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
if (tpacpi_brightness_get_raw(&b) < 0)
return 1;
- if (tp_features.bright_16levels)
- printk(TPACPI_INFO
- "detected a 16-level brightness capable ThinkPad\n");
-
- ibm_backlight_device = backlight_device_register(
- TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL,
- &ibm_backlight_data);
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_PLATFORM;
+ props.max_brightness = bright_maxlvl;
+ props.brightness = b & TP_EC_BACKLIGHT_LVLMSK;
+ ibm_backlight_device = backlight_device_register(TPACPI_BACKLIGHT_DEV_NAME,
+ NULL, NULL,
+ &ibm_backlight_data,
+ &props);
if (IS_ERR(ibm_backlight_device)) {
int rc = PTR_ERR(ibm_backlight_device);
ibm_backlight_device = NULL;
- printk(TPACPI_ERR "Could not register backlight device\n");
+ pr_err("Could not register backlight device\n");
return rc;
}
vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT,
"brightness is supported\n");
if (quirks & TPACPI_BRGHT_Q_ASK) {
- printk(TPACPI_NOTICE
- "brightness: will use unverified default: "
- "brightness_mode=%d\n", brightness_mode);
- printk(TPACPI_NOTICE
- "brightness: please report to %s whether it works well "
- "or not on your ThinkPad\n", TPACPI_MAIL);
+ pr_notice("brightness: will use unverified default: "
+ "brightness_mode=%d\n", brightness_mode);
+ pr_notice("brightness: please report to %s whether it works well "
+ "or not on your ThinkPad\n", TPACPI_MAIL);
}
- ibm_backlight_device->props.max_brightness =
- (tp_features.bright_16levels)? 15 : 7;
- ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK;
+ /* Added by mistake in early 2007. Probably useless, but it could
+ * be working around some unknown firmware problem where the value
+ * read at startup doesn't match the real hardware state... so leave
+ * it in place just in case */
backlight_update_status(ibm_backlight_device);
vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT,
@@ -6271,11 +6450,11 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
"as change notification\n");
tpacpi_hotkey_driver_mask_set(hotkey_driver_mask
| TP_ACPI_HKEY_BRGHTUP_MASK
- | TP_ACPI_HKEY_BRGHTDWN_MASK);;
+ | TP_ACPI_HKEY_BRGHTDWN_MASK);
return 0;
}
-static void brightness_suspend(pm_message_t state)
+static void brightness_suspend(void)
{
tpacpi_brightness_checkpoint_nvram();
}
@@ -6306,9 +6485,8 @@ static int brightness_read(struct seq_file *m)
} else {
seq_printf(m, "level:\t\t%d\n", level);
seq_printf(m, "commands:\tup, down\n");
- seq_printf(m, "commands:\tlevel <level>"
- " (<level> is 0-%d)\n",
- (tp_features.bright_16levels) ? 15 : 7);
+ seq_printf(m, "commands:\tlevel <level> (<level> is 0-%d)\n",
+ bright_maxlvl);
}
return 0;
@@ -6319,7 +6497,6 @@ static int brightness_write(char *buf)
int level;
int rc;
char *cmd;
- int max_level = (tp_features.bright_16levels) ? 15 : 7;
level = brightness_get(NULL);
if (level < 0)
@@ -6327,13 +6504,13 @@ static int brightness_write(char *buf)
while ((cmd = next_cmd(&buf))) {
if (strlencmp(cmd, "up") == 0) {
- if (level < max_level)
+ if (level < bright_maxlvl)
level++;
} else if (strlencmp(cmd, "down") == 0) {
if (level > 0)
level--;
} else if (sscanf(cmd, "level %d", &level) == 1 &&
- level >= 0 && level <= max_level) {
+ level >= 0 && level <= bright_maxlvl) {
/* new level set */
} else
return -EINVAL;
@@ -6390,9 +6567,14 @@ static struct ibm_struct brightness_driver_data = {
#define TPACPI_ALSA_SHRTNAME "ThinkPad Console Audio Control"
#define TPACPI_ALSA_MIXERNAME TPACPI_ALSA_SHRTNAME
-static int alsa_index = ~((1 << (SNDRV_CARDS - 3)) - 1); /* last three slots */
+#if SNDRV_CARDS <= 32
+#define DEFAULT_ALSA_IDX ~((1 << (SNDRV_CARDS - 3)) - 1)
+#else
+#define DEFAULT_ALSA_IDX ~((1 << (32 - 3)) - 1)
+#endif
+static int alsa_index = DEFAULT_ALSA_IDX; /* last three slots */
static char *alsa_id = "ThinkPadEC";
-static int alsa_enable = SNDRV_DEFAULT_ENABLE1;
+static bool alsa_enable = SNDRV_DEFAULT_ENABLE1;
struct tpacpi_alsa_data {
struct snd_card *card;
@@ -6435,7 +6617,7 @@ static enum tpacpi_volume_access_mode volume_mode =
TPACPI_VOL_MODE_MAX;
static enum tpacpi_volume_capabilities volume_capabilities;
-static int volume_control_allowed;
+static bool volume_control_allowed;
/*
* Used to syncronize writers to TP_EC_AUDIO and
@@ -6522,7 +6704,8 @@ static int volume_set_status(const u8 status)
return volume_set_status_ec(status);
}
-static int volume_set_mute_ec(const bool mute)
+/* returns < 0 on error, 0 on no change, 1 on change */
+static int __volume_set_mute_ec(const bool mute)
{
int rc;
u8 s, n;
@@ -6537,22 +6720,37 @@ static int volume_set_mute_ec(const bool mute)
n = (mute) ? s | TP_EC_AUDIO_MUTESW_MSK :
s & ~TP_EC_AUDIO_MUTESW_MSK;
- if (n != s)
+ if (n != s) {
rc = volume_set_status_ec(n);
+ if (!rc)
+ rc = 1;
+ }
unlock:
mutex_unlock(&volume_mutex);
return rc;
}
+static int volume_alsa_set_mute(const bool mute)
+{
+ dbg_printk(TPACPI_DBG_MIXER, "ALSA: trying to %smute\n",
+ (mute) ? "" : "un");
+ return __volume_set_mute_ec(mute);
+}
+
static int volume_set_mute(const bool mute)
{
+ int rc;
+
dbg_printk(TPACPI_DBG_MIXER, "trying to %smute\n",
(mute) ? "" : "un");
- return volume_set_mute_ec(mute);
+
+ rc = __volume_set_mute_ec(mute);
+ return (rc < 0) ? rc : 0;
}
-static int volume_set_volume_ec(const u8 vol)
+/* returns < 0 on error, 0 on no change, 1 on change */
+static int __volume_set_volume_ec(const u8 vol)
{
int rc;
u8 s, n;
@@ -6569,19 +6767,22 @@ static int volume_set_volume_ec(const u8 vol)
n = (s & ~TP_EC_AUDIO_LVL_MSK) | vol;
- if (n != s)
+ if (n != s) {
rc = volume_set_status_ec(n);
+ if (!rc)
+ rc = 1;
+ }
unlock:
mutex_unlock(&volume_mutex);
return rc;
}
-static int volume_set_volume(const u8 vol)
+static int volume_alsa_set_volume(const u8 vol)
{
dbg_printk(TPACPI_DBG_MIXER,
- "trying to set volume level to %hu\n", vol);
- return volume_set_volume_ec(vol);
+ "ALSA: trying to set volume level to %hu\n", vol);
+ return __volume_set_volume_ec(vol);
}
static void volume_alsa_notify_change(void)
@@ -6628,7 +6829,9 @@ static int volume_alsa_vol_get(struct snd_kcontrol *kcontrol,
static int volume_alsa_vol_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- return volume_set_volume(ucontrol->value.integer.value[0]);
+ tpacpi_disclose_usertask("ALSA", "set volume to %ld\n",
+ ucontrol->value.integer.value[0]);
+ return volume_alsa_set_volume(ucontrol->value.integer.value[0]);
}
#define volume_alsa_mute_info snd_ctl_boolean_mono_info
@@ -6651,10 +6854,13 @@ static int volume_alsa_mute_get(struct snd_kcontrol *kcontrol,
static int volume_alsa_mute_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- return volume_set_mute(!ucontrol->value.integer.value[0]);
+ tpacpi_disclose_usertask("ALSA", "%smute\n",
+ ucontrol->value.integer.value[0] ?
+ "un" : "");
+ return volume_alsa_set_mute(!ucontrol->value.integer.value[0]);
}
-static struct snd_kcontrol_new volume_alsa_control_vol __devinitdata = {
+static struct snd_kcontrol_new volume_alsa_control_vol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Console Playback Volume",
.index = 0,
@@ -6663,7 +6869,7 @@ static struct snd_kcontrol_new volume_alsa_control_vol __devinitdata = {
.get = volume_alsa_vol_get,
};
-static struct snd_kcontrol_new volume_alsa_control_mute __devinitdata = {
+static struct snd_kcontrol_new volume_alsa_control_mute = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Console Playback Switch",
.index = 0,
@@ -6672,7 +6878,7 @@ static struct snd_kcontrol_new volume_alsa_control_mute __devinitdata = {
.get = volume_alsa_mute_get,
};
-static void volume_suspend(pm_message_t state)
+static void volume_suspend(void)
{
tpacpi_volume_checkpoint_nvram();
}
@@ -6705,11 +6911,11 @@ static int __init volume_create_alsa_mixer(void)
struct snd_kcontrol *ctl_mute;
int rc;
- rc = snd_card_create(alsa_index, alsa_id, THIS_MODULE,
- sizeof(struct tpacpi_alsa_data), &card);
+ rc = snd_card_new(&tpacpi_pdev->dev,
+ alsa_index, alsa_id, THIS_MODULE,
+ sizeof(struct tpacpi_alsa_data), &card);
if (rc < 0 || !card) {
- printk(TPACPI_ERR
- "Failed to create ALSA card structures: %d\n", rc);
+ pr_err("Failed to create ALSA card structures: %d\n", rc);
return 1;
}
@@ -6743,9 +6949,8 @@ static int __init volume_create_alsa_mixer(void)
ctl_vol = snd_ctl_new1(&volume_alsa_control_vol, NULL);
rc = snd_ctl_add(card, ctl_vol);
if (rc < 0) {
- printk(TPACPI_ERR
- "Failed to create ALSA volume control: %d\n",
- rc);
+ pr_err("Failed to create ALSA volume control: %d\n",
+ rc);
goto err_exit;
}
data->ctl_vol_id = &ctl_vol->id;
@@ -6754,16 +6959,14 @@ static int __init volume_create_alsa_mixer(void)
ctl_mute = snd_ctl_new1(&volume_alsa_control_mute, NULL);
rc = snd_ctl_add(card, ctl_mute);
if (rc < 0) {
- printk(TPACPI_ERR "Failed to create ALSA mute control: %d\n",
- rc);
+ pr_err("Failed to create ALSA mute control: %d\n", rc);
goto err_exit;
}
data->ctl_mute_id = &ctl_mute->id;
- snd_card_set_dev(card, &tpacpi_pdev->dev);
rc = snd_card_register(card);
if (rc < 0) {
- printk(TPACPI_ERR "Failed to register ALSA card: %d\n", rc);
+ pr_err("Failed to register ALSA card: %d\n", rc);
goto err_exit;
}
@@ -6819,9 +7022,8 @@ static int __init volume_init(struct ibm_init_struct *iibm)
return -EINVAL;
if (volume_mode == TPACPI_VOL_MODE_UCMS_STEP) {
- printk(TPACPI_ERR
- "UCMS step volume mode not implemented, "
- "please contact %s\n", TPACPI_MAIL);
+ pr_err("UCMS step volume mode not implemented, "
+ "please contact %s\n", TPACPI_MAIL);
return 1;
}
@@ -6885,13 +7087,11 @@ static int __init volume_init(struct ibm_init_struct *iibm)
rc = volume_create_alsa_mixer();
if (rc) {
- printk(TPACPI_ERR
- "Could not create the ALSA mixer interface\n");
+ pr_err("Could not create the ALSA mixer interface\n");
return rc;
}
- printk(TPACPI_INFO
- "Console audio control enabled, mode: %s\n",
+ pr_info("Console audio control enabled, mode: %s\n",
(volume_control_allowed) ?
"override (read/write)" :
"monitor (read only)");
@@ -6953,12 +7153,10 @@ static int volume_write(char *buf)
if (!volume_control_allowed && tpacpi_lifecycle != TPACPI_LIFE_INIT) {
if (unlikely(!tp_warned.volume_ctrl_forbidden)) {
tp_warned.volume_ctrl_forbidden = 1;
- printk(TPACPI_NOTICE
- "Console audio control in monitor mode, "
- "changes are not allowed.\n");
- printk(TPACPI_NOTICE
- "Use the volume_control=1 module parameter "
- "to enable volume control\n");
+ pr_notice("Console audio control in monitor mode, "
+ "changes are not allowed\n");
+ pr_notice("Use the volume_control=1 module parameter "
+ "to enable volume control\n");
}
return -EPERM;
}
@@ -7033,8 +7231,7 @@ static void inline volume_alsa_notify_change(void)
static int __init volume_init(struct ibm_init_struct *iibm)
{
- printk(TPACPI_INFO
- "volume: disabled as there is no ALSA support in this kernel\n");
+ pr_info("volume: disabled as there is no ALSA support in this kernel\n");
return 1;
}
@@ -7070,7 +7267,7 @@ static struct ibm_struct volume_driver_data = {
*
* Fan speed changes of any sort (including those caused by the
* disengaged mode) are usually done slowly by the firmware as the
- * maximum ammount of fan duty cycle change per second seems to be
+ * maximum amount of fan duty cycle change per second seems to be
* limited.
*
* Reading is not available if GFAN exists.
@@ -7097,7 +7294,7 @@ static struct ibm_struct volume_driver_data = {
* TPACPI_FAN_WR_ACPI_FANS (X31/X40/X41)
*
* FIRMWARE BUG: on some models, EC 0x2f might not be initialized at
- * boot. Apparently the EC does not intialize it, so unless ACPI DSDT
+ * boot. Apparently the EC does not initialize it, so unless ACPI DSDT
* does so, its initial value is meaningless (0x07).
*
* For firmware bugs, refer to:
@@ -7198,7 +7395,7 @@ enum fan_control_commands {
* and also watchdog cmd */
};
-static int fan_control_allowed;
+static bool fan_control_allowed;
static enum fan_status_access_mode fan_status_access_mode;
static enum fan_control_access_mode fan_control_access_mode;
@@ -7241,9 +7438,8 @@ TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */
static void fan_quirk1_setup(void)
{
if (fan_control_initial_status == 0x07) {
- printk(TPACPI_NOTICE
- "fan_init: initial fan status is unknown, "
- "assuming it is in auto mode\n");
+ pr_notice("fan_init: initial fan status is unknown, "
+ "assuming it is in auto mode\n");
tp_features.fan_ctrl_status_undef = 1;
}
}
@@ -7318,17 +7514,18 @@ static int fan_get_status(u8 *status)
* Add TPACPI_FAN_RD_ACPI_FANS ? */
switch (fan_status_access_mode) {
- case TPACPI_FAN_RD_ACPI_GFAN:
+ case TPACPI_FAN_RD_ACPI_GFAN: {
/* 570, 600e/x, 770e, 770x */
+ int res;
- if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d")))
+ if (unlikely(!acpi_evalf(gfan_handle, &res, NULL, "d")))
return -EIO;
if (likely(status))
- *status = s & 0x07;
+ *status = res & 0x07;
break;
-
+ }
case TPACPI_FAN_RD_TPEC:
/* all except 570, 600e/x, 770e, 770x */
if (unlikely(!acpi_ec_read(fan_status_offset, &s)))
@@ -7616,26 +7813,15 @@ static int fan_set_speed(int speed)
static void fan_watchdog_reset(void)
{
- static int fan_watchdog_active;
-
if (fan_control_access_mode == TPACPI_FAN_WR_NONE)
return;
- if (fan_watchdog_active)
- cancel_delayed_work(&fan_watchdog_task);
-
if (fan_watchdog_maxinterval > 0 &&
- tpacpi_lifecycle != TPACPI_LIFE_EXITING) {
- fan_watchdog_active = 1;
- if (!queue_delayed_work(tpacpi_wq, &fan_watchdog_task,
- msecs_to_jiffies(fan_watchdog_maxinterval
- * 1000))) {
- printk(TPACPI_ERR
- "failed to queue the fan watchdog, "
- "watchdog will not trigger\n");
- }
- } else
- fan_watchdog_active = 0;
+ tpacpi_lifecycle != TPACPI_LIFE_EXITING)
+ mod_delayed_work(tpacpi_wq, &fan_watchdog_task,
+ msecs_to_jiffies(fan_watchdog_maxinterval * 1000));
+ else
+ cancel_delayed_work(&fan_watchdog_task);
}
static void fan_watchdog_fire(struct work_struct *ignored)
@@ -7645,11 +7831,11 @@ static void fan_watchdog_fire(struct work_struct *ignored)
if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING)
return;
- printk(TPACPI_NOTICE "fan watchdog: enabling fan\n");
+ pr_notice("fan watchdog: enabling fan\n");
rc = fan_set_enable();
if (rc < 0) {
- printk(TPACPI_ERR "fan watchdog: error %d while enabling fan, "
- "will try again later...\n", -rc);
+ pr_err("fan watchdog: error %d while enabling fan, "
+ "will try again later...\n", -rc);
/* reschedule for later */
fan_watchdog_reset();
}
@@ -7927,9 +8113,11 @@ static int __init fan_init(struct ibm_init_struct *iibm)
tp_features.second_fan = 0;
fan_control_desired_level = 7;
- TPACPI_ACPIHANDLE_INIT(fans);
- TPACPI_ACPIHANDLE_INIT(gfan);
- TPACPI_ACPIHANDLE_INIT(sfan);
+ if (tpacpi_is_ibm()) {
+ TPACPI_ACPIHANDLE_INIT(fans);
+ TPACPI_ACPIHANDLE_INIT(gfan);
+ TPACPI_ACPIHANDLE_INIT(sfan);
+ }
quirks = tpacpi_check_quirks(fan_quirk_table,
ARRAY_SIZE(fan_quirk_table));
@@ -7951,8 +8139,7 @@ static int __init fan_init(struct ibm_init_struct *iibm)
"secondary fan support enabled\n");
}
} else {
- printk(TPACPI_ERR
- "ThinkPad ACPI EC access misbehaving, "
+ pr_err("ThinkPad ACPI EC access misbehaving, "
"fan status and control unavailable\n");
return 1;
}
@@ -8041,7 +8228,7 @@ static void fan_exit(void)
flush_workqueue(tpacpi_wq);
}
-static void fan_suspend(pm_message_t state)
+static void fan_suspend(void)
{
int rc;
@@ -8052,9 +8239,8 @@ static void fan_suspend(pm_message_t state)
fan_control_resume_level = 0;
rc = fan_get_status_safe(&fan_control_resume_level);
if (rc < 0)
- printk(TPACPI_NOTICE
- "failed to read fan level for later "
- "restore during resume: %d\n", rc);
+ pr_notice("failed to read fan level for later "
+ "restore during resume: %d\n", rc);
/* if it is undefined, don't attempt to restore it.
* KEEP THIS LAST */
@@ -8109,13 +8295,11 @@ static void fan_resume(void)
return;
}
if (do_set) {
- printk(TPACPI_NOTICE
- "restoring fan level to 0x%02x\n",
- fan_control_resume_level);
+ pr_notice("restoring fan level to 0x%02x\n",
+ fan_control_resume_level);
rc = fan_set_level_safe(fan_control_resume_level);
if (rc < 0)
- printk(TPACPI_NOTICE
- "failed to restore fan level: %d\n", rc);
+ pr_notice("failed to restore fan level: %d\n", rc);
}
}
@@ -8207,8 +8391,8 @@ static int fan_write_cmd_level(const char *cmd, int *rc)
*rc = fan_set_level_safe(level);
if (*rc == -ENXIO)
- printk(TPACPI_ERR "level command accepted for unsupported "
- "access mode %d", fan_control_access_mode);
+ pr_err("level command accepted for unsupported access mode %d\n",
+ fan_control_access_mode);
else if (!*rc)
tpacpi_disclose_usertask("procfs fan",
"set level to %d\n", level);
@@ -8223,8 +8407,8 @@ static int fan_write_cmd_enable(const char *cmd, int *rc)
*rc = fan_set_enable();
if (*rc == -ENXIO)
- printk(TPACPI_ERR "enable command accepted for unsupported "
- "access mode %d", fan_control_access_mode);
+ pr_err("enable command accepted for unsupported access mode %d\n",
+ fan_control_access_mode);
else if (!*rc)
tpacpi_disclose_usertask("procfs fan", "enable\n");
@@ -8238,8 +8422,8 @@ static int fan_write_cmd_disable(const char *cmd, int *rc)
*rc = fan_set_disable();
if (*rc == -ENXIO)
- printk(TPACPI_ERR "disable command accepted for unsupported "
- "access mode %d", fan_control_access_mode);
+ pr_err("disable command accepted for unsupported access mode %d\n",
+ fan_control_access_mode);
else if (!*rc)
tpacpi_disclose_usertask("procfs fan", "disable\n");
@@ -8258,8 +8442,8 @@ static int fan_write_cmd_speed(const char *cmd, int *rc)
*rc = fan_set_speed(speed);
if (*rc == -ENXIO)
- printk(TPACPI_ERR "speed command accepted for unsupported "
- "access mode %d", fan_control_access_mode);
+ pr_err("speed command accepted for unsupported access mode %d\n",
+ fan_control_access_mode);
else if (!*rc)
tpacpi_disclose_usertask("procfs fan",
"set speed to %d\n", speed);
@@ -8318,6 +8502,103 @@ static struct ibm_struct fan_driver_data = {
.resume = fan_resume,
};
+/*************************************************************************
+ * Mute LED subdriver
+ */
+
+
+struct tp_led_table {
+ acpi_string name;
+ int on_value;
+ int off_value;
+ int state;
+};
+
+static struct tp_led_table led_tables[] = {
+ [TPACPI_LED_MUTE] = {
+ .name = "SSMS",
+ .on_value = 1,
+ .off_value = 0,
+ },
+ [TPACPI_LED_MICMUTE] = {
+ .name = "MMTS",
+ .on_value = 2,
+ .off_value = 0,
+ },
+};
+
+static int mute_led_on_off(struct tp_led_table *t, bool state)
+{
+ acpi_handle temp;
+ int output;
+
+ if (!ACPI_SUCCESS(acpi_get_handle(hkey_handle, t->name, &temp))) {
+ pr_warn("Thinkpad ACPI has no %s interface.\n", t->name);
+ return -EIO;
+ }
+
+ if (!acpi_evalf(hkey_handle, &output, t->name, "dd",
+ state ? t->on_value : t->off_value))
+ return -EIO;
+
+ t->state = state;
+ return state;
+}
+
+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 mute_led_init(struct ibm_init_struct *iibm)
+{
+ acpi_handle temp;
+ int i;
+
+ 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
+ t->state = -ENODEV;
+ }
+ return 0;
+}
+
+static void mute_led_exit(void)
+{
+ int i;
+
+ for (i = 0; i < TPACPI_LED_MAX; i++)
+ tpacpi_led_set(i, false);
+}
+
+static void mute_led_resume(void)
+{
+ int i;
+
+ for (i = 0; i < TPACPI_LED_MAX; i++) {
+ struct tp_led_table *t = &led_tables[i];
+ if (t->state >= 0)
+ mute_led_on_off(t, t->state);
+ }
+}
+
+static struct ibm_struct mute_led_driver_data = {
+ .name = "mute_led",
+ .exit = mute_led_exit,
+ .resume = mute_led_resume,
+};
+
/****************************************************************************
****************************************************************************
*
@@ -8374,7 +8655,7 @@ static struct proc_dir_entry *proc_dir;
* Module and infrastructure proble, init and exit handling
*/
-static int force_load;
+static bool force_load;
#ifdef CONFIG_THINKPAD_ACPI_DEBUG
static const char * __init str_supported(int is_supported)
@@ -8399,7 +8680,6 @@ static void ibm_exit(struct ibm_struct *ibm)
ibm->acpi->type,
dispatch_acpi_notify);
ibm->flags.acpi_notify_installed = 0;
- ibm->flags.acpi_notify_installed = 0;
}
if (ibm->flags.proc_created) {
@@ -8463,8 +8743,8 @@ static int __init ibm_init(struct ibm_init_struct *iibm)
if (ibm->acpi->notify) {
ret = setup_acpi_notify(ibm);
if (ret == -ENODEV) {
- printk(TPACPI_NOTICE "disabling subdriver %s\n",
- ibm->name);
+ pr_notice("disabling subdriver %s\n",
+ ibm->name);
ret = 0;
goto err_out;
}
@@ -8477,16 +8757,16 @@ static int __init ibm_init(struct ibm_init_struct *iibm)
"%s installed\n", ibm->name);
if (ibm->read) {
- mode_t mode;
+ umode_t mode = iibm->base_procfs_mode;
- mode = S_IRUGO;
+ if (!mode)
+ mode = S_IRUGO;
if (ibm->write)
mode |= S_IWUSR;
entry = proc_create_data(ibm->name, mode, proc_dir,
&dispatch_proc_fops, ibm);
if (!entry) {
- printk(TPACPI_ERR "unable to create proc entry %s\n",
- ibm->name);
+ pr_err("unable to create proc entry %s\n", ibm->name);
ret = -ENODEV;
goto err_out;
}
@@ -8520,10 +8800,10 @@ static bool __pure __init tpacpi_is_valid_fw_id(const char* const s,
return s && strlen(s) >= 8 &&
tpacpi_is_fw_digit(s[0]) &&
tpacpi_is_fw_digit(s[1]) &&
- s[2] == t && s[3] == 'T' &&
+ s[2] == t &&
+ (s[3] == 'T' || s[3] == 'N') &&
tpacpi_is_fw_digit(s[4]) &&
- tpacpi_is_fw_digit(s[5]) &&
- s[6] == 'W' && s[7] == 'W';
+ tpacpi_is_fw_digit(s[5]);
}
/* returns 0 - probe ok, or < 0 - probe error.
@@ -8554,7 +8834,8 @@ static int __must_check __init get_thinkpad_model_data(
return -ENOMEM;
/* Really ancient ThinkPad 240X will fail this, which is fine */
- if (!tpacpi_is_valid_fw_id(tp->bios_version_str, 'E'))
+ if (!(tpacpi_is_valid_fw_id(tp->bios_version_str, 'E') ||
+ tpacpi_is_valid_fw_id(tp->bios_version_str, 'C')))
return 0;
tp->bios_model = tp->bios_version_str[0]
@@ -8586,23 +8867,28 @@ static int __must_check __init get_thinkpad_model_data(
tp->ec_release = (ec_fw_string[4] << 8)
| ec_fw_string[5];
} else {
- printk(TPACPI_NOTICE
- "ThinkPad firmware release %s "
- "doesn't match the known patterns\n",
- ec_fw_string);
- printk(TPACPI_NOTICE
- "please report this to %s\n",
- TPACPI_MAIL);
+ 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;
}
}
s = dmi_get_system_info(DMI_PRODUCT_VERSION);
- if (s && !strnicmp(s, "ThinkPad", 8)) {
+ if (s && !(strnicmp(s, "ThinkPad", 8) && strnicmp(s, "Lenovo", 6))) {
tp->model_str = kstrdup(s, GFP_KERNEL);
if (!tp->model_str)
return -ENOMEM;
+ } else {
+ s = dmi_get_system_info(DMI_BIOS_VENDOR);
+ if (s && !(strnicmp(s, "Lenovo", 6))) {
+ tp->model_str = kstrdup(s, GFP_KERNEL);
+ if (!tp->model_str)
+ return -ENOMEM;
+ }
}
s = dmi_get_system_info(DMI_PRODUCT_NAME);
@@ -8620,6 +8906,10 @@ static int __init probe_for_thinkpad(void)
if (acpi_disabled)
return -ENODEV;
+ /* It would be dangerous to run the driver in this case */
+ if (!tpacpi_is_ibm() && !tpacpi_is_lenovo())
+ return -ENODEV;
+
/*
* Non-ancient models have better DMI tagging, but very old models
* don't. tpacpi_is_fw_known() is a cheat to help in that case.
@@ -8628,12 +8918,11 @@ static int __init probe_for_thinkpad(void)
(thinkpad_id.ec_model != 0) ||
tpacpi_is_fw_known();
- /* ec is required because many other handles are relative to it */
- TPACPI_ACPIHANDLE_INIT(ec);
+ /* The EC handler is required */
+ tpacpi_acpi_handle_locate("ec", TPACPI_ACPI_EC_HID, &ec_handle);
if (!ec_handle) {
if (is_thinkpad)
- printk(TPACPI_ERR
- "Not yet supported ThinkPad detected!\n");
+ pr_err("Not yet supported ThinkPad detected!\n");
return -ENODEV;
}
@@ -8643,12 +8932,34 @@ static int __init probe_for_thinkpad(void)
return 0;
}
+static void __init thinkpad_acpi_init_banner(void)
+{
+ pr_info("%s v%s\n", TPACPI_DESC, TPACPI_VERSION);
+ pr_info("%s\n", TPACPI_URL);
+
+ pr_info("ThinkPad BIOS %s, EC %s\n",
+ (thinkpad_id.bios_version_str) ?
+ thinkpad_id.bios_version_str : "unknown",
+ (thinkpad_id.ec_version_str) ?
+ thinkpad_id.ec_version_str : "unknown");
+
+ BUG_ON(!thinkpad_id.vendor);
+
+ if (thinkpad_id.model_str)
+ pr_info("%s %s, model %s\n",
+ (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ?
+ "IBM" : ((thinkpad_id.vendor ==
+ PCI_VENDOR_ID_LENOVO) ?
+ "Lenovo" : "Unknown vendor"),
+ thinkpad_id.model_str,
+ (thinkpad_id.nummodel_str) ?
+ thinkpad_id.nummodel_str : "unknown");
+}
/* Module init, exit, parameters */
static struct ibm_init_struct ibms_init[] __initdata = {
{
- .init = thinkpad_acpi_driver_init,
.data = &thinkpad_acpi_driver_data,
},
{
@@ -8670,6 +8981,7 @@ static struct ibm_init_struct ibms_init[] __initdata = {
#ifdef CONFIG_THINKPAD_ACPI_VIDEO
{
.init = video_init,
+ .base_procfs_mode = S_IRUSR,
.data = &video_driver_data,
},
#endif
@@ -8694,9 +9006,6 @@ static struct ibm_init_struct ibms_init[] __initdata = {
.data = &thermal_driver_data,
},
{
- .data = &ecdump_driver_data,
- },
- {
.init = brightness_init,
.data = &brightness_driver_data,
},
@@ -8708,6 +9017,10 @@ static struct ibm_init_struct ibms_init[] __initdata = {
.init = fan_init,
.data = &fan_driver_data,
},
+ {
+ .init = mute_led_init,
+ .data = &mute_led_driver_data,
+ },
};
static int __init set_ibm_param(const char *val, struct kernel_param *kp)
@@ -8762,11 +9075,6 @@ module_param(brightness_enable, uint, 0444);
MODULE_PARM_DESC(brightness_enable,
"Enables backlight control when 1, disables when 0");
-module_param(hotkey_report_mode, uint, 0444);
-MODULE_PARM_DESC(hotkey_report_mode,
- "used for backwards compatibility with userspace, "
- "see documentation");
-
#ifdef CONFIG_THINKPAD_ACPI_ALSA_SUPPORT
module_param_named(volume_mode, volume_mode, uint, 0444);
MODULE_PARM_DESC(volume_mode,
@@ -8804,7 +9112,6 @@ TPACPI_PARAM(light);
TPACPI_PARAM(cmos);
TPACPI_PARAM(led);
TPACPI_PARAM(beep);
-TPACPI_PARAM(ecdump);
TPACPI_PARAM(brightness);
TPACPI_PARAM(volume);
TPACPI_PARAM(fan);
@@ -8854,6 +9161,7 @@ static void thinkpad_acpi_module_exit(void)
input_unregister_device(tpacpi_inputdev);
else
input_free_device(tpacpi_inputdev);
+ kfree(hotkey_keycode_map);
}
if (tpacpi_hwmon)
@@ -8887,6 +9195,7 @@ static void thinkpad_acpi_module_exit(void)
kfree(thinkpad_id.bios_version_str);
kfree(thinkpad_id.ec_version_str);
kfree(thinkpad_id.model_str);
+ kfree(thinkpad_id.nummodel_str);
}
@@ -8896,16 +9205,11 @@ static int __init thinkpad_acpi_module_init(void)
tpacpi_lifecycle = TPACPI_LIFE_INIT;
- /* Parameter checking */
- if (hotkey_report_mode > 2)
- return -EINVAL;
-
/* Driver-level probe */
ret = get_thinkpad_model_data(&thinkpad_id);
if (ret) {
- printk(TPACPI_ERR
- "unable to get DMI data: %d\n", ret);
+ pr_err("unable to get DMI data: %d\n", ret);
thinkpad_acpi_module_exit();
return ret;
}
@@ -8917,6 +9221,9 @@ static int __init thinkpad_acpi_module_init(void)
/* Driver initialization */
+ thinkpad_acpi_init_banner();
+ tpacpi_check_outdated_fw();
+
TPACPI_ACPIHANDLE_INIT(ecrd);
TPACPI_ACPIHANDLE_INIT(ecwr);
@@ -8928,16 +9235,14 @@ static int __init thinkpad_acpi_module_init(void)
proc_dir = proc_mkdir(TPACPI_PROC_DIR, acpi_root_dir);
if (!proc_dir) {
- printk(TPACPI_ERR
- "unable to create proc dir " TPACPI_PROC_DIR);
+ pr_err("unable to create proc dir " TPACPI_PROC_DIR "\n");
thinkpad_acpi_module_exit();
return -ENODEV;
}
ret = platform_driver_register(&tpacpi_pdriver);
if (ret) {
- printk(TPACPI_ERR
- "unable to register main platform driver\n");
+ pr_err("unable to register main platform driver\n");
thinkpad_acpi_module_exit();
return ret;
}
@@ -8945,8 +9250,7 @@ static int __init thinkpad_acpi_module_init(void)
ret = platform_driver_register(&tpacpi_hwmon_pdriver);
if (ret) {
- printk(TPACPI_ERR
- "unable to register hwmon platform driver\n");
+ pr_err("unable to register hwmon platform driver\n");
thinkpad_acpi_module_exit();
return ret;
}
@@ -8959,8 +9263,7 @@ static int __init thinkpad_acpi_module_init(void)
&tpacpi_hwmon_pdriver.driver);
}
if (ret) {
- printk(TPACPI_ERR
- "unable to create sysfs driver attributes\n");
+ pr_err("unable to create sysfs driver attributes\n");
thinkpad_acpi_module_exit();
return ret;
}
@@ -8973,7 +9276,7 @@ static int __init thinkpad_acpi_module_init(void)
if (IS_ERR(tpacpi_pdev)) {
ret = PTR_ERR(tpacpi_pdev);
tpacpi_pdev = NULL;
- printk(TPACPI_ERR "unable to register platform device\n");
+ pr_err("unable to register platform device\n");
thinkpad_acpi_module_exit();
return ret;
}
@@ -8983,16 +9286,14 @@ static int __init thinkpad_acpi_module_init(void)
if (IS_ERR(tpacpi_sensors_pdev)) {
ret = PTR_ERR(tpacpi_sensors_pdev);
tpacpi_sensors_pdev = NULL;
- printk(TPACPI_ERR
- "unable to register hwmon platform device\n");
+ pr_err("unable to register hwmon platform device\n");
thinkpad_acpi_module_exit();
return ret;
}
ret = device_create_file(&tpacpi_sensors_pdev->dev,
&dev_attr_thinkpad_acpi_pdev_name);
if (ret) {
- printk(TPACPI_ERR
- "unable to create sysfs hwmon device attributes\n");
+ pr_err("unable to create sysfs hwmon device attributes\n");
thinkpad_acpi_module_exit();
return ret;
}
@@ -9001,14 +9302,13 @@ static int __init thinkpad_acpi_module_init(void)
if (IS_ERR(tpacpi_hwmon)) {
ret = PTR_ERR(tpacpi_hwmon);
tpacpi_hwmon = NULL;
- printk(TPACPI_ERR "unable to register hwmon device\n");
+ pr_err("unable to register hwmon device\n");
thinkpad_acpi_module_exit();
return ret;
}
mutex_init(&tpacpi_inputdev_send_mutex);
tpacpi_inputdev = input_allocate_device();
if (!tpacpi_inputdev) {
- printk(TPACPI_ERR "unable to allocate input device\n");
thinkpad_acpi_module_exit();
return -ENOMEM;
} else {
@@ -9016,13 +9316,16 @@ static int __init thinkpad_acpi_module_init(void)
tpacpi_inputdev->name = "ThinkPad Extra Buttons";
tpacpi_inputdev->phys = TPACPI_DRVR_NAME "/input0";
tpacpi_inputdev->id.bustype = BUS_HOST;
- tpacpi_inputdev->id.vendor = (thinkpad_id.vendor) ?
- thinkpad_id.vendor :
- PCI_VENDOR_ID_IBM;
+ tpacpi_inputdev->id.vendor = thinkpad_id.vendor;
tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT;
tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION;
tpacpi_inputdev->dev.parent = &tpacpi_pdev->dev;
}
+
+ /* Init subdriver dependencies */
+ tpacpi_detect_brightness_capabilities();
+
+ /* Init subdrivers */
for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
ret = ibm_init(&ibms_init[i]);
if (ret >= 0 && *ibms_init[i].param)
@@ -9032,16 +9335,18 @@ static int __init thinkpad_acpi_module_init(void)
return ret;
}
}
+
+ tpacpi_lifecycle = TPACPI_LIFE_RUNNING;
+
ret = input_register_device(tpacpi_inputdev);
if (ret < 0) {
- printk(TPACPI_ERR "unable to register input device\n");
+ pr_err("unable to register input device\n");
thinkpad_acpi_module_exit();
return ret;
} else {
tp_features.input_device_registered = 1;
}
- tpacpi_lifecycle = TPACPI_LIFE_RUNNING;
return 0;
}