1 /*
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 #define INPUT_KBD_MATRIX_COLUMN_DRIVE_NONE -1
27 
28 /** Special drive_column argument for driving all the columns */
29 #define INPUT_KBD_MATRIX_COLUMN_DRIVE_ALL  -2
30 
31 /** Number of tracked scan cycles */
32 #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 typedef uint8_t kbd_row_t;
40 #define PRIkbdrow "02" PRIx8
41 #endif
42 
43 #if defined(CONFIG_INPUT_KBD_ACTUAL_KEY_MASK_DYNAMIC) || defined(__DOXYGEN__)
44 #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 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 #define INPUT_KBD_MATRIX_ROW_BITS NUM_BITS(kbd_row_t)
71 
72 /**
73  * @brief Keyboard matrix internal APIs.
74  */
75 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 	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 	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 	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 struct input_kbd_matrix_common_config {
112 	const struct input_kbd_matrix_api *api;
113 	uint8_t row_size;
114 	uint8_t col_size;
115 	uint32_t poll_period_us;
116 	uint32_t stable_poll_period_us;
117 	uint32_t poll_timeout_ms;
118 	uint32_t debounce_down_us;
119 	uint32_t debounce_up_us;
120 	uint32_t settle_time_us;
121 	bool ghostkey_check;
122 	INPUT_KBD_ACTUAL_KEY_MASK_CONST kbd_row_t *actual_key_mask;
123 
124 	/* extra data pointers */
125 	kbd_row_t *matrix_stable_state;
126 	kbd_row_t *matrix_unstable_state;
127 	kbd_row_t *matrix_previous_state;
128 	kbd_row_t *matrix_new_state;
129 	uint8_t *scan_cycle_idx;
130 };
131 
132 #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 #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 #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 #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 #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 #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 #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 #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 #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 struct input_kbd_matrix_common_data {
252 	/* Track previous cycles, used for debouncing. */
253 	uint32_t scan_clk_cycle[INPUT_KBD_MATRIX_SCAN_OCURRENCES];
254 	uint8_t scan_cycles_idx;
255 
256 	struct k_sem poll_lock;
257 #ifdef CONFIG_PM_DEVICE
258 	atomic_t suspended;
259 #endif
260 
261 	struct k_thread thread;
262 
263 	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 #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 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 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 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_ */
334