text: /****************************************************************************
   * arch/arm/src/stm32/stm32_dma2d.c
   *
   *   Copyright (C) 2014-2015 Marco Krahl. All rights reserved.
   *   Author: Marco Krahl <ocram.lhark@gmail.com>
   *
   * References:
   *   STM32F429 Technical Reference Manual
   *
   * 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 <stdint.h>
  #include <string.h>
  #include <errno.h>
  #include <debug.h>
  #include <semaphore.h>
  
  #include <nuttx/irq.h>
  #include <nuttx/video/fb.h>
  #include <nuttx/kmalloc.h>
  
  #include <arch/chip/dma2d.h>
  #include <arch/board/board.h>
  
  #include "up_arch.h"
  #include "up_internal.h"
  #include "stm32.h"
  #include "chip/stm32_ltdc.h"
  #include "chip/stm32_dma2d.h"
  #include "chip/stm32_ccm.h"
  #include "stm32_dma2d.h"
  
  /****************************************************************************
   * Pre-processor Definitions
   ****************************************************************************/
  
  /* output, foreground and background layer */
  
  #define DMA2D_NLAYERS                   3
  
  /* DMA2D PFC value definitions */
  
  #define DMA2D_PF_ARGB8888               0
  #define DMA2D_PF_RGB888                 1
  #define DMA2D_PF_RGB565                 2
  #define DMA2D_PF_ARGB1555               3
  #define DMA2D_PF_ARGB14444              4
  #define DMA2D_PF_L8                     5
  #define DMA2D_PF_AL44                   6
  #define DMA2D_PF_AL88                   7
  #define DMA2D_PF_L4                     8
  #define DMA2D_PF_A8                     9
  #define DMA2D_PF_A4                     10
  
  /* DMA2D blender control */
  
  #define STM32_DMA2D_CR_MODE_BLIT        DMA2D_CR_MODE(0)
  #define STM32_DMA2D_CR_MODE_BLITPFC     DMA2D_CR_MODE(1)
  #define STM32_DMA2D_CR_MODE_BLEND       DMA2D_CR_MODE(2)
  #define STM32_DMA2D_CR_MODE_COLOR       DMA2D_CR_MODE(3)
  #define STM32_DMA2D_CR_MODE_CLEAR       STM32_DMA2D_CR_MODE_COLOR
  
  /* DMA2D PFC alpha mode */
  
  #define STM32_DMA2D_PFCCR_AM_NONE       0
  #define STM32_DMA2D_PFCCR_AM_CONST      1
  #define STM32_DMA2D_PFCCR_AM_PIXEL      10
  
  /* Only 8 bit per pixel overal supported */
  
  #define DMA2D_PF_BYPP(n)                ((n) / 8)
  
  #define DMA2D_CLUT_SIZE                 STM32_LTDC_NCLUT - 1
  
  /* Layer argb cmap conversion */
  
  #define DMA2D_CLUT_ALPHA(n)          ((uint32_t)(n) << 24)
  #define DMA2D_CLUT_RED(n)            ((uint32_t)(n) << 16)
  #define DMA2D_CLUT_GREEN(n)          ((uint32_t)(n) << 8)
  #define DMA2D_CLUT_BLUE(n)           ((uint32_t)(n) << 0)
  
  #define DMA2D_CMAP_ALPHA(n)          ((uint32_t)(n) >> 24)
  #define DMA2D_CMAP_RED(n)            ((uint32_t)(n) >> 16)
  #define DMA2D_CMAP_GREEN(n)          ((uint32_t)(n) >> 8)
  #define DMA2D_CMAP_BLUE(n)           ((uint32_t)(n) >> 0)
  
  /* Define shadow layer for ltdc interface */
  
  #ifdef CONFIG_STM32_LTDC_INTERFACE
  # ifdef CONFIG_STM32_LTDC_L2
  #  define DMA2D_SHADOW_LAYER    2
  #  define DMA2D_SHADOW_LAYER_L1 0
  #  define DMA2D_SHADOW_LAYER_L2 1
  # else
  #  define DMA2D_SHADOW_LAYER    1
  #  define DMA2D_SHADOW_LAYER_L1 0
  # endif
  # define DMA2D_LAYER_NSIZE      CONFIG_STM32_DMA2D_NLAYERS + DMA2D_SHADOW_LAYER
  #else
  # define DMA2D_LAYER_NSIZE      CONFIG_STM32_DMA2D_NLAYERS
  # define DMA2D_SHADOW_LAYER     0
  #endif
  
  /* Debug option */
  
  #ifdef CONFIG_STM32_DMA2D_REGDEBUG
  #  define regerr       err
  #  define reginfo      info
  #else
  #  define regerr(x...)
  #  define reginfo(x...)
  #endif
  
  /* check clut support */
  
  #ifdef CONFIG_STM32_DMA2D_L8
  # ifndef CONFIG_FB_CMAP
  #  error "Enable cmap to support the configured layer formats!"
  # endif
  #endif
  
  /* check ccm heap allocation */
  
  #ifndef CONFIG_STM32_CCMEXCLUDE
  # error "Enable CONFIG_STM32_CCMEXCLUDE from the heap allocation"
  #endif
  
  /****************************************************************************
   * Private Types
   ****************************************************************************/
  
  /* DMA2D General layer information */
  
  struct stm32_dma2d_s
  {
    struct dma2d_layer_s dma2d;   /* public dma2d interface */
  
    /* Fixed settings */
  
    int lid;                      /* Layer identifier */
    struct fb_videoinfo_s vinfo;  /* Layer videoinfo */
    struct fb_planeinfo_s pinfo;  /* Layer planeinfo */
  
    /* Blending */
  
    uint32_t blendmode;           /* the interface blendmode */
    uint8_t  alpha;               /* the alpha value */
  
    /* Coloring */
  
  #ifdef CONFIG_STM32_DMA2D_L8
    uint32_t *clut;               /* Color lookup table */
  #endif
  
    /* Operation */
    uint8_t  fmt;                 /* the controller pixel format */
    sem_t    *lock;               /* Ensure mutually exclusive access */
  };
  
  #ifdef CONFIG_STM32_LTDC_INTERFACE
  
  /* This structures provides the DMA2D layer for each LTDC layer */
  
  struct stm32_ltdc_dma2d_s
  {
    struct stm32_dma2d_s dma2ddev;
  #ifdef CONFIG_STM32_DMA2D_L8
    FAR struct ltdc_layer_s *ltdc;
  #endif
  };
  
  struct stm32_ltdc_layer_s
  {
    /* Layer state */
  
    struct stm32_ltdc_dma2d_s layer[DMA2D_SHADOW_LAYER];
  };
  #endif
  
  /* Interrupt handling */
  
  struct stm32_interrupt_s
  {
    bool  wait;       /* Informs that the task is waiting for the irq */
    bool  handled;    /* Informs that an irq was handled */
    int   irq;        /* irq number */
    sem_t *sem;       /* Semaphore for waiting for irq */
  };
  
  /* This enumeration foreground and background layer supported by the dma2d
   * controller
   */
  
  enum stm32_layer_e
  {
    DMA2D_LAYER_LFORE = 0,       /* Foreground Layer */
    DMA2D_LAYER_LBACK,           /* Background Layer */
    DMA2D_LAYER_LOUT,            /* Output Layer */
  };
  
  /* DMA2D memory address register */
  
  static const uintptr_t stm32_mar_layer_t[DMA2D_NLAYERS] =
  {
    STM32_DMA2D_FGMAR,
    STM32_DMA2D_BGMAR,
    STM32_DMA2D_OMAR
  };
  
  /* DMA2D offset register */
  
  static const uintptr_t stm32_or_layer_t[DMA2D_NLAYERS] =
  {
    STM32_DMA2D_FGOR,
    STM32_DMA2D_BGOR,
    STM32_DMA2D_OOR
  };
  
  /* DMA2D pfc control register */
  
  static const uintptr_t stm32_pfccr_layer_t[DMA2D_NLAYERS] =
  {
    STM32_DMA2D_FGPFCCR,
    STM32_DMA2D_BGPFCCR,
    STM32_DMA2D_OPFCCR
  };
  
  /* DMA2D color register */
  
  static const uintptr_t stm32_color_layer_t[DMA2D_NLAYERS] =
  {
    STM32_DMA2D_FGCOLR,
    STM32_DMA2D_BGCOLR,
    STM32_DMA2D_OCOLR
  };
  
  /* DMA2D clut memory address register */
  
  static const uintptr_t stm32_cmar_layer_t[DMA2D_NLAYERS - 1] =
  {
    STM32_DMA2D_FGCMAR,
    STM32_DMA2D_BGCMAR
  };
  
  /****************************************************************************
   * Private Function Prototypes
   ****************************************************************************/
  
  /* Private functions */
  
  static int stm32_dma2d_pixelformat(uint8_t fmt, uint8_t *fmtmap);
  static int stm32_dma2d_bpp(uint8_t fmt, uint8_t *bpp);
  static void stm32_dma2d_control(uint32_t setbits, uint32_t clrbits);
  static int stm32_dma2dirq(int irq, void *context);
  static int stm32_dma2d_waitforirq(void);
  static int stm32_dma2d_start(void);
  #ifdef CONFIG_STM32_DMA2D_L8
  static int stm32_dma2d_loadclut(uintptr_t reg);
  #endif
  static uint32_t stm32_dma2d_memaddress(FAR const struct stm32_dma2d_s *layer,
                                          fb_coord_t xpos, fb_coord_t ypos);
  static fb_coord_t stm32_dma2d_lineoffset(FAR const struct stm32_dma2d_s *layer,
                                            FAR const struct ltdc_area_s *area);
  
  static int stm32_dma2d_lfreelid(void);
  static FAR struct stm32_dma2d_s * stm32_dma2d_lalloc(void);
  static void stm32_dma2d_lfree(FAR struct stm32_dma2d_s *layer);
  static void stm32_dma2d_llayerscleanup(void);
  static bool stm32_dma2d_lvalidate(FAR const struct stm32_dma2d_s *layer);
  static bool stm32_dma2d_lvalidatesize(FAR const struct stm32_dma2d_s *layer,
                                        fb_coord_t xpos, fb_coord_t ypos,
                                        FAR const struct ltdc_area_s *area);
  static void stm32_dma2d_linit(FAR struct stm32_dma2d_s *layer,
                                  int lid, uint8_t fmt);
  
  static void stm32_dma2d_lfifo(FAR const struct stm32_dma2d_s *layer, int lid,
                                fb_coord_t xpos, fb_coord_t ypos,
                                FAR const struct ltdc_area_s *area);
  static void stm32_dma2d_lcolor(FAR const struct stm32_dma2d_s *layer,
                                 int lid, uint32_t color);
  static void stm32_dma2d_llnr(FAR struct stm32_dma2d_s *layer,
                                  FAR const struct ltdc_area_s *area);
  static int stm32_dma2d_loutpfc(FAR const struct stm32_dma2d_s *layer);
  static void stm32_dma2d_lpfc(FAR const struct stm32_dma2d_s *layer,
                                int lid, uint32_t blendmode);
  /* Public functions */
  
  static int stm32_dma2dgetvideoinfo(FAR struct dma2d_layer_s *layer,
                                    FAR struct fb_videoinfo_s *vinfo);
  static int stm32_dma2dgetplaneinfo(FAR struct dma2d_layer_s *layer, int planeno,
                                    FAR struct fb_planeinfo_s *pinfo);
  static int stm32_dma2dgetlid(FAR struct dma2d_layer_s *layer, int *lid);
  #ifdef CONFIG_STM32_DMA2D_L8
  static int stm32_dma2dsetclut(FAR struct dma2d_layer_s *layer,
                              const FAR struct fb_cmap_s *cmap);
  static int stm32_dma2dgetclut(FAR struct dma2d_layer_s *layer,
                              FAR struct fb_cmap_s *cmap);
  #endif
  static int stm32_dma2dsetalpha(FAR struct dma2d_layer_s *layer, uint8_t alpha);
  static int stm32_dma2dgetalpha(FAR struct dma2d_layer_s *layer, uint8_t *alpha);
  static int stm32_dma2dsetblendmode(FAR struct dma2d_layer_s *layer,
                                      uint32_t mode);
  static int stm32_dma2dgetblendmode(FAR struct dma2d_layer_s *layer,
                                      uint32_t *mode);
  static int stm32_dma2dblit(FAR struct dma2d_layer_s *dest,
                              fb_coord_t destxpos, fb_coord_t destypos,
                              FAR const struct dma2d_layer_s *src,
                              FAR const struct ltdc_area_s *srcarea);
  static int stm32_dma2dblend(FAR struct dma2d_layer_s *dest,
                              fb_coord_t destxpos, fb_coord_t destypos,
                              FAR const struct dma2d_layer_s *fore,
                              fb_coord_t forexpos, fb_coord_t foreypos,
                              FAR const struct dma2d_layer_s *back,
                              FAR const struct ltdc_area_s *backarea);
  static int stm32_dma2dfillarea(FAR struct dma2d_layer_s *layer,
                              FAR const struct ltdc_area_s *area, uint32_t color);
  
  /****************************************************************************
   * Private Data
   ****************************************************************************/
  
  /* Remember the layer references for alloc/deallocation */
  
  static struct stm32_dma2d_s *g_layers[DMA2D_LAYER_NSIZE];
  
  /* The DMA2D semaphore that enforces mutually exclusive access */
  
  static sem_t g_lock;
  
  #ifdef CONFIG_STM32_LTDC_INTERFACE
  /* This structure provides the DMA2D layer for each LTDC layer */
  
  static struct stm32_ltdc_layer_s g_ltdc_layer;
  #endif
  
  /* The initalized state of the driver */
  
  static bool g_initialized;
  
  /* Semaphore for interrupt handling */
  
  static sem_t g_semirq;
  
  /* This structure provides irq handling */
  
  static struct stm32_interrupt_s g_interrupt =
  {
    .wait    = false,
    .handled = true,
    .irq     = STM32_IRQ_DMA2D,
    .sem     = &g_semirq
  };
  
  /****************************************************************************
   * Public Data
   ****************************************************************************/
  
  /****************************************************************************
   * Private Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Name: stm32_dma2d_control
   *
   * Description:
   *   Change the DMA2D control register
   *
   * Parameter:
   *   setbits - The bits to set
   *   clrbits - The bits to clear
   *
   ****************************************************************************/
  
  static void stm32_dma2d_control(uint32_t setbits, uint32_t clrbits)
  {
    uint32_t   cr;
  
    ginfo("setbits=%08x, clrbits=%08x\n", setbits, clrbits);
  
    cr = getreg32(STM32_DMA2D_CR);
    cr &= ~clrbits;
    cr |= setbits;
    putreg32(cr, STM32_DMA2D_CR);
  }
  
  /****************************************************************************
   * Name: stm32_dma2dirq
   *
   * Description:
   *   DMA2D interrupt handler
   *
   ****************************************************************************/
  
  static int stm32_dma2dirq(int irq, void *context)
  {
    uint32_t regval = getreg32(STM32_DMA2D_ISR);
    FAR struct stm32_interrupt_s *priv = &g_interrupt;
  
    reginfo("irq = %d, regval = %08x\n", irq, regval);
  
    if (regval & DMA2D_ISR_TCIF)
      {
        /* Transfer complete interrupt */
  
        /* Clear the interrupt status register */
  
        putreg32(DMA2D_IFCR_CTCIF, STM32_DMA2D_IFCR);
      }
  #ifdef CONFIG_STM32_DMA2D_L8
    else if (regval & DMA2D_ISR_CTCIF)
      {
        /* CLUT transfer complete interrupt */
  
        /* Clear the interrupt status register */
  
        putreg32(DMA2D_IFCR_CCTCIF, STM32_DMA2D_IFCR);
      }
  #endif
    else
      {
        /* Unknown irq, should not occur */
  
        return OK;
      }
  
    /* Update the handled flag */
  
    priv->handled = true;
  
    /* Unlock the semaphore if locked */
  
    if (priv->wait)
      {
  
        int ret = sem_post(priv->sem);
  
        if (ret != OK)
          {
            err("sem_post() failed\n");
            return ret;
          }
      }
  
    return OK;
  }
  
  /****************************************************************************
   * Name: stm32_dma2d_waitforirq
   *
   * Description:
   *   Helper waits until the dma2d irq occurs. That means that an ongoing clut
   *   loading or dma transfer was completed.
   *   Note! The caller must use this function within a critical section.
   *
   * Return:
   *   On success OK otherwise ERROR
   *
   ****************************************************************************/
  
  static int stm32_dma2d_waitforirq(void)
  {
    FAR struct stm32_interrupt_s *priv = &g_interrupt;
  
    /* Only waits if last enabled interrupt is currently not handled */
  
    if (!priv->handled)
      {
        int ret;
  
        /* Inform the irq handler the task is able to wait for the irq */
  
        priv->wait = true;
  
        ret = sem_wait(priv->sem);
  
        /* irq or an error occurs, reset the wait flag */
  
        priv->wait = false;
  
        if (ret != OK)
          {
            err("sem_wait() failed\n");
            return ret;
          }
      }
  
    return OK;
  }
  
  
  #ifdef CONFIG_STM32_DMA2D_L8
  /****************************************************************************
   * Name: stm32_dma2d_loadclut
   *
   * Description:
   *   Starts clut loading but doesn't wait until loading is complete!
   *
   * Parameter:
   *   pfcreg - PFC control Register
   *
   * Return:
   *   On success - OK
   *   On error - -EINVAL
   *
   ****************************************************************************/
  
  static int stm32_dma2d_loadclut(uintptr_t pfcreg)
  {
    int        ret;
    uint32_t   regval;
    irqstate_t flags;
  
    flags = enter_critical_section();
  
    ret = stm32_dma2d_waitforirq();
    if (ret == OK)
      {
        FAR struct stm32_interrupt_s *priv = &g_interrupt;
  
        /* Reset the handled flag */
  
        priv->handled = false;
  
        /* Start clut loading */
  
        regval  = getreg32(pfcreg);
        regval |= DMA2D_xGPFCCR_START;
        reginfo("set regval=%08x\n", regval);
        putreg32(regval, pfcreg);
        reginfo("configured regval=%08x\n", getreg32(pfcreg));
      }
  
    leave_critical_section(flags);
    return OK;
  }
  #endif
  
  /****************************************************************************
   * Name: stm32_dma2d_start
   *
   * Description:
   *   Starts the dma transfer and waits until completed.
   *
   * Parameter:
   *   reg       - Register to set the start
   *   startflag - The related flag to start the dma transfer
   *   irqflag   - The interrupt enable flag in the DMA2D_CR register
   *
   ****************************************************************************/
  
  static int stm32_dma2d_start(void)
  {
    int        ret;
    irqstate_t flags;
  
    flags = enter_critical_section();
  
    ret = stm32_dma2d_waitforirq();
    if (ret == OK)
      {
        FAR struct stm32_interrupt_s *priv = &g_interrupt;
  
        /* Reset the handled flag */
  
        priv->handled = false;
  
        /* Start clut loading */
  
        stm32_dma2d_control(DMA2D_CR_START, 0);
  
        /* wait until transfer is complete */
  
        ret = stm32_dma2d_waitforirq();
      }
  
    leave_critical_section(flags);
    return ret;
  }
  
  /****************************************************************************
   * Name: stm32_dma2d_memaddress
   *
   * Description:
   *   Helper to calculate the layer memory address
   *
   * Parameter:
   *   layer - Reference to the common layer state structure
   *
   * Return:
   *   memory address
   *
   ****************************************************************************/
  
  static uint32_t stm32_dma2d_memaddress(FAR const struct stm32_dma2d_s *layer,
                                          fb_coord_t xpos, fb_coord_t ypos)
  {
    FAR const struct fb_planeinfo_s *pinfo = &layer->pinfo;
    uint32_t offset;
  
    offset = xpos * DMA2D_PF_BYPP(layer->pinfo.bpp) + layer->pinfo.stride * ypos;
  
    ginfo("%p\n", ((uint32_t) pinfo->fbmem) + offset);
    return ((uint32_t) pinfo->fbmem) + offset;
  }
  
  /****************************************************************************
   * Name: stm32_dma2d_lineoffset
   *
   * Description:
   *   Helper to calculate the layer line offset
   *
   * Parameter:
   *   layer - Reference to the common layer state structure
   *
   * Return:
   *   line offset
   *
   ****************************************************************************/
  
  static fb_coord_t stm32_dma2d_lineoffset(FAR const struct stm32_dma2d_s *layer,
                                            FAR const struct ltdc_area_s *area)
  {
    /* offset at the end of each line in the context to the area layer */
  
    ginfo("%d\n", layer->vinfo.xres - area->xres);
    return layer->vinfo.xres - area->xres;
  }
  
  /****************************************************************************
   * Name: stm32_dma2d_pixelformat
   *
   * Description:
   *   Helper to map to dma2d controller pixel format
   *
   * Parameter:
   *   layer - Reference to the common layer state structure
   *   fmt   - Reference to the location to store the pixel format
   *
   * Return:
   *   On success - OK
   *   On error   - -EINVAL
   *
   ****************************************************************************/
  
  static int stm32_dma2d_pixelformat(uint8_t fmt, uint8_t *fmtmap)
  {
    ginfo("fmt=%d, fmtmap=%p\n", fmt, fmtmap);
  
    /* Map to the controller known format
     *
     * Not supported by NuttX:
     * ARGB8888
     * ARGB1555
     * ARGB4444
     * AL44
     * AL88
     * L8 (non output layer only)
     * L4
     * A8
     * A4
     */
  
    switch (fmt)
      {
  #ifdef CONFIG_STM32_DMA2D_RGB565
        case FB_FMT_RGB16_565:
          *fmtmap = DMA2D_PF_RGB565;
          break;
  #endif
  #ifdef CONFIG_STM32_DMA2D_RGB888
        case FB_FMT_RGB24:
          *fmtmap = DMA2D_PF_RGB888;
          break;
  #endif
  #ifdef CONFIG_STM32_DMA2D_L8
        case FB_FMT_RGB8:
          *fmtmap = DMA2D_PF_L8;
          break;
  #endif
        default:
          gerr("ERROR: Returning EINVAL\n");
          return -EINVAL;
      }
  
    return OK;
  }
  
  /****************************************************************************
   * Name: stm32_dma2d_bpp
   *
   * Description:
   *   Helper to get the bits per pixel
   *
   * Parameter:
   *   layer - Reference to the common layer state structure
   *   bpp   - Reference to the location to store the pixel format
   *
   * Return:
   *   On success - OK
   *   On error   - -EINVAL
   *
   ****************************************************************************/
  
  static int stm32_dma2d_bpp(uint8_t fmt, uint8_t *bpp)
  {
    ginfo("fmt=%d, bpp=%p\n", fmt, bpp);
  
    switch (fmt)
      {
  #ifdef CONFIG_STM32_DMA2D_RGB565
        case FB_FMT_RGB16_565:
          *bpp = 16;
          break;
  #endif
  #ifdef CONFIG_STM32_DMA2D_RGB888
        case FB_FMT_RGB24:
          *bpp = 24;
          break;
  #endif
  #ifdef CONFIG_STM32_DMA2D_L8
        case FB_FMT_RGB8:
          *bpp = 8;
          break;
  #endif
        default:
          gerr("ERROR: Returning EINVAL\n");
          return -EINVAL;
      }
  
    return OK;
  }
  
  /****************************************************************************
   * Name: stm32_dma2d_lfreelid
   *
   * Description:
   *   Get a free layer id
   *
   * Return:
   *   The number of the free layer
   *   -1 if no free layer is available
   *
   ****************************************************************************/
  
  static int stm32_dma2d_lfreelid(void)
  {
    int n;
  
    for (n = DMA2D_SHADOW_LAYER; n < DMA2D_LAYER_NSIZE; n++)
      {
        if (g_layers[n] == NULL)
          {
            return n;
          }
      }
  
    return -1;
  }
  
  /****************************************************************************
   * Name: stm32_dma2d_lalloc
   *
   * Description:
   *   Allocate a new layer structure
   *
   * Return:
   *   A new allocated layer structure or NULL on error.
   *
   ****************************************************************************/
  
  static FAR struct stm32_dma2d_s * stm32_dma2d_lalloc(void)
  {
    FAR struct stm32_dma2d_s *layer;
  
  #ifdef HAVE_CCM_HEAP
    /* First try to allocate from the ccm heap */
  
    layer = ccm_malloc(sizeof(struct stm32_dma2d_s));
  
    if (!layer)
      {
        /* Use default allocator */
  
        layer = kmm_malloc(sizeof(struct stm32_dma2d_s));
      }
  #else
    layer = kmm_malloc(sizeof(struct stm32_dma2d_s));
  #endif
  
    return layer;
  }
  
  /****************************************************************************
   * Name: stm32_dma2d_lfree
   *
   * Description:
   *   Deallocate the dynamic allocated layer structure
   *
   * Input Parameters:
   *   A previous allocated layer structure
   *
   ****************************************************************************/
  
  static void stm32_dma2d_lfree(FAR struct stm32_dma2d_s *layer)
  {
    if (layer)
      {
  #ifdef HAVE_CCM_HEAP
        if (((uint32_t)layer & 0xf0000000) == 0x10000000)
          {
            ccm_free(layer);
          }
        else
          {
            kmm_free(layer);
          }
  #else
        kmm_free(layer);
  #endif
      }
  }
  
  /****************************************************************************
   * Name: stm32_dma2d_llayerscleanup
   *
   * Description:
   *   Cleanup all allocated layers
   *
   ****************************************************************************/
  
  static void stm32_dma2d_llayerscleanup(void)
  {
    int n;
  
    /* Do not uninitialize the ltdc related dma2d layer */
  
    for (n = DMA2D_SHADOW_LAYER; n < DMA2D_LAYER_NSIZE; n++)
      {
        FAR struct stm32_dma2d_s *priv = g_layers[n];
        if (priv)
          {
            kmm_free(priv->pinfo.fbmem);
            stm32_dma2d_lfree(priv);
            g_layers[n] = NULL;
          }
      }
  }
  
  /****************************************************************************
   * Name: stm32_dma2d_lvalidate
   *
   * Description:
   *   Helper to validate if the layer is valid
   *
   * Return:
   *   true if validates otherwise false
   *
   ****************************************************************************/
  
  static inline bool stm32_dma2d_lvalidate(FAR const struct stm32_dma2d_s *layer)
  {
    return layer && layer->lid < DMA2D_LAYER_NSIZE;
  }
  
  /****************************************************************************
   * Name: stm32_dma2d_lvalidatesize
   *
   * Description:
   *   Helper to check if area is outside the whole layer.
   *
   * Parameter:
   *   layer   - Reference to the layer control structure
   *   xpos    - The x position inside the whole layer
   *   ypos    - The y position inside the whole layer
   *   area    - the area inside the whole layer
   *
   * Return:
   *   true if area is inside the whole layer otherwise false
   *
   ****************************************************************************/
  
  static bool stm32_dma2d_lvalidatesize(FAR const struct stm32_dma2d_s *layer,
                                           fb_coord_t xpos, fb_coord_t ypos,
                                           FAR const struct ltdc_area_s *area)
  {
    return stm32_dma2d_lvalidate(layer) &&
              ((layer->vinfo.xres - xpos) * (layer->vinfo.yres - ypos) >=
                  area->xres * area->yres);
  }
  
  /****************************************************************************
   * Name: stm32_dma2d_linit
   *
   * Description:
   *   Initialize the internal layer structure
   *
   * Parameter:
   *
   *
   ****************************************************************************/
  
  static void stm32_dma2d_linit(FAR struct stm32_dma2d_s *layer,
                                  int lid, uint8_t fmt)
  {
    FAR struct dma2d_layer_s *priv = &layer->dma2d;
  
    ginfo("layer=%p, lid=%d, fmt=%02x\n", layer, lid, fmt);
  
    /* initialize the layer interface */
  
    priv->getvideoinfo = stm32_dma2dgetvideoinfo;
    priv->getplaneinfo = stm32_dma2dgetplaneinfo;
    priv->getlid       = stm32_dma2dgetlid;
  #ifdef CONFIG_STM32_DMA2D_L8
    priv->setclut      = stm32_dma2dsetclut;
    priv->getclut      = stm32_dma2dgetclut;
  #endif
    priv->setalpha     = stm32_dma2dsetalpha;
    priv->getalpha     = stm32_dma2dgetalpha;
    priv->setblendmode = stm32_dma2dsetblendmode;
    priv->getblendmode = stm32_dma2dgetblendmode;
    priv->blit         = stm32_dma2dblit;
    priv->blend        = stm32_dma2dblend;
    priv->fillarea     = stm32_dma2dfillarea;
  
    /* Initialize the layer structure */
  
    layer->lid          = lid;
  #ifdef CONFIG_STM32_DMA2D_L8
    layer->clut         = 0;
  #endif
    layer->blendmode    = DMA2D_BLEND_NONE;
    layer->alpha        = 255;
    layer->fmt          = fmt;
    layer->lock         = &g_lock;
  }
  
  /****************************************************************************
   * Name: stm32_dma2d_lfifo
   *
   * Description:
   *   Set the fifo for the foreground, background and output layer
   *   Configures the memory address register
   *   Configures the line offset register
   *
   * Parameter:
   *   layer - Reference to the common layer state structure
   *
   ****************************************************************************/
  
  static void stm32_dma2d_lfifo(FAR const struct stm32_dma2d_s *layer, int lid,
                                fb_coord_t xpos, fb_coord_t ypos,
                                FAR const struct ltdc_area_s *area)
  {
    ginfo("layer=%p, lid=%d, xpos=%d, ypos=%d, area=%p\n",
              layer, lid, xpos, ypos, area);
  
    putreg32(stm32_dma2d_memaddress(layer, xpos, ypos), stm32_mar_layer_t[lid]);
    putreg32(stm32_dma2d_lineoffset(layer, area), stm32_or_layer_t[lid]);
  }
  
  /****************************************************************************
   * Name: stm32_dma2d_lcolor
   *
   * Description:
   *   Set the color for the layer
   *
   * Parameter:
   *   layer - Reference to the common layer state structure
   *
   ****************************************************************************/
  
  static void stm32_dma2d_lcolor(FAR const struct stm32_dma2d_s *layer,
                                 int lid, uint32_t color)
  {
    ginfo("layer=%p, lid=%d, color=%08x\n", layer, lid, color);
    putreg32(color, stm32_color_layer_t[lid]);
  }
  
  /****************************************************************************
   * Name: stm32_dma2d_llnr
   *
   * Description:
   *   Set the number of line register
   *
   * Parameter:
   *   layer   - Reference to the common layer state structure
   *   area    - Reference to the area to copy
   *
   ****************************************************************************/
  
  static void stm32_dma2d_llnr(FAR struct stm32_dma2d_s *layer,
                                  FAR const struct ltdc_area_s *area)
  {
    uint32_t nlrreg;
  
    ginfo("pixel per line: %d, number of lines: %d\n", area->xres, area->yres);
  
    nlrreg = getreg32(STM32_DMA2D_NLR);
    nlrreg = (DMA2D_NLR_PL(area->xres) | DMA2D_NLR_NL(area->yres));
    putreg32(nlrreg, STM32_DMA2D_NLR);
  }
  
  /****************************************************************************
   * Name: stm32_dma2d_loutpfc
   *
   * Description:
   *   Set the output PFC control register
   *
   * Parameter:
   *   layer - Reference to the common layer state structure
   *
   ****************************************************************************/
  
  static int stm32_dma2d_loutpfc(FAR const struct stm32_dma2d_s *layer)
  {
    ginfo("layer=%p\n", layer);
  
    /* CLUT format isn't supported by the dma2d controller */
  
    if (layer->fmt == DMA2D_PF_L8)
      {
        /* Destination layer doesn't support CLUT output */
  
        gerr("ERROR: Returning ENOSYS, "
              "output to layer with CLUT format not supported.\n");
        return -ENOSYS;
      }
  
    /* Set the mapped pixel format of source layer */
  
    putreg32(DMA2D_OPFCCR_CM(layer->fmt), STM32_DMA2D_OPFCCR);
  
    return OK;
  }
  
  /****************************************************************************
   * Name: stm32_dma2d_lpfc
   *
   * Description:
   *   Configure foreground and background layer PFC control register
   *
   * Parameter:
   *   layer - Reference to the common layer state structure
   *
   ****************************************************************************/
  
  static void stm32_dma2d_lpfc(FAR const struct stm32_dma2d_s *layer,
                                int lid, uint32_t blendmode)
  {
    uint32_t   pfccrreg;
  
    ginfo("layer=%p, lid=%d, blendmode=%08x\n", layer, lid, blendmode);
  
    /* Set color format */
  
    pfccrreg = DMA2D_xGPFCCR_CM(layer->fmt);
  
  #ifdef CONFIG_STM32_DMA2D_L8
    if (layer->fmt == DMA2D_PF_L8)
      {
        /* Load CLUT automatically */
  
        pfccrreg |= DMA2D_xGPFCCR_START;
  
        /* Set the CLUT color mode */
  
  #ifndef CONFIG_FB_TRANSPARENCY
        pfccrreg |= DMA2D_xGPFCCR_CCM;
  #endif
  
        /* Set CLUT size */
  
        pfccrreg |= DMA2D_xGPFCCR_CS(DMA2D_CLUT_SIZE);
  
        /* Set the CLUT memory address */
  
        putreg32((uint32_t) layer->clut, stm32_cmar_layer_t[lid]);
  
        /* Start async clut loading */
  
        stm32_dma2d_loadclut(stm32_pfccr_layer_t[lid]);
      }
  #endif
  
    if (blendmode & DMA2D_BLEND_NONE)
      {
        /* No blend operation */
  
        pfccrreg |= DMA2D_xGPFCCR_AM(STM32_DMA2D_PFCCR_AM_NONE);
      }
    else
      {
        /* Set alpha value */
  
        pfccrreg |= DMA2D_xGPFCCR_ALPHA(layer->alpha);
  
        /* Set alpha mode */
  
        if (layer->blendmode & DMA2D_BLEND_ALPHA)
          {
            /* Blend with constant alpha */
  
            pfccrreg |= DMA2D_xGPFCCR_AM(STM32_DMA2D_PFCCR_AM_CONST);
          }
        else if (layer->blendmode & DMA2D_BLEND_PIXELALPHA)
          {
            /* Blend with pixel alpha value */
  
            pfccrreg |= DMA2D_xGPFCCR_AM(STM32_DMA2D_PFCCR_AM_PIXEL);
          }
      }
  
    putreg32(pfccrreg, stm32_pfccr_layer_t[lid]);
  }
  
  /****************************************************************************
   * Public Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Name: stm32_dma2dgetvideoinfo
   *
   * Description:
   *   Get video information about the layer
   *
   * Parameter:
   *   layer  - Reference to the layer control structure
   *   vinfo  - Reference to the video info structure
   *
   * Return:
   *   On success - OK
   *   On error   - -EINVAL
   *
   ****************************************************************************/
  
  static int stm32_dma2dgetvideoinfo(FAR struct dma2d_layer_s *layer,
                              FAR struct fb_videoinfo_s *vinfo)
  {
    FAR struct stm32_dma2d_s *priv = (FAR struct stm32_dma2d_s *)layer;
  
    ginfo("layer=%p, vinfo=%p\n", layer, vinfo);
  
    if (stm32_dma2d_lvalidate(priv) && vinfo)
      {
        sem_wait(priv->lock);
        memcpy(vinfo, &priv->vinfo, sizeof(struct fb_videoinfo_s));
        sem_post(priv->lock);
  
        return OK;
      }
  
    gerr("ERROR: Returning EINVAL\n");
    return -ENOSYS;
  }
  
  /****************************************************************************
   * Name: stm32_dma2dgetplaneinfo
   *
   * Description:
   *   Get plane information about the layer
   *
   * Parameter:
   *   layer   - Reference to the layer control structure
   *   planeno - Number of the plane
   *   pinfo   - Reference to the plane info structure
   *
   * Return:
   *   On success - OK
   *   On error   - -EINVAL
   *
   ****************************************************************************/
  
  static int stm32_dma2dgetplaneinfo(FAR struct dma2d_layer_s *layer, int planeno,
                                FAR struct fb_planeinfo_s *pinfo)
  {
    FAR struct stm32_dma2d_s *priv = (FAR struct stm32_dma2d_s *)layer;
  
    ginfo("layer=%p, planeno=%d, pinfo=%p\n", layer, planeno, pinfo);
  
    if (stm32_dma2d_lvalidate(priv) && pinfo && planeno == 0)
      {
        sem_wait(priv->lock);
        memcpy(pinfo, &priv->pinfo, sizeof(struct fb_planeinfo_s));
        sem_post(priv->lock);
  
        return OK;
      }
  
    gerr("ERROR: Returning EINVAL\n");
    return -EINVAL;
  }
  
  /****************************************************************************
   * Name: stm32_dma2dgetlid
   *
   * Description:
   *   Get a specific layer identifier.
   *
   * Parameter:
   *   layer - Reference to the layer structure
   *   lid   - Reference to store the layer id
   *
   * Return:
   *   On success - OK
   *   On error   - -EINVAL
   *
   ****************************************************************************/
  
  static int stm32_dma2dgetlid(FAR struct dma2d_layer_s *layer, int *lid)
  {
    FAR struct stm32_dma2d_s *priv = (FAR struct stm32_dma2d_s *)layer;
  
    ginfo("layer=%p, lid=%p\n", layer, lid);
  
    if (stm32_dma2d_lvalidate(priv) && lid)
      {
        sem_wait(priv->lock);
        *lid = priv->lid;
        sem_post(priv->lock);
        return OK;
      }
  
    gerr("ERROR: Returning EINVAL\n");
    return -EINVAL;
  }
  
  #ifdef CONFIG_STM32_DMA2D_L8
  /****************************************************************************
   * Name: stm32_dma2dsetclut
   *
   * Description:
   *   Configure layer clut (color lookup table).
   *   Non clut is defined during initializing.
   *
   * Parameter:
   *   layer  - Reference to the layer structure
   *   cmap   - color lookup table with up the 256 entries
   *
   * Return:
   *   On success - OK
   *   On error   - -EINVAL
   *
   ****************************************************************************/
  
  static int stm32_dma2dsetclut(FAR struct dma2d_layer_s *layer,
                              const FAR struct fb_cmap_s *cmap)
  {
    int   ret;
    FAR struct stm32_dma2d_s *priv = (FAR struct stm32_dma2d_s *)layer;
  
    ginfo("layer=%p, cmap=%p\n", layer, cmap);
  
    if (stm32_dma2d_lvalidate(priv) && cmap)
      {
        sem_wait(priv->lock);
  
  #ifdef CONFIG_STM32_LTDC_INTERFACE
        if (priv->lid < DMA2D_SHADOW_LAYER)
          {
            /* Update the shared color lookup table.
             *
             * Background:
             *
             *  We share the same memory region of the clut table with the LTDC
             *  driver. (see stm32_dma2dinitltdc). This is important because any
             *  changes to the framebuffer and color lookup table by the ltdc
             *  related dma2d layer should also effects to the ltdc visibility,
             *  except operation settings, alpha and blendmode.
             *
             *  But we can not only update the clut memory region. The LTDC driver
             *  also must update they own LTDC clut register to make the changes
             *  visible. Using the LTDC interface to update the clut table will
             *  also update the clut table of the related dma2d layer.
             */
  
            FAR struct ltdc_layer_s *ltdc =
                g_ltdc_layer.layer[DMA2D_SHADOW_LAYER_L1].ltdc;
  
            ret = ltdc->setclut(ltdc, cmap);
  
            sem_post(priv->lock);
  
            return ret;
          }
  #endif
  
        if (priv->fmt != DMA2D_PF_L8)
          {
            gerr("Error: CLUT is not supported for the pixel format: %d\n",
                      priv->vinfo.fmt);
            ret = -EINVAL;
          }
        else if (cmap->first >= STM32_DMA2D_NCLUT)
          {
            gerr("Error: only %d color table entries supported\n",
                      STM32_DMA2D_NCLUT);
            ret = -EINVAL;
          }
        else
          {
            uint32_t *clut;
            int      n;
  
            clut = priv->clut;
  
            for (n = cmap->first; n < cmap->len && n < STM32_DMA2D_NCLUT; n++)
              {
                /* Update the layer clut entry */
  
  #ifndef CONFIG_FB_TRANSPARENCY
                uint8_t  *clut888 = (uint8_t *)clut;
                uint16_t offset   = 3 * n;
  
                clut888[offset]     = cmap->blue[n];
                clut888[offset + 1] = cmap->green[n];
                clut888[offset + 2] = cmap->red[n];
  
                reginfo("n=%d, red=%02x, green=%02x, blue=%02x\n", n,
                          clut888[offset], clut888[offset + 1],
                          clut888[offset + 2]);
  #else
                clut[n] = (uint32_t)DMA2D_CLUT_RED(cmap->transp[n]) |
                          (uint32_t)DMA2D_CLUT_GREEN(cmap->red[n]) |
                          (uint32_t)DMA2D_CLUT_GREEN(cmap->green[n]) |
                          (uint32_t)DMA2D_CLUT_BLUE(cmap->blue[n]);
  
                reginfo("n=%d, alpha=%02x, red=%02x, green=%02x, blue=%02x\n", n,
                          DMA2D_CLUT_ALPHA(cmap->alpha[n]),
                          DMA2D_CLUT_RED(cmap->red[n]),
                          DMA2D_CLUT_GREEN(cmap->green[n]),
                          DMA2D_CLUT_BLUE(cmap->blue[n]));
  #endif
              }
  
  
            ret = OK;
          }
  
        sem_post(priv->lock);
        return ret;
      }
  
    gerr("ERROR: Returning EINVAL\n");
    return -EINVAL;
  }
  
  /****************************************************************************
   * Name: stm32_dma2dgetclut
   *
   * Description:
   *   Get configured layer clut (color lookup table).
   *
   * Parameter:
   *   layer - Reference to the layer structure
   *   cmap  - Reference to valid color lookup table accept up the 256 color
   *           entries
   *
   * Return:
   *   On success - OK
   *   On error   - -EINVAL
   *
   ****************************************************************************/
  
  static int stm32_dma2dgetclut(FAR struct dma2d_layer_s *layer,
                              FAR struct fb_cmap_s *cmap)
  {
    int ret;
    FAR struct stm32_dma2d_s *priv = (FAR struct stm32_dma2d_s *)layer;
  
    ginfo("layer=%p, cmap=%p\n", layer, cmap);
  
    if (stm32_dma2d_lvalidate(priv) && cmap)
      {
        sem_wait(priv->lock);
  
        if (priv->fmt != DMA2D_PF_L8)
          {
            gerr("Error: CLUT is not supported for the pixel format: %d\n",
                      priv->vinfo.fmt);
            ret = -EINVAL;
          }
        else if (cmap->first >= STM32_DMA2D_NCLUT)
          {
            gerr("Error: only %d color table entries supported\n",
                      STM32_DMA2D_NCLUT);
            ret = -EINVAL;
          }
        else
          {
            /* Copy from the layer clut */
  
            uint32_t *clut;
            int      n;
  
            clut = priv->clut;
  
            for (n = cmap->first; n < cmap->len && n < STM32_DMA2D_NCLUT; n++)
              {
  #ifndef CONFIG_FB_TRANSPARENCY
                uint8_t  *clut888 = (uint8_t *)clut;
                uint16_t offset   = 3 * n;
  
                cmap->blue[n]   = clut888[offset];
                cmap->green[n]  = clut888[offset + 1];
                cmap->red[n]    = clut888[offset + 2];
  
                reginfo("n=%d, red=%02x, green=%02x, blue=%02x\n", n,
                          clut888[offset], clut888[offset + 1],
                          clut888[offset + 2]);
  #else
                cmap->transp[n] = (uint8_t)DMA2D_CMAP_ALPHA(clut[n]);
                cmap->red[n]    = (uint8_t)DMA2D_CMAP_RED(clut[n]);
                cmap->green[n]  = (uint8_t)DMA2D_CMAP_GREEN(clut[n]);
                cmap->blue[n]   = (uint8_t)DMA2D_CMAP_BLUE(clut[n]);
  
                reginfo("n=%d, alpha=%02x, red=%02x, green=%02x, blue=%02x\n", n,
                          DMA2D_CMAP_ALPHA(clut[n]), DMA2D_CMAP_RED(clut[n]),
                          DMA2D_CMAP_GREEN(clut[n]), DMA2D_CMAP_BLUE(clut[n]));
  #endif
              }
  
            ret = OK;
          }
  
        sem_post(priv->lock);
  
        return ret;
      }
  
    gerr("ERROR: Returning EINVAL\n");
    return -EINVAL;
  }
  #endif
  
  /****************************************************************************
   * Name: stm32_dma2dsetalpha
   *
   * Description:
   *   Configure layer alpha value factor into blend operation.
   *   During the layer blend operation the source alpha value is multiplied
   *   with this alpha value. If the source color format doesn't support alpha
   *   channel (e.g. non ARGB8888) this alpha value will be used as constant
   *   alpha value for blend operation.
   *   Default value during initializing: 0xff
   *
   * Parameter:
   *   layer - Reference to the layer structure
   *   alpha - Alpha value
   *
   * Return:
   *   On success - OK
   *   On error - -EINVAL
   *
   ****************************************************************************/
  
  static int stm32_dma2dsetalpha(FAR struct dma2d_layer_s *layer, uint8_t alpha)
  {
    FAR struct stm32_dma2d_s *priv = (FAR struct stm32_dma2d_s *)layer;
  
    ginfo("layer=%p, alpha=%02x\n", layer, alpha);
  
    if (stm32_dma2d_lvalidate(priv))
      {
        sem_wait(priv->lock);
        priv->alpha = alpha;
        sem_post(priv->lock);
  
        return OK;
      }
  
    gerr("ERROR: Returning EINVAL\n");
    return -EINVAL;
  }
  
  /****************************************************************************
   * Name: stm32_dma2dgetalpha
   *
   * Description:
   *   Get configured layer alpha value factor for blend operation.
   *
   * Parameter:
   *   layer - Reference to the layer structure
   *   alpha - Reference to store the alpha value
   *
   * Return:
   *   On success - OK
   *   On error - -EINVAL
   *
   ****************************************************************************/
  
  static int stm32_dma2dgetalpha(FAR struct dma2d_layer_s *layer, uint8_t *alpha)
  {
    FAR struct stm32_dma2d_s *priv = (FAR struct stm32_dma2d_s *)layer;
  
    ginfo("layer=%p, alpha=%p\n", layer, alpha);
  
    if (stm32_dma2d_lvalidate(priv))
      {
        sem_wait(priv->lock);
        *alpha = priv->alpha;
        sem_post(priv->lock);
  
        return OK;
      }
  
    gerr("ERROR: Returning EINVAL\n");
    return -EINVAL;
  }
  
  /****************************************************************************
   * Name: stm32_dma2dsetblendmode
   *
   * Description:
   *   Configure blend mode of the layer.
   *   Default mode during initializing: DMA2D_BLEND_NONE
   *   Blendmode is active after next update.
   *
   * Parameter:
   *   layer - Reference to the layer structure
   *   mode  - Blend mode (see DMA2D_BLEND_*)
   *
   * Return:
   *   On success - OK
   *   On error - -EINVAL
   *
   * Procedure information:
   *   DMA2D_BLEND_NONE:
   *     Informs the driver to disable all blend operation for the given layer.
   *     That means the layer is opaque.
   *
   *   DMA2D_BLEND_ALPHA:
   *     Informs the driver to enable alpha blending for the given layer.
   *
   *   DMA2D_BLEND_PIXELALPHA:
   *     Informs the driver to use the pixel alpha value of the layer instead
   *     the constant alpha value. This is only useful for ARGB8888
   *     color format.
   *
   ****************************************************************************/
  
  static int stm32_dma2dsetblendmode(FAR struct dma2d_layer_s *layer,
                                      uint32_t mode)
  {
    FAR struct stm32_dma2d_s *priv = (FAR struct stm32_dma2d_s *)layer;
  
    ginfo("layer=%p, mode=%08x\n", layer, mode);
  
    if (stm32_dma2d_lvalidate(priv))
      {
        sem_wait(priv->lock);
        priv->blendmode = mode;
        sem_post(priv->lock);
  
        return OK;
      }
  
    gerr("ERROR: Returning EINVAL\n");
    return -EINVAL;
  }
  
  /****************************************************************************
   * Name: stm32_getblendmode
   *
   * Description:
   *   Get configured blend mode of the layer.
   *
   * Parameter:
   *   layer - Reference to the layer structure
   *   mode  - Reference to store the blend mode
   *
   * Return:
   *   On success - OK
   *   On error - -EINVAL
   *
   ****************************************************************************/
  
  static int stm32_dma2dgetblendmode(FAR struct dma2d_layer_s *layer,
                                      uint32_t *mode)
  {
    FAR struct stm32_dma2d_s *priv = (FAR struct stm32_dma2d_s *)layer;
  
    ginfo("layer=%p, mode=%p\n", layer, mode);
  
    if (stm32_dma2d_lvalidate(priv) && mode)
      {
        sem_wait(priv->lock);
        *mode = priv->blendmode;
        sem_post(priv->lock);
  
        return OK;
      }
  
    gerr("ERROR: Returning EINVAL\n");
    return -EINVAL;
  }
  
  /****************************************************************************
   * Name: stm32_dma2dblit
   *
   * Description:
   *   Copy selected area from a source layer to selected position of the
   *   destination layer.
   *
   * Parameter:
   *   dest     - Valid reference to the destination layer
   *   destxpos - Valid selected x position of the destination layer
   *   destypos - Valid selected y position of the destination layer
   *   src      - Valid reference to the source layer
   *   srcarea  - Valid reference to the selected area of the source layer
   *
   * Return:
   *    OK        - On success
   *   -EINVAL    - If one of the parameter invalid or if the size of the selected
   *                source area outside the visible area of the destination layer.
   *                (The visible area usually represents the display size)
   *   -ECANCELED - Operation cancelled, something goes wrong.
   *
   ****************************************************************************/
  
  static int stm32_dma2dblit(FAR struct dma2d_layer_s *dest,
                              fb_coord_t destxpos, fb_coord_t destypos,
                              FAR const struct dma2d_layer_s *src,
                              FAR const struct ltdc_area_s *srcarea)
  {
    uint32_t   mode;
    int        ret;
    FAR struct stm32_dma2d_s * destlayer = (FAR struct stm32_dma2d_s *)dest;
    FAR struct stm32_dma2d_s * srclayer = (FAR struct stm32_dma2d_s *)src;
  
    ginfo("dest=%p, destxpos=%d, destypos=%d, src=%p, srcarea=%p\n",
              dest, destxpos, destypos, src, srcarea);
  
    if (stm32_dma2d_lvalidatesize(destlayer, destxpos, destypos, srcarea) &&
          stm32_dma2d_lvalidatesize(srclayer, srcarea->xpos,
                                      srcarea->ypos, srcarea))
      {
        sem_wait(destlayer->lock);
  
        /* Set output pfc */
  
        ret = stm32_dma2d_loutpfc(destlayer);
  
        if (ret == OK)
          {
            /* Set foreground pfc */
  
            stm32_dma2d_lpfc(srclayer, DMA2D_LAYER_LFORE, DMA2D_BLEND_NONE);
  
            /* Set foreground fifo */
  
            stm32_dma2d_lfifo(srclayer, DMA2D_LAYER_LFORE,
                              srcarea->xpos, srcarea->ypos, srcarea);
  
            /* Set output fifo */
  
            stm32_dma2d_lfifo(destlayer, DMA2D_LAYER_LOUT,
                              destxpos, destypos, srcarea);
  
            /* Set number of lines and pixel per line */
  
            stm32_dma2d_llnr(destlayer, srcarea);
  
            /* Set dma2d mode for blit operation */
  
            if (destlayer->fmt == srclayer->fmt)
              {
                /* Blit without pfc */
  
                mode = STM32_DMA2D_CR_MODE_BLIT;
              }
            else
              {
                /* Blit with pfc */
  
                mode = STM32_DMA2D_CR_MODE_BLITPFC;
              }
  
            stm32_dma2d_control(mode, STM32_DMA2D_CR_MODE_CLEAR);
  
            /* Start DMA2D and wait until completed */
  
            ret = stm32_dma2d_start();
  
            if (ret != OK)
              {
                ret = -ECANCELED;
                gerr("ERROR: Returning ECANCELED\n");
              }
          }
  
        sem_post(destlayer->lock);
      }
    else
      {
        ret = -EINVAL;
        gerr("ERROR: Returning EINVAL\n");
      }
  
    return ret;
  }
  
  /****************************************************************************
   * Name: stm32_dma2dblend
   *
   * Description:
   *   Blends the selected area from a background layer with selected position
   *   of the foreground layer. Copies the result to the selected position of
   *   the destination layer. Note! The content of the foreground and background
   *   layer keeps unchanged as long destination layer is unequal to the
   *   foreground and background layer.
   *
   * Parameter:
   *   dest     - Reference to the destination layer
   *   fore     - Reference to the foreground layer
   *   forexpos - Selected x target position of the foreground layer
   *   foreypos - Selected y target position of the foreground layer
   *   back     - Reference to the background layer
   *   backarea - Reference to the selected area of the background layer
   *
   * Return:
   *    OK        - On success
   *   -EINVAL    - If one of the parameter invalid or if the size of the selected
   *                source area outside the visible area of the destination layer.
   *                (The visible area usually represents the display size)
   *   -ECANCELED - Operation cancelled, something goes wrong.
   *
   ****************************************************************************/
  
  static int stm32_dma2dblend(FAR struct dma2d_layer_s *dest,
                              fb_coord_t destxpos, fb_coord_t destypos,
                              FAR const struct dma2d_layer_s *fore,
                              fb_coord_t forexpos, fb_coord_t foreypos,
                              FAR const struct dma2d_layer_s *back,
                              FAR const struct ltdc_area_s *backarea)
  {
    int    ret;
    FAR struct stm32_dma2d_s * destlayer = (FAR struct stm32_dma2d_s *)dest;
    FAR struct stm32_dma2d_s * forelayer = (FAR struct stm32_dma2d_s *)fore;
    FAR struct stm32_dma2d_s * backlayer = (FAR struct stm32_dma2d_s *)back;
  
    ginfo("dest=%p, destxpos=%d, destypos=%d, "
          "fore=%p, forexpos=%d, foreypos=%d, "
          "back=%p, backarea=%p\n",
          dest, destxpos, destypos, fore, forexpos, foreypos, back, backarea);
  
    if (stm32_dma2d_lvalidatesize(destlayer, destxpos, destypos, backarea) &&
          stm32_dma2d_lvalidatesize(forelayer, forexpos, foreypos, backarea) &&
              stm32_dma2d_lvalidatesize(backlayer, backarea->xpos,
                                          backarea->ypos, backarea))
      {
  
        sem_wait(destlayer->lock);
  
        /* Set output pfc */
  
        ret = stm32_dma2d_loutpfc(destlayer);
  
        if (ret == OK)
          {
            /* Set background pfc */
  
            stm32_dma2d_lpfc(backlayer, DMA2D_LAYER_LBACK, backlayer->blendmode);
  
            /* Set foreground pfc */
  
            stm32_dma2d_lpfc(forelayer, DMA2D_LAYER_LFORE, forelayer->blendmode);
  
            /* Set background fifo */
  
            stm32_dma2d_lfifo(backlayer, DMA2D_LAYER_LBACK,
                                  backarea->xpos, backarea->ypos, backarea);
  
            /* Set foreground fifo */
  
            stm32_dma2d_lfifo(forelayer, DMA2D_LAYER_LFORE,
                                  forexpos, foreypos, backarea);
  
            /* Set output fifo */
  
            stm32_dma2d_lfifo(destlayer, DMA2D_LAYER_LOUT,
                                  destxpos, destypos, backarea);
  
            /* Set number of lines and pixel per line */
  
            stm32_dma2d_llnr(destlayer, backarea);
  
            /* Set watermark */
  
            /* Enable DMA2D blender */
  
            stm32_dma2d_control(STM32_DMA2D_CR_MODE_BLEND,
                                  STM32_DMA2D_CR_MODE_CLEAR);
  
            /* Start DMA2D and wait until completed */
  
            ret = stm32_dma2d_start();
  
            if (ret != OK)
              {
                ret = -ECANCELED;
                gerr("ERROR: Returning ECANCELED\n");
              }
          }
  
        sem_post(destlayer->lock);
      }
    else
      {
        ret = -EINVAL;
        gerr("ERROR: Returning EINVAL\n");
      }
  
    return ret;
  }
  
  /****************************************************************************
   * Name: stm32_dma2dfillarea
   *
   * Description:
   *   Fill the selected area of the whole layer with a specific color.
   *
   * Parameter:
   *   layer    - Reference to the layer structure
   *   area     - Reference to the valid area structure select the area
   *   color    - Color to fill the selected area. Color must be formatted
   *              according to the layer pixel format.
   *
   * Return:
   *    OK        - On success
   *   -EINVAL    - If one of the parameter invalid or if the size of the selected
   *                area outside the visible area of the layer.
   *   -ECANCELED - Operation cancelled, something goes wrong.
   *
   ****************************************************************************/
  
  static int stm32_dma2dfillarea(FAR struct dma2d_layer_s *layer,
                                 FAR const struct ltdc_area_s *area,
                                 uint32_t color)
  {
    int ret;
    FAR struct stm32_dma2d_s *priv = (FAR struct stm32_dma2d_s *)layer;
  
    ginfo("layer=%p, area=%p, color=%08x\n", layer, area, color);
  
    if (stm32_dma2d_lvalidatesize(priv, area->xpos, area->ypos, area))
      {
  
        sem_wait(priv->lock);
  
        /* Set output pfc */
  
        ret = stm32_dma2d_loutpfc(priv);
  
        if (ret == OK)
          {
            /* Set output fifo */
  
            stm32_dma2d_lfifo(priv, DMA2D_LAYER_LOUT,
                                  area->xpos, area->ypos, area);
  
            /* Set the output color register */
  
            stm32_dma2d_lcolor(priv, DMA2D_LAYER_LOUT, color);
  
            /* Set number of lines and pixel per line */
  
            stm32_dma2d_llnr(priv, area);
  
            /* Set register to memory transfer */
  
            stm32_dma2d_control(STM32_DMA2D_CR_MODE_COLOR,
                                  STM32_DMA2D_CR_MODE_CLEAR);
  
            /* Start DMA2D and wait until completed */
  
            ret = stm32_dma2d_start();
  
            if (ret != OK)
              {
                ret = -ECANCELED;
                gerr("ERROR: Returning ECANCELED\n");
              }
          }
  
        sem_post(priv->lock);
      }
    else
      {
        ret = -EINVAL;
        gerr("ERROR: Returning EINVAL\n");
      }
  
    return ret;
  }
  
  /****************************************************************************
   * Name: up_dma2dgetlayer
   *
   * Description:
   *   Get a dma2d layer structure by the layer identifier
   *
   * Parameter:
   *   lid - Layer identifier
   *
   * Return:
   *   Reference to the dma2d layer control structure on success or Null if no
   *   related exist.
   *
   ****************************************************************************/
  
  FAR struct dma2d_layer_s * up_dma2dgetlayer(int lid)
  {
    if (lid < DMA2D_LAYER_NSIZE)
      {
        FAR struct stm32_dma2d_s *priv;
        sem_wait(&g_lock);
        priv = g_layers[lid];
        sem_post(&g_lock);
  
        return &priv->dma2d;
      }
  
    gerr("ERROR: EINVAL, Unknown layer identifier\n");
    errno = EINVAL;
    return NULL;
  }
  
  /****************************************************************************
   * Name: up_dma2dcreatelayer
   *
   * Description:
   *   Create a new dma2d layer object to interact with the dma2d controller
   *
   * Parameter:
   *   width  - Layer width
   *   height - Layer height
   *   fmt    - Pixel format of the layer
   *
   * Return:
   *   On success - A valid dma2d layer reference
   *   On error   - NULL and errno is set to
   *                -EINVAL if one of the parameter is invalid
   *                -ENOMEM if no memory available or exceeds
   *                 CONFIG_STM32_DMA2D_NLAYERS
   *
   ****************************************************************************/
  
  FAR struct dma2d_layer_s *up_dma2dcreatelayer(fb_coord_t width,
                                                fb_coord_t height,
                                                uint8_t fmt)
  {
    int        ret;
    int        lid;
    uint8_t    fmtmap;
    uint8_t    bpp = 0;
    FAR struct stm32_dma2d_s *layer = NULL;
  
    ginfo("width=%d, height=%d, fmt=%02x \n", width, height, fmt);
  
    /* Validate if pixel format supported */
  
    ret = stm32_dma2d_pixelformat(fmt, &fmtmap);
  
    if (ret != OK)
      {
        errno = -ret;
        return NULL;
      }
  
    ret = stm32_dma2d_bpp(fmt, &bpp);
  
    sem_wait(&g_lock);
  
    /* Get a free layer identifier */
  
    lid = stm32_dma2d_lfreelid();
  
    if (lid >= 0)
      {
        layer = stm32_dma2d_lalloc();
  
        if (layer)
          {
            uint32_t   fblen;
            void       *fbmem;
            fb_coord_t stride;
  
            /* Stride calculation for the supported formats */
  
            stride = width * bpp / 8;
  
            /* Calculate buffer size */
  
            fblen = stride * height;
  
            /* Allocate 32-bit aligned memory for the layer buffer. As reported in
             * mm_memalign 8-byte alignment is guaranteed by normal malloc calls.
             * We have also ensure memory is allocated from the SRAM1/2/3 block.
             * The CCM block is only accessible through the D-BUS but not by
             * the AHB-BUS. Ensure that CONFIG_STM32_CCMEXCLUDE is set!
             */
  
            fbmem = kmm_zalloc(fblen);
  
            if (fbmem)
              {
                FAR struct fb_videoinfo_s *vinfo = &layer->vinfo;
                FAR struct fb_planeinfo_s *pinfo = &layer->pinfo;
  
                /* Initialize dma2d structure */
  
                stm32_dma2d_linit(layer, lid, fmtmap);
  
                /* Initialize the videoinfo structure */
  
                vinfo->fmt      = fmt;
                vinfo->xres     = width;
                vinfo->yres     = height;
                vinfo->nplanes  = 1;
  
                /* Initialize the planeinfo structure */
  
                pinfo->fbmem   = fbmem;
                pinfo->fblen   = fblen;
                pinfo->stride  = stride;
                pinfo->display = 0;
                pinfo->bpp     = bpp;
  
                /* Bind the layer to the identifier */
  
                g_layers[lid] = layer;
              }
            else
              {
                /* free the layer struture */
  
                kmm_free(layer);
                gerr("ERROR: ENOMEM, Unable to allocate layer buffer\n");
                errno = ENOMEM;
              }
          }
        else
          {
            gerr("ERROR: ENOMEM, unable to allocate layer structure\n");
            errno = ENOMEM;
          }
      }
    else
      {
        gerr("ERROR: EINVAL, no free layer available\n");
        errno = EINVAL;
      }
  
    sem_post(&g_lock);
    return (FAR struct dma2d_layer_s *)layer;
  }
  
  /****************************************************************************
   * Name: up_dma2dremovelayer
   *
   * Description:
   *  Remove and deallocate the dma2d layer
   *
   * Parameter:
   *   layer  - Reference to the layer to remove
   *
   * Return:
   *   On success - OK
   *   On error   - -EINVAL
   *
   ****************************************************************************/
  
  int up_dma2dremovelayer(FAR struct dma2d_layer_s *layer)
  {
    int ret = -EINVAL;
    FAR struct stm32_dma2d_s *priv = (FAR struct stm32_dma2d_s *)layer;
  
    /* Check if the layer is valid and unlike a ltdc related layer */
  
    if (stm32_dma2d_lvalidate(priv) && priv->lid >= DMA2D_SHADOW_LAYER)
      {
        sem_wait(priv->lock);
  
        /* Check also if the layer id is valid to the layer reference */
  
        if (priv == g_layers[priv->lid])
          {
            int lid = priv->lid;
  
            kmm_free(priv->pinfo.fbmem);
            stm32_dma2d_lfree(priv);
  
            g_layers[lid] = NULL;
            ret = OK;
          }
  
        sem_post(priv->lock);
      }
  
    return ret;
  }
  
  /****************************************************************************
   * Name: up_dma2dinitialize
   *
   * Description:
   *   Initialize the dma2d controller
   *
   * Return:
   *   OK - On success
   *   An error if initializing failed.
   *
   ****************************************************************************/
  
  int up_dma2dinitialize(void)
  {
    err("Initialize DMA2D driver\n");
  
    if (g_initialized == false)
      {
        /* Abort current dma2d data transfer */
  
        up_dma2duninitialize();
  
        /* Enable dma2d is done in rcc_enableahb1, see
         * arch/arm/src/stm32/stm32f40xxx_rcc.c
         */
  
        /* Initialize the DMA2D semaphore that enforces mutually exclusive access
         * to the driver
         */
  
        sem_init(&g_lock, 0, 1);
  
        /* Initialize the semaphore for interrupt handling */
  
        sem_init(g_interrupt.sem, 0, 0);
  
  #ifdef CONFIG_STM32_DMA2D_L8
        /* Enable dma2d transfer and clut loading interrupts only */
  
        stm32_dma2d_control(DMA2D_CR_TCIE | DMA2D_CR_CTCIE, DMA2D_CR_TEIE |
                            DMA2D_CR_TWIE | DMA2D_CR_CAEIE | DMA2D_CR_CEIE);
  #else
        /* Enable dma transfer interrupt only */
  
        stm32_dma2d_control(DMA2D_CR_TCIE, DMA2D_CR_TEIE | DMA2D_CR_TWIE |
                            DMA2D_CR_CAEIE | DMA2D_CR_CTCIE | DMA2D_CR_CEIE);
  #endif
  
        /* Attach DMA2D interrupt vector */
  
        (void)irq_attach(g_interrupt.irq, stm32_dma2dirq);
  
        /* Enable the IRQ at the NVIC */
  
        up_enable_irq(g_interrupt.irq);
  
        /* Initialize the dma2d layer for ltdc binding */
  
  #ifdef DMA2D_SHADOW_LAYER_L1
        g_layers[DMA2D_SHADOW_LAYER_L1] =
          &g_ltdc_layer.layer[DMA2D_SHADOW_LAYER_L1].dma2ddev;
  #endif
  #ifdef DMA2D_SHADOW_LAYER_L2
        g_layers[DMA2D_SHADOW_LAYER_L2] =
          &g_ltdc_layer.layer[DMA2D_SHADOW_LAYER_L2].dma2ddev;
  #endif
        /* Set initialized state */
  
        g_initialized = true;
      }
  
    return OK;
  }
  
  /****************************************************************************
   * Name: up_dma2duninitialize
   *
   * Description:
   *   Uninitialize the dma2d controller
   *
   ****************************************************************************/
  
  void up_dma2duninitialize(void)
  {
    /* Disable DMA2D interrupts */
  
    up_disable_irq(g_interrupt.irq);
    irq_detach(g_interrupt.irq);
  
    /* Cleanup all layers */
  
    stm32_dma2d_llayerscleanup();
  
    /* Abort current dma2d transfer */
  
    stm32_dma2d_control(DMA2D_CR_ABORT, 0);
  
    /* Set initialized state */
  
    g_initialized = false;
  }
  
  #ifdef CONFIG_STM32_LTDC_INTERFACE
  /****************************************************************************
   * Name: stm32_dma2dinitltdc
   *
   * Description:
   *   Get a reference to the dma2d layer coupled with the ltdc layer.
   *   It not intends to use this by user space applications.
   *   It resolves the following requirements:
   *   1. Share the color lookup table
   *   2. Share the planeinfo information
   *   3. Share the videoinfo information
   *
   * Parameter:
   *   layer  - a valid reference to the low level ltdc layer structure
   *   clut   - a pointer to a valid memory region to hold 256 clut colors
   *
   * Return:
   *   On success - A valid dma2d layer reference
   *   On error   - NULL and errno is set to
   *                -EINVAL if one of the parameter is invalid
   *
   ****************************************************************************/
  
  FAR struct dma2d_layer_s * stm32_dma2dinitltdc(FAR struct stm32_ltdc_s *layer)
  {
    int        ret;
    uint8_t    fmt = 0;
    FAR struct stm32_ltdc_dma2d_s *priv;
  
    ginfo("layer=%p\n", layer);
    DEBUGASSERT(layer && layer->lid >= 0 && layer->lid < DMA2D_SHADOW_LAYER);
  
    ret = stm32_dma2d_pixelformat(layer->vinfo.fmt, &fmt);
  
    if (ret != OK)
      {
        err("Returning -EINVAL, unsupported pixel format: %d\n",
              layer->vinfo.fmt);
        errno = -EINVAL;
        return NULL;
      }
  
    priv = &g_ltdc_layer.layer[layer->lid];
  
    stm32_dma2d_linit(&priv->dma2ddev, layer->lid, fmt);
  
    memcpy(&priv->dma2ddev.vinfo, &layer->vinfo, sizeof(struct fb_videoinfo_s));
    memcpy(&priv->dma2ddev.pinfo, &layer->pinfo, sizeof(struct fb_planeinfo_s));
  
  #ifdef CONFIG_STM32_DMA2D_L8
    /* Verifies that the ltdc layer has a clut. This ensures that DMA2D driver can
     * support clut format but the LTDC driver does not and vice versa.
     */
  
    if (layer->vinfo.fmt == FB_FMT_RGB8)
      {
        priv->dma2ddev.clut = layer->clut;
        priv->ltdc = stm32_ltdcgetlayer(layer->lid);
        DEBUGASSERT(priv->ltdc != NULL);
      }
  #endif
  
    return &priv->dma2ddev.dma2d;
  }
  #endif /* CONFIG_STM32_LTDC_INTERFACE */
  