diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-03 20:02:31 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-03 20:02:31 -0400 |
commit | 73f10274a6c8699738246ed2e1d704dc52702565 (patch) | |
tree | 9ee33e06c6351531c6493c977dd29e9db366b2bb /drivers/input/misc/soc_button_array.c | |
parent | 877f075aac900288ce2e6a64075cceff09210a7e (diff) | |
parent | 692d96552c9a86a919fe6b5b82288a6c77c015a5 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input updates from Dmitry Torokhov:
"The first round of updates for the input subsystem.
Just new drivers and existing driver fixes, no core changes except for
the new uinput IOCTL to allow userspace to fetch sysfs name of the
input device that was created"
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (43 commits)
Input: edt-ft5x06 - add a missing condition
Input: appletouch - fix jumps when additional fingers are detected
Input: appletouch - implement sensor data smoothing
Input: add driver for SOC button array
Input: pm8xxx-vibrator - add DT match table
Input: pmic8xxx-pwrkey - migrate to DT
Input: pmic8xxx-keypad - migrate to DT
Input: pmic8xxx-keypad - migrate to regmap APIs
Input: pmic8xxx-keypad - migrate to devm_* APIs
Input: pmic8xxx-keypad - fix build by removing gpio configuration
Input: add new driver for ARM CLPS711X keypad
Input: edt-ft5x06 - add support for M09 firmware version
Input: edt-ft5x06 - ignore touchdown events
Input: edt-ft5x06 - adjust delays to conform datasheet
Input: edt-ft5x06 - add DT support
Input: edt-ft5x06 - several cleanups; no functional change
Input: appletouch - dial back fuzz setting
Input: remove obsolete tnetv107x drivers
Input: sirfsoc-onkey - set the capability of reporting KEY_POWER
Input: da9052_onkey - use correct register bit for key status
...
Diffstat (limited to 'drivers/input/misc/soc_button_array.c')
-rw-r--r-- | drivers/input/misc/soc_button_array.c | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c new file mode 100644 index 000000000000..08ead2aaede5 --- /dev/null +++ b/drivers/input/misc/soc_button_array.c | |||
@@ -0,0 +1,218 @@ | |||
1 | /* | ||
2 | * Supports for the button array on SoC tablets originally running | ||
3 | * Windows 8. | ||
4 | * | ||
5 | * (C) Copyright 2014 Intel Corporation | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * as published by the Free Software Foundation; version 2 | ||
10 | * of the License. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/input.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/acpi.h> | ||
18 | #include <linux/gpio/consumer.h> | ||
19 | #include <linux/gpio_keys.h> | ||
20 | #include <linux/input.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/pnp.h> | ||
23 | |||
24 | /* | ||
25 | * Definition of buttons on the tablet. The ACPI index of each button | ||
26 | * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC | ||
27 | * Platforms" | ||
28 | */ | ||
29 | #define MAX_NBUTTONS 5 | ||
30 | |||
31 | struct soc_button_info { | ||
32 | const char *name; | ||
33 | int acpi_index; | ||
34 | unsigned int event_type; | ||
35 | unsigned int event_code; | ||
36 | bool autorepeat; | ||
37 | bool wakeup; | ||
38 | }; | ||
39 | |||
40 | /* | ||
41 | * Some of the buttons like volume up/down are auto repeat, while others | ||
42 | * are not. To support both, we register two platform devices, and put | ||
43 | * buttons into them based on whether the key should be auto repeat. | ||
44 | */ | ||
45 | #define BUTTON_TYPES 2 | ||
46 | |||
47 | struct soc_button_data { | ||
48 | struct platform_device *children[BUTTON_TYPES]; | ||
49 | }; | ||
50 | |||
51 | /* | ||
52 | * Get the Nth GPIO number from the ACPI object. | ||
53 | */ | ||
54 | static int soc_button_lookup_gpio(struct device *dev, int acpi_index) | ||
55 | { | ||
56 | struct gpio_desc *desc; | ||
57 | int gpio; | ||
58 | |||
59 | desc = gpiod_get_index(dev, KBUILD_MODNAME, acpi_index); | ||
60 | if (IS_ERR(desc)) | ||
61 | return PTR_ERR(desc); | ||
62 | |||
63 | gpio = desc_to_gpio(desc); | ||
64 | |||
65 | gpiod_put(desc); | ||
66 | |||
67 | return gpio; | ||
68 | } | ||
69 | |||
70 | static struct platform_device * | ||
71 | soc_button_device_create(struct pnp_dev *pdev, | ||
72 | const struct soc_button_info *button_info, | ||
73 | bool autorepeat) | ||
74 | { | ||
75 | const struct soc_button_info *info; | ||
76 | struct platform_device *pd; | ||
77 | struct gpio_keys_button *gpio_keys; | ||
78 | struct gpio_keys_platform_data *gpio_keys_pdata; | ||
79 | int n_buttons = 0; | ||
80 | int gpio; | ||
81 | int error; | ||
82 | |||
83 | gpio_keys_pdata = devm_kzalloc(&pdev->dev, | ||
84 | sizeof(*gpio_keys_pdata) + | ||
85 | sizeof(*gpio_keys) * MAX_NBUTTONS, | ||
86 | GFP_KERNEL); | ||
87 | gpio_keys = (void *)(gpio_keys_pdata + 1); | ||
88 | |||
89 | for (info = button_info; info->name; info++) { | ||
90 | if (info->autorepeat != autorepeat) | ||
91 | continue; | ||
92 | |||
93 | gpio = soc_button_lookup_gpio(&pdev->dev, info->acpi_index); | ||
94 | if (gpio < 0) | ||
95 | continue; | ||
96 | |||
97 | gpio_keys[n_buttons].type = info->event_type; | ||
98 | gpio_keys[n_buttons].code = info->event_code; | ||
99 | gpio_keys[n_buttons].gpio = gpio; | ||
100 | gpio_keys[n_buttons].active_low = 1; | ||
101 | gpio_keys[n_buttons].desc = info->name; | ||
102 | gpio_keys[n_buttons].wakeup = info->wakeup; | ||
103 | n_buttons++; | ||
104 | } | ||
105 | |||
106 | if (n_buttons == 0) { | ||
107 | error = -ENODEV; | ||
108 | goto err_free_mem; | ||
109 | } | ||
110 | |||
111 | gpio_keys_pdata->buttons = gpio_keys; | ||
112 | gpio_keys_pdata->nbuttons = n_buttons; | ||
113 | gpio_keys_pdata->rep = autorepeat; | ||
114 | |||
115 | pd = platform_device_alloc("gpio-keys", PLATFORM_DEVID_AUTO); | ||
116 | if (!pd) { | ||
117 | error = -ENOMEM; | ||
118 | goto err_free_mem; | ||
119 | } | ||
120 | |||
121 | error = platform_device_add_data(pd, gpio_keys_pdata, | ||
122 | sizeof(*gpio_keys_pdata)); | ||
123 | if (error) | ||
124 | goto err_free_pdev; | ||
125 | |||
126 | error = platform_device_add(pd); | ||
127 | if (error) | ||
128 | goto err_free_pdev; | ||
129 | |||
130 | return pd; | ||
131 | |||
132 | err_free_pdev: | ||
133 | platform_device_put(pd); | ||
134 | err_free_mem: | ||
135 | devm_kfree(&pdev->dev, gpio_keys_pdata); | ||
136 | return ERR_PTR(error); | ||
137 | } | ||
138 | |||
139 | static void soc_button_remove(struct pnp_dev *pdev) | ||
140 | { | ||
141 | struct soc_button_data *priv = pnp_get_drvdata(pdev); | ||
142 | int i; | ||
143 | |||
144 | for (i = 0; i < BUTTON_TYPES; i++) | ||
145 | if (priv->children[i]) | ||
146 | platform_device_unregister(priv->children[i]); | ||
147 | } | ||
148 | |||
149 | static int soc_button_pnp_probe(struct pnp_dev *pdev, | ||
150 | const struct pnp_device_id *id) | ||
151 | { | ||
152 | const struct soc_button_info *button_info = (void *)id->driver_data; | ||
153 | struct soc_button_data *priv; | ||
154 | struct platform_device *pd; | ||
155 | int i; | ||
156 | int error; | ||
157 | |||
158 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); | ||
159 | if (!priv) | ||
160 | return -ENOMEM; | ||
161 | |||
162 | pnp_set_drvdata(pdev, priv); | ||
163 | |||
164 | for (i = 0; i < BUTTON_TYPES; i++) { | ||
165 | pd = soc_button_device_create(pdev, button_info, i == 0); | ||
166 | if (IS_ERR(pd)) { | ||
167 | error = PTR_ERR(pd); | ||
168 | if (error != -ENODEV) { | ||
169 | soc_button_remove(pdev); | ||
170 | return error; | ||
171 | } | ||
172 | } | ||
173 | |||
174 | priv->children[i] = pd; | ||
175 | } | ||
176 | |||
177 | if (!priv->children[0] && !priv->children[1]) | ||
178 | return -ENODEV; | ||
179 | |||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | static struct soc_button_info soc_button_PNP0C40[] = { | ||
184 | { "power", 0, EV_KEY, KEY_POWER, false, true }, | ||
185 | { "home", 1, EV_KEY, KEY_HOME, false, true }, | ||
186 | { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false }, | ||
187 | { "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false }, | ||
188 | { "rotation_lock", 4, EV_SW, SW_ROTATE_LOCK, false, false }, | ||
189 | { } | ||
190 | }; | ||
191 | |||
192 | static const struct pnp_device_id soc_button_pnp_match[] = { | ||
193 | { .id = "PNP0C40", .driver_data = (long)soc_button_PNP0C40 }, | ||
194 | { .id = "" } | ||
195 | }; | ||
196 | MODULE_DEVICE_TABLE(pnp, soc_button_pnp_match); | ||
197 | |||
198 | static struct pnp_driver soc_button_pnp_driver = { | ||
199 | .name = KBUILD_MODNAME, | ||
200 | .id_table = soc_button_pnp_match, | ||
201 | .probe = soc_button_pnp_probe, | ||
202 | .remove = soc_button_remove, | ||
203 | }; | ||
204 | |||
205 | static int __init soc_button_init(void) | ||
206 | { | ||
207 | return pnp_register_driver(&soc_button_pnp_driver); | ||
208 | } | ||
209 | |||
210 | static void __exit soc_button_exit(void) | ||
211 | { | ||
212 | pnp_unregister_driver(&soc_button_pnp_driver); | ||
213 | } | ||
214 | |||
215 | module_init(soc_button_init); | ||
216 | module_exit(soc_button_exit); | ||
217 | |||
218 | MODULE_LICENSE("GPL"); | ||