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