|  | /* | 
|  | * Copyright 2023, NXP | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | /* Based on dsi_mcux.c, which is (c) 2022 NXP */ | 
|  |  | 
|  | #define DT_DRV_COMPAT nxp_mipi_dsi_2l | 
|  |  | 
|  | #include <zephyr/kernel.h> | 
|  | #include <zephyr/drivers/clock_control.h> | 
|  | #include <zephyr/drivers/mipi_dsi.h> | 
|  | #include <zephyr/drivers/mipi_dsi/mipi_dsi_mcux_2l.h> | 
|  | #include <zephyr/drivers/dma.h> | 
|  | #include <zephyr/drivers/dma/dma_mcux_smartdma.h> | 
|  | #include <zephyr/logging/log.h> | 
|  |  | 
|  | #include <fsl_inputmux.h> | 
|  | #include <fsl_mipi_dsi.h> | 
|  | #include <fsl_clock.h> | 
|  | #ifdef CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA | 
|  | #include <fsl_smartdma.h> | 
|  | #endif | 
|  |  | 
|  | #include <soc.h> | 
|  |  | 
|  | LOG_MODULE_REGISTER(dsi_mcux_host, CONFIG_MIPI_DSI_LOG_LEVEL); | 
|  |  | 
|  | struct mcux_mipi_dsi_config { | 
|  | MIPI_DSI_HOST_Type *base; | 
|  | dsi_dpi_config_t dpi_config; | 
|  | bool auto_insert_eotp; | 
|  | bool noncontinuous_hs_clk; | 
|  | const struct device *bit_clk_dev; | 
|  | clock_control_subsys_t bit_clk_subsys; | 
|  | const struct device *esc_clk_dev; | 
|  | clock_control_subsys_t esc_clk_subsys; | 
|  | const struct device *pixel_clk_dev; | 
|  | clock_control_subsys_t pixel_clk_subsys; | 
|  | uint32_t dphy_ref_freq; | 
|  | #ifdef CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA | 
|  | const struct device *smart_dma; | 
|  | #else | 
|  | void (*irq_config_func)(const struct device *dev); | 
|  | #endif | 
|  | }; | 
|  |  | 
|  | struct mcux_mipi_dsi_data { | 
|  | dsi_handle_t mipi_handle; | 
|  | struct k_sem transfer_sem; | 
|  | #ifdef CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA | 
|  | smartdma_dsi_param_t smartdma_params __aligned(4); | 
|  | uint32_t smartdma_stack[32]; | 
|  | uint8_t dma_slot; | 
|  | #endif | 
|  | }; | 
|  |  | 
|  |  | 
|  | /* MAX DSI TX payload */ | 
|  | #define DSI_TX_MAX_PAYLOAD_BYTE (64U * 4U) | 
|  |  | 
|  |  | 
|  | #ifdef CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA | 
|  |  | 
|  | /* Callback for DSI DMA transfer completion, called in ISR context */ | 
|  | static void dsi_mcux_dma_cb(const struct device *dma_dev, | 
|  | void *user_data, uint32_t channel, int status) | 
|  | { | 
|  | const struct device *dev = user_data; | 
|  | const struct mcux_mipi_dsi_config *config = dev->config; | 
|  | struct mcux_mipi_dsi_data *data = dev->data; | 
|  | uint32_t int_flags1, int_flags2; | 
|  |  | 
|  | if (status != 0) { | 
|  | LOG_ERR("SMARTDMA transfer failed"); | 
|  | } else { | 
|  | /* Disable DSI interrupts at transfer completion */ | 
|  | DSI_DisableInterrupts(config->base, kDSI_InterruptGroup1ApbTxDone | | 
|  | kDSI_InterruptGroup1HtxTo, 0U); | 
|  | DSI_GetAndClearInterruptStatus(config->base, &int_flags1, &int_flags2); | 
|  | k_sem_give(&data->transfer_sem); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Helper function to transfer DSI color (DMA based implementation) */ | 
|  | static int dsi_mcux_tx_color(const struct device *dev, uint8_t channel, | 
|  | struct mipi_dsi_msg *msg) | 
|  | { | 
|  | /* | 
|  | * Color streams are a special case for this DSI peripheral, because | 
|  | * the SMARTDMA peripheral (if enabled) can be used to accelerate | 
|  | * the transfer of data to the DSI. The SMARTDMA has the additional | 
|  | * advantage over traditional DMA of being able to automatically | 
|  | * byte swap color data. This is advantageous, as most graphical | 
|  | * frameworks store RGB data in little endian format, but many | 
|  | * MIPI displays expect color data in big endian format. | 
|  | */ | 
|  | const struct mcux_mipi_dsi_config *config = dev->config; | 
|  | struct mcux_mipi_dsi_data *data = dev->data; | 
|  | struct dma_config dma_cfg = {0}; | 
|  | int ret; | 
|  |  | 
|  | if (channel != 0) { | 
|  | return -ENOTSUP; /* DMA can only transfer on virtual channel 0 */ | 
|  | } | 
|  |  | 
|  | /* Configure smartDMA device, and run transfer */ | 
|  | data->smartdma_params.p_buffer = msg->tx_buf; | 
|  | data->smartdma_params.buffersize = msg->tx_len; | 
|  |  | 
|  | dma_cfg.dma_callback = dsi_mcux_dma_cb; | 
|  | dma_cfg.user_data = (struct device *)dev; | 
|  | dma_cfg.head_block = (struct dma_block_config *)&data->smartdma_params; | 
|  | dma_cfg.block_count = 1; | 
|  | dma_cfg.dma_slot = data->dma_slot; | 
|  | dma_cfg.channel_direction = MEMORY_TO_PERIPHERAL; | 
|  | ret = dma_config(config->smart_dma, 0, &dma_cfg); | 
|  | if (ret < 0) { | 
|  | LOG_ERR("Could not configure SMARTDMA"); | 
|  | return ret; | 
|  | } | 
|  | /* | 
|  | * SMARTDMA uses DSI interrupt line as input for the DMA | 
|  | * transfer trigger. Therefore, we need to enable DSI TX | 
|  | * interrupts in order to trigger the DMA engine. | 
|  | * Note that if the MIPI IRQ is enabled in | 
|  | * the NVIC, it will fire on every SMARTDMA transfer | 
|  | */ | 
|  | DSI_EnableInterrupts(config->base, kDSI_InterruptGroup1ApbTxDone | | 
|  | kDSI_InterruptGroup1HtxTo, 0U); | 
|  | /* Trigger DMA engine */ | 
|  | ret = dma_start(config->smart_dma, 0); | 
|  | if (ret < 0) { | 
|  | LOG_ERR("Could not start SMARTDMA"); | 
|  | return ret; | 
|  | } | 
|  | /* Wait for TX completion */ | 
|  | k_sem_take(&data->transfer_sem, K_FOREVER); | 
|  | return msg->tx_len; | 
|  | } | 
|  |  | 
|  | #else /* CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA is not set */ | 
|  |  | 
|  | /* Callback for DSI transfer completion, called in ISR context */ | 
|  | static void dsi_transfer_complete(MIPI_DSI_HOST_Type *base, | 
|  | dsi_handle_t *handle, status_t status, void *userData) | 
|  | { | 
|  | struct mcux_mipi_dsi_data *data = userData; | 
|  |  | 
|  | k_sem_give(&data->transfer_sem); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Helper function to transfer DSI color (Interrupt based implementation) */ | 
|  | static int dsi_mcux_tx_color(const struct device *dev, uint8_t channel, | 
|  | struct mipi_dsi_msg *msg) | 
|  | { | 
|  | const struct mcux_mipi_dsi_config *config = dev->config; | 
|  | struct mcux_mipi_dsi_data *data = dev->data; | 
|  | status_t status; | 
|  | dsi_transfer_t xfer = { | 
|  | .virtualChannel = channel, | 
|  | .txData = msg->tx_buf, | 
|  | .rxDataSize = (uint16_t)msg->rx_len, | 
|  | .rxData = msg->rx_buf, | 
|  | .sendDscCmd = true, | 
|  | .dscCmd = msg->cmd, | 
|  | .txDataType = kDSI_TxDataDcsLongWr, | 
|  | /* default to high speed unless told to use low power */ | 
|  | .flags = (msg->flags & MIPI_DSI_MSG_USE_LPM) ? 0 : kDSI_TransferUseHighSpeed, | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * Cap transfer size. Note that we subtract six bytes here, | 
|  | * one for the DSC command and five to insure that | 
|  | * transfers are still aligned on a pixel boundary | 
|  | * (two or three byte pixel sizes are supported). | 
|  | */ | 
|  | xfer.txDataSize = MIN(msg->tx_len, (DSI_TX_MAX_PAYLOAD_BYTE - 6)); | 
|  |  | 
|  | if (IS_ENABLED(CONFIG_MIPI_DSI_MCUX_2L_SWAP16)) { | 
|  | /* Manually swap the 16 byte color data in software */ | 
|  | uint8_t *src = (uint8_t *)xfer.txData; | 
|  | uint8_t tmp; | 
|  |  | 
|  | for (uint32_t i = 0; i < xfer.txDataSize; i += 2) { | 
|  | tmp = src[i]; | 
|  | src[i] = src[i + 1]; | 
|  | src[i + 1] = tmp; | 
|  | } | 
|  | } | 
|  | /* Send TX data using non-blocking DSI API */ | 
|  | status = DSI_TransferNonBlocking(config->base, | 
|  | &data->mipi_handle, &xfer); | 
|  | /* Wait for transfer completion */ | 
|  | k_sem_take(&data->transfer_sem, K_FOREVER); | 
|  | if (status != kStatus_Success) { | 
|  | LOG_ERR("Transmission failed"); | 
|  | return -EIO; | 
|  | } | 
|  | return xfer.txDataSize; | 
|  | } | 
|  |  | 
|  | /* ISR is used for DSI interrupt based implementation, unnecessary if DMA is used */ | 
|  | static int mipi_dsi_isr(const struct device *dev) | 
|  | { | 
|  | const struct mcux_mipi_dsi_config *config = dev->config; | 
|  | struct mcux_mipi_dsi_data *data = dev->data; | 
|  |  | 
|  | DSI_TransferHandleIRQ(config->base, &data->mipi_handle); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #endif | 
|  |  | 
|  | static int dsi_mcux_attach(const struct device *dev, | 
|  | uint8_t channel, | 
|  | const struct mipi_dsi_device *mdev) | 
|  | { | 
|  | const struct mcux_mipi_dsi_config *config = dev->config; | 
|  | dsi_dphy_config_t dphy_config; | 
|  | dsi_config_t dsi_config; | 
|  | uint32_t dphy_bit_clk_freq; | 
|  | uint32_t dphy_esc_clk_freq; | 
|  | uint32_t dsi_pixel_clk_freq; | 
|  | uint32_t bit_width; | 
|  |  | 
|  | DSI_GetDefaultConfig(&dsi_config); | 
|  | dsi_config.numLanes = mdev->data_lanes; | 
|  | dsi_config.autoInsertEoTp = config->auto_insert_eotp; | 
|  | dsi_config.enableNonContinuousHsClk = config->noncontinuous_hs_clk; | 
|  |  | 
|  | imxrt_pre_init_display_interface(); | 
|  |  | 
|  | /* Init the DSI module. */ | 
|  | DSI_Init(config->base, &dsi_config); | 
|  |  | 
|  | #ifdef CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA | 
|  | /* Connect DSI IRQ line to SMARTDMA trigger via | 
|  | * INPUTMUX. | 
|  | */ | 
|  | /* Attach INPUTMUX from MIPI to SMARTDMA */ | 
|  | INPUTMUX_Init(INPUTMUX); | 
|  | INPUTMUX_AttachSignal(INPUTMUX, 0, kINPUTMUX_MipiIrqToSmartDmaInput); | 
|  | /* Gate inputmux clock to save power */ | 
|  | INPUTMUX_Deinit(INPUTMUX); | 
|  |  | 
|  | if (!device_is_ready(config->smart_dma)) { | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | struct mcux_mipi_dsi_data *data = dev->data; | 
|  |  | 
|  | switch (mdev->pixfmt) { | 
|  | case MIPI_DSI_PIXFMT_RGB888: | 
|  | data->dma_slot = kSMARTDMA_MIPI_RGB888_DMA; | 
|  | data->smartdma_params.disablePixelByteSwap = true; | 
|  | break; | 
|  | case MIPI_DSI_PIXFMT_RGB565: | 
|  | data->dma_slot = kSMARTDMA_MIPI_RGB565_DMA; | 
|  | if (IS_ENABLED(CONFIG_MIPI_DSI_MCUX_2L_SWAP16)) { | 
|  | data->smartdma_params.disablePixelByteSwap = false; | 
|  | } else { | 
|  | data->smartdma_params.disablePixelByteSwap = true; | 
|  | } | 
|  | break; | 
|  | default: | 
|  | LOG_ERR("SMARTDMA does not support pixel_format %u", | 
|  | mdev->pixfmt); | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | data->smartdma_params.smartdma_stack = data->smartdma_stack; | 
|  |  | 
|  | dma_smartdma_install_fw(config->smart_dma, | 
|  | (uint8_t *)s_smartdmaDisplayFirmware, | 
|  | s_smartdmaDisplayFirmwareSize); | 
|  | #else | 
|  | struct mcux_mipi_dsi_data *data = dev->data; | 
|  |  | 
|  | /* Create transfer handle */ | 
|  | if (DSI_TransferCreateHandle(config->base, &data->mipi_handle, | 
|  | dsi_transfer_complete, data) != kStatus_Success) { | 
|  | return -ENODEV; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* Get the DPHY bit clock frequency */ | 
|  | if (clock_control_get_rate(config->bit_clk_dev, | 
|  | config->bit_clk_subsys, | 
|  | &dphy_bit_clk_freq)) { | 
|  | return -EINVAL; | 
|  | }; | 
|  | /* Get the DPHY ESC clock frequency */ | 
|  | if (clock_control_get_rate(config->esc_clk_dev, | 
|  | config->esc_clk_subsys, | 
|  | &dphy_esc_clk_freq)) { | 
|  | return -EINVAL; | 
|  | } | 
|  | /* Get the Pixel clock frequency */ | 
|  | if (clock_control_get_rate(config->pixel_clk_dev, | 
|  | config->pixel_clk_subsys, | 
|  | &dsi_pixel_clk_freq)) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | switch (config->dpi_config.pixelPacket) { | 
|  | case kDSI_PixelPacket16Bit: | 
|  | bit_width = 16; | 
|  | break; | 
|  | case kDSI_PixelPacket18Bit: | 
|  | __fallthrough; | 
|  | case kDSI_PixelPacket18BitLoosely: | 
|  | bit_width = 18; | 
|  | break; | 
|  | case kDSI_PixelPacket24Bit: | 
|  | bit_width = 24; | 
|  | break; | 
|  | default: | 
|  | return -EINVAL; /* Invalid bit width enum value? */ | 
|  | } | 
|  | /* Init DPHY. | 
|  | * | 
|  | * The DPHY bit clock must be fast enough to send out the pixels, it should be | 
|  | * larger than: | 
|  | * | 
|  | *         (Pixel clock * bit per output pixel) / number of MIPI data lane | 
|  | */ | 
|  | if (((dsi_pixel_clk_freq * bit_width) / mdev->data_lanes) > dphy_bit_clk_freq) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | DSI_GetDphyDefaultConfig(&dphy_config, dphy_bit_clk_freq, dphy_esc_clk_freq); | 
|  |  | 
|  | if (config->dphy_ref_freq != 0) { | 
|  | dphy_bit_clk_freq = DSI_InitDphy(config->base, | 
|  | &dphy_config, config->dphy_ref_freq); | 
|  | } else { | 
|  | /* DPHY PLL is not present, ref clock is unused */ | 
|  | DSI_InitDphy(config->base, &dphy_config, 0); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * If nxp,lcdif node is present, then the MIPI DSI driver will | 
|  | * accept input on the DPI port from the LCDIF, and convert the output | 
|  | * to DSI data. This is useful for video mode, where the LCDIF can | 
|  | * constantly refresh the MIPI panel. | 
|  | */ | 
|  | if (mdev->mode_flags & MIPI_DSI_MODE_VIDEO) { | 
|  | /* Init DPI interface. */ | 
|  | DSI_SetDpiConfig(config->base, &config->dpi_config, mdev->data_lanes, | 
|  | dsi_pixel_clk_freq, dphy_bit_clk_freq); | 
|  | } | 
|  |  | 
|  | imxrt_post_init_display_interface(); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int dsi_mcux_detach(const struct device *dev, uint8_t channel, | 
|  | const struct mipi_dsi_device *mdev) | 
|  | { | 
|  | const struct mcux_mipi_dsi_config *config = dev->config; | 
|  |  | 
|  | /* Enable DPHY auto power down */ | 
|  | DSI_DeinitDphy(config->base); | 
|  | /* Fully power off DPHY */ | 
|  | config->base->PD_DPHY = 0x1; | 
|  | /* Deinit MIPI */ | 
|  | DSI_Deinit(config->base); | 
|  | /* Call IMX RT clock function to gate clocks and power at SOC level */ | 
|  | imxrt_deinit_display_interface(); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | static ssize_t dsi_mcux_transfer(const struct device *dev, uint8_t channel, | 
|  | struct mipi_dsi_msg *msg) | 
|  | { | 
|  | const struct mcux_mipi_dsi_config *config = dev->config; | 
|  | dsi_transfer_t dsi_xfer = {0}; | 
|  | status_t status; | 
|  | int ret; | 
|  |  | 
|  | dsi_xfer.virtualChannel = channel; | 
|  | dsi_xfer.txDataSize = msg->tx_len; | 
|  | dsi_xfer.txData = msg->tx_buf; | 
|  | dsi_xfer.rxDataSize = msg->rx_len; | 
|  | dsi_xfer.rxData = msg->rx_buf; | 
|  | /* default to high speed unless told to use low power */ | 
|  | dsi_xfer.flags = (msg->flags & MIPI_DSI_MSG_USE_LPM) ? 0 : kDSI_TransferUseHighSpeed; | 
|  |  | 
|  | switch (msg->type) { | 
|  | case MIPI_DSI_DCS_READ: | 
|  | LOG_ERR("DCS Read not yet implemented or used"); | 
|  | return -ENOTSUP; | 
|  | case MIPI_DSI_DCS_SHORT_WRITE: | 
|  | dsi_xfer.sendDscCmd = true; | 
|  | dsi_xfer.dscCmd = msg->cmd; | 
|  | dsi_xfer.txDataType = kDSI_TxDataDcsShortWrNoParam; | 
|  | break; | 
|  | case MIPI_DSI_DCS_SHORT_WRITE_PARAM: | 
|  | dsi_xfer.sendDscCmd = true; | 
|  | dsi_xfer.dscCmd = msg->cmd; | 
|  | dsi_xfer.txDataType = kDSI_TxDataDcsShortWrOneParam; | 
|  | break; | 
|  | case MIPI_DSI_DCS_LONG_WRITE: | 
|  | dsi_xfer.sendDscCmd = true; | 
|  | dsi_xfer.dscCmd = msg->cmd; | 
|  | dsi_xfer.txDataType = kDSI_TxDataDcsLongWr; | 
|  | if (msg->flags & MCUX_DSI_2L_FB_DATA) { | 
|  | /* | 
|  | * Special case- transfer framebuffer data using | 
|  | * SMARTDMA or non blocking DSI API. The framebuffer | 
|  | * will also be color swapped, if enabled. | 
|  | */ | 
|  | ret = dsi_mcux_tx_color(dev, channel, msg); | 
|  | if (ret < 0) { | 
|  | LOG_ERR("Transmission failed"); | 
|  | return -EIO; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  | break; | 
|  | case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM: | 
|  | dsi_xfer.txDataType = kDSI_TxDataGenShortWrNoParam; | 
|  | break; | 
|  | case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM: | 
|  | dsi_xfer.txDataType = kDSI_TxDataGenShortWrOneParam; | 
|  | break; | 
|  | case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM: | 
|  | dsi_xfer.txDataType = kDSI_TxDataGenShortWrTwoParam; | 
|  | break; | 
|  | case MIPI_DSI_GENERIC_LONG_WRITE: | 
|  | dsi_xfer.txDataType = kDSI_TxDataGenLongWr; | 
|  | break; | 
|  | case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM: | 
|  | __fallthrough; | 
|  | case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM: | 
|  | __fallthrough; | 
|  | case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM: | 
|  | LOG_ERR("Generic Read not yet implemented or used"); | 
|  | return -ENOTSUP; | 
|  | default: | 
|  | LOG_ERR("Unsupported message type (%d)", msg->type); | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | status = DSI_TransferBlocking(config->base, &dsi_xfer); | 
|  | if (status != kStatus_Success) { | 
|  | LOG_ERR("Transmission failed"); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | if (msg->rx_len != 0) { | 
|  | /* Return rx_len on a read */ | 
|  | return msg->rx_len; | 
|  | } | 
|  |  | 
|  | /* Return tx_len on a write */ | 
|  | return msg->tx_len; | 
|  |  | 
|  | } | 
|  |  | 
|  | static DEVICE_API(mipi_dsi, dsi_mcux_api) = { | 
|  | .attach = dsi_mcux_attach, | 
|  | .detach = dsi_mcux_detach, | 
|  | .transfer = dsi_mcux_transfer, | 
|  | }; | 
|  |  | 
|  | static int mcux_mipi_dsi_init(const struct device *dev) | 
|  | { | 
|  | const struct mcux_mipi_dsi_config *config = dev->config; | 
|  | struct mcux_mipi_dsi_data *data = dev->data; | 
|  |  | 
|  | #ifndef CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA | 
|  | /* Enable IRQ */ | 
|  | config->irq_config_func(dev); | 
|  | #endif | 
|  |  | 
|  | k_sem_init(&data->transfer_sem, 0, 1); | 
|  |  | 
|  | if (!device_is_ready(config->bit_clk_dev) || | 
|  | !device_is_ready(config->esc_clk_dev) || | 
|  | !device_is_ready(config->pixel_clk_dev)) { | 
|  | return -ENODEV; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #define MCUX_DSI_DPI_CONFIG(id)									\ | 
|  | IF_ENABLED(DT_NODE_HAS_PROP(DT_DRV_INST(id), nxp_lcdif),				\ | 
|  | (.dpi_config = {									\ | 
|  | .dpiColorCoding = DT_INST_ENUM_IDX(id, dpi_color_coding),			\ | 
|  | .pixelPacket = DT_INST_ENUM_IDX(id, dpi_pixel_packet),				\ | 
|  | .videoMode = DT_INST_ENUM_IDX(id, dpi_video_mode),				\ | 
|  | .bllpMode = DT_INST_ENUM_IDX(id, dpi_bllp_mode),				\ | 
|  | .pixelPayloadSize = DT_INST_PROP_BY_PHANDLE(id, nxp_lcdif, width),		\ | 
|  | .panelHeight = DT_INST_PROP_BY_PHANDLE(id, nxp_lcdif, height),			\ | 
|  | .polarityFlags = (DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif),		\ | 
|  | display_timings),  vsync_active) ?			\ | 
|  | kDSI_DpiVsyncActiveHigh :				\ | 
|  | kDSI_DpiVsyncActiveLow) |				\ | 
|  | (DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif),		\ | 
|  | display_timings),  hsync_active) ?			\ | 
|  | kDSI_DpiHsyncActiveHigh :				\ | 
|  | kDSI_DpiHsyncActiveLow),				\ | 
|  | .hfp = DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif),				\ | 
|  | display_timings),  hfront_porch),			\ | 
|  | .hbp = DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif),				\ | 
|  | display_timings),  hback_porch),			\ | 
|  | .hsw = DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif),				\ | 
|  | display_timings),  hsync_len),				\ | 
|  | .vfp = DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif),				\ | 
|  | display_timings),  vfront_porch),			\ | 
|  | .vbp = DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif),				\ | 
|  | display_timings),  vback_porch),			\ | 
|  | },)) | 
|  |  | 
|  | #define MCUX_MIPI_DSI_DEVICE(id)								\ | 
|  | COND_CODE_1(CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA,						\ | 
|  | (), (static void mipi_dsi_##n##_irq_config_func(const struct device *dev)		\ | 
|  | {											\ | 
|  | IRQ_CONNECT(DT_INST_IRQN(id), DT_INST_IRQ(id, priority),			\ | 
|  | mipi_dsi_isr, DEVICE_DT_INST_GET(id), 0);				\ | 
|  | irq_enable(DT_INST_IRQN(id));						\ | 
|  | }))											\ | 
|  | \ | 
|  | static const struct mcux_mipi_dsi_config mipi_dsi_config_##id = {			\ | 
|  | MCUX_DSI_DPI_CONFIG(id)								\ | 
|  | COND_CODE_1(CONFIG_MIPI_DSI_MCUX_2L_SMARTDMA,					\ | 
|  | (.smart_dma = DEVICE_DT_GET(DT_INST_DMAS_CTLR_BY_NAME(id, smartdma)),),		\ | 
|  | (.irq_config_func = mipi_dsi_##n##_irq_config_func,))				\ | 
|  | .base = (MIPI_DSI_HOST_Type *)DT_INST_REG_ADDR(id),				\ | 
|  | .auto_insert_eotp = DT_INST_PROP(id, autoinsert_eotp),				\ | 
|  | .noncontinuous_hs_clk = DT_INST_PROP(id, noncontinuous_hs_clk),			\ | 
|  | .dphy_ref_freq = DT_INST_PROP_OR(id, dphy_ref_frequency, 0),			\ | 
|  | .bit_clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_NAME(id, dphy)),		\ | 
|  | .bit_clk_subsys =								\ | 
|  | (clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_NAME(id, dphy, name),	\ | 
|  | .esc_clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_NAME(id, esc)),		\ | 
|  | .esc_clk_subsys =								\ | 
|  | (clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_NAME(id, esc, name),	\ | 
|  | .pixel_clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_NAME(id, pixel)),		\ | 
|  | .pixel_clk_subsys =								\ | 
|  | (clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_NAME(id, pixel, name),	\ | 
|  | };											\ | 
|  | \ | 
|  | static struct mcux_mipi_dsi_data mipi_dsi_data_##id;					\ | 
|  | DEVICE_DT_INST_DEFINE(id,								\ | 
|  | &mcux_mipi_dsi_init,						\ | 
|  | NULL,								\ | 
|  | &mipi_dsi_data_##id,						\ | 
|  | &mipi_dsi_config_##id,						\ | 
|  | POST_KERNEL,							\ | 
|  | CONFIG_MIPI_DSI_INIT_PRIORITY,					\ | 
|  | &dsi_mcux_api); | 
|  |  | 
|  | DT_INST_FOREACH_STATUS_OKAY(MCUX_MIPI_DSI_DEVICE) |