1 /*
2  * Copyright 2023,2025 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7  /* Based on dsi_mcux.c, which is (c) 2022 NXP */
8 
9 #define DT_DRV_COMPAT nxp_mipi_dsi_2l
10 
11 #include <zephyr/kernel.h>
12 #include <zephyr/drivers/clock_control.h>
13 #include <zephyr/drivers/mipi_dsi.h>
14 #include <zephyr/drivers/mipi_dsi/mipi_dsi_mcux_2l.h>
15 #include <zephyr/drivers/dma.h>
16 #include <zephyr/drivers/dma/dma_mcux_smartdma.h>
17 #include <zephyr/logging/log.h>
18 
19 #include <fsl_mipi_dsi.h>
20 #include <fsl_clock.h>
21 #ifdef CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA
22 #include <fsl_inputmux.h>
23 #include <fsl_smartdma.h>
24 #endif
25 
26 #include <soc.h>
27 
28 LOG_MODULE_REGISTER(dsi_mcux_host, CONFIG_MIPI_DSI_LOG_LEVEL);
29 
30 struct mcux_mipi_dsi_config {
31 	MIPI_DSI_HOST_Type *base;
32 	dsi_dpi_config_t dpi_config;
33 	bool auto_insert_eotp;
34 	bool noncontinuous_hs_clk;
35 	const struct device *bit_clk_dev;
36 	clock_control_subsys_t bit_clk_subsys;
37 	const struct device *esc_clk_dev;
38 	clock_control_subsys_t esc_clk_subsys;
39 	const struct device *pixel_clk_dev;
40 	clock_control_subsys_t pixel_clk_subsys;
41 	uint32_t dphy_ref_freq;
42 #ifdef CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA
43 	const struct device *smart_dma;
44 #else
45 	void (*irq_config_func)(const struct device *dev);
46 #endif
47 };
48 
49 struct mcux_mipi_dsi_data {
50 	dsi_handle_t mipi_handle;
51 	struct k_sem transfer_sem;
52 	uint16_t flags;
53 	uint8_t lane_mask;
54 #if DT_PROP(DT_NODELABEL(mipi_dsi), ulps_control)
55 	uint32_t delay_hs_to_ulps_ns;
56 	uint32_t delay_lp_to_ulps_ns;
57 #endif
58 #ifdef CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA
59 	smartdma_dsi_param_t smartdma_params __aligned(4);
60 	uint32_t smartdma_stack[32];
61 	uint8_t dma_slot;
62 #endif
63 };
64 
65 
66 /* MAX DSI TX payload */
67 #define DSI_TX_MAX_PAYLOAD_BYTE (64U * 4U)
68 
dsi_mcux_transfer_prepare(const struct device * dev)69 static void dsi_mcux_transfer_prepare(const struct device *dev)
70 {
71 #if DT_PROP(DT_NODELABEL(mipi_dsi), ulps_control)
72 	const struct mcux_mipi_dsi_config *config = dev->config;
73 
74 	/* If in ULPS state, exit before transfer. */
75 	if (DSI_GetUlpsStatus(config->base) != 0U) {
76 		DSI_SetUlpsStatus(config->base, 0U);
77 		while (DSI_GetUlpsStatus(config->base) != 0U) {
78 		}
79 	}
80 #endif
81 }
82 
dsi_mcux_transfer_complete(const struct device * dev)83 static void dsi_mcux_transfer_complete(const struct device *dev)
84 {
85 #if DT_PROP(DT_NODELABEL(mipi_dsi), ulps_control)
86 	const struct mcux_mipi_dsi_config *config = dev->config;
87 	struct mcux_mipi_dsi_data *data = dev->data;
88 
89 	/* Enter ulps state after transfer completes. */
90 	if ((data->flags & MCUX_DSI_2L_ULPS) != 0U) {
91 		/* It is necessary to delay a period. */
92 		if (data->flags & MIPI_DSI_MSG_USE_LPM) {
93 			k_busy_wait(data->delay_lp_to_ulps_ns / 1000U);
94 		} else {
95 			k_busy_wait(data->delay_hs_to_ulps_ns / 1000U);
96 		}
97 		DSI_SetUlpsStatus(config->base, data->lane_mask);
98 		while (DSI_GetUlpsStatus(config->base) != data->lane_mask) {
99 		}
100 	}
101 #endif
102 }
103 
104 #ifdef CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA
105 
106 /* Callback for DSI DMA transfer completion, called in ISR context */
dsi_mcux_dma_cb(const struct device * dma_dev,void * user_data,uint32_t channel,int status)107 static void dsi_mcux_dma_cb(const struct device *dma_dev,
108 				void *user_data, uint32_t channel, int status)
109 {
110 	const struct device *dev = user_data;
111 	const struct mcux_mipi_dsi_config *config = dev->config;
112 	struct mcux_mipi_dsi_data *data = dev->data;
113 	uint32_t int_flags1, int_flags2;
114 
115 	if (status != 0) {
116 		LOG_ERR("SMARTDMA transfer failed");
117 	} else {
118 		/* Disable DSI interrupts at transfer completion */
119 		DSI_DisableInterrupts(config->base, kDSI_InterruptGroup1ApbTxDone |
120 						kDSI_InterruptGroup1HtxTo, 0U);
121 		DSI_GetAndClearInterruptStatus(config->base, &int_flags1, &int_flags2);
122 		k_sem_give(&data->transfer_sem);
123 	}
124 
125 	dsi_mcux_transfer_complete(dev);
126 }
127 
128 /* Helper function to transfer DSI color (DMA based implementation) */
dsi_mcux_tx_color(const struct device * dev,uint8_t channel,struct mipi_dsi_msg * msg)129 static int dsi_mcux_tx_color(const struct device *dev, uint8_t channel,
130 			     struct mipi_dsi_msg *msg)
131 {
132 	/*
133 	 * Color streams are a special case for this DSI peripheral, because
134 	 * the SMARTDMA peripheral (if enabled) can be used to accelerate
135 	 * the transfer of data to the DSI. The SMARTDMA has the additional
136 	 * advantage over traditional DMA of being able to automatically
137 	 * byte swap color data. This is advantageous, as most graphical
138 	 * frameworks store RGB data in little endian format, but many
139 	 * MIPI displays expect color data in big endian format.
140 	 */
141 	const struct mcux_mipi_dsi_config *config = dev->config;
142 	struct mcux_mipi_dsi_data *data = dev->data;
143 	struct dma_config dma_cfg = {0};
144 	int ret;
145 
146 	if (channel != 0) {
147 		return -ENOTSUP; /* DMA can only transfer on virtual channel 0 */
148 	}
149 
150 	/* Configure smartDMA device, and run transfer */
151 	data->smartdma_params.p_buffer = msg->tx_buf;
152 	data->smartdma_params.buffersize = msg->tx_len;
153 
154 	dma_cfg.dma_callback = dsi_mcux_dma_cb;
155 	dma_cfg.user_data = (struct device *)dev;
156 	dma_cfg.head_block = (struct dma_block_config *)&data->smartdma_params;
157 	dma_cfg.block_count = 1;
158 	dma_cfg.dma_slot = data->dma_slot;
159 	dma_cfg.channel_direction = MEMORY_TO_PERIPHERAL;
160 	ret = dma_config(config->smart_dma, 0, &dma_cfg);
161 	if (ret < 0) {
162 		LOG_ERR("Could not configure SMARTDMA");
163 		return ret;
164 	}
165 	/*
166 	 * SMARTDMA uses DSI interrupt line as input for the DMA
167 	 * transfer trigger. Therefore, we need to enable DSI TX
168 	 * interrupts in order to trigger the DMA engine.
169 	 * Note that if the MIPI IRQ is enabled in
170 	 * the NVIC, it will fire on every SMARTDMA transfer
171 	 */
172 	DSI_EnableInterrupts(config->base, kDSI_InterruptGroup1ApbTxDone |
173 					kDSI_InterruptGroup1HtxTo, 0U);
174 	/* Trigger DMA engine */
175 	ret = dma_start(config->smart_dma, 0);
176 	if (ret < 0) {
177 		LOG_ERR("Could not start SMARTDMA");
178 		return ret;
179 	}
180 	/* Wait for TX completion */
181 	k_sem_take(&data->transfer_sem, K_FOREVER);
182 	return msg->tx_len;
183 }
184 
185 #else /* CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA is not set */
186 
187 /* Callback for DSI transfer completion, called in ISR context */
dsi_transfer_complete(MIPI_DSI_HOST_Type * base,dsi_handle_t * handle,status_t status,void * userData)188 static void dsi_transfer_complete(MIPI_DSI_HOST_Type *base,
189 	dsi_handle_t *handle, status_t status, void *userData)
190 {
191 	struct device *dev = userData;
192 	struct mcux_mipi_dsi_data *data = dev->data;
193 
194 	dsi_mcux_transfer_complete(dev);
195 
196 	k_sem_give(&data->transfer_sem);
197 }
198 
199 
200 /* Helper function to transfer DSI color (Interrupt based implementation) */
dsi_mcux_tx_color(const struct device * dev,uint8_t channel,struct mipi_dsi_msg * msg)201 static int dsi_mcux_tx_color(const struct device *dev, uint8_t channel,
202 			     struct mipi_dsi_msg *msg)
203 {
204 	const struct mcux_mipi_dsi_config *config = dev->config;
205 	struct mcux_mipi_dsi_data *data = dev->data;
206 	status_t status;
207 	dsi_transfer_t xfer = {
208 		.virtualChannel = channel,
209 		.txData = msg->tx_buf,
210 		.rxDataSize = (uint16_t)msg->rx_len,
211 		.rxData = msg->rx_buf,
212 		.sendDscCmd = true,
213 		.dscCmd = msg->cmd,
214 		.txDataType = kDSI_TxDataDcsLongWr,
215 		/* default to high speed unless told to use low power */
216 		.flags = (msg->flags & MIPI_DSI_MSG_USE_LPM) ? 0 : kDSI_TransferUseHighSpeed,
217 	};
218 
219 	/*
220 	 * Cap transfer size. Note that we subtract six bytes here,
221 	 * one for the DSC command and five to insure that
222 	 * transfers are still aligned on a pixel boundary
223 	 * (two or three byte pixel sizes are supported).
224 	 */
225 	xfer.txDataSize = MIN(msg->tx_len, (DSI_TX_MAX_PAYLOAD_BYTE - 6));
226 
227 	if (IS_ENABLED(CONFIG_MIPI_DSI_MCUX_2L_SWAP16)) {
228 		/* Manually swap the 16 byte color data in software */
229 		uint8_t *src = (uint8_t *)xfer.txData;
230 		uint8_t tmp;
231 
232 		for (uint32_t i = 0; i < xfer.txDataSize; i += 2) {
233 			tmp = src[i];
234 			src[i] = src[i + 1];
235 			src[i + 1] = tmp;
236 		}
237 	}
238 	/* Send TX data using non-blocking DSI API */
239 	status = DSI_TransferNonBlocking(config->base,
240 					&data->mipi_handle, &xfer);
241 	/* Wait for transfer completion */
242 	k_sem_take(&data->transfer_sem, K_FOREVER);
243 	if (status != kStatus_Success) {
244 		LOG_ERR("Transmission failed");
245 		return -EIO;
246 	}
247 	return xfer.txDataSize;
248 }
249 
250 /* ISR is used for DSI interrupt based implementation, unnecessary if DMA is used */
mipi_dsi_isr(const struct device * dev)251 static int mipi_dsi_isr(const struct device *dev)
252 {
253 	const struct mcux_mipi_dsi_config *config = dev->config;
254 	struct mcux_mipi_dsi_data *data = dev->data;
255 
256 	DSI_TransferHandleIRQ(config->base, &data->mipi_handle);
257 	return 0;
258 }
259 
260 #endif
261 
dsi_mcux_attach(const struct device * dev,uint8_t channel,const struct mipi_dsi_device * mdev)262 static int dsi_mcux_attach(const struct device *dev,
263 			   uint8_t channel,
264 			   const struct mipi_dsi_device *mdev)
265 {
266 	const struct mcux_mipi_dsi_config *config = dev->config;
267 	struct mcux_mipi_dsi_data *data = dev->data;
268 	dsi_dphy_config_t dphy_config;
269 	dsi_config_t dsi_config;
270 	uint32_t dphy_bit_clk_freq;
271 	uint32_t dphy_esc_clk_freq;
272 	uint32_t dsi_pixel_clk_freq;
273 	uint32_t bit_width;
274 
275 	DSI_GetDefaultConfig(&dsi_config);
276 	dsi_config.numLanes = mdev->data_lanes;
277 	dsi_config.autoInsertEoTp = config->auto_insert_eotp;
278 	dsi_config.enableNonContinuousHsClk = config->noncontinuous_hs_clk;
279 
280 	imxrt_pre_init_display_interface();
281 
282 	/* Init the DSI module. */
283 	DSI_Init(config->base, &dsi_config);
284 
285 #ifdef CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA
286 	/* Connect DSI IRQ line to SMARTDMA trigger via
287 	 * INPUTMUX.
288 	 */
289 	/* Attach INPUTMUX from MIPI to SMARTDMA */
290 	INPUTMUX_Init(INPUTMUX);
291 	INPUTMUX_AttachSignal(INPUTMUX, 0, kINPUTMUX_MipiIrqToSmartDmaInput);
292 	/* Gate inputmux clock to save power */
293 	INPUTMUX_Deinit(INPUTMUX);
294 
295 	if (!device_is_ready(config->smart_dma)) {
296 		return -ENODEV;
297 	}
298 
299 	switch (mdev->pixfmt) {
300 	case MIPI_DSI_PIXFMT_RGB888:
301 		data->dma_slot = kSMARTDMA_MIPI_RGB888_DMA;
302 		data->smartdma_params.disablePixelByteSwap = true;
303 		break;
304 	case MIPI_DSI_PIXFMT_RGB565:
305 		data->dma_slot = kSMARTDMA_MIPI_RGB565_DMA;
306 		if (IS_ENABLED(CONFIG_MIPI_DSI_MCUX_2L_SWAP16)) {
307 			data->smartdma_params.disablePixelByteSwap = false;
308 		} else {
309 			data->smartdma_params.disablePixelByteSwap = true;
310 		}
311 		break;
312 	default:
313 		LOG_ERR("SMARTDMA does not support pixel_format %u",
314 			mdev->pixfmt);
315 		return -ENODEV;
316 	}
317 
318 	data->smartdma_params.smartdma_stack = data->smartdma_stack;
319 
320 	dma_smartdma_install_fw(config->smart_dma,
321 				(uint8_t *)s_smartdmaDisplayFirmware,
322 				s_smartdmaDisplayFirmwareSize);
323 #else
324 	/* Create transfer handle */
325 	if (DSI_TransferCreateHandle(config->base, &data->mipi_handle,
326 				dsi_transfer_complete, (void *)dev) != kStatus_Success) {
327 		return -ENODEV;
328 	}
329 #endif
330 
331 #if DT_PROP(DT_NODELABEL(mipi_dsi), ulps_control)
332 	/* Get how many lanes are enabled. */
333 	uint8_t data_lanes = mdev->data_lanes;
334 
335 	/* Calculate the lane mask. */
336 	data->lane_mask = 2U;
337 	while (data_lanes--) {
338 		data->lane_mask <<= 1U;
339 	}
340 	data->lane_mask--;
341 
342 	/* Clock lane cannot go into ULPS if not in non-continuous mode. */
343 	if (!dsi_config.enableNonContinuousHsClk) {
344 		data->lane_mask &= ~0x1U;
345 	}
346 #endif
347 
348 	/* Get the DPHY bit clock frequency */
349 	if (clock_control_get_rate(config->bit_clk_dev,
350 				config->bit_clk_subsys,
351 				&dphy_bit_clk_freq)) {
352 		return -EINVAL;
353 	};
354 	/* Get the DPHY ESC clock frequency */
355 	if (clock_control_get_rate(config->esc_clk_dev,
356 				config->esc_clk_subsys,
357 				&dphy_esc_clk_freq)) {
358 		return -EINVAL;
359 	}
360 	/* Get the Pixel clock frequency */
361 	if (clock_control_get_rate(config->pixel_clk_dev,
362 				config->pixel_clk_subsys,
363 				&dsi_pixel_clk_freq)) {
364 		return -EINVAL;
365 	}
366 
367 	switch (config->dpi_config.pixelPacket) {
368 	case kDSI_PixelPacket16Bit:
369 		bit_width = 16;
370 		break;
371 	case kDSI_PixelPacket18Bit:
372 		__fallthrough;
373 	case kDSI_PixelPacket18BitLoosely:
374 		bit_width = 18;
375 		break;
376 	case kDSI_PixelPacket24Bit:
377 		bit_width = 24;
378 		break;
379 	default:
380 		return -EINVAL; /* Invalid bit width enum value? */
381 	}
382 	/* Init DPHY.
383 	 *
384 	 * The DPHY bit clock must be fast enough to send out the pixels, it should be
385 	 * larger than:
386 	 *
387 	 *         (Pixel clock * bit per output pixel) / number of MIPI data lane
388 	 */
389 	if (((dsi_pixel_clk_freq * bit_width) / mdev->data_lanes) > dphy_bit_clk_freq) {
390 		return -EINVAL;
391 	}
392 
393 	DSI_GetDphyDefaultConfig(&dphy_config, dphy_bit_clk_freq, dphy_esc_clk_freq);
394 
395 	if (config->dphy_ref_freq != 0) {
396 		dphy_bit_clk_freq = DSI_InitDphy(config->base,
397 					&dphy_config, config->dphy_ref_freq);
398 	} else {
399 		/* DPHY PLL is not present, ref clock is unused */
400 		DSI_InitDphy(config->base, &dphy_config, 0);
401 	}
402 
403 	/*
404 	 * If nxp,lcdif node is present, then the MIPI DSI driver will
405 	 * accept input on the DPI port from the LCDIF, and convert the output
406 	 * to DSI data. This is useful for video mode, where the LCDIF can
407 	 * constantly refresh the MIPI panel.
408 	 */
409 	if (mdev->mode_flags & MIPI_DSI_MODE_VIDEO) {
410 		/* Init DPI interface. */
411 		DSI_SetDpiConfig(config->base, &config->dpi_config, mdev->data_lanes,
412 						dsi_pixel_clk_freq, dphy_bit_clk_freq);
413 	}
414 
415 #if DT_PROP(DT_NODELABEL(mipi_dsi), ulps_control)
416 	/* Calculate the delay before entering ULPS. After the last cycle of data has
417 	 * been accepted from the controller to the PHY, the data still needs to be
418 	 * serialized, transmitted, then the appropriate timing must be met before
419 	 * entering the ULPS. The data lane rate can impact this, so calcuate the time
420 	 * it takes for one data lane to send 1 bit in HS and LP mode first.
421 	 */
422 	uint32_t hs_bit_ns = 1000000000UL / dphy_bit_clk_freq;
423 	uint32_t lp_bit_ns = 1000000000UL / dphy_esc_clk_freq;
424 
425 	/* When the clock is in non-continuous mode, the formula for the delay after
426 	 * HS transmit would be:
427 	 * delay = 4*(UI*8) + m_prg_hs_trail*(UI*8) + 2*TxClkEsc_period +
428 	 * (cfg_t_post+2)*(UI*8) + mc_prg_hs_trail*(UI*8) + 2*TxClkEsc_period.
429 	 * Similarly, the delay after LP transmit would be:
430 	 * 4*(UI*8) + 2*TxClkEsc_period + TxClkEsc_period + 2*TxClkEsc_period.
431 	 */
432 	if (dsi_config.enableNonContinuousHsClk) {
433 		data->delay_hs_to_ulps_ns = 4U * 8U * hs_bit_ns / dsi_config.numLanes +
434 			dphy_config.tHsTrail_ByteClk * 8U * hs_bit_ns +
435 			2U * lp_bit_ns +
436 			(dphy_config.tClkPost_ByteClk + 2U) * hs_bit_ns +
437 			dphy_config.tClkTrail_ByteClk * 8U * hs_bit_ns +
438 			2U * lp_bit_ns;
439 		data->delay_lp_to_ulps_ns = (2U + 1U + 2U) * lp_bit_ns +
440 			4U * 8U * lp_bit_ns / dsi_config.numLanes;
441 	/* When the clock is in continuous mode, the formula for the delay after
442 	 * HS transmit would be:
443 	 * delay = 4*(UI*8) + m_prg_hs_trail*(UI*8) + 2*TxClkEsc_period.
444 	 * Similarly, the delay after LP transmit would be:
445 	 * delay = 4*(UI*8) + 2*TxClkEsc_period + TxClkEsc_period.
446 	 */
447 	} else {
448 		data->delay_hs_to_ulps_ns = 2U * lp_bit_ns +
449 			4U * 8U * hs_bit_ns / dsi_config.numLanes +
450 			dphy_config.tHsTrail_ByteClk * 8U * hs_bit_ns;
451 		data->delay_lp_to_ulps_ns = (2U + 1U) * lp_bit_ns +
452 			4U * 8U * lp_bit_ns / dsi_config.numLanes;
453 	}
454 #endif
455 
456 	imxrt_post_init_display_interface();
457 
458 	return 0;
459 }
460 
dsi_mcux_detach(const struct device * dev,uint8_t channel,const struct mipi_dsi_device * mdev)461 static int dsi_mcux_detach(const struct device *dev, uint8_t channel,
462 			   const struct mipi_dsi_device *mdev)
463 {
464 	const struct mcux_mipi_dsi_config *config = dev->config;
465 
466 	/* Enable DPHY auto power down */
467 	DSI_DeinitDphy(config->base);
468 	/* Fully power off DPHY */
469 	config->base->PD_DPHY = 0x1;
470 	/* Deinit MIPI */
471 	DSI_Deinit(config->base);
472 	/* Call IMX RT clock function to gate clocks and power at SOC level */
473 	imxrt_deinit_display_interface();
474 	return 0;
475 }
476 
477 
478 
dsi_mcux_transfer(const struct device * dev,uint8_t channel,struct mipi_dsi_msg * msg)479 static ssize_t dsi_mcux_transfer(const struct device *dev, uint8_t channel,
480 				 struct mipi_dsi_msg *msg)
481 {
482 	const struct mcux_mipi_dsi_config *config = dev->config;
483 
484 	dsi_transfer_t dsi_xfer = {0};
485 	status_t status;
486 	int ret;
487 
488 	dsi_xfer.virtualChannel = channel;
489 	dsi_xfer.txDataSize = msg->tx_len;
490 	dsi_xfer.txData = msg->tx_buf;
491 	dsi_xfer.rxDataSize = msg->rx_len;
492 	dsi_xfer.rxData = msg->rx_buf;
493 	/* default to high speed unless told to use low power */
494 	dsi_xfer.flags = (msg->flags & MIPI_DSI_MSG_USE_LPM) ? 0 : kDSI_TransferUseHighSpeed;
495 
496 #if DT_PROP(DT_NODELABEL(mipi_dsi), ulps_control)
497 	struct mcux_mipi_dsi_data *data = dev->data;
498 
499 	data->flags = (msg->flags & MIPI_DSI_MSG_USE_LPM) ? MIPI_DSI_MSG_USE_LPM : 0U;
500 	if (msg->flags & MCUX_DSI_2L_ULPS) {
501 		data->flags |= MCUX_DSI_2L_ULPS;
502 	}
503 #endif
504 
505 	dsi_mcux_transfer_prepare(dev);
506 
507 	switch (msg->type) {
508 	case MIPI_DSI_DCS_READ:
509 		LOG_ERR("DCS Read not yet implemented or used");
510 		return -ENOTSUP;
511 	case MIPI_DSI_DCS_SHORT_WRITE:
512 		dsi_xfer.sendDscCmd = true;
513 		dsi_xfer.dscCmd = msg->cmd;
514 		dsi_xfer.txDataType = kDSI_TxDataDcsShortWrNoParam;
515 		break;
516 	case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
517 		dsi_xfer.sendDscCmd = true;
518 		dsi_xfer.dscCmd = msg->cmd;
519 		dsi_xfer.txDataType = kDSI_TxDataDcsShortWrOneParam;
520 		break;
521 	case MIPI_DSI_DCS_LONG_WRITE:
522 		dsi_xfer.sendDscCmd = true;
523 		dsi_xfer.dscCmd = msg->cmd;
524 		dsi_xfer.txDataType = kDSI_TxDataDcsLongWr;
525 		if (msg->flags & MCUX_DSI_2L_FB_DATA) {
526 			/*
527 			 * Special case- transfer framebuffer data using
528 			 * SMARTDMA or non blocking DSI API. The framebuffer
529 			 * will also be color swapped, if enabled.
530 			 */
531 			ret = dsi_mcux_tx_color(dev, channel, msg);
532 			if (ret < 0) {
533 				LOG_ERR("Transmission failed");
534 				return -EIO;
535 			}
536 			return ret;
537 		}
538 		break;
539 	case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
540 		dsi_xfer.txDataType = kDSI_TxDataGenShortWrNoParam;
541 		break;
542 	case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
543 		dsi_xfer.txDataType = kDSI_TxDataGenShortWrOneParam;
544 		break;
545 	case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
546 		dsi_xfer.txDataType = kDSI_TxDataGenShortWrTwoParam;
547 		break;
548 	case MIPI_DSI_GENERIC_LONG_WRITE:
549 		dsi_xfer.txDataType = kDSI_TxDataGenLongWr;
550 		break;
551 	case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
552 		__fallthrough;
553 	case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
554 		__fallthrough;
555 	case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
556 		LOG_ERR("Generic Read not yet implemented or used");
557 		return -ENOTSUP;
558 	default:
559 		LOG_ERR("Unsupported message type (%d)", msg->type);
560 		return -ENOTSUP;
561 	}
562 
563 	status = DSI_TransferBlocking(config->base, &dsi_xfer);
564 
565 	dsi_mcux_transfer_complete(dev);
566 
567 	if (status != kStatus_Success) {
568 		LOG_ERR("Transmission failed");
569 		return -EIO;
570 	}
571 
572 	if (msg->rx_len != 0) {
573 		/* Return rx_len on a read */
574 		return msg->rx_len;
575 	}
576 
577 	/* Return tx_len on a write */
578 	return msg->tx_len;
579 
580 }
581 
582 static DEVICE_API(mipi_dsi, dsi_mcux_api) = {
583 	.attach = dsi_mcux_attach,
584 	.detach = dsi_mcux_detach,
585 	.transfer = dsi_mcux_transfer,
586 };
587 
mcux_mipi_dsi_init(const struct device * dev)588 static int mcux_mipi_dsi_init(const struct device *dev)
589 {
590 	const struct mcux_mipi_dsi_config *config = dev->config;
591 	struct mcux_mipi_dsi_data *data = dev->data;
592 
593 #ifndef CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA
594 	/* Enable IRQ */
595 	config->irq_config_func(dev);
596 #endif
597 
598 	k_sem_init(&data->transfer_sem, 0, 1);
599 
600 	if (!device_is_ready(config->bit_clk_dev) ||
601 			!device_is_ready(config->esc_clk_dev) ||
602 			!device_is_ready(config->pixel_clk_dev)) {
603 		return -ENODEV;
604 	}
605 	return 0;
606 }
607 
608 #define MCUX_DSI_DPI_CONFIG(id)									\
609 	IF_ENABLED(DT_NODE_HAS_PROP(DT_DRV_INST(id), nxp_lcdif),				\
610 	(.dpi_config = {									\
611 		.dpiColorCoding = DT_INST_ENUM_IDX(id, dpi_color_coding),			\
612 		.pixelPacket = DT_INST_ENUM_IDX(id, dpi_pixel_packet),				\
613 		.videoMode = DT_INST_ENUM_IDX(id, dpi_video_mode),				\
614 		.bllpMode = DT_INST_ENUM_IDX(id, dpi_bllp_mode),				\
615 		.pixelPayloadSize = DT_INST_PROP_BY_PHANDLE(id, nxp_lcdif, width),		\
616 		.panelHeight = DT_INST_PROP_BY_PHANDLE(id, nxp_lcdif, height),			\
617 		.polarityFlags = (DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif),		\
618 					display_timings),  vsync_active) ?			\
619 					kDSI_DpiVsyncActiveHigh :				\
620 					kDSI_DpiVsyncActiveLow) |				\
621 				(DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif),		\
622 					display_timings),  hsync_active) ?			\
623 					kDSI_DpiHsyncActiveHigh :				\
624 					kDSI_DpiHsyncActiveLow),				\
625 		.hfp = DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif),				\
626 					display_timings),  hfront_porch),			\
627 		.hbp = DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif),				\
628 					display_timings),  hback_porch),			\
629 		.hsw = DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif),				\
630 					display_timings),  hsync_len),				\
631 		.vfp = DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif),				\
632 					display_timings),  vfront_porch),			\
633 		.vbp = DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif),				\
634 					display_timings),  vback_porch),			\
635 	},))
636 
637 #define MCUX_MIPI_DSI_DEVICE(id)								\
638 	COND_CODE_1(CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA,						\
639 	(), (static void mipi_dsi_##n##_irq_config_func(const struct device *dev)		\
640 	{											\
641 		IRQ_CONNECT(DT_INST_IRQN(id), DT_INST_IRQ(id, priority),			\
642 			mipi_dsi_isr, DEVICE_DT_INST_GET(id), 0);				\
643 			irq_enable(DT_INST_IRQN(id));						\
644 	}))											\
645 												\
646 	static const struct mcux_mipi_dsi_config mipi_dsi_config_##id = {			\
647 		MCUX_DSI_DPI_CONFIG(id)								\
648 		COND_CODE_1(CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA,					\
649 		(.smart_dma = DEVICE_DT_GET(DT_INST_DMAS_CTLR_BY_NAME(id, smartdma)),),		\
650 		(.irq_config_func = mipi_dsi_##n##_irq_config_func,))				\
651 		.base = (MIPI_DSI_HOST_Type *)DT_INST_REG_ADDR(id),				\
652 		.auto_insert_eotp = DT_INST_PROP(id, autoinsert_eotp),				\
653 		.noncontinuous_hs_clk = DT_INST_PROP(id, noncontinuous_hs_clk),			\
654 		.dphy_ref_freq = DT_INST_PROP_OR(id, dphy_ref_frequency, 0),			\
655 		.bit_clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_NAME(id, dphy)),		\
656 		.bit_clk_subsys =								\
657 			(clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_NAME(id, dphy, name),	\
658 		.esc_clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_NAME(id, esc)),		\
659 		.esc_clk_subsys =								\
660 			(clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_NAME(id, esc, name),	\
661 		.pixel_clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_NAME(id, pixel)),		\
662 		.pixel_clk_subsys =								\
663 			(clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_NAME(id, pixel, name),	\
664 	};											\
665 												\
666 	static struct mcux_mipi_dsi_data mipi_dsi_data_##id;					\
667 	DEVICE_DT_INST_DEFINE(id,								\
668 			    &mcux_mipi_dsi_init,						\
669 			    NULL,								\
670 			    &mipi_dsi_data_##id,						\
671 			    &mipi_dsi_config_##id,						\
672 			    POST_KERNEL,							\
673 			    CONFIG_MIPI_DSI_INIT_PRIORITY,					\
674 			    &dsi_mcux_api);
675 
676 DT_INST_FOREACH_STATUS_OKAY(MCUX_MIPI_DSI_DEVICE)
677