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