[PATCH 03/21] drm/i915: Add DP Helper functions for Haswell

Eugeni Dodonov
2012-06-28T18:54:10Z

From: Shobhit Kumar <shobhit.kumar@intel.com>

Need to program new helpers for mode set and dpms as most of the stuff is done
using DDI port. The current commit uses SPLL clock for SCC enabled panel and
LCPLL for Non-SSC.

Also Haswell has LPT and DDIs are moved on CPU side and has DP_BUF_CTL and
DP_TP_CTL so added a new TP (Transport) holder as well along with DP in intel_dp

Main stream attributes have to be set explicitely from HSW onwrads. So
programmed the same.

Note that in DP mode the value of port width must match the one in the
DDI_BUF_CTL for the DDI port attached to the pipe.

Signed-off-by: Shobhit Kumar <shobhit.kumar@intel.com>
Signed-off-by: Sateesh Kavuri <sateesh.kavuri@intel.com>
Signed-off-by: Eugeni Dodonov <eugeni.dodonov@intel.com>
Reviewed-by: Eugeni Dodonov <eugeni.dodonov@intel.com>
---
drivers/gpu/drm/i915/i915_reg.h | 26 +++++-
drivers/gpu/drm/i915/intel_ddi.c | 167 +++++++++++++++++++++++++++++++++++++--
drivers/gpu/drm/i915/intel_dp.c | 18 ++++-
drivers/gpu/drm/i915/intel_drv.h | 8 ++
4 files changed, 210 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 9dfc4c5..51398a8 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -4289,6 +4289,7 @@
#define PIPE_DDI_BPC_6 (2<<20)
#define PIPE_DDI_BPC_12 (3<<20)
#define PIPE_DDI_BFI_ENABLE (1<<4)
+#define PIPE_DDI_PORT_WIDTH_MASK (7<<1)
#define PIPE_DDI_PORT_WIDTH_X1 (0<<1)
#define PIPE_DDI_PORT_WIDTH_X2 (1<<1)
#define PIPE_DDI_PORT_WIDTH_X4 (3<<1)
@@ -4335,6 +4336,7 @@
#define DDI_BUF_EMP_800MV_3_5DB_HSW (8<<24) /* Sel8 */
#define DDI_BUF_EMP_MASK (0xf<<24)
#define DDI_BUF_IS_IDLE (1<<7)
+#define DDI_PORT_WIDTH_MASK (7<<1)
#define DDI_PORT_WIDTH_X1 (0<<1)
#define DDI_PORT_WIDTH_X2 (1<<1)
#define DDI_PORT_WIDTH_X4 (3<<1)
@@ -4427,9 +4429,31 @@
#define LCPLL_CTL 0x130040
#define LCPLL_PLL_DISABLE (1<<31)
#define LCPLL_PLL_LOCK (1<<30)
-#define LCPLL_CD_CLOCK_DISABLE (1<<25)
+
+#define LCPLL_PLL_REFERENCE_BLK (2<<28)
+#define LCPLL_PLL_REFERENCE_SSC (3<<28)
+#define LCPLL_PLL_REFERENCE_NON_SSC (0<<28)
+#define LCPLL_PLL_REFERENCE_MASK (0x3<<28)
+
+#define LCPLL_CD_FREQ_SELECT_MASK (0x3<<26)
+#define LCPLL_CD_FREQ_450MHZ (0x0<<26)
+#define LCPLL_CD_FREQ_550MHZ (0x1<<26)
+
+#define LCPLL_CD_CLOCK_DISABLE (1<<25)
+#define LCPLL_ROOT_CD2X_CLOCK_DISABLE (1<<24)
#define LCPLL_CD2X_CLOCK_DISABLE (1<<23)

+#define HSW_MSA_CTL 0x60410
+#define HSW_MSA_SYNC_CLK (1<<0)
+#define HSW_MSA_DYNAMIC_RANGE (1<<3)
+#define HSW_MSA_COLORIMETRY (1<<4)
+#define HSW_MSA_BPC_MASK (7<<5)
+#define HSW_MSA_BPC_6_BITS (0<<5)
+#define HSW_MSA_BPC_8_BITS (1<<5)
+#define HSW_MSA_BPC_10_BITS (2<<5)
+#define HSW_MSA_BPC_12_BITS (3<<5)
+#define HSW_MSA_BPC_16_BITS (4<<5)
+
/* Pipe WM_LINETIME - watermark line time */
#define PIPE_WM_LINETIME_A 0x45270
#define PIPE_WM_LINETIME_B 0x45274
diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index f33fe1a..91ed708 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -729,10 +729,159 @@ void intel_ddi_mode_set(struct drm_encoder *encoder,
intel_hdmi->set_infoframes(encoder, adjusted_mode);
}

+void
+intel_ddi_mode_set_dp(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 intel_dp *intel_dp = enc_to_intel_dp(encoder);
+ struct drm_crtc *crtc = intel_dp->base.base.crtc;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int port = intel_dp->ddi_port;
+ int pipe = intel_crtc->pipe;
+ int port_width = PIPE_DDI_PORT_WIDTH_X1;
+ int temp;
+
+ DRM_DEBUG_KMS("Preparing DP DDI mode for Haswell on port %c, pipe %c\n",
+ port_name(port), pipe_name(pipe));
+
+ /* Turn on the eDP PLL if needed */
+ /* TBD: */
+
+ /*
+ * There are two kinds of DP registers in HSW:
+ * DDI CPU
+ * LPT PCH
+ */
+ /* Preserve the BIOS-computed detected bit. This is
+ * supposed to be read-only.
+ */
+ intel_dp->DP = I915_READ(DDI_BUF_CTL(intel_dp->ddi_port));
+ intel_dp->DP |= DDI_BUF_EMP_400MV_0DB_HSW;
+ intel_dp->TP = I915_READ(DP_TP_CTL(intel_dp->ddi_port));
+
+ intel_dp->DP &= ~(DDI_PORT_WIDTH_MASK);
+ switch (intel_dp->lane_count) {
+ case 1:
+ intel_dp->DP |= DDI_PORT_WIDTH_X1;
+ port_width = PIPE_DDI_PORT_WIDTH_X1;
+ break;
+ case 2:
+ intel_dp->DP |= DDI_PORT_WIDTH_X2;
+ port_width = PIPE_DDI_PORT_WIDTH_X2;
+ break;
+ case 4:
+ intel_dp->DP |= DDI_PORT_WIDTH_X4;
+ port_width = PIPE_DDI_PORT_WIDTH_X4;
+ break;
+ default:
+ BUG();
+ }
+
+ if (intel_dp->has_audio)
+ DRM_DEBUG_DRIVER("HSW DP Audio not yet supported\n");
+
+ memset(intel_dp->link_configuration, 0, DP_LINK_CONFIGURATION_SIZE);
+ intel_dp->link_configuration[0] = intel_dp->link_bw;
+ intel_dp->link_configuration[1] = intel_dp->lane_count;
+ intel_dp->link_configuration[8] = DP_SET_ANSI_8B10B;
+ /*
+ * Check for DPCD version > 1.1 and enhanced framing support
+ */
+ if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 &&
+ (intel_dp->dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP)) {
+ intel_dp->link_configuration[1] |=
+ DP_LANE_COUNT_ENHANCED_FRAME_EN;
+ }
+
+ /* Enable LCPLL if disabled; required in all cases */
+ temp = I915_READ(LCPLL_CTL);
+ if (temp & LCPLL_PLL_DISABLE) {
+ temp &= ~LCPLL_PLL_DISABLE;
+ temp &= ~LCPLL_CD2X_CLOCK_DISABLE;
+ temp &= ~LCPLL_ROOT_CD2X_CLOCK_DISABLE;
+ temp &= ~LCPLL_CD_CLOCK_DISABLE;
+ temp = (temp & ~LCPLL_CD_FREQ_SELECT_MASK) |
+ LCPLL_CD_FREQ_450MHZ;
+ I915_WRITE(LCPLL_CTL, temp);
+ udelay(20);
+ }
+
+ if (IS_HASWELL(dev)) {
+ if (intel_dp->link_configuration[1] &
+ DP_LANE_COUNT_ENHANCED_FRAME_EN)
+ intel_dp->TP |= DP_TP_CTL_ENHANCED_FRAME_ENABLE;
+
+ /* Set the MSA bits, since from HSW onwards,
+ * this has to be explictly set */
+ temp = I915_READ(HSW_MSA_CTL);
+ temp |= HSW_MSA_SYNC_CLK;
+ temp &= ~HSW_MSA_DYNAMIC_RANGE;
+ temp &= ~HSW_MSA_COLORIMETRY;
+ temp &= ~HSW_MSA_BPC_MASK;
+ temp |= HSW_MSA_BPC_8_BITS; /* Color depth */
+
+ I915_WRITE(HSW_MSA_CTL, temp);
+
+ if ((intel_dp->dpcd[DP_MAX_DOWNSPREAD] & 0x1) == 0x1) {
+ /* SSC Enabled Panel */
+ /* Configure SPLL, program the correct divider values
+ * for the desired frequency and wait for warmup */
+ I915_WRITE(SPLL_CTL,
+ SPLL_PLL_ENABLE |
+ SPLL_PLL_SCC |
+ SPLL_PLL_FREQ_1350MHz);
+ udelay(20);
+
+ /* Use SPLL clock to drive the output to the dp port,
+ * and tell the pipe to use this port for connection.
+ */
+ I915_WRITE(PORT_CLK_SEL(port),
+ PORT_CLK_SEL_SPLL);
+ I915_WRITE(PIPE_CLK_SEL(pipe),
+ PIPE_CLK_SEL_PORT(port));
+ } else {
+ /* Non SSC Panel */
+ /* configure LCPLL for NON-SSC */
+ temp = I915_READ(LCPLL_CTL);
+ temp = (temp & ~LCPLL_PLL_REFERENCE_MASK) |
+ LCPLL_PLL_REFERENCE_NON_SSC;
+ I915_WRITE(LCPLL_CTL, temp);
+ udelay(20);
+
+ /* Use LCPLL clock to drive the output to the dp port,
+ * and tell the pipe to use this port for connection.
+ */
+ I915_WRITE(PORT_CLK_SEL(port),
+ PORT_CLK_SEL_LCPLL_1350);
+ I915_WRITE(PIPE_CLK_SEL(pipe),
+ PIPE_CLK_SEL_PORT(port));
+ }
+
+ /* Enable PIPE_DDI_FUNC_CTL for the pipe to work in DP mode */
+ temp = I915_READ(DDI_FUNC_CTL(pipe));
+ temp &= ~PIPE_DDI_PORT_MASK;
+ temp &= ~PIPE_DDI_BPC_12;
+ temp &= ~PIPE_DDI_PORT_WIDTH_MASK;
+ temp |= PIPE_DDI_SELECT_PORT(port) |
+ PIPE_DDI_MODE_SELECT_DP_SST |
+ port_width |
+ ((intel_crtc->bpp > 24) ?
+ PIPE_DDI_BPC_12 :
+ PIPE_DDI_BPC_8) |
+ PIPE_DDI_FUNC_ENABLE;
+
+ I915_WRITE(DDI_FUNC_CTL(pipe), temp);
+ }
+}
+
void intel_ddi_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
int port = intel_hdmi->ddi_port;
u32 temp;
@@ -745,10 +894,16 @@ void intel_ddi_dpms(struct drm_encoder *encoder, int mode)
temp |= DDI_BUF_CTL_ENABLE;
}

- /* 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(DDI_BUF_CTL(port),
- temp);
+ if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT) {
+ DRM_DEBUG_DRIVER("DPMS is not yet enabled on DP port\n");
+ return;
+ } else {
+
+ /* 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(DDI_BUF_CTL(port),
+ temp);
+ }
}
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index e324c7a..f4c6e98 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -74,7 +74,7 @@ static bool is_cpu_edp(struct intel_dp *intel_dp)
return is_edp(intel_dp) && !is_pch_edp(intel_dp);
}

-static struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder)
+struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder)
{
return container_of(encoder, struct intel_dp, base.base);
}
@@ -2339,6 +2339,14 @@ static const struct drm_encoder_helper_funcs intel_dp_helper_funcs = {
.commit = intel_dp_commit,
};

+static const struct drm_encoder_helper_funcs intel_dp_helper_funcs_hsw = {
+ .dpms = intel_ddi_dpms,
+ .mode_fixup = intel_dp_mode_fixup,
+ .prepare = intel_dp_prepare,
+ .mode_set = intel_ddi_mode_set_dp,
+ .commit = intel_dp_commit,
+};
+
static const struct drm_connector_funcs intel_dp_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.detect = intel_dp_detect,
@@ -2482,7 +2490,13 @@ intel_dp_init(struct drm_device *dev, int output_reg)

drm_encoder_init(dev, &intel_encoder->base, &intel_dp_enc_funcs,
DRM_MODE_ENCODER_TMDS);
- drm_encoder_helper_add(&intel_encoder->base, &intel_dp_helper_funcs);
+
+ if (IS_HASWELL(dev))
+ drm_encoder_helper_add(&intel_encoder->base,
+ &intel_dp_helper_funcs_hsw);
+ else
+ drm_encoder_helper_add(&intel_encoder->base,
+ &intel_dp_helper_funcs);

intel_connector_attach_encoder(intel_connector, intel_encoder);
drm_sysfs_connector_add(connector);
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 7cffb12..65b5b7c 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -316,6 +316,7 @@ struct intel_dp {
struct intel_encoder base;
uint32_t output_reg;
uint32_t DP;
+ uint32_t TP;
uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE];
bool has_audio;
enum hdmi_force_audio force_audio;
@@ -390,6 +391,8 @@ extern void intel_mark_busy(struct drm_device *dev,
struct drm_i915_gem_object *obj);
extern bool intel_lvds_init(struct drm_device *dev);
extern void intel_dp_init(struct drm_device *dev, int dp_reg);
+extern struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder);
+
void
intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
@@ -534,5 +537,10 @@ extern void intel_ddi_dpms(struct drm_encoder *encoder, int mode);
extern void intel_ddi_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
+void
+intel_ddi_mode_set_dp(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+

#endif /* __INTEL_DRV_H__ */
--
1.7.11.1