msi-laptop: send out touchpad on/off key
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / platform / x86 / msi-laptop.c
1 /*-*-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  *
26  * Driver also supports S271, S420 models.
27  *
28  * 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
54 #include <linux/module.h>
55 #include <linux/kernel.h>
56 #include <linux/init.h>
57 #include <linux/acpi.h>
58 #include <linux/dmi.h>
59 #include <linux/backlight.h>
60 #include <linux/platform_device.h>
61 #include <linux/rfkill.h>
62 #include <linux/i8042.h>
63 #include <linux/input.h>
64 #include <linux/input/sparse-keymap.h>
65
66 #define MSI_DRIVER_VERSION "0.5"
67
68 #define MSI_LCD_LEVEL_MAX 9
69
70 #define MSI_EC_COMMAND_WIRELESS 0x10
71 #define MSI_EC_COMMAND_LCD_LEVEL 0x11
72
73 #define MSI_STANDARD_EC_COMMAND_ADDRESS 0x2e
74 #define MSI_STANDARD_EC_BLUETOOTH_MASK  (1 << 0)
75 #define MSI_STANDARD_EC_WEBCAM_MASK     (1 << 1)
76 #define MSI_STANDARD_EC_WLAN_MASK       (1 << 3)
77 #define MSI_STANDARD_EC_3G_MASK         (1 << 4)
78
79 /* For set SCM load flag to disable BIOS fn key */
80 #define MSI_STANDARD_EC_SCM_LOAD_ADDRESS        0x2d
81 #define MSI_STANDARD_EC_SCM_LOAD_MASK           (1 << 0)
82
83 #define MSI_STANDARD_EC_TOUCHPAD_ADDRESS        0xe4
84 #define MSI_STANDARD_EC_TOUCHPAD_MASK           (1 << 4)
85
86 static int msi_laptop_resume(struct platform_device *device);
87
88 #define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS  0x2f
89
90 static int force;
91 module_param(force, bool, 0);
92 MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
93
94 static int auto_brightness;
95 module_param(auto_brightness, int, 0);
96 MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)");
97
98 static const struct key_entry msi_laptop_keymap[] = {
99         {KE_KEY, KEY_TOUCHPAD_ON, {KEY_TOUCHPAD_ON} },  /* Touch Pad On */
100         {KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} },/* Touch Pad On */
101         {KE_END, 0}
102 };
103
104 static struct input_dev *msi_laptop_input_dev;
105
106 static bool old_ec_model;
107 static int wlan_s, bluetooth_s, threeg_s;
108 static int threeg_exists;
109
110 /* Some MSI 3G netbook only have one fn key to control Wlan/Bluetooth/3G,
111  * those netbook will load the SCM (windows app) to disable the original
112  * Wlan/Bluetooth control by BIOS when user press fn key, then control
113  * Wlan/Bluetooth/3G by SCM (software control by OS). Without SCM, user
114  * cann't on/off 3G module on those 3G netbook.
115  * On Linux, msi-laptop driver will do the same thing to disable the
116  * original BIOS control, then might need use HAL or other userland
117  * application to do the software control that simulate with SCM.
118  * e.g. MSI N034 netbook
119  */
120 static bool load_scm_model;
121 static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg;
122
123 /* Hardware access */
124
125 static int set_lcd_level(int level)
126 {
127         u8 buf[2];
128
129         if (level < 0 || level >= MSI_LCD_LEVEL_MAX)
130                 return -EINVAL;
131
132         buf[0] = 0x80;
133         buf[1] = (u8) (level*31);
134
135         return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf),
136                               NULL, 0, 1);
137 }
138
139 static int get_lcd_level(void)
140 {
141         u8 wdata = 0, rdata;
142         int result;
143
144         result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1,
145                                 &rdata, 1, 1);
146         if (result < 0)
147                 return result;
148
149         return (int) rdata / 31;
150 }
151
152 static int get_auto_brightness(void)
153 {
154         u8 wdata = 4, rdata;
155         int result;
156
157         result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1,
158                                 &rdata, 1, 1);
159         if (result < 0)
160                 return result;
161
162         return !!(rdata & 8);
163 }
164
165 static int set_auto_brightness(int enable)
166 {
167         u8 wdata[2], rdata;
168         int result;
169
170         wdata[0] = 4;
171
172         result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1,
173                                 &rdata, 1, 1);
174         if (result < 0)
175                 return result;
176
177         wdata[0] = 0x84;
178         wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0);
179
180         return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2,
181                               NULL, 0, 1);
182 }
183
184 static ssize_t set_device_state(const char *buf, size_t count, u8 mask)
185 {
186         int status;
187         u8 wdata = 0, rdata;
188         int result;
189
190         if (sscanf(buf, "%i", &status) != 1 || (status < 0 || status > 1))
191                 return -EINVAL;
192
193         /* read current device state */
194         result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
195         if (result < 0)
196                 return -EINVAL;
197
198         if (!!(rdata & mask) != status) {
199                 /* reverse device bit */
200                 if (rdata & mask)
201                         wdata = rdata & ~mask;
202                 else
203                         wdata = rdata | mask;
204
205                 result = ec_write(MSI_STANDARD_EC_COMMAND_ADDRESS, wdata);
206                 if (result < 0)
207                         return -EINVAL;
208         }
209
210         return count;
211 }
212
213 static int get_wireless_state(int *wlan, int *bluetooth)
214 {
215         u8 wdata = 0, rdata;
216         int result;
217
218         result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1, 1);
219         if (result < 0)
220                 return -1;
221
222         if (wlan)
223                 *wlan = !!(rdata & 8);
224
225         if (bluetooth)
226                 *bluetooth = !!(rdata & 128);
227
228         return 0;
229 }
230
231 static int get_wireless_state_ec_standard(void)
232 {
233         u8 rdata;
234         int result;
235
236         result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
237         if (result < 0)
238                 return -1;
239
240         wlan_s = !!(rdata & MSI_STANDARD_EC_WLAN_MASK);
241
242         bluetooth_s = !!(rdata & MSI_STANDARD_EC_BLUETOOTH_MASK);
243
244         threeg_s = !!(rdata & MSI_STANDARD_EC_3G_MASK);
245
246         return 0;
247 }
248
249 static int get_threeg_exists(void)
250 {
251         u8 rdata;
252         int result;
253
254         result = ec_read(MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS, &rdata);
255         if (result < 0)
256                 return -1;
257
258         threeg_exists = !!(rdata & MSI_STANDARD_EC_3G_MASK);
259
260         return 0;
261 }
262
263 /* Backlight device stuff */
264
265 static int bl_get_brightness(struct backlight_device *b)
266 {
267         return get_lcd_level();
268 }
269
270
271 static int bl_update_status(struct backlight_device *b)
272 {
273         return set_lcd_level(b->props.brightness);
274 }
275
276 static const struct backlight_ops msibl_ops = {
277         .get_brightness = bl_get_brightness,
278         .update_status  = bl_update_status,
279 };
280
281 static struct backlight_device *msibl_device;
282
283 /* Platform device */
284
285 static ssize_t show_wlan(struct device *dev,
286         struct device_attribute *attr, char *buf)
287 {
288
289         int ret, enabled;
290
291         if (old_ec_model) {
292                 ret = get_wireless_state(&enabled, NULL);
293         } else {
294                 ret = get_wireless_state_ec_standard();
295                 enabled = wlan_s;
296         }
297         if (ret < 0)
298                 return ret;
299
300         return sprintf(buf, "%i\n", enabled);
301 }
302
303 static ssize_t store_wlan(struct device *dev,
304         struct device_attribute *attr, const char *buf, size_t count)
305 {
306         return set_device_state(buf, count, MSI_STANDARD_EC_WLAN_MASK);
307 }
308
309 static ssize_t show_bluetooth(struct device *dev,
310         struct device_attribute *attr, char *buf)
311 {
312
313         int ret, enabled;
314
315         if (old_ec_model) {
316                 ret = get_wireless_state(NULL, &enabled);
317         } else {
318                 ret = get_wireless_state_ec_standard();
319                 enabled = bluetooth_s;
320         }
321         if (ret < 0)
322                 return ret;
323
324         return sprintf(buf, "%i\n", enabled);
325 }
326
327 static ssize_t store_bluetooth(struct device *dev,
328         struct device_attribute *attr, const char *buf, size_t count)
329 {
330         return set_device_state(buf, count, MSI_STANDARD_EC_BLUETOOTH_MASK);
331 }
332
333 static ssize_t show_threeg(struct device *dev,
334         struct device_attribute *attr, char *buf)
335 {
336
337         int ret;
338
339         /* old msi ec not support 3G */
340         if (old_ec_model)
341                 return -1;
342
343         ret = get_wireless_state_ec_standard();
344         if (ret < 0)
345                 return ret;
346
347         return sprintf(buf, "%i\n", threeg_s);
348 }
349
350 static ssize_t store_threeg(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_3G_MASK);
354 }
355
356 static ssize_t show_lcd_level(struct device *dev,
357         struct device_attribute *attr, char *buf)
358 {
359
360         int ret;
361
362         ret = get_lcd_level();
363         if (ret < 0)
364                 return ret;
365
366         return sprintf(buf, "%i\n", ret);
367 }
368
369 static ssize_t store_lcd_level(struct device *dev,
370         struct device_attribute *attr, const char *buf, size_t count)
371 {
372
373         int level, ret;
374
375         if (sscanf(buf, "%i", &level) != 1 ||
376             (level < 0 || level >= MSI_LCD_LEVEL_MAX))
377                 return -EINVAL;
378
379         ret = set_lcd_level(level);
380         if (ret < 0)
381                 return ret;
382
383         return count;
384 }
385
386 static ssize_t show_auto_brightness(struct device *dev,
387         struct device_attribute *attr, char *buf)
388 {
389
390         int ret;
391
392         ret = get_auto_brightness();
393         if (ret < 0)
394                 return ret;
395
396         return sprintf(buf, "%i\n", ret);
397 }
398
399 static ssize_t store_auto_brightness(struct device *dev,
400         struct device_attribute *attr, const char *buf, size_t count)
401 {
402
403         int enable, ret;
404
405         if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1)))
406                 return -EINVAL;
407
408         ret = set_auto_brightness(enable);
409         if (ret < 0)
410                 return ret;
411
412         return count;
413 }
414
415 static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
416 static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness,
417                    store_auto_brightness);
418 static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL);
419 static DEVICE_ATTR(wlan, 0444, show_wlan, NULL);
420 static DEVICE_ATTR(threeg, 0444, show_threeg, NULL);
421
422 static struct attribute *msipf_attributes[] = {
423         &dev_attr_lcd_level.attr,
424         &dev_attr_auto_brightness.attr,
425         &dev_attr_bluetooth.attr,
426         &dev_attr_wlan.attr,
427         NULL
428 };
429
430 static struct attribute_group msipf_attribute_group = {
431         .attrs = msipf_attributes
432 };
433
434 static struct platform_driver msipf_driver = {
435         .driver = {
436                 .name = "msi-laptop-pf",
437                 .owner = THIS_MODULE,
438         },
439         .resume = msi_laptop_resume,
440 };
441
442 static struct platform_device *msipf_device;
443
444 /* Initialization */
445
446 static int dmi_check_cb(const struct dmi_system_id *id)
447 {
448         printk(KERN_INFO "msi-laptop: Identified laptop model '%s'.\n",
449                id->ident);
450         return 1;
451 }
452
453 static struct dmi_system_id __initdata msi_dmi_table[] = {
454         {
455                 .ident = "MSI S270",
456                 .matches = {
457                         DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"),
458                         DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"),
459                         DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
460                         DMI_MATCH(DMI_CHASSIS_VENDOR,
461                                   "MICRO-STAR INT'L CO.,LTD")
462                 },
463                 .callback = dmi_check_cb
464         },
465         {
466                 .ident = "MSI S271",
467                 .matches = {
468                         DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
469                         DMI_MATCH(DMI_PRODUCT_NAME, "MS-1058"),
470                         DMI_MATCH(DMI_PRODUCT_VERSION, "0581"),
471                         DMI_MATCH(DMI_BOARD_NAME, "MS-1058")
472                 },
473                 .callback = dmi_check_cb
474         },
475         {
476                 .ident = "MSI S420",
477                 .matches = {
478                         DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
479                         DMI_MATCH(DMI_PRODUCT_NAME, "MS-1412"),
480                         DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
481                         DMI_MATCH(DMI_BOARD_NAME, "MS-1412")
482                 },
483                 .callback = dmi_check_cb
484         },
485         {
486                 .ident = "Medion MD96100",
487                 .matches = {
488                         DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"),
489                         DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"),
490                         DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
491                         DMI_MATCH(DMI_CHASSIS_VENDOR,
492                                   "MICRO-STAR INT'L CO.,LTD")
493                 },
494                 .callback = dmi_check_cb
495         },
496         { }
497 };
498
499 static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
500         {
501                 .ident = "MSI N034",
502                 .matches = {
503                         DMI_MATCH(DMI_SYS_VENDOR,
504                                 "MICRO-STAR INTERNATIONAL CO., LTD"),
505                         DMI_MATCH(DMI_PRODUCT_NAME, "MS-N034"),
506                         DMI_MATCH(DMI_CHASSIS_VENDOR,
507                         "MICRO-STAR INTERNATIONAL CO., LTD")
508                 },
509                 .callback = dmi_check_cb
510         },
511         {
512                 .ident = "MSI N051",
513                 .matches = {
514                         DMI_MATCH(DMI_SYS_VENDOR,
515                                 "MICRO-STAR INTERNATIONAL CO., LTD"),
516                         DMI_MATCH(DMI_PRODUCT_NAME, "MS-N051"),
517                         DMI_MATCH(DMI_CHASSIS_VENDOR,
518                         "MICRO-STAR INTERNATIONAL CO., LTD")
519                 },
520                 .callback = dmi_check_cb
521         },
522         {
523                 .ident = "MSI N014",
524                 .matches = {
525                         DMI_MATCH(DMI_SYS_VENDOR,
526                                 "MICRO-STAR INTERNATIONAL CO., LTD"),
527                         DMI_MATCH(DMI_PRODUCT_NAME, "MS-N014"),
528                 },
529                 .callback = dmi_check_cb
530         },
531         {
532                 .ident = "MSI CR620",
533                 .matches = {
534                         DMI_MATCH(DMI_SYS_VENDOR,
535                                 "Micro-Star International"),
536                         DMI_MATCH(DMI_PRODUCT_NAME, "CR620"),
537                 },
538                 .callback = dmi_check_cb
539         },
540         { }
541 };
542
543 static int rfkill_bluetooth_set(void *data, bool blocked)
544 {
545         /* Do something with blocked...*/
546         /*
547          * blocked == false is on
548          * blocked == true is off
549          */
550         if (blocked)
551                 set_device_state("0", 0, MSI_STANDARD_EC_BLUETOOTH_MASK);
552         else
553                 set_device_state("1", 0, MSI_STANDARD_EC_BLUETOOTH_MASK);
554
555         return 0;
556 }
557
558 static int rfkill_wlan_set(void *data, bool blocked)
559 {
560         if (blocked)
561                 set_device_state("0", 0, MSI_STANDARD_EC_WLAN_MASK);
562         else
563                 set_device_state("1", 0, MSI_STANDARD_EC_WLAN_MASK);
564
565         return 0;
566 }
567
568 static int rfkill_threeg_set(void *data, bool blocked)
569 {
570         if (blocked)
571                 set_device_state("0", 0, MSI_STANDARD_EC_3G_MASK);
572         else
573                 set_device_state("1", 0, MSI_STANDARD_EC_3G_MASK);
574
575         return 0;
576 }
577
578 static const struct rfkill_ops rfkill_bluetooth_ops = {
579         .set_block = rfkill_bluetooth_set
580 };
581
582 static const struct rfkill_ops rfkill_wlan_ops = {
583         .set_block = rfkill_wlan_set
584 };
585
586 static const struct rfkill_ops rfkill_threeg_ops = {
587         .set_block = rfkill_threeg_set
588 };
589
590 static void rfkill_cleanup(void)
591 {
592         if (rfk_bluetooth) {
593                 rfkill_unregister(rfk_bluetooth);
594                 rfkill_destroy(rfk_bluetooth);
595         }
596
597         if (rfk_threeg) {
598                 rfkill_unregister(rfk_threeg);
599                 rfkill_destroy(rfk_threeg);
600         }
601
602         if (rfk_wlan) {
603                 rfkill_unregister(rfk_wlan);
604                 rfkill_destroy(rfk_wlan);
605         }
606 }
607
608 static void msi_update_rfkill(struct work_struct *ignored)
609 {
610         get_wireless_state_ec_standard();
611
612         if (rfk_wlan)
613                 rfkill_set_sw_state(rfk_wlan, !wlan_s);
614         if (rfk_bluetooth)
615                 rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s);
616         if (rfk_threeg)
617                 rfkill_set_sw_state(rfk_threeg, !threeg_s);
618 }
619 static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill);
620
621 static void msi_send_touchpad_key(struct work_struct *ignored)
622 {
623         u8 rdata;
624         int result;
625
626         result = ec_read(MSI_STANDARD_EC_TOUCHPAD_ADDRESS, &rdata);
627         if (result < 0)
628                 return;
629
630         sparse_keymap_report_event(msi_laptop_input_dev,
631                 (rdata & MSI_STANDARD_EC_TOUCHPAD_MASK) ?
632                 KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF, 1, true);
633 }
634 static DECLARE_DELAYED_WORK(msi_touchpad_work, msi_send_touchpad_key);
635
636 static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
637                                 struct serio *port)
638 {
639         static bool extended;
640
641         if (str & 0x20)
642                 return false;
643
644         /* 0x54 wwan, 0x62 bluetooth, 0x76 wlan, 0xE4 touchpad toggle*/
645         if (unlikely(data == 0xe0)) {
646                 extended = true;
647                 return false;
648         } else if (unlikely(extended)) {
649                 extended = false;
650                 switch (data) {
651                 case 0xE4:
652                         schedule_delayed_work(&msi_touchpad_work,
653                                 round_jiffies_relative(0.5 * HZ));
654                         break;
655                 case 0x54:
656                 case 0x62:
657                 case 0x76:
658                         schedule_delayed_work(&msi_rfkill_work,
659                                 round_jiffies_relative(0.5 * HZ));
660                         break;
661                 }
662         }
663
664         return false;
665 }
666
667 static void msi_init_rfkill(struct work_struct *ignored)
668 {
669         if (rfk_wlan) {
670                 rfkill_set_sw_state(rfk_wlan, !wlan_s);
671                 rfkill_wlan_set(NULL, !wlan_s);
672         }
673         if (rfk_bluetooth) {
674                 rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s);
675                 rfkill_bluetooth_set(NULL, !bluetooth_s);
676         }
677         if (rfk_threeg) {
678                 rfkill_set_sw_state(rfk_threeg, !threeg_s);
679                 rfkill_threeg_set(NULL, !threeg_s);
680         }
681 }
682 static DECLARE_DELAYED_WORK(msi_rfkill_init, msi_init_rfkill);
683
684 static int rfkill_init(struct platform_device *sdev)
685 {
686         /* add rfkill */
687         int retval;
688
689         /* keep the hardware wireless state */
690         get_wireless_state_ec_standard();
691
692         rfk_bluetooth = rfkill_alloc("msi-bluetooth", &sdev->dev,
693                                 RFKILL_TYPE_BLUETOOTH,
694                                 &rfkill_bluetooth_ops, NULL);
695         if (!rfk_bluetooth) {
696                 retval = -ENOMEM;
697                 goto err_bluetooth;
698         }
699         retval = rfkill_register(rfk_bluetooth);
700         if (retval)
701                 goto err_bluetooth;
702
703         rfk_wlan = rfkill_alloc("msi-wlan", &sdev->dev, RFKILL_TYPE_WLAN,
704                                 &rfkill_wlan_ops, NULL);
705         if (!rfk_wlan) {
706                 retval = -ENOMEM;
707                 goto err_wlan;
708         }
709         retval = rfkill_register(rfk_wlan);
710         if (retval)
711                 goto err_wlan;
712
713         if (threeg_exists) {
714                 rfk_threeg = rfkill_alloc("msi-threeg", &sdev->dev,
715                                 RFKILL_TYPE_WWAN, &rfkill_threeg_ops, NULL);
716                 if (!rfk_threeg) {
717                         retval = -ENOMEM;
718                         goto err_threeg;
719                 }
720                 retval = rfkill_register(rfk_threeg);
721                 if (retval)
722                         goto err_threeg;
723         }
724
725         /* schedule to run rfkill state initial */
726         schedule_delayed_work(&msi_rfkill_init,
727                                 round_jiffies_relative(1 * HZ));
728
729         return 0;
730
731 err_threeg:
732         rfkill_destroy(rfk_threeg);
733         if (rfk_wlan)
734                 rfkill_unregister(rfk_wlan);
735 err_wlan:
736         rfkill_destroy(rfk_wlan);
737         if (rfk_bluetooth)
738                 rfkill_unregister(rfk_bluetooth);
739 err_bluetooth:
740         rfkill_destroy(rfk_bluetooth);
741
742         return retval;
743 }
744
745 static int msi_laptop_resume(struct platform_device *device)
746 {
747         u8 data;
748         int result;
749
750         if (!load_scm_model)
751                 return 0;
752
753         /* set load SCM to disable hardware control by fn key */
754         result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data);
755         if (result < 0)
756                 return result;
757
758         result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS,
759                 data | MSI_STANDARD_EC_SCM_LOAD_MASK);
760         if (result < 0)
761                 return result;
762
763         return 0;
764 }
765
766 static int __init msi_laptop_input_setup(void)
767 {
768         int err;
769
770         msi_laptop_input_dev = input_allocate_device();
771         if (!msi_laptop_input_dev)
772                 return -ENOMEM;
773
774         msi_laptop_input_dev->name = "MSI Laptop hotkeys";
775         msi_laptop_input_dev->phys = "msi-laptop/input0";
776         msi_laptop_input_dev->id.bustype = BUS_HOST;
777
778         err = sparse_keymap_setup(msi_laptop_input_dev,
779                 msi_laptop_keymap, NULL);
780         if (err)
781                 goto err_free_dev;
782
783         err = input_register_device(msi_laptop_input_dev);
784         if (err)
785                 goto err_free_keymap;
786
787         return 0;
788
789 err_free_keymap:
790         sparse_keymap_free(msi_laptop_input_dev);
791 err_free_dev:
792         input_free_device(msi_laptop_input_dev);
793         return err;
794 }
795
796 static void msi_laptop_input_destroy(void)
797 {
798         sparse_keymap_free(msi_laptop_input_dev);
799         input_unregister_device(msi_laptop_input_dev);
800 }
801
802 static int load_scm_model_init(struct platform_device *sdev)
803 {
804         u8 data;
805         int result;
806
807         /* allow userland write sysfs file  */
808         dev_attr_bluetooth.store = store_bluetooth;
809         dev_attr_wlan.store = store_wlan;
810         dev_attr_threeg.store = store_threeg;
811         dev_attr_bluetooth.attr.mode |= S_IWUSR;
812         dev_attr_wlan.attr.mode |= S_IWUSR;
813         dev_attr_threeg.attr.mode |= S_IWUSR;
814
815         /* disable hardware control by fn key */
816         result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data);
817         if (result < 0)
818                 return result;
819
820         result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS,
821                 data | MSI_STANDARD_EC_SCM_LOAD_MASK);
822         if (result < 0)
823                 return result;
824
825         /* initial rfkill */
826         result = rfkill_init(sdev);
827         if (result < 0)
828                 goto fail_rfkill;
829
830         /* setup input device */
831         result = msi_laptop_input_setup();
832         if (result)
833                 goto fail_input;
834
835         result = i8042_install_filter(msi_laptop_i8042_filter);
836         if (result) {
837                 printk(KERN_ERR
838                         "msi-laptop: Unable to install key filter\n");
839                 goto fail_filter;
840         }
841
842         return 0;
843
844 fail_filter:
845         msi_laptop_input_destroy();
846
847 fail_input:
848         rfkill_cleanup();
849
850 fail_rfkill:
851
852         return result;
853
854 }
855
856 static int __init msi_init(void)
857 {
858         int ret;
859
860         if (acpi_disabled)
861                 return -ENODEV;
862
863         if (force || dmi_check_system(msi_dmi_table))
864                 old_ec_model = 1;
865
866         if (!old_ec_model)
867                 get_threeg_exists();
868
869         if (!old_ec_model && dmi_check_system(msi_load_scm_models_dmi_table))
870                 load_scm_model = 1;
871
872         if (auto_brightness < 0 || auto_brightness > 2)
873                 return -EINVAL;
874
875         /* Register backlight stuff */
876
877         if (acpi_video_backlight_support()) {
878                 printk(KERN_INFO "MSI: Brightness ignored, must be controlled "
879                        "by ACPI video driver\n");
880         } else {
881                 struct backlight_properties props;
882                 memset(&props, 0, sizeof(struct backlight_properties));
883                 props.type = BACKLIGHT_PLATFORM;
884                 props.max_brightness = MSI_LCD_LEVEL_MAX - 1;
885                 msibl_device = backlight_device_register("msi-laptop-bl", NULL,
886                                                          NULL, &msibl_ops,
887                                                          &props);
888                 if (IS_ERR(msibl_device))
889                         return PTR_ERR(msibl_device);
890         }
891
892         ret = platform_driver_register(&msipf_driver);
893         if (ret)
894                 goto fail_backlight;
895
896         /* Register platform stuff */
897
898         msipf_device = platform_device_alloc("msi-laptop-pf", -1);
899         if (!msipf_device) {
900                 ret = -ENOMEM;
901                 goto fail_platform_driver;
902         }
903
904         ret = platform_device_add(msipf_device);
905         if (ret)
906                 goto fail_platform_device1;
907
908         if (load_scm_model && (load_scm_model_init(msipf_device) < 0)) {
909                 ret = -EINVAL;
910                 goto fail_platform_device1;
911         }
912
913         ret = sysfs_create_group(&msipf_device->dev.kobj,
914                                  &msipf_attribute_group);
915         if (ret)
916                 goto fail_platform_device2;
917
918         if (!old_ec_model) {
919                 if (threeg_exists)
920                         ret = device_create_file(&msipf_device->dev,
921                                                 &dev_attr_threeg);
922                 if (ret)
923                         goto fail_platform_device2;
924         }
925
926         /* Disable automatic brightness control by default because
927          * this module was probably loaded to do brightness control in
928          * software. */
929
930         if (auto_brightness != 2)
931                 set_auto_brightness(auto_brightness);
932
933         printk(KERN_INFO "msi-laptop: driver "MSI_DRIVER_VERSION" successfully loaded.\n");
934
935         return 0;
936
937 fail_platform_device2:
938
939         if (load_scm_model) {
940                 i8042_remove_filter(msi_laptop_i8042_filter);
941                 cancel_delayed_work_sync(&msi_rfkill_work);
942                 rfkill_cleanup();
943         }
944         platform_device_del(msipf_device);
945
946 fail_platform_device1:
947
948         platform_device_put(msipf_device);
949
950 fail_platform_driver:
951
952         platform_driver_unregister(&msipf_driver);
953
954 fail_backlight:
955
956         backlight_device_unregister(msibl_device);
957
958         return ret;
959 }
960
961 static void __exit msi_cleanup(void)
962 {
963         if (load_scm_model) {
964                 i8042_remove_filter(msi_laptop_i8042_filter);
965                 msi_laptop_input_destroy();
966                 cancel_delayed_work_sync(&msi_rfkill_work);
967                 rfkill_cleanup();
968         }
969
970         sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group);
971         if (!old_ec_model && threeg_exists)
972                 device_remove_file(&msipf_device->dev, &dev_attr_threeg);
973         platform_device_unregister(msipf_device);
974         platform_driver_unregister(&msipf_driver);
975         backlight_device_unregister(msibl_device);
976
977         /* Enable automatic brightness control again */
978         if (auto_brightness != 2)
979                 set_auto_brightness(1);
980
981         printk(KERN_INFO "msi-laptop: driver unloaded.\n");
982 }
983
984 module_init(msi_init);
985 module_exit(msi_cleanup);
986
987 MODULE_AUTHOR("Lennart Poettering");
988 MODULE_DESCRIPTION("MSI Laptop Support");
989 MODULE_VERSION(MSI_DRIVER_VERSION);
990 MODULE_LICENSE("GPL");
991
992 MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
993 MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*");
994 MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
995 MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
996 MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N034:*");
997 MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N051:*");
998 MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*");
999 MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*");