1 /*
2 * Copyright 2023, 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_inputmux.h>
20 #include <fsl_mipi_dsi.h>
21 #include <fsl_clock.h>
22
23 #include <soc.h>
24
25 LOG_MODULE_REGISTER(dsi_mcux_host, CONFIG_MIPI_DSI_LOG_LEVEL);
26
27 struct mcux_mipi_dsi_config {
28 MIPI_DSI_HOST_Type *base;
29 dsi_dpi_config_t dpi_config;
30 bool auto_insert_eotp;
31 bool noncontinuous_hs_clk;
32 const struct device *bit_clk_dev;
33 clock_control_subsys_t bit_clk_subsys;
34 const struct device *esc_clk_dev;
35 clock_control_subsys_t esc_clk_subsys;
36 const struct device *pixel_clk_dev;
37 clock_control_subsys_t pixel_clk_subsys;
38 uint32_t dphy_ref_freq;
39 #ifdef CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA
40 const struct device *smart_dma;
41 #else
42 void (*irq_config_func)(const struct device *dev);
43 #endif
44 };
45
46 struct mcux_mipi_dsi_data {
47 dsi_handle_t mipi_handle;
48 struct k_sem transfer_sem;
49 #ifdef CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA
50 uint8_t dma_slot;
51 #endif
52 };
53
54
55 /* MAX DSI TX payload */
56 #define DSI_TX_MAX_PAYLOAD_BYTE (64U * 4U)
57
58
59 #ifdef CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA
60
61 /* 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)62 static void dsi_mcux_dma_cb(const struct device *dma_dev,
63 void *user_data, uint32_t channel, int status)
64 {
65 const struct device *dev = user_data;
66 const struct mcux_mipi_dsi_config *config = dev->config;
67 struct mcux_mipi_dsi_data *data = dev->data;
68 uint32_t int_flags1, int_flags2;
69
70 if (status != 0) {
71 LOG_ERR("SMARTDMA transfer failed");
72 } else {
73 /* Disable DSI interrupts at transfer completion */
74 DSI_DisableInterrupts(config->base, kDSI_InterruptGroup1ApbTxDone |
75 kDSI_InterruptGroup1HtxTo, 0U);
76 DSI_GetAndClearInterruptStatus(config->base, &int_flags1, &int_flags2);
77 k_sem_give(&data->transfer_sem);
78 }
79 }
80
81 /* 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)82 static int dsi_mcux_tx_color(const struct device *dev, uint8_t channel,
83 struct mipi_dsi_msg *msg)
84 {
85 /*
86 * Color streams are a special case for this DSI peripheral, because
87 * the SMARTDMA peripheral (if enabled) can be used to accelerate
88 * the transfer of data to the DSI. The SMARTDMA has the additional
89 * advantage over traditional DMA of being able to automatically
90 * byte swap color data. This is advantageous, as most graphical
91 * frameworks store RGB data in little endian format, but many
92 * MIPI displays expect color data in big endian format.
93 */
94 const struct mcux_mipi_dsi_config *config = dev->config;
95 struct mcux_mipi_dsi_data *data = dev->data;
96 struct dma_config dma_cfg = {0};
97 struct dma_block_config block = {0};
98 int ret;
99
100 if (channel != 0) {
101 return -ENOTSUP; /* DMA can only transfer on virtual channel 0 */
102 }
103
104 /* Configure smartDMA device, and run transfer */
105 block.source_address = (uint32_t)msg->tx_buf;
106 block.block_size = msg->tx_len;
107
108 dma_cfg.dma_callback = dsi_mcux_dma_cb;
109 dma_cfg.user_data = (struct device *)dev;
110 dma_cfg.head_block = █
111 dma_cfg.block_count = 1;
112 dma_cfg.dma_slot = data->dma_slot;
113 dma_cfg.channel_direction = MEMORY_TO_PERIPHERAL;
114 ret = dma_config(config->smart_dma, 0, &dma_cfg);
115 if (ret < 0) {
116 LOG_ERR("Could not configure SMARTDMA");
117 return ret;
118 }
119 /*
120 * SMARTDMA uses DSI interrupt line as input for the DMA
121 * transfer trigger. Therefore, we need to enable DSI TX
122 * interrupts in order to trigger the DMA engine.
123 * Note that if the MIPI IRQ is enabled in
124 * the NVIC, it will fire on every SMARTDMA transfer
125 */
126 DSI_EnableInterrupts(config->base, kDSI_InterruptGroup1ApbTxDone |
127 kDSI_InterruptGroup1HtxTo, 0U);
128 /* Trigger DMA engine */
129 ret = dma_start(config->smart_dma, 0);
130 if (ret < 0) {
131 LOG_ERR("Could not start SMARTDMA");
132 return ret;
133 }
134 /* Wait for TX completion */
135 k_sem_take(&data->transfer_sem, K_FOREVER);
136 return msg->tx_len;
137 }
138
139 #else /* CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA is not set */
140
141 /* 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)142 static void dsi_transfer_complete(MIPI_DSI_HOST_Type *base,
143 dsi_handle_t *handle, status_t status, void *userData)
144 {
145 struct mcux_mipi_dsi_data *data = userData;
146
147 k_sem_give(&data->transfer_sem);
148 }
149
150
151 /* 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)152 static int dsi_mcux_tx_color(const struct device *dev, uint8_t channel,
153 struct mipi_dsi_msg *msg)
154 {
155 const struct mcux_mipi_dsi_config *config = dev->config;
156 struct mcux_mipi_dsi_data *data = dev->data;
157 status_t status;
158 dsi_transfer_t xfer = {
159 .virtualChannel = channel,
160 .txData = msg->tx_buf,
161 .rxDataSize = (uint16_t)msg->rx_len,
162 .rxData = msg->rx_buf,
163 .sendDscCmd = true,
164 .dscCmd = msg->cmd,
165 .txDataType = kDSI_TxDataDcsLongWr,
166 /* default to high speed unless told to use low power */
167 .flags = (msg->flags & MIPI_DSI_MSG_USE_LPM) ? 0 : kDSI_TransferUseHighSpeed,
168 };
169
170 /*
171 * Cap transfer size. Note that we subtract six bytes here,
172 * one for the DSC command and five to insure that
173 * transfers are still aligned on a pixel boundary
174 * (two or three byte pixel sizes are supported).
175 */
176 xfer.txDataSize = MIN(msg->tx_len, (DSI_TX_MAX_PAYLOAD_BYTE - 6));
177
178 if (IS_ENABLED(CONFIG_MIPI_DSI_MCUX_2L_SWAP16)) {
179 /* Manually swap the 16 byte color data in software */
180 uint8_t *src = (uint8_t *)xfer.txData;
181 uint8_t tmp;
182
183 for (uint32_t i = 0; i < xfer.txDataSize; i += 2) {
184 tmp = src[i];
185 src[i] = src[i + 1];
186 src[i + 1] = tmp;
187 }
188 }
189 /* Send TX data using non-blocking DSI API */
190 status = DSI_TransferNonBlocking(config->base,
191 &data->mipi_handle, &xfer);
192 /* Wait for transfer completion */
193 k_sem_take(&data->transfer_sem, K_FOREVER);
194 if (status != kStatus_Success) {
195 LOG_ERR("Transmission failed");
196 return -EIO;
197 }
198 return xfer.txDataSize;
199 }
200
201 /* ISR is used for DSI interrupt based implementation, unnecessary if DMA is used */
mipi_dsi_isr(const struct device * dev)202 static int mipi_dsi_isr(const struct device *dev)
203 {
204 const struct mcux_mipi_dsi_config *config = dev->config;
205 struct mcux_mipi_dsi_data *data = dev->data;
206
207 DSI_TransferHandleIRQ(config->base, &data->mipi_handle);
208 return 0;
209 }
210
211 #endif
212
dsi_mcux_attach(const struct device * dev,uint8_t channel,const struct mipi_dsi_device * mdev)213 static int dsi_mcux_attach(const struct device *dev,
214 uint8_t channel,
215 const struct mipi_dsi_device *mdev)
216 {
217 const struct mcux_mipi_dsi_config *config = dev->config;
218 dsi_dphy_config_t dphy_config;
219 dsi_config_t dsi_config;
220 uint32_t dphy_bit_clk_freq;
221 uint32_t dphy_esc_clk_freq;
222 uint32_t dsi_pixel_clk_freq;
223 uint32_t bit_width;
224
225 DSI_GetDefaultConfig(&dsi_config);
226 dsi_config.numLanes = mdev->data_lanes;
227 dsi_config.autoInsertEoTp = config->auto_insert_eotp;
228 dsi_config.enableNonContinuousHsClk = config->noncontinuous_hs_clk;
229
230 imxrt_pre_init_display_interface();
231
232 /* Init the DSI module. */
233 DSI_Init(config->base, &dsi_config);
234
235 #ifdef CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA
236 /* Connect DSI IRQ line to SMARTDMA trigger via
237 * INPUTMUX.
238 */
239 /* Attach INPUTMUX from MIPI to SMARTDMA */
240 INPUTMUX_Init(INPUTMUX);
241 INPUTMUX_AttachSignal(INPUTMUX, 0, kINPUTMUX_MipiIrqToSmartDmaInput);
242 /* Gate inputmux clock to save power */
243 INPUTMUX_Deinit(INPUTMUX);
244
245 if (!device_is_ready(config->smart_dma)) {
246 return -ENODEV;
247 }
248
249 struct mcux_mipi_dsi_data *data = dev->data;
250
251 switch (mdev->pixfmt) {
252 case MIPI_DSI_PIXFMT_RGB888:
253 data->dma_slot = DMA_SMARTDMA_MIPI_RGB888_DMA;
254 break;
255 case MIPI_DSI_PIXFMT_RGB565:
256 if (IS_ENABLED(CONFIG_MIPI_DSI_MCUX_2L_SWAP16)) {
257 data->dma_slot = DMA_SMARTDMA_MIPI_RGB565_DMA_SWAP;
258 } else {
259 data->dma_slot = DMA_SMARTDMA_MIPI_RGB565_DMA;
260 }
261 break;
262 default:
263 LOG_ERR("SMARTDMA does not support pixel_format %u",
264 mdev->pixfmt);
265 return -ENODEV;
266 }
267 #else
268 struct mcux_mipi_dsi_data *data = dev->data;
269
270 /* Create transfer handle */
271 if (DSI_TransferCreateHandle(config->base, &data->mipi_handle,
272 dsi_transfer_complete, data) != kStatus_Success) {
273 return -ENODEV;
274 }
275 #endif
276
277 /* Get the DPHY bit clock frequency */
278 if (clock_control_get_rate(config->bit_clk_dev,
279 config->bit_clk_subsys,
280 &dphy_bit_clk_freq)) {
281 return -EINVAL;
282 };
283 /* Get the DPHY ESC clock frequency */
284 if (clock_control_get_rate(config->esc_clk_dev,
285 config->esc_clk_subsys,
286 &dphy_esc_clk_freq)) {
287 return -EINVAL;
288 }
289 /* Get the Pixel clock frequency */
290 if (clock_control_get_rate(config->pixel_clk_dev,
291 config->pixel_clk_subsys,
292 &dsi_pixel_clk_freq)) {
293 return -EINVAL;
294 }
295
296 switch (config->dpi_config.pixelPacket) {
297 case kDSI_PixelPacket16Bit:
298 bit_width = 16;
299 break;
300 case kDSI_PixelPacket18Bit:
301 __fallthrough;
302 case kDSI_PixelPacket18BitLoosely:
303 bit_width = 18;
304 break;
305 case kDSI_PixelPacket24Bit:
306 bit_width = 24;
307 break;
308 default:
309 return -EINVAL; /* Invalid bit width enum value? */
310 }
311 /* Init DPHY.
312 *
313 * The DPHY bit clock must be fast enough to send out the pixels, it should be
314 * larger than:
315 *
316 * (Pixel clock * bit per output pixel) / number of MIPI data lane
317 */
318 if (((dsi_pixel_clk_freq * bit_width) / mdev->data_lanes) > dphy_bit_clk_freq) {
319 return -EINVAL;
320 }
321
322 DSI_GetDphyDefaultConfig(&dphy_config, dphy_bit_clk_freq, dphy_esc_clk_freq);
323
324 if (config->dphy_ref_freq != 0) {
325 dphy_bit_clk_freq = DSI_InitDphy(config->base,
326 &dphy_config, config->dphy_ref_freq);
327 } else {
328 /* DPHY PLL is not present, ref clock is unused */
329 DSI_InitDphy(config->base, &dphy_config, 0);
330 }
331
332 /*
333 * If nxp,lcdif node is present, then the MIPI DSI driver will
334 * accept input on the DPI port from the LCDIF, and convert the output
335 * to DSI data. This is useful for video mode, where the LCDIF can
336 * constantly refresh the MIPI panel.
337 */
338 if (mdev->mode_flags & MIPI_DSI_MODE_VIDEO) {
339 /* Init DPI interface. */
340 DSI_SetDpiConfig(config->base, &config->dpi_config, mdev->data_lanes,
341 dsi_pixel_clk_freq, dphy_bit_clk_freq);
342 }
343
344 imxrt_post_init_display_interface();
345
346 return 0;
347 }
348
dsi_mcux_detach(const struct device * dev,uint8_t channel,const struct mipi_dsi_device * mdev)349 static int dsi_mcux_detach(const struct device *dev, uint8_t channel,
350 const struct mipi_dsi_device *mdev)
351 {
352 const struct mcux_mipi_dsi_config *config = dev->config;
353
354 /* Enable DPHY auto power down */
355 DSI_DeinitDphy(config->base);
356 /* Fully power off DPHY */
357 config->base->PD_DPHY = 0x1;
358 /* Deinit MIPI */
359 DSI_Deinit(config->base);
360 /* Call IMX RT clock function to gate clocks and power at SOC level */
361 imxrt_deinit_display_interface();
362 return 0;
363 }
364
365
366
dsi_mcux_transfer(const struct device * dev,uint8_t channel,struct mipi_dsi_msg * msg)367 static ssize_t dsi_mcux_transfer(const struct device *dev, uint8_t channel,
368 struct mipi_dsi_msg *msg)
369 {
370 const struct mcux_mipi_dsi_config *config = dev->config;
371 dsi_transfer_t dsi_xfer = {0};
372 status_t status;
373 int ret;
374
375 dsi_xfer.virtualChannel = channel;
376 dsi_xfer.txDataSize = msg->tx_len;
377 dsi_xfer.txData = msg->tx_buf;
378 dsi_xfer.rxDataSize = msg->rx_len;
379 dsi_xfer.rxData = msg->rx_buf;
380 /* default to high speed unless told to use low power */
381 dsi_xfer.flags = (msg->flags & MIPI_DSI_MSG_USE_LPM) ? 0 : kDSI_TransferUseHighSpeed;
382
383 switch (msg->type) {
384 case MIPI_DSI_DCS_READ:
385 LOG_ERR("DCS Read not yet implemented or used");
386 return -ENOTSUP;
387 case MIPI_DSI_DCS_SHORT_WRITE:
388 dsi_xfer.sendDscCmd = true;
389 dsi_xfer.dscCmd = msg->cmd;
390 dsi_xfer.txDataType = kDSI_TxDataDcsShortWrNoParam;
391 break;
392 case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
393 dsi_xfer.sendDscCmd = true;
394 dsi_xfer.dscCmd = msg->cmd;
395 dsi_xfer.txDataType = kDSI_TxDataDcsShortWrOneParam;
396 break;
397 case MIPI_DSI_DCS_LONG_WRITE:
398 dsi_xfer.sendDscCmd = true;
399 dsi_xfer.dscCmd = msg->cmd;
400 dsi_xfer.txDataType = kDSI_TxDataDcsLongWr;
401 if (msg->flags & MCUX_DSI_2L_FB_DATA) {
402 /*
403 * Special case- transfer framebuffer data using
404 * SMARTDMA or non blocking DSI API. The framebuffer
405 * will also be color swapped, if enabled.
406 */
407 ret = dsi_mcux_tx_color(dev, channel, msg);
408 if (ret < 0) {
409 LOG_ERR("Transmission failed");
410 return -EIO;
411 }
412 return ret;
413 }
414 break;
415 case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
416 dsi_xfer.txDataType = kDSI_TxDataGenShortWrNoParam;
417 break;
418 case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
419 dsi_xfer.txDataType = kDSI_TxDataGenShortWrOneParam;
420 break;
421 case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
422 dsi_xfer.txDataType = kDSI_TxDataGenShortWrTwoParam;
423 break;
424 case MIPI_DSI_GENERIC_LONG_WRITE:
425 dsi_xfer.txDataType = kDSI_TxDataGenLongWr;
426 break;
427 case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
428 __fallthrough;
429 case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
430 __fallthrough;
431 case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
432 LOG_ERR("Generic Read not yet implemented or used");
433 return -ENOTSUP;
434 default:
435 LOG_ERR("Unsupported message type (%d)", msg->type);
436 return -ENOTSUP;
437 }
438
439 status = DSI_TransferBlocking(config->base, &dsi_xfer);
440 if (status != kStatus_Success) {
441 LOG_ERR("Transmission failed");
442 return -EIO;
443 }
444
445 if (msg->rx_len != 0) {
446 /* Return rx_len on a read */
447 return msg->rx_len;
448 }
449
450 /* Return tx_len on a write */
451 return msg->tx_len;
452
453 }
454
455 static struct mipi_dsi_driver_api dsi_mcux_api = {
456 .attach = dsi_mcux_attach,
457 .detach = dsi_mcux_detach,
458 .transfer = dsi_mcux_transfer,
459 };
460
mcux_mipi_dsi_init(const struct device * dev)461 static int mcux_mipi_dsi_init(const struct device *dev)
462 {
463 const struct mcux_mipi_dsi_config *config = dev->config;
464 struct mcux_mipi_dsi_data *data = dev->data;
465
466 #ifndef CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA
467 /* Enable IRQ */
468 config->irq_config_func(dev);
469 #endif
470
471 k_sem_init(&data->transfer_sem, 0, 1);
472
473 if (!device_is_ready(config->bit_clk_dev) ||
474 !device_is_ready(config->esc_clk_dev) ||
475 !device_is_ready(config->pixel_clk_dev)) {
476 return -ENODEV;
477 }
478 return 0;
479 }
480
481 #define MCUX_DSI_DPI_CONFIG(id) \
482 IF_ENABLED(DT_NODE_HAS_PROP(DT_DRV_INST(id), nxp_lcdif), \
483 (.dpi_config = { \
484 .dpiColorCoding = DT_INST_ENUM_IDX(id, dpi_color_coding), \
485 .pixelPacket = DT_INST_ENUM_IDX(id, dpi_pixel_packet), \
486 .videoMode = DT_INST_ENUM_IDX(id, dpi_video_mode), \
487 .bllpMode = DT_INST_ENUM_IDX(id, dpi_bllp_mode), \
488 .pixelPayloadSize = DT_INST_PROP_BY_PHANDLE(id, nxp_lcdif, width), \
489 .panelHeight = DT_INST_PROP_BY_PHANDLE(id, nxp_lcdif, height), \
490 .polarityFlags = (DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif), \
491 display_timings), vsync_active) ? \
492 kDSI_DpiVsyncActiveHigh : \
493 kDSI_DpiVsyncActiveLow) | \
494 (DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif), \
495 display_timings), hsync_active) ? \
496 kDSI_DpiHsyncActiveHigh : \
497 kDSI_DpiHsyncActiveLow), \
498 .hfp = DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif), \
499 display_timings), hfront_porch), \
500 .hbp = DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif), \
501 display_timings), hback_porch), \
502 .hsw = DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif), \
503 display_timings), hsync_len), \
504 .vfp = DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif), \
505 display_timings), vfront_porch), \
506 .vbp = DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif), \
507 display_timings), vback_porch), \
508 },))
509
510 #define MCUX_MIPI_DSI_DEVICE(id) \
511 COND_CODE_1(CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA, \
512 (), (static void mipi_dsi_##n##_irq_config_func(const struct device *dev) \
513 { \
514 IRQ_CONNECT(DT_INST_IRQN(id), DT_INST_IRQ(id, priority), \
515 mipi_dsi_isr, DEVICE_DT_INST_GET(id), 0); \
516 irq_enable(DT_INST_IRQN(id)); \
517 })) \
518 \
519 static const struct mcux_mipi_dsi_config mipi_dsi_config_##id = { \
520 MCUX_DSI_DPI_CONFIG(id) \
521 COND_CODE_1(CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA, \
522 (.smart_dma = DEVICE_DT_GET(DT_INST_DMAS_CTLR_BY_NAME(id, smartdma)),), \
523 (.irq_config_func = mipi_dsi_##n##_irq_config_func,)) \
524 .base = (MIPI_DSI_HOST_Type *)DT_INST_REG_ADDR(id), \
525 .auto_insert_eotp = DT_INST_PROP(id, autoinsert_eotp), \
526 .noncontinuous_hs_clk = DT_INST_PROP(id, noncontinuous_hs_clk), \
527 .dphy_ref_freq = DT_INST_PROP_OR(id, dphy_ref_frequency, 0), \
528 .bit_clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_NAME(id, dphy)), \
529 .bit_clk_subsys = \
530 (clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_NAME(id, dphy, name), \
531 .esc_clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_NAME(id, esc)), \
532 .esc_clk_subsys = \
533 (clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_NAME(id, esc, name), \
534 .pixel_clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_NAME(id, pixel)), \
535 .pixel_clk_subsys = \
536 (clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_NAME(id, pixel, name), \
537 }; \
538 \
539 static struct mcux_mipi_dsi_data mipi_dsi_data_##id; \
540 DEVICE_DT_INST_DEFINE(id, \
541 &mcux_mipi_dsi_init, \
542 NULL, \
543 &mipi_dsi_data_##id, \
544 &mipi_dsi_config_##id, \
545 POST_KERNEL, \
546 CONFIG_MIPI_DSI_INIT_PRIORITY, \
547 &dsi_mcux_api);
548
549 DT_INST_FOREACH_STATUS_OKAY(MCUX_MIPI_DSI_DEVICE)
550