Merge branch 'bdw-fixes' into backlight-rework
authorDaniel Vetter <daniel.vetter@ffwll.ch>
Thu, 14 Nov 2013 14:17:41 +0000 (15:17 +0100)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Fri, 15 Nov 2013 08:44:29 +0000 (09:44 +0100)
Merge the bdw changes into the backlight rework branch so that we can
adapt the new code for bdw, too.  This is a bit a mess, but doing this
another way would have delayed the merging of the backlight
refactoring. Mea culpa.

As discussed with Jani on irc only do bdw-specific callbacks for the
set/get methods and bake in the only other special-case into the pch
enable function.

Conflicts:
drivers/gpu/drm/i915/intel_panel.c

v2: Don't enable the PWM too early for bdw (Jani).

v3: Create new bdw_ functions for setup and enable - the rules change
sufficiently imo with the switch from controlling the pwm from the cpu
to controlling it completel from the pch to warrant this.

v4: Rip out unused pipe variable in bdw_enable_backlight (0-day
builder).

Tested-by: Ben Widawsky <ben@bwidawsk.net> (on bdw)
Reviewed-by: Jani Nikula <jani.nikula@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
1  2 
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/intel_opregion.c
drivers/gpu/drm/i915/intel_panel.c

Simple merge
Simple merge
index eadfe338dbebc27654e6190c0055e83d93842e34,e6f782d1c6696d94fe4d4a80cf7f4d6ee6b5c7d7..e480cf41c5365f5551eaff1a04efd458b7e37d12
@@@ -325,63 -325,106 +325,71 @@@ out
        pipe_config->gmch_pfit.lvds_border_bits = border;
  }
  
 -static int is_backlight_combination_mode(struct drm_device *dev)
 +static int i915_panel_invert_brightness;
 +MODULE_PARM_DESC(invert_brightness, "Invert backlight brightness "
 +      "(-1 force normal, 0 machine defaults, 1 force inversion), please "
 +      "report PCI device ID, subsystem vendor and subsystem device ID "
 +      "to dri-devel@lists.freedesktop.org, if your machine needs it. "
 +      "It will then be included in an upcoming module version.");
 +module_param_named(invert_brightness, i915_panel_invert_brightness, int, 0600);
 +static u32 intel_panel_compute_brightness(struct intel_connector *connector,
 +                                        u32 val)
  {
 +      struct drm_device *dev = connector->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
 +      struct intel_panel *panel = &connector->panel;
  
 -      if (IS_GEN4(dev))
 -              return I915_READ(BLC_PWM_CTL2) & BLM_COMBINATION_MODE;
 +      WARN_ON(panel->backlight.max == 0);
  
 -      if (IS_GEN2(dev))
 -              return I915_READ(BLC_PWM_CTL) & BLM_LEGACY_MODE;
 +      if (i915_panel_invert_brightness < 0)
 +              return val;
  
 -      return 0;
 +      if (i915_panel_invert_brightness > 0 ||
 +          dev_priv->quirks & QUIRK_INVERT_BRIGHTNESS) {
 +              return panel->backlight.max - val;
 +      }
 +
 +      return val;
  }
  
 -/* XXX: query mode clock or hardware clock and program max PWM appropriately
 - * when it's 0.
 - */
 -static u32 i915_read_blc_pwm_ctl(struct drm_device *dev, enum pipe pipe)
++static u32 bdw_get_backlight(struct intel_connector *connector)
+ {
++      struct drm_device *dev = connector->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
 -      u32 val;
 -      WARN_ON_SMP(!spin_is_locked(&dev_priv->backlight.lock));
 -
 -      /* Restore the CTL value if it lost, e.g. GPU reset */
 -
 -      if (HAS_PCH_SPLIT(dev_priv->dev)) {
 -              val = I915_READ(BLC_PWM_PCH_CTL2);
 -              if (dev_priv->regfile.saveBLC_PWM_CTL2 == 0) {
 -                      dev_priv->regfile.saveBLC_PWM_CTL2 = val;
 -              } else if (val == 0) {
 -                      val = dev_priv->regfile.saveBLC_PWM_CTL2;
 -                      I915_WRITE(BLC_PWM_PCH_CTL2, val);
 -              }
 -      } else if (IS_VALLEYVIEW(dev)) {
 -              val = I915_READ(VLV_BLC_PWM_CTL(pipe));
 -              if (dev_priv->regfile.saveBLC_PWM_CTL == 0) {
 -                      dev_priv->regfile.saveBLC_PWM_CTL = val;
 -                      dev_priv->regfile.saveBLC_PWM_CTL2 =
 -                              I915_READ(VLV_BLC_PWM_CTL2(pipe));
 -              } else if (val == 0) {
 -                      val = dev_priv->regfile.saveBLC_PWM_CTL;
 -                      I915_WRITE(VLV_BLC_PWM_CTL(pipe), val);
 -                      I915_WRITE(VLV_BLC_PWM_CTL2(pipe),
 -                                 dev_priv->regfile.saveBLC_PWM_CTL2);
 -              }
++      return I915_READ(BLC_PWM_PCH_CTL2) & BACKLIGHT_DUTY_CYCLE_MASK;
++}
 -              if (!val)
 -                      val = 0x0f42ffff;
 -      } else {
 -              val = I915_READ(BLC_PWM_CTL);
 -              if (dev_priv->regfile.saveBLC_PWM_CTL == 0) {
 -                      dev_priv->regfile.saveBLC_PWM_CTL = val;
 -                      if (INTEL_INFO(dev)->gen >= 4)
 -                              dev_priv->regfile.saveBLC_PWM_CTL2 =
 -                                      I915_READ(BLC_PWM_CTL2);
 -              } else if (val == 0) {
 -                      val = dev_priv->regfile.saveBLC_PWM_CTL;
 -                      I915_WRITE(BLC_PWM_CTL, val);
 -                      if (INTEL_INFO(dev)->gen >= 4)
 -                              I915_WRITE(BLC_PWM_CTL2,
 -                                         dev_priv->regfile.saveBLC_PWM_CTL2);
 -              }
 -      }
 +static u32 pch_get_backlight(struct intel_connector *connector)
 +{
 +      struct drm_device *dev = connector->base.dev;
 +      struct drm_i915_private *dev_priv = dev->dev_private;
  
 -      return val;
 +      return I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
  }
  
 -static u32 intel_panel_get_max_backlight(struct drm_device *dev,
 -                                       enum pipe pipe)
 +static u32 i9xx_get_backlight(struct intel_connector *connector)
  {
 -      u32 max;
 +      struct drm_device *dev = connector->base.dev;
 +      struct drm_i915_private *dev_priv = dev->dev_private;
 +      struct intel_panel *panel = &connector->panel;
 +      u32 val;
  
 -      max = i915_read_blc_pwm_ctl(dev, pipe);
 +      val = I915_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
 +      if (INTEL_INFO(dev)->gen < 4)
 +              val >>= 1;
  
 -      if (HAS_PCH_SPLIT(dev)) {
 -              max >>= 16;
 -      } else {
 -              if (INTEL_INFO(dev)->gen < 4)
 -                      max >>= 17;
 -              else
 -                      max >>= 16;
 +      if (panel->backlight.combination_mode) {
 +              u8 lbpc;
  
 -              if (is_backlight_combination_mode(dev))
 -                      max *= 0xff;
 +              pci_read_config_byte(dev->pdev, PCI_LBPC, &lbpc);
 +              val *= lbpc;
        }
  
 -      DRM_DEBUG_DRIVER("max backlight PWM = %d\n", max);
 -
 -      return max;
 +      return val;
  }
  
 -static int i915_panel_invert_brightness;
 -MODULE_PARM_DESC(invert_brightness, "Invert backlight brightness "
 -      "(-1 force normal, 0 machine defaults, 1 force inversion), please "
 -      "report PCI device ID, subsystem vendor and subsystem device ID "
 -      "to dri-devel@lists.freedesktop.org, if your machine needs it. "
 -      "It will then be included in an upcoming module version.");
 -module_param_named(invert_brightness, i915_panel_invert_brightness, int, 0600);
 -static u32 intel_panel_compute_brightness(struct drm_device *dev,
 -                                        enum pipe pipe, u32 val)
 +static u32 _vlv_get_backlight(struct drm_device *dev, enum pipe pipe)
  {
        struct drm_i915_private *dev_priv = dev->dev_private;
  
@@@ -414,29 -481,44 +422,37 @@@ static u32 intel_panel_get_backlight(st
        return val;
  }
  
 -static void intel_bdw_panel_set_backlight(struct drm_device *dev, u32 level)
++static void bdw_set_backlight(struct intel_connector *connector, u32 level)
+ {
++      struct drm_device *dev = connector->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 val = I915_READ(BLC_PWM_PCH_CTL2) & ~BACKLIGHT_DUTY_CYCLE_MASK;
+       I915_WRITE(BLC_PWM_PCH_CTL2, val | level);
+ }
 -static void intel_pch_panel_set_backlight(struct drm_device *dev, u32 level)
 +static void pch_set_backlight(struct intel_connector *connector, u32 level)
  {
 +      struct drm_device *dev = connector->base.dev;
        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);
 +      u32 tmp;
 +
 +      tmp = I915_READ(BLC_PWM_CPU_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
 +      I915_WRITE(BLC_PWM_CPU_CTL, tmp | level);
  }
  
 -static void intel_panel_actually_set_backlight(struct drm_device *dev,
 -                                             enum pipe pipe, u32 level)
 +static void i9xx_set_backlight(struct intel_connector *connector, u32 level)
  {
 +      struct drm_device *dev = connector->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
 -      u32 tmp;
 -      int reg;
 -
 -      DRM_DEBUG_DRIVER("set backlight PWM = %d\n", level);
 -      level = intel_panel_compute_brightness(dev, pipe, level);
 +      struct intel_panel *panel = &connector->panel;
 +      u32 tmp, mask;
  
 -      if (IS_BROADWELL(dev))
 -              return intel_bdw_panel_set_backlight(dev, level);
 -      else if (HAS_PCH_SPLIT(dev))
 -              return intel_pch_panel_set_backlight(dev, level);
 +      WARN_ON(panel->backlight.max == 0);
  
 -      if (is_backlight_combination_mode(dev)) {
 -              u32 max = intel_panel_get_max_backlight(dev, pipe);
 +      if (panel->backlight.combination_mode) {
                u8 lbpc;
  
 -              /* we're screwed, but keep behaviour backwards compatible */
 -              if (!max)
 -                      max = 1;
 -
 -              lbpc = level * 0xfe / max + 1;
 +              lbpc = level * 0xfe / panel->backlight.max + 1;
                level /= lbpc;
                pci_write_config_byte(dev->pdev, PCI_LBPC, lbpc);
        }
@@@ -577,15 -593,34 +593,47 @@@ void intel_panel_disable_backlight(stru
                return;
        }
  
 -      spin_lock_irqsave(&dev_priv->backlight.lock, flags);
 +      spin_lock_irqsave(&dev_priv->backlight_lock, flags);
  
 -      dev_priv->backlight.enabled = false;
 -      intel_panel_actually_set_backlight(dev, pipe, 0);
 +      panel->backlight.enabled = false;
 +      dev_priv->display.disable_backlight(connector);
  
 -      if (INTEL_INFO(dev)->gen >= 4) {
 -              uint32_t reg, tmp;
 +      spin_unlock_irqrestore(&dev_priv->backlight_lock, flags);
 +}
  
 -              if (HAS_PCH_SPLIT(dev))
 -                      reg = BLC_PWM_CPU_CTL2;
 -              else if (IS_VALLEYVIEW(dev))
 -                      reg = VLV_BLC_PWM_CTL2(pipe);
 -              else
 -                      reg = BLC_PWM_CTL2;
++static void bdw_enable_backlight(struct intel_connector *connector)
++{
++      struct drm_device *dev = connector->base.dev;
++      struct drm_i915_private *dev_priv = dev->dev_private;
++      struct intel_panel *panel = &connector->panel;
++      u32 pch_ctl1, pch_ctl2;
++
++      pch_ctl1 = I915_READ(BLC_PWM_PCH_CTL1);
++      if (pch_ctl1 & BLM_PCH_PWM_ENABLE) {
++              DRM_DEBUG_KMS("pch backlight already enabled\n");
++              pch_ctl1 &= ~BLM_PCH_PWM_ENABLE;
++              I915_WRITE(BLC_PWM_PCH_CTL1, pch_ctl1);
++      }
 -              I915_WRITE(reg, I915_READ(reg) & ~BLM_PWM_ENABLE);
++      pch_ctl2 = panel->backlight.max << 16;
++      I915_WRITE(BLC_PWM_PCH_CTL2, pch_ctl2);
 -              if (HAS_PCH_SPLIT(dev)) {
 -                      tmp = I915_READ(BLC_PWM_PCH_CTL1);
 -                      tmp &= ~BLM_PCH_PWM_ENABLE;
 -                      I915_WRITE(BLC_PWM_PCH_CTL1, tmp);
 -              }
 -      }
++      pch_ctl1 = 0;
++      if (panel->backlight.active_low_pwm)
++              pch_ctl1 |= BLM_PCH_POLARITY;
 -      spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
++      /* BDW always uses the pch pwm controls. */
++      pch_ctl1 |= BLM_PCH_OVERRIDE_ENABLE;
++
++      I915_WRITE(BLC_PWM_PCH_CTL1, pch_ctl1);
++      POSTING_READ(BLC_PWM_PCH_CTL1);
++      I915_WRITE(BLC_PWM_PCH_CTL1, pch_ctl1 | BLM_PCH_PWM_ENABLE);
++
++      /* This won't stick until the above enable. */
++      intel_panel_actually_set_backlight(connector, panel->backlight.level);
+ }
 -void intel_panel_enable_backlight(struct intel_connector *connector)
 +static void pch_enable_backlight(struct intel_connector *connector)
  {
        struct drm_device *dev = connector->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        enum pipe pipe = intel_get_pipe_from_connector(connector);
        enum transcoder cpu_transcoder =
                intel_pipe_to_cpu_transcoder(dev_priv, pipe);
 -      unsigned long flags;
 +      u32 cpu_ctl2, pch_ctl1, pch_ctl2;
  
 -      if (pipe == INVALID_PIPE)
 -              return;
 +      cpu_ctl2 = I915_READ(BLC_PWM_CPU_CTL2);
 +      if (cpu_ctl2 & BLM_PWM_ENABLE) {
 +              WARN(1, "cpu backlight already enabled\n");
 +              cpu_ctl2 &= ~BLM_PWM_ENABLE;
 +              I915_WRITE(BLC_PWM_CPU_CTL2, cpu_ctl2);
 +      }
  
 -      DRM_DEBUG_KMS("pipe %c\n", pipe_name(pipe));
 +      pch_ctl1 = I915_READ(BLC_PWM_PCH_CTL1);
 +      if (pch_ctl1 & BLM_PCH_PWM_ENABLE) {
 +              DRM_DEBUG_KMS("pch backlight already enabled\n");
 +              pch_ctl1 &= ~BLM_PCH_PWM_ENABLE;
 +              I915_WRITE(BLC_PWM_PCH_CTL1, pch_ctl1);
 +      }
  
 -      spin_lock_irqsave(&dev_priv->backlight.lock, flags);
 +      if (cpu_transcoder == TRANSCODER_EDP)
 +              cpu_ctl2 = BLM_TRANSCODER_EDP;
 +      else
 +              cpu_ctl2 = BLM_PIPE(cpu_transcoder);
 +      I915_WRITE(BLC_PWM_CPU_CTL2, cpu_ctl2);
 +      POSTING_READ(BLC_PWM_CPU_CTL2);
 +      I915_WRITE(BLC_PWM_CPU_CTL2, cpu_ctl2 | BLM_PWM_ENABLE);
 +
 +      /* This won't stick until the above enable. */
 +      intel_panel_actually_set_backlight(connector, panel->backlight.level);
 +
 +      pch_ctl2 = panel->backlight.max << 16;
 +      I915_WRITE(BLC_PWM_PCH_CTL2, pch_ctl2);
 +
 +      pch_ctl1 = 0;
 +      if (panel->backlight.active_low_pwm)
 +              pch_ctl1 |= BLM_PCH_POLARITY;
++
 +      I915_WRITE(BLC_PWM_PCH_CTL1, pch_ctl1);
 +      POSTING_READ(BLC_PWM_PCH_CTL1);
 +      I915_WRITE(BLC_PWM_PCH_CTL1, pch_ctl1 | BLM_PCH_PWM_ENABLE);
 +}
 +
 +static void i9xx_enable_backlight(struct intel_connector *connector)
 +{
 +      struct drm_device *dev = connector->base.dev;
 +      struct drm_i915_private *dev_priv = dev->dev_private;
 +      struct intel_panel *panel = &connector->panel;
 +      u32 ctl, freq;
  
 -      if (dev_priv->backlight.level == 0) {
 -              dev_priv->backlight.level = intel_panel_get_max_backlight(dev,
 -                                                                        pipe);
 -              if (dev_priv->backlight.device)
 -                      dev_priv->backlight.device->props.brightness =
 -                              dev_priv->backlight.level;
 +      ctl = I915_READ(BLC_PWM_CTL);
 +      if (ctl & BACKLIGHT_DUTY_CYCLE_MASK_PNV) {
 +              WARN(1, "backlight already enabled\n");
 +              I915_WRITE(BLC_PWM_CTL, 0);
        }
  
 -      if (INTEL_INFO(dev)->gen >= 4) {
 -              uint32_t reg, tmp;
 +      freq = panel->backlight.max;
 +      if (panel->backlight.combination_mode)
 +              freq /= 0xff;
  
 -              if (HAS_PCH_SPLIT(dev))
 -                      reg = BLC_PWM_CPU_CTL2;
 -              else if (IS_VALLEYVIEW(dev))
 -                      reg = VLV_BLC_PWM_CTL2(pipe);
 -              else
 -                      reg = BLC_PWM_CTL2;
 +      ctl = freq << 17;
 +      if (IS_GEN2(dev) && panel->backlight.combination_mode)
 +              ctl |= BLM_LEGACY_MODE;
 +      if (IS_PINEVIEW(dev) && panel->backlight.active_low_pwm)
 +              ctl |= BLM_POLARITY_PNV;
  
 -              tmp = I915_READ(reg);
 +      I915_WRITE(BLC_PWM_CTL, ctl);
 +      POSTING_READ(BLC_PWM_CTL);
  
 -              /* Note that this can also get called through dpms changes. And
 -               * we don't track the backlight dpms state, hence check whether
 -               * we have to do anything first. */
 -              if (tmp & BLM_PWM_ENABLE)
 -                      goto set_level;
 +      /* XXX: combine this into above write? */
 +      intel_panel_actually_set_backlight(connector, panel->backlight.level);
 +}
  
 -              if (INTEL_INFO(dev)->num_pipes == 3)
 -                      tmp &= ~BLM_PIPE_SELECT_IVB;
 -              else
 -                      tmp &= ~BLM_PIPE_SELECT;
 +static void i965_enable_backlight(struct intel_connector *connector)
 +{
 +      struct drm_device *dev = connector->base.dev;
 +      struct drm_i915_private *dev_priv = dev->dev_private;
 +      struct intel_panel *panel = &connector->panel;
 +      enum pipe pipe = intel_get_pipe_from_connector(connector);
 +      u32 ctl, ctl2, freq;
  
 -              if (cpu_transcoder == TRANSCODER_EDP)
 -                      tmp |= BLM_TRANSCODER_EDP;
 -              else
 -                      tmp |= BLM_PIPE(cpu_transcoder);
 -              tmp &= ~BLM_PWM_ENABLE;
 -
 -              I915_WRITE(reg, tmp);
 -              POSTING_READ(reg);
 -              I915_WRITE(reg, tmp | BLM_PWM_ENABLE);
 -
 -              if (IS_BROADWELL(dev)) {
 -                      /*
 -                       * Broadwell requires PCH override to drive the PCH
 -                       * backlight pin. The above will configure the CPU
 -                       * backlight pin, which we don't plan to use.
 -                       */
 -                      tmp = I915_READ(BLC_PWM_PCH_CTL1);
 -                      tmp |= BLM_PCH_OVERRIDE_ENABLE | BLM_PCH_PWM_ENABLE;
 -                      I915_WRITE(BLC_PWM_PCH_CTL1, tmp);
 -              } else if (HAS_PCH_SPLIT(dev) &&
 -                  !(dev_priv->quirks & QUIRK_NO_PCH_PWM_ENABLE)) {
 -                      tmp = I915_READ(BLC_PWM_PCH_CTL1);
 -                      tmp |= BLM_PCH_PWM_ENABLE;
 -                      tmp &= ~BLM_PCH_OVERRIDE_ENABLE;
 -                      I915_WRITE(BLC_PWM_PCH_CTL1, tmp);
 -              }
 +      ctl2 = I915_READ(BLC_PWM_CTL2);
 +      if (ctl2 & BLM_PWM_ENABLE) {
 +              WARN(1, "backlight already enabled\n");
 +              ctl2 &= ~BLM_PWM_ENABLE;
 +              I915_WRITE(BLC_PWM_CTL2, ctl2);
        }
  
 -set_level:
 -      /* Call below after setting BLC_PWM_CPU_CTL2 and BLC_PWM_PCH_CTL1.
 -       * BLC_PWM_CPU_CTL may be cleared to zero automatically when these
 -       * registers are set.
 -       */
 -      dev_priv->backlight.enabled = true;
 -      intel_panel_actually_set_backlight(dev, pipe,
 -                                         dev_priv->backlight.level);
 +      freq = panel->backlight.max;
 +      if (panel->backlight.combination_mode)
 +              freq /= 0xff;
  
 -      spin_unlock_irqrestore(&dev_priv->backlight.lock, flags);
 +      ctl = freq << 16;
 +      I915_WRITE(BLC_PWM_CTL, ctl);
 +
 +      /* XXX: combine this into above write? */
 +      intel_panel_actually_set_backlight(connector, panel->backlight.level);
 +
 +      ctl2 = BLM_PIPE(pipe);
 +      if (panel->backlight.combination_mode)
 +              ctl2 |= BLM_COMBINATION_MODE;
 +      if (panel->backlight.active_low_pwm)
 +              ctl2 |= BLM_POLARITY_I965;
 +      I915_WRITE(BLC_PWM_CTL2, ctl2);
 +      POSTING_READ(BLC_PWM_CTL2);
 +      I915_WRITE(BLC_PWM_CTL2, ctl2 | BLM_PWM_ENABLE);
  }
  
 -/* FIXME: use VBT vals to init PWM_CTL and PWM_CTL2 correctly */
 -static void intel_panel_init_backlight_regs(struct drm_device *dev)
 +static void vlv_enable_backlight(struct intel_connector *connector)
  {
 +      struct drm_device *dev = connector->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
 +      struct intel_panel *panel = &connector->panel;
 +      enum pipe pipe = intel_get_pipe_from_connector(connector);
 +      u32 ctl, ctl2;
  
 -      if (IS_VALLEYVIEW(dev)) {
 -              enum pipe pipe;
 +      ctl2 = I915_READ(VLV_BLC_PWM_CTL2(pipe));
 +      if (ctl2 & BLM_PWM_ENABLE) {
 +              WARN(1, "backlight already enabled\n");
 +              ctl2 &= ~BLM_PWM_ENABLE;
 +              I915_WRITE(VLV_BLC_PWM_CTL2(pipe), ctl2);
 +      }
  
 -              for_each_pipe(pipe) {
 -                      u32 cur_val = I915_READ(VLV_BLC_PWM_CTL(pipe));
 +      ctl = panel->backlight.max << 16;
 +      I915_WRITE(VLV_BLC_PWM_CTL(pipe), ctl);
  
 -                      /* Skip if the modulation freq is already set */
 -                      if (cur_val & ~BACKLIGHT_DUTY_CYCLE_MASK)
 -                              continue;
 +      /* XXX: combine this into above write? */
 +      intel_panel_actually_set_backlight(connector, panel->backlight.level);
  
 -                      cur_val &= BACKLIGHT_DUTY_CYCLE_MASK;
 -                      I915_WRITE(VLV_BLC_PWM_CTL(pipe), (0xf42 << 16) |
 -                                 cur_val);
 -              }
 -      }
 +      ctl2 = 0;
 +      if (panel->backlight.active_low_pwm)
 +              ctl2 |= BLM_POLARITY_I965;
 +      I915_WRITE(VLV_BLC_PWM_CTL2(pipe), ctl2);
 +      POSTING_READ(VLV_BLC_PWM_CTL2(pipe));
 +      I915_WRITE(VLV_BLC_PWM_CTL2(pipe), ctl2 | BLM_PWM_ENABLE);
  }
  
 -static void intel_panel_init_backlight(struct drm_device *dev)
 +void intel_panel_enable_backlight(struct intel_connector *connector)
  {
 +      struct drm_device *dev = connector->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
 +      struct intel_panel *panel = &connector->panel;
 +      enum pipe pipe = intel_get_pipe_from_connector(connector);
 +      unsigned long flags;
  
 -      intel_panel_init_backlight_regs(dev);
 +      if (pipe == INVALID_PIPE)
 +              return;
 +
 +      DRM_DEBUG_KMS("pipe %c\n", pipe_name(pipe));
 +
 +      spin_lock_irqsave(&dev_priv->backlight_lock, flags);
 +
 +      WARN_ON(panel->backlight.max == 0);
 +
 +      if (panel->backlight.level == 0) {
 +              panel->backlight.level = panel->backlight.max;
 +              if (panel->backlight.device)
 +                      panel->backlight.device->props.brightness =
 +                              panel->backlight.level;
 +      }
  
 -      dev_priv->backlight.level = intel_panel_get_backlight(dev, 0);
 -      dev_priv->backlight.enabled = dev_priv->backlight.level != 0;
 +      dev_priv->display.enable_backlight(connector);
 +      panel->backlight.enabled = true;
 +
 +      spin_unlock_irqrestore(&dev_priv->backlight_lock, flags);
  }
  
  enum drm_connector_status
@@@ -844,224 -835,26 +893,254 @@@ static int intel_backlight_device_regis
        return 0;
  }
  
 -void intel_panel_destroy_backlight(struct drm_device *dev)
 +static void intel_backlight_device_unregister(struct intel_connector *connector)
 +{
 +      struct intel_panel *panel = &connector->panel;
 +
 +      if (panel->backlight.device) {
 +              backlight_device_unregister(panel->backlight.device);
 +              panel->backlight.device = NULL;
 +      }
 +}
 +#else /* CONFIG_BACKLIGHT_CLASS_DEVICE */
 +static int intel_backlight_device_register(struct intel_connector *connector)
 +{
 +      return 0;
 +}
 +static void intel_backlight_device_unregister(struct intel_connector *connector)
 +{
 +}
 +#endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
 +
 +/*
 + * Note: The setup hooks can't assume pipe is set!
 + *
 + * XXX: Query mode clock or hardware clock and program PWM modulation frequency
 + * appropriately when it's 0. Use VBT and/or sane defaults.
 + */
++static int bdw_setup_backlight(struct intel_connector *connector)
+ {
++      struct drm_device *dev = connector->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
 -      if (dev_priv->backlight.device) {
 -              backlight_device_unregister(dev_priv->backlight.device);
 -              dev_priv->backlight.device = NULL;
++      struct intel_panel *panel = &connector->panel;
++      u32 pch_ctl1, pch_ctl2, val;
++
++      pch_ctl1 = I915_READ(BLC_PWM_PCH_CTL1);
++      panel->backlight.active_low_pwm = pch_ctl1 & BLM_PCH_POLARITY;
++
++      pch_ctl2 = I915_READ(BLC_PWM_PCH_CTL2);
++      panel->backlight.max = pch_ctl2 >> 16;
++      if (!panel->backlight.max)
++              return -ENODEV;
++
++      val = bdw_get_backlight(connector);
++      panel->backlight.level = intel_panel_compute_brightness(connector, val);
++
++      panel->backlight.enabled = (pch_ctl1 & BLM_PCH_PWM_ENABLE) &&
++              panel->backlight.level != 0;
++
++      return 0;
++}
++
 +static int pch_setup_backlight(struct intel_connector *connector)
 +{
 +      struct drm_device *dev = connector->base.dev;
 +      struct drm_i915_private *dev_priv = dev->dev_private;
 +      struct intel_panel *panel = &connector->panel;
 +      u32 cpu_ctl2, pch_ctl1, pch_ctl2, val;
 +
 +      pch_ctl1 = I915_READ(BLC_PWM_PCH_CTL1);
 +      panel->backlight.active_low_pwm = pch_ctl1 & BLM_PCH_POLARITY;
 +
 +      pch_ctl2 = I915_READ(BLC_PWM_PCH_CTL2);
 +      panel->backlight.max = pch_ctl2 >> 16;
 +      if (!panel->backlight.max)
 +              return -ENODEV;
 +
 +      val = pch_get_backlight(connector);
 +      panel->backlight.level = intel_panel_compute_brightness(connector, val);
 +
 +      cpu_ctl2 = I915_READ(BLC_PWM_CPU_CTL2);
 +      panel->backlight.enabled = (cpu_ctl2 & BLM_PWM_ENABLE) &&
 +              (pch_ctl1 & BLM_PCH_PWM_ENABLE) && panel->backlight.level != 0;
 +
 +      return 0;
 +}
 +
 +static int i9xx_setup_backlight(struct intel_connector *connector)
 +{
 +      struct drm_device *dev = connector->base.dev;
 +      struct drm_i915_private *dev_priv = dev->dev_private;
 +      struct intel_panel *panel = &connector->panel;
 +      u32 ctl, val;
 +
 +      ctl = I915_READ(BLC_PWM_CTL);
 +
 +      if (IS_GEN2(dev))
 +              panel->backlight.combination_mode = ctl & BLM_LEGACY_MODE;
 +
 +      if (IS_PINEVIEW(dev))
 +              panel->backlight.active_low_pwm = ctl & BLM_POLARITY_PNV;
 +
 +      panel->backlight.max = ctl >> 17;
 +      if (panel->backlight.combination_mode)
 +              panel->backlight.max *= 0xff;
 +
 +      if (!panel->backlight.max)
 +              return -ENODEV;
 +
 +      val = i9xx_get_backlight(connector);
 +      panel->backlight.level = intel_panel_compute_brightness(connector, val);
 +
 +      panel->backlight.enabled = panel->backlight.level != 0;
 +
 +      return 0;
 +}
 +
 +static int i965_setup_backlight(struct intel_connector *connector)
 +{
 +      struct drm_device *dev = connector->base.dev;
 +      struct drm_i915_private *dev_priv = dev->dev_private;
 +      struct intel_panel *panel = &connector->panel;
 +      u32 ctl, ctl2, val;
 +
 +      ctl2 = I915_READ(BLC_PWM_CTL2);
 +      panel->backlight.combination_mode = ctl2 & BLM_COMBINATION_MODE;
 +      panel->backlight.active_low_pwm = ctl2 & BLM_POLARITY_I965;
 +
 +      ctl = I915_READ(BLC_PWM_CTL);
 +      panel->backlight.max = ctl >> 16;
 +      if (panel->backlight.combination_mode)
 +              panel->backlight.max *= 0xff;
 +
 +      if (!panel->backlight.max)
 +              return -ENODEV;
 +
 +      val = i9xx_get_backlight(connector);
 +      panel->backlight.level = intel_panel_compute_brightness(connector, val);
 +
 +      panel->backlight.enabled = (ctl2 & BLM_PWM_ENABLE) &&
 +              panel->backlight.level != 0;
 +
 +      return 0;
 +}
 +
 +static int vlv_setup_backlight(struct intel_connector *connector)
 +{
 +      struct drm_device *dev = connector->base.dev;
 +      struct drm_i915_private *dev_priv = dev->dev_private;
 +      struct intel_panel *panel = &connector->panel;
 +      enum pipe pipe;
 +      u32 ctl, ctl2, val;
 +
 +      for_each_pipe(pipe) {
 +              u32 cur_val = I915_READ(VLV_BLC_PWM_CTL(pipe));
 +
 +              /* Skip if the modulation freq is already set */
 +              if (cur_val & ~BACKLIGHT_DUTY_CYCLE_MASK)
 +                      continue;
 +
 +              cur_val &= BACKLIGHT_DUTY_CYCLE_MASK;
 +              I915_WRITE(VLV_BLC_PWM_CTL(pipe), (0xf42 << 16) |
 +                         cur_val);
        }
 +
 +      ctl2 = I915_READ(VLV_BLC_PWM_CTL2(PIPE_A));
 +      panel->backlight.active_low_pwm = ctl2 & BLM_POLARITY_I965;
 +
 +      ctl = I915_READ(VLV_BLC_PWM_CTL(PIPE_A));
 +      panel->backlight.max = ctl >> 16;
 +      if (!panel->backlight.max)
 +              return -ENODEV;
 +
 +      val = _vlv_get_backlight(dev, PIPE_A);
 +      panel->backlight.level = intel_panel_compute_brightness(connector, val);
 +
 +      panel->backlight.enabled = (ctl2 & BLM_PWM_ENABLE) &&
 +              panel->backlight.level != 0;
 +
 +      return 0;
  }
 -#else
 +
  int intel_panel_setup_backlight(struct drm_connector *connector)
  {
 -      intel_panel_init_backlight(connector->dev);
 +      struct drm_device *dev = connector->dev;
 +      struct drm_i915_private *dev_priv = dev->dev_private;
 +      struct intel_connector *intel_connector = to_intel_connector(connector);
 +      struct intel_panel *panel = &intel_connector->panel;
 +      unsigned long flags;
 +      int ret;
 +
 +      /* set level and max in panel struct */
 +      spin_lock_irqsave(&dev_priv->backlight_lock, flags);
 +      ret = dev_priv->display.setup_backlight(intel_connector);
 +      spin_unlock_irqrestore(&dev_priv->backlight_lock, flags);
 +
 +      if (ret) {
 +              DRM_DEBUG_KMS("failed to setup backlight for connector %s\n",
 +                            drm_get_connector_name(connector));
 +              return ret;
 +      }
 +
 +      intel_backlight_device_register(intel_connector);
 +
 +      panel->backlight.present = true;
 +
 +      DRM_DEBUG_KMS("backlight initialized, %s, brightness %u/%u, "
 +                    "sysfs interface %sregistered\n",
 +                    panel->backlight.enabled ? "enabled" : "disabled",
 +                    panel->backlight.level, panel->backlight.max,
 +                    panel->backlight.device ? "" : "not ");
 +
        return 0;
  }
  
 -void intel_panel_destroy_backlight(struct drm_device *dev)
 +void intel_panel_destroy_backlight(struct drm_connector *connector)
  {
 -      return;
 +      struct intel_connector *intel_connector = to_intel_connector(connector);
 +      struct intel_panel *panel = &intel_connector->panel;
 +
 +      panel->backlight.present = false;
 +      intel_backlight_device_unregister(intel_connector);
 +}
 +
 +/* Set up chip specific backlight functions */
 +void intel_panel_init_backlight_funcs(struct drm_device *dev)
 +{
 +      struct drm_i915_private *dev_priv = dev->dev_private;
 +
-       if (HAS_PCH_SPLIT(dev)) {
++      if (IS_BROADWELL(dev)) {
++              dev_priv->display.setup_backlight = bdw_setup_backlight;
++              dev_priv->display.enable_backlight = bdw_enable_backlight;
++              dev_priv->display.disable_backlight = pch_disable_backlight;
++              dev_priv->display.set_backlight = bdw_set_backlight;
++              dev_priv->display.get_backlight = bdw_get_backlight;
++      } else if (HAS_PCH_SPLIT(dev)) {
 +              dev_priv->display.setup_backlight = pch_setup_backlight;
 +              dev_priv->display.enable_backlight = pch_enable_backlight;
 +              dev_priv->display.disable_backlight = pch_disable_backlight;
 +              dev_priv->display.set_backlight = pch_set_backlight;
 +              dev_priv->display.get_backlight = pch_get_backlight;
 +      } else if (IS_VALLEYVIEW(dev)) {
 +              dev_priv->display.setup_backlight = vlv_setup_backlight;
 +              dev_priv->display.enable_backlight = vlv_enable_backlight;
 +              dev_priv->display.disable_backlight = vlv_disable_backlight;
 +              dev_priv->display.set_backlight = vlv_set_backlight;
 +              dev_priv->display.get_backlight = vlv_get_backlight;
 +      } else if (IS_GEN4(dev)) {
 +              dev_priv->display.setup_backlight = i965_setup_backlight;
 +              dev_priv->display.enable_backlight = i965_enable_backlight;
 +              dev_priv->display.disable_backlight = i965_disable_backlight;
 +              dev_priv->display.set_backlight = i9xx_set_backlight;
 +              dev_priv->display.get_backlight = i9xx_get_backlight;
 +      } else {
 +              dev_priv->display.setup_backlight = i9xx_setup_backlight;
 +              dev_priv->display.enable_backlight = i9xx_enable_backlight;
 +              dev_priv->display.disable_backlight = i9xx_disable_backlight;
 +              dev_priv->display.set_backlight = i9xx_set_backlight;
 +              dev_priv->display.get_backlight = i9xx_get_backlight;
 +      }
  }
 -#endif
  
  int intel_panel_init(struct intel_panel *panel,
                     struct drm_display_mode *fixed_mode)