diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-03-03 13:16:19 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-03-03 13:16:19 -0500 |
| commit | 23caaeea271cfe3176f0e27374d2016bd7583ea8 (patch) | |
| tree | b603b654347c87cf211ce667e2406daa1339164a | |
| parent | a7c1120d2dcc83691bafa034d98f70285757e826 (diff) | |
| parent | 445e8d007c29d7f4d497c7912236b69f608340c6 (diff) | |
Merge branch 'for_linus' of git://cavan.codon.org.uk/platform-drivers-x86
Pull x86 platform driver updates from Matthew Garrett:
"Mostly relatively small updates, along with some hardware enablement
for Sony hardware and a pile of updates to Google's Chromebook driver"
* 'for_linus' of git://cavan.codon.org.uk/platform-drivers-x86: (49 commits)
ideapad-laptop: Depend on BACKLIGHT_CLASS_DEVICE instead of selecting it
ideapad: depends on backlight subsystem and update comment
Platform: x86: chromeos_laptop - add i915 gmbuses to adapter names
Platform: x86: chromeos_laptop - Add isl light sensor for Pixel
Platform: x86: chromeos_laptop - Add a more general add_i2c_device
Platform: x86: chromeos_laptop - Add Pixel Touchscreen
Platform: x86: chromeos_laptop - Add support for probing devices
Platform: x86: chromeos_laptop - Add Pixel Trackpad
hp-wmi: fix handling of platform device
sony-laptop: leak in error handling sony_nc_lid_resume_setup()
hp-wmi: Add support for SMBus hotkeys
asus-wmi: Fix unused function build warning
acer-wmi: avoid the warning of 'devices' may be used uninitialized
drivers/platform/x86/thinkpad_acpi.c: Handle HKEY event 0x6040
Platform: x86: chromeos_laptop - Add HP Pavilion 14
Platform: x86: chromeos_laptop - Add Taos tsl2583 device
Platform: x86: chromeos_laptop - Add Taos tsl2563 device
Platform: x86: chromeos_laptop - Add Acer C7 trackpad
Platform: x86: chromeos_laptop - Rename setup_lumpy_tp to setup_cyapa_smbus_tp
asus-laptop: always report brightness key events
...
| -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] |
