You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2023/09/12 14:10:20 UTC

[nuttx] 04/06: video/fb: add vsync queue

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit e578f3b20d4361ad5ebeebc38a746c3860a4ab01
Author: pengyiqiang <pe...@xiaomi.com>
AuthorDate: Wed Jul 19 21:11:07 2023 +0800

    video/fb: add vsync queue
    
    refact vsync queue to support multi fb poll and overlay poll.
    
    Signed-off-by: pengyiqiang <pe...@xiaomi.com>
    Signed-off-by: rongyichang <ro...@xiaomi.com>
---
 drivers/video/Kconfig    |   5 +
 drivers/video/fb.c       | 565 +++++++++++++++++++++++++++++++++++++----------
 include/nuttx/video/fb.h |  56 ++++-
 3 files changed, 500 insertions(+), 126 deletions(-)

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 9e97620ea9..4db3f10124 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -50,6 +50,11 @@ config VIDEO_FB
 	bool "Framebuffer character driver"
 	default n
 
+config VIDEO_FB_NPOLLWAITERS
+	int "Video fb poll count of each layer"
+	depends on VIDEO_FB
+	default 2
+
 config VIDEO_STREAM
 	bool "Video Stream Support"
 	default n
diff --git a/drivers/video/fb.c b/drivers/video/fb.c
index 532522931c..97515eb00b 100644
--- a/drivers/video/fb.c
+++ b/drivers/video/fb.c
@@ -41,17 +41,30 @@
 #include <nuttx/video/fb.h>
 #include <nuttx/clock.h>
 #include <nuttx/wdog.h>
+#include <nuttx/mm/circbuf.h>
 
 /****************************************************************************
  * Pre-processor definitions
  ****************************************************************************/
 
-#define FB_NO_OVERLAY -1
-
 /****************************************************************************
  * Private Types
  ****************************************************************************/
 
+struct fb_priv_s
+{
+  int overlay;                    /* Overlay number */
+};
+
+struct fb_paninfo_s
+{
+  FAR struct circbuf_s buf;       /* Pan buffer queued list */
+
+  /* Polling fds of waiting threads */
+
+  FAR struct pollfd *fds[CONFIG_VIDEO_FB_NPOLLWAITERS];
+};
+
 /* This structure defines one framebuffer device.  Note that which is
  * everything in this structure is constant data set up and initialization
  * time.  Therefore, no there is requirement for serialized access to this
@@ -60,23 +73,21 @@
 
 struct fb_chardev_s
 {
-  FAR struct fb_vtable_s *vtable; /* Framebuffer interface */
-  FAR struct pollfd *fds;         /* Polling structure of waiting thread */
-  uint8_t plane;                  /* Video plan number */
-  volatile int pollcnt;           /* Poll ready count */
-  clock_t vsyncoffset;            /* VSync offset ticks */
-  struct wdog_s wdog;             /* VSync offset timer */
-#ifdef CONFIG_FB_OVERLAY
-  int overlay;                    /* Overlay number */
-#endif
-  mutex_t lock;                  /* Mutual exclusion */
-  int16_t crefs;                 /* Number of open references */
+  FAR struct fb_vtable_s *vtable;   /* Framebuffer interface */
+  uint8_t plane;                    /* Video plan number */
+  clock_t vsyncoffset;              /* VSync offset ticks */
+  struct wdog_s wdog;               /* VSync offset timer */
+  mutex_t lock;                     /* Mutual exclusion */
+  int16_t crefs;                    /* Number of open references */
+  FAR struct fb_paninfo_s *paninfo; /* Pan info array */
+  size_t paninfo_count;             /* Pan info count */
 };
 
 struct fb_panelinfo_s
 {
   FAR void *fbmem;                /* Start of frame buffer memory */
   size_t fblen;                   /* Size of the framebuffer */
+  uint8_t fbcount;                /* Count of frame buffer */
   uint8_t bpp;                    /* Bits per pixel */
 };
 
@@ -84,6 +95,13 @@ struct fb_panelinfo_s
  * Private Function Prototypes
  ****************************************************************************/
 
+static FAR struct pollfd **fb_get_free_pollfds(FAR struct fb_chardev_s *fb,
+                                               int overlay);
+static FAR struct circbuf_s *fb_get_panbuf(FAR struct fb_chardev_s *fb,
+                                           int overlay);
+static int     fb_add_paninfo(FAR struct fb_vtable_s *vtable,
+                              FAR const union fb_paninfo_u *info,
+                              int overlay);
 static int     fb_open(FAR struct file *filep);
 static int     fb_close(FAR struct file *filep);
 static ssize_t fb_read(FAR struct file *filep, FAR char *buffer,
@@ -97,7 +115,8 @@ static int     fb_mmap(FAR struct file *filep,
 static int     fb_poll(FAR struct file *filep, FAR struct pollfd *fds,
                        bool setup);
 static int     fb_get_panelinfo(FAR struct fb_chardev_s *fb,
-                                FAR struct fb_panelinfo_s *panelinfo);
+                                FAR struct fb_panelinfo_s *panelinfo,
+                                int overlay);
 static int     fb_get_planeinfo(FAR struct fb_chardev_s *fb,
                                 FAR struct fb_planeinfo_s *pinfo,
                                 uint8_t display);
@@ -123,6 +142,93 @@ static const struct file_operations g_fb_fops =
  * Private Functions
  ****************************************************************************/
 
+/****************************************************************************
+ * Name: fb_get_free_pollfds
+ ****************************************************************************/
+
+static FAR struct pollfd **fb_get_free_pollfds(FAR struct fb_chardev_s *fb,
+                                               int overlay)
+{
+  FAR struct fb_paninfo_s *paninfo;
+  int id = overlay + 1;
+  int i;
+
+  DEBUGASSERT(id >= 0 && id < fb->paninfo_count);
+
+  paninfo = &fb->paninfo[id];
+
+  for (i = 0; i < CONFIG_VIDEO_FB_NPOLLWAITERS; ++i)
+    {
+      if (!paninfo->fds[i])
+        {
+          return &paninfo->fds[i];
+        }
+    }
+
+  return NULL;
+}
+
+/****************************************************************************
+ * Name: fb_get_panbuf
+ ****************************************************************************/
+
+static FAR struct circbuf_s *fb_get_panbuf(FAR struct fb_chardev_s *fb,
+                                           int overlay)
+{
+  int id = overlay + 1;
+
+  DEBUGASSERT(id >= 0 && id < fb->paninfo_count);
+
+  return &(fb->paninfo[id].buf);
+}
+
+/****************************************************************************
+ * Name: fb_add_paninfo
+ ****************************************************************************/
+
+static int fb_add_paninfo(FAR struct fb_vtable_s *vtable,
+                          FAR const union fb_paninfo_u *info,
+                          int overlay)
+{
+  FAR struct circbuf_s *panbuf;
+  FAR struct fb_chardev_s *fb;
+  irqstate_t flags;
+  ssize_t ret;
+
+  DEBUGASSERT(vtable != NULL);
+
+  /* Prevent calling before getting the vtable. */
+
+  fb = vtable->priv;
+  if (fb == NULL)
+    {
+      return -EINVAL;
+    }
+
+  panbuf = fb_get_panbuf(fb, overlay);
+  if (panbuf == NULL)
+    {
+      return -EINVAL;
+    }
+
+  /* Disable the interrupt when writing to the queue to
+   * prevent it from being modified by the interrupted
+   * thread during the writing process.
+   */
+
+  flags = enter_critical_section();
+
+  /* Write planeinfo information to the queue. */
+
+  ret = circbuf_write(panbuf, info, sizeof(union fb_paninfo_u));
+  DEBUGASSERT(ret == sizeof(union fb_paninfo_u));
+
+  /* Re-enable interrupts */
+
+  leave_critical_section(flags);
+  return ret <= 0 ? -ENOSPC : OK;
+}
+
 /****************************************************************************
  * Name: fb_open
  ****************************************************************************/
@@ -131,6 +237,7 @@ static int fb_open(FAR struct file *filep)
 {
   FAR struct inode *inode;
   FAR struct fb_chardev_s *fb;
+  FAR struct fb_priv_s *priv;
   int ret;
 
   inode = filep->f_inode;
@@ -144,20 +251,34 @@ static int fb_open(FAR struct file *filep)
       return ret;
     }
 
+  priv = kmm_zalloc(sizeof(*priv));
+  if (priv == NULL)
+    {
+      ret = -ENOMEM;
+      goto err_out;
+    }
+
   if (fb->crefs == 0)
     {
-      if (fb->vtable->open != NULL)
+      if (fb->vtable->open != NULL &&
+          (ret = fb->vtable->open(fb->vtable)) < 0)
         {
-          ret = fb->vtable->open(fb->vtable);
+          goto err_fb;
         }
     }
 
-  if (ret >= 0)
-    {
-      fb->crefs++;
-      DEBUGASSERT(fb->crefs > 0);
-    }
+  fb->crefs++;
+  DEBUGASSERT(fb->crefs > 0);
+
+  priv->overlay = FB_NO_OVERLAY;
+  filep->f_priv = priv;
 
+  nxmutex_unlock(&fb->lock);
+  return 0;
+
+err_fb:
+  kmm_free(priv);
+err_out:
   nxmutex_unlock(&fb->lock);
   return ret;
 }
@@ -170,12 +291,14 @@ static int fb_close(FAR struct file *filep)
 {
   FAR struct inode *inode;
   FAR struct fb_chardev_s *fb;
+  FAR struct fb_priv_s *priv;
   int ret;
 
   inode = filep->f_inode;
   fb    = inode->i_private;
+  priv  = filep->f_priv;
 
-  DEBUGASSERT(fb->vtable != NULL);
+  DEBUGASSERT(fb->vtable != NULL && priv != NULL);
 
   ret = nxmutex_lock(&fb->lock);
   if (ret < 0)
@@ -195,6 +318,7 @@ static int fb_close(FAR struct file *filep)
     {
       DEBUGASSERT(fb->crefs > 0);
       fb->crefs--;
+      kmm_free(priv);
     }
 
   nxmutex_unlock(&fb->lock);
@@ -209,6 +333,7 @@ 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_priv_s *priv;
   struct fb_panelinfo_s panelinfo;
   size_t start;
   size_t end;
@@ -221,10 +346,13 @@ static ssize_t fb_read(FAR struct file *filep, FAR char *buffer, size_t len)
 
   inode = filep->f_inode;
   fb    = inode->i_private;
+  priv  = filep->f_priv;
+
+  DEBUGASSERT(fb->vtable != NULL && priv != NULL);
 
   /* Get panel info */
 
-  ret = fb_get_panelinfo(fb, &panelinfo);
+  ret = fb_get_panelinfo(fb, &panelinfo, priv->overlay);
 
   if (ret < 0)
     {
@@ -263,6 +391,7 @@ 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_priv_s *priv;
   struct fb_panelinfo_s panelinfo;
   size_t start;
   size_t end;
@@ -275,10 +404,13 @@ static ssize_t fb_write(FAR struct file *filep, FAR const char *buffer,
 
   inode = filep->f_inode;
   fb    = inode->i_private;
+  priv  = filep->f_priv;
+
+  DEBUGASSERT(fb->vtable != NULL && priv != NULL);
 
   /* Get panel info */
 
-  ret = fb_get_panelinfo(fb, &panelinfo);
+  ret = fb_get_panelinfo(fb, &panelinfo, priv->overlay);
 
   if (ret < 0)
     {
@@ -322,6 +454,7 @@ 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_priv_s *priv;
   struct fb_panelinfo_s panelinfo;
   off_t newpos;
   int ret;
@@ -332,6 +465,9 @@ static off_t fb_seek(FAR struct file *filep, off_t offset, int whence)
 
   inode = filep->f_inode;
   fb    = inode->i_private;
+  priv  = filep->f_priv;
+
+  DEBUGASSERT(fb->vtable != NULL && priv != NULL);
 
   /* Determine the new, requested file position */
 
@@ -349,7 +485,7 @@ static off_t fb_seek(FAR struct file *filep, off_t offset, int whence)
 
       /* Get panel info */
 
-      ret = fb_get_panelinfo(fb, &panelinfo);
+      ret = fb_get_panelinfo(fb, &panelinfo, priv->overlay);
 
       if (ret < 0)
         {
@@ -507,14 +643,15 @@ static int fb_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
       case FBIO_SELECT_OVERLAY:  /* Select video overlay */
         {
           struct fb_overlayinfo_s oinfo;
+          FAR struct fb_priv_s *priv = filep->f_priv;
 
-          DEBUGASSERT(fb->vtable != NULL &&
+          DEBUGASSERT(priv != NULL && fb->vtable != NULL &&
                       fb->vtable->getoverlayinfo != NULL);
           memset(&oinfo, 0, sizeof(oinfo));
           ret = fb->vtable->getoverlayinfo(fb->vtable, arg, &oinfo);
-          if (ret == OK)
+          if (ret >= 0)
             {
-              fb->overlay = arg;
+              priv->overlay = arg;
             }
         }
         break;
@@ -625,10 +762,18 @@ static int fb_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
         {
           FAR struct fb_overlayinfo_s *oinfo =
             (FAR struct fb_overlayinfo_s *)((uintptr_t)arg);
+          union fb_paninfo_u paninfo;
 
-          DEBUGASSERT(oinfo != 0 && fb->vtable != NULL &&
-                      fb->vtable->panoverlay != NULL);
-          ret = fb->vtable->panoverlay(fb->vtable, oinfo);
+          DEBUGASSERT(oinfo != 0 && fb->vtable != NULL);
+
+          memcpy(&paninfo, oinfo, sizeof(*oinfo));
+
+          if (fb->vtable->panoverlay != NULL)
+            {
+              fb->vtable->panoverlay(fb->vtable, oinfo);
+            }
+
+          ret = fb_add_paninfo(fb->vtable, &paninfo, oinfo->overlay);
         }
         break;
 
@@ -676,15 +821,18 @@ static int fb_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
         {
           FAR struct fb_planeinfo_s *pinfo =
             (FAR struct fb_planeinfo_s *)((uintptr_t)arg);
+          union fb_paninfo_u paninfo;
+
+          DEBUGASSERT(pinfo != NULL && fb->vtable != NULL);
 
-          DEBUGASSERT(pinfo != NULL && fb->vtable != NULL &&
-                      fb->vtable->pandisplay != NULL);
-          ret = fb->vtable->pandisplay(fb->vtable, pinfo);
-          fb->pollcnt--;
+          memcpy(&paninfo, pinfo, sizeof(*pinfo));
 
-          /* Check pan display overrun. */
+          if (fb->vtable->pandisplay != NULL)
+            {
+              fb->vtable->pandisplay(fb->vtable, pinfo);
+            }
 
-          DEBUGASSERT(fb->pollcnt >= 0);
+          ret = fb_add_paninfo(fb->vtable, &paninfo, FB_NO_OVERLAY);
         }
         break;
 
@@ -844,6 +992,7 @@ 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_priv_s *priv;
   struct fb_panelinfo_s panelinfo;
   int ret;
 
@@ -851,10 +1000,13 @@ static int fb_mmap(FAR struct file *filep, FAR struct mm_map_entry_s *map)
 
   inode = filep->f_inode;
   fb    = inode->i_private;
+  priv  = filep->f_priv;
+
+  DEBUGASSERT(fb->vtable != NULL && priv != NULL);
 
   /* Get panel info */
 
-  ret = fb_get_panelinfo(fb, &panelinfo);
+  ret = fb_get_panelinfo(fb, &panelinfo, priv->overlay);
 
   if (ret < 0)
     {
@@ -885,36 +1037,52 @@ static int fb_poll(FAR struct file *filep, struct pollfd *fds, bool setup)
 {
   FAR struct inode *inode;
   FAR struct fb_chardev_s *fb;
+  FAR struct fb_priv_s *priv;
+  FAR struct circbuf_s *panbuf;
+  FAR struct pollfd **pollfds;
+  irqstate_t flags;
+  int ret = OK;
 
   /* Get the framebuffer instance */
 
   inode = filep->f_inode;
   fb    = inode->i_private;
+  priv  = filep->f_priv;
+
+  DEBUGASSERT(fb->vtable != NULL && priv != NULL);
+
+  flags = enter_critical_section();
 
   if (setup)
     {
-      if (fb->fds == NULL)
-        {
-          fb->fds = fds;
-          fds->priv = &fb->fds;
-        }
-      else
+      pollfds = fb_get_free_pollfds(fb, priv->overlay);
+      if (pollfds == NULL)
         {
-          return -EBUSY;
+          ret = -EBUSY;
+          goto errout;
         }
 
-      if (fb->pollcnt > 0)
+      *pollfds = fds;
+      fds->priv = pollfds;
+
+      panbuf = fb_get_panbuf(fb, priv->overlay);
+      if (!circbuf_is_full(panbuf))
         {
-          poll_notify(&fb->fds, 1, POLLOUT);
+          poll_notify(pollfds, 1, POLLOUT);
         }
     }
-  else if (fds->priv)
+  else if (fds->priv != NULL)
     {
-      fb->fds = NULL;
+      /* This is a request to tear down the poll. */
+
+      FAR struct pollfd **slot = (FAR struct pollfd **)fds->priv;
+      *slot = NULL;
       fds->priv = NULL;
     }
 
-  return OK;
+errout:
+  leave_critical_section(flags);
+  return ret;
 }
 
 /****************************************************************************
@@ -922,18 +1090,20 @@ static int fb_poll(FAR struct file *filep, struct pollfd *fds, bool setup)
  ****************************************************************************/
 
 static int fb_get_panelinfo(FAR struct fb_chardev_s *fb,
-                            FAR struct fb_panelinfo_s *panelinfo)
+                            FAR struct fb_panelinfo_s *panelinfo,
+                            int overlay)
 {
   struct fb_planeinfo_s pinfo;
+  struct fb_videoinfo_s vinfo;
   int ret;
 
 #ifdef CONFIG_FB_OVERLAY
-  if (fb->overlay != FB_NO_OVERLAY)
+  if (overlay != FB_NO_OVERLAY)
     {
       struct fb_overlayinfo_s oinfo;
       DEBUGASSERT(fb->vtable->getoverlayinfo != NULL);
       memset(&oinfo, 0, sizeof(oinfo));
-      ret = fb->vtable->getoverlayinfo(fb->vtable, fb->overlay, &oinfo);
+      ret = fb->vtable->getoverlayinfo(fb->vtable, overlay, &oinfo);
 
       if (ret < 0)
         {
@@ -941,9 +1111,11 @@ static int fb_get_panelinfo(FAR struct fb_chardev_s *fb,
           return ret;
         }
 
-      panelinfo->fbmem  = oinfo.fbmem;
-      panelinfo->fblen  = oinfo.fblen;
-      panelinfo->bpp    = oinfo.bpp;
+      panelinfo->fbmem   = oinfo.fbmem;
+      panelinfo->fblen   = oinfo.fblen;
+      panelinfo->fbcount = oinfo.yres_virtual == 0 ?
+                           1 : (oinfo.yres_virtual / oinfo.yres);
+      panelinfo->bpp     = oinfo.bpp;
       return OK;
     }
 #endif
@@ -954,9 +1126,18 @@ static int fb_get_panelinfo(FAR struct fb_chardev_s *fb,
       return ret;
     }
 
-  panelinfo->fbmem  = pinfo.fbmem;
-  panelinfo->fblen  = pinfo.fblen;
-  panelinfo->bpp    = pinfo.bpp;
+  ret = fb->vtable->getvideoinfo(fb->vtable, &vinfo);
+  if (ret < 0)
+    {
+      gerr("ERROR: getvideoinfo() failed: %d\n", ret);
+      return ret;
+    }
+
+  panelinfo->fbmem   = pinfo.fbmem;
+  panelinfo->fblen   = pinfo.fblen;
+  panelinfo->fbcount = pinfo.yres_virtual == 0 ?
+                       1 : (pinfo.yres_virtual / vinfo.yres);
+  panelinfo->bpp     = pinfo.bpp;
 
   return OK;
 }
@@ -994,18 +1175,24 @@ static int fb_get_planeinfo(FAR struct fb_chardev_s *fb,
 
 static void fb_do_pollnotify(wdparm_t arg)
 {
-  FAR struct fb_chardev_s *fb = (FAR struct fb_chardev_s *)arg;
+  FAR struct fb_paninfo_s *paninfo = (FAR struct fb_paninfo_s *)arg;
+  irqstate_t flags;
+  int i;
 
-  fb->pollcnt++;
+  flags = enter_critical_section();
 
-  /* Notify framebuffer is writable. */
+  for (i = 0; i < CONFIG_VIDEO_FB_NPOLLWAITERS; i++)
+    {
+      if (paninfo->fds[i] != NULL)
+        {
+          /* Notify framebuffer is writable. */
 
-  poll_notify(&fb->fds, 1, POLLOUT);
-}
+          poll_notify(&paninfo->fds[i], 1, POLLOUT);
+        }
+    }
 
-/****************************************************************************
- * Public Functions
- ****************************************************************************/
+  leave_critical_section(flags);
+}
 
 /****************************************************************************
  * Name: fb_pollnotify
@@ -1018,9 +1205,10 @@ static void fb_do_pollnotify(wdparm_t arg)
  *
  ****************************************************************************/
 
-void fb_pollnotify(FAR struct fb_vtable_s *vtable)
+static void fb_pollnotify(FAR struct fb_vtable_s *vtable, int overlay)
 {
   FAR struct fb_chardev_s *fb;
+  int id = overlay + 1;
 
   DEBUGASSERT(vtable != NULL);
 
@@ -1033,16 +1221,170 @@ void fb_pollnotify(FAR struct fb_vtable_s *vtable)
       return;
     }
 
+  DEBUGASSERT(id >= 0 && id < fb->paninfo_count);
+
   if (fb->vsyncoffset > 0)
     {
-      wd_start(&fb->wdog, fb->vsyncoffset, fb_do_pollnotify, (wdparm_t)fb);
+      wd_start(&fb->wdog, fb->vsyncoffset, fb_do_pollnotify,
+               (wdparm_t)(&fb->paninfo[id]));
     }
   else
     {
-      fb_do_pollnotify((wdparm_t)fb);
+      fb_do_pollnotify((wdparm_t)(&fb->paninfo[id]));
     }
 }
 
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fb_peek_paninfo
+ * Description:
+ *   Peek a frame from pan info queue of the specified overlay.
+ *
+ * Input Parameters:
+ *   vtable  - Pointer to framebuffer's virtual table.
+ *   info    - Pointer to pan info.
+ *   overlay - Overlay index.
+ *
+ * Returned Value:
+ *   Zero is returned on success; a negated errno value is returned on any
+ *   failure.
+ ****************************************************************************/
+
+int fb_peek_paninfo(FAR struct fb_vtable_s *vtable,
+                    FAR union fb_paninfo_u *info,
+                    int overlay)
+{
+  FAR struct circbuf_s *panbuf;
+  FAR struct fb_chardev_s *fb;
+  irqstate_t flags;
+  ssize_t ret;
+
+  /* Prevent calling before getting the vtable. */
+
+  fb = vtable->priv;
+  if (fb == NULL)
+    {
+      return -EINVAL;
+    }
+
+  panbuf = fb_get_panbuf(fb, overlay);
+  if (panbuf == NULL)
+    {
+      return -EINVAL;
+    }
+
+  flags = enter_critical_section();
+
+  /* Attempt to peek a frame from the vsync queue. */
+
+  ret = circbuf_peek(panbuf, info, sizeof(union fb_paninfo_u));
+  DEBUGASSERT(ret <= 0 || ret == sizeof(union fb_paninfo_u));
+
+  /* Re-enable interrupts */
+
+  leave_critical_section(flags);
+  return ret <= 0 ? -ENOSPC : OK;
+}
+
+/****************************************************************************
+ * Name: fb_remove_paninfo
+ * Description:
+ *   Remove a frame from pan info queue of the specified overlay.
+ *
+ * Input Parameters:
+ *   vtable  - Pointer to framebuffer's virtual table.
+ *   overlay - Overlay index.
+ *
+ * Returned Value:
+ *   Zero is returned on success; a negated errno value is returned on any
+ *   failure.
+ ****************************************************************************/
+
+int fb_remove_paninfo(FAR struct fb_vtable_s *vtable, int overlay)
+{
+  FAR struct circbuf_s *panbuf;
+  FAR struct fb_chardev_s *fb;
+  irqstate_t flags;
+  ssize_t ret;
+
+  fb = vtable->priv;
+  if (fb == NULL)
+    {
+      return -EINVAL;
+    }
+
+  panbuf = fb_get_panbuf(fb, overlay);
+  if (panbuf == NULL)
+    {
+      return -EINVAL;
+    }
+
+  flags = enter_critical_section();
+
+  /* Attempt to take a frame from the pan info. */
+
+  ret = circbuf_skip(panbuf, sizeof(union fb_paninfo_u));
+  DEBUGASSERT(ret <= 0 || ret == sizeof(union fb_paninfo_u));
+
+  /* Re-enable interrupts */
+
+  leave_critical_section(flags);
+
+  if (ret == sizeof(union fb_paninfo_u))
+    {
+      fb_pollnotify(vtable, overlay);
+    }
+
+  return ret <= 0 ? -ENOSPC : OK;
+}
+
+/****************************************************************************
+ * Name: fb_paninfo_count
+ * Description:
+ *   Get pan info count of specified overlay pan info queue.
+ *
+ * Input Parameters:
+ *   vtable  - Pointer to framebuffer's virtual table.
+ *   overlay - Overlay index.
+ *
+ * Returned Value:
+ *   a non-negative value is returned on success; a negated errno value is
+ *   returned on any failure.
+ ****************************************************************************/
+
+int fb_paninfo_count(FAR struct fb_vtable_s *vtable, int overlay)
+{
+  FAR struct circbuf_s *panbuf;
+  FAR struct fb_chardev_s *fb;
+  irqstate_t flags;
+  ssize_t ret;
+
+  /* Prevent calling before getting the vtable. */
+
+  fb = vtable->priv;
+  if (fb == NULL)
+    {
+      return -EINVAL;
+    }
+
+  panbuf = fb_get_panbuf(fb, overlay);
+  if (panbuf == NULL)
+    {
+      return -EINVAL;
+    }
+
+  flags = enter_critical_section();
+
+  ret = circbuf_used(panbuf) / sizeof(union fb_paninfo_u);
+
+  leave_critical_section(flags);
+
+  return ret;
+}
+
 /****************************************************************************
  * Name: fb_register
  *
@@ -1071,13 +1413,10 @@ int fb_register(int display, int plane)
   FAR struct fb_chardev_s *fb;
   struct fb_panelinfo_s panelinfo;
   struct fb_videoinfo_s vinfo;
-  struct fb_planeinfo_s pinfo;
-#ifdef CONFIG_FB_OVERLAY
-  struct fb_overlayinfo_s oinfo;
-#endif
   char devname[16];
   int nplanes;
   int ret;
+  size_t i;
 
   /* Allocate a framebuffer state instance */
 
@@ -1087,13 +1426,6 @@ int fb_register(int display, int plane)
       return -ENOMEM;
     }
 
-#ifdef CONFIG_FB_OVERLAY
-
-  /* Set the default overlay number */
-
-  fb->overlay = FB_NO_OVERLAY;
-#endif
-
   /* Initialize the frame buffer device. */
 
   ret = up_fbinitialize(display);
@@ -1127,58 +1459,39 @@ int fb_register(int display, int plane)
   nplanes = vinfo.nplanes;
   DEBUGASSERT(vinfo.nplanes > 0 && (unsigned)plane < vinfo.nplanes);
 
-  /* Get plane info */
+#ifdef CONFIG_FB_OVERLAY
+  fb->paninfo_count = vinfo.noverlays;
+#endif
 
-  ret = fb_get_planeinfo(fb, &pinfo, 0);
-  if (ret < 0)
-    {
-      goto errout_with_fb;
-    }
+  /* Add the primary framebuffer */
 
-  /* The initial value of pollcnt is the number of virtual framebuffers */
+  fb->paninfo_count += 1;
+  fb->paninfo = kmm_zalloc(sizeof(struct fb_paninfo_s) * fb->paninfo_count);
 
-  if (pinfo.yres_virtual > 0)
+  if (fb->paninfo == NULL)
     {
-      fb->pollcnt = pinfo.yres_virtual / vinfo.yres;
-      DEBUGASSERT(fb->pollcnt > 0);
-    }
-  else
-    {
-      fb->pollcnt = 1;
+      gerr("ERROR: alloc panbuf failed\n");
+      goto errout_with_fb;
     }
 
-  /* Get panel info */
-
-  ret = fb_get_panelinfo(fb, &panelinfo);
-
-  if (ret < 0)
+  for (i = 0; i < fb->paninfo_count; i++)
     {
-      goto errout_with_fb;
-    }
+      ret = fb_get_panelinfo(fb, &panelinfo, i - 1);
+      if (ret < 0)
+        {
+          goto errout_with_paninfo;
+        }
 
-  /* Clear the framebuffer memory */
+      ret = circbuf_init(&(fb->paninfo[i].buf), NULL, panelinfo.fbcount
+                         * sizeof(union fb_paninfo_u));
 
-  memset(panelinfo.fbmem, 0, panelinfo.fblen);
+      DEBUGASSERT(ret == 0);
 
-#ifdef CONFIG_FB_OVERLAY
-  /* Initialize first overlay but do not select */
+      /* Clear the framebuffer memory */
 
-  DEBUGASSERT(fb->vtable->getoverlayinfo != NULL);
-  memset(&oinfo, 0, sizeof(oinfo));
-  ret = fb->vtable->getoverlayinfo(fb->vtable, 0, &oinfo);
-  if (ret < 0)
-    {
-      gerr("ERROR: getoverlayinfo() failed: %d\n", ret);
-      goto errout_with_fb;
+      memset(panelinfo.fbmem, 0, panelinfo.fblen);
     }
 
-  /* Clear the overlay memory. Necessary when plane 0 and overlay 0
-   * different.
-   */
-
-  memset(oinfo.fbmem, 0, oinfo.fblen);
-#endif
-
   /* Register the framebuffer device */
 
   if (nplanes < 2)
@@ -1196,15 +1509,23 @@ int fb_register(int display, int plane)
   if (ret < 0)
     {
       gerr("ERROR: register_driver() failed: %d\n", ret);
-      goto errout_with_fb;
+      goto errout_with_nxmutex;
     }
 
   fb->vtable->priv = fb;
 
   return OK;
 
-errout_with_fb:
+errout_with_nxmutex:
   nxmutex_destroy(&fb->lock);
+errout_with_paninfo:
+  while (i-- > 0)
+    {
+      circbuf_uninit(&(fb->paninfo[i].buf));
+    }
+
+  kmm_free(fb->paninfo);
+errout_with_fb:
   kmm_free(fb);
   return ret;
 }
diff --git a/include/nuttx/video/fb.h b/include/nuttx/video/fb.h
index 9c69e45182..f597f502e6 100644
--- a/include/nuttx/video/fb.h
+++ b/include/nuttx/video/fb.h
@@ -181,6 +181,8 @@
 
 /* Hardware overlay acceleration ********************************************/
 
+#define FB_NO_OVERLAY         -1
+
 #ifdef CONFIG_FB_OVERLAY
 #  define FB_ACCL_TRANSP      0x01        /* Hardware tranparency support */
 #  define FB_ACCL_CHROMA      0x02        /* Hardware chromakey support */
@@ -673,6 +675,14 @@ struct fb_setcursor_s
 };
 #endif
 
+union fb_paninfo_u
+{
+  struct fb_planeinfo_s planeinfo;
+#ifdef CONFIG_FB_OVERLAY
+  struct fb_overlayinfo_s overlayinfo;
+#endif
+};
+
 /* The framebuffer "object" is accessed through within the OS via
  * the following vtable:
  */
@@ -996,17 +1006,55 @@ FAR struct fb_vtable_s *up_fbgetvplane(int display, int vplane);
 void up_fbuninitialize(int display);
 
 /****************************************************************************
- * Name: fb_pollnotify
+ * Name: fb_peek_paninfo
+ * Description:
+ *   Peek a frame from pan info queue of the specified overlay.
+ *
+ * Input Parameters:
+ *   vtable  - Pointer to framebuffer's virtual table.
+ *   info    - Pointer to pan info.
+ *   overlay - Overlay index.
+ *
+ * Returned Value:
+ *   Zero is returned on success; a negated errno value is returned on any
+ *   failure.
+ ****************************************************************************/
+
+int fb_peek_paninfo(FAR struct fb_vtable_s *vtable,
+                    FAR union fb_paninfo_u *info,
+                    int overlay);
+
+/****************************************************************************
+ * Name: fb_remove_paninfo
+ * Description:
+ *   Remove a frame from pan info queue of the specified overlay.
  *
+ * Input Parameters:
+ *   vtable  - Pointer to framebuffer's virtual table.
+ *   overlay - Overlay index.
+ *
+ * Returned Value:
+ *   Zero is returned on success; a negated errno value is returned on any
+ *   failure.
+ ****************************************************************************/
+
+int fb_remove_paninfo(FAR struct fb_vtable_s *vtable, int overlay);
+
+/****************************************************************************
+ * Name: fb_paninfo_count
  * Description:
- *   Notify the waiting thread that the framebuffer can be written.
+ *   Get pan info count of specified overlay pan info queue.
  *
  * Input Parameters:
- *   vtable - Pointer to framebuffer's virtual table.
+ *   vtable  - Pointer to framebuffer's virtual table.
+ *   overlay - Overlay index.
  *
+ * Returned Value:
+ *   a non-negative value is returned on success; a negated errno value is
+ *   returned on any failure.
  ****************************************************************************/
 
-void fb_pollnotify(FAR struct fb_vtable_s *vtable);
+int fb_paninfo_count(FAR struct fb_vtable_s *vtable, int overlay);
 
 /****************************************************************************
  * Name: fb_register