diff --git a/core/embed/trezorhal/stm32f4/backlight_pwm.c b/core/embed/trezorhal/stm32f4/backlight_pwm.c
index 7072b26f2d..a65e84e0fb 100644
--- a/core/embed/trezorhal/stm32f4/backlight_pwm.c
+++ b/core/embed/trezorhal/stm32f4/backlight_pwm.c
@@ -1,63 +1,81 @@
+/*
+ * 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 .
+ */
+
+#include
+#include
#include "backlight_pwm.h"
+#include "common.h"
#include STM32_HAL_H
#include TREZOR_BOARD
+// Requested PWM Timer clock frequency [Hz]
#define TIM_FREQ 10000000
-
+// Prescaler divider for the PWM Timer
#define LED_PWM_PRESCALER (SystemCoreClock / TIM_FREQ - 1)
-
-#define LED_PWM_PRESCALER_SLOW (SystemCoreClock / 1000000 - 1) // 1 MHz
-
+// Period of the PWM Timer
#define LED_PWM_TIM_PERIOD (TIM_FREQ / BACKLIGHT_PWM_FREQ)
-static int BACKLIGHT = -1;
+// Backlight driver state
+typedef struct {
+ // Set if driver is initialized
+ bool initialized;
+ // Current backlight level in range 0-255
+ int current_level;
-static int pwm_period = 0;
+} backlight_driver_t;
-int backlight_pwm_set(int val) {
- if (BACKLIGHT != val && val >= 0 && val <= 255) {
- // TPS61043 goes to shutdown when duty cycle is 0 (after 32ms),
- // so we need to set GPIO to high for at least 500us
- // to wake it up.
- if (BACKLIGHT_PWM_TIM->BACKLIGHT_PWM_TIM_CCR == 0) {
- GPIO_InitTypeDef GPIO_InitStructure = {0};
+// Backlight driver instance
+static backlight_driver_t g_backlight_driver = {
+ .initialized = false,
+};
- HAL_GPIO_WritePin(BACKLIGHT_PWM_PORT, BACKLIGHT_PWM_PIN, GPIO_PIN_SET);
- // LCD_PWM/PA7 (backlight control)
- GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
- GPIO_InitStructure.Pull = GPIO_NOPULL;
- GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
- GPIO_InitStructure.Pin = BACKLIGHT_PWM_PIN;
- HAL_GPIO_Init(BACKLIGHT_PWM_PORT, &GPIO_InitStructure);
+void backlight_pwm_init(backlight_action_t action) {
+ backlight_driver_t *drv = &g_backlight_driver;
- hal_delay_us(500);
-
- GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
- GPIO_InitStructure.Pull = GPIO_NOPULL;
- GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
- GPIO_InitStructure.Alternate = BACKLIGHT_PWM_TIM_AF;
- GPIO_InitStructure.Pin = BACKLIGHT_PWM_PIN;
- HAL_GPIO_Init(BACKLIGHT_PWM_PORT, &GPIO_InitStructure);
- }
-
- BACKLIGHT = val;
- BACKLIGHT_PWM_TIM->CCR1 = (pwm_period * val) / 255;
+ if (drv->initialized) {
+ return;
}
- return BACKLIGHT;
-}
-int backlight_pwm_get(void) { return BACKLIGHT; }
+ memset(drv, 0, sizeof(backlight_driver_t));
-void backlight_pwm_init(void) {
- // init peripherals
+ int initial_level = 0;
+
+ if (action == BACKLIGHT_RETAIN) {
+ // We expect the BACKLIGHT_PWM_TIM to be already initialized
+ // (e.g. by the bootloader or boardloader)
+ uint32_t prev_arr = BACKLIGHT_PWM_TIM->ARR;
+ uint32_t prev_ccr1 = BACKLIGHT_PWM_TIM->BACKLIGHT_PWM_TIM_CCR;
+
+ initial_level = (prev_ccr1 * 255) / (prev_arr + 1);
+ if (initial_level > 255) {
+ initial_level = 255;
+ }
+ }
+
+ // Enable peripheral clocks
BACKLIGHT_PWM_PORT_CLK_EN();
BACKLIGHT_PWM_TIM_CLK_EN();
+ // Initialize PWM GPIO
GPIO_InitTypeDef GPIO_InitStructure = {0};
-
- // LCD_PWM (backlight control)
GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
@@ -65,40 +83,40 @@ void backlight_pwm_init(void) {
GPIO_InitStructure.Pin = BACKLIGHT_PWM_PIN;
HAL_GPIO_Init(BACKLIGHT_PWM_PORT, &GPIO_InitStructure);
+ // Initialize PWM timer
+
uint32_t tmpcr1 = 0;
- /* Select the Counter Mode */
+ // Select the Counter Mode
tmpcr1 |= TIM_COUNTERMODE_UP;
- /* Set the clock division */
+ // Set the clock division
tmpcr1 |= (uint32_t)TIM_CLOCKDIVISION_DIV1;
- /* Set the auto-reload preload */
+ // Set the auto-reload preload
#ifdef STM32U5
tmpcr1 |= TIM_AUTORELOAD_PRELOAD_DISABLE;
#endif
BACKLIGHT_PWM_TIM->CR1 = tmpcr1;
- /* Set the Autoreload value */
+ // Set the Autoreload value
BACKLIGHT_PWM_TIM->ARR = (uint32_t)LED_PWM_TIM_PERIOD - 1;
- /* Set the Prescaler value */
+ // Set the Prescaler value
BACKLIGHT_PWM_TIM->PSC = LED_PWM_PRESCALER;
- /* Set the Repetition Counter value */
+ // Set the Repetition Counter value
BACKLIGHT_PWM_TIM->RCR = 0;
- /* Generate an update event to reload the Prescaler
- and the repetition counter (only for advanced timer) value immediately */
+ // Generate an update event to reload the Prescaler
+ // and the repetition counter (only for advanced timer) value immediately
BACKLIGHT_PWM_TIM->EGR = TIM_EGR_UG;
- pwm_period = LED_PWM_TIM_PERIOD;
-
- /* Set the Preload enable bit for channel1 */
+ // Set the Preload enable bit for channel1
BACKLIGHT_PWM_TIM->CCMR1 |= TIM_CCMR1_OC1PE;
- /* Configure the Output Fast mode */
+ // Configure the Output Fast mode
BACKLIGHT_PWM_TIM->CCMR1 &= ~TIM_CCMR1_OC1FE;
BACKLIGHT_PWM_TIM->CCMR1 |= TIM_OCFAST_DISABLE;
@@ -106,107 +124,156 @@ void backlight_pwm_init(void) {
uint32_t tmpccer;
uint32_t tmpcr2;
- /* Get the TIMx CCER register value */
+ // Get the TIMx CCER register value
tmpccer = BACKLIGHT_PWM_TIM->CCER;
- /* Disable the Channel 1: Reset the CC1E Bit */
+ // Disable the Channel 1: Reset the CC1E Bit
BACKLIGHT_PWM_TIM->CCER &= ~TIM_CCER_CC1E;
tmpccer |= TIM_CCER_CC1E;
- /* Get the TIMx CR2 register value */
+ // Get the TIMx CR2 register value
tmpcr2 = BACKLIGHT_PWM_TIM->CR2;
- /* Get the TIMx CCMR1 register value */
+ // Get the TIMx CCMR1 register value
tmpccmrx = BACKLIGHT_PWM_TIM->CCMR1;
- /* Reset the Output Compare Mode Bits */
+ // Reset the Output Compare Mode Bits
tmpccmrx &= ~TIM_CCMR1_OC1M;
tmpccmrx &= ~TIM_CCMR1_CC1S;
- /* Select the Output Compare Mode */
+ // Select the Output Compare Mode
tmpccmrx |= BACKLIGHT_PWM_TIM_OCMODE;
- /* Reset the Output Polarity level */
+ // Reset the Output Polarity level
tmpccer &= ~TIM_CCER_CC1P;
- /* Set the Output Compare Polarity */
+ // Set the Output Compare Polarity
tmpccer |= TIM_OCPOLARITY_HIGH;
if (IS_TIM_CCXN_INSTANCE(BACKLIGHT_PWM_TIM, TIM_CHANNEL_1)) {
- /* Check parameters */
+ // Check parameters
assert_param(IS_TIM_OCN_POLARITY(OC_Config->OCNPolarity));
- /* Reset the Output N Polarity level */
+ // Reset the Output N Polarity level
tmpccer &= ~TIM_CCER_CC1NP;
- /* Set the Output N Polarity */
+ // Set the Output N Polarity
tmpccer |= TIM_OCNPOLARITY_HIGH;
- /* Set the Output N State */
+ // Set the Output N State
tmpccer |= TIM_CCER_CC1NE;
}
if (IS_TIM_BREAK_INSTANCE(BACKLIGHT_PWM_TIM)) {
- /* Check parameters */
+ // Check parameters
assert_param(IS_TIM_OCNIDLE_STATE(OC_Config->OCNIdleState));
assert_param(IS_TIM_OCIDLE_STATE(OC_Config->OCIdleState));
- /* Reset the Output Compare and Output Compare N IDLE State */
+ // Reset the Output Compare and Output Compare N IDLE State
tmpcr2 &= ~TIM_CR2_OIS1;
tmpcr2 &= ~TIM_CR2_OIS1N;
- /* Set the Output Idle state */
+ // Set the Output Idle state
tmpcr2 |= TIM_OCIDLESTATE_SET;
- /* Set the Output N Idle state */
+ // Set the Output N Idle state
tmpcr2 |= TIM_OCNIDLESTATE_SET;
}
- /* Write to TIMx CR2 */
+ // Write to TIMx CR2
BACKLIGHT_PWM_TIM->CR2 = tmpcr2;
-
- /* Write to TIMx CCMR1 */
+ // Write to TIMx CCMR1
BACKLIGHT_PWM_TIM->CCMR1 = tmpccmrx;
-
- /* Set the Capture Compare Register value */
+ // Set the Capture Compare Register value
BACKLIGHT_PWM_TIM->CCR1 = 0;
-
- /* Write to TIMx CCER */
+ // Write to TIMx CCER
BACKLIGHT_PWM_TIM->CCER = tmpccer;
- backlight_pwm_set(0);
-
BACKLIGHT_PWM_TIM->BDTR |= TIM_BDTR_MOE;
BACKLIGHT_PWM_TIM->CR1 |= TIM_CR1_CEN;
+
+ drv->initialized = true;
+
+ backlight_pwm_set(initial_level);
}
-void backlight_pwm_reinit(void) {
- uint32_t prev_arr = BACKLIGHT_PWM_TIM->ARR;
- uint32_t prev_ccr1 = BACKLIGHT_PWM_TIM->BACKLIGHT_PWM_TIM_CCR;
+void backlight_pwm_deinit(backlight_action_t action) {
+ backlight_driver_t *drv = &g_backlight_driver;
- uint32_t prev_val = (prev_ccr1 * 255) / (prev_arr + 1);
- prev_val = prev_val > 255 ? 255 : prev_val;
- BACKLIGHT = prev_val;
+ if (!drv->initialized) {
+ return;
+ }
- pwm_period = LED_PWM_TIM_PERIOD;
- BACKLIGHT_PWM_TIM->PSC = LED_PWM_PRESCALER;
- BACKLIGHT_PWM_TIM->CR1 |= TIM_CR1_ARPE;
- BACKLIGHT_PWM_TIM->CR2 |= TIM_CR2_CCPC;
- BACKLIGHT_PWM_TIM->BACKLIGHT_PWM_TIM_CCR = (pwm_period * prev_val) / 255;
- BACKLIGHT_PWM_TIM->ARR = LED_PWM_TIM_PERIOD - 1;
-}
+ if (action == BACKLIGHT_RETAIN) {
+ // We keep both the GPIO and the timer running
#ifdef TREZOR_MODEL_T
+ // This code here is for backward compatibility with the old
+ // bootloader that used a different PWM settings.
-#define LED_PWM_SLOW_TIM_PERIOD \
- (10000) // about 10Hz (with PSC = (SystemCoreClock / 1000000) - 1)
+// about 10Hz (with PSC = (SystemCoreClock / 1000000) - 1)
+#define LED_PWM_SLOW_TIM_PERIOD (10000)
+#define LED_PWM_PRESCALER_SLOW (SystemCoreClock / 1000000 - 1) // 1 MHz
-void backlight_pwm_set_slow(void) {
- uint32_t prev_arr = BACKLIGHT_PWM_TIM->ARR;
- uint32_t prev_ccr1 = BACKLIGHT_PWM_TIM->CCR1;
-
- uint32_t prev_val = (prev_ccr1 * 255) / (prev_arr + 1);
- prev_val = prev_val > 255 ? 255 : prev_val;
-
- pwm_period = LED_PWM_SLOW_TIM_PERIOD;
- BACKLIGHT_PWM_TIM->PSC = LED_PWM_PRESCALER_SLOW;
- BACKLIGHT_PWM_TIM->CR1 |= TIM_CR1_ARPE;
- BACKLIGHT_PWM_TIM->CR2 |= TIM_CR2_CCPC;
- BACKLIGHT_PWM_TIM->ARR = LED_PWM_SLOW_TIM_PERIOD - 1;
- BACKLIGHT_PWM_TIM->BACKLIGHT_PWM_TIM_CCR = pwm_period * prev_val / 255;
-}
+ BACKLIGHT_PWM_TIM->PSC = LED_PWM_PRESCALER_SLOW;
+ BACKLIGHT_PWM_TIM->CR1 |= TIM_CR1_ARPE;
+ BACKLIGHT_PWM_TIM->CR2 |= TIM_CR2_CCPC;
+ BACKLIGHT_PWM_TIM->ARR = LED_PWM_SLOW_TIM_PERIOD - 1;
+ BACKLIGHT_PWM_TIM->BACKLIGHT_PWM_TIM_CCR =
+ (LED_PWM_SLOW_TIM_PERIOD * drv->current_level) / 255;
#endif
+ } else {
+ // TODO: deinitialize GPIOs and the TIMER
+ }
+
+ drv->initialized = false;
+}
+
+// Generate a pulse on the backlight control pin to wake up the TPS61043
+static void backlight_pwm_wakeup_pulse(void) {
+ GPIO_InitTypeDef GPIO_InitStructure = {0};
+
+ HAL_GPIO_WritePin(BACKLIGHT_PWM_PORT, BACKLIGHT_PWM_PIN, GPIO_PIN_SET);
+
+ GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
+ GPIO_InitStructure.Pull = GPIO_NOPULL;
+ GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+ GPIO_InitStructure.Pin = BACKLIGHT_PWM_PIN;
+ HAL_GPIO_Init(BACKLIGHT_PWM_PORT, &GPIO_InitStructure);
+
+ hal_delay_us(500);
+
+ GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+ GPIO_InitStructure.Pull = GPIO_NOPULL;
+ GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+ GPIO_InitStructure.Alternate = BACKLIGHT_PWM_TIM_AF;
+ GPIO_InitStructure.Pin = BACKLIGHT_PWM_PIN;
+ HAL_GPIO_Init(BACKLIGHT_PWM_PORT, &GPIO_InitStructure);
+}
+
+int backlight_pwm_set(int level) {
+ backlight_driver_t *drv = &g_backlight_driver;
+
+ if (!drv->initialized) {
+ return 0;
+ }
+
+ if (level >= 0 && level <= 255) {
+ // TPS61043 goes to shutdown when duty cycle is 0 (after 32ms),
+ // so we need to set GPIO to high for at least 500us
+ // to wake it up.
+ if (BACKLIGHT_PWM_TIM->BACKLIGHT_PWM_TIM_CCR == 0 && level != 0) {
+ backlight_pwm_wakeup_pulse();
+ }
+
+ BACKLIGHT_PWM_TIM->CCR1 = (LED_PWM_TIM_PERIOD * level) / 255;
+
+ drv->current_level = level;
+ }
+
+ return drv->current_level;
+}
+
+int backlight_pwm_get(void) {
+ backlight_driver_t *drv = &g_backlight_driver;
+
+ if (!drv->initialized) {
+ return 0;
+ }
+
+ return drv->current_level;
+}
diff --git a/core/embed/trezorhal/stm32f4/backlight_pwm.h b/core/embed/trezorhal/stm32f4/backlight_pwm.h
index 696c129d01..49f7c808c0 100644
--- a/core/embed/trezorhal/stm32f4/backlight_pwm.h
+++ b/core/embed/trezorhal/stm32f4/backlight_pwm.h
@@ -1,17 +1,57 @@
+/*
+ * 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 .
+ */
-#ifndef CORE_BACKLIGHT_H
-#define CORE_BACKLIGHT_H
+#ifndef TREZORHAL_BACKLIGHT_H
+#define TREZORHAL_BACKLIGHT_H
-#include "common.h"
+// Action to be taken when initializing or
+// deinitializing the backlight driver
+typedef enum {
+ BACKLIGHT_RESET,
+ BACKLIGHT_RETAIN,
+} backlight_action_t;
+// Initialize the backlight driver
+//
+// If the action is set to `BACKLIGHT_RESET`, the backlight level
+// is set to zero level. If the action is set to `BACKLIGHT_RETAIN`,
+// the backlight level is not changed (if possible).
+void backlight_pwm_init(backlight_action_t action);
+
+// Deinitialize the backlight driver
+//
+// If the action is set to `BACKLIGHT_RESET`, the backlight driver
+// is completely deinitialized. If the action is set to `BACKLIGHT_RETAIN`,
+// the driver is deinitialized as much as possible but the backlight
+// is kept on.
+void backlight_pwm_deinit(backlight_action_t action);
+
+// Sets the backlight level in range 0-255 and returns the actual level set.
+//
+// If the level is outside the range, the function has no effect
+// and just returns the actual level set. If the backlight driver
+// is not initialized, the function returns 0.
int backlight_pwm_set(int val);
+// Gets the backlight level in range 0-255
+//
+// Returns 0 if the backlight driver is not initialized.
int backlight_pwm_get(void);
-void backlight_pwm_init(void);
-
-void backlight_pwm_reinit(void);
-
-void backlight_pwm_set_slow(void);
-
-#endif // CORE_BACKLIGHT_H
+#endif // TREZORHAL_BACKLIGHT_H
diff --git a/core/embed/trezorhal/stm32f4/common.c b/core/embed/trezorhal/stm32f4/common.c
index 70e4828c7b..88c397c27c 100644
--- a/core/embed/trezorhal/stm32f4/common.c
+++ b/core/embed/trezorhal/stm32f4/common.c
@@ -129,7 +129,7 @@ void ensure_compatible_settings(void) {
#endif
display_orientation(0);
set_core_clock(CLOCK_168_MHZ);
- backlight_pwm_set_slow();
+ backlight_pwm_deinit(BACKLIGHT_RETAIN);
#endif
}
diff --git a/core/embed/trezorhal/stm32f4/displays/st7789v.c b/core/embed/trezorhal/stm32f4/displays/st7789v.c
index b0b03d9e1c..4e3c0fb99c 100644
--- a/core/embed/trezorhal/stm32f4/displays/st7789v.c
+++ b/core/embed/trezorhal/stm32f4/displays/st7789v.c
@@ -397,7 +397,7 @@ void display_init(void) {
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_FMC_CLK_ENABLE();
- backlight_pwm_init();
+ backlight_pwm_init(BACKLIGHT_RESET);
#ifdef STM32F4
#define DISPLAY_GPIO_SPEED GPIO_SPEED_FREQ_VERY_HIGH
@@ -477,7 +477,7 @@ void display_reinit(void) {
DISPLAY_ORIENTATION = 0;
panel_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1);
- backlight_pwm_reinit();
+ backlight_pwm_init(BACKLIGHT_RETAIN);
#ifdef TREZOR_MODEL_T
uint32_t id = display_identify();
diff --git a/core/embed/trezorhal/stm32f4/xdisplay/st-7789/display_driver.c b/core/embed/trezorhal/stm32f4/xdisplay/st-7789/display_driver.c
index 74a49e4f1a..e68851858f 100644
--- a/core/embed/trezorhal/stm32f4/xdisplay/st-7789/display_driver.c
+++ b/core/embed/trezorhal/stm32f4/xdisplay/st-7789/display_driver.c
@@ -51,7 +51,7 @@ void display_init(void) {
display_io_init_fmc();
display_panel_init();
display_panel_set_little_endian();
- backlight_pwm_init();
+ backlight_pwm_init(BACKLIGHT_RESET);
#ifdef XFRAMEBUFFER
display_io_init_te_interrupt();
@@ -69,7 +69,7 @@ void display_reinit(void) {
// Important for model T as this is not set in boardloader
display_panel_set_little_endian();
display_panel_init_gamma();
- backlight_pwm_reinit();
+ backlight_pwm_init(BACKLIGHT_RETAIN);
#ifdef XFRAMEBUFFER
display_io_init_te_interrupt();