brintos

brintos / linux-shallow public Read only

0
0
Text · 26.1 KiB · e5391a3 Raw
1150 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*-*-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 under11 * 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 the18 *   range 0..8. (rw)19 *20 *   auto_brightness - Enable automatic brightness control: contains21 *   either 0 or 1. If set to 1 the hardware adjusts the screen22 *   brightness automatically when the power cord is23 *   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 128 *   Please note that this file is constantly 0 if no Bluetooth29 *   hardware is available. (ro)30 *31 * In addition to these platform device attributes the driver32 * registers itself in the Linux backlight control subsystem and is33 * available to userspace under /sys/class/backlight/msi-laptop-bl/.34 *35 * This driver might work on other laptops produced by MSI. If you36 * want to try it you can pass force=1 as argument to the module which37 * will force it to load even when the DMI data doesn't identify the38 * laptop as MSI S270. YMMV.39 */40 41#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt42 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 957 58#define MSI_EC_COMMAND_WIRELESS 0x1059#define MSI_EC_COMMAND_LCD_LEVEL 0x1160 61#define MSI_STANDARD_EC_COMMAND_ADDRESS	0x2e62#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	0x2d69#define MSI_STANDARD_EC_SCM_LOAD_MASK		(1 << 0)70 71#define MSI_STANDARD_EC_FUNCTIONS_ADDRESS	0xe472/* 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		0x3382/* If zero, fan rotates at maximal speed */83#define MSI_STANDARD_EC_AUTOFAN_MASK		(1 << 0)84 85#ifdef CONFIG_PM_SLEEP86static int msi_laptop_resume(struct device *device);87#endif88static SIMPLE_DEV_PM_OPS(msi_laptop_pm, NULL, msi_laptop_resume);89 90#define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS	0x2f91 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 control117	 * Wlan/Bluetooth/3G, those netbook will load the SCM (windows app) to118	 * disable the original Wlan/Bluetooth control by BIOS when user press119	 * fn key, then control Wlan/Bluetooth/3G by SCM (software control by120	 * 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 the122	 * original BIOS control, then might need use HAL or other userland123	 * application to do the software control that simulate with SCM.124	 * e.g. MSI N034 netbook125	 */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 get132	 * some features working (e.g. ECO mode), but we cannot change133	 * 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		else223			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 sysfs_emit(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 sysfs_emit(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 sysfs_emit(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 sysfs_emit(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 sysfs_emit(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 sysfs_emit(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 sysfs_emit(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 sysfs_emit(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 sysfs_emit(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 sysfs_emit(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	NULL543};544 545static struct attribute *msipf_old_attributes[] = {546	&dev_attr_lcd_level.attr,547	&dev_attr_auto_brightness.attr,548	NULL549};550 551static const struct attribute_group msipf_attribute_group = {552	.attrs = msipf_attributes553};554 555static const struct attribute_group msipf_old_attribute_group = {556	.attrs = msipf_old_attributes557};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_cb612	},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_cb623	},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_cb634	},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_cb645	},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_cb657	},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_cb669	},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_cb679	},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_cb689	},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_cb699	},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_cb709	},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 on719	 * blocked == true is off720	 */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_set745};746 747static const struct rfkill_ops rfkill_wlan_ops = {748	.set_block = rfkill_wlan_set749};750 751static const struct rfkill_ops rfkill_threeg_ops = {752	.set_block = rfkill_threeg_set753};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	else778		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_SLEEP936static int msi_laptop_resume(struct device *device)937{938	return msi_scm_disable_hw_fn_handling();939}940#endif941 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 because1102		 * this module was probably loaded to do brightness control in1103		 * 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