diff options
| -rw-r--r-- | drivers/platform/x86/Kconfig | 10 | ||||
| -rw-r--r-- | drivers/platform/x86/Makefile | 1 | ||||
| -rw-r--r-- | drivers/platform/x86/msi-wmi.c | 369 |
3 files changed, 380 insertions, 0 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 55ca39dea42e..98ec6bd9226e 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
| @@ -365,6 +365,16 @@ config ACPI_WMI | |||
| 365 | It is safe to enable this driver even if your DSDT doesn't define | 365 | It is safe to enable this driver even if your DSDT doesn't define |
| 366 | any ACPI-WMI devices. | 366 | any ACPI-WMI devices. |
| 367 | 367 | ||
| 368 | config MSI_WMI | ||
| 369 | tristate "MSI WMI extras" | ||
| 370 | depends on ACPI_WMI | ||
| 371 | depends on INPUT | ||
| 372 | help | ||
| 373 | Say Y here if you want to support WMI-based hotkeys on MSI laptops. | ||
| 374 | |||
| 375 | To compile this driver as a module, choose M here: the module will | ||
| 376 | be called msi-wmi. | ||
| 377 | |||
| 368 | config ACPI_ASUS | 378 | config ACPI_ASUS |
| 369 | tristate "ASUS/Medion Laptop Extras (DEPRECATED)" | 379 | tristate "ASUS/Medion Laptop Extras (DEPRECATED)" |
| 370 | depends on ACPI | 380 | depends on ACPI |
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index d1c16210a512..13aa37310f33 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile | |||
| @@ -18,6 +18,7 @@ obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o | |||
| 18 | obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o | 18 | obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o |
| 19 | obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o | 19 | obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o |
| 20 | obj-$(CONFIG_ACPI_WMI) += wmi.o | 20 | obj-$(CONFIG_ACPI_WMI) += wmi.o |
| 21 | obj-$(CONFIG_MSI_WMI) += msi-wmi.o | ||
| 21 | obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o | 22 | obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o |
| 22 | obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o | 23 | obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o |
| 23 | obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o | 24 | obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o |
diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c new file mode 100644 index 000000000000..7e0dab659752 --- /dev/null +++ b/drivers/platform/x86/msi-wmi.c | |||
| @@ -0,0 +1,369 @@ | |||
| 1 | /* | ||
| 2 | * MSI WMI hotkeys | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Novell <trenn@suse.de> | ||
| 5 | * | ||
| 6 | * Most stuff taken over from hp-wmi | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License, or | ||
| 11 | * (at your option) any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License | ||
| 19 | * along with this program; if not, write to the Free Software | ||
| 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 21 | */ | ||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | #include <linux/kernel.h> | ||
| 26 | #include <linux/module.h> | ||
| 27 | #include <linux/init.h> | ||
| 28 | #include <linux/types.h> | ||
| 29 | #include <linux/input.h> | ||
| 30 | #include <acpi/acpi_drivers.h> | ||
| 31 | #include <linux/acpi.h> | ||
| 32 | #include <linux/string.h> | ||
| 33 | #include <linux/hrtimer.h> | ||
| 34 | #include <linux/backlight.h> | ||
| 35 | |||
| 36 | MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>"); | ||
| 37 | MODULE_DESCRIPTION("MSI laptop WMI hotkeys driver"); | ||
| 38 | MODULE_LICENSE("GPL"); | ||
| 39 | |||
| 40 | static int debug; | ||
| 41 | module_param(debug, int, 0); | ||
| 42 | MODULE_PARM_DESC(debug, "Set this to 1 to let the driver be more verbose"); | ||
| 43 | |||
| 44 | MODULE_ALIAS("wmi:551A1F84-FBDD-4125-91DB-3EA8F44F1D45"); | ||
| 45 | MODULE_ALIAS("wmi:B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2"); | ||
| 46 | |||
| 47 | /* Temporary workaround until the WMI sysfs interface goes in | ||
| 48 | { "svn", DMI_SYS_VENDOR }, | ||
| 49 | { "pn", DMI_PRODUCT_NAME }, | ||
| 50 | { "pvr", DMI_PRODUCT_VERSION }, | ||
| 51 | { "rvn", DMI_BOARD_VENDOR }, | ||
| 52 | { "rn", DMI_BOARD_NAME }, | ||
| 53 | */ | ||
| 54 | |||
| 55 | MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-6638:*"); | ||
| 56 | |||
| 57 | #define DRV_NAME "msi-wmi" | ||
| 58 | #define DRV_PFX DRV_NAME ": " | ||
| 59 | |||
| 60 | #define MSIWMI_BIOS_GUID "551A1F84-FBDD-4125-91DB-3EA8F44F1D45" | ||
| 61 | #define MSIWMI_EVENT_GUID "B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2" | ||
| 62 | |||
| 63 | #define dprintk(msg...) do { \ | ||
| 64 | if (debug) \ | ||
| 65 | printk(KERN_INFO DRV_PFX msg); \ | ||
| 66 | } while (0) | ||
| 67 | |||
| 68 | struct key_entry { | ||
| 69 | char type; /* See KE_* below */ | ||
| 70 | u16 code; | ||
| 71 | u16 keycode; | ||
| 72 | int instance; | ||
| 73 | ktime_t last_pressed; | ||
| 74 | }; | ||
| 75 | |||
| 76 | /* | ||
| 77 | * KE_KEY the only used key type, but keep this, others might also | ||
| 78 | * show up in the future. Compare with hp-wmi.c | ||
| 79 | */ | ||
| 80 | enum { KE_KEY, KE_END }; | ||
| 81 | |||
| 82 | static struct key_entry msi_wmi_keymap[] = { | ||
| 83 | { KE_KEY, 0xd0, KEY_BRIGHTNESSUP, 0, {0, } }, | ||
| 84 | { KE_KEY, 0xd1, KEY_BRIGHTNESSDOWN, 1, {0, } }, | ||
| 85 | { KE_KEY, 0xd2, KEY_VOLUMEUP, 2, {0, } }, | ||
| 86 | { KE_KEY, 0xd3, KEY_VOLUMEDOWN, 3, {0, } }, | ||
| 87 | { KE_END, 0} | ||
| 88 | }; | ||
| 89 | |||
| 90 | struct backlight_device *backlight; | ||
| 91 | |||
| 92 | static int backlight_map[] = { 0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF }; | ||
| 93 | |||
| 94 | static struct input_dev *msi_wmi_input_dev; | ||
| 95 | |||
| 96 | static int msi_wmi_query_block(int instance, int *ret) | ||
| 97 | { | ||
| 98 | acpi_status status; | ||
| 99 | union acpi_object *obj; | ||
| 100 | |||
| 101 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
| 102 | |||
| 103 | status = wmi_query_block(MSIWMI_BIOS_GUID, instance, &output); | ||
| 104 | |||
| 105 | obj = output.pointer; | ||
| 106 | |||
| 107 | if (!obj || obj->type != ACPI_TYPE_INTEGER) { | ||
| 108 | if (obj) { | ||
| 109 | printk(KERN_ERR DRV_PFX "query block returned object " | ||
| 110 | "type: %d - buffer length:%d\n", obj->type, | ||
| 111 | obj->type == ACPI_TYPE_BUFFER ? | ||
| 112 | obj->buffer.length : 0); | ||
| 113 | } | ||
| 114 | kfree(obj); | ||
| 115 | return -EINVAL; | ||
| 116 | } | ||
| 117 | *ret = obj->integer.value; | ||
| 118 | kfree(obj); | ||
| 119 | return 0; | ||
| 120 | } | ||
| 121 | |||
| 122 | static int msi_wmi_set_block(int instance, int value) | ||
| 123 | { | ||
| 124 | acpi_status status; | ||
| 125 | |||
| 126 | struct acpi_buffer input = { sizeof(int), &value }; | ||
| 127 | |||
| 128 | dprintk("Going to set block of instance: %d - value: %d\n", | ||
| 129 | instance, value); | ||
| 130 | |||
| 131 | status = wmi_set_block(MSIWMI_BIOS_GUID, instance, &input); | ||
| 132 | |||
| 133 | return ACPI_SUCCESS(status) ? 0 : 1; | ||
| 134 | } | ||
| 135 | |||
| 136 | static int bl_get(struct backlight_device *bd) | ||
| 137 | { | ||
| 138 | int level, err, ret = 0; | ||
| 139 | |||
| 140 | /* Instance 1 is "get backlight", cmp with DSDT */ | ||
| 141 | err = msi_wmi_query_block(1, &ret); | ||
| 142 | if (err) | ||
| 143 | printk(KERN_ERR DRV_PFX "Could not query backlight: %d\n", err); | ||
| 144 | dprintk("Get: Query block returned: %d\n", ret); | ||
| 145 | for (level = 0; level < ARRAY_SIZE(backlight_map); level++) { | ||
| 146 | if (backlight_map[level] == ret) { | ||
| 147 | dprintk("Current backlight level: 0x%X - index: %d\n", | ||
| 148 | backlight_map[level], level); | ||
| 149 | break; | ||
| 150 | } | ||
| 151 | } | ||
| 152 | if (level == ARRAY_SIZE(backlight_map)) { | ||
| 153 | printk(KERN_ERR DRV_PFX "get: Invalid brightness value: 0x%X\n", | ||
| 154 | ret); | ||
| 155 | return -EINVAL; | ||
| 156 | } | ||
| 157 | return level; | ||
| 158 | } | ||
| 159 | |||
| 160 | static int bl_set_status(struct backlight_device *bd) | ||
| 161 | { | ||
| 162 | int bright = bd->props.brightness; | ||
| 163 | if (bright >= ARRAY_SIZE(backlight_map) || bright < 0) | ||
