aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-platform-msi-laptop83
-rw-r--r--drivers/platform/x86/Kconfig15
-rw-r--r--drivers/platform/x86/Makefile1
-rw-r--r--drivers/platform/x86/acer-wmi.c21
-rw-r--r--drivers/platform/x86/asus-laptop.c85
-rw-r--r--drivers/platform/x86/asus-nb-wmi.c76
-rw-r--r--drivers/platform/x86/asus-wmi.c108
-rw-r--r--drivers/platform/x86/asus-wmi.h9
-rw-r--r--drivers/platform/x86/chromeos_laptop.c371
-rw-r--r--drivers/platform/x86/eeepc-wmi.c2
-rw-r--r--drivers/platform/x86/hp-wmi.c117
-rw-r--r--drivers/platform/x86/msi-laptop.c374
-rw-r--r--drivers/platform/x86/msi-wmi.c224
-rw-r--r--drivers/platform/x86/sony-laptop.c145
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c17
15 files changed, 1375 insertions, 273 deletions
diff --git a/Documentation/ABI/testing/sysfs-platform-msi-laptop b/Documentation/ABI/testing/sysfs-platform-msi-laptop
new file mode 100644
index 000000000000..307a247ba1ef
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-platform-msi-laptop
@@ -0,0 +1,83 @@
1What: /sys/devices/platform/msi-laptop-pf/lcd_level
2Date: Oct 2006
3KernelVersion: 2.6.19
4Contact: "Lennart Poettering <mzxreary@0pointer.de>"
5Description:
6 Screen brightness: contains a single integer in the range 0..8.
7
8What: /sys/devices/platform/msi-laptop-pf/auto_brightness
9Date: Oct 2006
10KernelVersion: 2.6.19
11Contact: "Lennart Poettering <mzxreary@0pointer.de>"
12Description:
13 Enable automatic brightness control: contains either 0 or 1. If
14 set to 1 the hardware adjusts the screen brightness
15 automatically when the power cord is plugged/unplugged.
16
17What: /sys/devices/platform/msi-laptop-pf/wlan
18Date: Oct 2006
19KernelVersion: 2.6.19
20Contact: "Lennart Poettering <mzxreary@0pointer.de>"
21Description:
22 WLAN subsystem enabled: contains either 0 or 1.
23
24What: /sys/devices/platform/msi-laptop-pf/bluetooth
25Date: Oct 2006
26KernelVersion: 2.6.19
27Contact: "Lennart Poettering <mzxreary@0pointer.de>"
28Description:
29 Bluetooth subsystem enabled: contains either 0 or 1. Please
30 note that this file is constantly 0 if no Bluetooth hardware is
31 available.
32
33What: /sys/devices/platform/msi-laptop-pf/touchpad
34Date: Nov 2012
35KernelVersion: 3.8
36Contact: "Maxim Mikityanskiy <maxtram95@gmail.com>"
37Description:
38 Contains either 0 or 1 and indicates if touchpad is turned on.
39 Touchpad state can only be toggled by pressing Fn+F3.
40
41What: /sys/devices/platform/msi-laptop-pf/turbo_mode
42Date: Nov 2012
43KernelVersion: 3.8
44Contact: "Maxim Mikityanskiy <maxtram95@gmail.com>"
45Description:
46 Contains either 0 or 1 and indicates if turbo mode is turned
47 on. In turbo mode power LED is orange and processor is
48 overclocked. Turbo mode is available only if charging. It is
49 only possible to toggle turbo mode state by pressing Fn+F10,
50 and there is a few seconds cooldown between subsequent toggles.
51 If user presses Fn+F10 too frequent, turbo mode state is not
52 changed.
53
54What: /sys/devices/platform/msi-laptop-pf/eco_mode
55Date: Nov 2012
56KernelVersion: 3.8
57Contact: "Maxim Mikityanskiy <maxtram95@gmail.com>"
58Description:
59 Contains either 0 or 1 and indicates if ECO mode is turned on.
60 In ECO mode power LED is green and userspace should do some
61 powersaving actions. ECO mode is available only on battery
62 power. ECO mode can only be toggled by pressing Fn+F10.
63
64What: /sys/devices/platform/msi-laptop-pf/turbo_cooldown
65Date: Nov 2012
66KernelVersion: 3.8
67Contact: "Maxim Mikityanskiy <maxtram95@gmail.com>"
68Description:
69 Contains value in range 0..3:
70 * 0 -> Turbo mode is off
71 * 1 -> Turbo mode is on, cannot be turned off yet
72 * 2 -> Turbo mode is off, cannot be turned on yet
73 * 3 -> Turbo mode is on
74
75What: /sys/devices/platform/msi-laptop-pf/auto_fan
76Date: Nov 2012
77KernelVersion: 3.8
78Contact: "Maxim Mikityanskiy <maxtram95@gmail.com>"
79Description:
80 Contains either 0 or 1 and indicates if fan speed is controlled
81 automatically (1) or fan runs at maximal speed (0). Can be
82 toggled in software.
83
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 7ab0b2fba503..3338437b559b 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -79,6 +79,17 @@ config ASUS_LAPTOP
79 79
80 If you have an ACPI-compatible ASUS laptop, say Y or M here. 80 If you have an ACPI-compatible ASUS laptop, say Y or M here.
81 81
82config CHROMEOS_LAPTOP
83 tristate "Chrome OS Laptop"
84 depends on I2C
85 depends on DMI
86 ---help---
87 This driver instantiates i2c and smbus devices such as
88 light sensors and touchpads.
89
90 If you have a supported Chromebook, choose Y or M here.
91 The module will be called chromeos_laptop.
92
82config DELL_LAPTOP 93config DELL_LAPTOP
83 tristate "Dell Laptop Extras" 94 tristate "Dell Laptop Extras"
84 depends on X86 95 depends on X86
@@ -288,9 +299,11 @@ config IDEAPAD_LAPTOP
288 depends on ACPI 299 depends on ACPI
289 depends on RFKILL && INPUT 300 depends on RFKILL && INPUT
290 depends on SERIO_I8042 301 depends on SERIO_I8042
302 depends on BACKLIGHT_CLASS_DEVICE
291 select INPUT_SPARSEKMAP 303 select INPUT_SPARSEKMAP
292 help 304 help
293 This is a driver for the rfkill switches on Lenovo IdeaPad netbooks. 305 This is a driver for Lenovo IdeaPad netbooks contains drivers for
306 rfkill switch, hotkey, fan control and backlight control.
294 307
295config THINKPAD_ACPI 308config THINKPAD_ACPI
296 tristate "ThinkPad ACPI Laptop Extras" 309 tristate "ThinkPad ACPI Laptop Extras"
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index bf7e4f935b17..ace2b38942fe 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -50,3 +50,4 @@ obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o
50obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o 50obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o
51obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o 51obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o
52obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o 52obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o
53obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index afed7018a2b5..c9076bdaf2c1 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -511,6 +511,24 @@ static struct dmi_system_id acer_quirks[] = {
511 }, 511 },
512 .driver_data = &quirk_fujitsu_amilo_li_1718, 512 .driver_data = &quirk_fujitsu_amilo_li_1718,
513 }, 513 },
514 {
515 .callback = dmi_matched,
516 .ident = "Lenovo Ideapad S205-10382JG",
517 .matches = {
518 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
519 DMI_MATCH(DMI_PRODUCT_NAME, "10382JG"),
520 },
521 .driver_data = &quirk_lenovo_ideapad_s205,
522 },
523 {
524 .callback = dmi_matched,
525 .ident = "Lenovo Ideapad S205-1038DPG",
526 .matches = {
527 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
528 DMI_MATCH(DMI_PRODUCT_NAME, "1038DPG"),
529 },
530 .driver_data = &quirk_lenovo_ideapad_s205,
531 },
514 {} 532 {}
515}; 533};
516 534
@@ -1204,6 +1222,9 @@ static acpi_status WMID_set_capabilities(void)
1204 devices = *((u32 *) obj->buffer.pointer); 1222 devices = *((u32 *) obj->buffer.pointer);
1205 } else if (obj->type == ACPI_TYPE_INTEGER) { 1223 } else if (obj->type == ACPI_TYPE_INTEGER) {
1206 devices = (u32) obj->integer.value; 1224 devices = (u32) obj->integer.value;
1225 } else {
1226 kfree(out.pointer);
1227 return AE_ERROR;
1207 } 1228 }
1208 } else { 1229 } else {
1209 kfree(out.pointer); 1230 kfree(out.pointer);
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c
index d9f9a0dbc6f3..0eea09c1c134 100644
--- a/drivers/platform/x86/asus-laptop.c
+++ b/drivers/platform/x86/asus-laptop.c
@@ -128,10 +128,12 @@ MODULE_PARM_DESC(als_status, "Set the ALS status on boot "
128/* 128/*
129 * Some events we use, same for all Asus 129 * Some events we use, same for all Asus
130 */ 130 */
131#define ATKD_BR_UP 0x10 /* (event & ~ATKD_BR_UP) = brightness level */ 131#define ATKD_BRNUP_MIN 0x10
132#define ATKD_BR_DOWN 0x20 /* (event & ~ATKD_BR_DOWN) = britghness level */ 132#define ATKD_BRNUP_MAX 0x1f
133#define ATKD_BR_MIN ATKD_BR_UP 133#define ATKD_BRNDOWN_MIN 0x20
134#define ATKD_BR_MAX (ATKD_BR_DOWN | 0xF) /* 0x2f */ 134#define ATKD_BRNDOWN_MAX 0x2f
135#define ATKD_BRNDOWN 0x20
136#define ATKD_BRNUP 0x2f
135#define ATKD_LCD_ON 0x33 137#define ATKD_LCD_ON 0x33
136#define ATKD_LCD_OFF 0x34 138#define ATKD_LCD_OFF 0x34
137 139
@@ -301,40 +303,65 @@ static const struct key_entry asus_keymap[] = {
301 {KE_KEY, 0x17, { KEY_ZOOM } }, 303 {KE_KEY, 0x17, { KEY_ZOOM } },
302 {KE_KEY, 0x1f, { KEY_BATTERY } }, 304 {KE_KEY, 0x1f, { KEY_BATTERY } },
303 /* End of Lenovo SL Specific keycodes */ 305 /* End of Lenovo SL Specific keycodes */
306 {KE_KEY, ATKD_BRNDOWN, { KEY_BRIGHTNESSDOWN } },
307 {KE_KEY, ATKD_BRNUP, { KEY_BRIGHTNESSUP } },
304 {KE_KEY, 0x30, { KEY_VOLUMEUP } }, 308 {KE_KEY, 0x30, { KEY_VOLUMEUP } },
305 {KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, 309 {KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
306 {KE_KEY, 0x32, { KEY_MUTE } }, 310 {KE_KEY, 0x32, { KEY_MUTE } },
307 {KE_KEY, 0x33, { KEY_SWITCHVIDEOMODE } }, 311 {KE_KEY, 0x33, { KEY_DISPLAYTOGGLE } }, /* LCD on */
308 {KE_KEY, 0x34, { KEY_SWITCHVIDEOMODE } }, 312 {KE_KEY, 0x34, { KEY_DISPLAY_OFF } }, /* LCD off */
309 {KE_KEY, 0x40, { KEY_PREVIOUSSONG } }, 313 {KE_KEY, 0x40, { KEY_PREVIOUSSONG } },
310 {KE_KEY, 0x41, { KEY_NEXTSONG } }, 314 {KE_KEY, 0x41, { KEY_NEXTSONG } },
311 {KE_KEY, 0x43, { KEY_STOPCD } }, 315 {KE_KEY, 0x43, { KEY_STOPCD } }, /* Stop/Eject */
312 {KE_KEY, 0x45, { KEY_PLAYPAUSE } }, 316 {KE_KEY, 0x45, { KEY_PLAYPAUSE } },
313 {KE_KEY, 0x4c, { KEY_MEDIA } }, 317 {KE_KEY, 0x4c, { KEY_MEDIA } }, /* WMP Key */
314 {KE_KEY, 0x50, { KEY_EMAIL } }, 318 {KE_KEY, 0x50, { KEY_EMAIL } },
315 {KE_KEY, 0x51, { KEY_WWW } }, 319 {KE_KEY, 0x51, { KEY_WWW } },
316 {KE_KEY, 0x55, { KEY_CALC } }, 320 {KE_KEY, 0x55, { KEY_CALC } },
321 {KE_IGNORE, 0x57, }, /* Battery mode */
322 {KE_IGNORE, 0x58, }, /* AC mode */
317 {KE_KEY, 0x5C, { KEY_SCREENLOCK } }, /* Screenlock */ 323 {KE_KEY, 0x5C, { KEY_SCREENLOCK } }, /* Screenlock */
318 {KE_KEY, 0x5D, { KEY_WLAN } }, 324 {KE_KEY, 0x5D, { KEY_WLAN } }, /* WLAN Toggle */
319 {KE_KEY, 0x5E, { KEY_WLAN } }, 325 {KE_KEY, 0x5E, { KEY_WLAN } }, /* WLAN Enable */
320 {KE_KEY, 0x5F, { KEY_WLAN } }, 326 {KE_KEY, 0x5F, { KEY_WLAN } }, /* WLAN Disable */
321 {KE_KEY, 0x60, { KEY_SWITCHVIDEOMODE } }, 327 {KE_KEY, 0x60, { KEY_TOUCHPAD_ON } },
322 {KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } }, 328 {KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD only */
323 {KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, 329 {KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT only */
324 {KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, 330 {KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT */
325 {KE_KEY, 0x6B, { KEY_F13 } }, /* Lock Touchpad */ 331 {KE_KEY, 0x64, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV */
332 {KE_KEY, 0x65, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV */
333 {KE_KEY, 0x66, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV */
334 {KE_KEY, 0x67, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV */
335 {KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } }, /* Lock Touchpad */
326 {KE_KEY, 0x6C, { KEY_SLEEP } }, /* Suspend */ 336 {KE_KEY, 0x6C, { KEY_SLEEP } }, /* Suspend */
327 {KE_KEY, 0x6D, { KEY_SLEEP } }, /* Hibernate */ 337 {KE_KEY, 0x6D, { KEY_SLEEP } }, /* Hibernate */
328 {KE_KEY, 0x7E, { KEY_BLUETOOTH } }, 338 {KE_IGNORE, 0x6E, }, /* Low Battery notification */
329 {KE_KEY, 0x7D, { KEY_BLUETOOTH } }, 339 {KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */
340 {KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */
330 {KE_KEY, 0x82, { KEY_CAMERA } }, 341 {KE_KEY, 0x82, { KEY_CAMERA } },
331 {KE_KEY, 0x88, { KEY_WLAN } }, 342 {KE_KEY, 0x88, { KEY_RFKILL } }, /* Radio Toggle Key */
332 {KE_KEY, 0x8A, { KEY_PROG1 } }, 343 {KE_KEY, 0x8A, { KEY_PROG1 } }, /* Color enhancement mode */
344 {KE_KEY, 0x8C, { KEY_SWITCHVIDEOMODE } }, /* SDSP DVI only */
345 {KE_KEY, 0x8D, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + DVI */
346 {KE_KEY, 0x8E, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + DVI */
347 {KE_KEY, 0x8F, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + DVI */
348 {KE_KEY, 0x90, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + DVI */
349 {KE_KEY, 0x91, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + DVI */
350 {KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */
351 {KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI */
333 {KE_KEY, 0x95, { KEY_MEDIA } }, 352 {KE_KEY, 0x95, { KEY_MEDIA } },
334 {KE_KEY, 0x99, { KEY_PHONE } }, 353 {KE_KEY, 0x99, { KEY_PHONE } },
335 {KE_KEY, 0xc4, { KEY_KBDILLUMUP } }, 354 {KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */
336 {KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } }, 355 {KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */
337 {KE_KEY, 0xb5, { KEY_CALC } }, 356 {KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */
357 {KE_KEY, 0xA3, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + HDMI */
358 {KE_KEY, 0xA4, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + HDMI */
359 {KE_KEY, 0xA5, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + HDMI */
360 {KE_KEY, 0xA6, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + HDMI */
361 {KE_KEY, 0xA7, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + HDMI */
362 {KE_KEY, 0xB5, { KEY_CALC } },
363 {KE_KEY, 0xC4, { KEY_KBDILLUMUP } },
364 {KE_KEY, 0xC5, { KEY_KBDILLUMDOWN } },
338 {KE_END, 0}, 365 {KE_END, 0},
339}; 366};
340 367
@@ -1521,15 +1548,19 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event)
1521 dev_name(&asus->device->dev), event, 1548 dev_name(&asus->device->dev), event,
1522 count); 1549 count);
1523 1550
1524 /* Brightness events are special */ 1551 if (event >= ATKD_BRNUP_MIN && event <= ATKD_BRNUP_MAX)
1525 if (event >= ATKD_BR_MIN && event <= ATKD_BR_MAX) { 1552 event = ATKD_BRNUP;
1553 else if (event >= ATKD_BRNDOWN_MIN &&
1554 event <= ATKD_BRNDOWN_MAX)
1555 event = ATKD_BRNDOWN;
1526 1556
1527 /* Ignore them completely if the acpi video driver is used */ 1557 /* Brightness events are special */
1558 if (event == ATKD_BRNDOWN || event == ATKD_BRNUP) {
1528 if (asus->backlight_device != NULL) { 1559 if (asus->backlight_device != NULL) {
1529 /* Update the backlight device. */ 1560 /* Update the backlight device. */
1530 asus_backlight_notify(asus); 1561 asus_backlight_notify(asus);
1562 return ;
1531 } 1563 }
1532 return ;
1533 } 1564 }
1534 1565
1535 /* Accelerometer "coarse orientation change" event */ 1566 /* Accelerometer "coarse orientation change" event */
diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c
index be790402e0f1..210b5b872125 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -59,6 +59,17 @@ static struct quirk_entry quirk_asus_unknown = {
59 .wapf = 0, 59 .wapf = 0,
60}; 60};
61 61
62/*
63 * For those machines that need software to control bt/wifi status
64 * and can't adjust brightness through ACPI interface
65 * and have duplicate events(ACPI and WMI) for display toggle
66 */
67static struct quirk_entry quirk_asus_x55u = {
68 .wapf = 4,
69 .wmi_backlight_power = true,
70 .no_display_toggle = true,
71};
72
62static struct quirk_entry quirk_asus_x401u = { 73static struct quirk_entry quirk_asus_x401u = {
63 .wapf = 4, 74 .wapf = 4,
64}; 75};
@@ -77,6 +88,15 @@ static struct dmi_system_id asus_quirks[] = {
77 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 88 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
78 DMI_MATCH(DMI_PRODUCT_NAME, "X401U"), 89 DMI_MATCH(DMI_PRODUCT_NAME, "X401U"),
79 }, 90 },
91 .driver_data = &quirk_asus_x55u,
92 },
93 {
94 .callback = dmi_matched,
95 .ident = "ASUSTeK COMPUTER INC. X401A",
96 .matches = {
97 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
98 DMI_MATCH(DMI_PRODUCT_NAME, "X401A"),
99 },
80 .driver_data = &quirk_asus_x401u, 100 .driver_data = &quirk_asus_x401u,
81 }, 101 },
82 { 102 {
@@ -95,6 +115,15 @@ static struct dmi_system_id asus_quirks[] = {
95 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 115 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
96 DMI_MATCH(DMI_PRODUCT_NAME, "X501U"), 116 DMI_MATCH(DMI_PRODUCT_NAME, "X501U"),
97 }, 117 },
118 .driver_data = &quirk_asus_x55u,
119 },
120 {
121 .callback = dmi_matched,
122 .ident = "ASUSTeK COMPUTER INC. X501A",
123 .matches = {
124 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
125 DMI_MATCH(DMI_PRODUCT_NAME, "X501A"),
126 },
98 .driver_data = &quirk_asus_x401u, 127 .driver_data = &quirk_asus_x401u,
99 }, 128 },
100 { 129 {
@@ -131,7 +160,7 @@ static struct dmi_system_id asus_quirks[] = {
131 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 160 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
132 DMI_MATCH(DMI_PRODUCT_NAME, "X55U"), 161 DMI_MATCH(DMI_PRODUCT_NAME, "X55U"),
133 }, 162 },
134 .driver_data = &quirk_asus_x401u, 163 .driver_data = &quirk_asus_x55u,
135 }, 164 },
136 { 165 {
137 .callback = dmi_matched, 166 .callback = dmi_matched,
@@ -161,6 +190,8 @@ static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver)
161} 190}
162 191
163static const struct key_entry asus_nb_wmi_keymap[] = { 192static const struct key_entry asus_nb_wmi_keymap[] = {
193 { KE_KEY, ASUS_WMI_BRN_DOWN, { KEY_BRIGHTNESSDOWN } },
194 { KE_KEY, ASUS_WMI_BRN_UP, { KEY_BRIGHTNESSUP } },
164 { KE_KEY, 0x30, { KEY_VOLUMEUP } }, 195 { KE_KEY, 0x30, { KEY_VOLUMEUP } },
165 { KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, 196 { KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
166 { KE_KEY, 0x32, { KEY_MUTE } }, 197 { KE_KEY, 0x32, { KEY_MUTE } },
@@ -168,9 +199,9 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
168 { KE_KEY, 0x34, { KEY_DISPLAY_OFF } }, /* LCD off */ 199 { KE_KEY, 0x34, { KEY_DISPLAY_OFF } }, /* LCD off */
169 { KE_KEY, 0x40, { KEY_PREVIOUSSONG } }, 200 { KE_KEY, 0x40, { KEY_PREVIOUSSONG } },
170 { KE_KEY, 0x41, { KEY_NEXTSONG } }, 201 { KE_KEY, 0x41, { KEY_NEXTSONG } },
171 { KE_KEY, 0x43, { KEY_STOPCD } }, 202 { KE_KEY, 0x43, { KEY_STOPCD } }, /* Stop/Eject */
172 { KE_KEY, 0x45, { KEY_PLAYPAUSE } }, 203 { KE_KEY, 0x45, { KEY_PLAYPAUSE } },
173 { KE_KEY, 0x4c, { KEY_MEDIA } }, 204 { KE_KEY, 0x4c, { KEY_MEDIA } }, /* WMP Key */
174 { KE_KEY, 0x50, { KEY_EMAIL } }, 205 { KE_KEY, 0x50, { KEY_EMAIL } },
175 { KE_KEY, 0x51, { KEY_WWW } }, 206 { KE_KEY, 0x51, { KEY_WWW } },
176 { KE_KEY, 0x55, { KEY_CALC } }, 207 { KE_KEY, 0x55, { KEY_CALC } },
@@ -180,25 +211,42 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
180 { KE_KEY, 0x5D, { KEY_WLAN } }, /* Wireless console Toggle */ 211 { KE_KEY, 0x5D, { KEY_WLAN } }, /* Wireless console Toggle */
181 { KE_KEY, 0x5E, { KEY_WLAN } }, /* Wireless console Enable */ 212 { KE_KEY, 0x5E, { KEY_WLAN } }, /* Wireless console Enable */
182 { KE_KEY, 0x5F, { KEY_WLAN } }, /* Wireless console Disable */ 213 { KE_KEY, 0x5F, { KEY_WLAN } }, /* Wireless console Disable */
183 { KE_KEY, 0x60, { KEY_SWITCHVIDEOMODE } }, 214 { KE_KEY, 0x60, { KEY_TOUCHPAD_ON } },
184 { KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } }, 215 { KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD only */
185 { KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, 216 { KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT only */
186 { KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, 217 { KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT */
218 { KE_KEY, 0x64, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV */
219 { KE_KEY, 0x65, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV */
220 { KE_KEY, 0x66, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV */
221 { KE_KEY, 0x67, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV */
187 { KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } }, 222 { KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },
188 { KE_KEY, 0x7D, { KEY_BLUETOOTH } }, 223 { KE_IGNORE, 0x6E, }, /* Low Battery notification */
189 { KE_KEY, 0x7E, { KEY_BLUETOOTH } }, 224 { KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */
225 { KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */
190 { KE_KEY, 0x82, { KEY_CAMERA } }, 226 { KE_KEY, 0x82, { KEY_CAMERA } },
191 { KE_KEY, 0x88, { KEY_RFKILL } }, 227 { KE_KEY, 0x88, { KEY_RFKILL } }, /* Radio Toggle Key */
192 { KE_KEY, 0x8A, { KEY_PROG1 } }, 228 { KE_KEY, 0x8A, { KEY_PROG1 } }, /* Color enhancement mode */
229 { KE_KEY, 0x8C, { KEY_SWITCHVIDEOMODE } }, /* SDSP DVI only */
230 { KE_KEY, 0x8D, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + DVI */
231 { KE_KEY, 0x8E, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + DVI */
232 { KE_KEY, 0x8F, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + DVI */
233 { KE_KEY, 0x90, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + DVI */
234 { KE_KEY, 0x91, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + DVI */
235 { KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */
236 { KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI */
193 { KE_KEY, 0x95, { KEY_MEDIA } }, 237 { KE_KEY, 0x95, { KEY_MEDIA } },
194 { KE_KEY, 0x99, { KEY_PHONE } }, 238 { KE_KEY, 0x99, { KEY_PHONE } },
195 { KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */ 239 { KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */
196 { KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */ 240 { KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */
197 { KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */ 241 { KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */
198 { KE_KEY, 0xA3, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + HDMI */ 242 { KE_KEY, 0xA3, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + HDMI */
199 { KE_KEY, 0xb5, { KEY_CALC } }, 243 { KE_KEY, 0xA4, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + HDMI */
200 { KE_KEY, 0xc4, { KEY_KBDILLUMUP } }, 244 { KE_KEY, 0xA5, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + HDMI */
201 { KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } }, 245 { KE_KEY, 0xA6, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + HDMI */
246 { KE_KEY, 0xA7, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + HDMI */
247 { KE_KEY, 0xB5, { KEY_CALC } },
248 { KE_KEY, 0xC4, { KEY_KBDILLUMUP } },
249 { KE_KEY, 0xC5, { KEY_KBDILLUMDOWN } },
202 { KE_END, 0}, 250 { KE_END, 0},
203}; 251};
204 252
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index f80ae4d10f68..c11b2426dac1 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -187,6 +187,8 @@ struct asus_wmi {
187 struct device *hwmon_device; 187 struct device *hwmon_device;
188 struct platform_device *platform_device; 188 struct platform_device *platform_device;
189 189
190 struct led_classdev wlan_led;
191 int wlan_led_wk;
190 struct led_classdev tpd_led; 192 struct led_classdev tpd_led;
191 int tpd_led_wk; 193 int tpd_led_wk;
192 struct led_classdev kbd_led; 194 struct led_classdev kbd_led;
@@ -194,6 +196,7 @@ struct asus_wmi {
194 struct workqueue_struct *led_workqueue; 196 struct workqueue_struct *led_workqueue;
195 struct work_struct tpd_led_work; 197 struct work_struct tpd_led_work;
196 struct work_struct kbd_led_work; 198 struct work_struct kbd_led_work;
199 struct work_struct wlan_led_work;
197 200
198 struct asus_rfkill wlan; 201 struct asus_rfkill wlan;
199 struct asus_rfkill bluetooth; 202 struct asus_rfkill bluetooth;
@@ -456,12 +459,65 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
456 return value; 459 return value;
457} 460}
458 461
462static int wlan_led_unknown_state(struct asus_wmi *asus)
463{
464 u32 result;
465
466 asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result);
467
468 return result & ASUS_WMI_DSTS_UNKNOWN_BIT;
469}
470
471static int wlan_led_presence(struct asus_wmi *asus)
472{
473 u32 result;
474
475 asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result);
476
477 return result & ASUS_WMI_DSTS_PRESENCE_BIT;
478}
479
480static void wlan_led_update(struct work_struct *work)
481{
482 int ctrl_param;
483 struct asus_wmi *asus;
484
485 asus = container_of(work, struct asus_wmi, wlan_led_work);
486
487 ctrl_param = asus->wlan_led_wk;
488 asus_wmi_set_devstate(ASUS_WMI_DEVID_WIRELESS_LED, ctrl_param, NULL);
489}
490
491static void wlan_led_set(struct led_classdev *led_cdev,
492 enum led_brightness value)
493{
494 struct asus_wmi *asus;
495
496 asus = container_of(led_cdev, struct asus_wmi, wlan_led);
497
498 asus->wlan_led_wk = !!value;
499 queue_work(asus->led_workqueue, &asus->wlan_led_work);
500}
501
502static enum led_brightness wlan_led_get(struct led_classdev *led_cdev)
503{
504 struct asus_wmi *asus;
505 u32 result;
506
507 asus = container_of(led_cdev, struct asus_wmi, wlan_led);
508 asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result);
509
510 return result & ASUS_WMI_DSTS_BRIGHTNESS_MASK;
511}
512
459static void asus_wmi_led_exit(struct asus_wmi *asus) 513static void asus_wmi_led_exit(struct asus_wmi *asus)
460{ 514{
461 if (!IS_ERR_OR_NULL(asus->kbd_led.dev)) 515 if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
462 led_classdev_unregister(&asus->kbd_led); 516 led_classdev_unregister(&asus->kbd_led);
463 if (!IS_ERR_OR_NULL(asus->tpd_led.dev)) 517 if (!IS_ERR_OR_NULL(asus->tpd_led.dev))
464 led_classdev_unregister(&asus->tpd_led); 518 led_classdev_unregister(&asus->tpd_led);
519 if (!IS_ERR_OR_NULL(asus->wlan_led.dev))
520 led_classdev_unregister(&asus->wlan_led);
465 if (asus->led_workqueue) 521 if (asus->led_workqueue)
466 destroy_workqueue(asus->led_workqueue); 522 destroy_workqueue(asus->led_workqueue);
467} 523}
@@ -498,6 +554,23 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
498 554
499 rv = led_classdev_register(&asus->platform_device->dev, 555 rv = led_classdev_register(&asus->platform_device->dev,
500 &asus->kbd_led); 556 &asus->kbd_led);
557 if (rv)
558 goto error;
559 }
560
561 if (wlan_led_presence(asus)) {
562 INIT_WORK(&asus->wlan_led_work, wlan_led_update);
563
564 asus->wlan_led.name = "asus::wlan";
565 asus->wlan_led.brightness_set = wlan_led_set;
566 if (!wlan_led_unknown_state(asus))
567 asus->wlan_led.brightness_get = wlan_led_get;
568 asus->wlan_led.flags = LED_CORE_SUSPENDRESUME;
569 asus->wlan_led.max_brightness = 1;
570 asus->wlan_led.default_trigger = "asus-wlan";
571
572 rv = led_classdev_register(&asus->platform_device->dev,
573 &asus->wlan_led);
501 } 574 }
502 575
503error: 576error:
@@ -813,6 +886,9 @@ static int asus_new_rfkill(struct asus_wmi *asus,
813 if (!*rfkill) 886 if (!*rfkill)
814 return -EINVAL; 887 return -EINVAL;
815 888
889 if (dev_id == ASUS_WMI_DEVID_WLAN)
890 rfkill_set_led_trigger_name(*rfkill, "asus-wlan");
891
816 rfkill_init_sw_state(*rfkill, !result); 892 rfkill_init_sw_state(*rfkill, !result);
817 result = rfkill_register(*rfkill); 893 result = rfkill_register(*rfkill);
818 if (result) { 894 if (result) {
@@ -1265,6 +1341,18 @@ static void asus_wmi_backlight_exit(struct asus_wmi *asus)
1265 asus->backlight_device = NULL; 1341 asus->backlight_device = NULL;
1266} 1342}
1267 1343
1344static int is_display_toggle(int code)
1345{
1346 /* display toggle keys */
1347 if ((code >= 0x61 && code <= 0x67) ||
1348 (code >= 0x8c && code <= 0x93) ||
1349 (code >= 0xa0 && code <= 0xa7) ||
1350 (code >= 0xd0 && code <= 0xd5))
1351 return 1;
1352
1353 return 0;
1354}
1355
1268static void asus_wmi_notify(u32 value, void *context) 1356static void asus_wmi_notify(u32 value, void *context)
1269{ 1357{
1270 struct asus_wmi *asus = context; 1358 struct asus_wmi *asus = context;
@@ -1298,16 +1386,24 @@ static void asus_wmi_notify(u32 value, void *context)
1298 } 1386 }
1299 1387
1300 if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX) 1388 if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
1301 code = NOTIFY_BRNUP_MIN; 1389 code = ASUS_WMI_BRN_UP;
1302 else if (code >= NOTIFY_BRNDOWN_MIN && 1390 else if (code >= NOTIFY_BRNDOWN_MIN &&
1303 code <= NOTIFY_BRNDOWN_MAX) 1391 code <= NOTIFY_BRNDOWN_MAX)
1304 code = NOTIFY_BRNDOWN_MIN; 1392 code = ASUS_WMI_BRN_DOWN;
1305 1393
1306 if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) { 1394 if (code == ASUS_WMI_BRN_DOWN || code == ASUS_WMI_BRN_UP) {
1307 if (!acpi_video_backlight_support()) 1395 if (!acpi_video_backlight_support()) {
1308 asus_wmi_backlight_notify(asus, orig_code); 1396 asus_wmi_backlight_notify(asus, orig_code);
1309 } else if (!sparse_keymap_report_event(asus->inputdev, code, 1397 goto exit;
1310 key_value, autorelease)) 1398 }
1399 }
1400
1401 if (is_display_toggle(code) &&
1402 asus->driver->quirks->no_display_toggle)
1403 goto exit;
1404
1405 if (!sparse_keymap_report_event(asus->inputdev, code,
1406 key_value, autorelease))
1311 pr_info("Unknown key %x pressed\n", code); 1407 pr_info("Unknown key %x pressed\n", code);
1312 1408
1313exit: 1409exit:
diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h
index 4c9bd38bb0a2..4da4c8bafe70 100644
--- a/drivers/platform/x86/asus-wmi.h
+++ b/drivers/platform/x86/asus-wmi.h
@@ -30,6 +30,8 @@
30#include <linux/platform_device.h> 30#include <linux/platform_device.h>
31 31
32#define ASUS_WMI_KEY_IGNORE (-1) 32#define ASUS_WMI_KEY_IGNORE (-1)
33#define ASUS_WMI_BRN_DOWN 0x20
34#define ASUS_WMI_BRN_UP 0x2f
33 35
34struct module; 36struct module;
35struct key_entry; 37struct key_entry;
@@ -41,6 +43,13 @@ struct quirk_entry {
41 bool store_backlight_power; 43 bool store_backlight_power;
42 bool wmi_backlight_power; 44 bool wmi_backlight_power;
43 int wapf; 45 int wapf;
46 /*
47 * For machines with AMD graphic chips, it will send out WMI event
48 * and ACPI interrupt at the same time while hitting the hotkey.
49 * To simplify the problem, we just have to ignore the WMI event,
50 * and let the ACPI interrupt to send out the key event.
51 */
52 int no_display_toggle;
44}; 53};
45 54
46struct asus_wmi_driver { 55struct asus_wmi_driver {
diff --git a/drivers/platform/x86/chromeos_laptop.c b/drivers/platform/x86/chromeos_laptop.c
new file mode 100644
index 000000000000..93d66809355a
--- /dev/null
+++ b/drivers/platform/x86/chromeos_laptop.c
@@ -0,0 +1,371 @@
1/*
2 * chromeos_laptop.c - Driver to instantiate Chromebook i2c/smbus devices.
3 *
4 * Author : Benson Leung <bleung@chromium.org>
5 *
6 * Copyright (C) 2012 Google, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#include <linux/dmi.h>
25#include <linux/i2c.h>
26#include <linux/module.h>
27
28#define ATMEL_TP_I2C_ADDR 0x4b
29#define ATMEL_TP_I2C_BL_ADDR 0x25
30#define ATMEL_TS_I2C_ADDR 0x4a
31#define ATMEL_TS_I2C_BL_ADDR 0x26
32#define CYAPA_TP_I2C_ADDR 0x67
33#define ISL_ALS_I2C_ADDR 0x44
34#define TAOS_ALS_I2C_ADDR 0x29
35
36static struct i2c_client *als;
37static struct i2c_client *tp;
38static struct i2c_client *ts;
39
40const char *i2c_adapter_names[] = {
41 "SMBus I801 adapter",
42 "i915 gmbus vga",
43 "i915 gmbus panel",
44};
45
46/* Keep this enum consistent with i2c_adapter_names */
47enum i2c_adapter_type {
48 I2C_ADAPTER_SMBUS = 0,
49 I2C_ADAPTER_VGADDC,
50 I2C_ADAPTER_PANEL,
51};
52
53static struct i2c_board_info __initdata cyapa_device = {
54 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
55 .flags = I2C_CLIENT_WAKE,
56};
57
58static struct i2c_board_info __initdata isl_als_device = {
59 I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
60};
61
62static struct i2c_board_info __initdata tsl2583_als_device = {
63 I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
64};
65
66static struct i2c_board_info __initdata tsl2563_als_device = {
67 I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR),
68};
69
70static struct i2c_board_info __initdata atmel_224s_tp_device = {
71 I2C_BOARD_INFO("atmel_mxt_tp", ATMEL_TP_I2C_ADDR),
72 .platform_data = NULL,
73 .flags = I2C_CLIENT_WAKE,
74};
75
76static struct i2c_board_info __initdata atmel_1664s_device = {
77 I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR),
78 .platform_data = NULL,
79 .flags = I2C_CLIENT_WAKE,
80};
81
82static struct i2c_client __init *__add_probed_i2c_device(
83 const char *name,
84 int bus,
85 struct i2c_board_info *info,
86 const unsigned short *addrs)
87{
88 const struct dmi_device *dmi_dev;
89 const struct dmi_dev_onboard *dev_data;
90 struct i2c_adapter *adapter;
91 struct i2c_client *client;
92
93 if (bus < 0)
94 return NULL;
95 /*
96 * If a name is specified, look for irq platform information stashed
97 * in DMI_DEV_TYPE_DEV_ONBOARD by the Chrome OS custom system firmware.
98 */
99 if (name) {
100 dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, name, NULL);
101 if (!dmi_dev) {
102 pr_err("%s failed to dmi find device %s.\n",
103 __func__,
104 name);
105 return NULL;
106 }
107 dev_data = (struct dmi_dev_onboard *)dmi_dev->device_data;
108 if (!dev_data) {
109 pr_err("%s failed to get data from dmi for %s.\n",
110 __func__, name);
111 return NULL;
112 }
113 info->irq = dev_data->instance;
114 }
115
116 adapter = i2c_get_adapter(bus);
117 if (!adapter) {
118 pr_err("%s failed to get i2c adapter %d.\n", __func__, bus);
119 return NULL;
120 }
121
122 /* add the i2c device */
123 client = i2c_new_probed_device(adapter, info, addrs, NULL);
124 if (!client)
125 pr_err("%s failed to register device %d-%02x\n",
126 __func__, bus, info->addr);
127 else
128 pr_debug("%s added i2c device %d-%02x\n",
129 __func__, bus, info->addr);
130
131 i2c_put_adapter(adapter);
132 return client;
133}
134
135static int __init __find_i2c_adap(struct device *dev, void *data)
136{
137 const char *name = data;
138 static const char *prefix = "i2c-";
139 struct i2c_adapter *adapter;
140 if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0)
141 return 0;
142 adapter = to_i2c_adapter(dev);
143 return (strncmp(adapter->name, name, strlen(name)) == 0);
144}
145
146static int __init find_i2c_adapter_num(enum i2c_adapter_type type)
147{
148 struct device *dev = NULL;
149 struct i2c_adapter *adapter;
150 const char *name = i2c_adapter_names[type];
151 /* find the adapter by name */
152 dev = bus_find_device(&i2c_bus_type, NULL, (void *)name,
153 __find_i2c_adap);
154 if (!dev) {
155 pr_err("%s: i2c adapter %s not found on system.\n", __func__,
156 name);
157 return -ENODEV;
158 }
159 adapter = to_i2c_adapter(dev);
160 return adapter->nr;
161}
162
163/*
164 * Takes a list of addresses in addrs as such :
165 * { addr1, ... , addrn, I2C_CLIENT_END };
166 * add_probed_i2c_device will use i2c_new_probed_device
167 * and probe for devices at all of the addresses listed.
168 * Returns NULL if no devices found.
169 * See Documentation/i2c/instantiating-devices for more information.
170 */
171static __init struct i2c_client *add_probed_i2c_device(
172 const char *name,
173 enum i2c_adapter_type type,
174 struct i2c_board_info *info,
175 const unsigned short *addrs)
176{
177 return __add_probed_i2c_device(name,
178 find_i2c_adapter_num(type),
179 info,
180 addrs);
181}
182
183/*
184 * Probes for a device at a single address, the one provided by
185 * info->addr.
186 * Returns NULL if no device found.
187 */
188static __init struct i2c_client *add_i2c_device(const char *name,
189 enum i2c_adapter_type type,
190 struct i2c_board_info *info)
191{
192 const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END };
193 return __add_probed_i2c_device(name,
194 find_i2c_adapter_num(type),
195 info,
196 addr_list);
197}
198
199
200static struct i2c_client __init *add_smbus_device(const char *name,
201 struct i2c_board_info *info)
202{
203 return add_i2c_device(name, I2C_ADAPTER_SMBUS, info);
204}
205
206static int __init setup_cyapa_smbus_tp(const struct dmi_system_id *id)
207{
208 /* add cyapa touchpad on smbus */
209 tp = add_smbus_device("trackpad", &cyapa_device);
210 return 0;
211}
212
213static int __init setup_atmel_224s_tp(const struct dmi_system_id *id)
214{
215 const unsigned short addr_list[] = { ATMEL_TP_I2C_BL_ADDR,
216 ATMEL_TP_I2C_ADDR,
217 I2C_CLIENT_END };
218
219 /* add atmel mxt touchpad on VGA DDC GMBus */
220 tp = add_probed_i2c_device("trackpad", I2C_ADAPTER_VGADDC,
221 &atmel_224s_tp_device, addr_list);
222 return 0;
223}
224
225static int __init setup_atmel_1664s_ts(const struct dmi_system_id *id)
226{
227 const unsigned short addr_list[] = { ATMEL_TS_I2C_BL_ADDR,
228 ATMEL_TS_I2C_ADDR,
229 I2C_CLIENT_END };
230
231 /* add atmel mxt touch device on PANEL GMBus */
232 ts = add_probed_i2c_device("touchscreen", I2C_ADAPTER_PANEL,
233 &atmel_1664s_device, addr_list);
234 return 0;
235}
236
237
238static int __init setup_isl29018_als(const struct dmi_system_id *id)
239{
240 /* add isl29018 light sensor */
241 als = add_smbus_device("lightsensor", &isl_als_device);
242 return 0;
243}
244
245static int __init setup_isl29023_als(const struct dmi_system_id *id)
246{
247 /* add isl29023 light sensor on Panel GMBus */
248 als = add_i2c_device("lightsensor", I2C_ADAPTER_PANEL,
249 &isl_als_device);
250 return 0;
251}
252
253static int __init setup_tsl2583_als(const struct dmi_system_id *id)
254{
255 /* add tsl2583 light sensor on smbus */
256 als = add_smbus_device(NULL, &tsl2583_als_device);
257 return 0;
258}
259
260static int __init setup_tsl2563_als(const struct dmi_system_id *id)
261{
262 /* add tsl2563 light sensor on smbus */
263 als = add_smbus_device(NULL, &tsl2563_als_device);
264 return 0;
265}
266
267static struct dmi_system_id __initdata chromeos_laptop_dmi_table[] = {
268 {
269 .ident = "Samsung Series 5 550 - Touchpad",
270 .matches = {
271 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
272 DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
273 },
274 .callback = setup_cyapa_smbus_tp,
275 },
276 {
277 .ident = "Chromebook Pixel - Touchscreen",
278 .matches = {
279 DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
280 DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
281 },
282 .callback = setup_atmel_1664s_ts,
283 },
284 {
285 .ident = "Chromebook Pixel - Touchpad",
286 .matches = {
287 DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
288 DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
289 },
290 .callback = setup_atmel_224s_tp,
291 },
292 {
293 .ident = "Samsung Series 5 550 - Light Sensor",
294 .matches = {
295 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
296 DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
297 },
298 .callback = setup_isl29018_als,
299 },
300 {
301 .ident = "Chromebook Pixel - Light Sensor",
302 .matches = {
303 DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
304 DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
305 },
306 .callback = setup_isl29023_als,
307 },
308 {
309 .ident = "Acer C7 Chromebook - Touchpad",
310 .matches = {
311 DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"),
312 },
313 .callback = setup_cyapa_smbus_tp,
314 },
315 {
316 .ident = "HP Pavilion 14 Chromebook - Touchpad",
317 .matches = {
318 DMI_MATCH(DMI_PRODUCT_NAME, "Butterfly"),
319 },
320 .callback = setup_cyapa_smbus_tp,
321 },
322 {
323 .ident = "Samsung Series 5 - Light Sensor",
324 .matches = {
325 DMI_MATCH(DMI_PRODUCT_NAME, "Alex"),
326 },
327 .callback = setup_tsl2583_als,
328 },
329 {
330 .ident = "Cr-48 - Light Sensor",
331 .matches = {
332 DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),
333 },
334 .callback = setup_tsl2563_als,
335 },
336 {
337 .ident = "Acer AC700 - Light Sensor",
338 .matches = {
339 DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"),
340 },
341 .callback = setup_tsl2563_als,
342 },
343 { }
344};
345MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table);
346
347static int __init chromeos_laptop_init(void)
348{
349 if (!dmi_check_system(chromeos_laptop_dmi_table)) {
350 pr_debug("%s unsupported system.\n", __func__);
351 return -ENODEV;
352 }
353 return 0;
354}
355
356static void __exit chromeos_laptop_exit(void)
357{
358 if (als)
359 i2c_unregister_device(als);
360 if (tp)
361 i2c_unregister_device(tp);
362 if (ts)
363 i2c_unregister_device(ts);
364}
365
366module_init(chromeos_laptop_init);
367module_exit(chromeos_laptop_exit);
368
369MODULE_DESCRIPTION("Chrome OS Laptop driver");
370MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
371MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c
index 60cb76a5b513..af67e6e56ebb 100644
--- a/drivers/platform/x86/eeepc-wmi.c
+++ b/drivers/platform/x86/eeepc-wmi.c
@@ -63,6 +63,8 @@ MODULE_PARM_DESC(hotplug_wireless,
63#define HOME_RELEASE 0xe5 63#define HOME_RELEASE 0xe5
64 64
65static const struct key_entry eeepc_wmi_keymap[] = { 65static const struct key_entry eeepc_wmi_keymap[] = {
66 { KE_KEY, ASUS_WMI_BRN_DOWN, { KEY_BRIGHTNESSDOWN } },
67 { KE_KEY, ASUS_WMI_BRN_UP, { KEY_BRIGHTNESSUP } },
66 /* Sleep already handled via generic ACPI code */ 68 /* Sleep already handled via generic ACPI code */
67 { KE_KEY, 0x30, { KEY_VOLUMEUP } }, 69 { KE_KEY, 0x30, { KEY_VOLUMEUP } },
68 { KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, 70 { KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c
index 1dde7accf27c..45cacf79f3a7 100644
--- a/drivers/platform/x86/hp-wmi.c
+++ b/drivers/platform/x86/hp-wmi.c
@@ -60,6 +60,7 @@ enum hp_wmi_radio {
60 HPWMI_WIFI = 0, 60 HPWMI_WIFI = 0,
61 HPWMI_BLUETOOTH = 1, 61 HPWMI_BLUETOOTH = 1,
62 HPWMI_WWAN = 2, 62 HPWMI_WWAN = 2,
63 HPWMI_GPS = 3,
63}; 64};
64 65
65enum hp_wmi_event_ids { 66enum hp_wmi_event_ids {
@@ -72,10 +73,6 @@ enum hp_wmi_event_ids {
72 HPWMI_LOCK_SWITCH = 7, 73 HPWMI_LOCK_SWITCH = 7,
73}; 74};
74 75
75static int hp_wmi_bios_setup(struct platform_device *device);
76static int __exit hp_wmi_bios_remove(struct platform_device *device);
77static int hp_wmi_resume_handler(struct device *device);
78
79struct bios_args { 76struct bios_args {
80 u32 signature; 77 u32 signature;
81 u32 command; 78 u32 command;
@@ -137,6 +134,7 @@ static const struct key_entry hp_wmi_keymap[] = {
137 { KE_KEY, 0x2142, { KEY_MEDIA } }, 134 { KE_KEY, 0x2142, { KEY_MEDIA } },
138 { KE_KEY, 0x213b, { KEY_INFO } }, 135 { KE_KEY, 0x213b, { KEY_INFO } },
139 { KE_KEY, 0x2169, { KEY_DIRECTION } }, 136 { KE_KEY, 0x2169, { KEY_DIRECTION } },
137 { KE_KEY, 0x216a, { KEY_SETUP } },
140 { KE_KEY, 0x231b, { KEY_HELP } }, 138 { KE_KEY, 0x231b, { KEY_HELP } },
141 { KE_END, 0 } 139 { KE_END, 0 }
142}; 140};
@@ -147,6 +145,7 @@ static struct platform_device *hp_wmi_platform_dev;
147static struct rfkill *wifi_rfkill; 145static struct rfkill *wifi_rfkill;
148static struct rfkill *bluetooth_rfkill; 146static struct rfkill *bluetooth_rfkill;
149static struct rfkill *wwan_rfkill; 147static struct rfkill *wwan_rfkill;
148static struct rfkill *gps_rfkill;
150 149
151struct rfkill2_device { 150struct rfkill2_device {
152 u8 id; 151 u8 id;
@@ -157,21 +156,6 @@ struct rfkill2_device {
157static int rfkill2_count; 156static int rfkill2_count;
158static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES]; 157static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES];
159 158
160static const struct dev_pm_ops hp_wmi_pm_ops = {
161 .resume = hp_wmi_resume_handler,
162 .restore = hp_wmi_resume_handler,
163};
164
165static struct platform_driver hp_wmi_driver = {
166 .driver = {
167 .name = "hp-wmi",
168 .owner = THIS_MODULE,
169 .pm = &hp_wmi_pm_ops,
170 },
171 .probe = hp_wmi_bios_setup,
172 .remove = hp_wmi_bios_remove,
173};
174
175/* 159/*
176 * hp_wmi_perform_query 160 * hp_wmi_perform_query
177 * 161 *
@@ -543,6 +527,10 @@ static void hp_wmi_notify(u32 value, void *context)
543 rfkill_set_states(wwan_rfkill, 527 rfkill_set_states(wwan_rfkill,
544 hp_wmi_get_sw_state(HPWMI_WWAN), 528 hp_wmi_get_sw_state(HPWMI_WWAN),
545 hp_wmi_get_hw_state(HPWMI_WWAN)); 529 hp_wmi_get_hw_state(HPWMI_WWAN));
530 if (gps_rfkill)
531 rfkill_set_states(gps_rfkill,
532 hp_wmi_get_sw_state(HPWMI_GPS),
533 hp_wmi_get_hw_state(HPWMI_GPS));
546 break; 534 break;
547 case HPWMI_CPU_BATTERY_THROTTLE: 535 case HPWMI_CPU_BATTERY_THROTTLE:
548 pr_info("Unimplemented CPU throttle because of 3 Cell battery event detected\n"); 536 pr_info("Unimplemented CPU throttle because of 3 Cell battery event detected\n");
@@ -670,7 +658,7 @@ static int hp_wmi_rfkill_setup(struct platform_device *device)
670 (void *) HPWMI_WWAN); 658 (void *) HPWMI_WWAN);
671 if (!wwan_rfkill) { 659 if (!wwan_rfkill) {
672 err = -ENOMEM; 660 err = -ENOMEM;
673 goto register_bluetooth_error; 661 goto register_gps_error;
674 } 662 }
675 rfkill_init_sw_state(wwan_rfkill, 663 rfkill_init_sw_state(wwan_rfkill,
676 hp_wmi_get_sw_state(HPWMI_WWAN)); 664 hp_wmi_get_sw_state(HPWMI_WWAN));
@@ -681,10 +669,33 @@ static int hp_wmi_rfkill_setup(struct platform_device *device)
681 goto register_wwan_err; 669 goto register_wwan_err;
682 } 670 }
683 671
672 if (wireless & 0x8) {
673 gps_rfkill = rfkill_alloc("hp-gps", &device->dev,
674 RFKILL_TYPE_GPS,
675 &hp_wmi_rfkill_ops,
676 (void *) HPWMI_GPS);
677 if (!gps_rfkill) {
678 err = -ENOMEM;
679 goto register_bluetooth_error;
680 }
681 rfkill_init_sw_state(gps_rfkill,
682 hp_wmi_get_sw_state(HPWMI_GPS));
683 rfkill_set_hw_state(bluetooth_rfkill,
684 hp_wmi_get_hw_state(HPWMI_GPS));
685 err = rfkill_register(gps_rfkill);
686 if (err)
687 goto register_gps_error;
688 }
689
684 return 0; 690 return 0;
685register_wwan_err: 691register_wwan_err:
686 rfkill_destroy(wwan_rfkill); 692 rfkill_destroy(wwan_rfkill);
687 wwan_rfkill = NULL; 693 wwan_rfkill = NULL;
694 if (gps_rfkill)
695 rfkill_unregister(gps_rfkill);
696register_gps_error:
697 rfkill_destroy(gps_rfkill);
698 gps_rfkill = NULL;
688 if (bluetooth_rfkill) 699 if (bluetooth_rfkill)
689 rfkill_unregister(bluetooth_rfkill); 700 rfkill_unregister(bluetooth_rfkill);
690register_bluetooth_error: 701register_bluetooth_error:
@@ -729,6 +740,10 @@ static int hp_wmi_rfkill2_setup(struct platform_device *device)
729 type = RFKILL_TYPE_WWAN; 740 type = RFKILL_TYPE_WWAN;
730 name = "hp-wwan"; 741 name = "hp-wwan";
731 break; 742 break;
743 case HPWMI_GPS:
744 type = RFKILL_TYPE_GPS;
745 name = "hp-gps";
746 break;
732 default: 747 default:
733 pr_warn("unknown device type 0x%x\n", 748 pr_warn("unknown device type 0x%x\n",
734 state.device[i].radio_type); 749 state.device[i].radio_type);
@@ -778,7 +793,7 @@ fail:
778 return err; 793 return err;
779} 794}
780 795
781static int hp_wmi_bios_setup(struct platform_device *device) 796static int __init hp_wmi_bios_setup(struct platform_device *device)
782{ 797{
783 int err; 798 int err;
784 799
@@ -786,6 +801,7 @@ static int hp_wmi_bios_setup(struct platform_device *device)
786 wifi_rfkill = NULL; 801 wifi_rfkill = NULL;
787 bluetooth_rfkill = NULL; 802 bluetooth_rfkill = NULL;
788 wwan_rfkill = NULL; 803 wwan_rfkill = NULL;
804 gps_rfkill = NULL;
789 rfkill2_count = 0; 805 rfkill2_count = 0;
790 806
791 if (hp_wmi_rfkill_setup(device)) 807 if (hp_wmi_rfkill_setup(device))
@@ -835,6 +851,10 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device)
835 rfkill_unregister(wwan_rfkill); 851 rfkill_unregister(wwan_rfkill);
836 rfkill_destroy(wwan_rfkill); 852 rfkill_destroy(wwan_rfkill);
837 } 853 }
854 if (gps_rfkill) {
855 rfkill_unregister(gps_rfkill);
856 rfkill_destroy(gps_rfkill);
857 }
838 858
839 return 0; 859 return 0;
840} 860}
@@ -870,51 +890,70 @@ static int hp_wmi_resume_handler(struct device *device)
870 rfkill_set_states(wwan_rfkill, 890 rfkill_set_states(wwan_rfkill,
871 hp_wmi_get_sw_state(HPWMI_WWAN), 891 hp_wmi_get_sw_state(HPWMI_WWAN),
872 hp_wmi_get_hw_state(HPWMI_WWAN)); 892 hp_wmi_get_hw_state(HPWMI_WWAN));
893 if (gps_rfkill)
894 rfkill_set_states(gps_rfkill,
895 hp_wmi_get_sw_state(HPWMI_GPS),
896 hp_wmi_get_hw_state(HPWMI_GPS));
873 897
874 return 0; 898 return 0;
875} 899}
876 900
901static const struct dev_pm_ops hp_wmi_pm_ops = {
902 .resume = hp_wmi_resume_handler,
903 .restore = hp_wmi_resume_handler,
904};
905
906static struct platform_driver hp_wmi_driver = {
907 .driver = {
908 .name = "hp-wmi",
909 .owner = THIS_MODULE,
910 .pm = &hp_wmi_pm_ops,
911 },
912 .remove = __exit_p(hp_wmi_bios_remove),
913};
914
877static int __init hp_wmi_init(void) 915static int __init hp_wmi_init(void)
878{ 916{
879 int err; 917 int err;
880 int event_capable = wmi_has_guid(HPWMI_EVENT_GUID); 918 int event_capable = wmi_has_guid(HPWMI_EVENT_GUID);
881 int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID); 919 int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID);
882 920
921 if (!bios_capable && !event_capable)
922 return -ENODEV;
923
883 if (event_capable) { 924 if (event_capable) {
884 err = hp_wmi_input_setup(); 925 err = hp_wmi_input_setup();
885 if (err) 926 if (err)
886 return err; 927 return err;
928
929 //Enable magic for hotkeys that run on the SMBus
930 ec_write(0xe6,0x6e);
887 } 931 }
888 932
889 if (bios_capable) { 933 if (bios_capable) {
890 err = platform_driver_register(&hp_wmi_driver); 934 hp_wmi_platform_dev =
891 if (err) 935 platform_device_register_simple("hp-wmi", -1, NULL, 0);
892 goto err_driver_reg; 936 if (IS_ERR(hp_wmi_platform_dev)) {
893 hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1); 937 err = PTR_ERR(hp_wmi_platform_dev);
894 if (!hp_wmi_platform_dev) { 938 goto err_destroy_input;
895 err = -ENOMEM;
896 goto err_device_alloc;
897 } 939 }
898 err = platform_device_add(hp_wmi_platform_dev); 940
941 err = platform_driver_probe(&hp_wmi_driver, hp_wmi_bios_setup);
899 if (err) 942 if (err)
900 goto err_device_add; 943 goto err_unregister_device;
901 } 944 }
902 945
903 if (!bios_capable && !event_capable)
904 return -ENODEV;
905
906 return 0; 946 return 0;
907 947
908err_device_add: 948err_unregister_device:
909 platform_device_put(hp_wmi_platform_dev); 949 platform_device_unregister(hp_wmi_platform_dev);
910err_device_alloc: 950err_destroy_input:
911 platform_driver_unregister(&hp_wmi_driver);
912err_driver_reg:
913 if (event_capable) 951 if (event_capable)
914 hp_wmi_input_destroy(); 952 hp_wmi_input_destroy();
915 953
916 return err; 954 return err;
917} 955}
956module_init(hp_wmi_init);
918 957
919static void __exit hp_wmi_exit(void) 958static void __exit hp_wmi_exit(void)
920{ 959{
@@ -926,6 +965,4 @@ static void __exit hp_wmi_exit(void)
926 platform_driver_unregister(&hp_wmi_driver); 965 platform_driver_unregister(&hp_wmi_driver);
927 } 966 }
928} 967}
929
930module_init(hp_wmi_init);
931module_exit(hp_wmi_exit); 968module_exit(hp_wmi_exit);
diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c
index 2111dbb7e1e3..6b2293875672 100644
--- a/drivers/platform/x86/msi-laptop.c
+++ b/drivers/platform/x86/msi-laptop.c
@@ -82,8 +82,19 @@
82#define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d 82#define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d
83#define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0) 83#define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0)
84 84
85#define MSI_STANDARD_EC_TOUCHPAD_ADDRESS 0xe4 85#define MSI_STANDARD_EC_FUNCTIONS_ADDRESS 0xe4
86/* Power LED is orange - Turbo mode */
87#define MSI_STANDARD_EC_TURBO_MASK (1 << 1)
88/* Power LED is green - ECO mode */
89#define MSI_STANDARD_EC_ECO_MASK (1 << 3)
90/* Touchpad is turned on */
86#define MSI_STANDARD_EC_TOUCHPAD_MASK (1 << 4) 91#define MSI_STANDARD_EC_TOUCHPAD_MASK (1 << 4)
92/* If this bit != bit 1, turbo mode can't be toggled */
93#define MSI_STANDARD_EC_TURBO_COOLDOWN_MASK (1 << 7)
94
95#define MSI_STANDARD_EC_FAN_ADDRESS 0x33
96/* If zero, fan rotates at maximal speed */
97#define MSI_STANDARD_EC_AUTOFAN_MASK (1 << 0)
87 98
88#ifdef CONFIG_PM_SLEEP 99#ifdef CONFIG_PM_SLEEP
89static int msi_laptop_resume(struct device *device); 100static int msi_laptop_resume(struct device *device);
@@ -108,23 +119,38 @@ static const struct key_entry msi_laptop_keymap[] = {
108 119
109static struct input_dev *msi_laptop_input_dev; 120static struct input_dev *msi_laptop_input_dev;
110 121
111static bool old_ec_model;
112static int wlan_s, bluetooth_s, threeg_s; 122static int wlan_s, bluetooth_s, threeg_s;
113static int threeg_exists; 123static int threeg_exists;
114
115/* Some MSI 3G netbook only have one fn key to control Wlan/Bluetooth/3G,
116 * those netbook will load the SCM (windows app) to disable the original
117 * Wlan/Bluetooth control by BIOS when user press fn key, then control
118 * Wlan/Bluetooth/3G by SCM (software control by OS). Without SCM, user
119 * cann't on/off 3G module on those 3G netbook.
120 * On Linux, msi-laptop driver will do the same thing to disable the
121 * original BIOS control, then might need use HAL or other userland
122 * application to do the software control that simulate with SCM.
123 * e.g. MSI N034 netbook
124 */
125static bool load_scm_model;
126static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg; 124static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg;
127 125
126/* MSI laptop quirks */
127struct quirk_entry {
128 bool old_ec_model;
129
130 /* Some MSI 3G netbook only have one fn key to control
131 * Wlan/Bluetooth/3G, those netbook will load the SCM (windows app) to
132 * disable the original Wlan/Bluetooth control by BIOS when user press
133 * fn key, then control Wlan/Bluetooth/3G by SCM (software control by
134 * OS). Without SCM, user cann't on/off 3G module on those 3G netbook.
135 * On Linux, msi-laptop driver will do the same thing to disable the
136 * original BIOS control, then might need use HAL or other userland
137 * application to do the software control that simulate with SCM.
138 * e.g. MSI N034 netbook
139 */
140 bool load_scm_model;
141
142 /* Some MSI laptops need delay before reading from EC */
143 bool ec_delay;
144
145 /* Some MSI Wind netbooks (e.g. MSI Wind U100) need loading SCM to get
146 * some features working (e.g. ECO mode), but we cannot change
147 * Wlan/Bluetooth state in software and we can only read its state.
148 */
149 bool ec_read_only;
150};
151
152static struct quirk_entry *quirks;
153
128/* Hardware access */ 154/* Hardware access */
129 155
130static int set_lcd_level(int level) 156static int set_lcd_level(int level)
@@ -195,10 +221,13 @@ static ssize_t set_device_state(const char *buf, size_t count, u8 mask)
195 if (sscanf(buf, "%i", &status) != 1 || (status < 0 || status > 1)) 221 if (sscanf(buf, "%i", &status) != 1 || (status < 0 || status > 1))
196 return -EINVAL; 222 return -EINVAL;
197 223
224 if (quirks->ec_read_only)
225 return -EOPNOTSUPP;
226
198 /* read current device state */ 227 /* read current device state */
199 result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata); 228 result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
200 if (result < 0) 229 if (result < 0)
201 return -EINVAL; 230 return result;
202 231
203 if (!!(rdata & mask) != status) { 232 if (!!(rdata & mask) != status) {
204 /* reverse device bit */ 233 /* reverse device bit */
@@ -209,7 +238,7 @@ static ssize_t set_device_state(const char *buf, size_t count, u8 mask)
209 238
210 result = ec_write(MSI_STANDARD_EC_COMMAND_ADDRESS, wdata); 239 result = ec_write(MSI_STANDARD_EC_COMMAND_ADDRESS, wdata);
211 if (result < 0) 240 if (result < 0)
212 return -EINVAL; 241 return result;
213 } 242 }
214 243
215 return count; 244 return count;
@@ -222,7 +251,7 @@ static int get_wireless_state(int *wlan, int *bluetooth)
222 251
223 result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1); 252 result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1);
224 if (result < 0) 253 if (result < 0)
225 return -1; 254 return result;
226 255
227 if (wlan) 256 if (wlan)
228 *wlan = !!(rdata & 8); 257 *wlan = !!(rdata & 8);
@@ -240,7 +269,7 @@ static int get_wireless_state_ec_standard(void)
240 269
241 result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata); 270 result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
242 if (result < 0) 271 if (result < 0)
243 return -1; 272 return result;
244 273
245 wlan_s = !!(rdata & MSI_STANDARD_EC_WLAN_MASK); 274 wlan_s = !!(rdata & MSI_STANDARD_EC_WLAN_MASK);
246 275
@@ -258,7 +287,7 @@ static int get_threeg_exists(void)
258 287
259 result = ec_read(MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS, &rdata); 288 result = ec_read(MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS, &rdata);
260 if (result < 0) 289 if (result < 0)
261 return -1; 290 return result;
262 291
263 threeg_exists = !!(rdata & MSI_STANDARD_EC_3G_MASK); 292 threeg_exists = !!(rdata & MSI_STANDARD_EC_3G_MASK);
264 293
@@ -291,9 +320,9 @@ static ssize_t show_wlan(struct device *dev,
291 struct device_attribute *attr, char *buf) 320 struct device_attribute *attr, char *buf)
292{ 321{
293 322
294 int ret, enabled; 323 int ret, enabled = 0;
295 324
296 if (old_ec_model) { 325 if (quirks->old_ec_model) {
297 ret = get_wireless_state(&enabled, NULL); 326 ret = get_wireless_state(&enabled, NULL);
298 } else { 327 } else {
299 ret = get_wireless_state_ec_standard(); 328 ret = get_wireless_state_ec_standard();
@@ -315,9 +344,9 @@ static ssize_t show_bluetooth(struct device *dev,
315 struct device_attribute *attr, char *buf) 344 struct device_attribute *attr, char *buf)
316{ 345{
317 346
318 int ret, enabled; 347 int ret, enabled = 0;
319 348
320 if (old_ec_model) { 349 if (quirks->old_ec_model) {
321 ret = get_wireless_state(NULL, &enabled); 350 ret = get_wireless_state(NULL, &enabled);
322 } else { 351 } else {
323 ret = get_wireless_state_ec_standard(); 352 ret = get_wireless_state_ec_standard();
@@ -342,8 +371,8 @@ static ssize_t show_threeg(struct device *dev,
342 int ret; 371 int ret;
343 372
344 /* old msi ec not support 3G */ 373 /* old msi ec not support 3G */
345 if (old_ec_model) 374 if (quirks->old_ec_model)
346 return -1; 375 return -ENODEV;
347 376
348 ret = get_wireless_state_ec_standard(); 377 ret = get_wireless_state_ec_standard();
349 if (ret < 0) 378 if (ret < 0)
@@ -417,18 +446,119 @@ static ssize_t store_auto_brightness(struct device *dev,
417 return count; 446 return count;
418} 447}
419 448
449static ssize_t show_touchpad(struct device *dev,
450 struct device_attribute *attr, char *buf)
451{
452
453 u8 rdata;
454 int result;
455
456 result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
457 if (result < 0)
458 return result;
459
460 return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_TOUCHPAD_MASK));
461}
462
463static ssize_t show_turbo(struct device *dev,
464 struct device_attribute *attr, char *buf)
465{
466
467 u8 rdata;
468 int result;
469
470 result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
471 if (result < 0)
472 return result;
473
474 return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_TURBO_MASK));
475}
476
477static ssize_t show_eco(struct device *dev,
478 struct device_attribute *attr, char *buf)
479{
480
481 u8 rdata;
482 int result;
483
484 result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
485 if (result < 0)
486 return result;
487
488 return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_ECO_MASK));
489}
490
491static ssize_t show_turbo_cooldown(struct device *dev,
492 struct device_attribute *attr, char *buf)
493{
494
495 u8 rdata;
496 int result;
497
498 result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
499 if (result < 0)
500 return result;
501
502 return sprintf(buf, "%i\n", (!!(rdata & MSI_STANDARD_EC_TURBO_MASK)) |
503 (!!(rdata & MSI_STANDARD_EC_TURBO_COOLDOWN_MASK) << 1));
504}
505
506static ssize_t show_auto_fan(struct device *dev,
507 struct device_attribute *attr, char *buf)
508{
509
510 u8 rdata;
511 int result;
512
513 result = ec_read(MSI_STANDARD_EC_FAN_ADDRESS, &rdata);
514 if (result < 0)
515 return result;
516
517 return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_AUTOFAN_MASK));
518}
519
520static ssize_t store_auto_fan(struct device *dev,
521 struct device_attribute *attr, const char *buf, size_t count)
522{
523
524 int enable, result;
525
526 if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1)))
527 return -EINVAL;
528
529 result = ec_write(MSI_STANDARD_EC_FAN_ADDRESS, enable);
530 if (result < 0)
531 return result;
532
533 return count;
534}
535
420static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); 536static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
421static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, 537static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness,
422 store_auto_brightness); 538 store_auto_brightness);
423static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL); 539static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL);
424static DEVICE_ATTR(wlan, 0444, show_wlan, NULL); 540static DEVICE_ATTR(wlan, 0444, show_wlan, NULL);
425static DEVICE_ATTR(threeg, 0444, show_threeg, NULL); 541static DEVICE_ATTR(threeg, 0444, show_threeg, NULL);
542static DEVICE_ATTR(touchpad, 0444, show_touchpad, NULL);
543static DEVICE_ATTR(turbo_mode, 0444, show_turbo, NULL);
544static DEVICE_ATTR(eco_mode, 0444, show_eco, NULL);
545static DEVICE_ATTR(turbo_cooldown, 0444, show_turbo_cooldown, NULL);
546static DEVICE_ATTR(auto_fan, 0644, show_auto_fan, store_auto_fan);
426 547
427static struct attribute *msipf_attributes[] = { 548static struct attribute *msipf_attributes[] = {
428 &dev_attr_lcd_level.attr,
429 &dev_attr_auto_brightness.attr,
430 &dev_attr_bluetooth.attr, 549 &dev_attr_bluetooth.attr,
431 &dev_attr_wlan.attr, 550 &dev_attr_wlan.attr,
551 &dev_attr_touchpad.attr,
552 &dev_attr_turbo_mode.attr,
553 &dev_attr_eco_mode.attr,
554 &dev_attr_turbo_cooldown.attr,
555 &dev_attr_auto_fan.attr,
556 NULL
557};
558
559static struct attribute *msipf_old_attributes[] = {
560 &dev_attr_lcd_level.attr,
561 &dev_attr_auto_brightness.attr,
432 NULL 562 NULL
433}; 563};
434 564
@@ -436,6 +566,10 @@ static struct attribute_group msipf_attribute_group = {
436 .attrs = msipf_attributes 566 .attrs = msipf_attributes
437}; 567};
438 568
569static struct attribute_group msipf_old_attribute_group = {
570 .attrs = msipf_old_attributes
571};
572
439static struct platform_driver msipf_driver = { 573static struct platform_driver msipf_driver = {
440 .driver = { 574 .driver = {
441 .name = "msi-laptop-pf", 575 .name = "msi-laptop-pf",
@@ -448,9 +582,26 @@ static struct platform_device *msipf_device;
448 582
449/* Initialization */ 583/* Initialization */
450 584
451static int dmi_check_cb(const struct dmi_system_id *id) 585static struct quirk_entry quirk_old_ec_model = {
586 .old_ec_model = true,
587};
588
589static struct quirk_entry quirk_load_scm_model = {
590 .load_scm_model = true,
591 .ec_delay = true,
592};
593
594static struct quirk_entry quirk_load_scm_ro_model = {
595 .load_scm_model = true,
596 .ec_read_only = true,
597};
598
599static int dmi_check_cb(const struct dmi_system_id *dmi)
452{ 600{
453 pr_info("Identified laptop model '%s'\n", id->ident); 601 pr_info("Identified laptop model '%s'\n", dmi->ident);
602
603 quirks = dmi->driver_data;
604
454 return 1; 605 return 1;
455} 606}
456 607
@@ -464,6 +615,7 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {
464 DMI_MATCH(DMI_CHASSIS_VENDOR, 615 DMI_MATCH(DMI_CHASSIS_VENDOR,
465 "MICRO-STAR INT'L CO.,LTD") 616 "MICRO-STAR INT'L CO.,LTD")
466 }, 617 },
618 .driver_data = &quirk_old_ec_model,
467 .callback = dmi_check_cb 619 .callback = dmi_check_cb
468 }, 620 },
469 { 621 {
@@ -474,6 +626,7 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {
474 DMI_MATCH(DMI_PRODUCT_VERSION, "0581"), 626 DMI_MATCH(DMI_PRODUCT_VERSION, "0581"),
475 DMI_MATCH(DMI_BOARD_NAME, "MS-1058") 627 DMI_MATCH(DMI_BOARD_NAME, "MS-1058")
476 }, 628 },
629 .driver_data = &quirk_old_ec_model,
477 .callback = dmi_check_cb 630 .callback = dmi_check_cb
478 }, 631 },
479 { 632 {
@@ -484,6 +637,7 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {
484 DMI_MATCH(DMI_BOARD_VENDOR, "MSI"), 637 DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
485 DMI_MATCH(DMI_BOARD_NAME, "MS-1412") 638 DMI_MATCH(DMI_BOARD_NAME, "MS-1412")
486 }, 639 },
640 .driver_data = &quirk_old_ec_model,
487 .callback = dmi_check_cb 641 .callback = dmi_check_cb
488 }, 642 },
489 { 643 {
@@ -495,12 +649,9 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {
495 DMI_MATCH(DMI_CHASSIS_VENDOR, 649 DMI_MATCH(DMI_CHASSIS_VENDOR,
496 "MICRO-STAR INT'L CO.,LTD") 650 "MICRO-STAR INT'L CO.,LTD")
497 }, 651 },
652 .driver_data = &quirk_old_ec_model,
498 .callback = dmi_check_cb 653 .callback = dmi_check_cb
499 }, 654 },
500 { }
501};
502
503static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
504 { 655 {
505 .ident = "MSI N034", 656 .ident = "MSI N034",
506 .matches = { 657 .matches = {
@@ -510,6 +661,7 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
510 DMI_MATCH(DMI_CHASSIS_VENDOR, 661 DMI_MATCH(DMI_CHASSIS_VENDOR,
511 "MICRO-STAR INTERNATIONAL CO., LTD") 662 "MICRO-STAR INTERNATIONAL CO., LTD")
512 }, 663 },
664 .driver_data = &quirk_load_scm_model,
513 .callback = dmi_check_cb 665 .callback = dmi_check_cb
514 }, 666 },
515 { 667 {
@@ -521,6 +673,7 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
521 DMI_MATCH(DMI_CHASSIS_VENDOR, 673 DMI_MATCH(DMI_CHASSIS_VENDOR,
522 "MICRO-STAR INTERNATIONAL CO., LTD") 674 "MICRO-STAR INTERNATIONAL CO., LTD")
523 }, 675 },
676 .driver_data = &quirk_load_scm_model,
524 .callback = dmi_check_cb 677 .callback = dmi_check_cb
525 }, 678 },
526 { 679 {
@@ -530,6 +683,7 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
530 "MICRO-STAR INTERNATIONAL CO., LTD"), 683 "MICRO-STAR INTERNATIONAL CO., LTD"),
531 DMI_MATCH(DMI_PRODUCT_NAME, "MS-N014"), 684 DMI_MATCH(DMI_PRODUCT_NAME, "MS-N014"),
532 }, 685 },
686 .driver_data = &quirk_load_scm_model,
533 .callback = dmi_check_cb 687 .callback = dmi_check_cb
534 }, 688 },
535 { 689 {
@@ -539,6 +693,7 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
539 "Micro-Star International"), 693 "Micro-Star International"),
540 DMI_MATCH(DMI_PRODUCT_NAME, "CR620"), 694 DMI_MATCH(DMI_PRODUCT_NAME, "CR620"),
541 }, 695 },
696 .driver_data = &quirk_load_scm_model,
542 .callback = dmi_check_cb 697 .callback = dmi_check_cb
543 }, 698 },
544 { 699 {
@@ -548,6 +703,17 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
548 "Micro-Star International Co., Ltd."), 703 "Micro-Star International Co., Ltd."),
549 DMI_MATCH(DMI_PRODUCT_NAME, "U270 series"), 704 DMI_MATCH(DMI_PRODUCT_NAME, "U270 series"),
550 }, 705 },
706 .driver_data = &quirk_load_scm_model,
707 .callback = dmi_check_cb
708 },
709 {
710 .ident = "MSI U90/U100",
711 .matches = {
712 DMI_MATCH(DMI_SYS_VENDOR,
713 "MICRO-STAR INTERNATIONAL CO., LTD"),
714 DMI_MATCH(DMI_PRODUCT_NAME, "U90/U100"),
715 },
716 .driver_data = &quirk_load_scm_ro_model,
551 .callback = dmi_check_cb 717 .callback = dmi_check_cb
552 }, 718 },
553 { } 719 { }
@@ -560,32 +726,26 @@ static int rfkill_bluetooth_set(void *data, bool blocked)
560 * blocked == false is on 726 * blocked == false is on
561 * blocked == true is off 727 * blocked == true is off
562 */ 728 */
563 if (blocked) 729 int result = set_device_state(blocked ? "0" : "1", 0,
564 set_device_state("0", 0, MSI_STANDARD_EC_BLUETOOTH_MASK); 730 MSI_STANDARD_EC_BLUETOOTH_MASK);
565 else
566 set_device_state("1", 0, MSI_STANDARD_EC_BLUETOOTH_MASK);
567 731
568 return 0; 732 return min(result, 0);
569} 733}
570 734
571static int rfkill_wlan_set(void *data, bool blocked) 735static int rfkill_wlan_set(void *data, bool blocked)
572{ 736{
573 if (blocked) 737 int result = set_device_state(blocked ? "0" : "1", 0,
574 set_device_state("0", 0, MSI_STANDARD_EC_WLAN_MASK); 738 MSI_STANDARD_EC_WLAN_MASK);
575 else
576 set_device_state("1", 0, MSI_STANDARD_EC_WLAN_MASK);
577 739
578 return 0; 740 return min(result, 0);
579} 741}
580 742
581static int rfkill_threeg_set(void *data, bool blocked) 743static int rfkill_threeg_set(void *data, bool blocked)
582{ 744{
583 if (blocked) 745 int result = set_device_state(blocked ? "0" : "1", 0,
584 set_device_state("0", 0, MSI_STANDARD_EC_3G_MASK); 746 MSI_STANDARD_EC_3G_MASK);
585 else
586 set_device_state("1", 0, MSI_STANDARD_EC_3G_MASK);
587 747
588 return 0; 748 return min(result, 0);
589} 749}
590 750
591static const struct rfkill_ops rfkill_bluetooth_ops = { 751static const struct rfkill_ops rfkill_bluetooth_ops = {
@@ -618,25 +778,34 @@ static void rfkill_cleanup(void)
618 } 778 }
619} 779}
620 780
781static bool msi_rfkill_set_state(struct rfkill *rfkill, bool blocked)
782{
783 if (quirks->ec_read_only)
784 return rfkill_set_hw_state(rfkill, blocked);
785 else
786 return rfkill_set_sw_state(rfkill, blocked);
787}
788
621static void msi_update_rfkill(struct work_struct *ignored) 789static void msi_update_rfkill(struct work_struct *ignored)
622{ 790{
623 get_wireless_state_ec_standard(); 791 get_wireless_state_ec_standard();
624 792
625 if (rfk_wlan) 793 if (rfk_wlan)
626 rfkill_set_sw_state(rfk_wlan, !wlan_s); 794 msi_rfkill_set_state(rfk_wlan, !wlan_s);
627 if (rfk_bluetooth) 795 if (rfk_bluetooth)
628 rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s); 796 msi_rfkill_set_state(rfk_bluetooth, !bluetooth_s);
629 if (rfk_threeg) 797 if (rfk_threeg)
630 rfkill_set_sw_state(rfk_threeg, !threeg_s); 798 msi_rfkill_set_state(rfk_threeg, !threeg_s);
631} 799}
632static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill); 800static DECLARE_DELAYED_WORK(msi_rfkill_dwork, msi_update_rfkill);
801static DECLARE_WORK(msi_rfkill_work, msi_update_rfkill);
633 802
634static void msi_send_touchpad_key(struct work_struct *ignored) 803static void msi_send_touchpad_key(struct work_struct *ignored)
635{ 804{
636 u8 rdata; 805 u8 rdata;
637 int result; 806 int result;
638 807
639 result = ec_read(MSI_STANDARD_EC_TOUCHPAD_ADDRESS, &rdata); 808 result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
640 if (result < 0) 809 if (result < 0)
641 return; 810 return;
642 811
@@ -644,7 +813,8 @@ static void msi_send_touchpad_key(struct work_struct *ignored)
644 (rdata & MSI_STANDARD_EC_TOUCHPAD_MASK) ? 813 (rdata & MSI_STANDARD_EC_TOUCHPAD_MASK) ?
645 KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF, 1, true); 814 KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF, 1, true);
646} 815}
647static DECLARE_DELAYED_WORK(msi_touchpad_work, msi_send_touchpad_key); 816static DECLARE_DELAYED_WORK(msi_touchpad_dwork, msi_send_touchpad_key);
817static DECLARE_WORK(msi_touchpad_work, msi_send_touchpad_key);
648 818
649static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, 819static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
650 struct serio *port) 820 struct serio *port)
@@ -662,14 +832,20 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
662 extended = false; 832 extended = false;
663 switch (data) { 833 switch (data) {
664 case 0xE4: 834 case 0xE4:
665 schedule_delayed_work(&msi_touchpad_work, 835 if (quirks->ec_delay) {
666 round_jiffies_relative(0.5 * HZ)); 836 schedule_delayed_work(&msi_touchpad_dwork,
837 round_jiffies_relative(0.5 * HZ));
838 } else
839 schedule_work(&msi_touchpad_work);
667 break; 840 break;
668 case 0x54: 841 case 0x54:
669 case 0x62: 842 case 0x62:
670 case 0x76: 843 case 0x76:
671 schedule_delayed_work(&msi_rfkill_work, 844 if (quirks->ec_delay) {
672 round_jiffies_relative(0.5 * HZ)); 845 schedule_delayed_work(&msi_rfkill_dwork,
846 round_jiffies_relative(0.5 * HZ));
847 } else
848 schedule_work(&msi_rfkill_work);
673 break; 849 break;
674 } 850 }
675 } 851 }
@@ -736,8 +912,11 @@ static int rfkill_init(struct platform_device *sdev)
736 } 912 }
737 913
738 /* schedule to run rfkill state initial */ 914 /* schedule to run rfkill state initial */
739 schedule_delayed_work(&msi_rfkill_init, 915 if (quirks->ec_delay) {
740 round_jiffies_relative(1 * HZ)); 916 schedule_delayed_work(&msi_rfkill_init,
917 round_jiffies_relative(1 * HZ));
918 } else
919 schedule_work(&msi_rfkill_work);
741 920
742 return 0; 921 return 0;
743 922
@@ -761,7 +940,7 @@ static int msi_laptop_resume(struct device *device)
761 u8 data; 940 u8 data;
762 int result; 941 int result;
763 942
764 if (!load_scm_model) 943 if (!quirks->load_scm_model)
765 return 0; 944 return 0;
766 945
767 /* set load SCM to disable hardware control by fn key */ 946 /* set load SCM to disable hardware control by fn key */
@@ -819,13 +998,15 @@ static int __init load_scm_model_init(struct platform_device *sdev)
819 u8 data; 998 u8 data;
820 int result; 999 int result;
821 1000
822 /* allow userland write sysfs file */ 1001 if (!quirks->ec_read_only) {
823 dev_attr_bluetooth.store = store_bluetooth; 1002 /* allow userland write sysfs file */
824 dev_attr_wlan.store = store_wlan; 1003 dev_attr_bluetooth.store = store_bluetooth;
825 dev_attr_threeg.store = store_threeg; 1004 dev_attr_wlan.store = store_wlan;
826 dev_attr_bluetooth.attr.mode |= S_IWUSR; 1005 dev_attr_threeg.store = store_threeg;
827 dev_attr_wlan.attr.mode |= S_IWUSR; 1006 dev_attr_bluetooth.attr.mode |= S_IWUSR;
828 dev_attr_threeg.attr.mode |= S_IWUSR; 1007 dev_attr_wlan.attr.mode |= S_IWUSR;
1008 dev_attr_threeg.attr.mode |= S_IWUSR;
1009 }
829 1010
830 /* disable hardware control by fn key */ 1011 /* disable hardware control by fn key */
831 result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data); 1012 result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data);
@@ -874,21 +1055,22 @@ static int __init msi_init(void)
874 if (acpi_disabled) 1055 if (acpi_disabled)
875 return -ENODEV; 1056 return -ENODEV;
876 1057
877 if (force || dmi_check_system(msi_dmi_table)) 1058 dmi_check_system(msi_dmi_table);
878 old_ec_model = 1; 1059 if (!quirks)
1060 /* quirks may be NULL if no match in DMI table */
1061 quirks = &quirk_load_scm_model;
1062 if (force)
1063 quirks = &quirk_old_ec_model;
879 1064
880 if (!old_ec_model) 1065 if (!quirks->old_ec_model)
881 get_threeg_exists(); 1066 get_threeg_exists();
882 1067
883 if (!old_ec_model && dmi_check_system(msi_load_scm_models_dmi_table))
884 load_scm_model = 1;
885
886 if (auto_brightness < 0 || auto_brightness > 2) 1068 if (auto_brightness < 0 || auto_brightness > 2)
887 return -EINVAL; 1069 return -EINVAL;
888 1070
889 /* Register backlight stuff */ 1071 /* Register backlight stuff */
890 1072
891 if (acpi_video_backlight_support()) { 1073 if (!quirks->old_ec_model || acpi_video_backlight_support()) {
892 pr_info("Brightness ignored, must be controlled by ACPI video driver\n"); 1074 pr_info("Brightness ignored, must be controlled by ACPI video driver\n");
893 } else { 1075 } else {
894 struct backlight_properties props; 1076 struct backlight_properties props;
@@ -918,7 +1100,7 @@ static int __init msi_init(void)
918 if (ret) 1100 if (ret)
919 goto fail_platform_device1; 1101 goto fail_platform_device1;
920 1102
921 if (load_scm_model && (load_scm_model_init(msipf_device) < 0)) { 1103 if (quirks->load_scm_model && (load_scm_model_init(msipf_device) < 0)) {
922 ret = -EINVAL; 1104 ret = -EINVAL;
923 goto fail_platform_device1; 1105 goto fail_platform_device1;
924 } 1106 }
@@ -928,20 +1110,25 @@ static int __init msi_init(void)
928 if (ret) 1110 if (ret)
929 goto fail_platform_device2; 1111 goto fail_platform_device2;
930 1112
931 if (!old_ec_model) { 1113 if (!quirks->old_ec_model) {
932 if (threeg_exists) 1114 if (threeg_exists)
933 ret = device_create_file(&msipf_device->dev, 1115 ret = device_create_file(&msipf_device->dev,
934 &dev_attr_threeg); 1116 &dev_attr_threeg);
935 if (ret) 1117 if (ret)
936 goto fail_platform_device2; 1118 goto fail_platform_device2;
937 } 1119 } else {
1120 ret = sysfs_create_group(&msipf_device->dev.kobj,
1121 &msipf_old_attribute_group);
1122 if (ret)
1123 goto fail_platform_device2;
938 1124
939 /* Disable automatic brightness control by default because 1125 /* Disable automatic brightness control by default because
940 * this module was probably loaded to do brightness control in 1126 * this module was probably loaded to do brightness control in
941 * software. */ 1127 * software. */
942 1128
943 if (auto_brightness != 2) 1129 if (auto_brightness != 2)
944 set_auto_brightness(auto_brightness); 1130 set_auto_brightness(auto_brightness);
1131 }
945 1132
946 pr_info("driver " MSI_DRIVER_VERSION " successfully loaded\n"); 1133 pr_info("driver " MSI_DRIVER_VERSION " successfully loaded\n");
947 1134
@@ -949,9 +1136,10 @@ static int __init msi_init(void)
949 1136
950fail_platform_device2: 1137fail_platform_device2:
951 1138
952 if (load_scm_model) { 1139 if (quirks->load_scm_model) {
953 i8042_remove_filter(msi_laptop_i8042_filter); 1140 i8042_remove_filter(msi_laptop_i8042_filter);
954 cancel_delayed_work_sync(&msi_rfkill_work); 1141 cancel_delayed_work_sync(&msi_rfkill_dwork);
1142 cancel_work_sync(&msi_rfkill_work);
955 rfkill_cleanup(); 1143 rfkill_cleanup();
956 } 1144 }
957 platform_device_del(msipf_device); 1145 platform_device_del(msipf_device);
@@ -973,23 +1161,26 @@ fail_backlight:
973 1161
974static void __exit msi_cleanup(void) 1162static void __exit msi_cleanup(void)
975{ 1163{
976 if (load_scm_model) { 1164 if (quirks->load_scm_model) {
977 i8042_remove_filter(msi_laptop_i8042_filter); 1165 i8042_remove_filter(msi_laptop_i8042_filter);
978 msi_laptop_input_destroy(); 1166 msi_laptop_input_destroy();
979 cancel_delayed_work_sync(&msi_rfkill_work); 1167 cancel_delayed_work_sync(&msi_rfkill_dwork);
1168 cancel_work_sync(&msi_rfkill_work);
980 rfkill_cleanup(); 1169 rfkill_cleanup();
981 } 1170 }
982 1171
983 sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group); 1172 sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group);
984 if (!old_ec_model && threeg_exists) 1173 if (!quirks->old_ec_model && threeg_exists)
985 device_remove_file(&msipf_device->dev, &dev_attr_threeg); 1174 device_remove_file(&msipf_device->dev, &dev_attr_threeg);
986 platform_device_unregister(msipf_device); 1175 platform_device_unregister(msipf_device);
987 platform_driver_unregister(&msipf_driver); 1176 platform_driver_unregister(&msipf_driver);
988 backlight_device_unregister(msibl_device); 1177 backlight_device_unregister(msibl_device);
989 1178
990 /* Enable automatic brightness control again */ 1179 if (quirks->old_ec_model) {
991 if (auto_brightness != 2) 1180 /* Enable automatic brightness control again */
992 set_auto_brightness(1); 1181 if (auto_brightness != 2)
1182 set_auto_brightness(1);
1183 }
993 1184
994 pr_info("driver unloaded\n"); 1185 pr_info("driver unloaded\n");
995} 1186}
@@ -1011,3 +1202,4 @@ MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N051:*");
1011MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*"); 1202MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*");
1012MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*"); 1203MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*");
1013MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnU270series:*"); 1204MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnU270series:*");
1205MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnU90/U100:*");
diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c
index 2264331bd48e..70222f265f68 100644
--- a/drivers/platform/x86/msi-wmi.c
+++ b/drivers/platform/x86/msi-wmi.c
@@ -34,29 +34,65 @@ MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>");
34MODULE_DESCRIPTION("MSI laptop WMI hotkeys driver"); 34MODULE_DESCRIPTION("MSI laptop WMI hotkeys driver");
35MODULE_LICENSE("GPL"); 35MODULE_LICENSE("GPL");
36 36
37MODULE_ALIAS("wmi:551A1F84-FBDD-4125-91DB-3EA8F44F1D45");
38MODULE_ALIAS("wmi:B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2");
39
40#define DRV_NAME "msi-wmi" 37#define DRV_NAME "msi-wmi"
41 38
42#define MSIWMI_BIOS_GUID "551A1F84-FBDD-4125-91DB-3EA8F44F1D45" 39#define MSIWMI_BIOS_GUID "551A1F84-FBDD-4125-91DB-3EA8F44F1D45"
43#define MSIWMI_EVENT_GUID "B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2" 40#define MSIWMI_MSI_EVENT_GUID "B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2"
44 41#define MSIWMI_WIND_EVENT_GUID "5B3CC38A-40D9-7245-8AE6-1145B751BE3F"
45#define SCANCODE_BASE 0xD0 42
46#define MSI_WMI_BRIGHTNESSUP SCANCODE_BASE 43MODULE_ALIAS("wmi:" MSIWMI_BIOS_GUID);
47#define MSI_WMI_BRIGHTNESSDOWN (SCANCODE_BASE + 1) 44MODULE_ALIAS("wmi:" MSIWMI_MSI_EVENT_GUID);
48#define MSI_WMI_VOLUMEUP (SCANCODE_BASE + 2) 45MODULE_ALIAS("wmi:" MSIWMI_WIND_EVENT_GUID);
49#define MSI_WMI_VOLUMEDOWN (SCANCODE_BASE + 3) 46
50#define MSI_WMI_MUTE (SCANCODE_BASE + 4) 47enum msi_scancodes {
48 /* Generic MSI keys (not present on MSI Wind) */
49 MSI_KEY_BRIGHTNESSUP = 0xD0,
50 MSI_KEY_BRIGHTNESSDOWN,
51 MSI_KEY_VOLUMEUP,
52 MSI_KEY_VOLUMEDOWN,
53 MSI_KEY_MUTE,
54 /* MSI Wind keys */
55 WIND_KEY_TOUCHPAD = 0x08, /* Fn+F3 touchpad toggle */
56 WIND_KEY_BLUETOOTH = 0x56, /* Fn+F11 Bluetooth toggle */
57 WIND_KEY_CAMERA, /* Fn+F6 webcam toggle */
58 WIND_KEY_WLAN = 0x5f, /* Fn+F11 Wi-Fi toggle */
59 WIND_KEY_TURBO, /* Fn+F10 turbo mode toggle */
60 WIND_KEY_ECO = 0x69, /* Fn+F10 ECO mode toggle */
61};
51static struct key_entry msi_wmi_keymap[] = { 62static struct key_entry msi_wmi_keymap[] = {
52 { KE_KEY, MSI_WMI_BRIGHTNESSUP, {KEY_BRIGHTNESSUP} }, 63 { KE_KEY, MSI_KEY_BRIGHTNESSUP, {KEY_BRIGHTNESSUP} },
53 { KE_KEY, MSI_WMI_BRIGHTNESSDOWN, {KEY_BRIGHTNESSDOWN} }, 64 { KE_KEY, MSI_KEY_BRIGHTNESSDOWN, {KEY_BRIGHTNESSDOWN} },
54 { KE_KEY, MSI_WMI_VOLUMEUP, {KEY_VOLUMEUP} }, 65 { KE_KEY, MSI_KEY_VOLUMEUP, {KEY_VOLUMEUP} },
55 { KE_KEY, MSI_WMI_VOLUMEDOWN, {KEY_VOLUMEDOWN} }, 66 { KE_KEY, MSI_KEY_VOLUMEDOWN, {KEY_VOLUMEDOWN} },
56 { KE_KEY, MSI_WMI_MUTE, {KEY_MUTE} }, 67 { KE_KEY, MSI_KEY_MUTE, {KEY_MUTE} },
57 { KE_END, 0} 68
69 /* These keys work without WMI. Ignore them to avoid double keycodes */
70 { KE_IGNORE, WIND_KEY_TOUCHPAD, {KEY_TOUCHPAD_TOGGLE} },
71 { KE_IGNORE, WIND_KEY_BLUETOOTH, {KEY_BLUETOOTH} },
72 { KE_IGNORE, WIND_KEY_CAMERA, {KEY_CAMERA} },
73 { KE_IGNORE, WIND_KEY_WLAN, {KEY_WLAN} },
74
75 /* These are unknown WMI events found on MSI Wind */
76 { KE_IGNORE, 0x00 },
77 { KE_IGNORE, 0x62 },
78 { KE_IGNORE, 0x63 },
79
80 /* These are MSI Wind keys that should be handled via WMI */
81 { KE_KEY, WIND_KEY_TURBO, {KEY_PROG1} },
82 { KE_KEY, WIND_KEY_ECO, {KEY_PROG2} },
83
84 { KE_END, 0 }
85};
86
87static ktime_t last_pressed;
88
89static const struct {
90 const char *guid;
91 bool quirk_last_pressed;
92} *event_wmi, event_wmis[] = {
93 { MSIWMI_MSI_EVENT_GUID, true },
94 { MSIWMI_WIND_EVENT_GUID, false },
58}; 95};
59static ktime_t last_pressed[ARRAY_SIZE(msi_wmi_keymap) - 1];
60 96
61static struct backlight_device *backlight; 97static struct backlight_device *backlight;
62 98
@@ -149,7 +185,6 @@ static void msi_wmi_notify(u32 value, void *context)
149 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; 185 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
150 static struct key_entry *key; 186 static struct key_entry *key;
151 union acpi_object *obj; 187 union acpi_object *obj;
152 ktime_t cur;
153 acpi_status status; 188 acpi_status status;
154 189
155 status = wmi_get_event_data(value, &response); 190 status = wmi_get_event_data(value, &response);
@@ -165,39 +200,67 @@ static void msi_wmi_notify(u32 value, void *context)
165 pr_debug("Eventcode: 0x%x\n", eventcode); 200 pr_debug("Eventcode: 0x%x\n", eventcode);
166 key = sparse_keymap_entry_from_scancode(msi_wmi_input_dev, 201 key = sparse_keymap_entry_from_scancode(msi_wmi_input_dev,
167 eventcode); 202 eventcode);
168 if (key) { 203 if (!key) {
169 ktime_t diff; 204 pr_info("Unknown key pressed - %x\n", eventcode);
170 cur = ktime_get_real(); 205 goto msi_wmi_notify_exit;
171 diff = ktime_sub(cur, last_pressed[key->code - 206 }
172 SCANCODE_BASE]); 207
173 /* Ignore event if the same event happened in a 50 ms 208 if (event_wmi->quirk_last_pressed) {
209 ktime_t cur = ktime_get_real();
210 ktime_t diff = ktime_sub(cur, last_pressed);
211 /* Ignore event if any event happened in a 50 ms
174 timeframe -> Key press may result in 10-20 GPEs */ 212 timeframe -> Key press may result in 10-20 GPEs */
175 if (ktime_to_us(diff) < 1000 * 50) { 213 if (ktime_to_us(diff) < 1000 * 50) {
176 pr_debug("Suppressed key event 0x%X - " 214 pr_debug("Suppressed key event 0x%X - "
177 "Last press was %lld us ago\n", 215 "Last press was %lld us ago\n",
178 key->code, ktime_to_us(diff)); 216 key->code, ktime_to_us(diff));
179 return; 217 goto msi_wmi_notify_exit;
180 }
181 last_pressed[key->code - SCANCODE_BASE] = cur;
182
183 if (key->type == KE_KEY &&
184 /* Brightness is served via acpi video driver */
185 (!acpi_video_backlight_support() ||
186 (key->code != MSI_WMI_BRIGHTNESSUP &&
187 key->code != MSI_WMI_BRIGHTNESSDOWN))) {
188 pr_debug("Send key: 0x%X - "
189 "Input layer keycode: %d\n",
190 key->code, key->keycode);
191 sparse_keymap_report_entry(msi_wmi_input_dev,
192 key, 1, true);
193 } 218 }
194 } else 219 last_pressed = cur;
195 pr_info("Unknown key pressed - %x\n", eventcode); 220 }
221
222 if (key->type == KE_KEY &&
223 /* Brightness is served via acpi video driver */
224 (backlight ||
225 (key->code != MSI_KEY_BRIGHTNESSUP &&
226 key->code != MSI_KEY_BRIGHTNESSDOWN))) {
227 pr_debug("Send key: 0x%X - Input layer keycode: %d\n",
228 key->code, key->keycode);
229 sparse_keymap_report_entry(msi_wmi_input_dev, key, 1,
230 true);
231 }
196 } else 232 } else
197 pr_info("Unknown event received\n"); 233 pr_info("Unknown event received\n");
234
235msi_wmi_notify_exit:
198 kfree(response.pointer); 236 kfree(response.pointer);
199} 237}
200 238
239static int __init msi_wmi_backlight_setup(void)
240{
241 int err;
242 struct backlight_properties props;
243
244 memset(&props, 0, sizeof(struct backlight_properties));
245 props.type = BACKLIGHT_PLATFORM;
246 props.max_brightness = ARRAY_SIZE(backlight_map) - 1;
247 backlight = backlight_device_register(DRV_NAME, NULL, NULL,
248 &msi_backlight_ops,
249 &props);
250 if (IS_ERR(backlight))
251 return PTR_ERR(backlight);
252
253 err = bl_get(NULL);
254 if (err < 0) {
255 backlight_device_unregister(backlight);
256 return err;
257 }
258
259 backlight->props.brightness = err;
260
261 return 0;
262}
263
201static int __init msi_wmi_input_setup(void) 264static int __init msi_wmi_input_setup(void)
202{ 265{
203 int err; 266 int err;
@@ -219,7 +282,7 @@ static int __init msi_wmi_input_setup(void)
219 if (err) 282 if (err)
220 goto err_free_keymap; 283 goto err_free_keymap;
221 284
222 memset(last_pressed, 0, sizeof(last_pressed)); 285 last_pressed = ktime_set(0, 0);
223 286
224 return 0; 287 return 0;
225 288
@@ -233,61 +296,66 @@ err_free_dev:
233static int __init msi_wmi_init(void) 296static int __init msi_wmi_init(void)
234{ 297{
235 int err; 298 int err;
299 int i;
236 300
237 if (!wmi_has_guid(MSIWMI_EVENT_GUID)) { 301 for (i = 0; i < ARRAY_SIZE(event_wmis); i++) {
238 pr_err("This machine doesn't have MSI-hotkeys through WMI\n"); 302 if (!wmi_has_guid(event_wmis[i].guid))
239 return -ENODEV; 303 continue;
240 }
241 err = wmi_install_notify_handler(MSIWMI_EVENT_GUID,
242 msi_wmi_notify, NULL);
243 if (ACPI_FAILURE(err))
244 return -EINVAL;
245 304
246 err = msi_wmi_input_setup(); 305 err = msi_wmi_input_setup();
247 if (err) 306 if (err) {
248 goto err_uninstall_notifier; 307 pr_err("Unable to setup input device\n");
249 308 return err;
250 if (!acpi_video_backlight_support()) { 309 }
251 struct backlight_properties props; 310
252 memset(&props, 0, sizeof(struct backlight_properties)); 311 err = wmi_install_notify_handler(event_wmis[i].guid,
253 props.type = BACKLIGHT_PLATFORM; 312 msi_wmi_notify, NULL);
254 props.max_brightness = ARRAY_SIZE(backlight_map) - 1; 313 if (ACPI_FAILURE(err)) {
255 backlight = backlight_device_register(DRV_NAME, NULL, NULL, 314 pr_err("Unable to setup WMI notify handler\n");
256 &msi_backlight_ops,
257 &props);
258 if (IS_ERR(backlight)) {
259 err = PTR_ERR(backlight);
260 goto err_free_input; 315 goto err_free_input;
261 } 316 }
262 317
263 err = bl_get(NULL); 318 pr_debug("Event handler installed\n");
264 if (err < 0) 319 event_wmi = &event_wmis[i];
265 goto err_free_backlight; 320 break;
321 }
266 322
267 backlight->props.brightness = err; 323 if (wmi_has_guid(MSIWMI_BIOS_GUID) && !acpi_video_backlight_support()) {
324 err = msi_wmi_backlight_setup();
325 if (err) {
326 pr_err("Unable to setup backlight device\n");
327 goto err_uninstall_handler;
328 }
329 pr_debug("Backlight device created\n");
330 }
331
332 if (!event_wmi && !backlight) {
333 pr_err("This machine doesn't have neither MSI-hotkeys nor backlight through WMI\n");
334 return -ENODEV;
268 } 335 }
269 pr_debug("Event handler installed\n");
270 336
271 return 0; 337 return 0;
272 338
273err_free_backlight: 339err_uninstall_handler:
274 backlight_device_unregister(backlight); 340 if (event_wmi)
341 wmi_remove_notify_handler(event_wmi->guid);
275err_free_input: 342err_free_input:
276 sparse_keymap_free(msi_wmi_input_dev); 343 if (event_wmi) {
277 input_unregister_device(msi_wmi_input_dev); 344 sparse_keymap_free(msi_wmi_input_dev);
278err_uninstall_notifier: 345 input_unregister_device(msi_wmi_input_dev);
279 wmi_remove_notify_handler(MSIWMI_EVENT_GUID); 346 }
280 return err; 347 return err;
281} 348}
282 349
283static void __exit msi_wmi_exit(void) 350static void __exit msi_wmi_exit(void)
284{ 351{
285 if (wmi_has_guid(MSIWMI_EVENT_GUID)) { 352 if (event_wmi) {
286 wmi_remove_notify_handler(MSIWMI_EVENT_GUID); 353 wmi_remove_notify_handler(event_wmi->guid);
287 sparse_keymap_free(msi_wmi_input_dev); 354 sparse_keymap_free(msi_wmi_input_dev);
288 input_unregister_device(msi_wmi_input_dev); 355 input_unregister_device(msi_wmi_input_dev);
289 backlight_device_unregister(backlight);
290 } 356 }
357 if (backlight)
358 backlight_device_unregister(backlight);
291} 359}
292 360
293module_init(msi_wmi_init); 361module_init(msi_wmi_init);
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
index 8da21876a794..14d4dced1def 100644
--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -158,6 +158,11 @@ static void sony_nc_thermal_cleanup(struct platform_device *pd);
158static int sony_nc_lid_resume_setup(struct platform_device *pd); 158static int sony_nc_lid_resume_setup(struct platform_device *pd);
159static void sony_nc_lid_resume_cleanup(struct platform_device *pd); 159static void sony_nc_lid_resume_cleanup(struct platform_device *pd);
160 160
161static int sony_nc_gfx_switch_setup(struct platform_device *pd,
162 unsigned int handle);
163static void sony_nc_gfx_switch_cleanup(struct platform_device *pd);
164static int __sony_nc_gfx_switch_status_get(void);
165
161static int sony_nc_highspeed_charging_setup(struct platform_device *pd); 166static int sony_nc_highspeed_charging_setup(struct platform_device *pd);
162static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd); 167static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd);
163 168
@@ -1241,17 +1246,13 @@ static void sony_nc_notify(struct acpi_device *device, u32 event)
1241 /* Hybrid GFX switching */ 1246 /* Hybrid GFX switching */
1242 sony_call_snc_handle(handle, 0x0000, &result); 1247 sony_call_snc_handle(handle, 0x0000, &result);
1243 dprintk("GFX switch event received (reason: %s)\n", 1248 dprintk("GFX switch event received (reason: %s)\n",
1244 (result & 0x01) ? 1249 (result == 0x1) ? "switch change" :
1245 "switch change" : "unknown"); 1250 (result == 0x2) ? "output switch" :
1246 1251 (result == 0x3) ? "output switch" :
1247 /* verify the switch state 1252 "");
1248 * 1: discrete GFX
1249 * 0: integrated GFX
1250 */
1251 sony_call_snc_handle(handle, 0x0100, &result);
1252 1253
1253 ev_type = GFX_SWITCH; 1254 ev_type = GFX_SWITCH;
1254 real_ev = result & 0xff; 1255 real_ev = __sony_nc_gfx_switch_status_get();
1255 break; 1256 break;
1256 1257
1257 default: 1258 default:
@@ -1350,6 +1351,13 @@ static void sony_nc_function_setup(struct acpi_device *device,
1350 pr_err("couldn't set up thermal profile function (%d)\n", 1351 pr_err("couldn't set up thermal profile function (%d)\n",
1351 result); 1352 result);
1352 break; 1353 break;
1354 case 0x0128:
1355 case 0x0146:
1356 result = sony_nc_gfx_switch_setup(pf_device, handle);
1357 if (result)
1358 pr_err("couldn't set up GFX Switch status (%d)\n",
1359 result);
1360 break;
1353 case 0x0131: 1361 case 0x0131:
1354 result = sony_nc_highspeed_charging_setup(pf_device); 1362 result = sony_nc_highspeed_charging_setup(pf_device);
1355 if (result) 1363 if (result)
@@ -1365,6 +1373,8 @@ static void sony_nc_function_setup(struct acpi_device *device,
1365 break; 1373 break;
1366 case 0x0137: 1374 case 0x0137:
1367 case 0x0143: 1375 case 0x0143:
1376 case 0x014b:
1377 case 0x014c:
1368 result = sony_nc_kbd_backlight_setup(pf_device, handle); 1378 result = sony_nc_kbd_backlight_setup(pf_device, handle);
1369 if (result) 1379 if (result)
1370 pr_err("couldn't set up keyboard backlight function (%d)\n", 1380 pr_err("couldn't set up keyboard backlight function (%d)\n",
@@ -1414,6 +1424,10 @@ static void sony_nc_function_cleanup(struct platform_device *pd)
1414 case 0x0122: 1424 case 0x0122:
1415 sony_nc_thermal_cleanup(pd); 1425 sony_nc_thermal_cleanup(pd);
1416 break; 1426 break;
1427 case 0x0128:
1428 case 0x0146:
1429 sony_nc_gfx_switch_cleanup(pd);
1430 break;
1417 case 0x0131: 1431 case 0x0131:
1418 sony_nc_highspeed_charging_cleanup(pd); 1432 sony_nc_highspeed_charging_cleanup(pd);
1419 break; 1433 break;
@@ -1423,6 +1437,8 @@ static void sony_nc_function_cleanup(struct platform_device *pd)
1423 break; 1437 break;
1424 case 0x0137: 1438 case 0x0137:
1425 case 0x0143: 1439 case 0x0143:
1440 case 0x014b:
1441 case 0x014c:
1426 sony_nc_kbd_backlight_cleanup(pd); 1442 sony_nc_kbd_backlight_cleanup(pd);
1427 break; 1443 break;
1428 default: 1444 default:
@@ -1467,6 +1483,8 @@ static void sony_nc_function_resume(void)
1467 break; 1483 break;
1468 case 0x0137: 1484 case 0x0137:
1469 case 0x0143: 1485 case 0x0143:
1486 case 0x014b:
1487 case 0x014c:
1470 sony_nc_kbd_backlight_resume(); 1488 sony_nc_kbd_backlight_resume();
1471 break; 1489 break;
1472 default: 1490 default:
@@ -1534,7 +1552,7 @@ static int sony_nc_rfkill_set(void *data, bool blocked)
1534 int argument = sony_rfkill_address[(long) data] + 0x100; 1552 int argument = sony_rfkill_address[(long) data] + 0x100;
1535 1553
1536 if (!blocked) 1554 if (!blocked)
1537 argument |= 0x030000; 1555 argument |= 0x070000;
1538 1556
1539 return sony_call_snc_handle(sony_rfkill_handle, argument, &result); 1557 return sony_call_snc_handle(sony_rfkill_handle, argument, &result);
1540} 1558}
@@ -2333,7 +2351,7 @@ static int sony_nc_lid_resume_setup(struct platform_device *pd)
2333 return 0; 2351 return 0;
2334 2352
2335liderror: 2353liderror:
2336 for (; i > 0; i--) 2354 for (i--; i >= 0; i--)
2337 device_remove_file(&pd->dev, &lid_ctl->attrs[i]); 2355 device_remove_file(&pd->dev, &lid_ctl->attrs[i]);
2338 2356
2339 kfree(lid_ctl); 2357 kfree(lid_ctl);
@@ -2355,6 +2373,97 @@ static void sony_nc_lid_resume_cleanup(struct platform_device *pd)
2355 } 2373 }
2356} 2374}
2357 2375
2376/* GFX Switch position */
2377enum gfx_switch {
2378 SPEED,
2379 STAMINA,
2380 AUTO
2381};
2382struct snc_gfx_switch_control {
2383 struct device_attribute attr;
2384 unsigned int handle;
2385};
2386static struct snc_gfx_switch_control *gfxs_ctl;
2387
2388/* returns 0 for speed, 1 for stamina */
2389static int __sony_nc_gfx_switch_status_get(void)
2390{
2391 unsigned int result;
2392
2393 if (sony_call_snc_handle(gfxs_ctl->handle, 0x0100, &result))
2394 return -EIO;
2395
2396 switch (gfxs_ctl->handle) {
2397 case 0x0146:
2398 /* 1: discrete GFX (speed)
2399 * 0: integrated GFX (stamina)
2400 */
2401 return result & 0x1 ? SPEED : STAMINA;
2402 break;
2403 case 0x0128:
2404 /* it's a more elaborated bitmask, for now:
2405 * 2: integrated GFX (stamina)
2406 * 0: discrete GFX (speed)
2407 */
2408 dprintk("GFX Status: 0x%x\n", result);
2409 return result & 0x80 ? AUTO :
2410 result & 0x02 ? STAMINA : SPEED;
2411 break;
2412 }
2413 return -EINVAL;
2414}
2415
2416static ssize_t sony_nc_gfx_switch_status_show(struct device *dev,
2417 struct device_attribute *attr,
2418 char *buffer)
2419{
2420 int pos = __sony_nc_gfx_switch_status_get();
2421
2422 if (pos < 0)
2423 return pos;
2424
2425 return snprintf(buffer, PAGE_SIZE, "%s\n", pos ? "speed" : "stamina");
2426}
2427
2428static int sony_nc_gfx_switch_setup(struct platform_device *pd,
2429 unsigned int handle)
2430{
2431 unsigned int result;
2432
2433 gfxs_ctl = kzalloc(sizeof(struct snc_gfx_switch_control), GFP_KERNEL);
2434 if (!gfxs_ctl)
2435 return -ENOMEM;
2436
2437 gfxs_ctl->handle = handle;
2438
2439 sysfs_attr_init(&gfxs_ctl->attr.attr);
2440 gfxs_ctl->attr.attr.name = "gfx_switch_status";
2441 gfxs_ctl->attr.attr.mode = S_IRUGO;
2442 gfxs_ctl->attr.show = sony_nc_gfx_switch_status_show;
2443
2444 result = device_create_file(&pd->dev, &gfxs_ctl->attr);
2445 if (result)
2446 goto gfxerror;
2447
2448 return 0;
2449
2450gfxerror:
2451 kfree(gfxs_ctl);
2452 gfxs_ctl = NULL;
2453
2454 return result;
2455}
2456
2457static void sony_nc_gfx_switch_cleanup(struct platform_device *pd)
2458{
2459 if (gfxs_ctl) {
2460 device_remove_file(&pd->dev, &gfxs_ctl->attr);
2461
2462 kfree(gfxs_ctl);
2463 gfxs_ctl = NULL;
2464 }
2465}
2466
2358/* High speed charging function */ 2467/* High speed charging function */
2359static struct device_attribute *hsc_handle; 2468static struct device_attribute *hsc_handle;
2360 2469
@@ -2533,6 +2642,8 @@ static void sony_nc_backlight_ng_read_limits(int handle,
2533 lvl_table_len = 9; 2642 lvl_table_len = 9;
2534 break; 2643 break;
2535 case 0x143: 2644 case 0x143:
2645 case 0x14b:
2646 case 0x14c:
2536 lvl_table_len = 16; 2647 lvl_table_len = 16;
2537 break; 2648 break;
2538 } 2649 }
@@ -2584,6 +2695,18 @@ static void sony_nc_backlight_setup(void)
2584 sony_nc_backlight_ng_read_limits(0x143, &sony_bl_props); 2695 sony_nc_backlight_ng_read_limits(0x143, &sony_bl_props);
2585 max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset; 2696 max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;
2586 2697
2698 } else if (sony_find_snc_handle(0x14b) >= 0) {
2699 ops = &sony_backlight_ng_ops;
2700 sony_bl_props.cmd_base = 0x3000;
2701 sony_nc_backlight_ng_read_limits(0x14b, &sony_bl_props);
2702 max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;
2703
2704 } else if (sony_find_snc_handle(0x14c) >= 0) {
2705 ops = &sony_backlight_ng_ops;
2706 sony_bl_props.cmd_base = 0x3000;
2707 sony_nc_backlight_ng_read_limits(0x14c, &sony_bl_props);
2708 max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;
2709
2587 } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", 2710 } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT",
2588 &unused))) { 2711 &unused))) {
2589 ops = &sony_backlight_ops; 2712 ops = &sony_backlight_ops;
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index f4f8408f3b5b..9a907567f41e 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -209,9 +209,8 @@ enum tpacpi_hkey_event_t {
209 TP_HKEY_EV_ALARM_SENSOR_XHOT = 0x6022, /* sensor critically hot */ 209 TP_HKEY_EV_ALARM_SENSOR_XHOT = 0x6022, /* sensor critically hot */
210 TP_HKEY_EV_THM_TABLE_CHANGED = 0x6030, /* thermal table changed */ 210 TP_HKEY_EV_THM_TABLE_CHANGED = 0x6030, /* thermal table changed */
211 211
212 TP_HKEY_EV_UNK_6040 = 0x6040, /* Related to AC change? 212 /* AC-related events */
213 some sort of APM hint, 213 TP_HKEY_EV_AC_CHANGED = 0x6040, /* AC status changed */
214 W520 */
215 214
216 /* Misc */ 215 /* Misc */
217 TP_HKEY_EV_RFKILL_CHANGED = 0x7000, /* rfkill switch changed */ 216 TP_HKEY_EV_RFKILL_CHANGED = 0x7000, /* rfkill switch changed */
@@ -3629,6 +3628,12 @@ static bool hotkey_notify_6xxx(const u32 hkey,
3629 "a sensor reports something is extremely hot!\n"); 3628 "a sensor reports something is extremely hot!\n");
3630 /* recommended action: immediate sleep/hibernate */ 3629 /* recommended action: immediate sleep/hibernate */
3631 break; 3630 break;
3631 case TP_HKEY_EV_AC_CHANGED:
3632 /* X120e, X121e, X220, X220i, X220t, X230, T420, T420s, W520:
3633 * AC status changed; can be triggered by plugging or
3634 * unplugging AC adapter, docking or undocking. */
3635
3636 /* fallthrough */
3632 3637
3633 case TP_HKEY_EV_KEY_NUMLOCK: 3638 case TP_HKEY_EV_KEY_NUMLOCK:
3634 case TP_HKEY_EV_KEY_FN: 3639 case TP_HKEY_EV_KEY_FN:
@@ -8574,7 +8579,8 @@ static bool __pure __init tpacpi_is_valid_fw_id(const char* const s,
8574 return s && strlen(s) >= 8 && 8579 return s && strlen(s) >= 8 &&
8575 tpacpi_is_fw_digit(s[0]) && 8580 tpacpi_is_fw_digit(s[0]) &&
8576 tpacpi_is_fw_digit(s[1]) && 8581 tpacpi_is_fw_digit(s[1]) &&
8577 s[2] == t && s[3] == 'T' && 8582 s[2] == t &&
8583 (s[3] == 'T' || s[3] == 'N') &&
8578 tpacpi_is_fw_digit(s[4]) && 8584 tpacpi_is_fw_digit(s[4]) &&
8579 tpacpi_is_fw_digit(s[5]); 8585 tpacpi_is_fw_digit(s[5]);
8580} 8586}
@@ -8607,7 +8613,8 @@ static int __must_check __init get_thinkpad_model_data(
8607 return -ENOMEM; 8613 return -ENOMEM;
8608 8614
8609 /* Really ancient ThinkPad 240X will fail this, which is fine */ 8615 /* Really ancient ThinkPad 240X will fail this, which is fine */
8610 if (!tpacpi_is_valid_fw_id(tp->bios_version_str, 'E')) 8616 if (!(tpacpi_is_valid_fw_id(tp->bios_version_str, 'E') ||
8617 tpacpi_is_valid_fw_id(tp->bios_version_str, 'C')))
8611 return 0; 8618 return 0;
8612 8619
8613 tp->bios_model = tp->bios_version_str[0] 8620 tp->bios_model = tp->bios_version_str[0]