diff --git a/drivers/mipi_dsi/Kconfig.mcux b/drivers/mipi_dsi/Kconfig.mcux index 17d8d437dfe..af624e6ac10 100644 --- a/drivers/mipi_dsi/Kconfig.mcux +++ b/drivers/mipi_dsi/Kconfig.mcux @@ -26,6 +26,14 @@ config MIPI_DSI_MCUX_2L_SMARTDMA convert RGB565 input data to BGR565 (little endian to big endian), and write it to the MIPI DSI. +config MIPI_DSI_MCUX_NXP_DCNANO_LCDIF + bool "Use NXP DCNano DBI controller with MIPI DSI" + default y + depends on DT_HAS_NXP_MIPI_DBI_DCNANO_LCDIF_ENABLED + select MIPI_DBI + help + Use DCNano DBI controller for the data transfer. + config MIPI_DSI_MCUX_2L_SWAP16 bool "Swap 16 byte color" help diff --git a/drivers/mipi_dsi/dsi_mcux_2l.c b/drivers/mipi_dsi/dsi_mcux_2l.c index c3ebd1ce7b7..f664974620b 100644 --- a/drivers/mipi_dsi/dsi_mcux_2l.c +++ b/drivers/mipi_dsi/dsi_mcux_2l.c @@ -22,11 +22,18 @@ #include #include #endif +#ifdef CONFIG_MIPI_DSI_MCUX_NXP_DCNANO_LCDIF +#include +#endif #include LOG_MODULE_REGISTER(dsi_mcux_host, CONFIG_MIPI_DSI_LOG_LEVEL); +#if CONFIG_MIPI_DSI_MCUX_NXP_DCNANO_LCDIF +#define MIPI_DSI_MAX_PAYLOAD_SIZE 0xFFFF +#endif + struct mcux_mipi_dsi_config { MIPI_DSI_HOST_Type *base; dsi_dpi_config_t dpi_config; @@ -44,6 +51,9 @@ struct mcux_mipi_dsi_config { #else void (*irq_config_func)(const struct device *dev); #endif +#ifdef CONFIG_MIPI_DSI_MCUX_NXP_DCNANO_LCDIF + const struct device *mipi_dbi; +#endif }; struct mcux_mipi_dsi_data { @@ -60,6 +70,11 @@ struct mcux_mipi_dsi_data { uint32_t smartdma_stack[32]; uint8_t dma_slot; #endif +#ifdef CONFIG_MIPI_DSI_MCUX_NXP_DCNANO_LCDIF + uint32_t width; + uint32_t height; + uint8_t src_bytes_per_pixel; +#endif }; @@ -196,7 +211,111 @@ static void dsi_transfer_complete(MIPI_DSI_HOST_Type *base, k_sem_give(&data->transfer_sem); } +#ifdef CONFIG_MIPI_DSI_MCUX_NXP_DCNANO_LCDIF +static status_t dsi_mcux_dcnano_transfer(const struct device *dev, uint8_t channel, + struct mipi_dsi_msg *msg, dsi_transfer_t *dsi_xfer) +{ + const struct mcux_mipi_dsi_config *config = dev->config; + struct mcux_mipi_dsi_data *data = dev->data; + uint8_t *tx_buf = (uint8_t *)msg->tx_buf; + /* Record the bpp, width, height of the buffer according to command. They are + * necessary to configure the DCNano DBI in the following memory write. + */ + if (channel == 0U) { + if (msg->cmd == MIPI_DCS_SET_PIXEL_FORMAT) { + if (tx_buf[0] == MIPI_DCS_PIXEL_FORMAT_16BIT) { + data->src_bytes_per_pixel = 2U; + } else { + data->src_bytes_per_pixel = 3U; + } + } else if (msg->cmd == MIPI_DCS_SET_COLUMN_ADDRESS) { + data->width = ((tx_buf[2] << 8U) | tx_buf[3]) - + ((tx_buf[0] << 8U) | tx_buf[1]) + 1U; + } else if (msg->cmd == MIPI_DCS_SET_PAGE_ADDRESS) { + data->height = ((tx_buf[2] << 8U) | tx_buf[3]) - + ((tx_buf[0] << 8U) | tx_buf[1]) + 1U; + } + } + + /* When the DSI channel is 0, for and only for DCS long write like + * MIPI_DCS_WRITE_MEMORY_START or MIPI_DCS_WRITE_MEMORY_CONTINUE, we can + * use DCNano DBI to speed up the frame buffer write. The DCS command and the + * following data are all handled by DCNano DBI, while on MIPI-DSI side special + * handling shall be done so that it can be properly used with DCNano DBI, + * like MIPI DBI FIFO configuration of send level and payload size, etc. + */ + if (((msg->cmd == MIPI_DCS_WRITE_MEMORY_START) || + (msg->cmd == MIPI_DCS_WRITE_MEMORY_CONTINUE)) && (channel == 0U)) { + enum display_pixel_format pixfmt; + struct mipi_dbi_config dbi_config = { + .mode = MIPI_DBI_MODE_8080_BUS_16_BIT, + }; + struct display_buffer_descriptor desc = { + .width = data->width, + .height = data->height, + .pitch = data->width, + }; + + /* Every time buffer 64 pixels first before the transfer. */ + DSI_SetDbiPixelFifoSendLevel(config->base, 64U); + + /* Set payload size. */ + if ((desc.height * desc.width * data->src_bytes_per_pixel) > + MIPI_DSI_MAX_PAYLOAD_SIZE) { + desc.height = MIPI_DSI_MAX_PAYLOAD_SIZE / + (desc.width * data->src_bytes_per_pixel); + } + + DSI_SetDbiPixelPayloadSize(config->base, + (desc.height * desc.width * data->src_bytes_per_pixel) >> 1U); + + /* Get the source buffer pixel format and DBI output format + * Currently only support RGB565 and RGB888 when using DCNano DBI. + */ + switch (data->src_bytes_per_pixel) { + case 2: + pixfmt = PIXEL_FORMAT_RGB_565; + dbi_config.mode |= MIPI_DBI_MODE_RGB565; + DSI_SetDbiPixelFormat(config->base, kDSI_DbiRGB565); + break; + case 3: + pixfmt = PIXEL_FORMAT_RGB_888; + /* If using the DBI, only RGB888 option 1 is supported. */ + dbi_config.mode |= MIPI_DBI_MODE_RGB888_1; + DSI_SetDbiPixelFormat(config->base, kDSI_DbiRGB888); + break; + default: + /* MIPI-DSI do not support other format of DBI pixel. */ + LOG_ERR("Pixel type not supported yet."); + return -EIO; + } + + /* Send the command first. */ + if (msg->cmd == MIPI_DCS_WRITE_MEMORY_START) { + mipi_dbi_command_write(config->mipi_dbi, &dbi_config, + MIPI_DCS_WRITE_MEMORY_START, NULL, 0); + } else { + mipi_dbi_command_write(config->mipi_dbi, &dbi_config, + MIPI_DCS_WRITE_MEMORY_CONTINUE, NULL, 0); + } + + /* Send the frame buffer data. */ + mipi_dbi_write_display(config->mipi_dbi, &dbi_config, msg->tx_buf, + &desc, pixfmt); + + msg->tx_len = desc.height * desc.width * data->src_bytes_per_pixel; + } else { + /* For other DCS commands, the DCNano DBI cannot be used. */ + if (DSI_TransferBlocking(config->base, dsi_xfer) != kStatus_Success) { + LOG_ERR("Transmission failed"); + return -EIO; + } + } + + return 0; +} +#else /* 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) @@ -246,6 +365,7 @@ static int dsi_mcux_tx_color(const struct device *dev, uint8_t channel, } return xfer.txDataSize; } +#endif /* ISR is used for DSI interrupt based implementation, unnecessary if DMA is used */ static int mipi_dsi_isr(const struct device *dev) @@ -479,11 +599,16 @@ static int dsi_mcux_detach(const struct device *dev, uint8_t channel, static ssize_t dsi_mcux_transfer(const struct device *dev, uint8_t channel, struct mipi_dsi_msg *msg) { +#if DT_PROP(DT_NODELABEL(mipi_dsi), ulps_control) + struct mcux_mipi_dsi_data *data = dev->data; +#endif + +#ifndef CONFIG_MIPI_DSI_MCUX_NXP_DCNANO_LCDIF const struct mcux_mipi_dsi_config *config = dev->config; +#endif dsi_transfer_t dsi_xfer = {0}; status_t status; - int ret; dsi_xfer.virtualChannel = channel; dsi_xfer.txDataSize = msg->tx_len; @@ -494,8 +619,6 @@ static ssize_t dsi_mcux_transfer(const struct device *dev, uint8_t channel, dsi_xfer.flags = (msg->flags & MIPI_DSI_MSG_USE_LPM) ? 0 : kDSI_TransferUseHighSpeed; #if DT_PROP(DT_NODELABEL(mipi_dsi), ulps_control) - struct mcux_mipi_dsi_data *data = dev->data; - data->flags = (msg->flags & MIPI_DSI_MSG_USE_LPM) ? MIPI_DSI_MSG_USE_LPM : 0U; if (msg->flags & MCUX_DSI_2L_ULPS) { data->flags |= MCUX_DSI_2L_ULPS; @@ -522,6 +645,9 @@ static ssize_t dsi_mcux_transfer(const struct device *dev, uint8_t channel, dsi_xfer.sendDscCmd = true; dsi_xfer.dscCmd = msg->cmd; dsi_xfer.txDataType = kDSI_TxDataDcsLongWr; +#ifndef CONFIG_MIPI_DSI_MCUX_NXP_DCNANO_LCDIF + int ret; + if (msg->flags & MCUX_DSI_2L_FB_DATA) { /* * Special case- transfer framebuffer data using @@ -535,6 +661,7 @@ static ssize_t dsi_mcux_transfer(const struct device *dev, uint8_t channel, } return ret; } +#endif break; case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM: dsi_xfer.txDataType = kDSI_TxDataGenShortWrNoParam; @@ -560,15 +687,19 @@ static ssize_t dsi_mcux_transfer(const struct device *dev, uint8_t channel, return -ENOTSUP; } +#ifdef CONFIG_MIPI_DSI_MCUX_NXP_DCNANO_LCDIF + status = dsi_mcux_dcnano_transfer(dev, channel, msg, &dsi_xfer); +#else status = DSI_TransferBlocking(config->base, &dsi_xfer); - - dsi_mcux_transfer_complete(dev); +#endif if (status != kStatus_Success) { LOG_ERR("Transmission failed"); return -EIO; } + dsi_mcux_transfer_complete(dev); + if (msg->rx_len != 0) { /* Return rx_len on a read */ return msg->rx_len; @@ -648,6 +779,8 @@ static int mcux_mipi_dsi_init(const struct device *dev) 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,)) \ + COND_CODE_1(CONFIG_MIPI_DSI_MCUX_NXP_DCNANO_LCDIF, \ + (.mipi_dbi = DEVICE_DT_GET(DT_NODELABEL(zephyr_lcdif)),), ()) \ .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), \