1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-01-31 09:50:58 +00:00

feat(core/embed): prepare backlight driver for low power mode

[no changelog]
This commit is contained in:
cepetr 2024-06-14 11:48:55 +02:00 committed by cepetr
parent 68911c972b
commit 59961984c3
5 changed files with 227 additions and 120 deletions

View File

@ -1,31 +1,234 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdbool.h>
#include <string.h>
#include "backlight_pwm.h" #include "backlight_pwm.h"
#include "common.h"
#include STM32_HAL_H #include STM32_HAL_H
#include TREZOR_BOARD #include TREZOR_BOARD
// Requested PWM Timer clock frequency [Hz]
#define TIM_FREQ 10000000 #define TIM_FREQ 10000000
// Prescaler divider for the PWM Timer
#define LED_PWM_PRESCALER (SystemCoreClock / TIM_FREQ - 1) #define LED_PWM_PRESCALER (SystemCoreClock / TIM_FREQ - 1)
// Period of the PWM Timer
#define LED_PWM_PRESCALER_SLOW (SystemCoreClock / 1000000 - 1) // 1 MHz
#define LED_PWM_TIM_PERIOD (TIM_FREQ / BACKLIGHT_PWM_FREQ) #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) { // Backlight driver instance
if (BACKLIGHT != val && val >= 0 && val <= 255) { static backlight_driver_t g_backlight_driver = {
// TPS61043 goes to shutdown when duty cycle is 0 (after 32ms), .initialized = false,
// 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) { void backlight_pwm_init(backlight_action_t action) {
backlight_driver_t *drv = &g_backlight_driver;
if (drv->initialized) {
return;
}
memset(drv, 0, sizeof(backlight_driver_t));
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};
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);
// Initialize PWM timer
uint32_t tmpcr1 = 0;
// Select the Counter Mode
tmpcr1 |= TIM_COUNTERMODE_UP;
// Set the clock division
tmpcr1 |= (uint32_t)TIM_CLOCKDIVISION_DIV1;
// Set the auto-reload preload
#ifdef STM32U5
tmpcr1 |= TIM_AUTORELOAD_PRELOAD_DISABLE;
#endif
BACKLIGHT_PWM_TIM->CR1 = tmpcr1;
// Set the Autoreload value
BACKLIGHT_PWM_TIM->ARR = (uint32_t)LED_PWM_TIM_PERIOD - 1;
// Set the Prescaler value
BACKLIGHT_PWM_TIM->PSC = LED_PWM_PRESCALER;
// 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
BACKLIGHT_PWM_TIM->EGR = TIM_EGR_UG;
// Set the Preload enable bit for channel1
BACKLIGHT_PWM_TIM->CCMR1 |= TIM_CCMR1_OC1PE;
// Configure the Output Fast mode
BACKLIGHT_PWM_TIM->CCMR1 &= ~TIM_CCMR1_OC1FE;
BACKLIGHT_PWM_TIM->CCMR1 |= TIM_OCFAST_DISABLE;
uint32_t tmpccmrx;
uint32_t tmpccer;
uint32_t tmpcr2;
// Get the TIMx CCER register value
tmpccer = BACKLIGHT_PWM_TIM->CCER;
// 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
tmpcr2 = BACKLIGHT_PWM_TIM->CR2;
// Get the TIMx CCMR1 register value
tmpccmrx = BACKLIGHT_PWM_TIM->CCMR1;
// Reset the Output Compare Mode Bits
tmpccmrx &= ~TIM_CCMR1_OC1M;
tmpccmrx &= ~TIM_CCMR1_CC1S;
// Select the Output Compare Mode
tmpccmrx |= BACKLIGHT_PWM_TIM_OCMODE;
// Reset the Output Polarity level
tmpccer &= ~TIM_CCER_CC1P;
// Set the Output Compare Polarity
tmpccer |= TIM_OCPOLARITY_HIGH;
if (IS_TIM_CCXN_INSTANCE(BACKLIGHT_PWM_TIM, TIM_CHANNEL_1)) {
// Check parameters
assert_param(IS_TIM_OCN_POLARITY(OC_Config->OCNPolarity));
// Reset the Output N Polarity level
tmpccer &= ~TIM_CCER_CC1NP;
// Set the Output N Polarity
tmpccer |= TIM_OCNPOLARITY_HIGH;
// Set the Output N State
tmpccer |= TIM_CCER_CC1NE;
}
if (IS_TIM_BREAK_INSTANCE(BACKLIGHT_PWM_TIM)) {
// 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
tmpcr2 &= ~TIM_CR2_OIS1;
tmpcr2 &= ~TIM_CR2_OIS1N;
// Set the Output Idle state
tmpcr2 |= TIM_OCIDLESTATE_SET;
// Set the Output N Idle state
tmpcr2 |= TIM_OCNIDLESTATE_SET;
}
// Write to TIMx CR2
BACKLIGHT_PWM_TIM->CR2 = tmpcr2;
// Write to TIMx CCMR1
BACKLIGHT_PWM_TIM->CCMR1 = tmpccmrx;
// Set the Capture Compare Register value
BACKLIGHT_PWM_TIM->CCR1 = 0;
// Write to TIMx CCER
BACKLIGHT_PWM_TIM->CCER = tmpccer;
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_deinit(backlight_action_t action) {
backlight_driver_t *drv = &g_backlight_driver;
if (!drv->initialized) {
return;
}
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.
// about 10Hz (with PSC = (SystemCoreClock / 1000000) - 1)
#define LED_PWM_SLOW_TIM_PERIOD (10000)
#define LED_PWM_PRESCALER_SLOW (SystemCoreClock / 1000000 - 1) // 1 MHz
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}; GPIO_InitTypeDef GPIO_InitStructure = {0};
HAL_GPIO_WritePin(BACKLIGHT_PWM_PORT, BACKLIGHT_PWM_PIN, GPIO_PIN_SET); 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.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pull = GPIO_NOPULL; GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
@ -42,171 +245,35 @@ int backlight_pwm_set(int val) {
HAL_GPIO_Init(BACKLIGHT_PWM_PORT, &GPIO_InitStructure); HAL_GPIO_Init(BACKLIGHT_PWM_PORT, &GPIO_InitStructure);
} }
BACKLIGHT = val; int backlight_pwm_set(int level) {
BACKLIGHT_PWM_TIM->CCR1 = (pwm_period * val) / 255; backlight_driver_t *drv = &g_backlight_driver;
}
return BACKLIGHT; if (!drv->initialized) {
return 0;
} }
int backlight_pwm_get(void) { return BACKLIGHT; } if (level >= 0 && level <= 255) {
// TPS61043 goes to shutdown when duty cycle is 0 (after 32ms),
void backlight_pwm_init(void) { // so we need to set GPIO to high for at least 500us
// init peripherals // to wake it up.
BACKLIGHT_PWM_PORT_CLK_EN(); if (BACKLIGHT_PWM_TIM->BACKLIGHT_PWM_TIM_CCR == 0 && level != 0) {
BACKLIGHT_PWM_TIM_CLK_EN(); backlight_pwm_wakeup_pulse();
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;
GPIO_InitStructure.Alternate = BACKLIGHT_PWM_TIM_AF;
GPIO_InitStructure.Pin = BACKLIGHT_PWM_PIN;
HAL_GPIO_Init(BACKLIGHT_PWM_PORT, &GPIO_InitStructure);
uint32_t tmpcr1 = 0;
/* Select the Counter Mode */
tmpcr1 |= TIM_COUNTERMODE_UP;
/* Set the clock division */
tmpcr1 |= (uint32_t)TIM_CLOCKDIVISION_DIV1;
/* Set the auto-reload preload */
#ifdef STM32U5
tmpcr1 |= TIM_AUTORELOAD_PRELOAD_DISABLE;
#endif
BACKLIGHT_PWM_TIM->CR1 = tmpcr1;
/* Set the Autoreload value */
BACKLIGHT_PWM_TIM->ARR = (uint32_t)LED_PWM_TIM_PERIOD - 1;
/* Set the Prescaler value */
BACKLIGHT_PWM_TIM->PSC = LED_PWM_PRESCALER;
/* 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 */
BACKLIGHT_PWM_TIM->EGR = TIM_EGR_UG;
pwm_period = LED_PWM_TIM_PERIOD;
/* Set the Preload enable bit for channel1 */
BACKLIGHT_PWM_TIM->CCMR1 |= TIM_CCMR1_OC1PE;
/* Configure the Output Fast mode */
BACKLIGHT_PWM_TIM->CCMR1 &= ~TIM_CCMR1_OC1FE;
BACKLIGHT_PWM_TIM->CCMR1 |= TIM_OCFAST_DISABLE;
uint32_t tmpccmrx;
uint32_t tmpccer;
uint32_t tmpcr2;
/* Get the TIMx CCER register value */
tmpccer = BACKLIGHT_PWM_TIM->CCER;
/* 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 */
tmpcr2 = BACKLIGHT_PWM_TIM->CR2;
/* Get the TIMx CCMR1 register value */
tmpccmrx = BACKLIGHT_PWM_TIM->CCMR1;
/* Reset the Output Compare Mode Bits */
tmpccmrx &= ~TIM_CCMR1_OC1M;
tmpccmrx &= ~TIM_CCMR1_CC1S;
/* Select the Output Compare Mode */
tmpccmrx |= BACKLIGHT_PWM_TIM_OCMODE;
/* Reset the Output Polarity level */
tmpccer &= ~TIM_CCER_CC1P;
/* Set the Output Compare Polarity */
tmpccer |= TIM_OCPOLARITY_HIGH;
if (IS_TIM_CCXN_INSTANCE(BACKLIGHT_PWM_TIM, TIM_CHANNEL_1)) {
/* Check parameters */
assert_param(IS_TIM_OCN_POLARITY(OC_Config->OCNPolarity));
/* Reset the Output N Polarity level */
tmpccer &= ~TIM_CCER_CC1NP;
/* Set the Output N Polarity */
tmpccer |= TIM_OCNPOLARITY_HIGH;
/* Set the Output N State */
tmpccer |= TIM_CCER_CC1NE;
} }
if (IS_TIM_BREAK_INSTANCE(BACKLIGHT_PWM_TIM)) { BACKLIGHT_PWM_TIM->CCR1 = (LED_PWM_TIM_PERIOD * level) / 255;
/* 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 */ drv->current_level = level;
tmpcr2 &= ~TIM_CR2_OIS1;
tmpcr2 &= ~TIM_CR2_OIS1N;
/* Set the Output Idle state */
tmpcr2 |= TIM_OCIDLESTATE_SET;
/* Set the Output N Idle state */
tmpcr2 |= TIM_OCNIDLESTATE_SET;
} }
/* Write to TIMx CR2 */ return drv->current_level;
BACKLIGHT_PWM_TIM->CR2 = tmpcr2;
/* Write to TIMx CCMR1 */
BACKLIGHT_PWM_TIM->CCMR1 = tmpccmrx;
/* Set the Capture Compare Register value */
BACKLIGHT_PWM_TIM->CCR1 = 0;
/* 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;
} }
void backlight_pwm_reinit(void) { int backlight_pwm_get(void) {
uint32_t prev_arr = BACKLIGHT_PWM_TIM->ARR; backlight_driver_t *drv = &g_backlight_driver;
uint32_t prev_ccr1 = BACKLIGHT_PWM_TIM->BACKLIGHT_PWM_TIM_CCR;
uint32_t prev_val = (prev_ccr1 * 255) / (prev_arr + 1); if (!drv->initialized) {
prev_val = prev_val > 255 ? 255 : prev_val; return 0;
BACKLIGHT = prev_val;
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;
} }
#ifdef TREZOR_MODEL_T return drv->current_level;
#define LED_PWM_SLOW_TIM_PERIOD \
(10000) // about 10Hz (with PSC = (SystemCoreClock / 1000000) - 1)
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;
} }
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef CORE_BACKLIGHT_H #ifndef TREZORHAL_BACKLIGHT_H
#define CORE_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); 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); int backlight_pwm_get(void);
void backlight_pwm_init(void); #endif // TREZORHAL_BACKLIGHT_H
void backlight_pwm_reinit(void);
void backlight_pwm_set_slow(void);
#endif // CORE_BACKLIGHT_H

View File

@ -129,7 +129,7 @@ void ensure_compatible_settings(void) {
#endif #endif
display_orientation(0); display_orientation(0);
set_core_clock(CLOCK_168_MHZ); set_core_clock(CLOCK_168_MHZ);
backlight_pwm_set_slow(); backlight_pwm_deinit(BACKLIGHT_RETAIN);
#endif #endif
} }

View File

@ -397,7 +397,7 @@ void display_init(void) {
__HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_FMC_CLK_ENABLE(); __HAL_RCC_FMC_CLK_ENABLE();
backlight_pwm_init(); backlight_pwm_init(BACKLIGHT_RESET);
#ifdef STM32F4 #ifdef STM32F4
#define DISPLAY_GPIO_SPEED GPIO_SPEED_FREQ_VERY_HIGH #define DISPLAY_GPIO_SPEED GPIO_SPEED_FREQ_VERY_HIGH
@ -477,7 +477,7 @@ void display_reinit(void) {
DISPLAY_ORIENTATION = 0; DISPLAY_ORIENTATION = 0;
panel_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1); panel_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1);
backlight_pwm_reinit(); backlight_pwm_init(BACKLIGHT_RETAIN);
#ifdef TREZOR_MODEL_T #ifdef TREZOR_MODEL_T
uint32_t id = display_identify(); uint32_t id = display_identify();

View File

@ -51,7 +51,7 @@ void display_init(void) {
display_io_init_fmc(); display_io_init_fmc();
display_panel_init(); display_panel_init();
display_panel_set_little_endian(); display_panel_set_little_endian();
backlight_pwm_init(); backlight_pwm_init(BACKLIGHT_RESET);
#ifdef XFRAMEBUFFER #ifdef XFRAMEBUFFER
display_io_init_te_interrupt(); display_io_init_te_interrupt();
@ -69,7 +69,7 @@ void display_reinit(void) {
// Important for model T as this is not set in boardloader // Important for model T as this is not set in boardloader
display_panel_set_little_endian(); display_panel_set_little_endian();
display_panel_init_gamma(); display_panel_init_gamma();
backlight_pwm_reinit(); backlight_pwm_init(BACKLIGHT_RETAIN);
#ifdef XFRAMEBUFFER #ifdef XFRAMEBUFFER
display_io_init_te_interrupt(); display_io_init_te_interrupt();