[Intel-gfx] [PATCH] CHROMIUM: drm/i915: Add backlight support for Link
james.ausmus at intel.com
james.ausmus at intel.com
Fri Aug 16 02:30:43 CEST 2013
From: Stéphane Marchesin <marcheu at chromium.org>
This adds device-specific backlight support for Link, and also
enables adaptive backlight by default there.
BUG=chrome-os-partner:13276,chrome-os-partner:15248
TEST=by hand
Change-Id: I9ef546bba9f121657a653aa9cfc6a80bbde55cb0
Reviewed-on: https://gerrit.chromium.org/gerrit/36976
Reviewed-by: Daniel Erat <derat at chromium.org>
Commit-Ready: Stéphane Marchesin <marcheu at chromium.org>
Tested-by: Stéphane Marchesin <marcheu at chromium.org>
[marcheu: Fixups for 3.8]
---
drivers/gpu/drm/i915/Makefile | 1 +
drivers/gpu/drm/i915/i915_drv.h | 17 +
drivers/gpu/drm/i915/i915_irq.c | 4 +-
drivers/gpu/drm/i915/i915_reg.h | 22 ++
drivers/gpu/drm/i915/intel_adaptive_backlight.c | 401 ++++++++++++++++++++++++
drivers/gpu/drm/i915/intel_dp.c | 24 ++
drivers/gpu/drm/i915/intel_drv.h | 3 +
drivers/gpu/drm/i915/intel_fixedpoint.h | 143 +++++++++
drivers/gpu/drm/i915/intel_modes.c | 48 +++
drivers/gpu/drm/i915/intel_panel.c | 38 +++
10 files changed, 700 insertions(+), 1 deletion(-)
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 0f2c549..1c8613d 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -16,6 +16,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \
i915_gem_tiling.o \
i915_sysfs.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_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 67932ce..646c3eb 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -711,6 +711,13 @@ typedef struct drm_i915_private {
void (*disable_backlight)(struct drm_device *dev);
void (*enable_backlight)(struct drm_device *dev, enum pipe pipe);
+ /* 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;
@@ -922,6 +929,9 @@ 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;
+
bool hw_contexts_disabled;
uint32_t hw_context_size;
@@ -1627,6 +1637,13 @@ extern int i915_restore_state(struct drm_device *dev);
void i915_setup_sysfs(struct drm_device *dev_priv);
void i915_teardown_sysfs(struct drm_device *dev_priv);
+/* 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);
+extern void intel_adaptive_backlight_setup(struct drm_device *dev);
+
/* 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 fe84338..17f6406 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -707,8 +707,10 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
intel_opregion_gse_intr(dev);
for (i = 0; i < 3; i++) {
- if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i)))
+ if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i))) {
drm_handle_vblank(dev, i);
+ intel_adaptive_backlight(dev, i);
+ }
if (de_iir & (DE_PLANEA_FLIP_DONE_IVB << (5 * i))) {
intel_prepare_page_flip(dev, i);
intel_finish_page_flip_plane(dev, i);
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 59afb7e..157cb5d 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -1960,6 +1960,28 @@
#define BLC_PWM_CPU_CTL2 0x48250
#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
+
/* PCH CTL1 is totally different, all but the below bits are reserved. CTL2 is
* like the normal CTL from gen4 and earlier. Hooray for confusing naming. */
#define BLC_PWM_PCH_CTL1 0xc8250
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..aa610c3
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_adaptive_backlight.c
@@ -0,0 +1,401 @@
+/*
+ * 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 "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;
+
+ dev_priv->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;
+ bool found = false;
+
+ if (!dev_priv->adaptive_backlight_enabled)
+ return;
+
+ /* Find the connector */
+ list_for_each_entry(connector,
+ &dev->mode_config.connector_list,
+ head)
+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
+ found = true;
+ break;
+ }
+
+ if (!found)
+ 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;
+
+ dev_priv->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);
+}
+
+static u32 intel_link_get_backlight(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 val;
+
+ val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
+
+ return (val - 2) / 4;
+}
+
+static u32 intel_link_get_max_backlight(struct drm_device *dev)
+{
+ return 255;
+}
+
+static void intel_link_set_backlight(struct drm_device *dev, u32 level)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 hw_level;
+ u32 val;
+
+ dev_priv->backlight_level = level;
+
+ if (dev_priv->adaptive_backlight_enabled)
+ level = level * dev_priv->backlight_correction_level >> 8;
+
+ if (level == 0)
+ hw_level = 0;
+ else
+ hw_level = level * 4 + 2;
+
+ val = I915_READ(BLC_PWM_CPU_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
+ I915_WRITE(BLC_PWM_CPU_CTL, val | hw_level);
+}
+
+static void intel_link_disable_backlight(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ dev_priv->backlight_enabled = false;
+ dev_priv->set_backlight(dev, 0);
+}
+
+static void intel_link_enable_backlight(struct drm_device *dev, enum pipe pipe)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ /* Increase the level from 0 */
+ if (dev_priv->backlight_level == 0)
+ dev_priv->backlight_level = dev_priv->get_max_backlight(dev);
+
+ dev_priv->backlight_enabled = true;
+ dev_priv->set_backlight(dev, dev_priv->backlight_level);
+}
+
+void intel_adaptive_backlight_setup(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ dev_priv->get_backlight = intel_link_get_backlight;
+ dev_priv->get_max_backlight = intel_link_get_max_backlight;
+ dev_priv->set_backlight = intel_link_set_backlight;
+ dev_priv->disable_backlight = intel_link_disable_backlight;
+ dev_priv->enable_backlight = intel_link_enable_backlight;
+
+ intel_adaptive_backlight_enable(dev_priv);
+}
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index d5f3105..e5d16bc 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -2442,6 +2442,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:
@@ -2451,6 +2468,7 @@ done:
crtc->x, crtc->y, crtc->fb);
}
+done_nomodeset:
return 0;
}
@@ -2575,6 +2593,12 @@ intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connect
DRM_MODE_SCALE_ASPECT);
intel_connector->panel.fitting_mode = DRM_MODE_SCALE_ASPECT;
}
+
+ if ((INTEL_INFO(connector->dev)->gen == 7) &&
+ (connector->connector_type == DRM_MODE_CONNECTOR_eDP)) {
+ intel_attach_adaptive_backlight_property(connector);
+ intel_attach_panel_gamma_property(connector);
+ }
}
static void
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 4f41b8a..0762970 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -423,6 +423,9 @@ int intel_ddc_get_modes(struct drm_connector *c, struct i2c_adapter *adapter);
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,
diff --git a/drivers/gpu/drm/i915/intel_fixedpoint.h b/drivers/gpu/drm/i915/intel_fixedpoint.h
new file mode 100644
index 0000000..0e9343b
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_fixedpoint.h
@@ -0,0 +1,143 @@
+/*
+ * 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;
+}
+
diff --git a/drivers/gpu/drm/i915/intel_modes.c b/drivers/gpu/drm/i915/intel_modes.c
index 0d9b115..57b9d52 100644
--- a/drivers/gpu/drm/i915/intel_modes.c
+++ b/drivers/gpu/drm/i915/intel_modes.c
@@ -100,6 +100,54 @@ intel_attach_force_audio_property(struct drm_connector *connector)
drm_object_attach_property(&connector->base, 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_object_attach_property(&connector->base, 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_object_attach_property(&connector->base, prop, 100);
+}
+
static const struct drm_prop_enum_list broadcast_rgb_names[] = {
{ 0, "Full" },
{ 1, "Limited 16:235" },
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index dddd4a1..cebabb0 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -30,6 +30,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/dmi.h>
#include <linux/moduleparam.h>
#include "intel_drv.h"
#include "i915_drv.h"
@@ -392,6 +393,23 @@ set_level:
intel_panel_actually_set_backlight(dev, dev_priv->backlight_level);
}
+static int intel_link_backlight(const struct dmi_system_id *id)
+{
+ DRM_DEBUG_KMS("Using Link backlight\n");
+ return 1;
+}
+
+static const struct dmi_system_id link_dmi_table[] = {
+ {
+ .callback = intel_link_backlight,
+ .ident = "Link",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
+ },
+ },
+ { }
+};
+
static void intel_panel_init_backlight(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -404,6 +422,26 @@ static void intel_panel_init_backlight(struct drm_device *dev)
dev_priv->backlight_level = dev_priv->get_backlight(dev);
dev_priv->backlight_enabled = dev_priv->backlight_level != 0;
+
+ if (dmi_check_system(link_dmi_table)) {
+ struct drm_connector *connector;
+ bool found = false;
+ /* Find the connector */
+ list_for_each_entry(connector,
+ &dev->mode_config.connector_list,
+ head)
+ if (connector->connector_type ==
+ DRM_MODE_CONNECTOR_eDP) {
+ found = true;
+ break;
+ }
+
+ if (found) {
+ intel_adaptive_backlight_setup(dev);
+ intel_attach_adaptive_backlight_property(connector);
+ intel_attach_panel_gamma_property(connector);
+ }
+ }
}
enum drm_connector_status
--
1.8.3.2
More information about the Intel-gfx
mailing list