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 <dt-bindings/display/ili9xxx.h>
12 #include <drivers/display.h>
13 #include <sys/byteorder.h>
14 
15 #include <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 	struct ili9xxx_data *data = dev->data;
248 
249 	int r;
250 	uint8_t tx_data = ILI9XXX_MADCTL_BGR;
251 
252 	if (orientation == DISPLAY_ORIENTATION_NORMAL) {
253 		tx_data |= ILI9XXX_MADCTL_MX;
254 	} else if (orientation == DISPLAY_ORIENTATION_ROTATED_90) {
255 		tx_data |= ILI9XXX_MADCTL_MV;
256 	} else if (orientation == DISPLAY_ORIENTATION_ROTATED_180) {
257 		tx_data |= ILI9XXX_MADCTL_MY;
258 	} else if (orientation == DISPLAY_ORIENTATION_ROTATED_270) {
259 		tx_data |= ILI9XXX_MADCTL_MV | ILI9XXX_MADCTL_MX |
260 			   ILI9XXX_MADCTL_MY;
261 	}
262 
263 	r = ili9xxx_transmit(dev, ILI9XXX_MADCTL, &tx_data, 1U);
264 	if (r < 0) {
265 		return r;
266 	}
267 
268 	data->orientation = orientation;
269 
270 	return 0;
271 }
272 
ili9xxx_get_capabilities(const struct device * dev,struct display_capabilities * capabilities)273 static void ili9xxx_get_capabilities(const struct device *dev,
274 				     struct display_capabilities *capabilities)
275 {
276 	struct ili9xxx_data *data = dev->data;
277 	const struct ili9xxx_config *config = dev->config;
278 
279 	memset(capabilities, 0, sizeof(struct display_capabilities));
280 
281 	capabilities->supported_pixel_formats =
282 		PIXEL_FORMAT_RGB_565 | PIXEL_FORMAT_RGB_888;
283 	capabilities->current_pixel_format = data->pixel_format;
284 
285 	if (data->orientation == DISPLAY_ORIENTATION_NORMAL ||
286 	    data->orientation == DISPLAY_ORIENTATION_ROTATED_180) {
287 		capabilities->x_resolution = config->x_resolution;
288 		capabilities->y_resolution = config->y_resolution;
289 	} else {
290 		capabilities->x_resolution = config->y_resolution;
291 		capabilities->y_resolution = config->x_resolution;
292 	}
293 
294 	capabilities->current_orientation = data->orientation;
295 }
296 
ili9xxx_configure(const struct device * dev)297 static int ili9xxx_configure(const struct device *dev)
298 {
299 	const struct ili9xxx_config *config = dev->config;
300 
301 	int r;
302 	enum display_pixel_format pixel_format;
303 	enum display_orientation orientation;
304 
305 	/* pixel format */
306 	if (config->pixel_format == ILI9XXX_PIXEL_FORMAT_RGB565) {
307 		pixel_format = PIXEL_FORMAT_RGB_565;
308 	} else {
309 		pixel_format = PIXEL_FORMAT_RGB_888;
310 	}
311 
312 	r = ili9xxx_set_pixel_format(dev, pixel_format);
313 	if (r < 0) {
314 		return r;
315 	}
316 
317 	/* orientation */
318 	if (config->rotation == 0U) {
319 		orientation = DISPLAY_ORIENTATION_NORMAL;
320 	} else if (config->rotation == 90U) {
321 		orientation = DISPLAY_ORIENTATION_ROTATED_90;
322 	} else if (config->rotation == 180U) {
323 		orientation = DISPLAY_ORIENTATION_ROTATED_180;
324 	} else {
325 		orientation = DISPLAY_ORIENTATION_ROTATED_270;
326 	}
327 
328 	r = ili9xxx_set_orientation(dev, orientation);
329 	if (r < 0) {
330 		return r;
331 	}
332 
333 	if (config->inversion) {
334 		r = ili9xxx_transmit(dev, ILI9XXX_DINVON, NULL, 0U);
335 		if (r < 0) {
336 			return r;
337 		}
338 	}
339 
340 	r = config->regs_init_fn(dev);
341 	if (r < 0) {
342 		return r;
343 	}
344 
345 	return 0;
346 }
347 
ili9xxx_init(const struct device * dev)348 static int ili9xxx_init(const struct device *dev)
349 {
350 	const struct ili9xxx_config *config = dev->config;
351 
352 	int r;
353 
354 	if (!spi_is_ready(&config->spi)) {
355 		LOG_ERR("SPI device is not ready");
356 		return -ENODEV;
357 	}
358 
359 	if (!device_is_ready(config->cmd_data.port)) {
360 		LOG_ERR("Command/Data GPIO device not ready");
361 		return -ENODEV;
362 	}
363 
364 	r = gpio_pin_configure_dt(&config->cmd_data, GPIO_OUTPUT);
365 	if (r < 0) {
366 		LOG_ERR("Could not configure command/data GPIO (%d)", r);
367 		return r;
368 	}
369 
370 	if (config->reset.port != NULL) {
371 		if (!device_is_ready(config->reset.port)) {
372 			LOG_ERR("Reset GPIO device not ready");
373 			return -ENODEV;
374 		}
375 
376 		r = gpio_pin_configure_dt(&config->reset, GPIO_OUTPUT_INACTIVE);
377 		if (r < 0) {
378 			LOG_ERR("Could not configure reset GPIO (%d)", r);
379 			return r;
380 		}
381 	}
382 
383 	ili9xxx_hw_reset(dev);
384 
385 	r = ili9xxx_transmit(dev, ILI9XXX_SWRESET, NULL, 0);
386 	if (r < 0) {
387 		LOG_ERR("Error transmit command Software Reset (%d)", r);
388 		return r;
389 	}
390 
391 	k_sleep(K_MSEC(ILI9XXX_RESET_WAIT_TIME));
392 
393 	ili9xxx_display_blanking_on(dev);
394 
395 	r = ili9xxx_configure(dev);
396 	if (r < 0) {
397 		LOG_ERR("Could not configure display (%d)", r);
398 		return r;
399 	}
400 
401 	r = ili9xxx_exit_sleep(dev);
402 	if (r < 0) {
403 		LOG_ERR("Could not exit sleep mode (%d)", r);
404 		return r;
405 	}
406 
407 	return 0;
408 }
409 
410 static const struct display_driver_api ili9xxx_api = {
411 	.blanking_on = ili9xxx_display_blanking_on,
412 	.blanking_off = ili9xxx_display_blanking_off,
413 	.write = ili9xxx_write,
414 	.read = ili9xxx_read,
415 	.get_framebuffer = ili9xxx_get_framebuffer,
416 	.set_brightness = ili9xxx_set_brightness,
417 	.set_contrast = ili9xxx_set_contrast,
418 	.get_capabilities = ili9xxx_get_capabilities,
419 	.set_pixel_format = ili9xxx_set_pixel_format,
420 	.set_orientation = ili9xxx_set_orientation,
421 };
422 
423 #define INST_DT_ILI9XXX(n, t) DT_INST(n, ilitek_ili##t)
424 
425 #define ILI9XXX_INIT(n, t)                                                     \
426 	ILI##t##_REGS_INIT(n);                                                 \
427 									       \
428 	static const struct ili9xxx_config ili9xxx_config_##n = {              \
429 		.spi = SPI_DT_SPEC_GET(INST_DT_ILI9XXX(n, t),                  \
430 				       SPI_OP_MODE_MASTER | SPI_WORD_SET(8),   \
431 				       0),                                     \
432 		.cmd_data = GPIO_DT_SPEC_GET(INST_DT_ILI9XXX(n, t),            \
433 					     cmd_data_gpios),                  \
434 		.reset = GPIO_DT_SPEC_GET_OR(INST_DT_ILI9XXX(n, t),            \
435 					     reset_gpios, {0}),                \
436 		.pixel_format = DT_PROP(INST_DT_ILI9XXX(n, t), pixel_format),  \
437 		.rotation = DT_PROP(INST_DT_ILI9XXX(n, t), rotation),          \
438 		.x_resolution = ILI##t##_X_RES,                                \
439 		.y_resolution = ILI##t##_Y_RES,                                \
440 		.inversion = DT_PROP(INST_DT_ILI9XXX(n, t), display_inversion),\
441 		.regs = &ili9xxx_regs_##n,                                     \
442 		.regs_init_fn = ili##t##_regs_init,                            \
443 	};                                                                     \
444 									       \
445 	static struct ili9xxx_data ili9xxx_data_##n;                           \
446 									       \
447 	DEVICE_DT_DEFINE(INST_DT_ILI9XXX(n, t), ili9xxx_init,                  \
448 			    NULL, &ili9xxx_data_##n,                           \
449 			    &ili9xxx_config_##n, POST_KERNEL,                  \
450 			    CONFIG_DISPLAY_INIT_PRIORITY, &ili9xxx_api);
451 
452 #define DT_INST_FOREACH_ILI9XXX_STATUS_OKAY(t)                                 \
453 	UTIL_LISTIFY(DT_NUM_INST_STATUS_OKAY(ilitek_ili##t), ILI9XXX_INIT, t)
454 
455 #ifdef CONFIG_ILI9340
456 #include "display_ili9340.h"
457 DT_INST_FOREACH_ILI9XXX_STATUS_OKAY(9340);
458 #endif
459 
460 #ifdef CONFIG_ILI9341
461 #include "display_ili9341.h"
462 DT_INST_FOREACH_ILI9XXX_STATUS_OKAY(9341);
463 #endif
464 
465 #ifdef CONFIG_ILI9488
466 #include "display_ili9488.h"
467 DT_INST_FOREACH_ILI9XXX_STATUS_OKAY(9488);
468 #endif
469