]>
Commit | Line | Data |
---|---|---|
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> | |
19 | #include <linux/init.h> | |
20 | #include <linux/interrupt.h> | |
21 | #include <linux/input.h> | |
22 | #include <linux/device.h> | |
23 | #include <linux/platform_device.h> | |
24 | #include <linux/gpio.h> | |
25 | #include <linux/rotary_encoder.h> | |
5a0e3ad6 | 26 | #include <linux/slab.h> |
80c99bcd DM |
27 | #include <linux/of_platform.h> |
28 | #include <linux/of_gpio.h> | |
73969ff0 DM |
29 | |
30 | #define DRV_NAME "rotary-encoder" | |
31 | ||
32 | struct rotary_encoder { | |
73969ff0 | 33 | struct input_dev *input; |
ce919537 | 34 | const struct rotary_encoder_platform_data *pdata; |
bd3ce655 HS |
35 | |
36 | unsigned int axis; | |
37 | unsigned int pos; | |
38 | ||
39 | unsigned int irq_a; | |
40 | unsigned int irq_b; | |
41 | ||
42 | bool armed; | |
43 | unsigned char dir; /* 0 - clockwise, 1 - CCW */ | |
e70bdd41 JH |
44 | |
45 | char last_stable; | |
73969ff0 DM |
46 | }; |
47 | ||
ce919537 | 48 | static int rotary_encoder_get_state(const struct rotary_encoder_platform_data *pdata) |
73969ff0 | 49 | { |
73969ff0 DM |
50 | int a = !!gpio_get_value(pdata->gpio_a); |
51 | int b = !!gpio_get_value(pdata->gpio_b); | |
73969ff0 DM |
52 | |
53 | a ^= pdata->inverted_a; | |
54 | b ^= pdata->inverted_b; | |
73969ff0 | 55 | |
521a8f5c JH |
56 | return ((a << 1) | b); |
57 | } | |
73969ff0 | 58 | |
521a8f5c JH |
59 | static void rotary_encoder_report_event(struct rotary_encoder *encoder) |
60 | { | |
ce919537 | 61 | const struct rotary_encoder_platform_data *pdata = encoder->pdata; |
73969ff0 | 62 | |
521a8f5c JH |
63 | if (pdata->relative_axis) { |
64 | input_report_rel(encoder->input, | |
65 | pdata->axis, encoder->dir ? -1 : 1); | |
66 | } else { | |
67 | unsigned int pos = encoder->pos; | |
68 | ||
69 | if (encoder->dir) { | |
70 | /* turning counter-clockwise */ | |
bd3ce655 | 71 | if (pdata->rollover) |
521a8f5c JH |
72 | pos += pdata->steps; |
73 | if (pos) | |
74 | pos--; | |
75 | } else { | |
76 | /* turning clockwise */ | |
77 | if (pdata->rollover || pos < pdata->steps) | |
78 | pos++; | |
73969ff0 | 79 | } |
73969ff0 | 80 | |
521a8f5c JH |
81 | if (pdata->rollover) |
82 | pos %= pdata->steps; | |
83 | ||
84 | encoder->pos = pos; | |
85 | input_report_abs(encoder->input, pdata->axis, encoder->pos); | |
86 | } | |
87 | ||
88 | input_sync(encoder->input); | |
89 | } | |
90 | ||
91 | static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) | |
92 | { | |
93 | struct rotary_encoder *encoder = dev_id; | |
94 | int state; | |
95 | ||
96 | state = rotary_encoder_get_state(encoder->pdata); | |
97 | ||
98 | switch (state) { | |
99 | case 0x0: | |
100 | if (encoder->armed) { | |
101 | rotary_encoder_report_event(encoder); | |
102 | encoder->armed = false; | |
103 | } | |
73969ff0 DM |
104 | break; |
105 | ||
106 | case 0x1: | |
107 | case 0x2: | |
108 | if (encoder->armed) | |
109 | encoder->dir = state - 1; | |
110 | break; | |
111 | ||
112 | case 0x3: | |
bd3ce655 | 113 | encoder->armed = true; |
73969ff0 DM |
114 | break; |
115 | } | |
116 | ||
117 | return IRQ_HANDLED; | |
118 | } | |
119 | ||
e70bdd41 JH |
120 | static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id) |
121 | { | |
122 | struct rotary_encoder *encoder = dev_id; | |
123 | int state; | |
124 | ||
125 | state = rotary_encoder_get_state(encoder->pdata); | |
126 | ||
127 | switch (state) { | |
128 | case 0x00: | |
129 | case 0x03: | |
130 | if (state != encoder->last_stable) { | |
131 | rotary_encoder_report_event(encoder); | |
132 | encoder->last_stable = state; | |
133 | } | |
134 | break; | |
135 | ||
136 | case 0x01: | |
137 | case 0x02: | |
138 | encoder->dir = (encoder->last_stable + state) & 0x01; | |
139 | break; | |
140 | } | |
141 | ||
142 | return IRQ_HANDLED; | |
143 | } | |
144 | ||
80c99bcd DM |
145 | #ifdef CONFIG_OF |
146 | static struct of_device_id rotary_encoder_of_match[] = { | |
147 | { .compatible = "rotary-encoder", }, | |
148 | { }, | |
149 | }; | |
150 | MODULE_DEVICE_TABLE(of, rotary_encoder_of_match); | |
151 | ||
5298cc4c | 152 | static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct device *dev) |
80c99bcd DM |
153 | { |
154 | const struct of_device_id *of_id = | |
155 | of_match_device(rotary_encoder_of_match, dev); | |
156 | struct device_node *np = dev->of_node; | |
157 | struct rotary_encoder_platform_data *pdata; | |
158 | enum of_gpio_flags flags; | |
159 | ||
160 | if (!of_id || !np) | |
161 | return NULL; | |
162 | ||
163 | pdata = kzalloc(sizeof(struct rotary_encoder_platform_data), | |
164 | GFP_KERNEL); | |
165 | if (!pdata) | |
166 | return ERR_PTR(-ENOMEM); | |
167 | ||
168 | of_property_read_u32(np, "rotary-encoder,steps", &pdata->steps); | |
169 | of_property_read_u32(np, "linux,axis", &pdata->axis); | |
170 | ||
171 | pdata->gpio_a = of_get_gpio_flags(np, 0, &flags); | |
172 | pdata->inverted_a = flags & OF_GPIO_ACTIVE_LOW; | |
173 | ||
174 | pdata->gpio_b = of_get_gpio_flags(np, 1, &flags); | |
175 | pdata->inverted_b = flags & OF_GPIO_ACTIVE_LOW; | |
176 | ||
177 | pdata->relative_axis = !!of_get_property(np, | |
178 | "rotary-encoder,relative-axis", NULL); | |
179 | pdata->rollover = !!of_get_property(np, | |
180 | "rotary-encoder,rollover", NULL); | |
181 | pdata->half_period = !!of_get_property(np, | |
182 | "rotary-encoder,half-period", NULL); | |
183 | ||
184 | return pdata; | |
185 | } | |
186 | #else | |
187 | static inline struct rotary_encoder_platform_data * | |
188 | rotary_encoder_parse_dt(struct device *dev) | |
189 | { | |
190 | return NULL; | |
191 | } | |
192 | #endif | |
193 | ||
5298cc4c | 194 | static int rotary_encoder_probe(struct platform_device *pdev) |
73969ff0 | 195 | { |
ce919537 DT |
196 | struct device *dev = &pdev->dev; |
197 | const struct rotary_encoder_platform_data *pdata = dev_get_platdata(dev); | |
73969ff0 DM |
198 | struct rotary_encoder *encoder; |
199 | struct input_dev *input; | |
e70bdd41 | 200 | irq_handler_t handler; |
73969ff0 DM |
201 | int err; |
202 | ||
06ee3d3c | 203 | if (!pdata) { |
80c99bcd DM |
204 | pdata = rotary_encoder_parse_dt(dev); |
205 | if (IS_ERR(pdata)) | |
206 | return PTR_ERR(pdata); | |
207 | ||
208 | if (!pdata) { | |
209 | dev_err(dev, "missing platform data\n"); | |
210 | return -EINVAL; | |
211 | } | |
73969ff0 DM |
212 | } |
213 | ||
214 | encoder = kzalloc(sizeof(struct rotary_encoder), GFP_KERNEL); | |
215 | input = input_allocate_device(); | |
216 | if (!encoder || !input) { | |
73969ff0 DM |
217 | err = -ENOMEM; |
218 | goto exit_free_mem; | |
219 | } | |
220 | ||
221 | encoder->input = input; | |
222 | encoder->pdata = pdata; | |
73969ff0 | 223 | |
73969ff0 DM |
224 | input->name = pdev->name; |
225 | input->id.bustype = BUS_HOST; | |
80c99bcd | 226 | input->dev.parent = dev; |
bd3ce655 HS |
227 | |
228 | if (pdata->relative_axis) { | |
229 | input->evbit[0] = BIT_MASK(EV_REL); | |
230 | input->relbit[0] = BIT_MASK(pdata->axis); | |
231 | } else { | |
232 | input->evbit[0] = BIT_MASK(EV_ABS); | |
233 | input_set_abs_params(encoder->input, | |
234 | pdata->axis, 0, pdata->steps, 0, 1); | |
235 | } | |
73969ff0 | 236 | |
73969ff0 | 237 | /* request the GPIOs */ |
429a34d7 | 238 | err = gpio_request_one(pdata->gpio_a, GPIOF_IN, dev_name(dev)); |
73969ff0 | 239 | if (err) { |
429a34d7 | 240 | dev_err(dev, "unable to request GPIO %d\n", pdata->gpio_a); |
80c99bcd | 241 | goto exit_free_mem; |
73969ff0 DM |
242 | } |
243 | ||
429a34d7 | 244 | err = gpio_request_one(pdata->gpio_b, GPIOF_IN, dev_name(dev)); |
5deeac99 | 245 | if (err) { |
429a34d7 | 246 | dev_err(dev, "unable to request GPIO %d\n", pdata->gpio_b); |
5deeac99 AC |
247 | goto exit_free_gpio_a; |
248 | } | |
249 | ||
a78769b8 DM |
250 | encoder->irq_a = gpio_to_irq(pdata->gpio_a); |
251 | encoder->irq_b = gpio_to_irq(pdata->gpio_b); | |
252 | ||
73969ff0 | 253 | /* request the IRQs */ |
e70bdd41 JH |
254 | if (pdata->half_period) { |
255 | handler = &rotary_encoder_half_period_irq; | |
256 | encoder->last_stable = rotary_encoder_get_state(pdata); | |
257 | } else { | |
258 | handler = &rotary_encoder_irq; | |
259 | } | |
260 | ||
261 | err = request_irq(encoder->irq_a, handler, | |
e0d5f4c3 | 262 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, |
73969ff0 DM |
263 | DRV_NAME, encoder); |
264 | if (err) { | |
429a34d7 | 265 | dev_err(dev, "unable to request IRQ %d\n", encoder->irq_a); |
73969ff0 DM |
266 | goto exit_free_gpio_b; |
267 | } | |
268 | ||
e70bdd41 | 269 | err = request_irq(encoder->irq_b, handler, |
e0d5f4c3 | 270 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, |
73969ff0 DM |
271 | DRV_NAME, encoder); |
272 | if (err) { | |
429a34d7 | 273 | dev_err(dev, "unable to request IRQ %d\n", encoder->irq_b); |
73969ff0 DM |
274 | goto exit_free_irq_a; |
275 | } | |
276 | ||
80c99bcd DM |
277 | err = input_register_device(input); |
278 | if (err) { | |
279 | dev_err(dev, "failed to register input device\n"); | |
280 | goto exit_free_irq_b; | |
281 | } | |
282 | ||
73969ff0 DM |
283 | platform_set_drvdata(pdev, encoder); |
284 | ||
285 | return 0; | |
286 | ||
80c99bcd DM |
287 | exit_free_irq_b: |
288 | free_irq(encoder->irq_b, encoder); | |
73969ff0 DM |
289 | exit_free_irq_a: |
290 | free_irq(encoder->irq_a, encoder); | |
291 | exit_free_gpio_b: | |
292 | gpio_free(pdata->gpio_b); | |
293 | exit_free_gpio_a: | |
294 | gpio_free(pdata->gpio_a); | |
73969ff0 DM |
295 | exit_free_mem: |
296 | input_free_device(input); | |
297 | kfree(encoder); | |
80c99bcd DM |
298 | if (!dev_get_platdata(&pdev->dev)) |
299 | kfree(pdata); | |
300 | ||
73969ff0 DM |
301 | return err; |
302 | } | |
303 | ||
e2619cf7 | 304 | static int rotary_encoder_remove(struct platform_device *pdev) |
73969ff0 DM |
305 | { |
306 | struct rotary_encoder *encoder = platform_get_drvdata(pdev); | |
ce919537 | 307 | const struct rotary_encoder_platform_data *pdata = encoder->pdata; |
73969ff0 DM |
308 | |
309 | free_irq(encoder->irq_a, encoder); | |
310 | free_irq(encoder->irq_b, encoder); | |
311 | gpio_free(pdata->gpio_a); | |
312 | gpio_free(pdata->gpio_b); | |
80c99bcd | 313 | |
73969ff0 | 314 | input_unregister_device(encoder->input); |
73969ff0 DM |
315 | kfree(encoder); |
316 | ||
80c99bcd DM |
317 | if (!dev_get_platdata(&pdev->dev)) |
318 | kfree(pdata); | |
319 | ||
73969ff0 DM |
320 | return 0; |
321 | } | |
322 | ||
323 | static struct platform_driver rotary_encoder_driver = { | |
324 | .probe = rotary_encoder_probe, | |
1cb0aa88 | 325 | .remove = rotary_encoder_remove, |
73969ff0 DM |
326 | .driver = { |
327 | .name = DRV_NAME, | |
328 | .owner = THIS_MODULE, | |
80c99bcd | 329 | .of_match_table = of_match_ptr(rotary_encoder_of_match), |
73969ff0 DM |
330 | } |
331 | }; | |
840a746b | 332 | module_platform_driver(rotary_encoder_driver); |
73969ff0 DM |
333 | |
334 | MODULE_ALIAS("platform:" DRV_NAME); | |
335 | MODULE_DESCRIPTION("GPIO rotary encoder driver"); | |
e70bdd41 | 336 | MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>, Johan Hovold"); |
73969ff0 | 337 | MODULE_LICENSE("GPL v2"); |