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 | ||
152 | static struct rotary_encoder_platform_data * __devinit | |
153 | rotary_encoder_parse_dt(struct device *dev) | |
154 | { | |
155 | const struct of_device_id *of_id = | |
156 | of_match_device(rotary_encoder_of_match, dev); | |
157 | struct device_node *np = dev->of_node; | |
158 | struct rotary_encoder_platform_data *pdata; | |
159 | enum of_gpio_flags flags; | |
160 | ||
161 | if (!of_id || !np) | |
162 | return NULL; | |
163 | ||
164 | pdata = kzalloc(sizeof(struct rotary_encoder_platform_data), | |
165 | GFP_KERNEL); | |
166 | if (!pdata) | |
167 | return ERR_PTR(-ENOMEM); | |
168 | ||
169 | of_property_read_u32(np, "rotary-encoder,steps", &pdata->steps); | |
170 | of_property_read_u32(np, "linux,axis", &pdata->axis); | |
171 | ||
172 | pdata->gpio_a = of_get_gpio_flags(np, 0, &flags); | |
173 | pdata->inverted_a = flags & OF_GPIO_ACTIVE_LOW; | |
174 | ||
175 | pdata->gpio_b = of_get_gpio_flags(np, 1, &flags); | |
176 | pdata->inverted_b = flags & OF_GPIO_ACTIVE_LOW; | |
177 | ||
178 | pdata->relative_axis = !!of_get_property(np, | |
179 | "rotary-encoder,relative-axis", NULL); | |
180 | pdata->rollover = !!of_get_property(np, | |
181 | "rotary-encoder,rollover", NULL); | |
182 | pdata->half_period = !!of_get_property(np, | |
183 | "rotary-encoder,half-period", NULL); | |
184 | ||
185 | return pdata; | |
186 | } | |
187 | #else | |
188 | static inline struct rotary_encoder_platform_data * | |
189 | rotary_encoder_parse_dt(struct device *dev) | |
190 | { | |
191 | return NULL; | |
192 | } | |
193 | #endif | |
194 | ||
73969ff0 DM |
195 | static int __devinit rotary_encoder_probe(struct platform_device *pdev) |
196 | { | |
ce919537 DT |
197 | struct device *dev = &pdev->dev; |
198 | const struct rotary_encoder_platform_data *pdata = dev_get_platdata(dev); | |
73969ff0 DM |
199 | struct rotary_encoder *encoder; |
200 | struct input_dev *input; | |
e70bdd41 | 201 | irq_handler_t handler; |
73969ff0 DM |
202 | int err; |
203 | ||
06ee3d3c | 204 | if (!pdata) { |
80c99bcd DM |
205 | pdata = rotary_encoder_parse_dt(dev); |
206 | if (IS_ERR(pdata)) | |
207 | return PTR_ERR(pdata); | |
208 | ||
209 | if (!pdata) { | |
210 | dev_err(dev, "missing platform data\n"); | |
211 | return -EINVAL; | |
212 | } | |
73969ff0 DM |
213 | } |
214 | ||
215 | encoder = kzalloc(sizeof(struct rotary_encoder), GFP_KERNEL); | |
216 | input = input_allocate_device(); | |
217 | if (!encoder || !input) { | |
73969ff0 DM |
218 | err = -ENOMEM; |
219 | goto exit_free_mem; | |
220 | } | |
221 | ||
222 | encoder->input = input; | |
223 | encoder->pdata = pdata; | |
73969ff0 | 224 | |
73969ff0 DM |
225 | input->name = pdev->name; |
226 | input->id.bustype = BUS_HOST; | |
80c99bcd | 227 | input->dev.parent = dev; |
bd3ce655 HS |
228 | |
229 | if (pdata->relative_axis) { | |
230 | input->evbit[0] = BIT_MASK(EV_REL); | |
231 | input->relbit[0] = BIT_MASK(pdata->axis); | |
232 | } else { | |
233 | input->evbit[0] = BIT_MASK(EV_ABS); | |
234 | input_set_abs_params(encoder->input, | |
235 | pdata->axis, 0, pdata->steps, 0, 1); | |
236 | } | |
73969ff0 | 237 | |
73969ff0 | 238 | /* request the GPIOs */ |
429a34d7 | 239 | err = gpio_request_one(pdata->gpio_a, GPIOF_IN, dev_name(dev)); |
73969ff0 | 240 | if (err) { |
429a34d7 | 241 | dev_err(dev, "unable to request GPIO %d\n", pdata->gpio_a); |
80c99bcd | 242 | goto exit_free_mem; |
73969ff0 DM |
243 | } |
244 | ||
429a34d7 | 245 | err = gpio_request_one(pdata->gpio_b, GPIOF_IN, dev_name(dev)); |
5deeac99 | 246 | if (err) { |
429a34d7 | 247 | dev_err(dev, "unable to request GPIO %d\n", pdata->gpio_b); |
5deeac99 AC |
248 | goto exit_free_gpio_a; |
249 | } | |
250 | ||
a78769b8 DM |
251 | encoder->irq_a = gpio_to_irq(pdata->gpio_a); |
252 | encoder->irq_b = gpio_to_irq(pdata->gpio_b); | |
253 | ||
73969ff0 | 254 | /* request the IRQs */ |
e70bdd41 JH |
255 | if (pdata->half_period) { |
256 | handler = &rotary_encoder_half_period_irq; | |
257 | encoder->last_stable = rotary_encoder_get_state(pdata); | |
258 | } else { | |
259 | handler = &rotary_encoder_irq; | |
260 | } | |
261 | ||
262 | err = request_irq(encoder->irq_a, handler, | |
e0d5f4c3 | 263 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, |
73969ff0 DM |
264 | DRV_NAME, encoder); |
265 | if (err) { | |
429a34d7 | 266 | dev_err(dev, "unable to request IRQ %d\n", encoder->irq_a); |
73969ff0 DM |
267 | goto exit_free_gpio_b; |
268 | } | |
269 | ||
e70bdd41 | 270 | err = request_irq(encoder->irq_b, handler, |
e0d5f4c3 | 271 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, |
73969ff0 DM |
272 | DRV_NAME, encoder); |
273 | if (err) { | |
429a34d7 | 274 | dev_err(dev, "unable to request IRQ %d\n", encoder->irq_b); |
73969ff0 DM |
275 | goto exit_free_irq_a; |
276 | } | |
277 | ||
80c99bcd DM |
278 | err = input_register_device(input); |
279 | if (err) { | |
280 | dev_err(dev, "failed to register input device\n"); | |
281 | goto exit_free_irq_b; | |
282 | } | |
283 | ||
73969ff0 DM |
284 | platform_set_drvdata(pdev, encoder); |
285 | ||
286 | return 0; | |
287 | ||
80c99bcd DM |
288 | exit_free_irq_b: |
289 | free_irq(encoder->irq_b, encoder); | |
73969ff0 DM |
290 | exit_free_irq_a: |
291 | free_irq(encoder->irq_a, encoder); | |
292 | exit_free_gpio_b: | |
293 | gpio_free(pdata->gpio_b); | |
294 | exit_free_gpio_a: | |
295 | gpio_free(pdata->gpio_a); | |
73969ff0 DM |
296 | exit_free_mem: |
297 | input_free_device(input); | |
298 | kfree(encoder); | |
80c99bcd DM |
299 | if (!dev_get_platdata(&pdev->dev)) |
300 | kfree(pdata); | |
301 | ||
73969ff0 DM |
302 | return err; |
303 | } | |
304 | ||
305 | static int __devexit rotary_encoder_remove(struct platform_device *pdev) | |
306 | { | |
307 | struct rotary_encoder *encoder = platform_get_drvdata(pdev); | |
ce919537 | 308 | const struct rotary_encoder_platform_data *pdata = encoder->pdata; |
73969ff0 DM |
309 | |
310 | free_irq(encoder->irq_a, encoder); | |
311 | free_irq(encoder->irq_b, encoder); | |
312 | gpio_free(pdata->gpio_a); | |
313 | gpio_free(pdata->gpio_b); | |
80c99bcd | 314 | |
73969ff0 | 315 | input_unregister_device(encoder->input); |
73969ff0 DM |
316 | kfree(encoder); |
317 | ||
80c99bcd DM |
318 | if (!dev_get_platdata(&pdev->dev)) |
319 | kfree(pdata); | |
320 | ||
321 | platform_set_drvdata(pdev, NULL); | |
322 | ||
73969ff0 DM |
323 | return 0; |
324 | } | |
325 | ||
326 | static struct platform_driver rotary_encoder_driver = { | |
327 | .probe = rotary_encoder_probe, | |
1cb0aa88 | 328 | .remove = rotary_encoder_remove, |
73969ff0 DM |
329 | .driver = { |
330 | .name = DRV_NAME, | |
331 | .owner = THIS_MODULE, | |
80c99bcd | 332 | .of_match_table = of_match_ptr(rotary_encoder_of_match), |
73969ff0 DM |
333 | } |
334 | }; | |
840a746b | 335 | module_platform_driver(rotary_encoder_driver); |
73969ff0 DM |
336 | |
337 | MODULE_ALIAS("platform:" DRV_NAME); | |
338 | MODULE_DESCRIPTION("GPIO rotary encoder driver"); | |
e70bdd41 | 339 | MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>, Johan Hovold"); |
73969ff0 | 340 | MODULE_LICENSE("GPL v2"); |