<div style="font-family:arial,helvetica,sans-serif"><font><br><br><div class="gmail_quote">On Thu, Jun 21, 2012 at 2:34 AM, Daniel Vetter <span dir="ltr"><<a href="mailto:daniel@ffwll.ch" target="_blank">daniel@ffwll.ch</a>></span> wrote:<br>

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


- With connector properties we could also expose tuneables in an easy way,<br>
  at least partially addressing Jani's comment (like the display gamma and<br>
  maybe also other parameters).<br></blockquote><div><br></div><div>Agreed.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
- We need to somehow cope with front-rendering X. Yeah, I know this sucks<br>
  and all, but such is life. We already mark framebuffers as busys when<br>
  rendering into them directly, I guess we could hook into to that either<br>
  disable this feature until the next pageflip. Or switch to a different<br>
  mode. The disable needs to be gradually, obviously.<br></blockquote><div><br></div><div>Ok, that's probably the toughest item on that list. I'll dig into that.</div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">


- I'm unsure whether we shouldn't move the fixed-point stuff to a common<br>
  place. Wrt all the fixed-point constant I think a little macro to<br>
  convert double to 16.16 fixed point would massively help code<br>
  readability.<br></blockquote><div><br></div><div>True, I'll do that. </div><div><br></div><div>Stéphane</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">


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