Merge branch 'for-next' of http://git.agner.ch/git/linux-drm-fsl-dcu into drm-next
[cascardo/linux.git] / drivers / gpu / drm / i915 / intel_lvds.c
index 4955047..e1d47d5 100644 (file)
@@ -48,6 +48,20 @@ struct intel_lvds_connector {
        struct notifier_block lid_notifier;
 };
 
+struct intel_lvds_pps {
+       /* 100us units */
+       int t1_t2;
+       int t3;
+       int t4;
+       int t5;
+       int tx;
+
+       int divider;
+
+       int port;
+       bool powerdown_on_reset;
+};
+
 struct intel_lvds_encoder {
        struct intel_encoder base;
 
@@ -55,6 +69,9 @@ struct intel_lvds_encoder {
        i915_reg_t reg;
        u32 a3_power;
 
+       struct intel_lvds_pps init_pps;
+       u32 init_lvds_val;
+
        struct intel_lvds_connector *attached_connector;
 };
 
@@ -136,28 +153,108 @@ static void intel_lvds_get_config(struct intel_encoder *encoder,
        pipe_config->base.adjusted_mode.crtc_clock = pipe_config->port_clock;
 }
 
-static void intel_pre_enable_lvds(struct intel_encoder *encoder)
+static void intel_lvds_pps_get_hw_state(struct drm_i915_private *dev_priv,
+                                       struct intel_lvds_pps *pps)
+{
+       u32 val;
+
+       pps->powerdown_on_reset = I915_READ(PP_CONTROL(0)) & PANEL_POWER_RESET;
+
+       val = I915_READ(PP_ON_DELAYS(0));
+       pps->port = (val & PANEL_PORT_SELECT_MASK) >>
+                   PANEL_PORT_SELECT_SHIFT;
+       pps->t1_t2 = (val & PANEL_POWER_UP_DELAY_MASK) >>
+                    PANEL_POWER_UP_DELAY_SHIFT;
+       pps->t5 = (val & PANEL_LIGHT_ON_DELAY_MASK) >>
+                 PANEL_LIGHT_ON_DELAY_SHIFT;
+
+       val = I915_READ(PP_OFF_DELAYS(0));
+       pps->t3 = (val & PANEL_POWER_DOWN_DELAY_MASK) >>
+                 PANEL_POWER_DOWN_DELAY_SHIFT;
+       pps->tx = (val & PANEL_LIGHT_OFF_DELAY_MASK) >>
+                 PANEL_LIGHT_OFF_DELAY_SHIFT;
+
+       val = I915_READ(PP_DIVISOR(0));
+       pps->divider = (val & PP_REFERENCE_DIVIDER_MASK) >>
+                      PP_REFERENCE_DIVIDER_SHIFT;
+       val = (val & PANEL_POWER_CYCLE_DELAY_MASK) >>
+             PANEL_POWER_CYCLE_DELAY_SHIFT;
+       /*
+        * Remove the BSpec specified +1 (100ms) offset that accounts for a
+        * too short power-cycle delay due to the asynchronous programming of
+        * the register.
+        */
+       if (val)
+               val--;
+       /* Convert from 100ms to 100us units */
+       pps->t4 = val * 1000;
+
+       if (INTEL_INFO(dev_priv)->gen <= 4 &&
+           pps->t1_t2 == 0 && pps->t5 == 0 && pps->t3 == 0 && pps->tx == 0) {
+               DRM_DEBUG_KMS("Panel power timings uninitialized, "
+                             "setting defaults\n");
+               /* Set T2 to 40ms and T5 to 200ms in 100 usec units */
+               pps->t1_t2 = 40 * 10;
+               pps->t5 = 200 * 10;
+               /* Set T3 to 35ms and Tx to 200ms in 100 usec units */
+               pps->t3 = 35 * 10;
+               pps->tx = 200 * 10;
+       }
+
+       DRM_DEBUG_DRIVER("LVDS PPS:t1+t2 %d t3 %d t4 %d t5 %d tx %d "
+                        "divider %d port %d powerdown_on_reset %d\n",
+                        pps->t1_t2, pps->t3, pps->t4, pps->t5, pps->tx,
+                        pps->divider, pps->port, pps->powerdown_on_reset);
+}
+
+static void intel_lvds_pps_init_hw(struct drm_i915_private *dev_priv,
+                                  struct intel_lvds_pps *pps)
+{
+       u32 val;
+
+       val = I915_READ(PP_CONTROL(0));
+       WARN_ON((val & PANEL_UNLOCK_MASK) != PANEL_UNLOCK_REGS);
+       if (pps->powerdown_on_reset)
+               val |= PANEL_POWER_RESET;
+       I915_WRITE(PP_CONTROL(0), val);
+
+       I915_WRITE(PP_ON_DELAYS(0), (pps->port << PANEL_PORT_SELECT_SHIFT) |
+                                   (pps->t1_t2 << PANEL_POWER_UP_DELAY_SHIFT) |
+                                   (pps->t5 << PANEL_LIGHT_ON_DELAY_SHIFT));
+       I915_WRITE(PP_OFF_DELAYS(0), (pps->t3 << PANEL_POWER_DOWN_DELAY_SHIFT) |
+                                    (pps->tx << PANEL_LIGHT_OFF_DELAY_SHIFT));
+
+       val = pps->divider << PP_REFERENCE_DIVIDER_SHIFT;
+       val |= (DIV_ROUND_UP(pps->t4, 1000) + 1) <<
+              PANEL_POWER_CYCLE_DELAY_SHIFT;
+       I915_WRITE(PP_DIVISOR(0), val);
+}
+
+static void intel_pre_enable_lvds(struct intel_encoder *encoder,
+                                 struct intel_crtc_state *pipe_config,
+                                 struct drm_connector_state *conn_state)
 {
        struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
-       struct drm_device *dev = encoder->base.dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
-       const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode;
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+       struct intel_crtc *crtc = to_intel_crtc(pipe_config->base.crtc);
+       const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
        int pipe = crtc->pipe;
        u32 temp;
 
-       if (HAS_PCH_SPLIT(dev)) {
+       if (HAS_PCH_SPLIT(dev_priv)) {
                assert_fdi_rx_pll_disabled(dev_priv, pipe);
                assert_shared_dpll_disabled(dev_priv,
-                                           crtc->config->shared_dpll);
+                                           pipe_config->shared_dpll);
        } else {
                assert_pll_disabled(dev_priv, pipe);
        }
 
-       temp = I915_READ(lvds_encoder->reg);
+       intel_lvds_pps_init_hw(dev_priv, &lvds_encoder->init_pps);
+
+       temp = lvds_encoder->init_lvds_val;
        temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP;
 
-       if (HAS_PCH_CPT(dev)) {
+       if (HAS_PCH_CPT(dev_priv)) {
                temp &= ~PORT_TRANS_SEL_MASK;
                temp |= PORT_TRANS_SEL_CPT(pipe);
        } else {
@@ -170,7 +267,7 @@ static void intel_pre_enable_lvds(struct intel_encoder *encoder)
 
        /* set the corresponsding LVDS_BORDER bit */
        temp &= ~LVDS_BORDER_ENABLE;
-       temp |= crtc->config->gmch_pfit.lvds_border_bits;
+       temp |= pipe_config->gmch_pfit.lvds_border_bits;
        /* Set the B0-B3 data pairs corresponding to whether we're going to
         * set the DPLLs for dual-channel mode or not.
         */
@@ -193,7 +290,7 @@ static void intel_pre_enable_lvds(struct intel_encoder *encoder)
        if (IS_GEN4(dev_priv)) {
                /* Bspec wording suggests that LVDS port dithering only exists
                 * for 18bpp panels. */
-               if (crtc->config->dither && crtc->config->pipe_bpp == 18)
+               if (pipe_config->dither && pipe_config->pipe_bpp == 18)
                        temp |= LVDS_ENABLE_DITHER;
                else
                        temp &= ~LVDS_ENABLE_DITHER;
@@ -210,57 +307,45 @@ static void intel_pre_enable_lvds(struct intel_encoder *encoder)
 /**
  * Sets the power state for the panel.
  */
-static void intel_enable_lvds(struct intel_encoder *encoder)
+static void intel_enable_lvds(struct intel_encoder *encoder,
+                             struct intel_crtc_state *pipe_config,
+                             struct drm_connector_state *conn_state)
 {
        struct drm_device *dev = encoder->base.dev;
        struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
        struct intel_connector *intel_connector =
                &lvds_encoder->attached_connector->base;
        struct drm_i915_private *dev_priv = to_i915(dev);
-       i915_reg_t ctl_reg, stat_reg;
-
-       if (HAS_PCH_SPLIT(dev)) {
-               ctl_reg = PCH_PP_CONTROL;
-               stat_reg = PCH_PP_STATUS;
-       } else {
-               ctl_reg = PP_CONTROL;
-               stat_reg = PP_STATUS;
-       }
 
        I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) | LVDS_PORT_EN);
 
-       I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON);
+       I915_WRITE(PP_CONTROL(0), I915_READ(PP_CONTROL(0)) | PANEL_POWER_ON);
        POSTING_READ(lvds_encoder->reg);
-       if (intel_wait_for_register(dev_priv, stat_reg, PP_ON, PP_ON, 1000))
+       if (intel_wait_for_register(dev_priv, PP_STATUS(0), PP_ON, PP_ON, 1000))
                DRM_ERROR("timed out waiting for panel to power on\n");
 
        intel_panel_enable_backlight(intel_connector);
 }
 
-static void intel_disable_lvds(struct intel_encoder *encoder)
+static void intel_disable_lvds(struct intel_encoder *encoder,
+                              struct intel_crtc_state *old_crtc_state,
+                              struct drm_connector_state *old_conn_state)
 {
-       struct drm_device *dev = encoder->base.dev;
        struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       i915_reg_t ctl_reg, stat_reg;
-
-       if (HAS_PCH_SPLIT(dev)) {
-               ctl_reg = PCH_PP_CONTROL;
-               stat_reg = PCH_PP_STATUS;
-       } else {
-               ctl_reg = PP_CONTROL;
-               stat_reg = PP_STATUS;
-       }
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
 
-       I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON);
-       if (intel_wait_for_register(dev_priv, stat_reg, PP_ON, 0, 1000))
+       I915_WRITE(PP_CONTROL(0), I915_READ(PP_CONTROL(0)) & ~PANEL_POWER_ON);
+       if (intel_wait_for_register(dev_priv, PP_STATUS(0), PP_ON, 0, 1000))
                DRM_ERROR("timed out waiting for panel to power off\n");
 
        I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) & ~LVDS_PORT_EN);
        POSTING_READ(lvds_encoder->reg);
 }
 
-static void gmch_disable_lvds(struct intel_encoder *encoder)
+static void gmch_disable_lvds(struct intel_encoder *encoder,
+                             struct intel_crtc_state *old_crtc_state,
+                             struct drm_connector_state *old_conn_state)
+
 {
        struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
        struct intel_connector *intel_connector =
@@ -268,10 +353,12 @@ static void gmch_disable_lvds(struct intel_encoder *encoder)
 
        intel_panel_disable_backlight(intel_connector);
 
-       intel_disable_lvds(encoder);
+       intel_disable_lvds(encoder, old_crtc_state, old_conn_state);
 }
 
-static void pch_disable_lvds(struct intel_encoder *encoder)
+static void pch_disable_lvds(struct intel_encoder *encoder,
+                            struct intel_crtc_state *old_crtc_state,
+                            struct drm_connector_state *old_conn_state)
 {
        struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
        struct intel_connector *intel_connector =
@@ -280,9 +367,11 @@ static void pch_disable_lvds(struct intel_encoder *encoder)
        intel_panel_disable_backlight(intel_connector);
 }
 
-static void pch_post_disable_lvds(struct intel_encoder *encoder)
+static void pch_post_disable_lvds(struct intel_encoder *encoder,
+                                 struct intel_crtc_state *old_crtc_state,
+                                 struct drm_connector_state *old_conn_state)
 {
-       intel_disable_lvds(encoder);
+       intel_disable_lvds(encoder, old_crtc_state, old_conn_state);
 }
 
 static enum drm_mode_status
@@ -304,7 +393,8 @@ intel_lvds_mode_valid(struct drm_connector *connector,
 }
 
 static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
-                                     struct intel_crtc_state *pipe_config)
+                                     struct intel_crtc_state *pipe_config,
+                                     struct drm_connector_state *conn_state)
 {
        struct drm_device *dev = intel_encoder->base.dev;
        struct intel_lvds_encoder *lvds_encoder =
@@ -900,17 +990,6 @@ void intel_lvds_init(struct drm_device *dev)
        int pipe;
        u8 pin;
 
-       /*
-        * Unlock registers and just leave them unlocked. Do this before
-        * checking quirk lists to avoid bogus WARNINGs.
-        */
-       if (HAS_PCH_SPLIT(dev)) {
-               I915_WRITE(PCH_PP_CONTROL,
-                          I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS);
-       } else if (INTEL_INFO(dev_priv)->gen < 5) {
-               I915_WRITE(PP_CONTROL,
-                          I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS);
-       }
        if (!intel_lvds_supported(dev))
                return;
 
@@ -943,18 +1022,6 @@ void intel_lvds_init(struct drm_device *dev)
                DRM_DEBUG_KMS("LVDS is not present in VBT, but enabled anyway\n");
        }
 
-        /* Set the Panel Power On/Off timings if uninitialized. */
-       if (INTEL_INFO(dev_priv)->gen < 5 &&
-           I915_READ(PP_ON_DELAYS) == 0 && I915_READ(PP_OFF_DELAYS) == 0) {
-               /* Set T2 to 40ms and T5 to 200ms */
-               I915_WRITE(PP_ON_DELAYS, 0x019007d0);
-
-               /* Set T3 to 35ms and Tx to 200ms */
-               I915_WRITE(PP_OFF_DELAYS, 0x015e07d0);
-
-               DRM_DEBUG_KMS("Panel power timings uninitialized, setting defaults\n");
-       }
-
        lvds_encoder = kzalloc(sizeof(*lvds_encoder), GFP_KERNEL);
        if (!lvds_encoder)
                return;
@@ -1020,6 +1087,10 @@ void intel_lvds_init(struct drm_device *dev)
                                      dev->mode_config.scaling_mode_property,
                                      DRM_MODE_SCALE_ASPECT);
        intel_connector->panel.fitting_mode = DRM_MODE_SCALE_ASPECT;
+
+       intel_lvds_pps_get_hw_state(dev_priv, &lvds_encoder->init_pps);
+       lvds_encoder->init_lvds_val = lvds;
+
        /*
         * LVDS discovery:
         * 1) check for EDID on DDC
@@ -1054,17 +1125,6 @@ void intel_lvds_init(struct drm_device *dev)
        }
        lvds_connector->base.edid = edid;
 
-       if (IS_ERR_OR_NULL(edid)) {
-               /* Didn't get an EDID, so
-                * Set wide sync ranges so we get all modes
-                * handed to valid_mode for checking
-                */
-               connector->display_info.min_vfreq = 0;
-               connector->display_info.max_vfreq = 200;
-               connector->display_info.min_hfreq = 0;
-               connector->display_info.max_hfreq = 200;
-       }
-
        list_for_each_entry(scan, &connector->probed_modes, head) {
                if (scan->type & DRM_MODE_TYPE_PREFERRED) {
                        DRM_DEBUG_KMS("using preferred mode from EDID: ");