nuttx/arch/arm/src/sama5/sam_lcd.c

3105 lines
88 KiB
C

/****************************************************************************
* arch/arm/src/sama5/sam_lcd.c
*
* Copyright (C) 2013-2014, 2016 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* References:
* SAMA5D3 Series Data Sheet
* Atmel NoOS sample code.
*
* The Atmel sample code has a BSD compatible license that requires this
* copyright notice:
*
* Copyright (c) 2012, Atmel Corporation
*
* 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 names NuttX nor Atmel 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/video/fb.h>
#include <nuttx/kmalloc.h>
#include <arch/board/board.h>
#include "up_arch.h"
#include "chip/sam_lcdc.h"
#include "chip/sam_pinmap.h"
#include "sam_pio.h"
#include "sam_periphclks.h"
#include "sam_memories.h"
#include "sam_lcd.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Configuration ************************************************************/
#ifndef CONFIG_SAMA5_LCDC_DEFBACKLIGHT
# define CONFIG_SAMA5_LCDC_DEFBACKLIGHT 0xf0
#endif
#define SAMA5_LCDC_BACKLIGHT_OFF 0x00
#if defined(CONFIG_FB_HWCURSOR) && !defined(CONFIG_SAMA5_LCDC_HCR)
# error CONFIG_FB_HWCURSOR=y but CONFIG_SAMA5_LCDC_HCR=n
#elif !defined(CONFIG_FB_HWCURSOR) && defined(CONFIG_SAMA5_LCDC_HCR)
# error CONFIG_FB_HWCURSOR=n but CONFIG_SAMA5_LCDC_HCR=y
#endif
/* Color/video formats */
#if defined(CONFIG_SAMA5_LCDC_BASE_RGB444)
# define SAMA5_LCDC_BASE_BPP 16 /* 12BPP but must be 16-bit aligned */
# define SAMA5_LCDC_BASE_COLOR_FMT FB_FMT_RGB12_444
#elif defined(CONFIG_SAMA5_LCDC_BASE_ARGB4444)
# define SAMA5_LCDC_BASE_BPP 16
# define SAMA5_LCDC_BASE_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_BASE_RGBA4444)
# define SAMA5_LCDC_BASE_BPP 16
# define SAMA5_LCDC_BASE_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_BASE_RGB565)
# define SAMA5_LCDC_BASE_BPP 16
# define SAMA5_LCDC_BASE_COLOR_FMT FB_FMT_RGB16_565
#elif defined(CONFIG_SAMA5_LCDC_BASE_TRGB1555)
# define SAMA5_LCDC_BASE_BPP 16
# define SAMA5_LCDC_BASE_COLOR_FMT FB_FMT_RGBT16
#elif defined(CONFIG_SAMA5_LCDC_BASE_RGB666)
# define SAMA5_LCDC_BASE_BPP 32 /* 18BPP but must be 32-bit aligned */
# define SAMA5_LCDC_BASE_COLOR_FMT RGB666
#elif defined(CONFIG_SAMA5_LCDC_BASE_RGB666P)
# define SAMA5_LCDC_BASE_BPP 24 /* 18BPP but must be byte aligned */
# define SAMA5_LCDC_BASE_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_BASE_TRGB1666)
# define SAMA5_LCDC_BASE_BPP 32 /* 19BPP but must be 32-bit aligned */
# define SAMA5_LCDC_BASE_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_BASE_TRGBP)
# define SAMA5_LCDC_BASE_BPP 24 /* 19BPP but must be byte aligned */
# define SAMA5_LCDC_BASE_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_BASE_RGB888P)
# define SAMA5_LCDC_BASE_BPP 24
# define SAMA5_LCDC_BASE_COLOR_FMT FB_FMT_RGB24
#elif defined(CONFIG_SAMA5_LCDC_BASE_RGB888)
# define SAMA5_LCDC_BASE_BPP 32
# define SAMA5_LCDC_BASE_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_BASE_TRGB1888)
# define SAMA5_LCDC_BASE_BPP 32 /* 25BPP but must be byte aligned */
# define SAMA5_LCDC_BASE_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_BASE_ARGB8888)
# define SAMA5_LCDC_BASE_BPP 32
# define SAMA5_LCDC_BASE_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_BASE_RGBA8888)
# define SAMA5_LCDC_BASE_BPP 32
# define SAMA5_LCDC_BASE_COLOR_FMT FB_FMT_RGBA32
#else
# error Undefined or unrecognized base color format
#endif
#if defined(CONFIG_SAMA5_LCDC_OVR1_RGB444)
# define SAMA5_LCDC_OVR1_BPP 16 /* 12BPP but must be 16-bit aligned */
# define SAMA5_LCDC_OVR1_COLOR_FMT FB_FMT_RGB12_444
#elif defined(CONFIG_SAMA5_LCDC_OVR1_ARGB4444)
# define SAMA5_LCDC_OVR1_BPP 16
# define SAMA5_LCDC_OVR1_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_OVR1_RGBA4444)
# define SAMA5_LCDC_OVR1_BPP 16
# define SAMA5_LCDC_OVR1_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_OVR1_RGB565)
# define SAMA5_LCDC_OVR1_BPP 16
# define SAMA5_LCDC_OVR1_COLOR_FMT FB_FMT_RGB16_565
#elif defined(CONFIG_SAMA5_LCDC_OVR1_TRGB1555)
# define SAMA5_LCDC_OVR1_BPP 16
# define SAMA5_LCDC_OVR1_COLOR_FMT FB_FMT_RGBT16
#elif defined(CONFIG_SAMA5_LCDC_OVR1_RGB666)
# define SAMA5_LCDC_OVR1_BPP 32 /* 18BPP but must be 32-bit aligned */
# define SAMA5_LCDC_OVR1_COLOR_FMT RGB666
#elif defined(CONFIG_SAMA5_LCDC_OVR1_RGB666P)
# define SAMA5_LCDC_OVR1_BPP 24 /* 18BPP but must be byte aligned */
# define SAMA5_LCDC_OVR1_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_OVR1_TRGB1666)
# define SAMA5_LCDC_OVR1_BPP 32 /* 19BPP but must be 32-bit aligned */
# define SAMA5_LCDC_OVR1_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_OVR1_TRGBP)
# define SAMA5_LCDC_OVR1_BPP 24 /* 19BPP but must be byte aligned */
# define SAMA5_LCDC_OVR1_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_OVR1_RGB888P)
# define SAMA5_LCDC_OVR1_BPP 24
# define SAMA5_LCDC_OVR1_COLOR_FMT FB_FMT_RGB24
#elif defined(CONFIG_SAMA5_LCDC_OVR1_RGB888)
# define SAMA5_LCDC_OVR1_BPP 32
# define SAMA5_LCDC_OVR1_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_OVR1_TRGB1888)
# define SAMA5_LCDC_OVR1_BPP 32 /* 25BPP but must be byte aligned */
# define SAMA5_LCDC_OVR1_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_OVR1_ARGB8888)
# define SAMA5_LCDC_OVR1_BPP 32
# define SAMA5_LCDC_OVR1_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_OVR1_RGBA8888)
# define SAMA5_LCDC_OVR1_BPP 32
# define SAMA5_LCDC_OVR1_COLOR_FMT FB_FMT_RGBA32
#elif defined(CONFIG_SAMA5_LCDC_OVR1)
# error Undefined or unrecognized overlay 1 color format
#endif
#if defined(CONFIG_SAMA5_LCDC_OVR2_RGB444)
# define SAMA5_LCDC_OVR2_BPP 16 /* 12BPP but must be 16-bit aligned */
# define SAMA5_LCDC_OVR2_COLOR_FMT FB_FMT_RGB12_444
#elif defined(CONFIG_SAMA5_LCDC_OVR2_ARGB4444)
# define SAMA5_LCDC_OVR2_BPP 16
# define SAMA5_LCDC_OVR2_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_OVR2_RGBA4444)
# define SAMA5_LCDC_OVR2_BPP 16
# define SAMA5_LCDC_OVR2_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_OVR2_RGB565)
# define SAMA5_LCDC_OVR2_BPP 16
# define SAMA5_LCDC_OVR2_COLOR_FMT FB_FMT_RGB16_565
#elif defined(CONFIG_SAMA5_LCDC_OVR2_TRGB1555)
# define SAMA5_LCDC_OVR2_BPP 16
# define SAMA5_LCDC_OVR2_COLOR_FMT FB_FMT_RGBT16
#elif defined(CONFIG_SAMA5_LCDC_OVR2_RGB666)
# define SAMA5_LCDC_OVR2_BPP 32 /* 18BPP but must be 32-bit aligned */
# define SAMA5_LCDC_OVR2_COLOR_FMT RGB666
#elif defined(CONFIG_SAMA5_LCDC_OVR2_RGB666P)
# define SAMA5_LCDC_OVR2_BPP 24 /* 18BPP but must be byte aligned */
# define SAMA5_LCDC_OVR2_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_OVR2_TRGB1666)
# define SAMA5_LCDC_OVR2_BPP 32 /* 19BPP but must be 32-bit aligned */
# define SAMA5_LCDC_OVR2_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_OVR2_TRGBP)
# define SAMA5_LCDC_OVR2_BPP 24 /* 19BPP but must be byte aligned */
# define SAMA5_LCDC_OVR2_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_OVR2_RGB888P)
# define SAMA5_LCDC_OVR2_BPP 24
# define SAMA5_LCDC_OVR2_COLOR_FMT FB_FMT_RGB24
#elif defined(CONFIG_SAMA5_LCDC_OVR2_RGB888)
# define SAMA5_LCDC_OVR2_BPP 32
# define SAMA5_LCDC_OVR2_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_OVR2_TRGB1888)
# define SAMA5_LCDC_OVR2_BPP 32 /* 25BPP but must be byte aligned */
# define SAMA5_LCDC_OVR2_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_OVR2_ARGB8888)
# define SAMA5_LCDC_OVR2_BPP 32
# define SAMA5_LCDC_OVR2_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_OVR2_RGBA8888)
# define SAMA5_LCDC_OVR2_BPP 32
# define SAMA5_LCDC_OVR2_COLOR_FMT FB_FMT_RGBA32
#elif defined(CONFIG_SAMA5_LCDC_OVR2)
# error Undefined or unrecognized overlay 2 color format
#endif
#if defined(CONFIG_SAMA5_LCDC_HEO_RGB444)
# define SAMA5_LCDC_HEO_BPP 16 /* 12BPP but must be 16-bit aligned */
# define SAMA5_LCDC_HEO_COLOR_FMT FB_FMT_RGB12_444
#elif defined(CONFIG_SAMA5_LCDC_HEO_ARGB4444)
# define SAMA5_LCDC_HEO_BPP 16
# define SAMA5_LCDC_HEO_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_HEO_RGBA4444)
# define SAMA5_LCDC_HEO_BPP 16
# define SAMA5_LCDC_HEO_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_HEO_RGB565)
# define SAMA5_LCDC_HEO_BPP 16
# define SAMA5_LCDC_HEO_COLOR_FMT FB_FMT_RGB16_565
#elif defined(CONFIG_SAMA5_LCDC_HEO_TRGB1555)
# define SAMA5_LCDC_HEO_BPP 16
# define SAMA5_LCDC_HEO_COLOR_FMT FB_FMT_RGBT16
#elif defined(CONFIG_SAMA5_LCDC_HEO_RGB666)
# define SAMA5_LCDC_HEO_BPP 32 /* 18BPP but must be 32-bit aligned */
# define SAMA5_LCDC_HEO_COLOR_FMT RGB666
#elif defined(CONFIG_SAMA5_LCDC_HEO_RGB666P)
# define SAMA5_LCDC_HEO_BPP 24 /* 18BPP but must be byte aligned */
# define SAMA5_LCDC_HEO_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_HEO_TRGB1666)
# define SAMA5_LCDC_HEO_BPP 32 /* 19BPP but must be 32-bit aligned */
# define SAMA5_LCDC_HEO_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_HEO_TRGBP)
# define SAMA5_LCDC_HEO_BPP 24 /* 19BPP but must be byte aligned */
# define SAMA5_LCDC_HEO_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_HEO_RGB888P)
# define SAMA5_LCDC_HEO_BPP 24
# define SAMA5_LCDC_HEO_COLOR_FMT FB_FMT_RGB24
#elif defined(CONFIG_SAMA5_LCDC_HEO_RGB888)
# define SAMA5_LCDC_HEO_BPP 32
# define SAMA5_LCDC_HEO_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_HEO_TRGB1888)
# define SAMA5_LCDC_HEO_BPP 32 /* 25BPP but must be byte aligned */
# define SAMA5_LCDC_HEO_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_HEO_ARGB8888)
# define SAMA5_LCDC_HEO_BPP 32
# define SAMA5_LCDC_HEO_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_HEO_RGBA8888)
# define SAMA5_LCDC_HEO_BPP 32
# define SAMA5_LCDC_HEO_COLOR_FMT FB_FMT_RGBA32
#elif defined(CONFIG_SAMA5_LCDC_HEO)
# error Undefined or unrecognized HEO color format
#endif
#if defined(CONFIG_SAMA5_LCDC_HCR_RGB444)
# define SAMA5_LCDC_HCR_BPP 16 /* 12BPP but must be 16-bit aligned */
# define SAMA5_LCDC_HCR_COLOR_FMT FB_FMT_RGB12_444
#elif defined(CONFIG_SAMA5_LCDC_HCR_ARGB4444)
# define SAMA5_LCDC_HCR_BPP 16
# define SAMA5_LCDC_HCR_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_HCR_RGBA4444)
# define SAMA5_LCDC_HCR_BPP 16
# define SAMA5_LCDC_HCR_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_HCR_RGB565)
# define SAMA5_LCDC_HCR_BPP 16
# define SAMA5_LCDC_HCR_COLOR_FMT FB_FMT_RGB16_565
#elif defined(CONFIG_SAMA5_LCDC_HCR_TRGB1555)
# define SAMA5_LCDC_HCR_BPP 16
# define SAMA5_LCDC_HCR_COLOR_FMT FB_FMT_RGBT16
#elif defined(CONFIG_SAMA5_LCDC_HCR_RGB666)
# define SAMA5_LCDC_HCR_BPP 32 /* 18BPP but must be 32-bit aligned */
# define SAMA5_LCDC_HCR_COLOR_FMT RGB666
#elif defined(CONFIG_SAMA5_LCDC_HCR_RGB666P)
# define SAMA5_LCDC_HCR_BPP 24 /* 18BPP but must be byte aligned */
# define SAMA5_LCDC_HCR_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_HCR_TRGB1666)
# define SAMA5_LCDC_HCR_BPP 32 /* 19BPP but must be 32-bit aligned */
# define SAMA5_LCDC_HCR_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_HCR_TRGBP)
# define SAMA5_LCDC_HCR_BPP 24 /* 19BPP but must be byte aligned */
# define SAMA5_LCDC_HCR_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_HCR_RGB888P)
# define SAMA5_LCDC_HCR_BPP 24
# define SAMA5_LCDC_HCR_COLOR_FMT FB_FMT_RGB24
#elif defined(CONFIG_SAMA5_LCDC_HCR_RGB888)
# define SAMA5_LCDC_HCR_BPP 32
# define SAMA5_LCDC_HCR_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_HCR_TRGB1888)
# define SAMA5_LCDC_HCR_BPP 32 /* 25BPP but must be byte aligned */
# define SAMA5_LCDC_HCR_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_HCR_ARGB8888)
# define SAMA5_LCDC_HCR_BPP 32
# define SAMA5_LCDC_HCR_COLOR_FMT ??? /* No color format definition */
#elif defined(CONFIG_SAMA5_LCDC_HCR_RGBA8888)
# define SAMA5_LCDC_HCR_BPP 32
# define SAMA5_LCDC_HCR_COLOR_FMT FB_FMT_RGBA32
#elif defined(CONFIG_SAMA5_LCDC_HCR)
# error Undefined or unrecognized cursor color format
#endif
/* Framebuffer sizes in bytes */
#ifndef BOARD_LCDC_WIDTH
# error BOARD_LCDC_WIDTH must be defined in the board.h header file
#endif
#ifndef BOARD_LCDC_HEIGHT
# error BOARD_LCDC_HEIGHT must be defined in the board.h header file
#endif
#if SAMA5_LCDC_BASE_BPP == 16
# define SAMA5_BASE_STRIDE ((BOARD_LCDC_WIDTH * 16 + 7) / 8)
#elif SAMA5_LCDC_BASE_BPP == 24
# define SAMA5_BASE_STRIDE ((BOARD_LCDC_WIDTH * 24 + 7) / 8)
#elif SAMA5_LCDC_BASE_BPP == 32
# define SAMA5_BASE_STRIDE ((BOARD_LCDC_WIDTH * 32 + 7) / 8)
#else
# error Undefined or unrecognized base resolution
#endif
#define SAMA5_BASE_FBSIZE (SAMA5_BASE_STRIDE * BOARD_LCDC_HEIGHT)
#ifdef CONFIG_SAMA5_LCDC_OVR1
# ifndef CONFIG_SAMA5_LCDC_OVR1_MAXWIDTH
# define CONFIG_SAMA5_LCDC_OVR1_MAXWIDTH BOARD_LCDC_WIDTH
# endif
# if CONFIG_SAMA5_LCDC_OVR1_MAXWIDTH > BOARD_LCDC_WIDTH
# error Width of overlay 1 exceeds the width of the display
# endif
# ifndef CONFIG_SAMA5_LCDC_OVR1_MAXHEIGHT
# define CONFIG_SAMA5_LCDC_OVR1_MAXHEIGHT BOARD_LCDC_HEIGHT
# endif
# if CONFIG_SAMA5_LCDC_OVR1_MAXHEIGHT > BOARD_LCDC_HEIGHT
# error Height of overlay 1 exceeds the height of the display
# endif
# if SAMA5_LCDC_OVR1_BPP == 16
# define SAMA5_OVR1_STRIDE ((CONFIG_SAMA5_LCDC_OVR1_MAXWIDTH * 16 + 7) / 8)
# elif SAMA5_LCDC_OVR1_BPP == 24
# define SAMA5_OVR1_STRIDE ((CONFIG_SAMA5_LCDC_OVR1_MAXWIDTH * 24 + 7) / 8)
# elif SAMA5_LCDC_OVR1_BPP == 32
# define SAMA5_OVR1_STRIDE ((CONFIG_SAMA5_LCDC_OVR1_MAXWIDTH * 32 + 7) / 8)
# elif defined(CONFIG_SAMA5_LCDC_OVR1)
# error Undefined or unrecognized overlay 1 color resolution
# endif
# define SAMA5_OVR1_FBSIZE (SAMA5_OVR1_STRIDE * CONFIG_SAMA5_LCDC_OVR1_MAXHEIGHT)
#else
# define SAMA5_OVR1_FBSIZE (0)
#endif
#ifdef CONFIG_SAMA5_LCDC_OVR2
# ifndef CONFIG_SAMA5_LCDC_OVR2_MAXWIDTH
# define CONFIG_SAMA5_LCDC_OVR2_MAXWIDTH BOARD_LCDC_WIDTH
# endif
# if CONFIG_SAMA5_LCDC_OVR2_MAXWIDTH > BOARD_LCDC_WIDTH
# error Width of overlay 2 exceeds the width of the display
# endif
# ifndef CONFIG_SAMA5_LCDC_OVR2_MAXHEIGHT
# define CONFIG_SAMA5_LCDC_OVR2_MAXHEIGHT BOARD_LCDC_HEIGHT
# endif
# if CONFIG_SAMA5_LCDC_OVR2_MAXHEIGHT > BOARD_LCDC_HEIGHT
# error Height of overlay 2 exceeds the height of the display
# endif
# if SAMA5_LCDC_OVR2_BPP == 16
# define SAMA5_OVR2_STRIDE ((CONFIG_SAMA5_LCDC_OVR2_MAXWIDTH * 16 + 7) / 8)
# elif SAMA5_LCDC_OVR2_BPP == 24
# define SAMA5_OVR2_STRIDE ((CONFIG_SAMA5_LCDC_OVR2_MAXWIDTH * 24 + 7) / 8)
# elif SAMA5_LCDC_OVR2_BPP == 32
# define SAMA5_OVR2_STRIDE ((CONFIG_SAMA5_LCDC_OVR2_MAXWIDTH * 32 + 7) / 8)
# elif defined(CONFIG_SAMA5_LCDC_OVR2)
# error Undefined or unrecognized overlay 2 color resolution
# endif
# define SAMA5_OVR2_FBSIZE (SAMA5_OVR2_STRIDE * CONFIG_SAMA5_LCDC_OVR2_MAXHEIGHT)
#else
# define SAMA5_OVR2_FBSIZE (0)
#endif
#ifdef CONFIG_SAMA5_LCDC_HEO
# ifndef CONFIG_SAMA5_LCDC_HEO_MAXWIDTH
# define CONFIG_SAMA5_LCDC_HEO_MAXWIDTH BOARD_LCDC_WIDTH
# endif
# if CONFIG_SAMA5_LCDC_HEO_MAXWIDTH > BOARD_LCDC_WIDTH
# error Width of HEO exceeds the width of the display
# endif
# ifndef CONFIG_SAMA5_LCDC_HEO_MAXHEIGHT
# define CONFIG_SAMA5_LCDC_HEO_MAXHEIGHT BOARD_LCDC_HEIGHT
# endif
# if CONFIG_SAMA5_LCDC_HEO_MAXHEIGHT > BOARD_LCDC_HEIGHT
# error Height of HEO exceeds the height of the display
# endif
# if SAMA5_LCDC_HEO_BPP == 16
# define SAMA5_HEO_STRIDE ((CONFIG_SAMA5_LCDC_HEO_MAXWIDTH * 16 + 7) / 8)
# elif SAMA5_LCDC_HEO_BPP == 24
# define SAMA5_HEO_STRIDE ((CONFIG_SAMA5_LCDC_HEO_MAXWIDTH * 24 + 7) / 8)
# elif SAMA5_LCDC_HEO_BPP == 32
# define SAMA5_HEO_STRIDE ((CONFIG_SAMA5_LCDC_HEO_MAXWIDTH * 32 + 7) / 8)
# elif defined(CONFIG_SAMA5_LCDC_HEO)
# error Undefined or unrecognized HEO color resolution
# endif
# define SAMA5_HEO_FBSIZE (SAMA5_HEO_STRIDE * CONFIG_SAMA5_LCDC_HEO_MAXHEIGHT)
#else
# define SAMA5_HEO_FBSIZE (0)
#endif
#ifdef CONFIG_SAMA5_LCDC_HCR
# ifndef CONFIG_SAMA5_LCDC_HCR_MAXWIDTH
# define CONFIG_SAMA5_LCDC_HCR_MAXWIDTH BOARD_LCDC_WIDTH
# endif
# if CONFIG_SAMA5_LCDC_HCR_MAXWIDTH > BOARD_LCDC_WIDTH
# error Width of the hardware cursor exceeds the width of the display
# endif
# ifndef CONFIG_SAMA5_LCDC_HCR_MAXHEIGHT
# define CONFIG_SAMA5_LCDC_HCR_MAXHEIGHT BOARD_LCDC_HEIGHT
# endif
# if CONFIG_SAMA5_LCDC_HCR_MAXHEIGHT > BOARD_LCDC_HEIGHT
# error Height of the hardware cursor exceeds the height of the display
# endif
# if SAMA5_LCDC_HCR_BPP == 16
# define SAMA5_HCR_STRIDE ((CONFIG_SAMA5_LCDC_HCR_MAXWIDTH * 16 + 7) / 8)
# elif SAMA5_LCDC_HCR_BPP == 24
# define SAMA5_HCR_STRIDE ((CONFIG_SAMA5_LCDC_HCR_MAXWIDTH * 24 + 7) / 8)
# elif SAMA5_LCDC_HCR_BPP == 32
# define SAMA5_HCR_STRIDE ((CONFIG_SAMA5_LCDC_HCR_MAXWIDTH * 32 + 7) / 8)
# elif defined(CONFIG_SAMA5_LCDC_HCR)
# error Undefined or unrecognized cursor color resolution
# endif
# define SAMA5_HCR_FBSIZE (SAMA5_HCR_STRIDE * CONFIG_SAMA5_LCDC_HCR_MAXHEIGHT)
#else
# define SAMA5_HCR_FBSIZE (0)
#endif
/* Total memory used for framebuffers */
#define SAMA5_TOTAL_FBSIZE (SAMA5_BASE_FBSIZE + SAMA5_OVR1_FBSIZE + \
SAMA5_OVR2_FBSIZE + SAMA5_HEO_FBSIZE + \
SAMA5_HCR_FBSIZE)
/* Are size, position, and pixel stride support needed? */
#undef SAMA5_HAVE_POSITION /* The base layer has none of these */
#undef SAMA5_HAVE_SIZE
#undef SAMA5_HAVE_PSTRIDE
#if defined(CONFIG_SAMA5_LCDC_OVR1) || defined(CONFIG_SAMA5_LCDC_OVR2) || \
defined(CONFIG_SAMA5_LCDC_HEO)
# define SAMA5_HAVE_POSITION 1
# define SAMA5_HAVE_SIZE 1
# define SAMA5_HAVE_PSTRIDE 1
#elif defined(CONFIG_SAMA5_LCDC_HCR)
# define SAMA5_HAVE_POSITION 1
# define SAMA5_HAVE_SIZE 1
#endif
/* Debug */
#ifndef CONFIG_DEBUG
# undef CONFIG_SAMA5_LCDC_REGDEBUG
#endif
/* LCDC flags */
#define LCDC_FLAG_BOTTOMUP (1 << 0) /* Rend bottom-up */
#define LCDC_FLAG_RIGHTLEFT (1 << 1) /* Rend right-to-left */
/* Preallocated LCDC DMA structures and framebuffers */
/* Base layer */
#define SAMA5_LCDC_BASE_DSCR (CONFIG_SAMA5_LCDC_FB_VBASE+0)
/* Overlay 1/2 Layers */
#define SAMA5_LCDC_OVR2_DSCR (CONFIG_SAMA5_LCDC_FB_VBASE+SIZEOF_SAM_DSCR_S)
#define SAMA5_LCDC_OVR1_DSCR (CONFIG_SAMA5_LCDC_FB_VBASE+2*SIZEOF_SAM_DSCR_S)
/* High End Overlay (HEO) Layer */
#define SAMA5_LCDC_HEO_DSCR (CONFIG_SAMA5_LCDC_FB_VBASE+3*SIZEOF_SAM_DSCR_S)
/* Hardware cursor (HRC) Layer */
#define SAMA5_LCDC_HCR_DSCR (CONFIG_SAMA5_LCDC_FB_VBASE+6*SIZEOF_SAM_DSCR_S)
#define SAMA5_LCDC_DSCR_SIZE (7*SIZEOF_SAM_DSCR_S)
#define SAMA5_LCDC_DSCR_END (CONFIG_SAMA5_LCDC_FB_VBASE+SAMA5_LCDC_DSCR_SIZE)
/* 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 SAMA5_LCDC_BUFFER_SIZE (CONFIG_SAMA5_LCDC_FB_SIZE-SAMA5_LCDC_DSCR_SIZE)
#define SAMA5_LCDC_BUFFER_FREE (SAMA5_LCDC_BUFFER_SIZE-SAMA5_TOTAL_FBSIZE)
#define SAMA5_LCDC_BUFFER_START (SAMA5_LCDC_DSCR_END + SAMA5_LCDC_BUFFER_FREE/2)
#if SAMA5_LCDC_BUFFER_FREE < 0
# error "SAMA5_LCDC_BUFFER_SIZE not large enough for frame buffers"
#endif
/* Base layer frame buffer */
#define SAMA5_LCDC_BUFFER_BASE SAMA5_LCDC_BUFFER_START
#define SAMA5_LCDC_ENDBUF_BASE (SAMA5_LCDC_BUFFER_BASE + SAMA5_BASE_FBSIZE)
#ifdef CONFIG_SAMA5_LCDC_OVR1
# define SAMA5_LCDC_BUFFER_OVR1 SAMA5_LCDC_ENDBUF_BASE
# define SAMA5_LCDC_ENDBUF_OVR1 (SAMA5_LCDC_BUFFER_OVR1 + SAMA5_OVR1_FBSIZE)
#else
# define SAMA5_LCDC_ENDBUF_OVR1 SAMA5_LCDC_ENDBUF_BASE
#endif
#ifdef CONFIG_SAMA5_LCDC_OVR2
# define SAMA5_LCDC_BUFFER_OVR2 SAMA5_LCDC_ENDBUF_OVR1
# define SAMA5_LCDC_ENDBUF_OVR2 (SAMA5_LCDC_BUFFER_OVR2 + SAMA5_OVR2_FBSIZE)
#else
# define SAMA5_LCDC_ENDBUF_OVR2 SAMA5_LCDC_ENDBUF_OVR1
#endif
#ifdef CONFIG_SAMA5_LCDC_HEO
# define SAMA5_LCDC_BUFFER_HEO SAMA5_LCDC_ENDBUF_OVR2
# define SAMA5_LCDC_ENDBUF_HEO (SAMA5_LCDC_BUFFER_HEO + SAMA5_HEO_FBSIZE)
#else
# define SAMA5_LCDC_ENDBUF_HEO SAMA5_LCDC_ENDBUF_OVR2
#endif
#ifdef CONFIG_SAMA5_LCDC_HCR
# define SAMA5_LCDC_BUFFER_HCR SAMA5_LCDC_ENDBUF_HEO
# define SAMA5_LCDC_ENDBUF_HCR (SAMA5_LCDC_BUFFER_HCR + SAMA5_HCR_FBSIZE)
#else
# define SAMA5_LCDC_ENDBUF_HCR SAMA5_LCDC_ENDBUF_HEO
#endif
/* Layer helpers */
#ifdef SAMA5_HAVE_LCDC_HCRCH
# define LCDC_NLAYERS 5
#else
# define LCDC_NLAYERS 4
#endif
#define LAYER(i) g_lcdc.layer[i]
#define LAYER_BASE g_lcdc.layer[LCDC_LAYER_BASE]
#define LAYER_OVR1 g_lcdc.layer[LCDC_LAYER_OVR1]
#define LAYER_OVR2 g_lcdc.layer[LCDC_LAYER_OVR2]
#define LAYER_HEO g_lcdc.layer[LCDC_LAYER_HEO]
#ifdef SAMA5_HAVE_LCDC_HCRCH
# define LAYER_HCR g_lcdc.layer[LCDC_LAYER_HCR]
#endif
/****************************************************************************
* Private Types
****************************************************************************/
/* This enumeration names each layer supported by the hardware */
enum sam_layer_e
{
LCDC_LAYER_BASE = 0, /* LCD base layer, display fixed size image */
LCDC_LAYER_OVR1, /* LCD Overlay 1 */
LCDC_LAYER_OVR2, /* LCD Overlay 2 */
LCDC_LAYER_HEO /* LCD HighEndOverlay, support resize */
#ifdef SAMA5_HAVE_LCDC_HCRCH
, LCDC_LAYER_HCR /* LCD Cursor, max size 128x128 */
#endif
};
/* Possible rotations supported by all layers */
enum sam_rotation_e
{
LCDC_ROT_0 = 0, /* LCD base layer, display fixed size image */
LCDC_ROT_90, /* LCD Overlay 1 */
LCDC_ROT_180, /* LCD Overlay 2 */
LCDC_ROT_270 /* LCD HighEndOverlay, support resize */
};
/* LCDC General Layer information */
struct sam_layer_s
{
/* Descriptors and buffering */
struct sam_dscr_s *dscr; /* DMA descriptor(s) */
uint8_t *framebuffer; /* DMA framebuffer memory */
uint8_t lid; /* Layer ID (see enum sam_layer_e) */
/* Orientation information */
uint8_t rotation; /* See enum_rotation_e */
uint8_t flags; /* See LDCD_FLAG_* definitions */
/* Color information */
uint8_t bpp; /* Bits per pixel */
#ifdef CONFIG_FB_CMAP
uint8_t offset; /* Offset to first value entry in CLUT */
uint8_t nclut; /* Number of colors in the CLUT */
#endif
};
/* This structure provides the overall state of the LCDC */
struct sam_lcdc_s
{
/* Layer information */
struct sam_layer_s layer[LCDC_NLAYERS];
#ifdef CONFIG_FB_HWCURSOR
struct fb_cursorpos_s cpos; /* Current cursor position */
#ifdef CONFIG_FB_HWCURSORSIZE
struct fb_cursorsize_s csize; /* Current cursor size */
#endif
#endif
/* Debug stuff */
#ifdef CONFIG_SAMA5_LCDC_REGDEBUG
bool wrlast; /* True: Last access was a write */
uintptr_t addrlast; /* Last address accessed */
uint32_t vallast; /* Last value read or written */
int ntimes; /* Number of consecutive accesses */
#endif
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Register operations ******************************************************/
#if defined(CONFIG_SAMA5_LCDC_REGDEBUG) && defined(CONFIG_DEBUG)
static bool sam_checkreg(bool wr, uint32_t regval, uintptr_t address);
static uint32_t sam_getreg(uintptr_t addr);
static void sam_putreg(uintptr_t addr, uint32_t val);
#else
# define sam_getreg(addr) getreg32(addr)
# define sam_putreg(addr,val) putreg32(val,addr)
#endif
static void sam_wait_lcdstatus(uint32_t mask, uint32_t value);
/* Frame buffer interface ***************************************************/
/* Get information about the video controller configuration and the
* configuration of each color plane.
*/
static int sam_base_getvideoinfo(struct fb_vtable_s *vtable,
struct fb_videoinfo_s *vinfo);
static int sam_base_getplaneinfo(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 CONFIG_FB_CMAP
static int sam_base_getcmap(struct fb_vtable_s *vtable,
struct fb_cmap_s *cmap);
static int sam_base_putcmap(struct fb_vtable_s *vtable,
const struct fb_cmap_s *cmap);
#endif
/* The following is provided only if the video hardware supports a hardware
* cursor
*/
#ifdef CONFIG_FB_HWCURSOR
static int sam_hcr_getcursor(struct fb_vtable_s *vtable,
struct fb_cursorattrib_s *attrib);
static int sam_hcr_setcursor(struct fb_vtable_s *vtable,
struct fb_setcursor_s *setttings);
#endif
/* Initialization ***********************************************************/
static void sam_dmasetup(int lid, struct sam_dscr_s *dscr, uint8_t *buffer);
#if 0 /* #if defined(SAMA5_HAVE_POSITION) && defined(SAMA5_HAVE_SIZE) -- not used */
static void sam_setposition(int lid, uint32_t x, uint32_t y)
#endif
#ifdef CONFIG_FB_CMAP
static int sam_setclut(struct sam_layer_s *layer,
const struct fb_cmap_s *cmap);
static int sam_getclut(struct sam_layer_s *layer,
struct fb_cmap_s *cmap);
#endif
static void sam_pio_config(void);
static void sam_backlight(uint32_t level);
static void sam_base_disable(void);
static void sam_ovr1_disable(void);
static void sam_ovr2_disable(void);
static void sam_heo_disable(void);
#ifdef SAMA5_HAVE_LCDC_HCRCH
static void sam_hcr_disable(void);
#endif
static void sam_lcd_disable(void);
static void sam_layer_orientation(void);
static void sam_layer_color(void);
static void sam_lcd_enable(void);
static void sam_layer_configure(void);
#ifdef CONFIG_SAMA5_LCDC_HEO
static uint32_t sam_scalefactor(uint32_t wnew, uint32_t oldw);
#endif
static void sam_show_layer(struct sam_layer_s *layer,
uint32_t dispx, uint32_t dispy, uint32_t dispw, uint32_t disph,
uint32_t imgw, uint32_t imgh);
static void sam_show_base(void);
#ifdef CONFIG_SAMA5_LCDC_HCR
static void sam_show_hcr(void);
#endif
/****************************************************************************
* Private Data
****************************************************************************/
/* This structure describes the simulated video controller */
static const struct fb_videoinfo_s g_base_videoinfo =
{
.fmt = SAMA5_LCDC_BASE_COLOR_FMT,
.xres = BOARD_LCDC_WIDTH,
.yres = BOARD_LCDC_HEIGHT,
.nplanes = 1,
};
/* This structure provides the overall state of the LCDC */
static struct sam_lcdc_s g_lcdc;
/* This structure provides the base layer interface */
static const struct fb_vtable_s g_base_vtable =
{
.getvideoinfo = sam_base_getvideoinfo,
.getplaneinfo = sam_base_getplaneinfo,
#ifdef CONFIG_FB_CMAP
.getcmap = sam_base_getcmap,
.putcmap = sam_base_putcmap,
#endif
#ifdef CONFIG_FB_HWCURSOR
.getcursor = sam_hcr_getcursor,
.setcursor = sam_hcr_setcursor,
#endif
};
/* PIO pin configurations */
static pio_pinset_t g_lcdcpins[] =
{
PIO_LCD_DAT0, PIO_LCD_DAT2, PIO_LCD_DAT1, PIO_LCD_DAT3,
PIO_LCD_DAT4, PIO_LCD_DAT5, PIO_LCD_DAT6, PIO_LCD_DAT7,
PIO_LCD_DAT8, PIO_LCD_DAT9, PIO_LCD_DAT10, PIO_LCD_DAT11,
#if BOARD_LCDC_OUTPUT_BPP > 12
PIO_LCD_DAT12, PIO_LCD_DAT13, PIO_LCD_DAT14, PIO_LCD_DAT15,
#if BOARD_LCDC_OUTPUT_BPP > 16
PIO_LCD_DAT16, PIO_LCD_DAT17,
#if BOARD_LCDC_OUTPUT_BPP > 18
PIO_LCD_DAT18, PIO_LCD_DAT19,
PIO_LCD_DAT20, PIO_LCD_DAT21, PIO_LCD_DAT22, PIO_LCD_DAT23,
#endif
#endif
#endif
PIO_LCD_PWM, PIO_LCD_DISP, PIO_LCD_VSYNC, PIO_LCD_HSYNC,
PIO_LCD_PCK, PIO_LCD_DEN
};
#define SAMA5_LCDC_NPINCONFIGS (sizeof(g_lcdcpins) / sizeof(pio_pinset_t))
/* Register lookup tables permit common logic to deal with different
* layers.
*/
static const uintptr_t g_layerenable[LCDC_NLAYERS] =
{
SAM_LCDC_BASECHER,
SAM_LCDC_OVR1CHER,
SAM_LCDC_OVR2CHER,
SAM_LCDC_HEOCHER
#ifdef SAMA5_HAVE_LCDC_HCRCH
, SAM_LCDC_HCRCHER
#endif
};
static const uintptr_t g_layerdisable[LCDC_NLAYERS] =
{
SAM_LCDC_BASECHDR,
SAM_LCDC_OVR1CHDR,
SAM_LCDC_OVR2CHDR,
SAM_LCDC_HEOCHDR
#ifdef SAMA5_HAVE_LCDC_HCRCH
, SAM_LCDC_HCRCHDR
#endif
};
static const uintptr_t g_layerstatus[LCDC_NLAYERS] =
{
SAM_LCDC_BASECHSR,
SAM_LCDC_OVR1CHSR,
SAM_LCDC_OVR2CHSR,
SAM_LCDC_HEOCHSR
#ifdef SAMA5_HAVE_LCDC_HCRCH
, SAM_LCDC_HCRCHSR
#endif
};
static const uintptr_t g_layerblend[LCDC_NLAYERS] =
{
SAM_LCDC_BASECFG4,
SAM_LCDC_OVR1CFG9,
SAM_LCDC_OVR2CFG9,
SAM_LCDC_HEOCFG12
#ifdef SAMA5_HAVE_LCDC_HCRCH
, SAM_LCDC_HCRCFG9
#endif
};
static const uintptr_t g_layerhead[LCDC_NLAYERS] =
{
SAM_LCDC_BASEHEAD,
SAM_LCDC_OVR1HEAD,
SAM_LCDC_OVR2HEAD,
SAM_LCDC_HEOHEAD
#ifdef SAMA5_HAVE_LCDC_HCRCH
, SAM_LCDC_HCRHEAD
#endif
};
static const uintptr_t g_layeraddr[LCDC_NLAYERS] =
{
SAM_LCDC_BASEADDR,
SAM_LCDC_OVR1ADDR,
SAM_LCDC_OVR2ADDR,
SAM_LCDC_HEOADDR
#ifdef SAMA5_HAVE_LCDC_HCRCH
, SAM_LCDC_HCRADDR
#endif
};
static const uintptr_t g_layerctrl[LCDC_NLAYERS] =
{
SAM_LCDC_BASECTRL,
SAM_LCDC_OVR1CTRL,
SAM_LCDC_OVR2CTRL,
SAM_LCDC_HEOCTRL
#ifdef SAMA5_HAVE_LCDC_HCRCH
, SAM_LCDC_HCRCTRL
#endif
};
static const uintptr_t g_layernext[LCDC_NLAYERS] =
{
SAM_LCDC_BASENEXT,
SAM_LCDC_OVR1NEXT,
SAM_LCDC_OVR2NEXT,
SAM_LCDC_HEONEXT
#ifdef SAMA5_HAVE_LCDC_HCRCH
, SAM_LCDC_HCRNEXT
#endif
};
static const uintptr_t g_layercfg[LCDC_NLAYERS] =
{
SAM_LCDC_BASECFG0,
SAM_LCDC_OVR1CFG0,
SAM_LCDC_OVR2CFG0,
SAM_LCDC_HEOCFG0
#ifdef SAMA5_HAVE_LCDC_HCRCH
, SAM_LCDC_HCRCFG0
#endif
};
static const uintptr_t g_layercolor[LCDC_NLAYERS] =
{
SAM_LCDC_BASECFG1,
SAM_LCDC_OVR1CFG1,
SAM_LCDC_OVR2CFG1,
SAM_LCDC_HEOCFG1
#ifdef SAMA5_HAVE_LCDC_HCRCH
, SAM_LCDC_HCRCFG1
#endif
};
#ifdef SAMA5_HAVE_POSITION
static const uintptr_t g_layerpos[LCDC_NLAYERS] =
{
0,
SAM_LCDC_OVR1CFG2,
SAM_LCDC_OVR2CFG2,
SAM_LCDC_HEOCFG2
#ifdef SAMA5_HAVE_LCDC_HCRCH
, SAM_LCDC_HCRCFG2
#endif
};
#endif
#ifdef SAMA5_HAVE_SIZE
static const uintptr_t g_layersize[LCDC_NLAYERS] =
{
0,
SAM_LCDC_OVR1CFG3,
SAM_LCDC_OVR2CFG3,
SAM_LCDC_HEOCFG3
#ifdef SAMA5_HAVE_LCDC_HCRCH
, SAM_LCDC_HCRCFG3
#endif
};
#endif
static const uintptr_t g_layerstride[LCDC_NLAYERS] =
{
SAM_LCDC_BASECFG2,
SAM_LCDC_OVR1CFG4,
SAM_LCDC_OVR2CFG4,
SAM_LCDC_HEOCFG5
#ifdef SAMA5_HAVE_LCDC_HCRCH
, SAM_LCDC_HCRCFG4
#endif
};
#ifdef SAMA5_HAVE_PSTRIDE
static const uintptr_t g_layerpstride[LCDC_NLAYERS] =
{
0, SAM_LCDC_OVR1CFG5, SAM_LCDC_OVR2CFG5, SAM_LCDC_HEOCFG6,
0
};
#endif
#ifdef CONFIG_FB_CMAP
static const uintptr_t g_layerclut[LCDC_NLAYERS] =
{
SAM_LCDC_BASECLUT,
SAM_LCDC_OVR1CLUT,
SAM_LCDC_OVR2CLUT,
SAM_LCDC_HEOCLUT
#ifdef SAMA5_HAVE_LCDC_HCRCH
, SAM_LCDC_HCRCLUT
#endif
};
#endif
/****************************************************************************
* Public Data
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: sam_checkreg
*
* Description:
* Check if the current register access is a duplicate of the preceding.
*
* Input Parameters:
* regval - The value to be written
* address - The address of the register to write to
*
* Returned Value:
* true: This is the first register access of this type.
* flase: This is the same as the preceding register access.
*
****************************************************************************/
#ifdef CONFIG_SAMA5_LCDC_REGDEBUG
static bool sam_checkreg(bool wr, uint32_t regval, uintptr_t address)
{
if (wr == g_lcdc.wrlast && /* Same kind of access? */
regval == g_lcdc.vallast && /* Same value? */
address == g_lcdc.addrlast) /* Same address? */
{
/* Yes, then just keep a count of the number of times we did this. */
g_lcdc.ntimes++;
return false;
}
else
{
/* Did we do the previous operation more than once? */
if (g_lcdc.ntimes > 0)
{
/* Yes... show how many times we did it */
lldbg("...[Repeats %d times]...\n", g_lcdc.ntimes);
}
/* Save information about the new access */
g_lcdc.wrlast = wr;
g_lcdc.vallast = regval;
g_lcdc.addrlast = address;
g_lcdc.ntimes = 0;
}
/* Return true if this is the first time that we have done this operation */
return true;
}
#endif
/****************************************************************************
* Name: sam_getreg
*
* Description:
* Read any 32-bit register using an absolute
*
****************************************************************************/
#ifdef CONFIG_SAMA5_LCDC_REGDEBUG
static uint32_t sam_getreg(uintptr_t address)
{
uint32_t regval = getreg32(address);
if (sam_checkreg(false, regval, address))
{
lldbg("%08x->%08x\n", address, regval);
}
return regval;
}
#endif
/****************************************************************************
* Name: sam_putreg
*
* Description:
* Write to any 32-bit register using an absolute address
*
****************************************************************************/
#ifdef CONFIG_SAMA5_LCDC_REGDEBUG
static void sam_putreg(uintptr_t address, uint32_t regval)
{
if (sam_checkreg(true, regval, address))
{
lldbg("%08x<-%08x\n", address, regval);
}
putreg32(regval, address);
}
#endif
/****************************************************************************
* Name: sam_wait_lcdstatus
*
* Description:
* Wait for the masked set of bits in the LCDC status register to take a
* specific value.
*
****************************************************************************/
static void sam_wait_lcdstatus(uint32_t mask, uint32_t value)
{
while ((sam_getreg(SAM_LCDC_LCDSR) & mask) != value);
}
/****************************************************************************
* Name: sam_base_getvideoinfo
****************************************************************************/
static int sam_base_getvideoinfo(struct fb_vtable_s *vtable,
struct fb_videoinfo_s *vinfo)
{
gvdbg("vtable=%p vinfo=%p\n", vtable, vinfo);
if (vtable && vinfo)
{
memcpy(vinfo, &g_base_videoinfo, sizeof(struct fb_videoinfo_s));
return OK;
}
gdbg("ERROR: Returning EINVAL\n");
return -EINVAL;
}
/****************************************************************************
* Name: sam_base_getplaneinfo
****************************************************************************/
static int sam_base_getplaneinfo(struct fb_vtable_s *vtable, int planeno,
struct fb_planeinfo_s *pinfo)
{
gvdbg("vtable=%p planeno=%d pinfo=%p\n", vtable, planeno, pinfo);
if (vtable && planeno == 0 && pinfo)
{
pinfo->fbmem = (void *)LAYER_BASE.framebuffer;
pinfo->fblen = SAMA5_BASE_FBSIZE;
pinfo->stride = SAMA5_BASE_STRIDE;
pinfo->display = 0;
pinfo->bpp = LAYER_BASE.bpp;
return OK;
}
gdbg("Returning EINVAL\n");
return -EINVAL;
}
/****************************************************************************
* Name: sam_base_getcmap
****************************************************************************/
#ifdef CONFIG_FB_CMAP
static int sam_base_getcmap(struct fb_vtable_s *vtable,
struct fb_cmap_s *cmap)
{
return sam_getclut(&LAYER_BASE, cmap);
}
#endif
/****************************************************************************
* Name: sam_base_putcmap
****************************************************************************/
#ifdef CONFIG_FB_CMAP
static int sam_base_putcmap(struct fb_vtable_s *vtable,
const struct fb_cmap_s *cmap)
{
return sam_setclut(&LAYER_BASE, cmap);
}
#endif
/****************************************************************************
* Name: sam_hcr_getcursor
****************************************************************************/
#ifdef CONFIG_FB_HWCURSOR
static int sam_hcr_getcursor(struct fb_vtable_s *vtable,
struct fb_cursorattrib_s *attrib)
{
gvdbg("vtable=%p attrib=%p\n", vtable, attrib);
if (vtable && attrib)
{
#ifdef CONFIG_FB_HWCURSORIMAGE
attrib->fmt = SAMA5_HCR_COLOR_FMT;
#endif
gvdbg("pos: (x=%d, y=%d)\n", g_lcdc.cpos.x, g_lcdc.cpos.y);
attrib->pos = g_lcdc.cpos;
#ifdef CONFIG_FB_HWCURSORSIZE
attrib->mxsize.h = CONFIG_SAMA5_LCDC_HCR_HEIGHT;
attrib->mxsize.w = CONFIG_SAMA5_LCDC_HCR_WIDTH;
gvdbg("size: (h=%d, w=%d)\n", g_lcdc.csize.h, g_lcdc.csize.w);
attrib->size = g_lcdc.csize;
#endif
return OK;
}
gdbg("Returning EINVAL\n");
return -EINVAL;
}
#endif
/****************************************************************************
* Name: sam_hcr_setcursor
****************************************************************************/
#ifdef CONFIG_FB_HWCURSOR
static int sam_hcr_setcursor(struct fb_vtable_s *vtable,
struct fb_setcursor_s *setttings)
{
gvdbg("vtable=%p setttings=%p\n", vtable, setttings);
if (vtable && setttings)
{
gvdbg("flags: %02x\n", settings->flags);
if ((flags & FB_CUR_SETPOSITION) != 0)
{
g_lcdc.cpos = settings->pos;
gvdbg("pos: (h:%d, w:%d)\n", g_lcdc.cpos.x, g_lcdc.cpos.y);
}
#ifdef CONFIG_FB_HWCURSORSIZE
if ((flags & FB_CUR_SETSIZE) != 0)
{
g_lcdc.csize = settings->size;
gvdbg("size: (h:%d, w:%d)\n", g_lcdc.csize.h, g_lcdc.csize.w);
}
#endif
#ifdef CONFIG_FB_HWCURSORIMAGE
if ((flags & FB_CUR_SETIMAGE) != 0)
{
gvdbg("image: (h:%d, w:%d) @ %p\n",
settings->img.height, settings->img.width,
settings->img.image);
}
#endif
return OK;
}
gdbg("Returning EINVAL\n");
return -EINVAL;
}
#endif
/****************************************************************************
* Name: sam_dmasetup
*
* Description:
* Configure the channel DMA
*
****************************************************************************/
static void sam_dmasetup(int lid, struct sam_dscr_s *dscr, uint8_t *buffer)
{
uintptr_t physbuffer;
uintptr_t physdscr;
/* Get the physical address of the DMA descriptor and the DMA buffer */
physbuffer = sam_physramaddr((uintptr_t)buffer);
physdscr = sam_physramaddr((uintptr_t)dscr);
/* 31.6.2.2 Programming a DMA Channel:
*
* 2. Write the channel descriptor (DSCR) structure in the system memory by
* writing DSCR.CHXADDR Frame base address, DSCR.CHXCTRL channel control
* and DSCR.CHXNEXT next descriptor location.
* 3. If more than one descriptor is expected, the DFETCH field of
* DSCR.CHXCTRL is set to one to enable the descriptor fetch operation.
*/
dscr->addr = (uint32_t)physbuffer;
dscr->ctrl = LCDC_BASECTRL_DFETCH;
dscr->next = (uint32_t)physdscr;
/* Check if the DMA is already active */
if ((sam_getreg(g_layerblend[lid]) & LCDC_BASECFG4_DMA) != 0)
{
/* Yes.. add the new descriptor to the buffer list
* 31.6.2.4 DMA Dynamic Linking of a New Transfer Descriptor:
*
* 2. Write the address of the new structure in the CHXHEAD register.
* 3. Add the new structure to the queue of descriptors by writing one
* to the A2QEN field of the CHXCHER register.
*/
sam_putreg(g_layerhead[lid], physdscr);
sam_putreg(g_layerenable[lid], LCDC_BASECHER_A2Q);
}
else
{
/* 31.6.2.2 Programming a DMA Channel:
*
* 4. Write the DSCR.CHXNEXT register with the address location
* of the descriptor structure and set DFETCH field of the
* DSCR.CHXCTRL register to one.
*/
sam_putreg(g_layeraddr[lid], physbuffer);
sam_putreg(g_layerctrl[lid], LCDC_BASECTRL_DFETCH);
sam_putreg(g_layernext[lid], physdscr);
}
#if defined(CONFIG_DEBUG_GRAPHICS) && defined(CONFIG_DEBUG_VERBOSE)
/* Dump the DMA setup */
gvdbg("DMA descriptor: addr=%08x ctrl=%08x next=%08x\n",
dscr->addr, dscr->ctrl, dscr->next);
gvdbg("DMA registers[%d]: head=%08x addr=%08x ctrl=%08x next=%08x\n",
lid, sam_getreg(g_layerhead[lid]), sam_getreg(g_layeraddr[lid]),
sam_getreg(g_layerctrl[lid]), sam_getreg(g_layernext[lid]));
#endif
}
/****************************************************************************
* Name: sam_setposition
*
* Description:
* Set the new position of a move-able layer (any layer except the base
* layer).
*
****************************************************************************/
#if 0 /* #if defined(SAMA5_HAVE_POSITION) && defined(SAMA5_HAVE_SIZE) -- not used */
static void sam_setposition(int lid, uint32_t x, uint32_t y)
{
uintptr_t regaddr;
uintptr_t regval;
uint32_t h;
uint32_t w;
regaddr = g_layersize[lid];
if (regaddr)
{
/* Get the layer size */
regval = sam_getreg(regaddr);
w = (regval & LCDC_OVR1CFG3_XSIZE_MASK) >> LCDC_OVR1CFG3_XSIZE_SHIFT;
h = (regval & LCDC_OVR1CFG3_YSIZE_MASK) >> LCDC_OVR1CFG3_YSIZE_SHIFT;
/* Clip the position so that the window lies on the physical display */
if (x + w >= BOARD_LCDC_WIDTH)
{
x = BOARD_LCDC_WIDTH - w;
}
if (y + h >= BOARD_LCDC_HEIGHT)
{
y = BOARD_LCDC_HEIGHT - h;
}
/* Set the new position of the layer */
regaddr = g_layerpos[lid];
if (regaddr)
{
sam_putreg(regaddr, LCDC_OVR1CFG2_XPOS(x) | LCDC_OVR1CFG2_YPOS(y));
/* If the channel is enabled, then update the layer */
regaddr = g_layerstatus[lid];
if ((sam_getreg(regaddr) & LCDC_OVR1CHSR_CH) != 0)
{
regaddr = g_layerenable[lid];
sam_putreg(regaddr, LCDC_OVR1CHER_UPDATE);
}
}
}
}
#endif
/****************************************************************************
* Name: sam_setclut
*
* Description:
* Set a range of CLUT values for any layer
*
****************************************************************************/
#ifdef CONFIG_FB_CMAP
static int sam_setclut(struct sam_layer_s *layer,
const struct fb_cmap_s *cmap)
{
uintptr_t regaddr;
uint32_t offset;
uint32_t rgb;
unsigned int len;
unsigned int end;
int i;
gvdbg("layer=%d cmap=%p first=%d len=%d\n",
layer->lid, cmap, cmap->first, cmap->len);
DEBUGASSERT(layer && cmap);
/* Get and verify the range of CLUT entries to modify */
offset = (uint32_t)cmap->first;
len = (unsigned int)cmap->len;
if (offset >= SAM_LCDC_NCLUT)
{
gdbg("ERROR: CLUT offset is out of range: %d\n", offset);
return -EINVAL;
}
if (offset + len > SAM_LCDC_NCLUT)
{
len = SAM_LCDC_NCLUT - offset;
}
/* Update the valid range of CLUT entries */
if (offset < layer->offset)
{
layer->offset = offset;
}
end = offset + len;
if (end > (layer->offset + layer->nclut))
{
layer->nclut = end - layer->offset;
}
/* Get the offset address to the first CLUT entry to modify */
regaddr = g_layerclut[layer->lid] + offset << 2;
/* Then set the number of CLUT entries beginning at this offset */
for (i = 0; i < len; i++)
{
/* Pack the RGB (+transparency?) values as required */
rgb = (uint32_t)cmap->red[i] << LCDC_BASECLUT_RCLUT_SHIFT |
(uint32_t)cmap->green[i] << LCDC_BASECLUT_GCLUT_SHIFT |
(uint32_t)cmap->blue[i] << LCDC_BASECLUT_BCLUT_SHIFT;
#ifdef CONFIG_FB_TRANSPARENCY
if (camp->transp)
{
rgb |= (uint32_t)cmap->transp[i] << LCDC_OVR1CLUT_ACLUT_SHIFT;
}
#endif
/* And write to the CLUT register */
sam_putreg(regaddr, clut[i]);
regaddr += sizeof(uint32_t);
}
return OK;
}
#endif
/****************************************************************************
* Name: sam_getclut
*
* Description:
* Get a range of CLUT values for any layer
*
****************************************************************************/
#ifdef CONFIG_FB_CMAP
static int sam_getclut(struct sam_layer_s *layer,
struct fb_cmap_s *cmap)
{
uintptr_t regaddr;
uintptr_t regval;
int i;
gvdbg("layer=%d cmap=%p first=%d len=%d\n",
layer->lid, cmap, layer->offset, layer->nclut);
DEBUGASSERT(layer && cmap);
/* Return the range of CLUT entries to modify */
cmap->first = layer->offset;
cmap->len = layer->nclut;
/* Get the offset address to the first CLUT entry to modify */
regaddr = g_layerclut[layer->lid] + (uint32_t)cmap->first << 2;
/* Then set the number of CLUT entries beginning at this offset */
for (i = 0; i < (int)cmap->len; i++)
{
/* Read the CLUT entry */
regval = getreg(regaddr);
regaddr += sizeof(uint32_t);
/* Unpack and return the RGB (+transparency?) values as required */
cmap->red[i] = (uint8_t)
(regval & LCDC_BASECLUT_RCLUT_MASK) << LCDC_BASECLUT_RCLUT_SHIFT;
cmap->green[i] = (uint8_t)
(regval & LCDC_BASECLUT_GCLUT_MASK) << LCDC_BASECLUT_GCLUT_SHIFT;
cmap->blue[i] = (uint8_t)
(regval & LCDC_BASECLUT_GCLUT_MASK) << LCDC_BASECLUT_BCLUT_SHIFT;
#ifdef CONFIG_FB_TRANSPARENCY
cmap->transp[i] = (uint8_t)
(regval & LCDC_OVR1CLUT_ACLUT_MASK) << LCDC_OVR1CLUT_ACLUT_SHIFT;
#endif
}
return OK;
}
#endif
/****************************************************************************
* Name: sam_pio_config
*
* Description:
* Configure PIO pins for use with the LCDC
*
****************************************************************************/
static void sam_pio_config(void)
{
int i;
gvdbg("Configuring pins\n");
/* Configure each pin */
for (i = 0; i < SAMA5_LCDC_NPINCONFIGS; i++)
{
sam_configpio(g_lcdcpins[i]);
}
}
/****************************************************************************
* Name: sam_backlight
*
* Description:
* Set the backlight level
*
****************************************************************************/
static void sam_backlight(uint32_t level)
{
uint32_t regval;
/* Are we turning the backlight off? */
if (level == SAMA5_LCDC_BACKLIGHT_OFF)
{
/* Disable the backlight */
sam_putreg(SAM_LCDC_LCDDIS, LCDC_LCDDIS_PWM);
/* And wait for the PWM to be disabled */
sam_wait_lcdstatus(LCDC_LCDSR_SIP | LCDC_LCDSR_PWM, 0);
}
#ifdef CONFIG_SAMA5_LCDC_BACKLIGHT
else
{
/* Set the backight level */
regval = sam_getreg(SAM_LCDC_LCDCFG6);
regval &= ~LCDC_LCDCFG6_PWMCVAL_MASK;
regval |= LCDC_LCDCFG6_PWMCVAL(level);
sam_putreg(SAM_LCDC_LCDCFG6, regval);
/* Enable the backlight */
sam_putreg(SAM_LCDC_LCDEN, LCDC_LCDEN_PWM);
}
#endif
}
/****************************************************************************
* Name: sam_base_disable
*
* Description:
* Disable the base layer
*
****************************************************************************/
static void sam_base_disable(void)
{
struct sam_dscr_s *dscr;
uintptr_t physaddr;
uint32_t regval;
dscr = LAYER_BASE.dscr;
DEBUGASSERT(dscr);
/* 1. Clear the DFETCH bit in the DSCR.CHXCTRL field of the DSCR structure
* will disable the channel at the end of the frame.
*/
regval = sam_getreg(SAM_LCDC_BASECTRL);
regval &= ~LCDC_BASECTRL_DFETCH;
sam_putreg(SAM_LCDC_BASECTRL, regval);
/* 2. Set the DSCR.CHXNEXT field of the DSCR structure will disable the
* channel at the end of the frame.
*/
physaddr = sam_physramaddr((uintptr_t)dscr);
dscr->next = physaddr;
sam_putreg(SAM_LCDC_BASENEXT, physaddr);
/* 3. Writing one to the CHDIS field of the CHXCHDR register will disable
* the channel at the end of the frame.
*/
sam_putreg(SAM_LCDC_BASECHDR, LCDC_BASECHDR_CH);
/* 4. Writing one to the CHRST field of the CHXCHDR register will disable
* the channel immediately. This may occur in the middle of the image.
*/
/* 5. Poll CHSR field in the CHXCHSR register until the channel is
* successfully disabled.
*/
while ((sam_getreg(SAM_LCDC_BASECHSR) & LCDC_BASECHSR_CH) != 0);
}
/****************************************************************************
* Name: sam_ovr1_disable
*
* Description:
* Disable the overlay 1 layer
*
****************************************************************************/
static void sam_ovr1_disable(void)
{
struct sam_dscr_s *dscr;
uintptr_t physaddr;
uint32_t regval;
dscr = LAYER_OVR1.dscr;
DEBUGASSERT(dscr);
/* 1. Clear the DFETCH bit in the DSCR.CHXCTRL field of the DSCR structure
* will disable the channel at the end of the frame.
*/
dscr->ctrl &= ~LCDC_OVR1CTRL_DFETCH;
regval = sam_getreg(SAM_LCDC_OVR1CTRL);
regval &= ~LCDC_OVR1CTRL_DFETCH;
sam_putreg(SAM_LCDC_OVR1CTRL, regval);
/* 2. Set the DSCR.CHXNEXT field of the DSCR structure will disable the
* channel at the end of the frame.
*/
physaddr = sam_physramaddr((uintptr_t)dscr);
dscr->next = physaddr;
sam_putreg(SAM_LCDC_OVR1NEXT, physaddr);
/* 3. Writing one to the CHDIS field of the CHXCHDR register will disable
* the channel at the end of the frame.
*/
sam_putreg(SAM_LCDC_OVR1CHDR, LCDC_OVR1CHDR_CH);
/* 4. Writing one to the CHRST field of the CHXCHDR register will disable
* the channel immediately. This may occur in the middle of the image.
*/
/* 5. Poll CHSR field in the CHXCHSR register until the channel is
* successfully disabled.
*/
while ((sam_getreg(SAM_LCDC_OVR1CHSR) & LCDC_OVR1CHSR_CH) != 0);
}
/****************************************************************************
* Name: sam_ovr2_disable
*
* Description:
* Disable the overlay 2 layer
*
****************************************************************************/
static void sam_ovr2_disable(void)
{
struct sam_dscr_s *dscr;
uintptr_t physaddr;
uint32_t regval;
dscr = LAYER_OVR2.dscr;
DEBUGASSERT(dscr);
/* 1. Clear the DFETCH bit in the DSCR.CHXCTRL field of the DSCR structure
* will disable the channel at the end of the frame.
*/
dscr->ctrl &= ~LCDC_OVR2CTRL_DFETCH;
regval = sam_getreg(SAM_LCDC_OVR2CTRL);
regval &= ~LCDC_OVR2CTRL_DFETCH;
sam_putreg(SAM_LCDC_OVR2CTRL, regval);
/* 2. Set the DSCR.CHXNEXT field of the DSCR structure will disable the
* channel at the end of the frame.
*/
physaddr = sam_physramaddr((uintptr_t)dscr);
dscr->next = physaddr;
sam_putreg(SAM_LCDC_OVR2NEXT, physaddr);
/* 3. Writing one to the CHDIS field of the CHXCHDR register will disable
* the channel at the end of the frame.
*/
sam_putreg(SAM_LCDC_OVR2CHDR, LCDC_OVR2CHDR_CH);
/* 4. Writing one to the CHRST field of the CHXCHDR register will disable
* the channel immediately. This may occur in the middle of the image.
*/
/* 5. Poll CHSR field in the CHXCHSR register until the channel is
* successfully disabled.
*/
while ((sam_getreg(SAM_LCDC_OVR2CHSR) & LCDC_OVR2CHSR_CH) != 0);
}
/****************************************************************************
* Name: sam_heo_disable
*
* Description:
* Disable the High End Overlay (HEO) layer
*
****************************************************************************/
static void sam_heo_disable(void)
{
struct sam_dscr_s *dscr;
uintptr_t physaddr;
uint32_t regval;
dscr = LAYER_HEO.dscr;
DEBUGASSERT(dscr);
/* 1. Clear the DFETCH bit in the DSCR.CHXCTRL field of the DSCR structure
* will disable the channel at the end of the frame.
*/
dscr[0].ctrl &= ~LCDC_HEOCTRL_DFETCH;
dscr[1].ctrl &= ~LCDC_HEOUCTRL_DFETCH;
dscr[2].ctrl &= ~LCDC_HEOVCTRL_DFETCH;
regval = sam_getreg(SAM_LCDC_HEOCTRL);
regval &= ~LCDC_HEOCTRL_DFETCH;
sam_putreg(SAM_LCDC_HEOCTRL, regval);
regval = sam_getreg(SAM_LCDC_HEOUCTRL);
regval &= ~LCDC_HEOUCTRL_DFETCH;
sam_putreg(SAM_LCDC_HEOUCTRL, regval);
regval = sam_getreg(SAM_LCDC_HEOVCTRL);
regval &= ~LCDC_HEOVCTRL_DFETCH;
sam_putreg(SAM_LCDC_HEOVCTRL, regval);
/* 2. Set the DSCR.CHXNEXT field of the DSCR structure will disable the
* channel at the end of the frame.
*/
physaddr = sam_physramaddr((uintptr_t)&dscr[0]);
dscr[0].next = physaddr;
sam_putreg(SAM_LCDC_HEONEXT, physaddr);
physaddr = sam_physramaddr((uintptr_t)&dscr[1]);
dscr[1].next = physaddr;
sam_putreg(SAM_LCDC_HEOUNEXT, physaddr);
physaddr = sam_physramaddr((uintptr_t)&dscr[2]);
dscr[2].next = physaddr;
sam_putreg(SAM_LCDC_HEOVNEXT, physaddr);
/* 3. Writing one to the CHDIS field of the CHXCHDR register will disable
* the channel at the end of the frame.
*/
sam_putreg(SAM_LCDC_HEOCHDR, LCDC_HEOCHDR_CH);
/* 4. Writing one to the CHRST field of the CHXCHDR register will disable
* the channel immediately. This may occur in the middle of the image.
*/
/* 5. Poll CHSR field in the CHXCHSR register until the channel is
* successfully disabled.
*/
while ((sam_getreg(SAM_LCDC_HEOCHSR) & LCDC_HEOCHSR_CH) != 0);
}
/****************************************************************************
* Name: sam_hcr_disable
*
* Description:
* Disable the Hardware Cursor Channel (HCR) layer
*
****************************************************************************/
#ifdef SAMA5_HAVE_LCDC_HCRCH
static void sam_hcr_disable(void)
{
struct sam_dscr_s *dscr;
uintptr_t physaddr;
uint32_t regval;
dscr = LAYER_HCR.dscr;
DEBUGASSERT(dscr);
/* 1. Clear the DFETCH bit in the DSCR.CHXCTRL field of the DSCR structure
* will disable the channel at the end of the frame.
*/
dscr->ctrl &= ~LCDC_HCRCTRL_DFETCH;
regval = sam_getreg(SAM_LCDC_HCRCTRL);
regval &= ~LCDC_HCRCTRL_DFETCH;
sam_putreg(SAM_LCDC_HCRCTRL, regval);
/* 2. Set the DSCR.CHXNEXT field of the DSCR structure will disable the
* channel at the end of the frame.
*/
physaddr = sam_physramaddr((uintptr_t)dscr);
dscr->next = physaddr;
sam_putreg(SAM_LCDC_HCRNEXT, physaddr);
/* 3. Writing one to the CHDIS field of the CHXCHDR register will disable
* the channel at the end of the frame.
*/
sam_putreg(SAM_LCDC_HCRCHDR, LCDC_HCRCHDR_CH);
/* 4. Writing one to the CHRST field of the CHXCHDR register will disable
* the channel immediately. This may occur in the middle of the image.
*/
/* 5. Poll CHSR field in the CHXCHSR register until the channel is
* successfully disabled.
*/
while ((sam_getreg(SAM_LCDC_HCRCHSR) & LCDC_HCRCHSR_CH) != 0);
}
#endif
/****************************************************************************
* Name: sam_lcd_disable
*
* Description:
* Disable the LCD peripheral
*
****************************************************************************/
static void sam_lcd_disable(void)
{
/* Disable layers */
sam_base_disable();
sam_ovr1_disable();
sam_ovr2_disable();
sam_heo_disable();
#ifdef SAMA5_HAVE_LCDC_HCRCH
sam_hcr_disable();
#endif
/* Disable DMA path */
sam_putreg(SAM_LCDC_BASECFG4, 0);
/* Turn off the back light */
sam_backlight(SAMA5_LCDC_BACKLIGHT_OFF);
/* Timing Engine Power Down Software Operation */
/* 1. Disable the DISP signal writing DISPDIS field of the LCDC_LCDDIS
* register.
*/
sam_putreg(SAM_LCDC_LCDDIS, LCDC_LCDDIS_DISP);
/* 2. Poll DISPSTS field of the LCDC_LCDSR register to verify that the DISP
* is no longer activated.
*/
sam_wait_lcdstatus(LCDC_LCDSR_SIP | LCDC_LCDSR_DISP, 0);
/* 3. Disable the hsync and vsync signals by writing one to SYNCDIS field of
* the LCDC_LCDDIS register.
*/
sam_putreg(SAM_LCDC_LCDDIS, LCDC_LCDDIS_SYNC);
/* 4. Poll LCDSTS field of the LCDC_LCDSR register to check that the
* synchronization is off.
*/
sam_wait_lcdstatus(LCDC_LCDSR_SIP | LCDC_LCDSR_LCD, 0);
/* 5. Disable the Pixel clock by writing one in the CLKDIS field of the
* LCDC_LCDDIS register.
*/
sam_putreg(SAM_LCDC_LCDDIS, LCDC_LCDDIS_CLK);
/* 6. Poll CLKSTS field of the LCDC_CLKSR register to check that Pixel Clock
* is disabled.
*/
sam_wait_lcdstatus(LCDC_LCDSR_SIP | LCDC_LCDSR_CLK, 0);
/* Disable peripheral clock */
sam_lcdc_disableclk();
/* Disable the LCD clock */
sam_putreg(SAM_PMC_SCDR, PMC_LCDCK);
}
/****************************************************************************
* Name: sam_layer_orientation
*
* Description:
* Configure LCDC layer orientation
*
****************************************************************************/
static void sam_layer_orientation(void)
{
/* Base channel orientation */
LAYER_BASE.flags = 0;
#if defined(CONFIG_SAMA5_LCDC_BASE_ROT90)
LAYER_BASE.rotation = LCDC_ROT_90;
#elif defined(CONFIG_SAMA5_LCDC_BASE_ROT180)
LAYER_BASE.rotation = LCDC_ROT_180;
#elif defined(CONFIG_SAMA5_LCDC_BASE_ROT270)
LAYER_BASE.rotation = LCDC_ROT_270;
#else
LAYER_BASE.rotation = LCDC_ROT_0;
#endif
#ifdef CONFIG_SAMA5_LCDC_OVR1
/* Overlay 1 orientation */
LAYER_OVR1.flags = 0;
#ifdef CONFIG_SAMA5_LCDC_OVR1_BOTTOMUP
LAYER_OVR1.flags |= LCDC_FLAG_BOTTOMUP;
#endif
#ifdef CONFIG_SAMA5_LCDC_OVR1_RIGHTLEFT
LAYER_OVR1.flags |= LCDC_FLAG_RIGHTLEFT;
#endif
#if defined(CONFIG_SAMA5_LCDC_OVR1_ROT90)
LAYER_OVR1.rotation = LCDC_ROT_90;
#elif defined(CONFIG_SAMA5_LCDC_OVR1_ROT180)
LAYER_OVR1.rotation = LCDC_ROT_180;
#elif defined(CONFIG_SAMA5_LCDC_OVR1_ROT270)
LAYER_OVR1.rotation = LCDC_ROT_270;
#else
LAYER_OVR1.rotation = LCDC_ROT_0;
#endif
#endif
#ifdef CONFIG_SAMA5_LCDC_OVR2
/* Overlay 2 orientation */
LAYER_OVR2.flags = 0;
#ifdef CONFIG_SAMA5_LCDC_OVR2_BOTTOMUP
LAYER_OVR2.flags |= LCDC_FLAG_BOTTOMUP;
#endif
#ifdef CONFIG_SAMA5_LCDC_OVR2_RIGHTLEFT
LAYER_OVR2.flags |= LCDC_FLAG_RIGHTLEFT;
#endif
#if defined(CONFIG_SAMA5_LCDC_OVR2_ROT90)
LAYER_OVR2.rotation = LCDC_ROT_90;
#elif defined(CONFIG_SAMA5_LCDC_OVR2_ROT180)
LAYER_OVR2.rotation = LCDC_ROT_180;
#elif defined(CONFIG_SAMA5_LCDC_OVR2_ROT270)
LAYER_OVR2.rotation = LCDC_ROT_270;
#else
LAYER_OVR2.rotation = LCDC_ROT_0;
#endif
#endif
#ifdef CONFIG_SAMA5_LCDC_HEO
/* High End Overlay orientation */
LAYER_HEO.flags = 0;
#ifdef CONFIG_SAMA5_LCDC_HEO_BOTTOMUP
LAYER_HEO.flags |= LCDC_FLAG_BOTTOMUP;
#endif
#ifdef CONFIG_SAMA5_LCDC_HEO_RIGHTLEFT
LAYER_HEO.flags |= LCDC_FLAG_RIGHTLEFT;
#endif
#if defined(CONFIG_SAMA5_LCDC_HEO_ROT90)
LAYER_HEO.rotation = LCDC_ROT_90;
#elif defined(CONFIG_SAMA5_LCDC_HEO_ROT180)
LAYER_HEO.rotation = LCDC_ROT_180;
#elif defined(CONFIG_SAMA5_LCDC_HEO_ROT270)
LAYER_HEO.rotation = LCDC_ROT_270;
#else
LAYER_HEO.rotation = LCDC_ROT_0;
#endif
#endif
#ifdef CONFIG_SAMA5_LCDC_HCR
/* Hardware Cursor orientation */
LAYER_HCR.flags = 0;
#if defined(CONFIG_SAMA5_LCDC_HCR_ROT90)
LAYER_HCR.rotation = LCDC_ROT_90;
#elif defined(CONFIG_SAMA5_LCDC_HCR_ROT180)
LAYER_HCR.rotation = LCDC_ROT_180;
#elif defined(CONFIG_SAMA5_LCDC_HCR_ROT270)
LAYER_HCR.rotation = LCDC_ROT_270;
#else
LAYER_HCR.rotation = LCDC_ROT_0;
#endif
#endif
}
/****************************************************************************
* Name: sam_layer_color
*
* Description:
* Configure LCDC layer color mode
*
****************************************************************************/
static void sam_layer_color(void)
{
/* Mark the CLUTs as empty */
#ifdef CONFIG_FB_CMAP
LAYER_BASE.offset = SAM_LCDC_NCLUT - 1;
LAYER_OVR1.offset = SAM_LCDC_NCLUT - 1;
LAYER_OVR2.offset = SAM_LCDC_NCLUT - 1;
LAYER_HEO.offset = SAM_LCDC_NCLUT - 1;
LAYER_HCR.offset = SAM_LCDC_NCLUT - 1;
#endif
/* Base channel color configuration */
#if defined(CONFIG_SAMA5_LCDC_BASE_RGB888P)
LAYER_BASE.bpp = 24;
sam_putreg(SAM_LCDC_BASECFG0,
LCDC_BASECFG0_DLBO | LCDC_BASECFG0_BLEN_INCR16);
sam_putreg(SAM_LCDC_BASECFG1,
LCDC_BASECFG1_24BPP_RGB888P);
# elif defined(CONFIG_SAMA5_LCDC_BASE_RGB565)
LAYER_BASE.bpp = 16;
sam_putreg(SAM_LCDC_BASECFG0,
LCDC_BASECFG0_DLBO | LCDC_BASECFG0_BLEN_INCR16);
sam_putreg(SAM_LCDC_BASECFG1,
LCDC_BASECFG1_16BPP_RGB565);
#else
# error Support for this resolution is not yet implemented
#endif
#ifdef CONFIG_SAMA5_LCDC_OVR1
/* Overlay 1 color configuration, GA 0xff */
# if defined(CONFIG_SAMA5_LCDC_OVR1_RGB888P)
LAYER_OVR1.bpp = 24;
sam_putreg(SAM_LCDC_OVR1CFG0,
LCDC_OVR1CFG0_DLBO | LCDC_BASECFG0_BLEN_INCR16 |
LCDC_OVR1CFG0_ROTDIS);
sam_putreg(SAM_LCDC_OVR1CFG1,
LCDC_OVR1CFG1_24BPP_RGB888P);
sam_putreg(SAM_LCDC_OVR1CFG9,
LCDC_OVR1CFG9_GA(0xff) | LCDC_OVR1CFG9_GAEN);
# elif defined(CONFIG_SAMA5_LCDC_OVR1_RGB565)
LAYER_OVR1.bpp = 16;
sam_putreg(SAM_LCDC_OVR1CFG0,
LCDC_OVR1CFG0_DLBO | LCDC_BASECFG0_BLEN_INCR16 |
LCDC_OVR1CFG0_ROTDIS);
sam_putreg(SAM_LCDC_OVR1CFG1,
LCDC_OVR1CFG1_16BPP_RGB565);
sam_putreg(SAM_LCDC_OVR1CFG9,
LCDC_OVR1CFG9_GA(0xff) | LCDC_OVR1CFG9_GAEN);
# else
# error Support for this resolution is not yet implemented
# endif
#endif
#ifdef CONFIG_SAMA5_LCDC_OVR2
/* Overlay 2 color configuration, GA 0xff */
# if defined(CONFIG_SAMA5_LCDC_OVR2_RGB888P)
LAYER_OVR2.bpp = 24;
sam_putreg(SAM_LCDC_OVR2CFG0,
LCDC_OVR2CFG0_DLBO | LCDC_BASECFG0_BLEN_INCR16 |
LCDC_OVR2CFG0_ROTDIS;
sam_putreg(SAM_LCDC_OVR2CFG1,
LCDC_OVR2CFG1_24BPP_RGB888P);
sam_putreg(SAM_LCDC_OVR2CFG9,
LCDC_OVR2CFG9_GA(0xff) | LCDC_OVR2CFG9_GAEN;
# elif defined(CONFIG_SAMA5_LCDC_OVR2_RGB565)
LAYER_OVR2.bpp = 16;
sam_putreg(SAM_LCDC_OVR2CFG0,
LCDC_OVR2CFG0_DLBO | LCDC_BASECFG0_BLEN_INCR16 |
LCDC_OVR2CFG0_ROTDIS);
sam_putreg(SAM_LCDC_OVR2CFG1,
LCDC_OVR2CFG1_16BPP_RGB565);
sam_putreg(SAM_LCDC_OVR2CFG9,
LCDC_OVR2CFG9_GA(0xff) | LCDC_OVR2CFG9_GAEN);
# else
# error Support for this resolution is not yet implemented
# endif
#endif
#ifdef CONFIG_SAMA5_LCDC_HEO
/* High End Overlay color configuration, GA 0xff */
# if defined(CONFIG_SAMA5_LCDC_HEO_RGB888P)
LAYER_HEO.bpp = 24;
sam_putreg(SAM_LCDC_HEOCFG0,
LCDC_HEOCFG0_DLBO | LCDC_HEOCFG0_BLEN_INCR16 |
LCDC_HEOCFG0_ROTDIS);
sam_putreg(SAM_LCDC_HEOCFG1,
LCDC_HEOCFG1_24BPP_RGB888P);
sam_putreg(SAM_LCDC_HEOCFG12,
LCDC_HEOCFG12_GA(0xff) | LCDC_HEOCFG12_GAEN);
# elif defined(CONFIG_SAMA5_LCDC_HEO_RGB565)
LAYER_HEO.bpp = 16;
sam_putreg(SAM_LCDC_HEOCFG0,
LCDC_HEOCFG0_DLBO | LCDC_HEOCFG0_BLEN_INCR16 |
LCDC_HEOCFG0_ROTDIS);
sam_putreg(SAM_LCDC_HEOCFG1,
LCDC_HEOCFG1_16BPP_RGB565);
sam_putreg(SAM_LCDC_HEOCFG9,
LCDC_HEOCFG9_GA(0xff) | LCDC_HEOCFG9_GAEN);
# else
# error Support for this resolution is not yet implemented
# endif
#endif
#ifdef SAMA5_HAVE_LCDC_HCRCH
#ifdef CONFIG_SAMA5_LCDC_HCR
/* Hardware Cursor color configuration, GA 0xff, Key #000000 */
# if defined(CONFIG_SAMA5_LCDC_HCR_RGB888P)
LAYER_HCR.bpp = 24;
sam_putreg(SAM_LCDC_HCRCFG0,
LCDC_HCRCFG0_DLBO | LCDC_HCRCFG0_BLEN_INCR16);
sam_putreg(SAM_LCDC_HCRCFG1,
LCDC_HCRCFG1_24BPP_RGB888P;
sam_putreg(SAM_LCDC_HCRCFG7,
0x000000);
sam_putreg(SAM_LCDC_HCRCFG8,
0xffffff);
sam_putreg(SAM_LCDC_HCRCFG9,
LCDC_HCRCFG9_GAEN(0xff) | LCDC_HCRCFG9_GAEN);
# elif defined(CONFIG_SAMA5_LCDC_HCR_RGB565)
LAYER_HCR.bpp = 16;
sam_putreg(SAM_LCDC_HCRCFG0,
LCDC_HCRCFG0_DLBO | LCDC_HCRCFG0_BLEN_INCR16 |
LCDC_HCRCFG0_ROTDIS);
sam_putreg(SAM_LCDC_HCRCFG1,
LCDC_HCRCFG1_16BPP_RGB565);
sam_putreg(SAM_LCDC_HCRCFG9,
LCDC_HCRCFG9_GA(0xff) | LCDC_HCRCFG9_GAEN);
# else
# error Support for this resolution is not yet implemented
# endif
#endif
#endif
}
/****************************************************************************
* Name: sam_lcd_enable
*
* Description:
* Enable the LCD for normal use
*
****************************************************************************/
static void sam_lcd_enable(void)
{
uint32_t regval;
uint32_t div;
/* Enable the LCD peripheral clock */
sam_lcdc_enableclk();
/* Enable the LCD clock */
sam_putreg(SAM_PMC_SCER, PMC_LCDCK);
/* 1. Configure LCD timing parameters, signal polarity and clock period. */
#ifdef BOARD_LCDC_MCK_MUL2
div = (2*BOARD_MCK_FREQUENCY + (BOARD_LCDC_PIXELCLOCK-1)) / BOARD_LCDC_PIXELCLOCK;
#else
div = (BOARD_MCK_FREQUENCY + (BOARD_LCDC_PIXELCLOCK-1)) / BOARD_LCDC_PIXELCLOCK;
#endif
DEBUGASSERT(div > 1);
regval = LCDC_LCDCFG0_CGDISBASE | LCDC_LCDCFG0_CLKDIV(div - 2);
#ifdef BOARD_LCDC_PIXCLK_INV
regval |= LCDC_LCDCFG0_CLKPOL;
#endif
#ifdef BOARD_LCDC_MCK_MUL2
regval |= LCDC_LCDCFG0_CLKSEL;
#endif
#ifdef CONFIG_SAMA5_LCDC_OVR1
regval |= LCDC_LCDCFG0_CGDISOVR1;
#endif
#ifdef CONFIG_SAMA5_LCDC_OVR2
regval |= LCDC_LCDCFG0_CGDISOVR2;
#endif
#ifdef CONFIG_SAMA5_LCDC_HEO
regval |= LCDC_LCDCFG0_CGDISHEO;
#endif
#ifdef CONFIG_SAMA5_LCDC_HCR
regval |= LCDC_LCDCFG0_CGDISHCR;
#endif
sam_putreg(SAM_LCDC_LCDCFG0, regval);
regval = LCDC_LCDCFG1_HSPW(BOARD_LCDC_HSPW - 1) |
LCDC_LCDCFG1_VSPW(BOARD_LCDC_VSPW - 1);
sam_putreg(SAM_LCDC_LCDCFG1, regval);
regval = LCDC_LCDCFG2_VFPW(BOARD_LCDC_VFPW - 1) |
LCDC_LCDCFG2_VBPW(BOARD_LCDC_VBPW);
sam_putreg(SAM_LCDC_LCDCFG2, regval);
regval = LCDC_LCDCFG3_HFPW(BOARD_LCDC_HFPW - 1) |
LCDC_LCDCFG3_HBPW(BOARD_LCDC_HBPW - 1);
sam_putreg(SAM_LCDC_LCDCFG3, regval);
regval = LCDC_LCDCFG4_PPL(BOARD_LCDC_WIDTH - 1) |
LCDC_LCDCFG4_RPF(BOARD_LCDC_HEIGHT - 1);
sam_putreg(SAM_LCDC_LCDCFG4, regval);
regval = LCDC_LCDCFG5_HSPOL | LCDC_LCDCFG5_VSPOL |
LCDC_LCDCFG5_VSPDLYS | LCDC_LCDCFG5_DISPDLY |
LCDC_LCDCFG5_GUARDTIME(BOARD_LCDC_GUARDTIME);
#if BOARD_LCDC_OUTPUT_BPP == 16
regval |= LCDC_LCDCFG5_MODE_12BPP;
#elif BOARD_LCDC_OUTPUT_BPP == 16
regval |= LCDC_LCDCFG5_MODE_16BPP;
#elif BOARD_LCDC_OUTPUT_BPP == 18
regval |= LCDC_LCDCFG5_MODE_18BPP;
#elif BOARD_LCDC_OUTPUT_BPP == 24
regval |= LCDC_LCDCFG5_MODE_24BPP;
#else
# error Unknown or undefined output resolution
#endif
sam_putreg(SAM_LCDC_LCDCFG5, regval);
regval = BOARD_LCDC_PWMPS | BOARD_LCDC_PWMPOL |
LCDC_LCDCFG6_PWMCVAL(CONFIG_SAMA5_LCDC_DEFBACKLIGHT);
sam_putreg(SAM_LCDC_LCDCFG6, regval);
/* 2. Enable the Pixel Clock by writing one to the CLKEN field of the
* LCDC_LCDEN register.
*/
sam_putreg(SAM_LCDC_LCDEN, LCDC_LCDEN_CLK);
/* 3. Poll CLKSTS field of the LCDC_LCDSR register to check that the clock
* is running.
*/
sam_wait_lcdstatus(LCDC_LCDSR_SIP | LCDC_LCDSR_CLK, LCDC_LCDSR_CLK);
/* 4. Enable Horizontal and Vertical Synchronization by writing one to the
* SYNCEN field of the LCDC_LCDEN register.
*/
sam_putreg(SAM_LCDC_LCDEN, LCDC_LCDEN_SYNC);
/* 5. Poll LCDSTS field of the LCDC_LCDSR register to check that the
* synchronization is up.
*/
sam_wait_lcdstatus(LCDC_LCDSR_SIP | LCDC_LCDSR_LCD, LCDC_LCDSR_LCD);
/* 6. Enable the display power signal writing one to the DISPEN field of the
* LCDC_LCDEN register.
*/
sam_putreg(SAM_LCDC_LCDEN, LCDC_LCDEN_DISP);
/* 7. Poll DISPSTS field of the LCDC_LCDSR register to check that the power
* signal is activated.
*/
sam_wait_lcdstatus(LCDC_LCDSR_SIP | LCDC_LCDSR_DISP, LCDC_LCDSR_DISP);
}
/****************************************************************************
* Name: sam_layer_configure
*
* Description:
* Configure layer layer structures, DMA descriptor memory, and
* framebuffers
*
****************************************************************************/
static void sam_layer_configure(void)
{
/* Common layer initialization */
memset(&LAYER_BASE, 0, sizeof(struct sam_layer_s));
LAYER_BASE.dscr = (struct sam_dscr_s *)SAMA5_LCDC_BASE_DSCR;
LAYER_BASE.lid = LCDC_LAYER_BASE;
LAYER_BASE.framebuffer = (uint8_t *)SAMA5_LCDC_BUFFER_BASE;
memset(&LAYER_OVR1, 0, sizeof(struct sam_layer_s));
LAYER_OVR1.dscr = (struct sam_dscr_s *)SAMA5_LCDC_OVR1_DSCR;
LAYER_OVR1.lid = LCDC_LAYER_OVR1;
#ifdef CONFIG_SAMA5_LCDC_OVR1
LAYER_OVR1.framebuffer = (uint8_t *)SAMA5_LCDC_BUFFER_OVR1;
#endif
memset(&LAYER_OVR2, 0, sizeof(struct sam_layer_s));
LAYER_OVR2.dscr = (struct sam_dscr_s *)SAMA5_LCDC_OVR2_DSCR;
LAYER_OVR2.lid = LCDC_LAYER_OVR2;
#ifdef CONFIG_SAMA5_LCDC_OVR2
LAYER_OVR2.framebuffer = (uint8_t *)SAMA5_LCDC_BUFFER_OVR2;
#endif
memset(&LAYER_HEO, 0, sizeof(struct sam_layer_s));
LAYER_HEO.dscr = (struct sam_dscr_s *)SAMA5_LCDC_HEO_DSCR;
LAYER_HEO.lid = LCDC_LAYER_HEO;
#ifdef CONFIG_SAMA5_LCDC_HEO
LAYER_HEO.framebuffer = (uint8_t *)SAMA5_LCDC_BUFFER_HEO;
#endif
#ifdef SAMA5_HAVE_LCDC_HCRCH
memset(&LAYER_HCR, 0, sizeof(struct sam_layer_s));
LAYER_HCR.dscr = (struct sam_dscr_s *)SAMA5_LCDC_HCR_DSCR;
LAYER_HCR.lid = LCDC_LAYER_HCR;
#ifdef CONFIG_SAMA5_LCDC_HCR
LAYER_HCR.framebuffer = (uint8_t *)SAMA5_LCDC_BUFFER_HCR;
#endif
#endif /* SAMA5_HAVE_LCDC_HCRCH */
}
/****************************************************************************
* Name: sam_scalefactor
*
* Description:
* Calculate HEO scale factor
*
* Input Parameters:
* oldw - the old image width
* neww - The new image width
*
****************************************************************************/
#ifdef CONFIG_SAMA5_LCDC_HEO
static uint32_t sam_scalefactor(uint32_t oldw, uint32_t neww)
{
return 2048 * (neww + 1) / (oldw + 1);
}
#endif
/****************************************************************************
* Name: sam_show_layer
*
* Description:
* Show the give layer with the specified orientation and (perhaps) scaling.
*
****************************************************************************/
static void sam_show_layer(struct sam_layer_s *layer,
uint32_t dispx, uint32_t dispy,
uint32_t dispw, uint32_t disph,
uint32_t imgw, uint32_t imgh)
{
struct sam_dscr_s *dscr;
uint8_t *buffer;
uintptr_t cfgaddr;
uintptr_t regaddr;
uint32_t padding = 0;
uint32_t regval;
uint32_t bytesprow;
uint32_t bytespp;
uint32_t bprow;
bool bottomup;
bool rightleft;
int lid;
DEBUGASSERT(layer && layer->dscr);
/* Windows position & size check */
if (dispx + dispw > BOARD_LCDC_WIDTH)
{
dispw = BOARD_LCDC_WIDTH - dispx;
}
if (dispy + disph > BOARD_LCDC_HEIGHT)
{
disph = BOARD_LCDC_HEIGHT - dispy;
}
if (dispw <= 0)
{
dispw = 1;
}
if (disph <= 0)
{
disph = 1;
}
if (imgw <= 0)
{
imgw = 1;
}
if (imgh <= 0)
{
imgh = 1;
}
/* Set display buffer and mode setup */
bytespp = (uint32_t)layer->bpp >> 3;
bprow = imgw * (uint32_t)layer->bpp;
bytesprow = bprow >> 3;
if ((bprow & 7) != 0)
{
bytesprow ++;
}
padding = 0;
if ((bytesprow & 3) != 0)
{
padding = 4 - (bytesprow & 0x3);
}
/* Bottom-up and Right-to-left mode setup */
bottomup = (layer->flags & LCDC_FLAG_BOTTOMUP) != 0;
rightleft = (layer->flags & LCDC_FLAG_RIGHTLEFT) != 0;
/* No X mirror supported layer, no Right->Left scan */
#ifdef SAMA5_HAVE_PSTRIDE
regaddr = g_layerpstride[lid];
if (regaddr)
{
rightleft = false;
}
#endif
dscr = layer->dscr;
lid = layer->lid;
buffer = layer->framebuffer;
cfgaddr = g_layercfg[lid];
/* Normal direction: Left,Top -> Right,Down */
if ((!rightleft && !bottomup && layer->rotation == LCDC_ROT_0 ) ||
( rightleft && bottomup && layer->rotation == LCDC_ROT_180))
{
/* No rotation optimization */
regval = sam_getreg(cfgaddr);
regval |= LCDC_HEOCFG0_ROTDIS;
sam_putreg(cfgaddr, regval);
/* X0 ++ */
#ifdef SAMA5_HAVE_PSTRIDE
regaddr = g_layerpstride[lid];
if (regaddr)
{
sam_putreg(regaddr, 0);
}
#endif
/* Y0 ++ */
regaddr = g_layerstride[lid];
sam_putreg(regaddr, padding);
/* Pointer to Left,Top (x0,y0) */
}
/* X mirror: Right,Top -> Left,Down */
else if (( rightleft && !bottomup && layer->rotation == LCDC_ROT_0 ) ||
(!rightleft && bottomup && layer->rotation == LCDC_ROT_180))
{
/* No rotation optimization */
regval = sam_getreg(cfgaddr);
regval |= LCDC_HEOCFG0_ROTDIS;
sam_putreg(cfgaddr, regval);
/* X1 -- */
#ifdef SAMA5_HAVE_PSTRIDE
regaddr = g_layerpstride[lid];
if (regaddr)
{
sam_putreg(regaddr, 0 - 2*bytespp);
}
#endif
/* Y0 ++ */
regaddr = g_layerstride[lid];
sam_putreg(regaddr, 2*bytesprow + padding - 2*bytespp);
/* Pointer to Right,Top (x1,y0) */
buffer = (uint8_t *)
((uint32_t)layer->framebuffer + bytespp * (imgw - 1));
}
/* Y mirror: Left,Down -> Right,Top */
else if ((!rightleft && bottomup && layer->rotation == LCDC_ROT_0 ) ||
( rightleft && !bottomup && layer->rotation == LCDC_ROT_180))
{
/* No rotation optimization */
regval = sam_getreg(cfgaddr);
regval |= LCDC_HEOCFG0_ROTDIS;
sam_putreg(cfgaddr, regval);
/* X0 ++ */
#ifdef SAMA5_HAVE_PSTRIDE
regaddr = g_layerpstride[lid];
if (regaddr)
{
sam_putreg(regaddr, 0);
}
#endif
/* Y1 -- */
regaddr = g_layerstride[lid];
sam_putreg(regaddr, 0 - (2*bytesprow + padding));
/* Pointer to Left,Down (x0,y1) */
buffer = (uint8_t *)
((uintptr_t)layer->framebuffer + (bytesprow + padding) * (imgh - 1));
}
/* X,Y mirror: Right,Top -> Left,Down */
else if (( rightleft && bottomup && layer->rotation == LCDC_ROT_0 ) ||
(!rightleft && !bottomup && layer->rotation == LCDC_ROT_180))
{
/* No rotation optimization */
regval = sam_getreg(cfgaddr);
regval |= LCDC_HEOCFG0_ROTDIS;
sam_putreg(cfgaddr, regval);
/* X1 -- */
#ifdef SAMA5_HAVE_PSTRIDE
regaddr = g_layerpstride[lid];
if (regaddr)
{
sam_putreg(regaddr, 0 - 2*bytespp;
}
#endif
/* Y1 -- */
regaddr = g_layerstride[lid];
sam_putreg(regaddr, 0 - (2*bytespp + padding));
/* Pointer to Left,Down (x1,y1) */
buffer = (uint8_t *)
((uint32_t)layer->framebuffer +
(bytesprow + padding) * (imgh - 1) +
bytespp * (imgw - 1));
}
/* Rotate 90: Down,Left -> Top,Right (with w,h swap) */
else if ((!rightleft && !bottomup && layer->rotation == LCDC_ROT_90 ) ||
( rightleft && bottomup && layer->rotation == LCDC_ROT_270))
{
/* No rotation optimization */
regval = sam_getreg(cfgaddr);
regval |= LCDC_HEOCFG0_ROTDIS;
sam_putreg(cfgaddr, regval);
/* Y -- as pixels in row */
#ifdef SAMA5_HAVE_PSTRIDE
regaddr = g_layerpstride[lid];
if (regaddr)
{
sam_putreg(regaddr, 0 - (bytespp + bytesprow + padding));
}
#endif
/* X ++ as rows */
regaddr = g_layerstride[lid];
sam_putreg(regaddr, (bytesprow + padding) * (imgh - 1));
/* Pointer to Bottom,Left */
buffer = (uint8_t *)
((uint32_t)layer->framebuffer +
(bytesprow + padding) * (imgh - 1));
}
/* Rotate 270: Top,Right -> Down,Left (with w,h swap) */
else if ((!rightleft && !bottomup && layer->rotation == LCDC_ROT_270) ||
( rightleft && bottomup && layer->rotation == LCDC_ROT_90 ))
{
/* No rotation optimization */
regval = sam_getreg(cfgaddr);
regval |= LCDC_HEOCFG0_ROTDIS;
sam_putreg(cfgaddr, regval);
/* Y ++ as pixels in row */
#ifdef SAMA5_HAVE_PSTRIDE
regaddr = g_layerpstride[lid];
if (regaddr)
{
sam_putreg(regaddr, bytesprow + padding - bytespp);
}
#endif
/* X -- as rows */
regaddr = g_layerstride[lid];
sam_putreg(regaddr, 0 - 2*bytespp - (bytesprow + padding) * (imgh - 1));
/* Pointer to top right */
buffer = (uint8_t *)
((uintptr_t)layer->framebuffer + bytespp * (imgw - 1));
}
/* Mirror X then Rotate 90: Down,Right -> Top,Left */
else if (( rightleft && !bottomup && layer->rotation == LCDC_ROT_90 ) ||
(!rightleft && bottomup && layer->rotation == LCDC_ROT_270))
{
/* No rotation optimization */
regval = sam_getreg(cfgaddr);
regval |= LCDC_HEOCFG0_ROTDIS;
sam_putreg(cfgaddr, regval);
/* Y -- as pixels in row */
#ifdef SAMA5_HAVE_PSTRIDE
regaddr = g_layerpstride[lid];
if (regaddr)
{
sam_putreg(regaddr, 0 - (bytespp + bytesprow + padding));
}
#endif
/* X -- as rows */
regaddr = g_layerstride[lid];
sam_putreg(regaddr, 0 - 2 * bytespp + (bytesprow + padding) * (imgh - 1));
/* Pointer to down right (x1,y1) */
buffer = (uint8_t *)
((uintptr_t)layer->framebuffer +
(bytesprow + padding) * (imgh - 1) +
(bytespp) * (imgw - 1));
}
/* Mirror Y then Rotate 90: Top,Left -> Down,Right */
else if ((!rightleft && bottomup && layer->rotation == 90) ||
( rightleft && !bottomup && layer->rotation == LCDC_ROT_270))
{
/* No rotation optimization */
regval = sam_getreg(cfgaddr);
regval |= LCDC_HEOCFG0_ROTDIS;
sam_putreg(cfgaddr, regval);
/* Y ++ as pixels in row */
#ifdef SAMA5_HAVE_PSTRIDE
regaddr = g_layerpstride[lid];
if (regaddr)
{
sam_putreg(regaddr, bytesprow + padding - bytespp);
}
#endif
/* X ++ as rows */
regaddr = g_layerstride[lid];
sam_putreg(regaddr, 0 - (bytesprow + padding) * (imgh - 1));
/* Pointer to top left (x0,y0) */
}
/* Configure DMA */
/* DMA is running, just add new descriptor to queue */
sam_dmasetup(lid, dscr, buffer);
/* Set layer position and size */
#ifdef SAMA5_HAVE_POSITION
regaddr = g_layerpos[lid];
if (regaddr)
{
sam_putreg(regaddr,
LCDC_HEOCFG2_XPOS(dispx) | LCDC_HEOCFG2_YPOS(dispy));
}
#endif
#ifdef SAMA5_HAVE_SIZE
regaddr = g_layersize[lid];
if (regaddr)
{
sam_putreg(regaddr,
LCDC_HEOCFG3_XSIZE(dispw - 1) | LCDC_HEOCFG3_YSIZE(disph - 1);
}
#endif
#ifdef CONFIG_SAMA5_LCDC_HEO
/* Scaling setup */
if (lid == LCDC_HEO)
{
uint32_t srcw;
uint32_t srch;
/* Image size only used in scaling */
/* Scaling target */
if (layer->rotation == LCDC_ROT_90 || layer->rotation == LCDC_ROT_270)
{
srcw = imgh;
srch = imgw;
}
else
{
srcw = imgw;
srch = imgh;
}
sam_putreg(SAM_LCDC_HEOCFG4,
LCDC_HEOCFG4_XMEM_SIZE(srcw - 1) |
LCDC_HEOCFG4_YMEM_SIZE(srch - 1));
/* Scaled */
if (dispw != srcw || disph != srch)
{
uint16_t xfactor;
uint16_t yfactor;
xfactor = sam_scalefactor(dispw, srcw);
yfactor = sam_scalefactor(disph, srch);
sam_putreg(LCDC_HEOCFG13,
LCDC_HEOCFG13_YFACTOR(yfactor) |
LCDC_HEOCFG13_XFACTOR(xfactor) |
LCDC_HEOCFG13_SCALEN;
}
/* Disable scaling */
else
{
sam_putreg(LCDC_HEOCFG13, 0);
}
}
#endif
/* Enable DMA */
if (buffer)
{
regaddr = g_layerblend[lid];
regval = sam_getreg(regaddr);
regval |= LCDC_HEOCFG12_DMA | LCDC_HEOCFG12_OVR;
sam_putreg(regaddr, regval);
}
/* 5. Enable the relevant channel by writing one to the CHEN field of the
* CHXCHER register.
*/
regaddr = g_layerenable[lid];
sam_putreg(regaddr, LCDC_HEOCHER_UPDATE | LCDC_HEOCHER_CH);
/* 6. An interrupt may be raised if unmasked when the descriptor has been
* loaded.
*/
}
/****************************************************************************
* Name: sam_show_base
*
* Description:
* Show the base layer
*
****************************************************************************/
static void sam_show_base(void)
{
sam_show_layer(&LAYER_BASE, 0, 0,
BOARD_LCDC_WIDTH, BOARD_LCDC_HEIGHT, BOARD_LCDC_WIDTH, BOARD_LCDC_HEIGHT);
}
/****************************************************************************
* Name: sam_show_hcr
*
* Description:
* Show the hardware cursor layer
*
****************************************************************************/
#ifdef CONFIG_SAMA5_LCDC_HCR
static void sam_show_hcr(void)
{
uint32_t regval;
/* Enable default transparent keying */
sam_putreg(SAM_LCDC_HCRCFG7, 0x00000000);
sam_putreg(SAM_LCDC_HCRCFG8, 0xffffffff);
regval = sam_getreg(SAM_LCDC_HCRCFG9);
regval |= LCDC_HCRCFG9_CRKEY;
sam_putreg(SAM_LCDC_HCRCFG9, regval);
/* And show the hardware cursor layer */
sam_show_layer(&LAYER_HCR,
(BOARD_LCDC_WIDTH - CONFIG_SAMA5_LCDC_HCR_MAXWIDTH) / 2,
(BOARD_LCDC_HEIGHT - CONFIG_SAMA5_LCDC_HCR_MAXHEIGHT) / 2,
CONFIG_SAMA5_LCDC_HCR_MAXWIDTH, CONFIG_SAMA5_LCDC_HCR_MAXHEIGHT,
CONFIG_SAMA5_LCDC_HCR_MAXWIDTH, CONFIG_SAMA5_LCDC_HCR_MAXHEIGHT);
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_fbinitialize
*
* Description:
* Initialize the framebuffer video hardware associated with the display.
*
* Input parameters:
* display - In the case of hardware with multiple displays, this
* specifies the display. Normally this is zero.
*
* Returned Value:
* Zero is returned on success; a negated errno value is returned on any
* failure.
*
****************************************************************************/
int up_fbinitialize(int display)
{
#if defined(CONFIG_SAMA5_LCDC_OVR1) && defined(CONFIG_SAMA5_LCDC_HEO)
uint32_t regval;
#endif
gvdbg("Entry\n");
/* Configure layer layer structures, DMA descriptor memory, and
* framebuffers
*/
sam_layer_configure();
/* Disable the LCD */
sam_lcd_disable();
/* Configure PIO pins */
sam_pio_config();
gvdbg("Configuring the LCD controller\n");
/* Enable the LCD peripheral clock */
sam_lcdc_enableclk();
/* Enable the LCD clock */
sam_putreg(SAM_PMC_SCER, PMC_LCDCK);
/* Disable LCD interrupts */
sam_putreg(SAM_LCDC_LCDIDR, LCDC_LCDINT_ALL);
/* Configure layer orientation */
sam_layer_orientation();
/* Configure layer colors */
sam_layer_color();
/* Clear the display memory */
sam_lcdclear(CONFIG_SAMA5_LCDC_BACKCOLOR);
/* And turn the LCD on */
gvdbg("Enabling the display\n");
sam_lcd_enable();
/* Display base layer */
sam_show_base();
#if defined(CONFIG_SAMA5_LCDC_OVR1) && defined(CONFIG_SAMA5_LCDC_HEO)
/* Overlay 1 is above the HEO layer */
regval = sam_getreg(SAM_LCDC_HEOCFG12);
regval |= LCDC_HEOCFG12_VIDPRI;
sam_putreg(SAM_LCDC_HEOCFG12, regval);
sam_putreg(SAM_LCDC_HEOCHER, LCDC_HEOCHER_UPDATE);
#endif
#ifdef CONFIG_SAMA5_LCDC_HCR
/* Show cursor layer */
sam_show_hcr();
#endif
/* Enable the backlight.
*
* REVISIT: Backlight level could be dynamically adjustable
*/
sam_backlight(CONFIG_SAMA5_LCDC_DEFBACKLIGHT);
return OK;
}
/****************************************************************************
* Name: up_fbgetvplane
*
* Description:
* Return a a reference to the framebuffer object for the specified video
* plane of the specified plane. Many OSDs support multiple planes of video.
*
* Input parameters:
* display - In the case of hardware with multiple displays, this
* specifies the display. Normally this is zero.
* vplane - Identifies the plane being queried.
*
* Returned Value:
* A non-NULL pointer to the frame buffer access structure is returned on
* success; NULL is returned on any failure.
*
****************************************************************************/
FAR struct fb_vtable_s *up_fbgetvplane(int display, int vplane)
{
gvdbg("vplane: %d\n", vplane);
if (vplane == 0)
{
return (struct fb_vtable_s *)&g_base_vtable;
}
else
{
return NULL;
}
}
/****************************************************************************
* Name: up_fbuninitialize
*
* Description:
* Uninitialize the framebuffer support for the specified display.
*
* Input Parameters:
* display - In the case of hardware with multiple displays, this
* specifies the display. Normally this is zero.
*
* Returned Value:
* None
*
****************************************************************************/
void up_fbuninitialize(int display)
{
/* Disable the LCD controller */
sam_lcd_disable();
}
/************************************************************************************
* Name: sam_lcdclear
*
* Description:
* This is a non-standard LCD interface just for the SAMA5. 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.
*
************************************************************************************/
void sam_lcdclear(nxgl_mxpixel_t color)
{
#if SAMA5_LCDC_BASE_BPP == 16
uint16_t *dest = (uint16_t *)LAYER_BASE.framebuffer;
int i;
gvdbg("Clearing display: BPP=16 color=%04x framebuffer=%08x size=%d\n",
color, LAYER_BASE.framebuffer, SAMA5_BASE_FBSIZE);
for (i = 0; i < SAMA5_BASE_FBSIZE; i += sizeof(uint16_t))
{
*dest++ = (uint16_t)color;
}
#elif SAMA5_LCDC_BASE_BPP == 24
uint8_t *dest = (uint8_t *)LAYER_BASE.framebuffer;
uint8_t r;
uint8_t g;
uint8_t b;
int i;
gvdbg("Clearing display: BPP=24 color=%06x framebuffer=%08x size=%d\n",
color, LAYER_BASE.framebuffer, SAMA5_BASE_FBSIZE);
b = color & 0xff;
g = (color >> 8) & 0xff;
r = (color >> 16) & 0xff;
for (i = 0; i < SAMA5_BASE_FBSIZE; i += 3*sizeof(uint8_t))
{
*dest++ = b;
*dest++ = g;
*dest++ = r;
}
#elif SAMA5_LCDC_BASE_BPP == 32
uint32_t *dest = (uint32_t *)LAYER_BASE.framebuffer;
int i;
gvdbg("Clearing display: BPP=32 color=%08x framebuffer=%08x size=%d\n",
color, LAYER_BASE.framebuffer, SAMA5_BASE_FBSIZE);
for (i = 0; i < SAMA5_BASE_FBSIZE; i += sizeof(uint32_t))
{
*dest++ = (uint32_t)color;
}
#endif
}