aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorHerton Ronaldo Krzesinski <herton@mandriva.com.br>2009-09-14 20:11:21 -0400
committerLen Brown <len.brown@intel.com>2009-09-19 01:16:13 -0400
commit9caeb5324427990db7bc97e674794d201c1f0797 (patch)
tree1054edb9c3ed38d73d867a43c163547c6933733b /drivers
parent74fca6a42863ffacaf7ba6f1936a9f228950f657 (diff)
topstar-laptop: add new driver for hotkeys support on Topstar N01
This adds Topstar Laptop Extras ACPI driver. It enables hotkeys functionality with Topstar N01 netbook. Besides hotkeys there are other functions exposed by its ACPI firmware, but for now only hotkeys reporting on Topstar N01 is supported. Topstar is a chinese manufacturer, its website can be currently reached at http://www.topstardigital.cn/ Signed-off-by: Herton Ronaldo Krzesinski <herton@mandriva.com.br> Reviewed-by: Alan Jenkins <alan-jenkins@tuffmail.co.uk> Reviewed-by: Bjorn Helgaas <bjorn.helgaas@hp.com> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/platform/x86/Kconfig9
-rw-r--r--drivers/platform/x86/Makefile1
-rw-r--r--drivers/platform/x86/topstar-laptop.c265
3 files changed, 275 insertions, 0 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 77c6097ced80..48315621046d 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -396,6 +396,15 @@ config ACPI_ASUS
396 NOTE: This driver is deprecated and will probably be removed soon, 396 NOTE: This driver is deprecated and will probably be removed soon,
397 use asus-laptop instead. 397 use asus-laptop instead.
398 398
399config TOPSTAR_LAPTOP
400 tristate "Topstar Laptop Extras"
401 depends on ACPI
402 depends on INPUT
403 ---help---
404 This driver adds support for hotkeys found on Topstar laptops.
405
406 If you have a Topstar laptop, say Y or M here.
407
399config ACPI_TOSHIBA 408config ACPI_TOSHIBA
400 tristate "Toshiba Laptop Extras" 409 tristate "Toshiba Laptop Extras"
401 depends on ACPI 410 depends on ACPI
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 641b8bfa5538..d1c16210a512 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -19,4 +19,5 @@ obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o
19obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o 19obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
20obj-$(CONFIG_ACPI_WMI) += wmi.o 20obj-$(CONFIG_ACPI_WMI) += wmi.o
21obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o 21obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o
22obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
22obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o 23obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c
new file mode 100644
index 000000000000..02f3d4e9e666
--- /dev/null
+++ b/drivers/platform/x86/topstar-laptop.c
@@ -0,0 +1,265 @@
1/*
2 * ACPI driver for Topstar notebooks (hotkeys support only)
3 *
4 * Copyright (c) 2009 Herton Ronaldo Krzesinski <herton@mandriva.com.br>
5 *
6 * Implementation inspired by existing x86 platform drivers, in special
7 * asus/eepc/fujitsu-laptop, thanks to their authors
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15
16#include <linux/kernel.h>
17#include <linux/module.h>
18#include <linux/init.h>
19#include <linux/acpi.h>
20#include <linux/input.h>
21
22#define ACPI_TOPSTAR_CLASS "topstar"
23
24struct topstar_hkey {
25 struct input_dev *inputdev;
26};
27
28struct tps_key_entry {
29 u8 code;
30 u16 keycode;
31};
32
33static struct tps_key_entry topstar_keymap[] = {
34 { 0x80, KEY_BRIGHTNESSUP },
35 { 0x81, KEY_BRIGHTNESSDOWN },
36 { 0x83, KEY_VOLUMEUP },
37 { 0x84, KEY_VOLUMEDOWN },
38 { 0x85, KEY_MUTE },
39 { 0x86, KEY_SWITCHVIDEOMODE },
40 { 0x87, KEY_F13 }, /* touchpad enable/disable key */
41 { 0x88, KEY_WLAN },
42 { 0x8a, KEY_WWW },
43 { 0x8b, KEY_MAIL },
44 { 0x8c, KEY_MEDIA },
45 { 0x96, KEY_F14 }, /* G key? */
46 { }
47};
48
49static struct tps_key_entry *tps_get_key_by_scancode(int code)
50{
51 struct tps_key_entry *key;
52
53 for (key = topstar_keymap; key->code; key++)
54 if (code == key->code)
55 return key;
56
57 return NULL;
58}
59
60static struct tps_key_entry *tps_get_key_by_keycode(int code)
61{
62 struct tps_key_entry *key;
63
64 for (key = topstar_keymap; key->code; key++)
65 if (code == key->keycode)
66 return key;
67
68 return NULL;
69}
70
71static void acpi_topstar_notify(struct acpi_device *device, u32 event)
72{
73 struct tps_key_entry *key;
74 static bool dup_evnt[2];
75 bool *dup;
76 struct topstar_hkey *hkey = acpi_driver_data(device);
77
78 /* 0x83 and 0x84 key events comes duplicated... */
79 if (event == 0x83 || event == 0x84) {
80 dup = &dup_evnt[event - 0x83];
81 if (*dup) {
82 *dup = false;
83 return;
84 }
85 *dup = true;
86 }
87
88 /*
89 * 'G key' generate two event codes, convert to only
90 * one event/key code for now (3G switch?)
91 */
92 if (event == 0x97)
93 event = 0x96;
94
95 key = tps_get_key_by_scancode(event);
96 if (key) {
97 input_report_key(hkey->inputdev, key->keycode, 1);
98 input_sync(hkey->inputdev);
99 input_report_key(hkey->inputdev, key->keycode, 0);
100 input_sync(hkey->inputdev);
101 return;
102 }
103
104 /* Known non hotkey events don't handled or that we don't care yet */
105 if (event == 0x8e || event == 0x8f || event == 0x90)
106 return;
107
108 pr_info("unknown event = 0x%02x\n", event);
109}
110
111static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state)
112{
113 acpi_status status;
114 union acpi_object fncx_params[1] = {
115 { .type = ACPI_TYPE_INTEGER }
116 };
117 struct acpi_object_list fncx_arg_list = { 1, &fncx_params[0] };
118
119 fncx_params[0].integer.value = state ? 0x86 : 0x87;
120 status = acpi_evaluate_object(device->handle, "FNCX", &fncx_arg_list, NULL);
121 if (ACPI_FAILURE(status)) {
122 pr_err("Unable to switch FNCX notifications\n");
123 return -ENODEV;
124 }
125
126 return 0;
127}
128
129static int topstar_getkeycode(struct input_dev *dev, int scancode, int *keycode)
130{
131 struct tps_key_entry *key = tps_get_key_by_scancode(scancode);
132
133 if (!key)
134 return -EINVAL;
135
136 *keycode = key->keycode;
137 return 0;
138}
139
140static int topstar_setkeycode(struct input_dev *dev, int scancode, int keycode)
141{
142 struct tps_key_entry *key;
143 int old_keycode;
144
145 if (keycode < 0 || keycode > KEY_MAX)
146 return -EINVAL;
147
148 key = tps_get_key_by_scancode(scancode);
149
150 if (!key)
151 return -EINVAL;
152
153 old_keycode = key->keycode;
154 key->keycode = keycode;
155 set_bit(keycode, dev->keybit);
156 if (!tps_get_key_by_keycode(old_keycode))
157 clear_bit(old_keycode, dev->keybit);
158 return 0;
159}
160
161static int acpi_topstar_init_hkey(struct topstar_hkey *hkey)
162{
163 struct tps_key_entry *key;
164
165 hkey->inputdev = input_allocate_device();
166 if (!hkey->inputdev) {
167 pr_err("Unable to allocate input device\n");
168 return -ENODEV;
169 }
170 hkey->inputdev->name = "Topstar Laptop extra buttons";
171 hkey->inputdev->phys = "topstar/input0";
172 hkey->inputdev->id.bustype = BUS_HOST;
173 hkey->inputdev->getkeycode = topstar_getkeycode;
174 hkey->inputdev->setkeycode = topstar_setkeycode;
175 for (key = topstar_keymap; key->code; key++) {
176 set_bit(EV_KEY, hkey->inputdev->evbit);
177 set_bit(key->keycode, hkey->inputdev->keybit);
178 }
179 if (input_register_device(hkey->inputdev)) {
180 pr_err("Unable to register input device\n");
181 input_free_device(hkey->inputdev);
182 return -ENODEV;
183 }
184
185 return 0;
186}
187
188static int acpi_topstar_add(struct acpi_device *device)
189{
190 struct topstar_hkey *tps_hkey;
191
192 tps_hkey = kzalloc(sizeof(struct topstar_hkey), GFP_KERNEL);
193 if (!tps_hkey)
194 return -ENOMEM;
195
196 strcpy(acpi_device_name(device), "Topstar TPSACPI");
197 strcpy(acpi_device_class(device), ACPI_TOPSTAR_CLASS);
198
199 if (acpi_topstar_fncx_switch(device, true))
200 goto add_err;
201
202 if (acpi_topstar_init_hkey(tps_hkey))
203 goto add_err;
204
205 device->driver_data = tps_hkey;
206 return 0;
207
208add_err:
209 kfree(tps_hkey);
210 return -ENODEV;
211}
212
213static int acpi_topstar_remove(struct acpi_device *device, int type)
214{
215 struct topstar_hkey *tps_hkey = acpi_driver_data(device);
216
217 acpi_topstar_fncx_switch(device, false);
218
219 input_unregister_device(tps_hkey->inputdev);
220 kfree(tps_hkey);
221
222 return 0;
223}
224
225static const struct acpi_device_id topstar_device_ids[] = {
226 { "TPSACPI01", 0 },
227 { "", 0 },
228};
229MODULE_DEVICE_TABLE(acpi, topstar_device_ids);
230
231static struct acpi_driver acpi_topstar_driver = {
232 .name = "Topstar laptop ACPI driver",
233 .class = ACPI_TOPSTAR_CLASS,
234 .ids = topstar_device_ids,
235 .ops = {
236 .add = acpi_topstar_add,
237 .remove = acpi_topstar_remove,
238 .notify = acpi_topstar_notify,
239 },
240};
241
242static int __init topstar_laptop_init(void)
243{
244 int ret;
245
246 ret = acpi_bus_register_driver(&acpi_topstar_driver);
247 if (ret < 0)
248 return ret;
249
250 printk(KERN_INFO "Topstar Laptop ACPI extras driver loaded\n");
251
252 return 0;
253}
254
255static void __exit topstar_laptop_exit(void)
256{
257 acpi_bus_unregister_driver(&acpi_topstar_driver);
258}
259
260module_init(topstar_laptop_init);
261module_exit(topstar_laptop_exit);
262
263MODULE_AUTHOR("Herton Ronaldo Krzesinski");
264MODULE_DESCRIPTION("Topstar Laptop ACPI Extras driver");
265MODULE_LICENSE("GPL");