diff --git a/core/SConscript.bootloader b/core/SConscript.bootloader index 25a6d6c14d0..2c2e9a7cb28 100644 --- a/core/SConscript.bootloader +++ b/core/SConscript.bootloader @@ -20,7 +20,7 @@ if TREZOR_MODEL in ('1', ): ) Return() -FEATURES_WANTED = ["input", "rgb_led", "consumption_mask", "usb", "optiga"] +FEATURES_WANTED = ["input", "rgb_led", "consumption_mask", "usb", "optiga", "dma2d"] CCFLAGS_MOD = '' CPPPATH_MOD = [] diff --git a/core/embed/boardloader/main.c b/core/embed/boardloader/main.c index ea1a00d9b51..675f2973072 100644 --- a/core/embed/boardloader/main.c +++ b/core/embed/boardloader/main.c @@ -275,6 +275,7 @@ int main(void) { display_init(); display_clear(); + display_refresh(); #if defined USE_SD_CARD sdcard_init(); diff --git a/core/embed/boardloader/memory_stm32u58.ld b/core/embed/boardloader/memory_stm32u58.ld index 8fe61fed3bb..986813e562f 100644 --- a/core/embed/boardloader/memory_stm32u58.ld +++ b/core/embed/boardloader/memory_stm32u58.ld @@ -92,6 +92,7 @@ SECTIONS { __fb_start = .; *(.fb1*); *(.fb2*); + *(.framebuffer_select*); __fb_end = .; . = ALIGN(4); } >SRAM3 diff --git a/core/embed/bootloader/main.c b/core/embed/bootloader/main.c index ca6924ae80d..816ba9a5356 100644 --- a/core/embed/bootloader/main.c +++ b/core/embed/bootloader/main.c @@ -216,7 +216,6 @@ static usb_result_t bootloader_usb_loop(const vendor_header *const vhdr, hal_delay(1000); usb_stop(); usb_deinit(); - ui_screen_boot_empty(true); return CONTINUE_TO_FIRMWARE; } break; @@ -715,6 +714,7 @@ int bootloader_main(void) { #ifdef STM32U5 firmware_jump_fn = jump_to_fw_through_reset; #else + ui_screen_boot_empty(true); firmware_jump_fn = real_jump_to_firmware; #endif break; diff --git a/core/embed/bootloader/memory_stm32u58.ld b/core/embed/bootloader/memory_stm32u58.ld index e5bc29b3015..4111e8fed66 100644 --- a/core/embed/bootloader/memory_stm32u58.ld +++ b/core/embed/bootloader/memory_stm32u58.ld @@ -93,6 +93,7 @@ SECTIONS { __fb_start = .; *(.fb1*); *(.fb2*); + *(.framebuffer_select*); __fb_end = .; . = ALIGN(4); } >SRAM3 diff --git a/core/embed/firmware/memory_T3T1.ld b/core/embed/firmware/memory_T3T1.ld index 1f8abef7dd3..f074a5e2307 100644 --- a/core/embed/firmware/memory_T3T1.ld +++ b/core/embed/firmware/memory_T3T1.ld @@ -104,6 +104,7 @@ SECTIONS { __fb_start = .; *(.fb1*); *(.fb2*); + *(.framebuffer_select*); __fb_end = .; . = ALIGN(4); } >SRAM3 diff --git a/core/embed/lib/display_interface.h b/core/embed/lib/display_interface.h index 2b48c723581..19f68161ac5 100644 --- a/core/embed/lib/display_interface.h +++ b/core/embed/lib/display_interface.h @@ -58,6 +58,7 @@ void display_init(void); void display_reinit(void); void display_sync(void); void display_refresh(void); +void display_finish_actions(void); const char *display_save(const char *prefix); void display_clear_save(void); diff --git a/core/embed/prodtest/memory_stm32u58.ld b/core/embed/prodtest/memory_stm32u58.ld index 54795edb71e..4dfb376aa57 100644 --- a/core/embed/prodtest/memory_stm32u58.ld +++ b/core/embed/prodtest/memory_stm32u58.ld @@ -108,6 +108,7 @@ SECTIONS { __fb_start = .; *(.fb1*); *(.fb2*); + *(.framebuffer_select*); __fb_end = .; . = ALIGN(4); } >SRAM3 diff --git a/core/embed/reflash/memory_stm32u58.ld b/core/embed/reflash/memory_stm32u58.ld index a632e270c27..c298dbac1b9 100644 --- a/core/embed/reflash/memory_stm32u58.ld +++ b/core/embed/reflash/memory_stm32u58.ld @@ -109,6 +109,7 @@ SECTIONS { __fb_start = .; *(.fb1*); *(.fb2*); + *(.framebuffer_select*); __fb_end = .; . = ALIGN(4); } >SRAM3 diff --git a/core/embed/rust/src/trezorhal/display.rs b/core/embed/rust/src/trezorhal/display.rs index 8fb2e70abf7..95a17350bee 100644 --- a/core/embed/rust/src/trezorhal/display.rs +++ b/core/embed/rust/src/trezorhal/display.rs @@ -80,7 +80,11 @@ pub fn text_baseline(font: i32) -> i16 { } #[inline(always)] -#[cfg(all(feature = "disp_i8080_16bit_dw", not(feature = "disp_i8080_8bit_dw")))] +#[cfg(all( + not(feature = "framebuffer"), + feature = "disp_i8080_16bit_dw", + not(feature = "disp_i8080_8bit_dw") +))] pub fn pixeldata(c: u16) { unsafe { ffi::DISPLAY_DATA_ADDRESS.write_volatile(c); @@ -93,7 +97,7 @@ pub fn get_fb_addr() -> FrameBuffer { } #[inline(always)] -#[cfg(feature = "disp_i8080_8bit_dw")] +#[cfg(all(not(feature = "framebuffer"), feature = "disp_i8080_8bit_dw"))] pub fn pixeldata(c: u16) { unsafe { ffi::DISPLAY_DATA_ADDRESS.write_volatile((c & 0xff) as u8); @@ -126,7 +130,10 @@ pub fn pixel(fb: FrameBuffer, x: i16, y: i16, c: u32) { } #[inline(always)] -#[cfg(not(any(feature = "disp_i8080_16bit_dw", feature = "disp_i8080_8bit_dw")))] +#[cfg(any( + feature = "framebuffer", + not(any(feature = "disp_i8080_16bit_dw", feature = "disp_i8080_8bit_dw")) +))] pub fn pixeldata(c: u16) { unsafe { ffi::display_pixeldata(c); diff --git a/core/embed/rust/src/ui/model_tt/bootloader/mod.rs b/core/embed/rust/src/ui/model_tt/bootloader/mod.rs index af5422bc819..805d5be3b1f 100644 --- a/core/embed/rust/src/ui/model_tt/bootloader/mod.rs +++ b/core/embed/rust/src/ui/model_tt/bootloader/mod.rs @@ -261,11 +261,10 @@ fn screen_progress( bg_color, ); display::loader(progress, -20, fg_color, bg_color, icon); + display::refresh(); if initialize { fadein(); } - - display::refresh(); } #[no_mangle] diff --git a/core/embed/trezorhal/bg_copy.h b/core/embed/trezorhal/bg_copy.h new file mode 100644 index 00000000000..a3cc2f2fa5f --- /dev/null +++ b/core/embed/trezorhal/bg_copy.h @@ -0,0 +1,25 @@ + +#ifndef TREZORHAL_BG_COPY_H +#define TREZORHAL_BG_COPY_H + +#include +#include +#include + +/** + * Wait for the data transfer completion + */ +void bg_copy_wait(void); + +/** + * Performs data copy from src to dst in the background. The destination is + * constant, meaning the address is not incremented. Ensure the transfer + * completion by calling bg_copy_wait + * + * @param src source data address + * @param dst destination data address + * @param size size of data to be transferred in bytes + */ +void bg_copy_start_const_out_8(const uint8_t *src, uint8_t *dst, size_t size); + +#endif diff --git a/core/embed/trezorhal/boards/trezor_t3t1_v4.h b/core/embed/trezorhal/boards/trezor_t3t1_v4.h index cf4ea3fa40c..fa5807b009f 100644 --- a/core/embed/trezorhal/boards/trezor_t3t1_v4.h +++ b/core/embed/trezorhal/boards/trezor_t3t1_v4.h @@ -22,6 +22,10 @@ #define DISPLAY_IDENTIFY 1 #define DISPLAY_TE_PORT GPIOD #define DISPLAY_TE_PIN GPIO_PIN_12 +#define DISPLAY_TE_INTERRUPT_HANDLER EXTI12_IRQHandler +#define DISPLAY_TE_INTERRUPT_NUM EXTI12_IRQn +#define DISPLAY_TE_INTERRUPT_GPIOSEL EXTI_GPIOD +#define DISPLAY_TE_INTERRUPT_EXTI_LINE EXTI_LINE_12 #define DISPLAY_PANEL_INIT_SEQ lx154a2422_init_seq #define DISPLAY_PANEL_ROTATE lx154a2422_rotate diff --git a/core/embed/trezorhal/stm32f4/backlight_pwm.c b/core/embed/trezorhal/stm32f4/backlight_pwm.c index becf4602fa3..4c1cfa42e5d 100644 --- a/core/embed/trezorhal/stm32f4/backlight_pwm.c +++ b/core/embed/trezorhal/stm32f4/backlight_pwm.c @@ -22,6 +22,8 @@ int backlight_pwm_set(int val) { return BACKLIGHT; } +int backlight_pwm_get(void) { return BACKLIGHT; } + void backlight_pwm_init(void) { // init peripherals BACKLIGHT_PWM_PORT_CLK_EN(); diff --git a/core/embed/trezorhal/stm32f4/common.c b/core/embed/trezorhal/stm32f4/common.c index 5e8eb5472fb..23f9b820291 100644 --- a/core/embed/trezorhal/stm32f4/common.c +++ b/core/embed/trezorhal/stm32f4/common.c @@ -222,6 +222,7 @@ void collect_hw_entropy(void) { // which might be incompatible with the other layers older versions, // where this setting might be unknown void ensure_compatible_settings(void) { + display_finish_actions(); #ifdef TREZOR_MODEL_T display_set_big_endian(); display_orientation(0); diff --git a/core/embed/trezorhal/stm32f4/displays/ltdc.c b/core/embed/trezorhal/stm32f4/displays/ltdc.c index 505410ccb7c..52cfd015dae 100644 --- a/core/embed/trezorhal/stm32f4/displays/ltdc.c +++ b/core/embed/trezorhal/stm32f4/displays/ltdc.c @@ -415,3 +415,5 @@ void display_shift_window(uint16_t pixels) { uint16_t display_get_window_offset(void) { return MAX_DISPLAY_RESX - display_get_window_width(); } + +void display_finish_actions(void) {} diff --git a/core/embed/trezorhal/stm32f4/displays/st7789v.c b/core/embed/trezorhal/stm32f4/displays/st7789v.c index 53c0d2ac9f8..ff2932e5050 100644 --- a/core/embed/trezorhal/stm32f4/displays/st7789v.c +++ b/core/embed/trezorhal/stm32f4/displays/st7789v.c @@ -19,11 +19,14 @@ #include #include +#include #include TREZOR_BOARD #include "backlight_pwm.h" #include "display_interface.h" +#include "irq.h" #include "memzero.h" #include "st7789v.h" +#include "supervise.h" #include STM32_HAL_H #ifdef TREZOR_MODEL_T @@ -57,6 +60,42 @@ __IO DISP_MEM_TYPE *const DISPLAY_DATA_ADDRESS = (__IO DISP_MEM_TYPE *const)((uint32_t)DISPLAY_MEMORY_BASE | (DISPLAY_ADDR_SHIFT << DISPLAY_MEMORY_PIN)); +#ifdef FRAMEBUFFER +#ifndef STM32U5 +#error Framebuffer only supported on STM32U5 for now +#endif + +#ifndef BOARDLOADER +#include "bg_copy.h" +#endif + +#define DATA_TRANSFER(X) \ + DATA((X)&0xFF); \ + DATA((X) >> 8) + +__attribute__((section(".fb1"))) +ALIGN_32BYTES(static uint16_t PhysFrameBuffer0[DISPLAY_RESX * DISPLAY_RESY]); +__attribute__((section(".fb2"))) +ALIGN_32BYTES(static uint16_t PhysFrameBuffer1[DISPLAY_RESX * DISPLAY_RESY]); + +__attribute__(( + section(".framebuffer_select"))) static uint32_t act_frame_buffer = 0; + +#ifndef BOARDLOADER +static bool pending_fb_switch = false; +#endif + +static uint16_t window_x0 = 0; +static uint16_t window_y0 = 0; +static uint16_t window_x1 = 0; +static uint16_t window_y1 = 0; +static uint16_t cursor_x = 0; +static uint16_t cursor_y = 0; + +#else +#define DATA_TRANSFER(X) PIXELDATA(X) +#endif + // section "9.1.3 RDDID (04h): Read Display ID" // of ST7789V datasheet #define DISPLAY_ID_ST7789V 0x858552U @@ -72,8 +111,6 @@ __IO DISP_MEM_TYPE *const DISPLAY_DATA_ADDRESS = static int DISPLAY_ORIENTATION = -1; static display_padding_t DISPLAY_PADDING = {0}; -void display_pixeldata(uint16_t c) { PIXELDATA(c); } - void display_pixeldata_dirty(void) {} #ifdef DISPLAY_IDENTIFY @@ -160,7 +197,7 @@ static void display_unsleep(void) { } } -void display_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { +void panel_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { x0 += DISPLAY_PADDING.x; x1 += DISPLAY_PADDING.x; y0 += DISPLAY_PADDING.y; @@ -187,10 +224,14 @@ int display_orientation(int degrees) { if (degrees == 0 || degrees == 90 || degrees == 180 || degrees == 270) { DISPLAY_ORIENTATION = degrees; - display_set_window(0, 0, MAX_DISPLAY_RESX - 1, MAX_DISPLAY_RESY - 1); + panel_set_window(0, 0, MAX_DISPLAY_RESX - 1, MAX_DISPLAY_RESY - 1); +#ifdef FRAMEBUFFER + memzero(PhysFrameBuffer1, sizeof(PhysFrameBuffer1)); + memzero(PhysFrameBuffer0, sizeof(PhysFrameBuffer0)); +#endif for (uint32_t i = 0; i < MAX_DISPLAY_RESX * MAX_DISPLAY_RESY; i++) { // 2 bytes per pixel because we're using RGB 5-6-5 format - PIXELDATA(0x0000); + DATA_TRANSFER(0x0000); } #ifdef TREZOR_MODEL_T uint32_t id = display_identify(); @@ -202,6 +243,7 @@ int display_orientation(int degrees) { #else DISPLAY_PANEL_ROTATE(degrees, &DISPLAY_PADDING); #endif + panel_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1); } } return DISPLAY_ORIENTATION; @@ -209,7 +251,19 @@ int display_orientation(int degrees) { int display_get_orientation(void) { return DISPLAY_ORIENTATION; } -int display_backlight(int val) { return backlight_pwm_set(val); } +int display_backlight(int val) { +#ifdef FRAMEBUFFER +#ifndef BOARDLOADER + // wait for DMA transfer to finish before changing backlight + // so that we know that panel has current data + if (backlight_pwm_get() != val && !is_mode_handler()) { + bg_copy_wait(); + } +#endif +#endif + + return backlight_pwm_set(val); +} void display_init_seq(void) { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_RESET); // LCD_RST/PC14 @@ -287,6 +341,23 @@ void display_setup_fmc(void) { HAL_SRAM_Init(&external_display_data_sram, &normal_mode_timing, NULL); } +#ifdef FRAMEBUFFER +void display_setup_te_interrupt(void) { +#ifdef DISPLAY_TE_PIN + EXTI_HandleTypeDef EXTI_Handle = {0}; + EXTI_ConfigTypeDef EXTI_Config = {0}; + EXTI_Config.GPIOSel = DISPLAY_TE_INTERRUPT_GPIOSEL; + EXTI_Config.Line = DISPLAY_TE_INTERRUPT_EXTI_LINE; + EXTI_Config.Mode = EXTI_MODE_INTERRUPT; + EXTI_Config.Trigger = EXTI_TRIGGER_RISING; + HAL_EXTI_SetConfigLine(&EXTI_Handle, &EXTI_Config); + + // setup interrupt for tearing effect pin + HAL_NVIC_SetPriority(DISPLAY_TE_INTERRUPT_NUM, IRQ_PRI_DMA, 0); +#endif +} +#endif + void display_init(void) { // init peripherals __HAL_RCC_GPIOE_CLK_ENABLE(); @@ -350,6 +421,12 @@ void display_init(void) { display_init_seq(); display_set_little_endian(); + + panel_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1); + +#ifdef FRAMEBUFFER + display_setup_te_interrupt(); +#endif } void display_reinit(void) { @@ -361,6 +438,7 @@ void display_reinit(void) { display_set_little_endian(); DISPLAY_ORIENTATION = 0; + panel_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1); backlight_pwm_reinit(); @@ -373,25 +451,12 @@ void display_reinit(void) { lx154a2411_gamma(); } #endif -} -void display_sync(void) { -#ifdef DISPLAY_TE_PIN - uint32_t id = display_identify(); - if (id && (id != DISPLAY_ID_GC9307)) { - // synchronize with the panel synchronization signal - // in order to avoid visual tearing effects - while (GPIO_PIN_SET == HAL_GPIO_ReadPin(DISPLAY_TE_PORT, DISPLAY_TE_PIN)) { - } - while (GPIO_PIN_RESET == - HAL_GPIO_ReadPin(DISPLAY_TE_PORT, DISPLAY_TE_PIN)) { - } - } +#ifdef FRAMEBUFFER + display_setup_te_interrupt(); #endif } -void display_refresh(void) {} - void display_set_little_endian(void) { uint32_t id = display_identify(); if (id == DISPLAY_ID_GC9307) { @@ -430,8 +495,227 @@ const char *display_save(const char *prefix) { return NULL; } void display_clear_save(void) {} +#ifdef FRAMEBUFFER + +void display_pixeldata(uint16_t c) { + uint16_t *address = 0; + + if (act_frame_buffer == 0) { + address = PhysFrameBuffer1; + } else { + address = PhysFrameBuffer0; + } + + /* Get the rectangle start address */ + address += cursor_y * DISPLAY_RESX + cursor_x; + + *address = c; + + cursor_x++; + if (cursor_x > window_x1) { + cursor_x = window_x0; + cursor_y++; + } + if (cursor_y > window_y1) { + cursor_y = window_y0; + } +} + +void display_sync(void) {} + +#ifndef BOARDLOADER +void DISPLAY_TE_INTERRUPT_HANDLER(void) { + HAL_NVIC_DisableIRQ(DISPLAY_TE_INTERRUPT_NUM); + + if (act_frame_buffer == 1) { + bg_copy_start_const_out_8((uint8_t *)PhysFrameBuffer1, + (uint8_t *)DISPLAY_DATA_ADDRESS, + DISPLAY_RESX * DISPLAY_RESY * 2); + + } else { + bg_copy_start_const_out_8((uint8_t *)PhysFrameBuffer0, + (uint8_t *)DISPLAY_DATA_ADDRESS, + DISPLAY_RESX * DISPLAY_RESY * 2); + } + + pending_fb_switch = false; + __HAL_GPIO_EXTI_CLEAR_FLAG(DISPLAY_TE_PIN); +} + +static void wait_for_fb_switch(void) { + while (pending_fb_switch) { + __WFI(); + } + bg_copy_wait(); +} +#endif + +static void copy_fb_to_display(uint16_t *fb) { + for (int i = 0; i < DISPLAY_RESX * DISPLAY_RESY; i++) { + // 2 bytes per pixel because we're using RGB 5-6-5 format + DATA_TRANSFER(fb[i]); + } +} + +static void switch_fb_manually(void) { + // sync with the panel refresh + while (GPIO_PIN_SET == HAL_GPIO_ReadPin(DISPLAY_TE_PORT, DISPLAY_TE_PIN)) { + } + while (GPIO_PIN_RESET == HAL_GPIO_ReadPin(DISPLAY_TE_PORT, DISPLAY_TE_PIN)) { + } + + if (act_frame_buffer == 0) { + act_frame_buffer = 1; + copy_fb_to_display(PhysFrameBuffer1); + memcpy(PhysFrameBuffer0, PhysFrameBuffer1, sizeof(PhysFrameBuffer1)); + + } else { + act_frame_buffer = 0; + copy_fb_to_display(PhysFrameBuffer0); + memcpy(PhysFrameBuffer1, PhysFrameBuffer0, sizeof(PhysFrameBuffer1)); + } +} + +#ifndef BOARDLOADER +static void switch_fb_in_backround(void) { + if (act_frame_buffer == 0) { + act_frame_buffer = 1; + + memcpy(PhysFrameBuffer0, PhysFrameBuffer1, sizeof(PhysFrameBuffer1)); + + pending_fb_switch = true; + __HAL_GPIO_EXTI_CLEAR_FLAG(DISPLAY_TE_PIN); + svc_enableIRQ(DISPLAY_TE_INTERRUPT_NUM); + } else { + act_frame_buffer = 0; + memcpy(PhysFrameBuffer1, PhysFrameBuffer0, sizeof(PhysFrameBuffer1)); + + pending_fb_switch = true; + __HAL_GPIO_EXTI_CLEAR_FLAG(DISPLAY_TE_PIN); + svc_enableIRQ(DISPLAY_TE_INTERRUPT_NUM); + } +} +#endif + +void display_refresh(void) { +#ifndef BOARDLOADER + wait_for_fb_switch(); + + if (is_mode_handler()) { + switch_fb_manually(); + } else { + switch_fb_in_backround(); + } +#else + switch_fb_manually(); +#endif +} + +void display_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { + window_x0 = x0; + window_y0 = y0; + window_x1 = x1; + window_y1 = y1; + cursor_x = x0; + cursor_y = y0; +} + +uint8_t *display_get_wr_addr(void) { + uint32_t address = 0; + + if (act_frame_buffer == 0) { + address = (uint32_t)PhysFrameBuffer1; + } else { + address = (uint32_t)PhysFrameBuffer0; + } + + /* Get the rectangle start address */ + address = (address + (2 * (cursor_y * DISPLAY_RESX + cursor_x))); + + return (uint8_t *)address; +} + +uint32_t *display_get_fb_addr(void) { + uint32_t address = 0; + + if (act_frame_buffer == 0) { + address = (uint32_t)PhysFrameBuffer1; + } else { + address = (uint32_t)PhysFrameBuffer0; + } + + return (uint32_t *)address; +} +uint16_t display_get_window_width(void) { return window_x1 - window_x0 + 1; } + +uint16_t display_get_window_height(void) { return window_y1 - window_y0 + 1; } + +void display_shift_window(uint16_t pixels) { + uint16_t w = display_get_window_width(); + uint16_t h = display_get_window_height(); + + uint16_t line_rem = w - (cursor_x - window_x0); + + if (pixels < line_rem) { + cursor_x += pixels; + return; + } + + // start of next line + pixels = pixels - line_rem; + cursor_x = window_x0; + cursor_y++; + + // add the rest of pixels + cursor_y = window_y0 + (((cursor_y - window_y0) + (pixels / w)) % h); + cursor_x += pixels % w; +} + +uint16_t display_get_window_offset(void) { + return DISPLAY_RESX - display_get_window_width(); +} + +void display_efficient_clear(void) { + memzero(PhysFrameBuffer1, sizeof(PhysFrameBuffer1)); + memzero(PhysFrameBuffer0, sizeof(PhysFrameBuffer0)); +} + +void display_finish_actions(void) { +#ifndef BOARDLOADER + bg_copy_wait(); +#endif +} +#else +// NOT FRAMEBUFFER + +void display_pixeldata(uint16_t c) { PIXELDATA(c); } + +void display_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { + panel_set_window(x0, y0, x1, y1); +} + +void display_sync(void) { +#ifdef DISPLAY_TE_PIN + uint32_t id = display_identify(); + if (id && (id != DISPLAY_ID_GC9307)) { + // synchronize with the panel synchronization signal + // in order to avoid visual tearing effects + while (GPIO_PIN_SET == HAL_GPIO_ReadPin(DISPLAY_TE_PORT, DISPLAY_TE_PIN)) + ; + while (GPIO_PIN_RESET == HAL_GPIO_ReadPin(DISPLAY_TE_PORT, DISPLAY_TE_PIN)) + ; + } +#endif +} + +void display_refresh(void) {} + uint8_t *display_get_wr_addr(void) { return (uint8_t *)DISPLAY_DATA_ADDRESS; } uint16_t display_get_window_offset(void) { return 0; } void display_shift_window(uint16_t pixels) {} + +void display_finish_actions(void) {} + +#endif diff --git a/core/embed/trezorhal/stm32f4/displays/st7789v.h b/core/embed/trezorhal/stm32f4/displays/st7789v.h index 9590a57cd84..f3f19587144 100644 --- a/core/embed/trezorhal/stm32f4/displays/st7789v.h +++ b/core/embed/trezorhal/stm32f4/displays/st7789v.h @@ -24,12 +24,33 @@ typedef struct { #error "Unsupported display interface" #endif +#define DISP_MEM_TYPE uint8_t + extern __IO DISP_MEM_TYPE *const DISPLAY_CMD_ADDRESS; extern __IO DISP_MEM_TYPE *const DISPLAY_DATA_ADDRESS; #define CMD(X) (*DISPLAY_CMD_ADDRESS = (X)) #define DATA(X) (*DISPLAY_DATA_ADDRESS = (X)) +void display_set_little_endian(void); +void display_set_big_endian(void); +void display_set_slow_pwm(void); + +#ifdef FRAMEBUFFER +#define DISPLAY_FRAMEBUFFER_WIDTH DISPLAY_RESX +#define DISPLAY_FRAMEBUFFER_HEIGHT DISPLAY_RESY + +#define DISPLAY_EFFICIENT_CLEAR 1 + +static inline void display_pixel(uint8_t *fb, int16_t x, int16_t y, + uint16_t color) { + uint32_t p = 2 * (y * DISPLAY_FRAMEBUFFER_WIDTH + x); + *((uint16_t *)(fb + p)) = color; +} +void display_efficient_clear(void); + +#else + #ifdef USE_DISP_I8080_16BIT_DW #define PIXELDATA(X) DATA(X) #elif USE_DISP_I8080_8BIT_DW @@ -38,8 +59,6 @@ extern __IO DISP_MEM_TYPE *const DISPLAY_DATA_ADDRESS; DATA((X) >> 8) #endif -void display_set_little_endian(void); -void display_set_big_endian(void); -void display_set_slow_pwm(void); +#endif #endif //_ST7789V_H diff --git a/core/embed/trezorhal/stm32f4/displays/ug-2828tswig01.c b/core/embed/trezorhal/stm32f4/displays/ug-2828tswig01.c index 897958af755..a9561b756d2 100644 --- a/core/embed/trezorhal/stm32f4/displays/ug-2828tswig01.c +++ b/core/embed/trezorhal/stm32f4/displays/ug-2828tswig01.c @@ -368,3 +368,5 @@ void display_reinit(void) {} const char *display_save(const char *prefix) { return NULL; } void display_clear_save(void) {} + +void display_finish_actions(void) {} diff --git a/core/embed/trezorhal/stm32f4/displays/vg-2864ksweg01.c b/core/embed/trezorhal/stm32f4/displays/vg-2864ksweg01.c index ca894efffba..29f371cd6b0 100644 --- a/core/embed/trezorhal/stm32f4/displays/vg-2864ksweg01.c +++ b/core/embed/trezorhal/stm32f4/displays/vg-2864ksweg01.c @@ -297,3 +297,5 @@ void display_refresh(void) { const char *display_save(const char *prefix) { return NULL; } void display_clear_save(void) {} + +void display_finish_actions(void) {} diff --git a/core/embed/trezorhal/stm32u5/bg_copy.c b/core/embed/trezorhal/stm32u5/bg_copy.c new file mode 100644 index 00000000000..d47f776d258 --- /dev/null +++ b/core/embed/trezorhal/stm32u5/bg_copy.c @@ -0,0 +1,97 @@ +#include "bg_copy.h" +#include "irq.h" + +#include STM32_HAL_H + +#define MAX_DATA_SIZE 0xFFF0 + +static volatile uint32_t dma_transfer_remaining = 0; +static volatile uint32_t dma_data_transferred = 0; +static void *data_src = NULL; +static void *data_dst = NULL; +static DMA_HandleTypeDef DMA_Handle = {0}; + +void HAL_DMA_XferCpltCallback(DMA_HandleTypeDef *hdma) { + if (dma_transfer_remaining > MAX_DATA_SIZE) { + dma_transfer_remaining -= MAX_DATA_SIZE; + dma_data_transferred += MAX_DATA_SIZE; + } else { + dma_data_transferred += dma_transfer_remaining; + dma_transfer_remaining = 0; + } + + if (dma_transfer_remaining > 0) { + uint32_t data_to_send = dma_transfer_remaining > MAX_DATA_SIZE + ? MAX_DATA_SIZE + : dma_transfer_remaining; + + HAL_DMA_Start_IT(hdma, + (uint32_t) & ((uint8_t *)data_src)[dma_data_transferred], + (uint32_t)data_dst, data_to_send); + } +} + +void GPDMA1_Channel0_IRQHandler(void) { + if ((DMA_Handle.Instance->CSR & DMA_CSR_TCF) == 0) { + // error, abort the transfer and allow the next one to start + dma_data_transferred = 0; + dma_transfer_remaining = 0; + } + + HAL_DMA_IRQHandler(&DMA_Handle); + + if (dma_transfer_remaining == 0) { + // transfer finished, disable the channel + HAL_DMA_DeInit(&DMA_Handle); + HAL_NVIC_DisableIRQ(GPDMA1_Channel0_IRQn); + data_src = NULL; + data_dst = NULL; + } +} + +bool bg_copy_pending(void) { return dma_transfer_remaining > 0; } + +void bg_copy_wait(void) { + while (dma_transfer_remaining > 0) { + __WFI(); + } +} + +void bg_copy_start_const_out_8(const uint8_t *src, uint8_t *dst, size_t size) { + uint32_t data_to_send = size > MAX_DATA_SIZE ? MAX_DATA_SIZE : size; + dma_transfer_remaining = size; + dma_data_transferred = 0; + data_src = (void *)src; + data_dst = (void *)dst; + + // setup DMA for data copy to constant output address + + __HAL_RCC_GPDMA1_CLK_ENABLE(); + + /* USER CODE END GPDMA1_Init 1 */ + DMA_Handle.Instance = GPDMA1_Channel0; + DMA_Handle.XferCpltCallback = HAL_DMA_XferCpltCallback; + DMA_Handle.Init.Request = GPDMA1_REQUEST_HASH_IN; + DMA_Handle.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST; + DMA_Handle.Init.Direction = DMA_MEMORY_TO_MEMORY; + DMA_Handle.Init.SrcInc = DMA_SINC_INCREMENTED; + DMA_Handle.Init.DestInc = DMA_DINC_FIXED; + DMA_Handle.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE; + DMA_Handle.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE; + DMA_Handle.Init.Priority = DMA_LOW_PRIORITY_HIGH_WEIGHT; + DMA_Handle.Init.SrcBurstLength = 1; + DMA_Handle.Init.DestBurstLength = 1; + DMA_Handle.Init.TransferAllocatedPort = + DMA_SRC_ALLOCATED_PORT1 | DMA_DEST_ALLOCATED_PORT0; + DMA_Handle.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER; + DMA_Handle.Init.Mode = DMA_NORMAL; + HAL_DMA_Init(&DMA_Handle); + HAL_DMA_ConfigChannelAttributes(&DMA_Handle, DMA_CHANNEL_SEC | + DMA_CHANNEL_SRC_SEC | + DMA_CHANNEL_DEST_SEC); + + HAL_NVIC_SetPriority(GPDMA1_Channel0_IRQn, IRQ_PRI_DMA, 0); + HAL_NVIC_EnableIRQ(GPDMA1_Channel0_IRQn); + + HAL_DMA_Start_IT(&DMA_Handle, (uint32_t)src, (uint32_t)dst, data_to_send); +} diff --git a/core/embed/trezorhal/stm32u5/common.c b/core/embed/trezorhal/stm32u5/common.c index dfe50cfcd82..9b081c446de 100644 --- a/core/embed/trezorhal/stm32u5/common.c +++ b/core/embed/trezorhal/stm32u5/common.c @@ -204,7 +204,7 @@ void collect_hw_entropy(void) { // this function resets settings changed in one layer (bootloader/firmware), // which might be incompatible with the other layers older versions, // where this setting might be unknown -void ensure_compatible_settings(void) {} +void ensure_compatible_settings(void) { display_finish_actions(); } void show_wipe_code_screen(void) { error_uni("WIPE CODE ENTERED", "All data has been erased from the device", diff --git a/core/embed/trezorhal/stm32u5/displays/dsi.c b/core/embed/trezorhal/stm32u5/displays/dsi.c index cf31cf64095..8e44fabb416 100644 --- a/core/embed/trezorhal/stm32u5/displays/dsi.c +++ b/core/embed/trezorhal/stm32u5/displays/dsi.c @@ -1728,3 +1728,5 @@ const char *display_save(const char *prefix) { return NULL; } void display_clear_save(void) {} void display_pixeldata_dirty(void) {} + +void display_finish_actions(void) {} diff --git a/core/embed/trezorhal/unix/display-unix.c b/core/embed/trezorhal/unix/display-unix.c index d60a18d72bf..85fccca3492 100644 --- a/core/embed/trezorhal/unix/display-unix.c +++ b/core/embed/trezorhal/unix/display-unix.c @@ -345,3 +345,5 @@ void display_clear_save(void) { } uint8_t *display_get_wr_addr(void) { return (uint8_t *)DISPLAY_DATA_ADDRESS; } + +void display_finish_actions(void) {} diff --git a/core/site_scons/boards/stm32f4_common.py b/core/site_scons/boards/stm32f4_common.py index c9d83588cfa..654f043819e 100644 --- a/core/site_scons/boards/stm32f4_common.py +++ b/core/site_scons/boards/stm32f4_common.py @@ -75,7 +75,7 @@ def stm32f4_common_files(env, defines, sources, paths): "-DSTM32_HAL_H=;" "-DSTM32F4;" "-DFLASH_BLOCK_WORDS=1;" - "-DFLASH_BIT_ACCESS=1" + "-DFLASH_BIT_ACCESS=1;" ) env.get("ENV")["SUFFIX"] = "stm32f4" diff --git a/core/site_scons/boards/stm32u5_common.py b/core/site_scons/boards/stm32u5_common.py index 2ddf6c808d2..1c274faf8d1 100644 --- a/core/site_scons/boards/stm32u5_common.py +++ b/core/site_scons/boards/stm32u5_common.py @@ -22,6 +22,7 @@ def stm32u5_common_files(env, defines, sources, paths): "vendor/stm32u5xx_hal_driver/Src/stm32u5xx_hal_dma.c", "vendor/stm32u5xx_hal_driver/Src/stm32u5xx_hal_dma_ex.c", "vendor/stm32u5xx_hal_driver/Src/stm32u5xx_hal_dsi.c", + "vendor/stm32u5xx_hal_driver/Src/stm32u5xx_hal_exti.c", "vendor/stm32u5xx_hal_driver/Src/stm32u5xx_hal_flash.c", "vendor/stm32u5xx_hal_driver/Src/stm32u5xx_hal_flash_ex.c", "vendor/stm32u5xx_hal_driver/Src/stm32u5xx_hal_gfxmmu.c", @@ -87,7 +88,7 @@ def stm32u5_common_files(env, defines, sources, paths): "-I../../vendor/cmsis_5/CMSIS/Core/Include;" "-DSTM32_HAL_H=;" "-DSTM32U5;" - "-DFLASH_BLOCK_WORDS=4" + "-DFLASH_BLOCK_WORDS=4;" ) env.get("ENV")["SUFFIX"] = "stm32u5" diff --git a/core/site_scons/boards/trezor_t3t1_v4.py b/core/site_scons/boards/trezor_t3t1_v4.py index 7e18d2586d2..7d66720dae6 100644 --- a/core/site_scons/boards/trezor_t3t1_v4.py +++ b/core/site_scons/boards/trezor_t3t1_v4.py @@ -17,6 +17,8 @@ def configure( hw_model = get_hw_model_as_number("T3T1") hw_revision = 0 features_available.append("disp_i8080_8bit_dw") + features_available.append("framebuffer") + defines += ["FRAMEBUFFER"] mcu = "STM32U585xx" linker_script = "stm32u58" @@ -44,6 +46,10 @@ def configure( "embed/trezorhal/stm32u5/displays/panels/lx154a2422.c", ] + env_constraints = env.get("CONSTRAINTS") + if not (env_constraints and "limited_util_s" in env_constraints): + sources += ["embed/trezorhal/stm32u5/bg_copy.c"] + features_available.append("backlight") if "input" in features_wanted: @@ -106,4 +112,8 @@ def configure( defs = env.get("CPPDEFINES_IMPLICIT") defs += ["__ARM_FEATURE_CMSE=3"] + rust_defs = env.get("ENV")["RUST_INCLUDES"] + rust_defs += "-DFRAMEBUFFER;" + env.get("ENV")["RUST_INCLUDES"] = rust_defs + return features_available diff --git a/core/src/apps/bitcoin/sign_tx/approvers.py b/core/src/apps/bitcoin/sign_tx/approvers.py index b6656f70d62..dece0a57250 100644 --- a/core/src/apps/bitcoin/sign_tx/approvers.py +++ b/core/src/apps/bitcoin/sign_tx/approvers.py @@ -16,6 +16,8 @@ from .tx_info import OriginalTxInfo if TYPE_CHECKING: + from typing import Optional + from trezor.crypto import bip32 from trezor.messages import SignTx, TxAckPaymentRequest, TxInput, TxOutput @@ -23,6 +25,7 @@ from apps.common.keychain import Keychain from ..authorization import CoinJoinAuthorization + from .bitcoin import Bitcoin from .payment_request import PaymentRequestVerifier from .tx_info import TxInfo @@ -132,7 +135,12 @@ async def approve_orig_txids( ) -> None: raise NotImplementedError - async def approve_tx(self, tx_info: TxInfo, orig_txs: list[OriginalTxInfo]) -> None: + async def approve_tx( + self, + tx_info: TxInfo, + orig_txs: list[OriginalTxInfo], + signer: Optional[Bitcoin], + ) -> None: self.finish_payment_request() @@ -270,13 +278,18 @@ def _replacement_title( else: return TR.bitcoin__title_update_transaction - async def approve_tx(self, tx_info: TxInfo, orig_txs: list[OriginalTxInfo]) -> None: + async def approve_tx( + self, + tx_info: TxInfo, + orig_txs: list[OriginalTxInfo], + signer: Optional[Bitcoin], + ) -> None: from trezor.wire import NotEnoughFunds coin = self.coin # local_cache_attribute amount_unit = self.amount_unit # local_cache_attribute - await super().approve_tx(tx_info, orig_txs) + await super().approve_tx(tx_info, orig_txs, signer) if self.has_unverified_external_input: await helpers.confirm_unverified_external_input() @@ -336,6 +349,8 @@ async def approve_tx(self, tx_info: TxInfo, orig_txs: list[OriginalTxInfo]) -> N ) if not self.is_payjoin(): + if signer is not None: + signer.init_signing() title = self._replacement_title(tx_info, orig_txs) # Not a PayJoin: Show the actual fee difference, since any difference in the fee is # coming entirely from the user's own funds and from decreases of external outputs. @@ -344,6 +359,8 @@ async def approve_tx(self, tx_info: TxInfo, orig_txs: list[OriginalTxInfo]) -> N title, fee - orig_fee, fee, fee_rate, coin, amount_unit ) elif spending > orig_spending: + if signer is not None: + signer.init_signing() title = self._replacement_title(tx_info, orig_txs) # PayJoin and user is spending more: Show the increase in the user's contribution # to the fee, ignoring any contribution from external inputs. Decreasing of @@ -363,6 +380,9 @@ async def approve_tx(self, tx_info: TxInfo, orig_txs: list[OriginalTxInfo]) -> N tx_info.tx.lock_time, tx_info.lock_time_disabled() ) + if signer is not None: + signer.init_signing() + if not self.external_in: await helpers.confirm_total( total, @@ -511,10 +531,15 @@ def _verify_coinjoin_request(self, tx_info: TxInfo): self.h_request.get_digest(), ) - async def approve_tx(self, tx_info: TxInfo, orig_txs: list[OriginalTxInfo]) -> None: + async def approve_tx( + self, + tx_info: TxInfo, + orig_txs: list[OriginalTxInfo], + signer: Optional[Bitcoin], + ) -> None: from ..authorization import FEE_RATE_DECIMALS - await super().approve_tx(tx_info, orig_txs) + await super().approve_tx(tx_info, orig_txs, signer) if not self._verify_coinjoin_request(tx_info): raise DataError("Invalid signature in coinjoin request.") diff --git a/core/src/apps/bitcoin/sign_tx/bitcoin.py b/core/src/apps/bitcoin/sign_tx/bitcoin.py index 10267415e57..ec31da941e0 100644 --- a/core/src/apps/bitcoin/sign_tx/bitcoin.py +++ b/core/src/apps/bitcoin/sign_tx/bitcoin.py @@ -40,6 +40,20 @@ class Bitcoin: + def init_signing(self) -> None: + # Next shown progress bar is already signing progress, but it isn't shown until approval from next dialog + progress.init_signing( + len(self.external), + len(self.segwit), + len(self.presigned), + self.taproot_only, + self.serialize, + self.coin, + self.tx_info.tx, + self.orig_txs, + ) + self.signing = True + async def signer(self) -> None: progress.init( self.tx_info.tx, is_coinjoin=isinstance(self.approver, CoinJoinApprover) @@ -56,18 +70,13 @@ async def signer(self) -> None: await self.step2_approve_outputs() # Check fee, approve lock_time and total. - await self.approver.approve_tx(self.tx_info, self.orig_txs) + await self.approver.approve_tx(self.tx_info, self.orig_txs, self) - progress.init_signing( - len(self.external), - len(self.segwit), - len(self.presigned), - self.taproot_only, - self.serialize, - self.coin, - self.tx_info.tx, - self.orig_txs, - ) + # Make sure proper progress is shown, in case dialog was not required + if not self.signing: + self.init_signing() + progress.report_init() + progress.report() # Following steps can take a long time, make sure autolock doesn't kick in. # This is set to True again after workflow is finished in start_default(). @@ -129,6 +138,9 @@ def __init__( # indicates whether all internal inputs are Taproot self.taproot_only = True + # indicates whether the transaction is being signed + self.signing = False + # transaction and signature serialization _SERIALIZED_TX_BUFFER[:] = bytes() self.serialized_tx = _SERIALIZED_TX_BUFFER diff --git a/core/src/apps/bitcoin/sign_tx/progress.py b/core/src/apps/bitcoin/sign_tx/progress.py index e94e3a13633..c375240c2cc 100644 --- a/core/src/apps/bitcoin/sign_tx/progress.py +++ b/core/src/apps/bitcoin/sign_tx/progress.py @@ -97,9 +97,6 @@ def init_signing( if serialize and not coin.decred: self.steps += tx.outputs_count - self.report_init() - self.report() - def init_prev_tx(self, inputs: int, outputs: int) -> None: self.prev_tx_step = _PREV_TX_MULTIPLIER / (inputs + outputs) diff --git a/core/src/trezor/ui/layouts/tr/progress.py b/core/src/trezor/ui/layouts/tr/progress.py index 0171eca4630..36ac9ced464 100644 --- a/core/src/trezor/ui/layouts/tr/progress.py +++ b/core/src/trezor/ui/layouts/tr/progress.py @@ -17,6 +17,7 @@ def __init__( self.layout = layout self.layout.attach_timer_fn(self.set_timer) self.layout.paint() + ui.refresh() def set_timer(self, token: int, deadline: int) -> None: raise RuntimeError # progress layouts should not set timers diff --git a/core/src/trezor/ui/layouts/tt/progress.py b/core/src/trezor/ui/layouts/tt/progress.py index 15c93382193..5997d247f51 100644 --- a/core/src/trezor/ui/layouts/tt/progress.py +++ b/core/src/trezor/ui/layouts/tt/progress.py @@ -18,6 +18,7 @@ def __init__( ui.backlight_fade(ui.style.BACKLIGHT_DIM) self.layout.attach_timer_fn(self.set_timer) self.layout.paint() + ui.refresh() ui.backlight_fade(ui.style.BACKLIGHT_NORMAL) def set_timer(self, token: int, deadline: int) -> None: diff --git a/core/tests/test_apps.bitcoin.approver.py b/core/tests/test_apps.bitcoin.approver.py index 92d142de4c9..fbbee64d6b5 100644 --- a/core/tests/test_apps.bitcoin.approver.py +++ b/core/tests/test_apps.bitcoin.approver.py @@ -188,7 +188,7 @@ def test_coinjoin_lots_of_inputs(self): else: await_result(approver.add_external_output(txo, script_pubkey=bytes(22))) - await_result(approver.approve_tx(TxInfo(signer, tx), [])) + await_result(approver.approve_tx(TxInfo(signer, tx), [], None)) def test_coinjoin_input_account_depth_mismatch(self): txi = TxInput(