Setting use-linear-interpolation in the dts will allow you to have linear interpolation between values of brightness-levels. There are now 256 between each of the values of brightness-levels. If something is requested halfway between 2 values, we'll use linear interpolation. 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 (eg: "brightness-levels = <0 2 4 8 16 32>;"). Patch based on the Alexandru M Stan work done for ChromeOS kernels. Signed-off-by: Enric Balletbo i Serra <enric.balletbo@xxxxxxxxxxxxx> --- .../bindings/leds/backlight/pwm-backlight.txt | 2 + drivers/video/backlight/pwm_bl.c | 55 +++++++++++++++++----- include/linux/pwm_backlight.h | 2 + 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt b/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt index 764db86..7c48f20 100644 --- a/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt +++ b/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt @@ -17,6 +17,8 @@ Optional properties: "pwms" property (see PWM binding[0]) - enable-gpios: contains a single GPIO specifier for the GPIO which enables and disables the backlight (see GPIO binding[1]) + - use-linear-interpolation: set this propriety to enable linear interpolation + between each of the values of brightness-levels. [0]: Documentation/devicetree/bindings/pwm/pwm.txt [1]: Documentation/devicetree/bindings/gpio/gpio.txt diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 9bd1768..59b1bfb 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -24,6 +24,8 @@ #include <linux/regulator/consumer.h> #include <linux/slab.h> +#define NSTEPS 256 + struct pwm_bl_data { struct pwm_device *pwm; struct device *dev; @@ -35,6 +37,7 @@ struct pwm_bl_data { struct gpio_desc *enable_gpio; unsigned int scale; bool legacy; + bool piecewise; int (*notify)(struct device *, int brightness); void (*notify_after)(struct device *, @@ -76,17 +79,36 @@ static void pwm_backlight_power_off(struct pwm_bl_data *pb) pb->enabled = false; } -static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness) +static int scale(struct pwm_bl_data *pb, int x) { unsigned int lth = pb->lth_brightness; + + return (x * (pb->period - lth) / pb->scale) + lth; +} + +static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness) +{ + int coarse = brightness / NSTEPS; + int fine = brightness % NSTEPS; int duty_cycle; - if (pb->levels) - duty_cycle = pb->levels[brightness]; - else - duty_cycle = brightness; + if (pb->levels) { + if (pb->piecewise) { + duty_cycle = scale(pb, pb->levels[coarse]); + if (fine > 0) + duty_cycle += (scale(pb, pb->levels[coarse + 1]) + - scale(pb, pb->levels[coarse])) + * fine / NSTEPS; + dev_dbg(pb->dev, "brightness=%d coarse=%d fine=%d\n", + brightness, coarse, fine); + } else { + duty_cycle = scale(pb, pb->levels[brightness]); + } + } else { + duty_cycle = scale(pb, brightness); + } - return (duty_cycle * (pb->period - lth) / pb->scale) + lth; + return duty_cycle; } static int pwm_backlight_update_status(struct backlight_device *bl) @@ -149,11 +171,11 @@ static int pwm_backlight_parse_dt(struct device *dev, if (!prop) return -EINVAL; - data->max_brightness = length / sizeof(u32); + data->levels_count = length / sizeof(u32); /* read brightness levels from DT property */ - if (data->max_brightness > 0) { - size_t size = sizeof(*data->levels) * data->max_brightness; + if (data->levels_count > 0) { + size_t size = sizeof(*data->levels) * data->levels_count; data->levels = devm_kzalloc(dev, size, GFP_KERNEL); if (!data->levels) @@ -161,7 +183,7 @@ static int pwm_backlight_parse_dt(struct device *dev, ret = of_property_read_u32_array(node, "brightness-levels", data->levels, - data->max_brightness); + data->levels_count); if (ret < 0) return ret; @@ -170,10 +192,18 @@ static int pwm_backlight_parse_dt(struct device *dev, if (ret < 0) return ret; + data->piecewise = of_property_read_bool(node, + "use-linear-interpolation"); + data->dft_brightness = value; - data->max_brightness--; + data->levels_count--; } + if (data->piecewise) + data->max_brightness = data->levels_count * NSTEPS; + else + data->max_brightness = data->levels_count; + data->enable_gpio = -EINVAL; return 0; } @@ -258,7 +288,7 @@ static int pwm_backlight_probe(struct platform_device *pdev) if (data->levels) { unsigned int i; - for (i = 0; i <= data->max_brightness; i++) + for (i = 0; i <= data->levels_count; i++) if (data->levels[i] > pb->scale) pb->scale = data->levels[i]; @@ -272,6 +302,7 @@ static int pwm_backlight_probe(struct platform_device *pdev) pb->exit = data->exit; pb->dev = &pdev->dev; pb->enabled = false; + pb->piecewise = data->piecewise; pb->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_ASIS); diff --git a/include/linux/pwm_backlight.h b/include/linux/pwm_backlight.h index e8afbd7..444a91b 100644 --- a/include/linux/pwm_backlight.h +++ b/include/linux/pwm_backlight.h @@ -14,6 +14,8 @@ struct platform_pwm_backlight_data { unsigned int lth_brightness; unsigned int pwm_period_ns; unsigned int *levels; + unsigned int levels_count; + bool piecewise; /* TODO remove once all users are switched to gpiod_* API */ int enable_gpio; int (*init)(struct device *dev); -- 2.9.3 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html