[Intel-gfx] [PATCH 10/17] drm/i915: Add Display Port support

Keith Packard keithp at keithp.com
Sun May 31 05:42:34 CEST 2009


Signed-off-by: Keith Packard <keithp at keithp.com>
---
 drivers/gpu/drm/i915/Makefile        |    1 +
 drivers/gpu/drm/i915/intel_display.c |   13 +-
 drivers/gpu/drm/i915/intel_dp.c      |  840 ++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_dp.h      |  133 ++++++
 drivers/gpu/drm/i915/intel_drv.h     |    1 +
 5 files changed, 987 insertions(+), 1 deletions(-)
 create mode 100644 drivers/gpu/drm/i915/intel_dp.c
 create mode 100644 drivers/gpu/drm/i915/intel_dp.h

diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 51c5a05..184b8bf 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -13,6 +13,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \
 	  intel_crt.o \
 	  intel_lvds.o \
 	  intel_bios.o \
+	  intel_dp.o \
 	  intel_hdmi.o \
 	  intel_sdvo.o \
 	  intel_modes.o \
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 504a69f..1fc7bb0 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -29,6 +29,7 @@
 #include "intel_drv.h"
 #include "i915_drm.h"
 #include "i915_drv.h"
+#include "intel_dp.h"
 
 #include "drm_crtc_helper.h"
 
@@ -1058,7 +1059,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 	intel_clock_t clock;
 	u32 dpll = 0, fp = 0, dspcntr, pipeconf;
 	bool ok, is_sdvo = false, is_dvo = false;
-	bool is_crt = false, is_lvds = false, is_tv = false;
+	bool is_crt = false, is_lvds = false, is_tv = false, is_dp = false;
 	struct drm_mode_config *mode_config = &dev->mode_config;
 	struct drm_connector *connector;
 	const intel_limit_t *limit;
@@ -1091,6 +1092,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 		case INTEL_OUTPUT_ANALOG:
 			is_crt = true;
 			break;
+		case INTEL_OUTPUT_DISPLAYPORT:
+			is_dp = true;
+			break;
 		}
 
 		num_outputs++;
@@ -1155,6 +1159,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 				dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
 			}
 		}
+		if (is_dp)
+			dpll |= DPLL_DVO_HIGH_SPEED;
 
 		/* compute bitmask from p1 value */
 		if (IS_IGD(dev))
@@ -1270,6 +1276,11 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 		I915_READ(LVDS);
 	}
 
+	/* The Display Port M/N ratio needs to be set before the DPLL is enabled
+	 */
+	if (is_dp)
+		intel_dp_set_m_n(crtc, mode, adjusted_mode);
+
 	I915_WRITE(fp_reg, fp);
 	I915_WRITE(dpll_reg, dpll);
 	I915_READ(dpll_reg);
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
new file mode 100644
index 0000000..5d07f14
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -0,0 +1,840 @@
+/*
+ * Copyright © 2008 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Keith Packard <keithp at keithp.com>
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+#include "intel_dp_regs.h"
+
+#undef SDVO_DEBUG
+
+#define DP_LINK_STATUS_SIZE	6
+#define DP_LINK_CHECK_TIMEOUT	(10 * 1000)
+
+#define DP_LINK_CONFIGURATION_SIZE	9
+
+struct intel_dp_priv {
+	/* I2C over AUX */
+	uint16_t i2c_address;
+	Bool i2c_running;
+	
+	/* Output config register */
+	uint32_t output_reg;
+	
+	/* Desired output config register state */
+	uint32_t DP;
+	/* Desire link config state */
+	uint8_t  link_configuration[DP_LINK_CONFIGURATION_SIZE];
+
+	/* Saved output config state */
+	uint32_t save_DP;
+	uint8_t  save_link_configuration[DP_LINK_CONFIGURATION_SIZE];
+
+	/* whether the sink supports audio (currently unset) */
+	Bool has_audio;
+
+	/* available link bandwidth */
+	uint8_t link_bw;
+	
+	/* available lane count */
+	uint8_t lane_count;
+
+	/* Device configuration information */
+	uint8_t dpcd[4];
+};
+
+static void
+intel_dp_link_train(xf86OutputPtr output, uint32_t DP,
+		    uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE]);
+
+static void
+intel_dp_link_down(xf86OutputPtr output, uint32_t DP);
+
+static int
+intel_dp_max_lane_count(xf86OutputPtr output)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_output *intel_output = enc_to_intel_output(encoder);
+	struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+	int max_lane_count = 4;
+
+	if (dp_priv->dpcd[0] >= 0x11) {
+		max_lane_count = dp_priv->dpcd[2] & 0x1f;
+		switch (max_lane_count) {
+		case 1: case 2: case 4:
+			break;
+		default:
+			max_lane_count = 4;
+		}
+	}
+	return max_lane_count;
+}
+
+static int
+intel_dp_max_link_bw(xf86OutputPtr output)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_output *intel_output = enc_to_intel_output(encoder);
+	struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+	int max_link_bw = dp_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
+intel_dp_link_clock(uint8_t link_bw)
+{
+	if (link_bw == DP_LINK_BW_2_7)
+		return 270000;
+	else
+		return 162000;
+}
+
+/* Return DP link clock rate.
+ *
+ * This driver always encodes 3 bytes per pixel (RGB)
+ */
+static int
+intel_dp_link_required(int pixel_clock)
+{
+	return pixel_clock * 3;
+}
+
+/* AUX channel communication in native and I2C modes */
+
+static uint32_t
+intel_pack_aux(uint8_t *src, int src_bytes)
+{
+	int	i;
+	uint32_t v = 0;
+
+	if (src_bytes > 4)
+		src_bytes = 4;
+	for (i = 0; i < src_bytes; i++)
+		v |= ((uint32_t) src[i]) << ((3-i) * 8);
+	return v;
+}
+
+static void
+intel_unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes)
+{
+	int i;
+	if (dst_bytes > 4)
+		dst_bytes = 4;
+	for (i = 0; i < dst_bytes; i++)
+		dst[i] = src >> ((3-i) * 8);
+}
+
+static int
+intel_dp_aux_ch(struct drm_encoder *encoder, uint32_t output_reg,
+		uint8_t *send, int send_bytes,
+		uint8_t *recv, int recv_size)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_output *intel_output = enc_to_intel_output(encoder);
+	struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+	uint32_t	ch_ctl = output_reg + 0x10;
+	uint32_t	ch_data = ch_ctl + 4;
+	int		i;
+	int		recv_bytes;
+	uint32_t	ctl;
+	uint32_t	status;
+
+	/* Load the send data into the aux channel data registers */
+	for (i = 0; i < send_bytes; i += 4) {
+		uint32_t    d = intel_pack_aux(send + i, send_bytes - i);;
+
+		I915_WRITE(ch_data + i, d);
+	}
+
+	/* The clock divider is based off the hrawclk,
+	 * and would like to run at 2MHz. The 133 below assumes
+	 * a 266MHz hrawclk; need to figure out how we're supposed
+	 * to know what hrawclk is...
+	 */
+	ctl = (DP_AUX_CH_CTL_SEND_BUSY |
+	       DP_AUX_CH_CTL_TIME_OUT_1600us |
+	       (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
+	       (5 << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
+	       (133 << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT) |
+	       DP_AUX_CH_CTL_TIME_OUT_ERROR |
+	       DP_AUX_CH_CTL_RECEIVE_ERROR);
+
+	/* Send the command and wait for it to complete */
+	I915_WRITE(ch_ctl, ctl);
+	for (;;) {
+		status = I915_READ(ch_ctl);
+		if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0)
+			break;
+		udelay(100);
+	}
+
+	/* Clear done status and any errors */
+	I915_READ(ch_ctl, (ctl |
+			   DP_AUX_CH_CTL_DONE |
+			   DP_AUX_CH_CTL_TIME_OUT_ERROR |
+			   DP_AUX_CH_CTL_RECEIVE_ERROR));
+
+	if ((status & DP_AUX_CH_CTL_DONE) == 0)
+		return -EIO;
+
+	/* Check for timeout or receive error.
+	 * Timeouts occur when the sink is not connected
+	 */
+	if (status & (DP_AUX_CH_CTL_TIME_OUT_ERROR | DP_AUX_CH_CTL_RECEIVE_ERROR))
+		return -EIO;
+
+	/* Unload any bytes sent back from the other side */
+	recv_bytes = ((status & DP_AUX_CH_CTL_MESSAGE_SIZE_MASK) >>
+		      DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT);
+
+	if (recv_bytes > recv_size)
+		recv_bytes = recv_size;
+	for (i = 0; i < recv_bytes; i += 4) {
+		uint32_t    d = INREG(ch_data + i);
+
+		intel_unpack_aux(d, recv + i, recv_bytes - i);
+	}
+
+	return recv_bytes;
+}
+
+/* Write data to the aux channel in native mode */
+static int
+intel_dp_aux_native_write(struct drm_encoder *encoder, uint32_t output_reg,
+			  uint16_t address, uint8_t *send, int send_bytes)
+{
+	int ret;
+	uint8_t	msg[20];
+	int msg_bytes;
+	uint8_t	ack;
+
+	assert(send_bytes <= 16);
+	msg[0] = AUX_NATIVE_WRITE << 4;
+	msg[1] = address >> 8;
+	msg[2] = address;
+	msg[3] = send_bytes - 1;
+	memcpy(&msg[4], send, send_bytes);
+	msg_bytes = send_bytes + 4;
+	for (;;) {
+		ret = intel_dp_aux_ch(encoder, output_reg, msg, msg_bytes, &ack, 1);
+		if (ret < 0)
+			return ret;
+		if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK)
+			break;
+		else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER)
+			udelay(100);
+		else
+			return -EIO;
+	}
+	return send_bytes;
+}
+
+/* Write a single byte to the aux channel in native mode */
+static int
+intel_dp_aux_native_write_1(struct drm_encoder *encoder, uint32_t output_reg,
+			    uint16_t address, uint8_t byte)
+{
+	return intel_dp_aux_native_write(encoder, output_reg, address, &byte, 1);
+}
+
+/* read bytes from a native aux channel */
+static int
+i830_dp_aux_native_read(struct drm_encoder *encoder, uint32_t output_reg,
+			uint16_t address, uint8_t *recv, int recv_bytes)
+{
+	uint8_t msg[4];
+	int msg_bytes;
+	uint8_t reply[20];
+	int reply_bytes;
+	uint8_t ack;
+	int ret;
+
+	msg[0] = AUX_NATIVE_READ << 4;
+	msg[1] = address >> 8;
+	msg[2] = address & 0xff;
+	msg[3] = recv_bytes - 1;
+
+	msg_bytes = 4;
+	reply_bytes = recv_bytes + 1;
+
+	for (;;) {
+		ret = intel_dp_aux_ch(encoder, output_reg, msg, msg_bytes,
+				      reply, reply_bytes);
+		if (ret <= 0)
+			return ret;
+		ack = reply[0];
+		if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK) {
+			memcpy(recv, reply + 1, ret - 1);
+			return ret - 1;
+		}
+		else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER)
+			usleep(100);
+		else
+			return -EIO;
+	}
+}
+
+/* Run a single AUX_CH I2C transaction, writing/reading data as necessary */
+
+enum dp_aux_i2c_mode {
+    aux_i2c_start,
+    aux_i2c_write,
+    aux_i2c_read,
+    aux_i2c_stop
+};
+
+static int
+intel_dp_aux_i2c_transaction(struct drm_encoder *encoder, uint32_t output_reg,
+			     uint16_t address,
+			     enum dp_aux_i2c_mode mode,
+			     uint8_t write_byte, uint8_t *read_byte)
+{
+	uint8_t msg[5];
+	uint8_t reply[2];
+	int msg_bytes;
+	int reply_bytes;
+	int ret;
+
+	/* Set up the command byte */
+	if (address & 1)
+		msg[0] = AUX_I2C_READ << 4;
+	else
+		msg[0] = AUX_I2C_WRITE << 4;
+
+	if (mode != aux_i2c_stop)
+		msg[0] |= AUX_I2C_MOT << 4;
+
+	/* Note that the AUX_CH I2C stuff wants the read/write
+	 * bit stripped off
+	 */
+	msg[1] = address >> 9;
+	msg[2] = address >> 1;
+
+	switch (mode) {
+	case aux_i2c_start:
+	case aux_i2c_stop:
+	default:
+		msg_bytes = 3;
+		reply_bytes = 1;
+		break;
+	case aux_i2c_write:
+		msg[3] = 0;
+		msg[4] = write_byte;
+		msg_bytes = 5;
+		reply_bytes = 1;
+		break;
+	case aux_i2c_read:
+		msg[3] = 0;
+		msg_bytes = 4;
+		reply_bytes = 2;
+		break;
+	}
+
+	for (;;) {
+		ret = intel_dp_aux_ch(encoder, output_reg, msg, msg_bytes,
+				      reply, reply_bytes);
+		if (ret <= 0)
+			return ret;
+
+		if ((reply[0] & AUX_I2C_REPLY_MASK) == AUX_I2C_REPLY_ACK) {
+			if (mode == aux_i2c_read)
+				*read_byte = reply[1];
+			return reply_bytes - 1;
+		}
+		else if ((reply[0] & AUX_I2C_REPLY_MASK) == AUX_I2C_REPLY_DEFER)
+			usleep(100);
+		else
+			return -EIO;
+	}
+}
+
+/*
+ * I2C over AUX CH
+ */
+
+/*
+ * Send the address. If the I2C link is running, this 'restarts'
+ * the connection with the new address, this is used for doing
+ * a write followed by a read (as needed for DDC)
+ */
+static Bool
+i830_dp_i2c_address(I2CDevPtr dev, I2CSlaveAddr addr)
+{
+	I2CBusPtr bus = dev->pI2CBus;
+	xf86OutputPtr output = bus->DriverPrivate.ptr;
+	I830OutputPrivatePtr intel_output = output->driver_private;
+	struct i830_dp_priv *dev_priv = intel_output->dev_priv;
+	ScrnInfoPtr scrn = output->scrn;
+
+	dev_priv->i2c_address = addr;
+	dev_priv->i2c_running = TRUE;
+	return i830_dp_aux_i2c_transaction(scrn, dev_priv->output_reg, addr,
+					   aux_i2c_start, 0, NULL) >= 0;
+}
+
+/* DIX never even calls this function, so it better not be necessary */
+static Bool
+i830_dp_i2c_start(I2CBusPtr bus, int timeout)
+{
+	return TRUE;
+}
+
+/*
+ * Stop the I2C transaction. This closes out the link, sending
+ * a bare address packet with the MOT bit turned off
+ */
+static void
+i830_dp_i2c_stop(I2CDevPtr dev)
+{
+	I2CBusPtr bus = dev->pI2CBus;
+	xf86OutputPtr output = bus->DriverPrivate.ptr;
+	ScrnInfoPtr scrn = output->scrn;
+	I830OutputPrivatePtr intel_output = output->driver_private;
+	struct i830_dp_priv *dev_priv = intel_output->dev_priv;
+
+	if (dev_priv->i2c_running)
+		(void) i830_dp_aux_i2c_transaction(scrn, dev_priv->output_reg,
+						   dev_priv->i2c_address,
+						   aux_i2c_stop, 0, NULL);
+	dev_priv->i2c_running = FALSE;
+}
+
+/*
+ * Write a single byte to the current I2C address, this assumes
+ * that the I2C link is running (or presumably it won't work).
+ */
+static Bool
+i830_dp_i2c_put_byte(I2CDevPtr dev, I2CByte byte)
+{
+	I2CBusPtr bus = dev->pI2CBus;
+	xf86OutputPtr output = bus->DriverPrivate.ptr;
+	ScrnInfoPtr scrn = output->scrn;
+	I830OutputPrivatePtr intel_output = output->driver_private;
+	struct i830_dp_priv *dev_priv = intel_output->dev_priv;
+
+	return i830_dp_aux_i2c_transaction(scrn, dev_priv->output_reg,
+					   dev_priv->i2c_address,
+					   aux_i2c_write, byte, NULL) >= 0;
+}
+
+static Bool
+i830_dp_i2c_get_byte(I2CDevPtr dev, I2CByte *byte_ret, Bool last)
+{
+	I2CBusPtr bus = dev->pI2CBus;
+	xf86OutputPtr output = bus->DriverPrivate.ptr;
+	I830OutputPrivatePtr intel_output = output->driver_private;
+	struct i830_dp_priv *dev_priv = intel_output->dev_priv;
+	ScrnInfoPtr scrn = output->scrn;
+
+	return i830_dp_aux_i2c_transaction(scrn, dev_priv->output_reg,
+					   dev_priv->i2c_address,
+					   aux_i2c_read, 0, byte_ret) == 1;
+}
+
+static Bool
+i830_dp_i2c_init(ScrnInfoPtr pScrn, I2CBusPtr *bus_ptr,
+		 xf86OutputPtr output, char *name)
+{
+	I2CBusPtr pI2CBus;
+
+	pI2CBus = xf86CreateI2CBusRec();
+
+	if (!pI2CBus)
+		return FALSE;
+
+	pI2CBus->BusName = name;
+	pI2CBus->scrnIndex = pScrn->scrnIndex;
+	pI2CBus->I2CGetByte = i830_dp_i2c_get_byte;
+	pI2CBus->I2CPutByte = i830_dp_i2c_put_byte;
+	pI2CBus->I2CAddress = i830_dp_i2c_address;
+	pI2CBus->I2CStart = i830_dp_i2c_start;
+	pI2CBus->I2CStop = i830_dp_i2c_stop;
+	pI2CBus->DriverPrivate.ptr = output;
+
+	/* Assume all busses are used for DDCish stuff */
+
+	/*
+	 * These were set incorrectly in the server pre-1.3, Having
+	 * duplicate settings is sub-optimal, but this lets the driver
+	 * work with older servers
+	 */
+	pI2CBus->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */
+	pI2CBus->StartTimeout = 550;
+	pI2CBus->BitTimeout = 40;
+	pI2CBus->AcknTimeout = 40;
+	pI2CBus->RiseFallTime = 20;
+
+	if (!xf86I2CBusInit(pI2CBus))
+		return FALSE;
+
+	*bus_ptr = pI2CBus;
+	return TRUE;
+}
+
+static int
+intel_dp_mode_valid(xf86OutputPtr output, DisplayModePtr mode)
+{
+	int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(output));
+	int max_lanes = intel_dp_max_lane_count(output);
+
+	if (intel_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 void intel_dp_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 intel_output *intel_output = enc_to_intel_output(encoder);
+	struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+	I830OutputPrivatePtr intel_output = output->driver_private;
+	struct i830_dp_priv *dev_priv = intel_output->dev_priv;
+	xf86CrtcPtr crtc = output->crtc;
+	I830CrtcPrivatePtr intel_crtc = crtc->driver_private;
+
+	dp_priv->DP = (DP_LINK_TRAIN_OFF |
+			DP_VOLTAGE_0_4 |
+			DP_PRE_EMPHASIS_0 |
+			DP_SYNC_VS_HIGH |
+			DP_SYNC_HS_HIGH);
+
+	switch (dp_priv->lane_count) {
+	case 1:
+		dp_priv->DP |= DP_PORT_WIDTH_1;
+		break;
+	case 2:
+		dp_priv->DP |= DP_PORT_WIDTH_2;
+		break;
+	case 4:
+		dp_priv->DP |= DP_PORT_WIDTH_4;
+		break;
+	}
+	if (dp_priv->has_audio)
+		dp_priv->DP |= DP_AUDIO_OUTPUT_ENABLE;
+
+	memset(dp_priv->link_configuration, 0, DP_LINK_CONFIGURATION_SIZE);
+	dp_priv->link_configuration[0] = dp_priv->link_bw;
+	dp_priv->link_configuration[1] = dp_priv->lane_count;
+
+	/*
+	 * Check for DPCD version > 1.1,
+	 * enable enahanced frame stuff in that case
+	 */
+	if (dp_priv->dpcd[0] >= 0x11) {
+		dp_priv->link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+		dp_priv->DP |= DP_ENHANCED_FRAMING;
+	}
+
+	if (intel_crtc->pipe == 1)
+		dp_priv->DP |= DP_PIPEB_SELECT;
+}
+
+static void intel_dp_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_output *intel_output = enc_to_intel_output(encoder);
+	struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+	uint32_t dp_reg = I915_READ(dp_priv->output_reg);
+
+	if (mode == DPMSModeOff) {
+		if (dp_reg & DP_PORT_EN)
+			i830_dp_link_down(output, dp_priv->DP);
+	} else {
+		if (!(dp_reg & DP_PORT_EN))
+			i830_dp_link_train(output, dp_priv->DP, dp_priv->link_configuration);
+	}
+}
+
+static void intel_dp_save(struct drm_connector *connector)
+{
+	struct drm_device *dev = connector->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_output *intel_output = to_intel_output(connector);
+	struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+
+	dp_priv->save_DP = I915_READ(dp_priv->output_reg);
+
+	/* Save the link configuration */
+	intel_dp_aux_native_read(pScrn, dp_priv->output_reg, DP_LINK_BW_SET,
+				 dp_priv->save_link_configuration,
+				 sizeof (dp_priv->save_link_configuration));
+	
+}
+
+static void intel_dp_restore(struct drm_connector *connector)
+{
+	struct drm_device *dev = connector->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_output *intel_output = to_intel_output(connector);
+	struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+
+	I915_WRITE(dp_priv->output_reg, dp_priv->save_DP);
+}
+
+static int intel_dp_mode_valid(struct drm_connector *connector,
+			       struct drm_display_mode *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 (intel_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 bool intel_dp_mode_fixup(struct drm_encoder *encoder,
+				  struct drm_display_mode *mode,
+				  struct drm_display_mode *adjusted_mode)
+{
+	struct drm_device *dev = connector->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_output *intel_output = to_intel_output(connector);
+	struct intel_dp_priv *dp_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 = intel_dp_link_clock(bws[clock]) * lane_count;
+
+			if (intel_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);
+				return TRUE;
+			}
+		}
+	}
+	return FALSE;
+}
+
+static enum drm_connector_status
+intel_dp_detect(struct drm_connector *connector)
+{
+	struct drm_device *dev = connector->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_output *intel_output = to_intel_output(connector);
+	struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+	u32 temp, bit;
+	enum drm_connector_status status;
+
+	dp_priv->has_audio = FALSE;
+
+	temp = I915_READ(PORT_HOTPLUG_EN);
+
+	I915_WRITE(PORT_HOTPLUG_EN,
+		   temp |
+		   DPB_HOTPLUG_INT_EN |
+		   DPC_HOTPLUG_INT_EN |
+		   DPD_HOTPLUG_INT_EN);
+
+	POSTING_READ(PORT_HOTPLUG_EN);
+
+	switch (dev_priv->output_reg) {
+	case DP_B:
+		bit = DPB_HOTPLUG_INT_STATUS;
+		break;
+	case DP_C:
+		bit = DPC_HOTPLUG_INT_STATUS;
+		break;
+	case DP_D:
+		bit = DPD_HOTPLUG_INT_STATUS;
+		break;
+	default:
+		return connector_status_unknown;
+	}
+
+	if ((I915_READ(PORT_HOTPLUG_STAT) & bit) == 0)
+		return connector_status_disconnected;
+	
+	status = connector_status_disconnected;
+	if (intel_dp_aux_native_read(pScrn, dp_priv->output_reg,
+				     0, dp_priv->dpcd,
+				     sizeof (dp_priv->dpcd)) == sizeof (dp_priv->dpcd))
+	{
+		if (dp_priv->dpcd[0] != 0)
+			status = connector_status_connected;
+	}
+	return status;
+}
+
+static int intel_dp_get_modes(struct drm_connector *connector)
+{
+	struct intel_output *intel_output = to_intel_output(connector);
+
+	/* We should parse the EDID data and find out if it's an HDMI sink so
+	 * we can send audio to it.
+	 */
+
+	return intel_ddc_get_modes(intel_output);
+}
+
+static void intel_dp_destroy(struct drm_connector *connector)
+{
+	struct intel_output *intel_output = to_intel_output(connector);
+
+	if (intel_output->i2c_bus)
+		intel_i2c_destroy(intel_output->i2c_bus);
+	drm_sysfs_connector_remove(connector);
+	drm_connector_cleanup(connector);
+	kfree(intel_output);
+}
+
+static const struct drm_encoder_helper_funcs intel_dp_helper_funcs = {
+	.dpms = intel_dp_dpms,
+	.mode_fixup = intel_dp_mode_fixup,
+	.prepare = intel_encoder_prepare,
+	.mode_set = intel_dp_mode_set,
+	.commit = intel_encoder_commit,
+};
+
+static const struct drm_connector_funcs intel_dp_connector_funcs = {
+	.save = intel_dp_save,
+	.restore = intel_dp_restore,
+	.detect = intel_dp_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = intel_dp_destroy,
+};
+
+static const struct drm_connector_helper_funcs intel_dp_connector_helper_funcs = {
+	.get_modes = intel_dp_get_modes,
+	.mode_valid = intel_dp_mode_valid,
+	.best_encoder = intel_best_encoder,
+};
+
+static void intel_dp_enc_destroy(struct drm_encoder *encoder)
+{
+	drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs intel_dp_enc_funcs = {
+	.destroy = intel_dp_enc_destroy,
+};
+
+struct intel_i2c_chan *intel_dp_i2c_create(struct drm_device *dev, const char *name)
+{
+}
+
+void intel_dp_init(struct drm_device *dev, int output_reg)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct drm_connector *connector;
+	struct intel_output *intel_output;
+	struct intel_dp_priv *dp_priv;
+
+	intel_output = kcalloc(sizeof(struct intel_output) +
+			       sizeof(struct intel_dp_priv), 1, GFP_KERNEL);
+	if (!intel_output)
+		return;
+	dp_priv = (struct intel_dp_priv *)(intel_output + 1);
+
+	connector = &intel_output->base;
+	drm_connector_init(dev, connector, &intel_dp_connector_funcs,
+			   DRM_MODE_CONNECTOR_DVID);
+	drm_connector_helper_add(connector, &intel_dp_connector_helper_funcs);
+
+	intel_output->type = INTEL_OUTPUT_DISPLAYPORT;
+
+	connector->interlace_allowed = 0;
+	connector->doublescan_allowed = 0;
+
+	/* Set up the DDC bus. */
+	switch (output_reg) {
+	case DP_B:
+	default:
+		intel_output->ddc_bus = intel_i2c_create(dev, "DP_B");
+	case DP_C:
+		intel_output->ddc_bus = intel_i2c_create(dev, "DP_C");
+	case DP_D:
+		intel_output->ddc_bus = intel_i2c_create(dev, "DP_D");
+	}
+
+	if (!intel_output->ddc_bus)
+		goto err_connector;
+
+	dp_priv->output_reg = output_reg;
+	intel_output->dev_priv = dp_priv;
+
+	drm_encoder_init(dev, &intel_output->enc, &intel_dp_enc_funcs,
+			 DRM_MODE_ENCODER_TMDS);
+	drm_encoder_helper_add(&intel_output->enc, &intel_dp_helper_funcs);
+
+	drm_mode_connector_attach_encoder(&intel_output->base,
+					  &intel_output->enc);
+	drm_sysfs_connector_add(connector);
+
+	/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
+	 * 0xd.  Failure to do so will result in spurious interrupts being
+	 * generated on the port when a cable is not attached.
+	 */
+	if (IS_G4X(dev) && !IS_GM45(dev)) {
+		u32 temp = I915_READ(PEG_BAND_GAP_DATA);
+		I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd);
+	}
+
+	return;
+
+err_connector:
+	drm_connector_cleanup(connector);
+	kfree(intel_output);
+
+	return;
+}
diff --git a/drivers/gpu/drm/i915/intel_dp.h b/drivers/gpu/drm/i915/intel_dp.h
new file mode 100644
index 0000000..cfce2c5
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_dp.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright © 2008 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef _INTEL_DP_H_
+#define _INTEL_DP_H_
+
+/* From the VESA DisplayPort spec */
+
+#define AUX_NATIVE_WRITE	0x8
+#define AUX_NATIVE_READ		0x9
+#define AUX_I2C_WRITE		0x0
+#define AUX_I2C_READ		0x1
+#define AUX_I2C_STATUS		0x2
+#define AUX_I2C_MOT		0x4
+
+#define AUX_NATIVE_REPLY_ACK	0x0
+#define AUX_NATIVE_REPLY_NACK	0x1
+#define AUX_NATIVE_REPLY_DEFER	0x2
+#define AUX_NATIVE_REPLY_MASK	0x3
+
+#define AUX_I2C_REPLY_ACK	(0x0 << 2)
+#define AUX_I2C_REPLY_NACK	(0x1 << 2)
+#define AUX_I2C_REPLY_DEFER	(0x2 << 2)
+#define AUX_I2C_REPLY_MASK	(0x3 << 2)
+
+/* AUX CH addresses */
+#define	DP_LINK_BW_SET		0x100
+# define DP_LINK_BW_1_62		    0x06
+# define DP_LINK_BW_2_7			    0x0a
+
+#define DP_LANE_COUNT_SET	0x101
+# define DP_LANE_COUNT_MASK		    0x0f
+# define DP_LANE_COUNT_ENHANCED_FRAME_EN    (1 << 7)
+
+#define DP_TRAINING_PATTERN_SET	0x102
+
+# define DP_TRAINING_PATTERN_DISABLE	    0
+# define DP_TRAINING_PATTERN_1		    1
+# define DP_TRAINING_PATTERN_2		    2
+# define DP_TRAINING_PATTERN_MASK	    0x3
+
+# define DP_LINK_QUAL_PATTERN_DISABLE	    (0 << 2)
+# define DP_LINK_QUAL_PATTERN_D10_2	    (1 << 2)
+# define DP_LINK_QUAL_PATTERN_ERROR_RATE    (2 << 2)
+# define DP_LINK_QUAL_PATTERN_PRBS7	    (3 << 2)
+# define DP_LINK_QUAL_PATTERN_MASK	    (3 << 2)
+
+# define DP_RECOVERED_CLOCK_OUT_EN	    (1 << 4)
+# define DP_LINK_SCRAMBLING_DISABLE	    (1 << 5)
+
+# define DP_SYMBOL_ERROR_COUNT_BOTH	    (0 << 6)
+# define DP_SYMBOL_ERROR_COUNT_DISPARITY    (1 << 6)
+# define DP_SYMBOL_ERROR_COUNT_SYMBOL	    (2 << 6)
+# define DP_SYMBOL_ERROR_COUNT_MASK	    (3 << 6)
+
+#define DP_TRAINING_LANE0_SET		    0x103
+#define DP_TRAINING_LANE1_SET		    0x104
+#define DP_TRAINING_LANE2_SET		    0x105
+#define DP_TRAINING_LANE3_SET		    0x106
+
+# define DP_TRAIN_VOLTAGE_SWING_MASK	    0x3
+# define DP_TRAIN_VOLTAGE_SWING_SHIFT	    0
+# define DP_TRAIN_MAX_SWING_REACHED	    (1 << 2)
+# define DP_TRAIN_VOLTAGE_SWING_400	    (0 << 0)
+# define DP_TRAIN_VOLTAGE_SWING_600	    (1 << 0)
+# define DP_TRAIN_VOLTAGE_SWING_800	    (2 << 0)
+# define DP_TRAIN_VOLTAGE_SWING_1200	    (3 << 0)
+
+# define DP_TRAIN_PRE_EMPHASIS_MASK	    (3 << 3)
+# define DP_TRAIN_PRE_EMPHASIS_0	    (0 << 3)
+# define DP_TRAIN_PRE_EMPHASIS_3_5	    (1 << 3)
+# define DP_TRAIN_PRE_EMPHASIS_6	    (2 << 3)
+# define DP_TRAIN_PRE_EMPHASIS_9_5	    (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)
+
+#define DP_MAIN_LINK_CHANNEL_CODING_SET	    0x108
+# define DP_SET_ANSI_8B10B		    (1 << 0)
+
+#define DP_LANE0_1_STATUS		    0x202
+#define DP_LANE2_3_STATUS		    0x203
+
+# 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
+
+#endif /* _INTEL_DP_H_ */
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index c585879..0937112 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -54,6 +54,7 @@
 #define INTEL_OUTPUT_LVDS 4
 #define INTEL_OUTPUT_TVOUT 5
 #define INTEL_OUTPUT_HDMI 6
+#define INTEL_OUTPUT_DISPLAYPORT 7
 
 #define INTEL_DVO_CHIP_NONE 0
 #define INTEL_DVO_CHIP_LVDS 1
-- 
1.6.3.1




More information about the Intel-gfx mailing list