From ab84cbfc405f8df2e77b2564dd9a1bbd57e3e635 Mon Sep 17 00:00:00 2001 From: Maureen Helm Date: Wed, 2 Jan 2019 11:31:08 -0600 Subject: [PATCH] drivers: display: Introduce mcux elcdif shim driver Adds a shim layer around the mcux elcdif driver to adapt it to the zephyr display interface. Although the hardware and underlying mcux sdk driver can support additional configurations, some shortcuts are currently made in the shim that force a given pixel format, lcd data bus width, and signal polarity. This works with the rocktech lcd module used on imx rt boards, but will need to be updated for other display panels. Signed-off-by: Maureen Helm --- drivers/display/CMakeLists.txt | 1 + drivers/display/Kconfig | 2 +- drivers/display/Kconfig.mcux_elcdif | 48 ++++ drivers/display/display_mcux_elcdif.c | 268 ++++++++++++++++++++ ext/hal/nxp/mcux/Kconfig | 6 + ext/hal/nxp/mcux/drivers/imx/CMakeLists.txt | 1 + 6 files changed, 325 insertions(+), 1 deletion(-) create mode 100644 drivers/display/Kconfig.mcux_elcdif create mode 100644 drivers/display/display_mcux_elcdif.c diff --git a/drivers/display/CMakeLists.txt b/drivers/display/CMakeLists.txt index 6f7f17950ad..bd546671917 100644 --- a/drivers/display/CMakeLists.txt +++ b/drivers/display/CMakeLists.txt @@ -1,4 +1,5 @@ +zephyr_sources_ifdef(CONFIG_DISPLAY_MCUX_ELCDIF display_mcux_elcdif.c) zephyr_sources_ifdef(CONFIG_GROVE_LCD_RGB grove_lcd_rgb.c) zephyr_sources_ifdef(CONFIG_MICROBIT_DISPLAY mb_display.c diff --git a/drivers/display/Kconfig b/drivers/display/Kconfig index 22f834322f8..dbf1998c04f 100644 --- a/drivers/display/Kconfig +++ b/drivers/display/Kconfig @@ -18,7 +18,7 @@ module-str = display source "subsys/logging/Kconfig.template.log_config" source "drivers/display/Kconfig.grove" - +source "drivers/display/Kconfig.mcux_elcdif" source "drivers/display/Kconfig.microbit" source "drivers/display/Kconfig.ili9340" source "drivers/display/Kconfig.sdl" diff --git a/drivers/display/Kconfig.mcux_elcdif b/drivers/display/Kconfig.mcux_elcdif new file mode 100644 index 00000000000..7a2f4dbf2c4 --- /dev/null +++ b/drivers/display/Kconfig.mcux_elcdif @@ -0,0 +1,48 @@ +# +# Copyright (c) 2019, NXP +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig DISPLAY_MCUX_ELCDIF + bool "MCUX eLCDIF driver" + depends on HAS_MCUX_ELCDIF + help + Enable support for mcux eLCDIF driver. + +if DISPLAY_MCUX_ELCDIF + +config MCUX_ELCDIF_POOL_BLOCK_NUM + int "Number of memory pool blocks" + default 2 + help + Number of blocks in the frame buffer memory pool. + +config MCUX_ELCDIF_POOL_BLOCK_MIN + hex "Minimum memory pool block size" + default 0x400 + help + Minimum block size in the frame buffer memory pool. + +config MCUX_ELCDIF_POOL_BLOCK_MAX + hex "Maximum memory pool block size" + default 0x40000 + help + Maximum block size in the frame buffer memory pool. + +config MCUX_ELCDIF_POOL_BLOCK_ALIGN + int "Alignment of memory pool blocks" + default 64 + help + Byte alignment in the frame buffer memory pool. + +choice MCUX_ELCDIF_PANEL + prompt "Panel selection" + default MCUX_ELCDIF_PANEL_RK043FN02H + +config MCUX_ELCDIF_PANEL_RK043FN02H + bool "Rocktech rk043fn02h-ct" + +endchoice + +endif # DISPLAY_MCUX_ELCDIF diff --git a/drivers/display/display_mcux_elcdif.c b/drivers/display/display_mcux_elcdif.c new file mode 100644 index 00000000000..573a545f7c7 --- /dev/null +++ b/drivers/display/display_mcux_elcdif.c @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2019, NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#ifdef CONFIG_HAS_MCUX_CACHE +#include +#endif + +#include + +LOG_MODULE_REGISTER(display_mcux_elcdif, CONFIG_DISPLAY_LOG_LEVEL); + +K_MEM_POOL_DEFINE(mcux_elcdif_pool, + CONFIG_MCUX_ELCDIF_POOL_BLOCK_MIN, + CONFIG_MCUX_ELCDIF_POOL_BLOCK_MAX, + CONFIG_MCUX_ELCDIF_POOL_BLOCK_NUM, + CONFIG_MCUX_ELCDIF_POOL_BLOCK_ALIGN); + +struct mcux_elcdif_config { + LCDIF_Type *base; + void (*irq_config_func)(struct device *dev); + elcdif_rgb_mode_config_t rgb_mode; + enum display_pixel_format pixel_format; + u8_t bits_per_pixel; +}; + +struct mcux_elcdif_data { + struct k_mem_block fb[2]; + struct k_sem sem; + size_t pixel_bytes; + size_t fb_bytes; + u8_t write_idx; +}; + +static int mcux_elcdif_write(const struct device *dev, const u16_t x, + const u16_t y, + const struct display_buffer_descriptor *desc, + const void *buf) +{ + const struct mcux_elcdif_config *config = dev->config->config_info; + struct mcux_elcdif_data *data = dev->driver_data; + + u8_t write_idx = data->write_idx; + u8_t read_idx = !write_idx; + + int h_idx; + const u8_t *src; + u8_t *dst; + + __ASSERT((data->pixel_bytes * desc->pitch * desc->height) <= + desc->buf_size, "Input buffer too small"); + + LOG_DBG("W=%d, H=%d, @%d,%d", desc->width, desc->height, x, y); + + k_sem_take(&data->sem, K_FOREVER); + + memcpy(data->fb[write_idx].data, data->fb[read_idx].data, + data->fb_bytes); + + src = buf; + dst = data->fb[data->write_idx].data; + dst += data->pixel_bytes * (y * config->rgb_mode.panelWidth + x); + + for (h_idx = 0; h_idx < desc->height; h_idx++) { + memcpy(dst, src, data->pixel_bytes * desc->width); + src += data->pixel_bytes * desc->pitch; + dst += data->pixel_bytes * config->rgb_mode.panelWidth; + } + +#ifdef CONFIG_HAS_MCUX_CACHE + DCACHE_CleanByRange((uint32_t) data->fb[write_idx].data, + data->fb_bytes); +#endif + + ELCDIF_SetNextBufferAddr(config->base, + (uint32_t) data->fb[write_idx].data); + + data->write_idx = read_idx; + + return 0; +} + +static int mcux_elcdif_read(const struct device *dev, const u16_t x, + const u16_t y, + const struct display_buffer_descriptor *desc, + void *buf) +{ + LOG_ERR("Read not implemented"); + return -ENOTSUP; +} + +static void *mcux_elcdif_get_framebuffer(const struct device *dev) +{ + LOG_ERR("Direct framebuffer access not implemented"); + return NULL; +} + +static int mcux_elcdif_display_blanking_off(const struct device *dev) +{ + LOG_ERR("Display blanking control not implemented"); + return -ENOTSUP; +} + +static int mcux_elcdif_display_blanking_on(const struct device *dev) +{ + LOG_ERR("Display blanking control not implemented"); + return -ENOTSUP; +} + +static int mcux_elcdif_set_brightness(const struct device *dev, + const u8_t brightness) +{ + LOG_WRN("Set brightness not implemented"); + return -ENOTSUP; +} + +static int mcux_elcdif_set_contrast(const struct device *dev, + const u8_t contrast) +{ + LOG_ERR("Set contrast not implemented"); + return -ENOTSUP; +} + +static int mcux_elcdif_set_pixel_format(const struct device *dev, + const enum display_pixel_format + pixel_format) +{ + const struct mcux_elcdif_config *config = dev->config->config_info; + + if (pixel_format == config->pixel_format) { + return 0; + } + LOG_ERR("Pixel format change not implemented"); + return -ENOTSUP; +} + +static int mcux_elcdif_set_orientation(const struct device *dev, + const enum display_orientation orientation) +{ + if (orientation == DISPLAY_ORIENTATION_NORMAL) { + return 0; + } + LOG_ERR("Changing display orientation not implemented"); + return -ENOTSUP; +} + +static void mcux_elcdif_get_capabilities(const struct device *dev, + struct display_capabilities *capabilities) +{ + const struct mcux_elcdif_config *config = dev->config->config_info; + + memset(capabilities, 0, sizeof(struct display_capabilities)); + capabilities->x_resolution = config->rgb_mode.panelWidth; + capabilities->y_resolution = config->rgb_mode.panelHeight; + capabilities->supported_pixel_formats = config->pixel_format; + capabilities->current_pixel_format = config->pixel_format; + capabilities->current_orientation = DISPLAY_ORIENTATION_NORMAL; +} + +static void mcux_elcdif_isr(void *arg) +{ + struct device *dev = (struct device *)arg; + const struct mcux_elcdif_config *config = dev->config->config_info; + struct mcux_elcdif_data *data = dev->driver_data; + u32_t status; + + status = ELCDIF_GetInterruptStatus(config->base); + ELCDIF_ClearInterruptStatus(config->base, status); + + k_sem_give(&data->sem); +} + +static int mcux_elcdif_init(struct device *dev) +{ + const struct mcux_elcdif_config *config = dev->config->config_info; + struct mcux_elcdif_data *data = dev->driver_data; + int i; + + elcdif_rgb_mode_config_t rgb_mode = config->rgb_mode; + + data->pixel_bytes = config->bits_per_pixel / 8; + data->fb_bytes = data->pixel_bytes * + rgb_mode.panelWidth * rgb_mode.panelHeight; + data->write_idx = 1; + + for (i = 0; i < ARRAY_SIZE(data->fb); i++) { + if (k_mem_pool_alloc(&mcux_elcdif_pool, &data->fb[i], + data->fb_bytes, K_NO_WAIT) != 0) { + LOG_ERR("Could not allocate frame buffer %d", i); + return -ENOMEM; + } + memset(data->fb[i].data, 0, data->fb_bytes); + } + rgb_mode.bufferAddr = (uint32_t) data->fb[0].data; + + k_sem_init(&data->sem, 1, 1); + + config->irq_config_func(dev); + + ELCDIF_RgbModeInit(config->base, &rgb_mode); + ELCDIF_EnableInterrupts(config->base, + kELCDIF_CurFrameDoneInterruptEnable); + ELCDIF_RgbModeStart(config->base); + + return 0; +} + +static const struct display_driver_api mcux_elcdif_api = { + .blanking_on = mcux_elcdif_display_blanking_on, + .blanking_off = mcux_elcdif_display_blanking_off, + .write = mcux_elcdif_write, + .read = mcux_elcdif_read, + .get_framebuffer = mcux_elcdif_get_framebuffer, + .set_brightness = mcux_elcdif_set_brightness, + .set_contrast = mcux_elcdif_set_contrast, + .get_capabilities = mcux_elcdif_get_capabilities, + .set_pixel_format = mcux_elcdif_set_pixel_format, + .set_orientation = mcux_elcdif_set_orientation, +}; + +static void mcux_elcdif_config_func_1(struct device *dev); + +static struct mcux_elcdif_config mcux_elcdif_config_1 = { + .base = (LCDIF_Type *) DT_FSL_IMX6SX_LCDIF_0_BASE_ADDRESS, + .irq_config_func = mcux_elcdif_config_func_1, +#ifdef CONFIG_MCUX_ELCDIF_PANEL_RK043FN02H + .rgb_mode = { + .panelWidth = 480, + .panelHeight = 272, + .hsw = 41, + .hfp = 4, + .hbp = 8, + .vsw = 10, + .vfp = 4, + .vbp = 2, + .polarityFlags = kELCDIF_DataEnableActiveHigh | + kELCDIF_VsyncActiveLow | + kELCDIF_HsyncActiveLow | + kELCDIF_DriveDataOnRisingClkEdge, + .pixelFormat = kELCDIF_PixelFormatRGB565, + .dataBus = kELCDIF_DataBus16Bit, + }, + .pixel_format = PIXEL_FORMAT_RGB_565, + .bits_per_pixel = 16, +#endif +}; + +static struct mcux_elcdif_data mcux_elcdif_data_1; + +DEVICE_AND_API_INIT(mcux_elcdif_1, DT_FSL_IMX6SX_LCDIF_0_LABEL, + &mcux_elcdif_init, + &mcux_elcdif_data_1, &mcux_elcdif_config_1, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &mcux_elcdif_api); + +static void mcux_elcdif_config_func_1(struct device *dev) +{ + IRQ_CONNECT(DT_FSL_IMX6SX_LCDIF_0_IRQ_0, + DT_FSL_IMX6SX_LCDIF_0_IRQ_0_PRIORITY, + mcux_elcdif_isr, DEVICE_GET(mcux_elcdif_1), 0); + + irq_enable(DT_FSL_IMX6SX_LCDIF_0_IRQ_0); +} diff --git a/ext/hal/nxp/mcux/Kconfig b/ext/hal/nxp/mcux/Kconfig index 0544c66ee80..7e11e03db46 100644 --- a/ext/hal/nxp/mcux/Kconfig +++ b/ext/hal/nxp/mcux/Kconfig @@ -27,6 +27,12 @@ config HAS_MCUX_CCM help Set if the clock control module (CCM) module is present in the SoC. +config HAS_MCUX_ELCDIF + bool + help + Set if the enhanced LCD interface (eLCDIF) module is present in the + SoC. + config HAS_MCUX_ENET bool help diff --git a/ext/hal/nxp/mcux/drivers/imx/CMakeLists.txt b/ext/hal/nxp/mcux/drivers/imx/CMakeLists.txt index 8872bc40b7f..fc4bb6ff286 100644 --- a/ext/hal/nxp/mcux/drivers/imx/CMakeLists.txt +++ b/ext/hal/nxp/mcux/drivers/imx/CMakeLists.txt @@ -15,6 +15,7 @@ zephyr_library_compile_definitions_ifdef( ) zephyr_sources_ifdef(CONFIG_HAS_MCUX_CACHE fsl_cache.c) +zephyr_sources_ifdef(CONFIG_DISPLAY_MCUX_ELCDIF fsl_elcdif.c) zephyr_sources_ifdef(CONFIG_GPIO_MCUX_IGPIO fsl_gpio.c) zephyr_sources_ifdef(CONFIG_I2C_MCUX_LPI2C fsl_lpi2c.c) zephyr_sources_ifdef(CONFIG_SPI_MCUX_LPSPI fsl_lpspi.c)