/**************************************************************************** * drivers/lcd/lcd_framebuffer.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #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 framebuffer */ 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_updateearea(FAR struct fb_vtable_s *vtable, FAR const struct fb_area_s *area); /* 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 static int lcdfb_setpower(FAR struct fb_vtable_s *vtable, FAR int power); /**************************************************************************** * 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_updateearea * * Description: * Update the LCD when there is a change to the framebuffer. * ****************************************************************************/ static int lcdfb_updateearea(FAR struct fb_vtable_s *vtable, FAR const struct fb_area_s *area) { FAR struct lcdfb_dev_s *priv = (FAR struct lcdfb_dev_s *)vtable; FAR struct lcd_planeinfo_s *pinfo = &priv->pinfo; FAR uint8_t *run = priv->fbmem; fb_coord_t row; fb_coord_t startx = 0; fb_coord_t endx = priv->xres - 1; fb_coord_t width = priv->xres; fb_coord_t starty = 0; fb_coord_t endy = priv->yres - 1; int ret; if (area != NULL) { /* Clip to fit in the framebuffer */ startx = area->x; if (startx < 0) { startx = 0; } endx = startx + area->w - 1; if (endx >= priv->xres) { endx = priv->xres - 1; } starty = area->y; if (starty < 0) { starty = 0; } endy = starty + area->h - 1; 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); } /* Get the starting position in the framebuffer */ run = priv->fbmem + starty * priv->stride; run += (startx * pinfo->bpp + 7) >> 3; } if (pinfo->putarea != NULL) { /* Each Driver's callback function putarea may be optimized by checking * if it is a full screen/full row mode or not. * In case of full screen/row mode the memory layout of drivers memory * and data provided to putarea function may be (or not, it depends of * display and driver implementation) identical. * Identical memory layout let us to use: * - memcopy (if there is shadow buffer in driver implementation) * - apply DMA channel to transfer data to driver memory. */ ret = pinfo->putarea(pinfo->dev, starty, endy, startx, endx, run, priv->stride); if (ret < 0) { lcderr("Failed to update area"); return ret; } } else { width = endx - startx + 1; for (row = starty; row <= endy; row++) { ret = pinfo->putrun(pinfo->dev, row, startx, run, width); if (ret < 0) { lcderr("Failed to update row"); return ret; } run += priv->stride; } } if (pinfo->redraw != NULL) { pinfo->redraw(pinfo->dev); } 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 color map 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) { /* Set the color map to 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 cursor 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) { /* Set the cursor info to 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 /**************************************************************************** * Name: lcdfb_setpower ****************************************************************************/ static int lcdfb_setpower(FAR struct fb_vtable_s *vtable, FAR int power) { int ret = -EINVAL; FAR struct lcdfb_dev_s *priv; FAR struct lcd_dev_s *lcd; DEBUGASSERT(vtable != NULL); priv = (FAR struct lcdfb_dev_s *)vtable; if (priv != NULL) { lcd = priv->lcd; DEBUGASSERT(lcd->setpower != NULL); ret = lcd->setpower(lcd, power); if (ret < 0) { lcderr("ERROR: LCD setpower() failed: %d\n", ret); } } return ret; } /**************************************************************************** * 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 fb_area_s area; 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 priv->vtable.updatearea = lcdfb_updateearea, priv->vtable.setpower = lcdfb_setpower, #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); ret = -ENODEV; goto errout_with_state; } #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 */ area.x = 0; area.y = 0; area.w = priv->xres; area.h = priv->yres; ret = lcdfb_updateearea(&priv->vtable, &area); if (ret < 0) { lcderr("FB update failed: %d\n", ret); } /* Turn the LCD on at 75% power */ priv->lcd->setpower(priv->lcd, ((3*CONFIG_LCD_MAXPOWER + 3) / 4)); return OK; errout_with_lcd: #ifndef CONFIG_LCD_EXTERNINIT board_lcd_uninitialize(); #endif 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; } #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; } } } #endif /* CONFIG_LCD_FRAMEBUFFER */