Input: rotary-encoder - support more than 2 gpios as input
[sfrench/cifs-2.6.git] / drivers / input / misc / rotary_encoder.c
index 5a4e1d69c4af86067e5e9e01d166e43fa6d5a3b8..96c486de49e0c8291af7363aa17ed9b281d7ae77 100644 (file)
@@ -40,35 +40,42 @@ struct rotary_encoder {
 
        unsigned int pos;
 
-       struct gpio_desc *gpio_a;
-       struct gpio_desc *gpio_b;
+       struct gpio_descs *gpios;
 
-       unsigned int irq_a;
-       unsigned int irq_b;
+       unsigned int *irq;
 
        bool armed;
-       unsigned char dir;      /* 0 - clockwise, 1 - CCW */
+       signed char dir;        /* 1 - clockwise, -1 - CCW */
 
-       char last_stable;
+       unsigned last_stable;
 };
 
-static int rotary_encoder_get_state(struct rotary_encoder *encoder)
+static unsigned rotary_encoder_get_state(struct rotary_encoder *encoder)
 {
-       int a = !!gpiod_get_value_cansleep(encoder->gpio_a);
-       int b = !!gpiod_get_value_cansleep(encoder->gpio_b);
+       int i;
+       unsigned ret = 0;
 
-       return ((a << 1) | b);
+       for (i = 0; i < encoder->gpios->ndescs; ++i) {
+               int val = gpiod_get_value_cansleep(encoder->gpios->desc[i]);
+               /* convert from gray encoding to normal */
+               if (ret & 1)
+                       val = !val;
+
+               ret = ret << 1 | val;
+       }
+
+       return ret & 3;
 }
 
 static void rotary_encoder_report_event(struct rotary_encoder *encoder)
 {
        if (encoder->relative_axis) {
                input_report_rel(encoder->input,
-                                encoder->axis, encoder->dir ? -1 : 1);
+                                encoder->axis, encoder->dir);
        } else {
                unsigned int pos = encoder->pos;
 
-               if (encoder->dir) {
+               if (encoder->dir < 0) {
                        /* turning counter-clockwise */
                        if (encoder->rollover)
                                pos += encoder->steps;
@@ -93,7 +100,7 @@ static void rotary_encoder_report_event(struct rotary_encoder *encoder)
 static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
 {
        struct rotary_encoder *encoder = dev_id;
-       int state;
+       unsigned state;
 
        mutex_lock(&encoder->access_mutex);
 
@@ -108,12 +115,12 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
                break;
 
        case 0x1:
-       case 0x2:
+       case 0x3:
                if (encoder->armed)
-                       encoder->dir = state - 1;
+                       encoder->dir = 2 - state;
                break;
 
-       case 0x3:
+       case 0x2:
                encoder->armed = true;
                break;
        }
@@ -126,25 +133,19 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
 static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id)
 {
        struct rotary_encoder *encoder = dev_id;
-       int state;
+       unsigned int state;
 
        mutex_lock(&encoder->access_mutex);
 
        state = rotary_encoder_get_state(encoder);
 
-       switch (state) {
-       case 0x00:
-       case 0x03:
+       if (state & 1) {
+               encoder->dir = ((encoder->last_stable - state + 1) % 4) - 1;
+       } else {
                if (state != encoder->last_stable) {
                        rotary_encoder_report_event(encoder);
                        encoder->last_stable = state;
                }
-               break;
-
-       case 0x01:
-       case 0x02:
-               encoder->dir = (encoder->last_stable + state) & 0x01;
-               break;
        }
 
        mutex_unlock(&encoder->access_mutex);
@@ -155,46 +156,18 @@ static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id)
 static irqreturn_t rotary_encoder_quarter_period_irq(int irq, void *dev_id)
 {
        struct rotary_encoder *encoder = dev_id;
-       unsigned char sum;
-       int state;
+       unsigned int state;
 
        mutex_lock(&encoder->access_mutex);
 
        state = rotary_encoder_get_state(encoder);
 
-       /*
-        * We encode the previous and the current state using a byte.
-        * The previous state in the MSB nibble, the current state in the LSB
-        * nibble. Then use a table to decide the direction of the turn.
-        */
-       sum = (encoder->last_stable << 4) + state;
-       switch (sum) {
-       case 0x31:
-       case 0x10:
-       case 0x02:
-       case 0x23:
-               encoder->dir = 0; /* clockwise */
-               break;
-
-       case 0x13:
-       case 0x01:
-       case 0x20:
-       case 0x32:
-               encoder->dir = 1; /* counter-clockwise */
-               break;
-
-       default:
-               /*
-                * Ignore all other values. This covers the case when the
-                * state didn't change (a spurious interrupt) and the
-                * cases where the state changed by two steps, making it
-                * impossible to tell the direction.
-                *
-                * In either case, don't report any event and save the
-                * state for later.
-                */
+       if ((encoder->last_stable + 1) % 4 == state)
+               encoder->dir = 1;
+       else if (encoder->last_stable == (state + 1) % 4)
+               encoder->dir = -1;
+       else
                goto out;
-       }
 
        rotary_encoder_report_event(encoder);
 
@@ -212,6 +185,7 @@ static int rotary_encoder_probe(struct platform_device *pdev)
        struct input_dev *input;
        irq_handler_t handler;
        u32 steps_per_period;
+       unsigned int i;
        int err;
 
        encoder = devm_kzalloc(dev, sizeof(struct rotary_encoder), GFP_KERNEL);
@@ -243,24 +217,16 @@ static int rotary_encoder_probe(struct platform_device *pdev)
        encoder->relative_axis =
                device_property_read_bool(dev, "rotary-encoder,relative-axis");
 
-       encoder->gpio_a = devm_gpiod_get_index(dev, NULL, 0, GPIOD_IN);
-       if (IS_ERR(encoder->gpio_a)) {
-               err = PTR_ERR(encoder->gpio_a);
-               dev_err(dev, "unable to get GPIO at index 0: %d\n", err);
-               return err;
+       encoder->gpios = devm_gpiod_get_array(dev, NULL, GPIOD_IN);
+       if (IS_ERR(encoder->gpios)) {
+               dev_err(dev, "unable to get gpios\n");
+               return PTR_ERR(encoder->gpios);
        }
-
-       encoder->irq_a = gpiod_to_irq(encoder->gpio_a);
-
-       encoder->gpio_b = devm_gpiod_get_index(dev, NULL, 1, GPIOD_IN);
-       if (IS_ERR(encoder->gpio_b)) {
-               err = PTR_ERR(encoder->gpio_b);
-               dev_err(dev, "unable to get GPIO at index 1: %d\n", err);
-               return err;
+       if (encoder->gpios->ndescs < 2) {
+               dev_err(dev, "not enough gpios found\n");
+               return -EINVAL;
        }
 
-       encoder->irq_b = gpiod_to_irq(encoder->gpio_b);
-
        input = devm_input_allocate_device(dev);
        if (!input)
                return -ENOMEM;
@@ -277,7 +243,7 @@ static int rotary_encoder_probe(struct platform_device *pdev)
                input_set_abs_params(input,
                                     encoder->axis, 0, encoder->steps, 0, 1);
 
-       switch (steps_per_period) {
+       switch (steps_per_period >> (encoder->gpios->ndescs - 2)) {
        case 4:
                handler = &rotary_encoder_quarter_period_irq;
                encoder->last_stable = rotary_encoder_get_state(encoder);
@@ -295,22 +261,26 @@ static int rotary_encoder_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       err = devm_request_threaded_irq(dev, encoder->irq_a, NULL, handler,
-                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
-                               IRQF_ONESHOT,
-                               DRV_NAME, encoder);
-       if (err) {
-               dev_err(dev, "unable to request IRQ %d\n", encoder->irq_a);
-               return err;
-       }
+       encoder->irq =
+               devm_kzalloc(dev,
+                            sizeof(*encoder->irq) * encoder->gpios->ndescs,
+                            GFP_KERNEL);
+       if (!encoder->irq)
+               return -ENOMEM;
 
-       err = devm_request_threaded_irq(dev, encoder->irq_b, NULL, handler,
+       for (i = 0; i < encoder->gpios->ndescs; ++i) {
+               encoder->irq[i] = gpiod_to_irq(encoder->gpios->desc[i]);
+
+               err = devm_request_threaded_irq(dev, encoder->irq[i],
+                               NULL, handler,
                                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
                                IRQF_ONESHOT,
                                DRV_NAME, encoder);
-       if (err) {
-               dev_err(dev, "unable to request IRQ %d\n", encoder->irq_b);
-               return err;
+               if (err) {
+                       dev_err(dev, "unable to request IRQ %d (gpio#%d)\n",
+                               encoder->irq[i], i);
+                       return err;
+               }
        }
 
        err = input_register_device(input);
@@ -330,10 +300,11 @@ static int rotary_encoder_probe(struct platform_device *pdev)
 static int __maybe_unused rotary_encoder_suspend(struct device *dev)
 {
        struct rotary_encoder *encoder = dev_get_drvdata(dev);
+       unsigned int i;
 
        if (device_may_wakeup(dev)) {
-               enable_irq_wake(encoder->irq_a);
-               enable_irq_wake(encoder->irq_b);
+               for (i = 0; i < encoder->gpios->ndescs; ++i)
+                       enable_irq_wake(encoder->irq[i]);
        }
 
        return 0;
@@ -342,10 +313,11 @@ static int __maybe_unused rotary_encoder_suspend(struct device *dev)
 static int __maybe_unused rotary_encoder_resume(struct device *dev)
 {
        struct rotary_encoder *encoder = dev_get_drvdata(dev);
+       unsigned int i;
 
        if (device_may_wakeup(dev)) {
-               disable_irq_wake(encoder->irq_a);
-               disable_irq_wake(encoder->irq_b);
+               for (i = 0; i < encoder->gpios->ndescs; ++i)
+                       disable_irq_wake(encoder->irq[i]);
        }
 
        return 0;