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