mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-25 05:04:09 +08:00
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 ...
This commit is contained in:
commit
23caaeea27
83
Documentation/ABI/testing/sysfs-platform-msi-laptop
Normal file
83
Documentation/ABI/testing/sysfs-platform-msi-laptop
Normal file
@ -0,0 +1,83 @@
|
||||
What: /sys/devices/platform/msi-laptop-pf/lcd_level
|
||||
Date: Oct 2006
|
||||
KernelVersion: 2.6.19
|
||||
Contact: "Lennart Poettering <mzxreary@0pointer.de>"
|
||||
Description:
|
||||
Screen brightness: contains a single integer in the range 0..8.
|
||||
|
||||
What: /sys/devices/platform/msi-laptop-pf/auto_brightness
|
||||
Date: Oct 2006
|
||||
KernelVersion: 2.6.19
|
||||
Contact: "Lennart Poettering <mzxreary@0pointer.de>"
|
||||
Description:
|
||||
Enable automatic brightness control: contains either 0 or 1. If
|
||||
set to 1 the hardware adjusts the screen brightness
|
||||
automatically when the power cord is plugged/unplugged.
|
||||
|
||||
What: /sys/devices/platform/msi-laptop-pf/wlan
|
||||
Date: Oct 2006
|
||||
KernelVersion: 2.6.19
|
||||
Contact: "Lennart Poettering <mzxreary@0pointer.de>"
|
||||
Description:
|
||||
WLAN subsystem enabled: contains either 0 or 1.
|
||||
|
||||
What: /sys/devices/platform/msi-laptop-pf/bluetooth
|
||||
Date: Oct 2006
|
||||
KernelVersion: 2.6.19
|
||||
Contact: "Lennart Poettering <mzxreary@0pointer.de>"
|
||||
Description:
|
||||
Bluetooth subsystem enabled: contains either 0 or 1. Please
|
||||
note that this file is constantly 0 if no Bluetooth hardware is
|
||||
available.
|
||||
|
||||
What: /sys/devices/platform/msi-laptop-pf/touchpad
|
||||
Date: Nov 2012
|
||||
KernelVersion: 3.8
|
||||
Contact: "Maxim Mikityanskiy <maxtram95@gmail.com>"
|
||||
Description:
|
||||
Contains either 0 or 1 and indicates if touchpad is turned on.
|
||||
Touchpad state can only be toggled by pressing Fn+F3.
|
||||
|
||||
What: /sys/devices/platform/msi-laptop-pf/turbo_mode
|
||||
Date: Nov 2012
|
||||
KernelVersion: 3.8
|
||||
Contact: "Maxim Mikityanskiy <maxtram95@gmail.com>"
|
||||
Description:
|
||||
Contains either 0 or 1 and indicates if turbo mode is turned
|
||||
on. In turbo mode power LED is orange and processor is
|
||||
overclocked. Turbo mode is available only if charging. It is
|
||||
only possible to toggle turbo mode state by pressing Fn+F10,
|
||||
and there is a few seconds cooldown between subsequent toggles.
|
||||
If user presses Fn+F10 too frequent, turbo mode state is not
|
||||
changed.
|
||||
|
||||
What: /sys/devices/platform/msi-laptop-pf/eco_mode
|
||||
Date: Nov 2012
|
||||
KernelVersion: 3.8
|
||||
Contact: "Maxim Mikityanskiy <maxtram95@gmail.com>"
|
||||
Description:
|
||||
Contains either 0 or 1 and indicates if ECO mode is turned on.
|
||||
In ECO mode power LED is green and userspace should do some
|
||||
powersaving actions. ECO mode is available only on battery
|
||||
power. ECO mode can only be toggled by pressing Fn+F10.
|
||||
|
||||
What: /sys/devices/platform/msi-laptop-pf/turbo_cooldown
|
||||
Date: Nov 2012
|
||||
KernelVersion: 3.8
|
||||
Contact: "Maxim Mikityanskiy <maxtram95@gmail.com>"
|
||||
Description:
|
||||
Contains value in range 0..3:
|
||||
* 0 -> Turbo mode is off
|
||||
* 1 -> Turbo mode is on, cannot be turned off yet
|
||||
* 2 -> Turbo mode is off, cannot be turned on yet
|
||||
* 3 -> Turbo mode is on
|
||||
|
||||
What: /sys/devices/platform/msi-laptop-pf/auto_fan
|
||||
Date: Nov 2012
|
||||
KernelVersion: 3.8
|
||||
Contact: "Maxim Mikityanskiy <maxtram95@gmail.com>"
|
||||
Description:
|
||||
Contains either 0 or 1 and indicates if fan speed is controlled
|
||||
automatically (1) or fan runs at maximal speed (0). Can be
|
||||
toggled in software.
|
||||
|
@ -79,6 +79,17 @@ config ASUS_LAPTOP
|
||||
|
||||
If you have an ACPI-compatible ASUS laptop, say Y or M here.
|
||||
|
||||
config CHROMEOS_LAPTOP
|
||||
tristate "Chrome OS Laptop"
|
||||
depends on I2C
|
||||
depends on DMI
|
||||
---help---
|
||||
This driver instantiates i2c and smbus devices such as
|
||||
light sensors and touchpads.
|
||||
|
||||
If you have a supported Chromebook, choose Y or M here.
|
||||
The module will be called chromeos_laptop.
|
||||
|
||||
config DELL_LAPTOP
|
||||
tristate "Dell Laptop Extras"
|
||||
depends on X86
|
||||
@ -288,9 +299,11 @@ config IDEAPAD_LAPTOP
|
||||
depends on ACPI
|
||||
depends on RFKILL && INPUT
|
||||
depends on SERIO_I8042
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
select INPUT_SPARSEKMAP
|
||||
help
|
||||
This is a driver for the rfkill switches on Lenovo IdeaPad netbooks.
|
||||
This is a driver for Lenovo IdeaPad netbooks contains drivers for
|
||||
rfkill switch, hotkey, fan control and backlight control.
|
||||
|
||||
config THINKPAD_ACPI
|
||||
tristate "ThinkPad ACPI Laptop Extras"
|
||||
|
@ -50,3 +50,4 @@ obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o
|
||||
obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o
|
||||
obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o
|
||||
obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o
|
||||
obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o
|
||||
|
@ -511,6 +511,24 @@ static struct dmi_system_id acer_quirks[] = {
|
||||
},
|
||||
.driver_data = &quirk_fujitsu_amilo_li_1718,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "Lenovo Ideapad S205-10382JG",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "10382JG"),
|
||||
},
|
||||
.driver_data = &quirk_lenovo_ideapad_s205,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "Lenovo Ideapad S205-1038DPG",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "1038DPG"),
|
||||
},
|
||||
.driver_data = &quirk_lenovo_ideapad_s205,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
@ -1204,6 +1222,9 @@ static acpi_status WMID_set_capabilities(void)
|
||||
devices = *((u32 *) obj->buffer.pointer);
|
||||
} else if (obj->type == ACPI_TYPE_INTEGER) {
|
||||
devices = (u32) obj->integer.value;
|
||||
} else {
|
||||
kfree(out.pointer);
|
||||
return AE_ERROR;
|
||||
}
|
||||
} else {
|
||||
kfree(out.pointer);
|
||||
|
@ -128,10 +128,12 @@ MODULE_PARM_DESC(als_status, "Set the ALS status on boot "
|
||||
/*
|
||||
* Some events we use, same for all Asus
|
||||
*/
|
||||
#define ATKD_BR_UP 0x10 /* (event & ~ATKD_BR_UP) = brightness level */
|
||||
#define ATKD_BR_DOWN 0x20 /* (event & ~ATKD_BR_DOWN) = britghness level */
|
||||
#define ATKD_BR_MIN ATKD_BR_UP
|
||||
#define ATKD_BR_MAX (ATKD_BR_DOWN | 0xF) /* 0x2f */
|
||||
#define ATKD_BRNUP_MIN 0x10
|
||||
#define ATKD_BRNUP_MAX 0x1f
|
||||
#define ATKD_BRNDOWN_MIN 0x20
|
||||
#define ATKD_BRNDOWN_MAX 0x2f
|
||||
#define ATKD_BRNDOWN 0x20
|
||||
#define ATKD_BRNUP 0x2f
|
||||
#define ATKD_LCD_ON 0x33
|
||||
#define ATKD_LCD_OFF 0x34
|
||||
|
||||
@ -301,40 +303,65 @@ static const struct key_entry asus_keymap[] = {
|
||||
{KE_KEY, 0x17, { KEY_ZOOM } },
|
||||
{KE_KEY, 0x1f, { KEY_BATTERY } },
|
||||
/* End of Lenovo SL Specific keycodes */
|
||||
{KE_KEY, ATKD_BRNDOWN, { KEY_BRIGHTNESSDOWN } },
|
||||
{KE_KEY, ATKD_BRNUP, { KEY_BRIGHTNESSUP } },
|
||||
{KE_KEY, 0x30, { KEY_VOLUMEUP } },
|
||||
{KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
|
||||
{KE_KEY, 0x32, { KEY_MUTE } },
|
||||
{KE_KEY, 0x33, { KEY_SWITCHVIDEOMODE } },
|
||||
{KE_KEY, 0x34, { KEY_SWITCHVIDEOMODE } },
|
||||
{KE_KEY, 0x33, { KEY_DISPLAYTOGGLE } }, /* LCD on */
|
||||
{KE_KEY, 0x34, { KEY_DISPLAY_OFF } }, /* LCD off */
|
||||
{KE_KEY, 0x40, { KEY_PREVIOUSSONG } },
|
||||
{KE_KEY, 0x41, { KEY_NEXTSONG } },
|
||||
{KE_KEY, 0x43, { KEY_STOPCD } },
|
||||
{KE_KEY, 0x43, { KEY_STOPCD } }, /* Stop/Eject */
|
||||
{KE_KEY, 0x45, { KEY_PLAYPAUSE } },
|
||||
{KE_KEY, 0x4c, { KEY_MEDIA } },
|
||||
{KE_KEY, 0x4c, { KEY_MEDIA } }, /* WMP Key */
|
||||
{KE_KEY, 0x50, { KEY_EMAIL } },
|
||||
{KE_KEY, 0x51, { KEY_WWW } },
|
||||
{KE_KEY, 0x55, { KEY_CALC } },
|
||||
{KE_IGNORE, 0x57, }, /* Battery mode */
|
||||
{KE_IGNORE, 0x58, }, /* AC mode */
|
||||
{KE_KEY, 0x5C, { KEY_SCREENLOCK } }, /* Screenlock */
|
||||
{KE_KEY, 0x5D, { KEY_WLAN } },
|
||||
{KE_KEY, 0x5E, { KEY_WLAN } },
|
||||
{KE_KEY, 0x5F, { KEY_WLAN } },
|
||||
{KE_KEY, 0x60, { KEY_SWITCHVIDEOMODE } },
|
||||
{KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } },
|
||||
{KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } },
|
||||
{KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } },
|
||||
{KE_KEY, 0x6B, { KEY_F13 } }, /* Lock Touchpad */
|
||||
{KE_KEY, 0x5D, { KEY_WLAN } }, /* WLAN Toggle */
|
||||
{KE_KEY, 0x5E, { KEY_WLAN } }, /* WLAN Enable */
|
||||
{KE_KEY, 0x5F, { KEY_WLAN } }, /* WLAN Disable */
|
||||
{KE_KEY, 0x60, { KEY_TOUCHPAD_ON } },
|
||||
{KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD only */
|
||||
{KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT only */
|
||||
{KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT */
|
||||
{KE_KEY, 0x64, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV */
|
||||
{KE_KEY, 0x65, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV */
|
||||
{KE_KEY, 0x66, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV */
|
||||
{KE_KEY, 0x67, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV */
|
||||
{KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } }, /* Lock Touchpad */
|
||||
{KE_KEY, 0x6C, { KEY_SLEEP } }, /* Suspend */
|
||||
{KE_KEY, 0x6D, { KEY_SLEEP } }, /* Hibernate */
|
||||
{KE_KEY, 0x7E, { KEY_BLUETOOTH } },
|
||||
{KE_KEY, 0x7D, { KEY_BLUETOOTH } },
|
||||
{KE_IGNORE, 0x6E, }, /* Low Battery notification */
|
||||
{KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */
|
||||
{KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */
|
||||
{KE_KEY, 0x82, { KEY_CAMERA } },
|
||||
{KE_KEY, 0x88, { KEY_WLAN } },
|
||||
{KE_KEY, 0x8A, { KEY_PROG1 } },
|
||||
{KE_KEY, 0x88, { KEY_RFKILL } }, /* Radio Toggle Key */
|
||||
{KE_KEY, 0x8A, { KEY_PROG1 } }, /* Color enhancement mode */
|
||||
{KE_KEY, 0x8C, { KEY_SWITCHVIDEOMODE } }, /* SDSP DVI only */
|
||||
{KE_KEY, 0x8D, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + DVI */
|
||||
{KE_KEY, 0x8E, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + DVI */
|
||||
{KE_KEY, 0x8F, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + DVI */
|
||||
{KE_KEY, 0x90, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + DVI */
|
||||
{KE_KEY, 0x91, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + DVI */
|
||||
{KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */
|
||||
{KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI */
|
||||
{KE_KEY, 0x95, { KEY_MEDIA } },
|
||||
{KE_KEY, 0x99, { KEY_PHONE } },
|
||||
{KE_KEY, 0xc4, { KEY_KBDILLUMUP } },
|
||||
{KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } },
|
||||
{KE_KEY, 0xb5, { KEY_CALC } },
|
||||
{KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */
|
||||
{KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */
|
||||
{KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */
|
||||
{KE_KEY, 0xA3, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + HDMI */
|
||||
{KE_KEY, 0xA4, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + HDMI */
|
||||
{KE_KEY, 0xA5, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + HDMI */
|
||||
{KE_KEY, 0xA6, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + HDMI */
|
||||
{KE_KEY, 0xA7, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + HDMI */
|
||||
{KE_KEY, 0xB5, { KEY_CALC } },
|
||||
{KE_KEY, 0xC4, { KEY_KBDILLUMUP } },
|
||||
{KE_KEY, 0xC5, { KEY_KBDILLUMDOWN } },
|
||||
{KE_END, 0},
|
||||
};
|
||||
|
||||
@ -1521,15 +1548,19 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event)
|
||||
dev_name(&asus->device->dev), event,
|
||||
count);
|
||||
|
||||
/* Brightness events are special */
|
||||
if (event >= ATKD_BR_MIN && event <= ATKD_BR_MAX) {
|
||||
if (event >= ATKD_BRNUP_MIN && event <= ATKD_BRNUP_MAX)
|
||||
event = ATKD_BRNUP;
|
||||
else if (event >= ATKD_BRNDOWN_MIN &&
|
||||
event <= ATKD_BRNDOWN_MAX)
|
||||
event = ATKD_BRNDOWN;
|
||||
|
||||
/* Ignore them completely if the acpi video driver is used */
|
||||
/* Brightness events are special */
|
||||
if (event == ATKD_BRNDOWN || event == ATKD_BRNUP) {
|
||||
if (asus->backlight_device != NULL) {
|
||||
/* Update the backlight device. */
|
||||
asus_backlight_notify(asus);
|
||||
return ;
|
||||
}
|
||||
return ;
|
||||
}
|
||||
|
||||
/* Accelerometer "coarse orientation change" event */
|
||||
|
@ -59,6 +59,17 @@ static struct quirk_entry quirk_asus_unknown = {
|
||||
.wapf = 0,
|
||||
};
|
||||
|
||||
/*
|
||||
* For those machines that need software to control bt/wifi status
|
||||
* and can't adjust brightness through ACPI interface
|
||||
* and have duplicate events(ACPI and WMI) for display toggle
|
||||
*/
|
||||
static struct quirk_entry quirk_asus_x55u = {
|
||||
.wapf = 4,
|
||||
.wmi_backlight_power = true,
|
||||
.no_display_toggle = true,
|
||||
};
|
||||
|
||||
static struct quirk_entry quirk_asus_x401u = {
|
||||
.wapf = 4,
|
||||
};
|
||||
@ -77,6 +88,15 @@ static struct dmi_system_id asus_quirks[] = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "X401U"),
|
||||
},
|
||||
.driver_data = &quirk_asus_x55u,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "ASUSTeK COMPUTER INC. X401A",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "X401A"),
|
||||
},
|
||||
.driver_data = &quirk_asus_x401u,
|
||||
},
|
||||
{
|
||||
@ -95,6 +115,15 @@ static struct dmi_system_id asus_quirks[] = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "X501U"),
|
||||
},
|
||||
.driver_data = &quirk_asus_x55u,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "ASUSTeK COMPUTER INC. X501A",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "X501A"),
|
||||
},
|
||||
.driver_data = &quirk_asus_x401u,
|
||||
},
|
||||
{
|
||||
@ -131,7 +160,7 @@ static struct dmi_system_id asus_quirks[] = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "X55U"),
|
||||
},
|
||||
.driver_data = &quirk_asus_x401u,
|
||||
.driver_data = &quirk_asus_x55u,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
@ -161,6 +190,8 @@ static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver)
|
||||
}
|
||||
|
||||
static const struct key_entry asus_nb_wmi_keymap[] = {
|
||||
{ KE_KEY, ASUS_WMI_BRN_DOWN, { KEY_BRIGHTNESSDOWN } },
|
||||
{ KE_KEY, ASUS_WMI_BRN_UP, { KEY_BRIGHTNESSUP } },
|
||||
{ KE_KEY, 0x30, { KEY_VOLUMEUP } },
|
||||
{ KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
|
||||
{ KE_KEY, 0x32, { KEY_MUTE } },
|
||||
@ -168,9 +199,9 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
|
||||
{ KE_KEY, 0x34, { KEY_DISPLAY_OFF } }, /* LCD off */
|
||||
{ KE_KEY, 0x40, { KEY_PREVIOUSSONG } },
|
||||
{ KE_KEY, 0x41, { KEY_NEXTSONG } },
|
||||
{ KE_KEY, 0x43, { KEY_STOPCD } },
|
||||
{ KE_KEY, 0x43, { KEY_STOPCD } }, /* Stop/Eject */
|
||||
{ KE_KEY, 0x45, { KEY_PLAYPAUSE } },
|
||||
{ KE_KEY, 0x4c, { KEY_MEDIA } },
|
||||
{ KE_KEY, 0x4c, { KEY_MEDIA } }, /* WMP Key */
|
||||
{ KE_KEY, 0x50, { KEY_EMAIL } },
|
||||
{ KE_KEY, 0x51, { KEY_WWW } },
|
||||
{ KE_KEY, 0x55, { KEY_CALC } },
|
||||
@ -180,25 +211,42 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
|
||||
{ KE_KEY, 0x5D, { KEY_WLAN } }, /* Wireless console Toggle */
|
||||
{ KE_KEY, 0x5E, { KEY_WLAN } }, /* Wireless console Enable */
|
||||
{ KE_KEY, 0x5F, { KEY_WLAN } }, /* Wireless console Disable */
|
||||
{ KE_KEY, 0x60, { KEY_SWITCHVIDEOMODE } },
|
||||
{ KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } },
|
||||
{ KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } },
|
||||
{ KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } },
|
||||
{ KE_KEY, 0x60, { KEY_TOUCHPAD_ON } },
|
||||
{ KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD only */
|
||||
{ KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT only */
|
||||
{ KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT */
|
||||
{ KE_KEY, 0x64, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV */
|
||||
{ KE_KEY, 0x65, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV */
|
||||
{ KE_KEY, 0x66, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV */
|
||||
{ KE_KEY, 0x67, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV */
|
||||
{ KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },
|
||||
{ KE_KEY, 0x7D, { KEY_BLUETOOTH } },
|
||||
{ KE_KEY, 0x7E, { KEY_BLUETOOTH } },
|
||||
{ KE_IGNORE, 0x6E, }, /* Low Battery notification */
|
||||
{ KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */
|
||||
{ KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */
|
||||
{ KE_KEY, 0x82, { KEY_CAMERA } },
|
||||
{ KE_KEY, 0x88, { KEY_RFKILL } },
|
||||
{ KE_KEY, 0x8A, { KEY_PROG1 } },
|
||||
{ KE_KEY, 0x88, { KEY_RFKILL } }, /* Radio Toggle Key */
|
||||
{ KE_KEY, 0x8A, { KEY_PROG1 } }, /* Color enhancement mode */
|
||||
{ KE_KEY, 0x8C, { KEY_SWITCHVIDEOMODE } }, /* SDSP DVI only */
|
||||
{ KE_KEY, 0x8D, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + DVI */
|
||||
{ KE_KEY, 0x8E, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + DVI */
|
||||
{ KE_KEY, 0x8F, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + DVI */
|
||||
{ KE_KEY, 0x90, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + DVI */
|
||||
{ KE_KEY, 0x91, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + DVI */
|
||||
{ KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */
|
||||
{ KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI */
|
||||
{ KE_KEY, 0x95, { KEY_MEDIA } },
|
||||
{ KE_KEY, 0x99, { KEY_PHONE } },
|
||||
{ KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */
|
||||
{ KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */
|
||||
{ KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */
|
||||
{ KE_KEY, 0xA3, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + HDMI */
|
||||
{ KE_KEY, 0xb5, { KEY_CALC } },
|
||||
{ KE_KEY, 0xc4, { KEY_KBDILLUMUP } },
|
||||
{ KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } },
|
||||
{ KE_KEY, 0xA4, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + HDMI */
|
||||
{ KE_KEY, 0xA5, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + HDMI */
|
||||
{ KE_KEY, 0xA6, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + HDMI */
|
||||
{ KE_KEY, 0xA7, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + HDMI */
|
||||
{ KE_KEY, 0xB5, { KEY_CALC } },
|
||||
{ KE_KEY, 0xC4, { KEY_KBDILLUMUP } },
|
||||
{ KE_KEY, 0xC5, { KEY_KBDILLUMDOWN } },
|
||||
{ KE_END, 0},
|
||||
};
|
||||
|
||||
|
@ -187,6 +187,8 @@ struct asus_wmi {
|
||||
struct device *hwmon_device;
|
||||
struct platform_device *platform_device;
|
||||
|
||||
struct led_classdev wlan_led;
|
||||
int wlan_led_wk;
|
||||
struct led_classdev tpd_led;
|
||||
int tpd_led_wk;
|
||||
struct led_classdev kbd_led;
|
||||
@ -194,6 +196,7 @@ struct asus_wmi {
|
||||
struct workqueue_struct *led_workqueue;
|
||||
struct work_struct tpd_led_work;
|
||||
struct work_struct kbd_led_work;
|
||||
struct work_struct wlan_led_work;
|
||||
|
||||
struct asus_rfkill wlan;
|
||||
struct asus_rfkill bluetooth;
|
||||
@ -456,12 +459,65 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
|
||||
return value;
|
||||
}
|
||||
|
||||
static int wlan_led_unknown_state(struct asus_wmi *asus)
|
||||
{
|
||||
u32 result;
|
||||
|
||||
asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result);
|
||||
|
||||
return result & ASUS_WMI_DSTS_UNKNOWN_BIT;
|
||||
}
|
||||
|
||||
static int wlan_led_presence(struct asus_wmi *asus)
|
||||
{
|
||||
u32 result;
|
||||
|
||||
asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result);
|
||||
|
||||
return result & ASUS_WMI_DSTS_PRESENCE_BIT;
|
||||
}
|
||||
|
||||
static void wlan_led_update(struct work_struct *work)
|
||||
{
|
||||
int ctrl_param;
|
||||
struct asus_wmi *asus;
|
||||
|
||||
asus = container_of(work, struct asus_wmi, wlan_led_work);
|
||||
|
||||
ctrl_param = asus->wlan_led_wk;
|
||||
asus_wmi_set_devstate(ASUS_WMI_DEVID_WIRELESS_LED, ctrl_param, NULL);
|
||||
}
|
||||
|
||||
static void wlan_led_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct asus_wmi *asus;
|
||||
|
||||
asus = container_of(led_cdev, struct asus_wmi, wlan_led);
|
||||
|
||||
asus->wlan_led_wk = !!value;
|
||||
queue_work(asus->led_workqueue, &asus->wlan_led_work);
|
||||
}
|
||||
|
||||
static enum led_brightness wlan_led_get(struct led_classdev *led_cdev)
|
||||
{
|
||||
struct asus_wmi *asus;
|
||||
u32 result;
|
||||
|
||||
asus = container_of(led_cdev, struct asus_wmi, wlan_led);
|
||||
asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result);
|
||||
|
||||
return result & ASUS_WMI_DSTS_BRIGHTNESS_MASK;
|
||||
}
|
||||
|
||||
static void asus_wmi_led_exit(struct asus_wmi *asus)
|
||||
{
|
||||
if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
|
||||
led_classdev_unregister(&asus->kbd_led);
|
||||
if (!IS_ERR_OR_NULL(asus->tpd_led.dev))
|
||||
led_classdev_unregister(&asus->tpd_led);
|
||||
if (!IS_ERR_OR_NULL(asus->wlan_led.dev))
|
||||
led_classdev_unregister(&asus->wlan_led);
|
||||
if (asus->led_workqueue)
|
||||
destroy_workqueue(asus->led_workqueue);
|
||||
}
|
||||
@ -498,6 +554,23 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
|
||||
|
||||
rv = led_classdev_register(&asus->platform_device->dev,
|
||||
&asus->kbd_led);
|
||||
if (rv)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (wlan_led_presence(asus)) {
|
||||
INIT_WORK(&asus->wlan_led_work, wlan_led_update);
|
||||
|
||||
asus->wlan_led.name = "asus::wlan";
|
||||
asus->wlan_led.brightness_set = wlan_led_set;
|
||||
if (!wlan_led_unknown_state(asus))
|
||||
asus->wlan_led.brightness_get = wlan_led_get;
|
||||
asus->wlan_led.flags = LED_CORE_SUSPENDRESUME;
|
||||
asus->wlan_led.max_brightness = 1;
|
||||
asus->wlan_led.default_trigger = "asus-wlan";
|
||||
|
||||
rv = led_classdev_register(&asus->platform_device->dev,
|
||||
&asus->wlan_led);
|
||||
}
|
||||
|
||||
error:
|
||||
@ -813,6 +886,9 @@ static int asus_new_rfkill(struct asus_wmi *asus,
|
||||
if (!*rfkill)
|
||||
return -EINVAL;
|
||||
|
||||
if (dev_id == ASUS_WMI_DEVID_WLAN)
|
||||
rfkill_set_led_trigger_name(*rfkill, "asus-wlan");
|
||||
|
||||
rfkill_init_sw_state(*rfkill, !result);
|
||||
result = rfkill_register(*rfkill);
|
||||
if (result) {
|
||||
@ -1265,6 +1341,18 @@ static void asus_wmi_backlight_exit(struct asus_wmi *asus)
|
||||
asus->backlight_device = NULL;
|
||||
}
|
||||
|
||||
static int is_display_toggle(int code)
|
||||
{
|
||||
/* display toggle keys */
|
||||
if ((code >= 0x61 && code <= 0x67) ||
|
||||
(code >= 0x8c && code <= 0x93) ||
|
||||
(code >= 0xa0 && code <= 0xa7) ||
|
||||
(code >= 0xd0 && code <= 0xd5))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void asus_wmi_notify(u32 value, void *context)
|
||||
{
|
||||
struct asus_wmi *asus = context;
|
||||
@ -1298,16 +1386,24 @@ static void asus_wmi_notify(u32 value, void *context)
|
||||
}
|
||||
|
||||
if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
|
||||
code = NOTIFY_BRNUP_MIN;
|
||||
code = ASUS_WMI_BRN_UP;
|
||||
else if (code >= NOTIFY_BRNDOWN_MIN &&
|
||||
code <= NOTIFY_BRNDOWN_MAX)
|
||||
code = NOTIFY_BRNDOWN_MIN;
|
||||
code = ASUS_WMI_BRN_DOWN;
|
||||
|
||||
if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) {
|
||||
if (!acpi_video_backlight_support())
|
||||
if (code == ASUS_WMI_BRN_DOWN || code == ASUS_WMI_BRN_UP) {
|
||||
if (!acpi_video_backlight_support()) {
|
||||
asus_wmi_backlight_notify(asus, orig_code);
|
||||
} else if (!sparse_keymap_report_event(asus->inputdev, code,
|
||||
key_value, autorelease))
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_display_toggle(code) &&
|
||||
asus->driver->quirks->no_display_toggle)
|
||||
goto exit;
|
||||
|
||||
if (!sparse_keymap_report_event(asus->inputdev, code,
|
||||
key_value, autorelease))
|
||||
pr_info("Unknown key %x pressed\n", code);
|
||||
|
||||
exit:
|
||||
|
@ -30,6 +30,8 @@
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define ASUS_WMI_KEY_IGNORE (-1)
|
||||
#define ASUS_WMI_BRN_DOWN 0x20
|
||||
#define ASUS_WMI_BRN_UP 0x2f
|
||||
|
||||
struct module;
|
||||
struct key_entry;
|
||||
@ -41,6 +43,13 @@ struct quirk_entry {
|
||||
bool store_backlight_power;
|
||||
bool wmi_backlight_power;
|
||||
int wapf;
|
||||
/*
|
||||
* For machines with AMD graphic chips, it will send out WMI event
|
||||
* and ACPI interrupt at the same time while hitting the hotkey.
|
||||
* To simplify the problem, we just have to ignore the WMI event,
|
||||
* and let the ACPI interrupt to send out the key event.
|
||||
*/
|
||||
int no_display_toggle;
|
||||
};
|
||||
|
||||
struct asus_wmi_driver {
|
||||
|
371
drivers/platform/x86/chromeos_laptop.c
Normal file
371
drivers/platform/x86/chromeos_laptop.c
Normal file
@ -0,0 +1,371 @@
|
||||
/*
|
||||
* chromeos_laptop.c - Driver to instantiate Chromebook i2c/smbus devices.
|
||||
*
|
||||
* Author : Benson Leung <bleung@chromium.org>
|
||||
*
|
||||
* Copyright (C) 2012 Google, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define ATMEL_TP_I2C_ADDR 0x4b
|
||||
#define ATMEL_TP_I2C_BL_ADDR 0x25
|
||||
#define ATMEL_TS_I2C_ADDR 0x4a
|
||||
#define ATMEL_TS_I2C_BL_ADDR 0x26
|
||||
#define CYAPA_TP_I2C_ADDR 0x67
|
||||
#define ISL_ALS_I2C_ADDR 0x44
|
||||
#define TAOS_ALS_I2C_ADDR 0x29
|
||||
|
||||
static struct i2c_client *als;
|
||||
static struct i2c_client *tp;
|
||||
static struct i2c_client *ts;
|
||||
|
||||
const char *i2c_adapter_names[] = {
|
||||
"SMBus I801 adapter",
|
||||
"i915 gmbus vga",
|
||||
"i915 gmbus panel",
|
||||
};
|
||||
|
||||
/* Keep this enum consistent with i2c_adapter_names */
|
||||
enum i2c_adapter_type {
|
||||
I2C_ADAPTER_SMBUS = 0,
|
||||
I2C_ADAPTER_VGADDC,
|
||||
I2C_ADAPTER_PANEL,
|
||||
};
|
||||
|
||||
static struct i2c_board_info __initdata cyapa_device = {
|
||||
I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
|
||||
.flags = I2C_CLIENT_WAKE,
|
||||
};
|
||||
|
||||
static struct i2c_board_info __initdata isl_als_device = {
|
||||
I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
|
||||
};
|
||||
|
||||
static struct i2c_board_info __initdata tsl2583_als_device = {
|
||||
I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
|
||||
};
|
||||
|
||||
static struct i2c_board_info __initdata tsl2563_als_device = {
|
||||
I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR),
|
||||
};
|
||||
|
||||
static struct i2c_board_info __initdata atmel_224s_tp_device = {
|
||||
I2C_BOARD_INFO("atmel_mxt_tp", ATMEL_TP_I2C_ADDR),
|
||||
.platform_data = NULL,
|
||||
.flags = I2C_CLIENT_WAKE,
|
||||
};
|
||||
|
||||
static struct i2c_board_info __initdata atmel_1664s_device = {
|
||||
I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR),
|
||||
.platform_data = NULL,
|
||||
.flags = I2C_CLIENT_WAKE,
|
||||
};
|
||||
|
||||
static struct i2c_client __init *__add_probed_i2c_device(
|
||||
const char *name,
|
||||
int bus,
|
||||
struct i2c_board_info *info,
|
||||
const unsigned short *addrs)
|
||||
{
|
||||
const struct dmi_device *dmi_dev;
|
||||
const struct dmi_dev_onboard *dev_data;
|
||||
struct i2c_adapter *adapter;
|
||||
struct i2c_client *client;
|
||||
|
||||
if (bus < 0)
|
||||
return NULL;
|
||||
/*
|
||||
* If a name is specified, look for irq platform information stashed
|
||||
* in DMI_DEV_TYPE_DEV_ONBOARD by the Chrome OS custom system firmware.
|
||||
*/
|
||||
if (name) {
|
||||
dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, name, NULL);
|
||||
if (!dmi_dev) {
|
||||
pr_err("%s failed to dmi find device %s.\n",
|
||||
__func__,
|
||||
name);
|
||||
return NULL;
|
||||
}
|
||||
dev_data = (struct dmi_dev_onboard *)dmi_dev->device_data;
|
||||
if (!dev_data) {
|
||||
pr_err("%s failed to get data from dmi for %s.\n",
|
||||
__func__, name);
|
||||
return NULL;
|
||||
}
|
||||
info->irq = dev_data->instance;
|
||||
}
|
||||
|
||||
adapter = i2c_get_adapter(bus);
|
||||
if (!adapter) {
|
||||
pr_err("%s failed to get i2c adapter %d.\n", __func__, bus);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* add the i2c device */
|
||||
client = i2c_new_probed_device(adapter, info, addrs, NULL);
|
||||
if (!client)
|
||||
pr_err("%s failed to register device %d-%02x\n",
|
||||
__func__, bus, info->addr);
|
||||
else
|
||||
pr_debug("%s added i2c device %d-%02x\n",
|
||||
__func__, bus, info->addr);
|
||||
|
||||
i2c_put_adapter(adapter);
|
||||
return client;
|
||||
}
|
||||
|
||||
static int __init __find_i2c_adap(struct device *dev, void *data)
|
||||
{
|
||||
const char *name = data;
|
||||
static const char *prefix = "i2c-";
|
||||
struct i2c_adapter *adapter;
|
||||
if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0)
|
||||
return 0;
|
||||
adapter = to_i2c_adapter(dev);
|
||||
return (strncmp(adapter->name, name, strlen(name)) == 0);
|
||||
}
|
||||
|
||||
static int __init find_i2c_adapter_num(enum i2c_adapter_type type)
|
||||
{
|
||||
struct device *dev = NULL;
|
||||
struct i2c_adapter *adapter;
|
||||
const char *name = i2c_adapter_names[type];
|
||||
/* find the adapter by name */
|
||||
dev = bus_find_device(&i2c_bus_type, NULL, (void *)name,
|
||||
__find_i2c_adap);
|
||||
if (!dev) {
|
||||
pr_err("%s: i2c adapter %s not found on system.\n", __func__,
|
||||
name);
|
||||
return -ENODEV;
|
||||
}
|
||||
adapter = to_i2c_adapter(dev);
|
||||
return adapter->nr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Takes a list of addresses in addrs as such :
|
||||
* { addr1, ... , addrn, I2C_CLIENT_END };
|
||||
* add_probed_i2c_device will use i2c_new_probed_device
|
||||
* and probe for devices at all of the addresses listed.
|
||||
* Returns NULL if no devices found.
|
||||
* See Documentation/i2c/instantiating-devices for more information.
|
||||
*/
|
||||
static __init struct i2c_client *add_probed_i2c_device(
|
||||
const char *name,
|
||||
enum i2c_adapter_type type,
|
||||
struct i2c_board_info *info,
|
||||
const unsigned short *addrs)
|
||||
{
|
||||
return __add_probed_i2c_device(name,
|
||||
find_i2c_adapter_num(type),
|
||||
info,
|
||||
addrs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Probes for a device at a single address, the one provided by
|
||||
* info->addr.
|
||||
* Returns NULL if no device found.
|
||||
*/
|
||||
static __init struct i2c_client *add_i2c_device(const char *name,
|
||||
enum i2c_adapter_type type,
|
||||
struct i2c_board_info *info)
|
||||
{
|
||||
const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END };
|
||||
return __add_probed_i2c_device(name,
|
||||
find_i2c_adapter_num(type),
|
||||
info,
|
||||
addr_list);
|
||||
}
|
||||
|
||||
|
||||
static struct i2c_client __init *add_smbus_device(const char *name,
|
||||
struct i2c_board_info *info)
|
||||
{
|
||||
return add_i2c_device(name, I2C_ADAPTER_SMBUS, info);
|
||||
}
|
||||
|
||||
static int __init setup_cyapa_smbus_tp(const struct dmi_system_id *id)
|
||||
{
|
||||
/* add cyapa touchpad on smbus */
|
||||
tp = add_smbus_device("trackpad", &cyapa_device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init setup_atmel_224s_tp(const struct dmi_system_id *id)
|
||||
{
|
||||
const unsigned short addr_list[] = { ATMEL_TP_I2C_BL_ADDR,
|
||||
ATMEL_TP_I2C_ADDR,
|
||||
I2C_CLIENT_END };
|
||||
|
||||
/* add atmel mxt touchpad on VGA DDC GMBus */
|
||||
tp = add_probed_i2c_device("trackpad", I2C_ADAPTER_VGADDC,
|
||||
&atmel_224s_tp_device, addr_list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init setup_atmel_1664s_ts(const struct dmi_system_id *id)
|
||||
{
|
||||
const unsigned short addr_list[] = { ATMEL_TS_I2C_BL_ADDR,
|
||||
ATMEL_TS_I2C_ADDR,
|
||||
I2C_CLIENT_END };
|
||||
|
||||
/* add atmel mxt touch device on PANEL GMBus */
|
||||
ts = add_probed_i2c_device("touchscreen", I2C_ADAPTER_PANEL,
|
||||
&atmel_1664s_device, addr_list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int __init setup_isl29018_als(const struct dmi_system_id *id)
|
||||
{
|
||||
/* add isl29018 light sensor */
|
||||
als = add_smbus_device("lightsensor", &isl_als_device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init setup_isl29023_als(const struct dmi_system_id *id)
|
||||
{
|
||||
/* add isl29023 light sensor on Panel GMBus */
|
||||
als = add_i2c_device("lightsensor", I2C_ADAPTER_PANEL,
|
||||
&isl_als_device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init setup_tsl2583_als(const struct dmi_system_id *id)
|
||||
{
|
||||
/* add tsl2583 light sensor on smbus */
|
||||
als = add_smbus_device(NULL, &tsl2583_als_device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init setup_tsl2563_als(const struct dmi_system_id *id)
|
||||
{
|
||||
/* add tsl2563 light sensor on smbus */
|
||||
als = add_smbus_device(NULL, &tsl2563_als_device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dmi_system_id __initdata chromeos_laptop_dmi_table[] = {
|
||||
{
|
||||
.ident = "Samsung Series 5 550 - Touchpad",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
|
||||
},
|
||||
.callback = setup_cyapa_smbus_tp,
|
||||
},
|
||||
{
|
||||
.ident = "Chromebook Pixel - Touchscreen",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
|
||||
},
|
||||
.callback = setup_atmel_1664s_ts,
|
||||
},
|
||||
{
|
||||
.ident = "Chromebook Pixel - Touchpad",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
|
||||
},
|
||||
.callback = setup_atmel_224s_tp,
|
||||
},
|
||||
{
|
||||
.ident = "Samsung Series 5 550 - Light Sensor",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
|
||||
},
|
||||
.callback = setup_isl29018_als,
|
||||
},
|
||||
{
|
||||
.ident = "Chromebook Pixel - Light Sensor",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
|
||||
},
|
||||
.callback = setup_isl29023_als,
|
||||
},
|
||||
{
|
||||
.ident = "Acer C7 Chromebook - Touchpad",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"),
|
||||
},
|
||||
.callback = setup_cyapa_smbus_tp,
|
||||
},
|
||||
{
|
||||
.ident = "HP Pavilion 14 Chromebook - Touchpad",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Butterfly"),
|
||||
},
|
||||
.callback = setup_cyapa_smbus_tp,
|
||||
},
|
||||
{
|
||||
.ident = "Samsung Series 5 - Light Sensor",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Alex"),
|
||||
},
|
||||
.callback = setup_tsl2583_als,
|
||||
},
|
||||
{
|
||||
.ident = "Cr-48 - Light Sensor",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),
|
||||
},
|
||||
.callback = setup_tsl2563_als,
|
||||
},
|
||||
{
|
||||
.ident = "Acer AC700 - Light Sensor",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"),
|
||||
},
|
||||
.callback = setup_tsl2563_als,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table);
|
||||
|
||||
static int __init chromeos_laptop_init(void)
|
||||
{
|
||||
if (!dmi_check_system(chromeos_laptop_dmi_table)) {
|
||||
pr_debug("%s unsupported system.\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit chromeos_laptop_exit(void)
|
||||
{
|
||||
if (als)
|
||||
i2c_unregister_device(als);
|
||||
if (tp)
|
||||
i2c_unregister_device(tp);
|
||||
if (ts)
|
||||
i2c_unregister_device(ts);
|
||||
}
|
||||
|
||||
module_init(chromeos_laptop_init);
|
||||
module_exit(chromeos_laptop_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Chrome OS Laptop driver");
|
||||
MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
|
||||
MODULE_LICENSE("GPL");
|
@ -63,6 +63,8 @@ MODULE_PARM_DESC(hotplug_wireless,
|
||||
#define HOME_RELEASE 0xe5
|
||||
|
||||
static const struct key_entry eeepc_wmi_keymap[] = {
|
||||
{ KE_KEY, ASUS_WMI_BRN_DOWN, { KEY_BRIGHTNESSDOWN } },
|
||||
{ KE_KEY, ASUS_WMI_BRN_UP, { KEY_BRIGHTNESSUP } },
|
||||
/* Sleep already handled via generic ACPI code */
|
||||
{ KE_KEY, 0x30, { KEY_VOLUMEUP } },
|
||||
{ KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
|
||||
|
@ -60,6 +60,7 @@ enum hp_wmi_radio {
|
||||
HPWMI_WIFI = 0,
|
||||
HPWMI_BLUETOOTH = 1,
|
||||
HPWMI_WWAN = 2,
|
||||
HPWMI_GPS = 3,
|
||||
};
|
||||
|
||||
enum hp_wmi_event_ids {
|
||||
@ -72,10 +73,6 @@ enum hp_wmi_event_ids {
|
||||
HPWMI_LOCK_SWITCH = 7,
|
||||
};
|
||||
|
||||
static int hp_wmi_bios_setup(struct platform_device *device);
|
||||
static int __exit hp_wmi_bios_remove(struct platform_device *device);
|
||||
static int hp_wmi_resume_handler(struct device *device);
|
||||
|
||||
struct bios_args {
|
||||
u32 signature;
|
||||
u32 command;
|
||||
@ -137,6 +134,7 @@ static const struct key_entry hp_wmi_keymap[] = {
|
||||
{ KE_KEY, 0x2142, { KEY_MEDIA } },
|
||||
{ KE_KEY, 0x213b, { KEY_INFO } },
|
||||
{ KE_KEY, 0x2169, { KEY_DIRECTION } },
|
||||
{ KE_KEY, 0x216a, { KEY_SETUP } },
|
||||
{ KE_KEY, 0x231b, { KEY_HELP } },
|
||||
{ KE_END, 0 }
|
||||
};
|
||||
@ -147,6 +145,7 @@ static struct platform_device *hp_wmi_platform_dev;
|
||||
static struct rfkill *wifi_rfkill;
|
||||
static struct rfkill *bluetooth_rfkill;
|
||||
static struct rfkill *wwan_rfkill;
|
||||
static struct rfkill *gps_rfkill;
|
||||
|
||||
struct rfkill2_device {
|
||||
u8 id;
|
||||
@ -157,21 +156,6 @@ struct rfkill2_device {
|
||||
static int rfkill2_count;
|
||||
static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES];
|
||||
|
||||
static const struct dev_pm_ops hp_wmi_pm_ops = {
|
||||
.resume = hp_wmi_resume_handler,
|
||||
.restore = hp_wmi_resume_handler,
|
||||
};
|
||||
|
||||
static struct platform_driver hp_wmi_driver = {
|
||||
.driver = {
|
||||
.name = "hp-wmi",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &hp_wmi_pm_ops,
|
||||
},
|
||||
.probe = hp_wmi_bios_setup,
|
||||
.remove = hp_wmi_bios_remove,
|
||||
};
|
||||
|
||||
/*
|
||||
* hp_wmi_perform_query
|
||||
*
|
||||
@ -543,6 +527,10 @@ static void hp_wmi_notify(u32 value, void *context)
|
||||
rfkill_set_states(wwan_rfkill,
|
||||
hp_wmi_get_sw_state(HPWMI_WWAN),
|
||||
hp_wmi_get_hw_state(HPWMI_WWAN));
|
||||
if (gps_rfkill)
|
||||
rfkill_set_states(gps_rfkill,
|
||||
hp_wmi_get_sw_state(HPWMI_GPS),
|
||||
hp_wmi_get_hw_state(HPWMI_GPS));
|
||||
break;
|
||||
case HPWMI_CPU_BATTERY_THROTTLE:
|
||||
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)
|
||||
(void *) HPWMI_WWAN);
|
||||
if (!wwan_rfkill) {
|
||||
err = -ENOMEM;
|
||||
goto register_bluetooth_error;
|
||||
goto register_gps_error;
|
||||
}
|
||||
rfkill_init_sw_state(wwan_rfkill,
|
||||
hp_wmi_get_sw_state(HPWMI_WWAN));
|
||||
@ -681,10 +669,33 @@ static int hp_wmi_rfkill_setup(struct platform_device *device)
|
||||
goto register_wwan_err;
|
||||
}
|
||||
|
||||
if (wireless & 0x8) {
|
||||
gps_rfkill = rfkill_alloc("hp-gps", &device->dev,
|
||||
RFKILL_TYPE_GPS,
|
||||
&hp_wmi_rfkill_ops,
|
||||
(void *) HPWMI_GPS);
|
||||
if (!gps_rfkill) {
|
||||
err = -ENOMEM;
|
||||
goto register_bluetooth_error;
|
||||
}
|
||||
rfkill_init_sw_state(gps_rfkill,
|
||||
hp_wmi_get_sw_state(HPWMI_GPS));
|
||||
rfkill_set_hw_state(bluetooth_rfkill,
|
||||
hp_wmi_get_hw_state(HPWMI_GPS));
|
||||
err = rfkill_register(gps_rfkill);
|
||||
if (err)
|
||||
goto register_gps_error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
register_wwan_err:
|
||||
rfkill_destroy(wwan_rfkill);
|
||||
wwan_rfkill = NULL;
|
||||
if (gps_rfkill)
|
||||
rfkill_unregister(gps_rfkill);
|
||||
register_gps_error:
|
||||
rfkill_destroy(gps_rfkill);
|
||||
gps_rfkill = NULL;
|
||||
if (bluetooth_rfkill)
|
||||
rfkill_unregister(bluetooth_rfkill);
|
||||
register_bluetooth_error:
|
||||
@ -729,6 +740,10 @@ static int hp_wmi_rfkill2_setup(struct platform_device *device)
|
||||
type = RFKILL_TYPE_WWAN;
|
||||
name = "hp-wwan";
|
||||
break;
|
||||
case HPWMI_GPS:
|
||||
type = RFKILL_TYPE_GPS;
|
||||
name = "hp-gps";
|
||||
break;
|
||||
default:
|
||||
pr_warn("unknown device type 0x%x\n",
|
||||
state.device[i].radio_type);
|
||||
@ -778,7 +793,7 @@ fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hp_wmi_bios_setup(struct platform_device *device)
|
||||
static int __init hp_wmi_bios_setup(struct platform_device *device)
|
||||
{
|
||||
int err;
|
||||
|
||||
@ -786,6 +801,7 @@ static int hp_wmi_bios_setup(struct platform_device *device)
|
||||
wifi_rfkill = NULL;
|
||||
bluetooth_rfkill = NULL;
|
||||
wwan_rfkill = NULL;
|
||||
gps_rfkill = NULL;
|
||||
rfkill2_count = 0;
|
||||
|
||||
if (hp_wmi_rfkill_setup(device))
|
||||
@ -835,6 +851,10 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device)
|
||||
rfkill_unregister(wwan_rfkill);
|
||||
rfkill_destroy(wwan_rfkill);
|
||||
}
|
||||
if (gps_rfkill) {
|
||||
rfkill_unregister(gps_rfkill);
|
||||
rfkill_destroy(gps_rfkill);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -870,51 +890,70 @@ static int hp_wmi_resume_handler(struct device *device)
|
||||
rfkill_set_states(wwan_rfkill,
|
||||
hp_wmi_get_sw_state(HPWMI_WWAN),
|
||||
hp_wmi_get_hw_state(HPWMI_WWAN));
|
||||
if (gps_rfkill)
|
||||
rfkill_set_states(gps_rfkill,
|
||||
hp_wmi_get_sw_state(HPWMI_GPS),
|
||||
hp_wmi_get_hw_state(HPWMI_GPS));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops hp_wmi_pm_ops = {
|
||||
.resume = hp_wmi_resume_handler,
|
||||
.restore = hp_wmi_resume_handler,
|
||||
};
|
||||
|
||||
static struct platform_driver hp_wmi_driver = {
|
||||
.driver = {
|
||||
.name = "hp-wmi",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &hp_wmi_pm_ops,
|
||||
},
|
||||
.remove = __exit_p(hp_wmi_bios_remove),
|
||||
};
|
||||
|
||||
static int __init hp_wmi_init(void)
|
||||
{
|
||||
int err;
|
||||
int event_capable = wmi_has_guid(HPWMI_EVENT_GUID);
|
||||
int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID);
|
||||
|
||||
if (!bios_capable && !event_capable)
|
||||
return -ENODEV;
|
||||
|
||||
if (event_capable) {
|
||||
err = hp_wmi_input_setup();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
//Enable magic for hotkeys that run on the SMBus
|
||||
ec_write(0xe6,0x6e);
|
||||
}
|
||||
|
||||
if (bios_capable) {
|
||||
err = platform_driver_register(&hp_wmi_driver);
|
||||
if (err)
|
||||
goto err_driver_reg;
|
||||
hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1);
|
||||
if (!hp_wmi_platform_dev) {
|
||||
err = -ENOMEM;
|
||||
goto err_device_alloc;
|
||||
hp_wmi_platform_dev =
|
||||
platform_device_register_simple("hp-wmi", -1, NULL, 0);
|
||||
if (IS_ERR(hp_wmi_platform_dev)) {
|
||||
err = PTR_ERR(hp_wmi_platform_dev);
|
||||
goto err_destroy_input;
|
||||
}
|
||||
err = platform_device_add(hp_wmi_platform_dev);
|
||||
if (err)
|
||||
goto err_device_add;
|
||||
}
|
||||
|
||||
if (!bios_capable && !event_capable)
|
||||
return -ENODEV;
|
||||
err = platform_driver_probe(&hp_wmi_driver, hp_wmi_bios_setup);
|
||||
if (err)
|
||||
goto err_unregister_device;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_device_add:
|
||||
platform_device_put(hp_wmi_platform_dev);
|
||||
err_device_alloc:
|
||||
platform_driver_unregister(&hp_wmi_driver);
|
||||
err_driver_reg:
|
||||
err_unregister_device:
|
||||
platform_device_unregister(hp_wmi_platform_dev);
|
||||
err_destroy_input:
|
||||
if (event_capable)
|
||||
hp_wmi_input_destroy();
|
||||
|
||||
return err;
|
||||
}
|
||||
module_init(hp_wmi_init);
|
||||
|
||||
static void __exit hp_wmi_exit(void)
|
||||
{
|
||||
@ -926,6 +965,4 @@ static void __exit hp_wmi_exit(void)
|
||||
platform_driver_unregister(&hp_wmi_driver);
|
||||
}
|
||||
}
|
||||
|
||||
module_init(hp_wmi_init);
|
||||
module_exit(hp_wmi_exit);
|
||||
|
@ -82,8 +82,19 @@
|
||||
#define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d
|
||||
#define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0)
|
||||
|
||||
#define MSI_STANDARD_EC_TOUCHPAD_ADDRESS 0xe4
|
||||
#define MSI_STANDARD_EC_FUNCTIONS_ADDRESS 0xe4
|
||||
/* Power LED is orange - Turbo mode */
|
||||
#define MSI_STANDARD_EC_TURBO_MASK (1 << 1)
|
||||
/* Power LED is green - ECO mode */
|
||||
#define MSI_STANDARD_EC_ECO_MASK (1 << 3)
|
||||
/* Touchpad is turned on */
|
||||
#define MSI_STANDARD_EC_TOUCHPAD_MASK (1 << 4)
|
||||
/* If this bit != bit 1, turbo mode can't be toggled */
|
||||
#define MSI_STANDARD_EC_TURBO_COOLDOWN_MASK (1 << 7)
|
||||
|
||||
#define MSI_STANDARD_EC_FAN_ADDRESS 0x33
|
||||
/* If zero, fan rotates at maximal speed */
|
||||
#define MSI_STANDARD_EC_AUTOFAN_MASK (1 << 0)
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int msi_laptop_resume(struct device *device);
|
||||
@ -108,23 +119,38 @@ static const struct key_entry msi_laptop_keymap[] = {
|
||||
|
||||
static struct input_dev *msi_laptop_input_dev;
|
||||
|
||||
static bool old_ec_model;
|
||||
static int wlan_s, bluetooth_s, threeg_s;
|
||||
static int threeg_exists;
|
||||
|
||||
/* Some MSI 3G netbook only have one fn key to control Wlan/Bluetooth/3G,
|
||||
* those netbook will load the SCM (windows app) to disable the original
|
||||
* Wlan/Bluetooth control by BIOS when user press fn key, then control
|
||||
* Wlan/Bluetooth/3G by SCM (software control by OS). Without SCM, user
|
||||
* cann't on/off 3G module on those 3G netbook.
|
||||
* On Linux, msi-laptop driver will do the same thing to disable the
|
||||
* original BIOS control, then might need use HAL or other userland
|
||||
* application to do the software control that simulate with SCM.
|
||||
* e.g. MSI N034 netbook
|
||||
*/
|
||||
static bool load_scm_model;
|
||||
static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg;
|
||||
|
||||
/* MSI laptop quirks */
|
||||
struct quirk_entry {
|
||||
bool old_ec_model;
|
||||
|
||||
/* Some MSI 3G netbook only have one fn key to control
|
||||
* Wlan/Bluetooth/3G, those netbook will load the SCM (windows app) to
|
||||
* disable the original Wlan/Bluetooth control by BIOS when user press
|
||||
* fn key, then control Wlan/Bluetooth/3G by SCM (software control by
|
||||
* OS). Without SCM, user cann't on/off 3G module on those 3G netbook.
|
||||
* On Linux, msi-laptop driver will do the same thing to disable the
|
||||
* original BIOS control, then might need use HAL or other userland
|
||||
* application to do the software control that simulate with SCM.
|
||||
* e.g. MSI N034 netbook
|
||||
*/
|
||||
bool load_scm_model;
|
||||
|
||||
/* Some MSI laptops need delay before reading from EC */
|
||||
bool ec_delay;
|
||||
|
||||
/* Some MSI Wind netbooks (e.g. MSI Wind U100) need loading SCM to get
|
||||
* some features working (e.g. ECO mode), but we cannot change
|
||||
* Wlan/Bluetooth state in software and we can only read its state.
|
||||
*/
|
||||
bool ec_read_only;
|
||||
};
|
||||
|
||||
static struct quirk_entry *quirks;
|
||||
|
||||
/* Hardware access */
|
||||
|
||||
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)
|
||||
if (sscanf(buf, "%i", &status) != 1 || (status < 0 || status > 1))
|
||||
return -EINVAL;
|
||||
|
||||
if (quirks->ec_read_only)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* read current device state */
|
||||
result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
|
||||
if (result < 0)
|
||||
return -EINVAL;
|
||||
return result;
|
||||
|
||||
if (!!(rdata & mask) != status) {
|
||||
/* reverse device bit */
|
||||
@ -209,7 +238,7 @@ static ssize_t set_device_state(const char *buf, size_t count, u8 mask)
|
||||
|
||||
result = ec_write(MSI_STANDARD_EC_COMMAND_ADDRESS, wdata);
|
||||
if (result < 0)
|
||||
return -EINVAL;
|
||||
return result;
|
||||
}
|
||||
|
||||
return count;
|
||||
@ -222,7 +251,7 @@ static int get_wireless_state(int *wlan, int *bluetooth)
|
||||
|
||||
result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1);
|
||||
if (result < 0)
|
||||
return -1;
|
||||
return result;
|
||||
|
||||
if (wlan)
|
||||
*wlan = !!(rdata & 8);
|
||||
@ -240,7 +269,7 @@ static int get_wireless_state_ec_standard(void)
|
||||
|
||||
result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
|
||||
if (result < 0)
|
||||
return -1;
|
||||
return result;
|
||||
|
||||
wlan_s = !!(rdata & MSI_STANDARD_EC_WLAN_MASK);
|
||||
|
||||
@ -258,7 +287,7 @@ static int get_threeg_exists(void)
|
||||
|
||||
result = ec_read(MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS, &rdata);
|
||||
if (result < 0)
|
||||
return -1;
|
||||
return result;
|
||||
|
||||
threeg_exists = !!(rdata & MSI_STANDARD_EC_3G_MASK);
|
||||
|
||||
@ -291,9 +320,9 @@ static ssize_t show_wlan(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
||||
int ret, enabled;
|
||||
int ret, enabled = 0;
|
||||
|
||||
if (old_ec_model) {
|
||||
if (quirks->old_ec_model) {
|
||||
ret = get_wireless_state(&enabled, NULL);
|
||||
} else {
|
||||
ret = get_wireless_state_ec_standard();
|
||||
@ -315,9 +344,9 @@ static ssize_t show_bluetooth(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
||||
int ret, enabled;
|
||||
int ret, enabled = 0;
|
||||
|
||||
if (old_ec_model) {
|
||||
if (quirks->old_ec_model) {
|
||||
ret = get_wireless_state(NULL, &enabled);
|
||||
} else {
|
||||
ret = get_wireless_state_ec_standard();
|
||||
@ -342,8 +371,8 @@ static ssize_t show_threeg(struct device *dev,
|
||||
int ret;
|
||||
|
||||
/* old msi ec not support 3G */
|
||||
if (old_ec_model)
|
||||
return -1;
|
||||
if (quirks->old_ec_model)
|
||||
return -ENODEV;
|
||||
|
||||
ret = get_wireless_state_ec_standard();
|
||||
if (ret < 0)
|
||||
@ -417,18 +446,119 @@ static ssize_t store_auto_brightness(struct device *dev,
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_touchpad(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
||||
u8 rdata;
|
||||
int result;
|
||||
|
||||
result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_TOUCHPAD_MASK));
|
||||
}
|
||||
|
||||
static ssize_t show_turbo(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
||||
u8 rdata;
|
||||
int result;
|
||||
|
||||
result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_TURBO_MASK));
|
||||
}
|
||||
|
||||
static ssize_t show_eco(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
||||
u8 rdata;
|
||||
int result;
|
||||
|
||||
result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_ECO_MASK));
|
||||
}
|
||||
|
||||
static ssize_t show_turbo_cooldown(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
||||
u8 rdata;
|
||||
int result;
|
||||
|
||||
result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
return sprintf(buf, "%i\n", (!!(rdata & MSI_STANDARD_EC_TURBO_MASK)) |
|
||||
(!!(rdata & MSI_STANDARD_EC_TURBO_COOLDOWN_MASK) << 1));
|
||||
}
|
||||
|
||||
static ssize_t show_auto_fan(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
||||
u8 rdata;
|
||||
int result;
|
||||
|
||||
result = ec_read(MSI_STANDARD_EC_FAN_ADDRESS, &rdata);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_AUTOFAN_MASK));
|
||||
}
|
||||
|
||||
static ssize_t store_auto_fan(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
|
||||
int enable, result;
|
||||
|
||||
if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1)))
|
||||
return -EINVAL;
|
||||
|
||||
result = ec_write(MSI_STANDARD_EC_FAN_ADDRESS, enable);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
|
||||
static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness,
|
||||
store_auto_brightness);
|
||||
static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL);
|
||||
static DEVICE_ATTR(wlan, 0444, show_wlan, NULL);
|
||||
static DEVICE_ATTR(threeg, 0444, show_threeg, NULL);
|
||||
static DEVICE_ATTR(touchpad, 0444, show_touchpad, NULL);
|
||||
static DEVICE_ATTR(turbo_mode, 0444, show_turbo, NULL);
|
||||
static DEVICE_ATTR(eco_mode, 0444, show_eco, NULL);
|
||||
static DEVICE_ATTR(turbo_cooldown, 0444, show_turbo_cooldown, NULL);
|
||||
static DEVICE_ATTR(auto_fan, 0644, show_auto_fan, store_auto_fan);
|
||||
|
||||
static struct attribute *msipf_attributes[] = {
|
||||
&dev_attr_lcd_level.attr,
|
||||
&dev_attr_auto_brightness.attr,
|
||||
&dev_attr_bluetooth.attr,
|
||||
&dev_attr_wlan.attr,
|
||||
&dev_attr_touchpad.attr,
|
||||
&dev_attr_turbo_mode.attr,
|
||||
&dev_attr_eco_mode.attr,
|
||||
&dev_attr_turbo_cooldown.attr,
|
||||
&dev_attr_auto_fan.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute *msipf_old_attributes[] = {
|
||||
&dev_attr_lcd_level.attr,
|
||||
&dev_attr_auto_brightness.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -436,6 +566,10 @@ static struct attribute_group msipf_attribute_group = {
|
||||
.attrs = msipf_attributes
|
||||
};
|
||||
|
||||
static struct attribute_group msipf_old_attribute_group = {
|
||||
.attrs = msipf_old_attributes
|
||||
};
|
||||
|
||||
static struct platform_driver msipf_driver = {
|
||||
.driver = {
|
||||
.name = "msi-laptop-pf",
|
||||
@ -448,9 +582,26 @@ static struct platform_device *msipf_device;
|
||||
|
||||
/* Initialization */
|
||||
|
||||
static int dmi_check_cb(const struct dmi_system_id *id)
|
||||
static struct quirk_entry quirk_old_ec_model = {
|
||||
.old_ec_model = true,
|
||||
};
|
||||
|
||||
static struct quirk_entry quirk_load_scm_model = {
|
||||
.load_scm_model = true,
|
||||
.ec_delay = true,
|
||||
};
|
||||
|
||||
static struct quirk_entry quirk_load_scm_ro_model = {
|
||||
.load_scm_model = true,
|
||||
.ec_read_only = true,
|
||||
};
|
||||
|
||||
static int dmi_check_cb(const struct dmi_system_id *dmi)
|
||||
{
|
||||
pr_info("Identified laptop model '%s'\n", id->ident);
|
||||
pr_info("Identified laptop model '%s'\n", dmi->ident);
|
||||
|
||||
quirks = dmi->driver_data;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -464,6 +615,7 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {
|
||||
DMI_MATCH(DMI_CHASSIS_VENDOR,
|
||||
"MICRO-STAR INT'L CO.,LTD")
|
||||
},
|
||||
.driver_data = &quirk_old_ec_model,
|
||||
.callback = dmi_check_cb
|
||||
},
|
||||
{
|
||||
@ -474,6 +626,7 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "0581"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "MS-1058")
|
||||
},
|
||||
.driver_data = &quirk_old_ec_model,
|
||||
.callback = dmi_check_cb
|
||||
},
|
||||
{
|
||||
@ -484,6 +637,7 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "MS-1412")
|
||||
},
|
||||
.driver_data = &quirk_old_ec_model,
|
||||
.callback = dmi_check_cb
|
||||
},
|
||||
{
|
||||
@ -495,12 +649,9 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {
|
||||
DMI_MATCH(DMI_CHASSIS_VENDOR,
|
||||
"MICRO-STAR INT'L CO.,LTD")
|
||||
},
|
||||
.driver_data = &quirk_old_ec_model,
|
||||
.callback = dmi_check_cb
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
|
||||
{
|
||||
.ident = "MSI N034",
|
||||
.matches = {
|
||||
@ -510,6 +661,7 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
|
||||
DMI_MATCH(DMI_CHASSIS_VENDOR,
|
||||
"MICRO-STAR INTERNATIONAL CO., LTD")
|
||||
},
|
||||
.driver_data = &quirk_load_scm_model,
|
||||
.callback = dmi_check_cb
|
||||
},
|
||||
{
|
||||
@ -521,6 +673,7 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
|
||||
DMI_MATCH(DMI_CHASSIS_VENDOR,
|
||||
"MICRO-STAR INTERNATIONAL CO., LTD")
|
||||
},
|
||||
.driver_data = &quirk_load_scm_model,
|
||||
.callback = dmi_check_cb
|
||||
},
|
||||
{
|
||||
@ -530,6 +683,7 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
|
||||
"MICRO-STAR INTERNATIONAL CO., LTD"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "MS-N014"),
|
||||
},
|
||||
.driver_data = &quirk_load_scm_model,
|
||||
.callback = dmi_check_cb
|
||||
},
|
||||
{
|
||||
@ -539,6 +693,7 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
|
||||
"Micro-Star International"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "CR620"),
|
||||
},
|
||||
.driver_data = &quirk_load_scm_model,
|
||||
.callback = dmi_check_cb
|
||||
},
|
||||
{
|
||||
@ -548,6 +703,17 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
|
||||
"Micro-Star International Co., Ltd."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "U270 series"),
|
||||
},
|
||||
.driver_data = &quirk_load_scm_model,
|
||||
.callback = dmi_check_cb
|
||||
},
|
||||
{
|
||||
.ident = "MSI U90/U100",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR,
|
||||
"MICRO-STAR INTERNATIONAL CO., LTD"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "U90/U100"),
|
||||
},
|
||||
.driver_data = &quirk_load_scm_ro_model,
|
||||
.callback = dmi_check_cb
|
||||
},
|
||||
{ }
|
||||
@ -560,32 +726,26 @@ static int rfkill_bluetooth_set(void *data, bool blocked)
|
||||
* blocked == false is on
|
||||
* blocked == true is off
|
||||
*/
|
||||
if (blocked)
|
||||
set_device_state("0", 0, MSI_STANDARD_EC_BLUETOOTH_MASK);
|
||||
else
|
||||
set_device_state("1", 0, MSI_STANDARD_EC_BLUETOOTH_MASK);
|
||||
int result = set_device_state(blocked ? "0" : "1", 0,
|
||||
MSI_STANDARD_EC_BLUETOOTH_MASK);
|
||||
|
||||
return 0;
|
||||
return min(result, 0);
|
||||
}
|
||||
|
||||
static int rfkill_wlan_set(void *data, bool blocked)
|
||||
{
|
||||
if (blocked)
|
||||
set_device_state("0", 0, MSI_STANDARD_EC_WLAN_MASK);
|
||||
else
|
||||
set_device_state("1", 0, MSI_STANDARD_EC_WLAN_MASK);
|
||||
int result = set_device_state(blocked ? "0" : "1", 0,
|
||||
MSI_STANDARD_EC_WLAN_MASK);
|
||||
|
||||
return 0;
|
||||
return min(result, 0);
|
||||
}
|
||||
|
||||
static int rfkill_threeg_set(void *data, bool blocked)
|
||||
{
|
||||
if (blocked)
|
||||
set_device_state("0", 0, MSI_STANDARD_EC_3G_MASK);
|
||||
else
|
||||
set_device_state("1", 0, MSI_STANDARD_EC_3G_MASK);
|
||||
int result = set_device_state(blocked ? "0" : "1", 0,
|
||||
MSI_STANDARD_EC_3G_MASK);
|
||||
|
||||
return 0;
|
||||
return min(result, 0);
|
||||
}
|
||||
|
||||
static const struct rfkill_ops rfkill_bluetooth_ops = {
|
||||
@ -618,25 +778,34 @@ static void rfkill_cleanup(void)
|
||||
}
|
||||
}
|
||||
|
||||
static bool msi_rfkill_set_state(struct rfkill *rfkill, bool blocked)
|
||||
{
|
||||
if (quirks->ec_read_only)
|
||||
return rfkill_set_hw_state(rfkill, blocked);
|
||||
else
|
||||
return rfkill_set_sw_state(rfkill, blocked);
|
||||
}
|
||||
|
||||
static void msi_update_rfkill(struct work_struct *ignored)
|
||||
{
|
||||
get_wireless_state_ec_standard();
|
||||
|
||||
if (rfk_wlan)
|
||||
rfkill_set_sw_state(rfk_wlan, !wlan_s);
|
||||
msi_rfkill_set_state(rfk_wlan, !wlan_s);
|
||||
if (rfk_bluetooth)
|
||||
rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s);
|
||||
msi_rfkill_set_state(rfk_bluetooth, !bluetooth_s);
|
||||
if (rfk_threeg)
|
||||
rfkill_set_sw_state(rfk_threeg, !threeg_s);
|
||||
msi_rfkill_set_state(rfk_threeg, !threeg_s);
|
||||
}
|
||||
static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill);
|
||||
static DECLARE_DELAYED_WORK(msi_rfkill_dwork, msi_update_rfkill);
|
||||
static DECLARE_WORK(msi_rfkill_work, msi_update_rfkill);
|
||||
|
||||
static void msi_send_touchpad_key(struct work_struct *ignored)
|
||||
{
|
||||
u8 rdata;
|
||||
int result;
|
||||
|
||||
result = ec_read(MSI_STANDARD_EC_TOUCHPAD_ADDRESS, &rdata);
|
||||
result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
|
||||
if (result < 0)
|
||||
return;
|
||||
|
||||
@ -644,7 +813,8 @@ static void msi_send_touchpad_key(struct work_struct *ignored)
|
||||
(rdata & MSI_STANDARD_EC_TOUCHPAD_MASK) ?
|
||||
KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF, 1, true);
|
||||
}
|
||||
static DECLARE_DELAYED_WORK(msi_touchpad_work, msi_send_touchpad_key);
|
||||
static DECLARE_DELAYED_WORK(msi_touchpad_dwork, msi_send_touchpad_key);
|
||||
static DECLARE_WORK(msi_touchpad_work, msi_send_touchpad_key);
|
||||
|
||||
static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
|
||||
struct serio *port)
|
||||
@ -662,14 +832,20 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
|
||||
extended = false;
|
||||
switch (data) {
|
||||
case 0xE4:
|
||||
schedule_delayed_work(&msi_touchpad_work,
|
||||
round_jiffies_relative(0.5 * HZ));
|
||||
if (quirks->ec_delay) {
|
||||
schedule_delayed_work(&msi_touchpad_dwork,
|
||||
round_jiffies_relative(0.5 * HZ));
|
||||
} else
|
||||
schedule_work(&msi_touchpad_work);
|
||||
break;
|
||||
case 0x54:
|
||||
case 0x62:
|
||||
case 0x76:
|
||||
schedule_delayed_work(&msi_rfkill_work,
|
||||
round_jiffies_relative(0.5 * HZ));
|
||||
if (quirks->ec_delay) {
|
||||
schedule_delayed_work(&msi_rfkill_dwork,
|
||||
round_jiffies_relative(0.5 * HZ));
|
||||
} else
|
||||
schedule_work(&msi_rfkill_work);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -736,8 +912,11 @@ static int rfkill_init(struct platform_device *sdev)
|
||||
}
|
||||
|
||||
/* schedule to run rfkill state initial */
|
||||
schedule_delayed_work(&msi_rfkill_init,
|
||||
round_jiffies_relative(1 * HZ));
|
||||
if (quirks->ec_delay) {
|
||||
schedule_delayed_work(&msi_rfkill_init,
|
||||
round_jiffies_relative(1 * HZ));
|
||||
} else
|
||||
schedule_work(&msi_rfkill_work);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -761,7 +940,7 @@ static int msi_laptop_resume(struct device *device)
|
||||
u8 data;
|
||||
int result;
|
||||
|
||||
if (!load_scm_model)
|
||||
if (!quirks->load_scm_model)
|
||||
return 0;
|
||||
|
||||
/* 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)
|
||||
u8 data;
|
||||
int result;
|
||||
|
||||
/* allow userland write sysfs file */
|
||||
dev_attr_bluetooth.store = store_bluetooth;
|
||||
dev_attr_wlan.store = store_wlan;
|
||||
dev_attr_threeg.store = store_threeg;
|
||||
dev_attr_bluetooth.attr.mode |= S_IWUSR;
|
||||
dev_attr_wlan.attr.mode |= S_IWUSR;
|
||||
dev_attr_threeg.attr.mode |= S_IWUSR;
|
||||
if (!quirks->ec_read_only) {
|
||||
/* allow userland write sysfs file */
|
||||
dev_attr_bluetooth.store = store_bluetooth;
|
||||
dev_attr_wlan.store = store_wlan;
|
||||
dev_attr_threeg.store = store_threeg;
|
||||
dev_attr_bluetooth.attr.mode |= S_IWUSR;
|
||||
dev_attr_wlan.attr.mode |= S_IWUSR;
|
||||
dev_attr_threeg.attr.mode |= S_IWUSR;
|
||||
}
|
||||
|
||||
/* disable hardware control by fn key */
|
||||
result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data);
|
||||
@ -874,21 +1055,22 @@ static int __init msi_init(void)
|
||||
if (acpi_disabled)
|
||||
return -ENODEV;
|
||||
|
||||
if (force || dmi_check_system(msi_dmi_table))
|
||||
old_ec_model = 1;
|
||||
dmi_check_system(msi_dmi_table);
|
||||
if (!quirks)
|
||||
/* quirks may be NULL if no match in DMI table */
|
||||
quirks = &quirk_load_scm_model;
|
||||
if (force)
|
||||
quirks = &quirk_old_ec_model;
|
||||
|
||||
if (!old_ec_model)
|
||||
if (!quirks->old_ec_model)
|
||||
get_threeg_exists();
|
||||
|
||||
if (!old_ec_model && dmi_check_system(msi_load_scm_models_dmi_table))
|
||||
load_scm_model = 1;
|
||||
|
||||
if (auto_brightness < 0 || auto_brightness > 2)
|
||||
return -EINVAL;
|
||||
|
||||
/* Register backlight stuff */
|
||||
|
||||
if (acpi_video_backlight_support()) {
|
||||
if (!quirks->old_ec_model || acpi_video_backlight_support()) {
|
||||
pr_info("Brightness ignored, must be controlled by ACPI video driver\n");
|
||||
} else {
|
||||
struct backlight_properties props;
|
||||
@ -918,7 +1100,7 @@ static int __init msi_init(void)
|
||||
if (ret)
|
||||
goto fail_platform_device1;
|
||||
|
||||
if (load_scm_model && (load_scm_model_init(msipf_device) < 0)) {
|
||||
if (quirks->load_scm_model && (load_scm_model_init(msipf_device) < 0)) {
|
||||
ret = -EINVAL;
|
||||
goto fail_platform_device1;
|
||||
}
|
||||
@ -928,30 +1110,36 @@ static int __init msi_init(void)
|
||||
if (ret)
|
||||
goto fail_platform_device2;
|
||||
|
||||
if (!old_ec_model) {
|
||||
if (!quirks->old_ec_model) {
|
||||
if (threeg_exists)
|
||||
ret = device_create_file(&msipf_device->dev,
|
||||
&dev_attr_threeg);
|
||||
if (ret)
|
||||
goto fail_platform_device2;
|
||||
} else {
|
||||
ret = sysfs_create_group(&msipf_device->dev.kobj,
|
||||
&msipf_old_attribute_group);
|
||||
if (ret)
|
||||
goto fail_platform_device2;
|
||||
|
||||
/* Disable automatic brightness control by default because
|
||||
* this module was probably loaded to do brightness control in
|
||||
* software. */
|
||||
|
||||
if (auto_brightness != 2)
|
||||
set_auto_brightness(auto_brightness);
|
||||
}
|
||||
|
||||
/* Disable automatic brightness control by default because
|
||||
* this module was probably loaded to do brightness control in
|
||||
* software. */
|
||||
|
||||
if (auto_brightness != 2)
|
||||
set_auto_brightness(auto_brightness);
|
||||
|
||||
pr_info("driver " MSI_DRIVER_VERSION " successfully loaded\n");
|
||||
|
||||
return 0;
|
||||
|
||||
fail_platform_device2:
|
||||
|
||||
if (load_scm_model) {
|
||||
if (quirks->load_scm_model) {
|
||||
i8042_remove_filter(msi_laptop_i8042_filter);
|
||||
cancel_delayed_work_sync(&msi_rfkill_work);
|
||||
cancel_delayed_work_sync(&msi_rfkill_dwork);
|
||||
cancel_work_sync(&msi_rfkill_work);
|
||||
rfkill_cleanup();
|
||||
}
|
||||
platform_device_del(msipf_device);
|
||||
@ -973,23 +1161,26 @@ fail_backlight:
|
||||
|
||||
static void __exit msi_cleanup(void)
|
||||
{
|
||||
if (load_scm_model) {
|
||||
if (quirks->load_scm_model) {
|
||||
i8042_remove_filter(msi_laptop_i8042_filter);
|
||||
msi_laptop_input_destroy();
|
||||
cancel_delayed_work_sync(&msi_rfkill_work);
|
||||
cancel_delayed_work_sync(&msi_rfkill_dwork);
|
||||
cancel_work_sync(&msi_rfkill_work);
|
||||
rfkill_cleanup();
|
||||
}
|
||||
|
||||
sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group);
|
||||
if (!old_ec_model && threeg_exists)
|
||||
if (!quirks->old_ec_model && threeg_exists)
|
||||
device_remove_file(&msipf_device->dev, &dev_attr_threeg);
|
||||
platform_device_unregister(msipf_device);
|
||||
platform_driver_unregister(&msipf_driver);
|
||||
backlight_device_unregister(msibl_device);
|
||||
|
||||
/* Enable automatic brightness control again */
|
||||
if (auto_brightness != 2)
|
||||
set_auto_brightness(1);
|
||||
if (quirks->old_ec_model) {
|
||||
/* Enable automatic brightness control again */
|
||||
if (auto_brightness != 2)
|
||||
set_auto_brightness(1);
|
||||
}
|
||||
|
||||
pr_info("driver unloaded\n");
|
||||
}
|
||||
@ -1011,3 +1202,4 @@ MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N051:*");
|
||||
MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*");
|
||||
MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*");
|
||||
MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnU270series:*");
|
||||
MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnU90/U100:*");
|
||||
|
@ -34,29 +34,65 @@ MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>");
|
||||
MODULE_DESCRIPTION("MSI laptop WMI hotkeys driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
MODULE_ALIAS("wmi:551A1F84-FBDD-4125-91DB-3EA8F44F1D45");
|
||||
MODULE_ALIAS("wmi:B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2");
|
||||
|
||||
#define DRV_NAME "msi-wmi"
|
||||
|
||||
#define MSIWMI_BIOS_GUID "551A1F84-FBDD-4125-91DB-3EA8F44F1D45"
|
||||
#define MSIWMI_EVENT_GUID "B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2"
|
||||
#define MSIWMI_MSI_EVENT_GUID "B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2"
|
||||
#define MSIWMI_WIND_EVENT_GUID "5B3CC38A-40D9-7245-8AE6-1145B751BE3F"
|
||||
|
||||
#define SCANCODE_BASE 0xD0
|
||||
#define MSI_WMI_BRIGHTNESSUP SCANCODE_BASE
|
||||
#define MSI_WMI_BRIGHTNESSDOWN (SCANCODE_BASE + 1)
|
||||
#define MSI_WMI_VOLUMEUP (SCANCODE_BASE + 2)
|
||||
#define MSI_WMI_VOLUMEDOWN (SCANCODE_BASE + 3)
|
||||
#define MSI_WMI_MUTE (SCANCODE_BASE + 4)
|
||||
static struct key_entry msi_wmi_keymap[] = {
|
||||
{ KE_KEY, MSI_WMI_BRIGHTNESSUP, {KEY_BRIGHTNESSUP} },
|
||||
{ KE_KEY, MSI_WMI_BRIGHTNESSDOWN, {KEY_BRIGHTNESSDOWN} },
|
||||
{ KE_KEY, MSI_WMI_VOLUMEUP, {KEY_VOLUMEUP} },
|
||||
{ KE_KEY, MSI_WMI_VOLUMEDOWN, {KEY_VOLUMEDOWN} },
|
||||
{ KE_KEY, MSI_WMI_MUTE, {KEY_MUTE} },
|
||||
{ KE_END, 0}
|
||||
MODULE_ALIAS("wmi:" MSIWMI_BIOS_GUID);
|
||||
MODULE_ALIAS("wmi:" MSIWMI_MSI_EVENT_GUID);
|
||||
MODULE_ALIAS("wmi:" MSIWMI_WIND_EVENT_GUID);
|
||||
|
||||
enum msi_scancodes {
|
||||
/* Generic MSI keys (not present on MSI Wind) */
|
||||
MSI_KEY_BRIGHTNESSUP = 0xD0,
|
||||
MSI_KEY_BRIGHTNESSDOWN,
|
||||
MSI_KEY_VOLUMEUP,
|
||||
MSI_KEY_VOLUMEDOWN,
|
||||
MSI_KEY_MUTE,
|
||||
/* MSI Wind keys */
|
||||
WIND_KEY_TOUCHPAD = 0x08, /* Fn+F3 touchpad toggle */
|
||||
WIND_KEY_BLUETOOTH = 0x56, /* Fn+F11 Bluetooth toggle */
|
||||
WIND_KEY_CAMERA, /* Fn+F6 webcam toggle */
|
||||
WIND_KEY_WLAN = 0x5f, /* Fn+F11 Wi-Fi toggle */
|
||||
WIND_KEY_TURBO, /* Fn+F10 turbo mode toggle */
|
||||
WIND_KEY_ECO = 0x69, /* Fn+F10 ECO mode toggle */
|
||||
};
|
||||
static struct key_entry msi_wmi_keymap[] = {
|
||||
{ KE_KEY, MSI_KEY_BRIGHTNESSUP, {KEY_BRIGHTNESSUP} },
|
||||
{ KE_KEY, MSI_KEY_BRIGHTNESSDOWN, {KEY_BRIGHTNESSDOWN} },
|
||||
{ KE_KEY, MSI_KEY_VOLUMEUP, {KEY_VOLUMEUP} },
|
||||
{ KE_KEY, MSI_KEY_VOLUMEDOWN, {KEY_VOLUMEDOWN} },
|
||||
{ KE_KEY, MSI_KEY_MUTE, {KEY_MUTE} },
|
||||
|
||||
/* These keys work without WMI. Ignore them to avoid double keycodes */
|
||||
{ KE_IGNORE, WIND_KEY_TOUCHPAD, {KEY_TOUCHPAD_TOGGLE} },
|
||||
{ KE_IGNORE, WIND_KEY_BLUETOOTH, {KEY_BLUETOOTH} },
|
||||
{ KE_IGNORE, WIND_KEY_CAMERA, {KEY_CAMERA} },
|
||||
{ KE_IGNORE, WIND_KEY_WLAN, {KEY_WLAN} },
|
||||
|
||||
/* These are unknown WMI events found on MSI Wind */
|
||||
{ KE_IGNORE, 0x00 },
|
||||
{ KE_IGNORE, 0x62 },
|
||||
{ KE_IGNORE, 0x63 },
|
||||
|
||||
/* These are MSI Wind keys that should be handled via WMI */
|
||||
{ KE_KEY, WIND_KEY_TURBO, {KEY_PROG1} },
|
||||
{ KE_KEY, WIND_KEY_ECO, {KEY_PROG2} },
|
||||
|
||||
{ KE_END, 0 }
|
||||
};
|
||||
|
||||
static ktime_t last_pressed;
|
||||
|
||||
static const struct {
|
||||
const char *guid;
|
||||
bool quirk_last_pressed;
|
||||
} *event_wmi, event_wmis[] = {
|
||||
{ MSIWMI_MSI_EVENT_GUID, true },
|
||||
{ MSIWMI_WIND_EVENT_GUID, false },
|
||||
};
|
||||
static ktime_t last_pressed[ARRAY_SIZE(msi_wmi_keymap) - 1];
|
||||
|
||||
static struct backlight_device *backlight;
|
||||
|
||||
@ -149,7 +185,6 @@ static void msi_wmi_notify(u32 value, void *context)
|
||||
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
static struct key_entry *key;
|
||||
union acpi_object *obj;
|
||||
ktime_t cur;
|
||||
acpi_status status;
|
||||
|
||||
status = wmi_get_event_data(value, &response);
|
||||
@ -165,39 +200,67 @@ static void msi_wmi_notify(u32 value, void *context)
|
||||
pr_debug("Eventcode: 0x%x\n", eventcode);
|
||||
key = sparse_keymap_entry_from_scancode(msi_wmi_input_dev,
|
||||
eventcode);
|
||||
if (key) {
|
||||
ktime_t diff;
|
||||
cur = ktime_get_real();
|
||||
diff = ktime_sub(cur, last_pressed[key->code -
|
||||
SCANCODE_BASE]);
|
||||
/* Ignore event if the same event happened in a 50 ms
|
||||
if (!key) {
|
||||
pr_info("Unknown key pressed - %x\n", eventcode);
|
||||
goto msi_wmi_notify_exit;
|
||||
}
|
||||
|
||||
if (event_wmi->quirk_last_pressed) {
|
||||
ktime_t cur = ktime_get_real();
|
||||
ktime_t diff = ktime_sub(cur, last_pressed);
|
||||
/* Ignore event if any event happened in a 50 ms
|
||||
timeframe -> Key press may result in 10-20 GPEs */
|
||||
if (ktime_to_us(diff) < 1000 * 50) {
|
||||
pr_debug("Suppressed key event 0x%X - "
|
||||
"Last press was %lld us ago\n",
|
||||
key->code, ktime_to_us(diff));
|
||||
return;
|
||||
goto msi_wmi_notify_exit;
|
||||
}
|
||||
last_pressed[key->code - SCANCODE_BASE] = cur;
|
||||
last_pressed = cur;
|
||||
}
|
||||
|
||||
if (key->type == KE_KEY &&
|
||||
/* Brightness is served via acpi video driver */
|
||||
(!acpi_video_backlight_support() ||
|
||||
(key->code != MSI_WMI_BRIGHTNESSUP &&
|
||||
key->code != MSI_WMI_BRIGHTNESSDOWN))) {
|
||||
pr_debug("Send key: 0x%X - "
|
||||
"Input layer keycode: %d\n",
|
||||
key->code, key->keycode);
|
||||
sparse_keymap_report_entry(msi_wmi_input_dev,
|
||||
key, 1, true);
|
||||
}
|
||||
} else
|
||||
pr_info("Unknown key pressed - %x\n", eventcode);
|
||||
if (key->type == KE_KEY &&
|
||||
/* Brightness is served via acpi video driver */
|
||||
(backlight ||
|
||||
(key->code != MSI_KEY_BRIGHTNESSUP &&
|
||||
key->code != MSI_KEY_BRIGHTNESSDOWN))) {
|
||||
pr_debug("Send key: 0x%X - Input layer keycode: %d\n",
|
||||
key->code, key->keycode);
|
||||
sparse_keymap_report_entry(msi_wmi_input_dev, key, 1,
|
||||
true);
|
||||
}
|
||||
} else
|
||||
pr_info("Unknown event received\n");
|
||||
|
||||
msi_wmi_notify_exit:
|
||||
kfree(response.pointer);
|
||||
}
|
||||
|
||||
static int __init msi_wmi_backlight_setup(void)
|
||||
{
|
||||
int err;
|
||||
struct backlight_properties props;
|
||||
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.type = BACKLIGHT_PLATFORM;
|
||||
props.max_brightness = ARRAY_SIZE(backlight_map) - 1;
|
||||
backlight = backlight_device_register(DRV_NAME, NULL, NULL,
|
||||
&msi_backlight_ops,
|
||||
&props);
|
||||
if (IS_ERR(backlight))
|
||||
return PTR_ERR(backlight);
|
||||
|
||||
err = bl_get(NULL);
|
||||
if (err < 0) {
|
||||
backlight_device_unregister(backlight);
|
||||
return err;
|
||||
}
|
||||
|
||||
backlight->props.brightness = err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init msi_wmi_input_setup(void)
|
||||
{
|
||||
int err;
|
||||
@ -219,7 +282,7 @@ static int __init msi_wmi_input_setup(void)
|
||||
if (err)
|
||||
goto err_free_keymap;
|
||||
|
||||
memset(last_pressed, 0, sizeof(last_pressed));
|
||||
last_pressed = ktime_set(0, 0);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -233,61 +296,66 @@ err_free_dev:
|
||||
static int __init msi_wmi_init(void)
|
||||
{
|
||||
int err;
|
||||
int i;
|
||||
|
||||
if (!wmi_has_guid(MSIWMI_EVENT_GUID)) {
|
||||
pr_err("This machine doesn't have MSI-hotkeys through WMI\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
err = wmi_install_notify_handler(MSIWMI_EVENT_GUID,
|
||||
for (i = 0; i < ARRAY_SIZE(event_wmis); i++) {
|
||||
if (!wmi_has_guid(event_wmis[i].guid))
|
||||
continue;
|
||||
|
||||
err = msi_wmi_input_setup();
|
||||
if (err) {
|
||||
pr_err("Unable to setup input device\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = wmi_install_notify_handler(event_wmis[i].guid,
|
||||
msi_wmi_notify, NULL);
|
||||
if (ACPI_FAILURE(err))
|
||||
return -EINVAL;
|
||||
|
||||
err = msi_wmi_input_setup();
|
||||
if (err)
|
||||
goto err_uninstall_notifier;
|
||||
|
||||
if (!acpi_video_backlight_support()) {
|
||||
struct backlight_properties props;
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.type = BACKLIGHT_PLATFORM;
|
||||
props.max_brightness = ARRAY_SIZE(backlight_map) - 1;
|
||||
backlight = backlight_device_register(DRV_NAME, NULL, NULL,
|
||||
&msi_backlight_ops,
|
||||
&props);
|
||||
if (IS_ERR(backlight)) {
|
||||
err = PTR_ERR(backlight);
|
||||
if (ACPI_FAILURE(err)) {
|
||||
pr_err("Unable to setup WMI notify handler\n");
|
||||
goto err_free_input;
|
||||
}
|
||||
|
||||
err = bl_get(NULL);
|
||||
if (err < 0)
|
||||
goto err_free_backlight;
|
||||
|
||||
backlight->props.brightness = err;
|
||||
pr_debug("Event handler installed\n");
|
||||
event_wmi = &event_wmis[i];
|
||||
break;
|
||||
}
|
||||
|
||||
if (wmi_has_guid(MSIWMI_BIOS_GUID) && !acpi_video_backlight_support()) {
|
||||
err = msi_wmi_backlight_setup();
|
||||
if (err) {
|
||||
pr_err("Unable to setup backlight device\n");
|
||||
goto err_uninstall_handler;
|
||||
}
|
||||
pr_debug("Backlight device created\n");
|
||||
}
|
||||
|
||||
if (!event_wmi && !backlight) {
|
||||
pr_err("This machine doesn't have neither MSI-hotkeys nor backlight through WMI\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
pr_debug("Event handler installed\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_backlight:
|
||||
backlight_device_unregister(backlight);
|
||||
err_uninstall_handler:
|
||||
if (event_wmi)
|
||||
wmi_remove_notify_handler(event_wmi->guid);
|
||||
err_free_input:
|
||||
sparse_keymap_free(msi_wmi_input_dev);
|
||||
input_unregister_device(msi_wmi_input_dev);
|
||||
err_uninstall_notifier:
|
||||
wmi_remove_notify_handler(MSIWMI_EVENT_GUID);
|
||||
if (event_wmi) {
|
||||
sparse_keymap_free(msi_wmi_input_dev);
|
||||
input_unregister_device(msi_wmi_input_dev);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit msi_wmi_exit(void)
|
||||
{
|
||||
if (wmi_has_guid(MSIWMI_EVENT_GUID)) {
|
||||
wmi_remove_notify_handler(MSIWMI_EVENT_GUID);
|
||||
if (event_wmi) {
|
||||
wmi_remove_notify_handler(event_wmi->guid);
|
||||
sparse_keymap_free(msi_wmi_input_dev);
|
||||
input_unregister_device(msi_wmi_input_dev);
|
||||
backlight_device_unregister(backlight);
|
||||
}
|
||||
if (backlight)
|
||||
backlight_device_unregister(backlight);
|
||||
}
|
||||
|
||||
module_init(msi_wmi_init);
|
||||
|
@ -158,6 +158,11 @@ static void sony_nc_thermal_cleanup(struct platform_device *pd);
|
||||
static int sony_nc_lid_resume_setup(struct platform_device *pd);
|
||||
static void sony_nc_lid_resume_cleanup(struct platform_device *pd);
|
||||
|
||||
static int sony_nc_gfx_switch_setup(struct platform_device *pd,
|
||||
unsigned int handle);
|
||||
static void sony_nc_gfx_switch_cleanup(struct platform_device *pd);
|
||||
static int __sony_nc_gfx_switch_status_get(void);
|
||||
|
||||
static int sony_nc_highspeed_charging_setup(struct platform_device *pd);
|
||||
static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd);
|
||||
|
||||
@ -1241,17 +1246,13 @@ static void sony_nc_notify(struct acpi_device *device, u32 event)
|
||||
/* Hybrid GFX switching */
|
||||
sony_call_snc_handle(handle, 0x0000, &result);
|
||||
dprintk("GFX switch event received (reason: %s)\n",
|
||||
(result & 0x01) ?
|
||||
"switch change" : "unknown");
|
||||
|
||||
/* verify the switch state
|
||||
* 1: discrete GFX
|
||||
* 0: integrated GFX
|
||||
*/
|
||||
sony_call_snc_handle(handle, 0x0100, &result);
|
||||
(result == 0x1) ? "switch change" :
|
||||
(result == 0x2) ? "output switch" :
|
||||
(result == 0x3) ? "output switch" :
|
||||
"");
|
||||
|
||||
ev_type = GFX_SWITCH;
|
||||
real_ev = result & 0xff;
|
||||
real_ev = __sony_nc_gfx_switch_status_get();
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -1350,6 +1351,13 @@ static void sony_nc_function_setup(struct acpi_device *device,
|
||||
pr_err("couldn't set up thermal profile function (%d)\n",
|
||||
result);
|
||||
break;
|
||||
case 0x0128:
|
||||
case 0x0146:
|
||||
result = sony_nc_gfx_switch_setup(pf_device, handle);
|
||||
if (result)
|
||||
pr_err("couldn't set up GFX Switch status (%d)\n",
|
||||
result);
|
||||
break;
|
||||
case 0x0131:
|
||||
result = sony_nc_highspeed_charging_setup(pf_device);
|
||||
if (result)
|
||||
@ -1365,6 +1373,8 @@ static void sony_nc_function_setup(struct acpi_device *device,
|
||||
break;
|
||||
case 0x0137:
|
||||
case 0x0143:
|
||||
case 0x014b:
|
||||
case 0x014c:
|
||||
result = sony_nc_kbd_backlight_setup(pf_device, handle);
|
||||
if (result)
|
||||
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)
|
||||
case 0x0122:
|
||||
sony_nc_thermal_cleanup(pd);
|
||||
break;
|
||||
case 0x0128:
|
||||
case 0x0146:
|
||||
sony_nc_gfx_switch_cleanup(pd);
|
||||
break;
|
||||
case 0x0131:
|
||||
sony_nc_highspeed_charging_cleanup(pd);
|
||||
break;
|
||||
@ -1423,6 +1437,8 @@ static void sony_nc_function_cleanup(struct platform_device *pd)
|
||||
break;
|
||||
case 0x0137:
|
||||
case 0x0143:
|
||||
case 0x014b:
|
||||
case 0x014c:
|
||||
sony_nc_kbd_backlight_cleanup(pd);
|
||||
break;
|
||||
default:
|
||||
@ -1467,6 +1483,8 @@ static void sony_nc_function_resume(void)
|
||||
break;
|
||||
case 0x0137:
|
||||
case 0x0143:
|
||||
case 0x014b:
|
||||
case 0x014c:
|
||||
sony_nc_kbd_backlight_resume();
|
||||
break;
|
||||
default:
|
||||
@ -1534,7 +1552,7 @@ static int sony_nc_rfkill_set(void *data, bool blocked)
|
||||
int argument = sony_rfkill_address[(long) data] + 0x100;
|
||||
|
||||
if (!blocked)
|
||||
argument |= 0x030000;
|
||||
argument |= 0x070000;
|
||||
|
||||
return sony_call_snc_handle(sony_rfkill_handle, argument, &result);
|
||||
}
|
||||
@ -2333,7 +2351,7 @@ static int sony_nc_lid_resume_setup(struct platform_device *pd)
|
||||
return 0;
|
||||
|
||||
liderror:
|
||||
for (; i > 0; i--)
|
||||
for (i--; i >= 0; i--)
|
||||
device_remove_file(&pd->dev, &lid_ctl->attrs[i]);
|
||||
|
||||
kfree(lid_ctl);
|
||||
@ -2355,6 +2373,97 @@ static void sony_nc_lid_resume_cleanup(struct platform_device *pd)
|
||||
}
|
||||
}
|
||||
|
||||
/* GFX Switch position */
|
||||
enum gfx_switch {
|
||||
SPEED,
|
||||
STAMINA,
|
||||
AUTO
|
||||
};
|
||||
struct snc_gfx_switch_control {
|
||||
struct device_attribute attr;
|
||||
unsigned int handle;
|
||||
};
|
||||
static struct snc_gfx_switch_control *gfxs_ctl;
|
||||
|
||||
/* returns 0 for speed, 1 for stamina */
|
||||
static int __sony_nc_gfx_switch_status_get(void)
|
||||
{
|
||||
unsigned int result;
|
||||
|
||||
if (sony_call_snc_handle(gfxs_ctl->handle, 0x0100, &result))
|
||||
return -EIO;
|
||||
|
||||
switch (gfxs_ctl->handle) {
|
||||
case 0x0146:
|
||||
/* 1: discrete GFX (speed)
|
||||
* 0: integrated GFX (stamina)
|
||||
*/
|
||||
return result & 0x1 ? SPEED : STAMINA;
|
||||
break;
|
||||
case 0x0128:
|
||||
/* it's a more elaborated bitmask, for now:
|
||||
* 2: integrated GFX (stamina)
|
||||
* 0: discrete GFX (speed)
|
||||
*/
|
||||
dprintk("GFX Status: 0x%x\n", result);
|
||||
return result & 0x80 ? AUTO :
|
||||
result & 0x02 ? STAMINA : SPEED;
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static ssize_t sony_nc_gfx_switch_status_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buffer)
|
||||
{
|
||||
int pos = __sony_nc_gfx_switch_status_get();
|
||||
|
||||
if (pos < 0)
|
||||
return pos;
|
||||
|
||||
return snprintf(buffer, PAGE_SIZE, "%s\n", pos ? "speed" : "stamina");
|
||||
}
|
||||
|
||||
static int sony_nc_gfx_switch_setup(struct platform_device *pd,
|
||||
unsigned int handle)
|
||||
{
|
||||
unsigned int result;
|
||||
|
||||
gfxs_ctl = kzalloc(sizeof(struct snc_gfx_switch_control), GFP_KERNEL);
|
||||
if (!gfxs_ctl)
|
||||
return -ENOMEM;
|
||||
|
||||
gfxs_ctl->handle = handle;
|
||||
|
||||
sysfs_attr_init(&gfxs_ctl->attr.attr);
|
||||
gfxs_ctl->attr.attr.name = "gfx_switch_status";
|
||||
gfxs_ctl->attr.attr.mode = S_IRUGO;
|
||||
gfxs_ctl->attr.show = sony_nc_gfx_switch_status_show;
|
||||
|
||||
result = device_create_file(&pd->dev, &gfxs_ctl->attr);
|
||||
if (result)
|
||||
goto gfxerror;
|
||||
|
||||
return 0;
|
||||
|
||||
gfxerror:
|
||||
kfree(gfxs_ctl);
|
||||
gfxs_ctl = NULL;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void sony_nc_gfx_switch_cleanup(struct platform_device *pd)
|
||||
{
|
||||
if (gfxs_ctl) {
|
||||
device_remove_file(&pd->dev, &gfxs_ctl->attr);
|
||||
|
||||
kfree(gfxs_ctl);
|
||||
gfxs_ctl = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* High speed charging function */
|
||||
static struct device_attribute *hsc_handle;
|
||||
|
||||
@ -2533,6 +2642,8 @@ static void sony_nc_backlight_ng_read_limits(int handle,
|
||||
lvl_table_len = 9;
|
||||
break;
|
||||
case 0x143:
|
||||
case 0x14b:
|
||||
case 0x14c:
|
||||
lvl_table_len = 16;
|
||||
break;
|
||||
}
|
||||
@ -2584,6 +2695,18 @@ static void sony_nc_backlight_setup(void)
|
||||
sony_nc_backlight_ng_read_limits(0x143, &sony_bl_props);
|
||||
max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;
|
||||
|
||||
} else if (sony_find_snc_handle(0x14b) >= 0) {
|
||||
ops = &sony_backlight_ng_ops;
|
||||
sony_bl_props.cmd_base = 0x3000;
|
||||
sony_nc_backlight_ng_read_limits(0x14b, &sony_bl_props);
|
||||
max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;
|
||||
|
||||
} else if (sony_find_snc_handle(0x14c) >= 0) {
|
||||
ops = &sony_backlight_ng_ops;
|
||||
sony_bl_props.cmd_base = 0x3000;
|
||||
sony_nc_backlight_ng_read_limits(0x14c, &sony_bl_props);
|
||||
max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;
|
||||
|
||||
} else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT",
|
||||
&unused))) {
|
||||
ops = &sony_backlight_ops;
|
||||
|
@ -209,9 +209,8 @@ enum tpacpi_hkey_event_t {
|
||||
TP_HKEY_EV_ALARM_SENSOR_XHOT = 0x6022, /* sensor critically hot */
|
||||
TP_HKEY_EV_THM_TABLE_CHANGED = 0x6030, /* thermal table changed */
|
||||
|
||||
TP_HKEY_EV_UNK_6040 = 0x6040, /* Related to AC change?
|
||||
some sort of APM hint,
|
||||
W520 */
|
||||
/* AC-related events */
|
||||
TP_HKEY_EV_AC_CHANGED = 0x6040, /* AC status changed */
|
||||
|
||||
/* Misc */
|
||||
TP_HKEY_EV_RFKILL_CHANGED = 0x7000, /* rfkill switch changed */
|
||||
@ -3629,6 +3628,12 @@ static bool hotkey_notify_6xxx(const u32 hkey,
|
||||
"a sensor reports something is extremely hot!\n");
|
||||
/* recommended action: immediate sleep/hibernate */
|
||||
break;
|
||||
case TP_HKEY_EV_AC_CHANGED:
|
||||
/* X120e, X121e, X220, X220i, X220t, X230, T420, T420s, W520:
|
||||
* AC status changed; can be triggered by plugging or
|
||||
* unplugging AC adapter, docking or undocking. */
|
||||
|
||||
/* fallthrough */
|
||||
|
||||
case TP_HKEY_EV_KEY_NUMLOCK:
|
||||
case TP_HKEY_EV_KEY_FN:
|
||||
@ -8574,7 +8579,8 @@ static bool __pure __init tpacpi_is_valid_fw_id(const char* const s,
|
||||
return s && strlen(s) >= 8 &&
|
||||
tpacpi_is_fw_digit(s[0]) &&
|
||||
tpacpi_is_fw_digit(s[1]) &&
|
||||
s[2] == t && s[3] == 'T' &&
|
||||
s[2] == t &&
|
||||
(s[3] == 'T' || s[3] == 'N') &&
|
||||
tpacpi_is_fw_digit(s[4]) &&
|
||||
tpacpi_is_fw_digit(s[5]);
|
||||
}
|
||||
@ -8607,7 +8613,8 @@ static int __must_check __init get_thinkpad_model_data(
|
||||
return -ENOMEM;
|
||||
|
||||
/* Really ancient ThinkPad 240X will fail this, which is fine */
|
||||
if (!tpacpi_is_valid_fw_id(tp->bios_version_str, 'E'))
|
||||
if (!(tpacpi_is_valid_fw_id(tp->bios_version_str, 'E') ||
|
||||
tpacpi_is_valid_fw_id(tp->bios_version_str, 'C')))
|
||||
return 0;
|
||||
|
||||
tp->bios_model = tp->bios_version_str[0]
|
||||
|
Loading…
Reference in New Issue
Block a user