1 /*
2 * Copyright 2019 Intel Corporation
3 * Copyright 2022 Nuvoton Technology Corporation.
4 * Copyright 2023 Google LLC
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <zephyr/device.h>
10 #include <zephyr/input/input.h>
11 #include <zephyr/input/input_kbd_matrix.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/logging/log.h>
15 #include <zephyr/sys/util.h>
16
17 LOG_MODULE_REGISTER(input_kbd_matrix, CONFIG_INPUT_LOG_LEVEL);
18
input_kbd_matrix_poll_start(const struct device * dev)19 void input_kbd_matrix_poll_start(const struct device *dev)
20 {
21 struct input_kbd_matrix_common_data *data = dev->data;
22
23 k_sem_give(&data->poll_lock);
24 }
25
input_kbd_matrix_ghosting(const struct device * dev)26 static bool input_kbd_matrix_ghosting(const struct device *dev)
27 {
28 const struct input_kbd_matrix_common_config *cfg = dev->config;
29 const kbd_row_t *state = cfg->matrix_new_state;
30
31 /*
32 * Matrix keyboard designs are suceptible to ghosting.
33 * An extra key appears to be pressed when 3 keys belonging to the same
34 * block are pressed. For example, in the following block:
35 *
36 * . . w . q .
37 * . . . . . .
38 * . . . . . .
39 * . . m . a .
40 *
41 * the key m would look as pressed if the user pressed keys w, q and a
42 * simultaneously. A block can also be formed, with not adjacent
43 * columns.
44 */
45 for (int c = 0; c < cfg->col_size; c++) {
46 if (!state[c]) {
47 continue;
48 }
49
50 for (int c_next = c + 1; c_next < cfg->col_size; c_next++) {
51 /*
52 * We AND the columns to detect a "block". This is an
53 * indication of ghosting, due to current flowing from
54 * a key which was never pressed. In our case, current
55 * flowing is a bit set to 1 as we flipped the bits
56 * when the matrix was scanned. Now we OR the colums
57 * using z&(z-1) which is non-zero only if z has more
58 * than one bit set.
59 */
60 kbd_row_t common_row_bits = state[c] & state[c_next];
61
62 if (common_row_bits & (common_row_bits - 1)) {
63 return true;
64 }
65 }
66 }
67
68 return false;
69 }
70
input_kbd_matrix_drive_column(const struct device * dev,int col)71 static void input_kbd_matrix_drive_column(const struct device *dev, int col)
72 {
73 const struct input_kbd_matrix_common_config *cfg = dev->config;
74 const struct input_kbd_matrix_api *api = cfg->api;
75
76 api->drive_column(dev, col);
77
78 #ifdef CONFIG_INPUT_KBD_DRIVE_COLUMN_HOOK
79 input_kbd_matrix_drive_column_hook(dev, col);
80 #endif
81 }
82
input_kbd_matrix_scan(const struct device * dev)83 static bool input_kbd_matrix_scan(const struct device *dev)
84 {
85 const struct input_kbd_matrix_common_config *cfg = dev->config;
86 const struct input_kbd_matrix_api *api = cfg->api;
87 kbd_row_t row;
88 kbd_row_t key_event = 0U;
89
90 for (int col = 0; col < cfg->col_size; col++) {
91 input_kbd_matrix_drive_column(dev, col);
92
93 /* Allow the matrix to stabilize before reading it */
94 k_busy_wait(cfg->settle_time_us);
95
96 row = api->read_row(dev);
97
98 if (cfg->actual_key_mask != NULL) {
99 row &= cfg->actual_key_mask[col];
100 }
101
102 cfg->matrix_new_state[col] = row;
103 key_event |= row;
104 }
105
106 input_kbd_matrix_drive_column(dev, INPUT_KBD_MATRIX_COLUMN_DRIVE_NONE);
107
108 return key_event != 0U;
109 }
110
input_kbd_matrix_update_state(const struct device * dev)111 static void input_kbd_matrix_update_state(const struct device *dev)
112 {
113 const struct input_kbd_matrix_common_config *cfg = dev->config;
114 struct input_kbd_matrix_common_data *data = dev->data;
115 kbd_row_t *matrix_new_state = cfg->matrix_new_state;
116 uint32_t cycles_now;
117 kbd_row_t row_changed;
118 kbd_row_t deb_col;
119
120 cycles_now = k_cycle_get_32();
121
122 data->scan_clk_cycle[data->scan_cycles_idx] = cycles_now;
123
124 /*
125 * The intent of this loop is to gather information related to key
126 * changes.
127 */
128 for (int c = 0; c < cfg->col_size; c++) {
129 /* Check if there was an update from the previous scan */
130 row_changed = matrix_new_state[c] ^ cfg->matrix_previous_state[c];
131
132 if (!row_changed) {
133 continue;
134 }
135
136 for (int r = 0; r < cfg->row_size; r++) {
137 uint8_t cyc_idx = c * cfg->row_size + r;
138
139 /*
140 * Index all they keys that changed for each row in
141 * order to debounce each key in terms of it
142 */
143 if (row_changed & BIT(r)) {
144 cfg->scan_cycle_idx[cyc_idx] = data->scan_cycles_idx;
145 }
146 }
147
148 cfg->matrix_unstable_state[c] |= row_changed;
149 cfg->matrix_previous_state[c] = matrix_new_state[c];
150 }
151
152 for (int c = 0; c < cfg->col_size; c++) {
153 deb_col = cfg->matrix_unstable_state[c];
154
155 if (!deb_col) {
156 continue;
157 }
158
159 /* Debouncing for each row key occurs here */
160 for (int r = 0; r < cfg->row_size; r++) {
161 kbd_row_t mask = BIT(r);
162 kbd_row_t row_bit = matrix_new_state[c] & mask;
163
164 /* Continue if we already debounce a key */
165 if (!(deb_col & mask)) {
166 continue;
167 }
168
169 uint8_t cyc_idx = c * cfg->row_size + r;
170 uint8_t scan_cyc_idx = cfg->scan_cycle_idx[cyc_idx];
171 uint32_t scan_clk_cycle = data->scan_clk_cycle[scan_cyc_idx];
172
173 /* Convert the clock cycle differences to usec */
174 uint32_t deb_t_us = k_cyc_to_us_floor32(cycles_now - scan_clk_cycle);
175
176 /* Does the key requires more time to be debounced? */
177 if (deb_t_us < (row_bit ? cfg->debounce_down_us : cfg->debounce_up_us)) {
178 /* Need more time to debounce */
179 continue;
180 }
181
182 cfg->matrix_unstable_state[c] &= ~mask;
183
184 /* Check if there was a change in the stable state */
185 if ((cfg->matrix_stable_state[c] & mask) == row_bit) {
186 /* Key state did not change */
187 continue;
188 }
189
190 /*
191 * The current row has been debounced, therefore update
192 * the stable state. Then, proceed to notify the
193 * application about the keys pressed.
194 */
195 cfg->matrix_stable_state[c] ^= mask;
196
197 input_report_abs(dev, INPUT_ABS_X, c, false, K_FOREVER);
198 input_report_abs(dev, INPUT_ABS_Y, r, false, K_FOREVER);
199 input_report_key(dev, INPUT_BTN_TOUCH, row_bit, true, K_FOREVER);
200 }
201 }
202
203 data->scan_cycles_idx = (data->scan_cycles_idx + 1) % INPUT_KBD_MATRIX_SCAN_OCURRENCES;
204 }
205
input_kbd_matrix_check_key_events(const struct device * dev)206 static bool input_kbd_matrix_check_key_events(const struct device *dev)
207 {
208 const struct input_kbd_matrix_common_config *cfg = dev->config;
209 bool key_pressed;
210
211 /* Scan the matrix */
212 key_pressed = input_kbd_matrix_scan(dev);
213
214 for (int c = 0; c < cfg->col_size; c++) {
215 LOG_DBG("c=%2d u=" PRIkbdrow " p=" PRIkbdrow " n=" PRIkbdrow,
216 c,
217 cfg->matrix_unstable_state[c],
218 cfg->matrix_previous_state[c],
219 cfg->matrix_new_state[c]);
220 }
221
222 /* Abort if ghosting is detected */
223 if (cfg->ghostkey_check && input_kbd_matrix_ghosting(dev)) {
224 return key_pressed;
225 }
226
227 input_kbd_matrix_update_state(dev);
228
229 return key_pressed;
230 }
231
input_kbd_matrix_poll_timeout(const struct device * dev)232 static k_timepoint_t input_kbd_matrix_poll_timeout(const struct device *dev)
233 {
234 const struct input_kbd_matrix_common_config *cfg = dev->config;
235
236 if (cfg->poll_timeout_ms == 0) {
237 return sys_timepoint_calc(K_FOREVER);
238 }
239
240 return sys_timepoint_calc(K_MSEC(cfg->poll_timeout_ms));
241 }
242
input_kbd_matrix_poll(const struct device * dev)243 static void input_kbd_matrix_poll(const struct device *dev)
244 {
245 const struct input_kbd_matrix_common_config *cfg = dev->config;
246 k_timepoint_t poll_time_end;
247 uint32_t current_cycles;
248 uint32_t cycles_diff;
249 uint32_t wait_period_us;
250
251 poll_time_end = input_kbd_matrix_poll_timeout(dev);
252
253 while (true) {
254 uint32_t start_period_cycles = k_cycle_get_32();
255
256 if (input_kbd_matrix_check_key_events(dev)) {
257 poll_time_end = input_kbd_matrix_poll_timeout(dev);
258 } else if (sys_timepoint_expired(poll_time_end)) {
259 break;
260 }
261
262 /*
263 * Subtract the time invested from the sleep period in order to
264 * compensate for the time invested in debouncing a key
265 */
266 current_cycles = k_cycle_get_32();
267 cycles_diff = current_cycles - start_period_cycles;
268 wait_period_us = cfg->poll_period_us - k_cyc_to_us_floor32(cycles_diff);
269
270 wait_period_us = CLAMP(wait_period_us,
271 USEC_PER_MSEC, cfg->poll_period_us);
272
273 LOG_DBG("wait_period_us: %d", wait_period_us);
274
275 /* Allow other threads to run while we sleep */
276 k_usleep(wait_period_us);
277 }
278 }
279
input_kbd_matrix_polling_thread(void * arg1,void * unused2,void * unused3)280 static void input_kbd_matrix_polling_thread(void *arg1, void *unused2, void *unused3)
281 {
282 const struct device *dev = arg1;
283 const struct input_kbd_matrix_common_config *cfg = dev->config;
284 const struct input_kbd_matrix_api *api = cfg->api;
285 struct input_kbd_matrix_common_data *data = dev->data;
286
287 ARG_UNUSED(unused2);
288 ARG_UNUSED(unused3);
289
290 while (true) {
291 input_kbd_matrix_drive_column(dev, INPUT_KBD_MATRIX_COLUMN_DRIVE_ALL);
292 api->set_detect_mode(dev, true);
293
294 /* Check the rows again after enabling the interrupt to catch
295 * any potential press since the last read.
296 */
297 if (api->read_row(dev) != 0) {
298 input_kbd_matrix_poll_start(dev);
299 }
300
301 k_sem_take(&data->poll_lock, K_FOREVER);
302 LOG_DBG("scan start");
303
304 /* Disable interrupt of KSI pins and start polling */
305 api->set_detect_mode(dev, false);
306
307 input_kbd_matrix_poll(dev);
308 }
309 }
310
input_kbd_matrix_common_init(const struct device * dev)311 int input_kbd_matrix_common_init(const struct device *dev)
312 {
313 struct input_kbd_matrix_common_data *data = dev->data;
314
315 k_sem_init(&data->poll_lock, 0, 1);
316
317 k_thread_create(&data->thread, data->thread_stack,
318 K_KERNEL_STACK_SIZEOF(data->thread_stack),
319 input_kbd_matrix_polling_thread, (void *)dev, NULL, NULL,
320 CONFIG_INPUT_KBD_MATRIX_THREAD_PRIORITY, 0, K_NO_WAIT);
321
322 k_thread_name_set(&data->thread, dev->name);
323
324 return 0;
325 }
326
327 #if CONFIG_INPUT_KBD_ACTUAL_KEY_MASK_DYNAMIC
input_kbd_matrix_actual_key_mask_set(const struct device * dev,uint8_t row,uint8_t col,bool enabled)328 int input_kbd_matrix_actual_key_mask_set(const struct device *dev,
329 uint8_t row, uint8_t col, bool enabled)
330 {
331 const struct input_kbd_matrix_common_config *cfg = dev->config;
332
333 if (row >= cfg->row_size || col >= cfg->col_size) {
334 return -EINVAL;
335 }
336
337 if (cfg->actual_key_mask == NULL) {
338 LOG_WRN("actual-key-mask not defined for %s", dev->name);
339 return -EINVAL;
340 }
341
342 WRITE_BIT(cfg->actual_key_mask[col], row, enabled);
343
344 return 0;
345 }
346 #endif
347