diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-10 16:58:32 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-10 16:58:32 -0700 |
commit | 2937f5efa5754754daf46de745f67350f7f06ec2 (patch) | |
tree | 2b146ffc245cc78a6076585eddeda65ac2b6062f /drivers/platform | |
parent | 9ee4d7a6538308a7681b638d2f35f2a301420355 (diff) | |
parent | f82bdd0d77b6bf0dea08a1d957ab45d503f328b1 (diff) |
Merge branch 'for_linus' of git://cavan.codon.org.uk/platform-drivers-x86
Pull x86 platform driver updates from Matthew Garrett:
"Very little of excitement here - the most significant is a new driver
for detecting device freefall on Dells, other than that it's pretty
much entirely minor fixes for specific machines"
* 'for_linus' of git://cavan.codon.org.uk/platform-drivers-x86:
hp-wmi: Enable hotkeys on some systems
thinkpad_acpi: Add mappings for F9 - F12 hotkeys on X240 / T440 / T540
platform: x86: dell-smo8800: Dell Latitude freefall driver (ACPI SMO8800/SMO8810)
ideapad_laptop: Introduce the use of the managed version of kzalloc
platform/x86: Fix run-time dependencies of OLPC drivers
platform: x86: asus-wmi.c: Cleaning up uninitialized variables
ix86/mid/thermal: Introduce the use of the managed version of kzalloc
platform x86 Kconfig: Refer to the laptop list in the Compal driver help
Documentation: Add list of laptop models supported by the Compal driver
ideapad-laptop: Blacklist rfkill control on the Lenovo Yoga 2 11
asus-wmi: Set WAPF to 4 for Asus X550CA
alienware-wmi: For WMAX HDMI method, introduce a way to query HDMI cable status
alienware-wmi: Update WMAX brightness method limit to 15
pvpanic: Set high notifier priority
platform/x86: samsung-laptop: Add support for Samsung's NP7[34]0U3E models.
toshiba_acpi: Add alternative keymap support for Satellite M840
platform-drivers-x86: intel_pmic_gpio: Fix off-by-one valid offset range check
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/Kconfig | 31 | ||||
-rw-r--r-- | drivers/platform/x86/Makefile | 1 | ||||
-rw-r--r-- | drivers/platform/x86/alienware-wmi.c | 121 | ||||
-rw-r--r-- | drivers/platform/x86/asus-nb-wmi.c | 9 | ||||
-rw-r--r-- | drivers/platform/x86/asus-wmi.c | 4 | ||||
-rw-r--r-- | drivers/platform/x86/dell-smo8800.c | 233 | ||||
-rw-r--r-- | drivers/platform/x86/hp-wmi.c | 18 | ||||
-rw-r--r-- | drivers/platform/x86/ideapad-laptop.c | 31 | ||||
-rw-r--r-- | drivers/platform/x86/intel_mid_thermal.c | 7 | ||||
-rw-r--r-- | drivers/platform/x86/intel_pmic_gpio.c | 4 | ||||
-rw-r--r-- | drivers/platform/x86/pvpanic.c | 1 | ||||
-rw-r--r-- | drivers/platform/x86/samsung-laptop.c | 38 | ||||
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 4 | ||||
-rw-r--r-- | drivers/platform/x86/toshiba_acpi.c | 30 |
14 files changed, 467 insertions, 65 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 27df2c533b09..172f26ce59ac 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -102,7 +102,7 @@ config DELL_LAPTOP default n ---help--- This driver adds support for rfkill and backlight control to Dell - laptops. + laptops (except for some models covered by the Compal driver). config DELL_WMI tristate "Dell WMI extras" @@ -127,6 +127,16 @@ config DELL_WMI_AIO To compile this driver as a module, choose M here: the module will be called dell-wmi-aio. +config DELL_SMO8800 + tristate "Dell Latitude freefall driver (ACPI SMO8800/SMO8810)" + depends on ACPI + ---help--- + Say Y here if you want to support SMO8800/SMO8810 freefall device + on Dell Latitude laptops. + + To compile this driver as a module, choose M here: the module will + be called dell-smo8800. + config FUJITSU_LAPTOP tristate "Fujitsu Laptop Extras" @@ -265,23 +275,21 @@ config PANASONIC_LAPTOP R2, R3, R5, T2, W2 and Y2 series), say Y. config COMPAL_LAPTOP - tristate "Compal Laptop Extras" + tristate "Compal (and others) Laptop Extras" depends on ACPI depends on BACKLIGHT_CLASS_DEVICE depends on RFKILL depends on HWMON depends on POWER_SUPPLY ---help--- - This is a driver for laptops built by Compal: - - Compal FL90/IFL90 - Compal FL91/IFL91 - Compal FL92/JFL92 - Compal FT00/IFT00 + This is a driver for laptops built by Compal, and some models by + other brands (e.g. Dell, Toshiba). - It adds support for Bluetooth, WLAN and LCD brightness control. + It adds support for rfkill, Bluetooth, WLAN and LCD brightness + control. - If you have an Compal FL9x/IFL9x/FT00 laptop, say Y or M here. + For a (possibly incomplete) list of supported laptops, please refer + to: Documentation/platform/x86-laptop-drivers.txt config SONY_LAPTOP tristate "Sony Laptop Extras" @@ -724,7 +732,7 @@ config IBM_RTL config XO1_RFKILL tristate "OLPC XO-1 software RF kill switch" - depends on OLPC + depends on OLPC || COMPILE_TEST depends on RFKILL ---help--- Support for enabling/disabling the WLAN interface on the OLPC XO-1 @@ -732,6 +740,7 @@ config XO1_RFKILL config XO15_EBOOK tristate "OLPC XO-1.5 ebook switch" + depends on OLPC || COMPILE_TEST depends on ACPI && INPUT ---help--- Support for the ebook switch on the OLPC XO-1.5 laptop. diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 1a2eafc9d48e..c4ca428fd3db 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o obj-$(CONFIG_DELL_WMI) += dell-wmi.o obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o +obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o obj-$(CONFIG_ACER_WMI) += acer-wmi.o obj-$(CONFIG_ACERHDF) += acerhdf.o obj-$(CONFIG_HP_ACCEL) += hp_accel.o diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c index 541f9514f76f..297b6640213f 100644 --- a/drivers/platform/x86/alienware-wmi.c +++ b/drivers/platform/x86/alienware-wmi.c @@ -32,6 +32,7 @@ #define WMAX_METHOD_HDMI_STATUS 0x2 #define WMAX_METHOD_BRIGHTNESS 0x3 #define WMAX_METHOD_ZONE_CONTROL 0x4 +#define WMAX_METHOD_HDMI_CABLE 0x5 MODULE_AUTHOR("Mario Limonciello <mario_limonciello@dell.com>"); MODULE_DESCRIPTION("Alienware special feature control"); @@ -350,12 +351,11 @@ static int alienware_zone_init(struct platform_device *dev) char *name; if (interface == WMAX) { - global_led.max_brightness = 100; lighting_control_state = WMAX_RUNNING; } else if (interface == LEGACY) { - global_led.max_brightness = 0x0F; lighting_control_state = LEGACY_RUNNING; } + global_led.max_brightness = 0x0F; global_brightness = global_led.max_brightness; /* @@ -423,41 +423,85 @@ static void alienware_zone_exit(struct platform_device *dev) The HDMI mux sysfs node indicates the status of the HDMI input mux. It can toggle between standard system GPU output and HDMI input. */ -static ssize_t show_hdmi(struct device *dev, struct device_attribute *attr, - char *buf) +static acpi_status alienware_hdmi_command(struct hdmi_args *in_args, + u32 command, int *out_data) { acpi_status status; - struct acpi_buffer input; union acpi_object *obj; - u32 tmp = 0; - struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer input; + struct acpi_buffer output; + + input.length = (acpi_size) sizeof(*in_args); + input.pointer = in_args; + if (out_data != NULL) { + output.length = ACPI_ALLOCATE_BUFFER; + output.pointer = NULL; + status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, + command, &input, &output); + } else + status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, + command, &input, NULL); + + if (ACPI_SUCCESS(status) && out_data != NULL) { + obj = (union acpi_object *)output.pointer; + if (obj && obj->type == ACPI_TYPE_INTEGER) + *out_data = (u32) obj->integer.value; + } + return status; + +} + +static ssize_t show_hdmi_cable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + acpi_status status; + u32 out_data; struct hdmi_args in_args = { .arg = 0, }; - input.length = (acpi_size) sizeof(in_args); - input.pointer = &in_args; - status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, - WMAX_METHOD_HDMI_STATUS, &input, &output); + status = + alienware_hdmi_command(&in_args, WMAX_METHOD_HDMI_CABLE, + (u32 *) &out_data); + if (ACPI_SUCCESS(status)) { + if (out_data == 0) + return scnprintf(buf, PAGE_SIZE, + "[unconnected] connected unknown\n"); + else if (out_data == 1) + return scnprintf(buf, PAGE_SIZE, + "unconnected [connected] unknown\n"); + } + pr_err("alienware-wmi: unknown HDMI cable status: %d\n", status); + return scnprintf(buf, PAGE_SIZE, "unconnected connected [unknown]\n"); +} + +static ssize_t show_hdmi_source(struct device *dev, + struct device_attribute *attr, char *buf) +{ + acpi_status status; + u32 out_data; + struct hdmi_args in_args = { + .arg = 0, + }; + status = + alienware_hdmi_command(&in_args, WMAX_METHOD_HDMI_STATUS, + (u32 *) &out_data); if (ACPI_SUCCESS(status)) { - obj = (union acpi_object *)output.pointer; - if (obj && obj->type == ACPI_TYPE_INTEGER) - tmp = (u32) obj->integer.value; - if (tmp == 1) + if (out_data == 1) return scnprintf(buf, PAGE_SIZE, "[input] gpu unknown\n"); - else if (tmp == 2) + else if (out_data == 2) return scnprintf(buf, PAGE_SIZE, "input [gpu] unknown\n"); } - pr_err("alienware-wmi: unknown HDMI status: %d\n", status); + pr_err("alienware-wmi: unknown HDMI source status: %d\n", out_data); return scnprintf(buf, PAGE_SIZE, "input gpu [unknown]\n"); } -static ssize_t toggle_hdmi(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t toggle_hdmi_source(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct acpi_buffer input; acpi_status status; struct hdmi_args args; if (strcmp(buf, "gpu\n") == 0) @@ -467,33 +511,46 @@ static ssize_t toggle_hdmi(struct device *dev, struct device_attribute *attr, else args.arg = 3; pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf); - input.length = (acpi_size) sizeof(args); - input.pointer = &args; - status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, - WMAX_METHOD_HDMI_SOURCE, &input, NULL); + + status = alienware_hdmi_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL); + if (ACPI_FAILURE(status)) pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", status); return count; } -static DEVICE_ATTR(hdmi, S_IRUGO | S_IWUSR, show_hdmi, toggle_hdmi); +static DEVICE_ATTR(cable, S_IRUGO, show_hdmi_cable, NULL); +static DEVICE_ATTR(source, S_IRUGO | S_IWUSR, show_hdmi_source, + toggle_hdmi_source); + +static struct attribute *hdmi_attrs[] = { + &dev_attr_cable.attr, + &dev_attr_source.attr, + NULL, +}; -static void remove_hdmi(struct platform_device *device) +static struct attribute_group hdmi_attribute_group = { + .name = "hdmi", + .attrs = hdmi_attrs, +}; + +static void remove_hdmi(struct platform_device *dev) { - device_remove_file(&device->dev, &dev_attr_hdmi); + sysfs_remove_group(&dev->dev.kobj, &hdmi_attribute_group); } -static int create_hdmi(void) +static int create_hdmi(struct platform_device *dev) { - int ret = -ENOMEM; - ret = device_create_file(&platform_device->dev, &dev_attr_hdmi); + int ret; + + ret = sysfs_create_group(&dev->dev.kobj, &hdmi_attribute_group); if (ret) goto error_create_hdmi; return 0; error_create_hdmi: - remove_hdmi(platform_device); + remove_hdmi(dev); return ret; } @@ -527,7 +584,7 @@ static int __init alienware_wmi_init(void) goto fail_platform_device2; if (interface == WMAX) { - ret = create_hdmi(); + ret = create_hdmi(platform_device); if (ret) goto fail_prep_hdmi; } diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index 563f59efa669..ddf0eefd862c 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -137,6 +137,15 @@ static struct dmi_system_id asus_quirks[] = { }, { .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. X550CA", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X550CA"), + }, + .driver_data = &quirk_asus_x401u, + }, + { + .callback = dmi_matched, .ident = "ASUSTeK COMPUTER INC. X55A", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 91ef69a52263..3c6ccedc82b6 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -266,7 +266,7 @@ static int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; acpi_status status; union acpi_object *obj; - u32 tmp; + u32 tmp = 0; status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 1, method_id, &input, &output); @@ -277,8 +277,6 @@ static int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, obj = (union acpi_object *)output.pointer; if (obj && obj->type == ACPI_TYPE_INTEGER) tmp = (u32) obj->integer.value; - else - tmp = 0; if (retval) *retval = tmp; diff --git a/drivers/platform/x86/dell-smo8800.c b/drivers/platform/x86/dell-smo8800.c new file mode 100644 index 000000000000..a653716055d1 --- /dev/null +++ b/drivers/platform/x86/dell-smo8800.c @@ -0,0 +1,233 @@ +/* + * dell-smo8800.c - Dell Latitude ACPI SMO8800/SMO8810 freefall sensor driver + * + * Copyright (C) 2012 Sonal Santan <sonal.santan@gmail.com> + * Copyright (C) 2014 Pali Rohár <pali.rohar@gmail.com> + * + * This is loosely based on lis3lv02d driver. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define DRIVER_NAME "smo8800" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/acpi.h> +#include <linux/interrupt.h> +#include <linux/miscdevice.h> + +struct smo8800_device { + u32 irq; /* acpi device irq */ + atomic_t counter; /* count after last read */ + struct miscdevice miscdev; /* for /dev/freefall */ + unsigned long misc_opened; /* whether the device is open */ + wait_queue_head_t misc_wait; /* Wait queue for the misc dev */ + struct device *dev; /* acpi device */ +}; + +static irqreturn_t smo8800_interrupt_quick(int irq, void *data) +{ + struct smo8800_device *smo8800 = data; + + atomic_inc(&smo8800->counter); + wake_up_interruptible(&smo8800->misc_wait); + return IRQ_WAKE_THREAD; +} + +static irqreturn_t smo8800_interrupt_thread(int irq, void *data) +{ + struct smo8800_device *smo8800 = data; + + dev_info(smo8800->dev, "detected free fall\n"); + return IRQ_HANDLED; +} + +static acpi_status smo8800_get_resource(struct acpi_resource *resource, + void *context) +{ + struct acpi_resource_extended_irq *irq; + + if (resource->type != ACPI_RESOURCE_TYPE_EXTENDED_IRQ) + return AE_OK; + + irq = &resource->data.extended_irq; + if (!irq || !irq->interrupt_count) + return AE_OK; + + *((u32 *)context) = irq->interrupts[0]; + return AE_CTRL_TERMINATE; +} + +static u32 smo8800_get_irq(struct acpi_device *device) +{ + u32 irq = 0; + acpi_status status; + + status = acpi_walk_resources(device->handle, METHOD_NAME__CRS, + smo8800_get_resource, &irq); + if (ACPI_FAILURE(status)) { + dev_err(&device->dev, "acpi_walk_resources failed\n"); + return 0; + } + + return irq; +} + +static ssize_t smo8800_misc_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct smo8800_device *smo8800 = container_of(file->private_data, + struct smo8800_device, miscdev); + + u32 data = 0; + unsigned char byte_data = 0; + ssize_t retval = 1; + + if (count < 1) + return -EINVAL; + + atomic_set(&smo8800->counter, 0); + retval = wait_event_interruptible(smo8800->misc_wait, + (data = atomic_xchg(&smo8800->counter, 0))); + + if (retval) + return retval; + + byte_data = 1; + retval = 1; + + if (data < 255) + byte_data = data; + else + byte_data = 255; + + if (put_user(byte_data, buf)) + retval = -EFAULT; + + return retval; +} + +static int smo8800_misc_open(struct inode *inode, struct file *file) +{ + struct smo8800_device *smo8800 = container_of(file->private_data, + struct smo8800_device, miscdev); + + if (test_and_set_bit(0, &smo8800->misc_opened)) + return -EBUSY; /* already open */ + + atomic_set(&smo8800->counter, 0); + return 0; +} + +static int smo8800_misc_release(struct inode *inode, struct file *file) +{ + struct smo8800_device *smo8800 = container_of(file->private_data, + struct smo8800_device, miscdev); + + clear_bit(0, &smo8800->misc_opened); /* release the device */ + return 0; +} + +static const struct file_operations smo8800_misc_fops = { + .owner = THIS_MODULE, + .read = smo8800_misc_read, + .open = smo8800_misc_open, + .release = smo8800_misc_release, +}; + +static int smo8800_add(struct acpi_device *device) +{ + int err; + struct smo8800_device *smo8800; + + smo8800 = devm_kzalloc(&device->dev, sizeof(*smo8800), GFP_KERNEL); + if (!smo8800) { + dev_err(&device->dev, "failed to allocate device data\n"); + return -ENOMEM; + } + + smo8800->dev = &device->dev; + smo8800->miscdev.minor = MISC_DYNAMIC_MINOR; + smo8800->miscdev.name = "freefall"; + smo8800->miscdev.fops = &smo8800_misc_fops; + + init_waitqueue_head(&smo8800->misc_wait); + + err = misc_register(&smo8800->miscdev); + if (err) { + dev_err(&device->dev, "failed to register misc dev: %d\n", err); + return err; + } + + device->driver_data = smo8800; + + smo8800->irq = smo8800_get_irq(device); + if (!smo8800->irq) { + dev_err(&device->dev, "failed to obtain IRQ\n"); + err = -EINVAL; + goto error; + } + + err = request_threaded_irq(smo8800->irq, smo8800_interrupt_quick, + smo8800_interrupt_thread, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + DRIVER_NAME, smo8800); + if (err) { + dev_err(&device->dev, + "failed to request thread for IRQ %d: %d\n", + smo8800->irq, err); + goto error; + } + + dev_dbg(&device->dev, "device /dev/freefall registered with IRQ %d\n", + smo8800->irq); + return 0; + +error: + misc_deregister(&smo8800->miscdev); + return err; +} + +static int smo8800_remove(struct acpi_device *device) +{ + struct smo8800_device *smo8800 = device->driver_data; + + free_irq(smo8800->irq, smo8800); + misc_deregister(&smo8800->miscdev); + dev_dbg(&device->dev, "device /dev/freefall unregistered\n"); + return 0; +} + +static const struct acpi_device_id smo8800_ids[] = { + { "SMO8800", 0 }, + { "SMO8810", 0 }, + { "", 0 }, +}; + +MODULE_DEVICE_TABLE(acpi, smo8800_ids); + +static struct acpi_driver smo8800_driver = { + .name = DRIVER_NAME, + .class = "Latitude", + .ids = smo8800_ids, + .ops = { + .add = smo8800_add, + .remove = smo8800_remove, + }, + .owner = THIS_MODULE, +}; + +module_acpi_driver(smo8800_driver); + +MODULE_DESCRIPTION("Dell Latitude freefall driver (ACPI SMO8800/SMO8810)"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sonal Santan, Pali Rohár"); diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 8ba8956b5a48..484a8673b835 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -53,6 +53,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); #define HPWMI_ALS_QUERY 0x3 #define HPWMI_HARDWARE_QUERY 0x4 #define HPWMI_WIRELESS_QUERY 0x5 +#define HPWMI_BIOS_QUERY 0x9 #define HPWMI_HOTKEY_QUERY 0xc #define HPWMI_FEATURE_QUERY 0xd #define HPWMI_WIRELESS2_QUERY 0x1b @@ -144,6 +145,7 @@ static const struct key_entry hp_wmi_keymap[] = { { KE_KEY, 0x2142, { KEY_MEDIA } }, { KE_KEY, 0x213b, { KEY_INFO } }, { KE_KEY, 0x2169, { KEY_DIRECTION } }, + { KE_KEY, 0x216a, { KEY_SETUP } }, { KE_KEY, 0x231b, { KEY_HELP } }, { KE_END, 0 } }; @@ -304,6 +306,19 @@ static int hp_wmi_bios_2009_later(void) return (state & 0x10) ? 1 : 0; } +static int hp_wmi_enable_hotkeys(void) +{ + int ret; + int query = 0x6e; + + ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, 1, &query, sizeof(query), + 0); + + if (ret) + return -EINVAL; + return 0; +} + static int hp_wmi_set_block(void *data, bool blocked) { enum hp_wmi_radio r = (enum hp_wmi_radio) data; @@ -648,6 +663,9 @@ static int __init hp_wmi_input_setup(void) hp_wmi_tablet_state()); input_sync(hp_wmi_input_dev); + if (hp_wmi_bios_2009_later() == 4) + hp_wmi_enable_hotkeys(); + status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL); if (ACPI_FAILURE(status)) { err = -EIO; diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 6dd060a0bb65..b4c495a62eec 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -36,6 +36,8 @@ #include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/i8042.h> +#include <linux/dmi.h> +#include <linux/device.h> #define IDEAPAD_RFKILL_DEV_NUM (3) @@ -819,6 +821,19 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data) } } +/* Blacklist for devices where the ideapad rfkill interface does not work */ +static struct dmi_system_id rfkill_blacklist[] = { + /* The Lenovo Yoga 2 11 always reports everything as blocked */ + { + .ident = "Lenovo Yoga 2 11", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 2 11"), + }, + }, + {} +}; + static int ideapad_acpi_add(struct platform_device *pdev) { int ret, i; @@ -833,7 +848,7 @@ static int ideapad_acpi_add(struct platform_device *pdev) if (read_method_int(adev->handle, "_CFG", &cfg)) return -ENODEV; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -844,7 +859,7 @@ static int ideapad_acpi_add(struct platform_device *pdev) ret = ideapad_sysfs_init(priv); if (ret) - goto sysfs_failed; + return ret; ret = ideapad_debugfs_init(priv); if (ret) @@ -854,11 +869,10 @@ static int ideapad_acpi_add(struct platform_device *pdev) if (ret) goto input_failed; - for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) { - if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg)) - ideapad_register_rfkill(priv, i); - else - priv->rfk[i] = NULL; + if (!dmi_check_system(rfkill_blacklist)) { + for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) + if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg)) + ideapad_register_rfkill(priv, i); } ideapad_sync_rfk_state(priv); ideapad_sync_touchpad_state(priv); @@ -884,8 +898,6 @@ input_failed: ideapad_debugfs_exit(priv); debugfs_failed: ideapad_sysfs_exit(priv); -sysfs_failed: - kfree(priv); return ret; } @@ -903,7 +915,6 @@ static int ideapad_acpi_remove(struct platform_device *pdev) ideapad_debugfs_exit(priv); ideapad_sysfs_exit(priv); dev_set_drvdata(&pdev->dev, NULL); - kfree(priv); return 0; } diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c index 93fab8b70ce1..ab7860a21a22 100644 --- a/drivers/platform/x86/intel_mid_thermal.c +++ b/drivers/platform/x86/intel_mid_thermal.c @@ -481,7 +481,8 @@ static int mid_thermal_probe(struct platform_device *pdev) int i; struct platform_info *pinfo; - pinfo = kzalloc(sizeof(struct platform_info), GFP_KERNEL); + pinfo = devm_kzalloc(&pdev->dev, sizeof(struct platform_info), + GFP_KERNEL); if (!pinfo) return -ENOMEM; @@ -489,7 +490,6 @@ static int mid_thermal_probe(struct platform_device *pdev) ret = mid_initialize_adc(&pdev->dev); if (ret) { dev_err(&pdev->dev, "ADC init failed"); - kfree(pinfo); return ret; } @@ -520,7 +520,6 @@ err: thermal_zone_device_unregister(pinfo->tzd[i]); } configure_adc(0); - kfree(pinfo); return ret; } @@ -541,8 +540,6 @@ static int mid_thermal_remove(struct platform_device *pdev) thermal_zone_device_unregister(pinfo->tzd[i]); } - kfree(pinfo); - /* Stop the ADC */ return configure_adc(0); } diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c index 2805988485f6..40929e4f7ad7 100644 --- a/drivers/platform/x86/intel_pmic_gpio.c +++ b/drivers/platform/x86/intel_pmic_gpio.c @@ -91,7 +91,7 @@ static void pmic_program_irqtype(int gpio, int type) static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { - if (offset > 8) { + if (offset >= 8) { pr_err("only pin 0-7 support input\n"); return -1;/* we only have 8 GPIO can use as input */ } @@ -130,7 +130,7 @@ static int pmic_gpio_get(struct gpio_chip *chip, unsigned offset) int ret; /* we only have 8 GPIO pins we can use as input */ - if (offset > 8) + if (offset >= 8) return -EOPNOTSUPP; ret = intel_scu_ipc_ioread8(GPIO0 + offset, &r); if (ret < 0) diff --git a/drivers/platform/x86/pvpanic.c b/drivers/platform/x86/pvpanic.c index c9f6e511daa6..073a90a63dbc 100644 --- a/drivers/platform/x86/pvpanic.c +++ b/drivers/platform/x86/pvpanic.c @@ -70,6 +70,7 @@ pvpanic_panic_notify(struct notifier_block *nb, unsigned long code, static struct notifier_block pvpanic_panic_nb = { .notifier_call = pvpanic_panic_notify, + .priority = 1, /* let this called before broken drm_fb_helper */ }; diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index d1f030053176..5a5966512277 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -27,6 +27,7 @@ #include <linux/debugfs.h> #include <linux/ctype.h> #include <linux/efi.h> +#include <linux/suspend.h> #include <acpi/video.h> /* @@ -340,6 +341,8 @@ struct samsung_laptop { struct samsung_laptop_debug debug; struct samsung_quirks *quirks; + struct notifier_block pm_nb; + bool handle_backlight; bool has_stepping_quirk; @@ -348,6 +351,8 @@ struct samsung_laptop { struct samsung_quirks { bool broken_acpi_video; + bool four_kbd_backlight_levels; + bool enable_kbd_backlight; }; static struct samsung_quirks samsung_unknown = {}; @@ -356,6 +361,11 @@ static struct samsung_quirks samsung_broken_acpi_video = { .broken_acpi_video = true, }; +static struct samsung_quirks samsung_np740u3e = { + .four_kbd_backlight_levels = true, + .enable_kbd_backlight = true, +}; + static bool force; module_param(force, bool, 0); MODULE_PARM_DESC(force, @@ -1051,6 +1061,8 @@ static int __init samsung_leds_init(struct samsung_laptop *samsung) samsung->kbd_led.brightness_set = kbd_led_set; samsung->kbd_led.brightness_get = kbd_led_get; samsung->kbd_led.max_brightness = 8; + if (samsung->quirks->four_kbd_backlight_levels) + samsung->kbd_led.max_brightness = 4; ret = led_classdev_register(&samsung->platform_device->dev, &samsung->kbd_led); @@ -1414,6 +1426,19 @@ static void samsung_platform_exit(struct samsung_laptop *samsung) } } +static int samsung_pm_notification(struct notifier_block *nb, + unsigned long val, void *ptr) +{ + struct samsung_laptop *samsung; + + samsung = container_of(nb, struct samsung_laptop, pm_nb); + if (val == PM_POST_HIBERNATION && + samsung->quirks->enable_kbd_backlight) + kbd_backlight_enable(samsung); + + return 0; +} + static int __init samsung_platform_init(struct samsung_laptop *samsung) { struct platform_device *pdev; @@ -1534,6 +1559,15 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = { }, .driver_data = &samsung_broken_acpi_video, }, + { + .callback = samsung_dmi_matched, + .ident = "730U3E/740U3E", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"), + }, + .driver_data = &samsung_np740u3e, + }, { }, }; MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); @@ -1608,6 +1642,9 @@ static int __init samsung_init(void) if (ret) goto error_debugfs; + samsung->pm_nb.notifier_call = samsung_pm_notification; + register_pm_notifier(&samsung->pm_nb); + samsung_platform_device = samsung->platform_device; return ret; @@ -1633,6 +1670,7 @@ static void __exit samsung_exit(void) struct samsung_laptop *samsung; samsung = platform_get_drvdata(samsung_platform_device); + unregister_pm_notifier(&samsung->pm_nb); samsung_debugfs_exit(samsung); samsung_leds_exit(samsung); diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 15e61c16736e..d82f196e3cfe 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -3171,8 +3171,10 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) KEY_MICMUTE, /* 0x1a: Mic mute (since ?400 or so) */ /* (assignments unknown, please report if found) */ - KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, + + /* Extra keys in use since the X240 / T440 / T540 */ + KEY_CONFIG, KEY_SEARCH, KEY_SCALE, KEY_COMPUTER, }, }; diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 46473ca7566b..76441dcbe5ff 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -56,6 +56,7 @@ #include <linux/workqueue.h> #include <linux/i8042.h> #include <linux/acpi.h> +#include <linux/dmi.h> #include <asm/uaccess.h> MODULE_AUTHOR("John Belmonte"); @@ -213,6 +214,30 @@ static const struct key_entry toshiba_acpi_keymap[] = { { KE_END, 0 }, }; +/* alternative keymap */ +static const struct dmi_system_id toshiba_alt_keymap_dmi[] = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "Satellite M840"), + }, + }, + {} +}; + +static const struct key_entry toshiba_acpi_alt_keymap[] = { + { KE_KEY, 0x157, { KEY_MUTE } }, + { KE_KEY, 0x102, { KEY_ZOOMOUT } }, + { KE_KEY, 0x103, { KEY_ZOOMIN } }, + { KE_KEY, 0x139, { KEY_ZOOMRESET } }, + { KE_KEY, 0x13e, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0x13c, { KEY_BRIGHTNESSDOWN } }, + { KE_KEY, 0x13d, { KEY_BRIGHTNESSUP } }, + { KE_KEY, 0x158, { KEY_WLAN } }, + { KE_KEY, 0x13f, { KEY_TOUCHPAD_TOGGLE } }, + { KE_END, 0 }, +}; + /* utility */ @@ -1440,6 +1465,7 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) acpi_handle ec_handle; int error; u32 hci_result; + const struct key_entry *keymap = toshiba_acpi_keymap; dev->hotkey_dev = input_allocate_device(); if (!dev->hotkey_dev) @@ -1449,7 +1475,9 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) dev->hotkey_dev->phys = "toshiba_acpi/input0"; dev->hotkey_dev->id.bustype = BUS_HOST; - error = sparse_keymap_setup(dev->hotkey_dev, toshiba_acpi_keymap, NULL); + if (dmi_check_system(toshiba_alt_keymap_dmi)) + keymap = toshiba_acpi_alt_keymap; + error = sparse_keymap_setup(dev->hotkey_dev, keymap, NULL); if (error) goto err_free_dev; |