/****************************************************************************
 * examples/ltdc/ltdc_main.c
 *
 *   Copyright (C) 2008, 2011-2012, 2015-2016 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 "ltdc.h"

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

/****************************************************************************
 * Private Data
****************************************************************************/

#ifdef CONFIG_STM32_LTDC_INTERFACE
#ifdef CONFIG_STM32_LTDC_L2
static struct surface g_surface[2];
#else
static struct surface g_surface[1];
#endif
#endif

#ifdef CONFIG_FB_CMAP
#ifdef CONFIG_FB_TRANSPARENCY
static uint8_t g_color[4*LTDC_EXAMPLE_NCOLORS];
#else
static uint8_t g_color[3*LTDC_EXAMPLE_NCOLORS];
#endif

static struct fb_cmap_s g_cmap =
{
  .first  = 0,
  .len    = LTDC_EXAMPLE_NCOLORS,
  .red    = &g_color[0],
  .green  = &g_color[LTDC_EXAMPLE_NCOLORS],
  .blue   = &g_color[2*LTDC_EXAMPLE_NCOLORS]
#ifdef CONFIG_FB_TRANSPARENCY
 ,.transp = &g_color[3*LTDC_EXAMPLE_NCOLORS]
#endif
};
#endif

/****************************************************************************
 * Private Function
****************************************************************************/

#ifdef CONFIG_STM32_LTDC_INTERFACE
/****************************************************************************
 * Name: ltdc_init_surface
 *
 * Description:
 *   Initialize layer and the layers videoinfo and planeinfo
 *
 ****************************************************************************/

static int ltdc_init_surface(int lid, uint32_t mode)
{
  FAR struct surface *sur = &g_surface[lid];

  sur->layer = up_ltdcgetlayer(lid);

  if (!sur->layer)
    {
      _err("ERROR: up_ltdcgetlayer() failed\n");
      return -1;
    }

  if (sur->layer->getvideoinfo(sur->layer, &sur->vinfo) != OK)
    {
      _err("ERROR: getvideoinfo() failed\n");
      return -1;
    }

  if (sur->layer->getplaneinfo(sur->layer, 0, &sur->pinfo) != OK)
    {
      _err("ERROR: getplaneinfo() failed\n");
      return -1;
    }

  _info("layer %d is configured with: xres = %d, yres = %d,"
      "fb start address = %p, fb size = %d, fmt = %d, bpp = %d\n",
          lid, sur->vinfo.xres, sur->vinfo.yres, sur->pinfo.fbmem, sur->pinfo.fblen,
          sur->vinfo.fmt, sur->pinfo.bpp);

#ifdef CONFIG_FB_CMAP

  /* Initialize the clut table */

  if (sur->vinfo.fmt == FB_FMT_RGB8)
    {
      sur->layer->setclut(sur->layer, &g_cmap);
      sur->layer->update(sur->layer, LTDC_UPDATE_NONE);
    }
#endif

#ifdef CONFIG_STM32_DMA2D
  /* Initialize dma2d layer */

  if (sur->layer->getlid(sur->layer, &lid, LTDC_LAYER_DMA2D) != OK)
    {
      _err("ERROR: getlid() failed\n");
      return -1;
    }

  sur->dma2d = up_dma2dgetlayer(lid);

  if (sur->dma2d == NULL)
    {
      _err("ERROR: up_dma2dgetlayer() failed\n");
      return -1;
    }
#endif

  return OK;
}

/****************************************************************************
 * Name: ltdc_setget_test
 *
 * Description:
 *   Perform layer area positioning test
 *
 ****************************************************************************/

static void ltdc_setget_test(void)
{
  uint8_t alpha;
  uint16_t xpos;
  uint16_t ypos;
  uint32_t color;
  uint32_t mode;
  int ret;
  FAR struct ltdc_area_s area;
  FAR struct surface *sur = ltdc_get_surface(LTDC_LAYER_ACTIVE);

  _info("Perform set and get test\n");

  /* setalpha */

  ret = sur->layer->setalpha(sur->layer, 0x7f);

  if (ret != OK)
    {
      _err("ERROR: setalpha() failed\n");
    }

  ret = sur->layer->getalpha(sur->layer, &alpha);

  if (ret != OK || alpha != 0x7f)
    {
      _err("ERROR: getalpha() failed\n");
    }

  /* setcolor */

  ret = sur->layer->setcolor(sur->layer, 0x11223344);

  if (ret != OK)
    {
      _err("ERROR: setcolor() failed\n");
    }

  ret = sur->layer->getcolor(sur->layer, &color);

  if (ret != OK || color != 0x11223344)
    {
      _err("ERROR: getcolor() failed\n");
    }

  /* setcolorkey */

  ret = sur->layer->setcolorkey(sur->layer, 0x55667788);

  if (ret != OK)
    {
      _err("ERROR: setcolorkey() failed\n");
    }

  ret = sur->layer->getcolorkey(sur->layer, &color);

  if (ret != OK || color != 0x55667788)
    {
      _err("ERROR: getcolorkey() failed\n");
    }

  /* setblendmode */

  ret = sur->layer->setblendmode(sur->layer, LTDC_BLEND_NONE);

  if (ret != OK)
    {
      _err("ERROR: setblendmode() failed\n");
    }

  ret = sur->layer->getblendmode(sur->layer, &mode);

  if (ret != OK || mode != LTDC_BLEND_NONE)
    {
      _err("ERROR: getblendmode() failed\n");
    }

  /* setarea */

  area.xpos = sur->vinfo.xres/4;
  area.ypos = sur->vinfo.yres/4;
  area.xres = sur->vinfo.xres/2;
  area.yres = sur->vinfo.yres/2;

  ret = sur->layer->setarea(sur->layer, &area,
            sur->vinfo.xres/8, sur->vinfo.yres/8);

  if (ret != OK)
    {
      _err("ERROR: setarea() failed\n");
    }

  ret = sur->layer->getarea(sur->layer, &area, &xpos, &ypos);

  if (ret != OK || xpos != sur->vinfo.xres/8 || ypos != sur->vinfo.yres/8 ||
      area.xpos != sur->vinfo.xres/4 || area.ypos != sur->vinfo.yres/4 ||
      area.xres != sur->vinfo.xres/2 || area.yres != sur->vinfo.yres/2)
    {
      _err("ERROR: getarea() failed\n");
    }

#ifdef CONFIG_FB_CMAP
  if (sur->vinfo.fmt == FB_FMT_RGB8)
    {

      FAR struct fb_cmap_s *cmap = ltdc_createcmap(LTDC_EXAMPLE_NCOLORS);

      ltdc_clrcolor(g_cmap.red, 0x11, LTDC_EXAMPLE_NCOLORS);
      ltdc_clrcolor(g_cmap.blue, 0x22, LTDC_EXAMPLE_NCOLORS);
      ltdc_clrcolor(g_cmap.green, 0x33, LTDC_EXAMPLE_NCOLORS);
#ifdef CONFIG_FB_TRANSPARENCY
      ltdc_clrcolor(g_cmap.transp, 0x44, LTDC_EXAMPLE_NCOLORS);
#endif

      ret = sur->layer->setclut(sur->layer, &g_cmap);

      if (ret != OK)
        {
          _err("ERROR: setclut() failed\n");
        }

      /* Clear all colors to black */

      ltdc_clrcolor(cmap->red, 0, LTDC_EXAMPLE_NCOLORS);
      ltdc_clrcolor(cmap->blue, 0, LTDC_EXAMPLE_NCOLORS);
      ltdc_clrcolor(cmap->green, 0, LTDC_EXAMPLE_NCOLORS);
#ifdef CONFIG_FB_TRANSPARENCY
      ltdc_clrcolor(cmap->transp, 0, LTDC_EXAMPLE_NCOLORS);
#endif

      ret = sur->layer->getclut(sur->layer, cmap);

      if (ret != OK)
        {
          _err("ERROR: getclut() failed\n");
        }

#ifdef CONFIG_FB_TRANSPARENCY
      if (ltdc_cmpcolor(g_cmap.red, cmap->red, LTDC_EXAMPLE_NCOLORS) ||
            ltdc_cmpcolor(g_cmap.blue, cmap->blue, LTDC_EXAMPLE_NCOLORS) ||
             ltdc_cmpcolor(g_cmap.green, cmap->green, LTDC_EXAMPLE_NCOLORS) ||
                ltdc_cmpcolor(g_cmap.transp, cmap->transp, LTDC_EXAMPLE_NCOLORS))
#else
      if (ltdc_cmpcolor(g_cmap.red, cmap->red, LTDC_EXAMPLE_NCOLORS) ||
            ltdc_cmpcolor(g_cmap.blue, cmap->blue, LTDC_EXAMPLE_NCOLORS) ||
             ltdc_cmpcolor(g_cmap.green, cmap->green, LTDC_EXAMPLE_NCOLORS))
#endif
        {
          _err("ERROR: getclut() failed, unexpected cmap content\n");
        }

      ltdc_deletecmap(cmap);

      /* Restore the origin cmap */

      ltdc_init_cmap();

      ret = sur->layer->setclut(sur->layer, &g_cmap);

      if (ret != OK)
        {
          _err("ERROR: setclut() failed\n");
        }
    }
#endif

  /* Restore to default state */

  area.xpos = 0;
  area.ypos = 0;
  area.xres = sur->vinfo.xres;
  area.yres = sur->vinfo.yres;

  sur->layer->setalpha(sur->layer, 0xff);
  sur->layer->setcolor(sur->layer, 0);
  sur->layer->setcolorkey(sur->layer, 0);
  sur->layer->setarea(sur->layer, &area, 0, 0);
  sur->layer->setblendmode(sur->layer, LTDC_BLEND_NONE);
  sur->layer->update(sur->layer, 0);
}

/****************************************************************************
 * Name: ltdc_color_test
 *
 * Description:
 *   Perform layer color test
 *
 ****************************************************************************/

static void ltdc_color_test(void)
{
  struct ltdc_area_s area;

  FAR struct surface *sur = ltdc_get_surface(LTDC_LAYER_ACTIVE);

  area.xpos = sur->vinfo.xres/4;
  area.ypos = sur->vinfo.yres/4;
  area.xres = sur->vinfo.xres/2;
  area.yres = sur->vinfo.yres/2;

  ltdc_simple_draw(&sur->vinfo, &sur->pinfo);

  usleep(1000000);

  /* Default Color black */

  _info("Set default color to black\n");

  sur->layer->setcolor(sur->layer, 0xff000000);

  _info("Update the layer\n");

  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK|LTDC_SYNC_WAIT);

  /* Set active layer to the upper half of the screen */

  _info("Set area to xpos = %d, ypos = %d, xres = %d, yres = %d\n",
        area.xpos, area.ypos, area.xres, area.yres);

  sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);

  _info("Update the layer, should be black outside the colorful rectangle\n");

  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK|LTDC_SYNC_WAIT);

  usleep(1000000);

  /* Default Color red */

  _info("Update the layer, should be red outside the colorful rectangle\n");

  sur->layer->setcolor(sur->layer, 0xffff0000);

  _info("Update the layer\n");

  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK|LTDC_SYNC_WAIT);

  usleep(1000000);

  /* Default Color green */

  _info("Update the layer, should be green outside the colorful rectangle\n");

  sur->layer->setcolor(sur->layer, 0xff00ff00);

  _info("Update the layer\n");

  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK|LTDC_SYNC_WAIT);

  usleep(1000000);

  /* Default Color blue */

  _info("Update the layer, should be blue outside the colorful rectangle\n");

  sur->layer->setcolor(sur->layer, 0xff0000ff);

  _info("Update the layer\n");

  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK|LTDC_SYNC_WAIT);

  usleep(1000000);

  /* Restore original size */

  area.xpos = 0;
  area.ypos = 0;
  area.xres = sur->vinfo.xres;
  area.yres = sur->vinfo.yres;

  _info("Set area to xpos = %d, ypos = %d, xres = %d, yres = %d\n",
        area.xpos, area.ypos, area.xres, area.yres);

  sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);

  /* Default Color black */

  _info("Set default color to black\n");

  sur->layer->setcolor(sur->layer, 0);

  _info("Update the layer\n");

  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK|LTDC_SYNC_WAIT);

  usleep(1000000);
}

/****************************************************************************
 * Name: ltdc_colorkey_test
 *
 * Description:
 *   Perform layer colorkey test
 *
 ****************************************************************************/

static void ltdc_colorkey_test(void)
{
  struct ltdc_area_s area;

  FAR struct surface *sur = ltdc_get_surface(LTDC_LAYER_ACTIVE);

  ltdc_simple_draw(&sur->vinfo, &sur->pinfo);

  area.xpos = sur->vinfo.xres/4;
  area.ypos = sur->vinfo.yres/4;
  area.xres = sur->vinfo.xres/2;
  area.yres = sur->vinfo.yres/2;

  /* Resize active layer */

  _info("Set area to xpos = %d, ypos = %d, xres = %d, yres = %d\n",
        area.xpos, area.ypos, area.xres, area.yres);

  sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);

  /* Enable colorkey */

  sur->layer->setblendmode(sur->layer, LTDC_BLEND_COLORKEY);

  /* Color key white */

  _info("Set colorkey to white\n");

  sur->layer->setcolorkey(sur->layer, 0xffffff);

  _info("Update the layer\n");

  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK|LTDC_SYNC_WAIT);

  usleep(1000000);

  /* Color key red */

  _info("Set colorkey to red\n");

  sur->layer->setcolorkey(sur->layer, 0xff0000);

  _info("Update the layer\n");

  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK|LTDC_SYNC_WAIT);

  usleep(1000000);

  /* Color key green */

  _info("Set colorkey to green\n");

  sur->layer->setcolorkey(sur->layer, 0xff00);

  _info("Update the layer\n");

  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK|LTDC_SYNC_WAIT);

  usleep(1000000);

  /* Color key red */

  _info("Set colorkey to blue\n");

  sur->layer->setcolorkey(sur->layer, 0xff);

  _info("Update the layer\n");

  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK|LTDC_SYNC_WAIT);

  _info("Disable colorkey\n");

  usleep(1000000);

  sur->layer->setcolorkey(sur->layer, 0);

  /* Restore original size */

  area.xpos = 0;
  area.ypos = 0;
  area.xres = sur->vinfo.xres;
  area.yres = sur->vinfo.yres;

  _info("Set area to xpos = %d, ypos = %d, xres = %d, yres = %d\n",
        area.xpos, area.ypos, area.xres, area.yres);

  sur->layer->setarea(sur->layer, &area, 0, 0);

  /* Disable colorkeying */

  sur->layer->setblendmode(sur->layer, LTDC_BLEND_NONE);

  _info("Update the layer\n");

  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK|LTDC_SYNC_WAIT);

  usleep(1000000);
}

/****************************************************************************
 * Name: ltdc_area_test
 *
 * Description:
 *   Perform layer area positioning test
 *
 ****************************************************************************/

static void ltdc_area_test(void)
{
  int n;
  int x;
  int y;
  struct ltdc_area_s area;
  FAR struct surface *sur = ltdc_get_surface(LTDC_LAYER_ACTIVE);

  _info("Perform area test\n");

  ltdc_simple_draw(&sur->vinfo, &sur->pinfo);

  usleep(1000000);

  /* Set active layer to the upper left rectangle of the screen */

  area.xpos = 0;
  area.ypos = 0;
  area.xres = sur->vinfo.xres/2;
  area.yres = sur->vinfo.yres/2;

  _info("Set area to xpos = %d, ypos = %d, xres = %d, yres = %d\n",
        area.xpos, area.ypos, area.xres, area.yres);

  sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);

  _info("Update the layer, to show the upper left rectangle of the screen\n");

  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);

  usleep(1000000);

  /* Set active layer to the upper rigth rectangle of the screen */

  area.xpos = sur->vinfo.xres/2;
  area.ypos = 0;
  area.xres = sur->vinfo.xres/2;
  area.yres = sur->vinfo.yres/2;

  _info("Set area to xpos = %d, ypos = %d, xres = %d, yres = %d\n",
        area.xpos, area.ypos, area.xres, area.yres);

  sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);

  _info("Update the layer, to show the upper right rectangle of the screen\n");

  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);

  usleep(1000000);

  /* Set active layer to the upper left rectangle of the screen */

  area.xpos = 0;
  area.ypos = sur->vinfo.yres/2;
  area.xres = sur->vinfo.xres/2;
  area.yres = sur->vinfo.yres/2;

  _info("Set area to xpos = %d, ypos = %d, xres = %d, yres = %d\n",
        area.xpos, area.ypos, area.xres, area.yres);

  sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);

  _info("Update the layer, to show the lower left rectangle of the screen\n");

  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);

  usleep(1000000);

  /* Set active layer to the upper right rectangle of the screen */

  area.xpos = sur->vinfo.xres/2;
  area.ypos = sur->vinfo.yres/2;
  area.xres = sur->vinfo.xres/2;
  area.yres = sur->vinfo.yres/2;

  _info("Set area to xpos = %d, ypos = %d, xres = %d, yres = %d\n",
        area.xpos, area.ypos, area.xres, area.yres);

  sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);

  _info("Update the layer, to show the lower right rectangle of the screen\n");

  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);

  usleep(1000000);

  /* Perform layer positioning */

  _info("Perform positioning test\n");

  /* Set layer in the middle of the screen */

  area.xpos = sur->vinfo.xres/4;
  area.ypos = sur->vinfo.yres/4;
  area.xres = sur->vinfo.xres/2;
  area.yres = sur->vinfo.yres/2;

  sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);
  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);

  /* Move right */

  for (n = 0; n < sur->vinfo.xres/8; n++)
    {
      area.xpos++;
      sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);
      sur->layer->update(sur->layer, LTDC_SYNC_NONE);
      usleep(5);
    }

  /* Move down */

  for (n = 0; n < sur->vinfo.yres/8; n++)
    {
      area.ypos++;
      sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);
      sur->layer->update(sur->layer, LTDC_SYNC_NONE);
      usleep(5);
    }

  /* Move left */

  for (n = 0; n < sur->vinfo.xres/4; n++)
    {
      area.xpos--;
      sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);
      sur->layer->update(sur->layer, LTDC_SYNC_NONE);
      usleep(5);
    }

  /* Move up */

  for (n = 0; n < sur->vinfo.yres/8; n++)
    {
      area.ypos--;
      sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);
      sur->layer->update(sur->layer, LTDC_SYNC_NONE);
      usleep(5);
    }

  /* Move back to the middle */

  for (n = 0; n < sur->vinfo.xres/8; n++)
    {
      area.xpos++;
      sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);
      sur->layer->update(sur->layer, LTDC_SYNC_NONE);
      usleep(5);
    }

  /* Perform move */

  _info("Perform move test\n");

  /* Set layer in the middle of the screen */

  area.xpos = sur->vinfo.xres/4;
  area.ypos = sur->vinfo.yres/4;
  area.xres = sur->vinfo.xres/2;
  area.yres = sur->vinfo.yres/2;

  sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);
  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);

  /* Move right */

  for (n = 0; n < sur->vinfo.xres/8; n++)
    {
      area.xpos++;
      sur->layer->setarea(sur->layer, &area,
                            sur->vinfo.xres/4,
                            sur->vinfo.yres/4);
      sur->layer->update(sur->layer, LTDC_SYNC_NONE);
      usleep(5);
    }

  /* Move down */

  for (n = 0; n < sur->vinfo.yres/8; n++)
    {
      area.ypos++;
      sur->layer->setarea(sur->layer, &area,
                            sur->vinfo.xres/4,
                            sur->vinfo.yres/4);
      sur->layer->update(sur->layer, LTDC_SYNC_NONE);
      usleep(5);
    }

  /* Move left */

  for (n = 0; n < sur->vinfo.xres/4; n++)
    {
      area.xpos--;
      sur->layer->setarea(sur->layer, &area,
                            sur->vinfo.xres/4,
                            sur->vinfo.yres/4);
      sur->layer->update(sur->layer, LTDC_SYNC_NONE);
      usleep(5);
    }

  /* Move up */

  for (n = 0; n < sur->vinfo.yres/8; n++)
    {
      area.ypos--;
      sur->layer->setarea(sur->layer, &area,
                            sur->vinfo.xres/4,
                            sur->vinfo.yres/4);
      sur->layer->update(sur->layer, LTDC_SYNC_NONE);
      usleep(5);
    }

  /* Move back to the middle */

  for (n = 0; n < sur->vinfo.xres/8; n++)
    {
      area.xpos++;
      sur->layer->setarea(sur->layer, &area,
                            sur->vinfo.xres/4,
                            sur->vinfo.yres/4);
      sur->layer->update(sur->layer, LTDC_SYNC_NONE);
      usleep(5);
    }

  /* Perform Reference position */

  _info("Perform reference positioning test\n");

  /* Set layer in the middle of the screen */

  area.xpos = sur->vinfo.xres/4;
  area.ypos = sur->vinfo.yres/4;
  area.xres = sur->vinfo.xres/2;
  area.yres = sur->vinfo.yres/2;

  sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);
  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);

  /* Move right */

  for (x = 0; x < sur->vinfo.xres/8; x++)
    {
      sur->layer->setarea(sur->layer, &area,
                            sur->vinfo.xres/4 - x,
                            sur->vinfo.yres/4);
      sur->layer->update(sur->layer, LTDC_SYNC_NONE);
      usleep(5);
    }

  /* Move down */

  for (y = 0; y < sur->vinfo.yres/8; y++)
    {
      sur->layer->setarea(sur->layer, &area,
                            sur->vinfo.xres/4 - x,
                            sur->vinfo.yres/4 - y);
      sur->layer->update(sur->layer, LTDC_SYNC_NONE);
      usleep(5);
    }

  /* Move left */

  for (x = 0; x < sur->vinfo.xres/4; x++)
    {
      sur->layer->setarea(sur->layer, &area,
                            sur->vinfo.xres/4 - sur->vinfo.xres/8 + x,
                            sur->vinfo.yres/4 - y);
      sur->layer->update(sur->layer, LTDC_SYNC_NONE);
      usleep(5);
    }

  /* Move up */

  for (y = 0; y < sur->vinfo.yres/8; y++)
    {
      sur->layer->setarea(sur->layer, &area,
                            sur->vinfo.xres/4 - sur->vinfo.xres/8 + x,
                            sur->vinfo.yres/4 - sur->vinfo.yres/8 + y);
      sur->layer->update(sur->layer, LTDC_SYNC_NONE);
      usleep(5);
    }

  /* Move back to the middle */

  for (x = 0; x < sur->vinfo.xres/8; x++)
    {
      sur->layer->setarea(sur->layer, &area,
                            sur->vinfo.xres/4 + sur->vinfo.xres/8 - x,
                            sur->vinfo.yres/4 - sur->vinfo.yres/8 + y);
      sur->layer->update(sur->layer, LTDC_SYNC_NONE);
      usleep(5);
    }

  usleep(1000000);

  /* Restore original size */

  area.xpos = 0;
  area.ypos = 0;
  area.xres = sur->vinfo.xres;
  area.yres = sur->vinfo.yres;

  _info("Set area to xpos = %d, ypos = %d, xres = %d, yres = %d\n",
        area.xpos, area.ypos, area.xres, area.yres);

  sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);

  _info("Update the layer to fullscreen\n");

  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);

  usleep(1000000);
}

/****************************************************************************
 * Name: ltdc_common_test
 *
 * Description:
 *   Perform test with all layer operations at once
 *   Todo: add alpha blending and default color
 *
 ****************************************************************************/

static void ltdc_common_test(void)
{
  int n;
  int c;
  int x;
  int y;
  uint32_t colorkey;
  struct ltdc_area_s area;
  FAR struct surface *sur;

  _info("Set layer 2 to the active layer, blend with subjacent layer 1\n");

  sur = ltdc_get_surface(LTDC_LAYER_TOP);

  ltdc_simple_draw(&sur->vinfo, &sur->pinfo);

  usleep(1000000);

  colorkey = LTDC_EXAMPLE_NCOLORS;

  /* Perform area test */

  _info("Perform area test\n");

  /* Set layer in the middle of the screen */

  area.xpos = sur->vinfo.xres/4;
  area.ypos = sur->vinfo.yres/4;
  area.xres = sur->vinfo.xres/2;
  area.yres = sur->vinfo.yres/2;

  sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);

  /* Enable colorkeying */

  sur->layer->setblendmode(sur->layer, LTDC_BLEND_COLORKEY);

  /* Update the layer */

  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);

  /* Move right */

  for (n = 0, c = 4; n < sur->vinfo.xres/8; n++)
    {
      area.xpos++;
      sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);
      if (c++ == 4)
        {
          c = 0;
          if (colorkey == LTDC_EXAMPLE_NCOLORS)
            colorkey = LTDC_RED;
          else
            colorkey++;

          sur->layer->setcolorkey(sur->layer, 0xff000000 | g_rgb24[colorkey]);
        }

      sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);
    }

  /* Move down */

  for (n = 0, c = 4; n < sur->vinfo.yres/8; n++)
    {
      area.ypos++;
      sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);
      if (c++ == 4)
        {
          c = 0;
          if (colorkey == LTDC_EXAMPLE_NCOLORS)
            colorkey = LTDC_RED;
          else
            colorkey++;

          sur->layer->setcolorkey(sur->layer, 0xff000000 | g_rgb24[colorkey]);
        }

      sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);
    }

  /* Move left */

  for (n = 0, c = 4; n < sur->vinfo.xres/4; n++)
    {
      area.xpos--;
      sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);
      if (c++ == 4)
        {
          c = 0;
          if (colorkey == LTDC_EXAMPLE_NCOLORS)
            colorkey = LTDC_RED;
          else
            colorkey++;

          sur->layer->setcolorkey(sur->layer, 0xff000000 | g_rgb24[colorkey]);
        }

      sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);
    }

  /* Move up */

  for (n = 0, c = 4; n < sur->vinfo.yres/8; n++)
    {
      area.ypos--;
      sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);
      if (c++ == 4)
        {
          c = 0;
          if (colorkey == LTDC_EXAMPLE_NCOLORS)
            colorkey = LTDC_RED;
          else
            colorkey++;

          sur->layer->setcolorkey(sur->layer, 0xff000000 | g_rgb24[colorkey]);
        }

      sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);
    }

  /* Move back to the middle */

  for (n = 0, c = 4; n < sur->vinfo.xres/8; n++)
    {
      area.xpos++;
      sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);
      if (c++ == 4)
        {
          c = 0;
          if (colorkey == LTDC_EXAMPLE_NCOLORS)
            colorkey = LTDC_RED;
          else
            colorkey++;

          sur->layer->setcolorkey(sur->layer, 0xff000000 | g_rgb24[colorkey]);
        }

      sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);
    }

  /* Perform positioning test */

  _info("Perform positioning test\n");

  /* Set layer in the middle of the screen */

  area.xpos = sur->vinfo.xres/4;
  area.ypos = sur->vinfo.yres/4;
  area.xres = sur->vinfo.xres/2;
  area.yres = sur->vinfo.yres/2;

  sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);
  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);

  /* Move right */

  for (n = 0, c = 4; n < sur->vinfo.xres/8; n++)
    {
      area.xpos++;
      sur->layer->setarea(sur->layer, &area,
                            sur->vinfo.xres/4, sur->vinfo.yres/4);
      if (c++ == 4)
        {
          c = 0;
          if (colorkey == LTDC_EXAMPLE_NCOLORS)
            colorkey = LTDC_RED;
          else
            colorkey++;

          sur->layer->setcolorkey(sur->layer, 0xff000000 | g_rgb24[colorkey]);
        }

      sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);
    }

  /* Move down */

  for (n = 0, c = 4; n < sur->vinfo.yres/8; n++)
    {
      area.ypos++;
      sur->layer->setarea(sur->layer, &area,
                            sur->vinfo.xres/4, sur->vinfo.yres/4);
      if (c++ == 4)
        {
          c = 0;
          if (colorkey == LTDC_EXAMPLE_NCOLORS)
            colorkey = LTDC_RED;
          else
            colorkey++;

          sur->layer->setcolorkey(sur->layer, 0xff000000 | g_rgb24[colorkey]);
        }

      sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);
    }

  /* Move left */

  for (n = 0, c = 4; n < sur->vinfo.xres/4; n++)
    {
      area.xpos--;
      sur->layer->setarea(sur->layer, &area,
                            sur->vinfo.xres/4, sur->vinfo.yres/4);
      if (c++ == 4)
        {
          c = 0;
          if (colorkey == LTDC_EXAMPLE_NCOLORS)
            colorkey = LTDC_RED;
          else
            colorkey++;

          sur->layer->setcolorkey(sur->layer, 0xff000000 | g_rgb24[colorkey]);
        }

      sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);
    }

  /* Move up */

  for (n = 0, c = 4; n < sur->vinfo.yres/8; n++)
    {
      area.ypos--;
      sur->layer->setarea(sur->layer, &area,
                            sur->vinfo.xres/4, sur->vinfo.yres/4);
      if (c++ == 4)
        {
          c = 0;
          if (colorkey == LTDC_EXAMPLE_NCOLORS)
            colorkey = LTDC_RED;
          else
            colorkey++;

          sur->layer->setcolorkey(sur->layer, 0xff000000 | g_rgb24[colorkey]);
        }

      sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);
    }

  /* Move back to the middle */

  for (n = 0, c = 4; n < sur->vinfo.xres/8; n++)
    {
      area.xpos++;
      sur->layer->setarea(sur->layer, &area,
                            sur->vinfo.xres/4, sur->vinfo.yres/4);
      if (c++ == 4)
        {
          c = 0;
          if (colorkey == LTDC_EXAMPLE_NCOLORS)
            colorkey = LTDC_RED;
          else
            colorkey++;

          sur->layer->setcolorkey(sur->layer, 0xff000000 | g_rgb24[colorkey]);
        }

      sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);
    }

  /* Perform Reference position */

  _info("Perform reference positioning test\n");

  /* Set layer in the middle of the screen */

  area.xpos = sur->vinfo.xres/4;
  area.ypos = sur->vinfo.yres/4;
  area.xres = sur->vinfo.xres/2;
  area.yres = sur->vinfo.yres/2;

  sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);
  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);

  /* Move right */

  for (x = 0, c = 4; x < sur->vinfo.xres/8; x++)
    {
      sur->layer->setarea(sur->layer, &area,
                            sur->vinfo.xres/4 - x,
                            sur->vinfo.yres/4);
      if (c++ == 4)
        {
          c = 0;
          if (colorkey == LTDC_EXAMPLE_NCOLORS)
            colorkey = LTDC_RED;
          else
            colorkey++;

          sur->layer->setcolorkey(sur->layer, 0xff000000 | g_rgb24[colorkey]);
        }

      sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);
    }

  /* Move down */

  for (y = 0, c = 4; y < sur->vinfo.yres/8; y++)
    {
      sur->layer->setarea(sur->layer, &area,
                            sur->vinfo.xres/4 - x,
                            sur->vinfo.yres/4 - y);
      if (c++ == 4)
        {
          c = 0;
          if (colorkey == LTDC_EXAMPLE_NCOLORS)
            colorkey = LTDC_RED;
          else
            colorkey++;

          sur->layer->setcolorkey(sur->layer, 0xff000000 | g_rgb24[colorkey]);
        }

      sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);
    }

  /* Move left */

  for (x = 0, c = 4; x < sur->vinfo.xres/4; x++)
    {
      sur->layer->setarea(sur->layer, &area,
                            sur->vinfo.xres/4 - sur->vinfo.xres/8 + x,
                            sur->vinfo.yres/4 - y);
      if (c++ == 4)
        {
          c = 0;
          if (colorkey == LTDC_EXAMPLE_NCOLORS)
            colorkey = LTDC_RED;
          else
            colorkey++;

          sur->layer->setcolorkey(sur->layer, 0xff000000 | g_rgb24[colorkey]);
        }

      sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);
    }

  /* Move up */

  for (y = 0, c = 4; y < sur->vinfo.yres/8; y++)
    {
      sur->layer->setarea(sur->layer, &area,
                            sur->vinfo.xres/4 - sur->vinfo.xres/8 + x,
                            sur->vinfo.yres/4 - sur->vinfo.yres/8 + y);
      if (c++ == 4)
        {
          c = 0;
          if (colorkey == LTDC_EXAMPLE_NCOLORS)
            colorkey = LTDC_RED;
          else
            colorkey++;

          sur->layer->setcolorkey(sur->layer, 0xff000000 | g_rgb24[colorkey]);
        }

      sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);
    }

  /* Move back to the middle */

  for (x = 0, c = 4; x < sur->vinfo.xres/8; x++)
    {
      sur->layer->setarea(sur->layer, &area,
                            sur->vinfo.xres/4 + sur->vinfo.xres/8 - x,
                            sur->vinfo.yres/4 - sur->vinfo.yres/8 + y);
      if (c++ == 4)
        {
          c = 0;
          if (colorkey == LTDC_EXAMPLE_NCOLORS)
            colorkey = LTDC_RED;
          else
            colorkey++;

          sur->layer->setcolorkey(sur->layer, 0xff000000 | g_rgb24[colorkey]);
        }

      sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);
    }

  usleep(1000000);

  /* Restore original size */

  area.xpos = 0;
  area.ypos = 0;
  area.xres = sur->vinfo.xres;
  area.yres = sur->vinfo.yres;

  _info("Set area to xpos = %d, ypos = %d, xres = %d, yres = %d\n",
        area.xpos, area.ypos, area.xres, area.yres);

  sur->layer->setarea(sur->layer, &area, area.xpos, area.ypos);

  /* Disable colorkeying */

  sur->layer->setcolorkey(sur->layer, 0);
  sur->layer->setblendmode(sur->layer, LTDC_BLEND_NONE);

  _info("Update the layer to fullscreen\n");

  sur->layer->update(sur->layer, LTDC_SYNC_VBLANK);

  usleep(1000000);
}

#ifdef CONFIG_STM32_LTDC_L2
/****************************************************************************
 * Name: ltdc_alpha_blend_test
 *
 * Description:
 *   Perform layer blend test
 *
 ****************************************************************************/

static void ltdc_alpha_blend_test(void)
{
  int i;
  FAR struct surface *top;
  FAR struct surface *bottom;
  struct ltdc_area_s area;

  /* Ensure operation on layer 2 */

  _info("Set layer 2 to the active layer, blend with subjacent layer 1\n");

  top = ltdc_get_surface(LTDC_LAYER_TOP);
  bottom = ltdc_get_surface(LTDC_LAYER_BOTTOM);

  _info("top = %p, bottom = %p\n", top->pinfo.fbmem, bottom->pinfo.fbmem);

  ltdc_simple_draw(&top->vinfo, &top->pinfo);

  _info("Fill layer1 with color black\n");

  ltdc_drawcolor(&bottom->vinfo, bottom->pinfo.fbmem,
                  bottom->vinfo.xres, bottom->vinfo.yres,
                  ltdc_color(&bottom->vinfo, LTDC_BLACK));

  area.xpos = top->vinfo.xres/4;
  area.ypos = top->vinfo.yres/4;
  area.xres = top->vinfo.xres/2;
  area.yres = top->vinfo.yres/2;

  _info("Set area to xpos = %d, ypos = %d, xres = %d, yres = %d\n",
        area.xpos, area.ypos, area.xres, area.yres);

  top->layer->setarea(top->layer, &area, area.xpos, area.ypos);

  _info("Set alpha blending with bottom layer1\n");

  top->layer->setblendmode(top->layer, LTDC_BLEND_ALPHA);
  _info("Disable blending for bottom layer1 to make the layer color visible\n");

  bottom->layer->setblendmode(bottom->layer, LTDC_BLEND_NONE);
  bottom->layer->setalpha(bottom->layer, 0xff);

  _info("Fill bottom layer1 with color black\n");

  ltdc_drawcolor(&bottom->vinfo, bottom->pinfo.fbmem,
                  bottom->vinfo.xres, bottom->vinfo.yres,
                  ltdc_color(&bottom->vinfo, LTDC_BLACK));

  _info("Blend in black subjacent layer\n");

  for (i = 255; i >= 0; i--)
    {
      top->layer->setalpha(top->layer, i);
      top->layer->update(top->layer, LTDC_UPDATE_SIM|LTDC_SYNC_VBLANK);
    }

  _info("Fill bottom layer1 with color red\n");

  ltdc_drawcolor(&bottom->vinfo, bottom->pinfo.fbmem,
                  bottom->vinfo.xres, bottom->vinfo.yres,
                  ltdc_color(&bottom->vinfo, LTDC_RED));

  _info("Blend in red subjacent layer\n");

  for (i = 255; i >= 0; i--)
    {
      top->layer->setalpha(top->layer, i);
      top->layer->update(top->layer, LTDC_UPDATE_SIM|LTDC_SYNC_VBLANK);
    }

  _info("Fill bottom layer1 with color green\n");

  ltdc_drawcolor(&bottom->vinfo, bottom->pinfo.fbmem,
                  bottom->vinfo.xres, bottom->vinfo.yres,
                  ltdc_color(&bottom->vinfo, LTDC_GREEN));

  _info("Blend in green subjacent layer\n");

  for (i = 255; i >= 0; i--)
    {
      top->layer->setalpha(top->layer, i);
      top->layer->update(top->layer, LTDC_UPDATE_SIM|LTDC_SYNC_VBLANK);
    }

  _info("Fill bottom layer1 with color blue\n");

  ltdc_drawcolor(&bottom->vinfo, bottom->pinfo.fbmem,
                  bottom->vinfo.xres, bottom->vinfo.yres,
                  ltdc_color(&bottom->vinfo, LTDC_BLUE));

  _info("Blend in blue subjacent layer\n");

  for (i = 255; i >= 0; i--)
    {
      top->layer->setalpha(top->layer, i);
      top->layer->update(top->layer, LTDC_UPDATE_SIM|LTDC_SYNC_VBLANK);
    }

  _info("Fill bottom layer1 with color white\n");

  ltdc_drawcolor(&bottom->vinfo, bottom->pinfo.fbmem,
                  bottom->vinfo.xres, bottom->vinfo.yres,
                  ltdc_color(&bottom->vinfo, LTDC_WHITE));

  _info("Blend in white subjacent layer\n");

  for (i = 255; i >= 0; i--)
    {
      top->layer->setalpha(top->layer, i);
      top->layer->update(top->layer, LTDC_UPDATE_SIM|LTDC_SYNC_VBLANK);
    }

  /* Restore settings */

  area.xpos = 0;
  area.ypos = 0;
  area.xres = top->vinfo.xres;
  area.yres = top->vinfo.yres;

  top->layer->setarea(top->layer, &area, area.xpos, area.ypos);
  top->layer->setalpha(top->layer, 255);
  top->layer->setblendmode(top->layer, LTDC_BLEND_NONE);
  top->layer->update(top->layer, LTDC_UPDATE_SIM|LTDC_SYNC_VBLANK);
}

/****************************************************************************
 * Name: ltdc_flip_test
 *
 * Description:
 *   Perform layer flip test
 *
 ****************************************************************************/

static void ltdc_flip_test(void)
{
  FAR struct surface *active = ltdc_get_surface(LTDC_LAYER_ACTIVE);
  FAR struct surface *inactive = ltdc_get_surface(LTDC_LAYER_INACTIVE);

  /* Flip with non blend */

  _info("Perform flip test without blending\n");

  _info("active->pinfo.fbmem = %p\n", active->pinfo.fbmem);
  _info("inactive->pinfo.fbmem = %p\n", inactive->pinfo.fbmem);

  _info("Ensure that both layer opaque\n");
  active->layer->setalpha(active->layer, 0xff);
  inactive->layer->setalpha(inactive->layer, 0xff);
  active->layer->setblendmode(active->layer, LTDC_BLEND_NONE);
  inactive->layer->setblendmode(inactive->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));

  usleep(1000000);

  _info("Set invisible layer to fullscreen blue\n");
  ltdc_drawcolor(&inactive->vinfo, inactive->pinfo.fbmem,
                    inactive->vinfo.xres, inactive->vinfo.yres,
                    ltdc_color(&inactive->vinfo, LTDC_BLUE));

  usleep(1000000);

  _info("Flip layer to see the blue fullscreen\n");
  inactive->layer->update(inactive->layer,
                            LTDC_UPDATE_FLIP|LTDC_SYNC_VBLANK);

  usleep(1000000);

  /* Active layer is now inactive */

  _info("Set invisible layer to fullscreen green\n");
  ltdc_drawcolor(&active->vinfo, active->pinfo.fbmem,
                    active->vinfo.xres, active->vinfo.yres,
                    ltdc_color(&active->vinfo, LTDC_GREEN));

  usleep(1000000);

  _info("Flip layer to see the green fullscreen\n");
  inactive->layer->update(inactive->layer,
                            LTDC_UPDATE_FLIP|LTDC_SYNC_VBLANK);

  usleep(1000000);

  _info("Set invisible layer to fullscreen red\n");
  ltdc_drawcolor(&inactive->vinfo, inactive->pinfo.fbmem,
                inactive->vinfo.xres, inactive->vinfo.yres,
                ltdc_color(&inactive->vinfo, LTDC_RED));

  usleep(1000000);

  _info("Flip layer to see the red fullscreen\n");
  inactive->layer->update(inactive->layer, LTDC_UPDATE_FLIP|LTDC_SYNC_VBLANK);

  usleep(1000000);

  /* Flip with alpha blend */

  _info("Perform flip test with alpha blending\n");

  /* Set the bottom layer to the current active layer */

  active = ltdc_get_surface(LTDC_LAYER_BOTTOM);
  inactive = ltdc_get_surface(LTDC_LAYER_TOP);

  _info("Ensure that both layer fullscreen black\n");
  ltdc_drawcolor(&active->vinfo, active->pinfo.fbmem,
                    active->vinfo.xres, active->vinfo.yres,
                    ltdc_color(&active->vinfo, LTDC_BLACK));
  ltdc_drawcolor(&inactive->vinfo, inactive->pinfo.fbmem,
                    inactive->vinfo.xres, inactive->vinfo.yres,
                    ltdc_color(&inactive->vinfo, LTDC_BLACK));

  _info("Ensure that both layer semitransparent\n");
  active->layer->setalpha(active->layer, 0x7f);
  inactive->layer->setalpha(inactive->layer, 0x7f);
  active->layer->setblendmode(active->layer, LTDC_BLEND_ALPHA);
  inactive->layer->setblendmode(inactive->layer, LTDC_BLEND_ALPHA);

  _info("Enter in the flip mode sequence\n");
  _info("Set the bottom layer to the active layer\n");
  _info("Also update both layer simultaneous\n");
  active->layer->update(active->layer,LTDC_UPDATE_ACTIVATE|
                                      LTDC_UPDATE_SIM|
                                      LTDC_UPDATE_FLIP|
                                      LTDC_SYNC_VBLANK);

  usleep(1000000);

  _info("Set invisible layer to fullscreen blue\n");
  ltdc_drawcolor(&inactive->vinfo, inactive->pinfo.fbmem,
                    inactive->vinfo.xres, inactive->vinfo.yres,
                    ltdc_color(&inactive->vinfo, LTDC_BLUE));

  usleep(1000000);

  _info("Flip layer to see the blue fullscreen\n");
  inactive->layer->update(active->layer, LTDC_UPDATE_FLIP|LTDC_SYNC_VBLANK);

  usleep(1000000);

  /* Active layer is top now */

  _info("Set invisible layer to fullscreen green\n");
  ltdc_drawcolor(&active->vinfo, active->pinfo.fbmem,
                    active->vinfo.xres, active->vinfo.yres,
                    ltdc_color(&active->vinfo, LTDC_GREEN));

  usleep(1000000);

  _info("Flip layer to see the green fullscreen\n");
  inactive->layer->update(active->layer,
                            LTDC_UPDATE_FLIP|LTDC_SYNC_VBLANK);

  usleep(1000000);

  /* Active layer is bottom now */

  _info("Set invisible layer to fullscreen red\n");
  ltdc_drawcolor(&inactive->vinfo, inactive->pinfo.fbmem,
                    inactive->vinfo.xres, inactive->vinfo.yres,
                    ltdc_color(&inactive->vinfo, LTDC_RED));

  usleep(1000000);

  _info("Flip layer to see the red fullscreen\n");
  inactive->layer->update(active->layer, LTDC_UPDATE_FLIP|LTDC_SYNC_VBLANK);

  usleep(1000000);

  /* Active layer is top now */

  _info("Set bottom layer back to fullscreen black\n");
  ltdc_drawcolor(&active->vinfo, active->pinfo.fbmem,
                    active->vinfo.xres, active->vinfo.yres,
                    ltdc_color(&active->vinfo, LTDC_BLACK));

  _info("Set bottom layer to alpha %d and disable blend mode\n", 0xff);
  inactive->layer->setalpha(active->layer, 0xff);
  inactive->layer->setblendmode(active->layer, LTDC_BLEND_NONE);

  usleep(1000000);

  _info("Flip layer to see the black fullscreen\n");
  inactive->layer->update(active->layer,
                            LTDC_UPDATE_FLIP|LTDC_SYNC_VBLANK);

  /* Active layer is bottom now */

  usleep(1000000);

  /* Disable flip sequence. Restore layers with there current settings and
   * activate them.
   */

  /* Restore settings  */

  _info("Finally set the top layer back to fullscreen black\n");
  ltdc_drawcolor(&inactive->vinfo, inactive->pinfo.fbmem,
                    inactive->vinfo.xres, inactive->vinfo.yres,
                    ltdc_color(&inactive->vinfo, LTDC_BLACK));

  _info("Set top layer to alpha %d and disable blend mode\n", 0xff);
  inactive->layer->setalpha(inactive->layer, 0xff);
  inactive->layer->setblendmode(inactive->layer, LTDC_BLEND_NONE);

  _info("Flip to the top layer\n");
  inactive->layer->update(inactive->layer,
                            LTDC_UPDATE_ACTIVATE|LTDC_SYNC_VBLANK);

}
#endif /* CONFIG_STM32_LTDC_L2 */
#endif /* CONFIG_STM32_LTDC_INTERFACE */

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: ltdc_clrcolor
 *
 * Description:
 *   Fills the color value table with an specific color.
 *   This works like memset but for 8-bit aligned memory.
 *
 * Parameter:
 *   color - Pointer to the color value table
 *   value - The color to set
 *   size  - the size of the color value table
 *
 ****************************************************************************/

void ltdc_clrcolor(uint8_t *color, uint8_t value, size_t size)
{
  while (size--)
    {
      *color++ = value;
    }
}

/****************************************************************************
 * Name: ltdc_cmpcolor
 *
 * Description:
 *   Compares two color value tables
 *
 * Parameter:
 *   color1 - color value table 1
 *   color2 - color value table 2
 *   size   - the size of the color value table
 *
 * Return:
 *   0 - if equal otherwise unequal to 0
 *
 ****************************************************************************/

int ltdc_cmpcolor(uint8_t *color1, uint8_t *color2, size_t size)
{
  int n;

  for (n = 0; !n && size; --size)
    {
      n = *color1++ - *color2++;
    }

  return n;
}

/****************************************************************************
 * Name: ltdc_init_cmap
 *
 * Description:
 *   Initialize the color lookup table
 *
 ****************************************************************************/

#ifdef CONFIG_FB_CMAP
void ltdc_init_cmap(void)
{
  memset(&g_color, 0, sizeof(g_color));

  /* CLUT color format definition
   *
   * Position value
   * 00       0x000000    black
   * 01       0xff0000    red
   * 02       0x00ff00    green
   * 03       0x0000ff    blue
   * 04       0xffffff    white
   *
   */
  g_cmap.red[1]    = 0xff;
  g_cmap.green[2]  = 0xff;
  g_cmap.blue[3]   = 0xff;
  g_cmap.red[4]    = 0xff;
  g_cmap.green[4]  = 0xff;
  g_cmap.blue[4]   = 0xff;
#ifdef CONFIG_FB_TRANSPARENCY
  g_cmap.transp[0] = 0xff;
  g_cmap.transp[1] = 0xff;
  g_cmap.transp[2] = 0xff;
  g_cmap.transp[3] = 0xff;
  g_cmap.transp[4] = 0xff;
#endif
}


/****************************************************************************
 * Name: ltdc_createcmap
 *
 * Description:
 *   Initialize
 *
 ****************************************************************************/

FAR struct fb_cmap_s * ltdc_createcmap(uint16_t ncolors)
{
  FAR struct fb_cmap_s *cmap = malloc(sizeof(struct fb_cmap_s));

  if (cmap)
    {
#ifdef CONFIG_FB_TRANSPARENCY
      uint8_t *clut = malloc(4 * ncolors * sizeof(uint8_t));
#else
      uint8_t *clut = malloc(3 * ncolors * sizeof(uint8_t));
#endif

      if (!clut)
        {
          _err("ERROR: malloc() failed\n");
          free(cmap);
          return NULL;;
        }

#ifdef CONFIG_FB_TRANSPARENCY
      cmap->transp = &clut[0];
      cmap->red = &clut[1 * ncolors * sizeof(uint8_t)];
      cmap->green = &clut[2 * ncolors * sizeof(uint8_t)];
      cmap->blue = &clut[3 * ncolors * sizeof(uint8_t)];
#else
      cmap->red = &clut[0];
      cmap->green = &clut[1 * ncolors * sizeof(uint8_t)];
      cmap->blue = &clut[2 * ncolors * sizeof(uint8_t)];
#endif
      cmap->first = 0;
      cmap->len   = ncolors;

    }

  return cmap;
}


/****************************************************************************
 * Name: ltdc_deletecmap
 *
 * Description:
 *   Initialize
 *
 ****************************************************************************/

void ltdc_deletecmap(FAR struct fb_cmap_s *cmap)
{
  if (cmap)
    {
#ifdef CONFIG_FB_TRANSPARENCY
      free(cmap->transp);
#else
      free(cmap->red);
#endif
      free(cmap);
    }
}
#endif

/****************************************************************************
 * Name: ltdc_color
 *
 * Description:
 *   Get the correct color value to the pixel format
 *
 ****************************************************************************/

uint32_t ltdc_color(FAR struct fb_videoinfo_s *vinfo, uint8_t color)
{
  uint32_t value;

  switch (vinfo->fmt)
    {
#if defined(CONFIG_STM32_LTDC_L1_L8) || defined(CONFIG_STM32_LTDC_L2_L8)
        case FB_FMT_RGB8:
          value = color;
          break;
#endif
#if defined(CONFIG_STM32_LTDC_L1_RGB565) || defined(CONFIG_STM32_LTDC_L2_RGB565)
        case FB_FMT_RGB16_565:
          value = g_rgb16[color];
          break;
#endif
#if defined(CONFIG_STM32_LTDC_L1_RGB888) || \
        defined(CONFIG_STM32_LTDC_L2_RGB888)
        case FB_FMT_RGB24:
          value = g_rgb24[color];
          break;
#endif
        default:
          _err("ERROR: Unsupported pixel format %d\n", vinfo->fmt);
          value = 0;
          break;
    }

  return value;
}

/****************************************************************************
 * Name: ltdc_simple_draw
 *
 * Description:
 *   Draw four different colored rectangles on the whole screen
 *
 ****************************************************************************/

void ltdc_simple_draw(FAR struct fb_videoinfo_s *vinfo,
                                FAR struct fb_planeinfo_s *pinfo)
{
  volatile int x, y;
  uint16_t xres = vinfo->xres;
  uint16_t yres = vinfo->yres;

  _info("draw a red and green rectangle in the upper half\n");
  _info("draw a white and blue rectangle in the lower half\n");

#if defined(CONFIG_STM32_LTDC_L1_L8) || defined(CONFIG_STM32_LTDC_L2_L8)
  if (vinfo->fmt == FB_FMT_RGB8)
    {
      uint8_t color;
      uint8_t *buf = (uint8_t *)pinfo->fbmem;

      for (y = 0; y < yres/2; y++)
        {
          color = ltdc_color(vinfo, LTDC_RED);

          for (x = 0; x < xres/2; x++)
            {
              *buf++ = color;
            }

          color = ltdc_color(vinfo, LTDC_GREEN);

          for (x = 0; x < xres/2; x++)
            {
              *buf++ = color;
            }
        }

      for (y = 0; y < yres/2; y++)
        {
          color = ltdc_color(vinfo, LTDC_WHITE);

          for (x = 0; x < xres/2; x++)
            {
              *buf++ = color;
            }

          color = ltdc_color(vinfo, LTDC_BLUE);

          for (x = 0; x < xres/2; x++)
            {
              *buf++ = color;
            }
        }
    }
#endif

#if defined(CONFIG_STM32_LTDC_L1_RGB565) || defined(CONFIG_STM32_LTDC_L2_RGB565)
  if(vinfo->fmt == FB_FMT_RGB16_565)
    {
      uint16_t color;
      uint16_t *buf = (uint16_t *)pinfo->fbmem;

      for (y = 0; y < yres/2; y++)
        {
          color = ltdc_color(vinfo, LTDC_RED);

          for (x = 0; x < xres/2; x++)
            {
              *buf++ = color;
            }

          color = ltdc_color(vinfo, LTDC_GREEN);

          for (x = 0; x < xres/2; x++)
            {
              *buf++ = color;
            }
        }

      for (y = 0; y < yres/2; y++)
        {
          color = ltdc_color(vinfo, LTDC_WHITE);

          for (x = 0; x < xres/2; x++)
            {
              *buf++ = color;
            }

          color = ltdc_color(vinfo, LTDC_BLUE);

          for (x = 0; x < xres/2; x++)
            {
              *buf++ = color;
            }
        }
    }
#endif

#if defined(CONFIG_STM32_LTDC_L1_RGB888) || defined(CONFIG_STM32_LTDC_L2_RGB888)
  if(vinfo->fmt == FB_FMT_RGB24)
    {
      uint32_t color;
      uint8_t *buf = (uint8_t *)pinfo->fbmem;

      for (y = 0; y < yres/2; y++)
        {
          color = ltdc_color(vinfo, LTDC_RED);

          for (x = 0; x < xres/2; x++)
            {
              *buf++ = color;
              *buf++ = (color >> 8);
              *buf++ = (color >> 16);
            }

          color = ltdc_color(vinfo, LTDC_GREEN);

          for (x = 0; x < xres/2; x++)
            {
              *buf++ = color;
              *buf++ = (color >> 8);
              *buf++ = (color >> 16);
            }
        }

      for (y = 0; y < yres/2; y++)
        {
          color = ltdc_color(vinfo, LTDC_WHITE);

          for (x = 0; x < xres/2; x++)
            {
              *buf++ = color;
              *buf++ = (color >> 8);
              *buf++ = (color >> 16);
            }

          color = ltdc_color(vinfo, LTDC_BLUE);

          for (x = 0; x < xres/2; x++)
            {
              *buf++ = color;
              *buf++ = (color >> 8);
              *buf++ = (color >> 16);
            }
        }
    }
#endif
}

#ifdef CONFIG_STM32_LTDC_L2
/****************************************************************************
 * Name: ltdc_drawcolor
 *
 * Description:
 *   Draw a specific color to the framebuffer
 *
 ****************************************************************************/

void ltdc_drawcolor(FAR struct fb_videoinfo_s *vinfo, void *buffer,
                           uint16_t xres, uint16_t yres, uint32_t color)
{
  int x,y;

  /* draw a blue rectangle */

  _info("draw a full screen rectangle with color %08x\n", color);

#if defined(CONFIG_STM32_LTDC_L1_L8) || defined(CONFIG_STM32_LTDC_L2_L8)
  if (vinfo->fmt == FB_FMT_RGB8)
    {
      uint8_t *buf = (uint8_t *)buffer;

      for (y = 0; y < yres; y++)
        {
          for (x = 0; x < xres; x++)
            {
              *buf++ = color;
            }
        }
    }
#endif

#if defined(CONFIG_STM32_LTDC_L1_RGB565) || defined(CONFIG_STM32_LTDC_L2_RGB565)
  if (vinfo->fmt == FB_FMT_RGB16_565)
    {
      uint16_t *buf = (uint16_t *)buffer;

      for (y = 0; y < yres; y++)
        {
          for (x = 0; x < xres; x++)
            {
              *buf++ = color;
            }
        }
    }
#endif

#if defined(CONFIG_STM32_LTDC_L1_RGB888) || defined(CONFIG_STM32_LTDC_L2_RGB888)
  if (vinfo->fmt == FB_FMT_RGB24)
    {
      uint8_t *buf = (uint8_t *)buffer;
      uint8_t r;
      uint8_t g;
      uint8_t b;

      r = (uint8_t)(color >> 16);
      g = (uint8_t)(color >> 8);
      b = (uint8_t)color;

      for (y = 0; y < yres; y++)
        {
          for (x = 0; x < xres; x++)
            {
              *buf++ = b;
              *buf++ = g;
              *buf++ = r;
            }
        }
    }
#endif
}
#endif

#ifdef CONFIG_STM32_LTDC_INTERFACE
/****************************************************************************
 * Name: ltdc_get_surface
 *
 * Description:
 *   Get a reference to a specific layer
 *
 ****************************************************************************/

struct surface * ltdc_get_surface(uint32_t mode)
{
  int ret;
  int lid;
  FAR struct surface *sur = &g_surface[0];

  ret = sur->layer->getlid(sur->layer, &lid, mode);

  if (ret != OK)
    {
      _err("ERROR: getlid() failed\n");
      _exit(1);
    }

  if (lid < 0 || lid > 1)
    {
      _err("ERROR: invalid layer id %d\n", lid);
      _exit(1);
    }

  return &g_surface[lid];
}
#endif


/****************************************************************************
 * ltdc_main
 ****************************************************************************/

int ltdc_main(int argc, char *argv[])
{
  FAR struct fb_planeinfo_s pinfo;
  FAR struct fb_videoinfo_s vinfo;
  FAR struct fb_vtable_s *fbtable;

  if (up_fbinitialize(0) < 0)
    {
      _err("ERROR: up_fbinitialize() failed\n");
      return -1;
    }

  fbtable = up_fbgetvplane(0,0);

  if (!fbtable)
    {
      _err("ERROR: up_fbgetvplane() failed\n");
      return -1;
    }

  if (fbtable->getvideoinfo(fbtable, &vinfo)<0)
    {
      _err("ERROR: getvideoinfo failed\n");
      return -1;
    }

  if (fbtable->getplaneinfo(fbtable, 0, &pinfo)<0)
    {
      _err("ERROR: getplaneinfo failed\n");
      return -1;
    }

  _info("fb is configured with: xres = %d, yres = %d, \
          fb start address = %p, fb size = %d, fmt = %d, bpp = %d\n",
          vinfo.xres, vinfo.yres, pinfo.fbmem, pinfo.fblen,
          vinfo.fmt, pinfo.bpp);

#ifdef CONFIG_FB_CMAP
  ltdc_init_cmap();

  if (vinfo.fmt == FB_FMT_RGB8)
    {
      if (fbtable->putcmap(fbtable, &g_cmap) != OK)
        {
          _err("ERROR: putcmap() failed\n");
          return -1;
        }
    }
#endif

  /* Tests */

  ltdc_simple_draw(&vinfo, &pinfo);

  usleep(1000000);

#ifdef CONFIG_STM32_LTDC_INTERFACE
  ltdc_init_surface(0, LTDC_LAYER_ACTIVE);
#ifdef CONFIG_STM32_LTDC_L2
  ltdc_init_surface(1, LTDC_LAYER_INACTIVE);
#endif
  usleep(1000000);

  ltdc_setget_test();

  usleep(1000000);

  ltdc_area_test();

  usleep(1000000);

  ltdc_color_test();

  usleep(1000000);

  ltdc_colorkey_test();

  usleep(1000000);

  ltdc_common_test();

#ifdef CONFIG_STM32_LTDC_L2

  usleep(1000000);

  ltdc_alpha_blend_test();

  usleep(1000000);

  ltdc_flip_test();

#ifdef CONFIG_STM32_DMA2D
  ltdc_dma2d_main();
#endif

#endif /* CONFIG_STM32_LTDC_L2 */
#endif /* CONFIG_STM32_LTDC_INTERFACE */
  return 0;
}
