1 /*
2  * Copyright (c) 2023 Seppo Takalo
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT xptek_xpt2046
8 
9 #include <zephyr/drivers/spi.h>
10 #include <zephyr/input/input.h>
11 #include <zephyr/kernel.h>
12 
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(xpt2046, CONFIG_INPUT_LOG_LEVEL);
15 
16 struct xpt2046_config {
17 	const struct spi_dt_spec bus;
18 	const struct gpio_dt_spec int_gpio;
19 	uint16_t min_x;
20 	uint16_t min_y;
21 	uint16_t max_x;
22 	uint16_t max_y;
23 	uint16_t threshold;
24 	uint16_t screen_size_x;
25 	uint16_t screen_size_y;
26 	uint16_t reads;
27 };
28 struct xpt2046_data {
29 	const struct device *dev;
30 	struct gpio_callback int_gpio_cb;
31 	struct k_work work;
32 	struct k_work_delayable dwork;
33 	uint8_t rbuf[9];
34 	uint32_t last_x;
35 	uint32_t last_y;
36 	bool pressed;
37 };
38 
39 enum xpt2046_channel {
40 	CH_TEMP0 = 0,
41 	CH_Y,
42 	CH_VBAT,
43 	CH_Z1,
44 	CH_Z2,
45 	CH_X,
46 	CH_AUXIN,
47 	CH_TEMP1
48 };
49 
50 struct measurement {
51 	uint32_t x;
52 	uint32_t y;
53 	uint32_t z;
54 };
55 
56 #define START		      BIT(7)
57 #define CHANNEL(ch)	      ((ch & 0x7) << 4)
58 #define MODE_8_BIT	      BIT(3)
59 #define SINGLE_ENDED	      BIT(2)
60 #define POWER_OFF	      0
61 #define POWER_ON	      0x03
62 #define CONVERT_U16(buf, idx) ((uint16_t)((buf[idx] & 0x7f) << 5) | (buf[idx + 1] >> 3))
63 
64 /* Read all Z1, X, Y, Z2 channels using 16 Clocks-per-Conversion mode.
65  * See the manual https://www.waveshare.com/w/upload/9/98/XPT2046-EN.pdf for details.
66  * Each follow-up command interleaves with previous conversion.
67  * So first command starts at byte 0. Second command starts at byte 2.
68  */
69 static uint8_t tbuf[9] = {
70 	[0] = START | CHANNEL(CH_Z1) | POWER_ON,
71 	[2] = START | CHANNEL(CH_Z2) | POWER_ON,
72 	[4] = START | CHANNEL(CH_X) | POWER_ON,
73 	[6] = START | CHANNEL(CH_Y) | POWER_OFF,
74 };
75 
xpt2046_isr_handler(const struct device * dev,struct gpio_callback * cb,uint32_t pins)76 static void xpt2046_isr_handler(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
77 {
78 	struct xpt2046_data *data = CONTAINER_OF(cb, struct xpt2046_data, int_gpio_cb);
79 	const struct xpt2046_config *config = data->dev->config;
80 
81 	gpio_remove_callback(config->int_gpio.port, &data->int_gpio_cb);
82 	k_work_submit(&data->work);
83 }
84 
xpt2046_read_and_cumulate(const struct spi_dt_spec * bus,const struct spi_buf_set * tx,const struct spi_buf_set * rx,struct measurement * meas)85 static int xpt2046_read_and_cumulate(const struct spi_dt_spec *bus, const struct spi_buf_set *tx,
86 				     const struct spi_buf_set *rx, struct measurement *meas)
87 {
88 	int ret = spi_transceive_dt(bus, tx, rx);
89 
90 	if (ret < 0) {
91 		LOG_ERR("spi_transceive() %d\n", ret);
92 		return ret;
93 	}
94 
95 	uint8_t *buf = rx->buffers->buf;
96 
97 	meas->z += CONVERT_U16(buf, 1) + 4096 - CONVERT_U16(buf, 3);
98 	meas->x += CONVERT_U16(buf, 5);
99 	meas->y += CONVERT_U16(buf, 7);
100 
101 	return 0;
102 }
103 
xpt2046_release_handler(struct k_work * kw)104 static void xpt2046_release_handler(struct k_work *kw)
105 {
106 	struct k_work_delayable *dw = k_work_delayable_from_work(kw);
107 	struct xpt2046_data *data = CONTAINER_OF(dw, struct xpt2046_data, dwork);
108 	struct xpt2046_config *config = (struct xpt2046_config *)data->dev->config;
109 
110 	if (!data->pressed) {
111 		return;
112 	}
113 
114 	/* Check if touch is still pressed */
115 	if (gpio_pin_get_dt(&config->int_gpio) == 0) {
116 		data->pressed = false;
117 		input_report_key(data->dev, INPUT_BTN_TOUCH, 0, true, K_FOREVER);
118 	} else {
119 		/* Re-check later */
120 		k_work_reschedule(&data->dwork, K_MSEC(10));
121 	}
122 }
123 
xpt2046_work_handler(struct k_work * kw)124 static void xpt2046_work_handler(struct k_work *kw)
125 {
126 	struct xpt2046_data *data = CONTAINER_OF(kw, struct xpt2046_data, work);
127 	struct xpt2046_config *config = (struct xpt2046_config *)data->dev->config;
128 	int ret;
129 
130 	const struct spi_buf txb = {.buf = tbuf, .len = sizeof(tbuf)};
131 	const struct spi_buf rxb = {.buf = data->rbuf, .len = sizeof(data->rbuf)};
132 	const struct spi_buf_set tx_bufs = {.buffers = &txb, .count = 1};
133 	const struct spi_buf_set rx_bufs = {.buffers = &rxb, .count = 1};
134 
135 	/* Run number of reads and calculate average */
136 	int rounds = config->reads;
137 	struct measurement meas = {0};
138 
139 	for (int i = 0; i < rounds; i++) {
140 		if (xpt2046_read_and_cumulate(&config->bus, &tx_bufs, &rx_bufs, &meas) != 0) {
141 			return;
142 		}
143 	}
144 	meas.x /= rounds;
145 	meas.y /= rounds;
146 	meas.z /= rounds;
147 
148 	/* Calculate Xp = M * Xt + C using fixed point aritchmetics, where
149 	 * Xp is the point in screen coordinates, Xt is the touch coordinates.
150 	 * Use signed int32_t for calculation to ensure that we cover the roll-over to negative
151 	 * values and return zero instead.
152 	 */
153 	int32_t mx = (config->screen_size_x << 16) / (config->max_x - config->min_x);
154 	int32_t cx = (config->screen_size_x << 16) - mx * config->max_x;
155 	int32_t x = mx * meas.x + cx;
156 
157 	x = (x < 0 ? 0 : x) >> 16;
158 
159 	int32_t my = (config->screen_size_y << 16) / (config->max_y - config->min_y);
160 	int32_t cy = (config->screen_size_y << 16) - my * config->max_y;
161 	int32_t y = my * meas.y + cy;
162 
163 	y = (y < 0 ? 0 : y) >> 16;
164 
165 	bool pressed = meas.z > config->threshold;
166 
167 	/* Don't send any other than "pressed" events.
168 	 * releasing seem to cause just random noise
169 	 */
170 	if (pressed) {
171 		LOG_DBG("raw: x=%4u y=%4u ==> x=%4d y=%4d", meas.x, meas.y, x, y);
172 
173 		input_report_abs(data->dev, INPUT_ABS_X, x, false, K_FOREVER);
174 		input_report_abs(data->dev, INPUT_ABS_Y, y, false, K_FOREVER);
175 		input_report_key(data->dev, INPUT_BTN_TOUCH, 1, true, K_FOREVER);
176 
177 		data->last_x = x;
178 		data->last_y = y;
179 		data->pressed = pressed;
180 
181 		/* Ensure that we send released event */
182 		k_work_reschedule(&data->dwork, K_MSEC(100));
183 	}
184 
185 	ret = gpio_add_callback(config->int_gpio.port, &data->int_gpio_cb);
186 	if (ret < 0) {
187 		LOG_ERR("Could not set gpio callback");
188 		return;
189 	}
190 }
191 
xpt2046_init(const struct device * dev)192 static int xpt2046_init(const struct device *dev)
193 {
194 	int r;
195 	const struct xpt2046_config *config = dev->config;
196 	struct xpt2046_data *data = dev->data;
197 
198 	if (!spi_is_ready_dt(&config->bus)) {
199 		LOG_ERR("SPI controller device not ready");
200 		return -ENODEV;
201 	}
202 
203 	data->dev = dev;
204 	k_work_init(&data->work, xpt2046_work_handler);
205 	k_work_init_delayable(&data->dwork, xpt2046_release_handler);
206 
207 	if (!gpio_is_ready_dt(&config->int_gpio)) {
208 		LOG_ERR("Interrupt GPIO controller device not ready");
209 		return -ENODEV;
210 	}
211 
212 	r = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT);
213 	if (r < 0) {
214 		LOG_ERR("Could not configure interrupt GPIO pin");
215 		return r;
216 	}
217 
218 	r = gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
219 	if (r < 0) {
220 		LOG_ERR("Could not configure interrupt GPIO interrupt.");
221 		return r;
222 	}
223 
224 	gpio_init_callback(&data->int_gpio_cb, xpt2046_isr_handler, BIT(config->int_gpio.pin));
225 
226 	r = gpio_add_callback(config->int_gpio.port, &data->int_gpio_cb);
227 	if (r < 0) {
228 		LOG_ERR("Could not set gpio callback");
229 		return r;
230 	}
231 
232 	LOG_INF("Init '%s' device", dev->name);
233 
234 	return 0;
235 }
236 
237 #define XPT2046_INIT(index)                                                                        \
238 	static const struct xpt2046_config xpt2046_config_##index = {                              \
239 		.bus = SPI_DT_SPEC_INST_GET(                                                       \
240 			index, SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8), 0),        \
241 		.int_gpio = GPIO_DT_SPEC_INST_GET(index, int_gpios),                               \
242 		.min_x = DT_INST_PROP(index, min_x),                                               \
243 		.min_y = DT_INST_PROP(index, min_y),                                               \
244 		.max_x = DT_INST_PROP(index, max_x),                                               \
245 		.max_y = DT_INST_PROP(index, max_y),                                               \
246 		.threshold = DT_INST_PROP(index, z_threshold),                                     \
247 		.screen_size_x = DT_INST_PROP(index, touchscreen_size_x),                          \
248 		.screen_size_y = DT_INST_PROP(index, touchscreen_size_y),                          \
249 		.reads = DT_INST_PROP(index, reads),                                               \
250 	};                                                                                         \
251 	static struct xpt2046_data xpt2046_data_##index;                                           \
252 	DEVICE_DT_INST_DEFINE(index, xpt2046_init, NULL, &xpt2046_data_##index,                    \
253 			      &xpt2046_config_##index, POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY,    \
254 			      NULL);                                                               \
255 	BUILD_ASSERT(DT_INST_PROP(index, min_x) < DT_INST_PROP(index, max_x),                      \
256 		     "min_x must be less than max_x");                                             \
257 	BUILD_ASSERT(DT_INST_PROP(index, min_y) < DT_INST_PROP(index, max_y),                      \
258 		     "min_y must be less than max_y");                                             \
259 	BUILD_ASSERT(DT_INST_PROP(index, z_threshold) > 10, "Too small threshold");                \
260 	BUILD_ASSERT(DT_INST_PROP(index, touchscreen_size_x) > 1 &&                                \
261 			     DT_INST_PROP(index, touchscreen_size_y) > 1,                          \
262 		     "Screen size undefined");                                                     \
263 	BUILD_ASSERT(DT_INST_PROP(index, reads) > 0, "Number of reads must be at least one");
264 
265 DT_INST_FOREACH_STATUS_OKAY(XPT2046_INIT)
266