From a95735569312f2ab0c80425e2cd1e5cb0b4e1870 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 22 Aug 2010 13:18:16 +0100 Subject: [PATCH] drm/i915: Refactor panel backlight controls There were two instances of code to control the panel backlight and neither handled the complete set of device variations. Fixes: Bug 29716 - [GM965] Regression: Backlight resets to minimum when changing resolution https://bugs.freedesktop.org/show_bug.cgi?id=29716 And a bug on one of my PineView boxes which overflowed the backlight value. Incorporates part of a similar patch by Matthew Garrett that exposes a native Intel backlight controller. Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/i915_drv.h | 2 +- drivers/gpu/drm/i915/intel_drv.h | 5 +- drivers/gpu/drm/i915/intel_lvds.c | 56 ++----------- drivers/gpu/drm/i915/intel_opregion.c | 61 ++------------ drivers/gpu/drm/i915/intel_panel.c | 109 ++++++++++++++++++++++++++ 5 files changed, 125 insertions(+), 108 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e6fbeb43d59c..cfc8bfd0fd7e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -319,7 +319,7 @@ typedef struct drm_i915_private { struct intel_overlay *overlay; /* LVDS info */ - int backlight_duty_cycle; /* restore backlight to this value */ + int backlight_level; /* restore backlight to this value */ bool panel_wants_dither; struct drm_display_mode *panel_fixed_mode; struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */ diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 949cfda4b49a..cdf9c78896cf 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -204,13 +204,16 @@ extern bool intel_pch_has_edp(struct drm_crtc *crtc); extern bool intel_dpd_is_edp(struct drm_device *dev); extern void intel_edp_link_config (struct intel_encoder *, int *, int *); - +/* intel_panel.c */ extern void intel_fixed_panel_mode(struct drm_display_mode *fixed_mode, struct drm_display_mode *adjusted_mode); extern void intel_pch_panel_fitting(struct drm_device *dev, int fitting_mode, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); +extern u32 intel_panel_get_max_backlight(struct drm_device *dev); +extern u32 intel_panel_get_backlight(struct drm_device *dev); +extern void intel_panel_set_backlight(struct drm_device *dev, u32 level); extern int intel_panel_fitter_pipe (struct drm_device *dev); extern void intel_crtc_load_lut(struct drm_crtc *crtc); diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 047bd9538c6a..8320279fad58 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -53,43 +53,6 @@ static struct intel_lvds *enc_to_intel_lvds(struct drm_encoder *encoder) return container_of(enc_to_intel_encoder(encoder), struct intel_lvds, base); } -/** - * Sets the backlight level. - * - * \param level backlight level, from 0 to intel_lvds_get_max_backlight(). - */ -static void intel_lvds_set_backlight(struct drm_device *dev, int level) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - u32 blc_pwm_ctl, reg; - - if (HAS_PCH_SPLIT(dev)) - reg = BLC_PWM_CPU_CTL; - else - reg = BLC_PWM_CTL; - - blc_pwm_ctl = I915_READ(reg) & ~BACKLIGHT_DUTY_CYCLE_MASK; - I915_WRITE(reg, (blc_pwm_ctl | - (level << BACKLIGHT_DUTY_CYCLE_SHIFT))); -} - -/** - * Returns the maximum level of the backlight duty cycle field. - */ -static u32 intel_lvds_get_max_backlight(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - u32 reg; - - if (HAS_PCH_SPLIT(dev)) - reg = BLC_PWM_PCH_CTL2; - else - reg = BLC_PWM_CTL; - - return ((I915_READ(reg) & BACKLIGHT_MODULATION_FREQ_MASK) >> - BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; -} - /** * Sets the power state for the panel. */ @@ -117,9 +80,9 @@ static void intel_lvds_set_power(struct drm_device *dev, bool on) if (wait_for(I915_READ(status_reg) & PP_ON, 1000)) DRM_ERROR("timed out waiting to enable LVDS pipe"); - intel_lvds_set_backlight(dev, dev_priv->backlight_duty_cycle); + intel_panel_set_backlight(dev, dev_priv->backlight_level); } else { - intel_lvds_set_backlight(dev, 0); + intel_panel_set_backlight(dev, 0); I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON); @@ -386,16 +349,8 @@ static void intel_lvds_prepare(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 reg; - - if (HAS_PCH_SPLIT(dev)) - reg = BLC_PWM_CPU_CTL; - else - reg = BLC_PWM_CTL; - dev_priv->saveBLC_PWM_CTL = I915_READ(reg); - dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL & - BACKLIGHT_DUTY_CYCLE_MASK); + dev_priv->backlight_level = intel_panel_get_backlight(dev); intel_lvds_set_power(dev, false); } @@ -405,9 +360,8 @@ static void intel_lvds_commit( struct drm_encoder *encoder) struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; - if (dev_priv->backlight_duty_cycle == 0) - dev_priv->backlight_duty_cycle = - intel_lvds_get_max_backlight(dev); + if (dev_priv->backlight_level == 0) + dev_priv->backlight_level = intel_panel_get_max_backlight(dev); intel_lvds_set_power(dev, true); } diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index 3cb13237ba58..917c7dc3cd6b 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -31,9 +31,9 @@ #include "drmP.h" #include "i915_drm.h" #include "i915_drv.h" +#include "intel_drv.h" #define PCI_ASLE 0xe4 -#define PCI_LBPC 0xf4 #define PCI_ASLS 0xfc #define OPREGION_HEADER_OFFSET 0 @@ -147,36 +147,17 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) { struct drm_i915_private *dev_priv = dev->dev_private; struct opregion_asle *asle = dev_priv->opregion.asle; - u32 blc_pwm_ctl, blc_pwm_ctl2; - u32 max_backlight, level, shift; + u32 max; if (!(bclp & ASLE_BCLP_VALID)) return ASLE_BACKLIGHT_FAILED; bclp &= ASLE_BCLP_MSK; - if (bclp < 0 || bclp > 255) + if (bclp > 255) return ASLE_BACKLIGHT_FAILED; - blc_pwm_ctl = I915_READ(BLC_PWM_CTL); - blc_pwm_ctl2 = I915_READ(BLC_PWM_CTL2); - - if (IS_I965G(dev) && (blc_pwm_ctl2 & BLM_COMBINATION_MODE)) - pci_write_config_dword(dev->pdev, PCI_LBPC, bclp); - else { - if (IS_PINEVIEW(dev)) { - blc_pwm_ctl &= ~(BACKLIGHT_DUTY_CYCLE_MASK - 1); - max_backlight = (blc_pwm_ctl & BACKLIGHT_MODULATION_FREQ_MASK) >> - BACKLIGHT_MODULATION_FREQ_SHIFT; - shift = BACKLIGHT_DUTY_CYCLE_SHIFT + 1; - } else { - blc_pwm_ctl &= ~BACKLIGHT_DUTY_CYCLE_MASK; - max_backlight = ((blc_pwm_ctl & BACKLIGHT_MODULATION_FREQ_MASK) >> - BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; - shift = BACKLIGHT_DUTY_CYCLE_SHIFT; - } - level = (bclp * max_backlight) / 255; - I915_WRITE(BLC_PWM_CTL, blc_pwm_ctl | (level << shift)); - } + max = intel_panel_get_max_backlight(dev); + intel_panel_set_backlight(dev, bclp * max / 255); asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID; return 0; @@ -243,36 +224,6 @@ void intel_opregion_asle_intr(struct drm_device *dev) asle->aslc = asle_stat; } -static u32 asle_set_backlight_ironlake(struct drm_device *dev, u32 bclp) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - struct opregion_asle *asle = dev_priv->opregion.asle; - u32 cpu_pwm_ctl, pch_pwm_ctl2; - u32 max_backlight, level; - - if (!(bclp & ASLE_BCLP_VALID)) - return ASLE_BACKLIGHT_FAILED; - - bclp &= ASLE_BCLP_MSK; - if (bclp < 0 || bclp > 255) - return ASLE_BACKLIGHT_FAILED; - - cpu_pwm_ctl = I915_READ(BLC_PWM_CPU_CTL); - pch_pwm_ctl2 = I915_READ(BLC_PWM_PCH_CTL2); - /* get the max PWM frequency */ - max_backlight = (pch_pwm_ctl2 >> 16) & BACKLIGHT_DUTY_CYCLE_MASK; - /* calculate the expected PMW frequency */ - level = (bclp * max_backlight) / 255; - /* reserve the high 16 bits */ - cpu_pwm_ctl &= ~(BACKLIGHT_DUTY_CYCLE_MASK); - /* write the updated PWM frequency */ - I915_WRITE(BLC_PWM_CPU_CTL, cpu_pwm_ctl | level); - - asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID; - - return 0; -} - /* Only present on Ironlake+ */ void intel_opregion_gse_intr(struct drm_device *dev) { @@ -297,7 +248,7 @@ void intel_opregion_gse_intr(struct drm_device *dev) } if (asle_req & ASLE_SET_BACKLIGHT) - asle_stat |= asle_set_backlight_ironlake(dev, asle->bclp); + asle_stat |= asle_set_backlight(dev, asle->bclp); if (asle_req & ASLE_SET_PFIT) { DRM_DEBUG_DRIVER("Pfit is not supported\n"); diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index e7f5299d9d57..30abe7afc942 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -30,6 +30,8 @@ #include "intel_drv.h" +#define PCI_LBPC 0xf4 /* legacy/combination backlight modes */ + void intel_fixed_panel_mode(struct drm_display_mode *fixed_mode, struct drm_display_mode *adjusted_mode) @@ -109,3 +111,110 @@ done: dev_priv->pch_pf_pos = (x << 16) | y; dev_priv->pch_pf_size = (width << 16) | height; } + +static int is_backlight_combination_mode(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (IS_I965G(dev)) + return I915_READ(BLC_PWM_CTL2) & BLM_COMBINATION_MODE; + + if (IS_GEN2(dev)) + return I915_READ(BLC_PWM_CTL) & BLM_LEGACY_MODE; + + return 0; +} + +u32 intel_panel_get_max_backlight(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 max; + + if (HAS_PCH_SPLIT(dev)) { + max = I915_READ(BLC_PWM_PCH_CTL2) >> 16; + } else { + max = I915_READ(BLC_PWM_CTL); + if (IS_PINEVIEW(dev)) { + max >>= 17; + } else { + max >>= 16; + if (!IS_I965G(dev)) + max &= ~1; + } + + if (is_backlight_combination_mode(dev)) + max *= 0xff; + } + + if (max == 0) { + /* XXX add code here to query mode clock or hardware clock + * and program max PWM appropriately. + */ + DRM_ERROR("fixme: max PWM is zero.\n"); + max = 1; + } + + DRM_DEBUG_DRIVER("max backlight PWM = %d\n", max); + return max; +} + +u32 intel_panel_get_backlight(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 val; + + if (HAS_PCH_SPLIT(dev)) { + val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK; + } else { + val = I915_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK; + if (IS_PINEVIEW(dev)) + val >>= 1; + + if (is_backlight_combination_mode(dev)){ + u8 lbpc; + + val &= ~1; + pci_read_config_byte(dev->pdev, PCI_LBPC, &lbpc); + val *= lbpc; + val >>= 1; + } + } + + DRM_DEBUG_DRIVER("get backlight PWM = %d\n", val); + return val; +} + +static void intel_pch_panel_set_backlight(struct drm_device *dev, u32 level) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 val = I915_READ(BLC_PWM_CPU_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK; + I915_WRITE(BLC_PWM_CPU_CTL, val | level); +} + +void intel_panel_set_backlight(struct drm_device *dev, u32 level) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 tmp; + + DRM_DEBUG_DRIVER("set backlight PWM = %d\n", level); + + if (HAS_PCH_SPLIT(dev)) + return intel_pch_panel_set_backlight(dev, level); + + if (is_backlight_combination_mode(dev)){ + u32 max = intel_panel_get_max_backlight(dev); + u8 lpbc; + + lpbc = level * 0xfe / max + 1; + level /= lpbc; + pci_write_config_byte(dev->pdev, PCI_LBPC, lpbc); + } + + tmp = I915_READ(BLC_PWM_CTL); + if (IS_PINEVIEW(dev)) { + tmp &= ~(BACKLIGHT_DUTY_CYCLE_MASK - 1); + level <<= 1; + } else + tmp &= ~BACKLIGHT_DUTY_CYCLE_MASK; + I915_WRITE(BLC_PWM_CTL, tmp | level); +} -- 2.39.5