Skip to content

Commit

Permalink
Input: rotary-encoder - add support for quarter-period mode
Browse files Browse the repository at this point in the history
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: Guido Martínez <[email protected]>
Signed-off-by: Ezequiel Garcia <[email protected]>
Acked-by: Rob Herring <[email protected]>
Signed-off-by: Dmitry Torokhov <[email protected]>
  • Loading branch information
ezequielgarcia authored and dtor committed Oct 16, 2015
1 parent 648b15c commit 3a341a4
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 9 deletions.
11 changes: 10 additions & 1 deletion Documentation/devicetree/bindings/input/rotary-encoder.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,18 @@ Optional properties:
device, hence no steps need to be passed.
- rotary-encoder,rollover: Automatic rollove when the rotary value becomes
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.

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.

Example:
Expand Down
8 changes: 6 additions & 2 deletions Documentation/input/rotary-encoder.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
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).
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 phase diagram of these two outputs look like this:

Expand All @@ -32,6 +33,9 @@ The phase diagram of these two outputs look like this:
|<-->|
one step (half-period mode)

|<>|
one step (quarter-period mode)

For more information, please see
https://en.wikipedia.org/wiki/Rotary_encoder

Expand Down
86 changes: 81 additions & 5 deletions drivers/input/misc/rotary_encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,55 @@ static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id)
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
static const struct of_device_id rotary_encoder_of_match[] = {
{ .compatible = "rotary-encoder", },
Expand All @@ -157,6 +206,7 @@ static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct devic
struct device_node *np = dev->of_node;
struct rotary_encoder_platform_data *pdata;
enum of_gpio_flags flags;
int error;

if (!of_id || !np)
return NULL;
Expand All @@ -178,8 +228,23 @@ static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct devic
pdata->relative_axis =
of_property_read_bool(np, "rotary-encoder,relative-axis");
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");

return pdata;
Expand Down Expand Up @@ -251,12 +316,23 @@ static int rotary_encoder_probe(struct platform_device *pdev)
encoder->irq_a = gpio_to_irq(pdata->gpio_a);
encoder->irq_b = gpio_to_irq(pdata->gpio_b);

/* request the IRQs */
if (pdata->half_period) {
switch (pdata->steps_per_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;
encoder->last_stable = rotary_encoder_get_state(pdata);
} else {
break;
case 1:
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,
Expand Down
2 changes: 1 addition & 1 deletion include/linux/rotary_encoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ struct rotary_encoder_platform_data {
unsigned int gpio_b;
unsigned int inverted_a;
unsigned int inverted_b;
unsigned int steps_per_period;
bool relative_axis;
bool rollover;
bool half_period;
bool wakeup_source;
};

Expand Down

0 comments on commit 3a341a4

Please sign in to comment.