This is an automated email from the ASF dual-hosted git repository.
xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new bd8ee153d31 drivers/video/fb: support non-consecutive framebuffers
bd8ee153d31 is described below
commit bd8ee153d314d6c3b9da72ac0dd28065ebdc4a47
Author: jianglianfang <[email protected]>
AuthorDate: Wed Apr 29 11:31:50 2026 +0800
drivers/video/fb: support non-consecutive framebuffers
Map logical file offsets to individual framebuffer buffers for file
access and mmap. Translate logical pan offsets in the simulator.
Preserve contiguous and overlay buffer lengths, validate the mapping
capacity and release partially initialized buffers on failure.
Signed-off-by: jianglianfang <[email protected]>
Signed-off-by: rongyichang <[email protected]>
---
Documentation/os/drivers/special/framebuffer.rst | 35 +++-
.../os/graphics/framebuffer_char_driver.rst | 19 ++
arch/sim/src/sim/sim_framebuffer.c | 9 +-
drivers/video/fb.c | 211 +++++++++++++--------
4 files changed, 188 insertions(+), 86 deletions(-)
diff --git a/Documentation/os/drivers/special/framebuffer.rst
b/Documentation/os/drivers/special/framebuffer.rst
index 1ae3354015b..e97dd92cd25 100644
--- a/Documentation/os/drivers/special/framebuffer.rst
+++ b/Documentation/os/drivers/special/framebuffer.rst
@@ -48,6 +48,19 @@ This example will walk through the path from userspace to
hardware-specific deta
#. ``include/nuttx/lcd/lcd.h`` provides structures and APIs needed to work
with LCD screens, whereas using the framebuffer adapter or the :doc:`lcd`;
+Non-consecutive buffers
+-----------------------
+
+The character driver supports equal-sized, non-consecutive buffers in the
+primary plane. The driver's ``getplaneinfo()`` callback uses
+``fb_planeinfo_s.display`` as a zero-based buffer index and returns that
+buffer's ``fbmem`` and ``fblen``. Set ``yres_virtual`` to ``yres`` times the
+buffer count, excluding gaps. For consecutive buffers, return the same base
+address and total length for each index.
+
+The current address table holds at most four buffers and is cached during
+registration; changing buffer addresses afterward is not supported.
+
VSYNC
======
@@ -110,12 +123,26 @@ NuttX Simulator
By selecting the ``sim:lvgl_fb`` config, the ``lvgldemo`` example will be
built with the framebuffer driver.
* ``boards/sim/sim/sim/src/sim_bringup.c`` registers the framebuffer driver
the same way :ref:`ttgotdisplayesp32_fb`;
-* ``arch/sim/src/sim/up_framebuffer.c`` and
``arch/sim/src/sim/up_x11framebuffer.c`` will be built as
``CONFIG_SIM_FRAMEBUFFER = y`` and ``CONFIG_SIM_X11FB = y`` are set,
respectively;
+* ``arch/sim/src/sim/sim_framebuffer.c`` and
``arch/sim/src/sim/posix/sim_x11framebuffer.c`` will be built as
``CONFIG_SIM_FRAMEBUFFER = y`` and ``CONFIG_SIM_X11FB = y`` are set,
respectively;
+
+ * ``sim_framebuffer.c`` provides ``up_fbinitialize``, which calls
+ ``sim_x11initialize`` from ``sim_x11framebuffer.c`` to create the
+ X11 framebuffer window.
+
+* The LVGL demo maps the framebuffer and uses ``FBIOPAN_DISPLAY`` to switch
+ between buffers.
+
+For two non-consecutive X11 framebuffers, use:
+
+.. code-block:: ini
- * ``up_framebuffer.c`` provides ``up_fbinitialize`` and,
- * calls ``up_x11initialize`` from ``up_x11framebuffer.c`` that initializes
a X11-based window as a framebuffer. This is the underlying "driver".
+ CONFIG_SIM_FRAMEBUFFER_COUNT=2
+ CONFIG_SIM_FB_INTERVAL_LINE=17
-* The LVGL demo application (``lvgldemo``) makes use of the ``ioctl`` system
call to trigger a ``FBIO_UPDATE`` request to the higher-level device driver in
order to refresh the LCD screen with framebuffer data as usual;
+``CONFIG_SIM_FB_INTERVAL_LINE=0`` keeps buffers consecutive; a positive value
+adds that many rows between buffers. Applications pass logical pan offsets
+without these gaps; SIM adds them when selecting the displayed buffer. See
+:doc:`/os/graphics/framebuffer_char_driver` for file offsets and mapping rules.
.. warning::
diff --git a/Documentation/os/graphics/framebuffer_char_driver.rst
b/Documentation/os/graphics/framebuffer_char_driver.rst
index 5fb77fdba8c..2b85ef83bea 100644
--- a/Documentation/os/graphics/framebuffer_char_driver.rst
+++ b/Documentation/os/graphics/framebuffer_char_driver.rst
@@ -120,6 +120,14 @@ from pixels to bytes is:
start_byte = (start_pixel * bits_per_pixel) >> 3;
end_byte = (end_pixel * bits_per_pixel + 7) >> 3;
+For equal-sized buffers in the primary plane, file offsets form a
+continuous logical address space, even when the buffers are not consecutive
+in memory. Each frame occupies ``framesize = stride * yres`` bytes; frame
+``i`` starts at ``i * framesize``, counting from zero. Gaps between buffers
+do not contribute to file offsets. ``read()`` and ``write()`` can cross frame
+boundaries, and ``SEEK_END`` uses the logical total size, ``framesize`` times
+the number of buffers.
+
While the framebuffer may be accessed with these POSIX interfaces,
a more typical way of interacting with the framebuffer from an
application would involve use of ``mmap()`` as described below.
@@ -261,6 +269,12 @@ IOCTL Commands
struct nxgl_point_s pt2; /* Lower, right-hand corner */
};
+* ``FBIOPAN_DISPLAY``. Pan the primary plane using a pointer to
+ ``struct fb_planeinfo_s``. For equal-sized buffers, ``yoffset`` is a
+ logical row offset: the second frame starts at ``yres``, not at the
+ difference between buffer addresses divided by ``stride``. Exclude any
+ gaps between buffers.
+
* ``FBIOGET_PANINFOCNT``. Retrieves the current number of pan info
structures. This IOCTL command requires the overlay index as a parameter.
@@ -297,6 +311,11 @@ kernel memory address and may not be valid in all
build configurations. Hence, ``mmap()`` is the preferred,
portable way to get the framebuffer address.
+Map non-consecutive primary-plane buffers separately. ``mmap()`` uses the
+same logical byte offsets as file I/O: map the second frame with offset
+``framesize`` and length ``framesize``. A request crossing a physical frame
+boundary fails with ``MAP_FAILED`` and ``errno`` set to ``EINVAL``.
+
Framebuffer vs. LCD Graphics Drivers
====================================
diff --git a/arch/sim/src/sim/sim_framebuffer.c
b/arch/sim/src/sim/sim_framebuffer.c
index 3f0aee1cbec..ab355dedd4c 100644
--- a/arch/sim/src/sim/sim_framebuffer.c
+++ b/arch/sim/src/sim/sim_framebuffer.c
@@ -192,6 +192,7 @@ static struct fb_vtable_s g_fbobject =
static int sim_openwindow(struct fb_vtable_s *vtable)
{
int ret = OK;
+
ginfo("vtable=%p\n", vtable);
#ifdef CONFIG_SIM_X11FB
@@ -208,6 +209,7 @@ static int sim_openwindow(struct fb_vtable_s *vtable)
static int sim_closewindow(struct fb_vtable_s *vtable)
{
int ret = OK;
+
ginfo("vtable=%p\n", vtable);
#ifdef CONFIG_SIM_X11FB
@@ -445,7 +447,12 @@ void sim_x11loop(void)
if (fb_peek_paninfo(&g_fbobject, &info, FB_NO_OVERLAY) == OK)
{
- sim_x11setoffset(info.planeinfo.yoffset * info.planeinfo.stride);
+ uint32_t yoffset = info.planeinfo.yoffset;
+#if CONFIG_SIM_FB_INTERVAL_LINE > 0
+ yoffset += (yoffset / CONFIG_SIM_FBHEIGHT)
+ * CONFIG_SIM_FB_INTERVAL_LINE;
+#endif
+ sim_x11setoffset(yoffset * info.planeinfo.stride);
}
sim_x11update();
diff --git a/drivers/video/fb.c b/drivers/video/fb.c
index e9df9f0447b..2c0ad04fb2e 100644
--- a/drivers/video/fb.c
+++ b/drivers/video/fb.c
@@ -56,6 +56,8 @@
* Pre-processor definitions
****************************************************************************/
+#define FB_MAX_BUFCOUNT 4
+
#ifdef CONFIG_VIDEO_FB_SPLASHSCREEN
# define SPLASH_BGCOL CONFIG_VIDEO_FB_SPLASHSCREEN_BG_COLOUR
# define SPLASH_SLEEP CONFIG_VIDEO_FB_SPLASHSCREEN_DISP_TIME
@@ -99,8 +101,11 @@ struct fb_priv_s
struct fb_paninfo_s
{
- FAR struct circbuf_s buf; /* Pan buffer queued list */
- struct wdog_s wdog; /* VSync offset timer */
+ FAR struct circbuf_s buf; /* Pan buffer queued list */
+ size_t framesize; /* Single frame size: stride * yres */
+ size_t fblen; /* Total: framesize * fbcount */
+ FAR char *fbmem[FB_MAX_BUFCOUNT]; /* Buffer address array */
+ struct wdog_s wdog; /* VSync offset timer */
FAR struct fb_chardev_s *dev;
};
@@ -125,6 +130,7 @@ struct fb_panelinfo_s
{
FAR void *fbmem; /* Start of frame buffer memory */
size_t fblen; /* Size of the framebuffer */
+ uint16_t stride; /* Length of a line in bytes */
uint8_t fbcount; /* Count of frame buffer */
uint8_t bpp; /* Bits per pixel */
};
@@ -330,17 +336,47 @@ static int fb_splashscreen(FAR struct fb_videoinfo_s
*vinfo,
#endif /* CONFIG_VIDEO_FB_SPLASHSCREEN */
/****************************************************************************
- * Name: fb_get_panbuf
+ * Name: fb_get_paninfo
****************************************************************************/
-static FAR struct circbuf_s *fb_get_panbuf(FAR struct fb_chardev_s *fb,
- int overlay)
+static inline_function FAR struct fb_paninfo_s *
+fb_get_paninfo(FAR struct fb_chardev_s *fb, int overlay)
{
int id = overlay + 1;
DEBUGASSERT(id >= 0 && id < fb->paninfo_count);
- return &(fb->paninfo[id].buf);
+ return &fb->paninfo[id];
+}
+
+/****************************************************************************
+ * Name: fb_map_region
+ ****************************************************************************/
+
+static inline_function FAR char *
+fb_map_region(FAR struct fb_paninfo_s *paninfo, size_t offset,
+ size_t length, FAR size_t *bytes)
+{
+ size_t idx = paninfo->fbmem[1] ? offset / paninfo->framesize : 0;
+ size_t off = paninfo->fbmem[1] ? offset % paninfo->framesize : offset;
+ size_t lim = paninfo->fbmem[1] ? paninfo->framesize : paninfo->fblen;
+
+ if (bytes != NULL)
+ {
+ *bytes = lim - off < length ? lim - off : length;
+ }
+
+ return paninfo->fbmem[idx] + off;
+}
+
+/****************************************************************************
+ * Name: fb_get_panbuf
+ ****************************************************************************/
+
+static inline_function FAR struct circbuf_s *
+fb_get_panbuf(FAR struct fb_chardev_s *fb, int overlay)
+{
+ return &(fb_get_paninfo(fb, overlay)->buf);
}
/****************************************************************************
@@ -547,12 +583,13 @@ static ssize_t fb_read(FAR struct file *filep, FAR char
*buffer, size_t len)
{
FAR struct inode *inode;
FAR struct fb_chardev_s *fb;
+ FAR struct fb_paninfo_s *paninfo;
FAR struct fb_priv_s *priv;
- struct fb_panelinfo_s panelinfo;
+ FAR const char *addr;
size_t start;
size_t end;
size_t size;
- int ret;
+ size_t bytes;
ginfo("len: %u\n", (unsigned int)len);
@@ -563,38 +600,36 @@ static ssize_t fb_read(FAR struct file *filep, FAR char
*buffer, size_t len)
priv = filep->f_priv;
DEBUGASSERT(fb->vtable != NULL && priv != NULL);
-
- /* Get panel info */
-
nxmutex_lock(&fb->lock);
- ret = fb_get_panelinfo(fb, &panelinfo, priv->overlay);
-
- if (ret < 0)
- {
- nxmutex_unlock(&fb->lock);
- return ret;
- }
+ paninfo = fb_get_paninfo(fb, priv->overlay);
/* Get the start and size of the transfer */
start = filep->f_pos;
- if (start >= panelinfo.fblen)
+ if (start >= paninfo->fblen)
{
nxmutex_unlock(&fb->lock);
return 0; /* Return end-of-file */
}
end = start + len;
- if (end >= panelinfo.fblen)
+ if (end >= paninfo->fblen)
{
- end = panelinfo.fblen;
+ end = paninfo->fblen;
}
size = end - start;
- /* And transfer the data from the frame buffer */
+ /* Transfer the data from the frame buffer */
+
+ while (start < end)
+ {
+ addr = fb_map_region(paninfo, start, end - start, &bytes);
+ memcpy(buffer, addr, bytes);
+ buffer += bytes;
+ start += bytes;
+ }
- memcpy(buffer, panelinfo.fbmem + start, size);
filep->f_pos += size;
nxmutex_unlock(&fb->lock);
return size;
@@ -609,12 +644,13 @@ static ssize_t fb_write(FAR struct file *filep, FAR const
char *buffer,
{
FAR struct inode *inode;
FAR struct fb_chardev_s *fb;
+ FAR struct fb_paninfo_s *paninfo;
FAR struct fb_priv_s *priv;
- struct fb_panelinfo_s panelinfo;
+ FAR char *addr;
size_t start;
size_t end;
size_t size;
- int ret;
+ size_t bytes;
ginfo("len: %u\n", (unsigned int)len);
@@ -625,38 +661,36 @@ static ssize_t fb_write(FAR struct file *filep, FAR const
char *buffer,
priv = filep->f_priv;
DEBUGASSERT(fb->vtable != NULL && priv != NULL);
-
- /* Get panel info */
-
nxmutex_lock(&fb->lock);
- ret = fb_get_panelinfo(fb, &panelinfo, priv->overlay);
-
- if (ret < 0)
- {
- nxmutex_unlock(&fb->lock);
- return ret;
- }
+ paninfo = fb_get_paninfo(fb, priv->overlay);
/* Get the start and size of the transfer */
start = filep->f_pos;
- if (start >= panelinfo.fblen)
+ if (start >= paninfo->fblen)
{
nxmutex_unlock(&fb->lock);
return -EFBIG; /* Cannot extend the framebuffer */
}
end = start + len;
- if (end >= panelinfo.fblen)
+ if (end >= paninfo->fblen)
{
- end = panelinfo.fblen;
+ end = paninfo->fblen;
}
size = end - start;
- /* And transfer the data into the frame buffer */
+ /* Transfer the data into the frame buffer */
+
+ while (start < end)
+ {
+ addr = fb_map_region(paninfo, start, end - start, &bytes);
+ memcpy(addr, buffer, bytes);
+ buffer += bytes;
+ start += bytes;
+ }
- memcpy(panelinfo.fbmem + start, buffer, size);
filep->f_pos += size;
nxmutex_unlock(&fb->lock);
return size;
@@ -676,8 +710,8 @@ static off_t fb_seek(FAR struct file *filep, off_t offset,
int whence)
{
FAR struct inode *inode;
FAR struct fb_chardev_s *fb;
+ FAR struct fb_paninfo_s *paninfo;
FAR struct fb_priv_s *priv;
- struct fb_panelinfo_s panelinfo;
off_t newpos;
int ret;
@@ -690,6 +724,7 @@ static off_t fb_seek(FAR struct file *filep, off_t offset,
int whence)
priv = filep->f_priv;
DEBUGASSERT(fb->vtable != NULL && priv != NULL);
+ paninfo = fb_get_paninfo(fb, priv->overlay);
/* Determine the new, requested file position */
@@ -704,17 +739,7 @@ static off_t fb_seek(FAR struct file *filep, off_t offset,
int whence)
break;
case SEEK_END:
-
- /* Get panel info */
-
- ret = fb_get_panelinfo(fb, &panelinfo, priv->overlay);
-
- if (ret < 0)
- {
- return ret;
- }
-
- newpos = panelinfo.fblen + offset;
+ newpos = paninfo->fblen + offset;
break;
default:
@@ -1158,7 +1183,6 @@ static int fb_ioctl(FAR struct file *filep, int cmd,
unsigned long arg)
union fb_paninfo_u paninfo;
DEBUGASSERT(pinfo != NULL && fb->vtable != NULL);
-
memcpy(&paninfo, pinfo, sizeof(*pinfo));
nxmutex_lock(&fb->lock);
@@ -1381,9 +1405,10 @@ static int fb_mmap(FAR struct file *filep, FAR struct
mm_map_entry_s *map)
{
FAR struct inode *inode;
FAR struct fb_chardev_s *fb;
+ FAR struct fb_paninfo_s *paninfo;
FAR struct fb_priv_s *priv;
- struct fb_panelinfo_s panelinfo;
- int ret;
+ FAR void *addr;
+ size_t rest;
/* Get the framebuffer instance */
@@ -1392,38 +1417,36 @@ static int fb_mmap(FAR struct file *filep, FAR struct
mm_map_entry_s *map)
priv = filep->f_priv;
DEBUGASSERT(fb->vtable != NULL && priv != NULL);
+ nxmutex_lock(&fb->lock);
+ paninfo = fb_get_paninfo(fb, priv->overlay);
- /* Get panel info */
+ if (map->offset < 0 || map->length == 0 ||
+ map->offset + map->length > paninfo->fblen)
+ {
+ nxmutex_unlock(&fb->lock);
+ return -EINVAL;
+ }
- nxmutex_lock(&fb->lock);
- ret = fb_get_panelinfo(fb, &panelinfo, priv->overlay);
+ /* Map virtual offset to physical address */
- if (ret < 0)
+ addr = fb_map_region(paninfo, map->offset, map->length, &rest);
+ if (map->length > rest)
{
nxmutex_unlock(&fb->lock);
- return ret;
+ return -EINVAL;
}
/* Return the address corresponding to the start of frame buffer. */
- if (map->offset >= 0 && map->offset < panelinfo.fblen &&
- map->length && map->offset + map->length <= panelinfo.fblen)
- {
#ifdef CONFIG_BUILD_KERNEL
- map->vaddr = vm_map_region((uintptr_t)panelinfo.fbmem + map->offset,
- panelinfo.fblen);
- map->length = panelinfo.fblen;
- map->munmap = fb_munmap;
- mm_map_add(get_current_mm(), map);
+ map->vaddr = vm_map_region((uintptr_t)addr, map->length);
+ map->munmap = fb_munmap;
+ mm_map_add(get_current_mm(), map);
#else
- map->vaddr = (FAR char *)panelinfo.fbmem + map->offset;
+ map->vaddr = addr;
#endif
- nxmutex_unlock(&fb->lock);
- return OK;
- }
-
nxmutex_unlock(&fb->lock);
- return -EINVAL;
+ return OK;
}
/****************************************************************************
@@ -1525,6 +1548,7 @@ static int fb_get_panelinfo(FAR struct fb_chardev_s *fb,
panelinfo->fbmem = oinfo.fbmem;
panelinfo->fblen = oinfo.fblen;
+ panelinfo->stride = oinfo.stride;
panelinfo->fbcount = oinfo.yres_virtual == 0 ?
1 : (oinfo.yres_virtual / oinfo.yres);
panelinfo->bpp = oinfo.bpp;
@@ -1547,6 +1571,7 @@ static int fb_get_panelinfo(FAR struct fb_chardev_s *fb,
panelinfo->fbmem = pinfo.fbmem;
panelinfo->fblen = pinfo.fblen;
+ panelinfo->stride = pinfo.stride;
panelinfo->fbcount = pinfo.yres_virtual == 0 ?
1 : (pinfo.yres_virtual / vinfo.yres);
panelinfo->bpp = pinfo.bpp;
@@ -1934,6 +1959,7 @@ int fb_register_device(int display, int plane,
{
FAR struct fb_chardev_s *fb;
struct fb_panelinfo_s panelinfo;
+ struct fb_planeinfo_s planeinfo;
#ifdef CONFIG_VIDEO_FB_SPLASHSCREEN
struct fb_planeinfo_s pinfo;
# ifdef CONFIG_FB_UPDATE
@@ -1942,9 +1968,9 @@ int fb_register_device(int display, int plane,
#endif
struct fb_videoinfo_s vinfo;
char devname[16];
- int nplanes;
+ size_t i;
+ size_t j;
int ret;
- ssize_t i;
/* Allocate a framebuffer state instance */
@@ -1970,8 +1996,7 @@ int fb_register_device(int display, int plane,
goto errout_with_fb;
}
- nplanes = vinfo.nplanes;
- DEBUGASSERT(vinfo.nplanes > 0 && (unsigned)plane < vinfo.nplanes);
+ DEBUGASSERT((unsigned)plane < vinfo.nplanes);
#ifdef CONFIG_FB_OVERLAY
fb->paninfo_count = vinfo.noverlays;
@@ -1988,6 +2013,8 @@ int fb_register_device(int display, int plane,
goto errout_with_fb;
}
+ /* Init circbuf for each panel */
+
for (i = 0; i < fb->paninfo_count; i++)
{
ret = fb_get_panelinfo(fb, &panelinfo, i - 1);
@@ -1998,19 +2025,41 @@ int fb_register_device(int display, int plane,
ret = circbuf_init(&(fb->paninfo[i].buf), NULL, panelinfo.fbcount
* sizeof(union fb_paninfo_u));
-
DEBUGASSERT(ret == 0);
-
fb->paninfo[i].dev = fb;
/* Clear the framebuffer memory */
memset(panelinfo.fbmem, 0, panelinfo.fblen);
+
+ /* Initialize buffer address map */
+
+ DEBUGASSERT(panelinfo.fbcount <= FB_MAX_BUFCOUNT);
+ fb->paninfo[i].framesize = panelinfo.stride * vinfo.yres;
+ fb->paninfo[i].fblen = fb->paninfo[i].framesize *
+ panelinfo.fbcount;
+ fb->paninfo[i].fbmem[0] = panelinfo.fbmem;
+
+ /* Fill fbmem[1..n] only for non-consecutive primary plane. */
+
+ for (j = 1; i == 0 && j < panelinfo.fbcount; j++)
+ {
+ ret = fb_get_planeinfo(fb, &planeinfo, j);
+ if (ret < 0)
+ {
+ goto errout_with_paninfo;
+ }
+
+ if (planeinfo.fbmem != fb->paninfo[0].fbmem[0])
+ {
+ fb->paninfo[0].fbmem[j] = planeinfo.fbmem;
+ }
+ }
}
/* Register the framebuffer device */
- if (nplanes < 2)
+ if (vinfo.nplanes < 2)
{
snprintf(devname, sizeof(devname), "/dev/fb%d", display);
}