diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_hdmi.c')
-rw-r--r-- | drivers/gpu/drm/i915/intel_hdmi.c | 192 |
1 files changed, 121 insertions, 71 deletions
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index bfbe07b6ddce..e97731aab6dc 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -223,10 +223,14 @@ static bool ibx_infoframe_enabled(struct drm_encoder *encoder) struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); + struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); int reg = TVIDEO_DIP_CTL(intel_crtc->pipe); u32 val = I915_READ(reg); - return val & VIDEO_DIP_ENABLE; + if (VIDEO_DIP_PORT(intel_dig_port->port) == (val & VIDEO_DIP_PORT_MASK)) + return val & VIDEO_DIP_ENABLE; + + return false; } static void cpt_write_infoframe(struct drm_encoder *encoder, @@ -324,10 +328,14 @@ static bool vlv_infoframe_enabled(struct drm_encoder *encoder) struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); + struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); int reg = VLV_TVIDEO_DIP_CTL(intel_crtc->pipe); u32 val = I915_READ(reg); - return val & VIDEO_DIP_ENABLE; + if (VIDEO_DIP_PORT(intel_dig_port->port) == (val & VIDEO_DIP_PORT_MASK)) + return val & VIDEO_DIP_ENABLE; + + return false; } static void hsw_write_infoframe(struct drm_encoder *encoder, @@ -865,59 +873,59 @@ static void intel_disable_hdmi(struct intel_encoder *encoder) struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); u32 temp; - u32 enable_bits = SDVO_ENABLE | SDVO_AUDIO_ENABLE; - - if (crtc->config->has_audio) - intel_audio_codec_disable(encoder); temp = I915_READ(intel_hdmi->hdmi_reg); - /* HW workaround for IBX, we need to move the port to transcoder A - * before disabling it. */ - if (HAS_PCH_IBX(dev)) { - struct drm_crtc *crtc = encoder->base.crtc; - int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1; - - if (temp & SDVO_PIPE_B_SELECT) { - temp &= ~SDVO_PIPE_B_SELECT; - I915_WRITE(intel_hdmi->hdmi_reg, temp); - POSTING_READ(intel_hdmi->hdmi_reg); - - /* Again we need to write this twice. */ - I915_WRITE(intel_hdmi->hdmi_reg, temp); - POSTING_READ(intel_hdmi->hdmi_reg); - - /* Transcoder selection bits only update - * effectively on vblank. */ - if (crtc) - intel_wait_for_vblank(dev, pipe); - else - msleep(50); - } - } - - /* HW workaround, need to toggle enable bit off and on for 12bpc, but - * we do this anyway which shows more stable in testing. - */ - if (HAS_PCH_SPLIT(dev)) { - I915_WRITE(intel_hdmi->hdmi_reg, temp & ~SDVO_ENABLE); - POSTING_READ(intel_hdmi->hdmi_reg); - } - - temp &= ~enable_bits; - + temp &= ~(SDVO_ENABLE | SDVO_AUDIO_ENABLE); I915_WRITE(intel_hdmi->hdmi_reg, temp); POSTING_READ(intel_hdmi->hdmi_reg); - /* HW workaround, need to write this twice for issue that may result - * in first write getting masked. + /* + * HW workaround for IBX, we need to move the port + * to transcoder A after disabling it to allow the + * matching DP port to be enabled on transcoder A. */ - if (HAS_PCH_SPLIT(dev)) { + if (HAS_PCH_IBX(dev) && crtc->pipe == PIPE_B) { + temp &= ~SDVO_PIPE_B_SELECT; + temp |= SDVO_ENABLE; + /* + * HW workaround, need to write this twice for issue + * that may result in first write getting masked. + */ + I915_WRITE(intel_hdmi->hdmi_reg, temp); + POSTING_READ(intel_hdmi->hdmi_reg); + I915_WRITE(intel_hdmi->hdmi_reg, temp); + POSTING_READ(intel_hdmi->hdmi_reg); + + temp &= ~SDVO_ENABLE; I915_WRITE(intel_hdmi->hdmi_reg, temp); POSTING_READ(intel_hdmi->hdmi_reg); } } +static void g4x_disable_hdmi(struct intel_encoder *encoder) +{ + struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); + + if (crtc->config->has_audio) + intel_audio_codec_disable(encoder); + + intel_disable_hdmi(encoder); +} + +static void pch_disable_hdmi(struct intel_encoder *encoder) +{ + struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); + + if (crtc->config->has_audio) + intel_audio_codec_disable(encoder); +} + +static void pch_post_disable_hdmi(struct intel_encoder *encoder) +{ + intel_disable_hdmi(encoder); +} + static int hdmi_portclock_limit(struct intel_hdmi *hdmi, bool respect_dvi_limit) { struct drm_device *dev = intel_hdmi_to_dev(hdmi); @@ -956,6 +964,7 @@ static bool hdmi_12bpc_possible(struct intel_crtc_state *crtc_state) struct drm_device *dev = crtc_state->base.crtc->dev; struct drm_atomic_state *state; struct intel_encoder *encoder; + struct drm_connector *connector; struct drm_connector_state *connector_state; int count = 0, count_hdmi = 0; int i; @@ -965,11 +974,7 @@ static bool hdmi_12bpc_possible(struct intel_crtc_state *crtc_state) state = crtc_state->base.state; - for (i = 0; i < state->num_connector; i++) { - if (!state->connectors[i]) - continue; - - connector_state = state->connector_states[i]; + for_each_connector_in_state(state, connector, connector_state, i) { if (connector_state->crtc != crtc_state->base.crtc) continue; @@ -1031,7 +1036,8 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, */ if (pipe_config->pipe_bpp > 8*3 && pipe_config->has_hdmi_sink && clock_12bpc <= portclock_limit && - hdmi_12bpc_possible(pipe_config)) { + hdmi_12bpc_possible(pipe_config) && + 0 /* FIXME 12bpc support totally broken */) { DRM_DEBUG_KMS("picking bpc to 12 for HDMI output\n"); desired_bpp = 12*3; @@ -1288,7 +1294,7 @@ static void vlv_hdmi_pre_enable(struct intel_encoder *encoder) u32 val; /* Enable clock channels for this port */ - mutex_lock(&dev_priv->dpio_lock); + mutex_lock(&dev_priv->sb_lock); val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW8(port)); val = 0; if (pipe) @@ -1311,7 +1317,7 @@ static void vlv_hdmi_pre_enable(struct intel_encoder *encoder) /* Program lane clock */ vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW14(port), 0x00760018); vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW23(port), 0x00400888); - mutex_unlock(&dev_priv->dpio_lock); + mutex_unlock(&dev_priv->sb_lock); intel_hdmi->set_infoframes(&encoder->base, intel_crtc->config->has_hdmi_sink, @@ -1319,7 +1325,7 @@ static void vlv_hdmi_pre_enable(struct intel_encoder *encoder) intel_enable_hdmi(encoder); - vlv_wait_port_ready(dev_priv, dport); + vlv_wait_port_ready(dev_priv, dport, 0x0); } static void vlv_hdmi_pre_pll_enable(struct intel_encoder *encoder) @@ -1335,7 +1341,7 @@ static void vlv_hdmi_pre_pll_enable(struct intel_encoder *encoder) intel_hdmi_prepare(encoder); /* Program Tx lane resets to default */ - mutex_lock(&dev_priv->dpio_lock); + mutex_lock(&dev_priv->sb_lock); vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW0(port), DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); @@ -1352,7 +1358,7 @@ static void vlv_hdmi_pre_pll_enable(struct intel_encoder *encoder) vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW9(port), 0x00002000); vlv_dpio_write(dev_priv, pipe, VLV_TX_DW5(port), DPIO_TX_OCALINIT_EN); - mutex_unlock(&dev_priv->dpio_lock); + mutex_unlock(&dev_priv->sb_lock); } static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder) @@ -1368,7 +1374,7 @@ static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder) intel_hdmi_prepare(encoder); - mutex_lock(&dev_priv->dpio_lock); + mutex_lock(&dev_priv->sb_lock); /* program left/right clock distribution */ if (pipe != PIPE_B) { @@ -1418,7 +1424,7 @@ static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder) val |= CHV_CMN_USEDCLKCHANNEL; vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW19(ch), val); - mutex_unlock(&dev_priv->dpio_lock); + mutex_unlock(&dev_priv->sb_lock); } static void vlv_hdmi_post_disable(struct intel_encoder *encoder) @@ -1431,10 +1437,10 @@ static void vlv_hdmi_post_disable(struct intel_encoder *encoder) int pipe = intel_crtc->pipe; /* Reset lanes to avoid HDMI flicker (VLV w/a) */ - mutex_lock(&dev_priv->dpio_lock); + mutex_lock(&dev_priv->sb_lock); vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW0(port), 0x00000000); vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW1(port), 0x00e00060); - mutex_unlock(&dev_priv->dpio_lock); + mutex_unlock(&dev_priv->sb_lock); } static void chv_hdmi_post_disable(struct intel_encoder *encoder) @@ -1448,7 +1454,7 @@ static void chv_hdmi_post_disable(struct intel_encoder *encoder) enum pipe pipe = intel_crtc->pipe; u32 val; - mutex_lock(&dev_priv->dpio_lock); + mutex_lock(&dev_priv->sb_lock); /* Propagate soft reset to data lane reset */ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch)); @@ -1467,7 +1473,7 @@ static void chv_hdmi_post_disable(struct intel_encoder *encoder) val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); - mutex_unlock(&dev_priv->dpio_lock); + mutex_unlock(&dev_priv->sb_lock); } static void chv_hdmi_pre_enable(struct intel_encoder *encoder) @@ -1482,10 +1488,10 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder) &intel_crtc->config->base.adjusted_mode; enum dpio_channel ch = vlv_dport_to_channel(dport); int pipe = intel_crtc->pipe; - int data, i; + int data, i, stagger; u32 val; - mutex_lock(&dev_priv->dpio_lock); + mutex_lock(&dev_priv->sb_lock); /* allow hardware to manage TX FIFO reset source */ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW11(ch)); @@ -1522,7 +1528,38 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder) } /* Data lane stagger programming */ - /* FIXME: Fix up value only after power analysis */ + if (intel_crtc->config->port_clock > 270000) + stagger = 0x18; + else if (intel_crtc->config->port_clock > 135000) + stagger = 0xd; + else if (intel_crtc->config->port_clock > 67500) + stagger = 0x7; + else if (intel_crtc->config->port_clock > 33750) + stagger = 0x4; + else + stagger = 0x2; + + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW11(ch)); + val |= DPIO_TX2_STAGGER_MASK(0x1f); + vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW11(ch), val); + + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW11(ch)); + val |= DPIO_TX2_STAGGER_MASK(0x1f); + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW11(ch), val); + + vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW12(ch), + DPIO_LANESTAGGER_STRAP(stagger) | + DPIO_LANESTAGGER_STRAP_OVRD | + DPIO_TX1_STAGGER_MASK(0x1f) | + DPIO_TX1_STAGGER_MULT(6) | + DPIO_TX2_STAGGER_MULT(0)); + + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW12(ch), + DPIO_LANESTAGGER_STRAP(stagger) | + DPIO_LANESTAGGER_STRAP_OVRD | + DPIO_TX1_STAGGER_MASK(0x1f) | + DPIO_TX1_STAGGER_MULT(7) | + DPIO_TX2_STAGGER_MULT(5)); /* Clear calc init */ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW10(ch)); @@ -1597,7 +1634,7 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder) val |= DPIO_LRC_BYPASS; vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW30, val); - mutex_unlock(&dev_priv->dpio_lock); + mutex_unlock(&dev_priv->sb_lock); intel_hdmi->set_infoframes(&encoder->base, intel_crtc->config->has_hdmi_sink, @@ -1605,7 +1642,7 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder) intel_enable_hdmi(encoder); - vlv_wait_port_ready(dev_priv, dport); + vlv_wait_port_ready(dev_priv, dport, 0x0); } static void intel_hdmi_destroy(struct drm_connector *connector) @@ -1676,18 +1713,26 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, switch (port) { case PORT_B: - intel_hdmi->ddc_bus = GMBUS_PORT_DPB; + if (IS_BROXTON(dev_priv)) + intel_hdmi->ddc_bus = GMBUS_PIN_1_BXT; + else + intel_hdmi->ddc_bus = GMBUS_PIN_DPB; intel_encoder->hpd_pin = HPD_PORT_B; break; case PORT_C: - intel_hdmi->ddc_bus = GMBUS_PORT_DPC; + if (IS_BROXTON(dev_priv)) + intel_hdmi->ddc_bus = GMBUS_PIN_2_BXT; + else + intel_hdmi->ddc_bus = GMBUS_PIN_DPC; intel_encoder->hpd_pin = HPD_PORT_C; break; case PORT_D: - if (IS_CHERRYVIEW(dev)) - intel_hdmi->ddc_bus = GMBUS_PORT_DPD_CHV; + if (WARN_ON(IS_BROXTON(dev_priv))) + intel_hdmi->ddc_bus = GMBUS_PIN_DISABLED; + else if (IS_CHERRYVIEW(dev_priv)) + intel_hdmi->ddc_bus = GMBUS_PIN_DPD_CHV; else - intel_hdmi->ddc_bus = GMBUS_PORT_DPD; + intel_hdmi->ddc_bus = GMBUS_PIN_DPD; intel_encoder->hpd_pin = HPD_PORT_D; break; case PORT_A: @@ -1762,7 +1807,12 @@ void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port) DRM_MODE_ENCODER_TMDS); intel_encoder->compute_config = intel_hdmi_compute_config; - intel_encoder->disable = intel_disable_hdmi; + if (HAS_PCH_SPLIT(dev)) { + intel_encoder->disable = pch_disable_hdmi; + intel_encoder->post_disable = pch_post_disable_hdmi; + } else { + intel_encoder->disable = g4x_disable_hdmi; + } intel_encoder->get_hw_state = intel_hdmi_get_hw_state; intel_encoder->get_config = intel_hdmi_get_config; if (IS_CHERRYVIEW(dev)) { |