11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
241b16dceSLen Brown /*
341b16dceSLen Brown * Acer WMI Laptop Extras
441b16dceSLen Brown *
54f0175dcSCarlos Corbacho * Copyright (C) 2007-2009 Carlos Corbacho <carlos@strangeworlds.co.uk>
641b16dceSLen Brown *
741b16dceSLen Brown * Based on acer_acpi:
841b16dceSLen Brown * Copyright (C) 2005-2007 E.M. Smith
941b16dceSLen Brown * Copyright (C) 2007-2008 Carlos Corbacho <cathectic@gmail.com>
1041b16dceSLen Brown */
1141b16dceSLen Brown
12cae15702SLee, Chun-Yi #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13cae15702SLee, Chun-Yi
1441b16dceSLen Brown #include <linux/kernel.h>
1541b16dceSLen Brown #include <linux/module.h>
1641b16dceSLen Brown #include <linux/init.h>
1741b16dceSLen Brown #include <linux/types.h>
1841b16dceSLen Brown #include <linux/dmi.h>
1941b16dceSLen Brown #include <linux/fb.h>
2041b16dceSLen Brown #include <linux/backlight.h>
2141b16dceSLen Brown #include <linux/leds.h>
2241b16dceSLen Brown #include <linux/platform_device.h>
2341b16dceSLen Brown #include <linux/acpi.h>
2441b16dceSLen Brown #include <linux/i8042.h>
2541b16dceSLen Brown #include <linux/rfkill.h>
2641b16dceSLen Brown #include <linux/workqueue.h>
2741b16dceSLen Brown #include <linux/debugfs.h>
285a0e3ad6STejun Heo #include <linux/slab.h>
293fdca87dSLee, Chun-Yi #include <linux/input.h>
303fdca87dSLee, Chun-Yi #include <linux/input/sparse-keymap.h>
3186924de2SLee, Chun-Yi #include <acpi/video.h>
3241b16dceSLen Brown
3341b16dceSLen Brown MODULE_AUTHOR("Carlos Corbacho");
3441b16dceSLen Brown MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver");
3541b16dceSLen Brown MODULE_LICENSE("GPL");
3641b16dceSLen Brown
3741b16dceSLen Brown /*
3841b16dceSLen Brown * Magic Number
3941b16dceSLen Brown * Meaning is unknown - this number is required for writing to ACPI for AMW0
4041b16dceSLen Brown * (it's also used in acerhk when directly accessing the BIOS)
4141b16dceSLen Brown */
4241b16dceSLen Brown #define ACER_AMW0_WRITE 0x9610
4341b16dceSLen Brown
4441b16dceSLen Brown /*
4541b16dceSLen Brown * Bit masks for the AMW0 interface
4641b16dceSLen Brown */
4741b16dceSLen Brown #define ACER_AMW0_WIRELESS_MASK 0x35
4841b16dceSLen Brown #define ACER_AMW0_BLUETOOTH_MASK 0x34
4941b16dceSLen Brown #define ACER_AMW0_MAILLED_MASK 0x31
5041b16dceSLen Brown
5141b16dceSLen Brown /*
5241b16dceSLen Brown * Method IDs for WMID interface
5341b16dceSLen Brown */
5441b16dceSLen Brown #define ACER_WMID_GET_WIRELESS_METHODID 1
5541b16dceSLen Brown #define ACER_WMID_GET_BLUETOOTH_METHODID 2
5641b16dceSLen Brown #define ACER_WMID_GET_BRIGHTNESS_METHODID 3
5741b16dceSLen Brown #define ACER_WMID_SET_WIRELESS_METHODID 4
5841b16dceSLen Brown #define ACER_WMID_SET_BLUETOOTH_METHODID 5
5941b16dceSLen Brown #define ACER_WMID_SET_BRIGHTNESS_METHODID 6
6041b16dceSLen Brown #define ACER_WMID_GET_THREEG_METHODID 10
6141b16dceSLen Brown #define ACER_WMID_SET_THREEG_METHODID 11
6241b16dceSLen Brown
63ca42c119SJafarAkhondali #define ACER_WMID_SET_GAMING_LED_METHODID 2
64ca42c119SJafarAkhondali #define ACER_WMID_GET_GAMING_LED_METHODID 4
65ca42c119SJafarAkhondali #define ACER_WMID_SET_GAMING_FAN_BEHAVIOR 14
66ca42c119SJafarAkhondali #define ACER_WMID_SET_GAMING_MISC_SETTING_METHODID 22
67ca42c119SJafarAkhondali
6841b16dceSLen Brown /*
6941b16dceSLen Brown * Acer ACPI method GUIDs
7041b16dceSLen Brown */
7141b16dceSLen Brown #define AMW0_GUID1 "67C3371D-95A3-4C37-BB61-DD47B491DAAB"
7241b16dceSLen Brown #define AMW0_GUID2 "431F16ED-0C2B-444C-B267-27DEB140CF9C"
73bbb70607SMatthew Garrett #define WMID_GUID1 "6AF4F258-B401-42FD-BE91-3D4AC2D7C0D3"
74298f19b2SPali Rohár #define WMID_GUID2 "95764E09-FB56-4E83-B31A-37761F60994A"
75b3c9092bSLee, Chun-Yi #define WMID_GUID3 "61EF69EA-865C-4BC3-A502-A0DEBA0CB531"
76ca42c119SJafarAkhondali #define WMID_GUID4 "7A4DDFE7-5B5D-40B4-8595-4408E0CC7F56"
7741b16dceSLen Brown
783fdca87dSLee, Chun-Yi /*
793fdca87dSLee, Chun-Yi * Acer ACPI event GUIDs
803fdca87dSLee, Chun-Yi */
813fdca87dSLee, Chun-Yi #define ACERWMID_EVENT_GUID "676AA15E-6A47-4D9F-A2CC-1E6D18D14026"
823fdca87dSLee, Chun-Yi
8341b16dceSLen Brown MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB");
8408a0799dSLee, Chun-Yi MODULE_ALIAS("wmi:6AF4F258-B401-42FD-BE91-3D4AC2D7C0D3");
853fdca87dSLee, Chun-Yi MODULE_ALIAS("wmi:676AA15E-6A47-4D9F-A2CC-1E6D18D14026");
863fdca87dSLee, Chun-Yi
873fdca87dSLee, Chun-Yi enum acer_wmi_event_ids {
883fdca87dSLee, Chun-Yi WMID_HOTKEY_EVENT = 0x1,
895c54cb6cSHans de Goede WMID_ACCEL_OR_KBD_DOCK_EVENT = 0x5,
90ca42c119SJafarAkhondali WMID_GAMING_TURBO_KEY_EVENT = 0x7,
91*b8261bfaSArmin Wolf WMID_AC_EVENT = 0x8,
923fdca87dSLee, Chun-Yi };
933fdca87dSLee, Chun-Yi
9487e44849SMathias Krause static const struct key_entry acer_wmi_keymap[] __initconst = {
953fdca87dSLee, Chun-Yi {KE_KEY, 0x01, {KEY_WLAN} }, /* WiFi */
968ae68de1SMelchior FRANZ {KE_KEY, 0x03, {KEY_WLAN} }, /* WiFi */
971a04d8ffSSeth Forshee {KE_KEY, 0x04, {KEY_WLAN} }, /* WiFi */
983fdca87dSLee, Chun-Yi {KE_KEY, 0x12, {KEY_BLUETOOTH} }, /* BT */
993fdca87dSLee, Chun-Yi {KE_KEY, 0x21, {KEY_PROG1} }, /* Backup */
1003fdca87dSLee, Chun-Yi {KE_KEY, 0x22, {KEY_PROG2} }, /* Arcade */
1013fdca87dSLee, Chun-Yi {KE_KEY, 0x23, {KEY_PROG3} }, /* P_Key */
1023fdca87dSLee, Chun-Yi {KE_KEY, 0x24, {KEY_PROG4} }, /* Social networking_Key */
103af024a39SHans de Goede {KE_KEY, 0x27, {KEY_HELP} },
10467e1d34cSMerlin Schumacher {KE_KEY, 0x29, {KEY_PROG3} }, /* P_Key for TM8372 */
1058ae68de1SMelchior FRANZ {KE_IGNORE, 0x41, {KEY_MUTE} },
1068ae68de1SMelchior FRANZ {KE_IGNORE, 0x42, {KEY_PREVIOUSSONG} },
107ca1469f5SSergey Senozhatsky {KE_IGNORE, 0x4d, {KEY_PREVIOUSSONG} },
1088ae68de1SMelchior FRANZ {KE_IGNORE, 0x43, {KEY_NEXTSONG} },
109ca1469f5SSergey Senozhatsky {KE_IGNORE, 0x4e, {KEY_NEXTSONG} },
1108ae68de1SMelchior FRANZ {KE_IGNORE, 0x44, {KEY_PLAYPAUSE} },
111ca1469f5SSergey Senozhatsky {KE_IGNORE, 0x4f, {KEY_PLAYPAUSE} },
1128ae68de1SMelchior FRANZ {KE_IGNORE, 0x45, {KEY_STOP} },
113ca1469f5SSergey Senozhatsky {KE_IGNORE, 0x50, {KEY_STOP} },
1148ae68de1SMelchior FRANZ {KE_IGNORE, 0x48, {KEY_VOLUMEUP} },
1158ae68de1SMelchior FRANZ {KE_IGNORE, 0x49, {KEY_VOLUMEDOWN} },
116ca1469f5SSergey Senozhatsky {KE_IGNORE, 0x4a, {KEY_VOLUMEDOWN} },
117af024a39SHans de Goede /*
118af024a39SHans de Goede * 0x61 is KEY_SWITCHVIDEOMODE. Usually this is a duplicate input event
119af024a39SHans de Goede * with the "Video Bus" input device events. But sometimes it is not
120af024a39SHans de Goede * a dup. Map it to KEY_UNKNOWN instead of using KE_IGNORE so that
121af024a39SHans de Goede * udev/hwdb can override it on systems where it is not a dup.
122af024a39SHans de Goede */
123af024a39SHans de Goede {KE_KEY, 0x61, {KEY_UNKNOWN} },
1248ae68de1SMelchior FRANZ {KE_IGNORE, 0x62, {KEY_BRIGHTNESSUP} },
1258ae68de1SMelchior FRANZ {KE_IGNORE, 0x63, {KEY_BRIGHTNESSDOWN} },
1263fdca87dSLee, Chun-Yi {KE_KEY, 0x64, {KEY_SWITCHVIDEOMODE} }, /* Display Switch */
1278ae68de1SMelchior FRANZ {KE_IGNORE, 0x81, {KEY_SLEEP} },
1288e2286ceSLee, Chun-Yi {KE_KEY, 0x82, {KEY_TOUCHPAD_TOGGLE} }, /* Touch Pad Toggle */
12919cf7054STimo Witte {KE_IGNORE, 0x84, {KEY_KBDILLUMTOGGLE} }, /* Automatic Keyboard background light toggle */
1308e2286ceSLee, Chun-Yi {KE_KEY, KEY_TOUCHPAD_ON, {KEY_TOUCHPAD_ON} },
1318e2286ceSLee, Chun-Yi {KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} },
1328ae68de1SMelchior FRANZ {KE_IGNORE, 0x83, {KEY_TOUCHPAD_TOGGLE} },
13368825ce2SSergey Senozhatsky {KE_KEY, 0x85, {KEY_TOUCHPAD_TOGGLE} },
1345ffa572aSChris Chiu {KE_KEY, 0x86, {KEY_WLAN} },
135c7a437fdSAntonio Rosario Intilisano {KE_KEY, 0x87, {KEY_POWER} },
1363fdca87dSLee, Chun-Yi {KE_END, 0}
1373fdca87dSLee, Chun-Yi };
1383fdca87dSLee, Chun-Yi
1393fdca87dSLee, Chun-Yi static struct input_dev *acer_wmi_input_dev;
1401eb3fe1dSMarek Vasut static struct input_dev *acer_wmi_accel_dev;
1413fdca87dSLee, Chun-Yi
1423fdca87dSLee, Chun-Yi struct event_return_value {
1433fdca87dSLee, Chun-Yi u8 function;
1443fdca87dSLee, Chun-Yi u8 key_num;
1453fdca87dSLee, Chun-Yi u16 device_state;
1465c54cb6cSHans de Goede u16 reserved1;
1475c54cb6cSHans de Goede u8 kbd_dock_state;
1485c54cb6cSHans de Goede u8 reserved2;
1491f88e0a2SJules Irenge } __packed;
15041b16dceSLen Brown
15141b16dceSLen Brown /*
152b3c9092bSLee, Chun-Yi * GUID3 Get Device Status device flags
153b3c9092bSLee, Chun-Yi */
1546c3df88fSLee, Chun-Yi #define ACER_WMID3_GDS_WIRELESS (1<<0) /* WiFi */
155b3c9092bSLee, Chun-Yi #define ACER_WMID3_GDS_THREEG (1<<6) /* 3G */
1566d88ff0fSLee, Chun-Yi #define ACER_WMID3_GDS_WIMAX (1<<7) /* WiMAX */
1576c3df88fSLee, Chun-Yi #define ACER_WMID3_GDS_BLUETOOTH (1<<11) /* BT */
1583e2bc5c5SJoão Paulo Rechi Vita #define ACER_WMID3_GDS_RFBTN (1<<14) /* RF Button */
1593e2bc5c5SJoão Paulo Rechi Vita
1608e2286ceSLee, Chun-Yi #define ACER_WMID3_GDS_TOUCHPAD (1<<1) /* Touchpad */
161b3c9092bSLee, Chun-Yi
162280642c3SChris Chiu /* Hotkey Customized Setting and Acer Application Status.
163280642c3SChris Chiu * Set Device Default Value and Report Acer Application Status.
164280642c3SChris Chiu * When Acer Application starts, it will run this method to inform
165280642c3SChris Chiu * BIOS/EC that Acer Application is on.
166280642c3SChris Chiu * App Status
167280642c3SChris Chiu * Bit[0]: Launch Manager Status
168280642c3SChris Chiu * Bit[1]: ePM Status
169280642c3SChris Chiu * Bit[2]: Device Control Status
170280642c3SChris Chiu * Bit[3]: Acer Power Button Utility Status
171280642c3SChris Chiu * Bit[4]: RF Button Status
172280642c3SChris Chiu * Bit[5]: ODD PM Status
173280642c3SChris Chiu * Bit[6]: Device Default Value Control
174280642c3SChris Chiu * Bit[7]: Hall Sensor Application Status
175280642c3SChris Chiu */
176280642c3SChris Chiu struct func_input_params {
17759ccf2f3SFrom: Lee, Chun-Yi u8 function_num; /* Function Number */
17859ccf2f3SFrom: Lee, Chun-Yi u16 commun_devices; /* Communication type devices default status */
17959ccf2f3SFrom: Lee, Chun-Yi u16 devices; /* Other type devices default status */
180280642c3SChris Chiu u8 app_status; /* Acer Device Status. LM, ePM, RF Button... */
181280642c3SChris Chiu u8 app_mask; /* Bit mask to app_status */
182280642c3SChris Chiu u8 reserved;
1831f88e0a2SJules Irenge } __packed;
18459ccf2f3SFrom: Lee, Chun-Yi
185280642c3SChris Chiu struct func_return_value {
18659ccf2f3SFrom: Lee, Chun-Yi u8 error_code; /* Error Code */
18759ccf2f3SFrom: Lee, Chun-Yi u8 ec_return_value; /* EC Return Value */
18859ccf2f3SFrom: Lee, Chun-Yi u16 reserved;
1891f88e0a2SJules Irenge } __packed;
19059ccf2f3SFrom: Lee, Chun-Yi
191996d23baSLee, Chun-Yi struct wmid3_gds_set_input_param { /* Set Device Status input parameter */
192996d23baSLee, Chun-Yi u8 function_num; /* Function Number */
193996d23baSLee, Chun-Yi u8 hotkey_number; /* Hotkey Number */
194996d23baSLee, Chun-Yi u16 devices; /* Set Device */
195996d23baSLee, Chun-Yi u8 volume_value; /* Volume Value */
1961f88e0a2SJules Irenge } __packed;
197996d23baSLee, Chun-Yi
198996d23baSLee, Chun-Yi struct wmid3_gds_get_input_param { /* Get Device Status input parameter */
199b3c9092bSLee, Chun-Yi u8 function_num; /* Function Number */
200b3c9092bSLee, Chun-Yi u8 hotkey_number; /* Hotkey Number */
201b3c9092bSLee, Chun-Yi u16 devices; /* Get Device */
2021f88e0a2SJules Irenge } __packed;
203b3c9092bSLee, Chun-Yi
204b3c9092bSLee, Chun-Yi struct wmid3_gds_return_value { /* Get Device Status return value*/
205b3c9092bSLee, Chun-Yi u8 error_code; /* Error Code */
206b3c9092bSLee, Chun-Yi u8 ec_return_value; /* EC Return Value */
207b3c9092bSLee, Chun-Yi u16 devices; /* Current Device Status */
208b3c9092bSLee, Chun-Yi u32 reserved;
2091f88e0a2SJules Irenge } __packed;
210b3c9092bSLee, Chun-Yi
2116c3df88fSLee, Chun-Yi struct hotkey_function_type_aa {
2126c3df88fSLee, Chun-Yi u8 type;
2136c3df88fSLee, Chun-Yi u8 length;
2146c3df88fSLee, Chun-Yi u16 handle;
2156c3df88fSLee, Chun-Yi u16 commun_func_bitmap;
21634b6cfabSLee, Chun-Yi u16 application_func_bitmap;
21734b6cfabSLee, Chun-Yi u16 media_func_bitmap;
21834b6cfabSLee, Chun-Yi u16 display_func_bitmap;
21934b6cfabSLee, Chun-Yi u16 others_func_bitmap;
22034b6cfabSLee, Chun-Yi u8 commun_fn_key_number;
2211f88e0a2SJules Irenge } __packed;
2226c3df88fSLee, Chun-Yi
223b3c9092bSLee, Chun-Yi /*
22441b16dceSLen Brown * Interface capability flags
22541b16dceSLen Brown */
2267c936d8dSHans de Goede #define ACER_CAP_MAILLED BIT(0)
2277c936d8dSHans de Goede #define ACER_CAP_WIRELESS BIT(1)
2287c936d8dSHans de Goede #define ACER_CAP_BLUETOOTH BIT(2)
2297c936d8dSHans de Goede #define ACER_CAP_BRIGHTNESS BIT(3)
2307c936d8dSHans de Goede #define ACER_CAP_THREEG BIT(4)
23182cb8a5cSHans de Goede #define ACER_CAP_SET_FUNCTION_MODE BIT(5)
2325c54cb6cSHans de Goede #define ACER_CAP_KBD_DOCK BIT(6)
233ca42c119SJafarAkhondali #define ACER_CAP_TURBO_OC BIT(7)
234ca42c119SJafarAkhondali #define ACER_CAP_TURBO_LED BIT(8)
235ca42c119SJafarAkhondali #define ACER_CAP_TURBO_FAN BIT(9)
23641b16dceSLen Brown
23741b16dceSLen Brown /*
23841b16dceSLen Brown * Interface type flags
23941b16dceSLen Brown */
24041b16dceSLen Brown enum interface_flags {
24141b16dceSLen Brown ACER_AMW0,
24241b16dceSLen Brown ACER_AMW0_V2,
24341b16dceSLen Brown ACER_WMID,
24472e71de1SLee, Chun-Yi ACER_WMID_v2,
24541b16dceSLen Brown };
24641b16dceSLen Brown
24741b16dceSLen Brown #define ACER_DEFAULT_WIRELESS 0
24841b16dceSLen Brown #define ACER_DEFAULT_BLUETOOTH 0
24941b16dceSLen Brown #define ACER_DEFAULT_MAILLED 0
25041b16dceSLen Brown #define ACER_DEFAULT_THREEG 0
25141b16dceSLen Brown
25241b16dceSLen Brown static int max_brightness = 0xF;
25341b16dceSLen Brown
25441b16dceSLen Brown static int mailled = -1;
25541b16dceSLen Brown static int brightness = -1;
25641b16dceSLen Brown static int threeg = -1;
25741b16dceSLen Brown static int force_series;
25839aa009bSHans de Goede static int force_caps = -1;
25959ccf2f3SFrom: Lee, Chun-Yi static bool ec_raw_mode;
2606c3df88fSLee, Chun-Yi static bool has_type_aa;
2611d1fc8a7SLee, Chun-Yi static u16 commun_func_bitmap;
26234b6cfabSLee, Chun-Yi static u8 commun_fn_key_number;
26341b16dceSLen Brown
26441b16dceSLen Brown module_param(mailled, int, 0444);
26541b16dceSLen Brown module_param(brightness, int, 0444);
26641b16dceSLen Brown module_param(threeg, int, 0444);
26741b16dceSLen Brown module_param(force_series, int, 0444);
26839aa009bSHans de Goede module_param(force_caps, int, 0444);
26959ccf2f3SFrom: Lee, Chun-Yi module_param(ec_raw_mode, bool, 0444);
27041b16dceSLen Brown MODULE_PARM_DESC(mailled, "Set initial state of Mail LED");
27141b16dceSLen Brown MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness");
27241b16dceSLen Brown MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware");
27341b16dceSLen Brown MODULE_PARM_DESC(force_series, "Force a different laptop series");
27439aa009bSHans de Goede MODULE_PARM_DESC(force_caps, "Force the capability bitmask to this value");
27559ccf2f3SFrom: Lee, Chun-Yi MODULE_PARM_DESC(ec_raw_mode, "Enable EC raw mode");
27641b16dceSLen Brown
27741b16dceSLen Brown struct acer_data {
27841b16dceSLen Brown int mailled;
27941b16dceSLen Brown int threeg;
28041b16dceSLen Brown int brightness;
28141b16dceSLen Brown };
28241b16dceSLen Brown
28341b16dceSLen Brown struct acer_debug {
28441b16dceSLen Brown struct dentry *root;
28541b16dceSLen Brown u32 wmid_devices;
28641b16dceSLen Brown };
28741b16dceSLen Brown
28841b16dceSLen Brown static struct rfkill *wireless_rfkill;
28941b16dceSLen Brown static struct rfkill *bluetooth_rfkill;
290b3c9092bSLee, Chun-Yi static struct rfkill *threeg_rfkill;
2918215af01SLee, Chun-Yi static bool rfkill_inited;
29241b16dceSLen Brown
29341b16dceSLen Brown /* Each low-level interface must define at least some of the following */
29441b16dceSLen Brown struct wmi_interface {
29541b16dceSLen Brown /* The WMI device type */
29641b16dceSLen Brown u32 type;
29741b16dceSLen Brown
29841b16dceSLen Brown /* The capabilities this interface provides */
29941b16dceSLen Brown u32 capability;
30041b16dceSLen Brown
30141b16dceSLen Brown /* Private data for the current interface */
30241b16dceSLen Brown struct acer_data data;
30341b16dceSLen Brown
30441b16dceSLen Brown /* debugfs entries associated with this interface */
30541b16dceSLen Brown struct acer_debug debug;
30641b16dceSLen Brown };
30741b16dceSLen Brown
30841b16dceSLen Brown /* The static interface pointer, points to the currently detected interface */
30941b16dceSLen Brown static struct wmi_interface *interface;
31041b16dceSLen Brown
31141b16dceSLen Brown /*
31241b16dceSLen Brown * Embedded Controller quirks
31341b16dceSLen Brown * Some laptops require us to directly access the EC to either enable or query
31441b16dceSLen Brown * features that are not available through WMI.
31541b16dceSLen Brown */
31641b16dceSLen Brown
31741b16dceSLen Brown struct quirk_entry {
31841b16dceSLen Brown u8 wireless;
31941b16dceSLen Brown u8 mailled;
32041b16dceSLen Brown s8 brightness;
32141b16dceSLen Brown u8 bluetooth;
322ca42c119SJafarAkhondali u8 turbo;
323ca42c119SJafarAkhondali u8 cpu_fans;
324ca42c119SJafarAkhondali u8 gpu_fans;
32541b16dceSLen Brown };
32641b16dceSLen Brown
32741b16dceSLen Brown static struct quirk_entry *quirks;
32841b16dceSLen Brown
set_quirks(void)32976d51dd9SMathias Krause static void __init set_quirks(void)
33041b16dceSLen Brown {
33141b16dceSLen Brown if (quirks->mailled)
33241b16dceSLen Brown interface->capability |= ACER_CAP_MAILLED;
33341b16dceSLen Brown
33441b16dceSLen Brown if (quirks->brightness)
33541b16dceSLen Brown interface->capability |= ACER_CAP_BRIGHTNESS;
336ca42c119SJafarAkhondali
337ca42c119SJafarAkhondali if (quirks->turbo)
338ca42c119SJafarAkhondali interface->capability |= ACER_CAP_TURBO_OC | ACER_CAP_TURBO_LED
339ca42c119SJafarAkhondali | ACER_CAP_TURBO_FAN;
34041b16dceSLen Brown }
34141b16dceSLen Brown
dmi_matched(const struct dmi_system_id * dmi)34276d51dd9SMathias Krause static int __init dmi_matched(const struct dmi_system_id *dmi)
34341b16dceSLen Brown {
34441b16dceSLen Brown quirks = dmi->driver_data;
345d53bf0f3SAxel Lin return 1;
34641b16dceSLen Brown }
34741b16dceSLen Brown
set_force_caps(const struct dmi_system_id * dmi)3485c54cb6cSHans de Goede static int __init set_force_caps(const struct dmi_system_id *dmi)
3495c54cb6cSHans de Goede {
3505c54cb6cSHans de Goede if (force_caps == -1) {
3515c54cb6cSHans de Goede force_caps = (uintptr_t)dmi->driver_data;
3525c54cb6cSHans de Goede pr_info("Found %s, set force_caps to 0x%x\n", dmi->ident, force_caps);
3535c54cb6cSHans de Goede }
3545c54cb6cSHans de Goede return 1;
3555c54cb6cSHans de Goede }
3565c54cb6cSHans de Goede
35741b16dceSLen Brown static struct quirk_entry quirk_unknown = {
35841b16dceSLen Brown };
35941b16dceSLen Brown
36041b16dceSLen Brown static struct quirk_entry quirk_acer_aspire_1520 = {
36141b16dceSLen Brown .brightness = -1,
36241b16dceSLen Brown };
36341b16dceSLen Brown
36441b16dceSLen Brown static struct quirk_entry quirk_acer_travelmate_2490 = {
36541b16dceSLen Brown .mailled = 1,
36641b16dceSLen Brown };
36741b16dceSLen Brown
368ca42c119SJafarAkhondali static struct quirk_entry quirk_acer_predator_ph315_53 = {
369ca42c119SJafarAkhondali .turbo = 1,
370ca42c119SJafarAkhondali .cpu_fans = 1,
371ca42c119SJafarAkhondali .gpu_fans = 1,
372ca42c119SJafarAkhondali };
373ca42c119SJafarAkhondali
37441b16dceSLen Brown /* This AMW0 laptop has no bluetooth */
37541b16dceSLen Brown static struct quirk_entry quirk_medion_md_98300 = {
37641b16dceSLen Brown .wireless = 1,
37741b16dceSLen Brown };
37841b16dceSLen Brown
37941b16dceSLen Brown static struct quirk_entry quirk_fujitsu_amilo_li_1718 = {
38041b16dceSLen Brown .wireless = 2,
38141b16dceSLen Brown };
38241b16dceSLen Brown
38315b956a0SLee, Chun-Yi static struct quirk_entry quirk_lenovo_ideapad_s205 = {
38415b956a0SLee, Chun-Yi .wireless = 3,
38515b956a0SLee, Chun-Yi };
38615b956a0SLee, Chun-Yi
387a74dd5fdSCarlos Corbacho /* The Aspire One has a dummy ACPI-WMI interface - disable it */
38876d51dd9SMathias Krause static const struct dmi_system_id acer_blacklist[] __initconst = {
389a74dd5fdSCarlos Corbacho {
390a74dd5fdSCarlos Corbacho .ident = "Acer Aspire One (SSD)",
391a74dd5fdSCarlos Corbacho .matches = {
392a74dd5fdSCarlos Corbacho DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
393a74dd5fdSCarlos Corbacho DMI_MATCH(DMI_PRODUCT_NAME, "AOA110"),
394a74dd5fdSCarlos Corbacho },
395a74dd5fdSCarlos Corbacho },
396a74dd5fdSCarlos Corbacho {
397a74dd5fdSCarlos Corbacho .ident = "Acer Aspire One (HDD)",
398a74dd5fdSCarlos Corbacho .matches = {
399a74dd5fdSCarlos Corbacho DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
400a74dd5fdSCarlos Corbacho DMI_MATCH(DMI_PRODUCT_NAME, "AOA150"),
401a74dd5fdSCarlos Corbacho },
402a74dd5fdSCarlos Corbacho },
403a74dd5fdSCarlos Corbacho {}
404a74dd5fdSCarlos Corbacho };
405a74dd5fdSCarlos Corbacho
4065241b193SLee, Chun-Yi static const struct dmi_system_id amw0_whitelist[] __initconst = {
4075241b193SLee, Chun-Yi {
4085241b193SLee, Chun-Yi .ident = "Acer",
4095241b193SLee, Chun-Yi .matches = {
4105241b193SLee, Chun-Yi DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
4115241b193SLee, Chun-Yi },
4125241b193SLee, Chun-Yi },
4135241b193SLee, Chun-Yi {
4145241b193SLee, Chun-Yi .ident = "Gateway",
4155241b193SLee, Chun-Yi .matches = {
4165241b193SLee, Chun-Yi DMI_MATCH(DMI_SYS_VENDOR, "Gateway"),
4175241b193SLee, Chun-Yi },
4185241b193SLee, Chun-Yi },
4195241b193SLee, Chun-Yi {
4205241b193SLee, Chun-Yi .ident = "Packard Bell",
4215241b193SLee, Chun-Yi .matches = {
4225241b193SLee, Chun-Yi DMI_MATCH(DMI_SYS_VENDOR, "Packard Bell"),
4235241b193SLee, Chun-Yi },
4245241b193SLee, Chun-Yi },
4255241b193SLee, Chun-Yi {}
4265241b193SLee, Chun-Yi };
4275241b193SLee, Chun-Yi
4285241b193SLee, Chun-Yi /*
4295241b193SLee, Chun-Yi * This quirk table is only for Acer/Gateway/Packard Bell family
4305241b193SLee, Chun-Yi * that those machines are supported by acer-wmi driver.
4315241b193SLee, Chun-Yi */
43276d51dd9SMathias Krause static const struct dmi_system_id acer_quirks[] __initconst = {
43341b16dceSLen Brown {
43441b16dceSLen Brown .callback = dmi_matched,
43541b16dceSLen Brown .ident = "Acer Aspire 1360",
43641b16dceSLen Brown .matches = {
43741b16dceSLen Brown DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
43841b16dceSLen Brown DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"),
43941b16dceSLen Brown },
44041b16dceSLen Brown .driver_data = &quirk_acer_aspire_1520,
44141b16dceSLen Brown },
44241b16dceSLen Brown {
44341b16dceSLen Brown .callback = dmi_matched,
44441b16dceSLen Brown .ident = "Acer Aspire 1520",
44541b16dceSLen Brown .matches = {
44641b16dceSLen Brown DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
44741b16dceSLen Brown DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1520"),
44841b16dceSLen Brown },
44941b16dceSLen Brown .driver_data = &quirk_acer_aspire_1520,
45041b16dceSLen Brown },
45141b16dceSLen Brown {
45241b16dceSLen Brown .callback = dmi_matched,
45341b16dceSLen Brown .ident = "Acer Aspire 3100",
45441b16dceSLen Brown .matches = {
45541b16dceSLen Brown DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
45641b16dceSLen Brown DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3100"),
45741b16dceSLen Brown },
45841b16dceSLen Brown .driver_data = &quirk_acer_travelmate_2490,
45941b16dceSLen Brown },
46041b16dceSLen Brown {
46141b16dceSLen Brown .callback = dmi_matched,
46241b16dceSLen Brown .ident = "Acer Aspire 3610",
46341b16dceSLen Brown .matches = {
46441b16dceSLen Brown DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
46541b16dceSLen Brown DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3610"),
46641b16dceSLen Brown },
46741b16dceSLen Brown .driver_data = &quirk_acer_travelmate_2490,
46841b16dceSLen Brown },
46941b16dceSLen Brown {
47041b16dceSLen Brown .callback = dmi_matched,
47141b16dceSLen Brown .ident = "Acer Aspire 5100",
47241b16dceSLen Brown .matches = {
47341b16dceSLen Brown DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
47441b16dceSLen Brown DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"),
47541b16dceSLen Brown },
47641b16dceSLen Brown .driver_data = &quirk_acer_travelmate_2490,
47741b16dceSLen Brown },
47841b16dceSLen Brown {
47941b16dceSLen Brown .callback = dmi_matched,
48041b16dceSLen Brown .ident = "Acer Aspire 5610",
48141b16dceSLen Brown .matches = {
48241b16dceSLen Brown DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
48341b16dceSLen Brown DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"),
48441b16dceSLen Brown },
48541b16dceSLen Brown .driver_data = &quirk_acer_travelmate_2490,
48641b16dceSLen Brown },
48741b16dceSLen Brown {
48841b16dceSLen Brown .callback = dmi_matched,
48941b16dceSLen Brown .ident = "Acer Aspire 5630",
49041b16dceSLen Brown .matches = {
49141b16dceSLen Brown DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
49241b16dceSLen Brown DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"),
49341b16dceSLen Brown },
49441b16dceSLen Brown .driver_data = &quirk_acer_travelmate_2490,
49541b16dceSLen Brown },
49641b16dceSLen Brown {
49741b16dceSLen Brown .callback = dmi_matched,
49841b16dceSLen Brown .ident = "Acer Aspire 5650",
49941b16dceSLen Brown .matches = {
50041b16dceSLen Brown DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
50141b16dceSLen Brown DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"),
50241b16dceSLen Brown },
50341b16dceSLen Brown .driver_data = &quirk_acer_travelmate_2490,
50441b16dceSLen Brown },
50541b16dceSLen Brown {
50641b16dceSLen Brown .callback = dmi_matched,
50741b16dceSLen Brown .ident = "Acer Aspire 5680",
50841b16dceSLen Brown .matches = {
50941b16dceSLen Brown DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
51041b16dceSLen Brown DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"),
51141b16dceSLen Brown },
51241b16dceSLen Brown .driver_data = &quirk_acer_travelmate_2490,
51341b16dceSLen Brown },
51441b16dceSLen Brown {
51541b16dceSLen Brown .callback = dmi_matched,
51641b16dceSLen Brown .ident = "Acer Aspire 9110",
51741b16dceSLen Brown .matches = {
51841b16dceSLen Brown DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
51941b16dceSLen Brown DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"),
52041b16dceSLen Brown },
52141b16dceSLen Brown .driver_data = &quirk_acer_travelmate_2490,
52241b16dceSLen Brown },
52341b16dceSLen Brown {
52441b16dceSLen Brown .callback = dmi_matched,
52541b16dceSLen Brown .ident = "Acer TravelMate 2490",
52641b16dceSLen Brown .matches = {
52741b16dceSLen Brown DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
52841b16dceSLen Brown DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"),
52941b16dceSLen Brown },
53041b16dceSLen Brown .driver_data = &quirk_acer_travelmate_2490,
53141b16dceSLen Brown },
53241b16dceSLen Brown {
53341b16dceSLen Brown .callback = dmi_matched,
53441b16dceSLen Brown .ident = "Acer TravelMate 4200",
53541b16dceSLen Brown .matches = {
53641b16dceSLen Brown DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
53741b16dceSLen Brown DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4200"),
53841b16dceSLen Brown },
53941b16dceSLen Brown .driver_data = &quirk_acer_travelmate_2490,
54041b16dceSLen Brown },
5415c54cb6cSHans de Goede {
542ca42c119SJafarAkhondali .callback = dmi_matched,
543ca42c119SJafarAkhondali .ident = "Acer Predator PH315-53",
544ca42c119SJafarAkhondali .matches = {
545ca42c119SJafarAkhondali DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
546ca42c119SJafarAkhondali DMI_MATCH(DMI_PRODUCT_NAME, "Predator PH315-53"),
547ca42c119SJafarAkhondali },
548ca42c119SJafarAkhondali .driver_data = &quirk_acer_predator_ph315_53,
549ca42c119SJafarAkhondali },
550ca42c119SJafarAkhondali {
5515c54cb6cSHans de Goede .callback = set_force_caps,
552bf753400SHans de Goede .ident = "Acer Aspire Switch 10E SW3-016",
553bf753400SHans de Goede .matches = {
554bf753400SHans de Goede DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
555bf753400SHans de Goede DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW3-016"),
556bf753400SHans de Goede },
557bf753400SHans de Goede .driver_data = (void *)ACER_CAP_KBD_DOCK,
558bf753400SHans de Goede },
559bf753400SHans de Goede {
560bf753400SHans de Goede .callback = set_force_caps,
5615c54cb6cSHans de Goede .ident = "Acer Aspire Switch 10 SW5-012",
5625c54cb6cSHans de Goede .matches = {
5635c54cb6cSHans de Goede DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
5645c54cb6cSHans de Goede DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"),
5655c54cb6cSHans de Goede },
5665c54cb6cSHans de Goede .driver_data = (void *)ACER_CAP_KBD_DOCK,
5675c54cb6cSHans de Goede },
5685c54cb6cSHans de Goede {
5695c54cb6cSHans de Goede .callback = set_force_caps,
5701e817b88SHans de Goede .ident = "Acer Aspire Switch V 10 SW5-017",
5711e817b88SHans de Goede .matches = {
5721e817b88SHans de Goede DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Acer"),
5731e817b88SHans de Goede DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "SW5-017"),
5741e817b88SHans de Goede },
5751e817b88SHans de Goede .driver_data = (void *)ACER_CAP_KBD_DOCK,
5761e817b88SHans de Goede },
5771e817b88SHans de Goede {
5781e817b88SHans de Goede .callback = set_force_caps,
5795c54cb6cSHans de Goede .ident = "Acer One 10 (S1003)",
5805c54cb6cSHans de Goede .matches = {
5815c54cb6cSHans de Goede DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Acer"),
5825c54cb6cSHans de Goede DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "One S1003"),
5835c54cb6cSHans de Goede },
5845c54cb6cSHans de Goede .driver_data = (void *)ACER_CAP_KBD_DOCK,
5855c54cb6cSHans de Goede },
5865241b193SLee, Chun-Yi {}
5875241b193SLee, Chun-Yi };
5885241b193SLee, Chun-Yi
5895241b193SLee, Chun-Yi /*
5905241b193SLee, Chun-Yi * This quirk list is for those non-acer machines that have AMW0_GUID1
5915241b193SLee, Chun-Yi * but supported by acer-wmi in past days. Keeping this quirk list here
5925241b193SLee, Chun-Yi * is only for backward compatible. Please do not add new machine to
5935241b193SLee, Chun-Yi * here anymore. Those non-acer machines should be supported by
5945241b193SLee, Chun-Yi * appropriate wmi drivers.
5955241b193SLee, Chun-Yi */
5965241b193SLee, Chun-Yi static const struct dmi_system_id non_acer_quirks[] __initconst = {
59741b16dceSLen Brown {
59841b16dceSLen Brown .callback = dmi_matched,
59941b16dceSLen Brown .ident = "Fujitsu Siemens Amilo Li 1718",
60041b16dceSLen Brown .matches = {
60141b16dceSLen Brown DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
60241b16dceSLen Brown DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Li 1718"),
60341b16dceSLen Brown },
60441b16dceSLen Brown .driver_data = &quirk_fujitsu_amilo_li_1718,
60541b16dceSLen Brown },
60641b16dceSLen Brown {
60741b16dceSLen Brown .callback = dmi_matched,
60841b16dceSLen Brown .ident = "Medion MD 98300",
60941b16dceSLen Brown .matches = {
61041b16dceSLen Brown DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
61141b16dceSLen Brown DMI_MATCH(DMI_PRODUCT_NAME, "WAM2030"),
61241b16dceSLen Brown },
61341b16dceSLen Brown .driver_data = &quirk_medion_md_98300,
61441b16dceSLen Brown },
61515b956a0SLee, Chun-Yi {
61615b956a0SLee, Chun-Yi .callback = dmi_matched,
61715b956a0SLee, Chun-Yi .ident = "Lenovo Ideapad S205",
61815b956a0SLee, Chun-Yi .matches = {
61915b956a0SLee, Chun-Yi DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
62015b956a0SLee, Chun-Yi DMI_MATCH(DMI_PRODUCT_NAME, "10382LG"),
62115b956a0SLee, Chun-Yi },
62215b956a0SLee, Chun-Yi .driver_data = &quirk_lenovo_ideapad_s205,
62315b956a0SLee, Chun-Yi },
624be3128b1SSeth Forshee {
625be3128b1SSeth Forshee .callback = dmi_matched,
626c08f2086SLee, Chun-Yi .ident = "Lenovo Ideapad S205 (Brazos)",
627c08f2086SLee, Chun-Yi .matches = {
628c08f2086SLee, Chun-Yi DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
629c08f2086SLee, Chun-Yi DMI_MATCH(DMI_PRODUCT_NAME, "Brazos"),
630c08f2086SLee, Chun-Yi },
631c08f2086SLee, Chun-Yi .driver_data = &quirk_lenovo_ideapad_s205,
632c08f2086SLee, Chun-Yi },
633c08f2086SLee, Chun-Yi {
634c08f2086SLee, Chun-Yi .callback = dmi_matched,
635be3128b1SSeth Forshee .ident = "Lenovo 3000 N200",
636be3128b1SSeth Forshee .matches = {
637be3128b1SSeth Forshee DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
638be3128b1SSeth Forshee DMI_MATCH(DMI_PRODUCT_NAME, "0687A31"),
639be3128b1SSeth Forshee },
640be3128b1SSeth Forshee .driver_data = &quirk_fujitsu_amilo_li_1718,
641be3128b1SSeth Forshee },
642e6c33f1fSLee, Chun-Yi {
643e6c33f1fSLee, Chun-Yi .callback = dmi_matched,
644e6c33f1fSLee, Chun-Yi .ident = "Lenovo Ideapad S205-10382JG",
645e6c33f1fSLee, Chun-Yi .matches = {
646e6c33f1fSLee, Chun-Yi DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
647e6c33f1fSLee, Chun-Yi DMI_MATCH(DMI_PRODUCT_NAME, "10382JG"),
648e6c33f1fSLee, Chun-Yi },
649e6c33f1fSLee, Chun-Yi .driver_data = &quirk_lenovo_ideapad_s205,
650e6c33f1fSLee, Chun-Yi },
6515f3511d2SLee, Chun-Yi {
6525f3511d2SLee, Chun-Yi .callback = dmi_matched,
6535f3511d2SLee, Chun-Yi .ident = "Lenovo Ideapad S205-1038DPG",
6545f3511d2SLee, Chun-Yi .matches = {
6555f3511d2SLee, Chun-Yi DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
6565f3511d2SLee, Chun-Yi DMI_MATCH(DMI_PRODUCT_NAME, "1038DPG"),
6575f3511d2SLee, Chun-Yi },
6585f3511d2SLee, Chun-Yi .driver_data = &quirk_lenovo_ideapad_s205,
6595f3511d2SLee, Chun-Yi },
66041b16dceSLen Brown {}
66141b16dceSLen Brown };
66241b16dceSLen Brown
66341b16dceSLen Brown /* Find which quirks are needed for a particular vendor/ model pair */
find_quirks(void)66476d51dd9SMathias Krause static void __init find_quirks(void)
66541b16dceSLen Brown {
66641b16dceSLen Brown if (!force_series) {
66741b16dceSLen Brown dmi_check_system(acer_quirks);
6685241b193SLee, Chun-Yi dmi_check_system(non_acer_quirks);
66941b16dceSLen Brown } else if (force_series == 2490) {
67041b16dceSLen Brown quirks = &quirk_acer_travelmate_2490;
67141b16dceSLen Brown }
67241b16dceSLen Brown
67341b16dceSLen Brown if (quirks == NULL)
67441b16dceSLen Brown quirks = &quirk_unknown;
67541b16dceSLen Brown }
67641b16dceSLen Brown
67741b16dceSLen Brown /*
67841b16dceSLen Brown * General interface convenience methods
67941b16dceSLen Brown */
68041b16dceSLen Brown
has_cap(u32 cap)68141b16dceSLen Brown static bool has_cap(u32 cap)
68241b16dceSLen Brown {
683aa7d16bfSGustavo A. R. Silva return interface->capability & cap;
68441b16dceSLen Brown }
68541b16dceSLen Brown
68641b16dceSLen Brown /*
68741b16dceSLen Brown * AMW0 (V1) interface
68841b16dceSLen Brown */
68941b16dceSLen Brown struct wmab_args {
69041b16dceSLen Brown u32 eax;
69141b16dceSLen Brown u32 ebx;
69241b16dceSLen Brown u32 ecx;
69341b16dceSLen Brown u32 edx;
69441b16dceSLen Brown };
69541b16dceSLen Brown
69641b16dceSLen Brown struct wmab_ret {
69741b16dceSLen Brown u32 eax;
69841b16dceSLen Brown u32 ebx;
69941b16dceSLen Brown u32 ecx;
70041b16dceSLen Brown u32 edx;
70141b16dceSLen Brown u32 eex;
70241b16dceSLen Brown };
70341b16dceSLen Brown
wmab_execute(struct wmab_args * regbuf,struct acpi_buffer * result)70441b16dceSLen Brown static acpi_status wmab_execute(struct wmab_args *regbuf,
70541b16dceSLen Brown struct acpi_buffer *result)
70641b16dceSLen Brown {
70741b16dceSLen Brown struct acpi_buffer input;
70841b16dceSLen Brown acpi_status status;
70941b16dceSLen Brown input.length = sizeof(struct wmab_args);
71041b16dceSLen Brown input.pointer = (u8 *)regbuf;
71141b16dceSLen Brown
71262fc743cSLee, Chun-Yi status = wmi_evaluate_method(AMW0_GUID1, 0, 1, &input, result);
71341b16dceSLen Brown
71441b16dceSLen Brown return status;
71541b16dceSLen Brown }
71641b16dceSLen Brown
AMW0_get_u32(u32 * value,u32 cap)71738db157dSLee, Chun-Yi static acpi_status AMW0_get_u32(u32 *value, u32 cap)
71841b16dceSLen Brown {
71941b16dceSLen Brown int err;
72041b16dceSLen Brown u8 result;
72141b16dceSLen Brown
72241b16dceSLen Brown switch (cap) {
72341b16dceSLen Brown case ACER_CAP_MAILLED:
72441b16dceSLen Brown switch (quirks->mailled) {
72541b16dceSLen Brown default:
72641b16dceSLen Brown err = ec_read(0xA, &result);
72741b16dceSLen Brown if (err)
72841b16dceSLen Brown return AE_ERROR;
72941b16dceSLen Brown *value = (result >> 7) & 0x1;
73041b16dceSLen Brown return AE_OK;
73141b16dceSLen Brown }
73241b16dceSLen Brown break;
73341b16dceSLen Brown case ACER_CAP_WIRELESS:
73441b16dceSLen Brown switch (quirks->wireless) {
73541b16dceSLen Brown case 1:
73641b16dceSLen Brown err = ec_read(0x7B, &result);
73741b16dceSLen Brown if (err)
73841b16dceSLen Brown return AE_ERROR;
73941b16dceSLen Brown *value = result & 0x1;
74041b16dceSLen Brown return AE_OK;
74141b16dceSLen Brown case 2:
74241b16dceSLen Brown err = ec_read(0x71, &result);
74341b16dceSLen Brown if (err)
74441b16dceSLen Brown return AE_ERROR;
74541b16dceSLen Brown *value = result & 0x1;
74641b16dceSLen Brown return AE_OK;
74715b956a0SLee, Chun-Yi case 3:
74815b956a0SLee, Chun-Yi err = ec_read(0x78, &result);
74915b956a0SLee, Chun-Yi if (err)
75015b956a0SLee, Chun-Yi return AE_ERROR;
75115b956a0SLee, Chun-Yi *value = result & 0x1;
75215b956a0SLee, Chun-Yi return AE_OK;
75341b16dceSLen Brown default:
75441b16dceSLen Brown err = ec_read(0xA, &result);
75541b16dceSLen Brown if (err)
75641b16dceSLen Brown return AE_ERROR;
75741b16dceSLen Brown *value = (result >> 2) & 0x1;
75841b16dceSLen Brown return AE_OK;
75941b16dceSLen Brown }
76041b16dceSLen Brown break;
76141b16dceSLen Brown case ACER_CAP_BLUETOOTH:
76241b16dceSLen Brown switch (quirks->bluetooth) {
76341b16dceSLen Brown default:
76441b16dceSLen Brown err = ec_read(0xA, &result);
76541b16dceSLen Brown if (err)
76641b16dceSLen Brown return AE_ERROR;
76741b16dceSLen Brown *value = (result >> 4) & 0x1;
76841b16dceSLen Brown return AE_OK;
76941b16dceSLen Brown }
77041b16dceSLen Brown break;
77141b16dceSLen Brown case ACER_CAP_BRIGHTNESS:
77241b16dceSLen Brown switch (quirks->brightness) {
77341b16dceSLen Brown default:
77441b16dceSLen Brown err = ec_read(0x83, &result);
77541b16dceSLen Brown if (err)
77641b16dceSLen Brown return AE_ERROR;
77741b16dceSLen Brown *value = result;
77841b16dceSLen Brown return AE_OK;
77941b16dceSLen Brown }
78041b16dceSLen Brown break;
78141b16dceSLen Brown default:
78241b16dceSLen Brown return AE_ERROR;
78341b16dceSLen Brown }
78441b16dceSLen Brown return AE_OK;
78541b16dceSLen Brown }
78641b16dceSLen Brown
AMW0_set_u32(u32 value,u32 cap)78738db157dSLee, Chun-Yi static acpi_status AMW0_set_u32(u32 value, u32 cap)
78841b16dceSLen Brown {
78941b16dceSLen Brown struct wmab_args args;
79041b16dceSLen Brown
79141b16dceSLen Brown args.eax = ACER_AMW0_WRITE;
79241b16dceSLen Brown args.ebx = value ? (1<<8) : 0;
79341b16dceSLen Brown args.ecx = args.edx = 0;
79441b16dceSLen Brown
79541b16dceSLen Brown switch (cap) {
79641b16dceSLen Brown case ACER_CAP_MAILLED:
79741b16dceSLen Brown if (value > 1)
79841b16dceSLen Brown return AE_BAD_PARAMETER;
79941b16dceSLen Brown args.ebx |= ACER_AMW0_MAILLED_MASK;
80041b16dceSLen Brown break;
80141b16dceSLen Brown case ACER_CAP_WIRELESS:
80241b16dceSLen Brown if (value > 1)
80341b16dceSLen Brown return AE_BAD_PARAMETER;
80441b16dceSLen Brown args.ebx |= ACER_AMW0_WIRELESS_MASK;
80541b16dceSLen Brown break;
80641b16dceSLen Brown case ACER_CAP_BLUETOOTH:
80741b16dceSLen Brown if (value > 1)
80841b16dceSLen Brown return AE_BAD_PARAMETER;
80941b16dceSLen Brown args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
81041b16dceSLen Brown break;
81141b16dceSLen Brown case ACER_CAP_BRIGHTNESS:
81241b16dceSLen Brown if (value > max_brightness)
81341b16dceSLen Brown return AE_BAD_PARAMETER;
81441b16dceSLen Brown switch (quirks->brightness) {
81541b16dceSLen Brown default:
81641b16dceSLen Brown return ec_write(0x83, value);
81741b16dceSLen Brown }
81841b16dceSLen Brown default:
81941b16dceSLen Brown return AE_ERROR;
82041b16dceSLen Brown }
82141b16dceSLen Brown
82241b16dceSLen Brown /* Actually do the set */
82341b16dceSLen Brown return wmab_execute(&args, NULL);
82441b16dceSLen Brown }
82541b16dceSLen Brown
AMW0_find_mailled(void)82676d51dd9SMathias Krause static acpi_status __init AMW0_find_mailled(void)
82741b16dceSLen Brown {
82841b16dceSLen Brown struct wmab_args args;
82941b16dceSLen Brown struct wmab_ret ret;
83041b16dceSLen Brown acpi_status status = AE_OK;
83141b16dceSLen Brown struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
83241b16dceSLen Brown union acpi_object *obj;
83341b16dceSLen Brown
83441b16dceSLen Brown args.eax = 0x86;
83541b16dceSLen Brown args.ebx = args.ecx = args.edx = 0;
83641b16dceSLen Brown
83741b16dceSLen Brown status = wmab_execute(&args, &out);
83841b16dceSLen Brown if (ACPI_FAILURE(status))
83941b16dceSLen Brown return status;
84041b16dceSLen Brown
84141b16dceSLen Brown obj = (union acpi_object *) out.pointer;
84241b16dceSLen Brown if (obj && obj->type == ACPI_TYPE_BUFFER &&
84341b16dceSLen Brown obj->buffer.length == sizeof(struct wmab_ret)) {
84441b16dceSLen Brown ret = *((struct wmab_ret *) obj->buffer.pointer);
84541b16dceSLen Brown } else {
8467677fbdfSAxel Lin kfree(out.pointer);
84741b16dceSLen Brown return AE_ERROR;
84841b16dceSLen Brown }
84941b16dceSLen Brown
85041b16dceSLen Brown if (ret.eex & 0x1)
85141b16dceSLen Brown interface->capability |= ACER_CAP_MAILLED;
85241b16dceSLen Brown
85341b16dceSLen Brown kfree(out.pointer);
85441b16dceSLen Brown
85541b16dceSLen Brown return AE_OK;
85641b16dceSLen Brown }
85741b16dceSLen Brown
85876d51dd9SMathias Krause static const struct acpi_device_id norfkill_ids[] __initconst = {
859461e7437SIke Panhc { "VPC2004", 0},
860461e7437SIke Panhc { "IBM0068", 0},
861461e7437SIke Panhc { "LEN0068", 0},
8625719b819SLee, Chun-Yi { "SNY5001", 0}, /* sony-laptop in charge */
863628b3198SMichael Powell { "HPQ6601", 0},
864461e7437SIke Panhc { "", 0},
865461e7437SIke Panhc };
866461e7437SIke Panhc
AMW0_set_cap_acpi_check_device(void)86776d51dd9SMathias Krause static int __init AMW0_set_cap_acpi_check_device(void)
868461e7437SIke Panhc {
869461e7437SIke Panhc const struct acpi_device_id *id;
870461e7437SIke Panhc
871461e7437SIke Panhc for (id = norfkill_ids; id->id[0]; id++)
8728c92a75eSLukas Wunner if (acpi_dev_found(id->id))
8738c92a75eSLukas Wunner return true;
8748c92a75eSLukas Wunner
8758c92a75eSLukas Wunner return false;
876461e7437SIke Panhc }
877461e7437SIke Panhc
AMW0_set_capabilities(void)87876d51dd9SMathias Krause static acpi_status __init AMW0_set_capabilities(void)
87941b16dceSLen Brown {
88041b16dceSLen Brown struct wmab_args args;
88141b16dceSLen Brown struct wmab_ret ret;
8827677fbdfSAxel Lin acpi_status status;
88341b16dceSLen Brown struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
88441b16dceSLen Brown union acpi_object *obj;
88541b16dceSLen Brown
88641b16dceSLen Brown /*
88741b16dceSLen Brown * On laptops with this strange GUID (non Acer), normal probing doesn't
88841b16dceSLen Brown * work.
88941b16dceSLen Brown */
89041b16dceSLen Brown if (wmi_has_guid(AMW0_GUID2)) {
891461e7437SIke Panhc if ((quirks != &quirk_unknown) ||
892461e7437SIke Panhc !AMW0_set_cap_acpi_check_device())
89341b16dceSLen Brown interface->capability |= ACER_CAP_WIRELESS;
89441b16dceSLen Brown return AE_OK;
89541b16dceSLen Brown }
89641b16dceSLen Brown
89741b16dceSLen Brown args.eax = ACER_AMW0_WRITE;
89841b16dceSLen Brown args.ecx = args.edx = 0;
89941b16dceSLen Brown
90041b16dceSLen Brown args.ebx = 0xa2 << 8;
90141b16dceSLen Brown args.ebx |= ACER_AMW0_WIRELESS_MASK;
90241b16dceSLen Brown
90341b16dceSLen Brown status = wmab_execute(&args, &out);
90441b16dceSLen Brown if (ACPI_FAILURE(status))
90541b16dceSLen Brown return status;
90641b16dceSLen Brown
9077677fbdfSAxel Lin obj = out.pointer;
90841b16dceSLen Brown if (obj && obj->type == ACPI_TYPE_BUFFER &&
90941b16dceSLen Brown obj->buffer.length == sizeof(struct wmab_ret)) {
91041b16dceSLen Brown ret = *((struct wmab_ret *) obj->buffer.pointer);
91141b16dceSLen Brown } else {
9127677fbdfSAxel Lin status = AE_ERROR;
9137677fbdfSAxel Lin goto out;
91441b16dceSLen Brown }
91541b16dceSLen Brown
91641b16dceSLen Brown if (ret.eax & 0x1)
91741b16dceSLen Brown interface->capability |= ACER_CAP_WIRELESS;
91841b16dceSLen Brown
91941b16dceSLen Brown args.ebx = 2 << 8;
92041b16dceSLen Brown args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
92141b16dceSLen Brown
9227677fbdfSAxel Lin /*
9237677fbdfSAxel Lin * It's ok to use existing buffer for next wmab_execute call.
9247677fbdfSAxel Lin * But we need to kfree(out.pointer) if next wmab_execute fail.
9257677fbdfSAxel Lin */
92641b16dceSLen Brown status = wmab_execute(&args, &out);
92741b16dceSLen Brown if (ACPI_FAILURE(status))
9287677fbdfSAxel Lin goto out;
92941b16dceSLen Brown
93041b16dceSLen Brown obj = (union acpi_object *) out.pointer;
93141b16dceSLen Brown if (obj && obj->type == ACPI_TYPE_BUFFER
93241b16dceSLen Brown && obj->buffer.length == sizeof(struct wmab_ret)) {
93341b16dceSLen Brown ret = *((struct wmab_ret *) obj->buffer.pointer);
93441b16dceSLen Brown } else {
9357677fbdfSAxel Lin status = AE_ERROR;
9367677fbdfSAxel Lin goto out;
93741b16dceSLen Brown }
93841b16dceSLen Brown
93941b16dceSLen Brown if (ret.eax & 0x1)
94041b16dceSLen Brown interface->capability |= ACER_CAP_BLUETOOTH;
94141b16dceSLen Brown
94241b16dceSLen Brown /*
94341b16dceSLen Brown * This appears to be safe to enable, since all Wistron based laptops
94441b16dceSLen Brown * appear to use the same EC register for brightness, even if they
94541b16dceSLen Brown * differ for wireless, etc
94641b16dceSLen Brown */
94741b16dceSLen Brown if (quirks->brightness >= 0)
94841b16dceSLen Brown interface->capability |= ACER_CAP_BRIGHTNESS;
94941b16dceSLen Brown
9507677fbdfSAxel Lin status = AE_OK;
9517677fbdfSAxel Lin out:
9527677fbdfSAxel Lin kfree(out.pointer);
9537677fbdfSAxel Lin return status;
95441b16dceSLen Brown }
95541b16dceSLen Brown
95641b16dceSLen Brown static struct wmi_interface AMW0_interface = {
95741b16dceSLen Brown .type = ACER_AMW0,
95841b16dceSLen Brown };
95941b16dceSLen Brown
96041b16dceSLen Brown static struct wmi_interface AMW0_V2_interface = {
96141b16dceSLen Brown .type = ACER_AMW0_V2,
96241b16dceSLen Brown };
96341b16dceSLen Brown
96441b16dceSLen Brown /*
96541b16dceSLen Brown * New interface (The WMID interface)
96641b16dceSLen Brown */
96741b16dceSLen Brown static acpi_status
WMI_execute_u32(u32 method_id,u32 in,u32 * out)96841b16dceSLen Brown WMI_execute_u32(u32 method_id, u32 in, u32 *out)
96941b16dceSLen Brown {
97041b16dceSLen Brown struct acpi_buffer input = { (acpi_size) sizeof(u32), (void *)(&in) };
97141b16dceSLen Brown struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
97241b16dceSLen Brown union acpi_object *obj;
973f20aaba9SLee, Chun-Yi u32 tmp = 0;
97441b16dceSLen Brown acpi_status status;
97541b16dceSLen Brown
97662fc743cSLee, Chun-Yi status = wmi_evaluate_method(WMID_GUID1, 0, method_id, &input, &result);
97741b16dceSLen Brown
97841b16dceSLen Brown if (ACPI_FAILURE(status))
97941b16dceSLen Brown return status;
98041b16dceSLen Brown
98141b16dceSLen Brown obj = (union acpi_object *) result.pointer;
982f20aaba9SLee, Chun-Yi if (obj) {
983f20aaba9SLee, Chun-Yi if (obj->type == ACPI_TYPE_BUFFER &&
984ab6a9316SLee, Chun-Yi (obj->buffer.length == sizeof(u32) ||
985ab6a9316SLee, Chun-Yi obj->buffer.length == sizeof(u64))) {
98641b16dceSLen Brown tmp = *((u32 *) obj->buffer.pointer);
987987dfbaaSLee, Chun-Yi } else if (obj->type == ACPI_TYPE_INTEGER) {
988987dfbaaSLee, Chun-Yi tmp = (u32) obj->integer.value;
989f20aaba9SLee, Chun-Yi }
99041b16dceSLen Brown }
99141b16dceSLen Brown
99241b16dceSLen Brown if (out)
99341b16dceSLen Brown *out = tmp;
99441b16dceSLen Brown
99541b16dceSLen Brown kfree(result.pointer);
99641b16dceSLen Brown
99741b16dceSLen Brown return status;
99841b16dceSLen Brown }
99941b16dceSLen Brown
WMID_get_u32(u32 * value,u32 cap)100038db157dSLee, Chun-Yi static acpi_status WMID_get_u32(u32 *value, u32 cap)
100141b16dceSLen Brown {
100241b16dceSLen Brown acpi_status status;
100341b16dceSLen Brown u8 tmp;
100441b16dceSLen Brown u32 result, method_id = 0;
100541b16dceSLen Brown
100641b16dceSLen Brown switch (cap) {
100741b16dceSLen Brown case ACER_CAP_WIRELESS:
100841b16dceSLen Brown method_id = ACER_WMID_GET_WIRELESS_METHODID;
100941b16dceSLen Brown break;
101041b16dceSLen Brown case ACER_CAP_BLUETOOTH:
101141b16dceSLen Brown method_id = ACER_WMID_GET_BLUETOOTH_METHODID;
101241b16dceSLen Brown break;
101341b16dceSLen Brown case ACER_CAP_BRIGHTNESS:
101441b16dceSLen Brown method_id = ACER_WMID_GET_BRIGHTNESS_METHODID;
101541b16dceSLen Brown break;
101641b16dceSLen Brown case ACER_CAP_THREEG:
101741b16dceSLen Brown method_id = ACER_WMID_GET_THREEG_METHODID;
101841b16dceSLen Brown break;
101941b16dceSLen Brown case ACER_CAP_MAILLED:
102041b16dceSLen Brown if (quirks->mailled == 1) {
102141b16dceSLen Brown ec_read(0x9f, &tmp);
102241b16dceSLen Brown *value = tmp & 0x1;
102341b16dceSLen Brown return 0;
102441b16dceSLen Brown }
1025df561f66SGustavo A. R. Silva fallthrough;
102641b16dceSLen Brown default:
102741b16dceSLen Brown return AE_ERROR;
102841b16dceSLen Brown }
102941b16dceSLen Brown status = WMI_execute_u32(method_id, 0, &result);
103041b16dceSLen Brown
103141b16dceSLen Brown if (ACPI_SUCCESS(status))
103241b16dceSLen Brown *value = (u8)result;
103341b16dceSLen Brown
103441b16dceSLen Brown return status;
103541b16dceSLen Brown }
103641b16dceSLen Brown
WMID_set_u32(u32 value,u32 cap)103738db157dSLee, Chun-Yi static acpi_status WMID_set_u32(u32 value, u32 cap)
103841b16dceSLen Brown {
103941b16dceSLen Brown u32 method_id = 0;
104041b16dceSLen Brown char param;
104141b16dceSLen Brown
104241b16dceSLen Brown switch (cap) {
104341b16dceSLen Brown case ACER_CAP_BRIGHTNESS:
104441b16dceSLen Brown if (value > max_brightness)
104541b16dceSLen Brown return AE_BAD_PARAMETER;
104641b16dceSLen Brown method_id = ACER_WMID_SET_BRIGHTNESS_METHODID;
104741b16dceSLen Brown break;
104841b16dceSLen Brown case ACER_CAP_WIRELESS:
104941b16dceSLen Brown if (value > 1)
105041b16dceSLen Brown return AE_BAD_PARAMETER;
105141b16dceSLen Brown method_id = ACER_WMID_SET_WIRELESS_METHODID;
105241b16dceSLen Brown break;
105341b16dceSLen Brown case ACER_CAP_BLUETOOTH:
105441b16dceSLen Brown if (value > 1)
105541b16dceSLen Brown return AE_BAD_PARAMETER;
105641b16dceSLen Brown method_id = ACER_WMID_SET_BLUETOOTH_METHODID;
105741b16dceSLen Brown break;
105841b16dceSLen Brown case ACER_CAP_THREEG:
105941b16dceSLen Brown if (value > 1)
106041b16dceSLen Brown return AE_BAD_PARAMETER;
106141b16dceSLen Brown method_id = ACER_WMID_SET_THREEG_METHODID;
106241b16dceSLen Brown break;
106341b16dceSLen Brown case ACER_CAP_MAILLED:
106441b16dceSLen Brown if (value > 1)
106541b16dceSLen Brown return AE_BAD_PARAMETER;
106641b16dceSLen Brown if (quirks->mailled == 1) {
106741b16dceSLen Brown param = value ? 0x92 : 0x93;
1068181d683dSDmitry Torokhov i8042_lock_chip();
106941b16dceSLen Brown i8042_command(¶m, 0x1059);
1070181d683dSDmitry Torokhov i8042_unlock_chip();
107141b16dceSLen Brown return 0;
107241b16dceSLen Brown }
107341b16dceSLen Brown break;
107441b16dceSLen Brown default:
107541b16dceSLen Brown return AE_ERROR;
107641b16dceSLen Brown }
107741b16dceSLen Brown return WMI_execute_u32(method_id, (u32)value, NULL);
107841b16dceSLen Brown }
107941b16dceSLen Brown
wmid3_get_device_status(u32 * value,u16 device)108072e71de1SLee, Chun-Yi static acpi_status wmid3_get_device_status(u32 *value, u16 device)
108172e71de1SLee, Chun-Yi {
108272e71de1SLee, Chun-Yi struct wmid3_gds_return_value return_value;
108372e71de1SLee, Chun-Yi acpi_status status;
108472e71de1SLee, Chun-Yi union acpi_object *obj;
1085996d23baSLee, Chun-Yi struct wmid3_gds_get_input_param params = {
108672e71de1SLee, Chun-Yi .function_num = 0x1,
108734b6cfabSLee, Chun-Yi .hotkey_number = commun_fn_key_number,
108872e71de1SLee, Chun-Yi .devices = device,
108972e71de1SLee, Chun-Yi };
109072e71de1SLee, Chun-Yi struct acpi_buffer input = {
1091996d23baSLee, Chun-Yi sizeof(struct wmid3_gds_get_input_param),
109272e71de1SLee, Chun-Yi ¶ms
109372e71de1SLee, Chun-Yi };
109472e71de1SLee, Chun-Yi struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
109572e71de1SLee, Chun-Yi
109672e71de1SLee, Chun-Yi status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output);
109772e71de1SLee, Chun-Yi if (ACPI_FAILURE(status))
109872e71de1SLee, Chun-Yi return status;
109972e71de1SLee, Chun-Yi
110072e71de1SLee, Chun-Yi obj = output.pointer;
110172e71de1SLee, Chun-Yi
110272e71de1SLee, Chun-Yi if (!obj)
110372e71de1SLee, Chun-Yi return AE_ERROR;
110472e71de1SLee, Chun-Yi else if (obj->type != ACPI_TYPE_BUFFER) {
110572e71de1SLee, Chun-Yi kfree(obj);
110672e71de1SLee, Chun-Yi return AE_ERROR;
110772e71de1SLee, Chun-Yi }
110872e71de1SLee, Chun-Yi if (obj->buffer.length != 8) {
110972e71de1SLee, Chun-Yi pr_warn("Unknown buffer length %d\n", obj->buffer.length);
111072e71de1SLee, Chun-Yi kfree(obj);
111172e71de1SLee, Chun-Yi return AE_ERROR;
111272e71de1SLee, Chun-Yi }
111372e71de1SLee, Chun-Yi
111472e71de1SLee, Chun-Yi return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
111572e71de1SLee, Chun-Yi kfree(obj);
111672e71de1SLee, Chun-Yi
111772e71de1SLee, Chun-Yi if (return_value.error_code || return_value.ec_return_value)
111872e71de1SLee, Chun-Yi pr_warn("Get 0x%x Device Status failed: 0x%x - 0x%x\n",
111972e71de1SLee, Chun-Yi device,
112072e71de1SLee, Chun-Yi return_value.error_code,
112172e71de1SLee, Chun-Yi return_value.ec_return_value);
112272e71de1SLee, Chun-Yi else
112372e71de1SLee, Chun-Yi *value = !!(return_value.devices & device);
112472e71de1SLee, Chun-Yi
112572e71de1SLee, Chun-Yi return status;
112672e71de1SLee, Chun-Yi }
112772e71de1SLee, Chun-Yi
wmid_v2_get_u32(u32 * value,u32 cap)112872e71de1SLee, Chun-Yi static acpi_status wmid_v2_get_u32(u32 *value, u32 cap)
112972e71de1SLee, Chun-Yi {
113072e71de1SLee, Chun-Yi u16 device;
113172e71de1SLee, Chun-Yi
113272e71de1SLee, Chun-Yi switch (cap) {
113372e71de1SLee, Chun-Yi case ACER_CAP_WIRELESS:
113472e71de1SLee, Chun-Yi device = ACER_WMID3_GDS_WIRELESS;
113572e71de1SLee, Chun-Yi break;
113672e71de1SLee, Chun-Yi case ACER_CAP_BLUETOOTH:
113772e71de1SLee, Chun-Yi device = ACER_WMID3_GDS_BLUETOOTH;
113872e71de1SLee, Chun-Yi break;
113972e71de1SLee, Chun-Yi case ACER_CAP_THREEG:
114072e71de1SLee, Chun-Yi device = ACER_WMID3_GDS_THREEG;
114172e71de1SLee, Chun-Yi break;
114272e71de1SLee, Chun-Yi default:
114372e71de1SLee, Chun-Yi return AE_ERROR;
114472e71de1SLee, Chun-Yi }
114572e71de1SLee, Chun-Yi return wmid3_get_device_status(value, device);
114672e71de1SLee, Chun-Yi }
114772e71de1SLee, Chun-Yi
wmid3_set_device_status(u32 value,u16 device)114872e71de1SLee, Chun-Yi static acpi_status wmid3_set_device_status(u32 value, u16 device)
114972e71de1SLee, Chun-Yi {
115072e71de1SLee, Chun-Yi struct wmid3_gds_return_value return_value;
115172e71de1SLee, Chun-Yi acpi_status status;
115272e71de1SLee, Chun-Yi union acpi_object *obj;
115372e71de1SLee, Chun-Yi u16 devices;
1154996d23baSLee, Chun-Yi struct wmid3_gds_get_input_param get_params = {
115572e71de1SLee, Chun-Yi .function_num = 0x1,
115634b6cfabSLee, Chun-Yi .hotkey_number = commun_fn_key_number,
11571d1fc8a7SLee, Chun-Yi .devices = commun_func_bitmap,
115872e71de1SLee, Chun-Yi };
1159996d23baSLee, Chun-Yi struct acpi_buffer get_input = {
1160996d23baSLee, Chun-Yi sizeof(struct wmid3_gds_get_input_param),
1161996d23baSLee, Chun-Yi &get_params
1162996d23baSLee, Chun-Yi };
1163996d23baSLee, Chun-Yi struct wmid3_gds_set_input_param set_params = {
1164996d23baSLee, Chun-Yi .function_num = 0x2,
1165996d23baSLee, Chun-Yi .hotkey_number = commun_fn_key_number,
1166996d23baSLee, Chun-Yi .devices = commun_func_bitmap,
1167996d23baSLee, Chun-Yi };
1168996d23baSLee, Chun-Yi struct acpi_buffer set_input = {
1169996d23baSLee, Chun-Yi sizeof(struct wmid3_gds_set_input_param),
1170996d23baSLee, Chun-Yi &set_params
117172e71de1SLee, Chun-Yi };
117272e71de1SLee, Chun-Yi struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
117372e71de1SLee, Chun-Yi struct acpi_buffer output2 = { ACPI_ALLOCATE_BUFFER, NULL };
117472e71de1SLee, Chun-Yi
1175996d23baSLee, Chun-Yi status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &get_input, &output);
117672e71de1SLee, Chun-Yi if (ACPI_FAILURE(status))
117772e71de1SLee, Chun-Yi return status;
117872e71de1SLee, Chun-Yi
117972e71de1SLee, Chun-Yi obj = output.pointer;
118072e71de1SLee, Chun-Yi
118172e71de1SLee, Chun-Yi if (!obj)
118272e71de1SLee, Chun-Yi return AE_ERROR;
118372e71de1SLee, Chun-Yi else if (obj->type != ACPI_TYPE_BUFFER) {
118472e71de1SLee, Chun-Yi kfree(obj);
118572e71de1SLee, Chun-Yi return AE_ERROR;
118672e71de1SLee, Chun-Yi }
118772e71de1SLee, Chun-Yi if (obj->buffer.length != 8) {
11886e71f38bSJoe Perches pr_warn("Unknown buffer length %d\n", obj->buffer.length);
118972e71de1SLee, Chun-Yi kfree(obj);
119072e71de1SLee, Chun-Yi return AE_ERROR;
119172e71de1SLee, Chun-Yi }
119272e71de1SLee, Chun-Yi
119372e71de1SLee, Chun-Yi return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
119472e71de1SLee, Chun-Yi kfree(obj);
119572e71de1SLee, Chun-Yi
119672e71de1SLee, Chun-Yi if (return_value.error_code || return_value.ec_return_value) {
11976e71f38bSJoe Perches pr_warn("Get Current Device Status failed: 0x%x - 0x%x\n",
11986e71f38bSJoe Perches return_value.error_code,
119972e71de1SLee, Chun-Yi return_value.ec_return_value);
120072e71de1SLee, Chun-Yi return status;
120172e71de1SLee, Chun-Yi }
120272e71de1SLee, Chun-Yi
120372e71de1SLee, Chun-Yi devices = return_value.devices;
1204996d23baSLee, Chun-Yi set_params.devices = (value) ? (devices | device) : (devices & ~device);
120572e71de1SLee, Chun-Yi
1206996d23baSLee, Chun-Yi status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &set_input, &output2);
120772e71de1SLee, Chun-Yi if (ACPI_FAILURE(status))
120872e71de1SLee, Chun-Yi return status;
120972e71de1SLee, Chun-Yi
121072e71de1SLee, Chun-Yi obj = output2.pointer;
121172e71de1SLee, Chun-Yi
121272e71de1SLee, Chun-Yi if (!obj)
121372e71de1SLee, Chun-Yi return AE_ERROR;
121472e71de1SLee, Chun-Yi else if (obj->type != ACPI_TYPE_BUFFER) {
121572e71de1SLee, Chun-Yi kfree(obj);
121672e71de1SLee, Chun-Yi return AE_ERROR;
121772e71de1SLee, Chun-Yi }
121872e71de1SLee, Chun-Yi if (obj->buffer.length != 4) {
12196e71f38bSJoe Perches pr_warn("Unknown buffer length %d\n", obj->buffer.length);
122072e71de1SLee, Chun-Yi kfree(obj);
122172e71de1SLee, Chun-Yi return AE_ERROR;
122272e71de1SLee, Chun-Yi }
122372e71de1SLee, Chun-Yi
122472e71de1SLee, Chun-Yi return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
122572e71de1SLee, Chun-Yi kfree(obj);
122672e71de1SLee, Chun-Yi
122772e71de1SLee, Chun-Yi if (return_value.error_code || return_value.ec_return_value)
12286e71f38bSJoe Perches pr_warn("Set Device Status failed: 0x%x - 0x%x\n",
12296e71f38bSJoe Perches return_value.error_code,
123072e71de1SLee, Chun-Yi return_value.ec_return_value);
123172e71de1SLee, Chun-Yi
123272e71de1SLee, Chun-Yi return status;
123372e71de1SLee, Chun-Yi }
123472e71de1SLee, Chun-Yi
wmid_v2_set_u32(u32 value,u32 cap)123572e71de1SLee, Chun-Yi static acpi_status wmid_v2_set_u32(u32 value, u32 cap)
123672e71de1SLee, Chun-Yi {
123772e71de1SLee, Chun-Yi u16 device;
123872e71de1SLee, Chun-Yi
123972e71de1SLee, Chun-Yi switch (cap) {
124072e71de1SLee, Chun-Yi case ACER_CAP_WIRELESS:
124172e71de1SLee, Chun-Yi device = ACER_WMID3_GDS_WIRELESS;
124272e71de1SLee, Chun-Yi break;
124372e71de1SLee, Chun-Yi case ACER_CAP_BLUETOOTH:
124472e71de1SLee, Chun-Yi device = ACER_WMID3_GDS_BLUETOOTH;
124572e71de1SLee, Chun-Yi break;
124672e71de1SLee, Chun-Yi case ACER_CAP_THREEG:
124772e71de1SLee, Chun-Yi device = ACER_WMID3_GDS_THREEG;
124872e71de1SLee, Chun-Yi break;
124972e71de1SLee, Chun-Yi default:
125072e71de1SLee, Chun-Yi return AE_ERROR;
125172e71de1SLee, Chun-Yi }
125272e71de1SLee, Chun-Yi return wmid3_set_device_status(value, device);
125372e71de1SLee, Chun-Yi }
125472e71de1SLee, Chun-Yi
type_aa_dmi_decode(const struct dmi_header * header,void * d)125576d51dd9SMathias Krause static void __init type_aa_dmi_decode(const struct dmi_header *header, void *d)
12566c3df88fSLee, Chun-Yi {
12576c3df88fSLee, Chun-Yi struct hotkey_function_type_aa *type_aa;
12586c3df88fSLee, Chun-Yi
12596c3df88fSLee, Chun-Yi /* We are looking for OEM-specific Type AAh */
12606c3df88fSLee, Chun-Yi if (header->type != 0xAA)
12616c3df88fSLee, Chun-Yi return;
12626c3df88fSLee, Chun-Yi
12636c3df88fSLee, Chun-Yi has_type_aa = true;
12646c3df88fSLee, Chun-Yi type_aa = (struct hotkey_function_type_aa *) header;
12656c3df88fSLee, Chun-Yi
1266cae15702SLee, Chun-Yi pr_info("Function bitmap for Communication Button: 0x%x\n",
12676c3df88fSLee, Chun-Yi type_aa->commun_func_bitmap);
12681d1fc8a7SLee, Chun-Yi commun_func_bitmap = type_aa->commun_func_bitmap;
12696c3df88fSLee, Chun-Yi
12706c3df88fSLee, Chun-Yi if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_WIRELESS)
12716c3df88fSLee, Chun-Yi interface->capability |= ACER_CAP_WIRELESS;
12726c3df88fSLee, Chun-Yi if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_THREEG)
12736c3df88fSLee, Chun-Yi interface->capability |= ACER_CAP_THREEG;
12746c3df88fSLee, Chun-Yi if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_BLUETOOTH)
12756c3df88fSLee, Chun-Yi interface->capability |= ACER_CAP_BLUETOOTH;
12767c936d8dSHans de Goede if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_RFBTN)
12773e2bc5c5SJoão Paulo Rechi Vita commun_func_bitmap &= ~ACER_WMID3_GDS_RFBTN;
127834b6cfabSLee, Chun-Yi
127934b6cfabSLee, Chun-Yi commun_fn_key_number = type_aa->commun_fn_key_number;
12806c3df88fSLee, Chun-Yi }
12816c3df88fSLee, Chun-Yi
WMID_set_capabilities(void)128276d51dd9SMathias Krause static acpi_status __init WMID_set_capabilities(void)
128341b16dceSLen Brown {
128441b16dceSLen Brown struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
128541b16dceSLen Brown union acpi_object *obj;
128641b16dceSLen Brown acpi_status status;
128741b16dceSLen Brown u32 devices;
128841b16dceSLen Brown
128962fc743cSLee, Chun-Yi status = wmi_query_block(WMID_GUID2, 0, &out);
129041b16dceSLen Brown if (ACPI_FAILURE(status))
129141b16dceSLen Brown return status;
129241b16dceSLen Brown
129341b16dceSLen Brown obj = (union acpi_object *) out.pointer;
1294f20aaba9SLee, Chun-Yi if (obj) {
1295f20aaba9SLee, Chun-Yi if (obj->type == ACPI_TYPE_BUFFER &&
1296ab6a9316SLee, Chun-Yi (obj->buffer.length == sizeof(u32) ||
1297ab6a9316SLee, Chun-Yi obj->buffer.length == sizeof(u64))) {
129841b16dceSLen Brown devices = *((u32 *) obj->buffer.pointer);
1299987dfbaaSLee, Chun-Yi } else if (obj->type == ACPI_TYPE_INTEGER) {
1300987dfbaaSLee, Chun-Yi devices = (u32) obj->integer.value;
1301f24c96eaSLee, Chun-Yi } else {
1302f24c96eaSLee, Chun-Yi kfree(out.pointer);
1303f24c96eaSLee, Chun-Yi return AE_ERROR;
1304f20aaba9SLee, Chun-Yi }
130541b16dceSLen Brown } else {
130666904863SAxel Lin kfree(out.pointer);
130741b16dceSLen Brown return AE_ERROR;
130841b16dceSLen Brown }
130941b16dceSLen Brown
13101fbc01a7SLee, Chun-Yi pr_info("Function bitmap for Communication Device: 0x%x\n", devices);
13111fbc01a7SLee, Chun-Yi if (devices & 0x07)
131241b16dceSLen Brown interface->capability |= ACER_CAP_WIRELESS;
1313a8d1a266SLee, Chun-Yi if (devices & 0x40)
131441b16dceSLen Brown interface->capability |= ACER_CAP_THREEG;
13156c3df88fSLee, Chun-Yi if (devices & 0x10)
13166c3df88fSLee, Chun-Yi interface->capability |= ACER_CAP_BLUETOOTH;
131741b16dceSLen Brown
131841b16dceSLen Brown if (!(devices & 0x20))
131941b16dceSLen Brown max_brightness = 0x9;
132041b16dceSLen Brown
132166904863SAxel Lin kfree(out.pointer);
132241b16dceSLen Brown return status;
132341b16dceSLen Brown }
132441b16dceSLen Brown
132541b16dceSLen Brown static struct wmi_interface wmid_interface = {
132641b16dceSLen Brown .type = ACER_WMID,
132741b16dceSLen Brown };
132841b16dceSLen Brown
132972e71de1SLee, Chun-Yi static struct wmi_interface wmid_v2_interface = {
133072e71de1SLee, Chun-Yi .type = ACER_WMID_v2,
133172e71de1SLee, Chun-Yi };
133272e71de1SLee, Chun-Yi
133341b16dceSLen Brown /*
1334ca42c119SJafarAkhondali * WMID Gaming interface
1335ca42c119SJafarAkhondali */
1336ca42c119SJafarAkhondali
1337ca42c119SJafarAkhondali static acpi_status
WMI_gaming_execute_u64(u32 method_id,u64 in,u64 * out)1338ca42c119SJafarAkhondali WMI_gaming_execute_u64(u32 method_id, u64 in, u64 *out)
1339ca42c119SJafarAkhondali {
1340ca42c119SJafarAkhondali struct acpi_buffer input = { (acpi_size) sizeof(u64), (void *)(&in) };
1341ca42c119SJafarAkhondali struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
1342ca42c119SJafarAkhondali union acpi_object *obj;
1343ca42c119SJafarAkhondali u32 tmp = 0;
1344ca42c119SJafarAkhondali acpi_status status;
1345ca42c119SJafarAkhondali
1346ca42c119SJafarAkhondali status = wmi_evaluate_method(WMID_GUID4, 0, method_id, &input, &result);
1347ca42c119SJafarAkhondali
1348ca42c119SJafarAkhondali if (ACPI_FAILURE(status))
1349ca42c119SJafarAkhondali return status;
1350ca42c119SJafarAkhondali obj = (union acpi_object *) result.pointer;
1351ca42c119SJafarAkhondali
1352ca42c119SJafarAkhondali if (obj) {
1353ca42c119SJafarAkhondali if (obj->type == ACPI_TYPE_BUFFER) {
1354ca42c119SJafarAkhondali if (obj->buffer.length == sizeof(u32))
1355ca42c119SJafarAkhondali tmp = *((u32 *) obj->buffer.pointer);
1356ca42c119SJafarAkhondali else if (obj->buffer.length == sizeof(u64))
1357ca42c119SJafarAkhondali tmp = *((u64 *) obj->buffer.pointer);
1358ca42c119SJafarAkhondali } else if (obj->type == ACPI_TYPE_INTEGER) {
1359ca42c119SJafarAkhondali tmp = (u64) obj->integer.value;
1360ca42c119SJafarAkhondali }
1361ca42c119SJafarAkhondali }
1362ca42c119SJafarAkhondali
1363ca42c119SJafarAkhondali if (out)
1364ca42c119SJafarAkhondali *out = tmp;
1365ca42c119SJafarAkhondali
1366ca42c119SJafarAkhondali kfree(result.pointer);
1367ca42c119SJafarAkhondali
1368ca42c119SJafarAkhondali return status;
1369ca42c119SJafarAkhondali }
1370ca42c119SJafarAkhondali
WMID_gaming_set_u64(u64 value,u32 cap)1371ca42c119SJafarAkhondali static acpi_status WMID_gaming_set_u64(u64 value, u32 cap)
1372ca42c119SJafarAkhondali {
1373ca42c119SJafarAkhondali u32 method_id = 0;
1374ca42c119SJafarAkhondali
1375ca42c119SJafarAkhondali if (!(interface->capability & cap))
1376ca42c119SJafarAkhondali return AE_BAD_PARAMETER;
1377ca42c119SJafarAkhondali
1378ca42c119SJafarAkhondali switch (cap) {
1379ca42c119SJafarAkhondali case ACER_CAP_TURBO_LED:
1380ca42c119SJafarAkhondali method_id = ACER_WMID_SET_GAMING_LED_METHODID;
1381ca42c119SJafarAkhondali break;
1382ca42c119SJafarAkhondali case ACER_CAP_TURBO_FAN:
1383ca42c119SJafarAkhondali method_id = ACER_WMID_SET_GAMING_FAN_BEHAVIOR;
1384ca42c119SJafarAkhondali break;
1385ca42c119SJafarAkhondali case ACER_CAP_TURBO_OC:
1386ca42c119SJafarAkhondali method_id = ACER_WMID_SET_GAMING_MISC_SETTING_METHODID;
1387ca42c119SJafarAkhondali break;
1388ca42c119SJafarAkhondali default:
1389ca42c119SJafarAkhondali return AE_BAD_PARAMETER;
1390ca42c119SJafarAkhondali }
1391ca42c119SJafarAkhondali
1392ca42c119SJafarAkhondali return WMI_gaming_execute_u64(method_id, value, NULL);
1393ca42c119SJafarAkhondali }
1394ca42c119SJafarAkhondali
WMID_gaming_get_u64(u64 * value,u32 cap)1395ca42c119SJafarAkhondali static acpi_status WMID_gaming_get_u64(u64 *value, u32 cap)
1396ca42c119SJafarAkhondali {
1397ca42c119SJafarAkhondali acpi_status status;
1398ca42c119SJafarAkhondali u64 result;
1399ca42c119SJafarAkhondali u64 input;
1400ca42c119SJafarAkhondali u32 method_id;
1401ca42c119SJafarAkhondali
1402ca42c119SJafarAkhondali if (!(interface->capability & cap))
1403ca42c119SJafarAkhondali return AE_BAD_PARAMETER;
1404ca42c119SJafarAkhondali
1405ca42c119SJafarAkhondali switch (cap) {
1406ca42c119SJafarAkhondali case ACER_CAP_TURBO_LED:
1407ca42c119SJafarAkhondali method_id = ACER_WMID_GET_GAMING_LED_METHODID;
1408ca42c119SJafarAkhondali input = 0x1;
1409ca42c119SJafarAkhondali break;
1410ca42c119SJafarAkhondali default:
1411ca42c119SJafarAkhondali return AE_BAD_PARAMETER;
1412ca42c119SJafarAkhondali }
1413ca42c119SJafarAkhondali status = WMI_gaming_execute_u64(method_id, input, &result);
1414ca42c119SJafarAkhondali if (ACPI_SUCCESS(status))
1415ca42c119SJafarAkhondali *value = (u64) result;
1416ca42c119SJafarAkhondali
1417ca42c119SJafarAkhondali return status;
1418ca42c119SJafarAkhondali }
1419ca42c119SJafarAkhondali
WMID_gaming_set_fan_mode(u8 fan_mode)1420ca42c119SJafarAkhondali static void WMID_gaming_set_fan_mode(u8 fan_mode)
1421ca42c119SJafarAkhondali {
1422ca42c119SJafarAkhondali /* fan_mode = 1 is used for auto, fan_mode = 2 used for turbo*/
1423ca42c119SJafarAkhondali u64 gpu_fan_config1 = 0, gpu_fan_config2 = 0;
1424ca42c119SJafarAkhondali int i;
1425ca42c119SJafarAkhondali
1426ca42c119SJafarAkhondali if (quirks->cpu_fans > 0)
1427ca42c119SJafarAkhondali gpu_fan_config2 |= 1;
1428ca42c119SJafarAkhondali for (i = 0; i < (quirks->cpu_fans + quirks->gpu_fans); ++i)
1429ca42c119SJafarAkhondali gpu_fan_config2 |= 1 << (i + 1);
1430ca42c119SJafarAkhondali for (i = 0; i < quirks->gpu_fans; ++i)
1431ca42c119SJafarAkhondali gpu_fan_config2 |= 1 << (i + 3);
1432ca42c119SJafarAkhondali if (quirks->cpu_fans > 0)
1433ca42c119SJafarAkhondali gpu_fan_config1 |= fan_mode;
1434ca42c119SJafarAkhondali for (i = 0; i < (quirks->cpu_fans + quirks->gpu_fans); ++i)
1435ca42c119SJafarAkhondali gpu_fan_config1 |= fan_mode << (2 * i + 2);
1436ca42c119SJafarAkhondali for (i = 0; i < quirks->gpu_fans; ++i)
1437ca42c119SJafarAkhondali gpu_fan_config1 |= fan_mode << (2 * i + 6);
1438ca42c119SJafarAkhondali WMID_gaming_set_u64(gpu_fan_config2 | gpu_fan_config1 << 16, ACER_CAP_TURBO_FAN);
1439ca42c119SJafarAkhondali }
1440ca42c119SJafarAkhondali
1441ca42c119SJafarAkhondali /*
144241b16dceSLen Brown * Generic Device (interface-independent)
144341b16dceSLen Brown */
144441b16dceSLen Brown
get_u32(u32 * value,u32 cap)144541b16dceSLen Brown static acpi_status get_u32(u32 *value, u32 cap)
144641b16dceSLen Brown {
144741b16dceSLen Brown acpi_status status = AE_ERROR;
144841b16dceSLen Brown
144941b16dceSLen Brown switch (interface->type) {
145041b16dceSLen Brown case ACER_AMW0:
145138db157dSLee, Chun-Yi status = AMW0_get_u32(value, cap);
145241b16dceSLen Brown break;
145341b16dceSLen Brown case ACER_AMW0_V2:
145441b16dceSLen Brown if (cap == ACER_CAP_MAILLED) {
145538db157dSLee, Chun-Yi status = AMW0_get_u32(value, cap);
145641b16dceSLen Brown break;
145741b16dceSLen Brown }
1458df561f66SGustavo A. R. Silva fallthrough;
145941b16dceSLen Brown case ACER_WMID:
146038db157dSLee, Chun-Yi status = WMID_get_u32(value, cap);
146141b16dceSLen Brown break;
146272e71de1SLee, Chun-Yi case ACER_WMID_v2:
146372e71de1SLee, Chun-Yi if (cap & (ACER_CAP_WIRELESS |
146472e71de1SLee, Chun-Yi ACER_CAP_BLUETOOTH |
146572e71de1SLee, Chun-Yi ACER_CAP_THREEG))
146672e71de1SLee, Chun-Yi status = wmid_v2_get_u32(value, cap);
146772e71de1SLee, Chun-Yi else if (wmi_has_guid(WMID_GUID2))
146838db157dSLee, Chun-Yi status = WMID_get_u32(value, cap);
146972e71de1SLee, Chun-Yi break;
147041b16dceSLen Brown }
147141b16dceSLen Brown
147241b16dceSLen Brown return status;
147341b16dceSLen Brown }
147441b16dceSLen Brown
set_u32(u32 value,u32 cap)147541b16dceSLen Brown static acpi_status set_u32(u32 value, u32 cap)
147641b16dceSLen Brown {
147741b16dceSLen Brown acpi_status status;
147841b16dceSLen Brown
147941b16dceSLen Brown if (interface->capability & cap) {
148041b16dceSLen Brown switch (interface->type) {
148141b16dceSLen Brown case ACER_AMW0:
148238db157dSLee, Chun-Yi return AMW0_set_u32(value, cap);
148341b16dceSLen Brown case ACER_AMW0_V2:
148441b16dceSLen Brown if (cap == ACER_CAP_MAILLED)
148538db157dSLee, Chun-Yi return AMW0_set_u32(value, cap);
148641b16dceSLen Brown
148741b16dceSLen Brown /*
148841b16dceSLen Brown * On some models, some WMID methods don't toggle
148941b16dceSLen Brown * properly. For those cases, we want to run the AMW0
149041b16dceSLen Brown * method afterwards to be certain we've really toggled
149141b16dceSLen Brown * the device state.
149241b16dceSLen Brown */
149341b16dceSLen Brown if (cap == ACER_CAP_WIRELESS ||
149441b16dceSLen Brown cap == ACER_CAP_BLUETOOTH) {
149538db157dSLee, Chun-Yi status = WMID_set_u32(value, cap);
149641b16dceSLen Brown if (ACPI_FAILURE(status))
149741b16dceSLen Brown return status;
149841b16dceSLen Brown
149938db157dSLee, Chun-Yi return AMW0_set_u32(value, cap);
150041b16dceSLen Brown }
1501df561f66SGustavo A. R. Silva fallthrough;
150241b16dceSLen Brown case ACER_WMID:
150338db157dSLee, Chun-Yi return WMID_set_u32(value, cap);
150472e71de1SLee, Chun-Yi case ACER_WMID_v2:
150572e71de1SLee, Chun-Yi if (cap & (ACER_CAP_WIRELESS |
150672e71de1SLee, Chun-Yi ACER_CAP_BLUETOOTH |
150772e71de1SLee, Chun-Yi ACER_CAP_THREEG))
150872e71de1SLee, Chun-Yi return wmid_v2_set_u32(value, cap);
150972e71de1SLee, Chun-Yi else if (wmi_has_guid(WMID_GUID2))
151038db157dSLee, Chun-Yi return WMID_set_u32(value, cap);
1511df561f66SGustavo A. R. Silva fallthrough;
151241b16dceSLen Brown default:
151341b16dceSLen Brown return AE_BAD_PARAMETER;
151441b16dceSLen Brown }
151541b16dceSLen Brown }
151641b16dceSLen Brown return AE_BAD_PARAMETER;
151741b16dceSLen Brown }
151841b16dceSLen Brown
acer_commandline_init(void)151941b16dceSLen Brown static void __init acer_commandline_init(void)
152041b16dceSLen Brown {
152141b16dceSLen Brown /*
152241b16dceSLen Brown * These will all fail silently if the value given is invalid, or the
152341b16dceSLen Brown * capability isn't available on the given interface
152441b16dceSLen Brown */
1525c2647b5eSLee, Chun-Yi if (mailled >= 0)
152641b16dceSLen Brown set_u32(mailled, ACER_CAP_MAILLED);
1527c2647b5eSLee, Chun-Yi if (!has_type_aa && threeg >= 0)
152841b16dceSLen Brown set_u32(threeg, ACER_CAP_THREEG);
1529c2647b5eSLee, Chun-Yi if (brightness >= 0)
153041b16dceSLen Brown set_u32(brightness, ACER_CAP_BRIGHTNESS);
153141b16dceSLen Brown }
153241b16dceSLen Brown
153341b16dceSLen Brown /*
153441b16dceSLen Brown * LED device (Mail LED only, no other LEDs known yet)
153541b16dceSLen Brown */
mail_led_set(struct led_classdev * led_cdev,enum led_brightness value)153641b16dceSLen Brown static void mail_led_set(struct led_classdev *led_cdev,
153741b16dceSLen Brown enum led_brightness value)
153841b16dceSLen Brown {
153941b16dceSLen Brown set_u32(value, ACER_CAP_MAILLED);
154041b16dceSLen Brown }
154141b16dceSLen Brown
154241b16dceSLen Brown static struct led_classdev mail_led = {
154341b16dceSLen Brown .name = "acer-wmi::mail",
154441b16dceSLen Brown .brightness_set = mail_led_set,
154541b16dceSLen Brown };
154641b16dceSLen Brown
acer_led_init(struct device * dev)1547b859f159SGreg Kroah-Hartman static int acer_led_init(struct device *dev)
154841b16dceSLen Brown {
154941b16dceSLen Brown return led_classdev_register(dev, &mail_led);
155041b16dceSLen Brown }
155141b16dceSLen Brown
acer_led_exit(void)155241b16dceSLen Brown static void acer_led_exit(void)
155341b16dceSLen Brown {
15549a0b74fdSPali Rohár set_u32(LED_OFF, ACER_CAP_MAILLED);
155541b16dceSLen Brown led_classdev_unregister(&mail_led);
155641b16dceSLen Brown }
155741b16dceSLen Brown
155841b16dceSLen Brown /*
155941b16dceSLen Brown * Backlight device
156041b16dceSLen Brown */
156141b16dceSLen Brown static struct backlight_device *acer_backlight_device;
156241b16dceSLen Brown
read_brightness(struct backlight_device * bd)156341b16dceSLen Brown static int read_brightness(struct backlight_device *bd)
156441b16dceSLen Brown {
156541b16dceSLen Brown u32 value;
156641b16dceSLen Brown get_u32(&value, ACER_CAP_BRIGHTNESS);
156741b16dceSLen Brown return value;
156841b16dceSLen Brown }
156941b16dceSLen Brown
update_bl_status(struct backlight_device * bd)157041b16dceSLen Brown static int update_bl_status(struct backlight_device *bd)
157141b16dceSLen Brown {
1572441ffc52SStephen Kitt int intensity = backlight_get_brightness(bd);
157341b16dceSLen Brown
157441b16dceSLen Brown set_u32(intensity, ACER_CAP_BRIGHTNESS);
157541b16dceSLen Brown
157641b16dceSLen Brown return 0;
157741b16dceSLen Brown }
157841b16dceSLen Brown
1579acc2472eSLionel Debroux static const struct backlight_ops acer_bl_ops = {
158041b16dceSLen Brown .get_brightness = read_brightness,
158141b16dceSLen Brown .update_status = update_bl_status,
158241b16dceSLen Brown };
158341b16dceSLen Brown
acer_backlight_init(struct device * dev)1584b859f159SGreg Kroah-Hartman static int acer_backlight_init(struct device *dev)
158541b16dceSLen Brown {
1586a19a6ee6SMatthew Garrett struct backlight_properties props;
158741b16dceSLen Brown struct backlight_device *bd;
158841b16dceSLen Brown
1589a19a6ee6SMatthew Garrett memset(&props, 0, sizeof(struct backlight_properties));
1590bb7ca747SMatthew Garrett props.type = BACKLIGHT_PLATFORM;
1591a19a6ee6SMatthew Garrett props.max_brightness = max_brightness;
1592a19a6ee6SMatthew Garrett bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops,
1593a19a6ee6SMatthew Garrett &props);
159441b16dceSLen Brown if (IS_ERR(bd)) {
1595cae15702SLee, Chun-Yi pr_err("Could not register Acer backlight device\n");
159641b16dceSLen Brown acer_backlight_device = NULL;
159741b16dceSLen Brown return PTR_ERR(bd);
159841b16dceSLen Brown }
159941b16dceSLen Brown
160041b16dceSLen Brown acer_backlight_device = bd;
160141b16dceSLen Brown
160241b16dceSLen Brown bd->props.power = FB_BLANK_UNBLANK;
16036f6ef82cSCarlos Corbacho bd->props.brightness = read_brightness(bd);
160441b16dceSLen Brown backlight_update_status(bd);
160541b16dceSLen Brown return 0;
160641b16dceSLen Brown }
160741b16dceSLen Brown
acer_backlight_exit(void)160841b16dceSLen Brown static void acer_backlight_exit(void)
160941b16dceSLen Brown {
161041b16dceSLen Brown backlight_device_unregister(acer_backlight_device);
161141b16dceSLen Brown }
161241b16dceSLen Brown
161341b16dceSLen Brown /*
16141eb3fe1dSMarek Vasut * Accelerometer device
16151eb3fe1dSMarek Vasut */
16161eb3fe1dSMarek Vasut static acpi_handle gsensor_handle;
16171eb3fe1dSMarek Vasut
acer_gsensor_init(void)16181eb3fe1dSMarek Vasut static int acer_gsensor_init(void)
16191eb3fe1dSMarek Vasut {
16201eb3fe1dSMarek Vasut acpi_status status;
16211eb3fe1dSMarek Vasut struct acpi_buffer output;
16221eb3fe1dSMarek Vasut union acpi_object out_obj;
16231eb3fe1dSMarek Vasut
16241eb3fe1dSMarek Vasut output.length = sizeof(out_obj);
16251eb3fe1dSMarek Vasut output.pointer = &out_obj;
16261eb3fe1dSMarek Vasut status = acpi_evaluate_object(gsensor_handle, "_INI", NULL, &output);
16271eb3fe1dSMarek Vasut if (ACPI_FAILURE(status))
16281eb3fe1dSMarek Vasut return -1;
16291eb3fe1dSMarek Vasut
16301eb3fe1dSMarek Vasut return 0;
16311eb3fe1dSMarek Vasut }
16321eb3fe1dSMarek Vasut
acer_gsensor_open(struct input_dev * input)16331eb3fe1dSMarek Vasut static int acer_gsensor_open(struct input_dev *input)
16341eb3fe1dSMarek Vasut {
16351eb3fe1dSMarek Vasut return acer_gsensor_init();
16361eb3fe1dSMarek Vasut }
16371eb3fe1dSMarek Vasut
acer_gsensor_event(void)16381eb3fe1dSMarek Vasut static int acer_gsensor_event(void)
16391eb3fe1dSMarek Vasut {
16401eb3fe1dSMarek Vasut acpi_status status;
16411eb3fe1dSMarek Vasut struct acpi_buffer output;
16421eb3fe1dSMarek Vasut union acpi_object out_obj[5];
16431eb3fe1dSMarek Vasut
16449feb0763SHans de Goede if (!acer_wmi_accel_dev)
16451eb3fe1dSMarek Vasut return -1;
16461eb3fe1dSMarek Vasut
16471eb3fe1dSMarek Vasut output.length = sizeof(out_obj);
16481eb3fe1dSMarek Vasut output.pointer = out_obj;
16491eb3fe1dSMarek Vasut
16501eb3fe1dSMarek Vasut status = acpi_evaluate_object(gsensor_handle, "RDVL", NULL, &output);
16511eb3fe1dSMarek Vasut if (ACPI_FAILURE(status))
16521eb3fe1dSMarek Vasut return -1;
16531eb3fe1dSMarek Vasut
16541eb3fe1dSMarek Vasut if (out_obj->package.count != 4)
16551eb3fe1dSMarek Vasut return -1;
16561eb3fe1dSMarek Vasut
16571eb3fe1dSMarek Vasut input_report_abs(acer_wmi_accel_dev, ABS_X,
16581eb3fe1dSMarek Vasut (s16)out_obj->package.elements[0].integer.value);
16591eb3fe1dSMarek Vasut input_report_abs(acer_wmi_accel_dev, ABS_Y,
16601eb3fe1dSMarek Vasut (s16)out_obj->package.elements[1].integer.value);
16611eb3fe1dSMarek Vasut input_report_abs(acer_wmi_accel_dev, ABS_Z,
16621eb3fe1dSMarek Vasut (s16)out_obj->package.elements[2].integer.value);
16631eb3fe1dSMarek Vasut input_sync(acer_wmi_accel_dev);
16641eb3fe1dSMarek Vasut return 0;
16651eb3fe1dSMarek Vasut }
16661eb3fe1dSMarek Vasut
16671eb3fe1dSMarek Vasut /*
1668ca42c119SJafarAkhondali * Predator series turbo button
1669ca42c119SJafarAkhondali */
acer_toggle_turbo(void)1670ca42c119SJafarAkhondali static int acer_toggle_turbo(void)
1671ca42c119SJafarAkhondali {
1672ca42c119SJafarAkhondali u64 turbo_led_state;
1673ca42c119SJafarAkhondali
1674ca42c119SJafarAkhondali /* Get current state from turbo button */
1675ca42c119SJafarAkhondali if (ACPI_FAILURE(WMID_gaming_get_u64(&turbo_led_state, ACER_CAP_TURBO_LED)))
1676ca42c119SJafarAkhondali return -1;
1677ca42c119SJafarAkhondali
1678ca42c119SJafarAkhondali if (turbo_led_state) {
1679ca42c119SJafarAkhondali /* Turn off turbo led */
1680ca42c119SJafarAkhondali WMID_gaming_set_u64(0x1, ACER_CAP_TURBO_LED);
1681ca42c119SJafarAkhondali
1682ca42c119SJafarAkhondali /* Set FAN mode to auto */
1683ca42c119SJafarAkhondali WMID_gaming_set_fan_mode(0x1);
1684ca42c119SJafarAkhondali
1685ca42c119SJafarAkhondali /* Set OC to normal */
1686ca42c119SJafarAkhondali WMID_gaming_set_u64(0x5, ACER_CAP_TURBO_OC);
1687ca42c119SJafarAkhondali WMID_gaming_set_u64(0x7, ACER_CAP_TURBO_OC);
1688ca42c119SJafarAkhondali } else {
1689ca42c119SJafarAkhondali /* Turn on turbo led */
1690ca42c119SJafarAkhondali WMID_gaming_set_u64(0x10001, ACER_CAP_TURBO_LED);
1691ca42c119SJafarAkhondali
1692ca42c119SJafarAkhondali /* Set FAN mode to turbo */
1693ca42c119SJafarAkhondali WMID_gaming_set_fan_mode(0x2);
1694ca42c119SJafarAkhondali
1695ca42c119SJafarAkhondali /* Set OC to turbo mode */
1696ca42c119SJafarAkhondali WMID_gaming_set_u64(0x205, ACER_CAP_TURBO_OC);
1697ca42c119SJafarAkhondali WMID_gaming_set_u64(0x207, ACER_CAP_TURBO_OC);
1698ca42c119SJafarAkhondali }
1699ca42c119SJafarAkhondali return turbo_led_state;
1700ca42c119SJafarAkhondali }
1701ca42c119SJafarAkhondali
1702ca42c119SJafarAkhondali /*
17035c54cb6cSHans de Goede * Switch series keyboard dock status
17045c54cb6cSHans de Goede */
acer_kbd_dock_state_to_sw_tablet_mode(u8 kbd_dock_state)17055c54cb6cSHans de Goede static int acer_kbd_dock_state_to_sw_tablet_mode(u8 kbd_dock_state)
17065c54cb6cSHans de Goede {
17075c54cb6cSHans de Goede switch (kbd_dock_state) {
17085c54cb6cSHans de Goede case 0x01: /* Docked, traditional clamshell laptop mode */
17095c54cb6cSHans de Goede return 0;
17105c54cb6cSHans de Goede case 0x04: /* Stand-alone tablet */
17115c54cb6cSHans de Goede case 0x40: /* Docked, tent mode, keyboard not usable */
17125c54cb6cSHans de Goede return 1;
17135c54cb6cSHans de Goede default:
17145c54cb6cSHans de Goede pr_warn("Unknown kbd_dock_state 0x%02x\n", kbd_dock_state);
17155c54cb6cSHans de Goede }
17165c54cb6cSHans de Goede
17175c54cb6cSHans de Goede return 0;
17185c54cb6cSHans de Goede }
17195c54cb6cSHans de Goede
acer_kbd_dock_get_initial_state(void)17205c54cb6cSHans de Goede static void acer_kbd_dock_get_initial_state(void)
17215c54cb6cSHans de Goede {
17225c54cb6cSHans de Goede u8 *output, input[8] = { 0x05, 0x00, };
17235c54cb6cSHans de Goede struct acpi_buffer input_buf = { sizeof(input), input };
17245c54cb6cSHans de Goede struct acpi_buffer output_buf = { ACPI_ALLOCATE_BUFFER, NULL };
17255c54cb6cSHans de Goede union acpi_object *obj;
17265c54cb6cSHans de Goede acpi_status status;
17275c54cb6cSHans de Goede int sw_tablet_mode;
17285c54cb6cSHans de Goede
17295c54cb6cSHans de Goede status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input_buf, &output_buf);
17305c54cb6cSHans de Goede if (ACPI_FAILURE(status)) {
1731ef14f0e8SHans de Goede pr_err("Error getting keyboard-dock initial status: %s\n",
1732ef14f0e8SHans de Goede acpi_format_exception(status));
17335c54cb6cSHans de Goede return;
17345c54cb6cSHans de Goede }
17355c54cb6cSHans de Goede
17365c54cb6cSHans de Goede obj = output_buf.pointer;
17375c54cb6cSHans de Goede if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length != 8) {
17385c54cb6cSHans de Goede pr_err("Unexpected output format getting keyboard-dock initial status\n");
17395c54cb6cSHans de Goede goto out_free_obj;
17405c54cb6cSHans de Goede }
17415c54cb6cSHans de Goede
17425c54cb6cSHans de Goede output = obj->buffer.pointer;
17435c54cb6cSHans de Goede if (output[0] != 0x00 || (output[3] != 0x05 && output[3] != 0x45)) {
17445c54cb6cSHans de Goede pr_err("Unexpected output [0]=0x%02x [3]=0x%02x getting keyboard-dock initial status\n",
17455c54cb6cSHans de Goede output[0], output[3]);
17465c54cb6cSHans de Goede goto out_free_obj;
17475c54cb6cSHans de Goede }
17485c54cb6cSHans de Goede
17495c54cb6cSHans de Goede sw_tablet_mode = acer_kbd_dock_state_to_sw_tablet_mode(output[4]);
17505c54cb6cSHans de Goede input_report_switch(acer_wmi_input_dev, SW_TABLET_MODE, sw_tablet_mode);
17515c54cb6cSHans de Goede
17525c54cb6cSHans de Goede out_free_obj:
17535c54cb6cSHans de Goede kfree(obj);
17545c54cb6cSHans de Goede }
17555c54cb6cSHans de Goede
acer_kbd_dock_event(const struct event_return_value * event)17565c54cb6cSHans de Goede static void acer_kbd_dock_event(const struct event_return_value *event)
17575c54cb6cSHans de Goede {
17585c54cb6cSHans de Goede int sw_tablet_mode;
17595c54cb6cSHans de Goede
17605c54cb6cSHans de Goede if (!has_cap(ACER_CAP_KBD_DOCK))
17615c54cb6cSHans de Goede return;
17625c54cb6cSHans de Goede
17635c54cb6cSHans de Goede sw_tablet_mode = acer_kbd_dock_state_to_sw_tablet_mode(event->kbd_dock_state);
17645c54cb6cSHans de Goede input_report_switch(acer_wmi_input_dev, SW_TABLET_MODE, sw_tablet_mode);
17655c54cb6cSHans de Goede input_sync(acer_wmi_input_dev);
17665c54cb6cSHans de Goede }
17675c54cb6cSHans de Goede
17685c54cb6cSHans de Goede /*
176941b16dceSLen Brown * Rfkill devices
177041b16dceSLen Brown */
177141b16dceSLen Brown static void acer_rfkill_update(struct work_struct *ignored);
177241b16dceSLen Brown static DECLARE_DELAYED_WORK(acer_rfkill_work, acer_rfkill_update);
acer_rfkill_update(struct work_struct * ignored)177341b16dceSLen Brown static void acer_rfkill_update(struct work_struct *ignored)
177441b16dceSLen Brown {
177541b16dceSLen Brown u32 state;
177641b16dceSLen Brown acpi_status status;
177741b16dceSLen Brown
17781709adabSLee, Chun-Yi if (has_cap(ACER_CAP_WIRELESS)) {
177941b16dceSLen Brown status = get_u32(&state, ACER_CAP_WIRELESS);
178015b956a0SLee, Chun-Yi if (ACPI_SUCCESS(status)) {
17811709adabSLee, Chun-Yi if (quirks->wireless == 3)
178215b956a0SLee, Chun-Yi rfkill_set_hw_state(wireless_rfkill, !state);
17831709adabSLee, Chun-Yi else
1784a878417cSTroy Moure rfkill_set_sw_state(wireless_rfkill, !state);
178515b956a0SLee, Chun-Yi }
178615b956a0SLee, Chun-Yi }
178741b16dceSLen Brown
178841b16dceSLen Brown if (has_cap(ACER_CAP_BLUETOOTH)) {
178941b16dceSLen Brown status = get_u32(&state, ACER_CAP_BLUETOOTH);
179041b16dceSLen Brown if (ACPI_SUCCESS(status))
1791a878417cSTroy Moure rfkill_set_sw_state(bluetooth_rfkill, !state);
179241b16dceSLen Brown }
179341b16dceSLen Brown
1794b3c9092bSLee, Chun-Yi if (has_cap(ACER_CAP_THREEG) && wmi_has_guid(WMID_GUID3)) {
179572e71de1SLee, Chun-Yi status = get_u32(&state, ACER_WMID3_GDS_THREEG);
1796b3c9092bSLee, Chun-Yi if (ACPI_SUCCESS(status))
1797b3c9092bSLee, Chun-Yi rfkill_set_sw_state(threeg_rfkill, !state);
1798b3c9092bSLee, Chun-Yi }
1799b3c9092bSLee, Chun-Yi
180041b16dceSLen Brown schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
180141b16dceSLen Brown }
180241b16dceSLen Brown
acer_rfkill_set(void * data,bool blocked)180319d337dfSJohannes Berg static int acer_rfkill_set(void *data, bool blocked)
180441b16dceSLen Brown {
180541b16dceSLen Brown acpi_status status;
180619d337dfSJohannes Berg u32 cap = (unsigned long)data;
18078215af01SLee, Chun-Yi
18088215af01SLee, Chun-Yi if (rfkill_inited) {
180972e71de1SLee, Chun-Yi status = set_u32(!blocked, cap);
181041b16dceSLen Brown if (ACPI_FAILURE(status))
181141b16dceSLen Brown return -ENODEV;
18128215af01SLee, Chun-Yi }
18138215af01SLee, Chun-Yi
181441b16dceSLen Brown return 0;
181541b16dceSLen Brown }
181641b16dceSLen Brown
181719d337dfSJohannes Berg static const struct rfkill_ops acer_rfkill_ops = {
181819d337dfSJohannes Berg .set_block = acer_rfkill_set,
181919d337dfSJohannes Berg };
182019d337dfSJohannes Berg
acer_rfkill_register(struct device * dev,enum rfkill_type type,char * name,u32 cap)182141b16dceSLen Brown static struct rfkill *acer_rfkill_register(struct device *dev,
182219d337dfSJohannes Berg enum rfkill_type type,
182319d337dfSJohannes Berg char *name, u32 cap)
182441b16dceSLen Brown {
182541b16dceSLen Brown int err;
182641b16dceSLen Brown struct rfkill *rfkill_dev;
1827466449cfSLee, Chun-Yi u32 state;
1828466449cfSLee, Chun-Yi acpi_status status;
182941b16dceSLen Brown
183019d337dfSJohannes Berg rfkill_dev = rfkill_alloc(name, dev, type,
183119d337dfSJohannes Berg &acer_rfkill_ops,
183219d337dfSJohannes Berg (void *)(unsigned long)cap);
183341b16dceSLen Brown if (!rfkill_dev)
183441b16dceSLen Brown return ERR_PTR(-ENOMEM);
183541b16dceSLen Brown
183672e71de1SLee, Chun-Yi status = get_u32(&state, cap);
1837466449cfSLee, Chun-Yi
183841b16dceSLen Brown err = rfkill_register(rfkill_dev);
183941b16dceSLen Brown if (err) {
184019d337dfSJohannes Berg rfkill_destroy(rfkill_dev);
184141b16dceSLen Brown return ERR_PTR(err);
184241b16dceSLen Brown }
18438215af01SLee, Chun-Yi
18448215af01SLee, Chun-Yi if (ACPI_SUCCESS(status))
18458215af01SLee, Chun-Yi rfkill_set_sw_state(rfkill_dev, !state);
18468215af01SLee, Chun-Yi
184741b16dceSLen Brown return rfkill_dev;
184841b16dceSLen Brown }
184941b16dceSLen Brown
acer_rfkill_init(struct device * dev)185041b16dceSLen Brown static int acer_rfkill_init(struct device *dev)
185141b16dceSLen Brown {
18521709adabSLee, Chun-Yi int err;
18531709adabSLee, Chun-Yi
18541709adabSLee, Chun-Yi if (has_cap(ACER_CAP_WIRELESS)) {
185541b16dceSLen Brown wireless_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_WLAN,
185641b16dceSLen Brown "acer-wireless", ACER_CAP_WIRELESS);
18571709adabSLee, Chun-Yi if (IS_ERR(wireless_rfkill)) {
18581709adabSLee, Chun-Yi err = PTR_ERR(wireless_rfkill);
18591709adabSLee, Chun-Yi goto error_wireless;
18601709adabSLee, Chun-Yi }
18611709adabSLee, Chun-Yi }
186241b16dceSLen Brown
186341b16dceSLen Brown if (has_cap(ACER_CAP_BLUETOOTH)) {
186441b16dceSLen Brown bluetooth_rfkill = acer_rfkill_register(dev,
186541b16dceSLen Brown RFKILL_TYPE_BLUETOOTH, "acer-bluetooth",
186641b16dceSLen Brown ACER_CAP_BLUETOOTH);
186741b16dceSLen Brown if (IS_ERR(bluetooth_rfkill)) {
18681709adabSLee, Chun-Yi err = PTR_ERR(bluetooth_rfkill);
18691709adabSLee, Chun-Yi goto error_bluetooth;
187041b16dceSLen Brown }
187141b16dceSLen Brown }
187241b16dceSLen Brown
1873b3c9092bSLee, Chun-Yi if (has_cap(ACER_CAP_THREEG)) {
1874b3c9092bSLee, Chun-Yi threeg_rfkill = acer_rfkill_register(dev,
1875b3c9092bSLee, Chun-Yi RFKILL_TYPE_WWAN, "acer-threeg",
1876b3c9092bSLee, Chun-Yi ACER_CAP_THREEG);
1877b3c9092bSLee, Chun-Yi if (IS_ERR(threeg_rfkill)) {
18781709adabSLee, Chun-Yi err = PTR_ERR(threeg_rfkill);
18791709adabSLee, Chun-Yi goto error_threeg;
1880b3c9092bSLee, Chun-Yi }
1881b3c9092bSLee, Chun-Yi }
1882b3c9092bSLee, Chun-Yi
18838215af01SLee, Chun-Yi rfkill_inited = true;
18848215af01SLee, Chun-Yi
18851709adabSLee, Chun-Yi if ((ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID)) &&
18861709adabSLee, Chun-Yi has_cap(ACER_CAP_WIRELESS | ACER_CAP_BLUETOOTH | ACER_CAP_THREEG))
188770a9b904SLee, Chun-Yi schedule_delayed_work(&acer_rfkill_work,
188870a9b904SLee, Chun-Yi round_jiffies_relative(HZ));
188941b16dceSLen Brown
189041b16dceSLen Brown return 0;
18911709adabSLee, Chun-Yi
18921709adabSLee, Chun-Yi error_threeg:
18931709adabSLee, Chun-Yi if (has_cap(ACER_CAP_BLUETOOTH)) {
18941709adabSLee, Chun-Yi rfkill_unregister(bluetooth_rfkill);
18951709adabSLee, Chun-Yi rfkill_destroy(bluetooth_rfkill);
18961709adabSLee, Chun-Yi }
18971709adabSLee, Chun-Yi error_bluetooth:
18981709adabSLee, Chun-Yi if (has_cap(ACER_CAP_WIRELESS)) {
18991709adabSLee, Chun-Yi rfkill_unregister(wireless_rfkill);
19001709adabSLee, Chun-Yi rfkill_destroy(wireless_rfkill);
19011709adabSLee, Chun-Yi }
19021709adabSLee, Chun-Yi error_wireless:
19031709adabSLee, Chun-Yi return err;
190441b16dceSLen Brown }
190541b16dceSLen Brown
acer_rfkill_exit(void)190641b16dceSLen Brown static void acer_rfkill_exit(void)
190741b16dceSLen Brown {
19081709adabSLee, Chun-Yi if ((ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID)) &&
19091709adabSLee, Chun-Yi has_cap(ACER_CAP_WIRELESS | ACER_CAP_BLUETOOTH | ACER_CAP_THREEG))
191041b16dceSLen Brown cancel_delayed_work_sync(&acer_rfkill_work);
191119d337dfSJohannes Berg
19121709adabSLee, Chun-Yi if (has_cap(ACER_CAP_WIRELESS)) {
191341b16dceSLen Brown rfkill_unregister(wireless_rfkill);
191419d337dfSJohannes Berg rfkill_destroy(wireless_rfkill);
19151709adabSLee, Chun-Yi }
191619d337dfSJohannes Berg
191741b16dceSLen Brown if (has_cap(ACER_CAP_BLUETOOTH)) {
191841b16dceSLen Brown rfkill_unregister(bluetooth_rfkill);
191919d337dfSJohannes Berg rfkill_destroy(bluetooth_rfkill);
192041b16dceSLen Brown }
1921b3c9092bSLee, Chun-Yi
1922b3c9092bSLee, Chun-Yi if (has_cap(ACER_CAP_THREEG)) {
1923b3c9092bSLee, Chun-Yi rfkill_unregister(threeg_rfkill);
1924b3c9092bSLee, Chun-Yi rfkill_destroy(threeg_rfkill);
1925b3c9092bSLee, Chun-Yi }
192641b16dceSLen Brown return;
192741b16dceSLen Brown }
192841b16dceSLen Brown
acer_wmi_notify(u32 value,void * context)19293fdca87dSLee, Chun-Yi static void acer_wmi_notify(u32 value, void *context)
19303fdca87dSLee, Chun-Yi {
19313fdca87dSLee, Chun-Yi struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
19323fdca87dSLee, Chun-Yi union acpi_object *obj;
19333fdca87dSLee, Chun-Yi struct event_return_value return_value;
19343fdca87dSLee, Chun-Yi acpi_status status;
193592530664SSeth Forshee u16 device_state;
193692530664SSeth Forshee const struct key_entry *key;
19378e2286ceSLee, Chun-Yi u32 scancode;
19383fdca87dSLee, Chun-Yi
19393fdca87dSLee, Chun-Yi status = wmi_get_event_data(value, &response);
19403fdca87dSLee, Chun-Yi if (status != AE_OK) {
1941249c720dSJoe Perches pr_warn("bad event status 0x%x\n", status);
19423fdca87dSLee, Chun-Yi return;
19433fdca87dSLee, Chun-Yi }
19443fdca87dSLee, Chun-Yi
19453fdca87dSLee, Chun-Yi obj = (union acpi_object *)response.pointer;
19463fdca87dSLee, Chun-Yi
19473fdca87dSLee, Chun-Yi if (!obj)
19483fdca87dSLee, Chun-Yi return;
19493fdca87dSLee, Chun-Yi if (obj->type != ACPI_TYPE_BUFFER) {
1950249c720dSJoe Perches pr_warn("Unknown response received %d\n", obj->type);
19513fdca87dSLee, Chun-Yi kfree(obj);
19523fdca87dSLee, Chun-Yi return;
19533fdca87dSLee, Chun-Yi }
19543fdca87dSLee, Chun-Yi if (obj->buffer.length != 8) {
1955249c720dSJoe Perches pr_warn("Unknown buffer length %d\n", obj->buffer.length);
19563fdca87dSLee, Chun-Yi kfree(obj);
19573fdca87dSLee, Chun-Yi return;
19583fdca87dSLee, Chun-Yi }
19593fdca87dSLee, Chun-Yi
19603fdca87dSLee, Chun-Yi return_value = *((struct event_return_value *)obj->buffer.pointer);
19613fdca87dSLee, Chun-Yi kfree(obj);
19623fdca87dSLee, Chun-Yi
19633fdca87dSLee, Chun-Yi switch (return_value.function) {
19643fdca87dSLee, Chun-Yi case WMID_HOTKEY_EVENT:
196592530664SSeth Forshee device_state = return_value.device_state;
19668ae68de1SMelchior FRANZ pr_debug("device state: 0x%x\n", device_state);
196792530664SSeth Forshee
196892530664SSeth Forshee key = sparse_keymap_entry_from_scancode(acer_wmi_input_dev,
196992530664SSeth Forshee return_value.key_num);
197092530664SSeth Forshee if (!key) {
197192530664SSeth Forshee pr_warn("Unknown key number - 0x%x\n",
197292530664SSeth Forshee return_value.key_num);
197392530664SSeth Forshee } else {
19748e2286ceSLee, Chun-Yi scancode = return_value.key_num;
197592530664SSeth Forshee switch (key->keycode) {
197692530664SSeth Forshee case KEY_WLAN:
197792530664SSeth Forshee case KEY_BLUETOOTH:
197870a9b904SLee, Chun-Yi if (has_cap(ACER_CAP_WIRELESS))
197970a9b904SLee, Chun-Yi rfkill_set_sw_state(wireless_rfkill,
198070a9b904SLee, Chun-Yi !(device_state & ACER_WMID3_GDS_WIRELESS));
198170a9b904SLee, Chun-Yi if (has_cap(ACER_CAP_THREEG))
198270a9b904SLee, Chun-Yi rfkill_set_sw_state(threeg_rfkill,
198370a9b904SLee, Chun-Yi !(device_state & ACER_WMID3_GDS_THREEG));
198492530664SSeth Forshee if (has_cap(ACER_CAP_BLUETOOTH))
198592530664SSeth Forshee rfkill_set_sw_state(bluetooth_rfkill,
198692530664SSeth Forshee !(device_state & ACER_WMID3_GDS_BLUETOOTH));
198792530664SSeth Forshee break;
19888e2286ceSLee, Chun-Yi case KEY_TOUCHPAD_TOGGLE:
19898e2286ceSLee, Chun-Yi scancode = (device_state & ACER_WMID3_GDS_TOUCHPAD) ?
19908e2286ceSLee, Chun-Yi KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF;
199170a9b904SLee, Chun-Yi }
19928e2286ceSLee, Chun-Yi sparse_keymap_report_event(acer_wmi_input_dev, scancode, 1, true);
199392530664SSeth Forshee }
19943fdca87dSLee, Chun-Yi break;
19955c54cb6cSHans de Goede case WMID_ACCEL_OR_KBD_DOCK_EVENT:
19961eb3fe1dSMarek Vasut acer_gsensor_event();
19975c54cb6cSHans de Goede acer_kbd_dock_event(&return_value);
19981eb3fe1dSMarek Vasut break;
1999ca42c119SJafarAkhondali case WMID_GAMING_TURBO_KEY_EVENT:
2000ca42c119SJafarAkhondali if (return_value.key_num == 0x4)
2001ca42c119SJafarAkhondali acer_toggle_turbo();
2002ca42c119SJafarAkhondali break;
2003*b8261bfaSArmin Wolf case WMID_AC_EVENT:
2004*b8261bfaSArmin Wolf /* We ignore AC events here */
2005*b8261bfaSArmin Wolf break;
20063fdca87dSLee, Chun-Yi default:
2007249c720dSJoe Perches pr_warn("Unknown function number - %d - %d\n",
20083fdca87dSLee, Chun-Yi return_value.function, return_value.key_num);
20093fdca87dSLee, Chun-Yi break;
20103fdca87dSLee, Chun-Yi }
20113fdca87dSLee, Chun-Yi }
20123fdca87dSLee, Chun-Yi
201376d51dd9SMathias Krause static acpi_status __init
wmid3_set_function_mode(struct func_input_params * params,struct func_return_value * return_value)2014280642c3SChris Chiu wmid3_set_function_mode(struct func_input_params *params,
2015280642c3SChris Chiu struct func_return_value *return_value)
201659ccf2f3SFrom: Lee, Chun-Yi {
201759ccf2f3SFrom: Lee, Chun-Yi acpi_status status;
201859ccf2f3SFrom: Lee, Chun-Yi union acpi_object *obj;
201959ccf2f3SFrom: Lee, Chun-Yi
2020280642c3SChris Chiu struct acpi_buffer input = { sizeof(struct func_input_params), params };
202159ccf2f3SFrom: Lee, Chun-Yi struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
202259ccf2f3SFrom: Lee, Chun-Yi
202359ccf2f3SFrom: Lee, Chun-Yi status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output);
202459ccf2f3SFrom: Lee, Chun-Yi if (ACPI_FAILURE(status))
202559ccf2f3SFrom: Lee, Chun-Yi return status;
202659ccf2f3SFrom: Lee, Chun-Yi
202759ccf2f3SFrom: Lee, Chun-Yi obj = output.pointer;
202859ccf2f3SFrom: Lee, Chun-Yi
202959ccf2f3SFrom: Lee, Chun-Yi if (!obj)
203059ccf2f3SFrom: Lee, Chun-Yi return AE_ERROR;
203159ccf2f3SFrom: Lee, Chun-Yi else if (obj->type != ACPI_TYPE_BUFFER) {
203259ccf2f3SFrom: Lee, Chun-Yi kfree(obj);
203359ccf2f3SFrom: Lee, Chun-Yi return AE_ERROR;
203459ccf2f3SFrom: Lee, Chun-Yi }
203559ccf2f3SFrom: Lee, Chun-Yi if (obj->buffer.length != 4) {
2036249c720dSJoe Perches pr_warn("Unknown buffer length %d\n", obj->buffer.length);
203759ccf2f3SFrom: Lee, Chun-Yi kfree(obj);
203859ccf2f3SFrom: Lee, Chun-Yi return AE_ERROR;
203959ccf2f3SFrom: Lee, Chun-Yi }
204059ccf2f3SFrom: Lee, Chun-Yi
2041280642c3SChris Chiu *return_value = *((struct func_return_value *)obj->buffer.pointer);
204259ccf2f3SFrom: Lee, Chun-Yi kfree(obj);
204359ccf2f3SFrom: Lee, Chun-Yi
204459ccf2f3SFrom: Lee, Chun-Yi return status;
204559ccf2f3SFrom: Lee, Chun-Yi }
204659ccf2f3SFrom: Lee, Chun-Yi
acer_wmi_enable_ec_raw(void)204776d51dd9SMathias Krause static int __init acer_wmi_enable_ec_raw(void)
204859ccf2f3SFrom: Lee, Chun-Yi {
2049280642c3SChris Chiu struct func_return_value return_value;
205059ccf2f3SFrom: Lee, Chun-Yi acpi_status status;
2051280642c3SChris Chiu struct func_input_params params = {
205259ccf2f3SFrom: Lee, Chun-Yi .function_num = 0x1,
205359ccf2f3SFrom: Lee, Chun-Yi .commun_devices = 0xFFFF,
205459ccf2f3SFrom: Lee, Chun-Yi .devices = 0xFFFF,
2055280642c3SChris Chiu .app_status = 0x00, /* Launch Manager Deactive */
2056280642c3SChris Chiu .app_mask = 0x01,
205759ccf2f3SFrom: Lee, Chun-Yi };
205859ccf2f3SFrom: Lee, Chun-Yi
2059280642c3SChris Chiu status = wmid3_set_function_mode(¶ms, &return_value);
206059ccf2f3SFrom: Lee, Chun-Yi
206159ccf2f3SFrom: Lee, Chun-Yi if (return_value.error_code || return_value.ec_return_value)
2062249c720dSJoe Perches pr_warn("Enabling EC raw mode failed: 0x%x - 0x%x\n",
2063249c720dSJoe Perches return_value.error_code,
206459ccf2f3SFrom: Lee, Chun-Yi return_value.ec_return_value);
206559ccf2f3SFrom: Lee, Chun-Yi else
2066249c720dSJoe Perches pr_info("Enabled EC raw mode\n");
206759ccf2f3SFrom: Lee, Chun-Yi
206859ccf2f3SFrom: Lee, Chun-Yi return status;
206959ccf2f3SFrom: Lee, Chun-Yi }
207059ccf2f3SFrom: Lee, Chun-Yi
acer_wmi_enable_lm(void)207176d51dd9SMathias Krause static int __init acer_wmi_enable_lm(void)
207259ccf2f3SFrom: Lee, Chun-Yi {
2073280642c3SChris Chiu struct func_return_value return_value;
207459ccf2f3SFrom: Lee, Chun-Yi acpi_status status;
2075280642c3SChris Chiu struct func_input_params params = {
207659ccf2f3SFrom: Lee, Chun-Yi .function_num = 0x1,
207759ccf2f3SFrom: Lee, Chun-Yi .commun_devices = 0xFFFF,
207859ccf2f3SFrom: Lee, Chun-Yi .devices = 0xFFFF,
2079280642c3SChris Chiu .app_status = 0x01, /* Launch Manager Active */
2080280642c3SChris Chiu .app_mask = 0x01,
208159ccf2f3SFrom: Lee, Chun-Yi };
208259ccf2f3SFrom: Lee, Chun-Yi
2083280642c3SChris Chiu status = wmid3_set_function_mode(¶ms, &return_value);
208459ccf2f3SFrom: Lee, Chun-Yi
208559ccf2f3SFrom: Lee, Chun-Yi if (return_value.error_code || return_value.ec_return_value)
2086249c720dSJoe Perches pr_warn("Enabling Launch Manager failed: 0x%x - 0x%x\n",
2087249c720dSJoe Perches return_value.error_code,
208859ccf2f3SFrom: Lee, Chun-Yi return_value.ec_return_value);
208959ccf2f3SFrom: Lee, Chun-Yi
209059ccf2f3SFrom: Lee, Chun-Yi return status;
209159ccf2f3SFrom: Lee, Chun-Yi }
209259ccf2f3SFrom: Lee, Chun-Yi
acer_wmi_enable_rf_button(void)2093280642c3SChris Chiu static int __init acer_wmi_enable_rf_button(void)
2094280642c3SChris Chiu {
2095280642c3SChris Chiu struct func_return_value return_value;
2096280642c3SChris Chiu acpi_status status;
2097280642c3SChris Chiu struct func_input_params params = {
2098280642c3SChris Chiu .function_num = 0x1,
2099280642c3SChris Chiu .commun_devices = 0xFFFF,
2100280642c3SChris Chiu .devices = 0xFFFF,
2101280642c3SChris Chiu .app_status = 0x10, /* RF Button Active */
2102280642c3SChris Chiu .app_mask = 0x10,
2103280642c3SChris Chiu };
2104280642c3SChris Chiu
2105280642c3SChris Chiu status = wmid3_set_function_mode(¶ms, &return_value);
2106280642c3SChris Chiu
2107280642c3SChris Chiu if (return_value.error_code || return_value.ec_return_value)
2108280642c3SChris Chiu pr_warn("Enabling RF Button failed: 0x%x - 0x%x\n",
2109280642c3SChris Chiu return_value.error_code,
2110280642c3SChris Chiu return_value.ec_return_value);
2111280642c3SChris Chiu
2112280642c3SChris Chiu return status;
2113280642c3SChris Chiu }
2114280642c3SChris Chiu
acer_wmi_accel_setup(void)21151eb3fe1dSMarek Vasut static int __init acer_wmi_accel_setup(void)
21161eb3fe1dSMarek Vasut {
21176fe9363bSAndy Shevchenko struct acpi_device *adev;
21181eb3fe1dSMarek Vasut int err;
21191eb3fe1dSMarek Vasut
21206fe9363bSAndy Shevchenko adev = acpi_dev_get_first_match_dev("BST0001", NULL, -1);
21216fe9363bSAndy Shevchenko if (!adev)
21226fe9363bSAndy Shevchenko return -ENODEV;
21236fe9363bSAndy Shevchenko
21246fe9363bSAndy Shevchenko gsensor_handle = acpi_device_handle(adev);
21256fe9363bSAndy Shevchenko acpi_dev_put(adev);
21261eb3fe1dSMarek Vasut
21271eb3fe1dSMarek Vasut acer_wmi_accel_dev = input_allocate_device();
21281eb3fe1dSMarek Vasut if (!acer_wmi_accel_dev)
21291eb3fe1dSMarek Vasut return -ENOMEM;
21301eb3fe1dSMarek Vasut
21311eb3fe1dSMarek Vasut acer_wmi_accel_dev->open = acer_gsensor_open;
21321eb3fe1dSMarek Vasut
21331eb3fe1dSMarek Vasut acer_wmi_accel_dev->name = "Acer BMA150 accelerometer";
21341eb3fe1dSMarek Vasut acer_wmi_accel_dev->phys = "wmi/input1";
21351eb3fe1dSMarek Vasut acer_wmi_accel_dev->id.bustype = BUS_HOST;
21361eb3fe1dSMarek Vasut acer_wmi_accel_dev->evbit[0] = BIT_MASK(EV_ABS);
21371eb3fe1dSMarek Vasut input_set_abs_params(acer_wmi_accel_dev, ABS_X, -16384, 16384, 0, 0);
21381eb3fe1dSMarek Vasut input_set_abs_params(acer_wmi_accel_dev, ABS_Y, -16384, 16384, 0, 0);
21391eb3fe1dSMarek Vasut input_set_abs_params(acer_wmi_accel_dev, ABS_Z, -16384, 16384, 0, 0);
21401eb3fe1dSMarek Vasut
21411eb3fe1dSMarek Vasut err = input_register_device(acer_wmi_accel_dev);
21421eb3fe1dSMarek Vasut if (err)
21431eb3fe1dSMarek Vasut goto err_free_dev;
21441eb3fe1dSMarek Vasut
21451eb3fe1dSMarek Vasut return 0;
21461eb3fe1dSMarek Vasut
21471eb3fe1dSMarek Vasut err_free_dev:
21481eb3fe1dSMarek Vasut input_free_device(acer_wmi_accel_dev);
21491eb3fe1dSMarek Vasut return err;
21501eb3fe1dSMarek Vasut }
21511eb3fe1dSMarek Vasut
acer_wmi_input_setup(void)21523fdca87dSLee, Chun-Yi static int __init acer_wmi_input_setup(void)
21533fdca87dSLee, Chun-Yi {
21543fdca87dSLee, Chun-Yi acpi_status status;
21553fdca87dSLee, Chun-Yi int err;
21563fdca87dSLee, Chun-Yi
21573fdca87dSLee, Chun-Yi acer_wmi_input_dev = input_allocate_device();
21583fdca87dSLee, Chun-Yi if (!acer_wmi_input_dev)
21593fdca87dSLee, Chun-Yi return -ENOMEM;
21603fdca87dSLee, Chun-Yi
21613fdca87dSLee, Chun-Yi acer_wmi_input_dev->name = "Acer WMI hotkeys";
21623fdca87dSLee, Chun-Yi acer_wmi_input_dev->phys = "wmi/input0";
21633fdca87dSLee, Chun-Yi acer_wmi_input_dev->id.bustype = BUS_HOST;
21643fdca87dSLee, Chun-Yi
21653fdca87dSLee, Chun-Yi err = sparse_keymap_setup(acer_wmi_input_dev, acer_wmi_keymap, NULL);
21663fdca87dSLee, Chun-Yi if (err)
21673fdca87dSLee, Chun-Yi goto err_free_dev;
21683fdca87dSLee, Chun-Yi
21695c54cb6cSHans de Goede if (has_cap(ACER_CAP_KBD_DOCK))
21705c54cb6cSHans de Goede input_set_capability(acer_wmi_input_dev, EV_SW, SW_TABLET_MODE);
21715c54cb6cSHans de Goede
21723fdca87dSLee, Chun-Yi status = wmi_install_notify_handler(ACERWMID_EVENT_GUID,
21733fdca87dSLee, Chun-Yi acer_wmi_notify, NULL);
21743fdca87dSLee, Chun-Yi if (ACPI_FAILURE(status)) {
21753fdca87dSLee, Chun-Yi err = -EIO;
2176cd1aaef0SMichał Kępień goto err_free_dev;
21773fdca87dSLee, Chun-Yi }
21783fdca87dSLee, Chun-Yi
21795c54cb6cSHans de Goede if (has_cap(ACER_CAP_KBD_DOCK))
21805c54cb6cSHans de Goede acer_kbd_dock_get_initial_state();
21815c54cb6cSHans de Goede
21823fdca87dSLee, Chun-Yi err = input_register_device(acer_wmi_input_dev);
21833fdca87dSLee, Chun-Yi if (err)
21843fdca87dSLee, Chun-Yi goto err_uninstall_notifier;
21853fdca87dSLee, Chun-Yi
21863fdca87dSLee, Chun-Yi return 0;
21873fdca87dSLee, Chun-Yi
21883fdca87dSLee, Chun-Yi err_uninstall_notifier:
21893fdca87dSLee, Chun-Yi wmi_remove_notify_handler(ACERWMID_EVENT_GUID);
21903fdca87dSLee, Chun-Yi err_free_dev:
21913fdca87dSLee, Chun-Yi input_free_device(acer_wmi_input_dev);
21923fdca87dSLee, Chun-Yi return err;
21933fdca87dSLee, Chun-Yi }
21943fdca87dSLee, Chun-Yi
acer_wmi_input_destroy(void)21953fdca87dSLee, Chun-Yi static void acer_wmi_input_destroy(void)
21963fdca87dSLee, Chun-Yi {
21973fdca87dSLee, Chun-Yi wmi_remove_notify_handler(ACERWMID_EVENT_GUID);
21983fdca87dSLee, Chun-Yi input_unregister_device(acer_wmi_input_dev);
21993fdca87dSLee, Chun-Yi }
22003fdca87dSLee, Chun-Yi
220141b16dceSLen Brown /*
220241b16dceSLen Brown * debugfs functions
220341b16dceSLen Brown */
get_wmid_devices(void)220441b16dceSLen Brown static u32 get_wmid_devices(void)
220541b16dceSLen Brown {
220641b16dceSLen Brown struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
220741b16dceSLen Brown union acpi_object *obj;
220841b16dceSLen Brown acpi_status status;
220966904863SAxel Lin u32 devices = 0;
221041b16dceSLen Brown
221162fc743cSLee, Chun-Yi status = wmi_query_block(WMID_GUID2, 0, &out);
221241b16dceSLen Brown if (ACPI_FAILURE(status))
221341b16dceSLen Brown return 0;
221441b16dceSLen Brown
221541b16dceSLen Brown obj = (union acpi_object *) out.pointer;
2216f20aaba9SLee, Chun-Yi if (obj) {
2217f20aaba9SLee, Chun-Yi if (obj->type == ACPI_TYPE_BUFFER &&
2218ab6a9316SLee, Chun-Yi (obj->buffer.length == sizeof(u32) ||
2219ab6a9316SLee, Chun-Yi obj->buffer.length == sizeof(u64))) {
222066904863SAxel Lin devices = *((u32 *) obj->buffer.pointer);
2221987dfbaaSLee, Chun-Yi } else if (obj->type == ACPI_TYPE_INTEGER) {
2222987dfbaaSLee, Chun-Yi devices = (u32) obj->integer.value;
222341b16dceSLen Brown }
2224f20aaba9SLee, Chun-Yi }
222566904863SAxel Lin
222666904863SAxel Lin kfree(out.pointer);
222766904863SAxel Lin return devices;
222841b16dceSLen Brown }
222941b16dceSLen Brown
223041b16dceSLen Brown /*
223141b16dceSLen Brown * Platform device
223241b16dceSLen Brown */
acer_platform_probe(struct platform_device * device)2233b859f159SGreg Kroah-Hartman static int acer_platform_probe(struct platform_device *device)
223441b16dceSLen Brown {
223541b16dceSLen Brown int err;
223641b16dceSLen Brown
223741b16dceSLen Brown if (has_cap(ACER_CAP_MAILLED)) {
223841b16dceSLen Brown err = acer_led_init(&device->dev);
223941b16dceSLen Brown if (err)
224041b16dceSLen Brown goto error_mailled;
224141b16dceSLen Brown }
224241b16dceSLen Brown
224341b16dceSLen Brown if (has_cap(ACER_CAP_BRIGHTNESS)) {
224441b16dceSLen Brown err = acer_backlight_init(&device->dev);
224541b16dceSLen Brown if (err)
224641b16dceSLen Brown goto error_brightness;
224741b16dceSLen Brown }
224841b16dceSLen Brown
224941b16dceSLen Brown err = acer_rfkill_init(&device->dev);
2250350e3290SAndy Whitcroft if (err)
2251350e3290SAndy Whitcroft goto error_rfkill;
225241b16dceSLen Brown
225341b16dceSLen Brown return err;
225441b16dceSLen Brown
2255350e3290SAndy Whitcroft error_rfkill:
2256350e3290SAndy Whitcroft if (has_cap(ACER_CAP_BRIGHTNESS))
2257350e3290SAndy Whitcroft acer_backlight_exit();
225841b16dceSLen Brown error_brightness:
2259350e3290SAndy Whitcroft if (has_cap(ACER_CAP_MAILLED))
226041b16dceSLen Brown acer_led_exit();
226141b16dceSLen Brown error_mailled:
226241b16dceSLen Brown return err;
226341b16dceSLen Brown }
226441b16dceSLen Brown
acer_platform_remove(struct platform_device * device)226596c59f59SUwe Kleine-König static void acer_platform_remove(struct platform_device *device)
226641b16dceSLen Brown {
226741b16dceSLen Brown if (has_cap(ACER_CAP_MAILLED))
226841b16dceSLen Brown acer_led_exit();
226941b16dceSLen Brown if (has_cap(ACER_CAP_BRIGHTNESS))
227041b16dceSLen Brown acer_backlight_exit();
227141b16dceSLen Brown
227241b16dceSLen Brown acer_rfkill_exit();
227341b16dceSLen Brown }
227441b16dceSLen Brown
227580f65558SMathias Krause #ifdef CONFIG_PM_SLEEP
acer_suspend(struct device * dev)22763c33be0bSRafael J. Wysocki static int acer_suspend(struct device *dev)
227741b16dceSLen Brown {
227841b16dceSLen Brown u32 value;
227941b16dceSLen Brown struct acer_data *data = &interface->data;
228041b16dceSLen Brown
228141b16dceSLen Brown if (!data)
228241b16dceSLen Brown return -ENOMEM;
228341b16dceSLen Brown
228441b16dceSLen Brown if (has_cap(ACER_CAP_MAILLED)) {
228541b16dceSLen Brown get_u32(&value, ACER_CAP_MAILLED);
22869a0b74fdSPali Rohár set_u32(LED_OFF, ACER_CAP_MAILLED);
228741b16dceSLen Brown data->mailled = value;
228841b16dceSLen Brown }
228941b16dceSLen Brown
229041b16dceSLen Brown if (has_cap(ACER_CAP_BRIGHTNESS)) {
229141b16dceSLen Brown get_u32(&value, ACER_CAP_BRIGHTNESS);
229241b16dceSLen Brown data->brightness = value;
229341b16dceSLen Brown }
229441b16dceSLen Brown
229541b16dceSLen Brown return 0;
229641b16dceSLen Brown }
229741b16dceSLen Brown
acer_resume(struct device * dev)22983c33be0bSRafael J. Wysocki static int acer_resume(struct device *dev)
229941b16dceSLen Brown {
230041b16dceSLen Brown struct acer_data *data = &interface->data;
230141b16dceSLen Brown
230241b16dceSLen Brown if (!data)
230341b16dceSLen Brown return -ENOMEM;
230441b16dceSLen Brown
230541b16dceSLen Brown if (has_cap(ACER_CAP_MAILLED))
230641b16dceSLen Brown set_u32(data->mailled, ACER_CAP_MAILLED);
230741b16dceSLen Brown
230841b16dceSLen Brown if (has_cap(ACER_CAP_BRIGHTNESS))
230941b16dceSLen Brown set_u32(data->brightness, ACER_CAP_BRIGHTNESS);
231041b16dceSLen Brown
23119feb0763SHans de Goede if (acer_wmi_accel_dev)
23121eb3fe1dSMarek Vasut acer_gsensor_init();
23131eb3fe1dSMarek Vasut
231441b16dceSLen Brown return 0;
231541b16dceSLen Brown }
231680f65558SMathias Krause #else
231780f65558SMathias Krause #define acer_suspend NULL
231880f65558SMathias Krause #define acer_resume NULL
231980f65558SMathias Krause #endif
232041b16dceSLen Brown
23213c33be0bSRafael J. Wysocki static SIMPLE_DEV_PM_OPS(acer_pm, acer_suspend, acer_resume);
23223c33be0bSRafael J. Wysocki
acer_platform_shutdown(struct platform_device * device)23239a0b74fdSPali Rohár static void acer_platform_shutdown(struct platform_device *device)
23249a0b74fdSPali Rohár {
23259a0b74fdSPali Rohár struct acer_data *data = &interface->data;
23269a0b74fdSPali Rohár
23279a0b74fdSPali Rohár if (!data)
23289a0b74fdSPali Rohár return;
23299a0b74fdSPali Rohár
23309a0b74fdSPali Rohár if (has_cap(ACER_CAP_MAILLED))
23319a0b74fdSPali Rohár set_u32(LED_OFF, ACER_CAP_MAILLED);
23329a0b74fdSPali Rohár }
23339a0b74fdSPali Rohár
233441b16dceSLen Brown static struct platform_driver acer_platform_driver = {
233541b16dceSLen Brown .driver = {
233641b16dceSLen Brown .name = "acer-wmi",
23373c33be0bSRafael J. Wysocki .pm = &acer_pm,
233841b16dceSLen Brown },
233941b16dceSLen Brown .probe = acer_platform_probe,
234096c59f59SUwe Kleine-König .remove_new = acer_platform_remove,
23419a0b74fdSPali Rohár .shutdown = acer_platform_shutdown,
234241b16dceSLen Brown };
234341b16dceSLen Brown
234441b16dceSLen Brown static struct platform_device *acer_platform_device;
234541b16dceSLen Brown
remove_debugfs(void)234641b16dceSLen Brown static void remove_debugfs(void)
234741b16dceSLen Brown {
23480b9dd934SGreg Kroah-Hartman debugfs_remove_recursive(interface->debug.root);
234941b16dceSLen Brown }
235041b16dceSLen Brown
create_debugfs(void)23510b9dd934SGreg Kroah-Hartman static void __init create_debugfs(void)
235241b16dceSLen Brown {
235341b16dceSLen Brown interface->debug.root = debugfs_create_dir("acer-wmi", NULL);
235441b16dceSLen Brown
23550b9dd934SGreg Kroah-Hartman debugfs_create_u32("devices", S_IRUGO, interface->debug.root,
235641b16dceSLen Brown &interface->debug.wmid_devices);
235741b16dceSLen Brown }
235841b16dceSLen Brown
acer_wmi_init(void)235941b16dceSLen Brown static int __init acer_wmi_init(void)
236041b16dceSLen Brown {
236141b16dceSLen Brown int err;
236241b16dceSLen Brown
2363cae15702SLee, Chun-Yi pr_info("Acer Laptop ACPI-WMI Extras\n");
236441b16dceSLen Brown
2365a74dd5fdSCarlos Corbacho if (dmi_check_system(acer_blacklist)) {
2366cae15702SLee, Chun-Yi pr_info("Blacklisted hardware detected - not loading\n");
2367a74dd5fdSCarlos Corbacho return -ENODEV;
2368a74dd5fdSCarlos Corbacho }
2369a74dd5fdSCarlos Corbacho
237041b16dceSLen Brown find_quirks();
237141b16dceSLen Brown
237241b16dceSLen Brown /*
23735241b193SLee, Chun-Yi * The AMW0_GUID1 wmi is not only found on Acer family but also other
23745241b193SLee, Chun-Yi * machines like Lenovo, Fujitsu and Medion. In the past days,
23755241b193SLee, Chun-Yi * acer-wmi driver handled those non-Acer machines by quirks list.
23765241b193SLee, Chun-Yi * But actually acer-wmi driver was loaded on any machines that have
23775241b193SLee, Chun-Yi * AMW0_GUID1. This behavior is strange because those machines should
23785241b193SLee, Chun-Yi * be supported by appropriate wmi drivers. e.g. fujitsu-laptop,
23795241b193SLee, Chun-Yi * ideapad-laptop. So, here checks the machine that has AMW0_GUID1
23805241b193SLee, Chun-Yi * should be in Acer/Gateway/Packard Bell white list, or it's already
23815241b193SLee, Chun-Yi * in the past quirk list.
23825241b193SLee, Chun-Yi */
23835241b193SLee, Chun-Yi if (wmi_has_guid(AMW0_GUID1) &&
23845241b193SLee, Chun-Yi !dmi_check_system(amw0_whitelist) &&
23855241b193SLee, Chun-Yi quirks == &quirk_unknown) {
23869bd5196eSBenjamin Herrenschmidt pr_debug("Unsupported machine has AMW0_GUID1, unable to load\n");
23875241b193SLee, Chun-Yi return -ENODEV;
23885241b193SLee, Chun-Yi }
23895241b193SLee, Chun-Yi
23905241b193SLee, Chun-Yi /*
239141b16dceSLen Brown * Detect which ACPI-WMI interface we're using.
239241b16dceSLen Brown */
239341b16dceSLen Brown if (wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
239441b16dceSLen Brown interface = &AMW0_V2_interface;
239541b16dceSLen Brown
239641b16dceSLen Brown if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
239741b16dceSLen Brown interface = &wmid_interface;
239841b16dceSLen Brown
239972e71de1SLee, Chun-Yi if (wmi_has_guid(WMID_GUID3))
240072e71de1SLee, Chun-Yi interface = &wmid_v2_interface;
240172e71de1SLee, Chun-Yi
240272e71de1SLee, Chun-Yi if (interface)
240372e71de1SLee, Chun-Yi dmi_walk(type_aa_dmi_decode, NULL);
240472e71de1SLee, Chun-Yi
240541b16dceSLen Brown if (wmi_has_guid(WMID_GUID2) && interface) {
240672e71de1SLee, Chun-Yi if (!has_type_aa && ACPI_FAILURE(WMID_set_capabilities())) {
2407cae15702SLee, Chun-Yi pr_err("Unable to detect available WMID devices\n");
240841b16dceSLen Brown return -ENODEV;
240941b16dceSLen Brown }
241072e71de1SLee, Chun-Yi /* WMID always provides brightness methods */
241172e71de1SLee, Chun-Yi interface->capability |= ACER_CAP_BRIGHTNESS;
241239aa009bSHans de Goede } else if (!wmi_has_guid(WMID_GUID2) && interface && !has_type_aa && force_caps == -1) {
2413cae15702SLee, Chun-Yi pr_err("No WMID device detection method found\n");
241441b16dceSLen Brown return -ENODEV;
241541b16dceSLen Brown }
241641b16dceSLen Brown
241741b16dceSLen Brown if (wmi_has_guid(AMW0_GUID1) && !wmi_has_guid(WMID_GUID1)) {
241841b16dceSLen Brown interface = &AMW0_interface;
241941b16dceSLen Brown
242041b16dceSLen Brown if (ACPI_FAILURE(AMW0_set_capabilities())) {
2421cae15702SLee, Chun-Yi pr_err("Unable to detect available AMW0 devices\n");
242241b16dceSLen Brown return -ENODEV;
242341b16dceSLen Brown }
242441b16dceSLen Brown }
242541b16dceSLen Brown
242641b16dceSLen Brown if (wmi_has_guid(AMW0_GUID1))
242741b16dceSLen Brown AMW0_find_mailled();
242841b16dceSLen Brown
242941b16dceSLen Brown if (!interface) {
2430cae15702SLee, Chun-Yi pr_err("No or unsupported WMI interface, unable to load\n");
243141b16dceSLen Brown return -ENODEV;
243241b16dceSLen Brown }
243341b16dceSLen Brown
243441b16dceSLen Brown set_quirks();
243541b16dceSLen Brown
24369a65f0dfSHans de Goede if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
243741b16dceSLen Brown interface->capability &= ~ACER_CAP_BRIGHTNESS;
243841b16dceSLen Brown
243982cb8a5cSHans de Goede if (wmi_has_guid(WMID_GUID3))
244082cb8a5cSHans de Goede interface->capability |= ACER_CAP_SET_FUNCTION_MODE;
244182cb8a5cSHans de Goede
244239aa009bSHans de Goede if (force_caps != -1)
244339aa009bSHans de Goede interface->capability = force_caps;
244439aa009bSHans de Goede
244582cb8a5cSHans de Goede if (wmi_has_guid(WMID_GUID3) &&
244682cb8a5cSHans de Goede (interface->capability & ACER_CAP_SET_FUNCTION_MODE)) {
2447280642c3SChris Chiu if (ACPI_FAILURE(acer_wmi_enable_rf_button()))
2448280642c3SChris Chiu pr_warn("Cannot enable RF Button Driver\n");
2449280642c3SChris Chiu
245059ccf2f3SFrom: Lee, Chun-Yi if (ec_raw_mode) {
245159ccf2f3SFrom: Lee, Chun-Yi if (ACPI_FAILURE(acer_wmi_enable_ec_raw())) {
2452cae15702SLee, Chun-Yi pr_err("Cannot enable EC raw mode\n");
245359ccf2f3SFrom: Lee, Chun-Yi return -ENODEV;
245459ccf2f3SFrom: Lee, Chun-Yi }
245559ccf2f3SFrom: Lee, Chun-Yi } else if (ACPI_FAILURE(acer_wmi_enable_lm())) {
2456cae15702SLee, Chun-Yi pr_err("Cannot enable Launch Manager mode\n");
245759ccf2f3SFrom: Lee, Chun-Yi return -ENODEV;
245859ccf2f3SFrom: Lee, Chun-Yi }
245959ccf2f3SFrom: Lee, Chun-Yi } else if (ec_raw_mode) {
2460cae15702SLee, Chun-Yi pr_info("No WMID EC raw mode enable method\n");
246159ccf2f3SFrom: Lee, Chun-Yi }
246259ccf2f3SFrom: Lee, Chun-Yi
24633fdca87dSLee, Chun-Yi if (wmi_has_guid(ACERWMID_EVENT_GUID)) {
24643fdca87dSLee, Chun-Yi err = acer_wmi_input_setup();
24653fdca87dSLee, Chun-Yi if (err)
24663fdca87dSLee, Chun-Yi return err;
246798d610c3SLee, Chun-Yi err = acer_wmi_accel_setup();
2468f9ac89f5SLee, Chun-Yi if (err && err != -ENODEV)
2469f9ac89f5SLee, Chun-Yi pr_warn("Cannot enable accelerometer\n");
24703fdca87dSLee, Chun-Yi }
24713fdca87dSLee, Chun-Yi
24721c79632bSAxel Lin err = platform_driver_register(&acer_platform_driver);
24731c79632bSAxel Lin if (err) {
24746e71f38bSJoe Perches pr_err("Unable to register platform driver\n");
247541b16dceSLen Brown goto error_platform_register;
247641b16dceSLen Brown }
24771c79632bSAxel Lin
24788d05fc03SBarnabás Pőcze acer_platform_device = platform_device_alloc("acer-wmi", PLATFORM_DEVID_NONE);
24791c79632bSAxel Lin if (!acer_platform_device) {
24801c79632bSAxel Lin err = -ENOMEM;
24811c79632bSAxel Lin goto error_device_alloc;
24821c79632bSAxel Lin }
24831c79632bSAxel Lin
24841c79632bSAxel Lin err = platform_device_add(acer_platform_device);
24851c79632bSAxel Lin if (err)
24861c79632bSAxel Lin goto error_device_add;
248741b16dceSLen Brown
248841b16dceSLen Brown if (wmi_has_guid(WMID_GUID2)) {
248941b16dceSLen Brown interface->debug.wmid_devices = get_wmid_devices();
24900b9dd934SGreg Kroah-Hartman create_debugfs();
249141b16dceSLen Brown }
249241b16dceSLen Brown
249341b16dceSLen Brown /* Override any initial settings with values from the commandline */
249441b16dceSLen Brown acer_commandline_init();
249541b16dceSLen Brown
249641b16dceSLen Brown return 0;
249741b16dceSLen Brown
24981c79632bSAxel Lin error_device_add:
24991c79632bSAxel Lin platform_device_put(acer_platform_device);
25001c79632bSAxel Lin error_device_alloc:
25011c79632bSAxel Lin platform_driver_unregister(&acer_platform_driver);
250241b16dceSLen Brown error_platform_register:
25033fdca87dSLee, Chun-Yi if (wmi_has_guid(ACERWMID_EVENT_GUID))
25043fdca87dSLee, Chun-Yi acer_wmi_input_destroy();
25059feb0763SHans de Goede if (acer_wmi_accel_dev)
25069feb0763SHans de Goede input_unregister_device(acer_wmi_accel_dev);
25073fdca87dSLee, Chun-Yi
25081c79632bSAxel Lin return err;
250941b16dceSLen Brown }
251041b16dceSLen Brown
acer_wmi_exit(void)251141b16dceSLen Brown static void __exit acer_wmi_exit(void)
251241b16dceSLen Brown {
25133fdca87dSLee, Chun-Yi if (wmi_has_guid(ACERWMID_EVENT_GUID))
25143fdca87dSLee, Chun-Yi acer_wmi_input_destroy();
25153fdca87dSLee, Chun-Yi
25169feb0763SHans de Goede if (acer_wmi_accel_dev)
25179feb0763SHans de Goede input_unregister_device(acer_wmi_accel_dev);
25181eb3fe1dSMarek Vasut
251941b16dceSLen Brown remove_debugfs();
252097ba0af0SAxel Lin platform_device_unregister(acer_platform_device);
252141b16dceSLen Brown platform_driver_unregister(&acer_platform_driver);
252241b16dceSLen Brown
2523cae15702SLee, Chun-Yi pr_info("Acer Laptop WMI Extras unloaded\n");
252441b16dceSLen Brown return;
252541b16dceSLen Brown }
252641b16dceSLen Brown
252741b16dceSLen Brown module_init(acer_wmi_init);
252841b16dceSLen Brown module_exit(acer_wmi_exit);
2529