diff options
Diffstat (limited to 'sys/dev/drm2/i915/intel_ddi.c')
-rw-r--r-- | sys/dev/drm2/i915/intel_ddi.c | 1140 |
1 files changed, 954 insertions, 186 deletions
diff --git a/sys/dev/drm2/i915/intel_ddi.c b/sys/dev/drm2/i915/intel_ddi.c index 52bcc1e38c7d..1dd4a359ea31 100644 --- a/sys/dev/drm2/i915/intel_ddi.c +++ b/sys/dev/drm2/i915/intel_ddi.c @@ -29,8 +29,6 @@ __FBSDID("$FreeBSD$"); #include <dev/drm2/drmP.h> -#include <dev/drm2/drm.h> -#include <dev/drm2/i915/i915_drm.h> #include <dev/drm2/i915/i915_drv.h> #include <dev/drm2/i915/intel_drv.h> @@ -64,6 +62,26 @@ static const u32 hsw_ddi_translations_fdi[] = { 0x00FFFFFF, 0x00040006 /* HDMI parameters */ }; +static enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder) +{ + struct drm_encoder *encoder = &intel_encoder->base; + int type = intel_encoder->type; + + if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP || + type == INTEL_OUTPUT_HDMI || type == INTEL_OUTPUT_UNKNOWN) { + struct intel_digital_port *intel_dig_port = + enc_to_dig_port(encoder); + return intel_dig_port->port; + + } else if (type == INTEL_OUTPUT_ANALOG) { + return PORT_E; + + } else { + DRM_ERROR("Invalid DDI encoder type %d\n", type); + BUG(); + } +} + /* On Haswell, DDI port buffers must be programmed with correct values * in advance. The buffer values are different for FDI and DP modes, * but the HDMI/DVI fields are shared among those. So we program the DDI @@ -83,11 +101,11 @@ static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port, bo port_name(port), use_fdi_mode ? "FDI" : "DP"); - if (use_fdi_mode && (port != PORT_E)) - DRM_DEBUG_KMS("Programming port %c in FDI mode, this probably will not work.\n", + WARN((use_fdi_mode && (port != PORT_E)), + "Programming port %c in FDI mode, this probably will not work.\n", port_name(port)); - for (i=0, reg=DDI_BUF_TRANS(port); i < DRM_ARRAY_SIZE(hsw_ddi_translations_fdi); i++) { + for (i=0, reg=DDI_BUF_TRANS(port); i < ARRAY_SIZE(hsw_ddi_translations_fdi); i++) { I915_WRITE(reg, ddi_translations[i]); reg += 4; } @@ -124,6 +142,19 @@ static const long hsw_ddi_buf_ctl_values[] = { DDI_BUF_EMP_800MV_3_5DB_HSW }; +static void intel_wait_ddi_buf_idle(struct drm_i915_private *dev_priv, + enum port port) +{ + uint32_t reg = DDI_BUF_CTL(port); + int i; + + for (i = 0; i < 8; i++) { + udelay(1); + if (I915_READ(reg) & DDI_BUF_IS_IDLE) + return; + } + DRM_ERROR("Timeout waiting for DDI BUF %c idle bit\n", port_name(port)); +} /* Starting with Haswell, different DDI ports can work in FDI mode for * connection to the PCH-located connectors. For this, it is necessary to train @@ -139,25 +170,34 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int pipe = intel_crtc->pipe; - u32 reg, temp, i; + u32 temp, i, rx_ctl_val; + + /* Set the FDI_RX_MISC pwrdn lanes and the 2 workarounds listed at the + * mode set "sequence for CRT port" document: + * - TP1 to TP2 time with the default value + * - FDI delay to 90h + */ + I915_WRITE(_FDI_RXA_MISC, FDI_RX_PWRDN_LANE1_VAL(2) | + FDI_RX_PWRDN_LANE0_VAL(2) | + FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90); - /* Configure CPU PLL, wait for warmup */ - I915_WRITE(SPLL_CTL, - SPLL_PLL_ENABLE | - SPLL_PLL_FREQ_1350MHz | - SPLL_PLL_SSC); + /* Enable the PCH Receiver FDI PLL */ + rx_ctl_val = dev_priv->fdi_rx_config | FDI_RX_ENHANCE_FRAME_ENABLE | + FDI_RX_PLL_ENABLE | ((intel_crtc->fdi_lanes - 1) << 19); + I915_WRITE(_FDI_RXA_CTL, rx_ctl_val); + POSTING_READ(_FDI_RXA_CTL); + udelay(220); - /* Use SPLL to drive the output when in FDI mode */ - I915_WRITE(PORT_CLK_SEL(PORT_E), - PORT_CLK_SEL_SPLL); - I915_WRITE(PIPE_CLK_SEL(pipe), - PIPE_CLK_SEL_PORT(PORT_E)); + /* Switch from Rawclk to PCDclk */ + rx_ctl_val |= FDI_PCDCLK; + I915_WRITE(_FDI_RXA_CTL, rx_ctl_val); - DELAY(20); + /* Configure Port Clock Select */ + I915_WRITE(PORT_CLK_SEL(PORT_E), intel_crtc->ddi_pll_sel); - /* Start the training iterating through available voltages and emphasis */ - for (i=0; i < DRM_ARRAY_SIZE(hsw_ddi_buf_ctl_values); i++) { + /* Start the training iterating through available voltages and emphasis, + * testing each value twice. */ + for (i = 0; i < ARRAY_SIZE(hsw_ddi_buf_ctl_values) * 2; i++) { /* Configure DP_TP_CTL with auto-training */ I915_WRITE(DP_TP_CTL(PORT_E), DP_TP_CTL_FDI_AUTOTRAIN | @@ -165,95 +205,79 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) DP_TP_CTL_LINK_TRAIN_PAT1 | DP_TP_CTL_ENABLE); - /* Configure and enable DDI_BUF_CTL for DDI E with next voltage */ - temp = I915_READ(DDI_BUF_CTL(PORT_E)); - temp = (temp & ~DDI_BUF_EMP_MASK); + /* Configure and enable DDI_BUF_CTL for DDI E with next voltage. + * DDI E does not support port reversal, the functionality is + * achieved on the PCH side in FDI_RX_CTL, so no need to set the + * port reversal bit */ I915_WRITE(DDI_BUF_CTL(PORT_E), - temp | - DDI_BUF_CTL_ENABLE | - DDI_PORT_WIDTH_X2 | - hsw_ddi_buf_ctl_values[i]); - - DELAY(600); - - /* Enable CPU FDI Receiver with auto-training */ - reg = FDI_RX_CTL(pipe); - I915_WRITE(reg, - I915_READ(reg) | - FDI_LINK_TRAIN_AUTO | - FDI_RX_ENABLE | - FDI_LINK_TRAIN_PATTERN_1_CPT | - FDI_RX_ENHANCE_FRAME_ENABLE | - FDI_PORT_WIDTH_2X_LPT | - FDI_RX_PLL_ENABLE); - POSTING_READ(reg); - DELAY(100); + DDI_BUF_CTL_ENABLE | + ((intel_crtc->fdi_lanes - 1) << 1) | + hsw_ddi_buf_ctl_values[i / 2]); + POSTING_READ(DDI_BUF_CTL(PORT_E)); + + udelay(600); + + /* Program PCH FDI Receiver TU */ + I915_WRITE(_FDI_RXA_TUSIZE1, TU_SIZE(64)); + + /* Enable PCH FDI Receiver with auto-training */ + rx_ctl_val |= FDI_RX_ENABLE | FDI_LINK_TRAIN_AUTO; + I915_WRITE(_FDI_RXA_CTL, rx_ctl_val); + POSTING_READ(_FDI_RXA_CTL); + + /* Wait for FDI receiver lane calibration */ + udelay(30); + + /* Unset FDI_RX_MISC pwrdn lanes */ + temp = I915_READ(_FDI_RXA_MISC); + temp &= ~(FDI_RX_PWRDN_LANE1_MASK | FDI_RX_PWRDN_LANE0_MASK); + I915_WRITE(_FDI_RXA_MISC, temp); + POSTING_READ(_FDI_RXA_MISC); + + /* Wait for FDI auto training time */ + udelay(5); temp = I915_READ(DP_TP_STATUS(PORT_E)); if (temp & DP_TP_STATUS_AUTOTRAIN_DONE) { - DRM_DEBUG_DRIVER("BUF_CTL training done on %d step\n", i); + DRM_DEBUG_KMS("FDI link training done on step %d\n", i); /* Enable normal pixel sending for FDI */ I915_WRITE(DP_TP_CTL(PORT_E), - DP_TP_CTL_FDI_AUTOTRAIN | - DP_TP_CTL_LINK_TRAIN_NORMAL | - DP_TP_CTL_ENHANCED_FRAME_ENABLE | - DP_TP_CTL_ENABLE); - - /* Enable PIPE_TRANS_DDI_FUNC_CTL for the pipe to work in FDI mode */ - temp = I915_READ(TRANS_DDI_FUNC_CTL(pipe)); - temp &= ~TRANS_DDI_PORT_MASK; - temp |= TRANS_DDI_SELECT_PORT(PORT_E) | - TRANS_DDI_MODE_SELECT_FDI | - TRANS_DDI_FUNC_ENABLE | - TRANS_DDI_PORT_WIDTH_X2; - I915_WRITE(TRANS_DDI_FUNC_CTL(pipe), - temp); - break; - } else { - DRM_ERROR("Error training BUF_CTL %d\n", i); + DP_TP_CTL_FDI_AUTOTRAIN | + DP_TP_CTL_LINK_TRAIN_NORMAL | + DP_TP_CTL_ENHANCED_FRAME_ENABLE | + DP_TP_CTL_ENABLE); - /* Disable DP_TP_CTL and FDI_RX_CTL) and retry */ - I915_WRITE(DP_TP_CTL(PORT_E), - I915_READ(DP_TP_CTL(PORT_E)) & - ~DP_TP_CTL_ENABLE); - I915_WRITE(FDI_RX_CTL(pipe), - I915_READ(FDI_RX_CTL(pipe)) & - ~FDI_RX_PLL_ENABLE); - continue; + return; } - } - DRM_DEBUG_KMS("FDI train done.\n"); -} + temp = I915_READ(DDI_BUF_CTL(PORT_E)); + temp &= ~DDI_BUF_CTL_ENABLE; + I915_WRITE(DDI_BUF_CTL(PORT_E), temp); + POSTING_READ(DDI_BUF_CTL(PORT_E)); -/* For DDI connections, it is possible to support different outputs over the - * same DDI port, such as HDMI or DP or even VGA via FDI. So we don't know by - * the time the output is detected what exactly is on the other end of it. This - * function aims at providing support for this detection and proper output - * configuration. - */ -void intel_ddi_init(struct drm_device *dev, enum port port) -{ - /* For now, we don't do any proper output detection and assume that we - * handle HDMI only */ + /* Disable DP_TP_CTL and FDI_RX_CTL and retry */ + temp = I915_READ(DP_TP_CTL(PORT_E)); + temp &= ~(DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_MASK); + temp |= DP_TP_CTL_LINK_TRAIN_PAT1; + I915_WRITE(DP_TP_CTL(PORT_E), temp); + POSTING_READ(DP_TP_CTL(PORT_E)); - switch(port){ - case PORT_A: - /* We don't handle eDP and DP yet */ - DRM_DEBUG_DRIVER("Found digital output on DDI port A\n"); - break; - /* Assume that the ports B, C and D are working in HDMI mode for now */ - case PORT_B: - case PORT_C: - case PORT_D: - intel_hdmi_init(dev, DDI_BUF_CTL(port)); - break; - default: - DRM_DEBUG_DRIVER("No handlers defined for port %d, skipping DDI initialization\n", - port); - break; + intel_wait_ddi_buf_idle(dev_priv, PORT_E); + + rx_ctl_val &= ~FDI_RX_ENABLE; + I915_WRITE(_FDI_RXA_CTL, rx_ctl_val); + POSTING_READ(_FDI_RXA_CTL); + + /* Reset FDI_RX_MISC pwrdn lanes */ + temp = I915_READ(_FDI_RXA_MISC); + temp &= ~(FDI_RX_PWRDN_LANE1_MASK | FDI_RX_PWRDN_LANE0_MASK); + temp |= FDI_RX_PWRDN_LANE1_VAL(2) | FDI_RX_PWRDN_LANE0_VAL(2); + I915_WRITE(_FDI_RXA_MISC, temp); + POSTING_READ(_FDI_RXA_MISC); } + + DRM_ERROR("FDI link training failed!\n"); } /* WRPLL clock dividers */ @@ -264,7 +288,8 @@ struct wrpll_tmds_clock { u16 r2; /* Reference divider */ }; -/* Table of matching values for WRPLL clocks programming for each frequency */ +/* Table of matching values for WRPLL clocks programming for each frequency. + * The code assumes this table is sorted. */ static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = { {19750, 38, 25, 18}, {20000, 48, 32, 18}, @@ -274,7 +299,6 @@ static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = { {23000, 36, 23, 15}, {23500, 40, 40, 23}, {23750, 26, 16, 14}, - {23750, 26, 16, 14}, {24000, 36, 24, 15}, {25000, 36, 25, 15}, {25175, 26, 40, 33}, @@ -434,7 +458,6 @@ static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = { {108000, 8, 24, 15}, {108108, 8, 173, 108}, {109000, 6, 23, 19}, - {109000, 6, 23, 19}, {110000, 6, 22, 18}, {110013, 6, 22, 18}, {110250, 8, 49, 30}, @@ -611,7 +634,6 @@ static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = { {218250, 4, 42, 26}, {218750, 4, 34, 21}, {219000, 4, 47, 29}, - {219000, 4, 47, 29}, {220000, 4, 44, 27}, {220640, 4, 49, 30}, {220750, 4, 36, 22}, @@ -644,118 +666,864 @@ static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = { {298000, 2, 21, 19}, }; -void intel_ddi_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static void intel_ddi_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) { - struct drm_device *dev = encoder->dev; - struct drm_i915_private *dev_priv = dev->dev_private; struct drm_crtc *crtc = encoder->crtc; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); - int port = intel_hdmi->ddi_port; + struct intel_encoder *intel_encoder = to_intel_encoder(encoder); + int port = intel_ddi_get_encoder_port(intel_encoder); int pipe = intel_crtc->pipe; - int p, n2, r2, valid=0; - u32 temp, i; + int type = intel_encoder->type; - /* On Haswell, we need to enable the clocks and prepare DDI function to - * work in HDMI mode for this pipe. - */ - DRM_DEBUG_KMS("Preparing HDMI DDI mode for Haswell on port %c, pipe %c\n", port_name(port), pipe_name(pipe)); + DRM_DEBUG_KMS("Preparing DDI mode for Haswell on port %c, pipe %c\n", + port_name(port), pipe_name(pipe)); - for (i=0; i < DRM_ARRAY_SIZE(wrpll_tmds_clock_table); i++) { - if (crtc->mode.clock == wrpll_tmds_clock_table[i].clock) { - p = wrpll_tmds_clock_table[i].p; - n2 = wrpll_tmds_clock_table[i].n2; - r2 = wrpll_tmds_clock_table[i].r2; + if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + struct intel_digital_port *intel_dig_port = + enc_to_dig_port(encoder); - DRM_DEBUG_KMS("WR PLL clock: found settings for %dKHz refresh rate: p=%d, n2=%d, r2=%d\n", - crtc->mode.clock, - p, n2, r2); + intel_dp->DP = intel_dig_port->port_reversal | + DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW; + switch (intel_dp->lane_count) { + case 1: + intel_dp->DP |= DDI_PORT_WIDTH_X1; + break; + case 2: + intel_dp->DP |= DDI_PORT_WIDTH_X2; + break; + case 4: + intel_dp->DP |= DDI_PORT_WIDTH_X4; + break; + default: + intel_dp->DP |= DDI_PORT_WIDTH_X4; + WARN(1, "Unexpected DP lane count %d\n", + intel_dp->lane_count); + break; + } - valid = 1; + if (intel_dp->has_audio) { + DRM_DEBUG_DRIVER("DP audio on pipe %c on DDI\n", + pipe_name(intel_crtc->pipe)); + + /* write eld */ + DRM_DEBUG_DRIVER("DP audio: write eld information\n"); + intel_write_eld(encoder, adjusted_mode); + } + + intel_dp_init_link_config(intel_dp); + + } else if (type == INTEL_OUTPUT_HDMI) { + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + + if (intel_hdmi->has_audio) { + /* Proper support for digital audio needs a new logic + * and a new set of registers, so we leave it for future + * patch bombing. + */ + DRM_DEBUG_DRIVER("HDMI audio on pipe %c on DDI\n", + pipe_name(intel_crtc->pipe)); + + /* write eld */ + DRM_DEBUG_DRIVER("HDMI audio: write eld information\n"); + intel_write_eld(encoder, adjusted_mode); + } + + intel_hdmi->set_infoframes(encoder, adjusted_mode); + } +} + +static struct intel_encoder * +intel_ddi_get_crtc_encoder(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_encoder *intel_encoder, *ret = NULL; + int num_encoders = 0; + + for_each_encoder_on_crtc(dev, crtc, intel_encoder) { + ret = intel_encoder; + num_encoders++; + } + + if (num_encoders != 1) + WARN(1, "%d encoders on crtc for pipe %d\n", num_encoders, + intel_crtc->pipe); + + BUG_ON(ret == NULL); + return ret; +} + +void intel_ddi_put_crtc_pll(struct drm_crtc *crtc) +{ + struct drm_i915_private *dev_priv = crtc->dev->dev_private; + struct intel_ddi_plls *plls = &dev_priv->ddi_plls; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + uint32_t val; + + switch (intel_crtc->ddi_pll_sel) { + case PORT_CLK_SEL_SPLL: + plls->spll_refcount--; + if (plls->spll_refcount == 0) { + DRM_DEBUG_KMS("Disabling SPLL\n"); + val = I915_READ(SPLL_CTL); + WARN_ON(!(val & SPLL_PLL_ENABLE)); + I915_WRITE(SPLL_CTL, val & ~SPLL_PLL_ENABLE); + POSTING_READ(SPLL_CTL); + } + break; + case PORT_CLK_SEL_WRPLL1: + plls->wrpll1_refcount--; + if (plls->wrpll1_refcount == 0) { + DRM_DEBUG_KMS("Disabling WRPLL 1\n"); + val = I915_READ(WRPLL_CTL1); + WARN_ON(!(val & WRPLL_PLL_ENABLE)); + I915_WRITE(WRPLL_CTL1, val & ~WRPLL_PLL_ENABLE); + POSTING_READ(WRPLL_CTL1); + } + break; + case PORT_CLK_SEL_WRPLL2: + plls->wrpll2_refcount--; + if (plls->wrpll2_refcount == 0) { + DRM_DEBUG_KMS("Disabling WRPLL 2\n"); + val = I915_READ(WRPLL_CTL2); + WARN_ON(!(val & WRPLL_PLL_ENABLE)); + I915_WRITE(WRPLL_CTL2, val & ~WRPLL_PLL_ENABLE); + POSTING_READ(WRPLL_CTL2); + } + break; + } + + WARN(plls->spll_refcount < 0, "Invalid SPLL refcount\n"); + WARN(plls->wrpll1_refcount < 0, "Invalid WRPLL1 refcount\n"); + WARN(plls->wrpll2_refcount < 0, "Invalid WRPLL2 refcount\n"); + + intel_crtc->ddi_pll_sel = PORT_CLK_SEL_NONE; +} + +static void intel_ddi_calculate_wrpll(int clock, int *p, int *n2, int *r2) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(wrpll_tmds_clock_table); i++) + if (clock <= wrpll_tmds_clock_table[i].clock) + break; + + if (i == ARRAY_SIZE(wrpll_tmds_clock_table)) + i--; + + *p = wrpll_tmds_clock_table[i].p; + *n2 = wrpll_tmds_clock_table[i].n2; + *r2 = wrpll_tmds_clock_table[i].r2; + + if (wrpll_tmds_clock_table[i].clock != clock) + DRM_INFO("WRPLL: using settings for %dKHz on %dKHz mode\n", + wrpll_tmds_clock_table[i].clock, clock); + + DRM_DEBUG_KMS("WRPLL: %dKHz refresh rate with p=%d, n2=%d r2=%d\n", + clock, *p, *n2, *r2); +} + +bool intel_ddi_pll_mode_set(struct drm_crtc *crtc, int clock) +{ + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc); + struct drm_encoder *encoder = &intel_encoder->base; + struct drm_i915_private *dev_priv = crtc->dev->dev_private; + struct intel_ddi_plls *plls = &dev_priv->ddi_plls; + int type = intel_encoder->type; + enum pipe pipe = intel_crtc->pipe; + uint32_t reg, val; + + /* TODO: reuse PLLs when possible (compare values) */ + + intel_ddi_put_crtc_pll(crtc); + + if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + + switch (intel_dp->link_bw) { + case DP_LINK_BW_1_62: + intel_crtc->ddi_pll_sel = PORT_CLK_SEL_LCPLL_810; + break; + case DP_LINK_BW_2_7: + intel_crtc->ddi_pll_sel = PORT_CLK_SEL_LCPLL_1350; + break; + case DP_LINK_BW_5_4: + intel_crtc->ddi_pll_sel = PORT_CLK_SEL_LCPLL_2700; break; + default: + DRM_ERROR("Link bandwidth %d unsupported\n", + intel_dp->link_bw); + return false; } + + /* We don't need to turn any PLL on because we'll use LCPLL. */ + return true; + + } else if (type == INTEL_OUTPUT_HDMI) { + int p, n2, r2; + + if (plls->wrpll1_refcount == 0) { + DRM_DEBUG_KMS("Using WRPLL 1 on pipe %c\n", + pipe_name(pipe)); + plls->wrpll1_refcount++; + reg = WRPLL_CTL1; + intel_crtc->ddi_pll_sel = PORT_CLK_SEL_WRPLL1; + } else if (plls->wrpll2_refcount == 0) { + DRM_DEBUG_KMS("Using WRPLL 2 on pipe %c\n", + pipe_name(pipe)); + plls->wrpll2_refcount++; + reg = WRPLL_CTL2; + intel_crtc->ddi_pll_sel = PORT_CLK_SEL_WRPLL2; + } else { + DRM_ERROR("No WRPLLs available!\n"); + return false; + } + + WARN(I915_READ(reg) & WRPLL_PLL_ENABLE, + "WRPLL already enabled\n"); + + intel_ddi_calculate_wrpll(clock, &p, &n2, &r2); + + val = WRPLL_PLL_ENABLE | WRPLL_PLL_SELECT_LCPLL_2700 | + WRPLL_DIVIDER_REFERENCE(r2) | WRPLL_DIVIDER_FEEDBACK(n2) | + WRPLL_DIVIDER_POST(p); + + } else if (type == INTEL_OUTPUT_ANALOG) { + if (plls->spll_refcount == 0) { + DRM_DEBUG_KMS("Using SPLL on pipe %c\n", + pipe_name(pipe)); + plls->spll_refcount++; + intel_crtc->ddi_pll_sel = PORT_CLK_SEL_SPLL; + } + reg = SPLL_CTL; + + WARN(I915_READ(reg) & SPLL_PLL_ENABLE, + "SPLL already enabled\n"); + + val = SPLL_PLL_ENABLE | SPLL_PLL_FREQ_1350MHz | SPLL_PLL_SSC; + + } else { + WARN(1, "Invalid DDI encoder type %d\n", type); + return false; } - if (!valid) { - DRM_ERROR("Unable to find WR PLL clock settings for %dKHz refresh rate\n", - crtc->mode.clock); - return; + I915_WRITE(reg, val); + udelay(20); + + return true; +} + +void intel_ddi_set_pipe_settings(struct drm_crtc *crtc) +{ + struct drm_i915_private *dev_priv = crtc->dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc); + enum transcoder cpu_transcoder = intel_crtc->cpu_transcoder; + int type = intel_encoder->type; + uint32_t temp; + + if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) { + + temp = TRANS_MSA_SYNC_CLK; + switch (intel_crtc->bpp) { + case 18: + temp |= TRANS_MSA_6_BPC; + break; + case 24: + temp |= TRANS_MSA_8_BPC; + break; + case 30: + temp |= TRANS_MSA_10_BPC; + break; + case 36: + temp |= TRANS_MSA_12_BPC; + break; + default: + temp |= TRANS_MSA_8_BPC; + WARN(1, "%d bpp unsupported by DDI function\n", + intel_crtc->bpp); + } + I915_WRITE(TRANS_MSA_MISC(cpu_transcoder), temp); } +} - /* Enable LCPLL if disabled */ - temp = I915_READ(LCPLL_CTL); - if (temp & LCPLL_PLL_DISABLE) - I915_WRITE(LCPLL_CTL, - temp & ~LCPLL_PLL_DISABLE); - - /* Configure WR PLL 1, program the correct divider values for - * the desired frequency and wait for warmup */ - I915_WRITE(WRPLL_CTL1, - WRPLL_PLL_ENABLE | - WRPLL_PLL_SELECT_LCPLL_2700 | - WRPLL_DIVIDER_REFERENCE(r2) | - WRPLL_DIVIDER_FEEDBACK(n2) | - WRPLL_DIVIDER_POST(p)); - - DELAY(20); - - /* Use WRPLL1 clock to drive the output to the port, and tell the pipe to use - * this port for connection. - */ - I915_WRITE(PORT_CLK_SEL(port), - PORT_CLK_SEL_WRPLL1); - I915_WRITE(PIPE_CLK_SEL(pipe), - PIPE_CLK_SEL_PORT(port)); +void intel_ddi_enable_pipe_func(struct drm_crtc *crtc) +{ + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc); + struct drm_encoder *encoder = &intel_encoder->base; + struct drm_i915_private *dev_priv = crtc->dev->dev_private; + enum pipe pipe = intel_crtc->pipe; + enum transcoder cpu_transcoder = intel_crtc->cpu_transcoder; + enum port port = intel_ddi_get_encoder_port(intel_encoder); + int type = intel_encoder->type; + uint32_t temp; - DELAY(20); + /* Enable TRANS_DDI_FUNC_CTL for the pipe to work in HDMI mode */ + temp = TRANS_DDI_FUNC_ENABLE; + temp |= TRANS_DDI_SELECT_PORT(port); - if (intel_hdmi->has_audio) { - /* Proper support for digital audio needs a new logic and a new set - * of registers, so we leave it for future patch bombing. - */ - DRM_DEBUG_DRIVER("HDMI audio on pipe %c not yet supported on DDI\n", - pipe_name(intel_crtc->pipe)); + switch (intel_crtc->bpp) { + case 18: + temp |= TRANS_DDI_BPC_6; + break; + case 24: + temp |= TRANS_DDI_BPC_8; + break; + case 30: + temp |= TRANS_DDI_BPC_10; + break; + case 36: + temp |= TRANS_DDI_BPC_12; + break; + default: + WARN(1, "%d bpp unsupported by transcoder DDI function\n", + intel_crtc->bpp); + } + + if (crtc->mode.flags & DRM_MODE_FLAG_PVSYNC) + temp |= TRANS_DDI_PVSYNC; + if (crtc->mode.flags & DRM_MODE_FLAG_PHSYNC) + temp |= TRANS_DDI_PHSYNC; + + if (cpu_transcoder == TRANSCODER_EDP) { + switch (pipe) { + case PIPE_A: + temp |= TRANS_DDI_EDP_INPUT_A_ONOFF; + break; + case PIPE_B: + temp |= TRANS_DDI_EDP_INPUT_B_ONOFF; + break; + case PIPE_C: + temp |= TRANS_DDI_EDP_INPUT_C_ONOFF; + break; + default: + BUG(); + break; + } + } + + if (type == INTEL_OUTPUT_HDMI) { + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + + if (intel_hdmi->has_hdmi_sink) + temp |= TRANS_DDI_MODE_SELECT_HDMI; + else + temp |= TRANS_DDI_MODE_SELECT_DVI; + + } else if (type == INTEL_OUTPUT_ANALOG) { + temp |= TRANS_DDI_MODE_SELECT_FDI; + temp |= (intel_crtc->fdi_lanes - 1) << 1; + + } else if (type == INTEL_OUTPUT_DISPLAYPORT || + type == INTEL_OUTPUT_EDP) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + + temp |= TRANS_DDI_MODE_SELECT_DP_SST; + + switch (intel_dp->lane_count) { + case 1: + temp |= TRANS_DDI_PORT_WIDTH_X1; + break; + case 2: + temp |= TRANS_DDI_PORT_WIDTH_X2; + break; + case 4: + temp |= TRANS_DDI_PORT_WIDTH_X4; + break; + default: + temp |= TRANS_DDI_PORT_WIDTH_X4; + WARN(1, "Unsupported lane count %d\n", + intel_dp->lane_count); + } + + } else { + WARN(1, "Invalid encoder type %d for pipe %d\n", + intel_encoder->type, pipe); } - /* Enable PIPE_TRANS_DDI_FUNC_CTL for the pipe to work in HDMI mode */ - temp = I915_READ(TRANS_DDI_FUNC_CTL(pipe)); - temp &= ~TRANS_DDI_PORT_MASK; - temp &= ~TRANS_DDI_BPC_12; - temp |= TRANS_DDI_SELECT_PORT(port) | - TRANS_DDI_MODE_SELECT_HDMI | - ((intel_crtc->bpp > 24) ? - TRANS_DDI_BPC_12 : - TRANS_DDI_BPC_8) | - TRANS_DDI_FUNC_ENABLE; - - I915_WRITE(TRANS_DDI_FUNC_CTL(pipe), temp); - - intel_hdmi_set_avi_infoframe(encoder, adjusted_mode); - intel_hdmi_set_spd_infoframe(encoder); + I915_WRITE(TRANS_DDI_FUNC_CTL(cpu_transcoder), temp); } -void intel_ddi_dpms(struct drm_encoder *encoder, int mode) +void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv, + enum transcoder cpu_transcoder) { - struct drm_device *dev = encoder->dev; + uint32_t reg = TRANS_DDI_FUNC_CTL(cpu_transcoder); + uint32_t val = I915_READ(reg); + + val &= ~(TRANS_DDI_FUNC_ENABLE | TRANS_DDI_PORT_MASK); + val |= TRANS_DDI_PORT_NONE; + I915_WRITE(reg, val); +} + +bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector) +{ + struct drm_device *dev = intel_connector->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); - int port = intel_hdmi->ddi_port; - u32 temp; + struct intel_encoder *intel_encoder = intel_connector->encoder; + int type = intel_connector->base.connector_type; + enum port port = intel_ddi_get_encoder_port(intel_encoder); + enum pipe pipe = 0; + enum transcoder cpu_transcoder; + uint32_t tmp; - temp = I915_READ(DDI_BUF_CTL(port)); + if (!intel_encoder->get_hw_state(intel_encoder, &pipe)) + return false; - if (mode != DRM_MODE_DPMS_ON) { - temp &= ~DDI_BUF_CTL_ENABLE; + if (port == PORT_A) + cpu_transcoder = TRANSCODER_EDP; + else + cpu_transcoder = (enum transcoder)pipe; + + tmp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder)); + + switch (tmp & TRANS_DDI_MODE_SELECT_MASK) { + case TRANS_DDI_MODE_SELECT_HDMI: + case TRANS_DDI_MODE_SELECT_DVI: + return (type == DRM_MODE_CONNECTOR_HDMIA); + + case TRANS_DDI_MODE_SELECT_DP_SST: + if (type == DRM_MODE_CONNECTOR_eDP) + return true; + case TRANS_DDI_MODE_SELECT_DP_MST: + return (type == DRM_MODE_CONNECTOR_DisplayPort); + + case TRANS_DDI_MODE_SELECT_FDI: + return (type == DRM_MODE_CONNECTOR_VGA); + + default: + return false; + } +} + +bool intel_ddi_get_hw_state(struct intel_encoder *encoder, + enum pipe *pipe) +{ + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + enum port port = intel_ddi_get_encoder_port(encoder); + u32 tmp; + int i; + + tmp = I915_READ(DDI_BUF_CTL(port)); + + if (!(tmp & DDI_BUF_CTL_ENABLE)) + return false; + + if (port == PORT_A) { + tmp = I915_READ(TRANS_DDI_FUNC_CTL(TRANSCODER_EDP)); + + switch (tmp & TRANS_DDI_EDP_INPUT_MASK) { + case TRANS_DDI_EDP_INPUT_A_ON: + case TRANS_DDI_EDP_INPUT_A_ONOFF: + *pipe = PIPE_A; + break; + case TRANS_DDI_EDP_INPUT_B_ONOFF: + *pipe = PIPE_B; + break; + case TRANS_DDI_EDP_INPUT_C_ONOFF: + *pipe = PIPE_C; + break; + } + + return true; + } else { + for (i = TRANSCODER_A; i <= TRANSCODER_C; i++) { + tmp = I915_READ(TRANS_DDI_FUNC_CTL(i)); + + if ((tmp & TRANS_DDI_PORT_MASK) + == TRANS_DDI_SELECT_PORT(port)) { + *pipe = i; + return true; + } + } + } + + DRM_DEBUG_KMS("No pipe for ddi port %i found\n", port); + + return true; +} + +static uint32_t intel_ddi_get_crtc_pll(struct drm_i915_private *dev_priv, + enum pipe pipe) +{ + uint32_t temp, ret; + enum port port; + enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, + pipe); + int i; + + if (cpu_transcoder == TRANSCODER_EDP) { + port = PORT_A; } else { - temp |= DDI_BUF_CTL_ENABLE; + temp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder)); + temp &= TRANS_DDI_PORT_MASK; + + for (i = PORT_B; i <= PORT_E; i++) + if (temp == TRANS_DDI_SELECT_PORT(i)) + port = i; + } + + ret = I915_READ(PORT_CLK_SEL(port)); + + DRM_DEBUG_KMS("Pipe %c connected to port %c using clock 0x%08x\n", + pipe_name(pipe), port_name(port), ret); + + return ret; +} + +void intel_ddi_setup_hw_pll_state(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + enum pipe pipe; + struct intel_crtc *intel_crtc; + + for_each_pipe(pipe) { + intel_crtc = + to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); + + if (!intel_crtc->active) + continue; + + intel_crtc->ddi_pll_sel = intel_ddi_get_crtc_pll(dev_priv, + pipe); + + switch (intel_crtc->ddi_pll_sel) { + case PORT_CLK_SEL_SPLL: + dev_priv->ddi_plls.spll_refcount++; + break; + case PORT_CLK_SEL_WRPLL1: + dev_priv->ddi_plls.wrpll1_refcount++; + break; + case PORT_CLK_SEL_WRPLL2: + dev_priv->ddi_plls.wrpll2_refcount++; + break; + } + } +} + +void intel_ddi_enable_pipe_clock(struct intel_crtc *intel_crtc) +{ + struct drm_crtc *crtc = &intel_crtc->base; + struct drm_i915_private *dev_priv = crtc->dev->dev_private; + struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc); + enum port port = intel_ddi_get_encoder_port(intel_encoder); + enum transcoder cpu_transcoder = intel_crtc->cpu_transcoder; + + if (cpu_transcoder != TRANSCODER_EDP) + I915_WRITE(TRANS_CLK_SEL(cpu_transcoder), + TRANS_CLK_SEL_PORT(port)); +} + +void intel_ddi_disable_pipe_clock(struct intel_crtc *intel_crtc) +{ + struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private; + enum transcoder cpu_transcoder = intel_crtc->cpu_transcoder; + + if (cpu_transcoder != TRANSCODER_EDP) + I915_WRITE(TRANS_CLK_SEL(cpu_transcoder), + TRANS_CLK_SEL_DISABLED); +} + +static void intel_ddi_pre_enable(struct intel_encoder *intel_encoder) +{ + struct drm_encoder *encoder = &intel_encoder->base; + struct drm_crtc *crtc = encoder->crtc; + struct drm_i915_private *dev_priv = encoder->dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + enum port port = intel_ddi_get_encoder_port(intel_encoder); + int type = intel_encoder->type; + + if (type == INTEL_OUTPUT_EDP) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + ironlake_edp_panel_vdd_on(intel_dp); + ironlake_edp_panel_on(intel_dp); + ironlake_edp_panel_vdd_off(intel_dp, true); + } + + WARN_ON(intel_crtc->ddi_pll_sel == PORT_CLK_SEL_NONE); + I915_WRITE(PORT_CLK_SEL(port), intel_crtc->ddi_pll_sel); + + if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + + intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); + intel_dp_start_link_train(intel_dp); + intel_dp_complete_link_train(intel_dp); + } +} + +static void intel_ddi_post_disable(struct intel_encoder *intel_encoder) +{ + struct drm_encoder *encoder = &intel_encoder->base; + struct drm_i915_private *dev_priv = encoder->dev->dev_private; + enum port port = intel_ddi_get_encoder_port(intel_encoder); + int type = intel_encoder->type; + uint32_t val; + bool wait = false; + + val = I915_READ(DDI_BUF_CTL(port)); + if (val & DDI_BUF_CTL_ENABLE) { + val &= ~DDI_BUF_CTL_ENABLE; + I915_WRITE(DDI_BUF_CTL(port), val); + wait = true; + } + + val = I915_READ(DP_TP_CTL(port)); + val &= ~(DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_MASK); + val |= DP_TP_CTL_LINK_TRAIN_PAT1; + I915_WRITE(DP_TP_CTL(port), val); + + if (wait) + intel_wait_ddi_buf_idle(dev_priv, port); + + if (type == INTEL_OUTPUT_EDP) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + ironlake_edp_panel_vdd_on(intel_dp); + ironlake_edp_panel_off(intel_dp); } - /* Enable DDI_BUF_CTL. In HDMI/DVI mode, the port width, - * and swing/emphasis values are ignored so nothing special needs - * to be done besides enabling the port. + I915_WRITE(PORT_CLK_SEL(port), PORT_CLK_SEL_NONE); +} + +static void intel_enable_ddi(struct intel_encoder *intel_encoder) +{ + struct drm_encoder *encoder = &intel_encoder->base; + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + enum port port = intel_ddi_get_encoder_port(intel_encoder); + int type = intel_encoder->type; + + if (type == INTEL_OUTPUT_HDMI) { + struct intel_digital_port *intel_dig_port = + enc_to_dig_port(encoder); + + /* In HDMI/DVI mode, the port width, and swing/emphasis values + * are ignored so nothing special needs to be done besides + * enabling the port. + */ + I915_WRITE(DDI_BUF_CTL(port), + intel_dig_port->port_reversal | DDI_BUF_CTL_ENABLE); + } else if (type == INTEL_OUTPUT_EDP) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + + ironlake_edp_backlight_on(intel_dp); + } +} + +static void intel_disable_ddi(struct intel_encoder *intel_encoder) +{ + struct drm_encoder *encoder = &intel_encoder->base; + int type = intel_encoder->type; + + if (type == INTEL_OUTPUT_EDP) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + + ironlake_edp_backlight_off(intel_dp); + } +} + +int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv) +{ + if (I915_READ(HSW_FUSE_STRAP) & HSW_CDCLK_LIMIT) + return 450; + else if ((I915_READ(LCPLL_CTL) & LCPLL_CLK_FREQ_MASK) == + LCPLL_CLK_FREQ_450) + return 450; + else if (IS_ULT(dev_priv->dev)) + return 338; + else + return 540; +} + +void intel_ddi_pll_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t val = I915_READ(LCPLL_CTL); + + /* The LCPLL register should be turned on by the BIOS. For now let's + * just check its state and print errors in case something is wrong. + * Don't even try to turn it on. */ - I915_WRITE(DDI_BUF_CTL(port), - temp); + + DRM_DEBUG_KMS("CDCLK running at %dMHz\n", + intel_ddi_get_cdclk_freq(dev_priv)); + + if (val & LCPLL_CD_SOURCE_FCLK) + DRM_ERROR("CDCLK source is not LCPLL\n"); + + if (val & LCPLL_PLL_DISABLE) + DRM_ERROR("LCPLL is disabled\n"); +} + +void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder) +{ + struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); + struct intel_dp *intel_dp = &intel_dig_port->dp; + struct drm_i915_private *dev_priv = encoder->dev->dev_private; + enum port port = intel_dig_port->port; + bool wait = false; + uint32_t val; + + if (I915_READ(DP_TP_CTL(port)) & DP_TP_CTL_ENABLE) { + val = I915_READ(DDI_BUF_CTL(port)); + if (val & DDI_BUF_CTL_ENABLE) { + val &= ~DDI_BUF_CTL_ENABLE; + I915_WRITE(DDI_BUF_CTL(port), val); + wait = true; + } + + val = I915_READ(DP_TP_CTL(port)); + val &= ~(DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_MASK); + val |= DP_TP_CTL_LINK_TRAIN_PAT1; + I915_WRITE(DP_TP_CTL(port), val); + POSTING_READ(DP_TP_CTL(port)); + + if (wait) + intel_wait_ddi_buf_idle(dev_priv, port); + } + + val = DP_TP_CTL_ENABLE | DP_TP_CTL_MODE_SST | + DP_TP_CTL_LINK_TRAIN_PAT1 | DP_TP_CTL_SCRAMBLE_DISABLE; + if (intel_dp->link_configuration[1] & DP_LANE_COUNT_ENHANCED_FRAME_EN) + val |= DP_TP_CTL_ENHANCED_FRAME_ENABLE; + I915_WRITE(DP_TP_CTL(port), val); + POSTING_READ(DP_TP_CTL(port)); + + intel_dp->DP |= DDI_BUF_CTL_ENABLE; + I915_WRITE(DDI_BUF_CTL(port), intel_dp->DP); + POSTING_READ(DDI_BUF_CTL(port)); + + udelay(600); +} + +void intel_ddi_fdi_disable(struct drm_crtc *crtc) +{ + struct drm_i915_private *dev_priv = crtc->dev->dev_private; + struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc); + uint32_t val; + + intel_ddi_post_disable(intel_encoder); + + val = I915_READ(_FDI_RXA_CTL); + val &= ~FDI_RX_ENABLE; + I915_WRITE(_FDI_RXA_CTL, val); + + val = I915_READ(_FDI_RXA_MISC); + val &= ~(FDI_RX_PWRDN_LANE1_MASK | FDI_RX_PWRDN_LANE0_MASK); + val |= FDI_RX_PWRDN_LANE1_VAL(2) | FDI_RX_PWRDN_LANE0_VAL(2); + I915_WRITE(_FDI_RXA_MISC, val); + + val = I915_READ(_FDI_RXA_CTL); + val &= ~FDI_PCDCLK; + I915_WRITE(_FDI_RXA_CTL, val); + + val = I915_READ(_FDI_RXA_CTL); + val &= ~FDI_RX_PLL_ENABLE; + I915_WRITE(_FDI_RXA_CTL, val); +} + +static void intel_ddi_hot_plug(struct intel_encoder *intel_encoder) +{ + struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base); + int type = intel_encoder->type; + + if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) + intel_dp_check_link_status(intel_dp); +} + +static void intel_ddi_destroy(struct drm_encoder *encoder) +{ + /* HDMI has nothing special to destroy, so we can go with this. */ + intel_dp_encoder_destroy(encoder); +} + +static bool intel_ddi_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct intel_encoder *intel_encoder = to_intel_encoder(encoder); + int type = intel_encoder->type; + + WARN(type == INTEL_OUTPUT_UNKNOWN, "mode_fixup() on unknown output!\n"); + + if (type == INTEL_OUTPUT_HDMI) + return intel_hdmi_mode_fixup(encoder, mode, adjusted_mode); + else + return intel_dp_mode_fixup(encoder, mode, adjusted_mode); +} + +static const struct drm_encoder_funcs intel_ddi_funcs = { + .destroy = intel_ddi_destroy, +}; + +static const struct drm_encoder_helper_funcs intel_ddi_helper_funcs = { + .mode_fixup = intel_ddi_mode_fixup, + .mode_set = intel_ddi_mode_set, + .disable = intel_encoder_noop, +}; + +void intel_ddi_init(struct drm_device *dev, enum port port) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_digital_port *intel_dig_port; + struct intel_encoder *intel_encoder; + struct drm_encoder *encoder; + struct intel_connector *hdmi_connector = NULL; + struct intel_connector *dp_connector = NULL; + + intel_dig_port = malloc(sizeof(struct intel_digital_port), DRM_MEM_KMS, M_WAITOK | M_ZERO); + if (!intel_dig_port) + return; + + dp_connector = malloc(sizeof(struct intel_connector), DRM_MEM_KMS, M_WAITOK | M_ZERO); + if (!dp_connector) { + free(intel_dig_port, DRM_MEM_KMS); + return; + } + + if (port != PORT_A) { + hdmi_connector = malloc(sizeof(struct intel_connector), + DRM_MEM_KMS, M_WAITOK | M_ZERO); + if (!hdmi_connector) { + free(dp_connector, DRM_MEM_KMS); + free(intel_dig_port, DRM_MEM_KMS); + return; + } + } + + intel_encoder = &intel_dig_port->base; + encoder = &intel_encoder->base; + + drm_encoder_init(dev, encoder, &intel_ddi_funcs, + DRM_MODE_ENCODER_TMDS); + drm_encoder_helper_add(encoder, &intel_ddi_helper_funcs); + + intel_encoder->enable = intel_enable_ddi; + intel_encoder->pre_enable = intel_ddi_pre_enable; + intel_encoder->disable = intel_disable_ddi; + intel_encoder->post_disable = intel_ddi_post_disable; + intel_encoder->get_hw_state = intel_ddi_get_hw_state; + + intel_dig_port->port = port; + intel_dig_port->port_reversal = I915_READ(DDI_BUF_CTL(port)) & + DDI_BUF_PORT_REVERSAL; + if (hdmi_connector) + intel_dig_port->hdmi.sdvox_reg = DDI_BUF_CTL(port); + else + intel_dig_port->hdmi.sdvox_reg = 0; + intel_dig_port->dp.output_reg = DDI_BUF_CTL(port); + + intel_encoder->type = INTEL_OUTPUT_UNKNOWN; + intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); + intel_encoder->cloneable = false; + intel_encoder->hot_plug = intel_ddi_hot_plug; + + if (hdmi_connector) + intel_hdmi_init_connector(intel_dig_port, hdmi_connector); + intel_dp_init_connector(intel_dig_port, dp_connector); } |