aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/misc/gpio_matrix.c
diff options
context:
space:
mode:
authorJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
committerJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
commitfcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch)
treea57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/input/misc/gpio_matrix.c
parent8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff)
Added missing tegra files.HEADmaster
Diffstat (limited to 'drivers/input/misc/gpio_matrix.c')
-rw-r--r--drivers/input/misc/gpio_matrix.c441
1 files changed, 441 insertions, 0 deletions
diff --git a/drivers/input/misc/gpio_matrix.c b/drivers/input/misc/gpio_matrix.c
new file mode 100644
index 00000000000..eaa9e89d473
--- /dev/null
+++ b/drivers/input/misc/gpio_matrix.c
@@ -0,0 +1,441 @@
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>
17#include <linux/gpio.h>
18#include <linux/gpio_event.h>
19#include <linux/hrtimer.h>
20#include <linux/interrupt.h>
21#include <linux/slab.h>
22#include <linux/wakelock.h>
23
24struct gpio_kp {
25 struct gpio_event_input_devs *input_devs;
26 struct gpio_event_matrix_info *keypad_info;
27 struct hrtimer timer;
28 struct wake_lock wake_lock;
29 int current_output;
30 unsigned int use_irq:1;
31 unsigned int key_state_changed:1;
32 unsigned int last_key_state_changed:1;
33 unsigned int some_keys_pressed:2;
34 unsigned int disabled_irq:1;
35 unsigned long keys_pressed[0];
36};
37
38static void clear_phantom_key(struct gpio_kp *kp, int out, int in)
39{
40 struct gpio_event_matrix_info *mi = kp->keypad_info;
41 int key_index = out * mi->ninputs + in;
42 unsigned short keyentry = mi->keymap[key_index];
43 unsigned short keycode = keyentry & MATRIX_KEY_MASK;
44 unsigned short dev = keyentry >> MATRIX_CODE_BITS;
45
46 if (!test_bit(keycode, kp->input_devs->dev[dev]->key)) {
47 if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS)
48 pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) "
49 "cleared\n", keycode, out, in,
50 mi->output_gpios[out], mi->input_gpios[in]);
51 __clear_bit(key_index, kp->keys_pressed);
52 } else {
53 if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS)
54 pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) "
55 "not cleared\n", keycode, out, in,
56 mi->output_gpios[out], mi->input_gpios[in]);
57 }
58}
59
60static int restore_keys_for_input(struct gpio_kp *kp, int out, int in)
61{
62 int rv = 0;
63 int key_index;
64
65 key_index = out * kp->keypad_info->ninputs + in;
66 while (out < kp->keypad_info->noutputs) {
67 if (test_bit(key_index, kp->keys_pressed)) {
68 rv = 1;
69 clear_phantom_key(kp, out, in);
70 }
71 key_index += kp->keypad_info->ninputs;
72 out++;
73 }
74 return rv;
75}
76
77static void remove_phantom_keys(struct gpio_kp *kp)
78{
79 int out, in, inp;
80 int key_index;
81
82 if (kp->some_keys_pressed < 3)
83 return;
84
85 for (out = 0; out < kp->keypad_info->noutputs; out++) {
86 inp = -1;
87 key_index = out * kp->keypad_info->ninputs;
88 for (in = 0; in < kp->keypad_info->ninputs; in++, key_index++) {
89 if (test_bit(key_index, kp->keys_pressed)) {
90 if (inp == -1) {
91 inp = in;
92 continue;
93 }
94 if (inp >= 0) {
95 if (!restore_keys_for_input(kp, out + 1,
96 inp))
97 break;
98 clear_phantom_key(kp, out, inp);
99 inp = -2;
100 }
101 restore_keys_for_input(kp, out, in);
102 }
103 }
104 }
105}
106
107static void report_key(struct gpio_kp *kp, int key_index, int out, int in)
108{
109 struct gpio_event_matrix_info *mi = kp->keypad_info;
110 int pressed = test_bit(key_index, kp->keys_pressed);
111 unsigned short keyentry = mi->keymap[key_index];
112 unsigned short keycode = keyentry & MATRIX_KEY_MASK;
113 unsigned short dev = keyentry >> MATRIX_CODE_BITS;
114
115 if (pressed != test_bit(keycode, kp->input_devs->dev[dev]->key)) {
116 if (keycode == KEY_RESERVED) {
117 if (mi->flags & GPIOKPF_PRINT_UNMAPPED_KEYS)
118 pr_info("gpiomatrix: unmapped key, %d-%d "
119 "(%d-%d) changed to %d\n",
120 out, in, mi->output_gpios[out],
121 mi->input_gpios[in], pressed);
122 } else {
123 if (mi->flags & GPIOKPF_PRINT_MAPPED_KEYS)
124 pr_info("gpiomatrix: key %x, %d-%d (%d-%d) "
125 "changed to %d\n", keycode,
126 out, in, mi->output_gpios[out],
127 mi->input_gpios[in], pressed);
128 input_report_key(kp->input_devs->dev[dev], keycode, pressed);
129 }
130 }
131}
132
133static void report_sync(struct gpio_kp *kp)
134{
135 int i;
136
137 for (i = 0; i < kp->input_devs->count; i++)
138 input_sync(kp->input_devs->dev[i]);
139}
140
141static enum hrtimer_restart gpio_keypad_timer_func(struct hrtimer *timer)
142{
143 int out, in;
144 int key_index;
145 int gpio;
146 struct gpio_kp *kp = container_of(timer, struct gpio_kp, timer);
147 struct gpio_event_matrix_info *mi = kp->keypad_info;
148 unsigned gpio_keypad_flags = mi->flags;
149 unsigned polarity = !!(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH);
150
151 out = kp->current_output;
152 if (out == mi->noutputs) {
153 out = 0;
154 kp->last_key_state_changed = kp->key_state_changed;
155 kp->key_state_changed = 0;
156 kp->some_keys_pressed = 0;
157 } else {
158 key_index = out * mi->ninputs;
159 for (in = 0; in < mi->ninputs; in++, key_index++) {
160 gpio = mi->input_gpios[in];
161 if (gpio_get_value(gpio) ^ !polarity) {
162 if (kp->some_keys_pressed < 3)
163 kp->some_keys_pressed++;
164 kp->key_state_changed |= !__test_and_set_bit(
165 key_index, kp->keys_pressed);
166 } else
167 kp->key_state_changed |= __test_and_clear_bit(
168 key_index, kp->keys_pressed);
169 }
170 gpio = mi->output_gpios[out];
171 if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
172 gpio_set_value(gpio, !polarity);
173 else
174 gpio_direction_input(gpio);
175 out++;
176 }
177 kp->current_output = out;
178 if (out < mi->noutputs) {
179 gpio = mi->output_gpios[out];
180 if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
181 gpio_set_value(gpio, polarity);
182 else
183 gpio_direction_output(gpio, polarity);
184 hrtimer_start(timer, mi->settle_time, HRTIMER_MODE_REL);
185 return HRTIMER_NORESTART;
186 }
187 if (gpio_keypad_flags & GPIOKPF_DEBOUNCE) {
188 if (kp->key_state_changed) {
189 hrtimer_start(&kp->timer, mi->debounce_delay,
190 HRTIMER_MODE_REL);
191 return HRTIMER_NORESTART;
192 }
193 kp->key_state_changed = kp->last_key_state_changed;
194 }
195 if (kp->key_state_changed) {
196 if (gpio_keypad_flags & GPIOKPF_REMOVE_SOME_PHANTOM_KEYS)
197 remove_phantom_keys(kp);
198 key_index = 0;
199 for (out = 0; out < mi->noutputs; out++)
200 for (in = 0; in < mi->ninputs; in++, key_index++)
201 report_key(kp, key_index, out, in);
202 report_sync(kp);