diff --git a/examples/fb/fb_main.c b/examples/fb/fb_main.c index 3d237df053a..627be1d005a 100644 --- a/examples/fb/fb_main.c +++ b/examples/fb/fb_main.c @@ -155,6 +155,7 @@ static void pan_display(FAR struct fb_state_s *state) { struct pollfd pfd; int ret; + pfd.fd = state->fd; pfd.events = POLLOUT; @@ -193,6 +194,7 @@ static int fbdev_get_pinfo(int fd, FAR struct fb_planeinfo_s *pinfo) if (ioctl(fd, FBIOGET_PLANEINFO, (unsigned long)((uintptr_t)pinfo)) < 0) { int errcode = errno; + fprintf(stderr, "ERROR: ioctl(FBIOGET_PLANEINFO) failed: %d\n", errcode); return EXIT_FAILURE; @@ -226,72 +228,51 @@ static int fbdev_get_pinfo(int fd, FAR struct fb_planeinfo_s *pinfo) static int fb_init_mem2(FAR struct fb_state_s *state) { - int ret; - uintptr_t buf_offset; struct fb_planeinfo_s pinfo; - FAR void *fbmem; + FAR void *mem2; + size_t framesize; + off_t offset; + int ret; + + /* Get display[1] planeinfo */ memset(&pinfo, 0, sizeof(pinfo)); pinfo.display = state->pinfo.display + 1; if ((ret = fbdev_get_pinfo(state->fd, &pinfo)) < 0) { - return EXIT_FAILURE; + return ret; } - fbmem = mmap(NULL, pinfo.fblen, PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_FILE, state->fd, 0); - - if (fbmem == MAP_FAILED) - { - int errcode = errno; - fprintf(stderr, "ERROR: ioctl(FBIOGET_PLANEINFO) failed: %d\n", - errcode); - return EXIT_FAILURE; - } - - /* Check bpp */ - if (pinfo.bpp != state->pinfo.bpp) { - fprintf(stderr, "ERROR: fbmem2 is incorrect\n"); + fprintf(stderr, "ERROR: fbmem2 bpp mismatch\n"); return -EINVAL; } - /* Check the buffer address offset, - * It needs to be divisible by pinfo.stride - */ - - buf_offset = fbmem - state->fbmem; + /* Use virtual offset = fblen = stride * yres */ - if ((buf_offset % state->pinfo.stride) != 0) - { - fprintf(stderr, "ERROR: It is detected that buf_offset(%" PRIuPTR ") " - "and stride(%d) are not divisible, please ensure " - "that the driver handles the address offset by itself.\n", - buf_offset, state->pinfo.stride); - } + framesize = state->vinfo.yres * state->pinfo.stride; + offset = framesize; + state->mem2_yoffset = state->vinfo.yres; - /* Calculate the address and yoffset of fbmem2 */ - - if (buf_offset == 0) + mem2 = mmap(NULL, framesize, + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_FILE, + state->fd, offset); + if (mem2 == MAP_FAILED) { - /* Use consecutive fbmem2. */ - - state->mem2_yoffset = state->vinfo.yres; - state->fbmem2 = fbmem + state->mem2_yoffset * pinfo.stride; - printf("Use consecutive fbmem2 = %p, yoffset = %" PRIu32"\n", - state->fbmem2, state->mem2_yoffset); + fprintf(stderr, "ERROR: mmap mem2 failed: %d\n", errno); + return -errno; } - else - { - /* Use non-consecutive fbmem2. */ - state->mem2_yoffset = buf_offset / state->pinfo.stride; - state->fbmem2 = fbmem; - printf("Use non-consecutive fbmem2 = %p, yoffset = %" PRIu32"\n", - state->fbmem2, state->mem2_yoffset); - } + state->fbmem2 = mem2; + fprintf(stderr, "Mapped FB mem2: %p, yoffset: %" PRIu32 + ", gap: %td (%s)\n", + state->fbmem2, state->mem2_yoffset, + (char *)mem2 - ((char *)state->fbmem + framesize), + ((char *)mem2 == (char *)state->fbmem + framesize) ? + "consecutive" : "non-consecutive"); return 0; } @@ -505,6 +486,7 @@ static void present_area(FAR struct fb_state_s *state, int yoffset = state->act_fbmem == state->fbmem ? 0 : state->mem2_yoffset; + update.y += yoffset; ret = ioctl(state->fd, FBIO_UPDATE, @@ -512,6 +494,7 @@ static void present_area(FAR struct fb_state_s *state, if (ret < 0) { int errcode = errno; + fprintf(stderr, "ERROR: ioctl(FBIO_UPDATE) failed: %d\n", errcode); } @@ -587,10 +570,12 @@ int main(int argc, FAR char *argv[]) /* Open the framebuffer driver */ + memset(&state, 0, sizeof(state)); state.fd = open(fbdev, O_RDWR); if (state.fd < 0) { int errcode = errno; + fprintf(stderr, "ERROR: Failed to open %s: %d\n", fbdev, errcode); return EXIT_FAILURE; } @@ -602,6 +587,7 @@ int main(int argc, FAR char *argv[]) if (ret < 0) { int errcode = errno; + fprintf(stderr, "ERROR: ioctl(FBIOGET_VIDEOINFO) failed: %d\n", errcode); close(state.fd); @@ -623,6 +609,7 @@ int main(int argc, FAR char *argv[]) if (ret < 0) { int errcode = errno; + fprintf(stderr, "ERROR: ioctl(FBIO_SELECT_OVERLAY) failed: %d\n", errcode); close(state.fd); @@ -637,6 +624,7 @@ int main(int argc, FAR char *argv[]) if (ret < 0) { int errcode = errno; + fprintf(stderr, "ERROR: ioctl(FBIOGET_OVERLAYINFO) failed: %d\n", errcode); close(state.fd); @@ -665,6 +653,7 @@ int main(int argc, FAR char *argv[]) if (ret < 0) { int errcode = errno; + fprintf(stderr, "ERROR: ioctl(FBIO_SELECT_OVERLAY) failed: %d\n", errcode); close(state.fd); @@ -693,13 +682,14 @@ int main(int argc, FAR char *argv[]) if (state.fbmem == MAP_FAILED) { int errcode = errno; + fprintf(stderr, "ERROR: mmap() failed: %d\n", errcode); close(state.fd); return EXIT_FAILURE; } - printf("Mapped FB: %p\n", state.fbmem); + printf("Mapped FB mem: %p\n", state.fbmem); /* double buffer mode */ @@ -707,7 +697,9 @@ int main(int argc, FAR char *argv[]) { if ((ret = fb_init_mem2(&state)) < 0) { - goto out; + munmap(state.fbmem, state.pinfo.fblen); + close(state.fd); + return ret; } } @@ -774,8 +766,9 @@ int main(int argc, FAR char *argv[]) printf("Test finished\n"); ret = EXIT_SUCCESS; -out: + munmap(state.fbmem, state.pinfo.fblen); + munmap(state.fbmem2, state.pinfo.fblen); close(state.fd); return ret; diff --git a/graphics/lvgl/0001-nuttx-fbdev-use-virtual-framebuffer-offset.patch b/graphics/lvgl/0001-nuttx-fbdev-use-virtual-framebuffer-offset.patch new file mode 100644 index 00000000000..7fe10f5b7fa --- /dev/null +++ b/graphics/lvgl/0001-nuttx-fbdev-use-virtual-framebuffer-offset.patch @@ -0,0 +1,168 @@ +From: jianglianfang +Date: Sat, 9 May 2026 19:07:22 +0800 +Subject: [PATCH] nuttx: fbdev: use virtual framebuffer offset + +Minimal backport of e2a03f78735104a582a938281dec7d4fd0f3866b +(Gerrit 7769608, patch set 1) to LVGL v9.2.2. + +Map the second frame separately at stride * yres and use yres for +panning instead of deriving a virtual offset from physical addresses. +Unlike the original change, v9.2.2 needs the second mapping introduced +here. Release both mappings on failure, rebinding and display deletion; +keep the existing off-screen mode. Avoid subtracting unrelated mapped +pointers for diagnostics. + +Signed-off-by: jianglianfang +--- +diff --git a/src/drivers/nuttx/lv_nuttx_fbdev.c b/src/drivers/nuttx/lv_nuttx_fbdev.c +--- a/src/drivers/nuttx/lv_nuttx_fbdev.c ++++ b/src/drivers/nuttx/lv_nuttx_fbdev.c +@@ -54,6 +54,7 @@ + static lv_color_format_t fb_fmt_to_color_format(int fmt); + static int fbdev_get_pinfo(int fd, struct fb_planeinfo_s * pinfo); + static int fbdev_init_mem2(lv_nuttx_fb_t * dsc); ++static void fbdev_release(lv_nuttx_fb_t * dsc); + static void display_refr_timer_cb(lv_timer_t * tmr); + static void display_release_cb(lv_event_t * e); + #if defined(CONFIG_FB_UPDATE) +@@ -96,7 +97,8 @@ + LV_ASSERT(disp && file); + lv_nuttx_fb_t * dsc = lv_display_get_driver_data(disp); + +- if(dsc->fd >= 0) close(dsc->fd); ++ lv_display_set_draw_buffers(disp, NULL, NULL); ++ fbdev_release(dsc); + + /* Open the file for reading and writing*/ + +@@ -125,14 +127,16 @@ + + lv_color_format_t color_format = fb_fmt_to_color_format(dsc->vinfo.fmt); + if(color_format == LV_COLOR_FORMAT_UNKNOWN) { ++ ret = -EINVAL; + goto errout; + } + + dsc->mem = mmap(NULL, dsc->pinfo.fblen, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_FILE, dsc->fd, 0); + if(dsc->mem == MAP_FAILED) { +- LV_LOG_ERROR("ioctl(FBIOGET_PLANEINFO) failed: %d", errno); ++ LV_LOG_ERROR("mmap mem failed: %d", errno); + ret = -errno; ++ dsc->mem = NULL; + goto errout; + } + +@@ -176,8 +180,7 @@ + return 0; + + errout: +- close(dsc->fd); +- dsc->fd = -1; ++ fbdev_release(dsc); + return ret; + } + +@@ -328,8 +331,9 @@ + + static int fbdev_init_mem2(lv_nuttx_fb_t * dsc) + { +- uintptr_t buf_offset; + struct fb_planeinfo_s pinfo; ++ void * mem2; ++ size_t framesize; + int ret; + + lv_memzero(&pinfo, sizeof(pinfo)); +@@ -349,35 +353,46 @@ + return -EINVAL; + } + +- /* Check the buffer address offset, +- * It needs to be divisible by pinfo.stride +- */ +- +- buf_offset = pinfo.fbmem - dsc->mem; +- +- if((buf_offset % dsc->pinfo.stride) != 0) { +- LV_LOG_WARN("It is detected that buf_offset(%" PRIuPTR ") " +- "and stride(%d) are not divisible, please ensure " +- "that the driver handles the address offset by itself.", +- buf_offset, dsc->pinfo.stride); +- } +- +- /* Calculate the address and yoffset of mem2 */ +- +- if(buf_offset == 0) { +- dsc->mem2_yoffset = dsc->vinfo.yres; +- dsc->mem2 = pinfo.fbmem + dsc->mem2_yoffset * pinfo.stride; +- LV_LOG_USER("Use consecutive mem2 = %p, yoffset = %" LV_PRIu32, +- dsc->mem2, dsc->mem2_yoffset); +- } +- else { +- dsc->mem2_yoffset = buf_offset / dsc->pinfo.stride; +- dsc->mem2 = pinfo.fbmem; +- LV_LOG_USER("Use non-consecutive mem2 = %p, yoffset = %" LV_PRIu32, +- dsc->mem2, dsc->mem2_yoffset); +- } ++ /* The second frame follows the first in the virtual framebuffer. */ ++ framesize = (size_t)dsc->vinfo.yres * dsc->pinfo.stride; ++ mem2 = mmap(NULL, framesize, PROT_READ | PROT_WRITE, ++ MAP_SHARED | MAP_FILE, dsc->fd, framesize); ++ if(mem2 == MAP_FAILED) { ++ int errcode = errno; ++ LV_LOG_ERROR("mmap mem2 failed: %d", errcode); ++ return -errcode; ++ } ++ ++ dsc->mem2 = mem2; ++ dsc->mem2_yoffset = dsc->vinfo.yres; ++ LV_LOG_USER("Mapped mem2 = %p, yoffset = %" LV_PRIu32, ++ dsc->mem2, dsc->mem2_yoffset); + + return 0; ++} ++ ++static void fbdev_release(lv_nuttx_fb_t * dsc) ++{ ++ if(dsc->mem2) { ++ munmap(dsc->mem2, (size_t)dsc->vinfo.yres * dsc->pinfo.stride); ++ dsc->mem2 = NULL; ++ } ++ dsc->mem2_yoffset = 0; ++ ++ if(dsc->mem) { ++ munmap(dsc->mem, dsc->pinfo.fblen); ++ dsc->mem = NULL; ++ } ++ ++ if(dsc->mem_off_screen) { ++ free(dsc->mem_off_screen); ++ dsc->mem_off_screen = NULL; ++ } ++ ++ if(dsc->fd >= 0) { ++ close(dsc->fd); ++ dsc->fd = -1; ++ } + } + + static void display_release_cb(lv_event_t * e) +@@ -388,16 +403,7 @@ + lv_display_set_driver_data(disp, NULL); + lv_display_set_flush_cb(disp, NULL); + +- if(dsc->fd >= 0) { +- close(dsc->fd); +- dsc->fd = -1; +- } +- +- if(dsc->mem_off_screen) { +- /* Free the off-screen buffer */ +- free(dsc->mem_off_screen); +- dsc->mem_off_screen = NULL; +- } ++ fbdev_release(dsc); + + lv_free(dsc); + } diff --git a/graphics/lvgl/CMakeLists.txt b/graphics/lvgl/CMakeLists.txt index 5551920aaa4..018b3992aa7 100644 --- a/graphics/lvgl/CMakeLists.txt +++ b/graphics/lvgl/CMakeLists.txt @@ -31,32 +31,59 @@ if(CONFIG_GRAPHICS_LVGL) "${CONFIG_LVGL_VERSION_MAJOR}.${CONFIG_LVGL_VERSION_MINOR}.${CONFIG_LVGL_VERSION_PATCH}" ) - if(NOT EXISTS ${LVGL_DIR}) - set(LVGL_URL - "https://github.com/lvgl/lvgl/archive/refs/tags/v${LVGL_VERSION}.zip") + set(LVGL_STAMP ${LVGL_DIR}/.nuttx_patched-${LVGL_VERSION}) + set(LVGL_FBDEV_PATCH + ${CMAKE_CURRENT_LIST_DIR}/0001-nuttx-fbdev-use-virtual-framebuffer-offset.patch + ) + + if(NOT EXISTS ${LVGL_DIR}/.git) if("${LVGL_VERSION}" STREQUAL "9.2.2") - set(LVGL_URL_HASH - URL_HASH - SHA256=b3d33f7aad1360f588762208d0f563574392debbc28d9187bda86012909f2203 - ) + set_property( + DIRECTORY + APPEND + PROPERTY CMAKE_CONFIGURE_DEPENDS ${LVGL_FBDEV_PATCH}) endif() - FetchContent_Declare( - lvgl_fetch - URL ${LVGL_URL} - ${LVGL_URL_HASH} - DOWNLOAD_DIR - ${CMAKE_CURRENT_LIST_DIR} - SOURCE_DIR - ${CMAKE_CURRENT_LIST_DIR}/lvgl - BINARY_DIR - ${NUTTX_BINARY_DIR}/apps/graphics/lvgl/lvgl - DOWNLOAD_NO_PROGRESS true - TIMEOUT 120) - - FetchContent_GetProperties(lvgl_fetch) - - if(NOT lvgl_fetch_POPULATED) - FetchContent_Populate(lvgl_fetch) + + if(NOT EXISTS ${LVGL_STAMP} + OR ("${LVGL_VERSION}" STREQUAL "9.2.2" AND ${LVGL_FBDEV_PATCH} + IS_NEWER_THAN ${LVGL_STAMP})) + # Reset download stamps so a failed patch retries from a fresh archive. + set(LVGL_SUBBUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/lvgl-subbuild) + file(REMOVE_RECURSE ${LVGL_SUBBUILD_DIR}) + set(LVGL_URL + "https://github.com/lvgl/lvgl/archive/refs/tags/v${LVGL_VERSION}.zip") + set(LVGL_URL_HASH "") + set(LVGL_PATCH_COMMAND "") + if("${LVGL_VERSION}" STREQUAL "9.2.2") + set(LVGL_URL_HASH + URL_HASH + SHA256=b3d33f7aad1360f588762208d0f563574392debbc28d9187bda86012909f2203 + ) + set(LVGL_PATCH_COMMAND patch -s -N -p1 -d ${LVGL_DIR} -i + ${LVGL_FBDEV_PATCH}) + endif() + FetchContent_Declare( + lvgl_fetch + URL ${LVGL_URL} + ${LVGL_URL_HASH} + DOWNLOAD_DIR + ${CMAKE_CURRENT_LIST_DIR} + SOURCE_DIR + ${LVGL_DIR} + BINARY_DIR + ${NUTTX_BINARY_DIR}/apps/graphics/lvgl/lvgl + SUBBUILD_DIR + ${LVGL_SUBBUILD_DIR} + PATCH_COMMAND ${LVGL_PATCH_COMMAND} + DOWNLOAD_NO_PROGRESS true + TIMEOUT 120) + + FetchContent_GetProperties(lvgl_fetch) + + if(NOT lvgl_fetch_POPULATED) + FetchContent_Populate(lvgl_fetch) + endif() + file(TOUCH ${LVGL_STAMP}) endif() endif() diff --git a/graphics/lvgl/Makefile b/graphics/lvgl/Makefile index 6bd8e2d1a85..610e8e439ce 100644 --- a/graphics/lvgl/Makefile +++ b/graphics/lvgl/Makefile @@ -72,8 +72,6 @@ LVGL_UNPACKNAME = lvgl UNPACK ?= unzip -o $(if $(V),,-q) CURL ?= curl -L -O $(if $(V),,-Ss) -LVGL_UNPACKDIR = $(WD)/$(LVGL_UNPACKNAME) - $(LVGL_TARBALL): $(ECHO_BEGIN)"Downloading: $(LVGL_TARBALL)" $(Q) $(CURL) $(CONFIG_GRAPH_LVGL_URL)/$(LVGL_TARBALL) @@ -82,19 +80,32 @@ ifneq ($(LVGL_TARBALL_SHA256),) endif $(ECHO_END) -$(LVGL_UNPACKNAME): $(LVGL_TARBALL) +# This minimal backport targets the default LVGL 9.2.2 archive. + +ifeq ($(LVGL_VERSION),9.2.2) +LVGL_PATCHES = $(WD)/0001-nuttx-fbdev-use-virtual-framebuffer-offset.patch +endif + +LVGL_STAMP = $(LVGL_UNPACKNAME)/.nuttx_patched-$(LVGL_VERSION) + +# Recreate incomplete or outdated archive trees; never modify a git checkout. + +ifeq ($(wildcard $(LVGL_UNPACKNAME)/.git),) +$(LVGL_STAMP): $(LVGL_TARBALL) $(LVGL_PATCHES) $(ECHO_BEGIN)"Unpacking: $(LVGL_TARBALL) -> $(LVGL_UNPACKNAME)" ifneq ($(LVGL_TARBALL_SHA256),) $(Q) $(CHECKHASH) sha256 $(LVGL_TARBALL_SHA256) $(LVGL_TARBALL) endif + $(Q) rm -rf $(LVGL_UNPACKNAME) lvgl-$(LVGL_VERSION) $(Q) $(UNPACK) $(LVGL_TARBALL) $(Q) mv lvgl-$(LVGL_VERSION) $(LVGL_UNPACKNAME) - $(Q) touch $(LVGL_UNPACKNAME) + $(Q) for p in $(LVGL_PATCHES); do \ + patch -s -N -p1 -d $(LVGL_UNPACKNAME) < $$p || exit 1; \ + done + $(Q) touch $@ $(ECHO_END) -# Download and unpack tarball if no git repo found -ifeq ($(wildcard $(LVGL_UNPACKNAME)/.git),) -context:: $(LVGL_UNPACKNAME) +context:: $(LVGL_STAMP) endif include $(APPDIR)/Application.mk