From 47b02df11e37d3088fb838fcd942772452926384 Mon Sep 17 00:00:00 2001
From: schneider <schneider@blinkenlichts.net>
Date: Tue, 10 Aug 2021 13:00:24 +0200
Subject: [PATCH] change(backlight): Keep PWM stable when changing PCLK

Timer frequency is now held close to a value which can be achieved both
with a 48 MHz PCLK (96 MHz system clock) and a 7.5 MHz PCLK (15 MHz
system clock).

If even lower PCLKs should be supported, the frequency of the timer has
to be changed as well.
---
 epicardium/drivers/display.c |  5 +++
 epicardium/drivers/drivers.h |  1 +
 lib/gfx/GUI_DEV/DEV_Config.c | 68 ++++++++++++++++++++++++++++++++++--
 lib/gfx/GUI_DEV/DEV_Config.h |  2 ++
 lib/gfx/LCD/LCD_Driver.c     |  8 +++++
 lib/gfx/LCD/LCD_Driver.h     |  1 +
 6 files changed, 82 insertions(+), 3 deletions(-)

diff --git a/epicardium/drivers/display.c b/epicardium/drivers/display.c
index 05b0eaec1..7cf92c7a2 100644
--- a/epicardium/drivers/display.c
+++ b/epicardium/drivers/display.c
@@ -311,6 +311,11 @@ int epic_disp_close()
 	}
 }
 
+void disp_update_backlight_clock(void)
+{
+	LCD_UpdateBacklightClock();
+}
+
 void disp_forcelock()
 {
 	TaskHandle_t task = xTaskGetCurrentTaskHandle();
diff --git a/epicardium/drivers/drivers.h b/epicardium/drivers/drivers.h
index 1f2f22fbb..047728b6c 100644
--- a/epicardium/drivers/drivers.h
+++ b/epicardium/drivers/drivers.h
@@ -61,6 +61,7 @@ int pmic_read_amux(enum pmic_amux_signal sig, float *result);
 /* ---------- Display ------------------------------------------------------ */
 /* Forces an unlock of the display. Only to be used in Epicardium */
 void disp_forcelock();
+void disp_update_backlight_clock(void);
 
 /* ---------- BHI160 ------------------------------------------------------- */
 #define BHI160_FIFO_SIZE             128
diff --git a/lib/gfx/GUI_DEV/DEV_Config.c b/lib/gfx/GUI_DEV/DEV_Config.c
index 67c73a142..68a5e5f10 100644
--- a/lib/gfx/GUI_DEV/DEV_Config.c
+++ b/lib/gfx/GUI_DEV/DEV_Config.c
@@ -70,7 +70,68 @@ void lcd_write(uint8_t *data, int size)
 #define PORT_PWM PORT_0    // port
 #define PIN_PWM PIN_28     // pin
 #define FREQ 1000          // (Hz)
+#define TIMER_FREQ 6000000 // _Target_ timer frequncy (Hz)
 #define PWM_TIMER MXC_TMR4 // must change PORT_PWM and PIN_PWM if changed
+
+/* Find a prescaler which gives us at least TIMER_FREQ HZ base clock
+ * at the current PCLK setting.
+ *
+ * Maximum prescaler chosen is 128.
+ */
+static uint8_t timer_prescale_factor(void)
+{
+	uint32_t target_prescaler = PeripheralClock / TIMER_FREQ;
+
+	if (target_prescaler == 0) {
+		printf("TIMER_FREQ to high for PeripheralClock\n");
+		while (1)
+			;
+	}
+
+	uint8_t prescaler = 1;
+	for (int i = 0; i < 7; i++) {
+		uint8_t next_prescaler = prescaler << 1;
+		if (next_prescaler > target_prescaler) {
+			break;
+		}
+		prescaler = next_prescaler;
+	}
+
+	return prescaler;
+}
+
+/* Return the constant need for the timer prescaler register to
+ * reach a least TIMER_FREQ HZ base clock frequency at the
+ * current PCLK setting */
+static uint32_t timer_prescaler(void)
+{
+	switch (timer_prescale_factor()) {
+	case 1:
+		return MXC_S_TMR_CN_PRES_DIV1;
+	case 2:
+		return MXC_S_TMR_CN_PRES_DIV2;
+	case 4:
+		return MXC_S_TMR_CN_PRES_DIV4;
+	case 8:
+		return MXC_S_TMR_CN_PRES_DIV8;
+	case 16:
+		return MXC_S_TMR_CN_PRES_DIV16;
+	case 32:
+		return MXC_S_TMR_CN_PRES_DIV32;
+	case 64:
+		return MXC_S_TMR_CN_PRES_DIV64;
+	default:
+		return MXC_S_TMR_CN_PRES_DIV128;
+	}
+}
+
+/* Update the timer prescaler to what ever PCLK currently requires */
+void DEV_Update_BL_Clock(void)
+{
+	PWM_TIMER->cn =
+		(PWM_TIMER->cn & ~(MXC_F_TMR_CN_PRES)) | timer_prescaler();
+}
+
 void DEV_Set_BL(uint16_t _Value)
 {
 	// Declare variables
@@ -82,8 +143,9 @@ void DEV_Set_BL(uint16_t _Value)
 		_Value = 100;
 	}
 
-	unsigned int period_ticks = PeripheralClock / FREQ;
-	unsigned int duty_ticks   = period_ticks * _Value / 100;
+	unsigned int period_ticks =
+		PeripheralClock / timer_prescale_factor() / FREQ;
+	unsigned int duty_ticks = period_ticks * _Value / 100;
 
 	TMR_Disable(PWM_TIMER);
 
@@ -95,7 +157,7 @@ void DEV_Set_BL(uint16_t _Value)
 		gpio_pwm.pad  = GPIO_PAD_PULL_DOWN;
 		GPIO_Config(&gpio_pwm);
 
-		TMR_Init(PWM_TIMER, TMR_PRES_1, 0);
+		TMR_Init(PWM_TIMER, timer_prescaler(), 0);
 
 		tmr.mode    = TMR_MODE_PWM;
 		tmr.cmp_cnt = period_ticks;
diff --git a/lib/gfx/GUI_DEV/DEV_Config.h b/lib/gfx/GUI_DEV/DEV_Config.h
index cbc4c5f65..321b6d9c9 100644
--- a/lib/gfx/GUI_DEV/DEV_Config.h
+++ b/lib/gfx/GUI_DEV/DEV_Config.h
@@ -74,6 +74,8 @@ void display_set_reset_pin(uint8_t state);
 
 //#define DEV_Set_BL(_Value)     DEV_BL_PIN= _Value
 void DEV_Set_BL(uint16_t _Value);
+void DEV_Update_BL(void);
+void DEV_Update_BL_Clock(void);
 /*-----------------------------------------------------------------------------*/
 
 #endif
diff --git a/lib/gfx/LCD/LCD_Driver.c b/lib/gfx/LCD/LCD_Driver.c
index 55c872a82..3ef962efa 100644
--- a/lib/gfx/LCD/LCD_Driver.c
+++ b/lib/gfx/LCD/LCD_Driver.c
@@ -56,6 +56,14 @@ void LCD_SetBacklight(UWORD Value)
 {
 	DEV_Set_BL(Value);
 }
+/*******************************************************************************
+function:
+	Update backlight clock
+*******************************************************************************/
+void LCD_UpdateBacklightClock(void)
+{
+	DEV_Update_BL_Clock();
+}
 
 /*******************************************************************************
 function:
diff --git a/lib/gfx/LCD/LCD_Driver.h b/lib/gfx/LCD/LCD_Driver.h
index 49358115b..ad6f29f24 100644
--- a/lib/gfx/LCD/LCD_Driver.h
+++ b/lib/gfx/LCD/LCD_Driver.h
@@ -46,6 +46,7 @@ void LCD_SetUWORD(UWORD x, UWORD y, UWORD Color);
 
 void LCD_Init(void);
 void LCD_SetBacklight(UWORD Value);
+void LCD_UpdateBacklightClock(void);
 void LCD_Clear(UWORD Color);
 void LCD_ClearWindow(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend, UWORD UWORD);
 uint8_t *LCD_Framebuffer(void);
-- 
GitLab