1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*-*-linux-c-*-*/
3*4882a593Smuzhiyun
4*4882a593Smuzhiyun /*
5*4882a593Smuzhiyun Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun /*
10*4882a593Smuzhiyun * msi-laptop.c - MSI S270 laptop support. This laptop is sold under
11*4882a593Smuzhiyun * various brands, including "Cytron/TCM/Medion/Tchibo MD96100".
12*4882a593Smuzhiyun *
13*4882a593Smuzhiyun * Driver also supports S271, S420 models.
14*4882a593Smuzhiyun *
15*4882a593Smuzhiyun * This driver exports a few files in /sys/devices/platform/msi-laptop-pf/:
16*4882a593Smuzhiyun *
17*4882a593Smuzhiyun * lcd_level - Screen brightness: contains a single integer in the
18*4882a593Smuzhiyun * range 0..8. (rw)
19*4882a593Smuzhiyun *
20*4882a593Smuzhiyun * auto_brightness - Enable automatic brightness control: contains
21*4882a593Smuzhiyun * either 0 or 1. If set to 1 the hardware adjusts the screen
22*4882a593Smuzhiyun * brightness automatically when the power cord is
23*4882a593Smuzhiyun * plugged/unplugged. (rw)
24*4882a593Smuzhiyun *
25*4882a593Smuzhiyun * wlan - WLAN subsystem enabled: contains either 0 or 1. (ro)
26*4882a593Smuzhiyun *
27*4882a593Smuzhiyun * bluetooth - Bluetooth subsystem enabled: contains either 0 or 1
28*4882a593Smuzhiyun * Please note that this file is constantly 0 if no Bluetooth
29*4882a593Smuzhiyun * hardware is available. (ro)
30*4882a593Smuzhiyun *
31*4882a593Smuzhiyun * In addition to these platform device attributes the driver
32*4882a593Smuzhiyun * registers itself in the Linux backlight control subsystem and is
33*4882a593Smuzhiyun * available to userspace under /sys/class/backlight/msi-laptop-bl/.
34*4882a593Smuzhiyun *
35*4882a593Smuzhiyun * This driver might work on other laptops produced by MSI. If you
36*4882a593Smuzhiyun * want to try it you can pass force=1 as argument to the module which
37*4882a593Smuzhiyun * will force it to load even when the DMI data doesn't identify the
38*4882a593Smuzhiyun * laptop as MSI S270. YMMV.
39*4882a593Smuzhiyun */
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun #include <linux/module.h>
44*4882a593Smuzhiyun #include <linux/kernel.h>
45*4882a593Smuzhiyun #include <linux/init.h>
46*4882a593Smuzhiyun #include <linux/acpi.h>
47*4882a593Smuzhiyun #include <linux/dmi.h>
48*4882a593Smuzhiyun #include <linux/backlight.h>
49*4882a593Smuzhiyun #include <linux/platform_device.h>
50*4882a593Smuzhiyun #include <linux/rfkill.h>
51*4882a593Smuzhiyun #include <linux/i8042.h>
52*4882a593Smuzhiyun #include <linux/input.h>
53*4882a593Smuzhiyun #include <linux/input/sparse-keymap.h>
54*4882a593Smuzhiyun #include <acpi/video.h>
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun #define MSI_DRIVER_VERSION "0.5"
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun #define MSI_LCD_LEVEL_MAX 9
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun #define MSI_EC_COMMAND_WIRELESS 0x10
61*4882a593Smuzhiyun #define MSI_EC_COMMAND_LCD_LEVEL 0x11
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun #define MSI_STANDARD_EC_COMMAND_ADDRESS 0x2e
64*4882a593Smuzhiyun #define MSI_STANDARD_EC_BLUETOOTH_MASK (1 << 0)
65*4882a593Smuzhiyun #define MSI_STANDARD_EC_WEBCAM_MASK (1 << 1)
66*4882a593Smuzhiyun #define MSI_STANDARD_EC_WLAN_MASK (1 << 3)
67*4882a593Smuzhiyun #define MSI_STANDARD_EC_3G_MASK (1 << 4)
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun /* For set SCM load flag to disable BIOS fn key */
70*4882a593Smuzhiyun #define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d
71*4882a593Smuzhiyun #define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0)
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun #define MSI_STANDARD_EC_FUNCTIONS_ADDRESS 0xe4
74*4882a593Smuzhiyun /* Power LED is orange - Turbo mode */
75*4882a593Smuzhiyun #define MSI_STANDARD_EC_TURBO_MASK (1 << 1)
76*4882a593Smuzhiyun /* Power LED is green - ECO mode */
77*4882a593Smuzhiyun #define MSI_STANDARD_EC_ECO_MASK (1 << 3)
78*4882a593Smuzhiyun /* Touchpad is turned on */
79*4882a593Smuzhiyun #define MSI_STANDARD_EC_TOUCHPAD_MASK (1 << 4)
80*4882a593Smuzhiyun /* If this bit != bit 1, turbo mode can't be toggled */
81*4882a593Smuzhiyun #define MSI_STANDARD_EC_TURBO_COOLDOWN_MASK (1 << 7)
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun #define MSI_STANDARD_EC_FAN_ADDRESS 0x33
84*4882a593Smuzhiyun /* If zero, fan rotates at maximal speed */
85*4882a593Smuzhiyun #define MSI_STANDARD_EC_AUTOFAN_MASK (1 << 0)
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
88*4882a593Smuzhiyun static int msi_laptop_resume(struct device *device);
89*4882a593Smuzhiyun #endif
90*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(msi_laptop_pm, NULL, msi_laptop_resume);
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun #define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun static bool force;
95*4882a593Smuzhiyun module_param(force, bool, 0);
96*4882a593Smuzhiyun MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun static int auto_brightness;
99*4882a593Smuzhiyun module_param(auto_brightness, int, 0);
100*4882a593Smuzhiyun MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)");
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun static const struct key_entry msi_laptop_keymap[] = {
103*4882a593Smuzhiyun {KE_KEY, KEY_TOUCHPAD_ON, {KEY_TOUCHPAD_ON} }, /* Touch Pad On */
104*4882a593Smuzhiyun {KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} },/* Touch Pad On */
105*4882a593Smuzhiyun {KE_END, 0}
106*4882a593Smuzhiyun };
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun static struct input_dev *msi_laptop_input_dev;
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun static int wlan_s, bluetooth_s, threeg_s;
111*4882a593Smuzhiyun static int threeg_exists;
112*4882a593Smuzhiyun static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg;
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun /* MSI laptop quirks */
115*4882a593Smuzhiyun struct quirk_entry {
116*4882a593Smuzhiyun bool old_ec_model;
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun /* Some MSI 3G netbook only have one fn key to control
119*4882a593Smuzhiyun * Wlan/Bluetooth/3G, those netbook will load the SCM (windows app) to
120*4882a593Smuzhiyun * disable the original Wlan/Bluetooth control by BIOS when user press
121*4882a593Smuzhiyun * fn key, then control Wlan/Bluetooth/3G by SCM (software control by
122*4882a593Smuzhiyun * OS). Without SCM, user cann't on/off 3G module on those 3G netbook.
123*4882a593Smuzhiyun * On Linux, msi-laptop driver will do the same thing to disable the
124*4882a593Smuzhiyun * original BIOS control, then might need use HAL or other userland
125*4882a593Smuzhiyun * application to do the software control that simulate with SCM.
126*4882a593Smuzhiyun * e.g. MSI N034 netbook
127*4882a593Smuzhiyun */
128*4882a593Smuzhiyun bool load_scm_model;
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun /* Some MSI laptops need delay before reading from EC */
131*4882a593Smuzhiyun bool ec_delay;
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun /* Some MSI Wind netbooks (e.g. MSI Wind U100) need loading SCM to get
134*4882a593Smuzhiyun * some features working (e.g. ECO mode), but we cannot change
135*4882a593Smuzhiyun * Wlan/Bluetooth state in software and we can only read its state.
136*4882a593Smuzhiyun */
137*4882a593Smuzhiyun bool ec_read_only;
138*4882a593Smuzhiyun };
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun static struct quirk_entry *quirks;
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun /* Hardware access */
143*4882a593Smuzhiyun
set_lcd_level(int level)144*4882a593Smuzhiyun static int set_lcd_level(int level)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun u8 buf[2];
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun if (level < 0 || level >= MSI_LCD_LEVEL_MAX)
149*4882a593Smuzhiyun return -EINVAL;
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun buf[0] = 0x80;
152*4882a593Smuzhiyun buf[1] = (u8) (level*31);
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf),
155*4882a593Smuzhiyun NULL, 0);
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun
get_lcd_level(void)158*4882a593Smuzhiyun static int get_lcd_level(void)
159*4882a593Smuzhiyun {
160*4882a593Smuzhiyun u8 wdata = 0, rdata;
161*4882a593Smuzhiyun int result;
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1,
164*4882a593Smuzhiyun &rdata, 1);
165*4882a593Smuzhiyun if (result < 0)
166*4882a593Smuzhiyun return result;
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun return (int) rdata / 31;
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun
get_auto_brightness(void)171*4882a593Smuzhiyun static int get_auto_brightness(void)
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun u8 wdata = 4, rdata;
174*4882a593Smuzhiyun int result;
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1,
177*4882a593Smuzhiyun &rdata, 1);
178*4882a593Smuzhiyun if (result < 0)
179*4882a593Smuzhiyun return result;
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun return !!(rdata & 8);
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun
set_auto_brightness(int enable)184*4882a593Smuzhiyun static int set_auto_brightness(int enable)
185*4882a593Smuzhiyun {
186*4882a593Smuzhiyun u8 wdata[2], rdata;
187*4882a593Smuzhiyun int result;
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun wdata[0] = 4;
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1,
192*4882a593Smuzhiyun &rdata, 1);
193*4882a593Smuzhiyun if (result < 0)
194*4882a593Smuzhiyun return result;
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun wdata[0] = 0x84;
197*4882a593Smuzhiyun wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0);
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2,
200*4882a593Smuzhiyun NULL, 0);
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun
set_device_state(const char * buf,size_t count,u8 mask)203*4882a593Smuzhiyun static ssize_t set_device_state(const char *buf, size_t count, u8 mask)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun int status;
206*4882a593Smuzhiyun u8 wdata = 0, rdata;
207*4882a593Smuzhiyun int result;
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun if (sscanf(buf, "%i", &status) != 1 || (status < 0 || status > 1))
210*4882a593Smuzhiyun return -EINVAL;
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun if (quirks->ec_read_only)
213*4882a593Smuzhiyun return -EOPNOTSUPP;
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun /* read current device state */
216*4882a593Smuzhiyun result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
217*4882a593Smuzhiyun if (result < 0)
218*4882a593Smuzhiyun return result;
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun if (!!(rdata & mask) != status) {
221*4882a593Smuzhiyun /* reverse device bit */
222*4882a593Smuzhiyun if (rdata & mask)
223*4882a593Smuzhiyun wdata = rdata & ~mask;
224*4882a593Smuzhiyun else
225*4882a593Smuzhiyun wdata = rdata | mask;
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun result = ec_write(MSI_STANDARD_EC_COMMAND_ADDRESS, wdata);
228*4882a593Smuzhiyun if (result < 0)
229*4882a593Smuzhiyun return result;
230*4882a593Smuzhiyun }
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun return count;
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun
get_wireless_state(int * wlan,int * bluetooth)235*4882a593Smuzhiyun static int get_wireless_state(int *wlan, int *bluetooth)
236*4882a593Smuzhiyun {
237*4882a593Smuzhiyun u8 wdata = 0, rdata;
238*4882a593Smuzhiyun int result;
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1);
241*4882a593Smuzhiyun if (result < 0)
242*4882a593Smuzhiyun return result;
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun if (wlan)
245*4882a593Smuzhiyun *wlan = !!(rdata & 8);
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun if (bluetooth)
248*4882a593Smuzhiyun *bluetooth = !!(rdata & 128);
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun return 0;
251*4882a593Smuzhiyun }
252*4882a593Smuzhiyun
get_wireless_state_ec_standard(void)253*4882a593Smuzhiyun static int get_wireless_state_ec_standard(void)
254*4882a593Smuzhiyun {
255*4882a593Smuzhiyun u8 rdata;
256*4882a593Smuzhiyun int result;
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
259*4882a593Smuzhiyun if (result < 0)
260*4882a593Smuzhiyun return result;
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun wlan_s = !!(rdata & MSI_STANDARD_EC_WLAN_MASK);
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun bluetooth_s = !!(rdata & MSI_STANDARD_EC_BLUETOOTH_MASK);
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun threeg_s = !!(rdata & MSI_STANDARD_EC_3G_MASK);
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun return 0;
269*4882a593Smuzhiyun }
270*4882a593Smuzhiyun
get_threeg_exists(void)271*4882a593Smuzhiyun static int get_threeg_exists(void)
272*4882a593Smuzhiyun {
273*4882a593Smuzhiyun u8 rdata;
274*4882a593Smuzhiyun int result;
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun result = ec_read(MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS, &rdata);
277*4882a593Smuzhiyun if (result < 0)
278*4882a593Smuzhiyun return result;
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun threeg_exists = !!(rdata & MSI_STANDARD_EC_3G_MASK);
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun return 0;
283*4882a593Smuzhiyun }
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun /* Backlight device stuff */
286*4882a593Smuzhiyun
bl_get_brightness(struct backlight_device * b)287*4882a593Smuzhiyun static int bl_get_brightness(struct backlight_device *b)
288*4882a593Smuzhiyun {
289*4882a593Smuzhiyun return get_lcd_level();
290*4882a593Smuzhiyun }
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun
bl_update_status(struct backlight_device * b)293*4882a593Smuzhiyun static int bl_update_status(struct backlight_device *b)
294*4882a593Smuzhiyun {
295*4882a593Smuzhiyun return set_lcd_level(b->props.brightness);
296*4882a593Smuzhiyun }
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun static const struct backlight_ops msibl_ops = {
299*4882a593Smuzhiyun .get_brightness = bl_get_brightness,
300*4882a593Smuzhiyun .update_status = bl_update_status,
301*4882a593Smuzhiyun };
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun static struct backlight_device *msibl_device;
304*4882a593Smuzhiyun
305*4882a593Smuzhiyun /* Platform device */
306*4882a593Smuzhiyun
show_wlan(struct device * dev,struct device_attribute * attr,char * buf)307*4882a593Smuzhiyun static ssize_t show_wlan(struct device *dev,
308*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
309*4882a593Smuzhiyun {
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun int ret, enabled = 0;
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun if (quirks->old_ec_model) {
314*4882a593Smuzhiyun ret = get_wireless_state(&enabled, NULL);
315*4882a593Smuzhiyun } else {
316*4882a593Smuzhiyun ret = get_wireless_state_ec_standard();
317*4882a593Smuzhiyun enabled = wlan_s;
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun if (ret < 0)
320*4882a593Smuzhiyun return ret;
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun return sprintf(buf, "%i\n", enabled);
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun
store_wlan(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)325*4882a593Smuzhiyun static ssize_t store_wlan(struct device *dev,
326*4882a593Smuzhiyun struct device_attribute *attr, const char *buf, size_t count)
327*4882a593Smuzhiyun {
328*4882a593Smuzhiyun return set_device_state(buf, count, MSI_STANDARD_EC_WLAN_MASK);
329*4882a593Smuzhiyun }
330*4882a593Smuzhiyun
show_bluetooth(struct device * dev,struct device_attribute * attr,char * buf)331*4882a593Smuzhiyun static ssize_t show_bluetooth(struct device *dev,
332*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
333*4882a593Smuzhiyun {
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun int ret, enabled = 0;
336*4882a593Smuzhiyun
337*4882a593Smuzhiyun if (quirks->old_ec_model) {
338*4882a593Smuzhiyun ret = get_wireless_state(NULL, &enabled);
339*4882a593Smuzhiyun } else {
340*4882a593Smuzhiyun ret = get_wireless_state_ec_standard();
341*4882a593Smuzhiyun enabled = bluetooth_s;
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun if (ret < 0)
344*4882a593Smuzhiyun return ret;
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun return sprintf(buf, "%i\n", enabled);
347*4882a593Smuzhiyun }
348*4882a593Smuzhiyun
store_bluetooth(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)349*4882a593Smuzhiyun static ssize_t store_bluetooth(struct device *dev,
350*4882a593Smuzhiyun struct device_attribute *attr, const char *buf, size_t count)
351*4882a593Smuzhiyun {
352*4882a593Smuzhiyun return set_device_state(buf, count, MSI_STANDARD_EC_BLUETOOTH_MASK);
353*4882a593Smuzhiyun }
354*4882a593Smuzhiyun
show_threeg(struct device * dev,struct device_attribute * attr,char * buf)355*4882a593Smuzhiyun static ssize_t show_threeg(struct device *dev,
356*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
357*4882a593Smuzhiyun {
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun int ret;
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun /* old msi ec not support 3G */
362*4882a593Smuzhiyun if (quirks->old_ec_model)
363*4882a593Smuzhiyun return -ENODEV;
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun ret = get_wireless_state_ec_standard();
366*4882a593Smuzhiyun if (ret < 0)
367*4882a593Smuzhiyun return ret;
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun return sprintf(buf, "%i\n", threeg_s);
370*4882a593Smuzhiyun }
371*4882a593Smuzhiyun
store_threeg(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)372*4882a593Smuzhiyun static ssize_t store_threeg(struct device *dev,
373*4882a593Smuzhiyun struct device_attribute *attr, const char *buf, size_t count)
374*4882a593Smuzhiyun {
375*4882a593Smuzhiyun return set_device_state(buf, count, MSI_STANDARD_EC_3G_MASK);
376*4882a593Smuzhiyun }
377*4882a593Smuzhiyun
show_lcd_level(struct device * dev,struct device_attribute * attr,char * buf)378*4882a593Smuzhiyun static ssize_t show_lcd_level(struct device *dev,
379*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
380*4882a593Smuzhiyun {
381*4882a593Smuzhiyun
382*4882a593Smuzhiyun int ret;
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun ret = get_lcd_level();
385*4882a593Smuzhiyun if (ret < 0)
386*4882a593Smuzhiyun return ret;
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun return sprintf(buf, "%i\n", ret);
389*4882a593Smuzhiyun }
390*4882a593Smuzhiyun
store_lcd_level(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)391*4882a593Smuzhiyun static ssize_t store_lcd_level(struct device *dev,
392*4882a593Smuzhiyun struct device_attribute *attr, const char *buf, size_t count)
393*4882a593Smuzhiyun {
394*4882a593Smuzhiyun
395*4882a593Smuzhiyun int level, ret;
396*4882a593Smuzhiyun
397*4882a593Smuzhiyun if (sscanf(buf, "%i", &level) != 1 ||
398*4882a593Smuzhiyun (level < 0 || level >= MSI_LCD_LEVEL_MAX))
399*4882a593Smuzhiyun return -EINVAL;
400*4882a593Smuzhiyun
401*4882a593Smuzhiyun ret = set_lcd_level(level);
402*4882a593Smuzhiyun if (ret < 0)
403*4882a593Smuzhiyun return ret;
404*4882a593Smuzhiyun
405*4882a593Smuzhiyun return count;
406*4882a593Smuzhiyun }
407*4882a593Smuzhiyun
show_auto_brightness(struct device * dev,struct device_attribute * attr,char * buf)408*4882a593Smuzhiyun static ssize_t show_auto_brightness(struct device *dev,
409*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
410*4882a593Smuzhiyun {
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun int ret;
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun ret = get_auto_brightness();
415*4882a593Smuzhiyun if (ret < 0)
416*4882a593Smuzhiyun return ret;
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun return sprintf(buf, "%i\n", ret);
419*4882a593Smuzhiyun }
420*4882a593Smuzhiyun
store_auto_brightness(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)421*4882a593Smuzhiyun static ssize_t store_auto_brightness(struct device *dev,
422*4882a593Smuzhiyun struct device_attribute *attr, const char *buf, size_t count)
423*4882a593Smuzhiyun {
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun int enable, ret;
426*4882a593Smuzhiyun
427*4882a593Smuzhiyun if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1)))
428*4882a593Smuzhiyun return -EINVAL;
429*4882a593Smuzhiyun
430*4882a593Smuzhiyun ret = set_auto_brightness(enable);
431*4882a593Smuzhiyun if (ret < 0)
432*4882a593Smuzhiyun return ret;
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun return count;
435*4882a593Smuzhiyun }
436*4882a593Smuzhiyun
show_touchpad(struct device * dev,struct device_attribute * attr,char * buf)437*4882a593Smuzhiyun static ssize_t show_touchpad(struct device *dev,
438*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
439*4882a593Smuzhiyun {
440*4882a593Smuzhiyun
441*4882a593Smuzhiyun u8 rdata;
442*4882a593Smuzhiyun int result;
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
445*4882a593Smuzhiyun if (result < 0)
446*4882a593Smuzhiyun return result;
447*4882a593Smuzhiyun
448*4882a593Smuzhiyun return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_TOUCHPAD_MASK));
449*4882a593Smuzhiyun }
450*4882a593Smuzhiyun
show_turbo(struct device * dev,struct device_attribute * attr,char * buf)451*4882a593Smuzhiyun static ssize_t show_turbo(struct device *dev,
452*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
453*4882a593Smuzhiyun {
454*4882a593Smuzhiyun
455*4882a593Smuzhiyun u8 rdata;
456*4882a593Smuzhiyun int result;
457*4882a593Smuzhiyun
458*4882a593Smuzhiyun result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
459*4882a593Smuzhiyun if (result < 0)
460*4882a593Smuzhiyun return result;
461*4882a593Smuzhiyun
462*4882a593Smuzhiyun return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_TURBO_MASK));
463*4882a593Smuzhiyun }
464*4882a593Smuzhiyun
show_eco(struct device * dev,struct device_attribute * attr,char * buf)465*4882a593Smuzhiyun static ssize_t show_eco(struct device *dev,
466*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
467*4882a593Smuzhiyun {
468*4882a593Smuzhiyun
469*4882a593Smuzhiyun u8 rdata;
470*4882a593Smuzhiyun int result;
471*4882a593Smuzhiyun
472*4882a593Smuzhiyun result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
473*4882a593Smuzhiyun if (result < 0)
474*4882a593Smuzhiyun return result;
475*4882a593Smuzhiyun
476*4882a593Smuzhiyun return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_ECO_MASK));
477*4882a593Smuzhiyun }
478*4882a593Smuzhiyun
show_turbo_cooldown(struct device * dev,struct device_attribute * attr,char * buf)479*4882a593Smuzhiyun static ssize_t show_turbo_cooldown(struct device *dev,
480*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
481*4882a593Smuzhiyun {
482*4882a593Smuzhiyun
483*4882a593Smuzhiyun u8 rdata;
484*4882a593Smuzhiyun int result;
485*4882a593Smuzhiyun
486*4882a593Smuzhiyun result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
487*4882a593Smuzhiyun if (result < 0)
488*4882a593Smuzhiyun return result;
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun return sprintf(buf, "%i\n", (!!(rdata & MSI_STANDARD_EC_TURBO_MASK)) |
491*4882a593Smuzhiyun (!!(rdata & MSI_STANDARD_EC_TURBO_COOLDOWN_MASK) << 1));
492*4882a593Smuzhiyun }
493*4882a593Smuzhiyun
show_auto_fan(struct device * dev,struct device_attribute * attr,char * buf)494*4882a593Smuzhiyun static ssize_t show_auto_fan(struct device *dev,
495*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
496*4882a593Smuzhiyun {
497*4882a593Smuzhiyun
498*4882a593Smuzhiyun u8 rdata;
499*4882a593Smuzhiyun int result;
500*4882a593Smuzhiyun
501*4882a593Smuzhiyun result = ec_read(MSI_STANDARD_EC_FAN_ADDRESS, &rdata);
502*4882a593Smuzhiyun if (result < 0)
503*4882a593Smuzhiyun return result;
504*4882a593Smuzhiyun
505*4882a593Smuzhiyun return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_AUTOFAN_MASK));
506*4882a593Smuzhiyun }
507*4882a593Smuzhiyun
store_auto_fan(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)508*4882a593Smuzhiyun static ssize_t store_auto_fan(struct device *dev,
509*4882a593Smuzhiyun struct device_attribute *attr, const char *buf, size_t count)
510*4882a593Smuzhiyun {
511*4882a593Smuzhiyun
512*4882a593Smuzhiyun int enable, result;
513*4882a593Smuzhiyun
514*4882a593Smuzhiyun if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1)))
515*4882a593Smuzhiyun return -EINVAL;
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun result = ec_write(MSI_STANDARD_EC_FAN_ADDRESS, enable);
518*4882a593Smuzhiyun if (result < 0)
519*4882a593Smuzhiyun return result;
520*4882a593Smuzhiyun
521*4882a593Smuzhiyun return count;
522*4882a593Smuzhiyun }
523*4882a593Smuzhiyun
524*4882a593Smuzhiyun static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
525*4882a593Smuzhiyun static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness,
526*4882a593Smuzhiyun store_auto_brightness);
527*4882a593Smuzhiyun static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL);
528*4882a593Smuzhiyun static DEVICE_ATTR(wlan, 0444, show_wlan, NULL);
529*4882a593Smuzhiyun static DEVICE_ATTR(threeg, 0444, show_threeg, NULL);
530*4882a593Smuzhiyun static DEVICE_ATTR(touchpad, 0444, show_touchpad, NULL);
531*4882a593Smuzhiyun static DEVICE_ATTR(turbo_mode, 0444, show_turbo, NULL);
532*4882a593Smuzhiyun static DEVICE_ATTR(eco_mode, 0444, show_eco, NULL);
533*4882a593Smuzhiyun static DEVICE_ATTR(turbo_cooldown, 0444, show_turbo_cooldown, NULL);
534*4882a593Smuzhiyun static DEVICE_ATTR(auto_fan, 0644, show_auto_fan, store_auto_fan);
535*4882a593Smuzhiyun
536*4882a593Smuzhiyun static struct attribute *msipf_attributes[] = {
537*4882a593Smuzhiyun &dev_attr_bluetooth.attr,
538*4882a593Smuzhiyun &dev_attr_wlan.attr,
539*4882a593Smuzhiyun &dev_attr_touchpad.attr,
540*4882a593Smuzhiyun &dev_attr_turbo_mode.attr,
541*4882a593Smuzhiyun &dev_attr_eco_mode.attr,
542*4882a593Smuzhiyun &dev_attr_turbo_cooldown.attr,
543*4882a593Smuzhiyun &dev_attr_auto_fan.attr,
544*4882a593Smuzhiyun NULL
545*4882a593Smuzhiyun };
546*4882a593Smuzhiyun
547*4882a593Smuzhiyun static struct attribute *msipf_old_attributes[] = {
548*4882a593Smuzhiyun &dev_attr_lcd_level.attr,
549*4882a593Smuzhiyun &dev_attr_auto_brightness.attr,
550*4882a593Smuzhiyun NULL
551*4882a593Smuzhiyun };
552*4882a593Smuzhiyun
553*4882a593Smuzhiyun static const struct attribute_group msipf_attribute_group = {
554*4882a593Smuzhiyun .attrs = msipf_attributes
555*4882a593Smuzhiyun };
556*4882a593Smuzhiyun
557*4882a593Smuzhiyun static const struct attribute_group msipf_old_attribute_group = {
558*4882a593Smuzhiyun .attrs = msipf_old_attributes
559*4882a593Smuzhiyun };
560*4882a593Smuzhiyun
561*4882a593Smuzhiyun static struct platform_driver msipf_driver = {
562*4882a593Smuzhiyun .driver = {
563*4882a593Smuzhiyun .name = "msi-laptop-pf",
564*4882a593Smuzhiyun .pm = &msi_laptop_pm,
565*4882a593Smuzhiyun },
566*4882a593Smuzhiyun };
567*4882a593Smuzhiyun
568*4882a593Smuzhiyun static struct platform_device *msipf_device;
569*4882a593Smuzhiyun
570*4882a593Smuzhiyun /* Initialization */
571*4882a593Smuzhiyun
572*4882a593Smuzhiyun static struct quirk_entry quirk_old_ec_model = {
573*4882a593Smuzhiyun .old_ec_model = true,
574*4882a593Smuzhiyun };
575*4882a593Smuzhiyun
576*4882a593Smuzhiyun static struct quirk_entry quirk_load_scm_model = {
577*4882a593Smuzhiyun .load_scm_model = true,
578*4882a593Smuzhiyun .ec_delay = true,
579*4882a593Smuzhiyun };
580*4882a593Smuzhiyun
581*4882a593Smuzhiyun static struct quirk_entry quirk_load_scm_ro_model = {
582*4882a593Smuzhiyun .load_scm_model = true,
583*4882a593Smuzhiyun .ec_read_only = true,
584*4882a593Smuzhiyun };
585*4882a593Smuzhiyun
dmi_check_cb(const struct dmi_system_id * dmi)586*4882a593Smuzhiyun static int dmi_check_cb(const struct dmi_system_id *dmi)
587*4882a593Smuzhiyun {
588*4882a593Smuzhiyun pr_info("Identified laptop model '%s'\n", dmi->ident);
589*4882a593Smuzhiyun
590*4882a593Smuzhiyun quirks = dmi->driver_data;
591*4882a593Smuzhiyun
592*4882a593Smuzhiyun return 1;
593*4882a593Smuzhiyun }
594*4882a593Smuzhiyun
595*4882a593Smuzhiyun static const struct dmi_system_id msi_dmi_table[] __initconst = {
596*4882a593Smuzhiyun {
597*4882a593Smuzhiyun .ident = "MSI S270",
598*4882a593Smuzhiyun .matches = {
599*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT"),
600*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"),
601*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
602*4882a593Smuzhiyun DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT")
603*4882a593Smuzhiyun },
604*4882a593Smuzhiyun .driver_data = &quirk_old_ec_model,
605*4882a593Smuzhiyun .callback = dmi_check_cb
606*4882a593Smuzhiyun },
607*4882a593Smuzhiyun {
608*4882a593Smuzhiyun .ident = "MSI S271",
609*4882a593Smuzhiyun .matches = {
610*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
611*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "MS-1058"),
612*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_VERSION, "0581"),
613*4882a593Smuzhiyun DMI_MATCH(DMI_BOARD_NAME, "MS-1058")
614*4882a593Smuzhiyun },
615*4882a593Smuzhiyun .driver_data = &quirk_old_ec_model,
616*4882a593Smuzhiyun .callback = dmi_check_cb
617*4882a593Smuzhiyun },
618*4882a593Smuzhiyun {
619*4882a593Smuzhiyun .ident = "MSI S420",
620*4882a593Smuzhiyun .matches = {
621*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
622*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "MS-1412"),
623*4882a593Smuzhiyun DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
624*4882a593Smuzhiyun DMI_MATCH(DMI_BOARD_NAME, "MS-1412")
625*4882a593Smuzhiyun },
626*4882a593Smuzhiyun .driver_data = &quirk_old_ec_model,
627*4882a593Smuzhiyun .callback = dmi_check_cb
628*4882a593Smuzhiyun },
629*4882a593Smuzhiyun {
630*4882a593Smuzhiyun .ident = "Medion MD96100",
631*4882a593Smuzhiyun .matches = {
632*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"),
633*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"),
634*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
635*4882a593Smuzhiyun DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT")
636*4882a593Smuzhiyun },
637*4882a593Smuzhiyun .driver_data = &quirk_old_ec_model,
638*4882a593Smuzhiyun .callback = dmi_check_cb
639*4882a593Smuzhiyun },
640*4882a593Smuzhiyun {
641*4882a593Smuzhiyun .ident = "MSI N034",
642*4882a593Smuzhiyun .matches = {
643*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR,
644*4882a593Smuzhiyun "MICRO-STAR INTERNATIONAL CO., LTD"),
645*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "MS-N034"),
646*4882a593Smuzhiyun DMI_MATCH(DMI_CHASSIS_VENDOR,
647*4882a593Smuzhiyun "MICRO-STAR INTERNATIONAL CO., LTD")
648*4882a593Smuzhiyun },
649*4882a593Smuzhiyun .driver_data = &quirk_load_scm_model,
650*4882a593Smuzhiyun .callback = dmi_check_cb
651*4882a593Smuzhiyun },
652*4882a593Smuzhiyun {
653*4882a593Smuzhiyun .ident = "MSI N051",
654*4882a593Smuzhiyun .matches = {
655*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR,
656*4882a593Smuzhiyun "MICRO-STAR INTERNATIONAL CO., LTD"),
657*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "MS-N051"),
658*4882a593Smuzhiyun DMI_MATCH(DMI_CHASSIS_VENDOR,
659*4882a593Smuzhiyun "MICRO-STAR INTERNATIONAL CO., LTD")
660*4882a593Smuzhiyun },
661*4882a593Smuzhiyun .driver_data = &quirk_load_scm_model,
662*4882a593Smuzhiyun .callback = dmi_check_cb
663*4882a593Smuzhiyun },
664*4882a593Smuzhiyun {
665*4882a593Smuzhiyun .ident = "MSI N014",
666*4882a593Smuzhiyun .matches = {
667*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR,
668*4882a593Smuzhiyun "MICRO-STAR INTERNATIONAL CO., LTD"),
669*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "MS-N014"),
670*4882a593Smuzhiyun },
671*4882a593Smuzhiyun .driver_data = &quirk_load_scm_model,
672*4882a593Smuzhiyun .callback = dmi_check_cb
673*4882a593Smuzhiyun },
674*4882a593Smuzhiyun {
675*4882a593Smuzhiyun .ident = "MSI CR620",
676*4882a593Smuzhiyun .matches = {
677*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR,
678*4882a593Smuzhiyun "Micro-Star International"),
679*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "CR620"),
680*4882a593Smuzhiyun },
681*4882a593Smuzhiyun .driver_data = &quirk_load_scm_model,
682*4882a593Smuzhiyun .callback = dmi_check_cb
683*4882a593Smuzhiyun },
684*4882a593Smuzhiyun {
685*4882a593Smuzhiyun .ident = "MSI U270",
686*4882a593Smuzhiyun .matches = {
687*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR,
688*4882a593Smuzhiyun "Micro-Star International Co., Ltd."),
689*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "U270 series"),
690*4882a593Smuzhiyun },
691*4882a593Smuzhiyun .driver_data = &quirk_load_scm_model,
692*4882a593Smuzhiyun .callback = dmi_check_cb
693*4882a593Smuzhiyun },
694*4882a593Smuzhiyun {
695*4882a593Smuzhiyun .ident = "MSI U90/U100",
696*4882a593Smuzhiyun .matches = {
697*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR,
698*4882a593Smuzhiyun "MICRO-STAR INTERNATIONAL CO., LTD"),
699*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "U90/U100"),
700*4882a593Smuzhiyun },
701*4882a593Smuzhiyun .driver_data = &quirk_load_scm_ro_model,
702*4882a593Smuzhiyun .callback = dmi_check_cb
703*4882a593Smuzhiyun },
704*4882a593Smuzhiyun { }
705*4882a593Smuzhiyun };
706*4882a593Smuzhiyun
rfkill_bluetooth_set(void * data,bool blocked)707*4882a593Smuzhiyun static int rfkill_bluetooth_set(void *data, bool blocked)
708*4882a593Smuzhiyun {
709*4882a593Smuzhiyun /* Do something with blocked...*/
710*4882a593Smuzhiyun /*
711*4882a593Smuzhiyun * blocked == false is on
712*4882a593Smuzhiyun * blocked == true is off
713*4882a593Smuzhiyun */
714*4882a593Smuzhiyun int result = set_device_state(blocked ? "0" : "1", 0,
715*4882a593Smuzhiyun MSI_STANDARD_EC_BLUETOOTH_MASK);
716*4882a593Smuzhiyun
717*4882a593Smuzhiyun return min(result, 0);
718*4882a593Smuzhiyun }
719*4882a593Smuzhiyun
rfkill_wlan_set(void * data,bool blocked)720*4882a593Smuzhiyun static int rfkill_wlan_set(void *data, bool blocked)
721*4882a593Smuzhiyun {
722*4882a593Smuzhiyun int result = set_device_state(blocked ? "0" : "1", 0,
723*4882a593Smuzhiyun MSI_STANDARD_EC_WLAN_MASK);
724*4882a593Smuzhiyun
725*4882a593Smuzhiyun return min(result, 0);
726*4882a593Smuzhiyun }
727*4882a593Smuzhiyun
rfkill_threeg_set(void * data,bool blocked)728*4882a593Smuzhiyun static int rfkill_threeg_set(void *data, bool blocked)
729*4882a593Smuzhiyun {
730*4882a593Smuzhiyun int result = set_device_state(blocked ? "0" : "1", 0,
731*4882a593Smuzhiyun MSI_STANDARD_EC_3G_MASK);
732*4882a593Smuzhiyun
733*4882a593Smuzhiyun return min(result, 0);
734*4882a593Smuzhiyun }
735*4882a593Smuzhiyun
736*4882a593Smuzhiyun static const struct rfkill_ops rfkill_bluetooth_ops = {
737*4882a593Smuzhiyun .set_block = rfkill_bluetooth_set
738*4882a593Smuzhiyun };
739*4882a593Smuzhiyun
740*4882a593Smuzhiyun static const struct rfkill_ops rfkill_wlan_ops = {
741*4882a593Smuzhiyun .set_block = rfkill_wlan_set
742*4882a593Smuzhiyun };
743*4882a593Smuzhiyun
744*4882a593Smuzhiyun static const struct rfkill_ops rfkill_threeg_ops = {
745*4882a593Smuzhiyun .set_block = rfkill_threeg_set
746*4882a593Smuzhiyun };
747*4882a593Smuzhiyun
rfkill_cleanup(void)748*4882a593Smuzhiyun static void rfkill_cleanup(void)
749*4882a593Smuzhiyun {
750*4882a593Smuzhiyun if (rfk_bluetooth) {
751*4882a593Smuzhiyun rfkill_unregister(rfk_bluetooth);
752*4882a593Smuzhiyun rfkill_destroy(rfk_bluetooth);
753*4882a593Smuzhiyun }
754*4882a593Smuzhiyun
755*4882a593Smuzhiyun if (rfk_threeg) {
756*4882a593Smuzhiyun rfkill_unregister(rfk_threeg);
757*4882a593Smuzhiyun rfkill_destroy(rfk_threeg);
758*4882a593Smuzhiyun }
759*4882a593Smuzhiyun
760*4882a593Smuzhiyun if (rfk_wlan) {
761*4882a593Smuzhiyun rfkill_unregister(rfk_wlan);
762*4882a593Smuzhiyun rfkill_destroy(rfk_wlan);
763*4882a593Smuzhiyun }
764*4882a593Smuzhiyun }
765*4882a593Smuzhiyun
msi_rfkill_set_state(struct rfkill * rfkill,bool blocked)766*4882a593Smuzhiyun static bool msi_rfkill_set_state(struct rfkill *rfkill, bool blocked)
767*4882a593Smuzhiyun {
768*4882a593Smuzhiyun if (quirks->ec_read_only)
769*4882a593Smuzhiyun return rfkill_set_hw_state(rfkill, blocked);
770*4882a593Smuzhiyun else
771*4882a593Smuzhiyun return rfkill_set_sw_state(rfkill, blocked);
772*4882a593Smuzhiyun }
773*4882a593Smuzhiyun
msi_update_rfkill(struct work_struct * ignored)774*4882a593Smuzhiyun static void msi_update_rfkill(struct work_struct *ignored)
775*4882a593Smuzhiyun {
776*4882a593Smuzhiyun get_wireless_state_ec_standard();
777*4882a593Smuzhiyun
778*4882a593Smuzhiyun if (rfk_wlan)
779*4882a593Smuzhiyun msi_rfkill_set_state(rfk_wlan, !wlan_s);
780*4882a593Smuzhiyun if (rfk_bluetooth)
781*4882a593Smuzhiyun msi_rfkill_set_state(rfk_bluetooth, !bluetooth_s);
782*4882a593Smuzhiyun if (rfk_threeg)
783*4882a593Smuzhiyun msi_rfkill_set_state(rfk_threeg, !threeg_s);
784*4882a593Smuzhiyun }
785*4882a593Smuzhiyun static DECLARE_DELAYED_WORK(msi_rfkill_dwork, msi_update_rfkill);
786*4882a593Smuzhiyun static DECLARE_WORK(msi_rfkill_work, msi_update_rfkill);
787*4882a593Smuzhiyun
msi_send_touchpad_key(struct work_struct * ignored)788*4882a593Smuzhiyun static void msi_send_touchpad_key(struct work_struct *ignored)
789*4882a593Smuzhiyun {
790*4882a593Smuzhiyun u8 rdata;
791*4882a593Smuzhiyun int result;
792*4882a593Smuzhiyun
793*4882a593Smuzhiyun result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
794*4882a593Smuzhiyun if (result < 0)
795*4882a593Smuzhiyun return;
796*4882a593Smuzhiyun
797*4882a593Smuzhiyun sparse_keymap_report_event(msi_laptop_input_dev,
798*4882a593Smuzhiyun (rdata & MSI_STANDARD_EC_TOUCHPAD_MASK) ?
799*4882a593Smuzhiyun KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF, 1, true);
800*4882a593Smuzhiyun }
801*4882a593Smuzhiyun static DECLARE_DELAYED_WORK(msi_touchpad_dwork, msi_send_touchpad_key);
802*4882a593Smuzhiyun static DECLARE_WORK(msi_touchpad_work, msi_send_touchpad_key);
803*4882a593Smuzhiyun
msi_laptop_i8042_filter(unsigned char data,unsigned char str,struct serio * port)804*4882a593Smuzhiyun static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
805*4882a593Smuzhiyun struct serio *port)
806*4882a593Smuzhiyun {
807*4882a593Smuzhiyun static bool extended;
808*4882a593Smuzhiyun
809*4882a593Smuzhiyun if (str & I8042_STR_AUXDATA)
810*4882a593Smuzhiyun return false;
811*4882a593Smuzhiyun
812*4882a593Smuzhiyun /* 0x54 wwan, 0x62 bluetooth, 0x76 wlan, 0xE4 touchpad toggle*/
813*4882a593Smuzhiyun if (unlikely(data == 0xe0)) {
814*4882a593Smuzhiyun extended = true;
815*4882a593Smuzhiyun return false;
816*4882a593Smuzhiyun } else if (unlikely(extended)) {
817*4882a593Smuzhiyun extended = false;
818*4882a593Smuzhiyun switch (data) {
819*4882a593Smuzhiyun case 0xE4:
820*4882a593Smuzhiyun if (quirks->ec_delay) {
821*4882a593Smuzhiyun schedule_delayed_work(&msi_touchpad_dwork,
822*4882a593Smuzhiyun round_jiffies_relative(0.5 * HZ));
823*4882a593Smuzhiyun } else
824*4882a593Smuzhiyun schedule_work(&msi_touchpad_work);
825*4882a593Smuzhiyun break;
826*4882a593Smuzhiyun case 0x54:
827*4882a593Smuzhiyun case 0x62:
828*4882a593Smuzhiyun case 0x76:
829*4882a593Smuzhiyun if (quirks->ec_delay) {
830*4882a593Smuzhiyun schedule_delayed_work(&msi_rfkill_dwork,
831*4882a593Smuzhiyun round_jiffies_relative(0.5 * HZ));
832*4882a593Smuzhiyun } else
833*4882a593Smuzhiyun schedule_work(&msi_rfkill_work);
834*4882a593Smuzhiyun break;
835*4882a593Smuzhiyun }
836*4882a593Smuzhiyun }
837*4882a593Smuzhiyun
838*4882a593Smuzhiyun return false;
839*4882a593Smuzhiyun }
840*4882a593Smuzhiyun
msi_init_rfkill(struct work_struct * ignored)841*4882a593Smuzhiyun static void msi_init_rfkill(struct work_struct *ignored)
842*4882a593Smuzhiyun {
843*4882a593Smuzhiyun if (rfk_wlan) {
844*4882a593Smuzhiyun rfkill_set_sw_state(rfk_wlan, !wlan_s);
845*4882a593Smuzhiyun rfkill_wlan_set(NULL, !wlan_s);
846*4882a593Smuzhiyun }
847*4882a593Smuzhiyun if (rfk_bluetooth) {
848*4882a593Smuzhiyun rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s);
849*4882a593Smuzhiyun rfkill_bluetooth_set(NULL, !bluetooth_s);
850*4882a593Smuzhiyun }
851*4882a593Smuzhiyun if (rfk_threeg) {
852*4882a593Smuzhiyun rfkill_set_sw_state(rfk_threeg, !threeg_s);
853*4882a593Smuzhiyun rfkill_threeg_set(NULL, !threeg_s);
854*4882a593Smuzhiyun }
855*4882a593Smuzhiyun }
856*4882a593Smuzhiyun static DECLARE_DELAYED_WORK(msi_rfkill_init, msi_init_rfkill);
857*4882a593Smuzhiyun
rfkill_init(struct platform_device * sdev)858*4882a593Smuzhiyun static int rfkill_init(struct platform_device *sdev)
859*4882a593Smuzhiyun {
860*4882a593Smuzhiyun /* add rfkill */
861*4882a593Smuzhiyun int retval;
862*4882a593Smuzhiyun
863*4882a593Smuzhiyun /* keep the hardware wireless state */
864*4882a593Smuzhiyun get_wireless_state_ec_standard();
865*4882a593Smuzhiyun
866*4882a593Smuzhiyun rfk_bluetooth = rfkill_alloc("msi-bluetooth", &sdev->dev,
867*4882a593Smuzhiyun RFKILL_TYPE_BLUETOOTH,
868*4882a593Smuzhiyun &rfkill_bluetooth_ops, NULL);
869*4882a593Smuzhiyun if (!rfk_bluetooth) {
870*4882a593Smuzhiyun retval = -ENOMEM;
871*4882a593Smuzhiyun goto err_bluetooth;
872*4882a593Smuzhiyun }
873*4882a593Smuzhiyun retval = rfkill_register(rfk_bluetooth);
874*4882a593Smuzhiyun if (retval)
875*4882a593Smuzhiyun goto err_bluetooth;
876*4882a593Smuzhiyun
877*4882a593Smuzhiyun rfk_wlan = rfkill_alloc("msi-wlan", &sdev->dev, RFKILL_TYPE_WLAN,
878*4882a593Smuzhiyun &rfkill_wlan_ops, NULL);
879*4882a593Smuzhiyun if (!rfk_wlan) {
880*4882a593Smuzhiyun retval = -ENOMEM;
881*4882a593Smuzhiyun goto err_wlan;
882*4882a593Smuzhiyun }
883*4882a593Smuzhiyun retval = rfkill_register(rfk_wlan);
884*4882a593Smuzhiyun if (retval)
885*4882a593Smuzhiyun goto err_wlan;
886*4882a593Smuzhiyun
887*4882a593Smuzhiyun if (threeg_exists) {
888*4882a593Smuzhiyun rfk_threeg = rfkill_alloc("msi-threeg", &sdev->dev,
889*4882a593Smuzhiyun RFKILL_TYPE_WWAN, &rfkill_threeg_ops, NULL);
890*4882a593Smuzhiyun if (!rfk_threeg) {
891*4882a593Smuzhiyun retval = -ENOMEM;
892*4882a593Smuzhiyun goto err_threeg;
893*4882a593Smuzhiyun }
894*4882a593Smuzhiyun retval = rfkill_register(rfk_threeg);
895*4882a593Smuzhiyun if (retval)
896*4882a593Smuzhiyun goto err_threeg;
897*4882a593Smuzhiyun }
898*4882a593Smuzhiyun
899*4882a593Smuzhiyun /* schedule to run rfkill state initial */
900*4882a593Smuzhiyun if (quirks->ec_delay) {
901*4882a593Smuzhiyun schedule_delayed_work(&msi_rfkill_init,
902*4882a593Smuzhiyun round_jiffies_relative(1 * HZ));
903*4882a593Smuzhiyun } else
904*4882a593Smuzhiyun schedule_work(&msi_rfkill_work);
905*4882a593Smuzhiyun
906*4882a593Smuzhiyun return 0;
907*4882a593Smuzhiyun
908*4882a593Smuzhiyun err_threeg:
909*4882a593Smuzhiyun rfkill_destroy(rfk_threeg);
910*4882a593Smuzhiyun if (rfk_wlan)
911*4882a593Smuzhiyun rfkill_unregister(rfk_wlan);
912*4882a593Smuzhiyun err_wlan:
913*4882a593Smuzhiyun rfkill_destroy(rfk_wlan);
914*4882a593Smuzhiyun if (rfk_bluetooth)
915*4882a593Smuzhiyun rfkill_unregister(rfk_bluetooth);
916*4882a593Smuzhiyun err_bluetooth:
917*4882a593Smuzhiyun rfkill_destroy(rfk_bluetooth);
918*4882a593Smuzhiyun
919*4882a593Smuzhiyun return retval;
920*4882a593Smuzhiyun }
921*4882a593Smuzhiyun
922*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
msi_laptop_resume(struct device * device)923*4882a593Smuzhiyun static int msi_laptop_resume(struct device *device)
924*4882a593Smuzhiyun {
925*4882a593Smuzhiyun u8 data;
926*4882a593Smuzhiyun int result;
927*4882a593Smuzhiyun
928*4882a593Smuzhiyun if (!quirks->load_scm_model)
929*4882a593Smuzhiyun return 0;
930*4882a593Smuzhiyun
931*4882a593Smuzhiyun /* set load SCM to disable hardware control by fn key */
932*4882a593Smuzhiyun result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data);
933*4882a593Smuzhiyun if (result < 0)
934*4882a593Smuzhiyun return result;
935*4882a593Smuzhiyun
936*4882a593Smuzhiyun result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS,
937*4882a593Smuzhiyun data | MSI_STANDARD_EC_SCM_LOAD_MASK);
938*4882a593Smuzhiyun if (result < 0)
939*4882a593Smuzhiyun return result;
940*4882a593Smuzhiyun
941*4882a593Smuzhiyun return 0;
942*4882a593Smuzhiyun }
943*4882a593Smuzhiyun #endif
944*4882a593Smuzhiyun
msi_laptop_input_setup(void)945*4882a593Smuzhiyun static int __init msi_laptop_input_setup(void)
946*4882a593Smuzhiyun {
947*4882a593Smuzhiyun int err;
948*4882a593Smuzhiyun
949*4882a593Smuzhiyun msi_laptop_input_dev = input_allocate_device();
950*4882a593Smuzhiyun if (!msi_laptop_input_dev)
951*4882a593Smuzhiyun return -ENOMEM;
952*4882a593Smuzhiyun
953*4882a593Smuzhiyun msi_laptop_input_dev->name = "MSI Laptop hotkeys";
954*4882a593Smuzhiyun msi_laptop_input_dev->phys = "msi-laptop/input0";
955*4882a593Smuzhiyun msi_laptop_input_dev->id.bustype = BUS_HOST;
956*4882a593Smuzhiyun
957*4882a593Smuzhiyun err = sparse_keymap_setup(msi_laptop_input_dev,
958*4882a593Smuzhiyun msi_laptop_keymap, NULL);
959*4882a593Smuzhiyun if (err)
960*4882a593Smuzhiyun goto err_free_dev;
961*4882a593Smuzhiyun
962*4882a593Smuzhiyun err = input_register_device(msi_laptop_input_dev);
963*4882a593Smuzhiyun if (err)
964*4882a593Smuzhiyun goto err_free_dev;
965*4882a593Smuzhiyun
966*4882a593Smuzhiyun return 0;
967*4882a593Smuzhiyun
968*4882a593Smuzhiyun err_free_dev:
969*4882a593Smuzhiyun input_free_device(msi_laptop_input_dev);
970*4882a593Smuzhiyun return err;
971*4882a593Smuzhiyun }
972*4882a593Smuzhiyun
load_scm_model_init(struct platform_device * sdev)973*4882a593Smuzhiyun static int __init load_scm_model_init(struct platform_device *sdev)
974*4882a593Smuzhiyun {
975*4882a593Smuzhiyun u8 data;
976*4882a593Smuzhiyun int result;
977*4882a593Smuzhiyun
978*4882a593Smuzhiyun if (!quirks->ec_read_only) {
979*4882a593Smuzhiyun /* allow userland write sysfs file */
980*4882a593Smuzhiyun dev_attr_bluetooth.store = store_bluetooth;
981*4882a593Smuzhiyun dev_attr_wlan.store = store_wlan;
982*4882a593Smuzhiyun dev_attr_threeg.store = store_threeg;
983*4882a593Smuzhiyun dev_attr_bluetooth.attr.mode |= S_IWUSR;
984*4882a593Smuzhiyun dev_attr_wlan.attr.mode |= S_IWUSR;
985*4882a593Smuzhiyun dev_attr_threeg.attr.mode |= S_IWUSR;
986*4882a593Smuzhiyun }
987*4882a593Smuzhiyun
988*4882a593Smuzhiyun /* disable hardware control by fn key */
989*4882a593Smuzhiyun result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data);
990*4882a593Smuzhiyun if (result < 0)
991*4882a593Smuzhiyun return result;
992*4882a593Smuzhiyun
993*4882a593Smuzhiyun result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS,
994*4882a593Smuzhiyun data | MSI_STANDARD_EC_SCM_LOAD_MASK);
995*4882a593Smuzhiyun if (result < 0)
996*4882a593Smuzhiyun return result;
997*4882a593Smuzhiyun
998*4882a593Smuzhiyun /* initial rfkill */
999*4882a593Smuzhiyun result = rfkill_init(sdev);
1000*4882a593Smuzhiyun if (result < 0)
1001*4882a593Smuzhiyun goto fail_rfkill;
1002*4882a593Smuzhiyun
1003*4882a593Smuzhiyun /* setup input device */
1004*4882a593Smuzhiyun result = msi_laptop_input_setup();
1005*4882a593Smuzhiyun if (result)
1006*4882a593Smuzhiyun goto fail_input;
1007*4882a593Smuzhiyun
1008*4882a593Smuzhiyun result = i8042_install_filter(msi_laptop_i8042_filter);
1009*4882a593Smuzhiyun if (result) {
1010*4882a593Smuzhiyun pr_err("Unable to install key filter\n");
1011*4882a593Smuzhiyun goto fail_filter;
1012*4882a593Smuzhiyun }
1013*4882a593Smuzhiyun
1014*4882a593Smuzhiyun return 0;
1015*4882a593Smuzhiyun
1016*4882a593Smuzhiyun fail_filter:
1017*4882a593Smuzhiyun input_unregister_device(msi_laptop_input_dev);
1018*4882a593Smuzhiyun
1019*4882a593Smuzhiyun fail_input:
1020*4882a593Smuzhiyun rfkill_cleanup();
1021*4882a593Smuzhiyun
1022*4882a593Smuzhiyun fail_rfkill:
1023*4882a593Smuzhiyun
1024*4882a593Smuzhiyun return result;
1025*4882a593Smuzhiyun
1026*4882a593Smuzhiyun }
1027*4882a593Smuzhiyun
msi_init(void)1028*4882a593Smuzhiyun static int __init msi_init(void)
1029*4882a593Smuzhiyun {
1030*4882a593Smuzhiyun int ret;
1031*4882a593Smuzhiyun
1032*4882a593Smuzhiyun if (acpi_disabled)
1033*4882a593Smuzhiyun return -ENODEV;
1034*4882a593Smuzhiyun
1035*4882a593Smuzhiyun dmi_check_system(msi_dmi_table);
1036*4882a593Smuzhiyun if (!quirks)
1037*4882a593Smuzhiyun /* quirks may be NULL if no match in DMI table */
1038*4882a593Smuzhiyun quirks = &quirk_load_scm_model;
1039*4882a593Smuzhiyun if (force)
1040*4882a593Smuzhiyun quirks = &quirk_old_ec_model;
1041*4882a593Smuzhiyun
1042*4882a593Smuzhiyun if (!quirks->old_ec_model)
1043*4882a593Smuzhiyun get_threeg_exists();
1044*4882a593Smuzhiyun
1045*4882a593Smuzhiyun if (auto_brightness < 0 || auto_brightness > 2)
1046*4882a593Smuzhiyun return -EINVAL;
1047*4882a593Smuzhiyun
1048*4882a593Smuzhiyun /* Register backlight stuff */
1049*4882a593Smuzhiyun if (quirks->old_ec_model &&
1050*4882a593Smuzhiyun acpi_video_get_backlight_type() == acpi_backlight_vendor) {
1051*4882a593Smuzhiyun struct backlight_properties props;
1052*4882a593Smuzhiyun memset(&props, 0, sizeof(struct backlight_properties));
1053*4882a593Smuzhiyun props.type = BACKLIGHT_PLATFORM;
1054*4882a593Smuzhiyun props.max_brightness = MSI_LCD_LEVEL_MAX - 1;
1055*4882a593Smuzhiyun msibl_device = backlight_device_register("msi-laptop-bl", NULL,
1056*4882a593Smuzhiyun NULL, &msibl_ops,
1057*4882a593Smuzhiyun &props);
1058*4882a593Smuzhiyun if (IS_ERR(msibl_device))
1059*4882a593Smuzhiyun return PTR_ERR(msibl_device);
1060*4882a593Smuzhiyun }
1061*4882a593Smuzhiyun
1062*4882a593Smuzhiyun ret = platform_driver_register(&msipf_driver);
1063*4882a593Smuzhiyun if (ret)
1064*4882a593Smuzhiyun goto fail_backlight;
1065*4882a593Smuzhiyun
1066*4882a593Smuzhiyun /* Register platform stuff */
1067*4882a593Smuzhiyun
1068*4882a593Smuzhiyun msipf_device = platform_device_alloc("msi-laptop-pf", -1);
1069*4882a593Smuzhiyun if (!msipf_device) {
1070*4882a593Smuzhiyun ret = -ENOMEM;
1071*4882a593Smuzhiyun goto fail_platform_driver;
1072*4882a593Smuzhiyun }
1073*4882a593Smuzhiyun
1074*4882a593Smuzhiyun ret = platform_device_add(msipf_device);
1075*4882a593Smuzhiyun if (ret)
1076*4882a593Smuzhiyun goto fail_device_add;
1077*4882a593Smuzhiyun
1078*4882a593Smuzhiyun if (quirks->load_scm_model && (load_scm_model_init(msipf_device) < 0)) {
1079*4882a593Smuzhiyun ret = -EINVAL;
1080*4882a593Smuzhiyun goto fail_scm_model_init;
1081*4882a593Smuzhiyun }
1082*4882a593Smuzhiyun
1083*4882a593Smuzhiyun ret = sysfs_create_group(&msipf_device->dev.kobj,
1084*4882a593Smuzhiyun &msipf_attribute_group);
1085*4882a593Smuzhiyun if (ret)
1086*4882a593Smuzhiyun goto fail_create_group;
1087*4882a593Smuzhiyun
1088*4882a593Smuzhiyun if (!quirks->old_ec_model) {
1089*4882a593Smuzhiyun if (threeg_exists)
1090*4882a593Smuzhiyun ret = device_create_file(&msipf_device->dev,
1091*4882a593Smuzhiyun &dev_attr_threeg);
1092*4882a593Smuzhiyun if (ret)
1093*4882a593Smuzhiyun goto fail_create_attr;
1094*4882a593Smuzhiyun } else {
1095*4882a593Smuzhiyun ret = sysfs_create_group(&msipf_device->dev.kobj,
1096*4882a593Smuzhiyun &msipf_old_attribute_group);
1097*4882a593Smuzhiyun if (ret)
1098*4882a593Smuzhiyun goto fail_create_attr;
1099*4882a593Smuzhiyun
1100*4882a593Smuzhiyun /* Disable automatic brightness control by default because
1101*4882a593Smuzhiyun * this module was probably loaded to do brightness control in
1102*4882a593Smuzhiyun * software. */
1103*4882a593Smuzhiyun
1104*4882a593Smuzhiyun if (auto_brightness != 2)
1105*4882a593Smuzhiyun set_auto_brightness(auto_brightness);
1106*4882a593Smuzhiyun }
1107*4882a593Smuzhiyun
1108*4882a593Smuzhiyun pr_info("driver " MSI_DRIVER_VERSION " successfully loaded\n");
1109*4882a593Smuzhiyun
1110*4882a593Smuzhiyun return 0;
1111*4882a593Smuzhiyun
1112*4882a593Smuzhiyun fail_create_attr:
1113*4882a593Smuzhiyun sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group);
1114*4882a593Smuzhiyun fail_create_group:
1115*4882a593Smuzhiyun if (quirks->load_scm_model) {
1116*4882a593Smuzhiyun i8042_remove_filter(msi_laptop_i8042_filter);
1117*4882a593Smuzhiyun cancel_delayed_work_sync(&msi_touchpad_dwork);
1118*4882a593Smuzhiyun input_unregister_device(msi_laptop_input_dev);
1119*4882a593Smuzhiyun cancel_delayed_work_sync(&msi_rfkill_dwork);
1120*4882a593Smuzhiyun cancel_work_sync(&msi_rfkill_work);
1121*4882a593Smuzhiyun rfkill_cleanup();
1122*4882a593Smuzhiyun }
1123*4882a593Smuzhiyun fail_scm_model_init:
1124*4882a593Smuzhiyun platform_device_del(msipf_device);
1125*4882a593Smuzhiyun fail_device_add:
1126*4882a593Smuzhiyun platform_device_put(msipf_device);
1127*4882a593Smuzhiyun fail_platform_driver:
1128*4882a593Smuzhiyun platform_driver_unregister(&msipf_driver);
1129*4882a593Smuzhiyun fail_backlight:
1130*4882a593Smuzhiyun backlight_device_unregister(msibl_device);
1131*4882a593Smuzhiyun
1132*4882a593Smuzhiyun return ret;
1133*4882a593Smuzhiyun }
1134*4882a593Smuzhiyun
msi_cleanup(void)1135*4882a593Smuzhiyun static void __exit msi_cleanup(void)
1136*4882a593Smuzhiyun {
1137*4882a593Smuzhiyun if (quirks->load_scm_model) {
1138*4882a593Smuzhiyun i8042_remove_filter(msi_laptop_i8042_filter);
1139*4882a593Smuzhiyun cancel_delayed_work_sync(&msi_touchpad_dwork);
1140*4882a593Smuzhiyun input_unregister_device(msi_laptop_input_dev);
1141*4882a593Smuzhiyun cancel_delayed_work_sync(&msi_rfkill_dwork);
1142*4882a593Smuzhiyun cancel_work_sync(&msi_rfkill_work);
1143*4882a593Smuzhiyun rfkill_cleanup();
1144*4882a593Smuzhiyun }
1145*4882a593Smuzhiyun
1146*4882a593Smuzhiyun sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group);
1147*4882a593Smuzhiyun if (!quirks->old_ec_model && threeg_exists)
1148*4882a593Smuzhiyun device_remove_file(&msipf_device->dev, &dev_attr_threeg);
1149*4882a593Smuzhiyun platform_device_unregister(msipf_device);
1150*4882a593Smuzhiyun platform_driver_unregister(&msipf_driver);
1151*4882a593Smuzhiyun backlight_device_unregister(msibl_device);
1152*4882a593Smuzhiyun
1153*4882a593Smuzhiyun if (quirks->old_ec_model) {
1154*4882a593Smuzhiyun /* Enable automatic brightness control again */
1155*4882a593Smuzhiyun if (auto_brightness != 2)
1156*4882a593Smuzhiyun set_auto_brightness(1);
1157*4882a593Smuzhiyun }
1158*4882a593Smuzhiyun
1159*4882a593Smuzhiyun pr_info("driver unloaded\n");
1160*4882a593Smuzhiyun }
1161*4882a593Smuzhiyun
1162*4882a593Smuzhiyun module_init(msi_init);
1163*4882a593Smuzhiyun module_exit(msi_cleanup);
1164*4882a593Smuzhiyun
1165*4882a593Smuzhiyun MODULE_AUTHOR("Lennart Poettering");
1166*4882a593Smuzhiyun MODULE_DESCRIPTION("MSI Laptop Support");
1167*4882a593Smuzhiyun MODULE_VERSION(MSI_DRIVER_VERSION);
1168*4882a593Smuzhiyun MODULE_LICENSE("GPL");
1169*4882a593Smuzhiyun
1170*4882a593Smuzhiyun MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
1171*4882a593Smuzhiyun MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*");
1172*4882a593Smuzhiyun MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
1173*4882a593Smuzhiyun MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
1174*4882a593Smuzhiyun MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N034:*");
1175*4882a593Smuzhiyun MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N051:*");
1176*4882a593Smuzhiyun MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*");
1177*4882a593Smuzhiyun MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*");
1178*4882a593Smuzhiyun MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnU270series:*");
1179*4882a593Smuzhiyun MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnU90/U100:*");
1180