drivers: mipi_dbi: introduce NXP DCnano driver

Introduce NXP NCNano driver using MIPI DBI class. This peripheral
supports 8080 and 6800 mode. The driver also supports used with
nxp,mipi_dsi_2l driver, for the panel with DPHY bus, such as g1120b0mipi.

Signed-off-by: Kate Wang <yumeng.wang@nxp.com>
This commit is contained in:
Kate Wang 2025-04-14 11:14:45 +08:00 committed by Benjamin Cabé
commit 0017bfcedc
6 changed files with 482 additions and 2 deletions

View file

@ -1,4 +1,4 @@
# Copyright 2023 NXP
# Copyright 2023,2025 NXP
#
# SPDX-License-Identifier: Apache-2.0
@ -7,6 +7,7 @@ zephyr_sources_ifdef(CONFIG_MIPI_DBI_BITBANG mipi_dbi_bitbang.c)
zephyr_sources_ifdef(CONFIG_MIPI_DBI_SMARTBOND mipi_dbi_smartbond.c)
zephyr_sources_ifdef(CONFIG_MIPI_DBI_NXP_LCDIC mipi_dbi_nxp_lcdic.c)
zephyr_sources_ifdef(CONFIG_MIPI_DBI_NXP_FLEXIO_LCDIF mipi_dbi_nxp_flexio_lcdif.c)
zephyr_sources_ifdef(CONFIG_MIPI_DBI_NXP_DCNANO_LCDIF mipi_dbi_nxp_dcnano_lcdif.c)
zephyr_sources_ifdef(CONFIG_MIPI_DBI_STM32_FMC mipi_dbi_stm32_fmc.c)
# Data bus width is used by the SDK driver and processes it as a compile time option
if(CONFIG_MIPI_DBI_NXP_FLEXIO_LCDIF)

View file

@ -1,6 +1,6 @@
# MIPI DBI controller options
# Copyright 2023 NXP
# Copyright 2023,2025 NXP
# SPDX-License-Identifier: Apache-2.0
menuconfig MIPI_DBI
@ -27,5 +27,6 @@ source "drivers/mipi_dbi/Kconfig.smartbond"
source "drivers/mipi_dbi/Kconfig.nxp_lcdic"
source "drivers/mipi_dbi/Kconfig.nxp_flexio_lcdif"
source "drivers/mipi_dbi/Kconfig.stm32_fmc"
source "drivers/mipi_dbi/Kconfig.nxp_dcnano_lcdif"
endif

View file

@ -0,0 +1,10 @@
# Copyright 2025 NXP
# SPDX-License-Identifier: Apache-2.0
config MIPI_DBI_NXP_DCNANO_LCDIF
bool "MIPI DBI driver for NXP DCnano LCDIF"
default y
depends on DT_HAS_NXP_MIPI_DBI_DCNANO_LCDIF_ENABLED
select PINCTRL
help
Enable support for MIPI DBI driver for NXP FlexIO based LCDIF controller.

View file

@ -0,0 +1,382 @@
/*
* Copyright 2025 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT nxp_mipi_dbi_dcnano_lcdif
#include <zephyr/drivers/display.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/mipi_dbi.h>
#include <zephyr/dt-bindings/mipi_dbi/mipi_dbi.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <fsl_lcdif.h>
LOG_MODULE_REGISTER(mipi_dbi_nxp_dcnano_lcdif, CONFIG_DISPLAY_LOG_LEVEL);
struct mcux_dcnano_lcdif_dbi_data {
struct k_sem transfer_done;
const struct mipi_dbi_config *active_cfg;
};
struct mcux_dcnano_lcdif_dbi_config {
LCDIF_Type *base;
void (*irq_config_func)(const struct device *dev);
lcdif_dbi_config_t dbi_config;
lcdif_panel_config_t panel_config;
const struct pinctrl_dev_config *pincfg;
const struct gpio_dt_spec reset;
};
struct mcux_dcnano_lcdif_dbi_foramt_map_t {
uint8_t bus_type;
uint8_t color_coding;
lcdif_dbi_out_format_t format;
};
const struct mcux_dcnano_lcdif_dbi_foramt_map_t format_map[] = {
{MIPI_DBI_MODE_6800_BUS_8_BIT, MIPI_DBI_MODE_RGB332, kLCDIF_DbiOutD8RGB332},
{MIPI_DBI_MODE_6800_BUS_8_BIT, MIPI_DBI_MODE_RGB444, kLCDIF_DbiOutD8RGB444},
{MIPI_DBI_MODE_6800_BUS_8_BIT, MIPI_DBI_MODE_RGB565, kLCDIF_DbiOutD8RGB565},
{MIPI_DBI_MODE_6800_BUS_8_BIT, MIPI_DBI_MODE_RGB666_1, kLCDIF_DbiOutD8RGB666},
{MIPI_DBI_MODE_6800_BUS_8_BIT, MIPI_DBI_MODE_RGB888_1, kLCDIF_DbiOutD8RGB888},
{MIPI_DBI_MODE_6800_BUS_9_BIT, MIPI_DBI_MODE_RGB666_1, kLCDIF_DbiOutD9RGB666},
{MIPI_DBI_MODE_6800_BUS_8_BIT, MIPI_DBI_MODE_RGB666_2, kLCDIF_DbiOutD8RGB666},
{MIPI_DBI_MODE_6800_BUS_8_BIT, MIPI_DBI_MODE_RGB888_2, kLCDIF_DbiOutD8RGB888},
{MIPI_DBI_MODE_6800_BUS_9_BIT, MIPI_DBI_MODE_RGB666_2, kLCDIF_DbiOutD9RGB666},
{MIPI_DBI_MODE_6800_BUS_16_BIT, MIPI_DBI_MODE_RGB332, kLCDIF_DbiOutD16RGB332},
{MIPI_DBI_MODE_6800_BUS_16_BIT, MIPI_DBI_MODE_RGB444, kLCDIF_DbiOutD16RGB444},
{MIPI_DBI_MODE_6800_BUS_16_BIT, MIPI_DBI_MODE_RGB565, kLCDIF_DbiOutD16RGB565},
{MIPI_DBI_MODE_6800_BUS_16_BIT, MIPI_DBI_MODE_RGB666_1,
kLCDIF_DbiOutD16RGB666Option1},
{MIPI_DBI_MODE_6800_BUS_16_BIT, MIPI_DBI_MODE_RGB666_2,
kLCDIF_DbiOutD16RGB666Option2},
{MIPI_DBI_MODE_6800_BUS_16_BIT, MIPI_DBI_MODE_RGB888_1,
kLCDIF_DbiOutD16RGB888Option1},
{MIPI_DBI_MODE_6800_BUS_16_BIT, MIPI_DBI_MODE_RGB888_2,
kLCDIF_DbiOutD16RGB888Option2},
};
static int mcux_dcnano_lcdif_dbi_get_format(uint8_t bus_type, uint8_t color_coding,
lcdif_dbi_out_format_t *format)
{
for (uint8_t i = 0; i < ARRAY_SIZE(format_map); i++) {
if ((format_map[i].bus_type == (bus_type - MIPI_DBI_MODE_6800_BUS_16_BIT)) &&
(format_map[i].color_coding == color_coding)) {
*format = format_map[i].format;
return 0;
}
}
return -EINVAL;
}
static void mcux_dcnano_lcdif_dbi_isr(const struct device *dev)
{
const struct mcux_dcnano_lcdif_dbi_config *config = dev->config;
struct mcux_dcnano_lcdif_dbi_data *lcdif_data = dev->data;
uint32_t status;
status = LCDIF_GetAndClearInterruptPendingFlags(config->base);
if (0 != (status & kLCDIF_Display0FrameDoneInterrupt)) {
k_sem_give(&lcdif_data->transfer_done);
}
}
static int mcux_dcnano_lcdif_dbi_configure(const struct device *dev,
const struct mipi_dbi_config *dbi_config)
{
const struct mcux_dcnano_lcdif_dbi_config *config = dev->config;
struct mcux_dcnano_lcdif_dbi_data *lcdif_data = dev->data;
uint8_t bus_type = dbi_config->mode & 0xFU;
uint8_t color_coding = dbi_config->mode & 0xF0U;
lcdif_dbi_config_t lcdif_dbi_config = config->dbi_config;
status_t status;
/* No need to update if configuration is the same. */
if (dbi_config == lcdif_data->active_cfg) {
return 0;
}
/* SPI mode is not supported by the SDK LCDIF driver */
if ((bus_type == MIPI_DBI_MODE_SPI_3WIRE) ||
(bus_type == MIPI_DBI_MODE_SPI_4WIRE)) {
LOG_ERR("Bus type not supported.");
return -EINVAL;
}
/* 9-bit bus only has RGB666 color coding. */
if (((bus_type == MIPI_DBI_MODE_6800_BUS_9_BIT) ||
(bus_type == MIPI_DBI_MODE_8080_BUS_9_BIT)) &&
((color_coding != MIPI_DBI_MODE_RGB666_1) &&
(color_coding != MIPI_DBI_MODE_RGB666_2))) {
return -EINVAL;
}
/* Get the bus type */
switch (bus_type) {
case MIPI_DBI_MODE_6800_BUS_16_BIT:
case MIPI_DBI_MODE_6800_BUS_9_BIT:
case MIPI_DBI_MODE_6800_BUS_8_BIT:
lcdif_dbi_config.type = kLCDIF_DbiTypeA_FixedE;
break;
case MIPI_DBI_MODE_8080_BUS_16_BIT:
case MIPI_DBI_MODE_8080_BUS_9_BIT:
case MIPI_DBI_MODE_8080_BUS_8_BIT:
lcdif_dbi_config.type = kLCDIF_DbiTypeB;
break;
default:
return -EINVAL;
}
/* Get the color cosing */
status = mcux_dcnano_lcdif_dbi_get_format(bus_type, color_coding,
&lcdif_dbi_config.format);
if (kStatus_Success != status) {
return -EINVAL;
}
/* Update DBI configuration. */
status = LCDIF_DbiModeSetConfig(config->base, 0, &lcdif_dbi_config);
if (kStatus_Success != status) {
return -EINVAL;
}
lcdif_data->active_cfg = dbi_config;
return 0;
}
static int mcux_dcnano_lcdif_dbi_init(const struct device *dev)
{
const struct mcux_dcnano_lcdif_dbi_config *config = dev->config;
struct mcux_dcnano_lcdif_dbi_data *lcdif_data = dev->data;
#ifndef CONFIG_MIPI_DSI_MCUX_NXP_DCNANO_LCDIF
int ret;
/* Pin control is not applied when DCNano is used in MCUX DSI driver. */
ret = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
if (ret) {
return ret;
}
#endif
LCDIF_Init(config->base);
LCDIF_DbiModeSetConfig(config->base, 0, &config->dbi_config);
LCDIF_SetPanelConfig(config->base, 0, &config->panel_config);
LCDIF_EnableInterrupts(config->base, kLCDIF_Display0FrameDoneInterrupt);
config->irq_config_func(dev);
k_sem_init(&lcdif_data->transfer_done, 0, 1);
LOG_DBG("%s device init complete", dev->name);
return 0;
}
static int mipi_dbi_dcnano_lcdif_write_display(const struct device *dev,
const struct mipi_dbi_config *dbi_config,
const uint8_t *framebuf,
struct display_buffer_descriptor *desc,
enum display_pixel_format pixfmt)
{
const struct mcux_dcnano_lcdif_dbi_config *config = dev->config;
struct mcux_dcnano_lcdif_dbi_data *lcdif_data = dev->data;
int ret = 0;
uint8_t bytes_per_pixel = 0U;
/* The DBI bus type and color coding. */
ret = mcux_dcnano_lcdif_dbi_configure(dev, dbi_config);
if (ret) {
return ret;
}
lcdif_fb_config_t fbConfig;
LCDIF_FrameBufferGetDefaultConfig(&fbConfig);
fbConfig.enable = true;
fbConfig.inOrder = kLCDIF_PixelInputOrderARGB;
fbConfig.rotateFlipMode = kLCDIF_Rotate0;
switch (pixfmt) {
case PIXEL_FORMAT_RGB_888:
fbConfig.format = kLCDIF_PixelFormatRGB888;
bytes_per_pixel = 3U;
break;
case PIXEL_FORMAT_ARGB_8888:
fbConfig.format = kLCDIF_PixelFormatARGB8888;
bytes_per_pixel = 4U;
break;
case PIXEL_FORMAT_BGR_565:
fbConfig.inOrder = kLCDIF_PixelInputOrderABGR;
case PIXEL_FORMAT_RGB_565:
fbConfig.format = kLCDIF_PixelFormatRGB565;
bytes_per_pixel = 2U;
break;
default:
LOG_ERR("Bus tyoe not supported.");
ret = -ENODEV;
break;
}
if (ret) {
return ret;
}
fbConfig.alpha.enable = false;
fbConfig.colorkey.enable = false;
fbConfig.topLeftX = 0U;
fbConfig.topLeftY = 0U;
fbConfig.width = desc->width;
fbConfig.height = desc->height;
LCDIF_SetFrameBufferConfig(config->base, 0, &fbConfig);
if (bytes_per_pixel == 3U) {
/* For RGB888 the stride shall be calculated as
* 4 bytes per pixel instead of 3.
*/
LCDIF_SetFrameBufferStride(config->base, 0, 4U * desc->pitch);
} else {
LCDIF_SetFrameBufferStride(config->base, 0, bytes_per_pixel * desc->pitch);
}
/* Set the updated area's size according to desc. */
LCDIF_SetFrameBufferPosition(config->base, 0U, 0U, 0U, desc->width,
desc->height);
LCDIF_DbiSelectArea(config->base, 0, 0, 0, desc->width - 1U,
desc->height - 1U, false);
LCDIF_SetFrameBufferAddr(config->base, 0, (uint32_t)framebuf);
/* Enable DMA and send out data. */
LCDIF_DbiWriteMem(config->base, 0);
/* Wait for transfer done. */
k_sem_take(&lcdif_data->transfer_done, K_FOREVER);
return 0;
}
static int mipi_dbi_dcnano_lcdif_command_write(const struct device *dev,
const struct mipi_dbi_config *dbi_config,
uint8_t cmd, const uint8_t *data_buf,
size_t len)
{
const struct mcux_dcnano_lcdif_dbi_config *config = dev->config;
int ret = 0;
/* The DBI bus type and color coding. */
ret = mcux_dcnano_lcdif_dbi_configure(dev, dbi_config);
if (ret) {
return ret;
}
LCDIF_DbiSendCommand(config->base, 0U, cmd);
if (len != 0U) {
LCDIF_DbiSendData(config->base, 0U, data_buf, len);
}
return kStatus_Success;
}
static int mipi_dbi_dcnano_lcdif_reset(const struct device *dev, k_timeout_t delay)
{
int ret;
const struct mcux_dcnano_lcdif_dbi_config *config = dev->config;
/* Check if a reset port is provided to reset the LCD controller */
if (config->reset.port == NULL) {
return 0;
}
/* Reset the LCD controller. */
ret = gpio_pin_configure_dt(&config->reset, GPIO_OUTPUT_HIGH);
if (ret) {
return ret;
}
ret = gpio_pin_set_dt(&config->reset, 0);
if (ret < 0) {
return ret;
}
k_sleep(delay);
ret = gpio_pin_set_dt(&config->reset, 1);
if (ret < 0) {
return ret;
}
LOG_DBG("%s device reset complete", dev->name);
return 0;
}
static struct mipi_dbi_driver_api mcux_dcnano_lcdif_dbi_api = {
.reset = mipi_dbi_dcnano_lcdif_reset,
.command_write = mipi_dbi_dcnano_lcdif_command_write,
.write_display = mipi_dbi_dcnano_lcdif_write_display,
};
#define MCUX_DCNANO_LCDIF_DEVICE_INIT(n) \
static void mcux_dcnano_lcdif_dbi_config_func_##n(const struct device *dev) \
{ \
IRQ_CONNECT(DT_INST_IRQN(n), \
DT_INST_IRQ(n, priority), \
mcux_dcnano_lcdif_dbi_isr, \
DEVICE_DT_INST_GET(n), \
0); \
irq_enable(DT_INST_IRQN(n)); \
} \
PINCTRL_DT_INST_DEFINE(n); \
struct mcux_dcnano_lcdif_dbi_data mcux_dcnano_lcdif_dbi_data_##n; \
struct mcux_dcnano_lcdif_dbi_config mcux_dcnano_lcdif_dbi_config_##n = { \
.base = (LCDIF_Type *) DT_INST_REG_ADDR(n), \
.irq_config_func = mcux_dcnano_lcdif_dbi_config_func_##n, \
.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
.reset = GPIO_DT_SPEC_INST_GET_OR(n, reset_gpios, {0}), \
.dbi_config = { \
.type = kLCDIF_DbiTypeA_FixedE, \
.swizzle = DT_INST_ENUM_IDX_OR(n, swizzle, 0), \
.format = kLCDIF_DbiOutD8RGB332, \
.acTimeUnit = DT_INST_PROP_OR(n, divider, 1) - 1, \
.writeWRPeriod = DT_INST_PROP(n, wr_period), \
.writeWRAssert = DT_INST_PROP(n, wr_assert), \
.writeWRDeassert = DT_INST_PROP(n, wr_deassert), \
.writeCSAssert = DT_INST_PROP(n, cs_assert), \
.writeCSDeassert = DT_INST_PROP(n, cs_deassert), \
}, \
.panel_config = { \
.enable = true, \
.enableGamma = false, \
.order = kLCDIF_VideoOverlay0Overlay1, \
.endian = DT_INST_ENUM_IDX_OR(n, endian, 0), \
}, \
}; \
DEVICE_DT_INST_DEFINE(n, \
&mcux_dcnano_lcdif_dbi_init, \
NULL, \
&mcux_dcnano_lcdif_dbi_data_##n, \
&mcux_dcnano_lcdif_dbi_config_##n, \
POST_KERNEL, \
CONFIG_MIPI_DBI_INIT_PRIORITY, \
&mcux_dcnano_lcdif_dbi_api);
DT_INST_FOREACH_STATUS_OKAY(MCUX_DCNANO_LCDIF_DEVICE_INIT)

View file

@ -0,0 +1,85 @@
# Copyright 2025 NXP
# SPDX-License-Identifier: Apache-2.0
description: |
DBI settings for NXP DCnano LCD controller. DCNano is used in DPI mode by default,
when used in DBI mode the compatible need to change in example overlay. For example:
&lcdif {
compatible = "nxp,mipi-dbi-dcnano-lcdif";
clock-frequency = <279000000>;
wr-period = <14>;
wr-assert = <6>;
wr-deassert = <13>;
cs-assert = <1>;
cs-deassert = <4>;
};
compatible: "nxp,mipi-dbi-dcnano-lcdif"
include: [base.yaml]
properties:
clock-frequency:
type: int
required: true
description: |
The DBI clock source frequency.
reset-gpios:
type: phandle-array
description: |
Reset GPIO pin.
divider:
type: int
description: |
Divider of the DBI clock source frequency, the final DBI frequency is
clock-frequency / divider.
wr-period:
type: int
required: true
description: |
How many clock cycles does one write cycle take.
wr-assert:
type: int
required: true
description: |
At which clock cycle to assert E signal for TypeA_ClockedE, or WRX signal for TypeB.
wr-deassert:
type: int
required: true
description: |
At which clock cycle to deassert E signal for TypeA_ClockedE, or WRX signal for TypeB.
cs-assert:
type: int
required: true
description: |
At which clock cycle to assert CSX signal for TypeA_FixedE or TypeB.
cs-deassert:
type: int
required: true
description: |
At which clock cycle to deassert CSX signal for TypeA_FixedE or TypeB.
endian:
type: string
description: |
Input data endian mode.
enum:
- "no-swap"
- "half-word"
- "word"
swizzle:
type: string
description: |
Bus data output order.
enum:
- "RGB"
- "BGR"

View file

@ -136,6 +136,7 @@ set_variable_ifdef(CONFIG_PINCTRL_NXP_PORT CONFIG_MCUX_COMPONENT_driver.por
set_variable_ifdef(CONFIG_DMA_NXP_EDMA CONFIG_MCUX_COMPONENT_driver.edma_soc_rev2)
set_variable_ifdef(CONFIG_COUNTER_MCUX_SNVS_SRTC CONFIG_MCUX_COMPONENT_driver.snvs_lp)
set_variable_ifdef(CONFIG_DISPLAY_MCUX_DCNANO_LCDIF CONFIG_MCUX_COMPONENT_driver.lcdif)
set_variable_ifdef(CONFIG_MIPI_DBI_NXP_DCNANO_LCDIF CONFIG_MCUX_COMPONENT_driver.lcdif)
set_variable_ifdef(CONFIG_MIPI_DBI_NXP_FLEXIO_LCDIF CONFIG_MCUX_COMPONENT_driver.flexio_mculcd)
set_variable_ifdef(CONFIG_VIDEO_MCUX_MIPI_CSI2RX CONFIG_MCUX_COMPONENT_driver.mipi_csi2rx)
set_variable_ifdef(CONFIG_ETH_NXP_IMX_NETC CONFIG_MCUX_COMPONENT_driver.netc)