Signed-off-by: Keith Packard <kei...@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 <kei...@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


------------------------------------------------------------------------------
Register Now for Creativity and Technology (CaT), June 3rd, NYC. CaT 
is a gathering of tech-side developers & brand creativity professionals. Meet
the minds behind Google Creative Lab, Visual Complexity, Processing, & 
iPhoneDevCamp as they present alongside digital heavyweights like Barbarian 
Group, R/GA, & Big Spaceship. http://p.sf.net/sfu/creativitycat-com 
--
_______________________________________________
Dri-devel mailing list
Dri-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/dri-devel

Reply via email to