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