blob: 0bf94b5c27448da263cfee64c0757eb2c31a2aa3 [file] [log] [blame]
Lennart Poettering8c4c7312006-10-06 01:27:02 -04001/*-*-linux-c-*-*/
2
3/*
4 Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301, USA.
20 */
21
22/*
23 * msi-laptop.c - MSI S270 laptop support. This laptop is sold under
24 * various brands, including "Cytron/TCM/Medion/Tchibo MD96100".
25 *
Lennart Poettering4f5c7912007-05-08 22:07:02 +020026 * Driver also supports S271, S420 models.
27 *
Lennart Poettering8c4c7312006-10-06 01:27:02 -040028 * This driver exports a few files in /sys/devices/platform/msi-laptop-pf/:
29 *
30 * lcd_level - Screen brightness: contains a single integer in the
31 * range 0..8. (rw)
32 *
33 * auto_brightness - Enable automatic brightness control: contains
34 * either 0 or 1. If set to 1 the hardware adjusts the screen
35 * brightness automatically when the power cord is
36 * plugged/unplugged. (rw)
37 *
38 * wlan - WLAN subsystem enabled: contains either 0 or 1. (ro)
39 *
40 * bluetooth - Bluetooth subsystem enabled: contains either 0 or 1
41 * Please note that this file is constantly 0 if no Bluetooth
42 * hardware is available. (ro)
43 *
44 * In addition to these platform device attributes the driver
45 * registers itself in the Linux backlight control subsystem and is
46 * available to userspace under /sys/class/backlight/msi-laptop-bl/.
47 *
48 * This driver might work on other laptops produced by MSI. If you
49 * want to try it you can pass force=1 as argument to the module which
50 * will force it to load even when the DMI data doesn't identify the
51 * laptop as MSI S270. YMMV.
52 */
53
Joey Leebbe24fe2011-03-16 01:55:19 -060054#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
55
Lennart Poettering8c4c7312006-10-06 01:27:02 -040056#include <linux/module.h>
57#include <linux/kernel.h>
58#include <linux/init.h>
59#include <linux/acpi.h>
60#include <linux/dmi.h>
61#include <linux/backlight.h>
62#include <linux/platform_device.h>
Lee, Chun-Yi472ea122010-01-22 00:15:59 +080063#include <linux/rfkill.h>
Lee, Chun-Yi339e7532010-05-12 09:58:10 -070064#include <linux/i8042.h>
Lee, Chun-Yi143a4c02011-03-07 15:46:28 +080065#include <linux/input.h>
66#include <linux/input/sparse-keymap.h>
Lennart Poettering8c4c7312006-10-06 01:27:02 -040067
68#define MSI_DRIVER_VERSION "0.5"
69
70#define MSI_LCD_LEVEL_MAX 9
71
72#define MSI_EC_COMMAND_WIRELESS 0x10
73#define MSI_EC_COMMAND_LCD_LEVEL 0x11
74
Lee, Chun-Yi46d0e9e2010-01-09 21:16:52 +080075#define MSI_STANDARD_EC_COMMAND_ADDRESS 0x2e
76#define MSI_STANDARD_EC_BLUETOOTH_MASK (1 << 0)
77#define MSI_STANDARD_EC_WEBCAM_MASK (1 << 1)
78#define MSI_STANDARD_EC_WLAN_MASK (1 << 3)
Lee, Chun-Yifc0dc4c2010-01-09 23:17:07 +080079#define MSI_STANDARD_EC_3G_MASK (1 << 4)
Lee, Chun-Yi46d0e9e2010-01-09 21:16:52 +080080
Lee, Chun-Yi472ea122010-01-22 00:15:59 +080081/* For set SCM load flag to disable BIOS fn key */
82#define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d
83#define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0)
84
Lee, Chun-Yi143a4c02011-03-07 15:46:28 +080085#define MSI_STANDARD_EC_TOUCHPAD_ADDRESS 0xe4
86#define MSI_STANDARD_EC_TOUCHPAD_MASK (1 << 4)
87
Rafael J. Wysocki3567a4e2012-08-09 23:00:13 +020088#ifdef CONFIG_PM_SLEEP
Rafael J. Wysocki90331322012-07-06 19:06:19 +020089static int msi_laptop_resume(struct device *device);
Rafael J. Wysocki3567a4e2012-08-09 23:00:13 +020090#endif
Rafael J. Wysocki90331322012-07-06 19:06:19 +020091static SIMPLE_DEV_PM_OPS(msi_laptop_pm, NULL, msi_laptop_resume);
Lee, Chun-Yiec766272010-01-27 00:13:45 +080092
Lee, Chun-Yie22388e2010-01-27 12:23:00 +080093#define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f
94
Rusty Russell90ab5ee2012-01-13 09:32:20 +103095static bool force;
Lennart Poettering8c4c7312006-10-06 01:27:02 -040096module_param(force, bool, 0);
97MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
98
99static int auto_brightness;
100module_param(auto_brightness, int, 0);
101MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)");
102
Lee, Chun-Yi143a4c02011-03-07 15:46:28 +0800103static const struct key_entry msi_laptop_keymap[] = {
104 {KE_KEY, KEY_TOUCHPAD_ON, {KEY_TOUCHPAD_ON} }, /* Touch Pad On */
105 {KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} },/* Touch Pad On */
106 {KE_END, 0}
107};
108
109static struct input_dev *msi_laptop_input_dev;
110
Lee, Chun-Yifc0dc4c2010-01-09 23:17:07 +0800111static int wlan_s, bluetooth_s, threeg_s;
Lee, Chun-Yie22388e2010-01-27 12:23:00 +0800112static int threeg_exists;
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800113static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg;
114
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200115/* MSI laptop quirks */
116struct quirk_entry {
117 bool old_ec_model;
118
119 /* Some MSI 3G netbook only have one fn key to control
120 * Wlan/Bluetooth/3G, those netbook will load the SCM (windows app) to
121 * disable the original Wlan/Bluetooth control by BIOS when user press
122 * fn key, then control Wlan/Bluetooth/3G by SCM (software control by
123 * OS). Without SCM, user cann't on/off 3G module on those 3G netbook.
124 * On Linux, msi-laptop driver will do the same thing to disable the
125 * original BIOS control, then might need use HAL or other userland
126 * application to do the software control that simulate with SCM.
127 * e.g. MSI N034 netbook
128 */
129 bool load_scm_model;
130
131 /* Some MSI laptops need delay before reading from EC */
132 bool ec_delay;
133
134 /* Some MSI Wind netbooks (e.g. MSI Wind U100) need loading SCM to get
135 * some features working (e.g. ECO mode), but we cannot change
136 * Wlan/Bluetooth state in software and we can only read its state.
137 */
138 bool ec_read_only;
139};
140
141static struct quirk_entry *quirks;
142
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400143/* Hardware access */
144
145static int set_lcd_level(int level)
146{
147 u8 buf[2];
148
149 if (level < 0 || level >= MSI_LCD_LEVEL_MAX)
150 return -EINVAL;
151
152 buf[0] = 0x80;
153 buf[1] = (u8) (level*31);
154
Greg Kroah-Hartman1ac34072010-05-12 12:03:00 -0700155 return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf),
Thomas Renninger1cb7b1e2011-03-31 13:36:38 +0200156 NULL, 0);
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400157}
158
159static int get_lcd_level(void)
160{
161 u8 wdata = 0, rdata;
162 int result;
163
Greg Kroah-Hartman1ac34072010-05-12 12:03:00 -0700164 result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1,
Thomas Renninger1cb7b1e2011-03-31 13:36:38 +0200165 &rdata, 1);
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400166 if (result < 0)
167 return result;
168
169 return (int) rdata / 31;
170}
171
172static int get_auto_brightness(void)
173{
174 u8 wdata = 4, rdata;
175 int result;
176
Greg Kroah-Hartman1ac34072010-05-12 12:03:00 -0700177 result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1,
Thomas Renninger1cb7b1e2011-03-31 13:36:38 +0200178 &rdata, 1);
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400179 if (result < 0)
180 return result;
181
182 return !!(rdata & 8);
183}
184
185static int set_auto_brightness(int enable)
186{
187 u8 wdata[2], rdata;
188 int result;
189
190 wdata[0] = 4;
191
Greg Kroah-Hartman1ac34072010-05-12 12:03:00 -0700192 result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1,
Thomas Renninger1cb7b1e2011-03-31 13:36:38 +0200193 &rdata, 1);
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400194 if (result < 0)
195 return result;
196
197 wdata[0] = 0x84;
198 wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0);
199
Greg Kroah-Hartman1ac34072010-05-12 12:03:00 -0700200 return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2,
Thomas Renninger1cb7b1e2011-03-31 13:36:38 +0200201 NULL, 0);
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400202}
203
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800204static ssize_t set_device_state(const char *buf, size_t count, u8 mask)
205{
206 int status;
207 u8 wdata = 0, rdata;
208 int result;
209
210 if (sscanf(buf, "%i", &status) != 1 || (status < 0 || status > 1))
211 return -EINVAL;
212
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200213 if (quirks->ec_read_only)
214 return -EOPNOTSUPP;
215
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800216 /* read current device state */
217 result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
218 if (result < 0)
Maxim Mikityanskiy27eb9e72012-12-15 19:31:25 +0200219 return result;
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800220
221 if (!!(rdata & mask) != status) {
222 /* reverse device bit */
223 if (rdata & mask)
224 wdata = rdata & ~mask;
225 else
226 wdata = rdata | mask;
227
228 result = ec_write(MSI_STANDARD_EC_COMMAND_ADDRESS, wdata);
229 if (result < 0)
Maxim Mikityanskiy27eb9e72012-12-15 19:31:25 +0200230 return result;
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800231 }
232
233 return count;
234}
235
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400236static int get_wireless_state(int *wlan, int *bluetooth)
237{
238 u8 wdata = 0, rdata;
239 int result;
240
Thomas Renninger1cb7b1e2011-03-31 13:36:38 +0200241 result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1);
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400242 if (result < 0)
Maxim Mikityanskiy27eb9e72012-12-15 19:31:25 +0200243 return result;
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400244
245 if (wlan)
246 *wlan = !!(rdata & 8);
247
248 if (bluetooth)
249 *bluetooth = !!(rdata & 128);
250
251 return 0;
252}
253
Lee, Chun-Yi46d0e9e2010-01-09 21:16:52 +0800254static int get_wireless_state_ec_standard(void)
255{
256 u8 rdata;
257 int result;
258
259 result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
260 if (result < 0)
Maxim Mikityanskiy27eb9e72012-12-15 19:31:25 +0200261 return result;
Lee, Chun-Yi46d0e9e2010-01-09 21:16:52 +0800262
263 wlan_s = !!(rdata & MSI_STANDARD_EC_WLAN_MASK);
264
265 bluetooth_s = !!(rdata & MSI_STANDARD_EC_BLUETOOTH_MASK);
266
Lee, Chun-Yifc0dc4c2010-01-09 23:17:07 +0800267 threeg_s = !!(rdata & MSI_STANDARD_EC_3G_MASK);
268
Lee, Chun-Yi46d0e9e2010-01-09 21:16:52 +0800269 return 0;
270}
271
Lee, Chun-Yie22388e2010-01-27 12:23:00 +0800272static int get_threeg_exists(void)
273{
274 u8 rdata;
275 int result;
276
277 result = ec_read(MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS, &rdata);
278 if (result < 0)
Maxim Mikityanskiy27eb9e72012-12-15 19:31:25 +0200279 return result;
Lee, Chun-Yie22388e2010-01-27 12:23:00 +0800280
281 threeg_exists = !!(rdata & MSI_STANDARD_EC_3G_MASK);
282
283 return 0;
284}
285
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400286/* Backlight device stuff */
287
288static int bl_get_brightness(struct backlight_device *b)
289{
290 return get_lcd_level();
291}
292
293
294static int bl_update_status(struct backlight_device *b)
295{
Richard Purdie599a52d2007-02-10 23:07:48 +0000296 return set_lcd_level(b->props.brightness);
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400297}
298
Greg Kroah-Hartman1ac34072010-05-12 12:03:00 -0700299static const struct backlight_ops msibl_ops = {
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400300 .get_brightness = bl_get_brightness,
301 .update_status = bl_update_status,
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400302};
303
304static struct backlight_device *msibl_device;
305
306/* Platform device */
307
308static ssize_t show_wlan(struct device *dev,
309 struct device_attribute *attr, char *buf)
310{
311
Maxim Mikityanskiy1b6517a2012-12-15 19:31:26 +0200312 int ret, enabled = 0;
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400313
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200314 if (quirks->old_ec_model) {
Lee, Chun-Yi46d0e9e2010-01-09 21:16:52 +0800315 ret = get_wireless_state(&enabled, NULL);
316 } else {
317 ret = get_wireless_state_ec_standard();
318 enabled = wlan_s;
319 }
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400320 if (ret < 0)
321 return ret;
322
323 return sprintf(buf, "%i\n", enabled);
324}
325
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800326static ssize_t store_wlan(struct device *dev,
327 struct device_attribute *attr, const char *buf, size_t count)
328{
329 return set_device_state(buf, count, MSI_STANDARD_EC_WLAN_MASK);
330}
331
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400332static ssize_t show_bluetooth(struct device *dev,
333 struct device_attribute *attr, char *buf)
334{
335
Maxim Mikityanskiy1b6517a2012-12-15 19:31:26 +0200336 int ret, enabled = 0;
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400337
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200338 if (quirks->old_ec_model) {
Lee, Chun-Yi46d0e9e2010-01-09 21:16:52 +0800339 ret = get_wireless_state(NULL, &enabled);
340 } else {
341 ret = get_wireless_state_ec_standard();
342 enabled = bluetooth_s;
343 }
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400344 if (ret < 0)
345 return ret;
346
347 return sprintf(buf, "%i\n", enabled);
348}
349
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800350static ssize_t store_bluetooth(struct device *dev,
351 struct device_attribute *attr, const char *buf, size_t count)
352{
353 return set_device_state(buf, count, MSI_STANDARD_EC_BLUETOOTH_MASK);
354}
355
Lee, Chun-Yifc0dc4c2010-01-09 23:17:07 +0800356static ssize_t show_threeg(struct device *dev,
357 struct device_attribute *attr, char *buf)
358{
359
360 int ret;
361
362 /* old msi ec not support 3G */
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200363 if (quirks->old_ec_model)
Maxim Mikityanskiy27eb9e72012-12-15 19:31:25 +0200364 return -ENODEV;
Lee, Chun-Yifc0dc4c2010-01-09 23:17:07 +0800365
366 ret = get_wireless_state_ec_standard();
367 if (ret < 0)
368 return ret;
369
370 return sprintf(buf, "%i\n", threeg_s);
371}
372
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800373static ssize_t store_threeg(struct device *dev,
374 struct device_attribute *attr, const char *buf, size_t count)
375{
376 return set_device_state(buf, count, MSI_STANDARD_EC_3G_MASK);
377}
378
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400379static ssize_t show_lcd_level(struct device *dev,
380 struct device_attribute *attr, char *buf)
381{
382
383 int ret;
384
385 ret = get_lcd_level();
386 if (ret < 0)
387 return ret;
388
389 return sprintf(buf, "%i\n", ret);
390}
391
392static ssize_t store_lcd_level(struct device *dev,
393 struct device_attribute *attr, const char *buf, size_t count)
394{
395
396 int level, ret;
397
Greg Kroah-Hartman1ac34072010-05-12 12:03:00 -0700398 if (sscanf(buf, "%i", &level) != 1 ||
399 (level < 0 || level >= MSI_LCD_LEVEL_MAX))
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400400 return -EINVAL;
401
402 ret = set_lcd_level(level);
403 if (ret < 0)
404 return ret;
405
406 return count;
407}
408
409static ssize_t show_auto_brightness(struct device *dev,
410 struct device_attribute *attr, char *buf)
411{
412
413 int ret;
414
415 ret = get_auto_brightness();
416 if (ret < 0)
417 return ret;
418
419 return sprintf(buf, "%i\n", ret);
420}
421
422static ssize_t store_auto_brightness(struct device *dev,
423 struct device_attribute *attr, const char *buf, size_t count)
424{
425
426 int enable, ret;
427
428 if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1)))
429 return -EINVAL;
430
431 ret = set_auto_brightness(enable);
432 if (ret < 0)
433 return ret;
434
435 return count;
436}
437
438static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
Greg Kroah-Hartman1ac34072010-05-12 12:03:00 -0700439static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness,
440 store_auto_brightness);
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400441static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL);
442static DEVICE_ATTR(wlan, 0444, show_wlan, NULL);
Lee, Chun-Yifc0dc4c2010-01-09 23:17:07 +0800443static DEVICE_ATTR(threeg, 0444, show_threeg, NULL);
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400444
445static struct attribute *msipf_attributes[] = {
446 &dev_attr_lcd_level.attr,
447 &dev_attr_auto_brightness.attr,
448 &dev_attr_bluetooth.attr,
449 &dev_attr_wlan.attr,
450 NULL
451};
452
453static struct attribute_group msipf_attribute_group = {
454 .attrs = msipf_attributes
455};
456
457static struct platform_driver msipf_driver = {
458 .driver = {
459 .name = "msi-laptop-pf",
460 .owner = THIS_MODULE,
Rafael J. Wysocki90331322012-07-06 19:06:19 +0200461 .pm = &msi_laptop_pm,
Lee, Chun-Yiec766272010-01-27 00:13:45 +0800462 },
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400463};
464
465static struct platform_device *msipf_device;
466
467/* Initialization */
468
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200469static struct quirk_entry quirk_old_ec_model = {
470 .old_ec_model = true,
471};
472
473static struct quirk_entry quirk_load_scm_model = {
474 .load_scm_model = true,
475 .ec_delay = true,
476};
477
478static struct quirk_entry quirk_load_scm_ro_model = {
479 .load_scm_model = true,
480 .ec_read_only = true,
481};
482
483static int dmi_check_cb(const struct dmi_system_id *dmi)
Lennart Poettering4f5c7912007-05-08 22:07:02 +0200484{
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200485 pr_info("Identified laptop model '%s'\n", dmi->ident);
486
487 quirks = dmi->driver_data;
488
Axel Lin80183a42010-07-20 15:19:40 -0700489 return 1;
Lennart Poettering4f5c7912007-05-08 22:07:02 +0200490}
491
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400492static struct dmi_system_id __initdata msi_dmi_table[] = {
493 {
494 .ident = "MSI S270",
495 .matches = {
496 DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"),
497 DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"),
Lennart Poettering4f5c7912007-05-08 22:07:02 +0200498 DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
Greg Kroah-Hartman1ac34072010-05-12 12:03:00 -0700499 DMI_MATCH(DMI_CHASSIS_VENDOR,
500 "MICRO-STAR INT'L CO.,LTD")
Lennart Poettering4f5c7912007-05-08 22:07:02 +0200501 },
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200502 .driver_data = &quirk_old_ec_model,
Lennart Poettering4f5c7912007-05-08 22:07:02 +0200503 .callback = dmi_check_cb
504 },
505 {
506 .ident = "MSI S271",
507 .matches = {
508 DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
509 DMI_MATCH(DMI_PRODUCT_NAME, "MS-1058"),
510 DMI_MATCH(DMI_PRODUCT_VERSION, "0581"),
511 DMI_MATCH(DMI_BOARD_NAME, "MS-1058")
512 },
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200513 .driver_data = &quirk_old_ec_model,
Lennart Poettering4f5c7912007-05-08 22:07:02 +0200514 .callback = dmi_check_cb
515 },
516 {
517 .ident = "MSI S420",
518 .matches = {
519 DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
520 DMI_MATCH(DMI_PRODUCT_NAME, "MS-1412"),
521 DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
522 DMI_MATCH(DMI_BOARD_NAME, "MS-1412")
523 },
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200524 .driver_data = &quirk_old_ec_model,
Lennart Poettering4f5c7912007-05-08 22:07:02 +0200525 .callback = dmi_check_cb
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400526 },
527 {
528 .ident = "Medion MD96100",
529 .matches = {
530 DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"),
531 DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"),
Lennart Poettering4f5c7912007-05-08 22:07:02 +0200532 DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
Greg Kroah-Hartman1ac34072010-05-12 12:03:00 -0700533 DMI_MATCH(DMI_CHASSIS_VENDOR,
534 "MICRO-STAR INT'L CO.,LTD")
Lennart Poettering4f5c7912007-05-08 22:07:02 +0200535 },
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200536 .driver_data = &quirk_old_ec_model,
Lennart Poettering4f5c7912007-05-08 22:07:02 +0200537 .callback = dmi_check_cb
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400538 },
Lee, Chun-Yi785cfc02010-05-19 20:03:05 +0800539 {
540 .ident = "MSI N034",
541 .matches = {
542 DMI_MATCH(DMI_SYS_VENDOR,
543 "MICRO-STAR INTERNATIONAL CO., LTD"),
544 DMI_MATCH(DMI_PRODUCT_NAME, "MS-N034"),
545 DMI_MATCH(DMI_CHASSIS_VENDOR,
546 "MICRO-STAR INTERNATIONAL CO., LTD")
547 },
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200548 .driver_data = &quirk_load_scm_model,
Lee, Chun-Yi785cfc02010-05-19 20:03:05 +0800549 .callback = dmi_check_cb
550 },
Lee, Chun-Yid0a4aa22010-05-12 09:58:07 -0700551 {
552 .ident = "MSI N051",
553 .matches = {
554 DMI_MATCH(DMI_SYS_VENDOR,
555 "MICRO-STAR INTERNATIONAL CO., LTD"),
556 DMI_MATCH(DMI_PRODUCT_NAME, "MS-N051"),
557 DMI_MATCH(DMI_CHASSIS_VENDOR,
558 "MICRO-STAR INTERNATIONAL CO., LTD")
559 },
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200560 .driver_data = &quirk_load_scm_model,
Lee, Chun-Yid0a4aa22010-05-12 09:58:07 -0700561 .callback = dmi_check_cb
562 },
563 {
564 .ident = "MSI N014",
565 .matches = {
566 DMI_MATCH(DMI_SYS_VENDOR,
567 "MICRO-STAR INTERNATIONAL CO., LTD"),
568 DMI_MATCH(DMI_PRODUCT_NAME, "MS-N014"),
569 },
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200570 .driver_data = &quirk_load_scm_model,
Lee, Chun-Yid0a4aa22010-05-12 09:58:07 -0700571 .callback = dmi_check_cb
572 },
Lee, Chun-Yi1f27e172010-05-12 09:58:08 -0700573 {
574 .ident = "MSI CR620",
575 .matches = {
576 DMI_MATCH(DMI_SYS_VENDOR,
577 "Micro-Star International"),
578 DMI_MATCH(DMI_PRODUCT_NAME, "CR620"),
579 },
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200580 .driver_data = &quirk_load_scm_model,
Lee, Chun-Yi1f27e172010-05-12 09:58:08 -0700581 .callback = dmi_check_cb
582 },
Lee, Chun-Yi38803142011-06-10 15:24:26 +0800583 {
584 .ident = "MSI U270",
585 .matches = {
586 DMI_MATCH(DMI_SYS_VENDOR,
587 "Micro-Star International Co., Ltd."),
588 DMI_MATCH(DMI_PRODUCT_NAME, "U270 series"),
589 },
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200590 .driver_data = &quirk_load_scm_model,
Lee, Chun-Yi38803142011-06-10 15:24:26 +0800591 .callback = dmi_check_cb
592 },
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400593 { }
594};
595
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800596static int rfkill_bluetooth_set(void *data, bool blocked)
597{
598 /* Do something with blocked...*/
599 /*
600 * blocked == false is on
601 * blocked == true is off
602 */
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200603 int result = set_device_state(blocked ? "0" : "1", 0,
604 MSI_STANDARD_EC_BLUETOOTH_MASK);
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800605
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200606 return min(result, 0);
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800607}
608
609static int rfkill_wlan_set(void *data, bool blocked)
610{
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200611 int result = set_device_state(blocked ? "0" : "1", 0,
612 MSI_STANDARD_EC_WLAN_MASK);
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800613
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200614 return min(result, 0);
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800615}
616
617static int rfkill_threeg_set(void *data, bool blocked)
618{
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200619 int result = set_device_state(blocked ? "0" : "1", 0,
620 MSI_STANDARD_EC_3G_MASK);
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800621
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200622 return min(result, 0);
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800623}
624
Axel Line38f0522010-07-20 15:19:46 -0700625static const struct rfkill_ops rfkill_bluetooth_ops = {
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800626 .set_block = rfkill_bluetooth_set
627};
628
Axel Line38f0522010-07-20 15:19:46 -0700629static const struct rfkill_ops rfkill_wlan_ops = {
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800630 .set_block = rfkill_wlan_set
631};
632
Axel Line38f0522010-07-20 15:19:46 -0700633static const struct rfkill_ops rfkill_threeg_ops = {
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800634 .set_block = rfkill_threeg_set
635};
636
637static void rfkill_cleanup(void)
638{
639 if (rfk_bluetooth) {
640 rfkill_unregister(rfk_bluetooth);
641 rfkill_destroy(rfk_bluetooth);
642 }
643
644 if (rfk_threeg) {
645 rfkill_unregister(rfk_threeg);
646 rfkill_destroy(rfk_threeg);
647 }
648
649 if (rfk_wlan) {
650 rfkill_unregister(rfk_wlan);
651 rfkill_destroy(rfk_wlan);
652 }
653}
654
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200655static bool msi_rfkill_set_state(struct rfkill *rfkill, bool blocked)
656{
657 if (quirks->ec_read_only)
658 return rfkill_set_hw_state(rfkill, blocked);
659 else
660 return rfkill_set_sw_state(rfkill, blocked);
661}
662
Lee, Chun-Yi339e7532010-05-12 09:58:10 -0700663static void msi_update_rfkill(struct work_struct *ignored)
664{
665 get_wireless_state_ec_standard();
666
667 if (rfk_wlan)
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200668 msi_rfkill_set_state(rfk_wlan, !wlan_s);
Lee, Chun-Yi339e7532010-05-12 09:58:10 -0700669 if (rfk_bluetooth)
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200670 msi_rfkill_set_state(rfk_bluetooth, !bluetooth_s);
Lee, Chun-Yi339e7532010-05-12 09:58:10 -0700671 if (rfk_threeg)
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200672 msi_rfkill_set_state(rfk_threeg, !threeg_s);
Lee, Chun-Yi339e7532010-05-12 09:58:10 -0700673}
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200674static DECLARE_DELAYED_WORK(msi_rfkill_dwork, msi_update_rfkill);
675static DECLARE_WORK(msi_rfkill_work, msi_update_rfkill);
Lee, Chun-Yi339e7532010-05-12 09:58:10 -0700676
Lee, Chun-Yi143a4c02011-03-07 15:46:28 +0800677static void msi_send_touchpad_key(struct work_struct *ignored)
678{
679 u8 rdata;
680 int result;
681
682 result = ec_read(MSI_STANDARD_EC_TOUCHPAD_ADDRESS, &rdata);
683 if (result < 0)
684 return;
685
686 sparse_keymap_report_event(msi_laptop_input_dev,
687 (rdata & MSI_STANDARD_EC_TOUCHPAD_MASK) ?
688 KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF, 1, true);
689}
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200690static DECLARE_DELAYED_WORK(msi_touchpad_dwork, msi_send_touchpad_key);
691static DECLARE_WORK(msi_touchpad_work, msi_send_touchpad_key);
Lee, Chun-Yi143a4c02011-03-07 15:46:28 +0800692
Lee, Chun-Yi339e7532010-05-12 09:58:10 -0700693static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
694 struct serio *port)
695{
696 static bool extended;
697
698 if (str & 0x20)
699 return false;
700
Lee, Chun-Yi143a4c02011-03-07 15:46:28 +0800701 /* 0x54 wwan, 0x62 bluetooth, 0x76 wlan, 0xE4 touchpad toggle*/
Lee, Chun-Yi339e7532010-05-12 09:58:10 -0700702 if (unlikely(data == 0xe0)) {
703 extended = true;
704 return false;
705 } else if (unlikely(extended)) {
Lee, Chun-Yi143a4c02011-03-07 15:46:28 +0800706 extended = false;
Lee, Chun-Yi339e7532010-05-12 09:58:10 -0700707 switch (data) {
Lee, Chun-Yi143a4c02011-03-07 15:46:28 +0800708 case 0xE4:
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200709 if (quirks->ec_delay) {
710 schedule_delayed_work(&msi_touchpad_dwork,
711 round_jiffies_relative(0.5 * HZ));
712 } else
713 schedule_work(&msi_touchpad_work);
Lee, Chun-Yi143a4c02011-03-07 15:46:28 +0800714 break;
Lee, Chun-Yi339e7532010-05-12 09:58:10 -0700715 case 0x54:
716 case 0x62:
717 case 0x76:
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200718 if (quirks->ec_delay) {
719 schedule_delayed_work(&msi_rfkill_dwork,
720 round_jiffies_relative(0.5 * HZ));
721 } else
722 schedule_work(&msi_rfkill_work);
Lee, Chun-Yi339e7532010-05-12 09:58:10 -0700723 break;
724 }
Lee, Chun-Yi339e7532010-05-12 09:58:10 -0700725 }
726
727 return false;
728}
729
Lee, Chun-Yi3bb97022010-05-12 09:58:09 -0700730static void msi_init_rfkill(struct work_struct *ignored)
731{
732 if (rfk_wlan) {
733 rfkill_set_sw_state(rfk_wlan, !wlan_s);
734 rfkill_wlan_set(NULL, !wlan_s);
735 }
736 if (rfk_bluetooth) {
737 rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s);
738 rfkill_bluetooth_set(NULL, !bluetooth_s);
739 }
740 if (rfk_threeg) {
741 rfkill_set_sw_state(rfk_threeg, !threeg_s);
742 rfkill_threeg_set(NULL, !threeg_s);
743 }
744}
745static DECLARE_DELAYED_WORK(msi_rfkill_init, msi_init_rfkill);
746
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800747static int rfkill_init(struct platform_device *sdev)
748{
749 /* add rfkill */
750 int retval;
751
Lee, Chun-Yi3bb97022010-05-12 09:58:09 -0700752 /* keep the hardware wireless state */
753 get_wireless_state_ec_standard();
754
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800755 rfk_bluetooth = rfkill_alloc("msi-bluetooth", &sdev->dev,
756 RFKILL_TYPE_BLUETOOTH,
757 &rfkill_bluetooth_ops, NULL);
758 if (!rfk_bluetooth) {
759 retval = -ENOMEM;
760 goto err_bluetooth;
761 }
762 retval = rfkill_register(rfk_bluetooth);
763 if (retval)
764 goto err_bluetooth;
765
766 rfk_wlan = rfkill_alloc("msi-wlan", &sdev->dev, RFKILL_TYPE_WLAN,
767 &rfkill_wlan_ops, NULL);
768 if (!rfk_wlan) {
769 retval = -ENOMEM;
770 goto err_wlan;
771 }
772 retval = rfkill_register(rfk_wlan);
773 if (retval)
774 goto err_wlan;
775
Lee, Chun-Yie22388e2010-01-27 12:23:00 +0800776 if (threeg_exists) {
777 rfk_threeg = rfkill_alloc("msi-threeg", &sdev->dev,
778 RFKILL_TYPE_WWAN, &rfkill_threeg_ops, NULL);
779 if (!rfk_threeg) {
780 retval = -ENOMEM;
781 goto err_threeg;
782 }
783 retval = rfkill_register(rfk_threeg);
784 if (retval)
785 goto err_threeg;
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800786 }
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800787
Lee, Chun-Yi3bb97022010-05-12 09:58:09 -0700788 /* schedule to run rfkill state initial */
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200789 if (quirks->ec_delay) {
790 schedule_delayed_work(&msi_rfkill_init,
791 round_jiffies_relative(1 * HZ));
792 } else
793 schedule_work(&msi_rfkill_work);
Lee, Chun-Yi3bb97022010-05-12 09:58:09 -0700794
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800795 return 0;
796
797err_threeg:
798 rfkill_destroy(rfk_threeg);
799 if (rfk_wlan)
800 rfkill_unregister(rfk_wlan);
801err_wlan:
802 rfkill_destroy(rfk_wlan);
803 if (rfk_bluetooth)
804 rfkill_unregister(rfk_bluetooth);
805err_bluetooth:
806 rfkill_destroy(rfk_bluetooth);
807
808 return retval;
809}
810
Rafael J. Wysocki3567a4e2012-08-09 23:00:13 +0200811#ifdef CONFIG_PM_SLEEP
Rafael J. Wysocki90331322012-07-06 19:06:19 +0200812static int msi_laptop_resume(struct device *device)
Lee, Chun-Yiec766272010-01-27 00:13:45 +0800813{
814 u8 data;
815 int result;
816
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200817 if (!quirks->load_scm_model)
Lee, Chun-Yiec766272010-01-27 00:13:45 +0800818 return 0;
819
820 /* set load SCM to disable hardware control by fn key */
821 result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data);
822 if (result < 0)
823 return result;
824
825 result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS,
826 data | MSI_STANDARD_EC_SCM_LOAD_MASK);
827 if (result < 0)
828 return result;
829
830 return 0;
831}
Rafael J. Wysocki3567a4e2012-08-09 23:00:13 +0200832#endif
Lee, Chun-Yiec766272010-01-27 00:13:45 +0800833
Lee, Chun-Yi143a4c02011-03-07 15:46:28 +0800834static int __init msi_laptop_input_setup(void)
835{
836 int err;
837
838 msi_laptop_input_dev = input_allocate_device();
839 if (!msi_laptop_input_dev)
840 return -ENOMEM;
841
842 msi_laptop_input_dev->name = "MSI Laptop hotkeys";
843 msi_laptop_input_dev->phys = "msi-laptop/input0";
844 msi_laptop_input_dev->id.bustype = BUS_HOST;
845
846 err = sparse_keymap_setup(msi_laptop_input_dev,
847 msi_laptop_keymap, NULL);
848 if (err)
849 goto err_free_dev;
850
851 err = input_register_device(msi_laptop_input_dev);
852 if (err)
853 goto err_free_keymap;
854
855 return 0;
856
857err_free_keymap:
858 sparse_keymap_free(msi_laptop_input_dev);
859err_free_dev:
860 input_free_device(msi_laptop_input_dev);
861 return err;
862}
863
864static void msi_laptop_input_destroy(void)
865{
866 sparse_keymap_free(msi_laptop_input_dev);
867 input_unregister_device(msi_laptop_input_dev);
868}
869
Lee, Chun-Yid4365142011-05-26 11:09:19 +0800870static int __init load_scm_model_init(struct platform_device *sdev)
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800871{
872 u8 data;
873 int result;
874
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200875 if (!quirks->ec_read_only) {
876 /* allow userland write sysfs file */
877 dev_attr_bluetooth.store = store_bluetooth;
878 dev_attr_wlan.store = store_wlan;
879 dev_attr_threeg.store = store_threeg;
880 dev_attr_bluetooth.attr.mode |= S_IWUSR;
881 dev_attr_wlan.attr.mode |= S_IWUSR;
882 dev_attr_threeg.attr.mode |= S_IWUSR;
883 }
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800884
885 /* disable hardware control by fn key */
886 result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data);
887 if (result < 0)
888 return result;
889
890 result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS,
891 data | MSI_STANDARD_EC_SCM_LOAD_MASK);
892 if (result < 0)
893 return result;
894
895 /* initial rfkill */
896 result = rfkill_init(sdev);
897 if (result < 0)
Lee, Chun-Yi339e7532010-05-12 09:58:10 -0700898 goto fail_rfkill;
899
Lee, Chun-Yi143a4c02011-03-07 15:46:28 +0800900 /* setup input device */
901 result = msi_laptop_input_setup();
902 if (result)
903 goto fail_input;
904
Lee, Chun-Yi339e7532010-05-12 09:58:10 -0700905 result = i8042_install_filter(msi_laptop_i8042_filter);
906 if (result) {
Joey Leebbe24fe2011-03-16 01:55:19 -0600907 pr_err("Unable to install key filter\n");
Lee, Chun-Yi339e7532010-05-12 09:58:10 -0700908 goto fail_filter;
909 }
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800910
911 return 0;
Lee, Chun-Yi339e7532010-05-12 09:58:10 -0700912
913fail_filter:
Lee, Chun-Yi143a4c02011-03-07 15:46:28 +0800914 msi_laptop_input_destroy();
915
916fail_input:
Lee, Chun-Yi339e7532010-05-12 09:58:10 -0700917 rfkill_cleanup();
918
919fail_rfkill:
920
921 return result;
922
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800923}
924
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400925static int __init msi_init(void)
926{
927 int ret;
928
929 if (acpi_disabled)
930 return -ENODEV;
931
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200932 dmi_check_system(msi_dmi_table);
933 if (!quirks)
934 /* quirks may be NULL if no match in DMI table */
935 quirks = &quirk_load_scm_model;
936 if (force)
937 quirks = &quirk_old_ec_model;
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400938
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200939 if (!quirks->old_ec_model)
Lee, Chun-Yie22388e2010-01-27 12:23:00 +0800940 get_threeg_exists();
941
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400942 if (auto_brightness < 0 || auto_brightness > 2)
943 return -EINVAL;
944
945 /* Register backlight stuff */
946
Thomas Renningera598c822008-08-01 17:38:01 +0200947 if (acpi_video_backlight_support()) {
Joe Perchesf9dcf192011-03-29 15:21:46 -0700948 pr_info("Brightness ignored, must be controlled by ACPI video driver\n");
Thomas Renningera598c822008-08-01 17:38:01 +0200949 } else {
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500950 struct backlight_properties props;
951 memset(&props, 0, sizeof(struct backlight_properties));
Matthew Garrettbb7ca742011-03-22 16:30:21 -0700952 props.type = BACKLIGHT_PLATFORM;
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500953 props.max_brightness = MSI_LCD_LEVEL_MAX - 1;
Thomas Renningera598c822008-08-01 17:38:01 +0200954 msibl_device = backlight_device_register("msi-laptop-bl", NULL,
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500955 NULL, &msibl_ops,
956 &props);
Thomas Renningera598c822008-08-01 17:38:01 +0200957 if (IS_ERR(msibl_device))
958 return PTR_ERR(msibl_device);
Thomas Renningera598c822008-08-01 17:38:01 +0200959 }
Richard Purdie599a52d2007-02-10 23:07:48 +0000960
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400961 ret = platform_driver_register(&msipf_driver);
962 if (ret)
963 goto fail_backlight;
964
965 /* Register platform stuff */
966
967 msipf_device = platform_device_alloc("msi-laptop-pf", -1);
968 if (!msipf_device) {
969 ret = -ENOMEM;
970 goto fail_platform_driver;
971 }
972
973 ret = platform_device_add(msipf_device);
974 if (ret)
975 goto fail_platform_device1;
976
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200977 if (quirks->load_scm_model && (load_scm_model_init(msipf_device) < 0)) {
Lee, Chun-Yi472ea122010-01-22 00:15:59 +0800978 ret = -EINVAL;
979 goto fail_platform_device1;
980 }
981
Greg Kroah-Hartman1ac34072010-05-12 12:03:00 -0700982 ret = sysfs_create_group(&msipf_device->dev.kobj,
983 &msipf_attribute_group);
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400984 if (ret)
985 goto fail_platform_device2;
986
Lee, Chun-Yi08163922012-12-15 19:31:27 +0200987 if (!quirks->old_ec_model) {
Lee, Chun-Yie22388e2010-01-27 12:23:00 +0800988 if (threeg_exists)
989 ret = device_create_file(&msipf_device->dev,
990 &dev_attr_threeg);
Lee, Chun-Yifc0dc4c2010-01-09 23:17:07 +0800991 if (ret)
992 goto fail_platform_device2;
993 }
994
Lennart Poettering8c4c7312006-10-06 01:27:02 -0400995 /* Disable automatic brightness control by default because
996 * this module was probably loaded to do brightness control in
997 * software. */
998
999 if (auto_brightness != 2)
1000 set_auto_brightness(auto_brightness);
1001
Joe Perchesf9dcf192011-03-29 15:21:46 -07001002 pr_info("driver " MSI_DRIVER_VERSION " successfully loaded\n");
Lennart Poettering8c4c7312006-10-06 01:27:02 -04001003
1004 return 0;
1005
1006fail_platform_device2:
1007
Lee, Chun-Yi08163922012-12-15 19:31:27 +02001008 if (quirks->load_scm_model) {
Lee, Chun-Yi7ab52522010-05-15 06:18:54 +08001009 i8042_remove_filter(msi_laptop_i8042_filter);
Lee, Chun-Yi08163922012-12-15 19:31:27 +02001010 cancel_delayed_work_sync(&msi_rfkill_dwork);
1011 cancel_work_sync(&msi_rfkill_work);
Lee, Chun-Yi7ab52522010-05-15 06:18:54 +08001012 rfkill_cleanup();
1013 }
Lennart Poettering8c4c7312006-10-06 01:27:02 -04001014 platform_device_del(msipf_device);
1015
1016fail_platform_device1:
1017
1018 platform_device_put(msipf_device);
1019
1020fail_platform_driver:
1021
1022 platform_driver_unregister(&msipf_driver);
1023
1024fail_backlight:
1025
1026 backlight_device_unregister(msibl_device);
1027
1028 return ret;
1029}
1030
1031static void __exit msi_cleanup(void)
1032{
Lee, Chun-Yi08163922012-12-15 19:31:27 +02001033 if (quirks->load_scm_model) {
Lee, Chun-Yi7ab52522010-05-15 06:18:54 +08001034 i8042_remove_filter(msi_laptop_i8042_filter);
Lee, Chun-Yi143a4c02011-03-07 15:46:28 +08001035 msi_laptop_input_destroy();
Lee, Chun-Yi08163922012-12-15 19:31:27 +02001036 cancel_delayed_work_sync(&msi_rfkill_dwork);
1037 cancel_work_sync(&msi_rfkill_work);
Lee, Chun-Yi7ab52522010-05-15 06:18:54 +08001038 rfkill_cleanup();
1039 }
Lennart Poettering8c4c7312006-10-06 01:27:02 -04001040
1041 sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group);
Lee, Chun-Yi08163922012-12-15 19:31:27 +02001042 if (!quirks->old_ec_model && threeg_exists)
Lee, Chun-Yifc0dc4c2010-01-09 23:17:07 +08001043 device_remove_file(&msipf_device->dev, &dev_attr_threeg);
Lennart Poettering8c4c7312006-10-06 01:27:02 -04001044 platform_device_unregister(msipf_device);
1045 platform_driver_unregister(&msipf_driver);
1046 backlight_device_unregister(msibl_device);
1047
1048 /* Enable automatic brightness control again */
1049 if (auto_brightness != 2)
1050 set_auto_brightness(1);
1051
Joe Perchesf9dcf192011-03-29 15:21:46 -07001052 pr_info("driver unloaded\n");
Lennart Poettering8c4c7312006-10-06 01:27:02 -04001053}
1054
1055module_init(msi_init);
1056module_exit(msi_cleanup);
1057
1058MODULE_AUTHOR("Lennart Poettering");
1059MODULE_DESCRIPTION("MSI Laptop Support");
1060MODULE_VERSION(MSI_DRIVER_VERSION);
1061MODULE_LICENSE("GPL");
Lennart Poettering4f5c7912007-05-08 22:07:02 +02001062
1063MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
1064MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*");
1065MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
1066MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
Lee, Chun-Yi46d0e9e2010-01-09 21:16:52 +08001067MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N034:*");
Lee, Chun-Yid0a4aa22010-05-12 09:58:07 -07001068MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N051:*");
1069MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*");
Lee, Chun-Yi1f27e172010-05-12 09:58:08 -07001070MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*");
Lee, Chun-Yi38803142011-06-10 15:24:26 +08001071MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnU270series:*");