Le ven. 14 août 2026 à 21:15, David Lechner <[email protected]> a écrit :
>
> Add support for the num-interpolated-steps devicetree property to the
> pwm_backlight driver. This is used in cases where there are many points
> and we don't want to list them all in brightness-levels.
>
> Signed-off-by: David Lechner <[email protected]>
> ---
> This will be used by the MediaTek Genio 360 EVK that we are currently
> working to bring up.
> ---
>  drivers/video/pwm_backlight.c | 124 
> ++++++++++++++++++++++++++++++++++++++----
>  1 file changed, 112 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/video/pwm_backlight.c b/drivers/video/pwm_backlight.c
> index 1470eaf4cab..cd469417552 100644
> --- a/drivers/video/pwm_backlight.c
> +++ b/drivers/video/pwm_backlight.c
> @@ -16,6 +16,65 @@
>  #include <linux/math64.h>
>  #include <power/regulator.h>
>
> +/**
> + * build_interpolated_levels() - Build a linearly interpolated levels table
> + *
> + * Some "brightness-levels" tables only list a few anchor points and rely on
> + * "num-interpolated-steps" to fill in the values between them, so that a
> + * high resolution PWM duty cycle can be used without listing every value.
> + *
> + * @raw_levels: Anchor point values read from "brightness-levels"
> + * @count:     Number of anchor points in @raw_levels
> + * @num_steps: Number of interpolated steps between each anchor point
> + * @levels:    Returns newly allocated table of (count - 1) * num_steps + 1
> + *             entries
> + * @num_levels: Returns the number of entries in @levels
> + * Return: 0 on success, -EINVAL if @count or @num_steps are invalid, -ENOMEM
> + *        on allocation failure
> + */
> +static int build_interpolated_levels(const u32 *raw_levels, u32 count,
> +                                    u32 num_steps, u32 **levels,
> +                                    u32 *num_levels)
> +{
> +       u64 count_out64;
> +       u32 count_out, *table, i, x;
> +
> +       /*
> +        * num_steps must fit in the s32 divisor of div_s64(), and count_out64
> +        * must fit both u32 (it is stored as such) and the byte size passed
> +        * to malloc().
> +        */
> +       if (count < 2 || num_steps == 0 || num_steps > S32_MAX)

we should include directly include/limits.h for S32_MAX

> +               return -EINVAL;
> +
> +       count_out64 = (u64)(count - 1) * num_steps + 1;
> +       if (count_out64 > U32_MAX || count_out64 > SIZE_MAX / sizeof(u32))
> +               return -EINVAL;
> +
> +       count_out = count_out64;
> +
> +       table = malloc(count_out * sizeof(u32));
> +       if (!table)
> +               return -ENOMEM;
> +
> +       for (i = 0; i < count - 1; i++) {
> +               u32 x1 = i * num_steps;
> +               u32 x2 = x1 + num_steps;
> +               u32 y1 = raw_levels[i];
> +               u32 y2 = raw_levels[i + 1];
> +               s64 dy = (s64)y2 - y1;
> +
> +               for (x = x1; x < x2; x++)
> +                       table[x] = y1 + div_s64(dy * (x - x1), num_steps);
> +       }
> +
> +       table[count_out - 1] = raw_levels[count - 1];
> +       *levels = table;
> +       *num_levels = count_out;
> +
> +       return 0;
> +}
> +
>  /**
>   * Private information for the PWM backlight
>   *
> @@ -49,7 +108,7 @@ struct pwm_backlight_priv {
>          */
>         bool polarity;
>         u32 *levels;
> -       int num_levels;
> +       u32 num_levels;
>         uint default_level;
>         int cur_level;
>         uint min_level;
> @@ -196,6 +255,7 @@ static int pwm_backlight_of_to_plat(struct udevice *dev)
>         struct ofnode_phandle_args args;
>         int index, ret, count, len;
>         const u32 *cell;
> +       u32 num_steps;
>
>         log_debug("start\n");
>         ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
> @@ -232,20 +292,60 @@ static int pwm_backlight_of_to_plat(struct udevice *dev)
>         index = dev_read_u32_default(dev, "default-brightness-level", 255);
>         cell = dev_read_prop(dev, "brightness-levels", &len);
>         count = len / sizeof(u32);
> -       if (cell && count > index) {
> -               priv->levels = malloc(len);
> -               if (!priv->levels)
> +
> +       /*
> +        * If present, "num-interpolated-steps" means the levels above are
> +        * just anchor points, and the actual table used for
> +        * default-brightness-level and PWM duty cycle is the interpolated
> +        * table built from those anchor points. Interpolating needs at
> +        * least two anchor points.
> +        */
> +       num_steps = dev_read_u32_default(dev, "num-interpolated-steps", 0);
> +
> +       priv->default_level = index;
> +       priv->max_level = 255;
> +
> +       if (cell && count >= (num_steps ? 2 : 1)) {
> +               u32 *raw_levels;
> +
> +               raw_levels = malloc(len);
> +               if (!raw_levels)
>                         return log_ret(-ENOMEM);
> -               ret = dev_read_u32_array(dev, "brightness-levels", 
> priv->levels,
> +
> +               ret = dev_read_u32_array(dev, "brightness-levels", raw_levels,
>                                          count);
> -               if (ret)
> +               if (ret) {
> +                       free(raw_levels);
>                         return log_msg_ret("levels", ret);
> -               priv->num_levels = count;
> -               priv->default_level = priv->levels[index];
> -               priv->max_level = priv->levels[count - 1];
> -       } else {
> -               priv->default_level = index;
> -               priv->max_level = 255;
> +               }
> +
> +               if (num_steps) {
> +                       ret = build_interpolated_levels(raw_levels, count, 
> num_steps,
> +                                                       &priv->levels,
> +                                                       &priv->num_levels);
> +                       free(raw_levels);
> +                       if (ret)
> +                               return log_ret(ret);
> +               } else {
> +                       priv->levels = raw_levels;
> +                       priv->num_levels = count;
> +               }
> +
> +               if (index < priv->num_levels) {
> +                       priv->default_level = priv->levels[index];
> +                       priv->max_level = priv->levels[priv->num_levels - 1];
> +               } else {
> +                       /*
> +                        * default-brightness-level is out of range for the
> +                        * table: fall back to raw 0-255 PWM scaling instead
> +                        * of using the table at all.
> +                        */
> +                       log_warning("default-brightness-level %d out of range 
> for %u-entry brightness-levels table, ignoring table\n",
> +                                   index, priv->num_levels);
> +                       free(priv->levels);
> +                       priv->levels = NULL;
> +                       priv->num_levels = 0;
> +               }
>         }
>         priv->cur_level = priv->default_level;
>         log_debug("done\n");
>
> ---
> base-commit: d3e8597e46b635ec556a057bc42f0b0859654bdf
> change-id: 20260814-video-pwm-backlight-num-interpolated-steps-7d63c7728db1
>
> Best regards,
> --
> David Lechner <[email protected]>
>

Except the little nitpick that can be fixed while applying:

Reviewed-by: Julien Stephan <[email protected]>

Reply via email to