Commit 385336e3 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'platform-drivers-x86-v3.19-1' of...

Merge tag 'platform-drivers-x86-v3.19-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86

Pull x86 platform driver update from Darren Hart:
 - thinkpad-acpi: Switch to software mute, cleanups
 - acerhdf: Bang-bang thermal governor, new models, cleanups
 - dell-laptop: New keyboard backlight support and documentation
 - toshiba_acpi: Keyboard backlight updates, hotkey handling
 - dell-wmi: Keypress filtering, WMI event processing
 - eeepc-laptop: Multiple cleanups, improved error handling, documentation
 - hp_wireless: Inform the user if hp_wireless_input_setup()/add() fails
 - misc: Code cleanups, quirks, various new IDs

* tag 'platform-drivers-x86-v3.19-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86: (33 commits)
  platform/x86/acerhdf: Still depends on THERMAL
  Documentation: Add entry for dell-laptop sysfs interface
  acpi: Remove _OSI(Linux) for ThinkPads
  thinkpad-acpi: Try to use full software mute control
  acerhdf: minor clean up
  acerhdf: added critical trip point
  acerhdf: Use bang-bang thermal governor
  acerhdf: Adding support for new models
  acerhdf: Adding support for "manual mode"
  dell-smo8800: Add more ACPI ids and change description of driver
  platform: x86: dell-laptop: Add support for keyboard backlight
  toshiba_acpi: Add keyboard backlight mode change event
  toshiba_acpi: Change notify funtion to handle more events
  toshiba_acpi: Move hotkey enabling code to its own function
  dell-wmi: Don't report keypresses on keybord illumination change
  dell-wmi: Don't report keypresses for radio state changes
  hp_wireless: Inform the user if hp_wireless_input_setup()/add() fails
  toshiba-acpi: Add missing ID (TOS6207)
  Sony-laptop: Deletion of an unnecessary check before the function call "pci_dev_put"
  platform: x86: Deletion of checks before backlight_device_unregister()
  ...
parents ebcffcda 200db647
What: /sys/class/leds/dell::kbd_backlight/als_setting
Date: December 2014
KernelVersion: 3.19
Contact: Gabriele Mazzotta <gabriele.mzt@gmail.com>,
Pali Rohár <pali.rohar@gmail.com>
Description:
This file allows to control the automatic keyboard
illumination mode on some systems that have an ambient
light sensor. Write 1 to this file to enable the auto
mode, 0 to disable it.
What: /sys/class/leds/dell::kbd_backlight/start_triggers
Date: December 2014
KernelVersion: 3.19
Contact: Gabriele Mazzotta <gabriele.mzt@gmail.com>,
Pali Rohár <pali.rohar@gmail.com>
Description:
This file allows to control the input triggers that
turn on the keyboard backlight illumination that is
disabled because of inactivity.
Read the file to see the triggers available. The ones
enabled are preceded by '+', those disabled by '-'.
To enable a trigger, write its name preceded by '+' to
this file. To disable a trigger, write its name preceded
by '-' instead.
For example, to enable the keyboard as trigger run:
echo +keyboard > /sys/class/leds/dell::kbd_backlight/start_triggers
To disable it:
echo -keyboard > /sys/class/leds/dell::kbd_backlight/start_triggers
Note that not all the available triggers can be configured.
What: /sys/class/leds/dell::kbd_backlight/stop_timeout
Date: December 2014
KernelVersion: 3.19
Contact: Gabriele Mazzotta <gabriele.mzt@gmail.com>,
Pali Rohár <pali.rohar@gmail.com>
Description:
This file allows to specify the interval after which the
keyboard illumination is disabled because of inactivity.
The timeouts are expressed in seconds, minutes, hours and
days, for which the symbols are 's', 'm', 'h' and 'd'
respectively.
To configure the timeout, write to this file a value along
with any the above units. If no unit is specified, the value
is assumed to be expressed in seconds.
For example, to set the timeout to 10 minutes run:
echo 10m > /sys/class/leds/dell::kbd_backlight/stop_timeout
Note that when this file is read, the returned value might be
expressed in a different unit than the one used when the timeout
was set.
Also note that only some timeouts are supported and that
some systems might fall back to a specific timeout in case
an invalid timeout is written to this file.
...@@ -304,60 +304,6 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { ...@@ -304,60 +304,6 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
* Linux ignores it, except for the machines enumerated below. * Linux ignores it, except for the machines enumerated below.
*/ */
/*
* Lenovo has a mix of systems OSI(Linux) situations
* and thus we can not wildcard the vendor.
*
* _OSI(Linux) helps sound
* DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad R61"),
* DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T61"),
* T400, T500
* _OSI(Linux) has Linux specific hooks
* DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X61"),
* _OSI(Linux) is a NOP:
* DMI_MATCH(DMI_PRODUCT_VERSION, "3000 N100"),
* DMI_MATCH(DMI_PRODUCT_VERSION, "LENOVO3000 V100"),
*/
{
.callback = dmi_enable_osi_linux,
.ident = "Lenovo ThinkPad R61",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad R61"),
},
},
{
.callback = dmi_enable_osi_linux,
.ident = "Lenovo ThinkPad T61",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T61"),
},
},
{
.callback = dmi_enable_osi_linux,
.ident = "Lenovo ThinkPad X61",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X61"),
},
},
{
.callback = dmi_enable_osi_linux,
.ident = "Lenovo ThinkPad T400",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T400"),
},
},
{
.callback = dmi_enable_osi_linux,
.ident = "Lenovo ThinkPad T500",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T500"),
},
},
/* /*
* Without this this EEEpc exports a non working WMI interface, with * Without this this EEEpc exports a non working WMI interface, with
* this it exports a working "good old" eeepc_laptop interface, fixing * this it exports a working "good old" eeepc_laptop interface, fixing
......
...@@ -38,7 +38,8 @@ config ACER_WMI ...@@ -38,7 +38,8 @@ config ACER_WMI
config ACERHDF config ACERHDF
tristate "Acer Aspire One temperature and fan driver" tristate "Acer Aspire One temperature and fan driver"
depends on THERMAL && ACPI depends on ACPI && THERMAL
select THERMAL_GOV_BANG_BANG
---help--- ---help---
This is a driver for Acer Aspire One netbooks. It allows to access This is a driver for Acer Aspire One netbooks. It allows to access
the temperature sensor and to control the fan. the temperature sensor and to control the fan.
...@@ -128,10 +129,10 @@ config DELL_WMI_AIO ...@@ -128,10 +129,10 @@ config DELL_WMI_AIO
be called dell-wmi-aio. be called dell-wmi-aio.
config DELL_SMO8800 config DELL_SMO8800
tristate "Dell Latitude freefall driver (ACPI SMO8800/SMO8810)" tristate "Dell Latitude freefall driver (ACPI SMO88XX)"
depends on ACPI depends on ACPI
---help--- ---help---
Say Y here if you want to support SMO8800/SMO8810 freefall device Say Y here if you want to support SMO88XX freefall devices
on Dell Latitude laptops. on Dell Latitude laptops.
To compile this driver as a module, choose M here: the module will To compile this driver as a module, choose M here: the module will
......
This diff is collapsed.
...@@ -843,8 +843,7 @@ static int asus_backlight_init(struct asus_laptop *asus) ...@@ -843,8 +843,7 @@ static int asus_backlight_init(struct asus_laptop *asus)
static void asus_backlight_exit(struct asus_laptop *asus) static void asus_backlight_exit(struct asus_laptop *asus)
{ {
if (asus->backlight_device) backlight_device_unregister(asus->backlight_device);
backlight_device_unregister(asus->backlight_device);
asus->backlight_device = NULL; asus->backlight_device = NULL;
} }
......
...@@ -189,6 +189,15 @@ static const struct dmi_system_id asus_quirks[] = { ...@@ -189,6 +189,15 @@ static const struct dmi_system_id asus_quirks[] = {
}, },
.driver_data = &quirk_asus_wapf4, .driver_data = &quirk_asus_wapf4,
}, },
{
.callback = dmi_matched,
.ident = "ASUSTeK COMPUTER INC. X551CA",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "X551CA"),
},
.driver_data = &quirk_asus_wapf4,
},
{ {
.callback = dmi_matched, .callback = dmi_matched,
.ident = "ASUSTeK COMPUTER INC. X55A", .ident = "ASUSTeK COMPUTER INC. X55A",
......
...@@ -1308,8 +1308,7 @@ static int asus_wmi_backlight_init(struct asus_wmi *asus) ...@@ -1308,8 +1308,7 @@ static int asus_wmi_backlight_init(struct asus_wmi *asus)
static void asus_wmi_backlight_exit(struct asus_wmi *asus) static void asus_wmi_backlight_exit(struct asus_wmi *asus)
{ {
if (asus->backlight_device) backlight_device_unregister(asus->backlight_device);
backlight_device_unregister(asus->backlight_device);
asus->backlight_device = NULL; asus->backlight_device = NULL;
} }
......
This diff is collapsed.
/* /*
* dell-smo8800.c - Dell Latitude ACPI SMO8800/SMO8810 freefall sensor driver * dell-smo8800.c - Dell Latitude ACPI SMO88XX freefall sensor driver
* *
* Copyright (C) 2012 Sonal Santan <sonal.santan@gmail.com> * Copyright (C) 2012 Sonal Santan <sonal.santan@gmail.com>
* Copyright (C) 2014 Pali Rohár <pali.rohar@gmail.com> * Copyright (C) 2014 Pali Rohár <pali.rohar@gmail.com>
...@@ -209,7 +209,13 @@ static int smo8800_remove(struct acpi_device *device) ...@@ -209,7 +209,13 @@ static int smo8800_remove(struct acpi_device *device)
static const struct acpi_device_id smo8800_ids[] = { static const struct acpi_device_id smo8800_ids[] = {
{ "SMO8800", 0 }, { "SMO8800", 0 },
{ "SMO8801", 0 },
{ "SMO8810", 0 }, { "SMO8810", 0 },
{ "SMO8811", 0 },
{ "SMO8820", 0 },
{ "SMO8821", 0 },
{ "SMO8830", 0 },
{ "SMO8831", 0 },
{ "", 0 }, { "", 0 },
}; };
...@@ -228,6 +234,6 @@ static struct acpi_driver smo8800_driver = { ...@@ -228,6 +234,6 @@ static struct acpi_driver smo8800_driver = {
module_acpi_driver(smo8800_driver); module_acpi_driver(smo8800_driver);
MODULE_DESCRIPTION("Dell Latitude freefall driver (ACPI SMO8800/SMO8810)"); MODULE_DESCRIPTION("Dell Latitude freefall driver (ACPI SMO88XX)");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sonal Santan, Pali Rohár"); MODULE_AUTHOR("Sonal Santan, Pali Rohár");
...@@ -65,10 +65,8 @@ static const struct key_entry dell_wmi_legacy_keymap[] __initconst = { ...@@ -65,10 +65,8 @@ static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
/* Battery health status button */ /* Battery health status button */
{ KE_KEY, 0xe007, { KEY_BATTERY } }, { KE_KEY, 0xe007, { KEY_BATTERY } },
/* This is actually for all radios. Although physically a /* Radio devices state change */
* switch, the notification does not provide an indication of { KE_IGNORE, 0xe008, { KEY_RFKILL } },
* state and so it should be reported as a key */
{ KE_KEY, 0xe008, { KEY_WLAN } },
/* The next device is at offset 6, the active devices are at /* The next device is at offset 6, the active devices are at
offset 8 and the attached devices at offset 10 */ offset 8 and the attached devices at offset 10 */
...@@ -145,57 +143,154 @@ static const u16 bios_to_linux_keycode[256] __initconst = { ...@@ -145,57 +143,154 @@ static const u16 bios_to_linux_keycode[256] __initconst = {
static struct input_dev *dell_wmi_input_dev; static struct input_dev *dell_wmi_input_dev;
static void dell_wmi_process_key(int reported_key)
{
const struct key_entry *key;
key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
reported_key);
if (!key) {
pr_info("Unknown key %x pressed\n", reported_key);
return;
}
pr_debug("Key %x pressed\n", reported_key);
/* Don't report brightness notifications that will also come via ACPI */
if ((key->keycode == KEY_BRIGHTNESSUP ||
key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video)
return;
sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true);
}
static void dell_wmi_notify(u32 value, void *context) static void dell_wmi_notify(u32 value, void *context)
{ {
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj; union acpi_object *obj;
acpi_status status; acpi_status status;
acpi_size buffer_size;
u16 *buffer_entry, *buffer_end;
int len, i;
status = wmi_get_event_data(value, &response); status = wmi_get_event_data(value, &response);
if (status != AE_OK) { if (status != AE_OK) {
pr_info("bad event status 0x%x\n", status); pr_warn("bad event status 0x%x\n", status);
return; return;
} }
obj = (union acpi_object *)response.pointer; obj = (union acpi_object *)response.pointer;
if (!obj) {
pr_warn("no response\n");
return;
}
if (obj && obj->type == ACPI_TYPE_BUFFER) { if (obj->type != ACPI_TYPE_BUFFER) {
const struct key_entry *key; pr_warn("bad response type %x\n", obj->type);
int reported_key; kfree(obj);
u16 *buffer_entry = (u16 *)obj->buffer.pointer; return;
int buffer_size = obj->buffer.length/2; }
if (buffer_size >= 2 && dell_new_hk_type && buffer_entry[1] != 0x10) {
pr_info("Received unknown WMI event (0x%x)\n",
buffer_entry[1]);
kfree(obj);
return;
}
if (buffer_size >= 3 && (dell_new_hk_type || buffer_entry[1] == 0x0)) pr_debug("Received WMI event (%*ph)\n",
reported_key = (int)buffer_entry[2]; obj->buffer.length, obj->buffer.pointer);
buffer_entry = (u16 *)obj->buffer.pointer;
buffer_size = obj->buffer.length/2;
if (!dell_new_hk_type) {
if (buffer_size >= 3 && buffer_entry[1] == 0x0)
dell_wmi_process_key(buffer_entry[2]);
else if (buffer_size >= 2) else if (buffer_size >= 2)
reported_key = (int)buffer_entry[1] & 0xffff; dell_wmi_process_key(buffer_entry[1]);
else { else
pr_info("Received unknown WMI event\n"); pr_info("Received unknown WMI event\n");
kfree(obj); kfree(obj);
return; return;
}
buffer_end = buffer_entry + buffer_size;
while (buffer_entry < buffer_end) {
len = buffer_entry[0];
if (len == 0)
break;
len++;
if (buffer_entry + len > buffer_end) {
pr_warn("Invalid length of WMI event\n");
break;
} }
key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev, pr_debug("Process buffer (%*ph)\n", len*2, buffer_entry);
reported_key);
if (!key) { switch (buffer_entry[1]) {
pr_info("Unknown key %x pressed\n", reported_key); case 0x00:
} else if ((key->keycode == KEY_BRIGHTNESSUP || for (i = 2; i < len; ++i) {
key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video) { switch (buffer_entry[i]) {
/* Don't report brightness notifications that will also case 0xe043:
* come via ACPI */ /* NIC Link is Up */
; pr_debug("NIC Link is Up\n");
} else { break;
sparse_keymap_report_entry(dell_wmi_input_dev, key, case 0xe044:
1, true); /* NIC Link is Down */
pr_debug("NIC Link is Down\n");
break;
case 0xe045:
/* Unknown event but defined in DSDT */
default:
/* Unknown event */
pr_info("Unknown WMI event type 0x00: "
"0x%x\n", (int)buffer_entry[i]);
break;
}
}
break;
case 0x10:
/* Keys pressed */
for (i = 2; i < len; ++i)
dell_wmi_process_key(buffer_entry[i]);
break;
case 0x11:
for (i = 2; i < len; ++i) {
switch (buffer_entry[i]) {
case 0xfff0:
/* Battery unplugged */
pr_debug("Battery unplugged\n");
break;
case 0xfff1:
/* Battery inserted */
pr_debug("Battery inserted\n");
break;
case 0x01e1:
case 0x02ea:
case 0x02eb:
case 0x02ec:
case 0x02f6:
/* Keyboard backlight level changed */
pr_debug("Keyboard backlight level "
"changed\n");
break;
default:
/* Unknown event */
pr_info("Unknown WMI event type 0x11: "
"0x%x\n", (int)buffer_entry[i]);
break;
}
}
break;
default:
/* Unknown event */
pr_info("Unknown WMI event type 0x%x\n",
(int)buffer_entry[1]);
break;
} }
buffer_entry += len;
} }
kfree(obj); kfree(obj);
} }
...@@ -213,11 +308,16 @@ static const struct key_entry * __init dell_wmi_prepare_new_keymap(void) ...@@ -213,11 +308,16 @@ static const struct key_entry * __init dell_wmi_prepare_new_keymap(void)
for (i = 0; i < hotkey_num; i++) { for (i = 0; i < hotkey_num; i++) {
const struct dell_bios_keymap_entry *bios_entry = const struct dell_bios_keymap_entry *bios_entry =
&dell_bios_hotkey_table->keymap[i]; &dell_bios_hotkey_table->keymap[i];
keymap[i].type = KE_KEY; u16 keycode = bios_entry->keycode < 256 ?
keymap[i].code = bios_entry->scancode;
keymap[i].keycode = bios_entry->keycode < 256 ?
bios_to_linux_keycode[bios_entry->keycode] : bios_to_linux_keycode[bios_entry->keycode] :
KEY_RESERVED; KEY_RESERVED;
if (keycode == KEY_KBDILLUMTOGGLE)
keymap[i].type = KE_IGNORE;
else
keymap[i].type = KE_KEY;
keymap[i].code = bios_entry->scancode;
keymap[i].keycode = keycode;
} }
keymap[hotkey_num].type = KE_END; keymap[hotkey_num].type = KE_END;
......
This diff is collapsed.
...@@ -1153,8 +1153,7 @@ static int __init fujitsu_init(void) ...@@ -1153,8 +1153,7 @@ static int __init fujitsu_init(void)
fail_hotkey: fail_hotkey:
platform_driver_unregister(&fujitsupf_driver); platform_driver_unregister(&fujitsupf_driver);
fail_backlight: fail_backlight:
if (fujitsu->bl_device) backlight_device_unregister(fujitsu->bl_device);
backlight_device_unregister(fujitsu->bl_device);
fail_sysfs_group: fail_sysfs_group:
sysfs_remove_group(&fujitsu->pf_device->dev.kobj, sysfs_remove_group(&fujitsu->pf_device->dev.kobj,
&fujitsupf_attribute_group); &fujitsupf_attribute_group);
...@@ -1178,8 +1177,7 @@ static void __exit fujitsu_cleanup(void) ...@@ -1178,8 +1177,7 @@ static void __exit fujitsu_cleanup(void)
platform_driver_unregister(&fujitsupf_driver); platform_driver_unregister(&fujitsupf_driver);
if (fujitsu->bl_device) backlight_device_unregister(fujitsu->bl_device);
backlight_device_unregister(fujitsu->bl_device);
sysfs_remove_group(&fujitsu->pf_device->dev.kobj, sysfs_remove_group(&fujitsu->pf_device->dev.kobj,
&fujitsupf_attribute_group); &fujitsupf_attribute_group);
......
...@@ -85,6 +85,9 @@ static int hpwl_add(struct acpi_device *device) ...@@ -85,6 +85,9 @@ static int hpwl_add(struct acpi_device *device)
int err; int err;
err = hp_wireless_input_setup(); err = hp_wireless_input_setup();
if (err)
pr_err("Failed to setup hp wireless hotkeys\n");
return err; return err;
} }
......
...@@ -246,6 +246,7 @@ static const struct dmi_system_id lis3lv02d_dmi_ids[] = { ...@@ -246,6 +246,7 @@ static const struct dmi_system_id lis3lv02d_dmi_ids[] = {
AXIS_DMI_MATCH("HPB64xx", "HP ProBook 64", xy_swap), AXIS_DMI_MATCH("HPB64xx", "HP ProBook 64", xy_swap),
AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap), AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap),
AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted), AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted),
AXIS_DMI_MATCH("HPZBook15", "HP ZBook 15", x_inverted),
{ NULL, } { NULL, }
/* Laptop models without axis info (yet): /* Laptop models without axis info (yet):
* "NC6910" "HP Compaq 6910" * "NC6910" "HP Compaq 6910"
......
...@@ -729,8 +729,7 @@ static int ideapad_backlight_init(struct ideapad_private *priv) ...@@ -729,8 +729,7 @@ static int ideapad_backlight_init(struct ideapad_private *priv)
static void ideapad_backlight_exit(struct ideapad_private *priv) static void ideapad_backlight_exit(struct ideapad_private *priv)
{ {
if (priv->blightdev) backlight_device_unregister(priv->blightdev);
backlight_device_unregister(priv->blightdev);
priv->blightdev = NULL; priv->blightdev = NULL;
} }
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
* performance by allocating more power or thermal budget to the CPU or GPU * performance by allocating more power or thermal budget to the CPU or GPU
* based on available headroom and activity. * based on available headroom and activity.
* *
* The basic algorithm is driven by a 5s moving average of tempurature. If * The basic algorithm is driven by a 5s moving average of temperature. If
* thermal headroom is available, the CPU and/or GPU power clamps may be * thermal headroom is available, the CPU and/or GPU power clamps may be
* adjusted upwards. If we hit the thermal ceiling or a thermal trigger, * adjusted upwards. If we hit the thermal ceiling or a thermal trigger,
* we scale back the clamp. Aside from trigger events (when we're critically * we scale back the clamp. Aside from trigger events (when we're critically
......
...@@ -271,8 +271,7 @@ static int oaktrail_backlight_init(void) ...@@ -271,8 +271,7 @@ static int oaktrail_backlight_init(void)
static void oaktrail_backlight_exit(void) static void oaktrail_backlight_exit(void)
{ {
if (oaktrail_bl_device) backlight_device_unregister(oaktrail_bl_device);
backlight_device_unregister(oaktrail_bl_device);
} }
static int oaktrail_probe(struct platform_device *pdev) static int oaktrail_probe(struct platform_device *pdev)
......
...@@ -820,7 +820,7 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, ...@@ -820,7 +820,7 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
{ {
static bool extended; static bool extended;
if (str & 0x20) if (str & I8042_STR_AUXDATA)
return false; return false;
/* 0x54 wwan, 0x62 bluetooth, 0x76 wlan, 0xE4 touchpad toggle*/ /* 0x54 wwan, 0x62 bluetooth, 0x76 wlan, 0xE4 touchpad toggle*/
......
...@@ -354,8 +354,7 @@ static void __exit msi_wmi_exit(void) ...@@ -354,8 +354,7 @@ static void __exit msi_wmi_exit(void)
sparse_keymap_free(msi_wmi_input_dev); sparse_keymap_free(msi_wmi_input_dev);
input_unregister_device(msi_wmi_input_dev); input_unregister_device(msi_wmi_input_dev);
} }
if (backlight) backlight_device_unregister(backlight);
backlight_device_unregister(backlight);
} }
module_init(msi_wmi_init); module_init(msi_wmi_init);
......
...@@ -3140,8 +3140,7 @@ static void sony_nc_backlight_setup(void) ...@@ -3140,8 +3140,7 @@ static void sony_nc_backlight_setup(void)
static void sony_nc_backlight_cleanup(void) static void sony_nc_backlight_cleanup(void)
{ {
if (sony_bl_props.dev) backlight_device_unregister(sony_bl_props.dev);
backlight_device_unregister(sony_bl_props.dev);
} }
static int sony_nc_add(struct acpi_device *device) static int sony_nc_add(struct acpi_device *device)
...@@ -3716,8 +3715,7 @@ static void sony_pic_detect_device_type(struct sony_pic_dev *dev) ...@@ -3716,8 +3715,7 @@ static void sony_pic_detect_device_type(struct sony_pic_dev *dev)
dev->event_types = type2_events; dev->event_types = type2_events;
out: out:
if (pcidev) pci_dev_put(pcidev);
pci_dev_put(pcidev);
pr_info("detected Type%d model\n", pr_info("detected Type%d model\n",
dev->model == SONYPI_DEVICE_TYPE1 ? 1 : dev->model == SONYPI_DEVICE_TYPE1 ? 1 :
......
...@@ -6557,6 +6557,17 @@ static struct ibm_struct brightness_driver_data = { ...@@ -6557,6 +6557,17 @@ static struct ibm_struct brightness_driver_data = {
* bits 3-0 (volume). Other bits in NVRAM may have other functions, * bits 3-0 (volume). Other bits in NVRAM may have other functions,
* such as bit 7 which is used to detect repeated presses of MUTE, * such as bit 7 which is used to detect repeated presses of MUTE,
* and we leave them unchanged. * and we leave them unchanged.
*
* On newer Lenovo ThinkPads, the EC can automatically change the volume
* in response to user input. Unfortunately, this rarely works well.
* The laptop changes the state of its internal MUTE gate and, on some
* models, sends KEY_MUTE, causing any user code that responds to the
* mute button to get confused. The hardware MUTE gate is also
* unnecessary, since user code can handle the mute button without
* kernel or EC help.
*
* To avoid confusing userspace, we simply disable all EC-based mute
* and volume controls when possible.
*/ */
#ifdef CONFIG_THINKPAD_ACPI_ALSA_SUPPORT #ifdef CONFIG_THINKPAD_ACPI_ALSA_SUPPORT
...@@ -6611,11 +6622,21 @@ enum tpacpi_volume_capabilities { ...@@ -6611,11 +6622,21 @@ enum tpacpi_volume_capabilities {
TPACPI_VOL_CAP_MAX TPACPI_VOL_CAP_MAX
}; };
enum tpacpi_mute_btn_mode {
TP_EC_MUTE_BTN_LATCH = 0, /* Mute mutes; up/down unmutes */
/* We don't know what mode 1 is. */
TP_EC_MUTE_BTN_NONE = 2, /* Mute and up/down are just keys */
TP_EC_MUTE_BTN_TOGGLE = 3, /* Mute toggles; up/down unmutes */
};
static enum tpacpi_volume_access_mode volume_mode = static enum tpacpi_volume_access_mode volume_mode =
TPACPI_VOL_MODE_MAX; TPACPI_VOL_MODE_MAX;
static enum tpacpi_volume_capabilities volume_capabilities; static enum tpacpi_volume_capabilities volume_capabilities;
static bool volume_control_allowed; static bool volume_control_allowed;
static bool software_mute_requested = true;
static bool software_mute_active;
static int software_mute_orig_mode;
/* /*
* Used to syncronize writers to TP_EC_AUDIO and * Used to syncronize writers to TP_EC_AUDIO and
...@@ -6633,6 +6654,8 @@ static void tpacpi_volume_checkpoint_nvram(void) ...@@ -6633,6 +6654,8 @@ static void tpacpi_volume_checkpoint_nvram(void)
return; return;
if (!volume_control_allowed) if (!volume_control_allowed)
return; return;
if (software_mute_active)
return;
vdbg_printk(TPACPI_DBG_MIXER, vdbg_printk(TPACPI_DBG_MIXER,
"trying to checkpoint mixer state to NVRAM...\n"); "trying to checkpoint mixer state to NVRAM...\n");
...@@ -6694,6 +6717,12 @@ static int volume_set_status_ec(const u8 status) ...@@ -6694,6 +6717,12 @@ static int volume_set_status_ec(const u8 status)
dbg_printk(TPACPI_DBG_MIXER, "set EC mixer to 0x%02x\n", status); dbg_printk(TPACPI_DBG_MIXER, "set EC mixer to 0x%02x\n", status);
/*
* On X200s, and possibly on others, it can take a while for
* reads to become correct.
*/
msleep(1);
return 0; return 0;
} }
...@@ -6776,6 +6805,57 @@ static int __volume_set_volume_ec(const u8 vol) ...@@ -6776,6 +6805,57 @@ static int __volume_set_volume_ec(const u8 vol)
return rc; return rc;
} }
static int volume_set_software_mute(bool startup)
{
int result;
if (!tpacpi_is_lenovo())
return -ENODEV;
if (startup) {
if (!acpi_evalf(ec_handle, &software_mute_orig_mode,
"HAUM", "qd"))
return -EIO;
dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER,
"Initial HAUM setting was %d\n",
software_mute_orig_mode);
}
if (!acpi_evalf(ec_handle, &result, "SAUM", "qdd",
(int)TP_EC_MUTE_BTN_NONE))
return -EIO;
if (result != TP_EC_MUTE_BTN_NONE)
pr_warn("Unexpected SAUM result %d\n",
result);
/*
* In software mute mode, the standard codec controls take
* precendence, so we unmute the ThinkPad HW switch at
* startup. Just on case there are SAUM-capable ThinkPads
* with level controls, set max HW volume as well.
*/
if (tp_features.mixer_no_level_control)
result = volume_set_mute(false);
else
result = volume_set_status(TP_EC_VOLUME_MAX);
if (result != 0)
pr_warn("Failed to unmute the HW mute switch\n");
return 0;
}
static void volume_exit_software_mute(void)
{
int r;
if (!acpi_evalf(ec_handle, &r, "SAUM", "qdd", software_mute_orig_mode)
|| r != software_mute_orig_mode)
pr_warn("Failed to restore mute mode\n");
}
static int volume_alsa_set_volume(const u8 vol) static int volume_alsa_set_volume(const u8 vol)
{ {
dbg_printk(TPACPI_DBG_MIXER, dbg_printk(TPACPI_DBG_MIXER,
...@@ -6883,7 +6963,12 @@ static void volume_suspend(void) ...@@ -6883,7 +6963,12 @@ static void volume_suspend(void)
static void volume_resume(void) static void volume_resume(void)
{ {
volume_alsa_notify_change(); if (software_mute_active) {
if (volume_set_software_mute(false) < 0)
pr_warn("Failed to restore software mute\n");
} else {
volume_alsa_notify_change();
}
} }
static void volume_shutdown(void) static void volume_shutdown(void)
...@@ -6899,6 +6984,9 @@ static void volume_exit(void) ...@@ -6899,6 +6984,9 @@ static void volume_exit(void)
} }
tpacpi_volume_checkpoint_nvram(); tpacpi_volume_checkpoint_nvram();
if (software_mute_active)
volume_exit_software_mute();
} }
static int __init volume_create_alsa_mixer(void) static int __init volume_create_alsa_mixer(void)
...@@ -7083,16 +7171,20 @@ static int __init volume_init(struct ibm_init_struct *iibm) ...@@ -7083,16 +7171,20 @@ static int __init volume_init(struct ibm_init_struct *iibm)
"mute is supported, volume control is %s\n", "mute is supported, volume control is %s\n",
str_supported(!tp_features.mixer_no_level_control)); str_supported(!tp_features.mixer_no_level_control));
rc = volume_create_alsa_mixer(); if (software_mute_requested && volume_set_software_mute(true) == 0) {
if (rc) { software_mute_active = true;
pr_err("Could not create the ALSA mixer interface\n"); } else {
return rc; rc = volume_create_alsa_mixer();
} if (rc) {
pr_err("Could not create the ALSA mixer interface\n");
return rc;
}
pr_info("Console audio control enabled, mode: %s\n", pr_info("Console audio control enabled, mode: %s\n",
(volume_control_allowed) ? (volume_control_allowed) ?
"override (read/write)" : "override (read/write)" :
"monitor (read only)"); "monitor (read only)");
}
vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER,
"registering volume hotkeys as change notification\n"); "registering volume hotkeys as change notification\n");
...@@ -9089,6 +9181,10 @@ MODULE_PARM_DESC(volume_control, ...@@ -9089,6 +9181,10 @@ MODULE_PARM_DESC(volume_control,
"Enables software override for the console audio " "Enables software override for the console audio "
"control when true"); "control when true");
module_param_named(software_mute, software_mute_requested, bool, 0444);
MODULE_PARM_DESC(software_mute,
"Request full software mute control");
/* ALSA module API parameters */ /* ALSA module API parameters */
module_param_named(index, alsa_index, int, 0444); module_param_named(index, alsa_index, int, 0444);
MODULE_PARM_DESC(index, "ALSA index for the ACPI EC Mixer"); MODULE_PARM_DESC(index, "ALSA index for the ACPI EC Mixer");
......
...@@ -186,6 +186,7 @@ static struct toshiba_acpi_dev *toshiba_acpi; ...@@ -186,6 +186,7 @@ static struct toshiba_acpi_dev *toshiba_acpi;
static const struct acpi_device_id toshiba_device_ids[] = { static const struct acpi_device_id toshiba_device_ids[] = {
{"TOS6200", 0}, {"TOS6200", 0},
{"TOS6207", 0},
{"TOS6208", 0}, {"TOS6208", 0},
{"TOS1900", 0}, {"TOS1900", 0},
{"", 0}, {"", 0},
...@@ -928,9 +929,7 @@ static int lcd_proc_open(struct inode *inode, struct file *file) ...@@ -928,9 +929,7 @@ static int lcd_proc_open(struct inode *inode, struct file *file)
static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value) static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value)
{ {
u32 in[TCI_WORDS] = { HCI_SET, HCI_LCD_BRIGHTNESS, 0, 0, 0, 0 }; u32 hci_result;
u32 out[TCI_WORDS];
acpi_status status;
if (dev->tr_backlight_supported) { if (dev->tr_backlight_supported) {
bool enable = !value; bool enable = !value;
...@@ -941,20 +940,9 @@ static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value) ...@@ -941,20 +940,9 @@ static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value)
value--; value--;
} }
in[2] = value << HCI_LCD_BRIGHTNESS_SHIFT; value = value << HCI_LCD_BRIGHTNESS_SHIFT;
status = tci_raw(dev, in, out); hci_result = hci_write1(dev, HCI_LCD_BRIGHTNESS, value);
if (ACPI_FAILURE(status) || out[0] == TOS_FAILURE) { return hci_result == TOS_SUCCESS ? 0 : -EIO;
pr_err("ACPI call to set brightness failed");
return -EIO;
}
/* Extra check for "incomplete" backlight method, where the AML code
* doesn't check for HCI_SET or HCI_GET and returns TOS_SUCCESS,
* the actual brightness, and in some cases the max brightness.
*/
if (out[2] > 0 || out[3] == 0xE000)
return -ENODEV;
return out[0] == TOS_SUCCESS ? 0 : -EIO;
} }
static int set_lcd_status(struct backlight_device *bd) static int set_lcd_status(struct backlight_device *bd)
...@@ -1406,12 +1394,6 @@ static ssize_t toshiba_kbd_bl_mode_store(struct device *dev, ...@@ -1406,12 +1394,6 @@ static ssize_t toshiba_kbd_bl_mode_store(struct device *dev,
if (ret) if (ret)
return ret; return ret;
/* Update sysfs entries on successful mode change*/
ret = sysfs_update_group(&toshiba->acpi_dev->dev.kobj,
&toshiba_attr_group);
if (ret)
return ret;
toshiba->kbd_mode = mode; toshiba->kbd_mode = mode;
} }
...@@ -1586,10 +1568,32 @@ static umode_t toshiba_sysfs_is_visible(struct kobject *kobj, ...@@ -1586,10 +1568,32 @@ static umode_t toshiba_sysfs_is_visible(struct kobject *kobj,
return exists ? attr->mode : 0; return exists ? attr->mode : 0;
} }
/*
* Hotkeys
*/
static int toshiba_acpi_enable_hotkeys(struct toshiba_acpi_dev *dev)
{
acpi_status status;
u32 result;
status = acpi_evaluate_object(dev->acpi_dev->handle,
"ENAB", NULL, NULL);
if (ACPI_FAILURE(status))
return -ENODEV;
result = hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE);
if (result == TOS_FAILURE)
return -EIO;
else if (result == TOS_NOT_SUPPORTED)
return -ENODEV;
return 0;
}
static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str, static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str,
struct serio *port) struct serio *port)
{ {
if (str & 0x20) if (str & I8042_STR_AUXDATA)
return false; return false;
if (unlikely(data == 0xe0)) if (unlikely(data == 0xe0))
...@@ -1648,9 +1652,45 @@ static void toshiba_acpi_report_hotkey(struct toshiba_acpi_dev *dev, ...@@ -1648,9 +1652,45 @@ static void toshiba_acpi_report_hotkey(struct toshiba_acpi_dev *dev,
pr_info("Unknown key %x\n", scancode); pr_info("Unknown key %x\n", scancode);
} }
static void toshiba_acpi_process_hotkeys(struct toshiba_acpi_dev *dev)
{
u32 hci_result, value;
int retries = 3;
int scancode;
if (dev->info_supported) {
scancode = toshiba_acpi_query_hotkey(dev);
if (scancode < 0)
pr_err("Failed to query hotkey event\n");
else if (scancode != 0)
toshiba_acpi_report_hotkey(dev, scancode);
} else if (dev->system_event_supported) {
do {
hci_result = hci_read1(dev, HCI_SYSTEM_EVENT, &value);
switch (hci_result) {
case TOS_SUCCESS:
toshiba_acpi_report_hotkey(dev, (int)value);
break;
case TOS_NOT_SUPPORTED:
/*
* This is a workaround for an unresolved
* issue on some machines where system events
* sporadically become disabled.
*/
hci_result =
hci_write1(dev, HCI_SYSTEM_EVENT, 1);
pr_notice("Re-enabled hotkeys\n");
/* fall through */
default:
retries--;
break;
}
} while (retries && hci_result != TOS_FIFO_EMPTY);
}
}
static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
{ {
acpi_status status;
acpi_handle ec_handle; acpi_handle ec_handle;
int error; int error;
u32 hci_result; u32 hci_result;
...@@ -1677,7 +1717,6 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) ...@@ -1677,7 +1717,6 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
* supported, so if it's present set up an i8042 key filter * supported, so if it's present set up an i8042 key filter
* for this purpose. * for this purpose.
*/ */
status = AE_ERROR;
ec_handle = ec_get_handle(); ec_handle = ec_get_handle();
if (ec_handle && acpi_has_method(ec_handle, "NTFY")) { if (ec_handle && acpi_has_method(ec_handle, "NTFY")) {
INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work); INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work);
...@@ -1708,10 +1747,9 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) ...@@ -1708,10 +1747,9 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
goto err_remove_filter; goto err_remove_filter;
} }
status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", NULL, NULL); error = toshiba_acpi_enable_hotkeys(dev);
if (ACPI_FAILURE(status)) { if (error) {
pr_info("Unable to enable hotkeys\n"); pr_info("Unable to enable hotkeys\n");
error = -ENODEV;
goto err_remove_filter; goto err_remove_filter;
} }
...@@ -1721,7 +1759,6 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) ...@@ -1721,7 +1759,6 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
goto err_remove_filter; goto err_remove_filter;
} }
hci_result = hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE);
return 0; return 0;
err_remove_filter: err_remove_filter:
...@@ -1810,8 +1847,7 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev) ...@@ -1810,8 +1847,7 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
rfkill_destroy(dev->bt_rfk); rfkill_destroy(dev->bt_rfk);
} }
if (dev->backlight_dev) backlight_device_unregister(dev->backlight_dev);
backlight_device_unregister(dev->backlight_dev);
if (dev->illumination_supported) if (dev->illumination_supported)
led_classdev_unregister(&dev->led_dev); led_classdev_unregister(&dev->led_dev);
...@@ -1967,41 +2003,29 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) ...@@ -1967,41 +2003,29 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event) static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event)
{ {
struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
u32 hci_result, value; int ret;
int retries = 3;
int scancode;
if (event != 0x80)
return;
if (dev->info_supported) { switch (event) {
scancode = toshiba_acpi_query_hotkey(dev); case 0x80: /* Hotkeys and some system events */
if (scancode < 0) toshiba_acpi_process_hotkeys(dev);
pr_err("Failed to query hotkey event\n"); break;
else if (scancode != 0) case 0x92: /* Keyboard backlight mode changed */
toshiba_acpi_report_hotkey(dev, scancode); /* Update sysfs entries */
} else if (dev->system_event_supported) { ret = sysfs_update_group(&acpi_dev->dev.kobj,
do { &toshiba_attr_group);
hci_result = hci_read1(dev, HCI_SYSTEM_EVENT, &value); if (ret)
switch (hci_result) { pr_err("Unable to update sysfs entries\n");
case TOS_SUCCESS: break;
toshiba_acpi_report_hotkey(dev, (int)value); case 0x81: /* Unknown */
break; case 0x82: /* Unknown */
case TOS_NOT_SUPPORTED: case 0x83: /* Unknown */
/* case 0x8c: /* Unknown */
* This is a workaround for an unresolved case 0x8e: /* Unknown */
* issue on some machines where system events case 0x8f: /* Unknown */
* sporadically become disabled. case 0x90: /* Unknown */
*/ default:
hci_result = pr_info("Unknown event received %x\n", event);
hci_write1(dev, HCI_SYSTEM_EVENT, 1); break;
pr_notice("Re-enabled hotkeys\n");
/* fall through */
default:
retries--;
break;
}
} while (retries && hci_result != TOS_FIFO_EMPTY);
} }
} }
...@@ -2020,16 +2044,12 @@ static int toshiba_acpi_suspend(struct device *device) ...@@ -2020,16 +2044,12 @@ static int toshiba_acpi_suspend(struct device *device)
static int toshiba_acpi_resume(struct device *device) static int toshiba_acpi_resume(struct device *device)
{ {
struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device)); struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device));
u32 result; int error;
acpi_status status;
if (dev->hotkey_dev) { if (dev->hotkey_dev) {
status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", error = toshiba_acpi_enable_hotkeys(dev);
NULL, NULL); if (error)
if (ACPI_FAILURE(status))
pr_info("Unable to re-enable hotkeys\n"); pr_info("Unable to re-enable hotkeys\n");
result = hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE);
} }
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment