This is an automated email from the ASF dual-hosted git repository.
acassis 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 5accd7c146 drivers/video: add Goldfish-FB driver
5accd7c146 is described below
commit 5accd7c1468b93764b151fdb1019aa15f2d4aee9
Author: jianglianfang <[email protected]>
AuthorDate: Thu Jun 29 19:18:09 2023 +0800
drivers/video: add Goldfish-FB driver
the register definition is described here:
https://android.googlesource.com/platform/external/qemu/+/master/docs/GOLDFISH-VIRTUAL-HARDWARE.TXT
Goldfish-FB driver is to communicate with Goldfish-FB in QEMU, and push the
framebuffer data to the emulator for display on the screen.
test: CONFIG_GOLDFISH_FB =1, and run demo=>lvgldemo widgets
Signed-off-by: jianglianfang <[email protected]>
---
drivers/video/Kconfig | 28 +++
drivers/video/Make.defs | 4 +
drivers/video/goldfish_fb.c | 443 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 475 insertions(+)
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index fcb80547bd..3e1e38d181 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -56,6 +56,34 @@ config VIDEO_STREAM
---help---
Enable video Stream support
+config GOLDFISH_FB
+ bool "Goldfish Framebuffer character driver"
+ depends on VIDEO_FB
+ default n
+
+config GOLDFISH_FB_VIDEO_MODE
+ bool "Goldfish Framebuffer display mode"
+ depends on GOLDFISH_FB
+ default n
+ ---help---
+ GOLDFISH_FB_VIDEO_MODE = y enable video mode
+ GOLDFISH_FB_VIDEO_MODE = n enable command mode
+
+config GOLDFISH_FB_IRQ
+ int "Goldfish fb irq"
+ depends on GOLDFISH_FB
+ default 48
+
+config GOLDFISH_FB_BASE
+ hex "Goldfish fb base"
+ depends on GOLDFISH_FB
+ default 0x0a020000
+
+config GOLDFISH_FB_FRAME_NBUFFER
+ int "Goldfish fb vsync size"
+ depends on GOLDFISH_FB
+ default 2
+
if VIDEO_STREAM
config VIDEO_REQBUFS_COUNT_MAX
diff --git a/drivers/video/Make.defs b/drivers/video/Make.defs
index b40305ee28..3bd614f612 100644
--- a/drivers/video/Make.defs
+++ b/drivers/video/Make.defs
@@ -48,6 +48,10 @@ endif
endif
+ifeq ($(CONFIG_GOLDFISH_FB),y)
+ CSRCS += goldfish_fb.c
+endif
+
# These video drivers depend on SPI support
ifeq ($(CONFIG_SPI),y)
diff --git a/drivers/video/goldfish_fb.c b/drivers/video/goldfish_fb.c
new file mode 100644
index 0000000000..44d5158011
--- /dev/null
+++ b/drivers/video/goldfish_fb.c
@@ -0,0 +1,443 @@
+/****************************************************************************
+ * drivers/video/goldfish_fb.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <debug.h>
+#include <nuttx/config.h>
+#include <nuttx/video/fb.h>
+#include <nuttx/irq.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mm/circbuf.h>
+
+/****************************************************************************
+ * Pre-processor definitions
+ ****************************************************************************/
+
+#ifndef putreg32
+#define putreg32(v, x) (*(volatile uint32_t *)(x) = (v))
+#endif
+
+#ifndef getreg32
+#define getreg32(x) (*(uint32_t *)(x))
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum
+{
+ GOLDFISH_FB_GET_WIDTH = 0x00,
+ GOLDFISH_FB_GET_HEIGHT = 0x04,
+ GOLDFISH_FB_INT_STATUS = 0x08,
+ GOLDFISH_FB_INT_ENABLE = 0x0c,
+ GOLDFISH_FB_SET_BASE = 0x10,
+ GOLDFISH_FB_SET_ROTATION = 0x14,
+ GOLDFISH_FB_SET_BLANK = 0x18,
+ GOLDFISH_FB_GET_PHYS_WIDTH = 0x1c,
+ GOLDFISH_FB_GET_PHYS_HEIGHT = 0x20,
+ GOLDFISH_FB_GET_FORMAT = 0x24,
+ GOLDFISH_FB_INT_VSYNC = 1U << 0,
+ GOLDFISH_FB_INT_UPDATE_DONE = 1U << 1,
+ GOLDFISH_FB_FORMAT_BRGA_8888 = 1,
+ GOLDFISH_FB_FORMAT_RGBX_8888 = 2,
+ GOLDFISH_FB_FORMAT_RGB_888 = 3,
+ GOLDFISH_FB_FORMAT_RGB_565 = 4,
+ GOLDFISH_FB_FORMAT_BGRA_8888 = 5,
+ GOLDFISH_FB_FORMAT_RGBA_5551 = 6,
+ GOLDFISH_FB_FORMAT_RGBA_4444 = 8
+};
+
+struct goldfish_fb_format_s
+{
+ uint8_t fmt;
+ uint8_t bpp;
+};
+
+struct goldfish_fb_s
+{
+ struct fb_vtable_s vtable;
+ struct fb_planeinfo_s planeinfo;
+ struct fb_videoinfo_s videoinfo;
+ FAR void *base;
+ int irq;
+ struct circbuf_s vsync; /* Vsync event queued */
+#ifdef CONFIG_GOLDFISH_FB_VIDEO_MODE
+ bool busy; /* Only used in the video mode */
+ uintptr_t cur_buf;
+#endif
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static FAR struct goldfish_fb_s *g_goldfish_fb;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int goldfish_fb_pan_display(FAR struct fb_vtable_s *vtable,
+ FAR struct fb_planeinfo_s *pinfo);
+static int goldfish_getvideoinfo(FAR struct fb_vtable_s *vtable,
+ FAR struct fb_videoinfo_s *vinfo);
+static int goldfish_getplaneinfo(FAR struct fb_vtable_s *vtable, int planeno,
+ FAR struct fb_planeinfo_s *pinfo);
+static int goldfish_fb_interrupt(int irq, FAR void *dev_id, FAR void *arg);
+static void goldfish_fb_vsync_irq(FAR struct goldfish_fb_s *fb);
+static void goldfish_fb_framedone_irq(FAR struct goldfish_fb_s *fb);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: goldfish_fb_vsync_irq
+ ****************************************************************************/
+
+#ifdef CONFIG_GOLDFISH_FB_VIDEO_MODE
+static void goldfish_fb_vsync_irq(FAR struct goldfish_fb_s *fb)
+{
+ struct fb_planeinfo_s pinfo;
+
+ /* Attempte to retrieve a frame from the vsync queue */
+
+ ssize_t ret = circbuf_read(&fb->vsync, &pinfo,
+ sizeof(struct fb_planeinfo_s));
+ DEBUGASSERT(ret <= 0 || ret == sizeof(struct fb_planeinfo_s));
+
+ fb->busy = true;
+ if (ret > 0)
+ {
+ fb->cur_buf = (uintptr_t)((uint8_t *)fb->planeinfo.fbmem +
+ fb->planeinfo.stride *
+ pinfo.yoffset);
+ }
+
+ if (fb->cur_buf)
+ {
+ /* Send buffer addr to GOLDFISH */
+
+ putreg32(fb->cur_buf, fb->base + GOLDFISH_FB_SET_BASE);
+ }
+}
+#else
+static void goldfish_fb_vsync_irq(FAR struct goldfish_fb_s *fb)
+{
+ struct fb_planeinfo_s pinfo;
+
+ /* Attempte to retrieve a frame from the vsync queue */
+
+ ssize_t ret = circbuf_read(&fb->vsync, &pinfo,
+ sizeof(struct fb_planeinfo_s));
+ DEBUGASSERT(ret <= 0 || ret == sizeof(struct fb_planeinfo_s));
+
+ if (ret > 0)
+ {
+ uintptr_t buf = (uintptr_t)((uint8_t *)fb->planeinfo.fbmem +
+ fb->planeinfo.stride *
+ pinfo.yoffset);
+
+ /* Send buffer addr to GOLDFISH */
+
+ putreg32(buf, fb->base + GOLDFISH_FB_SET_BASE);
+ }
+}
+#endif
+
+/****************************************************************************
+ * Name: goldfish_fb_framedone_irq
+ ****************************************************************************/
+
+#ifdef CONFIG_GOLDFISH_FB_VIDEO_MODE
+static void goldfish_fb_framedone_irq(FAR struct goldfish_fb_s *fb)
+{
+ fb->busy = false;
+
+ if (fb->cur_buf && !circbuf_is_empty(&fb->vsync))
+ {
+ /* Clear the current frame buffer */
+
+ fb->cur_buf = 0;
+
+ /* After the sending is completed, notify the upper
+ * layer that the framebuffer can be written.
+ */
+
+ fb_pollnotify(&fb->vtable);
+ }
+}
+#else
+static void goldfish_fb_framedone_irq(FAR struct goldfish_fb_s *fb)
+{
+ /* After the sending is completed, notify the upper
+ * layer that the framebuffer can be written.
+ */
+
+ fb_pollnotify(&fb->vtable);
+}
+#endif
+
+/****************************************************************************
+ * Name: goldfish_fb_interrupt
+ ****************************************************************************/
+
+static int goldfish_fb_interrupt(int irq, FAR void *dev_id, FAR void *arg)
+{
+ FAR struct goldfish_fb_s *fb = arg;
+ irqstate_t flags;
+ uint32_t status;
+
+ flags = enter_critical_section();
+ status = getreg32(fb->base + GOLDFISH_FB_INT_STATUS);
+ if (status & GOLDFISH_FB_INT_VSYNC)
+ {
+ goldfish_fb_vsync_irq(fb);
+ }
+
+ else if (status & GOLDFISH_FB_INT_UPDATE_DONE)
+ {
+ goldfish_fb_framedone_irq(fb);
+ }
+
+ leave_critical_section(flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: goldfish_fb_pan_display
+ ****************************************************************************/
+
+static int goldfish_fb_pan_display(FAR struct fb_vtable_s *vtable,
+ FAR struct fb_planeinfo_s *pinfo)
+{
+ struct goldfish_fb_s *fb = (FAR struct goldfish_fb_s *)vtable;
+ irqstate_t flags;
+ ssize_t ret;
+
+ /** 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 the planeinfo information submitted
+ * by the renderer to the queue
+ */
+
+ ret = circbuf_write(&fb->vsync, pinfo,
+ sizeof(struct fb_planeinfo_s));
+ DEBUGASSERT(ret == sizeof(struct fb_planeinfo_s));
+
+#ifdef CONFIG_GOLDFISH_FB_VIDEO_MODE
+ if (fb->cur_buf && !fb->busy)
+ {
+ /* Clear the current frame buffer if not busy in transfering */
+
+ fb->cur_buf = 0;
+
+ /* Notify the upper layer that the framebuffer can be written */
+
+ fb_pollnotify(&fb->vtable);
+ }
+#endif
+
+ /* Re-enable interrupts */
+
+ leave_critical_section(flags);
+ return ret < 0 ? ret : 0;
+}
+
+/****************************************************************************
+ * Name: goldfish_getvideoinfo
+ ****************************************************************************/
+
+static int goldfish_getvideoinfo(FAR struct fb_vtable_s *vtable,
+ FAR struct fb_videoinfo_s *vinfo)
+{
+ FAR struct goldfish_fb_s *fb = (FAR struct goldfish_fb_s *)vtable;
+
+ ginfo("vtable=%p vinfo=%p\n", vtable, vinfo);
+ if (fb && vinfo)
+ {
+ memcpy(vinfo, &fb->videoinfo, sizeof(struct fb_videoinfo_s));
+ return OK;
+ }
+
+ gerr("ERROR: Returning EINVAL\n");
+ return -EINVAL;
+}
+
+/****************************************************************************
+ * Name: goldfish_getplaneinfo
+ ****************************************************************************/
+
+static int goldfish_getplaneinfo(FAR struct fb_vtable_s *vtable, int planeno,
+ FAR struct fb_planeinfo_s *pinfo)
+{
+ FAR struct goldfish_fb_s *fb = (FAR struct goldfish_fb_s *)vtable;
+
+ ginfo("vtable=%p planeno=%d pinfo=%p\n", vtable, planeno, pinfo);
+ if (fb && planeno == 0 && pinfo)
+ {
+ memcpy(pinfo, &fb->planeinfo, sizeof(struct fb_planeinfo_s));
+ return OK;
+ }
+
+ gerr("ERROR: Returning EINVAL\n");
+ return -EINVAL;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_fbinitialize
+ ****************************************************************************/
+
+int up_fbinitialize(int display)
+{
+ FAR struct goldfish_fb_s *fb;
+ uint32_t fmt;
+ int ret = OK;
+
+ const struct goldfish_fb_format_s format_map[] =
+ {
+ [GOLDFISH_FB_FORMAT_BRGA_8888] =
+ {FB_FMT_RGBA32, 32},
+ [GOLDFISH_FB_FORMAT_RGBX_8888] =
+ {FB_FMT_RGB32, 32},
+ [GOLDFISH_FB_FORMAT_RGB_888] =
+ {FB_FMT_RGB24, 24},
+ [GOLDFISH_FB_FORMAT_RGB_565] =
+ {FB_FMT_RGB16_565, 16},
+ [GOLDFISH_FB_FORMAT_BGRA_8888] =
+ {FB_FMT_RGBA32, 32},
+ [GOLDFISH_FB_FORMAT_RGBA_5551] =
+ {FB_FMT_RGB16_555, 16},
+ [GOLDFISH_FB_FORMAT_RGBA_4444] =
+ {FB_FMT_RGBA16, 16},
+ };
+
+ fb = kmm_zalloc(sizeof(*fb));
+ if (fb == NULL)
+ {
+ return -ENOMEM;
+ }
+
+ fb->base = (FAR void *)CONFIG_GOLDFISH_FB_BASE;
+ fb->irq = CONFIG_GOLDFISH_FB_IRQ;
+
+ /* Initialize vsync queue */
+
+ ret = circbuf_init(&fb->vsync, NULL,
+ CONFIG_GOLDFISH_FB_FRAME_NBUFFER *
+ sizeof(struct fb_planeinfo_s));
+ if (ret < 0)
+ {
+ goto err_circbuf_alloc_failed;
+ }
+
+ fmt = getreg32(fb->base + GOLDFISH_FB_GET_FORMAT);
+
+ fb->videoinfo.xres = getreg32(fb->base + GOLDFISH_FB_GET_WIDTH);
+ fb->videoinfo.yres = getreg32(fb->base + GOLDFISH_FB_GET_HEIGHT);
+ fb->videoinfo.nplanes = 1;
+ fb->videoinfo.fmt = format_map[fmt].fmt;
+
+ fb->planeinfo.bpp = format_map[fmt].bpp;
+ fb->planeinfo.stride = fb->videoinfo.xres * (fb->planeinfo.bpp >> 3);
+ fb->planeinfo.yres_virtual = fb->videoinfo.yres *
+ CONFIG_GOLDFISH_FB_FRAME_NBUFFER;
+ fb->planeinfo.xres_virtual = fb->videoinfo.xres;
+
+ fb->planeinfo.fblen = fb->planeinfo.stride * fb->planeinfo.yres_virtual;
+ fb->planeinfo.fbmem = kmm_zalloc(fb->planeinfo.fblen);
+ if (fb->planeinfo.fbmem == NULL)
+ {
+ gerr("ERROR: Failed to allocate framebuffer memory: %zu KB\n",
+ fb->planeinfo.fblen / 1024);
+ ret = -ENOMEM;
+ goto err_fbmem_alloc_failed;
+ }
+
+ fb->vtable.pandisplay = goldfish_fb_pan_display;
+ fb->vtable.getplaneinfo = goldfish_getplaneinfo;
+ fb->vtable.getvideoinfo = goldfish_getvideoinfo;
+
+ ret = irq_attach(fb->irq, goldfish_fb_interrupt, fb);
+ if (ret < 0)
+ {
+ goto err_irq_attach_failed;
+ }
+
+ up_enable_irq(fb->irq);
+ putreg32(GOLDFISH_FB_INT_VSYNC | GOLDFISH_FB_INT_UPDATE_DONE,
+ fb->base + GOLDFISH_FB_INT_ENABLE);
+
+ /* Updates base */
+
+ putreg32((uintptr_t)fb->planeinfo.fbmem,
+ fb->base + GOLDFISH_FB_SET_BASE);
+
+ g_goldfish_fb = fb;
+ return OK;
+
+err_irq_attach_failed:
+ kmm_free(fb->planeinfo.fbmem);
+err_fbmem_alloc_failed:
+ circbuf_uninit(&fb->vsync);
+err_circbuf_alloc_failed:
+ kmm_free(fb);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: up_fbgetvplane
+ ****************************************************************************/
+
+FAR struct fb_vtable_s *up_fbgetvplane(int display, int vplane)
+{
+ return vplane || display ? NULL : &(g_goldfish_fb->vtable);
+}
+
+/****************************************************************************
+ * Name: up_fbuninitialize
+ ****************************************************************************/
+
+void up_fbuninitialize(int display)
+{
+ if (display == 0)
+ {
+ FAR struct goldfish_fb_s *fb = g_goldfish_fb;
+
+ irq_detach(fb->irq);
+ circbuf_uninit(&fb->vsync);
+ kmm_free(fb->planeinfo.fbmem);
+ kmm_free(fb);
+ g_goldfish_fb = NULL;
+ }
+}