diff options
Diffstat (limited to 'nuttx/arch/arm/src/stm32/stm32_dma2d.c')
-rw-r--r-- | nuttx/arch/arm/src/stm32/stm32_dma2d.c | 2208 |
1 files changed, 2169 insertions, 39 deletions
diff --git a/nuttx/arch/arm/src/stm32/stm32_dma2d.c b/nuttx/arch/arm/src/stm32/stm32_dma2d.c index 90396e561..f999d1b08 100644 --- a/nuttx/arch/arm/src/stm32/stm32_dma2d.c +++ b/nuttx/arch/arm/src/stm32/stm32_dma2d.c @@ -1,7 +1,7 @@ /****************************************************************************** * arch/arm/src/stm32/stm32_dma2d.c * - * Copyright (C) 2014 Marco Krahl. All rights reserved. + * Copyright (C) 2014-2015 Marco Krahl. All rights reserved. * Author: Marco Krahl <ocram.lhark@gmail.com> * * References: @@ -46,35 +46,344 @@ #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/ltdc.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 "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 regdbg dbg +# define regvdbg vdbg +#else +# define regdbg(x...) +# define regvdbg(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 ******************************************************************************/ @@ -84,72 +393,1754 @@ ******************************************************************************/ /****************************************************************************** + * 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; + + gvdbg("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; + + regvdbg("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) + { + dbg("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 irqsave state. + * + * 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) + { + dbg("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 = irqsave(); + + 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; + regvdbg("set regval=%08x\n", regval); + putreg32(regval, pfcreg); + regvdbg("configured regval=%08x\n", getreg32(pfcreg)); + } + + irqrestore(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 = irqsave(); + + 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(); + } + + irqrestore(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; + + gvdbg("%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 */ + + gvdbg("%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) +{ + gvdbg("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: + gdbg("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) +{ + gvdbg("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: + gdbg("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; + + gvdbg("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) +{ + gvdbg("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) +{ + gvdbg("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; + + gvdbg("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) +{ + gvdbg("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 */ + + gdbg("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; + + gvdbg("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; + + gvdbg("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; + } + + gdbg("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; + + gvdbg("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; + } + + gdbg("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; + + gvdbg("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; + } + + gdbg("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; + + gvdbg("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) + { + gdbg("Error: CLUT is not supported for the pixel format: %d\n", + priv->vinfo.fmt); + ret = -EINVAL; + } + else if (cmap->first >= STM32_DMA2D_NCLUT) + { + gdbg("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]; + + regvdbg("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]); + + regvdbg("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; + } + + gdbg("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; + + gvdbg("layer=%p, cmap=%p\n", layer, cmap); + + if (stm32_dma2d_lvalidate(priv) && cmap) + { + sem_wait(priv->lock); + + if (priv->fmt != DMA2D_PF_L8) + { + gdbg("Error: CLUT is not supported for the pixel format: %d\n", + priv->vinfo.fmt); + ret = -EINVAL; + } + else if (cmap->first >= STM32_DMA2D_NCLUT) + { + gdbg("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]; + + regvdbg("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]); + + regvdbg("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; + } + + gdbg("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; + + gvdbg("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; + } + + gdbg("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; + + gvdbg("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; + } + + gdbg("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; + + gvdbg("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; + } + + gdbg("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; + + gvdbg("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; + } + + gdbg("ERROR: Returning EINVAL\n"); + return -EINVAL; +} + +/****************************************************************************** * Name: stm32_dma2dblit * * Description: - * Copy selected area from a background layer to selected position of the - * foreground layer. Copies the result to the destination layer. + * Copy selected area from a source layer to selected position of the + * destination layer. * * Parameter: * dest - Valid reference to the destination layer - * fore - Valid reference to the foreground layer - * forexpos - Valid selected x target position of the destination layer - * foreypos - Valid selected y target position of the destination layer - * back - Valid reference to the background layer - * backarea - Valid reference to the selected area of the background 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 - On error + * 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. * ****************************************************************************/ -int stm32_dma2dblit(FAR struct stm32_ltdc_s *dest, - FAR struct stm32_ltdc_s *fore, - fb_coord_t forexpos, fb_coord_t foreypos, - FAR struct stm32_ltdc_s *back, - FAR const struct ltdc_area_s *backarea) +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) { - gdbg("Not implemented"); - return -ENOSYS; + 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; + + gvdbg("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; + gdbg("ERROR: Returning ECANCELED\n"); + } + } + + sem_post(destlayer->lock); + } + else + { + ret = -EINVAL; + gdbg("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. Blends the result with the destination layer. - * Note! This is the same as the blit operation but with blending depending on - * the blendmode settings of the layer. + * 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 - Valid reference to the destination layer - * fore - Valid reference to the foreground layer - * forexpos - Valid selected x target position of the destination layer - * foreypos - Valid selected y target position of the destination layer - * back - Valid reference to the background layer - * backarea - Valid reference to the selected area of the background layer + * 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 - On error + * 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. * ****************************************************************************/ -int stm32_dma2dblend(FAR struct stm32_ltdc_s *dest, - FAR struct stm32_ltdc_s *fore, - fb_coord_t forexpos, fb_coord_t foreypos, - FAR struct stm32_ltdc_s *back, - FAR const struct ltdc_area_s *backarea) +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) { - gdbg("Not implemented"); - return -ENOSYS; + 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; + + gvdbg("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; + gdbg("ERROR: Returning ECANCELED\n"); + } + } + + sem_post(destlayer->lock); + } + else + { + ret = -EINVAL; + gdbg("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; + + gvdbg("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; + gdbg("ERROR: Returning ECANCELED\n"); + } + } + + sem_post(priv->lock); + } + else + { + ret = -EINVAL; + gdbg("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; + } + + gdbg("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; + + gvdbg("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->bpp = bpp; + + /* Bind the layer to the identifier */ + + g_layers[lid] = layer; + } + else + { + /* free the layer struture */ + + kmm_free(layer); + gdbg("ERROR: ENOMEM, Unable to allocate layer buffer\n"); + errno = ENOMEM; + } + } + else + { + gdbg("ERROR: ENOMEM, unable to allocate layer structure\n"); + errno = ENOMEM; + } + } + else + { + gdbg("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); +#ifdef HAVE_CCM_HEAP + if (((uint32_t)priv & 0xF0000000) == 0x10000000) + { + ccm_free(priv); + } + else + { + kmm_free(priv); + } +#else + kmm_free(priv); +#endif + g_layers[lid] = NULL; + ret = OK; + } + + sem_post(priv->lock); + } + + return ret; } /****************************************************************************** @@ -166,6 +2157,63 @@ int stm32_dma2dblend(FAR struct stm32_ltdc_s *dest, int up_dma2dinitialize(void) { + dbg("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; } @@ -179,4 +2227,86 @@ int up_dma2dinitialize(void) 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; + + gvdbg("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) + { + dbg("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 */ |