Skip to content

Commit

Permalink
drm/i915: Refactor panel backlight controls
Browse files Browse the repository at this point in the history
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 <chris@chris-wilson.co.uk>
  • Loading branch information
Chris Wilson committed Sep 8, 2010
1 parent 5d607f9 commit a957355
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 108 deletions.
2 changes: 1 addition & 1 deletion drivers/gpu/drm/i915/i915_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
5 changes: 4 additions & 1 deletion drivers/gpu/drm/i915/intel_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
56 changes: 5 additions & 51 deletions drivers/gpu/drm/i915/intel_lvds.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
}
Expand Down
61 changes: 6 additions & 55 deletions drivers/gpu/drm/i915/intel_opregion.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand All @@ -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");
Expand Down
109 changes: 109 additions & 0 deletions drivers/gpu/drm/i915/intel_panel.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -109,3 +111,110 @@ intel_pch_panel_fitting(struct drm_device *dev,
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);
}

0 comments on commit a957355

Please sign in to comment.