/****************************************************************************
 * examples/ltdc/ltdc_main.c
 *
 *   Copyright (C) 2008, 2011-2012 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <gnutt@nuttx.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>

#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <debug.h>

#include <nuttx/video/rgbcolors.h>
#include <nuttx/video/fb.h>

#include <arch/chip/ltdc.h>

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

#define LTDC_EXAMPLE_NCOLORS    5

#ifdef CONFIG_STM32_LTDC_INTERFACE
struct surface
{
  struct fb_videoinfo_s vinfo;
  struct fb_planeinfo_s pinfo;
  FAR struct ltdc_layer_s *layer;
};
#endif

enum example_colors
{
  LTDC_BLACK,
  LTDC_RED,
  LTDC_GREEN,
  LTDC_BLUE,
  LTDC_WHITE
};

static const uint32_t g_rgb24[LTDC_EXAMPLE_NCOLORS] =
{
  RGB24_BLACK,
  RGB24_RED,
  RGB24_GREEN,
  RGB24_BLUE,
  RGB24_WHITE,
};

static const uint32_t g_rgb16[LTDC_EXAMPLE_NCOLORS] =
{
  RGB16_BLACK,
  RGB16_RED,
  RGB16_GREEN,
  RGB16_BLUE,
  RGB16_WHITE,
};

/****************************************************************************
 * 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
static uint8_t g_color[3*LTDC_EXAMPLE_NCOLORS];

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]
};
#endif

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

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

#ifdef CONFIG_FB_CMAP
static 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;
}
#endif

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

static 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:
          dbg("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
 *
 ***************************************************************************/

static 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;

  dbg("draw a red and green rectangle in the upper half\n");
  dbg("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
 *
 *****************************************************************************/

static 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 */

  dbg("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
 *
 *****************************************************************************/

static 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)
    {
      dbg("getlid() failed\n");
      _exit(1);
    }

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

  return &g_surface[lid];
}

/******************************************************************************
 * 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)
    {
      dbg("up_ltdcgetlayer() failed\n");
      return -1;
    }

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

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

  dbg("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
  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);

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

  /* setalpha */

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

  if (ret != OK)
    {
      dbg("setalpha() failed\n");
    }

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

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

  /* setcolor */

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

  if (ret != OK)
    {
      dbg("setcolor() failed\n");
    }

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

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

  /* setcolorkey */

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

  if (ret != OK)
    {
      dbg("setcolorkey() failed\n");
    }

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

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

  /* setblendmode */

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

  if (ret != OK)
    {
      dbg("setblendmode() failed\n");
    }

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

  if (ret != OK || mode != LTDC_BLEND_NONE)
    {
      dbg("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)
    {
      dbg("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)
    {
      dbg("getarea() failed\n");
    }

#ifdef CONFIG_FB_CMAP
  if (sur->vinfo.fmt == FB_FMT_RGB8)
    {
      ret = sur->layer->setclut(sur->layer, &g_cmap);

      if (ret != OK)
        {
          dbg("setclut() failed\n");
        }

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

      if (ret != OK)
        {
          dbg("getclut() 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 */

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

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

  dbg("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 */

  dbg("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);

  dbg("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 */

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

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

  dbg("Update the layer\n");

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

  usleep(1000000);

  /* Default Color green */

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

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

  dbg("Update the layer\n");

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

  usleep(1000000);

  /* Default Color blue */

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

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

  dbg("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;

  dbg("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 */

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

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

  dbg("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 */

  dbg("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 */

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

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

  dbg("Update the layer\n");

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

  usleep(1000000);

  /* Color key red */

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

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

  dbg("Update the layer\n");

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

  usleep(1000000);

  /* Color key green */

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

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

  dbg("Update the layer\n");

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

  usleep(1000000);

  /* Color key red */

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

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

  dbg("Update the layer\n");

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

  dbg("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;

  dbg("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);

  dbg("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);

  dbg("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;

  dbg("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);

  dbg("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;

  dbg("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);

  dbg("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;

  dbg("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);

  dbg("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;

  dbg("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);

  dbg("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 */

  dbg("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 */

  dbg("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 */

  dbg("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;

  dbg("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);

  dbg("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;

  dbg("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 */

  dbg("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 */

  dbg("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 */

  dbg("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;

  dbg("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);

  dbg("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 */

  dbg("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);

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

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

  dbg("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;

  dbg("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);

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

  top->layer->setblendmode(top->layer, LTDC_BLEND_ALPHA);
  dbg("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);

  dbg("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));

  dbg("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);
    }

  dbg("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));

  dbg("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);
    }

  dbg("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));

  dbg("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);
    }

  dbg("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));

  dbg("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);
    }

  dbg("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));

  dbg("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 */

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

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

  dbg("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);

  dbg("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);

  dbg("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);

  dbg("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 */

  dbg("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);

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

  usleep(1000000);

  dbg("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);

  dbg("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 */

  dbg("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);

  dbg("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));

  dbg("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);

  dbg("Enter in the flip mode sequence\n");
  dbg("Set the bottom layer to the active layer\n");
  dbg("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);

  dbg("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);

  dbg("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 */

  dbg("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);

  dbg("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 */

  dbg("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);

  dbg("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 */

  dbg("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));

  dbg("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);

  dbg("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  */

  dbg("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));

  dbg("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);

  dbg("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
 ****************************************************************************/

/****************************************************************************
 * 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)
    {
      dbg("up_fbinitialize() failed\n");
      return -1;
    }

  fbtable = up_fbgetvplane(0);

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

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

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

  dbg("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)
        {
          dbg("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();

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