Commit 3a341a4c authored by Ezequiel Garcia's avatar Ezequiel Garcia Committed by Dmitry Torokhov

Input: rotary-encoder - add support for quarter-period mode

Some encoders have both outputs low in stable states, others also have
a stable state with both outputs high (half-period mode) and some have
a stable state in all steps (quarter-period mode). The driver used to
support the former states and with this change it can also support the
later.

This commit also deprecates the 'half-period' property and introduces
a new property 'steps-per-period'. This property specifies the
number of steps (stable states) produced by the rotary encoder
for each GPIO period.
Signed-off-by: default avatarGuido Martínez <guido@vanguardiasur.com.ar>
Signed-off-by: default avatarEzequiel Garcia <ezequiel@vanguardiasur.com.ar>
Acked-by: default avatarRob Herring <robh@kernel.org>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 648b15cb
...@@ -14,9 +14,18 @@ Optional properties: ...@@ -14,9 +14,18 @@ Optional properties:
device, hence no steps need to be passed. device, hence no steps need to be passed.
- rotary-encoder,rollover: Automatic rollove when the rotary value becomes - rotary-encoder,rollover: Automatic rollove when the rotary value becomes
greater than the specified steps or smaller than 0. For absolute axis only. greater than the specified steps or smaller than 0. For absolute axis only.
- rotary-encoder,half-period: Makes the driver work on half-period mode. - rotary-encoder,steps-per-period: Number of steps (stable states) per period.
The values have the following meaning:
1: Full-period mode (default)
2: Half-period mode
4: Quarter-period mode
- wakeup-source: Boolean, rotary encoder can wake up the system. - wakeup-source: Boolean, rotary encoder can wake up the system.
Deprecated properties:
- rotary-encoder,half-period: Makes the driver work on half-period mode.
This property is deprecated. Instead, a 'steps-per-period ' value should
be used, such as "rotary-encoder,steps-per-period = <2>".
See Documentation/input/rotary-encoder.txt for more information. See Documentation/input/rotary-encoder.txt for more information.
Example: Example:
......
...@@ -9,8 +9,9 @@ peripherals with two wires. The outputs are phase-shifted by 90 degrees ...@@ -9,8 +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 Some encoders have both outputs low in stable states, others also have
a stable state with both outputs high (half-period mode). a stable state with both outputs high (half-period mode) and some have
a stable state in all steps (quarter-period mode).
The phase diagram of these two outputs look like this: The phase diagram of these two outputs look like this:
...@@ -32,6 +33,9 @@ The phase diagram of these two outputs look like this: ...@@ -32,6 +33,9 @@ The phase diagram of these two outputs look like this:
|<-->| |<-->|
one step (half-period mode) one step (half-period mode)
|<>|
one step (quarter-period mode)
For more information, please see For more information, please see
https://en.wikipedia.org/wiki/Rotary_encoder https://en.wikipedia.org/wiki/Rotary_encoder
......
...@@ -143,6 +143,55 @@ static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id) ...@@ -143,6 +143,55 @@ static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static irqreturn_t rotary_encoder_quarter_period_irq(int irq, void *dev_id)
{
struct rotary_encoder *encoder = dev_id;
unsigned char sum;
int state;
state = rotary_encoder_get_state(encoder->pdata);
/*
* 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.
*/
goto out;
}
rotary_encoder_report_event(encoder);
out:
encoder->last_stable = state;
return IRQ_HANDLED;
}
#ifdef CONFIG_OF #ifdef CONFIG_OF
static const struct of_device_id rotary_encoder_of_match[] = { static const struct of_device_id rotary_encoder_of_match[] = {
{ .compatible = "rotary-encoder", }, { .compatible = "rotary-encoder", },
...@@ -157,6 +206,7 @@ static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct devic ...@@ -157,6 +206,7 @@ static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct devic
struct device_node *np = dev->of_node; struct device_node *np = dev->of_node;
struct rotary_encoder_platform_data *pdata; struct rotary_encoder_platform_data *pdata;
enum of_gpio_flags flags; enum of_gpio_flags flags;
int error;
if (!of_id || !np) if (!of_id || !np)
return NULL; return NULL;
...@@ -178,8 +228,23 @@ static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct devic ...@@ -178,8 +228,23 @@ static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct devic
pdata->relative_axis = pdata->relative_axis =
of_property_read_bool(np, "rotary-encoder,relative-axis"); of_property_read_bool(np, "rotary-encoder,relative-axis");
pdata->rollover = of_property_read_bool(np, "rotary-encoder,rollover"); pdata->rollover = of_property_read_bool(np, "rotary-encoder,rollover");
pdata->half_period =
of_property_read_bool(np, "rotary-encoder,half-period"); error = of_property_read_u32(np, "rotary-encoder,steps-per-period",
&pdata->steps_per_period);
if (error) {
/*
* The 'half-period' property has been deprecated, you must use
* 'steps-per-period' and set an appropriate value, but we still
* need to parse it to maintain compatibility.
*/
if (of_property_read_bool(np, "rotary-encoder,half-period")) {
pdata->steps_per_period = 2;
} else {
/* Fallback to one step per period behavior */
pdata->steps_per_period = 1;
}
}
pdata->wakeup_source = of_property_read_bool(np, "wakeup-source"); pdata->wakeup_source = of_property_read_bool(np, "wakeup-source");
return pdata; return pdata;
...@@ -251,12 +316,23 @@ static int rotary_encoder_probe(struct platform_device *pdev) ...@@ -251,12 +316,23 @@ static int rotary_encoder_probe(struct platform_device *pdev)
encoder->irq_a = gpio_to_irq(pdata->gpio_a); encoder->irq_a = gpio_to_irq(pdata->gpio_a);
encoder->irq_b = gpio_to_irq(pdata->gpio_b); encoder->irq_b = gpio_to_irq(pdata->gpio_b);
/* request the IRQs */ switch (pdata->steps_per_period) {
if (pdata->half_period) { case 4:
handler = &rotary_encoder_quarter_period_irq;
encoder->last_stable = rotary_encoder_get_state(pdata);
break;
case 2:
handler = &rotary_encoder_half_period_irq; handler = &rotary_encoder_half_period_irq;
encoder->last_stable = rotary_encoder_get_state(pdata); encoder->last_stable = rotary_encoder_get_state(pdata);
} else { break;
case 1:
handler = &rotary_encoder_irq; handler = &rotary_encoder_irq;
break;
default:
dev_err(dev, "'%d' is not a valid steps-per-period value\n",
pdata->steps_per_period);
err = -EINVAL;
goto exit_free_gpio_b;
} }
err = request_irq(encoder->irq_a, handler, err = request_irq(encoder->irq_a, handler,
......
...@@ -8,9 +8,9 @@ struct rotary_encoder_platform_data { ...@@ -8,9 +8,9 @@ struct rotary_encoder_platform_data {
unsigned int gpio_b; unsigned int gpio_b;
unsigned int inverted_a; unsigned int inverted_a;
unsigned int inverted_b; unsigned int inverted_b;
unsigned int steps_per_period;
bool relative_axis; bool relative_axis;
bool rollover; bool rollover;
bool half_period;
bool wakeup_source; bool wakeup_source;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment