mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-23 14:58:09 +00:00
fix(core): improve framebuffer switching
[no changelog]
This commit is contained in:
parent
0cc2ccd568
commit
bb310ad60d
@ -7,9 +7,10 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait for the data transfer completion
|
* Callback function invoked from the IRQ context
|
||||||
|
* when the transfer is complete
|
||||||
*/
|
*/
|
||||||
void bg_copy_wait(void);
|
typedef void (*bg_copy_callback_t)(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs data copy from src to dst in the background. The destination is
|
* Performs data copy from src to dst in the background. The destination is
|
||||||
@ -19,8 +20,21 @@ void bg_copy_wait(void);
|
|||||||
* @param src source data address
|
* @param src source data address
|
||||||
* @param dst destination data address
|
* @param dst destination data address
|
||||||
* @param size size of data to be transferred in bytes
|
* @param size size of data to be transferred in bytes
|
||||||
|
* @param callback optional callback to be called when the transfer is complete
|
||||||
|
*/
|
||||||
|
void bg_copy_start_const_out_8(const uint8_t *src, uint8_t *dst, size_t size,
|
||||||
|
bg_copy_callback_t callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for the data transfer completion
|
||||||
|
*/
|
||||||
|
void bg_copy_wait(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Immediately aborts the data transfer
|
||||||
|
*
|
||||||
|
* @note The callback will not be called
|
||||||
*/
|
*/
|
||||||
void bg_copy_start_const_out_8(const uint8_t *src, uint8_t *dst, size_t size);
|
|
||||||
|
|
||||||
void bg_copy_abort(void);
|
void bg_copy_abort(void);
|
||||||
|
|
||||||
|
@ -567,12 +567,12 @@ void DISPLAY_TE_INTERRUPT_HANDLER(void) {
|
|||||||
if (act_frame_buffer == 1) {
|
if (act_frame_buffer == 1) {
|
||||||
bg_copy_start_const_out_8((uint8_t *)PhysFrameBuffer1,
|
bg_copy_start_const_out_8((uint8_t *)PhysFrameBuffer1,
|
||||||
(uint8_t *)DISPLAY_DATA_ADDRESS,
|
(uint8_t *)DISPLAY_DATA_ADDRESS,
|
||||||
DISPLAY_RESX * DISPLAY_RESY * 2);
|
DISPLAY_RESX * DISPLAY_RESY * 2, NULL);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
bg_copy_start_const_out_8((uint8_t *)PhysFrameBuffer0,
|
bg_copy_start_const_out_8((uint8_t *)PhysFrameBuffer0,
|
||||||
(uint8_t *)DISPLAY_DATA_ADDRESS,
|
(uint8_t *)DISPLAY_DATA_ADDRESS,
|
||||||
DISPLAY_RESX * DISPLAY_RESY * 2);
|
DISPLAY_RESX * DISPLAY_RESY * 2, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
pending_fb_switch = false;
|
pending_fb_switch = false;
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include <xdisplay.h>
|
#include <xdisplay.h>
|
||||||
|
|
||||||
#include "display_fb.h"
|
#include "display_fb.h"
|
||||||
|
#include "display_internal.h"
|
||||||
#include "display_io.h"
|
#include "display_io.h"
|
||||||
#include "display_panel.h"
|
#include "display_panel.h"
|
||||||
|
|
||||||
@ -39,14 +40,8 @@
|
|||||||
#error "Incompatible display resolution"
|
#error "Incompatible display resolution"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Display driver context.
|
|
||||||
typedef struct {
|
|
||||||
// Current display orientation (0, 90, 180, 270)
|
|
||||||
int orientation_angle;
|
|
||||||
} display_driver_t;
|
|
||||||
|
|
||||||
// Display driver instance
|
// Display driver instance
|
||||||
static display_driver_t g_display_driver;
|
display_driver_t g_display_driver;
|
||||||
|
|
||||||
void display_init(void) {
|
void display_init(void) {
|
||||||
display_driver_t* drv = &g_display_driver;
|
display_driver_t* drv = &g_display_driver;
|
||||||
@ -84,7 +79,8 @@ void display_reinit(void) {
|
|||||||
void display_finish_actions(void) {
|
void display_finish_actions(void) {
|
||||||
#ifdef XFRAMEBUFFER
|
#ifdef XFRAMEBUFFER
|
||||||
#ifndef BOARDLOADER
|
#ifndef BOARDLOADER
|
||||||
wait_for_fb_switch();
|
display_ensure_refreshed();
|
||||||
|
svc_disableIRQ(DISPLAY_TE_INTERRUPT_NUM);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -135,18 +131,4 @@ int display_get_orientation(void) {
|
|||||||
return drv->orientation_angle;
|
return drv->orientation_angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
void display_wait_for_sync(void) {
|
|
||||||
#ifdef DISPLAY_TE_PIN
|
|
||||||
uint32_t id = display_panel_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_set_compatible_settings(void) { display_panel_set_big_endian(); }
|
void display_set_compatible_settings(void) { display_panel_set_big_endian(); }
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include STM32_HAL_H
|
#include STM32_HAL_H
|
||||||
|
|
||||||
#include "display_fb.h"
|
#include "display_fb.h"
|
||||||
|
#include "display_internal.h"
|
||||||
#include "display_io.h"
|
#include "display_io.h"
|
||||||
#include "display_panel.h"
|
#include "display_panel.h"
|
||||||
#include "xdisplay.h"
|
#include "xdisplay.h"
|
||||||
@ -41,173 +42,220 @@
|
|||||||
#error Framebuffer only supported on STM32U5 for now
|
#error Framebuffer only supported on STM32U5 for now
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// The following code supports only 1 or 2 frame buffers
|
||||||
|
_Static_assert(FRAME_BUFFER_COUNT == 1 || FRAME_BUFFER_COUNT == 2);
|
||||||
|
|
||||||
// Size of the physical frame buffer in bytes
|
// Size of the physical frame buffer in bytes
|
||||||
#define PHYSICAL_FRAME_BUFFER_SIZE (DISPLAY_RESX * DISPLAY_RESY * 2)
|
#define PHYSICAL_FRAME_BUFFER_SIZE (DISPLAY_RESX * DISPLAY_RESY * 2)
|
||||||
|
|
||||||
// Physical frame buffers in internal SRAM memory.
|
// Physical frame buffers in internal SRAM memory.
|
||||||
// Both frame buffers layes in the fixed addresses that
|
// Both frame buffers layes in the fixed addresses that
|
||||||
// are shared between bootloaders and the firmware.
|
// are shared between bootloaders and the firmware.
|
||||||
__attribute__((section(".fb1")))
|
static __attribute__((section(".fb1")))
|
||||||
ALIGN_32BYTES(uint8_t physical_frame_buffer_0[PHYSICAL_FRAME_BUFFER_SIZE]);
|
ALIGN_32BYTES(uint8_t physical_frame_buffer_0[PHYSICAL_FRAME_BUFFER_SIZE]);
|
||||||
__attribute__((section(".fb2")))
|
|
||||||
ALIGN_32BYTES(uint8_t physical_frame_buffer_1[PHYSICAL_FRAME_BUFFER_SIZE]);
|
|
||||||
|
|
||||||
// The current frame buffer selector at fixed memory address
|
#if (FRAME_BUFFER_COUNT > 1)
|
||||||
// It's shared between bootloaders and the firmware
|
static __attribute__((section(".fb2")))
|
||||||
__attribute__((section(".framebuffer_select"))) uint32_t current_frame_buffer =
|
ALIGN_32BYTES(uint8_t physical_frame_buffer_1[PHYSICAL_FRAME_BUFFER_SIZE]);
|
||||||
0;
|
#endif
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
if (index == 0) {
|
||||||
|
return physical_frame_buffer_0;
|
||||||
|
#if (FRAME_BUFFER_COUNT > 1)
|
||||||
|
} else if (index == 1) {
|
||||||
|
return physical_frame_buffer_1;
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void display_physical_fb_clear(void) {
|
void display_physical_fb_clear(void) {
|
||||||
memset(physical_frame_buffer_0, 0, sizeof(physical_frame_buffer_0));
|
for (int i = 0; i < FRAME_BUFFER_COUNT; i++) {
|
||||||
memset(physical_frame_buffer_1, 0, sizeof(physical_frame_buffer_1));
|
memset(get_fb_ptr(i), 0, PHYSICAL_FRAME_BUFFER_SIZE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef BOARDLOADER
|
#ifndef BOARDLOADER
|
||||||
static volatile uint16_t pending_fb_switch = 0;
|
|
||||||
static volatile uint32_t last_fb_update_time = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef BOARDLOADER
|
// Callback called when the background copying is done
|
||||||
|
// It's called from the IRQ context
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interrupt routing handling TE signal
|
||||||
void DISPLAY_TE_INTERRUPT_HANDLER(void) {
|
void DISPLAY_TE_INTERRUPT_HANDLER(void) {
|
||||||
if (pending_fb_switch == 1) {
|
display_driver_t *drv = &g_display_driver;
|
||||||
if (current_frame_buffer == 1) {
|
|
||||||
bg_copy_start_const_out_8((uint8_t *)physical_frame_buffer_1,
|
|
||||||
(uint8_t *)DISPLAY_DATA_ADDRESS,
|
|
||||||
DISPLAY_RESX * DISPLAY_RESY * 2);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
bg_copy_start_const_out_8((uint8_t *)physical_frame_buffer_0,
|
|
||||||
(uint8_t *)DISPLAY_DATA_ADDRESS,
|
|
||||||
DISPLAY_RESX * DISPLAY_RESY * 2);
|
|
||||||
}
|
|
||||||
last_fb_update_time = HAL_GetTick();
|
|
||||||
pending_fb_switch = 2;
|
|
||||||
} else if (pending_fb_switch == 2) {
|
|
||||||
HAL_NVIC_DisableIRQ(DISPLAY_TE_INTERRUPT_NUM);
|
|
||||||
pending_fb_switch = 0;
|
|
||||||
} else {
|
|
||||||
HAL_NVIC_DisableIRQ(DISPLAY_TE_INTERRUPT_NUM);
|
|
||||||
pending_fb_switch = 0;
|
|
||||||
}
|
|
||||||
__HAL_GPIO_EXTI_CLEAR_FLAG(DISPLAY_TE_PIN);
|
__HAL_GPIO_EXTI_CLEAR_FLAG(DISPLAY_TE_PIN);
|
||||||
}
|
|
||||||
|
|
||||||
static void copy_fb_to_display(const uint16_t *fb) {
|
if (drv->queue.rix >= FRAME_BUFFER_COUNT) {
|
||||||
for (int i = 0; i < DISPLAY_RESX * DISPLAY_RESY; i++) {
|
// This is an invalid state and we should never get here
|
||||||
// 2 bytes per pixel because we're using RGB 5-6-5 format
|
return;
|
||||||
ISSUE_PIXEL_DATA(fb[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void wait_for_fb_switch(void) {
|
|
||||||
if (is_mode_handler()) {
|
|
||||||
if (pending_fb_switch != 0) {
|
|
||||||
if (current_frame_buffer == 0) {
|
|
||||||
copy_fb_to_display((uint16_t *)physical_frame_buffer_1);
|
|
||||||
} else {
|
|
||||||
copy_fb_to_display((uint16_t *)physical_frame_buffer_0);
|
|
||||||
}
|
|
||||||
pending_fb_switch = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while (pending_fb_switch != 0) {
|
|
||||||
__WFI();
|
|
||||||
}
|
|
||||||
bg_copy_wait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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 (current_frame_buffer == 0) {
|
switch (drv->queue.entry[drv->queue.rix]) {
|
||||||
current_frame_buffer = 1;
|
case FB_STATE_EMPTY:
|
||||||
copy_fb_to_display((uint16_t *)physical_frame_buffer_1);
|
case FB_STATE_PREPARING:
|
||||||
memcpy(physical_frame_buffer_0, physical_frame_buffer_1,
|
// No new frame queued
|
||||||
sizeof(physical_frame_buffer_0));
|
break;
|
||||||
|
|
||||||
} else {
|
case FB_STATE_COPYING:
|
||||||
current_frame_buffer = 0;
|
// Currently we are copying a data to the display.
|
||||||
copy_fb_to_display((uint16_t *)physical_frame_buffer_0);
|
// We need to wait for the next TE interrupt.
|
||||||
memcpy(physical_frame_buffer_1, physical_frame_buffer_0,
|
break;
|
||||||
sizeof(physical_frame_buffer_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef BOARDLOADER
|
case FB_STATE_READY:
|
||||||
static void switch_fb_in_background(void) {
|
// Now it's proper time to copy the data to the display
|
||||||
if (current_frame_buffer == 0) {
|
drv->queue.entry[drv->queue.rix] = FB_STATE_COPYING;
|
||||||
current_frame_buffer = 1;
|
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);
|
||||||
|
|
||||||
memcpy(physical_frame_buffer_0, physical_frame_buffer_1,
|
// NOTE: when copying is done, this queue slot is marked empty
|
||||||
sizeof(physical_frame_buffer_0));
|
// (see bg_copy_callback())
|
||||||
|
break;
|
||||||
|
|
||||||
pending_fb_switch = 1;
|
default:
|
||||||
__HAL_GPIO_EXTI_CLEAR_FLAG(DISPLAY_TE_PIN);
|
// This is an invalid state and we should never get here
|
||||||
svc_enableIRQ(DISPLAY_TE_INTERRUPT_NUM);
|
break;
|
||||||
} else {
|
|
||||||
current_frame_buffer = 0;
|
|
||||||
memcpy(physical_frame_buffer_1, physical_frame_buffer_0,
|
|
||||||
sizeof(physical_frame_buffer_1));
|
|
||||||
|
|
||||||
pending_fb_switch = 1;
|
|
||||||
__HAL_GPIO_EXTI_CLEAR_FLAG(DISPLAY_TE_PIN);
|
|
||||||
svc_enableIRQ(DISPLAY_TE_INTERRUPT_NUM);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
display_fb_info_t display_get_frame_buffer(void) {
|
display_fb_info_t display_get_frame_buffer(void) {
|
||||||
void *addr;
|
display_driver_t *drv = &g_display_driver;
|
||||||
|
|
||||||
if (current_frame_buffer == 0) {
|
frame_buffer_state_t state;
|
||||||
addr = (void *)physical_frame_buffer_1;
|
|
||||||
} else {
|
// We have to wait if the buffer was passed for copying
|
||||||
addr = (void *)physical_frame_buffer_0;
|
// to the interrupt handler
|
||||||
}
|
do {
|
||||||
|
state = drv->queue.entry[drv->queue.wix];
|
||||||
|
} while (state == FB_STATE_READY || state == FB_STATE_COPYING);
|
||||||
|
|
||||||
|
if (state == FB_STATE_EMPTY) {
|
||||||
|
// First use of this buffer, copy the previous buffer into it
|
||||||
|
#if (FRAME_BUFFER_COUNT > 1)
|
||||||
|
uint8_t *src = get_fb_ptr((FRAME_BUFFER_COUNT + drv->queue.wix - 1) %
|
||||||
|
FRAME_BUFFER_COUNT);
|
||||||
|
uint8_t *dst = get_fb_ptr(drv->queue.wix);
|
||||||
|
memcpy(dst, src, PHYSICAL_FRAME_BUFFER_SIZE);
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
drv->queue.entry[drv->queue.wix] = FB_STATE_PREPARING;
|
||||||
|
|
||||||
display_fb_info_t fb = {
|
display_fb_info_t fb = {
|
||||||
.ptr = addr,
|
.ptr = get_fb_ptr(drv->queue.wix),
|
||||||
.stride = DISPLAY_RESX * sizeof(uint16_t),
|
.stride = DISPLAY_RESX * sizeof(uint16_t),
|
||||||
};
|
};
|
||||||
|
|
||||||
return fb;
|
return fb;
|
||||||
}
|
}
|
||||||
|
|
||||||
void display_refresh(void) {
|
// Copies the frame buffer with the given index to the display
|
||||||
#ifndef BOARDLOADER
|
static void copy_fb_to_display(uint8_t index) {
|
||||||
|
uint16_t *fb = (uint16_t *)get_fb_ptr(index);
|
||||||
|
|
||||||
if (is_mode_handler()) {
|
if (fb != NULL) {
|
||||||
if (pending_fb_switch != 0) {
|
display_panel_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1);
|
||||||
pending_fb_switch = 0;
|
for (int i = 0; i < DISPLAY_RESX * DISPLAY_RESY; i++) {
|
||||||
bg_copy_abort();
|
// 2 bytes per pixel because we're using RGB 5-6-5 format
|
||||||
|
ISSUE_PIXEL_DATA(fb[i]);
|
||||||
}
|
}
|
||||||
display_panel_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1);
|
|
||||||
switch_fb_manually();
|
|
||||||
} else {
|
|
||||||
wait_for_fb_switch();
|
|
||||||
display_panel_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1);
|
|
||||||
switch_fb_in_background();
|
|
||||||
}
|
}
|
||||||
#else
|
}
|
||||||
display_panel_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1);
|
|
||||||
switch_fb_manually();
|
static void wait_for_te_signal(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)) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_refresh(void) {
|
||||||
|
display_driver_t *drv = &g_display_driver;
|
||||||
|
|
||||||
|
if (drv->queue.entry[drv->queue.wix] != FB_STATE_PREPARING) {
|
||||||
|
// No refresh needed as the frame buffer is not in
|
||||||
|
// the state to be copied to the display
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef BOARDLOADER
|
||||||
|
if (is_mode_handler()) {
|
||||||
|
// Disable scheduling of any new background copying
|
||||||
|
HAL_NVIC_DisableIRQ(DISPLAY_TE_INTERRUPT_NUM);
|
||||||
|
// Wait for next TE signal. During this time the
|
||||||
|
// display might be updated in the background
|
||||||
|
wait_for_te_signal();
|
||||||
|
// 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);
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
// Enable normal processing again
|
||||||
|
HAL_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // BOARDLOADER
|
||||||
|
wait_for_te_signal();
|
||||||
|
copy_fb_to_display(drv->queue.wix);
|
||||||
|
drv->queue.entry[drv->queue.wix] = FB_STATE_EMPTY;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void display_ensure_refreshed(void) {
|
void display_ensure_refreshed(void) {
|
||||||
#ifndef BOARDLOADER
|
#ifndef BOARDLOADER
|
||||||
|
display_driver_t *drv = &g_display_driver;
|
||||||
|
|
||||||
if (!is_mode_handler()) {
|
if (!is_mode_handler()) {
|
||||||
wait_for_fb_switch();
|
bool copy_pending;
|
||||||
// the update time is collected after starting the BG copy, then we need to
|
|
||||||
// wait: for the bg copy to finish and for at least one full refresh cycle
|
// Wait until all frame buffers are written to the display
|
||||||
// before we can consider the display fully redrawn
|
// so we can be sure there's not scheduled or pending
|
||||||
while (HAL_GetTick() - last_fb_update_time < 40) {
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__WFI();
|
||||||
|
} while (copy_pending);
|
||||||
|
|
||||||
|
// Wait until the display is fully refreshed
|
||||||
|
// (TE signal is low when the display is updating)
|
||||||
|
while (GPIO_PIN_RESET ==
|
||||||
|
HAL_GPIO_ReadPin(DISPLAY_TE_PORT, DISPLAY_TE_PIN)) {
|
||||||
__WFI();
|
__WFI();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,6 @@ void display_physical_fb_clear(void);
|
|||||||
|
|
||||||
void display_ensure_refreshed(void);
|
void display_ensure_refreshed(void);
|
||||||
|
|
||||||
void wait_for_fb_switch(void);
|
|
||||||
|
|
||||||
#endif // XFRAMEBUFFER
|
#endif // XFRAMEBUFFER
|
||||||
|
|
||||||
#endif // TREZORHAL_DISPLAY_FB_H
|
#endif // TREZORHAL_DISPLAY_FB_H
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
#ifndef TREZORHAL_DISPLAY_INTERNAL_H
|
||||||
|
#define TREZORHAL_DISPLAY_INTERNAL_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef XFRAMEBUFFER
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
#endif // XFRAMEBUFFER
|
||||||
|
|
||||||
|
// Display driver state
|
||||||
|
typedef struct {
|
||||||
|
#ifdef XFRAMEBUFFER
|
||||||
|
// Framebuffer queue
|
||||||
|
// (accessed & updated in the context of the main thread
|
||||||
|
// and the interrupt context)
|
||||||
|
volatile frame_buffer_queue_t queue;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Current display orientation (0, 90, 180, 270)
|
||||||
|
int orientation_angle;
|
||||||
|
|
||||||
|
} display_driver_t;
|
||||||
|
|
||||||
|
// Display driver instance
|
||||||
|
extern display_driver_t g_display_driver;
|
||||||
|
|
||||||
|
#endif // TREZORHAL_DISPLAY_INTERNAL_H
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include "display_io.h"
|
#include "display_io.h"
|
||||||
#include "irq.h"
|
#include "irq.h"
|
||||||
|
#include "supervise.h"
|
||||||
|
|
||||||
__IO DISP_MEM_TYPE *const DISPLAY_CMD_ADDRESS =
|
__IO DISP_MEM_TYPE *const DISPLAY_CMD_ADDRESS =
|
||||||
(__IO DISP_MEM_TYPE *const)((uint32_t)DISPLAY_MEMORY_BASE);
|
(__IO DISP_MEM_TYPE *const)((uint32_t)DISPLAY_MEMORY_BASE);
|
||||||
@ -35,7 +36,6 @@ void display_io_init_gpio(void) {
|
|||||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
__HAL_RCC_GPIOA_CLK_ENABLE();
|
||||||
__HAL_RCC_GPIOC_CLK_ENABLE();
|
__HAL_RCC_GPIOC_CLK_ENABLE();
|
||||||
__HAL_RCC_GPIOD_CLK_ENABLE();
|
__HAL_RCC_GPIOD_CLK_ENABLE();
|
||||||
__HAL_RCC_FMC_CLK_ENABLE();
|
|
||||||
|
|
||||||
GPIO_InitTypeDef GPIO_InitStructure;
|
GPIO_InitTypeDef GPIO_InitStructure;
|
||||||
|
|
||||||
@ -87,6 +87,8 @@ void display_io_init_gpio(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void display_io_init_fmc(void) {
|
void display_io_init_fmc(void) {
|
||||||
|
__HAL_RCC_FMC_CLK_ENABLE();
|
||||||
|
|
||||||
// Reference UM1725 "Description of STM32F4 HAL and LL drivers",
|
// Reference UM1725 "Description of STM32F4 HAL and LL drivers",
|
||||||
// section 64.2.1 "How to use this driver"
|
// section 64.2.1 "How to use this driver"
|
||||||
SRAM_HandleTypeDef external_display_data_sram = {0};
|
SRAM_HandleTypeDef external_display_data_sram = {0};
|
||||||
@ -141,5 +143,6 @@ void display_io_init_te_interrupt(void) {
|
|||||||
|
|
||||||
// setup interrupt for tearing effect pin
|
// setup interrupt for tearing effect pin
|
||||||
HAL_NVIC_SetPriority(DISPLAY_TE_INTERRUPT_NUM, IRQ_PRI_DMA, 0);
|
HAL_NVIC_SetPriority(DISPLAY_TE_INTERRUPT_NUM, IRQ_PRI_DMA, 0);
|
||||||
|
svc_enableIRQ(DISPLAY_TE_INTERRUPT_NUM);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -25,7 +25,23 @@
|
|||||||
#include "display_panel.h"
|
#include "display_panel.h"
|
||||||
|
|
||||||
void display_refresh(void) {
|
void display_refresh(void) {
|
||||||
// if the framebuffer is not used the implementation is empty
|
// If the framebuffer is not used the, we do not need
|
||||||
|
// to refresh the display explicitly as we write the data
|
||||||
|
// directly to the display internal RAM.
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_wait_for_sync(void) {
|
||||||
|
#ifdef DISPLAY_TE_PIN
|
||||||
|
uint32_t id = display_panel_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
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void set_window(const gfx_bitblt_t* bb) {
|
static inline void set_window(const gfx_bitblt_t* bb) {
|
||||||
|
@ -9,6 +9,7 @@ static volatile uint32_t dma_transfer_remaining = 0;
|
|||||||
static volatile uint32_t dma_data_transferred = 0;
|
static volatile uint32_t dma_data_transferred = 0;
|
||||||
static void *data_src = NULL;
|
static void *data_src = NULL;
|
||||||
static void *data_dst = NULL;
|
static void *data_dst = NULL;
|
||||||
|
static bg_copy_callback_t bg_copy_callback = NULL;
|
||||||
static DMA_HandleTypeDef DMA_Handle = {0};
|
static DMA_HandleTypeDef DMA_Handle = {0};
|
||||||
|
|
||||||
void HAL_DMA_XferCpltCallback(DMA_HandleTypeDef *hdma) {
|
void HAL_DMA_XferCpltCallback(DMA_HandleTypeDef *hdma) {
|
||||||
@ -46,6 +47,10 @@ void GPDMA1_Channel0_IRQHandler(void) {
|
|||||||
HAL_NVIC_DisableIRQ(GPDMA1_Channel0_IRQn);
|
HAL_NVIC_DisableIRQ(GPDMA1_Channel0_IRQn);
|
||||||
data_src = NULL;
|
data_src = NULL;
|
||||||
data_dst = NULL;
|
data_dst = NULL;
|
||||||
|
|
||||||
|
if (bg_copy_callback != NULL) {
|
||||||
|
bg_copy_callback();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,12 +62,14 @@ void bg_copy_wait(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void bg_copy_start_const_out_8(const uint8_t *src, uint8_t *dst, size_t size) {
|
void bg_copy_start_const_out_8(const uint8_t *src, uint8_t *dst, size_t size,
|
||||||
|
bg_copy_callback_t callback) {
|
||||||
uint32_t data_to_send = size > MAX_DATA_SIZE ? MAX_DATA_SIZE : size;
|
uint32_t data_to_send = size > MAX_DATA_SIZE ? MAX_DATA_SIZE : size;
|
||||||
dma_transfer_remaining = size;
|
dma_transfer_remaining = size;
|
||||||
dma_data_transferred = 0;
|
dma_data_transferred = 0;
|
||||||
data_src = (void *)src;
|
data_src = (void *)src;
|
||||||
data_dst = (void *)dst;
|
data_dst = (void *)dst;
|
||||||
|
bg_copy_callback = callback;
|
||||||
|
|
||||||
// setup DMA for data copy to constant output address
|
// setup DMA for data copy to constant output address
|
||||||
|
|
||||||
|
@ -40,4 +40,8 @@ int display_backlight(int level) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void display_sync(void) {}
|
void display_sync(void) {
|
||||||
|
#ifndef XFRAMEBUFFER
|
||||||
|
display_wait_for_sync();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user