Эх сурвалжийг харах

Input: rotary-encoder - add support for half-period encoders

Add support for encoders that have two detents per input signal period.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Acked-by: Daniel Mack <zonque@gmail.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Johan Hovold 14 жил өмнө
parent
commit
e70bdd41bd

+ 13 - 0
Documentation/input/rotary-encoder.txt

@@ -9,6 +9,9 @@ peripherals with two wires. The outputs are phase-shifted by 90 degrees
 and by triggering on falling and rising edges, the turn direction can
 and by triggering on falling and rising edges, the turn direction can
 be determined.
 be determined.
 
 
+Some encoders have both outputs low in stable states, whereas others also have
+a stable state with both outputs high (half-period mode).
+
 The phase diagram of these two outputs look like this:
 The phase diagram of these two outputs look like this:
 
 
                   _____       _____       _____
                   _____       _____       _____
@@ -26,6 +29,8 @@ The phase diagram of these two outputs look like this:
                 |<-------->|
                 |<-------->|
 	          one step
 	          one step
 
 
+                |<-->|
+	          one step (half-period mode)
 
 
 For more information, please see
 For more information, please see
 	http://en.wikipedia.org/wiki/Rotary_encoder
 	http://en.wikipedia.org/wiki/Rotary_encoder
@@ -34,6 +39,13 @@ For more information, please see
 1. Events / state machine
 1. Events / state machine
 -------------------------
 -------------------------
 
 
+In half-period mode, state a) and c) above are used to determine the
+rotational direction based on the last stable state. Events are reported in
+states b) and d) given that the new stable state is different from the last
+(i.e. the rotation was not reversed half-way).
+
+Otherwise, the following apply:
+
 a) Rising edge on channel A, channel B in low state
 a) Rising edge on channel A, channel B in low state
 	This state is used to recognize a clockwise turn
 	This state is used to recognize a clockwise turn
 
 
@@ -96,6 +108,7 @@ static struct rotary_encoder_platform_data my_rotary_encoder_info = {
 	.gpio_b		= GPIO_ROTARY_B,
 	.gpio_b		= GPIO_ROTARY_B,
 	.inverted_a	= 0,
 	.inverted_a	= 0,
 	.inverted_b	= 0,
 	.inverted_b	= 0,
+	.half_period	= false,
 };
 };
 
 
 static struct platform_device rotary_encoder_device = {
 static struct platform_device rotary_encoder_device = {

+ 39 - 3
drivers/input/misc/rotary_encoder.c

@@ -2,6 +2,7 @@
  * rotary_encoder.c
  * rotary_encoder.c
  *
  *
  * (c) 2009 Daniel Mack <daniel@caiaq.de>
  * (c) 2009 Daniel Mack <daniel@caiaq.de>
+ * Copyright (C) 2011 Johan Hovold <jhovold@gmail.com>
  *
  *
  * state machine code inspired by code from Tim Ruetz
  * state machine code inspired by code from Tim Ruetz
  *
  *
@@ -38,6 +39,8 @@ struct rotary_encoder {
 
 
 	bool armed;
 	bool armed;
 	unsigned char dir;	/* 0 - clockwise, 1 - CCW */
 	unsigned char dir;	/* 0 - clockwise, 1 - CCW */
+
+	char last_stable;
 };
 };
 
 
 static int rotary_encoder_get_state(struct rotary_encoder_platform_data *pdata)
 static int rotary_encoder_get_state(struct rotary_encoder_platform_data *pdata)
@@ -112,11 +115,37 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
 	return IRQ_HANDLED;
 	return IRQ_HANDLED;
 }
 }
 
 
+static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id)
+{
+	struct rotary_encoder *encoder = dev_id;
+	int state;
+
+	state = rotary_encoder_get_state(encoder->pdata);
+
+	switch (state) {
+	case 0x00:
+	case 0x03:
+		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;
+	}
+
+	return IRQ_HANDLED;
+}
+
 static int __devinit rotary_encoder_probe(struct platform_device *pdev)
 static int __devinit rotary_encoder_probe(struct platform_device *pdev)
 {
 {
 	struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data;
 	struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data;
 	struct rotary_encoder *encoder;
 	struct rotary_encoder *encoder;
 	struct input_dev *input;
 	struct input_dev *input;
+	irq_handler_t handler;
 	int err;
 	int err;
 
 
 	if (!pdata) {
 	if (!pdata) {
@@ -187,7 +216,14 @@ static int __devinit rotary_encoder_probe(struct platform_device *pdev)
 	}
 	}
 
 
 	/* request the IRQs */
 	/* request the IRQs */
-	err = request_irq(encoder->irq_a, &rotary_encoder_irq,
+	if (pdata->half_period) {
+		handler = &rotary_encoder_half_period_irq;
+		encoder->last_stable = rotary_encoder_get_state(pdata);
+	} else {
+		handler = &rotary_encoder_irq;
+	}
+
+	err = request_irq(encoder->irq_a, handler,
 			  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
 			  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
 			  DRV_NAME, encoder);
 			  DRV_NAME, encoder);
 	if (err) {
 	if (err) {
@@ -196,7 +232,7 @@ static int __devinit rotary_encoder_probe(struct platform_device *pdev)
 		goto exit_free_gpio_b;
 		goto exit_free_gpio_b;
 	}
 	}
 
 
-	err = request_irq(encoder->irq_b, &rotary_encoder_irq,
+	err = request_irq(encoder->irq_b, handler,
 			  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
 			  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
 			  DRV_NAME, encoder);
 			  DRV_NAME, encoder);
 	if (err) {
 	if (err) {
@@ -264,5 +300,5 @@ module_exit(rotary_encoder_exit);
 
 
 MODULE_ALIAS("platform:" DRV_NAME);
 MODULE_ALIAS("platform:" DRV_NAME);
 MODULE_DESCRIPTION("GPIO rotary encoder driver");
 MODULE_DESCRIPTION("GPIO rotary encoder driver");
-MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
+MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>, Johan Hovold");
 MODULE_LICENSE("GPL v2");
 MODULE_LICENSE("GPL v2");

+ 1 - 0
include/linux/rotary_encoder.h

@@ -10,6 +10,7 @@ struct rotary_encoder_platform_data {
 	unsigned int inverted_b;
 	unsigned int inverted_b;
 	bool relative_axis;
 	bool relative_axis;
 	bool rollover;
 	bool rollover;
+	bool half_period;
 };
 };
 
 
 #endif /* __ROTARY_ENCODER_H__ */
 #endif /* __ROTARY_ENCODER_H__ */