1 /*
2  * Copyright 2025, NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/device.h>
9 #include <zephyr/input/input.h>
10 #include <zephyr/logging/log.h>
11 #include <fsl_kpp.h>
12 #include <zephyr/drivers/clock_control.h>
13 #include <zephyr/dt-bindings/clock/imx_ccm.h>
14 #include <zephyr/drivers/pinctrl.h>
15 #include <zephyr/sys/util.h>
16 
17 LOG_MODULE_REGISTER(kpp, CONFIG_INPUT_LOG_LEVEL);
18 
19 #define DT_DRV_COMPAT nxp_mcux_kpp
20 
21 #define INPUT_KPP_COLUMNNUM_MAX  KPP_KEYPAD_COLUMNNUM_MAX
22 #define INPUT_KPP_ROWNUM_MAX     KPP_KEYPAD_ROWNUM_MAX
23 #define INPUT_KPP_ROWNUM_MAX     KPP_KEYPAD_ROWNUM_MAX
24 
25 struct kpp_config {
26 	KPP_Type *base;
27 	const struct device *ccm_dev;
28 	clock_control_subsys_t clk_sub_sys;
29 	const struct pinctrl_dev_config *pcfg;
30 };
31 
32 struct kpp_data {
33 	uint32_t clock_rate;
34 	struct k_work_delayable work;
35 	uint8_t read_keys_old[KPP_KEYPAD_COLUMNNUM_MAX];
36 	uint8_t read_keys_new[KPP_KEYPAD_COLUMNNUM_MAX];
37 	uint8_t key_pressed_number;
38 	const struct device *dev;
39 };
40 
get_source_clk_rate(const struct device * dev,uint32_t * clk_rate)41 static void get_source_clk_rate(const struct device *dev, uint32_t *clk_rate)
42 {
43 	const struct kpp_config *dev_cfg = dev->config;
44 	const struct device *ccm_dev = dev_cfg->ccm_dev;
45 	clock_control_subsys_t clk_sub_sys = dev_cfg->clk_sub_sys;
46 
47 	if (!device_is_ready(ccm_dev)) {
48 		LOG_ERR("CCM driver is not installed");
49 		*clk_rate = 0;
50 		return;
51 	}
52 
53 	clock_control_get_rate(ccm_dev, clk_sub_sys, clk_rate);
54 }
55 
kpp_work_handler(struct k_work * work)56 static void kpp_work_handler(struct k_work *work)
57 {
58 	struct k_work_delayable *dwork = k_work_delayable_from_work(work);
59 	struct kpp_data *drv_data = CONTAINER_OF(dwork, struct kpp_data, work);
60 	const struct device *dev = drv_data->dev;
61 	const struct kpp_config *config = dev->config;
62 	status_t stable = kStatus_Success;
63 
64 	uint8_t read_keys_new[KPP_KEYPAD_COLUMNNUM_MAX];
65 
66 	/* Read the key press data */
67 	stable = KPP_keyPressScanning(config->base, read_keys_new, drv_data->clock_rate);
68 
69 	if (stable != kStatus_Success) {
70 		k_work_schedule(&drv_data->work, K_MSEC(CONFIG_INPUT_KPP_PERIOD_MS));
71 		return;
72 	}
73 
74 	/* Analyze the keypad data */
75 	for (int col = 0; col < INPUT_KPP_COLUMNNUM_MAX; col++) {
76 		if (drv_data->read_keys_old[col] == read_keys_new[col]) {
77 			continue;
78 		}
79 		for (int row = 0; row < INPUT_KPP_ROWNUM_MAX; row++) {
80 			if (((drv_data->read_keys_old[col] ^ read_keys_new[col])
81 				& BIT(row)) == 0) {
82 				continue;
83 			}
84 			if ((read_keys_new[col] & BIT(row)) != 0) {
85 				/* Key press event */
86 				KPP_SetSynchronizeChain(config->base,
87 					kKPP_ClearKeyDepressSyncChain);
88 				input_report_abs(dev, INPUT_ABS_X, col, false, K_FOREVER);
89 				input_report_abs(dev, INPUT_ABS_Y, row, false, K_FOREVER);
90 				input_report_key(dev, INPUT_BTN_TOUCH, 1, true, K_FOREVER);
91 				drv_data->key_pressed_number++;
92 			} else {
93 				/* Key release event */
94 				KPP_SetSynchronizeChain(config->base,
95 					kKPP_SetKeyReleasesSyncChain);
96 				input_report_abs(dev, INPUT_ABS_X, col, false, K_FOREVER);
97 				input_report_abs(dev, INPUT_ABS_Y, row, false, K_FOREVER);
98 				input_report_key(dev, INPUT_BTN_TOUCH, 0, true, K_FOREVER);
99 				drv_data->key_pressed_number--;
100 			}
101 		}
102 		drv_data->read_keys_old[col] = read_keys_new[col];
103 	}
104 
105 	if (drv_data->key_pressed_number == 0U) {
106 		KPP_ClearStatusFlag(config->base, kKPP_keyDepressInterrupt |
107 			kKPP_keyReleaseInterrupt);
108 		KPP_EnableInterrupts(config->base, kKPP_keyDepressInterrupt);
109 	} else {
110 		k_work_schedule(&drv_data->work, K_MSEC(CONFIG_INPUT_KPP_PERIOD_MS));
111 	}
112 }
113 
kpp_isr(const struct device * dev)114 static void kpp_isr(const struct device *dev)
115 {
116 	const struct kpp_config *config = dev->config;
117 	struct kpp_data *drv_data = dev->data;
118 
119 	uint16_t status = KPP_GetStatusFlag(config->base);
120 
121 	if ((status & kKPP_keyDepressInterrupt) == 0) {
122 		LOG_ERR("No key press or release detected");
123 		return;
124 	}
125 
126 	drv_data->key_pressed_number = 0;
127 	/* Disable interrupts. */
128 	KPP_DisableInterrupts(config->base, kKPP_keyDepressInterrupt |
129 		kKPP_keyReleaseInterrupt);
130 	/* Clear status. */
131 	KPP_ClearStatusFlag(config->base, kKPP_keyDepressInterrupt |
132 		kKPP_keyReleaseInterrupt);
133 	/* Key depress report */
134 	k_work_schedule(&drv_data->work, K_MSEC(0));
135 }
136 
input_kpp_init(const struct device * dev)137 static int input_kpp_init(const struct device *dev)
138 {
139 	const struct kpp_config *config = dev->config;
140 	struct kpp_data *drv_data = dev->data;
141 	kpp_config_t kppConfig;
142 	status_t stable = kStatus_Success;
143 
144 	if (!device_is_ready(config->ccm_dev)) {
145 		LOG_ERR("CCM driver is not installed");
146 		return -ENODEV;
147 	}
148 
149 	int ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
150 
151 	if (ret < 0) {
152 		LOG_ERR("Failed to configure pin");
153 		return ret;
154 	}
155 
156 	kppConfig.activeRow = 0xFF;
157 	kppConfig.activeColumn = 0xFF;
158 	kppConfig.interrupt = kKPP_keyDepressInterrupt;
159 
160 	KPP_Init(config->base, &kppConfig);
161 
162 	get_source_clk_rate(dev, &drv_data->clock_rate);
163 
164 	drv_data->dev = dev;
165 	stable = KPP_keyPressScanning(config->base, drv_data->read_keys_old, drv_data->clock_rate);
166 
167 	if (stable != kStatus_Success) {
168 		LOG_ERR("Kpp key status not stable");
169 		return -EIO;
170 	}
171 
172 	k_work_init_delayable(&drv_data->work, kpp_work_handler);
173 
174 	IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority),
175 		kpp_isr, DEVICE_DT_INST_GET(0), 0);
176 	return 0;
177 }
178 
179 #define INPUT_KPP_INIT(n)                                                               \
180 	static struct kpp_data kpp_data_##n;                                            \
181                                                                                         \
182 	PINCTRL_DT_INST_DEFINE(n);                                                      \
183                                                                                         \
184 	static const struct kpp_config kpp_config_##n = {                               \
185 		.base = (KPP_Type *)DT_INST_REG_ADDR(n),                                \
186 		.clk_sub_sys =                                                          \
187 			(clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_IDX(n, 0, name),	\
188 		.ccm_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)),                       \
189 		.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n),                              \
190 	};                                                                              \
191                                                                                         \
192 	DEVICE_DT_INST_DEFINE(n,                                                        \
193 			      input_kpp_init,                                           \
194 			      NULL,                                                     \
195 			      &kpp_data_##n,                                            \
196 			      &kpp_config_##n,                                          \
197 			      POST_KERNEL,                                              \
198 			      CONFIG_INPUT_INIT_PRIORITY,                               \
199 			      NULL);
200 
201 DT_INST_FOREACH_STATUS_OKAY(INPUT_KPP_INIT)
202