293 lines
8.4 KiB
C
293 lines
8.4 KiB
C
|
/*
|
||
|
* Copyright (C) 2023 Daniele Lacamera <root@danielinux.net>
|
||
|
*
|
||
|
* This program is free software: you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||
|
* the Free Software Foundation, either version 3 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU Lesser General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Lesser General Public License
|
||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
/* Includes ------------------------------------------------------------------*/
|
||
|
#include <string.h>
|
||
|
#include "malloc.h"
|
||
|
#include "ui.h"
|
||
|
#include <unicore-mx/stm32/rcc.h>
|
||
|
#include <unicore-mx/stm32/gpio.h>
|
||
|
#include <unicore-mx/stm32/ltdc.h>
|
||
|
#include "stm32f7_ltdc.h"
|
||
|
|
||
|
|
||
|
/* Private function prototypes -----------------------------------------------*/
|
||
|
static void ltdc_config(void);
|
||
|
static void ltdc_pinmux(void);
|
||
|
static void ltdc_clock(void);
|
||
|
static int ltdc_config_layer(void);
|
||
|
|
||
|
static uint32_t Screen_size = 0;
|
||
|
|
||
|
static int ltdc_config_layer(void)
|
||
|
{
|
||
|
uint32_t format = 0;
|
||
|
Screen_size = xres * yres * (bits_per_pixel/8);
|
||
|
|
||
|
/* Screen configuration */
|
||
|
ltdc_setup_windowing(LTDC_LAYER_1, xres, xres);
|
||
|
ltdc_set_pixel_format(LTDC_LAYER_1, pixel_format);
|
||
|
ltdc_set_default_colors(LTDC_LAYER_1, 0, 0, 0, 0);
|
||
|
ltdc_set_constant_alpha(LTDC_LAYER_1, 255);
|
||
|
ltdc_set_blending_factors(LTDC_LAYER_1, LTDC_LxBFCR_BF1_CONST_ALPHA, LTDC_LxBFCR_BF2_CONST_ALPHA);
|
||
|
ltdc_set_fbuffer_address(LTDC_LAYER_1, Screen_address);
|
||
|
ltdc_set_fb_line_length(LTDC_LAYER_1, xres * (bits_per_pixel/8), xres * (bits_per_pixel/8));
|
||
|
ltdc_set_fb_line_count(LTDC_LAYER_1, yres);
|
||
|
|
||
|
#if (OVERLAY)
|
||
|
/* Overlay configuration */
|
||
|
ltdc_setup_windowing(LTDC_LAYER_2, xres, xres);
|
||
|
ltdc_set_pixel_format(LTDC_LAYER_2, LTDC_LxPFCR_AL44);
|
||
|
ltdc_set_default_colors(LTDC_LAYER_2, 255, 0, 0, 0);
|
||
|
ltdc_set_constant_alpha(LTDC_LAYER_2, 0);
|
||
|
// ltdc_set_blending_factors(LTDC_LAYER_2, LTDC_LxBFCR_BF1_CONST_ALPHA, LTDC_LxBFCR_BF2_CONST_ALPHA);
|
||
|
ltdc_set_fbuffer_address(LTDC_LAYER_2, Overlay_address);
|
||
|
ltdc_set_fb_line_length(LTDC_LAYER_2, xres * (bits_per_pixel/8), xres * (bits_per_pixel/8));
|
||
|
ltdc_set_fb_line_count(LTDC_LAYER_2, yres);
|
||
|
/* Enable layer 2 */
|
||
|
ltdc_layer_ctrl_enable(LTDC_LAYER_2, LTDC_LxCR_LAYER_ENABLE);
|
||
|
#endif
|
||
|
|
||
|
/* Enable layer 1 */
|
||
|
ltdc_layer_ctrl_enable(LTDC_LAYER_1, LTDC_LxCR_LAYER_ENABLE);
|
||
|
|
||
|
|
||
|
/* Sets the Reload type */
|
||
|
ltdc_reload(LTDC_SRCR_IMR);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief LCD Configuration.
|
||
|
* @note This function Configure tha LTDC peripheral :
|
||
|
* 1) Configure the Pixel Clock for the LCD
|
||
|
* 2) Configure the LTDC Timing and Polarity
|
||
|
* 3) Configure the LTDC Layer 1 :
|
||
|
* - The frame buffer is located at FLASH memory
|
||
|
* - The Layer size configuration : 480x272
|
||
|
* @retval
|
||
|
* None
|
||
|
*/
|
||
|
static void ltdc_config(void)
|
||
|
{
|
||
|
/* LTDC Initialization */
|
||
|
ltdc_ctrl_disable(LTDC_GCR_HSPOL_ACTIVE_HIGH); /* Active Low Horizontal Sync */
|
||
|
ltdc_ctrl_disable(LTDC_GCR_VSPOL_ACTIVE_HIGH); /* Active Low Vertical Sync */
|
||
|
ltdc_ctrl_disable(LTDC_GCR_DEPOL_ACTIVE_HIGH); /* Active Low Date Enable */
|
||
|
ltdc_ctrl_disable(LTDC_GCR_PCPOL_ACTIVE_HIGH); /* Active Low Pixel Clock */
|
||
|
|
||
|
/* Configure the LTDC */
|
||
|
ltdc_set_tft_sync_timings(RK043FN48H_HSYNC, RK043FN48H_VSYNC,
|
||
|
RK043FN48H_HBP, RK043FN48H_VBP,
|
||
|
RK043FN48H_WIDTH, RK043FN48H_HEIGHT,
|
||
|
RK043FN48H_HFP, RK043FN48H_VFP);
|
||
|
|
||
|
ltdc_set_background_color(0, 0, 0);
|
||
|
ltdc_ctrl_enable(LTDC_GCR_LTDC_ENABLE);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void ltdc_clock(void)
|
||
|
{
|
||
|
/* LCD clock configuration */
|
||
|
/* PLLSAI_VCO Input = HSE_VALUE/PLL_M = 1 Mhz */
|
||
|
/* PLLSAI_VCO Output = PLLSAI_VCO Input * PLLSAIN = 192 Mhz */
|
||
|
/* PLLLCDCLK = PLLSAI_VCO Output/PLLSAIR = 192/5 = 38.4 Mhz */
|
||
|
/* LTDC clock frequency = PLLLCDCLK / LTDC_PLLSAI_DIVR_4 = 38.4/4 = 9.6Mhz */
|
||
|
|
||
|
/* Disable PLLSAI */
|
||
|
RCC_CR &= ~RCC_CR_PLLSAION;
|
||
|
while((RCC_CR & (RCC_CR_PLLSAIRDY))) {};
|
||
|
|
||
|
/* N and R are needed,
|
||
|
* P and Q are not needed for LTDC */
|
||
|
RCC_PLLSAICFGR &= ~RCC_PLLSAICFGR_PLLSAIN_MASK;
|
||
|
RCC_PLLSAICFGR |= 192 << RCC_PLLSAICFGR_PLLSAIN_SHIFT;
|
||
|
RCC_PLLSAICFGR &= ~RCC_PLLSAICFGR_PLLSAIR_MASK;
|
||
|
RCC_PLLSAICFGR |= 5 << RCC_PLLSAICFGR_PLLSAIR_SHIFT;
|
||
|
RCC_DCKCFGR1 &= ~RCC_DCKCFGR1_PLLSAIDIVR_MASK;
|
||
|
RCC_DCKCFGR1 |= RCC_DCKCFGR1_PLLSAIDIVR_DIVR_4;
|
||
|
|
||
|
/* Enable PLLSAI */
|
||
|
RCC_CR |= RCC_CR_PLLSAION;
|
||
|
while(!(RCC_CR & (RCC_CR_PLLSAIRDY))) {};
|
||
|
}
|
||
|
|
||
|
int ltdc_blank(void);
|
||
|
|
||
|
static void ltdc_screen_on(void)
|
||
|
{
|
||
|
/* Assert display enable LCD_DISP pin */
|
||
|
gpio_set(GPIOI, GPIO12);
|
||
|
/* Assert backlight LCD_BL_CTRL pin */
|
||
|
gpio_set(GPIOK, GPIO3);
|
||
|
}
|
||
|
|
||
|
void ltdc_enable_clut(void)
|
||
|
{
|
||
|
/* Disable LTDC color lookup table by setting CLUTEN bit */
|
||
|
ltdc_layer_ctrl_enable(LTDC_LAYER_1, LTDC_LxCR_CLUT_ENABLE);
|
||
|
|
||
|
/* Sets the Reload type */
|
||
|
ltdc_reload(LTDC_SRCR_IMR);
|
||
|
}
|
||
|
|
||
|
/* Only L8 CLUTs supported for now */
|
||
|
void ltdc_config_clut(uint32_t *CLUT, uint32_t size)
|
||
|
{
|
||
|
uint32_t i = 0;
|
||
|
|
||
|
for(i = 0; (i < size); i++)
|
||
|
{
|
||
|
/* Specifies the C-LUT address and RGB value */
|
||
|
LTDC_LxCLUTWR(LTDC_LAYER_1) = ((i << 24) | ((uint32_t)(*CLUT) & 0xFF) | ((uint32_t)(*CLUT) & 0xFF00) | ((uint32_t)(*CLUT) & 0xFF0000));
|
||
|
CLUT++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int ltdc_set_cmap(uint32_t *cmap)
|
||
|
{
|
||
|
ltdc_config_clut(cmap, 256);
|
||
|
ltdc_enable_clut();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void lcd_pinmux(void)
|
||
|
{
|
||
|
int i;
|
||
|
uint32_t base, pin, af;
|
||
|
|
||
|
rcc_periph_clock_enable(RCC_GPIOE);
|
||
|
rcc_periph_clock_enable(RCC_GPIOG);
|
||
|
rcc_periph_clock_enable(RCC_GPIOI);
|
||
|
rcc_periph_clock_enable(RCC_GPIOJ);
|
||
|
rcc_periph_clock_enable(RCC_GPIOK);
|
||
|
|
||
|
/* Enable the LTDC Clock */
|
||
|
rcc_periph_clock_enable(RCC_LTDC);
|
||
|
|
||
|
/* PE4 */
|
||
|
base = GPIOE;
|
||
|
pin = GPIO4;
|
||
|
af = GPIO_AF14;
|
||
|
gpio_mode_setup(base, GPIO_MODE_AF, GPIO_PUPD_NONE, pin);
|
||
|
gpio_set_af(base, af, pin);
|
||
|
|
||
|
/* PG12 */
|
||
|
base = GPIOG;
|
||
|
pin = GPIO12;
|
||
|
af = GPIO_AF9;
|
||
|
gpio_mode_setup(base, GPIO_MODE_AF, GPIO_PUPD_NONE, pin);
|
||
|
gpio_set_af(base, af, pin);
|
||
|
|
||
|
/* LTDC PI8:PI10*/
|
||
|
for (i = 8; i < 11; i++) {
|
||
|
base = GPIOI;
|
||
|
pin = (1 << i);
|
||
|
af = GPIO_AF14;
|
||
|
gpio_mode_setup(base, GPIO_MODE_AF, GPIO_PUPD_NONE, pin);
|
||
|
gpio_set_af(base, af, pin);
|
||
|
}
|
||
|
|
||
|
/* LTDC PI14:PI15 */
|
||
|
for (i = 14; i < 16; i++) {
|
||
|
base = GPIOI;
|
||
|
pin = (1 << i);
|
||
|
af = GPIO_AF14;
|
||
|
gpio_mode_setup(base, GPIO_MODE_AF, GPIO_PUPD_NONE, pin);
|
||
|
gpio_set_af(base, af, pin);
|
||
|
}
|
||
|
|
||
|
/* LTDC PJ0:PJ15 (excl. PJ12) */
|
||
|
for (i = 0; i < 16; i++) {
|
||
|
if (i != 12) {
|
||
|
base = GPIOJ;
|
||
|
pin = (1 << i);
|
||
|
af = GPIO_AF14;
|
||
|
gpio_mode_setup(base, GPIO_MODE_AF, GPIO_PUPD_NONE, pin);
|
||
|
gpio_set_af(base, af, pin);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* LTDC PK0:PK7 (excl. PK3) */
|
||
|
for (i = 0; i < 8; i++) {
|
||
|
if (i != 3) {
|
||
|
base = GPIOK;
|
||
|
pin = (1 << i);
|
||
|
af = GPIO_AF14;
|
||
|
gpio_mode_setup(base, GPIO_MODE_AF, GPIO_PUPD_NONE, pin);
|
||
|
gpio_set_af(base, af, pin);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* LCD_DISP control PI12 (output) */
|
||
|
base = GPIOI;
|
||
|
pin = GPIO12;
|
||
|
gpio_mode_setup(base, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, pin);
|
||
|
|
||
|
/* LCD_BL control PK3 (output) */
|
||
|
base = GPIOK;
|
||
|
pin = GPIO3;
|
||
|
gpio_mode_setup(base, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, pin);
|
||
|
ltdc_screen_on();
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/* DRIVER INIT */
|
||
|
void ltdc_init(void)
|
||
|
{
|
||
|
extern const uint32_t xterm_cmap[256];
|
||
|
lcd_pinmux();
|
||
|
ltdc_clock();
|
||
|
ltdc_config(); /* Configure LCD : Only one layer is used */
|
||
|
ltdc_config_layer();
|
||
|
ltdc_set_cmap(xterm_cmap);
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
int ltdc_blank(void)
|
||
|
{
|
||
|
uint32_t pixels = (xres * yres * (bits_per_pixel/8));
|
||
|
int i;
|
||
|
uint8_t *screen = (uint8_t *)Screen_address;
|
||
|
for (i = 0; i < pixels; i++) {
|
||
|
screen[i] = 0x00;
|
||
|
}
|
||
|
#if OVERLAY
|
||
|
screen = (uint8_t *)Overlay_address;
|
||
|
for (i = 0; i < pixels; i++) {
|
||
|
screen[i] = 0xFF;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifdef GTEST
|
||
|
draw_h_segment(BRIGHT(RED), 20, 20, 30);
|
||
|
draw_h_segment(BRIGHT(RED), 50, 20, 30);
|
||
|
draw_v_segment(BRIGHT(PURPLE), 20, 20, 30);
|
||
|
draw_v_segment(BRIGHT(BLUE), 50, 20, 30);
|
||
|
text_at(BRIGHT(YELLOW), 50,50,"hello world");
|
||
|
fill_area(BRIGHT(CYAN), 200, 200, 270, 400);
|
||
|
#endif
|
||
|
return 0;
|
||
|
}
|