Input: rotary_encoder - add support for REL_* axes
H Hartley Sweeten [Sat, 18 Apr 2009 03:12:35 +0000 (20:12 -0700)]
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
drivers/input/misc/rotary_encoder.c
include/linux/rotary_encoder.h

index 435102a..3a6aec4 100644 (file)
@@ -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,
index 5bb3ab5..c806fbf 100644 (file)
 #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) {
index 12d63a3..215278b 100644 (file)
@@ -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__ */