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