/****************************************************************************
 * apps/examples/fboverlay/fboverlay_main.c
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 ****************************************************************************/

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

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <errno.h>
#include <unistd.h>

#include <nuttx/video/fb.h>

#include <nuttx/config.h>

/****************************************************************************
 * Preprocessor Definitions
 ****************************************************************************/

/****************************************************************************
 * Private Types
 ****************************************************************************/

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

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Name: rgb8888
 *
 * Description:
 *   Color conversion from argb8888 to rgb color channel
 *
 * Parameters:
 *   argb - argb8888 color
 *   a    - Reference to 8-bit alpha channel
 *   r    - Reference to 8-bit red channel
 *   g    - Reference to 8-bit green channel
 *   b    - Reference to 8-bit blue channel
 *
 ****************************************************************************/

#ifdef CONFIG_FB_CMAP
static void rgb8888(uint32_t argb, uint8_t * a, uint8_t * r, uint8_t * g,
                    uint8_t * b)
{
  *a = (uint8_t)(argb >> 24);
  *r = (uint8_t)(argb >> 16);
  *g = (uint8_t)(argb >> 8);
  *b = (uint8_t)argb;
}
#endif

/****************************************************************************
 * Name: rgb565
 *
 * Description:
 *   Color conversion from argb8888 to rgb565
 *
 * Parameters:
 *   argb - argb8888 color
 *
 ****************************************************************************/

#ifdef CONFIG_FB_FMT_RGB16_565
static uint16_t rgb565(uint32_t argb)
{
  uint16_t r = (argb >> 8) & 0xf800;
  uint16_t g = (argb >> 5) & 0x7e0;
  uint16_t b = (argb >> 3) & 0x1f;

  return (r | g | b);
}
#endif

/****************************************************************************
 * Name: draw_rect8
 *
 * Description:
 *   Draw a rectangle for 8 bit color mode
 *
 * Parameters:
 *   fbmem - Points to framebuffer start address
 *   oinfo - Reference to overlay information
 *   area  - Area draw
 *   color - cmap color entry
 *
 ****************************************************************************/

#ifdef CONFIG_FB_CMAP
static void draw_rect8(FAR void *fbmem, FAR struct fb_overlayinfo_s *oinfo,
                       FAR const struct fb_area_s *area, uint8_t color)
{
  FAR uint8_t *dest;
  FAR uint8_t *row;
  int x;
  int y;

  printf("Fill area (%d,%d,%d,%d) with color: %08x\n" , area->x, area->y,
          area->w, area->h, color);

  row = (FAR uint8_t *)fbmem + oinfo->stride * area->y;
  for (y = 0; y < area->h; y++)
    {
      dest = row + area->x;
      for (x = 0; x < area->w; x++)
        {
          *dest++ = color;
        }

      row += oinfo->stride;
    }
}
#endif

/****************************************************************************
 * Name: draw_rect16
 *
 * Description:
 *   Draw a rectangle for 16 bit color mode
 *
 * Parameters:
 *   fbmem - Points to framebuffer start address
 *   oinfo - Reference to overlay information
 *   area  - Area draw
 *   rgb   - rgb565 color
 *
 ****************************************************************************/

static void draw_rect16(FAR void *fbmem, FAR struct fb_overlayinfo_s *oinfo,
                        FAR const struct fb_area_s *area, uint16_t rgb)
{
  FAR uint16_t *dest;
  FAR uint8_t *row;
  int x;
  int y;

  printf("Fill area (%d,%d,%d,%d) with color: %04x\n", area->x, area->y,
          area->w, area->h, rgb);

  row = (FAR uint8_t *)fbmem + oinfo->stride * area->y;
  for (y = 0; y < area->h; y++)
    {
      dest = ((FAR uint16_t *)row) + area->x;
      for (x = 0; x < area->w; x++)
        {
          *dest++ = rgb;
        }

      row += oinfo->stride;
    }
}

/****************************************************************************
 * Name: draw_rect24
 *
 * Description:
 *   Draw a rectangle for 24 bit color mode
 *
 * Parameters:
 *   fbmem - Points to framebuffer start address
 *   oinfo - Reference to overlay information
 *   area  - Area draw
 *   rgb   - rgb888 color
 *
 ****************************************************************************/

static void draw_rect24(FAR void *fbmem, FAR struct fb_overlayinfo_s *oinfo,
                        FAR const struct fb_area_s *area, uint32_t rgb)
{
  int         x;
  int         y;
  uint8_t     r;
  uint8_t     g;
  uint8_t     b;
  FAR uint8_t *dest;
  FAR uint8_t *row;

  r = rgb;
  g = (rgb >> 8);
  b = (rgb >> 16);

  printf("Fill area (%d,%d,%d,%d) with color: %08" PRIx32 " -> (r,g,b) = "
         "(%02x,%02x,%02x)\n", area->x, area->y, area->w, area->h,
         rgb, b, g, r);

  row = (FAR uint8_t *)fbmem + oinfo->stride * area->y;
  for (y = 0; y < area->h; y++)
    {
      dest = row + area->x * 3;
      for (x = 0; x < area->w; x++)
        {
          *dest++ = r;
          *dest++ = g;
          *dest++ = b;
        }

      row += oinfo->stride;
    }
}

/****************************************************************************
 * Name: draw_rect32
 *
 * Description:
 *   Draw a rectangle for 32 bit color mode
 *
 * Parameters:
 *   fbmem - Points to framebuffer start address
 *   oinfo - Reference to overlay information
 *   area  - Area draw
 *   argb  - argb8888 color
 *
 ****************************************************************************/

static void draw_rect32(FAR void *fbmem, FAR struct fb_overlayinfo_s *oinfo,
                        FAR const struct fb_area_s *area, uint32_t argb)
{
  int          x;
  int          y;
  FAR uint32_t *dest;
  FAR uint8_t  *row;

  printf("Fill area (%d,%d,%d,%d) with color: %08" PRIx32 " -> (a,r,g,b) = "
         "(%02" PRIx32 ",%02" PRIx32 ",%02" PRIx32 ",%02" PRIx32 ")\n",
         area->x, area->y, area->w, area->h,
         argb, argb >> 24, argb >> 16, argb >> 8, argb);

  row = (FAR uint8_t *)fbmem + oinfo->stride * area->y;
  for (y = 0; y < area->h; y++)
    {
      dest = ((FAR uint32_t *)row) + area->x;
      for (x = 0; x < area->w; x++)
        {
          *dest++ = argb;
        }

      row += oinfo->stride;
    }
}

/****************************************************************************
 * Name: video_cmap
 *
 * Description:
 *   Sets cmap
 *
 * Parameters:
 *   fb        - Open framebuffer filehandle
 *   cmap      - Readonly reference to color map
 *
 ****************************************************************************/

#ifdef CONFIG_FB_CMAP
static int video_putcmap(int fb, FAR const struct fb_cmap_s *cmap)
{
  int n;
  int ret;

  printf("Set cmap\n");
  for (n = cmap->first; n < cmap->len; n++)
    {
      printf("  Color %d: (a,r,g,b) = (%02x,%02x,%02x,%02x)\n", n,
#  ifdef CONFIG_CMAP_TRANSPARENCY
             cmap->transp[n],
#  else
             (uint8_t)0xff,
#  endif
             cmap->red[n],
             cmap->green[n],
             cmap->blue[n]);
    }

  ret = ioctl(fb, FBIOPUT_CMAP, (unsigned long)(uintptr_t)cmap);
  if (ret != OK)
    {
      fprintf(stderr, "Unable to set camp\n");
    }

  return ret;
}

/****************************************************************************
 * Name: video_getcmap
 *
 * Description:
 *   Sets cmap
 *
 * Parameters:
 *   fb        - Open framebuffer filehandle
 *   cmap      - Reference to color map
 *
 ****************************************************************************/

static int video_getcmap(int fb, FAR struct fb_cmap_s *cmap)
{
  int n;
  int ret;

  ret = ioctl(fb, FBIOGET_CMAP, (unsigned long)(uintptr_t)cmap);
  if (ret != OK)
    {
      fprintf(stderr, "Unable to set camp\n");
    }

  printf("Get cmap\n");
  for (n = cmap->first; n < cmap->len; n++)
    {
      printf("  Color %d: (a,r,g,b) = (%02x,%02x,%02x,%02x)\n", n,
#  ifdef CONFIG_CMAP_TRANSPARENCY
             cmap->transp[n],
#  else
             (uint8_t)0xff,
#  endif
             cmap->red[n],
             cmap->green[n],
             cmap->blue[n]);
    }

  return ret;
}
#endif /* CONFIG_FB_CMAP */

/****************************************************************************
 * Name: print_video_info
 *
 * Description:
 *   Prints video information
 *
 * Parameters:
 *   fb        - Open framebuffer filehandle
 *
 ****************************************************************************/

static void print_video_info(int fb)
{
  int ret;
  struct fb_videoinfo_s vinfo;

  ret = ioctl(fb, FBIOGET_VIDEOINFO, (unsigned long)(uintptr_t)&vinfo);
  if (ret != OK)
    {
      fprintf(stderr, "Unable to get video information\n");
      return;
    }

  printf("VideoInfo:\n"
         "      fmt: %u\n"
         "     xres: %u\n"
         "     yres: %u\n"
         "  nplanes: %u\n"
         "noverlays: %u\n",
         vinfo.fmt, vinfo.xres, vinfo.yres, vinfo.nplanes, vinfo.noverlays);
}

/****************************************************************************
 * Name: print_plane_info
 *
 * Description:
 *   Prints plane information
 *
 * Parameters:
 *   fb - Open framebuffer filehandle
 *
 ****************************************************************************/

static void print_plane_info(int fb)
{
  int ret;
  FAR void *fbmem;
  struct fb_planeinfo_s pinfo;

  ret = ioctl(fb, FBIOGET_PLANEINFO, (unsigned long)(uintptr_t)&pinfo);
  if (ret != OK)
    {
      fprintf(stderr, "Unable to get plane information\n");
      return;
    }

  fbmem = mmap(NULL, pinfo.fblen, PROT_READ | PROT_WRITE,
               MAP_SHARED | MAP_FILE, fb, 0);

  if (fbmem == MAP_FAILED)
    {
      fprintf(stderr, "ERROR: ioctl(FBIO_MMAP) failed: %d\n", errno);
      return;
    }

  printf("PlaneInfo:\n"
         "    fbmem: %p\n"
         "    fblen: %zu\n"
         "   stride: %u\n"
         "  display: %u\n"
         "      bpp: %u\n"
         "    fbmem: %p\n",
         pinfo.fbmem, pinfo.fblen, pinfo.stride,
         pinfo.display,
         pinfo.bpp, fbmem);
}

/****************************************************************************
 * Name: print_overlay_info
 *
 * Description:
 *   Prints overlay information
 *
 * Parameters:
 *   fb        - Open framebuffer filehandle
 *   overlayno - Overlay number
 *
 ****************************************************************************/

static void print_overlay_info(int fb, uint8_t overlayno)
{
  int ret;
  FAR void *fbmem;
  struct fb_overlayinfo_s oinfo;

  /* Select overlay to compare fbmem from overlayinfo and the one by mmap */

  ret = ioctl(fb, FBIO_SELECT_OVERLAY, overlayno);
  if (ret != OK)
    {
      fprintf(stderr, "Unable to select overlay: %d\n", overlayno);
      return;
    }

  oinfo.overlay = overlayno;

  ret = ioctl(fb, FBIOGET_OVERLAYINFO, (unsigned long)(uintptr_t)&oinfo);
  if (ret != OK)
    {
      fprintf(stderr, "Unable to get overlay information\n");
      return;
    }

  fbmem = mmap(NULL, oinfo.fblen, PROT_READ | PROT_WRITE,
               MAP_SHARED | MAP_FILE, fb, 0);

  if (fbmem == MAP_FAILED)
    {
      fprintf(stderr, "ERROR: ioctl(FBIO_MMAP) failed: %d\n", errno);
      return;
    }

  printf("OverlayInfo:\n"
         "    fbmem: %p\n"
         "    fblen: %zu\n"
         "   stride: %u\n"
         "  overlay: %u\n"
         "      bpp: %u\n"
         "    blank: %08x\n"
         "chromakey: %08" PRIx32 "\n"
         "    color: %08" PRIx32 "\n"
         "   transp: %02x\n"
         "     mode: %08x\n"
         "     accl: %08" PRIx32 "\n"
         "     mmap: %p\n",
         oinfo.fbmem, oinfo.fblen, oinfo.stride,
         oinfo.overlay,
         oinfo.bpp, oinfo.blank, oinfo.chromakey, oinfo.color,
         oinfo.transp.transp, oinfo.transp.transp_mode, oinfo.accl, fbmem);
}

/****************************************************************************
 * Name: overlay_fill
 *
 * Description:
 *   Fill the overlay area with a user defined color
 *
 * Parameters:
 *   fb        - Open framebuffer filehandle
 *   overlayno - Overlay number
 *   color      - color
 *
 ****************************************************************************/

static int overlay_fill(int fb, uint8_t overlayno, uint32_t color,
                        FAR const struct fb_area_s *area)
{
  int ret;
  FAR void *fbmem;
  struct fb_videoinfo_s vinfo;
  struct fb_overlayinfo_s oinfo;

  ret = ioctl(fb, FBIOGET_VIDEOINFO, (unsigned long)((uintptr_t)&vinfo));
  if (ret < 0)
    {
      fprintf(stderr, "Unable to get video info\n");
      return -1;
    }

  oinfo.overlay = overlayno;

  ret = ioctl(fb, FBIOGET_OVERLAYINFO, (unsigned long)((uintptr_t)&oinfo));
  if (ret < 0)
    {
      fprintf(stderr, "Unable to get overlay info\n");
      return -1;
    }

  ret = ioctl(fb, FBIO_SELECT_OVERLAY, overlayno);
  if (ret != OK)
    {
      fprintf(stderr, "Unable to select overlay: %d\n", overlayno);
      return -1;
    }

  fbmem = mmap(NULL, oinfo.fblen, PROT_READ | PROT_WRITE,
               MAP_SHARED | MAP_FILE, fb, 0);

  if (fbmem == MAP_FAILED)
    {
      fprintf(stderr, "ERROR: ioctl(FBIO_MMAP) failed\n");
      ret = -1;
    }
  else
    {
      uint32_t offset = (area->y + area->h - 1) * oinfo.stride +
                            (area->x + area->w) * oinfo.bpp / 8;

      if (offset > oinfo.fblen)
        {
          fprintf(stderr, "Area is out of range: %" PRId32 " >= %zu\n",
                  offset, oinfo.fblen);
          return -1;
        }

#ifdef CONFIG_FB_SYNC
      ret = ioctl(fb, FBIO_WAITFORVSYNC, 0);
      if (ret != OK)
        {
          fprintf(stderr, "Unable to sync upon vertical line\n");
        }
#endif

#ifdef CONFIG_FB_CMAP
      if (vinfo.fmt == FB_FMT_RGB8)
        {
          draw_rect8(fbmem, &oinfo, area, color);
        }
#endif
      else if (vinfo.fmt == FB_FMT_RGB16_565)
        {
          draw_rect16(fbmem, &oinfo, area, color);
        }
      else if (vinfo.fmt == FB_FMT_RGB24)
        {
          draw_rect24(fbmem, &oinfo, area, color);
        }
      else if (vinfo.fmt == FB_FMT_RGB32)
        {
          draw_rect32(fbmem, &oinfo, area, color);
        }
    }

  return ret;
}

/****************************************************************************
 * Name: overlay_accl
 *
 * Description:
 *   Determines overlay acceleration support
 *
 * Parameters:
 *   fb        - Open framebuffer filehandle
 *   overlayno - Overlay number
 *   accl      - Acceleration to detect
 *
 * Return:
 *   OK    - Success
 *   ERROR - Failed
 ****************************************************************************/

static int overlay_accl(int fb, uint8_t overlayno, uint32_t accl)
{
  int ret;
  struct fb_overlayinfo_s oinfo;

  oinfo.overlay = overlayno;

  ret = ioctl(fb, FBIOGET_OVERLAYINFO, (unsigned long)((uintptr_t)&oinfo));
  if (ret != OK)
    {
      fprintf(stderr, "Unable to get overlay information\n");
      return -1;
    }

  printf("%s: %08" PRIx32 " %08" PRIx32 "\n", __func__, oinfo.accl, accl);
  return (oinfo.accl & accl) ? OK : -1;
}

/****************************************************************************
 * Name: overlay_color
 *
 * Description:
 *   Set overlay color
 *
 * Parameters:
 *   fb    - Open framebuffer filehandle
 *   oinfo - Overlay information
 *
 ****************************************************************************/

static int overlay_color(int fb, FAR struct fb_overlayinfo_s *oinfo)
{
  int ret;

  printf("Overlay: %d, set color: 0x%08" PRIx32 "\n",
         oinfo->overlay, oinfo->color);

  ret = overlay_accl(fb, oinfo->overlay, FB_ACCL_COLOR);
  if (ret != OK)
    {
      fprintf(stderr, "No hardware acceleration to set a color within the "
              "selected overlay area\n");
    }
  else
    {
#ifdef CONFIG_FB_SYNC
      ret = ioctl(fb, FBIO_WAITFORVSYNC, 0);
      if (ret != OK)
        {
          fprintf(stderr, "Unable to sync upon vertical line\n");
        }
#endif

      ret = ioctl(fb, FBIOSET_COLOR, (unsigned long)(uintptr_t)oinfo);
      if (ret != OK)
        {
          fprintf(stderr, "Unable to set overlay color\n");
        }
    }

  return ret;
}

/****************************************************************************
 * Name: overlay_chromakey
 *
 * Description:
 *   Set overlay chromakey
 *
 * Parameters:
 *   fb        - Open framebuffer filehandle
 *   oinfo - Overlay information
 *
 ****************************************************************************/

static int overlay_chromakey(int fb, FAR struct fb_overlayinfo_s *oinfo)
{
  int ret;

  printf("Overlay: %d, set chromakey: 0x%08" PRIx32 "\n", oinfo->overlay,
         oinfo->chromakey);

  ret = overlay_accl(fb, oinfo->overlay, FB_ACCL_CHROMA);
  if (ret != OK)
    {
      fprintf(stderr, "No hardware acceleration to set the chromakey within "
              "the selected overlay area\n");
    }
  else
    {
#ifdef CONFIG_FB_SYNC
      ret = ioctl(fb, FBIO_WAITFORVSYNC, 0);
      if (ret != OK)
        {
          fprintf(stderr, "Unable to sync upon vertical line\n");
        }
#endif

      ret = ioctl(fb, FBIOSET_CHROMAKEY, (unsigned long)(uintptr_t)oinfo);
      if (ret != OK)
        {
          fprintf(stderr, "Unable to set overlay chroma key\n");
        }
    }

  return ret;
}

/****************************************************************************
 * Name: overlay_transp
 *
 * Description:
 *   Set overlay transparency
 *
 * Parameters:
 *   fb    - Open framebuffer filehandle
 *   oinfo - Overlay information
 *
 ****************************************************************************/

static int overlay_transp(int fb, FAR struct fb_overlayinfo_s *oinfo)
{
  int ret;

  printf("Overlay: %d, set transp: %d, transp_mode: %08x\n", oinfo->overlay,
         oinfo->transp.transp, oinfo->transp.transp_mode);

  ret = overlay_accl(fb, oinfo->overlay, FB_ACCL_TRANSP);
  if (ret != OK)
    {
      fprintf(stderr,
              "No hardware acceleration to set the transparency within "
              "the selected overlay area\n");
    }
  else
    {
#ifdef CONFIG_FB_SYNC
      ret = ioctl(fb, FBIO_WAITFORVSYNC, 0);
      if (ret != OK)
        {
          fprintf(stderr, "Unable to sync upon vertical line\n");
        }
#endif

      ret = ioctl(fb, FBIOSET_TRANSP, (unsigned long)(uintptr_t)oinfo);
      if (ret != OK)
        {
          fprintf(stderr, "Unable to set transparency\n");
        }
    }

  return ret;
}

/****************************************************************************
 * Name: overlay_blank
 *
 * Description:
 *   Enable or disable overlay
 *
 * Parameters:
 *   fb    - Open framebuffer filehandle
 *   oinfo - Overlay information
 *
 ****************************************************************************/

static int overlay_blank(int fb, FAR struct fb_overlayinfo_s *oinfo)
{
  int ret;

  printf("Overlay: %d, set blank: %08x\n", oinfo->overlay, oinfo->blank);

  ret = ioctl(fb, FBIOSET_BLANK, (unsigned long)(uintptr_t)oinfo);
  if (ret != OK)
    {
      fprintf(stderr, "Unable to blank overlay\n");
    }

  return ret;
}

/****************************************************************************
 * Name: overlay_area
 *
 * Description:
 *   Select overlay area
 *
 * Parameters:
 *   fb        - Open framebuffer filehandle
 *   oinfo     - Overlay information
 *
 ****************************************************************************/

static int overlay_area(int fb, FAR struct fb_overlayinfo_s *oinfo)
{
  int ret;

  printf("Overlay: %d, set xpos: %d, ypos: %d, xres: %d, yres: %d\n",
         oinfo->overlay, oinfo->sarea.x, oinfo->sarea.y, oinfo->sarea.w,
         oinfo->sarea.h);

  ret = overlay_accl(fb, oinfo->overlay, FB_ACCL_AREA);
  if (ret != OK)
    {
      fprintf(stderr, "No hardware acceleration to select area within "
              "the selected overlay area\n");
    }
  else
    {
      ret = ioctl(fb, FBIOSET_AREA, (unsigned long)(uintptr_t)oinfo);
      if (ret != OK)
        {
          fprintf(stderr, "Unable to select area\n");
        }
    }

  return ret;
}

/****************************************************************************
 * Name: overlay_blit
 *
 * Description:
 *   Blit content from source to destination overlay
 *
 * Parameters:
 *   fb         - Open framebuffer filehandle
 *   blit       - Blit control information
 *
 ****************************************************************************/

#ifdef CONFIG_FB_OVERLAY_BLIT
static int overlay_blit(int fb, FAR struct fb_overlayblit_s *blit)
{
  int ret;

  ret = overlay_accl(fb, blit->src.overlay, FB_ACCL_BLIT);
  if (ret == OK)
    {
      ret = overlay_accl(fb, blit->dest.overlay, FB_ACCL_BLIT);
    }

  if (ret != OK)
    {
      fprintf(stderr, "Hardware blit not supported\n");
    }
  else
    {
#  ifdef CONFIG_FB_SYNC
      ret = ioctl(fb, FBIO_WAITFORVSYNC, 0);
      if (ret != OK)
        {
          fprintf(stderr, "Unable to sync upon vertical line\n");
        }
#  endif

      ret = ioctl(fb, FBIOSET_BLIT, (unsigned long)(uintptr_t)blit);
      if (ret != OK)
        {
          fprintf(stderr, "Unable to blit overlay content\n");
        }
    }

  return ret;
}

/****************************************************************************
 * Name: overlay_blend
 *
 * Description:
 *   Blend content from foreground and background overlay to a destination
 *   overlay
 *
 * Parameters:
 *   fb         - Open framebuffer filehandle
 *   blend      - Blend operation information
 *
 ****************************************************************************/

static int overlay_blend(int fb, FAR struct fb_overlayblend_s *blend)
{
  int ret;

  ret = overlay_accl(fb, blend->foreground.overlay, FB_ACCL_BLEND);
  if (ret == OK)
    {
      ret = overlay_accl(fb, blend->background.overlay, FB_ACCL_BLEND);
    }

  if (ret == OK)
    {
      ret = overlay_accl(fb, blend->dest.overlay, FB_ACCL_BLEND);
    }

  if (ret != OK)
    {
      fprintf(stderr, "Hardware blending not supported\n");
    }
  else
    {
#  ifdef CONFIG_FB_SYNC
      ret = ioctl(fb, FBIO_WAITFORVSYNC, 0);
      if (ret != OK)
        {
          fprintf(stderr, "Unable to sync upon vertical line\n");
        }
#  endif

      ret = ioctl(fb, FBIOSET_BLEND, (unsigned long)(uintptr_t)blend);
      if (ret != OK)
        {
          fprintf(stderr, "Unable to blend overlay content\n");
        }
    }

  return ret;
}
#endif /* CONFIG_FB_OVERLAY_BLIT */

/****************************************************************************
 * Name: fbopen
 *
 * Description:
 *   Open framebuffer device
 *
 * Parameters:
 *   device - Path to framebuffer device
 *
 * Return:
 *   Open filehandle to framebuffer device or ERROR when failed
 ****************************************************************************/

static int fbopen(const char * device)
{
  int fb = open(device, O_RDWR);

  if (fb < 0)
    {
      fprintf(stderr, "Unable to open framebuffer device: %s\n", device);
      return EXIT_FAILURE;
    }

  return fb;
}

/****************************************************************************
 * Name: usage
 *
 * Description:
 *   Commandline info
 *
 * Parameters:
 *   progname - Name of the program
 *
 ****************************************************************************/

static void usage(const char * progname)
{
  fprintf(stderr,
          "usage: %s <option> -d <fbdev>\n"
          "\n"
          "    -vinfo\n"
          "    -pinfo\n"
          "    -oinfo overlayno\n"
          "    -fill overlayno <color> <xpos> <ypos> <xres> <yres>\n"
          "      color: pixel format color\n"
          "      xpos: x-offset\n"
          "      ypos: y-offset\n"
          "      xres: x-resolution or area width\n"
          "      yres: y-resolution or area height\n"
#ifdef CONFIG_FB_CMAP
          "    -cmap <color1> <color2> <color3> <color4> <color5>\n"
          "      colors: 0xAARRGGBB\n"
          "      one color must be set at least\n"
#endif
          "    -color overlayno <value>\n"
          "      value: pixel format color\n"
          "    -chroma overlayno <value>\n"
          "      value: pixel format color\n"
          "    -transp overlayno <value> <mode>\n"
          "      value: 0-255\n"
          "      mode : %d = const alpha, %d = pixel alpha\n"
          "    -blank : <value>\n"
          "      0 : On\n"
          "      1 : Off\n"
          "    -area overlayno <xpos> <ypos> <xres> <yres>\n"
#ifdef CONFIG_FB_OVERLAY_BLIT
          "    -blit doverlayno <destxpos> <destypos> <destxres> "
          "<destyres>\n"
          "          soverlayno <srcxpos> <srcypos> <srcxres> <srcyres>\n"
          "    -blend doverlayno <dxpos> <dypos> <dxres> <dyres>\n"
          "           foverlayno <fxpos> <fypos> <fxres> <fyres>\n"
          "           boverlayno <bxpos> <bypos> <bxres> <byres>\n"
#endif
          "\n"
          "    -d <fbdev> optional, default: \"/dev/fb0\"\n",
          progname, FB_CONST_ALPHA, FB_PIXEL_ALPHA);
}

/****************************************************************************
 * Name: fboverlay_main
 *
 * Description:
 *   Main entry point for the fboverlay test application.
 *
 ****************************************************************************/

int main(int argc, FAR char *argv[])
{
  char *fbdevice;
  int  fb = -1;

  if (argc < 2)
    {
      usage(argv[0]);
      return EXIT_FAILURE;
    }

  if (argc >= 2 && !strcmp(argv[argc - 2], "-d"))
    {
      fbdevice = argv[argc - 1];
    }
  else
    {
      fbdevice = "/dev/fb0";
    }

  if (!strcmp(argv[1], "-vinfo"))
    {
      fb = fbopen(fbdevice);
      if (fb >= 0)
        {
          print_video_info(fb);
        }
    }
  else if (!strcmp(argv[1], "-pinfo"))
    {
      fb = fbopen(fbdevice);
      if (fb >= 0)
        {
          print_plane_info(fb);
        }
    }
  else if (!strcmp(argv[1], "-oinfo") && argc >= 3)
    {
      int overlayno = atoi(argv[2]);

      fb = fbopen(fbdevice);
      if (fb >= 0)
        {
          print_overlay_info(fb, overlayno);
        }
    }
  else if (!strcmp(argv[1], "-fill") && argc >= 8)
    {
      struct fb_area_s area;
      int overlayno = atoi(argv[2]);
      uint32_t argb = strtoul(argv[3], NULL, 16);

      area.x = strtoul(argv[4], NULL, 10);
      area.y = strtoul(argv[5], NULL, 10);
      area.w = strtoul(argv[6], NULL, 10);
      area.h = strtoul(argv[7], NULL, 10);

      fb = fbopen(fbdevice);
      if (fb >= 0)
        {
          overlay_fill(fb, overlayno, argb, &area);
        }
    }
#ifdef CONFIG_FB_CMAP
  else if (!strcmp(argv[1], "-cmap") && argc >= 2)
    {
      struct fb_cmap_s cmap;
      uint8_t a[5];
      uint8_t r[5];
      uint8_t g[5];
      uint8_t b[5];

      cmap.first = 0;
      cmap.len = 1;
      rgb8888(strtoul(argv[2], NULL, 16), &a[0], &r[0], &g[0], &b[0]);

      if (argc >= 3)
        {
          rgb8888(strtoul(argv[3], NULL, 16), &a[1], &r[1], &g[1], &b[1]);
          cmap.len = 2;
        }

      if (argc >= 4)
        {
          rgb8888(strtoul(argv[4], NULL, 16), &a[2], &r[2], &g[2], &b[2]);
          cmap.len = 3;
        }

      if (argc >= 5)

        {
          rgb8888(strtoul(argv[5], NULL, 16), &a[3], &r[3], &g[3], &b[3]);
          cmap.len = 4;
        }

      if (argc >= 6)
        {
          rgb8888(strtoul(argv[6], NULL, 16), &a[4], &r[4], &g[4], &b[4]);
          cmap.len = 5;
        }

#  ifdef CONFIG_FB_TRANSPARENCY
      cmap.transp = a;
#  endif
      cmap.red = r;
      cmap.green = g;
      cmap.blue = b;
      fb = fbopen(fbdevice);
      if (fb >= 0)
        {
          /* Set new cmap */

          video_putcmap(fb, &cmap);

          /* Fetch cmap form driver to compare */

          video_getcmap(fb, &cmap);
        }
    }
#endif /* CONFIG_FB_CMAP */
  else if (!strcmp(argv[1], "-color") && argc >= 4)
    {
      struct fb_overlayinfo_s oinfo;

      oinfo.overlay   = atoi(argv[2]);
      oinfo.color     = strtoul(argv[3], NULL, 16);

      fb = fbopen(fbdevice);
      if (fb >= 0)
        {
          overlay_color(fb, &oinfo);
        }
    }
  else if (!strcmp(argv[1], "-chroma") && argc >= 4)
    {
      struct fb_overlayinfo_s oinfo;

      oinfo.overlay   = atoi(argv[2]);
      oinfo.chromakey = strtoul(argv[3], NULL, 16);

      fb = fbopen(fbdevice);
      if (fb >= 0)
        {
          overlay_chromakey(fb, &oinfo);
        }
    }
  else if (!strcmp(argv[1], "-transp") && argc >= 5)
    {
      struct fb_overlayinfo_s oinfo;

      oinfo.overlay            = atoi(argv[2]);
      oinfo.transp.transp      = strtoul(argv[3], NULL, 10);
      oinfo.transp.transp_mode = strtoul(argv[4], NULL, 10);

      if (oinfo.transp.transp_mode != FB_CONST_ALPHA &&
          oinfo.transp.transp_mode != FB_PIXEL_ALPHA)
        {
          fprintf(stderr, "Invalid transparency mode: %d\n",
                  oinfo.transp.transp_mode);
        }
      else
        {
          fb = fbopen(fbdevice);
          if (fb >= 0)
            {
              overlay_transp(fb, &oinfo);
            }
        }
    }
  else if (!strcmp(argv[1], "-area") && argc >= 7)
    {
      struct fb_overlayinfo_s oinfo;

      oinfo.overlay = atoi(argv[2]);
      oinfo.sarea.x = strtoul(argv[3], NULL, 10);
      oinfo.sarea.y = strtoul(argv[4], NULL, 10);
      oinfo.sarea.w = strtoul(argv[5], NULL, 10);
      oinfo.sarea.h = strtoul(argv[6], NULL, 10);

      fb = fbopen(fbdevice);
      if (fb >= 0)
        {
          overlay_area(fb, &oinfo);
        }
    }
  else if (!strcmp(argv[1], "-blank") && argc >= 3)
    {
      struct fb_overlayinfo_s oinfo;

      oinfo.overlay  = atoi(argv[2]);
      oinfo.blank    = strtoul(argv[3], NULL, 10);

      fb = fbopen(fbdevice);
      if (fb >= 0)
        {
          overlay_blank(fb, &oinfo);
        }
    }
#ifdef CONFIG_FB_OVERLAY_BLIT
  else if (!strcmp(argv[1], "-blit") && argc >= 11)
    {
      struct fb_overlayblit_s blit;

      blit.dest.overlay = atoi(argv[2]);
      blit.dest.area.x  = strtoul(argv[3], NULL, 10);
      blit.dest.area.y  = strtoul(argv[4], NULL, 10);
      blit.dest.area.w  = strtoul(argv[5], NULL, 10);
      blit.dest.area.h  = strtoul(argv[6], NULL, 10);

      blit.src.overlay = atoi(argv[7]);
      blit.src.area.x  = strtoul(argv[8], NULL, 10);
      blit.src.area.y  = strtoul(argv[9], NULL, 10);
      blit.src.area.w  = strtoul(argv[10], NULL, 10);
      blit.src.area.h  = strtoul(argv[11], NULL, 10);

      fb = fbopen(fbdevice);
      if (fb >= 0)
        {
          overlay_blit(fb, &blit);
        }
    }
  else if (!strcmp(argv[1], "-blend") && argc >= 16)
    {
      struct fb_overlayblend_s blend;

      blend.dest.overlay       = atoi(argv[2]);
      blend.dest.area.x        = strtoul(argv[3], NULL, 10);
      blend.dest.area.y        = strtoul(argv[4], NULL, 10);
      blend.dest.area.w        = strtoul(argv[5], NULL, 10);
      blend.dest.area.h        = strtoul(argv[6], NULL, 10);

      blend.foreground.overlay = atoi(argv[7]);
      blend.foreground.area.x  = strtoul(argv[8], NULL, 10);
      blend.foreground.area.y  = strtoul(argv[9], NULL, 10);
      blend.foreground.area.w  = strtoul(argv[10], NULL, 10);
      blend.foreground.area.h  = strtoul(argv[11], NULL, 10);

      blend.background.overlay = atoi(argv[12]);
      blend.background.area.x  = strtoul(argv[13], NULL, 10);
      blend.background.area.y  = strtoul(argv[14], NULL, 10);
      blend.background.area.w  = strtoul(argv[15], NULL, 10);
      blend.background.area.h  = strtoul(argv[16], NULL, 10);

      fb = fbopen(fbdevice);
      if (fb >= 0)
        {
          overlay_blend(fb, &blend);
        }
    }
#endif
  else
    {
      usage(argv[0]);
      return EXIT_FAILURE;
    }

  if (fb >= 0)
    {
      close(fb);
    }

  return EXIT_SUCCESS;
}