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, &param, 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, &param, 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, &param[0]);
412 	/* Second two bytes are ending X coordinate */
413 	sys_put_be16(end, &param[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, &param[0]);
426 	/* Second two bytes are ending X coordinate */
427 	sys_put_be16(end, &param[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, &param, 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