msi-laptop: Add resume method for set the SCM load again
[safe/jmp/linux-2.6] / 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
63 #define MSI_DRIVER_VERSION "0.5"
64
65 #define MSI_LCD_LEVEL_MAX 9
66
67 #define MSI_EC_COMMAND_WIRELESS 0x10
68 #define MSI_EC_COMMAND_LCD_LEVEL 0x11
69
70 #define MSI_STANDARD_EC_COMMAND_ADDRESS 0x2e
71 #define MSI_STANDARD_EC_BLUETOOTH_MASK  (1 << 0)
72 #define MSI_STANDARD_EC_WEBCAM_MASK     (1 << 1)
73 #define MSI_STANDARD_EC_WLAN_MASK       (1 << 3)
74 #define MSI_STANDARD_EC_3G_MASK         (1 << 4)
75
76 /* For set SCM load flag to disable BIOS fn key */
77 #define MSI_STANDARD_EC_SCM_LOAD_ADDRESS        0x2d
78 #define MSI_STANDARD_EC_SCM_LOAD_MASK           (1 << 0)
79
80 static int msi_laptop_resume(struct platform_device *device);
81
82 static int force;
83 module_param(force, bool, 0);
84 MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
85
86 static int auto_brightness;
87 module_param(auto_brightness, int, 0);
88 MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)");
89
90 static bool old_ec_model;
91 static int wlan_s, bluetooth_s, threeg_s;
92
93 /* Some MSI 3G netbook only have one fn key to control Wlan/Bluetooth/3G,
94  * those netbook will load the SCM (windows app) to disable the original
95  * Wlan/Bluetooth control by BIOS when user press fn key, then control
96  * Wlan/Bluetooth/3G by SCM (software control by OS). Without SCM, user
97  * cann't on/off 3G module on those 3G netbook.
98  * On Linux, msi-laptop driver will do the same thing to disable the
99  * original BIOS control, then might need use HAL or other userland
100  * application to do the software control that simulate with SCM.
101  * e.g. MSI N034 netbook
102  */
103 static bool load_scm_model;
104 static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg;
105
106 /* Hardware access */
107
108 static int set_lcd_level(int level)
109 {
110         u8 buf[2];
111
112         if (level < 0 || level >= MSI_LCD_LEVEL_MAX)
113                 return -EINVAL;
114
115         buf[0] = 0x80;
116         buf[1] = (u8) (level*31);
117
118         return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), NULL, 0, 1);
119 }
120
121 static int get_lcd_level(void)
122 {
123         u8 wdata = 0, rdata;
124         int result;
125
126         result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1);
127         if (result < 0)
128                 return result;
129
130         return (int) rdata / 31;
131 }
132
133 static int get_auto_brightness(void)
134 {
135         u8 wdata = 4, rdata;
136         int result;
137
138         result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1);
139         if (result < 0)
140                 return result;
141
142         return !!(rdata & 8);
143 }
144
145 static int set_auto_brightness(int enable)
146 {
147         u8 wdata[2], rdata;
148         int result;
149
150         wdata[0] = 4;
151
152         result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, &rdata, 1, 1);
153         if (result < 0)
154                 return result;
155
156         wdata[0] = 0x84;
157         wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0);
158
159         return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0, 1);
160 }
161
162 static ssize_t set_device_state(const char *buf, size_t count, u8 mask)
163 {
164         int status;
165         u8 wdata = 0, rdata;
166         int result;
167
168         if (sscanf(buf, "%i", &status) != 1 || (status < 0 || status > 1))
169                 return -EINVAL;
170
171         /* read current device state */
172         result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
173         if (result < 0)
174                 return -EINVAL;
175
176         if (!!(rdata & mask) != status) {
177                 /* reverse device bit */
178                 if (rdata & mask)
179                         wdata = rdata & ~mask;
180                 else
181                         wdata = rdata | mask;
182
183                 result = ec_write(MSI_STANDARD_EC_COMMAND_ADDRESS, wdata);
184                 if (result < 0)
185                         return -EINVAL;
186         }
187
188         return count;
189 }
190
191 static int get_wireless_state(int *wlan, int *bluetooth)
192 {
193         u8 wdata = 0, rdata;
194         int result;
195
196         result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1, 1);
197         if (result < 0)
198                 return -1;
199
200         if (wlan)
201                 *wlan = !!(rdata & 8);
202
203         if (bluetooth)
204                 *bluetooth = !!(rdata & 128);
205
206         return 0;
207 }
208
209 static int get_wireless_state_ec_standard(void)
210 {
211         u8 rdata;
212         int result;
213
214         result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
215         if (result < 0)
216                 return -1;
217
218         wlan_s = !!(rdata & MSI_STANDARD_EC_WLAN_MASK);
219
220         bluetooth_s = !!(rdata & MSI_STANDARD_EC_BLUETOOTH_MASK);
221
222         threeg_s = !!(rdata & MSI_STANDARD_EC_3G_MASK);
223
224         return 0;
225 }
226
227 /* Backlight device stuff */
228
229 static int bl_get_brightness(struct backlight_device *b)
230 {
231         return get_lcd_level();
232 }
233
234
235 static int bl_update_status(struct backlight_device *b)
236 {
237         return set_lcd_level(b->props.brightness);
238 }
239
240 static struct backlight_ops msibl_ops = {
241         .get_brightness = bl_get_brightness,
242         .update_status  = bl_update_status,
243 };
244
245 static struct backlight_device *msibl_device;
246
247 /* Platform device */
248
249 static ssize_t show_wlan(struct device *dev,
250         struct device_attribute *attr, char *buf)
251 {
252
253         int ret, enabled;
254
255         if (old_ec_model) {
256                 ret = get_wireless_state(&enabled, NULL);
257         } else {
258                 ret = get_wireless_state_ec_standard();
259                 enabled = wlan_s;
260         }
261         if (ret < 0)
262                 return ret;
263
264         return sprintf(buf, "%i\n", enabled);
265 }
266
267 static ssize_t store_wlan(struct device *dev,
268         struct device_attribute *attr, const char *buf, size_t count)
269 {
270         return set_device_state(buf, count, MSI_STANDARD_EC_WLAN_MASK);
271 }
272
273 static ssize_t show_bluetooth(struct device *dev,
274         struct device_attribute *attr, char *buf)
275 {
276
277         int ret, enabled;
278
279         if (old_ec_model) {
280                 ret = get_wireless_state(NULL, &enabled);
281         } else {
282                 ret = get_wireless_state_ec_standard();
283                 enabled = bluetooth_s;
284         }
285         if (ret < 0)
286                 return ret;
287
288         return sprintf(buf, "%i\n", enabled);
289 }
290
291 static ssize_t store_bluetooth(struct device *dev,
292         struct device_attribute *attr, const char *buf, size_t count)
293 {
294         return set_device_state(buf, count, MSI_STANDARD_EC_BLUETOOTH_MASK);
295 }
296
297 static ssize_t show_threeg(struct device *dev,
298         struct device_attribute *attr, char *buf)
299 {
300
301         int ret;
302
303         /* old msi ec not support 3G */
304         if (old_ec_model)
305                 return -1;
306
307         ret = get_wireless_state_ec_standard();
308         if (ret < 0)
309                 return ret;
310
311         return sprintf(buf, "%i\n", threeg_s);
312 }
313
314 static ssize_t store_threeg(struct device *dev,
315         struct device_attribute *attr, const char *buf, size_t count)
316 {
317         return set_device_state(buf, count, MSI_STANDARD_EC_3G_MASK);
318 }
319
320 static ssize_t show_lcd_level(struct device *dev,
321         struct device_attribute *attr, char *buf)
322 {
323
324         int ret;
325
326         ret = get_lcd_level();
327         if (ret < 0)
328                 return ret;
329
330         return sprintf(buf, "%i\n", ret);
331 }
332
333 static ssize_t store_lcd_level(struct device *dev,
334         struct device_attribute *attr, const char *buf, size_t count)
335 {
336
337         int level, ret;
338
339         if (sscanf(buf, "%i", &level) != 1 || (level < 0 || level >= MSI_LCD_LEVEL_MAX))
340                 return -EINVAL;
341
342         ret = set_lcd_level(level);
343         if (ret < 0)
344                 return ret;
345
346         return count;
347 }
348
349 static ssize_t show_auto_brightness(struct device *dev,
350         struct device_attribute *attr, char *buf)
351 {
352
353         int ret;
354
355         ret = get_auto_brightness();
356         if (ret < 0)
357                 return ret;
358
359         return sprintf(buf, "%i\n", ret);
360 }
361
362 static ssize_t store_auto_brightness(struct device *dev,
363         struct device_attribute *attr, const char *buf, size_t count)
364 {
365
366         int enable, ret;
367
368         if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1)))
369                 return -EINVAL;
370
371         ret = set_auto_brightness(enable);
372         if (ret < 0)
373                 return ret;
374
375         return count;
376 }
377
378 static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
379 static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, store_auto_brightness);
380 static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL);
381 static DEVICE_ATTR(wlan, 0444, show_wlan, NULL);
382 static DEVICE_ATTR(threeg, 0444, show_threeg, NULL);
383
384 static struct attribute *msipf_attributes[] = {
385         &dev_attr_lcd_level.attr,
386         &dev_attr_auto_brightness.attr,
387         &dev_attr_bluetooth.attr,
388         &dev_attr_wlan.attr,
389         NULL
390 };
391
392 static struct attribute_group msipf_attribute_group = {
393         .attrs = msipf_attributes
394 };
395
396 static struct platform_driver msipf_driver = {
397         .driver = {
398                 .name = "msi-laptop-pf",
399                 .owner = THIS_MODULE,
400         },
401         .resume = msi_laptop_resume,
402 };
403
404 static struct platform_device *msipf_device;
405
406 /* Initialization */
407
408 static int dmi_check_cb(const struct dmi_system_id *id)
409 {
410         printk("msi-laptop: Identified laptop model '%s'.\n", id->ident);
411         return 0;
412 }
413
414 static struct dmi_system_id __initdata msi_dmi_table[] = {
415         {
416                 .ident = "MSI S270",
417                 .matches = {
418                         DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"),
419                         DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"),
420                         DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
421                         DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD")
422                 },
423                 .callback = dmi_check_cb
424         },
425         {
426                 .ident = "MSI S271",
427                 .matches = {
428                         DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
429                         DMI_MATCH(DMI_PRODUCT_NAME, "MS-1058"),
430                         DMI_MATCH(DMI_PRODUCT_VERSION, "0581"),
431                         DMI_MATCH(DMI_BOARD_NAME, "MS-1058")
432                 },
433                 .callback = dmi_check_cb
434         },
435         {
436                 .ident = "MSI S420",
437                 .matches = {
438                         DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
439                         DMI_MATCH(DMI_PRODUCT_NAME, "MS-1412"),
440                         DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
441                         DMI_MATCH(DMI_BOARD_NAME, "MS-1412")
442                 },
443                 .callback = dmi_check_cb
444         },
445         {
446                 .ident = "Medion MD96100",
447                 .matches = {
448                         DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"),
449                         DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"),
450                         DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
451                         DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD")
452                 },
453                 .callback = dmi_check_cb
454         },
455         { }
456 };
457
458 static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
459         {
460                 .ident = "MSI N034",
461                 .matches = {
462                         DMI_MATCH(DMI_SYS_VENDOR,
463                                 "MICRO-STAR INTERNATIONAL CO., LTD"),
464                         DMI_MATCH(DMI_PRODUCT_NAME, "MS-N034"),
465                         DMI_MATCH(DMI_CHASSIS_VENDOR,
466                         "MICRO-STAR INTERNATIONAL CO., LTD")
467                 },
468                 .callback = dmi_check_cb
469         },
470         { }
471 };
472
473 static int rfkill_bluetooth_set(void *data, bool blocked)
474 {
475         /* Do something with blocked...*/
476         /*
477          * blocked == false is on
478          * blocked == true is off
479          */
480         if (blocked)
481                 set_device_state("0", 0, MSI_STANDARD_EC_BLUETOOTH_MASK);
482         else
483                 set_device_state("1", 0, MSI_STANDARD_EC_BLUETOOTH_MASK);
484
485         return 0;
486 }
487
488 static int rfkill_wlan_set(void *data, bool blocked)
489 {
490         if (blocked)
491                 set_device_state("0", 0, MSI_STANDARD_EC_WLAN_MASK);
492         else
493                 set_device_state("1", 0, MSI_STANDARD_EC_WLAN_MASK);
494
495         return 0;
496 }
497
498 static int rfkill_threeg_set(void *data, bool blocked)
499 {
500         if (blocked)
501                 set_device_state("0", 0, MSI_STANDARD_EC_3G_MASK);
502         else
503                 set_device_state("1", 0, MSI_STANDARD_EC_3G_MASK);
504
505         return 0;
506 }
507
508 static struct rfkill_ops rfkill_bluetooth_ops = {
509         .set_block = rfkill_bluetooth_set
510 };
511
512 static struct rfkill_ops rfkill_wlan_ops = {
513         .set_block = rfkill_wlan_set
514 };
515
516 static struct rfkill_ops rfkill_threeg_ops = {
517         .set_block = rfkill_threeg_set
518 };
519
520 static void rfkill_cleanup(void)
521 {
522         if (rfk_bluetooth) {
523                 rfkill_unregister(rfk_bluetooth);
524                 rfkill_destroy(rfk_bluetooth);
525         }
526
527         if (rfk_threeg) {
528                 rfkill_unregister(rfk_threeg);
529                 rfkill_destroy(rfk_threeg);
530         }
531
532         if (rfk_wlan) {
533                 rfkill_unregister(rfk_wlan);
534                 rfkill_destroy(rfk_wlan);
535         }
536 }
537
538 static int rfkill_init(struct platform_device *sdev)
539 {
540         /* add rfkill */
541         int retval;
542
543         rfk_bluetooth = rfkill_alloc("msi-bluetooth", &sdev->dev,
544                                 RFKILL_TYPE_BLUETOOTH,
545                                 &rfkill_bluetooth_ops, NULL);
546         if (!rfk_bluetooth) {
547                 retval = -ENOMEM;
548                 goto err_bluetooth;
549         }
550         retval = rfkill_register(rfk_bluetooth);
551         if (retval)
552                 goto err_bluetooth;
553
554         rfk_wlan = rfkill_alloc("msi-wlan", &sdev->dev, RFKILL_TYPE_WLAN,
555                                 &rfkill_wlan_ops, NULL);
556         if (!rfk_wlan) {
557                 retval = -ENOMEM;
558                 goto err_wlan;
559         }
560         retval = rfkill_register(rfk_wlan);
561         if (retval)
562                 goto err_wlan;
563
564         rfk_threeg = rfkill_alloc("msi-threeg", &sdev->dev, RFKILL_TYPE_WWAN,
565                                 &rfkill_threeg_ops, NULL);
566         if (!rfk_threeg) {
567                 retval = -ENOMEM;
568                 goto err_threeg;
569         }
570         retval = rfkill_register(rfk_threeg);
571         if (retval)
572                 goto err_threeg;
573
574         return 0;
575
576 err_threeg:
577         rfkill_destroy(rfk_threeg);
578         if (rfk_wlan)
579                 rfkill_unregister(rfk_wlan);
580 err_wlan:
581         rfkill_destroy(rfk_wlan);
582         if (rfk_bluetooth)
583                 rfkill_unregister(rfk_bluetooth);
584 err_bluetooth:
585         rfkill_destroy(rfk_bluetooth);
586
587         return retval;
588 }
589
590 static int msi_laptop_resume(struct platform_device *device)
591 {
592         u8 data;
593         int result;
594
595         if (!load_scm_model)
596                 return 0;
597
598         /* set load SCM to disable hardware control by fn key */
599         result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data);
600         if (result < 0)
601                 return result;
602
603         result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS,
604                 data | MSI_STANDARD_EC_SCM_LOAD_MASK);
605         if (result < 0)
606                 return result;
607
608         return 0;
609 }
610
611 static int load_scm_model_init(struct platform_device *sdev)
612 {
613         u8 data;
614         int result;
615
616         /* allow userland write sysfs file  */
617         dev_attr_bluetooth.store = store_bluetooth;
618         dev_attr_wlan.store = store_wlan;
619         dev_attr_threeg.store = store_threeg;
620         dev_attr_bluetooth.attr.mode |= S_IWUSR;
621         dev_attr_wlan.attr.mode |= S_IWUSR;
622         dev_attr_threeg.attr.mode |= S_IWUSR;
623
624         /* disable hardware control by fn key */
625         result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data);
626         if (result < 0)
627                 return result;
628
629         result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS,
630                 data | MSI_STANDARD_EC_SCM_LOAD_MASK);
631         if (result < 0)
632                 return result;
633
634         /* initial rfkill */
635         result = rfkill_init(sdev);
636         if (result < 0)
637                 return result;
638
639         return 0;
640 }
641
642 static int __init msi_init(void)
643 {
644         int ret;
645
646         if (acpi_disabled)
647                 return -ENODEV;
648
649         if (force || dmi_check_system(msi_dmi_table))
650                 old_ec_model = 1;
651
652         if (!old_ec_model && dmi_check_system(msi_load_scm_models_dmi_table))
653                 load_scm_model = 1;
654
655         if (auto_brightness < 0 || auto_brightness > 2)
656                 return -EINVAL;
657
658         /* Register backlight stuff */
659
660         if (acpi_video_backlight_support()) {
661                 printk(KERN_INFO "MSI: Brightness ignored, must be controlled "
662                        "by ACPI video driver\n");
663         } else {
664                 msibl_device = backlight_device_register("msi-laptop-bl", NULL,
665                                                          NULL, &msibl_ops);
666                 if (IS_ERR(msibl_device))
667                         return PTR_ERR(msibl_device);
668                 msibl_device->props.max_brightness = MSI_LCD_LEVEL_MAX-1;
669         }
670
671         ret = platform_driver_register(&msipf_driver);
672         if (ret)
673                 goto fail_backlight;
674
675         /* Register platform stuff */
676
677         msipf_device = platform_device_alloc("msi-laptop-pf", -1);
678         if (!msipf_device) {
679                 ret = -ENOMEM;
680                 goto fail_platform_driver;
681         }
682
683         ret = platform_device_add(msipf_device);
684         if (ret)
685                 goto fail_platform_device1;
686
687         if (load_scm_model && (load_scm_model_init(msipf_device) < 0)) {
688                 ret = -EINVAL;
689                 goto fail_platform_device1;
690         }
691
692         ret = sysfs_create_group(&msipf_device->dev.kobj, &msipf_attribute_group);
693         if (ret)
694                 goto fail_platform_device2;
695
696         if (!old_ec_model) {
697                 ret = device_create_file(&msipf_device->dev, &dev_attr_threeg);
698                 if (ret)
699                         goto fail_platform_device2;
700         }
701
702         /* Disable automatic brightness control by default because
703          * this module was probably loaded to do brightness control in
704          * software. */
705
706         if (auto_brightness != 2)
707                 set_auto_brightness(auto_brightness);
708
709         printk(KERN_INFO "msi-laptop: driver "MSI_DRIVER_VERSION" successfully loaded.\n");
710
711         return 0;
712
713 fail_platform_device2:
714
715         platform_device_del(msipf_device);
716
717 fail_platform_device1:
718
719         platform_device_put(msipf_device);
720
721 fail_platform_driver:
722
723         platform_driver_unregister(&msipf_driver);
724
725 fail_backlight:
726
727         backlight_device_unregister(msibl_device);
728
729         return ret;
730 }
731
732 static void __exit msi_cleanup(void)
733 {
734
735         sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group);
736         if (!old_ec_model)
737                 device_remove_file(&msipf_device->dev, &dev_attr_threeg);
738         platform_device_unregister(msipf_device);
739         platform_driver_unregister(&msipf_driver);
740         backlight_device_unregister(msibl_device);
741
742         rfkill_cleanup();
743
744         /* Enable automatic brightness control again */
745         if (auto_brightness != 2)
746                 set_auto_brightness(1);
747
748         printk(KERN_INFO "msi-laptop: driver unloaded.\n");
749 }
750
751 module_init(msi_init);
752 module_exit(msi_cleanup);
753
754 MODULE_AUTHOR("Lennart Poettering");
755 MODULE_DESCRIPTION("MSI Laptop Support");
756 MODULE_VERSION(MSI_DRIVER_VERSION);
757 MODULE_LICENSE("GPL");
758
759 MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
760 MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*");
761 MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
762 MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
763 MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N034:*");