summaryrefslogtreecommitdiff
path: root/drivers/platform/x86/msi-laptop.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86/msi-laptop.c')
-rw-r--r--drivers/platform/x86/msi-laptop.c95
1 files changed, 86 insertions, 9 deletions
diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c
index 142d385..23fb2af 100644
--- a/drivers/platform/x86/msi-laptop.c
+++ b/drivers/platform/x86/msi-laptop.c
@@ -51,6 +51,8 @@
* laptop as MSI S270. YMMV.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
@@ -60,6 +62,8 @@
#include <linux/platform_device.h>
#include <linux/rfkill.h>
#include <linux/i8042.h>
+#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
#define MSI_DRIVER_VERSION "0.5"
@@ -78,6 +82,9 @@
#define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d
#define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0)
+#define MSI_STANDARD_EC_TOUCHPAD_ADDRESS 0xe4
+#define MSI_STANDARD_EC_TOUCHPAD_MASK (1 << 4)
+
static int msi_laptop_resume(struct platform_device *device);
#define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f
@@ -90,6 +97,14 @@ static int auto_brightness;
module_param(auto_brightness, int, 0);
MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)");
+static const struct key_entry msi_laptop_keymap[] = {
+ {KE_KEY, KEY_TOUCHPAD_ON, {KEY_TOUCHPAD_ON} }, /* Touch Pad On */
+ {KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} },/* Touch Pad On */
+ {KE_END, 0}
+};
+
+static struct input_dev *msi_laptop_input_dev;
+
static bool old_ec_model;
static int wlan_s, bluetooth_s, threeg_s;
static int threeg_exists;
@@ -432,8 +447,7 @@ static struct platform_device *msipf_device;
static int dmi_check_cb(const struct dmi_system_id *id)
{
- printk(KERN_INFO "msi-laptop: Identified laptop model '%s'.\n",
- id->ident);
+ pr_info("Identified laptop model '%s'.\n", id->ident);
return 1;
}
@@ -605,6 +619,21 @@ static void msi_update_rfkill(struct work_struct *ignored)
}
static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill);
+static void msi_send_touchpad_key(struct work_struct *ignored)
+{
+ u8 rdata;
+ int result;
+
+ result = ec_read(MSI_STANDARD_EC_TOUCHPAD_ADDRESS, &rdata);
+ if (result < 0)
+ return;
+
+ sparse_keymap_report_event(msi_laptop_input_dev,
+ (rdata & MSI_STANDARD_EC_TOUCHPAD_MASK) ?
+ KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF, 1, true);
+}
+static DECLARE_DELAYED_WORK(msi_touchpad_work, msi_send_touchpad_key);
+
static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
struct serio *port)
{
@@ -613,12 +642,17 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
if (str & 0x20)
return false;
- /* 0x54 wwan, 0x62 bluetooth, 0x76 wlan*/
+ /* 0x54 wwan, 0x62 bluetooth, 0x76 wlan, 0xE4 touchpad toggle*/
if (unlikely(data == 0xe0)) {
extended = true;
return false;
} else if (unlikely(extended)) {
+ extended = false;
switch (data) {
+ case 0xE4:
+ schedule_delayed_work(&msi_touchpad_work,
+ round_jiffies_relative(0.5 * HZ));
+ break;
case 0x54:
case 0x62:
case 0x76:
@@ -626,7 +660,6 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
round_jiffies_relative(0.5 * HZ));
break;
}
- extended = false;
}
return false;
@@ -731,6 +764,42 @@ static int msi_laptop_resume(struct platform_device *device)
return 0;
}
+static int __init msi_laptop_input_setup(void)
+{
+ int err;
+
+ msi_laptop_input_dev = input_allocate_device();
+ if (!msi_laptop_input_dev)
+ return -ENOMEM;
+
+ msi_laptop_input_dev->name = "MSI Laptop hotkeys";
+ msi_laptop_input_dev->phys = "msi-laptop/input0";
+ msi_laptop_input_dev->id.bustype = BUS_HOST;
+
+ err = sparse_keymap_setup(msi_laptop_input_dev,
+ msi_laptop_keymap, NULL);
+ if (err)
+ goto err_free_dev;
+
+ err = input_register_device(msi_laptop_input_dev);
+ if (err)
+ goto err_free_keymap;
+
+ return 0;
+
+err_free_keymap:
+ sparse_keymap_free(msi_laptop_input_dev);
+err_free_dev:
+ input_free_device(msi_laptop_input_dev);
+ return err;
+}
+
+static void msi_laptop_input_destroy(void)
+{
+ sparse_keymap_free(msi_laptop_input_dev);
+ input_unregister_device(msi_laptop_input_dev);
+}
+
static int load_scm_model_init(struct platform_device *sdev)
{
u8 data;
@@ -759,16 +828,23 @@ static int load_scm_model_init(struct platform_device *sdev)
if (result < 0)
goto fail_rfkill;
+ /* setup input device */
+ result = msi_laptop_input_setup();
+ if (result)
+ goto fail_input;
+
result = i8042_install_filter(msi_laptop_i8042_filter);
if (result) {
- printk(KERN_ERR
- "msi-laptop: Unable to install key filter\n");
+ pr_err("Unable to install key filter\n");
goto fail_filter;
}
return 0;
fail_filter:
+ msi_laptop_input_destroy();
+
+fail_input:
rfkill_cleanup();
fail_rfkill:
@@ -799,7 +875,7 @@ static int __init msi_init(void)
/* Register backlight stuff */
if (acpi_video_backlight_support()) {
- printk(KERN_INFO "MSI: Brightness ignored, must be controlled "
+ pr_info("Brightness ignored, must be controlled "
"by ACPI video driver\n");
} else {
struct backlight_properties props;
@@ -854,7 +930,7 @@ static int __init msi_init(void)
if (auto_brightness != 2)
set_auto_brightness(auto_brightness);
- printk(KERN_INFO "msi-laptop: driver "MSI_DRIVER_VERSION" successfully loaded.\n");
+ pr_info("driver "MSI_DRIVER_VERSION" successfully loaded.\n");
return 0;
@@ -886,6 +962,7 @@ static void __exit msi_cleanup(void)
{
if (load_scm_model) {
i8042_remove_filter(msi_laptop_i8042_filter);
+ msi_laptop_input_destroy();
cancel_delayed_work_sync(&msi_rfkill_work);
rfkill_cleanup();
}
@@ -901,7 +978,7 @@ static void __exit msi_cleanup(void)
if (auto_brightness != 2)
set_auto_brightness(1);
- printk(KERN_INFO "msi-laptop: driver unloaded.\n");
+ pr_info("driver unloaded.\n");
}
module_init(msi_init);