devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/4] backlight: pwm_bl: support linear interpolation and brightness to human eye
@ 2018-02-07 14:13 Enric Balletbo i Serra
  2018-02-07 14:13 ` [PATCH v2 1/4] backlight: pwm_bl: linear interpolation between brightness-levels Enric Balletbo i Serra
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: Enric Balletbo i Serra @ 2018-02-07 14:13 UTC (permalink / raw)
  To: Daniel Thompson, Doug Anderson, Pavel Machek, Rob Herring
  Cc: Jingoo Han, Richard Purdie, Jacek Anaszewski, Brian Norris,
	Guenter Roeck, Lee Jones, Alexandru Stan, linux-leds, devicetree,
	linux-kernel

Dear all,

This series is a second patchset integrating the requested changes.

The first and second patch what tries to solve is the problem of
granularity for high resolution PWMs. The idea is simple interpolate
between 2 brightness values so we can have a high PWM duty cycle (a
16 bits PWM is up to 65535 possible steps) without having to list
out every possible value in the dts. I think that this patch is
required to not break backward compability, to be more flexible and
also extend the functionality to be able to use high resolution PWM
with enough steps to have a good UI experience in userspace.

The thirth and fourth patch is a bit more ambicious, the idea is let
decide the driver the brightness-levels required in function of the PWM
resolution. To do this create a brightness-levels table filled with the
CIE 1931 algorithm values to convert brightness to PWM duty cycle.

More detailed info is available in the commit message of every patch.

Both functionalities were tested on a Samsung Chromebook Plus (that has
a 16 bits PWM) and a SL50 device (with a 8 bits PWM)

Waiting for your feedback.

Best regards,

Enric Balletbo i Serra (4):
  backlight: pwm_bl: linear interpolation between brightness-levels
  dt-bindings: pwm-backlight: add a num-interpolation-steps property.
  backlight: pwm_bl: compute brightness of LED linearly to human eye.
  dt-bindings: pwm-backlight: move brightness-levels to optional.

 .../bindings/leds/backlight/pwm-backlight.txt      |  34 ++-
 drivers/video/backlight/pwm_bl.c                   | 235 +++++++++++++++++++--
 2 files changed, 249 insertions(+), 20 deletions(-)

-- 
2.15.1

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v2 1/4] backlight: pwm_bl: linear interpolation between brightness-levels
  2018-02-07 14:13 [PATCH v2 0/4] backlight: pwm_bl: support linear interpolation and brightness to human eye Enric Balletbo i Serra
@ 2018-02-07 14:13 ` Enric Balletbo i Serra
  2018-02-07 15:07   ` Daniel Thompson
  2018-02-07 14:13 ` [PATCH v2 2/4] dt-bindings: pwm-backlight: add a num-interpolation-steps property Enric Balletbo i Serra
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 8+ messages in thread
From: Enric Balletbo i Serra @ 2018-02-07 14:13 UTC (permalink / raw)
  To: Daniel Thompson, Doug Anderson, Pavel Machek, Rob Herring
  Cc: Jingoo Han, Richard Purdie, Jacek Anaszewski, Brian Norris,
	Guenter Roeck, Lee Jones, Alexandru Stan, linux-leds, devicetree,
	linux-kernel

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@collabora.com>
---
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));
+			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

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v2 2/4] dt-bindings: pwm-backlight: add a num-interpolation-steps property.
  2018-02-07 14:13 [PATCH v2 0/4] backlight: pwm_bl: support linear interpolation and brightness to human eye Enric Balletbo i Serra
  2018-02-07 14:13 ` [PATCH v2 1/4] backlight: pwm_bl: linear interpolation between brightness-levels Enric Balletbo i Serra
@ 2018-02-07 14:13 ` Enric Balletbo i Serra
  2018-02-07 16:11   ` Daniel Thompson
  2018-02-07 14:13 ` [PATCH v2 3/4] backlight: pwm_bl: compute brightness of LED linearly to human eye Enric Balletbo i Serra
  2018-02-07 14:13 ` [PATCH v2 4/4] dt-bindings: pwm-backlight: move brightness-levels to optional Enric Balletbo i Serra
  3 siblings, 1 reply; 8+ messages in thread
From: Enric Balletbo i Serra @ 2018-02-07 14:13 UTC (permalink / raw)
  To: Daniel Thompson, Doug Anderson, Pavel Machek, Rob Herring
  Cc: Jingoo Han, Richard Purdie, Jacek Anaszewski, Brian Norris,
	Guenter Roeck, Lee Jones, Alexandru Stan, linux-leds, devicetree,
	linux-kernel

The num-interpolated-steps property specifies the number of
interpolated steps between each value of brightness-level table. This is
useful for high resolution PWMs to not have to list out every possible
value in the brightness-level array.

Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
---
Changes since v1:
- Add an example with a small but realistic curve. Suggested by Rob and
  Daniel

 .../bindings/leds/backlight/pwm-backlight.txt         | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt b/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt
index 310810906613..ce9b5746b774 100644
--- a/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt
+++ b/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt
@@ -21,6 +21,11 @@ Optional properties:
                           and enabling the backlight using GPIO.
   - pwm-off-delay-ms: Delay in ms between disabling the backlight using GPIO
                       and setting PWM value to 0.
+  - num-interpolated-steps: Number of interpolated steps between each value
+                            of brightness-levels table. This way a high
+                            resolution pwm duty cycle can be used without
+                            having to list out every possible value in the
+                            brightness-level array.
 
 [0]: Documentation/devicetree/bindings/pwm/pwm.txt
 [1]: Documentation/devicetree/bindings/gpio/gpio.txt
@@ -39,3 +44,17 @@ Example:
 		post-pwm-on-delay-ms = <10>;
 		pwm-off-delay-ms = <10>;
 	};
+
+Example using num-interpolation-steps:
+
+	backlight {
+		compatible = "pwm-backlight";
+		pwms = <&pwm 0 5000000>;
+
+		brightness-levels = <0 2048 4096 8192 16384 65535>;
+		num-interpolated-steps = <2048>;
+		default-brightness-level = <4096>;
+
+		power-supply = <&vdd_bl_reg>;
+		enable-gpios = <&gpio 58 0>;
+	};
-- 
2.15.1

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v2 3/4] backlight: pwm_bl: compute brightness of LED linearly to human eye.
  2018-02-07 14:13 [PATCH v2 0/4] backlight: pwm_bl: support linear interpolation and brightness to human eye Enric Balletbo i Serra
  2018-02-07 14:13 ` [PATCH v2 1/4] backlight: pwm_bl: linear interpolation between brightness-levels Enric Balletbo i Serra
  2018-02-07 14:13 ` [PATCH v2 2/4] dt-bindings: pwm-backlight: add a num-interpolation-steps property Enric Balletbo i Serra
@ 2018-02-07 14:13 ` Enric Balletbo i Serra
  2018-02-07 14:13 ` [PATCH v2 4/4] dt-bindings: pwm-backlight: move brightness-levels to optional Enric Balletbo i Serra
  3 siblings, 0 replies; 8+ messages in thread
From: Enric Balletbo i Serra @ 2018-02-07 14:13 UTC (permalink / raw)
  To: Daniel Thompson, Doug Anderson, Pavel Machek, Rob Herring
  Cc: Jingoo Han, Richard Purdie, Jacek Anaszewski, Brian Norris,
	Guenter Roeck, Lee Jones, Alexandru Stan, linux-leds, devicetree,
	linux-kernel

When you want to change the brightness using a PWM signal, one thing you
need to consider is how human perceive the brightness. Human perceive
the brightness change non-linearly, we have better sensitivity at low
luminance than high luminance, so to achieve perceived linear dimming,
the brightness must be matches to the way our eyes behave. The CIE 1931
lightness formula is what actually describes how we perceive light.

This patch computes a default table with the brightness levels filled
with the numbers provided by the CIE 1931 algorithm, the number of the
brightness levels is calculated based on the PWM resolution.

The calculation of the table using the CIE 1931 algorithm is enabled by
default when you do not define the 'brightness-levels' propriety in your
device tree.

Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
---
Changes since v1:
- Fix an horrible and unjustifiable mistake a missing { fortunately
  catched very quick.

 drivers/video/backlight/pwm_bl.c | 149 +++++++++++++++++++++++++++++++++++----
 1 file changed, 136 insertions(+), 13 deletions(-)

diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
index 9dbf0b3e806f..05096862ee3f 100644
--- a/drivers/video/backlight/pwm_bl.c
+++ b/drivers/video/backlight/pwm_bl.c
@@ -143,6 +143,107 @@ static const struct backlight_ops pwm_backlight_ops = {
 };
 
 #ifdef CONFIG_OF
+#define PWM_LUMINANCE_SCALE	10000 /* luminance scale */
+
+/* An integer based power function */
+static u64 int_pow(u64 base, int exp)
+{
+	u64 result = 1;
+
+	while (exp) {
+		if (exp & 1)
+			result *= base;
+		exp >>= 1;
+		base *= base;
+	}
+
+	return result;
+}
+
+/*
+ * CIE lightness to PWM conversion.
+ *
+ * The CIE 1931 lightness formula is what actually describes how we perceive
+ * light:
+ *          Y = (L* / 902.3)           if L* ≤ 0.08856
+ *          Y = ((L* + 16) / 116)^3    if L* > 0.08856
+ *
+ * Where Y is the luminance, the amount of light coming out of the screen, and
+ * is a number between 0.0 and 1.0; and L* is the lightness, how bright a human
+ * perceives the screen to be, and is a number between 0 and 100.
+ *
+ * The following function does the fixed point maths needed to implement the
+ * above formula.
+ */
+static u64 cie1931(unsigned int lightness, unsigned int scale)
+{
+	u64 retval;
+
+	lightness *= 100;
+	if (lightness <= (8 * scale)) {
+		retval = DIV_ROUND_CLOSEST_ULL(lightness * 10, 9023);
+	} else {
+		retval = int_pow((lightness + (16 * scale)) / 116, 3);
+		retval = DIV_ROUND_CLOSEST_ULL(retval, (scale * scale));
+	}
+
+	return retval;
+}
+
+/*
+ * Create a default correction table for PWM values to create linear brightness
+ * for LED based backlights using the CIE1931 algorithm.
+ */
+static
+int pwm_backlight_brightness_default(struct device *dev,
+				     struct platform_pwm_backlight_data *data,
+				     unsigned int period)
+{
+	unsigned int counter = 0;
+	unsigned int i, n;
+	u64 retval;
+
+	/*
+	 * Count the number of bits needed to represent the period number. The
+	 * number of bits is used to calculate the number of levels used for the
+	 * brightness-levels table, the purpose of this calculation is have a
+	 * pre-computed table with enough levels to get linear brightness
+	 * perception. The period is divided by the number of bits so for a
+	 * 8-bit PWM we have 255 / 8 = 32 brightness levels or for a 16-bit PWM
+	 * we have 65535 / 16 = 4096 brightness levels.
+	 *
+	 * Note that this method is based on empirical testing on different
+	 * devices with PWM of 8 and 16 bits of resolution.
+	 */
+	n = period;
+	while (n) {
+		counter += n % 2;
+		n >>= 1;
+	}
+
+	data->max_brightness = DIV_ROUND_UP(period, counter);
+	data->levels = devm_kcalloc(dev, data->max_brightness,
+				    sizeof(*data->levels), GFP_KERNEL);
+	if (!data->levels)
+		return -ENOMEM;
+
+	/* Fill the table using the cie1931 algorithm */
+	for (i = 0; i < data->max_brightness; i++) {
+		retval = cie1931((i * PWM_LUMINANCE_SCALE) /
+				 data->max_brightness, PWM_LUMINANCE_SCALE) *
+				 period;
+		retval = DIV_ROUND_CLOSEST_ULL(retval, PWM_LUMINANCE_SCALE);
+		if (retval > UINT_MAX)
+			return -EINVAL;
+		data->levels[i] = (unsigned int)retval;
+	}
+
+	data->dft_brightness = data->max_brightness / 2;
+	data->max_brightness--;
+
+	return 0;
+}
+
 static int pwm_backlight_parse_dt(struct device *dev,
 				  struct platform_pwm_backlight_data *data)
 {
@@ -161,10 +262,13 @@ static int pwm_backlight_parse_dt(struct device *dev,
 
 	memset(data, 0, sizeof(*data));
 
-	/* determine the number of brightness levels */
+	/*
+	 * Determine the number of brightness levels, if this property is not
+	 * set a default table of brightness levels will be used.
+	 */
 	prop = of_find_property(node, "brightness-levels", &length);
 	if (!prop)
-		return -EINVAL;
+		return 0;
 
 	data->max_brightness = length / sizeof(u32);
 
@@ -297,6 +401,14 @@ static int pwm_backlight_parse_dt(struct device *dev,
 {
 	return -ENODEV;
 }
+
+static
+int pwm_backlight_brightness_default(struct device *dev,
+				     struct platform_pwm_backlight_data *data,
+				     unsigned int period)
+{
+	return -ENODEV;
+}
 #endif
 
 static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb)
@@ -337,7 +449,9 @@ static int pwm_backlight_probe(struct platform_device *pdev)
 	struct backlight_device *bl;
 	struct device_node *node = pdev->dev.of_node;
 	struct pwm_bl_data *pb;
+	struct pwm_state state;
 	struct pwm_args pargs;
+	unsigned int i;
 	int ret;
 
 	if (!data) {
@@ -362,17 +476,6 @@ static int pwm_backlight_probe(struct platform_device *pdev)
 		goto err_alloc;
 	}
 
-	if (data->levels) {
-		unsigned int i;
-
-		for (i = 0; i <= data->max_brightness; i++)
-			if (data->levels[i] > pb->scale)
-				pb->scale = data->levels[i];
-
-		pb->levels = data->levels;
-	} else
-		pb->scale = data->max_brightness;
-
 	pb->notify = data->notify;
 	pb->notify_after = data->notify_after;
 	pb->check_fb = data->check_fb;
@@ -439,6 +542,26 @@ static int pwm_backlight_probe(struct platform_device *pdev)
 
 	dev_dbg(&pdev->dev, "got pwm for backlight\n");
 
+	if (!data->levels) {
+		/* Get the PWM period (in nanoseconds) */
+		pwm_get_state(pb->pwm, &state);
+
+		ret = pwm_backlight_brightness_default(&pdev->dev, data,
+						       state.period);
+		if (ret < 0) {
+			dev_err(&pdev->dev,
+				"failed to setup default brightness table\n");
+			goto err_alloc;
+		}
+	}
+
+	for (i = 0; i <= data->max_brightness; i++) {
+		if (data->levels[i] > pb->scale)
+			pb->scale = data->levels[i];
+
+		pb->levels = data->levels;
+	}
+
 	/*
 	 * FIXME: pwm_apply_args() should be removed when switching to
 	 * the atomic PWM API.
-- 
2.15.1

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v2 4/4] dt-bindings: pwm-backlight: move brightness-levels to optional.
  2018-02-07 14:13 [PATCH v2 0/4] backlight: pwm_bl: support linear interpolation and brightness to human eye Enric Balletbo i Serra
                   ` (2 preceding siblings ...)
  2018-02-07 14:13 ` [PATCH v2 3/4] backlight: pwm_bl: compute brightness of LED linearly to human eye Enric Balletbo i Serra
@ 2018-02-07 14:13 ` Enric Balletbo i Serra
  2018-02-07 16:10   ` Daniel Thompson
  3 siblings, 1 reply; 8+ messages in thread
From: Enric Balletbo i Serra @ 2018-02-07 14:13 UTC (permalink / raw)
  To: Daniel Thompson, Doug Anderson, Pavel Machek, Rob Herring
  Cc: Jingoo Han, Richard Purdie, Jacek Anaszewski, Brian Norris,
	Guenter Roeck, Lee Jones, Alexandru Stan, linux-leds, devicetree,
	linux-kernel

The patch 'backlight: pwm_bl: compute brightness of LED linearly to
human eye' introduced a default brightness-levels table that is used
when brightness-levels is not available in the dts. So move
brightness-levels and default-brightness-level to be optional.

Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
Changes since v1:
- Fix a couple of typos as requested by Rob.
  - s/availablel/available/
  - s/move move/move/
  - "so move..." should be a new sentence.
- Add Reviewed-by: Rob Herring <robh@kernel.org>
 
 .../devicetree/bindings/leds/backlight/pwm-backlight.txt  | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt b/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt
index ce9b5746b774..64fa2fbd98c9 100644
--- a/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt
+++ b/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt
@@ -3,13 +3,6 @@ pwm-backlight bindings
 Required properties:
   - compatible: "pwm-backlight"
   - pwms: OF device-tree PWM specification (see PWM binding[0])
-  - brightness-levels: Array of distinct brightness levels. Typically these
-      are in the range from 0 to 255, but any range starting at 0 will do.
-      The actual brightness level (PWM duty cycle) will be interpolated
-      from these values. 0 means a 0% duty cycle (darkest/off), while the
-      last value in the array represents a 100% duty cycle (brightest).
-  - default-brightness-level: the default brightness level (index into the
-      array defined by the "brightness-levels" property)
   - power-supply: regulator for supply voltage
 
 Optional properties:
@@ -21,6 +14,14 @@ Optional properties:
                           and enabling the backlight using GPIO.
   - pwm-off-delay-ms: Delay in ms between disabling the backlight using GPIO
                       and setting PWM value to 0.
+  - brightness-levels: Array of distinct brightness levels. Typically these
+                       are in the range from 0 to 255, but any range starting at
+                       0 will do. The actual brightness level (PWM duty cycle)
+                       will be interpolated from these values. 0 means a 0% duty
+                       cycle (darkest/off), while the last value in the array
+                       represents a 100% duty cycle (brightest).
+  - default-brightness-level: The default brightness level (index into the
+                              array defined by the "brightness-levels" property).
   - num-interpolated-steps: Number of interpolated steps between each value
                             of brightness-levels table. This way a high
                             resolution pwm duty cycle can be used without
-- 
2.15.1

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 1/4] backlight: pwm_bl: linear interpolation between brightness-levels
  2018-02-07 14:13 ` [PATCH v2 1/4] backlight: pwm_bl: linear interpolation between brightness-levels Enric Balletbo i Serra
@ 2018-02-07 15:07   ` Daniel Thompson
  0 siblings, 0 replies; 8+ messages in thread
From: Daniel Thompson @ 2018-02-07 15:07 UTC (permalink / raw)
  To: Enric Balletbo i Serra
  Cc: Doug Anderson, Pavel Machek, Rob Herring, Jingoo Han,
	Richard Purdie, Jacek Anaszewski, Brian Norris, Guenter Roeck,
	Lee Jones, Alexandru Stan, linux-leds, devicetree, linux-kernel

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@collabora.com>

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
> 

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 4/4] dt-bindings: pwm-backlight: move brightness-levels to optional.
  2018-02-07 14:13 ` [PATCH v2 4/4] dt-bindings: pwm-backlight: move brightness-levels to optional Enric Balletbo i Serra
@ 2018-02-07 16:10   ` Daniel Thompson
  0 siblings, 0 replies; 8+ messages in thread
From: Daniel Thompson @ 2018-02-07 16:10 UTC (permalink / raw)
  To: Enric Balletbo i Serra
  Cc: Doug Anderson, Pavel Machek, Rob Herring, Jingoo Han,
	Richard Purdie, Jacek Anaszewski, Brian Norris, Guenter Roeck,
	Lee Jones, Alexandru Stan, linux-leds, devicetree, linux-kernel

On Wed, Feb 07, 2018 at 03:13:37PM +0100, Enric Balletbo i Serra wrote:
> The patch 'backlight: pwm_bl: compute brightness of LED linearly to
> human eye' introduced a default brightness-levels table that is used
> when brightness-levels is not available in the dts. So move
> brightness-levels and default-brightness-level to be optional.
> 
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> Reviewed-by: Rob Herring <robh@kernel.org>

Acked-by: Daniel Thompson <daniel.thompson@linaro.org>

> ---
> Changes since v1:
> - Fix a couple of typos as requested by Rob.
>   - s/availablel/available/
>   - s/move move/move/
>   - "so move..." should be a new sentence.
> - Add Reviewed-by: Rob Herring <robh@kernel.org>
>  
>  .../devicetree/bindings/leds/backlight/pwm-backlight.txt  | 15 ++++++++-------
>  1 file changed, 8 insertions(+), 7 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt b/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt
> index ce9b5746b774..64fa2fbd98c9 100644
> --- a/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt
> +++ b/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt
> @@ -3,13 +3,6 @@ pwm-backlight bindings
>  Required properties:
>    - compatible: "pwm-backlight"
>    - pwms: OF device-tree PWM specification (see PWM binding[0])
> -  - brightness-levels: Array of distinct brightness levels. Typically these
> -      are in the range from 0 to 255, but any range starting at 0 will do.
> -      The actual brightness level (PWM duty cycle) will be interpolated
> -      from these values. 0 means a 0% duty cycle (darkest/off), while the
> -      last value in the array represents a 100% duty cycle (brightest).
> -  - default-brightness-level: the default brightness level (index into the
> -      array defined by the "brightness-levels" property)
>    - power-supply: regulator for supply voltage
>  
>  Optional properties:
> @@ -21,6 +14,14 @@ Optional properties:
>                            and enabling the backlight using GPIO.
>    - pwm-off-delay-ms: Delay in ms between disabling the backlight using GPIO
>                        and setting PWM value to 0.
> +  - brightness-levels: Array of distinct brightness levels. Typically these
> +                       are in the range from 0 to 255, but any range starting at
> +                       0 will do. The actual brightness level (PWM duty cycle)
> +                       will be interpolated from these values. 0 means a 0% duty
> +                       cycle (darkest/off), while the last value in the array
> +                       represents a 100% duty cycle (brightest).
> +  - default-brightness-level: The default brightness level (index into the
> +                              array defined by the "brightness-levels" property).
>    - num-interpolated-steps: Number of interpolated steps between each value
>                              of brightness-levels table. This way a high
>                              resolution pwm duty cycle can be used without
> -- 
> 2.15.1
> 

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 2/4] dt-bindings: pwm-backlight: add a num-interpolation-steps property.
  2018-02-07 14:13 ` [PATCH v2 2/4] dt-bindings: pwm-backlight: add a num-interpolation-steps property Enric Balletbo i Serra
@ 2018-02-07 16:11   ` Daniel Thompson
  0 siblings, 0 replies; 8+ messages in thread
From: Daniel Thompson @ 2018-02-07 16:11 UTC (permalink / raw)
  To: Enric Balletbo i Serra
  Cc: Doug Anderson, Pavel Machek, Rob Herring, Jingoo Han,
	Richard Purdie, Jacek Anaszewski, Brian Norris, Guenter Roeck,
	Lee Jones, Alexandru Stan, linux-leds, devicetree, linux-kernel

On Wed, Feb 07, 2018 at 03:13:35PM +0100, Enric Balletbo i Serra wrote:
> The num-interpolated-steps property specifies the number of
> interpolated steps between each value of brightness-level table. This is
> useful for high resolution PWMs to not have to list out every possible
> value in the brightness-level array.
> 
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>

Acked-by: Daniel Thompson <daniel.thompson@linaro.org>
> ---
> Changes since v1:
> - Add an example with a small but realistic curve. Suggested by Rob and
>   Daniel
> 
>  .../bindings/leds/backlight/pwm-backlight.txt         | 19 +++++++++++++++++++
>  1 file changed, 19 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt b/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt
> index 310810906613..ce9b5746b774 100644
> --- a/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt
> +++ b/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt
> @@ -21,6 +21,11 @@ Optional properties:
>                            and enabling the backlight using GPIO.
>    - pwm-off-delay-ms: Delay in ms between disabling the backlight using GPIO
>                        and setting PWM value to 0.
> +  - num-interpolated-steps: Number of interpolated steps between each value
> +                            of brightness-levels table. This way a high
> +                            resolution pwm duty cycle can be used without
> +                            having to list out every possible value in the
> +                            brightness-level array.
>  
>  [0]: Documentation/devicetree/bindings/pwm/pwm.txt
>  [1]: Documentation/devicetree/bindings/gpio/gpio.txt
> @@ -39,3 +44,17 @@ Example:
>  		post-pwm-on-delay-ms = <10>;
>  		pwm-off-delay-ms = <10>;
>  	};
> +
> +Example using num-interpolation-steps:
> +
> +	backlight {
> +		compatible = "pwm-backlight";
> +		pwms = <&pwm 0 5000000>;
> +
> +		brightness-levels = <0 2048 4096 8192 16384 65535>;
> +		num-interpolated-steps = <2048>;
> +		default-brightness-level = <4096>;
> +
> +		power-supply = <&vdd_bl_reg>;
> +		enable-gpios = <&gpio 58 0>;
> +	};
> -- 
> 2.15.1
> 

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2018-02-07 16:11 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-02-07 14:13 [PATCH v2 0/4] backlight: pwm_bl: support linear interpolation and brightness to human eye Enric Balletbo i Serra
2018-02-07 14:13 ` [PATCH v2 1/4] backlight: pwm_bl: linear interpolation between brightness-levels Enric Balletbo i Serra
2018-02-07 15:07   ` Daniel Thompson
2018-02-07 14:13 ` [PATCH v2 2/4] dt-bindings: pwm-backlight: add a num-interpolation-steps property Enric Balletbo i Serra
2018-02-07 16:11   ` Daniel Thompson
2018-02-07 14:13 ` [PATCH v2 3/4] backlight: pwm_bl: compute brightness of LED linearly to human eye Enric Balletbo i Serra
2018-02-07 14:13 ` [PATCH v2 4/4] dt-bindings: pwm-backlight: move brightness-levels to optional Enric Balletbo i Serra
2018-02-07 16:10   ` Daniel Thompson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).