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