diff --git a/Documentation/devicetree/bindings/input/rotary-encoder.txt b/Documentation/devicetree/bindings/input/rotary-encoder.txt index 891ddba2d792..de99cbbbf6da 100644 --- a/Documentation/devicetree/bindings/input/rotary-encoder.txt +++ b/Documentation/devicetree/bindings/input/rotary-encoder.txt @@ -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: diff --git a/Documentation/input/rotary-encoder.txt b/Documentation/input/rotary-encoder.txt index bddbee188624..46a74f0c551a 100644 --- a/Documentation/input/rotary-encoder.txt +++ b/Documentation/input/rotary-encoder.txt @@ -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: @@ -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 diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index 962f9e86310b..8aee71986430 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -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", }, @@ -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; @@ -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; @@ -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, diff --git a/include/linux/rotary_encoder.h b/include/linux/rotary_encoder.h index b33f2d2a708f..fe3dc64e5aeb 100644 --- a/include/linux/rotary_encoder.h +++ b/include/linux/rotary_encoder.h @@ -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; };