1 /*
2  * Copyright (c) 2017 Jan Van Winkel <jan.van_winkel@dxplore.eu>
3  * Copyright (c) 2019 Nordic Semiconductor ASA
4  * Copyright (c) 2020 Teslabs Engineering S.L.
5  * Copyright (c) 2021 Krivorot Oleg <krivorot.oleg@gmail.com>
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 
9 #include "display_ili9xxx.h"
10 
11 #include <zephyr/dt-bindings/display/ili9xxx.h>
12 #include <zephyr/drivers/display.h>
13 #include <zephyr/sys/byteorder.h>
14 
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(display_ili9xxx, CONFIG_DISPLAY_LOG_LEVEL);
17 
18 struct ili9xxx_data {
19 	uint8_t bytes_per_pixel;
20 	enum display_pixel_format pixel_format;
21 	enum display_orientation orientation;
22 };
23 
24 #ifdef CONFIG_ILI9XXX_READ
25 
26 /* We set this LUT directly when reads are enabled,
27  * so that we can be sure the bitshift to convert GRAM data back
28  * to RGB565 will result in correct data
29  */
30 const uint8_t ili9xxx_rgb_lut[] = {
31 	0, 2, 4, 6,
32 	8, 10, 12, 14,
33 	16, 18, 20, 22,
34 	24, 26, 28, 30,
35 	32, 34, 36, 38,
36 	40, 42, 44, 46,
37 	48, 50, 52, 54,
38 	56, 58, 60, 62,
39 	0, 1, 2, 3,
40 	4, 5, 6, 7,
41 	8, 9, 10, 11,
42 	12, 13, 14, 15,
43 	16, 17, 18, 19,
44 	20, 21, 22, 23,
45 	24, 25, 26, 27,
46 	28, 29, 30, 31,
47 	32, 33, 34, 35,
48 	36, 37, 38, 39,
49 	40, 41, 42, 43,
50 	44, 45, 46, 47,
51 	48, 49, 50, 51,
52 	52, 53, 54, 55,
53 	56, 57, 58, 59,
54 	60, 61, 62, 63,
55 	0, 2, 4, 6,
56 	8, 10, 12, 14,
57 	16, 18, 20, 22,
58 	24, 26, 28, 30,
59 	32, 34, 36, 38,
60 	40, 42, 44, 46,
61 	48, 50, 52, 54,
62 	56, 58, 60, 62
63 };
64 
65 #endif
66 
ili9xxx_transmit(const struct device * dev,uint8_t cmd,const void * tx_data,size_t tx_len)67 int ili9xxx_transmit(const struct device *dev, uint8_t cmd, const void *tx_data,
68 		     size_t tx_len)
69 {
70 	const struct ili9xxx_config *config = dev->config;
71 
72 	return mipi_dbi_command_write(config->mipi_dev, &config->dbi_config,
73 				      cmd, tx_data, tx_len);
74 }
75 
ili9xxx_exit_sleep(const struct device * dev)76 static int ili9xxx_exit_sleep(const struct device *dev)
77 {
78 	int r;
79 
80 	r = ili9xxx_transmit(dev, ILI9XXX_SLPOUT, NULL, 0);
81 	if (r < 0) {
82 		return r;
83 	}
84 
85 	k_sleep(K_MSEC(ILI9XXX_SLEEP_OUT_TIME));
86 
87 	return 0;
88 }
89 
ili9xxx_hw_reset(const struct device * dev)90 static void ili9xxx_hw_reset(const struct device *dev)
91 {
92 	const struct ili9xxx_config *config = dev->config;
93 
94 	if (mipi_dbi_reset(config->mipi_dev, ILI9XXX_RESET_PULSE_TIME) < 0) {
95 		return;
96 	};
97 	k_sleep(K_MSEC(ILI9XXX_RESET_WAIT_TIME));
98 }
99 
ili9xxx_set_mem_area(const struct device * dev,const uint16_t x,const uint16_t y,const uint16_t w,const uint16_t h)100 static int ili9xxx_set_mem_area(const struct device *dev, const uint16_t x,
101 				const uint16_t y, const uint16_t w,
102 				const uint16_t h)
103 {
104 	int r;
105 	uint16_t spi_data[2];
106 
107 	spi_data[0] = sys_cpu_to_be16(x);
108 	spi_data[1] = sys_cpu_to_be16(x + w - 1U);
109 	r = ili9xxx_transmit(dev, ILI9XXX_CASET, &spi_data[0], 4U);
110 	if (r < 0) {
111 		return r;
112 	}
113 
114 	spi_data[0] = sys_cpu_to_be16(y);
115 	spi_data[1] = sys_cpu_to_be16(y + h - 1U);
116 	r = ili9xxx_transmit(dev, ILI9XXX_PASET, &spi_data[0], 4U);
117 	if (r < 0) {
118 		return r;
119 	}
120 
121 	return 0;
122 }
123 
ili9xxx_write(const struct device * dev,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc,const void * buf)124 static int ili9xxx_write(const struct device *dev, const uint16_t x,
125 			 const uint16_t y,
126 			 const struct display_buffer_descriptor *desc,
127 			 const void *buf)
128 {
129 	const struct ili9xxx_config *config = dev->config;
130 	struct ili9xxx_data *data = dev->data;
131 	struct display_buffer_descriptor mipi_desc;
132 
133 	int r;
134 	const uint8_t *write_data_start = (const uint8_t *)buf;
135 	uint16_t write_cnt;
136 	uint16_t nbr_of_writes;
137 	uint16_t write_h;
138 
139 	__ASSERT(desc->width <= desc->pitch, "Pitch is smaller than width");
140 	__ASSERT((desc->pitch * data->bytes_per_pixel * desc->height) <=
141 			 desc->buf_size,
142 		 "Input buffer too small");
143 
144 	LOG_DBG("Writing %dx%d (w,h) @ %dx%d (x,y)", desc->width, desc->height,
145 		x, y);
146 	r = ili9xxx_set_mem_area(dev, x, y, desc->width, desc->height);
147 	if (r < 0) {
148 		return r;
149 	}
150 
151 	if (desc->pitch > desc->width) {
152 		write_h = 1U;
153 		nbr_of_writes = desc->height;
154 		mipi_desc.height = 1;
155 		mipi_desc.buf_size = desc->pitch * data->bytes_per_pixel;
156 	} else {
157 		write_h = desc->height;
158 		mipi_desc.height = desc->height;
159 		mipi_desc.buf_size = desc->width * data->bytes_per_pixel * write_h;
160 		nbr_of_writes = 1U;
161 	}
162 
163 	mipi_desc.width = desc->width;
164 	/* Per MIPI API, pitch must always match width */
165 	mipi_desc.pitch = desc->width;
166 	mipi_desc.frame_incomplete = desc->frame_incomplete;
167 
168 	r = ili9xxx_transmit(dev, ILI9XXX_RAMWR, NULL, 0);
169 	if (r < 0) {
170 		return r;
171 	}
172 
173 	for (write_cnt = 0U; write_cnt < nbr_of_writes; ++write_cnt) {
174 		r = mipi_dbi_write_display(config->mipi_dev,
175 					   &config->dbi_config,
176 					   write_data_start,
177 					   &mipi_desc,
178 					   data->pixel_format);
179 		if (r < 0) {
180 			return r;
181 		}
182 
183 		write_data_start += desc->pitch * data->bytes_per_pixel;
184 	}
185 
186 	return 0;
187 }
188 
189 #ifdef CONFIG_ILI9XXX_READ
190 
ili9xxx_read(const struct device * dev,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc,void * buf)191 static int ili9xxx_read(const struct device *dev, const uint16_t x,
192 			const uint16_t y,
193 			const struct display_buffer_descriptor *desc, void *buf)
194 {
195 	const struct ili9xxx_config *config = dev->config;
196 	struct ili9xxx_data *data = dev->data;
197 	struct display_buffer_descriptor mipi_desc;
198 	int r;
199 	uint32_t gram_data, nbr_of_reads;
200 	uint16_t *read_data_start = (uint16_t *)buf;
201 
202 	if (data->pixel_format != PIXEL_FORMAT_RGB_565) {
203 		/* Only RGB565 can be supported, see note below */
204 		return -ENOTSUP;
205 	}
206 
207 	__ASSERT(desc->width <= desc->pitch, "Pitch is smaller than width");
208 	__ASSERT((desc->pitch * data->bytes_per_pixel * desc->height) <=
209 			 desc->buf_size,
210 		 "Output buffer too small");
211 
212 	LOG_DBG("Reading %dx%d (w,h) @ %dx%d (x,y)", desc->width, desc->height,
213 		x, y);
214 
215 	r = ili9xxx_set_mem_area(dev, x, y, desc->width, desc->height);
216 	if (r < 0) {
217 		return r;
218 	}
219 
220 	/*
221 	 * ILI9XXX stores all pixel data in graphics ram (GRAM) as 18 bit
222 	 * values. When using RGB565 pixel format, pixels are converted to
223 	 * 18 bit values via a lookup table. When using RGB888 format, the
224 	 * lower 2 bits of each pixel are simply dropped. When reading pixels,
225 	 * the response format will always look like so:
226 	 * | R[5:0] | x | x | G[5:0] | x | x | B[5:0] | x | x |
227 	 * Where x represents "don't care". The internal format of the
228 	 * ILI9XXX graphics RAM results in the following restrictions:
229 	 * - RGB888 mode can't be supported.
230 	 * - we can only read one pixel at once (since we need to do
231 	 *   byte manipulation on the output)
232 	 */
233 
234 	/* Setup MIPI descriptor to read 3 bytes (one pixel in GRAM) */
235 	mipi_desc.width = 1;
236 	mipi_desc.height = 1;
237 	/* Per MIPI API, pitch must always match width */
238 	mipi_desc.pitch = 1;
239 
240 	nbr_of_reads = desc->width * desc->height;
241 
242 	/* Initial read command should consist of RAMRD command, plus
243 	 * 8 dummy clock cycles
244 	 */
245 	uint8_t cmd[] = {ILI9XXX_RAMRD, 0xFF};
246 
247 	for (uint32_t read_cnt = 0; read_cnt < nbr_of_reads; read_cnt++) {
248 		r = mipi_dbi_command_read(config->mipi_dev,
249 					  &config->dbi_config,
250 					  cmd, sizeof(cmd),
251 					  (uint8_t *)&gram_data, 3);
252 		if (r < 0) {
253 			return r;
254 		}
255 
256 		/* Bitshift the graphics RAM data to RGB565.
257 		 * For more details on the formatting of this data,
258 		 * see "Read data through 4-line SPI mode" diagram
259 		 * on page 64 of datasheet.
260 		 */
261 		read_data_start[read_cnt] =
262 			((gram_data & 0xF80000) >> 11) | /* Blue */
263 			((gram_data & 0x1C00) << 3) |  /* Green */
264 			((gram_data & 0xE000) >> 13) |  /* Green */
265 			(gram_data & 0xF8); /* Red */
266 
267 		/* After first read, we should use read memory continue command */
268 		cmd[0] = ILI9XXX_RAMRD_CONT;
269 	}
270 
271 	return 0;
272 }
273 
274 #endif
275 
ili9xxx_display_blanking_off(const struct device * dev)276 static int ili9xxx_display_blanking_off(const struct device *dev)
277 {
278 	LOG_DBG("Turning display blanking off");
279 	return ili9xxx_transmit(dev, ILI9XXX_DISPON, NULL, 0);
280 }
281 
ili9xxx_display_blanking_on(const struct device * dev)282 static int ili9xxx_display_blanking_on(const struct device *dev)
283 {
284 	LOG_DBG("Turning display blanking on");
285 	return ili9xxx_transmit(dev, ILI9XXX_DISPOFF, NULL, 0);
286 }
287 
288 static int
ili9xxx_set_pixel_format(const struct device * dev,const enum display_pixel_format pixel_format)289 ili9xxx_set_pixel_format(const struct device *dev,
290 			 const enum display_pixel_format pixel_format)
291 {
292 	struct ili9xxx_data *data = dev->data;
293 
294 	int r;
295 	uint8_t tx_data;
296 	uint8_t bytes_per_pixel;
297 
298 	if (pixel_format == PIXEL_FORMAT_RGB_565) {
299 		bytes_per_pixel = 2U;
300 		tx_data = ILI9XXX_PIXSET_MCU_16_BIT | ILI9XXX_PIXSET_RGB_16_BIT;
301 	} else if (pixel_format == PIXEL_FORMAT_RGB_888) {
302 		bytes_per_pixel = 3U;
303 		tx_data = ILI9XXX_PIXSET_MCU_18_BIT | ILI9XXX_PIXSET_RGB_18_BIT;
304 	} else {
305 		LOG_ERR("Unsupported pixel format");
306 		return -ENOTSUP;
307 	}
308 
309 	r = ili9xxx_transmit(dev, ILI9XXX_PIXSET, &tx_data, 1U);
310 	if (r < 0) {
311 		return r;
312 	}
313 
314 	data->pixel_format = pixel_format;
315 	data->bytes_per_pixel = bytes_per_pixel;
316 
317 	return 0;
318 }
319 
ili9xxx_set_orientation(const struct device * dev,const enum display_orientation orientation)320 static int ili9xxx_set_orientation(const struct device *dev,
321 				   const enum display_orientation orientation)
322 {
323 	const struct ili9xxx_config *config = dev->config;
324 	struct ili9xxx_data *data = dev->data;
325 
326 	int r;
327 	uint8_t tx_data = ILI9XXX_MADCTL_BGR;
328 	if (config->quirks->cmd_set == CMD_SET_1) {
329 		if (orientation == DISPLAY_ORIENTATION_NORMAL) {
330 			tx_data |= ILI9XXX_MADCTL_MX;
331 		} else if (orientation == DISPLAY_ORIENTATION_ROTATED_90) {
332 			tx_data |= ILI9XXX_MADCTL_MV;
333 		} else if (orientation == DISPLAY_ORIENTATION_ROTATED_180) {
334 			tx_data |= ILI9XXX_MADCTL_MY;
335 		} else if (orientation == DISPLAY_ORIENTATION_ROTATED_270) {
336 			tx_data |= ILI9XXX_MADCTL_MV | ILI9XXX_MADCTL_MX |
337 				   ILI9XXX_MADCTL_MY;
338 		}
339 	} else if (config->quirks->cmd_set == CMD_SET_2) {
340 		if (orientation == DISPLAY_ORIENTATION_NORMAL) {
341 			/* Do nothing */
342 		} else if (orientation == DISPLAY_ORIENTATION_ROTATED_90) {
343 			tx_data |= ILI9XXX_MADCTL_MV | ILI9XXX_MADCTL_MY;
344 		} else if (orientation == DISPLAY_ORIENTATION_ROTATED_180) {
345 			tx_data |= ILI9XXX_MADCTL_MY | ILI9XXX_MADCTL_MX;
346 		} else if (orientation == DISPLAY_ORIENTATION_ROTATED_270) {
347 			tx_data |= ILI9XXX_MADCTL_MV | ILI9XXX_MADCTL_MX;
348 		}
349 	}
350 
351 	r = ili9xxx_transmit(dev, ILI9XXX_MADCTL, &tx_data, 1U);
352 	if (r < 0) {
353 		return r;
354 	}
355 
356 	data->orientation = orientation;
357 
358 	return 0;
359 }
360 
ili9xxx_get_capabilities(const struct device * dev,struct display_capabilities * capabilities)361 static void ili9xxx_get_capabilities(const struct device *dev,
362 				     struct display_capabilities *capabilities)
363 {
364 	struct ili9xxx_data *data = dev->data;
365 	const struct ili9xxx_config *config = dev->config;
366 
367 	memset(capabilities, 0, sizeof(struct display_capabilities));
368 
369 	capabilities->supported_pixel_formats =
370 		PIXEL_FORMAT_RGB_565 | PIXEL_FORMAT_RGB_888;
371 	capabilities->current_pixel_format = data->pixel_format;
372 
373 	if (data->orientation == DISPLAY_ORIENTATION_NORMAL ||
374 	    data->orientation == DISPLAY_ORIENTATION_ROTATED_180) {
375 		capabilities->x_resolution = config->x_resolution;
376 		capabilities->y_resolution = config->y_resolution;
377 	} else {
378 		capabilities->x_resolution = config->y_resolution;
379 		capabilities->y_resolution = config->x_resolution;
380 	}
381 
382 	capabilities->current_orientation = data->orientation;
383 }
384 
ili9xxx_configure(const struct device * dev)385 static int ili9xxx_configure(const struct device *dev)
386 {
387 	const struct ili9xxx_config *config = dev->config;
388 
389 	int r;
390 	enum display_pixel_format pixel_format;
391 	enum display_orientation orientation;
392 
393 	/* pixel format */
394 	if (config->pixel_format == ILI9XXX_PIXEL_FORMAT_RGB565) {
395 		pixel_format = PIXEL_FORMAT_RGB_565;
396 	} else {
397 		pixel_format = PIXEL_FORMAT_RGB_888;
398 	}
399 
400 	r = ili9xxx_set_pixel_format(dev, pixel_format);
401 	if (r < 0) {
402 		return r;
403 	}
404 
405 	/* orientation */
406 	if (config->rotation == 0U) {
407 		orientation = DISPLAY_ORIENTATION_NORMAL;
408 	} else if (config->rotation == 90U) {
409 		orientation = DISPLAY_ORIENTATION_ROTATED_90;
410 	} else if (config->rotation == 180U) {
411 		orientation = DISPLAY_ORIENTATION_ROTATED_180;
412 	} else {
413 		orientation = DISPLAY_ORIENTATION_ROTATED_270;
414 	}
415 
416 	r = ili9xxx_set_orientation(dev, orientation);
417 	if (r < 0) {
418 		return r;
419 	}
420 
421 	if (config->inversion) {
422 		r = ili9xxx_transmit(dev, ILI9XXX_DINVON, NULL, 0U);
423 		if (r < 0) {
424 			return r;
425 		}
426 	}
427 
428 	r = config->regs_init_fn(dev);
429 	if (r < 0) {
430 		return r;
431 	}
432 
433 	return 0;
434 }
435 
ili9xxx_init(const struct device * dev)436 static int ili9xxx_init(const struct device *dev)
437 {
438 	const struct ili9xxx_config *config = dev->config;
439 
440 	int r;
441 
442 	if (!device_is_ready(config->mipi_dev)) {
443 		LOG_ERR("MIPI DBI device is not ready");
444 		return -ENODEV;
445 	}
446 
447 	ili9xxx_hw_reset(dev);
448 
449 	r = ili9xxx_transmit(dev, ILI9XXX_SWRESET, NULL, 0);
450 	if (r < 0) {
451 		LOG_ERR("Error transmit command Software Reset (%d)", r);
452 		return r;
453 	}
454 
455 #ifdef CONFIG_ILI9XXX_READ
456 	/* Set RGB LUT table to enable display read API */
457 	ili9xxx_transmit(dev, ILI9XXX_RGBSET, ili9xxx_rgb_lut, sizeof(ili9xxx_rgb_lut));
458 #endif
459 
460 	k_sleep(K_MSEC(ILI9XXX_RESET_WAIT_TIME));
461 
462 	ili9xxx_display_blanking_on(dev);
463 
464 	r = ili9xxx_configure(dev);
465 	if (r < 0) {
466 		LOG_ERR("Could not configure display (%d)", r);
467 		return r;
468 	}
469 
470 	r = ili9xxx_exit_sleep(dev);
471 	if (r < 0) {
472 		LOG_ERR("Could not exit sleep mode (%d)", r);
473 		return r;
474 	}
475 
476 	return 0;
477 }
478 
479 static DEVICE_API(display, ili9xxx_api) = {
480 	.blanking_on = ili9xxx_display_blanking_on,
481 	.blanking_off = ili9xxx_display_blanking_off,
482 	.write = ili9xxx_write,
483 #ifdef CONFIG_ILI9XXX_READ
484 	.read = ili9xxx_read,
485 #endif
486 	.get_capabilities = ili9xxx_get_capabilities,
487 	.set_pixel_format = ili9xxx_set_pixel_format,
488 	.set_orientation = ili9xxx_set_orientation,
489 };
490 
491 #ifdef CONFIG_ILI9340
492 static const struct ili9xxx_quirks ili9340_quirks = {
493 	.cmd_set = CMD_SET_1,
494 };
495 #endif
496 
497 #ifdef CONFIG_ILI9341
498 static const struct ili9xxx_quirks ili9341_quirks = {
499 	.cmd_set = CMD_SET_1,
500 };
501 #endif
502 
503 #ifdef CONFIG_ILI9342C
504 static const struct ili9xxx_quirks ili9342c_quirks = {
505 	.cmd_set = CMD_SET_2,
506 };
507 #endif
508 
509 #ifdef CONFIG_ILI9488
510 static const struct ili9xxx_quirks ili9488_quirks = {
511 	.cmd_set = CMD_SET_1,
512 };
513 #endif
514 
515 #define INST_DT_ILI9XXX(n, t) DT_INST(n, ilitek_ili##t)
516 
517 #define ILI9XXX_INIT(n, t)                                                     \
518 	ILI##t##_REGS_INIT(n);                                                 \
519 									       \
520 	static const struct ili9xxx_config ili9##t##_config_##n = {            \
521 		.quirks = &ili##t##_quirks,                                    \
522 		.mipi_dev = DEVICE_DT_GET(DT_PARENT(INST_DT_ILI9XXX(n, t))),   \
523 		.dbi_config = {                                                \
524 			.mode = DT_STRING_UPPER_TOKEN_OR(                      \
525 				INST_DT_ILI9XXX(n, t),                         \
526 				mipi_mode, MIPI_DBI_MODE_SPI_4WIRE),           \
527 			.config = MIPI_DBI_SPI_CONFIG_DT(                      \
528 						INST_DT_ILI9XXX(n, t),         \
529 						SPI_OP_MODE_MASTER |           \
530 						SPI_WORD_SET(8),               \
531 						0),                            \
532 		},                                                             \
533 		.pixel_format = DT_PROP(INST_DT_ILI9XXX(n, t), pixel_format),  \
534 		.rotation = DT_PROP(INST_DT_ILI9XXX(n, t), rotation),          \
535 		.x_resolution = ILI##t##_X_RES,                                \
536 		.y_resolution = ILI##t##_Y_RES,                                \
537 		.inversion = DT_PROP(INST_DT_ILI9XXX(n, t), display_inversion),\
538 		.regs = &ili##t##_regs_##n,                                    \
539 		.regs_init_fn = ili##t##_regs_init,                            \
540 	};                                                                     \
541 									       \
542 	static struct ili9xxx_data ili9##t##_data_##n;                         \
543 									       \
544 	DEVICE_DT_DEFINE(INST_DT_ILI9XXX(n, t), ili9xxx_init,                  \
545 			    NULL, &ili9##t##_data_##n,                         \
546 			    &ili9##t##_config_##n, POST_KERNEL,                \
547 			    CONFIG_DISPLAY_INIT_PRIORITY, &ili9xxx_api)
548 
549 #define DT_INST_FOREACH_ILI9XXX_STATUS_OKAY(t)                                 \
550 	LISTIFY(DT_NUM_INST_STATUS_OKAY(ilitek_ili##t), ILI9XXX_INIT, (;), t)
551 
552 #ifdef CONFIG_ILI9340
553 #include "display_ili9340.h"
554 DT_INST_FOREACH_ILI9XXX_STATUS_OKAY(9340);
555 #endif
556 
557 #ifdef CONFIG_ILI9341
558 #include "display_ili9341.h"
559 DT_INST_FOREACH_ILI9XXX_STATUS_OKAY(9341);
560 #endif
561 
562 #ifdef CONFIG_ILI9342C
563 #include "display_ili9342c.h"
564 DT_INST_FOREACH_ILI9XXX_STATUS_OKAY(9342c);
565 #endif
566 
567 #ifdef CONFIG_ILI9488
568 #include "display_ili9488.h"
569 DT_INST_FOREACH_ILI9XXX_STATUS_OKAY(9488);
570 #endif
571