| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 1 | /*-*-linux-c-*-*/ | 
 | 2 |  | 
 | 3 | /* | 
 | 4 |   Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de> | 
 | 5 |  | 
 | 6 |   This program is free software; you can redistribute it and/or modify | 
 | 7 |   it under the terms of the GNU General Public License as published by | 
 | 8 |   the Free Software Foundation; either version 2 of the License, or | 
 | 9 |   (at your option) any later version. | 
 | 10 |  | 
 | 11 |   This program is distributed in the hope that it will be useful, but | 
 | 12 |   WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 13 |   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 | 14 |   General Public License for more details. | 
 | 15 |  | 
 | 16 |   You should have received a copy of the GNU General Public License | 
 | 17 |   along with this program; if not, write to the Free Software | 
 | 18 |   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 
 | 19 |   02110-1301, USA. | 
 | 20 |  */ | 
 | 21 |  | 
 | 22 | /* | 
 | 23 |  * msi-laptop.c - MSI S270 laptop support. This laptop is sold under | 
 | 24 |  * various brands, including "Cytron/TCM/Medion/Tchibo MD96100". | 
 | 25 |  * | 
| Lennart Poettering | 4f5c791 | 2007-05-08 22:07:02 +0200 | [diff] [blame] | 26 |  * Driver also supports S271, S420 models. | 
 | 27 |  * | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 28 |  * This driver exports a few files in /sys/devices/platform/msi-laptop-pf/: | 
 | 29 |  * | 
 | 30 |  *   lcd_level - Screen brightness: contains a single integer in the | 
 | 31 |  *   range 0..8. (rw) | 
 | 32 |  * | 
 | 33 |  *   auto_brightness - Enable automatic brightness control: contains | 
 | 34 |  *   either 0 or 1. If set to 1 the hardware adjusts the screen | 
 | 35 |  *   brightness automatically when the power cord is | 
 | 36 |  *   plugged/unplugged. (rw) | 
 | 37 |  * | 
 | 38 |  *   wlan - WLAN subsystem enabled: contains either 0 or 1. (ro) | 
 | 39 |  * | 
 | 40 |  *   bluetooth - Bluetooth subsystem enabled: contains either 0 or 1 | 
 | 41 |  *   Please note that this file is constantly 0 if no Bluetooth | 
 | 42 |  *   hardware is available. (ro) | 
 | 43 |  * | 
 | 44 |  * In addition to these platform device attributes the driver | 
 | 45 |  * registers itself in the Linux backlight control subsystem and is | 
 | 46 |  * available to userspace under /sys/class/backlight/msi-laptop-bl/. | 
 | 47 |  * | 
 | 48 |  * This driver might work on other laptops produced by MSI. If you | 
 | 49 |  * want to try it you can pass force=1 as argument to the module which | 
 | 50 |  * will force it to load even when the DMI data doesn't identify the | 
 | 51 |  * laptop as MSI S270. YMMV. | 
 | 52 |  */ | 
 | 53 |  | 
| Joey Lee | bbe24fe | 2011-03-16 01:55:19 -0600 | [diff] [blame] | 54 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 
 | 55 |  | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 56 | #include <linux/module.h> | 
 | 57 | #include <linux/kernel.h> | 
 | 58 | #include <linux/init.h> | 
 | 59 | #include <linux/acpi.h> | 
 | 60 | #include <linux/dmi.h> | 
 | 61 | #include <linux/backlight.h> | 
 | 62 | #include <linux/platform_device.h> | 
| Lee, Chun-Yi | 472ea12 | 2010-01-22 00:15:59 +0800 | [diff] [blame] | 63 | #include <linux/rfkill.h> | 
| Lee, Chun-Yi | 339e753 | 2010-05-12 09:58:10 -0700 | [diff] [blame] | 64 | #include <linux/i8042.h> | 
| Lee, Chun-Yi | 143a4c0 | 2011-03-07 15:46:28 +0800 | [diff] [blame] | 65 | #include <linux/input.h> | 
 | 66 | #include <linux/input/sparse-keymap.h> | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 67 |  | 
 | 68 | #define MSI_DRIVER_VERSION "0.5" | 
 | 69 |  | 
 | 70 | #define MSI_LCD_LEVEL_MAX 9 | 
 | 71 |  | 
 | 72 | #define MSI_EC_COMMAND_WIRELESS 0x10 | 
 | 73 | #define MSI_EC_COMMAND_LCD_LEVEL 0x11 | 
 | 74 |  | 
| Lee, Chun-Yi | 46d0e9e | 2010-01-09 21:16:52 +0800 | [diff] [blame] | 75 | #define MSI_STANDARD_EC_COMMAND_ADDRESS	0x2e | 
 | 76 | #define MSI_STANDARD_EC_BLUETOOTH_MASK	(1 << 0) | 
 | 77 | #define MSI_STANDARD_EC_WEBCAM_MASK	(1 << 1) | 
 | 78 | #define MSI_STANDARD_EC_WLAN_MASK	(1 << 3) | 
| Lee, Chun-Yi | fc0dc4c | 2010-01-09 23:17:07 +0800 | [diff] [blame] | 79 | #define MSI_STANDARD_EC_3G_MASK		(1 << 4) | 
| Lee, Chun-Yi | 46d0e9e | 2010-01-09 21:16:52 +0800 | [diff] [blame] | 80 |  | 
| Lee, Chun-Yi | 472ea12 | 2010-01-22 00:15:59 +0800 | [diff] [blame] | 81 | /* For set SCM load flag to disable BIOS fn key */ | 
 | 82 | #define MSI_STANDARD_EC_SCM_LOAD_ADDRESS	0x2d | 
 | 83 | #define MSI_STANDARD_EC_SCM_LOAD_MASK		(1 << 0) | 
 | 84 |  | 
| Lee, Chun-Yi | 143a4c0 | 2011-03-07 15:46:28 +0800 | [diff] [blame] | 85 | #define MSI_STANDARD_EC_TOUCHPAD_ADDRESS	0xe4 | 
 | 86 | #define MSI_STANDARD_EC_TOUCHPAD_MASK		(1 << 4) | 
 | 87 |  | 
| Rafael J. Wysocki | 3567a4e | 2012-08-09 23:00:13 +0200 | [diff] [blame] | 88 | #ifdef CONFIG_PM_SLEEP | 
| Rafael J. Wysocki | 9033132 | 2012-07-06 19:06:19 +0200 | [diff] [blame] | 89 | static int msi_laptop_resume(struct device *device); | 
| Rafael J. Wysocki | 3567a4e | 2012-08-09 23:00:13 +0200 | [diff] [blame] | 90 | #endif | 
| Rafael J. Wysocki | 9033132 | 2012-07-06 19:06:19 +0200 | [diff] [blame] | 91 | static SIMPLE_DEV_PM_OPS(msi_laptop_pm, NULL, msi_laptop_resume); | 
| Lee, Chun-Yi | ec76627 | 2010-01-27 00:13:45 +0800 | [diff] [blame] | 92 |  | 
| Lee, Chun-Yi | e22388e | 2010-01-27 12:23:00 +0800 | [diff] [blame] | 93 | #define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS	0x2f | 
 | 94 |  | 
| Rusty Russell | 90ab5ee | 2012-01-13 09:32:20 +1030 | [diff] [blame] | 95 | static bool force; | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 96 | module_param(force, bool, 0); | 
 | 97 | MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); | 
 | 98 |  | 
 | 99 | static int auto_brightness; | 
 | 100 | module_param(auto_brightness, int, 0); | 
 | 101 | MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)"); | 
 | 102 |  | 
| Lee, Chun-Yi | 143a4c0 | 2011-03-07 15:46:28 +0800 | [diff] [blame] | 103 | static const struct key_entry msi_laptop_keymap[] = { | 
 | 104 | 	{KE_KEY, KEY_TOUCHPAD_ON, {KEY_TOUCHPAD_ON} },	/* Touch Pad On */ | 
 | 105 | 	{KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} },/* Touch Pad On */ | 
 | 106 | 	{KE_END, 0} | 
 | 107 | }; | 
 | 108 |  | 
 | 109 | static struct input_dev *msi_laptop_input_dev; | 
 | 110 |  | 
| Lee, Chun-Yi | 46d0e9e | 2010-01-09 21:16:52 +0800 | [diff] [blame] | 111 | static bool old_ec_model; | 
| Lee, Chun-Yi | fc0dc4c | 2010-01-09 23:17:07 +0800 | [diff] [blame] | 112 | static int wlan_s, bluetooth_s, threeg_s; | 
| Lee, Chun-Yi | e22388e | 2010-01-27 12:23:00 +0800 | [diff] [blame] | 113 | static int threeg_exists; | 
| Lee, Chun-Yi | 46d0e9e | 2010-01-09 21:16:52 +0800 | [diff] [blame] | 114 |  | 
| Lee, Chun-Yi | 472ea12 | 2010-01-22 00:15:59 +0800 | [diff] [blame] | 115 | /* Some MSI 3G netbook only have one fn key to control Wlan/Bluetooth/3G, | 
 | 116 |  * those netbook will load the SCM (windows app) to disable the original | 
 | 117 |  * Wlan/Bluetooth control by BIOS when user press fn key, then control | 
 | 118 |  * Wlan/Bluetooth/3G by SCM (software control by OS). Without SCM, user | 
 | 119 |  * cann't on/off 3G module on those 3G netbook. | 
 | 120 |  * On Linux, msi-laptop driver will do the same thing to disable the | 
 | 121 |  * original BIOS control, then might need use HAL or other userland | 
 | 122 |  * application to do the software control that simulate with SCM. | 
 | 123 |  * e.g. MSI N034 netbook | 
 | 124 |  */ | 
 | 125 | static bool load_scm_model; | 
 | 126 | static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg; | 
 | 127 |  | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 128 | /* Hardware access */ | 
 | 129 |  | 
 | 130 | static int set_lcd_level(int level) | 
 | 131 | { | 
 | 132 | 	u8 buf[2]; | 
 | 133 |  | 
 | 134 | 	if (level < 0 || level >= MSI_LCD_LEVEL_MAX) | 
 | 135 | 		return -EINVAL; | 
 | 136 |  | 
 | 137 | 	buf[0] = 0x80; | 
 | 138 | 	buf[1] = (u8) (level*31); | 
 | 139 |  | 
| Greg Kroah-Hartman | 1ac3407 | 2010-05-12 12:03:00 -0700 | [diff] [blame] | 140 | 	return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), | 
| Thomas Renninger | 1cb7b1e | 2011-03-31 13:36:38 +0200 | [diff] [blame] | 141 | 			      NULL, 0); | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 142 | } | 
 | 143 |  | 
 | 144 | static int get_lcd_level(void) | 
 | 145 | { | 
 | 146 | 	u8 wdata = 0, rdata; | 
 | 147 | 	int result; | 
 | 148 |  | 
| Greg Kroah-Hartman | 1ac3407 | 2010-05-12 12:03:00 -0700 | [diff] [blame] | 149 | 	result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, | 
| Thomas Renninger | 1cb7b1e | 2011-03-31 13:36:38 +0200 | [diff] [blame] | 150 | 				&rdata, 1); | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 151 | 	if (result < 0) | 
 | 152 | 		return result; | 
 | 153 |  | 
 | 154 | 	return (int) rdata / 31; | 
 | 155 | } | 
 | 156 |  | 
 | 157 | static int get_auto_brightness(void) | 
 | 158 | { | 
 | 159 | 	u8 wdata = 4, rdata; | 
 | 160 | 	int result; | 
 | 161 |  | 
| Greg Kroah-Hartman | 1ac3407 | 2010-05-12 12:03:00 -0700 | [diff] [blame] | 162 | 	result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, | 
| Thomas Renninger | 1cb7b1e | 2011-03-31 13:36:38 +0200 | [diff] [blame] | 163 | 				&rdata, 1); | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 164 | 	if (result < 0) | 
 | 165 | 		return result; | 
 | 166 |  | 
 | 167 | 	return !!(rdata & 8); | 
 | 168 | } | 
 | 169 |  | 
 | 170 | static int set_auto_brightness(int enable) | 
 | 171 | { | 
 | 172 | 	u8 wdata[2], rdata; | 
 | 173 | 	int result; | 
 | 174 |  | 
 | 175 | 	wdata[0] = 4; | 
 | 176 |  | 
| Greg Kroah-Hartman | 1ac3407 | 2010-05-12 12:03:00 -0700 | [diff] [blame] | 177 | 	result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, | 
| Thomas Renninger | 1cb7b1e | 2011-03-31 13:36:38 +0200 | [diff] [blame] | 178 | 				&rdata, 1); | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 179 | 	if (result < 0) | 
 | 180 | 		return result; | 
 | 181 |  | 
 | 182 | 	wdata[0] = 0x84; | 
 | 183 | 	wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0); | 
 | 184 |  | 
| Greg Kroah-Hartman | 1ac3407 | 2010-05-12 12:03:00 -0700 | [diff] [blame] | 185 | 	return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, | 
| Thomas Renninger | 1cb7b1e | 2011-03-31 13:36:38 +0200 | [diff] [blame] | 186 | 			      NULL, 0); | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 187 | } | 
 | 188 |  | 
| Lee, Chun-Yi | 472ea12 | 2010-01-22 00:15:59 +0800 | [diff] [blame] | 189 | static ssize_t set_device_state(const char *buf, size_t count, u8 mask) | 
 | 190 | { | 
 | 191 | 	int status; | 
 | 192 | 	u8 wdata = 0, rdata; | 
 | 193 | 	int result; | 
 | 194 |  | 
 | 195 | 	if (sscanf(buf, "%i", &status) != 1 || (status < 0 || status > 1)) | 
 | 196 | 		return -EINVAL; | 
 | 197 |  | 
 | 198 | 	/* read current device state */ | 
 | 199 | 	result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata); | 
 | 200 | 	if (result < 0) | 
 | 201 | 		return -EINVAL; | 
 | 202 |  | 
 | 203 | 	if (!!(rdata & mask) != status) { | 
 | 204 | 		/* reverse device bit */ | 
 | 205 | 		if (rdata & mask) | 
 | 206 | 			wdata = rdata & ~mask; | 
 | 207 | 		else | 
 | 208 | 			wdata = rdata | mask; | 
 | 209 |  | 
 | 210 | 		result = ec_write(MSI_STANDARD_EC_COMMAND_ADDRESS, wdata); | 
 | 211 | 		if (result < 0) | 
 | 212 | 			return -EINVAL; | 
 | 213 | 	} | 
 | 214 |  | 
 | 215 | 	return count; | 
 | 216 | } | 
 | 217 |  | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 218 | static int get_wireless_state(int *wlan, int *bluetooth) | 
 | 219 | { | 
 | 220 | 	u8 wdata = 0, rdata; | 
 | 221 | 	int result; | 
 | 222 |  | 
| Thomas Renninger | 1cb7b1e | 2011-03-31 13:36:38 +0200 | [diff] [blame] | 223 | 	result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1); | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 224 | 	if (result < 0) | 
 | 225 | 		return -1; | 
 | 226 |  | 
 | 227 | 	if (wlan) | 
 | 228 | 		*wlan = !!(rdata & 8); | 
 | 229 |  | 
 | 230 | 	if (bluetooth) | 
 | 231 | 		*bluetooth = !!(rdata & 128); | 
 | 232 |  | 
 | 233 | 	return 0; | 
 | 234 | } | 
 | 235 |  | 
| Lee, Chun-Yi | 46d0e9e | 2010-01-09 21:16:52 +0800 | [diff] [blame] | 236 | static int get_wireless_state_ec_standard(void) | 
 | 237 | { | 
 | 238 | 	u8 rdata; | 
 | 239 | 	int result; | 
 | 240 |  | 
 | 241 | 	result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata); | 
 | 242 | 	if (result < 0) | 
 | 243 | 		return -1; | 
 | 244 |  | 
 | 245 | 	wlan_s = !!(rdata & MSI_STANDARD_EC_WLAN_MASK); | 
 | 246 |  | 
 | 247 | 	bluetooth_s = !!(rdata & MSI_STANDARD_EC_BLUETOOTH_MASK); | 
 | 248 |  | 
| Lee, Chun-Yi | fc0dc4c | 2010-01-09 23:17:07 +0800 | [diff] [blame] | 249 | 	threeg_s = !!(rdata & MSI_STANDARD_EC_3G_MASK); | 
 | 250 |  | 
| Lee, Chun-Yi | 46d0e9e | 2010-01-09 21:16:52 +0800 | [diff] [blame] | 251 | 	return 0; | 
 | 252 | } | 
 | 253 |  | 
| Lee, Chun-Yi | e22388e | 2010-01-27 12:23:00 +0800 | [diff] [blame] | 254 | static int get_threeg_exists(void) | 
 | 255 | { | 
 | 256 | 	u8 rdata; | 
 | 257 | 	int result; | 
 | 258 |  | 
 | 259 | 	result = ec_read(MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS, &rdata); | 
 | 260 | 	if (result < 0) | 
 | 261 | 		return -1; | 
 | 262 |  | 
 | 263 | 	threeg_exists = !!(rdata & MSI_STANDARD_EC_3G_MASK); | 
 | 264 |  | 
 | 265 | 	return 0; | 
 | 266 | } | 
 | 267 |  | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 268 | /* Backlight device stuff */ | 
 | 269 |  | 
 | 270 | static int bl_get_brightness(struct backlight_device *b) | 
 | 271 | { | 
 | 272 | 	return get_lcd_level(); | 
 | 273 | } | 
 | 274 |  | 
 | 275 |  | 
 | 276 | static int bl_update_status(struct backlight_device *b) | 
 | 277 | { | 
| Richard Purdie | 599a52d | 2007-02-10 23:07:48 +0000 | [diff] [blame] | 278 | 	return set_lcd_level(b->props.brightness); | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 279 | } | 
 | 280 |  | 
| Greg Kroah-Hartman | 1ac3407 | 2010-05-12 12:03:00 -0700 | [diff] [blame] | 281 | static const struct backlight_ops msibl_ops = { | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 282 | 	.get_brightness = bl_get_brightness, | 
 | 283 | 	.update_status  = bl_update_status, | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 284 | }; | 
 | 285 |  | 
 | 286 | static struct backlight_device *msibl_device; | 
 | 287 |  | 
 | 288 | /* Platform device */ | 
 | 289 |  | 
 | 290 | static ssize_t show_wlan(struct device *dev, | 
 | 291 | 	struct device_attribute *attr, char *buf) | 
 | 292 | { | 
 | 293 |  | 
 | 294 | 	int ret, enabled; | 
 | 295 |  | 
| Lee, Chun-Yi | 46d0e9e | 2010-01-09 21:16:52 +0800 | [diff] [blame] | 296 | 	if (old_ec_model) { | 
 | 297 | 		ret = get_wireless_state(&enabled, NULL); | 
 | 298 | 	} else { | 
 | 299 | 		ret = get_wireless_state_ec_standard(); | 
 | 300 | 		enabled = wlan_s; | 
 | 301 | 	} | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 302 | 	if (ret < 0) | 
 | 303 | 		return ret; | 
 | 304 |  | 
 | 305 | 	return sprintf(buf, "%i\n", enabled); | 
 | 306 | } | 
 | 307 |  | 
| Lee, Chun-Yi | 472ea12 | 2010-01-22 00:15:59 +0800 | [diff] [blame] | 308 | static ssize_t store_wlan(struct device *dev, | 
 | 309 | 	struct device_attribute *attr, const char *buf, size_t count) | 
 | 310 | { | 
 | 311 | 	return set_device_state(buf, count, MSI_STANDARD_EC_WLAN_MASK); | 
 | 312 | } | 
 | 313 |  | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 314 | static ssize_t show_bluetooth(struct device *dev, | 
 | 315 | 	struct device_attribute *attr, char *buf) | 
 | 316 | { | 
 | 317 |  | 
 | 318 | 	int ret, enabled; | 
 | 319 |  | 
| Lee, Chun-Yi | 46d0e9e | 2010-01-09 21:16:52 +0800 | [diff] [blame] | 320 | 	if (old_ec_model) { | 
 | 321 | 		ret = get_wireless_state(NULL, &enabled); | 
 | 322 | 	} else { | 
 | 323 | 		ret = get_wireless_state_ec_standard(); | 
 | 324 | 		enabled = bluetooth_s; | 
 | 325 | 	} | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 326 | 	if (ret < 0) | 
 | 327 | 		return ret; | 
 | 328 |  | 
 | 329 | 	return sprintf(buf, "%i\n", enabled); | 
 | 330 | } | 
 | 331 |  | 
| Lee, Chun-Yi | 472ea12 | 2010-01-22 00:15:59 +0800 | [diff] [blame] | 332 | static ssize_t store_bluetooth(struct device *dev, | 
 | 333 | 	struct device_attribute *attr, const char *buf, size_t count) | 
 | 334 | { | 
 | 335 | 	return set_device_state(buf, count, MSI_STANDARD_EC_BLUETOOTH_MASK); | 
 | 336 | } | 
 | 337 |  | 
| Lee, Chun-Yi | fc0dc4c | 2010-01-09 23:17:07 +0800 | [diff] [blame] | 338 | static ssize_t show_threeg(struct device *dev, | 
 | 339 | 	struct device_attribute *attr, char *buf) | 
 | 340 | { | 
 | 341 |  | 
 | 342 | 	int ret; | 
 | 343 |  | 
 | 344 | 	/* old msi ec not support 3G */ | 
 | 345 | 	if (old_ec_model) | 
 | 346 | 		return -1; | 
 | 347 |  | 
 | 348 | 	ret = get_wireless_state_ec_standard(); | 
 | 349 | 	if (ret < 0) | 
 | 350 | 		return ret; | 
 | 351 |  | 
 | 352 | 	return sprintf(buf, "%i\n", threeg_s); | 
 | 353 | } | 
 | 354 |  | 
| Lee, Chun-Yi | 472ea12 | 2010-01-22 00:15:59 +0800 | [diff] [blame] | 355 | static ssize_t store_threeg(struct device *dev, | 
 | 356 | 	struct device_attribute *attr, const char *buf, size_t count) | 
 | 357 | { | 
 | 358 | 	return set_device_state(buf, count, MSI_STANDARD_EC_3G_MASK); | 
 | 359 | } | 
 | 360 |  | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 361 | static ssize_t show_lcd_level(struct device *dev, | 
 | 362 | 	struct device_attribute *attr, char *buf) | 
 | 363 | { | 
 | 364 |  | 
 | 365 | 	int ret; | 
 | 366 |  | 
 | 367 | 	ret = get_lcd_level(); | 
 | 368 | 	if (ret < 0) | 
 | 369 | 		return ret; | 
 | 370 |  | 
 | 371 | 	return sprintf(buf, "%i\n", ret); | 
 | 372 | } | 
 | 373 |  | 
 | 374 | static ssize_t store_lcd_level(struct device *dev, | 
 | 375 | 	struct device_attribute *attr, const char *buf, size_t count) | 
 | 376 | { | 
 | 377 |  | 
 | 378 | 	int level, ret; | 
 | 379 |  | 
| Greg Kroah-Hartman | 1ac3407 | 2010-05-12 12:03:00 -0700 | [diff] [blame] | 380 | 	if (sscanf(buf, "%i", &level) != 1 || | 
 | 381 | 	    (level < 0 || level >= MSI_LCD_LEVEL_MAX)) | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 382 | 		return -EINVAL; | 
 | 383 |  | 
 | 384 | 	ret = set_lcd_level(level); | 
 | 385 | 	if (ret < 0) | 
 | 386 | 		return ret; | 
 | 387 |  | 
 | 388 | 	return count; | 
 | 389 | } | 
 | 390 |  | 
 | 391 | static ssize_t show_auto_brightness(struct device *dev, | 
 | 392 | 	struct device_attribute *attr, char *buf) | 
 | 393 | { | 
 | 394 |  | 
 | 395 | 	int ret; | 
 | 396 |  | 
 | 397 | 	ret = get_auto_brightness(); | 
 | 398 | 	if (ret < 0) | 
 | 399 | 		return ret; | 
 | 400 |  | 
 | 401 | 	return sprintf(buf, "%i\n", ret); | 
 | 402 | } | 
 | 403 |  | 
 | 404 | static ssize_t store_auto_brightness(struct device *dev, | 
 | 405 | 	struct device_attribute *attr, const char *buf, size_t count) | 
 | 406 | { | 
 | 407 |  | 
 | 408 | 	int enable, ret; | 
 | 409 |  | 
 | 410 | 	if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1))) | 
 | 411 | 		return -EINVAL; | 
 | 412 |  | 
 | 413 | 	ret = set_auto_brightness(enable); | 
 | 414 | 	if (ret < 0) | 
 | 415 | 		return ret; | 
 | 416 |  | 
 | 417 | 	return count; | 
 | 418 | } | 
 | 419 |  | 
 | 420 | static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); | 
| Greg Kroah-Hartman | 1ac3407 | 2010-05-12 12:03:00 -0700 | [diff] [blame] | 421 | static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, | 
 | 422 | 		   store_auto_brightness); | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 423 | static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL); | 
 | 424 | static DEVICE_ATTR(wlan, 0444, show_wlan, NULL); | 
| Lee, Chun-Yi | fc0dc4c | 2010-01-09 23:17:07 +0800 | [diff] [blame] | 425 | static DEVICE_ATTR(threeg, 0444, show_threeg, NULL); | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 426 |  | 
 | 427 | static struct attribute *msipf_attributes[] = { | 
 | 428 | 	&dev_attr_lcd_level.attr, | 
 | 429 | 	&dev_attr_auto_brightness.attr, | 
 | 430 | 	&dev_attr_bluetooth.attr, | 
 | 431 | 	&dev_attr_wlan.attr, | 
 | 432 | 	NULL | 
 | 433 | }; | 
 | 434 |  | 
 | 435 | static struct attribute_group msipf_attribute_group = { | 
 | 436 | 	.attrs = msipf_attributes | 
 | 437 | }; | 
 | 438 |  | 
 | 439 | static struct platform_driver msipf_driver = { | 
 | 440 | 	.driver = { | 
 | 441 | 		.name = "msi-laptop-pf", | 
 | 442 | 		.owner = THIS_MODULE, | 
| Rafael J. Wysocki | 9033132 | 2012-07-06 19:06:19 +0200 | [diff] [blame] | 443 | 		.pm = &msi_laptop_pm, | 
| Lee, Chun-Yi | ec76627 | 2010-01-27 00:13:45 +0800 | [diff] [blame] | 444 | 	}, | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 445 | }; | 
 | 446 |  | 
 | 447 | static struct platform_device *msipf_device; | 
 | 448 |  | 
 | 449 | /* Initialization */ | 
 | 450 |  | 
| Jeff Garzik | 1855256 | 2007-10-03 15:15:40 -0400 | [diff] [blame] | 451 | static int dmi_check_cb(const struct dmi_system_id *id) | 
| Lennart Poettering | 4f5c791 | 2007-05-08 22:07:02 +0200 | [diff] [blame] | 452 | { | 
| Joe Perches | f9dcf19 | 2011-03-29 15:21:46 -0700 | [diff] [blame] | 453 | 	pr_info("Identified laptop model '%s'\n", id->ident); | 
| Axel Lin | 80183a4 | 2010-07-20 15:19:40 -0700 | [diff] [blame] | 454 | 	return 1; | 
| Lennart Poettering | 4f5c791 | 2007-05-08 22:07:02 +0200 | [diff] [blame] | 455 | } | 
 | 456 |  | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 457 | static struct dmi_system_id __initdata msi_dmi_table[] = { | 
 | 458 | 	{ | 
 | 459 | 		.ident = "MSI S270", | 
 | 460 | 		.matches = { | 
 | 461 | 			DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"), | 
 | 462 | 			DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"), | 
| Lennart Poettering | 4f5c791 | 2007-05-08 22:07:02 +0200 | [diff] [blame] | 463 | 			DMI_MATCH(DMI_PRODUCT_VERSION, "0131"), | 
| Greg Kroah-Hartman | 1ac3407 | 2010-05-12 12:03:00 -0700 | [diff] [blame] | 464 | 			DMI_MATCH(DMI_CHASSIS_VENDOR, | 
 | 465 | 				  "MICRO-STAR INT'L CO.,LTD") | 
| Lennart Poettering | 4f5c791 | 2007-05-08 22:07:02 +0200 | [diff] [blame] | 466 | 		}, | 
 | 467 | 		.callback = dmi_check_cb | 
 | 468 | 	}, | 
 | 469 | 	{ | 
 | 470 | 		.ident = "MSI S271", | 
 | 471 | 		.matches = { | 
 | 472 | 			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), | 
 | 473 | 			DMI_MATCH(DMI_PRODUCT_NAME, "MS-1058"), | 
 | 474 | 			DMI_MATCH(DMI_PRODUCT_VERSION, "0581"), | 
 | 475 | 			DMI_MATCH(DMI_BOARD_NAME, "MS-1058") | 
 | 476 | 		}, | 
 | 477 | 		.callback = dmi_check_cb | 
 | 478 | 	}, | 
 | 479 | 	{ | 
 | 480 | 		.ident = "MSI S420", | 
 | 481 | 		.matches = { | 
 | 482 | 			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), | 
 | 483 | 			DMI_MATCH(DMI_PRODUCT_NAME, "MS-1412"), | 
 | 484 | 			DMI_MATCH(DMI_BOARD_VENDOR, "MSI"), | 
 | 485 | 			DMI_MATCH(DMI_BOARD_NAME, "MS-1412") | 
 | 486 | 		}, | 
 | 487 | 		.callback = dmi_check_cb | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 488 | 	}, | 
 | 489 | 	{ | 
 | 490 | 		.ident = "Medion MD96100", | 
 | 491 | 		.matches = { | 
 | 492 | 			DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"), | 
 | 493 | 			DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"), | 
| Lennart Poettering | 4f5c791 | 2007-05-08 22:07:02 +0200 | [diff] [blame] | 494 | 			DMI_MATCH(DMI_PRODUCT_VERSION, "0131"), | 
| Greg Kroah-Hartman | 1ac3407 | 2010-05-12 12:03:00 -0700 | [diff] [blame] | 495 | 			DMI_MATCH(DMI_CHASSIS_VENDOR, | 
 | 496 | 				  "MICRO-STAR INT'L CO.,LTD") | 
| Lennart Poettering | 4f5c791 | 2007-05-08 22:07:02 +0200 | [diff] [blame] | 497 | 		}, | 
 | 498 | 		.callback = dmi_check_cb | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 499 | 	}, | 
| Lee, Chun-Yi | 785cfc0 | 2010-05-19 20:03:05 +0800 | [diff] [blame] | 500 | 	{ } | 
 | 501 | }; | 
 | 502 |  | 
 | 503 | static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = { | 
 | 504 | 	{ | 
 | 505 | 		.ident = "MSI N034", | 
 | 506 | 		.matches = { | 
 | 507 | 			DMI_MATCH(DMI_SYS_VENDOR, | 
 | 508 | 				"MICRO-STAR INTERNATIONAL CO., LTD"), | 
 | 509 | 			DMI_MATCH(DMI_PRODUCT_NAME, "MS-N034"), | 
 | 510 | 			DMI_MATCH(DMI_CHASSIS_VENDOR, | 
 | 511 | 			"MICRO-STAR INTERNATIONAL CO., LTD") | 
 | 512 | 		}, | 
 | 513 | 		.callback = dmi_check_cb | 
 | 514 | 	}, | 
| Lee, Chun-Yi | d0a4aa2 | 2010-05-12 09:58:07 -0700 | [diff] [blame] | 515 | 	{ | 
 | 516 | 		.ident = "MSI N051", | 
 | 517 | 		.matches = { | 
 | 518 | 			DMI_MATCH(DMI_SYS_VENDOR, | 
 | 519 | 				"MICRO-STAR INTERNATIONAL CO., LTD"), | 
 | 520 | 			DMI_MATCH(DMI_PRODUCT_NAME, "MS-N051"), | 
 | 521 | 			DMI_MATCH(DMI_CHASSIS_VENDOR, | 
 | 522 | 			"MICRO-STAR INTERNATIONAL CO., LTD") | 
 | 523 | 		}, | 
 | 524 | 		.callback = dmi_check_cb | 
 | 525 | 	}, | 
 | 526 | 	{ | 
 | 527 | 		.ident = "MSI N014", | 
 | 528 | 		.matches = { | 
 | 529 | 			DMI_MATCH(DMI_SYS_VENDOR, | 
 | 530 | 				"MICRO-STAR INTERNATIONAL CO., LTD"), | 
 | 531 | 			DMI_MATCH(DMI_PRODUCT_NAME, "MS-N014"), | 
 | 532 | 		}, | 
 | 533 | 		.callback = dmi_check_cb | 
 | 534 | 	}, | 
| Lee, Chun-Yi | 1f27e17 | 2010-05-12 09:58:08 -0700 | [diff] [blame] | 535 | 	{ | 
 | 536 | 		.ident = "MSI CR620", | 
 | 537 | 		.matches = { | 
 | 538 | 			DMI_MATCH(DMI_SYS_VENDOR, | 
 | 539 | 				"Micro-Star International"), | 
 | 540 | 			DMI_MATCH(DMI_PRODUCT_NAME, "CR620"), | 
 | 541 | 		}, | 
 | 542 | 		.callback = dmi_check_cb | 
 | 543 | 	}, | 
| Lee, Chun-Yi | 3880314 | 2011-06-10 15:24:26 +0800 | [diff] [blame] | 544 | 	{ | 
 | 545 | 		.ident = "MSI U270", | 
 | 546 | 		.matches = { | 
 | 547 | 			DMI_MATCH(DMI_SYS_VENDOR, | 
 | 548 | 				"Micro-Star International Co., Ltd."), | 
 | 549 | 			DMI_MATCH(DMI_PRODUCT_NAME, "U270 series"), | 
 | 550 | 		}, | 
 | 551 | 		.callback = dmi_check_cb | 
 | 552 | 	}, | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 553 | 	{ } | 
 | 554 | }; | 
 | 555 |  | 
| Lee, Chun-Yi | 472ea12 | 2010-01-22 00:15:59 +0800 | [diff] [blame] | 556 | static int rfkill_bluetooth_set(void *data, bool blocked) | 
 | 557 | { | 
 | 558 | 	/* Do something with blocked...*/ | 
 | 559 | 	/* | 
 | 560 | 	 * blocked == false is on | 
 | 561 | 	 * blocked == true is off | 
 | 562 | 	 */ | 
 | 563 | 	if (blocked) | 
 | 564 | 		set_device_state("0", 0, MSI_STANDARD_EC_BLUETOOTH_MASK); | 
 | 565 | 	else | 
 | 566 | 		set_device_state("1", 0, MSI_STANDARD_EC_BLUETOOTH_MASK); | 
 | 567 |  | 
 | 568 | 	return 0; | 
 | 569 | } | 
 | 570 |  | 
 | 571 | static int rfkill_wlan_set(void *data, bool blocked) | 
 | 572 | { | 
 | 573 | 	if (blocked) | 
 | 574 | 		set_device_state("0", 0, MSI_STANDARD_EC_WLAN_MASK); | 
 | 575 | 	else | 
 | 576 | 		set_device_state("1", 0, MSI_STANDARD_EC_WLAN_MASK); | 
 | 577 |  | 
 | 578 | 	return 0; | 
 | 579 | } | 
 | 580 |  | 
 | 581 | static int rfkill_threeg_set(void *data, bool blocked) | 
 | 582 | { | 
 | 583 | 	if (blocked) | 
 | 584 | 		set_device_state("0", 0, MSI_STANDARD_EC_3G_MASK); | 
 | 585 | 	else | 
 | 586 | 		set_device_state("1", 0, MSI_STANDARD_EC_3G_MASK); | 
 | 587 |  | 
 | 588 | 	return 0; | 
 | 589 | } | 
 | 590 |  | 
| Axel Lin | e38f052 | 2010-07-20 15:19:46 -0700 | [diff] [blame] | 591 | static const struct rfkill_ops rfkill_bluetooth_ops = { | 
| Lee, Chun-Yi | 472ea12 | 2010-01-22 00:15:59 +0800 | [diff] [blame] | 592 | 	.set_block = rfkill_bluetooth_set | 
 | 593 | }; | 
 | 594 |  | 
| Axel Lin | e38f052 | 2010-07-20 15:19:46 -0700 | [diff] [blame] | 595 | static const struct rfkill_ops rfkill_wlan_ops = { | 
| Lee, Chun-Yi | 472ea12 | 2010-01-22 00:15:59 +0800 | [diff] [blame] | 596 | 	.set_block = rfkill_wlan_set | 
 | 597 | }; | 
 | 598 |  | 
| Axel Lin | e38f052 | 2010-07-20 15:19:46 -0700 | [diff] [blame] | 599 | static const struct rfkill_ops rfkill_threeg_ops = { | 
| Lee, Chun-Yi | 472ea12 | 2010-01-22 00:15:59 +0800 | [diff] [blame] | 600 | 	.set_block = rfkill_threeg_set | 
 | 601 | }; | 
 | 602 |  | 
 | 603 | static void rfkill_cleanup(void) | 
 | 604 | { | 
 | 605 | 	if (rfk_bluetooth) { | 
 | 606 | 		rfkill_unregister(rfk_bluetooth); | 
 | 607 | 		rfkill_destroy(rfk_bluetooth); | 
 | 608 | 	} | 
 | 609 |  | 
 | 610 | 	if (rfk_threeg) { | 
 | 611 | 		rfkill_unregister(rfk_threeg); | 
 | 612 | 		rfkill_destroy(rfk_threeg); | 
 | 613 | 	} | 
 | 614 |  | 
 | 615 | 	if (rfk_wlan) { | 
 | 616 | 		rfkill_unregister(rfk_wlan); | 
 | 617 | 		rfkill_destroy(rfk_wlan); | 
 | 618 | 	} | 
 | 619 | } | 
 | 620 |  | 
| Lee, Chun-Yi | 339e753 | 2010-05-12 09:58:10 -0700 | [diff] [blame] | 621 | static void msi_update_rfkill(struct work_struct *ignored) | 
 | 622 | { | 
 | 623 | 	get_wireless_state_ec_standard(); | 
 | 624 |  | 
 | 625 | 	if (rfk_wlan) | 
 | 626 | 		rfkill_set_sw_state(rfk_wlan, !wlan_s); | 
 | 627 | 	if (rfk_bluetooth) | 
 | 628 | 		rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s); | 
 | 629 | 	if (rfk_threeg) | 
 | 630 | 		rfkill_set_sw_state(rfk_threeg, !threeg_s); | 
 | 631 | } | 
 | 632 | static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill); | 
 | 633 |  | 
| Lee, Chun-Yi | 143a4c0 | 2011-03-07 15:46:28 +0800 | [diff] [blame] | 634 | static void msi_send_touchpad_key(struct work_struct *ignored) | 
 | 635 | { | 
 | 636 | 	u8 rdata; | 
 | 637 | 	int result; | 
 | 638 |  | 
 | 639 | 	result = ec_read(MSI_STANDARD_EC_TOUCHPAD_ADDRESS, &rdata); | 
 | 640 | 	if (result < 0) | 
 | 641 | 		return; | 
 | 642 |  | 
 | 643 | 	sparse_keymap_report_event(msi_laptop_input_dev, | 
 | 644 | 		(rdata & MSI_STANDARD_EC_TOUCHPAD_MASK) ? | 
 | 645 | 		KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF, 1, true); | 
 | 646 | } | 
 | 647 | static DECLARE_DELAYED_WORK(msi_touchpad_work, msi_send_touchpad_key); | 
 | 648 |  | 
| Lee, Chun-Yi | 339e753 | 2010-05-12 09:58:10 -0700 | [diff] [blame] | 649 | static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, | 
 | 650 | 				struct serio *port) | 
 | 651 | { | 
 | 652 | 	static bool extended; | 
 | 653 |  | 
 | 654 | 	if (str & 0x20) | 
 | 655 | 		return false; | 
 | 656 |  | 
| Lee, Chun-Yi | 143a4c0 | 2011-03-07 15:46:28 +0800 | [diff] [blame] | 657 | 	/* 0x54 wwan, 0x62 bluetooth, 0x76 wlan, 0xE4 touchpad toggle*/ | 
| Lee, Chun-Yi | 339e753 | 2010-05-12 09:58:10 -0700 | [diff] [blame] | 658 | 	if (unlikely(data == 0xe0)) { | 
 | 659 | 		extended = true; | 
 | 660 | 		return false; | 
 | 661 | 	} else if (unlikely(extended)) { | 
| Lee, Chun-Yi | 143a4c0 | 2011-03-07 15:46:28 +0800 | [diff] [blame] | 662 | 		extended = false; | 
| Lee, Chun-Yi | 339e753 | 2010-05-12 09:58:10 -0700 | [diff] [blame] | 663 | 		switch (data) { | 
| Lee, Chun-Yi | 143a4c0 | 2011-03-07 15:46:28 +0800 | [diff] [blame] | 664 | 		case 0xE4: | 
 | 665 | 			schedule_delayed_work(&msi_touchpad_work, | 
 | 666 | 				round_jiffies_relative(0.5 * HZ)); | 
 | 667 | 			break; | 
| Lee, Chun-Yi | 339e753 | 2010-05-12 09:58:10 -0700 | [diff] [blame] | 668 | 		case 0x54: | 
 | 669 | 		case 0x62: | 
 | 670 | 		case 0x76: | 
 | 671 | 			schedule_delayed_work(&msi_rfkill_work, | 
 | 672 | 				round_jiffies_relative(0.5 * HZ)); | 
 | 673 | 			break; | 
 | 674 | 		} | 
| Lee, Chun-Yi | 339e753 | 2010-05-12 09:58:10 -0700 | [diff] [blame] | 675 | 	} | 
 | 676 |  | 
 | 677 | 	return false; | 
 | 678 | } | 
 | 679 |  | 
| Lee, Chun-Yi | 3bb9702 | 2010-05-12 09:58:09 -0700 | [diff] [blame] | 680 | static void msi_init_rfkill(struct work_struct *ignored) | 
 | 681 | { | 
 | 682 | 	if (rfk_wlan) { | 
 | 683 | 		rfkill_set_sw_state(rfk_wlan, !wlan_s); | 
 | 684 | 		rfkill_wlan_set(NULL, !wlan_s); | 
 | 685 | 	} | 
 | 686 | 	if (rfk_bluetooth) { | 
 | 687 | 		rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s); | 
 | 688 | 		rfkill_bluetooth_set(NULL, !bluetooth_s); | 
 | 689 | 	} | 
 | 690 | 	if (rfk_threeg) { | 
 | 691 | 		rfkill_set_sw_state(rfk_threeg, !threeg_s); | 
 | 692 | 		rfkill_threeg_set(NULL, !threeg_s); | 
 | 693 | 	} | 
 | 694 | } | 
 | 695 | static DECLARE_DELAYED_WORK(msi_rfkill_init, msi_init_rfkill); | 
 | 696 |  | 
| Lee, Chun-Yi | 472ea12 | 2010-01-22 00:15:59 +0800 | [diff] [blame] | 697 | static int rfkill_init(struct platform_device *sdev) | 
 | 698 | { | 
 | 699 | 	/* add rfkill */ | 
 | 700 | 	int retval; | 
 | 701 |  | 
| Lee, Chun-Yi | 3bb9702 | 2010-05-12 09:58:09 -0700 | [diff] [blame] | 702 | 	/* keep the hardware wireless state */ | 
 | 703 | 	get_wireless_state_ec_standard(); | 
 | 704 |  | 
| Lee, Chun-Yi | 472ea12 | 2010-01-22 00:15:59 +0800 | [diff] [blame] | 705 | 	rfk_bluetooth = rfkill_alloc("msi-bluetooth", &sdev->dev, | 
 | 706 | 				RFKILL_TYPE_BLUETOOTH, | 
 | 707 | 				&rfkill_bluetooth_ops, NULL); | 
 | 708 | 	if (!rfk_bluetooth) { | 
 | 709 | 		retval = -ENOMEM; | 
 | 710 | 		goto err_bluetooth; | 
 | 711 | 	} | 
 | 712 | 	retval = rfkill_register(rfk_bluetooth); | 
 | 713 | 	if (retval) | 
 | 714 | 		goto err_bluetooth; | 
 | 715 |  | 
 | 716 | 	rfk_wlan = rfkill_alloc("msi-wlan", &sdev->dev, RFKILL_TYPE_WLAN, | 
 | 717 | 				&rfkill_wlan_ops, NULL); | 
 | 718 | 	if (!rfk_wlan) { | 
 | 719 | 		retval = -ENOMEM; | 
 | 720 | 		goto err_wlan; | 
 | 721 | 	} | 
 | 722 | 	retval = rfkill_register(rfk_wlan); | 
 | 723 | 	if (retval) | 
 | 724 | 		goto err_wlan; | 
 | 725 |  | 
| Lee, Chun-Yi | e22388e | 2010-01-27 12:23:00 +0800 | [diff] [blame] | 726 | 	if (threeg_exists) { | 
 | 727 | 		rfk_threeg = rfkill_alloc("msi-threeg", &sdev->dev, | 
 | 728 | 				RFKILL_TYPE_WWAN, &rfkill_threeg_ops, NULL); | 
 | 729 | 		if (!rfk_threeg) { | 
 | 730 | 			retval = -ENOMEM; | 
 | 731 | 			goto err_threeg; | 
 | 732 | 		} | 
 | 733 | 		retval = rfkill_register(rfk_threeg); | 
 | 734 | 		if (retval) | 
 | 735 | 			goto err_threeg; | 
| Lee, Chun-Yi | 472ea12 | 2010-01-22 00:15:59 +0800 | [diff] [blame] | 736 | 	} | 
| Lee, Chun-Yi | 472ea12 | 2010-01-22 00:15:59 +0800 | [diff] [blame] | 737 |  | 
| Lee, Chun-Yi | 3bb9702 | 2010-05-12 09:58:09 -0700 | [diff] [blame] | 738 | 	/* schedule to run rfkill state initial */ | 
 | 739 | 	schedule_delayed_work(&msi_rfkill_init, | 
 | 740 | 				round_jiffies_relative(1 * HZ)); | 
 | 741 |  | 
| Lee, Chun-Yi | 472ea12 | 2010-01-22 00:15:59 +0800 | [diff] [blame] | 742 | 	return 0; | 
 | 743 |  | 
 | 744 | err_threeg: | 
 | 745 | 	rfkill_destroy(rfk_threeg); | 
 | 746 | 	if (rfk_wlan) | 
 | 747 | 		rfkill_unregister(rfk_wlan); | 
 | 748 | err_wlan: | 
 | 749 | 	rfkill_destroy(rfk_wlan); | 
 | 750 | 	if (rfk_bluetooth) | 
 | 751 | 		rfkill_unregister(rfk_bluetooth); | 
 | 752 | err_bluetooth: | 
 | 753 | 	rfkill_destroy(rfk_bluetooth); | 
 | 754 |  | 
 | 755 | 	return retval; | 
 | 756 | } | 
 | 757 |  | 
| Rafael J. Wysocki | 3567a4e | 2012-08-09 23:00:13 +0200 | [diff] [blame] | 758 | #ifdef CONFIG_PM_SLEEP | 
| Rafael J. Wysocki | 9033132 | 2012-07-06 19:06:19 +0200 | [diff] [blame] | 759 | static int msi_laptop_resume(struct device *device) | 
| Lee, Chun-Yi | ec76627 | 2010-01-27 00:13:45 +0800 | [diff] [blame] | 760 | { | 
 | 761 | 	u8 data; | 
 | 762 | 	int result; | 
 | 763 |  | 
 | 764 | 	if (!load_scm_model) | 
 | 765 | 		return 0; | 
 | 766 |  | 
 | 767 | 	/* set load SCM to disable hardware control by fn key */ | 
 | 768 | 	result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data); | 
 | 769 | 	if (result < 0) | 
 | 770 | 		return result; | 
 | 771 |  | 
 | 772 | 	result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, | 
 | 773 | 		data | MSI_STANDARD_EC_SCM_LOAD_MASK); | 
 | 774 | 	if (result < 0) | 
 | 775 | 		return result; | 
 | 776 |  | 
 | 777 | 	return 0; | 
 | 778 | } | 
| Rafael J. Wysocki | 3567a4e | 2012-08-09 23:00:13 +0200 | [diff] [blame] | 779 | #endif | 
| Lee, Chun-Yi | ec76627 | 2010-01-27 00:13:45 +0800 | [diff] [blame] | 780 |  | 
| Lee, Chun-Yi | 143a4c0 | 2011-03-07 15:46:28 +0800 | [diff] [blame] | 781 | static int __init msi_laptop_input_setup(void) | 
 | 782 | { | 
 | 783 | 	int err; | 
 | 784 |  | 
 | 785 | 	msi_laptop_input_dev = input_allocate_device(); | 
 | 786 | 	if (!msi_laptop_input_dev) | 
 | 787 | 		return -ENOMEM; | 
 | 788 |  | 
 | 789 | 	msi_laptop_input_dev->name = "MSI Laptop hotkeys"; | 
 | 790 | 	msi_laptop_input_dev->phys = "msi-laptop/input0"; | 
 | 791 | 	msi_laptop_input_dev->id.bustype = BUS_HOST; | 
 | 792 |  | 
 | 793 | 	err = sparse_keymap_setup(msi_laptop_input_dev, | 
 | 794 | 		msi_laptop_keymap, NULL); | 
 | 795 | 	if (err) | 
 | 796 | 		goto err_free_dev; | 
 | 797 |  | 
 | 798 | 	err = input_register_device(msi_laptop_input_dev); | 
 | 799 | 	if (err) | 
 | 800 | 		goto err_free_keymap; | 
 | 801 |  | 
 | 802 | 	return 0; | 
 | 803 |  | 
 | 804 | err_free_keymap: | 
 | 805 | 	sparse_keymap_free(msi_laptop_input_dev); | 
 | 806 | err_free_dev: | 
 | 807 | 	input_free_device(msi_laptop_input_dev); | 
 | 808 | 	return err; | 
 | 809 | } | 
 | 810 |  | 
 | 811 | static void msi_laptop_input_destroy(void) | 
 | 812 | { | 
 | 813 | 	sparse_keymap_free(msi_laptop_input_dev); | 
 | 814 | 	input_unregister_device(msi_laptop_input_dev); | 
 | 815 | } | 
 | 816 |  | 
| Lee, Chun-Yi | d436514 | 2011-05-26 11:09:19 +0800 | [diff] [blame] | 817 | static int __init load_scm_model_init(struct platform_device *sdev) | 
| Lee, Chun-Yi | 472ea12 | 2010-01-22 00:15:59 +0800 | [diff] [blame] | 818 | { | 
 | 819 | 	u8 data; | 
 | 820 | 	int result; | 
 | 821 |  | 
 | 822 | 	/* allow userland write sysfs file  */ | 
 | 823 | 	dev_attr_bluetooth.store = store_bluetooth; | 
 | 824 | 	dev_attr_wlan.store = store_wlan; | 
 | 825 | 	dev_attr_threeg.store = store_threeg; | 
 | 826 | 	dev_attr_bluetooth.attr.mode |= S_IWUSR; | 
 | 827 | 	dev_attr_wlan.attr.mode |= S_IWUSR; | 
 | 828 | 	dev_attr_threeg.attr.mode |= S_IWUSR; | 
 | 829 |  | 
 | 830 | 	/* disable hardware control by fn key */ | 
 | 831 | 	result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data); | 
 | 832 | 	if (result < 0) | 
 | 833 | 		return result; | 
 | 834 |  | 
 | 835 | 	result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, | 
 | 836 | 		data | MSI_STANDARD_EC_SCM_LOAD_MASK); | 
 | 837 | 	if (result < 0) | 
 | 838 | 		return result; | 
 | 839 |  | 
 | 840 | 	/* initial rfkill */ | 
 | 841 | 	result = rfkill_init(sdev); | 
 | 842 | 	if (result < 0) | 
| Lee, Chun-Yi | 339e753 | 2010-05-12 09:58:10 -0700 | [diff] [blame] | 843 | 		goto fail_rfkill; | 
 | 844 |  | 
| Lee, Chun-Yi | 143a4c0 | 2011-03-07 15:46:28 +0800 | [diff] [blame] | 845 | 	/* setup input device */ | 
 | 846 | 	result = msi_laptop_input_setup(); | 
 | 847 | 	if (result) | 
 | 848 | 		goto fail_input; | 
 | 849 |  | 
| Lee, Chun-Yi | 339e753 | 2010-05-12 09:58:10 -0700 | [diff] [blame] | 850 | 	result = i8042_install_filter(msi_laptop_i8042_filter); | 
 | 851 | 	if (result) { | 
| Joey Lee | bbe24fe | 2011-03-16 01:55:19 -0600 | [diff] [blame] | 852 | 		pr_err("Unable to install key filter\n"); | 
| Lee, Chun-Yi | 339e753 | 2010-05-12 09:58:10 -0700 | [diff] [blame] | 853 | 		goto fail_filter; | 
 | 854 | 	} | 
| Lee, Chun-Yi | 472ea12 | 2010-01-22 00:15:59 +0800 | [diff] [blame] | 855 |  | 
 | 856 | 	return 0; | 
| Lee, Chun-Yi | 339e753 | 2010-05-12 09:58:10 -0700 | [diff] [blame] | 857 |  | 
 | 858 | fail_filter: | 
| Lee, Chun-Yi | 143a4c0 | 2011-03-07 15:46:28 +0800 | [diff] [blame] | 859 | 	msi_laptop_input_destroy(); | 
 | 860 |  | 
 | 861 | fail_input: | 
| Lee, Chun-Yi | 339e753 | 2010-05-12 09:58:10 -0700 | [diff] [blame] | 862 | 	rfkill_cleanup(); | 
 | 863 |  | 
 | 864 | fail_rfkill: | 
 | 865 |  | 
 | 866 | 	return result; | 
 | 867 |  | 
| Lee, Chun-Yi | 472ea12 | 2010-01-22 00:15:59 +0800 | [diff] [blame] | 868 | } | 
 | 869 |  | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 870 | static int __init msi_init(void) | 
 | 871 | { | 
 | 872 | 	int ret; | 
 | 873 |  | 
 | 874 | 	if (acpi_disabled) | 
 | 875 | 		return -ENODEV; | 
 | 876 |  | 
| Lee, Chun-Yi | 46d0e9e | 2010-01-09 21:16:52 +0800 | [diff] [blame] | 877 | 	if (force || dmi_check_system(msi_dmi_table)) | 
 | 878 | 		old_ec_model = 1; | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 879 |  | 
| Lee, Chun-Yi | e22388e | 2010-01-27 12:23:00 +0800 | [diff] [blame] | 880 | 	if (!old_ec_model) | 
 | 881 | 		get_threeg_exists(); | 
 | 882 |  | 
| Lee, Chun-Yi | 472ea12 | 2010-01-22 00:15:59 +0800 | [diff] [blame] | 883 | 	if (!old_ec_model && dmi_check_system(msi_load_scm_models_dmi_table)) | 
 | 884 | 		load_scm_model = 1; | 
 | 885 |  | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 886 | 	if (auto_brightness < 0 || auto_brightness > 2) | 
 | 887 | 		return -EINVAL; | 
 | 888 |  | 
 | 889 | 	/* Register backlight stuff */ | 
 | 890 |  | 
| Thomas Renninger | a598c82 | 2008-08-01 17:38:01 +0200 | [diff] [blame] | 891 | 	if (acpi_video_backlight_support()) { | 
| Joe Perches | f9dcf19 | 2011-03-29 15:21:46 -0700 | [diff] [blame] | 892 | 		pr_info("Brightness ignored, must be controlled by ACPI video driver\n"); | 
| Thomas Renninger | a598c82 | 2008-08-01 17:38:01 +0200 | [diff] [blame] | 893 | 	} else { | 
| Matthew Garrett | a19a6ee | 2010-02-17 16:39:44 -0500 | [diff] [blame] | 894 | 		struct backlight_properties props; | 
 | 895 | 		memset(&props, 0, sizeof(struct backlight_properties)); | 
| Matthew Garrett | bb7ca74 | 2011-03-22 16:30:21 -0700 | [diff] [blame] | 896 | 		props.type = BACKLIGHT_PLATFORM; | 
| Matthew Garrett | a19a6ee | 2010-02-17 16:39:44 -0500 | [diff] [blame] | 897 | 		props.max_brightness = MSI_LCD_LEVEL_MAX - 1; | 
| Thomas Renninger | a598c82 | 2008-08-01 17:38:01 +0200 | [diff] [blame] | 898 | 		msibl_device = backlight_device_register("msi-laptop-bl", NULL, | 
| Matthew Garrett | a19a6ee | 2010-02-17 16:39:44 -0500 | [diff] [blame] | 899 | 							 NULL, &msibl_ops, | 
 | 900 | 							 &props); | 
| Thomas Renninger | a598c82 | 2008-08-01 17:38:01 +0200 | [diff] [blame] | 901 | 		if (IS_ERR(msibl_device)) | 
 | 902 | 			return PTR_ERR(msibl_device); | 
| Thomas Renninger | a598c82 | 2008-08-01 17:38:01 +0200 | [diff] [blame] | 903 | 	} | 
| Richard Purdie | 599a52d | 2007-02-10 23:07:48 +0000 | [diff] [blame] | 904 |  | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 905 | 	ret = platform_driver_register(&msipf_driver); | 
 | 906 | 	if (ret) | 
 | 907 | 		goto fail_backlight; | 
 | 908 |  | 
 | 909 | 	/* Register platform stuff */ | 
 | 910 |  | 
 | 911 | 	msipf_device = platform_device_alloc("msi-laptop-pf", -1); | 
 | 912 | 	if (!msipf_device) { | 
 | 913 | 		ret = -ENOMEM; | 
 | 914 | 		goto fail_platform_driver; | 
 | 915 | 	} | 
 | 916 |  | 
 | 917 | 	ret = platform_device_add(msipf_device); | 
 | 918 | 	if (ret) | 
 | 919 | 		goto fail_platform_device1; | 
 | 920 |  | 
| Lee, Chun-Yi | 472ea12 | 2010-01-22 00:15:59 +0800 | [diff] [blame] | 921 | 	if (load_scm_model && (load_scm_model_init(msipf_device) < 0)) { | 
 | 922 | 		ret = -EINVAL; | 
 | 923 | 		goto fail_platform_device1; | 
 | 924 | 	} | 
 | 925 |  | 
| Greg Kroah-Hartman | 1ac3407 | 2010-05-12 12:03:00 -0700 | [diff] [blame] | 926 | 	ret = sysfs_create_group(&msipf_device->dev.kobj, | 
 | 927 | 				 &msipf_attribute_group); | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 928 | 	if (ret) | 
 | 929 | 		goto fail_platform_device2; | 
 | 930 |  | 
| Lee, Chun-Yi | fc0dc4c | 2010-01-09 23:17:07 +0800 | [diff] [blame] | 931 | 	if (!old_ec_model) { | 
| Lee, Chun-Yi | e22388e | 2010-01-27 12:23:00 +0800 | [diff] [blame] | 932 | 		if (threeg_exists) | 
 | 933 | 			ret = device_create_file(&msipf_device->dev, | 
 | 934 | 						&dev_attr_threeg); | 
| Lee, Chun-Yi | fc0dc4c | 2010-01-09 23:17:07 +0800 | [diff] [blame] | 935 | 		if (ret) | 
 | 936 | 			goto fail_platform_device2; | 
 | 937 | 	} | 
 | 938 |  | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 939 | 	/* Disable automatic brightness control by default because | 
 | 940 | 	 * this module was probably loaded to do brightness control in | 
 | 941 | 	 * software. */ | 
 | 942 |  | 
 | 943 | 	if (auto_brightness != 2) | 
 | 944 | 		set_auto_brightness(auto_brightness); | 
 | 945 |  | 
| Joe Perches | f9dcf19 | 2011-03-29 15:21:46 -0700 | [diff] [blame] | 946 | 	pr_info("driver " MSI_DRIVER_VERSION " successfully loaded\n"); | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 947 |  | 
 | 948 | 	return 0; | 
 | 949 |  | 
 | 950 | fail_platform_device2: | 
 | 951 |  | 
| Lee, Chun-Yi | 7ab5252 | 2010-05-15 06:18:54 +0800 | [diff] [blame] | 952 | 	if (load_scm_model) { | 
 | 953 | 		i8042_remove_filter(msi_laptop_i8042_filter); | 
 | 954 | 		cancel_delayed_work_sync(&msi_rfkill_work); | 
 | 955 | 		rfkill_cleanup(); | 
 | 956 | 	} | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 957 | 	platform_device_del(msipf_device); | 
 | 958 |  | 
 | 959 | fail_platform_device1: | 
 | 960 |  | 
 | 961 | 	platform_device_put(msipf_device); | 
 | 962 |  | 
 | 963 | fail_platform_driver: | 
 | 964 |  | 
 | 965 | 	platform_driver_unregister(&msipf_driver); | 
 | 966 |  | 
 | 967 | fail_backlight: | 
 | 968 |  | 
 | 969 | 	backlight_device_unregister(msibl_device); | 
 | 970 |  | 
 | 971 | 	return ret; | 
 | 972 | } | 
 | 973 |  | 
 | 974 | static void __exit msi_cleanup(void) | 
 | 975 | { | 
| Lee, Chun-Yi | 7ab5252 | 2010-05-15 06:18:54 +0800 | [diff] [blame] | 976 | 	if (load_scm_model) { | 
 | 977 | 		i8042_remove_filter(msi_laptop_i8042_filter); | 
| Lee, Chun-Yi | 143a4c0 | 2011-03-07 15:46:28 +0800 | [diff] [blame] | 978 | 		msi_laptop_input_destroy(); | 
| Lee, Chun-Yi | 7ab5252 | 2010-05-15 06:18:54 +0800 | [diff] [blame] | 979 | 		cancel_delayed_work_sync(&msi_rfkill_work); | 
 | 980 | 		rfkill_cleanup(); | 
 | 981 | 	} | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 982 |  | 
 | 983 | 	sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group); | 
| Lee, Chun-Yi | e22388e | 2010-01-27 12:23:00 +0800 | [diff] [blame] | 984 | 	if (!old_ec_model && threeg_exists) | 
| Lee, Chun-Yi | fc0dc4c | 2010-01-09 23:17:07 +0800 | [diff] [blame] | 985 | 		device_remove_file(&msipf_device->dev, &dev_attr_threeg); | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 986 | 	platform_device_unregister(msipf_device); | 
 | 987 | 	platform_driver_unregister(&msipf_driver); | 
 | 988 | 	backlight_device_unregister(msibl_device); | 
 | 989 |  | 
 | 990 | 	/* Enable automatic brightness control again */ | 
 | 991 | 	if (auto_brightness != 2) | 
 | 992 | 		set_auto_brightness(1); | 
 | 993 |  | 
| Joe Perches | f9dcf19 | 2011-03-29 15:21:46 -0700 | [diff] [blame] | 994 | 	pr_info("driver unloaded\n"); | 
| Lennart Poettering | 8c4c731 | 2006-10-06 01:27:02 -0400 | [diff] [blame] | 995 | } | 
 | 996 |  | 
 | 997 | module_init(msi_init); | 
 | 998 | module_exit(msi_cleanup); | 
 | 999 |  | 
 | 1000 | MODULE_AUTHOR("Lennart Poettering"); | 
 | 1001 | MODULE_DESCRIPTION("MSI Laptop Support"); | 
 | 1002 | MODULE_VERSION(MSI_DRIVER_VERSION); | 
 | 1003 | MODULE_LICENSE("GPL"); | 
| Lennart Poettering | 4f5c791 | 2007-05-08 22:07:02 +0200 | [diff] [blame] | 1004 |  | 
 | 1005 | MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); | 
 | 1006 | MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*"); | 
 | 1007 | MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); | 
 | 1008 | MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); | 
| Lee, Chun-Yi | 46d0e9e | 2010-01-09 21:16:52 +0800 | [diff] [blame] | 1009 | MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N034:*"); | 
| Lee, Chun-Yi | d0a4aa2 | 2010-05-12 09:58:07 -0700 | [diff] [blame] | 1010 | MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N051:*"); | 
 | 1011 | MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*"); | 
| Lee, Chun-Yi | 1f27e17 | 2010-05-12 09:58:08 -0700 | [diff] [blame] | 1012 | MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*"); | 
| Lee, Chun-Yi | 3880314 | 2011-06-10 15:24:26 +0800 | [diff] [blame] | 1013 | MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnU270series:*"); |