diff --git a/drivers/lcd/Kconfig b/drivers/lcd/Kconfig index f9914b35e34..bbc9d654ad7 100644 --- a/drivers/lcd/Kconfig +++ b/drivers/lcd/Kconfig @@ -21,6 +21,23 @@ if LCD comment "Common Graphic LCD Settings" +config LCD_FRAMEBUFFER + bool "LCD framebuffer front end" + default n + select NX_UPDATE if NX + ---help--- + Enable a "front end" that converts an sequential LCD driver into a + standard, NuttX frame buffer driver. + + NOTE: Some LCD drivers implement an internal framebuffer for + effeciency reasons but do not export a framebuffer interface. So + those LCD cannot be used as framebuffer drivers. If the option is + available, then such internal framebuffer support should be + disabled because this external commone framebuffer interface will + provide the necessary buffering. + +menu "LCD driver selection" + config LCD_CONSOLE bool "LCD console output" default n @@ -118,6 +135,11 @@ config P14201_FRAMEBUFFER All pixel writes must be aligned to byte boundaries. The latter limitation effectively reduces the 128x96 disply to 64x96. + NOTE: This option should not be used if CONFIG_LCD_FRAMBEBUFFER is + enabled. That options provides for a more geneneralized, external + LCD framebuffer. This internal framebuffer support should not be + enabled with CONFIG_LCD_FRAMBEBUFFER because this external commone + framebuffer interface will provide the necessary buffering. endif config LCD_NOKIA6100 @@ -1083,6 +1105,7 @@ config LCD_RA8875_EXTENDED driver anyway. endif # LCD_RA8875 +endmenu # LCD Driver selection endif # LCD menuconfig SLCD diff --git a/drivers/lcd/Make.defs b/drivers/lcd/Make.defs index 43d5ebd61d4..658d48d807b 100644 --- a/drivers/lcd/Make.defs +++ b/drivers/lcd/Make.defs @@ -1,7 +1,7 @@ ############################################################################ # drivers/lcd/Make.defs # -# Copyright (C) 2010-2012 Gregory Nutt. All rights reserved. +# Copyright (C) 2010-2012, 2017 Gregory Nutt. All rights reserved. # Author: Gregory Nutt # # Redistribution and use in source and binary forms, with or without @@ -33,7 +33,13 @@ # ############################################################################ -ifeq ($(CONFIG_NX_LCDDRIVER),y) +ifeq ($(CONFIG_LCD),y) + +# Support for the generic LCD framebufer front-end + +ifeq ($(CONFIG_LCD_FRAMEBUFFER),y) + CSRCS += lcd_framebuffer.c +endif # Include support for Graphics LCD drivers @@ -117,7 +123,7 @@ endif # CONFIG_SLCD # Include LCD driver build support (the nested if-then-else implements an OR) -ifeq ($(CONFIG_NX_LCDDRIVER),y) +ifeq ($(CONFIG_LCD),y) DEPPATH += --dep-path lcd VPATH += :lcd CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" $(TOPDIR)$(DELIM)drivers$(DELIM)lcd} diff --git a/drivers/lcd/lcd_framebuffer.c b/drivers/lcd/lcd_framebuffer.c new file mode 100644 index 00000000000..29c750fedc4 --- /dev/null +++ b/drivers/lcd/lcd_framebuffer.c @@ -0,0 +1,705 @@ +/**************************************************************************** + * drivers/lcd/lcd_frambuffer.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 +#include +#include +#include + +#ifdef CONFIG_LCD_FRAMEBUFFER + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* At present, only video plane 0 is supported */ + +#define VIDEO_PLANE 0 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure describes the LCD frambuffer */ + +struct lcdfb_dev_s +{ + struct fb_vtable_s vtable; /* Must be cast compatible with lcdfb_dev_s */ + FAR struct lcdfb_dev_s *flink; /* Supports a singly linked list */ + FAR struct lcd_dev_s *lcd; /* Contained LCD device */ + FAR uint8_t *fbmem; /* Allocated framebuffer */ + FAR struct lcd_planeinfo_s pinfo; /* LCD plane info */ + size_t fblen; /* Size of the framebuffer in bytes */ + fb_coord_t xres; /* Horizontal resolution in pixel columns */ + fb_coord_t yres; /* Vertical resolution in pixel rows */ + fb_coord_t stride; /* Width of a row in bytes */ + uint8_t display; /* Display number */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Update the LCD when there is a change to the framebuffer */ + +static void lcdfb_update(FAR struct lcdfb_dev_s *priv, + FAR const struct nxgl_rect_s *rect); + +/* Get information about the video controller configuration and the + * configuration of each color plane. + */ + +static int lcdfb_getvideoinfo(FAR struct fb_vtable_s *vtable, + FAR struct fb_videoinfo_s *vinfo); +static int lcdfb_getplaneinfo(FAR struct fb_vtable_s *vtable, int planeno, + FAR struct fb_planeinfo_s *pinfo); + +/* The following is provided only if the video hardware supports RGB color + * mapping + */ + +#ifdef CONFIG_FB_CMAP +static int lcdfb_getcmap(FAR struct fb_vtable_s *vtable, + FAR struct fb_cmap_s *cmap); +static int lcdfb_putcmap(FAR struct fb_vtable_s *vtable, + FAR const struct fb_cmap_s *cmap); +#endif + +/* The following is provided only if the video hardware supports a hardware + * cursor + */ + +#ifdef CONFIG_FB_HWCURSOR +static int lcdfb_getcursor(FAR struct fb_vtable_s *vtable, + FAR struct fb_cursorattrib_s *attrib); +static int lcdfb_setcursor(FAR struct fb_vtable_s *vtable, + FAR struct fb_setcursor_s *settings); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* This is a singly linked list that supports look-up of framebuffer state + * using the display number. + */ + +static FAR struct lcdfb_dev_s *g_lcdfb; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lcdfb_find + * + * Description: + * Find the LCD framebuffer state associated with the display. + * + ****************************************************************************/ + +static FAR struct lcdfb_dev_s *lcdfb_find(int display) +{ + FAR struct lcdfb_dev_s *priv; + + /* Look up the LCD framebuffer state structure for this display. + * + * REVISIT: If many LCD framebuffers are used, then this lookup would be + * a performance issue. + * REVISIT: Semaphore protections is needed if there is concurrent access. + */ + + for (priv = g_lcdfb; priv != NULL; priv = priv->flink) + { + if (priv->display == display) + { + return priv; + } + } + + return NULL; +} + +/**************************************************************************** + * Name: lcdfb_update + * + * Description: + * Update the LCD when there is a change to the framebuffer. + * + ****************************************************************************/ + +static void lcdfb_update(FAR struct lcdfb_dev_s *priv, + FAR const struct nxgl_rect_s *rect) +{ + FAR struct lcd_planeinfo_s *pinfo = &priv->pinfo; + FAR uint8_t *run; + fb_coord_t row; + fb_coord_t startx; + fb_coord_t endx; + fb_coord_t width; + fb_coord_t starty; + fb_coord_t endy; + + /* Clip to fit in the framebuffer */ + + startx = rect->pt1.x; + if (startx < 0) + { + startx = 0; + } + + endx = rect->pt2.x; + if (endx >= priv->xres) + { + endx = priv->xres-1; + } + + width = endx - startx + 1; + + starty = rect->pt1.y; + if (starty < 0) + { + starty = 0; + } + + endy = rect->pt2.y; + if (endy >= priv->yres) + { + endy = priv->yres-1; + } + + /* Get the starting position in the framebuffer */ + + run = priv->fbmem + starty * priv->stride; + run += (startx * pinfo->bpp + 7) >> 3; + + for (row = starty; row <= endy; row++) + { + pinfo->putrun(row, startx, run, width); + run += priv->stride; + } +} + +/**************************************************************************** + * Name: lcdfb_getvideoinfo + ****************************************************************************/ + +static int lcdfb_getvideoinfo(FAR struct fb_vtable_s *vtable, + FAR struct fb_videoinfo_s *vinfo) +{ + FAR struct lcdfb_dev_s *priv; + FAR struct lcd_dev_s *lcd; + int ret = -EINVAL; + + lcdinfo("vtable=%p vinfo=%p\n", vtable, vinfo); + + DEBUGASSERT(vtable != NULL && vinfo != NULL); + priv = (FAR struct lcdfb_dev_s *)vtable; + + if (priv != NULL && vinfo != NULL) + { + /* Get the video info from the contained LCD */ + + lcd = priv->lcd; + DEBUGASSERT(lcd->getvideoinfo != NULL); + ret = lcd->getvideoinfo(lcd, vinfo); + if (ret < 0) + { + lcderr("ERROR: LCD getvideoinfo() failed: %d\n", ret); + } + } + + return ret; +} + +/**************************************************************************** + * Name: lcdfb_getplaneinfo + ****************************************************************************/ + +static int lcdfb_getplaneinfo(FAR struct fb_vtable_s *vtable, int planeno, + FAR struct fb_planeinfo_s *pinfo) +{ + FAR struct lcdfb_dev_s *priv; + int ret = -EINVAL; + + lcdinfo("vtable=%p planeno=%d pinfo=%p\n", vtable, planeno, pinfo); + + DEBUGASSERT(vtable != NULL && planeno == VIDEO_PLANE && pinfo != NULL); + priv = (FAR struct lcdfb_dev_s *)vtable; + + if (priv != NULL && planeno == VIDEO_PLANE && pinfo != NULL) + { + /* Return the plane info */ + + pinfo->fbmem = priv->fbmem; + pinfo->fblen = priv->fblen; + pinfo->stride = priv->stride; + pinfo->display = priv->display; + pinfo->bpp = priv->pinfo.bpp; + + ret = OK; + } + + return ret; +} + +/**************************************************************************** + * Name: lcdfb_getcmap + ****************************************************************************/ + +#ifdef CONFIG_FB_CMAP +static int lcdfb_getcmap(FAR struct fb_vtable_s *vtable, + FAR struct fb_cmap_s *cmap) +{ + FAR struct lcdfb_dev_s *priv; + FAR struct lcd_dev_s *lcd; + int ret = -EINVAL; + + lcdinfo("vtable=%p cmap=%p\n", vtable, cmap); + + DEBUGASSERT(vtable != NULL && cmap != NULL); + priv = (FAR struct lcdfb_dev_s *)vtable; + + if (priv != NULL && cmap != NULL) + { + /* Get the video info from the contained LCD */ + + lcd = priv->lcd + DEBUGASSERT(lcd->getcmap != NULL); + ret = lcd->getcmap(lcd, cmap); + if (ret < 0) + { + lcderr("ERROR: LCD getcmap() failed: %d\n", ret); + } + } + + return ret; +} +#endif + +/**************************************************************************** + * Name: lcdfb_putcmap + ****************************************************************************/ + +#ifdef CONFIG_FB_CMAP +static int lcdfb_putcmap(FAR struct fb_vtable_s *vtable, + FAR const struct fb_cmap_s *cmap) +{ + FAR struct lcdfb_dev_s *priv; + FAR struct lcd_dev_s *lcd; + int ret = -EINVAL; + + lcdinfo("vtable=%p cmap=%p\n", vtable, cmap); + + DEBUGASSERT(vtable != NULL && cmap != NULL); + priv = (FAR struct lcdfb_dev_s *)vtable; + + if (priv != NULL && cmap != NULL) + { + /* Get the video info from the contained LCD */ + + lcd = priv->lcd + DEBUGASSERT(lcd->putcmap != NULL); + ret = lcd->putcmap(lcd, cmap); + if (ret < 0) + { + lcderr("ERROR: LCD putcmap() failed: %d\n", ret); + } + } + + return ret; +} +#endif + +/**************************************************************************** + * Name: lcdfb_getcursor + ****************************************************************************/ + +#ifdef CONFIG_FB_HWCURSOR +static int lcdfb_getcursor(FAR struct fb_vtable_s *vtable, + FAR struct fb_cursorattrib_s *attrib) +{ + lcdinfo("vtable=%p attrib=%p\n", vtable, attrib); + FAR struct lcdfb_dev_s *priv; + FAR struct lcd_dev_s *lcd; + int ret = -EINVAL; + + lcdinfo("vtable=%p attrib=%p\n", vtable, attrib); + + DEBUGASSERT(vtable != NULL && attrib != NULL); + priv = (FAR struct lcdfb_dev_s *)vtable; + + if (priv != NULL && attrib != NULL) + { + /* Get the video info from the contained LCD */ + + lcd = priv->lcd + DEBUGASSERT(lcd->getcursor != NULL); + ret = lcd->getcursor(lcd, attrib); + if (ret < 0) + { + lcderr("ERROR: LCD getcursor() failed: %d\n", ret); + } + } + + return ret; +} +#endif + +/**************************************************************************** + * Name: lcdfb_setcursor + ****************************************************************************/ + +#ifdef CONFIG_FB_HWCURSOR +static int lcdfb_setcursor(FAR struct fb_vtable_s *vtable, + FAR struct fb_setcursor_s *settings) +{ + FAR struct lcdfb_dev_s *priv; + FAR struct lcd_dev_s *lcd; + int ret = -EINVAL; + + lcdinfo("vtable=%p settings=%p\n", vtable, settings); + + DEBUGASSERT(vtable != NULL && settings != NULL); + priv = (FAR struct lcdfb_dev_s *)vtable; + + if (priv != NULL && settings != NULL) + { + /* Get the video info from the contained LCD */ + + lcd = priv->lcd + DEBUGASSERT(lcd->setcursor != NULL); + ret = lcd->setcursor(lcd, settings); + if (ret < 0) + { + lcderr("ERROR: LCD setcursor() failed: %d\n", ret); + } + } + + return ret; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_fbinitialize + * + * Description: + * Initialize the framebuffer video hardware associated with the display. + * + * Input parameters: + * display - In the case of hardware with multiple displays, this + * specifies the display. Normally this is zero. + * + * Returned Value: + * Zero is returned on success; a negated errno value is returned on any + * failure. + * + ****************************************************************************/ + +int up_fbinitialize(int display) +{ + FAR struct lcdfb_dev_s *priv; + FAR struct lcd_dev_s *lcd; + struct fb_videoinfo_s vinfo; + struct nxgl_rect_s rect; + int ret; + + lcdinfo("display=%d\n", display); + DEBUGASSERT((unsigned)display < UINT8_MAX); + + /* Allocate the framebuffer state structure */ + + priv = (FAR struct lcdfb_dev_s *)kmm_zalloc(sizeof(struct lcdfb_dev_s)); + if (priv == NULL) + { + lcderr("ERROR: Failed to allocate state structure\n"); + return -ENOMEM; + } + + /* Initialize the LCD-independent fields of the state structure */ + + priv->display = display; + + priv->vtable.getvideoinfo = lcdfb_getvideoinfo, + priv->vtable.getplaneinfo = lcdfb_getplaneinfo, +#ifdef CONFIG_FB_CMAP + priv->vtable.getcmap = lcdfb_getcmap, + priv->vtable.putcmap = lcdfb_putcmap, +#endif +#ifdef CONFIG_FB_HWCURSOR + priv->vtable.getcursor = lcdfb_getcursor, + priv->vtable.setcursor = lcdfb_setcursor, +#endif + + /* Initialize the LCD device */ + + ret = board_lcd_initialize(); + if (ret < 0) + { + lcderr("ERROR: board_lcd_initialize() failed: %d\n", ret); + goto errout_with_state; + } + + /* Get the device instance */ + + lcd = board_lcd_getdev(display); + if (lcd == NULL) + { + lcderr("ERROR: board_lcd_getdev failed, devno=%d\n", display); + ret = -ENODEV; + goto errout_with_lcd; + } + + priv->lcd = lcd; + + /* Initialize the LCD-dependent fields of the state structure */ + + DEBUGASSERT(lcd->getvideoinfo != NULL); + ret = lcd->getvideoinfo(lcd, &vinfo); + if (ret < 0) + { + gerr("ERROR: getvideoinfo() failed: %d\n", ret); + goto errout_with_lcd; + } + + priv->xres = vinfo.xres; + priv->yres = vinfo.yres; + + DEBUGASSERT(lcd->getplaneinfo != NULL); + ret = lcd->getplaneinfo(lcd, VIDEO_PLANE, &priv->pinfo); + if (ret < 0) + { + gerr("ERROR: getplaneinfo() failed: %d\n", ret); + goto errout_with_lcd; + } + + /* Allocate (and clear) the framebuffer */ + + priv->stride = ((size_t)priv->xres * priv->pinfo.bpp + 7) >> 3; + priv->fblen = priv->stride * priv->yres; + + priv->fbmem = (FAR uint8_t *)kmm_zalloc(priv->fblen); + if (priv->fbmem == NULL) + { + gerr("ERROR: getplaneinfo() failed: %d\n", ret); + ret = -ENOMEM; + goto errout_with_lcd; + } + + /* Add the state structure to the list of framebuffer interfaces */ + + priv->flink = g_lcdfb; + g_lcdfb = priv; + + /* Write the entire framebuffer to the LCD */ + + rect.pt1.x = 0; + rect.pt1.y = 0; + rect.pt2.x = priv->xres - 1; + rect.pt2.y = priv->yres - 1; + + lcdfb_update(priv, &rect); + + /* Turn the LCD on at 75% power */ + + (void)priv->lcd->setpower(priv->lcd, ((3*CONFIG_LCD_MAXPOWER + 3)/4)); + return OK; + +errout_with_lcd: + board_lcd_uninitialize(); + +errout_with_state: + kmm_free(priv); + return ret; +} + +/**************************************************************************** + * Name: up_fbgetvplane + * + * Description: + * Return a a reference to the framebuffer object for the specified video + * plane of the specified plane. Many OSDs support multiple planes of video. + * + * Input parameters: + * display - In the case of hardware with multiple displays, this + * specifies the display. Normally this is zero. + * vplane - Identifies the plane being queried. + * + * Returned Value: + * A non-NULL pointer to the frame buffer access structure is returned on + * success; NULL is returned on any failure. + * + ****************************************************************************/ + +FAR struct fb_vtable_s *up_fbgetvplane(int display, int vplane) +{ + FAR struct lcdfb_dev_s *priv; + + lcdinfo("display=%d vplane=%d\n", display, vplane); + DEBUGASSERT(vplane == VIDEO_PLANE); + + /* Look up the LCD framebuffer state structure for this display. */ + + priv = lcdfb_find(display); + if (priv == NULL) + { + lcderr("ERROR: lcd_find(%d) failed\n", display); + return NULL; + } + + return &priv->vtable; +} + +/**************************************************************************** + * Name: up_fbuninitialize + * + * Description: + * Uninitialize the framebuffer support for the specified display. + * + * Input Parameters: + * display - In the case of hardware with multiple displays, this + * specifies the display. Normally this is zero. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void up_fbuninitialize(int display) +{ + FAR struct lcdfb_dev_s *priv; + FAR struct lcdfb_dev_s *prev; + + /* Find the LCD framebuffer state associated with this display. + * REVISIT: Semaphore protections is needed if there is concurrent access. + */ + + for (prev = NULL, priv = g_lcdfb; + priv != NULL; + prev = priv, priv = priv->flink) + { + if (priv->display == display) + { + /* Remove the state structure from the list */ + + if (prev != NULL) + { + prev->flink = priv->flink; + } + else + { + g_lcdfb = priv->flink; + } + + /* Uninitialize the LCD */ + + board_lcd_uninitialize(); + + /* Free the frame buffer allocation */ + + kmm_free(priv->fbmem); + + /* Free the state structure allocation */ + + kmm_free(priv); + break; + } + } +} + +/**************************************************************************** + * Name: nx_notify_rectangle + * + * Description: + * When CONFIG_NX_UPDATE=y, then the graphics system will callout to + * inform some external module that the display has been updated. This + * would be useful in a couple for cases. + * + * - When a serial LCD is used, but a framebuffer is used to access the + * LCD. In this case, the update callout can be used to refresh the + * affected region of the display. + * + * - When VNC is enabled. This is case, this callout is necessary to + * update the remote frame buffer to match the local framebuffer. + * + * When this feature is enabled, some external logic must provide this + * interface. This is the function that will handle the notification. It + * receives the rectangular region that was updated on the provided plane. + * + ****************************************************************************/ + +#ifdef CONFIG_NX_UPDATE +void nx_notify_rectangle(FAR NX_PLANEINFOTYPE *pinfo, + FAR const struct nxgl_rect_s *rect) +{ + FAR struct fb_planeinfo_s *fpinfo = (FAR struct fb_planeinfo_s *)pinfo; + FAR struct lcdfb_dev_s *priv; + + DEBUGASSERT(fpinfo != NULL && rect != NULL); + + /* Look up the LCD framebuffer state structure for this display. + * + * REVISIT: If many LCD framebuffers are used, then this lookup would be + * a performance issue. + */ + + priv = lcdfb_find(fpinfo->display); + if (priv != NULL) + { + lcdfb_update(priv, rect); + } +} +#endif + +#endif /* CONFIG_LCD_FRAMEBUFFER */ \ No newline at end of file