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