forked from Minki/linux
Merge branch 'for-upstream/platform-x86_tpacpi' of git://repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6 into x86-platform
This commit is contained in:
commit
420f5f0c5a
@ -292,13 +292,13 @@ sysfs notes:
|
|||||||
|
|
||||||
Warning: when in NVRAM mode, the volume up/down/mute
|
Warning: when in NVRAM mode, the volume up/down/mute
|
||||||
keys are synthesized according to changes in the mixer,
|
keys are synthesized according to changes in the mixer,
|
||||||
so you have to use volume up or volume down to unmute,
|
which uses a single volume up or volume down hotkey
|
||||||
as per the ThinkPad volume mixer user interface. When
|
press to unmute, as per the ThinkPad volume mixer user
|
||||||
in ACPI event mode, volume up/down/mute are reported as
|
interface. When in ACPI event mode, volume up/down/mute
|
||||||
separate events, but this behaviour may be corrected in
|
events are reported by the firmware and can behave
|
||||||
future releases of this driver, in which case the
|
differently (and that behaviour changes with firmware
|
||||||
ThinkPad volume mixer user interface semantics will be
|
version -- not just with firmware models -- as well as
|
||||||
enforced.
|
OSI(Linux) state).
|
||||||
|
|
||||||
hotkey_poll_freq:
|
hotkey_poll_freq:
|
||||||
frequency in Hz for hot key polling. It must be between
|
frequency in Hz for hot key polling. It must be between
|
||||||
@ -309,7 +309,7 @@ sysfs notes:
|
|||||||
will cause hot key presses that require NVRAM polling
|
will cause hot key presses that require NVRAM polling
|
||||||
to never be reported.
|
to never be reported.
|
||||||
|
|
||||||
Setting hotkey_poll_freq too low will cause repeated
|
Setting hotkey_poll_freq too low may cause repeated
|
||||||
pressings of the same hot key to be misreported as a
|
pressings of the same hot key to be misreported as a
|
||||||
single key press, or to not even be detected at all.
|
single key press, or to not even be detected at all.
|
||||||
The recommended polling frequency is 10Hz.
|
The recommended polling frequency is 10Hz.
|
||||||
@ -397,6 +397,7 @@ ACPI Scan
|
|||||||
event code Key Notes
|
event code Key Notes
|
||||||
|
|
||||||
0x1001 0x00 FN+F1 -
|
0x1001 0x00 FN+F1 -
|
||||||
|
|
||||||
0x1002 0x01 FN+F2 IBM: battery (rare)
|
0x1002 0x01 FN+F2 IBM: battery (rare)
|
||||||
Lenovo: Screen lock
|
Lenovo: Screen lock
|
||||||
|
|
||||||
@ -404,7 +405,8 @@ event code Key Notes
|
|||||||
this hot key, even with hot keys
|
this hot key, even with hot keys
|
||||||
disabled or with Fn+F3 masked
|
disabled or with Fn+F3 masked
|
||||||
off
|
off
|
||||||
IBM: screen lock
|
IBM: screen lock, often turns
|
||||||
|
off the ThinkLight as side-effect
|
||||||
Lenovo: battery
|
Lenovo: battery
|
||||||
|
|
||||||
0x1004 0x03 FN+F4 Sleep button (ACPI sleep button
|
0x1004 0x03 FN+F4 Sleep button (ACPI sleep button
|
||||||
@ -433,7 +435,8 @@ event code Key Notes
|
|||||||
Do you feel lucky today?
|
Do you feel lucky today?
|
||||||
|
|
||||||
0x1008 0x07 FN+F8 IBM: toggle screen expand
|
0x1008 0x07 FN+F8 IBM: toggle screen expand
|
||||||
Lenovo: configure UltraNav
|
Lenovo: configure UltraNav,
|
||||||
|
or toggle screen expand
|
||||||
|
|
||||||
0x1009 0x08 FN+F9 -
|
0x1009 0x08 FN+F9 -
|
||||||
.. .. ..
|
.. .. ..
|
||||||
@ -444,7 +447,7 @@ event code Key Notes
|
|||||||
either through the ACPI event,
|
either through the ACPI event,
|
||||||
or through a hotkey event.
|
or through a hotkey event.
|
||||||
The firmware may refuse to
|
The firmware may refuse to
|
||||||
generate further FN+F4 key
|
generate further FN+F12 key
|
||||||
press events until a S3 or S4
|
press events until a S3 or S4
|
||||||
ACPI sleep cycle is performed,
|
ACPI sleep cycle is performed,
|
||||||
or some time passes.
|
or some time passes.
|
||||||
@ -512,15 +515,19 @@ events for switches:
|
|||||||
SW_RFKILL_ALL T60 and later hardware rfkill rocker switch
|
SW_RFKILL_ALL T60 and later hardware rfkill rocker switch
|
||||||
SW_TABLET_MODE Tablet ThinkPads HKEY events 0x5009 and 0x500A
|
SW_TABLET_MODE Tablet ThinkPads HKEY events 0x5009 and 0x500A
|
||||||
|
|
||||||
Non hot-key ACPI HKEY event map:
|
Non hotkey ACPI HKEY event map:
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
Events that are not propagated by the driver, except for legacy
|
||||||
|
compatibility purposes when hotkey_report_mode is set to 1:
|
||||||
|
|
||||||
0x5001 Lid closed
|
0x5001 Lid closed
|
||||||
0x5002 Lid opened
|
0x5002 Lid opened
|
||||||
0x5009 Tablet swivel: switched to tablet mode
|
0x5009 Tablet swivel: switched to tablet mode
|
||||||
0x500A Tablet swivel: switched to normal mode
|
0x500A Tablet swivel: switched to normal mode
|
||||||
0x7000 Radio Switch may have changed state
|
0x7000 Radio Switch may have changed state
|
||||||
|
|
||||||
The above events are not propagated by the driver, except for legacy
|
Events that are never propagated by the driver:
|
||||||
compatibility purposes when hotkey_report_mode is set to 1.
|
|
||||||
|
|
||||||
0x2304 System is waking up from suspend to undock
|
0x2304 System is waking up from suspend to undock
|
||||||
0x2305 System is waking up from suspend to eject bay
|
0x2305 System is waking up from suspend to eject bay
|
||||||
@ -528,14 +535,39 @@ compatibility purposes when hotkey_report_mode is set to 1.
|
|||||||
0x2405 System is waking up from hibernation to eject bay
|
0x2405 System is waking up from hibernation to eject bay
|
||||||
0x5010 Brightness level changed/control event
|
0x5010 Brightness level changed/control event
|
||||||
|
|
||||||
The above events are never propagated by the driver.
|
Events that are propagated by the driver to userspace:
|
||||||
|
|
||||||
|
0x2313 ALARM: System is waking up from suspend because
|
||||||
|
the battery is nearly empty
|
||||||
|
0x2413 ALARM: System is waking up from hibernation because
|
||||||
|
the battery is nearly empty
|
||||||
0x3003 Bay ejection (see 0x2x05) complete, can sleep again
|
0x3003 Bay ejection (see 0x2x05) complete, can sleep again
|
||||||
|
0x3006 Bay hotplug request (hint to power up SATA link when
|
||||||
|
the optical drive tray is ejected)
|
||||||
0x4003 Undocked (see 0x2x04), can sleep again
|
0x4003 Undocked (see 0x2x04), can sleep again
|
||||||
0x500B Tablet pen inserted into its storage bay
|
0x500B Tablet pen inserted into its storage bay
|
||||||
0x500C Tablet pen removed from its storage bay
|
0x500C Tablet pen removed from its storage bay
|
||||||
|
0x6011 ALARM: battery is too hot
|
||||||
|
0x6012 ALARM: battery is extremely hot
|
||||||
|
0x6021 ALARM: a sensor is too hot
|
||||||
|
0x6022 ALARM: a sensor is extremely hot
|
||||||
|
0x6030 System thermal table changed
|
||||||
|
|
||||||
The above events are propagated by the driver.
|
Battery nearly empty alarms are a last resort attempt to get the
|
||||||
|
operating system to hibernate or shutdown cleanly (0x2313), or shutdown
|
||||||
|
cleanly (0x2413) before power is lost. They must be acted upon, as the
|
||||||
|
wake up caused by the firmware will have negated most safety nets...
|
||||||
|
|
||||||
|
When any of the "too hot" alarms happen, according to Lenovo the user
|
||||||
|
should suspend or hibernate the laptop (and in the case of battery
|
||||||
|
alarms, unplug the AC adapter) to let it cool down. These alarms do
|
||||||
|
signal that something is wrong, they should never happen on normal
|
||||||
|
operating conditions.
|
||||||
|
|
||||||
|
The "extremely hot" alarms are emergencies. According to Lenovo, the
|
||||||
|
operating system is to force either an immediate suspend or hibernate
|
||||||
|
cycle, or a system shutdown. Obviously, something is very wrong if this
|
||||||
|
happens.
|
||||||
|
|
||||||
Compatibility notes:
|
Compatibility notes:
|
||||||
|
|
||||||
|
@ -122,8 +122,14 @@ enum {
|
|||||||
TP_NVRAM_POS_LEVEL_VOLUME = 0,
|
TP_NVRAM_POS_LEVEL_VOLUME = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Misc NVRAM-related */
|
||||||
|
enum {
|
||||||
|
TP_NVRAM_LEVEL_VOLUME_MAX = 14,
|
||||||
|
};
|
||||||
|
|
||||||
/* ACPI HIDs */
|
/* ACPI HIDs */
|
||||||
#define TPACPI_ACPI_HKEY_HID "IBM0068"
|
#define TPACPI_ACPI_HKEY_HID "IBM0068"
|
||||||
|
#define TPACPI_ACPI_EC_HID "PNP0C09"
|
||||||
|
|
||||||
/* Input IDs */
|
/* Input IDs */
|
||||||
#define TPACPI_HKEY_INPUT_PRODUCT 0x5054 /* "TP" */
|
#define TPACPI_HKEY_INPUT_PRODUCT 0x5054 /* "TP" */
|
||||||
@ -299,8 +305,8 @@ static struct {
|
|||||||
u32 hotkey_tablet:1;
|
u32 hotkey_tablet:1;
|
||||||
u32 light:1;
|
u32 light:1;
|
||||||
u32 light_status:1;
|
u32 light_status:1;
|
||||||
u32 bright_16levels:1;
|
|
||||||
u32 bright_acpimode:1;
|
u32 bright_acpimode:1;
|
||||||
|
u32 bright_unkfw:1;
|
||||||
u32 wan:1;
|
u32 wan:1;
|
||||||
u32 uwb:1;
|
u32 uwb:1;
|
||||||
u32 fan_ctrl_status_undef:1;
|
u32 fan_ctrl_status_undef:1;
|
||||||
@ -363,6 +369,9 @@ struct tpacpi_led_classdev {
|
|||||||
unsigned int led;
|
unsigned int led;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* brightness level capabilities */
|
||||||
|
static unsigned int bright_maxlvl; /* 0 = unknown */
|
||||||
|
|
||||||
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
|
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
|
||||||
static int dbg_wlswemul;
|
static int dbg_wlswemul;
|
||||||
static int tpacpi_wlsw_emulstate;
|
static int tpacpi_wlsw_emulstate;
|
||||||
@ -480,6 +489,15 @@ static unsigned long __init tpacpi_check_quirks(
|
|||||||
return 0;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
****************************************************************************
|
****************************************************************************
|
||||||
@ -494,21 +512,13 @@ static unsigned long __init tpacpi_check_quirks(
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
static acpi_handle root_handle;
|
static acpi_handle root_handle;
|
||||||
|
static acpi_handle ec_handle;
|
||||||
|
|
||||||
#define TPACPI_HANDLE(object, parent, paths...) \
|
#define TPACPI_HANDLE(object, parent, paths...) \
|
||||||
static acpi_handle object##_handle; \
|
static acpi_handle object##_handle; \
|
||||||
static acpi_handle *object##_parent = &parent##_handle; \
|
static const acpi_handle *object##_parent __initdata = \
|
||||||
static char *object##_path; \
|
&parent##_handle; \
|
||||||
static char *object##_paths[] = { paths }
|
static char *object##_paths[] __initdata = { 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 */
|
|
||||||
);
|
|
||||||
|
|
||||||
TPACPI_HANDLE(ecrd, ec, "ECRD"); /* 570 */
|
TPACPI_HANDLE(ecrd, ec, "ECRD"); /* 570 */
|
||||||
TPACPI_HANDLE(ecwr, ec, "ECWR"); /* 570 */
|
TPACPI_HANDLE(ecwr, ec, "ECWR"); /* 570 */
|
||||||
@ -528,6 +538,7 @@ TPACPI_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */
|
|||||||
"\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */
|
"\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */
|
||||||
"\\_SB.PCI0.VID0", /* 770e */
|
"\\_SB.PCI0.VID0", /* 770e */
|
||||||
"\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */
|
"\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */
|
||||||
|
"\\_SB.PCI0.AGP.VGA", /* X100e and a few others */
|
||||||
"\\_SB.PCI0.AGP.VID", /* all others */
|
"\\_SB.PCI0.AGP.VID", /* all others */
|
||||||
); /* R30, R31 */
|
); /* R30, R31 */
|
||||||
|
|
||||||
@ -594,9 +605,10 @@ static int acpi_evalf(acpi_handle handle,
|
|||||||
|
|
||||||
switch (res_type) {
|
switch (res_type) {
|
||||||
case 'd': /* int */
|
case 'd': /* int */
|
||||||
if (res)
|
success = (status == AE_OK &&
|
||||||
|
out_obj.type == ACPI_TYPE_INTEGER);
|
||||||
|
if (success && res)
|
||||||
*(int *)res = out_obj.integer.value;
|
*(int *)res = out_obj.integer.value;
|
||||||
success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER;
|
|
||||||
break;
|
break;
|
||||||
case 'v': /* void */
|
case 'v': /* void */
|
||||||
success = status == AE_OK;
|
success = status == AE_OK;
|
||||||
@ -609,8 +621,8 @@ static int acpi_evalf(acpi_handle handle,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!success && !quiet)
|
if (!success && !quiet)
|
||||||
printk(TPACPI_ERR "acpi_evalf(%s, %s, ...) failed: %d\n",
|
printk(TPACPI_ERR "acpi_evalf(%s, %s, ...) failed: %s\n",
|
||||||
method, fmt0, status);
|
method, fmt0, acpi_format_exception(status));
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
@ -661,11 +673,11 @@ static int issue_thinkpad_cmos_command(int cmos_cmd)
|
|||||||
|
|
||||||
#define TPACPI_ACPIHANDLE_INIT(object) \
|
#define TPACPI_ACPIHANDLE_INIT(object) \
|
||||||
drv_acpi_handle_init(#object, &object##_handle, *object##_parent, \
|
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,
|
static void __init drv_acpi_handle_init(const char *name,
|
||||||
acpi_handle *handle, acpi_handle parent,
|
acpi_handle *handle, const acpi_handle parent,
|
||||||
char **paths, int num_paths, char **path)
|
char **paths, const int num_paths)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
acpi_status status;
|
acpi_status status;
|
||||||
@ -676,10 +688,9 @@ static void drv_acpi_handle_init(char *name,
|
|||||||
for (i = 0; i < num_paths; i++) {
|
for (i = 0; i < num_paths; i++) {
|
||||||
status = acpi_get_handle(parent, paths[i], handle);
|
status = acpi_get_handle(parent, paths[i], handle);
|
||||||
if (ACPI_SUCCESS(status)) {
|
if (ACPI_SUCCESS(status)) {
|
||||||
*path = paths[i];
|
|
||||||
dbg_printk(TPACPI_DBG_INIT,
|
dbg_printk(TPACPI_DBG_INIT,
|
||||||
"Found ACPI handle %s for %s\n",
|
"Found ACPI handle %s for %s\n",
|
||||||
*path, name);
|
paths[i], name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -689,6 +700,43 @@ static void drv_acpi_handle_init(char *name,
|
|||||||
*handle = NULL;
|
*handle = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static acpi_status __init tpacpi_acpi_handle_locate_callback(acpi_handle handle,
|
||||||
|
u32 level, void *context, void **return_value)
|
||||||
|
{
|
||||||
|
*(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 || !hid || !handle);
|
||||||
|
vdbg_printk(TPACPI_DBG_INIT,
|
||||||
|
"trying to locate ACPI handle for %s, using HID %s\n",
|
||||||
|
name, hid);
|
||||||
|
|
||||||
|
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)
|
static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data)
|
||||||
{
|
{
|
||||||
struct ibm_struct *ibm = data;
|
struct ibm_struct *ibm = data;
|
||||||
@ -736,8 +784,8 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm)
|
|||||||
"handling %s events\n", ibm->name);
|
"handling %s events\n", ibm->name);
|
||||||
} else {
|
} else {
|
||||||
printk(TPACPI_ERR
|
printk(TPACPI_ERR
|
||||||
"acpi_install_notify_handler(%s) failed: %d\n",
|
"acpi_install_notify_handler(%s) failed: %s\n",
|
||||||
ibm->name, status);
|
ibm->name, acpi_format_exception(status));
|
||||||
}
|
}
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
@ -1035,80 +1083,6 @@ static void tpacpi_disable_brightness_delay(void)
|
|||||||
"ACPI backlight control delay disabled\n");
|
"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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void printk_deprecated_attribute(const char * const what,
|
static void printk_deprecated_attribute(const char * const what,
|
||||||
const char * const details)
|
const char * const details)
|
||||||
{
|
{
|
||||||
@ -1872,34 +1846,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)
|
static int thinkpad_acpi_driver_read(struct seq_file *m)
|
||||||
{
|
{
|
||||||
seq_printf(m, "driver:\t\t%s\n", TPACPI_DESC);
|
seq_printf(m, "driver:\t\t%s\n", TPACPI_DESC);
|
||||||
@ -2405,6 +2354,36 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
|
|||||||
tpacpi_hotkey_send_key(__scancode); \
|
tpacpi_hotkey_send_key(__scancode); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
void issue_volchange(const unsigned int oldvol,
|
||||||
|
const unsigned int newvol)
|
||||||
|
{
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void issue_brightnesschange(const unsigned int oldbrt,
|
||||||
|
const unsigned int newbrt)
|
||||||
|
{
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle);
|
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle);
|
||||||
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle);
|
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle);
|
||||||
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF7, display_toggle);
|
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF7, display_toggle);
|
||||||
@ -2414,41 +2393,61 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
|
|||||||
|
|
||||||
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF8, displayexp_toggle);
|
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF8, displayexp_toggle);
|
||||||
|
|
||||||
/* handle volume */
|
/*
|
||||||
if (oldn->volume_toggle != newn->volume_toggle) {
|
* Handle volume
|
||||||
if (oldn->mute != newn->mute) {
|
*
|
||||||
|
* 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 minumum 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);
|
||||||
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE);
|
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE);
|
||||||
}
|
}
|
||||||
if (oldn->volume_level > newn->volume_level) {
|
} else {
|
||||||
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
|
/* unmute */
|
||||||
} else if (oldn->volume_level < newn->volume_level) {
|
if (oldn->mute) {
|
||||||
|
/* recently unmuted, issue 'unmute' keypress */
|
||||||
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
|
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
|
||||||
} else if (oldn->mute == newn->mute) {
|
}
|
||||||
/* repeated key presses that didn't change state */
|
if (oldn->volume_level != newn->volume_level) {
|
||||||
if (newn->mute) {
|
issue_volchange(oldn->volume_level, newn->volume_level);
|
||||||
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE);
|
} else if (oldn->volume_toggle != newn->volume_toggle) {
|
||||||
} else if (newn->volume_level != 0) {
|
/* repeated vol up/down keypress at end of scale ? */
|
||||||
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
|
if (newn->volume_level == 0)
|
||||||
} else {
|
|
||||||
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
|
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 */
|
/* handle brightness */
|
||||||
if (oldn->brightness_toggle != newn->brightness_toggle) {
|
if (oldn->brightness_level != newn->brightness_level) {
|
||||||
if (oldn->brightness_level < newn->brightness_level) {
|
issue_brightnesschange(oldn->brightness_level,
|
||||||
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
|
newn->brightness_level);
|
||||||
} else if (oldn->brightness_level > newn->brightness_level) {
|
} 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);
|
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
|
||||||
} else {
|
else if (newn->brightness_level >= bright_maxlvl
|
||||||
/* repeated key presses that didn't change state */
|
&& !tp_features.bright_unkfw)
|
||||||
if (newn->brightness_level != 0) {
|
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
|
||||||
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
|
|
||||||
} else {
|
|
||||||
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef TPACPI_COMPARE_KEY
|
#undef TPACPI_COMPARE_KEY
|
||||||
@ -3353,7 +3352,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
|
|||||||
goto err_exit;
|
goto err_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) {
|
if (tpacpi_is_lenovo()) {
|
||||||
dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY,
|
dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY,
|
||||||
"using Lenovo default hot key map\n");
|
"using Lenovo default hot key map\n");
|
||||||
memcpy(hotkey_keycode_map, &lenovo_keycode_map,
|
memcpy(hotkey_keycode_map, &lenovo_keycode_map,
|
||||||
@ -3391,11 +3390,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Do not issue duplicate brightness change events to
|
/* Do not issue duplicate brightness change events to
|
||||||
* userspace */
|
* userspace. tpacpi_detect_brightness_capabilities() must have
|
||||||
if (!tp_features.bright_acpimode)
|
* been called before this point */
|
||||||
/* update bright_acpimode... */
|
|
||||||
tpacpi_check_std_acpi_brightness_support();
|
|
||||||
|
|
||||||
if (tp_features.bright_acpimode && acpi_video_backlight_support()) {
|
if (tp_features.bright_acpimode && acpi_video_backlight_support()) {
|
||||||
printk(TPACPI_INFO
|
printk(TPACPI_INFO
|
||||||
"This ThinkPad has standard ACPI backlight "
|
"This ThinkPad has standard ACPI backlight "
|
||||||
@ -4422,7 +4418,8 @@ static int __init video_init(struct ibm_init_struct *iibm)
|
|||||||
vdbg_printk(TPACPI_DBG_INIT, "initializing video subdriver\n");
|
vdbg_printk(TPACPI_DBG_INIT, "initializing video subdriver\n");
|
||||||
|
|
||||||
TPACPI_ACPIHANDLE_INIT(vid);
|
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)
|
if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga)
|
||||||
/* G41, assume IVGA doesn't change */
|
/* G41, assume IVGA doesn't change */
|
||||||
@ -4431,10 +4428,12 @@ static int __init video_init(struct ibm_init_struct *iibm)
|
|||||||
if (!vid_handle)
|
if (!vid_handle)
|
||||||
/* video switching not supported on R30, R31 */
|
/* video switching not supported on R30, R31 */
|
||||||
video_supported = TPACPI_VIDEO_NONE;
|
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 */
|
/* 570 */
|
||||||
video_supported = TPACPI_VIDEO_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 */
|
/* 600e/x, 770e, 770x */
|
||||||
video_supported = TPACPI_VIDEO_770;
|
video_supported = TPACPI_VIDEO_770;
|
||||||
else
|
else
|
||||||
@ -4811,8 +4810,10 @@ static int __init light_init(struct ibm_init_struct *iibm)
|
|||||||
|
|
||||||
vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n");
|
vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n");
|
||||||
|
|
||||||
TPACPI_ACPIHANDLE_INIT(ledb);
|
if (tpacpi_is_ibm()) {
|
||||||
TPACPI_ACPIHANDLE_INIT(lght);
|
TPACPI_ACPIHANDLE_INIT(ledb);
|
||||||
|
TPACPI_ACPIHANDLE_INIT(lght);
|
||||||
|
}
|
||||||
TPACPI_ACPIHANDLE_INIT(cmos);
|
TPACPI_ACPIHANDLE_INIT(cmos);
|
||||||
INIT_WORK(&tpacpi_led_thinklight.work, light_set_status_worker);
|
INIT_WORK(&tpacpi_led_thinklight.work, light_set_status_worker);
|
||||||
|
|
||||||
@ -5007,11 +5008,7 @@ enum { /* For TPACPI_LED_OLD */
|
|||||||
|
|
||||||
static enum led_access_mode led_supported;
|
static enum led_access_mode led_supported;
|
||||||
|
|
||||||
TPACPI_HANDLE(led, ec, "SLED", /* 570 */
|
static acpi_handle led_handle;
|
||||||
"SYSL", /* 600e/x, 770e, 770x, A21e, A2xm/p, */
|
|
||||||
/* T20-22, X20-21 */
|
|
||||||
"LED", /* all others */
|
|
||||||
); /* R30, R31 */
|
|
||||||
|
|
||||||
#define TPACPI_LED_NUMLEDS 16
|
#define TPACPI_LED_NUMLEDS 16
|
||||||
static struct tpacpi_led_classdev *tpacpi_leds;
|
static struct tpacpi_led_classdev *tpacpi_leds;
|
||||||
@ -5271,6 +5268,32 @@ static const struct tpacpi_quirk led_useful_qtable[] __initconst = {
|
|||||||
#undef TPACPI_LEDQ_IBM
|
#undef TPACPI_LEDQ_IBM
|
||||||
#undef TPACPI_LEDQ_LNV
|
#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)
|
static int __init led_init(struct ibm_init_struct *iibm)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
@ -5279,20 +5302,7 @@ static int __init led_init(struct ibm_init_struct *iibm)
|
|||||||
|
|
||||||
vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n");
|
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;
|
|
||||||
|
|
||||||
vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n",
|
vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n",
|
||||||
str_supported(led_supported), led_supported);
|
str_supported(led_supported), led_supported);
|
||||||
@ -5741,11 +5751,12 @@ static int __init thermal_init(struct ibm_init_struct *iibm)
|
|||||||
TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8;
|
TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8;
|
||||||
}
|
}
|
||||||
} else if (acpi_tmp7) {
|
} 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 */
|
/* 600e/x, 770e, 770x */
|
||||||
thermal_read_mode = TPACPI_THERMAL_ACPI_UPDT;
|
thermal_read_mode = TPACPI_THERMAL_ACPI_UPDT;
|
||||||
} else {
|
} else {
|
||||||
/* Standard ACPI TMPx access, max 8 sensors */
|
/* IBM/LENOVO DSDT EC.TMPx access, max 8 sensors */
|
||||||
thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
|
thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -5954,7 +5965,7 @@ static unsigned int tpacpi_brightness_nvram_get(void)
|
|||||||
lnvram = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS)
|
lnvram = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS)
|
||||||
& TP_NVRAM_MASK_LEVEL_BRIGHTNESS)
|
& TP_NVRAM_MASK_LEVEL_BRIGHTNESS)
|
||||||
>> TP_NVRAM_POS_LEVEL_BRIGHTNESS;
|
>> TP_NVRAM_POS_LEVEL_BRIGHTNESS;
|
||||||
lnvram &= (tp_features.bright_16levels) ? 0x0f : 0x07;
|
lnvram &= bright_maxlvl;
|
||||||
|
|
||||||
return lnvram;
|
return lnvram;
|
||||||
}
|
}
|
||||||
@ -6063,8 +6074,7 @@ static int brightness_set(unsigned int value)
|
|||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
if (value > ((tp_features.bright_16levels)? 15 : 7) ||
|
if (value > bright_maxlvl || value < 0)
|
||||||
value < 0)
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
vdbg_printk(TPACPI_DBG_BRGHT,
|
vdbg_printk(TPACPI_DBG_BRGHT,
|
||||||
@ -6139,6 +6149,80 @@ static struct backlight_ops ibm_backlight_data = {
|
|||||||
|
|
||||||
/* --------------------------------------------------------------------- */
|
/* --------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
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 unsigned 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;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These are only useful for models that have only one possibility
|
* 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
|
* of GPU. If the BIOS model handles both ATI and Intel, don't use
|
||||||
@ -6169,6 +6253,47 @@ static const struct tpacpi_quirk brightness_quirk_table[] __initconst = {
|
|||||||
TPACPI_Q_IBM('7', '5', TPACPI_BRGHT_Q_NOEC), /* X41 Tablet */
|
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;
|
||||||
|
printk(TPACPI_INFO
|
||||||
|
"detected a 16-level brightness capable ThinkPad\n");
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
case 0:
|
||||||
|
bright_maxlvl = 7;
|
||||||
|
printk(TPACPI_INFO
|
||||||
|
"detected a 8-level brightness capable ThinkPad\n");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printk(TPACPI_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)
|
static int __init brightness_init(struct ibm_init_struct *iibm)
|
||||||
{
|
{
|
||||||
struct backlight_properties props;
|
struct backlight_properties props;
|
||||||
@ -6182,14 +6307,13 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
|
|||||||
quirks = tpacpi_check_quirks(brightness_quirk_table,
|
quirks = tpacpi_check_quirks(brightness_quirk_table,
|
||||||
ARRAY_SIZE(brightness_quirk_table));
|
ARRAY_SIZE(brightness_quirk_table));
|
||||||
|
|
||||||
/*
|
/* tpacpi_detect_brightness_capabilities() must have run already */
|
||||||
* 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 it is unknown, we don't handle it: it wouldn't be safe */
|
||||||
|
if (tp_features.bright_unkfw)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (tp_features.bright_acpimode) {
|
||||||
if (acpi_video_backlight_support()) {
|
if (acpi_video_backlight_support()) {
|
||||||
if (brightness_enable > 1) {
|
if (brightness_enable > 1) {
|
||||||
printk(TPACPI_NOTICE
|
printk(TPACPI_NOTICE
|
||||||
@ -6218,15 +6342,6 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (b > 16) {
|
|
||||||
printk(TPACPI_ERR
|
|
||||||
"Unsupported brightness interface, "
|
|
||||||
"please contact %s\n", TPACPI_MAIL);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (b == 16)
|
|
||||||
tp_features.bright_16levels = 1;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check for module parameter bogosity, note that we
|
* Check for module parameter bogosity, note that we
|
||||||
* init brightness_mode to TPACPI_BRGHT_MODE_MAX in order to be
|
* init brightness_mode to TPACPI_BRGHT_MODE_MAX in order to be
|
||||||
@ -6249,7 +6364,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Safety */
|
/* Safety */
|
||||||
if (thinkpad_id.vendor != PCI_VENDOR_ID_IBM &&
|
if (!tpacpi_is_ibm() &&
|
||||||
(brightness_mode == TPACPI_BRGHT_MODE_ECNVRAM ||
|
(brightness_mode == TPACPI_BRGHT_MODE_ECNVRAM ||
|
||||||
brightness_mode == TPACPI_BRGHT_MODE_EC))
|
brightness_mode == TPACPI_BRGHT_MODE_EC))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -6257,12 +6372,9 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
|
|||||||
if (tpacpi_brightness_get_raw(&b) < 0)
|
if (tpacpi_brightness_get_raw(&b) < 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (tp_features.bright_16levels)
|
|
||||||
printk(TPACPI_INFO
|
|
||||||
"detected a 16-level brightness capable ThinkPad\n");
|
|
||||||
|
|
||||||
memset(&props, 0, sizeof(struct backlight_properties));
|
memset(&props, 0, sizeof(struct backlight_properties));
|
||||||
props.max_brightness = (tp_features.bright_16levels) ? 15 : 7;
|
props.max_brightness = bright_maxlvl;
|
||||||
|
props.brightness = b & TP_EC_BACKLIGHT_LVLMSK;
|
||||||
ibm_backlight_device = backlight_device_register(TPACPI_BACKLIGHT_DEV_NAME,
|
ibm_backlight_device = backlight_device_register(TPACPI_BACKLIGHT_DEV_NAME,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
&ibm_backlight_data,
|
&ibm_backlight_data,
|
||||||
@ -6285,7 +6397,10 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
|
|||||||
"or not on your ThinkPad\n", TPACPI_MAIL);
|
"or not on your ThinkPad\n", TPACPI_MAIL);
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
backlight_update_status(ibm_backlight_device);
|
||||||
|
|
||||||
vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT,
|
vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT,
|
||||||
@ -6328,9 +6443,8 @@ static int brightness_read(struct seq_file *m)
|
|||||||
} else {
|
} else {
|
||||||
seq_printf(m, "level:\t\t%d\n", level);
|
seq_printf(m, "level:\t\t%d\n", level);
|
||||||
seq_printf(m, "commands:\tup, down\n");
|
seq_printf(m, "commands:\tup, down\n");
|
||||||
seq_printf(m, "commands:\tlevel <level>"
|
seq_printf(m, "commands:\tlevel <level> (<level> is 0-%d)\n",
|
||||||
" (<level> is 0-%d)\n",
|
bright_maxlvl);
|
||||||
(tp_features.bright_16levels) ? 15 : 7);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -6341,7 +6455,6 @@ static int brightness_write(char *buf)
|
|||||||
int level;
|
int level;
|
||||||
int rc;
|
int rc;
|
||||||
char *cmd;
|
char *cmd;
|
||||||
int max_level = (tp_features.bright_16levels) ? 15 : 7;
|
|
||||||
|
|
||||||
level = brightness_get(NULL);
|
level = brightness_get(NULL);
|
||||||
if (level < 0)
|
if (level < 0)
|
||||||
@ -6349,13 +6462,13 @@ static int brightness_write(char *buf)
|
|||||||
|
|
||||||
while ((cmd = next_cmd(&buf))) {
|
while ((cmd = next_cmd(&buf))) {
|
||||||
if (strlencmp(cmd, "up") == 0) {
|
if (strlencmp(cmd, "up") == 0) {
|
||||||
if (level < max_level)
|
if (level < bright_maxlvl)
|
||||||
level++;
|
level++;
|
||||||
} else if (strlencmp(cmd, "down") == 0) {
|
} else if (strlencmp(cmd, "down") == 0) {
|
||||||
if (level > 0)
|
if (level > 0)
|
||||||
level--;
|
level--;
|
||||||
} else if (sscanf(cmd, "level %d", &level) == 1 &&
|
} else if (sscanf(cmd, "level %d", &level) == 1 &&
|
||||||
level >= 0 && level <= max_level) {
|
level >= 0 && level <= bright_maxlvl) {
|
||||||
/* new level set */
|
/* new level set */
|
||||||
} else
|
} else
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -6669,6 +6782,8 @@ static int volume_alsa_vol_get(struct snd_kcontrol *kcontrol,
|
|||||||
static int volume_alsa_vol_put(struct snd_kcontrol *kcontrol,
|
static int volume_alsa_vol_put(struct snd_kcontrol *kcontrol,
|
||||||
struct snd_ctl_elem_value *ucontrol)
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
{
|
{
|
||||||
|
tpacpi_disclose_usertask("ALSA", "set volume to %ld\n",
|
||||||
|
ucontrol->value.integer.value[0]);
|
||||||
return volume_alsa_set_volume(ucontrol->value.integer.value[0]);
|
return volume_alsa_set_volume(ucontrol->value.integer.value[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6692,6 +6807,9 @@ static int volume_alsa_mute_get(struct snd_kcontrol *kcontrol,
|
|||||||
static int volume_alsa_mute_put(struct snd_kcontrol *kcontrol,
|
static int volume_alsa_mute_put(struct snd_kcontrol *kcontrol,
|
||||||
struct snd_ctl_elem_value *ucontrol)
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
{
|
{
|
||||||
|
tpacpi_disclose_usertask("ALSA", "%smute\n",
|
||||||
|
ucontrol->value.integer.value[0] ?
|
||||||
|
"un" : "");
|
||||||
return volume_alsa_set_mute(!ucontrol->value.integer.value[0]);
|
return volume_alsa_set_mute(!ucontrol->value.integer.value[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7968,9 +8086,11 @@ static int __init fan_init(struct ibm_init_struct *iibm)
|
|||||||
tp_features.second_fan = 0;
|
tp_features.second_fan = 0;
|
||||||
fan_control_desired_level = 7;
|
fan_control_desired_level = 7;
|
||||||
|
|
||||||
TPACPI_ACPIHANDLE_INIT(fans);
|
if (tpacpi_is_ibm()) {
|
||||||
TPACPI_ACPIHANDLE_INIT(gfan);
|
TPACPI_ACPIHANDLE_INIT(fans);
|
||||||
TPACPI_ACPIHANDLE_INIT(sfan);
|
TPACPI_ACPIHANDLE_INIT(gfan);
|
||||||
|
TPACPI_ACPIHANDLE_INIT(sfan);
|
||||||
|
}
|
||||||
|
|
||||||
quirks = tpacpi_check_quirks(fan_quirk_table,
|
quirks = tpacpi_check_quirks(fan_quirk_table,
|
||||||
ARRAY_SIZE(fan_quirk_table));
|
ARRAY_SIZE(fan_quirk_table));
|
||||||
@ -8662,6 +8782,10 @@ static int __init probe_for_thinkpad(void)
|
|||||||
if (acpi_disabled)
|
if (acpi_disabled)
|
||||||
return -ENODEV;
|
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
|
* 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.
|
* don't. tpacpi_is_fw_known() is a cheat to help in that case.
|
||||||
@ -8670,8 +8794,8 @@ static int __init probe_for_thinkpad(void)
|
|||||||
(thinkpad_id.ec_model != 0) ||
|
(thinkpad_id.ec_model != 0) ||
|
||||||
tpacpi_is_fw_known();
|
tpacpi_is_fw_known();
|
||||||
|
|
||||||
/* ec is required because many other handles are relative to it */
|
/* The EC handler is required */
|
||||||
TPACPI_ACPIHANDLE_INIT(ec);
|
tpacpi_acpi_handle_locate("ec", TPACPI_ACPI_EC_HID, &ec_handle);
|
||||||
if (!ec_handle) {
|
if (!ec_handle) {
|
||||||
if (is_thinkpad)
|
if (is_thinkpad)
|
||||||
printk(TPACPI_ERR
|
printk(TPACPI_ERR
|
||||||
@ -8685,12 +8809,34 @@ static int __init probe_for_thinkpad(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void __init thinkpad_acpi_init_banner(void)
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
|
||||||
|
BUG_ON(!thinkpad_id.vendor);
|
||||||
|
|
||||||
|
if (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");
|
||||||
|
}
|
||||||
|
|
||||||
/* Module init, exit, parameters */
|
/* Module init, exit, parameters */
|
||||||
|
|
||||||
static struct ibm_init_struct ibms_init[] __initdata = {
|
static struct ibm_init_struct ibms_init[] __initdata = {
|
||||||
{
|
{
|
||||||
.init = thinkpad_acpi_driver_init,
|
|
||||||
.data = &thinkpad_acpi_driver_data,
|
.data = &thinkpad_acpi_driver_data,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -8960,6 +9106,9 @@ static int __init thinkpad_acpi_module_init(void)
|
|||||||
|
|
||||||
/* Driver initialization */
|
/* Driver initialization */
|
||||||
|
|
||||||
|
thinkpad_acpi_init_banner();
|
||||||
|
tpacpi_check_outdated_fw();
|
||||||
|
|
||||||
TPACPI_ACPIHANDLE_INIT(ecrd);
|
TPACPI_ACPIHANDLE_INIT(ecrd);
|
||||||
TPACPI_ACPIHANDLE_INIT(ecwr);
|
TPACPI_ACPIHANDLE_INIT(ecwr);
|
||||||
|
|
||||||
@ -9059,13 +9208,16 @@ static int __init thinkpad_acpi_module_init(void)
|
|||||||
tpacpi_inputdev->name = "ThinkPad Extra Buttons";
|
tpacpi_inputdev->name = "ThinkPad Extra Buttons";
|
||||||
tpacpi_inputdev->phys = TPACPI_DRVR_NAME "/input0";
|
tpacpi_inputdev->phys = TPACPI_DRVR_NAME "/input0";
|
||||||
tpacpi_inputdev->id.bustype = BUS_HOST;
|
tpacpi_inputdev->id.bustype = BUS_HOST;
|
||||||
tpacpi_inputdev->id.vendor = (thinkpad_id.vendor) ?
|
tpacpi_inputdev->id.vendor = thinkpad_id.vendor;
|
||||||
thinkpad_id.vendor :
|
|
||||||
PCI_VENDOR_ID_IBM;
|
|
||||||
tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT;
|
tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT;
|
||||||
tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION;
|
tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION;
|
||||||
tpacpi_inputdev->dev.parent = &tpacpi_pdev->dev;
|
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++) {
|
for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
|
||||||
ret = ibm_init(&ibms_init[i]);
|
ret = ibm_init(&ibms_init[i]);
|
||||||
if (ret >= 0 && *ibms_init[i].param)
|
if (ret >= 0 && *ibms_init[i].param)
|
||||||
|
Loading…
Reference in New Issue
Block a user