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