feat(core): add basic support for efficient frame buffer graphics

[no changelog]
pull/3339/head
tychovrahe 9 months ago committed by TychoVrahe
parent 2c894a66ef
commit df42c99bc9

@ -123,7 +123,7 @@ void ui_screen_boot(const vendor_header *const vhdr,
#endif
PIXELDATA_DIRTY();
display_pixeldata_dirty();
display_refresh();
}
@ -134,7 +134,7 @@ void ui_screen_boot_wait(int wait_seconds) {
boot_background);
display_text_center(DISPLAY_RESX / 2, DISPLAY_RESY - 5, wait_str, -1,
FONT_NORMAL, COLOR_BL_BG, boot_background);
PIXELDATA_DIRTY();
display_pixeldata_dirty();
display_refresh();
}
@ -182,7 +182,7 @@ void ui_screen_boot_click(void) {
display_bar(0, BOOT_WAIT_Y_TOP, DISPLAY_RESX, BOOT_WAIT_HEIGHT,
boot_background);
bld_continue_label(boot_background);
PIXELDATA_DIRTY();
display_pixeldata_dirty();
display_refresh();
ui_click();
}

@ -24,6 +24,7 @@ uint32_t rgb565_to_rgb888(uint16_t color) {
res |= ((((((uint32_t)color & 0xF800) >> 11) * 527) + 23) >> 6) << 16;
res |= ((((((uint32_t)color & 0x07E0) >> 5) * 259) + 33) >> 6) << 8;
res |= ((((((uint32_t)color & 0x001F) >> 0) * 527) + 23) >> 6) << 0;
res |= 0xFF000000;
return res;
}
@ -37,5 +38,5 @@ uint32_t interpolate_rgb888_color(uint32_t color0, uint32_t color1,
((color1 & 0xFF00) >> 8) * (15 - step)) /
15;
cb = ((color0 & 0x00FF) * step + (color1 & 0x00FF) * (15 - step)) / 15;
return (cr << 16) | (cg << 8) | cb;
return (cr << 16) | (cg << 8) | cb | 0xFF000000;
}

@ -51,6 +51,9 @@ static inline void clamp_coords(int x, int y, int w, int h, int *x0, int *y0,
}
void display_clear(void) {
#ifdef DISPLAY_EFFICIENT_CLEAR
display_efficient_clear();
#else
const int saved_orientation = display_get_orientation();
display_reset_state();
@ -68,7 +71,8 @@ void display_clear(void) {
// if valid, go back to the saved orientation
display_orientation(saved_orientation);
// flag display for refresh
PIXELDATA_DIRTY();
#endif
display_pixeldata_dirty();
}
void display_bar(int x, int y, int w, int h, uint16_t c) {
@ -80,104 +84,7 @@ void display_bar(int x, int y, int w, int h, uint16_t c) {
for (int i = 0; i < (x1 - x0 + 1) * (y1 - y0 + 1); i++) {
PIXELDATA(c);
}
PIXELDATA_DIRTY();
}
#define CORNER_RADIUS 16
static const uint8_t cornertable[CORNER_RADIUS * CORNER_RADIUS] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, 9, 12, 14, 15, 0, 0, 0,
0, 0, 0, 0, 0, 3, 9, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0,
0, 8, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 3, 12, 15, 15,
15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 3, 14, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 0, 0, 0, 3, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 0, 0, 0, 12, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0,
8, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 3, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 9, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 1, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 5, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 9, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 12,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 14, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15,
};
void display_bar_radius(int x, int y, int w, int h, uint16_t c, uint16_t b,
uint8_t r) {
if (r != 2 && r != 4 && r != 8 && r != 16) {
return;
} else {
r = 16 / r;
}
uint16_t colortable[16] = {0};
set_color_table(colortable, c, b);
x += DISPLAY_OFFSET.x;
y += DISPLAY_OFFSET.y;
int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
clamp_coords(x, y, w, h, &x0, &y0, &x1, &y1);
display_set_window(x0, y0, x1, y1);
for (int j = y0; j <= y1; j++) {
for (int i = x0; i <= x1; i++) {
int rx = i - x;
int ry = j - y;
if (rx < CORNER_RADIUS / r && ry < CORNER_RADIUS / r) {
uint8_t c = cornertable[rx * r + ry * r * CORNER_RADIUS];
PIXELDATA(colortable[c]);
} else if (rx < CORNER_RADIUS / r && ry >= h - CORNER_RADIUS / r) {
uint8_t c = cornertable[rx * r + (h - 1 - ry) * r * CORNER_RADIUS];
PIXELDATA(colortable[c]);
} else if (rx >= w - CORNER_RADIUS / r && ry < CORNER_RADIUS / r) {
uint8_t c = cornertable[(w - 1 - rx) * r + ry * r * CORNER_RADIUS];
PIXELDATA(colortable[c]);
} else if (rx >= w - CORNER_RADIUS / r && ry >= h - CORNER_RADIUS / r) {
uint8_t c =
cornertable[(w - 1 - rx) * r + (h - 1 - ry) * r * CORNER_RADIUS];
PIXELDATA(colortable[c]);
} else {
PIXELDATA(c);
}
}
}
PIXELDATA_DIRTY();
}
void display_bar_radius_buffer(int x, int y, int w, int h, uint8_t r,
buffer_text_t *buffer) {
if (h > TEXT_BUFFER_HEIGHT) {
return;
}
if (r != 2 && r != 4 && r != 8 && r != 16) {
return;
} else {
r = 16 / r;
}
int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
clamp_coords(x, y, w, h, &x0, &y0, &x1, &y1);
for (int j = y0; j <= y1; j++) {
for (int i = x0; i <= x1; i++) {
int rx = i - x;
int ry = j - y;
int p = j * DISPLAY_RESX + i;
uint8_t c = 0;
if (rx < CORNER_RADIUS / r && ry < CORNER_RADIUS / r) {
c = cornertable[rx * r + ry * r * CORNER_RADIUS];
} else if (rx < CORNER_RADIUS / r && ry >= h - CORNER_RADIUS / r) {
c = cornertable[rx * r + (h - 1 - ry) * r * CORNER_RADIUS];
} else if (rx >= w - CORNER_RADIUS / r && ry < CORNER_RADIUS / r) {
c = cornertable[(w - 1 - rx) * r + ry * r * CORNER_RADIUS];
} else if (rx >= w - CORNER_RADIUS / r && ry >= h - CORNER_RADIUS / r) {
c = cornertable[(w - 1 - rx) * r + (h - 1 - ry) * r * CORNER_RADIUS];
} else {
c = 15;
}
int b = p / 2;
if (p % 2) {
buffer->buffer[b] |= c << 4;
} else {
buffer->buffer[b] |= (c);
}
}
}
display_pixeldata_dirty();
}
void display_text_render_buffer(const char *text, int textlen, int font,
@ -357,7 +264,7 @@ void display_print(const char *text, int textlen) {
PIXELDATA(display_print_bgcolor);
}
}
PIXELDATA_DIRTY();
display_pixeldata_dirty();
display_refresh();
}
@ -384,6 +291,67 @@ void display_printf(const char *fmt, ...) {
#endif // TREZOR_PRINT_DISABLE
#ifdef FRAMEBUFFER
static void display_text_render(int x, int y, const char *text, int textlen,
int font, uint16_t fgcolor, uint16_t bgcolor) {
// determine text length if not provided
if (textlen < 0) {
textlen = strlen(text);
}
int total_adv = 0;
uint32_t *fb = display_get_fb_addr();
uint16_t colortable[16] = {0};
set_color_table(colortable, fgcolor, bgcolor);
// render glyphs
for (int c_idx = 0; c_idx < textlen; c_idx++) {
const uint8_t *g = font_get_glyph(font, (uint8_t)text[c_idx]);
if (!g) continue;
const uint8_t w = g[0]; // width
const uint8_t h = g[1]; // height
const uint8_t adv = g[2]; // advance
const uint8_t bearX = g[3]; // bearingX
const uint8_t bearY = g[4]; // bearingY
if (w && h) {
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) {
const int a = i + j * w;
#if TREZOR_FONT_BPP == 1
const uint8_t c = ((g[5 + a / 8] >> (7 - (a % 8) * 1)) & 0x01) * 15;
#elif TREZOR_FONT_BPP == 2
const uint8_t c = ((g[5 + a / 4] >> (6 - (a % 4) * 2)) & 0x03) * 5;
#elif TREZOR_FONT_BPP == 4
const uint8_t c = (g[5 + a / 2] >> (4 - (a % 2) * 4)) & 0x0F;
#elif TREZOR_FONT_BPP == 8
#error Rendering into buffer not supported when using TREZOR_FONT_BPP = 8
// const uint8_t c = g[5 + a / 1] >> 4;
#else
#error Unsupported TREZOR_FONT_BPP value
#endif
int x_pos = x + i + total_adv + bearX;
int y_pos = y + j - bearY;
if (y_pos < 0) continue;
if (x_pos >= DISPLAY_FRAMEBUFFER_WIDTH || x_pos < 0 ||
y_pos >= DISPLAY_FRAMEBUFFER_HEIGHT || y_pos < 0) {
continue;
}
display_pixel((uint8_t *)fb, x_pos, y_pos, colortable[c]);
}
}
}
total_adv += adv;
}
display_pixeldata_dirty();
}
#else
static void display_text_render(int x, int y, const char *text, int textlen,
int font, uint16_t fgcolor, uint16_t bgcolor) {
// determine text length if not provided
@ -431,8 +399,9 @@ static void display_text_render(int x, int y, const char *text, int textlen,
}
x += adv;
}
PIXELDATA_DIRTY();
display_pixeldata_dirty();
}
#endif
void display_text(int x, int y, const char *text, int textlen, int font,
uint16_t fgcolor, uint16_t bgcolor) {
@ -552,7 +521,7 @@ void display_qrcode(int x, int y, const char *data, uint8_t scale) {
}
}
}
PIXELDATA_DIRTY();
display_pixeldata_dirty();
}
#endif
@ -610,5 +579,3 @@ void display_utf8_substr(const char *buf_start, size_t buf_len, int char_off,
*out_start = buf_start + i_start;
*out_len = i - i_start;
}
void display_pixeldata_dirty(void) { PIXELDATA_DIRTY(); }

@ -37,24 +37,11 @@ typedef enum {
TOIF_GRAYSCALE_EH = 3, // even hi
} toif_format_t;
// provided by port
void display_init(void);
void display_init_seq(void);
void display_refresh(void);
const char *display_save(const char *prefix);
void display_clear_save(void);
// provided by common
void display_clear(void);
void display_bar(int x, int y, int w, int h, uint16_t c);
void display_bar_radius(int x, int y, int w, int h, uint16_t c, uint16_t b,
uint8_t r);
void display_bar_radius_buffer(int x, int y, int w, int h, uint8_t r,
buffer_text_t *buffer);
bool display_toif_info(const uint8_t *buf, uint32_t len, uint16_t *out_w,
uint16_t *out_h, toif_format_t *out_format);
@ -86,7 +73,4 @@ void display_fade(int start, int end, int delay);
void display_utf8_substr(const char *buf_start, size_t buf_len, int char_off,
int char_len, const char **out_start, int *out_len);
// pixeldata accessor
void display_pixeldata_dirty();
#endif

@ -24,15 +24,28 @@
#include "common.h"
#include TREZOR_BOARD
#ifndef DISPLAY_FRAMEBUFFER_OFFSET_Y
#define DISPLAY_FRAMEBUFFER_OFFSET_Y 0
#endif
#ifndef DISPLAY_FRAMEBUFFER_OFFSET_X
#define DISPLAY_FRAMEBUFFER_OFFSET_X 0
#endif
#ifndef DISPLAY_FRAMEBUFFER_WIDTH
#define DISPLAY_FRAMEBUFFER_WIDTH 0
#endif
#ifndef DISPLAY_FRAMEBUFFER_HEIGHT
#define DISPLAY_FRAMEBUFFER_HEIGHT 0
#endif
#ifndef PIXELDATA
#define PIXELDATA(c) display_pixeldata(c)
#endif
void display_pixeldata(uint16_t c);
#ifndef PIXELDATA_DIRTY
#define PIXELDATA_DIRTY()
#endif
void display_pixeldata_dirty(void);
void display_reset_state();
@ -48,4 +61,10 @@ void display_refresh(void);
const char *display_save(const char *prefix);
void display_clear_save(void);
void display_efficient_clear(void);
uint32_t *display_get_fb_addr(void);
uint8_t *display_get_wr_addr(void);
void display_shift_window(uint16_t pixels);
uint16_t display_get_window_offset(void);
#endif //_DISPLAY_INTERFACE_H

@ -14,6 +14,8 @@ micropython = []
protobuf = ["micropython"]
ui = []
dma2d = []
framebuffer = []
framebuffer32bit = []
ui_debug = []
ui_bounds = []
bootloader = []

@ -296,14 +296,17 @@ fn generate_trezorhal_bindings() {
.allowlist_function("display_text")
.allowlist_function("display_text_render_buffer")
.allowlist_function("display_text_width")
.allowlist_function("display_bar")
.allowlist_function("display_bar_radius")
.allowlist_function("display_bar_radius_buffer")
.allowlist_function("display_pixeldata")
.allowlist_function("display_pixeldata_dirty")
.allowlist_function("display_set_window")
.allowlist_function("display_sync")
.allowlist_function("display_get_fb_addr")
.allowlist_function("display_get_wr_addr")
.allowlist_var("DISPLAY_DATA_ADDRESS")
.allowlist_var("DISPLAY_FRAMEBUFFER_WIDTH")
.allowlist_var("DISPLAY_FRAMEBUFFER_HEIGHT")
.allowlist_var("DISPLAY_FRAMEBUFFER_OFFSET_X")
.allowlist_var("DISPLAY_FRAMEBUFFER_OFFSET_Y")
.allowlist_var("DISPLAY_RESX")
.allowlist_var("DISPLAY_RESY")
.allowlist_type("toif_format_t")
@ -332,12 +335,15 @@ fn generate_trezorhal_bindings() {
.allowlist_function("hal_delay")
.allowlist_function("hal_ticks_ms")
// dma2d
.allowlist_function("dma2d_setup_const")
.allowlist_function("dma2d_setup_4bpp")
.allowlist_function("dma2d_setup_16bpp")
.allowlist_function("dma2d_setup_4bpp_over_4bpp")
.allowlist_function("dma2d_setup_4bpp_over_16bpp")
.allowlist_function("dma2d_start")
.allowlist_function("dma2d_start_blend")
.allowlist_function("dma2d_start_const")
.allowlist_function("dma2d_start_const_multiline")
.allowlist_function("dma2d_wait_for_transfer")
//buffers
.allowlist_function("buffers_get_line_16bpp")
@ -352,6 +358,7 @@ fn generate_trezorhal_bindings() {
.allowlist_function("buffers_free_jpeg_work")
.allowlist_function("buffers_get_blurring")
.allowlist_function("buffers_free_blurring")
.allowlist_var("TEXT_BUFFER_HEIGHT")
.no_copy("buffer_line_16bpp_t")
.no_copy("buffer_line_4bpp_t")
.no_copy("buffer_text_t")

@ -5,6 +5,8 @@ use core::{
use super::ffi;
pub use ffi::TEXT_BUFFER_HEIGHT;
macro_rules! buffer_wrapper {
($rust_name: ident, $type: ident, $get: ident, $free: ident) => {
pub struct $rust_name(ptr::NonNull<ffi::$type>);

@ -6,6 +6,20 @@ use crate::trezorhal::buffers::BufferText;
pub use ffi::{DISPLAY_RESX, DISPLAY_RESY};
#[cfg(feature = "framebuffer")]
pub use ffi::{
DISPLAY_FRAMEBUFFER_HEIGHT, DISPLAY_FRAMEBUFFER_OFFSET_X, DISPLAY_FRAMEBUFFER_OFFSET_Y,
DISPLAY_FRAMEBUFFER_WIDTH,
};
#[cfg(all(feature = "framebuffer", not(feature = "framebuffer32bit")))]
#[derive(Copy, Clone)]
pub struct FrameBuffer(*mut u16);
#[cfg(all(feature = "framebuffer", feature = "framebuffer32bit"))]
#[derive(Copy, Clone)]
pub struct FrameBuffer(*mut u32);
#[derive(PartialEq, Debug, Eq, FromPrimitive, Clone, Copy)]
pub enum ToifFormat {
FullColorBE = ffi::toif_format_t_TOIF_FULL_COLOR_BE as _,
@ -74,51 +88,49 @@ pub fn text_baseline(font: i32) -> i16 {
unsafe { ffi::font_baseline(font).try_into().unwrap_or(i16::MAX) }
}
pub fn bar(x: i16, y: i16, w: i16, h: i16, fgcolor: u16) {
unsafe { ffi::display_bar(x.into(), y.into(), w.into(), h.into(), fgcolor) }
}
pub fn bar_radius(x: i16, y: i16, w: i16, h: i16, fgcolor: u16, bgcolor: u16, radius: u8) {
#[inline(always)]
#[cfg(all(feature = "disp_i8080_16bit_dw", not(feature = "disp_i8080_8bit_dw")))]
pub fn pixeldata(c: u16) {
unsafe {
ffi::display_bar_radius(
x.into(),
y.into(),
w.into(),
h.into(),
fgcolor,
bgcolor,
radius,
)
ffi::DISPLAY_DATA_ADDRESS.write_volatile(c);
}
}
pub fn bar_radius_buffer(x: i16, y: i16, w: i16, h: i16, radius: u8, buffer: &mut BufferText) {
#[cfg(feature = "framebuffer")]
pub fn get_fb_addr() -> FrameBuffer {
unsafe { FrameBuffer(ffi::display_get_fb_addr() as _) }
}
#[inline(always)]
#[cfg(feature = "disp_i8080_8bit_dw")]
pub fn pixeldata(c: u16) {
unsafe {
ffi::display_bar_radius_buffer(
x.into(),
y.into(),
w.into(),
h.into(),
radius,
buffer.deref_mut(),
)
ffi::DISPLAY_DATA_ADDRESS.write_volatile((c & 0xff) as u8);
ffi::DISPLAY_DATA_ADDRESS.write_volatile((c >> 8) as u8);
}
}
#[inline(always)]
#[cfg(all(feature = "disp_i8080_16bit_dw", not(feature = "disp_i8080_8bit_dw")))]
pub fn pixeldata(c: u16) {
#[cfg(all(feature = "framebuffer", not(feature = "framebuffer32bit")))]
pub fn pixel(fb: FrameBuffer, x: i16, y: i16, c: u16) {
unsafe {
ffi::DISPLAY_DATA_ADDRESS.write_volatile(c);
let addr = fb.0.offset(
((y as u32 + DISPLAY_FRAMEBUFFER_OFFSET_Y) * DISPLAY_FRAMEBUFFER_WIDTH
+ (x as u32 + DISPLAY_FRAMEBUFFER_OFFSET_X)) as isize,
);
addr.write_volatile(c);
}
}
#[inline(always)]
#[cfg(feature = "disp_i8080_8bit_dw")]
pub fn pixeldata(c: u16) {
#[cfg(all(feature = "framebuffer", feature = "framebuffer32bit"))]
pub fn pixel(fb: FrameBuffer, x: i16, y: i16, c: u32) {
unsafe {
ffi::DISPLAY_DATA_ADDRESS.write_volatile((c & 0xff) as u8);
ffi::DISPLAY_DATA_ADDRESS.write_volatile((c >> 8) as u8);
let addr = fb.0.offset(
((y as u32 + DISPLAY_FRAMEBUFFER_OFFSET_Y) * DISPLAY_FRAMEBUFFER_WIDTH
+ (x as u32 + DISPLAY_FRAMEBUFFER_OFFSET_X)) as isize,
);
addr.write_volatile(c);
}
}

@ -1,6 +1,10 @@
use super::ffi;
#[allow(dead_code)]
pub fn dma2d_setup_const() {
unsafe { ffi::dma2d_setup_const() }
}
pub fn dma2d_setup_4bpp(fg_color: u16, bg_color: u16) {
unsafe { ffi::dma2d_setup_4bpp(fg_color, bg_color) }
}
@ -30,7 +34,7 @@ pub unsafe fn dma2d_start(buffer: &[u8], pixels: i16) {
unsafe {
ffi::dma2d_start(
buffer.as_ptr() as _,
ffi::DISPLAY_DATA_ADDRESS as _,
ffi::display_get_wr_addr() as _,
pixels as _,
);
}
@ -50,12 +54,48 @@ pub unsafe fn dma2d_start_blend(overlay_buffer: &[u8], bg_buffer: &[u8], pixels:
ffi::dma2d_start_blend(
overlay_buffer.as_ptr() as _,
bg_buffer.as_ptr() as _,
ffi::DISPLAY_DATA_ADDRESS as _,
ffi::display_get_wr_addr() as _,
pixels as _,
);
}
}
/// Starts blending
///
/// # Safety
///
/// This function is unsafe because the caller has to guarantee that he:
/// 1) doesn't mutate the buffers until the transfer is finished, which is
/// guaranteed by calling `dma2d_wait_for_transfer`
/// 2) the buffer doesn't get dropped until the transfer is finished
/// 3) doesn't call this function while another transfer is running
pub unsafe fn dma2d_start_const(color: u16, pixels: i16) {
unsafe {
ffi::dma2d_start_const(color, ffi::display_get_wr_addr() as _, pixels as _);
}
}
#[cfg(feature = "framebuffer")]
/// Starts blending
///
/// # Safety
///
/// This function is unsafe because the caller has to guarantee that he:
/// 1) doesn't mutate the buffers until the transfer is finished, which is
/// guaranteed by calling `dma2d_wait_for_transfer`
/// 2) the buffer doesn't get dropped until the transfer is finished
/// 3) doesn't call this function while another transfer is running
pub unsafe fn dma2d_start_const_multiline(color: u16, width: i16, height: i16) {
unsafe {
ffi::dma2d_start_const_multiline(
color,
ffi::display_get_wr_addr() as _,
width as _,
height as _,
);
}
}
pub fn dma2d_wait_for_transfer() {
unsafe {
ffi::dma2d_wait_for_transfer();

@ -7,6 +7,13 @@ impl Color {
pub const fn from_u16(val: u16) -> Self {
Self(val)
}
pub const fn from_u32(val: u32) -> Self {
Self::rgb(
((val >> 16) & 0xFF) as u8,
((val >> 8) & 0xFF) as u8,
(val & 0xFF) as u8,
)
}
pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
let r = (r as u16 & 0xF8) << 8;
@ -56,6 +63,10 @@ impl Color {
self.0
}
pub fn to_u32(self) -> u32 {
((self.r() as u32) << 16) | ((self.g() as u32) << 8) | (self.b() as u32) | 0xff000000
}
pub fn hi_byte(self) -> u8 {
(self.to_u16() >> 8) as u8
}
@ -97,3 +108,15 @@ impl From<Color> for u16 {
val.to_u16()
}
}
impl From<u32> for Color {
fn from(val: u32) -> Self {
Self::from_u32(val)
}
}
impl From<Color> for u32 {
fn from(val: Color) -> Self {
val.to_u32()
}
}

@ -32,6 +32,7 @@ use crate::{
};
// Reexports
use crate::trezorhal::buffers::BufferText;
pub use crate::ui::display::toif::Icon;
pub use color::Color;
pub use font::{Font, Glyph, GlyphMetrics};
@ -39,6 +40,13 @@ pub use loader::{
loader, loader_indeterminate, loader_small_indeterminate, LOADER_MAX, LOADER_MIN,
};
#[cfg(all(feature = "dma2d", feature = "framebuffer"))]
use crate::trezorhal::{
display::{get_fb_addr, pixel},
dma2d::{dma2d_setup_const, dma2d_start_const_multiline},
};
use crate::ui::constant::WIDTH;
pub fn backlight() -> u16 {
display::backlight(-1) as u16
}
@ -78,34 +86,223 @@ pub fn fade_backlight(_: u16) {}
#[cfg(not(feature = "backlight"))]
pub fn fade_backlight_duration(_: u16, _: u32) {}
#[cfg(not(feature = "framebuffer"))]
/// Fill a whole rectangle with a specific color.
pub fn rect_fill(r: Rect, fg_color: Color) {
display::bar(r.x0, r.y0, r.width(), r.height(), fg_color.into());
let r = r.translate(get_offset());
let r = r.clamp(constant::screen());
set_window(r);
for _ in r.y0..r.y1 {
for _ in r.x0..r.x1 {
pixeldata(fg_color.into());
}
}
pixeldata_dirty();
}
#[cfg(feature = "framebuffer")]
pub fn rect_fill(r: Rect, fg_color: Color) {
let r = r.translate(get_offset());
let r = r.clamp(constant::screen());
set_window(r);
dma2d_setup_const();
unsafe {
dma2d_start_const_multiline(fg_color.into(), r.width(), r.height());
}
dma2d_wait_for_transfer();
pixeldata_dirty();
}
pub fn rect_stroke(r: Rect, fg_color: Color) {
display::bar(r.x0, r.y0, r.width(), 1, fg_color.into());
display::bar(r.x0, r.y0 + r.height() - 1, r.width(), 1, fg_color.into());
display::bar(r.x0, r.y0, 1, r.height(), fg_color.into());
display::bar(r.x0 + r.width() - 1, r.y0, 1, r.height(), fg_color.into());
rect_fill(
Rect::from_top_left_and_size(Point::new(r.x0, r.y0), Offset::new(r.width(), 1)),
fg_color,
);
rect_fill(
Rect::from_top_left_and_size(
Point::new(r.x0, r.y0 + r.height() - 1),
Offset::new(r.width(), 1),
),
fg_color,
);
rect_fill(
Rect::from_top_left_and_size(Point::new(r.x0, r.y0), Offset::new(1, r.height())),
fg_color,
);
rect_fill(
Rect::from_top_left_and_size(
Point::new(r.x0 + r.width() - 1, r.y0),
Offset::new(1, r.height()),
),
fg_color,
);
}
const CORNER_RADIUS: usize = 16;
#[rustfmt::skip]
const CORNER_TABLE: [usize; CORNER_RADIUS * CORNER_RADIUS] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, 9, 12, 14, 15,
0, 0, 0, 0, 0, 0, 0, 0, 3, 9, 15, 15, 15, 15, 15, 15,
0, 0, 0, 0, 0, 0, 0, 8, 15, 15, 15, 15, 15, 15, 15, 15,
0, 0, 0, 0, 0, 3, 12, 15, 15, 15, 15, 15, 15, 15, 15, 15,
0, 0, 0, 0, 3, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
0, 0, 0, 3, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
0, 0, 0, 12, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
0, 0, 8, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
0, 3, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
0, 9, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
1, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
5, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
9, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
12, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
];
/// Draw a rectangle with rounded corners.
#[cfg(not(feature = "framebuffer"))]
pub fn rect_fill_rounded(r: Rect, fg_color: Color, bg_color: Color, radius: u8) {
if radius == 1 {
rect_fill_rounded1(r, fg_color, bg_color);
} else {
assert!([2, 4, 8, 16].iter().any(|allowed| radius == *allowed));
display::bar_radius(
r.x0,
r.y0,
r.width(),
r.height(),
fg_color.into(),
bg_color.into(),
radius,
);
let color_table = get_color_table(fg_color, bg_color);
let area = r.translate(get_offset());
let clamped = area.clamp(constant::screen());
set_window(clamped);
let radius = radius as i16;
let radius_inv = 16 / radius;
for y in area.y0..area.y1 {
for x in area.x0..area.x1 {
if x - r.x0 < radius && y - r.y0 < radius {
let c = CORNER_TABLE[((x - area.x0) * radius_inv
+ (y - area.y0) * radius_inv * CORNER_RADIUS as i16)
as usize];
pixeldata(color_table[c]);
} else if x - r.x0 < radius && y - r.y0 >= r.height() - radius {
let c = CORNER_TABLE[((x - area.x0) * radius_inv
+ (r.height() - 1 - (y - area.y0)) * radius_inv * CORNER_RADIUS as i16)
as usize];
pixeldata(color_table[c]);
} else if x - r.x0 >= r.width() - radius && y - r.y0 < radius {
let c = CORNER_TABLE[((r.width() - 1 - (x - area.x0)) * radius_inv
+ (y - area.y0) * radius_inv * CORNER_RADIUS as i16)
as usize];
pixeldata(color_table[c]);
} else if x - r.x0 >= r.width() - radius && y - r.y0 >= r.height() - radius {
let c = CORNER_TABLE[((r.width() - 1 - (x - area.x0)) * radius_inv
+ (r.height() - 1 - (y - area.y0)) * radius_inv * CORNER_RADIUS as i16)
as usize];
pixeldata(color_table[c]);
} else {
pixeldata(color_table[15]);
}
}
}
}
pixeldata_dirty();
}
pub fn rect_fill_rounded_buffer(r: Rect, radius: u8, buffer: &mut BufferText) {
if r.height() > r.y0 + buffers::TEXT_BUFFER_HEIGHT as i16 || r.x0 + r.width() > WIDTH {
return;
}
assert!([2, 4, 8, 16].iter().any(|allowed| radius == *allowed));
let radius = radius as i16;
let radius_inv = 16 / radius;
for y in r.y0..r.y1 {
for x in r.x0..r.x1 {
let c = if x - r.x0 < radius && y - r.y0 < radius {
CORNER_TABLE[((x - r.x0) * radius_inv
+ (y - r.y0) * radius_inv * CORNER_RADIUS as i16)
as usize]
} else if x - r.x0 < radius && y - r.y0 >= r.height() - radius {
CORNER_TABLE[((x - r.x0) * radius_inv
+ (r.height() - 1 - (y - r.y0)) * radius_inv * CORNER_RADIUS as i16)
as usize]
} else if x - r.x0 >= r.width() - radius && y - r.y0 < radius {
CORNER_TABLE[((r.width() - 1 - (x - r.x0)) * radius_inv
+ (y - r.y0) * radius_inv * CORNER_RADIUS as i16)
as usize]
} else if x - r.x0 >= r.width() - radius && y - r.y0 >= r.height() - radius {
CORNER_TABLE[((r.width() - 1 - (x - r.x0)) * radius_inv
+ (r.height() - 1 - (y - r.y0)) * radius_inv * CORNER_RADIUS as i16)
as usize]
} else {
15usize
};
let p = y * WIDTH + x;
let b = (p / 2) as usize;
if p % 2 != 0 {
buffer.buffer[b] |= (c << 4) as u8;
} else {
buffer.buffer[b] |= c as u8;
}
}
}
pixeldata_dirty();
}
#[cfg(feature = "framebuffer")]
/// Draw a rectangle with rounded corners.
pub fn rect_fill_rounded(area: Rect, fg_color: Color, bg_color: Color, radius: u8) {
let radius = radius as i16;
if radius == 1 {
rect_fill_rounded1(area, fg_color, bg_color);
} else {
assert!([2, 4, 8, 16].iter().any(|allowed| radius == *allowed));
let r = area.translate(get_offset());
let r = r.clamp(constant::screen());
let fb = get_fb_addr();
rect_fill(r, fg_color);
let r_inv = 16 / radius;
let color_table = get_color_table(fg_color, bg_color);
for y in 0..radius {
for x in 0..radius {
let c = CORNER_TABLE[(x * r_inv + y * r_inv * 16) as usize];
pixel(fb, r.x0 + x, r.y0 + y, color_table[c].into());
}
}
for y in 0..radius {
for x in 0..radius {
let c = CORNER_TABLE[((radius - x - 1) * r_inv + y * r_inv * 16) as usize];
pixel(fb, r.x1 - radius + x, r.y0 + y, color_table[c].into());
}
}
for y in 0..radius {
for x in 0..radius {
let c = CORNER_TABLE[(x * r_inv + (radius - y - 1) * r_inv * 16) as usize];
pixel(fb, r.x0 + x, r.y1 - radius + y, color_table[c].into());
}
}
for y in 0..radius {
for x in 0..radius {
let c = CORNER_TABLE
[((radius - x - 1) * r_inv + (radius - y - 1) * r_inv * 16) as usize];
pixel(
fb,
r.x1 - radius + x,
r.y1 - radius + y,
color_table[c].into(),
);
}
}
}
pixeldata_dirty();
}
/// Filling a rectangle with a rounding of 1 pixel - removing the corners.
@ -137,7 +334,10 @@ pub fn rect_outline_rounded(r: Rect, fg_color: Color, bg_color: Color, radius: u
pub fn rect_fill_corners(r: Rect, fg_color: Color) {
for p in r.corner_points().iter() {
// This draws a 1x1 rectangle at the given point.
display::bar(p.x, p.y, 1, 1, fg_color.into());
rect_fill(
Rect::from_top_left_and_size(*p, Offset::uniform(1)),
fg_color,
);
}
}
@ -860,7 +1060,10 @@ pub fn marquee(area: Rect, text: &str, offset: i16, font: Font, fg: Color, bg: C
pub fn dotted_line(start: Point, width: i16, color: Color, step: i16) {
for x in (start.x..width).step_by(step as usize) {
display::bar(x, start.y, 1, 1, color.into());
rect_fill(
Rect::from_top_left_and_size(Point::new(x, start.y), Offset::new(1, 1)),
color,
);
}
}

@ -6,7 +6,7 @@ use crate::{
ui::{
component::image::Image,
constant,
display::{get_color_table, get_offset, pixeldata, pixeldata_dirty, set_window},
display::{get_offset, pixeldata_dirty, set_window},
geometry::{Alignment2D, Offset, Point, Rect},
},
};
@ -20,6 +20,12 @@ use crate::{
ui::display::process_buffer,
};
#[cfg(not(feature = "framebuffer"))]
use crate::ui::display::{get_color_table, pixeldata};
#[cfg(feature = "framebuffer")]
use crate::trezorhal::{buffers::BufferLine4bpp, dma2d::dma2d_setup_4bpp};
use super::Color;
const TOIF_HEADER_LENGTH: usize = 12;
@ -28,6 +34,7 @@ pub fn render_icon(icon: &Icon, center: Point, fg_color: Color, bg_color: Color)
render_toif(&icon.toif, center, fg_color, bg_color);
}
#[cfg(not(feature = "framebuffer"))]
pub fn render_toif(toif: &Toif, center: Point, fg_color: Color, bg_color: Color) {
let r = Rect::from_center_and_size(center, toif.size());
let area = r.translate(get_offset());
@ -63,6 +70,34 @@ pub fn render_toif(toif: &Toif, center: Point, fg_color: Color, bg_color: Color)
pixeldata_dirty();
}
#[cfg(feature = "framebuffer")]
pub fn render_toif(toif: &Toif, center: Point, fg_color: Color, bg_color: Color) {
let r = Rect::from_center_and_size(center, toif.size());
let area = r.translate(get_offset());
set_window(area);
let mut b1 = BufferLine4bpp::get_cleared();
let mut b2 = BufferLine4bpp::get_cleared();
let mut window = [0; UZLIB_WINDOW_SIZE];
let mut ctx = toif.decompression_context(Some(&mut window));
dma2d_setup_4bpp(fg_color.into(), bg_color.into());
for y in area.y0..area.y1 {
let img_buffer_used = if y % 2 == 0 { &mut b1 } else { &mut b2 };
unwrap!(ctx.uncompress(&mut (&mut img_buffer_used.buffer)[0..(area.width() / 2) as usize]));
dma2d_wait_for_transfer();
unsafe { dma2d_start(&img_buffer_used.buffer, area.width()) };
}
dma2d_wait_for_transfer();
pixeldata_dirty();
}
#[no_mangle]
extern "C" fn display_image(
x: cty::int16_t,

@ -2,7 +2,6 @@ use crate::{
trezorhal::{
buffers::{BufferBlurring, BufferJpeg, BufferLine16bpp, BufferLine4bpp, BufferText},
display,
display::bar_radius_buffer,
dma2d::{dma2d_setup_4bpp_over_16bpp, dma2d_start_blend, dma2d_wait_for_transfer},
uzlib::UzlibContext,
},
@ -10,7 +9,7 @@ use crate::{
component::text::TextStyle,
constant::{screen, HEIGHT, WIDTH},
display::{
position_buffer, set_window,
position_buffer, rect_fill_rounded_buffer, set_window,
tjpgd::{BufferInput, BufferOutput, JDEC},
Color, Icon,
},
@ -645,11 +644,11 @@ pub fn homescreen(
let mut next_text_idx = 0;
let mut text_info = if let Some(notification) = notification {
bar_radius_buffer(
NOTIFICATION_BORDER,
0,
WIDTH - NOTIFICATION_BORDER * 2,
NOTIFICATION_HEIGHT,
rect_fill_rounded_buffer(
Rect::from_top_left_and_size(
Point::new(NOTIFICATION_BORDER, 0),
Offset::new(WIDTH - NOTIFICATION_BORDER * 2, NOTIFICATION_HEIGHT),
),
2,
&mut text_buffer,
);

@ -8,7 +8,7 @@ mod fido_icons;
mod error;
mod frame;
mod hold_to_confirm;
#[cfg(feature = "dma2d")]
#[cfg(feature = "micropython")]
mod homescreen;
mod horizontal_page;
mod keyboard;
@ -32,7 +32,7 @@ pub use error::ErrorScreen;
pub use fido::{FidoConfirm, FidoMsg};
pub use frame::{Frame, FrameMsg};
pub use hold_to_confirm::{HoldToConfirm, HoldToConfirmMsg};
#[cfg(feature = "dma2d")]
#[cfg(feature = "micropython")]
pub use homescreen::{Homescreen, HomescreenMsg, Lockscreen};
pub use horizontal_page::HorizontalPage;
pub use keyboard::{

@ -33,6 +33,8 @@ void dma2d_setup_4bpp_over_16bpp(uint16_t overlay_color);
void dma2d_start(uint8_t* in_addr, uint8_t* out_addr, int32_t pixels);
void dma2d_start_const(uint16_t color, uint8_t* out_addr, int32_t pixels);
void dma2d_start_const_multiline(uint16_t color, uint8_t* out_addr,
int32_t width, int32_t height);
void dma2d_start_blend(uint8_t* overlay_addr, uint8_t* bg_addr,
uint8_t* out_addr, int32_t pixels);

@ -43,29 +43,30 @@ static int DISPLAY_ORIENTATION = -1;
// this is just for compatibility with DMA2D using algorithms
uint8_t *const DISPLAY_DATA_ADDRESS = 0;
uint16_t display_index_x = 0;
uint16_t display_index_y = 0;
uint16_t display_window_x0 = 0;
uint16_t display_window_y0 = MAX_DISPLAY_RESX - 1;
uint16_t display_window_x1 = 0;
uint16_t display_window_y1 = MAX_DISPLAY_RESY - 1;
uint16_t cursor_x = 0;
uint16_t cursor_y = 0;
uint16_t window_x0 = 0;
uint16_t window_y0 = MAX_DISPLAY_RESX - 1;
uint16_t window_x1 = 0;
uint16_t window_y1 = MAX_DISPLAY_RESY - 1;
void display_pixeldata(uint16_t c) {
((uint16_t *)LCD_FRAME_BUFFER)[(display_index_y * MAX_DISPLAY_RESX) +
display_index_x] = c;
((uint16_t *)LCD_FRAME_BUFFER)[(cursor_y * MAX_DISPLAY_RESX) + cursor_x] = c;
display_index_x++;
cursor_x++;
if (display_index_x > display_window_x1) {
display_index_x = display_window_x0;
display_index_y++;
if (cursor_x > window_x1) {
cursor_x = window_x0;
cursor_y++;
if (display_index_y > display_window_y1) {
display_index_y = display_window_y0;
if (cursor_y > window_y1) {
cursor_y = window_y0;
}
}
}
void display_pixeldata_dirty(void) {}
void display_reset_state() {}
static void __attribute__((unused)) display_sleep(void) {}
@ -192,12 +193,12 @@ void BSP_LCD_SetLayerAddress_NoReload(uint32_t LayerIndex, uint32_t Address) {
// static struct { uint16_t x, y; } BUFFER_OFFSET;
void display_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
display_window_x0 = x0;
display_window_x1 = x1;
display_window_y0 = y0;
display_window_y1 = y1;
display_index_x = x0;
display_index_y = y0;
window_x0 = x0;
window_x1 = x1;
window_y0 = y0;
window_y1 = y1;
cursor_x = x0;
cursor_y = y0;
// /* Reconfigure the layer size */
// HAL_LTDC_SetWindowSize_NoReload(&LtdcHandler, x1-x0 + 1, y1-y0 + 1, 0);
@ -371,3 +372,46 @@ void display_sync(void) {}
const char *display_save(const char *prefix) { return NULL; }
void display_clear_save(void) {}
void display_efficient_clear(void) {
memzero((void *)LCD_FRAME_BUFFER, 153600);
}
uint8_t *display_get_wr_addr(void) {
uint32_t address = LCD_FRAME_BUFFER;
/* Get the rectangle start address */
address = (address + (2 * ((cursor_y)*MAX_DISPLAY_RESX + (cursor_x))));
return (uint8_t *)address;
}
uint32_t *display_get_fb_addr(void) { return (uint32_t *)LCD_FRAME_BUFFER; }
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 MAX_DISPLAY_RESX - display_get_window_width();
}

@ -5,7 +5,19 @@
#include STM32_HAL_H
#define TREZOR_FONT_BPP 4
#define DISPLAY_FRAMEBUFFER_WIDTH MAX_DISPLAY_RESX
#define DISPLAY_FRAMEBUFFER_HEIGHT MAX_DISPLAY_RESY
#define DISPLAY_FRAMEBUFFER_OFFSET_X 0
#define DISPLAY_FRAMEBUFFER_OFFSET_Y 0
#define DISPLAY_COLOR_MODE DMA2D_OUTPUT_RGB565
#define DISPLAY_EFFICIENT_CLEAR 1
extern uint8_t *const DISPLAY_DATA_ADDRESS;
extern uint8_t* const DISPLAY_DATA_ADDRESS;
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;
}
#endif //_LTDC_H

@ -70,6 +70,8 @@ static int DISPLAY_ORIENTATION = -1;
void display_pixeldata(uint16_t c) { PIXELDATA(c); }
void display_pixeldata_dirty(void) {}
static uint32_t read_display_id(uint8_t command) {
volatile uint8_t c = 0;
uint32_t id = 0;
@ -470,3 +472,9 @@ void display_set_big_endian(void) {
const char *display_save(const char *prefix) { return NULL; }
void display_clear_save(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) {}

@ -7,6 +7,7 @@
// ILI9341V, GC9307 and ST7789V drivers support 240px x 320px display resolution
#define MAX_DISPLAY_RESX 240
#define MAX_DISPLAY_RESY 320
#define DISPLAY_COLOR_MODE DMA2D_OUTPUT_RGB565
#define TREZOR_FONT_BPP 4
#ifdef USE_DISP_I8080_16BIT_DW

@ -33,9 +33,6 @@
(1 << DISPLAY_MEMORY_PIN)))))
#define DATA(X) (ADDR) = (X)
// noop on TR as we don't need to push data to display
#define PIXELDATA_DIRTY()
static int DISPLAY_BACKLIGHT = -1;
static int DISPLAY_ORIENTATION = -1;
struct {
@ -97,7 +94,7 @@ void display_pixeldata(uint16_t c) {
}
}
#define PIXELDATA(c) display_pixeldata(c)
void display_pixeldata_dirty(void) {}
void display_reset_state(void) {
memzero(DISPLAY_STATE.RAM, sizeof(DISPLAY_STATE.RAM));

@ -92,11 +92,9 @@ void display_pixeldata(uint16_t c) {
}
}
#define PIXELDATA(c) display_pixeldata(c)
void display_reset_state() {}
void pixeldata_dirty(void) { pixeldata_dirty_flag = true; }
void display_pixeldata_dirty(void) { pixeldata_dirty_flag = true; }
void display_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
PIXELWINDOW.start.x = x0;

@ -7,7 +7,4 @@
#define DISPLAY_RESY 64
#define TREZOR_FONT_BPP 1
void pixeldata_dirty(void);
#define PIXELDATA_DIRTY() pixeldata_dirty();
#endif //_VG_2864KSWEG01_H

@ -20,6 +20,7 @@
#include "dma2d.h"
#include "colors.h"
#include STM32_HAL_H
#include "display_interface.h"
typedef enum {
DMA2D_LAYER_FG = 1,
@ -27,12 +28,14 @@ typedef enum {
} dma2d_layer_t;
static DMA2D_HandleTypeDef dma2d_handle = {0};
static uint16_t current_width = 0;
static uint16_t current_height = 0;
void dma2d_init(void) {
__HAL_RCC_DMA2D_CLK_ENABLE();
dma2d_handle.Instance = (DMA2D_TypeDef*)DMA2D_BASE;
dma2d_handle.Init.ColorMode = DMA2D_OUTPUT_RGB565;
dma2d_handle.Init.ColorMode = DISPLAY_COLOR_MODE;
dma2d_handle.Init.OutputOffset = 0;
}
@ -61,11 +64,13 @@ static void dma2d_init_clut(uint16_t fg, uint16_t bg, dma2d_layer_t layer) {
void dma2d_setup_const(void) {
dma2d_handle.Init.Mode = DMA2D_R2M;
dma2d_handle.Init.OutputOffset = display_get_window_offset();
HAL_DMA2D_Init(&dma2d_handle);
}
void dma2d_setup_4bpp(uint16_t fg_color, uint16_t bg_color) {
dma2d_handle.Init.Mode = DMA2D_M2M_PFC;
dma2d_handle.Init.OutputOffset = display_get_window_offset();
dma2d_handle.LayerCfg[1].InputColorMode = DMA2D_INPUT_L4;
dma2d_handle.LayerCfg[1].InputOffset = 0;
dma2d_handle.LayerCfg[1].AlphaMode = 0;
@ -79,6 +84,7 @@ void dma2d_setup_4bpp(uint16_t fg_color, uint16_t bg_color) {
void dma2d_setup_16bpp(void) {
dma2d_handle.Init.Mode = DMA2D_M2M_PFC;
dma2d_handle.Init.OutputOffset = display_get_window_offset();
dma2d_handle.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB565;
dma2d_handle.LayerCfg[1].InputOffset = 0;
dma2d_handle.LayerCfg[1].AlphaMode = 0;
@ -90,6 +96,7 @@ void dma2d_setup_16bpp(void) {
void dma2d_setup_4bpp_over_16bpp(uint16_t overlay_color) {
dma2d_handle.Init.Mode = DMA2D_M2M_BLEND;
dma2d_handle.Init.OutputOffset = display_get_window_offset();
dma2d_handle.LayerCfg[1].InputColorMode = DMA2D_INPUT_A4;
dma2d_handle.LayerCfg[1].InputOffset = 0;
dma2d_handle.LayerCfg[1].AlphaMode = 0;
@ -109,6 +116,7 @@ void dma2d_setup_4bpp_over_16bpp(uint16_t overlay_color) {
void dma2d_setup_4bpp_over_4bpp(uint16_t fg_color, uint16_t bg_color,
uint16_t overlay_color) {
dma2d_handle.Init.Mode = DMA2D_M2M_BLEND;
dma2d_handle.Init.OutputOffset = display_get_window_offset();
dma2d_handle.LayerCfg[1].InputColorMode = DMA2D_INPUT_A4;
dma2d_handle.LayerCfg[1].InputOffset = 0;
dma2d_handle.LayerCfg[1].AlphaMode = 0;
@ -127,17 +135,31 @@ void dma2d_setup_4bpp_over_4bpp(uint16_t fg_color, uint16_t bg_color,
}
void dma2d_start(uint8_t* in_addr, uint8_t* out_addr, int32_t pixels) {
current_width = pixels;
current_height = 1;
HAL_DMA2D_Start(&dma2d_handle, (uint32_t)in_addr, (uint32_t)out_addr, pixels,
1);
}
void dma2d_start_const(uint16_t color, uint8_t* out_addr, int32_t pixels) {
current_width = pixels;
current_height = 1;
HAL_DMA2D_Start(&dma2d_handle, rgb565_to_rgb888(color), (uint32_t)out_addr,
pixels, 1);
}
void dma2d_start_const_multiline(uint16_t color, uint8_t* out_addr,
int32_t width, int32_t height) {
current_width = width;
current_height = height;
HAL_DMA2D_Start(&dma2d_handle, rgb565_to_rgb888(color), (uint32_t)out_addr,
width, height);
}
void dma2d_start_blend(uint8_t* overlay_addr, uint8_t* bg_addr,
uint8_t* out_addr, int32_t pixels) {
current_width = pixels;
current_height = 1;
HAL_DMA2D_BlendingStart(&dma2d_handle, (uint32_t)overlay_addr,
(uint32_t)bg_addr, (uint32_t)out_addr, pixels, 1);
}
@ -145,4 +167,7 @@ void dma2d_start_blend(uint8_t* overlay_addr, uint8_t* bg_addr,
void dma2d_wait_for_transfer(void) {
while (HAL_DMA2D_PollForTransfer(&dma2d_handle, 10) != HAL_OK)
;
display_shift_window(current_width * current_height);
current_width = 0;
current_height = 0;
}

@ -115,7 +115,7 @@ void display_pixeldata(pixel_color c) {
}
}
#define PIXELDATA(c) display_pixeldata(c)
void display_pixeldata_dirty(void) {}
void display_reset_state() {}
@ -332,3 +332,5 @@ void display_clear_save(void) {
SDL_FreeSurface(PREV_SAVED);
PREV_SAVED = NULL;
}
uint8_t *display_get_wr_addr(void) { return (uint8_t *)DISPLAY_DATA_ADDRESS; }

@ -38,11 +38,18 @@ def configure(
]
sources += [f"embed/trezorhal/stm32f4/displays/{display}"]
sources += ["embed/trezorhal/stm32f4/displays/ili9341_spi.c"]
sources += ["embed/trezorhal/stm32f4/dma.c"]
sources += ["embed/trezorhal/stm32f4/dma2d.c"]
sources += [
"vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma2d.c"
]
sources += ["embed/trezorhal/stm32f4/sdram.c"]
sources += [
"vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c"
]
defines += ["USE_DMA2D"]
defines += ["FRAMEBUFFER"]
features_available.append("dma2d")
features_available.append("framebuffer")
if "input" in features_wanted:
sources += ["embed/trezorhal/stm32f4/i2c.c"]
@ -50,11 +57,6 @@ def configure(
sources += ["embed/lib/touch.c"]
features_available.append("touch")
if "dma2d" in features_wanted:
defines += ["USE_DMA2D"]
sources += ["embed/lib/dma2d_emul.c"]
features_available.append("dma2d")
if "usb" in features_wanted:
sources += [
"embed/trezorhal/stm32f4/usb.c",

Loading…
Cancel
Save