aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHeiko Stübner <heiko@sntech.de>2011-11-29 14:04:09 -0500
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2011-12-01 02:41:16 -0500
commit3bfd5c5baf66e975b0f365a0cda8d75bf2953ebe (patch)
tree49517da40dd6071ed9d3acde9fa1ef97d39ede13
parenta6c61789c8499381a5fe612f11dc95d0b55e590a (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>
-rw-r--r--Documentation/input/gpio-tilt.txt103
-rw-r--r--drivers/input/misc/Kconfig14
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/gpio_tilt_polled.c213
-rw-r--r--include/linux/input/gpio_tilt.h73
5 files changed, 404 insertions, 0 deletions
diff --git a/Documentation/input/gpio-tilt.txt b/Documentation/input/gpio-tilt.txt
new file mode 100644
index 000000000000..06d60c3ff5e7
--- /dev/null
+++ b/Documentation/input/gpio-tilt.txt
@@ -0,0 +1,103 @@
1Driver for tilt-switches connected via GPIOs
2============================================
3
4Generic driver to read data from tilt switches connected via gpios.
5Orientation can be provided by one or more than one tilt switches,
6i.e. each tilt switch providing one axis, and the number of axes
7is also not limited.
8
9
10Data structures:
11----------------
12
13The array of struct gpio in the gpios field is used to list the gpios
14that represent the current tilt state.
15
16The array of struct gpio_tilt_axis describes the axes that are reported
17to the input system. The values set therein are used for the
18input_set_abs_params calls needed to init the axes.
19
20The array of struct gpio_tilt_state maps gpio states to the corresponding
21values to report. The gpio state is represented as a bitfield where the
22bit-index corresponds to the index of the gpio in the struct gpio array.
23In the same manner the values stored in the axes array correspond to
24the elements of the gpio_tilt_axis-array.
25
26
27Example:
28--------
29
30Example configuration for a single TS1003 tilt switch that rotates around
31one axis in 4 steps and emitts the current tilt via two GPIOs.
32
33static int sg060_tilt_enable(struct device *dev) {
34 /* code to enable the sensors */
35};
36
37static void sg060_tilt_disable(struct device *dev) {
38 /* code to disable the sensors */
39};
40
41static struct gpio sg060_tilt_gpios[] = {
42 { SG060_TILT_GPIO_SENSOR1, GPIOF_IN, "tilt_sensor1" },
43 { SG060_TILT_GPIO_SENSOR2, GPIOF_IN, "tilt_sensor2" },
44};
45
46static struct gpio_tilt_state sg060_tilt_states[] = {
47 {
48 .gpios = (0 << 1) | (0 << 0),
49 .axes = (int[]) {
50 0,
51 },
52 }, {
53 .gpios = (0 << 1) | (1 << 0),
54 .axes = (int[]) {
55 1, /* 90 degrees */
56 },
57 }, {
58 .gpios = (1 << 1) | (1 << 0),
59 .axes = (int[]) {
60 2, /* 180 degrees */
61 },
62 }, {
63 .gpios = (1 << 1) | (0 << 0),
64 .axes = (int[]) {
65 3, /* 270 degrees */
66 },
67 },
68};
69
70static struct gpio_tilt_axis sg060_tilt_axes[] = {
71 {
72 .axis = ABS_RY,
73 .min = 0,
74 .max = 3,
75 .fuzz = 0,
76 .flat = 0,
77 },
78};
79
80static struct gpio_tilt_platform_data sg060_tilt_pdata= {
81 .gpios = sg060_tilt_gpios,
82 .nr_gpios = ARRAY_SIZE(sg060_tilt_gpios),
83
84 .axes = sg060_tilt_axes,
85 .nr_axes = ARRAY_SIZE(sg060_tilt_axes),
86
87 .states = sg060_tilt_states,
88 .nr_states = ARRAY_SIZE(sg060_tilt_states),
89
90 .debounce_interval = 100,
91
92 .poll_interval = 1000,
93 .enable = sg060_tilt_enable,
94 .disable = sg060_tilt_disable,
95};
96
97static struct platform_device sg060_device_tilt = {
98 .name = "gpio-tilt-polled",
99 .id = -1,
100 .dev = {
101 .platform_data = &sg060_tilt_pdata,
102 },
103};
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
182config 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
182config INPUT_IXP4XX_BEEPER 196config 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
23obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o 23obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
24obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o 24obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
25obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o 25obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
26obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o
26obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o 27obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
27obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o 28obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
28obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o 29obj-$(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
30struct 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
41static 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
76static 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
90static 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
99static 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
172err_free_polldev:
173 input_free_polled_device(poll_dev);
174err_free_gpios:
175 gpio_free_array(pdata->gpios, pdata->nr_gpios);
176err_free_tdev:
177 kfree(tdev);
178
179 return error;
180}
181
182static 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
199static 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
208module_platform_driver(gpio_tilt_polled_driver);
209
210MODULE_LICENSE("GPL v2");
211MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
212MODULE_DESCRIPTION("Polled GPIO tilt driver");
213MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/include/linux/input/gpio_tilt.h b/include/linux/input/gpio_tilt.h
new file mode 100644
index 000000000000..c1cc52d380e0
--- /dev/null
+++ b/include/linux/input/gpio_tilt.h
@@ -0,0 +1,73 @@
1#ifndef _INPUT_GPIO_TILT_H
2#define _INPUT_GPIO_TILT_H
3
4/**
5 * struct gpio_tilt_axis - Axis used by the tilt switch
6 * @axis: Constant describing the axis, e.g. ABS_X
7 * @min: minimum value for abs_param
8 * @max: maximum value for abs_param
9 * @fuzz: fuzz value for abs_param
10 * @flat: flat value for abs_param
11 */
12struct gpio_tilt_axis {
13 int axis;
14 int min;
15 int max;
16 int fuzz;
17 int flat;
18};
19
20/**
21 * struct gpio_tilt_state - state description
22 * @gpios: bitfield of gpio target-states for the value
23 * @axes: array containing the axes settings for the gpio state
24 * The array indizes must correspond to the axes defined
25 * in platform_data
26 *
27 * This structure describes a supported axis settings
28 * and the necessary gpio-state which represent it.
29 *
30 * The n-th bit in the bitfield describes the state of the n-th GPIO
31 * from the gpios-array defined in gpio_regulator_config below.
32 */
33struct gpio_tilt_state {
34 int gpios;
35 int *axes;
36};
37
38/**
39 * struct gpio_tilt_platform_data
40 * @gpios: Array containing the gpios determining the tilt state
41 * @nr_gpios: Number of gpios
42 * @axes: Array of gpio_tilt_axis descriptions
43 * @nr_axes: Number of axes
44 * @states: Array of gpio_tilt_state entries describing
45 * the gpio state for specific tilts
46 * @nr_states: Number of states available
47 * @debounce_interval: debounce ticks interval in msecs
48 * @poll_interval: polling interval in msecs - for polling driver only
49 * @enable: callback to enable the tilt switch
50 * @disable: callback to disable the tilt switch
51 *
52 * This structure contains gpio-tilt-switch configuration
53 * information that must be passed by platform code to the
54 * gpio-tilt input driver.
55 */
56struct gpio_tilt_platform_data {
57 struct gpio *gpios;
58 int nr_gpios;
59
60 struct gpio_tilt_axis *axes;
61 int nr_axes;
62
63 struct gpio_tilt_state *states;
64 int nr_states;
65
66 int debounce_interval;
67
68 unsigned int poll_interval;
69 int (*enable)(struct device *dev);
70 void (*disable)(struct device *dev);
71};
72
73#endif