1 /*
2 * Copyright 2023, NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT himax_hx8394
8
9 #include <zephyr/drivers/display.h>
10 #include <zephyr/drivers/mipi_dsi.h>
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/logging/log.h>
14
15 LOG_MODULE_REGISTER(hx8394, CONFIG_DISPLAY_LOG_LEVEL);
16
17 struct hx8394_config {
18 const struct device *mipi_dsi;
19 const struct gpio_dt_spec reset_gpio;
20 const struct gpio_dt_spec bl_gpio;
21 uint8_t num_of_lanes;
22 uint8_t pixel_format;
23 uint16_t panel_width;
24 uint16_t panel_height;
25 uint8_t channel;
26 };
27
28 /* MIPI DCS commands specific to this display driver */
29 #define HX8394_SETMIPI 0xBA
30 #define HX8394_MIPI_LPTX_BTA_READ BIT(6)
31 #define HX8394_MIPI_LP_CD_DIS BIT(5)
32 #define HX8394_MIPI_TA_6TL 0x3
33 #define HX8394_MIPI_DPHYCMD_LPRX_8NS 0x40
34 #define HX8394_MIPI_DPHYCMD_LPRX_66mV 0x20
35 #define HX8394_MIPI_DPHYCMD_LPTX_SRLIM 0x8
36 #define HX8394_MIPI_DPHYCMD_LDO_1_55V 0x60
37 #define HX8394_MIPI_DPHYCMD_HSRX_7X 0x8
38 #define HX8394_MIPI_DPHYCMD_HSRX_100OHM 0x2
39 #define HX8394_MIPI_DPHYCMD_LPCD_1X 0x1
40
41 #define HX8394_SET_ADDRESS 0x36
42 #define HX8394_FLIP_HORIZONTAL BIT(1)
43 #define HX8394_FLIP_VERTICAL BIT(0)
44
45 #define HX8394_SETPOWER 0xB1
46 #define HX8394_POWER_AP_1_0UA 0x8
47 #define HX8394_POWER_HX5186 0x40
48 #define HX8394_POWER_VRHP_4_8V 0x12
49 #define HX8394_POWER_VRHN_4_8V 0x12
50 #define HX8394_POWER_VPPS_8_25V 0x60
51 #define HX8394_POWER_XDK_X2 0x1
52 #define HX8394_POWER_VSP_FBOFF 0x8
53 #define HX8394_POWER_FS0_DIV_8 0x2
54 #define HX8394_POWER_CLK_OPT_VGH_HSYNC_RST 0x10
55 #define HX8394_POWER_CLK_OPT_VGL_HSYNC_RST 0x20
56 #define HX8394_POWER_FS2_DIV_192 0x4
57 #define HX8394_POWER_FS1_DIV_224 0x50
58 #define HX8394_POWER_BTP_5_55V 0x11
59 #define HX8394_POWER_VGH_RATIO_2VSPVSN 0x60
60 #define HX8394_POWER_BTN_5_55V 0x11
61 #define HX8394_POWER_VGL_RATIO_2VSPVSN 0x60
62 #define HX8394_POWER_VGHS_16V 0x57
63 #define HX8394_POWER_VGLS_12_4V 0x47
64
65 #define HX8394_SETDISP 0xB2
66 #define HX8394_DISP_COL_INV 0x0
67 #define HX8394_DISP_MESSI_ENB 0x80
68 #define HX8394_DISP_NL_1280 0x64
69 #define HX8394_DISP_BP_14 0xC
70 #define HX8394_DISP_FP_15 0xD
71 #define HX8394_DISP_RTN_144 0x2F
72
73 #define HX8394_SETCYC 0xB4
74
75 #define HX8394_SETGIP0 0xD3
76 #define HX8394_GIP0_EQ_OPT_BOTH 0x0
77 #define HX8394_GIP0_EQ_HSYNC_NORMAL 0x0
78 #define HX8394_GIP0_EQ_VSEL_VSSA 0x0
79 #define HX8394_SHP_START_4 0x40
80 #define HX8394_SCP_WIDTH_7X_HSYNC 0x7
81 #define HX8394_CHR0_12X_HSYNC 0xA
82 #define HX8394_CHR1_18X_HSYNC 0x10
83
84 #define HX8394_SETGIP1 0xD5
85
86 #define HX8394_SETGIP2 0xD6
87
88 #define HX8394_SETVCOM 0xB6
89 #define HX8394_VCMC_F_1_76V 0x92
90 #define HX8394_VCMC_B_1_76V 0x92
91
92 #define HX8394_SETGAMMA 0xE0
93
94 #define HX8394_SETPANEL 0xCC
95 #define HX8394_COLOR_BGR BIT(0)
96 #define HX8394_REV_PANEL BIT(1)
97
98 #define HX8394_SETBANK 0xBD
99
100 #define HX8394_SET_TEAR 0x35
101 #define HX8394_TEAR_VBLANK 0x0
102
103 #define HX8394_SETEXTC 0xB9
104 #define HX8394_EXTC1_MAGIC 0xFF
105 #define HX8394_EXTC2_MAGIC 0x83
106 #define HX8394_EXTC3_MAGIC 0x94
107
108
109 const uint8_t enable_extension[] = {
110 HX8394_SETEXTC,
111 HX8394_EXTC1_MAGIC,
112 HX8394_EXTC2_MAGIC,
113 HX8394_EXTC3_MAGIC,
114 };
115
116 const uint8_t address_config[] = {
117 HX8394_SET_ADDRESS,
118 HX8394_FLIP_HORIZONTAL
119 };
120
121 const uint8_t power_config[] = {
122 HX8394_SETPOWER,
123 (HX8394_POWER_HX5186 | HX8394_POWER_AP_1_0UA),
124 HX8394_POWER_VRHP_4_8V,
125 (HX8394_POWER_VPPS_8_25V | HX8394_POWER_VRHN_4_8V),
126 (HX8394_POWER_VSP_FBOFF | HX8394_POWER_XDK_X2),
127 (HX8394_POWER_CLK_OPT_VGL_HSYNC_RST |
128 HX8394_POWER_CLK_OPT_VGH_HSYNC_RST |
129 HX8394_POWER_FS0_DIV_8),
130 (HX8394_POWER_FS1_DIV_224 | HX8394_POWER_FS2_DIV_192),
131 (HX8394_POWER_VGH_RATIO_2VSPVSN | HX8394_POWER_BTP_5_55V),
132 (HX8394_POWER_VGL_RATIO_2VSPVSN | HX8394_POWER_BTN_5_55V),
133 HX8394_POWER_VGHS_16V,
134 HX8394_POWER_VGLS_12_4V
135 };
136
137 const uint8_t line_config[] = {
138 HX8394_SETDISP,
139 HX8394_DISP_COL_INV,
140 HX8394_DISP_MESSI_ENB,
141 HX8394_DISP_NL_1280,
142 HX8394_DISP_BP_14,
143 HX8394_DISP_FP_15,
144 HX8394_DISP_RTN_144
145 };
146
147 const uint8_t cycle_config[] = {
148 HX8394_SETCYC,
149 0x73, /* SPON delay */
150 0x74, /* SPOFF delay */
151 0x73, /* CON delay */
152 0x74, /* COFF delay */
153 0x73, /* CON1 delay */
154 0x74, /* COFF1 delay */
155 0x1, /* EQON time */
156 0xC, /* SON time */
157 0x86, /* SOFF time */
158 0x75, /* SAP1_P, SAP2 (1st and second stage op amp bias) */
159 0x00, /* DX2 off, EQ off, EQ_MI off */
160 0x3F, /* DX2 off period setting */
161 0x73, /* SPON_MPU delay */
162 0x74, /* SPOFF_MPU delay */
163 0x73, /* CON_MPU delay */
164 0x74, /* COFF_MPU delay */
165 0x73, /* CON1_MPU delay */
166 0x74, /* COFF1_MPU delay */
167 0x1, /* EQON_MPU time */
168 0xC, /* SON_MPU time */
169 0x86 /* SOFF_MPU time */
170 };
171
172 const uint8_t gip0_config[] = {
173 HX8394_SETGIP0,
174 (HX8394_GIP0_EQ_OPT_BOTH | HX8394_GIP0_EQ_HSYNC_NORMAL),
175 HX8394_GIP0_EQ_VSEL_VSSA,
176 0x7, /* EQ_DELAY_ON1 (in cycles of TCON CLK */
177 0x7, /* EQ_DELAY_OFF1 (in cycles of TCON CLK */
178 0x40, /* GPWR signal frequency (64x per frame) */
179 0x7, /* GPWR signal non overlap timing (in cycles of TCON */
180 0xC, /* GIP dummy clock for first CKV */
181 0x00, /* GIP dummy clock for second CKV */
182 /* Group delays. Sets start/end signal delay from VYSNC
183 * falling edge in multiples of HSYNC
184 */
185 0x8, /* SHR0_2 = 8, SHR0_3 = 0 */
186 0x10, /* SHR0_1 = 1, SHR0[11:8] = 0x0 */
187 0x8, /* SHR0 = 0x8 */
188 0x0, /* SHR0_GS[11:8]. Unset. */
189 0x8, /* SHR0_GS = 0x8 */
190 0x54, /* SHR1_3 = 0x5, SHR1_2 = 0x4 */
191 0x15, /* SHR1_1 = 0x1, SHR1[11:8] = 0x5 */
192 0xA, /* SHR1[7:0] = 0xA (SHR1 = 0x50A) */
193 0x5, /* SHR1_GS[11:8] = 0x5 */
194 0xA, /* SHR1_GS[7:0] = 0xA (SHR1_GS = 0x50A) */
195 0x2, /* SHR2_3 = 0x0, SHR2_2 = 0x2 */
196 0x15, /* SHR2_1 = 0x1, SHR2[11:8] = 0x5 */
197 0x6, /* SHR2[7:0] = 0x6 (SHR2 = 0x506) */
198 0x5, /* SHR2_GS[11:8] = 0x5 */
199 0x6, /* SHR2_GS[7:0 = 0x6 (SHR2_GS = 0x506) */
200 (HX8394_SHP_START_4 | HX8394_SCP_WIDTH_7X_HSYNC),
201 0x44, /* SHP2 = 0x4, SHP1 = 0x4 */
202 HX8394_CHR0_12X_HSYNC,
203 HX8394_CHR0_12X_HSYNC,
204 0x4B, /* CHP0 = 4x hsync, CCP0 = 0xB */
205 HX8394_CHR1_18X_HSYNC,
206 0x7, /* CHR1_GS = 9x hsync */
207 0x7, /* CHP1 = 1x hsync, CCP1 = 0x7 */
208 /* These parameters are not documented in datasheet */
209 0xC,
210 0x40
211 };
212
213 const uint8_t gip1_config[] = {
214 HX8394_SETGIP1,
215 /* Select output clock sources
216 * See COSn_L/COSn_R values in datasheet
217 */
218 0x1C, /* COS1_L */
219 0x1C, /* COS1_R */
220 0x1D, /* COS2_L */
221 0x1D, /* COS2_R */
222 0x00, /* COS3_L */
223 0x01, /* COS3_R */
224 0x02, /* COS4_L */
225 0x03, /* COS4_R */
226 0x04, /* COS5_L */
227 0x05, /* COS5_R */
228 0x06, /* COS6_L */
229 0x07, /* COS6_R */
230 0x08, /* COS7_L */
231 0x09, /* COS7_R */
232 0x0A, /* COS8_L */
233 0x0B, /* COS8_R */
234 0x24, /* COS9_L */
235 0x25, /* COS9_R */
236 0x18, /* COS10_L */
237 0x18, /* COS10_R */
238 0x26, /* COS11_L */
239 0x27, /* COS11_R */
240 0x18, /* COS12_L */
241 0x18, /* COS12_R */
242 0x18, /* COS13_L */
243 0x18, /* COS13_R */
244 0x18, /* COS14_L */
245 0x18, /* COS14_R */
246 0x18, /* COS15_L */
247 0x18, /* COS15_R */
248 0x18, /* COS16_L */
249 0x18, /* COS16_R */
250 0x18, /* COS17_L */
251 0x18, /* COS17_R */
252 0x18, /* COS18_L */
253 0x18, /* COS18_R */
254 0x18, /* COS19_L */
255 0x18, /* COS19_R */
256 0x20, /* COS20_L */
257 0x21, /* COS20_R */
258 0x18, /* COS21_L */
259 0x18, /* COS21_R */
260 0x18, /* COS22_L */
261 0x18 /* COS22_R */
262 };
263
264 const uint8_t gip2_config[] = {
265 HX8394_SETGIP2,
266 /* Select output clock sources for GS mode.
267 * See COSn_L_GS/COSn_R_GS values in datasheet
268 */
269 0x1C, /* COS1_L_GS */
270 0x1C, /* COS1_R_GS */
271 0x1D, /* COS2_L_GS */
272 0x1D, /* COS2_R_GS */
273 0x07, /* COS3_L_GS */
274 0x06, /* COS3_R_GS */
275 0x05, /* COS4_L_GS */
276 0x04, /* COS4_R_GS */
277 0x03, /* COS5_L_GS */
278 0x02, /* COS5_R_GS */
279 0x01, /* COS6_L_GS */
280 0x00, /* COS6_R_GS */
281 0x0B, /* COS7_L_GS */
282 0x0A, /* COS7_R_GS */
283 0x09, /* COS8_L_GS */
284 0x08, /* COS8_R_GS */
285 0x21, /* COS9_L_GS */
286 0x20, /* COS9_R_GS */
287 0x18, /* COS10_L_GS */
288 0x18, /* COS10_R_GS */
289 0x27, /* COS11_L_GS */
290 0x26, /* COS11_R_GS */
291 0x18, /* COS12_L_GS */
292 0x18, /* COS12_R_GS */
293 0x18, /* COS13_L_GS */
294 0x18, /* COS13_R_GS */
295 0x18, /* COS14_L_GS */
296 0x18, /* COS14_R_GS */
297 0x18, /* COS15_L_GS */
298 0x18, /* COS15_R_GS */
299 0x18, /* COS16_L_GS */
300 0x18, /* COS16_R_GS */
301 0x18, /* COS17_L_GS */
302 0x18, /* COS17_R_GS */
303 0x18, /* COS18_L_GS */
304 0x18, /* COS18_R_GS */
305 0x18, /* COS19_L_GS */
306 0x18, /* COS19_R_GS */
307 0x25, /* COS20_L_GS */
308 0x24, /* COS20_R_GS */
309 0x18, /* COS21_L_GS */
310 0x18, /* COS21_R_GS */
311 0x18, /* COS22_L_GS */
312 0x18 /* COS22_R_GS */
313 };
314
315 const uint8_t vcom_config[] = {
316 HX8394_SETVCOM,
317 HX8394_VCMC_F_1_76V,
318 HX8394_VCMC_B_1_76V
319 };
320
321 const uint8_t gamma_config[] = {
322 HX8394_SETGAMMA,
323 0x00, /* VHP0 */
324 0x0A, /* VHP1 */
325 0x15, /* VHP2 */
326 0x1B, /* VHP3 */
327 0x1E, /* VHP4 */
328 0x21, /* VHP5 */
329 0x24, /* VHP6 */
330 0x22, /* VHP7 */
331 0x47, /* VMP0 */
332 0x56, /* VMP1 */
333 0x65, /* VMP2 */
334 0x66, /* VMP3 */
335 0x6E, /* VMP4 */
336 0x82, /* VMP5 */
337 0x88, /* VMP6 */
338 0x8B, /* VMP7 */
339 0x9A, /* VMP8 */
340 0x9D, /* VMP9 */
341 0x98, /* VMP10 */
342 0xA8, /* VMP11 */
343 0xB9, /* VMP12 */
344 0x5D, /* VLP0 */
345 0x5C, /* VLP1 */
346 0x61, /* VLP2 */
347 0x66, /* VLP3 */
348 0x6A, /* VLP4 */
349 0x6F, /* VLP5 */
350 0x7F, /* VLP6 */
351 0x7F, /* VLP7 */
352 0x00, /* VHN0 */
353 0x0A, /* VHN1 */
354 0x15, /* VHN2 */
355 0x1B, /* VHN3 */
356 0x1E, /* VHN4 */
357 0x21, /* VHN5 */
358 0x24, /* VHN6 */
359 0x22, /* VHN7 */
360 0x47, /* VMN0 */
361 0x56, /* VMN1 */
362 0x65, /* VMN2 */
363 0x65, /* VMN3 */
364 0x6E, /* VMN4 */
365 0x81, /* VMN5 */
366 0x87, /* VMN6 */
367 0x8B, /* VMN7 */
368 0x98, /* VMN8 */
369 0x9D, /* VMN9 */
370 0x99, /* VMN10 */
371 0xA8, /* VMN11 */
372 0xBA, /* VMN12 */
373 0x5D, /* VLN0 */
374 0x5D, /* VLN1 */
375 0x62, /* VLN2 */
376 0x67, /* VLN3 */
377 0x6B, /* VLN4 */
378 0x72, /* VLN5 */
379 0x7F, /* VLN6 */
380 0x7F /* VLN7 */
381 };
382
383 const uint8_t hx8394_cmd1[] = {0xC0U, 0x1FU, 0x31U};
384
385 const uint8_t panel_config[] = {
386 HX8394_SETPANEL,
387 (HX8394_COLOR_BGR | HX8394_REV_PANEL)
388 };
389
390 const uint8_t hx8394_cmd2[] = {0xD4, 0x2};
391
392 const uint8_t hx8394_bank2[] = {
393 0xD8U, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU,
394 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU,
395 0xFFU
396 };
397
398 const uint8_t hx8394_bank1[] = {0xB1U, 0x00U};
399
400 const uint8_t hx8394_bank0[] = {
401 0xBFU, 0x40U, 0x81U, 0x50U,
402 0x00U, 0x1AU, 0xFCU, 0x01
403 };
404
405 const uint8_t hx8394_cmd3[] = {0xC6U, 0xEDU};
406
407 const uint8_t tear_config[] = {HX8394_SET_TEAR, HX8394_TEAR_VBLANK};
408
hx8394_mipi_tx(const struct device * mipi_dev,uint8_t channel,const void * buf,size_t len)409 static ssize_t hx8394_mipi_tx(const struct device *mipi_dev, uint8_t channel,
410 const void *buf, size_t len)
411 {
412 /* Send MIPI transfers using low power mode */
413 struct mipi_dsi_msg msg = {
414 .tx_buf = buf,
415 .tx_len = len,
416 .flags = MIPI_DSI_MSG_USE_LPM,
417 };
418
419 switch (len) {
420 case 0U:
421 msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM;
422 break;
423
424 case 1U:
425 msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM;
426 break;
427
428 case 2U:
429 msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM;
430 break;
431
432 default:
433 msg.type = MIPI_DSI_GENERIC_LONG_WRITE;
434 break;
435 }
436
437 return mipi_dsi_transfer(mipi_dev, channel, &msg);
438 }
439
hx8394_write(const struct device * dev,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc,const void * buf)440 static int hx8394_write(const struct device *dev, const uint16_t x,
441 const uint16_t y,
442 const struct display_buffer_descriptor *desc,
443 const void *buf)
444 {
445 LOG_WRN("Write not supported, use LCD controller display driver");
446 return 0;
447 }
448
hx8394_blanking_off(const struct device * dev)449 static int hx8394_blanking_off(const struct device *dev)
450 {
451 const struct hx8394_config *config = dev->config;
452
453 if (config->bl_gpio.port != NULL) {
454 return gpio_pin_set_dt(&config->bl_gpio, 1);
455 } else {
456 return -ENOTSUP;
457 }
458 }
459
hx8394_blanking_on(const struct device * dev)460 static int hx8394_blanking_on(const struct device *dev)
461 {
462 const struct hx8394_config *config = dev->config;
463
464 if (config->bl_gpio.port != NULL) {
465 return gpio_pin_set_dt(&config->bl_gpio, 0);
466 } else {
467 return -ENOTSUP;
468 }
469 }
470
hx8394_set_pixel_format(const struct device * dev,const enum display_pixel_format pixel_format)471 static int hx8394_set_pixel_format(const struct device *dev,
472 const enum display_pixel_format pixel_format)
473 {
474 const struct hx8394_config *config = dev->config;
475
476 if (pixel_format == config->pixel_format) {
477 return 0;
478 }
479 LOG_WRN("Pixel format change not implemented");
480 return -ENOTSUP;
481 }
482
hx8394_set_orientation(const struct device * dev,const enum display_orientation orientation)483 static int hx8394_set_orientation(const struct device *dev,
484 const enum display_orientation orientation)
485 {
486 const struct hx8394_config *config = dev->config;
487 uint8_t param[2] = {0};
488
489 /* Note- this simply flips the scan direction of the display
490 * driver. Can be useful if your application needs the display
491 * flipped on the X or Y axis
492 */
493 param[0] = HX8394_SET_ADDRESS;
494 switch (orientation) {
495 case DISPLAY_ORIENTATION_NORMAL:
496 /* Default orientation for this display flips image on x axis */
497 param[1] = HX8394_FLIP_HORIZONTAL;
498 break;
499 case DISPLAY_ORIENTATION_ROTATED_90:
500 param[1] = HX8394_FLIP_VERTICAL;
501 break;
502 case DISPLAY_ORIENTATION_ROTATED_180:
503 param[1] = 0;
504 break;
505 case DISPLAY_ORIENTATION_ROTATED_270:
506 param[1] = HX8394_FLIP_HORIZONTAL | HX8394_FLIP_VERTICAL;
507 break;
508 default:
509 return -ENOTSUP;
510 }
511 return hx8394_mipi_tx(config->mipi_dsi, config->channel, param, 2);
512 }
513
hx8394_get_capabilities(const struct device * dev,struct display_capabilities * capabilities)514 static void hx8394_get_capabilities(const struct device *dev,
515 struct display_capabilities *capabilities)
516 {
517 const struct hx8394_config *config = dev->config;
518
519 memset(capabilities, 0, sizeof(struct display_capabilities));
520 capabilities->x_resolution = config->panel_width;
521 capabilities->y_resolution = config->panel_height;
522 capabilities->supported_pixel_formats = config->pixel_format;
523 capabilities->current_pixel_format = config->pixel_format;
524 capabilities->current_orientation = DISPLAY_ORIENTATION_NORMAL;
525 }
526
527 static const struct display_driver_api hx8394_api = {
528 .blanking_on = hx8394_blanking_on,
529 .blanking_off = hx8394_blanking_off,
530 .write = hx8394_write,
531 .get_capabilities = hx8394_get_capabilities,
532 .set_pixel_format = hx8394_set_pixel_format,
533 .set_orientation = hx8394_set_orientation,
534 };
535
hx8394_init(const struct device * dev)536 static int hx8394_init(const struct device *dev)
537 {
538 const struct hx8394_config *config = dev->config;
539 int ret;
540 struct mipi_dsi_device mdev;
541 uint8_t param[2];
542 uint8_t setmipi[7] = {
543 HX8394_SETMIPI,
544 (HX8394_MIPI_LPTX_BTA_READ | HX8394_MIPI_LP_CD_DIS),
545 HX8394_MIPI_TA_6TL,
546 (HX8394_MIPI_DPHYCMD_LPRX_8NS |
547 HX8394_MIPI_DPHYCMD_LPRX_66mV |
548 HX8394_MIPI_DPHYCMD_LPTX_SRLIM),
549 (HX8394_MIPI_DPHYCMD_LDO_1_55V |
550 HX8394_MIPI_DPHYCMD_HSRX_7X |
551 HX8394_MIPI_DPHYCMD_HSRX_100OHM |
552 HX8394_MIPI_DPHYCMD_LPCD_1X),
553 /* The remaining parameters here are not documented */
554 0xB2U, 0xC0U};
555
556 mdev.data_lanes = config->num_of_lanes;
557 mdev.pixfmt = config->pixel_format;
558 /* HX8394 runs in video mode */
559 mdev.mode_flags = MIPI_DSI_MODE_VIDEO;
560
561 ret = mipi_dsi_attach(config->mipi_dsi, config->channel, &mdev);
562 if (ret < 0) {
563 LOG_ERR("Could not attach to MIPI-DSI host");
564 return ret;
565 }
566
567 if (gpio_is_ready_dt(&config->reset_gpio)) {
568 /* Regulator API will have supplied power to the display
569 * driver. Per datasheet, we must wait 1ms for the RESX
570 * pin to be valid.
571 */
572 k_sleep(K_MSEC(1));
573 /* Initialize reset GPIO */
574 ret = gpio_pin_configure_dt(&config->reset_gpio, GPIO_OUTPUT_INACTIVE);
575 if (ret < 0) {
576 return ret;
577 }
578 /* Pull reset GPIO low */
579 gpio_pin_set_dt(&config->reset_gpio, 0);
580 /* Datasheet says we must keep reset pin low at least 10us.
581 * hold it low for 1ms to be safe.
582 */
583 k_sleep(K_MSEC(1));
584 gpio_pin_set_dt(&config->reset_gpio, 1);
585 /* Per datasheet, we must delay at least 50ms before first
586 * host command
587 */
588 k_sleep(K_MSEC(50));
589 }
590 /* Enable extended commands */
591 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
592 enable_extension, sizeof(enable_extension));
593 if (ret < 0) {
594 return ret;
595 }
596
597 /* Set the number of lanes to DSISETUP0 parameter */
598 setmipi[1] |= (config->num_of_lanes - 1);
599 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
600 setmipi, sizeof(setmipi));
601 if (ret < 0) {
602 return ret;
603 }
604
605 /* Set scan direction */
606 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
607 address_config, sizeof(address_config));
608 if (ret < 0) {
609 return ret;
610 }
611
612 /* Set voltage and current targets */
613 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
614 power_config, sizeof(power_config));
615 if (ret < 0) {
616 return ret;
617 }
618
619 /* Setup display line count and front/back porch size */
620 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
621 line_config, sizeof(line_config));
622 if (ret < 0) {
623 return ret;
624 }
625
626 /* Setup display cycle counts (in counts of TCON CLK) */
627 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
628 cycle_config, sizeof(cycle_config));
629 if (ret < 0) {
630 return ret;
631 }
632
633 /* Set group delay values */
634 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
635 gip0_config, sizeof(gip0_config));
636 if (ret < 0) {
637 return ret;
638 }
639
640
641 /* Set group clock selections */
642 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
643 gip1_config, sizeof(gip1_config));
644 if (ret < 0) {
645 return ret;
646 }
647
648 /* Set group clock selections for GS mode */
649 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
650 gip2_config, sizeof(gip2_config));
651 if (ret < 0) {
652 return ret;
653 }
654
655 /* Delay for a moment before setting VCOM. It is not clear
656 * from the datasheet why this is required, but without this
657 * delay the panel stops responding to additional commands
658 */
659 k_msleep(1);
660 /* Set VCOM voltage config */
661 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
662 vcom_config, sizeof(vcom_config));
663 if (ret < 0) {
664 return ret;
665 }
666
667 /* Set manufacturer supplied gamma values */
668 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
669 gamma_config, sizeof(gamma_config));
670 if (ret < 0) {
671 return ret;
672 }
673
674 /* This command is not documented in datasheet, but is included
675 * in the display initialization done by MCUXpresso SDK
676 */
677 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
678 hx8394_cmd1, sizeof(hx8394_cmd1));
679 if (ret < 0) {
680 return ret;
681 }
682
683 /* Set panel to BGR mode, and reverse colors */
684 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
685 panel_config, sizeof(panel_config));
686 if (ret < 0) {
687 return ret;
688 }
689
690 /* This command is not documented in datasheet, but is included
691 * in the display initialization done by MCUXpresso SDK
692 */
693 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
694 hx8394_cmd2, sizeof(hx8394_cmd2));
695 if (ret < 0) {
696 return ret;
697 }
698
699 /* Write values to manufacturer register banks */
700 param[0] = HX8394_SETBANK;
701 param[1] = 0x2;
702 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
703 param, 2);
704 if (ret < 0) {
705 return ret;
706 }
707 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
708 hx8394_bank2, sizeof(hx8394_bank2));
709 if (ret < 0) {
710 return ret;
711 }
712 param[1] = 0x0;
713 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
714 param, 2);
715 if (ret < 0) {
716 return ret;
717 }
718 /* Select bank 1 */
719 param[1] = 0x1;
720 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
721 param, 2);
722 if (ret < 0) {
723 return ret;
724 }
725 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
726 hx8394_bank1, sizeof(hx8394_bank1));
727 if (ret < 0) {
728 return ret;
729 }
730 /* Select bank 0 */
731 param[1] = 0x0;
732 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
733 param, 2);
734 if (ret < 0) {
735 return ret;
736 }
737 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
738 hx8394_bank0, sizeof(hx8394_bank0));
739 if (ret < 0) {
740 return ret;
741 }
742
743 /* This command is not documented in datasheet, but is included
744 * in the display initialization done by MCUXpresso SDK
745 */
746 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
747 hx8394_cmd3, sizeof(hx8394_cmd3));
748 if (ret < 0) {
749 return ret;
750 }
751
752 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
753 tear_config, sizeof(tear_config));
754 if (ret < 0) {
755 return ret;
756 }
757
758 param[0] = MIPI_DCS_EXIT_SLEEP_MODE;
759
760 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
761 param, 1);
762 if (ret < 0) {
763 return ret;
764 }
765 /* We must delay 120ms after exiting sleep mode per datasheet */
766 k_sleep(K_MSEC(120));
767
768 param[0] = MIPI_DCS_SET_DISPLAY_ON;
769 ret = hx8394_mipi_tx(config->mipi_dsi, config->channel,
770 param, 1);
771
772 if (config->bl_gpio.port != NULL) {
773 ret = gpio_pin_configure_dt(&config->bl_gpio, GPIO_OUTPUT_ACTIVE);
774 if (ret < 0) {
775 LOG_ERR("Could not configure bl GPIO (%d)", ret);
776 return ret;
777 }
778 }
779
780 return ret;
781 }
782
783 #define HX8394_PANEL(id) \
784 static const struct hx8394_config hx8394_config_##id = { \
785 .mipi_dsi = DEVICE_DT_GET(DT_INST_BUS(id)), \
786 .reset_gpio = GPIO_DT_SPEC_INST_GET_OR(id, reset_gpios, {0}), \
787 .bl_gpio = GPIO_DT_SPEC_INST_GET_OR(id, bl_gpios, {0}), \
788 .num_of_lanes = DT_INST_PROP_BY_IDX(id, data_lanes, 0), \
789 .pixel_format = DT_INST_PROP(id, pixel_format), \
790 .panel_width = DT_INST_PROP(id, width), \
791 .panel_height = DT_INST_PROP(id, height), \
792 .channel = DT_INST_REG_ADDR(id), \
793 }; \
794 DEVICE_DT_INST_DEFINE(id, \
795 &hx8394_init, \
796 NULL, \
797 NULL, \
798 &hx8394_config_##id, \
799 POST_KERNEL, \
800 CONFIG_APPLICATION_INIT_PRIORITY, \
801 &hx8394_api);
802
803 DT_INST_FOREACH_STATUS_OKAY(HX8394_PANEL)
804