Commit 7dde4e74 authored by Uwe Kleine-König's avatar Uwe Kleine-König Committed by Dmitry Torokhov

Input: rotary-encoder - support more than 2 gpios as input

This changes how the used gpios are stored (i.e. a struct gpio_descs
instead of two struct gpio_desc) and as with >2 gpios the states are
numbered differently the function rotary_encoder_get_state returns
unencoded numbers instead of grey encoded numbers before. The latter has
some implications on how the returned value is used and so the change is
bigger than one might expect at first.
Signed-off-by: default avatarUwe Kleine-König <u.kleine-koenig@pengutronix.de>
Acked-by: default avatarRob Herring <robh@kernel.org>
Acked-by: default avatarDaniel Mack <daniel@zonque.org>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent a9e340dc
Rotary encoder DT bindings
Required properties:
- gpios: a spec for two GPIOs to be used
- gpios: a spec for at least two GPIOs to be used, most significant first
Optional properties:
- linux,axis: the input subsystem axis to map to this rotary encoder.
......
......@@ -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;
......
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