From be46639b59431589d5989ab3c20e5f2fb3ab8bea Mon Sep 17 00:00:00 2001 From: tychovrahe Date: Wed, 24 May 2023 12:07:03 +0200 Subject: [PATCH] feat(core): add hardware support for T3W1 --- core/SConscript.bootloader | 2 +- core/SConscript.bootloader_ci | 2 +- core/SConscript.bootloader_emu | 6 +- core/SConscript.firmware | 6 +- core/SConscript.prodtest | 2 +- core/SConscript.reflash | 2 +- core/SConscript.unix | 14 +- .../firmware/bootloaders/bootloader_T3W1.bin | Bin 0 -> 111104 bytes core/embed/firmware/memory_T3W1.ld | 1 + core/embed/models/model.h | 2 + core/embed/models/model_T3W1.h | 29 ++++ core/embed/models/model_T3W1_layout.c | 82 ++++++++++ core/embed/rust/src/ui/event.rs | 2 + core/embed/trezorhal/boards/board-unix.h | 7 + core/embed/trezorhal/boards/trezor_t.h | 18 ++- core/embed/trezorhal/boards/trezor_t3w1_d1.h | 71 ++++++++ core/embed/trezorhal/button.h | 16 +- core/embed/trezorhal/stm32f4/button.c | 76 ++++++--- .../trezorhal/stm32f4/displays/panels/154a.h | 4 +- .../stm32f4/displays/panels/lhs200kb-if21.c | 151 ++++++++++++++++++ .../stm32f4/displays/panels/lhs200kb-if21.h | 12 ++ .../stm32f4/displays/panels/lx154a2411.h | 4 +- .../stm32f4/displays/panels/lx154a2422.c | 66 ++++++++ .../stm32f4/displays/panels/lx154a2422.h | 4 + .../stm32f4/displays/panels/tf15411a.c | 65 ++++++++ .../stm32f4/displays/panels/tf15411a.h | 5 +- .../trezorhal/stm32f4/displays/st7789v.c | 93 +++-------- .../trezorhal/stm32f4/displays/st7789v.h | 6 + core/embed/trezorhal/stm32f4/lowlevel.c | 1 + core/embed/trezorhal/stm32f4/sdcard.c | 17 +- core/embed/trezorhal/stm32f4/touch/ft6x36.c | 53 +++--- .../vendorheader/T3W1/vendor_unsafe.json | 19 +++ .../vendorheader/T3W1/vendor_unsafe.toif | Bin 0 -> 2162 bytes .../T3W1/vendorheader_unsafe_signed_dev.bin | Bin 0 -> 4608 bytes .../T3W1/vendorheader_unsafe_signed_prod.bin | Bin 0 -> 4608 bytes .../T3W1/vendorheader_unsafe_unsigned.bin | Bin 0 -> 4608 bytes core/embed/vendorheader/generate.sh | 2 +- core/site_scons/boards/trezor_t3w1_d1.py | 90 +++++++++++ core/site_scons/tools.py | 7 + python/src/trezorlib/firmware/models.py | 29 ++++ python/src/trezorlib/models.py | 11 +- 41 files changed, 830 insertions(+), 147 deletions(-) create mode 100644 core/embed/firmware/bootloaders/bootloader_T3W1.bin create mode 120000 core/embed/firmware/memory_T3W1.ld create mode 100644 core/embed/models/model_T3W1.h create mode 100644 core/embed/models/model_T3W1_layout.c create mode 100644 core/embed/trezorhal/boards/trezor_t3w1_d1.h create mode 100644 core/embed/trezorhal/stm32f4/displays/panels/lhs200kb-if21.c create mode 100644 core/embed/trezorhal/stm32f4/displays/panels/lhs200kb-if21.h create mode 100644 core/embed/vendorheader/T3W1/vendor_unsafe.json create mode 100644 core/embed/vendorheader/T3W1/vendor_unsafe.toif create mode 100644 core/embed/vendorheader/T3W1/vendorheader_unsafe_signed_dev.bin create mode 100644 core/embed/vendorheader/T3W1/vendorheader_unsafe_signed_prod.bin create mode 100644 core/embed/vendorheader/T3W1/vendorheader_unsafe_unsigned.bin create mode 100644 core/site_scons/boards/trezor_t3w1_d1.py diff --git a/core/SConscript.bootloader b/core/SConscript.bootloader index bb6f685a4c..6bf5fa128a 100644 --- a/core/SConscript.bootloader +++ b/core/SConscript.bootloader @@ -36,7 +36,7 @@ if TREZOR_MODEL in ('R', ): FONT_BOLD='Font_PixelOperator_Bold_8' FONT_MONO='Font_PixelOperator_Regular_8' FONT_BIG=None -elif TREZOR_MODEL in ('T', 'DISC1'): +elif TREZOR_MODEL in ('T', 'T3W1', 'DISC1'): FONT_NORMAL='Font_TTHoves_Regular_21' FONT_DEMIBOLD=None FONT_BOLD='Font_TTHoves_Bold_17' diff --git a/core/SConscript.bootloader_ci b/core/SConscript.bootloader_ci index 181f914027..9bac6a7c2b 100644 --- a/core/SConscript.bootloader_ci +++ b/core/SConscript.bootloader_ci @@ -34,7 +34,7 @@ if TREZOR_MODEL in ('1', 'R'): FONT_BOLD=None FONT_MONO='Font_PixelOperatorMono_Regular_8' FONT_BIG=None -elif TREZOR_MODEL in ('T', ): +elif TREZOR_MODEL in ('T', 'T3W1'): FONT_NORMAL='Font_Roboto_Regular_20' FONT_DEMIBOLD=None FONT_BOLD=None diff --git a/core/SConscript.bootloader_emu b/core/SConscript.bootloader_emu index 025ea3f5a8..a0dbf53430 100644 --- a/core/SConscript.bootloader_emu +++ b/core/SConscript.bootloader_emu @@ -33,7 +33,7 @@ if TREZOR_MODEL in ('1', 'R'): FONT_BOLD='Font_PixelOperator_Bold_8' FONT_MONO='Font_PixelOperator_Regular_8' FONT_BIG=None -elif TREZOR_MODEL in ('T', ): +elif TREZOR_MODEL in ('T', 'T3W1'): FONT_NORMAL='Font_TTHoves_Regular_21' FONT_DEMIBOLD=None FONT_BOLD='Font_TTHoves_Bold_17' @@ -159,7 +159,7 @@ env.Replace( env.Replace( TREZOR_MODEL=TREZOR_MODEL, ) -if TREZOR_MODEL in ('T', 'R'): +if TREZOR_MODEL in ('T', 'R', 'T3W1'): CPU_MODEL = 'STM32F427xx' elif TREZOR_MODEL in ('DISC1', ): CPU_MODEL = 'STM32F429xx' @@ -249,7 +249,7 @@ def cargo_build(): else: features = ["model_tt"] - if TREZOR_MODEL in ('T',): + if TREZOR_MODEL in ('T', 'T3W1'): features.append('touch') features.append('backlight') if TREZOR_MODEL in ('R', '1'): diff --git a/core/SConscript.firmware b/core/SConscript.firmware index 42f2725a7c..b0286bc437 100644 --- a/core/SConscript.firmware +++ b/core/SConscript.firmware @@ -38,7 +38,7 @@ if TREZOR_MODEL in ('1', 'R'): FONT_BOLD='Font_PixelOperator_Bold_8' FONT_MONO='Font_PixelOperatorMono_Regular_8' FONT_BIG='Font_Unifont_Regular_16' -elif TREZOR_MODEL in ('T', 'DISC1'): +elif TREZOR_MODEL in ('T', 'T3W1', 'DISC1'): FONT_NORMAL='Font_TTHoves_Regular_21' FONT_DEMIBOLD='Font_TTHoves_DemiBold_21' FONT_BOLD='Font_TTHoves_Bold_17' @@ -379,7 +379,7 @@ env = Environment(ENV=os.environ, CFLAGS=f"{ARGUMENTS.get('CFLAGS', '')} -DPRODU FEATURES_AVAILABLE = tools.configure_board(TREZOR_MODEL, FEATURES_WANTED, env, CPPDEFINES_HAL, SOURCE_HAL, PATH_HAL) -if TREZOR_MODEL in ('T', 'DISC1'): +if TREZOR_MODEL in ('T', 'T3W1', 'DISC1'): LAYOUT = 'LAYOUT_TTV2' elif TREZOR_MODEL in ('1', 'R'): LAYOUT = 'LAYOUT_TR' @@ -813,7 +813,7 @@ BINARY_NAME += "-dirty" if tools.get_git_modified() else "" BINARY_NAME += ".bin" -if TREZOR_MODEL in ('T', 'R', 'DISC1'): +if TREZOR_MODEL in ('T', 'R', 'T3W1', 'DISC1'): action_bin=[ '$OBJCOPY -O binary -j .vendorheader -j .header -j .flash -j .data --pad-to 0x08100000 $SOURCE ${TARGET}.p1', '$OBJCOPY -O binary -j .flash2 $SOURCE ${TARGET}.p2', diff --git a/core/SConscript.prodtest b/core/SConscript.prodtest index 927693ad3c..ca432e6c91 100644 --- a/core/SConscript.prodtest +++ b/core/SConscript.prodtest @@ -38,7 +38,7 @@ if TREZOR_MODEL in ('1', 'R'): FONT_BOLD='Font_PixelOperator_Bold_8' FONT_MONO=None FONT_BIG=None -elif TREZOR_MODEL in ('T', ): +elif TREZOR_MODEL in ('T', 'T3W1'): FONT_NORMAL=None FONT_DEMIBOLD=None FONT_BOLD='Font_Roboto_Bold_20' diff --git a/core/SConscript.reflash b/core/SConscript.reflash index ea2cb2a1f7..f6577682c5 100644 --- a/core/SConscript.reflash +++ b/core/SConscript.reflash @@ -33,7 +33,7 @@ if TREZOR_MODEL in ('1', 'R'): FONT_BOLD='Font_PixelOperator_Bold_8' FONT_MONO=None FONT_BIG=None -elif TREZOR_MODEL in ('T', ): +elif TREZOR_MODEL in ('T', 'T3W1'): FONT_NORMAL=None FONT_DEMIBOLD=None FONT_BOLD='Font_Roboto_Bold_20' diff --git a/core/SConscript.unix b/core/SConscript.unix index 1fe590556d..f386543f14 100644 --- a/core/SConscript.unix +++ b/core/SConscript.unix @@ -42,7 +42,7 @@ if TREZOR_MODEL in ('1', 'R'): FONT_BOLD='Font_PixelOperator_Bold_8' FONT_MONO='Font_PixelOperatorMono_Regular_8' FONT_BIG='Font_Unifont_Regular_16' -elif TREZOR_MODEL in ('T', ): +elif TREZOR_MODEL in ('T', 'T3W1'): FONT_NORMAL='Font_TTHoves_Regular_21' FONT_DEMIBOLD='Font_TTHoves_DemiBold_21' FONT_BOLD='Font_TTHoves_Bold_17' @@ -384,7 +384,7 @@ SOURCE_UNIX = [ 'vendor/micropython/ports/unix/input.c', 'vendor/micropython/ports/unix/unix_mphal.c', ] -if TREZOR_MODEL in ('T', 'R'): +if TREZOR_MODEL in ('T', 'R', 'T3W1'): SOURCE_UNIX += [ 'embed/trezorhal/unix/sbu.c', ] @@ -416,14 +416,14 @@ else: env = Environment(ENV=os.environ, CFLAGS='%s -DPYOPT=%s -DBITCOIN_ONLY=%s %s' % (ARGUMENTS.get('CFLAGS', ''), PYOPT, BITCOIN_ONLY, STATIC)) -if TREZOR_MODEL in ('T',): +if TREZOR_MODEL in ('T', 'T3W1'): LAYOUT = 'LAYOUT_TTV2' elif TREZOR_MODEL in ('1', 'R'): LAYOUT = 'LAYOUT_TR' else: raise ValueError('Unknown Trezor model') -if TREZOR_MODEL in ('T',): +if TREZOR_MODEL in ('T', 'T3W1'): SDCARD = True SOURCE_UNIX += [ 'embed/trezorhal/unix/sdcard.c', @@ -479,7 +479,7 @@ if ARGUMENTS.get('TREZOR_MEMPERF', '0') == '1': env.Replace( TREZOR_MODEL=TREZOR_MODEL, ) -if TREZOR_MODEL in ('T', 'R'): +if TREZOR_MODEL in ('T', 'R', 'T3W1'): CPU_MODEL = 'STM32F427xx' elif TREZOR_MODEL in ('DISC1', ): CPU_MODEL = 'STM32F429xx' @@ -768,7 +768,7 @@ RUST_LIBPATH = f'{RUST_LIBDIR}/lib{RUST_LIB}.a' def cargo_build(): # T1 does not have its own Rust feature, it shares it with TR - model_feature = 'model_tr' if TREZOR_MODEL == '1' else f'model_t{TREZOR_MODEL.lower()}' + model_feature = 'model_tr' if TREZOR_MODEL == '1' else 'model_tt' features = ['micropython', 'protobuf', model_feature] if BITCOIN_ONLY == '1': features.append('bitcoin_only') @@ -778,7 +778,7 @@ def cargo_build(): if DMA2D: features.append('dma2d') - if TREZOR_MODEL in ('T',): + if TREZOR_MODEL in ('T', 'T3W1'): features.append('touch') features.append('sd_card') if TREZOR_MODEL in ('R', '1'): diff --git a/core/embed/firmware/bootloaders/bootloader_T3W1.bin b/core/embed/firmware/bootloaders/bootloader_T3W1.bin new file mode 100644 index 0000000000000000000000000000000000000000..304db439e28d883dce88742835c57cbd9cff3ddb GIT binary patch literal 111104 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%0DrR{3 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 PhysicalButton::Left, 1 => PhysicalButton::Right, + 2 => PhysicalButton::Power, _ => return Err(error::Error::OutOfRange), }; let result = match event & 0xFF { diff --git a/core/embed/trezorhal/boards/board-unix.h b/core/embed/trezorhal/boards/board-unix.h index bdcb7b5452..a964dd3843 100644 --- a/core/embed/trezorhal/boards/board-unix.h +++ b/core/embed/trezorhal/boards/board-unix.h @@ -9,6 +9,13 @@ #define USE_BACKLIGHT 1 #endif +#ifdef TREZOR_MODEL_T3W1 +#define USE_TOUCH 1 +#define USE_SD_CARD 1 +#define USE_SBU 1 +#define USE_RGB_COLORS 1 +#endif + #ifdef TREZOR_MODEL_1 #define USE_BUTTON 1 #endif diff --git a/core/embed/trezorhal/boards/trezor_t.h b/core/embed/trezorhal/boards/trezor_t.h index 950df81579..873406ea9e 100644 --- a/core/embed/trezorhal/boards/trezor_t.h +++ b/core/embed/trezorhal/boards/trezor_t.h @@ -14,6 +14,13 @@ #define USE_BACKLIGHT 1 #define USE_DISP_I8080_8BIT_DW 1 +#include "displays/panels/lx154a2422.h" +#include "displays/st7789v.h" +#define DISPLAY_IDENTIFY 1 +#define DISPLAY_TE_PORT GPIOD +#define DISPLAY_TE_PIN GPIO_PIN_12 +#define TRANSFORM_TOUCH_COORDS lx154a2422_transform_touch_coords + #define BACKLIGHT_PWM_FREQ 50000 #define BACKLIGHT_PWM_TIM TIM1 #define BACKLIGHT_PWM_TIM_CLK_EN __HAL_RCC_TIM1_CLK_ENABLE @@ -39,7 +46,16 @@ #define I2C_INSTANCE_1_RESET_FLG RCC_APB1RSTR_I2C1RST #define TOUCH_I2C_NUM 0 +#define TOUCH_RST_PORT GPIOC +#define TOUCH_RST_PIN GPIO_PIN_5 +#define TOUCH_INT_PORT GPIOC +#define TOUCH_INT_PIN GPIO_PIN_4 +#define TOUCH_ON_PORT GPIOB +#define TOUCH_ON_PIN GPIO_PIN_10 -#include "displays/st7789v.h" +#define SD_DETECT_PORT GPIOC +#define SD_DETECT_PIN GPIO_PIN_13 +#define SD_ENABLE_PORT GPIOC +#define SD_ENABLE_PIN GPIO_PIN_0 #endif //_TREZOR_T_H diff --git a/core/embed/trezorhal/boards/trezor_t3w1_d1.h b/core/embed/trezorhal/boards/trezor_t3w1_d1.h new file mode 100644 index 0000000000..7c5fbfc36e --- /dev/null +++ b/core/embed/trezorhal/boards/trezor_t3w1_d1.h @@ -0,0 +1,71 @@ +#ifndef _TREZOR_T3W1_H +#define _TREZOR_T3W1_H + +#define DISPLAY_RESX 240 +#define DISPLAY_RESY 320 + +#define USE_SD_CARD 1 +#define USE_I2C 1 +#define USE_TOUCH 1 +#define USE_BUTTON 1 +#define USE_SBU 1 +#define USE_RGB_COLORS 1 +#define USE_BACKLIGHT 1 +#define USE_DISP_I8080_16BIT_DW 1 + +#define DISPLAY_PANEL_INIT_SEQ lhs200kb_if21_init_seq +#define DISPLAY_PANEL_ROTATE lhs200kb_if21_rotate +#define TRANSFORM_TOUCH_COORDS lhs200kb_if21_transform_touch_coords + +#include "displays/panels/lhs200kb-if21.h" +#include "displays/st7789v.h" + +#define BACKLIGHT_PWM_FREQ 50000 +#define BACKLIGHT_PWM_TIM TIM1 +#define BACKLIGHT_PWM_TIM_CLK_EN __HAL_RCC_TIM1_CLK_ENABLE +#define BACKLIGHT_PWM_TIM_AF GPIO_AF1_TIM1 +#define BACKLIGHT_PWM_TIM_OCMODE TIM_OCMODE_PWM2 +#define BACKLIGHT_PWM_TIM_CHANNEL TIM_CHANNEL_1 +#define BACKLIGHT_PWM_TIM_CCR CCR1 +#define BACKLIGHT_PWM_PIN GPIO_PIN_7 +#define BACKLIGHT_PWM_PORT GPIOA +#define BACKLIGHT_PWM_PORT_CLK_EN __HAL_RCC_GPIOA_CLK_ENABLE + +#define I2C_COUNT 1 +#define I2C_INSTANCE_1 I2C2 +#define I2C_INSTANCE_1_CLK_EN __HAL_RCC_I2C2_CLK_ENABLE +#define I2C_INSTANCE_1_CLK_DIS __HAL_RCC_I2C2_CLK_DISABLE +#define I2C_INSTANCE_1_PIN_AF GPIO_AF4_I2C2 +#define I2C_INSTANCE_1_SDA_PORT GPIOB +#define I2C_INSTANCE_1_SDA_PIN GPIO_PIN_11 +#define I2C_INSTANCE_1_SDA_CLK_EN __HAL_RCC_GPIOB_CLK_ENABLE +#define I2C_INSTANCE_1_SCL_PORT GPIOB +#define I2C_INSTANCE_1_SCL_PIN GPIO_PIN_10 +#define I2C_INSTANCE_1_SCL_CLK_EN __HAL_RCC_GPIOB_CLK_ENABLE +#define I2C_INSTANCE_1_RESET_FLG RCC_APB1RSTR_I2C2RST + +#define TOUCH_I2C_NUM 0 +#define TOUCH_RST_PORT GPIOC +#define TOUCH_RST_PIN GPIO_PIN_5 +#define TOUCH_INT_PORT GPIOC +#define TOUCH_INT_PIN GPIO_PIN_4 +#define TOUCH_ON_PORT GPIOB +#define TOUCH_ON_PIN GPIO_PIN_8 + +#define SD_DETECT_PORT GPIOB +#define SD_DETECT_PIN GPIO_PIN_0 +#define SD_ENABLE_PORT GPIOE +#define SD_ENABLE_PIN GPIO_PIN_1 + +#define GPIO_1_PORT GPIOC +#define GPIO_1_PIN GPIO_PIN_1 +#define GPIO_2_PORT GPIOC +#define GPIO_2_PIN GPIO_PIN_6 +#define GPIO_3_PORT GPIOC +#define GPIO_3_PIN GPIO_PIN_7 + +#define BTN_POWER_CLK_ENA __HAL_RCC_GPIOE_CLK_ENABLE +#define BTN_POWER_PORT GPIOE +#define BTN_POWER_PIN GPIO_PIN_0 + +#endif //_TREZOR_T3W1_H diff --git a/core/embed/trezorhal/button.h b/core/embed/trezorhal/button.h index f1389f7f4e..ee4953a5fe 100644 --- a/core/embed/trezorhal/button.h +++ b/core/embed/trezorhal/button.h @@ -20,6 +20,9 @@ #ifndef TREZORHAL_BUTTON_H #define TREZORHAL_BUTTON_H +#include TREZOR_BOARD + +#include #include #define BTN_EVT_DOWN (1U << 24) @@ -27,10 +30,19 @@ #define BTN_LEFT 0 #define BTN_RIGHT 1 +#define BTN_POWER 2 void button_init(void); uint32_t button_read(void); -char button_state_left(void); -char button_state_right(void); + +#ifdef BTN_LEFT_CLK_ENA +bool button_state_left(void); +#endif +#ifdef BTN_RIGHT_CLK_ENA +bool button_state_right(void); +#endif +#ifdef BTN_POWER_CLK_ENA +bool button_state_power(void); +#endif #endif diff --git a/core/embed/trezorhal/stm32f4/button.c b/core/embed/trezorhal/stm32f4/button.c index 7555781d4e..9221f60595 100644 --- a/core/embed/trezorhal/stm32f4/button.c +++ b/core/embed/trezorhal/stm32f4/button.c @@ -1,28 +1,54 @@ + +#include "stdbool.h" + #include STM32_HAL_H #include "button.h" -#include TREZOR_BOARD - -static char last_left = 0, last_right = 0; - -void button_init(void) { - BTN_LEFT_CLK_ENA(); - BTN_RIGHT_CLK_ENA(); +static void init_btn(GPIO_TypeDef *port, uint16_t pin) { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.Mode = GPIO_MODE_INPUT; GPIO_InitStructure.Pull = GPIO_PULLUP; GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStructure.Pin = BTN_LEFT_PIN; - HAL_GPIO_Init(BTN_LEFT_PORT, &GPIO_InitStructure); - GPIO_InitStructure.Pin = BTN_RIGHT_PIN; - HAL_GPIO_Init(BTN_RIGHT_PORT, &GPIO_InitStructure); + GPIO_InitStructure.Pin = pin; + HAL_GPIO_Init(port, &GPIO_InitStructure); +} + +#ifdef BTN_LEFT_CLK_ENA +static bool last_left = 0; +bool button_state_left(void) { return last_left; } +#endif + +#ifdef BTN_RIGHT_CLK_ENA +static bool last_right = 0; +bool button_state_right(void) { return last_right; } +#endif + +#ifdef BTN_POWER_CLK_ENA +static bool last_power = 0; +bool button_state_power(void) { return last_power; } +#endif + +void button_init(void) { +#ifdef BTN_LEFT_CLK_ENA + BTN_LEFT_CLK_ENA(); + init_btn(BTN_LEFT_PORT, BTN_LEFT_PIN); +#endif + +#ifdef BTN_RIGHT_CLK_ENA + BTN_RIGHT_CLK_ENA(); + init_btn(BTN_RIGHT_PORT, BTN_RIGHT_PIN); +#endif + +#ifdef BTN_POWER_CLK_ENA + BTN_POWER_CLK_ENA(); + init_btn(BTN_POWER_PORT, BTN_POWER_PIN); +#endif } uint32_t button_read(void) { - char left = (GPIO_PIN_RESET == HAL_GPIO_ReadPin(BTN_LEFT_PORT, BTN_LEFT_PIN)); - char right = - (GPIO_PIN_RESET == HAL_GPIO_ReadPin(BTN_RIGHT_PORT, BTN_RIGHT_PIN)); +#ifdef BTN_LEFT_CLK_ENA + bool left = (GPIO_PIN_RESET == HAL_GPIO_ReadPin(BTN_LEFT_PORT, BTN_LEFT_PIN)); if (last_left != left) { last_left = left; if (left) { @@ -31,6 +57,10 @@ uint32_t button_read(void) { return BTN_EVT_UP | BTN_LEFT; } } +#endif +#ifdef BTN_RIGHT_CLK_ENA + bool right = + (GPIO_PIN_RESET == HAL_GPIO_ReadPin(BTN_RIGHT_PORT, BTN_RIGHT_PIN)); if (last_right != right) { last_right = right; if (right) { @@ -39,9 +69,19 @@ uint32_t button_read(void) { return BTN_EVT_UP | BTN_RIGHT; } } +#endif +#ifdef BTN_POWER_CLK_ENA + bool power = + (GPIO_PIN_RESET == HAL_GPIO_ReadPin(BTN_POWER_PORT, BTN_POWER_PIN)); + if (last_power != power) { + last_power = power; + if (power) { + return BTN_EVT_DOWN | BTN_POWER; + } else { + return BTN_EVT_UP | BTN_POWER; + } + } +#endif + return 0; } - -char button_state_left(void) { return last_left; } - -char button_state_right(void) { return last_right; } diff --git a/core/embed/trezorhal/stm32f4/displays/panels/154a.h b/core/embed/trezorhal/stm32f4/displays/panels/154a.h index 0fd36a96fb..e9e6d66819 100644 --- a/core/embed/trezorhal/stm32f4/displays/panels/154a.h +++ b/core/embed/trezorhal/stm32f4/displays/panels/154a.h @@ -1,5 +1,5 @@ -#ifndef TT_OLD2_H_ -#define TT_OLD2_H_ +#ifndef _154A_H_ +#define _154A_H_ // ILI9341 IC controller diff --git a/core/embed/trezorhal/stm32f4/displays/panels/lhs200kb-if21.c b/core/embed/trezorhal/stm32f4/displays/panels/lhs200kb-if21.c new file mode 100644 index 0000000000..f692011c20 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/displays/panels/lhs200kb-if21.c @@ -0,0 +1,151 @@ + +#include TREZOR_BOARD +#include "display_interface.h" +#include "displays/st7789v.h" +#include "touch.h" + +void lhs200kb_if21_init_seq() { + CMD(0x36); + DATA(0x00); + + CMD(0x35); + DATA(0x00); + + CMD(0x3A); + DATA(0x05); + + CMD(0xB2); + DATA(0x0C); + DATA(0x0C); + DATA(0x00); + DATA(0x33); + DATA(0x33); + + CMD(0xB7); + DATA(0x78); + + CMD(0xBB); + DATA(0x2F); + + CMD(0xC0); + DATA(0x2C); + + CMD(0xC2); + DATA(0x01); + + CMD(0xC3); + DATA(0x19); + + CMD(0xC4); + DATA(0x20); + + CMD(0xC6); + DATA(0x0F); + + CMD(0xD0); + DATA(0xA4); + DATA(0xA1); + + CMD(0xD6); + DATA(0xA1); + + CMD(0xE0); + DATA(0xF0); + DATA(0x08); + DATA(0x0F); + DATA(0x0B); + DATA(0x0B); + DATA(0x07); + DATA(0x34); + DATA(0x43); + DATA(0x4B); + DATA(0x38); + DATA(0x14); + DATA(0x13); + DATA(0x2C); + DATA(0x31); + + CMD(0xE1); + DATA(0xF0); + DATA(0x0C); + DATA(0x11); + DATA(0x09); + DATA(0x08); + DATA(0x24); + DATA(0x34); + DATA(0x33); + DATA(0x4A); + DATA(0x3A); + DATA(0x16); + DATA(0x16); + DATA(0x2E); + DATA(0x32); + + CMD(0x21); + + CMD(0x29); +} + +void lhs200kb_if21_rotate(int degrees, buffer_offset_t* offset) { + uint16_t shift = 0; + char BX = 0, BY = 0; + +#define RGB (1 << 3) +#define ML (1 << 4) // vertical refresh order +#define MH (1 << 2) // horizontal refresh order +#define MV (1 << 5) +#define MX (1 << 6) +#define MY (1 << 7) + // MADCTL: Memory Data Access Control - reference: + // section 8.12 in the ST7789V manual + uint8_t display_command_parameter = 0; + switch (degrees) { + case 0: + display_command_parameter = 0; + BY = 0; + break; + case 90: + display_command_parameter = MV | MX | MH | ML; + BX = 1; + shift = 1; + break; + case 180: + display_command_parameter = MX | MY | MH | ML; + BY = 0; + shift = 1; + break; + case 270: + display_command_parameter = MV | MY; + BX = 1; + break; + } + + CMD(0x36); + DATA(display_command_parameter); + + if (shift) { + // GATECTRL: Gate Control; NL = 320 gate lines, first scan line is + // gate 0.; gate scan direction 319 -> 0 + CMD(0xE4); + DATA(0x27); + DATA(0x00); + DATA(0x10); + } else { + // GATECTRL: Gate Control; NL = 320 gate lines, first scan line is + // gate 0.; gate scan direction 319 -> 0 + CMD(0xE4); + DATA(0x27); + DATA(0x00); + DATA(0x10); + } + + // reset the column and page extents + display_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1); + + offset->x = BX ? (MAX_DISPLAY_RESY - DISPLAY_RESY) : 0; + offset->y = BY ? (MAX_DISPLAY_RESY - DISPLAY_RESY) : 0; +} + +uint32_t lhs200kb_if21_transform_touch_coords(uint16_t x, uint16_t y) { + return touch_pack_xy(y, MAX_DISPLAY_RESY - x); +} diff --git a/core/embed/trezorhal/stm32f4/displays/panels/lhs200kb-if21.h b/core/embed/trezorhal/stm32f4/displays/panels/lhs200kb-if21.h new file mode 100644 index 0000000000..3cddcba7e8 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/displays/panels/lhs200kb-if21.h @@ -0,0 +1,12 @@ + +#ifndef CORE_LHS200KB_IF21_H +#define CORE_LHS200KB_IF21_H +// ST7789_V IC controller + +#include "displays/st7789v.h" + +void lhs200kb_if21_init_seq(void); +void lhs200kb_if21_rotate(int degrees, buffer_offset_t* offset); +uint32_t lhs200kb_if21_transform_touch_coords(uint16_t x, uint16_t y); + +#endif // CORE_LHS200KB_IF21_H diff --git a/core/embed/trezorhal/stm32f4/displays/panels/lx154a2411.h b/core/embed/trezorhal/stm32f4/displays/panels/lx154a2411.h index 83485b2800..a785ca6506 100644 --- a/core/embed/trezorhal/stm32f4/displays/panels/lx154a2411.h +++ b/core/embed/trezorhal/stm32f4/displays/panels/lx154a2411.h @@ -1,5 +1,5 @@ -#ifndef TT_OLD3_H_ -#define TT_OLD3_H_ +#ifndef LX154A2411_H_ +#define LX154A2411_H_ // ST7789_V IC controller void lx154a2411_gamma(void); diff --git a/core/embed/trezorhal/stm32f4/displays/panels/lx154a2422.c b/core/embed/trezorhal/stm32f4/displays/panels/lx154a2422.c index 9787d51a58..cc24ddcc73 100644 --- a/core/embed/trezorhal/stm32f4/displays/panels/lx154a2422.c +++ b/core/embed/trezorhal/stm32f4/displays/panels/lx154a2422.c @@ -1,5 +1,7 @@ +#include "display_interface.h" #include "displays/st7789v.h" +#include "touch.h" void lx154a2422_gamma(void) { // positive voltage correction @@ -80,3 +82,67 @@ void lx154a2422_init_seq(void) { lx154a2422_gamma(); } + +void lx154a2422_rotate(int degrees, buffer_offset_t* offset) { + uint16_t shift = 0; + char BX = 0, BY = 0; + +#define RGB (1 << 3) +#define ML (1 << 4) // vertical refresh order +#define MH (1 << 2) // horizontal refresh order +#define MV (1 << 5) +#define MX (1 << 6) +#define MY (1 << 7) + // MADCTL: Memory Data Access Control - reference: + // section 8.12 in the ST7789V manual + uint8_t display_command_parameter = 0; + switch (degrees) { + case 0: + display_command_parameter = 0; + BY = 0; + break; + case 90: + display_command_parameter = MV | MX | MH | ML; + BX = 1; + shift = 1; + break; + case 180: + display_command_parameter = MX | MY | MH | ML; + BY = 0; + shift = 1; + break; + case 270: + display_command_parameter = MV | MY; + BX = 1; + break; + } + + CMD(0x36); + DATA(display_command_parameter); + + if (shift) { + // GATECTRL: Gate Control; NL = 240 gate lines, first scan line is + // gate 80.; gate scan direction 319 -> 0 + CMD(0xE4); + DATA(0x1D); + DATA(0x00); + DATA(0x11); + } else { + // GATECTRL: Gate Control; NL = 240 gate lines, first scan line is + // gate 80.; gate scan direction 319 -> 0 + CMD(0xE4); + DATA(0x1D); + DATA(0x0A); + DATA(0x11); + } + + // reset the column and page extents + display_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1); + + offset->x = BX ? (MAX_DISPLAY_RESY - DISPLAY_RESY) : 0; + offset->y = BY ? (MAX_DISPLAY_RESY - DISPLAY_RESY) : 0; +} + +uint32_t lx154a2422_transform_touch_coords(uint16_t x, uint16_t y) { + return touch_pack_xy(x, y); +} diff --git a/core/embed/trezorhal/stm32f4/displays/panels/lx154a2422.h b/core/embed/trezorhal/stm32f4/displays/panels/lx154a2422.h index 6f8508e527..3232542c3e 100644 --- a/core/embed/trezorhal/stm32f4/displays/panels/lx154a2422.h +++ b/core/embed/trezorhal/stm32f4/displays/panels/lx154a2422.h @@ -1,7 +1,11 @@ #ifndef LX154A2422_H_ #define LX154A2422_H_ +#include "displays/st7789v.h" + void lx154a2422_init_seq(void); void lx154a2422_gamma(void); +void lx154a2422_rotate(int degrees, buffer_offset_t* offset); +uint32_t lx154a2422_transform_touch_coords(uint16_t x, uint16_t y); #endif diff --git a/core/embed/trezorhal/stm32f4/displays/panels/tf15411a.c b/core/embed/trezorhal/stm32f4/displays/panels/tf15411a.c index cbceaeee9a..073e9b179c 100644 --- a/core/embed/trezorhal/stm32f4/displays/panels/tf15411a.c +++ b/core/embed/trezorhal/stm32f4/displays/panels/tf15411a.c @@ -1,3 +1,4 @@ +#include "display_interface.h" #include "displays/st7789v.h" void tf15411a_init_seq(void) { @@ -91,3 +92,67 @@ void tf15411a_init_seq(void) { DATA(0x37); DATA(0x8F); } + +void tf15411a_rotate(int degrees, buffer_offset_t* offset) { + uint16_t shift = 0; + char BX = 0, BY = 0; + +#define RGB (1 << 3) +#define ML (1 << 4) // vertical refresh order +#define MH (1 << 2) // horizontal refresh order +#define MV (1 << 5) +#define MX (1 << 6) +#define MY (1 << 7) + // MADCTL: Memory Data Access Control - reference: + // section 9.3 in the ILI9341 manual + // section 6.2.18 in the GC9307 manual + // section 8.12 in the ST7789V manual + uint8_t display_command_parameter = 0; + switch (degrees) { + case 0: + display_command_parameter = 0; + BY = 1; + break; + case 90: + display_command_parameter = MV | MX | MH | ML; + BX = 0; + shift = 1; + break; + case 180: + display_command_parameter = MX | MY | MH | ML; + BY = 1; + shift = 1; + break; + case 270: + display_command_parameter = MV | MY; + BX = 0; + break; + } + + display_command_parameter ^= RGB | MY; // XOR RGB and MY settings + + CMD(0x36); + DATA(display_command_parameter); + + if (shift) { + // GATECTRL: Gate Control; NL = 240 gate lines, first scan line is + // gate 80.; gate scan direction 319 -> 0 + CMD(0xE4); + DATA(0x1D); + DATA(0x00); + DATA(0x11); + } else { + // GATECTRL: Gate Control; NL = 240 gate lines, first scan line is + // gate 80.; gate scan direction 319 -> 0 + CMD(0xE4); + DATA(0x1D); + DATA(0x0A); + DATA(0x11); + } + + // reset the column and page extents + display_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1); + + offset->x = BX ? (MAX_DISPLAY_RESY - DISPLAY_RESY) : 0; + offset->y = BY ? (MAX_DISPLAY_RESY - DISPLAY_RESY) : 0; +} diff --git a/core/embed/trezorhal/stm32f4/displays/panels/tf15411a.h b/core/embed/trezorhal/stm32f4/displays/panels/tf15411a.h index 2771e7e836..12abc40aed 100644 --- a/core/embed/trezorhal/stm32f4/displays/panels/tf15411a.h +++ b/core/embed/trezorhal/stm32f4/displays/panels/tf15411a.h @@ -1,8 +1,9 @@ -#ifndef TT_OLD1_H_ -#define TT_OLD1_H_ +#ifndef TF15411A_H_ +#define TF15411A_H_ // GC9307 IC controller void tf15411a_init_seq(void); +void tf15411a_rotate(int degrees, buffer_offset_t* offset); #endif diff --git a/core/embed/trezorhal/stm32f4/displays/st7789v.c b/core/embed/trezorhal/stm32f4/displays/st7789v.c index 681531ab72..acf782fd7c 100644 --- a/core/embed/trezorhal/stm32f4/displays/st7789v.c +++ b/core/embed/trezorhal/stm32f4/displays/st7789v.c @@ -67,9 +67,12 @@ __IO DISP_MEM_TYPE *const DISPLAY_DATA_ADDRESS = #define DISPLAY_ID_ILI9341V 0x009341U static int DISPLAY_ORIENTATION = -1; +static buffer_offset_t BUFFER_OFFSET = {0}; void display_pixeldata(uint16_t c) { PIXELDATA(c); } +#ifdef DISPLAY_IDENTIFY + static uint32_t read_display_id(uint8_t command) { volatile uint8_t c = 0; uint32_t id = 0; @@ -105,6 +108,9 @@ static uint32_t display_identify(void) { id_set = 1; return id; } +#else +static uint32_t display_identify(void) { return DISPLAY_ID_ST7789V; } +#endif bool display_is_inverted() { bool inv_on = false; @@ -149,8 +155,6 @@ static void display_unsleep(void) { } } -static struct { uint16_t x, y; } BUFFER_OFFSET; - void display_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { x0 += BUFFER_OFFSET.x; x1 += BUFFER_OFFSET.x; @@ -183,70 +187,16 @@ int display_orientation(int degrees) { // 2 bytes per pixel because we're using RGB 5-6-5 format PIXELDATA(0x0000); } - - uint16_t shift = 0; - char BX = 0, BY = 0; +#ifdef TREZOR_MODEL_T uint32_t id = display_identify(); - if ((id == DISPLAY_ID_ILI9341V) || (id == DISPLAY_ID_GC9307) || - (id == DISPLAY_ID_ST7789V)) { -#define RGB (1 << 3) -#define ML (1 << 4) // vertical refresh order -#define MH (1 << 2) // horizontal refresh order -#define MV (1 << 5) -#define MX (1 << 6) -#define MY (1 << 7) - // MADCTL: Memory Data Access Control - reference: - // section 9.3 in the ILI9341 manual - // section 6.2.18 in the GC9307 manual - // section 8.12 in the ST7789V manual - uint8_t display_command_parameter = 0; - switch (degrees) { - case 0: - display_command_parameter = 0; - BY = (id == DISPLAY_ID_GC9307); - break; - case 90: - display_command_parameter = MV | MX | MH | ML; - BX = (id != DISPLAY_ID_GC9307); - shift = 1; - break; - case 180: - display_command_parameter = MX | MY | MH | ML; - BY = (id == DISPLAY_ID_GC9307); - shift = 1; - break; - case 270: - display_command_parameter = MV | MY; - BX = (id != DISPLAY_ID_GC9307); - break; - } - if (id == DISPLAY_ID_GC9307) { - display_command_parameter ^= RGB | MY; // XOR RGB and MY settings - } - CMD(0x36); - DATA(display_command_parameter); - - if (shift) { - // GATECTRL: Gate Control; NL = 240 gate lines, first scan line is - // gate 80.; gate scan direction 319 -> 0 - CMD(0xE4); - DATA(0x1D); - DATA(0x00); - DATA(0x11); - } else { - // GATECTRL: Gate Control; NL = 240 gate lines, first scan line is - // gate 80.; gate scan direction 319 -> 0 - CMD(0xE4); - DATA(0x1D); - DATA(0x0A); - DATA(0x11); - } - - // reset the column and page extents - display_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1); + if (id == DISPLAY_ID_GC9307) { + tf15411a_rotate(degrees, &BUFFER_OFFSET); + } else { + lx154a2422_rotate(degrees, &BUFFER_OFFSET); } - BUFFER_OFFSET.x = BX ? (MAX_DISPLAY_RESY - DISPLAY_RESY) : 0; - BUFFER_OFFSET.y = BY ? (MAX_DISPLAY_RESY - DISPLAY_RESY) : 0; +#else + DISPLAY_PANEL_ROTATE(degrees, &BUFFER_OFFSET); +#endif } } return DISPLAY_ORIENTATION; @@ -355,13 +305,15 @@ void display_init(void) { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_RESET); HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); - // LCD_FMARK/PD12 (tearing effect) +#ifdef DISPLAY_TE_PIN + // LCD_FMARK (tearing effect) GPIO_InitStructure.Mode = GPIO_MODE_INPUT; GPIO_InitStructure.Pull = GPIO_NOPULL; GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStructure.Alternate = 0; - GPIO_InitStructure.Pin = GPIO_PIN_12; - HAL_GPIO_Init(GPIOD, &GPIO_InitStructure); + GPIO_InitStructure.Pin = DISPLAY_TE_PIN; + HAL_GPIO_Init(DISPLAY_TE_PORT, &GPIO_InitStructure); +#endif GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; GPIO_InitStructure.Pull = GPIO_NOPULL; @@ -420,15 +372,18 @@ void display_reinit(void) { } 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(GPIOD, GPIO_PIN_12)) { + while (GPIO_PIN_SET == HAL_GPIO_ReadPin(DISPLAY_TE_PORT, DISPLAY_TE_PIN)) { } - while (GPIO_PIN_RESET == HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_12)) { + while (GPIO_PIN_RESET == + HAL_GPIO_ReadPin(DISPLAY_TE_PORT, DISPLAY_TE_PIN)) { } } +#endif } void display_refresh(void) {} diff --git a/core/embed/trezorhal/stm32f4/displays/st7789v.h b/core/embed/trezorhal/stm32f4/displays/st7789v.h index 70445ff1fb..2905585aa2 100644 --- a/core/embed/trezorhal/stm32f4/displays/st7789v.h +++ b/core/embed/trezorhal/stm32f4/displays/st7789v.h @@ -2,6 +2,12 @@ #define _ST7789V_H #include STM32_HAL_H + +typedef struct { + uint16_t x; + uint16_t y; +} buffer_offset_t; + #include TREZOR_BOARD // ILI9341V, GC9307 and ST7789V drivers support 240px x 320px display resolution diff --git a/core/embed/trezorhal/stm32f4/lowlevel.c b/core/embed/trezorhal/stm32f4/lowlevel.c index 6170e69fba..4e72208861 100644 --- a/core/embed/trezorhal/stm32f4/lowlevel.c +++ b/core/embed/trezorhal/stm32f4/lowlevel.c @@ -141,6 +141,7 @@ void periph_init(void) { __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOD_CLK_ENABLE(); + __HAL_RCC_GPIOE_CLK_ENABLE(); // enable the PVD (programmable voltage detector). // select the "2.7V" threshold (level 5). diff --git a/core/embed/trezorhal/stm32f4/sdcard.c b/core/embed/trezorhal/stm32f4/sdcard.c index a3a242b4d9..728c548064 100644 --- a/core/embed/trezorhal/stm32f4/sdcard.c +++ b/core/embed/trezorhal/stm32f4/sdcard.c @@ -44,6 +44,7 @@ */ #include STM32_HAL_H +#include TREZOR_BOARD #include @@ -68,7 +69,7 @@ void DMA2_Stream3_IRQHandler(void) { } static inline void sdcard_default_pin_state(void) { - HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET); // SD_ON/PC0 + HAL_GPIO_WritePin(SD_ENABLE_PORT, SD_ENABLE_PIN, GPIO_PIN_SET); // SD_ON HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_RESET); // SD_DAT0/PC8 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9, GPIO_PIN_RESET); // SD_DAT1/PC9 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_RESET); // SD_DAT2/PC10 @@ -82,8 +83,8 @@ static inline void sdcard_default_pin_state(void) { GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStructure.Pull = GPIO_NOPULL; GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStructure.Pin = GPIO_PIN_0; - HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); + GPIO_InitStructure.Pin = SD_ENABLE_PIN; + HAL_GPIO_Init(SD_ENABLE_PORT, &GPIO_InitStructure); // configure SD GPIO GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; @@ -99,12 +100,13 @@ static inline void sdcard_default_pin_state(void) { GPIO_InitStructure.Mode = GPIO_MODE_INPUT; GPIO_InitStructure.Pull = GPIO_PULLUP; GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStructure.Pin = GPIO_PIN_13; - HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); + GPIO_InitStructure.Pin = SD_DETECT_PIN; + HAL_GPIO_Init(SD_DETECT_PORT, &GPIO_InitStructure); } static inline void sdcard_active_pin_state(void) { - HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET); // SD_ON/PC0 + HAL_GPIO_WritePin(SD_ENABLE_PORT, SD_ENABLE_PIN, + GPIO_PIN_RESET); // SD_ON/PC0 HAL_Delay(10); // we need to wait until the circuit fully kicks-in GPIO_InitTypeDef GPIO_InitStructure; @@ -210,7 +212,8 @@ void sdcard_power_off(void) { } secbool sdcard_is_present(void) { - return sectrue * (GPIO_PIN_RESET == HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13)); + return sectrue * + (GPIO_PIN_RESET == HAL_GPIO_ReadPin(SD_DETECT_PORT, SD_DETECT_PIN)); } uint64_t sdcard_get_capacity_in_bytes(void) { diff --git a/core/embed/trezorhal/stm32f4/touch/ft6x36.c b/core/embed/trezorhal/stm32f4/touch/ft6x36.c index ab53da47f3..3cdde5a70e 100644 --- a/core/embed/trezorhal/stm32f4/touch/ft6x36.c +++ b/core/embed/trezorhal/stm32f4/touch/ft6x36.c @@ -47,15 +47,14 @@ static void touch_default_pin_state(void) { // set power off and other pins as per section 3.5 of FT6236 datasheet - HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, - GPIO_PIN_SET); // CTP_ON/PB10 (active low) i.e.- CTPM power + HAL_GPIO_WritePin(TOUCH_ON_PORT, TOUCH_ON_PIN, + GPIO_PIN_SET); // CTP_ON (active low) i.e.- CTPM power // off when set/high/log 1 - HAL_GPIO_WritePin( - GPIOC, GPIO_PIN_4, - GPIO_PIN_RESET); // CTP_INT/PC4 normally an input, but drive low as an - // output while powered off - HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5, - GPIO_PIN_RESET); // CTP_REST/PC5 (active low) i.e.- CTPM + HAL_GPIO_WritePin(TOUCH_INT_PORT, TOUCH_INT_PIN, + GPIO_PIN_RESET); // CTP_INT normally an input, but drive + // low as an output while powered off + HAL_GPIO_WritePin(TOUCH_RST_PORT, TOUCH_RST_PIN, + GPIO_PIN_RESET); // CTP_REST (active low) i.e.- CTPM // held in reset until released // set above pins to OUTPUT / NOPULL @@ -64,10 +63,12 @@ static void touch_default_pin_state(void) { GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStructure.Pull = GPIO_NOPULL; GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStructure.Pin = GPIO_PIN_10; - HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); - GPIO_InitStructure.Pin = GPIO_PIN_4 | GPIO_PIN_5; - HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); + GPIO_InitStructure.Pin = TOUCH_INT_PIN; + HAL_GPIO_Init(TOUCH_INT_PORT, &GPIO_InitStructure); + GPIO_InitStructure.Pin = TOUCH_RST_PIN; + HAL_GPIO_Init(TOUCH_RST_PORT, &GPIO_InitStructure); + GPIO_InitStructure.Pin = TOUCH_ON_PIN; + HAL_GPIO_Init(TOUCH_ON_PORT, &GPIO_InitStructure); // in-case power was on, or CTPM was active make sure to wait long enough // for these changes to take effect. a reset needs to be low for @@ -77,20 +78,21 @@ static void touch_default_pin_state(void) { } static void touch_active_pin_state(void) { - HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET); // CTP_ON/PB10 + HAL_GPIO_WritePin(TOUCH_ON_PORT, TOUCH_ON_PIN, GPIO_PIN_RESET); // CTP_ON HAL_Delay(10); // we need to wait until the circuit fully kicks-in GPIO_InitTypeDef GPIO_InitStructure; - // PC4 capacitive touch panel module (CTPM) interrupt (INT) input + // capacitive touch panel module (CTPM) interrupt (INT) input GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING; GPIO_InitStructure.Pull = GPIO_PULLUP; GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStructure.Pin = GPIO_PIN_4; - HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); - __HAL_GPIO_EXTI_CLEAR_FLAG(GPIO_PIN_4); + GPIO_InitStructure.Pin = TOUCH_INT_PIN; + HAL_GPIO_Init(TOUCH_INT_PORT, &GPIO_InitStructure); + __HAL_GPIO_EXTI_CLEAR_FLAG(TOUCH_INT_PIN); - HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5, GPIO_PIN_SET); // release CTPM reset + HAL_GPIO_WritePin(TOUCH_RST_PORT, TOUCH_RST_PIN, + GPIO_PIN_SET); // release CTPM reset HAL_Delay(310); // "Time of starting to report point after resetting" min is // 300ms, giving an extra 10ms } @@ -115,6 +117,7 @@ void touch_power_on(void) { // turn on CTP circuitry touch_active_pin_state(); + HAL_Delay(50); } @@ -131,9 +134,9 @@ void touch_init(void) { GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING; GPIO_InitStructure.Pull = GPIO_PULLUP; GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStructure.Pin = GPIO_PIN_4; - HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); - __HAL_GPIO_EXTI_CLEAR_FLAG(GPIO_PIN_4); + GPIO_InitStructure.Pin = TOUCH_INT_PIN; + HAL_GPIO_Init(TOUCH_INT_PORT, &GPIO_InitStructure); + __HAL_GPIO_EXTI_CLEAR_FLAG(TOUCH_INT_PIN); touch_set_mode(); touch_sensitivity(0x06); @@ -161,9 +164,9 @@ uint32_t touch_is_detected(void) { // Reference section 1.2 of "Application Note for FT6x06 CTPM". we // configure the touch controller to use "interrupt trigger mode". - uint32_t event = __HAL_GPIO_EXTI_GET_FLAG(GPIO_PIN_4); + uint32_t event = __HAL_GPIO_EXTI_GET_FLAG(TOUCH_INT_PIN); if (event != 0) { - __HAL_GPIO_EXTI_CLEAR_FLAG(GPIO_PIN_4); + __HAL_GPIO_EXTI_CLEAR_FLAG(TOUCH_INT_PIN); } return event; @@ -239,8 +242,8 @@ uint32_t touch_read(void) { // first touch) (tested with FT6206) const uint32_t event_flag = touch_data[3] & 0xC0; if (touch_data[1] == GESTURE_NO_GESTURE) { - xy = touch_pack_xy((X_POS_MSB << 8) | X_POS_LSB, - (Y_POS_MSB << 8) | Y_POS_LSB); + xy = TRANSFORM_TOUCH_COORDS((X_POS_MSB << 8) | X_POS_LSB, + (Y_POS_MSB << 8) | Y_POS_LSB); if ((number_of_touch_points == 1) && (event_flag == EVENT_PRESS_DOWN)) { touching = 1; return TOUCH_START | xy; diff --git a/core/embed/vendorheader/T3W1/vendor_unsafe.json b/core/embed/vendorheader/T3W1/vendor_unsafe.json new file mode 100644 index 0000000000..ddc8741f1f --- /dev/null +++ b/core/embed/vendorheader/T3W1/vendor_unsafe.json @@ -0,0 +1,19 @@ +{ + "header_len": 4608, + "text": "UNSAFE, DO NOT USE!", + "hw_model": "T3W1", + "expiry": 0, + "version": [0, 1], + "sig_m": 2, + "trust": { + "show_vendor_string": true, + "require_user_click": true, + "red_background": true, + "delay": 1 + }, + "pubkeys": [ + "e28a8970753332bd72fef413e6b0b2ef1b4aadda7aa2c141f233712a6876b351", + "d4eec1869fb1b8a4e817516ad5a931557cb56805c3eb16e8f3a803d647df7869", + "772c8a442b7db06e166cfbc1ccbcbcde6f3eba76a4e98ef3ffc519502237d6ef" + ] +} diff --git a/core/embed/vendorheader/T3W1/vendor_unsafe.toif b/core/embed/vendorheader/T3W1/vendor_unsafe.toif new file mode 100644 index 0000000000000000000000000000000000000000..8c25f0ddd9f77e9e0259da329d46af4621bbb0db GIT binary patch literal 2162 zcmV-&2#xntPf1320C)gq2mk=>k}G&3OAv(-2m}Ig1p+Yw5fOnvL|{fBA`plb2*e0P zL`1}j2uuV5frvmLMj#Lo5fKQ)7(_%w1OkCTj6keFAn5Mu$4z$k+uhJbU|tE6k15$x zr_Nue5+re~V;$>Q$2!(amFa?>vvZbWW%`vlTeQG?(oyq&%26YE#Rp%E`-Y@pCe=G1 z$Wyra608|JzKD1*p6e{316yuoC8l(c-DCRF56&O&LxMV;pFctHy#1$}N z!JDUb)W5ABxSm;`Ja8NRzp3)ExakLux$nso+t!g{J&EMTK5u~ixli*`)bNV*^`&q@}VNnI%`-Gk!2L{d@ej``8LV3=0tfD+Er5t7MwVfj+C z$T##>%JeX3O_JfdKS!mQU&RHrk%X(649(AgV)|Y2IkattrMn9C-XO%%tx2*{ z_u?pPXQxXFd?xB8~T2QQO`GEBhU@)~I)(6Q$7nh^WqQyk zqP$lgcufL`i<)#p=UoEk^@4fPm~c#E=_#EPhG}GYxU?-gLiRcoh9{LO!aG+TPrL@{ zB3Ck!0~#K!fjU*H;c><@%n{x+cvERm%*z!wLgQV~CNiPQO^-Sil()^ishFgIY~NOk zkP@~?5xF3cWzYIQ7{;4oUUl+xCUr;OgcSc7zH2BeKAiR#c@#QNGHj?lXg3*qNgQ!l0KQ)(N zynek6g+i{y4aD0;G@B~L{Hfg_+w;%6piLx;q3}<8!sdu#F@U1ZTUY+o{zDVKV1D(k zC6;cVgvmRS{?+=Vj^xpkhA*`aIEY#Y5&?L1e`hj?-X-V`9d+-+W0H;}w!0wi5=7S= zieQS^4US}5g0BRU#AztHX*A)dJreGd9MU=5b62~nOyBCx zl^cp~pLCI%EUDeDt6cAEsy0iIaO&!feujso+h$E9r`sL+EjmZKzKO70C5TC$VI*Hw zd$I!t2~W9*Bs^Cw`d&{~V(B($f`nORx_GI4=&E%jt24R1BMn%(RUWRd=&H)Ggiitg z+afb0-I6j$TzCyqLh=G>ZP^Z%?h}i~Su|U`V_3r2g|;Xl>1Jj4p4=B#RbJ(HEJ?#x z&q^%emNw~uBjKt>$c{{M3iUtgb(My&b8b+K<6$G=M>JjR4uVxd+v+ofJ)?>wms5*3 z$t?l90yktQ=WMP`anvB>8*&Mj-4g!&1H1au&^Nyp*sZNXzoImf-kD6&zt^n4Pes`2 znk0wes*wzv*CkVAvwttTg6!i53)@A13onGuizj>u#?Lp4O!($oO_pi+EA zw5H8kaqIWq*IN4jiSRunkiEWsRUXBx|1_qo?EicjlbN=QB}CyNNpr|Gk4Y4>GTLru zzbB1JY2(FFl}LCK6tuX(M-AoXM?Jwe(}1bzVTwK(};KzWI-IOJ4-ZT8Z=RUcXn{T%~LE>E#zR6UR{hu#4 z8;X9cXA}g*Fo&QO& zL37$`GsX$G*<9lp$ufO^A7?U2<4D`0BdOCoaPyyko|vQmqNXB#!~=Sb(Aui~=34t? zEf8SUlTWd_>PrBLadd^X@n;%r?8leQOvOnon4efpIw(VZBi&!sMH7Dg=BoKZeZ@0; z35Qo1e#^7cesQ_&D++8sd74vS@tet;Ef;ae>@P=KR|}dT+o}Wh7o)vn+i72`0`)IL z3$nd~_O&ipPksg3tW51e`&wl>;qhjjs{&8b{b*myBqyvE+myRvKn;@cTq#x*@gjB9 o18>j-2~y{*%BKRP{i1f5)l?(d6-#&wNP4Ve9qU-fI@U+k|Kc$>ZU6uP literal 0 HcmV?d00001 diff --git a/core/embed/vendorheader/T3W1/vendorheader_unsafe_signed_dev.bin b/core/embed/vendorheader/T3W1/vendorheader_unsafe_signed_dev.bin new file mode 100644 index 0000000000000000000000000000000000000000..d18bca9311ace0083ffa13c0eb74960f8a194f28 GIT binary patch literal 4608 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 A#sB~S literal 0 HcmV?d00001 diff --git a/core/embed/vendorheader/generate.sh b/core/embed/vendorheader/generate.sh index 7dc1a264fa..31888dbbca 100755 --- a/core/embed/vendorheader/generate.sh +++ b/core/embed/vendorheader/generate.sh @@ -16,7 +16,7 @@ for arg in "$@"; do fi done -MODELS=(T2T1 T2B1 D001) +MODELS=(T2T1 T2B1 T3W1 D001) for MODEL in ${MODELS[@]}; do cd $MODEL diff --git a/core/site_scons/boards/trezor_t3w1_d1.py b/core/site_scons/boards/trezor_t3w1_d1.py new file mode 100644 index 0000000000..79eff5a920 --- /dev/null +++ b/core/site_scons/boards/trezor_t3w1_d1.py @@ -0,0 +1,90 @@ +from __future__ import annotations + +from . import get_hw_model_as_number +from .stm32f4_common import stm32f4_common_files + + +def configure( + env: dict, + features_wanted: list[str], + defines: list[str | tuple[str, str]], + sources: list[str], + paths: list[str], +) -> list[str]: + features_available: list[str] = [] + board = "trezor_t3w1_d1.h" + display = "st7789v.c" + hw_model = get_hw_model_as_number("T3W1") + hw_revision = 0 + features_available.append("disp_i8080_16bit_dw") + + mcu = "STM32F427xx" + + stm32f4_common_files(env, defines, sources, paths) + + env.get("ENV")[ + "CPU_ASFLAGS" + ] = "-mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16" + env.get("ENV")[ + "CPU_CCFLAGS" + ] = "-mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -mtune=cortex-m4 " + env.get("ENV")["RUST_TARGET"] = "thumbv7em-none-eabihf" + + defines += [mcu] + defines += [f'TREZOR_BOARD=\\"boards/{board}\\"'] + defines += [f"HW_MODEL={hw_model}"] + defines += [f"HW_REVISION={hw_revision}"] + sources += [ + "embed/models/model_T3W1_layout.c", + ] + sources += [f"embed/trezorhal/stm32f4/displays/{display}"] + sources += ["embed/trezorhal/stm32f4/backlight_pwm.c"] + sources += ["embed/trezorhal/stm32f4/displays/panels/lhs200kb-if21.c"] + features_available.append("backlight") + + if "input" in features_wanted: + sources += ["embed/lib/touch.c"] + sources += ["embed/trezorhal/stm32f4/i2c.c"] + sources += ["embed/trezorhal/stm32f4/touch/ft6x36.c"] + features_available.append("touch") + sources += ["embed/trezorhal/stm32f4/button.c"] + features_available.append("button") + + if "sd_card" in features_wanted: + sources += ["embed/trezorhal/stm32f4/sdcard.c"] + sources += ["embed/extmod/modtrezorio/ff.c"] + sources += ["embed/extmod/modtrezorio/ffunicode.c"] + features_available.append("sd_card") + + if "sd_card" in features_wanted: + sources += [ + "vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c" + ] + + if "sbu" in features_wanted: + sources += ["embed/trezorhal/stm32f4/sbu.c"] + features_available.append("sbu") + + if "usb" in features_wanted: + sources += [ + "embed/trezorhal/stm32f4/usb.c", + "embed/trezorhal/stm32f4/usbd_conf.c", + "embed/trezorhal/stm32f4/usbd_core.c", + "embed/trezorhal/stm32f4/usbd_ctlreq.c", + "embed/trezorhal/stm32f4/usbd_ioreq.c", + "vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_usb.c", + ] + features_available.append("usb") + + if "dma2d" in features_wanted: + defines += ["USE_DMA2D"] + sources += ["embed/trezorhal/stm32f4/dma2d.c"] + sources += [ + "vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma2d.c", + ] + features_available.append("dma2d") + + env.get("ENV")["TREZOR_BOARD"] = board + env.get("ENV")["MCU_TYPE"] = mcu + + return features_available diff --git a/core/site_scons/tools.py b/core/site_scons/tools.py index dccbb9f1d4..7ad16f4d55 100644 --- a/core/site_scons/tools.py +++ b/core/site_scons/tools.py @@ -3,6 +3,8 @@ from __future__ import annotations import subprocess from pathlib import Path +import subprocess + from boards import ( discovery, trezor_1, @@ -11,6 +13,7 @@ from boards import ( trezor_r_v6, trezor_r_v10, trezor_t, + trezor_t3w1_d1, ) HERE = Path(__file__).parent.resolve() @@ -56,6 +59,8 @@ def configure_board( elif model_r_version == 10: return trezor_r_v10.configure(env, features_wanted, defines, sources, paths) raise Exception("Unknown model_r_version") + elif model in ('T3W1',): + return trezor_t3w1_d1.configure(env, features_wanted, defines, sources, paths) elif model in ("DISC1",): return discovery.configure(env, features_wanted, defines, sources, paths) raise Exception("Unknown model") @@ -68,6 +73,8 @@ def get_model_identifier(model: str) -> str: return "T2T1" elif model == "R": return "T2B1" + elif model == "T3W1": + return "T3W1" elif model == "DISC1": return "D001" else: diff --git a/python/src/trezorlib/firmware/models.py b/python/src/trezorlib/firmware/models.py index e6f917360e..c032e720a2 100644 --- a/python/src/trezorlib/firmware/models.py +++ b/python/src/trezorlib/firmware/models.py @@ -27,6 +27,7 @@ class Model(Enum): T = b"T2T1" R = b"T2B1" DISC1 = b"D001" + T3W1 = b"T3W1" @classmethod def from_hw_model(cls, hw_model: t.Union["Self", bytes]) -> "Self": @@ -201,15 +202,42 @@ TREZOR_R = ModelKeys( firmware_sigs_needed=-1, ) + +TREZOR_T3W1 = ModelKeys( + production=False, + boardloader_keys=[ + bytes.fromhex(key) + for key in ( + "db995fe25169d141cab9bbba92baa01f9f2e1ece7df4cb2ac05190f37fcc1f9d", + "2152f8d19b791d24453242e15f2eab6cb7cffa7b6a5ed30097960e069881db12", + "22fc297792f0b6ffc0bfcfdb7edb0c0aa14e025a365ec0e342e86e3829cb74b6", + ) + ], + boardloader_sigs_needed=2, + bootloader_keys=[ + bytes.fromhex(key) + for key in ( + "d759793bbc13a2819a827c76adb6fba8a49aee007f49f2d0992d99b825ad2c48", + "6355691c178a8ff91007a7478afb955ef7352c63e7b25703984cf78b26e21a56", + "ee93a4f66f8d16b819bb9beb9ffccdfcdc1412e87fee6a324c2a99a1e0e67148", + ) + ], + bootloader_sigs_needed=2, + firmware_keys=(), + firmware_sigs_needed=-1, +) + TREZOR_R_DEV = TREZOR_T_DEV DISC1 = TREZOR_T_DEV DISC1_DEV = TREZOR_T_DEV +TREZOR_T3W1_DEV = TREZOR_T_DEV MODEL_MAP = { Model.ONE: TREZOR_ONE_V3, Model.T: TREZOR_T, Model.R: TREZOR_R, Model.DISC1: DISC1, + Model.T3W1: TREZOR_T3W1, } MODEL_MAP_DEV = { @@ -217,4 +245,5 @@ MODEL_MAP_DEV = { Model.T: TREZOR_T_DEV, Model.R: TREZOR_R_DEV, Model.DISC1: DISC1_DEV, + Model.T3W1: TREZOR_T3W1_DEV, } diff --git a/python/src/trezorlib/models.py b/python/src/trezorlib/models.py index f10eaabf13..87dd8db937 100644 --- a/python/src/trezorlib/models.py +++ b/python/src/trezorlib/models.py @@ -70,7 +70,16 @@ TREZOR_DISC1 = TrezorModel( default_mapping=mapping.DEFAULT_MAPPING, ) -TREZORS = {TREZOR_ONE, TREZOR_T, TREZOR_R, TREZOR_DISC1} +TREZOR_T3W1 = TrezorModel( + name="T3W1", + internal_name="T3W1", + minimum_version=(2, 1, 0), + vendors=VENDORS, + usb_ids=((0x1209, 0x53C1), (0x1209, 0x53C0)), + default_mapping=mapping.DEFAULT_MAPPING, +) + +TREZORS = {TREZOR_ONE, TREZOR_T, TREZOR_R, TREZOR_T3W1, TREZOR_DISC1} def by_name(name: Optional[str]) -> Optional[TrezorModel]: