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