Commit 2937f5ef authored by Linus Torvalds's avatar Linus Torvalds

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
parents 9ee4d7a6 f82bdd0d
compal-laptop
=============
List of supported hardware:
by Compal:
Compal FL90/IFL90
Compal FL91/IFL91
Compal FL92/JFL92
Compal FT00/IFT00
by Dell:
Dell Vostro 1200
Dell Mini 9 (Inspiron 910)
Dell Mini 10 (Inspiron 1010)
Dell Mini 10v (Inspiron 1011)
Dell Mini 1012 (Inspiron 1012)
Dell Inspiron 11z (Inspiron 1110)
Dell Mini 12 (Inspiron 1210)
......@@ -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:
This is a driver for laptops built by Compal, and some models by
other brands (e.g. Dell, Toshiba).
Compal FL90/IFL90
Compal FL91/IFL91
Compal FL92/JFL92
Compal FT00/IFT00
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.
......
......@@ -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
......
......@@ -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,
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 struct attribute_group hdmi_attribute_group = {
.name = "hdmi",
.attrs = hdmi_attrs,
};
static void remove_hdmi(struct platform_device *device)
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;
}
......
......@@ -135,6 +135,15 @@ static struct dmi_system_id asus_quirks[] = {
},
.driver_data = &quirk_asus_x401u,
},
{
.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",
......
......@@ -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;
......
/*
* 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");
......@@ -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;
......
......@@ -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 (!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);
else
priv->rfk[i] = NULL;
}
ideapad_sync_rfk_state(priv);
ideapad_sync_touchpad_state(priv);
......@@ -884,8 +898,6 @@ static int ideapad_acpi_add(struct platform_device *pdev)
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;
}
......
......@@ -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 @@ static int mid_thermal_probe(struct platform_device *pdev)
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);
}
......
......@@ -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)
......
......@@ -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 */
};
......
......@@ -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);
......
......@@ -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,
},
};
......
......@@ -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;
......
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