drm/i915: Add Display Port support
authorKeith Packard <keithp@keithp.com>
Tue, 7 Apr 2009 23:16:42 +0000 (16:16 -0700)
committerKeith Packard <keithp@keithp.com>
Thu, 18 Jun 2009 22:54:07 +0000 (15:54 -0700)
Signed-off-by: Keith Packard <keithp@keithp.com>
drivers/gpu/drm/i915/Makefile
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_suspend.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_dp.c [new file with mode: 0644]
drivers/gpu/drm/i915/intel_dp.h [new file with mode: 0644]
drivers/gpu/drm/i915/intel_dp_i2c.c [new file with mode: 0644]
drivers/gpu/drm/i915/intel_drv.h

index 51c5a050aa730ee2b1f841d2a413c6e77c31833d..30d6b99fb302908d4aaadd03edef03ba1266f733 100644 (file)
@@ -13,6 +13,8 @@ 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_dp_i2c.o \
          intel_hdmi.o \
          intel_sdvo.o \
          intel_modes.o \
index 7a84f04e8439c1ea2f68acce4520f0d4665490f7..bb4c2d387b6c3eef6ab5b1a4f05fd9fc5f3393cf 100644 (file)
@@ -306,6 +306,17 @@ typedef struct drm_i915_private {
        u32 saveCURBPOS;
        u32 saveCURBBASE;
        u32 saveCURSIZE;
+       u32 saveDP_B;
+       u32 saveDP_C;
+       u32 saveDP_D;
+       u32 savePIPEA_GMCH_DATA_M;
+       u32 savePIPEB_GMCH_DATA_M;
+       u32 savePIPEA_GMCH_DATA_N;
+       u32 savePIPEB_GMCH_DATA_N;
+       u32 savePIPEA_DP_LINK_M;
+       u32 savePIPEB_DP_LINK_M;
+       u32 savePIPEA_DP_LINK_N;
+       u32 savePIPEB_DP_LINK_N;
 
        struct {
                struct drm_mm gtt_space;
@@ -857,6 +868,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
 #define HAS_128_BYTE_Y_TILING(dev) (IS_I9XX(dev) && !(IS_I915G(dev) || \
                                                      IS_I915GM(dev)))
 #define SUPPORTS_INTEGRATED_HDMI(dev)  (IS_G4X(dev) || IS_IGDNG(dev))
+#define SUPPORTS_INTEGRATED_DP(dev)    (IS_G4X(dev) || IS_IGDNG(dev))
 #define I915_HAS_HOTPLUG(dev) (IS_I945G(dev) || IS_I945GM(dev) || IS_I965G(dev))
 
 #define PRIMARY_RINGBUFFER_SIZE         (128*1024)
index a98e2831ed31787ee6985fb058bdc3d32725367c..8d8e083d14ab22acaea6d3d246655bbc7d76f13a 100644 (file)
@@ -322,6 +322,20 @@ int i915_save_state(struct drm_device *dev)
        dev_priv->savePP_OFF_DELAYS = I915_READ(PP_OFF_DELAYS);
        dev_priv->savePP_DIVISOR = I915_READ(PP_DIVISOR);
 
+       /* Display Port state */
+       if (SUPPORTS_INTEGRATED_DP(dev)) {
+               dev_priv->saveDP_B = I915_READ(DP_B);
+               dev_priv->saveDP_C = I915_READ(DP_C);
+               dev_priv->saveDP_D = I915_READ(DP_D);
+               dev_priv->savePIPEA_GMCH_DATA_M = I915_READ(PIPEA_GMCH_DATA_M);
+               dev_priv->savePIPEB_GMCH_DATA_M = I915_READ(PIPEB_GMCH_DATA_M);
+               dev_priv->savePIPEA_GMCH_DATA_N = I915_READ(PIPEA_GMCH_DATA_N);
+               dev_priv->savePIPEB_GMCH_DATA_N = I915_READ(PIPEB_GMCH_DATA_N);
+               dev_priv->savePIPEA_DP_LINK_M = I915_READ(PIPEA_DP_LINK_M);
+               dev_priv->savePIPEB_DP_LINK_M = I915_READ(PIPEB_DP_LINK_M);
+               dev_priv->savePIPEA_DP_LINK_N = I915_READ(PIPEA_DP_LINK_N);
+               dev_priv->savePIPEB_DP_LINK_N = I915_READ(PIPEB_DP_LINK_N);
+       }
        /* FIXME: save TV & SDVO state */
 
        /* FBC state */
@@ -404,7 +418,19 @@ int i915_restore_state(struct drm_device *dev)
                        for (i = 0; i < 8; i++)
                                I915_WRITE(FENCE_REG_945_8 + (i * 4), dev_priv->saveFENCE[i+8]);
        }
-
+       
+       /* Display port ratios (must be done before clock is set) */
+       if (SUPPORTS_INTEGRATED_DP(dev)) {
+               I915_WRITE(PIPEA_GMCH_DATA_M, dev_priv->savePIPEA_GMCH_DATA_M);
+               I915_WRITE(PIPEB_GMCH_DATA_M, dev_priv->savePIPEB_GMCH_DATA_M);
+               I915_WRITE(PIPEA_GMCH_DATA_N, dev_priv->savePIPEA_GMCH_DATA_N);
+               I915_WRITE(PIPEB_GMCH_DATA_N, dev_priv->savePIPEB_GMCH_DATA_N);
+               I915_WRITE(PIPEA_DP_LINK_M, dev_priv->savePIPEA_DP_LINK_M);
+               I915_WRITE(PIPEB_DP_LINK_M, dev_priv->savePIPEB_DP_LINK_M);
+               I915_WRITE(PIPEA_DP_LINK_N, dev_priv->savePIPEA_DP_LINK_N);
+               I915_WRITE(PIPEB_DP_LINK_N, dev_priv->savePIPEB_DP_LINK_N);
+       }
+       
        /* Pipe & plane A info */
        /* Prime the clock */
        if (dev_priv->saveDPLL_A & DPLL_VCO_ENABLE) {
@@ -518,6 +544,12 @@ int i915_restore_state(struct drm_device *dev)
        I915_WRITE(PP_DIVISOR, dev_priv->savePP_DIVISOR);
        I915_WRITE(PP_CONTROL, dev_priv->savePP_CONTROL);
 
+       /* Display Port state */
+       if (SUPPORTS_INTEGRATED_DP(dev)) {
+               I915_WRITE(DP_B, dev_priv->saveDP_B);
+               I915_WRITE(DP_C, dev_priv->saveDP_C);
+               I915_WRITE(DP_D, dev_priv->saveDP_D);
+       }
        /* FIXME: restore TV & SDVO state */
 
        /* FBC info */
index 3e1c7816211904391c1b65a43a7aace7726db5fc..5af55aa0d7a656053cab12df4bfa34bec59fa7f5 100644 (file)
@@ -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"
 
@@ -135,10 +136,11 @@ struct intel_limit {
 #define INTEL_LIMIT_G4X_HDMI_DAC   5
 #define INTEL_LIMIT_G4X_SINGLE_CHANNEL_LVDS   6
 #define INTEL_LIMIT_G4X_DUAL_CHANNEL_LVDS   7
-#define INTEL_LIMIT_IGD_SDVO_DAC    8
-#define INTEL_LIMIT_IGD_LVDS       9
-#define INTEL_LIMIT_IGDNG_SDVO_DAC  10
-#define INTEL_LIMIT_IGDNG_LVDS     11
+#define INTEL_LIMIT_G4X_DISPLAY_PORT   8
+#define INTEL_LIMIT_IGD_SDVO_DAC    9
+#define INTEL_LIMIT_IGD_LVDS       10
+#define INTEL_LIMIT_IGDNG_SDVO_DAC  11
+#define INTEL_LIMIT_IGDNG_LVDS     12
 
 /*The parameter is for SDVO on G4x platform*/
 #define G4X_DOT_SDVO_MIN           25000
@@ -218,6 +220,25 @@ struct intel_limit {
 #define G4X_P2_DUAL_CHANNEL_LVDS_FAST           7
 #define G4X_P2_DUAL_CHANNEL_LVDS_LIMIT          0
 
+/*The parameter is for DISPLAY PORT on G4x platform*/
+#define G4X_DOT_DISPLAY_PORT_MIN           161670
+#define G4X_DOT_DISPLAY_PORT_MAX           227000
+#define G4X_N_DISPLAY_PORT_MIN             1
+#define G4X_N_DISPLAY_PORT_MAX             2
+#define G4X_M_DISPLAY_PORT_MIN             97
+#define G4X_M_DISPLAY_PORT_MAX             108
+#define G4X_M1_DISPLAY_PORT_MIN            0x10
+#define G4X_M1_DISPLAY_PORT_MAX            0x12
+#define G4X_M2_DISPLAY_PORT_MIN            0x05
+#define G4X_M2_DISPLAY_PORT_MAX            0x06
+#define G4X_P_DISPLAY_PORT_MIN             10
+#define G4X_P_DISPLAY_PORT_MAX             20
+#define G4X_P1_DISPLAY_PORT_MIN            1
+#define G4X_P1_DISPLAY_PORT_MAX            2
+#define G4X_P2_DISPLAY_PORT_SLOW           10
+#define G4X_P2_DISPLAY_PORT_FAST           10
+#define G4X_P2_DISPLAY_PORT_LIMIT          0
+
 /* IGDNG */
 /* as we calculate clock using (register_value + 2) for
    N/M1/M2, so here the range value for them is (actual_value-2).
@@ -256,6 +277,10 @@ static bool
 intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
                        int target, int refclk, intel_clock_t *best_clock);
 
+static bool
+intel_find_pll_g4x_dp(const intel_limit_t *, struct drm_crtc *crtc,
+                     int target, int refclk, intel_clock_t *best_clock);
+
 static const intel_limit_t intel_limits[] = {
     { /* INTEL_LIMIT_I8XX_DVO_DAC */
         .dot = { .min = I8XX_DOT_MIN,          .max = I8XX_DOT_MAX },
@@ -389,6 +414,28 @@ static const intel_limit_t intel_limits[] = {
        },
        .find_pll = intel_g4x_find_best_PLL,
     },
+    {   /* INTEL_LIMIT_G4X_DISPLAY_PORT */
+        .dot = { .min = G4X_DOT_DISPLAY_PORT_MIN,
+                 .max = G4X_DOT_DISPLAY_PORT_MAX },
+        .vco = { .min = G4X_VCO_MIN,
+                 .max = G4X_VCO_MAX},
+        .n   = { .min = G4X_N_DISPLAY_PORT_MIN,
+                 .max = G4X_N_DISPLAY_PORT_MAX },
+        .m   = { .min = G4X_M_DISPLAY_PORT_MIN,
+                 .max = G4X_M_DISPLAY_PORT_MAX },
+        .m1  = { .min = G4X_M1_DISPLAY_PORT_MIN,
+                 .max = G4X_M1_DISPLAY_PORT_MAX },
+        .m2  = { .min = G4X_M2_DISPLAY_PORT_MIN,
+                 .max = G4X_M2_DISPLAY_PORT_MAX },
+        .p   = { .min = G4X_P_DISPLAY_PORT_MIN,
+                 .max = G4X_P_DISPLAY_PORT_MAX },
+        .p1  = { .min = G4X_P1_DISPLAY_PORT_MIN,
+                 .max = G4X_P1_DISPLAY_PORT_MAX},
+        .p2  = { .dot_limit = G4X_P2_DISPLAY_PORT_LIMIT,
+                 .p2_slow = G4X_P2_DISPLAY_PORT_SLOW,
+                 .p2_fast = G4X_P2_DISPLAY_PORT_FAST },
+        .find_pll = intel_find_pll_g4x_dp,
+    },
     { /* INTEL_LIMIT_IGD_SDVO */
         .dot = { .min = I9XX_DOT_MIN,          .max = I9XX_DOT_MAX},
         .vco = { .min = IGD_VCO_MIN,           .max = IGD_VCO_MAX },
@@ -478,6 +525,8 @@ static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc)
                limit = &intel_limits[INTEL_LIMIT_G4X_HDMI_DAC];
        } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO)) {
                limit = &intel_limits[INTEL_LIMIT_G4X_SDVO];
+       } else if (intel_pipe_has_type (crtc, INTEL_OUTPUT_DISPLAYPORT)) {
+               limit = &intel_limits[INTEL_LIMIT_G4X_DISPLAY_PORT];
        } else /* The option is for other outputs */
                limit = &intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC];
 
@@ -764,6 +813,35 @@ out:
        return found;
 }
 
+/* DisplayPort has only two frequencies, 162MHz and 270MHz */
+static bool
+intel_find_pll_g4x_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
+                     int target, int refclk, intel_clock_t *best_clock)
+{
+    intel_clock_t clock;
+    if (target < 200000) {
+       clock.dot = 161670;
+       clock.p = 20;
+       clock.p1 = 2;
+       clock.p2 = 10;
+       clock.n = 0x01;
+       clock.m = 97;
+       clock.m1 = 0x10;
+       clock.m2 = 0x05;
+    } else {
+       clock.dot = 270000;
+       clock.p = 10;
+       clock.p1 = 1;
+       clock.p2 = 10;
+       clock.n = 0x02;
+       clock.m = 108;
+       clock.m1 = 0x12;
+       clock.m2 = 0x06;
+    }
+    memcpy(best_clock, &clock, sizeof(intel_clock_t));
+    return true;
+}
+
 void
 intel_wait_for_vblank(struct drm_device *dev)
 {
@@ -1541,7 +1619,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;
@@ -1585,6 +1663,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++;
@@ -1600,6 +1681,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        } else {
                refclk = 48000;
        }
+       
 
        /*
         * Returns a set of divisors for the desired target clock with the given
@@ -1662,6 +1744,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                        else if (IS_IGDNG(dev))
                                dpll |= (sdvo_pixel_multiply - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT;
                }
+               if (is_dp)
+                       dpll |= DPLL_DVO_HIGH_SPEED;
 
                /* compute bitmask from p1 value */
                if (IS_IGD(dev))
@@ -1809,6 +1893,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                I915_WRITE(lvds_reg, lvds);
                I915_READ(lvds_reg);
        }
+       if (is_dp)
+               intel_dp_set_m_n(crtc, mode, adjusted_mode);
 
        I915_WRITE(fp_reg, fp);
        I915_WRITE(dpll_reg, dpll);
@@ -2475,6 +2561,8 @@ static void intel_setup_outputs(struct drm_device *dev)
                        found = intel_sdvo_init(dev, SDVOB);
                        if (!found && SUPPORTS_INTEGRATED_HDMI(dev))
                                intel_hdmi_init(dev, SDVOB);
+                       if (!found && SUPPORTS_INTEGRATED_DP(dev))
+                               intel_dp_init(dev, DP_B);
                }
 
                /* Before G4X SDVOC doesn't have its own detect register */
@@ -2487,7 +2575,11 @@ static void intel_setup_outputs(struct drm_device *dev)
                        found = intel_sdvo_init(dev, SDVOC);
                        if (!found && SUPPORTS_INTEGRATED_HDMI(dev))
                                intel_hdmi_init(dev, SDVOC);
+                       if (!found && SUPPORTS_INTEGRATED_DP(dev))
+                               intel_dp_init(dev, DP_C);
                }
+               if (SUPPORTS_INTEGRATED_DP(dev) && (I915_READ(DP_D) & DP_DETECTED))
+                       intel_dp_init(dev, DP_D);
        } else
                intel_dvo_init(dev);
 
@@ -2530,6 +2622,11 @@ static void intel_setup_outputs(struct drm_device *dev)
                                     (1 << 1));
                        clone_mask = (1 << INTEL_OUTPUT_TVOUT);
                        break;
+               case INTEL_OUTPUT_DISPLAYPORT:
+                       crtc_mask = ((1 << 0) |
+                                    (1 << 1));
+                       clone_mask = (1 << INTEL_OUTPUT_DISPLAYPORT);
+                       break;
                }
                encoder->possible_crtcs = crtc_mask;
                encoder->possible_clones = intel_connector_clones(dev, clone_mask);
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
new file mode 100644 (file)
index 0000000..c57cdab
--- /dev/null
@@ -0,0 +1,1098 @@
+/*
+ * 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@keithp.com>
+ *
+ */
+
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+#include "intel_dp.h"
+
+#define DP_LINK_STATUS_SIZE    6
+#define DP_LINK_CHECK_TIMEOUT  (10 * 1000)
+
+#define DP_LINK_CONFIGURATION_SIZE     9
+
+struct intel_dp_priv {
+       uint32_t output_reg;
+       uint32_t DP;
+       uint8_t  link_configuration[DP_LINK_CONFIGURATION_SIZE];
+       uint32_t save_DP;
+       uint8_t  save_link_configuration[DP_LINK_CONFIGURATION_SIZE];
+       bool has_audio;
+       uint8_t link_bw;
+       uint8_t lane_count;
+       uint8_t dpcd[4];
+       struct intel_output *intel_output;
+       struct i2c_adapter adapter;
+       struct i2c_algo_dp_aux_data algo;
+};
+
+static void
+intel_dp_link_train(struct intel_output *intel_output, uint32_t DP,
+                   uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE]);
+
+static void
+intel_dp_link_down(struct intel_output *intel_output, uint32_t DP);
+
+static int
+intel_dp_max_lane_count(struct intel_output *intel_output)
+{
+       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(struct intel_output *intel_output)
+{
+       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;
+}
+
+/* I think this is a fiction */
+static int
+intel_dp_link_required(int pixel_clock)
+{
+       return pixel_clock * 3;
+}
+
+static int
+intel_dp_mode_valid(struct drm_connector *connector,
+                   struct drm_display_mode *mode)
+{
+       struct intel_output *intel_output = to_intel_output(connector);
+       int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(intel_output));
+       int max_lanes = intel_dp_max_lane_count(intel_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 uint32_t
+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
+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 intel_output *intel_output,
+               uint8_t *send, int send_bytes,
+               uint8_t *recv, int recv_size)
+{
+       struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+       uint32_t output_reg = dp_priv->output_reg;
+       struct drm_device *dev = intel_output->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       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 = 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);
+       (void) I915_READ(ch_ctl);
+       for (;;) {
+               udelay(100);
+               status = I915_READ(ch_ctl);
+               if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0)
+                       break;
+       }
+
+       /* Clear done status and any errors */
+       I915_WRITE(ch_ctl, (ctl |
+                       DP_AUX_CH_CTL_DONE |
+                       DP_AUX_CH_CTL_TIME_OUT_ERROR |
+                       DP_AUX_CH_CTL_RECEIVE_ERROR));
+       (void) I915_READ(ch_ctl);
+
+       if ((status & DP_AUX_CH_CTL_DONE) == 0) {
+               printk(KERN_ERR "dp_aux_ch not done status 0x%08x\n", status);
+               return -1;
+       }
+
+       /* 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)) {
+               printk(KERN_ERR "dp_aux_ch error status 0x%08x\n", status);
+               return -1;
+       }
+
+       /* 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 = I915_READ(ch_data + i);
+
+               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 intel_output *intel_output,
+                         uint16_t address, uint8_t *send, int send_bytes)
+{
+       int ret;
+       uint8_t msg[20];
+       int msg_bytes;
+       uint8_t ack;
+
+       if (send_bytes > 16)
+               return -1;
+       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(intel_output, 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 -1;
+       }
+       return send_bytes;
+}
+
+/* Write a single byte to the aux channel in native mode */
+static int
+intel_dp_aux_native_write_1(struct intel_output *intel_output,
+                           uint16_t address, uint8_t byte)
+{
+       return intel_dp_aux_native_write(intel_output, address, &byte, 1);
+}
+
+/* read bytes from a native aux channel */
+static int
+intel_dp_aux_native_read(struct intel_output *intel_output,
+                        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(intel_output, 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)
+                       udelay(100);
+               else
+                       return -1;
+       }
+}
+
+static int
+intel_dp_i2c_aux_ch(struct i2c_adapter *adapter,
+                   uint8_t *send, int send_bytes,
+                   uint8_t *recv, int recv_bytes)
+{
+       struct intel_dp_priv *dp_priv = container_of(adapter,
+                                                    struct intel_dp_priv,
+                                                    adapter);
+       struct intel_output *intel_output = dp_priv->intel_output;
+
+       return intel_dp_aux_ch(intel_output,
+                              send, send_bytes, recv, recv_bytes);
+}
+
+static int
+intel_dp_i2c_init(struct intel_output *intel_output, const char *name)
+{
+       struct intel_dp_priv   *dp_priv = intel_output->dev_priv;
+
+       DRM_ERROR("i2c_init %s\n", name);
+       dp_priv->algo.running = false;
+       dp_priv->algo.address = 0;
+       dp_priv->algo.aux_ch = intel_dp_i2c_aux_ch;
+
+       memset(&dp_priv->adapter, '\0', sizeof (dp_priv->adapter));
+       dp_priv->adapter.owner = THIS_MODULE;
+       dp_priv->adapter.class = I2C_CLASS_DDC;
+       strncpy (dp_priv->adapter.name, name, sizeof dp_priv->adapter.name - 1);
+       dp_priv->adapter.name[sizeof dp_priv->adapter.name - 1] = '\0';
+       dp_priv->adapter.algo_data = &dp_priv->algo;
+       dp_priv->adapter.dev.parent = &intel_output->base.kdev;
+       
+       return i2c_dp_aux_add_bus(&dp_priv->adapter);
+}
+
+static bool
+intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
+                   struct drm_display_mode *adjusted_mode)
+{
+       struct intel_output *intel_output = enc_to_intel_output(encoder);
+       struct intel_dp_priv   *dp_priv = intel_output->dev_priv;
+       int lane_count, clock;
+       int max_lane_count = intel_dp_max_lane_count(intel_output);
+       int max_clock = intel_dp_max_link_bw(intel_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) {
+                               dp_priv->link_bw = bws[clock];
+                               dp_priv->lane_count = lane_count;
+                               adjusted_mode->clock = intel_dp_link_clock(dp_priv->link_bw);
+                               printk(KERN_ERR "link bw %02x lane count %d clock %d\n",
+                                      dp_priv->link_bw, dp_priv->lane_count,
+                                      adjusted_mode->clock);
+                               return true;
+                       }
+               }
+       }
+       return false;
+}
+
+struct intel_dp_m_n {
+       uint32_t        tu;
+       uint32_t        gmch_m;
+       uint32_t        gmch_n;
+       uint32_t        link_m;
+       uint32_t        link_n;
+};
+
+static void
+intel_reduce_ratio(uint32_t *num, uint32_t *den)
+{
+       while (*num > 0xffffff || *den > 0xffffff) {
+               *num >>= 1;
+               *den >>= 1;
+       }
+}
+
+static void
+intel_dp_compute_m_n(int bytes_per_pixel,
+                    int nlanes,
+                    int pixel_clock,
+                    int link_clock,
+                    struct intel_dp_m_n *m_n)
+{
+       m_n->tu = 64;
+       m_n->gmch_m = pixel_clock * bytes_per_pixel;
+       m_n->gmch_n = link_clock * nlanes;
+       intel_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n);
+       m_n->link_m = pixel_clock;
+       m_n->link_n = link_clock;
+       intel_reduce_ratio(&m_n->link_m, &m_n->link_n);
+}
+
+void
+intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
+                struct drm_display_mode *adjusted_mode)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_mode_config *mode_config = &dev->mode_config;
+       struct drm_connector *connector;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int lane_count = 4;
+       struct intel_dp_m_n m_n;
+
+       /*
+        * Find the lane count in the intel_output private
+        */
+       list_for_each_entry(connector, &mode_config->connector_list, head) {
+               struct intel_output *intel_output = to_intel_output(connector);
+               struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+
+               if (!connector->encoder || connector->encoder->crtc != crtc)
+                       continue;
+
+               if (intel_output->type == INTEL_OUTPUT_DISPLAYPORT) {
+                       lane_count = dp_priv->lane_count;
+                       break;
+               }
+       }
+
+       /*
+        * 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.
+        */
+       intel_dp_compute_m_n(3, lane_count,
+                            mode->clock, adjusted_mode->clock, &m_n);
+
+       if (intel_crtc->pipe == 0) {
+               I915_WRITE(PIPEA_GMCH_DATA_M,
+                      ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
+                      m_n.gmch_m);
+               I915_WRITE(PIPEA_GMCH_DATA_N,
+                      m_n.gmch_n);
+               I915_WRITE(PIPEA_DP_LINK_M, m_n.link_m);
+               I915_WRITE(PIPEA_DP_LINK_N, m_n.link_n);
+       } else {
+               I915_WRITE(PIPEB_GMCH_DATA_M,
+                      ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
+                      m_n.gmch_m);
+               I915_WRITE(PIPEB_GMCH_DATA_N,
+                      m_n.gmch_n);
+               I915_WRITE(PIPEB_DP_LINK_M, m_n.link_m);
+               I915_WRITE(PIPEB_DP_LINK_N, m_n.link_n);
+       }
+}
+
+static void
+intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
+                 struct drm_display_mode *adjusted_mode)
+{
+       struct intel_output *intel_output = enc_to_intel_output(encoder);
+       struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+       struct drm_crtc *crtc = intel_output->enc.crtc;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+       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 intel_output *intel_output = enc_to_intel_output(encoder);
+       struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+       struct drm_device *dev = intel_output->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t dp_reg = I915_READ(dp_priv->output_reg);
+
+       if (mode != DRM_MODE_DPMS_ON) {
+               if (dp_reg & DP_PORT_EN)
+                       intel_dp_link_down(intel_output, dp_priv->DP);
+       } else {
+               if (!(dp_reg & DP_PORT_EN))
+                       intel_dp_link_train(intel_output, dp_priv->DP, dp_priv->link_configuration);
+       }
+}
+
+/*
+ * Fetch AUX CH registers 0x202 - 0x207 which contain
+ * link status information
+ */
+static bool
+intel_dp_get_link_status(struct intel_output *intel_output,
+                        uint8_t link_status[DP_LINK_STATUS_SIZE])
+{
+       int ret;
+
+       ret = intel_dp_aux_native_read(intel_output,
+                                      DP_LANE0_1_STATUS,
+                                      link_status, DP_LINK_STATUS_SIZE);
+       if (ret != DP_LINK_STATUS_SIZE)
+               return false;
+       return true;
+}
+
+static uint8_t
+intel_dp_link_status(uint8_t link_status[DP_LINK_STATUS_SIZE],
+                    int r)
+{
+       return link_status[r - DP_LANE0_1_STATUS];
+}
+
+static void
+intel_dp_save(struct drm_connector *connector)
+{
+       struct intel_output *intel_output = to_intel_output(connector);
+       struct drm_device *dev = intel_output->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+
+       dp_priv->save_DP = I915_READ(dp_priv->output_reg);
+       intel_dp_aux_native_read(intel_output, DP_LINK_BW_SET,
+                                dp_priv->save_link_configuration,
+                                sizeof (dp_priv->save_link_configuration));
+}
+
+static uint8_t
+intel_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE],
+                                int lane)
+{
+       int         i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
+       int         s = ((lane & 1) ?
+                        DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
+                        DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
+       uint8_t l = intel_dp_link_status(link_status, i);
+
+       return ((l >> s) & 3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
+}
+
+static uint8_t
+intel_get_adjust_request_pre_emphasis(uint8_t link_status[DP_LINK_STATUS_SIZE],
+                                     int lane)
+{
+       int         i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
+       int         s = ((lane & 1) ?
+                        DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
+                        DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
+       uint8_t l = intel_dp_link_status(link_status, i);
+
+       return ((l >> s) & 3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
+}
+
+
+#if 0
+static char    *voltage_names[] = {
+       "0.4V", "0.6V", "0.8V", "1.2V"
+};
+static char    *pre_emph_names[] = {
+       "0dB", "3.5dB", "6dB", "9.5dB"
+};
+static char    *link_train_names[] = {
+       "pattern 1", "pattern 2", "idle", "off"
+};
+#endif
+
+/*
+ * These are source-specific values; current Intel hardware supports
+ * a maximum voltage of 800mV and a maximum pre-emphasis of 6dB
+ */
+#define I830_DP_VOLTAGE_MAX        DP_TRAIN_VOLTAGE_SWING_800
+
+static uint8_t
+intel_dp_pre_emphasis_max(uint8_t voltage_swing)
+{
+       switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
+       case DP_TRAIN_VOLTAGE_SWING_400:
+               return DP_TRAIN_PRE_EMPHASIS_6;
+       case DP_TRAIN_VOLTAGE_SWING_600:
+               return DP_TRAIN_PRE_EMPHASIS_6;
+       case DP_TRAIN_VOLTAGE_SWING_800:
+               return DP_TRAIN_PRE_EMPHASIS_3_5;
+       case DP_TRAIN_VOLTAGE_SWING_1200:
+       default:
+               return DP_TRAIN_PRE_EMPHASIS_0;
+       }
+}
+
+static void
+intel_get_adjust_train(struct intel_output *intel_output,
+                      uint8_t link_status[DP_LINK_STATUS_SIZE],
+                      int lane_count,
+                      uint8_t train_set[4])
+{
+       uint8_t v = 0;
+       uint8_t p = 0;
+       int lane;
+
+       for (lane = 0; lane < lane_count; lane++) {
+               uint8_t this_v = intel_get_adjust_request_voltage(link_status, lane);
+               uint8_t this_p = intel_get_adjust_request_pre_emphasis(link_status, lane);
+
+               if (this_v > v)
+                       v = this_v;
+               if (this_p > p)
+                       p = this_p;
+       }
+
+       if (v >= I830_DP_VOLTAGE_MAX)
+               v = I830_DP_VOLTAGE_MAX | DP_TRAIN_MAX_SWING_REACHED;
+
+       if (p >= intel_dp_pre_emphasis_max(v))
+               p = intel_dp_pre_emphasis_max(v) | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+
+       for (lane = 0; lane < 4; lane++)
+               train_set[lane] = v | p;
+}
+
+static uint32_t
+intel_dp_signal_levels(uint8_t train_set, int lane_count)
+{
+       uint32_t        signal_levels = 0;
+
+       switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
+       case DP_TRAIN_VOLTAGE_SWING_400:
+       default:
+               signal_levels |= DP_VOLTAGE_0_4;
+               break;
+       case DP_TRAIN_VOLTAGE_SWING_600:
+               signal_levels |= DP_VOLTAGE_0_6;
+               break;
+       case DP_TRAIN_VOLTAGE_SWING_800:
+               signal_levels |= DP_VOLTAGE_0_8;
+               break;
+       case DP_TRAIN_VOLTAGE_SWING_1200:
+               signal_levels |= DP_VOLTAGE_1_2;
+               break;
+       }
+       switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) {
+       case DP_TRAIN_PRE_EMPHASIS_0:
+       default:
+               signal_levels |= DP_PRE_EMPHASIS_0;
+               break;
+       case DP_TRAIN_PRE_EMPHASIS_3_5:
+               signal_levels |= DP_PRE_EMPHASIS_3_5;
+               break;
+       case DP_TRAIN_PRE_EMPHASIS_6:
+               signal_levels |= DP_PRE_EMPHASIS_6;
+               break;
+       case DP_TRAIN_PRE_EMPHASIS_9_5:
+               signal_levels |= DP_PRE_EMPHASIS_9_5;
+               break;
+       }
+       return signal_levels;
+}
+
+static uint8_t
+intel_get_lane_status(uint8_t link_status[DP_LINK_STATUS_SIZE],
+                     int lane)
+{
+       int i = DP_LANE0_1_STATUS + (lane >> 1);
+       int s = (lane & 1) * 4;
+       uint8_t l = intel_dp_link_status(link_status, i);
+
+       return (l >> s) & 0xf;
+}
+
+/* Check for clock recovery is done on all channels */
+static bool
+intel_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count)
+{
+       int lane;
+       uint8_t lane_status;
+
+       for (lane = 0; lane < lane_count; lane++) {
+               lane_status = intel_get_lane_status(link_status, lane);
+               if ((lane_status & DP_LANE_CR_DONE) == 0)
+                       return false;
+       }
+       return true;
+}
+
+/* Check to see if channel eq is done on all channels */
+#define CHANNEL_EQ_BITS (DP_LANE_CR_DONE|\
+                        DP_LANE_CHANNEL_EQ_DONE|\
+                        DP_LANE_SYMBOL_LOCKED)
+static bool
+intel_channel_eq_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count)
+{
+       uint8_t lane_align;
+       uint8_t lane_status;
+       int lane;
+
+       lane_align = intel_dp_link_status(link_status,
+                                         DP_LANE_ALIGN_STATUS_UPDATED);
+       if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0)
+               return false;
+       for (lane = 0; lane < lane_count; lane++) {
+               lane_status = intel_get_lane_status(link_status, lane);
+               if ((lane_status & CHANNEL_EQ_BITS) != CHANNEL_EQ_BITS)
+                       return false;
+       }
+       return true;
+}
+
+static bool
+intel_dp_set_link_train(struct intel_output *intel_output,
+                       uint32_t dp_reg_value,
+                       uint8_t dp_train_pat,
+                       uint8_t train_set[4],
+                       bool first)
+{
+       struct drm_device *dev = intel_output->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+       int ret;
+
+       I915_WRITE(dp_priv->output_reg, dp_reg_value);
+       POSTING_READ(dp_priv->output_reg);
+       if (first)
+               intel_wait_for_vblank(dev);
+
+       intel_dp_aux_native_write_1(intel_output,
+                                   DP_TRAINING_PATTERN_SET,
+                                   dp_train_pat);
+
+       ret = intel_dp_aux_native_write(intel_output,
+                                       DP_TRAINING_LANE0_SET, train_set, 4);
+       if (ret != 4)
+               return false;
+
+       return true;
+}
+
+static void
+intel_dp_link_train(struct intel_output *intel_output, uint32_t DP,
+                   uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE])
+{
+       struct drm_device *dev = intel_output->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+       uint8_t train_set[4];
+       uint8_t link_status[DP_LINK_STATUS_SIZE];
+       int i;
+       uint8_t voltage;
+       bool clock_recovery = false;
+       bool channel_eq = false;
+       bool first = true;
+       int tries;
+
+       /* Write the link configuration data */
+       intel_dp_aux_native_write(intel_output, 0x100,
+                                 link_configuration, DP_LINK_CONFIGURATION_SIZE);
+
+       DP |= DP_PORT_EN;
+       DP &= ~DP_LINK_TRAIN_MASK;
+       memset(train_set, 0, 4);
+       voltage = 0xff;
+       tries = 0;
+       clock_recovery = false;
+       for (;;) {
+               /* Use train_set[0] to set the voltage and pre emphasis values */
+               uint32_t    signal_levels = intel_dp_signal_levels(train_set[0], dp_priv->lane_count);
+               DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels;
+
+               if (!intel_dp_set_link_train(intel_output, DP | DP_LINK_TRAIN_PAT_1,
+                                            DP_TRAINING_PATTERN_1, train_set, first))
+                       break;
+               first = false;
+               /* Set training pattern 1 */
+
+               udelay(100);
+               if (!intel_dp_get_link_status(intel_output, link_status))
+                       break;
+
+               if (intel_clock_recovery_ok(link_status, dp_priv->lane_count)) {
+                       clock_recovery = true;
+                       break;
+               }
+
+               /* Check to see if we've tried the max voltage */
+               for (i = 0; i < dp_priv->lane_count; i++)
+                       if ((train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
+                               break;
+               if (i == dp_priv->lane_count)
+                       break;
+
+               /* Check to see if we've tried the same voltage 5 times */
+               if ((train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
+                       ++tries;
+                       if (tries == 5)
+                               break;
+               } else
+                       tries = 0;
+               voltage = train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
+
+               /* Compute new train_set as requested by target */
+               intel_get_adjust_train(intel_output, link_status, dp_priv->lane_count, train_set);
+       }
+
+       /* channel equalization */
+       tries = 0;
+       channel_eq = false;
+       for (;;) {
+               /* Use train_set[0] to set the voltage and pre emphasis values */
+               uint32_t    signal_levels = intel_dp_signal_levels(train_set[0], dp_priv->lane_count);
+               DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels;
+
+               /* channel eq pattern */
+               if (!intel_dp_set_link_train(intel_output, DP | DP_LINK_TRAIN_PAT_2,
+                                            DP_TRAINING_PATTERN_2, train_set,
+                                            false))
+                       break;
+
+               udelay(400);
+               if (!intel_dp_get_link_status(intel_output, link_status))
+                       break;
+
+               if (intel_channel_eq_ok(link_status, dp_priv->lane_count)) {
+                       channel_eq = true;
+                       break;
+               }
+
+               /* Try 5 times */
+               if (tries > 5)
+                       break;
+
+               /* Compute new train_set as requested by target */
+               intel_get_adjust_train(intel_output, link_status, dp_priv->lane_count, train_set);
+               ++tries;
+       }
+
+       I915_WRITE(dp_priv->output_reg, DP | DP_LINK_TRAIN_OFF);
+       POSTING_READ(dp_priv->output_reg);
+       intel_dp_aux_native_write_1(intel_output,
+                                   DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE);
+}
+
+static void
+intel_dp_link_down(struct intel_output *intel_output, uint32_t DP)
+{
+       struct drm_device *dev = intel_output->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+
+       I915_WRITE(dp_priv->output_reg, DP & ~DP_PORT_EN);
+       POSTING_READ(dp_priv->output_reg);
+}
+
+static void
+intel_dp_restore(struct drm_connector *connector)
+{
+       struct intel_output *intel_output = to_intel_output(connector);
+       struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+
+       if (dp_priv->save_DP & DP_PORT_EN)
+               intel_dp_link_train(intel_output, dp_priv->save_DP, dp_priv->save_link_configuration);
+       else
+               intel_dp_link_down(intel_output,  dp_priv->save_DP);
+}
+
+#if 0
+/*
+ * According to DP spec
+ * 5.1.2:
+ *  1. Read DPCD
+ *  2. Configure link according to Receiver Capabilities
+ *  3. Use Link Training from 2.5.3.3 and 3.5.1.3
+ *  4. Check link status on receipt of hot-plug interrupt
+ */
+
+static void
+intel_dp_check_link_status(struct intel_output *intel_output)
+{
+       struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+       uint8_t link_status[DP_LINK_STATUS_SIZE];
+
+       if (!intel_output->enc.crtc)
+               return;
+
+       if (!intel_dp_get_link_status(intel_output, link_status)) {
+               intel_dp_link_down(intel_output, dp_priv->DP);
+               return;
+       }
+
+       if (!intel_channel_eq_ok(link_status, dp_priv->lane_count))
+               intel_dp_link_train(intel_output, dp_priv->DP, dp_priv->link_configuration);
+}
+#endif
+
+/**
+ * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect DP connection.
+ *
+ * \return true if DP port is connected.
+ * \return false if DP port is disconnected.
+ */
+static enum drm_connector_status
+intel_dp_detect(struct drm_connector *connector)
+{
+       struct intel_output *intel_output = to_intel_output(connector);
+       struct drm_device *dev = intel_output->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+       uint32_t 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 (dp_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;
+       }
+
+       temp = I915_READ(PORT_HOTPLUG_STAT);
+
+       if ((temp & bit) == 0)
+               return connector_status_disconnected;
+
+       status = connector_status_disconnected;
+       if (intel_dp_aux_native_read(intel_output,
+                                    0x000, 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 has an audio sink
+        */
+
+       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 = {
+       .dpms = drm_helper_connector_dpms,
+       .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,
+};
+
+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_DisplayPort);
+       drm_connector_helper_add(connector, &intel_dp_connector_helper_funcs);
+
+       intel_output->type = INTEL_OUTPUT_DISPLAYPORT;
+
+       connector->interlace_allowed = true;
+       connector->doublescan_allowed = 0;
+
+       dp_priv->intel_output = intel_output;
+       dp_priv->output_reg = output_reg;
+       dp_priv->has_audio = false;
+       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);
+
+       /* Set up the DDC bus. */
+       intel_dp_i2c_init(intel_output,
+                         (output_reg == DP_B) ? "DPDDC-B" :
+                         (output_reg == DP_C) ? "DPDDC-C" : "DPDDC-D");
+       intel_output->ddc_bus = &dp_priv->adapter;
+
+       /* 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);
+       }
+}
diff --git a/drivers/gpu/drm/i915/intel_dp.h b/drivers/gpu/drm/i915/intel_dp.h
new file mode 100644 (file)
index 0000000..2b38054
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * 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 << 4)
+#define AUX_NATIVE_REPLY_NACK  (0x1 << 4)
+#define AUX_NATIVE_REPLY_DEFER (0x2 << 4)
+#define AUX_NATIVE_REPLY_MASK  (0x3 << 4)
+
+#define AUX_I2C_REPLY_ACK      (0x0 << 6)
+#define AUX_I2C_REPLY_NACK     (0x1 << 6)
+#define AUX_I2C_REPLY_DEFER    (0x2 << 6)
+#define AUX_I2C_REPLY_MASK     (0x3 << 6)
+
+/* 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
+
+struct i2c_algo_dp_aux_data {
+       bool running;
+       u16 address;
+       int (*aux_ch) (struct i2c_adapter *adapter,
+                      uint8_t *send, int send_bytes,
+                      uint8_t *recv, int recv_bytes);
+};
+
+int
+i2c_dp_aux_add_bus(struct i2c_adapter *adapter);
+
+#endif /* _INTEL_DP_H_ */
diff --git a/drivers/gpu/drm/i915/intel_dp_i2c.c b/drivers/gpu/drm/i915/intel_dp_i2c.c
new file mode 100644 (file)
index 0000000..4e60f14
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * Copyright © 2009 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/i2c.h>
+#include "intel_dp.h"
+
+/* Run a single AUX_CH I2C transaction, writing/reading data as necessary */
+
+#define MODE_I2C_START 1
+#define MODE_I2C_WRITE 2
+#define MODE_I2C_READ  4
+#define MODE_I2C_STOP  8
+
+static int
+i2c_algo_dp_aux_transaction(struct i2c_adapter *adapter, int mode,
+                           uint8_t write_byte, uint8_t *read_byte)
+{
+       struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
+       uint16_t address = algo_data->address;
+       uint8_t msg[5];
+       uint8_t reply[2];
+       int msg_bytes;
+       int reply_bytes;
+       int ret;
+
+       /* Set up the command byte */
+       if (mode & MODE_I2C_READ)
+               msg[0] = AUX_I2C_READ << 4;
+       else
+               msg[0] = AUX_I2C_WRITE << 4;
+
+       if (!(mode & MODE_I2C_STOP))
+               msg[0] |= AUX_I2C_MOT << 4;
+
+       msg[1] = address >> 8;
+       msg[2] = address;
+
+       switch (mode) {
+       case MODE_I2C_WRITE:
+               msg[3] = 0;
+               msg[4] = write_byte;
+               msg_bytes = 5;
+               reply_bytes = 1;
+               break;
+       case MODE_I2C_READ:
+               msg[3] = 0;
+               msg_bytes = 4;
+               reply_bytes = 2;
+               break;
+       default:
+               msg_bytes = 3;
+               reply_bytes = 1;
+               break;
+       }
+
+       for (;;) {
+               ret = (*algo_data->aux_ch)(adapter,
+                                          msg, msg_bytes,
+                                          reply, reply_bytes);
+               if (ret < 0) {
+                       printk(KERN_ERR "aux_ch failed %d\n", ret);
+                       return ret;
+               }
+               switch (reply[0] & AUX_I2C_REPLY_MASK) {
+               case AUX_I2C_REPLY_ACK:
+                       if (mode == MODE_I2C_READ) {
+                               *read_byte = reply[1];
+                       }
+                       return reply_bytes - 1;
+               case AUX_I2C_REPLY_NACK:
+                       printk(KERN_ERR "aux_ch nack\n");
+                       return -EREMOTEIO;
+               case AUX_I2C_REPLY_DEFER:
+                       printk(KERN_ERR "aux_ch defer\n");
+                       udelay(100);
+                       break;
+               default:
+                       printk(KERN_ERR "aux_ch invalid reply 0x%02x\n", reply[0]);
+                       return -EREMOTEIO;
+               }
+       }
+}
+
+/*
+ * 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 int
+i2c_algo_dp_aux_address(struct i2c_adapter *adapter, u16 address, bool reading)
+{
+       struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
+       int mode = MODE_I2C_START;
+       int ret;
+
+       if (reading)
+               mode |= MODE_I2C_READ;
+       else
+               mode |= MODE_I2C_WRITE;
+       algo_data->address = address;
+       algo_data->running = true;
+       ret = i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL);
+       return ret;
+}
+
+/*
+ * Stop the I2C transaction. This closes out the link, sending
+ * a bare address packet with the MOT bit turned off
+ */
+static void
+i2c_algo_dp_aux_stop(struct i2c_adapter *adapter, bool reading)
+{
+       struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
+       int mode = MODE_I2C_STOP;
+
+       if (reading)
+               mode |= MODE_I2C_READ;
+       else
+               mode |= MODE_I2C_WRITE;
+       if (algo_data->running) {
+               (void) i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL);
+               algo_data->running = false;
+       }
+}
+
+/*
+ * Write a single byte to the current I2C address, the
+ * the I2C link must be running or this returns -EIO
+ */
+static int
+i2c_algo_dp_aux_put_byte(struct i2c_adapter *adapter, u8 byte)
+{
+       struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
+       int ret;
+
+       if (!algo_data->running)
+               return -EIO;
+
+       ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_WRITE, byte, NULL);
+       return ret;
+}
+
+/*
+ * Read a single byte from the current I2C address, the
+ * I2C link must be running or this returns -EIO
+ */
+static int
+i2c_algo_dp_aux_get_byte(struct i2c_adapter *adapter, u8 *byte_ret)
+{
+       struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
+       int ret;
+
+       if (!algo_data->running)
+               return -EIO;
+
+       ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_READ, 0, byte_ret);
+       return ret;
+}
+
+static int
+i2c_algo_dp_aux_xfer(struct i2c_adapter *adapter,
+                    struct i2c_msg *msgs,
+                    int num)
+{
+       int ret = 0;
+       bool reading = false;
+       int m;
+       int b;
+
+       for (m = 0; m < num; m++) {
+               u16 len = msgs[m].len;
+               u8 *buf = msgs[m].buf;
+               reading = (msgs[m].flags & I2C_M_RD) != 0;
+               ret = i2c_algo_dp_aux_address(adapter, msgs[m].addr, reading);
+               if (ret < 0)
+                       break;
+               if (reading) {
+                       for (b = 0; b < len; b++) {
+                               ret = i2c_algo_dp_aux_get_byte(adapter, &buf[b]);
+                               if (ret < 0)
+                                       break;
+                       }
+               } else {
+                       for (b = 0; b < len; b++) {
+                               ret = i2c_algo_dp_aux_put_byte(adapter, buf[b]);
+                               if (ret < 0)
+                                       break;
+                       }
+               }
+               if (ret < 0)
+                       break;
+       }
+       if (ret >= 0)
+               ret = num;
+       i2c_algo_dp_aux_stop(adapter, reading);
+       printk(KERN_ERR "dp_aux_xfer return %d\n", ret);
+       return ret;
+}
+
+static u32
+i2c_algo_dp_aux_functionality(struct i2c_adapter *adapter)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
+              I2C_FUNC_SMBUS_READ_BLOCK_DATA |
+              I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
+              I2C_FUNC_10BIT_ADDR;
+}
+
+static const struct i2c_algorithm i2c_dp_aux_algo = {
+       .master_xfer    = i2c_algo_dp_aux_xfer,
+       .functionality  = i2c_algo_dp_aux_functionality,
+};
+
+static void
+i2c_dp_aux_reset_bus(struct i2c_adapter *adapter)
+{
+       (void) i2c_algo_dp_aux_address(adapter, 0, false);
+       (void) i2c_algo_dp_aux_stop(adapter, false);
+                                          
+}
+
+static int
+i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter)
+{
+       adapter->algo = &i2c_dp_aux_algo;
+       adapter->retries = 3;
+       i2c_dp_aux_reset_bus(adapter);
+       return 0;
+}
+
+int
+i2c_dp_aux_add_bus(struct i2c_adapter *adapter)
+{
+       int error;
+       
+       error = i2c_dp_aux_prepare_bus(adapter);
+       if (error)
+               return error;
+       error = i2c_add_adapter(adapter);
+       return error;
+}
+EXPORT_SYMBOL(i2c_dp_aux_add_bus);
index c5858792c80635828eca4927cccbddbf181fc8a5..004541c935a80bd61bf591c820131d3c3bd02843 100644 (file)
@@ -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
@@ -116,6 +117,10 @@ extern bool intel_sdvo_init(struct drm_device *dev, int output_device);
 extern void intel_dvo_init(struct drm_device *dev);
 extern void intel_tv_init(struct drm_device *dev);
 extern void intel_lvds_init(struct drm_device *dev);
+extern void intel_dp_init(struct drm_device *dev, int dp_reg);
+void
+intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
+                struct drm_display_mode *adjusted_mode);
 
 extern void intel_crtc_load_lut(struct drm_crtc *crtc);
 extern void intel_encoder_prepare (struct drm_encoder *encoder);