aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-06-10 19:58:32 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-06-10 19:58:32 -0400
commit2937f5efa5754754daf46de745f67350f7f06ec2 (patch)
tree2b146ffc245cc78a6076585eddeda65ac2b6062f
parent9ee4d7a6538308a7681b638d2f35f2a301420355 (diff)
parentf82bdd0d77b6bf0dea08a1d957ab45d503f328b1 (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
-rw-r--r--Documentation/platform/x86-laptop-drivers.txt18
-rw-r--r--drivers/platform/x86/Kconfig31
-rw-r--r--drivers/platform/x86/Makefile1
-rw-r--r--drivers/platform/x86/alienware-wmi.c121
-rw-r--r--drivers/platform/x86/asus-nb-wmi.c9
-rw-r--r--drivers/platform/x86/asus-wmi.c4
-rw-r--r--drivers/platform/x86/dell-smo8800.c233
-rw-r--r--drivers/platform/x86/hp-wmi.c18
-rw-r--r--drivers/platform/x86/ideapad-laptop.c31
-rw-r--r--drivers/platform/x86/intel_mid_thermal.c7
-rw-r--r--drivers/platform/x86/intel_pmic_gpio.c4
-rw-r--r--drivers/platform/x86/pvpanic.c1
-rw-r--r--drivers/platform/x86/samsung-laptop.c38
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c4
-rw-r--r--drivers/platform/x86/toshiba_acpi.c30
15 files changed, 485 insertions, 65 deletions
diff --git a/Documentation/platform/x86-laptop-drivers.txt b/Documentation/platform/x86-laptop-drivers.txt
new file mode 100644
index 000000000000..01facd2590bb
--- /dev/null
+++ b/Documentation/platform/x86-laptop-drivers.txt
@@ -0,0 +1,18 @@
1compal-laptop
2=============
3List of supported hardware:
4
5by Compal:
6 Compal FL90/IFL90
7 Compal FL91/IFL91
8 Compal FL92/JFL92
9 Compal FT00/IFT00
10
11by Dell:
12 Dell Vostro 1200
13 Dell Mini 9 (Inspiron 910)
14 Dell Mini 10 (Inspiron 1010)
15 Dell Mini 10v (Inspiron 1011)
16 Dell Mini 1012 (Inspiron 1012)
17 Dell Inspiron 11z (Inspiron 1110)
18 Dell Mini 12 (Inspiron 1210)
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
102 default n 102 default n
103 ---help--- 103 ---help---
104 This driver adds support for rfkill and backlight control to Dell 104 This driver adds support for rfkill and backlight control to Dell
105 laptops. 105 laptops (except for some models covered by the Compal driver).
106 106
107config DELL_WMI 107config DELL_WMI
108 tristate "Dell WMI extras" 108 tristate "Dell WMI extras"
@@ -127,6 +127,16 @@ config DELL_WMI_AIO
127 To compile this driver as a module, choose M here: the module will 127 To compile this driver as a module, choose M here: the module will
128 be called dell-wmi-aio. 128 be called dell-wmi-aio.
129 129
130config DELL_SMO8800
131 tristate "Dell Latitude freefall driver (ACPI SMO8800/SMO8810)"
132 depends on ACPI
133 ---help---
134 Say Y here if you want to support SMO8800/SMO8810 freefall device
135 on Dell Latitude laptops.
136
137 To compile this driver as a module, choose M here: the module will
138 be called dell-smo8800.
139
130 140
131config FUJITSU_LAPTOP 141config FUJITSU_LAPTOP
132 tristate "Fujitsu Laptop Extras" 142 tristate "Fujitsu Laptop Extras"
@@ -265,23 +275,21 @@ config PANASONIC_LAPTOP
265 R2, R3, R5, T2, W2 and Y2 series), say Y. 275 R2, R3, R5, T2, W2 and Y2 series), say Y.
266 276
267config COMPAL_LAPTOP 277config COMPAL_LAPTOP
268 tristate "Compal Laptop Extras" 278 tristate "Compal (and others) Laptop Extras"
269 depends on ACPI 279 depends on ACPI
270 depends on BACKLIGHT_CLASS_DEVICE 280 depends on BACKLIGHT_CLASS_DEVICE
271 depends on RFKILL 281 depends on RFKILL
272 depends on HWMON 282 depends on HWMON
273 depends on POWER_SUPPLY 283 depends on POWER_SUPPLY
274 ---help--- 284 ---help---
275 This is a driver for laptops built by Compal: 285 This is a driver for laptops built by Compal, and some models by
276 286 other brands (e.g. Dell, Toshiba).
277 Compal FL90/IFL90
278 Compal FL91/IFL91
279 Compal FL92/JFL92
280 Compal FT00/IFT00
281 287
282 It adds support for Bluetooth, WLAN and LCD brightness control. 288 It adds support for rfkill, Bluetooth, WLAN and LCD brightness
289 control.
283 290
284 If you have an Compal FL9x/IFL9x/FT00 laptop, say Y or M here. 291 For a (possibly incomplete) list of supported laptops, please refer
292 to: Documentation/platform/x86-laptop-drivers.txt
285 293
286config SONY_LAPTOP 294config SONY_LAPTOP
287 tristate "Sony Laptop Extras" 295 tristate "Sony Laptop Extras"
@@ -724,7 +732,7 @@ config IBM_RTL
724 732
725config XO1_RFKILL 733config XO1_RFKILL
726 tristate "OLPC XO-1 software RF kill switch" 734 tristate "OLPC XO-1 software RF kill switch"
727 depends on OLPC 735 depends on OLPC || COMPILE_TEST
728 depends on RFKILL 736 depends on RFKILL
729 ---help--- 737 ---help---
730 Support for enabling/disabling the WLAN interface on the OLPC XO-1 738 Support for enabling/disabling the WLAN interface on the OLPC XO-1
@@ -732,6 +740,7 @@ config XO1_RFKILL
732 740
733config XO15_EBOOK 741config XO15_EBOOK
734 tristate "OLPC XO-1.5 ebook switch" 742 tristate "OLPC XO-1.5 ebook switch"
743 depends on OLPC || COMPILE_TEST
735 depends on ACPI && INPUT 744 depends on ACPI && INPUT
736 ---help--- 745 ---help---
737 Support for the ebook switch on the OLPC XO-1.5 laptop. 746 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
13obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o 13obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
14obj-$(CONFIG_DELL_WMI) += dell-wmi.o 14obj-$(CONFIG_DELL_WMI) += dell-wmi.o
15obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o 15obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o
16obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o
16obj-$(CONFIG_ACER_WMI) += acer-wmi.o 17obj-$(CONFIG_ACER_WMI) += acer-wmi.o
17obj-$(CONFIG_ACERHDF) += acerhdf.o 18obj-$(CONFIG_ACERHDF) += acerhdf.o
18obj-$(CONFIG_HP_ACCEL) += hp_accel.o 19obj-$(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 @@
32#define WMAX_METHOD_HDMI_STATUS 0x2 32#define WMAX_METHOD_HDMI_STATUS 0x2
33#define WMAX_METHOD_BRIGHTNESS 0x3 33#define WMAX_METHOD_BRIGHTNESS 0x3
34#define WMAX_METHOD_ZONE_CONTROL 0x4 34#define WMAX_METHOD_ZONE_CONTROL 0x4
35#define WMAX_METHOD_HDMI_CABLE 0x5
35 36
36MODULE_AUTHOR("Mario Limonciello <mario_limonciello@dell.com>"); 37MODULE_AUTHOR("Mario Limonciello <mario_limonciello@dell.com>");
37MODULE_DESCRIPTION("Alienware special feature control"); 38MODULE_DESCRIPTION("Alienware special feature control");
@@ -350,12 +351,11 @@ static int alienware_zone_init(struct platform_device *dev)
350 char *name; 351 char *name;
351 352
352 if (interface == WMAX) { 353 if (interface == WMAX) {
353 global_led.max_brightness = 100;
354 lighting_control_state = WMAX_RUNNING; 354 lighting_control_state = WMAX_RUNNING;
355 } else if (interface == LEGACY) { 355 } else if (interface == LEGACY) {
356 global_led.max_brightness = 0x0F;
357 lighting_control_state = LEGACY_RUNNING; 356 lighting_control_state = LEGACY_RUNNING;
358 } 357 }
358 global_led.max_brightness = 0x0F;
359 global_brightness = global_led.max_brightness; 359 global_brightness = global_led.max_brightness;
360 360
361 /* 361 /*
@@ -423,41 +423,85 @@ static void alienware_zone_exit(struct platform_device *dev)
423 The HDMI mux sysfs node indicates the status of the HDMI input mux. 423 The HDMI mux sysfs node indicates the status of the HDMI input mux.
424 It can toggle between standard system GPU output and HDMI input. 424 It can toggle between standard system GPU output and HDMI input.
425*/ 425*/
426static ssize_t show_hdmi(struct device *dev, struct device_attribute *attr, 426static acpi_status alienware_hdmi_command(struct hdmi_args *in_args,
427 char *buf) 427 u32 command, int *out_data)
428{ 428{
429 acpi_status status; 429 acpi_status status;
430 struct acpi_buffer input;
431 union acpi_object *obj; 430 union acpi_object *obj;
432 u32 tmp = 0; 431 struct acpi_buffer input;
433 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 432 struct acpi_buffer output;
433
434 input.length = (acpi_size) sizeof(*in_args);
435 input.pointer = in_args;
436 if (out_data != NULL) {
437 output.length = ACPI_ALLOCATE_BUFFER;
438 output.pointer = NULL;
439 status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1,
440 command, &input, &output);
441 } else
442 status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1,
443 command, &input, NULL);
444
445 if (ACPI_SUCCESS(status) && out_data != NULL) {
446 obj = (union acpi_object *)output.pointer;
447 if (obj && obj->type == ACPI_TYPE_INTEGER)
448 *out_data = (u32) obj->integer.value;
449 }
450 return status;
451
452}
453
454static ssize_t show_hdmi_cable(struct device *dev,
455 struct device_attribute *attr, char *buf)
456{
457 acpi_status status;
458 u32 out_data;
434 struct hdmi_args in_args = { 459 struct hdmi_args in_args = {
435 .arg = 0, 460 .arg = 0,
436 }; 461 };
437 input.length = (acpi_size) sizeof(in_args); 462 status =
438 input.pointer = &in_args; 463 alienware_hdmi_command(&in_args, WMAX_METHOD_HDMI_CABLE,
439 status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, 464 (u32 *) &out_data);
440 WMAX_METHOD_HDMI_STATUS, &input, &output); 465 if (ACPI_SUCCESS(status)) {
466 if (out_data == 0)
467 return scnprintf(buf, PAGE_SIZE,
468 "[unconnected] connected unknown\n");
469 else if (out_data == 1)
470 return scnprintf(buf, PAGE_SIZE,
471 "unconnected [connected] unknown\n");
472 }
473 pr_err("alienware-wmi: unknown HDMI cable status: %d\n", status);
474 return scnprintf(buf, PAGE_SIZE, "unconnected connected [unknown]\n");
475}
476
477static ssize_t show_hdmi_source(struct device *dev,
478 struct device_attribute *attr, char *buf)
479{
480 acpi_status status;
481 u32 out_data;
482 struct hdmi_args in_args = {
483 .arg = 0,
484 };
485 status =
486 alienware_hdmi_command(&in_args, WMAX_METHOD_HDMI_STATUS,
487 (u32 *) &out_data);
441 488
442 if (ACPI_SUCCESS(status)) { 489 if (ACPI_SUCCESS(status)) {
443 obj = (union acpi_object *)output.pointer; 490 if (out_data == 1)
444 if (obj && obj->type == ACPI_TYPE_INTEGER)
445 tmp = (u32) obj->integer.value;
446 if (tmp == 1)
447 return scnprintf(buf, PAGE_SIZE, 491 return scnprintf(buf, PAGE_SIZE,
448 "[input] gpu unknown\n"); 492 "[input] gpu unknown\n");
449 else if (tmp == 2) 493 else if (out_data == 2)
450 return scnprintf(buf, PAGE_SIZE, 494 return scnprintf(buf, PAGE_SIZE,
451 "input [gpu] unknown\n"); 495 "input [gpu] unknown\n");
452 } 496 }
453 pr_err("alienware-wmi: unknown HDMI status: %d\n", status); 497 pr_err("alienware-wmi: unknown HDMI source status: %d\n", out_data);
454 return scnprintf(buf, PAGE_SIZE, "input gpu [unknown]\n"); 498 return scnprintf(buf, PAGE_SIZE, "input gpu [unknown]\n");
455} 499}
456 500
457static ssize_t toggle_hdmi(struct device *dev, struct device_attribute *attr, 501static ssize_t toggle_hdmi_source(struct device *dev,
458 const char *buf, size_t count) 502 struct device_attribute *attr,
503 const char *buf, size_t count)
459{ 504{
460 struct acpi_buffer input;
461 acpi_status status; 505 acpi_status status;
462 struct hdmi_args args; 506 struct hdmi_args args;
463 if (strcmp(buf, "gpu\n") == 0) 507 if (strcmp(buf, "gpu\n") == 0)
@@ -467,33 +511,46 @@ static ssize_t toggle_hdmi(struct device *dev, struct device_attribute *attr,
467 else 511 else
468 args.arg = 3; 512 args.arg = 3;
469 pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf); 513 pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
470 input.length = (acpi_size) sizeof(args); 514
471 input.pointer = &args; 515 status = alienware_hdmi_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
472 status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, 516
473 WMAX_METHOD_HDMI_SOURCE, &input, NULL);
474 if (ACPI_FAILURE(status)) 517 if (ACPI_FAILURE(status))
475 pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", 518 pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
476 status); 519 status);
477 return count; 520 return count;
478} 521}
479 522
480static DEVICE_ATTR(hdmi, S_IRUGO | S_IWUSR, show_hdmi, toggle_hdmi); 523static DEVICE_ATTR(cable, S_IRUGO, show_hdmi_cable, NULL);
524static DEVICE_ATTR(source, S_IRUGO | S_IWUSR, show_hdmi_source,
525 toggle_hdmi_source);
526
527static struct attribute *hdmi_attrs[] = {
528 &dev_attr_cable.attr,
529 &dev_attr_source.attr,
530 NULL,
531};
481 532
482static void remove_hdmi(struct platform_device *device) 533static struct attribute_group hdmi_attribute_group = {
534 .name = "hdmi",
535 .attrs = hdmi_attrs,
536};
537
538static void remove_hdmi(struct platform_device *dev)
483{ 539{
484 device_remove_file(&device->dev, &dev_attr_hdmi); 540 sysfs_remove_group(&dev->dev.kobj, &hdmi_attribute_group);
485} 541}
486 542
487static int create_hdmi(void) 543static int create_hdmi(struct platform_device *dev)
488{ 544{
489 int ret = -ENOMEM; 545 int ret;
490 ret = device_create_file(&platform_device->dev, &dev_attr_hdmi); 546
547 ret = sysfs_create_group(&dev->dev.kobj, &hdmi_attribute_group);
491 if (ret) 548 if (ret)
492 goto error_create_hdmi; 549 goto error_create_hdmi;
493 return 0; 550 return 0;
494 551
495error_create_hdmi: 552error_create_hdmi:
496 remove_hdmi(platform_device); 553 remove_hdmi(dev);
497 return ret; 554 return ret;
498} 555}
499 556
@@ -527,7 +584,7 @@ static int __init alienware_wmi_init(void)
527 goto fail_platform_device2; 584 goto fail_platform_device2;
528 585
529 if (interface == WMAX) { 586 if (interface == WMAX) {
530 ret = create_hdmi(); 587 ret = create_hdmi(platform_device);
531 if (ret) 588 if (ret)
532 goto fail_prep_hdmi; 589 goto fail_prep_hdmi;
533 } 590 }
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[] = {
137 }, 137 },
138 { 138 {
139 .callback = dmi_matched, 139 .callback = dmi_matched,
140 .ident = "ASUSTeK COMPUTER INC. X550CA",
141 .matches = {
142 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
143 DMI_MATCH(DMI_PRODUCT_NAME, "X550CA"),
144 },
145 .driver_data = &quirk_asus_x401u,
146 },
147 {
148 .callback = dmi_matched,
140 .ident = "ASUSTeK COMPUTER INC. X55A", 149 .ident = "ASUSTeK COMPUTER INC. X55A",
141 .matches = { 150 .matches = {
142 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 151 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,
266 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 266 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
267 acpi_status status; 267 acpi_status status;
268 union acpi_object *obj; 268 union acpi_object *obj;
269 u32 tmp; 269 u32 tmp = 0;
270 270
271 status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 1, method_id, 271 status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 1, method_id,
272 &input, &output); 272 &input, &output);
@@ -277,8 +277,6 @@ static int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
277 obj = (union acpi_object *)output.pointer; 277 obj = (union acpi_object *)output.pointer;
278 if (obj && obj->type == ACPI_TYPE_INTEGER) 278 if (obj && obj->type == ACPI_TYPE_INTEGER)
279 tmp = (u32) obj->integer.value; 279 tmp = (u32) obj->integer.value;
280 else
281 tmp = 0;
282 280
283 if (retval) 281 if (retval)
284 *retval = tmp; 282 *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 @@
1/*
2 * dell-smo8800.c - Dell Latitude ACPI SMO8800/SMO8810 freefall sensor driver
3 *
4 * Copyright (C) 2012 Sonal Santan <sonal.santan@gmail.com>
5 * Copyright (C) 2014 Pali Rohár <pali.rohar@gmail.com>
6 *
7 * This is loosely based on lis3lv02d driver.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 */
19
20#define DRIVER_NAME "smo8800"
21
22#include <linux/kernel.h>
23#include <linux/module.h>
24#include <linux/acpi.h>
25#include <linux/interrupt.h>
26#include <linux/miscdevice.h>
27
28struct smo8800_device {
29 u32 irq; /* acpi device irq */
30 atomic_t counter; /* count after last read */
31 struct miscdevice miscdev; /* for /dev/freefall */
32 unsigned long misc_opened; /* whether the device is open */
33 wait_queue_head_t misc_wait; /* Wait queue for the misc dev */
34 struct device *dev; /* acpi device */
35};
36
37static irqreturn_t smo8800_interrupt_quick(int irq, void *data)
38{
39 struct smo8800_device *smo8800 = data;
40
41 atomic_inc(&smo8800->counter);
42 wake_up_interruptible(&smo8800->misc_wait);
43 return IRQ_WAKE_THREAD;
44}
45
46static irqreturn_t smo8800_interrupt_thread(int irq, void *data)
47{
48 struct smo8800_device *smo8800 = data;
49
50 dev_info(smo8800->dev, "detected free fall\n");
51 return IRQ_HANDLED;
52}
53
54static acpi_status smo8800_get_resource(struct acpi_resource *resource,
55 void *context)
56{
57 struct acpi_resource_extended_irq *irq;
58
59 if (resource->type != ACPI_RESOURCE_TYPE_EXTENDED_IRQ)
60 return AE_OK;
61
62 irq = &resource->data.extended_irq;
63 if (!irq || !irq->interrupt_count)
64 return AE_OK;
65
66 *((u32 *)context) = irq->interrupts[0];
67 return AE_CTRL_TERMINATE;
68}
69
70static u32 smo8800_get_irq(struct acpi_device *device)
71{
72 u32 irq = 0;
73 acpi_status status;
74
75 status = acpi_walk_resources(device->handle, METHOD_NAME__CRS,
76 smo8800_get_resource, &irq);
77 if (ACPI_FAILURE(status)) {
78 dev_err(&device->dev, "acpi_walk_resources failed\n");
79 return 0;
80 }
81
82 return irq;
83}
84
85static ssize_t smo8800_misc_read(struct file *file, char __user *buf,
86 size_t count, loff_t *pos)
87{
88 struct smo8800_device *smo8800 = container_of(file->private_data,
89 struct smo8800_device, miscdev);
90
91 u32 data = 0;
92 unsigned char byte_data = 0;
93 ssize_t retval = 1;
94
95 if (count < 1)
96 return -EINVAL;
97
98 atomic_set(&smo8800->counter, 0);
99 retval = wait_event_interruptible(smo8800->misc_wait,
100 (data = atomic_xchg(&smo8800->counter, 0)));
101
102 if (retval)
103 return retval;
104
105 byte_data = 1;
106 retval = 1;
107
108 if (data < 255)
109 byte_data = data;
110 else
111 byte_data = 255;
112
113 if (put_user(byte_data, buf))
114 retval = -EFAULT;
115
116 return retval;
117}
118
119static int smo8800_misc_open(struct inode *inode, struct file *file)
120{
121 struct smo8800_device *smo8800 = container_of(file->private_data,
122 struct smo8800_device, miscdev);
123
124 if (test_and_set_bit(0, &smo8800->misc_opened))
125 return -EBUSY; /* already open */
126
127 atomic_set(&smo8800->counter, 0);
128 return 0;
129}
130
131static int smo8800_misc_release(struct inode *inode, struct file *file)
132{
133 struct smo8800_device *smo8800 = container_of(file->private_data,
134 struct smo8800_device, miscdev);
135
136 clear_bit(0, &smo8800->misc_opened); /* release the device */
137 return 0;
138}
139
140static const struct file_operations smo8800_misc_fops = {
141 .owner = THIS_MODULE,
142 .read = smo8800_misc_read,
143 .open = smo8800_misc_open,
144 .release = smo8800_misc_release,
145};
146
147static int smo8800_add(struct acpi_device *device)
148{
149 int err;
150 struct smo8800_device *smo8800;
151
152 smo8800 = devm_kzalloc(&device->dev, sizeof(*smo8800), GFP_KERNEL);
153 if (!smo8800) {
154 dev_err(&device->dev, "failed to allocate device data\n");
155 return -ENOMEM;
156 }
157
158 smo8800->dev = &device->dev;
159 smo8800->miscdev.minor = MISC_DYNAMIC_MINOR;
160 smo8800->miscdev.name = "freefall";
161 smo8800->miscdev.fops = &smo8800_misc_fops;
162
163 init_waitqueue_head(&smo8800->misc_wait);
164
165 err = misc_register(&smo8800->miscdev);
166 if (err) {
167 dev_err(&device->dev, "failed to register misc dev: %d\n", err);
168 return err;
169 }
170
171 device->driver_data = smo8800;
172
173 smo8800->irq = smo8800_get_irq(device);
174 if (!smo8800->irq) {
175 dev_err(&device->dev, "failed to obtain IRQ\n");
176 err = -EINVAL;
177 goto error;
178 }
179
180 err = request_threaded_irq(smo8800->irq, smo8800_interrupt_quick,
181 smo8800_interrupt_thread,
182 IRQF_TRIGGER_RISING | IRQF_ONESHOT,
183 DRIVER_NAME, smo8800);
184 if (err) {
185 dev_err(&device->dev,
186 "failed to request thread for IRQ %d: %d\n",
187 smo8800->irq, err);
188 goto error;
189 }
190
191 dev_dbg(&device->dev, "device /dev/freefall registered with IRQ %d\n",
192 smo8800->irq);
193 return 0;
194
195error:
196 misc_deregister(&smo8800->miscdev);
197 return err;
198}
199
200static int smo8800_remove(struct acpi_device *device)
201{
202 struct smo8800_device *smo8800 = device->driver_data;
203
204 free_irq(smo8800->irq, smo8800);
205 misc_deregister(&smo8800->miscdev);
206 dev_dbg(&device->dev, "device /dev/freefall unregistered\n");
207 return 0;
208}
209
210static const struct acpi_device_id smo8800_ids[] = {
211 { "SMO8800", 0 },
212 { "SMO8810", 0 },
213 { "", 0 },
214};
215
216MODULE_DEVICE_TABLE(acpi, smo8800_ids);
217
218static struct acpi_driver smo8800_driver = {
219 .name = DRIVER_NAME,
220 .class = "Latitude",
221 .ids = smo8800_ids,
222 .ops = {
223 .add = smo8800_add,
224 .remove = smo8800_remove,
225 },
226 .owner = THIS_MODULE,
227};
228
229module_acpi_driver(smo8800_driver);
230
231MODULE_DESCRIPTION("Dell Latitude freefall driver (ACPI SMO8800/SMO8810)");
232MODULE_LICENSE("GPL");
233MODULE_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");
53#define HPWMI_ALS_QUERY 0x3 53#define HPWMI_ALS_QUERY 0x3
54#define HPWMI_HARDWARE_QUERY 0x4 54#define HPWMI_HARDWARE_QUERY 0x4
55#define HPWMI_WIRELESS_QUERY 0x5 55#define HPWMI_WIRELESS_QUERY 0x5
56#define HPWMI_BIOS_QUERY 0x9
56#define HPWMI_HOTKEY_QUERY 0xc 57#define HPWMI_HOTKEY_QUERY 0xc
57#define HPWMI_FEATURE_QUERY 0xd 58#define HPWMI_FEATURE_QUERY 0xd
58#define HPWMI_WIRELESS2_QUERY 0x1b 59#define HPWMI_WIRELESS2_QUERY 0x1b
@@ -144,6 +145,7 @@ static const struct key_entry hp_wmi_keymap[] = {
144 { KE_KEY, 0x2142, { KEY_MEDIA } }, 145 { KE_KEY, 0x2142, { KEY_MEDIA } },
145 { KE_KEY, 0x213b, { KEY_INFO } }, 146 { KE_KEY, 0x213b, { KEY_INFO } },
146 { KE_KEY, 0x2169, { KEY_DIRECTION } }, 147 { KE_KEY, 0x2169, { KEY_DIRECTION } },
148 { KE_KEY, 0x216a, { KEY_SETUP } },
147 { KE_KEY, 0x231b, { KEY_HELP } }, 149 { KE_KEY, 0x231b, { KEY_HELP } },
148 { KE_END, 0 } 150 { KE_END, 0 }
149}; 151};
@@ -304,6 +306,19 @@ static int hp_wmi_bios_2009_later(void)
304 return (state & 0x10) ? 1 : 0; 306 return (state & 0x10) ? 1 : 0;
305} 307}
306 308
309static int hp_wmi_enable_hotkeys(void)
310{
311 int ret;
312 int query = 0x6e;
313
314 ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, 1, &query, sizeof(query),
315 0);
316
317 if (ret)
318 return -EINVAL;
319 return 0;
320}
321
307static int hp_wmi_set_block(void *data, bool blocked) 322static int hp_wmi_set_block(void *data, bool blocked)
308{ 323{
309 enum hp_wmi_radio r = (enum hp_wmi_radio) data; 324 enum hp_wmi_radio r = (enum hp_wmi_radio) data;
@@ -648,6 +663,9 @@ static int __init hp_wmi_input_setup(void)
648 hp_wmi_tablet_state()); 663 hp_wmi_tablet_state());
649 input_sync(hp_wmi_input_dev); 664 input_sync(hp_wmi_input_dev);
650 665
666 if (hp_wmi_bios_2009_later() == 4)
667 hp_wmi_enable_hotkeys();
668
651 status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL); 669 status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL);
652 if (ACPI_FAILURE(status)) { 670 if (ACPI_FAILURE(status)) {
653 err = -EIO; 671 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 @@
36#include <linux/debugfs.h> 36#include <linux/debugfs.h>
37#include <linux/seq_file.h> 37#include <linux/seq_file.h>
38#include <linux/i8042.h> 38#include <linux/i8042.h>
39#include <linux/dmi.h>
40#include <linux/device.h>
39 41
40#define IDEAPAD_RFKILL_DEV_NUM (3) 42#define IDEAPAD_RFKILL_DEV_NUM (3)
41 43
@@ -819,6 +821,19 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)
819 } 821 }
820} 822}
821 823
824/* Blacklist for devices where the ideapad rfkill interface does not work */
825static struct dmi_system_id rfkill_blacklist[] = {
826 /* The Lenovo Yoga 2 11 always reports everything as blocked */
827 {
828 .ident = "Lenovo Yoga 2 11",
829 .matches = {
830 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
831 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 2 11"),
832 },
833 },
834 {}
835};
836
822static int ideapad_acpi_add(struct platform_device *pdev) 837static int ideapad_acpi_add(struct platform_device *pdev)
823{ 838{
824 int ret, i; 839 int ret, i;
@@ -833,7 +848,7 @@ static int ideapad_acpi_add(struct platform_device *pdev)
833 if (read_method_int(adev->handle, "_CFG", &cfg)) 848 if (read_method_int(adev->handle, "_CFG", &cfg))
834 return -ENODEV; 849 return -ENODEV;
835 850
836 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 851 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
837 if (!priv) 852 if (!priv)
838 return -ENOMEM; 853 return -ENOMEM;
839 854
@@ -844,7 +859,7 @@ static int ideapad_acpi_add(struct platform_device *pdev)
844 859
845 ret = ideapad_sysfs_init(priv); 860 ret = ideapad_sysfs_init(priv);
846 if (ret) 861 if (ret)
847 goto sysfs_failed; 862 return ret;
848 863
849 ret = ideapad_debugfs_init(priv); 864 ret = ideapad_debugfs_init(priv);
850 if (ret) 865 if (ret)
@@ -854,11 +869,10 @@ static int ideapad_acpi_add(struct platform_device *pdev)
854 if (ret) 869 if (ret)
855 goto input_failed; 870 goto input_failed;
856 871
857 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) { 872 if (!dmi_check_system(rfkill_blacklist)) {
858 if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg)) 873 for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
859 ideapad_register_rfkill(priv, i); 874 if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg))
860 else 875 ideapad_register_rfkill(priv, i);
861 priv->rfk[i] = NULL;
862 } 876 }
863 ideapad_sync_rfk_state(priv); 877 ideapad_sync_rfk_state(priv);
864 ideapad_sync_touchpad_state(priv); 878 ideapad_sync_touchpad_state(priv);
@@ -884,8 +898,6 @@ input_failed:
884 ideapad_debugfs_exit(priv); 898 ideapad_debugfs_exit(priv);
885debugfs_failed: 899debugfs_failed:
886 ideapad_sysfs_exit(priv); 900 ideapad_sysfs_exit(priv);
887sysfs_failed:
888 kfree(priv);
889 return ret; 901 return ret;
890} 902}
891 903
@@ -903,7 +915,6 @@ static int ideapad_acpi_remove(struct platform_device *pdev)
903 ideapad_debugfs_exit(priv); 915 ideapad_debugfs_exit(priv);
904 ideapad_sysfs_exit(priv); 916 ideapad_sysfs_exit(priv);
905 dev_set_drvdata(&pdev->dev, NULL); 917 dev_set_drvdata(&pdev->dev, NULL);
906 kfree(priv);
907 918
908 return 0; 919 return 0;
909} 920}
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)
481 int i; 481 int i;
482 struct platform_info *pinfo; 482 struct platform_info *pinfo;
483 483
484 pinfo = kzalloc(sizeof(struct platform_info), GFP_KERNEL); 484 pinfo = devm_kzalloc(&pdev->dev, sizeof(struct platform_info),
485 GFP_KERNEL);
485 if (!pinfo) 486 if (!pinfo)
486 return -ENOMEM; 487 return -ENOMEM;
487 488
@@ -489,7 +490,6 @@ static int mid_thermal_probe(struct platform_device *pdev)
489 ret = mid_initialize_adc(&pdev->dev); 490 ret = mid_initialize_adc(&pdev->dev);
490 if (ret) { 491 if (ret) {
491 dev_err(&pdev->dev, "ADC init failed"); 492 dev_err(&pdev->dev, "ADC init failed");
492 kfree(pinfo);
493 return ret; 493 return ret;
494 } 494 }
495 495
@@ -520,7 +520,6 @@ err:
520 thermal_zone_device_unregister(pinfo->tzd[i]); 520 thermal_zone_device_unregister(pinfo->tzd[i]);
521 } 521 }
522 configure_adc(0); 522 configure_adc(0);
523 kfree(pinfo);
524 return ret; 523 return ret;
525} 524}
526 525
@@ -541,8 +540,6 @@ static int mid_thermal_remove(struct platform_device *pdev)
541 thermal_zone_device_unregister(pinfo->tzd[i]); 540 thermal_zone_device_unregister(pinfo->tzd[i]);
542 } 541 }
543 542
544 kfree(pinfo);
545
546 /* Stop the ADC */ 543 /* Stop the ADC */
547 return configure_adc(0); 544 return configure_adc(0);
548} 545}
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)
91 91
92static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned offset) 92static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
93{ 93{
94 if (offset > 8) { 94 if (offset >= 8) {
95 pr_err("only pin 0-7 support input\n"); 95 pr_err("only pin 0-7 support input\n");
96 return -1;/* we only have 8 GPIO can use as input */ 96 return -1;/* we only have 8 GPIO can use as input */
97 } 97 }
@@ -130,7 +130,7 @@ static int pmic_gpio_get(struct gpio_chip *chip, unsigned offset)
130 int ret; 130 int ret;
131 131
132 /* we only have 8 GPIO pins we can use as input */ 132 /* we only have 8 GPIO pins we can use as input */
133 if (offset > 8) 133 if (offset >= 8)
134 return -EOPNOTSUPP; 134 return -EOPNOTSUPP;
135 ret = intel_scu_ipc_ioread8(GPIO0 + offset, &r); 135 ret = intel_scu_ipc_ioread8(GPIO0 + offset, &r);
136 if (ret < 0) 136 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,
70 70
71static struct notifier_block pvpanic_panic_nb = { 71static struct notifier_block pvpanic_panic_nb = {
72 .notifier_call = pvpanic_panic_notify, 72 .notifier_call = pvpanic_panic_notify,
73 .priority = 1, /* let this called before broken drm_fb_helper */
73}; 74};
74 75
75 76
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 @@
27#include <linux/debugfs.h> 27#include <linux/debugfs.h>
28#include <linux/ctype.h> 28#include <linux/ctype.h>
29#include <linux/efi.h> 29#include <linux/efi.h>
30#include <linux/suspend.h>
30#include <acpi/video.h> 31#include <acpi/video.h>
31 32
32/* 33/*
@@ -340,6 +341,8 @@ struct samsung_laptop {
340 struct samsung_laptop_debug debug; 341 struct samsung_laptop_debug debug;
341 struct samsung_quirks *quirks; 342 struct samsung_quirks *quirks;
342 343
344 struct notifier_block pm_nb;
345
343 bool handle_backlight; 346 bool handle_backlight;
344 bool has_stepping_quirk; 347 bool has_stepping_quirk;
345 348
@@ -348,6 +351,8 @@ struct samsung_laptop {
348 351
349struct samsung_quirks { 352struct samsung_quirks {
350 bool broken_acpi_video; 353 bool broken_acpi_video;
354 bool four_kbd_backlight_levels;
355 bool enable_kbd_backlight;
351}; 356};
352 357
353static struct samsung_quirks samsung_unknown = {}; 358static struct samsung_quirks samsung_unknown = {};
@@ -356,6 +361,11 @@ static struct samsung_quirks samsung_broken_acpi_video = {
356 .broken_acpi_video = true, 361 .broken_acpi_video = true,
357}; 362};
358 363
364static struct samsung_quirks samsung_np740u3e = {
365 .four_kbd_backlight_levels = true,
366 .enable_kbd_backlight = true,
367};
368
359static bool force; 369static bool force;
360module_param(force, bool, 0); 370module_param(force, bool, 0);
361MODULE_PARM_DESC(force, 371MODULE_PARM_DESC(force,
@@ -1051,6 +1061,8 @@ static int __init samsung_leds_init(struct samsung_laptop *samsung)
1051 samsung->kbd_led.brightness_set = kbd_led_set; 1061 samsung->kbd_led.brightness_set = kbd_led_set;
1052 samsung->kbd_led.brightness_get = kbd_led_get; 1062 samsung->kbd_led.brightness_get = kbd_led_get;
1053 samsung->kbd_led.max_brightness = 8; 1063 samsung->kbd_led.max_brightness = 8;
1064 if (samsung->quirks->four_kbd_backlight_levels)
1065 samsung->kbd_led.max_brightness = 4;
1054 1066
1055 ret = led_classdev_register(&samsung->platform_device->dev, 1067 ret = led_classdev_register(&samsung->platform_device->dev,
1056 &samsung->kbd_led); 1068 &samsung->kbd_led);
@@ -1414,6 +1426,19 @@ static void samsung_platform_exit(struct samsung_laptop *samsung)
1414 } 1426 }
1415} 1427}
1416 1428
1429static int samsung_pm_notification(struct notifier_block *nb,
1430 unsigned long val, void *ptr)
1431{
1432 struct samsung_laptop *samsung;
1433
1434 samsung = container_of(nb, struct samsung_laptop, pm_nb);
1435 if (val == PM_POST_HIBERNATION &&
1436 samsung->quirks->enable_kbd_backlight)
1437 kbd_backlight_enable(samsung);
1438
1439 return 0;
1440}
1441
1417static int __init samsung_platform_init(struct samsung_laptop *samsung) 1442static int __init samsung_platform_init(struct samsung_laptop *samsung)
1418{ 1443{
1419 struct platform_device *pdev; 1444 struct platform_device *pdev;
@@ -1534,6 +1559,15 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = {
1534 }, 1559 },
1535 .driver_data = &samsung_broken_acpi_video, 1560 .driver_data = &samsung_broken_acpi_video,
1536 }, 1561 },
1562 {
1563 .callback = samsung_dmi_matched,
1564 .ident = "730U3E/740U3E",
1565 .matches = {
1566 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1567 DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"),
1568 },
1569 .driver_data = &samsung_np740u3e,
1570 },
1537 { }, 1571 { },
1538}; 1572};
1539MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); 1573MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
@@ -1608,6 +1642,9 @@ static int __init samsung_init(void)
1608 if (ret) 1642 if (ret)
1609 goto error_debugfs; 1643 goto error_debugfs;
1610 1644
1645 samsung->pm_nb.notifier_call = samsung_pm_notification;
1646 register_pm_notifier(&samsung->pm_nb);
1647
1611 samsung_platform_device = samsung->platform_device; 1648 samsung_platform_device = samsung->platform_device;
1612 return ret; 1649 return ret;
1613 1650
@@ -1633,6 +1670,7 @@ static void __exit samsung_exit(void)
1633 struct samsung_laptop *samsung; 1670 struct samsung_laptop *samsung;
1634 1671
1635 samsung = platform_get_drvdata(samsung_platform_device); 1672 samsung = platform_get_drvdata(samsung_platform_device);
1673 unregister_pm_notifier(&samsung->pm_nb);
1636 1674
1637 samsung_debugfs_exit(samsung); 1675 samsung_debugfs_exit(samsung);
1638 samsung_leds_exit(samsung); 1676 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)
3171 KEY_MICMUTE, /* 0x1a: Mic mute (since ?400 or so) */ 3171 KEY_MICMUTE, /* 0x1a: Mic mute (since ?400 or so) */
3172 3172
3173 /* (assignments unknown, please report if found) */ 3173 /* (assignments unknown, please report if found) */
3174 KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
3175 KEY_UNKNOWN, 3174 KEY_UNKNOWN,
3175
3176 /* Extra keys in use since the X240 / T440 / T540 */
3177 KEY_CONFIG, KEY_SEARCH, KEY_SCALE, KEY_COMPUTER,
3176 }, 3178 },
3177 }; 3179 };
3178 3180
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 @@
56#include <linux/workqueue.h> 56#include <linux/workqueue.h>
57#include <linux/i8042.h> 57#include <linux/i8042.h>
58#include <linux/acpi.h> 58#include <linux/acpi.h>
59#include <linux/dmi.h>
59#include <asm/uaccess.h> 60#include <asm/uaccess.h>
60 61
61MODULE_AUTHOR("John Belmonte"); 62MODULE_AUTHOR("John Belmonte");
@@ -213,6 +214,30 @@ static const struct key_entry toshiba_acpi_keymap[] = {
213 { KE_END, 0 }, 214 { KE_END, 0 },
214}; 215};
215 216
217/* alternative keymap */
218static const struct dmi_system_id toshiba_alt_keymap_dmi[] = {
219 {
220 .matches = {
221 DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
222 DMI_MATCH(DMI_PRODUCT_NAME, "Satellite M840"),
223 },
224 },
225 {}
226};
227
228static const struct key_entry toshiba_acpi_alt_keymap[] = {
229 { KE_KEY, 0x157, { KEY_MUTE } },
230 { KE_KEY, 0x102, { KEY_ZOOMOUT } },
231 { KE_KEY, 0x103, { KEY_ZOOMIN } },
232 { KE_KEY, 0x139, { KEY_ZOOMRESET } },
233 { KE_KEY, 0x13e, { KEY_SWITCHVIDEOMODE } },
234 { KE_KEY, 0x13c, { KEY_BRIGHTNESSDOWN } },
235 { KE_KEY, 0x13d, { KEY_BRIGHTNESSUP } },
236 { KE_KEY, 0x158, { KEY_WLAN } },
237 { KE_KEY, 0x13f, { KEY_TOUCHPAD_TOGGLE } },
238 { KE_END, 0 },
239};
240
216/* utility 241/* utility
217 */ 242 */
218 243
@@ -1440,6 +1465,7 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
1440 acpi_handle ec_handle; 1465 acpi_handle ec_handle;
1441 int error; 1466 int error;
1442 u32 hci_result; 1467 u32 hci_result;
1468 const struct key_entry *keymap = toshiba_acpi_keymap;
1443 1469
1444 dev->hotkey_dev = input_allocate_device(); 1470 dev->hotkey_dev = input_allocate_device();
1445 if (!dev->hotkey_dev) 1471 if (!dev->hotkey_dev)
@@ -1449,7 +1475,9 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
1449 dev->hotkey_dev->phys = "toshiba_acpi/input0"; 1475 dev->hotkey_dev->phys = "toshiba_acpi/input0";
1450 dev->hotkey_dev->id.bustype = BUS_HOST; 1476 dev->hotkey_dev->id.bustype = BUS_HOST;
1451 1477
1452 error = sparse_keymap_setup(dev->hotkey_dev, toshiba_acpi_keymap, NULL); 1478 if (dmi_check_system(toshiba_alt_keymap_dmi))
1479 keymap = toshiba_acpi_alt_keymap;
1480 error = sparse_keymap_setup(dev->hotkey_dev, keymap, NULL);
1453 if (error) 1481 if (error)
1454 goto err_free_dev; 1482 goto err_free_dev;
1455 1483