Skip to content

Commit 0105a85

Browse files
danieldegrassecarlescufi
authored andcommitted
drivers: display: introduce driver for NXP DCNANO LCDIF peripheral
Introduce driver for NXP DCNANO LCDIF (lcd interface) peripheral, present on iMX.RT500. Currently this driver only supports updating the primary framebuffer, and does not implement support for the cursor buffer present on this IP. Signed-off-by: Daniel DeGrasse <[email protected]>
1 parent 5cc33d2 commit 0105a85

File tree

5 files changed

+503
-0
lines changed

5 files changed

+503
-0
lines changed

drivers/display/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ zephyr_library_sources_ifdef(CONFIG_DISPLAY_MCUX_ELCDIF display_mcux_elcdif.c)
55
zephyr_library_sources_ifdef(CONFIG_DISPLAY_NRF_LED_MATRIX display_nrf_led_matrix.c)
66
zephyr_library_sources_ifdef(CONFIG_DUMMY_DISPLAY display_dummy.c)
77
zephyr_library_sources_ifdef(CONFIG_INTEL_MULTIBOOTFB_DISPLAY display_intel_multibootfb.c)
8+
zephyr_library_sources_ifdef(CONFIG_DISPLAY_MCUX_DCNANO_LCDIF display_mcux_dcnano_lcdif.c)
89
zephyr_library_sources_ifdef(CONFIG_UC81XX uc81xx.c)
910
zephyr_library_sources_ifdef(CONFIG_ILI9XXX display_ili9xxx.c)
1011
zephyr_library_sources_ifdef(CONFIG_ILI9340 display_ili9340.c)

drivers/display/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,6 @@ source "drivers/display/Kconfig.ls0xx"
3636
source "drivers/display/Kconfig.rm68200"
3737
source "drivers/display/Kconfig.max7219"
3838
source "drivers/display/Kconfig.intel_multibootfb"
39+
source "drivers/display/Kconfig.mcux_dcnano_lcdif"
3940

4041
endif # DISPLAY
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Copyright 2023 NXP
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
5+
menuconfig DISPLAY_MCUX_DCNANO_LCDIF
6+
bool "MCUX DCNano LCDIF driver"
7+
default y
8+
depends on DT_HAS_NXP_DCNANO_LCDIF_ENABLED
9+
help
10+
Enable support for mcux DCNano LCDIF driver.
11+
12+
13+
if DISPLAY_MCUX_DCNANO_LCDIF
14+
15+
config MCUX_DCNANO_LCDIF_DOUBLE_FRAMEBUFFER
16+
bool "Double framebuffer"
17+
help
18+
Enable dual framebuffer for LCDIF peripheral. Two framebuffers
19+
will be allocated, and switched between for each frame.
20+
Note that for partial display updates, the prior framebuffer must
21+
be copied into the next one. This can have significant performance
22+
impact.
23+
24+
config MCUX_DCNANO_LCDIF_MAINTAIN_CACHE
25+
bool "Maintain cache coherency"
26+
default y
27+
help
28+
Maintain cache coherency for LCDIF framebuffer. This is generally
29+
required, unless an external framebuffer is utilized with custom
30+
caching settings, or caching is disabled.
31+
32+
config MCUX_DCNANO_LCDIF_EXTERNAL_FB_MEM
33+
bool "Use external memory for framebuffer"
34+
imply MEMC
35+
help
36+
Use external memory for framebuffer. Configures the LCDIF to write
37+
framebuffer data to a memory mapped external device.
38+
39+
Note that no specific linker section is used for this framebuffer, so
40+
if the application uses the external memory for other purposes, care
41+
should be taken to ensure that the memory allocated for the LCDIF
42+
does not overlap with other data. Each allocated LCDIF buffer will
43+
utilize (lcd_width * lcd_height * bytes_per_pixel) bytes of data,
44+
and buffers will be allocated contiguously.
45+
46+
if MCUX_DCNANO_LCDIF_EXTERNAL_FB_MEM
47+
48+
config MCUX_DCNANO_LCDIF_EXTERNAL_FB_ADDR
49+
hex "LCDIF framebuffer address"
50+
help
51+
Address of memory mapped external framebuffer.
52+
Must be 128 byte aligned
53+
54+
endif # MCUX_DCNANO_LCDIF_EXTERNAL_FB_MEM
55+
56+
endif # DISPLAY_MCUX_ELCDIF
Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
/*
2+
* Copyright 2023 NXP
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT nxp_dcnano_lcdif
8+
9+
#include <zephyr/drivers/display.h>
10+
#include <zephyr/drivers/gpio.h>
11+
#include <zephyr/kernel.h>
12+
#include <zephyr/logging/log.h>
13+
#include <zephyr/irq.h>
14+
15+
#include <fsl_lcdif.h>
16+
#ifdef CONFIG_HAS_MCUX_CACHE
17+
#include <fsl_cache.h>
18+
#endif
19+
20+
21+
LOG_MODULE_REGISTER(display_mcux_dcnano_lcdif, CONFIG_DISPLAY_LOG_LEVEL);
22+
23+
#ifdef CONFIG_MCUX_DCNANO_LCDIF_DOUBLE_FRAMEBUFFER
24+
#define MCUX_DCNANO_LCDIF_FB_NUM 2
25+
#else
26+
#define MCUX_DCNANO_LCDIF_FB_NUM 1
27+
#endif
28+
29+
struct mcux_dcnano_lcdif_config {
30+
LCDIF_Type *base;
31+
void (*irq_config_func)(const struct device *dev);
32+
const struct gpio_dt_spec backlight_gpio;
33+
lcdif_dpi_config_t dpi_config;
34+
/* Pointer to start of first framebuffer */
35+
uint8_t *fb_ptr;
36+
/* Number of bytes used for each framebuffer */
37+
uint32_t fb_bytes;
38+
};
39+
40+
struct mcux_dcnano_lcdif_data {
41+
uint8_t *fb[MCUX_DCNANO_LCDIF_FB_NUM];
42+
uint8_t fb_idx;
43+
lcdif_fb_config_t fb_config;
44+
uint8_t pixel_bytes;
45+
struct k_sem sem;
46+
};
47+
48+
static int mcux_dcnano_lcdif_write(const struct device *dev, const uint16_t x,
49+
const uint16_t y,
50+
const struct display_buffer_descriptor *desc,
51+
const void *buf)
52+
{
53+
const struct mcux_dcnano_lcdif_config *config = dev->config;
54+
struct mcux_dcnano_lcdif_data *data = dev->data;
55+
uint8_t next_fb_idx = (data->fb_idx + 1) % MCUX_DCNANO_LCDIF_FB_NUM;
56+
uint32_t h_idx;
57+
const uint8_t *src;
58+
uint8_t *dst;
59+
60+
__ASSERT((data->pixel_bytes * desc->pitch * desc->height) <=
61+
desc->buf_size, "Input buffer too small");
62+
63+
LOG_DBG("W=%d, H=%d @%d,%d", desc->width, desc->height, x, y);
64+
65+
#ifdef CONFIG_MCUX_DCNANO_LCDIF_DOUBLE_FRAMEBUFFER
66+
/* If display write is partial, copy current framebuffer to
67+
* queued one. Note- this has a significant performance
68+
* impact, especially when using external RAM.
69+
*/
70+
if ((x != 0) ||
71+
(y != 0) ||
72+
(desc->height != config->dpi_config.panelHeight) ||
73+
(desc->width != config->dpi_config.panelWidth)) {
74+
memcpy(data->fb[next_fb_idx], data->fb[data->fb_idx],
75+
config->fb_bytes);
76+
}
77+
#endif
78+
79+
80+
src = buf;
81+
dst = data->fb[next_fb_idx];
82+
dst += data->pixel_bytes * ((y * config->dpi_config.panelWidth) + x);
83+
84+
for (h_idx = 0; h_idx < desc->height; h_idx++) {
85+
memcpy(dst, src, data->pixel_bytes * desc->width);
86+
src += data->pixel_bytes * desc->pitch;
87+
dst += data->pixel_bytes * config->dpi_config.panelWidth;
88+
}
89+
90+
#if defined(CONFIG_HAS_MCUX_CACHE) && defined(CONFIG_MCUX_DCNANO_LCDIF_MAINTAIN_CACHE)
91+
CACHE64_InvalidateCacheByRange((uint32_t) data->fb[next_fb_idx],
92+
config->fb_bytes);
93+
#endif
94+
95+
96+
/* Wait for framebuffer completion before writing */
97+
k_sem_take(&data->sem, K_FOREVER);
98+
99+
/* Set new framebuffer */
100+
LCDIF_SetFrameBufferStride(config->base, 0,
101+
config->dpi_config.panelWidth * data->pixel_bytes);
102+
LCDIF_SetFrameBufferAddr(config->base, 0,
103+
(uint32_t)data->fb[next_fb_idx]);
104+
LCDIF_SetFrameBufferConfig(config->base, 0, &data->fb_config);
105+
106+
/* Update current framebuffer IDX */
107+
data->fb_idx = next_fb_idx;
108+
109+
return 0;
110+
}
111+
112+
static void mcux_dcnano_lcdif_get_capabilities(const struct device *dev,
113+
struct display_capabilities *capabilities)
114+
{
115+
const struct mcux_dcnano_lcdif_config *config = dev->config;
116+
struct mcux_dcnano_lcdif_data *data = dev->data;
117+
118+
capabilities->y_resolution = config->dpi_config.panelHeight;
119+
capabilities->x_resolution = config->dpi_config.panelWidth;
120+
capabilities->supported_pixel_formats =
121+
(PIXEL_FORMAT_BGR_565 | PIXEL_FORMAT_ARGB_8888);
122+
capabilities->current_orientation = DISPLAY_ORIENTATION_NORMAL;
123+
switch (data->fb_config.format) {
124+
case kLCDIF_PixelFormatRGB565:
125+
/* Zephyr stores RGB565 as big endian, and LCDIF
126+
* expects little endian. Use BGR565 format to resolve
127+
* this.
128+
*/
129+
capabilities->current_pixel_format = PIXEL_FORMAT_BGR_565;
130+
break;
131+
case kLCDIF_PixelFormatXRGB8888:
132+
capabilities->current_pixel_format = PIXEL_FORMAT_ARGB_8888;
133+
break;
134+
default:
135+
/* Other LCDIF formats don't have a Zephyr enum yet */
136+
return;
137+
}
138+
}
139+
140+
static void *mcux_dcnano_lcdif_get_framebuffer(const struct device *dev)
141+
{
142+
const struct mcux_dcnano_lcdif_config *config = dev->config;
143+
144+
if (IS_ENABLED(CONFIG_MCUX_DCNANO_LCDIF_DOUBLE_FRAMEBUFFER)) {
145+
return NULL; /* Direct framebuffer access not supported */
146+
} else {
147+
return config->fb_ptr;
148+
}
149+
}
150+
151+
static int mcux_dcnano_lcdif_display_blanking_off(const struct device *dev)
152+
{
153+
const struct mcux_dcnano_lcdif_config *config = dev->config;
154+
155+
return gpio_pin_set_dt(&config->backlight_gpio, 1);
156+
}
157+
158+
static int mcux_dcnano_lcdif_display_blanking_on(const struct device *dev)
159+
{
160+
const struct mcux_dcnano_lcdif_config *config = dev->config;
161+
162+
return gpio_pin_set_dt(&config->backlight_gpio, 0);
163+
}
164+
165+
static int mcux_dcnano_lcdif_set_pixel_format(const struct device *dev,
166+
const enum display_pixel_format
167+
pixel_format)
168+
{
169+
struct mcux_dcnano_lcdif_data *data = dev->data;
170+
171+
switch (pixel_format) {
172+
case PIXEL_FORMAT_BGR_565:
173+
/* Zephyr stores RGB565 as big endian, and LCDIF
174+
* expects little endian. Use BGR565 format to resolve
175+
* this.
176+
*/
177+
data->fb_config.format = kLCDIF_PixelFormatRGB565;
178+
data->pixel_bytes = 2;
179+
break;
180+
case PIXEL_FORMAT_ARGB_8888:
181+
data->fb_config.format = kLCDIF_PixelFormatXRGB8888;
182+
data->pixel_bytes = 4;
183+
break;
184+
default:
185+
return -ENOTSUP;
186+
}
187+
return 0;
188+
}
189+
190+
static void mcux_dcnano_lcdif_isr(const struct device *dev)
191+
{
192+
const struct mcux_dcnano_lcdif_config *config = dev->config;
193+
struct mcux_dcnano_lcdif_data *data = dev->data;
194+
uint32_t status;
195+
196+
status = LCDIF_GetAndClearInterruptPendingFlags(config->base);
197+
198+
if (0 != (status & kLCDIF_Display0FrameDoneInterrupt)) {
199+
k_sem_give(&data->sem);
200+
}
201+
}
202+
203+
static int mcux_dcnano_lcdif_init(const struct device *dev)
204+
{
205+
const struct mcux_dcnano_lcdif_config *config = dev->config;
206+
struct mcux_dcnano_lcdif_data *data = dev->data;
207+
int ret;
208+
209+
ret = gpio_pin_configure_dt(&config->backlight_gpio, GPIO_OUTPUT_ACTIVE);
210+
if (ret) {
211+
return ret;
212+
}
213+
214+
LCDIF_Init(config->base);
215+
216+
LCDIF_DpiModeSetConfig(config->base, 0, &config->dpi_config);
217+
218+
LCDIF_EnableInterrupts(config->base, kLCDIF_Display0FrameDoneInterrupt);
219+
config->irq_config_func(dev);
220+
221+
data->fb[0] = config->fb_ptr;
222+
#ifdef CONFIG_MCUX_DCNANO_LCDIF_DOUBLE_FRAMEBUFFER
223+
data->fb[1] = config->fb_ptr + config->fb_bytes;
224+
#endif
225+
226+
k_sem_init(&data->sem, 1, 1);
227+
228+
#ifdef CONFIG_MCUX_DCNANO_LCDIF_EXTERNAL_FB_MEM
229+
/* Clear external memory, as it is uninitialized */
230+
memset(config->fb_ptr, 0, config->fb_bytes * MCUX_DCNANO_LCDIF_FB_NUM);
231+
#endif
232+
233+
return 0;
234+
}
235+
236+
static const struct display_driver_api mcux_dcnano_lcdif_api = {
237+
.blanking_on = mcux_dcnano_lcdif_display_blanking_on,
238+
.blanking_off = mcux_dcnano_lcdif_display_blanking_off,
239+
.set_pixel_format = mcux_dcnano_lcdif_set_pixel_format,
240+
.write = mcux_dcnano_lcdif_write,
241+
.get_capabilities = mcux_dcnano_lcdif_get_capabilities,
242+
.get_framebuffer = mcux_dcnano_lcdif_get_framebuffer,
243+
};
244+
245+
/* This macro evaluates to 4 when the pixel format enum is set to 4,
246+
* and 2 for all other enum values.
247+
*/
248+
#define MCUX_DCNANO_LCDIF_PIXEL_BYTES(n) \
249+
(((DT_INST_ENUM_IDX(n, pixel_format) / 4) * 2) + 2)
250+
251+
/* When using external framebuffer mem, we should not allocate framebuffers
252+
* in SRAM. Instead, we use external framebuffer address and size
253+
* from devicetree.
254+
*/
255+
#ifdef CONFIG_MCUX_DCNANO_LCDIF_EXTERNAL_FB_MEM
256+
#define MCUX_DCNANO_LCDIF_FRAMEBUFFER_DECL(n)
257+
#define MCUX_DCNANO_LCDIF_FB_SIZE(n) DT_INST_PROP(n, width) * \
258+
DT_INST_PROP(n, height) * MCUX_DCNANO_LCDIF_PIXEL_BYTES(n)
259+
#define MCUX_DCNANO_LCDIF_FRAMEBUFFER(n) \
260+
(uint8_t *)CONFIG_MCUX_DCNANO_LCDIF_EXTERNAL_FB_ADDR
261+
#else
262+
#define MCUX_DCNANO_LCDIF_FRAMEBUFFER_DECL(n) uint8_t __aligned(LCDIF_FB_ALIGN) \
263+
mcux_dcnano_lcdif_frame_buffer_##n[DT_INST_PROP(n, width) * \
264+
DT_INST_PROP(n, height) * \
265+
MCUX_DCNANO_LCDIF_PIXEL_BYTES(n) * \
266+
MCUX_DCNANO_LCDIF_FB_NUM]
267+
#define MCUX_DCNANO_LCDIF_FB_SIZE(n) \
268+
sizeof(mcux_dcnano_lcdif_frame_buffer_##n) / MCUX_DCNANO_LCDIF_FB_NUM
269+
#define MCUX_DCNANO_LCDIF_FRAMEBUFFER(n) mcux_dcnano_lcdif_frame_buffer_##n
270+
#endif
271+
272+
#define MCUX_DCNANO_LCDIF_DEVICE_INIT(n) \
273+
static void mcux_dcnano_lcdif_config_func_##n(const struct device *dev) \
274+
{ \
275+
IRQ_CONNECT(DT_INST_IRQN(n), \
276+
DT_INST_IRQ(n, priority), \
277+
mcux_dcnano_lcdif_isr, \
278+
DEVICE_DT_INST_GET(n), \
279+
0); \
280+
irq_enable(DT_INST_IRQN(n)); \
281+
} \
282+
MCUX_DCNANO_LCDIF_FRAMEBUFFER_DECL(n); \
283+
struct mcux_dcnano_lcdif_data mcux_dcnano_lcdif_data_##n = { \
284+
.fb_config = { \
285+
.enable = true, \
286+
.enableGamma = false, \
287+
.format = DT_INST_ENUM_IDX(n, pixel_format), \
288+
}, \
289+
.pixel_bytes = MCUX_DCNANO_LCDIF_PIXEL_BYTES(n), \
290+
}; \
291+
struct mcux_dcnano_lcdif_config mcux_dcnano_lcdif_config_##n = { \
292+
.base = (LCDIF_Type *) DT_INST_REG_ADDR(n), \
293+
.irq_config_func = mcux_dcnano_lcdif_config_func_##n, \
294+
.backlight_gpio = GPIO_DT_SPEC_INST_GET(n, backlight_gpios), \
295+
.dpi_config = { \
296+
.panelWidth = DT_INST_PROP(n, width), \
297+
.panelHeight = DT_INST_PROP(n, height), \
298+
.hsw = DT_INST_PROP(n, hsync), \
299+
.hfp = DT_INST_PROP(n, hfp), \
300+
.hbp = DT_INST_PROP(n, hbp), \
301+
.vsw = DT_INST_PROP(n, vsync), \
302+
.vfp = DT_INST_PROP(n, vfp), \
303+
.vbp = DT_INST_PROP(n, vbp), \
304+
.polarityFlags = DT_INST_PROP(n, polarity), \
305+
.format = DT_INST_ENUM_IDX(n, data_bus_width), \
306+
}, \
307+
.fb_ptr = MCUX_DCNANO_LCDIF_FRAMEBUFFER(n), \
308+
.fb_bytes = MCUX_DCNANO_LCDIF_FB_SIZE(n), \
309+
}; \
310+
DEVICE_DT_INST_DEFINE(n, \
311+
&mcux_dcnano_lcdif_init, \
312+
NULL, \
313+
&mcux_dcnano_lcdif_data_##n, \
314+
&mcux_dcnano_lcdif_config_##n, \
315+
POST_KERNEL, \
316+
CONFIG_DISPLAY_INIT_PRIORITY, \
317+
&mcux_dcnano_lcdif_api);
318+
319+
DT_INST_FOREACH_STATUS_OKAY(MCUX_DCNANO_LCDIF_DEVICE_INIT)

0 commit comments

Comments
 (0)