mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-04 13:38:28 +00:00
Merge ee77086f92
into ef02c4de5d
This commit is contained in:
commit
6ea797578e
129
core/embed/io/display/fb_queue/fb_queue.c
Normal file
129
core/embed/io/display/fb_queue/fb_queue.c
Normal file
@ -0,0 +1,129 @@
|
||||
#ifdef KERNEL_MODE
|
||||
#include "fb_queue.h"
|
||||
|
||||
int16_t fb_queue_get_for_copy(volatile frame_buffer_queue_t *queue) {
|
||||
if (queue->entry[queue->wix] != FB_STATE_PREPARING) {
|
||||
// No refresh needed as the frame buffer is not in
|
||||
// the state to be copied to the display
|
||||
return -1;
|
||||
}
|
||||
|
||||
return queue->wix;
|
||||
}
|
||||
|
||||
int16_t fb_queue_get_for_write(volatile frame_buffer_queue_t *queue) {
|
||||
frame_buffer_state_t state;
|
||||
|
||||
// We have to wait if the buffer was passed for copying
|
||||
// to the interrupt handler
|
||||
do {
|
||||
state = queue->entry[queue->wix];
|
||||
} while (state == FB_STATE_READY || state == FB_STATE_COPYING);
|
||||
|
||||
queue->entry[queue->wix] = FB_STATE_PREPARING;
|
||||
|
||||
return queue->wix;
|
||||
}
|
||||
|
||||
int16_t fb_queue_get_for_transfer(volatile frame_buffer_queue_t *queue) {
|
||||
if (queue->rix >= FRAME_BUFFER_COUNT) {
|
||||
// This is an invalid state, and we should never get here
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (queue->entry[queue->rix]) {
|
||||
case FB_STATE_EMPTY:
|
||||
case FB_STATE_PREPARING:
|
||||
// No new frame queued
|
||||
|
||||
case FB_STATE_COPYING:
|
||||
// Currently we are copying a data to the display.
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case FB_STATE_READY:
|
||||
// Now it's proper time to copy the data to the display
|
||||
queue->entry[queue->rix] = FB_STATE_COPYING;
|
||||
return queue->rix;
|
||||
|
||||
// NOTE: when copying is done, this queue slot is marked empty
|
||||
break;
|
||||
|
||||
default:
|
||||
// This is an invalid state, and we should never get here
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool fb_queue_set_done(volatile frame_buffer_queue_t *queue) {
|
||||
if (queue->rix >= FRAME_BUFFER_COUNT) {
|
||||
// This is an invalid state, and we should never get here
|
||||
return false;
|
||||
}
|
||||
|
||||
if (queue->entry[queue->rix] == FB_STATE_COPYING) {
|
||||
queue->entry[queue->rix] = FB_STATE_EMPTY;
|
||||
queue->rix = (queue->rix + 1) % FRAME_BUFFER_COUNT;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fb_queue_set_switched(volatile frame_buffer_queue_t *queue) {
|
||||
if (queue->rix >= FRAME_BUFFER_COUNT) {
|
||||
// This is an invalid state, and we should never get here
|
||||
return false;
|
||||
}
|
||||
|
||||
if (queue->entry[queue->rix] == FB_STATE_COPYING) {
|
||||
if (queue->aix >= 0) {
|
||||
queue->entry[queue->aix] = FB_STATE_EMPTY;
|
||||
}
|
||||
queue->aix = queue->rix;
|
||||
queue->rix = (queue->rix + 1) % FRAME_BUFFER_COUNT;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fb_queue_set_ready_for_transfer(volatile frame_buffer_queue_t *queue) {
|
||||
if (queue->wix >= FRAME_BUFFER_COUNT) {
|
||||
// This is an invalid state, and we should never get here
|
||||
return false;
|
||||
}
|
||||
|
||||
if (queue->entry[queue->rix] == FB_STATE_PREPARING) {
|
||||
queue->entry[queue->rix] = FB_STATE_READY;
|
||||
queue->wix = (queue->wix + 1) % FRAME_BUFFER_COUNT;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void fb_queue_reset(volatile frame_buffer_queue_t *queue) {
|
||||
// Reset the buffer queue so we can eventually continue
|
||||
// safely in thread mode
|
||||
queue->wix = 0;
|
||||
queue->rix = 0;
|
||||
for (int i = 0; i < FRAME_BUFFER_COUNT; i++) {
|
||||
queue->entry[i] = FB_STATE_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
bool fb_queue_is_processed(volatile frame_buffer_queue_t *queue) {
|
||||
for (int i = 0; i < FRAME_BUFFER_COUNT; i++) {
|
||||
frame_buffer_state_t state = queue->entry[i];
|
||||
if (state == FB_STATE_READY ||
|
||||
(state == FB_STATE_COPYING && i != queue->aix)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
73
core/embed/io/display/fb_queue/fb_queue.h
Normal file
73
core/embed/io/display/fb_queue/fb_queue.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
// Number of frame buffers used (1 or 2)
|
||||
// If 1 buffer is selected, some animations may not
|
||||
// be so smooth but the memory usage is lower.
|
||||
#define FRAME_BUFFER_COUNT 2
|
||||
|
||||
// Each frame buffer can be in one of the following states:
|
||||
typedef enum {
|
||||
// The frame buffer is empty and can be written to
|
||||
FB_STATE_EMPTY = 0,
|
||||
// The frame buffer pass passed to application
|
||||
FB_STATE_PREPARING = 1,
|
||||
// The frame buffer was written to and is ready
|
||||
// to be copied to the display
|
||||
FB_STATE_READY = 2,
|
||||
// The frame buffer is currently being copied to
|
||||
// the display
|
||||
FB_STATE_COPYING = 3,
|
||||
|
||||
} frame_buffer_state_t;
|
||||
|
||||
typedef struct {
|
||||
// Queue entries
|
||||
volatile frame_buffer_state_t entry[FRAME_BUFFER_COUNT];
|
||||
// Active index
|
||||
// (accessed & updated in the context of the interrupt handlers
|
||||
int16_t aix;
|
||||
// Read index
|
||||
// (accessed & updated in the context of the interrupt handlers
|
||||
uint8_t rix;
|
||||
// Write index
|
||||
// (accessed & updated in context of the main thread)
|
||||
uint8_t wix;
|
||||
|
||||
} frame_buffer_queue_t;
|
||||
|
||||
int16_t fb_queue_get_for_copy(volatile frame_buffer_queue_t *queue);
|
||||
|
||||
int16_t fb_queue_get_for_write(volatile frame_buffer_queue_t *queue);
|
||||
|
||||
int16_t fb_queue_get_for_transfer(volatile frame_buffer_queue_t *queue);
|
||||
|
||||
bool fb_queue_set_done(volatile frame_buffer_queue_t *queue);
|
||||
|
||||
bool fb_queue_set_switched(volatile frame_buffer_queue_t *queue);
|
||||
|
||||
bool fb_queue_set_ready_for_transfer(volatile frame_buffer_queue_t *queue);
|
||||
|
||||
void fb_queue_reset(volatile frame_buffer_queue_t *queue);
|
||||
|
||||
bool fb_queue_is_processed(volatile frame_buffer_queue_t *queue);
|
404
core/embed/io/display/ltdc_dsi/display_driver.c
Normal file
404
core/embed/io/display/ltdc_dsi/display_driver.c
Normal file
@ -0,0 +1,404 @@
|
||||
|
||||
#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 void 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;
|
||||
(void)HAL_RCCEx_PeriphCLKConfig(&PLL3InitPeriph);
|
||||
}
|
||||
|
||||
static void 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;
|
||||
|
||||
(void)HAL_RCCEx_PeriphCLKConfig(&DSIPHYInitPeriph);
|
||||
|
||||
/* 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;
|
||||
|
||||
HAL_DSI_Init(&drv->hlcd_dsi, &PLLInit);
|
||||
HAL_DSI_SetGenericVCID(&drv->hlcd_dsi, 0);
|
||||
|
||||
/* 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 */
|
||||
HAL_DSI_ConfigVideoMode(&drv->hlcd_dsi, &drv->DSIVidCfg);
|
||||
|
||||
/*********************/
|
||||
/* LCD configuration */
|
||||
/*********************/
|
||||
PhyTimers.ClockLaneHS2LPTime = 11;
|
||||
PhyTimers.ClockLaneLP2HSTime = 40;
|
||||
PhyTimers.DataLaneHS2LPTime = 12;
|
||||
PhyTimers.DataLaneLP2HSTime = 23;
|
||||
PhyTimers.DataLaneMaxReadTime = 0;
|
||||
PhyTimers.StopWaitTime = 7;
|
||||
|
||||
HAL_DSI_ConfigPhyTimer(&drv->hlcd_dsi, &PhyTimers);
|
||||
|
||||
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;
|
||||
|
||||
HAL_DSI_ConfigHostTimeouts(&drv->hlcd_dsi, &HostTimeouts);
|
||||
HAL_DSI_ConfigFlowControl(&drv->hlcd_dsi, DSI_FLOW_CONTROL_BTA);
|
||||
|
||||
/* Enable the DSI host */
|
||||
__HAL_DSI_ENABLE(&drv->hlcd_dsi);
|
||||
}
|
||||
|
||||
void 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;
|
||||
HAL_LTDC_ConfigLayer(hltdc, &LayerCfg, LTDC_LAYER_1);
|
||||
}
|
||||
|
||||
void display_ltdc_init(void) {
|
||||
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;
|
||||
|
||||
HAL_LTDCEx_StructInitFromVideoConfig(&drv->hlcd_ltdc, &drv->DSIVidCfg);
|
||||
|
||||
HAL_LTDC_Init(&drv->hlcd_ltdc);
|
||||
|
||||
display_ltdc_config_layer(&drv->hlcd_ltdc, display_fb_get_initial_addr());
|
||||
}
|
||||
|
||||
void display_set_fb(uint32_t fb_addr) {
|
||||
display_driver_t *drv = &g_display_driver;
|
||||
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
|
||||
|
||||
display_fb_clear();
|
||||
|
||||
__HAL_RCC_LTDC_RELEASE_RESET();
|
||||
__HAL_RCC_DSI_RELEASE_RESET();
|
||||
|
||||
#ifdef DISPLAY_GFXMMU
|
||||
display_gfxmmu_init(drv);
|
||||
#endif
|
||||
|
||||
display_pll_init();
|
||||
display_dsi_init();
|
||||
display_ltdc_init();
|
||||
|
||||
/* Start DSI */
|
||||
HAL_DSI_Start(&drv->hlcd_dsi);
|
||||
|
||||
panel_init(drv);
|
||||
|
||||
HAL_LTDC_ProgramLineEvent(&drv->hlcd_ltdc, LCD_HEIGHT);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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
|
238
core/embed/io/display/ltdc_dsi/display_fb.c
Normal file
238
core/embed/io/display/ltdc_dsi/display_fb.c
Normal file
@ -0,0 +1,238 @@
|
||||
/*
|
||||
* 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/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
|
||||
|
||||
int16_t fb_idx = fb_queue_get_for_write(&drv->queue);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int16_t fb_idx = fb_queue_get_for_copy(&drv->queue);
|
||||
|
||||
if (fb_idx < 0) {
|
||||
// No refresh needed as the frame buffer is not in
|
||||
// the state to be copied to the display
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_mode_exception()) {
|
||||
// Disable scheduling of any new background copying
|
||||
HAL_NVIC_DisableIRQ(LTDC_IRQn);
|
||||
HAL_NVIC_DisableIRQ(LTDC_ER_IRQn);
|
||||
|
||||
display_set_fb((uint32_t)get_fb_ptr(fb_idx));
|
||||
|
||||
// Reset the buffer queue so we can eventually continue
|
||||
// safely in thread mode
|
||||
fb_queue_reset(&drv->queue);
|
||||
|
||||
// Enable normal processing again
|
||||
HAL_NVIC_EnableIRQ(LTDC_IRQn);
|
||||
HAL_NVIC_EnableIRQ(LTDC_ER_IRQn);
|
||||
} else {
|
||||
// Mark the buffer ready to switch to
|
||||
fb_queue_set_ready_for_transfer(&drv->queue);
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
copy_pending = !fb_queue_is_processed(&drv->queue);
|
||||
__WFI();
|
||||
} while (copy_pending || drv->update_pending > 0);
|
||||
}
|
||||
}
|
||||
|
||||
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_get_initial_addr(void) {
|
||||
display_fb_info_t fb_info;
|
||||
|
||||
display_get_frame_buffer(&fb_info);
|
||||
|
||||
fb_queue_set_ready_for_transfer(&g_display_driver.queue);
|
||||
|
||||
fb_queue_get_for_transfer(&g_display_driver.queue);
|
||||
|
||||
return (uint32_t)fb_info.ptr;
|
||||
}
|
||||
|
||||
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_get_for_transfer(&drv->queue);
|
||||
if (fb_idx >= 0) {
|
||||
fb_queue_set_switched(&drv->queue);
|
||||
display_set_fb((uint32_t)get_fb_ptr(fb_idx));
|
||||
drv->update_pending = 3;
|
||||
}
|
||||
|
||||
HAL_LTDC_ProgramLineEvent(&drv->hlcd_ltdc, LCD_HEIGHT);
|
||||
}
|
||||
|
||||
#endif
|
64
core/embed/io/display/ltdc_dsi/display_fb_rgb565.c
Normal file
64
core/embed/io/display/ltdc_dsi/display_fb_rgb565.c
Normal 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
|
65
core/embed/io/display/ltdc_dsi/display_fb_rgb888.c
Normal file
65
core/embed/io/display/ltdc_dsi/display_fb_rgb888.c
Normal 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
|
61
core/embed/io/display/ltdc_dsi/display_gfxmmu.c
Normal file
61
core/embed/io/display/ltdc_dsi/display_gfxmmu.c
Normal file
@ -0,0 +1,61 @@
|
||||
#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];
|
||||
|
||||
void 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();
|
||||
|
||||
/* Enable GFXMMU interrupt */
|
||||
NVIC_SetPriority(GFXMMU_IRQn, IRQ_PRI_NORMAL);
|
||||
NVIC_EnableIRQ(GFXMMU_IRQn);
|
||||
|
||||
/* 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 */
|
||||
HAL_GFXMMU_Init(&drv->hlcd_gfxmmu);
|
||||
|
||||
/* Initialize LUT */
|
||||
HAL_GFXMMU_ConfigLut(&drv->hlcd_gfxmmu, 0, LCD_HEIGHT,
|
||||
(uint32_t)&gfxmmu_lut_config);
|
||||
|
||||
HAL_GFXMMU_DisableLutLines(&drv->hlcd_gfxmmu, LCD_HEIGHT, 1024 - LCD_HEIGHT);
|
||||
}
|
||||
#endif
|
83
core/embed/io/display/ltdc_dsi/display_internal.h
Normal file
83
core/embed/io/display/ltdc_dsi/display_internal.h
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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;
|
||||
frame_buffer_queue_t queue;
|
||||
|
||||
// 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;
|
||||
|
||||
void display_set_fb(uint32_t fb_addr);
|
||||
|
||||
void display_fb_clear(void);
|
||||
|
||||
uint32_t display_fb_get_initial_addr(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);
|
||||
|
||||
void panel_init(display_driver_t *drv);
|
||||
|
||||
#ifdef DISPLAY_GFXMMU
|
||||
void display_gfxmmu_init(display_driver_t *drv);
|
||||
#endif
|
||||
|
||||
#endif // TREZOR_HAL_DISPLAY_INTERNAL_H
|
166
core/embed/io/display/ltdc_dsi/panels/lx200d2406a/lx200d2406a.c
Normal file
166
core/embed/io/display/ltdc_dsi/panels/lx200d2406a/lx200d2406a.c
Normal file
@ -0,0 +1,166 @@
|
||||
|
||||
#include <trezor_bsp.h>
|
||||
#include <trezor_rtl.h>
|
||||
|
||||
#include <sys/systick.h>
|
||||
|
||||
#include "lx200d2406a.h"
|
||||
|
||||
#include "../../display_internal.h"
|
||||
|
||||
// todo static assert resolution
|
||||
|
||||
void panel_init(display_driver_t *drv) {
|
||||
HAL_DSI_ShortWrite(&drv->hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P0, 0x11, 0);
|
||||
|
||||
systick_delay_ms(120);
|
||||
|
||||
HAL_DSI_ShortWrite(&drv->hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, 0x36, 0x00);
|
||||
HAL_DSI_ShortWrite(&drv->hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, 0x3A, 0x06);
|
||||
|
||||
// mipi video mode
|
||||
HAL_DSI_ShortWrite(&drv->hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, 0xB0, 0x10);
|
||||
|
||||
// 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);
|
||||
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});
|
||||
|
||||
// Write(Command , 0xB7);
|
||||
// Write(Parameter , 0x00);
|
||||
// Write(Parameter , 0x06);
|
||||
HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 2, 0xB7,
|
||||
(uint8_t[]){0x00, 0x06});
|
||||
|
||||
// Write(Command , 0xBB);
|
||||
// Write(Parameter , 0x00);
|
||||
// Write(Parameter , 0x1E);
|
||||
HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 2, 0xBB,
|
||||
(uint8_t[]){0x00, 0x1E});
|
||||
|
||||
// Write(Command , 0xC0);
|
||||
// Write(Parameter , 0x00);
|
||||
// Write(Parameter , 0x2C);
|
||||
HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 2, 0xC0,
|
||||
(uint8_t[]){0x00, 0x2C});
|
||||
|
||||
// Write(Command , 0xC2);
|
||||
// Write(Parameter , 0x00);
|
||||
// Write(Parameter , 0x01);
|
||||
HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 2, 0xC2,
|
||||
(uint8_t[]){0x00, 0x01});
|
||||
|
||||
// Write(Command , 0xC3);
|
||||
// Write(Parameter , 0x00);
|
||||
// Write(Parameter , 0x0F);
|
||||
HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 2, 0xC3,
|
||||
(uint8_t[]){0x00, 0x0F});
|
||||
|
||||
// Write(Command , 0xC6);
|
||||
// Write(Parameter , 0x00);
|
||||
// Write(Parameter , 0x0F);
|
||||
HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 2, 0xC6,
|
||||
(uint8_t[]){0x00, 0x0F});
|
||||
|
||||
// Write(Command , 0xD0);
|
||||
// Write(Parameter , 0x00);
|
||||
// Write(Parameter , 0xA7);
|
||||
HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 2, 0xD0,
|
||||
(uint8_t[]){0x00, 0xA7});
|
||||
|
||||
// Write(Command , 0xD0);
|
||||
// Write(Parameter , 0x00);
|
||||
// Write(Parameter , 0xA4);
|
||||
// Write(Parameter , 0x00);
|
||||
// Write(Parameter , 0xA1);
|
||||
HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 4, 0xD0,
|
||||
(uint8_t[]){0x00, 0xA4, 0x00, 0xA1});
|
||||
|
||||
// Write(Command , 0xD6);
|
||||
// Write(Parameter , 0x00);
|
||||
// Write(Parameter , 0xA1);
|
||||
HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 2, 0xD6,
|
||||
(uint8_t[]){0x00, 0xA1});
|
||||
|
||||
// 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);
|
||||
// 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});
|
||||
|
||||
// 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);
|
||||
// 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});
|
||||
|
||||
HAL_DSI_ShortWrite(&drv->hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P0, 0x21, 0);
|
||||
HAL_DSI_ShortWrite(&drv->hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P0, 0x29, 0);
|
||||
HAL_DSI_ShortWrite(&drv->hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P0, 0x2C, 0);
|
||||
}
|
@ -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
|
@ -1,35 +1,9 @@
|
||||
/* USER CODE BEGIN Header */
|
||||
/**
|
||||
******************************************************************************
|
||||
* File Name : gfxmmu_lut.h
|
||||
* Description : header file for GFX MMU Configuration Table
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* Copyright (c) 2022 STMicroelectronics.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed under terms that can be found in the LICENSE file
|
||||
* in the root directory of this software component.
|
||||
* If no LICENSE file comes with this software, it is provided AS-IS.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
/* USER CODE END Header */
|
||||
/* Define to prevent recursive inclusion -------------------------------------*/
|
||||
#ifndef __gfxmmu_lut_H
|
||||
#define __gfxmmu_lut_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
// GFX MMU Configuration Table
|
||||
#ifdef KERNEL_MODE
|
||||
#include <trezor_rtl.h>
|
||||
|
||||
#define GFXMMU_FB_SIZE 733936
|
||||
#define GFXMMU_LUT_FIRST 0
|
||||
#define GFXMMU_LUT_LAST 479
|
||||
#define GFXMMU_LUT_SIZE 480
|
||||
#include "../../display_internal.h"
|
||||
|
||||
static const uint32_t gfxmmu_lut_config[2 * GFXMMU_LUT_SIZE] = {
|
||||
const uint32_t gfxmmu_lut_config[2 * GFXMMU_LUT_SIZE] = {
|
||||
0x00413601, // GFXMMU_LUT0L
|
||||
0x003FFCA0, // GFXMMU_LUT0H
|
||||
0x00433401, // GFXMMU_LUT1L
|
||||
@ -992,15 +966,116 @@ static const uint32_t gfxmmu_lut_config[2 * GFXMMU_LUT_SIZE] = {
|
||||
0x000B2ED0 // GFXMMU_LUT479H
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
void panel_init(display_driver_t* drv) {
|
||||
/* CMD Mode */
|
||||
uint8_t InitParam1[3] = {0xFF, 0x83, 0x79};
|
||||
HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 3, 0xB9,
|
||||
InitParam1);
|
||||
|
||||
/* SETPOWER */
|
||||
uint8_t InitParam2[16] = {0x44, 0x1C, 0x1C, 0x37, 0x57, 0x90, 0xD0, 0xE2,
|
||||
0x58, 0x80, 0x38, 0x38, 0xF8, 0x33, 0x34, 0x42};
|
||||
HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 16, 0xB1,
|
||||
InitParam2);
|
||||
|
||||
/* SETDISP */
|
||||
uint8_t InitParam3[9] = {0x80, 0x14, 0x0C, 0x30, 0x20,
|
||||
0x50, 0x11, 0x42, 0x1D};
|
||||
HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 9, 0xB2,
|
||||
InitParam3);
|
||||
|
||||
/* Set display cycle timing */
|
||||
uint8_t InitParam4[10] = {0x01, 0xAA, 0x01, 0xAF, 0x01,
|
||||
0xAF, 0x10, 0xEA, 0x1C, 0xEA};
|
||||
HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 10, 0xB4,
|
||||
InitParam4);
|
||||
|
||||
/* SETVCOM */
|
||||
uint8_t InitParam5[4] = {00, 00, 00, 0xC0};
|
||||
HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 4, 0xC7,
|
||||
InitParam5);
|
||||
|
||||
/* Set Panel Related Registers */
|
||||
HAL_DSI_ShortWrite(&drv->hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, 0xCC, 0x02);
|
||||
|
||||
HAL_DSI_ShortWrite(&drv->hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, 0xD2, 0x77);
|
||||
|
||||
uint8_t InitParam6[37] = {0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x08, 0x32,
|
||||
0x10, 0x01, 0x00, 0x01, 0x03, 0x72, 0x03, 0x72,
|
||||
0x00, 0x08, 0x00, 0x08, 0x33, 0x33, 0x05, 0x05,
|
||||
0x37, 0x05, 0x05, 0x37, 0x0A, 0x00, 0x00, 0x00,
|
||||
0x0A, 0x00, 0x01, 0x00, 0x0E};
|
||||
HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 37, 0xD3,
|
||||
InitParam6);
|
||||
|
||||
uint8_t InitParam7[34] = {
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x18, 0x18,
|
||||
0x18, 0x18, 0x19, 0x19, 0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06,
|
||||
0x23, 0x22, 0x21, 0x20, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00};
|
||||
HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 34, 0xD5,
|
||||
InitParam7);
|
||||
|
||||
uint8_t InitParam8[32] = {0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
0x19, 0x19, 0x18, 0x18, 0x19, 0x19, 0x18, 0x18,
|
||||
0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01,
|
||||
0x20, 0x21, 0x22, 0x23, 0x18, 0x18, 0x18, 0x18};
|
||||
HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 35, 0xD6,
|
||||
InitParam8);
|
||||
|
||||
/* SET GAMMA */
|
||||
uint8_t InitParam9[42] = {
|
||||
0x00, 0x16, 0x1B, 0x30, 0x36, 0x3F, 0x24, 0x40, 0x09, 0x0D, 0x0F,
|
||||
0x18, 0x0E, 0x11, 0x12, 0x11, 0x14, 0x07, 0x12, 0x13, 0x18, 0x00,
|
||||
0x17, 0x1C, 0x30, 0x36, 0x3F, 0x24, 0x40, 0x09, 0x0C, 0x0F, 0x18,
|
||||
0x0E, 0x11, 0x14, 0x11, 0x12, 0x07, 0x12, 0x14, 0x18};
|
||||
HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 42, 0xE0,
|
||||
InitParam9);
|
||||
|
||||
uint8_t InitParam10[3] = {0x2C, 0x2C, 00};
|
||||
HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 3, 0xB6,
|
||||
InitParam10);
|
||||
|
||||
HAL_DSI_ShortWrite(&drv->hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, 0xBD, 0x00);
|
||||
|
||||
uint8_t InitParam11[] = {0x01, 0x00, 0x07, 0x0F, 0x16, 0x1F, 0x27, 0x30, 0x38,
|
||||
0x40, 0x47, 0x4E, 0x56, 0x5D, 0x65, 0x6D, 0x74, 0x7D,
|
||||
0x84, 0x8A, 0x90, 0x99, 0xA1, 0xA9, 0xB0, 0xB6, 0xBD,
|
||||
0xC4, 0xCD, 0xD4, 0xDD, 0xE5, 0xEC, 0xF3, 0x36, 0x07,
|
||||
0x1C, 0xC0, 0x1B, 0x01, 0xF1, 0x34, 0x00};
|
||||
HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 42, 0xC1,
|
||||
InitParam11);
|
||||
|
||||
HAL_DSI_ShortWrite(&drv->hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, 0xBD, 0x01);
|
||||
|
||||
uint8_t InitParam12[] = {0x00, 0x08, 0x0F, 0x16, 0x1F, 0x28, 0x31, 0x39, 0x41,
|
||||
0x48, 0x51, 0x59, 0x60, 0x68, 0x70, 0x78, 0x7F, 0x87,
|
||||
0x8D, 0x94, 0x9C, 0xA3, 0xAB, 0xB3, 0xB9, 0xC1, 0xC8,
|
||||
0xD0, 0xD8, 0xE0, 0xE8, 0xEE, 0xF5, 0x3B, 0x1A, 0xB6,
|
||||
0xA0, 0x07, 0x45, 0xC5, 0x37, 0x00};
|
||||
HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 42, 0xC1,
|
||||
InitParam12);
|
||||
|
||||
HAL_DSI_ShortWrite(&drv->hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, 0xBD, 0x02);
|
||||
|
||||
uint8_t InitParam13[42] = {
|
||||
0x00, 0x09, 0x0F, 0x18, 0x21, 0x2A, 0x34, 0x3C, 0x45, 0x4C, 0x56,
|
||||
0x5E, 0x66, 0x6E, 0x76, 0x7E, 0x87, 0x8E, 0x95, 0x9D, 0xA6, 0xAF,
|
||||
0xB7, 0xBD, 0xC5, 0xCE, 0xD5, 0xDF, 0xE7, 0xEE, 0xF4, 0xFA, 0xFF,
|
||||
0x0C, 0x31, 0x83, 0x3C, 0x5B, 0x56, 0x1E, 0x5A, 0xFF};
|
||||
HAL_DSI_LongWrite(&drv->hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 42, 0xC1,
|
||||
InitParam13);
|
||||
|
||||
HAL_DSI_ShortWrite(&drv->hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, 0xBD, 0x00);
|
||||
|
||||
/* Exit Sleep Mode*/
|
||||
HAL_DSI_ShortWrite(&drv->hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P0, 0x11, 0x00);
|
||||
|
||||
HAL_Delay(120);
|
||||
|
||||
/* Display On */
|
||||
HAL_DSI_ShortWrite(&drv->hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1,
|
||||
DSI_SET_DISPLAY_ON, 0x00);
|
||||
|
||||
HAL_Delay(120);
|
||||
}
|
||||
#endif
|
||||
#endif /*__ gfxmmu_lut_H */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#define VSYNC 1
|
||||
#define VBP 12
|
||||
#define VFP 50
|
||||
#define VACT 481
|
||||
#define HSYNC 2
|
||||
#define HBP 1
|
||||
#define HFP 1
|
||||
#define HACT 480
|
||||
#define LCD_WIDTH 480
|
||||
#define LCD_HEIGHT 480
|
||||
|
||||
#define LCD_Y_OFFSET 1
|
||||
|
||||
#define GFXMMU_FB_SIZE 733936
|
||||
#define GFXMMU_LUT_FIRST 0
|
||||
#define GFXMMU_LUT_LAST 479
|
||||
#define GFXMMU_LUT_SIZE 480
|
||||
|
||||
#define PANEL_DSI_MODE DSI_VID_MODE_BURST
|
||||
#define PANEL_DSI_LANES DSI_TWO_DATA_LANES
|
||||
#define PANEL_LTDC_PIXEL_FORMAT LTDC_PIXEL_FORMAT_ARGB8888
|
||||
|
||||
// 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 (184320 * 4)
|
||||
|
||||
// Pitch (in pixels) of the virtual frame buffer
|
||||
#define FRAME_BUFFER_PIXELS_PER_LINE 768
|
||||
|
||||
// Size of the virtual frame buffer in bytes
|
||||
#define VIRTUAL_FRAME_BUFFER_SIZE \
|
||||
(FRAME_BUFFER_PIXELS_PER_LINE * LCD_HEIGHT * 4)
|
@ -120,13 +120,7 @@ void display_physical_fb_clear(void) {
|
||||
static void bg_copy_callback(void) {
|
||||
display_driver_t *drv = &g_display_driver;
|
||||
|
||||
if (drv->queue.rix >= FRAME_BUFFER_COUNT) {
|
||||
// This is an invalid state and we should never get here
|
||||
return;
|
||||
}
|
||||
|
||||
drv->queue.entry[drv->queue.rix] = FB_STATE_EMPTY;
|
||||
drv->queue.rix = (drv->queue.rix + 1) % FRAME_BUFFER_COUNT;
|
||||
fb_queue_set_done(&drv->queue);
|
||||
}
|
||||
|
||||
// Interrupt routing handling TE signal
|
||||
@ -135,37 +129,13 @@ static void display_te_interrupt_handler(void) {
|
||||
|
||||
__HAL_GPIO_EXTI_CLEAR_FLAG(DISPLAY_TE_PIN);
|
||||
|
||||
if (drv->queue.rix >= FRAME_BUFFER_COUNT) {
|
||||
// This is an invalid state and we should never get here
|
||||
return;
|
||||
}
|
||||
int16_t fb_idx = fb_queue_get_for_transfer(&drv->queue);
|
||||
|
||||
switch (drv->queue.entry[drv->queue.rix]) {
|
||||
case FB_STATE_EMPTY:
|
||||
case FB_STATE_PREPARING:
|
||||
// No new frame queued
|
||||
break;
|
||||
|
||||
case FB_STATE_COPYING:
|
||||
// Currently we are copying a data to the display.
|
||||
// We need to wait for the next TE interrupt.
|
||||
break;
|
||||
|
||||
case FB_STATE_READY:
|
||||
// Now it's proper time to copy the data to the display
|
||||
drv->queue.entry[drv->queue.rix] = FB_STATE_COPYING;
|
||||
display_panel_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1);
|
||||
bg_copy_start_const_out_8(get_fb_ptr(drv->queue.rix),
|
||||
(uint8_t *)DISPLAY_DATA_ADDRESS,
|
||||
PHYSICAL_FRAME_BUFFER_SIZE, bg_copy_callback);
|
||||
|
||||
// NOTE: when copying is done, this queue slot is marked empty
|
||||
// (see bg_copy_callback())
|
||||
break;
|
||||
|
||||
default:
|
||||
// This is an invalid state and we should never get here
|
||||
break;
|
||||
if (fb_idx >= 0) {
|
||||
display_panel_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1);
|
||||
bg_copy_start_const_out_8(get_fb_ptr(fb_idx),
|
||||
(uint8_t *)DISPLAY_DATA_ADDRESS,
|
||||
PHYSICAL_FRAME_BUFFER_SIZE, bg_copy_callback);
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,17 +157,9 @@ bool display_get_frame_buffer(display_fb_info_t *fb) {
|
||||
return false;
|
||||
}
|
||||
|
||||
frame_buffer_state_t state;
|
||||
uint8_t fb_idx = fb_queue_get_for_write(&drv->queue);
|
||||
|
||||
// We have to wait if the buffer was passed for copying
|
||||
// to the interrupt handler
|
||||
do {
|
||||
state = drv->queue.entry[drv->queue.wix];
|
||||
} while (state == FB_STATE_READY || state == FB_STATE_COPYING);
|
||||
|
||||
drv->queue.entry[drv->queue.wix] = FB_STATE_PREPARING;
|
||||
|
||||
fb->ptr = get_fb_ptr(drv->queue.wix);
|
||||
fb->ptr = get_fb_ptr(fb_idx);
|
||||
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);
|
||||
@ -236,7 +198,9 @@ void display_refresh(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (drv->queue.entry[drv->queue.wix] != FB_STATE_PREPARING) {
|
||||
int16_t fb_idx = fb_queue_get_for_copy(&drv->queue);
|
||||
|
||||
if (fb_idx < 0) {
|
||||
// No refresh needed as the frame buffer is not in
|
||||
// the state to be copied to the display
|
||||
return;
|
||||
@ -255,26 +219,26 @@ void display_refresh(void) {
|
||||
// Stop any background copying even if it is not finished yet
|
||||
bg_copy_abort();
|
||||
// Copy the frame buffer to the display manually
|
||||
copy_fb_to_display(drv->queue.wix);
|
||||
copy_fb_to_display(fb_idx);
|
||||
// Reset the buffer queue so we can eventually continue
|
||||
// safely in thread mode
|
||||
drv->queue.wix = 0;
|
||||
drv->queue.rix = 0;
|
||||
for (int i = 0; i < FRAME_BUFFER_COUNT; i++) {
|
||||
drv->queue.entry[i] = FB_STATE_EMPTY;
|
||||
}
|
||||
fb_queue_reset(&drv->queue);
|
||||
|
||||
// Enable normal processing again
|
||||
NVIC_EnableIRQ(DISPLAY_TE_INTERRUPT_NUM);
|
||||
} else {
|
||||
// Mark the buffer ready to switch to
|
||||
drv->queue.entry[drv->queue.wix] = FB_STATE_READY;
|
||||
drv->queue.wix = (drv->queue.wix + 1) % FRAME_BUFFER_COUNT;
|
||||
fb_queue_set_ready_for_transfer(&drv->queue);
|
||||
}
|
||||
|
||||
#else // BOARDLOADER
|
||||
wait_for_te_signal();
|
||||
copy_fb_to_display(drv->queue.wix);
|
||||
drv->queue.entry[drv->queue.wix] = FB_STATE_EMPTY;
|
||||
fb_queue_set_ready_for_transfer(&drv->queue);
|
||||
fb_idx = fb_queue_get_for_transfer(&drv->queue);
|
||||
if (fb_idx >= 0) {
|
||||
copy_fb_to_display(fb_idx);
|
||||
fb_queue_set_done(&drv->queue);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -293,14 +257,7 @@ void display_ensure_refreshed(void) {
|
||||
// so we can be sure there's not scheduled or pending
|
||||
// background copying
|
||||
do {
|
||||
copy_pending = false;
|
||||
for (int i = 0; i < FRAME_BUFFER_COUNT; i++) {
|
||||
frame_buffer_state_t state = drv->queue.entry[i];
|
||||
if (state == FB_STATE_READY || state == FB_STATE_COPYING) {
|
||||
copy_pending = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
copy_pending = !fb_queue_is_processed(&drv->queue);
|
||||
__WFI();
|
||||
} while (copy_pending);
|
||||
|
||||
|
@ -6,37 +6,7 @@
|
||||
|
||||
#ifdef FRAMEBUFFER
|
||||
|
||||
// Number of frame buffers used (1 or 2)
|
||||
// If 1 buffer is selected, some animations may not
|
||||
// be so smooth but the memory usage is lower.
|
||||
#define FRAME_BUFFER_COUNT 2
|
||||
|
||||
// Each frame buffer can be in one of the following states:
|
||||
typedef enum {
|
||||
// The frame buffer is empty and can be written to
|
||||
FB_STATE_EMPTY = 0,
|
||||
// The frame buffer pass passed to application
|
||||
FB_STATE_PREPARING = 1,
|
||||
// The frame buffer was written to and is ready
|
||||
// to be copied to the display
|
||||
FB_STATE_READY = 2,
|
||||
// The frame buffer is currently being copied to
|
||||
// the display
|
||||
FB_STATE_COPYING = 3,
|
||||
|
||||
} frame_buffer_state_t;
|
||||
|
||||
typedef struct {
|
||||
// Queue entries
|
||||
volatile frame_buffer_state_t entry[FRAME_BUFFER_COUNT];
|
||||
// Read index
|
||||
// (accessed & updated in the context of the interrupt handlers
|
||||
uint8_t rix;
|
||||
// Write index
|
||||
// (accessed & updated in context of the main thread)
|
||||
uint8_t wix;
|
||||
|
||||
} frame_buffer_queue_t;
|
||||
#include "../fb_queue/fb_queue.h"
|
||||
|
||||
#endif // FRAMEBUFFER
|
||||
|
||||
|
@ -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
|
@ -1,226 +0,0 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include <trezor_bsp.h>
|
||||
#include <trezor_model.h>
|
||||
#include <trezor_rtl.h>
|
||||
|
||||
#include <io/display.h>
|
||||
#include <sys/mpu.h>
|
||||
|
||||
#include "display_internal.h"
|
||||
|
||||
#if (DISPLAY_RESX != 240) || (DISPLAY_RESY != 240)
|
||||
#error "Incompatible display resolution"
|
||||
#endif
|
||||
|
||||
#ifdef KERNEL_MODE
|
||||
|
||||
// Display driver instance
|
||||
display_driver_t g_display_driver = {
|
||||
.initialized = false,
|
||||
};
|
||||
|
||||
void display_init(display_content_mode_t mode) {
|
||||
display_driver_t *drv = &g_display_driver;
|
||||
|
||||
if (drv->initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == DISPLAY_RESET_CONTENT) {
|
||||
__HAL_RCC_DSI_FORCE_RESET();
|
||||
__HAL_RCC_LTDC_FORCE_RESET();
|
||||
__HAL_RCC_GFXMMU_FORCE_RESET();
|
||||
__HAL_RCC_DSI_RELEASE_RESET();
|
||||
__HAL_RCC_LTDC_RELEASE_RESET();
|
||||
__HAL_RCC_GFXMMU_RELEASE_RESET();
|
||||
|
||||
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
|
||||
|
||||
// Initializes the common periph clock
|
||||
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_LTDC | RCC_PERIPHCLK_DSI;
|
||||
PeriphClkInit.DsiClockSelection = RCC_DSICLKSOURCE_PLL3;
|
||||
PeriphClkInit.LtdcClockSelection = RCC_LTDCCLKSOURCE_PLL3;
|
||||
PeriphClkInit.PLL3.PLL3Source = RCC_PLLSOURCE_HSE;
|
||||
PeriphClkInit.PLL3.PLL3M = 4;
|
||||
PeriphClkInit.PLL3.PLL3N = 125;
|
||||
PeriphClkInit.PLL3.PLL3P = 8;
|
||||
PeriphClkInit.PLL3.PLL3Q = 2;
|
||||
PeriphClkInit.PLL3.PLL3R = 24;
|
||||
PeriphClkInit.PLL3.PLL3RGE = RCC_PLLVCIRANGE_0;
|
||||
PeriphClkInit.PLL3.PLL3FRACN = 0;
|
||||
PeriphClkInit.PLL3.PLL3ClockOut = RCC_PLL3_DIVP | RCC_PLL3_DIVR;
|
||||
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
|
||||
|
||||
// Clear framebuffers
|
||||
mpu_set_active_fb(physical_frame_buffer_0, PHYSICAL_FRAME_BUFFER_SIZE);
|
||||
memset(physical_frame_buffer_0, 0x00, PHYSICAL_FRAME_BUFFER_SIZE);
|
||||
|
||||
mpu_set_active_fb(physical_frame_buffer_1, PHYSICAL_FRAME_BUFFER_SIZE);
|
||||
memset(physical_frame_buffer_1, 0x00, PHYSICAL_FRAME_BUFFER_SIZE);
|
||||
|
||||
mpu_set_active_fb(NULL, 0);
|
||||
|
||||
BSP_LCD_Init(0, LCD_ORIENTATION_PORTRAIT);
|
||||
BSP_LCD_SetBrightness(0, 100);
|
||||
BSP_LCD_DisplayOn(0);
|
||||
} else {
|
||||
// Retain display content
|
||||
BSP_LCD_Reinit(0);
|
||||
if (drv->current_frame_buffer == 0) {
|
||||
BSP_LCD_SetFrameBuffer(0, GFXMMU_VIRTUAL_BUFFER1_BASE_S);
|
||||
} else {
|
||||
BSP_LCD_SetFrameBuffer(0, GFXMMU_VIRTUAL_BUFFER0_BASE_S);
|
||||
}
|
||||
}
|
||||
|
||||
drv->initialized = true;
|
||||
}
|
||||
|
||||
void display_deinit(display_content_mode_t mode) {
|
||||
display_driver_t *drv = &g_display_driver;
|
||||
|
||||
if (!drv->initialized) {
|
||||
if (mode == DISPLAY_RESET_CONTENT) {
|
||||
__HAL_RCC_DSI_FORCE_RESET();
|
||||
__HAL_RCC_LTDC_FORCE_RESET();
|
||||
__HAL_RCC_GFXMMU_FORCE_RESET();
|
||||
__HAL_RCC_DSI_RELEASE_RESET();
|
||||
__HAL_RCC_LTDC_RELEASE_RESET();
|
||||
__HAL_RCC_GFXMMU_RELEASE_RESET();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == DISPLAY_RESET_CONTENT) {
|
||||
BSP_LCD_DisplayOff(0);
|
||||
BSP_LCD_SetBrightness(0, 0);
|
||||
BSP_LCD_DeInit(0);
|
||||
}
|
||||
|
||||
mpu_set_active_fb(NULL, 0);
|
||||
|
||||
drv->initialized = false;
|
||||
}
|
||||
|
||||
int display_set_backlight(int level) {
|
||||
display_driver_t *drv = &g_display_driver;
|
||||
|
||||
if (!drv->initialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Just emulation, not doing anything
|
||||
drv->backlight_level = level;
|
||||
return level;
|
||||
}
|
||||
|
||||
int display_get_backlight(void) {
|
||||
display_driver_t *drv = &g_display_driver;
|
||||
|
||||
if (!drv->initialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return drv->orientation_angle;
|
||||
}
|
||||
|
||||
int display_set_orientation(int angle) {
|
||||
display_driver_t *drv = &g_display_driver;
|
||||
|
||||
if (!drv->initialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (angle == 0 || angle == 90 || angle == 180 || angle == 270) {
|
||||
// Just emulation, not doing anything
|
||||
drv->orientation_angle = angle;
|
||||
}
|
||||
|
||||
return drv->orientation_angle;
|
||||
}
|
||||
|
||||
int display_get_orientation(void) {
|
||||
display_driver_t *drv = &g_display_driver;
|
||||
|
||||
if (!drv->initialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return drv->orientation_angle;
|
||||
}
|
||||
|
||||
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
|
@ -1,124 +0,0 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include <trezor_bsp.h>
|
||||
#include <trezor_model.h>
|
||||
#include <trezor_rtl.h>
|
||||
|
||||
#include <io/display.h>
|
||||
#include <sys/mpu.h>
|
||||
|
||||
#include "display_internal.h"
|
||||
|
||||
#ifdef USE_TRUSTZONE
|
||||
#include <sys/trustzone.h>
|
||||
#endif
|
||||
|
||||
#ifdef KERNEL_MODE
|
||||
|
||||
// Physical frame buffers in internal SRAM memory
|
||||
__attribute__((section(".fb1"), aligned(PHYSICAL_FRAME_BUFFER_ALIGNMENT)))
|
||||
uint8_t physical_frame_buffer_0[PHYSICAL_FRAME_BUFFER_SIZE];
|
||||
|
||||
__attribute__((section(".fb2"), aligned(PHYSICAL_FRAME_BUFFER_ALIGNMENT)))
|
||||
uint8_t physical_frame_buffer_1[PHYSICAL_FRAME_BUFFER_SIZE];
|
||||
|
||||
#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.
|
||||
|
||||
if (!unpriv) {
|
||||
tz_set_gfxmmu_unpriv(unpriv);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (unpriv) {
|
||||
tz_set_gfxmmu_unpriv(unpriv);
|
||||
}
|
||||
|
||||
#ifdef USE_DMA2D
|
||||
tz_set_dma2d_unpriv(unpriv);
|
||||
#endif
|
||||
}
|
||||
#endif // USE_TRUSTZONE
|
||||
|
||||
bool display_get_frame_buffer(display_fb_info_t *fb) {
|
||||
display_driver_t *drv = &g_display_driver;
|
||||
|
||||
if (!drv->initialized) {
|
||||
fb->ptr = NULL;
|
||||
fb->stride = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
uintptr_t addr;
|
||||
|
||||
if (drv->current_frame_buffer == 0) {
|
||||
addr = GFXMMU_VIRTUAL_BUFFER1_BASE_S;
|
||||
} else {
|
||||
addr = GFXMMU_VIRTUAL_BUFFER0_BASE_S;
|
||||
}
|
||||
|
||||
uint32_t fb_stride = FRAME_BUFFER_PIXELS_PER_LINE * sizeof(uint32_t);
|
||||
|
||||
// We do not utilize whole area of the display
|
||||
// (discovery kit display is 480x480 and we need just 240x240)
|
||||
addr += (480 - DISPLAY_RESY) / 2 * sizeof(uint32_t);
|
||||
addr += (480 - DISPLAY_RESX) / 2 * fb_stride;
|
||||
|
||||
fb->ptr = (void *)addr;
|
||||
fb->stride = fb_stride;
|
||||
|
||||
// Enable access to the frame buffer from the unprivileged code
|
||||
mpu_set_active_fb(fb->ptr, VIRTUAL_FRAME_BUFFER_SIZE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void display_refresh(void) {
|
||||
display_driver_t *drv = &g_display_driver;
|
||||
|
||||
if (!drv->initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable access to the frame buffer from the unprivileged code
|
||||
mpu_set_active_fb(NULL, 0);
|
||||
|
||||
if (drv->current_frame_buffer == 0) {
|
||||
drv->current_frame_buffer = 1;
|
||||
BSP_LCD_SetFrameBuffer(0, GFXMMU_VIRTUAL_BUFFER1_BASE_S);
|
||||
} else {
|
||||
drv->current_frame_buffer = 0;
|
||||
BSP_LCD_SetFrameBuffer(0, GFXMMU_VIRTUAL_BUFFER0_BASE_S);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,88 +0,0 @@
|
||||
/*
|
||||
* 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_types.h>
|
||||
|
||||
#include <rtl/sizedefs.h>
|
||||
|
||||
#ifdef USE_TRUSTZONE
|
||||
#include <sys/trustzone.h>
|
||||
#endif
|
||||
|
||||
// Display driver context.
|
||||
typedef struct {
|
||||
// Set if the driver is initialized
|
||||
bool initialized;
|
||||
// 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
|
||||
uint32_t current_frame_buffer;
|
||||
} display_driver_t;
|
||||
|
||||
// Display driver instance
|
||||
extern display_driver_t g_display_driver;
|
||||
|
||||
// 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
|
||||
//
|
||||
// It's smaller than size of the virtual frame buffer
|
||||
// due to used GFXMMU settings
|
||||
#define PHYSICAL_FRAME_BUFFER_SIZE \
|
||||
ALIGN_UP_CONST(184320 * 4, PHYSICAL_FRAME_BUFFER_ALIGNMENT)
|
||||
|
||||
// Pitch (in pixels) of the virtual frame buffer
|
||||
#define FRAME_BUFFER_PIXELS_PER_LINE 768
|
||||
|
||||
// Size of the virtual frame buffer in bytes
|
||||
#define VIRTUAL_FRAME_BUFFER_SIZE \
|
||||
(FRAME_BUFFER_PIXELS_PER_LINE * DISPLAY_RESY * 4)
|
||||
|
||||
// Physical frame buffers in internal SRAM memory
|
||||
//
|
||||
// Both frame buffers layers in the fixed addresses that
|
||||
// are shared between bootloaders and the firmware.
|
||||
extern uint8_t physical_frame_buffer_0[PHYSICAL_FRAME_BUFFER_SIZE];
|
||||
extern uint8_t physical_frame_buffer_1[PHYSICAL_FRAME_BUFFER_SIZE];
|
||||
|
||||
// LCD orientations
|
||||
#define LCD_ORIENTATION_PORTRAIT 0U
|
||||
#define LCD_ORIENTATION_LANDSCAPE 1U
|
||||
#define LCD_ORIENTATION_PORTRAIT_ROT180 2U
|
||||
#define LCD_ORIENTATION_LANDSCAPE_ROT180 3U
|
||||
|
||||
int32_t BSP_LCD_Init(uint32_t Instance, uint32_t Orientation);
|
||||
int32_t BSP_LCD_DeInit(uint32_t Instance);
|
||||
int32_t BSP_LCD_Reinit(uint32_t Instance);
|
||||
int32_t BSP_LCD_SetBrightness(uint32_t Instance, uint32_t Brightness);
|
||||
int32_t BSP_LCD_DisplayOn(uint32_t Instance);
|
||||
int32_t BSP_LCD_DisplayOff(uint32_t Instance);
|
||||
int32_t BSP_LCD_SetFrameBuffer(uint32_t Instance, uint32_t fb_addr);
|
||||
|
||||
#endif // TREZOR_HAL_DISPLAY_INTERNAL_H
|
File diff suppressed because it is too large
Load Diff
@ -4,6 +4,14 @@
|
||||
#define VDD_1V8 1
|
||||
|
||||
#define DISPLAY_COLOR_MODE DMA2D_OUTPUT_ARGB8888
|
||||
#define DISPLAY_PANEL_STM32U5A9J_DK
|
||||
#define DISPLAY_GFXMMU 1
|
||||
#define DISPLAY_RESET_PIN GPIO_PIN_5
|
||||
#define DISPLAY_RESET_PORT GPIOD
|
||||
#define DISPLAY_RESET_CLK_ENA __HAL_RCC_GPIOD_CLK_ENABLE
|
||||
#define DISPLAY_BACKLIGHT_PIN GPIO_PIN_6
|
||||
#define DISPLAY_BACKLIGHT_PORT GPIOI
|
||||
#define DISPLAY_BACKLIGHT_CLK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE()
|
||||
|
||||
#define I2C_COUNT 1
|
||||
#define I2C_INSTANCE_0 I2C5
|
||||
|
@ -9,6 +9,13 @@
|
||||
#define BTN_POWER_CLK_ENA __HAL_RCC_GPIOE_CLK_ENABLE
|
||||
|
||||
#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
|
||||
|
@ -39,9 +39,12 @@ def configure(
|
||||
]
|
||||
|
||||
sources += [
|
||||
"embed/io/display/stm32u5a9j-dk/display_driver.c",
|
||||
"embed/io/display/stm32u5a9j-dk/display_fb.c",
|
||||
"embed/io/display/stm32u5a9j-dk/display_ltdc_dsi.c",
|
||||
"embed/io/display/ltdc_dsi/display_driver.c",
|
||||
"embed/io/display/ltdc_dsi/panels/stm32u5a9j-dk/stm32u5a9j-dk.c",
|
||||
"embed/io/display/ltdc_dsi/display_fb.c",
|
||||
"embed/io/display/ltdc_dsi/display_fb_rgb888.c",
|
||||
"embed/io/display/ltdc_dsi/display_gfxmmu.c",
|
||||
"embed/io/display/fb_queue/fb_queue.c",
|
||||
]
|
||||
paths += ["embed/io/display/inc"]
|
||||
|
||||
|
@ -47,6 +47,7 @@ def configure(
|
||||
sources += ["embed/io/display/st-7789/display_io.c"]
|
||||
sources += ["embed/io/display/st-7789/display_panel.c"]
|
||||
sources += ["embed/io/display/st-7789/panels/lx154a2482.c"]
|
||||
sources += ["embed/io/display/fb_queue/fb_queue.c"]
|
||||
paths += ["embed/io/display/inc"]
|
||||
|
||||
sources += ["embed/io/display/backlight/stm32/backlight_pwm.c"]
|
||||
|
@ -39,7 +39,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",
|
||||
]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user