3620 lines
101 KiB
C
3620 lines
101 KiB
C
/****************************************************************************
|
|
* arch/arm/src/stm32/stm32_ltdc.c
|
|
*
|
|
* Copyright (C) 2013-2015 Ken Pettit. All rights reserved.
|
|
* Authors: Ken Pettit <pettitd@gmail.com>
|
|
* Marco Krahl <ocram.lhark@gmail.com>
|
|
*
|
|
* 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 <nuttx/config.h>
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <debug.h>
|
|
|
|
#include <nuttx/irq.h>
|
|
#include <nuttx/video/fb.h>
|
|
#include <nuttx/kmalloc.h>
|
|
|
|
#include <arch/chip/ltdc.h>
|
|
#include <arch/chip/dma2d.h>
|
|
#include <arch/board/board.h>
|
|
|
|
#include "up_arch.h"
|
|
#include "up_internal.h"
|
|
#include "stm32.h"
|
|
#include "chip/stm32_ltdc.h"
|
|
#include "stm32_ltdc.h"
|
|
#include "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)
|
|
|
|
/* 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 regerr lcderr
|
|
# define reginfo lcdinfo
|
|
#else
|
|
# define regerr(x...)
|
|
# define reginfo(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
|
|
|
|
/* Check pixel format support by DMA2D driver */
|
|
|
|
#ifdef CONFIG_STM32_DMA2D
|
|
# if defined(CONFIG_STM32_LTDC_L1_L8) || \
|
|
defined(CONFIG_STM32_LTDC_L2_L8)
|
|
# if !defined(CONFIG_STM32_DMA2D_L8)
|
|
# error "DMA2D must support FB_FMT_RGB8 pixel format"
|
|
# endif
|
|
# endif
|
|
# if defined(CONFIG_STM32_LTDC_L1_RGB565) || \
|
|
defined(CONFIG_STM32_LTDC_L2_RGB565)
|
|
# if !defined(CONFIG_STM32_DMA2D_RGB565)
|
|
# error "DMA2D must support FB_FMT_RGB16_565 pixel format"
|
|
# endif
|
|
# endif
|
|
# if defined(CONFIG_STM32_LTDC_L1_RGB888) || \
|
|
defined(CONFIG_STM32_LTDC_L2_RGB888)
|
|
# if !defined(CONFIG_STM32_DMA2D_RGB888)
|
|
# error "DMA2D must support FB_FMT_RGB24 pixel format"
|
|
# endif
|
|
# endif
|
|
#endif
|
|
|
|
/* Calculate the size of the layers clut table */
|
|
|
|
#ifdef CONFIG_FB_CMAP
|
|
# if defined(CONFIG_STM32_DMA2D) && !defined(CONFIG_STM32_DMA2D_L8)
|
|
# error "DMA2D must also support L8 CLUT pixel format if supported by LTDC"
|
|
# endif
|
|
# ifdef STM32_LTDC_L1CMAP
|
|
# ifdef CONFIG_FB_TRANSPARENCY
|
|
# define STM32_LAYER_CLUT_SIZE STM32_LTDC_NCLUT * sizeof(uint32_t)
|
|
# else
|
|
# define STM32_LAYER_CLUT_SIZE STM32_LTDC_NCLUT * 3 * sizeof(uint8_t)
|
|
# endif
|
|
# endif
|
|
# ifdef STM32_LTDC_L2CMAP
|
|
# undef STM32_LAYER_CLUT_SIZE
|
|
# ifdef CONFIG_FB_TRANSPARENCY
|
|
# define STM32_LAYER_CLUT_SIZE STM32_LTDC_NCLUT * sizeof(uint32_t) * 2
|
|
# else
|
|
# define STM32_LAYER_CLUT_SIZE STM32_LTDC_NCLUT * 3 * sizeof(uint8_t) * 2
|
|
# endif
|
|
# 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 argb clut register position */
|
|
|
|
#define LTDC_CLUT_ADD(n) ((uint32_t)(n) << 24)
|
|
#define LTDC_CLUT_ALPHA(n) LTDC_CLUT_ADD(n)
|
|
#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)
|
|
#define LTDC_CLUT_RGB888_MASK 0xffffff
|
|
|
|
/* Layer argb cmap conversion */
|
|
|
|
#define LTDC_CMAP_ALPHA(n) ((uint32_t)(n) >> 24)
|
|
#define LTDC_CMAP_RED(n) ((uint32_t)(n) >> 16)
|
|
#define LTDC_CMAP_GREEN(n) ((uint32_t)(n) >> 8)
|
|
#define LTDC_CMAP_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 */
|
|
#ifdef CONFIG_STM32_DMA2D
|
|
FAR struct dma2d_layer_s *dma2d; /* dma2d interface */
|
|
#endif
|
|
};
|
|
|
|
/* 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_OFFSET = 0,
|
|
LTDC_L2CLUT_OFFSET = STM32_LTDC_NCLUT * sizeof(uint32_t)
|
|
};
|
|
#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 */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* 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 int stm32_ltdcirq(int irq, void *context);
|
|
static int stm32_ltdc_waitforirq(void);
|
|
static int stm32_ltdc_reload(uint8_t value, bool waitvblank);
|
|
|
|
/* 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 const 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_lclut(FAR struct stm32_layer_s *layer,
|
|
FAR const struct fb_cmap_s *cmap);
|
|
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,
|
|
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_blend(FAR struct ltdc_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_fillarea(FAR struct ltdc_layer_s *layer,
|
|
FAR const struct ltdc_area_s *area,
|
|
uint32_t color);
|
|
#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 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_LTDCINT,
|
|
.sem = &g_semirq
|
|
};
|
|
|
|
/* The layer active state */
|
|
|
|
static uint8_t g_lactive;
|
|
|
|
#ifdef STM32_LAYER_CLUT_SIZE
|
|
/* The layers clut table entries */
|
|
|
|
static uint32_t g_clut[STM32_LAYER_CLUT_SIZE];
|
|
#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,
|
|
.display = 0,
|
|
.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
|
|
, .clut = &g_clut[LTDC_L1CLUT_OFFSET]
|
|
#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,
|
|
.display = 1,
|
|
.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
|
|
, .clut = &g_clut[LTDC_L2CLUT_OFFSET]
|
|
#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
|
|
|
|
/* The initialized state of the driver */
|
|
|
|
static bool g_initialized;
|
|
|
|
/****************************************************************************
|
|
* 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;
|
|
|
|
lcdinfo("Configuring pins\n");
|
|
|
|
/* Configure each pin */
|
|
|
|
for (i = 0; i < STM32_LTDC_NPINCONFIGS; i++)
|
|
{
|
|
reginfo("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 */
|
|
|
|
reginfo("configured RCC_APB2ENR=%08x\n", getreg32(STM32_RCC_APB2ENR));
|
|
|
|
/* Configure the SAI PLL external to provide the LCD_CLK */
|
|
|
|
reginfo("configured RCC_PLLSAI=%08x\n", getreg32(STM32_RCC_PLLSAICFGR));
|
|
|
|
/* Configure dedicated clock external */
|
|
|
|
reginfo("configured RCC_DCKCFGR=%08x\n", getreg32(STM32_RCC_DCKCFGR));
|
|
|
|
/* Configure LTDC_SSCR */
|
|
|
|
regval = (STM32_LTDC_SSCR_VSH | STM32_LTDC_SSCR_HSW);
|
|
reginfo("set LTDC_SSCR=%08x\n", regval);
|
|
putreg32(regval, STM32_LTDC_SSCR);
|
|
reginfo("configured LTDC_SSCR=%08x\n", getreg32(STM32_LTDC_SSCR));
|
|
|
|
/* Configure LTDC_BPCR */
|
|
|
|
regval = (STM32_LTDC_BPCR_AVBP | STM32_LTDC_BPCR_AHBP);
|
|
reginfo("set LTDC_BPCR=%08x\n", regval);
|
|
putreg32(regval, STM32_LTDC_BPCR);
|
|
reginfo("configured LTDC_BPCR=%08x\n", getreg32(STM32_LTDC_BPCR));
|
|
|
|
/* Configure LTDC_AWCR */
|
|
|
|
regval = (STM32_LTDC_AWCR_AAH | STM32_LTDC_AWCR_AAW);
|
|
reginfo("set LTDC_AWCR=%08x\n", regval);
|
|
putreg32(regval, STM32_LTDC_AWCR);
|
|
reginfo("configured LTDC_AWCR=%08x\n", getreg32(STM32_LTDC_AWCR));
|
|
|
|
/* Configure LTDC_TWCR */
|
|
|
|
regval = (STM32_LTDC_TWCR_TOTALH | STM32_LTDC_TWCR_TOTALW);
|
|
reginfo("set LTDC_TWCR=%08x\n", regval);
|
|
putreg32(regval, STM32_LTDC_TWCR);
|
|
reginfo("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);
|
|
reginfo("set LTDC_GCR=%08x\n", regval);
|
|
putreg32(regval, STM32_LTDC_GCR);
|
|
reginfo("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)
|
|
{
|
|
reginfo("set LTDC_BCCR=%08x\n", rgb);
|
|
putreg32(rgb, STM32_LTDC_BCCR);
|
|
reginfo("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));
|
|
|
|
reginfo("set LTDC_GCR=%08x\n", regval);
|
|
putreg32(regval, STM32_LTDC_GCR);
|
|
reginfo("configured LTDC_GCR=%08x\n", getreg32(STM32_LTDC_GCR));
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32_ltdc_linepos
|
|
*
|
|
* Description:
|
|
* Configures line position register
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void stm32_ltdc_linepos(void)
|
|
{
|
|
/* Configure LTDC_LIPCR */
|
|
|
|
reginfo("set LTDC_LIPCR=%08x\n", STM32_LTDC_LIPCR_LIPOS);
|
|
putreg32(STM32_LTDC_LIPCR_LIPOS, STM32_LTDC_LIPCR);
|
|
reginfo("configured LTDC_LIPCR=%08x\n", getreg32(STM32_LTDC_LIPCR));
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32_ltdc_irqctrl
|
|
*
|
|
* Description:
|
|
* Control interrupts generated by the ltdc controller
|
|
*
|
|
* Parameter:
|
|
* setirqs - set interrupt mask
|
|
* clrirqs - clear interrupt mask
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void stm32_ltdc_irqctrl(uint32_t setirqs, uint32_t clrirqs)
|
|
{
|
|
uint32_t regval;
|
|
|
|
regval = getreg32(STM32_LTDC_IER);
|
|
regval &= ~clrirqs;
|
|
regval |= setirqs;
|
|
reginfo("set LTDC_IER=%08x\n", regval);
|
|
putreg32(regval, STM32_LTDC_IER);
|
|
reginfo("configured LTDC_IER=%08x\n", getreg32(STM32_LTDC_IER));
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32_ltdcirq
|
|
*
|
|
* Description:
|
|
* LTDC interrupt handler
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int stm32_ltdcirq(int irq, void *context)
|
|
{
|
|
FAR struct stm32_interrupt_s *priv = &g_interrupt;
|
|
|
|
uint32_t regval = getreg32(STM32_LTDC_ISR);
|
|
|
|
reginfo("irq = %d, regval = %08x\n", irq, regval);
|
|
|
|
if (regval & LTDC_ISR_RRIF)
|
|
{
|
|
/* Register reload interrupt */
|
|
|
|
/* Clear the interrupt status register */
|
|
|
|
putreg32(LTDC_ICR_CRRIF, STM32_LTDC_ICR);
|
|
|
|
/* Update the handled flag */
|
|
|
|
priv->handled = true;
|
|
|
|
/* Unlock the semaphore if locked */
|
|
|
|
if (priv->wait)
|
|
{
|
|
int ret = sem_post(priv->sem);
|
|
|
|
if (ret != OK)
|
|
{
|
|
lcderr("ERROR: sem_post() failed\n");
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32_ltdc_waitforirq
|
|
*
|
|
* Description:
|
|
* Helper waits until the ltdc irq occurs. In the current design That means
|
|
* that a register reload was been completed.
|
|
* Note! The caller must use this function within a critical section.
|
|
*
|
|
* Return:
|
|
* OK - On success otherwise ERROR
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int stm32_ltdc_waitforirq(void)
|
|
{
|
|
int ret = OK;
|
|
FAR struct stm32_interrupt_s *priv = &g_interrupt;
|
|
|
|
irqstate_t flags;
|
|
|
|
flags = enter_critical_section();
|
|
|
|
/* Only waits if last enabled interrupt is currently not handled */
|
|
|
|
if (!priv->handled)
|
|
{
|
|
/* 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)
|
|
{
|
|
lcderr("ERROR: sem_wait() failed\n");
|
|
}
|
|
}
|
|
|
|
leave_critical_section(flags);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32_ltdc_reload
|
|
*
|
|
* Description:
|
|
* Reload the layer shadow register and make layer changes visible.
|
|
* Note! The caller must ensure that a previous register reloading has been
|
|
* completed.
|
|
*
|
|
* Parameter:
|
|
* value - Reload flag (e.g. upon vertical blank or immediately)
|
|
* waitvblank - Wait until register reload is finished
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int stm32_ltdc_reload(uint8_t value, bool waitvblank)
|
|
{
|
|
int ret = OK;
|
|
FAR struct stm32_interrupt_s *priv = &g_interrupt;
|
|
|
|
if (value == LTDC_SRCR_VBR)
|
|
{
|
|
irqstate_t flags;
|
|
|
|
/* Prepare shadow register reload for later detection by the task.
|
|
* At this point the last register reload must be completed. This is done
|
|
* in stm32_update before the next operation is triggered and manipulates
|
|
* the shadow register. This handling is only neccessary in the case of
|
|
* the application causes shadow register reload.
|
|
*/
|
|
|
|
flags = enter_critical_section();
|
|
|
|
ASSERT(priv->handled == true);
|
|
|
|
/* Reset the handled flag */
|
|
|
|
priv->handled = false;
|
|
leave_critical_section(flags);
|
|
}
|
|
|
|
/* Reloads the shadow register.
|
|
* Note! This will not trigger an register reload interrupt if
|
|
* immediately reload is set.
|
|
*/
|
|
|
|
reginfo("set LTDC_SRCR=%08x\n", value);
|
|
putreg32(value, STM32_LTDC_SRCR);
|
|
reginfo("configured LTDC_SRCR=%08x\n", getreg32(STM32_LTDC_SRCR));
|
|
|
|
if (waitvblank & (value == LTDC_SRCR_VBR))
|
|
{
|
|
/* Wait upon vertical blanking period */
|
|
|
|
ret = stm32_ltdc_waitforirq();
|
|
}
|
|
|
|
/* Otherwise check if reload is completed before the next operation */
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* 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);
|
|
|
|
/* Initialize the semaphore for interrupt handling */
|
|
|
|
sem_init(g_interrupt.sem, 0, 0);
|
|
|
|
/* Attach LTDC interrupt vector */
|
|
|
|
(void)irq_attach(g_interrupt.irq, stm32_ltdcirq);
|
|
|
|
/* Enable the IRQ at the NVIC */
|
|
|
|
up_enable_irq(g_interrupt.irq);
|
|
|
|
/* Enable register reload interrupt only */
|
|
|
|
stm32_ltdc_irqctrl(LTDC_IER_RRIE, LTDC_IER_TERRIE | LTDC_IER_FUIE | LTDC_IER_LIE);
|
|
|
|
/* Configure line interrupt */
|
|
|
|
stm32_ltdc_linepos();
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* 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);
|
|
reginfo("get LTDC_GCR=%08x\n", regval);
|
|
|
|
if (enable == true)
|
|
{
|
|
regval |= LTDC_GCR_LTDCEN;
|
|
}
|
|
else
|
|
{
|
|
regval &= ~LTDC_GCR_LTDCEN;
|
|
}
|
|
|
|
reginfo("set LTDC_GCR=%08x\n", regval);
|
|
putreg32(regval, STM32_LTDC_GCR);
|
|
reginfo("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]);
|
|
reginfo("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;
|
|
}
|
|
|
|
reginfo("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 const 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))
|
|
|
|
{
|
|
lcderr("ERROR: 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);
|
|
|
|
lcderr(" 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;
|
|
|
|
reginfo("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 */
|
|
|
|
reginfo("set LTDC_L%dWHPCR=%08x\n", priv->lid + 1, whpcr);
|
|
putreg32(whpcr, stm32_whpcr_layer_t[priv->lid]);
|
|
reginfo("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 */
|
|
|
|
reginfo("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;
|
|
|
|
reginfo("set LTDC_L%dCFBAR=%08x\n", priv->lid + 1, pinfo->fbmem + offset);
|
|
putreg32((uint32_t)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);
|
|
|
|
reginfo("set LTDC_L%dCFBLR=%08x\n", priv->lid + 1, cfblr);
|
|
putreg32(cfblr, stm32_cfblr_layer_t[priv->lid]);
|
|
|
|
/* Configure LxCFBLNR register */
|
|
|
|
reginfo("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);
|
|
reginfo("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)
|
|
{
|
|
reginfo("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)
|
|
{
|
|
reginfo("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 */
|
|
|
|
reginfo("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;
|
|
}
|
|
|
|
reginfo("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,
|
|
FAR const struct fb_cmap_s *cmap)
|
|
{
|
|
int n;
|
|
uint32_t regval;
|
|
uint32_t *clut;
|
|
irqstate_t flags;
|
|
|
|
/* Disable clut during register update */
|
|
|
|
stm32_ltdc_lclutenable(layer, false);
|
|
|
|
/* Set the clut memory address */
|
|
|
|
clut = layer->state.clut;
|
|
|
|
/* 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, false);
|
|
|
|
flags = enter_critical_section();
|
|
|
|
/* Update the clut registers */
|
|
|
|
for (n = cmap->first; n < cmap->len && n < STM32_LTDC_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];
|
|
|
|
regval = (uint32_t)LTDC_CLUT_BLUE(clut888[offset]) |
|
|
(uint32_t)LTDC_CLUT_GREEN(clut888[offset + 1]) |
|
|
(uint32_t)LTDC_CLUT_RED(clut888[offset + 2]) |
|
|
(uint32_t)LTDC_CLUT_ADD(n);
|
|
#else
|
|
clut[n] = (uint32_t)LTDC_CLUT_ALPHA(cmap->transp[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]);
|
|
regval = (uint32_t)LTDC_CLUT_ADD(n) | (clut[n] & LTDC_CLUT_RGB888_MASK);
|
|
#endif
|
|
|
|
|
|
reginfo("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]);
|
|
}
|
|
|
|
leave_critical_section(flags);
|
|
|
|
/* Enable clut */
|
|
|
|
stm32_ltdc_lclutenable(layer, true);
|
|
|
|
/* Reload shadow control register */
|
|
|
|
stm32_ltdc_reload(LTDC_SRCR_IMR, false);
|
|
}
|
|
#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;
|
|
}
|
|
|
|
reginfo("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;
|
|
|
|
lcdinfo("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;
|
|
|
|
lcdinfo("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;
|
|
|
|
lcdinfo("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;
|
|
|
|
lcdinfo("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
|
|
* - layer dma2d interface binding
|
|
*
|
|
* 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;
|
|
ltdc->fillarea = stm32_fillarea;
|
|
#endif
|
|
#endif
|
|
|
|
/* Initialize the layer state */
|
|
|
|
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
|
|
|
|
#ifdef CONFIG_STM32_DMA2D
|
|
/* Bind the dma2d interface */
|
|
|
|
layer->dma2d = stm32_dma2dinitltdc(state);
|
|
DEBUGASSERT(layer->dma2d);
|
|
#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)
|
|
{
|
|
lcdinfo("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);
|
|
}
|
|
|
|
lcderr("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)
|
|
{
|
|
lcdinfo("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);
|
|
}
|
|
|
|
lcderr("ERROR: 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)
|
|
{
|
|
lcdinfo("layer=%p vinfo=%p\n", layer, vinfo);
|
|
FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer;
|
|
|
|
if (stm32_ltdc_lvalidate(priv))
|
|
{
|
|
memcpy(vinfo, &priv->state.vinfo, sizeof(struct fb_videoinfo_s));
|
|
|
|
return OK;
|
|
}
|
|
|
|
lcderr("ERROR: 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)
|
|
{
|
|
FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer;
|
|
|
|
lcdinfo("layer=%p planeno=%d pinfo=%p\n", layer, planeno, pinfo);
|
|
if (stm32_ltdc_lvalidate(priv) && planeno == 0)
|
|
{
|
|
memcpy(pinfo, &priv->state.pinfo, sizeof(struct fb_planeinfo_s));
|
|
return OK;
|
|
}
|
|
|
|
lcderr("ERROR: 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)
|
|
{
|
|
FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer;
|
|
int ret;
|
|
|
|
lcdinfo("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)
|
|
{
|
|
lcderr("ERROR: CLUT is not supported for the pixel format: %d\n",
|
|
priv->state.vinfo.fmt);
|
|
ret = -EINVAL;
|
|
}
|
|
else if (cmap->first >= STM32_LTDC_NCLUT)
|
|
{
|
|
lcderr("ERROR: only %d color table entries supported\n",
|
|
STM32_LTDC_NCLUT);
|
|
ret = -EINVAL;
|
|
}
|
|
else
|
|
{
|
|
/* Update layer clut and clut register */
|
|
|
|
stm32_ltdc_lclut(priv, cmap);
|
|
|
|
ret = OK;
|
|
}
|
|
|
|
sem_post(priv->state.lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
lcderr("ERROR: 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)
|
|
{
|
|
FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer;
|
|
int ret;
|
|
|
|
lcdinfo("layer=%p cmap=%p\n", layer, cmap);
|
|
|
|
if (priv == &LAYER_L1 || priv == &LAYER_L2)
|
|
{
|
|
sem_wait(priv->state.lock);
|
|
#ifdef CONFIG_STM32_DMA2D
|
|
/* Note! We share the same color lookup table with the dma2d driver and
|
|
* the getclut implementation works in the same way.
|
|
* To prevent redundant code we simply call the getclut function of the
|
|
* dma2d interface.
|
|
*/
|
|
|
|
ret = priv->dma2d->getclut(priv->dma2d, cmap);
|
|
#else
|
|
if (priv->state.vinfo.fmt != FB_FMT_RGB8)
|
|
{
|
|
lcderr("ERROR: CLUT is not supported for the pixel format: %d\n",
|
|
priv->state.vinfo.fmt);
|
|
ret = -EINVAL;
|
|
}
|
|
else if (cmap->first >= STM32_LTDC_NCLUT)
|
|
{
|
|
lcderr("ERROR: only %d color table entries supported\n",
|
|
STM32_LTDC_NCLUT);
|
|
ret = -EINVAL;
|
|
}
|
|
else
|
|
{
|
|
/* Copy from the layer clut */
|
|
|
|
uint32_t *clut;
|
|
int n;
|
|
|
|
clut = priv->state.clut;
|
|
|
|
for (n = cmap->first; n < cmap->len && n < STM32_LTDC_NCLUT; n++)
|
|
{
|
|
# ifndef CONFIG_FB_TRANSPARENCY
|
|
uint8_t *clut888 = (uint8_t *)clut;
|
|
uint16_t offset = 3 * n;
|
|
|
|
cmap->blue[n] = clut888[offset];
|
|
cmap->green[n] = clut888[offset + 1];
|
|
cmap->red[n] = clut888[offset + 2];
|
|
|
|
reginfo("n=%d, red=%02x, green=%02x, blue=%02x\n", n,
|
|
clut888[offset], clut888[offset + 1],
|
|
clut888[offset + 2]);
|
|
# else
|
|
cmap->transp[n] = (uint8_t)LTDC_CMAP_ALPHA(clut[n]);
|
|
cmap->red[n] = (uint8_t)LTDC_CMAP_RED(clut[n]);
|
|
cmap->green[n] = (uint8_t)LTDC_CMAP_GREEN(clut[n]);
|
|
cmap->blue[n] = (uint8_t)LTDC_CMAP_BLUE(clut[n]);
|
|
|
|
reginfo("n=%d, alpha=%02x, red=%02x, green=%02x, blue=%02x\n", n,
|
|
DMA2D_CMAP_ALPHA(clut[n]), DMA2D_CMAP_RED(clut[n]),
|
|
DMA2D_CMAP_GREEN(clut[n]), DMA2D_CMAP_BLUE(clut[n]));
|
|
# endif
|
|
}
|
|
|
|
ret = OK;
|
|
}
|
|
#endif
|
|
sem_post(priv->state.lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
lcderr("ERROR: 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;
|
|
|
|
lcdinfo("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
|
|
#ifdef CONFIG_STM32_DMA2D
|
|
case LTDC_LAYER_DMA2D:
|
|
ret = priv->dma2d->getlid(priv->dma2d, lid);
|
|
break;
|
|
#endif
|
|
default:
|
|
ret = EINVAL;
|
|
lcderr("ERROR: Returning EINVAL\n");
|
|
break;
|
|
}
|
|
|
|
sem_post(priv->state.lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
lcderr("ERROR: 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;
|
|
|
|
lcdinfo("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;
|
|
}
|
|
|
|
lcderr("ERROR: 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;
|
|
|
|
lcdinfo("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;
|
|
}
|
|
|
|
lcderr("ERROR: 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;
|
|
|
|
lcdinfo("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;
|
|
}
|
|
|
|
lcderr("ERROR: 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;
|
|
|
|
lcdinfo("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;
|
|
}
|
|
|
|
lcderr("ERROR: 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;
|
|
|
|
lcdinfo("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;
|
|
}
|
|
|
|
lcderr("ERROR: 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;
|
|
|
|
lcdinfo("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;
|
|
}
|
|
|
|
lcderr("ERROR: Returning EINVAL\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: setblendmode
|
|
*
|
|
* Description:
|
|
* Configure blend mode of the layer.
|
|
* Default mode during initializing: 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;
|
|
uint32_t blendmode = mode;
|
|
|
|
lcdinfo("layer = %p, mode = %08x\n", layer, mode);
|
|
|
|
if (stm32_ltdc_lvalidate(priv))
|
|
{
|
|
int ret = OK;
|
|
|
|
sem_wait(priv->state.lock);
|
|
|
|
/* Disable colorkeying by default */
|
|
|
|
priv->operation &= ~LTDC_LAYER_ENABLECOLORKEY;
|
|
|
|
if (blendmode & (LTDC_BLEND_ALPHA | LTDC_BLEND_PIXELALPHA |
|
|
LTDC_BLEND_ALPHAINV | LTDC_BLEND_PIXELALPHAINV))
|
|
{
|
|
/* Enable any alpha blending */
|
|
|
|
stm32_ltdc_lunsetopac(priv);
|
|
}
|
|
else
|
|
{
|
|
/* Disable any alpha blending */
|
|
|
|
stm32_ltdc_lsetopac(priv);
|
|
}
|
|
|
|
if (blendmode & LTDC_BLEND_ALPHA || blendmode == LTDC_BLEND_NONE)
|
|
{
|
|
/* alpha blending introduce LTDC_BLEND_ALPHAINV */
|
|
|
|
priv->bf1 = LTDC_BF1_CONST_ALPHA;
|
|
priv->bf2 = LTDC_BF2_CONST_ALPHA;
|
|
blendmode &= ~LTDC_BLEND_ALPHA;
|
|
}
|
|
|
|
if (blendmode & LTDC_BLEND_PIXELALPHA)
|
|
{
|
|
/* pixel alpha blending introduce LTDC_BLEND_PIXELALPHAINV */
|
|
|
|
priv->bf1 = LTDC_BF1_PIXEL_ALPHA;
|
|
priv->bf2 = LTDC_BF2_PIXEL_ALPHA;
|
|
blendmode &= ~LTDC_BLEND_PIXELALPHA;
|
|
}
|
|
|
|
if (blendmode & LTDC_BLEND_ALPHAINV)
|
|
{
|
|
/* alpha blending of source input */
|
|
|
|
priv->bf2 = LTDC_BF2_CONST_ALPHA;
|
|
blendmode &= ~LTDC_BLEND_ALPHAINV;
|
|
}
|
|
|
|
if (blendmode & LTDC_BLEND_PIXELALPHAINV)
|
|
{
|
|
/* pixel alpha blending of source input */
|
|
|
|
priv->bf2 = LTDC_BF2_PIXEL_ALPHA;
|
|
blendmode &= ~LTDC_BLEND_PIXELALPHAINV;
|
|
}
|
|
|
|
if (mode & LTDC_BLEND_COLORKEY)
|
|
{
|
|
/* Enable colorkeying */
|
|
|
|
priv->operation |= LTDC_LAYER_ENABLECOLORKEY;
|
|
blendmode &= ~LTDC_BLEND_COLORKEY;
|
|
}
|
|
if (blendmode)
|
|
{
|
|
lcderr("ERROR: Unknown blendmode %02x\n", blendmode);
|
|
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;
|
|
}
|
|
|
|
lcderr("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_getblendmode(FAR struct ltdc_layer_s *layer, uint32_t *mode)
|
|
{
|
|
FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer;
|
|
|
|
lcdinfo("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;
|
|
}
|
|
|
|
lcderr("ERROR: 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;
|
|
|
|
lcdinfo("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;
|
|
}
|
|
|
|
lcderr("ERROR: 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;
|
|
|
|
lcdinfo("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;
|
|
}
|
|
|
|
lcderr("ERROR: 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
|
|
* -ECANCELED - Operation cancelled, something goes wrong
|
|
*
|
|
* 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
|
|
|
|
lcdinfo("layer = %p, mode = %08x\n", layer, mode);
|
|
|
|
if (stm32_ltdc_lvalidate(priv))
|
|
{
|
|
/* Reload immediately by default */
|
|
|
|
bool waitvblank = false;
|
|
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)
|
|
{
|
|
waitvblank = true;
|
|
}
|
|
|
|
/* Ensures that last register reload operation has been completed */
|
|
|
|
if (stm32_ltdc_waitforirq() != OK)
|
|
{
|
|
lcderr("ERROR: Returning ECANCELED\n");
|
|
return -ECANCELED;
|
|
}
|
|
|
|
/* 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, waitvblank);
|
|
|
|
sem_post(priv->state.lock);
|
|
|
|
return OK;
|
|
}
|
|
|
|
lcderr("ERROR: Returning EINVAL\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
#ifdef CONFIG_STM32_DMA2D
|
|
/****************************************************************************
|
|
* Name: stm32_blit
|
|
*
|
|
* Description:
|
|
* Copy selected area from a source layer to selected position of the
|
|
* destination layer.
|
|
*
|
|
* Parameter:
|
|
* dest - Reference to the destination layer
|
|
* destxpos - Selected x position of the destination layer
|
|
* destypos - Selected y position of the destination layer
|
|
* src - Reference to the source layer
|
|
* srcarea - 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)
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int stm32_blit(FAR struct ltdc_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)
|
|
{
|
|
FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)dest;
|
|
|
|
lcdinfo("dest = %p, destxpos = %d, destypos = %d, src = %p, srcarea = %p\n",
|
|
dest, destxpos, destypos, src, srcarea);
|
|
|
|
if (stm32_ltdc_lvalidate(priv))
|
|
{
|
|
int ret;
|
|
|
|
sem_wait(priv->state.lock);
|
|
priv->dma2d->blit(priv->dma2d, destxpos, destypos, src, srcarea);
|
|
sem_post(priv->state.lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
lcderr("ERROR: Returning EINVAL\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32_blend
|
|
*
|
|
* Description:
|
|
* Blends the selected area from a foreground layer with selected position
|
|
* of the background layer. Copy the result to the destination layer. Note!
|
|
* The content of the foreground and background layer is not changed.
|
|
*
|
|
* Parameter:
|
|
* dest - Reference to the destination layer
|
|
* destxpos - Selected x position of the destination layer
|
|
* destypos - Selected y position of the destination layer
|
|
* fore - Reference to the foreground layer
|
|
* forexpos - Selected x position of the foreground layer
|
|
* foreypos - Selected y 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)
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int stm32_blend(FAR struct ltdc_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)
|
|
{
|
|
FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)dest;
|
|
|
|
lcdinfo("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_ltdc_lvalidate(priv))
|
|
{
|
|
int ret;
|
|
|
|
sem_wait(priv->state.lock);
|
|
priv->dma2d->blend(priv->dma2d, destxpos, destypos,
|
|
fore, forexpos, foreypos, back, backarea);
|
|
sem_post(priv->state.lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
lcderr("ERROR: Returning EINVAL\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: fillarea
|
|
*
|
|
* 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.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int stm32_fillarea(FAR struct ltdc_layer_s *layer,
|
|
FAR const struct ltdc_area_s *area,
|
|
uint32_t color)
|
|
{
|
|
FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer;
|
|
|
|
lcdinfo("layer = %p, area = %p, color = %08x\n", layer, area, color);
|
|
|
|
if (stm32_ltdc_lvalidate(priv))
|
|
{
|
|
int ret;
|
|
|
|
sem_wait(priv->state.lock);
|
|
priv->dma2d->fillarea(priv->dma2d, area, color);
|
|
sem_post(priv->state.lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
lcderr("ERROR: 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)
|
|
{
|
|
lcdinfo("lid: %d\n", lid);
|
|
|
|
if (lid == LTDC_LAYER_L1 || lid == LTDC_LAYER_L2)
|
|
{
|
|
return (FAR struct ltdc_layer_s *) &LAYER(lid);
|
|
}
|
|
|
|
lcderr("ERROR: Returning 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)
|
|
{
|
|
#ifdef CONFIG_STM32_DMA2D
|
|
int ret;
|
|
#endif
|
|
|
|
lcdinfo("Initialize LTDC driver\n");
|
|
|
|
if (g_initialized == true)
|
|
{
|
|
return OK;
|
|
}
|
|
|
|
/* Disable the LCD */
|
|
|
|
stm32_lcd_enable(false);
|
|
|
|
lcdinfo("Configuring the LCD controller\n");
|
|
|
|
/* Configure LCD periphery */
|
|
|
|
lcdinfo("Configure lcd periphery\n");
|
|
stm32_ltdc_periphconfig();
|
|
|
|
/* Configure global ltdc register */
|
|
|
|
lcdinfo("Configure global register\n");
|
|
stm32_global_configure();
|
|
|
|
#ifdef CONFIG_STM32_DMA2D
|
|
/* Initialize the dma2d controller */
|
|
|
|
ret = up_dma2dinitialize();
|
|
|
|
if (ret != OK)
|
|
{
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/* Initialize ltdc layer */
|
|
|
|
lcdinfo("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 */
|
|
|
|
lcdinfo("Reload shadow register\n");
|
|
stm32_ltdc_reload(LTDC_SRCR_IMR, false);
|
|
|
|
/* Turn the LCD on */
|
|
|
|
lcdinfo("Enabling the display\n");
|
|
stm32_lcd_enable(true);
|
|
|
|
/* Set initialized state */
|
|
|
|
g_initialized = 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)
|
|
{
|
|
lcdinfo("vplane: %d\n", vplane);
|
|
|
|
if (vplane == 0)
|
|
{
|
|
return (struct fb_vtable_s *)&g_vtable;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32_ltdcuninitialize
|
|
*
|
|
* Description:
|
|
* Uninitialize the framebuffer driver. Bad things will happen if you
|
|
* call this without first calling fb_initialize()!
|
|
*
|
|
****************************************************************************/
|
|
|
|
void stm32_ltdcuninitialize(void)
|
|
{
|
|
/* Disable all ltdc interrupts */
|
|
|
|
stm32_ltdc_irqctrl(0, LTDC_IER_RRIE | LTDC_IER_TERRIE |
|
|
LTDC_IER_FUIE | LTDC_IER_LIE);
|
|
|
|
up_disable_irq(g_interrupt.irq);
|
|
irq_detach(g_interrupt.irq);
|
|
|
|
/* Disable the LCD controller */
|
|
|
|
stm32_lcd_enable(false);
|
|
|
|
/* Set initialized state */
|
|
|
|
g_initialized = 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 */
|
|
|
|
lcderr("ERROR: Not supported\n");
|
|
}
|
|
#endif
|