From patchwork Fri Aug 16 00:30:43 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: James Ausmus X-Patchwork-Id: 2845307 Return-Path: X-Original-To: patchwork-intel-gfx@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id C4C289F2F5 for ; Fri, 16 Aug 2013 00:34:44 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id DFB09202AE for ; Fri, 16 Aug 2013 00:34:42 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id BF59A202A1 for ; Fri, 16 Aug 2013 00:34:40 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 99729E7227 for ; Thu, 15 Aug 2013 17:34:40 -0700 (PDT) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mga02.intel.com (mga02.intel.com [134.134.136.20]) by gabe.freedesktop.org (Postfix) with ESMTP id B1202E5C25 for ; Thu, 15 Aug 2013 17:31:57 -0700 (PDT) Received: from orsmga002.jf.intel.com ([10.7.209.21]) by orsmga101.jf.intel.com with ESMTP; 15 Aug 2013 17:31:57 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.89,889,1367996400"; d="scan'208";a="387901449" Received: from jausmus-gentoo-dev5.jf.intel.com ([10.7.198.60]) by orsmga002.jf.intel.com with ESMTP; 15 Aug 2013 17:31:56 -0700 From: james.ausmus@intel.com To: intel-gfx@lists.freedesktop.org Date: Thu, 15 Aug 2013 17:30:43 -0700 Message-Id: <1376613069-15790-19-git-send-email-james.ausmus@intel.com> X-Mailer: git-send-email 1.8.3.2 In-Reply-To: <1376613069-15790-1-git-send-email-james.ausmus@intel.com> References: <1376613069-15790-1-git-send-email-james.ausmus@intel.com> MIME-Version: 1.0 Subject: [Intel-gfx] [PATCH] CHROMIUM: drm/i915: Add backlight support for Link X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: intel-gfx-bounces+patchwork-intel-gfx=patchwork.kernel.org@lists.freedesktop.org Errors-To: intel-gfx-bounces+patchwork-intel-gfx=patchwork.kernel.org@lists.freedesktop.org X-Spam-Status: No, score=-7.0 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Stéphane Marchesin 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 Commit-Ready: Stéphane Marchesin Tested-by: Stéphane Marchesin [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 #include #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