diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index a699efd..344f717 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -4531,6 +4531,7 @@
 #define SOUTH_CHICKEN2		0xc2004
 #define  FDI_MPHY_IOSFSB_RESET_STATUS	(1<<13)
 #define  FDI_MPHY_IOSFSB_RESET_CTL	(1<<12)
+#define  PWM_GRANULARITY		(1<<5)	/* LPT */
 #define  DPLS_EDP_PPS_FIX_DIS		(1<<0)
 
 #define _FDI_RXA_CHICKEN         0xc200c
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index 1cf8085f..c020ab0 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -916,10 +916,93 @@ 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!
+ * HSW/BDW: This value represents the period of the PWM stream in clock periods
+ * multiplied by 128 (default increment) or 16 (alternate increment, selected in
+ * LPT SOUTH_CHICKEN2 register bit 5).
  *
- * XXX: Query mode clock or hardware clock and program PWM modulation frequency
- * appropriately when it's 0. Use VBT and/or sane defaults.
+ * XXX: This only works when driving the PCH PWM. When driving the CPU PWM on
+ * the utility pin, the granularity needs to be determined by BLC_PWM_CTL bit
+ * 27.
+ */
+static int hsw_hz_to_pwm(struct drm_device *dev, u16 pwm_freq_hz)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	u32 mul, clock;
+
+	if (I915_READ(SOUTH_CHICKEN2) & PWM_GRANULARITY)
+		mul = 16;
+	else
+		mul = 128;
+
+	if (dev_priv->pch_id == INTEL_PCH_LPT_DEVICE_ID_TYPE)
+		clock = MHz(135); /* LPT:H */
+	else
+		clock = MHz(24); /* LPT:LP */
+
+	return clock / (pwm_freq_hz * mul);
+}
+
+/*
+ * ILK/SNB/IVB: This value represents the period of the PWM stream in PCH
+ * display raw clocks multiplied by 128.
+ */
+static int pch_hz_to_pwm(struct drm_device *dev, u16 pwm_freq_hz)
+{
+	int clock = MHz(intel_pch_rawclk(dev));
+
+	return clock / (pwm_freq_hz * 128);
+}
+
+/*
+ * Gen2: This field determines the number of time base events (display core
+ * clock frequency/32) in total for a complete cycle of modulated backlight
+ * control.
+ *
+ * Gen3: A time base event equals the display core clock ([DevPNV] HRAW clock)
+ * divided by 32.
+ */
+static int i9xx_hz_to_pwm(struct drm_device *dev, u16 pwm_freq_hz)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	int clock;
+
+	if (IS_PINEVIEW(dev))
+		clock = intel_hrawclk(dev);
+	else
+		clock = 1000 * dev_priv->display.get_display_clock_speed(dev);
+
+	return clock / (pwm_freq_hz * 32);
+}
+
+/*
+ * Gen4: This value represents the period of the PWM stream in display core
+ * clocks multiplied by 128.
+ */
+static int i965_hz_to_pwm(struct drm_device *dev, u16 pwm_freq_hz)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	int clock = 1000 * dev_priv->display.get_display_clock_speed(dev);
+
+	return clock / (pwm_freq_hz * 128);
+}
+
+/*
+ * VLV: This value represents the period of the PWM stream in display core
+ * clocks ([DevCTG] 100MHz HRAW clocks) multiplied by 128 or 25MHz S0IX clocks
+ * multiplied by 16.
+ *
+ * XXX: Where is this selected???
+ */
+static int vlv_hz_to_pwm(struct drm_device *dev, u16 pwm_freq_hz)
+{
+	if (1)
+		return MHz(25) / (pwm_freq_hz * 16);
+	else
+		return MHz(100) / (pwm_freq_hz * 128);
+}
+
+/*
+ * Note: The setup hooks can't assume pipe is set!
  */
 static int bdw_setup_backlight(struct intel_connector *connector)
 {
@@ -933,8 +1016,18 @@ static int bdw_setup_backlight(struct intel_connector *connector)
 
 	pch_ctl2 = I915_READ(BLC_PWM_PCH_CTL2);
 	panel->backlight.max = pch_ctl2 >> 16;
-	if (!panel->backlight.max)
-		return -ENODEV;
+
+	if (!panel->backlight.max) {
+		u16 pwm_freq_hz = dev_priv->vbt.backlight.pwm_freq_hz;
+
+		if (!pwm_freq_hz)
+			return -ENODEV;
+
+		DRM_DEBUG_KMS("setting backlight frequency to %u Hz from VBT\n",
+			      pwm_freq_hz);
+
+		panel->backlight.max = hsw_hz_to_pwm(dev, pwm_freq_hz);
+	}
 
 	val = bdw_get_backlight(connector);
 	panel->backlight.level = intel_panel_compute_brightness(connector, val);
@@ -957,8 +1050,21 @@ static int pch_setup_backlight(struct intel_connector *connector)
 
 	pch_ctl2 = I915_READ(BLC_PWM_PCH_CTL2);
 	panel->backlight.max = pch_ctl2 >> 16;
-	if (!panel->backlight.max)
-		return -ENODEV;
+
+	if (!panel->backlight.max) {
+		u16 pwm_freq_hz = dev_priv->vbt.backlight.pwm_freq_hz;
+
+		if (!pwm_freq_hz)
+			return -ENODEV;
+
+		DRM_DEBUG_KMS("setting backlight frequency to %u Hz from VBT\n",
+			      pwm_freq_hz);
+
+		if (IS_HASWELL(dev))
+			panel->backlight.max = hsw_hz_to_pwm(dev, pwm_freq_hz);
+		else
+			panel->backlight.max = pch_hz_to_pwm(dev, pwm_freq_hz);
+	}
 
 	val = pch_get_backlight(connector);
 	panel->backlight.level = intel_panel_compute_brightness(connector, val);
@@ -986,12 +1092,23 @@ static int i9xx_setup_backlight(struct intel_connector *connector)
 		panel->backlight.active_low_pwm = ctl & BLM_POLARITY_PNV;
 
 	panel->backlight.max = ctl >> 17;
+
+	if (!panel->backlight.max) {
+		u16 pwm_freq_hz = dev_priv->vbt.backlight.pwm_freq_hz;
+
+		if (!pwm_freq_hz)
+			return -ENODEV;
+
+		DRM_DEBUG_KMS("setting backlight frequency to %u Hz from VBT\n",
+			      pwm_freq_hz);
+
+		panel->backlight.max = i9xx_hz_to_pwm(dev, pwm_freq_hz);
+		panel->backlight.max >>= 1;
+	}
+
 	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);
 
@@ -1013,12 +1130,22 @@ static int i965_setup_backlight(struct intel_connector *connector)
 
 	ctl = I915_READ(BLC_PWM_CTL);
 	panel->backlight.max = ctl >> 16;
+
+	if (!panel->backlight.max) {
+		u16 pwm_freq_hz = dev_priv->vbt.backlight.pwm_freq_hz;
+
+		if (!pwm_freq_hz)
+			return -ENODEV;
+
+		DRM_DEBUG_KMS("setting backlight frequency to %u Hz from VBT\n",
+			      pwm_freq_hz);
+
+		panel->backlight.max = i965_hz_to_pwm(dev, pwm_freq_hz);
+	}
+
 	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);
 
@@ -1033,30 +1160,44 @@ 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;
+	enum pipe p, pipe = PIPE_A;
 	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;
+	/* prefer a pipe that's configured and enabled, fall back to A */
+	for_each_pipe(p) {
+		ctl = I915_READ(VLV_BLC_PWM_CTL(p));
+		if (ctl >> 16) {
+			pipe = p;
 
-		cur_val &= BACKLIGHT_DUTY_CYCLE_MASK;
-		I915_WRITE(VLV_BLC_PWM_CTL(pipe), (0xf42 << 16) |
-			   cur_val);
+			ctl2 = I915_READ(VLV_BLC_PWM_CTL2(p));
+			if (ctl2 & BLM_PWM_ENABLE)
+				break;
+		}
 	}
 
-	ctl2 = I915_READ(VLV_BLC_PWM_CTL2(PIPE_A));
+	ctl2 = I915_READ(VLV_BLC_PWM_CTL2(pipe));
 	panel->backlight.active_low_pwm = ctl2 & BLM_POLARITY_I965;
 
-	ctl = I915_READ(VLV_BLC_PWM_CTL(PIPE_A));
+	ctl = I915_READ(VLV_BLC_PWM_CTL(pipe));
 	panel->backlight.max = ctl >> 16;
-	if (!panel->backlight.max)
-		return -ENODEV;
 
-	val = _vlv_get_backlight(dev, PIPE_A);
+	if (!panel->backlight.max) {
+		u16 pwm_freq_hz = dev_priv->vbt.backlight.pwm_freq_hz;
+
+		if (!pwm_freq_hz) {
+			pwm_freq_hz = 400;
+			DRM_DEBUG_KMS("defaulting to %u Hz backlight on %s\n",
+				      pwm_freq_hz,
+				      drm_get_connector_name(&connector->base));
+		} else {
+			DRM_DEBUG_KMS("setting backlight frequency to %u Hz from VBT\n",
+				      pwm_freq_hz);
+		}
+
+		panel->backlight.max = vlv_hz_to_pwm(dev, pwm_freq_hz);
+	}
+
+	val = _vlv_get_backlight(dev, pipe);
 	panel->backlight.level = intel_panel_compute_brightness(connector, val);
 
 	panel->backlight.enabled = (ctl2 & BLM_PWM_ENABLE) &&
