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

3144 lines
88 KiB
C

/****************************************************************************
* arch/arm/src/stm32/stm32_ltdc.c
*
* Copyright (C) 2013-2015, 2018 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/kmalloc.h>
#include <nuttx/semaphore.h>
#include <nuttx/video/fb.h>
#include <arch/board/board.h>
#include "up_arch.h"
#include "up_internal.h"
#include "stm32.h"
#include "chip/stm32_ltdc.h"
#include "chip/stm32_dma2d.h"
#include "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_L1_DMA2D_PF DMA2D_PF_L8
# define STM32_LTDC_L1CMAP
#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)
# define STM32_LTDC_L1_DMA2D_PF DMA2D_PF_RGB565
#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)
# define STM32_LTDC_L1_DMA2D_PF DMA2D_PF_RGB888
#elif defined(CONFIG_STM32_LTDC_L1_ARGB8888)
# define STM32_LTDC_L1_BPP 32
# define STM32_LTDC_L1_COLOR_FMT FB_FMT_RGB32
# define STM32_LTDC_L1PFCR_PF LTDC_LxPFCR_PF(LTDC_PF_ARGB8888)
# define STM32_LTDC_L1_DMA2D_PF DMA2D_PF_ARGB8888
#else
# error "LTDC pixel format not supported"
#endif
/* Layer 2 format */
#ifdef CONFIG_STM32_LTDC_L2
# 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_L2_DMA2D_PF DMA2D_PF_L8
# define STM32_LTDC_L2CMAP
# 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)
# define STM32_LTDC_L2_DMA2D_PF DMA2D_PF_RGB565
# 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)
# define STM32_LTDC_L2_DMA2D_PF DMA2D_PF_RGB888
# elif defined(CONFIG_STM32_LTDC_L2_ARGB8888)
# define STM32_LTDC_L2_BPP 32
# define STM32_LTDC_L2_COLOR_FMT FB_FMT_RGB32
# define STM32_LTDC_L2PFCR_PF LTDC_LxPFCR_PF(LTDC_PF_ARGB8888)
# define STM32_LTDC_L2_DMA2D_PF DMA2D_PF_ARGB8888
# else
# error "LTDC pixel format not supported"
# endif
#endif /* CONFIG_STM32_LTDC_L2 */
/* Framebuffer sizes in bytes */
#if STM32_LTDC_L1_BPP == 8
# define STM32_LTDC_L1_STRIDE (STM32_LTDC_WIDTH)
#elif STM32_LTDC_L1_BPP == 16
# define STM32_LTDC_L1_STRIDE ((STM32_LTDC_WIDTH * 16 + 7) / 8)
#elif STM32_LTDC_L1_BPP == 24
# define STM32_LTDC_L1_STRIDE ((STM32_LTDC_WIDTH * 24 + 7) / 8)
#elif STM32_LTDC_L1_BPP == 32
# define STM32_LTDC_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_LTDC_L1_FBSIZE (STM32_LTDC_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_LTDC_L2_STRIDE (CONFIG_STM32_LTDC_L2_WIDTH)
# elif STM32_LTDC_L2_BPP == 16
# define STM32_LTDC_L2_STRIDE ((CONFIG_STM32_LTDC_L2_WIDTH * 16 + 7) / 8)
# elif STM32_LTDC_L2_BPP == 24
# define STM32_LTDC_L2_STRIDE ((CONFIG_STM32_LTDC_L2_WIDTH * 24 + 7) / 8)
# elif STM32_LTDC_L2_BPP == 32
# define STM32_LTDC_L2_STRIDE ((CONFIG_STM32_LTDC_L2_WIDTH * 32 + 7) / 8)
# else
# error Undefined or unrecognized base resolution
# endif
# define STM32_LTDC_L2_FBSIZE (STM32_LTDC_L2_STRIDE * \
CONFIG_STM32_LTDC_L2_HEIGHT)
#else
# define STM32_LTDC_L2_FBSIZE (0)
#endif
/* Total memory used for framebuffers */
#define STM32_LTDC_TOTAL_FBSIZE (STM32_LTDC_L1_FBSIZE + \
STM32_LTDC_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_LTDC_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_LTDC_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_LTDC_L2_FBSIZE)
#else
# define STM32_LTDC_ENDBUF_L2 STM32_LTDC_ENDBUF_L1
#endif
/* LTDC layer */
#ifdef CONFIG_STM32_LTDC_L2
# define LTDC_NLAYERS 2
#else
# define LTDC_NLAYERS 1
#endif
/* DMA2D layer */
#ifdef CONFIG_STM32_DMA2D
# define DMA2D_NLAYERS CONFIG_STM32_DMA2D_NLAYERS
# if DMA2D_NLAYERS < 1
# error "DMA2D must at least support 1 overlay"
# endif
#define STM32_DMA2D_WIDTH CONFIG_STM32_DMA2D_LAYER_PPLINE
# if defined(CONFIG_STM32_DMA2D_L8)
# define STM32_DMA2D_STRIDE (STM32_DMA2D_WIDTH)
# define STM32_DMA2D_BPP 8
# define STM32_DMA2D_COLOR_FMT DMA2D_PF_L8
# elif defined(CONFIG_STM32_DMA2D_RGB565)
# define STM32_DMA2D_STRIDE ((STM32_DMA2D_WIDTH * 16 + 7) / 8)
# define STM32_DMA2D_BPP 16
# define STM32_DMA2D_COLOR_FMT DMA2D_PF_RGB565
# elif defined(CONFIG_STM32_DMA2D_RGB888)
# define STM32_DMA2D_STRIDE ((STM32_DMA2D_WIDTH * 24 + 7) / 8)
# define STM32_DMA2D_BPP 24
# define STM32_DMA2D_COLOR_FMT DMA2D_PF_RGB888
# elif defined(CONFIG_STM32_DMA2D_ARGB8888)
# define STM32_DMA2D_STRIDE ((STM32_DMA2D_WIDTH * 32 + 7) / 8)
# define STM32_DMA2D_BPP 32
# define STM32_DMA2D_COLOR_FMT DMA2D_PF_ARGB8888
# else
# error "DMA2D pixel format not supported"
# endif
# ifdef CONFIG_STM32_DMA2D_LAYER_SHARED
# define STM32_DMA2D_FBSIZE CONFIG_STM32_DMA2D_FB_SIZE
# define STM32_DMA2D_LAYER_SIZE 0
# else
# define STM32_DMA2D_FBSIZE CONFIG_STM32_DMA2D_FB_SIZE / DMA2D_NLAYERS
# define STM32_DMA2D_LAYER_SIZE STM32_DMA2D_FBSIZE
# if STM32_DMA2D_FBSIZE * DMA2D_NLAYERS > CONFIG_STM32_DMA2D_FB_SIZE
# error "DMA2D framebuffer size to small for configured number of overlays"
# endif
# endif /* CONFIG_STM32_DMA2D_LAYER_SHARED */
# define STM32_DMA2D_HEIGHT STM32_DMA2D_FBSIZE / STM32_DMA2D_STRIDE
# define STM32_DMA2D_BUFFER_START CONFIG_STM32_DMA2D_FB_BASE
#else
# define DMA2D_NLAYERS 0
#endif /* CONFIG_STM32_DMA2D */
#define LTDC_NOVERLAYS LTDC_NLAYERS + DMA2D_NLAYERS
/* 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
/* Layer default color */
#ifdef CONFIG_STM32_LTDC_L1_COLOR
# define STM32_LTDC_L1_COLOR CONFIG_STM32_LTDC_L1_COLOR
#else
# define STM32_LTDC_L1_COLOR 0x000000
#endif
#ifdef CONFIG_STM32_LTDC_L2
# ifdef CONFIG_STM32_LTDC_L2_COLOR
# define STM32_LTDC_L2_COLOR CONFIG_STM32_LTDC_L2_COLOR
# else
# define STM32_LTDC_L2_COLOR 0x000000
# endif
#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
# if defined(CONFIG_STM32_LTDC_L1_ARGB8888) || \
defined(CONFIG_STM32_LTDC_L2_ARGB8888)
# if !defined(CONFIG_STM32_DMA2D_ARGB8888)
# error "DMA2D must support FB_FMT_RGB32 pixel format"
# endif
# endif
#endif
/* Calculate the size of the layers clut table */
#ifdef CONFIG_STM32_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_STM32_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_STM32_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_STM32_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)
/* Hardware acceleration support */
/* Acceleration support for LTDC overlays */
#ifdef CONFIG_STM32_LTDC_L1_CHROMAKEYEN
# define STM32_LTDC_L1_CHROMAEN true
# define STM32_LTDC_L1_CHROMAKEY CONFIG_STM32_LTDC_L1_CHROMAKEY
# define LTDC_LTDC_ACCL_L1 FB_ACCL_TRANSP | FB_ACCL_CHROMA
#else
# define STM32_LTDC_L1_CHROMAEN false
# define STM32_LTDC_L1_CHROMAKEY 0
# define LTDC_LTDC_ACCL_L1 FB_ACCL_TRANSP
#endif
#ifdef CONFIG_STM32_LTDC_L2_CHROMAKEYEN
# define STM32_LTDC_L2_CHROMAEN true
# define STM32_LTDC_L2_CHROMAKEY CONFIG_STM32_LTDC_L2_CHROMAKEY
# define LTDC_LTDC_ACCL_L2 FB_ACCL_TRANSP | FB_ACCL_CHROMA
#else
# define STM32_LTDC_L2_CHROMAEN false
# define STM32_LTDC_L2_CHROMAKEY 0
# define LTDC_LTDC_ACCL_L2 FB_ACCL_TRANSP
#endif
#ifdef CONFIG_STM32_DMA2D
# ifdef CONFIG_FB_OVERLAY_BLIT
# ifdef CONFIG_STM32_FB_CMAP
# define LTDC_BLIT_ACCL FB_ACCL_BLIT
# else
# define LTDC_BLIT_ACCL FB_ACCL_BLIT | FB_ACCL_BLEND
# endif /* CONFIG_STM32_FB_CMAP */
# else
# define LTDC_BLIT_ACCL 0
# endif /* CONFIG_FB_OVERLAY_BLIT */
# ifdef CONFIG_STM32_FB_CMAP
# define LTDC_DMA2D_ACCL LTDC_BLIT_ACCL
# else
# define LTDC_DMA2D_ACCL FB_ACCL_COLOR | LTDC_BLIT_ACCL
# endif /* CONFIG_STM32_FB_CMAP */
#else
# define LTDC_DMA2D_ACCL 0
#endif /* CONFIG_STM32_DMA2D */
#define LTDC_L1_ACCL LTDC_LTDC_ACCL_L1 | LTDC_DMA2D_ACCL
#ifdef CONFIG_STM32_LTDC_L2
# define LTDC_L2_ACCL LTDC_LTDC_ACCL_L2 | LTDC_DMA2D_ACCL
#endif
/* Acceleration support for DMA2D overlays */
#ifdef CONFIG_STM32_FB_CMAP
# ifdef CONFIG_FB_OVERLAY_BLIT
# define DMA2D_ACCL FB_ACCL_BLIT | FB_ACCL_AREA
# else
# define DMA2D_ACCL FB_ACCL_AREA
# endif
#else
# ifdef CONFIG_FB_OVERLAY_BLIT
# define DMA2D_ACCL FB_ACCL_AREA | \
FB_ACCL_TRANSP | \
FB_ACCL_COLOR | \
FB_ACCL_BLIT | \
FB_ACCL_BLEND
# else
# define DMA2D_ACCL FB_ACCL_AREA | \
FB_ACCL_TRANSP | \
FB_ACCL_COLOR
# endif
#endif
/* Helper */
#define MIN(x,y) ((x) < (y) ? (x) : (y))
/* Color normalization */
#if defined(CONFIG_STM32_LTDC_L1_RGB565)
# define RGB888_R(x) (((((x) >> 11) & 0x1f) * 527 + 23) >> 6)
# define RGB888_G(x) (((((x) >> 5) & 0x3f) * 259 + 33) >> 6)
# define RGB888_B(x) ((((x) & 0x1f) * 527 + 23) >> 6)
# define ARGB8888(x) ((RGB888_R(x) << 16) | \
(RGB888_G(x) << 8) | \
RGB888_B(x))
#else
# define ARGB8888(x) (x)
#endif
/****************************************************************************
* 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_ltdc_s
{
int layerno; /* layer number */
#ifdef CONFIG_FB_OVERLAY
struct fb_overlayinfo_s oinfo; /* Overlay info */
#endif
#ifdef CONFIG_STM32_DMA2D
struct stm32_dma2d_overlay_s dma2dinfo; /* Overlay info for DMA2D */
#endif
sem_t *lock; /* Layer exclusive access */
};
/* This structure provides the overall state of the LTDC layer */
struct stm32_ltdcdev_s
{
/* Framebuffer interface */
struct fb_vtable_s vtable;
/* Framebuffer video information */
struct fb_videoinfo_s vinfo;
/* Framebuffer plane information */
struct fb_planeinfo_s pinfo;
/* Cmap information */
#ifdef CONFIG_STM32_FB_CMAP
struct fb_cmap_s cmap;
#endif
/* Layer information */
struct stm32_ltdc_s layer[LTDC_NOVERLAYS];
#ifdef CONFIG_STM32_DMA2D
/* Interface to the dma2d controller */
FAR struct dma2d_layer_s *dma2d;
#endif
};
/* Interrupt handling */
struct stm32_interrupt_s
{
int irq; /* irq number */
int error; /* Interrupt error */
sem_t *sem; /* Semaphore for waiting for irq */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Overal LTDC helper */
static void stm32_ltdc_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, FAR void *arg);
static int stm32_ltdc_waitforirq(void);
static int stm32_ltdc_reload(uint8_t value, bool waitvblank);
/* Helper for layer register configuration */
static void stm32_ltdc_lpixelformat(FAR struct stm32_ltdc_s *layer);
static void stm32_ltdc_lframebuffer(FAR struct stm32_ltdc_s *layer);
static void stm32_ltdc_lenable(FAR struct stm32_ltdc_s *layer, bool enable);
static void stm32_ltdc_ldefaultcolor(FAR struct stm32_ltdc_s * layer,
uint32_t rgb);
static void stm32_ltdc_ltransp(FAR struct stm32_ltdc_s *layer, uint8_t transp,
uint32_t mode);
static void stm32_ltdc_lchromakey(FAR struct stm32_ltdc_s *layer,
uint32_t chromakey);
static void stm32_ltdc_lchromakeyenable(FAR struct stm32_ltdc_s *layer,
bool enable);
static void stm32_ltdc_linit(uint8_t lid);
#ifdef CONFIG_STM32_DMA2D
static void stm32_ltdc_dma2dlinit(void);
# ifdef CONFIG_FB_OVERLAY_BLIT
static bool stm32_ltdc_lvalidate(FAR const struct stm32_ltdc_s *layer,
FAR const struct fb_area_s *area);
# endif
#endif
#ifdef CONFIG_STM32_FB_CMAP
static void stm32_ltdc_lputclut(FAR struct stm32_ltdc_s * layer,
FAR const struct fb_cmap_s *cmap);
static void stm32_ltdc_lgetclut(FAR struct stm32_ltdc_s * layer,
FAR struct fb_cmap_s *cmap);
static void stm32_ltdc_lclutenable(FAR struct stm32_ltdc_s *layer,
bool enable);
#endif
static void stm32_ltdc_lclear(uint8_t overlayno);
/* Framebuffer interface */
static int stm32_getvideoinfo(FAR struct fb_vtable_s *vtable,
FAR struct fb_videoinfo_s *vinfo);
static int stm32_getplaneinfo(FAR struct fb_vtable_s *vtable,
int planeno,
FAR struct fb_planeinfo_s *pinfo);
/* The following is provided only if the video hardware supports RGB color
* mapping
*/
#ifdef CONFIG_STM32_FB_CMAP
static int stm32_getcmap(FAR struct fb_vtable_s *vtable,
FAR struct fb_cmap_s *cmap);
static int stm32_putcmap(FAR struct fb_vtable_s *vtable,
FAR const struct fb_cmap_s *cmap);
#endif
/* The following is provided only if the video hardware signals vertical
* synchronisation
*/
#ifdef CONFIG_FB_SYNC
static int stm32_waitforvsync(FAR struct fb_vtable_s *vtable);
#endif
/* The following is provided only if the video hardware supports overlays */
#ifdef CONFIG_FB_OVERLAY
static int stm32_getoverlayinfo(FAR struct fb_vtable_s *vtable,
int overlayno,
FAR struct fb_overlayinfo_s *oinfo);
static int stm32_settransp(FAR struct fb_vtable_s *vtable,
FAR const struct fb_overlayinfo_s *oinfo);
static int stm32_setchromakey(FAR struct fb_vtable_s *vtable,
FAR const struct fb_overlayinfo_s *oinfo);
static int stm32_setcolor(FAR struct fb_vtable_s *vtable,
FAR const struct fb_overlayinfo_s *oinfo);
static int stm32_setblank(FAR struct fb_vtable_s *vtable,
FAR const struct fb_overlayinfo_s *oinfo);
static int stm32_setarea(FAR struct fb_vtable_s *vtable,
FAR const struct fb_overlayinfo_s *oinfo);
/* The following is provided only if the video hardware supports blit and blend
* operation
*/
# ifdef CONFIG_FB_OVERLAY_BLIT
static int stm32_blit(FAR struct fb_vtable_s *vtable,
FAR const struct fb_overlayblit_s *blit);
static int stm32_blend(FAR struct fb_vtable_s *vtable,
FAR const struct fb_overlayblend_s *blend);
# endif /* CONFIG_FB_OVERLAY_BLIT */
#endif /* CONFIG_FB_OVERLAY */
/****************************************************************************
* 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))
#ifdef CONFIG_STM32_FB_CMAP
/* The layers clut table entries */
static uint8_t g_redclut[STM32_LTDC_NCLUT];
static uint8_t g_greenclut[STM32_LTDC_NCLUT];
static uint8_t g_blueclut[STM32_LTDC_NCLUT];
# ifdef CONFIG_STM32_FB_TRANSPARENCY
static uint8_t g_transpclut[STM32_LTDC_NCLUT];
# endif
#endif /* CONFIG_STM32_FB_CMAP */
/* 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 =
{
.irq = STM32_IRQ_LTDCINT,
.error = OK,
.sem = &g_semirq
};
/* This structure provides the internal interface */
static struct stm32_ltdcdev_s g_vtable =
{
.vtable =
{
.getvideoinfo = stm32_getvideoinfo,
.getplaneinfo = stm32_getplaneinfo
#ifdef CONFIG_FB_SYNC
,
.waitforvsync = stm32_waitforvsync
#endif
#ifdef CONFIG_STM32_FB_CMAP
,
.getcmap = stm32_getcmap,
.putcmap = stm32_putcmap
#endif
#ifdef CONFIG_FB_OVERLAY
,
.getoverlayinfo = stm32_getoverlayinfo,
.settransp = stm32_settransp,
.setchromakey = stm32_setchromakey,
.setcolor = stm32_setcolor,
.setblank = stm32_setblank,
.setarea = stm32_setarea
# ifdef CONFIG_FB_OVERLAY_BLIT
,
.blit = stm32_blit,
.blend = stm32_blend
# endif
#endif /* CONFIG_FB_OVERLAY */
},
#ifdef CONFIG_STM32_LTDC_L2
.pinfo =
{
.fbmem = (uint8_t *)STM32_LTDC_BUFFER_L2,
.fblen = STM32_LTDC_L2_FBSIZE,
.stride = STM32_LTDC_L2_STRIDE,
.display = 0,
.bpp = STM32_LTDC_L2_BPP
},
.vinfo =
{
.fmt = STM32_LTDC_L2_COLOR_FMT,
.xres = STM32_LTDC_WIDTH,
.yres = STM32_LTDC_HEIGHT,
.nplanes = 1,
# ifdef CONFIG_FB_OVERLAY
.noverlays = LTDC_NOVERLAYS
# endif
}
#else
.pinfo =
{
.fbmem = (uint8_t *)STM32_LTDC_BUFFER_L1,
.fblen = STM32_LTDC_L1_FBSIZE,
.stride = STM32_LTDC_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 CONFIG_FB_OVERLAY
.noverlays = LTDC_NOVERLAYS
# endif
}
#endif /* CONFIG_STM32_LTDC_L2 */
,
#ifdef CONFIG_STM32_FB_CMAP
.cmap =
{
.first = 0,
.len = STM32_LTDC_NCLUT,
.red = g_redclut,
.green = g_greenclut,
.blue = g_blueclut,
# ifdef CONFIG_STM32_FB_TRANSPARENCY
.transp = g_transpclut
# endif
}
,
#endif
.layer[LTDC_LAYER_L1] =
{
.layerno = LTDC_LAYER_L1,
#ifdef CONFIG_FB_OVERLAY
.oinfo =
{
.fbmem = (uint8_t *)STM32_LTDC_BUFFER_L1,
.fblen = STM32_LTDC_L1_FBSIZE,
.stride = STM32_LTDC_L1_STRIDE,
.overlay = LTDC_LAYER_L1,
.bpp = STM32_LTDC_L1_BPP,
.blank = 0,
.chromakey = 0,
.color = 0,
.transp =
{
.transp = 0xff,
.transp_mode = FB_CONST_ALPHA
},
.sarea =
{
.x = 0,
.y = 0,
.w = STM32_LTDC_WIDTH,
.h = STM32_LTDC_HEIGHT
},
.accl = LTDC_L1_ACCL
},
#endif
#ifdef CONFIG_STM32_DMA2D
.dma2dinfo =
{
.fmt = STM32_LTDC_L1_DMA2D_PF,
.transp_mode = STM32_DMA2D_PFCCR_AM_NONE,
.xres = STM32_LTDC_WIDTH,
.yres = STM32_LTDC_HEIGHT,
.oinfo = &g_vtable.layer[LTDC_LAYER_L1].oinfo
},
#endif
.lock = &g_lock
}
#ifdef CONFIG_STM32_LTDC_L2
,
.layer[LTDC_LAYER_L2] =
{
.layerno = LTDC_LAYER_L2,
#ifdef CONFIG_FB_OVERLAY
.oinfo =
{
.overlay = LTDC_LAYER_L2,
.fbmem = (uint8_t *)STM32_LTDC_BUFFER_L2,
.fblen = STM32_LTDC_L2_FBSIZE,
.stride = STM32_LTDC_L2_STRIDE,
.bpp = STM32_LTDC_L2_BPP,
.blank = 0,
.chromakey = 0,
.color = 0,
.transp =
{
.transp = 0xff,
.transp_mode = FB_CONST_ALPHA
},
.sarea =
{
.x = 0,
.y = 0,
.w = STM32_LTDC_WIDTH,
.h = STM32_LTDC_HEIGHT
},
.accl = LTDC_L2_ACCL
},
#endif
#ifdef CONFIG_STM32_DMA2D
.dma2dinfo =
{
.fmt = STM32_LTDC_L2_DMA2D_PF,
.transp_mode = STM32_DMA2D_PFCCR_AM_NONE,
.xres = STM32_LTDC_WIDTH,
.yres = STM32_LTDC_HEIGHT,
.oinfo = &g_vtable.layer[LTDC_LAYER_L2].oinfo
},
#endif
.lock = &g_lock
}
#endif
};
/* Configuration lookup tables */
/* LTDC width */
static const uint32_t stm32_width_layer_t[LTDC_NLAYERS] =
{
STM32_LTDC_WIDTH
#ifdef CONFIG_STM32_LTDC_L2
, STM32_LTDC_WIDTH
#endif
};
/* LTDC height */
static const uint32_t stm32_height_layer_t[LTDC_NLAYERS] =
{
STM32_LTDC_HEIGHT
#ifdef CONFIG_STM32_LTDC_L2
, STM32_LTDC_HEIGHT
#endif
};
/* LTDC stride */
static const uint32_t stm32_stride_layer_t[LTDC_NLAYERS] =
{
STM32_LTDC_L1_STRIDE
#ifdef CONFIG_STM32_LTDC_L2
, STM32_LTDC_L2_STRIDE
#endif
};
/* LTDC bpp */
static const uint32_t stm32_bpp_layer_t[LTDC_NLAYERS] =
{
STM32_LTDC_L1_BPP
#ifdef CONFIG_STM32_LTDC_L2
, STM32_LTDC_L2_BPP
#endif
};
/* LTDC framebuffer len */
static const uint32_t stm32_fblen_layer_t[LTDC_NLAYERS] =
{
STM32_LTDC_L1_FBSIZE
#ifdef CONFIG_STM32_LTDC_L2
, STM32_LTDC_L2_FBSIZE
#endif
};
/* LTDC framebuffer */
static const uint32_t stm32_fbmem_layer_t[LTDC_NLAYERS] =
{
STM32_LTDC_BUFFER_L1
#ifdef CONFIG_STM32_LTDC_L2
, STM32_LTDC_BUFFER_L2
#endif
};
/* LTDC default color lookup table */
static const uint32_t stm32_defaultcolor_layer_t[LTDC_NLAYERS] =
{
STM32_LTDC_L1_COLOR
#ifdef CONFIG_STM32_LTDC_L2
, STM32_LTDC_L2_COLOR
#endif
};
/* LTDC default chromakey */
static const uint32_t stm32_chromakey_layer_t[LTDC_NLAYERS] =
{
STM32_LTDC_L1_CHROMAKEY
#ifdef CONFIG_STM32_LTDC_L2
, STM32_LTDC_L2_CHROMAKEY
#endif
};
/* LTDC chromakey enabled state */
static const bool stm32_chromakeyen_layer_t[LTDC_NLAYERS] =
{
STM32_LTDC_L1_CHROMAEN
#ifdef CONFIG_STM32_LTDC_L2
, STM32_LTDC_L2_CHROMAEN
#endif
};
/* LTDC 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 CONFIG_STM32_FB_CMAP
static const uintptr_t stm32_clutwr_layer_t[LTDC_NLAYERS] =
{
STM32_LTDC_L1CLUTWR
# ifdef CONFIG_STM32_LTDC_L2
, STM32_LTDC_L2CLUTWR
# endif
};
#endif /* CONFIG_STM32_FB_CMAP */
/* The initialized state of the driver */
static bool g_initialized;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* 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 = getreg32(STM32_LTDC_GCR);
regval &= ~(LTDC_GCR_PCPOL | LTDC_GCR_DEPOL | LTDC_GCR_VSPOL |
LTDC_GCR_HSPOL);
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_ldefaultcolor
*
* Description:
* Configures layer default color.
*
* Input Parameters:
* layer - Reference to the layer control structure
* rgb - RGB888 background color
*
****************************************************************************/
static void stm32_ltdc_ldefaultcolor(FAR struct stm32_ltdc_s * layer,
uint32_t rgb)
{
DEBUGASSERT(layer->layerno < LTDC_NLAYERS);
reginfo("set LTDC_L%dDCCR=%08x\n", layer->layerno + 1, rgb);
putreg32(rgb, stm32_dccr_layer_t[layer->layerno]);
/* Reload shadow register */
stm32_ltdc_reload(LTDC_SRCR_IMR, false);
reginfo("configured LTDC_L%dDCCR=%08x\n", layer->layerno + 1,
getreg32(STM32_LTDC_BCCR));
}
/****************************************************************************
* Name: stm32_ltdc_bgcolor
*
* Description:
* Configures background color of the LCD controller.
*
* Input Parameters:
* 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.
*
* Input Parameters:
* 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_DBW_MASK | LTDC_GCR_DGW_MASK | LTDC_GCR_DRW_MASK);
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
*
* Input Parameters:
* 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 void *arg)
{
int ret;
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 */
reginfo("Register reloaded\n");
putreg32(LTDC_ICR_CRRIF, STM32_LTDC_ICR);
priv->error = OK;
}
else if (regval & LTDC_IER_LIE)
{
/* Line interrupt */
/* Clear the interrupt status register */
reginfo("Line interrupt\n");
putreg32(LTDC_ICR_CLIF, STM32_LTDC_ICR);
priv->error = OK;
}
else if (regval & LTDC_IER_TERRIE)
{
/* Transfer error interrupt */
/* Clear the interrupt status register */
reginfo("Error transfer\n");
putreg32(LTDC_ICR_CTERRIF, STM32_LTDC_ICR);
priv->error = -ECANCELED;
}
else if (regval & LTDC_IER_FUIE)
{
/* Fifo underrun error interrupt */
/* Clear the interrupt status register */
reginfo("Error fifo underrun\n");
putreg32(LTDC_ICR_CFUIF, STM32_LTDC_ICR);
priv->error = -ECANCELED;
}
else
{
DEBUGASSERT("Unknown interrupt");
}
/* Unlock the semaphore if locked */
ret = nxsem_post(priv->sem);
if (ret < 0)
{
lcderr("ERROR: nxsem_post() failed\n");
}
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.
*
* Returned Value:
* OK - On success otherwise ERROR
*
****************************************************************************/
static int stm32_ltdc_waitforirq(void)
{
int ret = OK;
FAR struct stm32_interrupt_s *priv = &g_interrupt;
ret = nxsem_wait(priv->sem);
if (ret < 0)
{
lcderr("ERROR: nxsem_wait() failed\n");
}
ret = priv->error;
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.
*
* Input Parameters:
* 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;
/* 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 (value == LTDC_SRCR_VBR && waitvblank)
{
/* Wait upon vertical blanking period */
ret = stm32_ltdc_waitforirq();
}
else
{
/* Wait until register reload hase been done */
while (getreg32(STM32_LTDC_SRCR) & value);
}
return ret;
}
/****************************************************************************
* Name: stm32_ltdc_irqconfig
*
* Description:
* Configure interrupts
*
****************************************************************************/
static void stm32_ltdc_irqconfig(void)
{
/* Initialize the LTDC semaphore that enforces mutually exclusive access */
nxsem_init(&g_lock, 0, 1);
/* Initialize the semaphore for interrupt handling. This waitsem
* semaphore is used for signaling and, hence, should not have priority
* inheritance enabled.
*/
nxsem_init(g_interrupt.sem, 0, 0);
nxsem_setprotocol(g_interrupt.sem, SEM_PRIO_NONE);
/* Attach LTDC interrupt vector */
(void)irq_attach(g_interrupt.irq, stm32_ltdcirq, NULL);
/* Enable the IRQ at the NVIC */
up_enable_irq(g_interrupt.irq);
/* Enable interrupts expect line interrupt */
stm32_ltdc_irqctrl(LTDC_IER_RRIE |
LTDC_IER_TERRIE |
LTDC_IER_FUIE,
LTDC_IER_LIE);
/* Configure line interrupt */
stm32_ltdc_linepos();
}
/****************************************************************************
* Name: stm32_ltdc_globalconfig
*
* Description:
* Configure background color
* Configure dithering
*
****************************************************************************/
static void stm32_ltdc_globalconfig(void)
{
/* 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 */
stm32_ltdc_bgcolor(STM32_LTDC_BACKCOLOR);
}
/****************************************************************************
* Name: stm32_ltdc_enable
*
* Description:
* Disable the LCD peripheral
*
* Input Parameters:
* enable - Enable or disable
*
****************************************************************************/
static void stm32_ltdc_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));
}
/****************************************************************************
* Name: stm32_ltdc_lpixelformat
*
* Description:
* Set the layer pixel format.
* Note! This changes have no effect until the shadow register reload has
* been done.
*
* Input Parameters:
* Reference to the layer control structure
*
****************************************************************************/
static void stm32_ltdc_lpixelformat(FAR struct stm32_ltdc_s *layer)
{
uint8_t overlay = layer->layerno;
DEBUGASSERT(layer->layerno < LTDC_NLAYERS);
/* Configure PFCR register */
reginfo("set LTDC_L%dPFCR=%08x\n", overlay + 1, stm32_fmt_layer_t[overlay]);
putreg32(stm32_fmt_layer_t[overlay], stm32_pfcr_layer_t[overlay]);
/* Reload shadow register */
stm32_ltdc_reload(LTDC_SRCR_IMR, false);
}
/****************************************************************************
* Name: stm32_ltdc_lframebuffer
*
* Description:
* Configure layer framebuffer of the entire window.
* Note! This changes have no effect until the shadow register reload has
* been done.
*
* Input Parameters:
* Reference to the layer control structure
*
****************************************************************************/
static void stm32_ltdc_lframebuffer(FAR struct stm32_ltdc_s *layer)
{
uint32_t cfblr;
uint32_t rxpos;
uint32_t rypos;
uint32_t whpcr;
uint32_t wvpcr;
uint8_t layerno = layer->layerno;
DEBUGASSERT(layer->layerno < LTDC_NLAYERS);
reginfo("xpos = %d, ypos = %d, xres = %d, yres = %d\n", 0, 0,
stm32_width_layer_t[layerno], stm32_height_layer_t[layerno]);
/* Calculate register position */
rxpos = STM32_LTDC_LxWHPCR_WHSTPOS + 1;
rypos = STM32_LTDC_LxWVPCR_WVSTPOS + 1;
/* Accumulate horizontal position */
whpcr = LTDC_LxWHPCR_WHSTPOS(rxpos);
whpcr |= LTDC_LxWHPCR_WHSPPOS(rxpos + stm32_width_layer_t[layerno] - 1);
/* Accumulate vertical position */
wvpcr = LTDC_LxWVPCR_WVSTPOS(rypos);
wvpcr |= LTDC_LxWVPCR_WVSPPOS(rypos + stm32_height_layer_t[layerno] - 1);
/* Configure LxWHPCR / LxWVPCR register */
reginfo("set LTDC_L%dWHPCR=%08x\n", layerno + 1, whpcr);
putreg32(whpcr, stm32_whpcr_layer_t[layerno]);
reginfo("set LTDC_L%dWVPCR=%08x\n", layerno + 1, wvpcr);
putreg32(wvpcr, stm32_wvpcr_layer_t[layerno]);
/* Configure LxCFBAR register */
reginfo("set LTDC_L%dCFBAR=%08x\n", layerno + 1,
stm32_fbmem_layer_t[layerno]);
putreg32(stm32_fbmem_layer_t[layerno], stm32_cfbar_layer_t[layerno]);
/* Configure LxCFBLR register */
/* Calculate line length */
cfblr = LTDC_LxCFBLR_CFBP(stm32_stride_layer_t[layerno]) |
LTDC_LxCFBLR_CFBLL(stm32_width_layer_t[layerno] *
STM32_LTDC_Lx_BYPP(stm32_bpp_layer_t[layerno]) + 3);
reginfo("set LTDC_L%dCFBLR=%08x\n", layerno + 1, cfblr);
putreg32(cfblr, stm32_cfblr_layer_t[layerno]);
/* Configure LxCFBLNR register */
reginfo("set LTDC_L%dCFBLNR=%08x\n", layerno + 1, stm32_height_layer_t[layerno]);
putreg32(stm32_height_layer_t[layerno], stm32_cfblnr_layer_t[layerno]);
/* Reload shadow register */
stm32_ltdc_reload(LTDC_SRCR_IMR, false);
}
/****************************************************************************
* Name: stm32_ltdc_lenable
*
* Description:
* Enable or disable layer.
* Note! This changes have no effect until the shadow register reload has
* been done.
*
* Input Parameters:
* layer - Reference to the layer control structure
* enable - Enable or disable layer
*
****************************************************************************/
static void stm32_ltdc_lenable(FAR struct stm32_ltdc_s *layer, bool enable)
{
uint32_t regval;
DEBUGASSERT(layer->layerno < LTDC_NLAYERS);
regval = getreg32(stm32_cr_layer_t[layer->layerno]);
if (enable == true)
{
regval |= LTDC_LxCR_LEN;
}
else
{
regval &= ~LTDC_LxCR_LEN;
}
/* Enable/Disable layer */
reginfo("set LTDC_L%dCR=%08x\n", layer->layerno + 1, regval);
putreg32(regval, stm32_cr_layer_t[layer->layerno]);
/* Reload shadow register */
stm32_ltdc_reload(LTDC_SRCR_IMR, false);
}
/****************************************************************************
* Name: stm32_ltdc_ltransp
*
* Description:
* Change layer transparency.
* Note! This changes have no effect until the shadow register reload has
* been done.
*
* Input Parameters:
* layer - Reference to the layer control structure
* transp - Transparency
* mode - Transparency mode
*
****************************************************************************/
static void stm32_ltdc_ltransp(FAR struct stm32_ltdc_s *layer, uint8_t transp,
uint32_t mode)
{
uint32_t bf1;
uint32_t bf2;
DEBUGASSERT(layer->layerno < LTDC_NLAYERS);
#ifdef CONFIG_FB_OVERLAY
if (mode == FB_CONST_ALPHA)
{
bf1 = LTDC_BF1_CONST_ALPHA;
bf2 = LTDC_BF2_CONST_ALPHA;
}
else
{
bf1 = LTDC_BF1_PIXEL_ALPHA;
bf2 = LTDC_BF2_PIXEL_ALPHA;
}
#else
bf1 = LTDC_BF1_CONST_ALPHA;
bf2 = LTDC_BF2_CONST_ALPHA;
#endif
reginfo("set LTDC_L%dBFCR=%08x\n", layer->layerno + 1,
(LTDC_LxBFCR_BF1(bf1) | LTDC_LxBFCR_BF2(bf2)));
/* Set blendmode */
putreg32((LTDC_LxBFCR_BF1(bf1) | LTDC_LxBFCR_BF2(bf2)),
stm32_bfcr_layer_t[layer->layerno]);
/* Set alpha */
reginfo("set LTDC_L%dCACR=%02x\n", layer->layerno + 1, transp);
putreg32(transp, stm32_cacr_layer_t[layer->layerno]);
/* Reload shadow register */
stm32_ltdc_reload(LTDC_SRCR_IMR, false);
}
/****************************************************************************
* Name: stm32_ltdc_lchromakey
*
* Description:
* Change layer chromakey.
* Note! This changes have no effect until the shadow register reload has
* been done.
*
* Input Parameters:
* layer - Reference to the layer control structure
* chroma - chromakey
*
****************************************************************************/
static void stm32_ltdc_lchromakey(FAR struct stm32_ltdc_s *layer,
uint32_t chroma)
{
uint32_t rgb;
DEBUGASSERT(layer->layerno < LTDC_NLAYERS);
reginfo("%08x\n", getreg32(stm32_cr_layer_t[layer->layerno]));
/* Set chromakey */
#ifdef CONFIG_STM32_FB_CMAP
uint8_t r = g_vtable.cmap.red[chroma];
uint8_t g = g_vtable.cmap.green[chroma];
uint8_t b = g_vtable.cmap.blue[chroma];
rgb = ((r << 16)|(g << 8)|b);
#else
rgb = ARGB8888(chroma);
#endif
reginfo("set LTDC_L%dCKCR=%08x\n", layer->layerno + 1, rgb);
putreg32(rgb, stm32_ckcr_layer_t[layer->layerno]);
/* Reload shadow register */
stm32_ltdc_reload(LTDC_SRCR_IMR, false);
}
/****************************************************************************
* Name: stm32_ltdc_lchromakeyenable
*
* Description:
* Enable or disable layer chromakey support.
* Note! This changes have no effect until the shadow register reload has
* been done.
*
* Input Parameters:
* layer - Reference to the layer control structure
* enable - Enable or disable chromakey
*
****************************************************************************/
static void stm32_ltdc_lchromakeyenable(FAR struct stm32_ltdc_s *layer,
bool enable)
{
uint32_t regval;
DEBUGASSERT(layer->layerno < LTDC_NLAYERS);
regval = getreg32(stm32_cr_layer_t[layer->layerno]);
/* Enable/Disable colorkey */
if (enable == true)
{
regval |= LTDC_LxCR_COLKEN;
}
else
{
regval &= ~LTDC_LxCR_COLKEN;
}
reginfo("set LTDC_L%dCR=%08x\n", layer->layerno + 1, regval);
putreg32(regval, stm32_cr_layer_t[layer->layerno]);
/* Reload shadow register */
stm32_ltdc_reload(LTDC_SRCR_IMR, false);
}
/****************************************************************************
* Name: stm32_ltdc_lclutenable
*
* Description:
* Disable or enable the layer clut support
*
* Input Parameters:
* layer - Reference to the layer control structure
* enable - Enable or disable
*
****************************************************************************/
#ifdef CONFIG_STM32_FB_CMAP
static void stm32_ltdc_lclutenable(FAR struct stm32_ltdc_s *layer, bool enable)
{
uint32_t regval;
regval = getreg32(stm32_cr_layer_t[layer->oinfo.overlay]);
reginfo("get LTDC_L%dCR=%08x\n", layer->oinfo.overlay + 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->oinfo.overlay, regval);
putreg32(regval, stm32_cr_layer_t[layer->oinfo.overlay]);
/* Reload shadow register */
stm32_ltdc_reload(LTDC_SRCR_IMR, false);
}
/****************************************************************************
* Name: stm32_ltdc_lputclut
*
* Description:
* Update the clut layer register during blank period.
* Note! The clut register is no shadow register.
*
* Input Parameters:
* layer - Reference to the layer control structure
* cmap - Color map
*
****************************************************************************/
static void stm32_ltdc_lputclut(FAR struct stm32_ltdc_s *layer,
FAR const struct fb_cmap_s *cmap)
{
int n;
irqstate_t flags;
/* Disable clut during register update */
stm32_ltdc_lclutenable(layer, false);
/* Update the clut registers. Ensure operation is atomic or in interrupt
* protected context.
*/
flags = enter_critical_section();
for (n = cmap->first; n < cmap->len && n < STM32_LTDC_NCLUT; n++)
{
uint32_t regval;
regval = (uint32_t)LTDC_CLUT_ADD(n) |
(uint32_t)LTDC_CLUT_RED(cmap->red[n]) |
(uint32_t)LTDC_CLUT_GREEN(cmap->green[n]) |
(uint32_t)LTDC_CLUT_BLUE(cmap->blue[n]);
reginfo("set LTDC_L%dCLUTWR = %08x, first = %d, len = %d\n",
layer->oinfo.overlay + 1, regval, cmap->first, cmap->len);
putreg32(regval, stm32_clutwr_layer_t[layer->oinfo.overlay]);
}
leave_critical_section(flags);
/* Enable clut after register update */
stm32_ltdc_lclutenable(layer, true);
/* Reload shadow control register */
stm32_ltdc_reload(LTDC_SRCR_IMR, false);
}
/****************************************************************************
* Name: stm32_ltdc_lgetclut
*
* Description:
* Copy the layers color lookup table.
*
* Input Parameters:
* layer - Reference to the layer control structure
* cmap - Color map
*
****************************************************************************/
static void stm32_ltdc_lgetclut(FAR struct stm32_ltdc_s * layer,
FAR struct fb_cmap_s * cmap)
{
int n;
FAR struct fb_cmap_s *priv_cmap = &g_vtable.cmap;
/* Copy from internal cmap */
for (n = cmap->first; n < cmap->len && n < STM32_LTDC_NCLUT; n++)
{
# ifdef CONFIG_STM32_FB_TRANSPARENCY
cmap->transp[n] = priv_cmap->transp[n];
# endif
cmap->red[n] = priv_cmap->red[n];
cmap->green[n] = priv_cmap->green[n];
cmap->blue[n] = priv_cmap->blue[n];
reginfo("color = %d, transp=%02x, red=%02x, green=%02x, blue=%02x\n", n,
# ifdef CONFIG_STM32_FB_TRANSPARENCY
cmap->transp[n],
# endif
cmap->red[n],
cmap->green[n],
cmap->blue[n]);
}
}
#endif /* CONFIG_STM32_FB_CMAP */
/****************************************************************************
* Name: stm32_ltdc_lclear
*
* Description:
* Clear the whole layer
*
* Input Parameters:
* overlayno - Number overlay
*
****************************************************************************/
static void stm32_ltdc_lclear(uint8_t overlayno)
{
memset((uint8_t*)stm32_fbmem_layer_t[overlayno], 0,
stm32_fblen_layer_t[overlayno]);
}
/****************************************************************************
* Name: stm32_ltdc_lvalidate
*
* Description:
* Validates if the given area is within the overlay framebuffer memory
* region
*
* Input Parameters:
* layer - Reference to the layer control structure
* area - Reference to the overlay area
*
****************************************************************************/
#if defined(CONFIG_STM32_DMA2D) && defined(CONFIG_FB_OVERLAY_BLIT)
static bool stm32_ltdc_lvalidate(FAR const struct stm32_ltdc_s *layer,
FAR const struct fb_area_s *area)
{
uint32_t offset;
offset = (area->y + area->h - 1) * layer->oinfo.stride + (area->x + area->w) *
layer->oinfo.bpp / 8;
return (offset <= layer->oinfo.fblen && area->w > 0 && area->h > 0);
}
#endif /* defined(CONFIG_STM32_DMA2D) && defined(CONFIG_FB_OVERLAY_BLIT) */
/****************************************************************************
* Name: stm32_ltdc_linit
*
* Description:
* Initialize layer to their default states.
*
* Initialize:
* - layer framebuffer
* - layer pixelformat
* - layer defaultcolor
* - layer chromakey
* - layer transparency
* - layer clut
*
* Input Parameters:
* layer - Reference to the layer control structure
*
****************************************************************************/
static void stm32_ltdc_linit(uint8_t overlay)
{
DEBUGASSERT(overlay < LTDC_NLAYERS);
FAR struct stm32_ltdcdev_s *dev = &g_vtable;
FAR struct stm32_ltdc_s *layer = &dev->layer[overlay];
/* Disable layer */
stm32_ltdc_lenable(layer, false);
/* Clear the layer framebuffer */
stm32_ltdc_lclear(overlay);
/* Set layers framebuffer */
stm32_ltdc_lframebuffer(layer);
/* Set layers pixel input format */
stm32_ltdc_lpixelformat(layer);
/* Configure layer default color */
stm32_ltdc_ldefaultcolor(layer, stm32_defaultcolor_layer_t[overlay]);
/* Layers default transparency */
stm32_ltdc_ltransp(layer, 0xff, 0);
/* Layers chromakey */
stm32_ltdc_lchromakey(layer, stm32_chromakey_layer_t[overlay]);
/* Enable chromakey */
stm32_ltdc_lchromakeyenable(layer, stm32_chromakeyen_layer_t[overlay]);
#ifdef CONFIG_STM32_FB_CMAP
/* Disable clut by default */
if (dev->vinfo.fmt == FB_FMT_RGB8)
{
/* Initialize LTDC clut register */
stm32_ltdc_lputclut(layer, &g_vtable.cmap);
/* Configure the clut register */
stm32_ltdc_lclutenable(layer, true);
}
#endif
/* Finally enable the layer */
stm32_ltdc_lenable(layer, true);
}
/****************************************************************************
* Name: stm32_ltdc_dma2dlinit
*
* Description:
* Initialize dma2d layer to their default states.
*
* Initialize:
* - layer framebuffer
* - layer pixelformat
* - layer size
* - layer color
* - layer chromakey
* - layer transparency
* - layer clut
*
* Input Parameters:
* layer - Reference to the layer control structure
*
****************************************************************************/
#ifdef CONFIG_STM32_DMA2D
static void stm32_ltdc_dma2dlinit(void)
{
int n;
FAR struct stm32_ltdcdev_s *dev = &g_vtable;
for (n = 0; n < DMA2D_NLAYERS; n++)
{
uint32_t overlay = n + LTDC_NLAYERS;
FAR struct stm32_ltdc_s *layer = &dev->layer[overlay];
uint8_t * fbmem = (uint8_t *)STM32_DMA2D_BUFFER_START;
layer->layerno = overlay;
layer->oinfo.fbmem = fbmem + STM32_DMA2D_LAYER_SIZE * n;
layer->oinfo.fblen = STM32_DMA2D_FBSIZE;
layer->oinfo.stride = STM32_DMA2D_STRIDE;
layer->oinfo.overlay = overlay;
layer->oinfo.bpp = STM32_DMA2D_BPP;
layer->oinfo.blank = 0;
layer->oinfo.chromakey = 0;
layer->oinfo.color = 0;
layer->oinfo.transp.transp = 0xff;
layer->oinfo.transp.transp_mode = 0;
layer->oinfo.sarea.x = 0;
layer->oinfo.sarea.y = 0;
layer->oinfo.sarea.w = STM32_DMA2D_WIDTH;
layer->oinfo.sarea.h = STM32_DMA2D_HEIGHT;
layer->oinfo.accl = DMA2D_ACCL;
layer->lock = &g_lock;
layer->dma2dinfo.fmt = STM32_DMA2D_COLOR_FMT;
layer->dma2dinfo.transp_mode = STM32_DMA2D_PFCCR_AM_NONE;
layer->dma2dinfo.xres = layer->oinfo.sarea.w;
layer->dma2dinfo.yres = layer->oinfo.sarea.h;
layer->dma2dinfo.oinfo = &layer->oinfo;
}
}
#endif /* CONFIG_STM32_DMA2D */
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: stm32_getvideoinfo
*
* Description:
* Entrypoint ioctl FBIOGET_VIDEOINFO
* Get the videoinfo for the framebuffer
*
* Input Parameters:
* vtable - The framebuffer driver object
* vinfo - the videoinfo object
*
* Returned Value:
* On success - OK
* On error - -EINVAL
*
****************************************************************************/
static int stm32_getvideoinfo(struct fb_vtable_s *vtable,
struct fb_videoinfo_s *vinfo)
{
FAR struct stm32_ltdcdev_s *priv = (FAR struct stm32_ltdcdev_s *)vtable;
lcdinfo("vtable=%p vinfo=%p\n", vtable, vinfo);
DEBUGASSERT(vtable != NULL && priv == &g_vtable && vinfo != NULL);
memcpy(vinfo, &priv->vinfo, sizeof(struct fb_videoinfo_s));
return OK;
}
/****************************************************************************
* Name: stm32_getplaneinfo
*
* Description:
* Entrypoint ioctl FBIOGET_PLANEINFO
* Get the planeinfo for the framebuffer
*
* Input Parameters:
* vtable - The framebuffer driver object
* pinfo - the planeinfo object
*
* Returned Value:
* On success - OK
* On error - -EINVAL
*
****************************************************************************/
static int stm32_getplaneinfo(struct fb_vtable_s *vtable, int planeno,
struct fb_planeinfo_s *pinfo)
{
FAR struct stm32_ltdcdev_s *priv = (FAR struct stm32_ltdcdev_s *)vtable;
DEBUGASSERT(vtable != NULL && priv == &g_vtable);
lcdinfo("vtable=%p planeno=%d pinfo=%p\n", vtable, planeno, pinfo);
if (planeno == 0)
{
memcpy(pinfo, &priv->pinfo, sizeof(struct fb_planeinfo_s));
return OK;
}
lcderr("ERROR: Returning EINVAL\n");
return -EINVAL;
}
/****************************************************************************
* Name: stm32_getcmap
*
* Description:
* Entrypoint ioctl FBIOGET_CMAP
* Get a range of CLUT values for the LCD
*
* Input Parameters:
* vtable - The framebuffer driver object
* cmap - the color table
*
* Returned Value:
* On success - OK
* On error - -EINVAL
*
****************************************************************************/
#ifdef CONFIG_STM32_FB_CMAP
static int stm32_getcmap(struct fb_vtable_s *vtable,
struct fb_cmap_s *cmap)
{
int ret;
FAR struct stm32_ltdcdev_s *priv = (FAR struct stm32_ltdcdev_s *)vtable;
DEBUGASSERT(vtable != NULL && priv == &g_vtable && cmap != NULL);
lcdinfo("vtable=%p cmap=%p\n", vtable, cmap);
if (priv->vinfo.fmt != FB_FMT_RGB8)
{
lcderr("ERROR: CLUT is not supported for the pixel format: %d\n",
priv->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
{
/* Currently, there is no api to set color map for each overlay
* separately. LTDC layers can have different color maps. Get the cmap
* from the main overlay.
*/
FAR struct stm32_ltdc_s * layer;
# ifdef CONFIG_STM32_LTDC_L2
layer = &priv->layer[LTDC_LAYER_L2];
# else
layer = &priv->layer[LTDC_LAYER_L1];
# endif
nxsem_wait(layer->lock);
stm32_ltdc_lgetclut(layer, cmap);
nxsem_post(layer->lock);
ret = OK;
}
return ret;
}
/****************************************************************************
* Name: stm32_putcmap
*
* Description:
* Entrypoint ioctl FBIOPUT_CMAP
* Set a range of the CLUT values for the LCD
*
* Input Parameters:
* vtable - The framebuffer driver object
* cmap - the color table
*
* Returned Value:
* On success - OK
* On error - -EINVAL
*
****************************************************************************/
static int stm32_putcmap(struct fb_vtable_s *vtable,
const struct fb_cmap_s *cmap)
{
int ret;
FAR struct stm32_ltdcdev_s *priv = (FAR struct stm32_ltdcdev_s *)vtable;
DEBUGASSERT(vtable != NULL && priv == &g_vtable && cmap != NULL);
lcdinfo("vtable=%p cmap=%p\n", vtable, cmap);
if (priv->vinfo.fmt != FB_FMT_RGB8)
{
lcderr("ERROR: CLUT is not supported for the pixel format: %d\n",
priv->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
{
/* Currently, there is no api to set color map for each overlay
* separately. LTDC layers can have different color maps, but is shared
* for now.
*/
int n;
FAR struct fb_cmap_s *priv_cmap = &g_vtable.cmap;
/* First copy to internal cmap */
for (n = cmap->first; n < cmap->len && n < STM32_LTDC_NCLUT; n++)
{
priv_cmap->red[n] = cmap->red[n];
priv_cmap->green[n] = cmap->green[n];
priv_cmap->blue[n] = cmap->blue[n];
# ifdef CONFIG_STM32_FB_TRANSPARENCY
/* Not supported by LTDC */
priv_cmap->transp[n] = cmap->transp[n];
# endif
}
priv_cmap->first = cmap->first;
priv_cmap->len = cmap->len;
/* Update the layer clut register */
nxsem_wait(&g_lock);
for (n = 0; n < LTDC_NLAYERS; n++)
{
FAR struct stm32_ltdc_s * layer = &priv->layer[n];
stm32_ltdc_lputclut(layer, priv_cmap);
}
# ifdef CONFIG_STM32_DMA2D
/* Update dma2d cmap */
priv->dma2d->setclut(cmap);
# endif
nxsem_post(&g_lock);
ret = OK;
}
return ret;
}
#endif /* CONFIG_STM32_FB_CMAP */
/***************************************************************************
* Name: stm32_ioctl_waitforvsync
* Description:
* Entrypoint ioctl FBIO_WAITFORSYNC
***************************************************************************/
#ifdef CONFIG_FB_SYNC
static int stm32_waitforvsync(FAR struct fb_vtable_s *vtable)
{
int ret;
DEBUGASSERT(vtable != NULL && vtable == &g_vtable.vtable);
/* Wait upon vertical synchronization. */
ret = stm32_ltdc_reload(LTDC_SRCR_VBR, true);
return ret;
}
#endif /* CONFIG_FB_SYNC */
/***************************************************************************
* Name: stm32_getoverlayinfo
* Description:
* Entrypoint ioctl FBIOGET_OVERLAYINFO
***************************************************************************/
#ifdef CONFIG_FB_OVERLAY
static int stm32_getoverlayinfo(FAR struct fb_vtable_s *vtable,
int overlayno,
FAR struct fb_overlayinfo_s *oinfo)
{
FAR struct stm32_ltdcdev_s *priv = (FAR struct stm32_ltdcdev_s*)vtable;
lcdinfo("vtable=%p overlay=%d oinfo=%p\n", vtable, overlayno, oinfo);
DEBUGASSERT(vtable != NULL && priv == &g_vtable);
if (overlayno < LTDC_NOVERLAYS)
{
FAR struct stm32_ltdc_s * layer = &priv->layer[overlayno];
memcpy(oinfo, &layer->oinfo, sizeof(struct fb_overlayinfo_s));
return OK;
}
lcderr("ERROR: Returning EINVAL\n");
return -EINVAL;
}
/***************************************************************************
* Name: stm32_settransp
* Description:
* Entrypoint ioctl FBIOSET_TRANSP
***************************************************************************/
static int stm32_settransp(FAR struct fb_vtable_s *vtable,
FAR const struct fb_overlayinfo_s *oinfo)
{
FAR struct stm32_ltdcdev_s *priv = (FAR struct stm32_ltdcdev_s*)vtable;
DEBUGASSERT(vtable != NULL && priv == &g_vtable);
lcdinfo("vtable=%p, overlay=%d, transp=%02x, transp_mode=%02x\n", vtable,
oinfo->overlay, oinfo->transp.transp, oinfo->transp.transp_mode);
if (oinfo->transp.transp_mode > 1)
{
lcderr("ERROR: Returning ENOSYS, transparency mode not supported\n");
return -ENOSYS;
}
if (oinfo->overlay < LTDC_NOVERLAYS)
{
FAR struct stm32_ltdc_s * layer = &priv->layer[oinfo->overlay];
nxsem_wait(layer->lock);
layer->oinfo.transp.transp = oinfo->transp.transp;
layer->oinfo.transp.transp_mode = oinfo->transp.transp_mode;
# ifdef CONFIG_STM32_DMA2D
if (layer->oinfo.transp.transp_mode == 0)
{
layer->dma2dinfo.transp_mode = STM32_DMA2D_PFCCR_AM_CONST;
}
else if (layer->oinfo.transp.transp_mode == 1)
{
layer->dma2dinfo.transp_mode = STM32_DMA2D_PFCCR_AM_PIXEL;
}
if (oinfo->overlay < LTDC_NLAYERS)
# endif
{
/* Set LTDC blendmode and alpha value */
stm32_ltdc_ltransp(layer, layer->oinfo.transp.transp,
layer->oinfo.transp.transp_mode);
}
nxsem_post(layer->lock);
return OK;
}
lcderr("ERROR: Returning EINVAL\n");
return -EINVAL;
}
/***************************************************************************
* Name: stm32_setchromakey
* Description:
* Entrypoint ioctl FBIOSET_CHROMAKEY
***************************************************************************/
static int stm32_setchromakey(FAR struct fb_vtable_s *vtable,
FAR const struct fb_overlayinfo_s *oinfo)
{
FAR struct stm32_ltdcdev_s *priv = (FAR struct stm32_ltdcdev_s *)vtable;
DEBUGASSERT(vtable != NULL && priv == &g_vtable && oinfo != NULL);
lcdinfo("vtable=%p, overlay=%d, chromakey=%08x\n", vtable,
oinfo->overlay, oinfo->chromakey);
if (oinfo->overlay < LTDC_NLAYERS)
{
int ret;
FAR struct stm32_ltdc_s * layer = &priv->layer[oinfo->overlay];
# ifndef CONFIG_STM32_LTDC_L1_CHROMAKEY
if (oinfo->overlay == LTDC_LAYER_L1)
{
return -ENOSYS;
}
# endif
# ifndef CONFIG_STM32_LTDC_L2_CHROMAKEY
if (oinfo->overlay == LTDC_LAYER_L2)
{
return -ENOSYS;
}
# endif
nxsem_wait(layer->lock);
# ifdef CONFIG_STM32_FB_CMAP
if (oinfo->chromakey >= g_vtable.cmap.len)
{
lcderr("ERROR: Clut index %d is out of range\n", oinfo->chromakey);
ret = -EINVAL;
}
else
# endif
{
layer->oinfo.chromakey = oinfo->chromakey;
/* Set chromakey */
stm32_ltdc_lchromakey(layer, layer->oinfo.chromakey);
ret = OK;
}
nxsem_post(layer->lock);
return ret;
}
# ifdef CONFIG_STM32_DMA2D
else if (oinfo->overlay < LTDC_NOVERLAYS)
{
/* Chromakey not supported by DMA2D */
return -ENOSYS;
}
# endif
lcderr("ERROR: Returning EINVAL\n");
return -EINVAL;
}
/***************************************************************************
* Name: stm32_setcolor
* Description:
* Entrypoint ioctl FBIOSET_COLOR
***************************************************************************/
static int stm32_setcolor(FAR struct fb_vtable_s *vtable,
FAR const struct fb_overlayinfo_s *oinfo)
{
DEBUGASSERT(vtable != NULL && vtable == &g_vtable.vtable && oinfo != NULL);
lcdinfo("vtable=%p, overlay=%d, color=%08x\n", vtable, oinfo->color);
if (oinfo->overlay < LTDC_NOVERLAYS)
{
# ifdef CONFIG_STM32_DMA2D
/* Set color within the active overlay is not supported by LTDC. So use
* DMA2D controller instead when configured.
*/
int ret;
FAR struct stm32_ltdcdev_s *priv = (FAR struct stm32_ltdcdev_s*)vtable;
FAR struct stm32_ltdc_s * layer = &priv->layer[oinfo->overlay];
FAR struct fb_overlayinfo_s * poverlay = layer->dma2dinfo.oinfo;
DEBUGASSERT(&layer->oinfo == poverlay);
nxsem_wait(layer->lock);
poverlay->color = oinfo->color;
ret = priv->dma2d->fillcolor(&layer->dma2dinfo, &poverlay->sarea,
poverlay->color);
nxsem_post(layer->lock);
return ret;
# else
/* Coloring not supported by LTDC */
return -ENOSYS;
# endif
}
lcderr("ERROR: Returning EINVAL\n");
return -EINVAL;
}
/***************************************************************************
* Name: stm32_setblank
* Description:
* Entrypoint ioctl FBIOSET_BLANK
***************************************************************************/
static int stm32_setblank(FAR struct fb_vtable_s *vtable,
FAR const struct fb_overlayinfo_s *oinfo)
{
FAR struct stm32_ltdcdev_s *priv = (FAR struct stm32_ltdcdev_s*)vtable;
DEBUGASSERT(vtable != NULL && priv == &g_vtable && oinfo != NULL);
lcdinfo("vtable=%p, overlay=%d, blank=%02x\n", vtable, oinfo->blank);
if (oinfo->overlay < LTDC_NLAYERS)
{
FAR struct stm32_ltdc_s * layer = &priv->layer[oinfo->overlay];
nxsem_wait(layer->lock);
layer->oinfo.blank = oinfo->blank;
/* Enable or disable layer */
stm32_ltdc_lenable(layer, (layer->oinfo.blank == 0));
nxsem_post(layer->lock);
return OK;
}
# ifdef CONFIG_STM32_DMA2D
else if (oinfo->overlay < LTDC_NOVERLAYS)
{
/* DMA2D overlays are non visible */
return OK;
}
# endif
lcderr("ERROR: Returning EINVAL\n");
return -EINVAL;
}
/***************************************************************************
* Name: stm32_setarea
* Description:
* Entrypoint ioctl FBIOSET_AREA
***************************************************************************/
static int stm32_setarea(FAR struct fb_vtable_s *vtable,
FAR const struct fb_overlayinfo_s *oinfo)
{
DEBUGASSERT(vtable != NULL && vtable == &g_vtable.vtable && oinfo != NULL);
lcdinfo("vtable=%p, overlay=%d, x=%d, y=%d, w=%d, h=%d\n", vtable,
oinfo->overlay, oinfo->sarea.x, oinfo->sarea.y, oinfo->sarea.w,
oinfo->sarea.h);
if (oinfo->overlay < LTDC_NLAYERS)
{
/* LTDC area is defined by the overlay size (display resolution) only */
return -ENOSYS;
}
# ifdef CONFIG_STM32_DMA2D
if (oinfo->overlay < LTDC_NOVERLAYS)
{
FAR struct stm32_ltdcdev_s *priv = (FAR struct stm32_ltdcdev_s*)vtable;
FAR struct stm32_ltdc_s * layer = &priv->layer[oinfo->overlay];
nxsem_wait(layer->lock);
memcpy(&layer->oinfo.sarea, &oinfo->sarea, sizeof(struct fb_area_s));
nxsem_post(layer->lock);
return OK;
}
# endif
lcderr("ERROR: Returning EINVAL\n");
return -EINVAL;
}
/***************************************************************************
* Name: stm32_blit
* Description:
* Entrypoint ioctl FBIOSET_BLIT
***************************************************************************/
# ifdef CONFIG_FB_OVERLAY_BLIT
static int stm32_blit(FAR struct fb_vtable_s *vtable,
FAR const struct fb_overlayblit_s *blit)
{
DEBUGASSERT(vtable != NULL && vtable == &g_vtable.vtable && blit != NULL);
lcdinfo("vtable = %p, blit = %p\n", vtable, blit);
if (blit->dest.overlay < LTDC_NOVERLAYS && blit->src.overlay < LTDC_NOVERLAYS)
{
# ifdef CONFIG_STM32_DMA2D
int ret;
struct fb_area_s sarea;
FAR const struct fb_area_s *darea = &blit->dest.area;
FAR struct stm32_ltdcdev_s *priv = (FAR struct stm32_ltdcdev_s*)vtable;
FAR struct stm32_ltdc_s *dlayer = &priv->layer[blit->dest.overlay];
FAR struct stm32_ltdc_s *slayer = &priv->layer[blit->src.overlay];
DEBUGASSERT(&dlayer->oinfo == dlayer->dma2dinfo.oinfo &&
&slayer->oinfo == slayer->dma2dinfo.oinfo);
/* DMA2D doesn't support image scale, so set to the smallest area */
memcpy(&sarea, &blit->src.area, sizeof(struct fb_area_s));
/* Check if area is within the entire overlay */
if (!stm32_ltdc_lvalidate(dlayer, darea) ||
!stm32_ltdc_lvalidate(slayer, &sarea))
{
return -EINVAL;
}
sarea.w = MIN(darea->w, sarea.w);
sarea.h = MIN(darea->h, sarea.h);
nxsem_wait(dlayer->lock);
ret = priv->dma2d->blit(&dlayer->dma2dinfo, darea->x, darea->y,
&slayer->dma2dinfo, &sarea);
nxsem_post(dlayer->lock);
return ret;
# else
/* LTDC doesn't support blit transfer */
return -ENOSYS;
# endif
}
lcderr("ERROR: Returning EINVAL\n");
return -EINVAL;
}
/***************************************************************************
* Name: stm32_blend
* Description:
* Entrypoint ioctl FBIOSET_BLEND
***************************************************************************/
static int stm32_blend(FAR struct fb_vtable_s *vtable,
FAR const struct fb_overlayblend_s *blend)
{
DEBUGASSERT(vtable != NULL && vtable == &g_vtable.vtable && blend != NULL);
lcdinfo("vtable = %p, blend = %p\n", vtable, blend);
if (blend->dest.overlay < LTDC_NOVERLAYS &&
blend->foreground.overlay < LTDC_NOVERLAYS &&
blend->background.overlay < LTDC_NOVERLAYS)
{
# ifdef CONFIG_STM32_DMA2D
int ret;
struct fb_area_s barea;
FAR const struct fb_area_s *darea = &blend->dest.area;
FAR const struct fb_area_s *farea = &blend->foreground.area;
FAR struct stm32_ltdcdev_s *priv = (FAR struct stm32_ltdcdev_s*)vtable;
FAR struct stm32_ltdc_s *dlayer = &priv->layer[blend->dest.overlay];
FAR struct stm32_ltdc_s *flayer = &priv->layer[blend->foreground.overlay];
FAR struct stm32_ltdc_s *blayer = &priv->layer[blend->background.overlay];
DEBUGASSERT(&dlayer->oinfo == dlayer->dma2dinfo.oinfo &&
&flayer->oinfo == flayer->dma2dinfo.oinfo &&
&blayer->oinfo == blayer->dma2dinfo.oinfo);
/* DMA2D doesn't support image scale, so set to the smallest area */
memcpy(&barea, &blend->background.area, sizeof(struct fb_area_s));
/* Check if area is within the entire overlay */
if (!stm32_ltdc_lvalidate(dlayer, darea) ||
!stm32_ltdc_lvalidate(flayer, farea) ||
!stm32_ltdc_lvalidate(blayer, &barea))
{
lcderr("ERROR: Returning EINVAL\n");
return -EINVAL;
}
barea.w = MIN(darea->w, barea.w);
barea.h = MIN(darea->h, barea.h);
barea.w = MIN(farea->w, barea.w);
barea.h = MIN(farea->h, barea.h);
nxsem_wait(dlayer->lock);
ret = priv->dma2d->blend(&dlayer->dma2dinfo, darea->x, darea->y,
&flayer->dma2dinfo, farea->x, farea->y,
&blayer->dma2dinfo, &barea);
nxsem_post(dlayer->lock);
return ret;
# else
/* LTDC doesn't support blend transfer */
return -ENOSYS;
# endif
}
lcderr("ERROR: Returning EINVAL\n");
return -EINVAL;
}
# endif /* CONFIG_FB_OVERLAY_BLIT */
#endif /* CONFIG_FB_OVERLAY */
/****************************************************************************
* Name: stm32_ltdcreset
*
* Description:
* Reset LTDC via APB2RSTR
*
****************************************************************************/
void stm32_ltdcreset(void)
{
uint32_t regval = getreg32(STM32_RCC_APB2RSTR);
putreg32(regval | RCC_APB2RSTR_LTDCRST, STM32_RCC_APB2RSTR);
putreg32(regval & ~RCC_APB2RSTR_LTDCRST, STM32_RCC_APB2RSTR);
}
/****************************************************************************
* Name: stm32_ltdcinitialize
*
* Description:
* Initialize the ltdc controller
*
* Returned Value:
* OK
*
****************************************************************************/
int stm32_ltdcinitialize(void)
{
int ret = OK;
lcdinfo("Initialize LTDC driver\n");
if (g_initialized == true)
{
return ret;
}
/* Disable the LCD */
stm32_ltdc_enable(false);
lcdinfo("Configuring the LCD controller\n");
/* Configure LCD periphery */
lcdinfo("Configure lcd periphery\n");
stm32_ltdc_periphconfig();
/* Configure interrupts */
lcdinfo("Configure interrupts\n");
stm32_ltdc_irqconfig();
/* Configure global ltdc register */
lcdinfo("Configure global register\n");
stm32_ltdc_globalconfig();
#ifdef CONFIG_STM32_DMA2D
/* Initialize the dma2d controller */
ret = stm32_dma2dinitialize();
if (ret != OK)
{
return ret;
}
/* Bind the dma2d interface */
g_vtable.dma2d = stm32_dma2ddev();
DEBUGASSERT(g_vtable.dma2d != NULL);
#endif
#ifdef CONFIG_STM32_FB_CMAP
/* Cleanup clut */
memset(&g_redclut, 0, STM32_LTDC_NCLUT);
memset(&g_blueclut, 0, STM32_LTDC_NCLUT);
memset(&g_greenclut, 0, STM32_LTDC_NCLUT);
# ifdef CONFIG_STM32_FB_TRANSPARENCY
memset(&g_transpclut, 0, STM32_LTDC_NCLUT);
# endif
#endif /* CONFIG_STM32_FB_CMAP */
/* 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
#ifdef CONFIG_STM32_DMA2D
stm32_ltdc_dma2dlinit();
#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_ltdc_enable(true);
/* Set initialized state */
g_initialized = true;
return ret;
}
/****************************************************************************
* 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 &g_vtable.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_ltdc_enable(false);
/* Set initialized state */
g_initialized = false;
}
/****************************************************************************
* Name: stm32_lcd_backlight
*
* Description:
* Provide this interface to turn the backlight on and off.
*
* Input Parameters:
* 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