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