Skip to content

Commit 3bb88d3

Browse files
WinteriWangcfriedt
authored andcommitted
driver: mucx_lcdifv3: support both full and partial refresh
If the framebuffer size is same as the full screen, full refresh is applied. Otherwise, partial refresh is applied. Signed-off-by: Winteri Wang <[email protected]> Signed-off-by: Jiafei Pan <[email protected]> Signed-off-by: Ruoshan Shi <[email protected]>
1 parent 0da5142 commit 3bb88d3

File tree

2 files changed

+122
-52
lines changed

2 files changed

+122
-52
lines changed

drivers/display/Kconfig.mcux_lcdifv3

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,23 @@
22
# SPDX-License-Identifier: Apache-2.0
33

44

5-
config DISPLAY_MCUX_LCDIFV3
5+
menuconfig DISPLAY_MCUX_LCDIFV3
66
bool "MCUX LCDIFV3 driver"
77
default y
88
depends on DT_HAS_NXP_IMX_LCDIFV3_ENABLED
99
depends on CLOCK_CONTROL
1010
select INIT_VIDEO_PLL
1111
help
1212
Enable support for mcux LCDIFV3 driver.
13+
14+
if DISPLAY_MCUX_LCDIFV3
15+
16+
config MCUX_LCDIFV3_FB_NUM
17+
int "Framebuffers to be allocated in driver"
18+
default 2
19+
range 0 2
20+
help
21+
Number of framebuffers to be allocated in LCDIFV3 driver. Driver allocates
22+
framebuffers in order to support partial display update.
23+
24+
endif # DISPLAY_MCUX_LCDIFV3

drivers/display/display_mcux_lcdifv3.c

Lines changed: 109 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,10 @@
1010
#include <zephyr/drivers/clock_control.h>
1111
#include <zephyr/kernel.h>
1212
#include <fsl_lcdifv3.h>
13-
1413
#include <zephyr/logging/log.h>
1514
#include <zephyr/irq.h>
1615
#include <zephyr/cache.h>
1716

18-
#define MCUX_LCDIFV3_FB_NUM 1
19-
2017
LOG_MODULE_REGISTER(display_mcux_lcdifv3, CONFIG_DISPLAY_LOG_LEVEL);
2118

2219
/* Required by DEVICE_MMIO_NAMED_* macros */
@@ -40,56 +37,103 @@ struct mcux_lcdifv3_config {
4037
lcdifv3_buffer_config_t buffer_config;
4138
lcdifv3_display_config_t display_config;
4239
enum display_pixel_format pixel_format;
43-
size_t pixel_bytes;
40+
uint8_t *fb_ptr;
4441
size_t fb_bytes;
4542
};
4643

4744
struct mcux_lcdifv3_data {
4845
DEVICE_MMIO_NAMED_RAM(reg_base);
49-
uint8_t *fb_ptr;
50-
uint8_t *fb[MCUX_LCDIFV3_FB_NUM];
46+
/* Pointer to active framebuffer */
47+
const uint8_t *active_fb;
48+
uint8_t *fb[CONFIG_MCUX_LCDIFV3_FB_NUM];
49+
uint8_t pixel_bytes;
5150
struct k_sem sem;
52-
uint8_t write_idx;
51+
/* Tracks index of next active driver framebuffer */
52+
uint8_t next_idx;
5353
};
5454

55+
static void dump_reg(LCDIF_Type *base)
56+
{
57+
/* Dump LCDIF Registers */
58+
LOG_DBG("CTRL: 0x%x", base->CTRL.RW);
59+
LOG_DBG("DISP_PARA: 0x%x", base->DISP_PARA);
60+
LOG_DBG("DISP_SIZE: 0x%x", base->DISP_SIZE);
61+
LOG_DBG("HSYN_PARA: 0x%x", base->HSYN_PARA);
62+
LOG_DBG("VSYN_PARA: 0x%x", base->VSYN_PARA);
63+
LOG_DBG("VSYN_HSYN_WIDTH: 0x%x", base->VSYN_HSYN_WIDTH);
64+
LOG_DBG("INT_STATUS_D0: 0x%x", base->INT_STATUS_D0);
65+
LOG_DBG("INT_STATUS_D1: 0x%x", base->INT_STATUS_D1);
66+
LOG_DBG("CTRLDESCL_1: 0x%x", base->CTRLDESCL_1[0]);
67+
LOG_DBG("CTRLDESCL_3: 0x%x", base->CTRLDESCL_3[0]);
68+
LOG_DBG("CTRLDESCL_LOW_4: 0x%x", base->CTRLDESCL_LOW_4[0]);
69+
LOG_DBG("CTRLDESCL_HIGH_4: 0x%x", base->CTRLDESCL_HIGH_4[0]);
70+
LOG_DBG("CTRLDESCL_5: 0x%x", base->CTRLDESCL_5[0]);
71+
}
72+
5573
static int mcux_lcdifv3_write(const struct device *dev, const uint16_t x, const uint16_t y,
5674
const struct display_buffer_descriptor *desc, const void *buf)
5775
{
5876
const struct mcux_lcdifv3_config *config = dev->config;
59-
struct mcux_lcdifv3_data *dev_data = dev->data;
77+
struct mcux_lcdifv3_data *data = dev->data;
6078
LCDIF_Type *base = (LCDIF_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
61-
62-
if ((config->pixel_bytes * desc->pitch * desc->height) > desc->buf_size) {
63-
LOG_ERR("Input buffer too small");
64-
return -ENOTSUP;
79+
uint32_t h_idx;
80+
const uint8_t *src;
81+
uint8_t *dst;
82+
83+
__ASSERT((data->pixel_bytes * desc->pitch * desc->height) <= desc->buf_size,
84+
"Input buffer too small");
85+
86+
LOG_DBG("W=%d, H=%d @%d,%d", desc->width, desc->height, x, y);
87+
88+
if ((x == 0) && (y == 0) && (desc->width == config->display_config.panelWidth) &&
89+
(desc->height == config->display_config.panelHeight) && (desc->pitch == desc->width)) {
90+
/* We can use the display buffer directly, without copying */
91+
LOG_DBG("Setting FB from %p->%p", (void *)data->active_fb, (void *)buf);
92+
data->active_fb = buf;
93+
} else {
94+
/* We must use partial framebuffer copy */
95+
if (CONFIG_MCUX_LCDIFV3_FB_NUM == 0) {
96+
LOG_ERR("Partial display refresh requires driver framebuffers");
97+
return -ENOTSUP;
98+
} else if (data->active_fb != data->fb[data->next_idx]) {
99+
/*
100+
* Copy the entirety of the current framebuffer to new
101+
* buffer, since we are changing the active buffer address
102+
*/
103+
src = data->active_fb;
104+
dst = data->fb[data->next_idx];
105+
memcpy(dst, src, config->fb_bytes);
106+
}
107+
/* Write the display update to the active framebuffer */
108+
src = buf;
109+
dst = data->fb[data->next_idx];
110+
dst += data->pixel_bytes * (y * config->display_config.panelWidth + x);
111+
112+
for (h_idx = 0; h_idx < desc->height; h_idx++) {
113+
memcpy(dst, src, data->pixel_bytes * desc->width);
114+
src += data->pixel_bytes * desc->pitch;
115+
dst += data->pixel_bytes * config->display_config.panelWidth;
116+
}
117+
LOG_DBG("Setting FB from %p->%p", (void *)data->active_fb,
118+
(void *)data->fb[data->next_idx]);
119+
/* Set new active framebuffer */
120+
data->active_fb = data->fb[data->next_idx];
65121
}
66122

67-
LOG_DBG("W=%d, H=%d, @%d,%d", desc->width, desc->height, x, y);
123+
sys_cache_data_flush_and_invd_range((void *)data->active_fb, config->fb_bytes);
68124

69-
/* Dump LCDIF Registers */
70-
LOG_DBG("CTRL: 0x%x\n", base->CTRL.RW);
71-
LOG_DBG("DISP_PARA: 0x%x\n", base->DISP_PARA);
72-
LOG_DBG("DISP_SIZE: 0x%x\n", base->DISP_SIZE);
73-
LOG_DBG("HSYN_PARA: 0x%x\n", base->HSYN_PARA);
74-
LOG_DBG("VSYN_PARA: 0x%x\n", base->VSYN_PARA);
75-
LOG_DBG("VSYN_HSYN_WIDTH: 0x%x\n", base->VSYN_HSYN_WIDTH);
76-
LOG_DBG("INT_STATUS_D0: 0x%x\n", base->INT_STATUS_D0);
77-
LOG_DBG("INT_STATUS_D1: 0x%x\n", base->INT_STATUS_D1);
78-
LOG_DBG("CTRLDESCL_1: 0x%x\n", base->CTRLDESCL_1[0]);
79-
LOG_DBG("CTRLDESCL_3: 0x%x\n", base->CTRLDESCL_3[0]);
80-
LOG_DBG("CTRLDESCL_LOW_4: 0x%x\n", base->CTRLDESCL_LOW_4[0]);
81-
LOG_DBG("CTRLDESCL_HIGH_4: 0x%x\n", base->CTRLDESCL_HIGH_4[0]);
82-
LOG_DBG("CTRLDESCL_5: 0x%x\n", base->CTRLDESCL_5[0]);
83-
84-
/* wait for the next frame done */
85-
k_sem_reset(&dev_data->sem);
86-
87-
sys_cache_data_flush_and_invd_range((void *)buf, desc->buf_size);
88-
LCDIFV3_SetLayerSize(base, 0, desc->width, desc->height);
89-
LCDIFV3_SetLayerBufferAddr(base, 0, (uint32_t)(uintptr_t)buf);
125+
k_sem_reset(&data->sem);
126+
127+
/* Set new framebuffer */
128+
LCDIFV3_SetLayerBufferAddr(base, 0, (uint32_t)(uintptr_t)data->active_fb);
90129
LCDIFV3_TriggerLayerShadowLoad(base, 0);
91130

92-
k_sem_take(&dev_data->sem, K_FOREVER);
131+
#if CONFIG_MCUX_LCDIFV3_FB_NUM != 0
132+
/* Update index of active framebuffer */
133+
data->next_idx = (data->next_idx + 1) % CONFIG_MCUX_LCDIFV3_FB_NUM;
134+
#endif
135+
/* Wait for frame to complete */
136+
k_sem_take(&data->sem, K_FOREVER);
93137

94138
return 0;
95139
}
@@ -98,7 +142,7 @@ static void *mcux_lcdifv3_get_framebuffer(const struct device *dev)
98142
{
99143
struct mcux_lcdifv3_data *dev_data = dev->data;
100144

101-
return dev_data->fb_ptr;
145+
return (void *)dev_data->active_fb;
102146
}
103147

104148
static void mcux_lcdifv3_get_capabilities(const struct device *dev,
@@ -172,21 +216,28 @@ static int mcux_axi_apb_configure_clock(const struct device *dev)
172216
static int mcux_lcdifv3_init(const struct device *dev)
173217
{
174218
const struct mcux_lcdifv3_config *config = dev->config;
175-
struct mcux_lcdifv3_data *dev_data = dev->data;
219+
struct mcux_lcdifv3_data *data = dev->data;
176220
uint32_t clk_freq;
177221

178222
DEVICE_MMIO_NAMED_MAP(dev, reg_base, K_MEM_CACHE_NONE | K_MEM_DIRECT_MAP);
179223
LCDIF_Type *base = (LCDIF_Type *)DEVICE_MMIO_NAMED_GET(dev, reg_base);
180224

181-
dev_data->fb[0] = dev_data->fb_ptr;
225+
config->irq_config_func(dev);
182226

183-
k_sem_init(&dev_data->sem, 1, 1);
227+
for (int i = 0; i < CONFIG_MCUX_LCDIFV3_FB_NUM; i++) {
228+
/* Record pointers to each driver framebuffer */
229+
data->fb[i] = config->fb_ptr + (config->fb_bytes * i);
230+
}
231+
data->active_fb = config->fb_ptr;
184232

185-
config->irq_config_func(dev);
233+
k_sem_init(&data->sem, 1, 1);
234+
235+
/* Clear external memory, as it is uninitialized */
236+
memset(config->fb_ptr, 0, config->fb_bytes * CONFIG_MCUX_LCDIFV3_FB_NUM);
186237

187238
/* configure disp_pix_clk */
188239
if (!device_is_ready(config->disp_pix_clk_dev)) {
189-
LOG_ERR("cam_pix clock control device not ready\n");
240+
LOG_ERR("cam_pix clock control device not ready");
190241
return -ENODEV;
191242
}
192243

@@ -195,7 +246,7 @@ static int mcux_lcdifv3_init(const struct device *dev)
195246

196247
if (clock_control_get_rate(config->disp_pix_clk_dev, config->disp_pix_clk_subsys,
197248
&clk_freq)) {
198-
LOG_ERR("Failed to get disp_pix_clk\n");
249+
LOG_ERR("Failed to get disp_pix_clk");
199250
return -EINVAL;
200251
}
201252
LOG_INF("disp_pix clock frequency %d", clk_freq);
@@ -219,11 +270,13 @@ static int mcux_lcdifv3_init(const struct device *dev)
219270
LCDIFV3_SetLayerSize(base, 0, display_config.panelWidth, display_config.panelHeight);
220271
LCDIFV3_EnableLayer(base, 0, true);
221272
LCDIFV3_EnablePlanePanic(base);
222-
LCDIFV3_SetLayerBufferAddr(base, 0, (uint64_t)dev_data->fb[0]);
273+
LCDIFV3_SetLayerBufferAddr(base, 0, (uint64_t)data->fb[0]);
223274
LCDIFV3_TriggerLayerShadowLoad(base, 0);
224275
LCDIFV3_EnableInterrupts(base, kLCDIFV3_VerticalBlankingInterrupt);
225276

226-
LOG_INF("%s init succeeded\n", dev->name);
277+
LOG_INF("%s init succeeded", dev->name);
278+
279+
dump_reg(base);
227280

228281
return 0;
229282
}
@@ -245,13 +298,24 @@ static const struct display_driver_api mcux_lcdifv3_api = {
245298
? 2 \
246299
: ((DT_INST_ENUM_IDX(id, pixel_format) == 1) ? 3 : 4))
247300

301+
#define MCUX_LCDIFV3_FRAMEBUFFER_DECL(id) \
302+
uint8_t __aligned(64) \
303+
mcux_lcdifv3_frame_buffer_##id[DT_INST_PROP(id, width) * DT_INST_PROP(id, height) * \
304+
GET_PIXEL_BYTES(id) * CONFIG_MCUX_LCDIFV3_FB_NUM]
305+
#define MCUX_LCDIFV3_FRAMEBUFFER(id) mcux_lcdifv3_frame_buffer_##id
306+
248307
#define MCUX_LCDIFV3_DEVICE_INIT(id) \
249308
static void mcux_lcdifv3_config_func_##id(const struct device *dev) \
250309
{ \
251310
IRQ_CONNECT(DT_INST_IRQN(id), DT_INST_IRQ(id, priority), mcux_lcdifv3_isr, \
252311
DEVICE_DT_INST_GET(id), 0); \
253312
irq_enable(DT_INST_IRQN(id)); \
254313
} \
314+
MCUX_LCDIFV3_FRAMEBUFFER_DECL(id); \
315+
static struct mcux_lcdifv3_data mcux_lcdifv3_data_##id = { \
316+
.next_idx = 0, \
317+
.pixel_bytes = GET_PIXEL_BYTES(id), \
318+
}; \
255319
static const struct mcux_lcdifv3_config mcux_lcdifv3_config_##id = { \
256320
DEVICE_MMIO_NAMED_ROM_INIT(reg_base, DT_DRV_INST(id)), \
257321
.disp_pix_clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_IDX(id, 0)), \
@@ -298,16 +362,10 @@ static const struct display_driver_api mcux_lcdifv3_api = {
298362
: kLCDIFV3_DriveDataOnFallingClkEdge), \
299363
}, \
300364
.pixel_format = GET_PIXEL_FORMAT(id), \
301-
.pixel_bytes = GET_PIXEL_BYTES(id), \
365+
.fb_ptr = MCUX_LCDIFV3_FRAMEBUFFER(id), \
302366
.fb_bytes = \
303367
DT_INST_PROP(id, width) * DT_INST_PROP(id, height) * GET_PIXEL_BYTES(id), \
304368
}; \
305-
static uint8_t \
306-
__aligned(64) frame_buffer_##id[MCUX_LCDIFV3_FB_NUM * DT_INST_PROP(id, width) * \
307-
DT_INST_PROP(id, height) * GET_PIXEL_BYTES(id)]; \
308-
static struct mcux_lcdifv3_data mcux_lcdifv3_data_##id = { \
309-
.fb_ptr = frame_buffer_##id, \
310-
}; \
311369
DEVICE_DT_INST_DEFINE(id, &mcux_lcdifv3_init, NULL, &mcux_lcdifv3_data_##id, \
312370
&mcux_lcdifv3_config_##id, POST_KERNEL, \
313371
CONFIG_DISPLAY_INIT_PRIORITY, &mcux_lcdifv3_api);

0 commit comments

Comments
 (0)