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