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