drivers: mipi_dsi: dsi_mcux_2l: Use NXP DCNano DBI driver for memory write

There is no smartdma on RT700, so to perform DCS memory write the CPU has
to write APB buffer word by word, which is too slow for most applications.
But the DCNano in DBI mode can be used to interface with the MIPI-DSI on
RT700, and send data to MIPI-DSI to transfer, once it is properly
configured, which solves the issue.
First added new parameter first_write in display_buffer_descriptor to let
NXP DCNano DBI driver know to use MIPI_DCS_WRITE_MEMORY_START or
MIPI_DCS_WRITE_MEMORY_CONTINUE.
Second updated the MCUX MIPI-DSI driver to support using the NXP DCNano DBI
driver for memory write.

Signed-off-by: Kate Wang <yumeng.wang@nxp.com>
This commit is contained in:
Kate Wang 2025-04-10 16:57:54 +08:00 committed by Benjamin Cabé
commit d628bfc148
2 changed files with 146 additions and 5 deletions

View file

@ -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

View file

@ -22,11 +22,18 @@
#include <fsl_inputmux.h>
#include <fsl_smartdma.h>
#endif
#ifdef CONFIG_MIPI_DSI_MCUX_NXP_DCNANO_LCDIF
#include <zephyr/drivers/mipi_dbi.h>
#endif
#include <soc.h>
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), \