550 lines
16 KiB
C
550 lines
16 KiB
C
/*
|
|
* 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>
|
|
|
|
#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
|
|
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 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};
|
|
struct dma_block_config block = {0};
|
|
int ret;
|
|
|
|
if (channel != 0) {
|
|
return -ENOTSUP; /* DMA can only transfer on virtual channel 0 */
|
|
}
|
|
|
|
/* Configure smartDMA device, and run transfer */
|
|
block.source_address = (uint32_t)msg->tx_buf;
|
|
block.block_size = msg->tx_len;
|
|
|
|
dma_cfg.dma_callback = dsi_mcux_dma_cb;
|
|
dma_cfg.user_data = (struct device *)dev;
|
|
dma_cfg.head_block = █
|
|
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 = DMA_SMARTDMA_MIPI_RGB888_DMA;
|
|
break;
|
|
case MIPI_DSI_PIXFMT_RGB565:
|
|
if (IS_ENABLED(CONFIG_MIPI_DSI_MCUX_2L_SWAP16)) {
|
|
data->dma_slot = DMA_SMARTDMA_MIPI_RGB565_DMA_SWAP;
|
|
} else {
|
|
data->dma_slot = DMA_SMARTDMA_MIPI_RGB565_DMA;
|
|
}
|
|
break;
|
|
default:
|
|
LOG_ERR("SMARTDMA does not support pixel_format %u",
|
|
mdev->pixfmt);
|
|
return -ENODEV;
|
|
}
|
|
#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 struct mipi_dsi_driver_api 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)
|