* [PATCH] generic driver for rotary encoders on GPIOs
2009-02-27 6:43 ` Daniel Mack
@ 2009-02-27 8:48 ` Daniel Mack
2009-02-27 17:13 ` hartleys
2009-02-27 18:00 ` Daniel Mack
2 siblings, 0 replies; 32+ messages in thread
From: Daniel Mack @ 2009-02-27 8:48 UTC (permalink / raw)
To: linux-input; +Cc: Daniel Mack
This patch adds a generic driver for rotary encoders connected to GPIO
pins of a system. It relies on gpiolib and generic hardware irqs. The
documentation that also comes with this patch explains the concept and
how to use the driver.
Signed-off-by: Daniel Mack <daniel@caiaq.de>
---
new version:
* dropped depencency to GENERIC_HARDIRQS
* dropped gpio_a and gpio_b from struct rotary_encoder_platform_data
* use irq_to_gpio()
* added include/linux/rotary-encoder.h
Documentation/input/rotary-encoder.txt | 108 +++++++++++++++
drivers/input/misc/Kconfig | 11 ++
drivers/input/misc/Makefile | 2 +
drivers/input/misc/rotary_encoder.c | 237 ++++++++++++++++++++++++++++++++
include/linux/rotary-encoder.h | 10 ++
5 files changed, 368 insertions(+), 0 deletions(-)
create mode 100644 Documentation/input/rotary-encoder.txt
create mode 100644 drivers/input/misc/rotary_encoder.c
create mode 100644 include/linux/rotary-encoder.h
diff --git a/Documentation/input/rotary-encoder.txt b/Documentation/input/rotary-encoder.txt
new file mode 100644
index 0000000..ef18df6
--- /dev/null
+++ b/Documentation/input/rotary-encoder.txt
@@ -0,0 +1,108 @@
+rotary-encoder - a generic driver for GPIO connected devices
+Daniel Mack <daniel@caiaq.de>, Feb 2009
+
+0. Function
+-----------
+
+Rotary encoders are devices which are connected to the CPU or other
+peripherals with two wires. The outputs are phase-shifted by 90 degrees
+and by triggering on falling and rising edges, the turn direction can
+be determined.
+
+The phase diagram of these two outputs look like this:
+
+ _____ _____ _____
+ | | | | | |
+ Channel A ____| |_____| |_____| |____
+
+ : : : : : : : : : : : :
+ __ _____ _____ _____
+ | | | | | | |
+ Channel B |_____| |_____| |_____| |__
+
+ : : : : : : : : : : : :
+ Event a b c d a b c d a b c d
+
+ |<-------->|
+ one step
+
+
+For more information, please see
+ http://en.wikipedia.org/wiki/Rotary_encoder
+
+
+1. Events / state machine
+-------------------------
+
+a) Rising edge on channel A, channel B in low state
+ This state is used to recognize a clockwise turn
+
+b) Rising edge on channel B, channel A in high state
+ When entering this state, the encoder is put into 'armed' state,
+ meaning that there it has seen half the way of a one-step transition.
+
+c) Falling edge on channel A, channel B in high state
+ This state is used to recognize a counter-clockwise turn
+
+d) Falling edge on channel B, channel A in low state
+ Parking position. If the encoder enters this state, a full transition
+ should have happend, unless it flipped back on half the way. The
+ 'armed' state tells us about that.
+
+2. Platform requirements
+------------------------
+
+As there is no hardware dependent call in this driver, the platform it is
+used with must support gpiolib. Another requirement is that IRQs must be
+able to fire on both edges.
+
+
+3. Board integration
+--------------------
+
+To use this driver in your system, register a platform_device with the
+name 'rotary-encoder' and associate the IRQs and some specific platform
+data with it.
+
+struct rotary_encoder_platform_data is declared in
+include/linux/rotary-encoder.h and needs to be filled with the number of
+steps the encoder has and can carry information about externally inverted
+signals (because of used invertig buffer or other reasons).
+
+Because GPIO to IRQ mapping is platform specific, this information must
+be given in seperately to the driver. See the example below.
+
+---------<snip>---------
+
+/* board support file example */
+
+#define GPIO_ROTARY_A 1
+#define GPIO_ROTARY_B 2
+
+static struct resource rotary_encoder_resources[] = {
+ [0] = {
+ .flags = IORESOURCE_IRQ,
+ .start = gpio_to_irq(GPIO_ROTARY_A)
+ },
+ [1] = {
+ .flags = IORESOURCE_IRQ,
+ .start = gpio_to_irq(GPIO_ROTARY_B)
+ }
+};
+
+static struct rotary_encoder_platform_data my_rotary_encoder_info = {
+ .steps = 24,
+ .inverted_a = 0,
+ .inverted_b = 0,
+};
+
+static struct platform_device rotary_encoder_device = {
+ .name = "rotary-encoder",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(rotary_encoder_resources),
+ .resource = rotary_encoder_resources,
+ .dev = {
+ .platform_data = &my_rotary_encoder_info,
+ }
+};
+
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 67e5553..806d2e6 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -227,4 +227,15 @@ config INPUT_PCF50633_PMU
Say Y to include support for delivering PMU events via input
layer on NXP PCF50633.
+config INPUT_GPIO_ROTARY_ENCODER
+ tristate "Rotary encoders connected to GPIO pins"
+ depends on GPIOLIB && GENERIC_GPIO
+ help
+ Say Y here to add support for rotary encoders connected to GPIO lines.
+ Check file:Documentation/incput/rotary_encoder.txt for more
+ information.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rotary_encoder.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index bb62e6e..e86cee6 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -22,3 +22,5 @@ obj-$(CONFIG_INPUT_UINPUT) += uinput.o
obj-$(CONFIG_INPUT_APANEL) += apanel.o
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
+obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
+
diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c
new file mode 100644
index 0000000..77d8eca
--- /dev/null
+++ b/drivers/input/misc/rotary_encoder.c
@@ -0,0 +1,237 @@
+/*
+ * rotary_encoder.c
+ *
+ * (c) 2009 Daniel Mack <daniel@caiaq.de>
+ *
+ * state machine code inspired by code from Tim Ruetz
+ *
+ * A generic driver for rotary encoders connected to GPIO lines.
+ * See file:Documentation/input/rotary_encoder.txt for more information
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/rotary-encoder.h>
+
+#define DRV_NAME "rotary-encoder"
+
+struct rotary_encoder {
+ unsigned int irq_a;
+ unsigned int irq_b;
+ unsigned int gpio_a;
+ unsigned int gpio_b;
+ unsigned int pos;
+ unsigned int armed;
+ unsigned int dir;
+ struct input_dev *input;
+ struct rotary_encoder_platform_data *pdata;
+};
+
+static void rotary_encoder_left(struct rotary_encoder *encoder)
+{
+ encoder->pos += encoder->pdata->steps;
+ encoder->pos--;
+ encoder->pos %= encoder->pdata->steps;
+ input_report_abs(encoder->input, ABS_X, encoder->pos);
+ input_sync(encoder->input);
+}
+
+static void rotary_encoder_right(struct rotary_encoder *encoder)
+{
+ encoder->pos++;
+ encoder->pos %= encoder->pdata->steps;
+ input_report_abs(encoder->input, ABS_X, encoder->pos);
+ input_sync(encoder->input);
+}
+
+static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
+{
+ struct rotary_encoder *encoder = dev_id;
+ int a = !!gpio_get_value(encoder->gpio_a);
+ int b = !!gpio_get_value(encoder->gpio_b);
+ int state;
+
+ a ^= encoder->pdata->inverted_a;
+ b ^= encoder->pdata->inverted_b;
+ state = (a << 1) | b;
+
+ switch (state) {
+ case 0x0:
+ if (!encoder->armed)
+ break;
+
+ if (encoder->dir)
+ rotary_encoder_left(encoder);
+ else
+ rotary_encoder_right(encoder);
+
+ encoder->armed = 0;
+ break;
+ case 0x1:
+ case 0x2:
+ if (!encoder->armed)
+ break;
+
+ encoder->dir = state - 1;
+ break;
+ case 0x3:
+ encoder->armed = 1;
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit rotary_encoder_probe(struct platform_device *pdev)
+{
+ struct rotary_encoder *encoder;
+ struct resource *irq_a_res;
+ struct resource *irq_b_res;
+ unsigned long irq_flags;
+ int err;
+
+ encoder = kzalloc(sizeof(struct rotary_encoder), GFP_KERNEL);
+ if (!encoder) {
+ dev_err(&pdev->dev, "failed to allocate driver data\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ irq_flags = IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE;
+ irq_a_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ irq_b_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ encoder->pdata = pdev->dev.platform_data;
+
+ if (!irq_a_res || !irq_b_res || !encoder->pdata) {
+ dev_err(&pdev->dev, "insufficient resources\n");
+ err = -ENOENT;
+ goto exit_free_mem;
+ }
+
+ encoder->irq_a = irq_a_res->start;
+ encoder->irq_b = irq_b_res->start;
+ encoder->gpio_a = irq_to_gpio(encoder->irq_a);
+ encoder->gpio_b = irq_to_gpio(encoder->irq_b);
+
+ /* create and register the input driver */
+ encoder->input = input_allocate_device();
+ if (!encoder->input) {
+ dev_err(&pdev->dev, "failed to allocate input device\n");
+ err = -ENOMEM;
+ goto exit_free_mem;
+ }
+
+ encoder->input->name = pdev->name;
+ encoder->input->id.bustype = BUS_HOST;
+ encoder->input->dev.parent = &pdev->dev;
+ encoder->input->evbit[0] = BIT_MASK(EV_ABS);
+ encoder->input->absbit[0] = BIT_MASK(ABS_X);
+ input_set_abs_params(encoder->input, ABS_X, 0,
+ encoder->pdata->steps, 0, 1);
+ platform_set_drvdata(pdev, encoder);
+
+ err = input_register_device(encoder->input);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register input device\n");
+ goto exit_free_input;
+ }
+
+ /* request the GPIOs */
+ err = gpio_request(encoder->gpio_a, DRV_NAME);
+ if (err) {
+ dev_err(&pdev->dev, "unable to request GPIO %d\n",
+ encoder->gpio_a);
+ goto exit_free_input;
+ }
+
+ err = gpio_request(encoder->gpio_b, DRV_NAME);
+ if (err) {
+ dev_err(&pdev->dev, "unable to request GPIO %d\n",
+ encoder->gpio_b);
+ goto exit_free_gpio_a;
+ }
+
+ /* request the IRQs */
+ err = request_irq(encoder->irq_a, &rotary_encoder_irq,
+ irq_flags, DRV_NAME, encoder);
+ if (err) {
+ dev_err(&pdev->dev, "unable to request IRQ %d\n",
+ encoder->irq_a);
+ goto exit_free_gpio;
+ }
+
+ err = request_irq(encoder->irq_b, &rotary_encoder_irq,
+ irq_flags, DRV_NAME, encoder);
+ if (err) {
+ dev_err(&pdev->dev, "unable to request IRQ %d\n",
+ encoder->irq_b);
+ goto exit_free_irq_a;
+ }
+
+ return 0;
+
+exit_free_irq_a:
+ free_irq(encoder->irq_a, encoder);
+exit_free_gpio:
+ gpio_free(encoder->gpio_b);
+exit_free_gpio_a:
+ gpio_free(encoder->gpio_a);
+exit_free_input:
+ input_free_device(encoder->input);
+exit_free_mem:
+ kfree(encoder);
+exit:
+ return err;
+}
+
+
+static int __devexit rotary_encoder_remove(struct platform_device *pdev)
+{
+ struct rotary_encoder *encoder = platform_get_drvdata(pdev);
+ gpio_free(encoder->gpio_a);
+ gpio_free(encoder->gpio_b);
+ free_irq(encoder->irq_a, encoder);
+ free_irq(encoder->irq_b, encoder);
+ input_free_device(encoder->input);
+ platform_set_drvdata(pdev, NULL);
+ kfree(encoder);
+ return 0;
+}
+
+static struct platform_driver rotary_encoder_driver = {
+ .probe = rotary_encoder_probe,
+ .remove = __devexit_p(rotary_encoder_remove),
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init rotary_encoder_init(void)
+{
+ return platform_driver_register(&rotary_encoder_driver);
+}
+
+static void __exit rotary_encoder_exit(void)
+{
+ platform_driver_unregister(&rotary_encoder_driver);
+}
+
+module_init(rotary_encoder_init);
+module_exit(rotary_encoder_exit);
+
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DESCRIPTION("GPIO rotary encoder driver");
+MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/include/linux/rotary-encoder.h b/include/linux/rotary-encoder.h
new file mode 100644
index 0000000..b6238ff
--- /dev/null
+++ b/include/linux/rotary-encoder.h
@@ -0,0 +1,10 @@
+#ifndef __ROTARY_ENCODER_H__
+#define __ROTARY_ENCODER_H__
+
+struct rotary_encoder_platform_data {
+ unsigned int steps;
+ unsigned int inverted_a;
+ unsigned int inverted_b;
+};
+
+#endif /* __ROTARY_ENCODER_H__ */
--
1.6.1.3
^ permalink raw reply related [flat|nested] 32+ messages in thread
* RE: [PATCH] generic driver for rotary encoders on GPIOs
2009-02-27 6:43 ` Daniel Mack
2009-02-27 8:48 ` Daniel Mack
@ 2009-02-27 17:13 ` hartleys
2009-02-27 17:17 ` Daniel Mack
2009-02-27 18:00 ` Daniel Mack
2 siblings, 1 reply; 32+ messages in thread
From: hartleys @ 2009-02-27 17:13 UTC (permalink / raw)
To: Daniel Mack; +Cc: linux-input
On Thursday, February 26, 2009 11:43 PM, Daniel Mack wrote:
> On Thu, Feb 26, 2009 at 10:18:40PM -0500, hartleys wrote:
>> +static struct rotary_encoder_platform_data my_rotary_encoder_info =
{
>> + .steps = 24,
>> + .gpio_a = GPIO_ROTARY_A,
>> + .gpio_b = GPIO_ROTARY_B,
>> + .inverted_a = 0,
>> + .inverted_b = 0,
>> +};
>> +
>> +static struct platform_device rotary_encoder_device = {
>> + .name = "rotary-encoder",
>> + .id = 0,
>> + .num_resources = ARRAY_SIZE(rotary_encoder_resources),
>> + .resource = rotary_encoder_resources,
>> + .dev = {
>> + .platform_data = &my_rotary_encoder_info,
>> + }
>> +};
>> +
>>
>> IRQ_GPIO appears to be only defined for mach-imx and mach-pxa.
>>
>> Why not just use the platform_data information to get the irq for
>> the gpio at runtime with gpio_to_irq()?
>
> Yes, true. But wouldn't be using the resources only and irq_to_gpio()
> be the even cleaner solution?
I really don't know which one is cleaner or the correct choice.
The new example in your updated patch will not work for a generic
solution.
+/* board support file example */
+
+#define GPIO_ROTARY_A 1
+#define GPIO_ROTARY_B 2
+
+static struct resource rotary_encoder_resources[] = {
+ [0] = {
+ .flags = IORESOURCE_IRQ,
+ .start = gpio_to_irq(GPIO_ROTARY_A)
+ },
+ [1] = {
+ .flags = IORESOURCE_IRQ,
+ .start = gpio_to_irq(GPIO_ROTARY_B)
+ }
+};
Unless gpio_to_irq() is defined as a macro that returns a constant at
compile time you will get an error similar to:
error: initializer element is not constant
error: (near initialization for 'rotary_encoder_resources[0].start')
error: initializer element is not constant
error: (near initialization for 'rotary_encoder_resources[1].start')
>> +#include <linux/rotary-encoder.h>
>>
>> This file is missing in the patch.
>
> Will be added in the next version.
Cool.
I was able to compile and test your driver on an ep93xx based system
with a Bourns ECW1J-B24-BC0024 rotary encoder connected to the EGPIO8
and EGPIO9 pins. But I had to make the following changes to your
original patch.
Keep gpio_a and gpio_b in the rotary_encoder_platform_data.
Removed the struct resource from the platform setup.
Following is the platform setup I used:
---
#define GPIO_ROTARY_A EP93XX_GPIO_LINE_EGPIO8
#define GPIO_ROTARY_B EP93XX_GPIO_LINE_EGPIO9
static struct rotary_encoder_platform_data my_rotary_encoder_info = {
.steps = 24,
.gpio_a = GPIO_ROTARY_A,
.gpio_b = GPIO_ROTARY_B,
.inverted_a = 0,
.inverted_b = 0,
};
static struct platform_device rotary_encoder_device = {
.name = "rotary-encoder",
.id = 0,
.dev = {
.platform_data = &my_rotary_encoder_info,
},
};
---
I also had to modified the probe routine as follows:
---
static int __devinit rotary_encoder_probe(struct platform_device *pdev)
{
struct rotary_encoder *encoder;
- struct resource *irq_a_res;
- struct resource *irq_b_res;
- unsigned long irq_flags;
int err;
encoder = kzalloc(sizeof(struct rotary_encoder), GFP_KERNEL);
if (!encoder) {
dev_err(&pdev->dev, "failed to allocate driver data\n");
err = -ENOMEM;
goto exit;
}
- irq_flags = IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE;
- irq_a_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- irq_b_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
encoder->pdata = pdev->dev.platform_data;
-
- if (!irq_a_res || !irq_b_res || !encoder->pdata) {
+ if (!encoder->pdata) {
dev_err(&pdev->dev, "insufficient resources\n");
err = -ENOENT;
goto exit_free_mem;
}
- encoder->irq_a = irq_a_res->start;
- encoder->irq_b = irq_b_res->start;
+ encoder->irq_a = gpio_to_irq(encoder->pdata->gpio_a);
+ encoder->irq_b = gpio_to_irq(encoder->pdata->gpio_b);
/* create and register the input driver */
encoder->input = input_allocate_device();
if (!encoder->input) {
dev_err(&pdev->dev, "failed to allocate input
device\n");
err = -ENOMEM;
goto exit_free_mem;
}
encoder->input->name = pdev->name;
encoder->input->id.bustype = BUS_HOST;
encoder->input->dev.parent = &pdev->dev;
encoder->input->evbit[0] = BIT_MASK(EV_ABS);
encoder->input->absbit[0] = BIT_MASK(ABS_X);
input_set_abs_params(encoder->input, ABS_X, 0,
encoder->pdata->steps, 0, 1);
platform_set_drvdata(pdev, encoder);
err = input_register_device(encoder->input);
if (err) {
dev_err(&pdev->dev, "failed to register input
device\n");
goto exit_free_input;
}
/* request the GPIOs */
err = gpio_request(encoder->pdata->gpio_a, DRV_NAME);
if (err) {
dev_err(&pdev->dev, "unable to request GPIO %d\n",
encoder->pdata->gpio_a);
goto exit_free_input;
}
err = gpio_request(encoder->pdata->gpio_b, DRV_NAME);
if (err) {
dev_err(&pdev->dev, "unable to request GPIO %d\n",
encoder->pdata->gpio_b);
goto exit_free_gpio_a;
}
/* request the IRQs */
err = request_irq(encoder->irq_a, &rotary_encoder_irq,
- irq_flags, DRV_NAME, encoder);
+ IORESOURCE_IRQ_HIGHEDGE |
IORESOURCE_IRQ_LOWEDGE,
+ DRV_NAME, encoder);
if (err) {
dev_err(&pdev->dev, "unable to request IRQ %d\n",
encoder->irq_a);
goto exit_free_gpio;
}
err = request_irq(encoder->irq_b, &rotary_encoder_irq,
- irq_flags, DRV_NAME, encoder);
+ IORESOURCE_IRQ_HIGHEDGE |
IORESOURCE_IRQ_LOWEDGE,
+ DRV_NAME, encoder); if (err) {
dev_err(&pdev->dev, "unable to request IRQ %d\n",
encoder->irq_b);
goto exit_free_irq_a;
}
return 0;
exit_free_irq_a:
free_irq(encoder->irq_a, encoder);
exit_free_gpio:
gpio_free(encoder->pdata->gpio_b);
exit_free_gpio_a:
gpio_free(encoder->pdata->gpio_a);
exit_free_input:
input_free_device(encoder->input);
exit_free_mem:
kfree(encoder);
exit:
return err;
}
Regards,
Hartley
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH] generic driver for rotary encoders on GPIOs
2009-02-27 17:13 ` hartleys
@ 2009-02-27 17:17 ` Daniel Mack
0 siblings, 0 replies; 32+ messages in thread
From: Daniel Mack @ 2009-02-27 17:17 UTC (permalink / raw)
To: hartleys; +Cc: linux-input
On Fri, Feb 27, 2009 at 12:13:10PM -0500, hartleys wrote:
> Cool.
>
> I was able to compile and test your driver on an ep93xx based system
> with a Bourns ECW1J-B24-BC0024 rotary encoder connected to the EGPIO8
> and EGPIO9 pins. But I had to make the following changes to your
> original patch.
Ok, I'm fine with your changes. Will post a new patch that includes
them.
Daniel
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH] generic driver for rotary encoders on GPIOs
2009-02-27 6:43 ` Daniel Mack
2009-02-27 8:48 ` Daniel Mack
2009-02-27 17:13 ` hartleys
@ 2009-02-27 18:00 ` Daniel Mack
2009-02-27 18:30 ` hartleys
2 siblings, 1 reply; 32+ messages in thread
From: Daniel Mack @ 2009-02-27 18:00 UTC (permalink / raw)
To: linux-input; +Cc: Daniel Mack
This patch adds a generic driver for rotary encoders connected to GPIO
pins of a system. It relies on gpiolib and generic hardware irqs. The
documentation that also comes with this patch explains the concept and
how to use the driver.
Signed-off-by: Daniel Mack <daniel@caiaq.de>
---
new version as discussed on linux-input.
Documentation/input/rotary-encoder.txt | 99 ++++++++++++++
drivers/input/misc/Kconfig | 11 ++
drivers/input/misc/Makefile | 2 +
drivers/input/misc/rotary_encoder.c | 228 ++++++++++++++++++++++++++++++++
include/linux/rotary_encoder.h | 12 ++
5 files changed, 352 insertions(+), 0 deletions(-)
create mode 100644 Documentation/input/rotary-encoder.txt
create mode 100644 drivers/input/misc/rotary_encoder.c
create mode 100644 include/linux/rotary_encoder.h
diff --git a/Documentation/input/rotary-encoder.txt b/Documentation/input/rotary-encoder.txt
new file mode 100644
index 0000000..1519a2d
--- /dev/null
+++ b/Documentation/input/rotary-encoder.txt
@@ -0,0 +1,99 @@
+rotary-encoder - a generic driver for GPIO connected devices
+Daniel Mack <daniel@caiaq.de>, Feb 2009
+
+0. Function
+-----------
+
+Rotary encoders are devices which are connected to the CPU or other
+peripherals with two wires. The outputs are phase-shifted by 90 degrees
+and by triggering on falling and rising edges, the turn direction can
+be determined.
+
+The phase diagram of these two outputs look like this:
+
+ _____ _____ _____
+ | | | | | |
+ Channel A ____| |_____| |_____| |____
+
+ : : : : : : : : : : : :
+ __ _____ _____ _____
+ | | | | | | |
+ Channel B |_____| |_____| |_____| |__
+
+ : : : : : : : : : : : :
+ Event a b c d a b c d a b c d
+
+ |<-------->|
+ one step
+
+
+For more information, please see
+ http://en.wikipedia.org/wiki/Rotary_encoder
+
+
+1. Events / state machine
+-------------------------
+
+a) Rising edge on channel A, channel B in low state
+ This state is used to recognize a clockwise turn
+
+b) Rising edge on channel B, channel A in high state
+ When entering this state, the encoder is put into 'armed' state,
+ meaning that there it has seen half the way of a one-step transition.
+
+c) Falling edge on channel A, channel B in high state
+ This state is used to recognize a counter-clockwise turn
+
+d) Falling edge on channel B, channel A in low state
+ Parking position. If the encoder enters this state, a full transition
+ should have happend, unless it flipped back on half the way. The
+ 'armed' state tells us about that.
+
+2. Platform requirements
+------------------------
+
+As there is no hardware dependent call in this driver, the platform it is
+used with must support gpiolib. Another requirement is that IRQs must be
+able to fire on both edges.
+
+
+3. Board integration
+--------------------
+
+To use this driver in your system, register a platform_device with the
+name 'rotary-encoder' and associate the IRQs and some specific platform
+data with it.
+
+struct rotary_encoder_platform_data is declared in
+include/linux/rotary-encoder.h and needs to be filled with the number of
+steps the encoder has and can carry information about externally inverted
+signals (because of used invertig buffer or other reasons).
+
+Because GPIO to IRQ mapping is platform specific, this information must
+be given in seperately to the driver. See the example below.
+
+---------<snip>---------
+
+/* board support file example */
+
+#include <linux/rotary_encoder.h>
+
+#define GPIO_ROTARY_A 1
+#define GPIO_ROTARY_B 2
+
+static struct rotary_encoder_platform_data my_rotary_encoder_info = {
+ .steps = 24,
+ .gpio_a = GPIO_ROTARY_A,
+ .gpio_b = GPIO_ROTARY_B,
+ .inverted_a = 0,
+ .inverted_b = 0,
+};
+
+static struct platform_device rotary_encoder_device = {
+ .name = "rotary-encoder",
+ .id = 0,
+ .dev = {
+ .platform_data = &my_rotary_encoder_info,
+ }
+};
+
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 67e5553..806d2e6 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -227,4 +227,15 @@ config INPUT_PCF50633_PMU
Say Y to include support for delivering PMU events via input
layer on NXP PCF50633.
+config INPUT_GPIO_ROTARY_ENCODER
+ tristate "Rotary encoders connected to GPIO pins"
+ depends on GPIOLIB && GENERIC_GPIO
+ help
+ Say Y here to add support for rotary encoders connected to GPIO lines.
+ Check file:Documentation/incput/rotary_encoder.txt for more
+ information.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rotary_encoder.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index bb62e6e..e86cee6 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -22,3 +22,5 @@ obj-$(CONFIG_INPUT_UINPUT) += uinput.o
obj-$(CONFIG_INPUT_APANEL) += apanel.o
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
+obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
+
diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c
new file mode 100644
index 0000000..2621ed9
--- /dev/null
+++ b/drivers/input/misc/rotary_encoder.c
@@ -0,0 +1,228 @@
+/*
+ * rotary_encoder.c
+ *
+ * (c) 2009 Daniel Mack <daniel@caiaq.de>
+ *
+ * state machine code inspired by code from Tim Ruetz
+ *
+ * A generic driver for rotary encoders connected to GPIO lines.
+ * See file:Documentation/input/rotary_encoder.txt for more information
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/rotary_encoder.h>
+
+#define DRV_NAME "rotary-encoder"
+
+struct rotary_encoder {
+ unsigned int irq_a;
+ unsigned int irq_b;
+ unsigned int pos;
+ unsigned int armed;
+ unsigned int dir;
+ struct input_dev *input;
+ struct rotary_encoder_platform_data *pdata;
+};
+
+static void rotary_encoder_left(struct rotary_encoder *encoder)
+{
+ encoder->pos += encoder->pdata->steps;
+ encoder->pos--;
+ encoder->pos %= encoder->pdata->steps;
+ input_report_abs(encoder->input, ABS_X, encoder->pos);
+ input_sync(encoder->input);
+}
+
+static void rotary_encoder_right(struct rotary_encoder *encoder)
+{
+ encoder->pos++;
+ encoder->pos %= encoder->pdata->steps;
+ input_report_abs(encoder->input, ABS_X, encoder->pos);
+ input_sync(encoder->input);
+}
+
+static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
+{
+ struct rotary_encoder *encoder = dev_id;
+ int a = !!gpio_get_value(encoder->pdata->gpio_a);
+ int b = !!gpio_get_value(encoder->pdata->gpio_b);
+ int state;
+
+ a ^= encoder->pdata->inverted_a;
+ b ^= encoder->pdata->inverted_b;
+ state = (a << 1) | b;
+
+ switch (state) {
+ case 0x0:
+ if (!encoder->armed)
+ break;
+
+ if (encoder->dir)
+ rotary_encoder_left(encoder);
+ else
+ rotary_encoder_right(encoder);
+
+ encoder->armed = 0;
+ break;
+ case 0x1:
+ case 0x2:
+ if (!encoder->armed)
+ break;
+
+ encoder->dir = state - 1;
+ break;
+ case 0x3:
+ encoder->armed = 1;
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit rotary_encoder_probe(struct platform_device *pdev)
+{
+ struct rotary_encoder *encoder;
+ int err;
+
+ encoder = kzalloc(sizeof(struct rotary_encoder), GFP_KERNEL);
+ if (!encoder) {
+ dev_err(&pdev->dev, "failed to allocate driver data\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ encoder->pdata = pdev->dev.platform_data;
+
+ if (!encoder->pdata || !encoder->pdata->steps) {
+ dev_err(&pdev->dev, "invalid platform data\n");
+ err = -ENOENT;
+ goto exit_free_mem;
+ }
+
+ encoder->irq_a = gpio_to_irq(encoder->pdata->gpio_a);
+ encoder->irq_b = gpio_to_irq(encoder->pdata->gpio_b);
+
+ /* create and register the input driver */
+ encoder->input = input_allocate_device();
+ if (!encoder->input) {
+ dev_err(&pdev->dev, "failed to allocate input device\n");
+ err = -ENOMEM;
+ goto exit_free_mem;
+ }
+
+ encoder->input->name = pdev->name;
+ encoder->input->id.bustype = BUS_HOST;
+ encoder->input->dev.parent = &pdev->dev;
+ encoder->input->evbit[0] = BIT_MASK(EV_ABS);
+ encoder->input->absbit[0] = BIT_MASK(ABS_X);
+ input_set_abs_params(encoder->input, ABS_X, 0,
+ encoder->pdata->steps, 0, 1);
+ platform_set_drvdata(pdev, encoder);
+
+ err = input_register_device(encoder->input);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register input device\n");
+ goto exit_free_input;
+ }
+
+ /* request the GPIOs */
+ err = gpio_request(encoder->pdata->gpio_a, DRV_NAME);
+ if (err) {
+ dev_err(&pdev->dev, "unable to request GPIO %d\n",
+ encoder->pdata->gpio_a);
+ goto exit_free_input;
+ }
+
+ err = gpio_request(encoder->pdata->gpio_b, DRV_NAME);
+ if (err) {
+ dev_err(&pdev->dev, "unable to request GPIO %d\n",
+ encoder->pdata->gpio_b);
+ goto exit_free_gpio_a;
+ }
+
+ /* request the IRQs */
+ err = request_irq(encoder->irq_a, &rotary_encoder_irq,
+ IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE,
+ DRV_NAME, encoder);
+ if (err) {
+ dev_err(&pdev->dev, "unable to request IRQ %d\n",
+ encoder->irq_a);
+ goto exit_free_gpio;
+ }
+
+ err = request_irq(encoder->irq_b, &rotary_encoder_irq,
+ IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE,
+ DRV_NAME, encoder);
+ if (err) {
+ dev_err(&pdev->dev, "unable to request IRQ %d\n",
+ encoder->irq_b);
+ goto exit_free_irq_a;
+ }
+
+ return 0;
+
+exit_free_irq_a:
+ free_irq(encoder->irq_a, encoder);
+exit_free_gpio:
+ gpio_free(encoder->pdata->gpio_b);
+exit_free_gpio_a:
+ gpio_free(encoder->pdata->gpio_a);
+exit_free_input:
+ input_free_device(encoder->input);
+exit_free_mem:
+ kfree(encoder);
+exit:
+ return err;
+}
+
+static int __devexit rotary_encoder_remove(struct platform_device *pdev)
+{
+ struct rotary_encoder *encoder = platform_get_drvdata(pdev);
+ gpio_free(encoder->pdata->gpio_a);
+ gpio_free(encoder->pdata->gpio_b);
+ free_irq(encoder->irq_a, encoder);
+ free_irq(encoder->irq_b, encoder);
+ input_free_device(encoder->input);
+ platform_set_drvdata(pdev, NULL);
+ kfree(encoder);
+ return 0;
+}
+
+static struct platform_driver rotary_encoder_driver = {
+ .probe = rotary_encoder_probe,
+ .remove = __devexit_p(rotary_encoder_remove),
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init rotary_encoder_init(void)
+{
+ return platform_driver_register(&rotary_encoder_driver);
+}
+
+static void __exit rotary_encoder_exit(void)
+{
+ platform_driver_unregister(&rotary_encoder_driver);
+}
+
+module_init(rotary_encoder_init);
+module_exit(rotary_encoder_exit);
+
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DESCRIPTION("GPIO rotary encoder driver");
+MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/include/linux/rotary_encoder.h b/include/linux/rotary_encoder.h
new file mode 100644
index 0000000..2c52861
--- /dev/null
+++ b/include/linux/rotary_encoder.h
@@ -0,0 +1,12 @@
+#ifndef __ROTARY_ENCODER_H__
+#define __ROTARY_ENCODER_H__
+
+struct rotary_encoder_platform_data {
+ unsigned int steps;
+ unsigned int gpio_a;
+ unsigned int gpio_b;
+ unsigned int inverted_a;
+ unsigned int inverted_b;
+};
+
+#endif /* __ROTARY_ENCODER_H__ */
--
1.6.1.3
^ permalink raw reply related [flat|nested] 32+ messages in thread
* RE: [PATCH] generic driver for rotary encoders on GPIOs
2009-02-27 18:00 ` Daniel Mack
@ 2009-02-27 18:30 ` hartleys
2009-02-27 18:34 ` Daniel Mack
0 siblings, 1 reply; 32+ messages in thread
From: hartleys @ 2009-02-27 18:30 UTC (permalink / raw)
To: Daniel Mack, linux-input
On Friday, February 27, 2009 11:01 AM, Daniel Mack wrote:
> This patch adds a generic driver for rotary encoders connected to
> GPIO pins of a system. It relies on gpiolib and generic hardware
> irqs. The documentation that also comes with this patch explains
> the concept and how to use the driver.
>
> Signed-off-by: Daniel Mack <daniel@caiaq.de>
> ---
> new version as discussed on linux-input.
Works great now on the ep93xx with no changes.
I have a minor patch to it that allows the platform init to configure
the absolute/relative axis to report the encoder on. If you would like
to see it I can put together a patch.
Also, any ideas if the driver could be modified to support more that one
rotary encoder?
It might be possible to load driver multiple times, once for each
encoder, but then all the events show up in different /dev/input/event*
places.
BTW, pretty cool. I was handling a rotary encoder in my FPGA but this
makes it much cleaner.
Regards,
Hartley
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH] generic driver for rotary encoders on GPIOs
2009-02-27 18:30 ` hartleys
@ 2009-02-27 18:34 ` Daniel Mack
2009-02-27 20:15 ` hartleys
0 siblings, 1 reply; 32+ messages in thread
From: Daniel Mack @ 2009-02-27 18:34 UTC (permalink / raw)
To: hartleys; +Cc: linux-input
On Fri, Feb 27, 2009 at 01:30:15PM -0500, hartleys wrote:
> On Friday, February 27, 2009 11:01 AM, Daniel Mack wrote:
> > This patch adds a generic driver for rotary encoders connected to
> > GPIO pins of a system. It relies on gpiolib and generic hardware
> > irqs. The documentation that also comes with this patch explains
> > the concept and how to use the driver.
> >
> > Signed-off-by: Daniel Mack <daniel@caiaq.de>
> > ---
> > new version as discussed on linux-input.
>
> Works great now on the ep93xx with no changes.
Glad to hear that :)
> I have a minor patch to it that allows the platform init to configure
> the absolute/relative axis to report the encoder on. If you would like
> to see it I can put together a patch.
Yes, and probably just post it as follow-up as soon as this one has been
commited. I'm new to this list, so I don't know who can/will/wants to do
this?
> Also, any ideas if the driver could be modified to support more that one
> rotary encoder?
>
> It might be possible to load driver multiple times, once for each
> encoder, but then all the events show up in different /dev/input/event*
> places.
That would have been my idea as well. It doesn't really matter how many
input devices you listen to from user space and as the number of
encoders on a certain board is not dynamic, I don't see much reason to
put any efford in this ;)
Daniel
^ permalink raw reply [flat|nested] 32+ messages in thread
* RE: [PATCH] generic driver for rotary encoders on GPIOs
2009-02-27 18:34 ` Daniel Mack
@ 2009-02-27 20:15 ` hartleys
2009-03-02 14:43 ` Daniel Mack
0 siblings, 1 reply; 32+ messages in thread
From: hartleys @ 2009-02-27 20:15 UTC (permalink / raw)
To: Daniel Mack; +Cc: linux-input
On Friday, February 27, 2009 11:35 AM, Daniel Mack wrote:
> On Fri, Feb 27, 2009 at 01:30:15PM -0500, hartleys wrote:
>> On Friday, February 27, 2009 11:01 AM, Daniel Mack wrote:
>>> This patch adds a generic driver for rotary encoders connected
>>> to GPIO pins of a system. It relies on gpiolib and generic
>>> hardware irqs. The documentation that also comes with this patch
>>> explains the concept and how to use the driver.
>>>
>>> Signed-off-by: Daniel Mack <daniel@caiaq.de>
>>> ---
>>> new version as discussed on linux-input.
>>
>> Works great now on the ep93xx with no changes.
>
> Glad to hear that :)
Not sure if it matters but you can add my Tested-by if you like.
Tested-by: H Hartley Sweeten <hsweeten@visionengravers.com>
>> I have a minor patch to it that allows the platform init to
>> configure the absolute/relative axis to report the encoder on.
>> If you would like to see it I can put together a patch.
>
> Yes, and probably just post it as follow-up as soon as this one
> has been commited. I'm new to this list, so I don't know who
> can/will/wants to do this?
Dmitry Torokhov is listed as the maintainer of the input drivers. I
think he needs to Ack the driver in order for it to be accepted and then
committed to the kernel.
You also might try posting the patch to linux-kernel to get some more
eyes on it.
>> Also, any ideas if the driver could be modified to support more that
>> one rotary encoder?
>>
>> It might be possible to load driver multiple times, once for
>> each encoder, but then all the events show up in different
>> /dev/input/event* places.
>
> That would have been my idea as well. It doesn't really matter how
> many input devices you listen to from user space and as the number
> of encoders on a certain board is not dynamic, I don't see much
> reason to put any efford in this ;)
For right now the current driver looks ok.
I do think handling multiple encoders would be handy. Having all the
encoders report with one /dev/input/event* is easier to handle in
userspace. You could still load the driver multiple times in order to
"group" logical collections of encoders.
Regards,
Hartley
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH] generic driver for rotary encoders on GPIOs
2009-02-27 20:15 ` hartleys
@ 2009-03-02 14:43 ` Daniel Mack
2009-03-03 8:52 ` Dmitry Torokhov
0 siblings, 1 reply; 32+ messages in thread
From: Daniel Mack @ 2009-03-02 14:43 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input
On Fri, Feb 27, 2009 at 03:15:23PM -0500, hartleys wrote:
> Not sure if it matters but you can add my Tested-by if you like.
>
> Tested-by: H Hartley Sweeten <hsweeten@visionengravers.com>
>
> >> I have a minor patch to it that allows the platform init to
> >> configure the absolute/relative axis to report the encoder on.
> >> If you would like to see it I can put together a patch.
> >
> > Yes, and probably just post it as follow-up as soon as this one
> > has been commited. I'm new to this list, so I don't know who
> > can/will/wants to do this?
>
> Dmitry Torokhov is listed as the maintainer of the input drivers. I
> think he needs to Ack the driver in order for it to be accepted and then
> committed to the kernel.
Dmitry, are you willing to take this patch? Just wanted to make sure
it's not getting lost :)
Daniel
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH] generic driver for rotary encoders on GPIOs
2009-03-02 14:43 ` Daniel Mack
@ 2009-03-03 8:52 ` Dmitry Torokhov
2009-03-03 9:03 ` Dmitry Torokhov
0 siblings, 1 reply; 32+ messages in thread
From: Dmitry Torokhov @ 2009-03-03 8:52 UTC (permalink / raw)
To: Daniel Mack; +Cc: linux-input
Hi Daniel,
On Mon, Mar 02, 2009 at 03:43:08PM +0100, Daniel Mack wrote:
> On Fri, Feb 27, 2009 at 03:15:23PM -0500, hartleys wrote:
> > Not sure if it matters but you can add my Tested-by if you like.
> >
> > Tested-by: H Hartley Sweeten <hsweeten@visionengravers.com>
> >
> > >> I have a minor patch to it that allows the platform init to
> > >> configure the absolute/relative axis to report the encoder on.
> > >> If you would like to see it I can put together a patch.
> > >
> > > Yes, and probably just post it as follow-up as soon as this one
> > > has been commited. I'm new to this list, so I don't know who
> > > can/will/wants to do this?
> >
> > Dmitry Torokhov is listed as the maintainer of the input drivers. I
> > think he needs to Ack the driver in order for it to be accepted and then
> > committed to the kernel.
>
> Dmitry, are you willing to take this patch? Just wanted to make sure
> it's not getting lost :)
>
I am willing, however I would like you to change it so you call
input_unregister_device() instead of input_free_device() in cases
when input device was successfully registered with the input core.
When input_register_device() fails (or if it wasn't invoked) then
you should call input_free_device().
Thanks!
--
Dmitry
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH] generic driver for rotary encoders on GPIOs
2009-03-03 8:52 ` Dmitry Torokhov
@ 2009-03-03 9:03 ` Dmitry Torokhov
2009-03-03 9:59 ` Daniel Mack
0 siblings, 1 reply; 32+ messages in thread
From: Dmitry Torokhov @ 2009-03-03 9:03 UTC (permalink / raw)
To: Daniel Mack; +Cc: linux-input
On Tue, Mar 03, 2009 at 12:52:00AM -0800, Dmitry Torokhov wrote:
> Hi Daniel,
>
> On Mon, Mar 02, 2009 at 03:43:08PM +0100, Daniel Mack wrote:
> > On Fri, Feb 27, 2009 at 03:15:23PM -0500, hartleys wrote:
> > > Not sure if it matters but you can add my Tested-by if you like.
> > >
> > > Tested-by: H Hartley Sweeten <hsweeten@visionengravers.com>
> > >
> > > >> I have a minor patch to it that allows the platform init to
> > > >> configure the absolute/relative axis to report the encoder on.
> > > >> If you would like to see it I can put together a patch.
> > > >
> > > > Yes, and probably just post it as follow-up as soon as this one
> > > > has been commited. I'm new to this list, so I don't know who
> > > > can/will/wants to do this?
> > >
> > > Dmitry Torokhov is listed as the maintainer of the input drivers. I
> > > think he needs to Ack the driver in order for it to be accepted and then
> > > committed to the kernel.
> >
> > Dmitry, are you willing to take this patch? Just wanted to make sure
> > it's not getting lost :)
> >
>
> I am willing, however I would like you to change it so you call
> input_unregister_device() instead of input_free_device() in cases
> when input device was successfully registered with the input core.
> When input_register_device() fails (or if it wasn't invoked) then
> you should call input_free_device().
>
> Thanks!
>
BTW, you should probably make the axis configurable via platform data
instead of hardcoding it to ABS_X.
--
Dmitry
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH] generic driver for rotary encoders on GPIOs
2009-03-03 9:03 ` Dmitry Torokhov
@ 2009-03-03 9:59 ` Daniel Mack
2009-03-04 8:48 ` Dmitry Torokhov
0 siblings, 1 reply; 32+ messages in thread
From: Daniel Mack @ 2009-03-03 9:59 UTC (permalink / raw)
To: linux-input; +Cc: Daniel Mack, Dmitry Torokhov, H Hartley Sweeten
This patch adds a generic driver for rotary encoders connected to GPIO
pins of a system. It relies on gpiolib and generic hardware irqs. The
documentation that also comes with this patch explains the concept and
how to use the driver.
Signed-off-by: Daniel Mack <daniel@caiaq.de>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: H Hartley Sweeten <hsweeten@visionengravers.com>
---
New version with Dmitry's change requests included:
* calling input_free_device() in case input_register_device() fails
* calling input_unregister_device() in all other cases
* made the axis information part of the platform data
Documentation/input/rotary-encoder.txt | 101 ++++++++++++++
drivers/input/misc/Kconfig | 11 ++
drivers/input/misc/Makefile | 2 +
drivers/input/misc/rotary_encoder.c | 229 ++++++++++++++++++++++++++++++++
include/linux/rotary_encoder.h | 13 ++
5 files changed, 356 insertions(+), 0 deletions(-)
create mode 100644 Documentation/input/rotary-encoder.txt
create mode 100644 drivers/input/misc/rotary_encoder.c
create mode 100644 include/linux/rotary_encoder.h
diff --git a/Documentation/input/rotary-encoder.txt b/Documentation/input/rotary-encoder.txt
new file mode 100644
index 0000000..435102a
--- /dev/null
+++ b/Documentation/input/rotary-encoder.txt
@@ -0,0 +1,101 @@
+rotary-encoder - a generic driver for GPIO connected devices
+Daniel Mack <daniel@caiaq.de>, Feb 2009
+
+0. Function
+-----------
+
+Rotary encoders are devices which are connected to the CPU or other
+peripherals with two wires. The outputs are phase-shifted by 90 degrees
+and by triggering on falling and rising edges, the turn direction can
+be determined.
+
+The phase diagram of these two outputs look like this:
+
+ _____ _____ _____
+ | | | | | |
+ Channel A ____| |_____| |_____| |____
+
+ : : : : : : : : : : : :
+ __ _____ _____ _____
+ | | | | | | |
+ Channel B |_____| |_____| |_____| |__
+
+ : : : : : : : : : : : :
+ Event a b c d a b c d a b c d
+
+ |<-------->|
+ one step
+
+
+For more information, please see
+ http://en.wikipedia.org/wiki/Rotary_encoder
+
+
+1. Events / state machine
+-------------------------
+
+a) Rising edge on channel A, channel B in low state
+ This state is used to recognize a clockwise turn
+
+b) Rising edge on channel B, channel A in high state
+ When entering this state, the encoder is put into 'armed' state,
+ meaning that there it has seen half the way of a one-step transition.
+
+c) Falling edge on channel A, channel B in high state
+ This state is used to recognize a counter-clockwise turn
+
+d) Falling edge on channel B, channel A in low state
+ Parking position. If the encoder enters this state, a full transition
+ should have happend, unless it flipped back on half the way. The
+ 'armed' state tells us about that.
+
+2. Platform requirements
+------------------------
+
+As there is no hardware dependent call in this driver, the platform it is
+used with must support gpiolib. Another requirement is that IRQs must be
+able to fire on both edges.
+
+
+3. Board integration
+--------------------
+
+To use this driver in your system, register a platform_device with the
+name 'rotary-encoder' and associate the IRQs and some specific platform
+data with it.
+
+struct rotary_encoder_platform_data is declared in
+include/linux/rotary-encoder.h and needs to be filled with the number of
+steps the encoder has and can carry information about externally inverted
+signals (because of used invertig buffer or other reasons).
+
+Because GPIO to IRQ mapping is platform specific, this information must
+be given in seperately to the driver. See the example below.
+
+---------<snip>---------
+
+/* board support file example */
+
+#include <linux/input.h>
+#include <linux/rotary_encoder.h>
+
+#define GPIO_ROTARY_A 1
+#define GPIO_ROTARY_B 2
+
+static struct rotary_encoder_platform_data my_rotary_encoder_info = {
+ .steps = 24,
+ .axis = ABS_X,
+ .gpio_a = GPIO_ROTARY_A,
+ .gpio_b = GPIO_ROTARY_B,
+ .inverted_a = 0,
+ .inverted_b = 0,
+};
+
+static struct platform_device rotary_encoder_device = {
+ .name = "rotary-encoder",
+ .id = 0,
+ .dev = {
+ .platform_data = &my_rotary_encoder_info,
+ }
+};
+
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 67e5553..806d2e6 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -227,4 +227,15 @@ config INPUT_PCF50633_PMU
Say Y to include support for delivering PMU events via input
layer on NXP PCF50633.
+config INPUT_GPIO_ROTARY_ENCODER
+ tristate "Rotary encoders connected to GPIO pins"
+ depends on GPIOLIB && GENERIC_GPIO
+ help
+ Say Y here to add support for rotary encoders connected to GPIO lines.
+ Check file:Documentation/incput/rotary_encoder.txt for more
+ information.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rotary_encoder.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index bb62e6e..e86cee6 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -22,3 +22,5 @@ obj-$(CONFIG_INPUT_UINPUT) += uinput.o
obj-$(CONFIG_INPUT_APANEL) += apanel.o
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
+obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
+
diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c
new file mode 100644
index 0000000..11ebb30
--- /dev/null
+++ b/drivers/input/misc/rotary_encoder.c
@@ -0,0 +1,229 @@
+/*
+ * rotary_encoder.c
+ *
+ * (c) 2009 Daniel Mack <daniel@caiaq.de>
+ *
+ * state machine code inspired by code from Tim Ruetz
+ *
+ * A generic driver for rotary encoders connected to GPIO lines.
+ * See file:Documentation/input/rotary_encoder.txt for more information
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/rotary_encoder.h>
+
+#define DRV_NAME "rotary-encoder"
+
+struct rotary_encoder {
+ unsigned int irq_a;
+ unsigned int irq_b;
+ unsigned int pos;
+ unsigned int armed;
+ unsigned int dir;
+ struct input_dev *input;
+ struct rotary_encoder_platform_data *pdata;
+};
+
+static void rotary_encoder_left(struct rotary_encoder *encoder)
+{
+ encoder->pos += encoder->pdata->steps;
+ encoder->pos--;
+ encoder->pos %= encoder->pdata->steps;
+ input_report_abs(encoder->input, encoder->pdata->axis, encoder->pos);
+ input_sync(encoder->input);
+}
+
+static void rotary_encoder_right(struct rotary_encoder *encoder)
+{
+ encoder->pos++;
+ encoder->pos %= encoder->pdata->steps;
+ input_report_abs(encoder->input, encoder->pdata->axis, encoder->pos);
+ input_sync(encoder->input);
+}
+
+static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
+{
+ struct rotary_encoder *encoder = dev_id;
+ int a = !!gpio_get_value(encoder->pdata->gpio_a);
+ int b = !!gpio_get_value(encoder->pdata->gpio_b);
+ int state;
+
+ a ^= encoder->pdata->inverted_a;
+ b ^= encoder->pdata->inverted_b;
+ state = (a << 1) | b;
+
+ switch (state) {
+ case 0x0:
+ if (!encoder->armed)
+ break;
+
+ if (encoder->dir)
+ rotary_encoder_left(encoder);
+ else
+ rotary_encoder_right(encoder);
+
+ encoder->armed = 0;
+ break;
+ case 0x1:
+ case 0x2:
+ if (!encoder->armed)
+ break;
+
+ encoder->dir = state - 1;
+ break;
+ case 0x3:
+ encoder->armed = 1;
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit rotary_encoder_probe(struct platform_device *pdev)
+{
+ struct rotary_encoder *encoder;
+ int err;
+
+ encoder = kzalloc(sizeof(struct rotary_encoder), GFP_KERNEL);
+ if (!encoder) {
+ dev_err(&pdev->dev, "failed to allocate driver data\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ encoder->pdata = pdev->dev.platform_data;
+
+ if (!encoder->pdata || !encoder->pdata->steps) {
+ dev_err(&pdev->dev, "invalid platform data\n");
+ err = -ENOENT;
+ goto exit_free_mem;
+ }
+
+ encoder->irq_a = gpio_to_irq(encoder->pdata->gpio_a);
+ encoder->irq_b = gpio_to_irq(encoder->pdata->gpio_b);
+
+ /* create and register the input driver */
+ encoder->input = input_allocate_device();
+ if (!encoder->input) {
+ dev_err(&pdev->dev, "failed to allocate input device\n");
+ err = -ENOMEM;
+ goto exit_free_mem;
+ }
+
+ encoder->input->name = pdev->name;
+ encoder->input->id.bustype = BUS_HOST;
+ encoder->input->dev.parent = &pdev->dev;
+ encoder->input->evbit[0] = BIT_MASK(EV_ABS);
+ encoder->input->absbit[0] = BIT_MASK(encoder->pdata->axis);
+ input_set_abs_params(encoder->input, encoder->pdata->axis, 0,
+ encoder->pdata->steps, 0, 1);
+ platform_set_drvdata(pdev, encoder);
+
+ err = input_register_device(encoder->input);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register input device\n");
+ input_free_device(encoder->input);
+ goto exit_free_mem;
+ }
+
+ /* request the GPIOs */
+ err = gpio_request(encoder->pdata->gpio_a, DRV_NAME);
+ if (err) {
+ dev_err(&pdev->dev, "unable to request GPIO %d\n",
+ encoder->pdata->gpio_a);
+ goto exit_free_input;
+ }
+
+ err = gpio_request(encoder->pdata->gpio_b, DRV_NAME);
+ if (err) {
+ dev_err(&pdev->dev, "unable to request GPIO %d\n",
+ encoder->pdata->gpio_b);
+ goto exit_free_gpio_a;
+ }
+
+ /* request the IRQs */
+ err = request_irq(encoder->irq_a, &rotary_encoder_irq,
+ IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE,
+ DRV_NAME, encoder);
+ if (err) {
+ dev_err(&pdev->dev, "unable to request IRQ %d\n",
+ encoder->irq_a);
+ goto exit_free_gpio;
+ }
+
+ err = request_irq(encoder->irq_b, &rotary_encoder_irq,
+ IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE,
+ DRV_NAME, encoder);
+ if (err) {
+ dev_err(&pdev->dev, "unable to request IRQ %d\n",
+ encoder->irq_b);
+ goto exit_free_irq_a;
+ }
+
+ return 0;
+
+exit_free_irq_a:
+ free_irq(encoder->irq_a, encoder);
+exit_free_gpio:
+ gpio_free(encoder->pdata->gpio_b);
+exit_free_gpio_a:
+ gpio_free(encoder->pdata->gpio_a);
+exit_free_input:
+ input_unregister_device(encoder->input);
+exit_free_mem:
+ kfree(encoder);
+exit:
+ return err;
+}
+
+static int __devexit rotary_encoder_remove(struct platform_device *pdev)
+{
+ struct rotary_encoder *encoder = platform_get_drvdata(pdev);
+ gpio_free(encoder->pdata->gpio_a);
+ gpio_free(encoder->pdata->gpio_b);
+ free_irq(encoder->irq_a, encoder);
+ free_irq(encoder->irq_b, encoder);
+ input_unregister_device(encoder->input);
+ platform_set_drvdata(pdev, NULL);
+ kfree(encoder);
+ return 0;
+}
+
+static struct platform_driver rotary_encoder_driver = {
+ .probe = rotary_encoder_probe,
+ .remove = __devexit_p(rotary_encoder_remove),
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init rotary_encoder_init(void)
+{
+ return platform_driver_register(&rotary_encoder_driver);
+}
+
+static void __exit rotary_encoder_exit(void)
+{
+ platform_driver_unregister(&rotary_encoder_driver);
+}
+
+module_init(rotary_encoder_init);
+module_exit(rotary_encoder_exit);
+
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DESCRIPTION("GPIO rotary encoder driver");
+MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/include/linux/rotary_encoder.h b/include/linux/rotary_encoder.h
new file mode 100644
index 0000000..12d63a3
--- /dev/null
+++ b/include/linux/rotary_encoder.h
@@ -0,0 +1,13 @@
+#ifndef __ROTARY_ENCODER_H__
+#define __ROTARY_ENCODER_H__
+
+struct rotary_encoder_platform_data {
+ unsigned int steps;
+ unsigned int axis;
+ unsigned int gpio_a;
+ unsigned int gpio_b;
+ unsigned int inverted_a;
+ unsigned int inverted_b;
+};
+
+#endif /* __ROTARY_ENCODER_H__ */
--
1.6.1.3
^ permalink raw reply related [flat|nested] 32+ messages in thread
* Re: [PATCH] generic driver for rotary encoders on GPIOs
2009-03-03 9:59 ` Daniel Mack
@ 2009-03-04 8:48 ` Dmitry Torokhov
2009-03-04 9:50 ` Daniel Mack
0 siblings, 1 reply; 32+ messages in thread
From: Dmitry Torokhov @ 2009-03-04 8:48 UTC (permalink / raw)
To: Daniel Mack; +Cc: linux-input, H Hartley Sweeten
Hi Daniel,
On Tue, Mar 03, 2009 at 10:59:27AM +0100, Daniel Mack wrote:
> This patch adds a generic driver for rotary encoders connected to GPIO
> pins of a system. It relies on gpiolib and generic hardware irqs. The
> documentation that also comes with this patch explains the concept and
> how to use the driver.
>
> Signed-off-by: Daniel Mack <daniel@caiaq.de>
> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> Cc: H Hartley Sweeten <hsweeten@visionengravers.com>
> ---
> New version with Dmitry's change requests included:
>
> * calling input_free_device() in case input_register_device() fails
> * calling input_unregister_device() in all other cases
> * made the axis information part of the platform data
>
I fiddled with the driver a little bit more changing formatting, please
take a look and if you are still OK with it I will apply to 'next'.
Thanks!
--
Dmitry
Input: generic driver for rotary encoders on GPIOs
From: Daniel Mack <daniel@caiaq.de>
This patch adds a generic driver for rotary encoders connected to GPIO
pins of a system. It relies on gpiolib and generic hardware irqs. The
documentation that also comes with this patch explains the concept and
how to use the driver.
Signed-off-by: Daniel Mack <daniel@caiaq.de>
Tested-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---
Documentation/input/rotary-encoder.txt | 101 +++++++++++++++
drivers/input/misc/Kconfig | 11 ++
drivers/input/misc/Makefile | 2
drivers/input/misc/rotary_encoder.c | 219 ++++++++++++++++++++++++++++++++
include/linux/rotary_encoder.h | 13 ++
5 files changed, 346 insertions(+), 0 deletions(-)
create mode 100644 Documentation/input/rotary-encoder.txt
create mode 100644 drivers/input/misc/rotary_encoder.c
create mode 100644 include/linux/rotary_encoder.h
diff --git a/Documentation/input/rotary-encoder.txt b/Documentation/input/rotary-encoder.txt
new file mode 100644
index 0000000..435102a
--- /dev/null
+++ b/Documentation/input/rotary-encoder.txt
@@ -0,0 +1,101 @@
+rotary-encoder - a generic driver for GPIO connected devices
+Daniel Mack <daniel@caiaq.de>, Feb 2009
+
+0. Function
+-----------
+
+Rotary encoders are devices which are connected to the CPU or other
+peripherals with two wires. The outputs are phase-shifted by 90 degrees
+and by triggering on falling and rising edges, the turn direction can
+be determined.
+
+The phase diagram of these two outputs look like this:
+
+ _____ _____ _____
+ | | | | | |
+ Channel A ____| |_____| |_____| |____
+
+ : : : : : : : : : : : :
+ __ _____ _____ _____
+ | | | | | | |
+ Channel B |_____| |_____| |_____| |__
+
+ : : : : : : : : : : : :
+ Event a b c d a b c d a b c d
+
+ |<-------->|
+ one step
+
+
+For more information, please see
+ http://en.wikipedia.org/wiki/Rotary_encoder
+
+
+1. Events / state machine
+-------------------------
+
+a) Rising edge on channel A, channel B in low state
+ This state is used to recognize a clockwise turn
+
+b) Rising edge on channel B, channel A in high state
+ When entering this state, the encoder is put into 'armed' state,
+ meaning that there it has seen half the way of a one-step transition.
+
+c) Falling edge on channel A, channel B in high state
+ This state is used to recognize a counter-clockwise turn
+
+d) Falling edge on channel B, channel A in low state
+ Parking position. If the encoder enters this state, a full transition
+ should have happend, unless it flipped back on half the way. The
+ 'armed' state tells us about that.
+
+2. Platform requirements
+------------------------
+
+As there is no hardware dependent call in this driver, the platform it is
+used with must support gpiolib. Another requirement is that IRQs must be
+able to fire on both edges.
+
+
+3. Board integration
+--------------------
+
+To use this driver in your system, register a platform_device with the
+name 'rotary-encoder' and associate the IRQs and some specific platform
+data with it.
+
+struct rotary_encoder_platform_data is declared in
+include/linux/rotary-encoder.h and needs to be filled with the number of
+steps the encoder has and can carry information about externally inverted
+signals (because of used invertig buffer or other reasons).
+
+Because GPIO to IRQ mapping is platform specific, this information must
+be given in seperately to the driver. See the example below.
+
+---------<snip>---------
+
+/* board support file example */
+
+#include <linux/input.h>
+#include <linux/rotary_encoder.h>
+
+#define GPIO_ROTARY_A 1
+#define GPIO_ROTARY_B 2
+
+static struct rotary_encoder_platform_data my_rotary_encoder_info = {
+ .steps = 24,
+ .axis = ABS_X,
+ .gpio_a = GPIO_ROTARY_A,
+ .gpio_b = GPIO_ROTARY_B,
+ .inverted_a = 0,
+ .inverted_b = 0,
+};
+
+static struct platform_device rotary_encoder_device = {
+ .name = "rotary-encoder",
+ .id = 0,
+ .dev = {
+ .platform_data = &my_rotary_encoder_info,
+ }
+};
+
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 67e5553..806d2e6 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -227,4 +227,15 @@ config INPUT_PCF50633_PMU
Say Y to include support for delivering PMU events via input
layer on NXP PCF50633.
+config INPUT_GPIO_ROTARY_ENCODER
+ tristate "Rotary encoders connected to GPIO pins"
+ depends on GPIOLIB && GENERIC_GPIO
+ help
+ Say Y here to add support for rotary encoders connected to GPIO lines.
+ Check file:Documentation/incput/rotary_encoder.txt for more
+ information.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rotary_encoder.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index bb62e6e..e86cee6 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -22,3 +22,5 @@ obj-$(CONFIG_INPUT_UINPUT) += uinput.o
obj-$(CONFIG_INPUT_APANEL) += apanel.o
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
+obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
+
diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c
new file mode 100644
index 0000000..c9bcab4
--- /dev/null
+++ b/drivers/input/misc/rotary_encoder.c
@@ -0,0 +1,219 @@
+/*
+ * rotary_encoder.c
+ *
+ * (c) 2009 Daniel Mack <daniel@caiaq.de>
+ *
+ * state machine code inspired by code from Tim Ruetz
+ *
+ * A generic driver for rotary encoders connected to GPIO lines.
+ * See file:Documentation/input/rotary_encoder.txt for more information
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/rotary_encoder.h>
+
+#define DRV_NAME "rotary-encoder"
+
+struct rotary_encoder {
+ unsigned int irq_a;
+ unsigned int irq_b;
+ unsigned int pos;
+ unsigned int armed;
+ unsigned int dir;
+ struct input_dev *input;
+ struct rotary_encoder_platform_data *pdata;
+};
+
+static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
+{
+ struct rotary_encoder *encoder = dev_id;
+ struct rotary_encoder_platform_data *pdata = encoder->pdata;
+ int a = !!gpio_get_value(pdata->gpio_a);
+ int b = !!gpio_get_value(pdata->gpio_b);
+ int state;
+
+ a ^= pdata->inverted_a;
+ b ^= pdata->inverted_b;
+ state = (a << 1) | b;
+
+ switch (state) {
+
+ case 0x0:
+ if (encoder->armed) {
+ if (encoder->dir) {
+ /* turning counter-clockwise */
+ encoder->pos += pdata->steps;
+ encoder->pos--;
+ encoder->pos %= pdata->steps;
+ } else {
+ /* turning clockwise */
+ encoder->pos++;
+ encoder->pos %= pdata->steps;
+ }
+ input_report_abs(encoder->input,
+ pdata->axis, encoder->pos);
+ input_sync(encoder->input);
+ encoder->armed = 0;
+ }
+ break;
+
+ case 0x1:
+ case 0x2:
+ if (encoder->armed)
+ encoder->dir = state - 1;
+ break;
+
+ case 0x3:
+ encoder->armed = 1;
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit rotary_encoder_probe(struct platform_device *pdev)
+{
+ struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data;
+ struct rotary_encoder *encoder;
+ struct input_dev *input;
+ int err;
+
+ if (!pdata || !pdata->steps) {
+ dev_err(&pdev->dev, "invalid platform data\n");
+ return -ENOENT;
+ }
+
+ encoder = kzalloc(sizeof(struct rotary_encoder), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!encoder || !input) {
+ dev_err(&pdev->dev, "failed to allocate memory for device\n");
+ err = -ENOMEM;
+ goto exit_free_mem;
+ }
+
+ encoder->input = input;
+ encoder->pdata = pdata;
+ encoder->irq_a = gpio_to_irq(pdata->gpio_a);
+ encoder->irq_b = gpio_to_irq(pdata->gpio_b);
+
+ /* create and register the input driver */
+ input->name = pdev->name;
+ input->id.bustype = BUS_HOST;
+ input->dev.parent = &pdev->dev;
+ input->evbit[0] = BIT_MASK(EV_ABS);
+ input_set_abs_params(encoder->input,
+ pdata->axis, 0, pdata->steps, 0, 1);
+
+ err = input_register_device(input);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register input device\n");
+ goto exit_free_mem;
+ }
+
+ /* request the GPIOs */
+ err = gpio_request(pdata->gpio_a, DRV_NAME);
+ if (err) {
+ dev_err(&pdev->dev, "unable to request GPIO %d\n",
+ pdata->gpio_a);
+ goto exit_unregister_input;
+ }
+
+ err = gpio_request(pdata->gpio_b, DRV_NAME);
+ if (err) {
+ dev_err(&pdev->dev, "unable to request GPIO %d\n",
+ pdata->gpio_b);
+ goto exit_free_gpio_a;
+ }
+
+ /* request the IRQs */
+ err = request_irq(encoder->irq_a, &rotary_encoder_irq,
+ IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE,
+ DRV_NAME, encoder);
+ if (err) {
+ dev_err(&pdev->dev, "unable to request IRQ %d\n",
+ encoder->irq_a);
+ goto exit_free_gpio_b;
+ }
+
+ err = request_irq(encoder->irq_b, &rotary_encoder_irq,
+ IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE,
+ DRV_NAME, encoder);
+ if (err) {
+ dev_err(&pdev->dev, "unable to request IRQ %d\n",
+ encoder->irq_b);
+ goto exit_free_irq_a;
+ }
+
+ platform_set_drvdata(pdev, encoder);
+
+ return 0;
+
+exit_free_irq_a:
+ free_irq(encoder->irq_a, encoder);
+exit_free_gpio_b:
+ gpio_free(pdata->gpio_b);
+exit_free_gpio_a:
+ gpio_free(pdata->gpio_a);
+exit_unregister_input:
+ input_unregister_device(input);
+ input = NULL; /* so we don't try to free it */
+exit_free_mem:
+ input_free_device(input);
+ kfree(encoder);
+ return err;
+}
+
+static int __devexit rotary_encoder_remove(struct platform_device *pdev)
+{
+ struct rotary_encoder *encoder = platform_get_drvdata(pdev);
+ struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data;
+
+ free_irq(encoder->irq_a, encoder);
+ free_irq(encoder->irq_b, encoder);
+ gpio_free(pdata->gpio_a);
+ gpio_free(pdata->gpio_b);
+ input_unregister_device(encoder->input);
+ platform_set_drvdata(pdev, NULL);
+ kfree(encoder);
+
+ return 0;
+}
+
+static struct platform_driver rotary_encoder_driver = {
+ .probe = rotary_encoder_probe,
+ .remove = __devexit_p(rotary_encoder_remove),
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init rotary_encoder_init(void)
+{
+ return platform_driver_register(&rotary_encoder_driver);
+}
+
+static void __exit rotary_encoder_exit(void)
+{
+ platform_driver_unregister(&rotary_encoder_driver);
+}
+
+module_init(rotary_encoder_init);
+module_exit(rotary_encoder_exit);
+
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DESCRIPTION("GPIO rotary encoder driver");
+MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/include/linux/rotary_encoder.h b/include/linux/rotary_encoder.h
new file mode 100644
index 0000000..12d63a3
--- /dev/null
+++ b/include/linux/rotary_encoder.h
@@ -0,0 +1,13 @@
+#ifndef __ROTARY_ENCODER_H__
+#define __ROTARY_ENCODER_H__
+
+struct rotary_encoder_platform_data {
+ unsigned int steps;
+ unsigned int axis;
+ unsigned int gpio_a;
+ unsigned int gpio_b;
+ unsigned int inverted_a;
+ unsigned int inverted_b;
+};
+
+#endif /* __ROTARY_ENCODER_H__ */
^ permalink raw reply related [flat|nested] 32+ messages in thread
* Re: [PATCH] generic driver for rotary encoders on GPIOs
2009-03-04 8:48 ` Dmitry Torokhov
@ 2009-03-04 9:50 ` Daniel Mack
2009-03-04 17:02 ` hartleys
0 siblings, 1 reply; 32+ messages in thread
From: Daniel Mack @ 2009-03-04 9:50 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input, H Hartley Sweeten
On Wed, Mar 04, 2009 at 12:48:52AM -0800, Dmitry Torokhov wrote:
> On Tue, Mar 03, 2009 at 10:59:27AM +0100, Daniel Mack wrote:
> > This patch adds a generic driver for rotary encoders connected to GPIO
> > pins of a system. It relies on gpiolib and generic hardware irqs. The
> > documentation that also comes with this patch explains the concept and
> > how to use the driver.
> >
> > Signed-off-by: Daniel Mack <daniel@caiaq.de>
> > Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> > Cc: H Hartley Sweeten <hsweeten@visionengravers.com>
> > ---
> > New version with Dmitry's change requests included:
> >
> > * calling input_free_device() in case input_register_device() fails
> > * calling input_unregister_device() in all other cases
> > * made the axis information part of the platform data
> >
>
> I fiddled with the driver a little bit more changing formatting, please
> take a look and if you are still OK with it I will apply to 'next'.
Ok for me, except for one thing ...
> + case 0x0:
> + if (encoder->armed) {
> + if (encoder->dir) {
> + /* turning counter-clockwise */
> + encoder->pos += pdata->steps;
> + encoder->pos--;
> + encoder->pos %= pdata->steps;
> + } else {
> + /* turning clockwise */
> + encoder->pos++;
> + encoder->pos %= pdata->steps;
> + }
> + input_report_abs(encoder->input,
> + pdata->axis, encoder->pos);
> + input_sync(encoder->input);
> + encoder->armed = 0;
> + }
I really prefer early exits ("if (!encoder->armed) break;") as it saves
one indentation level and makes the code more readable.
Thanks,
Daniel
^ permalink raw reply [flat|nested] 32+ messages in thread
* RE: [PATCH] generic driver for rotary encoders on GPIOs
2009-03-04 9:50 ` Daniel Mack
@ 2009-03-04 17:02 ` hartleys
2009-03-04 17:20 ` Daniel Mack
0 siblings, 1 reply; 32+ messages in thread
From: hartleys @ 2009-03-04 17:02 UTC (permalink / raw)
To: Daniel Mack, Dmitry Torokhov; +Cc: linux-input
On Wednesday, March 04, 2009 2:51 AM, Daniel Mack wrote:
> On Wed, Mar 04, 2009 at 12:48:52AM -0800, Dmitry Torokhov wrote:
>> On Tue, Mar 03, 2009 at 10:59:27AM +0100, Daniel Mack wrote:
>>> This patch adds a generic driver for rotary encoders connected to
GPIO
>>> pins of a system. It relies on gpiolib and generic hardware irqs.
The
>>> documentation that also comes with this patch explains the concept
and
>>> how to use the driver.
>>>
>>> Signed-off-by: Daniel Mack <daniel@caiaq.de>
>>> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
>>> Cc: H Hartley Sweeten <hsweeten@visionengravers.com>
>>> ---
>>> New version with Dmitry's change requests included:
>>>
>>> * calling input_free_device() in case input_register_device()
fails
>>> * calling input_unregister_device() in all other cases
>>> * made the axis information part of the platform data
>>>
>>
>> I fiddled with the driver a little bit more changing formatting,
please
>> take a look and if you are still OK with it I will apply to 'next'.
Couple of comments, none should hold up committing this driver.
1) For an absolute encoder shouldn't the position clamp to the
minimum/maximum values? Currently the driver is setup to report the
events going from 0 to 'steps' then the count rolls over (in both
directions). Maybe this should be a platform optional flag? Also,
maybe the minimum/maximum of the axis should be platform configurable
instead of just having the 'steps' of the encoder?
2) I have a patch to the driver that allows the axis to be a REL_* type.
I will post this patch for review after the driver is committed.
3) Are both 'inverted_*' flags really needed? It appears that the end
effect of these just reverses the logical direction of the encoder.
inverted_a inverted_b result
0 0 normal encoder
0 1 backwards encoder
1 0 backwards encoder
1 1 normal encoder
The same effect should be attainable with one flag to reverse the
direction, or just reverse the wires on the encoder.
3) It might be possible to reconstruct the interrupt handler so that
only one gpio needs to be interrupt capable.
Looking at the phase diagram you could consider one of the channel
inputs as a 'step' interrupt and the other as the 'direction' of the
step. On the positive edge of any 'step' if the 'direction' is low it's
going one way, high it's going the other way.
This would double the effective number of encoders that could be
connected. And it removes the added overhead of the extra interrupt
handler and needing to keep track of the 'armed' and 'dir' states.
Actually with both interrupts sharing the same handler the 'armed' and
'dir' variables seem a little bit racy.
Once the driver is committed I will mess with the interrupt code and see
if this is possible.
Regards,
Hartley
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH] generic driver for rotary encoders on GPIOs
2009-03-04 17:02 ` hartleys
@ 2009-03-04 17:20 ` Daniel Mack
2009-03-07 17:06 ` Daniel Mack
0 siblings, 1 reply; 32+ messages in thread
From: Daniel Mack @ 2009-03-04 17:20 UTC (permalink / raw)
To: hartleys; +Cc: Dmitry Torokhov, linux-input
Hi Hartleys,
On Wed, Mar 04, 2009 at 12:02:47PM -0500, hartleys wrote:
> >> take a look and if you are still OK with it I will apply to 'next'.
>
> Couple of comments, none should hold up committing this driver.
>
> 1) For an absolute encoder shouldn't the position clamp to the
> minimum/maximum values? Currently the driver is setup to report the
> events going from 0 to 'steps' then the count rolls over (in both
> directions). Maybe this should be a platform optional flag? Also,
> maybe the minimum/maximum of the axis should be platform configurable
> instead of just having the 'steps' of the encoder?
Depends on the user level software, I'd say - getting the absolute
position in degrees (which is what is reported, basically) should be
enough as hardware input, anything else is context related, right?
> 2) I have a patch to the driver that allows the axis to be a REL_* type.
> I will post this patch for review after the driver is committed.
Cool, that might be a good alternative for users.
> 3) Are both 'inverted_*' flags really needed? It appears that the end
> effect of these just reverses the logical direction of the encoder.
>
> inverted_a inverted_b result
> 0 0 normal encoder
> 0 1 backwards encoder
> 1 0 backwards encoder
> 1 1 normal encoder
Probably not. We just had the lines inverted due to some fancy other
logic on the board and hence I made it configurable.
> The same effect should be attainable with one flag to reverse the
> direction, or just reverse the wires on the encoder.
True. The other approache just seemed more straight-forward.
> 3) It might be possible to reconstruct the interrupt handler so that
> only one gpio needs to be interrupt capable.
>
> Looking at the phase diagram you could consider one of the channel
> inputs as a 'step' interrupt and the other as the 'direction' of the
> step. On the positive edge of any 'step' if the 'direction' is low it's
> going one way, high it's going the other way.
Hmm - what about the case you're not turning the encoder a full step
ahead but stop in the middle and let it flip back? Will it still detect
that correctly?
> This would double the effective number of encoders that could be
> connected. And it removes the added overhead of the extra interrupt
> handler and needing to keep track of the 'armed' and 'dir' states.
> Actually with both interrupts sharing the same handler the 'armed' and
> 'dir' variables seem a little bit racy.
>
> Once the driver is committed I will mess with the interrupt code and see
> if this is possible.
Ok, I'll happily take a look at that. If you flush the idea behind the
state machine, please remember to remove my colleague's name from the
header.
Best regards,
Daniel
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH] generic driver for rotary encoders on GPIOs
2009-03-04 17:20 ` Daniel Mack
@ 2009-03-07 17:06 ` Daniel Mack
2009-04-13 23:06 ` [PATCH] add REL_* axes support to the rotary encoder driver H Hartley Sweeten
0 siblings, 1 reply; 32+ messages in thread
From: Daniel Mack @ 2009-03-07 17:06 UTC (permalink / raw)
To: hartleys; +Cc: linux-input
On Wed, Mar 04, 2009 at 06:20:54PM +0100, Daniel Mack wrote:
> > 2) I have a patch to the driver that allows the axis to be a REL_* type.
> > I will post this patch for review after the driver is committed.
>
> Cool, that might be a good alternative for users.
>
> > 3) Are both 'inverted_*' flags really needed? It appears that the end
> > effect of these just reverses the logical direction of the encoder.
> >
> > inverted_a inverted_b result
> > 0 0 normal encoder
> > 0 1 backwards encoder
> > 1 0 backwards encoder
> > 1 1 normal encoder
The driver has now been commited to input.git/next, so you could send a
patch with your ideas on top of it if you like and find some time :)
Daniel
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH] add REL_* axes support to the rotary encoder driver
2009-03-07 17:06 ` Daniel Mack
@ 2009-04-13 23:06 ` H Hartley Sweeten
2009-04-14 5:50 ` Daniel Mack
2009-04-16 2:08 ` Dmitry Torokhov
0 siblings, 2 replies; 32+ messages in thread
From: H Hartley Sweeten @ 2009-04-13 23:06 UTC (permalink / raw)
To: Daniel Mack; +Cc: linux-input
Add REL_* axes support to the rotary encoder driver.
The rotary encoder driver only supports returning input events
for ABS_* axes, this adds support for REL_* axes. The relative
axis input event is reported as -1 for each counter-clockwise
step and +1 for each clockwise step.
In addition the private data structure has been rearranged and
modified to use less memory.
Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Daniel Mack <daniel@caiaq.de>
---
diff --git a/Documentation/input/rotary-encoder.txt
b/Documentation/input/rotary-encoder.txt
index 435102a..1b90ac7 100644
--- a/Documentation/input/rotary-encoder.txt
+++ b/Documentation/input/rotary-encoder.txt
@@ -67,7 +67,11 @@ data with it.
struct rotary_encoder_platform_data is declared in
include/linux/rotary-encoder.h and needs to be filled with the number
of
steps the encoder has and can carry information about externally
inverted
-signals (because of used invertig buffer or other reasons).
+signals (because of an inverting buffer or other reasons). The encoder
+can be set up to deliver input information as either an absolute or
relative
+axes. For absolute axes the position of the encoder rolls over between
zero
+and the number of steps. For relative axes the input event returns +/-1
for
+each step.
Because GPIO to IRQ mapping is platform specific, this information must
be given in seperately to the driver. See the example below.
@@ -84,7 +88,7 @@ be given in seperately to the driver. See the example
below.
static struct rotary_encoder_platform_data my_rotary_encoder_info = {
.steps = 24,
- .axis = ABS_X,
+ .axis = ROTARY_ENCODER_ABS | ABS_X,
.gpio_a = GPIO_ROTARY_A,
.gpio_b = GPIO_ROTARY_B,
.inverted_a = 0,
diff --git a/drivers/input/misc/rotary_encoder.c
b/drivers/input/misc/rotary_encoder.c
index 5bb3ab5..fff9c1c 100644
--- a/drivers/input/misc/rotary_encoder.c
+++ b/drivers/input/misc/rotary_encoder.c
@@ -26,13 +26,18 @@
#define DRV_NAME "rotary-encoder"
struct rotary_encoder {
- unsigned int irq_a;
- unsigned int irq_b;
- unsigned int pos;
- unsigned int armed;
- unsigned int dir;
- struct input_dev *input;
- struct rotary_encoder_platform_data *pdata;
+ struct input_dev *input;
+ struct rotary_encoder_platform_data *pdata;
+
+ unsigned int axis;
+ unsigned int pos;
+
+ unsigned int irq_a;
+ unsigned int irq_b;
+
+ unsigned int abs:1;
+ unsigned int dir:1;
+ unsigned int armed:1;
};
static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
@@ -55,16 +60,29 @@ static irqreturn_t rotary_encoder_irq(int irq, void
*dev_id)
if (encoder->dir) {
/* turning counter-clockwise */
- encoder->pos += pdata->steps;
- encoder->pos--;
- encoder->pos %= pdata->steps;
+ if (encoder->abs) {
+ encoder->pos += pdata->steps;
+ encoder->pos--;
+ encoder->pos %= pdata->steps;
+ input_report_abs(encoder->input,
+ encoder->axis,
encoder->pos);
+ } else {
+ input_report_rel(encoder->input,
+ encoder->axis, -1);
+ }
} else {
/* turning clockwise */
- encoder->pos++;
- encoder->pos %= pdata->steps;
+ if (encoder->abs) {
+ encoder->pos++;
+ encoder->pos %= pdata->steps;
+ input_report_abs(encoder->input,
+ encoder->axis,
encoder->pos);
+ } else {
+ input_report_rel(encoder->input,
+ encoder->axis, 1);
+ }
}
- input_report_abs(encoder->input, pdata->axis,
encoder->pos);
input_sync(encoder->input);
encoder->armed = 0;
@@ -108,14 +126,23 @@ static int __devinit rotary_encoder_probe(struct
platform_device *pdev)
encoder->pdata = pdata;
encoder->irq_a = gpio_to_irq(pdata->gpio_a);
encoder->irq_b = gpio_to_irq(pdata->gpio_b);
+ encoder->abs = !!(pdata->axis & ROTARY_ENCODER_ABS);
/* create and register the input driver */
input->name = pdev->name;
input->id.bustype = BUS_HOST;
input->dev.parent = &pdev->dev;
- input->evbit[0] = BIT_MASK(EV_ABS);
- input_set_abs_params(encoder->input,
- pdata->axis, 0, pdata->steps, 0, 1);
+ if (encoder->abs) {
+ encoder->axis = pdata->axis & ABS_MAX;
+ input->evbit[0] = BIT_MASK(EV_ABS);
+ input->absbit[0] = BIT_MASK(encoder->axis);
+ input_set_abs_params(encoder->input,
+ encoder->axis, 0, pdata->steps, 0,
1);
+ } else {
+ encoder->axis = pdata->axis & REL_MAX;
+ input->evbit[0] = BIT_MASK(EV_REL);
+ input->relbit[0] = BIT_MASK(encoder->axis);
+ }
err = input_register_device(input);
if (err) {
diff --git a/include/linux/rotary_encoder.h
b/include/linux/rotary_encoder.h
index 12d63a3..ae668db 100644
--- a/include/linux/rotary_encoder.h
+++ b/include/linux/rotary_encoder.h
@@ -1,6 +1,9 @@
#ifndef __ROTARY_ENCODER_H__
#define __ROTARY_ENCODER_H__
+#define ROTARY_ENCODER_REL (0<<31)
+#define ROTARY_ENCODER_ABS (1<<31)
+
struct rotary_encoder_platform_data {
unsigned int steps;
unsigned int axis;
^ permalink raw reply related [flat|nested] 32+ messages in thread
* Re: [PATCH] add REL_* axes support to the rotary encoder driver
2009-04-13 23:06 ` [PATCH] add REL_* axes support to the rotary encoder driver H Hartley Sweeten
@ 2009-04-14 5:50 ` Daniel Mack
2009-04-14 15:33 ` H Hartley Sweeten
2009-04-16 2:08 ` Dmitry Torokhov
1 sibling, 1 reply; 32+ messages in thread
From: Daniel Mack @ 2009-04-14 5:50 UTC (permalink / raw)
To: H Hartley Sweeten; +Cc: linux-input
Hi,
On Mon, Apr 13, 2009 at 07:06:24PM -0400, H Hartley Sweeten wrote:
> Add REL_* axes support to the rotary encoder driver.
>
> The rotary encoder driver only supports returning input events
> for ABS_* axes, this adds support for REL_* axes. The relative
> axis input event is reported as -1 for each counter-clockwise
> step and +1 for each clockwise step.
>
> In addition the private data structure has been rearranged and
> modified to use less memory.
>
> Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
> Cc: Daniel Mack <daniel@caiaq.de>
Your patch was line wrapped and did only apply with fuzz 1i againt the
current Linus master, but apart from that ...
Acked-by: Daniel Mack <daniel@caiaq.de>
Thanks :)
Daniel
> ---
>
> diff --git a/Documentation/input/rotary-encoder.txt
> b/Documentation/input/rotary-encoder.txt
> index 435102a..1b90ac7 100644
> --- a/Documentation/input/rotary-encoder.txt
> +++ b/Documentation/input/rotary-encoder.txt
> @@ -67,7 +67,11 @@ data with it.
> struct rotary_encoder_platform_data is declared in
> include/linux/rotary-encoder.h and needs to be filled with the number
> of
> steps the encoder has and can carry information about externally
> inverted
> -signals (because of used invertig buffer or other reasons).
> +signals (because of an inverting buffer or other reasons). The encoder
> +can be set up to deliver input information as either an absolute or
> relative
> +axes. For absolute axes the position of the encoder rolls over between
> zero
> +and the number of steps. For relative axes the input event returns +/-1
> for
> +each step.
>
> Because GPIO to IRQ mapping is platform specific, this information must
> be given in seperately to the driver. See the example below.
> @@ -84,7 +88,7 @@ be given in seperately to the driver. See the example
> below.
>
> static struct rotary_encoder_platform_data my_rotary_encoder_info = {
> .steps = 24,
> - .axis = ABS_X,
> + .axis = ROTARY_ENCODER_ABS | ABS_X,
> .gpio_a = GPIO_ROTARY_A,
> .gpio_b = GPIO_ROTARY_B,
> .inverted_a = 0,
> diff --git a/drivers/input/misc/rotary_encoder.c
> b/drivers/input/misc/rotary_encoder.c
> index 5bb3ab5..fff9c1c 100644
> --- a/drivers/input/misc/rotary_encoder.c
> +++ b/drivers/input/misc/rotary_encoder.c
> @@ -26,13 +26,18 @@
> #define DRV_NAME "rotary-encoder"
>
> struct rotary_encoder {
> - unsigned int irq_a;
> - unsigned int irq_b;
> - unsigned int pos;
> - unsigned int armed;
> - unsigned int dir;
> - struct input_dev *input;
> - struct rotary_encoder_platform_data *pdata;
> + struct input_dev *input;
> + struct rotary_encoder_platform_data *pdata;
> +
> + unsigned int axis;
> + unsigned int pos;
> +
> + unsigned int irq_a;
> + unsigned int irq_b;
> +
> + unsigned int abs:1;
> + unsigned int dir:1;
> + unsigned int armed:1;
> };
>
> static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
> @@ -55,16 +60,29 @@ static irqreturn_t rotary_encoder_irq(int irq, void
> *dev_id)
>
> if (encoder->dir) {
> /* turning counter-clockwise */
> - encoder->pos += pdata->steps;
> - encoder->pos--;
> - encoder->pos %= pdata->steps;
> + if (encoder->abs) {
> + encoder->pos += pdata->steps;
> + encoder->pos--;
> + encoder->pos %= pdata->steps;
> + input_report_abs(encoder->input,
> + encoder->axis,
> encoder->pos);
> + } else {
> + input_report_rel(encoder->input,
> + encoder->axis, -1);
> + }
> } else {
> /* turning clockwise */
> - encoder->pos++;
> - encoder->pos %= pdata->steps;
> + if (encoder->abs) {
> + encoder->pos++;
> + encoder->pos %= pdata->steps;
> + input_report_abs(encoder->input,
> + encoder->axis,
> encoder->pos);
> + } else {
> + input_report_rel(encoder->input,
> + encoder->axis, 1);
> + }
> }
>
> - input_report_abs(encoder->input, pdata->axis,
> encoder->pos);
> input_sync(encoder->input);
>
> encoder->armed = 0;
> @@ -108,14 +126,23 @@ static int __devinit rotary_encoder_probe(struct
> platform_device *pdev)
> encoder->pdata = pdata;
> encoder->irq_a = gpio_to_irq(pdata->gpio_a);
> encoder->irq_b = gpio_to_irq(pdata->gpio_b);
> + encoder->abs = !!(pdata->axis & ROTARY_ENCODER_ABS);
>
> /* create and register the input driver */
> input->name = pdev->name;
> input->id.bustype = BUS_HOST;
> input->dev.parent = &pdev->dev;
> - input->evbit[0] = BIT_MASK(EV_ABS);
> - input_set_abs_params(encoder->input,
> - pdata->axis, 0, pdata->steps, 0, 1);
> + if (encoder->abs) {
> + encoder->axis = pdata->axis & ABS_MAX;
> + input->evbit[0] = BIT_MASK(EV_ABS);
> + input->absbit[0] = BIT_MASK(encoder->axis);
> + input_set_abs_params(encoder->input,
> + encoder->axis, 0, pdata->steps, 0,
> 1);
> + } else {
> + encoder->axis = pdata->axis & REL_MAX;
> + input->evbit[0] = BIT_MASK(EV_REL);
> + input->relbit[0] = BIT_MASK(encoder->axis);
> + }
>
> err = input_register_device(input);
> if (err) {
> diff --git a/include/linux/rotary_encoder.h
> b/include/linux/rotary_encoder.h
> index 12d63a3..ae668db 100644
> --- a/include/linux/rotary_encoder.h
> +++ b/include/linux/rotary_encoder.h
> @@ -1,6 +1,9 @@
> #ifndef __ROTARY_ENCODER_H__
> #define __ROTARY_ENCODER_H__
>
> +#define ROTARY_ENCODER_REL (0<<31)
> +#define ROTARY_ENCODER_ABS (1<<31)
> +
> struct rotary_encoder_platform_data {
> unsigned int steps;
> unsigned int axis;
^ permalink raw reply [flat|nested] 32+ messages in thread
* RE: [PATCH] add REL_* axes support to the rotary encoder driver
2009-04-14 5:50 ` Daniel Mack
@ 2009-04-14 15:33 ` H Hartley Sweeten
0 siblings, 0 replies; 32+ messages in thread
From: H Hartley Sweeten @ 2009-04-14 15:33 UTC (permalink / raw)
To: Daniel Mack; +Cc: linux-input
On Monday, April 13, 2009 10:51 PM, Daniel Mack wrote:
> On Mon, Apr 13, 2009 at 07:06:24PM -0400, H Hartley Sweeten wrote:
>> Add REL_* axes support to the rotary encoder driver.
>>
>> The rotary encoder driver only supports returning input events
>> for ABS_* axes, this adds support for REL_* axes. The relative
>> axis input event is reported as -1 for each counter-clockwise
>> step and +1 for each clockwise step.
>>
>> In addition the private data structure has been rearranged and
>> modified to use less memory.
>>
>> Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
>> Cc: Daniel Mack <daniel@caiaq.de>
>
> Your patch was line wrapped and did only apply with fuzz 1i againt
> the current Linus master, but apart from that ...
>
> Acked-by: Daniel Mack <daniel@caiaq.de>
>
> Thanks :)
>
> Daniel
I'll add your Acked-by and repost the patch later today. I'll also
attach the patch to avoid the line wrapping issue.
I've been fighting with my email provider on that issue. It appears
one of the servers they use is doing the wrapping when the message
bounces through it. If anyone has an idea to get around that I would
appreciate the input.
Regards,
Hartley
> ---
>
> diff --git a/Documentation/input/rotary-encoder.txt
> b/Documentation/input/rotary-encoder.txt
> index 435102a..1b90ac7 100644
> --- a/Documentation/input/rotary-encoder.txt
> +++ b/Documentation/input/rotary-encoder.txt
> @@ -67,7 +67,11 @@ data with it.
> struct rotary_encoder_platform_data is declared in
> include/linux/rotary-encoder.h and needs to be filled with the number
> of
> steps the encoder has and can carry information about externally
> inverted
> -signals (because of used invertig buffer or other reasons).
> +signals (because of an inverting buffer or other reasons). The
encoder
> +can be set up to deliver input information as either an absolute or
> relative
> +axes. For absolute axes the position of the encoder rolls over
between
> zero
> +and the number of steps. For relative axes the input event returns
+/-1
> for
> +each step.
>
> Because GPIO to IRQ mapping is platform specific, this information
must
> be given in seperately to the driver. See the example below.
> @@ -84,7 +88,7 @@ be given in seperately to the driver. See the
example
> below.
>
> static struct rotary_encoder_platform_data my_rotary_encoder_info = {
> .steps = 24,
> - .axis = ABS_X,
> + .axis = ROTARY_ENCODER_ABS | ABS_X,
> .gpio_a = GPIO_ROTARY_A,
> .gpio_b = GPIO_ROTARY_B,
> .inverted_a = 0,
> diff --git a/drivers/input/misc/rotary_encoder.c
> b/drivers/input/misc/rotary_encoder.c
> index 5bb3ab5..fff9c1c 100644
> --- a/drivers/input/misc/rotary_encoder.c
> +++ b/drivers/input/misc/rotary_encoder.c
> @@ -26,13 +26,18 @@
> #define DRV_NAME "rotary-encoder"
>
> struct rotary_encoder {
> - unsigned int irq_a;
> - unsigned int irq_b;
> - unsigned int pos;
> - unsigned int armed;
> - unsigned int dir;
> - struct input_dev *input;
> - struct rotary_encoder_platform_data *pdata;
> + struct input_dev *input;
> + struct rotary_encoder_platform_data *pdata;
> +
> + unsigned int axis;
> + unsigned int pos;
> +
> + unsigned int irq_a;
> + unsigned int irq_b;
> +
> + unsigned int abs:1;
> + unsigned int dir:1;
> + unsigned int armed:1;
> };
>
> static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
> @@ -55,16 +60,29 @@ static irqreturn_t rotary_encoder_irq(int irq,
void
> *dev_id)
>
> if (encoder->dir) {
> /* turning counter-clockwise */
> - encoder->pos += pdata->steps;
> - encoder->pos--;
> - encoder->pos %= pdata->steps;
> + if (encoder->abs) {
> + encoder->pos += pdata->steps;
> + encoder->pos--;
> + encoder->pos %= pdata->steps;
> + input_report_abs(encoder->input,
> + encoder->axis,
> encoder->pos);
> + } else {
> + input_report_rel(encoder->input,
> + encoder->axis, -1);
> + }
> } else {
> /* turning clockwise */
> - encoder->pos++;
> - encoder->pos %= pdata->steps;
> + if (encoder->abs) {
> + encoder->pos++;
> + encoder->pos %= pdata->steps;
> + input_report_abs(encoder->input,
> + encoder->axis,
> encoder->pos);
> + } else {
> + input_report_rel(encoder->input,
> + encoder->axis, 1);
> + }
> }
>
> - input_report_abs(encoder->input, pdata->axis,
> encoder->pos);
> input_sync(encoder->input);
>
> encoder->armed = 0;
> @@ -108,14 +126,23 @@ static int __devinit rotary_encoder_probe(struct
> platform_device *pdev)
> encoder->pdata = pdata;
> encoder->irq_a = gpio_to_irq(pdata->gpio_a);
> encoder->irq_b = gpio_to_irq(pdata->gpio_b);
> + encoder->abs = !!(pdata->axis & ROTARY_ENCODER_ABS);
>
> /* create and register the input driver */
> input->name = pdev->name;
> input->id.bustype = BUS_HOST;
> input->dev.parent = &pdev->dev;
> - input->evbit[0] = BIT_MASK(EV_ABS);
> - input_set_abs_params(encoder->input,
> - pdata->axis, 0, pdata->steps, 0, 1);
> + if (encoder->abs) {
> + encoder->axis = pdata->axis & ABS_MAX;
> + input->evbit[0] = BIT_MASK(EV_ABS);
> + input->absbit[0] = BIT_MASK(encoder->axis);
> + input_set_abs_params(encoder->input,
> + encoder->axis, 0, pdata->steps, 0,
> 1);
> + } else {
> + encoder->axis = pdata->axis & REL_MAX;
> + input->evbit[0] = BIT_MASK(EV_REL);
> + input->relbit[0] = BIT_MASK(encoder->axis);
> + }
>
> err = input_register_device(input);
> if (err) {
> diff --git a/include/linux/rotary_encoder.h
> b/include/linux/rotary_encoder.h
> index 12d63a3..ae668db 100644
> --- a/include/linux/rotary_encoder.h
> +++ b/include/linux/rotary_encoder.h
> @@ -1,6 +1,9 @@
> #ifndef __ROTARY_ENCODER_H__
> #define __ROTARY_ENCODER_H__
>
> +#define ROTARY_ENCODER_REL (0<<31)
> +#define ROTARY_ENCODER_ABS (1<<31)
> +
> struct rotary_encoder_platform_data {
> unsigned int steps;
> unsigned int axis;
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH] add REL_* axes support to the rotary encoder driver
2009-04-13 23:06 ` [PATCH] add REL_* axes support to the rotary encoder driver H Hartley Sweeten
2009-04-14 5:50 ` Daniel Mack
@ 2009-04-16 2:08 ` Dmitry Torokhov
2009-04-16 2:24 ` H Hartley Sweeten
1 sibling, 1 reply; 32+ messages in thread
From: Dmitry Torokhov @ 2009-04-16 2:08 UTC (permalink / raw)
To: H Hartley Sweeten; +Cc: Daniel Mack, linux-input
Hi,
On Monday 13 April 2009 16:06:24 H Hartley Sweeten wrote:
> +
> + unsigned int axis;
> + unsigned int pos;
> +
> + unsigned int irq_a;
> + unsigned int irq_b;
> +
> + unsigned int abs:1;
> + unsigned int dir:1;
> + unsigned int armed:1;
Doing it this way makes us go from simple store to read/modify/write cycle in
the interrupt handler.
>
> +#define ROTARY_ENCODER_REL (0<<31)
> +#define ROTARY_ENCODER_ABS (1<<31)
Meh... How about below instead?
--
Dmitry
Input: rotary_encoder - add support for REL_* axes
From: H Hartley Sweeten <hartleys@visionengravers.com>
The rotary encoder driver only supports returning input events
for ABS_* axes, this adds support for REL_* axes. The relative
axis input event is reported as -1 for each counter-clockwise
step and +1 for each clockwise step.
In addition the private data structure has been rearranged and
modified to use less memory.
Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Daniel Mack <daniel@caiaq.de>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---
Documentation/input/rotary-encoder.txt | 7 ++++
drivers/input/misc/rotary_encoder.c | 52 ++++++++++++++++++++------------
include/linux/rotary_encoder.h | 3 ++
3 files changed, 42 insertions(+), 20 deletions(-)
diff --git a/Documentation/input/rotary-encoder.txt b/Documentation/input/rotary-encoder.txt
index 435102a..9b6b6e3 100644
--- a/Documentation/input/rotary-encoder.txt
+++ b/Documentation/input/rotary-encoder.txt
@@ -67,7 +67,11 @@ data with it.
struct rotary_encoder_platform_data is declared in
include/linux/rotary-encoder.h and needs to be filled with the number of
steps the encoder has and can carry information about externally inverted
-signals (because of used invertig buffer or other reasons).
+signals (because of an inverting buffer or other reasons). The encoder
+can be set up to deliver input information as either an absolute or relative
+axes. For absolute axes the position of the encoder rolls over between zero
+and the number of steps. For relative axes the input event returns +/-1 for
+each step.
Because GPIO to IRQ mapping is platform specific, this information must
be given in seperately to the driver. See the example below.
@@ -85,6 +89,7 @@ be given in seperately to the driver. See the example below.
static struct rotary_encoder_platform_data my_rotary_encoder_info = {
.steps = 24,
.axis = ABS_X,
+ .relative_axis = false,
.gpio_a = GPIO_ROTARY_A,
.gpio_b = GPIO_ROTARY_B,
.inverted_a = 0,
diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c
index 5bb3ab5..5d81116 100644
--- a/drivers/input/misc/rotary_encoder.c
+++ b/drivers/input/misc/rotary_encoder.c
@@ -26,13 +26,17 @@
#define DRV_NAME "rotary-encoder"
struct rotary_encoder {
- unsigned int irq_a;
- unsigned int irq_b;
- unsigned int pos;
- unsigned int armed;
- unsigned int dir;
struct input_dev *input;
struct rotary_encoder_platform_data *pdata;
+
+ unsigned int axis;
+ unsigned int pos;
+
+ unsigned int irq_a;
+ unsigned int irq_b;
+
+ bool armed;
+ unsigned char dir; /* 0 - clockwise, 1 - CCW */
};
static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
@@ -53,21 +57,25 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
if (!encoder->armed)
break;
- if (encoder->dir) {
- /* turning counter-clockwise */
- encoder->pos += pdata->steps;
- encoder->pos--;
- encoder->pos %= pdata->steps;
+ if (pdata->relative_axis) {
+ input_report_rel(encoder->input, pdata->axis,
+ encoder->dir ? -1 : 1);
} else {
- /* turning clockwise */
- encoder->pos++;
+ if (encoder->dir) {
+ /* turning counter-clockwise */
+ encoder->pos += pdata->steps;
+ encoder->pos--;
+ } else {
+ /* turning clockwise */
+ encoder->pos++;
+ }
encoder->pos %= pdata->steps;
+ input_report_abs(encoder->input, pdata->axis,
+ encoder->pos);
}
-
- input_report_abs(encoder->input, pdata->axis, encoder->pos);
input_sync(encoder->input);
- encoder->armed = 0;
+ encoder->armed = false;
break;
case 0x1:
@@ -77,7 +85,7 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
break;
case 0x3:
- encoder->armed = 1;
+ encoder->armed = true;
break;
}
@@ -113,9 +121,15 @@ static int __devinit rotary_encoder_probe(struct platform_device *pdev)
input->name = pdev->name;
input->id.bustype = BUS_HOST;
input->dev.parent = &pdev->dev;
- input->evbit[0] = BIT_MASK(EV_ABS);
- input_set_abs_params(encoder->input,
- pdata->axis, 0, pdata->steps, 0, 1);
+
+ if (pdata->relative_axis) {
+ input->evbit[0] = BIT_MASK(EV_ABS);
+ input_set_abs_params(encoder->input,
+ pdata->axis, 0, pdata->steps, 0, 1);
+ } else {
+ input->evbit[0] = BIT_MASK(EV_REL);
+ input->relbit[0] = BIT_MASK(pdata->axis);
+ }
err = input_register_device(input);
if (err) {
diff --git a/include/linux/rotary_encoder.h b/include/linux/rotary_encoder.h
index 12d63a3..a178bdc 100644
--- a/include/linux/rotary_encoder.h
+++ b/include/linux/rotary_encoder.h
@@ -1,6 +1,8 @@
#ifndef __ROTARY_ENCODER_H__
#define __ROTARY_ENCODER_H__
+#include <linux/types.h>
+
struct rotary_encoder_platform_data {
unsigned int steps;
unsigned int axis;
@@ -8,6 +10,7 @@ struct rotary_encoder_platform_data {
unsigned int gpio_b;
unsigned int inverted_a;
unsigned int inverted_b;
+ bool relative_axis;
};
#endif /* __ROTARY_ENCODER_H__ */
^ permalink raw reply related [flat|nested] 32+ messages in thread
* RE: [PATCH] add REL_* axes support to the rotary encoder driver
2009-04-16 2:08 ` Dmitry Torokhov
@ 2009-04-16 2:24 ` H Hartley Sweeten
2009-04-16 2:33 ` Dmitry Torokhov
0 siblings, 1 reply; 32+ messages in thread
From: H Hartley Sweeten @ 2009-04-16 2:24 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: Daniel Mack, linux-input
On Wednesday, April 15, 2009 7:09 PM, Dmitry Torokhov wrote:
>
> Hi,
>
> On Monday 13 April 2009 16:06:24 H Hartley Sweeten wrote:
>> +
>> + unsigned int axis;
>> + unsigned int pos;
>> +
>> + unsigned int irq_a;
>> + unsigned int irq_b;
>> +
>> + unsigned int abs:1;
>> + unsigned int dir:1;
>> + unsigned int armed:1;
>
> Doing it this way makes us go from simple store to read/modify/write
> cycle in the interrupt handler.
Didn't think of that. Good catch.
>>
>> +#define ROTARY_ENCODER_REL (0<<31)
>> +#define ROTARY_ENCODER_ABS (1<<31)
>
> Meh... How about below instead?
Your changes look good to me.
The only issue I can see is that absolute axis encoders wrap.
If you have an encoder of something like ABS_VOLUME you would probably
want the "pos" to go from 0 to "steps" and then clamp. The encoder
could actually rotate multiple times depending on it's actual line
count, but the effect would be correct.
Regards,
Hartley
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH] add REL_* axes support to the rotary encoder driver
2009-04-16 2:24 ` H Hartley Sweeten
@ 2009-04-16 2:33 ` Dmitry Torokhov
2009-04-16 3:11 ` H Hartley Sweeten
0 siblings, 1 reply; 32+ messages in thread
From: Dmitry Torokhov @ 2009-04-16 2:33 UTC (permalink / raw)
To: H Hartley Sweeten; +Cc: Daniel Mack, linux-input
On Wednesday 15 April 2009 19:24:58 H Hartley Sweeten wrote:
> On Wednesday, April 15, 2009 7:09 PM, Dmitry Torokhov wrote:
> > Hi,
> >
> > On Monday 13 April 2009 16:06:24 H Hartley Sweeten wrote:
> >> +
> >> + unsigned int axis;
> >> + unsigned int pos;
> >> +
> >> + unsigned int irq_a;
> >> + unsigned int irq_b;
> >> +
> >> + unsigned int abs:1;
> >> + unsigned int dir:1;
> >> + unsigned int armed:1;
> >
> > Doing it this way makes us go from simple store to read/modify/write
> > cycle in the interrupt handler.
>
> Didn't think of that. Good catch.
>
> >> +#define ROTARY_ENCODER_REL (0<<31)
> >> +#define ROTARY_ENCODER_ABS (1<<31)
> >
> > Meh... How about below instead?
>
> Your changes look good to me.
>
> The only issue I can see is that absolute axis encoders wrap.
>
> If you have an encoder of something like ABS_VOLUME you would probably
> want the "pos" to go from 0 to "steps" and then clamp. The encoder
> could actually rotate multiple times depending on it's actual line
> count, but the effect would be correct.
I see. Care to prepare a patch? Thanks!
--
Dmitry
^ permalink raw reply [flat|nested] 32+ messages in thread
* RE: [PATCH] add REL_* axes support to the rotary encoder driver
2009-04-16 2:33 ` Dmitry Torokhov
@ 2009-04-16 3:11 ` H Hartley Sweeten
2009-04-16 6:35 ` Daniel Mack
` (2 more replies)
0 siblings, 3 replies; 32+ messages in thread
From: H Hartley Sweeten @ 2009-04-16 3:11 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: Daniel Mack, linux-input
On Wednesday, April 15, 2009 7:33 PM, Dmitry Torokhov wrote:
>>> Meh... How about below instead?
>>
>> Your changes look good to me.
>>
>> The only issue I can see is that absolute axis encoders wrap.
>>
>> If you have an encoder of something like ABS_VOLUME you would
>> probably want the "pos" to go from 0 to "steps" and then clamp.
>> The encoder could actually rotate multiple times depending on
>> it's actual line count, but the effect would be correct.
>
> I see. Care to prepare a patch? Thanks!
Dmitry,
How's this? I left the ability for the original ABS_* axis
operation since that might be what Daniel Mack needed.
Regards,
Hartley
Input: rotary_encoder - add support for REL_* axes
From: H Hartley Sweeten <hartleys@visionengravers.com>
The rotary encoder driver only supports returning input events
for ABS_* axes, this adds support for REL_* axes. The relative
axis input event is reported as -1 for each counter-clockwise
step and +1 for each clockwise step.
The ability to clamp the position of ABS_* axes between 0 and
a maximum of "steps" has also been added.
Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Daniel Mack <daniel@caiaq.de>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---
diff --git a/Documentation/input/rotary-encoder.txt
b/Documentation/input/rotary-encoder.txt
index 435102a..3a6aec4 100644
--- a/Documentation/input/rotary-encoder.txt
+++ b/Documentation/input/rotary-encoder.txt
@@ -67,7 +67,12 @@ data with it.
struct rotary_encoder_platform_data is declared in
include/linux/rotary-encoder.h and needs to be filled with the number
of
steps the encoder has and can carry information about externally
inverted
-signals (because of used invertig buffer or other reasons).
+signals (because of an inverting buffer or other reasons). The encoder
+can be set up to deliver input information as either an absolute or
relative
+axes. For relative axes the input event returns +/-1 for each step. For
+absolute axes the position of the encoder can either roll over between
zero
+and the number of steps or will clamp at the maximum and zero depending
on
+the configuration.
Because GPIO to IRQ mapping is platform specific, this information must
be given in seperately to the driver. See the example below.
@@ -85,6 +90,8 @@ be given in seperately to the driver. See the example
below.
static struct rotary_encoder_platform_data my_rotary_encoder_info = {
.steps = 24,
.axis = ABS_X,
+ .relative_axis = false,
+ .rollover = false,
.gpio_a = GPIO_ROTARY_A,
.gpio_b = GPIO_ROTARY_B,
.inverted_a = 0,
diff --git a/drivers/input/misc/rotary_encoder.c
b/drivers/input/misc/rotary_encoder.c
index 5bb3ab5..4a6a72a 100644
--- a/drivers/input/misc/rotary_encoder.c
+++ b/drivers/input/misc/rotary_encoder.c
@@ -26,13 +26,17 @@
#define DRV_NAME "rotary-encoder"
struct rotary_encoder {
- unsigned int irq_a;
- unsigned int irq_b;
- unsigned int pos;
- unsigned int armed;
- unsigned int dir;
struct input_dev *input;
struct rotary_encoder_platform_data *pdata;
+
+ unsigned int axis;
+ unsigned int pos;
+
+ unsigned int irq_a;
+ unsigned int irq_b;
+
+ bool armed;
+ unsigned char dir; /* 0 - clockwise, 1 - CCW */
};
static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
@@ -53,21 +57,32 @@ static irqreturn_t rotary_encoder_irq(int irq, void
*dev_id)
if (!encoder->armed)
break;
- if (encoder->dir) {
- /* turning counter-clockwise */
- encoder->pos += pdata->steps;
- encoder->pos--;
- encoder->pos %= pdata->steps;
+ if (pdata->relative_axis) {
+ input_report_rel(encoder->input, pdata->axis,
+ encoder->dir ? -1 : 1);
} else {
- /* turning clockwise */
- encoder->pos++;
- encoder->pos %= pdata->steps;
+ unsigned int pos = encoder->pos;
+
+ if (encoder->dir) {
+ /* turning counter-clockwise */
+ if (encoder->rollover)
+ pos += pdata->steps;
+ if (pos)
+ pos--;
+ } else {
+ /* turning clockwise */
+ if (encoder->rollover || pos <
pdata->steps)
+ pos++;
+ }
+ if (encoder->rollover)
+ pos %= pdata->steps;
+ encoder->pos = pos;
+ input_report_abs(encoder->input, pdata->axis,
+ encoder->pos);
}
-
- input_report_abs(encoder->input, pdata->axis,
encoder->pos);
input_sync(encoder->input);
- encoder->armed = 0;
+ encoder->armed = false;
break;
case 0x1:
@@ -77,7 +92,7 @@ static irqreturn_t rotary_encoder_irq(int irq, void
*dev_id)
break;
case 0x3:
- encoder->armed = 1;
+ encoder->armed = true;
break;
}
@@ -113,9 +128,15 @@ static int __devinit rotary_encoder_probe(struct
platform_device *pdev)
input->name = pdev->name;
input->id.bustype = BUS_HOST;
input->dev.parent = &pdev->dev;
- input->evbit[0] = BIT_MASK(EV_ABS);
- input_set_abs_params(encoder->input,
- pdata->axis, 0, pdata->steps, 0, 1);
+
+ if (pdata->relative_axis) {
+ input->evbit[0] = BIT_MASK(EV_ABS);
+ input_set_abs_params(encoder->input,
+ pdata->axis, 0, pdata->steps, 0,
1);
+ } else {
+ input->evbit[0] = BIT_MASK(EV_REL);
+ input->relbit[0] = BIT_MASK(pdata->axis);
+ }
err = input_register_device(input);
if (err) {
diff --git a/include/linux/rotary_encoder.h
b/include/linux/rotary_encoder.h
index 12d63a3..215278b 100644
--- a/include/linux/rotary_encoder.h
+++ b/include/linux/rotary_encoder.h
@@ -8,6 +8,8 @@ struct rotary_encoder_platform_data {
unsigned int gpio_b;
unsigned int inverted_a;
unsigned int inverted_b;
+ bool relative_axis;
+ bool rollover;
};
#endif /* __ROTARY_ENCODER_H__ */
^ permalink raw reply related [flat|nested] 32+ messages in thread
* Re: [PATCH] add REL_* axes support to the rotary encoder driver
2009-04-16 3:11 ` H Hartley Sweeten
@ 2009-04-16 6:35 ` Daniel Mack
2009-04-16 8:05 ` Daniel Mack
2009-04-16 8:39 ` Daniel Mack
2 siblings, 0 replies; 32+ messages in thread
From: Daniel Mack @ 2009-04-16 6:35 UTC (permalink / raw)
To: H Hartley Sweeten; +Cc: Dmitry Torokhov, linux-input
On Wed, Apr 15, 2009 at 11:11:41PM -0400, H Hartley Sweeten wrote:
> >> Your changes look good to me.
> >>
> >> The only issue I can see is that absolute axis encoders wrap.
> >>
> >> If you have an encoder of something like ABS_VOLUME you would
> >> probably want the "pos" to go from 0 to "steps" and then clamp.
> >> The encoder could actually rotate multiple times depending on
> >> it's actual line count, but the effect would be correct.
> >
> > I see. Care to prepare a patch? Thanks!
>
> Dmitry,
>
> How's this? I left the ability for the original ABS_* axis
> operation since that might be what Daniel Mack needed.
Yep, thanks. Even though now as axis can be relative, I realize this
actually makes a lot more sense in my application, too :) But we should
still leave it in for others. The more versatile the driver is, the
better.
Acked-by: Daniel Mack <daniel@caiaq.de>
> Input: rotary_encoder - add support for REL_* axes
>
> From: H Hartley Sweeten <hartleys@visionengravers.com>
>
> The rotary encoder driver only supports returning input events
> for ABS_* axes, this adds support for REL_* axes. The relative
> axis input event is reported as -1 for each counter-clockwise
> step and +1 for each clockwise step.
>
> The ability to clamp the position of ABS_* axes between 0 and
> a maximum of "steps" has also been added.
>
> Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
> Cc: Daniel Mack <daniel@caiaq.de>
> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
>
> ---
>
> diff --git a/Documentation/input/rotary-encoder.txt
> b/Documentation/input/rotary-encoder.txt
> index 435102a..3a6aec4 100644
> --- a/Documentation/input/rotary-encoder.txt
> +++ b/Documentation/input/rotary-encoder.txt
> @@ -67,7 +67,12 @@ data with it.
> struct rotary_encoder_platform_data is declared in
> include/linux/rotary-encoder.h and needs to be filled with the number
> of
> steps the encoder has and can carry information about externally
> inverted
> -signals (because of used invertig buffer or other reasons).
> +signals (because of an inverting buffer or other reasons). The encoder
> +can be set up to deliver input information as either an absolute or
> relative
> +axes. For relative axes the input event returns +/-1 for each step. For
> +absolute axes the position of the encoder can either roll over between
> zero
> +and the number of steps or will clamp at the maximum and zero depending
> on
> +the configuration.
>
> Because GPIO to IRQ mapping is platform specific, this information must
> be given in seperately to the driver. See the example below.
> @@ -85,6 +90,8 @@ be given in seperately to the driver. See the example
> below.
> static struct rotary_encoder_platform_data my_rotary_encoder_info = {
> .steps = 24,
> .axis = ABS_X,
> + .relative_axis = false,
> + .rollover = false,
> .gpio_a = GPIO_ROTARY_A,
> .gpio_b = GPIO_ROTARY_B,
> .inverted_a = 0,
> diff --git a/drivers/input/misc/rotary_encoder.c
> b/drivers/input/misc/rotary_encoder.c
> index 5bb3ab5..4a6a72a 100644
> --- a/drivers/input/misc/rotary_encoder.c
> +++ b/drivers/input/misc/rotary_encoder.c
> @@ -26,13 +26,17 @@
> #define DRV_NAME "rotary-encoder"
>
> struct rotary_encoder {
> - unsigned int irq_a;
> - unsigned int irq_b;
> - unsigned int pos;
> - unsigned int armed;
> - unsigned int dir;
> struct input_dev *input;
> struct rotary_encoder_platform_data *pdata;
> +
> + unsigned int axis;
> + unsigned int pos;
> +
> + unsigned int irq_a;
> + unsigned int irq_b;
> +
> + bool armed;
> + unsigned char dir; /* 0 - clockwise, 1 - CCW */
> };
>
> static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
> @@ -53,21 +57,32 @@ static irqreturn_t rotary_encoder_irq(int irq, void
> *dev_id)
> if (!encoder->armed)
> break;
>
> - if (encoder->dir) {
> - /* turning counter-clockwise */
> - encoder->pos += pdata->steps;
> - encoder->pos--;
> - encoder->pos %= pdata->steps;
> + if (pdata->relative_axis) {
> + input_report_rel(encoder->input, pdata->axis,
> + encoder->dir ? -1 : 1);
> } else {
> - /* turning clockwise */
> - encoder->pos++;
> - encoder->pos %= pdata->steps;
> + unsigned int pos = encoder->pos;
> +
> + if (encoder->dir) {
> + /* turning counter-clockwise */
> + if (encoder->rollover)
> + pos += pdata->steps;
> + if (pos)
> + pos--;
> + } else {
> + /* turning clockwise */
> + if (encoder->rollover || pos <
> pdata->steps)
> + pos++;
> + }
> + if (encoder->rollover)
> + pos %= pdata->steps;
> + encoder->pos = pos;
> + input_report_abs(encoder->input, pdata->axis,
> + encoder->pos);
> }
> -
> - input_report_abs(encoder->input, pdata->axis,
> encoder->pos);
> input_sync(encoder->input);
>
> - encoder->armed = 0;
> + encoder->armed = false;
> break;
>
> case 0x1:
> @@ -77,7 +92,7 @@ static irqreturn_t rotary_encoder_irq(int irq, void
> *dev_id)
> break;
>
> case 0x3:
> - encoder->armed = 1;
> + encoder->armed = true;
> break;
> }
>
> @@ -113,9 +128,15 @@ static int __devinit rotary_encoder_probe(struct
> platform_device *pdev)
> input->name = pdev->name;
> input->id.bustype = BUS_HOST;
> input->dev.parent = &pdev->dev;
> - input->evbit[0] = BIT_MASK(EV_ABS);
> - input_set_abs_params(encoder->input,
> - pdata->axis, 0, pdata->steps, 0, 1);
> +
> + if (pdata->relative_axis) {
> + input->evbit[0] = BIT_MASK(EV_ABS);
> + input_set_abs_params(encoder->input,
> + pdata->axis, 0, pdata->steps, 0,
> 1);
> + } else {
> + input->evbit[0] = BIT_MASK(EV_REL);
> + input->relbit[0] = BIT_MASK(pdata->axis);
> + }
>
> err = input_register_device(input);
> if (err) {
> diff --git a/include/linux/rotary_encoder.h
> b/include/linux/rotary_encoder.h
> index 12d63a3..215278b 100644
> --- a/include/linux/rotary_encoder.h
> +++ b/include/linux/rotary_encoder.h
> @@ -8,6 +8,8 @@ struct rotary_encoder_platform_data {
> unsigned int gpio_b;
> unsigned int inverted_a;
> unsigned int inverted_b;
> + bool relative_axis;
> + bool rollover;
> };
>
> #endif /* __ROTARY_ENCODER_H__ */
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH] add REL_* axes support to the rotary encoder driver
2009-04-16 3:11 ` H Hartley Sweeten
2009-04-16 6:35 ` Daniel Mack
@ 2009-04-16 8:05 ` Daniel Mack
2009-04-16 16:48 ` H Hartley Sweeten
2009-04-16 8:39 ` Daniel Mack
2 siblings, 1 reply; 32+ messages in thread
From: Daniel Mack @ 2009-04-16 8:05 UTC (permalink / raw)
To: H Hartley Sweeten; +Cc: Dmitry Torokhov, linux-input
On Wed, Apr 15, 2009 at 11:11:41PM -0400, H Hartley Sweeten wrote:
> How's this? I left the ability for the original ABS_* axis
> operation since that might be what Daniel Mack needed.
Erm, applying this patch gives me
CC drivers/input/misc/rotary_encoder.o
drivers/input/misc/rotary_encoder.c: In function 'rotary_encoder_irq':
drivers/input/misc/rotary_encoder.c:68: error: 'struct rotary_encoder'
has no member named 'rollover'
drivers/input/misc/rotary_encoder.c:74: error: 'struct rotary_encoder'
has no member named 'rollover'
drivers/input/misc/rotary_encoder.c:77: error: 'struct rotary_encoder'
has no member named 'rollover'
make[3]: *** [drivers/input/misc/rotary_encoder.o] Error 1
make[2]: *** [drivers/input/misc] Error 2
make[1]: *** [drivers/input] Error 2
make: *** [drivers] Error 2
You could at least have compiled it ;) Below is a clean version of that
patch.
Daniel
>From f1d72c5511ea65263c82d16521b2436f91af743c Mon Sep 17 00:00:00 2001
From: Daniel Mack <daniel@caiaq.de>
Date: Thu, 16 Apr 2009 09:56:37 +0200
Subject: [PATCH] Input: rotary_encoder - add support for REL_* axes
From: H Hartley Sweeten <hartleys@visionengravers.com>
The rotary encoder driver only supports returning input events
for ABS_* axes, this adds support for REL_* axes. The relative
axis input event is reported as -1 for each counter-clockwise
step and +1 for each clockwise step.
The ability to clamp the position of ABS_* axes between 0 and
a maximum of "steps" has also been added.
Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Signed-off-by: Daniel Mack <daniel@caiaq.de>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---
Documentation/input/rotary-encoder.txt | 9 ++++-
drivers/input/misc/rotary_encoder.c | 61 +++++++++++++++++++++----------
include/linux/rotary_encoder.h | 2 +
3 files changed, 51 insertions(+), 21 deletions(-)
diff --git a/Documentation/input/rotary-encoder.txt b/Documentation/input/rotary-encoder.txt
index 435102a..3a6aec4 100644
--- a/Documentation/input/rotary-encoder.txt
+++ b/Documentation/input/rotary-encoder.txt
@@ -67,7 +67,12 @@ data with it.
struct rotary_encoder_platform_data is declared in
include/linux/rotary-encoder.h and needs to be filled with the number of
steps the encoder has and can carry information about externally inverted
-signals (because of used invertig buffer or other reasons).
+signals (because of an inverting buffer or other reasons). The encoder
+can be set up to deliver input information as either an absolute or relative
+axes. For relative axes the input event returns +/-1 for each step. For
+absolute axes the position of the encoder can either roll over between zero
+and the number of steps or will clamp at the maximum and zero depending on
+the configuration.
Because GPIO to IRQ mapping is platform specific, this information must
be given in seperately to the driver. See the example below.
@@ -85,6 +90,8 @@ be given in seperately to the driver. See the example below.
static struct rotary_encoder_platform_data my_rotary_encoder_info = {
.steps = 24,
.axis = ABS_X,
+ .relative_axis = false,
+ .rollover = false,
.gpio_a = GPIO_ROTARY_A,
.gpio_b = GPIO_ROTARY_B,
.inverted_a = 0,
diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c
index 5bb3ab5..1aebf9a 100644
--- a/drivers/input/misc/rotary_encoder.c
+++ b/drivers/input/misc/rotary_encoder.c
@@ -26,13 +26,17 @@
#define DRV_NAME "rotary-encoder"
struct rotary_encoder {
- unsigned int irq_a;
- unsigned int irq_b;
- unsigned int pos;
- unsigned int armed;
- unsigned int dir;
struct input_dev *input;
struct rotary_encoder_platform_data *pdata;
+
+ unsigned int axis;
+ unsigned int pos;
+
+ unsigned int irq_a;
+ unsigned int irq_b;
+
+ bool armed;
+ unsigned char dir; /* 0 - clockwise, 1 - CCW */
};
static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
@@ -53,21 +57,32 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
if (!encoder->armed)
break;
- if (encoder->dir) {
- /* turning counter-clockwise */
- encoder->pos += pdata->steps;
- encoder->pos--;
- encoder->pos %= pdata->steps;
+ if (pdata->relative_axis) {
+ input_report_rel(encoder->input, pdata->axis,
+ encoder->dir ? -1 : 1);
} else {
- /* turning clockwise */
- encoder->pos++;
- encoder->pos %= pdata->steps;
+ unsigned int pos = encoder->pos;
+
+ if (encoder->dir) {
+ /* turning counter-clockwise */
+ if (pdata->rollover)
+ pos += pdata->steps;
+ if (pos)
+ pos--;
+ } else {
+ /* turning clockwise */
+ if (pdata->rollover || pos < pdata->steps)
+ pos++;
+ }
+ if (pdata->rollover)
+ pos %= pdata->steps;
+ encoder->pos = pos;
+ input_report_abs(encoder->input, pdata->axis,
+ encoder->pos);
}
-
- input_report_abs(encoder->input, pdata->axis, encoder->pos);
input_sync(encoder->input);
- encoder->armed = 0;
+ encoder->armed = false;
break;
case 0x1:
@@ -77,7 +92,7 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
break;
case 0x3:
- encoder->armed = 1;
+ encoder->armed = true;
break;
}
@@ -113,9 +128,15 @@ static int __devinit rotary_encoder_probe(struct platform_device *pdev)
input->name = pdev->name;
input->id.bustype = BUS_HOST;
input->dev.parent = &pdev->dev;
- input->evbit[0] = BIT_MASK(EV_ABS);
- input_set_abs_params(encoder->input,
- pdata->axis, 0, pdata->steps, 0, 1);
+
+ if (pdata->relative_axis) {
+ input->evbit[0] = BIT_MASK(EV_ABS);
+ input_set_abs_params(encoder->input,
+ pdata->axis, 0, pdata->steps, 0, 1);
+ } else {
+ input->evbit[0] = BIT_MASK(EV_REL);
+ input->relbit[0] = BIT_MASK(pdata->axis);
+ }
err = input_register_device(input);
if (err) {
diff --git a/include/linux/rotary_encoder.h b/include/linux/rotary_encoder.h
index 12d63a3..215278b 100644
--- a/include/linux/rotary_encoder.h
+++ b/include/linux/rotary_encoder.h
@@ -8,6 +8,8 @@ struct rotary_encoder_platform_data {
unsigned int gpio_b;
unsigned int inverted_a;
unsigned int inverted_b;
+ bool relative_axis;
+ bool rollover;
};
#endif /* __ROTARY_ENCODER_H__ */
--
1.6.2.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* RE: [PATCH] add REL_* axes support to the rotary encoder driver
2009-04-16 8:05 ` Daniel Mack
@ 2009-04-16 16:48 ` H Hartley Sweeten
0 siblings, 0 replies; 32+ messages in thread
From: H Hartley Sweeten @ 2009-04-16 16:48 UTC (permalink / raw)
To: Daniel Mack; +Cc: Dmitry Torokhov, linux-input
On Thursday, April 16, 2009 1:05 AM, Daniel Mack wrote:
> On Wed, Apr 15, 2009 at 11:11:41PM -0400, H Hartley Sweeten wrote:
>> How's this? I left the ability for the original ABS_* axis
>> operation since that might be what Daniel Mack needed.
>
> Erm, applying this patch gives me
>
> CC drivers/input/misc/rotary_encoder.o
> drivers/input/misc/rotary_encoder.c: In function
'rotary_encoder_irq':
> drivers/input/misc/rotary_encoder.c:68: error: 'struct
rotary_encoder'
> has no member named 'rollover'
> drivers/input/misc/rotary_encoder.c:74: error: 'struct
rotary_encoder'
> has no member named 'rollover'
> drivers/input/misc/rotary_encoder.c:77: error: 'struct
rotary_encoder'
> has no member named 'rollover'
> make[3]: *** [drivers/input/misc/rotary_encoder.o] Error 1
> make[2]: *** [drivers/input/misc] Error 2
> make[1]: *** [drivers/input] Error 2
> make: *** [drivers] Error 2
>
> You could at least have compiled it ;) Below is a clean version of
that
> patch.
Sorry about that. I copied the wrong file from my working tree to my git
tree when I created the patch.
I'll re-verify the patch and submit it again shortly.
Thanks,
Hartley
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH] add REL_* axes support to the rotary encoder driver
2009-04-16 3:11 ` H Hartley Sweeten
2009-04-16 6:35 ` Daniel Mack
2009-04-16 8:05 ` Daniel Mack
@ 2009-04-16 8:39 ` Daniel Mack
2009-04-16 17:09 ` H Hartley Sweeten
2 siblings, 1 reply; 32+ messages in thread
From: Daniel Mack @ 2009-04-16 8:39 UTC (permalink / raw)
To: H Hartley Sweeten; +Cc: Dmitry Torokhov, linux-input
On Wed, Apr 15, 2009 at 11:11:41PM -0400, H Hartley Sweeten wrote:
> + if (pdata->relative_axis) {
> + input->evbit[0] = BIT_MASK(EV_ABS);
> + input_set_abs_params(encoder->input,
> + pdata->axis, 0, pdata->steps, 0,
> 1);
> + } else {
> + input->evbit[0] = BIT_MASK(EV_REL);
> + input->relbit[0] = BIT_MASK(pdata->axis);
> + }
Ah, and this is obviously wrong, too. One more round.
Daniel
>From c127d44df63e558bbb6af465b960f96b3293b3aa Mon Sep 17 00:00:00 2001
From: Daniel Mack <daniel@caiaq.de>
Date: Thu, 16 Apr 2009 09:56:37 +0200
Subject: [PATCH] Input: rotary_encoder - add support for REL_* axes
From: H Hartley Sweeten <hartleys@visionengravers.com>
The rotary encoder driver only supports returning input events
for ABS_* axes, this adds support for REL_* axes. The relative
axis input event is reported as -1 for each counter-clockwise
step and +1 for each clockwise step.
The ability to clamp the position of ABS_* axes between 0 and
a maximum of "steps" has also been added.
Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Signed-off-by: Daniel Mack <daniel@caiaq.de>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---
Documentation/input/rotary-encoder.txt | 9 ++++-
drivers/input/misc/rotary_encoder.c | 61 +++++++++++++++++++++----------
include/linux/rotary_encoder.h | 2 +
3 files changed, 51 insertions(+), 21 deletions(-)
diff --git a/Documentation/input/rotary-encoder.txt b/Documentation/input/rotary-encoder.txt
index 435102a..3a6aec4 100644
--- a/Documentation/input/rotary-encoder.txt
+++ b/Documentation/input/rotary-encoder.txt
@@ -67,7 +67,12 @@ data with it.
struct rotary_encoder_platform_data is declared in
include/linux/rotary-encoder.h and needs to be filled with the number of
steps the encoder has and can carry information about externally inverted
-signals (because of used invertig buffer or other reasons).
+signals (because of an inverting buffer or other reasons). The encoder
+can be set up to deliver input information as either an absolute or relative
+axes. For relative axes the input event returns +/-1 for each step. For
+absolute axes the position of the encoder can either roll over between zero
+and the number of steps or will clamp at the maximum and zero depending on
+the configuration.
Because GPIO to IRQ mapping is platform specific, this information must
be given in seperately to the driver. See the example below.
@@ -85,6 +90,8 @@ be given in seperately to the driver. See the example below.
static struct rotary_encoder_platform_data my_rotary_encoder_info = {
.steps = 24,
.axis = ABS_X,
+ .relative_axis = false,
+ .rollover = false,
.gpio_a = GPIO_ROTARY_A,
.gpio_b = GPIO_ROTARY_B,
.inverted_a = 0,
diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c
index 5bb3ab5..c806fbf 100644
--- a/drivers/input/misc/rotary_encoder.c
+++ b/drivers/input/misc/rotary_encoder.c
@@ -26,13 +26,17 @@
#define DRV_NAME "rotary-encoder"
struct rotary_encoder {
- unsigned int irq_a;
- unsigned int irq_b;
- unsigned int pos;
- unsigned int armed;
- unsigned int dir;
struct input_dev *input;
struct rotary_encoder_platform_data *pdata;
+
+ unsigned int axis;
+ unsigned int pos;
+
+ unsigned int irq_a;
+ unsigned int irq_b;
+
+ bool armed;
+ unsigned char dir; /* 0 - clockwise, 1 - CCW */
};
static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
@@ -53,21 +57,32 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
if (!encoder->armed)
break;
- if (encoder->dir) {
- /* turning counter-clockwise */
- encoder->pos += pdata->steps;
- encoder->pos--;
- encoder->pos %= pdata->steps;
+ if (pdata->relative_axis) {
+ input_report_rel(encoder->input, pdata->axis,
+ encoder->dir ? -1 : 1);
} else {
- /* turning clockwise */
- encoder->pos++;
- encoder->pos %= pdata->steps;
+ unsigned int pos = encoder->pos;
+
+ if (encoder->dir) {
+ /* turning counter-clockwise */
+ if (pdata->rollover)
+ pos += pdata->steps;
+ if (pos)
+ pos--;
+ } else {
+ /* turning clockwise */
+ if (pdata->rollover || pos < pdata->steps)
+ pos++;
+ }
+ if (pdata->rollover)
+ pos %= pdata->steps;
+ encoder->pos = pos;
+ input_report_abs(encoder->input, pdata->axis,
+ encoder->pos);
}
-
- input_report_abs(encoder->input, pdata->axis, encoder->pos);
input_sync(encoder->input);
- encoder->armed = 0;
+ encoder->armed = false;
break;
case 0x1:
@@ -77,7 +92,7 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
break;
case 0x3:
- encoder->armed = 1;
+ encoder->armed = true;
break;
}
@@ -113,9 +128,15 @@ static int __devinit rotary_encoder_probe(struct platform_device *pdev)
input->name = pdev->name;
input->id.bustype = BUS_HOST;
input->dev.parent = &pdev->dev;
- input->evbit[0] = BIT_MASK(EV_ABS);
- input_set_abs_params(encoder->input,
- pdata->axis, 0, pdata->steps, 0, 1);
+
+ if (pdata->relative_axis) {
+ input->evbit[0] = BIT_MASK(EV_REL);
+ input->relbit[0] = BIT_MASK(pdata->axis);
+ } else {
+ input->evbit[0] = BIT_MASK(EV_ABS);
+ input_set_abs_params(encoder->input,
+ pdata->axis, 0, pdata->steps, 0, 1);
+ }
err = input_register_device(input);
if (err) {
diff --git a/include/linux/rotary_encoder.h b/include/linux/rotary_encoder.h
index 12d63a3..215278b 100644
--- a/include/linux/rotary_encoder.h
+++ b/include/linux/rotary_encoder.h
@@ -8,6 +8,8 @@ struct rotary_encoder_platform_data {
unsigned int gpio_b;
unsigned int inverted_a;
unsigned int inverted_b;
+ bool relative_axis;
+ bool rollover;
};
#endif /* __ROTARY_ENCODER_H__ */
--
1.6.2.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* RE: [PATCH] add REL_* axes support to the rotary encoder driver
2009-04-16 8:39 ` Daniel Mack
@ 2009-04-16 17:09 ` H Hartley Sweeten
0 siblings, 0 replies; 32+ messages in thread
From: H Hartley Sweeten @ 2009-04-16 17:09 UTC (permalink / raw)
To: Daniel Mack; +Cc: Dmitry Torokhov, linux-input
Input: rotary_encoder - add support for REL_* axes
From: H Hartley Sweeten <hartleys@visionengravers.com>
The rotary encoder driver only supports returning input events
for ABS_* axes, this adds support for REL_* axes. The relative
axis input event is reported as -1 for each counter-clockwise
step and +1 for each clockwise step.
The ability to clamp the position of ABS_* axes between 0 and
a maximum of "steps" has also been added.
The rotary-encoder documentation has been updated to provide
additional information on the new available usage.
Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Signed-off-by: Daniel Mack <daniel@caiaq.de>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---
diff --git a/Documentation/input/rotary-encoder.txt
b/Documentation/input/rotary-encoder.txt
index 435102a..ae4f1a4 100644
--- a/Documentation/input/rotary-encoder.txt
+++ b/Documentation/input/rotary-encoder.txt
@@ -67,7 +67,17 @@ data with it.
struct rotary_encoder_platform_data is declared in
include/linux/rotary-encoder.h and needs to be filled with the number
of
steps the encoder has and can carry information about externally
inverted
-signals (because of used invertig buffer or other reasons).
+signals (because of an inverting buffer or other reasons). The encoder
+can be set up to deliver input information as either an absolute or
relative
+axis. For a relative axis the input event returns +/-1 for each step.
For an
+absolute axis the position of the encoder can either rollover between
zero
+and the number of steps or will clamp at the maximum and zero depending
on
+the configuration.
+
+Note that for an absolute axis that does not rollover, the steps value
does
+not have to match the actual encoder line count. A larger value will
make
+the encoder behave like a multi-turn potentiometer that returns input
+positions from 0 (full CCW) to "steps" (full CW).
Because GPIO to IRQ mapping is platform specific, this information must
be given in seperately to the driver. See the example below.
@@ -85,6 +95,8 @@ be given in seperately to the driver. See the example
below.
static struct rotary_encoder_platform_data my_rotary_encoder_info = {
.steps = 24,
.axis = ABS_X,
+ .relative_axis = false,
+ .rollover = false,
.gpio_a = GPIO_ROTARY_A,
.gpio_b = GPIO_ROTARY_B,
.inverted_a = 0,
diff --git a/drivers/input/misc/rotary_encoder.c
b/drivers/input/misc/rotary_encoder.c
index 5bb3ab5..c806fbf 100644
--- a/drivers/input/misc/rotary_encoder.c
+++ b/drivers/input/misc/rotary_encoder.c
@@ -26,13 +26,17 @@
#define DRV_NAME "rotary-encoder"
struct rotary_encoder {
- unsigned int irq_a;
- unsigned int irq_b;
- unsigned int pos;
- unsigned int armed;
- unsigned int dir;
struct input_dev *input;
struct rotary_encoder_platform_data *pdata;
+
+ unsigned int axis;
+ unsigned int pos;
+
+ unsigned int irq_a;
+ unsigned int irq_b;
+
+ bool armed;
+ unsigned char dir; /* 0 - clockwise, 1 - CCW */
};
static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
@@ -53,21 +57,32 @@ static irqreturn_t rotary_encoder_irq(int irq, void
*dev_id)
if (!encoder->armed)
break;
- if (encoder->dir) {
- /* turning counter-clockwise */
- encoder->pos += pdata->steps;
- encoder->pos--;
- encoder->pos %= pdata->steps;
+ if (pdata->relative_axis) {
+ input_report_rel(encoder->input, pdata->axis,
+ encoder->dir ? -1 : 1);
} else {
- /* turning clockwise */
- encoder->pos++;
- encoder->pos %= pdata->steps;
+ unsigned int pos = encoder->pos;
+
+ if (encoder->dir) {
+ /* turning counter-clockwise */
+ if (pdata->rollover)
+ pos += pdata->steps;
+ if (pos)
+ pos--;
+ } else {
+ /* turning clockwise */
+ if (pdata->rollover || pos <
pdata->steps)
+ pos++;
+ }
+ if (pdata->rollover)
+ pos %= pdata->steps;
+ encoder->pos = pos;
+ input_report_abs(encoder->input, pdata->axis,
+ encoder->pos);
}
-
- input_report_abs(encoder->input, pdata->axis,
encoder->pos);
input_sync(encoder->input);
- encoder->armed = 0;
+ encoder->armed = false;
break;
case 0x1:
@@ -77,7 +92,7 @@ static irqreturn_t rotary_encoder_irq(int irq, void
*dev_id)
break;
case 0x3:
- encoder->armed = 1;
+ encoder->armed = true;
break;
}
@@ -113,9 +128,15 @@ static int __devinit rotary_encoder_probe(struct
platform_device *pdev)
input->name = pdev->name;
input->id.bustype = BUS_HOST;
input->dev.parent = &pdev->dev;
- input->evbit[0] = BIT_MASK(EV_ABS);
- input_set_abs_params(encoder->input,
- pdata->axis, 0, pdata->steps, 0, 1);
+
+ if (pdata->relative_axis) {
+ input->evbit[0] = BIT_MASK(EV_REL);
+ input->relbit[0] = BIT_MASK(pdata->axis);
+ } else {
+ input->evbit[0] = BIT_MASK(EV_ABS);
+ input_set_abs_params(encoder->input,
+ pdata->axis, 0, pdata->steps, 0,
1);
+ }
err = input_register_device(input);
if (err) {
diff --git a/include/linux/rotary_encoder.h
b/include/linux/rotary_encoder.h
index 12d63a3..215278b 100644
--- a/include/linux/rotary_encoder.h
+++ b/include/linux/rotary_encoder.h
@@ -8,6 +8,8 @@ struct rotary_encoder_platform_data {
unsigned int gpio_b;
unsigned int inverted_a;
unsigned int inverted_b;
+ bool relative_axis;
+ bool rollover;
};
#endif /* __ROTARY_ENCODER_H__ */
^ permalink raw reply related [flat|nested] 32+ messages in thread