diff options
Diffstat (limited to 'drivers/platform/x86/msi-laptop.c')
-rw-r--r-- | drivers/platform/x86/msi-laptop.c | 111 |
1 files changed, 94 insertions, 17 deletions
diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index 7e9bb6df9d39..3ff629df9f01 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c | |||
@@ -51,6 +51,8 @@ | |||
51 | * laptop as MSI S270. YMMV. | 51 | * laptop as MSI S270. YMMV. |
52 | */ | 52 | */ |
53 | 53 | ||
54 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
55 | |||
54 | #include <linux/module.h> | 56 | #include <linux/module.h> |
55 | #include <linux/kernel.h> | 57 | #include <linux/kernel.h> |
56 | #include <linux/init.h> | 58 | #include <linux/init.h> |
@@ -60,6 +62,8 @@ | |||
60 | #include <linux/platform_device.h> | 62 | #include <linux/platform_device.h> |
61 | #include <linux/rfkill.h> | 63 | #include <linux/rfkill.h> |
62 | #include <linux/i8042.h> | 64 | #include <linux/i8042.h> |
65 | #include <linux/input.h> | ||
66 | #include <linux/input/sparse-keymap.h> | ||
63 | 67 | ||
64 | #define MSI_DRIVER_VERSION "0.5" | 68 | #define MSI_DRIVER_VERSION "0.5" |
65 | 69 | ||
@@ -78,6 +82,9 @@ | |||
78 | #define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d | 82 | #define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d |
79 | #define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0) | 83 | #define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0) |
80 | 84 | ||
85 | #define MSI_STANDARD_EC_TOUCHPAD_ADDRESS 0xe4 | ||
86 | #define MSI_STANDARD_EC_TOUCHPAD_MASK (1 << 4) | ||
87 | |||
81 | static int msi_laptop_resume(struct platform_device *device); | 88 | static int msi_laptop_resume(struct platform_device *device); |
82 | 89 | ||
83 | #define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f | 90 | #define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f |
@@ -90,6 +97,14 @@ static int auto_brightness; | |||
90 | module_param(auto_brightness, int, 0); | 97 | module_param(auto_brightness, int, 0); |
91 | MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)"); | 98 | MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)"); |
92 | 99 | ||
100 | static 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 | |||
106 | static struct input_dev *msi_laptop_input_dev; | ||
107 | |||
93 | static bool old_ec_model; | 108 | static bool old_ec_model; |
94 | static int wlan_s, bluetooth_s, threeg_s; | 109 | static int wlan_s, bluetooth_s, threeg_s; |
95 | static int threeg_exists; | 110 | static int threeg_exists; |
@@ -120,7 +135,7 @@ static int set_lcd_level(int level) | |||
120 | buf[1] = (u8) (level*31); | 135 | buf[1] = (u8) (level*31); |
121 | 136 | ||
122 | return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), | 137 | return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), |
123 | NULL, 0, 1); | 138 | NULL, 0); |
124 | } | 139 | } |
125 | 140 | ||
126 | static int get_lcd_level(void) | 141 | static int get_lcd_level(void) |
@@ -129,7 +144,7 @@ static int get_lcd_level(void) | |||
129 | int result; | 144 | int result; |
130 | 145 | ||
131 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, | 146 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, |
132 | &rdata, 1, 1); | 147 | &rdata, 1); |
133 | if (result < 0) | 148 | if (result < 0) |
134 | return result; | 149 | return result; |
135 | 150 | ||
@@ -142,7 +157,7 @@ static int get_auto_brightness(void) | |||
142 | int result; | 157 | int result; |
143 | 158 | ||
144 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, | 159 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, |
145 | &rdata, 1, 1); | 160 | &rdata, 1); |
146 | if (result < 0) | 161 | if (result < 0) |
147 | return result; | 162 | return result; |
148 | 163 | ||
@@ -157,7 +172,7 @@ static int set_auto_brightness(int enable) | |||
157 | wdata[0] = 4; | 172 | wdata[0] = 4; |
158 | 173 | ||
159 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, | 174 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, |
160 | &rdata, 1, 1); | 175 | &rdata, 1); |
161 | if (result < 0) | 176 | if (result < 0) |
162 | return result; | 177 | return result; |
163 | 178 | ||
@@ -165,7 +180,7 @@ static int set_auto_brightness(int enable) | |||
165 | wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0); | 180 | wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0); |
166 | 181 | ||
167 | return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, | 182 | return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, |
168 | NULL, 0, 1); | 183 | NULL, 0); |
169 | } | 184 | } |
170 | 185 | ||
171 | static ssize_t set_device_state(const char *buf, size_t count, u8 mask) | 186 | static ssize_t set_device_state(const char *buf, size_t count, u8 mask) |
@@ -202,7 +217,7 @@ static int get_wireless_state(int *wlan, int *bluetooth) | |||
202 | u8 wdata = 0, rdata; | 217 | u8 wdata = 0, rdata; |
203 | int result; | 218 | int result; |
204 | 219 | ||
205 | result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1, 1); | 220 | result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1); |
206 | if (result < 0) | 221 | if (result < 0) |
207 | return -1; | 222 | return -1; |
208 | 223 | ||
@@ -432,8 +447,7 @@ static struct platform_device *msipf_device; | |||
432 | 447 | ||
433 | static int dmi_check_cb(const struct dmi_system_id *id) | 448 | static int dmi_check_cb(const struct dmi_system_id *id) |
434 | { | 449 | { |
435 | printk(KERN_INFO "msi-laptop: Identified laptop model '%s'.\n", | 450 | pr_info("Identified laptop model '%s'\n", id->ident); |
436 | id->ident); | ||
437 | return 1; | 451 | return 1; |
438 | } | 452 | } |
439 | 453 | ||
@@ -605,6 +619,21 @@ static void msi_update_rfkill(struct work_struct *ignored) | |||
605 | } | 619 | } |
606 | static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill); | 620 | static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill); |
607 | 621 | ||
622 | static void msi_send_touchpad_key(struct work_struct *ignored) | ||
623 | { | ||
624 | u8 rdata; | ||
625 | int result; | ||
626 | |||
627 | result = ec_read(MSI_STANDARD_EC_TOUCHPAD_ADDRESS, &rdata); | ||
628 | if (result < 0) | ||
629 | return; | ||
630 | |||
631 | sparse_keymap_report_event(msi_laptop_input_dev, | ||
632 | (rdata & MSI_STANDARD_EC_TOUCHPAD_MASK) ? | ||
633 | KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF, 1, true); | ||
634 | } | ||
635 | static DECLARE_DELAYED_WORK(msi_touchpad_work, msi_send_touchpad_key); | ||
636 | |||
608 | static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, | 637 | static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, |
609 | struct serio *port) | 638 | struct serio *port) |
610 | { | 639 | { |
@@ -613,12 +642,17 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, | |||
613 | if (str & 0x20) | 642 | if (str & 0x20) |
614 | return false; | 643 | return false; |
615 | 644 | ||
616 | /* 0x54 wwan, 0x62 bluetooth, 0x76 wlan*/ | 645 | /* 0x54 wwan, 0x62 bluetooth, 0x76 wlan, 0xE4 touchpad toggle*/ |
617 | if (unlikely(data == 0xe0)) { | 646 | if (unlikely(data == 0xe0)) { |
618 | extended = true; | 647 | extended = true; |
619 | return false; | 648 | return false; |
620 | } else if (unlikely(extended)) { | 649 | } else if (unlikely(extended)) { |
650 | extended = false; | ||
621 | switch (data) { | 651 | switch (data) { |
652 | case 0xE4: | ||
653 | schedule_delayed_work(&msi_touchpad_work, | ||
654 | round_jiffies_relative(0.5 * HZ)); | ||
655 | break; | ||
622 | case 0x54: | 656 | case 0x54: |
623 | case 0x62: | 657 | case 0x62: |
624 | case 0x76: | 658 | case 0x76: |
@@ -626,7 +660,6 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, | |||
626 | round_jiffies_relative(0.5 * HZ)); | 660 | round_jiffies_relative(0.5 * HZ)); |
627 | break; | 661 | break; |
628 | } | 662 | } |
629 | extended = false; | ||
630 | } | 663 | } |
631 | 664 | ||
632 | return false; | 665 | return false; |
@@ -731,7 +764,43 @@ static int msi_laptop_resume(struct platform_device *device) | |||
731 | return 0; | 764 | return 0; |
732 | } | 765 | } |
733 | 766 | ||
734 | static int load_scm_model_init(struct platform_device *sdev) | 767 | static int __init msi_laptop_input_setup(void) |
768 | { | ||
769 | int err; | ||
770 | |||
771 | msi_laptop_input_dev = input_allocate_device(); | ||
772 | if (!msi_laptop_input_dev) | ||
773 | return -ENOMEM; | ||
774 | |||
775 | msi_laptop_input_dev->name = "MSI Laptop hotkeys"; | ||
776 | msi_laptop_input_dev->phys = "msi-laptop/input0"; | ||
777 | msi_laptop_input_dev->id.bustype = BUS_HOST; | ||
778 | |||
779 | err = sparse_keymap_setup(msi_laptop_input_dev, | ||
780 | msi_laptop_keymap, NULL); | ||
781 | if (err) | ||
782 | goto err_free_dev; | ||
783 | |||
784 | err = input_register_device(msi_laptop_input_dev); | ||
785 | if (err) | ||
786 | goto err_free_keymap; | ||
787 | |||
788 | return 0; | ||
789 | |||
790 | err_free_keymap: | ||
791 | sparse_keymap_free(msi_laptop_input_dev); | ||
792 | err_free_dev: | ||
793 | input_free_device(msi_laptop_input_dev); | ||
794 | return err; | ||
795 | } | ||
796 | |||
797 | static void msi_laptop_input_destroy(void) | ||
798 | { | ||
799 | sparse_keymap_free(msi_laptop_input_dev); | ||
800 | input_unregister_device(msi_laptop_input_dev); | ||
801 | } | ||
802 | |||
803 | static int __init load_scm_model_init(struct platform_device *sdev) | ||
735 | { | 804 | { |
736 | u8 data; | 805 | u8 data; |
737 | int result; | 806 | int result; |
@@ -759,16 +828,23 @@ static int load_scm_model_init(struct platform_device *sdev) | |||
759 | if (result < 0) | 828 | if (result < 0) |
760 | goto fail_rfkill; | 829 | goto fail_rfkill; |
761 | 830 | ||
831 | /* setup input device */ | ||
832 | result = msi_laptop_input_setup(); | ||
833 | if (result) | ||
834 | goto fail_input; | ||
835 | |||
762 | result = i8042_install_filter(msi_laptop_i8042_filter); | 836 | result = i8042_install_filter(msi_laptop_i8042_filter); |
763 | if (result) { | 837 | if (result) { |
764 | printk(KERN_ERR | 838 | pr_err("Unable to install key filter\n"); |
765 | "msi-laptop: Unable to install key filter\n"); | ||
766 | goto fail_filter; | 839 | goto fail_filter; |
767 | } | 840 | } |
768 | 841 | ||
769 | return 0; | 842 | return 0; |
770 | 843 | ||
771 | fail_filter: | 844 | fail_filter: |
845 | msi_laptop_input_destroy(); | ||
846 | |||
847 | fail_input: | ||
772 | rfkill_cleanup(); | 848 | rfkill_cleanup(); |
773 | 849 | ||
774 | fail_rfkill: | 850 | fail_rfkill: |
@@ -799,11 +875,11 @@ static int __init msi_init(void) | |||
799 | /* Register backlight stuff */ | 875 | /* Register backlight stuff */ |
800 | 876 | ||
801 | if (acpi_video_backlight_support()) { | 877 | if (acpi_video_backlight_support()) { |
802 | printk(KERN_INFO "MSI: Brightness ignored, must be controlled " | 878 | pr_info("Brightness ignored, must be controlled by ACPI video driver\n"); |
803 | "by ACPI video driver\n"); | ||
804 | } else { | 879 | } else { |
805 | struct backlight_properties props; | 880 | struct backlight_properties props; |
806 | memset(&props, 0, sizeof(struct backlight_properties)); | 881 | memset(&props, 0, sizeof(struct backlight_properties)); |
882 | props.type = BACKLIGHT_PLATFORM; | ||
807 | props.max_brightness = MSI_LCD_LEVEL_MAX - 1; | 883 | props.max_brightness = MSI_LCD_LEVEL_MAX - 1; |
808 | msibl_device = backlight_device_register("msi-laptop-bl", NULL, | 884 | msibl_device = backlight_device_register("msi-laptop-bl", NULL, |
809 | NULL, &msibl_ops, | 885 | NULL, &msibl_ops, |
@@ -853,7 +929,7 @@ static int __init msi_init(void) | |||
853 | if (auto_brightness != 2) | 929 | if (auto_brightness != 2) |
854 | set_auto_brightness(auto_brightness); | 930 | set_auto_brightness(auto_brightness); |
855 | 931 | ||
856 | printk(KERN_INFO "msi-laptop: driver "MSI_DRIVER_VERSION" successfully loaded.\n"); | 932 | pr_info("driver " MSI_DRIVER_VERSION " successfully loaded\n"); |
857 | 933 | ||
858 | return 0; | 934 | return 0; |
859 | 935 | ||
@@ -885,6 +961,7 @@ static void __exit msi_cleanup(void) | |||
885 | { | 961 | { |
886 | if (load_scm_model) { | 962 | if (load_scm_model) { |
887 | i8042_remove_filter(msi_laptop_i8042_filter); | 963 | i8042_remove_filter(msi_laptop_i8042_filter); |
964 | msi_laptop_input_destroy(); | ||
888 | cancel_delayed_work_sync(&msi_rfkill_work); | 965 | cancel_delayed_work_sync(&msi_rfkill_work); |
889 | rfkill_cleanup(); | 966 | rfkill_cleanup(); |
890 | } | 967 | } |
@@ -900,7 +977,7 @@ static void __exit msi_cleanup(void) | |||
900 | if (auto_brightness != 2) | 977 | if (auto_brightness != 2) |
901 | set_auto_brightness(1); | 978 | set_auto_brightness(1); |
902 | 979 | ||
903 | printk(KERN_INFO "msi-laptop: driver unloaded.\n"); | 980 | pr_info("driver unloaded\n"); |
904 | } | 981 | } |
905 | 982 | ||
906 | module_init(msi_init); | 983 | module_init(msi_init); |