1 /*
2 * Copyright (c) 2022 Andreas Sandberg
3 * Copyright (c) 2020 PHYTEC Messtechnik GmbH
4 * Copyright 2024 NXP
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <string.h>
10 #include <zephyr/device.h>
11 #include <zephyr/init.h>
12 #include <zephyr/drivers/display.h>
13 #include <zephyr/drivers/gpio.h>
14 #include <zephyr/drivers/mipi_dbi.h>
15 #include <zephyr/sys/byteorder.h>
16
17 #include "uc81xx_regs.h"
18
19 #include <zephyr/logging/log.h>
20 LOG_MODULE_REGISTER(uc81xx, CONFIG_DISPLAY_LOG_LEVEL);
21
22 /**
23 * UC81XX compatible EPD controller driver.
24 *
25 * Currently only the black/white panels are supported (KW mode),
26 * also first gate/source should be 0.
27 */
28
29 #define UC81XX_PIXELS_PER_BYTE 8U
30
31 struct uc81xx_dt_array {
32 uint8_t *data;
33 uint8_t len;
34 };
35
36 enum uc81xx_profile_type {
37 UC81XX_PROFILE_FULL = 0,
38 UC81XX_PROFILE_PARTIAL,
39 UC81XX_NUM_PROFILES,
40 UC81XX_PROFILE_INVALID = UC81XX_NUM_PROFILES,
41 };
42
43 struct uc81xx_profile {
44 struct uc81xx_dt_array pwr;
45
46 uint8_t cdi;
47 bool override_cdi;
48 uint8_t tcon;
49 bool override_tcon;
50 uint8_t pll;
51 bool override_pll;
52 uint8_t vdcs;
53 bool override_vdcs;
54
55 const struct uc81xx_dt_array lutc;
56 const struct uc81xx_dt_array lutww;
57 const struct uc81xx_dt_array lutkw;
58 const struct uc81xx_dt_array lutwk;
59 const struct uc81xx_dt_array lutkk;
60 const struct uc81xx_dt_array lutbd;
61 };
62
63 struct uc81xx_quirks {
64 uint16_t max_width;
65 uint16_t max_height;
66
67 bool auto_copy;
68
69 int (*set_cdi)(const struct device *dev, bool border);
70 int (*set_tres)(const struct device *dev);
71 int (*set_ptl)(const struct device *dev, uint16_t x, uint16_t y,
72 uint16_t x_end_idx, uint16_t y_end_idx,
73 const struct display_buffer_descriptor *desc);
74 };
75
76 struct uc81xx_config {
77 const struct uc81xx_quirks *quirks;
78
79 const struct device *mipi_dev;
80 const struct mipi_dbi_config dbi_config;
81 struct gpio_dt_spec busy_gpio;
82
83 uint16_t height;
84 uint16_t width;
85
86 struct uc81xx_dt_array softstart;
87
88 const struct uc81xx_profile *profiles[UC81XX_NUM_PROFILES];
89 };
90
91 struct uc81xx_data {
92 bool blanking_on;
93 enum uc81xx_profile_type profile;
94 };
95
96
uc81xx_busy_wait(const struct device * dev)97 static inline void uc81xx_busy_wait(const struct device *dev)
98 {
99 const struct uc81xx_config *config = dev->config;
100 int pin = gpio_pin_get_dt(&config->busy_gpio);
101
102 while (pin > 0) {
103 __ASSERT(pin >= 0, "Failed to get pin level");
104 k_sleep(K_MSEC(UC81XX_BUSY_DELAY));
105 pin = gpio_pin_get_dt(&config->busy_gpio);
106 }
107 }
108
uc81xx_write_cmd(const struct device * dev,uint8_t cmd,const uint8_t * data,size_t len)109 static inline int uc81xx_write_cmd(const struct device *dev, uint8_t cmd,
110 const uint8_t *data, size_t len)
111 {
112 const struct uc81xx_config *config = dev->config;
113 int err;
114
115 uc81xx_busy_wait(dev);
116
117 err = mipi_dbi_command_write(config->mipi_dev, &config->dbi_config,
118 cmd, data, len);
119 mipi_dbi_release(config->mipi_dev, &config->dbi_config);
120 return err;
121 }
122
uc81xx_write_cmd_pattern(const struct device * dev,uint8_t cmd,uint8_t pattern,size_t len)123 static inline int uc81xx_write_cmd_pattern(const struct device *dev,
124 uint8_t cmd,
125 uint8_t pattern, size_t len)
126 {
127 const struct uc81xx_config *config = dev->config;
128 struct display_buffer_descriptor mipi_desc;
129 int err;
130 uint8_t data[64];
131
132 uc81xx_busy_wait(dev);
133
134 err = mipi_dbi_command_write(config->mipi_dev, &config->dbi_config,
135 cmd, NULL, 0);
136 if (err < 0) {
137 return err;
138 }
139
140 /*
141 * MIPI display write API requires a display buffer descriptor.
142 * Create one that describes the buffer we are writing
143 */
144 mipi_desc.height = 1;
145
146 memset(data, pattern, sizeof(data));
147 while (len) {
148 mipi_desc.buf_size = mipi_desc.width = mipi_desc.pitch =
149 MIN(len, sizeof(data));
150
151 err = mipi_dbi_write_display(config->mipi_dev,
152 &config->dbi_config,
153 data, &mipi_desc,
154 PIXEL_FORMAT_MONO10);
155 if (err < 0) {
156 goto out;
157 }
158
159 len -= mipi_desc.buf_size;
160 }
161
162 out:
163 mipi_dbi_release(config->mipi_dev, &config->dbi_config);
164 return err;
165 }
166
uc81xx_write_cmd_uint8(const struct device * dev,uint8_t cmd,uint8_t data)167 static inline int uc81xx_write_cmd_uint8(const struct device *dev, uint8_t cmd,
168 uint8_t data)
169 {
170 return uc81xx_write_cmd(dev, cmd, &data, 1);
171 }
172
uc81xx_write_array_opt(const struct device * dev,uint8_t cmd,const struct uc81xx_dt_array * array)173 static inline int uc81xx_write_array_opt(const struct device *dev, uint8_t cmd,
174 const struct uc81xx_dt_array *array)
175 {
176 if (array->len && array->data) {
177 return uc81xx_write_cmd(dev, cmd, array->data, array->len);
178 } else {
179 return 0;
180 }
181 }
182
uc81xx_have_profile(const struct device * dev,enum uc81xx_profile_type type)183 static int uc81xx_have_profile(const struct device *dev,
184 enum uc81xx_profile_type type)
185 {
186 const struct uc81xx_config *config = dev->config;
187
188 return type < UC81XX_NUM_PROFILES &&
189 config->profiles[type];
190 }
191
uc81xx_set_profile(const struct device * dev,enum uc81xx_profile_type type)192 static int uc81xx_set_profile(const struct device *dev,
193 enum uc81xx_profile_type type)
194 {
195 const struct uc81xx_config *config = dev->config;
196 const struct uc81xx_profile *p;
197 struct uc81xx_data *data = dev->data;
198 uint8_t psr =
199 UC81XX_PSR_KW_R |
200 UC81XX_PSR_UD |
201 UC81XX_PSR_SHL |
202 UC81XX_PSR_SHD |
203 UC81XX_PSR_RST;
204
205 if (type >= UC81XX_NUM_PROFILES) {
206 return -EINVAL;
207 }
208
209 /* No need to update the current profile, so do nothing */
210 if (data->profile == type) {
211 return 0;
212 }
213
214 p = config->profiles[type];
215 data->profile = type;
216
217 LOG_DBG("Initialize UC81XX controller with profile %d", type);
218
219 if (p) {
220 LOG_HEXDUMP_DBG(p->pwr.data, p->pwr.len, "PWR");
221 if (uc81xx_write_array_opt(dev, UC81XX_CMD_PWR, &p->pwr)) {
222 return -EIO;
223 }
224
225 if (uc81xx_write_array_opt(dev, UC81XX_CMD_BTST,
226 &config->softstart)) {
227 return -EIO;
228 }
229
230 /*
231 * Enable LUT overrides if a LUT has been provided by
232 * the user.
233 */
234 if (p->lutc.len || p->lutww.len || p->lutkw.len ||
235 p->lutwk.len || p->lutbd.len) {
236 LOG_DBG("Using LUT from registers");
237 psr |= UC81XX_PSR_REG;
238 }
239 }
240
241 /* Panel settings, KW mode and soft reset */
242 LOG_DBG("PSR: %#hhx", psr);
243 if (uc81xx_write_cmd_uint8(dev, UC81XX_CMD_PSR, psr)) {
244 return -EIO;
245 }
246
247 /* Set panel resolution */
248 if (config->quirks->set_tres(dev)) {
249 return -EIO;
250 }
251
252 /* Set CDI and enable border output */
253 if (config->quirks->set_cdi(dev, true)) {
254 return -EIO;
255 }
256
257 /*
258 * The rest of the configuration is optional and depends on
259 * having profile overrides specified in the device tree.
260 */
261 if (!p) {
262 return 0;
263 }
264
265 if (uc81xx_write_array_opt(dev, UC81XX_CMD_LUTC, &p->lutc)) {
266 return -EIO;
267 }
268
269 if (uc81xx_write_array_opt(dev, UC81XX_CMD_LUTWW, &p->lutww)) {
270 return -EIO;
271 }
272
273 if (uc81xx_write_array_opt(dev, UC81XX_CMD_LUTKW, &p->lutkw)) {
274 return -EIO;
275 }
276
277 if (uc81xx_write_array_opt(dev, UC81XX_CMD_LUTWK, &p->lutwk)) {
278 return -EIO;
279 }
280
281 if (uc81xx_write_array_opt(dev, UC81XX_CMD_LUTKK, &p->lutkk)) {
282 return -EIO;
283 }
284
285 if (uc81xx_write_array_opt(dev, UC81XX_CMD_LUTBD, &p->lutbd)) {
286 return -EIO;
287 }
288
289 if (p->override_pll) {
290 LOG_DBG("PLL: %#hhx", p->pll);
291 if (uc81xx_write_cmd_uint8(dev, UC81XX_CMD_PLL, p->pll)) {
292 return -EIO;
293 }
294 }
295
296 if (p->override_vdcs) {
297 LOG_DBG("VDCS: %#hhx", p->vdcs);
298 if (uc81xx_write_cmd_uint8(dev, UC81XX_CMD_VDCS, p->vdcs)) {
299 return -EIO;
300 }
301 }
302
303 if (p->override_tcon) {
304 if (uc81xx_write_cmd_uint8(dev, UC81XX_CMD_TCON, p->tcon)) {
305 return -EIO;
306 }
307 }
308
309 return 0;
310 }
311
uc81xx_update_display(const struct device * dev)312 static int uc81xx_update_display(const struct device *dev)
313 {
314 LOG_DBG("Trigger update sequence");
315
316 /* Turn on: booster, controller, regulators, and sensor. */
317 if (uc81xx_write_cmd(dev, UC81XX_CMD_PON, NULL, 0)) {
318 return -EIO;
319 }
320
321 k_sleep(K_MSEC(UC81XX_PON_DELAY));
322
323 if (uc81xx_write_cmd(dev, UC81XX_CMD_DRF, NULL, 0)) {
324 return -EIO;
325 }
326
327 k_sleep(K_MSEC(UC81XX_BUSY_DELAY));
328
329 /* Turn on: booster, controller, regulators, and sensor. */
330 if (uc81xx_write_cmd(dev, UC81XX_CMD_POF, NULL, 0)) {
331 return -EIO;
332 }
333
334 return 0;
335 }
336
uc81xx_blanking_off(const struct device * dev)337 static int uc81xx_blanking_off(const struct device *dev)
338 {
339 struct uc81xx_data *data = dev->data;
340
341 if (data->blanking_on) {
342 /* Update EPD panel in normal mode */
343 if (uc81xx_update_display(dev)) {
344 return -EIO;
345 }
346 }
347
348 data->blanking_on = false;
349
350 return 0;
351 }
352
uc81xx_blanking_on(const struct device * dev)353 static int uc81xx_blanking_on(const struct device *dev)
354 {
355 struct uc81xx_data *data = dev->data;
356
357 if (!data->blanking_on) {
358 if (uc81xx_set_profile(dev, UC81XX_PROFILE_FULL)) {
359 return -EIO;
360 }
361 }
362
363 data->blanking_on = true;
364
365 return 0;
366 }
367
uc81xx_write(const struct device * dev,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc,const void * buf)368 static int uc81xx_write(const struct device *dev, const uint16_t x, const uint16_t y,
369 const struct display_buffer_descriptor *desc,
370 const void *buf)
371 {
372 const struct uc81xx_config *config = dev->config;
373 struct uc81xx_data *data = dev->data;
374
375 uint16_t x_end_idx = x + desc->width - 1;
376 uint16_t y_end_idx = y + desc->height - 1;
377 size_t buf_len;
378 const uint8_t back_buffer = data->blanking_on ?
379 UC81XX_CMD_DTM1 : UC81XX_CMD_DTM2;
380
381 LOG_DBG("x %u, y %u, height %u, width %u, pitch %u",
382 x, y, desc->height, desc->width, desc->pitch);
383
384 buf_len = MIN(desc->buf_size,
385 desc->height * desc->width / UC81XX_PIXELS_PER_BYTE);
386 __ASSERT(desc->width <= desc->pitch, "Pitch is smaller then width");
387 __ASSERT(buf != NULL, "Buffer is not available");
388 __ASSERT(buf_len != 0U, "Buffer of length zero");
389 __ASSERT(!(desc->width % UC81XX_PIXELS_PER_BYTE),
390 "Buffer width not multiple of %d", UC81XX_PIXELS_PER_BYTE);
391
392 if ((y_end_idx > (config->height - 1)) ||
393 (x_end_idx > (config->width - 1))) {
394 LOG_ERR("Position out of bounds");
395 return -EINVAL;
396 }
397
398 if (!data->blanking_on) {
399 /* Blanking isn't on, so this is a partial
400 * refresh. Request the partial profile if it
401 * exists. If a partial profile hasn't been provided,
402 * we continue to use the full refresh profile. Note
403 * that the controller still only scans a partial
404 * window.
405 *
406 * This operation becomes a no-op if the profile is
407 * already active
408 */
409 if (uc81xx_have_profile(dev, UC81XX_PROFILE_PARTIAL) &&
410 uc81xx_set_profile(dev, UC81XX_PROFILE_PARTIAL)) {
411 return -EIO;
412 }
413 }
414
415 if (uc81xx_write_cmd(dev, UC81XX_CMD_PTIN, NULL, 0)) {
416 return -EIO;
417 }
418
419 if (config->quirks->set_ptl(dev, x, y, x_end_idx, y_end_idx, desc)) {
420 return -EIO;
421 }
422
423 if (uc81xx_write_cmd(dev, UC81XX_CMD_DTM2, (uint8_t *)buf, buf_len)) {
424 return -EIO;
425 }
426
427 /* Update the display */
428 if (data->blanking_on == false) {
429 /* Disable border output */
430 if (config->quirks->set_cdi(dev, false)) {
431 return -EIO;
432 }
433
434 if (uc81xx_update_display(dev)) {
435 return -EIO;
436 }
437
438 /* Enable border output */
439 if (config->quirks->set_cdi(dev, true)) {
440 return -EIO;
441 }
442 }
443
444 if (!config->quirks->auto_copy) {
445 /* Some controllers don't copy the new data to the old
446 * data buffer on refresh. Do that manually here if
447 * needed.
448 */
449
450 if (config->quirks->set_ptl(dev, x, y, x_end_idx, y_end_idx, desc)) {
451 return -EIO;
452 }
453
454 if (uc81xx_write_cmd(dev, back_buffer,
455 (uint8_t *)buf, buf_len)) {
456 return -EIO;
457 }
458 }
459
460 if (uc81xx_write_cmd(dev, UC81XX_CMD_PTOUT, NULL, 0)) {
461 return -EIO;
462 }
463
464 return 0;
465 }
466
uc81xx_get_capabilities(const struct device * dev,struct display_capabilities * caps)467 static void uc81xx_get_capabilities(const struct device *dev,
468 struct display_capabilities *caps)
469 {
470 const struct uc81xx_config *config = dev->config;
471
472 memset(caps, 0, sizeof(struct display_capabilities));
473 caps->x_resolution = config->width;
474 caps->y_resolution = config->height;
475 caps->supported_pixel_formats = PIXEL_FORMAT_MONO10;
476 caps->current_pixel_format = PIXEL_FORMAT_MONO10;
477 caps->screen_info = SCREEN_INFO_MONO_MSB_FIRST | SCREEN_INFO_EPD;
478 }
479
uc81xx_set_pixel_format(const struct device * dev,const enum display_pixel_format pf)480 static int uc81xx_set_pixel_format(const struct device *dev,
481 const enum display_pixel_format pf)
482 {
483 if (pf == PIXEL_FORMAT_MONO10) {
484 return 0;
485 }
486
487 LOG_ERR("not supported");
488 return -ENOTSUP;
489 }
490
uc81xx_clear_and_write_buffer(const struct device * dev,uint8_t pattern,bool update)491 static int uc81xx_clear_and_write_buffer(const struct device *dev,
492 uint8_t pattern, bool update)
493 {
494 const struct uc81xx_config *config = dev->config;
495 const int size = config->width * config->height
496 / UC81XX_PIXELS_PER_BYTE;
497
498 if (uc81xx_write_cmd_pattern(dev, UC81XX_CMD_DTM1, pattern, size)) {
499 return -EIO;
500 }
501
502 if (uc81xx_write_cmd_pattern(dev, UC81XX_CMD_DTM2, pattern, size)) {
503 return -EIO;
504 }
505
506 if (update == true) {
507 if (uc81xx_update_display(dev)) {
508 return -EIO;
509 }
510 }
511
512 return 0;
513 }
514
uc81xx_controller_init(const struct device * dev)515 static int uc81xx_controller_init(const struct device *dev)
516 {
517 const struct uc81xx_config *config = dev->config;
518 struct uc81xx_data *data = dev->data;
519
520 mipi_dbi_reset(config->mipi_dev, UC81XX_RESET_DELAY);
521 k_sleep(K_MSEC(UC81XX_RESET_DELAY));
522 uc81xx_busy_wait(dev);
523
524 data->blanking_on = true;
525 data->profile = UC81XX_PROFILE_INVALID;
526
527 if (uc81xx_set_profile(dev, UC81XX_PROFILE_FULL)) {
528 return -EIO;
529 }
530
531 if (uc81xx_clear_and_write_buffer(dev, 0xff, false)) {
532 return -EIO;
533 }
534
535 return 0;
536 }
537
uc81xx_init(const struct device * dev)538 static int uc81xx_init(const struct device *dev)
539 {
540 const struct uc81xx_config *config = dev->config;
541
542 LOG_DBG("");
543
544 if (!device_is_ready(config->mipi_dev)) {
545 LOG_ERR("MIPI device not ready");
546 return -ENODEV;
547 }
548
549 if (!gpio_is_ready_dt(&config->busy_gpio)) {
550 LOG_ERR("Busy GPIO device not ready");
551 return -ENODEV;
552 }
553
554 gpio_pin_configure_dt(&config->busy_gpio, GPIO_INPUT);
555
556 if (config->width > config->quirks->max_width ||
557 config->height > config->quirks->max_height) {
558 LOG_ERR("Display size out of range.");
559 return -EINVAL;
560 }
561
562 return uc81xx_controller_init(dev);
563 }
564
565 #if DT_HAS_COMPAT_STATUS_OKAY(ultrachip_uc8175)
uc81xx_set_tres_8(const struct device * dev)566 static int uc81xx_set_tres_8(const struct device *dev)
567 {
568 const struct uc81xx_config *config = dev->config;
569 const struct uc81xx_tres8 tres = {
570 .hres = config->width,
571 .vres = config->height,
572 };
573
574 LOG_HEXDUMP_DBG(&tres, sizeof(tres), "TRES");
575
576 return uc81xx_write_cmd(dev, UC81XX_CMD_TRES, (const void *)&tres, sizeof(tres));
577 }
578
uc81xx_set_ptl_8(const struct device * dev,uint16_t x,uint16_t y,uint16_t x_end_idx,uint16_t y_end_idx,const struct display_buffer_descriptor * desc)579 static inline int uc81xx_set_ptl_8(const struct device *dev, uint16_t x, uint16_t y,
580 uint16_t x_end_idx, uint16_t y_end_idx,
581 const struct display_buffer_descriptor *desc)
582 {
583 const struct uc81xx_ptl8 ptl = {
584 .hrst = x,
585 .hred = x_end_idx,
586 .vrst = y,
587 .vred = y_end_idx,
588 .flags = UC81XX_PTL_FLAG_PT_SCAN,
589 };
590
591 /* Setup Partial Window and enable Partial Mode */
592 LOG_HEXDUMP_DBG(&ptl, sizeof(ptl), "ptl");
593
594 return uc81xx_write_cmd(dev, UC81XX_CMD_PTL, (const void *)&ptl, sizeof(ptl));
595 }
596 #endif
597
598 #if DT_HAS_COMPAT_STATUS_OKAY(ultrachip_uc8176) || DT_HAS_COMPAT_STATUS_OKAY(ultrachip_uc8179)
uc81xx_set_tres_16(const struct device * dev)599 static int uc81xx_set_tres_16(const struct device *dev)
600 {
601 const struct uc81xx_config *config = dev->config;
602 const struct uc81xx_tres16 tres = {
603 .hres = sys_cpu_to_be16(config->width),
604 .vres = sys_cpu_to_be16(config->height),
605 };
606
607 LOG_HEXDUMP_DBG(&tres, sizeof(tres), "TRES");
608
609 return uc81xx_write_cmd(dev, UC81XX_CMD_TRES, (const void *)&tres, sizeof(tres));
610 }
611
uc81xx_set_ptl_16(const struct device * dev,uint16_t x,uint16_t y,uint16_t x_end_idx,uint16_t y_end_idx,const struct display_buffer_descriptor * desc)612 static inline int uc81xx_set_ptl_16(const struct device *dev, uint16_t x, uint16_t y,
613 uint16_t x_end_idx, uint16_t y_end_idx,
614 const struct display_buffer_descriptor *desc)
615 {
616 const struct uc81xx_ptl16 ptl = {
617 .hrst = sys_cpu_to_be16(x),
618 .hred = sys_cpu_to_be16(x_end_idx),
619 .vrst = sys_cpu_to_be16(y),
620 .vred = sys_cpu_to_be16(y_end_idx),
621 .flags = UC81XX_PTL_FLAG_PT_SCAN,
622 };
623
624 /* Setup Partial Window and enable Partial Mode */
625 LOG_HEXDUMP_DBG(&ptl, sizeof(ptl), "ptl");
626
627 return uc81xx_write_cmd(dev, UC81XX_CMD_PTL, (const void *)&ptl, sizeof(ptl));
628 }
629 #endif
630
631 #if DT_HAS_COMPAT_STATUS_OKAY(ultrachip_uc8175) || DT_HAS_COMPAT_STATUS_OKAY(ultrachip_uc8176)
uc8176_set_cdi(const struct device * dev,bool border)632 static int uc8176_set_cdi(const struct device *dev, bool border)
633 {
634 const struct uc81xx_config *config = dev->config;
635 const struct uc81xx_data *data = dev->data;
636 const struct uc81xx_profile *p = config->profiles[data->profile];
637 uint8_t cdi = UC8176_CDI_VBD1 | UC8176_CDI_DDX0 |
638 (p ? (p->cdi & UC8176_CDI_CDI_MASK) : 0);
639
640 if (!p || !p->override_cdi) {
641 return 0;
642 }
643
644 if (!border) {
645 /* Floating border */
646 cdi |= UC8176_CDI_VBD1 | UC8176_CDI_VBD0;
647 }
648
649 LOG_DBG("CDI: %#hhx", cdi);
650 return uc81xx_write_cmd_uint8(dev, UC81XX_CMD_CDI, cdi);
651 }
652 #endif
653
654 #if DT_HAS_COMPAT_STATUS_OKAY(ultrachip_uc8175)
655 static const struct uc81xx_quirks uc8175_quirks = {
656 .max_width = 80,
657 .max_height = 160,
658
659 .auto_copy = false,
660
661 .set_cdi = uc8176_set_cdi,
662 .set_tres = uc81xx_set_tres_8,
663 .set_ptl = uc81xx_set_ptl_8,
664 };
665 #endif
666
667 #if DT_HAS_COMPAT_STATUS_OKAY(ultrachip_uc8176)
668 static const struct uc81xx_quirks uc8176_quirks = {
669 .max_width = 400,
670 .max_height = 300,
671
672 .auto_copy = false,
673
674 .set_cdi = uc8176_set_cdi,
675 .set_tres = uc81xx_set_tres_16,
676 .set_ptl = uc81xx_set_ptl_16,
677 };
678 #endif
679
680 #if DT_HAS_COMPAT_STATUS_OKAY(ultrachip_uc8179)
uc8179_set_cdi(const struct device * dev,bool border)681 static int uc8179_set_cdi(const struct device *dev, bool border)
682 {
683 const struct uc81xx_config *config = dev->config;
684 const struct uc81xx_data *data = dev->data;
685 const struct uc81xx_profile *p = config->profiles[data->profile];
686 uint8_t cdi[UC8179_CDI_REG_LENGTH] = {
687 UC8179_CDI_BDV1 | UC8179_CDI_N2OCP | UC8179_CDI_DDX0,
688 p ? p->cdi : 0,
689 };
690
691 if (!p || !p->override_cdi) {
692 return 0;
693 }
694
695 cdi[UC8179_CDI_BDZ_DDX_IDX] |= border ? 0 : UC8179_CDI_BDZ;
696
697 LOG_HEXDUMP_DBG(cdi, sizeof(cdi), "CDI");
698 return uc81xx_write_cmd(dev, UC81XX_CMD_CDI, cdi, sizeof(cdi));
699 }
700
701 static const struct uc81xx_quirks uc8179_quirks = {
702 .max_width = 800,
703 .max_height = 600,
704
705 .auto_copy = true,
706
707 .set_cdi = uc8179_set_cdi,
708 .set_tres = uc81xx_set_tres_16,
709 .set_ptl = uc81xx_set_ptl_16,
710 };
711 #endif
712
713 static const struct display_driver_api uc81xx_driver_api = {
714 .blanking_on = uc81xx_blanking_on,
715 .blanking_off = uc81xx_blanking_off,
716 .write = uc81xx_write,
717 .get_capabilities = uc81xx_get_capabilities,
718 .set_pixel_format = uc81xx_set_pixel_format,
719 };
720
721 #define UC81XX_MAKE_ARRAY_OPT(n, p) \
722 static uint8_t data_ ## n ## _ ## p[] = DT_PROP_OR(n, p, {})
723
724 #define UC81XX_MAKE_ARRAY(n, p) \
725 static uint8_t data_ ## n ## _ ## p[] = DT_PROP(n, p)
726
727 #define UC81XX_ASSIGN_ARRAY(n, p) \
728 { \
729 .data = data_ ## n ## _ ## p, \
730 .len = sizeof(data_ ## n ## _ ## p), \
731 }
732
733 #define UC81XX_PROFILE(n) \
734 UC81XX_MAKE_ARRAY_OPT(n, pwr); \
735 UC81XX_MAKE_ARRAY_OPT(n, lutc); \
736 UC81XX_MAKE_ARRAY_OPT(n, lutww); \
737 UC81XX_MAKE_ARRAY_OPT(n, lutkw); \
738 UC81XX_MAKE_ARRAY_OPT(n, lutwk); \
739 UC81XX_MAKE_ARRAY_OPT(n, lutkk); \
740 UC81XX_MAKE_ARRAY_OPT(n, lutbd); \
741 \
742 static const struct uc81xx_profile uc81xx_profile_ ## n = { \
743 .pwr = UC81XX_ASSIGN_ARRAY(n, pwr), \
744 .cdi = DT_PROP_OR(n, cdi, 0), \
745 .override_cdi = DT_NODE_HAS_PROP(n, cdi), \
746 .tcon = DT_PROP_OR(n, tcon, 0), \
747 .override_tcon = DT_NODE_HAS_PROP(n, tcon), \
748 .pll = DT_PROP_OR(n, pll, 0), \
749 .override_pll = DT_NODE_HAS_PROP(n, pll), \
750 .vdcs = DT_PROP_OR(n, vdcs, 0), \
751 .override_vdcs = DT_NODE_HAS_PROP(n, vdcs), \
752 \
753 .lutc = UC81XX_ASSIGN_ARRAY(n, lutc), \
754 .lutww = UC81XX_ASSIGN_ARRAY(n, lutww), \
755 .lutkw = UC81XX_ASSIGN_ARRAY(n, lutkw), \
756 .lutwk = UC81XX_ASSIGN_ARRAY(n, lutwk), \
757 .lutkk = UC81XX_ASSIGN_ARRAY(n, lutkk), \
758 .lutbd = UC81XX_ASSIGN_ARRAY(n, lutbd), \
759 };
760
761 #define _UC81XX_PROFILE_PTR(n) &uc81xx_profile_ ## n
762
763 #define UC81XX_PROFILE_PTR(n) \
764 COND_CODE_1(DT_NODE_EXISTS(n), \
765 (_UC81XX_PROFILE_PTR(n)), \
766 NULL)
767
768 #define UC81XX_DEFINE(n, quirks_ptr) \
769 UC81XX_MAKE_ARRAY_OPT(n, softstart); \
770 \
771 DT_FOREACH_CHILD(n, UC81XX_PROFILE); \
772 \
773 static const struct uc81xx_config uc81xx_cfg_ ## n = { \
774 .quirks = quirks_ptr, \
775 .mipi_dev = DEVICE_DT_GET(DT_PARENT(n)), \
776 .dbi_config = { \
777 .mode = MIPI_DBI_MODE_SPI_4WIRE, \
778 .config = MIPI_DBI_SPI_CONFIG_DT(n, \
779 SPI_OP_MODE_MASTER | \
780 SPI_LOCK_ON | SPI_WORD_SET(8), \
781 0), \
782 }, \
783 .busy_gpio = GPIO_DT_SPEC_GET(n, busy_gpios), \
784 \
785 .height = DT_PROP(n, height), \
786 .width = DT_PROP(n, width), \
787 \
788 .softstart = UC81XX_ASSIGN_ARRAY(n, softstart), \
789 \
790 .profiles = { \
791 [UC81XX_PROFILE_FULL] = \
792 UC81XX_PROFILE_PTR(DT_CHILD(n, full)), \
793 [UC81XX_PROFILE_PARTIAL] = \
794 UC81XX_PROFILE_PTR(DT_CHILD(n, partial)), \
795 }, \
796 }; \
797 \
798 static struct uc81xx_data uc81xx_data_##n = {}; \
799 \
800 DEVICE_DT_DEFINE(n, uc81xx_init, NULL, \
801 &uc81xx_data_ ## n, \
802 &uc81xx_cfg_ ## n, \
803 POST_KERNEL, \
804 CONFIG_DISPLAY_INIT_PRIORITY, \
805 &uc81xx_driver_api);
806
807 DT_FOREACH_STATUS_OKAY_VARGS(ultrachip_uc8175, UC81XX_DEFINE,
808 &uc8175_quirks);
809
810 DT_FOREACH_STATUS_OKAY_VARGS(ultrachip_uc8176, UC81XX_DEFINE,
811 &uc8176_quirks);
812
813 DT_FOREACH_STATUS_OKAY_VARGS(ultrachip_uc8179, UC81XX_DEFINE,
814 &uc8179_quirks);
815