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 (!gpio_is_ready_dt(&config->reset_gpio)) {
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 (!gpio_is_ready_dt(&config->dc_gpio)) {
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 (!gpio_is_ready_dt(&config->busy_gpio)) {
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