/**************************************************************************** * arch/arm/src/stm32/stm32_ltdc.c * * Copyright (C) 2013-2014 Ken Pettit. All rights reserved. * Authors: Ken Pettit * Marco Krahl * * References: * STM32F429 Technical Reference Manual and Data Sheet * * 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 #include #include #include #include #include #include #include #include #include "up_arch.h" #include "up_internal.h" #include "stm32.h" #include "chip/stm32_ltdc.h" #include "stm32_ltdc.h" #include "stm32_dma2d.h" /**************************************************************************** * Pre-Processor Definitions ****************************************************************************/ /* Register definition ******************************************************/ #ifndef BOARD_LTDC_WIDTH # error BOARD_LTDC_WIDTH must be defined in the board.h header file #endif #ifndef BOARD_LTDC_HEIGHT # error BOARD_LTDC_HEIGHT must be defined in the board.h header file #endif #define STM32_LTDC_HEIGHT BOARD_LTDC_HEIGHT #define STM32_LTDC_WIDTH BOARD_LTDC_WIDTH /* Configure LTDC register */ /* LTDC_LxWHPCR register */ #define STM32_LTDC_LxWHPCR_WHSTPOS (BOARD_LTDC_HSYNC + BOARD_LTDC_HBP - 1) #define STM32_LTDC_LxWHPCR_WHSPPOS (BOARD_LTDC_HSYNC + BOARD_LTDC_HBP + \ STM32_LTDC_WIDTH - 1) /* LTDC_LxWVPCR register */ #define STM32_LTDC_LxWVPCR_WVSTPOS (BOARD_LTDC_VSYNC + BOARD_LTDC_VBP - 1) #define STM32_LTDC_LxWVPCR_WVSPPOS (BOARD_LTDC_VSYNC + BOARD_LTDC_VBP + \ STM32_LTDC_HEIGHT - 1) /* LTDC_SSCR register */ #define STM32_LTDC_SSCR_VSH LTDC_SSCR_VSH(BOARD_LTDC_VSYNC - 1) #define STM32_LTDC_SSCR_HSW LTDC_SSCR_HSW(BOARD_LTDC_HSYNC - 1) /* LTDC_BPCR register */ #define STM32_LTDC_BPCR_AVBP LTDC_BPCR_AVBP(STM32_LTDC_LxWVPCR_WVSTPOS) #define STM32_LTDC_BPCR_AHBP LTDC_BPCR_AHBP(STM32_LTDC_LxWHPCR_WHSTPOS) /* LTDC_AWCR register */ #define STM32_LTDC_AWCR_AAH LTDC_AWCR_AAH(STM32_LTDC_LxWVPCR_WVSPPOS) #define STM32_LTDC_AWCR_AAW LTDC_AWCR_AAW(STM32_LTDC_LxWHPCR_WHSPPOS) /* LTDC_TWCR register */ #define STM32_LTDC_TWCR_TOTALH LTDC_TWCR_TOTALH(BOARD_LTDC_VSYNC + \ BOARD_LTDC_VBP + \ STM32_LTDC_HEIGHT + BOARD_LTDC_VFP - 1) #define STM32_LTDC_TWCR_TOTALW LTDC_TWCR_TOTALW(BOARD_LTDC_HSYNC + \ BOARD_LTDC_HBP + \ STM32_LTDC_WIDTH + BOARD_LTDC_HFP - 1) /* Global GCR register */ /* Synchronisation and Polarity */ #define STM32_LTDC_GCR_PCPOL BOARD_LTDC_GCR_PCPOL #define STM32_LTDC_GCR_DEPOL BOARD_LTDC_GCR_DEPOL #define STM32_LTDC_GCR_VSPOL BOARD_LTDC_GCR_VSPOL #define STM32_LTDC_GCR_HSPOL BOARD_LTDC_GCR_HSPOL /* Dither */ #define STM32_LTDC_GCR_DEN BOARD_LTDC_GCR_DEN #define STM32_LTDC_GCR_DBW LTDC_GCR_GBW(BOARD_LTDC_GCR_DBW) #define STM32_LTDC_GCR_DGW LTDC_GCR_DGW(BOARD_LTDC_GCR_DGW) #define STN32_LTDC_GCR_DRW LTDC_GCR_DBW(BOARD_LTDC_GCR_DRW) /* IER register */ #define STM32_LTDC_IER_LIE LTDC_IER_LIE #define STM32_LTDC_IER_FUIE !LTDC_IER_FUIE #define STM32_LTDC_IER_TERRIE !LTDC_IER_TERRIE #define STM32_LTDC_IER_RRIE LTDC_IER_RRIE /* LIPCR register */ #define STM32_LTDC_LIPCR_LIPOS LTDC_LIPCR_LIPOS(STM32_LTDC_TWCR_TOTALW) /* Configuration ************************************************************/ #ifndef CONFIG_STM32_LTDC_DEFBACKLIGHT # define CONFIG_STM32_LTDC_DEFBACKLIGHT 0xf0 #endif #define STM32_LTDC_BACKLIGHT_OFF 0x00 /* Color/video formats */ /* Layer 1 format */ #if defined(CONFIG_STM32_LTDC_L1_L8) # define STM32_LTDC_L1_BPP 8 # define STM32_LTDC_L1_COLOR_FMT FB_FMT_RGB8 # define STM32_LTDC_L1PFCR_PF LTDC_LxPFCR_PF(LTDC_PF_L8) # define STM32_LTDC_L1CMAP #elif defined(CONFIG_STM32_LTDC_L1_AL44) # define STM32_LTDC_L1_BPP 8 # define STM32_LTDC_L1_COLOR_FMT ??? # define STM32_LTDC_L1PFCR_PF LTDC_LxPFCR_PF(LTDC_PF_AL44) #elif defined(CONFIG_STM32_LTDC_L1_AL88) # define STM32_LTDC_L1_BPP 16 # define STM32_LTDC_L1_COLOR_FMT ??? # define STM32_LTDC_L1PFCR_PF LTDC_LxPFCR_PF(LTDC_PF_AL88) #elif defined(CONFIG_STM32_LTDC_L1_ARGB4444) # define STM32_LTDC_L1_BPP 16 # define STM32_LTDC_L1_COLOR_FMT ??? # define STM32_LTDC_L1PFCR_PF LTDC_LxPFCR_PF(LTDC_PF_ARGB4444) #elif defined(CONFIG_STM32_LTDC_L1_RGB565) # define STM32_LTDC_L1_BPP 16 # define STM32_LTDC_L1_COLOR_FMT FB_FMT_RGB16_565 # define STM32_LTDC_L1PFCR_PF LTDC_LxPFCR_PF(LTDC_PF_RGB565) #elif defined(CONFIG_STM32_LTDC_L1_ARGB1555) # define STM32_LTDC_L1_BPP 16 # define STM32_LTDC_L1_COLOR_FMT ??? # define STM32_LTDC_L1PFCR_PF LTDC_LxPFCR_PF(LTDC_PF_ARGB1555) #elif defined(CONFIG_STM32_LTDC_L1_RGB888) # define STM32_LTDC_L1_BPP 24 # define STM32_LTDC_L1_COLOR_FMT FB_FMT_RGB24 # define STM32_LTDC_L1PFCR_PF LTDC_LxPFCR_PF(LTDC_PF_RGB888) #elif defined(CONFIG_STM32_LTDC_L1_ARGB8888) # define STM32_LTDC_L1_BPP 32 # define STM32_LTDC_L1_COLOR_FMT ??? # define STM32_LTDC_L1PFCR_PF LTDC_LxPFCR_PF(LTDC_PF_ARGB8888) #endif /* Layer 2 format */ #if defined(CONFIG_STM32_LTDC_L2_L8) # define STM32_LTDC_L2_BPP 8 # define STM32_LTDC_L2_COLOR_FMT FB_FMT_RGB8 # define STM32_LTDC_L2PFCR_PF LTDC_LxPFCR_PF(LTDC_PF_L8) # define STM32_LTDC_L2CMAP #elif defined(CONFIG_STM32_LTDC_L2_AL44) # define STM32_LTDC_L2_BPP 8 # define STM32_LTDC_L2_COLOR_FMT ??? # define STM32_LTDC_L2PFCR_PF LTDC_LxPFCR_PF(LTDC_PF_AL44) #elif defined(CONFIG_STM32_LTDC_L2_AL88) # define STM32_LTDC_L2_BPP 16 # define STM32_LTDC_L2_COLOR_FMT ??? # define STM32_LTDC_L2PFCR_PF LTDC_LxPFCR_PF(LTDC_PF_AL88) #elif defined(CONFIG_STM32_LTDC_L2_ARGB4444) # define STM32_LTDC_L2_BPP 16 # define STM32_LTDC_L2_COLOR_FMT ??? # define STM32_LTDC_L2PFCR_PF LTDC_LxPFCR_PF(LTDC_PF_ARGB4444) #elif defined(CONFIG_STM32_LTDC_L2_RGB565) # define STM32_LTDC_L2_BPP 16 # define STM32_LTDC_L2_COLOR_FMT FB_FMT_RGB16_565 # define STM32_LTDC_L2PFCR_PF LTDC_LxPFCR_PF(LTDC_PF_RGB565) #elif defined(CONFIG_STM32_LTDC_L2_ARGB1555) # define STM32_LTDC_L2_BPP 16 # define STM32_LTDC_L2_COLOR_FMT ??? # define STM32_LTDC_L2PFCR_PF LTDC_LxPFCR_PF(LTDC_PF_ARGB1555) #elif defined(CONFIG_STM32_LTDC_L2_RGB888) # define STM32_LTDC_L2_BPP 24 # define STM32_LTDC_L2_COLOR_FMT FB_FMT_RGB24 # define STM32_LTDC_L2PFCR_PF LTDC_LxPFCR_PF(LTDC_PF_RGB888) #elif defined(CONFIG_STM32_LTDC_L2_ARGB8888) # define STM32_LTDC_L2_BPP 32 # define STM32_LTDC_L2_COLOR_FMT ??? # define STM32_LTDC_L2PFCR_PF LTDC_LxPFCR_PF(LTDC_PF_ARGB8888) #endif /* Framebuffer sizes in bytes */ #if STM32_LTDC_L1_BPP == 8 # define STM32_L1_STRIDE (STM32_LTDC_WIDTH) #elif STM32_LTDC_L1_BPP == 16 # define STM32_L1_STRIDE ((STM32_LTDC_WIDTH * 16 + 7) / 8) #elif STM32_LTDC_L1_BPP == 24 # define STM32_L1_STRIDE ((STM32_LTDC_WIDTH * 24 + 7) / 8) #elif STM32_LTDC_L1_BPP == 32 # define STM32_L1_STRIDE ((STM32_LTDC_WIDTH * 32 + 7) / 8) #else # error Undefined or unrecognized base resolution #endif /* LTDC only supports 8 bit per pixel overal */ #define STM32_LTDC_Lx_BYPP(n) ((n) / 8) #define STM32_L1_FBSIZE (STM32_L1_STRIDE * STM32_LTDC_HEIGHT) #ifdef CONFIG_STM32_LTDC_L2 # ifndef CONFIG_STM32_LTDC_L2_WIDTH # define CONFIG_STM32_LTDC_L2_WIDTH STM32_LTDC_WIDTH # endif # if CONFIG_STM32_LTDC_L2_WIDTH > STM32_LTDC_WIDTH # error Width of Layer 2 exceeds the width of the display # endif # ifndef CONFIG_STM32_LTDC_L2_HEIGHT # define CONFIG_STM32_LTDC_L2_HEIGHT STM32_LTDC_HEIGHT # endif # if CONFIG_STM32_LTDC_L2_HEIGHT > STM32_LTDC_HEIGHT # error Height of Layer 2 exceeds the height of the display # endif # if STM32_LTDC_L2_BPP == 8 # define STM32_L2_STRIDE (CONFIG_STM32_LTDC_L2_WIDTH) # elif STM32_LTDC_L2_BPP == 16 # define STM32_L2_STRIDE ((CONFIG_STM32_LTDC_L2_WIDTH * 16 + 7) / 8) # elif STM32_LTDC_L2_BPP == 24 # define STM32_L2_STRIDE ((CONFIG_STM32_LTDC_L2_WIDTH * 24 + 7) / 8) # elif STM32_LTDC_L2_BPP == 32 # define STM32_L2_STRIDE ((CONFIG_STM32_LTDC_L2_WIDTH * 32 + 7) / 8) # else # error Undefined or unrecognized base resolution # endif # define STM32_L2_FBSIZE (STM32_L2_STRIDE * CONFIG_STM32_LTDC_L2_HEIGHT) #else # define STM32_L2_FBSIZE (0) #endif /* Total memory used for framebuffers */ #define STM32_TOTAL_FBSIZE (STM32_L1_FBSIZE + STM32_L2_FBSIZE) /* Debug option */ #ifdef CONFIG_STM32_LTDC_REGDEBUG # define regdbg dbg # define regvdbg vdbg #else # define regdbg(x...) # define regvdbg(x...) #endif /* Preallocated LTDC framebuffers */ /* Position the framebuffer memory in the center of the memory set aside. We * will use any skirts before or after the framebuffer memory as a guard against * wild framebuffer writes. */ #define STM32_LTDC_BUFFER_SIZE CONFIG_STM32_LTDC_FB_SIZE #define STM32_LTDC_BUFFER_FREE (STM32_LTDC_BUFFER_SIZE - STM32_TOTAL_FBSIZE) #define STM32_LTDC_BUFFER_START (CONFIG_STM32_LTDC_FB_BASE + STM32_LTDC_BUFFER_FREE/2) #if STM32_LTDC_BUFFER_FREE < 0 # error "STM32_LTDC_BUFFER_SIZE not large enough for frame buffers" #endif /* Layer frame buffer */ #define STM32_LTDC_BUFFER_L1 STM32_LTDC_BUFFER_START #define STM32_LTDC_ENDBUF_L1 (STM32_LTDC_BUFFER_L1 + STM32_L1_FBSIZE) #ifdef CONFIG_STM32_LTDC_L2 # define STM32_LTDC_BUFFER_L2 STM32_LTDC_ENDBUF_L1 # define STM32_LTDC_ENDBUF_L2 (STM32_LTDC_BUFFER_L2 + STM32_L2_FBSIZE) #else # define STM32_LTDC_ENDBUF_L2 STM32_LTDC_ENDBUF_L1 #endif /* Layer helpers */ #ifdef CONFIG_STM32_LTDC_L2 # define LTDC_NLAYERS 2 #else # define LTDC_NLAYERS 1 #endif #define LAYER(i) g_ltdc.layer[i] #define LAYER_L1 g_ltdc.layer[LTDC_LAYER_L1] #define LAYER_L2 g_ltdc.layer[LTDC_LAYER_L2] /* Dithering */ #ifndef CONFIG_STM32_LTDC_DITHER_RED # define STM32_LTDC_DITHER_RED 0 #else # define STM32_LTDC_DITHER_RED CONFIG_STM32_LTDC_DITHER_RED #endif #ifndef CONFIG_STM32_LTDC_DITHER_GREEN # define STM32_LTDC_DITHER_GREEN 0 #else # define STM32_LTDC_DITHER_GREEN CONFIG_STM32_LTDC_DITHER_GREEN #endif #ifndef CONFIG_STM32_LTDC_DITHER_BLUE # define STM32_LTDC_DITHER_BLUE 0 #else # define STM32_LTDC_DITHER_BLUE CONFIG_STM32_LTDC_DITHER_BLUE #endif /* Background color */ #ifndef CONFIG_STM32_LTDC_BACKCOLOR # define STM32_LTDC_BACKCOLOR 0 #else # define STM32_LTDC_BACKCOLOR CONFIG_STM32_LTDC_BACKCOLOR #endif /* Internal operation flags */ #define LTDC_LAYER_SETAREA (1 << 0) /* Change visible area */ #define LTDC_LAYER_SETALPHAVALUE (1 << 1) /* Change constant alpha value */ #define LTDC_LAYER_SETBLENDMODE (1 << 2) /* Change blendmode */ #define LTDC_LAYER_SETCOLORKEY (1 << 3) /* Change color key */ #define LTDC_LAYER_ENABLECOLORKEY (1 << 4) /* Enable colorkey */ #define LTDC_LAYER_SETCOLOR (1 << 5) /* Change default color */ #define LTDC_LAYER_SETENABLE (1 << 6) /* Change enabled state */ #define LTDC_LAYER_ENABLE (1 << 7) /* Enable the layer */ /* Layer initializing state */ #define LTDC_LAYER_INIT LTDC_LAYER_SETAREA | \ LTDC_LAYER_SETALPHAVALUE | \ LTDC_LAYER_SETBLENDMODE | \ LTDC_LAYER_SETCOLORKEY | \ LTDC_LAYER_SETCOLOR | \ LTDC_LAYER_SETENABLE | \ LTDC_LAYER_ENABLE /* Blendfactor reset values for flip operation */ #define STM32_LTDC_BF1_RESET 6 #define STM32_LTDC_BF2_RESET 7 /* * Waits upon vertical sync * Note! Position is reserved in current SRCR register but not used */ #define LTDC_SRCR_WAIT 4 /* Calculate the size of the layers clut table */ #ifdef CONFIG_FB_CMAP #define STM32_LTDC_CLUT_ENTRIES 256 # ifdef STM32_LTDC_L1CMAP # define STM32_LAYER_CLUT_SIZE 1 * 3 * STM32_LTDC_CLUT_ENTRIES # endif # ifdef STM32_LTDC_L2CMAP # undef STM32_LAYER_CLUT_SIZE # define STM32_LAYER_CLUT_SIZE 2 * 3 * STM32_LTDC_CLUT_ENTRIES # endif #endif #ifndef CONFIG_FB_CMAP # if defined(STM32_LTDC_L1CMAP) || defined(STM32_LTDC_L2CMAP) # undef STM32_LTDC_L1CMAP # undef STM32_LTDC_L2CMAP # error "Enable cmap to support the configured layer format!" # endif #endif /* Layer clut rgb value positioning */ #define LTDC_L1CLUT_REDOFFSET 0 #define LTDC_L1CLUT_GREENOFFSET 256 #define LTDC_L1CLUT_BLUEOFFSET 512 #define LTDC_L2CLUT_REDOFFSET 768 #define LTDC_L2CLUT_GREENOFFSET 1024 #define LTDC_L2CLUT_BLUEOFFSET 1280 /* Layer rgb clut register position */ #define LTDC_CLUT_ADD(n) ((uint32_t)(n) << 24) #define LTDC_CLUT_RED(n) ((uint32_t)(n) << 16) #define LTDC_CLUT_GREEN(n) ((uint32_t)(n) << 8) #define LTDC_CLUT_BLUE(n) ((uint32_t)(n) << 0) /**************************************************************************** * Private Types ****************************************************************************/ /* This enumeration names each layer supported by the hardware */ enum stm32_layer_e { LTDC_LAYER_L1 = 0, /* LCD Layer 1*/ LTDC_LAYER_L2, /* LCD Layer 2 */ }; /* LTDC General layer information */ struct stm32_layer_s { #ifdef CONFIG_STM32_LTDC_INTERFACE /* LTDC interface */ struct ltdc_layer_s ltdc; /* Layer control structure */ #endif struct stm32_ltdc_s state; /* Layer state structure */ /* Blending */ uint8_t opac; /* Opacity value for blending */ uint8_t bf1; /* Blend factor 1 */ uint8_t bf2; /* Blend factor 2 */ /* Operation */ uint8_t operation; /* Operation flags */ }; /* This structure provides the state of each LTDC layer */ struct stm32_state_s { /* Layer state */ struct stm32_ltdc_s state[LTDC_NLAYERS]; }; /* This structure provides the overall state of the LTDC layer */ struct stm32_ltdcdev_s { /* Layer information */ struct stm32_layer_s layer[LTDC_NLAYERS]; }; /* Layer cmap table description */ #ifdef STM32_LAYER_CLUT_SIZE enum stm32_clut_e { LTDC_L1CLUT_RED = LTDC_L1CLUT_REDOFFSET, LTDC_L1CLUT_GREEN = LTDC_L1CLUT_GREENOFFSET, LTDC_L1CLUT_BLUE = LTDC_L1CLUT_BLUEOFFSET, LTDC_L2CLUT_RED = LTDC_L2CLUT_REDOFFSET, LTDC_L2CLUT_GREEN = LTDC_L2CLUT_GREENOFFSET, LTDC_L2CLUT_BLUE = LTDC_L2CLUT_BLUEOFFSET }; #endif /**************************************************************************** * Private Function Prototypes ****************************************************************************/ /* Global register operation */ static void stm32_lcd_enable(bool enable); static void stm32_ltdc_gpioconfig(void); static void stm32_ltdc_periphconfig(void); static void stm32_ltdc_bgcolor(uint32_t rgb); static void stm32_ltdc_dither(bool enable, uint8_t red, uint8_t green, uint8_t blue); static void stm32_ltdc_interrupt(uint32_t mask, uint32_t value); static void stm32_ltdc_reload(uint8_t value); /* Layer and layer register operation */ static inline void stm32_ltdc_lsetopac(FAR struct stm32_layer_s *layer); static inline void stm32_ltdc_lunsetopac(FAR struct stm32_layer_s *layer); static inline uint8_t stm32_ltdc_lgetopac(FAR struct stm32_layer_s *layer); static inline bool stm32_ltdc_lvalidate(FAR struct stm32_layer_s *layer); #ifdef CONFIG_STM32_LTDC_INTERFACE static int stm32_ltdc_lvalidatearea(FAR struct stm32_layer_s *layer, fb_coord_t xpos, fb_coord_t ypos, fb_coord_t xres, fb_coord_t yres, fb_coord_t srcxpos, fb_coord_t srcypos); #endif static void stm32_ltdc_lupdate(FAR struct stm32_layer_s *layer); static void stm32_ltdc_lpixelformat(FAR struct stm32_layer_s *layer); static inline void stm32_ltdc_lframebuffer(FAR struct stm32_layer_s *layer); static void stm32_ltdc_larea(FAR struct stm32_layer_s *layer); static void stm32_ltdc_lcolor(FAR struct stm32_layer_s *layer, uint32_t argb); static void stm32_ltdc_lcolorkey(FAR struct stm32_layer_s *layer); static void stm32_ltdc_lalpha(FAR struct stm32_layer_s *layer); static void stm32_ltdc_lblendmode(FAR struct stm32_layer_s *layer, uint8_t bf1, uint8_t bf2); #ifdef STM32_LAYER_CLUT_SIZE static void stm32_ltdc_cmapcpy(FAR struct fb_cmap_s *dest, FAR const struct fb_cmap_s *src); static void stm32_ltdc_lclut(FAR struct stm32_layer_s *layer); static void stm32_ltdc_lclutenable(FAR struct stm32_layer_s* layer, bool enable); #endif static void stm32_ltdc_linit(int lid); static void stm32_ltdc_lenable(FAR struct stm32_layer_s *layer); static void stm32_ltdc_lclear(FAR struct stm32_layer_s *layer, nxgl_mxpixel_t color); /* Generic frame buffer interface */ static int stm32_getvideoinfo(FAR struct fb_vtable_s *vtable, struct fb_videoinfo_s *vinfo); static int stm32_getplaneinfo(FAR struct fb_vtable_s *vtable, int planeno, struct fb_planeinfo_s *pinfo); /* The following is provided only if the video hardware supports RGB color * mapping */ #ifdef STM32_LAYER_CLUT_SIZE static int stm32_getcmap(struct fb_vtable_s *vtable, struct fb_cmap_s *cmap); static int stm32_putcmap(struct fb_vtable_s *vtable, const struct fb_cmap_s *cmap); #endif /* ltdc interface */ #ifdef STM32_LAYER_CLUT_SIZE static int stm32_setclut(struct ltdc_layer_s *layer, const struct fb_cmap_s *cmap); static int stm32_getclut(struct ltdc_layer_s *layer, struct fb_cmap_s *cmap); #endif static int stm32_lgetvideoinfo(struct ltdc_layer_s *layer, struct fb_videoinfo_s *vinfo); static int stm32_lgetplaneinfo(struct ltdc_layer_s *layer, int planeno, struct fb_planeinfo_s *pinfo); #ifdef CONFIG_STM32_LTDC_INTERFACE static int stm32_getlid(FAR struct ltdc_layer_s *layer, int *lid, uint32_t flag); static int stm32_setcolor(FAR struct ltdc_layer_s *layer, uint32_t argb); static int stm32_getcolor(FAR struct ltdc_layer_s *layer, uint32_t *argb); static int stm32_setcolorkey(FAR struct ltdc_layer_s *layer, uint32_t argb); static int stm32_getcolorkey(FAR struct ltdc_layer_s *layer, uint32_t *argb); static int stm32_setalpha(FAR struct ltdc_layer_s *layer, uint8_t alpha); static int stm32_getalpha(FAR struct ltdc_layer_s *layer, uint8_t *alpha); static int stm32_setblendmode(FAR struct ltdc_layer_s *layer, uint32_t mode); static int stm32_getblendmode(FAR struct ltdc_layer_s *layer, uint32_t *mode); static int stm32_setarea(FAR struct ltdc_layer_s *layer, FAR const struct ltdc_area_s *area, fb_coord_t srcxpos, fb_coord_t srcypos); static int stm32_getarea(FAR struct ltdc_layer_s *layer, FAR struct ltdc_area_s *area, fb_coord_t *srcxpos, fb_coord_t *srcypos); static int stm32_update(FAR struct ltdc_layer_s *layer, uint32_t mode); #ifdef CONFIG_STM32_DMA2D static int stm32_blit(FAR struct ltdc_layer_s *dest, FAR struct ltdc_layer_s *fore, fb_coord_t forexpos, fb_coord_t foreypos, FAR struct ltdc_layer_s *back, FAR const struct ltdc_area_s *backarea); static int stm32_blend(FAR struct ltdc_layer_s *dest, FAR struct ltdc_layer_s *fore, fb_coord_t forexpos, fb_coord_t foreypos, FAR struct ltdc_layer_s *back, FAR const struct ltdc_area_s *backarea); #endif #endif /* CONFIG_STM32_LTDC_INTERFACE */ /**************************************************************************** * Private Data ****************************************************************************/ /* PIO pin configurations */ static const uint32_t g_ltdcpins[] = { GPIO_LTDC_R4, GPIO_LTDC_R5, GPIO_LTDC_R6, GPIO_LTDC_R7, GPIO_LTDC_G4, GPIO_LTDC_G5, GPIO_LTDC_G6, GPIO_LTDC_G7, GPIO_LTDC_B4, GPIO_LTDC_B5, GPIO_LTDC_B6, GPIO_LTDC_B7, #if BOARD_LTDC_OUTPUT_BPP > 12 GPIO_LTDC_R3, GPIO_LTDC_G2, GPIO_LTDC_G3, GPIO_LTDC_B3, # if BOARD_LTDC_OUTPUT_BPP > 16 GPIO_LTDC_R2, GPIO_LTDC_B2, # if BOARD_LTDC_OUTPUT_BPP > 18 GPIO_LTDC_R0, GPIO_LTDC_R1, GPIO_LTDC_G0, GPIO_LTDC_G1, GPIO_LTDC_B0, GPIO_LTDC_B1, # endif # endif #endif GPIO_LTDC_VSYNC, GPIO_LTDC_HSYNC, GPIO_LTDC_DE, GPIO_LTDC_CLK }; #define STM32_LTDC_NPINCONFIGS (sizeof(g_ltdcpins) / sizeof(uint32_t)) /* This structure provides the base layer interface */ static const struct fb_vtable_s g_vtable = { .getvideoinfo = stm32_getvideoinfo, .getplaneinfo = stm32_getplaneinfo #ifdef STM32_LAYER_CLUT_SIZE ,.getcmap = stm32_getcmap, .putcmap = stm32_putcmap #endif }; /* The LTDC semaphore that enforces mutually exclusive access */ static sem_t g_lock; /* The layer active state */ static uint8_t g_lactive; #ifdef STM32_LAYER_CLUT_SIZE /* The layers clut table entries */ static uint8_t g_clut[STM32_LAYER_CLUT_SIZE]; /* The layer 1 cmap */ #ifdef STM32_LTDC_L1CMAP static struct fb_cmap_s g_cmap_l1 = { .len = STM32_LTDC_CLUT_ENTRIES, .red = &g_clut[LTDC_L1CLUT_RED], .green = &g_clut[LTDC_L1CLUT_GREEN], .blue = &g_clut[LTDC_L1CLUT_BLUE] }; #endif /* The layer 2 cmap */ #ifdef STM32_LTDC_L2CMAP static struct fb_cmap_s g_cmap_l2 = { .len = STM32_LTDC_CLUT_ENTRIES, .red = &g_clut[LTDC_L2CLUT_RED], .green = &g_clut[LTDC_L2CLUT_GREEN], .blue = &g_clut[LTDC_L2CLUT_BLUE] }; #endif #endif /* The initialized state of the overall LTDC layers */ static struct stm32_ltdcdev_s g_ltdc = { .layer[LTDC_LAYER_L1] = { .state = { .lid = LTDC_LAYER_L1, .pinfo = { .fbmem = (uint8_t *)STM32_LTDC_BUFFER_L1, .fblen = STM32_L1_FBSIZE, .stride = STM32_L1_STRIDE, .bpp = STM32_LTDC_L1_BPP }, .vinfo = { .fmt = STM32_LTDC_L1_COLOR_FMT, .xres = STM32_LTDC_WIDTH, .yres = STM32_LTDC_HEIGHT, .nplanes = 1 } #ifdef STM32_LTDC_L1CMAP ,.cmap = &g_cmap_l1 #endif } } #ifdef CONFIG_STM32_LTDC_L2 ,.layer[LTDC_LAYER_L2] = { .state = { .lid = LTDC_LAYER_L2, .pinfo = { .fbmem = (uint8_t *)STM32_LTDC_BUFFER_L2, .fblen = STM32_L2_FBSIZE, .stride = STM32_L2_STRIDE, .bpp = STM32_LTDC_L2_BPP }, .vinfo = { .fmt = STM32_LTDC_L2_COLOR_FMT, .xres = STM32_LTDC_WIDTH, .yres = STM32_LTDC_HEIGHT, .nplanes = 1 } #ifdef STM32_LTDC_L2CMAP ,.cmap = &g_cmap_l2 #endif } } #endif }; /* Pixel format lookup table */ static const uint32_t stm32_fmt_layer_t[LTDC_NLAYERS] = { STM32_LTDC_L1PFCR_PF #ifdef CONFIG_STM32_LTDC_L2 ,STM32_LTDC_L2PFCR_PF #endif }; /* Register lookup tables */ /* LTDC_LxCR */ static const uintptr_t stm32_cr_layer_t[LTDC_NLAYERS] = { STM32_LTDC_L1CR #ifdef CONFIG_STM32_LTDC_L2 , STM32_LTDC_L2CR #endif }; /* LTDC_LxWHPCR */ static const uintptr_t stm32_whpcr_layer_t[LTDC_NLAYERS] = { STM32_LTDC_L1WHPCR #ifdef CONFIG_STM32_LTDC_L2 , STM32_LTDC_L2WHPCR #endif }; /* LTDC_LxWVPCR */ static const uintptr_t stm32_wvpcr_layer_t[LTDC_NLAYERS] = { STM32_LTDC_L1WVPCR #ifdef CONFIG_STM32_LTDC_L2 , STM32_LTDC_L2WVPCR #endif }; /* LTDC_LxPFCR */ static const uintptr_t stm32_pfcr_layer_t[LTDC_NLAYERS] = { STM32_LTDC_L1PFCR #ifdef CONFIG_STM32_LTDC_L2 , STM32_LTDC_L2PFCR #endif }; /* LTDC_LxDCCR */ static const uintptr_t stm32_dccr_layer_t[LTDC_NLAYERS] = { STM32_LTDC_L1DCCR #ifdef CONFIG_STM32_LTDC_L2 , STM32_LTDC_L2DCCR #endif }; /* LTDC_LxCKCR */ static const uintptr_t stm32_ckcr_layer_t[LTDC_NLAYERS] = { STM32_LTDC_L1CKCR #ifdef CONFIG_STM32_LTDC_L2 , STM32_LTDC_L2CKCR #endif }; /* LTDC_LxCACR */ static const uintptr_t stm32_cacr_layer_t[LTDC_NLAYERS] = { STM32_LTDC_L1CACR #ifdef CONFIG_STM32_LTDC_L2 , STM32_LTDC_L2CACR #endif }; /* LTDC_LxBFCR */ static const uintptr_t stm32_bfcr_layer_t[LTDC_NLAYERS] = { STM32_LTDC_L1BFCR #ifdef CONFIG_STM32_LTDC_L2 , STM32_LTDC_L2BFCR #endif }; /* LTDC_LxCFBAR */ static const uintptr_t stm32_cfbar_layer_t[LTDC_NLAYERS] = { STM32_LTDC_L1CFBAR #ifdef CONFIG_STM32_LTDC_L2 , STM32_LTDC_L2CFBAR #endif }; /* LTDC_LxCFBLR */ static const uintptr_t stm32_cfblr_layer_t[LTDC_NLAYERS] = { STM32_LTDC_L1CFBLR #ifdef CONFIG_STM32_LTDC_L2 , STM32_LTDC_L2CFBLR #endif }; /* LTDC_LxCFBLNR */ static const uintptr_t stm32_cfblnr_layer_t[LTDC_NLAYERS] = { STM32_LTDC_L1CFBLNR #ifdef CONFIG_STM32_LTDC_L2 , STM32_LTDC_L2CFBLNR #endif }; /* LTDC_LxCLUTWR */ #ifdef STM32_LAYER_CLUT_SIZE static const uintptr_t stm32_clutwr_layer_t[LTDC_NLAYERS] = { STM32_LTDC_L1CLUTWR # ifdef CONFIG_STM32_LTDC_L2 , STM32_LTDC_L2CLUTWR # endif }; #endif /**************************************************************************** * Public Data ****************************************************************************/ /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Configure global register ****************************************************************************/ /**************************************************************************** * Name: stm32_ltdc_gpioconfig * * Description: * Configure GPIO pins for use with the LTDC * ****************************************************************************/ static void stm32_ltdc_gpioconfig(void) { int i; gvdbg("Configuring pins\n"); /* Configure each pin */ for (i = 0; i < STM32_LTDC_NPINCONFIGS; i++) { regvdbg("set gpio%d = %08x\n", i, g_ltdcpins[i]); stm32_configgpio(g_ltdcpins[i]); } } /**************************************************************************** * Name: stm32_ltdc_periphconfig * * Description: * Configures the synchronous timings * Configures the synchronous signals and clock polarity * ****************************************************************************/ static void stm32_ltdc_periphconfig(void) { uint32_t regval; /* Configure GPIO's */ stm32_ltdc_gpioconfig(); /* Configure APB2 LTDC clock external */ regvdbg("configured RCC_APB2ENR=%08x\n", getreg32(STM32_RCC_APB2ENR)); /* Configure the SAI PLL external to provide the LCD_CLK */ regvdbg("configured RCC_PLLSAI=%08x\n", getreg32(STM32_RCC_PLLSAICFGR)); /* Configure dedicated clock external */ regvdbg("configured RCC_DCKCFGR=%08x\n", getreg32(STM32_RCC_DCKCFGR)); /* Configure LTDC_SSCR */ regval = (STM32_LTDC_SSCR_VSH | STM32_LTDC_SSCR_HSW); regvdbg("set LTDC_SSCR=%08x\n", regval); putreg32(regval, STM32_LTDC_SSCR); regvdbg("configured LTDC_SSCR=%08x\n", getreg32(STM32_LTDC_SSCR)); /* Configure LTDC_BPCR */ regval = (STM32_LTDC_BPCR_AVBP | STM32_LTDC_BPCR_AHBP); regvdbg("set LTDC_BPCR=%08x\n", regval); putreg32(regval, STM32_LTDC_BPCR); regvdbg("configured LTDC_BPCR=%08x\n", getreg32(STM32_LTDC_BPCR)); /* Configure LTDC_AWCR */ regval = (STM32_LTDC_AWCR_AAH | STM32_LTDC_AWCR_AAW); regvdbg("set LTDC_AWCR=%08x\n", regval); putreg32(regval, STM32_LTDC_AWCR); regvdbg("configured LTDC_AWCR=%08x\n", getreg32(STM32_LTDC_AWCR)); /* Configure LTDC_TWCR */ regval = (STM32_LTDC_TWCR_TOTALH | STM32_LTDC_TWCR_TOTALW); regvdbg("set LTDC_TWCR=%08x\n", regval); putreg32(regval, STM32_LTDC_TWCR); regvdbg("configured LTDC_TWCR=%08x\n", getreg32(STM32_LTDC_TWCR)); /* Configure LTDC_GCR */ regval = (STM32_LTDC_GCR_PCPOL | STM32_LTDC_GCR_DEPOL | STM32_LTDC_GCR_VSPOL | STM32_LTDC_GCR_HSPOL); regvdbg("set LTDC_GCR=%08x\n", regval); putreg32(regval, STM32_LTDC_GCR); regvdbg("configured LTDC_GCR=%08x\n", getreg32(STM32_LTDC_GCR)); } /**************************************************************************** * Name: stm32_ltdc_bgcolor * * Description: * Configures background color of the LCD controller. * * Parameter: * rgb - RGB888 background color * ****************************************************************************/ static void stm32_ltdc_bgcolor(uint32_t rgb) { regvdbg("set LTDC_BCCR=%08x\n", rgb); putreg32(rgb, STM32_LTDC_BCCR); regvdbg("configured LTDC_BCCR=%08x\n", getreg32(STM32_LTDC_BCCR)); } /**************************************************************************** * Name: stm32_ltdc_dither * * Description: * Configures dither settings of the LCD controller. * * Parameter: * enable - Enable dithering * red - Red dither width * green - Green dither width * blue - Blue dither width * ****************************************************************************/ static void stm32_ltdc_dither(bool enable, uint8_t red, uint8_t green, uint8_t blue) { uint32_t regval; regval = getreg32(STM32_LTDC_GCR); if (enable == true) { regval |= LTDC_GCR_DEN; } else { regval &= ~LTDC_GCR_DEN; } regval &= ~(!LTDC_GCR_DEN | LTDC_GCR_DRW(0) | LTDC_GCR_DGW(0) | LTDC_GCR_DBW(0)); regval |= (LTDC_GCR_DRW(red) | LTDC_GCR_DGW(green) | LTDC_GCR_DBW(blue)); regvdbg("set LTDC_GCR=%08x\n", regval); putreg32(regval, STM32_LTDC_GCR); regvdbg("configured LTDC_GCR=%08x\n", getreg32(STM32_LTDC_GCR)); } /**************************************************************************** * Name: stm32_ltdc_interrupt * * Description: * Configures specific interrupt * * Parameter: * mask - Interrupt mask * value - Interrupt value * ****************************************************************************/ static void stm32_ltdc_interrupt(uint32_t mask, uint32_t value) { uint32_t regval; /* Get interrupts */ regval = getreg32(STM32_LTDC_IER); regvdbg("get LTDC_IER=%08x\n", regval); regval &= ~mask; /* Set interrupt */ regval |= value; regvdbg("set LTDC_IER=%08x\n", regval); putreg32(regval, STM32_LTDC_IER); regvdbg("configured LTDC_IER=%08x\n", getreg32(STM32_LTDC_IER)); /* Configure LTDC_LIPCR */ regvdbg("set LTDC_LIPCR=%08x\n", STM32_LTDC_LIPCR_LIPOS); putreg32(STM32_LTDC_LIPCR_LIPOS, STM32_LTDC_LIPCR); regvdbg("configured LTDC_LIPCR=%08x\n", getreg32(STM32_LTDC_LIPCR)); } /**************************************************************************** * Name: stm32_ltdc_reload * * Description: * Reload the layer shadow register and make layer changes visible * * Parameter: * value - Reload flag (e.g. upon vertical blank or immediately) * ****************************************************************************/ static void stm32_ltdc_reload(uint8_t value) { /* Check if last reload is finished */ while ((getreg32(STM32_LTDC_SRCR) & (LTDC_SRCR_VBR|LTDC_SRCR_IMR)) != 0) { } /* Reload shadow register */ regvdbg("set LTDC_SRCR=%08x\n", value & ~LTDC_SRCR_WAIT); putreg32(value & ~LTDC_SRCR_WAIT, STM32_LTDC_SRCR); regvdbg("configured LTDC_SRCR=%08x\n", getreg32(STM32_LTDC_SRCR)); /* Wait until registers reloaded */ if (value & LTDC_SRCR_WAIT) { while ((getreg32(STM32_LTDC_SRCR) & (value & ~LTDC_SRCR_WAIT)) != 0) { } } } /**************************************************************************** * Name: stm32_global_configure * * Description: * Configure background color * Configure interrupts * Configure dithering * ****************************************************************************/ static void stm32_global_configure(void) { /* Initialize the LTDC semaphore that enforces mutually exclusive access */ sem_init(&g_lock, 0, 1); /* Set the default active layer */ #ifndef CONFIG_STM32_LTDC_L2 g_lactive = LTDC_LAYER_L1; #else g_lactive = LTDC_LAYER_L2; #endif #ifdef STM32_LAYER_CLUT_SIZE /* cleanup clut */ memset(g_clut, 0, sizeof(g_clut)); #endif /* Configure dither */ stm32_ltdc_dither( #ifdef CONFIG_STM32_LTDC_DITHER true, #else false, #endif STM32_LTDC_DITHER_RED, STM32_LTDC_DITHER_GREEN, STM32_LTDC_DITHER_BLUE); /* Configure background color of the controller */ stm32_ltdc_bgcolor(STM32_LTDC_BACKCOLOR); /* Enable line interrupt */ stm32_ltdc_interrupt(STM32_LTDC_IER_LIE, 1); /* Disable fifo underrun interrupt */ stm32_ltdc_interrupt(STM32_LTDC_IER_FUIE, 0); /* Disable transfer error interrupt */ stm32_ltdc_interrupt(STM32_LTDC_IER_TERRIE, 0); /* Enable register reload interrupt */ stm32_ltdc_interrupt(STM32_LTDC_IER_RRIE, 1); } /**************************************************************************** * Name: stm32_lcd_enable * * Description: * Disable the LCD peripheral * * Parameter: * enable - Enable or disable * ****************************************************************************/ static void stm32_lcd_enable(bool enable) { uint32_t regval; regval = getreg32(STM32_LTDC_GCR); regvdbg("get LTDC_GCR=%08x\n", regval); if (enable == true) { regval |= LTDC_GCR_LTDCEN; } else { regval &= ~LTDC_GCR_LTDCEN; } regvdbg("set LTDC_GCR=%08x\n", regval); putreg32(regval, STM32_LTDC_GCR); regvdbg("configured LTDC_GCR=%08x\n", getreg32(STM32_LTDC_GCR)); } /**************************************************************************** * Configure layer register ****************************************************************************/ /**************************************************************************** * Name: stm32_ltdc_lclutenable * * Description: * Disable or enable the layer clut support * * Parameter: * layer - Reference to the layer control structure * enable - Enable or disable * ****************************************************************************/ #ifdef STM32_LAYER_CLUT_SIZE static void stm32_ltdc_lclutenable(FAR struct stm32_layer_s* layer, bool enable) { uint32_t regval; regval = getreg32(stm32_cr_layer_t[layer->state.lid]); regvdbg("get LTDC_L%dCR=%08x\n", layer->state.lid + 1, regval); /* Disable the clut support during update the color table */ if (enable == true) { regval |= LTDC_LxCR_CLUTEN; } else { regval &= ~LTDC_LxCR_CLUTEN; } regvdbg("set LTDC_L%dCR=%08x\n", layer->state.lid + 1, regval); putreg32(regval, stm32_cr_layer_t[layer->state.lid]); } #endif /**************************************************************************** * Name: stm32_ltdc_lsetopac * * Description: * Helper to set the layer to opac * * Parameter: * layer - Reference to the layer control structure * ****************************************************************************/ static inline void stm32_ltdc_lsetopac(FAR struct stm32_layer_s *layer) { layer->opac = 0xff; } /**************************************************************************** * Name: stm32_ltdc_lunsetopac * * Description: * Helper to set the layer opacity to the alpha value * * Parameter: * layer - Reference to the layer control structure * ****************************************************************************/ static inline void stm32_ltdc_lunsetopac(FAR struct stm32_layer_s *layer) { layer->opac = 0; } /**************************************************************************** * Name: stm32_ltdc_lgetopac * * Description: * Helper to get the configured layer opacity * * Parameter: * layer - Reference to the layer control structure * ****************************************************************************/ static inline uint8_t stm32_ltdc_lgetopac(FAR struct stm32_layer_s *layer) { return layer->opac | layer->state.alpha; } /**************************************************************************** * Name: stm32_ltdc_lvalidate * * Description: * Helper to check if the layer is an valid ltdc layer * * Parameter: * layer - Reference to the layer control structure * * Return: * true - layer valid * false - layer invalid * ****************************************************************************/ static inline bool stm32_ltdc_lvalidate(FAR struct stm32_layer_s *layer) { #ifdef CONFIG_STM32_LTDC_L2 return layer == &LAYER_L1 || layer == &LAYER_L2; #else return layer == &LAYER_L1; #endif } /**************************************************************************** * Name: stm32_ltdc_lvalidatearea * * Description: * Check if layer coordinates out of valid area. * * Parameter: * layer - Reference to the layer control structure * xpos - top left x position of the active area * ypos - top left y position of the active area * xres - width of the active area * yres - height of teh active area * srcxpos - Top left x position from where data visible in the active area * srcypos - Top left y position from where data visible in the active area * * Return: * On success - OK * On error - -EINVAL * ****************************************************************************/ #ifdef CONFIG_STM32_LTDC_INTERFACE static int stm32_ltdc_lvalidatearea(FAR struct stm32_layer_s *layer, fb_coord_t xpos, fb_coord_t ypos, fb_coord_t xres, fb_coord_t yres, fb_coord_t srcxpos, fb_coord_t srcypos) { FAR const struct fb_videoinfo_s *vinfo = &layer->state.vinfo; if ((xpos > vinfo->xres - 1) || (ypos > vinfo->yres -1) || (xres > vinfo->xres - xpos) || (yres > vinfo->yres - ypos) || (srcxpos > xpos + xres - 1) || (srcypos > ypos + yres - 1)) { gdbg("layer coordinates out of valid area: xpos = %d > %d, \ ypos = %d > %d, width = %d > %d, height = %d > %d, \ srcxpos = %d > %d, srcypos = %d > %d", xpos, vinfo->xres - 1, ypos, vinfo->yres - 1, xres, vinfo->xres - xpos, yres, vinfo->yres - ypos, srcxpos, xpos + xres - 1, srcypos, ypos + yres - 1); gdbg("Returning EINVAL\n"); return -EINVAL; } return OK; } #endif /**************************************************************************** * Name: stm32_ltdc_lupdate * * Description: * Updates shadow register content depending on the layer operation flag. * This made changes for the given layer visible after the next shadow * register reload. * * Parameter: * layer - Reference to the layer control structure * ****************************************************************************/ static void stm32_ltdc_lupdate(FAR struct stm32_layer_s *layer) { if (layer->operation & LTDC_LAYER_SETAREA) { /* Updates the layer horizontal and vertical position register */ stm32_ltdc_larea(layer); } if (layer->operation & LTDC_LAYER_SETALPHAVALUE) { /* Updates the constant alpha register */ stm32_ltdc_lalpha(layer); } if (layer->operation & LTDC_LAYER_SETBLENDMODE) { /* Update blendfactor 1 and 2 register */ stm32_ltdc_lblendmode(layer, layer->bf1, layer->bf2); } if (layer->operation & LTDC_LAYER_SETCOLORKEY) { /* Update layer colorkey register */ stm32_ltdc_lcolorkey(layer); } if (layer->operation & LTDC_LAYER_SETCOLOR) { /* Update layer color register */ stm32_ltdc_lcolor(layer, layer->state.color); } if (layer->operation & LTDC_LAYER_SETENABLE) { /* Enable the layer */ stm32_ltdc_lenable(layer); } } /**************************************************************************** * Name: stm32_ltdc_larea * * Description: * Change the active area of the layer * * Parameter: * layer - Reference to the layer control structure * ****************************************************************************/ static void stm32_ltdc_larea(struct stm32_layer_s *layer) { uint32_t lxpos; uint32_t lypos; uint32_t whpcr; uint32_t wvpcr; FAR struct stm32_ltdc_s *priv = &layer->state; FAR struct ltdc_area_s *area = &priv->area; regvdbg("xpos = %d, ypos = %d, xres = %d, yres = %d\n", area->xpos, area->ypos, area->xres, area->yres); lxpos = area->xpos + (STM32_LTDC_LxWHPCR_WHSTPOS + 1); lypos = area->ypos + (STM32_LTDC_LxWVPCR_WVSTPOS + 1); /* Accumulate horizontal position */ whpcr = LTDC_LxWHPCR_WHSTPOS(lxpos); whpcr |= LTDC_LxWHPCR_WHSPPOS(lxpos + area->xres - 1); /* Accumulate vertical position */ wvpcr = LTDC_LxWVPCR_WVSTPOS(lypos); wvpcr |= LTDC_LxWVPCR_WVSPPOS(lypos + area->yres - 1); /* Configure LxWHPCR / LxWVPCR register */ regvdbg("set LTDC_L%dWHPCR=%08x\n", priv->lid + 1, whpcr); putreg32(whpcr, stm32_whpcr_layer_t[priv->lid]); regvdbg("set LTDC_L%dWVPCR=%08x\n", priv->lid + 1, wvpcr); putreg32(wvpcr, stm32_wvpcr_layer_t[priv->lid]); /* Configure framebuffer */ stm32_ltdc_lframebuffer(layer); /* Clear area operation flag */ layer->operation &= ~LTDC_LAYER_SETAREA; } /**************************************************************************** * Name: stm32_ltdc_lpixelformat * * Description: * Set the layer pixel format. * Note! This changes have no effect until the shadow register reload has * been done. * * Parameter: * Reference to the layer control structure * ****************************************************************************/ static void stm32_ltdc_lpixelformat(FAR struct stm32_layer_s *layer) { /* Configure PFCR register */ regvdbg("set LTDC_L%dPFCR=%08x\n", layer->state.lid + 1, stm32_fmt_layer_t[layer->state.lid]); putreg32(stm32_fmt_layer_t[layer->state.lid], stm32_pfcr_layer_t[layer->state.lid]); } /**************************************************************************** * Name: stm32_ltdc_framebuffer * * Description: * Change layer framebuffer offset. * Note! This changes have no effect until the shadow register reload has * been done. * * Parameter: * Reference to the layer control structure * ****************************************************************************/ static inline void stm32_ltdc_lframebuffer(FAR struct stm32_layer_s *layer) { uint32_t offset; uint32_t cfblr; FAR struct stm32_ltdc_s *priv = &layer->state; FAR struct ltdc_area_s *area = &priv->area; FAR const struct fb_planeinfo_s *pinfo = &priv->pinfo; /* Configure LxCFBAR register */ /* Calculate offset position in the framebuffer */ offset = priv->xpos * STM32_LTDC_Lx_BYPP(pinfo->bpp) + pinfo->stride * priv->ypos; regvdbg("set LTDC_L%dCFBAR=%08x\n", priv->lid + 1, pinfo->fbmem + offset); putreg32(pinfo->fbmem + offset, stm32_cfbar_layer_t[priv->lid]); /* Configure LxCFBLR register */ /* Calculate line length */ cfblr = LTDC_LxCFBLR_CFBP(pinfo->stride) | LTDC_LxCFBLR_CFBLL(area->xres * STM32_LTDC_Lx_BYPP(pinfo->bpp) + 3); regvdbg("set LTDC_L%dCFBLR=%08x\n", priv->lid + 1, cfblr); putreg32(cfblr, stm32_cfblr_layer_t[priv->lid]); /* Configure LxCFBLNR register */ regvdbg("set LTDC_L%dCFBLNR=%08x\n", priv->lid + 1, area->yres); putreg32(area->yres, stm32_cfblnr_layer_t[priv->lid]); } /**************************************************************************** * Name: stm32_ltdc_lalpha * * Description: * Change the layer alpha value and clear the alpha operation flag. * Note! This changes have no effect until the shadow register reload has * been done. * * Parameter: * layer - Reference to the layer control structure * ****************************************************************************/ static void stm32_ltdc_lalpha(FAR struct stm32_layer_s *layer) { uint8_t opac = stm32_ltdc_lgetopac(layer); regvdbg("set LTDC_L%dCACR=%02x\n", layer->state.lid + 1, opac); putreg32(opac, stm32_cacr_layer_t[layer->state.lid]); /* Clear the constant alpha operation flag */ layer->operation &= ~LTDC_LAYER_SETALPHAVALUE; } /**************************************************************************** * Name: stm32_ltdc_blendfactor * * Description: * Change layer blend factors used for blend operation and clear the * blendmode operation flag. * Note! This changes have no effect until the shadow register reload has * been done. * * Parameter: * layer - Reference to the laxer control structure * bf1 - Value of blend factor 1 * bf2 - Value of blend factor 2 * ****************************************************************************/ static void stm32_ltdc_lblendmode(FAR struct stm32_layer_s *layer, uint8_t bf1, uint8_t bf2) { regvdbg("set LTDC_L%dBFCR=%08x\n", layer->state.lid + 1, (LTDC_LxBFCR_BF1(bf1) | LTDC_LxBFCR_BF2(bf2))); putreg32((LTDC_LxBFCR_BF1(bf1) | LTDC_LxBFCR_BF2(bf2)), stm32_bfcr_layer_t[layer->state.lid]); /* Clear the blendmode operation flag */ layer->operation &= ~LTDC_LAYER_SETBLENDMODE; } /**************************************************************************** * Name: stm32_ltdc_lcolor * * Description: * Change layer default color and clear the color operation flag. * Note! This changes have no effect until the shadow register reload has * been done. * * Parameter: * layer - Reference to the layer control structure * ****************************************************************************/ static void stm32_ltdc_lcolor(FAR struct stm32_layer_s *layer, uint32_t argb) { regvdbg("set LTDC_L%dDCCR=%08x\n", layer->state.lid + 1, argb); putreg32(argb, stm32_dccr_layer_t[layer->state.lid]); /* Clear the color operation flag */ layer->operation &= ~LTDC_LAYER_SETCOLOR; } /**************************************************************************** * Name: stm32_ltdc_lcolorkey * * Description: * Change layer colorkey and clear the colorkey operation flag. * Note! This changes have no effect until the shadow register reload has * been done. * * Parameter: * layer - Reference to the layer control structure * ****************************************************************************/ static void stm32_ltdc_lcolorkey(FAR struct stm32_layer_s *layer) { uint32_t regval; regval = getreg32(stm32_cr_layer_t[layer->state.lid]); if (layer->operation & LTDC_LAYER_ENABLECOLORKEY) { /* Set colorkey */ regvdbg("set LTDC_L%dCKCR=%08x\n", layer->state.lid + 1, layer->state.colorkey); putreg32(layer->state.colorkey, stm32_ckcr_layer_t[layer->state.lid]); /* Enable colorkey */ regval |= LTDC_LxCR_COLKEN; } else { /* Disable colorkey */ regval &= ~LTDC_LxCR_COLKEN; } regvdbg("set LTDC_L%dCR=%08x\n", layer->state.lid + 1, regval); putreg32(regval, stm32_cr_layer_t[layer->state.lid]); /* Clear the colorkey operation flag */ layer->operation &= ~LTDC_LAYER_SETCOLORKEY; } /**************************************************************************** * Name: stm32_ltdc_lclut * * Description: * Update the clut layer register during blank period. * Note! The clut register are no shadow register. * * Parameter: * layer - Reference to the layer control structure * ****************************************************************************/ #ifdef STM32_LAYER_CLUT_SIZE static void stm32_ltdc_lclut(FAR struct stm32_layer_s *layer) { int n; uint32_t regval; FAR struct fb_cmap_s *cmap = layer->state.cmap; irqstate_t flags; /* Disable clut during register update */ stm32_ltdc_lclutenable(layer, false); /* Reload shadow control register. * This never changed any layer setting as long the layer register not up to * date. This is what stm32_update does. */ stm32_ltdc_reload(LTDC_SRCR_IMR); flags = irqsave(); /* Update the clut registers */ for (n = cmap->first; n < cmap->len - cmap->first; n++) { regval = (uint32_t)LTDC_CLUT_ADD(n) | (uint32_t)LTDC_CLUT_RED(cmap->red[n]) | (uint32_t)LTDC_CLUT_GREEN(cmap->green[n]) | (uint32_t)LTDC_CLUT_BLUE(cmap->blue[n]); regvdbg("set LTDC_L%dCLUTWR = %08x, cmap->first = %d, cmap->len = %d\n", layer->state.lid + 1, regval, cmap->first, cmap->len); putreg32(regval, stm32_clutwr_layer_t[layer->state.lid]); } irqrestore(flags); /* Enable clut */ stm32_ltdc_lclutenable(layer, true); /* Reload shadow control register */ stm32_ltdc_reload(LTDC_SRCR_IMR); } #endif /**************************************************************************** * Name: stm32_ltdc_lenable * * Description: * Disable or enable specific layer. * Note! This changes have no effect until the shadow register reload has * been done. * * Parameter: * layer - Reference to the layer control structure * ****************************************************************************/ static void stm32_ltdc_lenable(FAR struct stm32_layer_s *layer) { uint32_t regval; /* Enable or disable layer */ regval = getreg32(stm32_cr_layer_t[layer->state.lid]); if (layer->operation & LTDC_LAYER_ENABLE) { regval |= LTDC_LxCR_LEN; } else { regval &= ~LTDC_LxCR_LEN; } regvdbg("set LTDC_L%dCR=%08x\n", layer->state.lid + 1, regval); putreg32(regval, stm32_cr_layer_t[layer->state.lid]); /* Clear the enable operation flag */ layer->operation &= ~LTDC_LAYER_SETENABLE; } /**************************************************************************** * Name stm32_ltdc_lclear * * Description: * Clear the whole layer * * Parameter: * layer - Reference to the layer control structure * color - The color to clear * * Return: * OK - On success * -EINVAL - If one of the parameter invalid * ****************************************************************************/ static void stm32_ltdc_lclear(FAR struct stm32_layer_s *layer, nxgl_mxpixel_t color) { FAR struct stm32_ltdc_s *priv = &layer->state; #if STM32_LTDC_L1_BPP == 8 || STM32_LTDC_L2_BPP == 8 if (priv->pinfo.bpp == 8) { uint8_t *dest = (uint8_t*)priv->pinfo.fbmem; int i; gvdbg("Clearing display: BPP=%d color=%04x framebuffer=%08x size=%d\n", priv->pinfo.bpp, color, dest, priv->pinfo.fblen); for (i = 0; i < priv->pinfo.fblen; i += sizeof(uint8_t)) { *dest++ = (uint8_t)color; } return; } #endif #if STM32_LTDC_L1_BPP == 16 || STM32_LTDC_L2_BPP == 16 if (priv->pinfo.bpp == 16) { uint16_t *dest = (uint16_t*)priv->pinfo.fbmem; int i; gvdbg("Clearing display: BPP=%d color=%04x framebuffer=%08x size=%d\n", priv->pinfo.bpp, color, dest, priv->pinfo.fblen); for (i = 0; i < priv->pinfo.fblen; i += sizeof(uint16_t)) { *dest++ = (uint16_t)color; } return; } #endif #if STM32_LTDC_L1_BPP == 24 || STM32_LTDC_L2_BPP == 24 if (priv->pinfo.bpp == 24) { uint8_t *dest = (uint8_t*)priv->pinfo.fbmem; uint8_t r; uint8_t g; uint8_t b; int i; gvdbg("Clearing display: BPP=%d color=%04x framebuffer=%08x size=%d\n", priv->pinfo.bpp, color, dest, priv->pinfo.fblen); r = (uint8_t) color; g = (uint8_t) (color >> 8); b = (uint8_t) (color >> 16); for (i = 0; i < priv->pinfo.fblen; i += 3*sizeof(uint8_t)) { *dest++ = r; *dest++ = g; *dest++ = b; } return; } #endif #if STM32_LTDC_L1_BPP == 32 || STM32_LTDC_L2_BPP == 32 if (priv->pinfo.bpp == 32) { uint32_t *dest = (uint32_t*)priv->pinfo.fbmem; int i; gvdbg("Clearing display: BPP=%d color=%04x framebuffer=%08x size=%d\n", priv->pinfo.bpp, color, dest, priv->pinfo.fblen); for (i = 0; i < priv->pinfo.fblen; i += sizeof(uint32_t)) { *dest++ = (uint32_t)color; } } #endif } /**************************************************************************** * Name: stm32_ltdc_linit * * Description: * Initialize layer to their default states. * * Initialize: * - Reset layer * - layer fram * - Reset layerebuffers * - layer position * - layer pixelformat * - layer color * - layer colorkey * - layer alpha * - layer blendmode * * Parameter * layer - Reference to the layer control structure * ****************************************************************************/ static void stm32_ltdc_linit(int lid) { /* Reset layer to their default state */ FAR struct stm32_layer_s *layer = &LAYER(lid); FAR struct stm32_ltdc_s *state = &layer->state; #ifdef CONFIG_STM32_LTDC_INTERFACE FAR struct ltdc_layer_s *ltdc = &layer->ltdc; /* Initialize the ltdc interface */ ltdc->getlid = stm32_getlid; ltdc->getvideoinfo = stm32_lgetvideoinfo; ltdc->getplaneinfo = stm32_lgetplaneinfo; # ifdef STM32_LAYER_CLUT_SIZE ltdc->setclut = stm32_setclut; ltdc->getclut = stm32_getclut; #endif ltdc->setcolor = stm32_setcolor; ltdc->getcolor = stm32_getcolor; ltdc->setcolorkey = stm32_setcolorkey; ltdc->getcolorkey = stm32_getcolorkey; ltdc->setalpha = stm32_setalpha; ltdc->getalpha = stm32_getalpha; ltdc->setblendmode = stm32_setblendmode; ltdc->getblendmode = stm32_getblendmode; ltdc->setarea = stm32_setarea; ltdc->getarea = stm32_getarea; ltdc->update = stm32_update; #ifdef CONFIG_STM32_DMA2D ltdc->blit = stm32_blit; ltdc->blend = stm32_blend; #endif #endif /* Initialize the layer state */ #ifdef STM32_LAYER_CLUT_SIZE state->cmap->first = 0; #endif state->area.xpos = 0; state->area.ypos = 0; state->area.xres = STM32_LTDC_WIDTH; state->area.yres = STM32_LTDC_HEIGHT; state->xpos = 0; state->ypos = 0; state->color = 0; state->colorkey = 0; state->alpha = 0xff; state->blendmode = LTDC_BLEND_NONE; state->lock = &g_lock; /* Initialize driver internals */ layer->opac = 0xff; layer->bf1 = LTDC_BF1_CONST_ALPHA; layer->bf2 = LTDC_BF2_CONST_ALPHA; layer->operation = LTDC_LAYER_INIT; /* Clear the layer framebuffer */ stm32_ltdc_lclear(layer, 0); /* Set Pixel input format */ stm32_ltdc_lpixelformat(layer); /* Set position, color, colorkey, blendmode, alpha */ stm32_ltdc_lupdate(layer); #ifdef STM32_LAYER_CLUT_SIZE /* Disable clut by default */ if (layer->state.vinfo.fmt == FB_FMT_RGB8) { stm32_ltdc_lclutenable(layer, false); } #endif } /**************************************************************************** * Name: stm32_ltdc_cmapcpy * * Description: * Copies a color table * Note! Caller must check if first position is out of range * * Parameter: * dest - color cmap to copy to * src - color map to copy from * * Return: * On success - OK * On error - -EINVAL * ****************************************************************************/ #ifdef STM32_LAYER_CLUT_SIZE static void stm32_ltdc_cmapcpy(FAR struct fb_cmap_s *dest, FAR const struct fb_cmap_s *src) { int n; for (n = src->first; n < src->len && n < STM32_LTDC_CLUT_ENTRIES; n++) { regvdbg("n = %d, red = %02x, green = %02x, blue = %02x\n", n, src->red[n], src->green[n], src->blue[n]); dest->red[n] = src->red[n]; dest->green[n] = src->green[n]; dest->blue[n] = src->blue[n]; } dest->first = src->first; dest->len = src->len; } #endif /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: stm32_getvideoinfo * * Description: * Get the videoinfo for the framebuffer * * Parameter: * vtable - The framebuffer driver object * vinfo - the videoinfo object * * Return: * On success - OK * On error - -EINVAL * ****************************************************************************/ static int stm32_getvideoinfo(struct fb_vtable_s *vtable, struct fb_videoinfo_s *vinfo) { gvdbg("vtable=%p vinfo=%p\n", vtable, vinfo); if (vtable) { FAR struct ltdc_layer_s *ltdc; #ifdef CONFIG_STM32_LTDC_L2 ltdc = (FAR struct ltdc_layer_s*)&LAYER_L2; #else ltdc = (FAR struct ltdc_layer_s*)&LAYER_L1; #endif return stm32_lgetvideoinfo(ltdc, vinfo); } gdbg("ERROR: Returning EINVAL\n"); return -EINVAL; } /**************************************************************************** * Name: stm32_getplaneinfo * * Description: * Get the planeinfo for the framebuffer * * Parameter: * vtable - The framebuffer driver object * pinfo - the planeinfo object * * Return: * On success - OK * On error - -EINVAL * ****************************************************************************/ static int stm32_getplaneinfo(struct fb_vtable_s *vtable, int planeno, struct fb_planeinfo_s *pinfo) { gvdbg("vtable=%p planeno=%d pinfo=%p\n", vtable, planeno, pinfo); if (vtable) { FAR struct ltdc_layer_s *ltdc; #ifdef CONFIG_STM32_LTDC_L2 ltdc = (FAR struct ltdc_layer_s*)&LAYER_L2; #else ltdc = (FAR struct ltdc_layer_s*)&LAYER_L1; #endif return stm32_lgetplaneinfo(ltdc, planeno, pinfo); } gdbg("Returning EINVAL\n"); return -EINVAL; } /**************************************************************************** * Name: stm32_getcmap * * Description: * Get a range of CLUT values for the LCD * * Parameter: * vtable - The framebuffer driver object * cmap - the color table * * Return: * On success - OK * On error - -EINVAL * ****************************************************************************/ #ifdef STM32_LAYER_CLUT_SIZE static int stm32_getcmap(struct fb_vtable_s *vtable, struct fb_cmap_s *cmap) { #ifdef CONFIG_STM32_LTDC_L2 return stm32_getclut((FAR struct ltdc_layer_s*)&LAYER_L2, cmap); #else return stm32_getclut((FAR struct ltdc_layer_s*)&LAYER_L1, cmap); #endif } /**************************************************************************** * Name: stm32_putcmap * * Description: * Set a range of the CLUT values for the LCD * * Parameter: * vtable - The framebuffer driver object * cmap - the color table * * Return: * On success - OK * On error - -EINVAL * ****************************************************************************/ static int stm32_putcmap(struct fb_vtable_s *vtable, const struct fb_cmap_s *cmap) { #ifdef CONFIG_STM32_LTDC_L2 return stm32_setclut((FAR struct ltdc_layer_s*)&LAYER_L2, cmap); #else return stm32_setclut((FAR struct ltdc_layer_s*)&LAYER_L1, cmap); #endif } #endif /* STM32_LAYER_CLUT_SIZE */ /**************************************************************************** * Name: stm32_lgetvideoinfo * * 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_lgetvideoinfo(struct ltdc_layer_s *layer, struct fb_videoinfo_s *vinfo) { gvdbg("layer=%p vinfo=%p\n", layer, vinfo); FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer; if (priv == &LAYER_L1 || priv == &LAYER_L2) { memcpy(vinfo, &priv->state.vinfo, sizeof(struct fb_videoinfo_s)); return OK; } gdbg("Returning EINVAL\n"); return -EINVAL; } /**************************************************************************** * Name: stm32_lgetplaneinfo * * 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_lgetplaneinfo(struct ltdc_layer_s *layer, int planeno, struct fb_planeinfo_s *pinfo) { gvdbg("layer=%p planeno=%d pinfo=%p\n", layer, planeno, pinfo); FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer; if (stm32_ltdc_lvalidate(priv) && planeno == 0) { memcpy(pinfo, &priv->state.pinfo, sizeof(struct fb_planeinfo_s)); return OK; } gdbg("Returning EINVAL\n"); return -EINVAL; } /**************************************************************************** * Name: stm32_setclut * * Description: * Configure layer clut (color lookup table). * Non clut is defined during initializing. * Clut is active during next vertical blank period. Do not need an update. * * Parameter: * layer - Reference to the layer structure * cmap - color lookup table with up the 256 entries * * Return: * On success - OK * On error - -EINVAL * ****************************************************************************/ #ifdef STM32_LAYER_CLUT_SIZE static int stm32_setclut(struct ltdc_layer_s *layer, const struct fb_cmap_s *cmap) { int ret; FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer; gvdbg("layer=%p cmap=%p\n", layer, cmap); if (stm32_ltdc_lvalidate(priv) && cmap) { sem_wait(priv->state.lock); if (priv->state.vinfo.fmt != FB_FMT_RGB8) { gdbg("Error: CLUT is not supported for the pixel format: %d\n", priv->state.vinfo.fmt); ret = -EINVAL; } else if (cmap->first > STM32_LTDC_CLUT_ENTRIES) { gdbg("Error: CLUT offset is out of range: %d\n", cmap->first); ret = -EINVAL; } else { /* Copy to the layer cmap */ stm32_ltdc_cmapcpy(priv->state.cmap, cmap); /* Update layer clut register */ stm32_ltdc_lclut(priv); ret = OK; } sem_post(priv->state.lock); return ret; } gdbg("Returning EINVAL\n"); return -EINVAL; } /**************************************************************************** * Name: stm32_getclut * * 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_getclut(struct ltdc_layer_s *layer, struct fb_cmap_s *cmap) { int ret; FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer; gvdbg("layer=%p cmap=%p\n", layer, cmap); if (priv == &LAYER_L1 || priv == &LAYER_L2) { sem_wait(priv->state.lock); if (priv->state.vinfo.fmt != FB_FMT_RGB8) { gdbg("Error: CLUT is not supported for the pixel format: %d\n", priv->state.vinfo.fmt); ret = -EINVAL; } else if (cmap->len < priv->state.cmap->len) { gdbg("Error: cmap must accept %d color table entries: %d\n", priv->state.cmap->len); ret = -EINVAL; } else { /* Copy from the layer cmap */ stm32_ltdc_cmapcpy(cmap, priv->state.cmap); ret = OK; } sem_post(priv->state.lock); return ret; } gdbg("Returning EINVAL\n"); return -EINVAL; } #endif /* STM32_LAYER_CLUT_SIZE */ #ifdef CONFIG_STM32_LTDC_INTERFACE /**************************************************************************** * Name: getlid * * Description: * Get a specific layer identifier. * * Parameter: * layer - Reference to the layer structure * lid - Reference to store the layer id * flag - Operation flag describe the layer identifier * e.g. get the current active or inactive layer. * See LTDC_LAYER_* for possible values * * Return: * OK - On success * Null if invalid flag * ****************************************************************************/ static int stm32_getlid(FAR struct ltdc_layer_s *layer, int *lid, uint32_t flag) { FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer; gvdbg("flag = %08x\n", flag); if (stm32_ltdc_lvalidate(priv)) { int ret = OK; sem_wait(priv->state.lock); switch (flag) { case LTDC_LAYER_OWN: *lid = priv->state.lid; break; #ifdef CONFIG_STM32_LTDC_L2 case LTDC_LAYER_ACTIVE: *lid = g_lactive; break; case LTDC_LAYER_INACTIVE: *lid = !g_lactive; break; case LTDC_LAYER_TOP: *lid = LTDC_LAYER_L2; break; case LTDC_LAYER_BOTTOM: *lid = LTDC_LAYER_L1; break; #else case LTDC_LAYER_ACTIVE: case LTDC_LAYER_INACTIVE: case LTDC_LAYER_TOP: case LTDC_LAYER_BOTTOM: *lid = LTDC_LAYER_L1; break; #endif default: ret = EINVAL; gdbg("Returning EINVAL\n"); break; } sem_post(priv->state.lock); return ret; } gdbg("Returning EINVAL\n"); return -EINVAL; } /**************************************************************************** * Name: stm32_setcolor * * Description: * Configure layer default color value for the non active layer area. * Default value during initializing: 0x00000000 * Color is active after next update. * * Parameter: * layer - Reference to the layer structure * argb - ARGB8888 color value * * Return: * On success - OK * On error - -EINVAL * ****************************************************************************/ static int stm32_setcolor(FAR struct ltdc_layer_s *layer, uint32_t argb) { FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer; gvdbg("layer = %p, argb = %08x\n", layer, argb); if (stm32_ltdc_lvalidate(priv)) { sem_wait(priv->state.lock); priv->state.color = argb; priv->operation |= LTDC_LAYER_SETCOLOR; sem_post(priv->state.lock); return OK; } gdbg("Returning EINVAL\n"); return -EINVAL; } /**************************************************************************** * Name: stm32_getcolor * * Description: * Get configured layer color for the non active layer area. * * Parameter: * layer - Reference to the layer structure * argb - Reference to store the ARGB8888 color value * * Return: * On success - OK * On error - -EINVAL * *******************************************************************************/ static int stm32_getcolor(FAR struct ltdc_layer_s *layer, uint32_t *argb) { FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer; gvdbg("layer = %p, argb = %p\n", layer, argb); if (stm32_ltdc_lvalidate(priv)) { sem_wait(priv->state.lock); *argb = priv->state.color; sem_post(priv->state.lock); return OK; } gdbg("Returning EINVAL\n"); return -EINVAL; } /**************************************************************************** * Name: stm32_setcolorkey * * Description: * Configure layer default color key (chromakey) value for transparency. * Layer default value during initializing: 0x00000000 * Colorkey is active after next update. * * Parameter: * layer - Reference to the layer structure * rgb - RGB888 color value * * Return: * On success - OK * On error - -EINVAL * ****************************************************************************/ static int stm32_setcolorkey(FAR struct ltdc_layer_s *layer, uint32_t rgb) { FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer; gvdbg("layer = %p, argb = %08x\n", layer, rgb); if (stm32_ltdc_lvalidate(priv)) { sem_wait(priv->state.lock); priv->state.colorkey = rgb; priv->operation |= LTDC_LAYER_SETCOLORKEY; sem_post(priv->state.lock); return OK; } gdbg("Returning EINVAL\n"); return -EINVAL; } /**************************************************************************** * Name: stm32_getcolorkey * * Description: * Get the configured layer color key (chromakey) for transparency. * * Parameter: * layer - Reference to the layer structure * rgb - Reference to store the RGB888 color key * * Return: * On success - OK * On error - -EINVAL * ****************************************************************************/ static int stm32_getcolorkey(FAR struct ltdc_layer_s *layer, uint32_t *rgb) { FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer; gvdbg("layer = %p, argb = %p\n", layer, rgb); if (stm32_ltdc_lvalidate(priv)) { sem_wait(priv->state.lock); *rgb = priv->state.colorkey; sem_post(priv->state.lock); return OK; } gdbg("Returning EINVAL\n"); return -EINVAL; } /**************************************************************************** * Name: setalpha * * 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 alpha value during initializing: 0xff * Alpha is active after next update. * * Parameter: * layer - Reference to the layer structure * alpha - Alpha value * * Return: * On success - OK * On error - -EINVAL * ****************************************************************************/ static int stm32_setalpha(FAR struct ltdc_layer_s *layer, uint8_t alpha) { FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer; gvdbg("layer = %p, alpha = %02x\n", layer, alpha); if (stm32_ltdc_lvalidate(priv)) { sem_wait(priv->state.lock); priv->state.alpha = alpha; priv->operation |= LTDC_LAYER_SETALPHAVALUE; sem_post(priv->state.lock); return OK; } gdbg("Returning EINVAL\n"); return -EINVAL; } /**************************************************************************** * Name: stm32_getalpha * * 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_getalpha(FAR struct ltdc_layer_s *layer, uint8_t *alpha) { FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer; gvdbg("layer = %p, alpha = %p\n", layer, alpha); if (stm32_ltdc_lvalidate(priv)) { sem_wait(priv->state.lock); *alpha = priv->state.alpha; sem_post(priv->state.lock); return OK; } gdbg("Returning EINVAL\n"); return -EINVAL; } /**************************************************************************** * Name: setblendmode * * Description: * Configure blend mode of the layer. * Default mode during initialzing: LTDC_BLEND_NONE * Blendmode is active after next update. * * Parameter: * layer - Reference to the layer structure * mode - Blend mode (see LTDC_BLEND_*) * * Return: * On success - OK * On error - -EINVAL * * Procedure information: * LTDC_BLEND_NONE: * Informs the driver to disable all blend operation for the given layer. * That means the layer is opaque. Note this has no effect on the * colorkey settings. * * LTDC_BLEND_ALPHA: * Informs the driver to enable alpha blending for the given layer. * * LTDC_BLEND_COLORKEY: * Informs the driver to enable colorkeying for the given layer. * * LTDC_BLEND_SRCPIXELALPHA: * 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. * * LTDC_BLEND_DESTPIXELALPHA: * Informs the driver to use the pixel alpha value of the subjacent layer * instead the constant alpha value. This is only useful for ARGB8888 * color format. * ****************************************************************************/ static int stm32_setblendmode(FAR struct ltdc_layer_s *layer, uint32_t mode) { FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer; gvdbg("layer = %p, mode = %08x\n", layer, mode); if (stm32_ltdc_lvalidate(priv)) { int ret = OK; sem_wait(priv->state.lock); /* Disable alpha blending by default */ priv->bf1 = LTDC_BF1_CONST_ALPHA; priv->bf2 = LTDC_BF2_CONST_ALPHA; stm32_ltdc_lsetopac(priv); /* Disable colorkeying by default */ priv->operation &=~ LTDC_LAYER_ENABLECOLORKEY; if (mode & LTDC_BLEND_ALPHA) { /* Initialize blend factor */ if (mode & LTDC_BLEND_SRCPIXELALPHA) { priv->bf1 = LTDC_BF1_PIXEL_ALPHA; } else if (mode & LTDC_BLEND_DESTPIXELALPHA) { priv->bf2 = LTDC_BF2_PIXEL_ALPHA; } /* Enable blending, restore the alpha value */ stm32_ltdc_lunsetopac(priv); mode &= ~LTDC_BLEND_ALPHA; } if (mode & LTDC_BLEND_COLORKEY) { /* Enable colorkeying */ priv->operation |= LTDC_LAYER_ENABLECOLORKEY; mode &= ~LTDC_BLEND_COLORKEY; } if (mode & ~(LTDC_BLEND_SRCPIXELALPHA|LTDC_BLEND_DESTPIXELALPHA)) { gdbg("Unknown blendmode %02x\n", mode); ret = -EINVAL; } if (ret == OK) { priv->state.blendmode = mode; priv->operation |= (LTDC_LAYER_SETBLENDMODE| LTDC_LAYER_SETALPHAVALUE| LTDC_LAYER_SETCOLORKEY); } sem_post(priv->state.lock); return ret; } gdbg("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_getblendmode(FAR struct ltdc_layer_s *layer, uint32_t *mode) { FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer; gvdbg("layer = %p, mode = %p\n", layer, mode); if (stm32_ltdc_lvalidate(priv)) { sem_wait(priv->state.lock); *mode = priv->state.blendmode; sem_post(priv->state.lock); return OK; } gdbg("Returning EINVAL\n"); return -EINVAL; } /**************************************************************************** * Name: stm32_setarea * * Description: * Configure visible layer area and the reference position of the first * pixel of the whole layer which is the first visible top left pixel in * the active area. * Area is active after next update. * * Parameter: * layer - Reference to the layer control structure * area - Reference to the valid area structure for the new active area * srcxpos - x position of the visible pixel of the whole layer * srcypos - y position of the visible pixel of the whole layer * * Return: * On success - OK * On error - -EINVAL * * Procedure Information: * If the srcxpos and srcypos unequal the the xpos and ypos of the area * structure this acts like moving the visible area to another position on * the screen during the next update operation. * ****************************************************************************/ static int stm32_setarea(FAR struct ltdc_layer_s *layer, FAR const struct ltdc_area_s *area, fb_coord_t srcxpos, fb_coord_t srcypos) { FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer; gvdbg("layer = %p, area = %p, srcxpos = %d, srcypos = %d\n", layer, area, srcxpos, srcypos); if (stm32_ltdc_lvalidate(priv)) { int ret; sem_wait(priv->state.lock); ret = stm32_ltdc_lvalidatearea(priv, area->xpos, area->ypos, area->xres, area->yres, srcxpos, srcypos); if (ret == OK) { priv->state.xpos = srcxpos; priv->state.ypos = srcypos; priv->state.area.xpos = area->xpos; priv->state.area.ypos = area->ypos; priv->state.area.xres = area->xres; priv->state.area.yres = area->yres; priv->operation |= LTDC_LAYER_SETAREA; } sem_post(priv->state.lock); return ret; } gdbg("Returning EINVAL\n"); return -EINVAL; } /**************************************************************************** * Name: stm32_getarea * * Description: * Get configured visible layer area. * * Parameter: * layer - Reference to the layer control structure * area - Reference to the area structure to store the active area * srcxpos - Reference to store the referenced x position of the whole layer * srcypos - Reference to store the reterenced y position of the whole layer * * Return: * On success - OK * On error - -EINVAL * ****************************************************************************/ static int stm32_getarea(FAR struct ltdc_layer_s *layer, FAR struct ltdc_area_s *area, fb_coord_t *srcxpos, fb_coord_t *srcypos) { FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer; gvdbg("layer = %p, area = %p, srcxpos = %p, srcypos = %p\n", layer, area, srcxpos, srcypos); if (stm32_ltdc_lvalidate(priv)) { sem_wait(priv->state.lock); *srcxpos = priv->state.xpos; *srcypos = priv->state.ypos; memcpy(area, &priv->state.area, sizeof(struct ltdc_area_s)); sem_post(priv->state.lock); return OK; } gdbg("Returning EINVAL\n"); return -EINVAL; } /**************************************************************************** * Name: stm32_update * * Description: * Update current layer settings and make changes visible. * * Parameter: * layer - Reference to the layer structure * mode - operation mode * * Return: * OK - On success * -EINVAL - If one of the parameter invalid * * Procedure information: * LTDC_UPDATE_SIM: * Informs the driver to update both layers simultaneously. Otherwise update * the given layer only. * * LTDC_UPDATE_FLIP: * Informs the driver to perform a flip operation. * This only effects the ltdc layer 1 and 2 and can be useful for double * buffering. Each flip operation changed the active layer to the inactive * and vice versa. In the context of the ltdc that means, the inactive layer * is complete disabled. So the subjacent layer is the background layer * (background color). To reactivate both layer and their current settings * perform an update without LTDC_UPDATE_FLIP flag. * * LTDC_UPDATE_ACTIVATE: * Informs the driver that the given layer should be the active layer when * the operation is complete. * * LTDC_SYNC_VBLANK: * Informs the driver to update the layer upon vertical blank. Otherwise * immediately. * ****************************************************************************/ static int stm32_update(FAR struct ltdc_layer_s *layer, uint32_t mode) { FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer; #ifdef CONFIG_STM32_LTDC_L2 FAR struct stm32_layer_s *active = &LAYER(g_lactive); FAR struct stm32_layer_s *inactive = &LAYER(!g_lactive); #endif gvdbg("layer = %p, mode = %08x\n", layer, mode); if (stm32_ltdc_lvalidate(priv)) { /* Reload immediately by default */ uint8_t reload = LTDC_SRCR_IMR; sem_wait(priv->state.lock); if (mode & LTDC_SYNC_VBLANK) { reload = LTDC_SRCR_VBR; } if (mode & LTDC_SYNC_WAIT) { reload |= LTDC_SRCR_WAIT; } /* Update the given layer */ stm32_ltdc_lupdate(priv); #ifdef CONFIG_STM32_LTDC_L2 /* The following operation only useful if layer 2 is supported. * Otherwise ignore it. */ if (mode & LTDC_UPDATE_SIM) { /* Also update the flip layer */ stm32_ltdc_lupdate(&LAYER(!priv->state.lid)); } if (mode & LTDC_UPDATE_ACTIVATE) { /* Set the given layer to the next active layer */ g_lactive = priv->state.lid; /* Also change the current active layer for flip operation */ active = &LAYER(!g_lactive); } if (mode & LTDC_UPDATE_FLIP) { /* Reset if manipulated by ACTIVATE flag */ inactive = &LAYER(!active->state.lid); /* Set blendfactor for current active layer to there reset value */ stm32_ltdc_lblendmode(active, STM32_LTDC_BF1_RESET, STM32_LTDC_BF2_RESET); /* Set blendfactor for current inactive layer */ stm32_ltdc_lblendmode(inactive, inactive->bf1, inactive->bf2); /* Disable the active layer */ active->operation &= ~LTDC_LAYER_ENABLE; stm32_ltdc_lenable(active); /* Enable the inactive layer */ inactive->operation |= LTDC_LAYER_ENABLE; stm32_ltdc_lenable(inactive); /* Ensure that both layer active and the manipulated layer * settings restored during the next update (non flip) operation */ active->operation |= (LTDC_LAYER_SETBLENDMODE| LTDC_LAYER_ENABLE| LTDC_LAYER_SETCOLOR| LTDC_LAYER_SETENABLE); /* Change layer activity */ g_lactive = inactive->state.lid; } #endif /* Make the changes visible */ stm32_ltdc_reload(reload); sem_post(priv->state.lock); return OK; } gdbg("Returning EINVAL\n"); return -EINVAL; } #ifdef CONFIG_STM32_DMA2D /**************************************************************************** * Name: blit * * Description: * Copy selected area from a background layer to selected position of the * foreground layer. Copies the result to the destination layer. * * Parameter: * dest - Reference to the destination layer * fore - Reference to the foreground layer * forexpos - Selected x target position of the destination layer * foreypos - Selected y target position of the destination 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) * ****************************************************************************/ static int stm32_blit(FAR struct ltdc_layer_s *dest, FAR struct ltdc_layer_s *fore, fb_coord_t forexpos, fb_coord_t foreypos, FAR struct ltdc_layer_s *back, FAR const struct ltdc_area_s *backarea) { FAR struct stm32_layer_s *destlayer = (FAR struct stm32_layer_s *)dest; FAR struct stm32_layer_s *forelayer = (FAR struct stm32_layer_s *)fore; FAR struct stm32_layer_s *backlayer = (FAR struct stm32_layer_s *)back; gvdbg("dest = %p, fore = %p, forexpos = %d, foreypos = %d, back = %p, \ backarea = %p\n", dest, fore, forexpos, foreypos, back, backarea); if (stm32_ltdc_lvalidate(destlayer) && stm32_ltdc_lvalidate(forelayer) && stm32_ltdc_lvalidate(backlayer)) { return stm32_dma2dblit(&destlayer->state, &forelayer->state, forexpos, foreypos, &backlayer->state, backarea); } gdbg("Returning EINVAL\n"); return -EINVAL; } /**************************************************************************** * * Name: blend * * 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. * * Parameter: * dest - Reference to the destination layer * fore - Reference to the foreground layer * forexpos - Selected x target position of the destination layer * foreypos - Selected y target position of the destination 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) * ****************************************************************************/ static int stm32_blend(FAR struct ltdc_layer_s *dest, FAR struct ltdc_layer_s *fore, fb_coord_t forexpos, fb_coord_t foreypos, FAR struct ltdc_layer_s *back, FAR const struct ltdc_area_s *backarea) { FAR struct stm32_layer_s *destlayer = (FAR struct stm32_layer_s *)dest; FAR struct stm32_layer_s *forelayer = (FAR struct stm32_layer_s *)fore; FAR struct stm32_layer_s *backlayer = (FAR struct stm32_layer_s *)back; gvdbg("dest = %p, fore = %p, forexpos = %d, foreypos = %d, back = %p, \ backarea = %p\n", dest, fore, forexpos, foreypos, back, backarea); if (stm32_ltdc_lvalidate(destlayer) && stm32_ltdc_lvalidate(forelayer) && stm32_ltdc_lvalidate(backlayer)) { return stm32_dma2dblend(&destlayer->state, &forelayer->state, forexpos, foreypos, &backlayer->state, backarea); } gdbg("Returning EINVAL\n"); return -EINVAL; } #endif /**************************************************************************** * Name: stm32_ltdcgetlayer * * Description: * This is a non-standard framebuffer interface. * Get the specific layer object by the layer id to enable layer hardware * support. * * Parameter: * lid - Layer identifier * * Return: * Reference to the layer control structure on success or Null if lid * is invalid. * ****************************************************************************/ FAR struct ltdc_layer_s *stm32_ltdcgetlayer(int lid) { gvdbg("lid: %d\n", lid); if (lid == LTDC_LAYER_L1 || lid == LTDC_LAYER_L2) { return (FAR struct ltdc_layer_s *) &LAYER(lid); } gdbg("EINVAL\n"); errno = EINVAL; return NULL; } #endif /* CONFIG_STM32_LTDC_INTERFACE */ /**************************************************************************** * Name: stm32_ltdcinitialize * * Description: * Initialize the ltdc controller * * Return: * OK * ****************************************************************************/ int stm32_ltdcinitialize(void) { gvdbg("Initialize LTDC driver\n"); /* Disable the LCD */ stm32_lcd_enable(false); gvdbg("Configuring the LCD controller\n"); /* Configure LCD periphery */ gvdbg("Configure lcd periphery\n"); stm32_ltdc_periphconfig(); /* Configure global ltdc register */ gvdbg("Configure global register\n"); stm32_global_configure(); /* Initialize ltdc layer */ gvdbg("Initialize ltdc layer\n"); stm32_ltdc_linit(LTDC_LAYER_L1); #ifdef CONFIG_STM32_LTDC_L2 stm32_ltdc_linit(LTDC_LAYER_L2); #endif /* Display layer 1 and 2 */ stm32_ltdc_lenable(&LAYER_L1); #ifdef CONFIG_STM32_LTDC_L2 stm32_ltdc_lenable(&LAYER_L2); #endif /* Enable the backlight */ #ifdef CONFIG_STM32_LCD_BACKLIGHT stm32_backlight(true); #endif /* Reload shadow register */ gvdbg("Reload shadow register\n"); stm32_ltdc_reload(LTDC_SRCR_IMR); /* Turn the LCD on */ gvdbg("Enabling the display\n"); stm32_lcd_enable(true); return OK; } /**************************************************************************** * Name: stm32_ltdcgetvplane * * Description: * Return a a reference to the framebuffer object for the specified video * plane. * * Input parameters: * None * * Returned value: * Reference to the framebuffer object (NULL on failure) * ****************************************************************************/ struct fb_vtable_s *stm32_ltdcgetvplane(int vplane) { gvdbg("vplane: %d\n", vplane); if (vplane == 0) { return (struct fb_vtable_s *)&g_vtable; } return NULL; } /**************************************************************************** * Name: fb_uninitialize * * Description: * Uninitialize the framebuffer driver. Bad things will happen if you * call this without first calling fb_initialize()! * ****************************************************************************/ void stm32_ltdcuninitialize(void) { /* Disable the LCD controller */ stm32_lcd_enable(false); } /**************************************************************************** * Name: stm32_lcdclear * * Description: * This is a non-standard LCD interface just for the STM32 LTDC. Clearing the * display in the normal way by writing a sequences of runs that covers the * entire display can be slow. Here the display is cleared by simply setting * all video memory to the specified color. * * Parameter: * color - The color the clear the whole framebuffer * ****************************************************************************/ void stm32_lcdclear(nxgl_mxpixel_t color) { #ifdef CONFIG_STM32_LTDC_L2 stm32_ltdc_lclear(&LAYER(LTDC_LAYER_L2), color); #endif stm32_ltdc_lclear(&LAYER(LTDC_LAYER_L1), color); } /**************************************************************************** * Name: stm32_lcd_backlight * * Description: * Provide this interface to turn the backlight on and off. * * Parameter: * blon - Enable or disable the lcd backlight * ****************************************************************************/ #ifdef CONFIG_STM32_LCD_BACKLIGHT void stm32_backlight(bool blon) { /* Set default backlight level CONFIG_STM32_LTDC_DEFBACKLIGHT */ gdbg("Not supported\n"); } #endif