mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-08-03 12:23:59 +00:00
Put the device APIs in their respective linker sections with the DEVICE_API wrapper macro. Signed-off-by: Pieter De Gendt <pieter.degendt@basalte.be>
383 lines
11 KiB
C
383 lines
11 KiB
C
/*
|
|
* 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 DEVICE_API(mipi_dbi, 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)
|