Skip to content

Commit

Permalink
feat(disp): update nuttx disp framebuffer (lvgl#4591)
Browse files Browse the repository at this point in the history
Signed-off-by: rongyichang <rongyichang@xiaomi.com>
  • Loading branch information
terry0012 authored Sep 25, 2023
1 parent 27357d5 commit 064a844
Show file tree
Hide file tree
Showing 9 changed files with 345 additions and 27 deletions.
8 changes: 4 additions & 4 deletions Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1190,10 +1190,6 @@ menu "LVGL configuration"
bool "Use BSD flavored framebuffer device"
depends on LV_USE_LINUX_FBDEV
default n
config LV_LINUX_FBDEV_NUTTX
bool "Use NuttX flavored framebuffer device"
depends on LV_USE_LINUX_FBDEV
default n

choice
prompt "Framebuffer device render mode"
Expand Down Expand Up @@ -1247,6 +1243,10 @@ menu "LVGL configuration"
depends on LV_USE_LINUX_FBDEV && LV_LINUX_FBDEV_CUSTOM_BUFFER
default 60

config LV_USE_NUTTX_FBDEV
bool "Use Nuttx framebuffer device"
default n

config LV_USE_NUTTX_LCD
bool "Use NuttX LCD device"
default n
Expand Down
2 changes: 2 additions & 0 deletions env_support/cmsis-pack/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ Make sure `LV_MEM_SIZE` is no less than `(64*1024U)`.

- LV_USE_LINUX_FBDEV

- LV_USE_NUTTX_FBDEV

- LV_USE_LINUX_DRM

- LV_USE_TFT_ESPI
Expand Down
3 changes: 2 additions & 1 deletion lv_conf_template.h
Original file line number Diff line number Diff line change
Expand Up @@ -745,12 +745,13 @@
#define LV_USE_LINUX_FBDEV 0
#if LV_USE_LINUX_FBDEV
#define LV_LINUX_FBDEV_BSD 0
#define LV_LINUX_FBDEV_NUTTX 0
#define LV_LINUX_FBDEV_RENDER_MODE LV_DISPLAY_RENDER_MODE_PARTIAL
#define LV_LINUX_FBDEV_BUFFER_COUNT 0
#define LV_LINUX_FBDEV_BUFFER_SIZE 60
#endif

#define LV_USE_NUTTX_FBDEV 0

/*Driver for /dev/lcd*/
#define LV_USE_NUTTX_LCD 0
#if LV_USE_NUTTX_LCD
Expand Down
1 change: 1 addition & 0 deletions lvgl.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ extern "C" {

#include "src/dev/display/drm/lv_linux_drm.h"
#include "src/dev/display/fb/lv_linux_fbdev.h"
#include "src/dev/display/fb/lv_nuttx_fbdev.h"
#include "src/dev/display/lcd/lv_nuttx_lcd.h"

#include "src/dev/input/touchscreen/lv_nuttx_touchscreen.h"
Expand Down
14 changes: 0 additions & 14 deletions src/dev/display/fb/lv_linux_fbdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@
#include <sys/time.h>
#include <sys/consio.h>
#include <sys/fbio.h>
#elif LV_LINUX_FBDEV_NUTTX
#include <nuttx/video/fb.h>
#else
#include <linux/fb.h>
#endif /* LV_LINUX_FBDEV_BSD */
Expand Down Expand Up @@ -130,13 +128,11 @@ void lv_linux_fbdev_set_file(lv_display_t * disp, const char * file)
}
LV_LOG_INFO("The framebuffer device was opened successfully");

#if !LV_LINUX_FBDEV_NUTTX
/* Make sure that the display is on.*/
if(ioctl(dsc->fbfd, FBIOBLANK, FB_BLANK_UNBLANK) != 0) {
perror("ioctl(FBIOBLANK)");
/* Don't return. Some framebuffer drivers like efifb or simplefb don't implement FBIOBLANK.*/
}
#endif /* !LV_LINUX_FBDEV_NUTTX */

#if LV_LINUX_FBDEV_BSD
struct fbtype fb;
Expand Down Expand Up @@ -256,16 +252,6 @@ static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * colo
}
}

#if LV_LINUX_FBDEV_NUTTX && defined(CONFIG_FB_UPDATE)
/*May be some direct update command is required*/
struct fb_area_s fb_area;
fb_area.x = area->x1;
fb_area.y = area->y1;
fb_area.w = lv_area_get_width(area);
fb_area.h = lv_area_get_height(area);
ioctl(dsc->fbfd, FBIO_UPDATE, (unsigned long)((uintptr_t)&fb_area));
#endif

lv_display_flush_ready(disp);
}

Expand Down
281 changes: 281 additions & 0 deletions src/dev/display/fb/lv_nuttx_fbdev.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
/**
* @file lv_nuttx_fbdev.c
*
*/

/*********************
* INCLUDES
*********************/
#include "lv_nuttx_fbdev.h"
#if LV_USE_NUTTX_FBDEV

#include <stdlib.h>
#include <unistd.h>
#include <stddef.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <nuttx/video/fb.h>
#include <lvgl/lvgl.h>
#include "../../../lvgl_private.h"

/*********************
* DEFINES
*********************/

/**********************
* TYPEDEFS
**********************/

typedef struct {
struct fb_videoinfo_s vinfo;
struct fb_planeinfo_s pinfo;

void * mem;
void * mem2;
uint32_t mem2_yoffset;
int fd;
} lv_nuttx_fb_t;

/**********************
* STATIC PROTOTYPES
**********************/

static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * color_p);
static int fbdev_get_pinfo(int fd, struct fb_planeinfo_s * pinfo);
static int fbdev_init_mem2(lv_nuttx_fb_t * dsc);

/**********************
* STATIC VARIABLES
**********************/

/**********************
* MACROS
**********************/

/**********************
* GLOBAL FUNCTIONS
**********************/

/****************************************************************************
* Name: lv_nuttx_fbdev_create
****************************************************************************/

lv_display_t * lv_nuttx_fbdev_create(void)
{
lv_nuttx_fb_t * dsc = lv_malloc(sizeof(lv_nuttx_fb_t));
LV_ASSERT_MALLOC(dsc);
if(dsc == NULL) return NULL;
lv_memzero(dsc, sizeof(lv_nuttx_fb_t));

lv_display_t * disp = lv_display_create(800, 480);
if(disp == NULL) {
lv_free(dsc);
return NULL;
}
dsc->fd = -1;
lv_display_set_driver_data(disp, dsc);
lv_display_set_flush_cb(disp, flush_cb);
return disp;
}

/****************************************************************************
* Name: lv_nuttx_fbdev_set_file
****************************************************************************/

int lv_nuttx_fbdev_set_file(lv_display_t * disp, const char * file)
{
int ret;
LV_ASSERT(disp && file);
lv_nuttx_fb_t * dsc = lv_display_get_driver_data(disp);

if(dsc->fd >= 0) close(dsc->fd);

/* Open the file for reading and writing*/
dsc->fd = open(file, O_RDWR);
if(dsc->fd < 0) {
LV_LOG_ERROR("Error: cannot open framebuffer device");
return -errno;
}
LV_LOG_INFO("The framebuffer device was opened successfully");

if(ioctl(dsc->fd, FBIOGET_VIDEOINFO, (unsigned long)((uintptr_t)&dsc->vinfo)) < 0) {
LV_LOG_ERROR("ioctl(FBIOGET_VIDEOINFO) failed: %d", errno);
ret = -errno;
goto errout;
}

LV_LOG_INFO("VideoInfo:");
LV_LOG_INFO(" fmt: %u", dsc->vinfo.fmt);
LV_LOG_INFO(" xres: %u", dsc->vinfo.xres);
LV_LOG_INFO(" yres: %u", dsc->vinfo.yres);
LV_LOG_INFO(" nplanes: %u", dsc->vinfo.nplanes);

if((ret = fbdev_get_pinfo(dsc->fd, &dsc->pinfo)) < 0) {
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);
ret = -errno;
goto errout;
}

/* double buffer mode */
if(dsc->pinfo.yres_virtual == (dsc->vinfo.yres * 2)) {
if((ret = fbdev_init_mem2(dsc)) < 0) {
goto errout;
}
}

lv_display_set_draw_buffers(disp, dsc->mem, dsc->mem2,
(dsc->pinfo.stride * dsc->vinfo.yres), LV_DISP_RENDER_MODE_DIRECT);
lv_display_set_resolution(disp, dsc->vinfo.xres, dsc->vinfo.yres);

LV_LOG_INFO("Resolution is set to %dx%d at %ddpi", dsc->vinfo.xres, dsc->vinfo.yres, lv_display_get_dpi(disp));
return 0;

errout:
close(dsc->fd);
dsc->fd = -1;
return ret;
}

/**********************
* STATIC FUNCTIONS
**********************/

static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * color_p)
{
lv_nuttx_fb_t * dsc = lv_display_get_driver_data(disp);

if(dsc->mem == NULL ||
area->x2 < 0 || area->y2 < 0 ||
area->x1 > (int32_t)dsc->vinfo.xres - 1 || area->y1 > (int32_t)dsc->vinfo.yres - 1) {
lv_display_flush_ready(disp);
return;
}

#if defined(CONFIG_FB_UPDATE)
/*May be some direct update command is required*/
struct fb_area_s fb_area;
fb_area.x = area->x1;
fb_area.y = area->y1;
fb_area.w = lv_area_get_width(area);
fb_area.h = lv_area_get_height(area);
if(ioctl(dsc->fd, FBIO_UPDATE, (unsigned long)((uintptr_t)&fb_area)) < 0) {
LV_LOG_ERROR("ioctl(FBIO_UPDATE) failed: %d", errno);
}
#endif

/* double framebuffer */
if(dsc->mem2 != NULL) {
if(disp->buf_act == disp->buf_1) {
dsc->pinfo.yoffset = 0;
}
else {
dsc->pinfo.yoffset = dsc->mem2_yoffset;
}

if(ioctl(dsc->fd, FBIOPAN_DISPLAY, (unsigned long)((uintptr_t) & (dsc->pinfo))) < 0) {
LV_LOG_ERROR("ioctl(FBIOPAN_DISPLAY) failed: %d", errno);
}
}
lv_display_flush_ready(disp);
}


/****************************************************************************
* Name: fbdev_get_pinfo
****************************************************************************/

static int fbdev_get_pinfo(int fd, FAR struct fb_planeinfo_s * pinfo)
{
if(ioctl(fd, FBIOGET_PLANEINFO, (unsigned long)((uintptr_t)pinfo)) < 0) {
LV_LOG_ERROR("ERROR: ioctl(FBIOGET_PLANEINFO) failed: %d", errno);
return -errno;
}

LV_LOG_INFO("PlaneInfo (plane %d):", pinfo->display);
LV_LOG_INFO(" mem: %p", pinfo->mem);
LV_LOG_INFO(" fblen: %zu", pinfo->fblen);
LV_LOG_INFO(" stride: %u", pinfo->stride);
LV_LOG_INFO(" display: %u", pinfo->display);
LV_LOG_INFO(" bpp: %u", pinfo->bpp);

/* Only these pixel depths are supported. viinfo.fmt is ignored, only
* certain color formats are supported.
*/

if(pinfo->bpp != 32 && pinfo->bpp != 16 &&
pinfo->bpp != 8 && pinfo->bpp != 1) {
LV_LOG_ERROR("bpp = %u not supported", pinfo->bpp);
return -EINVAL;
}

return 0;
}

/****************************************************************************
* Name: fbdev_init_mem2
****************************************************************************/

static int fbdev_init_mem2(lv_nuttx_fb_t * dsc)
{
uintptr_t buf_offset;
struct fb_planeinfo_s pinfo;
int ret;

memset(&pinfo, 0, sizeof(pinfo));

/* Get display[1] planeinfo */

pinfo.display = dsc->pinfo.display + 1;

if((ret = fbdev_get_pinfo(dsc->fd, &pinfo)) < 0) {
return ret;
}

/* Check bpp */

if(pinfo.bpp != dsc->pinfo.bpp) {
LV_LOG_WARN("mem2 is incorrect");
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_INFO("Use consecutive mem2 = %p, yoffset = %" PRIu32,
dsc->mem2, dsc->mem2_yoffset);
}
else {
dsc->mem2_yoffset = buf_offset / dsc->pinfo.stride;
dsc->mem2 = pinfo.fbmem;
LV_LOG_INFO("Use non-consecutive mem2 = %p, yoffset = %" PRIu32,
dsc->mem2, dsc->mem2_yoffset);
}

return 0;
}

#endif /*LV_USE_NUTTX_FBDEV*/
Loading

0 comments on commit 064a844

Please sign in to comment.