1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-15 02:48:11 +00:00

feat(core): display driver for T3W1

[no changelog]
This commit is contained in:
tychovrahe 2024-11-25 16:10:52 +01:00 committed by TychoVrahe
parent dac6c17f73
commit 03a296f815
11 changed files with 1260 additions and 94 deletions

View File

@ -0,0 +1,458 @@
#include <trezor_bsp.h>
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <io/display.h>
#ifdef KERNEL_MODE
#include <sys/irq.h>
#include <sys/mpu.h>
#include <sys/systick.h>
#ifdef USE_BACKLIGHT
#include "../backlight/backlight_pwm.h"
#endif
#include "display_internal.h"
display_driver_t g_display_driver = {
.initialized = false,
};
static bool display_pll_init(void) {
RCC_PeriphCLKInitTypeDef PLL3InitPeriph = {0};
/* Start and configure PLL3 */
/* HSE = 16/32MHZ */
/* 16/32/(M=8) = 4MHz input (min) */
/* 4*(N=125) = 500MHz VCO (almost max) */
/* 500/(P=8) = 62.5 for DSI ie exactly the lane byte clock*/
PLL3InitPeriph.PeriphClockSelection = RCC_PERIPHCLK_DSI | RCC_PERIPHCLK_LTDC;
PLL3InitPeriph.DsiClockSelection = RCC_DSICLKSOURCE_PLL3;
PLL3InitPeriph.LtdcClockSelection = RCC_LTDCCLKSOURCE_PLL3;
#if HSE_VALUE == 32000000
PLL3InitPeriph.PLL3.PLL3M = 8;
#elif HSE_VALUE == 16000000
PLL3InitPeriph.PLL3.PLL3M = 4;
#endif
PLL3InitPeriph.PLL3.PLL3N = 125;
PLL3InitPeriph.PLL3.PLL3P = 8;
PLL3InitPeriph.PLL3.PLL3Q = 8;
PLL3InitPeriph.PLL3.PLL3R = 24;
PLL3InitPeriph.PLL3.PLL3FRACN = 0;
PLL3InitPeriph.PLL3.PLL3RGE = RCC_PLLVCIRANGE_0;
PLL3InitPeriph.PLL3.PLL3ClockOut = RCC_PLL3_DIVR | RCC_PLL3_DIVP;
PLL3InitPeriph.PLL3.PLL3Source = RCC_PLLSOURCE_HSE;
return HAL_RCCEx_PeriphCLKConfig(&PLL3InitPeriph) == HAL_OK;
}
static bool display_dsi_init(void) {
display_driver_t *drv = &g_display_driver;
RCC_PeriphCLKInitTypeDef DSIPHYInitPeriph = {0};
DSI_PLLInitTypeDef PLLInit = {0};
DSI_PHY_TimerTypeDef PhyTimers = {0};
DSI_HOST_TimeoutTypeDef HostTimeouts = {0};
/* Enable DSI clock */
__HAL_RCC_DSI_CLK_ENABLE();
/* Switch to D-PHY source clock */
/* Enable the DSI host */
drv->hlcd_dsi.Instance = DSI;
__HAL_DSI_ENABLE(&drv->hlcd_dsi);
/* Enable the DSI PLL */
__HAL_DSI_PLL_ENABLE(&drv->hlcd_dsi);
HAL_Delay(1);
/* Enable the clock lane and the digital section of the D-PHY */
drv->hlcd_dsi.Instance->PCTLR |= (DSI_PCTLR_CKE | DSI_PCTLR_DEN);
/* Set the TX escape clock division factor */
drv->hlcd_dsi.Instance->CCR = 4;
HAL_Delay(1);
/* Config DSI Clock to DSI PHY */
DSIPHYInitPeriph.PeriphClockSelection = RCC_PERIPHCLK_DSI;
DSIPHYInitPeriph.DsiClockSelection = RCC_DSICLKSOURCE_DSIPHY;
if (HAL_RCCEx_PeriphCLKConfig(&DSIPHYInitPeriph) != HAL_OK) {
return false;
}
/* Reset the TX escape clock division factor */
drv->hlcd_dsi.Instance->CCR &= ~DSI_CCR_TXECKDIV;
/* Disable the DSI PLL */
__HAL_DSI_PLL_DISABLE(&drv->hlcd_dsi);
/* Disable the DSI host */
__HAL_DSI_DISABLE(&drv->hlcd_dsi);
/* DSI initialization */
drv->hlcd_dsi.Instance = DSI;
drv->hlcd_dsi.Init.AutomaticClockLaneControl = DSI_AUTO_CLK_LANE_CTRL_DISABLE;
/* We have 1 data lane at 500Mbps => lane byte clock at 500/8 = 62,5 MHZ */
/* We want TX escape clock at around 20MHz and under 20MHz so clock division
* is set to 4 */
drv->hlcd_dsi.Init.TXEscapeCkdiv = 4;
drv->hlcd_dsi.Init.NumberOfLanes = PANEL_DSI_LANES;
drv->hlcd_dsi.Init.PHYFrequencyRange = DSI_DPHY_FRANGE_450MHZ_510MHZ;
drv->hlcd_dsi.Init.PHYLowPowerOffset = 0;
#if HSE_VALUE == 32000000
PLLInit.PLLNDIV = 62;
#elif HSE_VALUE == 16000000
PLLInit.PLLNDIV = 125;
#endif
PLLInit.PLLIDF = 4;
PLLInit.PLLODF = 2;
PLLInit.PLLVCORange = DSI_DPHY_VCO_FRANGE_800MHZ_1GHZ;
PLLInit.PLLChargePump = DSI_PLL_CHARGE_PUMP_2000HZ_4400HZ;
PLLInit.PLLTuning = DSI_PLL_LOOP_FILTER_2000HZ_4400HZ;
if (HAL_DSI_Init(&drv->hlcd_dsi, &PLLInit) != HAL_OK) {
return false;
}
if (HAL_DSI_SetGenericVCID(&drv->hlcd_dsi, 0) != HAL_OK) {
return false;
}
/* Configure the DSI for Video mode */
drv->DSIVidCfg.VirtualChannelID = 0;
drv->DSIVidCfg.HSPolarity = DSI_HSYNC_ACTIVE_HIGH;
drv->DSIVidCfg.VSPolarity = DSI_VSYNC_ACTIVE_HIGH;
drv->DSIVidCfg.DEPolarity = DSI_DATA_ENABLE_ACTIVE_HIGH;
drv->DSIVidCfg.ColorCoding = DSI_RGB888;
drv->DSIVidCfg.Mode = PANEL_DSI_MODE;
drv->DSIVidCfg.PacketSize = LCD_WIDTH;
drv->DSIVidCfg.NullPacketSize = 0xFFFU;
drv->DSIVidCfg.HorizontalSyncActive = HSYNC * 3;
drv->DSIVidCfg.HorizontalBackPorch = HBP * 3;
drv->DSIVidCfg.HorizontalLine = (HACT + HSYNC + HBP + HFP) * 3;
drv->DSIVidCfg.VerticalSyncActive = VSYNC;
drv->DSIVidCfg.VerticalBackPorch = VBP;
drv->DSIVidCfg.VerticalFrontPorch = VFP;
drv->DSIVidCfg.VerticalActive = VACT;
drv->DSIVidCfg.LPCommandEnable = DSI_LP_COMMAND_ENABLE;
drv->DSIVidCfg.LPLargestPacketSize = 64;
/* Specify for each region of the video frame, if the transmission of command
* in LP mode is allowed in this region */
/* while streaming is active in video mode */
drv->DSIVidCfg.LPHorizontalFrontPorchEnable = DSI_LP_HFP_ENABLE;
drv->DSIVidCfg.LPHorizontalBackPorchEnable = DSI_LP_HBP_ENABLE;
drv->DSIVidCfg.LPVerticalActiveEnable = DSI_LP_VACT_ENABLE;
drv->DSIVidCfg.LPVerticalFrontPorchEnable = DSI_LP_VFP_ENABLE;
drv->DSIVidCfg.LPVerticalBackPorchEnable = DSI_LP_VBP_ENABLE;
drv->DSIVidCfg.LPVerticalSyncActiveEnable = DSI_LP_VSYNC_ENABLE;
drv->DSIVidCfg.FrameBTAAcknowledgeEnable = DSI_FBTAA_ENABLE;
drv->DSIVidCfg.LooselyPacked = DSI_LOOSELY_PACKED_DISABLE;
/* Drive the display */
if (HAL_DSI_ConfigVideoMode(&drv->hlcd_dsi, &drv->DSIVidCfg) != HAL_OK) {
return false;
}
/*********************/
/* LCD configuration */
/*********************/
PhyTimers.ClockLaneHS2LPTime = 11;
PhyTimers.ClockLaneLP2HSTime = 40;
PhyTimers.DataLaneHS2LPTime = 12;
PhyTimers.DataLaneLP2HSTime = 23;
PhyTimers.DataLaneMaxReadTime = 0;
PhyTimers.StopWaitTime = 7;
if (HAL_DSI_ConfigPhyTimer(&drv->hlcd_dsi, &PhyTimers)) {
return false;
}
HostTimeouts.TimeoutCkdiv = 1;
HostTimeouts.HighSpeedTransmissionTimeout = 0;
HostTimeouts.LowPowerReceptionTimeout = 0;
HostTimeouts.HighSpeedReadTimeout = 0;
HostTimeouts.LowPowerReadTimeout = 0;
HostTimeouts.HighSpeedWriteTimeout = 0;
HostTimeouts.HighSpeedWritePrespMode = 0;
HostTimeouts.LowPowerWriteTimeout = 0;
HostTimeouts.BTATimeout = 0;
if (HAL_DSI_ConfigHostTimeouts(&drv->hlcd_dsi, &HostTimeouts) != HAL_OK) {
return false;
}
if (HAL_DSI_ConfigFlowControl(&drv->hlcd_dsi, DSI_FLOW_CONTROL_BTA) !=
HAL_OK) {
return false;
}
/* Enable the DSI host */
__HAL_DSI_ENABLE(&drv->hlcd_dsi);
return true;
}
static bool display_ltdc_config_layer(LTDC_HandleTypeDef *hltdc,
uint32_t fb_addr) {
LTDC_LayerCfgTypeDef LayerCfg = {0};
/* LTDC layer configuration */
LayerCfg.WindowX0 = 0;
LayerCfg.WindowX1 = LCD_WIDTH;
LayerCfg.WindowY0 = LCD_Y_OFFSET;
LayerCfg.WindowY1 = LCD_HEIGHT + LCD_Y_OFFSET;
LayerCfg.PixelFormat = PANEL_LTDC_PIXEL_FORMAT;
LayerCfg.Alpha = 0xFF; /* NU default value */
LayerCfg.Alpha0 = 0; /* NU default value */
LayerCfg.BlendingFactor1 =
LTDC_BLENDING_FACTOR1_PAxCA; /* Not Used: default value */
LayerCfg.BlendingFactor2 =
LTDC_BLENDING_FACTOR2_PAxCA; /* Not Used: default value */
LayerCfg.FBStartAdress = fb_addr;
LayerCfg.ImageWidth =
FRAME_BUFFER_PIXELS_PER_LINE; /* Number of pixels per line in virtual
frame buffer */
LayerCfg.ImageHeight = LCD_HEIGHT;
LayerCfg.Backcolor.Red = 0; /* Not Used: default value */
LayerCfg.Backcolor.Green = 0; /* Not Used: default value */
LayerCfg.Backcolor.Blue = 0; /* Not Used: default value */
LayerCfg.Backcolor.Reserved = 0xFF;
return HAL_LTDC_ConfigLayer(hltdc, &LayerCfg, LTDC_LAYER_1) == HAL_OK;
}
static bool display_ltdc_init(uint32_t fb_addr) {
display_driver_t *drv = &g_display_driver;
__HAL_RCC_LTDC_CLK_ENABLE();
/* LTDC initialization */
drv->hlcd_ltdc.Instance = LTDC;
drv->hlcd_ltdc.Init.HSPolarity = LTDC_HSPOLARITY_AL;
drv->hlcd_ltdc.Init.VSPolarity = LTDC_VSPOLARITY_AL;
drv->hlcd_ltdc.Init.DEPolarity = LTDC_DEPOLARITY_AL;
drv->hlcd_ltdc.Init.PCPolarity = LTDC_PCPOLARITY_IPC;
drv->hlcd_ltdc.Init.HorizontalSync = HSYNC - 1;
drv->hlcd_ltdc.Init.AccumulatedHBP = HSYNC + HBP - 1;
drv->hlcd_ltdc.Init.AccumulatedActiveW = HACT + HBP + HSYNC - 1;
drv->hlcd_ltdc.Init.TotalWidth = HACT + HBP + HFP + HSYNC - 1;
drv->hlcd_ltdc.Init.Backcolor.Red = 0; /* Not used default value */
drv->hlcd_ltdc.Init.Backcolor.Green = 0; /* Not used default value */
drv->hlcd_ltdc.Init.Backcolor.Blue = 0; /* Not used default value */
drv->hlcd_ltdc.Init.Backcolor.Reserved = 0xFF;
if (HAL_LTDCEx_StructInitFromVideoConfig(&drv->hlcd_ltdc, &drv->DSIVidCfg) !=
HAL_OK) {
return false;
}
if (HAL_LTDC_Init(&drv->hlcd_ltdc) != HAL_OK) {
return false;
}
return display_ltdc_config_layer(&drv->hlcd_ltdc, fb_addr);
}
bool display_set_fb(uint32_t fb_addr) {
display_driver_t *drv = &g_display_driver;
return display_ltdc_config_layer(&drv->hlcd_ltdc, fb_addr);
}
// Fully initializes the display controller.
void display_init(display_content_mode_t mode) {
display_driver_t *drv = &g_display_driver;
GPIO_InitTypeDef GPIO_InitStructure = {0};
__HAL_RCC_DSI_FORCE_RESET();
__HAL_RCC_LTDC_FORCE_RESET();
#ifdef DISPLAY_PWREN_PIN
DISPLAY_PWREN_CLK_ENA();
HAL_GPIO_WritePin(DISPLAY_PWREN_PORT, DISPLAY_PWREN_PIN, GPIO_PIN_RESET);
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_LOW;
GPIO_InitStructure.Pin = DISPLAY_PWREN_PIN;
HAL_GPIO_Init(DISPLAY_PWREN_PORT, &GPIO_InitStructure);
#endif
#ifdef DISPLAY_RESET_PIN
DISPLAY_RESET_CLK_ENA();
HAL_GPIO_WritePin(GPIOE, DISPLAY_RESET_PIN, GPIO_PIN_RESET);
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_LOW;
GPIO_InitStructure.Pin = DISPLAY_RESET_PIN;
HAL_GPIO_Init(DISPLAY_RESET_PORT, &GPIO_InitStructure);
systick_delay_ms(10);
HAL_GPIO_WritePin(DISPLAY_RESET_PORT, DISPLAY_RESET_PIN, GPIO_PIN_SET);
systick_delay_ms(120);
#endif
#ifdef DISPLAY_BACKLIGHT_PIN
DISPLAY_BACKLIGHT_CLK_ENABLE();
/* Configure LCD Backlight Pin */
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Pin = DISPLAY_BACKLIGHT_PIN;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(DISPLAY_BACKLIGHT_PORT, &GPIO_InitStructure);
#endif
#ifdef USE_BACKLIGHT
backlight_pwm_init(BACKLIGHT_RESET);
#endif
uint32_t fb_addr = display_fb_init();
__HAL_RCC_LTDC_RELEASE_RESET();
__HAL_RCC_DSI_RELEASE_RESET();
#ifdef DISPLAY_GFXMMU
display_gfxmmu_init(drv);
#endif
if (!display_pll_init()) {
goto cleanup;
}
if (!display_dsi_init()) {
goto cleanup;
}
if (!display_ltdc_init(fb_addr)) {
goto cleanup;
}
/* Start DSI */
if (HAL_DSI_Start(&drv->hlcd_dsi) != HAL_OK) {
goto cleanup;
}
if (!panel_init(drv)) {
goto cleanup;
}
if (HAL_LTDC_ProgramLineEvent(&drv->hlcd_ltdc, LCD_HEIGHT) != HAL_OK) {
goto cleanup;
}
/* Enable LTDC interrupt */
NVIC_SetPriority(LTDC_IRQn, IRQ_PRI_NORMAL);
NVIC_EnableIRQ(LTDC_IRQn);
NVIC_SetPriority(LTDC_ER_IRQn, IRQ_PRI_NORMAL);
NVIC_EnableIRQ(LTDC_ER_IRQn);
__HAL_LTDC_ENABLE_IT(&drv->hlcd_ltdc, LTDC_IT_LI | LTDC_IT_FU | LTDC_IT_TE);
drv->initialized = true;
return;
cleanup:
NVIC_DisableIRQ(LTDC_IRQn);
NVIC_DisableIRQ(LTDC_ER_IRQn);
__HAL_RCC_DSI_FORCE_RESET();
__HAL_RCC_LTDC_FORCE_RESET();
__HAL_RCC_LTDC_RELEASE_RESET();
__HAL_RCC_DSI_RELEASE_RESET();
#ifdef DISPLAY_GFXMMU
__HAL_RCC_GFXMMU_FORCE_RESET();
__HAL_RCC_GFXMMU_RELEASE_RESET();
#endif
drv->initialized = false;
}
int display_set_backlight(int level) {
display_driver_t *drv = &g_display_driver;
if (!drv->initialized) {
return 0;
}
#ifdef USE_BACKLIGHT
if (level > backlight_pwm_get()) {
display_ensure_refreshed();
}
return backlight_pwm_set(level);
#else
// Just emulation, not doing anything
drv->backlight_level = level;
return level;
#endif
}
int display_get_backlight(void) {
display_driver_t *drv = &g_display_driver;
if (!drv->initialized) {
return 0;
}
#ifdef USE_BACKLIGHT
return backlight_pwm_get();
#else
return drv->backlight_level;
#endif
}
int display_set_orientation(int angle) { return angle; }
int display_get_orientation(void) { return 0; }
void LTDC_IRQHandler(void) {
IRQ_LOG_ENTER();
mpu_mode_t mode = mpu_reconfig(MPU_MODE_DEFAULT);
display_driver_t *drv = &g_display_driver;
if (drv->hlcd_ltdc.State != HAL_LTDC_STATE_RESET) {
HAL_LTDC_IRQHandler(&drv->hlcd_ltdc);
} else {
LTDC->ICR = 0x3F;
}
mpu_restore(mode);
IRQ_LOG_EXIT();
}
void LTDC_ER_IRQHandler(void) {
IRQ_LOG_ENTER();
mpu_mode_t mode = mpu_reconfig(MPU_MODE_DEFAULT);
display_driver_t *drv = &g_display_driver;
if (drv->hlcd_ltdc.State != HAL_LTDC_STATE_RESET) {
HAL_LTDC_IRQHandler(&drv->hlcd_ltdc);
} else {
LTDC->ICR = 0x3F;
}
mpu_restore(mode);
IRQ_LOG_EXIT();
}
void display_deinit(display_content_mode_t mode) {
display_driver_t *drv = &g_display_driver;
if (!drv->initialized) {
return;
}
// todo
NVIC_DisableIRQ(LTDC_IRQn);
NVIC_DisableIRQ(LTDC_ER_IRQn);
}
#endif

View File

@ -0,0 +1,226 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef KERNEL_MODE
#include <trezor_bsp.h>
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <io/display.h>
#include <sys/irq.h>
#include <sys/mpu.h>
#include <sys/trustzone.h>
#include "display_internal.h"
#define ALIGNED_PHYSICAL_FRAME_BUFFER_SIZE \
ALIGN_UP_CONST(PHYSICAL_FRAME_BUFFER_SIZE, PHYSICAL_FRAME_BUFFER_ALIGNMENT)
// Physical frame buffers in internal SRAM memory.
// Both frame buffers layers in the fixed addresses that
// are shared between bootloaders and the firmware.
__attribute__((section(".fb1"), aligned(PHYSICAL_FRAME_BUFFER_ALIGNMENT)))
uint8_t physical_frame_buffer_0[ALIGNED_PHYSICAL_FRAME_BUFFER_SIZE];
#if (FRAME_BUFFER_COUNT > 1)
__attribute__((section(".fb2"), aligned(PHYSICAL_FRAME_BUFFER_ALIGNMENT)))
uint8_t physical_frame_buffer_1[ALIGNED_PHYSICAL_FRAME_BUFFER_SIZE];
#endif
#ifdef USE_TRUSTZONE
void display_set_unpriv_access(bool unpriv) {
// To allow unprivileged access both GFXMMU virtual buffers area and
// underlying SRAM region must be configured as unprivileged.
// Order of GFXMMU and SRAM unprivileged access configuration is important
// to avoid the situation the virtual frame buffer has lower privileges
// than underlying frame buffer in physical memory so LTDC could not
// refresh the display properly.
#ifdef DISPLAY_GFXMMU
if (!unpriv) {
tz_set_gfxmmu_unpriv(unpriv);
}
#endif
tz_set_sram_unpriv((uint32_t)physical_frame_buffer_0,
PHYSICAL_FRAME_BUFFER_SIZE, unpriv);
tz_set_sram_unpriv((uint32_t)physical_frame_buffer_1,
PHYSICAL_FRAME_BUFFER_SIZE, unpriv);
#ifdef DISPLAY_GFXMMU
if (unpriv) {
tz_set_gfxmmu_unpriv(unpriv);
}
#endif
#ifdef USE_DMA2D
tz_set_dma2d_unpriv(unpriv);
#endif
}
#endif // USE_TRUSTZONE
// Returns the pointer to the physical frame buffer (0.. FRAME_BUFFER_COUNT-1)
// Returns NULL if the framebuffer index is out of range.
static uint8_t *get_fb_ptr(int16_t index) {
#ifdef DISPLAY_GFXMMU
if (index == 0) {
return (uint8_t *)GFXMMU_VIRTUAL_BUFFER0_BASE_S;
#if (FRAME_BUFFER_COUNT > 1)
} else if (index == 1) {
return (uint8_t *)GFXMMU_VIRTUAL_BUFFER1_BASE_S;
#endif
#else
if (index == 0) {
return physical_frame_buffer_0;
#if (FRAME_BUFFER_COUNT > 1)
} else if (index == 1) {
return physical_frame_buffer_1;
#endif
#endif
} else {
return NULL;
}
}
bool display_get_frame_buffer(display_fb_info_t *fb_dest) {
display_driver_t *drv = &g_display_driver;
if (!drv->initialized) {
return false;
}
#if PANEL_LTDC_PIXEL_FORMAT == LTDC_PIXEL_FORMAT_ARGB8888
#define FB_PIXEL_SIZE 4
#elif PANEL_LTDC_PIXEL_FORMAT == LTDC_PIXEL_FORMAT_RGB565
#define FB_PIXEL_SIZE 2
#endif
fb_queue_wait(&drv->empty_frames);
int16_t fb_idx = fb_queue_peek(&drv->empty_frames);
if (fb_idx < 0) {
fb_dest->ptr = NULL;
fb_dest->stride = 0;
return false;
}
uintptr_t addr = (uintptr_t)get_fb_ptr(fb_idx);
uint32_t fb_stride = FRAME_BUFFER_PIXELS_PER_LINE * FB_PIXEL_SIZE;
// We may not utilize whole area of the display
addr += (LCD_HEIGHT - DISPLAY_RESY) / 2 * FB_PIXEL_SIZE;
addr += (LCD_WIDTH - DISPLAY_RESX) / 2 * fb_stride;
display_fb_info_t fb = {
.ptr = (void *)addr,
.stride = fb_stride,
};
mpu_set_active_fb((void *)addr, VIRTUAL_FRAME_BUFFER_SIZE);
memcpy(fb_dest, &fb, sizeof(display_fb_info_t));
return true;
}
void display_refresh(void) {
display_driver_t *drv = &g_display_driver;
if (!drv->initialized) {
return;
}
if (!fb_queue_peeked(&drv->empty_frames)) {
// No refresh needed as the frame buffer is not in
// the state to be copied to the display
return;
}
fb_queue_put(&drv->ready_frames, fb_queue_take(&drv->empty_frames));
}
void display_ensure_refreshed(void) {
display_driver_t *drv = &g_display_driver;
if (!drv->initialized) {
return;
}
if (!is_mode_exception()) {
bool copy_pending;
// Wait until all frame buffers are written to the display
// so we can be sure there's not scheduled or pending
// background copying
do {
irq_key_t key = irq_lock();
copy_pending =
!fb_queue_empty(&drv->ready_frames) || drv->update_pending > 0;
irq_unlock(key);
__WFI();
} while (copy_pending);
}
}
void display_fb_clear(void) {
mpu_set_active_fb(physical_frame_buffer_0, PHYSICAL_FRAME_BUFFER_SIZE);
memset(physical_frame_buffer_0, 0, PHYSICAL_FRAME_BUFFER_SIZE);
mpu_set_active_fb(physical_frame_buffer_1, PHYSICAL_FRAME_BUFFER_SIZE);
memset(physical_frame_buffer_1, 0, PHYSICAL_FRAME_BUFFER_SIZE);
mpu_set_active_fb(NULL, 0);
}
uint32_t display_fb_init(void) {
display_fb_clear();
fb_queue_reset(&g_display_driver.empty_frames);
fb_queue_reset(&g_display_driver.ready_frames);
fb_queue_put(&g_display_driver.empty_frames, 1);
g_display_driver.active_frame = 0;
return (uint32_t)get_fb_ptr(0);
}
void HAL_LTDC_LineEvenCallback(LTDC_HandleTypeDef *hltdc) {
display_driver_t *drv = &g_display_driver;
if (!drv->initialized) {
return;
}
if (drv->update_pending > 0) {
drv->update_pending--;
}
int16_t fb_idx = fb_queue_take(&drv->ready_frames);
if (fb_idx >= 0) {
fb_queue_put(&drv->empty_frames, drv->active_frame);
drv->active_frame = fb_idx;
display_set_fb((uint32_t)get_fb_ptr(drv->active_frame));
drv->update_pending = 3;
}
HAL_LTDC_ProgramLineEvent(&drv->hlcd_ltdc, LCD_HEIGHT);
}
#endif

View File

@ -0,0 +1,64 @@
#include <trezor_bsp.h>
#include <trezor_rtl.h>
#include <gfx/gfx_bitblt.h>
#include <io/display.h>
#ifdef KERNEL_MODE
void display_copy_rgb565(const gfx_bitblt_t *bb) {
display_fb_info_t fb;
if (!display_get_frame_buffer(&fb)) {
return;
}
gfx_bitblt_t bb_new = *bb;
bb_new.dst_row = (uint8_t *)fb.ptr + (fb.stride * bb_new.dst_y);
bb_new.dst_stride = fb.stride;
gfx_rgb565_copy_rgb565(&bb_new);
}
void display_fill(const gfx_bitblt_t *bb) {
display_fb_info_t fb;
if (!display_get_frame_buffer(&fb)) {
return;
}
gfx_bitblt_t bb_new = *bb;
bb_new.dst_row = (uint8_t *)fb.ptr + (fb.stride * bb_new.dst_y);
bb_new.dst_stride = fb.stride;
gfx_rgb565_fill(&bb_new);
}
void display_copy_mono1p(const gfx_bitblt_t *bb) {
display_fb_info_t fb;
if (!display_get_frame_buffer(&fb)) {
return;
}
gfx_bitblt_t bb_new = *bb;
bb_new.dst_row = (uint8_t *)fb.ptr + (fb.stride * bb_new.dst_y);
bb_new.dst_stride = fb.stride;
gfx_rgb565_copy_mono1p(&bb_new);
}
void display_copy_mono4(const gfx_bitblt_t *bb) {
display_fb_info_t fb;
if (!display_get_frame_buffer(&fb)) {
return;
}
gfx_bitblt_t bb_new = *bb;
bb_new.dst_row = (uint8_t *)fb.ptr + (fb.stride * bb_new.dst_y);
bb_new.dst_stride = fb.stride;
gfx_rgb565_copy_mono4(&bb_new);
}
#endif

View File

@ -0,0 +1,65 @@
#ifdef KERNEL_MODE
#include <trezor_bsp.h>
#include <trezor_model.h>
#include <gfx/gfx_bitblt.h>
#include <io/display.h>
void display_fill(const gfx_bitblt_t *bb) {
display_fb_info_t fb;
if (!display_get_frame_buffer(&fb)) {
return;
}
gfx_bitblt_t bb_new = *bb;
bb_new.dst_row = (uint8_t *)fb.ptr + (fb.stride * bb_new.dst_y);
bb_new.dst_stride = fb.stride;
gfx_rgba8888_fill(&bb_new);
}
void display_copy_rgb565(const gfx_bitblt_t *bb) {
display_fb_info_t fb;
if (!display_get_frame_buffer(&fb)) {
return;
}
gfx_bitblt_t bb_new = *bb;
bb_new.dst_row = (uint8_t *)fb.ptr + (fb.stride * bb_new.dst_y);
bb_new.dst_stride = fb.stride;
gfx_rgba8888_copy_rgb565(&bb_new);
}
void display_copy_mono1p(const gfx_bitblt_t *bb) {
display_fb_info_t fb;
if (!display_get_frame_buffer(&fb)) {
return;
}
gfx_bitblt_t bb_new = *bb;
bb_new.dst_row = (uint8_t *)fb.ptr + (fb.stride * bb_new.dst_y);
bb_new.dst_stride = fb.stride;
gfx_rgba8888_copy_mono1p(&bb_new);
}
void display_copy_mono4(const gfx_bitblt_t *bb) {
display_fb_info_t fb;
if (!display_get_frame_buffer(&fb)) {
return;
}
gfx_bitblt_t bb_new = *bb;
bb_new.dst_row = (uint8_t *)fb.ptr + (fb.stride * bb_new.dst_y);
bb_new.dst_stride = fb.stride;
gfx_rgba8888_copy_mono4(&bb_new);
}
#endif

View File

@ -0,0 +1,66 @@
#ifdef KERNEL_MODE
#include <trezor_bsp.h>
#include <sys/irq.h>
#include "display_internal.h"
extern uint8_t physical_frame_buffer_0[PHYSICAL_FRAME_BUFFER_SIZE];
extern uint8_t physical_frame_buffer_1[PHYSICAL_FRAME_BUFFER_SIZE];
extern const uint32_t gfxmmu_lut_config[2 * GFXMMU_LUT_SIZE];
bool display_gfxmmu_init(display_driver_t *drv) {
__HAL_RCC_GFXMMU_FORCE_RESET();
__HAL_RCC_GFXMMU_RELEASE_RESET();
/* GFXMMU clock enable */
__HAL_RCC_GFXMMU_CLK_ENABLE();
/* GFXMMU peripheral initialization */
drv->hlcd_gfxmmu.Instance = GFXMMU;
drv->hlcd_gfxmmu.Init.BlocksPerLine = GFXMMU_192BLOCKS;
drv->hlcd_gfxmmu.Init.DefaultValue = 0xFFFFFFFFU;
drv->hlcd_gfxmmu.Init.Buffers.Buf0Address = (uint32_t)physical_frame_buffer_0;
drv->hlcd_gfxmmu.Init.Buffers.Buf1Address = (uint32_t)physical_frame_buffer_1;
drv->hlcd_gfxmmu.Init.Buffers.Buf2Address = 0;
drv->hlcd_gfxmmu.Init.Buffers.Buf3Address = 0;
#if defined(GFXMMU_CR_CE)
drv->hlcd_gfxmmu.Init.CachePrefetch.Activation = DISABLE;
drv->hlcd_gfxmmu.Init.CachePrefetch.CacheLock = GFXMMU_CACHE_LOCK_DISABLE;
drv->hlcd_gfxmmu.Init.CachePrefetch.CacheLockBuffer =
GFXMMU_CACHE_LOCK_BUFFER0; /* NU */
drv->hlcd_gfxmmu.Init.CachePrefetch.CacheForce =
GFXMMU_CACHE_FORCE_ENABLE; /* NU */
drv->hlcd_gfxmmu.Init.CachePrefetch.OutterBufferability =
GFXMMU_OUTTER_BUFFERABILITY_DISABLE;
drv->hlcd_gfxmmu.Init.CachePrefetch.OutterCachability =
GFXMMU_OUTTER_CACHABILITY_DISABLE;
drv->hlcd_gfxmmu.Init.CachePrefetch.Prefetch = GFXMMU_PREFETCH_DISABLE;
#endif /* GFXMMU_CR_CE */
#if defined(GFXMMU_CR_ACE)
drv->hlcd_gfxmmu.Init.AddressCache.Activation = DISABLE;
drv->hlcd_gfxmmu.Init.AddressCache.AddressCacheLockBuffer =
GFXMMU_ADDRESSCACHE_LOCK_BUFFER0;
#endif /* GFXMMU_CR_ACE */
drv->hlcd_gfxmmu.Init.Interrupts.Activation = DISABLE;
drv->hlcd_gfxmmu.Init.Interrupts.UsedInterrupts =
GFXMMU_AHB_MASTER_ERROR_IT; /* NU */
if (HAL_GFXMMU_Init(&drv->hlcd_gfxmmu) != HAL_OK) {
return false;
}
/* Initialize LUT */
if (HAL_GFXMMU_ConfigLut(&drv->hlcd_gfxmmu, 0, LCD_HEIGHT,
(uint32_t)&gfxmmu_lut_config) != HAL_OK) {
return false;
}
if (HAL_GFXMMU_DisableLutLines(&drv->hlcd_gfxmmu, LCD_HEIGHT,
1024 - LCD_HEIGHT) != HAL_OK) {
return false;
}
return true;
}
#endif

View File

@ -0,0 +1,87 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TREZOR_HAL_DISPLAY_INTERNAL_H
#define TREZOR_HAL_DISPLAY_INTERNAL_H
#include <trezor_bsp.h>
#include <trezor_types.h>
#include "../fb_queue/fb_queue.h"
#ifdef DISPLAY_PANEL_LX200D2406A
#include "panels/lx200d2406a/lx200d2406a.h"
#elif defined DISPLAY_PANEL_STM32U5A9J_DK
#include "panels/stm32u5a9j-dk/stm32u5a9j-dk.h"
#endif
// Hardware requires physical frame buffer alignment
#ifdef USE_TRUSTZONE
#define PHYSICAL_FRAME_BUFFER_ALIGNMENT TZ_SRAM_ALIGNMENT
#else
#define PHYSICAL_FRAME_BUFFER_ALIGNMENT 32
#endif
typedef struct {
bool initialized;
uint16_t update_pending;
fb_queue_t empty_frames;
fb_queue_t ready_frames;
int16_t active_frame;
// Current display orientation (0, 90, 180, 270)
int orientation_angle;
// Current backlight level ranging from 0 to 255
int backlight_level;
// The current frame buffer selector
DSI_HandleTypeDef hlcd_dsi;
LTDC_HandleTypeDef hlcd_ltdc;
DSI_VidCfgTypeDef DSIVidCfg;
#ifdef DISPLAY_GFXMMU
GFXMMU_HandleTypeDef hlcd_gfxmmu;
#endif
} display_driver_t;
extern display_driver_t g_display_driver;
bool display_set_fb(uint32_t fb_addr);
void display_fb_clear(void);
uint32_t display_fb_init(void);
static inline uint32_t is_mode_exception(void) {
uint32_t isr_number = __get_IPSR() & IPSR_ISR_Msk;
// Check if the ISR number is not 0 (thread mode) or 11 (SVCall)
return (isr_number != 0) && (isr_number != 11);
}
void display_ensure_refreshed(void);
bool panel_init(display_driver_t *drv);
#ifdef DISPLAY_GFXMMU
bool display_gfxmmu_init(display_driver_t *drv);
#endif
#endif // TREZOR_HAL_DISPLAY_INTERNAL_H

View File

@ -0,0 +1,233 @@
#include <trezor_bsp.h>
#include <trezor_rtl.h>
#include <sys/systick.h>
#include "lx200d2406a.h"
#include "../../display_internal.h"
// todo static assert resolution
bool panel_init(display_driver_t *drv) {
HAL_StatusTypeDef ret;
ret = HAL_DSI_ShortWrite(&drv->hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P0, 0x11,
0);
if (ret != HAL_OK) {
return false;
}
systick_delay_ms(120);
ret = HAL_DSI_ShortWrite(&drv->hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, 0x36,
0x00);
if (ret != HAL_OK) {
return false;
}
ret = HAL_DSI_ShortWrite(&drv->hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, 0x3A,
0x06);
if (ret != HAL_OK) {
return false;
}
// mipi video mode
ret = HAL_DSI_ShortWrite(&drv->hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, 0xB0,
0x10);
if (ret != HAL_OK) {
return false;
}
// Write(Command , 0xB2);
// Write(Parameter , 0x00);
// Write(Parameter , 0x0C);
// Write(Parameter , 0x00);
// Write(Parameter , 0x0C);
// Write(Parameter , 0x00);
// Write(Parameter , 0x00);
// Write(Parameter , 0x00);
// Write(Parameter , 0x33);
// Write(Parameter , 0x00);
// Write(Parameter , 0x33);
ret = HAL_DSI_LongWrite(
&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 10, 0xB2,
(uint8_t[]){0x00, 0x0c, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x33, 0x00, 0x33});
if (ret != HAL_OK) {
return false;
}
// Write(Command , 0xB7);
// Write(Parameter , 0x00);
// Write(Parameter , 0x06);
ret = HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 2, 0xB7,
(uint8_t[]){0x00, 0x06});
if (ret != HAL_OK) {
return false;
}
// Write(Command , 0xBB);
// Write(Parameter , 0x00);
// Write(Parameter , 0x1E);
ret = HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 2, 0xBB,
(uint8_t[]){0x00, 0x1E});
if (ret != HAL_OK) {
return false;
}
// Write(Command , 0xC0);
// Write(Parameter , 0x00);
// Write(Parameter , 0x2C);
ret = HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 2, 0xC0,
(uint8_t[]){0x00, 0x2C});
if (ret != HAL_OK) {
return false;
}
// Write(Command , 0xC2);
// Write(Parameter , 0x00);
// Write(Parameter , 0x01);
ret = HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 2, 0xC2,
(uint8_t[]){0x00, 0x01});
if (ret != HAL_OK) {
return false;
}
// Write(Command , 0xC3);
// Write(Parameter , 0x00);
// Write(Parameter , 0x0F);
ret = HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 2, 0xC3,
(uint8_t[]){0x00, 0x0F});
if (ret != HAL_OK) {
return false;
}
// Write(Command , 0xC6);
// Write(Parameter , 0x00);
// Write(Parameter , 0x0F);
ret = HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 2, 0xC6,
(uint8_t[]){0x00, 0x0F});
if (ret != HAL_OK) {
return false;
}
// Write(Command , 0xD0);
// Write(Parameter , 0x00);
// Write(Parameter , 0xA7);
ret = HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 2, 0xD0,
(uint8_t[]){0x00, 0xA7});
if (ret != HAL_OK) {
return false;
}
// Write(Command , 0xD0);
// Write(Parameter , 0x00);
// Write(Parameter , 0xA4);
// Write(Parameter , 0x00);
// Write(Parameter , 0xA1);
ret = HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 4, 0xD0,
(uint8_t[]){0x00, 0xA4, 0x00, 0xA1});
if (ret != HAL_OK) {
return false;
}
// Write(Command , 0xD6);
// Write(Parameter , 0x00);
// Write(Parameter , 0xA1);
ret = HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 2, 0xD6,
(uint8_t[]){0x00, 0xA1});
if (ret != HAL_OK) {
return false;
}
// Write(Command , 0xE0);
// Write(Parameter , 0x00);
// Write(Parameter , 0xF0);
// Write(Parameter , 0x00);
// Write(Parameter , 0x06);
// Write(Parameter , 0x00);
// Write(Parameter , 0x11);
// Write(Parameter , 0x00);
// Write(Parameter , 0x09);
// Write(Parameter , 0x00);
// Write(Parameter , 0x0A);
// Write(Parameter , 0x00);
// Write(Parameter , 0x28);
// Write(Parameter , 0x00);
// Write(Parameter , 0x37);
// Write(Parameter , 0x00);
// Write(Parameter , 0x44);
// Write(Parameter , 0x00);
// Write(Parameter , 0x4E);
// Write(Parameter , 0x00);
// Write(Parameter , 0x39);
// Write(Parameter , 0x00);
// Write(Parameter , 0x14);
// Write(Parameter , 0x00);
// Write(Parameter , 0x15);
// Write(Parameter , 0x00);
// Write(Parameter , 0x34);
// Write(Parameter , 0x00);
// Write(Parameter , 0x3A);
// ret = HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 28,
// 0xE0, (uint8_t[]){0x00, 0xF0, 0x00, 0x06, 0x00, 0x11, 0x00, 0x09, 0x00,
// 0x0A, 0x00, 0x28, 0x00, 0x37, 0x00, 0x44, 0x00, 0x4E, 0x00, 0x39, 0x00,
// 0x14, 0x00, 0x15, 0x00, 0x34, 0x00, 0x3A}); if (ret != HAL_OK) {
// return false;
// }
// Write(Command , 0xE1);
// Write(Parameter , 0x00);
// Write(Parameter , 0xF0);
// Write(Parameter , 0x00);
// Write(Parameter , 0x0E);
// Write(Parameter , 0x00);
// Write(Parameter , 0x0F);
// Write(Parameter , 0x00);
// Write(Parameter , 0x0A);
// Write(Parameter , 0x00);
// Write(Parameter , 0x08);
// Write(Parameter , 0x00);
// Write(Parameter , 0x04);
// Write(Parameter , 0x00);
// Write(Parameter , 0x37);
// Write(Parameter , 0x00);
// Write(Parameter , 0x43);
// Write(Parameter , 0x00);
// Write(Parameter , 0x4D);
// Write(Parameter , 0x00);
// Write(Parameter , 0x35);
// Write(Parameter , 0x00);
// Write(Parameter , 0x12);
// Write(Parameter , 0x00);
// Write(Parameter , 0x13);
// Write(Parameter , 0x00);
// Write(Parameter , 0x32);
// Write(Parameter , 0x00);
// Write(Parameter , 0x39);
// ret = HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 28,
// 0xE1, (uint8_t[]){0x00, 0xF0, 0x00, 0x0E, 0x00, 0x0F, 0x00, 0x0A, 0x00,
// 0x08, 0x00, 0x04, 0x00, 0x37, 0x00, 0x43, 0x00, 0x4D, 0x00, 0x35, 0x00,
// 0x12, 0x00, 0x13, 0x00, 0x32, 0x00, 0x39}); if (ret != HAL_OK) {
// return false;
// }
ret = HAL_DSI_ShortWrite(&drv->hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P0, 0x21,
0);
if (ret != HAL_OK) {
return false;
}
ret = HAL_DSI_ShortWrite(&drv->hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P0, 0x29,
0);
if (ret != HAL_OK) {
return false;
}
ret = HAL_DSI_ShortWrite(&drv->hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P0, 0x2C,
0);
if (ret != HAL_OK) {
return false;
}
return true;
}

View File

@ -0,0 +1,49 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <trezor_types.h>
#define VSYNC 4
#define VBP 4
#define VFP 660
#define VACT 320
#define HSYNC 30
#define HBP 60
#define HFP 60
#define HACT 240
#define LCD_WIDTH 240
#define LCD_HEIGHT 320
#define LCD_Y_OFFSET 0
#define PANEL_DSI_MODE DSI_VID_MODE_NB_PULSES
#define PANEL_DSI_LANES DSI_ONE_DATA_LANE
#define PANEL_LTDC_PIXEL_FORMAT LTDC_PIXEL_FORMAT_RGB565
// Size of the physical frame buffer in bytes
//
// It's smaller than size of the virtual frame buffer
// due to used GFXMMU settings
#define PHYSICAL_FRAME_BUFFER_SIZE (240 * 320 * 2)
#define VIRTUAL_FRAME_BUFFER_SIZE PHYSICAL_FRAME_BUFFER_SIZE
// Pitch (in pixels) of the virtual frame buffer
#define FRAME_BUFFER_PIXELS_PER_LINE 240

View File

@ -1,93 +0,0 @@
#include <sys/systick.h>
#ifdef KERNEL_MODE
#include <trezor_bsp.h>
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <io/display.h>
#include <sys/mpu.h>
#include <sys/trustzone.h>
#include "../backlight/backlight_pwm.h"
// Hardware requires physical frame buffer alignment
#ifdef USE_TRUSTZONE
#define PHYSICAL_FRAME_BUFFER_ALIGNMENT TZ_SRAM_ALIGNMENT
#else
#define PHYSICAL_FRAME_BUFFER_ALIGNMENT 32
#endif
// Size of the physical frame buffer in bytes
#define PHYSICAL_FRAME_BUFFER_SIZE \
ALIGN_UP_CONST(DISPLAY_RESX *DISPLAY_RESY * 2, \
PHYSICAL_FRAME_BUFFER_ALIGNMENT)
static
__attribute__((section(".fb1"), aligned(PHYSICAL_FRAME_BUFFER_ALIGNMENT)))
uint8_t physical_frame_buffer_0[PHYSICAL_FRAME_BUFFER_SIZE];
#if (FRAME_BUFFER_COUNT > 1)
static
__attribute__((section(".fb2"), aligned(PHYSICAL_FRAME_BUFFER_ALIGNMENT)))
uint8_t physical_frame_buffer_1[PHYSICAL_FRAME_BUFFER_SIZE];
#endif
void display_init(display_content_mode_t mode) {
__HAL_RCC_GPIOE_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStructure.Alternate = 0;
GPIO_InitStructure.Pin = GPIO_PIN_2;
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_2, GPIO_PIN_RESET);
HAL_GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_InitStructure.Pin = GPIO_PIN_0;
// default to keeping display in reset
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_0, GPIO_PIN_RESET);
HAL_GPIO_Init(GPIOE, &GPIO_InitStructure);
hal_delay(100);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_2, GPIO_PIN_SET);
backlight_pwm_init(mode);
}
void display_deinit(display_content_mode_t mode) { backlight_pwm_deinit(mode); }
void display_set_unpriv_access(bool unpriv) {}
int display_set_backlight(int level) {
return 0;
// return backlight_pwm_set(level);
}
int display_get_backlight(void) { return backlight_pwm_get(); }
int display_set_orientation(int angle) { return angle; }
int display_get_orientation(void) { return 0; }
// Returns the pointer to the physical frame buffer (0.. FRAME_BUFFER_COUNT-1)
// Returns NULL if the framebuffer index is out of range.
static uint8_t *get_fb_ptr(uint32_t index) { return physical_frame_buffer_0; }
bool display_get_frame_buffer(display_fb_info_t *fb) {
fb->ptr = get_fb_ptr(0);
fb->stride = DISPLAY_RESX * sizeof(uint16_t);
// Enable access to the frame buffer from the unprivileged code
mpu_set_active_fb(fb->ptr, PHYSICAL_FRAME_BUFFER_SIZE);
return true;
}
void display_refresh(void) {}
void display_fill(const gfx_bitblt_t *bb) {}
void display_copy_rgb565(const gfx_bitblt_t *bb) {}
void display_copy_mono4(const gfx_bitblt_t *bb) {}
void display_copy_mono1p(const gfx_bitblt_t *bb) {}
#endif

View File

@ -14,6 +14,13 @@
#define BTN_EXTI_INTERRUPT_HANDLER EXTI5_IRQHandler
#define DISPLAY_COLOR_MODE DMA2D_OUTPUT_RGB565
#define DISPLAY_PANEL_LX200D2406A
#define DISPLAY_RESET_PIN GPIO_PIN_2
#define DISPLAY_RESET_PORT GPIOE
#define DISPLAY_RESET_CLK_ENA __HAL_RCC_GPIOE_CLK_ENABLE
#define DISPLAY_PWREN_PIN GPIO_PIN_0
#define DISPLAY_PWREN_PORT GPIOE
#define DISPLAY_PWREN_CLK_ENA __HAL_RCC_GPIOE_CLK_ENABLE
#define BACKLIGHT_PWM_FREQ 1000
#define BACKLIGHT_PWM_TIM TIM17

View File

@ -40,7 +40,11 @@ def configure(
]
sources += [
"embed/io/display/st7785ma/display_driver.c",
"embed/io/display/ltdc_dsi/display_driver.c",
"embed/io/display/ltdc_dsi/panels/lx200d2406a/lx200d2406a.c",
"embed/io/display/ltdc_dsi/display_fb.c",
"embed/io/display/ltdc_dsi/display_fb_rgb565.c",
"embed/io/display/fb_queue/fb_queue.c",
"embed/io/display/backlight/stm32/backlight_pwm.c",
]