diff options
author | Heiko Stübner <heiko@sntech.de> | 2011-11-29 14:04:09 -0500 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2011-12-01 02:41:16 -0500 |
commit | 3bfd5c5baf66e975b0f365a0cda8d75bf2953ebe (patch) | |
tree | 49517da40dd6071ed9d3acde9fa1ef97d39ede13 /drivers/input/misc | |
parent | a6c61789c8499381a5fe612f11dc95d0b55e590a (diff) |
Input: add generic GPIO-tilt driver
There exist tilt switches that simply report their tilt-state via
some gpios. The number and orientation of their axes can vary
depending on the switch used and the build of the device. Also two
or more one-axis switches could be combined to provide multi-dimensional
orientation.
One example of a device using such a switch is the family of Qisda
ebook readers, where the switch provides information about the
landscape / portrait orientation of the device. The example in
Documentation/input/gpio-tilt.txt documents exactly this one-axis
device.
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/input/misc')
-rw-r--r-- | drivers/input/misc/Kconfig | 14 | ||||
-rw-r--r-- | drivers/input/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/misc/gpio_tilt_polled.c | 213 |
3 files changed, 228 insertions, 0 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 22d875fde53a..e53b443d1e33 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig | |||
@@ -179,6 +179,20 @@ config INPUT_APANEL | |||
179 | To compile this driver as a module, choose M here: the module will | 179 | To compile this driver as a module, choose M here: the module will |
180 | be called apanel. | 180 | be called apanel. |
181 | 181 | ||
182 | config INPUT_GPIO_TILT_POLLED | ||
183 | tristate "Polled GPIO tilt switch" | ||
184 | depends on GENERIC_GPIO | ||
185 | select INPUT_POLLDEV | ||
186 | help | ||
187 | This driver implements support for tilt switches connected | ||
188 | to GPIO pins that are not capable of generating interrupts. | ||
189 | |||
190 | The list of gpios to use and the mapping of their states | ||
191 | to specific angles is done via platform data. | ||
192 | |||
193 | To compile this driver as a module, choose M here: the | ||
194 | module will be called gpio_tilt_polled. | ||
195 | |||
182 | config INPUT_IXP4XX_BEEPER | 196 | config INPUT_IXP4XX_BEEPER |
183 | tristate "IXP4XX Beeper support" | 197 | tristate "IXP4XX Beeper support" |
184 | depends on ARCH_IXP4XX | 198 | depends on ARCH_IXP4XX |
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index a244fc6a781c..90070c1a4ad3 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile | |||
@@ -23,6 +23,7 @@ obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o | |||
23 | obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o | 23 | obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o |
24 | obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o | 24 | obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o |
25 | obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o | 25 | obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o |
26 | obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o | ||
26 | obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o | 27 | obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o |
27 | obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o | 28 | obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o |
28 | obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o | 29 | obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o |
diff --git a/drivers/input/misc/gpio_tilt_polled.c b/drivers/input/misc/gpio_tilt_polled.c new file mode 100644 index 000000000000..277a0574c199 --- /dev/null +++ b/drivers/input/misc/gpio_tilt_polled.c | |||
@@ -0,0 +1,213 @@ | |||
1 | /* | ||
2 | * Driver for tilt switches connected via GPIO lines | ||
3 | * not capable of generating interrupts | ||
4 | * | ||
5 | * Copyright (C) 2011 Heiko Stuebner <heiko@sntech.de> | ||
6 | * | ||
7 | * based on: drivers/input/keyboard/gpio_keys_polled.c | ||
8 | * | ||
9 | * Copyright (C) 2007-2010 Gabor Juhos <juhosg@openwrt.org> | ||
10 | * Copyright (C) 2010 Nuno Goncalves <nunojpg@gmail.com> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | */ | ||
16 | |||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/input.h> | ||
22 | #include <linux/input-polldev.h> | ||
23 | #include <linux/ioport.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <linux/gpio.h> | ||
26 | #include <linux/input/gpio_tilt.h> | ||
27 | |||
28 | #define DRV_NAME "gpio-tilt-polled" | ||
29 | |||
30 | struct gpio_tilt_polled_dev { | ||
31 | struct input_polled_dev *poll_dev; | ||
32 | struct device *dev; | ||
33 | const struct gpio_tilt_platform_data *pdata; | ||
34 | |||
35 | int last_state; | ||
36 | |||
37 | int threshold; | ||
38 | int count; | ||
39 | }; | ||
40 | |||
41 | static void gpio_tilt_polled_poll(struct input_polled_dev *dev) | ||
42 | { | ||
43 | struct gpio_tilt_polled_dev *tdev = dev->private; | ||
44 | const struct gpio_tilt_platform_data *pdata = tdev->pdata; | ||
45 | struct input_dev *input = dev->input; | ||
46 | struct gpio_tilt_state *tilt_state = NULL; | ||
47 | int state, i; | ||
48 | |||
49 | if (tdev->count < tdev->threshold) { | ||
50 | tdev->count++; | ||
51 | } else { | ||
52 | state = 0; | ||
53 | for (i = 0; i < pdata->nr_gpios; i++) | ||
54 | state |= (!!gpio_get_value(pdata->gpios[i].gpio) << i); | ||
55 | |||
56 | if (state != tdev->last_state) { | ||
57 | for (i = 0; i < pdata->nr_states; i++) | ||
58 | if (pdata->states[i].gpios == state) | ||
59 | tilt_state = &pdata->states[i]; | ||
60 | |||
61 | if (tilt_state) { | ||
62 | for (i = 0; i < pdata->nr_axes; i++) | ||
63 | input_report_abs(input, | ||
64 | pdata->axes[i].axis, | ||
65 | tilt_state->axes[i]); | ||
66 | |||
67 | input_sync(input); | ||
68 | } | ||
69 | |||
70 | tdev->count = 0; | ||
71 | tdev->last_state = state; | ||
72 | } | ||
73 | } | ||
74 | } | ||
75 | |||
76 | static void gpio_tilt_polled_open(struct input_polled_dev *dev) | ||
77 | { | ||
78 | struct gpio_tilt_polled_dev *tdev = dev->private; | ||
79 | const struct gpio_tilt_platform_data *pdata = tdev->pdata; | ||
80 | |||
81 | if (pdata->enable) | ||
82 | pdata->enable(tdev->dev); | ||
83 | |||
84 | /* report initial state of the axes */ | ||
85 | tdev->last_state = -1; | ||
86 | tdev->count = tdev->threshold; | ||
87 | gpio_tilt_polled_poll(tdev->poll_dev); | ||
88 | } | ||
89 | |||
90 | static void gpio_tilt_polled_close(struct input_polled_dev *dev) | ||
91 | { | ||
92 | struct gpio_tilt_polled_dev *tdev = dev->private; | ||
93 | const struct gpio_tilt_platform_data *pdata = tdev->pdata; | ||
94 | |||
95 | if (pdata->disable) | ||
96 | pdata->disable(tdev->dev); | ||
97 | } | ||
98 | |||
99 | static int __devinit gpio_tilt_polled_probe(struct platform_device *pdev) | ||
100 | { | ||
101 | const struct gpio_tilt_platform_data *pdata = pdev->dev.platform_data; | ||
102 | struct device *dev = &pdev->dev; | ||
103 | struct gpio_tilt_polled_dev *tdev; | ||
104 | struct input_polled_dev *poll_dev; | ||
105 | struct input_dev *input; | ||
106 | int error, i; | ||
107 | |||
108 | if (!pdata || !pdata->poll_interval) | ||
109 | return -EINVAL; | ||
110 | |||
111 | tdev = kzalloc(sizeof(struct gpio_tilt_polled_dev), GFP_KERNEL); | ||
112 | if (!tdev) { | ||
113 | dev_err(dev, "no memory for private data\n"); | ||
114 | return -ENOMEM; | ||
115 | } | ||
116 | |||
117 | error = gpio_request_array(pdata->gpios, pdata->nr_gpios); | ||
118 | if (error) { | ||
119 | dev_err(dev, | ||
120 | "Could not request tilt GPIOs: %d\n", error); | ||
121 | goto err_free_tdev; | ||
122 | } | ||
123 | |||
124 | poll_dev = input_allocate_polled_device(); | ||
125 | if (!poll_dev) { | ||
126 | dev_err(dev, "no memory for polled device\n"); | ||
127 | error = -ENOMEM; | ||
128 | goto err_free_gpios; | ||
129 | } | ||
130 | |||
131 | poll_dev->private = tdev; | ||
132 | poll_dev->poll = gpio_tilt_polled_poll; | ||
133 | poll_dev->poll_interval = pdata->poll_interval; | ||
134 | poll_dev->open = gpio_tilt_polled_open; | ||
135 | poll_dev->close = gpio_tilt_polled_close; | ||
136 | |||
137 | input = poll_dev->input; | ||
138 | |||
139 | input->name = pdev->name; | ||
140 | input->phys = DRV_NAME"/input0"; | ||
141 | input->dev.parent = &pdev->dev; | ||
142 | |||
143 | input->id.bustype = BUS_HOST; | ||
144 | input->id.vendor = 0x0001; | ||
145 | input->id.product = 0x0001; | ||
146 | input->id.version = 0x0100; | ||
147 | |||
148 | __set_bit(EV_ABS, input->evbit); | ||
149 | for (i = 0; i < pdata->nr_axes; i++) | ||
150 | input_set_abs_params(input, pdata->axes[i].axis, | ||
151 | pdata->axes[i].min, pdata->axes[i].max, | ||
152 | pdata->axes[i].fuzz, pdata->axes[i].flat); | ||
153 | |||
154 | tdev->threshold = DIV_ROUND_UP(pdata->debounce_interval, | ||
155 | pdata->poll_interval); | ||
156 | |||
157 | tdev->poll_dev = poll_dev; | ||
158 | tdev->dev = dev; | ||
159 | tdev->pdata = pdata; | ||
160 | |||
161 | error = input_register_polled_device(poll_dev); | ||
162 | if (error) { | ||
163 | dev_err(dev, "unable to register polled device, err=%d\n", | ||
164 | error); | ||
165 | goto err_free_polldev; | ||
166 | } | ||
167 | |||
168 | platform_set_drvdata(pdev, tdev); | ||
169 | |||
170 | return 0; | ||
171 | |||
172 | err_free_polldev: | ||
173 | input_free_polled_device(poll_dev); | ||
174 | err_free_gpios: | ||
175 | gpio_free_array(pdata->gpios, pdata->nr_gpios); | ||
176 | err_free_tdev: | ||
177 | kfree(tdev); | ||
178 | |||
179 | return error; | ||
180 | } | ||
181 | |||
182 | static int __devexit gpio_tilt_polled_remove(struct platform_device *pdev) | ||
183 | { | ||
184 | struct gpio_tilt_polled_dev *tdev = platform_get_drvdata(pdev); | ||
185 | const struct gpio_tilt_platform_data *pdata = tdev->pdata; | ||
186 | |||
187 | platform_set_drvdata(pdev, NULL); | ||
188 | |||
189 | input_unregister_polled_device(tdev->poll_dev); | ||
190 | input_free_polled_device(tdev->poll_dev); | ||
191 | |||
192 | gpio_free_array(pdata->gpios, pdata->nr_gpios); | ||
193 | |||
194 | kfree(tdev); | ||
195 | |||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | static struct platform_driver gpio_tilt_polled_driver = { | ||
200 | .probe = gpio_tilt_polled_probe, | ||
201 | .remove = __devexit_p(gpio_tilt_polled_remove), | ||
202 | .driver = { | ||
203 | .name = DRV_NAME, | ||
204 | .owner = THIS_MODULE, | ||
205 | }, | ||
206 | }; | ||
207 | |||
208 | module_platform_driver(gpio_tilt_polled_driver); | ||
209 | |||
210 | MODULE_LICENSE("GPL v2"); | ||
211 | MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); | ||
212 | MODULE_DESCRIPTION("Polled GPIO tilt driver"); | ||
213 | MODULE_ALIAS("platform:" DRV_NAME); | ||