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