gadget-kspconsole/stm32f7_ltdc.c
Daniele Lacamera b8a7ffcd31 Initial import
2023-11-27 15:16:45 +01:00

292 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;
}