1 /*
2  * Copyright (c) 2022 Andreas Sandberg
3  * Copyright (c) 2018-2020 PHYTEC Messtechnik GmbH
4  * Copyright 2024 NXP
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 
9 #define LOG_LEVEL CONFIG_DISPLAY_LOG_LEVEL
10 #include <zephyr/logging/log.h>
11 LOG_MODULE_REGISTER(ssd16xx);
12 
13 #include <string.h>
14 #include <zephyr/device.h>
15 #include <zephyr/drivers/display.h>
16 #include <zephyr/init.h>
17 #include <zephyr/drivers/gpio.h>
18 #include <zephyr/drivers/mipi_dbi.h>
19 #include <zephyr/sys/byteorder.h>
20 
21 #include <zephyr/display/ssd16xx.h>
22 #include "ssd16xx_regs.h"
23 
24 /**
25  * SSD16xx compatible EPD controller driver.
26  */
27 
28 #define EPD_PANEL_NUMOF_ROWS_PER_PAGE	8
29 #define SSD16XX_PANEL_FIRST_PAGE	0
30 #define SSD16XX_PANEL_FIRST_GATE	0
31 #define SSD16XX_PIXELS_PER_BYTE		8
32 #define SSD16XX_DEFAULT_TR_VALUE	25U
33 #define SSD16XX_TR_SCALE_FACTOR		256U
34 
35 
36 enum ssd16xx_profile_type {
37 	SSD16XX_PROFILE_FULL = 0,
38 	SSD16XX_PROFILE_PARTIAL,
39 	SSD16XX_NUM_PROFILES,
40 	SSD16XX_PROFILE_INVALID = SSD16XX_NUM_PROFILES,
41 };
42 
43 struct ssd16xx_quirks {
44 	/* Gates */
45 	uint16_t max_width;
46 	/* Sources */
47 	uint16_t max_height;
48 	/* Width (bits) of integer type representing an x coordinate */
49 	uint8_t pp_width_bits;
50 	/* Width (bits) of integer type representing a y coordinate */
51 	uint8_t pp_height_bits;
52 
53 	/*
54 	 * Device specific flags to be included in
55 	 * SSD16XX_CMD_UPDATE_CTRL2 for a full refresh.
56 	 */
57 	uint8_t ctrl2_full;
58 	/*
59 	 * Device specific flags to be included in
60 	 * SSD16XX_CMD_UPDATE_CTRL2 for a partial refresh.
61 	 */
62 	uint8_t ctrl2_partial;
63 };
64 
65 struct ssd16xx_data {
66 	bool read_supported;
67 	uint8_t scan_mode;
68 	bool blanking_on;
69 	enum ssd16xx_profile_type profile;
70 	enum display_orientation orientation;
71 };
72 
73 struct ssd16xx_dt_array {
74 	uint8_t *data;
75 	uint8_t len;
76 };
77 
78 struct ssd16xx_profile {
79 	struct ssd16xx_dt_array lut;
80 	struct ssd16xx_dt_array gdv;
81 	struct ssd16xx_dt_array sdv;
82 	uint8_t vcom;
83 	uint8_t bwf;
84 	uint8_t dummy_line;
85 	uint8_t gate_line_width;
86 
87 	bool override_vcom;
88 	bool override_bwf;
89 	bool override_dummy_line;
90 	bool override_gate_line_width;
91 };
92 
93 struct ssd16xx_config {
94 	const struct device *mipi_dev;
95 	const struct mipi_dbi_config dbi_config;
96 	struct gpio_dt_spec busy_gpio;
97 
98 	const struct ssd16xx_quirks *quirks;
99 
100 	struct ssd16xx_dt_array softstart;
101 
102 	const struct ssd16xx_profile *profiles[SSD16XX_NUM_PROFILES];
103 
104 	uint16_t rotation;
105 	uint16_t height;
106 	uint16_t width;
107 	uint8_t tssv;
108 };
109 
110 static int ssd16xx_set_profile(const struct device *dev,
111 			       enum ssd16xx_profile_type type);
112 
ssd16xx_busy_wait(const struct device * dev)113 static inline void ssd16xx_busy_wait(const struct device *dev)
114 {
115 	const struct ssd16xx_config *config = dev->config;
116 	int pin = gpio_pin_get_dt(&config->busy_gpio);
117 
118 	while (pin > 0) {
119 		__ASSERT(pin >= 0, "Failed to get pin level");
120 		k_msleep(SSD16XX_BUSY_DELAY);
121 		pin = gpio_pin_get_dt(&config->busy_gpio);
122 	}
123 }
124 
ssd16xx_write_cmd(const struct device * dev,uint8_t cmd,const uint8_t * data,size_t len)125 static inline int ssd16xx_write_cmd(const struct device *dev, uint8_t cmd,
126 				    const uint8_t *data, size_t len)
127 {
128 	const struct ssd16xx_config *config = dev->config;
129 	int err;
130 
131 	ssd16xx_busy_wait(dev);
132 
133 	err = mipi_dbi_command_write(config->mipi_dev, &config->dbi_config,
134 				      cmd, data, len);
135 	mipi_dbi_release(config->mipi_dev, &config->dbi_config);
136 	return err;
137 }
138 
ssd16xx_write_uint8(const struct device * dev,uint8_t cmd,uint8_t data)139 static inline int ssd16xx_write_uint8(const struct device *dev, uint8_t cmd,
140 				      uint8_t data)
141 {
142 	return ssd16xx_write_cmd(dev, cmd, &data, 1);
143 }
144 
ssd16xx_read_cmd(const struct device * dev,uint8_t cmd,uint8_t * data,size_t len)145 static inline int ssd16xx_read_cmd(const struct device *dev, uint8_t cmd,
146 				    uint8_t *data, size_t len)
147 {
148 	const struct ssd16xx_config *config = dev->config;
149 	const struct ssd16xx_data *dev_data = dev->data;
150 
151 	if (!dev_data->read_supported) {
152 		return -ENOTSUP;
153 	}
154 
155 	ssd16xx_busy_wait(dev);
156 
157 	return mipi_dbi_command_read(config->mipi_dev, &config->dbi_config,
158 				     &cmd, 1, data, len);
159 }
160 
push_x_param(const struct device * dev,uint8_t * data,uint16_t x)161 static inline size_t push_x_param(const struct device *dev,
162 				  uint8_t *data, uint16_t x)
163 {
164 	const struct ssd16xx_config *config = dev->config;
165 
166 	if (config->quirks->pp_width_bits == 8) {
167 		data[0] = (uint8_t)x;
168 		return 1;
169 	}
170 
171 	if (config->quirks->pp_width_bits == 16) {
172 		sys_put_le16(sys_cpu_to_le16(x), data);
173 		return 2;
174 	}
175 
176 	LOG_ERR("Unsupported pp_width_bits %u",
177 		config->quirks->pp_width_bits);
178 	return 0;
179 }
180 
push_y_param(const struct device * dev,uint8_t * data,uint16_t y)181 static inline size_t push_y_param(const struct device *dev,
182 				  uint8_t *data, uint16_t y)
183 {
184 	const struct ssd16xx_config *config = dev->config;
185 
186 	if (config->quirks->pp_height_bits == 8) {
187 		data[0] = (uint8_t)y;
188 		return 1;
189 	}
190 
191 	if (config->quirks->pp_height_bits == 16) {
192 		sys_put_le16(sys_cpu_to_le16(y), data);
193 		return 2;
194 	}
195 
196 	LOG_ERR("Unsupported pp_height_bitsa %u",
197 		config->quirks->pp_height_bits);
198 	return 0;
199 }
200 
201 
202 
ssd16xx_set_ram_param(const struct device * dev,uint16_t sx,uint16_t ex,uint16_t sy,uint16_t ey)203 static inline int ssd16xx_set_ram_param(const struct device *dev,
204 					uint16_t sx, uint16_t ex,
205 					uint16_t sy, uint16_t ey)
206 {
207 	int err;
208 	uint8_t tmp[4];
209 	size_t len;
210 
211 	len  = push_x_param(dev, tmp, sx);
212 	len += push_x_param(dev, tmp + len, ex);
213 	err = ssd16xx_write_cmd(dev, SSD16XX_CMD_RAM_XPOS_CTRL, tmp, len);
214 	if (err < 0) {
215 		return err;
216 	}
217 
218 	len  = push_y_param(dev, tmp, sy);
219 	len += push_y_param(dev, tmp + len, ey);
220 	err = ssd16xx_write_cmd(dev, SSD16XX_CMD_RAM_YPOS_CTRL, tmp,	len);
221 	if (err < 0) {
222 		return err;
223 	}
224 
225 	return 0;
226 }
227 
ssd16xx_set_ram_ptr(const struct device * dev,uint16_t x,uint16_t y)228 static inline int ssd16xx_set_ram_ptr(const struct device *dev, uint16_t x,
229 				      uint16_t y)
230 {
231 	int err;
232 	uint8_t tmp[2];
233 	size_t len;
234 
235 	len = push_x_param(dev, tmp, x);
236 	err = ssd16xx_write_cmd(dev, SSD16XX_CMD_RAM_XPOS_CNTR, tmp, len);
237 	if (err < 0) {
238 		return err;
239 	}
240 
241 	len = push_y_param(dev, tmp, y);
242 	return ssd16xx_write_cmd(dev, SSD16XX_CMD_RAM_YPOS_CNTR, tmp, len);
243 }
244 
ssd16xx_activate(const struct device * dev,uint8_t ctrl2)245 static int ssd16xx_activate(const struct device *dev, uint8_t ctrl2)
246 {
247 	int err;
248 
249 	err = ssd16xx_write_uint8(dev, SSD16XX_CMD_UPDATE_CTRL2, ctrl2);
250 	if (err < 0) {
251 		return err;
252 	}
253 
254 	return ssd16xx_write_cmd(dev, SSD16XX_CMD_MASTER_ACTIVATION, NULL, 0);
255 }
256 
ssd16xx_update_display(const struct device * dev)257 static int ssd16xx_update_display(const struct device *dev)
258 {
259 	const struct ssd16xx_config *config = dev->config;
260 	const struct ssd16xx_data *data = dev->data;
261 	const struct ssd16xx_profile *p = config->profiles[data->profile];
262 	const struct ssd16xx_quirks *quirks = config->quirks;
263 	const bool load_lut = !p || p->lut.len == 0;
264 	const bool load_temp = load_lut && config->tssv;
265 	const bool partial = data->profile == SSD16XX_PROFILE_PARTIAL;
266 	const uint8_t update_cmd =
267 		SSD16XX_CTRL2_ENABLE_CLK |
268 		SSD16XX_CTRL2_ENABLE_ANALOG |
269 		(load_lut ? SSD16XX_CTRL2_LOAD_LUT : 0) |
270 		(load_temp ? SSD16XX_CTRL2_LOAD_TEMPERATURE : 0) |
271 		(partial ? quirks->ctrl2_partial : quirks->ctrl2_full) |
272 		SSD16XX_CTRL2_DISABLE_ANALOG |
273 		SSD16XX_CTRL2_DISABLE_CLK;
274 
275 	return ssd16xx_activate(dev, update_cmd);
276 }
277 
ssd16xx_blanking_off(const struct device * dev)278 static int ssd16xx_blanking_off(const struct device *dev)
279 {
280 	struct ssd16xx_data *data = dev->data;
281 
282 	if (data->blanking_on) {
283 		data->blanking_on = false;
284 		return ssd16xx_update_display(dev);
285 	}
286 
287 	return 0;
288 }
289 
ssd16xx_blanking_on(const struct device * dev)290 static int ssd16xx_blanking_on(const struct device *dev)
291 {
292 	struct ssd16xx_data *data = dev->data;
293 
294 	if (!data->blanking_on) {
295 		if (ssd16xx_set_profile(dev, SSD16XX_PROFILE_FULL)) {
296 			return -EIO;
297 		}
298 	}
299 
300 	data->blanking_on = true;
301 
302 	return 0;
303 }
304 
ssd16xx_set_window(const struct device * dev,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc)305 static int ssd16xx_set_window(const struct device *dev,
306 			      const uint16_t x, const uint16_t y,
307 			      const struct display_buffer_descriptor *desc)
308 {
309 	const struct ssd16xx_config *config = dev->config;
310 	const struct ssd16xx_data *data = dev->data;
311 	int err;
312 	uint16_t x_start;
313 	uint16_t x_end;
314 	uint16_t y_start;
315 	uint16_t y_end;
316 	uint16_t panel_h = config->height -
317 			   config->height % EPD_PANEL_NUMOF_ROWS_PER_PAGE;
318 
319 	if (desc->pitch < desc->width) {
320 		LOG_ERR("Pitch is smaller than width");
321 		return -EINVAL;
322 	}
323 
324 	if (desc->pitch > desc->width) {
325 		LOG_ERR("Unsupported mode");
326 		return -ENOTSUP;
327 	}
328 
329 	if (data->orientation == DISPLAY_ORIENTATION_NORMAL ||
330 	    data->orientation == DISPLAY_ORIENTATION_ROTATED_180) {
331 		if ((y + desc->height) > panel_h) {
332 			LOG_ERR("Buffer out of bounds (height)");
333 			return -EINVAL;
334 		}
335 
336 		if ((x + desc->width) > config->width) {
337 			LOG_ERR("Buffer out of bounds (width)");
338 			return -EINVAL;
339 		}
340 
341 		if ((desc->height % EPD_PANEL_NUMOF_ROWS_PER_PAGE) != 0U) {
342 			LOG_ERR("Buffer height not multiple of %d", EPD_PANEL_NUMOF_ROWS_PER_PAGE);
343 			return -EINVAL;
344 		}
345 
346 		if ((y % EPD_PANEL_NUMOF_ROWS_PER_PAGE) != 0U) {
347 			LOG_ERR("Y coordinate not multiple of %d", EPD_PANEL_NUMOF_ROWS_PER_PAGE);
348 			return -EINVAL;
349 		}
350 	} else {
351 		if ((y + desc->height) > config->width) {
352 			LOG_ERR("Buffer out of bounds (height)");
353 			return -EINVAL;
354 		}
355 
356 		if ((x + desc->width) > panel_h) {
357 			LOG_ERR("Buffer out of bounds (width)");
358 			return -EINVAL;
359 		}
360 
361 		if ((desc->width % SSD16XX_PIXELS_PER_BYTE) != 0U) {
362 			LOG_ERR("Buffer width not multiple of %d", SSD16XX_PIXELS_PER_BYTE);
363 			return -EINVAL;
364 		}
365 
366 		if ((x % SSD16XX_PIXELS_PER_BYTE) != 0U) {
367 			LOG_ERR("X coordinate not multiple of %d", SSD16XX_PIXELS_PER_BYTE);
368 			return -EINVAL;
369 		}
370 	}
371 
372 	switch (data->orientation) {
373 	case DISPLAY_ORIENTATION_NORMAL:
374 		x_start = (panel_h - 1 - y) / SSD16XX_PIXELS_PER_BYTE;
375 		x_end = (panel_h - 1 - (y + desc->height - 1)) / SSD16XX_PIXELS_PER_BYTE;
376 		y_start = x;
377 		y_end = (x + desc->width - 1);
378 		break;
379 	case DISPLAY_ORIENTATION_ROTATED_90:
380 		x_start = (panel_h - 1 - x) / SSD16XX_PIXELS_PER_BYTE;
381 		x_end = (panel_h - 1 - (x + desc->width - 1)) / SSD16XX_PIXELS_PER_BYTE;
382 		y_start = (config->width - 1 - y);
383 		y_end = (config->width - 1 - (y + desc->height - 1));
384 		break;
385 	case DISPLAY_ORIENTATION_ROTATED_180:
386 		x_start = y / SSD16XX_PIXELS_PER_BYTE;
387 		x_end = (y + desc->height - 1) / SSD16XX_PIXELS_PER_BYTE;
388 		y_start = (x + desc->width - 1);
389 		y_end = x;
390 		break;
391 	case DISPLAY_ORIENTATION_ROTATED_270:
392 		x_start = x / SSD16XX_PIXELS_PER_BYTE;
393 		x_end = (x + desc->width - 1) / SSD16XX_PIXELS_PER_BYTE;
394 		y_start = y;
395 		y_end = (y + desc->height - 1);
396 		break;
397 	default:
398 		return -EINVAL;
399 	}
400 
401 	err = ssd16xx_set_ram_param(dev, x_start, x_end, y_start, y_end);
402 	if (err < 0) {
403 		return err;
404 	}
405 
406 	err = ssd16xx_set_ram_ptr(dev, x_start, y_start);
407 	if (err < 0) {
408 		return err;
409 	}
410 
411 	return 0;
412 }
413 
ssd16xx_write(const struct device * dev,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc,const void * buf)414 static int ssd16xx_write(const struct device *dev, const uint16_t x,
415 			 const uint16_t y,
416 			 const struct display_buffer_descriptor *desc,
417 			 const void *buf)
418 {
419 	const struct ssd16xx_config *config = dev->config;
420 	const struct ssd16xx_data *data = dev->data;
421 	const bool have_partial_refresh =
422 		config->profiles[SSD16XX_PROFILE_PARTIAL] != NULL;
423 	const bool partial_refresh = !data->blanking_on && have_partial_refresh;
424 	const size_t buf_len = MIN(desc->buf_size,
425 				   desc->height * desc->width / 8);
426 	int err;
427 
428 	if (buf == NULL || buf_len == 0U) {
429 		LOG_ERR("Display buffer is not available");
430 		return -EINVAL;
431 	}
432 
433 	if (partial_refresh) {
434 		/*
435 		 * Request the partial profile. This operation becomes
436 		 * a no-op if the profile is already active.
437 		 */
438 		err = ssd16xx_set_profile(dev, SSD16XX_PROFILE_PARTIAL);
439 		if (err < 0) {
440 			return -EIO;
441 		}
442 	}
443 
444 	err = ssd16xx_set_window(dev, x, y, desc);
445 	if (err < 0) {
446 		return err;
447 	}
448 
449 	err = ssd16xx_write_cmd(dev, SSD16XX_CMD_WRITE_RAM, (uint8_t *)buf,
450 				buf_len);
451 	if (err < 0) {
452 		return err;
453 	}
454 
455 	if (!data->blanking_on) {
456 		err = ssd16xx_update_display(dev);
457 		if (err < 0) {
458 			return err;
459 		}
460 	}
461 
462 	if (data->blanking_on && have_partial_refresh) {
463 		/*
464 		 * We will trigger a full refresh when blanking is
465 		 * turned off. The controller won't keep track of the
466 		 * old frame buffer, which is needed to perform a
467 		 * partial update, when this happens. Maintain the old
468 		 * frame buffer manually here to make sure future
469 		 * partial updates will work as expected.
470 		 */
471 		err = ssd16xx_write_cmd(dev, SSD16XX_CMD_WRITE_RED_RAM,
472 					(uint8_t *)buf, buf_len);
473 		if (err < 0) {
474 			return err;
475 		}
476 	} else if (partial_refresh) {
477 		/*
478 		 * We just performed a partial refresh. After the
479 		 * refresh, the controller swaps the black/red buffers
480 		 * containing the current and new image. We need to
481 		 * perform a second write here to ensure that future
482 		 * updates work on an up-to-date framebuffer.
483 		 */
484 		err = ssd16xx_write_cmd(dev, SSD16XX_CMD_WRITE_RAM,
485 					(uint8_t *)buf, buf_len);
486 		if (err < 0) {
487 			return err;
488 		}
489 	}
490 
491 	return 0;
492 }
493 
ssd16xx_read_ram(const struct device * dev,enum ssd16xx_ram ram_type,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc,void * buf)494 int ssd16xx_read_ram(const struct device *dev, enum ssd16xx_ram ram_type,
495 		     const uint16_t x, const uint16_t y,
496 		     const struct display_buffer_descriptor *desc,
497 		     void *buf)
498 {
499 	const struct ssd16xx_data *data = dev->data;
500 	const size_t buf_len = MIN(desc->buf_size,
501 				   desc->height * desc->width / 8);
502 	int err;
503 	uint8_t ram_ctrl;
504 
505 	if (!data->read_supported) {
506 		return -ENOTSUP;
507 	}
508 
509 	switch (ram_type) {
510 	case SSD16XX_RAM_BLACK:
511 		ram_ctrl = SSD16XX_RAM_READ_CTRL_BLACK;
512 		break;
513 
514 	case SSD16XX_RAM_RED:
515 		ram_ctrl = SSD16XX_RAM_READ_CTRL_RED;
516 		break;
517 
518 	default:
519 		return -EINVAL;
520 	}
521 
522 	if (buf == NULL || buf_len == 0U) {
523 		LOG_ERR("Display buffer is not available");
524 		return -EINVAL;
525 	}
526 
527 	err = ssd16xx_set_window(dev, x, y, desc);
528 	if (err < 0) {
529 		return err;
530 	}
531 
532 	err = ssd16xx_write_cmd(dev, SSD16XX_CMD_RAM_READ_CTRL,
533 				&ram_ctrl, sizeof(ram_ctrl));
534 	if (err < 0) {
535 		return err;
536 	}
537 
538 	err = ssd16xx_read_cmd(dev, SSD16XX_CMD_READ_RAM, (uint8_t *)buf,
539 			       buf_len);
540 	if (err < 0) {
541 		return err;
542 	}
543 
544 	return 0;
545 }
546 
ssd16xx_read(const struct device * dev,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc,void * buf)547 static int ssd16xx_read(const struct device *dev,
548 			const uint16_t x, const uint16_t y,
549 			const struct display_buffer_descriptor *desc,
550 			void *buf)
551 {
552 	return ssd16xx_read_ram(dev, SSD16XX_RAM_BLACK, x, y, desc, buf);
553 }
554 
ssd16xx_get_capabilities(const struct device * dev,struct display_capabilities * caps)555 static void ssd16xx_get_capabilities(const struct device *dev,
556 				     struct display_capabilities *caps)
557 {
558 	const struct ssd16xx_config *config = dev->config;
559 	struct ssd16xx_data *data = dev->data;
560 
561 	memset(caps, 0, sizeof(struct display_capabilities));
562 	caps->x_resolution = config->width;
563 	caps->y_resolution = config->height -
564 			     config->height % EPD_PANEL_NUMOF_ROWS_PER_PAGE;
565 	caps->supported_pixel_formats = PIXEL_FORMAT_MONO10;
566 	caps->current_pixel_format = PIXEL_FORMAT_MONO10;
567 	caps->screen_info = SCREEN_INFO_MONO_MSB_FIRST | SCREEN_INFO_EPD;
568 
569 	if (data->orientation == DISPLAY_ORIENTATION_NORMAL ||
570 	    data->orientation == DISPLAY_ORIENTATION_ROTATED_180) {
571 		caps->screen_info |= SCREEN_INFO_MONO_VTILED;
572 	}
573 
574 	caps->current_orientation = data->orientation;
575 }
576 
ssd16xx_set_pixel_format(const struct device * dev,const enum display_pixel_format pf)577 static int ssd16xx_set_pixel_format(const struct device *dev,
578 				    const enum display_pixel_format pf)
579 {
580 	if (pf == PIXEL_FORMAT_MONO10) {
581 		return 0;
582 	}
583 
584 	LOG_ERR("not supported");
585 	return -ENOTSUP;
586 }
587 
ssd16xx_set_orientation(const struct device * dev,const enum display_orientation orientation)588 static int ssd16xx_set_orientation(const struct device *dev,
589 				   const enum display_orientation orientation)
590 {
591 	struct ssd16xx_data *data = dev->data;
592 	int err;
593 
594 	if (orientation == DISPLAY_ORIENTATION_NORMAL) {
595 		data->scan_mode = SSD16XX_DATA_ENTRY_XDYIY;
596 	} else if (orientation == DISPLAY_ORIENTATION_ROTATED_90) {
597 		data->scan_mode = SSD16XX_DATA_ENTRY_XDYDX;
598 	} else if (orientation == DISPLAY_ORIENTATION_ROTATED_180) {
599 		data->scan_mode = SSD16XX_DATA_ENTRY_XIYDY;
600 	} else if (orientation == DISPLAY_ORIENTATION_ROTATED_270) {
601 		data->scan_mode = SSD16XX_DATA_ENTRY_XIYIX;
602 	}
603 
604 	err = ssd16xx_write_uint8(dev, SSD16XX_CMD_ENTRY_MODE, data->scan_mode);
605 	if (err < 0) {
606 		return err;
607 	}
608 
609 	data->orientation = orientation;
610 
611 	return 0;
612 }
613 
ssd16xx_clear_cntlr_mem(const struct device * dev,uint8_t ram_cmd)614 static int ssd16xx_clear_cntlr_mem(const struct device *dev, uint8_t ram_cmd)
615 {
616 	const struct ssd16xx_config *config = dev->config;
617 	uint16_t panel_h = config->height / EPD_PANEL_NUMOF_ROWS_PER_PAGE;
618 	uint16_t last_gate = config->width - 1;
619 	uint8_t clear_page[64];
620 	int err;
621 
622 	/*
623 	 * Clear unusable memory area when the resolution of the panel is not
624 	 * multiple of an octet.
625 	 */
626 	if (config->height % EPD_PANEL_NUMOF_ROWS_PER_PAGE) {
627 		panel_h += 1;
628 	}
629 
630 	err = ssd16xx_write_uint8(dev, SSD16XX_CMD_ENTRY_MODE,
631 				  SSD16XX_DATA_ENTRY_XIYDY);
632 	if (err < 0) {
633 		return err;
634 	}
635 
636 	err = ssd16xx_set_ram_param(dev, SSD16XX_PANEL_FIRST_PAGE,
637 				    panel_h - 1, last_gate,
638 				    SSD16XX_PANEL_FIRST_GATE);
639 	if (err < 0) {
640 		return err;
641 	}
642 
643 	err = ssd16xx_set_ram_ptr(dev, SSD16XX_PANEL_FIRST_PAGE, last_gate);
644 	if (err < 0) {
645 		return err;
646 	}
647 
648 	memset(clear_page, 0xff, sizeof(clear_page));
649 	for (int h = 0; h < panel_h; h++) {
650 		size_t x = config->width;
651 
652 		while (x) {
653 			size_t l = MIN(x, sizeof(clear_page));
654 
655 			x -= l;
656 			err = ssd16xx_write_cmd(dev, ram_cmd, clear_page, l);
657 			if (err < 0) {
658 				return err;
659 			}
660 		}
661 	}
662 
663 	return 0;
664 }
665 
ssd16xx_load_ws_from_otp_tssv(const struct device * dev)666 static inline int ssd16xx_load_ws_from_otp_tssv(const struct device *dev)
667 {
668 	const struct ssd16xx_config *config = dev->config;
669 
670 	/*
671 	 * Controller has an integrated temperature sensor or external
672 	 * temperature sensor is connected to the controller.
673 	 */
674 	LOG_INF("Select and load WS from OTP");
675 	return ssd16xx_write_uint8(dev, SSD16XX_CMD_TSENSOR_SELECTION,
676 				   config->tssv);
677 }
678 
ssd16xx_load_ws_from_otp(const struct device * dev)679 static inline int ssd16xx_load_ws_from_otp(const struct device *dev)
680 {
681 	int16_t t = (SSD16XX_DEFAULT_TR_VALUE * SSD16XX_TR_SCALE_FACTOR);
682 	uint8_t tmp[2];
683 	int err;
684 
685 	LOG_INF("Load default WS (25 degrees Celsius) from OTP");
686 
687 	err = ssd16xx_activate(dev, SSD16XX_CTRL2_ENABLE_CLK);
688 	if (err < 0) {
689 		return err;
690 	}
691 
692 	/* Load temperature value */
693 	sys_put_be16(t, tmp);
694 	err = ssd16xx_write_cmd(dev, SSD16XX_CMD_TSENS_CTRL, tmp, 2);
695 	if (err < 0) {
696 		return err;
697 	}
698 
699 	err = ssd16xx_activate(dev, SSD16XX_CTRL2_DISABLE_CLK);
700 	if (err < 0) {
701 		return err;
702 	}
703 
704 	return 0;
705 }
706 
707 
ssd16xx_load_lut(const struct device * dev,const struct ssd16xx_dt_array * lut)708 static int ssd16xx_load_lut(const struct device *dev,
709 			    const struct ssd16xx_dt_array *lut)
710 {
711 	const struct ssd16xx_config *config = dev->config;
712 
713 	if (lut && lut->len) {
714 		LOG_DBG("Using user-provided LUT");
715 		return ssd16xx_write_cmd(dev, SSD16XX_CMD_UPDATE_LUT,
716 					 lut->data, lut->len);
717 	} else {
718 		if (config->tssv) {
719 			return ssd16xx_load_ws_from_otp_tssv(dev);
720 		} else {
721 			return ssd16xx_load_ws_from_otp(dev);
722 		}
723 	}
724 }
725 
ssd16xx_set_profile(const struct device * dev,enum ssd16xx_profile_type type)726 static int ssd16xx_set_profile(const struct device *dev,
727 			       enum ssd16xx_profile_type type)
728 {
729 	const struct ssd16xx_config *config = dev->config;
730 	struct ssd16xx_data *data = dev->data;
731 	const struct ssd16xx_profile *p;
732 	const uint16_t last_gate = config->width - 1;
733 	uint8_t gdo[3];
734 	size_t gdo_len;
735 	int err = 0;
736 
737 	if (type >= SSD16XX_NUM_PROFILES) {
738 		return -EINVAL;
739 	}
740 
741 	p = config->profiles[type];
742 
743 	/*
744 	 * The full profile is the only one that always exists. If it
745 	 * hasn't been specified, we use the defaults.
746 	 */
747 	if (!p && type != SSD16XX_PROFILE_FULL) {
748 		return -ENOENT;
749 	}
750 
751 	if (type == data->profile) {
752 		return 0;
753 	}
754 
755 	/*
756 	 * Perform a soft reset to make sure registers are reset. This
757 	 * will leave the RAM contents intact.
758 	 */
759 	err = ssd16xx_write_cmd(dev, SSD16XX_CMD_SW_RESET, NULL, 0);
760 	if (err < 0) {
761 		return err;
762 	}
763 
764 	gdo_len = push_y_param(dev, gdo, last_gate);
765 	gdo[gdo_len++] = 0U;
766 	err = ssd16xx_write_cmd(dev, SSD16XX_CMD_GDO_CTRL, gdo, gdo_len);
767 	if (err < 0) {
768 		return err;
769 	}
770 
771 	if (config->softstart.len) {
772 		err = ssd16xx_write_cmd(dev, SSD16XX_CMD_SOFTSTART,
773 					config->softstart.data,
774 					config->softstart.len);
775 		if (err < 0) {
776 			return err;
777 		}
778 	}
779 
780 	err = ssd16xx_load_lut(dev, p ? &p->lut : NULL);
781 	if (err < 0) {
782 		return err;
783 	}
784 
785 	if (p && p->override_dummy_line) {
786 		err = ssd16xx_write_uint8(dev, SSD16XX_CMD_DUMMY_LINE,
787 					  p->dummy_line);
788 		if (err < 0) {
789 			return err;
790 		}
791 	}
792 
793 	if (p && p->override_gate_line_width) {
794 		err = ssd16xx_write_uint8(dev, SSD16XX_CMD_GATE_LINE_WIDTH,
795 					  p->override_gate_line_width);
796 		if (err < 0) {
797 			return err;
798 		}
799 	}
800 
801 	if (p && p->gdv.len) {
802 		LOG_DBG("Setting GDV");
803 		err = ssd16xx_write_cmd(dev, SSD16XX_CMD_GDV_CTRL,
804 					p->gdv.data, p->gdv.len);
805 		if (err < 0) {
806 			return err;
807 		}
808 	}
809 
810 	if (p && p->sdv.len) {
811 		LOG_DBG("Setting SDV");
812 		err = ssd16xx_write_cmd(dev, SSD16XX_CMD_SDV_CTRL,
813 					p->sdv.data, p->sdv.len);
814 		if (err < 0) {
815 			return err;
816 		}
817 	}
818 
819 	if (p && p->override_vcom) {
820 		LOG_DBG("Setting VCOM");
821 		err = ssd16xx_write_cmd(dev, SSD16XX_CMD_VCOM_VOLTAGE,
822 					&p->vcom, 1);
823 		if (err < 0) {
824 			return err;
825 		}
826 	}
827 
828 	if (p && p->override_bwf) {
829 		LOG_DBG("Setting BWF");
830 		err = ssd16xx_write_cmd(dev, SSD16XX_CMD_BWF_CTRL,
831 					&p->bwf, 1);
832 		if (err < 0) {
833 			return err;
834 		}
835 	}
836 
837 	err = ssd16xx_write_uint8(dev, SSD16XX_CMD_ENTRY_MODE, data->scan_mode);
838 	if (err < 0) {
839 		return err;
840 	}
841 
842 	data->profile = type;
843 
844 	return 0;
845 }
846 
ssd16xx_controller_init(const struct device * dev)847 static int ssd16xx_controller_init(const struct device *dev)
848 {
849 	const struct ssd16xx_config *config = dev->config;
850 	struct ssd16xx_data *data = dev->data;
851 	enum display_orientation orientation;
852 	int err;
853 
854 	LOG_DBG("");
855 
856 	data->blanking_on = false;
857 	data->profile = SSD16XX_PROFILE_INVALID;
858 
859 	err = mipi_dbi_reset(config->mipi_dev, SSD16XX_RESET_DELAY);
860 	if (err < 0) {
861 		return err;
862 	}
863 
864 	k_msleep(SSD16XX_RESET_DELAY);
865 
866 	err = ssd16xx_set_profile(dev, SSD16XX_PROFILE_FULL);
867 	if (err < 0) {
868 		return err;
869 	}
870 
871 	err = ssd16xx_clear_cntlr_mem(dev, SSD16XX_CMD_WRITE_RAM);
872 	if (err < 0) {
873 		return err;
874 	}
875 
876 	err = ssd16xx_clear_cntlr_mem(dev, SSD16XX_CMD_WRITE_RED_RAM);
877 	if (err < 0) {
878 		return err;
879 	}
880 
881 	if (config->rotation == 0U) {
882 		orientation = DISPLAY_ORIENTATION_NORMAL;
883 	} else if (config->rotation == 90U) {
884 		orientation = DISPLAY_ORIENTATION_ROTATED_90;
885 	} else if (config->rotation == 180U) {
886 		orientation = DISPLAY_ORIENTATION_ROTATED_180;
887 	} else {
888 		orientation = DISPLAY_ORIENTATION_ROTATED_270;
889 	}
890 
891 	err = ssd16xx_set_orientation(dev, orientation);
892 	if (err < 0) {
893 		return err;
894 	}
895 
896 	err = ssd16xx_update_display(dev);
897 	if (err < 0) {
898 		return err;
899 	}
900 
901 	return 0;
902 }
903 
ssd16xx_init(const struct device * dev)904 static int ssd16xx_init(const struct device *dev)
905 {
906 	const struct ssd16xx_config *config = dev->config;
907 	struct ssd16xx_data *data = dev->data;
908 	int err;
909 
910 	LOG_DBG("");
911 
912 	if (!device_is_ready(config->mipi_dev)) {
913 		LOG_ERR("MIPI Device not ready");
914 		return -ENODEV;
915 	}
916 
917 	data->read_supported =
918 		(config->dbi_config.config.operation & SPI_HALF_DUPLEX) != 0;
919 
920 	if (!gpio_is_ready_dt(&config->busy_gpio)) {
921 		LOG_ERR("Busy GPIO device not ready");
922 		return -ENODEV;
923 	}
924 
925 	err = gpio_pin_configure_dt(&config->busy_gpio, GPIO_INPUT);
926 	if (err < 0) {
927 		LOG_ERR("Failed to configure busy GPIO");
928 		return err;
929 	}
930 
931 	if (config->width > config->quirks->max_width ||
932 	    config->height > config->quirks->max_height) {
933 		LOG_ERR("Display size out of range.");
934 		return -EINVAL;
935 	}
936 
937 	return ssd16xx_controller_init(dev);
938 }
939 
940 static const struct display_driver_api ssd16xx_driver_api = {
941 	.blanking_on = ssd16xx_blanking_on,
942 	.blanking_off = ssd16xx_blanking_off,
943 	.write = ssd16xx_write,
944 	.read = ssd16xx_read,
945 	.get_capabilities = ssd16xx_get_capabilities,
946 	.set_pixel_format = ssd16xx_set_pixel_format,
947 	.set_orientation = ssd16xx_set_orientation,
948 };
949 
950 #if DT_HAS_COMPAT_STATUS_OKAY(solomon_ssd1608)
951 static struct ssd16xx_quirks quirks_solomon_ssd1608 = {
952 	.max_width = 320,
953 	.max_height = 240,
954 	.pp_width_bits = 16,
955 	.pp_height_bits = 16,
956 	.ctrl2_full = SSD16XX_GEN1_CTRL2_TO_PATTERN,
957 	.ctrl2_partial = SSD16XX_GEN1_CTRL2_TO_PATTERN,
958 };
959 #endif
960 
961 #if DT_HAS_COMPAT_STATUS_OKAY(solomon_ssd1673)
962 static struct ssd16xx_quirks quirks_solomon_ssd1673 = {
963 	.max_width = 250,
964 	.max_height = 150,
965 	.pp_width_bits = 8,
966 	.pp_height_bits = 8,
967 	.ctrl2_full = SSD16XX_GEN1_CTRL2_TO_PATTERN,
968 	.ctrl2_partial = SSD16XX_GEN1_CTRL2_TO_PATTERN,
969 };
970 #endif
971 
972 #if DT_HAS_COMPAT_STATUS_OKAY(solomon_ssd1675a)
973 static struct ssd16xx_quirks quirks_solomon_ssd1675a = {
974 	.max_width = 296,
975 	.max_height = 160,
976 	.pp_width_bits = 8,
977 	.pp_height_bits = 16,
978 	.ctrl2_full = SSD16XX_GEN1_CTRL2_TO_PATTERN,
979 	.ctrl2_partial = SSD16XX_GEN1_CTRL2_TO_PATTERN,
980 };
981 #endif
982 
983 #if DT_HAS_COMPAT_STATUS_OKAY(solomon_ssd1680)
984 static const struct ssd16xx_quirks quirks_solomon_ssd1680 = {
985 	.max_width = 296,
986 	.max_height = 176,
987 	.pp_width_bits = 8,
988 	.pp_height_bits = 16,
989 	.ctrl2_full = SSD16XX_GEN2_CTRL2_DISPLAY,
990 	.ctrl2_partial = SSD16XX_GEN2_CTRL2_DISPLAY | SSD16XX_GEN2_CTRL2_MODE2,
991 };
992 #endif
993 
994 #if DT_HAS_COMPAT_STATUS_OKAY(solomon_ssd1681)
995 static struct ssd16xx_quirks quirks_solomon_ssd1681 = {
996 	.max_width = 200,
997 	.max_height = 200,
998 	.pp_width_bits = 8,
999 	.pp_height_bits = 16,
1000 	.ctrl2_full = SSD16XX_GEN2_CTRL2_DISPLAY,
1001 	.ctrl2_partial = SSD16XX_GEN2_CTRL2_DISPLAY | SSD16XX_GEN2_CTRL2_MODE2,
1002 };
1003 #endif
1004 
1005 #define SOFTSTART_ASSIGN(n)						\
1006 		.softstart = {						\
1007 			.data = softstart_##n,				\
1008 			.len = sizeof(softstart_##n),			\
1009 		},
1010 
1011 #define SSD16XX_MAKE_ARRAY_OPT(n, p)					\
1012 	static uint8_t data_ ## n ## _ ## p[] = DT_PROP_OR(n, p, {})
1013 
1014 #define SSD16XX_ASSIGN_ARRAY(n, p)					\
1015 	{								\
1016 		.data = data_ ## n ## _ ## p,				\
1017 		.len = sizeof(data_ ## n ## _ ## p),			\
1018 	}
1019 
1020 #define SSD16XX_PROFILE(n)						\
1021 	SSD16XX_MAKE_ARRAY_OPT(n, lut);					\
1022 	SSD16XX_MAKE_ARRAY_OPT(n, gdv);					\
1023 	SSD16XX_MAKE_ARRAY_OPT(n, sdv);					\
1024 									\
1025 	static const struct ssd16xx_profile ssd16xx_profile_ ## n = {	\
1026 		.lut = SSD16XX_ASSIGN_ARRAY(n, lut),			\
1027 		.gdv = SSD16XX_ASSIGN_ARRAY(n, gdv),			\
1028 		.sdv = SSD16XX_ASSIGN_ARRAY(n, sdv),			\
1029 		.vcom = DT_PROP_OR(n, vcom, 0),				\
1030 		.override_vcom = DT_NODE_HAS_PROP(n, vcom),		\
1031 		.bwf = DT_PROP_OR(n, border_waveform, 0),		\
1032 		.override_bwf = DT_NODE_HAS_PROP(n, border_waveform),	\
1033 		.dummy_line = DT_PROP_OR(n, dummy_line, 0),		\
1034 		.override_dummy_line = DT_NODE_HAS_PROP(n, dummy_line),	\
1035 		.gate_line_width = DT_PROP_OR(n, gate_line_width, 0),	\
1036 		.override_gate_line_width = DT_NODE_HAS_PROP(		\
1037 			n, gate_line_width),				\
1038 	};
1039 
1040 
1041 #define _SSD16XX_PROFILE_PTR(n) &ssd16xx_profile_ ## n
1042 
1043 #define SSD16XX_PROFILE_PTR(n)						\
1044 	COND_CODE_1(DT_NODE_EXISTS(n),					\
1045 		    (_SSD16XX_PROFILE_PTR(n)),				\
1046 		    NULL)
1047 
1048 #define SSD16XX_DEFINE(n, quirks_ptr)					\
1049 	SSD16XX_MAKE_ARRAY_OPT(n, softstart);				\
1050 									\
1051 	DT_FOREACH_CHILD(n, SSD16XX_PROFILE);				\
1052 									\
1053 	static const struct ssd16xx_config ssd16xx_cfg_ ## n = {	\
1054 		.mipi_dev = DEVICE_DT_GET(DT_PARENT(n)),                \
1055 		.dbi_config = {                                         \
1056 			.mode = MIPI_DBI_MODE_SPI_4WIRE,                \
1057 			.config = MIPI_DBI_SPI_CONFIG_DT(n,             \
1058 				SPI_OP_MODE_MASTER | SPI_WORD_SET(8) |  \
1059 				SPI_HOLD_ON_CS | SPI_LOCK_ON, 0),       \
1060 		},                                                      \
1061 		.busy_gpio = GPIO_DT_SPEC_GET(n, busy_gpios),		\
1062 		.quirks = quirks_ptr,					\
1063 		.height = DT_PROP(n, height),				\
1064 		.width = DT_PROP(n, width),				\
1065 		.rotation = DT_PROP(n, rotation),			\
1066 		.tssv = DT_PROP_OR(n, tssv, 0),				\
1067 		.softstart = SSD16XX_ASSIGN_ARRAY(n, softstart),	\
1068 		.profiles = {						\
1069 			[SSD16XX_PROFILE_FULL] =			\
1070 				SSD16XX_PROFILE_PTR(DT_CHILD(n, full)),	\
1071 			[SSD16XX_PROFILE_PARTIAL] =			\
1072 				SSD16XX_PROFILE_PTR(DT_CHILD(n, partial)),\
1073 		},							\
1074 	};								\
1075 									\
1076 	static struct ssd16xx_data ssd16xx_data_ ## n;			\
1077 									\
1078 	DEVICE_DT_DEFINE(n,						\
1079 			 ssd16xx_init, NULL,				\
1080 			 &ssd16xx_data_ ## n,				\
1081 			 &ssd16xx_cfg_ ## n,				\
1082 			 POST_KERNEL,					\
1083 			 CONFIG_DISPLAY_INIT_PRIORITY,			\
1084 			 &ssd16xx_driver_api)
1085 
1086 DT_FOREACH_STATUS_OKAY_VARGS(solomon_ssd1608, SSD16XX_DEFINE,
1087 			     &quirks_solomon_ssd1608);
1088 DT_FOREACH_STATUS_OKAY_VARGS(solomon_ssd1673, SSD16XX_DEFINE,
1089 			     &quirks_solomon_ssd1673);
1090 DT_FOREACH_STATUS_OKAY_VARGS(solomon_ssd1675a, SSD16XX_DEFINE,
1091 			     &quirks_solomon_ssd1675a);
1092 DT_FOREACH_STATUS_OKAY_VARGS(solomon_ssd1680, SSD16XX_DEFINE,
1093 			     &quirks_solomon_ssd1680);
1094 DT_FOREACH_STATUS_OKAY_VARGS(solomon_ssd1681, SSD16XX_DEFINE,
1095 			     &quirks_solomon_ssd1681);
1096