All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] drm/i915: Adaptive backlight support
@ 2012-06-20 22:12 Stéphane Marchesin
  2012-06-21  8:21 ` Jani Nikula
  2012-06-21  9:34 ` Daniel Vetter
  0 siblings, 2 replies; 5+ messages in thread
From: Stéphane Marchesin @ 2012-06-20 22:12 UTC (permalink / raw)
  To: intel-gfx

This is an initial implementation of i915 adaptive backlight support.
The intended use for the adaptive backlight is to generate interrupts
whenever the luminance of the screen changes by some thresholds. The
main caveat with that implementation is that those additional
interrupts will wake up the CPU and consume more power. Instead, we
hook into the vblank handler and handle it from there. This makes the
implementation a little less intuitive but a lot more efficient.
We also need to compute the gamma correction from the interrupt
handler so we do this with a (new) fixed point module.

Change-Id: I9b9631cacc7d90e2801a542a3789118521bc25f0
Signed-off-by: Stéphane Marchesin <marcheu@chromium.org>
---
 drivers/gpu/drm/i915/Makefile                   |    1 +
 drivers/gpu/drm/i915/i915_dma.c                 |   16 ++
 drivers/gpu/drm/i915/i915_drv.h                 |   10 +
 drivers/gpu/drm/i915/i915_irq.c                 |    8 +-
 drivers/gpu/drm/i915/i915_reg.h                 |   22 ++
 drivers/gpu/drm/i915/intel_adaptive_backlight.c |  266 +++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_fixedpoint.h         |  140 ++++++++++++
 drivers/gpu/drm/i915/intel_panel.c              |    6 +
 include/drm/i915_drm.h                          |    2 +
 9 files changed, 469 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/i915/intel_adaptive_backlight.c
 create mode 100644 drivers/gpu/drm/i915/intel_fixedpoint.h

diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index ce7fc77..5c125c3 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -13,6 +13,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \
 	  i915_gem_gtt.o \
 	  i915_gem_tiling.o \
 	  i915_trace_points.o \
+	  intel_adaptive_backlight.o \
 	  intel_display.o \
 	  intel_crt.o \
 	  intel_lvds.o \
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index ba60f3c..c2626d6 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -828,6 +828,22 @@ static int i915_setparam(struct drm_device *dev, void *data,
 		/* Userspace can use first N regs */
 		dev_priv->fence_reg_start = param->value;
 		break;
+	case I915_SETPARAM_ADAPTIVE_BACKLIGHT_ENABLE:
+		if (INTEL_INFO(dev)->gen == 6) {
+			dev_priv->adaptive_backlight_enabled = param->value;
+		} else {
+			DRM_ERROR("No adaptive backlight on !GEN6\n");
+			return -EINVAL;
+		}
+		break;
+	case I915_SETPARAM_PANEL_GAMMA:
+		if (INTEL_INFO(dev)->gen == 6) {
+			dev_priv->adaptive_backlight_panel_gamma = param->value;
+		} else {
+			DRM_ERROR("No adaptive backlight on !GEN6\n");
+			return -EINVAL;
+		}
+		break;
 	default:
 		DRM_DEBUG_DRIVER("unknown parameter %d\n",
 					param->param);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index d89f585..f778f93 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -401,6 +401,13 @@ typedef struct drm_i915_private {
 	struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */
 	struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */
 
+	/* Adaptive backlight */
+	bool adaptive_backlight_enabled;
+	int backlight_correction_level;
+	int backlight_correction_count;
+	int backlight_correction_direction;
+	int adaptive_backlight_panel_gamma;
+
 	/* Feature bits from the VBIOS */
 	unsigned int int_tv_support:1;
 	unsigned int lvds_dither:1;
@@ -1358,6 +1365,9 @@ extern int i915_restore_state(struct drm_device *dev);
 extern int i915_save_state(struct drm_device *dev);
 extern int i915_restore_state(struct drm_device *dev);
 
+/* intel_adaptive_backlight.c */
+extern void intel_adaptive_backlight(struct drm_device *dev, int pipe);
+
 /* intel_i2c.c */
 extern int intel_setup_gmbus(struct drm_device *dev);
 extern void intel_teardown_gmbus(struct drm_device *dev);
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index afd4e03..948da6b 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -619,11 +619,15 @@ static irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS)
 		intel_finish_page_flip_plane(dev, 1);
 	}
 
-	if (de_iir & DE_PIPEA_VBLANK)
+	if (de_iir & DE_PIPEA_VBLANK) {
 		drm_handle_vblank(dev, 0);
+		intel_adaptive_backlight(dev, 0);
+	}
 
-	if (de_iir & DE_PIPEB_VBLANK)
+	if (de_iir & DE_PIPEB_VBLANK) {
 		drm_handle_vblank(dev, 1);
+		intel_adaptive_backlight(dev, 1);
+	}
 
 	/* check event from PCH */
 	if (de_iir & DE_PCH_EVENT) {
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 552264c..2db874d 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -3587,6 +3587,28 @@
 #define  PWM_PIPE_B		(1 << 29)
 #define BLC_PWM_CPU_CTL		0x48254
 
+#define BLM_HIST_CTL			0x48260
+#define  ENH_HIST_ENABLE		(1<<31)
+#define  ENH_MODIF_TBL_ENABLE		(1<<30)
+#define  ENH_PIPE_A_SELECT		(0<<29)
+#define  ENH_PIPE_B_SELECT		(1<<29)
+#define  ENH_PIPE(pipe) _PIPE(pipe, ENH_PIPE_A_SELECT, ENH_PIPE_B_SELECT)
+#define  HIST_MODE_YUV			(0<<24)
+#define  HIST_MODE_HSV			(1<<24)
+#define  ENH_MODE_DIRECT		(0<<13)
+#define  ENH_MODE_ADDITIVE		(1<<13)
+#define  ENH_MODE_MULTIPLICATIVE	(2<<13)
+#define  BIN_REGISTER_SET		(1<<11)
+#define  ENH_NUM_BINS			32
+
+#define BLM_HIST_ENH			0x48264
+
+#define BLM_HIST_GUARD_BAND		0x48268
+#define  BLM_HIST_INTR_ENABLE		(1<<31)
+#define  BLM_HIST_EVENT_STATUS		(1<<30)
+#define  BLM_HIST_INTR_DELAY_MASK	(0xFF<<22)
+#define  BLM_HIST_INTR_DELAY_SHIFT	22
+
 #define BLC_PWM_PCH_CTL1	0xc8250
 #define  PWM_PCH_ENABLE		(1 << 31)
 #define  PWM_POLARITY_ACTIVE_LOW	(1 << 29)
diff --git a/drivers/gpu/drm/i915/intel_adaptive_backlight.c b/drivers/gpu/drm/i915/intel_adaptive_backlight.c
new file mode 100644
index 0000000..4234962
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_adaptive_backlight.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2012 The Chromium OS Authors.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+
+#include "drmP.h"
+#include "drm.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+#include "i915_reg.h"
+#include "intel_drv.h"
+#include "intel_fixedpoint.h"
+
+/*
+ * This function takes a histogram of buckets as input and determines an
+ * acceptable target backlight level.
+ */
+static int histogram_find_correction_level(int *levels)
+{
+	int i, sum = 0;
+	int ratio, distortion, prev_distortion = 0, off, final_ratio, target;
+
+	for (i = 0; i < ENH_NUM_BINS; i++)
+		sum += levels[i];
+
+	/* Allow 0.33/256 distortion per pixel, on average */
+	target = sum / 3;
+
+	/* Special case where we only have less than 100 pixels
+	 * outside of the darkest bin.
+	 */
+	if (sum - levels[0] <= 100)
+		return 70;
+
+	for (ratio = ENH_NUM_BINS - 1; ratio >= 0 ; ratio--) {
+		distortion = 0;
+		for (i = ratio; i < ENH_NUM_BINS; i++) {
+			int pixel_distortion = (i - ratio)*8;
+			int num_pixels = levels[i];
+			distortion += num_pixels * pixel_distortion;
+		}
+		if (distortion > target)
+			break;
+		else
+			prev_distortion = distortion;
+	}
+
+	ratio++;
+
+	/* If we're not exactly at the border between two buckets, extrapolate
+	 * to get 3 extra bits of accuracy.
+	 */
+	if (distortion - prev_distortion)
+		off = 8 * (target - prev_distortion) /
+		      (distortion - prev_distortion);
+	else
+		off = 0;
+
+	final_ratio = ratio * 255 / 31 + off;
+
+	if (final_ratio > 255)
+		final_ratio = 255;
+
+	/* Never aim for less than 50% of the total backlight */
+	if (final_ratio < 128)
+		final_ratio = 128;
+
+	return final_ratio;
+}
+
+static void histogram_get_levels(struct drm_device *dev, int pipe, int *levels)
+{
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	int i;
+
+	for (i = 0; i < ENH_NUM_BINS; i++) {
+		I915_WRITE(BLM_HIST_CTL, ENH_HIST_ENABLE |
+					 ENH_MODIF_TBL_ENABLE |
+					 ENH_PIPE(pipe) |
+					 HIST_MODE_YUV |
+					 ENH_MODE_ADDITIVE |
+					 i);
+		levels[i] = I915_READ(BLM_HIST_ENH);
+	}
+}
+
+static void histogram_set_levels(struct drm_device *dev, int pipe, int *levels)
+{
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	int i;
+
+	for (i = 0; i < ENH_NUM_BINS; i++) {
+		I915_WRITE(BLM_HIST_CTL, ENH_HIST_ENABLE |
+					 ENH_MODIF_TBL_ENABLE |
+					 ENH_PIPE(pipe) |
+					 HIST_MODE_YUV |
+					 ENH_MODE_ADDITIVE |
+					 BIN_REGISTER_SET |
+					 i);
+		I915_WRITE(BLM_HIST_ENH, levels[i]);
+	}
+}
+
+/*
+ * This function computes the backlight correction level for an acceptable
+ * distortion and fills up the correction bins adequately.
+ */
+static void
+adaptive_backlight_compute_correction(struct drm_device *dev, int *levels)
+{
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	int correction_level;
+	int i, multiplier, one_over_gamma;
+
+	/* Find the correction level for an acceptable distortion */
+	correction_level = histogram_find_correction_level(levels);
+
+	/* If we're not yet at our correction target, we need to decide by how
+	 * much to move.
+	 */
+	if (dev_priv->backlight_correction_level != correction_level) {
+		int delta, direction;
+
+		direction = (correction_level >
+			     dev_priv->backlight_correction_level);
+
+		if (direction == dev_priv->backlight_correction_direction) {
+			dev_priv->backlight_correction_count++;
+		} else {
+			dev_priv->backlight_correction_count = 0;
+			dev_priv->backlight_correction_direction = direction;
+		}
+
+		delta = abs(correction_level -
+			    dev_priv->backlight_correction_level)/4;
+
+		if (delta < 1)
+			delta = 1;
+
+		/* For increasing the brightness, we do it instantly.
+		 * For lowering the brightness, we require at least 10 frames
+		 * below the current value. This avoids ping-ponging of the
+		 * backlight level.
+		 *
+		 * We also never increase the backlight by more than 6% per
+		 * frame, and never lower it by more than 3% per frame, because
+		 * the backlight needs time to adjust and the LCD correction
+		 * would be "ahead" otherwise.
+		 */
+		if (correction_level > dev_priv->backlight_correction_level) {
+			if (delta > 15)
+				delta = 15;
+			dev_priv->backlight_correction_level += delta;
+		} else if ((dev_priv->backlight_correction_count > 10) &&
+		   (correction_level < dev_priv->backlight_correction_level)) {
+			if (delta > 7)
+				delta = 7;
+			dev_priv->backlight_correction_level -= delta;
+		}
+	}
+
+	/* We need to invert the gamma correction of the LCD values,
+	 * but not of the backlight which is linear.
+	 */
+	one_over_gamma = intel_fixed_div(FIXED_ONE,
+				dev_priv->adaptive_backlight_panel_gamma);
+	multiplier = intel_fixed_pow(dev_priv->backlight_correction_level * 256,
+				     one_over_gamma);
+
+	for (i = 0; i < ENH_NUM_BINS; i++) {
+		int base_value = i * 8 * 4;
+		levels[i] = base_value - base_value * multiplier / 65536;
+	}
+}
+
+/*
+ * A quick note about the adaptive backlight implementation:
+ * If we let it run as designed, it will generate a lot of interrupts which
+ * tends to wake the CPU up and waste power. This is a bad idea for a power
+ * saving feature. Instead, we couple it to the vblank interrupt since that
+ * means we drew something. This means that we do not react to non-vsynced GL
+ * updates, or updates to the front buffer, and also adds a little bit of
+ * extra latency. But it is an acceptable tradeoff to make.
+ */
+void intel_adaptive_backlight(struct drm_device *dev, int pipe_vblank_event)
+{
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	int levels[ENH_NUM_BINS];
+	int pipe;
+	struct drm_connector *connector;
+	struct intel_crtc *intel_crtc;
+
+	if (!dev_priv->adaptive_backlight_enabled)
+		return;
+
+	/* Find the connector */
+	if (dev_priv->int_lvds_connector)
+		connector = dev_priv->int_lvds_connector;
+	else if (dev_priv->int_edp_connector)
+		connector = dev_priv->int_edp_connector;
+	else
+		return;
+
+	if (!connector)
+		return;
+
+	if (!connector->encoder)
+		return;
+
+	if (!connector->encoder->crtc)
+		return;
+
+	/* Find the pipe for the panel. */
+	intel_crtc = to_intel_crtc(connector->encoder->crtc);
+	pipe = intel_crtc->pipe;
+
+	/* The callback happens for both pipe A & B. Now that we know which
+	 * pipe we're doing adaptive backlight on, check that it's the right
+	 * one. Bail if it isn't.
+	 */
+	if (pipe != pipe_vblank_event)
+		return;
+
+	/* Return if no event. */
+	if (I915_READ(BLM_HIST_GUARD_BAND) & BLM_HIST_EVENT_STATUS == 0)
+		return;
+
+	/* Make sure we ack the previous event. Even though we do not get the
+	 * IRQs (see above explanation), we must still ack the events otherwise
+	 * the histogram data doesn't get updated any more.
+	 */
+	I915_WRITE(BLM_HIST_GUARD_BAND, BLM_HIST_INTR_ENABLE |
+					BLM_HIST_EVENT_STATUS |
+					(1 << BLM_HIST_INTR_DELAY_SHIFT));
+
+	histogram_get_levels(dev, pipe, levels);
+
+	adaptive_backlight_compute_correction(dev, levels);
+
+	histogram_set_levels(dev, pipe, levels);
+
+	intel_panel_set_backlight(dev, dev_priv->backlight_level);
+}
+
diff --git a/drivers/gpu/drm/i915/intel_fixedpoint.h b/drivers/gpu/drm/i915/intel_fixedpoint.h
new file mode 100644
index 0000000..fff5f0b
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_fixedpoint.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2012 The Chromium OS Authors.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/*
+ * The backlight is corrected in linear space. However the LCD correction is
+ * corrected in gama space. So to be able to compute the correction value for
+ * the LCD, we have to compute the inverse gamma. To do so, we carry this
+ * small fixed point module which allows us to use pow() to compute inverse
+ * gamma.
+ *
+ * The fixed point format used here is 16.16.
+ */
+
+/* intel_fixed_exp_tbl[x*32] = exp(x) * 65536 */
+static const int intel_fixed_exp_tbl[33] = {
+0x00010000, 0x00010820, 0x00011083, 0x00011929, 0x00012216, 0x00012b4b,
+0x000134cc, 0x00013e99, 0x000148b6, 0x00015325, 0x00015de9, 0x00016905,
+0x0001747a, 0x0001804d, 0x00018c80, 0x00019916, 0x0001a613, 0x0001b378,
+0x0001c14b, 0x0001cf8e, 0x0001de45, 0x0001ed74, 0x0001fd1e, 0x00020d47,
+0x00021df4, 0x00022f28, 0x000240e8, 0x00025338, 0x0002661d, 0x0002799b,
+0x00028db8, 0x0002a278, 0x0002b7e1
+};
+
+/* intel_fixed_log_tbl[x*32] = log(x) * 65536 */
+static const int intel_fixed_log_tbl[33] = {
+0x80000000, 0xfffc88c6, 0xfffd3a38, 0xfffda204, 0xfffdebaa, 0xfffe24ca,
+0xfffe5376, 0xfffe7aed, 0xfffe9d1c, 0xfffebb43, 0xfffed63c, 0xfffeeea2,
+0xffff04e8, 0xffff1966, 0xffff2c5f, 0xffff3e08, 0xffff4e8e, 0xffff5e13,
+0xffff6cb5, 0xffff7a8c, 0xffff87ae, 0xffff942b, 0xffffa014, 0xffffab75,
+0xffffb65a, 0xffffc0ce, 0xffffcad8, 0xffffd481, 0xffffddd1, 0xffffe6cd,
+0xffffef7a, 0xfffff7df, 0xffffffff
+};
+
+/* e * 65536 */
+#define FIXED_E (intel_fixed_exp_tbl[32])
+/* 1 * 65536 */
+#define FIXED_ONE 65536
+
+static int intel_fixed_mul(int a, int b)
+{
+	return (int) ((int64_t)a * (int64_t)b / 65536LL);
+}
+
+static int intel_fixed_div(int a, int b)
+{
+	return (int) ((int64_t)a * 65536LL / (int64_t)b);
+}
+
+/*
+ * Approximate fixed point log function.
+ * Only works for inputs in [0,1[
+ */
+static int intel_fixed_log(int val)
+{
+	int index = val * 32 / FIXED_ONE;
+	int remainder = (val & 0x7ff) << 5;
+	int v1 = intel_fixed_log_tbl[index];
+	int v2 = intel_fixed_log_tbl[index+1];
+	int final = v1 + intel_fixed_mul(v2 - v1, remainder);
+	return final;
+}
+
+/*
+ * Approximate fixed point exp function.
+ */
+static int intel_fixed_exp(int val)
+{
+	int count = 0;
+	int index, remainder;
+	int int_part = FIXED_ONE, frac_part;
+	int i, v, v1, v2;
+
+	while (val < 0) {
+		val += FIXED_ONE;
+		count--;
+	}
+
+	while (val > FIXED_ONE) {
+		val -= FIXED_ONE;
+		count++;
+	}
+
+	index = val * 32 / FIXED_ONE;
+	remainder = (val & 0x7ff) << 5;
+
+	v1 = intel_fixed_exp_tbl[index];
+	v2 = intel_fixed_exp_tbl[index+1];
+	frac_part = v1 + intel_fixed_mul(v2 - v1, remainder);
+
+	if (count < 0) {
+		for (i = 0; i < -count; i++)
+			int_part = intel_fixed_mul(int_part, FIXED_E);
+
+		v = intel_fixed_div(frac_part, int_part);
+	} else {
+		for (i = 0; i < count; i++)
+			int_part = intel_fixed_mul(int_part, FIXED_E);
+
+		v = intel_fixed_mul(frac_part, int_part);
+	}
+	return (v >= 0) ? v : 0;
+}
+
+/*
+ * Approximate fixed point pow function.
+ * Only works for x in [0,1[
+ */
+static int intel_fixed_pow(int x, int y)
+{
+	int e, p, r;
+	e = intel_fixed_log(x);
+	p = intel_fixed_mul(e, y);
+	r = intel_fixed_exp(p);
+	return r;
+}
+
+
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index 3f9249b..ae95163 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -251,6 +251,8 @@ static void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	u32 tmp;
 
+	level = level * dev_priv->backlight_correction_level >> 8;
+
 	DRM_DEBUG_DRIVER("set backlight PWM = %d\n", level);
 
 	if (HAS_PCH_SPLIT(dev))
@@ -306,6 +308,10 @@ static void intel_panel_init_backlight(struct drm_device *dev)
 
 	dev_priv->backlight_level = intel_panel_get_backlight(dev);
 	dev_priv->backlight_enabled = dev_priv->backlight_level != 0;
+	dev_priv->adaptive_backlight_enabled = 0;
+	/* 2.2 as 16.16 fixed point */
+	dev_priv->adaptive_backlight_panel_gamma = 144179;
+	dev_priv->backlight_correction_level = 256;
 }
 
 enum drm_connector_status
diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h
index da929bb..70c9cfc 100644
--- a/include/drm/i915_drm.h
+++ b/include/drm/i915_drm.h
@@ -309,6 +309,8 @@ typedef struct drm_i915_getparam {
 #define I915_SETPARAM_TEX_LRU_LOG_GRANULARITY             2
 #define I915_SETPARAM_ALLOW_BATCHBUFFER                   3
 #define I915_SETPARAM_NUM_USED_FENCES                     4
+#define I915_SETPARAM_ADAPTIVE_BACKLIGHT_ENABLE           5
+#define I915_SETPARAM_PANEL_GAMMA                         6
 
 typedef struct drm_i915_setparam {
 	int param;
-- 
1.7.5.3.367.ga9930

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH] drm/i915: Adaptive backlight support
  2012-06-20 22:12 [PATCH] drm/i915: Adaptive backlight support Stéphane Marchesin
@ 2012-06-21  8:21 ` Jani Nikula
  2012-06-21 22:08   ` Stéphane Marchesin
  2012-06-21  9:34 ` Daniel Vetter
  1 sibling, 1 reply; 5+ messages in thread
From: Jani Nikula @ 2012-06-21  8:21 UTC (permalink / raw)
  To: Stéphane Marchesin, intel-gfx

On Thu, 21 Jun 2012, Stéphane Marchesin <marcheu@chromium.org> wrote:
> This is an initial implementation of i915 adaptive backlight support.
> The intended use for the adaptive backlight is to generate interrupts
> whenever the luminance of the screen changes by some thresholds. The
> main caveat with that implementation is that those additional
> interrupts will wake up the CPU and consume more power. Instead, we
> hook into the vblank handler and handle it from there. This makes the
> implementation a little less intuitive but a lot more efficient.
> We also need to compute the gamma correction from the interrupt
> handler so we do this with a (new) fixed point module.

Hi Stéphane, interesting work.

My understanding is that content adaptive backlight control is basically
a function that tries to reduce backlight brightness to save power while
adjusting gamma to ensure there is no user detectable change in
image. Like this:

    (reduced_brightness, gamma) = f(brightness, luminance)

In my view, the histogram information, interrupt on its meaningful
change, brightness and gamma setting are clearly mechanisms the driver
should provide. I'm not at all sure if the function that defines the
policy for brightness and gamma changes should be part of the driver.

In fact, looking at the histogram_find_correction_level() and
adaptive_backlight_compute_correction() functions below, they are filled
with policy conditions. Usually it's preferred to have the policy in
user space. The fixed point troubles would all go away as well in user
space. And there's the added benefit of being able to handle the user's
desired brightness and possible ambient light sensor based adjustment to
brightness in a single point of truth.

I guess the question is how to define sensible interfaces to the
mechanisms in a way that the adaptive backlight function is feasible to
implement in user space, and the power saving goal could still be
reached. I would at least like this to be looked into first.


BR,
Jani.




>
> Change-Id: I9b9631cacc7d90e2801a542a3789118521bc25f0
> Signed-off-by: Stéphane Marchesin <marcheu@chromium.org>
> ---
>  drivers/gpu/drm/i915/Makefile                   |    1 +
>  drivers/gpu/drm/i915/i915_dma.c                 |   16 ++
>  drivers/gpu/drm/i915/i915_drv.h                 |   10 +
>  drivers/gpu/drm/i915/i915_irq.c                 |    8 +-
>  drivers/gpu/drm/i915/i915_reg.h                 |   22 ++
>  drivers/gpu/drm/i915/intel_adaptive_backlight.c |  266 +++++++++++++++++++++++
>  drivers/gpu/drm/i915/intel_fixedpoint.h         |  140 ++++++++++++
>  drivers/gpu/drm/i915/intel_panel.c              |    6 +
>  include/drm/i915_drm.h                          |    2 +
>  9 files changed, 469 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/gpu/drm/i915/intel_adaptive_backlight.c
>  create mode 100644 drivers/gpu/drm/i915/intel_fixedpoint.h
>
> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> index ce7fc77..5c125c3 100644
> --- a/drivers/gpu/drm/i915/Makefile
> +++ b/drivers/gpu/drm/i915/Makefile
> @@ -13,6 +13,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \
>  	  i915_gem_gtt.o \
>  	  i915_gem_tiling.o \
>  	  i915_trace_points.o \
> +	  intel_adaptive_backlight.o \
>  	  intel_display.o \
>  	  intel_crt.o \
>  	  intel_lvds.o \
> diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
> index ba60f3c..c2626d6 100644
> --- a/drivers/gpu/drm/i915/i915_dma.c
> +++ b/drivers/gpu/drm/i915/i915_dma.c
> @@ -828,6 +828,22 @@ static int i915_setparam(struct drm_device *dev, void *data,
>  		/* Userspace can use first N regs */
>  		dev_priv->fence_reg_start = param->value;
>  		break;
> +	case I915_SETPARAM_ADAPTIVE_BACKLIGHT_ENABLE:
> +		if (INTEL_INFO(dev)->gen == 6) {
> +			dev_priv->adaptive_backlight_enabled = param->value;
> +		} else {
> +			DRM_ERROR("No adaptive backlight on !GEN6\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case I915_SETPARAM_PANEL_GAMMA:
> +		if (INTEL_INFO(dev)->gen == 6) {
> +			dev_priv->adaptive_backlight_panel_gamma = param->value;
> +		} else {
> +			DRM_ERROR("No adaptive backlight on !GEN6\n");
> +			return -EINVAL;
> +		}
> +		break;
>  	default:
>  		DRM_DEBUG_DRIVER("unknown parameter %d\n",
>  					param->param);
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index d89f585..f778f93 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -401,6 +401,13 @@ typedef struct drm_i915_private {
>  	struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */
>  	struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */
>  
> +	/* Adaptive backlight */
> +	bool adaptive_backlight_enabled;
> +	int backlight_correction_level;
> +	int backlight_correction_count;
> +	int backlight_correction_direction;
> +	int adaptive_backlight_panel_gamma;
> +
>  	/* Feature bits from the VBIOS */
>  	unsigned int int_tv_support:1;
>  	unsigned int lvds_dither:1;
> @@ -1358,6 +1365,9 @@ extern int i915_restore_state(struct drm_device *dev);
>  extern int i915_save_state(struct drm_device *dev);
>  extern int i915_restore_state(struct drm_device *dev);
>  
> +/* intel_adaptive_backlight.c */
> +extern void intel_adaptive_backlight(struct drm_device *dev, int pipe);
> +
>  /* intel_i2c.c */
>  extern int intel_setup_gmbus(struct drm_device *dev);
>  extern void intel_teardown_gmbus(struct drm_device *dev);
> diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
> index afd4e03..948da6b 100644
> --- a/drivers/gpu/drm/i915/i915_irq.c
> +++ b/drivers/gpu/drm/i915/i915_irq.c
> @@ -619,11 +619,15 @@ static irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS)
>  		intel_finish_page_flip_plane(dev, 1);
>  	}
>  
> -	if (de_iir & DE_PIPEA_VBLANK)
> +	if (de_iir & DE_PIPEA_VBLANK) {
>  		drm_handle_vblank(dev, 0);
> +		intel_adaptive_backlight(dev, 0);
> +	}
>  
> -	if (de_iir & DE_PIPEB_VBLANK)
> +	if (de_iir & DE_PIPEB_VBLANK) {
>  		drm_handle_vblank(dev, 1);
> +		intel_adaptive_backlight(dev, 1);
> +	}
>  
>  	/* check event from PCH */
>  	if (de_iir & DE_PCH_EVENT) {
> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> index 552264c..2db874d 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -3587,6 +3587,28 @@
>  #define  PWM_PIPE_B		(1 << 29)
>  #define BLC_PWM_CPU_CTL		0x48254
>  
> +#define BLM_HIST_CTL			0x48260
> +#define  ENH_HIST_ENABLE		(1<<31)
> +#define  ENH_MODIF_TBL_ENABLE		(1<<30)
> +#define  ENH_PIPE_A_SELECT		(0<<29)
> +#define  ENH_PIPE_B_SELECT		(1<<29)
> +#define  ENH_PIPE(pipe) _PIPE(pipe, ENH_PIPE_A_SELECT, ENH_PIPE_B_SELECT)
> +#define  HIST_MODE_YUV			(0<<24)
> +#define  HIST_MODE_HSV			(1<<24)
> +#define  ENH_MODE_DIRECT		(0<<13)
> +#define  ENH_MODE_ADDITIVE		(1<<13)
> +#define  ENH_MODE_MULTIPLICATIVE	(2<<13)
> +#define  BIN_REGISTER_SET		(1<<11)
> +#define  ENH_NUM_BINS			32
> +
> +#define BLM_HIST_ENH			0x48264
> +
> +#define BLM_HIST_GUARD_BAND		0x48268
> +#define  BLM_HIST_INTR_ENABLE		(1<<31)
> +#define  BLM_HIST_EVENT_STATUS		(1<<30)
> +#define  BLM_HIST_INTR_DELAY_MASK	(0xFF<<22)
> +#define  BLM_HIST_INTR_DELAY_SHIFT	22
> +
>  #define BLC_PWM_PCH_CTL1	0xc8250
>  #define  PWM_PCH_ENABLE		(1 << 31)
>  #define  PWM_POLARITY_ACTIVE_LOW	(1 << 29)
> diff --git a/drivers/gpu/drm/i915/intel_adaptive_backlight.c b/drivers/gpu/drm/i915/intel_adaptive_backlight.c
> new file mode 100644
> index 0000000..4234962
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/intel_adaptive_backlight.c
> @@ -0,0 +1,266 @@
> +/*
> + * Copyright 2012 The Chromium OS Authors.
> + * All Rights Reserved.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the
> + * "Software"), to deal in the Software without restriction, including
> + * without limitation the rights to use, copy, modify, merge, publish,
> + * distribute, sub license, and/or sell copies of the Software, and to
> + * permit persons to whom the Software is furnished to do so, subject to
> + * the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the
> + * next paragraph) shall be included in all copies or substantial portions
> + * of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
> + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
> + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
> + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
> + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
> + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> + *
> + */
> +
> +
> +#include "drmP.h"
> +#include "drm.h"
> +#include "i915_drm.h"
> +#include "i915_drv.h"
> +#include "i915_reg.h"
> +#include "intel_drv.h"
> +#include "intel_fixedpoint.h"
> +
> +/*
> + * This function takes a histogram of buckets as input and determines an
> + * acceptable target backlight level.
> + */
> +static int histogram_find_correction_level(int *levels)
> +{
> +	int i, sum = 0;
> +	int ratio, distortion, prev_distortion = 0, off, final_ratio, target;
> +
> +	for (i = 0; i < ENH_NUM_BINS; i++)
> +		sum += levels[i];
> +
> +	/* Allow 0.33/256 distortion per pixel, on average */
> +	target = sum / 3;
> +
> +	/* Special case where we only have less than 100 pixels
> +	 * outside of the darkest bin.
> +	 */
> +	if (sum - levels[0] <= 100)
> +		return 70;
> +
> +	for (ratio = ENH_NUM_BINS - 1; ratio >= 0 ; ratio--) {
> +		distortion = 0;
> +		for (i = ratio; i < ENH_NUM_BINS; i++) {
> +			int pixel_distortion = (i - ratio)*8;
> +			int num_pixels = levels[i];
> +			distortion += num_pixels * pixel_distortion;
> +		}
> +		if (distortion > target)
> +			break;
> +		else
> +			prev_distortion = distortion;
> +	}
> +
> +	ratio++;
> +
> +	/* If we're not exactly at the border between two buckets, extrapolate
> +	 * to get 3 extra bits of accuracy.
> +	 */
> +	if (distortion - prev_distortion)
> +		off = 8 * (target - prev_distortion) /
> +		      (distortion - prev_distortion);
> +	else
> +		off = 0;
> +
> +	final_ratio = ratio * 255 / 31 + off;
> +
> +	if (final_ratio > 255)
> +		final_ratio = 255;
> +
> +	/* Never aim for less than 50% of the total backlight */
> +	if (final_ratio < 128)
> +		final_ratio = 128;
> +
> +	return final_ratio;
> +}
> +
> +static void histogram_get_levels(struct drm_device *dev, int pipe, int *levels)
> +{
> +	drm_i915_private_t *dev_priv = dev->dev_private;
> +	int i;
> +
> +	for (i = 0; i < ENH_NUM_BINS; i++) {
> +		I915_WRITE(BLM_HIST_CTL, ENH_HIST_ENABLE |
> +					 ENH_MODIF_TBL_ENABLE |
> +					 ENH_PIPE(pipe) |
> +					 HIST_MODE_YUV |
> +					 ENH_MODE_ADDITIVE |
> +					 i);
> +		levels[i] = I915_READ(BLM_HIST_ENH);
> +	}
> +}
> +
> +static void histogram_set_levels(struct drm_device *dev, int pipe, int *levels)
> +{
> +	drm_i915_private_t *dev_priv = dev->dev_private;
> +	int i;
> +
> +	for (i = 0; i < ENH_NUM_BINS; i++) {
> +		I915_WRITE(BLM_HIST_CTL, ENH_HIST_ENABLE |
> +					 ENH_MODIF_TBL_ENABLE |
> +					 ENH_PIPE(pipe) |
> +					 HIST_MODE_YUV |
> +					 ENH_MODE_ADDITIVE |
> +					 BIN_REGISTER_SET |
> +					 i);
> +		I915_WRITE(BLM_HIST_ENH, levels[i]);
> +	}
> +}
> +
> +/*
> + * This function computes the backlight correction level for an acceptable
> + * distortion and fills up the correction bins adequately.
> + */
> +static void
> +adaptive_backlight_compute_correction(struct drm_device *dev, int *levels)
> +{
> +	drm_i915_private_t *dev_priv = dev->dev_private;
> +	int correction_level;
> +	int i, multiplier, one_over_gamma;
> +
> +	/* Find the correction level for an acceptable distortion */
> +	correction_level = histogram_find_correction_level(levels);
> +
> +	/* If we're not yet at our correction target, we need to decide by how
> +	 * much to move.
> +	 */
> +	if (dev_priv->backlight_correction_level != correction_level) {
> +		int delta, direction;
> +
> +		direction = (correction_level >
> +			     dev_priv->backlight_correction_level);
> +
> +		if (direction == dev_priv->backlight_correction_direction) {
> +			dev_priv->backlight_correction_count++;
> +		} else {
> +			dev_priv->backlight_correction_count = 0;
> +			dev_priv->backlight_correction_direction = direction;
> +		}
> +
> +		delta = abs(correction_level -
> +			    dev_priv->backlight_correction_level)/4;
> +
> +		if (delta < 1)
> +			delta = 1;
> +
> +		/* For increasing the brightness, we do it instantly.
> +		 * For lowering the brightness, we require at least 10 frames
> +		 * below the current value. This avoids ping-ponging of the
> +		 * backlight level.
> +		 *
> +		 * We also never increase the backlight by more than 6% per
> +		 * frame, and never lower it by more than 3% per frame, because
> +		 * the backlight needs time to adjust and the LCD correction
> +		 * would be "ahead" otherwise.
> +		 */
> +		if (correction_level > dev_priv->backlight_correction_level) {
> +			if (delta > 15)
> +				delta = 15;
> +			dev_priv->backlight_correction_level += delta;
> +		} else if ((dev_priv->backlight_correction_count > 10) &&
> +		   (correction_level < dev_priv->backlight_correction_level)) {
> +			if (delta > 7)
> +				delta = 7;
> +			dev_priv->backlight_correction_level -= delta;
> +		}
> +	}
> +
> +	/* We need to invert the gamma correction of the LCD values,
> +	 * but not of the backlight which is linear.
> +	 */
> +	one_over_gamma = intel_fixed_div(FIXED_ONE,
> +				dev_priv->adaptive_backlight_panel_gamma);
> +	multiplier = intel_fixed_pow(dev_priv->backlight_correction_level * 256,
> +				     one_over_gamma);
> +
> +	for (i = 0; i < ENH_NUM_BINS; i++) {
> +		int base_value = i * 8 * 4;
> +		levels[i] = base_value - base_value * multiplier / 65536;
> +	}
> +}
> +
> +/*
> + * A quick note about the adaptive backlight implementation:
> + * If we let it run as designed, it will generate a lot of interrupts which
> + * tends to wake the CPU up and waste power. This is a bad idea for a power
> + * saving feature. Instead, we couple it to the vblank interrupt since that
> + * means we drew something. This means that we do not react to non-vsynced GL
> + * updates, or updates to the front buffer, and also adds a little bit of
> + * extra latency. But it is an acceptable tradeoff to make.
> + */
> +void intel_adaptive_backlight(struct drm_device *dev, int pipe_vblank_event)
> +{
> +	drm_i915_private_t *dev_priv = dev->dev_private;
> +	int levels[ENH_NUM_BINS];
> +	int pipe;
> +	struct drm_connector *connector;
> +	struct intel_crtc *intel_crtc;
> +
> +	if (!dev_priv->adaptive_backlight_enabled)
> +		return;
> +
> +	/* Find the connector */
> +	if (dev_priv->int_lvds_connector)
> +		connector = dev_priv->int_lvds_connector;
> +	else if (dev_priv->int_edp_connector)
> +		connector = dev_priv->int_edp_connector;
> +	else
> +		return;
> +
> +	if (!connector)
> +		return;
> +
> +	if (!connector->encoder)
> +		return;
> +
> +	if (!connector->encoder->crtc)
> +		return;
> +
> +	/* Find the pipe for the panel. */
> +	intel_crtc = to_intel_crtc(connector->encoder->crtc);
> +	pipe = intel_crtc->pipe;
> +
> +	/* The callback happens for both pipe A & B. Now that we know which
> +	 * pipe we're doing adaptive backlight on, check that it's the right
> +	 * one. Bail if it isn't.
> +	 */
> +	if (pipe != pipe_vblank_event)
> +		return;
> +
> +	/* Return if no event. */
> +	if (I915_READ(BLM_HIST_GUARD_BAND) & BLM_HIST_EVENT_STATUS == 0)
> +		return;
> +
> +	/* Make sure we ack the previous event. Even though we do not get the
> +	 * IRQs (see above explanation), we must still ack the events otherwise
> +	 * the histogram data doesn't get updated any more.
> +	 */
> +	I915_WRITE(BLM_HIST_GUARD_BAND, BLM_HIST_INTR_ENABLE |
> +					BLM_HIST_EVENT_STATUS |
> +					(1 << BLM_HIST_INTR_DELAY_SHIFT));
> +
> +	histogram_get_levels(dev, pipe, levels);
> +
> +	adaptive_backlight_compute_correction(dev, levels);
> +
> +	histogram_set_levels(dev, pipe, levels);
> +
> +	intel_panel_set_backlight(dev, dev_priv->backlight_level);
> +}
> +
> diff --git a/drivers/gpu/drm/i915/intel_fixedpoint.h b/drivers/gpu/drm/i915/intel_fixedpoint.h
> new file mode 100644
> index 0000000..fff5f0b
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/intel_fixedpoint.h
> @@ -0,0 +1,140 @@
> +/*
> + * Copyright 2012 The Chromium OS Authors.
> + * All Rights Reserved.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the
> + * "Software"), to deal in the Software without restriction, including
> + * without limitation the rights to use, copy, modify, merge, publish,
> + * distribute, sub license, and/or sell copies of the Software, and to
> + * permit persons to whom the Software is furnished to do so, subject to
> + * the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the
> + * next paragraph) shall be included in all copies or substantial portions
> + * of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
> + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
> + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
> + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
> + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
> + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> + *
> + */
> +
> +/*
> + * The backlight is corrected in linear space. However the LCD correction is
> + * corrected in gama space. So to be able to compute the correction value for
> + * the LCD, we have to compute the inverse gamma. To do so, we carry this
> + * small fixed point module which allows us to use pow() to compute inverse
> + * gamma.
> + *
> + * The fixed point format used here is 16.16.
> + */
> +
> +/* intel_fixed_exp_tbl[x*32] = exp(x) * 65536 */
> +static const int intel_fixed_exp_tbl[33] = {
> +0x00010000, 0x00010820, 0x00011083, 0x00011929, 0x00012216, 0x00012b4b,
> +0x000134cc, 0x00013e99, 0x000148b6, 0x00015325, 0x00015de9, 0x00016905,
> +0x0001747a, 0x0001804d, 0x00018c80, 0x00019916, 0x0001a613, 0x0001b378,
> +0x0001c14b, 0x0001cf8e, 0x0001de45, 0x0001ed74, 0x0001fd1e, 0x00020d47,
> +0x00021df4, 0x00022f28, 0x000240e8, 0x00025338, 0x0002661d, 0x0002799b,
> +0x00028db8, 0x0002a278, 0x0002b7e1
> +};
> +
> +/* intel_fixed_log_tbl[x*32] = log(x) * 65536 */
> +static const int intel_fixed_log_tbl[33] = {
> +0x80000000, 0xfffc88c6, 0xfffd3a38, 0xfffda204, 0xfffdebaa, 0xfffe24ca,
> +0xfffe5376, 0xfffe7aed, 0xfffe9d1c, 0xfffebb43, 0xfffed63c, 0xfffeeea2,
> +0xffff04e8, 0xffff1966, 0xffff2c5f, 0xffff3e08, 0xffff4e8e, 0xffff5e13,
> +0xffff6cb5, 0xffff7a8c, 0xffff87ae, 0xffff942b, 0xffffa014, 0xffffab75,
> +0xffffb65a, 0xffffc0ce, 0xffffcad8, 0xffffd481, 0xffffddd1, 0xffffe6cd,
> +0xffffef7a, 0xfffff7df, 0xffffffff
> +};
> +
> +/* e * 65536 */
> +#define FIXED_E (intel_fixed_exp_tbl[32])
> +/* 1 * 65536 */
> +#define FIXED_ONE 65536
> +
> +static int intel_fixed_mul(int a, int b)
> +{
> +	return (int) ((int64_t)a * (int64_t)b / 65536LL);
> +}
> +
> +static int intel_fixed_div(int a, int b)
> +{
> +	return (int) ((int64_t)a * 65536LL / (int64_t)b);
> +}
> +
> +/*
> + * Approximate fixed point log function.
> + * Only works for inputs in [0,1[
> + */
> +static int intel_fixed_log(int val)
> +{
> +	int index = val * 32 / FIXED_ONE;
> +	int remainder = (val & 0x7ff) << 5;
> +	int v1 = intel_fixed_log_tbl[index];
> +	int v2 = intel_fixed_log_tbl[index+1];
> +	int final = v1 + intel_fixed_mul(v2 - v1, remainder);
> +	return final;
> +}
> +
> +/*
> + * Approximate fixed point exp function.
> + */
> +static int intel_fixed_exp(int val)
> +{
> +	int count = 0;
> +	int index, remainder;
> +	int int_part = FIXED_ONE, frac_part;
> +	int i, v, v1, v2;
> +
> +	while (val < 0) {
> +		val += FIXED_ONE;
> +		count--;
> +	}
> +
> +	while (val > FIXED_ONE) {
> +		val -= FIXED_ONE;
> +		count++;
> +	}
> +
> +	index = val * 32 / FIXED_ONE;
> +	remainder = (val & 0x7ff) << 5;
> +
> +	v1 = intel_fixed_exp_tbl[index];
> +	v2 = intel_fixed_exp_tbl[index+1];
> +	frac_part = v1 + intel_fixed_mul(v2 - v1, remainder);
> +
> +	if (count < 0) {
> +		for (i = 0; i < -count; i++)
> +			int_part = intel_fixed_mul(int_part, FIXED_E);
> +
> +		v = intel_fixed_div(frac_part, int_part);
> +	} else {
> +		for (i = 0; i < count; i++)
> +			int_part = intel_fixed_mul(int_part, FIXED_E);
> +
> +		v = intel_fixed_mul(frac_part, int_part);
> +	}
> +	return (v >= 0) ? v : 0;
> +}
> +
> +/*
> + * Approximate fixed point pow function.
> + * Only works for x in [0,1[
> + */
> +static int intel_fixed_pow(int x, int y)
> +{
> +	int e, p, r;
> +	e = intel_fixed_log(x);
> +	p = intel_fixed_mul(e, y);
> +	r = intel_fixed_exp(p);
> +	return r;
> +}
> +
> +
> diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
> index 3f9249b..ae95163 100644
> --- a/drivers/gpu/drm/i915/intel_panel.c
> +++ b/drivers/gpu/drm/i915/intel_panel.c
> @@ -251,6 +251,8 @@ static void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level
>  	struct drm_i915_private *dev_priv = dev->dev_private;
>  	u32 tmp;
>  
> +	level = level * dev_priv->backlight_correction_level >> 8;
> +
>  	DRM_DEBUG_DRIVER("set backlight PWM = %d\n", level);
>  
>  	if (HAS_PCH_SPLIT(dev))
> @@ -306,6 +308,10 @@ static void intel_panel_init_backlight(struct drm_device *dev)
>  
>  	dev_priv->backlight_level = intel_panel_get_backlight(dev);
>  	dev_priv->backlight_enabled = dev_priv->backlight_level != 0;
> +	dev_priv->adaptive_backlight_enabled = 0;
> +	/* 2.2 as 16.16 fixed point */
> +	dev_priv->adaptive_backlight_panel_gamma = 144179;
> +	dev_priv->backlight_correction_level = 256;
>  }
>  
>  enum drm_connector_status
> diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h
> index da929bb..70c9cfc 100644
> --- a/include/drm/i915_drm.h
> +++ b/include/drm/i915_drm.h
> @@ -309,6 +309,8 @@ typedef struct drm_i915_getparam {
>  #define I915_SETPARAM_TEX_LRU_LOG_GRANULARITY             2
>  #define I915_SETPARAM_ALLOW_BATCHBUFFER                   3
>  #define I915_SETPARAM_NUM_USED_FENCES                     4
> +#define I915_SETPARAM_ADAPTIVE_BACKLIGHT_ENABLE           5
> +#define I915_SETPARAM_PANEL_GAMMA                         6
>  
>  typedef struct drm_i915_setparam {
>  	int param;
> -- 
> 1.7.5.3.367.ga9930
>
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH] drm/i915: Adaptive backlight support
  2012-06-20 22:12 [PATCH] drm/i915: Adaptive backlight support Stéphane Marchesin
  2012-06-21  8:21 ` Jani Nikula
@ 2012-06-21  9:34 ` Daniel Vetter
  2012-06-21 22:18   ` Stéphane Marchesin
  1 sibling, 1 reply; 5+ messages in thread
From: Daniel Vetter @ 2012-06-21  9:34 UTC (permalink / raw)
  To: Stéphane Marchesin; +Cc: intel-gfx

On Wed, Jun 20, 2012 at 03:12:35PM -0700, Stéphane Marchesin wrote:
> This is an initial implementation of i915 adaptive backlight support.
> The intended use for the adaptive backlight is to generate interrupts
> whenever the luminance of the screen changes by some thresholds. The
> main caveat with that implementation is that those additional
> interrupts will wake up the CPU and consume more power. Instead, we
> hook into the vblank handler and handle it from there. This makes the
> implementation a little less intuitive but a lot more efficient.
> We also need to compute the gamma correction from the interrupt
> handler so we do this with a (new) fixed point module.
> 
> Change-Id: I9b9631cacc7d90e2801a542a3789118521bc25f0
> Signed-off-by: Stéphane Marchesin <marcheu@chromium.org>

A few quick comments:
- I don't like setparam for this too much. Imo exposing this as an
  lvds/eDP connector property makes more sense, and also makes the feature
  more accessible to mere mortals. btw I think we should do the same with
  the downclock stuff and expose a panel_downclock_freq plus a
  panel_downclock enable which is simply initialized at driver load time
  with the defaults, but could be freely changed afterwards.
- With connector properties we could also expose tuneables in an easy way,
  at least partially addressing Jani's comment (like the display gamma and
  maybe also other parameters).
- We need to somehow cope with front-rendering X. Yeah, I know this sucks
  and all, but such is life. We already mark framebuffers as busys when
  rendering into them directly, I guess we could hook into to that either
  disable this feature until the next pageflip. Or switch to a different
  mode. The disable needs to be gradually, obviously.
- I'm unsure whether we shouldn't move the fixed-point stuff to a common
  place. Wrt all the fixed-point constant I think a little macro to
  convert double to 16.16 fixed point would massively help code
  readability.

Yours, Daniel

> ---
>  drivers/gpu/drm/i915/Makefile                   |    1 +
>  drivers/gpu/drm/i915/i915_dma.c                 |   16 ++
>  drivers/gpu/drm/i915/i915_drv.h                 |   10 +
>  drivers/gpu/drm/i915/i915_irq.c                 |    8 +-
>  drivers/gpu/drm/i915/i915_reg.h                 |   22 ++
>  drivers/gpu/drm/i915/intel_adaptive_backlight.c |  266 +++++++++++++++++++++++
>  drivers/gpu/drm/i915/intel_fixedpoint.h         |  140 ++++++++++++
>  drivers/gpu/drm/i915/intel_panel.c              |    6 +
>  include/drm/i915_drm.h                          |    2 +
>  9 files changed, 469 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/gpu/drm/i915/intel_adaptive_backlight.c
>  create mode 100644 drivers/gpu/drm/i915/intel_fixedpoint.h
> 
> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> index ce7fc77..5c125c3 100644
> --- a/drivers/gpu/drm/i915/Makefile
> +++ b/drivers/gpu/drm/i915/Makefile
> @@ -13,6 +13,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \
>  	  i915_gem_gtt.o \
>  	  i915_gem_tiling.o \
>  	  i915_trace_points.o \
> +	  intel_adaptive_backlight.o \
>  	  intel_display.o \
>  	  intel_crt.o \
>  	  intel_lvds.o \
> diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
> index ba60f3c..c2626d6 100644
> --- a/drivers/gpu/drm/i915/i915_dma.c
> +++ b/drivers/gpu/drm/i915/i915_dma.c
> @@ -828,6 +828,22 @@ static int i915_setparam(struct drm_device *dev, void *data,
>  		/* Userspace can use first N regs */
>  		dev_priv->fence_reg_start = param->value;
>  		break;
> +	case I915_SETPARAM_ADAPTIVE_BACKLIGHT_ENABLE:
> +		if (INTEL_INFO(dev)->gen == 6) {
> +			dev_priv->adaptive_backlight_enabled = param->value;
> +		} else {
> +			DRM_ERROR("No adaptive backlight on !GEN6\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case I915_SETPARAM_PANEL_GAMMA:
> +		if (INTEL_INFO(dev)->gen == 6) {
> +			dev_priv->adaptive_backlight_panel_gamma = param->value;
> +		} else {
> +			DRM_ERROR("No adaptive backlight on !GEN6\n");
> +			return -EINVAL;
> +		}
> +		break;
>  	default:
>  		DRM_DEBUG_DRIVER("unknown parameter %d\n",
>  					param->param);
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index d89f585..f778f93 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -401,6 +401,13 @@ typedef struct drm_i915_private {
>  	struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */
>  	struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */
>  
> +	/* Adaptive backlight */
> +	bool adaptive_backlight_enabled;
> +	int backlight_correction_level;
> +	int backlight_correction_count;
> +	int backlight_correction_direction;
> +	int adaptive_backlight_panel_gamma;
> +
>  	/* Feature bits from the VBIOS */
>  	unsigned int int_tv_support:1;
>  	unsigned int lvds_dither:1;
> @@ -1358,6 +1365,9 @@ extern int i915_restore_state(struct drm_device *dev);
>  extern int i915_save_state(struct drm_device *dev);
>  extern int i915_restore_state(struct drm_device *dev);
>  
> +/* intel_adaptive_backlight.c */
> +extern void intel_adaptive_backlight(struct drm_device *dev, int pipe);
> +
>  /* intel_i2c.c */
>  extern int intel_setup_gmbus(struct drm_device *dev);
>  extern void intel_teardown_gmbus(struct drm_device *dev);
> diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
> index afd4e03..948da6b 100644
> --- a/drivers/gpu/drm/i915/i915_irq.c
> +++ b/drivers/gpu/drm/i915/i915_irq.c
> @@ -619,11 +619,15 @@ static irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS)
>  		intel_finish_page_flip_plane(dev, 1);
>  	}
>  
> -	if (de_iir & DE_PIPEA_VBLANK)
> +	if (de_iir & DE_PIPEA_VBLANK) {
>  		drm_handle_vblank(dev, 0);
> +		intel_adaptive_backlight(dev, 0);
> +	}
>  
> -	if (de_iir & DE_PIPEB_VBLANK)
> +	if (de_iir & DE_PIPEB_VBLANK) {
>  		drm_handle_vblank(dev, 1);
> +		intel_adaptive_backlight(dev, 1);
> +	}
>  
>  	/* check event from PCH */
>  	if (de_iir & DE_PCH_EVENT) {
> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> index 552264c..2db874d 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -3587,6 +3587,28 @@
>  #define  PWM_PIPE_B		(1 << 29)
>  #define BLC_PWM_CPU_CTL		0x48254
>  
> +#define BLM_HIST_CTL			0x48260
> +#define  ENH_HIST_ENABLE		(1<<31)
> +#define  ENH_MODIF_TBL_ENABLE		(1<<30)
> +#define  ENH_PIPE_A_SELECT		(0<<29)
> +#define  ENH_PIPE_B_SELECT		(1<<29)
> +#define  ENH_PIPE(pipe) _PIPE(pipe, ENH_PIPE_A_SELECT, ENH_PIPE_B_SELECT)
> +#define  HIST_MODE_YUV			(0<<24)
> +#define  HIST_MODE_HSV			(1<<24)
> +#define  ENH_MODE_DIRECT		(0<<13)
> +#define  ENH_MODE_ADDITIVE		(1<<13)
> +#define  ENH_MODE_MULTIPLICATIVE	(2<<13)
> +#define  BIN_REGISTER_SET		(1<<11)
> +#define  ENH_NUM_BINS			32
> +
> +#define BLM_HIST_ENH			0x48264
> +
> +#define BLM_HIST_GUARD_BAND		0x48268
> +#define  BLM_HIST_INTR_ENABLE		(1<<31)
> +#define  BLM_HIST_EVENT_STATUS		(1<<30)
> +#define  BLM_HIST_INTR_DELAY_MASK	(0xFF<<22)
> +#define  BLM_HIST_INTR_DELAY_SHIFT	22
> +
>  #define BLC_PWM_PCH_CTL1	0xc8250
>  #define  PWM_PCH_ENABLE		(1 << 31)
>  #define  PWM_POLARITY_ACTIVE_LOW	(1 << 29)
> diff --git a/drivers/gpu/drm/i915/intel_adaptive_backlight.c b/drivers/gpu/drm/i915/intel_adaptive_backlight.c
> new file mode 100644
> index 0000000..4234962
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/intel_adaptive_backlight.c
> @@ -0,0 +1,266 @@
> +/*
> + * Copyright 2012 The Chromium OS Authors.
> + * All Rights Reserved.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the
> + * "Software"), to deal in the Software without restriction, including
> + * without limitation the rights to use, copy, modify, merge, publish,
> + * distribute, sub license, and/or sell copies of the Software, and to
> + * permit persons to whom the Software is furnished to do so, subject to
> + * the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the
> + * next paragraph) shall be included in all copies or substantial portions
> + * of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
> + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
> + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
> + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
> + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
> + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> + *
> + */
> +
> +
> +#include "drmP.h"
> +#include "drm.h"
> +#include "i915_drm.h"
> +#include "i915_drv.h"
> +#include "i915_reg.h"
> +#include "intel_drv.h"
> +#include "intel_fixedpoint.h"
> +
> +/*
> + * This function takes a histogram of buckets as input and determines an
> + * acceptable target backlight level.
> + */
> +static int histogram_find_correction_level(int *levels)
> +{
> +	int i, sum = 0;
> +	int ratio, distortion, prev_distortion = 0, off, final_ratio, target;
> +
> +	for (i = 0; i < ENH_NUM_BINS; i++)
> +		sum += levels[i];
> +
> +	/* Allow 0.33/256 distortion per pixel, on average */
> +	target = sum / 3;
> +
> +	/* Special case where we only have less than 100 pixels
> +	 * outside of the darkest bin.
> +	 */
> +	if (sum - levels[0] <= 100)
> +		return 70;
> +
> +	for (ratio = ENH_NUM_BINS - 1; ratio >= 0 ; ratio--) {
> +		distortion = 0;
> +		for (i = ratio; i < ENH_NUM_BINS; i++) {
> +			int pixel_distortion = (i - ratio)*8;
> +			int num_pixels = levels[i];
> +			distortion += num_pixels * pixel_distortion;
> +		}
> +		if (distortion > target)
> +			break;
> +		else
> +			prev_distortion = distortion;
> +	}
> +
> +	ratio++;
> +
> +	/* If we're not exactly at the border between two buckets, extrapolate
> +	 * to get 3 extra bits of accuracy.
> +	 */
> +	if (distortion - prev_distortion)
> +		off = 8 * (target - prev_distortion) /
> +		      (distortion - prev_distortion);
> +	else
> +		off = 0;
> +
> +	final_ratio = ratio * 255 / 31 + off;
> +
> +	if (final_ratio > 255)
> +		final_ratio = 255;
> +
> +	/* Never aim for less than 50% of the total backlight */
> +	if (final_ratio < 128)
> +		final_ratio = 128;
> +
> +	return final_ratio;
> +}
> +
> +static void histogram_get_levels(struct drm_device *dev, int pipe, int *levels)
> +{
> +	drm_i915_private_t *dev_priv = dev->dev_private;
> +	int i;
> +
> +	for (i = 0; i < ENH_NUM_BINS; i++) {
> +		I915_WRITE(BLM_HIST_CTL, ENH_HIST_ENABLE |
> +					 ENH_MODIF_TBL_ENABLE |
> +					 ENH_PIPE(pipe) |
> +					 HIST_MODE_YUV |
> +					 ENH_MODE_ADDITIVE |
> +					 i);
> +		levels[i] = I915_READ(BLM_HIST_ENH);
> +	}
> +}
> +
> +static void histogram_set_levels(struct drm_device *dev, int pipe, int *levels)
> +{
> +	drm_i915_private_t *dev_priv = dev->dev_private;
> +	int i;
> +
> +	for (i = 0; i < ENH_NUM_BINS; i++) {
> +		I915_WRITE(BLM_HIST_CTL, ENH_HIST_ENABLE |
> +					 ENH_MODIF_TBL_ENABLE |
> +					 ENH_PIPE(pipe) |
> +					 HIST_MODE_YUV |
> +					 ENH_MODE_ADDITIVE |
> +					 BIN_REGISTER_SET |
> +					 i);
> +		I915_WRITE(BLM_HIST_ENH, levels[i]);
> +	}
> +}
> +
> +/*
> + * This function computes the backlight correction level for an acceptable
> + * distortion and fills up the correction bins adequately.
> + */
> +static void
> +adaptive_backlight_compute_correction(struct drm_device *dev, int *levels)
> +{
> +	drm_i915_private_t *dev_priv = dev->dev_private;
> +	int correction_level;
> +	int i, multiplier, one_over_gamma;
> +
> +	/* Find the correction level for an acceptable distortion */
> +	correction_level = histogram_find_correction_level(levels);
> +
> +	/* If we're not yet at our correction target, we need to decide by how
> +	 * much to move.
> +	 */
> +	if (dev_priv->backlight_correction_level != correction_level) {
> +		int delta, direction;
> +
> +		direction = (correction_level >
> +			     dev_priv->backlight_correction_level);
> +
> +		if (direction == dev_priv->backlight_correction_direction) {
> +			dev_priv->backlight_correction_count++;
> +		} else {
> +			dev_priv->backlight_correction_count = 0;
> +			dev_priv->backlight_correction_direction = direction;
> +		}
> +
> +		delta = abs(correction_level -
> +			    dev_priv->backlight_correction_level)/4;
> +
> +		if (delta < 1)
> +			delta = 1;
> +
> +		/* For increasing the brightness, we do it instantly.
> +		 * For lowering the brightness, we require at least 10 frames
> +		 * below the current value. This avoids ping-ponging of the
> +		 * backlight level.
> +		 *
> +		 * We also never increase the backlight by more than 6% per
> +		 * frame, and never lower it by more than 3% per frame, because
> +		 * the backlight needs time to adjust and the LCD correction
> +		 * would be "ahead" otherwise.
> +		 */
> +		if (correction_level > dev_priv->backlight_correction_level) {
> +			if (delta > 15)
> +				delta = 15;
> +			dev_priv->backlight_correction_level += delta;
> +		} else if ((dev_priv->backlight_correction_count > 10) &&
> +		   (correction_level < dev_priv->backlight_correction_level)) {
> +			if (delta > 7)
> +				delta = 7;
> +			dev_priv->backlight_correction_level -= delta;
> +		}
> +	}
> +
> +	/* We need to invert the gamma correction of the LCD values,
> +	 * but not of the backlight which is linear.
> +	 */
> +	one_over_gamma = intel_fixed_div(FIXED_ONE,
> +				dev_priv->adaptive_backlight_panel_gamma);
> +	multiplier = intel_fixed_pow(dev_priv->backlight_correction_level * 256,
> +				     one_over_gamma);
> +
> +	for (i = 0; i < ENH_NUM_BINS; i++) {
> +		int base_value = i * 8 * 4;
> +		levels[i] = base_value - base_value * multiplier / 65536;
> +	}
> +}
> +
> +/*
> + * A quick note about the adaptive backlight implementation:
> + * If we let it run as designed, it will generate a lot of interrupts which
> + * tends to wake the CPU up and waste power. This is a bad idea for a power
> + * saving feature. Instead, we couple it to the vblank interrupt since that
> + * means we drew something. This means that we do not react to non-vsynced GL
> + * updates, or updates to the front buffer, and also adds a little bit of
> + * extra latency. But it is an acceptable tradeoff to make.
> + */
> +void intel_adaptive_backlight(struct drm_device *dev, int pipe_vblank_event)
> +{
> +	drm_i915_private_t *dev_priv = dev->dev_private;
> +	int levels[ENH_NUM_BINS];
> +	int pipe;
> +	struct drm_connector *connector;
> +	struct intel_crtc *intel_crtc;
> +
> +	if (!dev_priv->adaptive_backlight_enabled)
> +		return;
> +
> +	/* Find the connector */
> +	if (dev_priv->int_lvds_connector)
> +		connector = dev_priv->int_lvds_connector;
> +	else if (dev_priv->int_edp_connector)
> +		connector = dev_priv->int_edp_connector;
> +	else
> +		return;
> +
> +	if (!connector)
> +		return;
> +
> +	if (!connector->encoder)
> +		return;
> +
> +	if (!connector->encoder->crtc)
> +		return;
> +
> +	/* Find the pipe for the panel. */
> +	intel_crtc = to_intel_crtc(connector->encoder->crtc);
> +	pipe = intel_crtc->pipe;
> +
> +	/* The callback happens for both pipe A & B. Now that we know which
> +	 * pipe we're doing adaptive backlight on, check that it's the right
> +	 * one. Bail if it isn't.
> +	 */
> +	if (pipe != pipe_vblank_event)
> +		return;
> +
> +	/* Return if no event. */
> +	if (I915_READ(BLM_HIST_GUARD_BAND) & BLM_HIST_EVENT_STATUS == 0)
> +		return;
> +
> +	/* Make sure we ack the previous event. Even though we do not get the
> +	 * IRQs (see above explanation), we must still ack the events otherwise
> +	 * the histogram data doesn't get updated any more.
> +	 */
> +	I915_WRITE(BLM_HIST_GUARD_BAND, BLM_HIST_INTR_ENABLE |
> +					BLM_HIST_EVENT_STATUS |
> +					(1 << BLM_HIST_INTR_DELAY_SHIFT));
> +
> +	histogram_get_levels(dev, pipe, levels);
> +
> +	adaptive_backlight_compute_correction(dev, levels);
> +
> +	histogram_set_levels(dev, pipe, levels);
> +
> +	intel_panel_set_backlight(dev, dev_priv->backlight_level);
> +}
> +
> diff --git a/drivers/gpu/drm/i915/intel_fixedpoint.h b/drivers/gpu/drm/i915/intel_fixedpoint.h
> new file mode 100644
> index 0000000..fff5f0b
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/intel_fixedpoint.h
> @@ -0,0 +1,140 @@
> +/*
> + * Copyright 2012 The Chromium OS Authors.
> + * All Rights Reserved.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the
> + * "Software"), to deal in the Software without restriction, including
> + * without limitation the rights to use, copy, modify, merge, publish,
> + * distribute, sub license, and/or sell copies of the Software, and to
> + * permit persons to whom the Software is furnished to do so, subject to
> + * the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the
> + * next paragraph) shall be included in all copies or substantial portions
> + * of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
> + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
> + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
> + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
> + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
> + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> + *
> + */
> +
> +/*
> + * The backlight is corrected in linear space. However the LCD correction is
> + * corrected in gama space. So to be able to compute the correction value for
> + * the LCD, we have to compute the inverse gamma. To do so, we carry this
> + * small fixed point module which allows us to use pow() to compute inverse
> + * gamma.
> + *
> + * The fixed point format used here is 16.16.
> + */
> +
> +/* intel_fixed_exp_tbl[x*32] = exp(x) * 65536 */
> +static const int intel_fixed_exp_tbl[33] = {
> +0x00010000, 0x00010820, 0x00011083, 0x00011929, 0x00012216, 0x00012b4b,
> +0x000134cc, 0x00013e99, 0x000148b6, 0x00015325, 0x00015de9, 0x00016905,
> +0x0001747a, 0x0001804d, 0x00018c80, 0x00019916, 0x0001a613, 0x0001b378,
> +0x0001c14b, 0x0001cf8e, 0x0001de45, 0x0001ed74, 0x0001fd1e, 0x00020d47,
> +0x00021df4, 0x00022f28, 0x000240e8, 0x00025338, 0x0002661d, 0x0002799b,
> +0x00028db8, 0x0002a278, 0x0002b7e1
> +};
> +
> +/* intel_fixed_log_tbl[x*32] = log(x) * 65536 */
> +static const int intel_fixed_log_tbl[33] = {
> +0x80000000, 0xfffc88c6, 0xfffd3a38, 0xfffda204, 0xfffdebaa, 0xfffe24ca,
> +0xfffe5376, 0xfffe7aed, 0xfffe9d1c, 0xfffebb43, 0xfffed63c, 0xfffeeea2,
> +0xffff04e8, 0xffff1966, 0xffff2c5f, 0xffff3e08, 0xffff4e8e, 0xffff5e13,
> +0xffff6cb5, 0xffff7a8c, 0xffff87ae, 0xffff942b, 0xffffa014, 0xffffab75,
> +0xffffb65a, 0xffffc0ce, 0xffffcad8, 0xffffd481, 0xffffddd1, 0xffffe6cd,
> +0xffffef7a, 0xfffff7df, 0xffffffff
> +};
> +
> +/* e * 65536 */
> +#define FIXED_E (intel_fixed_exp_tbl[32])
> +/* 1 * 65536 */
> +#define FIXED_ONE 65536
> +
> +static int intel_fixed_mul(int a, int b)
> +{
> +	return (int) ((int64_t)a * (int64_t)b / 65536LL);
> +}
> +
> +static int intel_fixed_div(int a, int b)
> +{
> +	return (int) ((int64_t)a * 65536LL / (int64_t)b);
> +}
> +
> +/*
> + * Approximate fixed point log function.
> + * Only works for inputs in [0,1[
> + */
> +static int intel_fixed_log(int val)
> +{
> +	int index = val * 32 / FIXED_ONE;
> +	int remainder = (val & 0x7ff) << 5;
> +	int v1 = intel_fixed_log_tbl[index];
> +	int v2 = intel_fixed_log_tbl[index+1];
> +	int final = v1 + intel_fixed_mul(v2 - v1, remainder);
> +	return final;
> +}
> +
> +/*
> + * Approximate fixed point exp function.
> + */
> +static int intel_fixed_exp(int val)
> +{
> +	int count = 0;
> +	int index, remainder;
> +	int int_part = FIXED_ONE, frac_part;
> +	int i, v, v1, v2;
> +
> +	while (val < 0) {
> +		val += FIXED_ONE;
> +		count--;
> +	}
> +
> +	while (val > FIXED_ONE) {
> +		val -= FIXED_ONE;
> +		count++;
> +	}
> +
> +	index = val * 32 / FIXED_ONE;
> +	remainder = (val & 0x7ff) << 5;
> +
> +	v1 = intel_fixed_exp_tbl[index];
> +	v2 = intel_fixed_exp_tbl[index+1];
> +	frac_part = v1 + intel_fixed_mul(v2 - v1, remainder);
> +
> +	if (count < 0) {
> +		for (i = 0; i < -count; i++)
> +			int_part = intel_fixed_mul(int_part, FIXED_E);
> +
> +		v = intel_fixed_div(frac_part, int_part);
> +	} else {
> +		for (i = 0; i < count; i++)
> +			int_part = intel_fixed_mul(int_part, FIXED_E);
> +
> +		v = intel_fixed_mul(frac_part, int_part);
> +	}
> +	return (v >= 0) ? v : 0;
> +}
> +
> +/*
> + * Approximate fixed point pow function.
> + * Only works for x in [0,1[
> + */
> +static int intel_fixed_pow(int x, int y)
> +{
> +	int e, p, r;
> +	e = intel_fixed_log(x);
> +	p = intel_fixed_mul(e, y);
> +	r = intel_fixed_exp(p);
> +	return r;
> +}
> +
> +
> diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
> index 3f9249b..ae95163 100644
> --- a/drivers/gpu/drm/i915/intel_panel.c
> +++ b/drivers/gpu/drm/i915/intel_panel.c
> @@ -251,6 +251,8 @@ static void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level
>  	struct drm_i915_private *dev_priv = dev->dev_private;
>  	u32 tmp;
>  
> +	level = level * dev_priv->backlight_correction_level >> 8;
> +
>  	DRM_DEBUG_DRIVER("set backlight PWM = %d\n", level);
>  
>  	if (HAS_PCH_SPLIT(dev))
> @@ -306,6 +308,10 @@ static void intel_panel_init_backlight(struct drm_device *dev)
>  
>  	dev_priv->backlight_level = intel_panel_get_backlight(dev);
>  	dev_priv->backlight_enabled = dev_priv->backlight_level != 0;
> +	dev_priv->adaptive_backlight_enabled = 0;
> +	/* 2.2 as 16.16 fixed point */
> +	dev_priv->adaptive_backlight_panel_gamma = 144179;
> +	dev_priv->backlight_correction_level = 256;
>  }
>  
>  enum drm_connector_status
> diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h
> index da929bb..70c9cfc 100644
> --- a/include/drm/i915_drm.h
> +++ b/include/drm/i915_drm.h
> @@ -309,6 +309,8 @@ typedef struct drm_i915_getparam {
>  #define I915_SETPARAM_TEX_LRU_LOG_GRANULARITY             2
>  #define I915_SETPARAM_ALLOW_BATCHBUFFER                   3
>  #define I915_SETPARAM_NUM_USED_FENCES                     4
> +#define I915_SETPARAM_ADAPTIVE_BACKLIGHT_ENABLE           5
> +#define I915_SETPARAM_PANEL_GAMMA                         6
>  
>  typedef struct drm_i915_setparam {
>  	int param;
> -- 
> 1.7.5.3.367.ga9930
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx

-- 
Daniel Vetter
Mail: daniel@ffwll.ch
Mobile: +41 (0)79 365 57 48

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

* Re: [PATCH] drm/i915: Adaptive backlight support
  2012-06-21  8:21 ` Jani Nikula
@ 2012-06-21 22:08   ` Stéphane Marchesin
  0 siblings, 0 replies; 5+ messages in thread
From: Stéphane Marchesin @ 2012-06-21 22:08 UTC (permalink / raw)
  To: Jani Nikula; +Cc: intel-gfx


[-- Attachment #1.1: Type: text/plain, Size: 28019 bytes --]

On Thu, Jun 21, 2012 at 1:21 AM, Jani Nikula <jani.nikula@linux.intel.com>wrote:

> On Thu, 21 Jun 2012, Stéphane Marchesin <marcheu@chromium.org> wrote:
> > This is an initial implementation of i915 adaptive backlight support.
> > The intended use for the adaptive backlight is to generate interrupts
> > whenever the luminance of the screen changes by some thresholds. The
> > main caveat with that implementation is that those additional
> > interrupts will wake up the CPU and consume more power. Instead, we
> > hook into the vblank handler and handle it from there. This makes the
> > implementation a little less intuitive but a lot more efficient.
> > We also need to compute the gamma correction from the interrupt
> > handler so we do this with a (new) fixed point module.
>
> Hi Stéphane, interesting work.
>
> My understanding is that content adaptive backlight control is basically
> a function that tries to reduce backlight brightness to save power while
> adjusting gamma to ensure there is no user detectable change in
> image. Like this:
>
>    (reduced_brightness, gamma) = f(brightness, luminance)
>
> In my view, the histogram information, interrupt on its meaningful
> change, brightness and gamma setting are clearly mechanisms the driver
> should provide. I'm not at all sure if the function that defines the
> policy for brightness and gamma changes should be part of the driver.
>
>
Yes, I don't like it either, but the problem is that if we want fast
response time we have to do it from the irq handler, or at least the kernel.



> In fact, looking at the histogram_find_correction_level() and
> adaptive_backlight_compute_correction() functions below, they are filled
> with policy conditions. Usually it's preferred to have the policy in
> user space. The fixed point troubles would all go away as well in user
> space. And there's the added benefit of being able to handle the user's
> desired brightness and possible ambient light sensor based adjustment to
> brightness in a single point of truth.
>
> I guess the question is how to define sensible interfaces to the
> mechanisms in a way that the adaptive backlight function is feasible to
> implement in user space, and the power saving goal could still be
> reached. I would at least like this to be looked into first.
>
>
The issue is that if you have to wake up a user space process every time
you want to recompute the backlight, you're going to offset the backlight
savings with some CPU losses. I wanted to avoid that.

Stéphane



>
> BR,
> Jani.
>
>
>
>
> >
> > Change-Id: I9b9631cacc7d90e2801a542a3789118521bc25f0
> > Signed-off-by: Stéphane Marchesin <marcheu@chromium.org>
> > ---
> >  drivers/gpu/drm/i915/Makefile                   |    1 +
> >  drivers/gpu/drm/i915/i915_dma.c                 |   16 ++
> >  drivers/gpu/drm/i915/i915_drv.h                 |   10 +
> >  drivers/gpu/drm/i915/i915_irq.c                 |    8 +-
> >  drivers/gpu/drm/i915/i915_reg.h                 |   22 ++
> >  drivers/gpu/drm/i915/intel_adaptive_backlight.c |  266
> +++++++++++++++++++++++
> >  drivers/gpu/drm/i915/intel_fixedpoint.h         |  140 ++++++++++++
> >  drivers/gpu/drm/i915/intel_panel.c              |    6 +
> >  include/drm/i915_drm.h                          |    2 +
> >  9 files changed, 469 insertions(+), 2 deletions(-)
> >  create mode 100644 drivers/gpu/drm/i915/intel_adaptive_backlight.c
> >  create mode 100644 drivers/gpu/drm/i915/intel_fixedpoint.h
> >
> > diff --git a/drivers/gpu/drm/i915/Makefile
> b/drivers/gpu/drm/i915/Makefile
> > index ce7fc77..5c125c3 100644
> > --- a/drivers/gpu/drm/i915/Makefile
> > +++ b/drivers/gpu/drm/i915/Makefile
> > @@ -13,6 +13,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \
> >         i915_gem_gtt.o \
> >         i915_gem_tiling.o \
> >         i915_trace_points.o \
> > +       intel_adaptive_backlight.o \
> >         intel_display.o \
> >         intel_crt.o \
> >         intel_lvds.o \
> > diff --git a/drivers/gpu/drm/i915/i915_dma.c
> b/drivers/gpu/drm/i915/i915_dma.c
> > index ba60f3c..c2626d6 100644
> > --- a/drivers/gpu/drm/i915/i915_dma.c
> > +++ b/drivers/gpu/drm/i915/i915_dma.c
> > @@ -828,6 +828,22 @@ static int i915_setparam(struct drm_device *dev,
> void *data,
> >               /* Userspace can use first N regs */
> >               dev_priv->fence_reg_start = param->value;
> >               break;
> > +     case I915_SETPARAM_ADAPTIVE_BACKLIGHT_ENABLE:
> > +             if (INTEL_INFO(dev)->gen == 6) {
> > +                     dev_priv->adaptive_backlight_enabled =
> param->value;
> > +             } else {
> > +                     DRM_ERROR("No adaptive backlight on !GEN6\n");
> > +                     return -EINVAL;
> > +             }
> > +             break;
> > +     case I915_SETPARAM_PANEL_GAMMA:
> > +             if (INTEL_INFO(dev)->gen == 6) {
> > +                     dev_priv->adaptive_backlight_panel_gamma =
> param->value;
> > +             } else {
> > +                     DRM_ERROR("No adaptive backlight on !GEN6\n");
> > +                     return -EINVAL;
> > +             }
> > +             break;
> >       default:
> >               DRM_DEBUG_DRIVER("unknown parameter %d\n",
> >                                       param->param);
> > diff --git a/drivers/gpu/drm/i915/i915_drv.h
> b/drivers/gpu/drm/i915/i915_drv.h
> > index d89f585..f778f93 100644
> > --- a/drivers/gpu/drm/i915/i915_drv.h
> > +++ b/drivers/gpu/drm/i915/i915_drv.h
> > @@ -401,6 +401,13 @@ typedef struct drm_i915_private {
> >       struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */
> >       struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */
> >
> > +     /* Adaptive backlight */
> > +     bool adaptive_backlight_enabled;
> > +     int backlight_correction_level;
> > +     int backlight_correction_count;
> > +     int backlight_correction_direction;
> > +     int adaptive_backlight_panel_gamma;
> > +
> >       /* Feature bits from the VBIOS */
> >       unsigned int int_tv_support:1;
> >       unsigned int lvds_dither:1;
> > @@ -1358,6 +1365,9 @@ extern int i915_restore_state(struct drm_device
> *dev);
> >  extern int i915_save_state(struct drm_device *dev);
> >  extern int i915_restore_state(struct drm_device *dev);
> >
> > +/* intel_adaptive_backlight.c */
> > +extern void intel_adaptive_backlight(struct drm_device *dev, int pipe);
> > +
> >  /* intel_i2c.c */
> >  extern int intel_setup_gmbus(struct drm_device *dev);
> >  extern void intel_teardown_gmbus(struct drm_device *dev);
> > diff --git a/drivers/gpu/drm/i915/i915_irq.c
> b/drivers/gpu/drm/i915/i915_irq.c
> > index afd4e03..948da6b 100644
> > --- a/drivers/gpu/drm/i915/i915_irq.c
> > +++ b/drivers/gpu/drm/i915/i915_irq.c
> > @@ -619,11 +619,15 @@ static irqreturn_t
> ironlake_irq_handler(DRM_IRQ_ARGS)
> >               intel_finish_page_flip_plane(dev, 1);
> >       }
> >
> > -     if (de_iir & DE_PIPEA_VBLANK)
> > +     if (de_iir & DE_PIPEA_VBLANK) {
> >               drm_handle_vblank(dev, 0);
> > +             intel_adaptive_backlight(dev, 0);
> > +     }
> >
> > -     if (de_iir & DE_PIPEB_VBLANK)
> > +     if (de_iir & DE_PIPEB_VBLANK) {
> >               drm_handle_vblank(dev, 1);
> > +             intel_adaptive_backlight(dev, 1);
> > +     }
> >
> >       /* check event from PCH */
> >       if (de_iir & DE_PCH_EVENT) {
> > diff --git a/drivers/gpu/drm/i915/i915_reg.h
> b/drivers/gpu/drm/i915/i915_reg.h
> > index 552264c..2db874d 100644
> > --- a/drivers/gpu/drm/i915/i915_reg.h
> > +++ b/drivers/gpu/drm/i915/i915_reg.h
> > @@ -3587,6 +3587,28 @@
> >  #define  PWM_PIPE_B          (1 << 29)
> >  #define BLC_PWM_CPU_CTL              0x48254
> >
> > +#define BLM_HIST_CTL                 0x48260
> > +#define  ENH_HIST_ENABLE             (1<<31)
> > +#define  ENH_MODIF_TBL_ENABLE                (1<<30)
> > +#define  ENH_PIPE_A_SELECT           (0<<29)
> > +#define  ENH_PIPE_B_SELECT           (1<<29)
> > +#define  ENH_PIPE(pipe) _PIPE(pipe, ENH_PIPE_A_SELECT,
> ENH_PIPE_B_SELECT)
> > +#define  HIST_MODE_YUV                       (0<<24)
> > +#define  HIST_MODE_HSV                       (1<<24)
> > +#define  ENH_MODE_DIRECT             (0<<13)
> > +#define  ENH_MODE_ADDITIVE           (1<<13)
> > +#define  ENH_MODE_MULTIPLICATIVE     (2<<13)
> > +#define  BIN_REGISTER_SET            (1<<11)
> > +#define  ENH_NUM_BINS                        32
> > +
> > +#define BLM_HIST_ENH                 0x48264
> > +
> > +#define BLM_HIST_GUARD_BAND          0x48268
> > +#define  BLM_HIST_INTR_ENABLE                (1<<31)
> > +#define  BLM_HIST_EVENT_STATUS               (1<<30)
> > +#define  BLM_HIST_INTR_DELAY_MASK    (0xFF<<22)
> > +#define  BLM_HIST_INTR_DELAY_SHIFT   22
> > +
> >  #define BLC_PWM_PCH_CTL1     0xc8250
> >  #define  PWM_PCH_ENABLE              (1 << 31)
> >  #define  PWM_POLARITY_ACTIVE_LOW     (1 << 29)
> > diff --git a/drivers/gpu/drm/i915/intel_adaptive_backlight.c
> b/drivers/gpu/drm/i915/intel_adaptive_backlight.c
> > new file mode 100644
> > index 0000000..4234962
> > --- /dev/null
> > +++ b/drivers/gpu/drm/i915/intel_adaptive_backlight.c
> > @@ -0,0 +1,266 @@
> > +/*
> > + * Copyright 2012 The Chromium OS Authors.
> > + * All Rights Reserved.
> > + *
> > + * Permission is hereby granted, free of charge, to any person
> obtaining a
> > + * copy of this software and associated documentation files (the
> > + * "Software"), to deal in the Software without restriction, including
> > + * without limitation the rights to use, copy, modify, merge, publish,
> > + * distribute, sub license, and/or sell copies of the Software, and to
> > + * permit persons to whom the Software is furnished to do so, subject to
> > + * the following conditions:
> > + *
> > + * The above copyright notice and this permission notice (including the
> > + * next paragraph) shall be included in all copies or substantial
> portions
> > + * of the Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> EXPRESS
> > + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> > + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> NON-INFRINGEMENT.
> > + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
> > + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
> CONTRACT,
> > + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
> > + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> > + *
> > + */
> > +
> > +
> > +#include "drmP.h"
> > +#include "drm.h"
> > +#include "i915_drm.h"
> > +#include "i915_drv.h"
> > +#include "i915_reg.h"
> > +#include "intel_drv.h"
> > +#include "intel_fixedpoint.h"
> > +
> > +/*
> > + * This function takes a histogram of buckets as input and determines an
> > + * acceptable target backlight level.
> > + */
> > +static int histogram_find_correction_level(int *levels)
> > +{
> > +     int i, sum = 0;
> > +     int ratio, distortion, prev_distortion = 0, off, final_ratio,
> target;
> > +
> > +     for (i = 0; i < ENH_NUM_BINS; i++)
> > +             sum += levels[i];
> > +
> > +     /* Allow 0.33/256 distortion per pixel, on average */
> > +     target = sum / 3;
> > +
> > +     /* Special case where we only have less than 100 pixels
> > +      * outside of the darkest bin.
> > +      */
> > +     if (sum - levels[0] <= 100)
> > +             return 70;
> > +
> > +     for (ratio = ENH_NUM_BINS - 1; ratio >= 0 ; ratio--) {
> > +             distortion = 0;
> > +             for (i = ratio; i < ENH_NUM_BINS; i++) {
> > +                     int pixel_distortion = (i - ratio)*8;
> > +                     int num_pixels = levels[i];
> > +                     distortion += num_pixels * pixel_distortion;
> > +             }
> > +             if (distortion > target)
> > +                     break;
> > +             else
> > +                     prev_distortion = distortion;
> > +     }
> > +
> > +     ratio++;
> > +
> > +     /* If we're not exactly at the border between two buckets,
> extrapolate
> > +      * to get 3 extra bits of accuracy.
> > +      */
> > +     if (distortion - prev_distortion)
> > +             off = 8 * (target - prev_distortion) /
> > +                   (distortion - prev_distortion);
> > +     else
> > +             off = 0;
> > +
> > +     final_ratio = ratio * 255 / 31 + off;
> > +
> > +     if (final_ratio > 255)
> > +             final_ratio = 255;
> > +
> > +     /* Never aim for less than 50% of the total backlight */
> > +     if (final_ratio < 128)
> > +             final_ratio = 128;
> > +
> > +     return final_ratio;
> > +}
> > +
> > +static void histogram_get_levels(struct drm_device *dev, int pipe, int
> *levels)
> > +{
> > +     drm_i915_private_t *dev_priv = dev->dev_private;
> > +     int i;
> > +
> > +     for (i = 0; i < ENH_NUM_BINS; i++) {
> > +             I915_WRITE(BLM_HIST_CTL, ENH_HIST_ENABLE |
> > +                                      ENH_MODIF_TBL_ENABLE |
> > +                                      ENH_PIPE(pipe) |
> > +                                      HIST_MODE_YUV |
> > +                                      ENH_MODE_ADDITIVE |
> > +                                      i);
> > +             levels[i] = I915_READ(BLM_HIST_ENH);
> > +     }
> > +}
> > +
> > +static void histogram_set_levels(struct drm_device *dev, int pipe, int
> *levels)
> > +{
> > +     drm_i915_private_t *dev_priv = dev->dev_private;
> > +     int i;
> > +
> > +     for (i = 0; i < ENH_NUM_BINS; i++) {
> > +             I915_WRITE(BLM_HIST_CTL, ENH_HIST_ENABLE |
> > +                                      ENH_MODIF_TBL_ENABLE |
> > +                                      ENH_PIPE(pipe) |
> > +                                      HIST_MODE_YUV |
> > +                                      ENH_MODE_ADDITIVE |
> > +                                      BIN_REGISTER_SET |
> > +                                      i);
> > +             I915_WRITE(BLM_HIST_ENH, levels[i]);
> > +     }
> > +}
> > +
> > +/*
> > + * This function computes the backlight correction level for an
> acceptable
> > + * distortion and fills up the correction bins adequately.
> > + */
> > +static void
> > +adaptive_backlight_compute_correction(struct drm_device *dev, int
> *levels)
> > +{
> > +     drm_i915_private_t *dev_priv = dev->dev_private;
> > +     int correction_level;
> > +     int i, multiplier, one_over_gamma;
> > +
> > +     /* Find the correction level for an acceptable distortion */
> > +     correction_level = histogram_find_correction_level(levels);
> > +
> > +     /* If we're not yet at our correction target, we need to decide by
> how
> > +      * much to move.
> > +      */
> > +     if (dev_priv->backlight_correction_level != correction_level) {
> > +             int delta, direction;
> > +
> > +             direction = (correction_level >
> > +                          dev_priv->backlight_correction_level);
> > +
> > +             if (direction == dev_priv->backlight_correction_direction)
> {
> > +                     dev_priv->backlight_correction_count++;
> > +             } else {
> > +                     dev_priv->backlight_correction_count = 0;
> > +                     dev_priv->backlight_correction_direction =
> direction;
> > +             }
> > +
> > +             delta = abs(correction_level -
> > +                         dev_priv->backlight_correction_level)/4;
> > +
> > +             if (delta < 1)
> > +                     delta = 1;
> > +
> > +             /* For increasing the brightness, we do it instantly.
> > +              * For lowering the brightness, we require at least 10
> frames
> > +              * below the current value. This avoids ping-ponging of the
> > +              * backlight level.
> > +              *
> > +              * We also never increase the backlight by more than 6% per
> > +              * frame, and never lower it by more than 3% per frame,
> because
> > +              * the backlight needs time to adjust and the LCD
> correction
> > +              * would be "ahead" otherwise.
> > +              */
> > +             if (correction_level >
> dev_priv->backlight_correction_level) {
> > +                     if (delta > 15)
> > +                             delta = 15;
> > +                     dev_priv->backlight_correction_level += delta;
> > +             } else if ((dev_priv->backlight_correction_count > 10) &&
> > +                (correction_level <
> dev_priv->backlight_correction_level)) {
> > +                     if (delta > 7)
> > +                             delta = 7;
> > +                     dev_priv->backlight_correction_level -= delta;
> > +             }
> > +     }
> > +
> > +     /* We need to invert the gamma correction of the LCD values,
> > +      * but not of the backlight which is linear.
> > +      */
> > +     one_over_gamma = intel_fixed_div(FIXED_ONE,
> > +                             dev_priv->adaptive_backlight_panel_gamma);
> > +     multiplier = intel_fixed_pow(dev_priv->backlight_correction_level
> * 256,
> > +                                  one_over_gamma);
> > +
> > +     for (i = 0; i < ENH_NUM_BINS; i++) {
> > +             int base_value = i * 8 * 4;
> > +             levels[i] = base_value - base_value * multiplier / 65536;
> > +     }
> > +}
> > +
> > +/*
> > + * A quick note about the adaptive backlight implementation:
> > + * If we let it run as designed, it will generate a lot of interrupts
> which
> > + * tends to wake the CPU up and waste power. This is a bad idea for a
> power
> > + * saving feature. Instead, we couple it to the vblank interrupt since
> that
> > + * means we drew something. This means that we do not react to
> non-vsynced GL
> > + * updates, or updates to the front buffer, and also adds a little bit
> of
> > + * extra latency. But it is an acceptable tradeoff to make.
> > + */
> > +void intel_adaptive_backlight(struct drm_device *dev, int
> pipe_vblank_event)
> > +{
> > +     drm_i915_private_t *dev_priv = dev->dev_private;
> > +     int levels[ENH_NUM_BINS];
> > +     int pipe;
> > +     struct drm_connector *connector;
> > +     struct intel_crtc *intel_crtc;
> > +
> > +     if (!dev_priv->adaptive_backlight_enabled)
> > +             return;
> > +
> > +     /* Find the connector */
> > +     if (dev_priv->int_lvds_connector)
> > +             connector = dev_priv->int_lvds_connector;
> > +     else if (dev_priv->int_edp_connector)
> > +             connector = dev_priv->int_edp_connector;
> > +     else
> > +             return;
> > +
> > +     if (!connector)
> > +             return;
> > +
> > +     if (!connector->encoder)
> > +             return;
> > +
> > +     if (!connector->encoder->crtc)
> > +             return;
> > +
> > +     /* Find the pipe for the panel. */
> > +     intel_crtc = to_intel_crtc(connector->encoder->crtc);
> > +     pipe = intel_crtc->pipe;
> > +
> > +     /* The callback happens for both pipe A & B. Now that we know which
> > +      * pipe we're doing adaptive backlight on, check that it's the
> right
> > +      * one. Bail if it isn't.
> > +      */
> > +     if (pipe != pipe_vblank_event)
> > +             return;
> > +
> > +     /* Return if no event. */
> > +     if (I915_READ(BLM_HIST_GUARD_BAND) & BLM_HIST_EVENT_STATUS == 0)
> > +             return;
> > +
> > +     /* Make sure we ack the previous event. Even though we do not get
> the
> > +      * IRQs (see above explanation), we must still ack the events
> otherwise
> > +      * the histogram data doesn't get updated any more.
> > +      */
> > +     I915_WRITE(BLM_HIST_GUARD_BAND, BLM_HIST_INTR_ENABLE |
> > +                                     BLM_HIST_EVENT_STATUS |
> > +                                     (1 << BLM_HIST_INTR_DELAY_SHIFT));
> > +
> > +     histogram_get_levels(dev, pipe, levels);
> > +
> > +     adaptive_backlight_compute_correction(dev, levels);
> > +
> > +     histogram_set_levels(dev, pipe, levels);
> > +
> > +     intel_panel_set_backlight(dev, dev_priv->backlight_level);
> > +}
> > +
> > diff --git a/drivers/gpu/drm/i915/intel_fixedpoint.h
> b/drivers/gpu/drm/i915/intel_fixedpoint.h
> > new file mode 100644
> > index 0000000..fff5f0b
> > --- /dev/null
> > +++ b/drivers/gpu/drm/i915/intel_fixedpoint.h
> > @@ -0,0 +1,140 @@
> > +/*
> > + * Copyright 2012 The Chromium OS Authors.
> > + * All Rights Reserved.
> > + *
> > + * Permission is hereby granted, free of charge, to any person
> obtaining a
> > + * copy of this software and associated documentation files (the
> > + * "Software"), to deal in the Software without restriction, including
> > + * without limitation the rights to use, copy, modify, merge, publish,
> > + * distribute, sub license, and/or sell copies of the Software, and to
> > + * permit persons to whom the Software is furnished to do so, subject to
> > + * the following conditions:
> > + *
> > + * The above copyright notice and this permission notice (including the
> > + * next paragraph) shall be included in all copies or substantial
> portions
> > + * of the Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> EXPRESS
> > + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> > + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> NON-INFRINGEMENT.
> > + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
> > + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
> CONTRACT,
> > + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
> > + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> > + *
> > + */
> > +
> > +/*
> > + * The backlight is corrected in linear space. However the LCD
> correction is
> > + * corrected in gama space. So to be able to compute the correction
> value for
> > + * the LCD, we have to compute the inverse gamma. To do so, we carry
> this
> > + * small fixed point module which allows us to use pow() to compute
> inverse
> > + * gamma.
> > + *
> > + * The fixed point format used here is 16.16.
> > + */
> > +
> > +/* intel_fixed_exp_tbl[x*32] = exp(x) * 65536 */
> > +static const int intel_fixed_exp_tbl[33] = {
> > +0x00010000, 0x00010820, 0x00011083, 0x00011929, 0x00012216, 0x00012b4b,
> > +0x000134cc, 0x00013e99, 0x000148b6, 0x00015325, 0x00015de9, 0x00016905,
> > +0x0001747a, 0x0001804d, 0x00018c80, 0x00019916, 0x0001a613, 0x0001b378,
> > +0x0001c14b, 0x0001cf8e, 0x0001de45, 0x0001ed74, 0x0001fd1e, 0x00020d47,
> > +0x00021df4, 0x00022f28, 0x000240e8, 0x00025338, 0x0002661d, 0x0002799b,
> > +0x00028db8, 0x0002a278, 0x0002b7e1
> > +};
> > +
> > +/* intel_fixed_log_tbl[x*32] = log(x) * 65536 */
> > +static const int intel_fixed_log_tbl[33] = {
> > +0x80000000, 0xfffc88c6, 0xfffd3a38, 0xfffda204, 0xfffdebaa, 0xfffe24ca,
> > +0xfffe5376, 0xfffe7aed, 0xfffe9d1c, 0xfffebb43, 0xfffed63c, 0xfffeeea2,
> > +0xffff04e8, 0xffff1966, 0xffff2c5f, 0xffff3e08, 0xffff4e8e, 0xffff5e13,
> > +0xffff6cb5, 0xffff7a8c, 0xffff87ae, 0xffff942b, 0xffffa014, 0xffffab75,
> > +0xffffb65a, 0xffffc0ce, 0xffffcad8, 0xffffd481, 0xffffddd1, 0xffffe6cd,
> > +0xffffef7a, 0xfffff7df, 0xffffffff
> > +};
> > +
> > +/* e * 65536 */
> > +#define FIXED_E (intel_fixed_exp_tbl[32])
> > +/* 1 * 65536 */
> > +#define FIXED_ONE 65536
> > +
> > +static int intel_fixed_mul(int a, int b)
> > +{
> > +     return (int) ((int64_t)a * (int64_t)b / 65536LL);
> > +}
> > +
> > +static int intel_fixed_div(int a, int b)
> > +{
> > +     return (int) ((int64_t)a * 65536LL / (int64_t)b);
> > +}
> > +
> > +/*
> > + * Approximate fixed point log function.
> > + * Only works for inputs in [0,1[
> > + */
> > +static int intel_fixed_log(int val)
> > +{
> > +     int index = val * 32 / FIXED_ONE;
> > +     int remainder = (val & 0x7ff) << 5;
> > +     int v1 = intel_fixed_log_tbl[index];
> > +     int v2 = intel_fixed_log_tbl[index+1];
> > +     int final = v1 + intel_fixed_mul(v2 - v1, remainder);
> > +     return final;
> > +}
> > +
> > +/*
> > + * Approximate fixed point exp function.
> > + */
> > +static int intel_fixed_exp(int val)
> > +{
> > +     int count = 0;
> > +     int index, remainder;
> > +     int int_part = FIXED_ONE, frac_part;
> > +     int i, v, v1, v2;
> > +
> > +     while (val < 0) {
> > +             val += FIXED_ONE;
> > +             count--;
> > +     }
> > +
> > +     while (val > FIXED_ONE) {
> > +             val -= FIXED_ONE;
> > +             count++;
> > +     }
> > +
> > +     index = val * 32 / FIXED_ONE;
> > +     remainder = (val & 0x7ff) << 5;
> > +
> > +     v1 = intel_fixed_exp_tbl[index];
> > +     v2 = intel_fixed_exp_tbl[index+1];
> > +     frac_part = v1 + intel_fixed_mul(v2 - v1, remainder);
> > +
> > +     if (count < 0) {
> > +             for (i = 0; i < -count; i++)
> > +                     int_part = intel_fixed_mul(int_part, FIXED_E);
> > +
> > +             v = intel_fixed_div(frac_part, int_part);
> > +     } else {
> > +             for (i = 0; i < count; i++)
> > +                     int_part = intel_fixed_mul(int_part, FIXED_E);
> > +
> > +             v = intel_fixed_mul(frac_part, int_part);
> > +     }
> > +     return (v >= 0) ? v : 0;
> > +}
> > +
> > +/*
> > + * Approximate fixed point pow function.
> > + * Only works for x in [0,1[
> > + */
> > +static int intel_fixed_pow(int x, int y)
> > +{
> > +     int e, p, r;
> > +     e = intel_fixed_log(x);
> > +     p = intel_fixed_mul(e, y);
> > +     r = intel_fixed_exp(p);
> > +     return r;
> > +}
> > +
> > +
> > diff --git a/drivers/gpu/drm/i915/intel_panel.c
> b/drivers/gpu/drm/i915/intel_panel.c
> > index 3f9249b..ae95163 100644
> > --- a/drivers/gpu/drm/i915/intel_panel.c
> > +++ b/drivers/gpu/drm/i915/intel_panel.c
> > @@ -251,6 +251,8 @@ static void
> intel_panel_actually_set_backlight(struct drm_device *dev, u32 level
> >       struct drm_i915_private *dev_priv = dev->dev_private;
> >       u32 tmp;
> >
> > +     level = level * dev_priv->backlight_correction_level >> 8;
> > +
> >       DRM_DEBUG_DRIVER("set backlight PWM = %d\n", level);
> >
> >       if (HAS_PCH_SPLIT(dev))
> > @@ -306,6 +308,10 @@ static void intel_panel_init_backlight(struct
> drm_device *dev)
> >
> >       dev_priv->backlight_level = intel_panel_get_backlight(dev);
> >       dev_priv->backlight_enabled = dev_priv->backlight_level != 0;
> > +     dev_priv->adaptive_backlight_enabled = 0;
> > +     /* 2.2 as 16.16 fixed point */
> > +     dev_priv->adaptive_backlight_panel_gamma = 144179;
> > +     dev_priv->backlight_correction_level = 256;
> >  }
> >
> >  enum drm_connector_status
> > diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h
> > index da929bb..70c9cfc 100644
> > --- a/include/drm/i915_drm.h
> > +++ b/include/drm/i915_drm.h
> > @@ -309,6 +309,8 @@ typedef struct drm_i915_getparam {
> >  #define I915_SETPARAM_TEX_LRU_LOG_GRANULARITY             2
> >  #define I915_SETPARAM_ALLOW_BATCHBUFFER                   3
> >  #define I915_SETPARAM_NUM_USED_FENCES                     4
> > +#define I915_SETPARAM_ADAPTIVE_BACKLIGHT_ENABLE           5
> > +#define I915_SETPARAM_PANEL_GAMMA                         6
> >
> >  typedef struct drm_i915_setparam {
> >       int param;
> > --
> > 1.7.5.3.367.ga9930
> >
> > _______________________________________________
> > Intel-gfx mailing list
> > Intel-gfx@lists.freedesktop.org
> > http://lists.freedesktop.org/mailman/listinfo/intel-gfx
>

[-- Attachment #1.2: Type: text/html, Size: 32533 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH] drm/i915: Adaptive backlight support
  2012-06-21  9:34 ` Daniel Vetter
@ 2012-06-21 22:18   ` Stéphane Marchesin
  0 siblings, 0 replies; 5+ messages in thread
From: Stéphane Marchesin @ 2012-06-21 22:18 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: intel-gfx


[-- Attachment #1.1: Type: text/plain, Size: 27756 bytes --]

On Thu, Jun 21, 2012 at 2:34 AM, Daniel Vetter <daniel@ffwll.ch> wrote:

> On Wed, Jun 20, 2012 at 03:12:35PM -0700, Stéphane Marchesin wrote:
> > This is an initial implementation of i915 adaptive backlight support.
> > The intended use for the adaptive backlight is to generate interrupts
> > whenever the luminance of the screen changes by some thresholds. The
> > main caveat with that implementation is that those additional
> > interrupts will wake up the CPU and consume more power. Instead, we
> > hook into the vblank handler and handle it from there. This makes the
> > implementation a little less intuitive but a lot more efficient.
> > We also need to compute the gamma correction from the interrupt
> > handler so we do this with a (new) fixed point module.
> >
> > Change-Id: I9b9631cacc7d90e2801a542a3789118521bc25f0
> > Signed-off-by: Stéphane Marchesin <marcheu@chromium.org>
>
> A few quick comments:
> - I don't like setparam for this too much. Imo exposing this as an
>  lvds/eDP connector property makes more sense, and also makes the feature
>  more accessible to mere mortals. btw I think we should do the same with
>  the downclock stuff and expose a panel_downclock_freq plus a
>  panel_downclock enable which is simply initialized at driver load time
>  with the defaults, but could be freely changed afterwards.
>

Agreed, I realized a property would be better after sending that patch :)



> - With connector properties we could also expose tuneables in an easy way,
>  at least partially addressing Jani's comment (like the display gamma and
>  maybe also other parameters).
>

Agreed.


> - We need to somehow cope with front-rendering X. Yeah, I know this sucks
>  and all, but such is life. We already mark framebuffers as busys when
>  rendering into them directly, I guess we could hook into to that either
>  disable this feature until the next pageflip. Or switch to a different
>  mode. The disable needs to be gradually, obviously.
>

Ok, that's probably the toughest item on that list. I'll dig into that.



> - I'm unsure whether we shouldn't move the fixed-point stuff to a common
>  place. Wrt all the fixed-point constant I think a little macro to
>  convert double to 16.16 fixed point would massively help code
>  readability.
>

True, I'll do that.

Stéphane


> Yours, Daniel
>
> > ---
> >  drivers/gpu/drm/i915/Makefile                   |    1 +
> >  drivers/gpu/drm/i915/i915_dma.c                 |   16 ++
> >  drivers/gpu/drm/i915/i915_drv.h                 |   10 +
> >  drivers/gpu/drm/i915/i915_irq.c                 |    8 +-
> >  drivers/gpu/drm/i915/i915_reg.h                 |   22 ++
> >  drivers/gpu/drm/i915/intel_adaptive_backlight.c |  266
> +++++++++++++++++++++++
> >  drivers/gpu/drm/i915/intel_fixedpoint.h         |  140 ++++++++++++
> >  drivers/gpu/drm/i915/intel_panel.c              |    6 +
> >  include/drm/i915_drm.h                          |    2 +
> >  9 files changed, 469 insertions(+), 2 deletions(-)
> >  create mode 100644 drivers/gpu/drm/i915/intel_adaptive_backlight.c
> >  create mode 100644 drivers/gpu/drm/i915/intel_fixedpoint.h
> >
> > diff --git a/drivers/gpu/drm/i915/Makefile
> b/drivers/gpu/drm/i915/Makefile
> > index ce7fc77..5c125c3 100644
> > --- a/drivers/gpu/drm/i915/Makefile
> > +++ b/drivers/gpu/drm/i915/Makefile
> > @@ -13,6 +13,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \
> >         i915_gem_gtt.o \
> >         i915_gem_tiling.o \
> >         i915_trace_points.o \
> > +       intel_adaptive_backlight.o \
> >         intel_display.o \
> >         intel_crt.o \
> >         intel_lvds.o \
> > diff --git a/drivers/gpu/drm/i915/i915_dma.c
> b/drivers/gpu/drm/i915/i915_dma.c
> > index ba60f3c..c2626d6 100644
> > --- a/drivers/gpu/drm/i915/i915_dma.c
> > +++ b/drivers/gpu/drm/i915/i915_dma.c
> > @@ -828,6 +828,22 @@ static int i915_setparam(struct drm_device *dev,
> void *data,
> >               /* Userspace can use first N regs */
> >               dev_priv->fence_reg_start = param->value;
> >               break;
> > +     case I915_SETPARAM_ADAPTIVE_BACKLIGHT_ENABLE:
> > +             if (INTEL_INFO(dev)->gen == 6) {
> > +                     dev_priv->adaptive_backlight_enabled =
> param->value;
> > +             } else {
> > +                     DRM_ERROR("No adaptive backlight on !GEN6\n");
> > +                     return -EINVAL;
> > +             }
> > +             break;
> > +     case I915_SETPARAM_PANEL_GAMMA:
> > +             if (INTEL_INFO(dev)->gen == 6) {
> > +                     dev_priv->adaptive_backlight_panel_gamma =
> param->value;
> > +             } else {
> > +                     DRM_ERROR("No adaptive backlight on !GEN6\n");
> > +                     return -EINVAL;
> > +             }
> > +             break;
> >       default:
> >               DRM_DEBUG_DRIVER("unknown parameter %d\n",
> >                                       param->param);
> > diff --git a/drivers/gpu/drm/i915/i915_drv.h
> b/drivers/gpu/drm/i915/i915_drv.h
> > index d89f585..f778f93 100644
> > --- a/drivers/gpu/drm/i915/i915_drv.h
> > +++ b/drivers/gpu/drm/i915/i915_drv.h
> > @@ -401,6 +401,13 @@ typedef struct drm_i915_private {
> >       struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */
> >       struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */
> >
> > +     /* Adaptive backlight */
> > +     bool adaptive_backlight_enabled;
> > +     int backlight_correction_level;
> > +     int backlight_correction_count;
> > +     int backlight_correction_direction;
> > +     int adaptive_backlight_panel_gamma;
> > +
> >       /* Feature bits from the VBIOS */
> >       unsigned int int_tv_support:1;
> >       unsigned int lvds_dither:1;
> > @@ -1358,6 +1365,9 @@ extern int i915_restore_state(struct drm_device
> *dev);
> >  extern int i915_save_state(struct drm_device *dev);
> >  extern int i915_restore_state(struct drm_device *dev);
> >
> > +/* intel_adaptive_backlight.c */
> > +extern void intel_adaptive_backlight(struct drm_device *dev, int pipe);
> > +
> >  /* intel_i2c.c */
> >  extern int intel_setup_gmbus(struct drm_device *dev);
> >  extern void intel_teardown_gmbus(struct drm_device *dev);
> > diff --git a/drivers/gpu/drm/i915/i915_irq.c
> b/drivers/gpu/drm/i915/i915_irq.c
> > index afd4e03..948da6b 100644
> > --- a/drivers/gpu/drm/i915/i915_irq.c
> > +++ b/drivers/gpu/drm/i915/i915_irq.c
> > @@ -619,11 +619,15 @@ static irqreturn_t
> ironlake_irq_handler(DRM_IRQ_ARGS)
> >               intel_finish_page_flip_plane(dev, 1);
> >       }
> >
> > -     if (de_iir & DE_PIPEA_VBLANK)
> > +     if (de_iir & DE_PIPEA_VBLANK) {
> >               drm_handle_vblank(dev, 0);
> > +             intel_adaptive_backlight(dev, 0);
> > +     }
> >
> > -     if (de_iir & DE_PIPEB_VBLANK)
> > +     if (de_iir & DE_PIPEB_VBLANK) {
> >               drm_handle_vblank(dev, 1);
> > +             intel_adaptive_backlight(dev, 1);
> > +     }
> >
> >       /* check event from PCH */
> >       if (de_iir & DE_PCH_EVENT) {
> > diff --git a/drivers/gpu/drm/i915/i915_reg.h
> b/drivers/gpu/drm/i915/i915_reg.h
> > index 552264c..2db874d 100644
> > --- a/drivers/gpu/drm/i915/i915_reg.h
> > +++ b/drivers/gpu/drm/i915/i915_reg.h
> > @@ -3587,6 +3587,28 @@
> >  #define  PWM_PIPE_B          (1 << 29)
> >  #define BLC_PWM_CPU_CTL              0x48254
> >
> > +#define BLM_HIST_CTL                 0x48260
> > +#define  ENH_HIST_ENABLE             (1<<31)
> > +#define  ENH_MODIF_TBL_ENABLE                (1<<30)
> > +#define  ENH_PIPE_A_SELECT           (0<<29)
> > +#define  ENH_PIPE_B_SELECT           (1<<29)
> > +#define  ENH_PIPE(pipe) _PIPE(pipe, ENH_PIPE_A_SELECT,
> ENH_PIPE_B_SELECT)
> > +#define  HIST_MODE_YUV                       (0<<24)
> > +#define  HIST_MODE_HSV                       (1<<24)
> > +#define  ENH_MODE_DIRECT             (0<<13)
> > +#define  ENH_MODE_ADDITIVE           (1<<13)
> > +#define  ENH_MODE_MULTIPLICATIVE     (2<<13)
> > +#define  BIN_REGISTER_SET            (1<<11)
> > +#define  ENH_NUM_BINS                        32
> > +
> > +#define BLM_HIST_ENH                 0x48264
> > +
> > +#define BLM_HIST_GUARD_BAND          0x48268
> > +#define  BLM_HIST_INTR_ENABLE                (1<<31)
> > +#define  BLM_HIST_EVENT_STATUS               (1<<30)
> > +#define  BLM_HIST_INTR_DELAY_MASK    (0xFF<<22)
> > +#define  BLM_HIST_INTR_DELAY_SHIFT   22
> > +
> >  #define BLC_PWM_PCH_CTL1     0xc8250
> >  #define  PWM_PCH_ENABLE              (1 << 31)
> >  #define  PWM_POLARITY_ACTIVE_LOW     (1 << 29)
> > diff --git a/drivers/gpu/drm/i915/intel_adaptive_backlight.c
> b/drivers/gpu/drm/i915/intel_adaptive_backlight.c
> > new file mode 100644
> > index 0000000..4234962
> > --- /dev/null
> > +++ b/drivers/gpu/drm/i915/intel_adaptive_backlight.c
> > @@ -0,0 +1,266 @@
> > +/*
> > + * Copyright 2012 The Chromium OS Authors.
> > + * All Rights Reserved.
> > + *
> > + * Permission is hereby granted, free of charge, to any person
> obtaining a
> > + * copy of this software and associated documentation files (the
> > + * "Software"), to deal in the Software without restriction, including
> > + * without limitation the rights to use, copy, modify, merge, publish,
> > + * distribute, sub license, and/or sell copies of the Software, and to
> > + * permit persons to whom the Software is furnished to do so, subject to
> > + * the following conditions:
> > + *
> > + * The above copyright notice and this permission notice (including the
> > + * next paragraph) shall be included in all copies or substantial
> portions
> > + * of the Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> EXPRESS
> > + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> > + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> NON-INFRINGEMENT.
> > + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
> > + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
> CONTRACT,
> > + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
> > + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> > + *
> > + */
> > +
> > +
> > +#include "drmP.h"
> > +#include "drm.h"
> > +#include "i915_drm.h"
> > +#include "i915_drv.h"
> > +#include "i915_reg.h"
> > +#include "intel_drv.h"
> > +#include "intel_fixedpoint.h"
> > +
> > +/*
> > + * This function takes a histogram of buckets as input and determines an
> > + * acceptable target backlight level.
> > + */
> > +static int histogram_find_correction_level(int *levels)
> > +{
> > +     int i, sum = 0;
> > +     int ratio, distortion, prev_distortion = 0, off, final_ratio,
> target;
> > +
> > +     for (i = 0; i < ENH_NUM_BINS; i++)
> > +             sum += levels[i];
> > +
> > +     /* Allow 0.33/256 distortion per pixel, on average */
> > +     target = sum / 3;
> > +
> > +     /* Special case where we only have less than 100 pixels
> > +      * outside of the darkest bin.
> > +      */
> > +     if (sum - levels[0] <= 100)
> > +             return 70;
> > +
> > +     for (ratio = ENH_NUM_BINS - 1; ratio >= 0 ; ratio--) {
> > +             distortion = 0;
> > +             for (i = ratio; i < ENH_NUM_BINS; i++) {
> > +                     int pixel_distortion = (i - ratio)*8;
> > +                     int num_pixels = levels[i];
> > +                     distortion += num_pixels * pixel_distortion;
> > +             }
> > +             if (distortion > target)
> > +                     break;
> > +             else
> > +                     prev_distortion = distortion;
> > +     }
> > +
> > +     ratio++;
> > +
> > +     /* If we're not exactly at the border between two buckets,
> extrapolate
> > +      * to get 3 extra bits of accuracy.
> > +      */
> > +     if (distortion - prev_distortion)
> > +             off = 8 * (target - prev_distortion) /
> > +                   (distortion - prev_distortion);
> > +     else
> > +             off = 0;
> > +
> > +     final_ratio = ratio * 255 / 31 + off;
> > +
> > +     if (final_ratio > 255)
> > +             final_ratio = 255;
> > +
> > +     /* Never aim for less than 50% of the total backlight */
> > +     if (final_ratio < 128)
> > +             final_ratio = 128;
> > +
> > +     return final_ratio;
> > +}
> > +
> > +static void histogram_get_levels(struct drm_device *dev, int pipe, int
> *levels)
> > +{
> > +     drm_i915_private_t *dev_priv = dev->dev_private;
> > +     int i;
> > +
> > +     for (i = 0; i < ENH_NUM_BINS; i++) {
> > +             I915_WRITE(BLM_HIST_CTL, ENH_HIST_ENABLE |
> > +                                      ENH_MODIF_TBL_ENABLE |
> > +                                      ENH_PIPE(pipe) |
> > +                                      HIST_MODE_YUV |
> > +                                      ENH_MODE_ADDITIVE |
> > +                                      i);
> > +             levels[i] = I915_READ(BLM_HIST_ENH);
> > +     }
> > +}
> > +
> > +static void histogram_set_levels(struct drm_device *dev, int pipe, int
> *levels)
> > +{
> > +     drm_i915_private_t *dev_priv = dev->dev_private;
> > +     int i;
> > +
> > +     for (i = 0; i < ENH_NUM_BINS; i++) {
> > +             I915_WRITE(BLM_HIST_CTL, ENH_HIST_ENABLE |
> > +                                      ENH_MODIF_TBL_ENABLE |
> > +                                      ENH_PIPE(pipe) |
> > +                                      HIST_MODE_YUV |
> > +                                      ENH_MODE_ADDITIVE |
> > +                                      BIN_REGISTER_SET |
> > +                                      i);
> > +             I915_WRITE(BLM_HIST_ENH, levels[i]);
> > +     }
> > +}
> > +
> > +/*
> > + * This function computes the backlight correction level for an
> acceptable
> > + * distortion and fills up the correction bins adequately.
> > + */
> > +static void
> > +adaptive_backlight_compute_correction(struct drm_device *dev, int
> *levels)
> > +{
> > +     drm_i915_private_t *dev_priv = dev->dev_private;
> > +     int correction_level;
> > +     int i, multiplier, one_over_gamma;
> > +
> > +     /* Find the correction level for an acceptable distortion */
> > +     correction_level = histogram_find_correction_level(levels);
> > +
> > +     /* If we're not yet at our correction target, we need to decide by
> how
> > +      * much to move.
> > +      */
> > +     if (dev_priv->backlight_correction_level != correction_level) {
> > +             int delta, direction;
> > +
> > +             direction = (correction_level >
> > +                          dev_priv->backlight_correction_level);
> > +
> > +             if (direction == dev_priv->backlight_correction_direction)
> {
> > +                     dev_priv->backlight_correction_count++;
> > +             } else {
> > +                     dev_priv->backlight_correction_count = 0;
> > +                     dev_priv->backlight_correction_direction =
> direction;
> > +             }
> > +
> > +             delta = abs(correction_level -
> > +                         dev_priv->backlight_correction_level)/4;
> > +
> > +             if (delta < 1)
> > +                     delta = 1;
> > +
> > +             /* For increasing the brightness, we do it instantly.
> > +              * For lowering the brightness, we require at least 10
> frames
> > +              * below the current value. This avoids ping-ponging of the
> > +              * backlight level.
> > +              *
> > +              * We also never increase the backlight by more than 6% per
> > +              * frame, and never lower it by more than 3% per frame,
> because
> > +              * the backlight needs time to adjust and the LCD
> correction
> > +              * would be "ahead" otherwise.
> > +              */
> > +             if (correction_level >
> dev_priv->backlight_correction_level) {
> > +                     if (delta > 15)
> > +                             delta = 15;
> > +                     dev_priv->backlight_correction_level += delta;
> > +             } else if ((dev_priv->backlight_correction_count > 10) &&
> > +                (correction_level <
> dev_priv->backlight_correction_level)) {
> > +                     if (delta > 7)
> > +                             delta = 7;
> > +                     dev_priv->backlight_correction_level -= delta;
> > +             }
> > +     }
> > +
> > +     /* We need to invert the gamma correction of the LCD values,
> > +      * but not of the backlight which is linear.
> > +      */
> > +     one_over_gamma = intel_fixed_div(FIXED_ONE,
> > +                             dev_priv->adaptive_backlight_panel_gamma);
> > +     multiplier = intel_fixed_pow(dev_priv->backlight_correction_level
> * 256,
> > +                                  one_over_gamma);
> > +
> > +     for (i = 0; i < ENH_NUM_BINS; i++) {
> > +             int base_value = i * 8 * 4;
> > +             levels[i] = base_value - base_value * multiplier / 65536;
> > +     }
> > +}
> > +
> > +/*
> > + * A quick note about the adaptive backlight implementation:
> > + * If we let it run as designed, it will generate a lot of interrupts
> which
> > + * tends to wake the CPU up and waste power. This is a bad idea for a
> power
> > + * saving feature. Instead, we couple it to the vblank interrupt since
> that
> > + * means we drew something. This means that we do not react to
> non-vsynced GL
> > + * updates, or updates to the front buffer, and also adds a little bit
> of
> > + * extra latency. But it is an acceptable tradeoff to make.
> > + */
> > +void intel_adaptive_backlight(struct drm_device *dev, int
> pipe_vblank_event)
> > +{
> > +     drm_i915_private_t *dev_priv = dev->dev_private;
> > +     int levels[ENH_NUM_BINS];
> > +     int pipe;
> > +     struct drm_connector *connector;
> > +     struct intel_crtc *intel_crtc;
> > +
> > +     if (!dev_priv->adaptive_backlight_enabled)
> > +             return;
> > +
> > +     /* Find the connector */
> > +     if (dev_priv->int_lvds_connector)
> > +             connector = dev_priv->int_lvds_connector;
> > +     else if (dev_priv->int_edp_connector)
> > +             connector = dev_priv->int_edp_connector;
> > +     else
> > +             return;
> > +
> > +     if (!connector)
> > +             return;
> > +
> > +     if (!connector->encoder)
> > +             return;
> > +
> > +     if (!connector->encoder->crtc)
> > +             return;
> > +
> > +     /* Find the pipe for the panel. */
> > +     intel_crtc = to_intel_crtc(connector->encoder->crtc);
> > +     pipe = intel_crtc->pipe;
> > +
> > +     /* The callback happens for both pipe A & B. Now that we know which
> > +      * pipe we're doing adaptive backlight on, check that it's the
> right
> > +      * one. Bail if it isn't.
> > +      */
> > +     if (pipe != pipe_vblank_event)
> > +             return;
> > +
> > +     /* Return if no event. */
> > +     if (I915_READ(BLM_HIST_GUARD_BAND) & BLM_HIST_EVENT_STATUS == 0)
> > +             return;
> > +
> > +     /* Make sure we ack the previous event. Even though we do not get
> the
> > +      * IRQs (see above explanation), we must still ack the events
> otherwise
> > +      * the histogram data doesn't get updated any more.
> > +      */
> > +     I915_WRITE(BLM_HIST_GUARD_BAND, BLM_HIST_INTR_ENABLE |
> > +                                     BLM_HIST_EVENT_STATUS |
> > +                                     (1 << BLM_HIST_INTR_DELAY_SHIFT));
> > +
> > +     histogram_get_levels(dev, pipe, levels);
> > +
> > +     adaptive_backlight_compute_correction(dev, levels);
> > +
> > +     histogram_set_levels(dev, pipe, levels);
> > +
> > +     intel_panel_set_backlight(dev, dev_priv->backlight_level);
> > +}
> > +
> > diff --git a/drivers/gpu/drm/i915/intel_fixedpoint.h
> b/drivers/gpu/drm/i915/intel_fixedpoint.h
> > new file mode 100644
> > index 0000000..fff5f0b
> > --- /dev/null
> > +++ b/drivers/gpu/drm/i915/intel_fixedpoint.h
> > @@ -0,0 +1,140 @@
> > +/*
> > + * Copyright 2012 The Chromium OS Authors.
> > + * All Rights Reserved.
> > + *
> > + * Permission is hereby granted, free of charge, to any person
> obtaining a
> > + * copy of this software and associated documentation files (the
> > + * "Software"), to deal in the Software without restriction, including
> > + * without limitation the rights to use, copy, modify, merge, publish,
> > + * distribute, sub license, and/or sell copies of the Software, and to
> > + * permit persons to whom the Software is furnished to do so, subject to
> > + * the following conditions:
> > + *
> > + * The above copyright notice and this permission notice (including the
> > + * next paragraph) shall be included in all copies or substantial
> portions
> > + * of the Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> EXPRESS
> > + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> > + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> NON-INFRINGEMENT.
> > + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
> > + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
> CONTRACT,
> > + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
> > + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> > + *
> > + */
> > +
> > +/*
> > + * The backlight is corrected in linear space. However the LCD
> correction is
> > + * corrected in gama space. So to be able to compute the correction
> value for
> > + * the LCD, we have to compute the inverse gamma. To do so, we carry
> this
> > + * small fixed point module which allows us to use pow() to compute
> inverse
> > + * gamma.
> > + *
> > + * The fixed point format used here is 16.16.
> > + */
> > +
> > +/* intel_fixed_exp_tbl[x*32] = exp(x) * 65536 */
> > +static const int intel_fixed_exp_tbl[33] = {
> > +0x00010000, 0x00010820, 0x00011083, 0x00011929, 0x00012216, 0x00012b4b,
> > +0x000134cc, 0x00013e99, 0x000148b6, 0x00015325, 0x00015de9, 0x00016905,
> > +0x0001747a, 0x0001804d, 0x00018c80, 0x00019916, 0x0001a613, 0x0001b378,
> > +0x0001c14b, 0x0001cf8e, 0x0001de45, 0x0001ed74, 0x0001fd1e, 0x00020d47,
> > +0x00021df4, 0x00022f28, 0x000240e8, 0x00025338, 0x0002661d, 0x0002799b,
> > +0x00028db8, 0x0002a278, 0x0002b7e1
> > +};
> > +
> > +/* intel_fixed_log_tbl[x*32] = log(x) * 65536 */
> > +static const int intel_fixed_log_tbl[33] = {
> > +0x80000000, 0xfffc88c6, 0xfffd3a38, 0xfffda204, 0xfffdebaa, 0xfffe24ca,
> > +0xfffe5376, 0xfffe7aed, 0xfffe9d1c, 0xfffebb43, 0xfffed63c, 0xfffeeea2,
> > +0xffff04e8, 0xffff1966, 0xffff2c5f, 0xffff3e08, 0xffff4e8e, 0xffff5e13,
> > +0xffff6cb5, 0xffff7a8c, 0xffff87ae, 0xffff942b, 0xffffa014, 0xffffab75,
> > +0xffffb65a, 0xffffc0ce, 0xffffcad8, 0xffffd481, 0xffffddd1, 0xffffe6cd,
> > +0xffffef7a, 0xfffff7df, 0xffffffff
> > +};
> > +
> > +/* e * 65536 */
> > +#define FIXED_E (intel_fixed_exp_tbl[32])
> > +/* 1 * 65536 */
> > +#define FIXED_ONE 65536
> > +
> > +static int intel_fixed_mul(int a, int b)
> > +{
> > +     return (int) ((int64_t)a * (int64_t)b / 65536LL);
> > +}
> > +
> > +static int intel_fixed_div(int a, int b)
> > +{
> > +     return (int) ((int64_t)a * 65536LL / (int64_t)b);
> > +}
> > +
> > +/*
> > + * Approximate fixed point log function.
> > + * Only works for inputs in [0,1[
> > + */
> > +static int intel_fixed_log(int val)
> > +{
> > +     int index = val * 32 / FIXED_ONE;
> > +     int remainder = (val & 0x7ff) << 5;
> > +     int v1 = intel_fixed_log_tbl[index];
> > +     int v2 = intel_fixed_log_tbl[index+1];
> > +     int final = v1 + intel_fixed_mul(v2 - v1, remainder);
> > +     return final;
> > +}
> > +
> > +/*
> > + * Approximate fixed point exp function.
> > + */
> > +static int intel_fixed_exp(int val)
> > +{
> > +     int count = 0;
> > +     int index, remainder;
> > +     int int_part = FIXED_ONE, frac_part;
> > +     int i, v, v1, v2;
> > +
> > +     while (val < 0) {
> > +             val += FIXED_ONE;
> > +             count--;
> > +     }
> > +
> > +     while (val > FIXED_ONE) {
> > +             val -= FIXED_ONE;
> > +             count++;
> > +     }
> > +
> > +     index = val * 32 / FIXED_ONE;
> > +     remainder = (val & 0x7ff) << 5;
> > +
> > +     v1 = intel_fixed_exp_tbl[index];
> > +     v2 = intel_fixed_exp_tbl[index+1];
> > +     frac_part = v1 + intel_fixed_mul(v2 - v1, remainder);
> > +
> > +     if (count < 0) {
> > +             for (i = 0; i < -count; i++)
> > +                     int_part = intel_fixed_mul(int_part, FIXED_E);
> > +
> > +             v = intel_fixed_div(frac_part, int_part);
> > +     } else {
> > +             for (i = 0; i < count; i++)
> > +                     int_part = intel_fixed_mul(int_part, FIXED_E);
> > +
> > +             v = intel_fixed_mul(frac_part, int_part);
> > +     }
> > +     return (v >= 0) ? v : 0;
> > +}
> > +
> > +/*
> > + * Approximate fixed point pow function.
> > + * Only works for x in [0,1[
> > + */
> > +static int intel_fixed_pow(int x, int y)
> > +{
> > +     int e, p, r;
> > +     e = intel_fixed_log(x);
> > +     p = intel_fixed_mul(e, y);
> > +     r = intel_fixed_exp(p);
> > +     return r;
> > +}
> > +
> > +
> > diff --git a/drivers/gpu/drm/i915/intel_panel.c
> b/drivers/gpu/drm/i915/intel_panel.c
> > index 3f9249b..ae95163 100644
> > --- a/drivers/gpu/drm/i915/intel_panel.c
> > +++ b/drivers/gpu/drm/i915/intel_panel.c
> > @@ -251,6 +251,8 @@ static void
> intel_panel_actually_set_backlight(struct drm_device *dev, u32 level
> >       struct drm_i915_private *dev_priv = dev->dev_private;
> >       u32 tmp;
> >
> > +     level = level * dev_priv->backlight_correction_level >> 8;
> > +
> >       DRM_DEBUG_DRIVER("set backlight PWM = %d\n", level);
> >
> >       if (HAS_PCH_SPLIT(dev))
> > @@ -306,6 +308,10 @@ static void intel_panel_init_backlight(struct
> drm_device *dev)
> >
> >       dev_priv->backlight_level = intel_panel_get_backlight(dev);
> >       dev_priv->backlight_enabled = dev_priv->backlight_level != 0;
> > +     dev_priv->adaptive_backlight_enabled = 0;
> > +     /* 2.2 as 16.16 fixed point */
> > +     dev_priv->adaptive_backlight_panel_gamma = 144179;
> > +     dev_priv->backlight_correction_level = 256;
> >  }
> >
> >  enum drm_connector_status
> > diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h
> > index da929bb..70c9cfc 100644
> > --- a/include/drm/i915_drm.h
> > +++ b/include/drm/i915_drm.h
> > @@ -309,6 +309,8 @@ typedef struct drm_i915_getparam {
> >  #define I915_SETPARAM_TEX_LRU_LOG_GRANULARITY             2
> >  #define I915_SETPARAM_ALLOW_BATCHBUFFER                   3
> >  #define I915_SETPARAM_NUM_USED_FENCES                     4
> > +#define I915_SETPARAM_ADAPTIVE_BACKLIGHT_ENABLE           5
> > +#define I915_SETPARAM_PANEL_GAMMA                         6
> >
> >  typedef struct drm_i915_setparam {
> >       int param;
> > --
> > 1.7.5.3.367.ga9930
> >
> > _______________________________________________
> > Intel-gfx mailing list
> > Intel-gfx@lists.freedesktop.org
> > http://lists.freedesktop.org/mailman/listinfo/intel-gfx
>
> --
> Daniel Vetter
> Mail: daniel@ffwll.ch
> Mobile: +41 (0)79 365 57 48
>

[-- Attachment #1.2: Type: text/html, Size: 32724 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

end of thread, other threads:[~2012-06-21 22:18 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-06-20 22:12 [PATCH] drm/i915: Adaptive backlight support Stéphane Marchesin
2012-06-21  8:21 ` Jani Nikula
2012-06-21 22:08   ` Stéphane Marchesin
2012-06-21  9:34 ` Daniel Vetter
2012-06-21 22:18   ` Stéphane Marchesin

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.