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 
ili9xxx_transmit(const struct device * dev,uint8_t cmd,const void * tx_data,size_t tx_len)24 int ili9xxx_transmit(const struct device *dev, uint8_t cmd, const void *tx_data,
25 		     size_t tx_len)
26 {
27 	const struct ili9xxx_config *config = dev->config;
28 
29 	int r;
30 	struct spi_buf tx_buf;
31 	struct spi_buf_set tx_bufs = { .buffers = &tx_buf, .count = 1U };
32 
33 	/* send command */
34 	tx_buf.buf = &cmd;
35 	tx_buf.len = 1U;
36 
37 	gpio_pin_set_dt(&config->cmd_data, ILI9XXX_CMD);
38 	r = spi_write_dt(&config->spi, &tx_bufs);
39 	if (r < 0) {
40 		return r;
41 	}
42 
43 	/* send data (if any) */
44 	if (tx_data != NULL) {
45 		tx_buf.buf = (void *)tx_data;
46 		tx_buf.len = tx_len;
47 
48 		gpio_pin_set_dt(&config->cmd_data, ILI9XXX_DATA);
49 		r = spi_write_dt(&config->spi, &tx_bufs);
50 		if (r < 0) {
51 			return r;
52 		}
53 	}
54 
55 	return 0;
56 }
57 
ili9xxx_exit_sleep(const struct device * dev)58 static int ili9xxx_exit_sleep(const struct device *dev)
59 {
60 	int r;
61 
62 	r = ili9xxx_transmit(dev, ILI9XXX_SLPOUT, NULL, 0);
63 	if (r < 0) {
64 		return r;
65 	}
66 
67 	k_sleep(K_MSEC(ILI9XXX_SLEEP_OUT_TIME));
68 
69 	return 0;
70 }
71 
ili9xxx_hw_reset(const struct device * dev)72 static void ili9xxx_hw_reset(const struct device *dev)
73 {
74 	const struct ili9xxx_config *config = dev->config;
75 
76 	if (config->reset.port == NULL) {
77 		return;
78 	}
79 
80 	gpio_pin_set_dt(&config->reset, 1);
81 	k_sleep(K_MSEC(ILI9XXX_RESET_PULSE_TIME));
82 	gpio_pin_set_dt(&config->reset, 0);
83 
84 	k_sleep(K_MSEC(ILI9XXX_RESET_WAIT_TIME));
85 }
86 
ili9xxx_set_mem_area(const struct device * dev,const uint16_t x,const uint16_t y,const uint16_t w,const uint16_t h)87 static int ili9xxx_set_mem_area(const struct device *dev, const uint16_t x,
88 				const uint16_t y, const uint16_t w,
89 				const uint16_t h)
90 {
91 	int r;
92 	uint16_t spi_data[2];
93 
94 	spi_data[0] = sys_cpu_to_be16(x);
95 	spi_data[1] = sys_cpu_to_be16(x + w - 1U);
96 	r = ili9xxx_transmit(dev, ILI9XXX_CASET, &spi_data[0], 4U);
97 	if (r < 0) {
98 		return r;
99 	}
100 
101 	spi_data[0] = sys_cpu_to_be16(y);
102 	spi_data[1] = sys_cpu_to_be16(y + h - 1U);
103 	r = ili9xxx_transmit(dev, ILI9XXX_PASET, &spi_data[0], 4U);
104 	if (r < 0) {
105 		return r;
106 	}
107 
108 	return 0;
109 }
110 
ili9xxx_write(const struct device * dev,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc,const void * buf)111 static int ili9xxx_write(const struct device *dev, const uint16_t x,
112 			 const uint16_t y,
113 			 const struct display_buffer_descriptor *desc,
114 			 const void *buf)
115 {
116 	const struct ili9xxx_config *config = dev->config;
117 	struct ili9xxx_data *data = dev->data;
118 
119 	int r;
120 	const uint8_t *write_data_start = (const uint8_t *)buf;
121 	struct spi_buf tx_buf;
122 	struct spi_buf_set tx_bufs;
123 	uint16_t write_cnt;
124 	uint16_t nbr_of_writes;
125 	uint16_t write_h;
126 
127 	__ASSERT(desc->width <= desc->pitch, "Pitch is smaller than width");
128 	__ASSERT((desc->pitch * data->bytes_per_pixel * desc->height) <=
129 			 desc->buf_size,
130 		 "Input buffer to small");
131 
132 	LOG_DBG("Writing %dx%d (w,h) @ %dx%d (x,y)", desc->width, desc->height,
133 		x, y);
134 	r = ili9xxx_set_mem_area(dev, x, y, desc->width, desc->height);
135 	if (r < 0) {
136 		return r;
137 	}
138 
139 	if (desc->pitch > desc->width) {
140 		write_h = 1U;
141 		nbr_of_writes = desc->height;
142 	} else {
143 		write_h = desc->height;
144 		nbr_of_writes = 1U;
145 	}
146 
147 	r = ili9xxx_transmit(dev, ILI9XXX_RAMWR, write_data_start,
148 			     desc->width * data->bytes_per_pixel * write_h);
149 	if (r < 0) {
150 		return r;
151 	}
152 
153 	tx_bufs.buffers = &tx_buf;
154 	tx_bufs.count = 1;
155 
156 	write_data_start += desc->pitch * data->bytes_per_pixel;
157 	for (write_cnt = 1U; write_cnt < nbr_of_writes; ++write_cnt) {
158 		tx_buf.buf = (void *)write_data_start;
159 		tx_buf.len = desc->width * data->bytes_per_pixel * write_h;
160 
161 		r = spi_write_dt(&config->spi, &tx_bufs);
162 		if (r < 0) {
163 			return r;
164 		}
165 
166 		write_data_start += desc->pitch * data->bytes_per_pixel;
167 	}
168 
169 	return 0;
170 }
171 
ili9xxx_read(const struct device * dev,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc,void * buf)172 static int ili9xxx_read(const struct device *dev, const uint16_t x,
173 			const uint16_t y,
174 			const struct display_buffer_descriptor *desc, void *buf)
175 {
176 	LOG_ERR("Reading not supported");
177 	return -ENOTSUP;
178 }
179 
ili9xxx_get_framebuffer(const struct device * dev)180 static void *ili9xxx_get_framebuffer(const struct device *dev)
181 {
182 	LOG_ERR("Direct framebuffer access not supported");
183 	return NULL;
184 }
185 
ili9xxx_display_blanking_off(const struct device * dev)186 static int ili9xxx_display_blanking_off(const struct device *dev)
187 {
188 	LOG_DBG("Turning display blanking off");
189 	return ili9xxx_transmit(dev, ILI9XXX_DISPON, NULL, 0);
190 }
191 
ili9xxx_display_blanking_on(const struct device * dev)192 static int ili9xxx_display_blanking_on(const struct device *dev)
193 {
194 	LOG_DBG("Turning display blanking on");
195 	return ili9xxx_transmit(dev, ILI9XXX_DISPOFF, NULL, 0);
196 }
197 
ili9xxx_set_brightness(const struct device * dev,const uint8_t brightness)198 static int ili9xxx_set_brightness(const struct device *dev,
199 				  const uint8_t brightness)
200 {
201 	LOG_ERR("Set brightness not implemented");
202 	return -ENOTSUP;
203 }
204 
ili9xxx_set_contrast(const struct device * dev,const uint8_t contrast)205 static int ili9xxx_set_contrast(const struct device *dev,
206 				const uint8_t contrast)
207 {
208 	LOG_ERR("Set contrast not supported");
209 	return -ENOTSUP;
210 }
211 
212 static int
ili9xxx_set_pixel_format(const struct device * dev,const enum display_pixel_format pixel_format)213 ili9xxx_set_pixel_format(const struct device *dev,
214 			 const enum display_pixel_format pixel_format)
215 {
216 	struct ili9xxx_data *data = dev->data;
217 
218 	int r;
219 	uint8_t tx_data;
220 	uint8_t bytes_per_pixel;
221 
222 	if (pixel_format == PIXEL_FORMAT_RGB_565) {
223 		bytes_per_pixel = 2U;
224 		tx_data = ILI9XXX_PIXSET_MCU_16_BIT | ILI9XXX_PIXSET_RGB_16_BIT;
225 	} else if (pixel_format == PIXEL_FORMAT_RGB_888) {
226 		bytes_per_pixel = 3U;
227 		tx_data = ILI9XXX_PIXSET_MCU_18_BIT | ILI9XXX_PIXSET_RGB_18_BIT;
228 	} else {
229 		LOG_ERR("Unsupported pixel format");
230 		return -ENOTSUP;
231 	}
232 
233 	r = ili9xxx_transmit(dev, ILI9XXX_PIXSET, &tx_data, 1U);
234 	if (r < 0) {
235 		return r;
236 	}
237 
238 	data->pixel_format = pixel_format;
239 	data->bytes_per_pixel = bytes_per_pixel;
240 
241 	return 0;
242 }
243 
ili9xxx_set_orientation(const struct device * dev,const enum display_orientation orientation)244 static int ili9xxx_set_orientation(const struct device *dev,
245 				   const enum display_orientation orientation)
246 {
247 	const struct ili9xxx_config *config = dev->config;
248 	struct ili9xxx_data *data = dev->data;
249 
250 	int r;
251 	uint8_t tx_data = ILI9XXX_MADCTL_BGR;
252 	if (config->quirks->cmd_set == CMD_SET_1) {
253 		if (orientation == DISPLAY_ORIENTATION_NORMAL) {
254 			tx_data |= ILI9XXX_MADCTL_MX;
255 		} else if (orientation == DISPLAY_ORIENTATION_ROTATED_90) {
256 			tx_data |= ILI9XXX_MADCTL_MV;
257 		} else if (orientation == DISPLAY_ORIENTATION_ROTATED_180) {
258 			tx_data |= ILI9XXX_MADCTL_MY;
259 		} else if (orientation == DISPLAY_ORIENTATION_ROTATED_270) {
260 			tx_data |= ILI9XXX_MADCTL_MV | ILI9XXX_MADCTL_MX |
261 				   ILI9XXX_MADCTL_MY;
262 		}
263 	} else if (config->quirks->cmd_set == CMD_SET_2) {
264 		if (orientation == DISPLAY_ORIENTATION_NORMAL) {
265 			/* Do nothing */
266 		} else if (orientation == DISPLAY_ORIENTATION_ROTATED_90) {
267 			tx_data |= ILI9XXX_MADCTL_MV | ILI9XXX_MADCTL_MY;
268 		} else if (orientation == DISPLAY_ORIENTATION_ROTATED_180) {
269 			tx_data |= ILI9XXX_MADCTL_MY | ILI9XXX_MADCTL_MX;
270 		} else if (orientation == DISPLAY_ORIENTATION_ROTATED_270) {
271 			tx_data |= ILI9XXX_MADCTL_MV | ILI9XXX_MADCTL_MX;
272 		}
273 	}
274 
275 	r = ili9xxx_transmit(dev, ILI9XXX_MADCTL, &tx_data, 1U);
276 	if (r < 0) {
277 		return r;
278 	}
279 
280 	data->orientation = orientation;
281 
282 	return 0;
283 }
284 
ili9xxx_get_capabilities(const struct device * dev,struct display_capabilities * capabilities)285 static void ili9xxx_get_capabilities(const struct device *dev,
286 				     struct display_capabilities *capabilities)
287 {
288 	struct ili9xxx_data *data = dev->data;
289 	const struct ili9xxx_config *config = dev->config;
290 
291 	memset(capabilities, 0, sizeof(struct display_capabilities));
292 
293 	capabilities->supported_pixel_formats =
294 		PIXEL_FORMAT_RGB_565 | PIXEL_FORMAT_RGB_888;
295 	capabilities->current_pixel_format = data->pixel_format;
296 
297 	if (data->orientation == DISPLAY_ORIENTATION_NORMAL ||
298 	    data->orientation == DISPLAY_ORIENTATION_ROTATED_180) {
299 		capabilities->x_resolution = config->x_resolution;
300 		capabilities->y_resolution = config->y_resolution;
301 	} else {
302 		capabilities->x_resolution = config->y_resolution;
303 		capabilities->y_resolution = config->x_resolution;
304 	}
305 
306 	capabilities->current_orientation = data->orientation;
307 }
308 
ili9xxx_configure(const struct device * dev)309 static int ili9xxx_configure(const struct device *dev)
310 {
311 	const struct ili9xxx_config *config = dev->config;
312 
313 	int r;
314 	enum display_pixel_format pixel_format;
315 	enum display_orientation orientation;
316 
317 	/* pixel format */
318 	if (config->pixel_format == ILI9XXX_PIXEL_FORMAT_RGB565) {
319 		pixel_format = PIXEL_FORMAT_RGB_565;
320 	} else {
321 		pixel_format = PIXEL_FORMAT_RGB_888;
322 	}
323 
324 	r = ili9xxx_set_pixel_format(dev, pixel_format);
325 	if (r < 0) {
326 		return r;
327 	}
328 
329 	/* orientation */
330 	if (config->rotation == 0U) {
331 		orientation = DISPLAY_ORIENTATION_NORMAL;
332 	} else if (config->rotation == 90U) {
333 		orientation = DISPLAY_ORIENTATION_ROTATED_90;
334 	} else if (config->rotation == 180U) {
335 		orientation = DISPLAY_ORIENTATION_ROTATED_180;
336 	} else {
337 		orientation = DISPLAY_ORIENTATION_ROTATED_270;
338 	}
339 
340 	r = ili9xxx_set_orientation(dev, orientation);
341 	if (r < 0) {
342 		return r;
343 	}
344 
345 	if (config->inversion) {
346 		r = ili9xxx_transmit(dev, ILI9XXX_DINVON, NULL, 0U);
347 		if (r < 0) {
348 			return r;
349 		}
350 	}
351 
352 	r = config->regs_init_fn(dev);
353 	if (r < 0) {
354 		return r;
355 	}
356 
357 	return 0;
358 }
359 
ili9xxx_init(const struct device * dev)360 static int ili9xxx_init(const struct device *dev)
361 {
362 	const struct ili9xxx_config *config = dev->config;
363 
364 	int r;
365 
366 	if (!spi_is_ready_dt(&config->spi)) {
367 		LOG_ERR("SPI device is not ready");
368 		return -ENODEV;
369 	}
370 
371 	if (!gpio_is_ready_dt(&config->cmd_data)) {
372 		LOG_ERR("Command/Data GPIO device not ready");
373 		return -ENODEV;
374 	}
375 
376 	r = gpio_pin_configure_dt(&config->cmd_data, GPIO_OUTPUT);
377 	if (r < 0) {
378 		LOG_ERR("Could not configure command/data GPIO (%d)", r);
379 		return r;
380 	}
381 
382 	if (config->reset.port != NULL) {
383 		if (!gpio_is_ready_dt(&config->reset)) {
384 			LOG_ERR("Reset GPIO device not ready");
385 			return -ENODEV;
386 		}
387 
388 		r = gpio_pin_configure_dt(&config->reset, GPIO_OUTPUT_INACTIVE);
389 		if (r < 0) {
390 			LOG_ERR("Could not configure reset GPIO (%d)", r);
391 			return r;
392 		}
393 	}
394 
395 	ili9xxx_hw_reset(dev);
396 
397 	r = ili9xxx_transmit(dev, ILI9XXX_SWRESET, NULL, 0);
398 	if (r < 0) {
399 		LOG_ERR("Error transmit command Software Reset (%d)", r);
400 		return r;
401 	}
402 
403 	k_sleep(K_MSEC(ILI9XXX_RESET_WAIT_TIME));
404 
405 	ili9xxx_display_blanking_on(dev);
406 
407 	r = ili9xxx_configure(dev);
408 	if (r < 0) {
409 		LOG_ERR("Could not configure display (%d)", r);
410 		return r;
411 	}
412 
413 	r = ili9xxx_exit_sleep(dev);
414 	if (r < 0) {
415 		LOG_ERR("Could not exit sleep mode (%d)", r);
416 		return r;
417 	}
418 
419 	return 0;
420 }
421 
422 static const struct display_driver_api ili9xxx_api = {
423 	.blanking_on = ili9xxx_display_blanking_on,
424 	.blanking_off = ili9xxx_display_blanking_off,
425 	.write = ili9xxx_write,
426 	.read = ili9xxx_read,
427 	.get_framebuffer = ili9xxx_get_framebuffer,
428 	.set_brightness = ili9xxx_set_brightness,
429 	.set_contrast = ili9xxx_set_contrast,
430 	.get_capabilities = ili9xxx_get_capabilities,
431 	.set_pixel_format = ili9xxx_set_pixel_format,
432 	.set_orientation = ili9xxx_set_orientation,
433 };
434 
435 #ifdef CONFIG_ILI9340
436 static const struct ili9xxx_quirks ili9340_quirks = {
437 	.cmd_set = CMD_SET_1,
438 };
439 #endif
440 
441 #ifdef CONFIG_ILI9341
442 static const struct ili9xxx_quirks ili9341_quirks = {
443 	.cmd_set = CMD_SET_1,
444 };
445 #endif
446 
447 #ifdef CONFIG_ILI9342C
448 static const struct ili9xxx_quirks ili9342c_quirks = {
449 	.cmd_set = CMD_SET_2,
450 };
451 #endif
452 
453 #ifdef CONFIG_ILI9488
454 static const struct ili9xxx_quirks ili9488_quirks = {
455 	.cmd_set = CMD_SET_1,
456 };
457 #endif
458 
459 #define INST_DT_ILI9XXX(n, t) DT_INST(n, ilitek_ili##t)
460 
461 #define ILI9XXX_INIT(n, t)                                                     \
462 	ILI##t##_REGS_INIT(n);                                                 \
463 									       \
464 	static const struct ili9xxx_config ili9xxx_config_##n = {              \
465 		.quirks = &ili##t##_quirks,                                    \
466 		.spi = SPI_DT_SPEC_GET(INST_DT_ILI9XXX(n, t),                  \
467 				       SPI_OP_MODE_MASTER | SPI_WORD_SET(8),   \
468 				       0),                                     \
469 		.cmd_data = GPIO_DT_SPEC_GET(INST_DT_ILI9XXX(n, t),            \
470 					     cmd_data_gpios),                  \
471 		.reset = GPIO_DT_SPEC_GET_OR(INST_DT_ILI9XXX(n, t),            \
472 					     reset_gpios, {0}),                \
473 		.pixel_format = DT_PROP(INST_DT_ILI9XXX(n, t), pixel_format),  \
474 		.rotation = DT_PROP(INST_DT_ILI9XXX(n, t), rotation),          \
475 		.x_resolution = ILI##t##_X_RES,                                \
476 		.y_resolution = ILI##t##_Y_RES,                                \
477 		.inversion = DT_PROP(INST_DT_ILI9XXX(n, t), display_inversion),\
478 		.regs = &ili9xxx_regs_##n,                                     \
479 		.regs_init_fn = ili##t##_regs_init,                            \
480 	};                                                                     \
481 									       \
482 	static struct ili9xxx_data ili9xxx_data_##n;                           \
483 									       \
484 	DEVICE_DT_DEFINE(INST_DT_ILI9XXX(n, t), ili9xxx_init,                  \
485 			    NULL, &ili9xxx_data_##n,                           \
486 			    &ili9xxx_config_##n, POST_KERNEL,                  \
487 			    CONFIG_DISPLAY_INIT_PRIORITY, &ili9xxx_api)
488 
489 #define DT_INST_FOREACH_ILI9XXX_STATUS_OKAY(t)                                 \
490 	LISTIFY(DT_NUM_INST_STATUS_OKAY(ilitek_ili##t), ILI9XXX_INIT, (;), t)
491 
492 #ifdef CONFIG_ILI9340
493 #include "display_ili9340.h"
494 DT_INST_FOREACH_ILI9XXX_STATUS_OKAY(9340);
495 #endif
496 
497 #ifdef CONFIG_ILI9341
498 #include "display_ili9341.h"
499 DT_INST_FOREACH_ILI9XXX_STATUS_OKAY(9341);
500 #endif
501 
502 #ifdef CONFIG_ILI9342C
503 #include "display_ili9342c.h"
504 DT_INST_FOREACH_ILI9XXX_STATUS_OKAY(9342c);
505 #endif
506 
507 #ifdef CONFIG_ILI9488
508 #include "display_ili9488.h"
509 DT_INST_FOREACH_ILI9XXX_STATUS_OKAY(9488);
510 #endif
511