CHROMIUM: drm/i915: Adaptive backlight support
authorStéphane Marchesin <marcheu@chromium.org>
Wed, 13 Jun 2012 23:47:47 +0000 (16:47 -0700)
committerGerrit <chrome-bot@google.com>
Thu, 23 Aug 2012 19:44:28 +0000 (12:44 -0700)
This patch implements i915 adaptive backlight support for SNB and IVB.
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.

BUG=chrome-os-partner:11002
TEST=set the adaptive backlight property with:
TEST=xrandr --output eDP1 --set "Adaptive backlight" on
TEST=then watch a full screen movie and see the backlight level change
TEST=to read the backlight percentage:
TEST=echo $((`printf "%d\n" \`intel_reg_read 0x48254 | cut -f 2 -d :\``*100/4882))

Change-Id: I9b9631cacc7d90e2801a542a3789118521bc25f0
Signed-off-by: Stéphane Marchesin <marcheu@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/26748

drivers/gpu/drm/i915/Makefile
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_irq.c
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_adaptive_backlight.c [new file with mode: 0644]
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_fixedpoint.h [new file with mode: 0644]
drivers/gpu/drm/i915/intel_modes.c
drivers/gpu/drm/i915/intel_panel.c

index ce7fc77..5c125c3 100644 (file)
@@ -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 \
index d89f585..849ab07 100644 (file)
@@ -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; /* as 16.16 fixed point */
+
        /* Feature bits from the VBIOS */
        unsigned int int_tv_support:1;
        unsigned int lvds_dither:1;
@@ -783,6 +790,8 @@ typedef struct drm_i915_private {
 
        struct drm_property *broadcast_rgb_property;
        struct drm_property *force_audio_property;
+       struct drm_property *adaptive_backlight_property;
+       struct drm_property *panel_gamma_property;
 } drm_i915_private_t;
 
 enum hdmi_force_audio {
@@ -1358,6 +1367,12 @@ 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);
+extern void intel_adaptive_backlight_enable(struct drm_i915_private *dev_priv);
+extern void intel_adaptive_backlight_disable(struct drm_i915_private *dev_priv,
+                                            struct drm_connector *connector);
+
 /* intel_i2c.c */
 extern int intel_setup_gmbus(struct drm_device *dev);
 extern void intel_teardown_gmbus(struct drm_device *dev);
index afd4e03..31ebb84 100644 (file)
@@ -519,11 +519,15 @@ static irqreturn_t ivybridge_irq_handler(DRM_IRQ_ARGS)
                intel_finish_page_flip_plane(dev, 1);
        }
 
-       if (de_iir & DE_PIPEA_VBLANK_IVB)
+       if (de_iir & DE_PIPEA_VBLANK_IVB) {
                drm_handle_vblank(dev, 0);
+               intel_adaptive_backlight(dev, 0);
+       }
 
-       if (de_iir & DE_PIPEB_VBLANK_IVB)
+       if (de_iir & DE_PIPEB_VBLANK_IVB) {
                drm_handle_vblank(dev, 1);
+               intel_adaptive_backlight(dev, 1);
+       }
 
        /* check event from PCH */
        if (de_iir & DE_PCH_EVENT_IVB) {
@@ -619,11 +623,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) {
index 8bdf2cb..fa64805 100644 (file)
 #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 (file)
index 0000000..8395c14
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+#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"
+
+/*
+ * Some notes 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.
+ * - Ivy bridge has a hardware issue where the color correction doesn't seem
+ *   to work. When you enable the ENH_MODIF_TBL_ENABLE bit, not only does the
+ *   correction not work, but it becomes impossible to read the levels.
+ *   Instead, as a workaround, we don't set that bit on ivy bridge and
+ *   (ab)use the gamma ramp registers to do the correction.
+ */
+
+/*
+ * 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 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++) {
+               u32 hist_ctl = ENH_HIST_ENABLE |
+                              ENH_MODIF_TBL_ENABLE |
+                              ENH_PIPE(pipe) |
+                              HIST_MODE_YUV |
+                              ENH_MODE_ADDITIVE |
+                              i;
+
+               /* Ivb workaround, see the explanation at the top */
+               if (INTEL_INFO(dev)->gen == 7)
+                       hist_ctl &= ~ENH_MODIF_TBL_ENABLE;
+
+               I915_WRITE(BLM_HIST_CTL, hist_ctl);
+
+               levels[i] = I915_READ(BLM_HIST_ENH);
+       }
+}
+
+/* Multiplier is 16.16 fixed point */
+static void set_levels(struct drm_device *dev, int pipe, int multiplier)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       int i;
+
+       if (INTEL_INFO(dev)->gen == 7) {
+               /* Ivb workaround, see the explanation at the top */
+               for (i = 0; i < 256; i++) {
+                       int v = intel_fixed_div(i, multiplier);
+                       if (v > 255)
+                               v = 255;
+                       v = v | (v << 8) | (v << 16);
+                       I915_WRITE(LGC_PALETTE(pipe) + i * 4, v);
+               }
+
+               return;
+       }
+
+       for (i = 0; i < ENH_NUM_BINS; i++) {
+               int base_value = i * 8 * 4;
+               int level = base_value -
+                           intel_fixed_mul(base_value, multiplier);
+               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, level);
+       }
+}
+
+/* Compute the current step. Returns true if we need to change the levels,
+ * false otherwise.
+ */
+static bool adaptive_backlight_current_step(drm_i915_private_t *dev_priv,
+                                          int 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;
+       } else {
+               return false;
+       }
+
+       return true;
+}
+
+/*
+ * This function computes the backlight correction level for an acceptable
+ * distortion and fills up the correction bins adequately.
+ */
+static void
+adaptive_backlight_correct(struct drm_device *dev, int pipe)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       int correction_level;
+       int multiplier, one_over_gamma;
+       int levels[ENH_NUM_BINS];
+
+       get_levels(dev, pipe, levels);
+
+       /* Find the correction level for an acceptable distortion */
+       correction_level = histogram_find_correction_level(levels);
+
+       /* If we're already at our correction target, then there is
+        * nothing to do
+        */
+       if (dev_priv->backlight_correction_level == correction_level)
+               return;
+
+       /* Decide by how much to move this step. If we didn't move, return */
+       if (!adaptive_backlight_current_step(dev_priv, correction_level))
+               return;
+
+       intel_panel_set_backlight(dev, dev_priv->backlight_level);
+
+       /* 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);
+
+       set_levels(dev, pipe, multiplier);
+}
+
+void intel_adaptive_backlight(struct drm_device *dev, int pipe_vblank_event)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       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;
+
+       /* 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));
+
+
+       adaptive_backlight_correct(dev, pipe);
+}
+
+void intel_adaptive_backlight_enable(struct drm_i915_private *dev_priv)
+{
+       dev_priv->backlight_correction_level = 256;
+       dev_priv->backlight_correction_count = 0;
+       dev_priv->backlight_correction_direction = 0;
+       /* Default gamma is 2.2 as 16.16 fixed point */
+       if (!dev_priv->adaptive_backlight_panel_gamma)
+               dev_priv->adaptive_backlight_panel_gamma = 144179;
+
+       dev_priv->adaptive_backlight_enabled = true;
+}
+
+void intel_adaptive_backlight_disable(struct drm_i915_private *dev_priv,
+                                     struct drm_connector *connector)
+{
+       struct intel_crtc *intel_crtc;
+       int pipe;
+       struct drm_device *dev = dev_priv->dev;
+
+       dev_priv->adaptive_backlight_enabled = false;
+
+       dev_priv->backlight_correction_level = 256;
+
+       intel_panel_set_backlight(dev, dev_priv->backlight_level);
+
+       /* Find the pipe */
+       if (!connector->encoder)
+               return;
+
+       if (!connector->encoder->crtc)
+               return;
+
+       intel_crtc = to_intel_crtc(connector->encoder->crtc);
+       pipe = intel_crtc->pipe;
+
+       /* Reset the levels to default */
+       set_levels(dev, pipe, FIXED_ONE);
+}
+
index 4b63791..397b8ef 100644 (file)
@@ -2258,6 +2258,23 @@ intel_dp_set_property(struct drm_connector *connector,
                goto done;
        }
 
+       if (property == dev_priv->adaptive_backlight_property) {
+               dev_priv->adaptive_backlight_enabled = !!val;
+
+               if (dev_priv->adaptive_backlight_enabled)
+                       intel_adaptive_backlight_enable(dev_priv);
+               else
+                       intel_adaptive_backlight_disable(dev_priv, connector);
+
+               goto done_nomodeset;
+       }
+
+       if (property == dev_priv->panel_gamma_property) {
+               dev_priv->adaptive_backlight_panel_gamma = (u32)val * 65536 / 100;
+
+               goto done_nomodeset;
+       }
+
        return -EINVAL;
 
 done:
@@ -2267,6 +2284,7 @@ done:
                                         crtc->x, crtc->y,
                                         crtc->fb);
        }
+done_nomodeset:
 
        return 0;
 }
@@ -2375,10 +2393,18 @@ bool intel_dpd_is_edp(struct drm_device *dev)
 }
 
 static void
-intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connector)
+intel_dp_add_properties(struct drm_device *dev,
+                       struct intel_dp *intel_dp,
+                       struct drm_connector *connector)
 {
        intel_attach_force_audio_property(connector);
        intel_attach_broadcast_rgb_property(connector);
+
+       if ((INTEL_INFO(dev)->gen >= 6) &&
+           (connector->connector_type == DRM_MODE_CONNECTOR_eDP)) {
+               intel_attach_adaptive_backlight_property(connector);
+               intel_attach_panel_gamma_property(connector);
+       }
 }
 
 void
@@ -2549,7 +2575,7 @@ intel_dp_init(struct drm_device *dev, int output_reg)
                intel_panel_setup_backlight(dev);
        }
 
-       intel_dp_add_properties(intel_dp, connector);
+       intel_dp_add_properties(dev, intel_dp, connector);
 
        /* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
         * 0xd.  Failure to do so will result in spurious interrupts being
index ee5ffac..d47e6cd 100644 (file)
@@ -306,6 +306,9 @@ extern bool intel_ddc_probe(struct intel_encoder *intel_encoder, int ddc_bus);
 
 extern void intel_attach_force_audio_property(struct drm_connector *connector);
 extern void intel_attach_broadcast_rgb_property(struct drm_connector *connector);
+extern void
+intel_attach_adaptive_backlight_property(struct drm_connector *connector);
+extern void intel_attach_panel_gamma_property(struct drm_connector *connector);
 
 extern void intel_crt_init(struct drm_device *dev);
 extern void intel_hdmi_init(struct drm_device *dev, int sdvox_reg);
diff --git a/drivers/gpu/drm/i915/intel_fixedpoint.h b/drivers/gpu/drm/i915/intel_fixedpoint.h
new file mode 100644 (file)
index 0000000..5b2c89b
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * 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 gamma 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)
+{
+       int64_t p = (int64_t)a * b;
+       do_div(p, 65536);
+       return (int)p;
+}
+
+static int intel_fixed_div(int a, int b)
+{
+       int64_t p = (int64_t)a * 65536;
+       do_div(p, b);
+       return (int)p;
+}
+
+/*
+ * 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;
+}
+
+
index d67ec3a..896bc86 100644 (file)
@@ -139,3 +139,51 @@ intel_attach_broadcast_rgb_property(struct drm_connector *connector)
 
        drm_connector_attach_property(connector, prop, 0);
 }
+
+static const struct drm_prop_enum_list adaptive_backlight_names[] = {
+       { 0, "off" },
+       { 1, "on" },
+};
+
+void
+intel_attach_adaptive_backlight_property(struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_property *prop;
+
+       prop = dev_priv->adaptive_backlight_property;
+       if (prop == NULL) {
+               prop = drm_property_create_enum(dev, 0,
+                                       "Adaptive backlight",
+                                       adaptive_backlight_names,
+                                       ARRAY_SIZE(adaptive_backlight_names));
+               if (prop == NULL)
+                       return;
+
+               dev_priv->adaptive_backlight_property = prop;
+       }
+       drm_connector_attach_property(connector, prop, 0);
+}
+
+void
+intel_attach_panel_gamma_property(struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_property *prop;
+
+       prop = dev_priv->panel_gamma_property;
+       if (prop == NULL) {
+               prop = drm_property_create_range(dev, 0,
+                                       "Panel gamma",
+                                       100,
+                                       550);
+
+               if (prop == NULL)
+                       return;
+
+               dev_priv->panel_gamma_property = prop;
+       }
+       drm_connector_attach_property(connector, prop, 0);
+}
index 3f9249b..0907fbb 100644 (file)
@@ -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))