summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/i915/intel_lvds.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/i915/intel_lvds.c')
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c214
1 files changed, 137 insertions, 77 deletions
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index 49550470483e..e1d47d51ea47 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -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: ");