drivers/lcd: Add suppose for a generic front-end that will convert any LCD driver into a framebuffer driver.

This commit is contained in:
Gregory Nutt 2017-09-17 10:39:23 -06:00
parent b90b4d40b6
commit a9c054237a
3 changed files with 737 additions and 3 deletions

View File

@ -21,6 +21,23 @@ if LCD
comment "Common Graphic LCD Settings" 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 config LCD_CONSOLE
bool "LCD console output" bool "LCD console output"
default n default n
@ -118,6 +135,11 @@ config P14201_FRAMEBUFFER
All pixel writes must be aligned to byte boundaries. All pixel writes must be aligned to byte boundaries.
The latter limitation effectively reduces the 128x96 disply to 64x96. 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 endif
config LCD_NOKIA6100 config LCD_NOKIA6100
@ -1083,6 +1105,7 @@ config LCD_RA8875_EXTENDED
driver anyway. driver anyway.
endif # LCD_RA8875 endif # LCD_RA8875
endmenu # LCD Driver selection
endif # LCD endif # LCD
menuconfig SLCD menuconfig SLCD

View File

@ -1,7 +1,7 @@
############################################################################ ############################################################################
# drivers/lcd/Make.defs # 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 <gnutt@nuttx.org> # Author: Gregory Nutt <gnutt@nuttx.org>
# #
# Redistribution and use in source and binary forms, with or without # 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 # 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) # 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 DEPPATH += --dep-path lcd
VPATH += :lcd VPATH += :lcd
CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" $(TOPDIR)$(DELIM)drivers$(DELIM)lcd} CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" $(TOPDIR)$(DELIM)drivers$(DELIM)lcd}

View File

@ -0,0 +1,705 @@
/****************************************************************************
* drivers/lcd/lcd_frambuffer.c
*
* Copyright (C) 2017 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* 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 <nuttx/config.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/board.h>
#include <nuttx/kmalloc.h>
#include <nuttx/nx/nx.h>
#include <nuttx/nx/nxglib.h>
#include <nuttx/lcd/lcd.h>
#include <nuttx/video/fb.h>
#include <arch/board/board.h>
#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 */