1 /*
2 * Copyright (c) 2023 bytes at work AG
3 * Copyright (c) 2020 Teslabs Engineering S.L.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #define DT_DRV_COMPAT orisetech_otm8009a
9
10 #include <zephyr/kernel.h>
11 #include <zephyr/drivers/display.h>
12 #include <zephyr/drivers/mipi_dsi.h>
13 #include <zephyr/drivers/gpio.h>
14 #include <zephyr/sys/byteorder.h>
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(otm8009a, CONFIG_DISPLAY_LOG_LEVEL);
17
18 #include "display_otm8009a.h"
19
20 struct otm8009a_config {
21 const struct device *mipi_dsi;
22 const struct gpio_dt_spec reset;
23 const struct gpio_dt_spec backlight;
24 uint8_t data_lanes;
25 uint16_t width;
26 uint16_t height;
27 uint8_t channel;
28 uint16_t rotation;
29 };
30
31 struct otm8009a_data {
32 uint16_t xres;
33 uint16_t yres;
34 uint8_t dsi_pixel_format;
35 enum display_pixel_format pixel_format;
36 enum display_orientation orientation;
37 };
38
otm8009a_dcs_write(const struct device * dev,uint8_t cmd,const void * buf,size_t len)39 static inline int otm8009a_dcs_write(const struct device *dev, uint8_t cmd, const void *buf,
40 size_t len)
41 {
42 const struct otm8009a_config *cfg = dev->config;
43 int ret;
44
45 ret = mipi_dsi_dcs_write(cfg->mipi_dsi, cfg->channel, cmd, buf, len);
46 if (ret < 0) {
47 LOG_ERR("DCS 0x%x write failed! (%d)", cmd, ret);
48 return ret;
49 }
50
51 return 0;
52 }
53
otm8009a_mcs_write(const struct device * dev,uint16_t cmd,const void * buf,size_t len)54 static int otm8009a_mcs_write(const struct device *dev, uint16_t cmd, const void *buf, size_t len)
55 {
56 const struct otm8009a_config *cfg = dev->config;
57 uint8_t scmd;
58 int ret;
59
60 scmd = cmd & 0xFF;
61 ret = mipi_dsi_dcs_write(cfg->mipi_dsi, cfg->channel, OTM8009A_MCS_ADRSFT, &scmd, 1);
62 if (ret < 0) {
63 return ret;
64 }
65
66 ret = mipi_dsi_dcs_write(cfg->mipi_dsi, cfg->channel, cmd >> 8, buf, len);
67 if (ret < 0) {
68 return ret;
69 }
70
71 return 0;
72 }
73
otm8009a_check_id(const struct device * dev)74 static int otm8009a_check_id(const struct device *dev)
75 {
76 const struct otm8009a_config *cfg = dev->config;
77 uint32_t id = 0;
78 int ret;
79
80 ret = mipi_dsi_dcs_read(cfg->mipi_dsi, cfg->channel, OTM8009A_CMD_ID1, &id, sizeof(id));
81 if (ret != sizeof(id)) {
82 LOG_ERR("Read panel ID failed! (%d)", ret);
83 return -EIO;
84 }
85
86 if (id != OTM8009A_ID1) {
87 LOG_ERR("ID 0x%x (should 0x%x)", id, OTM8009A_ID1);
88 return -EINVAL;
89 }
90
91 return 0;
92 }
93
otm8009a_configure(const struct device * dev)94 static int otm8009a_configure(const struct device *dev)
95 {
96 struct otm8009a_data *data = dev->data;
97 uint8_t buf[4];
98 int ret;
99
100 static const uint8_t pwr_ctrl2[] = {0x96, 0x34, 0x01, 0x33, 0x33, 0x34, 0x33};
101 static const uint8_t sd_ctrl[] = {0x0D, 0x1B, 0x02, 0x01, 0x3C, 0x08};
102 static const uint8_t goavst[] = {
103 0x85, 0x01, 0x00, 0x84, 0x01, 0x00, 0x81, 0x01, 0x28, 0x82, 0x01, 0x28
104 };
105 static const uint8_t goaclka1[] = {0x18, 0x04, 0x03, 0x39, 0x00, 0x00, 0x00};
106 static const uint8_t goaclka2[] = {0x18, 0x03, 0x03, 0x3A, 0x00, 0x00, 0x00};
107 static const uint8_t goaclka3[] = {0x18, 0x02, 0x03, 0x3B, 0x00, 0x00, 0x00};
108 static const uint8_t goaclka4[] = {0x18, 0x01, 0x03, 0x3C, 0x00, 0x00, 0x00};
109 static const uint8_t goaeclk[] = {0x01, 0x01, 0x20, 0x20, 0x00, 0x00};
110 static const uint8_t panctrlset1[] = {
111 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
112 };
113 static const uint8_t panctrlset2[] = {
114 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
115 0x00
116 };
117 static const uint8_t panctrlset3[] = {
118 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
119 0x00
120 };
121 static const uint8_t panctrlset4[] = {
122 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
123 };
124 static const uint8_t panctrlset5[] = {
125 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
126 0x00
127 };
128 static const uint8_t panctrlset6[] = {
129 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00,
130 0x00
131 };
132 static const uint8_t panctrlset7[] = {
133 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
134 };
135 static const uint8_t panctrlset8[] = {
136 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
137 };
138 static const uint8_t panu2d1[] = {
139 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25, 0x00, 0x00, 0x00, 0x00
140 };
141 static const uint8_t panu2d2[] = {
142 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0A, 0x0C,
143 0x02
144 };
145 static const uint8_t panu2d3[] = {
146 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
147 0x00
148 };
149 static const uint8_t pand2u1[] = {
150 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26, 0x00, 0x00, 0x00, 0x00
151 };
152 static const uint8_t pand2u2[] = {
153 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x0B, 0x09,
154 0x01
155 };
156 static const uint8_t pand2u3[] = {
157 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
158 0x00
159 };
160 static const uint8_t pgamma[] = {
161 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10,
162 0x0A, 0x01
163 };
164 static const uint8_t ngamma[] = {
165 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10,
166 0x0A, 0x01
167 };
168
169 /* enter command 2 mode to access manufacturer registers (ref. 5.3) */
170 buf[0] = 0x80;
171 buf[1] = 0x09;
172 buf[2] = 0x01;
173 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_CMD2_ENA1, buf, 3);
174 if (ret < 0) {
175 return ret;
176 }
177
178 /* enter Orise command 2 mode */
179 buf[0] = 0x80;
180 buf[1] = 0x09;
181 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_CMD2_ENA2, buf, 2);
182 if (ret < 0) {
183 return ret;
184 }
185
186 /* source driver precharge control */
187 buf[0] = 0x30;
188 buf[1] = 0x8A;
189 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_SD_PCH_CTRL, buf, 2);
190 if (ret < 0) {
191 return ret;
192 }
193
194 /* not documented */
195 buf[0] = 0x40;
196 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_NO_DOC1, buf, 1);
197 if (ret < 0) {
198 return ret;
199 }
200
201 /* power control settings 4 for DC voltage settings */
202 /* enable GVDD test mode */
203 buf[0] = 0x04;
204 buf[1] = 0xA9;
205 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_PWR_CTRL4, buf, 2);
206 if (ret < 0) {
207 return ret;
208 }
209
210 /* power control settings 2 for normal mode */
211 /* set pump 4 vgh voltage from 15.0v down to 13.0v */
212 /* set pump 5 vgh voltage from -12.0v downto -9.0v */
213 /* set pump 4&5 x6 (ONLY VALID when PUMP4_EN_ASDM_HV = "0") */
214 /* change pump4 clock ratio from 1 line to 1/2 line */
215 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_PWR_CTRL2, pwr_ctrl2, sizeof(pwr_ctrl2));
216 if (ret < 0) {
217 return ret;
218 }
219
220 /* panel driving mode */
221 /* set column inversion */
222 buf[0] = 0x50;
223 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_P_DRV_M, buf, 1);
224 if (ret < 0) {
225 return ret;
226 }
227
228 /* VCOM voltage setting */
229 /* VCOM Voltage settings from -1.0000v downto -1.2625v */
230 buf[0] = 0x4E;
231 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_VCOMDC, buf, 1);
232 if (ret < 0) {
233 return ret;
234 }
235
236 /* oscillator adjustment for idle/normal mode */
237 /* set 65Hz */
238 buf[0] = 0x66;
239 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_OSC_ADJ, buf, 1);
240 if (ret < 0) {
241 return ret;
242 }
243
244 /* RGB video mode setting */
245 buf[0] = 0x08;
246 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_RGB_VID_SET, buf, 1);
247 if (ret < 0) {
248 return ret;
249 }
250
251 /* GVDD/NGVDD */
252 buf[0] = 0x79;
253 buf[1] = 0x79;
254 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_GVDDSET, buf, 2);
255 if (ret < 0) {
256 return ret;
257 }
258
259 /* source driver timing setting */
260 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_SD_CTRL, sd_ctrl, sizeof(sd_ctrl));
261 if (ret < 0) {
262 return ret;
263 }
264
265 /* panel type setting */
266 buf[0] = 0x00;
267 buf[1] = 0x01;
268 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_PANSET, buf, 2);
269 if (ret < 0) {
270 return ret;
271 }
272
273 /* GOA VST setting */
274 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_GOAVST, goavst, sizeof(goavst));
275 if (ret < 0) {
276 return ret;
277 }
278
279 /* GOA CLKA1 setting */
280 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_GOACLKA1, goaclka1, sizeof(goaclka1));
281 if (ret < 0) {
282 return ret;
283 }
284
285 /* GOA CLKA2 setting */
286 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_GOACLKA2, goaclka2, sizeof(goaclka2));
287 if (ret < 0) {
288 return ret;
289 }
290
291 /* GOA CLKA3 setting */
292 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_GOACLKA3, goaclka3, sizeof(goaclka3));
293 if (ret < 0) {
294 return ret;
295 }
296
297 /* GOA CLKA4 setting */
298 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_GOACLKA4, goaclka4, sizeof(goaclka4));
299 if (ret < 0) {
300 return ret;
301 }
302
303 /* GOA ECLK */
304 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_GOAECLK, goaeclk, sizeof(goaeclk));
305 if (ret < 0) {
306 return ret;
307 }
308
309 /** GOA Other Options 1 */
310 buf[0] = 0x01;
311 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_GOAPT1, buf, 1);
312 if (ret < 0) {
313 return ret;
314 }
315
316 /* GOA Signal Toggle Option Setting */
317 buf[0] = 0x02;
318 buf[1] = 0x00;
319 buf[2] = 0x00;
320 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_GOATGOPT, buf, 3);
321 if (ret < 0) {
322 return ret;
323 }
324
325 /* not documented */
326 buf[0] = 0x00;
327 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_NO_DOC2, buf, 1);
328 if (ret < 0) {
329 return ret;
330 }
331
332 /* Panel Control Setting 1 */
333 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_PANCTRLSET1, panctrlset1, sizeof(panctrlset1));
334 if (ret < 0) {
335 return ret;
336 }
337
338 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_PANCTRLSET2, panctrlset2, sizeof(panctrlset2));
339 if (ret < 0) {
340 return ret;
341 }
342
343 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_PANCTRLSET3, panctrlset3, sizeof(panctrlset3));
344 if (ret < 0) {
345 return ret;
346 }
347
348 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_PANCTRLSET4, panctrlset4, sizeof(panctrlset4));
349 if (ret < 0) {
350 return ret;
351 }
352
353 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_PANCTRLSET5, panctrlset5, sizeof(panctrlset5));
354 if (ret < 0) {
355 return ret;
356 }
357
358 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_PANCTRLSET6, panctrlset6, sizeof(panctrlset6));
359 if (ret < 0) {
360 return ret;
361 }
362
363 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_PANCTRLSET7, panctrlset7, sizeof(panctrlset7));
364 if (ret < 0) {
365 return ret;
366 }
367
368 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_PANCTRLSET8, panctrlset8, sizeof(panctrlset8));
369 if (ret < 0) {
370 return ret;
371 }
372
373 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_PANU2D1, panu2d1, sizeof(panu2d1));
374 if (ret < 0) {
375 return ret;
376 }
377
378 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_PANU2D2, panu2d2, sizeof(panu2d2));
379 if (ret < 0) {
380 return ret;
381 }
382
383 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_PANU2D3, panu2d3, sizeof(panu2d3));
384 if (ret < 0) {
385 return ret;
386 }
387
388 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_PAND2U1, pand2u1, sizeof(pand2u1));
389 if (ret < 0) {
390 return ret;
391 }
392
393 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_PAND2U2, pand2u2, sizeof(pand2u2));
394 if (ret < 0) {
395 return ret;
396 }
397
398 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_PAND2U3, pand2u3, sizeof(pand2u3));
399 if (ret < 0) {
400 return ret;
401 }
402
403 /* power control setting 1 */
404 /* Pump 1 min and max DM */
405 buf[0] = 0x08;
406 buf[1] = 0x66;
407 buf[2] = 0x83;
408 buf[3] = 0x00;
409 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_PWR_CTRL1, buf, 4);
410 if (ret < 0) {
411 return ret;
412 }
413
414 /* not documented */
415 buf[0] = 0x06;
416 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_NO_DOC3, buf, 1);
417 if (ret < 0) {
418 return ret;
419 }
420
421 /* PWM parameter 3 */
422 /* Freq: 19.5 KHz */
423 buf[0] = 0x06;
424 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_PWM_PARA3, buf, 1);
425 if (ret < 0) {
426 return ret;
427 }
428
429 /* gamma correction 2.2+ */
430 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_GMCT2_2P, pgamma, sizeof(pgamma));
431 if (ret < 0) {
432 return ret;
433 }
434
435 /* gamma correction 2.2- */
436 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_GMCT2_2N, ngamma, sizeof(ngamma));
437 if (ret < 0) {
438 return ret;
439 }
440
441 /* exit command 2 mode */
442 buf[0] = 0xFF;
443 buf[1] = 0xFF;
444 buf[2] = 0xFF;
445 ret = otm8009a_mcs_write(dev, OTM8009A_MCS_CMD2_ENA1, buf, 3);
446 if (ret < 0) {
447 return ret;
448 }
449
450 /* exit sleep mode */
451 ret = otm8009a_dcs_write(dev, MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0);
452 if (ret < 0) {
453 return ret;
454 }
455
456 k_msleep(OTM8009A_EXIT_SLEEP_MODE_WAIT_TIME);
457
458 /* set pixel color format */
459 switch (data->dsi_pixel_format) {
460 case MIPI_DSI_PIXFMT_RGB565:
461 buf[0] = MIPI_DCS_PIXEL_FORMAT_16BIT;
462 break;
463 case MIPI_DSI_PIXFMT_RGB888:
464 buf[0] = MIPI_DCS_PIXEL_FORMAT_24BIT;
465 break;
466 default:
467 LOG_ERR("Unsupported pixel format 0x%x!", data->dsi_pixel_format);
468 return -ENOTSUP;
469 }
470
471 ret = otm8009a_dcs_write(dev, MIPI_DCS_SET_PIXEL_FORMAT, buf, 1);
472 if (ret < 0) {
473 return ret;
474 }
475
476 /* configure address mode */
477 if (data->orientation == DISPLAY_ORIENTATION_NORMAL) {
478 buf[0] = 0x00;
479 } else if (data->orientation == DISPLAY_ORIENTATION_ROTATED_90) {
480 buf[0] = MIPI_DCS_ADDRESS_MODE_MIRROR_X | MIPI_DCS_ADDRESS_MODE_SWAP_XY;
481 } else if (data->orientation == DISPLAY_ORIENTATION_ROTATED_180) {
482 buf[0] = MIPI_DCS_ADDRESS_MODE_MIRROR_X | MIPI_DCS_ADDRESS_MODE_MIRROR_Y;
483 } else if (data->orientation == DISPLAY_ORIENTATION_ROTATED_270) {
484 buf[0] = MIPI_DCS_ADDRESS_MODE_MIRROR_Y | MIPI_DCS_ADDRESS_MODE_SWAP_XY;
485 }
486
487 ret = otm8009a_dcs_write(dev, MIPI_DCS_SET_ADDRESS_MODE, buf, 1);
488 if (ret < 0) {
489 return ret;
490 }
491
492 buf[0] = 0x00;
493 buf[1] = 0x00;
494 sys_put_be16(data->xres, (uint8_t *)&buf[2]);
495 ret = otm8009a_dcs_write(dev, MIPI_DCS_SET_COLUMN_ADDRESS, buf, 4);
496 if (ret < 0) {
497 return ret;
498 }
499
500 buf[0] = 0x00;
501 buf[1] = 0x00;
502 sys_put_be16(data->yres, (uint8_t *)&buf[2]);
503 ret = otm8009a_dcs_write(dev, MIPI_DCS_SET_PAGE_ADDRESS, buf, 4);
504 if (ret < 0) {
505 return ret;
506 }
507
508 /* backlight control */
509 buf[0] = OTM8009A_WRCTRLD_BCTRL | OTM8009A_WRCTRLD_DD | OTM8009A_WRCTRLD_BL;
510 ret = otm8009a_dcs_write(dev, MIPI_DCS_WRITE_CONTROL_DISPLAY, buf, 1);
511 if (ret < 0) {
512 return ret;
513 }
514
515 /* adaptive brightness control */
516 buf[0] = OTM8009A_WRCABC_UI;
517 ret = otm8009a_dcs_write(dev, MIPI_DCS_WRITE_POWER_SAVE, buf, 1);
518 if (ret < 0) {
519 return ret;
520 }
521
522 /* adaptive brightness control minimum brightness */
523 buf[0] = 0xFF;
524 ret = otm8009a_dcs_write(dev, MIPI_DCS_SET_CABC_MIN_BRIGHTNESS, buf, 1);
525 if (ret < 0) {
526 return ret;
527 }
528
529 /* brightness */
530 buf[0] = 0xFF;
531 ret = otm8009a_dcs_write(dev, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, buf, 1);
532 if (ret < 0) {
533 return ret;
534 }
535
536 /* Display On */
537 ret = otm8009a_dcs_write(dev, MIPI_DCS_SET_DISPLAY_ON, NULL, 0);
538 if (ret < 0) {
539 return ret;
540 }
541
542 /* trigger display write (from data coming by DSI bus) */
543 ret = otm8009a_dcs_write(dev, MIPI_DCS_WRITE_MEMORY_START, NULL, 0);
544 if (ret < 0) {
545 return ret;
546 }
547
548 return 0;
549 }
550
otm8009a_blanking_on(const struct device * dev)551 static int otm8009a_blanking_on(const struct device *dev)
552 {
553 const struct otm8009a_config *cfg = dev->config;
554 int ret;
555
556 if (cfg->backlight.port != NULL) {
557 ret = gpio_pin_set_dt(&cfg->backlight, 0);
558 if (ret) {
559 LOG_ERR("Disable backlight failed! (%d)", ret);
560 return ret;
561 }
562 }
563
564 return otm8009a_dcs_write(dev, MIPI_DCS_SET_DISPLAY_OFF, NULL, 0);
565 }
566
otm8009a_blanking_off(const struct device * dev)567 static int otm8009a_blanking_off(const struct device *dev)
568 {
569 const struct otm8009a_config *cfg = dev->config;
570 int ret;
571
572 if (cfg->backlight.port != NULL) {
573 ret = gpio_pin_set_dt(&cfg->backlight, 1);
574 if (ret) {
575 LOG_ERR("Enable backlight failed! (%d)", ret);
576 return ret;
577 }
578 }
579
580 return otm8009a_dcs_write(dev, MIPI_DCS_SET_DISPLAY_ON, NULL, 0);
581 }
582
otm8009a_write(const struct device * dev,uint16_t x,uint16_t y,const struct display_buffer_descriptor * desc,const void * buf)583 static int otm8009a_write(const struct device *dev, uint16_t x, uint16_t y,
584 const struct display_buffer_descriptor *desc, const void *buf)
585 {
586 return -ENOTSUP;
587 }
588
otm8009a_set_brightness(const struct device * dev,uint8_t brightness)589 static int otm8009a_set_brightness(const struct device *dev, uint8_t brightness)
590 {
591 return otm8009a_dcs_write(dev, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, &brightness, 1);
592 }
593
otm8009a_get_capabilities(const struct device * dev,struct display_capabilities * capabilities)594 static void otm8009a_get_capabilities(const struct device *dev,
595 struct display_capabilities *capabilities)
596 {
597 const struct otm8009a_config *cfg = dev->config;
598 struct otm8009a_data *data = dev->data;
599
600 memset(capabilities, 0, sizeof(struct display_capabilities));
601 capabilities->x_resolution = cfg->width;
602 capabilities->y_resolution = cfg->height;
603 capabilities->supported_pixel_formats = data->pixel_format;
604 capabilities->current_pixel_format = data->pixel_format;
605 capabilities->current_orientation = data->orientation;
606 }
607
608 static DEVICE_API(display, otm8009a_api) = {
609 .blanking_on = otm8009a_blanking_on,
610 .blanking_off = otm8009a_blanking_off,
611 .write = otm8009a_write,
612 .set_brightness = otm8009a_set_brightness,
613 .get_capabilities = otm8009a_get_capabilities,
614 };
615
otm8009a_init(const struct device * dev)616 static int otm8009a_init(const struct device *dev)
617 {
618 const struct otm8009a_config *cfg = dev->config;
619 struct otm8009a_data *data = dev->data;
620 struct mipi_dsi_device mdev;
621 int ret;
622
623 if (cfg->reset.port) {
624 if (!gpio_is_ready_dt(&cfg->reset)) {
625 LOG_ERR("Reset GPIO device is not ready!");
626 return -ENODEV;
627 }
628 ret = gpio_pin_configure_dt(&cfg->reset, GPIO_OUTPUT_INACTIVE);
629 if (ret < 0) {
630 LOG_ERR("Reset display failed! (%d)", ret);
631 return ret;
632 }
633 k_msleep(OTM8009A_RESET_TIME);
634 ret = gpio_pin_set_dt(&cfg->reset, 1);
635 if (ret < 0) {
636 LOG_ERR("Enable display failed! (%d)", ret);
637 return ret;
638 }
639 k_msleep(OTM8009A_WAKE_TIME);
640 }
641
642 /* store x/y resolution & rotation */
643 if (cfg->rotation == 0) {
644 data->xres = cfg->width;
645 data->yres = cfg->height;
646 data->orientation = DISPLAY_ORIENTATION_NORMAL;
647 } else if (cfg->rotation == 90) {
648 data->xres = cfg->height;
649 data->yres = cfg->width;
650 data->orientation = DISPLAY_ORIENTATION_ROTATED_90;
651 } else if (cfg->rotation == 180) {
652 data->xres = cfg->width;
653 data->yres = cfg->height;
654 data->orientation = DISPLAY_ORIENTATION_ROTATED_180;
655 } else if (cfg->rotation == 270) {
656 data->xres = cfg->height;
657 data->yres = cfg->width;
658 data->orientation = DISPLAY_ORIENTATION_ROTATED_270;
659 }
660
661 /* attach to MIPI-DSI host */
662 mdev.data_lanes = cfg->data_lanes;
663 mdev.pixfmt = data->dsi_pixel_format;
664 mdev.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM;
665
666 mdev.timings.hactive = data->xres;
667 mdev.timings.hbp = OTM8009A_HBP;
668 mdev.timings.hfp = OTM8009A_HFP;
669 mdev.timings.hsync = OTM8009A_HSYNC;
670 mdev.timings.vactive = data->yres;
671 mdev.timings.vbp = OTM8009A_VBP;
672 mdev.timings.vfp = OTM8009A_VFP;
673 mdev.timings.vsync = OTM8009A_VSYNC;
674
675 ret = mipi_dsi_attach(cfg->mipi_dsi, cfg->channel, &mdev);
676 if (ret < 0) {
677 LOG_ERR("MIPI-DSI attach failed! (%d)", ret);
678 return ret;
679 }
680
681 ret = otm8009a_check_id(dev);
682 if (ret) {
683 LOG_ERR("Panel ID check failed! (%d)", ret);
684 return ret;
685 }
686
687 ret = otm8009a_configure(dev);
688 if (ret) {
689 LOG_ERR("DSI init sequence failed! (%d)", ret);
690 return ret;
691 }
692
693 ret = otm8009a_blanking_off(dev);
694 if (ret) {
695 LOG_ERR("Display blanking off failed! (%d)", ret);
696 return ret;
697 }
698
699 return 0;
700 }
701
702 #define OTM8009A_DEVICE(inst) \
703 static const struct otm8009a_config otm8009a_config_##inst = { \
704 .mipi_dsi = DEVICE_DT_GET(DT_INST_BUS(inst)), \
705 .reset = GPIO_DT_SPEC_INST_GET_OR(inst, reset_gpios, {0}), \
706 .backlight = GPIO_DT_SPEC_INST_GET_OR(inst, bl_gpios, {0}), \
707 .data_lanes = DT_INST_PROP_BY_IDX(inst, data_lanes, 0), \
708 .width = DT_INST_PROP(inst, width), \
709 .height = DT_INST_PROP(inst, height), \
710 .channel = DT_INST_REG_ADDR(inst), \
711 .rotation = DT_INST_PROP(inst, rotation), \
712 }; \
713 static struct otm8009a_data otm8009a_data_##inst = { \
714 .dsi_pixel_format = DT_INST_PROP(inst, pixel_format), \
715 }; \
716 DEVICE_DT_INST_DEFINE(inst, &otm8009a_init, NULL, &otm8009a_data_##inst, \
717 &otm8009a_config_##inst, POST_KERNEL, \
718 CONFIG_DISPLAY_OTM8009A_INIT_PRIORITY, &otm8009a_api); \
719
720 DT_INST_FOREACH_STATUS_OKAY(OTM8009A_DEVICE)
721