1 /*
2 * Copyright 2023, NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT raydium_rm67162
8
9 #include <zephyr/drivers/display.h>
10 #include <zephyr/drivers/mipi_dsi.h>
11 #include <zephyr/drivers/mipi_dsi/mipi_dsi_mcux_2l.h>
12 #include <zephyr/drivers/gpio.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/logging/log.h>
15 #include <zephyr/pm/policy.h>
16 #include <zephyr/pm/device.h>
17 #include <zephyr/sys/byteorder.h>
18
19 LOG_MODULE_REGISTER(rm67162, CONFIG_DISPLAY_LOG_LEVEL);
20
21 /*
22 * These commands are taken from NXP's MCUXpresso SDK.
23 * Additional documentation is added where possible, but the
24 * Manufacture command set pages are not described in the datasheet
25 */
26 static const struct {
27 uint8_t cmd;
28 uint8_t param;
29 } rm67162_init_400x392[] = {
30 /* CMD Mode switch, select manufacture command set page 0 */
31 {.cmd = 0xFE, .param = 0x01},
32 {.cmd = 0x06, .param = 0x62},
33 {.cmd = 0x0E, .param = 0x80},
34 {.cmd = 0x0F, .param = 0x80},
35 {.cmd = 0x10, .param = 0x71},
36 {.cmd = 0x13, .param = 0x81},
37 {.cmd = 0x14, .param = 0x81},
38 {.cmd = 0x15, .param = 0x82},
39 {.cmd = 0x16, .param = 0x82},
40 {.cmd = 0x18, .param = 0x88},
41 {.cmd = 0x19, .param = 0x55},
42 {.cmd = 0x1A, .param = 0x10},
43 {.cmd = 0x1C, .param = 0x99},
44 {.cmd = 0x1D, .param = 0x03},
45 {.cmd = 0x1E, .param = 0x03},
46 {.cmd = 0x1F, .param = 0x03},
47 {.cmd = 0x20, .param = 0x03},
48 {.cmd = 0x25, .param = 0x03},
49 {.cmd = 0x26, .param = 0x8D},
50 {.cmd = 0x2A, .param = 0x03},
51 {.cmd = 0x2B, .param = 0x8D},
52 {.cmd = 0x36, .param = 0x00},
53 {.cmd = 0x37, .param = 0x10},
54 {.cmd = 0x3A, .param = 0x00},
55 {.cmd = 0x3B, .param = 0x00},
56 {.cmd = 0x3D, .param = 0x20},
57 {.cmd = 0x3F, .param = 0x3A},
58 {.cmd = 0x40, .param = 0x30},
59 {.cmd = 0x41, .param = 0x30},
60 {.cmd = 0x42, .param = 0x33},
61 {.cmd = 0x43, .param = 0x22},
62 {.cmd = 0x44, .param = 0x11},
63 {.cmd = 0x45, .param = 0x66},
64 {.cmd = 0x46, .param = 0x55},
65 {.cmd = 0x47, .param = 0x44},
66 {.cmd = 0x4C, .param = 0x33},
67 {.cmd = 0x4D, .param = 0x22},
68 {.cmd = 0x4E, .param = 0x11},
69 {.cmd = 0x4F, .param = 0x66},
70 {.cmd = 0x50, .param = 0x55},
71 {.cmd = 0x51, .param = 0x44},
72 {.cmd = 0x57, .param = 0xB3},
73 {.cmd = 0x6B, .param = 0x19},
74 {.cmd = 0x70, .param = 0x55},
75 {.cmd = 0x74, .param = 0x0C},
76
77 /* VGMP/VGSP Voltage Control (select manufacture command set page 1 ) */
78 {.cmd = 0xFE, .param = 0x02},
79 {.cmd = 0x9B, .param = 0x40},
80 {.cmd = 0x9C, .param = 0x67},
81 {.cmd = 0x9D, .param = 0x20},
82
83 /* VGMP/VGSP Voltage Control (select manufacture command set page 2 ) */
84 {.cmd = 0xFE, .param = 0x03},
85 {.cmd = 0x9B, .param = 0x40},
86 {.cmd = 0x9C, .param = 0x67},
87 {.cmd = 0x9D, .param = 0x20},
88
89 /* VSR Command (select manufacture command set page 3 ) */
90 {.cmd = 0xFE, .param = 0x04},
91 {.cmd = 0x5D, .param = 0x10},
92
93 /* VSR1 Timing Set (select manufacture command set page 3 ) */
94 {.cmd = 0xFE, .param = 0x04},
95 {.cmd = 0x00, .param = 0x8D},
96 {.cmd = 0x01, .param = 0x00},
97 {.cmd = 0x02, .param = 0x01},
98 {.cmd = 0x03, .param = 0x01},
99 {.cmd = 0x04, .param = 0x10},
100 {.cmd = 0x05, .param = 0x01},
101 {.cmd = 0x06, .param = 0xA7},
102 {.cmd = 0x07, .param = 0x20},
103 {.cmd = 0x08, .param = 0x00},
104
105 /* VSR2 Timing Set (select manufacture command set page 3 ) */
106 {.cmd = 0xFE, .param = 0x04},
107 {.cmd = 0x09, .param = 0xC2},
108 {.cmd = 0x0A, .param = 0x00},
109 {.cmd = 0x0B, .param = 0x02},
110 {.cmd = 0x0C, .param = 0x01},
111 {.cmd = 0x0D, .param = 0x40},
112 {.cmd = 0x0E, .param = 0x06},
113 {.cmd = 0x0F, .param = 0x01},
114 {.cmd = 0x10, .param = 0xA7},
115 {.cmd = 0x11, .param = 0x00},
116
117 /* VSR3 Timing Set (select manufacture command set page 3 ) */
118 {.cmd = 0xFE, .param = 0x04},
119 {.cmd = 0x12, .param = 0xC2},
120 {.cmd = 0x13, .param = 0x00},
121 {.cmd = 0x14, .param = 0x02},
122 {.cmd = 0x15, .param = 0x01},
123 {.cmd = 0x16, .param = 0x40},
124 {.cmd = 0x17, .param = 0x07},
125 {.cmd = 0x18, .param = 0x01},
126 {.cmd = 0x19, .param = 0xA7},
127 {.cmd = 0x1A, .param = 0x00},
128
129 /* VSR4 Timing Set (select manufacture command set page 3 ) */
130 {.cmd = 0xFE, .param = 0x04},
131 {.cmd = 0x1B, .param = 0x82},
132 {.cmd = 0x1C, .param = 0x00},
133 {.cmd = 0x1D, .param = 0xFF},
134 {.cmd = 0x1E, .param = 0x05},
135 {.cmd = 0x1F, .param = 0x60},
136 {.cmd = 0x20, .param = 0x02},
137 {.cmd = 0x21, .param = 0x01},
138 {.cmd = 0x22, .param = 0x7C},
139 {.cmd = 0x23, .param = 0x00},
140
141 /* VSR5 Timing Set (select manufacture command set page 3 ) */
142 {.cmd = 0xFE, .param = 0x04},
143 {.cmd = 0x24, .param = 0xC2},
144 {.cmd = 0x25, .param = 0x00},
145 {.cmd = 0x26, .param = 0x04},
146 {.cmd = 0x27, .param = 0x02},
147 {.cmd = 0x28, .param = 0x70},
148 {.cmd = 0x29, .param = 0x05},
149 {.cmd = 0x2A, .param = 0x74},
150 {.cmd = 0x2B, .param = 0x8D},
151 {.cmd = 0x2D, .param = 0x00},
152
153 /* VSR6 Timing Set (select manufacture command set page 3 ) */
154 {.cmd = 0xFE, .param = 0x04},
155 {.cmd = 0x2F, .param = 0xC2},
156 {.cmd = 0x30, .param = 0x00},
157 {.cmd = 0x31, .param = 0x04},
158 {.cmd = 0x32, .param = 0x02},
159 {.cmd = 0x33, .param = 0x70},
160 {.cmd = 0x34, .param = 0x07},
161 {.cmd = 0x35, .param = 0x74},
162 {.cmd = 0x36, .param = 0x8D},
163 {.cmd = 0x37, .param = 0x00},
164
165 /* VSR Marping command (select manufacture command set page 3 ) */
166 {.cmd = 0xFE, .param = 0x04},
167 {.cmd = 0x5E, .param = 0x20},
168 {.cmd = 0x5F, .param = 0x31},
169 {.cmd = 0x60, .param = 0x54},
170 {.cmd = 0x61, .param = 0x76},
171 {.cmd = 0x62, .param = 0x98},
172
173 /* Select manufacture command set page 4 */
174 /* ELVSS -2.4V(RT4723). 0x15: RT4723. 0x01: RT4723B. 0x17: STAM1332. */
175 {.cmd = 0xFE, .param = 0x05},
176 {.cmd = 0x05, .param = 0x15},
177 {.cmd = 0x2A, .param = 0x04},
178 {.cmd = 0x91, .param = 0x00},
179
180 /* Select user command set */
181 {.cmd = 0xFE, .param = 0x00},
182 /* Set tearing effect signal to only output at V-blank*/
183 {.cmd = 0x35, .param = 0x00},
184 };
185
186 struct rm67162_config {
187 const struct device *mipi_dsi;
188 uint8_t channel;
189 uint8_t num_of_lanes;
190 const struct gpio_dt_spec reset_gpio;
191 const struct gpio_dt_spec bl_gpio;
192 const struct gpio_dt_spec te_gpio;
193 uint16_t panel_width;
194 uint16_t panel_height;
195 };
196
197
198 struct rm67162_data {
199 uint8_t pixel_format;
200 uint8_t bytes_per_pixel;
201 struct gpio_callback te_gpio_cb;
202 struct k_sem te_sem;
203 };
204
rm67162_te_isr_handler(const struct device * gpio_dev,struct gpio_callback * cb,uint32_t pins)205 static void rm67162_te_isr_handler(const struct device *gpio_dev,
206 struct gpio_callback *cb, uint32_t pins)
207 {
208 struct rm67162_data *data = CONTAINER_OF(cb, struct rm67162_data, te_gpio_cb);
209
210 k_sem_give(&data->te_sem);
211 }
212
rm67162_init(const struct device * dev)213 static int rm67162_init(const struct device *dev)
214 {
215 const struct rm67162_config *config = dev->config;
216 struct rm67162_data *data = dev->data;
217 struct mipi_dsi_device mdev = {0};
218 int ret;
219 uint32_t i;
220 uint8_t cmd, param;
221
222 /* Attach to MIPI DSI host */
223 mdev.data_lanes = config->num_of_lanes;
224 mdev.pixfmt = data->pixel_format;
225
226 ret = mipi_dsi_attach(config->mipi_dsi, config->channel, &mdev);
227 if (ret < 0) {
228 LOG_ERR("Could not attach to MIPI-DSI host");
229 return ret;
230 }
231
232 if (config->reset_gpio.port != NULL) {
233 ret = gpio_pin_configure_dt(&config->reset_gpio, GPIO_OUTPUT_INACTIVE);
234 if (ret < 0) {
235 LOG_ERR("Could not configure reset GPIO (%d)", ret);
236 return ret;
237 }
238
239 /*
240 * Power to the display has been enabled via the regulator fixed api during
241 * regulator init. Per datasheet, we must wait at least 10ms before
242 * starting reset sequence after power on.
243 */
244 k_sleep(K_MSEC(10));
245 /* Start reset sequence */
246 ret = gpio_pin_set_dt(&config->reset_gpio, 0);
247 if (ret < 0) {
248 LOG_ERR("Could not pull reset low (%d)", ret);
249 return ret;
250 }
251 /* Per datasheet, reset low pulse width should be at least 10usec */
252 k_sleep(K_USEC(30));
253 gpio_pin_set_dt(&config->reset_gpio, 1);
254 if (ret < 0) {
255 LOG_ERR("Could not pull reset high (%d)", ret);
256 return ret;
257 }
258 /*
259 * It is necessary to wait at least 120msec after releasing reset,
260 * before sending additional commands. This delay can be 5msec
261 * if we are certain the display module is in SLEEP IN state,
262 * but this is not guaranteed (for example, with a warm reset)
263 */
264 k_sleep(K_MSEC(150));
265 }
266
267 /* Now, write initialization settings for display, running at 400x392 */
268 for (i = 0; i < ARRAY_SIZE(rm67162_init_400x392); i++) {
269 cmd = rm67162_init_400x392[i].cmd;
270 param = rm67162_init_400x392[i].param;
271 ret = mipi_dsi_dcs_write(config->mipi_dsi, config->channel,
272 cmd, ¶m, 1);
273 if (ret < 0) {
274 return ret;
275 }
276 }
277
278 /* Set pixel format */
279 if (data->pixel_format == MIPI_DSI_PIXFMT_RGB888) {
280 param = MIPI_DCS_PIXEL_FORMAT_24BIT;
281 data->bytes_per_pixel = 3;
282 } else if (data->pixel_format == MIPI_DSI_PIXFMT_RGB565) {
283 param = MIPI_DCS_PIXEL_FORMAT_16BIT;
284 data->bytes_per_pixel = 2;
285 } else {
286 /* Unsupported pixel format */
287 LOG_ERR("Pixel format not supported");
288 return -ENOTSUP;
289 }
290 ret = mipi_dsi_dcs_write(config->mipi_dsi, config->channel,
291 MIPI_DCS_SET_PIXEL_FORMAT, ¶m, 1);
292 if (ret < 0) {
293 return ret;
294 }
295
296 /* Delay 50 ms before exiting sleep mode */
297 k_sleep(K_MSEC(50));
298 ret = mipi_dsi_dcs_write(config->mipi_dsi, config->channel,
299 MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0);
300 if (ret < 0) {
301 return ret;
302 }
303 /*
304 * We must wait 5 ms after exiting sleep mode before sending additional
305 * commands. If we intend to enter sleep mode, we must delay
306 * 120 ms before sending that command. To be safe, delay 150ms
307 */
308 k_sleep(K_MSEC(150));
309
310 /* Setup backlight */
311 if (config->bl_gpio.port != NULL) {
312 ret = gpio_pin_configure_dt(&config->bl_gpio, GPIO_OUTPUT_ACTIVE);
313 if (ret < 0) {
314 LOG_ERR("Could not configure bl GPIO (%d)", ret);
315 return ret;
316 }
317 }
318
319 if (config->te_gpio.port != NULL) {
320 /* Setup TE pin */
321 ret = gpio_pin_configure_dt(&config->te_gpio, GPIO_INPUT);
322 if (ret < 0) {
323 LOG_ERR("Could not configure TE GPIO (%d)", ret);
324 return ret;
325 }
326
327 ret = gpio_pin_interrupt_configure_dt(&config->te_gpio,
328 GPIO_INT_EDGE_TO_ACTIVE);
329 if (ret < 0) {
330 LOG_ERR("Could not configure TE interrupt (%d)", ret);
331 return ret;
332 }
333
334 /* Init and install GPIO callback */
335 gpio_init_callback(&data->te_gpio_cb, rm67162_te_isr_handler,
336 BIT(config->te_gpio.pin));
337 ret = gpio_add_callback(config->te_gpio.port, &data->te_gpio_cb);
338 if (ret < 0) {
339 LOG_ERR("Could not add TE gpio callback");
340 return ret;
341 }
342
343 /* Setup te pin semaphore */
344 k_sem_init(&data->te_sem, 0, 1);
345 }
346
347 /* Now, enable display */
348 return mipi_dsi_dcs_write(config->mipi_dsi, config->channel,
349 MIPI_DCS_SET_DISPLAY_ON, NULL, 0);
350 }
351
352 /* Helper to write framebuffer data to rm67162 via MIPI interface. */
rm67162_write_fb(const struct device * dev,bool first_write,const uint8_t * src,uint32_t len)353 static int rm67162_write_fb(const struct device *dev, bool first_write,
354 const uint8_t *src, uint32_t len)
355 {
356 const struct rm67162_config *config = dev->config;
357 ssize_t wlen;
358 struct mipi_dsi_msg msg = {0};
359
360 /* Note- we need to set custom flags on the DCS message,
361 * so we bypass the mipi_dsi_dcs_write API
362 */
363 if (first_write) {
364 msg.cmd = MIPI_DCS_WRITE_MEMORY_START;
365 } else {
366 msg.cmd = MIPI_DCS_WRITE_MEMORY_CONTINUE;
367 }
368 msg.type = MIPI_DSI_DCS_LONG_WRITE;
369 msg.flags = MCUX_DSI_2L_FB_DATA;
370 while (len > 0) {
371 msg.tx_len = len;
372 msg.tx_buf = src;
373 wlen = mipi_dsi_transfer(config->mipi_dsi, config->channel, &msg);
374 if (wlen < 0) {
375 return (int)wlen;
376 }
377 /* Advance source pointer and decrement remaining */
378 src += wlen;
379 len -= wlen;
380 /* All future commands should use WRITE_MEMORY_CONTINUE */
381 msg.cmd = MIPI_DCS_WRITE_MEMORY_CONTINUE;
382 }
383 return wlen;
384 }
385
rm67162_write(const struct device * dev,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc,const void * buf)386 static int rm67162_write(const struct device *dev, const uint16_t x,
387 const uint16_t y,
388 const struct display_buffer_descriptor *desc,
389 const void *buf)
390 {
391 const struct rm67162_config *config = dev->config;
392 struct rm67162_data *data = dev->data;
393 int ret;
394 uint16_t start, end, h_idx;
395 const uint8_t *src;
396 bool first_cmd;
397 uint8_t param[4];
398
399 LOG_DBG("W=%d, H=%d @%d,%d", desc->width, desc->height, x, y);
400
401 /*
402 * RM67162 runs in MIPI DBI mode. This means we can use command mode
403 * to write to the video memory buffer on the RM67162 control IC,
404 * and the IC will update the display automatically.
405 */
406
407 /* Set column address of target area */
408 /* First two bytes are starting X coordinate */
409 start = x;
410 end = x + desc->width - 1;
411 sys_put_be16(start, ¶m[0]);
412 /* Second two bytes are ending X coordinate */
413 sys_put_be16(end, ¶m[2]);
414 ret = mipi_dsi_dcs_write(config->mipi_dsi, config->channel,
415 MIPI_DCS_SET_COLUMN_ADDRESS, param,
416 sizeof(param));
417 if (ret < 0) {
418 return ret;
419 }
420
421 /* Set page address of target area */
422 /* First two bytes are starting Y coordinate */
423 start = y;
424 end = y + desc->height - 1;
425 sys_put_be16(start, ¶m[0]);
426 /* Second two bytes are ending X coordinate */
427 sys_put_be16(end, ¶m[2]);
428 ret = mipi_dsi_dcs_write(config->mipi_dsi, config->channel,
429 MIPI_DCS_SET_PAGE_ADDRESS, param,
430 sizeof(param));
431 if (ret < 0) {
432 return ret;
433 }
434
435 /*
436 * Now, write the framebuffer. If the tearing effect GPIO is present,
437 * wait until the display controller issues an interrupt (which will
438 * give to the TE semaphore) before sending the frame
439 */
440 if (config->te_gpio.port != NULL) {
441 /* Block sleep state until next TE interrupt so we can send
442 * frame during that interval
443 */
444 pm_policy_state_lock_get(PM_STATE_SUSPEND_TO_IDLE,
445 PM_ALL_SUBSTATES);
446 k_sem_take(&data->te_sem, K_FOREVER);
447 pm_policy_state_lock_put(PM_STATE_SUSPEND_TO_IDLE,
448 PM_ALL_SUBSTATES);
449 }
450 src = buf;
451 first_cmd = true;
452
453 if (desc->pitch == desc->width) {
454 /* Buffer is contiguous, we can perform entire transfer */
455 rm67162_write_fb(dev, first_cmd, src,
456 desc->height * desc->width * data->bytes_per_pixel);
457 } else {
458 /* Buffer is not contiguous, we must write each line separately */
459 for (h_idx = 0; h_idx < desc->height; h_idx++) {
460 rm67162_write_fb(dev, first_cmd, src,
461 desc->width * data->bytes_per_pixel);
462 first_cmd = false;
463 /* The pitch is not equal to width, account for it here */
464 src += data->bytes_per_pixel * (desc->pitch - desc->width);
465 }
466 }
467
468 return 0;
469 }
470
rm67162_get_capabilities(const struct device * dev,struct display_capabilities * capabilities)471 static void rm67162_get_capabilities(const struct device *dev,
472 struct display_capabilities *capabilities)
473 {
474 const struct rm67162_config *config = dev->config;
475 const struct rm67162_data *data = dev->data;
476
477 memset(capabilities, 0, sizeof(struct display_capabilities));
478 capabilities->x_resolution = config->panel_width;
479 capabilities->y_resolution = config->panel_height;
480 capabilities->supported_pixel_formats = PIXEL_FORMAT_RGB_565 |
481 PIXEL_FORMAT_RGB_888;
482 switch (data->pixel_format) {
483 case MIPI_DSI_PIXFMT_RGB565:
484 capabilities->current_pixel_format = PIXEL_FORMAT_RGB_565;
485 break;
486 case MIPI_DSI_PIXFMT_RGB888:
487 capabilities->current_pixel_format = PIXEL_FORMAT_RGB_888;
488 break;
489 default:
490 LOG_WRN("Unsupported display format");
491 /* Other display formats not implemented */
492 break;
493 }
494 capabilities->current_orientation = DISPLAY_ORIENTATION_ROTATED_90;
495 }
496
rm67162_blanking_off(const struct device * dev)497 static int rm67162_blanking_off(const struct device *dev)
498 {
499 const struct rm67162_config *config = dev->config;
500
501 if (config->bl_gpio.port != NULL) {
502 return gpio_pin_set_dt(&config->bl_gpio, 1);
503 } else {
504 return -ENOTSUP;
505 }
506 }
507
rm67162_blanking_on(const struct device * dev)508 static int rm67162_blanking_on(const struct device *dev)
509 {
510 const struct rm67162_config *config = dev->config;
511
512 if (config->bl_gpio.port != NULL) {
513 return gpio_pin_set_dt(&config->bl_gpio, 0);
514 } else {
515 return -ENOTSUP;
516 }
517 }
518
rm67162_set_pixel_format(const struct device * dev,const enum display_pixel_format pixel_format)519 static int rm67162_set_pixel_format(const struct device *dev,
520 const enum display_pixel_format pixel_format)
521 {
522 const struct rm67162_config *config = dev->config;
523 struct rm67162_data *data = dev->data;
524 uint8_t param;
525
526 switch (pixel_format) {
527 case PIXEL_FORMAT_RGB_565:
528 data->pixel_format = MIPI_DSI_PIXFMT_RGB565;
529 param = MIPI_DCS_PIXEL_FORMAT_16BIT;
530 data->bytes_per_pixel = 2;
531 break;
532 case PIXEL_FORMAT_RGB_888:
533 data->pixel_format = MIPI_DSI_PIXFMT_RGB888;
534 param = MIPI_DCS_PIXEL_FORMAT_24BIT;
535 data->bytes_per_pixel = 3;
536 break;
537 default:
538 /* Other display formats not implemented */
539 return -ENOTSUP;
540 }
541 return mipi_dsi_dcs_write(config->mipi_dsi, config->channel,
542 MIPI_DCS_SET_PIXEL_FORMAT, ¶m, 1);
543 }
544
rm67162_set_orientation(const struct device * dev,const enum display_orientation orientation)545 static int rm67162_set_orientation(const struct device *dev,
546 const enum display_orientation orientation)
547 {
548 if (orientation == DISPLAY_ORIENTATION_NORMAL) {
549 return 0;
550 }
551 LOG_ERR("Changing display orientation not implemented");
552 return -ENOTSUP;
553 }
554
555 #ifdef CONFIG_PM_DEVICE
556
rm67162_pm_action(const struct device * dev,enum pm_device_action action)557 static int rm67162_pm_action(const struct device *dev,
558 enum pm_device_action action)
559 {
560 const struct rm67162_config *config = dev->config;
561 struct rm67162_data *data = dev->data;
562 struct mipi_dsi_device mdev = {0};
563
564 mdev.data_lanes = config->num_of_lanes;
565 mdev.pixfmt = data->pixel_format;
566
567 switch (action) {
568 case PM_DEVICE_ACTION_SUSPEND:
569 /* Detach from the MIPI DSI controller */
570 return mipi_dsi_detach(config->mipi_dsi, config->channel, &mdev);
571 case PM_DEVICE_ACTION_RESUME:
572 return mipi_dsi_attach(config->mipi_dsi, config->channel, &mdev);
573 default:
574 return -ENOTSUP;
575 }
576 }
577
578 #endif /* CONFIG_PM_DEVICE */
579
580 static DEVICE_API(display, rm67162_api) = {
581 .blanking_on = rm67162_blanking_on,
582 .blanking_off = rm67162_blanking_off,
583 .get_capabilities = rm67162_get_capabilities,
584 .write = rm67162_write,
585 .set_pixel_format = rm67162_set_pixel_format,
586 .set_orientation = rm67162_set_orientation,
587 };
588
589 #define RM67162_PANEL(id) \
590 static const struct rm67162_config rm67162_config_##id = { \
591 .mipi_dsi = DEVICE_DT_GET(DT_INST_BUS(id)), \
592 .num_of_lanes = DT_INST_PROP_BY_IDX(id, data_lanes, 0), \
593 .channel = DT_INST_REG_ADDR(id), \
594 .reset_gpio = GPIO_DT_SPEC_INST_GET_OR(id, reset_gpios, {0}), \
595 .bl_gpio = GPIO_DT_SPEC_INST_GET_OR(id, bl_gpios, {0}), \
596 .te_gpio = GPIO_DT_SPEC_INST_GET_OR(id, te_gpios, {0}), \
597 .panel_width = DT_INST_PROP(id, width), \
598 .panel_height = DT_INST_PROP(id, height), \
599 }; \
600 static struct rm67162_data rm67162_data_##id = { \
601 .pixel_format = DT_INST_PROP(id, pixel_format), \
602 }; \
603 PM_DEVICE_DT_INST_DEFINE(id, rm67162_pm_action); \
604 DEVICE_DT_INST_DEFINE(id, \
605 &rm67162_init, \
606 PM_DEVICE_DT_INST_GET(id), \
607 &rm67162_data_##id, \
608 &rm67162_config_##id, \
609 POST_KERNEL, \
610 CONFIG_APPLICATION_INIT_PRIORITY, \
611 &rm67162_api);
612
613 DT_INST_FOREACH_STATUS_OKAY(RM67162_PANEL)
614