drivers: display: enable direct framebuffer rendering with DCNANO LCDIF
Enable direct framebuffer rendering using DCNANO LCDIF, to improve performance when the call to display_write is attempting to refresh the full display. Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
This commit is contained in:
parent
52b4144145
commit
a688fb109d
2 changed files with 72 additions and 58 deletions
|
@ -12,14 +12,19 @@ menuconfig DISPLAY_MCUX_DCNANO_LCDIF
|
|||
|
||||
if DISPLAY_MCUX_DCNANO_LCDIF
|
||||
|
||||
config MCUX_DCNANO_LCDIF_DOUBLE_FRAMEBUFFER
|
||||
bool "Double framebuffer"
|
||||
config MCUX_DCNANO_LCDIF_FB_NUM
|
||||
int "Framebuffers to allocate in driver"
|
||||
default 1
|
||||
range 0 2
|
||||
help
|
||||
Enable dual framebuffer for LCDIF peripheral. Two framebuffers
|
||||
will be allocated, and switched between for each frame.
|
||||
Note that for partial display updates, the prior framebuffer must
|
||||
be copied into the next one. This can have significant performance
|
||||
impact.
|
||||
Number of framebuffers to allocate in DCNANO driver. Driver allocated
|
||||
framebuffers are required to support partial display updates.
|
||||
The driver has been validated to support 0 through 2 framebuffers.
|
||||
Note that hardware will likely perform best if zero driver
|
||||
framebuffers are allocated by the driver, and the application
|
||||
implements double framebuffering by always calling display_write with
|
||||
a buffer equal in size to the connected panel.
|
||||
|
||||
|
||||
config MCUX_DCNANO_LCDIF_MAINTAIN_CACHE
|
||||
bool "Maintain cache coherency"
|
||||
|
|
|
@ -20,12 +20,6 @@
|
|||
|
||||
LOG_MODULE_REGISTER(display_mcux_dcnano_lcdif, CONFIG_DISPLAY_LOG_LEVEL);
|
||||
|
||||
#ifdef CONFIG_MCUX_DCNANO_LCDIF_DOUBLE_FRAMEBUFFER
|
||||
#define MCUX_DCNANO_LCDIF_FB_NUM 2
|
||||
#else
|
||||
#define MCUX_DCNANO_LCDIF_FB_NUM 1
|
||||
#endif
|
||||
|
||||
struct mcux_dcnano_lcdif_config {
|
||||
LCDIF_Type *base;
|
||||
void (*irq_config_func)(const struct device *dev);
|
||||
|
@ -38,11 +32,14 @@ struct mcux_dcnano_lcdif_config {
|
|||
};
|
||||
|
||||
struct mcux_dcnano_lcdif_data {
|
||||
uint8_t *fb[MCUX_DCNANO_LCDIF_FB_NUM];
|
||||
uint8_t fb_idx;
|
||||
/* Pointer to active framebuffer */
|
||||
const uint8_t *active_fb;
|
||||
uint8_t *fb[CONFIG_MCUX_DCNANO_LCDIF_FB_NUM];
|
||||
lcdif_fb_config_t fb_config;
|
||||
uint8_t pixel_bytes;
|
||||
struct k_sem sem;
|
||||
/* Tracks index of next active driver framebuffer */
|
||||
uint8_t next_idx;
|
||||
};
|
||||
|
||||
static int mcux_dcnano_lcdif_write(const struct device *dev, const uint16_t x,
|
||||
|
@ -52,7 +49,6 @@ static int mcux_dcnano_lcdif_write(const struct device *dev, const uint16_t x,
|
|||
{
|
||||
const struct mcux_dcnano_lcdif_config *config = dev->config;
|
||||
struct mcux_dcnano_lcdif_data *data = dev->data;
|
||||
uint8_t next_fb_idx = (data->fb_idx + 1) % MCUX_DCNANO_LCDIF_FB_NUM;
|
||||
uint32_t h_idx;
|
||||
const uint8_t *src;
|
||||
uint8_t *dst;
|
||||
|
@ -62,49 +58,64 @@ static int mcux_dcnano_lcdif_write(const struct device *dev, const uint16_t x,
|
|||
|
||||
LOG_DBG("W=%d, H=%d @%d,%d", desc->width, desc->height, x, y);
|
||||
|
||||
#ifdef CONFIG_MCUX_DCNANO_LCDIF_DOUBLE_FRAMEBUFFER
|
||||
/* If display write is partial, copy current framebuffer to
|
||||
* queued one. Note- this has a significant performance
|
||||
* impact, especially when using external RAM.
|
||||
*/
|
||||
if ((x != 0) ||
|
||||
(y != 0) ||
|
||||
(desc->height != config->dpi_config.panelHeight) ||
|
||||
(desc->width != config->dpi_config.panelWidth)) {
|
||||
memcpy(data->fb[next_fb_idx], data->fb[data->fb_idx],
|
||||
config->fb_bytes);
|
||||
}
|
||||
#endif
|
||||
if ((x == 0) && (y == 0) &&
|
||||
(desc->width == config->dpi_config.panelWidth) &&
|
||||
(desc->height == config->dpi_config.panelHeight) &&
|
||||
(desc->pitch == desc->width)) {
|
||||
/* We can use the display buffer directly, without copying */
|
||||
LOG_DBG("Setting FB from %p->%p",
|
||||
(void *)data->active_fb, (void *)buf);
|
||||
data->active_fb = buf;
|
||||
} else {
|
||||
/* We must use partial framebuffer copy */
|
||||
if (CONFIG_MCUX_DCNANO_LCDIF_FB_NUM == 0) {
|
||||
LOG_ERR("Partial display refresh requires driver framebuffers");
|
||||
return -ENOTSUP;
|
||||
} else if (data->active_fb != data->fb[data->next_idx]) {
|
||||
/*
|
||||
* Copy the entirety of the current framebuffer to new
|
||||
* buffer, since we are changing the active buffer address
|
||||
*/
|
||||
src = data->active_fb;
|
||||
dst = data->fb[data->next_idx];
|
||||
memcpy(dst, src, config->fb_bytes);
|
||||
}
|
||||
/* Write the display update to the active framebuffer */
|
||||
src = buf;
|
||||
dst = data->fb[data->next_idx];
|
||||
dst += data->pixel_bytes * (y * config->dpi_config.panelWidth + x);
|
||||
|
||||
|
||||
src = buf;
|
||||
dst = data->fb[next_fb_idx];
|
||||
dst += data->pixel_bytes * ((y * config->dpi_config.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->dpi_config.panelWidth;
|
||||
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->dpi_config.panelWidth;
|
||||
}
|
||||
LOG_DBG("Setting FB from %p->%p", (void *) data->active_fb,
|
||||
(void *) data->fb[data->next_idx]);
|
||||
/* Set new active framebuffer */
|
||||
data->active_fb = data->fb[data->next_idx];
|
||||
}
|
||||
|
||||
#if defined(CONFIG_HAS_MCUX_CACHE) && defined(CONFIG_MCUX_DCNANO_LCDIF_MAINTAIN_CACHE)
|
||||
CACHE64_InvalidateCacheByRange((uint32_t) data->fb[next_fb_idx],
|
||||
CACHE64_CleanCacheByRange((uint32_t) data->active_fb,
|
||||
config->fb_bytes);
|
||||
#endif
|
||||
|
||||
|
||||
/* Wait for framebuffer completion before writing */
|
||||
k_sem_take(&data->sem, K_FOREVER);
|
||||
k_sem_reset(&data->sem);
|
||||
|
||||
/* Set new framebuffer */
|
||||
LCDIF_SetFrameBufferStride(config->base, 0,
|
||||
config->dpi_config.panelWidth * data->pixel_bytes);
|
||||
LCDIF_SetFrameBufferAddr(config->base, 0,
|
||||
(uint32_t)data->fb[next_fb_idx]);
|
||||
(uint32_t)data->active_fb);
|
||||
LCDIF_SetFrameBufferConfig(config->base, 0, &data->fb_config);
|
||||
|
||||
/* Update current framebuffer IDX */
|
||||
data->fb_idx = next_fb_idx;
|
||||
#if CONFIG_MCUX_DCNANO_LCDIF_FB_NUM != 0
|
||||
/* Update index of active framebuffer */
|
||||
data->next_idx = (data->next_idx + 1) % CONFIG_MCUX_DCNANO_LCDIF_FB_NUM;
|
||||
#endif
|
||||
/* Wait for frame to complete */
|
||||
k_sem_take(&data->sem, K_FOREVER);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -139,13 +150,9 @@ static void mcux_dcnano_lcdif_get_capabilities(const struct device *dev,
|
|||
|
||||
static void *mcux_dcnano_lcdif_get_framebuffer(const struct device *dev)
|
||||
{
|
||||
const struct mcux_dcnano_lcdif_config *config = dev->config;
|
||||
struct mcux_dcnano_lcdif_data *data = dev->data;
|
||||
|
||||
if (IS_ENABLED(CONFIG_MCUX_DCNANO_LCDIF_DOUBLE_FRAMEBUFFER)) {
|
||||
return NULL; /* Direct framebuffer access not supported */
|
||||
} else {
|
||||
return config->fb_ptr;
|
||||
}
|
||||
return (void *)data->active_fb;
|
||||
}
|
||||
|
||||
static int mcux_dcnano_lcdif_display_blanking_off(const struct device *dev)
|
||||
|
@ -224,16 +231,17 @@ static int mcux_dcnano_lcdif_init(const struct device *dev)
|
|||
LCDIF_EnableInterrupts(config->base, kLCDIF_Display0FrameDoneInterrupt);
|
||||
config->irq_config_func(dev);
|
||||
|
||||
data->fb[0] = config->fb_ptr;
|
||||
#ifdef CONFIG_MCUX_DCNANO_LCDIF_DOUBLE_FRAMEBUFFER
|
||||
data->fb[1] = config->fb_ptr + config->fb_bytes;
|
||||
#endif
|
||||
for (int i = 0; i < CONFIG_MCUX_DCNANO_LCDIF_FB_NUM; i++) {
|
||||
/* Record pointers to each driver framebuffer */
|
||||
data->fb[i] = config->fb_ptr + (config->fb_bytes * i);
|
||||
}
|
||||
data->active_fb = config->fb_ptr;
|
||||
|
||||
k_sem_init(&data->sem, 1, 1);
|
||||
|
||||
#ifdef CONFIG_MCUX_DCNANO_LCDIF_EXTERNAL_FB_MEM
|
||||
/* Clear external memory, as it is uninitialized */
|
||||
memset(config->fb_ptr, 0, config->fb_bytes * MCUX_DCNANO_LCDIF_FB_NUM);
|
||||
memset(config->fb_ptr, 0, config->fb_bytes * CONFIG_MCUX_DCNANO_LCDIF_FB_NUM);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
@ -266,9 +274,9 @@ static const struct display_driver_api mcux_dcnano_lcdif_api = {
|
|||
mcux_dcnano_lcdif_frame_buffer_##n[DT_INST_PROP(n, width) * \
|
||||
DT_INST_PROP(n, height) * \
|
||||
MCUX_DCNANO_LCDIF_PIXEL_BYTES(n) * \
|
||||
MCUX_DCNANO_LCDIF_FB_NUM]
|
||||
CONFIG_MCUX_DCNANO_LCDIF_FB_NUM]
|
||||
#define MCUX_DCNANO_LCDIF_FB_SIZE(n) \
|
||||
sizeof(mcux_dcnano_lcdif_frame_buffer_##n) / MCUX_DCNANO_LCDIF_FB_NUM
|
||||
sizeof(mcux_dcnano_lcdif_frame_buffer_##n) / CONFIG_MCUX_DCNANO_LCDIF_FB_NUM
|
||||
#define MCUX_DCNANO_LCDIF_FRAMEBUFFER(n) mcux_dcnano_lcdif_frame_buffer_##n
|
||||
#endif
|
||||
|
||||
|
@ -289,6 +297,7 @@ static const struct display_driver_api mcux_dcnano_lcdif_api = {
|
|||
.enableGamma = false, \
|
||||
.format = DT_INST_PROP(n, pixel_format), \
|
||||
}, \
|
||||
.next_idx = 0, \
|
||||
.pixel_bytes = MCUX_DCNANO_LCDIF_PIXEL_BYTES(n), \
|
||||
}; \
|
||||
struct mcux_dcnano_lcdif_config mcux_dcnano_lcdif_config_##n = { \
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue