Line data Source code
1 0 : /*
2 : * Copyright 2023 Google LLC
3 : *
4 : * SPDX-License-Identifier: Apache-2.0
5 : */
6 :
7 : #ifndef ZEPHYR_INCLUDE_INPUT_KBD_MATRIX_H_
8 : #define ZEPHYR_INCLUDE_INPUT_KBD_MATRIX_H_
9 :
10 : /**
11 : * @brief Keyboard Matrix API
12 : * @defgroup input_kbd_matrix Keyboard Matrix API
13 : * @ingroup io_interfaces
14 : * @{
15 : */
16 :
17 : #include <zephyr/device.h>
18 : #include <zephyr/kernel.h>
19 : #include <zephyr/pm/device.h>
20 : #include <zephyr/sys/atomic.h>
21 : #include <zephyr/sys/util.h>
22 : #include <zephyr/sys_clock.h>
23 : #include <zephyr/toolchain.h>
24 :
25 : /** Special drive_column argument for not driving any column */
26 1 : #define INPUT_KBD_MATRIX_COLUMN_DRIVE_NONE -1
27 :
28 : /** Special drive_column argument for driving all the columns */
29 1 : #define INPUT_KBD_MATRIX_COLUMN_DRIVE_ALL -2
30 :
31 : /** Number of tracked scan cycles */
32 1 : #define INPUT_KBD_MATRIX_SCAN_OCURRENCES 30U
33 :
34 : /** Row entry data type */
35 : #if CONFIG_INPUT_KBD_MATRIX_16_BIT_ROW
36 : typedef uint16_t kbd_row_t;
37 : #define PRIkbdrow "04" PRIx16
38 : #else
39 1 : typedef uint8_t kbd_row_t;
40 0 : #define PRIkbdrow "02" PRIx8
41 : #endif
42 :
43 : #if defined(CONFIG_INPUT_KBD_ACTUAL_KEY_MASK_DYNAMIC) || defined(__DOXYGEN__)
44 0 : #define INPUT_KBD_ACTUAL_KEY_MASK_CONST
45 : /**
46 : * @brief Enables or disables a specific row, column combination in the actual
47 : * key mask.
48 : *
49 : * This allows enabling or disabling specific row, column combination in the
50 : * actual key mask in runtime. It can be useful if some of the keys are not
51 : * present in some configuration, and the specific configuration is determined
52 : * in runtime. Requires @kconfig{CONFIG_INPUT_KBD_ACTUAL_KEY_MASK_DYNAMIC} to
53 : * be enabled.
54 : *
55 : * @param dev Pointer to the keyboard matrix device.
56 : * @param row The matrix row to enable or disable.
57 : * @param col The matrix column to enable or disable.
58 : * @param enabled Whether the specified row, col has to be enabled or disabled.
59 : *
60 : * @retval 0 If the change is successful.
61 : * @retval -errno Negative errno if row or col are out of range for the device.
62 : */
63 1 : int input_kbd_matrix_actual_key_mask_set(const struct device *dev,
64 : uint8_t row, uint8_t col, bool enabled);
65 : #else
66 : #define INPUT_KBD_ACTUAL_KEY_MASK_CONST const
67 : #endif
68 :
69 : /** Maximum number of rows */
70 1 : #define INPUT_KBD_MATRIX_ROW_BITS NUM_BITS(kbd_row_t)
71 :
72 : /**
73 : * @brief Keyboard matrix internal APIs.
74 : */
75 1 : struct input_kbd_matrix_api {
76 : /**
77 : * @brief Request to drive a specific column.
78 : *
79 : * Request to drive a specific matrix column, or none, or all.
80 : *
81 : * @param dev Pointer to the keyboard matrix device.
82 : * @param col The column to drive, or
83 : * @ref INPUT_KBD_MATRIX_COLUMN_DRIVE_NONE or
84 : * @ref INPUT_KBD_MATRIX_COLUMN_DRIVE_ALL.
85 : */
86 1 : void (*drive_column)(const struct device *dev, int col);
87 : /**
88 : * @brief Read the matrix row.
89 : *
90 : * @param dev Pointer to the keyboard matrix device.
91 : */
92 1 : kbd_row_t (*read_row)(const struct device *dev);
93 : /**
94 : * @brief Request to put the matrix in detection mode.
95 : *
96 : * Request to put the driver in detection mode, this is called after a
97 : * request to drive all the column and typically involves reenabling
98 : * interrupts row pin changes.
99 : *
100 : * @param dev Pointer to the keyboard matrix device.
101 : * @param enable Whether detection mode has to be enabled or disabled.
102 : */
103 1 : void (*set_detect_mode)(const struct device *dev, bool enabled);
104 : };
105 :
106 : /**
107 : * @brief Common keyboard matrix config.
108 : *
109 : * This structure **must** be placed first in the driver's config structure.
110 : */
111 1 : struct input_kbd_matrix_common_config {
112 0 : const struct input_kbd_matrix_api *api;
113 0 : uint8_t row_size;
114 0 : uint8_t col_size;
115 0 : uint32_t poll_period_us;
116 0 : uint32_t stable_poll_period_us;
117 0 : uint32_t poll_timeout_ms;
118 0 : uint32_t debounce_down_us;
119 0 : uint32_t debounce_up_us;
120 0 : uint32_t settle_time_us;
121 0 : bool ghostkey_check;
122 0 : INPUT_KBD_ACTUAL_KEY_MASK_CONST kbd_row_t *actual_key_mask;
123 :
124 : /* extra data pointers */
125 0 : kbd_row_t *matrix_stable_state;
126 0 : kbd_row_t *matrix_unstable_state;
127 0 : kbd_row_t *matrix_previous_state;
128 0 : kbd_row_t *matrix_new_state;
129 0 : uint8_t *scan_cycle_idx;
130 : };
131 :
132 0 : #define INPUT_KBD_MATRIX_DATA_NAME(node_id, name) \
133 : _CONCAT(__input_kbd_matrix_, \
134 : _CONCAT(name, DEVICE_DT_NAME_GET(node_id)))
135 :
136 : /**
137 : * @brief Defines the common keyboard matrix support data from devicetree,
138 : * specify row and col count.
139 : */
140 1 : #define INPUT_KBD_MATRIX_DT_DEFINE_ROW_COL(node_id, _row_size, _col_size) \
141 : BUILD_ASSERT(IN_RANGE(_row_size, 1, INPUT_KBD_MATRIX_ROW_BITS), "invalid row-size"); \
142 : BUILD_ASSERT(IN_RANGE(_col_size, 1, UINT8_MAX), "invalid col-size"); \
143 : IF_ENABLED(DT_NODE_HAS_PROP(node_id, actual_key_mask), ( \
144 : BUILD_ASSERT(DT_PROP_LEN(node_id, actual_key_mask) == _col_size, \
145 : "actual-key-mask size does not match the number of columns"); \
146 : static INPUT_KBD_ACTUAL_KEY_MASK_CONST kbd_row_t \
147 : INPUT_KBD_MATRIX_DATA_NAME(node_id, actual_key_mask)[_col_size] = \
148 : DT_PROP(node_id, actual_key_mask); \
149 : )) \
150 : static kbd_row_t INPUT_KBD_MATRIX_DATA_NAME(node_id, stable_state)[_col_size]; \
151 : static kbd_row_t INPUT_KBD_MATRIX_DATA_NAME(node_id, unstable_state)[_col_size]; \
152 : static kbd_row_t INPUT_KBD_MATRIX_DATA_NAME(node_id, previous_state)[_col_size]; \
153 : static kbd_row_t INPUT_KBD_MATRIX_DATA_NAME(node_id, new_state)[_col_size]; \
154 : static uint8_t INPUT_KBD_MATRIX_DATA_NAME(node_id, scan_cycle_idx)[_row_size * _col_size];
155 :
156 : /**
157 : * @brief Defines the common keyboard matrix support data from devicetree.
158 : */
159 1 : #define INPUT_KBD_MATRIX_DT_DEFINE(node_id) \
160 : INPUT_KBD_MATRIX_DT_DEFINE_ROW_COL( \
161 : node_id, DT_PROP(node_id, row_size), DT_PROP(node_id, col_size))
162 :
163 : /**
164 : * @brief Defines the common keyboard matrix support data from devicetree
165 : * instance, specify row and col count.
166 : *
167 : * @param inst Instance.
168 : * @param row_size The matrix row count.
169 : * @param col_size The matrix column count.
170 : */
171 1 : #define INPUT_KBD_MATRIX_DT_INST_DEFINE_ROW_COL(inst, row_size, col_size) \
172 : INPUT_KBD_MATRIX_DT_DEFINE_ROW_COL(DT_DRV_INST(inst), row_size, col_size)
173 :
174 : /**
175 : * @brief Defines the common keyboard matrix support data from devicetree instance.
176 : *
177 : * @param inst Instance.
178 : */
179 1 : #define INPUT_KBD_MATRIX_DT_INST_DEFINE(inst) \
180 : INPUT_KBD_MATRIX_DT_DEFINE(DT_DRV_INST(inst))
181 :
182 : /**
183 : * @brief Initialize common keyboard matrix config from devicetree, specify row and col count.
184 : *
185 : * @param node_id The devicetree node identifier.
186 : * @param _api Pointer to a @ref input_kbd_matrix_api structure.
187 : * @param _row_size The matrix row count.
188 : * @param _col_size The matrix column count.
189 : */
190 1 : #define INPUT_KBD_MATRIX_DT_COMMON_CONFIG_INIT_ROW_COL(node_id, _api, _row_size, _col_size) \
191 : { \
192 : .api = _api, \
193 : .row_size = _row_size, \
194 : .col_size = _col_size, \
195 : .poll_period_us = DT_PROP(node_id, poll_period_ms) * USEC_PER_MSEC, \
196 : .stable_poll_period_us = DT_PROP_OR(node_id, stable_poll_period_ms, \
197 : DT_PROP(node_id, poll_period_ms)) * \
198 : USEC_PER_MSEC, \
199 : .poll_timeout_ms = DT_PROP(node_id, poll_timeout_ms), \
200 : .debounce_down_us = DT_PROP(node_id, debounce_down_ms) * USEC_PER_MSEC, \
201 : .debounce_up_us = DT_PROP(node_id, debounce_up_ms) * USEC_PER_MSEC, \
202 : .settle_time_us = DT_PROP(node_id, settle_time_us), \
203 : .ghostkey_check = !DT_PROP(node_id, no_ghostkey_check), \
204 : IF_ENABLED(DT_NODE_HAS_PROP(node_id, actual_key_mask), ( \
205 : .actual_key_mask = INPUT_KBD_MATRIX_DATA_NAME(node_id, actual_key_mask), \
206 : )) \
207 : \
208 : .matrix_stable_state = INPUT_KBD_MATRIX_DATA_NAME(node_id, stable_state), \
209 : .matrix_unstable_state = INPUT_KBD_MATRIX_DATA_NAME(node_id, unstable_state), \
210 : .matrix_previous_state = INPUT_KBD_MATRIX_DATA_NAME(node_id, previous_state), \
211 : .matrix_new_state = INPUT_KBD_MATRIX_DATA_NAME(node_id, new_state), \
212 : .scan_cycle_idx = INPUT_KBD_MATRIX_DATA_NAME(node_id, scan_cycle_idx), \
213 : }
214 :
215 : /**
216 : * @brief Initialize common keyboard matrix config from devicetree.
217 : *
218 : * @param node_id The devicetree node identifier.
219 : * @param api Pointer to a @ref input_kbd_matrix_api structure.
220 : */
221 1 : #define INPUT_KBD_MATRIX_DT_COMMON_CONFIG_INIT(node_id, api) \
222 : INPUT_KBD_MATRIX_DT_COMMON_CONFIG_INIT_ROW_COL( \
223 : node_id, api, DT_PROP(node_id, row_size), DT_PROP(node_id, col_size))
224 :
225 : /**
226 : * @brief Initialize common keyboard matrix config from devicetree instance,
227 : * specify row and col count.
228 : *
229 : * @param inst Instance.
230 : * @param api Pointer to a @ref input_kbd_matrix_api structure.
231 : * @param row_size The matrix row count.
232 : * @param col_size The matrix column count.
233 : */
234 1 : #define INPUT_KBD_MATRIX_DT_INST_COMMON_CONFIG_INIT_ROW_COL(inst, api, row_size, col_size) \
235 : INPUT_KBD_MATRIX_DT_COMMON_CONFIG_INIT_ROW_COL(DT_DRV_INST(inst), api, row_size, col_size)
236 :
237 : /**
238 : * @brief Initialize common keyboard matrix config from devicetree instance.
239 : *
240 : * @param inst Instance.
241 : * @param api Pointer to a @ref input_kbd_matrix_api structure.
242 : */
243 1 : #define INPUT_KBD_MATRIX_DT_INST_COMMON_CONFIG_INIT(inst, api) \
244 : INPUT_KBD_MATRIX_DT_COMMON_CONFIG_INIT(DT_DRV_INST(inst), api)
245 :
246 : /**
247 : * @brief Common keyboard matrix data.
248 : *
249 : * This structure **must** be placed first in the driver's data structure.
250 : */
251 1 : struct input_kbd_matrix_common_data {
252 : /* Track previous cycles, used for debouncing. */
253 0 : uint32_t scan_clk_cycle[INPUT_KBD_MATRIX_SCAN_OCURRENCES];
254 0 : uint8_t scan_cycles_idx;
255 :
256 0 : struct k_sem poll_lock;
257 : #ifdef CONFIG_PM_DEVICE
258 : atomic_t suspended;
259 : #endif
260 :
261 0 : struct k_thread thread;
262 :
263 0 : K_KERNEL_STACK_MEMBER(thread_stack,
264 : CONFIG_INPUT_KBD_MATRIX_THREAD_STACK_SIZE);
265 : };
266 :
267 : /**
268 : * @brief Validate the offset of the common data structures.
269 : *
270 : * @param config Name of the config structure.
271 : * @param data Name of the data structure.
272 : */
273 1 : #define INPUT_KBD_STRUCT_CHECK(config, data) \
274 : BUILD_ASSERT(offsetof(config, common) == 0, \
275 : "struct input_kbd_matrix_common_config must be placed first"); \
276 : BUILD_ASSERT(offsetof(data, common) == 0, \
277 : "struct input_kbd_matrix_common_data must be placed first")
278 :
279 : /**
280 : * @brief Start scanning the keyboard matrix
281 : *
282 : * Starts the keyboard matrix scanning cycle, this should be called in reaction
283 : * of a press event, after the device has been put in detect mode.
284 : *
285 : * @param dev Keyboard matrix device instance.
286 : */
287 1 : void input_kbd_matrix_poll_start(const struct device *dev);
288 :
289 : #if defined(CONFIG_INPUT_KBD_DRIVE_COLUMN_HOOK) || defined(__DOXYGEN__)
290 : /**
291 : * @brief Drive column hook
292 : *
293 : * This can be implemented by the application to handle column selection
294 : * quirks. Called after the driver specific drive_column function. Requires
295 : * @kconfig{CONFIG_INPUT_KBD_DRIVE_COLUMN_HOOK} to be enabled.
296 : *
297 : * @param dev Keyboard matrix device instance.
298 : * @param col The column to drive, or
299 : * @ref INPUT_KBD_MATRIX_COLUMN_DRIVE_NONE or
300 : * @ref INPUT_KBD_MATRIX_COLUMN_DRIVE_ALL.
301 : */
302 1 : void input_kbd_matrix_drive_column_hook(const struct device *dev, int col);
303 : #endif
304 :
305 : /**
306 : * @brief Common function to initialize a keyboard matrix device at init time.
307 : *
308 : * This function must be called at the end of the device init function.
309 : *
310 : * @param dev Keyboard matrix device instance.
311 : *
312 : * @retval 0 If initialized successfully.
313 : * @retval -errno Negative errno in case of failure.
314 : */
315 1 : int input_kbd_matrix_common_init(const struct device *dev);
316 :
317 : #ifdef CONFIG_PM_DEVICE
318 : /**
319 : * @brief Common power management action handler.
320 : *
321 : * This handles PM actions for a keyboard matrix device, meant to be used as
322 : * argument of @ref PM_DEVICE_DT_INST_DEFINE.
323 : *
324 : * @param dev Keyboard matrix device instance.
325 : * @param action The power management action to handle.
326 : */
327 : int input_kbd_matrix_pm_action(const struct device *dev,
328 : enum pm_device_action action);
329 : #endif
330 :
331 : /** @} */
332 :
333 : #endif /* ZEPHYR_INCLUDE_INPUT_KBD_MATRIX_H_ */
|