Currently, the implementation computes the best matched period based on the requested one, by looping through all possible register configurations. The best matched period is below the requested period. This means the PWM consumer could request duty cycle values between the best matched period and the requested period, which with the current implementation for computing the PWM value, it will result in values out of range with respect to the selected resolution. So change the way the PWM value is determined as a ratio between the requested period and duty cycle, mapped on the resolution interval. Do this in contrast to using the register configuration selected when the best matching period was determined. Signed-off-by: Abel Vesa <abel.vesa@xxxxxxxxxx> --- This patch is based on the following patchset: https://lore.kernel.org/all/20250303-leds-qcom-lpg-fix-max-pwm-on-hi-res-v3-0-62703c0ab76a@xxxxxxxxxx/ --- drivers/leds/rgb/leds-qcom-lpg.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/leds/rgb/leds-qcom-lpg.c b/drivers/leds/rgb/leds-qcom-lpg.c index 4f2a178e3d265a2cc88e651d3e2ca6ae3dfac2e2..80130c205dce7c6ae1c356b7a7c5f6460a2b0bb0 100644 --- a/drivers/leds/rgb/leds-qcom-lpg.c +++ b/drivers/leds/rgb/leds-qcom-lpg.c @@ -523,8 +523,10 @@ static int lpg_calc_freq(struct lpg_channel *chan, uint64_t period) return 0; } -static void lpg_calc_duty(struct lpg_channel *chan, uint64_t duty) +static void lpg_calc_duty(struct lpg_channel *chan, uint64_t duty, uint64_t period) { + const unsigned int *pwm_resolution_arr; + unsigned int step; unsigned int max; unsigned int val; unsigned int clk_rate; @@ -532,13 +534,15 @@ static void lpg_calc_duty(struct lpg_channel *chan, uint64_t duty) if (chan->subtype == LPG_SUBTYPE_HI_RES_PWM) { max = BIT(lpg_pwm_resolution_hi_res[chan->pwm_resolution_sel]) - 1; clk_rate = lpg_clk_rates_hi_res[chan->clk_sel]; + pwm_resolution_arr = lpg_pwm_resolution_hi_res; } else { max = BIT(lpg_pwm_resolution[chan->pwm_resolution_sel]) - 1; clk_rate = lpg_clk_rates[chan->clk_sel]; + pwm_resolution_arr = lpg_pwm_resolution; } - val = div64_u64(duty * clk_rate, - (u64)NSEC_PER_SEC * lpg_pre_divs[chan->pre_div_sel] * (1 << chan->pre_div_exp)); + step = div64_u64(period, max); + val = div64_u64(duty, step); chan->pwm_value = min(val, max); } @@ -829,7 +833,7 @@ static void lpg_brightness_set(struct lpg_led *led, struct led_classdev *cdev, lpg_calc_freq(chan, NSEC_PER_MSEC); duty = div_u64(brightness * chan->period, cdev->max_brightness); - lpg_calc_duty(chan, duty); + lpg_calc_duty(chan, duty, NSEC_PER_MSEC); chan->enabled = true; chan->ramp_enabled = false; @@ -906,7 +910,7 @@ static int lpg_blink_set(struct lpg_led *led, chan = led->channels[i]; lpg_calc_freq(chan, period); - lpg_calc_duty(chan, duty); + lpg_calc_duty(chan, duty, period); chan->enabled = true; chan->ramp_enabled = false; @@ -1241,7 +1245,7 @@ static int lpg_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, if (ret < 0) goto out_unlock; - lpg_calc_duty(chan, state->duty_cycle); + lpg_calc_duty(chan, state->duty_cycle, state->period); } chan->enabled = state->enabled; --- base-commit: 0067a4b21c9ab441bbe6bf3635b3ddd21f6ca7c3 change-id: 20250303-leds-qcom-lpg-compute-pwm-value-using-period-0823ceb7542a Best regards, -- Abel Vesa <abel.vesa@xxxxxxxxxx>