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
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 \
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;
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 {
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);
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) {
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) {
#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)
--- /dev/null
+/*
+ * 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);
+}
+
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:
crtc->x, crtc->y,
crtc->fb);
}
+done_nomodeset:
return 0;
}
}
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
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
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);
--- /dev/null
+/*
+ * 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;
+}
+
+
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);
+}
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))