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