diff mbox series

[U-Boot,3/3] pwm: sunxi: choose best prescaler to improve PWM resolution

Message ID 20181017045635.9857-3-anarsoul@gmail.com
State Accepted
Commit c33ba7ec8b9be0a6fe7a2bd47a36c991bf9189e1
Delegated to: Jagannadha Sutradharudu Teki
Headers show
Series [U-Boot,1/3] pwm: sunxi: fix off-by-one that prevented PWM to use prescaler bypass | expand

Commit Message

Vasily Khoruzhick Oct. 17, 2018, 4:56 a.m. UTC
Choose best prescaler to improve PWM resolution. Without this change
driver chooses first prescaler that gives us period value within
range, but it could be not the best one.

Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
---
 drivers/pwm/sunxi_pwm.c | 32 +++++++++++++++++++-------------
 1 file changed, 19 insertions(+), 13 deletions(-)

Comments

Vagrant Cascadian Oct. 18, 2018, 4:34 p.m. UTC | #1
On 2018-10-16, Vasily Khoruzhick wrote:
> Choose best prescaler to improve PWM resolution. Without this change
> driver chooses first prescaler that gives us period value within
> range, but it could be not the best one.
>
> Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>

Fixes LCD backlight issues with the pinebook series:

  https://patchwork.ozlabs.org/project/uboot/list/?series=71358

Tested-by: Vagrant Cascadian <vagrant@debian.org>

> ---
>  drivers/pwm/sunxi_pwm.c | 32 +++++++++++++++++++-------------
>  1 file changed, 19 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/pwm/sunxi_pwm.c b/drivers/pwm/sunxi_pwm.c
> index 6284409b4f..8a55e4f461 100644
> --- a/drivers/pwm/sunxi_pwm.c
> +++ b/drivers/pwm/sunxi_pwm.c
> @@ -67,49 +67,55 @@ static int sunxi_pwm_set_config(struct udevice *dev, uint channel,
>  {
>  	struct sunxi_pwm_priv *priv = dev_get_priv(dev);
>  	struct sunxi_pwm *regs = priv->regs;
> -	int prescaler;
> -	u32 v, period = 0, duty;
> -	u64 scaled_freq = 0;
> +	int best_prescaler = 0;
> +	u32 v, best_period = 0, duty;
> +	u64 best_scaled_freq = 0;
>  	const u32 nsecs_per_sec = 1000000000U;
>  
>  	debug("%s: period_ns=%u, duty_ns=%u\n", __func__, period_ns, duty_ns);
>  
> -	for (prescaler = 0; prescaler <= SUNXI_PWM_CTRL_PRESCALE0_MASK;
> +	for (int prescaler = 0; prescaler <= SUNXI_PWM_CTRL_PRESCALE0_MASK;
>  	     prescaler++) {
> +		u32 period = 0;
> +		u64 scaled_freq = 0;
>  		if (!prescaler_table[prescaler])
>  			continue;
>  		scaled_freq = lldiv(OSC_24MHZ, prescaler_table[prescaler]);
>  		period = lldiv(scaled_freq * period_ns, nsecs_per_sec);
> -		if (period - 1 <= SUNXI_PWM_CH0_PERIOD_MAX)
> -			break;
> +		if ((period - 1 <= SUNXI_PWM_CH0_PERIOD_MAX) &&
> +		    best_period < period) {
> +			best_period = period;
> +			best_scaled_freq = scaled_freq;
> +			best_prescaler = prescaler;
> +		}
>  	}
>  
> -	if (period - 1 > SUNXI_PWM_CH0_PERIOD_MAX) {
> +	if (best_period - 1 > SUNXI_PWM_CH0_PERIOD_MAX) {
>  		debug("%s: failed to find prescaler value\n", __func__);
>  		return -EINVAL;
>  	}
>  
> -	duty = lldiv(scaled_freq * duty_ns, nsecs_per_sec);
> +	duty = lldiv(best_scaled_freq * duty_ns, nsecs_per_sec);
>  
> -	if (priv->prescaler != prescaler) {
> +	if (priv->prescaler != best_prescaler) {
>  		/* Mask clock to update prescaler */
>  		v = readl(&regs->ctrl);
>  		v &= ~SUNXI_PWM_CTRL_CLK_GATE;
>  		writel(v, &regs->ctrl);
>  		v &= ~SUNXI_PWM_CTRL_PRESCALE0_MASK;
> -		v |= (prescaler & SUNXI_PWM_CTRL_PRESCALE0_MASK);
> +		v |= (best_prescaler & SUNXI_PWM_CTRL_PRESCALE0_MASK);
>  		writel(v, &regs->ctrl);
>  		v |= SUNXI_PWM_CTRL_CLK_GATE;
>  		writel(v, &regs->ctrl);
> -		priv->prescaler = prescaler;
> +		priv->prescaler = best_prescaler;
>  	}
>  
> -	writel(SUNXI_PWM_CH0_PERIOD_PRD(period) |
> +	writel(SUNXI_PWM_CH0_PERIOD_PRD(best_period) |
>  	       SUNXI_PWM_CH0_PERIOD_DUTY(duty), &regs->ch0_period);
>  
>  	debug("%s: prescaler: %d, period: %d, duty: %d\n",
>  	      __func__, priv->prescaler,
> -	      period, duty);
> +	      best_period, duty);
>  
>  	return 0;
>  }
Jagan Teki Oct. 22, 2018, 6:29 p.m. UTC | #2
On Wed, Oct 17, 2018 at 10:28 AM Vasily Khoruzhick <anarsoul@gmail.com> wrote:
>
> Choose best prescaler to improve PWM resolution. Without this change
> driver chooses first prescaler that gives us period value within
> range, but it could be not the best one.

This commit need to elaborate about the real issue, ie got fixed.
Understand it has chosen best prescaler but why and how it.
Vasily Khoruzhick Oct. 22, 2018, 6:35 p.m. UTC | #3
On Mon, Oct 22, 2018 at 11:29 AM Jagan Teki <jagan@amarulasolutions.com> wrote:
>
> On Wed, Oct 17, 2018 at 10:28 AM Vasily Khoruzhick <anarsoul@gmail.com> wrote:
> >
> > Choose best prescaler to improve PWM resolution. Without this change
> > driver chooses first prescaler that gives us period value within
> > range, but it could be not the best one.
>
> This commit need to elaborate about the real issue, ie got fixed.
> Understand it has chosen best prescaler but why and how it.

If PWM resolution is low then close backlight levels can result in the
same PWM settings, i.e. 0% and 10% can be the same (and thus backlight
remains powered off). Basically the patch does what comment says - it
improves PWM resolution.
Jagan Teki Oct. 24, 2018, 4:28 p.m. UTC | #4
On Wed, Oct 17, 2018 at 10:28 AM Vasily Khoruzhick <anarsoul@gmail.com> wrote:
>
> Choose best prescaler to improve PWM resolution. Without this change
> driver chooses first prescaler that gives us period value within
> range, but it could be not the best one.
>
> Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
> ---

Applied to all to u-boot-sunxi/master
Vasily Khoruzhick Oct. 24, 2018, 5:07 p.m. UTC | #5
I think this series already went into Anatolij's
u-boot-staging/agust@denx.de, please make sure that you don't
introduce any conflicts.On Wed, Oct 24, 2018 at 9:28 AM Jagan Teki
<jagan@amarulasolutions.com> wrote:
>
> On Wed, Oct 17, 2018 at 10:28 AM Vasily Khoruzhick <anarsoul@gmail.com> wrote:
> >
> > Choose best prescaler to improve PWM resolution. Without this change
> > driver chooses first prescaler that gives us period value within
> > range, but it could be not the best one.
> >
> > Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
> > ---
>
> Applied to all to u-boot-sunxi/master
diff mbox series

Patch

diff --git a/drivers/pwm/sunxi_pwm.c b/drivers/pwm/sunxi_pwm.c
index 6284409b4f..8a55e4f461 100644
--- a/drivers/pwm/sunxi_pwm.c
+++ b/drivers/pwm/sunxi_pwm.c
@@ -67,49 +67,55 @@  static int sunxi_pwm_set_config(struct udevice *dev, uint channel,
 {
 	struct sunxi_pwm_priv *priv = dev_get_priv(dev);
 	struct sunxi_pwm *regs = priv->regs;
-	int prescaler;
-	u32 v, period = 0, duty;
-	u64 scaled_freq = 0;
+	int best_prescaler = 0;
+	u32 v, best_period = 0, duty;
+	u64 best_scaled_freq = 0;
 	const u32 nsecs_per_sec = 1000000000U;
 
 	debug("%s: period_ns=%u, duty_ns=%u\n", __func__, period_ns, duty_ns);
 
-	for (prescaler = 0; prescaler <= SUNXI_PWM_CTRL_PRESCALE0_MASK;
+	for (int prescaler = 0; prescaler <= SUNXI_PWM_CTRL_PRESCALE0_MASK;
 	     prescaler++) {
+		u32 period = 0;
+		u64 scaled_freq = 0;
 		if (!prescaler_table[prescaler])
 			continue;
 		scaled_freq = lldiv(OSC_24MHZ, prescaler_table[prescaler]);
 		period = lldiv(scaled_freq * period_ns, nsecs_per_sec);
-		if (period - 1 <= SUNXI_PWM_CH0_PERIOD_MAX)
-			break;
+		if ((period - 1 <= SUNXI_PWM_CH0_PERIOD_MAX) &&
+		    best_period < period) {
+			best_period = period;
+			best_scaled_freq = scaled_freq;
+			best_prescaler = prescaler;
+		}
 	}
 
-	if (period - 1 > SUNXI_PWM_CH0_PERIOD_MAX) {
+	if (best_period - 1 > SUNXI_PWM_CH0_PERIOD_MAX) {
 		debug("%s: failed to find prescaler value\n", __func__);
 		return -EINVAL;
 	}
 
-	duty = lldiv(scaled_freq * duty_ns, nsecs_per_sec);
+	duty = lldiv(best_scaled_freq * duty_ns, nsecs_per_sec);
 
-	if (priv->prescaler != prescaler) {
+	if (priv->prescaler != best_prescaler) {
 		/* Mask clock to update prescaler */
 		v = readl(&regs->ctrl);
 		v &= ~SUNXI_PWM_CTRL_CLK_GATE;
 		writel(v, &regs->ctrl);
 		v &= ~SUNXI_PWM_CTRL_PRESCALE0_MASK;
-		v |= (prescaler & SUNXI_PWM_CTRL_PRESCALE0_MASK);
+		v |= (best_prescaler & SUNXI_PWM_CTRL_PRESCALE0_MASK);
 		writel(v, &regs->ctrl);
 		v |= SUNXI_PWM_CTRL_CLK_GATE;
 		writel(v, &regs->ctrl);
-		priv->prescaler = prescaler;
+		priv->prescaler = best_prescaler;
 	}
 
-	writel(SUNXI_PWM_CH0_PERIOD_PRD(period) |
+	writel(SUNXI_PWM_CH0_PERIOD_PRD(best_period) |
 	       SUNXI_PWM_CH0_PERIOD_DUTY(duty), &regs->ch0_period);
 
 	debug("%s: prescaler: %d, period: %d, duty: %d\n",
 	      __func__, priv->prescaler,
-	      period, duty);
+	      best_period, duty);
 
 	return 0;
 }