diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 56c2fe89fdc..871f4815523 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -3,6 +3,10 @@ # see the file kconfig-language.txt in the NuttX tools repository. # +config VIDEO_FB + bool "Framebuffer character driver" + default n + config VIDEO_OV2640 bool "OV2640 camera chip" default n diff --git a/drivers/video/Make.defs b/drivers/video/Make.defs index 27a5028488e..9a9e4dd5f53 100644 --- a/drivers/video/Make.defs +++ b/drivers/video/Make.defs @@ -1,7 +1,7 @@ ############################################################################ # drivers/ov2640/Make.defs # -# Copyright (C) 2013 Gregory Nutt. All rights reserved. +# Copyright (C) 2013, 2017 Gregory Nutt. All rights reserved. # Author: Gregory Nutt # # Redistribution and use in source and binary forms, with or without @@ -37,6 +37,10 @@ ifeq ($(CONFIG_VIDEO_DEVICES),y) +ifeq ($(CONFIG_VIDEO_FB),y) + CSRCS += fb.c +endif + # These video drivers depend on I2C support ifeq ($(CONFIG_I2C),y) diff --git a/drivers/video/fb.c b/drivers/video/fb.c new file mode 100644 index 00000000000..ff7bcfa6937 --- /dev/null +++ b/drivers/video/fb.c @@ -0,0 +1,524 @@ +/**************************************************************************** + * graphis/fb/fb.c + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* 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 + * structure. + */ + +struct fb_chardev_s +{ + FAR struct fb_vtable_s *vtable; /* Framebuffer interface */ + FAR void *fbmem; /* Start of frame buffer memory */ + size_t fblen; /* Size of the framebuffer */ + fb_coord_t xres; /* Horizontal resolution in pixel columns */ + fb_coord_t yres; /* Vertical resolution in pixel rows */ + fb_coord_t stride; /* Length of a line in bytes */ + uint8_t plane; /* Video plan number */ + uint8_t bpp; /* Bits per pixel */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static size_t fb_pixel2byte(FAR struct fb_chardev_s *fb, size_t pixsize); + +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, + size_t buflen); +static ssize_t fb_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static off_t fb_seek(FAR struct file *filep, off_t offset, int whence); +static int fb_ioctl(FAR struct file *filep, int cmd, unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations fb_fops = +{ + fb_open, /* open */ + fb_close, /* close */ + fb_read, /* read */ + fb_write, /* write */ + fb_seek, /* seek */ + fb_ioctl /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , NULL /* poll */ +#endif +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + , NULL /* unlink */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: fb_pixel2byte + * + * Description: + * Convert a value in units of pixels to units of bytes. + * + ****************************************************************************/ + +static size_t fb_pixel2byte(FAR struct fb_chardev_s *fb, size_t pixsize) +{ + return (pixsize * fb->bpp) >> 3; /* Overflow possible? */ +} + +/**************************************************************************** + * Name: fb_open + * + * Description: + * This function is called whenever the framebuffer device is opened. + * + ****************************************************************************/ + +static int fb_open(FAR struct file *filep) +{ + DEBUGASSERT(filep != NULL && filep->f_inode != NULL); + return OK; +} + +/**************************************************************************** + * Name: fb_close + * + * Description: + * This function is called when the framebuffer device is closed. + * + ****************************************************************************/ + +static int fb_close(FAR struct file *filep) +{ + DEBUGASSERT(filep != NULL && filep->f_inode != NULL); + return OK; +} + +/**************************************************************************** + * Name: fb_read + ****************************************************************************/ + +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; + size_t start; + size_t end; + size_t size; + + ginfo("len: %u\n", (unsigned int)len); + + /* Get the framebuffer instance */ + + DEBUGASSERT(filep != NULL && filep->f_inode != NULL); + inode = filep->f_inode; + fb = (FAR struct fb_chardev_s *)inode->i_private; + + /* Get the start and size of the transfer */ + + start = fb_pixel2byte(fb, filep->f_pos); + if (start >= fb->fblen) + { + return 0; /* Return end-of-file */ + } + + end = start + len; + if (end >= fb->fblen) + { + end = fb->fblen; + } + + size = end - start; + + /* And transfer the data from the frame buffer */ + + memcpy(buffer, fb->fbmem, size); + filep->f_pos += size; + return size; +} + +/**************************************************************************** + * Name: fb_write + ****************************************************************************/ + +static ssize_t fb_write(FAR struct file *filep, FAR const char *buffer, size_t len) +{ + FAR struct inode *inode; + FAR struct fb_chardev_s *fb; + size_t start; + size_t end; + size_t size; + + ginfo("len: %u\n", (unsigned int)len); + + /* Get the framebuffer instance */ + + DEBUGASSERT(filep != NULL && filep->f_inode != NULL); + inode = filep->f_inode; + fb = (FAR struct fb_chardev_s *)inode->i_private; + + /* Get the start and size of the transfer */ + + start = fb_pixel2byte(fb, filep->f_pos); + if (start >= fb->fblen) + { + return -EFBIG; /* Cannot extend the framebuffer */ + } + + end = start + len; + if (end >= fb->fblen) + { + end = fb->fblen; + } + + size = end - start; + + /* And transfer the data into the frame buffer */ + + memcpy(fb->fbmem, buffer, size); + filep->f_pos += size; + return size; +} + +/**************************************************************************** + * Name: fb_seek + * + * Description: + * Seek the logical file pointer to the specified position. The offset + * is in units of pixels, with offset zero being the beginning of the + * framebuffer. + * + ****************************************************************************/ + +static off_t fb_seek(FAR struct file *filep, off_t offset, int whence) +{ + FAR struct inode *inode; + FAR struct fb_chardev_s *fb; + off_t newpos; + int ret; + + ginfo("offset: %u whence: %d\n", (unsigned int)offset, whence); + + /* Get the framebuffer instance */ + + DEBUGASSERT(filep != NULL && filep->f_inode != NULL); + inode = filep->f_inode; + fb = (FAR struct fb_chardev_s *)inode->i_private; + + /* Determine the new, requested file position */ + + switch (whence) + { + case SEEK_CUR: + newpos = filep->f_pos + offset; + break; + + case SEEK_SET: + newpos = offset; + break; + + case SEEK_END: + newpos = fb->fblen + offset; + break; + + default: + /* Return EINVAL if the whence argument is invalid */ + + return -EINVAL; + } + + /* Opengroup.org: + * + * "The lseek() function shall allow the file offset to be set beyond the end + * of the existing data in the file. If data is later written at this point, + * subsequent reads of data in the gap shall return bytes with the value 0 + * until data is actually written into the gap." + * + * We can conform to the first part, but not the second. But return EINVAL if + * + * "...the resulting file offset would be negative for a regular file, block + * special file, or directory." + */ + + if (newpos >= 0) + { + filep->f_pos = newpos; + ret = newpos; + } + else + { + ret = -EINVAL; + } + + return ret; +} + +/**************************************************************************** + * Name: fb_ioctl + * + * Description: + * The standard ioctl method. + * + ****************************************************************************/ + +static int fb_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct inode *inode; + FAR struct fb_chardev_s *fb; + int ret; + + ginfo("cmd: %d arg: %ld\n", cmd, arg); + + /* Get the framebuffer instance */ + + DEBUGASSERT(filep != NULL && filep->f_inode != NULL); + inode = filep->f_inode; + fb = (FAR struct fb_chardev_s *)inode->i_private; + + /* Process the IOCTL command */ + + switch (cmd) + { + case FIOC_MMAP: /* Get color plane info */ + { + FAR void **ppv = (FAR void **)((uintptr_t)arg); + + /* Return the address corresponding to the start of frame buffer. */ + + DEBUGASSERT(ppv != NULL); + *ppv = fb->fbmem; + ret = OK; + } + break; + + case FBIOGET_VIDEOINFO: /* Get color plane info */ + { + FAR struct fb_videoinfo_s *vinfo = + (FAR struct fb_videoinfo_s *)((uintptr_t)arg); + + DEBUGASSERT(vinfo != 0 && fb->vtable != NULL && + fb->vtable->getvideoinfo != NULL); + ret = fb->vtable->getvideoinfo(fb->vtable, vinfo); + } + break; + + case FBIOGET_PLANEINFO: /* Get video plane info */ + { + FAR struct fb_planeinfo_s *pinfo = + (FAR struct fb_planeinfo_s *)((uintptr_t)arg); + + DEBUGASSERT(pinfo != 0 && fb->vtable != NULL && + fb->vtable->getplaneinfo != NULL); + ret = fb->vtable->getplaneinfo(fb->vtable, fb->plane, pinfo); + } + break; + +#ifdef CONFIG_FB_CMAP + case FBIOGET_CMAP: /* Get RGB color mapping */ + { + FAR struct fb_cmap_s *cmap = + (FAR struct fb_cmap_s *)((uintptr_t)arg); + + DEBUGASSERT(cmap != 0 && fb->vtable != NULL && + fb->vtable->getcmap != NULL); + ret = fb->vtable->getcmap(fb->vtable, cmap); + } + break; + + case FBIOPUT_CMAP: /* Put RGB color mapping */ + { + FAR const struct fb_cmap_s *cmap = + (FAR struct fb_cmap_s *)((uintptr_t)arg); + + DEBUGASSERT(cmap != 0 && fb->vtable != NULL && + fb->vtable->putcmap != NULL); + ret = fb->vtable->putcmap(fb->vtable, cmap); + } + break; +#endif +#ifdef CONFIG_FB_HWCURSOR + case FBIOGET_CURSOR: /* Get cursor attributes */ + { + FAR struct fb_cursorattrib_s *attrib = + (FAR struct fb_cursorattrib_s *)((uintptr_t)arg); + + DEBUGASSERT(attrib != 0 && fb->vtable != NULL && + fb->vtable->getcursor != NULL); + ret = fb->vtable->getcursor(fb->vtable, attrib); + } + break; + + case FBIOPUT_CURSOR: /* Set cursor attibutes */ + { + FAR struct fb_setcursor_s *cursor = + (FAR struct fb_setcursor_s *)((uintptr_t)arg); + + DEBUGASSERT(cursor != 0 && fb->vtable != NULL && + fb->vtable->setcursor != NULL); + ret = fb->vtable->setcursor(fb->vtable, cursor); + } + break; +#endif + + default: + gerr("ERROR: Unsupported IOCTL command: %d\n", cmd); + ret = -ENOTTY; + break; + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: fb_register + * + * Description: + * Register the framebuffer device at /dev/fbN-M where N is the display + * number and M is the display plane for displays with multiple color + * planes. + * + * Returned Value: + * Zero (OK) is returned success; a negated errno value is returned on any + * failure. + * + ****************************************************************************/ + +int fb_register(int display, int plane) +{ + FAR struct fb_chardev_s *fb; + struct fb_videoinfo_s vinfo; + struct fb_planeinfo_s pinfo; + char devname[16]; + int ret; + + /* Allocate a framebuffer state instance */ + + fb = (FAR struct fb_chardev_s *)kmm_zalloc(sizeof(struct fb_chardev_s)); + if (fb == NULL) + { + return -ENOMEM; + } + + /* Initialize the frame buffer device. */ + + ret = up_fbinitialize(display); + if (ret < 0) + { + gerr("ERROR: up_fbinitialize() failed for display %d: %d\n", display, ret); + goto errout_with_fb; + } + + DEBUGASSERT((unsigned)plane <= UINT8_MAX); + fb->plane = plane; + + fb->vtable = up_fbgetvplane(display, plane); + if (fb->vtable == NULL) + { + gerr("ERROR: up_fbgetvplane() failed, vplane=%d\n", plane); + goto errout_with_fb; + } + + /* Initialize the frame buffer instance. */ + + DEBUGASSERT(fb->vtable->getvideoinfo != NULL); + ret = fb->vtable->getvideoinfo(fb->vtable, &vinfo); + if (ret < 0) + { + gerr("ERROR: getvideoinfo() failed: %d\n", ret); + goto errout_with_fb; + } + + fb->xres = vinfo.xres; + fb->yres = vinfo.yres; + + DEBUGASSERT(fb->vtable->getplaneinfo != NULL); + ret = fb->vtable->getplaneinfo(fb->vtable, plane, &pinfo); + if (ret < 0) + { + gerr("ERROR: getplaneinfo() failed: %d\n", ret); + goto errout_with_fb; + } + + fb->fbmem = pinfo.fbmem; + fb->fblen = pinfo.fblen; + fb->stride = pinfo.stride; + fb->bpp = pinfo.bpp; + + /* Register the framebuffer device */ + + (void)snprintf(devname, 16, "/dev/fb%d-%d", display, plane); + ret = register_driver(devname, &fb_fops, 0666, (FAR void *)fb); + if (ret < 0) + { + gerr("ERROR: register_driver() failed: %d\n", ret); + goto errout_with_fb; + } + + return OK; + +errout_with_fb: + kmm_free(fb); + return ret; +} diff --git a/include/nuttx/fs/ioctl.h b/include/nuttx/fs/ioctl.h index 05f3d7288d9..2f3bc2faa66 100644 --- a/include/nuttx/fs/ioctl.h +++ b/include/nuttx/fs/ioctl.h @@ -90,6 +90,7 @@ #define _USBCBASE (0x2500) /* USB-C controller ioctl commands */ #define _MAC802154BASE (0x2600) /* 802.15.4 MAC ioctl commands */ #define _PWRBASE (0x2700) /* Power-related ioctl commands */ +#define _FBIOCBASE (0x2800) /* Frame buffer character driver ioctl commands */ /* boardctl() commands share the same number space */ @@ -431,9 +432,14 @@ /* Power-Related IOCTLs *****************************************************/ -#define _PWRIOCVALID(c) (_IOC_TYPE(c)==_SMPS_BASE) +#define _PWRIOCVALID(c) (_IOC_TYPE(c)==_PWRBASE) #define _PWRIOC(nr) _IOC(_PWRBASE,nr) +/* Frame buffer character drivers *******************************************/ + +#define _FBIOCVALID(c) (_IOC_TYPE(c)==_FBIOCBASE) +#define _FBIOC(nr) _IOC(_FBIOCBASE,nr) + /* boardctl() command definitions *******************************************/ #define _BOARDIOCVALID(c) (_IOC_TYPE(c)==_BOARDBASE) diff --git a/include/nuttx/video/fb.h b/include/nuttx/video/fb.h index fec81822ca9..2ae27204916 100644 --- a/include/nuttx/video/fb.h +++ b/include/nuttx/video/fb.h @@ -42,6 +42,7 @@ #include #include +#include /**************************************************************************** * Pre-processor definitions @@ -190,6 +191,27 @@ #define FB_CUR_XOR 0x10 /* Use XOR vs COPY ROP on image */ #endif +/* FB character driver IOCTL commands ***************************************/ + +/* ioctls */ + +#define FBIOGET_VIDEOINFO _FBIOC(0x0001) /* Get color plane info */ + /* Argument: writable struct fb_videoinfo_s */ +#define FBIOGET_PLANEINFO _FBIOC(0x0002) /* Get video plane info */ + /* Argument: writable struct fb_planeinfo_s */ +#ifdef CONFIG_FB_CMAP +# define FBIOGET_CMAP _FBIOC(0x0003) /* Get RGB color mapping */ + /* Argument: writable struct fb_cmap_s */ +# define FBIOPUT_CMAP _FBIOC(0x0004) /* Put RGB color mapping */ + /* Argument: read-only struct fb_cmap_s */ +#endif +#ifdef CONFIG_FB_HWCURSOR +# define FBIOGET_CURSOR _FBIOC(0x0005) /* Get cursor attributes */ + /* Argument: writable struct fb_cursorattrib_s */ +# define FBIOPUT_CURSOR _FBIOC(0x0006) /* Set cursor attibutes */ + /* Argument: read-only struct fb_setcursor_s */ +#endif + /**************************************************************************** * Public Types ****************************************************************************/ @@ -307,8 +329,8 @@ struct fb_setcursor_s }; #endif -/* The framebuffer "driver" under NuttX is not a driver at all, but simply - * a driver "object" that is accessed through the following vtable: +/* The framebuffer "object" is accessed through within the OS via + * the following vtable: */ struct fb_vtable_s @@ -322,22 +344,22 @@ struct fb_vtable_s int (*getplaneinfo)(FAR struct fb_vtable_s *vtable, int planeno, FAR struct fb_planeinfo_s *pinfo); +#ifdef CONFIG_FB_CMAP /* The following are provided only if the video hardware supports RGB * color mapping */ -#ifdef CONFIG_FB_CMAP int (*getcmap)(FAR struct fb_vtable_s *vtable, FAR struct fb_cmap_s *cmap); int (*putcmap)(FAR struct fb_vtable_s *vtable, FAR const struct fb_cmap_s *cmap); #endif +#ifdef CONFIG_FB_HWCURSOR /* The following are provided only if the video hardware supports a * hardware cursor. */ -#ifdef CONFIG_FB_HWCURSOR int (*getcursor)(FAR struct fb_vtable_s *vtable, FAR struct fb_cursorattrib_s *attrib); int (*setcursor)(FAR struct fb_vtable_s *vtable,