1 /*
2  * Copyright 2023 Google LLC
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT gpio_kbd_matrix
8 
9 #include <stdint.h>
10 #include <stdlib.h>
11 
12 #include <zephyr/device.h>
13 #include <zephyr/drivers/gpio.h>
14 #include <zephyr/input/input_kbd_matrix.h>
15 #include <zephyr/kernel.h>
16 #include <zephyr/sys/util.h>
17 
18 #include <zephyr/logging/log.h>
19 LOG_MODULE_REGISTER(input_gpio_kbd_matrix, CONFIG_INPUT_LOG_LEVEL);
20 
21 struct gpio_kbd_matrix_config {
22 	struct input_kbd_matrix_common_config common;
23 	const struct gpio_dt_spec *row_gpio;
24 	const struct gpio_dt_spec *col_gpio;
25 
26 	struct gpio_callback *gpio_cb;
27 	gpio_callback_handler_t gpio_cb_handler;
28 
29 	struct k_work_delayable *idle_poll_dwork;
30 	k_work_handler_t idle_poll_handler;
31 
32 	bool col_drive_inactive;
33 };
34 
35 struct gpio_kbd_matrix_data {
36 	struct input_kbd_matrix_common_data common;
37 	uint32_t last_col_state;
38 	bool direct_read;
39 	bool direct_write;
40 };
41 
42 INPUT_KBD_STRUCT_CHECK(struct gpio_kbd_matrix_config,
43 		       struct gpio_kbd_matrix_data);
44 
gpio_kbd_matrix_drive_column(const struct device * dev,int col)45 static void gpio_kbd_matrix_drive_column(const struct device *dev, int col)
46 {
47 	const struct gpio_kbd_matrix_config *cfg = dev->config;
48 	const struct input_kbd_matrix_common_config *common = &cfg->common;
49 	struct gpio_kbd_matrix_data *data = dev->data;
50 	uint32_t state;
51 
52 	if (col == INPUT_KBD_MATRIX_COLUMN_DRIVE_NONE) {
53 		state = 0;
54 	} else if (col == INPUT_KBD_MATRIX_COLUMN_DRIVE_ALL) {
55 		state = BIT_MASK(common->col_size);
56 	} else {
57 		state = BIT(col);
58 	}
59 
60 	if (data->direct_write) {
61 		const struct gpio_dt_spec *gpio0 = &cfg->col_gpio[0];
62 		gpio_port_pins_t gpio_mask;
63 		gpio_port_value_t gpio_val;
64 
65 		gpio_mask = BIT_MASK(common->col_size) << gpio0->pin;
66 		gpio_val = state << gpio0->pin;
67 
68 		gpio_port_set_masked(gpio0->port, gpio_mask, gpio_val);
69 
70 		return;
71 	}
72 
73 	for (int i = 0; i < common->col_size; i++) {
74 		const struct gpio_dt_spec *gpio = &cfg->col_gpio[i];
75 
76 		if ((data->last_col_state ^ state) & BIT(i)) {
77 			if (cfg->col_drive_inactive) {
78 				gpio_pin_set_dt(gpio, state & BIT(i));
79 			} else if (state & BIT(i)) {
80 				gpio_pin_configure_dt(gpio, GPIO_OUTPUT_ACTIVE);
81 			} else {
82 				gpio_pin_configure_dt(gpio, GPIO_INPUT);
83 			}
84 		}
85 	}
86 
87 	data->last_col_state = state;
88 }
89 
gpio_kbd_matrix_read_row(const struct device * dev)90 static kbd_row_t gpio_kbd_matrix_read_row(const struct device *dev)
91 {
92 	const struct gpio_kbd_matrix_config *cfg = dev->config;
93 	const struct input_kbd_matrix_common_config *common = &cfg->common;
94 	struct gpio_kbd_matrix_data *data = dev->data;
95 	kbd_row_t val = 0;
96 
97 	if (data->direct_read) {
98 		const struct gpio_dt_spec *gpio0 = &cfg->row_gpio[0];
99 		gpio_port_value_t gpio_val;
100 
101 		gpio_port_get(gpio0->port, &gpio_val);
102 
103 		return (gpio_val >> gpio0->pin) & BIT_MASK(common->row_size);
104 	}
105 
106 	for (int i = 0; i < common->row_size; i++) {
107 		const struct gpio_dt_spec *gpio = &cfg->row_gpio[i];
108 
109 		if (gpio_pin_get_dt(gpio)) {
110 			val |= BIT(i);
111 		}
112 	}
113 
114 	return val;
115 }
116 
gpio_kbd_matrix_idle_poll_handler(const struct device * dev)117 static __maybe_unused void gpio_kbd_matrix_idle_poll_handler(const struct device *dev)
118 {
119 	const struct gpio_kbd_matrix_config *cfg = dev->config;
120 	const struct input_kbd_matrix_common_config *common = &cfg->common;
121 
122 	if (gpio_kbd_matrix_read_row(dev) == 0) {
123 		k_work_reschedule(cfg->idle_poll_dwork,
124 				  K_USEC(common->poll_period_us));
125 		return;
126 	}
127 
128 	input_kbd_matrix_poll_start(dev);
129 }
130 
gpio_kbd_matrix_set_detect_mode(const struct device * dev,bool enabled)131 static void gpio_kbd_matrix_set_detect_mode(const struct device *dev, bool enabled)
132 {
133 	const struct gpio_kbd_matrix_config *cfg = dev->config;
134 	const struct input_kbd_matrix_common_config *common = &cfg->common;
135 	int ret;
136 
137 	if (cfg->idle_poll_dwork != NULL) {
138 		if (enabled) {
139 			k_work_reschedule(cfg->idle_poll_dwork,
140 					  K_USEC(common->poll_period_us));
141 		}
142 		return;
143 	}
144 
145 	if (cfg->gpio_cb == NULL) {
146 		return;
147 	}
148 
149 	for (int i = 0; i < common->row_size; i++) {
150 		const struct gpio_dt_spec *gpio = &cfg->row_gpio[i];
151 		gpio_flags_t flags = enabled ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE;
152 
153 		ret = gpio_pin_interrupt_configure_dt(gpio, flags);
154 		if (ret != 0) {
155 			LOG_ERR("Pin %d interrupt configuration failed: %d", i, ret);
156 			return;
157 		}
158 	}
159 }
160 
gpio_kbd_matrix_is_gpio_coherent(const struct gpio_dt_spec * gpio,int gpio_count)161 static bool gpio_kbd_matrix_is_gpio_coherent(
162 		const struct gpio_dt_spec *gpio, int gpio_count)
163 {
164 	const struct gpio_dt_spec *gpio0 = &gpio[0];
165 
166 	for (int i = 1; i < gpio_count; i++) {
167 		if (gpio[i].port != gpio0->port ||
168 		    gpio[i].dt_flags != gpio0->dt_flags ||
169 		    gpio[i].pin != gpio0->pin + i) {
170 			return false;
171 		}
172 	}
173 
174 	return true;
175 }
176 
gpio_kbd_continuous_scan_mode(const struct device * dev)177 static bool gpio_kbd_continuous_scan_mode(const struct device *dev)
178 {
179 	const struct gpio_kbd_matrix_config *cfg = dev->config;
180 
181 	if (cfg->gpio_cb == NULL && cfg->idle_poll_dwork == NULL) {
182 		return true;
183 	}
184 
185 	return false;
186 }
187 
gpio_kbd_matrix_init(const struct device * dev)188 static int gpio_kbd_matrix_init(const struct device *dev)
189 {
190 	const struct gpio_kbd_matrix_config *cfg = dev->config;
191 	const struct input_kbd_matrix_common_config *common = &cfg->common;
192 	struct gpio_kbd_matrix_data *data = dev->data;
193 	int ret;
194 	int i;
195 
196 	for (i = 0; i < common->col_size; i++) {
197 		const struct gpio_dt_spec *gpio = &cfg->col_gpio[i];
198 
199 		if (!gpio_is_ready_dt(gpio)) {
200 			LOG_ERR("%s is not ready", gpio->port->name);
201 			return -ENODEV;
202 		}
203 
204 		if (cfg->col_drive_inactive) {
205 			ret = gpio_pin_configure_dt(gpio, GPIO_OUTPUT_INACTIVE);
206 		} else {
207 			ret = gpio_pin_configure_dt(gpio, GPIO_INPUT);
208 		}
209 		if (ret != 0) {
210 			LOG_ERR("Pin %d configuration failed: %d", i, ret);
211 			return ret;
212 		}
213 	}
214 
215 	for (i = 0; i < common->row_size; i++) {
216 		const struct gpio_dt_spec *gpio = &cfg->row_gpio[i];
217 		struct gpio_callback *gpio_cb;
218 
219 		if (!gpio_is_ready_dt(gpio)) {
220 			LOG_ERR("%s is not ready", gpio->port->name);
221 			return -ENODEV;
222 		}
223 
224 		ret = gpio_pin_configure_dt(gpio, GPIO_INPUT);
225 		if (ret != 0) {
226 			LOG_ERR("Pin %d configuration failed: %d", i, ret);
227 			return ret;
228 		}
229 
230 		if (cfg->gpio_cb == NULL) {
231 			continue;
232 		}
233 		gpio_cb = &cfg->gpio_cb[i];
234 
235 		gpio_init_callback(gpio_cb, cfg->gpio_cb_handler,
236 				   BIT(gpio->pin));
237 
238 		ret = gpio_add_callback_dt(gpio, gpio_cb);
239 		if (ret < 0) {
240 			LOG_ERR("Could not set gpio callback");
241 			return ret;
242 		}
243 	}
244 
245 	if (cfg->idle_poll_dwork != NULL) {
246 		k_work_init_delayable(cfg->idle_poll_dwork,
247 				      cfg->idle_poll_handler);
248 	}
249 
250 	data->direct_read = gpio_kbd_matrix_is_gpio_coherent(
251 			cfg->row_gpio, common->row_size);
252 
253 	if (cfg->col_drive_inactive) {
254 		data->direct_write = gpio_kbd_matrix_is_gpio_coherent(
255 				cfg->col_gpio, common->col_size);
256 	}
257 
258 	LOG_DBG("direct_read: %d direct_write: %d",
259 		data->direct_read, data->direct_write);
260 
261 	ret = input_kbd_matrix_common_init(dev);
262 	if (ret != 0) {
263 		return ret;
264 	}
265 
266 	if (gpio_kbd_continuous_scan_mode(dev)) {
267 		input_kbd_matrix_poll_start(dev);
268 	}
269 
270 	return 0;
271 }
272 
273 static const struct input_kbd_matrix_api gpio_kbd_matrix_api = {
274 	.drive_column = gpio_kbd_matrix_drive_column,
275 	.read_row = gpio_kbd_matrix_read_row,
276 	.set_detect_mode = gpio_kbd_matrix_set_detect_mode,
277 };
278 
279 #define INPUT_GPIO_KBD_MATRIX_INIT(n)								\
280 	BUILD_ASSERT(DT_INST_PROP_LEN(n, col_gpios) <= 32, "invalid col-size");			\
281 												\
282 	INPUT_KBD_MATRIX_DT_INST_DEFINE_ROW_COL(						\
283 		n, DT_INST_PROP_LEN(n, row_gpios), DT_INST_PROP_LEN(n, col_gpios));		\
284 												\
285 	static const struct gpio_dt_spec gpio_kbd_matrix_row_gpio_##n[DT_INST_PROP_LEN(		\
286 			n, row_gpios)] = {							\
287 		DT_INST_FOREACH_PROP_ELEM_SEP(n, row_gpios, GPIO_DT_SPEC_GET_BY_IDX, (,))	\
288 	};											\
289 	static const struct gpio_dt_spec gpio_kbd_matrix_col_gpio_##n[DT_INST_PROP_LEN(		\
290 			n, col_gpios)] = {							\
291 		DT_INST_FOREACH_PROP_ELEM_SEP(n, col_gpios, GPIO_DT_SPEC_GET_BY_IDX, (,))	\
292 	};											\
293 												\
294 	IF_ENABLED(DT_INST_ENUM_HAS_VALUE(n, idle_mode, interrupt), (				\
295 	static struct gpio_callback gpio_kbd_matrix_gpio_cb_##n[DT_INST_PROP_LEN(n, row_gpios)];\
296 	static void gpio_kbd_matrix_cb_##n(const struct device *gpio_dev,			\
297 					   struct gpio_callback *cb, uint32_t pins)		\
298 	{											\
299 		input_kbd_matrix_poll_start(DEVICE_DT_INST_GET(n));				\
300 	}											\
301 	))											\
302 	IF_ENABLED(DT_INST_ENUM_HAS_VALUE(n, idle_mode, poll), (				\
303 	static struct k_work_delayable gpio_kbd_matrix_idle_poll_dwork_##n;			\
304 	static void gpio_kbd_matrix_idle_poll_handler_##n(struct k_work *work)			\
305 	{											\
306 		gpio_kbd_matrix_idle_poll_handler(DEVICE_DT_INST_GET(n));			\
307 	}											\
308 	))											\
309 	IF_ENABLED(DT_INST_ENUM_HAS_VALUE(n, idle_mode, scan), (				\
310 	BUILD_ASSERT(DT_INST_PROP(n, poll_timeout_ms) == 0,					\
311 		     "poll-timeout-ms must be set to 0 for scan mode to work correctly");	\
312 	))											\
313 												\
314 	static const struct gpio_kbd_matrix_config gpio_kbd_matrix_cfg_##n = {			\
315 		.common = INPUT_KBD_MATRIX_DT_INST_COMMON_CONFIG_INIT_ROW_COL(			\
316 			n, &gpio_kbd_matrix_api,						\
317 			DT_INST_PROP_LEN(n, row_gpios), DT_INST_PROP_LEN(n, col_gpios)),	\
318 		.row_gpio = gpio_kbd_matrix_row_gpio_##n,					\
319 		.col_gpio = gpio_kbd_matrix_col_gpio_##n,					\
320 		IF_ENABLED(DT_INST_ENUM_HAS_VALUE(n, idle_mode, interrupt), (			\
321 		.gpio_cb = gpio_kbd_matrix_gpio_cb_##n,						\
322 		.gpio_cb_handler = gpio_kbd_matrix_cb_##n,					\
323 		))										\
324 		IF_ENABLED(DT_INST_ENUM_HAS_VALUE(n, idle_mode, poll), (			\
325 		.idle_poll_dwork = &gpio_kbd_matrix_idle_poll_dwork_##n,			\
326 		.idle_poll_handler = gpio_kbd_matrix_idle_poll_handler_##n,			\
327 		))										\
328 		.col_drive_inactive = DT_INST_PROP(n, col_drive_inactive),			\
329 	};											\
330 												\
331 	static struct gpio_kbd_matrix_data gpio_kbd_matrix_data_##n;				\
332 												\
333 	DEVICE_DT_INST_DEFINE(n, gpio_kbd_matrix_init, NULL,					\
334 			      &gpio_kbd_matrix_data_##n, &gpio_kbd_matrix_cfg_##n,		\
335 			      POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY,				\
336 			      NULL);
337 
338 DT_INST_FOREACH_STATUS_OKAY(INPUT_GPIO_KBD_MATRIX_INIT)
339