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