mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-04 21:48:17 +00:00
refactor(core): extract framebuffer queue for reuse
[no changelog]
This commit is contained in:
parent
01cf58f2a1
commit
f9ff748a90
129
core/embed/io/display/fb_queue/fb_queue.c
Normal file
129
core/embed/io/display/fb_queue/fb_queue.c
Normal file
@ -0,0 +1,129 @@
|
||||
#ifdef KERNEL_MODE
|
||||
#include "fb_queue.h"
|
||||
|
||||
int16_t fb_queue_get_for_copy(volatile frame_buffer_queue_t *queue) {
|
||||
if (queue->entry[queue->wix] != FB_STATE_PREPARING) {
|
||||
// No refresh needed as the frame buffer is not in
|
||||
// the state to be copied to the display
|
||||
return -1;
|
||||
}
|
||||
|
||||
return queue->wix;
|
||||
}
|
||||
|
||||
int16_t fb_queue_get_for_write(volatile frame_buffer_queue_t *queue) {
|
||||
frame_buffer_state_t state;
|
||||
|
||||
// We have to wait if the buffer was passed for copying
|
||||
// to the interrupt handler
|
||||
do {
|
||||
state = queue->entry[queue->wix];
|
||||
} while (state == FB_STATE_READY || state == FB_STATE_COPYING);
|
||||
|
||||
queue->entry[queue->wix] = FB_STATE_PREPARING;
|
||||
|
||||
return queue->wix;
|
||||
}
|
||||
|
||||
int16_t fb_queue_get_for_transfer(volatile frame_buffer_queue_t *queue) {
|
||||
if (queue->rix >= FRAME_BUFFER_COUNT) {
|
||||
// This is an invalid state, and we should never get here
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (queue->entry[queue->rix]) {
|
||||
case FB_STATE_EMPTY:
|
||||
case FB_STATE_PREPARING:
|
||||
// No new frame queued
|
||||
|
||||
case FB_STATE_COPYING:
|
||||
// Currently we are copying a data to the display.
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case FB_STATE_READY:
|
||||
// Now it's proper time to copy the data to the display
|
||||
queue->entry[queue->rix] = FB_STATE_COPYING;
|
||||
return queue->rix;
|
||||
|
||||
// NOTE: when copying is done, this queue slot is marked empty
|
||||
break;
|
||||
|
||||
default:
|
||||
// This is an invalid state, and we should never get here
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool fb_queue_set_done(volatile frame_buffer_queue_t *queue) {
|
||||
if (queue->rix >= FRAME_BUFFER_COUNT) {
|
||||
// This is an invalid state, and we should never get here
|
||||
return false;
|
||||
}
|
||||
|
||||
if (queue->entry[queue->rix] == FB_STATE_COPYING) {
|
||||
queue->entry[queue->rix] = FB_STATE_EMPTY;
|
||||
queue->rix = (queue->rix + 1) % FRAME_BUFFER_COUNT;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fb_queue_set_switched(volatile frame_buffer_queue_t *queue) {
|
||||
if (queue->rix >= FRAME_BUFFER_COUNT) {
|
||||
// This is an invalid state, and we should never get here
|
||||
return false;
|
||||
}
|
||||
|
||||
if (queue->entry[queue->rix] == FB_STATE_COPYING) {
|
||||
if (queue->aix >= 0) {
|
||||
queue->entry[queue->aix] = FB_STATE_EMPTY;
|
||||
}
|
||||
queue->aix = queue->rix;
|
||||
queue->rix = (queue->rix + 1) % FRAME_BUFFER_COUNT;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fb_queue_set_ready_for_transfer(volatile frame_buffer_queue_t *queue) {
|
||||
if (queue->wix >= FRAME_BUFFER_COUNT) {
|
||||
// This is an invalid state, and we should never get here
|
||||
return false;
|
||||
}
|
||||
|
||||
if (queue->entry[queue->rix] == FB_STATE_PREPARING) {
|
||||
queue->entry[queue->rix] = FB_STATE_READY;
|
||||
queue->wix = (queue->wix + 1) % FRAME_BUFFER_COUNT;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void fb_queue_reset(volatile frame_buffer_queue_t *queue) {
|
||||
// Reset the buffer queue so we can eventually continue
|
||||
// safely in thread mode
|
||||
queue->wix = 0;
|
||||
queue->rix = 0;
|
||||
for (int i = 0; i < FRAME_BUFFER_COUNT; i++) {
|
||||
queue->entry[i] = FB_STATE_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
bool fb_queue_is_processed(volatile frame_buffer_queue_t *queue) {
|
||||
for (int i = 0; i < FRAME_BUFFER_COUNT; i++) {
|
||||
frame_buffer_state_t state = queue->entry[i];
|
||||
if (state == FB_STATE_READY ||
|
||||
(state == FB_STATE_COPYING && i != queue->aix)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
73
core/embed/io/display/fb_queue/fb_queue.h
Normal file
73
core/embed/io/display/fb_queue/fb_queue.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* This file is part of the Trezor project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trezor_types.h>
|
||||
|
||||
// Number of frame buffers used (1 or 2)
|
||||
// If 1 buffer is selected, some animations may not
|
||||
// be so smooth but the memory usage is lower.
|
||||
#define FRAME_BUFFER_COUNT 2
|
||||
|
||||
// Each frame buffer can be in one of the following states:
|
||||
typedef enum {
|
||||
// The frame buffer is empty and can be written to
|
||||
FB_STATE_EMPTY = 0,
|
||||
// The frame buffer pass passed to application
|
||||
FB_STATE_PREPARING = 1,
|
||||
// The frame buffer was written to and is ready
|
||||
// to be copied to the display
|
||||
FB_STATE_READY = 2,
|
||||
// The frame buffer is currently being copied to
|
||||
// the display
|
||||
FB_STATE_COPYING = 3,
|
||||
|
||||
} frame_buffer_state_t;
|
||||
|
||||
typedef struct {
|
||||
// Queue entries
|
||||
volatile frame_buffer_state_t entry[FRAME_BUFFER_COUNT];
|
||||
// Active index
|
||||
// (accessed & updated in the context of the interrupt handlers
|
||||
int16_t aix;
|
||||
// Read index
|
||||
// (accessed & updated in the context of the interrupt handlers
|
||||
uint8_t rix;
|
||||
// Write index
|
||||
// (accessed & updated in context of the main thread)
|
||||
uint8_t wix;
|
||||
|
||||
} frame_buffer_queue_t;
|
||||
|
||||
int16_t fb_queue_get_for_copy(volatile frame_buffer_queue_t *queue);
|
||||
|
||||
int16_t fb_queue_get_for_write(volatile frame_buffer_queue_t *queue);
|
||||
|
||||
int16_t fb_queue_get_for_transfer(volatile frame_buffer_queue_t *queue);
|
||||
|
||||
bool fb_queue_set_done(volatile frame_buffer_queue_t *queue);
|
||||
|
||||
bool fb_queue_set_switched(volatile frame_buffer_queue_t *queue);
|
||||
|
||||
bool fb_queue_set_ready_for_transfer(volatile frame_buffer_queue_t *queue);
|
||||
|
||||
void fb_queue_reset(volatile frame_buffer_queue_t *queue);
|
||||
|
||||
bool fb_queue_is_processed(volatile frame_buffer_queue_t *queue);
|
@ -120,13 +120,7 @@ void display_physical_fb_clear(void) {
|
||||
static void bg_copy_callback(void) {
|
||||
display_driver_t *drv = &g_display_driver;
|
||||
|
||||
if (drv->queue.rix >= FRAME_BUFFER_COUNT) {
|
||||
// This is an invalid state and we should never get here
|
||||
return;
|
||||
}
|
||||
|
||||
drv->queue.entry[drv->queue.rix] = FB_STATE_EMPTY;
|
||||
drv->queue.rix = (drv->queue.rix + 1) % FRAME_BUFFER_COUNT;
|
||||
fb_queue_set_done(&drv->queue);
|
||||
}
|
||||
|
||||
// Interrupt routing handling TE signal
|
||||
@ -135,37 +129,13 @@ static void display_te_interrupt_handler(void) {
|
||||
|
||||
__HAL_GPIO_EXTI_CLEAR_FLAG(DISPLAY_TE_PIN);
|
||||
|
||||
if (drv->queue.rix >= FRAME_BUFFER_COUNT) {
|
||||
// This is an invalid state and we should never get here
|
||||
return;
|
||||
}
|
||||
int16_t fb_idx = fb_queue_get_for_transfer(&drv->queue);
|
||||
|
||||
switch (drv->queue.entry[drv->queue.rix]) {
|
||||
case FB_STATE_EMPTY:
|
||||
case FB_STATE_PREPARING:
|
||||
// No new frame queued
|
||||
break;
|
||||
|
||||
case FB_STATE_COPYING:
|
||||
// Currently we are copying a data to the display.
|
||||
// We need to wait for the next TE interrupt.
|
||||
break;
|
||||
|
||||
case FB_STATE_READY:
|
||||
// Now it's proper time to copy the data to the display
|
||||
drv->queue.entry[drv->queue.rix] = FB_STATE_COPYING;
|
||||
display_panel_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1);
|
||||
bg_copy_start_const_out_8(get_fb_ptr(drv->queue.rix),
|
||||
(uint8_t *)DISPLAY_DATA_ADDRESS,
|
||||
PHYSICAL_FRAME_BUFFER_SIZE, bg_copy_callback);
|
||||
|
||||
// NOTE: when copying is done, this queue slot is marked empty
|
||||
// (see bg_copy_callback())
|
||||
break;
|
||||
|
||||
default:
|
||||
// This is an invalid state and we should never get here
|
||||
break;
|
||||
if (fb_idx >= 0) {
|
||||
display_panel_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1);
|
||||
bg_copy_start_const_out_8(get_fb_ptr(fb_idx),
|
||||
(uint8_t *)DISPLAY_DATA_ADDRESS,
|
||||
PHYSICAL_FRAME_BUFFER_SIZE, bg_copy_callback);
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,17 +157,9 @@ bool display_get_frame_buffer(display_fb_info_t *fb) {
|
||||
return false;
|
||||
}
|
||||
|
||||
frame_buffer_state_t state;
|
||||
uint8_t fb_idx = fb_queue_get_for_write(&drv->queue);
|
||||
|
||||
// We have to wait if the buffer was passed for copying
|
||||
// to the interrupt handler
|
||||
do {
|
||||
state = drv->queue.entry[drv->queue.wix];
|
||||
} while (state == FB_STATE_READY || state == FB_STATE_COPYING);
|
||||
|
||||
drv->queue.entry[drv->queue.wix] = FB_STATE_PREPARING;
|
||||
|
||||
fb->ptr = get_fb_ptr(drv->queue.wix);
|
||||
fb->ptr = get_fb_ptr(fb_idx);
|
||||
fb->stride = DISPLAY_RESX * sizeof(uint16_t);
|
||||
// Enable access to the frame buffer from the unprivileged code
|
||||
mpu_set_active_fb(fb->ptr, PHYSICAL_FRAME_BUFFER_SIZE);
|
||||
@ -236,7 +198,9 @@ void display_refresh(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (drv->queue.entry[drv->queue.wix] != FB_STATE_PREPARING) {
|
||||
int16_t fb_idx = fb_queue_get_for_copy(&drv->queue);
|
||||
|
||||
if (fb_idx < 0) {
|
||||
// No refresh needed as the frame buffer is not in
|
||||
// the state to be copied to the display
|
||||
return;
|
||||
@ -255,26 +219,26 @@ void display_refresh(void) {
|
||||
// Stop any background copying even if it is not finished yet
|
||||
bg_copy_abort();
|
||||
// Copy the frame buffer to the display manually
|
||||
copy_fb_to_display(drv->queue.wix);
|
||||
copy_fb_to_display(fb_idx);
|
||||
// Reset the buffer queue so we can eventually continue
|
||||
// safely in thread mode
|
||||
drv->queue.wix = 0;
|
||||
drv->queue.rix = 0;
|
||||
for (int i = 0; i < FRAME_BUFFER_COUNT; i++) {
|
||||
drv->queue.entry[i] = FB_STATE_EMPTY;
|
||||
}
|
||||
fb_queue_reset(&drv->queue);
|
||||
|
||||
// Enable normal processing again
|
||||
NVIC_EnableIRQ(DISPLAY_TE_INTERRUPT_NUM);
|
||||
} else {
|
||||
// Mark the buffer ready to switch to
|
||||
drv->queue.entry[drv->queue.wix] = FB_STATE_READY;
|
||||
drv->queue.wix = (drv->queue.wix + 1) % FRAME_BUFFER_COUNT;
|
||||
fb_queue_set_ready_for_transfer(&drv->queue);
|
||||
}
|
||||
|
||||
#else // BOARDLOADER
|
||||
wait_for_te_signal();
|
||||
copy_fb_to_display(drv->queue.wix);
|
||||
drv->queue.entry[drv->queue.wix] = FB_STATE_EMPTY;
|
||||
fb_queue_set_ready_for_transfer(&drv->queue);
|
||||
fb_idx = fb_queue_get_for_transfer(&drv->queue);
|
||||
if (fb_idx >= 0) {
|
||||
copy_fb_to_display(fb_idx);
|
||||
fb_queue_set_done(&drv->queue);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -293,14 +257,7 @@ void display_ensure_refreshed(void) {
|
||||
// so we can be sure there's not scheduled or pending
|
||||
// background copying
|
||||
do {
|
||||
copy_pending = false;
|
||||
for (int i = 0; i < FRAME_BUFFER_COUNT; i++) {
|
||||
frame_buffer_state_t state = drv->queue.entry[i];
|
||||
if (state == FB_STATE_READY || state == FB_STATE_COPYING) {
|
||||
copy_pending = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
copy_pending = !fb_queue_is_processed(&drv->queue);
|
||||
__WFI();
|
||||
} while (copy_pending);
|
||||
|
||||
|
@ -6,37 +6,7 @@
|
||||
|
||||
#ifdef FRAMEBUFFER
|
||||
|
||||
// Number of frame buffers used (1 or 2)
|
||||
// If 1 buffer is selected, some animations may not
|
||||
// be so smooth but the memory usage is lower.
|
||||
#define FRAME_BUFFER_COUNT 2
|
||||
|
||||
// Each frame buffer can be in one of the following states:
|
||||
typedef enum {
|
||||
// The frame buffer is empty and can be written to
|
||||
FB_STATE_EMPTY = 0,
|
||||
// The frame buffer pass passed to application
|
||||
FB_STATE_PREPARING = 1,
|
||||
// The frame buffer was written to and is ready
|
||||
// to be copied to the display
|
||||
FB_STATE_READY = 2,
|
||||
// The frame buffer is currently being copied to
|
||||
// the display
|
||||
FB_STATE_COPYING = 3,
|
||||
|
||||
} frame_buffer_state_t;
|
||||
|
||||
typedef struct {
|
||||
// Queue entries
|
||||
volatile frame_buffer_state_t entry[FRAME_BUFFER_COUNT];
|
||||
// Read index
|
||||
// (accessed & updated in the context of the interrupt handlers
|
||||
uint8_t rix;
|
||||
// Write index
|
||||
// (accessed & updated in context of the main thread)
|
||||
uint8_t wix;
|
||||
|
||||
} frame_buffer_queue_t;
|
||||
#include "../fb_queue/fb_queue.h"
|
||||
|
||||
#endif // FRAMEBUFFER
|
||||
|
||||
|
@ -47,6 +47,7 @@ def configure(
|
||||
sources += ["embed/io/display/st-7789/display_io.c"]
|
||||
sources += ["embed/io/display/st-7789/display_panel.c"]
|
||||
sources += ["embed/io/display/st-7789/panels/lx154a2482.c"]
|
||||
sources += ["embed/io/display/fb_queue/fb_queue.c"]
|
||||
paths += ["embed/io/display/inc"]
|
||||
|
||||
sources += ["embed/io/display/backlight/stm32/backlight_pwm.c"]
|
||||
|
Loading…
Reference in New Issue
Block a user