diff options
-rw-r--r-- | Documentation/ABI/testing/sysfs-platform-msi-laptop | 83 | ||||
-rw-r--r-- | drivers/platform/x86/Kconfig | 15 | ||||
-rw-r--r-- | drivers/platform/x86/Makefile | 1 | ||||
-rw-r--r-- | drivers/platform/x86/acer-wmi.c | 21 | ||||
-rw-r--r-- | drivers/platform/x86/asus-laptop.c | 85 | ||||
-rw-r--r-- | drivers/platform/x86/asus-nb-wmi.c | 76 | ||||
-rw-r--r-- | drivers/platform/x86/asus-wmi.c | 108 | ||||
-rw-r--r-- | drivers/platform/x86/asus-wmi.h | 9 | ||||
-rw-r--r-- | drivers/platform/x86/chromeos_laptop.c | 371 | ||||
-rw-r--r-- | drivers/platform/x86/eeepc-wmi.c | 2 | ||||
-rw-r--r-- | drivers/platform/x86/hp-wmi.c | 117 | ||||
-rw-r--r-- | drivers/platform/x86/msi-laptop.c | 374 | ||||
-rw-r--r-- | drivers/platform/x86/msi-wmi.c | 224 | ||||
-rw-r--r-- | drivers/platform/x86/sony-laptop.c | 145 | ||||
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 17 |
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 @@ | |||
1 | What: /sys/devices/platform/msi-laptop-pf/lcd_level | ||
2 | Date: Oct 2006 | ||
3 | KernelVersion: 2.6.19 | ||
4 | Contact: "Lennart Poettering <mzxreary@0pointer.de>" | ||
5 | Description: | ||
6 | Screen brightness: contains a single integer in the range 0..8. | ||
7 | |||
8 | What: /sys/devices/platform/msi-laptop-pf/auto_brightness | ||
9 | Date: Oct 2006 | ||
10 | KernelVersion: 2.6.19 | ||
11 | Contact: "Lennart Poettering <mzxreary@0pointer.de>" | ||
12 | Description: | ||
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 | |||
17 | What: /sys/devices/platform/msi-laptop-pf/wlan | ||
18 | Date: Oct 2006 | ||
19 | KernelVersion: 2.6.19 | ||
20 | Contact: "Lennart Poettering <mzxreary@0pointer.de>" | ||
21 | Description: | ||
22 | WLAN subsystem enabled: contains either 0 or 1. | ||
23 | |||
24 | What: /sys/devices/platform/msi-laptop-pf/bluetooth | ||
25 | Date: Oct 2006 | ||
26 | KernelVersion: 2.6.19 | ||
27 | Contact: "Lennart Poettering <mzxreary@0pointer.de>" | ||
28 | Description: | ||
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 | |||
33 | What: /sys/devices/platform/msi-laptop-pf/touchpad | ||
34 | Date: Nov 2012 | ||
35 | KernelVersion: 3.8 | ||
36 | Contact: "Maxim Mikityanskiy <maxtram95@gmail.com>" | ||
37 | Description: | ||
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 | |||
41 | What: /sys/devices/platform/msi-laptop-pf/turbo_mode | ||
42 | Date: Nov 2012 | ||
43 | KernelVersion: 3.8 | ||
44 | Contact: "Maxim Mikityanskiy <maxtram95@gmail.com>" | ||
45 | Description: | ||
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 | |||
54 | What: /sys/devices/platform/msi-laptop-pf/eco_mode | ||
55 | Date: Nov 2012 | ||
56 | KernelVersion: 3.8 | ||
57 | Contact: "Maxim Mikityanskiy <maxtram95@gmail.com>" | ||
58 | Description: | ||
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 | |||
64 | What: /sys/devices/platform/msi-laptop-pf/turbo_cooldown | ||
65 | Date: Nov 2012 | ||
66 | KernelVersion: 3.8 | ||
67 | Contact: "Maxim Mikityanskiy <maxtram95@gmail.com>" | ||
68 | Description: | ||
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 | |||
75 | What: /sys/devices/platform/msi-laptop-pf/auto_fan | ||
76 | Date: Nov 2012 | ||
77 | KernelVersion: 3.8 | ||
78 | Contact: "Maxim Mikityanskiy <maxtram95@gmail.com>" | ||
79 | Description: | ||
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 | ||
82 | config 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 | |||
82 | config DELL_LAPTOP | 93 | config 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 | ||
295 | config THINKPAD_ACPI | 308 | config 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 | |||
50 | obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o | 50 | obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o |
51 | obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o | 51 | obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o |
52 | obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o | 52 | obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o |
53 | obj-$(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 | */ | ||
67 | static struct quirk_entry quirk_asus_x55u = { | ||
68 | .wapf = 4, | ||
69 | .wmi_backlight_power = true, | ||
70 | .no_display_toggle = true, | ||
71 | }; | ||
72 | |||
62 | static struct quirk_entry quirk_asus_x401u = { | 73 | static 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 | ||
163 | static const struct key_entry asus_nb_wmi_keymap[] = { | 192 | static 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 | ||
462 | static 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 | |||
471 | static 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 | |||
480 | static 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 | |||
491 | static 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 | |||
502 | static 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 | |||
459 | static void asus_wmi_led_exit(struct asus_wmi *asus) | 513 | static 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 | ||
503 | error: | 576 | error: |
@@ -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 | ||
1344 | static 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 | |||
1268 | static void asus_wmi_notify(u32 value, void *context) | 1356 | static 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 | ||
1313 | exit: | 1409 | exit: |
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 | ||
34 | struct module; | 36 | struct module; |
35 | struct key_entry; | 37 | struct 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 | ||
46 | struct asus_wmi_driver { | 55 | struct 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 | |||
36 | static struct i2c_client *als; | ||
37 | static struct i2c_client *tp; | ||
38 | static struct i2c_client *ts; | ||
39 | |||
40 | const 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 */ | ||
47 | enum i2c_adapter_type { | ||
48 | I2C_ADAPTER_SMBUS = 0, | ||
49 | I2C_ADAPTER_VGADDC, | ||
50 | I2C_ADAPTER_PANEL, | ||
51 | }; | ||
52 | |||
53 | static struct i2c_board_info __initdata cyapa_device = { | ||
54 | I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), | ||
55 | .flags = I2C_CLIENT_WAKE, | ||
56 | }; | ||
57 | |||
58 | static struct i2c_board_info __initdata isl_als_device = { | ||
59 | I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR), | ||
60 | }; | ||
61 | |||
62 | static struct i2c_board_info __initdata tsl2583_als_device = { | ||
63 | I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR), | ||
64 | }; | ||
65 | |||
66 | static struct i2c_board_info __initdata tsl2563_als_device = { | ||
67 | I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR), | ||
68 | }; | ||
69 | |||
70 | static 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 | |||
76 | static 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 | |||
82 | static 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 | |||
135 | static 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 | |||
146 | static 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 | */ | ||
171 | static __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 | */ | ||
188 | static __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 | |||
200 | static 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 | |||
206 | static 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 | |||
213 | static 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 | |||
225 | static 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 | |||
238 | static 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 | |||
245 | static 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 | |||
253 | static 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 | |||
260 | static 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 | |||
267 | static 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 | }; | ||
345 | MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table); | ||
346 | |||
347 | static 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 | |||
356 | static 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 | |||
366 | module_init(chromeos_laptop_init); | ||
367 | module_exit(chromeos_laptop_exit); | ||
368 | |||
369 | MODULE_DESCRIPTION("Chrome OS Laptop driver"); | ||
370 | MODULE_AUTHOR("Benson Leung <bleung@chromium.org>"); | ||
371 | MODULE_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 | ||
65 | static const struct key_entry eeepc_wmi_keymap[] = { | 65 | static 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 | ||
65 | enum hp_wmi_event_ids { | 66 | enum 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 | ||
75 | static int hp_wmi_bios_setup(struct platform_device *device); | ||
76 | static int __exit hp_wmi_bios_remove(struct platform_device *device); | ||
77 | static int hp_wmi_resume_handler(struct device *device); | ||
78 | |||
79 | struct bios_args { | 76 | struct 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; | |||
147 | static struct rfkill *wifi_rfkill; | 145 | static struct rfkill *wifi_rfkill; |
148 | static struct rfkill *bluetooth_rfkill; | 146 | static struct rfkill *bluetooth_rfkill; |
149 | static struct rfkill *wwan_rfkill; | 147 | static struct rfkill *wwan_rfkill; |
148 | static struct rfkill *gps_rfkill; | ||
150 | 149 | ||
151 | struct rfkill2_device { | 150 | struct rfkill2_device { |
152 | u8 id; | 151 | u8 id; |
@@ -157,21 +156,6 @@ struct rfkill2_device { | |||
157 | static int rfkill2_count; | 156 | static int rfkill2_count; |
158 | static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES]; | 157 | static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES]; |
159 | 158 | ||
160 | static const struct dev_pm_ops hp_wmi_pm_ops = { | ||
161 | .resume = hp_wmi_resume_handler, | ||
162 | .restore = hp_wmi_resume_handler, | ||
163 | }; | ||
164 | |||
165 | static 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; |
685 | register_wwan_err: | 691 | register_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); | ||
696 | register_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); |
690 | register_bluetooth_error: | 701 | register_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 | ||
781 | static int hp_wmi_bios_setup(struct platform_device *device) | 796 | static 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 | ||
901 | static const struct dev_pm_ops hp_wmi_pm_ops = { | ||
902 | .resume = hp_wmi_resume_handler, | ||
903 | .restore = hp_wmi_resume_handler, | ||
904 | }; | ||
905 | |||
906 | static 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 | |||
877 | static int __init hp_wmi_init(void) | 915 | static 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 | ||
908 | err_device_add: | 948 | err_unregister_device: |
909 | platform_device_put(hp_wmi_platform_dev); | 949 | platform_device_unregister(hp_wmi_platform_dev); |
910 | err_device_alloc: | 950 | err_destroy_input: |
911 | platform_driver_unregister(&hp_wmi_driver); | ||
912 | err_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 | } |
956 | module_init(hp_wmi_init); | ||
918 | 957 | ||
919 | static void __exit hp_wmi_exit(void) | 958 | static 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 | |||
930 | module_init(hp_wmi_init); | ||
931 | module_exit(hp_wmi_exit); | 968 | module_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 |
89 | static int msi_laptop_resume(struct device *device); | 100 | static int msi_laptop_resume(struct device *device); |
@@ -108,23 +119,38 @@ static const struct key_entry msi_laptop_keymap[] = { | |||
108 | 119 | ||
109 | static struct input_dev *msi_laptop_input_dev; | 120 | static struct input_dev *msi_laptop_input_dev; |
110 | 121 | ||
111 | static bool old_ec_model; | ||
112 | static int wlan_s, bluetooth_s, threeg_s; | 122 | static int wlan_s, bluetooth_s, threeg_s; |
113 | static int threeg_exists; | 123 | static 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 | */ | ||
125 | static bool load_scm_model; | ||
126 | static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg; | 124 | static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg; |
127 | 125 | ||
126 | /* MSI laptop quirks */ | ||
127 | struct 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 | |||
152 | static struct quirk_entry *quirks; | ||
153 | |||
128 | /* Hardware access */ | 154 | /* Hardware access */ |
129 | 155 | ||
130 | static int set_lcd_level(int level) | 156 | static 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 | ||
449 | static 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 | |||
463 | static 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 | |||
477 | static 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 | |||
491 | static 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 | |||
506 | static 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 | |||
520 | static 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 | |||
420 | static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); | 536 | static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); |
421 | static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, | 537 | static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, |
422 | store_auto_brightness); | 538 | store_auto_brightness); |
423 | static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL); | 539 | static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL); |
424 | static DEVICE_ATTR(wlan, 0444, show_wlan, NULL); | 540 | static DEVICE_ATTR(wlan, 0444, show_wlan, NULL); |
425 | static DEVICE_ATTR(threeg, 0444, show_threeg, NULL); | 541 | static DEVICE_ATTR(threeg, 0444, show_threeg, NULL); |
542 | static DEVICE_ATTR(touchpad, 0444, show_touchpad, NULL); | ||
543 | static DEVICE_ATTR(turbo_mode, 0444, show_turbo, NULL); | ||
544 | static DEVICE_ATTR(eco_mode, 0444, show_eco, NULL); | ||
545 | static DEVICE_ATTR(turbo_cooldown, 0444, show_turbo_cooldown, NULL); | ||
546 | static DEVICE_ATTR(auto_fan, 0644, show_auto_fan, store_auto_fan); | ||
426 | 547 | ||
427 | static struct attribute *msipf_attributes[] = { | 548 | static 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 | |||
559 | static 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 | ||
569 | static struct attribute_group msipf_old_attribute_group = { | ||
570 | .attrs = msipf_old_attributes | ||
571 | }; | ||
572 | |||
439 | static struct platform_driver msipf_driver = { | 573 | static 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 | ||
451 | static int dmi_check_cb(const struct dmi_system_id *id) | 585 | static struct quirk_entry quirk_old_ec_model = { |
586 | .old_ec_model = true, | ||
587 | }; | ||
588 | |||
589 | static struct quirk_entry quirk_load_scm_model = { | ||
590 | .load_scm_model = true, | ||
591 | .ec_delay = true, | ||
592 | }; | ||
593 | |||
594 | static struct quirk_entry quirk_load_scm_ro_model = { | ||
595 | .load_scm_model = true, | ||
596 | .ec_read_only = true, | ||
597 | }; | ||
598 | |||
599 | static 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 | |||
503 | static 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 | ||
571 | static int rfkill_wlan_set(void *data, bool blocked) | 735 | static 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 | ||
581 | static int rfkill_threeg_set(void *data, bool blocked) | 743 | static 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 | ||
591 | static const struct rfkill_ops rfkill_bluetooth_ops = { | 751 | static const struct rfkill_ops rfkill_bluetooth_ops = { |
@@ -618,25 +778,34 @@ static void rfkill_cleanup(void) | |||
618 | } | 778 | } |
619 | } | 779 | } |
620 | 780 | ||
781 | static 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 | |||
621 | static void msi_update_rfkill(struct work_struct *ignored) | 789 | static 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 | } |
632 | static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill); | 800 | static DECLARE_DELAYED_WORK(msi_rfkill_dwork, msi_update_rfkill); |
801 | static DECLARE_WORK(msi_rfkill_work, msi_update_rfkill); | ||
633 | 802 | ||
634 | static void msi_send_touchpad_key(struct work_struct *ignored) | 803 | static 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 | } |
647 | static DECLARE_DELAYED_WORK(msi_touchpad_work, msi_send_touchpad_key); | 816 | static DECLARE_DELAYED_WORK(msi_touchpad_dwork, msi_send_touchpad_key); |
817 | static DECLARE_WORK(msi_touchpad_work, msi_send_touchpad_key); | ||
648 | 818 | ||
649 | static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, | 819 | static 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 | ||
950 | fail_platform_device2: | 1137 | fail_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 | ||
974 | static void __exit msi_cleanup(void) | 1162 | static 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:*"); | |||
1011 | MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*"); | 1202 | MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*"); |
1012 | MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*"); | 1203 | MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*"); |
1013 | MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnU270series:*"); | 1204 | MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnU270series:*"); |
1205 | MODULE_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>"); | |||
34 | MODULE_DESCRIPTION("MSI laptop WMI hotkeys driver"); | 34 | MODULE_DESCRIPTION("MSI laptop WMI hotkeys driver"); |
35 | MODULE_LICENSE("GPL"); | 35 | MODULE_LICENSE("GPL"); |
36 | 36 | ||
37 | MODULE_ALIAS("wmi:551A1F84-FBDD-4125-91DB-3EA8F44F1D45"); | ||
38 | MODULE_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 | 43 | MODULE_ALIAS("wmi:" MSIWMI_BIOS_GUID); |
47 | #define MSI_WMI_BRIGHTNESSDOWN (SCANCODE_BASE + 1) | 44 | MODULE_ALIAS("wmi:" MSIWMI_MSI_EVENT_GUID); |
48 | #define MSI_WMI_VOLUMEUP (SCANCODE_BASE + 2) | 45 | MODULE_ALIAS("wmi:" MSIWMI_WIND_EVENT_GUID); |
49 | #define MSI_WMI_VOLUMEDOWN (SCANCODE_BASE + 3) | 46 | |
50 | #define MSI_WMI_MUTE (SCANCODE_BASE + 4) | 47 | enum 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 | }; | ||
51 | static struct key_entry msi_wmi_keymap[] = { | 62 | static 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 | |||
87 | static ktime_t last_pressed; | ||
88 | |||
89 | static 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 | }; |
59 | static ktime_t last_pressed[ARRAY_SIZE(msi_wmi_keymap) - 1]; | ||
60 | 96 | ||
61 | static struct backlight_device *backlight; | 97 | static 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 | |||
235 | msi_wmi_notify_exit: | ||
198 | kfree(response.pointer); | 236 | kfree(response.pointer); |
199 | } | 237 | } |
200 | 238 | ||
239 | static 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 | |||
201 | static int __init msi_wmi_input_setup(void) | 264 | static 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: | |||
233 | static int __init msi_wmi_init(void) | 296 | static 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 | ||
273 | err_free_backlight: | 339 | err_uninstall_handler: |
274 | backlight_device_unregister(backlight); | 340 | if (event_wmi) |
341 | wmi_remove_notify_handler(event_wmi->guid); | ||
275 | err_free_input: | 342 | err_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); |
278 | err_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 | ||
283 | static void __exit msi_wmi_exit(void) | 350 | static 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 | ||
293 | module_init(msi_wmi_init); | 361 | module_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); | |||
158 | static int sony_nc_lid_resume_setup(struct platform_device *pd); | 158 | static int sony_nc_lid_resume_setup(struct platform_device *pd); |
159 | static void sony_nc_lid_resume_cleanup(struct platform_device *pd); | 159 | static void sony_nc_lid_resume_cleanup(struct platform_device *pd); |
160 | 160 | ||
161 | static int sony_nc_gfx_switch_setup(struct platform_device *pd, | ||
162 | unsigned int handle); | ||
163 | static void sony_nc_gfx_switch_cleanup(struct platform_device *pd); | ||
164 | static int __sony_nc_gfx_switch_status_get(void); | ||
165 | |||
161 | static int sony_nc_highspeed_charging_setup(struct platform_device *pd); | 166 | static int sony_nc_highspeed_charging_setup(struct platform_device *pd); |
162 | static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd); | 167 | static 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 | ||
2335 | liderror: | 2353 | liderror: |
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 */ | ||
2377 | enum gfx_switch { | ||
2378 | SPEED, | ||
2379 | STAMINA, | ||
2380 | AUTO | ||
2381 | }; | ||
2382 | struct snc_gfx_switch_control { | ||
2383 | struct device_attribute attr; | ||
2384 | unsigned int handle; | ||
2385 | }; | ||
2386 | static struct snc_gfx_switch_control *gfxs_ctl; | ||
2387 | |||
2388 | /* returns 0 for speed, 1 for stamina */ | ||
2389 | static 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 | |||
2416 | static 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 | |||
2428 | static 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 | |||
2450 | gfxerror: | ||
2451 | kfree(gfxs_ctl); | ||
2452 | gfxs_ctl = NULL; | ||
2453 | |||
2454 | return result; | ||
2455 | } | ||
2456 | |||
2457 | static 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 */ |
2359 | static struct device_attribute *hsc_handle; | 2468 | static 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] |