]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blame - drivers/input/misc/rotary_encoder.c
Input: rotary_encoder - convert to use gpiod API
[mirror_ubuntu-zesty-kernel.git] / drivers / input / misc / rotary_encoder.c
CommitLineData
73969ff0
DM
1/*
2 * rotary_encoder.c
3 *
4 * (c) 2009 Daniel Mack <daniel@caiaq.de>
e70bdd41 5 * Copyright (C) 2011 Johan Hovold <jhovold@gmail.com>
73969ff0
DM
6 *
7 * state machine code inspired by code from Tim Ruetz
8 *
9 * A generic driver for rotary encoders connected to GPIO lines.
395cf969 10 * See file:Documentation/input/rotary-encoder.txt for more information
73969ff0
DM
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License version 2 as
14 * published by the Free Software Foundation.
15 */
16
17#include <linux/kernel.h>
18#include <linux/module.h>
73969ff0
DM
19#include <linux/interrupt.h>
20#include <linux/input.h>
21#include <linux/device.h>
22#include <linux/platform_device.h>
77a8f0ad 23#include <linux/gpio/consumer.h>
73969ff0 24#include <linux/rotary_encoder.h>
5a0e3ad6 25#include <linux/slab.h>
2e45e539 26#include <linux/of.h>
80c99bcd 27#include <linux/of_platform.h>
47ec6e5a 28#include <linux/pm.h>
73969ff0
DM
29
30#define DRV_NAME "rotary-encoder"
31
32struct rotary_encoder {
73969ff0 33 struct input_dev *input;
ce919537 34 const struct rotary_encoder_platform_data *pdata;
dee520e3 35 struct mutex access_mutex;
bd3ce655
HS
36
37 unsigned int axis;
38 unsigned int pos;
39
77a8f0ad
DT
40 struct gpio_desc *gpio_a;
41 struct gpio_desc *gpio_b;
42
bd3ce655
HS
43 unsigned int irq_a;
44 unsigned int irq_b;
45
46 bool armed;
47 unsigned char dir; /* 0 - clockwise, 1 - CCW */
e70bdd41
JH
48
49 char last_stable;
73969ff0
DM
50};
51
77a8f0ad 52static int rotary_encoder_get_state(struct rotary_encoder *encoder)
73969ff0 53{
77a8f0ad
DT
54 int a = !!gpiod_get_value_cansleep(encoder->gpio_a);
55 int b = !!gpiod_get_value_cansleep(encoder->gpio_b);
73969ff0 56
521a8f5c
JH
57 return ((a << 1) | b);
58}
73969ff0 59
521a8f5c
JH
60static void rotary_encoder_report_event(struct rotary_encoder *encoder)
61{
ce919537 62 const struct rotary_encoder_platform_data *pdata = encoder->pdata;
73969ff0 63
521a8f5c
JH
64 if (pdata->relative_axis) {
65 input_report_rel(encoder->input,
66 pdata->axis, encoder->dir ? -1 : 1);
67 } else {
68 unsigned int pos = encoder->pos;
69
70 if (encoder->dir) {
71 /* turning counter-clockwise */
bd3ce655 72 if (pdata->rollover)
521a8f5c
JH
73 pos += pdata->steps;
74 if (pos)
75 pos--;
76 } else {
77 /* turning clockwise */
78 if (pdata->rollover || pos < pdata->steps)
79 pos++;
73969ff0 80 }
73969ff0 81
521a8f5c
JH
82 if (pdata->rollover)
83 pos %= pdata->steps;
84
85 encoder->pos = pos;
86 input_report_abs(encoder->input, pdata->axis, encoder->pos);
87 }
88
89 input_sync(encoder->input);
90}
91
92static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
93{
94 struct rotary_encoder *encoder = dev_id;
95 int state;
96
dee520e3
TT
97 mutex_lock(&encoder->access_mutex);
98
77a8f0ad 99 state = rotary_encoder_get_state(encoder);
521a8f5c
JH
100
101 switch (state) {
102 case 0x0:
103 if (encoder->armed) {
104 rotary_encoder_report_event(encoder);
105 encoder->armed = false;
106 }
73969ff0
DM
107 break;
108
109 case 0x1:
110 case 0x2:
111 if (encoder->armed)
112 encoder->dir = state - 1;
113 break;
114
115 case 0x3:
bd3ce655 116 encoder->armed = true;
73969ff0
DM
117 break;
118 }
119
dee520e3
TT
120 mutex_unlock(&encoder->access_mutex);
121
73969ff0
DM
122 return IRQ_HANDLED;
123}
124
e70bdd41
JH
125static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id)
126{
127 struct rotary_encoder *encoder = dev_id;
128 int state;
129
dee520e3
TT
130 mutex_lock(&encoder->access_mutex);
131
77a8f0ad 132 state = rotary_encoder_get_state(encoder);
e70bdd41
JH
133
134 switch (state) {
135 case 0x00:
136 case 0x03:
137 if (state != encoder->last_stable) {
138 rotary_encoder_report_event(encoder);
139 encoder->last_stable = state;
140 }
141 break;
142
143 case 0x01:
144 case 0x02:
145 encoder->dir = (encoder->last_stable + state) & 0x01;
146 break;
147 }
148
dee520e3
TT
149 mutex_unlock(&encoder->access_mutex);
150
e70bdd41
JH
151 return IRQ_HANDLED;
152}
153
3a341a4c
EG
154static irqreturn_t rotary_encoder_quarter_period_irq(int irq, void *dev_id)
155{
156 struct rotary_encoder *encoder = dev_id;
157 unsigned char sum;
158 int state;
159
dee520e3
TT
160 mutex_lock(&encoder->access_mutex);
161
77a8f0ad 162 state = rotary_encoder_get_state(encoder);
3a341a4c
EG
163
164 /*
165 * We encode the previous and the current state using a byte.
166 * The previous state in the MSB nibble, the current state in the LSB
167 * nibble. Then use a table to decide the direction of the turn.
168 */
169 sum = (encoder->last_stable << 4) + state;
170 switch (sum) {
171 case 0x31:
172 case 0x10:
173 case 0x02:
174 case 0x23:
175 encoder->dir = 0; /* clockwise */
176 break;
177
178 case 0x13:
179 case 0x01:
180 case 0x20:
181 case 0x32:
182 encoder->dir = 1; /* counter-clockwise */
183 break;
184
185 default:
186 /*
187 * Ignore all other values. This covers the case when the
188 * state didn't change (a spurious interrupt) and the
189 * cases where the state changed by two steps, making it
190 * impossible to tell the direction.
191 *
192 * In either case, don't report any event and save the
193 * state for later.
194 */
195 goto out;
196 }
197
198 rotary_encoder_report_event(encoder);
199
200out:
201 encoder->last_stable = state;
dee520e3
TT
202 mutex_unlock(&encoder->access_mutex);
203
3a341a4c
EG
204 return IRQ_HANDLED;
205}
206
80c99bcd 207#ifdef CONFIG_OF
5f155ee1 208static const struct of_device_id rotary_encoder_of_match[] = {
80c99bcd
DM
209 { .compatible = "rotary-encoder", },
210 { },
211};
212MODULE_DEVICE_TABLE(of, rotary_encoder_of_match);
213
5298cc4c 214static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct device *dev)
80c99bcd
DM
215{
216 const struct of_device_id *of_id =
217 of_match_device(rotary_encoder_of_match, dev);
218 struct device_node *np = dev->of_node;
219 struct rotary_encoder_platform_data *pdata;
3a341a4c 220 int error;
80c99bcd
DM
221
222 if (!of_id || !np)
223 return NULL;
224
d9202af2
TT
225 pdata = devm_kzalloc(dev, sizeof(struct rotary_encoder_platform_data),
226 GFP_KERNEL);
80c99bcd
DM
227 if (!pdata)
228 return ERR_PTR(-ENOMEM);
229
230 of_property_read_u32(np, "rotary-encoder,steps", &pdata->steps);
231 of_property_read_u32(np, "linux,axis", &pdata->axis);
232
648b15cb
BG
233 pdata->relative_axis =
234 of_property_read_bool(np, "rotary-encoder,relative-axis");
235 pdata->rollover = of_property_read_bool(np, "rotary-encoder,rollover");
3a341a4c
EG
236
237 error = of_property_read_u32(np, "rotary-encoder,steps-per-period",
238 &pdata->steps_per_period);
239 if (error) {
240 /*
241 * The 'half-period' property has been deprecated, you must use
242 * 'steps-per-period' and set an appropriate value, but we still
243 * need to parse it to maintain compatibility.
244 */
245 if (of_property_read_bool(np, "rotary-encoder,half-period")) {
246 pdata->steps_per_period = 2;
247 } else {
248 /* Fallback to one step per period behavior */
249 pdata->steps_per_period = 1;
250 }
251 }
252
648b15cb 253 pdata->wakeup_source = of_property_read_bool(np, "wakeup-source");
80c99bcd
DM
254
255 return pdata;
256}
257#else
258static inline struct rotary_encoder_platform_data *
259rotary_encoder_parse_dt(struct device *dev)
260{
261 return NULL;
262}
263#endif
264
5298cc4c 265static int rotary_encoder_probe(struct platform_device *pdev)
73969ff0 266{
ce919537
DT
267 struct device *dev = &pdev->dev;
268 const struct rotary_encoder_platform_data *pdata = dev_get_platdata(dev);
73969ff0
DM
269 struct rotary_encoder *encoder;
270 struct input_dev *input;
e70bdd41 271 irq_handler_t handler;
73969ff0
DM
272 int err;
273
06ee3d3c 274 if (!pdata) {
80c99bcd
DM
275 pdata = rotary_encoder_parse_dt(dev);
276 if (IS_ERR(pdata))
277 return PTR_ERR(pdata);
278
279 if (!pdata) {
280 dev_err(dev, "missing platform data\n");
281 return -EINVAL;
282 }
73969ff0
DM
283 }
284
d9202af2
TT
285 encoder = devm_kzalloc(dev, sizeof(struct rotary_encoder), GFP_KERNEL);
286 if (!encoder)
287 return -ENOMEM;
288
77a8f0ad
DT
289 mutex_init(&encoder->access_mutex);
290 encoder->pdata = pdata;
291
292 encoder->gpio_a = devm_gpiod_get_index(dev, NULL, 0, GPIOD_IN);
293 if (IS_ERR(encoder->gpio_a)) {
294 err = PTR_ERR(encoder->gpio_a);
295 dev_err(dev, "unable to get GPIO at index 0: %d\n", err);
296 return err;
297 }
298
299 encoder->irq_a = gpiod_to_irq(encoder->gpio_a);
300
301 encoder->gpio_b = devm_gpiod_get_index(dev, NULL, 1, GPIOD_IN);
302 if (IS_ERR(encoder->gpio_b)) {
303 err = PTR_ERR(encoder->gpio_b);
304 dev_err(dev, "unable to get GPIO at index 1: %d\n", err);
305 return err;
306 }
307
308 encoder->irq_b = gpiod_to_irq(encoder->gpio_b);
309
d9202af2
TT
310 input = devm_input_allocate_device(dev);
311 if (!input)
312 return -ENOMEM;
73969ff0
DM
313
314 encoder->input = input;
73969ff0 315
73969ff0
DM
316 input->name = pdev->name;
317 input->id.bustype = BUS_HOST;
80c99bcd 318 input->dev.parent = dev;
bd3ce655
HS
319
320 if (pdata->relative_axis) {
321 input->evbit[0] = BIT_MASK(EV_REL);
322 input->relbit[0] = BIT_MASK(pdata->axis);
323 } else {
324 input->evbit[0] = BIT_MASK(EV_ABS);
325 input_set_abs_params(encoder->input,
326 pdata->axis, 0, pdata->steps, 0, 1);
327 }
73969ff0 328
3a341a4c
EG
329 switch (pdata->steps_per_period) {
330 case 4:
331 handler = &rotary_encoder_quarter_period_irq;
77a8f0ad 332 encoder->last_stable = rotary_encoder_get_state(encoder);
3a341a4c
EG
333 break;
334 case 2:
e70bdd41 335 handler = &rotary_encoder_half_period_irq;
77a8f0ad 336 encoder->last_stable = rotary_encoder_get_state(encoder);
3a341a4c
EG
337 break;
338 case 1:
e70bdd41 339 handler = &rotary_encoder_irq;
3a341a4c
EG
340 break;
341 default:
342 dev_err(dev, "'%d' is not a valid steps-per-period value\n",
343 pdata->steps_per_period);
d9202af2 344 return -EINVAL;
e70bdd41
JH
345 }
346
dee520e3
TT
347 err = devm_request_threaded_irq(dev, encoder->irq_a, NULL, handler,
348 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
349 IRQF_ONESHOT,
350 DRV_NAME, encoder);
73969ff0 351 if (err) {
429a34d7 352 dev_err(dev, "unable to request IRQ %d\n", encoder->irq_a);
d9202af2 353 return err;
73969ff0
DM
354 }
355
dee520e3
TT
356 err = devm_request_threaded_irq(dev, encoder->irq_b, NULL, handler,
357 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
358 IRQF_ONESHOT,
359 DRV_NAME, encoder);
73969ff0 360 if (err) {
429a34d7 361 dev_err(dev, "unable to request IRQ %d\n", encoder->irq_b);
d9202af2 362 return err;
73969ff0
DM
363 }
364
80c99bcd
DM
365 err = input_register_device(input);
366 if (err) {
367 dev_err(dev, "failed to register input device\n");
d9202af2 368 return err;
80c99bcd
DM
369 }
370
47ec6e5a
SR
371 device_init_wakeup(&pdev->dev, pdata->wakeup_source);
372
73969ff0
DM
373 platform_set_drvdata(pdev, encoder);
374
375 return 0;
73969ff0
DM
376}
377
6a6f70b3 378static int __maybe_unused rotary_encoder_suspend(struct device *dev)
47ec6e5a
SR
379{
380 struct rotary_encoder *encoder = dev_get_drvdata(dev);
381
382 if (device_may_wakeup(dev)) {
383 enable_irq_wake(encoder->irq_a);
384 enable_irq_wake(encoder->irq_b);
385 }
386
387 return 0;
388}
389
6a6f70b3 390static int __maybe_unused rotary_encoder_resume(struct device *dev)
47ec6e5a
SR
391{
392 struct rotary_encoder *encoder = dev_get_drvdata(dev);
393
394 if (device_may_wakeup(dev)) {
395 disable_irq_wake(encoder->irq_a);
396 disable_irq_wake(encoder->irq_b);
397 }
398
399 return 0;
400}
47ec6e5a
SR
401
402static SIMPLE_DEV_PM_OPS(rotary_encoder_pm_ops,
6a6f70b3 403 rotary_encoder_suspend, rotary_encoder_resume);
47ec6e5a 404
73969ff0
DM
405static struct platform_driver rotary_encoder_driver = {
406 .probe = rotary_encoder_probe,
73969ff0
DM
407 .driver = {
408 .name = DRV_NAME,
47ec6e5a 409 .pm = &rotary_encoder_pm_ops,
80c99bcd 410 .of_match_table = of_match_ptr(rotary_encoder_of_match),
73969ff0
DM
411 }
412};
840a746b 413module_platform_driver(rotary_encoder_driver);
73969ff0
DM
414
415MODULE_ALIAS("platform:" DRV_NAME);
416MODULE_DESCRIPTION("GPIO rotary encoder driver");
e70bdd41 417MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>, Johan Hovold");
73969ff0 418MODULE_LICENSE("GPL v2");