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