123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754 |
- /****************************************************************************
- * drivers/lcd/lcd_frambuffer.c
- *
- * Copyright (C) 2017=-2018 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>
- #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 int 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 int 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;
- int ret;
- /* 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;
- }
- starty = rect->pt1.y;
- if (starty < 0)
- {
- starty = 0;
- }
- endy = rect->pt2.y;
- if (endy >= priv->yres)
- {
- endy = priv->yres-1;
- }
- /* If the display uses a value of BPP < 8, then we may have to extend the
- * rectangle on the left so that it is byte aligned. Works for BPP={1,2,4}
- */
- if (pinfo->bpp < 8)
- {
- unsigned int pixperbyte = 8 / pinfo->bpp;
- startx &= ~(pixperbyte - 1);
- }
- width = endx - startx + 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++)
- {
- /* REVISIT: Some LCD hardware certain aligment requirements on DMA
- * memory.
- */
- ret = pinfo->putrun(row, startx, run, width);
- if (ret < 0)
- {
- return ret;
- }
- run += priv->stride;
- }
- return OK;
- }
- /****************************************************************************
- * 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
- #ifdef CONFIG_LCD_EXTERNINIT
- /* Use external graphics driver initialization */
- lcd = board_graphics_setup(display);
- if (lcd == NULL)
- {
- gerr("ERROR: board_graphics_setup failed, devno=%d\n", display);
- return EXIT_FAILURE;
- }
- #else
- /* 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;
- }
- #endif
- priv->lcd = lcd;
- /* Initialize the LCD-dependent fields of the state structure */
- DEBUGASSERT(lcd->getvideoinfo != NULL);
- ret = lcd->getvideoinfo(lcd, &vinfo);
- if (ret < 0)
- {
- lcderr("ERROR: LCD 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)
- {
- lcderr("ERROR: LCD 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)
- {
- lcderr("ERROR: Failed to allocate frame buffer memory\n");
- 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;
- ret = lcdfb_update(priv, &rect);
- if (ret < 0)
- {
- lcderr("FB update failed: %d\n", ret);
- }
- /* Turn the LCD on at 75% power */
- (void)priv->lcd->setpower(priv->lcd, ((3*CONFIG_LCD_MAXPOWER + 3)/4));
- return OK;
- errout_with_lcd:
- #ifndef CONFIG_LCD_EXTERNINIT
- board_lcd_uninitialize();
- errout_with_state:
- #endif
- 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;
- }
- #ifndef CONFIG_LCD_EXTERNINIT
- /* Uninitialize the LCD */
- board_lcd_uninitialize();
- #endif
- /* 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_LCD_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.
- *
- * NOTE: This function is also required for use with the LCD framebuffer
- * driver front end when CONFIG_LCD_UPDATE=y, although that use does not
- * depend on CONFIG_NX (and this function seems misnamed in that case).
- *
- ****************************************************************************/
- #if defined(CONFIG_LCD_UPDATE) || defined(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;
- int ret;
- 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)
- {
- ret = lcdfb_update(priv, rect);
- if (ret < 0)
- {
- lcderr("FB update failed: %d\n", ret);
- }
- }
- }
- #endif
- #endif /* CONFIG_LCD_FRAMEBUFFER */
|