aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/misc/soc_button_array.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-04-03 20:02:31 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-04-03 20:02:31 -0400
commit73f10274a6c8699738246ed2e1d704dc52702565 (patch)
tree9ee33e06c6351531c6493c977dd29e9db366b2bb /drivers/input/misc/soc_button_array.c
parent877f075aac900288ce2e6a64075cceff09210a7e (diff)
parent692d96552c9a86a919fe6b5b82288a6c77c015a5 (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.c218
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
31struct 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
47struct soc_button_data {
48 struct platform_device *children[BUTTON_TYPES];
49};
50
51/*
52 * Get the Nth GPIO number from the ACPI object.
53 */
54static 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
70static struct platform_device *
71soc_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
132err_free_pdev:
133 platform_device_put(pd);
134err_free_mem:
135 devm_kfree(&pdev->dev, gpio_keys_pdata);
136 return ERR_PTR(error);
137}
138
139static 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
149static 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
183static 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
192static const struct pnp_device_id soc_button_pnp_match[] = {
193 { .id = "PNP0C40", .driver_data = (long)soc_button_PNP0C40 },
194 { .id = "" }
195};
196MODULE_DEVICE_TABLE(pnp, soc_button_pnp_match);
197
198static 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
205static int __init soc_button_init(void)
206{
207 return pnp_register_driver(&soc_button_pnp_driver);
208}
209
210static void __exit soc_button_exit(void)
211{
212 pnp_unregister_driver(&soc_button_pnp_driver);
213}
214
215module_init(soc_button_init);
216module_exit(soc_button_exit);
217
218MODULE_LICENSE("GPL");