15ca02815Sjsg // SPDX-License-Identifier: MIT
25ca02815Sjsg /*
35ca02815Sjsg * Copyright © 2020 Intel Corporation
45ca02815Sjsg */
51bb76ff1Sjsg
61bb76ff1Sjsg #include <linux/string_helpers.h>
71bb76ff1Sjsg
8*f005ef32Sjsg #include "i915_reg.h"
95ca02815Sjsg #include "intel_atomic.h"
101bb76ff1Sjsg #include "intel_crtc.h"
115ca02815Sjsg #include "intel_ddi.h"
125ca02815Sjsg #include "intel_de.h"
135ca02815Sjsg #include "intel_display_types.h"
145ca02815Sjsg #include "intel_fdi.h"
15*f005ef32Sjsg #include "intel_fdi_regs.h"
165ca02815Sjsg
171bb76ff1Sjsg struct intel_fdi_funcs {
181bb76ff1Sjsg void (*fdi_link_train)(struct intel_crtc *crtc,
191bb76ff1Sjsg const struct intel_crtc_state *crtc_state);
201bb76ff1Sjsg };
211bb76ff1Sjsg
assert_fdi_tx(struct drm_i915_private * dev_priv,enum pipe pipe,bool state)221bb76ff1Sjsg static void assert_fdi_tx(struct drm_i915_private *dev_priv,
231bb76ff1Sjsg enum pipe pipe, bool state)
241bb76ff1Sjsg {
251bb76ff1Sjsg bool cur_state;
261bb76ff1Sjsg
271bb76ff1Sjsg if (HAS_DDI(dev_priv)) {
281bb76ff1Sjsg /*
291bb76ff1Sjsg * DDI does not have a specific FDI_TX register.
301bb76ff1Sjsg *
311bb76ff1Sjsg * FDI is never fed from EDP transcoder
321bb76ff1Sjsg * so pipe->transcoder cast is fine here.
331bb76ff1Sjsg */
341bb76ff1Sjsg enum transcoder cpu_transcoder = (enum transcoder)pipe;
351bb76ff1Sjsg cur_state = intel_de_read(dev_priv, TRANS_DDI_FUNC_CTL(cpu_transcoder)) & TRANS_DDI_FUNC_ENABLE;
361bb76ff1Sjsg } else {
371bb76ff1Sjsg cur_state = intel_de_read(dev_priv, FDI_TX_CTL(pipe)) & FDI_TX_ENABLE;
381bb76ff1Sjsg }
39*f005ef32Sjsg I915_STATE_WARN(dev_priv, cur_state != state,
401bb76ff1Sjsg "FDI TX state assertion failure (expected %s, current %s)\n",
411bb76ff1Sjsg str_on_off(state), str_on_off(cur_state));
421bb76ff1Sjsg }
431bb76ff1Sjsg
assert_fdi_tx_enabled(struct drm_i915_private * i915,enum pipe pipe)441bb76ff1Sjsg void assert_fdi_tx_enabled(struct drm_i915_private *i915, enum pipe pipe)
451bb76ff1Sjsg {
461bb76ff1Sjsg assert_fdi_tx(i915, pipe, true);
471bb76ff1Sjsg }
481bb76ff1Sjsg
assert_fdi_tx_disabled(struct drm_i915_private * i915,enum pipe pipe)491bb76ff1Sjsg void assert_fdi_tx_disabled(struct drm_i915_private *i915, enum pipe pipe)
501bb76ff1Sjsg {
511bb76ff1Sjsg assert_fdi_tx(i915, pipe, false);
521bb76ff1Sjsg }
531bb76ff1Sjsg
assert_fdi_rx(struct drm_i915_private * dev_priv,enum pipe pipe,bool state)541bb76ff1Sjsg static void assert_fdi_rx(struct drm_i915_private *dev_priv,
551bb76ff1Sjsg enum pipe pipe, bool state)
561bb76ff1Sjsg {
571bb76ff1Sjsg bool cur_state;
581bb76ff1Sjsg
591bb76ff1Sjsg cur_state = intel_de_read(dev_priv, FDI_RX_CTL(pipe)) & FDI_RX_ENABLE;
60*f005ef32Sjsg I915_STATE_WARN(dev_priv, cur_state != state,
611bb76ff1Sjsg "FDI RX state assertion failure (expected %s, current %s)\n",
621bb76ff1Sjsg str_on_off(state), str_on_off(cur_state));
631bb76ff1Sjsg }
641bb76ff1Sjsg
assert_fdi_rx_enabled(struct drm_i915_private * i915,enum pipe pipe)651bb76ff1Sjsg void assert_fdi_rx_enabled(struct drm_i915_private *i915, enum pipe pipe)
661bb76ff1Sjsg {
671bb76ff1Sjsg assert_fdi_rx(i915, pipe, true);
681bb76ff1Sjsg }
691bb76ff1Sjsg
assert_fdi_rx_disabled(struct drm_i915_private * i915,enum pipe pipe)701bb76ff1Sjsg void assert_fdi_rx_disabled(struct drm_i915_private *i915, enum pipe pipe)
711bb76ff1Sjsg {
721bb76ff1Sjsg assert_fdi_rx(i915, pipe, false);
731bb76ff1Sjsg }
741bb76ff1Sjsg
assert_fdi_tx_pll_enabled(struct drm_i915_private * i915,enum pipe pipe)751bb76ff1Sjsg void assert_fdi_tx_pll_enabled(struct drm_i915_private *i915,
761bb76ff1Sjsg enum pipe pipe)
771bb76ff1Sjsg {
781bb76ff1Sjsg bool cur_state;
791bb76ff1Sjsg
801bb76ff1Sjsg /* ILK FDI PLL is always enabled */
811bb76ff1Sjsg if (IS_IRONLAKE(i915))
821bb76ff1Sjsg return;
831bb76ff1Sjsg
841bb76ff1Sjsg /* On Haswell, DDI ports are responsible for the FDI PLL setup */
851bb76ff1Sjsg if (HAS_DDI(i915))
861bb76ff1Sjsg return;
871bb76ff1Sjsg
881bb76ff1Sjsg cur_state = intel_de_read(i915, FDI_TX_CTL(pipe)) & FDI_TX_PLL_ENABLE;
89*f005ef32Sjsg I915_STATE_WARN(i915, !cur_state,
90*f005ef32Sjsg "FDI TX PLL assertion failure, should be active but is disabled\n");
911bb76ff1Sjsg }
921bb76ff1Sjsg
assert_fdi_rx_pll(struct drm_i915_private * i915,enum pipe pipe,bool state)931bb76ff1Sjsg static void assert_fdi_rx_pll(struct drm_i915_private *i915,
941bb76ff1Sjsg enum pipe pipe, bool state)
951bb76ff1Sjsg {
961bb76ff1Sjsg bool cur_state;
971bb76ff1Sjsg
981bb76ff1Sjsg cur_state = intel_de_read(i915, FDI_RX_CTL(pipe)) & FDI_RX_PLL_ENABLE;
99*f005ef32Sjsg I915_STATE_WARN(i915, cur_state != state,
1001bb76ff1Sjsg "FDI RX PLL assertion failure (expected %s, current %s)\n",
1011bb76ff1Sjsg str_on_off(state), str_on_off(cur_state));
1021bb76ff1Sjsg }
1031bb76ff1Sjsg
assert_fdi_rx_pll_enabled(struct drm_i915_private * i915,enum pipe pipe)1041bb76ff1Sjsg void assert_fdi_rx_pll_enabled(struct drm_i915_private *i915, enum pipe pipe)
1051bb76ff1Sjsg {
1061bb76ff1Sjsg assert_fdi_rx_pll(i915, pipe, true);
1071bb76ff1Sjsg }
1081bb76ff1Sjsg
assert_fdi_rx_pll_disabled(struct drm_i915_private * i915,enum pipe pipe)1091bb76ff1Sjsg void assert_fdi_rx_pll_disabled(struct drm_i915_private *i915, enum pipe pipe)
1101bb76ff1Sjsg {
1111bb76ff1Sjsg assert_fdi_rx_pll(i915, pipe, false);
1121bb76ff1Sjsg }
1131bb76ff1Sjsg
intel_fdi_link_train(struct intel_crtc * crtc,const struct intel_crtc_state * crtc_state)1141bb76ff1Sjsg void intel_fdi_link_train(struct intel_crtc *crtc,
1151bb76ff1Sjsg const struct intel_crtc_state *crtc_state)
1161bb76ff1Sjsg {
1171bb76ff1Sjsg struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
1181bb76ff1Sjsg
1191bb76ff1Sjsg dev_priv->display.funcs.fdi->fdi_link_train(crtc, crtc_state);
1201bb76ff1Sjsg }
1211bb76ff1Sjsg
1225ca02815Sjsg /* units of 100MHz */
pipe_required_fdi_lanes(struct intel_crtc_state * crtc_state)1235ca02815Sjsg static int pipe_required_fdi_lanes(struct intel_crtc_state *crtc_state)
1245ca02815Sjsg {
1255ca02815Sjsg if (crtc_state->hw.enable && crtc_state->has_pch_encoder)
1265ca02815Sjsg return crtc_state->fdi_lanes;
1275ca02815Sjsg
1285ca02815Sjsg return 0;
1295ca02815Sjsg }
1305ca02815Sjsg
ilk_check_fdi_lanes(struct drm_device * dev,enum pipe pipe,struct intel_crtc_state * pipe_config)1315ca02815Sjsg static int ilk_check_fdi_lanes(struct drm_device *dev, enum pipe pipe,
1325ca02815Sjsg struct intel_crtc_state *pipe_config)
1335ca02815Sjsg {
1345ca02815Sjsg struct drm_i915_private *dev_priv = to_i915(dev);
1355ca02815Sjsg struct drm_atomic_state *state = pipe_config->uapi.state;
1365ca02815Sjsg struct intel_crtc *other_crtc;
1375ca02815Sjsg struct intel_crtc_state *other_crtc_state;
1385ca02815Sjsg
1395ca02815Sjsg drm_dbg_kms(&dev_priv->drm,
1405ca02815Sjsg "checking fdi config on pipe %c, lanes %i\n",
1415ca02815Sjsg pipe_name(pipe), pipe_config->fdi_lanes);
1425ca02815Sjsg if (pipe_config->fdi_lanes > 4) {
1435ca02815Sjsg drm_dbg_kms(&dev_priv->drm,
1445ca02815Sjsg "invalid fdi lane config on pipe %c: %i lanes\n",
1455ca02815Sjsg pipe_name(pipe), pipe_config->fdi_lanes);
1465ca02815Sjsg return -EINVAL;
1475ca02815Sjsg }
1485ca02815Sjsg
1495ca02815Sjsg if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) {
1505ca02815Sjsg if (pipe_config->fdi_lanes > 2) {
1515ca02815Sjsg drm_dbg_kms(&dev_priv->drm,
1525ca02815Sjsg "only 2 lanes on haswell, required: %i lanes\n",
1535ca02815Sjsg pipe_config->fdi_lanes);
1545ca02815Sjsg return -EINVAL;
1555ca02815Sjsg } else {
1565ca02815Sjsg return 0;
1575ca02815Sjsg }
1585ca02815Sjsg }
1595ca02815Sjsg
1605ca02815Sjsg if (INTEL_NUM_PIPES(dev_priv) == 2)
1615ca02815Sjsg return 0;
1625ca02815Sjsg
1635ca02815Sjsg /* Ivybridge 3 pipe is really complicated */
1645ca02815Sjsg switch (pipe) {
1655ca02815Sjsg case PIPE_A:
1665ca02815Sjsg return 0;
1675ca02815Sjsg case PIPE_B:
1685ca02815Sjsg if (pipe_config->fdi_lanes <= 2)
1695ca02815Sjsg return 0;
1705ca02815Sjsg
1711bb76ff1Sjsg other_crtc = intel_crtc_for_pipe(dev_priv, PIPE_C);
1725ca02815Sjsg other_crtc_state =
1735ca02815Sjsg intel_atomic_get_crtc_state(state, other_crtc);
1745ca02815Sjsg if (IS_ERR(other_crtc_state))
1755ca02815Sjsg return PTR_ERR(other_crtc_state);
1765ca02815Sjsg
1775ca02815Sjsg if (pipe_required_fdi_lanes(other_crtc_state) > 0) {
1785ca02815Sjsg drm_dbg_kms(&dev_priv->drm,
1795ca02815Sjsg "invalid shared fdi lane config on pipe %c: %i lanes\n",
1805ca02815Sjsg pipe_name(pipe), pipe_config->fdi_lanes);
1815ca02815Sjsg return -EINVAL;
1825ca02815Sjsg }
1835ca02815Sjsg return 0;
1845ca02815Sjsg case PIPE_C:
1855ca02815Sjsg if (pipe_config->fdi_lanes > 2) {
1865ca02815Sjsg drm_dbg_kms(&dev_priv->drm,
1875ca02815Sjsg "only 2 lanes on pipe %c: required %i lanes\n",
1885ca02815Sjsg pipe_name(pipe), pipe_config->fdi_lanes);
1895ca02815Sjsg return -EINVAL;
1905ca02815Sjsg }
1915ca02815Sjsg
1921bb76ff1Sjsg other_crtc = intel_crtc_for_pipe(dev_priv, PIPE_B);
1935ca02815Sjsg other_crtc_state =
1945ca02815Sjsg intel_atomic_get_crtc_state(state, other_crtc);
1955ca02815Sjsg if (IS_ERR(other_crtc_state))
1965ca02815Sjsg return PTR_ERR(other_crtc_state);
1975ca02815Sjsg
1985ca02815Sjsg if (pipe_required_fdi_lanes(other_crtc_state) > 2) {
1995ca02815Sjsg drm_dbg_kms(&dev_priv->drm,
2005ca02815Sjsg "fdi link B uses too many lanes to enable link C\n");
2015ca02815Sjsg return -EINVAL;
2025ca02815Sjsg }
2035ca02815Sjsg return 0;
2045ca02815Sjsg default:
2051bb76ff1Sjsg MISSING_CASE(pipe);
2061bb76ff1Sjsg return 0;
2075ca02815Sjsg }
2085ca02815Sjsg }
2095ca02815Sjsg
intel_fdi_pll_freq_update(struct drm_i915_private * i915)2101bb76ff1Sjsg void intel_fdi_pll_freq_update(struct drm_i915_private *i915)
2111bb76ff1Sjsg {
2121bb76ff1Sjsg if (IS_IRONLAKE(i915)) {
2131bb76ff1Sjsg u32 fdi_pll_clk =
2141bb76ff1Sjsg intel_de_read(i915, FDI_PLL_BIOS_0) & FDI_PLL_FB_CLOCK_MASK;
2151bb76ff1Sjsg
2161bb76ff1Sjsg i915->display.fdi.pll_freq = (fdi_pll_clk + 2) * 10000;
2171bb76ff1Sjsg } else if (IS_SANDYBRIDGE(i915) || IS_IVYBRIDGE(i915)) {
2181bb76ff1Sjsg i915->display.fdi.pll_freq = 270000;
2191bb76ff1Sjsg } else {
2201bb76ff1Sjsg return;
2211bb76ff1Sjsg }
2221bb76ff1Sjsg
2231bb76ff1Sjsg drm_dbg(&i915->drm, "FDI PLL freq=%d\n", i915->display.fdi.pll_freq);
2241bb76ff1Sjsg }
2251bb76ff1Sjsg
intel_fdi_link_freq(struct drm_i915_private * i915,const struct intel_crtc_state * pipe_config)2261bb76ff1Sjsg int intel_fdi_link_freq(struct drm_i915_private *i915,
2271bb76ff1Sjsg const struct intel_crtc_state *pipe_config)
2281bb76ff1Sjsg {
2291bb76ff1Sjsg if (HAS_DDI(i915))
2301bb76ff1Sjsg return pipe_config->port_clock; /* SPLL */
2311bb76ff1Sjsg else
2321bb76ff1Sjsg return i915->display.fdi.pll_freq;
2331bb76ff1Sjsg }
2341bb76ff1Sjsg
ilk_fdi_compute_config(struct intel_crtc * crtc,struct intel_crtc_state * pipe_config)2355ca02815Sjsg int ilk_fdi_compute_config(struct intel_crtc *crtc,
2365ca02815Sjsg struct intel_crtc_state *pipe_config)
2375ca02815Sjsg {
2385ca02815Sjsg struct drm_device *dev = crtc->base.dev;
2395ca02815Sjsg struct drm_i915_private *i915 = to_i915(dev);
2405ca02815Sjsg const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode;
2415ca02815Sjsg int lane, link_bw, fdi_dotclock, ret;
2425ca02815Sjsg bool needs_recompute = false;
2435ca02815Sjsg
2445ca02815Sjsg retry:
2455ca02815Sjsg /* FDI is a binary signal running at ~2.7GHz, encoding
2465ca02815Sjsg * each output octet as 10 bits. The actual frequency
2475ca02815Sjsg * is stored as a divider into a 100MHz clock, and the
2485ca02815Sjsg * mode pixel clock is stored in units of 1KHz.
2495ca02815Sjsg * Hence the bw of each lane in terms of the mode signal
2505ca02815Sjsg * is:
2515ca02815Sjsg */
2525ca02815Sjsg link_bw = intel_fdi_link_freq(i915, pipe_config);
2535ca02815Sjsg
2545ca02815Sjsg fdi_dotclock = adjusted_mode->crtc_clock;
2555ca02815Sjsg
2565ca02815Sjsg lane = ilk_get_lanes_required(fdi_dotclock, link_bw,
2575ca02815Sjsg pipe_config->pipe_bpp);
2585ca02815Sjsg
2595ca02815Sjsg pipe_config->fdi_lanes = lane;
2605ca02815Sjsg
2615ca02815Sjsg intel_link_compute_m_n(pipe_config->pipe_bpp, lane, fdi_dotclock,
2621bb76ff1Sjsg link_bw, &pipe_config->fdi_m_n, false);
2635ca02815Sjsg
2645ca02815Sjsg ret = ilk_check_fdi_lanes(dev, crtc->pipe, pipe_config);
2655ca02815Sjsg if (ret == -EDEADLK)
2665ca02815Sjsg return ret;
2675ca02815Sjsg
2685ca02815Sjsg if (ret == -EINVAL && pipe_config->pipe_bpp > 6*3) {
2695ca02815Sjsg pipe_config->pipe_bpp -= 2*3;
2705ca02815Sjsg drm_dbg_kms(&i915->drm,
2715ca02815Sjsg "fdi link bw constraint, reducing pipe bpp to %i\n",
2725ca02815Sjsg pipe_config->pipe_bpp);
2735ca02815Sjsg needs_recompute = true;
2745ca02815Sjsg pipe_config->bw_constrained = true;
2755ca02815Sjsg
2765ca02815Sjsg goto retry;
2775ca02815Sjsg }
2785ca02815Sjsg
2795ca02815Sjsg if (needs_recompute)
2801bb76ff1Sjsg return -EAGAIN;
2815ca02815Sjsg
2825ca02815Sjsg return ret;
2835ca02815Sjsg }
2845ca02815Sjsg
cpt_set_fdi_bc_bifurcation(struct drm_i915_private * dev_priv,bool enable)2851bb76ff1Sjsg static void cpt_set_fdi_bc_bifurcation(struct drm_i915_private *dev_priv, bool enable)
2861bb76ff1Sjsg {
2871bb76ff1Sjsg u32 temp;
2881bb76ff1Sjsg
2891bb76ff1Sjsg temp = intel_de_read(dev_priv, SOUTH_CHICKEN1);
2901bb76ff1Sjsg if (!!(temp & FDI_BC_BIFURCATION_SELECT) == enable)
2911bb76ff1Sjsg return;
2921bb76ff1Sjsg
2931bb76ff1Sjsg drm_WARN_ON(&dev_priv->drm,
2941bb76ff1Sjsg intel_de_read(dev_priv, FDI_RX_CTL(PIPE_B)) &
2951bb76ff1Sjsg FDI_RX_ENABLE);
2961bb76ff1Sjsg drm_WARN_ON(&dev_priv->drm,
2971bb76ff1Sjsg intel_de_read(dev_priv, FDI_RX_CTL(PIPE_C)) &
2981bb76ff1Sjsg FDI_RX_ENABLE);
2991bb76ff1Sjsg
3001bb76ff1Sjsg temp &= ~FDI_BC_BIFURCATION_SELECT;
3011bb76ff1Sjsg if (enable)
3021bb76ff1Sjsg temp |= FDI_BC_BIFURCATION_SELECT;
3031bb76ff1Sjsg
3041bb76ff1Sjsg drm_dbg_kms(&dev_priv->drm, "%sabling fdi C rx\n",
3051bb76ff1Sjsg enable ? "en" : "dis");
3061bb76ff1Sjsg intel_de_write(dev_priv, SOUTH_CHICKEN1, temp);
3071bb76ff1Sjsg intel_de_posting_read(dev_priv, SOUTH_CHICKEN1);
3081bb76ff1Sjsg }
3091bb76ff1Sjsg
ivb_update_fdi_bc_bifurcation(const struct intel_crtc_state * crtc_state)3101bb76ff1Sjsg static void ivb_update_fdi_bc_bifurcation(const struct intel_crtc_state *crtc_state)
3111bb76ff1Sjsg {
3121bb76ff1Sjsg struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
3131bb76ff1Sjsg struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
3141bb76ff1Sjsg
3151bb76ff1Sjsg switch (crtc->pipe) {
3161bb76ff1Sjsg case PIPE_A:
3171bb76ff1Sjsg break;
3181bb76ff1Sjsg case PIPE_B:
3191bb76ff1Sjsg if (crtc_state->fdi_lanes > 2)
3201bb76ff1Sjsg cpt_set_fdi_bc_bifurcation(dev_priv, false);
3211bb76ff1Sjsg else
3221bb76ff1Sjsg cpt_set_fdi_bc_bifurcation(dev_priv, true);
3231bb76ff1Sjsg
3241bb76ff1Sjsg break;
3251bb76ff1Sjsg case PIPE_C:
3261bb76ff1Sjsg cpt_set_fdi_bc_bifurcation(dev_priv, true);
3271bb76ff1Sjsg
3281bb76ff1Sjsg break;
3291bb76ff1Sjsg default:
3301bb76ff1Sjsg MISSING_CASE(crtc->pipe);
3311bb76ff1Sjsg }
3321bb76ff1Sjsg }
3331bb76ff1Sjsg
intel_fdi_normal_train(struct intel_crtc * crtc)3345ca02815Sjsg void intel_fdi_normal_train(struct intel_crtc *crtc)
3355ca02815Sjsg {
3365ca02815Sjsg struct drm_device *dev = crtc->base.dev;
3375ca02815Sjsg struct drm_i915_private *dev_priv = to_i915(dev);
3385ca02815Sjsg enum pipe pipe = crtc->pipe;
3395ca02815Sjsg i915_reg_t reg;
3405ca02815Sjsg u32 temp;
3415ca02815Sjsg
3425ca02815Sjsg /* enable normal train */
3435ca02815Sjsg reg = FDI_TX_CTL(pipe);
3445ca02815Sjsg temp = intel_de_read(dev_priv, reg);
3455ca02815Sjsg if (IS_IVYBRIDGE(dev_priv)) {
3465ca02815Sjsg temp &= ~FDI_LINK_TRAIN_NONE_IVB;
3475ca02815Sjsg temp |= FDI_LINK_TRAIN_NONE_IVB | FDI_TX_ENHANCE_FRAME_ENABLE;
3485ca02815Sjsg } else {
3495ca02815Sjsg temp &= ~FDI_LINK_TRAIN_NONE;
3505ca02815Sjsg temp |= FDI_LINK_TRAIN_NONE | FDI_TX_ENHANCE_FRAME_ENABLE;
3515ca02815Sjsg }
3525ca02815Sjsg intel_de_write(dev_priv, reg, temp);
3535ca02815Sjsg
3545ca02815Sjsg reg = FDI_RX_CTL(pipe);
3555ca02815Sjsg temp = intel_de_read(dev_priv, reg);
3565ca02815Sjsg if (HAS_PCH_CPT(dev_priv)) {
3575ca02815Sjsg temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
3585ca02815Sjsg temp |= FDI_LINK_TRAIN_NORMAL_CPT;
3595ca02815Sjsg } else {
3605ca02815Sjsg temp &= ~FDI_LINK_TRAIN_NONE;
3615ca02815Sjsg temp |= FDI_LINK_TRAIN_NONE;
3625ca02815Sjsg }
3635ca02815Sjsg intel_de_write(dev_priv, reg, temp | FDI_RX_ENHANCE_FRAME_ENABLE);
3645ca02815Sjsg
3655ca02815Sjsg /* wait one idle pattern time */
3665ca02815Sjsg intel_de_posting_read(dev_priv, reg);
3675ca02815Sjsg udelay(1000);
3685ca02815Sjsg
3695ca02815Sjsg /* IVB wants error correction enabled */
3705ca02815Sjsg if (IS_IVYBRIDGE(dev_priv))
371*f005ef32Sjsg intel_de_rmw(dev_priv, reg, 0, FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE);
3725ca02815Sjsg }
3735ca02815Sjsg
3745ca02815Sjsg /* The FDI link training functions for ILK/Ibexpeak. */
ilk_fdi_link_train(struct intel_crtc * crtc,const struct intel_crtc_state * crtc_state)3755ca02815Sjsg static void ilk_fdi_link_train(struct intel_crtc *crtc,
3765ca02815Sjsg const struct intel_crtc_state *crtc_state)
3775ca02815Sjsg {
3785ca02815Sjsg struct drm_device *dev = crtc->base.dev;
3795ca02815Sjsg struct drm_i915_private *dev_priv = to_i915(dev);
3805ca02815Sjsg enum pipe pipe = crtc->pipe;
3815ca02815Sjsg i915_reg_t reg;
3825ca02815Sjsg u32 temp, tries;
3835ca02815Sjsg
3841bb76ff1Sjsg /*
3851bb76ff1Sjsg * Write the TU size bits before fdi link training, so that error
3861bb76ff1Sjsg * detection works.
3871bb76ff1Sjsg */
3881bb76ff1Sjsg intel_de_write(dev_priv, FDI_RX_TUSIZE1(pipe),
3891bb76ff1Sjsg intel_de_read(dev_priv, PIPE_DATA_M1(pipe)) & TU_SIZE_MASK);
3901bb76ff1Sjsg
3915ca02815Sjsg /* FDI needs bits from pipe first */
3921bb76ff1Sjsg assert_transcoder_enabled(dev_priv, crtc_state->cpu_transcoder);
3935ca02815Sjsg
3945ca02815Sjsg /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit
3955ca02815Sjsg for train result */
3965ca02815Sjsg reg = FDI_RX_IMR(pipe);
3975ca02815Sjsg temp = intel_de_read(dev_priv, reg);
3985ca02815Sjsg temp &= ~FDI_RX_SYMBOL_LOCK;
3995ca02815Sjsg temp &= ~FDI_RX_BIT_LOCK;
4005ca02815Sjsg intel_de_write(dev_priv, reg, temp);
4015ca02815Sjsg intel_de_read(dev_priv, reg);
4025ca02815Sjsg udelay(150);
4035ca02815Sjsg
4045ca02815Sjsg /* enable CPU FDI TX and PCH FDI RX */
4055ca02815Sjsg reg = FDI_TX_CTL(pipe);
4065ca02815Sjsg temp = intel_de_read(dev_priv, reg);
4075ca02815Sjsg temp &= ~FDI_DP_PORT_WIDTH_MASK;
4085ca02815Sjsg temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes);
4095ca02815Sjsg temp &= ~FDI_LINK_TRAIN_NONE;
4105ca02815Sjsg temp |= FDI_LINK_TRAIN_PATTERN_1;
4115ca02815Sjsg intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE);
4125ca02815Sjsg
4135ca02815Sjsg reg = FDI_RX_CTL(pipe);
4145ca02815Sjsg temp = intel_de_read(dev_priv, reg);
4155ca02815Sjsg temp &= ~FDI_LINK_TRAIN_NONE;
4165ca02815Sjsg temp |= FDI_LINK_TRAIN_PATTERN_1;
4175ca02815Sjsg intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE);
4185ca02815Sjsg
4195ca02815Sjsg intel_de_posting_read(dev_priv, reg);
4205ca02815Sjsg udelay(150);
4215ca02815Sjsg
4225ca02815Sjsg /* Ironlake workaround, enable clock pointer after FDI enable*/
4235ca02815Sjsg intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe),
4245ca02815Sjsg FDI_RX_PHASE_SYNC_POINTER_OVR);
4255ca02815Sjsg intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe),
4265ca02815Sjsg FDI_RX_PHASE_SYNC_POINTER_OVR | FDI_RX_PHASE_SYNC_POINTER_EN);
4275ca02815Sjsg
4285ca02815Sjsg reg = FDI_RX_IIR(pipe);
4295ca02815Sjsg for (tries = 0; tries < 5; tries++) {
4305ca02815Sjsg temp = intel_de_read(dev_priv, reg);
4315ca02815Sjsg drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp);
4325ca02815Sjsg
4335ca02815Sjsg if ((temp & FDI_RX_BIT_LOCK)) {
4345ca02815Sjsg drm_dbg_kms(&dev_priv->drm, "FDI train 1 done.\n");
4355ca02815Sjsg intel_de_write(dev_priv, reg, temp | FDI_RX_BIT_LOCK);
4365ca02815Sjsg break;
4375ca02815Sjsg }
4385ca02815Sjsg }
4395ca02815Sjsg if (tries == 5)
4405ca02815Sjsg drm_err(&dev_priv->drm, "FDI train 1 fail!\n");
4415ca02815Sjsg
4425ca02815Sjsg /* Train 2 */
443*f005ef32Sjsg intel_de_rmw(dev_priv, FDI_TX_CTL(pipe),
444*f005ef32Sjsg FDI_LINK_TRAIN_NONE, FDI_LINK_TRAIN_PATTERN_2);
445*f005ef32Sjsg intel_de_rmw(dev_priv, FDI_RX_CTL(pipe),
446*f005ef32Sjsg FDI_LINK_TRAIN_NONE, FDI_LINK_TRAIN_PATTERN_2);
447*f005ef32Sjsg intel_de_posting_read(dev_priv, FDI_RX_CTL(pipe));
4485ca02815Sjsg udelay(150);
4495ca02815Sjsg
4505ca02815Sjsg reg = FDI_RX_IIR(pipe);
4515ca02815Sjsg for (tries = 0; tries < 5; tries++) {
4525ca02815Sjsg temp = intel_de_read(dev_priv, reg);
4535ca02815Sjsg drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp);
4545ca02815Sjsg
4555ca02815Sjsg if (temp & FDI_RX_SYMBOL_LOCK) {
4565ca02815Sjsg intel_de_write(dev_priv, reg,
4575ca02815Sjsg temp | FDI_RX_SYMBOL_LOCK);
4585ca02815Sjsg drm_dbg_kms(&dev_priv->drm, "FDI train 2 done.\n");
4595ca02815Sjsg break;
4605ca02815Sjsg }
4615ca02815Sjsg }
4625ca02815Sjsg if (tries == 5)
4635ca02815Sjsg drm_err(&dev_priv->drm, "FDI train 2 fail!\n");
4645ca02815Sjsg
4655ca02815Sjsg drm_dbg_kms(&dev_priv->drm, "FDI train done\n");
4665ca02815Sjsg
4675ca02815Sjsg }
4685ca02815Sjsg
4695ca02815Sjsg static const int snb_b_fdi_train_param[] = {
4705ca02815Sjsg FDI_LINK_TRAIN_400MV_0DB_SNB_B,
4715ca02815Sjsg FDI_LINK_TRAIN_400MV_6DB_SNB_B,
4725ca02815Sjsg FDI_LINK_TRAIN_600MV_3_5DB_SNB_B,
4735ca02815Sjsg FDI_LINK_TRAIN_800MV_0DB_SNB_B,
4745ca02815Sjsg };
4755ca02815Sjsg
4765ca02815Sjsg /* The FDI link training functions for SNB/Cougarpoint. */
gen6_fdi_link_train(struct intel_crtc * crtc,const struct intel_crtc_state * crtc_state)4775ca02815Sjsg static void gen6_fdi_link_train(struct intel_crtc *crtc,
4785ca02815Sjsg const struct intel_crtc_state *crtc_state)
4795ca02815Sjsg {
4805ca02815Sjsg struct drm_device *dev = crtc->base.dev;
4815ca02815Sjsg struct drm_i915_private *dev_priv = to_i915(dev);
4825ca02815Sjsg enum pipe pipe = crtc->pipe;
4835ca02815Sjsg i915_reg_t reg;
4845ca02815Sjsg u32 temp, i, retry;
4855ca02815Sjsg
4861bb76ff1Sjsg /*
4871bb76ff1Sjsg * Write the TU size bits before fdi link training, so that error
4881bb76ff1Sjsg * detection works.
4891bb76ff1Sjsg */
4901bb76ff1Sjsg intel_de_write(dev_priv, FDI_RX_TUSIZE1(pipe),
4911bb76ff1Sjsg intel_de_read(dev_priv, PIPE_DATA_M1(pipe)) & TU_SIZE_MASK);
4921bb76ff1Sjsg
4935ca02815Sjsg /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit
4945ca02815Sjsg for train result */
4955ca02815Sjsg reg = FDI_RX_IMR(pipe);
4965ca02815Sjsg temp = intel_de_read(dev_priv, reg);
4975ca02815Sjsg temp &= ~FDI_RX_SYMBOL_LOCK;
4985ca02815Sjsg temp &= ~FDI_RX_BIT_LOCK;
4995ca02815Sjsg intel_de_write(dev_priv, reg, temp);
5005ca02815Sjsg
5015ca02815Sjsg intel_de_posting_read(dev_priv, reg);
5025ca02815Sjsg udelay(150);
5035ca02815Sjsg
5045ca02815Sjsg /* enable CPU FDI TX and PCH FDI RX */
5055ca02815Sjsg reg = FDI_TX_CTL(pipe);
5065ca02815Sjsg temp = intel_de_read(dev_priv, reg);
5075ca02815Sjsg temp &= ~FDI_DP_PORT_WIDTH_MASK;
5085ca02815Sjsg temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes);
5095ca02815Sjsg temp &= ~FDI_LINK_TRAIN_NONE;
5105ca02815Sjsg temp |= FDI_LINK_TRAIN_PATTERN_1;
5115ca02815Sjsg temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
5125ca02815Sjsg /* SNB-B */
5135ca02815Sjsg temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B;
5145ca02815Sjsg intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE);
5155ca02815Sjsg
5165ca02815Sjsg intel_de_write(dev_priv, FDI_RX_MISC(pipe),
5175ca02815Sjsg FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90);
5185ca02815Sjsg
5195ca02815Sjsg reg = FDI_RX_CTL(pipe);
5205ca02815Sjsg temp = intel_de_read(dev_priv, reg);
5215ca02815Sjsg if (HAS_PCH_CPT(dev_priv)) {
5225ca02815Sjsg temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
5235ca02815Sjsg temp |= FDI_LINK_TRAIN_PATTERN_1_CPT;
5245ca02815Sjsg } else {
5255ca02815Sjsg temp &= ~FDI_LINK_TRAIN_NONE;
5265ca02815Sjsg temp |= FDI_LINK_TRAIN_PATTERN_1;
5275ca02815Sjsg }
5285ca02815Sjsg intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE);
5295ca02815Sjsg
5305ca02815Sjsg intel_de_posting_read(dev_priv, reg);
5315ca02815Sjsg udelay(150);
5325ca02815Sjsg
5335ca02815Sjsg for (i = 0; i < 4; i++) {
534*f005ef32Sjsg intel_de_rmw(dev_priv, FDI_TX_CTL(pipe),
535*f005ef32Sjsg FDI_LINK_TRAIN_VOL_EMP_MASK, snb_b_fdi_train_param[i]);
536*f005ef32Sjsg intel_de_posting_read(dev_priv, FDI_TX_CTL(pipe));
5375ca02815Sjsg udelay(500);
5385ca02815Sjsg
5395ca02815Sjsg for (retry = 0; retry < 5; retry++) {
5405ca02815Sjsg reg = FDI_RX_IIR(pipe);
5415ca02815Sjsg temp = intel_de_read(dev_priv, reg);
5425ca02815Sjsg drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp);
5435ca02815Sjsg if (temp & FDI_RX_BIT_LOCK) {
5445ca02815Sjsg intel_de_write(dev_priv, reg,
5455ca02815Sjsg temp | FDI_RX_BIT_LOCK);
5465ca02815Sjsg drm_dbg_kms(&dev_priv->drm,
5475ca02815Sjsg "FDI train 1 done.\n");
5485ca02815Sjsg break;
5495ca02815Sjsg }
5505ca02815Sjsg udelay(50);
5515ca02815Sjsg }
5525ca02815Sjsg if (retry < 5)
5535ca02815Sjsg break;
5545ca02815Sjsg }
5555ca02815Sjsg if (i == 4)
5565ca02815Sjsg drm_err(&dev_priv->drm, "FDI train 1 fail!\n");
5575ca02815Sjsg
5585ca02815Sjsg /* Train 2 */
5595ca02815Sjsg reg = FDI_TX_CTL(pipe);
5605ca02815Sjsg temp = intel_de_read(dev_priv, reg);
5615ca02815Sjsg temp &= ~FDI_LINK_TRAIN_NONE;
5625ca02815Sjsg temp |= FDI_LINK_TRAIN_PATTERN_2;
5635ca02815Sjsg if (IS_SANDYBRIDGE(dev_priv)) {
5645ca02815Sjsg temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
5655ca02815Sjsg /* SNB-B */
5665ca02815Sjsg temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B;
5675ca02815Sjsg }
5685ca02815Sjsg intel_de_write(dev_priv, reg, temp);
5695ca02815Sjsg
5705ca02815Sjsg reg = FDI_RX_CTL(pipe);
5715ca02815Sjsg temp = intel_de_read(dev_priv, reg);
5725ca02815Sjsg if (HAS_PCH_CPT(dev_priv)) {
5735ca02815Sjsg temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
5745ca02815Sjsg temp |= FDI_LINK_TRAIN_PATTERN_2_CPT;
5755ca02815Sjsg } else {
5765ca02815Sjsg temp &= ~FDI_LINK_TRAIN_NONE;
5775ca02815Sjsg temp |= FDI_LINK_TRAIN_PATTERN_2;
5785ca02815Sjsg }
5795ca02815Sjsg intel_de_write(dev_priv, reg, temp);
5805ca02815Sjsg
5815ca02815Sjsg intel_de_posting_read(dev_priv, reg);
5825ca02815Sjsg udelay(150);
5835ca02815Sjsg
5845ca02815Sjsg for (i = 0; i < 4; i++) {
585*f005ef32Sjsg intel_de_rmw(dev_priv, FDI_TX_CTL(pipe),
586*f005ef32Sjsg FDI_LINK_TRAIN_VOL_EMP_MASK, snb_b_fdi_train_param[i]);
587*f005ef32Sjsg intel_de_posting_read(dev_priv, FDI_TX_CTL(pipe));
5885ca02815Sjsg udelay(500);
5895ca02815Sjsg
5905ca02815Sjsg for (retry = 0; retry < 5; retry++) {
5915ca02815Sjsg reg = FDI_RX_IIR(pipe);
5925ca02815Sjsg temp = intel_de_read(dev_priv, reg);
5935ca02815Sjsg drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp);
5945ca02815Sjsg if (temp & FDI_RX_SYMBOL_LOCK) {
5955ca02815Sjsg intel_de_write(dev_priv, reg,
5965ca02815Sjsg temp | FDI_RX_SYMBOL_LOCK);
5975ca02815Sjsg drm_dbg_kms(&dev_priv->drm,
5985ca02815Sjsg "FDI train 2 done.\n");
5995ca02815Sjsg break;
6005ca02815Sjsg }
6015ca02815Sjsg udelay(50);
6025ca02815Sjsg }
6035ca02815Sjsg if (retry < 5)
6045ca02815Sjsg break;
6055ca02815Sjsg }
6065ca02815Sjsg if (i == 4)
6075ca02815Sjsg drm_err(&dev_priv->drm, "FDI train 2 fail!\n");
6085ca02815Sjsg
6095ca02815Sjsg drm_dbg_kms(&dev_priv->drm, "FDI train done.\n");
6105ca02815Sjsg }
6115ca02815Sjsg
6125ca02815Sjsg /* Manual link training for Ivy Bridge A0 parts */
ivb_manual_fdi_link_train(struct intel_crtc * crtc,const struct intel_crtc_state * crtc_state)6135ca02815Sjsg static void ivb_manual_fdi_link_train(struct intel_crtc *crtc,
6145ca02815Sjsg const struct intel_crtc_state *crtc_state)
6155ca02815Sjsg {
6165ca02815Sjsg struct drm_device *dev = crtc->base.dev;
6175ca02815Sjsg struct drm_i915_private *dev_priv = to_i915(dev);
6185ca02815Sjsg enum pipe pipe = crtc->pipe;
6195ca02815Sjsg i915_reg_t reg;
6205ca02815Sjsg u32 temp, i, j;
6215ca02815Sjsg
6221bb76ff1Sjsg ivb_update_fdi_bc_bifurcation(crtc_state);
6231bb76ff1Sjsg
6241bb76ff1Sjsg /*
6251bb76ff1Sjsg * Write the TU size bits before fdi link training, so that error
6261bb76ff1Sjsg * detection works.
6271bb76ff1Sjsg */
6281bb76ff1Sjsg intel_de_write(dev_priv, FDI_RX_TUSIZE1(pipe),
6291bb76ff1Sjsg intel_de_read(dev_priv, PIPE_DATA_M1(pipe)) & TU_SIZE_MASK);
6301bb76ff1Sjsg
6315ca02815Sjsg /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit
6325ca02815Sjsg for train result */
6335ca02815Sjsg reg = FDI_RX_IMR(pipe);
6345ca02815Sjsg temp = intel_de_read(dev_priv, reg);
6355ca02815Sjsg temp &= ~FDI_RX_SYMBOL_LOCK;
6365ca02815Sjsg temp &= ~FDI_RX_BIT_LOCK;
6375ca02815Sjsg intel_de_write(dev_priv, reg, temp);
6385ca02815Sjsg
6395ca02815Sjsg intel_de_posting_read(dev_priv, reg);
6405ca02815Sjsg udelay(150);
6415ca02815Sjsg
6425ca02815Sjsg drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR before link train 0x%x\n",
6435ca02815Sjsg intel_de_read(dev_priv, FDI_RX_IIR(pipe)));
6445ca02815Sjsg
6455ca02815Sjsg /* Try each vswing and preemphasis setting twice before moving on */
6465ca02815Sjsg for (j = 0; j < ARRAY_SIZE(snb_b_fdi_train_param) * 2; j++) {
6475ca02815Sjsg /* disable first in case we need to retry */
6485ca02815Sjsg reg = FDI_TX_CTL(pipe);
6495ca02815Sjsg temp = intel_de_read(dev_priv, reg);
6505ca02815Sjsg temp &= ~(FDI_LINK_TRAIN_AUTO | FDI_LINK_TRAIN_NONE_IVB);
6515ca02815Sjsg temp &= ~FDI_TX_ENABLE;
6525ca02815Sjsg intel_de_write(dev_priv, reg, temp);
6535ca02815Sjsg
6545ca02815Sjsg reg = FDI_RX_CTL(pipe);
6555ca02815Sjsg temp = intel_de_read(dev_priv, reg);
6565ca02815Sjsg temp &= ~FDI_LINK_TRAIN_AUTO;
6575ca02815Sjsg temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
6585ca02815Sjsg temp &= ~FDI_RX_ENABLE;
6595ca02815Sjsg intel_de_write(dev_priv, reg, temp);
6605ca02815Sjsg
6615ca02815Sjsg /* enable CPU FDI TX and PCH FDI RX */
6625ca02815Sjsg reg = FDI_TX_CTL(pipe);
6635ca02815Sjsg temp = intel_de_read(dev_priv, reg);
6645ca02815Sjsg temp &= ~FDI_DP_PORT_WIDTH_MASK;
6655ca02815Sjsg temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes);
6665ca02815Sjsg temp |= FDI_LINK_TRAIN_PATTERN_1_IVB;
6675ca02815Sjsg temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
6685ca02815Sjsg temp |= snb_b_fdi_train_param[j/2];
6695ca02815Sjsg temp |= FDI_COMPOSITE_SYNC;
6705ca02815Sjsg intel_de_write(dev_priv, reg, temp | FDI_TX_ENABLE);
6715ca02815Sjsg
6725ca02815Sjsg intel_de_write(dev_priv, FDI_RX_MISC(pipe),
6735ca02815Sjsg FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90);
6745ca02815Sjsg
6755ca02815Sjsg reg = FDI_RX_CTL(pipe);
6765ca02815Sjsg temp = intel_de_read(dev_priv, reg);
6775ca02815Sjsg temp |= FDI_LINK_TRAIN_PATTERN_1_CPT;
6785ca02815Sjsg temp |= FDI_COMPOSITE_SYNC;
6795ca02815Sjsg intel_de_write(dev_priv, reg, temp | FDI_RX_ENABLE);
6805ca02815Sjsg
6815ca02815Sjsg intel_de_posting_read(dev_priv, reg);
6825ca02815Sjsg udelay(1); /* should be 0.5us */
6835ca02815Sjsg
6845ca02815Sjsg for (i = 0; i < 4; i++) {
6855ca02815Sjsg reg = FDI_RX_IIR(pipe);
6865ca02815Sjsg temp = intel_de_read(dev_priv, reg);
6875ca02815Sjsg drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp);
6885ca02815Sjsg
6895ca02815Sjsg if (temp & FDI_RX_BIT_LOCK ||
6905ca02815Sjsg (intel_de_read(dev_priv, reg) & FDI_RX_BIT_LOCK)) {
6915ca02815Sjsg intel_de_write(dev_priv, reg,
6925ca02815Sjsg temp | FDI_RX_BIT_LOCK);
6935ca02815Sjsg drm_dbg_kms(&dev_priv->drm,
6945ca02815Sjsg "FDI train 1 done, level %i.\n",
6955ca02815Sjsg i);
6965ca02815Sjsg break;
6975ca02815Sjsg }
6985ca02815Sjsg udelay(1); /* should be 0.5us */
6995ca02815Sjsg }
7005ca02815Sjsg if (i == 4) {
7015ca02815Sjsg drm_dbg_kms(&dev_priv->drm,
7025ca02815Sjsg "FDI train 1 fail on vswing %d\n", j / 2);
7035ca02815Sjsg continue;
7045ca02815Sjsg }
7055ca02815Sjsg
7065ca02815Sjsg /* Train 2 */
707*f005ef32Sjsg intel_de_rmw(dev_priv, FDI_TX_CTL(pipe),
708*f005ef32Sjsg FDI_LINK_TRAIN_NONE_IVB,
709*f005ef32Sjsg FDI_LINK_TRAIN_PATTERN_2_IVB);
710*f005ef32Sjsg intel_de_rmw(dev_priv, FDI_RX_CTL(pipe),
711*f005ef32Sjsg FDI_LINK_TRAIN_PATTERN_MASK_CPT,
712*f005ef32Sjsg FDI_LINK_TRAIN_PATTERN_2_CPT);
713*f005ef32Sjsg intel_de_posting_read(dev_priv, FDI_RX_CTL(pipe));
7145ca02815Sjsg udelay(2); /* should be 1.5us */
7155ca02815Sjsg
7165ca02815Sjsg for (i = 0; i < 4; i++) {
7175ca02815Sjsg reg = FDI_RX_IIR(pipe);
7185ca02815Sjsg temp = intel_de_read(dev_priv, reg);
7195ca02815Sjsg drm_dbg_kms(&dev_priv->drm, "FDI_RX_IIR 0x%x\n", temp);
7205ca02815Sjsg
7215ca02815Sjsg if (temp & FDI_RX_SYMBOL_LOCK ||
7225ca02815Sjsg (intel_de_read(dev_priv, reg) & FDI_RX_SYMBOL_LOCK)) {
7235ca02815Sjsg intel_de_write(dev_priv, reg,
7245ca02815Sjsg temp | FDI_RX_SYMBOL_LOCK);
7255ca02815Sjsg drm_dbg_kms(&dev_priv->drm,
7265ca02815Sjsg "FDI train 2 done, level %i.\n",
7275ca02815Sjsg i);
7285ca02815Sjsg goto train_done;
7295ca02815Sjsg }
7305ca02815Sjsg udelay(2); /* should be 1.5us */
7315ca02815Sjsg }
7325ca02815Sjsg if (i == 4)
7335ca02815Sjsg drm_dbg_kms(&dev_priv->drm,
7345ca02815Sjsg "FDI train 2 fail on vswing %d\n", j / 2);
7355ca02815Sjsg }
7365ca02815Sjsg
7375ca02815Sjsg train_done:
7385ca02815Sjsg drm_dbg_kms(&dev_priv->drm, "FDI train done.\n");
7395ca02815Sjsg }
7405ca02815Sjsg
7415ca02815Sjsg /* Starting with Haswell, different DDI ports can work in FDI mode for
7425ca02815Sjsg * connection to the PCH-located connectors. For this, it is necessary to train
7435ca02815Sjsg * both the DDI port and PCH receiver for the desired DDI buffer settings.
7445ca02815Sjsg *
7455ca02815Sjsg * The recommended port to work in FDI mode is DDI E, which we use here. Also,
7465ca02815Sjsg * please note that when FDI mode is active on DDI E, it shares 2 lines with
7475ca02815Sjsg * DDI A (which is used for eDP)
7485ca02815Sjsg */
hsw_fdi_link_train(struct intel_encoder * encoder,const struct intel_crtc_state * crtc_state)7495ca02815Sjsg void hsw_fdi_link_train(struct intel_encoder *encoder,
7505ca02815Sjsg const struct intel_crtc_state *crtc_state)
7515ca02815Sjsg {
7525ca02815Sjsg struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
7535ca02815Sjsg struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
7545ca02815Sjsg u32 temp, i, rx_ctl_val;
7555ca02815Sjsg int n_entries;
7565ca02815Sjsg
7575ca02815Sjsg encoder->get_buf_trans(encoder, crtc_state, &n_entries);
7585ca02815Sjsg
7595ca02815Sjsg hsw_prepare_dp_ddi_buffers(encoder, crtc_state);
7605ca02815Sjsg
7615ca02815Sjsg /* Set the FDI_RX_MISC pwrdn lanes and the 2 workarounds listed at the
7625ca02815Sjsg * mode set "sequence for CRT port" document:
7635ca02815Sjsg * - TP1 to TP2 time with the default value
7645ca02815Sjsg * - FDI delay to 90h
7655ca02815Sjsg *
7665ca02815Sjsg * WaFDIAutoLinkSetTimingOverrride:hsw
7675ca02815Sjsg */
7685ca02815Sjsg intel_de_write(dev_priv, FDI_RX_MISC(PIPE_A),
7695ca02815Sjsg FDI_RX_PWRDN_LANE1_VAL(2) | FDI_RX_PWRDN_LANE0_VAL(2) | FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90);
7705ca02815Sjsg
7715ca02815Sjsg /* Enable the PCH Receiver FDI PLL */
7721bb76ff1Sjsg rx_ctl_val = dev_priv->display.fdi.rx_config | FDI_RX_ENHANCE_FRAME_ENABLE |
7735ca02815Sjsg FDI_RX_PLL_ENABLE |
7745ca02815Sjsg FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes);
7755ca02815Sjsg intel_de_write(dev_priv, FDI_RX_CTL(PIPE_A), rx_ctl_val);
7765ca02815Sjsg intel_de_posting_read(dev_priv, FDI_RX_CTL(PIPE_A));
7775ca02815Sjsg udelay(220);
7785ca02815Sjsg
7795ca02815Sjsg /* Switch from Rawclk to PCDclk */
7805ca02815Sjsg rx_ctl_val |= FDI_PCDCLK;
7815ca02815Sjsg intel_de_write(dev_priv, FDI_RX_CTL(PIPE_A), rx_ctl_val);
7825ca02815Sjsg
7835ca02815Sjsg /* Configure Port Clock Select */
7845ca02815Sjsg drm_WARN_ON(&dev_priv->drm, crtc_state->shared_dpll->info->id != DPLL_ID_SPLL);
7855ca02815Sjsg intel_ddi_enable_clock(encoder, crtc_state);
7865ca02815Sjsg
7875ca02815Sjsg /* Start the training iterating through available voltages and emphasis,
7885ca02815Sjsg * testing each value twice. */
7895ca02815Sjsg for (i = 0; i < n_entries * 2; i++) {
7905ca02815Sjsg /* Configure DP_TP_CTL with auto-training */
7915ca02815Sjsg intel_de_write(dev_priv, DP_TP_CTL(PORT_E),
7925ca02815Sjsg DP_TP_CTL_FDI_AUTOTRAIN |
7935ca02815Sjsg DP_TP_CTL_ENHANCED_FRAME_ENABLE |
7945ca02815Sjsg DP_TP_CTL_LINK_TRAIN_PAT1 |
7955ca02815Sjsg DP_TP_CTL_ENABLE);
7965ca02815Sjsg
7975ca02815Sjsg /* Configure and enable DDI_BUF_CTL for DDI E with next voltage.
7985ca02815Sjsg * DDI E does not support port reversal, the functionality is
7995ca02815Sjsg * achieved on the PCH side in FDI_RX_CTL, so no need to set the
8005ca02815Sjsg * port reversal bit */
8015ca02815Sjsg intel_de_write(dev_priv, DDI_BUF_CTL(PORT_E),
8025ca02815Sjsg DDI_BUF_CTL_ENABLE | ((crtc_state->fdi_lanes - 1) << 1) | DDI_BUF_TRANS_SELECT(i / 2));
8035ca02815Sjsg intel_de_posting_read(dev_priv, DDI_BUF_CTL(PORT_E));
8045ca02815Sjsg
8055ca02815Sjsg udelay(600);
8065ca02815Sjsg
8075ca02815Sjsg /* Program PCH FDI Receiver TU */
8085ca02815Sjsg intel_de_write(dev_priv, FDI_RX_TUSIZE1(PIPE_A), TU_SIZE(64));
8095ca02815Sjsg
8105ca02815Sjsg /* Enable PCH FDI Receiver with auto-training */
8115ca02815Sjsg rx_ctl_val |= FDI_RX_ENABLE | FDI_LINK_TRAIN_AUTO;
8125ca02815Sjsg intel_de_write(dev_priv, FDI_RX_CTL(PIPE_A), rx_ctl_val);
8135ca02815Sjsg intel_de_posting_read(dev_priv, FDI_RX_CTL(PIPE_A));
8145ca02815Sjsg
8155ca02815Sjsg /* Wait for FDI receiver lane calibration */
8165ca02815Sjsg udelay(30);
8175ca02815Sjsg
8185ca02815Sjsg /* Unset FDI_RX_MISC pwrdn lanes */
819*f005ef32Sjsg intel_de_rmw(dev_priv, FDI_RX_MISC(PIPE_A),
820*f005ef32Sjsg FDI_RX_PWRDN_LANE1_MASK | FDI_RX_PWRDN_LANE0_MASK, 0);
8215ca02815Sjsg intel_de_posting_read(dev_priv, FDI_RX_MISC(PIPE_A));
8225ca02815Sjsg
8235ca02815Sjsg /* Wait for FDI auto training time */
8245ca02815Sjsg udelay(5);
8255ca02815Sjsg
8265ca02815Sjsg temp = intel_de_read(dev_priv, DP_TP_STATUS(PORT_E));
8275ca02815Sjsg if (temp & DP_TP_STATUS_AUTOTRAIN_DONE) {
8285ca02815Sjsg drm_dbg_kms(&dev_priv->drm,
8295ca02815Sjsg "FDI link training done on step %d\n", i);
8305ca02815Sjsg break;
8315ca02815Sjsg }
8325ca02815Sjsg
8335ca02815Sjsg /*
8345ca02815Sjsg * Leave things enabled even if we failed to train FDI.
8355ca02815Sjsg * Results in less fireworks from the state checker.
8365ca02815Sjsg */
8375ca02815Sjsg if (i == n_entries * 2 - 1) {
8385ca02815Sjsg drm_err(&dev_priv->drm, "FDI link training failed!\n");
8395ca02815Sjsg break;
8405ca02815Sjsg }
8415ca02815Sjsg
8425ca02815Sjsg rx_ctl_val &= ~FDI_RX_ENABLE;
8435ca02815Sjsg intel_de_write(dev_priv, FDI_RX_CTL(PIPE_A), rx_ctl_val);
8445ca02815Sjsg intel_de_posting_read(dev_priv, FDI_RX_CTL(PIPE_A));
8455ca02815Sjsg
846*f005ef32Sjsg intel_de_rmw(dev_priv, DDI_BUF_CTL(PORT_E), DDI_BUF_CTL_ENABLE, 0);
8475ca02815Sjsg intel_de_posting_read(dev_priv, DDI_BUF_CTL(PORT_E));
8485ca02815Sjsg
8495ca02815Sjsg /* Disable DP_TP_CTL and FDI_RX_CTL and retry */
850*f005ef32Sjsg intel_de_rmw(dev_priv, DP_TP_CTL(PORT_E), DP_TP_CTL_ENABLE, 0);
8515ca02815Sjsg intel_de_posting_read(dev_priv, DP_TP_CTL(PORT_E));
8525ca02815Sjsg
8535ca02815Sjsg intel_wait_ddi_buf_idle(dev_priv, PORT_E);
8545ca02815Sjsg
8555ca02815Sjsg /* Reset FDI_RX_MISC pwrdn lanes */
856*f005ef32Sjsg intel_de_rmw(dev_priv, FDI_RX_MISC(PIPE_A),
857*f005ef32Sjsg FDI_RX_PWRDN_LANE1_MASK | FDI_RX_PWRDN_LANE0_MASK,
858*f005ef32Sjsg FDI_RX_PWRDN_LANE1_VAL(2) | FDI_RX_PWRDN_LANE0_VAL(2));
8595ca02815Sjsg intel_de_posting_read(dev_priv, FDI_RX_MISC(PIPE_A));
8605ca02815Sjsg }
8615ca02815Sjsg
8625ca02815Sjsg /* Enable normal pixel sending for FDI */
8635ca02815Sjsg intel_de_write(dev_priv, DP_TP_CTL(PORT_E),
8645ca02815Sjsg DP_TP_CTL_FDI_AUTOTRAIN |
8655ca02815Sjsg DP_TP_CTL_LINK_TRAIN_NORMAL |
8665ca02815Sjsg DP_TP_CTL_ENHANCED_FRAME_ENABLE |
8675ca02815Sjsg DP_TP_CTL_ENABLE);
8685ca02815Sjsg }
8695ca02815Sjsg
hsw_fdi_disable(struct intel_encoder * encoder)8701bb76ff1Sjsg void hsw_fdi_disable(struct intel_encoder *encoder)
8711bb76ff1Sjsg {
8721bb76ff1Sjsg struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
8731bb76ff1Sjsg
8741bb76ff1Sjsg /*
8751bb76ff1Sjsg * Bspec lists this as both step 13 (before DDI_BUF_CTL disable)
8761bb76ff1Sjsg * and step 18 (after clearing PORT_CLK_SEL). Based on a BUN,
8771bb76ff1Sjsg * step 13 is the correct place for it. Step 18 is where it was
8781bb76ff1Sjsg * originally before the BUN.
8791bb76ff1Sjsg */
880*f005ef32Sjsg intel_de_rmw(dev_priv, FDI_RX_CTL(PIPE_A), FDI_RX_ENABLE, 0);
881*f005ef32Sjsg intel_de_rmw(dev_priv, DDI_BUF_CTL(PORT_E), DDI_BUF_CTL_ENABLE, 0);
8821bb76ff1Sjsg intel_wait_ddi_buf_idle(dev_priv, PORT_E);
8831bb76ff1Sjsg intel_ddi_disable_clock(encoder);
884*f005ef32Sjsg intel_de_rmw(dev_priv, FDI_RX_MISC(PIPE_A),
885*f005ef32Sjsg FDI_RX_PWRDN_LANE1_MASK | FDI_RX_PWRDN_LANE0_MASK,
886*f005ef32Sjsg FDI_RX_PWRDN_LANE1_VAL(2) | FDI_RX_PWRDN_LANE0_VAL(2));
887*f005ef32Sjsg intel_de_rmw(dev_priv, FDI_RX_CTL(PIPE_A), FDI_PCDCLK, 0);
888*f005ef32Sjsg intel_de_rmw(dev_priv, FDI_RX_CTL(PIPE_A), FDI_RX_PLL_ENABLE, 0);
8891bb76ff1Sjsg }
8901bb76ff1Sjsg
ilk_fdi_pll_enable(const struct intel_crtc_state * crtc_state)8915ca02815Sjsg void ilk_fdi_pll_enable(const struct intel_crtc_state *crtc_state)
8925ca02815Sjsg {
8935ca02815Sjsg struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
8945ca02815Sjsg struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
8955ca02815Sjsg enum pipe pipe = crtc->pipe;
8965ca02815Sjsg i915_reg_t reg;
8975ca02815Sjsg u32 temp;
8985ca02815Sjsg
8995ca02815Sjsg /* enable PCH FDI RX PLL, wait warmup plus DMI latency */
9005ca02815Sjsg reg = FDI_RX_CTL(pipe);
9015ca02815Sjsg temp = intel_de_read(dev_priv, reg);
9025ca02815Sjsg temp &= ~(FDI_DP_PORT_WIDTH_MASK | (0x7 << 16));
9035ca02815Sjsg temp |= FDI_DP_PORT_WIDTH(crtc_state->fdi_lanes);
904*f005ef32Sjsg temp |= (intel_de_read(dev_priv, TRANSCONF(pipe)) & TRANSCONF_BPC_MASK) << 11;
9055ca02815Sjsg intel_de_write(dev_priv, reg, temp | FDI_RX_PLL_ENABLE);
9065ca02815Sjsg
9075ca02815Sjsg intel_de_posting_read(dev_priv, reg);
9085ca02815Sjsg udelay(200);
9095ca02815Sjsg
9105ca02815Sjsg /* Switch from Rawclk to PCDclk */
911*f005ef32Sjsg intel_de_rmw(dev_priv, reg, 0, FDI_PCDCLK);
9125ca02815Sjsg intel_de_posting_read(dev_priv, reg);
9135ca02815Sjsg udelay(200);
9145ca02815Sjsg
9155ca02815Sjsg /* Enable CPU FDI TX PLL, always on for Ironlake */
9165ca02815Sjsg reg = FDI_TX_CTL(pipe);
9175ca02815Sjsg temp = intel_de_read(dev_priv, reg);
9185ca02815Sjsg if ((temp & FDI_TX_PLL_ENABLE) == 0) {
9195ca02815Sjsg intel_de_write(dev_priv, reg, temp | FDI_TX_PLL_ENABLE);
9205ca02815Sjsg
9215ca02815Sjsg intel_de_posting_read(dev_priv, reg);
9225ca02815Sjsg udelay(100);
9235ca02815Sjsg }
9245ca02815Sjsg }
9255ca02815Sjsg
ilk_fdi_pll_disable(struct intel_crtc * crtc)9265ca02815Sjsg void ilk_fdi_pll_disable(struct intel_crtc *crtc)
9275ca02815Sjsg {
9285ca02815Sjsg struct drm_device *dev = crtc->base.dev;
9295ca02815Sjsg struct drm_i915_private *dev_priv = to_i915(dev);
9305ca02815Sjsg enum pipe pipe = crtc->pipe;
9315ca02815Sjsg
9325ca02815Sjsg /* Switch from PCDclk to Rawclk */
933*f005ef32Sjsg intel_de_rmw(dev_priv, FDI_RX_CTL(pipe), FDI_PCDCLK, 0);
9345ca02815Sjsg
9355ca02815Sjsg /* Disable CPU FDI TX PLL */
936*f005ef32Sjsg intel_de_rmw(dev_priv, FDI_TX_CTL(pipe), FDI_TX_PLL_ENABLE, 0);
937*f005ef32Sjsg intel_de_posting_read(dev_priv, FDI_TX_CTL(pipe));
9385ca02815Sjsg udelay(100);
9395ca02815Sjsg
9405ca02815Sjsg /* Wait for the clocks to turn off. */
941*f005ef32Sjsg intel_de_rmw(dev_priv, FDI_RX_CTL(pipe), FDI_RX_PLL_ENABLE, 0);
942*f005ef32Sjsg intel_de_posting_read(dev_priv, FDI_RX_CTL(pipe));
9435ca02815Sjsg udelay(100);
9445ca02815Sjsg }
9455ca02815Sjsg
ilk_fdi_disable(struct intel_crtc * crtc)9465ca02815Sjsg void ilk_fdi_disable(struct intel_crtc *crtc)
9475ca02815Sjsg {
9485ca02815Sjsg struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
9495ca02815Sjsg enum pipe pipe = crtc->pipe;
9505ca02815Sjsg i915_reg_t reg;
9515ca02815Sjsg u32 temp;
9525ca02815Sjsg
9535ca02815Sjsg /* disable CPU FDI tx and PCH FDI rx */
954*f005ef32Sjsg intel_de_rmw(dev_priv, FDI_TX_CTL(pipe), FDI_TX_ENABLE, 0);
955*f005ef32Sjsg intel_de_posting_read(dev_priv, FDI_TX_CTL(pipe));
9565ca02815Sjsg
9575ca02815Sjsg reg = FDI_RX_CTL(pipe);
9585ca02815Sjsg temp = intel_de_read(dev_priv, reg);
9595ca02815Sjsg temp &= ~(0x7 << 16);
960*f005ef32Sjsg temp |= (intel_de_read(dev_priv, TRANSCONF(pipe)) & TRANSCONF_BPC_MASK) << 11;
9615ca02815Sjsg intel_de_write(dev_priv, reg, temp & ~FDI_RX_ENABLE);
9625ca02815Sjsg
9635ca02815Sjsg intel_de_posting_read(dev_priv, reg);
9645ca02815Sjsg udelay(100);
9655ca02815Sjsg
9665ca02815Sjsg /* Ironlake workaround, disable clock pointer after downing FDI */
9675ca02815Sjsg if (HAS_PCH_IBX(dev_priv))
9685ca02815Sjsg intel_de_write(dev_priv, FDI_RX_CHICKEN(pipe),
9695ca02815Sjsg FDI_RX_PHASE_SYNC_POINTER_OVR);
9705ca02815Sjsg
9715ca02815Sjsg /* still set train pattern 1 */
972*f005ef32Sjsg intel_de_rmw(dev_priv, FDI_TX_CTL(pipe),
973*f005ef32Sjsg FDI_LINK_TRAIN_NONE, FDI_LINK_TRAIN_PATTERN_1);
9745ca02815Sjsg
9755ca02815Sjsg reg = FDI_RX_CTL(pipe);
9765ca02815Sjsg temp = intel_de_read(dev_priv, reg);
9775ca02815Sjsg if (HAS_PCH_CPT(dev_priv)) {
9785ca02815Sjsg temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
9795ca02815Sjsg temp |= FDI_LINK_TRAIN_PATTERN_1_CPT;
9805ca02815Sjsg } else {
9815ca02815Sjsg temp &= ~FDI_LINK_TRAIN_NONE;
9825ca02815Sjsg temp |= FDI_LINK_TRAIN_PATTERN_1;
9835ca02815Sjsg }
984*f005ef32Sjsg /* BPC in FDI rx is consistent with that in TRANSCONF */
9855ca02815Sjsg temp &= ~(0x07 << 16);
986*f005ef32Sjsg temp |= (intel_de_read(dev_priv, TRANSCONF(pipe)) & TRANSCONF_BPC_MASK) << 11;
9875ca02815Sjsg intel_de_write(dev_priv, reg, temp);
9885ca02815Sjsg
9895ca02815Sjsg intel_de_posting_read(dev_priv, reg);
9905ca02815Sjsg udelay(100);
9915ca02815Sjsg }
9925ca02815Sjsg
9931bb76ff1Sjsg static const struct intel_fdi_funcs ilk_funcs = {
9941bb76ff1Sjsg .fdi_link_train = ilk_fdi_link_train,
9951bb76ff1Sjsg };
9961bb76ff1Sjsg
9971bb76ff1Sjsg static const struct intel_fdi_funcs gen6_funcs = {
9981bb76ff1Sjsg .fdi_link_train = gen6_fdi_link_train,
9991bb76ff1Sjsg };
10001bb76ff1Sjsg
10011bb76ff1Sjsg static const struct intel_fdi_funcs ivb_funcs = {
10021bb76ff1Sjsg .fdi_link_train = ivb_manual_fdi_link_train,
10031bb76ff1Sjsg };
10041bb76ff1Sjsg
10055ca02815Sjsg void
intel_fdi_init_hook(struct drm_i915_private * dev_priv)10065ca02815Sjsg intel_fdi_init_hook(struct drm_i915_private *dev_priv)
10075ca02815Sjsg {
10085ca02815Sjsg if (IS_IRONLAKE(dev_priv)) {
10091bb76ff1Sjsg dev_priv->display.funcs.fdi = &ilk_funcs;
10105ca02815Sjsg } else if (IS_SANDYBRIDGE(dev_priv)) {
10111bb76ff1Sjsg dev_priv->display.funcs.fdi = &gen6_funcs;
10125ca02815Sjsg } else if (IS_IVYBRIDGE(dev_priv)) {
10135ca02815Sjsg /* FIXME: detect B0+ stepping and use auto training */
10141bb76ff1Sjsg dev_priv->display.funcs.fdi = &ivb_funcs;
10155ca02815Sjsg }
10165ca02815Sjsg }
1017