From 03a296f815c1ae45c5718c6943e5a6c9b96e500f Mon Sep 17 00:00:00 2001 From: tychovrahe Date: Mon, 25 Nov 2024 16:10:52 +0100 Subject: [PATCH] feat(core): display driver for T3W1 [no changelog] --- .../io/display/ltdc_dsi/display_driver.c | 458 ++++++++++++++++++ core/embed/io/display/ltdc_dsi/display_fb.c | 226 +++++++++ .../io/display/ltdc_dsi/display_fb_rgb565.c | 64 +++ .../io/display/ltdc_dsi/display_fb_rgb888.c | 65 +++ .../io/display/ltdc_dsi/display_gfxmmu.c | 66 +++ .../io/display/ltdc_dsi/display_internal.h | 87 ++++ .../ltdc_dsi/panels/lx200d2406a/lx200d2406a.c | 233 +++++++++ .../ltdc_dsi/panels/lx200d2406a/lx200d2406a.h | 49 ++ .../io/display/st7785ma/display_driver.c | 93 ---- .../models/T3W1/boards/trezor_t3w1_revA.h | 7 + .../models/T3W1/trezor_t3w1_revA.py | 6 +- 11 files changed, 1260 insertions(+), 94 deletions(-) create mode 100644 core/embed/io/display/ltdc_dsi/display_driver.c create mode 100644 core/embed/io/display/ltdc_dsi/display_fb.c create mode 100644 core/embed/io/display/ltdc_dsi/display_fb_rgb565.c create mode 100644 core/embed/io/display/ltdc_dsi/display_fb_rgb888.c create mode 100644 core/embed/io/display/ltdc_dsi/display_gfxmmu.c create mode 100644 core/embed/io/display/ltdc_dsi/display_internal.h create mode 100644 core/embed/io/display/ltdc_dsi/panels/lx200d2406a/lx200d2406a.c create mode 100644 core/embed/io/display/ltdc_dsi/panels/lx200d2406a/lx200d2406a.h delete mode 100644 core/embed/io/display/st7785ma/display_driver.c diff --git a/core/embed/io/display/ltdc_dsi/display_driver.c b/core/embed/io/display/ltdc_dsi/display_driver.c new file mode 100644 index 0000000000..c0acf8bb78 --- /dev/null +++ b/core/embed/io/display/ltdc_dsi/display_driver.c @@ -0,0 +1,458 @@ + +#include +#include +#include + +#include + +#ifdef KERNEL_MODE + +#include +#include +#include + +#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 diff --git a/core/embed/io/display/ltdc_dsi/display_fb.c b/core/embed/io/display/ltdc_dsi/display_fb.c new file mode 100644 index 0000000000..ecef41231c --- /dev/null +++ b/core/embed/io/display/ltdc_dsi/display_fb.c @@ -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 . + */ +#ifdef KERNEL_MODE +#include +#include +#include + +#include +#include +#include +#include + +#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 diff --git a/core/embed/io/display/ltdc_dsi/display_fb_rgb565.c b/core/embed/io/display/ltdc_dsi/display_fb_rgb565.c new file mode 100644 index 0000000000..cd6a98f2e5 --- /dev/null +++ b/core/embed/io/display/ltdc_dsi/display_fb_rgb565.c @@ -0,0 +1,64 @@ +#include +#include + +#include +#include + +#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 diff --git a/core/embed/io/display/ltdc_dsi/display_fb_rgb888.c b/core/embed/io/display/ltdc_dsi/display_fb_rgb888.c new file mode 100644 index 0000000000..45cdb05243 --- /dev/null +++ b/core/embed/io/display/ltdc_dsi/display_fb_rgb888.c @@ -0,0 +1,65 @@ + +#ifdef KERNEL_MODE + +#include +#include + +#include +#include + +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 diff --git a/core/embed/io/display/ltdc_dsi/display_gfxmmu.c b/core/embed/io/display/ltdc_dsi/display_gfxmmu.c new file mode 100644 index 0000000000..78ce5a7ef8 --- /dev/null +++ b/core/embed/io/display/ltdc_dsi/display_gfxmmu.c @@ -0,0 +1,66 @@ +#ifdef KERNEL_MODE +#include + +#include + +#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 diff --git a/core/embed/io/display/ltdc_dsi/display_internal.h b/core/embed/io/display/ltdc_dsi/display_internal.h new file mode 100644 index 0000000000..2b071ea8cd --- /dev/null +++ b/core/embed/io/display/ltdc_dsi/display_internal.h @@ -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 . + */ + +#ifndef TREZOR_HAL_DISPLAY_INTERNAL_H +#define TREZOR_HAL_DISPLAY_INTERNAL_H + +#include +#include + +#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 diff --git a/core/embed/io/display/ltdc_dsi/panels/lx200d2406a/lx200d2406a.c b/core/embed/io/display/ltdc_dsi/panels/lx200d2406a/lx200d2406a.c new file mode 100644 index 0000000000..dfbe30ea7a --- /dev/null +++ b/core/embed/io/display/ltdc_dsi/panels/lx200d2406a/lx200d2406a.c @@ -0,0 +1,233 @@ + +#include +#include + +#include + +#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; +} diff --git a/core/embed/io/display/ltdc_dsi/panels/lx200d2406a/lx200d2406a.h b/core/embed/io/display/ltdc_dsi/panels/lx200d2406a/lx200d2406a.h new file mode 100644 index 0000000000..b696dde2fe --- /dev/null +++ b/core/embed/io/display/ltdc_dsi/panels/lx200d2406a/lx200d2406a.h @@ -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 . + */ + +#pragma once + +#include + +#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 diff --git a/core/embed/io/display/st7785ma/display_driver.c b/core/embed/io/display/st7785ma/display_driver.c deleted file mode 100644 index 3036e03685..0000000000 --- a/core/embed/io/display/st7785ma/display_driver.c +++ /dev/null @@ -1,93 +0,0 @@ - -#include -#ifdef KERNEL_MODE - -#include -#include -#include - -#include -#include -#include -#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 diff --git a/core/embed/models/T3W1/boards/trezor_t3w1_revA.h b/core/embed/models/T3W1/boards/trezor_t3w1_revA.h index 9d7b8da70e..64387315d7 100644 --- a/core/embed/models/T3W1/boards/trezor_t3w1_revA.h +++ b/core/embed/models/T3W1/boards/trezor_t3w1_revA.h @@ -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 diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revA.py b/core/site_scons/models/T3W1/trezor_t3w1_revA.py index 82a8e90b1b..569cebfbcb 100644 --- a/core/site_scons/models/T3W1/trezor_t3w1_revA.py +++ b/core/site_scons/models/T3W1/trezor_t3w1_revA.py @@ -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", ]