Re: [PATCH v2 1/4] backlight: pwm_bl: linear interpolation between brightness-levels
From: Daniel Thompson
Date: Wed Feb 07 2018 - 10:07:34 EST
On Wed, Feb 07, 2018 at 03:13:34PM +0100, Enric Balletbo i Serra wrote:
> Setting num-interpolated-steps in the dts will allow you to have linear
> interpolation between values of brightness-levels. This way a high
> resolution pwm duty cycle can be used without having to list out every
> possible value in the dts. This system also allows for gamma corrected
> values.
>
> The most simple example is interpolate between two brightness values a
> number of steps, this can be done setting the following in the dts:
>
> brightness-levels = <0 65535>;
> num-interpolated-steps = <1024>;
> default-brightness-level = <512>;
>
> This will create a brightness-level table with the following values:
>
> <0 63 126 189 252 315 378 441 ... 64260 64323 64386 64449 65535>
>
> Another use case can be describe a gamma corrected curve, as we have
> better sensitivity at low luminance than high luminance we probably
> want have smaller steps for low brightness levels values and bigger
> steps for high brightness levels values. This can be achieved with
> the following in the dts:
>
> brightness-levels = <0 4096 65535>;
> num-interpolated-steps = <1024>;
> default-brightness-level = <512>;
>
> This will create a brightness-levels table with the following values:
>
> <0 4 8 12 16 20 ... 4096 4156 4216 4276 ... 65535>
>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@xxxxxxxxxxxxx>
I'd really like to ack this one (especially so given I'm extremely late
with a review) but unfortunately...
> ---
> Changes since v1:
> - None.
>
> drivers/video/backlight/pwm_bl.c | 86 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 86 insertions(+)
>
> diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
> index 8e3f1245f5c5..9dbf0b3e806f 100644
> --- a/drivers/video/backlight/pwm_bl.c
> +++ b/drivers/video/backlight/pwm_bl.c
> @@ -147,7 +147,11 @@ static int pwm_backlight_parse_dt(struct device *dev,
> struct platform_pwm_backlight_data *data)
> {
> struct device_node *node = dev->of_node;
> + unsigned int num_levels = 0;
> + unsigned int levels_count;
> + unsigned int num_steps;
> struct property *prop;
> + unsigned int *table;
> int length;
> u32 value;
> int ret;
> @@ -167,6 +171,7 @@ static int pwm_backlight_parse_dt(struct device *dev,
> /* read brightness levels from DT property */
> if (data->max_brightness > 0) {
> size_t size = sizeof(*data->levels) * data->max_brightness;
> + unsigned int i, j, n = 0;
>
> data->levels = devm_kzalloc(dev, size, GFP_KERNEL);
> if (!data->levels)
> @@ -184,6 +189,87 @@ static int pwm_backlight_parse_dt(struct device *dev,
> return ret;
>
> data->dft_brightness = value;
> +
> + /*
> + * This property is optional, if is set enables linear
> + * interpolation between each of the values of brightness levels
> + * and creates a new pre-computed table.
> + */
> + of_property_read_u32(node, "num-interpolated-steps",
> + &num_steps);
> +
> + /*
> + * Make sure that there is at least two entries in the
> + * brightness-levels table, otherwise we can't interpolate
> + * between two points.
> + */
> + if (num_steps) {
> + if (data->max_brightness < 2) {
> + dev_err(dev, "can't interpolate\n");
> + return -EINVAL;
> + }
> +
> + /*
> + * Recalculate the number of brightness levels, now
> + * taking in consideration the number of interpolated
> + * steps between two levels.
> + */
> + for (i = 0; i < data->max_brightness - 1; i++) {
> + if ((data->levels[i + 1] - data->levels[i]) /
> + num_steps)
> + num_levels += num_steps;
> + else
> + num_levels++;
> + }
> + num_levels++;
> + dev_dbg(dev, "new number of brightness levels: %d\n",
> + num_levels);
> +
> + /*
> + * Create a new table of brightness levels with all the
> + * interpolated steps.
> + */
> + table = kcalloc(num_levels, sizeof(*table), GFP_KERNEL);
> + if (!table)
> + return -ENOMEM;
> +
> + /* Fill the interpolated table. */
> + levels_count = 0;
> + for (i = 0; i < data->max_brightness - 1; i++) {
> + value = data->levels[i];
> + n = (data->levels[i + 1] - value) / num_steps;
> + if (n > 0) {
> + for (j = 0; j < num_steps; j++) {
> + table[levels_count] = value;
> + value += n;
> + levels_count++;
> + }
> + } else {
> + table[levels_count] = data->levels[i];
> + levels_count++;
> + }
> + }
> + table[levels_count] = data->levels[i];
> +
> + /*
> + * As we use interpolation lets remove current
> + * brightness levels table for the new interpolated
> + * table.
> + */
> + devm_kfree(dev, data->levels);
> + data->levels = devm_kcalloc(dev, num_levels,
> + sizeof(*data->levels),
> + GFP_KERNEL);
> + memcpy(data->levels, table,
> + num_levels * sizeof(*table));
... this looks a bit too odd.
I think I can live we a pre-calculated table (not my preference... but I
can live with it). However the way table is allocated, then allocated
again and copied without a NULL check isn't OK.
Can't we just switch the first allocation over to a devres alloc and
then just swap pointers here?
And a minor nit: do we need calloc? The loop should initialize every
element anyway.
Daniel.
> + kfree(table);
> + /*
> + * Reassign max_brightness value to the new total number
> + * of brightness levels.
> + */
> + data->max_brightness = num_levels;
> + }
> +
> data->max_brightness--;
> }
>
> --
> 2.15.1
>