1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-01-18 19:31:04 +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,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 <http://www.gnu.org/licenses/>.
*/
#include <stdbool.h>
#include <string.h>
#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;
}

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
#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

View File

@ -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
}

View File

@ -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();

View File

@ -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();