nuttx/arch/arm/src/stm32/stm32_dma2d.c

2304 lines
63 KiB
C

/****************************************************************************
* 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 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
****************************************************************************/
/****************************************************************************
* 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;
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 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)
{
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 = 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;
regvdbg("set regval=%08x\n", regval);
putreg32(regval, pfcreg);
regvdbg("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;
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 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;
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. 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;
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->display = 0;
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);
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)
{
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;
}
/****************************************************************************
* 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;
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 */