nuttx-apps/examples/ltdc/dma2d.c
2016-06-16 12:12:34 -06:00

2470 lines
68 KiB
C

/****************************************************************************
* examples/ltdc/dma2d.c
*
* Copyright (C) 2008, 2011-2012, 2015 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
* Marco Krahl <ocram.lhark@gmail.com>
*
* 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 "ltdc.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Data
****************************************************************************/
/****************************************************************************
* Private Function
****************************************************************************/
/****************************************************************************
* Name: ltdc_remove_dma2d_surface
*
* Description:
* Remove the surface of the dma2dlayer
*
****************************************************************************/
static void ltdc_remove_dma2d_surface(FAR struct dma2d_surface *sur)
{
if (sur)
{
up_dma2dremovelayer(sur->dma2d);
free(sur);
}
}
/****************************************************************************
* Name: ltdc_create_dma2d_surface
*
* Description:
* Create a surface for the dma2dlayer
*
****************************************************************************/
static FAR struct dma2d_surface *ltdc_create_dma2d_surface(uint16_t xres,
uint16_t yres,
uint8_t fmt)
{
int ret;
FAR struct dma2d_surface *sur = (FAR struct dma2d_surface *)
malloc(sizeof(struct dma2d_surface));
if (sur)
{
sur->dma2d = up_dma2dcreatelayer(xres, yres, fmt);
if (!sur->dma2d)
{
_err("ERROR: up_dma2dcreatelayer failed\n");
free(sur);
sur = NULL;
}
else
{
ret = sur->dma2d->getvideoinfo(sur->dma2d, &sur->vinfo);
if (ret != OK)
{
_err("ERROR: getvideoinfo() failed\n");
}
else
{
ret = sur->dma2d->getplaneinfo(sur->dma2d, 0, &sur->pinfo);
if (ret != OK)
{
_err("ERROR: getplaneinfo() failed\n");
}
}
if(ret != OK)
{
ltdc_remove_dma2d_surface(sur);
sur = NULL;
}
else
{
int lid;
sur->dma2d->getlid(sur->dma2d, &lid);
_info("dma2d layer %d is created with: "
"layer = %p, xres = %d, yres = %d, fb start address = %p, "
"fb size = %d, fmt = %d, bpp = %d\n",
lid, sur->dma2d, sur->vinfo.xres, sur->vinfo.yres,
sur->pinfo.fbmem, sur->pinfo.fblen, sur->vinfo.fmt,
sur->pinfo.bpp);
}
}
}
return sur;
}
/****************************************************************************
* Name: ltdc_clearlayer
*
* Description:
* Clear the whole ltdc layer with a specific color
*
****************************************************************************/
static void ltdc_clearlayer(FAR struct surface *sur, uint8_t color)
{
uint32_t argb;
struct ltdc_area_s area;
argb = ltdc_color(&sur->vinfo, color);
area.xpos = 0;
area.ypos = 0;
area.xres = sur->vinfo.xres;
area.yres = sur->vinfo.yres;
sur->layer->fillarea(sur->layer, &area, argb);
}
/****************************************************************************
* Name: dma2d_clearlayer
*
* Description:
* Clear the whole dma2d layer with a specific color
*
****************************************************************************/
static void dma2d_clearlayer(FAR struct dma2d_surface *sur, uint8_t color)
{
uint32_t argb;
struct ltdc_area_s area;
argb = ltdc_color(&sur->vinfo, color);
area.xpos = 0;
area.ypos = 0;
area.xres = sur->vinfo.xres;
area.yres = sur->vinfo.yres;
sur->dma2d->fillarea(sur->dma2d, &area, argb);
}
/****************************************************************************
* Name: ltdc_blendshadow
*
* Description:
* Helper: Blend a rectangle with an existing layer.
* This also blends a subjacent mirror image of the rectangle position
* dependent.
*
* Note! layer back and fore must have the same size.
* layer dest must be larger or equal to the size of the layer back and
* fore.
*
****************************************************************************/
static void ltdc_blendshadow(FAR struct surface *dest,
FAR struct dma2d_surface *fore,
FAR struct dma2d_surface *back,
fb_coord_t xpos, fb_coord_t ypos,
fb_coord_t shadowlen,
uint8_t alpha)
{
FAR struct ltdc_area_s area;
int32_t shadowx;
int32_t shadowy;
area.xpos = xpos;
area.ypos = ypos;
area.xres = back->vinfo.xres;
area.yres = back->vinfo.yres;
/* Calculate the position of the mirror image */
shadowx = ((xpos - (dest->vinfo.xres - back->vinfo.xres) / 2) * shadowlen * 2) /
((dest->vinfo.xres - back->vinfo.xres) / 2);
if (xpos + shadowx < 0)
{
shadowx = -xpos;
}
else if (xpos + back->vinfo.xres + shadowx > dest->vinfo.xres)
{
shadowx = dest->vinfo.xres - (xpos + back->vinfo.xres);
}
shadowy = ((ypos - (dest->vinfo.yres - back->vinfo.yres) / 2) * shadowlen * 2) /
((dest->vinfo.yres - back->vinfo.yres) / 2);
if (ypos + shadowy < 0)
{
shadowy = -ypos;
}
else if (ypos + back->vinfo.yres + shadowy > dest->vinfo.yres)
{
shadowy = dest->vinfo.yres - (ypos + back->vinfo.yres);
}
/* We do not really need the scratch layer, but we want to test blit and blend
* operation with 3 dma2d layer
*/
fore->dma2d->blit(fore->dma2d, 0, 0, dest->dma2d, &area);
area.xpos = 0;
area.ypos = 0;
/* Blend the shadow surface semitransparency */
back->dma2d->setalpha(back->dma2d, 86 * alpha / 255);
fore->dma2d->setalpha(fore->dma2d, 169 * alpha / 255);
dest->layer->blend(dest->layer, xpos + shadowx, ypos + shadowy, fore->dma2d,
0, 0, back->dma2d, &area);
/* Blit to the origin surface */
back->dma2d->setalpha(back->dma2d, alpha);
fore->dma2d->setalpha(fore->dma2d, 255 - alpha);
dest->layer->blend(dest->layer, xpos, ypos, fore->dma2d,
0, 0, back->dma2d, &area);
}
/****************************************************************************
* Name: ltdc_blendrect
*
* Description:
* Helper: Blend a rectangle to the a specific pixel position.
* Note! This is only useful for the blitflipositioning test.
*
****************************************************************************/
static void ltdc_blendrect(FAR struct dma2d_surface *fore,
FAR struct dma2d_surface *back,
fb_coord_t xpos, fb_coord_t ypos,
fb_coord_t shadowlen)
{
FAR struct surface *sur = ltdc_get_surface(LTDC_LAYER_INACTIVE);
/* Clear the invisible flip layer */
ltdc_clearlayer(sur, LTDC_BLACK);
/* Blend the rectangle */
ltdc_blendshadow(sur, fore, back, xpos, ypos, 16, 255);
/* Flip the layer to make the changes visible */
sur->layer->update(sur->layer, LTDC_UPDATE_FLIP|
LTDC_SYNC_VBLANK|
LTDC_SYNC_WAIT);
usleep(10);
}
/****************************************************************************
* Name: ltdc_blendoutline
*
* Description:
* Draw the outline of a rectangle.
* Note! This is done by performing sequential blend operations.
* It does not claim to have a good speed performance.
*
****************************************************************************/
static void ltdc_blendoutline(FAR struct dma2d_surface *fore,
FAR struct dma2d_surface *back)
{
int n;
struct ltdc_area_s area;
/* Use the inactive layer as second scratch layer */
FAR struct surface *inactive = ltdc_get_surface(LTDC_LAYER_INACTIVE);
/* Draw the outline */
for (n = 0; n < 5 ; n++)
{
area.xpos = n;
area.ypos = n;
area.yres = fore->vinfo.yres - n * 2;
area.xres = fore->vinfo.xres - n * 2;
if (n == 0 || n == 2)
{
dma2d_clearlayer(fore, LTDC_BLACK);
back->dma2d->setalpha(back->dma2d, 1);
back->dma2d->setalpha(fore->dma2d, 2);
}
else if (n == 1)
{
dma2d_clearlayer(fore, LTDC_WHITE);
back->dma2d->setalpha(back->dma2d, 2);
back->dma2d->setalpha(fore->dma2d, 1);
}
else if (n == 3)
{
dma2d_clearlayer(fore, LTDC_BLACK);
back->dma2d->setalpha(back->dma2d, 3);
back->dma2d->setalpha(fore->dma2d, 1);
}
else
{
dma2d_clearlayer(fore, LTDC_BLACK);
back->dma2d->setalpha(back->dma2d, 3);
back->dma2d->setalpha(fore->dma2d, 0);
}
inactive->dma2d->blend(inactive->dma2d, n, n, back->dma2d,
n, n, fore->dma2d, &area);
}
/* Copy the result back to the background layer */
area.xpos = 0;
area.ypos = 0;
area.yres = back->vinfo.yres;
area.xres = back->vinfo.xres;
back->dma2d->blit(back->dma2d, 0, 0, inactive->dma2d, &area);
}
/****************************************************************************
* Name: ltdc_calcpixel
*
* Description:
* Calculates the next pixel position.
* This is based on the Breseham algorithmus.
*
****************************************************************************/
static void ltdc_calcpos(int32_t *x0, int32_t *y0, int32_t x1, int32_t y1)
{
if (*x0 != x1 && *y0 != y1)
{
int32_t sx = *x0<x1 ? 1 : -1;
int32_t sy = *y0<y1 ? 1 : -1;
int32_t dx = x1 - *x0 < 0 ? *x0 - x1 : x1 - *x0;
int32_t dy = y1 - *y0 < 0 ? y1 - *y0 : *y0 - y1;
int32_t e = 2 * (dx + dy);
if (e > dy)
{
*x0 += sx;
}
if (e < dx)
{
*y0 += sy;
}
}
}
/****************************************************************************
* Name: ltdc_dma2d_interface
*
* Description:
* Test: Error handling of dma2d interface
*
****************************************************************************/
static void ltdc_dma2d_interface(void)
{
int ret;
uint8_t alpha = 0;
uint32_t blendmode = 0;
#ifdef CONFIG_STM32_LTDC_L2
FAR struct surface *active = ltdc_get_surface(LTDC_LAYER_INACTIVE);
#else
FAR struct surface *active = ltdc_get_surface(LTDC_LAYER_ACTIVE);
#endif
FAR struct dma2d_layer_s *dma2d = active->dma2d;
_info("Perform simple dma2d interface test\n");
/* test setalpha */
ret = dma2d->setalpha(dma2d, 127);
if (ret != OK)
{
_err("ERROR: setalpha() failed\n");
_exit(1);
}
/* test getalpha */
ret = dma2d->getalpha(dma2d, &alpha);
if (ret != OK || alpha != 127)
{
_err("ERROR: getalpha() failed\n");
_exit(1);
}
/* test setblendmode */
ret = dma2d->setblendmode(dma2d, DMA2D_BLEND_ALPHA);
if (ret != OK)
{
_err("ERROR: setblendmode() failed\n");
_exit(1);
}
/* test getblendmode */
ret = dma2d->getblendmode(dma2d, &blendmode);
if (ret != OK || blendmode != DMA2D_BLEND_ALPHA)
{
_err("ERROR: getblendmode() failed\n");
_exit(1);
}
#ifdef CONFIG_STM32_DMA2D_L8
/* test setclut */
if (active->vinfo.fmt == FB_FMT_RGB8)
{
int i;
FAR struct fb_cmap_s *cmapltdc = ltdc_createcmap(LTDC_EXAMPLE_NCOLORS);
FAR struct fb_cmap_s *cmap = ltdc_createcmap(256);
if (!cmap || !cmapltdc)
{
ltdc_deletecmap(cmap);
ltdc_deletecmap(cmapltdc);
_exit(1);
}
/* store the clut table of the ltdc layer */
ret = active->layer->getclut(active->layer, cmapltdc);
if (ret != OK)
{
_err("ERROR: ltdc getclut() failed\n");
_exit(1);
}
for (i = 0; i < LTDC_EXAMPLE_NCOLORS; i++)
{
#ifdef CONFIG_FB_TRANSPARENCY
_info("ltdc color %d, %02x:%02x:%02x:%02x\n", i,
cmapltdc->transp[i],
cmapltdc->red[i],
cmapltdc->green[i],
cmapltdc->blue[i]);
#else
_info("ltdc color %d, %02x:%02x:%02x\n", i,
cmapltdc->red[i],
cmapltdc->green[i],
cmapltdc->blue[i]);
#endif
}
ret = dma2d->getclut(dma2d, cmap);
if (ret != OK)
{
_err("ERROR: getclut() failed\n");
_exit(1);
}
memset(cmap->red, 0, 256);
memset(cmap->green, 0, 256);
memset(cmap->blue, 0, 256);
#ifdef CONFIG_FB_TRANSPARENCY
memset(cmap->transp, 0, 256);
#endif
_info("set color lookup table to black\n");
ret = dma2d->setclut(dma2d, cmap);
if (ret != OK)
{
_err("ERROR: setclut() failed\n");
_exit(1);
}
ret = dma2d->getclut(dma2d, cmap);
if (ret != OK)
{
_err("ERROR: getclut() failed\n");
_exit(1);
}
/* Check if the clut is black */
#ifdef CONFIG_FB_TRANSPARENCY
if(memcmp(cmap->red, cmap->blue, 256) ||
memcmp(cmap->red, cmap->green, 256) ||
memcmp(cmap->transp, cmap->blue, 256))
#else
if(memcmp(cmap->red, cmap->blue, 256) ||
memcmp(cmap->red, cmap->green, 256))
#endif
{
_err("ERROR: unexpected clut content\n");
_exit(1);
}
/* Check if the ltdc clut is set by the dma2d interface */
ret = active->layer->getclut(active->layer, cmap);
if (ret != OK)
{
_err("ERROR: ltdc getclut() failed\n");
_exit(1);
}
/* Check if the ltdc clut is black */
#ifdef CONFIG_FB_TRANSPARENCY
if(memcmp(cmap->red, cmap->blue, 256) ||
memcmp(cmap->red, cmap->green, 256) ||
memcmp(cmap->transp, cmap->blue, 256))
#else
if(memcmp(cmap->red, cmap->blue, 256) ||
memcmp(cmap->red, cmap->green, 256))
#endif
{
_err("ERROR: unexpected clut content\n");
_exit(1);
}
/* Check if the clut is set by the ltdc interface */
/* Restore the clut table of the ltdc layer */
ret = active->layer->setclut(active->layer, cmapltdc);
if (ret != OK)
{
_err("ERROR: ltdc setclut() failed\n");
_exit(1);
}
/* Compare with the related clut table of the dma2d layer */
ret = dma2d->getclut(dma2d, cmap);
if (ret != OK)
{
_err("ERROR: getclut() failed\n");
_exit(1);
}
#ifdef CONFIG_FB_TRANSPARENCY
if(memcmp(cmap->transp, cmapltdc->transp, LTDC_EXAMPLE_NCOLORS) ||
memcmp(cmap->red, cmapltdc->red, LTDC_EXAMPLE_NCOLORS) ||
memcmp(cmap->green, cmapltdc->green, LTDC_EXAMPLE_NCOLORS) ||
memcmp(cmap->blue, cmapltdc->blue, LTDC_EXAMPLE_NCOLORS))
#else
if(memcmp(cmap->red, cmapltdc->red, LTDC_EXAMPLE_NCOLORS) ||
memcmp(cmap->green, cmapltdc->green, LTDC_EXAMPLE_NCOLORS) ||
memcmp(cmap->blue, cmapltdc->blue, LTDC_EXAMPLE_NCOLORS))
#endif
{
_err("ERROR: clut of ltdc layer and related dma2d layer are different\n");
_exit(1);
}
else
{
_info("ok, changing the clut by the ltdc layer also changed the clut of "
"the dma2d layer as expected.\n");
}
/* Check expected setclut error */
cmap->first = 256;
ret = dma2d->setclut(dma2d, cmap);
if (ret == OK)
{
_err("ERROR: setclut() failed, expected error if first color exceeds 256\n");
}
else
{
_info("setclut() Ok, unsupported cmap detected\n");
}
/* Check expected getclut error */
ret = dma2d->getclut(dma2d, cmap);
if (ret == OK)
{
_err("ERROR: getclut() failed, expected error if first color exceeds 256\n");
}
else
{
_info("getclut() Ok, unsupported cmap detected\n");
}
cmap->first = 0;
/* Restore the clut table of the ltdc layer if something goes wrong */
ret = active->layer->setclut(active->layer, cmapltdc);
if (ret != OK)
{
_err("ERROR: ltdc setclut() failed\n");
_exit(1);
}
ltdc_deletecmap(cmap);
ltdc_deletecmap(cmapltdc);
}
#endif
}
/****************************************************************************
* Name: ltdc_dma2d_fillarea
*
* Description:
* Test: Drawing color to specific area.
*
****************************************************************************/
static void ltdc_dma2d_fillarea(void)
{
int ret = !OK;
FAR struct ltdc_area_s area;
FAR struct surface *active = ltdc_get_surface(LTDC_LAYER_ACTIVE);
#ifdef CONFIG_STM32_DMA2D_L8
if (active->vinfo.fmt == FB_FMT_RGB8)
{
_warn("WARNING: skipped, output to layer with CLUT pixel format not supported\n");
return;
}
#endif
/* Wrong positioning detection */
area.xpos = 1;
area.ypos = 0;
area.xres = active->vinfo.xres;
area.yres = active->vinfo.yres;
_info("check if the ltdc driver recognized when positioning overflows the whole"
" layer buffer\n");
if (active->layer->fillarea(active->layer, &area, 0) != OK)
{
ret = OK;
}
area.xpos = 0;
area.ypos = 1;
if (active->layer->fillarea(active->layer, &area, 0) != OK)
{
ret = OK == OK ? OK : ret;
}
if (ret == OK)
{
_info("ok, driver detects wrong positioning\n");
}
else
{
_err("ERROR: fail, wrong positioning can overflow layer buffer\n");
}
_info("check if the dma2d driver recognized when positioning overflows the"
" whole layer buffer\n");
if (active->dma2d->fillarea(active->dma2d, &area, 0) != OK)
{
ret = OK == OK ? OK : ret;
}
area.xpos = 0;
area.ypos = 1;
if (active->dma2d->fillarea(active->dma2d, &area, 0) != OK)
{
ret = OK == OK ? OK : ret;
}
if (ret == OK)
{
_info("ok, driver detects wrong positioning\n");
}
else
{
_err("ERROR: fail, wrong positioning can overflow layer buffer\n");
}
/* Flip with non blend */
_info("Perform fillarea test\n");
_info("Ensure that the active layer is opaque\n");
active->layer->setalpha(active->layer, 0xff);
_info("Disable blend mode for the active layer\n");
active->layer->setblendmode(active->layer, LTDC_BLEND_NONE);
_info("Set the active layer to fullscreen black\n");
ltdc_drawcolor(&active->vinfo, active->pinfo.fbmem,
active->vinfo.xres, active->vinfo.yres,
ltdc_color(&active->vinfo, LTDC_BLACK));
_info("Flip the top layer to the active visible layer\n");
active->layer->update(active->layer, LTDC_UPDATE_ACTIVATE|LTDC_SYNC_VBLANK);
_info("Draw a red rectangle in top left quarter of the screen\n");
area.xpos = 0;
area.ypos = 0;
area.xres = active->vinfo.xres/2;
area.yres = active->vinfo.yres/2;
active->layer->fillarea(active->layer, &area,
ltdc_color(&active->vinfo, LTDC_RED));
usleep(1000000);
_info("Set the active layer to fullscreen black\n");
ltdc_drawcolor(&active->vinfo, active->pinfo.fbmem,
active->vinfo.xres, active->vinfo.yres,
ltdc_color(&active->vinfo, LTDC_BLACK));
_info("Draw a green rectangle in top right quarter of the screen\n");
area.xpos = active->vinfo.xres/2;
area.ypos = 0;
active->layer->fillarea(active->layer, &area,
ltdc_color(&active->vinfo, LTDC_GREEN));
usleep(1000000);
_info("Set the active layer to fullscreen black\n");
ltdc_drawcolor(&active->vinfo, active->pinfo.fbmem,
active->vinfo.xres, active->vinfo.yres,
ltdc_color(&active->vinfo, LTDC_BLACK));
_info("Draw a white rectangle in bottom left quarter of the screen\n");
area.xpos = 0;
area.ypos = active->vinfo.yres/2;
active->layer->fillarea(active->layer, &area,
ltdc_color(&active->vinfo, LTDC_WHITE));
_info("Draw a blue rectangle in bottom right quarter of the screen\n");
usleep(1000000);
_info("Set the active layer to fullscreen black\n");
ltdc_drawcolor(&active->vinfo, active->pinfo.fbmem,
active->vinfo.xres, active->vinfo.yres,
ltdc_color(&active->vinfo, LTDC_BLACK));
area.xpos = active->vinfo.xres/2;;
area.ypos = active->vinfo.yres/2;
active->layer->fillarea(active->layer, &area,
ltdc_color(&active->vinfo, LTDC_BLUE));
usleep(1000000);
_info("Set the active layer to fullscreen black\n");
ltdc_drawcolor(&active->vinfo, active->pinfo.fbmem,
active->vinfo.xres, active->vinfo.yres,
ltdc_color(&active->vinfo, LTDC_BLACK));
}
#ifdef CONFIG_STM32_LTDC_L2
/****************************************************************************
* Name: ltdc_dma2d_blitsimple
*
* Description:
* Test: Perform simple blit operation to check source area positioning
*
****************************************************************************/
static void ltdc_dma2d_blitsimple(void)
{
FAR struct ltdc_area_s area;
FAR struct surface *active = ltdc_get_surface(LTDC_LAYER_ACTIVE);
FAR struct surface *inactive = ltdc_get_surface(LTDC_LAYER_INACTIVE);
#ifdef CONFIG_STM32_DMA2D_L8
if (active->vinfo.fmt == FB_FMT_RGB8)
{
_warn("WARNING: skipped, output to layer with CLUT pixel format not supported\n");
return;
}
#endif
/* Flip with non blend */
_info("Perform simple blit operation\n");
_info("active->pinfo.fbmem = %p\n", active->pinfo.fbmem);
_info("inactive->pinfo.fbmem = %p\n", inactive->pinfo.fbmem);
_info("Ensure that both ltdc layer are opaque\n");
active->layer->setalpha(active->layer, 0xff);
inactive->layer->setalpha(inactive->layer, 0xff);
_info("Disable blend mode for ltdc both layer\n");
active->layer->setblendmode(active->layer, LTDC_BLEND_NONE);
inactive->layer->setblendmode(inactive->layer, LTDC_BLEND_NONE);
/* Fullscreen blit */
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
_info("Flip the top layer to the active visible layer\n");
inactive->layer->update(active->layer,
LTDC_UPDATE_ACTIVATE|LTDC_SYNC_VBLANK);
ltdc_simple_draw(&inactive->vinfo, &inactive->pinfo);
_info("Blit the whole bottom layer to the top layer\n");
area.xpos = 0;
area.ypos = 0;
area.xres = inactive->vinfo.xres;
area.yres = inactive->vinfo.yres;
active->layer->blit(active->layer, 0, 0, inactive->dma2d, &area);
usleep(1000000);
/* Top left to the middle blit */
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
area.xpos = 0;
area.ypos = 0;
area.xres = inactive->vinfo.xres/2;
area.yres = inactive->vinfo.yres/2;
_info("Blit the top left red rectangle from the bottom layer with the"
" top layer\n");
active->layer->blit(active->layer, active->vinfo.xres/4,
active->vinfo.yres/4, inactive->dma2d, &area);
usleep(1000000);
/* Top right to the middle blit */
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
area.xpos = inactive->vinfo.xres/2;
area.ypos = 0;
area.xres = inactive->vinfo.xres/2;
area.yres = inactive->vinfo.yres/2;
_info("Blit the top right green rectangle from the bottom layer with the"
" top layer\n");
active->layer->blit(active->layer, active->vinfo.xres/4,
active->vinfo.yres/4, inactive->dma2d, &area);
usleep(1000000);
/* Bottom left to the middle blit */
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
area.xpos = 0;
area.ypos = inactive->vinfo.yres/2;
area.xres = inactive->vinfo.xres/2;
area.yres = inactive->vinfo.yres/2;
_info("Blit the bottom left white rectangle from the bottom layer with the"
" top layer\n");
active->layer->blit(active->layer, active->vinfo.xres/4,
active->vinfo.yres/4, inactive->dma2d, &area);
usleep(1000000);
/* Bottom right to the middle blit */
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
area.xpos = inactive->vinfo.xres/2;
area.ypos = inactive->vinfo.yres/2;
area.xres = inactive->vinfo.xres/2;
area.yres = inactive->vinfo.yres/2;
_info("Blit the bottom right blue rectangle from the bottom layer with the"
" top layer\n");
active->layer->blit(active->layer, active->vinfo.xres/4,
active->vinfo.yres/4, inactive->dma2d, &area);
usleep(1000000);
}
/****************************************************************************
* Name: ltdc_dma2d_blitpositioning
*
* Description:
* Test: Perform simple blit operation to check source and destination
* positioning
*
****************************************************************************/
static void ltdc_dma2d_blitpositioning(void)
{
uint32_t x, y;
FAR struct ltdc_area_s area;
FAR struct surface *active = ltdc_get_surface(LTDC_LAYER_ACTIVE);
FAR struct surface *inactive = ltdc_get_surface(LTDC_LAYER_INACTIVE);
#ifdef CONFIG_STM32_DMA2D_L8
if (active->vinfo.fmt == FB_FMT_RGB8)
{
_warn("WARNING: skipped, output to layer with CLUT pixel format not supported\n");
return;
}
#endif
/* Set layer in the middle of the screen */
_info("active->pinfo.fbmem = %p\n", active->pinfo.fbmem);
_info("inactive->pinfo.fbmem = %p\n", inactive->pinfo.fbmem);
_info("Ensure that both ltdc layer opaque\n");
active->layer->setalpha(active->layer, 0xff);
inactive->layer->setalpha(inactive->layer, 0xff);
_info("Disable blend mode for both ltdc layer\n");
active->layer->setblendmode(active->layer, LTDC_BLEND_NONE);
inactive->layer->setblendmode(inactive->layer, LTDC_BLEND_NONE);
/* Fullscreen blit */
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
_info("Flip the top layer to the active visible layer\n");
inactive->layer->update(active->layer,
LTDC_UPDATE_ACTIVATE|LTDC_SYNC_VBLANK);
/* Create colored background buffer */
ltdc_simple_draw(&inactive->vinfo, &inactive->pinfo);
_info("Perform positioning test\n");
area.xres = inactive->vinfo.xres/2;
area.yres = inactive->vinfo.yres/2;
area.xpos = inactive->vinfo.xres/4;
area.ypos = inactive->vinfo.yres/4;
x = active->vinfo.xres/4;
y = active->vinfo.yres/4;
/* Move right */
for (; x < active->vinfo.xres/4 + active->vinfo.xres/8; x++)
{
/* Cleanup current screen */
ltdc_clearlayer(active, LTDC_BLACK);
area.xpos = x;
active->layer->blit(active->layer, x, y, inactive->dma2d, &area);
usleep(5);
}
/* Move down */
for (; y < active->vinfo.yres/4 + active->vinfo.yres/8; y++)
{
/* Cleanup current screen */
ltdc_clearlayer(active, LTDC_BLACK);
area.ypos = y;
active->layer->blit(active->layer, x, y, inactive->dma2d, &area);
usleep(5);
}
/* Move left */
for (; x >= active->vinfo.xres/4 - active->vinfo.xres/8; x--)
{
/* Cleanup current screen */
ltdc_clearlayer(active, LTDC_BLACK);
area.xpos = x;
active->layer->blit(active->layer, x, y, inactive->dma2d, &area);
usleep(5);
}
/* Move up */
for (; y >= active->vinfo.yres/4; y--)
{
/* Cleanup current screen */
ltdc_clearlayer(active, LTDC_BLACK);
area.ypos = y;
active->layer->blit(active->layer, x, y, inactive->dma2d, &area);
usleep(5);
}
/* Move right to the start position */
for (; x <= active->vinfo.xres/4; x++)
{
/* Cleanup current screen */
ltdc_clearlayer(active, LTDC_BLACK);
area.xpos = x;
active->layer->blit(active->layer, x, y, inactive->dma2d, &area);
usleep(5);
}
_info("Perform move test\n");
area.xpos = inactive->vinfo.xres/4;
area.ypos = inactive->vinfo.yres/4;
area.xres = inactive->vinfo.xres/2;
area.yres = inactive->vinfo.yres/2;
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
/* Move right */
for (x = area.xpos; x < area.xpos + area.xres/8; x++)
{
/* Cleanup current screen */
ltdc_clearlayer(active, LTDC_BLACK);
active->layer->blit(active->layer, x, area.ypos, inactive->dma2d, &area);
usleep(5);
}
/* Move down */
for (y = area.ypos; y < area.ypos + area.yres/8; y++)
{
/* Cleanup current screen */
ltdc_clearlayer(active, LTDC_BLACK);
active->layer->blit(active->layer, x, y, inactive->dma2d, &area);
usleep(5);
}
/* Move left */
for (; x >= area.xpos - area.xres/8; x--)
{
/* Cleanup current screen */
ltdc_clearlayer(active, LTDC_BLACK);
active->layer->blit(active->layer, x, y, inactive->dma2d, &area);
usleep(5);
}
/* Move up */
for (; y >= area.ypos; y--)
{
/* Cleanup current screen */
ltdc_clearlayer(active, LTDC_BLACK);
active->layer->blit(active->layer, x, y, inactive->dma2d, &area);
usleep(5);
}
/* Move right to the start position */
for (; x <= area.xpos; x++)
{
/* Cleanup current screen */
ltdc_clearlayer(active, LTDC_BLACK);
active->layer->blit(active->layer, x, y, inactive->dma2d, &area);
usleep(5);
}
_info("Perform reference positioning test\n");
area.xres = inactive->vinfo.xres/2;
area.yres = inactive->vinfo.yres/2;
area.xpos = inactive->vinfo.xres/4;
area.ypos = inactive->vinfo.yres/4;
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
/* Move right */
for (; area.xpos >= active->vinfo.xres/4 - active->vinfo.xres/8; area.xpos--)
{
/* Cleanup current screen */
ltdc_clearlayer(active, LTDC_BLACK);
active->layer->blit(active->layer, active->vinfo.xres/4,
active->vinfo.yres/4, inactive->dma2d, &area);
usleep(5);
}
/* Move down */
for (; area.ypos >= active->vinfo.yres/4 - active->vinfo.yres/8; area.ypos--)
{
/* Cleanup current screen */
ltdc_clearlayer(active, LTDC_BLACK);
active->layer->blit(active->layer, active->vinfo.xres/4,
active->vinfo.yres/4, inactive->dma2d, &area);
usleep(5);
}
/* Move left */
for (; area.xpos < active->vinfo.xres/4 + active->vinfo.xres/8; area.xpos++)
{
/* Cleanup current screen */
ltdc_clearlayer(active, LTDC_BLACK);
active->layer->blit(active->layer, active->vinfo.xres/4,
active->vinfo.yres/4, inactive->dma2d, &area);
usleep(5);
}
/* Move up */
for (; area.ypos < active->vinfo.yres/4; area.ypos++)
{
/* Cleanup current screen */
ltdc_clearlayer(active, LTDC_BLACK);
active->layer->blit(active->layer, active->vinfo.xres/4,
active->vinfo.yres/4, inactive->dma2d, &area);
usleep(5);
}
/* Move right to the start position */
for (; area.xpos >= active->vinfo.xres/4; area.xpos--)
{
/* Cleanup current screen */
ltdc_clearlayer(active, LTDC_BLACK);
active->layer->blit(active->layer, active->vinfo.xres/4,
active->vinfo.yres/4, inactive->dma2d, &area);
usleep(5);
}
}
/****************************************************************************
* Name: ltdc_dma2d_blendsimple
*
* Description:
* Test: Perform simple blend operation to check source area positioning
*
****************************************************************************/
static void ltdc_dma2d_blendsimple(void)
{
uint8_t alpha;
FAR struct ltdc_area_s area;
FAR struct surface *active = ltdc_get_surface(LTDC_LAYER_ACTIVE);
FAR struct surface *inactive = ltdc_get_surface(LTDC_LAYER_INACTIVE);
#ifdef CONFIG_STM32_DMA2D_L8
if (active->vinfo.fmt == FB_FMT_RGB8)
{
_warn("WARNING: skipped, output to layer with CLUT pixel format not supported\n");
return;
}
#endif
_info("Perform simple blend operation\n");
_info("active->pinfo.fbmem = %p\n", active->pinfo.fbmem);
_info("inactive->pinfo.fbmem = %p\n", inactive->pinfo.fbmem);
_info("Ensure that both ltdc layer are opaque\n");
active->layer->setalpha(active->layer, 0xff);
inactive->layer->setalpha(inactive->layer, 0xff);
_info("Enable alpha blend mode for both dma2d layer\n");
active->dma2d->setblendmode(active->dma2d, DMA2D_BLEND_ALPHA);
inactive->dma2d->setblendmode(inactive->dma2d, DMA2D_BLEND_ALPHA);
/* Fullscreen blend */
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
_info("Flip the top layer to the active visible layer\n");
active->layer->update(active->layer,
LTDC_UPDATE_ACTIVATE|LTDC_UPDATE_SIM|LTDC_SYNC_VBLANK);
ltdc_simple_draw(&inactive->vinfo, &inactive->pinfo);
_info("Blend the whole bottom layer to the top layer\n");
area.xpos = 0;
area.ypos = 0;
area.xres = inactive->vinfo.xres;
area.yres = inactive->vinfo.yres;
for (alpha = 0; alpha < 255 ; alpha++)
{
active->dma2d->setalpha(active->dma2d, 255 - alpha/4);
inactive->dma2d->setalpha(inactive->dma2d, alpha);
active->layer->blend(active->layer, area.xpos, area.ypos,
active->dma2d, area.xpos, area.ypos,
inactive->dma2d, &area);
usleep(5);
}
/* Blend top left to the middle */
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
area.xpos = 0;
area.ypos = 0;
area.xres = inactive->vinfo.xres/2;
area.yres = inactive->vinfo.yres/2;
_info("Blend the top left red rectangle from the bottom layer with the middle"
" of the top layer\n");
for (alpha = 0; alpha < 255 ; alpha++)
{
active->dma2d->setalpha(active->dma2d, 255 - alpha/4);
inactive->dma2d->setalpha(inactive->dma2d, alpha);
active->layer->blend(active->layer, area.xpos, area.ypos,
active->dma2d, area.xpos, area.ypos,
inactive->dma2d, &area);
usleep(5);
}
/* Top right to the middle blit */
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
area.xpos = inactive->vinfo.xres/2;
area.ypos = 0;
area.xres = inactive->vinfo.xres/2;
area.yres = inactive->vinfo.yres/2;
_info("Blend the top right green rectangle from the bottom layer with the"
" middle of the top layer\n");
for (alpha = 0; alpha < 255 ; alpha++)
{
active->dma2d->setalpha(active->dma2d, 255 - alpha/4);
inactive->dma2d->setalpha(inactive->dma2d, alpha);
active->layer->blend(active->layer, area.xpos, area.ypos,
active->dma2d, area.xpos, area.ypos,
inactive->dma2d, &area);
usleep(5);
}
/* Bottom left to the middle blit */
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
area.xpos = 0;
area.ypos = inactive->vinfo.yres/2;
area.xres = inactive->vinfo.xres/2;
area.yres = inactive->vinfo.yres/2;
_info("Blit the bottom left white rectangle from the bottom layer with the"
" middle of the top layer\n");
for (alpha = 0; alpha < 255 ; alpha++)
{
active->dma2d->setalpha(active->dma2d, 255 - alpha/4);
inactive->dma2d->setalpha(inactive->dma2d, alpha);
active->layer->blend(active->layer, area.xpos, area.ypos,
active->dma2d, area.xpos, area.ypos,
inactive->dma2d, &area);
usleep(5);
}
/* Bottom right to the middle blit */
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
area.xpos = inactive->vinfo.xres/2;
area.ypos = inactive->vinfo.yres/2;
area.xres = inactive->vinfo.xres/2;
area.yres = inactive->vinfo.yres/2;
_info("Blit the bottom right blue rectangle from the bottom layer with the"
" middle of the top layer\n");
for (alpha = 0; alpha < 255 ; alpha++)
{
active->dma2d->setalpha(active->dma2d, 255 - alpha/4);
inactive->dma2d->setalpha(inactive->dma2d, alpha);
active->layer->blend(active->layer, area.xpos, area.ypos,
active->dma2d, area.xpos, area.ypos,
inactive->dma2d, &area);
usleep(5);
}
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
}
#endif /* CONFIG_STM32_LTDC_L2 */
/****************************************************************************
* Name: ltdc_dma2d_blitdynamiclayer
*
* Description:
* Test: Perform simple blit operation with allocated dma2d layer using the
* dma2d interface.
*
****************************************************************************/
static void ltdc_dma2d_blitdynamiclayer(void)
{
int ret = !OK;
FAR struct ltdc_area_s area;
FAR struct ltdc_area_s forearea;
FAR struct dma2d_surface *fore;
FAR struct dma2d_surface *back;
FAR struct surface *active = ltdc_get_surface(LTDC_LAYER_ACTIVE);
#ifdef CONFIG_STM32_DMA2D_L8
if (active->vinfo.fmt == FB_FMT_RGB8)
{
_warn("WARNING: skipped, output to layer with CLUT pixel format not supported\n");
return;
}
#endif
/* Create two new dma2d layer */
back = ltdc_create_dma2d_surface(active->vinfo.xres, active->vinfo.yres,
active->vinfo.fmt);
if (!back)
{
_exit(1);
}
_info("create background dma2d surface: %p\n", back);
fore = ltdc_create_dma2d_surface(active->vinfo.xres/2, active->vinfo.yres/2,
active->vinfo.fmt);
if (!fore)
{
ltdc_remove_dma2d_surface(back);
_exit(1);
}
_info("create foreground dma2d surface: %p\n", fore);
/* Wrong positioning detection */
forearea.xpos = 0;
forearea.ypos = 0;
forearea.xres = fore->vinfo.xres;
forearea.yres = fore->vinfo.yres;
_info("check if the ltdc driver recognized when positioning overflows the whole"
" layer buffer\n");
if (active->layer->blit(active->layer,
active->vinfo.xres - forearea.xres + 1,
active->vinfo.yres - forearea.yres, fore->dma2d, &forearea) != OK)
{
ret = OK;
}
if (active->layer->blit(active->layer,
active->vinfo.xres - forearea.xres,
active->vinfo.yres - forearea.yres + 1, fore->dma2d, &forearea) != OK)
{
ret = ret == OK ? OK : ret;
}
forearea.xpos = 1;
if (active->layer->blit(active->layer, 0, 0, fore->dma2d, &forearea) != OK)
{
ret = ret == OK ? OK : ret;
}
forearea.xpos = 0;
forearea.ypos = 1;
if (active->layer->blit(active->layer, 0, 0, fore->dma2d, &forearea) != OK)
{
ret = ret == OK ? OK : ret;
}
if (ret == OK)
{
_info("ok, driver detects wrong positioning\n");
}
else
{
_err("ERROR: fail, wrong positioning can overflow layer buffer\n");
}
_info("check if the dma2d driver recognized when positioning overflows the"
" whole layer buffer\n");
forearea.xpos = 0;
forearea.ypos = 0;
if (active->dma2d->blit(active->dma2d,
active->vinfo.xres - forearea.xres + 1,
active->vinfo.yres - forearea.yres, fore->dma2d, &forearea) != OK)
{
ret = OK;
}
if (active->dma2d->blit(active->dma2d,
active->vinfo.xres - forearea.xres,
active->vinfo.yres - forearea.yres + 1, fore->dma2d, &forearea) != OK)
{
ret = ret == OK ? OK : ret;
}
forearea.xpos = 1;
if (active->dma2d->blit(active->dma2d, 0, 0, fore->dma2d, &forearea) != OK)
{
ret = ret == OK ? OK : ret;
}
forearea.xpos = 0;
forearea.ypos = 1;
if (active->dma2d->blit(active->dma2d, 0, 0, fore->dma2d, &forearea) != OK)
{
ret = ret == OK ? OK : ret;
}
if (ret == OK)
{
_info("ok, driver detects wrong positioning\n");
}
else
{
_err("ERROR: fail, wrong positioning can overflow layer buffer\n");
}
/* Initialize the dma2d fullscreen background layer */
ltdc_simple_draw(&back->vinfo, &back->pinfo);
/* Initialize foreground area for blitting to the screen */
forearea.xpos = 0;
forearea.ypos = 0;
forearea.xres = fore->vinfo.xres;
forearea.yres = fore->vinfo.yres;
/* Blit test */
_info("Perform simple dma2d blit operation\n");
_info("Ensure that the ltdc layer is opaque\n");
active->layer->setalpha(active->layer, 0xff);
_info("Disable blend mode for the ltdc layer\n");
active->layer->setblendmode(active->layer, LTDC_BLEND_NONE);
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
_info("Flip the top layer to the active visible layer\n");
active->layer->update(active->layer,
LTDC_UPDATE_ACTIVATE|LTDC_SYNC_VBLANK);
/* Top left to the middle blit */
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
area.xpos = 0;
area.ypos = 0;
area.xres = active->vinfo.xres/2;
area.yres = active->vinfo.yres/2;
_info("Blit the top left red rectangle from the background layer to the"
" foreground layer\n");
fore->dma2d->blit(fore->dma2d, 0, 0, back->dma2d, &area);
_info("Blit the resulting dma2d layer to the middle of the screen\n");
active->layer->blit(active->layer, active->vinfo.xres/4,
active->vinfo.yres/4, back->dma2d, &forearea);
usleep(1000000);
/* Top right to the middle blit */
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
area.xpos = active->vinfo.xres/2;
area.ypos = 0;
area.xres = active->vinfo.xres/2;
area.yres = active->vinfo.yres/2;
_info("Blit the top right green rectangle from the background layer to the"
" foreground layer\n");
fore->dma2d->blit(fore->dma2d, 0, 0, back->dma2d, &area);
_info("Blit the resulting dma2d layer to the middle of the screen\n");
active->layer->blit(active->layer, active->vinfo.xres/4,
active->vinfo.yres/4, fore->dma2d, &forearea);
usleep(1000000);
/* Bottom left to the middle blit */
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
area.xpos = 0;
area.ypos = active->vinfo.yres/2;
area.xres = active->vinfo.xres/2;
area.yres = active->vinfo.yres/2;
_info("Blit the bottom left white rectangle from the background layer to the"
" foreground layer\n");
fore->dma2d->blit(fore->dma2d, 0, 0, back->dma2d, &area);
_info("Blit the resulting dma2d layer to the middle of the screen\n");
active->layer->blit(active->layer, active->vinfo.xres/4,
active->vinfo.yres/4, fore->dma2d, &forearea);
usleep(1000000);
/* Bottom right to the middle blit */
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
area.xpos = active->vinfo.xres/2;
area.ypos = active->vinfo.yres/2;
area.xres = active->vinfo.xres/2;
area.yres = active->vinfo.yres/2;
_info("Blit the bottom right blue rectangle from the background layer to the"
" foreground layer\n");
fore->dma2d->blit(fore->dma2d, 0, 0, back->dma2d, &area);
_info("Blit the resulting dma2d layer to the middle of the screen\n");
active->layer->blit(active->layer, active->vinfo.xres/4,
active->vinfo.yres/4, fore->dma2d, &forearea);
usleep(1000000);
/* Middle to the middle blit */
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
area.xpos = active->vinfo.xres/4;
area.ypos = active->vinfo.yres/4;
area.xres = active->vinfo.xres/2;
area.yres = active->vinfo.yres/2;
_info("Blit the bottom half rectangle from the background layers to the middle"
" of the foreground layer\n");
fore->dma2d->blit(fore->dma2d, 0, 0, back->dma2d, &area);
_info("Blit the resulting dma2d layer to the middle of the screen\n");
active->layer->blit(active->layer, active->vinfo.xres/4,
active->vinfo.yres/4, fore->dma2d, &forearea);
usleep(1000000);
ltdc_remove_dma2d_surface(fore);
ltdc_remove_dma2d_surface(back);
}
/****************************************************************************
* Name: ltdc_dma2d_blenddynamiclayer
*
* Description:
* Test: Perform simple blend operation with allocated dma2d layer using the
* dma2d interface.
*
****************************************************************************/
static void ltdc_dma2d_blenddynamiclayer(void)
{
int ret = !OK;
uint8_t alpha;
FAR struct ltdc_area_s area;
FAR struct dma2d_surface *fore;
FAR struct dma2d_surface *back;
FAR struct surface *active = ltdc_get_surface(LTDC_LAYER_ACTIVE);
#ifdef CONFIG_STM32_DMA2D_L8
if (active->vinfo.fmt == FB_FMT_RGB8)
{
_warn("WARNING: skipped, output to layer with CLUT pixel format not supported\n");
return;
}
#endif
/* Create two new dma2d layer */
back = ltdc_create_dma2d_surface(active->vinfo.xres, active->vinfo.yres,
active->vinfo.fmt);
if (!back)
{
_exit(1);
}
_info("create background dma2d surface: %p\n", back);
fore = ltdc_create_dma2d_surface(active->vinfo.xres/2, active->vinfo.yres/2,
active->vinfo.fmt);
if (!fore)
{
ltdc_remove_dma2d_surface(back);
_exit(1);
}
_info("create foreground dma2d surface: %p\n", fore);
/* Wrong positioning detection */
_info("check if the ltdc driver recognized when positioning overflows the whole"
" layer buffer\n");
area.xpos = 0;
area.ypos = 0;
area.xres = back->vinfo.xres;
area.xres = back->vinfo.yres;
if (active->layer->blend(active->layer,
active->vinfo.xres - area.xres + 1,
active->vinfo.yres - area.yres, fore->dma2d,
0, 0, back->dma2d, &area) != OK)
{
ret = OK;
}
if (active->layer->blend(active->layer,
active->vinfo.xres - area.xres + 1,
active->vinfo.yres - area.yres, fore->dma2d,
0, 0, back->dma2d, &area) != OK)
{
ret = ret == OK ? OK : ret;
}
if (active->layer->blend(active->layer, 0, 0, fore->dma2d,
fore->vinfo.xres - area.xres + 1,
fore->vinfo.yres - area.yres, back->dma2d, &area) != OK)
{
ret = ret == OK ? OK : ret;
}
if (active->layer->blend(active->layer, 0, 0, fore->dma2d,
fore->vinfo.xres - area.xres,
fore->vinfo.yres - area.yres + 1, back->dma2d, &area) != OK)
{
ret = ret == OK ? OK : ret;
}
area.xpos = 1;
if (active->layer->blend(active->layer, 0, 0, fore->dma2d,
0, 0, back->dma2d, &area) != OK)
{
ret = ret == OK ? OK : ret;
}
area.xpos = 0;
area.ypos = 1;
if (active->layer->blend(active->layer, 0, 0, fore->dma2d,
0, 0, back->dma2d, &area) != OK)
{
ret = ret == OK ? OK : ret;
}
if (ret == OK)
{
_info("ok, driver detects wrong positioning\n");
}
else
{
_err("ERROR: fail, wrong positioning can overflow layer buffer\n");
}
_info("check if the dma2d driver recognized when positioning overflows the"
" whole layer buffer\n");
area.xpos = 0;
area.ypos = 0;
if (active->dma2d->blend(active->dma2d,
active->vinfo.xres - area.xres + 1,
active->vinfo.yres - area.yres, fore->dma2d,
0, 0, back->dma2d, &area) != OK)
{
ret = OK;
}
if (active->dma2d->blend(active->dma2d,
active->vinfo.xres - area.xres + 1,
active->vinfo.yres - area.yres, fore->dma2d,
0, 0, back->dma2d, &area) != OK)
{
ret = ret == OK ? OK : ret;
}
if (active->dma2d->blend(active->dma2d, 0, 0, fore->dma2d,
fore->vinfo.xres - area.xres + 1,
fore->vinfo.yres - area.yres, back->dma2d, &area) != OK)
{
ret = ret == OK ? OK : ret;
}
if (active->dma2d->blend(active->dma2d, 0, 0, fore->dma2d,
fore->vinfo.xres - area.xres,
fore->vinfo.yres - area.yres + 1, back->dma2d, &area) != OK)
{
ret = ret == OK ? OK : ret;
}
area.xpos = 1;
if (active->dma2d->blend(active->dma2d, 0, 0, fore->dma2d,
0, 0, back->dma2d, &area) != OK)
{
ret = ret == OK ? OK : ret;
}
area.xpos = 0;
area.ypos = 1;
if (active->dma2d->blend(active->dma2d, 0, 0, fore->dma2d,
0, 0, back->dma2d, &area) != OK)
{
ret = ret == OK ? OK : ret;
}
if (ret == OK)
{
_info("ok, driver detects wrong positioning\n");
}
else
{
_err("ERROR: fail, wrong positioning can overflow layer buffer\n");
}
/* Initialize the dma2d fullscreen background layer */
ltdc_simple_draw(&back->vinfo, &back->pinfo);
/* Blit test */
_info("Perform simple dma2d blend operation\n");
_info("Ensure that the ltdc layer is opaque\n");
active->layer->setalpha(active->layer, 0xff);
_info("Disable blend mode for the ltdc layer\n");
active->layer->setblendmode(active->layer, LTDC_BLEND_NONE);
_info("Enable alpha blend mode for both dma2d layer\n");
fore->dma2d->setblendmode(fore->dma2d, DMA2D_BLEND_ALPHA);
back->dma2d->setblendmode(back->dma2d, DMA2D_BLEND_ALPHA);
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
_info("Flip the top layer to the active visible layer\n");
active->layer->update(active->layer,
LTDC_UPDATE_ACTIVATE|LTDC_SYNC_VBLANK);
/* Top left to the middle */
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
area.xpos = 0;
area.ypos = 0;
area.xres = active->vinfo.xres/2;
area.yres = active->vinfo.yres/2;
_info("Blend the top left red rectangle from the background layer with the"
" middle of the foreground layer\n");
for (alpha = 0; alpha < 255 ; alpha++)
{
fore->dma2d->setalpha(fore->dma2d, 255 - alpha);
back->dma2d->setalpha(back->dma2d, alpha);
active->dma2d->blend(active->dma2d, active->vinfo.xres/4,
active->vinfo.yres/4, fore->dma2d, 0, 0,
back->dma2d, &area);
usleep(5);
}
usleep(1000000);
/* Top right to the middle blit */
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
area.xpos = active->vinfo.xres/2;
area.ypos = 0;
area.xres = active->vinfo.xres/2;
area.yres = active->vinfo.yres/2;
_info("Blend the top right green rectangle from the background layer with the"
" middle of the foreground layer\n");
for (alpha = 0; alpha < 255 ; alpha++)
{
fore->dma2d->setalpha(fore->dma2d, 255 - alpha);
back->dma2d->setalpha(back->dma2d, alpha);
active->dma2d->blend(active->dma2d, active->vinfo.xres/4,
active->vinfo.yres/4, fore->dma2d, 0, 0,
back->dma2d, &area);
usleep(5);
}
usleep(1000000);
/* Bottom left to the middle blit */
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
area.xpos = 0;
area.ypos = active->vinfo.yres/2;
area.xres = active->vinfo.xres/2;
area.yres = active->vinfo.yres/2;
_info("Blend the bottom left white rectangle from the background layer with the"
" middle of foreground layer\n");
for (alpha = 0; alpha < 255 ; alpha++)
{
fore->dma2d->setalpha(fore->dma2d, 255 - alpha);
back->dma2d->setalpha(back->dma2d, alpha);
active->dma2d->blend(active->dma2d, active->vinfo.xres/4,
active->vinfo.yres/4, fore->dma2d, 0, 0,
back->dma2d, &area);
usleep(5);
}
usleep(1000000);
/* Bottom right to the middle blit */
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
area.xpos = active->vinfo.xres/2;
area.ypos = active->vinfo.yres/2;
area.xres = active->vinfo.xres/2;
area.yres = active->vinfo.yres/2;
_info("Blend the bottom right blue rectangle from the background layer with the"
" middle of the foreground layer\n");
for (alpha = 0; alpha < 255 ; alpha++)
{
fore->dma2d->setalpha(fore->dma2d, 255 - alpha);
back->dma2d->setalpha(back->dma2d, alpha);
active->dma2d->blend(active->dma2d, active->vinfo.xres/4,
active->vinfo.yres/4, fore->dma2d, 0, 0,
back->dma2d, &area);
usleep(5);
}
usleep(1000000);
/* Middle to the middle blit */
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
area.xpos = active->vinfo.xres/4;
area.ypos = active->vinfo.yres/4;
area.xres = active->vinfo.xres/2;
area.yres = active->vinfo.yres/2;
_info("Blend the bottom half screen rectangle from the background layers middle"
" with the middle of the foreground layer\n");
for (alpha = 0; alpha < 255 ; alpha++)
{
fore->dma2d->setalpha(fore->dma2d, 255 - alpha);
back->dma2d->setalpha(back->dma2d, alpha);
active->dma2d->blend(active->dma2d, active->vinfo.xres/4,
active->vinfo.yres/4, fore->dma2d, 0, 0,
back->dma2d, &area);
usleep(5);
}
usleep(1000000);
ltdc_remove_dma2d_surface(fore);
ltdc_remove_dma2d_surface(back);
}
/****************************************************************************
* Name: ltdc_dma2d_blitflippositioning
*
* Description:
* Perform simple blit and flip operation with both interfaces
*
****************************************************************************/
static void ltdc_dma2d_blitflippositioning(void)
{
uint32_t x, y;
FAR struct ltdc_area_s area;
FAR struct dma2d_surface *fore;
FAR struct dma2d_surface *back;
FAR struct dma2d_surface *image;
FAR struct surface *active = ltdc_get_surface(LTDC_LAYER_ACTIVE);
FAR struct surface *inactive = ltdc_get_surface(LTDC_LAYER_INACTIVE);
#ifdef CONFIG_STM32_DMA2D_L8
if (active->vinfo.fmt == FB_FMT_RGB8 || inactive->vinfo.fmt == FB_FMT_RGB8)
{
_warn("WARNING: skipped, output to layer with CLUT pixel format not supported\n");
return;
}
#endif
/* Create three new dma2d layer */
fore = ltdc_create_dma2d_surface(active->vinfo.xres/2, active->vinfo.yres/2,
active->vinfo.fmt);
if (!fore)
{
_exit(1);
}
_info("create foreground dma2d surface: %p\n", fore);
back = ltdc_create_dma2d_surface(active->vinfo.xres/2, active->vinfo.yres/2,
active->vinfo.fmt);
if (!back)
{
ltdc_remove_dma2d_surface(fore);
_exit(1);
}
_info("create background dma2d surface: %p\n", back);
image = ltdc_create_dma2d_surface(active->vinfo.xres, active->vinfo.yres,
active->vinfo.fmt);
if (!image)
{
ltdc_remove_dma2d_surface(fore);
ltdc_remove_dma2d_surface(back);
_exit(1);
}
_info("create the dma2d surface to store the image: %p\n", image);
_info("Enable alpha blending for both dma2d layer\n");
fore->dma2d->setblendmode(fore->dma2d, DMA2D_BLEND_ALPHA);
back->dma2d->setblendmode(back->dma2d, DMA2D_BLEND_ALPHA);
_info("Ensure that both ltdc layer opaque\n");
active->layer->setalpha(active->layer, 0xff);
inactive->layer->setalpha(inactive->layer, 0xff);
_info("Disable blend mode for both ltdc layer\n");
active->layer->setblendmode(active->layer, LTDC_BLEND_NONE);
inactive->layer->setblendmode(inactive->layer, LTDC_BLEND_NONE);
/* Fullscreen blit */
_info("Set the active layer to fullscreen black\n");
ltdc_clearlayer(active, LTDC_BLACK);
_info("Flip the top layer to the active visible layer\n");
inactive->layer->update(active->layer,
LTDC_UPDATE_ACTIVATE|LTDC_SYNC_VBLANK);
/* Draw the four colored rectangles for blend operations */
ltdc_simple_draw(&image->vinfo, &image->pinfo);
_info("Perform positioning test\n");
area.xpos = inactive->vinfo.xres/4;
area.ypos = inactive->vinfo.yres/4;
area.yres = back->vinfo.yres;
area.xres = back->vinfo.xres;
/* Move right */
y = active->vinfo.yres/4;
for (x = active->vinfo.xres/4; x < active->vinfo.xres/4 + active->vinfo.xres/8; x++)
{
area.xpos = x;
back->dma2d->blit(back->dma2d, 0, 0, image->dma2d, &area);
ltdc_blendoutline(fore, back);
ltdc_blendrect(fore, back, x, y, 16);
}
/* Move down */
for (; y < active->vinfo.yres/4 + active->vinfo.yres/8; y++)
{
area.ypos = y;
back->dma2d->blit(back->dma2d, 0, 0, image->dma2d, &area);
ltdc_blendoutline(fore, back);
ltdc_blendrect(fore, back, x, y, 16);
}
/* Move left */
for (; x >= active->vinfo.xres/4 - active->vinfo.xres/8; x--)
{
area.xpos = x;
back->dma2d->blit(back->dma2d, 0, 0, image->dma2d, &area);
ltdc_blendoutline(fore, back);
ltdc_blendrect(fore, back, x, y, 16);
}
/* Move up */
for (; y >= active->vinfo.yres/4; y--)
{
area.ypos = y;
back->dma2d->blit(back->dma2d, 0, 0, image->dma2d, &area);
ltdc_blendoutline(fore, back);
ltdc_blendrect(fore, back, x, y, 16);
}
/* Move right to the start position */
for (; x <= active->vinfo.xres/4; x++)
{
area.xpos = x;
back->dma2d->blit(back->dma2d, 0, 0, image->dma2d, &area);
ltdc_blendoutline(fore, back);
ltdc_blendrect(fore, back, x, y, 16);
}
ltdc_simple_draw(&back->vinfo, &back->pinfo);
ltdc_blendoutline(fore, back);
_info("Perform move test\n");
area.xpos = inactive->vinfo.xres/4;
area.ypos = inactive->vinfo.yres/4;
area.yres = back->vinfo.yres;
area.xres = back->vinfo.xres;
back->dma2d->blit(back->dma2d, 0, 0, inactive->dma2d, &area);
/* Move right */
y = area.ypos;
for (x = area.xpos; x < area.xpos + active->vinfo.xres/8; x++)
{
back->dma2d->blit(back->dma2d, 0, 0, image->dma2d, &area);
ltdc_blendoutline(fore, back);
ltdc_blendrect(fore, back, x, y, 16);
}
/* Move down */
for (; y < area.ypos + active->vinfo.yres/8; y++)
{
back->dma2d->blit(back->dma2d, 0, 0, image->dma2d, &area);
ltdc_blendoutline(fore, back);
ltdc_blendrect(fore, back, x, y, 16);
}
/* Move left */
for (; x >= area.xpos - active->vinfo.xres/8; x--)
{
back->dma2d->blit(back->dma2d, 0, 0, image->dma2d, &area);
ltdc_blendoutline(fore, back);
ltdc_blendrect(fore, back, x, y, 16);
}
/* Move up */
for (; y >= area.ypos; y--)
{
back->dma2d->blit(back->dma2d, 0, 0, image->dma2d, &area);
ltdc_blendoutline(fore, back);
ltdc_blendrect(fore, back, x, y, 16);
}
/* Move right to the start position */
for (; x <= area.xpos; x++)
{
back->dma2d->blit(back->dma2d, 0, 0, image->dma2d, &area);
ltdc_blendoutline(fore, back);
ltdc_blendrect(fore, back, x, y, 16);
}
_info("Perform reference positioning test\n");
area.xpos = inactive->vinfo.xres/4;
area.ypos = inactive->vinfo.yres/4;
area.yres = back->vinfo.yres;
area.xres = back->vinfo.xres;
/* Move right */
for (; area.xpos >= active->vinfo.xres/4 - active->vinfo.xres/8; area.xpos--)
{
back->dma2d->blit(back->dma2d, 0, 0, image->dma2d, &area);
ltdc_blendoutline(fore, back);
ltdc_blendrect(fore, back, active->vinfo.xres/4, active->vinfo.yres/4, 16);
}
/* Move down */
for (; area.ypos >= active->vinfo.yres/4 - active->vinfo.yres/8; area.ypos--)
{
back->dma2d->blit(back->dma2d, 0, 0, image->dma2d, &area);
ltdc_blendoutline(fore, back);
ltdc_blendrect(fore, back, active->vinfo.xres/4, active->vinfo.yres/4, 16);
}
/* Move left */
for (; area.xpos < active->vinfo.xres/4 + active->vinfo.xres/8; area.xpos++)
{
back->dma2d->blit(back->dma2d, 0, 0, image->dma2d, &area);
ltdc_blendoutline(fore, back);
ltdc_blendrect(fore, back, active->vinfo.xres/4, active->vinfo.yres/4, 16);
}
/* Move up */
for (; area.ypos < active->vinfo.yres/4; area.ypos++)
{
back->dma2d->blit(back->dma2d, 0, 0, image->dma2d, &area);
ltdc_blendoutline(fore, back);
ltdc_blendrect(fore, back, active->vinfo.xres/4, active->vinfo.yres/4, 16);
}
/* Move right to the start position */
for (; area.xpos >= active->vinfo.xres/4; area.xpos--)
{
back->dma2d->blit(back->dma2d, 0, 0, image->dma2d, &area);
ltdc_blendoutline(fore, back);
ltdc_blendrect(fore, back, active->vinfo.xres/4, active->vinfo.yres/4, 16);
}
ltdc_remove_dma2d_surface(fore);
ltdc_remove_dma2d_surface(back);
ltdc_remove_dma2d_surface(image);
}
/****************************************************************************
* Name: ltdc_screensaver
*
* Description:
* Perform the screensaver test.
* Note! This test runs in an endless loop.
*
****************************************************************************/
static void ltdc_screensaver(void)
{
int32_t x1, y1, x2, y2;
FAR struct ltdc_area_s area;
FAR struct dma2d_surface *scratch;
FAR struct dma2d_surface *rect1;
FAR struct dma2d_surface *rect2;
FAR struct dma2d_surface *image;
FAR struct surface *active = ltdc_get_surface(LTDC_LAYER_ACTIVE);
FAR struct surface *inactive = ltdc_get_surface(LTDC_LAYER_INACTIVE);
#ifdef CONFIG_STM32_DMA2D_L8
if (active->vinfo.fmt == FB_FMT_RGB8 || inactive->vinfo.fmt == FB_FMT_RGB8)
{
_info("skipped, output to layer with CLUT pixel format not supported\n");
return;
}
#endif
/* Create three new dma2d layer */
scratch = ltdc_create_dma2d_surface(active->vinfo.xres/4,
active->vinfo.yres/4,
active->vinfo.fmt);
if (!scratch)
{
_exit(1);
}
_info("create a scratch dma2d layer: %p\n", scratch);
rect1 = ltdc_create_dma2d_surface(active->vinfo.xres/4,
active->vinfo.yres/4,
active->vinfo.fmt);
if (!rect1)
{
ltdc_remove_dma2d_surface(scratch);
_exit(1);
}
_info("create a dma2d layer for the rectangle 1: %p\n", rect1);
rect2 = ltdc_create_dma2d_surface(active->vinfo.xres/4,
active->vinfo.yres/4,
active->vinfo.fmt);
if (!rect2)
{
ltdc_remove_dma2d_surface(scratch);
ltdc_remove_dma2d_surface(rect1);
_exit(1);
}
_info("create a dma2d layer for rectangle 2: %p\n", rect2);
image = ltdc_create_dma2d_surface(active->vinfo.xres,
active->vinfo.yres,
active->vinfo.fmt);
if (!image)
{
ltdc_remove_dma2d_surface(scratch);
ltdc_remove_dma2d_surface(rect1);
ltdc_remove_dma2d_surface(rect2);
_exit(1);
}
_info("create a dma2d layer to store the background image: %p\n", image);
_info("Enable alpha blending for the dma2d layer\n");
scratch->dma2d->setblendmode(scratch->dma2d, DMA2D_BLEND_ALPHA);
rect1->dma2d->setblendmode(rect1->dma2d, DMA2D_BLEND_ALPHA);
rect2->dma2d->setblendmode(rect2->dma2d, DMA2D_BLEND_ALPHA);
/* ltdc layer settings */
_info("Ensure that both ltdc layer opaque\n");
active->layer->setalpha(active->layer, 0xff);
inactive->layer->setalpha(inactive->layer, 0xff);
_info("Disable blend mode for both ltdc layer\n");
active->layer->setblendmode(active->layer, LTDC_BLEND_NONE);
inactive->layer->setblendmode(inactive->layer, LTDC_BLEND_NONE);
/* Draw the four colored rectangles for blend operations */
ltdc_simple_draw(&image->vinfo, &image->pinfo);
_info("Perform screensaver\n");
area.xpos = image->vinfo.xres/4 + image->vinfo.xres / 8;
area.ypos = image->vinfo.yres/4 + image->vinfo.yres / 8;
area.xres = rect1->vinfo.xres;
area.yres = rect1->vinfo.yres;
/* Create a rectangle with fix content */
rect1->dma2d->blit(rect1->dma2d, 0, 0, image->dma2d, &area);
ltdc_blendoutline(scratch, rect1);
/* Randomize the start position */
x1 = rand() % (inactive->vinfo.xres - rect1->vinfo.xres);
y1 = rand() % (inactive->vinfo.yres - rect1->vinfo.yres);
x2 = rand() % (inactive->vinfo.xres - rect2->vinfo.xres);
y2 = rand() % (inactive->vinfo.yres - rect2->vinfo.yres);
/* Change the blend area to the rect2 resolution */
area.yres = rect2->vinfo.yres;
area.xres = rect2->vinfo.xres;
for(;;)
{
int32_t xend1;
int32_t yend1;
int32_t xend2;
int32_t yend2;
/* Randomize next end position */
xend1 = rand() % (inactive->vinfo.xres - rect1->vinfo.xres);
yend1 = rand() % (inactive->vinfo.yres - rect1->vinfo.yres);
xend2 = rand() % (inactive->vinfo.xres - rect2->vinfo.xres);
yend2 = rand() % (inactive->vinfo.yres - rect2->vinfo.yres);
while (x1 != xend1 && y1 != yend1 && x2 != xend2 && y2 != yend2)
{
FAR struct surface *sur = ltdc_get_surface(LTDC_LAYER_INACTIVE);
/* Calculate the next pixel start positions */
ltdc_calcpos(&x1, &y1, xend1, yend1);
ltdc_calcpos(&x2, &y2, xend2, yend2);
area.xpos = x2;
area.ypos = y2;
/* Create the rectangle with the dynamic content */
rect2->dma2d->blit(rect2->dma2d, 0, 0, image->dma2d, &area);
ltdc_blendoutline(scratch, rect2);
ltdc_clearlayer(sur, LTDC_BLACK);
/* Blend rect2 as underlying rectangle */
ltdc_blendshadow(sur, scratch, rect2, x2, y2, 10, 255);
/* Blend rect1 as overlying rectangle ans semitransparency */
ltdc_blendshadow(sur, scratch, rect1, x1, y1, 10, 127);
/* Flip the layer to make the changes visible */
sur->layer->update(sur->layer, LTDC_UPDATE_FLIP|
LTDC_SYNC_VBLANK|
LTDC_SYNC_WAIT);
usleep(500);
}
}
}
/****************************************************************************
* Name: ltdc_dma2d_blitmain
*
* Description:
* Triggers the dma2d tests
*
****************************************************************************/
void ltdc_dma2d_main(void)
{
ltdc_dma2d_interface();
ltdc_dma2d_fillarea();
#ifdef CONFIG_STM32_LTDC_L2
ltdc_dma2d_blitsimple();
ltdc_dma2d_blitpositioning();
ltdc_dma2d_blendsimple();
#endif
ltdc_dma2d_blitdynamiclayer();
ltdc_dma2d_blenddynamiclayer();
#ifdef CONFIG_STM32_LTDC_L2
ltdc_dma2d_blitflippositioning();
ltdc_screensaver();
#endif
}