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 | |
| 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>
| -rw-r--r-- | Documentation/input/gpio-tilt.txt | 103 | ||||
| -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 | ||||
| -rw-r--r-- | include/linux/input/gpio_tilt.h | 73 |
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 @@ | |||
| 1 | Driver for tilt-switches connected via GPIOs | ||
| 2 | ============================================ | ||
| 3 | |||
| 4 | Generic driver to read data from tilt switches connected via gpios. | ||
| 5 | Orientation can be provided by one or more than one tilt switches, | ||
| 6 | i.e. each tilt switch providing one axis, and the number of axes | ||
| 7 | is also not limited. | ||
| 8 | |||
| 9 | |||
| 10 | Data structures: | ||
| 11 | ---------------- | ||
| 12 | |||
| 13 | The array of struct gpio in the gpios field is used to list the gpios | ||
| 14 | that represent the current tilt state. | ||
| 15 | |||
| 16 | The array of struct gpio_tilt_axis describes the axes that are reported | ||
| 17 | to the input system. The values set therein are used for the | ||
| 18 | input_set_abs_params calls needed to init the axes. | ||
| 19 | |||
| 20 | The array of struct gpio_tilt_state maps gpio states to the corresponding | ||
| 21 | values to report. The gpio state is represented as a bitfield where the | ||
| 22 | bit-index corresponds to the index of the gpio in the struct gpio array. | ||
| 23 | In the same manner the values stored in the axes array correspond to | ||
| 24 | the elements of the gpio_tilt_axis-array. | ||
| 25 | |||
| 26 | |||
| 27 | Example: | ||
| 28 | -------- | ||
| 29 | |||
| 30 | Example configuration for a single TS1003 tilt switch that rotates around | ||
| 31 | one axis in 4 steps and emitts the current tilt via two GPIOs. | ||
| 32 | |||
| 33 | static int sg060_tilt_enable(struct device *dev) { | ||
| 34 | /* code to enable the sensors */ | ||
| 35 | }; | ||
| 36 | |||
| 37 | static void sg060_tilt_disable(struct device *dev) { | ||
| 38 | /* code to disable the sensors */ | ||
| 39 | }; | ||
| 40 | |||
| 41 | static 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 | |||
| 46 | static 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 | |||
| 70 | static 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 | |||
| 80 | static 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 | |||
| 97 | static 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 | ||
| 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); | ||
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 | */ | ||
| 12 | struct 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 | */ | ||
| 33 | struct 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 | */ | ||
| 56 | struct 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 | ||
