xf86-video-intel: Branch 'display-port' - 3 commits - src/i810_reg.h src/i830_dp.c src/i830_dp.h

Keith Packard keithp at kemper.freedesktop.org
Fri Mar 20 00:08:56 PDT 2009


 src/i810_reg.h |    4 
 src/i830_dp.c  |  443 ++++++++++++++++++++++++++++++++++++++-------------------
 src/i830_dp.h  |   35 +++-
 3 files changed, 332 insertions(+), 150 deletions(-)

New commits:
commit 561ac52f824183dbac9f22a0b51d34d718bb8f91
Author: Keith Packard <keithp at keithp.com>
Date:   Fri Mar 20 00:06:47 2009 -0700

    Follow VESA algorithm for DP training
    
    The DP spec gives a very clear sequence of operations to perform
    DP training. Following that appears to rapidly converge on the
    correct voltage and pre-emphasis values.
    
    Signed-off-by: Keith Packard <keithp at keithp.com>

diff --git a/src/i830_dp.c b/src/i830_dp.c
index 8581fd2..d01cb4c 100644
--- a/src/i830_dp.c
+++ b/src/i830_dp.c
@@ -158,8 +158,6 @@ i830_dp_aux_ch(ScrnInfoPtr pScrn, uint32_t output_reg,
     for (i = 0; i < send_bytes; i += 4) {
 	uint32_t    d = pack_aux(send + i, send_bytes - i);;
 
-//	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
-//		   "aux_ch send[%d] = %08x\n", i/4, d);
 	OUTREG(ch_data + i, d);
     }
 
@@ -208,8 +206,6 @@ i830_dp_aux_ch(ScrnInfoPtr pScrn, uint32_t output_reg,
     for (i = 0; i < recv_bytes; i += 4) {
 	uint32_t    d = INREG(ch_data + i);
 
-//	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
-//		   "aux_ch recv[%d] = %08x\n", i/4, d);
 	unpack_aux(d, recv + i, recv_bytes - i);
     }
 
@@ -381,8 +377,10 @@ static Bool
 i830_dp_mode_fixup(xf86OutputPtr output, DisplayModePtr mode,
 		     DisplayModePtr adjusted_mode)
 {
+    ScrnInfoPtr	pScrn = output->scrn;
     I830OutputPrivatePtr intel_output = output->driver_private;
     struct i830_dp_priv *dev_priv = intel_output->dev_priv;
+    I830Ptr pI830 = I830PTR(pScrn);
     int	lane_count, clock;
     int max_lane_count = i830_dp_max_lane_count(output);
     int max_clock = i830_dp_max_link_bw(output) == DP_LINK_BW_2_7 ? 1 : 0;;
@@ -399,9 +397,11 @@ i830_dp_mode_fixup(xf86OutputPtr output, DisplayModePtr mode,
 		dev_priv->link_bw = bws[clock];
 		dev_priv->lane_count = lane_count;
 		adjusted_mode->Clock = i830_dp_link_clock(dev_priv->link_bw);
-		xf86DrvMsg(0, X_INFO,
-			   "link_bw %d lane_count %d clock %d\n",
-			   dev_priv->link_bw, dev_priv->lane_count, adjusted_mode->Clock);
+		if (pI830->debug_modes)
+			xf86DrvMsg(0, X_INFO,
+				   "link_bw %d lane_count %d clock %d\n",
+				   dev_priv->link_bw, dev_priv->lane_count,
+				   adjusted_mode->Clock);
 		return TRUE;
 	    }
 	}
@@ -551,8 +551,10 @@ i830_dp_dpms(xf86OutputPtr output, int mode)
     }
 }
 
-static int
-i830_dp_lane_status(xf86OutputPtr output, uint8_t lane_status[3])
+#define DP_LANE_STATUS_SIZE	6
+
+static Bool
+i830_dp_lane_status(xf86OutputPtr output, uint8_t lane_status[DP_LANE_STATUS_SIZE])
 {
     ScrnInfoPtr pScrn = output->scrn;
     I830OutputPrivatePtr intel_output = output->driver_private;
@@ -561,11 +563,13 @@ i830_dp_lane_status(xf86OutputPtr output, uint8_t lane_status[3])
 
     ret = i830_dp_aux_native_read(pScrn,
 				  dev_priv->output_reg, DP_LANE0_1_STATUS,
-				  lane_status, 3);
-    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
-	       "lane status(%d) %02x %02x %02x\n",
-	       ret, lane_status[0], lane_status[1], lane_status[2]);
-    return ret;
+				  lane_status, DP_LANE_STATUS_SIZE);
+    if (ret != DP_LANE_STATUS_SIZE) {
+	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+		   "lane status failed\n");
+	return FALSE;
+    }
+    return ret == 6;
 }
 
 static void
@@ -579,17 +583,68 @@ i830_dp_save(xf86OutputPtr output)
     dev_priv->save_DP = INREG(dev_priv->output_reg);
     i830_dp_aux_native_read(pScrn, dev_priv->output_reg, 0x100,
 		     dev_priv->save_link_configuration, sizeof (dev_priv->save_link_configuration));
-    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
-	       "link configuration: %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
-	       dev_priv->save_link_configuration[0],
-	       dev_priv->save_link_configuration[1],
-	       dev_priv->save_link_configuration[2],
-	       dev_priv->save_link_configuration[3],
-	       dev_priv->save_link_configuration[4],
-	       dev_priv->save_link_configuration[5],
-	       dev_priv->save_link_configuration[6],
-	       dev_priv->save_link_configuration[7],
-	       dev_priv->save_link_configuration[8]);
+    if (pI830->debug_modes)
+	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+		   "link configuration: %02x %02x %02x %02x "
+		   "%02x %02x %02x %02x %02x\n",
+		   dev_priv->save_link_configuration[0],
+		   dev_priv->save_link_configuration[1],
+		   dev_priv->save_link_configuration[2],
+		   dev_priv->save_link_configuration[3],
+		   dev_priv->save_link_configuration[4],
+		   dev_priv->save_link_configuration[5],
+		   dev_priv->save_link_configuration[6],
+		   dev_priv->save_link_configuration[7],
+		   dev_priv->save_link_configuration[8]);
+}
+
+static uint8_t
+i830_get_adjust_request_voltage(uint8_t lane_status[DP_LANE_STATUS_SIZE],
+				int lane)
+{
+    int	    i = DP_ADJUST_REQUEST_LANE0_1 - DP_LANE0_1_STATUS + (lane >> 1);
+    int	    s = (lane & 1) ? DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT;
+    uint8_t l = lane_status[i];
+
+    return (l >> s) & 3;
+}
+
+static uint8_t
+i830_get_adjust_request_pre_emphasis(uint8_t lane_status[DP_LANE_STATUS_SIZE],
+				     int lane)
+{
+    int	    i = DP_ADJUST_REQUEST_LANE0_1 - DP_LANE0_1_STATUS + (lane >> 1);
+    int	    s = (lane & 1) ? DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT : DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT;
+    uint8_t l = lane_status[i];
+
+    return (l >> s) & 3;
+}
+
+static uint8_t
+i830_get_adjust_train(uint8_t lane_status[DP_LANE_STATUS_SIZE],
+		      int lane)
+{
+    uint8_t v = i830_get_adjust_request_voltage(lane_status, lane);
+    uint8_t p = i830_get_adjust_request_pre_emphasis(lane_status, lane);
+    uint8_t t;
+
+    t = (v << DP_TRAIN_VOLTAGE_SWING_SET_SHIFT) | (p << DP_TRAIN_PRE_EMPHASIS_SHIFT);
+    if (v == 3)
+	t |= DP_TRAIN_MAX_SWING_REACHED;
+    if (v + p == 3)
+	t |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+    return t;
+}
+
+static uint8_t
+i830_get_lane_status(uint8_t lane_status[DP_LANE_STATUS_SIZE],
+		     int lane)
+{
+    int i = (lane >> 1);
+    int s = (lane & 1) * 4;
+    uint8_t l = lane_status[i];
+
+    return (l >> s) & 0xf;
 }
 
 static void
@@ -599,18 +654,16 @@ i830_dp_link_train(xf86OutputPtr output, uint32_t DP)
     I830OutputPrivatePtr intel_output = output->driver_private;
     struct i830_dp_priv *dev_priv = intel_output->dev_priv;
     I830Ptr pI830 = I830PTR(pScrn);
-    uint8_t	training[10];
     uint8_t	train_set[4];
-    uint8_t lane_status[3];
-    uint8_t adjust_request[10];
+    uint8_t lane_status[DP_LANE_STATUS_SIZE];
     int ret;
     int i;
-    int voltage;
-    int pre_emphasis;
+    uint8_t voltage = 0;
+    uint8_t pre_emphasis = 0;
     Bool clock_recovery = FALSE;
+    Bool channel_eq = FALSE;
+    int tries;
 
-    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
-	       "i830_dp_link_train dp 0x%08x\n", DP);
     if ((DP & DP_PORT_EN) == 0) {
 	OUTREG(dev_priv->output_reg, DP);
 	POSTING_READ(dev_priv->output_reg);
@@ -618,104 +671,143 @@ i830_dp_link_train(xf86OutputPtr output, uint32_t DP)
     }
     DP &= ~DP_LINK_TRAIN_MASK;
 
-    usleep (20*1000);
+    memset(train_set, 0, 4);
+    voltage = 0xff;
+    tries = 0;
+    clock_recovery = FALSE;
+    for (;;) {
+	/* Set training pattern 1 */
+	OUTREG(dev_priv->output_reg, DP | DP_LINK_TRAIN_PAT_1);
+	POSTING_READ(dev_priv->output_reg);
+	i830_dp_aux_native_write_1(pScrn, dev_priv->output_reg,
+				   DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_1);
 
-    ret = i830_dp_aux_native_read(pScrn, dev_priv->output_reg,
-			   0x206, adjust_request, 2);
-    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
-	       "adjust_request(%d): %02x %02x\n",
-	       ret, adjust_request[0], adjust_request[1]);
-    /* main link disabled */
-    OUTREG(dev_priv->output_reg, DP | DP_LINK_TRAIN_PAT_1);
-    POSTING_READ(dev_priv->output_reg);
-    usleep (15*1000);
-    i830_dp_aux_native_write_1(pScrn, dev_priv->output_reg,
-			DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_1);
-    usleep (15*1000);
-    ret = i830_dp_aux_native_read(pScrn, dev_priv->output_reg,
-			   DP_TRAINING_PATTERN_SET,
-			   training, 1);
-    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
-	       "training pattern 1: %02x\n", training[0]);
-    /* clock recovery pattern */
-    for (voltage = 0; voltage < 4; voltage++) {
-	for (pre_emphasis = 0; pre_emphasis < 4; pre_emphasis++) {
-	    memset(train_set, (voltage << 0) | (pre_emphasis << 3), 4);
-	    ret = i830_dp_aux_native_write(pScrn, dev_priv->output_reg,
-					   DP_TRAINING_LANE0_SET, train_set, 4);
-	    if (ret != 4) {
-		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
-			   "voltage %d pre_emphasis %d set failed\n",
-			   voltage, pre_emphasis);
-		continue;
-	    }
-	    for (i = 0; i < 4; i++) {
-		usleep (15*1000);
-		ret = i830_dp_lane_status(output, lane_status);
-		if (ret != 3)
-		    continue;
-		if ((lane_status[0] & DP_LANE_CR_DONE) &&
-		    (dev_priv->lane_count < 2 || (lane_status[0] & (DP_LANE_CR_DONE << 4))) &&
-		    (dev_priv->lane_count < 3 || (lane_status[1] & DP_LANE_CR_DONE)) &&
-		    (dev_priv->lane_count < 4 || (lane_status[1] & (DP_LANE_CR_DONE << 4))))
-		{
-		    clock_recovery = TRUE;
-		}
-		if (clock_recovery)
-		    break;
-	    }
-	    if (clock_recovery)
+	ret = i830_dp_aux_native_write(pScrn, dev_priv->output_reg,
+				       DP_TRAINING_LANE0_SET, train_set, 4);
+	if (ret != 4) {
+	    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+		       "clock recovery failed: couldn't write train set\n");
+	    break;
+	}
+
+	usleep(400);
+        if (!i830_dp_lane_status(output, lane_status))
+	    break;
+
+	/* Check for DONE on all channels */
+	for (i = 0; i < dev_priv->lane_count; i++) {
+	    uint8_t s = i830_get_lane_status(lane_status, i);
+	    if ((s & DP_LANE_CR_DONE) == 0) {
 		break;
+	    }
+	}
+	if (i == dev_priv->lane_count) {
+	    clock_recovery = TRUE;
+	    voltage = train_set[0] & DP_TRAIN_VOLTAGE_SWING_SET_MASK;
+	    pre_emphasis = (train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
+	    break;
 	}
-	if (clock_recovery)
+
+	/* Check to see if we've tried the max voltage */
+	for (i = 0; i < dev_priv->lane_count; i++)
+	    if ((train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
+		break;
+	if (i == dev_priv->lane_count) {
+	    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+		       "clock recovery reached max voltage\n");
 	    break;
+	}
+
+	/* Check to see if we've tried the same voltage 5 times */
+	if ((train_set[0] & DP_TRAIN_VOLTAGE_SWING_SET_MASK) == voltage) {
+	    ++tries;
+	    if (tries == 5) {
+		xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+			   "clock recovery tried 5 times\n");
+		break;
+	    }
+	} else
+	    tries = 0;
+	voltage = train_set[0] & DP_TRAIN_VOLTAGE_SWING_SET_MASK;
+
+	/* Compute new train_set as requested by target */
+	for (i = 0; i < dev_priv->lane_count; i++)
+	    train_set[i] = i830_get_adjust_train(lane_status, i);
     }
-    if (!clock_recovery) {
+    if (!clock_recovery)
 	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
 		   "clock recovery failed\n");
-    } else {
-	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
-		   "clock recovery at voltage %d pre_emphasis %d\n",
+    else if (pI830->debug_modes)
+	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+		   "clock recovery at voltage %d pre-emphasis %d\n",
 		   voltage, pre_emphasis);
-    }
-    /* channel eq pattern */
-    OUTREG(dev_priv->output_reg, DP | DP_LINK_TRAIN_PAT_2);
-    POSTING_READ(dev_priv->output_reg);
-    usleep (15*1000);
-    i830_dp_aux_native_write_1(pScrn, dev_priv->output_reg,
-			DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_2);
-    usleep(15*1000);
-    ret = i830_dp_aux_native_read(pScrn, dev_priv->output_reg,
-			   DP_TRAINING_PATTERN_SET,
-			   training, 1);
-    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
-	       "training pattern 2: %02x\n", training[0]);
-    for (i = 0; i < 4; i++) {
-	int ret;
 
+    /* channel equalization */
+    tries = 0;
+    channel_eq = FALSE;
+    for (;;) {
+	/* channel eq pattern */
+	OUTREG(dev_priv->output_reg, DP | DP_LINK_TRAIN_PAT_2);
+	POSTING_READ(dev_priv->output_reg);
+	i830_dp_aux_native_write_1(pScrn, dev_priv->output_reg,
+				   DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_2);
+
+	/* assign the current training values */
 	ret = i830_dp_aux_native_write(pScrn, dev_priv->output_reg,
-				DP_TRAINING_LANE0_SET, train_set, 4);
+				       DP_TRAINING_LANE0_SET, train_set, 4);
 	if (ret != 4) {
 	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
-		       "voltage swing set failed\n");
+		       "train set failed\n");
 	    break;
 	}
 
-	usleep(15*1000);
-	ret = i830_dp_lane_status(output, lane_status);
-	if (ret != 3)
+	usleep(400);
+        if (!i830_dp_lane_status(output, lane_status))
 	    break;
-	if ((lane_status[0] & DP_LANE_CHANNEL_EQ_DONE) &&
-	    (dev_priv->lane_count < 2 || (lane_status[0] & (DP_LANE_CHANNEL_EQ_DONE<<4))) &&
-	    (dev_priv->lane_count < 3 || (lane_status[1] & DP_LANE_CHANNEL_EQ_DONE)) &&
-	    (dev_priv->lane_count < 4 || (lane_status[1] & (DP_LANE_CHANNEL_EQ_DONE<<4))))
+
+	channel_eq = TRUE;
+#define CHANNEL_EQ_BITS (DP_LANE_CR_DONE|\
+			 DP_LANE_CHANNEL_EQ_DONE|\
+			 DP_LANE_SYMBOL_LOCKED)
+
+	/* Check for DONE|EQ_DONE|SYMBOL_LOCKED on all channels */
+	for (i = 0; i < dev_priv->lane_count; i++) {
+	    uint8_t s = i830_get_lane_status(lane_status, i);
+	    if ((s & (CHANNEL_EQ_BITS)) != CHANNEL_EQ_BITS)
+		break;
+	}
+
+	/* Check for INTERLANE_ALIGN */
+	if (i == dev_priv->lane_count &&
+	    (lane_status[DP_LANE_ALIGN_STATUS_UPDATED -
+	    DP_LANE0_1_STATUS] & DP_INTERLANE_ALIGN_DONE) != 0)
+	{
+	    channel_eq = TRUE;
+	    voltage = train_set[0] & DP_TRAIN_VOLTAGE_SWING_SET_MASK;
+	    pre_emphasis = (train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
 	    break;
-	if (i == 3) {
-	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
-		       "channel eq failed\n");
+	}
+
+	/* Try 5 times */
+	if (tries > 5) {
+	    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+		       "channel eq failed: 5 tries\n");
 	    break;
 	}
+
+	/* Compute new train_set as requested by target */
+	for (i = 0; i < dev_priv->lane_count; i++)
+	    train_set[i] = i830_get_adjust_train(lane_status, i);
+	++tries;
     }
+    if (!channel_eq)
+	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+		   "channel eq failed\n");
+    else if (pI830->debug_modes)
+	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+		   "channel eq at voltage %d pre-emphasis %d\n",
+		   voltage, pre_emphasis);
+
     OUTREG(dev_priv->output_reg, DP | DP_LINK_TRAIN_OFF);
     POSTING_READ(dev_priv->output_reg);
     usleep (15*1000);
@@ -856,9 +948,6 @@ i830_dp_restore(xf86OutputPtr output)
 
     i830_dp_aux_native_write(pScrn, dev_priv->output_reg, 0x100,
 		     dev_priv->save_link_configuration, sizeof (dev_priv->save_link_configuration));
-    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
-	       "%s: i830_dp_link_train\n", __func__);
-    i830DumpRegs(pScrn);
     i830_dp_link_train(output, dev_priv->save_DP);
 }
 
@@ -1028,9 +1117,11 @@ i830_dp_init(ScrnInfoPtr pScrn, int output_reg)
 		     (output_reg == DP_C) ? "DPDDC-C" : "DPDDC_D");
 
 
-    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
-	       "DP output %d detected\n",
-	       (output_reg == DP_B) ? 1 :
-	       (output_reg == DP_C) ? 2 : 3);
+    if (pI830->debug_modes) {
+	    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+		       "DP output %d detected\n",
+		       (output_reg == DP_B) ? 1 :
+		       (output_reg == DP_C) ? 2 : 3);
+    }
     return TRUE;
 }
diff --git a/src/i830_dp.h b/src/i830_dp.h
index 9fd54ac..f53b9de 100644
--- a/src/i830_dp.h
+++ b/src/i830_dp.h
@@ -74,13 +74,13 @@
 #define DP_TRAINING_LANE2_SET		    0x105
 #define DP_TRAINING_LANE3_SET		    0x106
 
-# define DP_VOLTAGE_SWING_SET_MASK	    0x3
-# define DP_VOLTAGE_SWING_SET_SHIFT	    0
-# define DP_MAX_SWING_REACHED		    (1 << 2)
+# define DP_TRAIN_VOLTAGE_SWING_SET_MASK    0x3
+# define DP_TRAIN_VOLTAGE_SWING_SET_SHIFT   0
+# define DP_TRAIN_MAX_SWING_REACHED	    (1 << 2)
 
-# define DP_TRAINING_PRE_EMPHASIS_MASK	    (3 << 3)
-# define DP_PRE_EMPHASIS_SHIFT		    0
-# define DP_MAX_PRE_EMPHASIS_REACHED	    (1 << 5)
+# define DP_TRAIN_PRE_EMPHASIS_MASK	    (3 << 3)
+# define DP_TRAIN_PRE_EMPHASIS_SHIFT	    3
+# define DP_TRAIN_MAX_PRE_EMPHASIS_REACHED  (1 << 5)
 
 #define DP_DOWNSPREAD_CTRL		    0x107
 # define DP_SPREAD_AMP_0_5		    (1 << 4)
@@ -94,3 +94,26 @@
 # define DP_LANE_CR_DONE		    (1 << 0)
 # define DP_LANE_CHANNEL_EQ_DONE	    (1 << 1)
 # define DP_LANE_SYMBOL_LOCKED		    (1 << 2)
+
+#define DP_LANE_ALIGN_STATUS_UPDATED	    0x204
+
+#define DP_INTERLANE_ALIGN_DONE		    (1 << 0)
+#define DP_DOWNSTREAM_PORT_STATUS_CHANGED   (1 << 6)
+#define DP_LINK_STATUS_UPDATED		    (1 << 7)
+
+#define DP_SINK_STATUS			    0x205
+
+#define DP_RECEIVE_PORT_0_STATUS	    (1 << 0)
+#define DP_RECEIVE_PORT_1_STATUS	    (1 << 1)
+
+#define DP_ADJUST_REQUEST_LANE0_1	    0x206
+#define DP_ADJUST_REQUEST_LANE2_3	    0x207
+
+#define DP_ADJUST_VOLTAGE_SWING_LANE0_MASK  0x03
+#define DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT 0
+#define DP_ADJUST_PRE_EMPHASIS_LANE0_MASK   0x0c
+#define DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT  2
+#define DP_ADJUST_VOLTAGE_SWING_LANE1_MASK  0x30
+#define DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT 4
+#define DP_ADJUST_PRE_EMPHASIS_LANE1_MASK   0xc0
+#define DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT  6
commit 30697ee9673cb93fd41cc73fd2097b1773a64752
Author: Keith Packard <keithp at keithp.com>
Date:   Thu Mar 19 21:35:29 2009 -0700

    Respect DP sink limits in DP mode setting.
    
    The DP sink can limit the number of lanes and the bandwidth per lane, so
    make sure the driver looks for those. This involved looking at the DPCD
    values quite often, so those are now saved in the detection code and used
    elsewhere in the driver.
    
    Signed-off-by: Keith Packard <keithp at keithp.com>

diff --git a/src/i830_dp.c b/src/i830_dp.c
index a9e2690..8581fd2 100644
--- a/src/i830_dp.c
+++ b/src/i830_dp.c
@@ -45,21 +45,47 @@ struct i830_dp_priv {
     uint8_t link_bw;
     uint8_t lane_count;
     Bool i2c_running;
+    uint8_t dpcd[4];
 };
 
 static void
 i830_dp_link_train(xf86OutputPtr output, uint32_t DP);
 
 static int
-i830_dp_mode_valid(xf86OutputPtr output, DisplayModePtr mode)
+i830_dp_max_lane_count(xf86OutputPtr output)
 {
-    if (mode->Clock > 270000)
-	return MODE_CLOCK_HIGH;
+    I830OutputPrivatePtr intel_output = output->driver_private;
+    struct i830_dp_priv *dev_priv = intel_output->dev_priv;
+    int max_lane_count = 4;
 
-    if (mode->Clock < 20000)
-	return MODE_CLOCK_LOW;
+    if (dev_priv->dpcd[0] >= 0x11) {
+	max_lane_count = dev_priv->dpcd[2] & 0x1f;
+	switch (max_lane_count) {
+	case 1: case 2: case 4:
+	    break;
+	default:
+	    max_lane_count = 4;
+	}
+    }
+    return max_lane_count;
+}
 
-    return MODE_OK;
+static int
+i830_dp_max_link_bw(xf86OutputPtr output)
+{
+    I830OutputPrivatePtr intel_output = output->driver_private;
+    struct i830_dp_priv *dev_priv = intel_output->dev_priv;
+    int max_link_bw = dev_priv->dpcd[1];
+
+    switch (max_link_bw) {
+    case DP_LINK_BW_1_62:
+    case DP_LINK_BW_2_7:
+	break;
+    default:
+	max_link_bw = DP_LINK_BW_1_62;
+	break;
+    }
+    return max_link_bw;
 }
 
 static int
@@ -71,19 +97,25 @@ i830_dp_link_clock(uint8_t link_bw)
 	return 162000;
 }
 
-static Bool
-i830_dp_mode_fixup(xf86OutputPtr output, DisplayModePtr mode,
-		     DisplayModePtr adjusted_mode)
+static int
+i830_dp_link_required(int pixel_clock)
 {
-    I830OutputPrivatePtr intel_output = output->driver_private;
-    struct i830_dp_priv *dev_priv = intel_output->dev_priv;
+    return pixel_clock * 3;
+}
 
-    /* XXX compute these values instead of just setting them */
-    dev_priv->link_bw = DP_LINK_BW_1_62;
-    dev_priv->lane_count = 4;
-    /* Pin the CRTC to the LS_Clk value (162 or 270 MHz) */
-    adjusted_mode->Clock = i830_dp_link_clock(dev_priv->link_bw);
-    return TRUE;
+static int
+i830_dp_mode_valid(xf86OutputPtr output, DisplayModePtr mode)
+{
+    int max_link_clock = i830_dp_link_clock(i830_dp_max_link_bw(output));
+    int max_lanes = i830_dp_max_lane_count(output);
+
+    if (i830_dp_link_required(mode->Clock) > max_link_clock * max_lanes)
+	return MODE_CLOCK_HIGH;
+
+    if (mode->Clock < 10000)
+	return MODE_CLOCK_LOW;
+
+    return MODE_OK;
 }
 
 static uint32_t
@@ -345,6 +377,38 @@ i830_dp_aux_i2c_transaction(ScrnInfoPtr pScrn, uint32_t output_reg,
     }
 }
 
+static Bool
+i830_dp_mode_fixup(xf86OutputPtr output, DisplayModePtr mode,
+		     DisplayModePtr adjusted_mode)
+{
+    I830OutputPrivatePtr intel_output = output->driver_private;
+    struct i830_dp_priv *dev_priv = intel_output->dev_priv;
+    int	lane_count, clock;
+    int max_lane_count = i830_dp_max_lane_count(output);
+    int max_clock = i830_dp_max_link_bw(output) == DP_LINK_BW_2_7 ? 1 : 0;;
+    static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 };
+
+    for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1)
+    {
+	for (clock = 0; clock <= max_clock; clock++)
+	{
+	    int link_avail = i830_dp_link_clock(bws[clock]) * lane_count;
+
+	    if (i830_dp_link_required(mode->Clock) <= link_avail)
+	    {
+		dev_priv->link_bw = bws[clock];
+		dev_priv->lane_count = lane_count;
+		adjusted_mode->Clock = i830_dp_link_clock(dev_priv->link_bw);
+		xf86DrvMsg(0, X_INFO,
+			   "link_bw %d lane_count %d clock %d\n",
+			   dev_priv->link_bw, dev_priv->lane_count, adjusted_mode->Clock);
+		return TRUE;
+	    }
+	}
+    }
+    return FALSE;
+}
+
 struct i830_dp_m_n {
 	uint32_t	tu;
 	uint32_t	gmch_m;
@@ -395,7 +459,6 @@ i830_dp_mode_set(xf86OutputPtr output, DisplayModePtr mode,
     uint32_t dp;
     struct i830_dp_m_n m_n;
     uint8_t link_configuration[0x9];
-    uint8_t dpcd[4];
 
     /*
      * Compute the GMCH and Link ratios. The '3' here is
@@ -451,13 +514,9 @@ i830_dp_mode_set(xf86OutputPtr output, DisplayModePtr mode,
      * Check for DPCD version > 1.1,
      * enable enahanced frame stuff in that case
      */
-    if (i830_dp_aux_native_read(pScrn, dev_priv->output_reg,
-				0, dpcd, sizeof (dpcd)) == sizeof (dpcd))
-    {
-	if (dpcd[0] >= 0x11) {
-	    link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
-	    dp |= DP_ENHANCED_FRAMING;
-	}
+    if (dev_priv->dpcd[0] >= 0x11) {
+	link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+	dp |= DP_ENHANCED_FRAMING;
     }
 
     i830_dp_aux_native_write(pScrn, dev_priv->output_reg, 0x100,
@@ -827,7 +886,6 @@ i830_dp_detect(xf86OutputPtr output)
     I830Ptr pI830 = I830PTR(pScrn);
     uint32_t temp, bit;
     xf86OutputStatus status;
-    uint8_t dpcd[4];
 
     dev_priv->has_audio = FALSE;
 
@@ -871,9 +929,10 @@ i830_dp_detect(xf86OutputPtr output)
 
     status = XF86OutputStatusDisconnected;
     if (i830_dp_aux_native_read(pScrn, dev_priv->output_reg,
-				0, dpcd, sizeof (dpcd)) == sizeof (dpcd))
+				0, dev_priv->dpcd,
+				sizeof (dev_priv->dpcd)) == sizeof (dev_priv->dpcd))
     {
-	if (dpcd[0] != 0)
+	if (dev_priv->dpcd[0] != 0)
 	    status = XF86OutputStatusConnected;
     }
     if (pI830->debug_modes)
commit 725551605bdd706e3cb2bf604ed21f5c0e615a94
Author: Keith Packard <keithp at keithp.com>
Date:   Thu Mar 19 20:10:53 2009 -0700

    The GMCH ratio uses the bytes of RGB data out of the LUT, not the FB
    
    Using the FB format makes no sense when you think about the VGA plane and
    the cursor, but using the final mixed RGB format does make sense. This also
    fixes the weird (nlanes+1) hack.
    
    Signed-off-by: Keith Packard <keithp at keithp.com>

diff --git a/src/i810_reg.h b/src/i810_reg.h
index 3af027d..5a7ac7b 100644
--- a/src/i810_reg.h
+++ b/src/i810_reg.h
@@ -2297,6 +2297,10 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  * ls_clk (we assume) is the DP link clock (1.62 or 2.7 GHz)
  *
  * The GMCH value is used internally
+ *
+ * bytes_per_pixel is the number of bytes coming out of the plane,
+ * which is after the LUTs, so we want the bytes for our color format.
+ * For our current usage, this is always 3, one byte for R, G and B.
  */
 #define PIPEA_GMCH_DATA_M	0x70050
 
diff --git a/src/i830_dp.c b/src/i830_dp.c
index 1005f9a..a9e2690 100644
--- a/src/i830_dp.c
+++ b/src/i830_dp.c
@@ -375,7 +375,7 @@ i830_dp_compute_m_n(int bytes_per_pixel,
 	 * I do not understand this one -- the docs say nlanes,
 	 * and yet (nlanes + 1) appears to be the correct value
 	 */
-	m_n->gmch_n = link_clock * (nlanes + 1);
+	m_n->gmch_n = link_clock * nlanes;
 	i830_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n);
 	m_n->link_m = pixel_clock;
 	m_n->link_n = link_clock;
@@ -397,7 +397,12 @@ i830_dp_mode_set(xf86OutputPtr output, DisplayModePtr mode,
     uint8_t link_configuration[0x9];
     uint8_t dpcd[4];
 
-    i830_dp_compute_m_n(pI830->cpp, dev_priv->lane_count,
+    /*
+     * Compute the GMCH and Link ratios. The '3' here is
+     * the number of bytes_per_pixel post-LUT, which we always
+     * set up for 8-bits of R/G/B, or 3 bytes total.
+     */
+    i830_dp_compute_m_n(3, dev_priv->lane_count,
 			mode->Clock, adjusted_mode->Clock, &m_n);
 
     if (intel_crtc->pipe == 0) {


More information about the xorg-commit mailing list