@@ -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
@@ -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) &&
Signed-off-by: Jani Nikula <jani.nikula@intel.com> --- drivers/gpu/drm/i915/i915_reg.h | 1 + drivers/gpu/drm/i915/intel_panel.c | 197 +++++++++++++++++++++++++++++++----- 2 files changed, 170 insertions(+), 28 deletions(-)