1 /*
2  * Copyright 2023 NXP
3  * Copyright 2024-2025 TiaC Systems
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define DT_DRV_COMPAT zephyr_mipi_dbi_spi
9 
10 #include <zephyr/drivers/mipi_dbi.h>
11 #include <zephyr/drivers/spi.h>
12 #include <zephyr/drivers/gpio.h>
13 #include <zephyr/sys/byteorder.h>
14 
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(mipi_dbi_spi, CONFIG_MIPI_DBI_LOG_LEVEL);
17 
18 struct mipi_dbi_spi_config {
19 	/* SPI hardware used to send data */
20 	const struct device *spi_dev;
21 	/* Command/Data gpio */
22 	const struct gpio_dt_spec cmd_data;
23 	/* Reset GPIO */
24 	const struct gpio_dt_spec reset;
25 	/* Minimum transfer bits */
26 	const uint8_t xfr_min_bits;
27 };
28 
29 struct mipi_dbi_spi_data {
30 	struct k_mutex lock;
31 	/* Used for 3 wire mode */
32 	uint16_t spi_byte;
33 };
34 
35 /* Expands to 1 if the node does not have the `write-only` property */
36 #define MIPI_DBI_SPI_WRITE_ONLY_ABSENT(n) (!DT_INST_PROP(n, write_only)) |
37 
38 /* This macro will evaluate to 1 if any of the nodes with zephyr,mipi-dbi-spi
39  * lack a `write-only` property. The intention here is to allow the entire
40  * command_read function to be optimized out when it is not needed.
41  */
42 #define MIPI_DBI_SPI_READ_REQUIRED DT_INST_FOREACH_STATUS_OKAY(MIPI_DBI_SPI_WRITE_ONLY_ABSENT) 0
43 uint32_t var = MIPI_DBI_SPI_READ_REQUIRED;
44 
45 /* Expands to 1 if the node does reflect the enum in `xfr-min-bits` property */
46 #define MIPI_DBI_SPI_XFR_8BITS(n) (DT_INST_STRING_UPPER_TOKEN(n, xfr_min_bits) \
47 						== MIPI_DBI_SPI_XFR_8BIT) |
48 #define MIPI_DBI_SPI_XFR_16BITS(n) (DT_INST_STRING_UPPER_TOKEN(n, xfr_min_bits) \
49 						== MIPI_DBI_SPI_XFR_16BIT) |
50 
51 /* This macros will evaluate to 1 if any of the nodes with zephyr,mipi-dbi-spi
52  * have the `xfr-min-bits` property to corresponding enum value. The intention
53  * here is to allow the write helper functions to be optimized out when not all
54  * minimum transfer bits will be needed.
55  */
56 #define MIPI_DBI_SPI_WRITE_8BIT_REQUIRED DT_INST_FOREACH_STATUS_OKAY(MIPI_DBI_SPI_XFR_8BITS) 0
57 #define MIPI_DBI_SPI_WRITE_16BIT_REQUIRED DT_INST_FOREACH_STATUS_OKAY(MIPI_DBI_SPI_XFR_16BITS) 0
58 
59 /* In Type C mode 1 MIPI BIT communication, the 9th bit of the word
60  * (first bit sent in each word) indicates if the word is a command or
61  * data. Typically 0 indicates a command and 1 indicates data, but some
62  * displays may vary.
63  * Index starts from 0 so that BIT(8) means 9th bit.
64  */
65 #define MIPI_DBI_DC_BIT BIT(8)
66 
67 static inline int
mipi_dbi_spi_write_helper_3wire(const struct device * dev,const struct mipi_dbi_config * dbi_config,bool cmd_present,uint8_t cmd,const uint8_t * data_buf,size_t len)68 mipi_dbi_spi_write_helper_3wire(const struct device *dev,
69 				const struct mipi_dbi_config *dbi_config,
70 				bool cmd_present, uint8_t cmd,
71 				const uint8_t *data_buf, size_t len)
72 {
73 	const struct mipi_dbi_spi_config *config = dev->config;
74 	struct mipi_dbi_spi_data *data = dev->data;
75 	struct spi_buf buffer;
76 	struct spi_buf_set buf_set = {
77 		.buffers = &buffer,
78 		.count = 1,
79 	};
80 	int ret = 0;
81 
82 	/*
83 	 * 9 bit word mode must be used, as the command/data bit
84 	 * is stored before the data word.
85 	 */
86 
87 	if ((dbi_config->config.operation & SPI_WORD_SIZE_MASK)
88 	    != SPI_WORD_SET(9)) {
89 		return -ENOTSUP;
90 	}
91 	buffer.buf = &data->spi_byte;
92 	buffer.len = 2;
93 
94 	/* Send command */
95 	if (cmd_present) {
96 		data->spi_byte = cmd;
97 		ret = spi_write(config->spi_dev, &dbi_config->config, &buf_set);
98 		if (ret < 0) {
99 			goto out;
100 		}
101 	}
102 	/* Write data, byte by byte */
103 	for (size_t i = 0; i < len; i++) {
104 		data->spi_byte = MIPI_DBI_DC_BIT | data_buf[i];
105 		ret = spi_write(config->spi_dev, &dbi_config->config, &buf_set);
106 		if (ret < 0) {
107 			goto out;
108 		}
109 	}
110 out:
111 	return ret;
112 }
113 
114 #if MIPI_DBI_SPI_WRITE_8BIT_REQUIRED
115 
116 static inline int
mipi_dbi_spi_write_helper_4wire_8bit(const struct device * dev,const struct mipi_dbi_config * dbi_config,bool cmd_present,uint8_t cmd,const uint8_t * data_buf,size_t len)117 mipi_dbi_spi_write_helper_4wire_8bit(const struct device *dev,
118 				     const struct mipi_dbi_config *dbi_config,
119 				     bool cmd_present, uint8_t cmd,
120 				     const uint8_t *data_buf, size_t len)
121 {
122 	const struct mipi_dbi_spi_config *config = dev->config;
123 	struct spi_buf buffer;
124 	struct spi_buf_set buf_set = {
125 		.buffers = &buffer,
126 		.count = 1,
127 	};
128 	int ret = 0;
129 
130 	/*
131 	 * 4 wire mode is much simpler. We just toggle the
132 	 * command/data GPIO to indicate if we are sending
133 	 * a command or data
134 	 */
135 
136 	buffer.buf = &cmd;
137 	buffer.len = sizeof(cmd);
138 
139 	if (cmd_present) {
140 		/* Set CD pin low for command */
141 		gpio_pin_set_dt(&config->cmd_data, 0);
142 		ret = spi_write(config->spi_dev, &dbi_config->config, &buf_set);
143 		if (ret < 0) {
144 			goto out;
145 		}
146 	}
147 
148 	if (len > 0) {
149 		buffer.buf = (void *)data_buf;
150 		buffer.len = len;
151 
152 		/* Set CD pin high for data */
153 		gpio_pin_set_dt(&config->cmd_data, 1);
154 		ret = spi_write(config->spi_dev, &dbi_config->config, &buf_set);
155 		if (ret < 0) {
156 			goto out;
157 		}
158 	}
159 out:
160 	return ret;
161 }
162 
163 #endif /* MIPI_DBI_SPI_WRITE_8BIT_REQUIRED */
164 
165 #if MIPI_DBI_SPI_WRITE_16BIT_REQUIRED
166 
167 static inline int
mipi_dbi_spi_write_helper_4wire_16bit(const struct device * dev,const struct mipi_dbi_config * dbi_config,bool cmd_present,uint8_t cmd,const uint8_t * data_buf,size_t len)168 mipi_dbi_spi_write_helper_4wire_16bit(const struct device *dev,
169 				      const struct mipi_dbi_config *dbi_config,
170 				      bool cmd_present, uint8_t cmd,
171 				      const uint8_t *data_buf, size_t len)
172 {
173 	const struct mipi_dbi_spi_config *config = dev->config;
174 	struct spi_buf buffer;
175 	struct spi_buf_set buf_set = {
176 		.buffers = &buffer,
177 		.count = 1,
178 	};
179 	uint16_t data16;
180 	int ret = 0;
181 
182 	/*
183 	 * 4 wire mode with toggle the command/data GPIO
184 	 * to indicate if we are sending a command or data
185 	 * but send 16-bit blocks (with bit stuffing).
186 	 */
187 
188 	if (cmd_present) {
189 		data16 = sys_cpu_to_be16(cmd);
190 		buffer.buf = &data16;
191 		buffer.len = sizeof(data16);
192 
193 		/* Set CD pin low for command */
194 		gpio_pin_set_dt(&config->cmd_data, 0);
195 		ret = spi_write(config->spi_dev, &dbi_config->config,
196 				&buf_set);
197 		if (ret < 0) {
198 			goto out;
199 		}
200 
201 		/* Set CD pin high for data, if there are any */
202 		if (len > 0) {
203 			gpio_pin_set_dt(&config->cmd_data, 1);
204 		}
205 
206 		/* iterate command data */
207 		for (int i = 0; i < len; i++) {
208 			data16 = sys_cpu_to_be16(data_buf[i]);
209 
210 			ret = spi_write(config->spi_dev, &dbi_config->config,
211 					&buf_set);
212 			if (ret < 0) {
213 				goto out;
214 			}
215 		}
216 	} else {
217 		int stuffing = len % sizeof(data16);
218 
219 		/* Set CD pin high for data, if there are any */
220 		if (len > 0) {
221 			gpio_pin_set_dt(&config->cmd_data, 1);
222 		}
223 
224 		/* pass through generic device data */
225 		if (len - stuffing > 0) {
226 			buffer.buf = (void *)data_buf;
227 			buffer.len = len - stuffing;
228 
229 			ret = spi_write(config->spi_dev, &dbi_config->config,
230 					&buf_set);
231 			if (ret < 0) {
232 				goto out;
233 			}
234 		}
235 
236 		/* iterate remaining data with stuffing */
237 		for (int i = len - stuffing; i < len; i++) {
238 			data16 = sys_cpu_to_be16(data_buf[i]);
239 			buffer.buf = &data16;
240 			buffer.len = sizeof(data16);
241 
242 			ret = spi_write(config->spi_dev, &dbi_config->config,
243 					&buf_set);
244 			if (ret < 0) {
245 				goto out;
246 			}
247 		}
248 	}
249 out:
250 	return ret;
251 }
252 
253 #endif /* MIPI_DBI_SPI_WRITE_16BIT_REQUIRED */
254 
mipi_dbi_spi_write_helper(const struct device * dev,const struct mipi_dbi_config * dbi_config,bool cmd_present,uint8_t cmd,const uint8_t * data_buf,size_t len)255 static int mipi_dbi_spi_write_helper(const struct device *dev,
256 				     const struct mipi_dbi_config *dbi_config,
257 				     bool cmd_present, uint8_t cmd,
258 				     const uint8_t *data_buf, size_t len)
259 {
260 	const struct mipi_dbi_spi_config *config = dev->config;
261 	struct mipi_dbi_spi_data *data = dev->data;
262 	int ret = 0;
263 
264 	ret = k_mutex_lock(&data->lock, K_FOREVER);
265 	if (ret < 0) {
266 		return ret;
267 	}
268 
269 	if (dbi_config->mode == MIPI_DBI_MODE_SPI_3WIRE &&
270 	    IS_ENABLED(CONFIG_MIPI_DBI_SPI_3WIRE)) {
271 		ret = mipi_dbi_spi_write_helper_3wire(dev, dbi_config,
272 						      cmd_present, cmd,
273 						      data_buf, len);
274 		goto out;
275 	}
276 
277 	if (dbi_config->mode == MIPI_DBI_MODE_SPI_4WIRE) {
278 
279 #if MIPI_DBI_SPI_WRITE_8BIT_REQUIRED
280 		if (config->xfr_min_bits == MIPI_DBI_SPI_XFR_8BIT) {
281 			ret = mipi_dbi_spi_write_helper_4wire_8bit(
282 							dev, dbi_config,
283 							cmd_present, cmd,
284 							data_buf, len);
285 			goto out;
286 		}
287 #endif
288 
289 #if MIPI_DBI_SPI_WRITE_16BIT_REQUIRED
290 		if (config->xfr_min_bits == MIPI_DBI_SPI_XFR_16BIT) {
291 			ret = mipi_dbi_spi_write_helper_4wire_16bit(
292 							dev, dbi_config,
293 							cmd_present, cmd,
294 							data_buf, len);
295 			goto out;
296 		}
297 #endif
298 
299 	}
300 
301 	/* Otherwise, unsupported mode */
302 	ret = -ENOTSUP;
303 
304 out:
305 	k_mutex_unlock(&data->lock);
306 	return ret;
307 }
308 
mipi_dbi_spi_command_write(const struct device * dev,const struct mipi_dbi_config * dbi_config,uint8_t cmd,const uint8_t * data_buf,size_t len)309 static int mipi_dbi_spi_command_write(const struct device *dev,
310 				      const struct mipi_dbi_config *dbi_config,
311 				      uint8_t cmd, const uint8_t *data_buf,
312 				      size_t len)
313 {
314 	return mipi_dbi_spi_write_helper(dev, dbi_config, true, cmd,
315 					 data_buf, len);
316 }
317 
mipi_dbi_spi_write_display(const struct device * dev,const struct mipi_dbi_config * dbi_config,const uint8_t * framebuf,struct display_buffer_descriptor * desc,enum display_pixel_format pixfmt)318 static int mipi_dbi_spi_write_display(const struct device *dev,
319 				      const struct mipi_dbi_config *dbi_config,
320 				      const uint8_t *framebuf,
321 				      struct display_buffer_descriptor *desc,
322 				      enum display_pixel_format pixfmt)
323 {
324 	ARG_UNUSED(pixfmt);
325 
326 	return mipi_dbi_spi_write_helper(dev, dbi_config, false, 0x0,
327 					 framebuf, desc->buf_size);
328 }
329 
330 #if MIPI_DBI_SPI_READ_REQUIRED
331 
332 static inline int
mipi_dbi_spi_read_helper_3wire(const struct device * dev,const struct mipi_dbi_config * dbi_config,uint8_t * cmds,size_t num_cmds,uint8_t * response,size_t len)333 mipi_dbi_spi_read_helper_3wire(const struct device *dev,
334 			       const struct mipi_dbi_config *dbi_config,
335 			       uint8_t *cmds, size_t num_cmds,
336 			       uint8_t *response, size_t len)
337 {
338 	const struct mipi_dbi_spi_config *config = dev->config;
339 	struct mipi_dbi_spi_data *data = dev->data;
340 	struct spi_config tmp_config;
341 	struct spi_buf buffer;
342 	struct spi_buf_set buf_set = {
343 		.buffers = &buffer,
344 		.count = 1,
345 	};
346 	int ret = 0;
347 
348 	/*
349 	 * We have to emulate 3 wire mode by packing the data/command
350 	 * bit into the upper bit of the SPI transfer, switch SPI to
351 	 * 9 bit mode, and write the transfer.
352 	 */
353 
354 	if ((dbi_config->config.operation & SPI_WORD_SIZE_MASK)
355 	    != SPI_WORD_SET(9)) {
356 		return -ENOTSUP;
357 	}
358 
359 	memcpy(&tmp_config, &dbi_config->config, sizeof(tmp_config));
360 	tmp_config.operation &= ~SPI_WORD_SIZE_MASK;
361 	tmp_config.operation |= SPI_WORD_SET(9);
362 
363 	buffer.buf = &data->spi_byte;
364 	buffer.len = 1;
365 
366 	/* Send each command */
367 	for (size_t i = 0; i < num_cmds; i++) {
368 		data->spi_byte = cmds[i];
369 		ret = spi_write(config->spi_dev, &tmp_config, &buf_set);
370 		if (ret < 0) {
371 			goto out;
372 		}
373 	}
374 
375 	/* Now, we can switch to 8 bit mode, and read data */
376 	buffer.buf = (void *)response;
377 	buffer.len = len;
378 	ret = spi_read(config->spi_dev, &dbi_config->config, &buf_set);
379 
380 out:
381 	spi_release(config->spi_dev, &tmp_config); /* Really necessary here? */
382 	return ret;
383 }
384 
385 static inline int
mipi_dbi_spi_read_helper_4wire(const struct device * dev,const struct mipi_dbi_config * dbi_config,uint8_t * cmds,size_t num_cmds,uint8_t * response,size_t len)386 mipi_dbi_spi_read_helper_4wire(const struct device *dev,
387 			       const struct mipi_dbi_config *dbi_config,
388 			       uint8_t *cmds, size_t num_cmds,
389 			       uint8_t *response, size_t len)
390 {
391 	const struct mipi_dbi_spi_config *config = dev->config;
392 	struct spi_config tmp_config;
393 	struct spi_buf buffer;
394 	struct spi_buf_set buf_set = {
395 		.buffers = &buffer,
396 		.count = 1,
397 	};
398 	int ret = 0;
399 
400 	/*
401 	 * 4 wire mode is much simpler. We just toggle the
402 	 * command/data GPIO to indicate if we are sending
403 	 * a command or data. Note that since some SPI displays
404 	 * require CS to be held low for the entire read sequence,
405 	 * we set SPI_HOLD_ON_CS
406 	 */
407 
408 	memcpy(&tmp_config, &dbi_config->config, sizeof(tmp_config));
409 	tmp_config.operation |= SPI_HOLD_ON_CS;
410 
411 	if (num_cmds > 0) {
412 		buffer.buf = cmds;
413 		buffer.len = num_cmds;
414 
415 		/* Set CD pin low for command */
416 		gpio_pin_set_dt(&config->cmd_data, 0);
417 
418 		ret = spi_write(config->spi_dev, &tmp_config, &buf_set);
419 		if (ret < 0) {
420 			goto out;
421 		}
422 	}
423 
424 	if (len > 0) {
425 		buffer.buf = (void *)response;
426 		buffer.len = len;
427 
428 		/* Set CD pin high for data */
429 		gpio_pin_set_dt(&config->cmd_data, 1);
430 
431 		ret = spi_read(config->spi_dev, &tmp_config, &buf_set);
432 		if (ret < 0) {
433 			goto out;
434 		}
435 	}
436 
437 out:
438 	spi_release(config->spi_dev, &tmp_config);
439 	return ret;
440 }
441 
mipi_dbi_spi_command_read(const struct device * dev,const struct mipi_dbi_config * dbi_config,uint8_t * cmds,size_t num_cmds,uint8_t * response,size_t len)442 static int mipi_dbi_spi_command_read(const struct device *dev,
443 				     const struct mipi_dbi_config *dbi_config,
444 				     uint8_t *cmds, size_t num_cmds,
445 				     uint8_t *response, size_t len)
446 {
447 	struct mipi_dbi_spi_data *data = dev->data;
448 	int ret = 0;
449 
450 	ret = k_mutex_lock(&data->lock, K_FOREVER);
451 	if (ret < 0) {
452 		return ret;
453 	}
454 	if (dbi_config->mode == MIPI_DBI_MODE_SPI_3WIRE &&
455 	    IS_ENABLED(CONFIG_MIPI_DBI_SPI_3WIRE)) {
456 		ret = mipi_dbi_spi_read_helper_3wire(dev, dbi_config,
457 						     cmds, num_cmds,
458 						     response, len);
459 		if (ret < 0) {
460 			goto out;
461 		}
462 	} else if (dbi_config->mode == MIPI_DBI_MODE_SPI_4WIRE) {
463 		ret = mipi_dbi_spi_read_helper_4wire(dev, dbi_config,
464 						     cmds, num_cmds,
465 						     response, len);
466 		if (ret < 0) {
467 			goto out;
468 		}
469 	} else {
470 		/* Otherwise, unsupported mode */
471 		ret = -ENOTSUP;
472 	}
473 out:
474 	k_mutex_unlock(&data->lock);
475 	return ret;
476 }
477 
478 #endif /* MIPI_DBI_SPI_READ_REQUIRED */
479 
mipi_dbi_has_pin(const struct gpio_dt_spec * spec)480 static inline bool mipi_dbi_has_pin(const struct gpio_dt_spec *spec)
481 {
482 	return spec->port != NULL;
483 }
484 
mipi_dbi_spi_reset(const struct device * dev,k_timeout_t delay)485 static int mipi_dbi_spi_reset(const struct device *dev, k_timeout_t delay)
486 {
487 	const struct mipi_dbi_spi_config *config = dev->config;
488 	int ret;
489 
490 	if (!mipi_dbi_has_pin(&config->reset)) {
491 		return -ENOTSUP;
492 	}
493 
494 	ret = gpio_pin_set_dt(&config->reset, 1);
495 	if (ret < 0) {
496 		return ret;
497 	}
498 	k_sleep(delay);
499 	return gpio_pin_set_dt(&config->reset, 0);
500 }
501 
mipi_dbi_spi_release(const struct device * dev,const struct mipi_dbi_config * dbi_config)502 static int mipi_dbi_spi_release(const struct device *dev,
503 				const struct mipi_dbi_config *dbi_config)
504 {
505 	const struct mipi_dbi_spi_config *config = dev->config;
506 
507 	return spi_release(config->spi_dev, &dbi_config->config);
508 }
509 
mipi_dbi_spi_init(const struct device * dev)510 static int mipi_dbi_spi_init(const struct device *dev)
511 {
512 	const struct mipi_dbi_spi_config *config = dev->config;
513 	struct mipi_dbi_spi_data *data = dev->data;
514 	int ret;
515 
516 	if (!device_is_ready(config->spi_dev)) {
517 		LOG_ERR("SPI device is not ready");
518 		return -ENODEV;
519 	}
520 
521 	if (mipi_dbi_has_pin(&config->cmd_data)) {
522 		if (!gpio_is_ready_dt(&config->cmd_data)) {
523 			return -ENODEV;
524 		}
525 		ret = gpio_pin_configure_dt(&config->cmd_data, GPIO_OUTPUT);
526 		if (ret < 0) {
527 			LOG_ERR("Could not configure command/data GPIO (%d)", ret);
528 			return ret;
529 		}
530 	}
531 
532 	if (mipi_dbi_has_pin(&config->reset)) {
533 		if (!gpio_is_ready_dt(&config->reset)) {
534 			return -ENODEV;
535 		}
536 		ret = gpio_pin_configure_dt(&config->reset, GPIO_OUTPUT_INACTIVE);
537 		if (ret < 0) {
538 			LOG_ERR("Could not configure reset GPIO (%d)", ret);
539 			return ret;
540 		}
541 	}
542 
543 	k_mutex_init(&data->lock);
544 
545 	return 0;
546 }
547 
548 static DEVICE_API(mipi_dbi, mipi_dbi_spi_driver_api) = {
549 	.reset = mipi_dbi_spi_reset,
550 	.command_write = mipi_dbi_spi_command_write,
551 	.write_display = mipi_dbi_spi_write_display,
552 	.release = mipi_dbi_spi_release,
553 #if MIPI_DBI_SPI_READ_REQUIRED
554 	.command_read = mipi_dbi_spi_command_read,
555 #endif
556 };
557 
558 #define MIPI_DBI_SPI_INIT(n)							\
559 	static const struct mipi_dbi_spi_config					\
560 	    mipi_dbi_spi_config_##n = {						\
561 		    .spi_dev = DEVICE_DT_GET(					\
562 				    DT_INST_PHANDLE(n, spi_dev)),		\
563 		    .cmd_data = GPIO_DT_SPEC_INST_GET_OR(n, dc_gpios, {}),	\
564 		    .reset = GPIO_DT_SPEC_INST_GET_OR(n, reset_gpios, {}),	\
565 		    .xfr_min_bits = DT_INST_STRING_UPPER_TOKEN(n, xfr_min_bits) \
566 	};									\
567 	static struct mipi_dbi_spi_data mipi_dbi_spi_data_##n;			\
568 										\
569 	DEVICE_DT_INST_DEFINE(n, mipi_dbi_spi_init, NULL,			\
570 			&mipi_dbi_spi_data_##n,					\
571 			&mipi_dbi_spi_config_##n,				\
572 			POST_KERNEL,						\
573 			CONFIG_MIPI_DBI_INIT_PRIORITY,				\
574 			&mipi_dbi_spi_driver_api);
575 
576 DT_INST_FOREACH_STATUS_OKAY(MIPI_DBI_SPI_INIT)
577