]>
Commit | Line | Data |
---|---|---|
420818f9 AH |
1 | /* drivers/input/misc/gpio_matrix.c |
2 | * | |
3 | * Copyright (C) 2007 Google, Inc. | |
4 | * | |
5 | * This software is licensed under the terms of the GNU General Public | |
6 | * License version 2, as published by the Free Software Foundation, and | |
7 | * may be copied, distributed, and modified under those terms. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | */ | |
15 | ||
16 | #include <linux/kernel.h> | |
5a0e3ad6 | 17 | #include <linux/slab.h> |
420818f9 AH |
18 | #include <linux/gpio.h> |
19 | #include <linux/gpio_event.h> | |
20 | #include <linux/hrtimer.h> | |
21 | #include <linux/interrupt.h> | |
420818f9 AH |
22 | |
23 | struct gpio_kp { | |
24 | struct input_dev *input_dev; | |
25 | struct gpio_event_matrix_info *keypad_info; | |
26 | struct hrtimer timer; | |
420818f9 AH |
27 | int current_output; |
28 | unsigned int use_irq:1; | |
29 | unsigned int key_state_changed:1; | |
30 | unsigned int last_key_state_changed:1; | |
31 | unsigned int some_keys_pressed:2; | |
32 | unsigned long keys_pressed[0]; | |
33 | }; | |
34 | ||
35 | static void clear_phantom_key(struct gpio_kp *kp, int out, int in) | |
36 | { | |
37 | struct gpio_event_matrix_info *mi = kp->keypad_info; | |
38 | int key_index = out * mi->ninputs + in; | |
39 | unsigned short keycode = mi->keymap[key_index];; | |
40 | ||
41 | if (!test_bit(keycode, kp->input_dev->key)) { | |
42 | if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS) | |
43 | pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) " | |
44 | "cleared\n", keycode, out, in, | |
45 | mi->output_gpios[out], mi->input_gpios[in]); | |
46 | __clear_bit(key_index, kp->keys_pressed); | |
47 | } else { | |
48 | if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS) | |
49 | pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) " | |
50 | "not cleared\n", keycode, out, in, | |
51 | mi->output_gpios[out], mi->input_gpios[in]); | |
52 | } | |
53 | } | |
54 | ||
55 | static int restore_keys_for_input(struct gpio_kp *kp, int out, int in) | |
56 | { | |
57 | int rv = 0; | |
58 | int key_index; | |
59 | ||
60 | key_index = out * kp->keypad_info->ninputs + in; | |
61 | while (out < kp->keypad_info->noutputs) { | |
62 | if (test_bit(key_index, kp->keys_pressed)) { | |
63 | rv = 1; | |
64 | clear_phantom_key(kp, out, in); | |
65 | } | |
66 | key_index += kp->keypad_info->ninputs; | |
67 | out++; | |
68 | } | |
69 | return rv; | |
70 | } | |
71 | ||
72 | static void remove_phantom_keys(struct gpio_kp *kp) | |
73 | { | |
74 | int out, in, inp; | |
75 | int key_index; | |
76 | ||
77 | if (kp->some_keys_pressed < 3) | |
78 | return; | |
79 | ||
80 | for (out = 0; out < kp->keypad_info->noutputs; out++) { | |
81 | inp = -1; | |
82 | key_index = out * kp->keypad_info->ninputs; | |
83 | for (in = 0; in < kp->keypad_info->ninputs; in++, key_index++) { | |
84 | if (test_bit(key_index, kp->keys_pressed)) { | |
85 | if (inp == -1) { | |
86 | inp = in; | |
87 | continue; | |
88 | } | |
89 | if (inp >= 0) { | |
90 | if (!restore_keys_for_input(kp, out + 1, | |
91 | inp)) | |
92 | break; | |
93 | clear_phantom_key(kp, out, inp); | |
94 | inp = -2; | |
95 | } | |
96 | restore_keys_for_input(kp, out, in); | |
97 | } | |
98 | } | |
99 | } | |
100 | } | |
101 | ||
102 | static void report_key(struct gpio_kp *kp, int key_index, int out, int in) | |
103 | { | |
104 | struct gpio_event_matrix_info *mi = kp->keypad_info; | |
105 | int pressed = test_bit(key_index, kp->keys_pressed); | |
106 | unsigned short keycode = mi->keymap[key_index]; | |
107 | if (pressed != test_bit(keycode, kp->input_dev->key)) { | |
108 | if (keycode == KEY_RESERVED) { | |
109 | if (mi->flags & GPIOKPF_PRINT_UNMAPPED_KEYS) | |
110 | pr_info("gpiomatrix: unmapped key, %d-%d " | |
111 | "(%d-%d) changed to %d\n", | |
112 | out, in, mi->output_gpios[out], | |
113 | mi->input_gpios[in], pressed); | |
114 | } else { | |
115 | if (mi->flags & GPIOKPF_PRINT_MAPPED_KEYS) | |
116 | pr_info("gpiomatrix: key %x, %d-%d (%d-%d) " | |
117 | "changed to %d\n", keycode, | |
118 | out, in, mi->output_gpios[out], | |
119 | mi->input_gpios[in], pressed); | |
120 | input_report_key(kp->input_dev, keycode, pressed); | |
121 | } | |
122 | } | |
123 | } | |
124 | ||
125 | static enum hrtimer_restart gpio_keypad_timer_func(struct hrtimer *timer) | |
126 | { | |
127 | int out, in; | |
128 | int key_index; | |
129 | int gpio; | |
130 | struct gpio_kp *kp = container_of(timer, struct gpio_kp, timer); | |
131 | struct gpio_event_matrix_info *mi = kp->keypad_info; | |
132 | unsigned gpio_keypad_flags = mi->flags; | |
133 | unsigned polarity = !!(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH); | |
134 | ||
135 | out = kp->current_output; | |
136 | if (out == mi->noutputs) { | |
137 | out = 0; | |
138 | kp->last_key_state_changed = kp->key_state_changed; | |
139 | kp->key_state_changed = 0; | |
140 | kp->some_keys_pressed = 0; | |
141 | } else { | |
142 | key_index = out * mi->ninputs; | |
143 | for (in = 0; in < mi->ninputs; in++, key_index++) { | |
144 | gpio = mi->input_gpios[in]; | |
145 | if (gpio_get_value(gpio) ^ !polarity) { | |
146 | if (kp->some_keys_pressed < 3) | |
147 | kp->some_keys_pressed++; | |
148 | kp->key_state_changed |= !__test_and_set_bit( | |
149 | key_index, kp->keys_pressed); | |
150 | } else | |
151 | kp->key_state_changed |= __test_and_clear_bit( | |
152 | key_index, kp->keys_pressed); | |
153 | } | |
154 | gpio = mi->output_gpios[out]; | |
155 | if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) | |
156 | gpio_set_value(gpio, !polarity); | |
157 | else | |
158 | gpio_direction_input(gpio); | |
159 | out++; | |
160 | } | |
161 | kp->current_output = out; | |
162 | if (out < mi->noutputs) { | |
163 | gpio = mi->output_gpios[out]; | |
164 | if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) | |
165 | gpio_set_value(gpio, polarity); | |
166 | else | |
167 | gpio_direction_output(gpio, polarity); | |
168 | hrtimer_start(timer, mi->settle_time, HRTIMER_MODE_REL); | |
169 | return HRTIMER_NORESTART; | |
170 | } | |
171 | if (gpio_keypad_flags & GPIOKPF_DEBOUNCE) { | |
172 | if (kp->key_state_changed) { | |
173 | hrtimer_start(&kp->timer, mi->debounce_delay, | |
174 | HRTIMER_MODE_REL); | |
175 | return HRTIMER_NORESTART; | |
176 | } | |
177 | kp->key_state_changed = kp->last_key_state_changed; | |
178 | } | |
179 | if (kp->key_state_changed) { | |
180 | if (gpio_keypad_flags & GPIOKPF_REMOVE_SOME_PHANTOM_KEYS) | |
181 | remove_phantom_keys(kp); | |
182 | key_index = 0; | |
183 | for (out = 0; out < mi->noutputs; out++) | |
184 | for (in = 0; in < mi->ninputs; in++, key_index++) | |
185 | report_key(kp, key_index, out, in); | |
186 | } | |
187 | if (!kp->use_irq || kp->some_keys_pressed) { | |
188 | hrtimer_start(timer, mi->poll_time, HRTIMER_MODE_REL); | |
189 | return HRTIMER_NORESTART; | |
190 | } | |
191 | ||
192 | /* No keys are pressed, reenable interrupt */ | |
193 | for (out = 0; out < mi->noutputs; out++) { | |
194 | if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) | |
195 | gpio_set_value(mi->output_gpios[out], polarity); | |
196 | else | |
197 | gpio_direction_output(mi->output_gpios[out], polarity); | |
198 | } | |
199 | for (in = 0; in < mi->ninputs; in++) | |
200 | enable_irq(gpio_to_irq(mi->input_gpios[in])); | |
420818f9 AH |
201 | return HRTIMER_NORESTART; |
202 | } | |
203 | ||
204 | static irqreturn_t gpio_keypad_irq_handler(int irq_in, void *dev_id) | |
205 | { | |
206 | int i; | |
207 | struct gpio_kp *kp = dev_id; | |
208 | struct gpio_event_matrix_info *mi = kp->keypad_info; | |
209 | unsigned gpio_keypad_flags = mi->flags; | |
210 | ||
211 | if (!kp->use_irq) /* ignore interrupt while registering the handler */ | |
212 | return IRQ_HANDLED; | |
213 | ||
214 | for (i = 0; i < mi->ninputs; i++) | |
215 | disable_irq(gpio_to_irq(mi->input_gpios[i])); | |
216 | for (i = 0; i < mi->noutputs; i++) { | |
217 | if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) | |
218 | gpio_set_value(mi->output_gpios[i], | |
219 | !(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH)); | |
220 | else | |
221 | gpio_direction_input(mi->output_gpios[i]); | |
222 | } | |
420818f9 AH |
223 | hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); |
224 | return IRQ_HANDLED; | |
225 | } | |
226 | ||
227 | static int gpio_keypad_request_irqs(struct gpio_kp *kp) | |
228 | { | |
229 | int i; | |
230 | int err; | |
231 | unsigned int irq; | |
232 | unsigned long request_flags; | |
233 | struct gpio_event_matrix_info *mi = kp->keypad_info; | |
234 | ||
235 | switch (mi->flags & (GPIOKPF_ACTIVE_HIGH|GPIOKPF_LEVEL_TRIGGERED_IRQ)) { | |
236 | default: | |
237 | request_flags = IRQF_TRIGGER_FALLING; | |
238 | break; | |
239 | case GPIOKPF_ACTIVE_HIGH: | |
240 | request_flags = IRQF_TRIGGER_RISING; | |
241 | break; | |
242 | case GPIOKPF_LEVEL_TRIGGERED_IRQ: | |
243 | request_flags = IRQF_TRIGGER_LOW; | |
244 | break; | |
245 | case GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_ACTIVE_HIGH: | |
246 | request_flags = IRQF_TRIGGER_HIGH; | |
247 | break; | |
248 | } | |
249 | ||
250 | for (i = 0; i < mi->ninputs; i++) { | |
251 | err = irq = gpio_to_irq(mi->input_gpios[i]); | |
252 | if (err < 0) | |
253 | goto err_gpio_get_irq_num_failed; | |
254 | err = request_irq(irq, gpio_keypad_irq_handler, request_flags, | |
255 | "gpio_kp", kp); | |
256 | if (err) { | |
257 | pr_err("gpiomatrix: request_irq failed for input %d, " | |
258 | "irq %d\n", mi->input_gpios[i], irq); | |
259 | goto err_request_irq_failed; | |
260 | } | |
261 | err = set_irq_wake(irq, 1); | |
262 | if (err) { | |
263 | pr_err("gpiomatrix: set_irq_wake failed for input %d, " | |
264 | "irq %d\n", mi->input_gpios[i], irq); | |
265 | } | |
266 | disable_irq(irq); | |
267 | } | |
268 | return 0; | |
269 | ||
270 | for (i = mi->noutputs - 1; i >= 0; i--) { | |
271 | free_irq(gpio_to_irq(mi->input_gpios[i]), kp); | |
272 | err_request_irq_failed: | |
273 | err_gpio_get_irq_num_failed: | |
274 | ; | |
275 | } | |
276 | return err; | |
277 | } | |
278 | ||
279 | int gpio_event_matrix_func(struct input_dev *input_dev, | |
280 | struct gpio_event_info *info, void **data, int func) | |
281 | { | |
282 | int i; | |
283 | int err; | |
284 | int key_count; | |
285 | struct gpio_kp *kp; | |
286 | struct gpio_event_matrix_info *mi; | |
287 | ||
288 | mi = container_of(info, struct gpio_event_matrix_info, info); | |
289 | if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) { | |
290 | /* TODO: disable scanning */ | |
291 | return 0; | |
292 | } | |
293 | ||
294 | if (func == GPIO_EVENT_FUNC_INIT) { | |
295 | if (mi->keymap == NULL || | |
296 | mi->input_gpios == NULL || | |
297 | mi->output_gpios == NULL) { | |
298 | err = -ENODEV; | |
299 | pr_err("gpiomatrix: Incomplete pdata\n"); | |
300 | goto err_invalid_platform_data; | |
301 | } | |
302 | key_count = mi->ninputs * mi->noutputs; | |
303 | ||
304 | *data = kp = kzalloc(sizeof(*kp) + sizeof(kp->keys_pressed[0]) * | |
305 | BITS_TO_LONGS(key_count), GFP_KERNEL); | |
306 | if (kp == NULL) { | |
307 | err = -ENOMEM; | |
308 | pr_err("gpiomatrix: Failed to allocate private data\n"); | |
309 | goto err_kp_alloc_failed; | |
310 | } | |
311 | kp->input_dev = input_dev; | |
312 | kp->keypad_info = mi; | |
313 | set_bit(EV_KEY, input_dev->evbit); | |
314 | for (i = 0; i < key_count; i++) { | |
315 | if (mi->keymap[i]) | |
316 | set_bit(mi->keymap[i] & KEY_MAX, | |
317 | input_dev->keybit); | |
318 | } | |
319 | ||
320 | for (i = 0; i < mi->noutputs; i++) { | |
321 | if (gpio_cansleep(mi->output_gpios[i])) { | |
322 | pr_err("gpiomatrix: unsupported output gpio %d," | |
323 | " can sleep\n", mi->output_gpios[i]); | |
324 | err = -EINVAL; | |
325 | goto err_request_output_gpio_failed; | |
326 | } | |
327 | err = gpio_request(mi->output_gpios[i], "gpio_kp_out"); | |
328 | if (err) { | |
329 | pr_err("gpiomatrix: gpio_request failed for " | |
330 | "output %d\n", mi->output_gpios[i]); | |
331 | goto err_request_output_gpio_failed; | |
332 | } | |
333 | if (mi->flags & GPIOKPF_DRIVE_INACTIVE) | |
334 | err = gpio_direction_output(mi->output_gpios[i], | |
335 | !(mi->flags & GPIOKPF_ACTIVE_HIGH)); | |
336 | else | |
337 | err = gpio_direction_input(mi->output_gpios[i]); | |
338 | if (err) { | |
339 | pr_err("gpiomatrix: gpio_configure failed for " | |
340 | "output %d\n", mi->output_gpios[i]); | |
341 | goto err_output_gpio_configure_failed; | |
342 | } | |
343 | } | |
344 | for (i = 0; i < mi->ninputs; i++) { | |
345 | err = gpio_request(mi->input_gpios[i], "gpio_kp_in"); | |
346 | if (err) { | |
347 | pr_err("gpiomatrix: gpio_request failed for " | |
348 | "input %d\n", mi->input_gpios[i]); | |
349 | goto err_request_input_gpio_failed; | |
350 | } | |
351 | err = gpio_direction_input(mi->input_gpios[i]); | |
352 | if (err) { | |
353 | pr_err("gpiomatrix: gpio_direction_input failed" | |
354 | " for input %d\n", mi->input_gpios[i]); | |
355 | goto err_gpio_direction_input_failed; | |
356 | } | |
357 | } | |
358 | kp->current_output = mi->noutputs; | |
359 | kp->key_state_changed = 1; | |
360 | ||
361 | hrtimer_init(&kp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | |
362 | kp->timer.function = gpio_keypad_timer_func; | |
420818f9 AH |
363 | err = gpio_keypad_request_irqs(kp); |
364 | kp->use_irq = err == 0; | |
365 | ||
366 | pr_info("GPIO Matrix Keypad Driver: Start keypad matrix for %s " | |
367 | "in %s mode\n", input_dev->name, | |
368 | kp->use_irq ? "interrupt" : "polling"); | |
369 | ||
420818f9 AH |
370 | hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); |
371 | ||
372 | return 0; | |
373 | } | |
374 | ||
375 | err = 0; | |
376 | kp = *data; | |
377 | ||
378 | if (kp->use_irq) | |
379 | for (i = mi->noutputs - 1; i >= 0; i--) | |
380 | free_irq(gpio_to_irq(mi->input_gpios[i]), kp); | |
381 | ||
382 | hrtimer_cancel(&kp->timer); | |
420818f9 AH |
383 | for (i = mi->noutputs - 1; i >= 0; i--) { |
384 | err_gpio_direction_input_failed: | |
385 | gpio_free(mi->input_gpios[i]); | |
386 | err_request_input_gpio_failed: | |
387 | ; | |
388 | } | |
389 | for (i = mi->noutputs - 1; i >= 0; i--) { | |
390 | err_output_gpio_configure_failed: | |
391 | gpio_free(mi->output_gpios[i]); | |
392 | err_request_output_gpio_failed: | |
393 | ; | |
394 | } | |
395 | kfree(kp); | |
396 | err_kp_alloc_failed: | |
397 | err_invalid_platform_data: | |
398 | return err; | |
399 | } |