[Intel-gfx] [PATCH 7/9] drm/i915: enable kernel modesetting on IGDNG

Zhenyu Wang zhenyuw at linux.intel.com
Fri Jun 5 09:38:42 CEST 2009


This adds kernel mode setting on IGDNG with VGA output support.
Note that suspend/resume doesn't work yet.

Signed-off-by: Zhenyu Wang <zhenyuw at linux.intel.com>
---
 drivers/gpu/drm/i915/i915_dma.c      |    2 +-
 drivers/gpu/drm/i915/intel_crt.c     |   76 ++++-
 drivers/gpu/drm/i915/intel_display.c |  603 ++++++++++++++++++++++++++++++++--
 3 files changed, 643 insertions(+), 38 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 1de50a5..d8f8e35 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -922,7 +922,7 @@ static int i915_probe_agp(struct drm_device *dev, unsigned long *aperture_size,
 	 * Some of the preallocated space is taken by the GTT
 	 * and popup.  GTT is 1K per MB of aperture size, and popup is 4K.
 	 */
-	if (IS_G4X(dev) || IS_IGD(dev))
+	if (IS_G4X(dev) || IS_IGD(dev) || IS_IGDNG(dev))
 		overhead = 4096;
 	else
 		overhead = (*aperture_size / 1024) + 4096;
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index 640f515..ff9bcca 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -37,9 +37,14 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
 {
 	struct drm_device *dev = encoder->dev;
 	struct drm_i915_private *dev_priv = dev->dev_private;
-	u32 temp;
+	u32 temp, reg;
 
-	temp = I915_READ(ADPA);
+	if (IS_IGDNG(dev))
+		reg = PCH_ADPA;
+	else
+		reg = ADPA;
+
+	temp = I915_READ(reg);
 	temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
 	temp |= ADPA_DAC_ENABLE;
 
@@ -58,7 +63,7 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
 		break;
 	}
 
-	I915_WRITE(ADPA, temp);
+	I915_WRITE(reg, temp);
 }
 
 static int intel_crt_mode_valid(struct drm_connector *connector,
@@ -101,17 +106,23 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	int dpll_md_reg;
 	u32 adpa, dpll_md;
+	u32 adpa_reg;
 
 	if (intel_crtc->pipe == 0)
 		dpll_md_reg = DPLL_A_MD;
 	else
 		dpll_md_reg = DPLL_B_MD;
 
+	if (IS_IGDNG(dev))
+		adpa_reg = PCH_ADPA;
+	else
+		adpa_reg = ADPA;
+
 	/*
 	 * Disable separate mode multiplier used when cloning SDVO to CRT
 	 * XXX this needs to be adjusted when we really are cloning
 	 */
-	if (IS_I965G(dev)) {
+	if (IS_I965G(dev) && !IS_IGDNG(dev)) {
 		dpll_md = I915_READ(dpll_md_reg);
 		I915_WRITE(dpll_md_reg,
 			   dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK);
@@ -125,13 +136,53 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
 
 	if (intel_crtc->pipe == 0) {
 		adpa |= ADPA_PIPE_A_SELECT;
-		I915_WRITE(BCLRPAT_A, 0);
+		if (!IS_IGDNG(dev))
+			I915_WRITE(BCLRPAT_A, 0);
 	} else {
 		adpa |= ADPA_PIPE_B_SELECT;
-		I915_WRITE(BCLRPAT_B, 0);
+		if (!IS_IGDNG(dev))
+			I915_WRITE(BCLRPAT_B, 0);
 	}
 
-	I915_WRITE(ADPA, adpa);
+	I915_WRITE(adpa_reg, adpa);
+}
+
+static bool intel_igdng_crt_detect_hotplug(struct drm_connector *connector)
+{
+	struct drm_device *dev = connector->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	u32 adpa, temp;
+	bool ret;
+
+	temp = adpa = I915_READ(PCH_ADPA);
+
+	adpa &= ~ADPA_CRT_HOTPLUG_MASK;
+
+	adpa |= (ADPA_CRT_HOTPLUG_PERIOD_128 |
+			ADPA_CRT_HOTPLUG_WARMUP_10MS |
+			ADPA_CRT_HOTPLUG_SAMPLE_4S |
+			ADPA_CRT_HOTPLUG_VOLTAGE_50 | /* default */
+			ADPA_CRT_HOTPLUG_VOLREF_325MV |
+			ADPA_CRT_HOTPLUG_ENABLE |
+			ADPA_CRT_HOTPLUG_FORCE_TRIGGER);
+
+	DRM_DEBUG("pch crt adpa 0x%x", adpa);
+	I915_WRITE(PCH_ADPA, adpa);
+
+	/* This might not be needed as not specified in spec...*/
+	udelay(1000);
+
+	/* Check the status to see if both blue and green are on now */
+	adpa = I915_READ(PCH_ADPA);
+	if ((adpa & ADPA_CRT_HOTPLUG_MONITOR_MASK) ==
+			ADPA_CRT_HOTPLUG_MONITOR_COLOR)
+		ret = true;
+	else
+		ret = false;
+
+	/* restore origin register */
+	I915_WRITE(PCH_ADPA, temp);
+	return ret;
 }
 
 /**
@@ -148,6 +199,10 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	u32 hotplug_en;
 	int i, tries = 0;
+
+	if (IS_IGDNG(dev))
+		return intel_igdng_crt_detect_hotplug(connector);
+
 	/*
 	 * On 4 series desktop, CRT detect sequence need to be done twice
 	 * to get a reliable result.
@@ -427,6 +482,7 @@ void intel_crt_init(struct drm_device *dev)
 {
 	struct drm_connector *connector;
 	struct intel_output *intel_output;
+	u32 i2c_reg;
 
 	intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
 	if (!intel_output)
@@ -443,7 +499,11 @@ void intel_crt_init(struct drm_device *dev)
 					  &intel_output->enc);
 
 	/* Set up the DDC bus. */
-	intel_output->ddc_bus = intel_i2c_create(dev, GPIOA, "CRTDDC_A");
+	if (IS_IGDNG(dev))
+		i2c_reg = PCH_GPIOA;
+	else
+		i2c_reg = GPIOA;
+	intel_output->ddc_bus = intel_i2c_create(dev, i2c_reg, "CRTDDC_A");
 	if (!intel_output->ddc_bus) {
 		dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
 			   "failed.\n");
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index c9d6f10..2cd6ba6 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -137,6 +137,8 @@ struct intel_limit {
 #define INTEL_LIMIT_G4X_DUAL_CHANNEL_LVDS   7
 #define INTEL_LIMIT_IGD_SDVO_DAC    8
 #define INTEL_LIMIT_IGD_LVDS	    9
+#define INTEL_LIMIT_IGDNG_SDVO_DAC  10
+#define INTEL_LIMIT_IGDNG_LVDS	    11
 
 /*The parameter is for SDVO on G4x platform*/
 #define G4X_DOT_SDVO_MIN           25000
@@ -216,12 +218,43 @@ struct intel_limit {
 #define G4X_P2_DUAL_CHANNEL_LVDS_FAST           7
 #define G4X_P2_DUAL_CHANNEL_LVDS_LIMIT          0
 
+/* IGDNG */
+/* as we calculate clock using (register_value + 2) for
+   N/M1/M2, so here the range value for them is (actual_value-2).
+ */
+#define IGDNG_DOT_MIN         25000
+#define IGDNG_DOT_MAX         350000
+#define IGDNG_VCO_MIN         1760000
+#define IGDNG_VCO_MAX         3510000
+#define IGDNG_N_MIN           1
+#define IGDNG_N_MAX           5
+#define IGDNG_M_MIN           79
+#define IGDNG_M_MAX           118
+#define IGDNG_M1_MIN          12
+#define IGDNG_M1_MAX          23
+#define IGDNG_M2_MIN          5
+#define IGDNG_M2_MAX          9
+#define IGDNG_P_SDVO_DAC_MIN  5
+#define IGDNG_P_SDVO_DAC_MAX  80
+#define IGDNG_P_LVDS_MIN      28
+#define IGDNG_P_LVDS_MAX      112
+#define IGDNG_P1_MIN          1
+#define IGDNG_P1_MAX          8
+#define IGDNG_P2_SDVO_DAC_SLOW 10
+#define IGDNG_P2_SDVO_DAC_FAST 5
+#define IGDNG_P2_LVDS_SLOW    14 /* single channel */
+#define IGDNG_P2_LVDS_FAST    7  /* double channel */
+#define IGDNG_P2_DOT_LIMIT    225000 /* 225Mhz */
+
 static bool
 intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
 		    int target, int refclk, intel_clock_t *best_clock);
 static bool
 intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
 			int target, int refclk, intel_clock_t *best_clock);
+static bool
+intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+			int target, int refclk, intel_clock_t *best_clock);
 
 static const intel_limit_t intel_limits[] = {
     { /* INTEL_LIMIT_I8XX_DVO_DAC */
@@ -383,9 +416,47 @@ static const intel_limit_t intel_limits[] = {
 		 .p2_slow = I9XX_P2_LVDS_SLOW,	.p2_fast = I9XX_P2_LVDS_SLOW },
 	.find_pll = intel_find_best_PLL,
     },
-
+    { /* INTEL_LIMIT_IGDNG_SDVO_DAC */
+	.dot = { .min = IGDNG_DOT_MIN,          .max = IGDNG_DOT_MAX },
+	.vco = { .min = IGDNG_VCO_MIN,          .max = IGDNG_VCO_MAX },
+	.n   = { .min = IGDNG_N_MIN,            .max = IGDNG_N_MAX },
+	.m   = { .min = IGDNG_M_MIN,            .max = IGDNG_M_MAX },
+	.m1  = { .min = IGDNG_M1_MIN,           .max = IGDNG_M1_MAX },
+	.m2  = { .min = IGDNG_M2_MIN,           .max = IGDNG_M2_MAX },
+	.p   = { .min = IGDNG_P_SDVO_DAC_MIN,   .max = IGDNG_P_SDVO_DAC_MAX },
+	.p1  = { .min = IGDNG_P1_MIN,           .max = IGDNG_P1_MAX },
+	.p2  = { .dot_limit = IGDNG_P2_DOT_LIMIT,
+		 .p2_slow = IGDNG_P2_SDVO_DAC_SLOW,
+		 .p2_fast = IGDNG_P2_SDVO_DAC_FAST },
+	.find_pll = intel_igdng_find_best_PLL,
+    },
+    { /* INTEL_LIMIT_IGDNG_LVDS */
+	.dot = { .min = IGDNG_DOT_MIN,          .max = IGDNG_DOT_MAX },
+	.vco = { .min = IGDNG_VCO_MIN,          .max = IGDNG_VCO_MAX },
+	.n   = { .min = IGDNG_N_MIN,            .max = IGDNG_N_MAX },
+	.m   = { .min = IGDNG_M_MIN,            .max = IGDNG_M_MAX },
+	.m1  = { .min = IGDNG_M1_MIN,           .max = IGDNG_M1_MAX },
+	.m2  = { .min = IGDNG_M2_MIN,           .max = IGDNG_M2_MAX },
+	.p   = { .min = IGDNG_P_LVDS_MIN,       .max = IGDNG_P_LVDS_MAX },
+	.p1  = { .min = IGDNG_P1_MIN,           .max = IGDNG_P1_MAX },
+	.p2  = { .dot_limit = IGDNG_P2_DOT_LIMIT,
+		 .p2_slow = IGDNG_P2_LVDS_SLOW,
+		 .p2_fast = IGDNG_P2_LVDS_FAST },
+	.find_pll = intel_igdng_find_best_PLL,
+    },
 };
 
+static const intel_limit_t *intel_igdng_limit(struct drm_crtc *crtc)
+{
+	const intel_limit_t *limit;
+	if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+		limit = &intel_limits[INTEL_LIMIT_IGDNG_LVDS];
+	else
+		limit = &intel_limits[INTEL_LIMIT_IGDNG_SDVO_DAC];
+
+	return limit;
+}
+
 static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc)
 {
 	struct drm_device *dev = crtc->dev;
@@ -418,7 +489,9 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc)
 	struct drm_device *dev = crtc->dev;
 	const intel_limit_t *limit;
 
-	if (IS_G4X(dev)) {
+	if (IS_IGDNG(dev))
+		limit = intel_igdng_limit(crtc);
+	else if (IS_G4X(dev)) {
 		limit = intel_g4x_limit(crtc);
 	} else if (IS_I9XX(dev) && !IS_IGD(dev)) {
 		if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
@@ -630,7 +703,64 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
 			}
 		}
 	}
+	return found;
+}
+
+static bool
+intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+			int target, int refclk, intel_clock_t *best_clock)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	intel_clock_t clock;
+	int max_n;
+	bool found;
+	int err_most = 47;
+	found = false;
+
+	if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+		if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
+		    LVDS_CLKB_POWER_UP)
+			clock.p2 = limit->p2.p2_fast;
+		else
+			clock.p2 = limit->p2.p2_slow;
+	} else {
+		if (target < limit->p2.dot_limit)
+			clock.p2 = limit->p2.p2_slow;
+		else
+			clock.p2 = limit->p2.p2_fast;
+	}
+
+	memset(best_clock, 0, sizeof(*best_clock));
+	max_n = limit->n.max;
+	/* based on hardware requriment prefer smaller n to precision */
+	for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) {
+		/* based on hardware requirment prefere larger m1,m2, p1 */
+		for (clock.m1 = limit->m1.max;
+		     clock.m1 >= limit->m1.min; clock.m1--) {
+			for (clock.m2 = limit->m2.max;
+			     clock.m2 >= limit->m2.min; clock.m2--) {
+				for (clock.p1 = limit->p1.max;
+				     clock.p1 >= limit->p1.min; clock.p1--) {
+					int this_err;
 
+					intel_clock(dev, refclk, &clock);
+					if (!intel_PLL_is_valid(crtc, &clock))
+						continue;
+					this_err = abs((10000 - (target*10000/clock.dot)));
+					if (this_err < err_most) {
+						*best_clock = clock;
+						err_most = this_err;
+						max_n = clock.n;
+						found = true;
+						/* found on first matching */
+						goto out;
+					}
+				}
+			}
+		}
+	}
+out:
 	return found;
 }
 
@@ -785,18 +915,292 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
 	return 0;
 }
 
+static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+	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;
+	int plane = intel_crtc->pipe;
+	int pch_dpll_reg = (pipe == 0) ? PCH_DPLL_A : PCH_DPLL_B;
+	int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
+	int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR;
+	int dspbase_reg = (plane == 0) ? DSPAADDR : DSPBADDR;
+	int fdi_tx_reg = (pipe == 0) ? FDI_TXA_CTL : FDI_TXB_CTL;
+	int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL;
+	int fdi_rx_iir_reg = (pipe == 0) ? FDI_RXA_IIR : FDI_RXB_IIR;
+	int fdi_rx_imr_reg = (pipe == 0) ? FDI_RXA_IMR : FDI_RXB_IMR;
+	int transconf_reg = (pipe == 0) ? TRANSACONF : TRANSBCONF;
+	int pf_ctl_reg = (pipe == 0) ? PFA_CTL_1 : PFB_CTL_1;
+	int cpu_htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B;
+	int cpu_hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B;
+	int cpu_hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B;
+	int cpu_vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B;
+	int cpu_vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B;
+	int cpu_vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B;
+	int trans_htot_reg = (pipe == 0) ? TRANS_HTOTAL_A : TRANS_HTOTAL_B;
+	int trans_hblank_reg = (pipe == 0) ? TRANS_HBLANK_A : TRANS_HBLANK_B;
+	int trans_hsync_reg = (pipe == 0) ? TRANS_HSYNC_A : TRANS_HSYNC_B;
+	int trans_vtot_reg = (pipe == 0) ? TRANS_VTOTAL_A : TRANS_VTOTAL_B;
+	int trans_vblank_reg = (pipe == 0) ? TRANS_VBLANK_A : TRANS_VBLANK_B;
+	int trans_vsync_reg = (pipe == 0) ? TRANS_VSYNC_A : TRANS_VSYNC_B;
+	u32 temp;
+	int tries = 5, j;
 
+	/* XXX: When our outputs are all unaware of DPMS modes other than off
+	 * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
+	 */
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+	case DRM_MODE_DPMS_STANDBY:
+	case DRM_MODE_DPMS_SUSPEND:
+		DRM_DEBUG("crtc %d dpms on\n", pipe);
+		/* enable PCH DPLL */
+		temp = I915_READ(pch_dpll_reg);
+		if ((temp & DPLL_VCO_ENABLE) == 0) {
+			I915_WRITE(pch_dpll_reg, temp | DPLL_VCO_ENABLE);
+			I915_READ(pch_dpll_reg);
+		}
 
-/**
- * Sets the power management mode of the pipe and plane.
- *
- * This code should probably grow support for turning the cursor off and back
- * on appropriately at the same time as we're turning the pipe off/on.
- */
-static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
+		/* enable PCH FDI RX PLL, wait warmup plus DMI latency */
+		temp = I915_READ(fdi_rx_reg);
+		I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE |
+				FDI_SEL_PCDCLK |
+				FDI_DP_PORT_WIDTH_X4); /* default 4 lanes */
+		I915_READ(fdi_rx_reg);
+		udelay(200);
+
+		/* Enable CPU FDI TX PLL, always on for IGDNG */
+		temp = I915_READ(fdi_tx_reg);
+		if ((temp & FDI_TX_PLL_ENABLE) == 0) {
+			I915_WRITE(fdi_tx_reg, temp | FDI_TX_PLL_ENABLE);
+			I915_READ(fdi_tx_reg);
+			udelay(100);
+		}
+
+		/* Enable CPU pipe */
+		temp = I915_READ(pipeconf_reg);
+		if ((temp & PIPEACONF_ENABLE) == 0) {
+			I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE);
+			I915_READ(pipeconf_reg);
+			udelay(100);
+		}
+
+		/* configure and enable CPU plane */
+		temp = I915_READ(dspcntr_reg);
+		if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
+			I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE);
+			/* Flush the plane changes */
+			I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+		}
+
+		/* enable CPU FDI TX and PCH FDI RX */
+		temp = I915_READ(fdi_tx_reg);
+		temp |= FDI_TX_ENABLE;
+		temp |= FDI_DP_PORT_WIDTH_X4; /* default */
+		temp &= ~FDI_LINK_TRAIN_NONE;
+		temp |= FDI_LINK_TRAIN_PATTERN_1;
+		I915_WRITE(fdi_tx_reg, temp);
+		I915_READ(fdi_tx_reg);
+
+		temp = I915_READ(fdi_rx_reg);
+		temp &= ~FDI_LINK_TRAIN_NONE;
+		temp |= FDI_LINK_TRAIN_PATTERN_1;
+		I915_WRITE(fdi_rx_reg, temp | FDI_RX_ENABLE);
+		I915_READ(fdi_rx_reg);
+
+		udelay(150);
+
+		/* Train FDI. */
+		/* umask FDI RX Interrupt symbol_lock and bit_lock bit
+		   for train result */
+		temp = I915_READ(fdi_rx_imr_reg);
+		temp &= ~FDI_RX_SYMBOL_LOCK;
+		temp &= ~FDI_RX_BIT_LOCK;
+		I915_WRITE(fdi_rx_imr_reg, temp);
+		I915_READ(fdi_rx_imr_reg);
+		udelay(150);
+
+		temp = I915_READ(fdi_rx_iir_reg);
+		DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+
+		if ((temp & FDI_RX_BIT_LOCK) == 0) {
+			for (j = 0; j < tries; j++) {
+				temp = I915_READ(fdi_rx_iir_reg);
+				DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+				if (temp & FDI_RX_BIT_LOCK)
+					break;
+				udelay(200);
+			}
+			if (j != tries)
+				I915_WRITE(fdi_rx_iir_reg,
+						temp | FDI_RX_BIT_LOCK);
+			else
+				DRM_DEBUG("train 1 fail\n");
+		} else {
+			I915_WRITE(fdi_rx_iir_reg,
+					temp | FDI_RX_BIT_LOCK);
+			DRM_DEBUG("train 1 ok 2!\n");
+		}
+		temp = I915_READ(fdi_tx_reg);
+		temp &= ~FDI_LINK_TRAIN_NONE;
+		temp |= FDI_LINK_TRAIN_PATTERN_2;
+		I915_WRITE(fdi_tx_reg, temp);
+
+		temp = I915_READ(fdi_rx_reg);
+		temp &= ~FDI_LINK_TRAIN_NONE;
+		temp |= FDI_LINK_TRAIN_PATTERN_2;
+		I915_WRITE(fdi_rx_reg, temp);
+
+		udelay(150);
+
+		temp = I915_READ(fdi_rx_iir_reg);
+		DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+
+		if ((temp & FDI_RX_SYMBOL_LOCK) == 0) {
+			for (j = 0; j < tries; j++) {
+				temp = I915_READ(fdi_rx_iir_reg);
+				DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+				if (temp & FDI_RX_SYMBOL_LOCK)
+					break;
+				udelay(200);
+			}
+			if (j != tries) {
+				I915_WRITE(fdi_rx_iir_reg,
+						temp | FDI_RX_SYMBOL_LOCK);
+				DRM_DEBUG("train 2 ok 1!\n");
+			} else
+				DRM_DEBUG("train 2 fail\n");
+		} else {
+			I915_WRITE(fdi_rx_iir_reg, temp | FDI_RX_SYMBOL_LOCK);
+			DRM_DEBUG("train 2 ok 2!\n");
+		}
+		DRM_DEBUG("train done\n");
+
+		/* set transcoder timing */
+		I915_WRITE(trans_htot_reg, I915_READ(cpu_htot_reg));
+		I915_WRITE(trans_hblank_reg, I915_READ(cpu_hblank_reg));
+		I915_WRITE(trans_hsync_reg, I915_READ(cpu_hsync_reg));
+
+		I915_WRITE(trans_vtot_reg, I915_READ(cpu_vtot_reg));
+		I915_WRITE(trans_vblank_reg, I915_READ(cpu_vblank_reg));
+		I915_WRITE(trans_vsync_reg, I915_READ(cpu_vsync_reg));
+
+		/* enable PCH transcoder */
+		temp = I915_READ(transconf_reg);
+		I915_WRITE(transconf_reg, temp | TRANS_ENABLE);
+		I915_READ(transconf_reg);
+
+		while ((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) == 0)
+			;
+
+		/* enable normal */
+
+		temp = I915_READ(fdi_tx_reg);
+		temp &= ~FDI_LINK_TRAIN_NONE;
+		I915_WRITE(fdi_tx_reg, temp | FDI_LINK_TRAIN_NONE |
+				FDI_TX_ENHANCE_FRAME_ENABLE);
+		I915_READ(fdi_tx_reg);
+
+		temp = I915_READ(fdi_rx_reg);
+		temp &= ~FDI_LINK_TRAIN_NONE;
+		I915_WRITE(fdi_rx_reg, temp | FDI_LINK_TRAIN_NONE |
+				FDI_RX_ENHANCE_FRAME_ENABLE);
+		I915_READ(fdi_rx_reg);
+
+		/* wait one idle pattern time */
+		udelay(100);
+
+		intel_crtc_load_lut(crtc);
+
+	break;
+	case DRM_MODE_DPMS_OFF:
+		DRM_DEBUG("crtc %d dpms off\n", pipe);
+
+		/* Disable the VGA plane that we never use */
+		I915_WRITE(CPU_VGACNTRL, VGA_DISP_DISABLE);
+
+		/* Disable display plane */
+		temp = I915_READ(dspcntr_reg);
+		if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
+			I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE);
+			/* Flush the plane changes */
+			I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+			I915_READ(dspbase_reg);
+		}
+
+		/* disable cpu pipe, disable after all planes disabled */
+		temp = I915_READ(pipeconf_reg);
+		if ((temp & PIPEACONF_ENABLE) != 0) {
+			I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE);
+			I915_READ(pipeconf_reg);
+			/* wait for cpu pipe off, pipe state */
+			while ((I915_READ(pipeconf_reg) & I965_PIPECONF_ACTIVE) != 0)
+				;
+		} else
+			DRM_DEBUG("crtc %d is disabled\n", pipe);
+
+		/* IGDNG-A : disable cpu panel fitter ? */
+		temp = I915_READ(pf_ctl_reg);
+		if ((temp & PF_ENABLE) != 0) {
+			I915_WRITE(pf_ctl_reg, temp & ~PF_ENABLE);
+			I915_READ(pf_ctl_reg);
+		}
+
+		/* disable CPU FDI tx and PCH FDI rx */
+		temp = I915_READ(fdi_tx_reg);
+		I915_WRITE(fdi_tx_reg, temp & ~FDI_TX_ENABLE);
+		I915_READ(fdi_tx_reg);
+
+		temp = I915_READ(fdi_rx_reg);
+		I915_WRITE(fdi_rx_reg, temp & ~FDI_RX_ENABLE);
+		I915_READ(fdi_rx_reg);
+
+		/* still set train pattern 1 */
+		temp = I915_READ(fdi_tx_reg);
+		temp &= ~FDI_LINK_TRAIN_NONE;
+		temp |= FDI_LINK_TRAIN_PATTERN_1;
+		I915_WRITE(fdi_tx_reg, temp);
+
+		temp = I915_READ(fdi_rx_reg);
+		temp &= ~FDI_LINK_TRAIN_NONE;
+		temp |= FDI_LINK_TRAIN_PATTERN_1;
+		I915_WRITE(fdi_rx_reg, temp);
+
+		/* disable PCH transcoder */
+		temp = I915_READ(transconf_reg);
+		if ((temp & TRANS_ENABLE) != 0) {
+			I915_WRITE(transconf_reg, temp & ~TRANS_ENABLE);
+			I915_READ(transconf_reg);
+			/* wait for PCH transcoder off, transcoder state */
+			while ((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) != 0)
+				;
+		}
+
+		/* disable PCH DPLL */
+		temp = I915_READ(pch_dpll_reg);
+		if ((temp & DPLL_VCO_ENABLE) != 0) {
+			I915_WRITE(pch_dpll_reg, temp & ~DPLL_VCO_ENABLE);
+			I915_READ(pch_dpll_reg);
+		}
+
+		temp = I915_READ(fdi_rx_reg);
+		if ((temp & FDI_RX_PLL_ENABLE) != 0) {
+			temp &= ~FDI_SEL_PCDCLK;
+			temp &= ~FDI_RX_PLL_ENABLE;
+			I915_WRITE(fdi_rx_reg, temp);
+			I915_READ(fdi_rx_reg);
+		}
+
+		/* Wait for the clocks to turn off. */
+		udelay(150);
+		break;
+	}
+}
+
+static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
 {
 	struct drm_device *dev = crtc->dev;
-	struct drm_i915_master_private *master_priv;
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 	int pipe = intel_crtc->pipe;
@@ -805,7 +1209,6 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
 	int dspbase_reg = (pipe == 0) ? DSPAADDR : DSPBADDR;
 	int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
 	u32 temp;
-	bool enabled;
 
 	/* XXX: When our outputs are all unaware of DPMS modes other than off
 	 * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
@@ -890,6 +1293,26 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
 		udelay(150);
 		break;
 	}
+}
+
+/**
+ * Sets the power management mode of the pipe and plane.
+ *
+ * This code should probably grow support for turning the cursor off and back
+ * on appropriately at the same time as we're turning the pipe off/on.
+ */
+static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_i915_master_private *master_priv;
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+	int pipe = intel_crtc->pipe;
+	bool enabled;
+
+	if (IS_IGDNG(dev))
+		igdng_crtc_dpms(crtc, mode);
+	else
+		i9xx_crtc_dpms(crtc, mode);
 
 	if (!dev->primary->master)
 		return;
@@ -947,6 +1370,12 @@ static bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
 				  struct drm_display_mode *mode,
 				  struct drm_display_mode *adjusted_mode)
 {
+	struct drm_device *dev = crtc->dev;
+	if (IS_IGDNG(dev)) {
+		/* FDI link clock is fixed at 2.7G */
+		if (mode->clock * 3 > 27000 * 4)
+			return MODE_CLOCK_HIGH;
+	}
 	return true;
 }
 
@@ -1030,6 +1459,48 @@ static int intel_panel_fitter_pipe (struct drm_device *dev)
 	return 1;
 }
 
+struct fdi_m_n {
+	u32        tu;
+	u32        gmch_m;
+	u32        gmch_n;
+	u32        link_m;
+	u32        link_n;
+};
+
+static void
+fdi_reduce_ratio(u32 *num, u32 *den)
+{
+	while (*num > 0xffffff || *den > 0xffffff) {
+		*num >>= 1;
+		*den >>= 1;
+	}
+}
+
+#define DATA_N 0x800000
+#define LINK_N 0x80000
+
+static void
+igdng_compute_m_n(int bytes_per_pixel, int nlanes,
+		int pixel_clock, int link_clock,
+		struct fdi_m_n *m_n)
+{
+	u64 temp;
+
+	m_n->tu = 64; /* default size */
+
+	temp = (u64) DATA_N * pixel_clock;
+	temp = div_u64(temp, link_clock);
+	m_n->gmch_m = (temp * bytes_per_pixel) / nlanes;
+	m_n->gmch_n = DATA_N;
+	fdi_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n);
+
+	temp = (u64) LINK_N * pixel_clock;
+	m_n->link_m = div_u64(temp, link_clock);
+	m_n->link_n = LINK_N;
+	fdi_reduce_ratio(&m_n->link_m, &m_n->link_n);
+}
+
+
 static int intel_crtc_mode_set(struct drm_crtc *crtc,
 			       struct drm_display_mode *mode,
 			       struct drm_display_mode *adjusted_mode,
@@ -1063,6 +1534,16 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 	struct drm_connector *connector;
 	const intel_limit_t *limit;
 	int ret;
+	struct fdi_m_n m_n = {0};
+	int data_m1_reg = (pipe == 0) ? PIPEA_DATA_M1 : PIPEB_DATA_M1;
+	int data_n1_reg = (pipe == 0) ? PIPEA_DATA_N1 : PIPEB_DATA_N1;
+	int link_m1_reg = (pipe == 0) ? PIPEA_LINK_M1 : PIPEB_LINK_M1;
+	int link_n1_reg = (pipe == 0) ? PIPEA_LINK_N1 : PIPEB_LINK_N1;
+	int pch_fp_reg = (pipe == 0) ? PCH_FPA0 : PCH_FPB0;
+	int pch_dpll_reg = (pipe == 0) ? PCH_DPLL_A : PCH_DPLL_B;
+	int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL;
+	u32 temp;
+	int sdvo_pixel_multiply;
 
 	drm_vblank_pre_modeset(dev, pipe);
 
@@ -1101,6 +1582,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 		DRM_DEBUG("using SSC reference clock of %d MHz\n", refclk / 1000);
 	} else if (IS_I9XX(dev)) {
 		refclk = 96000;
+		if (IS_IGDNG(dev))
+			refclk = 120000; /* 120Mhz refclk */
 	} else {
 		refclk = 48000;
 	}
@@ -1137,12 +1620,21 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 		}
 	}
 
+	/* FDI link */
+	if (IS_IGDNG(dev))
+		igdng_compute_m_n(3, 4, /* lane num 4 */
+				adjusted_mode->clock,
+				270000, /* lane clock */
+				&m_n);
+
 	if (IS_IGD(dev))
 		fp = (1 << clock.n) << 16 | clock.m1 << 8 | clock.m2;
 	else
 		fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
 
-	dpll = DPLL_VGA_MODE_DIS;
+	if (!IS_IGDNG(dev))
+		dpll = DPLL_VGA_MODE_DIS;
+
 	if (IS_I9XX(dev)) {
 		if (is_lvds)
 			dpll |= DPLLB_MODE_LVDS;
@@ -1150,17 +1642,22 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 			dpll |= DPLLB_MODE_DAC_SERIAL;
 		if (is_sdvo) {
 			dpll |= DPLL_DVO_HIGH_SPEED;
-			if (IS_I945G(dev) || IS_I945GM(dev)) {
-				int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+			sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+			if (IS_I945G(dev) || IS_I945GM(dev))
 				dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
-			}
+			else if (IS_IGDNG(dev))
+				dpll |= (sdvo_pixel_multiply - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT;
 		}
 
 		/* compute bitmask from p1 value */
 		if (IS_IGD(dev))
 			dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT_IGD;
-		else
+		else {
 			dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
+			/* also FPA1 */
+			if (IS_IGDNG(dev))
+				dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT;
+		}
 		switch (clock.p2) {
 		case 5:
 			dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5;
@@ -1175,7 +1672,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 			dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14;
 			break;
 		}
-		if (IS_I965G(dev))
+		if (IS_I965G(dev) && !IS_IGDNG(dev))
 			dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT);
 	} else {
 		if (is_lvds) {
@@ -1207,10 +1704,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 	/* Set up the display plane register */
 	dspcntr = DISPPLANE_GAMMA_ENABLE;
 
-	if (pipe == 0)
-		dspcntr |= DISPPLANE_SEL_PIPE_A;
-	else
-		dspcntr |= DISPPLANE_SEL_PIPE_B;
+	/* IGDNG's plane is forced to pipe, bit 24 is to
+	   enable color space conversion */
+	if (!IS_IGDNG(dev)) {
+		if (pipe == 0)
+			dspcntr |= DISPPLANE_SEL_PIPE_A;
+		else
+			dspcntr |= DISPPLANE_SEL_PIPE_B;
+	}
 
 	if (pipe == 0 && !IS_I965G(dev)) {
 		/* Enable pixel doubling when the dot clock is > 90% of the (display)
@@ -1231,12 +1732,17 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 
 
 	/* Disable the panel fitter if it was on our pipe */
-	if (intel_panel_fitter_pipe(dev) == pipe)
+	if (!IS_IGDNG(dev) && intel_panel_fitter_pipe(dev) == pipe)
 		I915_WRITE(PFIT_CONTROL, 0);
 
 	DRM_DEBUG("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
 	drm_mode_debug_printmodeline(mode);
 
+	/* assign to IGDNG registers */
+	if (IS_IGDNG(dev)) {
+		fp_reg = pch_fp_reg;
+		dpll_reg = pch_dpll_reg;
+	}
 
 	if (dpll & DPLL_VCO_ENABLE) {
 		I915_WRITE(fp_reg, fp);
@@ -1245,6 +1751,22 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 		udelay(150);
 	}
 
+	if (IS_IGDNG(dev)) {
+		/* enable PCH clock reference source */
+		/* XXX need to change the setting for other outputs */
+		u32 temp;
+		temp = I915_READ(PCH_DREF_CONTROL);
+		temp &= ~DREF_NONSPREAD_SOURCE_MASK;
+		temp |= DREF_NONSPREAD_CK505_ENABLE;
+		temp &= ~DREF_SSC_SOURCE_MASK;
+		temp |= DREF_SSC_SOURCE_ENABLE;
+		temp &= ~DREF_SSC1_ENABLE;
+		/* if no eDP, disable source output to CPU */
+		temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
+		temp |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
+		I915_WRITE(PCH_DREF_CONTROL, temp);
+	}
+
 	/* The LVDS pin pair needs to be on before the DPLLs are enabled.
 	 * This is an exception to the general rule that mode_set doesn't turn
 	 * things on.
@@ -1276,8 +1798,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 	/* Wait for the clocks to stabilize. */
 	udelay(150);
 
-	if (IS_I965G(dev)) {
-		int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+	if (IS_I965G(dev) && !IS_IGDNG(dev)) {
+		sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
 		I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) |
 			   ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT));
 	} else {
@@ -1303,9 +1825,25 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 	/* pipesrc and dspsize control the size that is scaled from, which should
 	 * always be the user's requested size.
 	 */
-	I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1));
-	I915_WRITE(dsppos_reg, 0);
+	if (!IS_IGDNG(dev)) {
+		I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) |
+				(mode->hdisplay - 1));
+		I915_WRITE(dsppos_reg, 0);
+	}
 	I915_WRITE(pipesrc_reg, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
+
+	if (IS_IGDNG(dev)) {
+		I915_WRITE(data_m1_reg, TU_SIZE(m_n.tu) | m_n.gmch_m);
+		I915_WRITE(data_n1_reg, TU_SIZE(m_n.tu) | m_n.gmch_n);
+		I915_WRITE(link_m1_reg, m_n.link_m);
+		I915_WRITE(link_n1_reg, m_n.link_n);
+
+		 /* enable FDI RX PLL too */
+		temp = I915_READ(fdi_rx_reg);
+		I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE);
+		udelay(200);
+	}
+
 	I915_WRITE(pipeconf_reg, pipeconf);
 	I915_READ(pipeconf_reg);
 
@@ -1336,6 +1874,11 @@ void intel_crtc_load_lut(struct drm_crtc *crtc)
 	if (!crtc->enabled)
 		return;
 
+	/* use legacy palette for IGDNG */
+	if (IS_IGDNG(dev))
+		palreg = (intel_crtc->pipe == 0) ? LGC_PALETTE_A :
+						   LGC_PALETTE_B;
+
 	for (i = 0; i < 256; i++) {
 		I915_WRITE(palreg + 4 * i,
 			   (intel_crtc->lut_r[i] << 16) |
@@ -1885,10 +2428,12 @@ static void intel_setup_outputs(struct drm_device *dev)
 	intel_crt_init(dev);
 
 	/* Set up integrated LVDS */
-	if (IS_MOBILE(dev) && !IS_I830(dev))
+	if (IS_MOBILE(dev) && !IS_I830(dev) && !IS_IGDNG(dev))
 		intel_lvds_init(dev);
 
-	if (IS_I9XX(dev)) {
+	if (IS_IGDNG(dev)) {
+		/* ignore for other outputs */
+	} else if (IS_I9XX(dev)) {
 		int found;
 		u32 reg;
 
@@ -1912,7 +2457,7 @@ static void intel_setup_outputs(struct drm_device *dev)
 	} else
 		intel_dvo_init(dev);
 
-	if (IS_I9XX(dev) && IS_MOBILE(dev))
+	if (IS_I9XX(dev) && IS_MOBILE(dev) && !IS_IGDNG(dev))
 		intel_tv_init(dev);
 
 	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-- 
1.6.3




More information about the Intel-gfx mailing list