This adds a test that compiles the link training code from i915 into a
separate executable and uses it to train a fake sink device. The test
also uses device information from i915 to exercise the different code
paths for different hardwdare generations.

In order to get the code to compile a lot of stubbing was necessary. It
was also easier to copy a few functions from the drm_dp_helpers instead
of getting the whole thing to compile as part of the test.
---
 link-training-test/Makefile             |  40 +++
 link-training-test/drm_dp_helper.c      | 115 +++++++++
 link-training-test/intel_drv.h          | 148 ++++++++++++
 link-training-test/link_training_test.c | 414 ++++++++++++++++++++++++++++++++
 4 files changed, 717 insertions(+)
 create mode 100644 link-training-test/Makefile
 create mode 100644 link-training-test/drm_dp_helper.c
 create mode 100644 link-training-test/intel_drv.h
 create mode 100644 link-training-test/link_training_test.c

diff --git a/link-training-test/Makefile b/link-training-test/Makefile
new file mode 100644
index 0000000..07a9914
--- /dev/null
+++ b/link-training-test/Makefile
@@ -0,0 +1,40 @@
+KERNEL_SRC_DIR=/home/aconselv/linux
+
+# Files copied from i915 source tree
+COPIED_SOURCES = \
+       intel_dp_link_training.c \
+       intel_dev_info.c \
+       intel_dev_info.h \
+       i915_reg.h
+
+INTEL_DP_LINK_TRAINING_C = 
$(KERNEL_SRC_DIR)/drivers/gpu/drm/i915/intel_dp_link_training.c
+
+INCLUDEDIR = \
+       -I$(KERNEL_SRC_DIR)/include/drm \
+       -I$(KERNEL_SRC_DIR)/ \
+       -I.
+
+DEFINES = \
+       -D__KERNEL__
+
+HEADER_FILES = \
+       intel_drv.h
+
+SOURCE_FILES = \
+       intel_dp_link_training.c \
+       link_training_test.c \
+       drm_dp_helper.c
+
+all: link_training_test
+
+#intel_dp_link_training.c: $(INTEL_DP_LINK_TRAINING_C)
+#      cp $(INTEL_DP_LINK_TRAINING_C) .
+
+$(COPIED_SOURCES): %: $(KERNEL_SRC_DIR)/drivers/gpu/drm/i915/%
+       cp $< $@
+
+link_training_test: $(COPIED_SOURCES) $(SOURCE_FILES) $(HEADER_FILES)
+       gcc -g3 -O0 -std=gnu99 -o link_training_test $(SOURCE_FILES) 
$(INCLUDEDIR) $(DEFINES)
+
+clean:
+       rm link_training_test $(COPIED_SOURCES)
diff --git a/link-training-test/drm_dp_helper.c 
b/link-training-test/drm_dp_helper.c
new file mode 100644
index 0000000..a8db7f9
--- /dev/null
+++ b/link-training-test/drm_dp_helper.c
@@ -0,0 +1,115 @@
+/*
+ * 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 "intel_drv.h"
+
+/* TODO: Get rid of this copy of drm_dp_helper functions. */
+
+static u8 dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
+{
+        return link_status[r - DP_LANE0_1_STATUS];
+}
+
+static u8 dp_get_lane_status(const u8 link_status[DP_LINK_STATUS_SIZE],
+                             int lane)
+{
+        int i = DP_LANE0_1_STATUS + (lane >> 1);
+        int s = (lane & 1) * 4;
+        u8 l = dp_link_status(link_status, i);
+        return (l >> s) & 0xf;
+}
+
+bool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
+                          int lane_count)
+{
+        u8 lane_align;
+        u8 lane_status;
+        int lane;
+
+        lane_align = 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 = dp_get_lane_status(link_status, lane);
+                if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS)
+                        return false;
+        }
+        return true;
+}
+
+bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
+                              int lane_count)
+{
+        int lane;
+        u8 lane_status;
+
+        for (lane = 0; lane < lane_count; lane++) {
+                lane_status = dp_get_lane_status(link_status, lane);
+                if ((lane_status & DP_LANE_CR_DONE) == 0)
+                        return false;
+        }
+        return true;
+}
+
+u8 drm_dp_get_adjust_request_voltage(const u8 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);
+        u8 l = dp_link_status(link_status, i);
+
+        return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
+}
+
+u8 drm_dp_get_adjust_request_pre_emphasis(const u8 
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);
+        u8 l = dp_link_status(link_status, i);
+
+        return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
+}
+
+/* FIXME: */
+static void udelay() {}
+static void mdelay() {}
+
+void drm_dp_link_train_clock_recovery_delay(const u8 
dpcd[DP_RECEIVER_CAP_SIZE]) {
+        if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0)
+                udelay(100);
+        else
+                mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4);
+}
+
+void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
+        if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0)
+                udelay(400);
+        else
+                mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4);
+}
+
diff --git a/link-training-test/intel_drv.h b/link-training-test/intel_drv.h
new file mode 100644
index 0000000..f7a6a6c
--- /dev/null
+++ b/link-training-test/intel_drv.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2006 Dave Airlie <airl...@linux.ie>
+ * Copyright (c) 2007-2015 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.
+ */
+
+#ifndef FAKE_INTEL_DRV_H
+#define FAKE_INTEL_DRV_H
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <tools/include/linux/compiler.h>
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+typedef unsigned long size_t;
+typedef long ssize_t;
+
+struct drm_device {
+       void *dev_private;
+};
+
+static inline struct drm_i915_private *to_i915(const struct drm_device *dev)
+{
+       return dev->dev_private;
+}
+
+#define BUILD_BUG()
+
+#include "intel_dev_info.h"
+
+struct drm_i915_private {
+       struct intel_device_info info;
+       bool edp_low_vswing;
+       enum intel_pch pch_type;
+};
+
+
+struct i2c_adapter {
+};
+
+struct mutex {
+};
+
+#include <drm_dp_helper.h>
+
+#define DRM_ERROR printf
+#define DRM_DEBUG_KMS printf
+
+enum port {
+        PORT_A = 0,
+        PORT_B,
+        PORT_C,
+        PORT_D,
+        PORT_E,
+        I915_MAX_PORTS
+};
+#define port_name(p) ((p) + 'A')
+
+struct drm_encoder {
+       void *dev;
+};
+
+struct intel_encoder {
+       struct drm_encoder base;
+};
+
+struct intel_dp {
+       int link_rate;
+       int lane_count;
+       uint8_t link_bw;
+       uint8_t num_sink_rates;
+       uint8_t train_set[4];
+       bool train_set_valid;
+       struct drm_dp_aux aux;
+       uint8_t dpcd[DP_RECEIVER_CAP_SIZE];
+       uint32_t DP;
+       bool use_tps3;
+
+       /* Hold test private data */
+       void *priv;
+};
+
+struct intel_digital_port {
+       struct intel_encoder base;
+       struct intel_dp dp;
+       enum port port;
+};
+
+#define offsetof(type, member)  __builtin_offsetof (type, member)
+#define container_of(ptr, type, member) ({                     \
+       const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+       (type *)( (char *)__mptr - offsetof(type,member) );})
+
+static inline struct intel_digital_port *
+dp_to_dig_port(struct intel_dp *intel_dp)
+{
+       return container_of(intel_dp, struct intel_digital_port, dp);
+}
+
+void
+intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
+                                      uint8_t dp_train_pat);
+void
+intel_dp_update_signal_levels(struct intel_dp *intel_dp);
+void intel_dp_set_idle_link_train(struct intel_dp *intel_dp);
+void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
+                          uint8_t *link_bw, uint8_t *rate_select);
+bool
+intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t 
link_status[DP_LINK_STATUS_SIZE]);
+void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder);
+void
+intel_dp_start_link_train(struct intel_dp *intel_dp);
+void
+intel_dp_stop_link_train(struct intel_dp *intel_dp);
+bool intel_dp_source_supports_hbr2(struct drm_device *dev);
+
+static inline struct drm_device *intel_dp_to_dev(struct intel_dp *intel_dp)
+{
+       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+
+       return intel_dig_port->base.base.dev;
+}
+
+#include "i915_reg.h"
+
+#endif /* FAKE_INTEL_DRV_H */
diff --git a/link-training-test/link_training_test.c 
b/link-training-test/link_training_test.c
new file mode 100644
index 0000000..aa73b9e
--- /dev/null
+++ b/link-training-test/link_training_test.c
@@ -0,0 +1,414 @@
+/*
+ * Copyright © 2015 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:
+ *    Ander Conselvan de Oliveira <ander.conselvan.de.olive...@intel.com>
+ *
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include "intel_drv.h"
+#include "i915_reg.h"
+
+struct sink_device {
+       ssize_t (*dpcd_write)(struct sink_device *sink, unsigned int offset,
+                             void *buffer, size_t size);
+       bool (*get_link_status)(struct sink_device *sink,
+                               uint8_t link_status[DP_LINK_STATUS_SIZE]);
+
+       struct {
+               bool lane_count_and_bw_set;
+               bool training_pattern_1_set;
+               bool started_with_non_zero_levels;
+               bool cr_done;
+               bool channel_eq_done;
+
+               uint8_t dpcd[0x3000];
+       } data;
+};
+
+/* Fake sink device implementation */
+
+static uint8_t
+sink_device_lane_count(struct sink_device *sink)
+{
+       return sink->data.dpcd[DP_LANE_COUNT_SET];
+}
+
+static uint8_t
+sink_device_get_training_pattern(struct sink_device *sink)
+{
+       return sink->data.dpcd[DP_TRAINING_PATTERN_SET] & 
DP_TRAINING_PATTERN_MASK;
+}
+
+static uint8_t
+sink_device_get_voltage_swing(struct sink_device *sink, int lane)
+{
+       return sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] &
+               DP_TRAIN_VOLTAGE_SWING_MASK;
+}
+
+static uint8_t
+sink_device_get_pre_emphasis_level(struct sink_device *sink, int lane)
+{
+       return sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] &
+                DP_TRAIN_PRE_EMPHASIS_MASK;
+}
+
+static void
+sink_device_check_lane_count_and_bw(struct sink_device *sink)
+{
+       if (sink->data.lane_count_and_bw_set)
+               return;
+
+       assert(sink->data.dpcd[DP_TRAINING_PATTERN_SET] == 0);
+
+       if (sink->data.dpcd[DP_LINK_BW_SET] != 0 &&
+           sink->data.dpcd[DP_LANE_COUNT_SET] != 0)
+               sink->data.lane_count_and_bw_set = true;
+}
+
+static void
+sink_device_check_pattern_1_set(struct sink_device *sink)
+{
+       if (!sink->data.lane_count_and_bw_set ||
+           sink->data.training_pattern_1_set)
+               return;
+
+       assert(sink_device_get_training_pattern(sink) <= DP_TRAINING_PATTERN_1);
+
+       if (sink_device_get_training_pattern(sink) != DP_TRAINING_PATTERN_1)
+               return;
+
+       assert(sink->data.dpcd[DP_LINK_BW_SET] == DP_LINK_BW_1_62 ||
+              sink->data.dpcd[DP_LINK_BW_SET] == DP_LINK_BW_2_7);
+
+       assert(sink->data.dpcd[DP_LANE_COUNT_SET] == 1 ||
+              sink->data.dpcd[DP_LANE_COUNT_SET] == 2 ||
+              sink->data.dpcd[DP_LANE_COUNT_SET] == 4);
+
+       for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
+               if (sink_device_get_voltage_swing(sink, lane) != 
DP_TRAIN_VOLTAGE_SWING_LEVEL_0 ||
+                   sink_device_get_pre_emphasis_level(sink, lane) != 
DP_TRAIN_PRE_EMPH_LEVEL_0)
+                       sink->data.started_with_non_zero_levels = true;
+       }
+
+       sink->data.training_pattern_1_set = true;
+}
+
+static void
+sink_device_check_pattern_2_set(struct sink_device *sink)
+{
+       if (!sink->data.cr_done)
+               return;
+
+       assert(sink_device_get_training_pattern(sink) == DP_TRAINING_PATTERN_2);
+}
+
+static void
+sink_device_check_pattern_disable(struct sink_device *sink)
+{
+       if (!sink->data.cr_done || ! sink->data.channel_eq_done)
+               return;
+
+       assert(sink_device_get_training_pattern(sink) == 
DP_TRAINING_PATTERN_DISABLE);
+}
+
+static ssize_t
+sink_device_dpcd_write(struct sink_device *sink, unsigned int offset,
+                      void *buffer, size_t size)
+{
+       memcpy(sink->data.dpcd + offset, buffer, size);
+
+       sink_device_check_lane_count_and_bw(sink);
+
+       if (!sink->data.cr_done)
+               sink_device_check_pattern_1_set(sink);
+       else if (!sink->data.channel_eq_done)
+               sink_device_check_pattern_2_set(sink);
+       else
+               sink_device_check_pattern_disable(sink);
+
+       return size;
+}
+
+static bool
+sink_device_max_voltage_reached(struct sink_device *sink, int lane)
+{
+       return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] & 
DP_TRAIN_MAX_SWING_REACHED) ==
+               DP_TRAIN_MAX_SWING_REACHED;
+}
+
+static bool
+sink_device_max_pre_emphasis_reached(struct sink_device *sink, int lane)
+{
+       return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] & 
DP_TRAIN_MAX_PRE_EMPHASIS_REACHED) ==
+               DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+}
+
+static bool
+sink_device_request_higher_voltage_swing(struct sink_device *sink)
+{
+       bool max_reached;
+
+       for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
+               if (sink_device_max_voltage_reached(sink, lane)) {
+                       max_reached = true;
+                       break;
+               }
+       }
+
+       if (max_reached)
+               return false;
+
+       for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
+               sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |=
+                       (sink_device_get_voltage_swing(sink, lane) + 1) <<
+                               (DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 
1));
+       }
+
+       return true;
+}
+
+static bool
+sink_device_request_higher_pre_emphasis(struct sink_device *sink)
+{
+       bool max_reached;
+
+       for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
+               if (sink_device_max_pre_emphasis_reached(sink, lane)) {
+                       max_reached = true;
+                       break;
+               }
+       }
+
+       if (max_reached)
+               return false;
+
+       for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
+               sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |=
+                       (sink_device_get_pre_emphasis_level(sink, lane) + 1) <<
+                               (DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT +
+                                (DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 
1)));
+       }
+
+       return true;
+}
+
+static void
+sink_device_mark_cr_done(struct sink_device *sink)
+{
+       for (int lane = 0; lane < sink_device_lane_count(sink); lane++)
+               sink->data.dpcd[DP_LANE0_1_STATUS + (lane >> 1)] |=
+                       DP_LANE_CR_DONE << (4 * (lane & 1));
+
+       sink->data.cr_done = true;
+}
+
+static void
+sink_device_mark_channel_eq_done(struct sink_device *sink)
+{
+       for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
+               uint8_t mask = (DP_LANE_CHANNEL_EQ_DONE | 
DP_LANE_SYMBOL_LOCKED);
+               sink->data.dpcd[DP_LANE0_1_STATUS + (lane >> 1)] |=
+                       mask << (4 * (lane & 1));
+       }
+
+       sink->data.dpcd[DP_LANE_ALIGN_STATUS_UPDATED] |= 
DP_INTERLANE_ALIGN_DONE;
+
+       sink->data.channel_eq_done = true;
+}
+
+static bool
+sink_device_get_link_status(struct sink_device *sink,
+                           uint8_t link_status[DP_LINK_STATUS_SIZE])
+{
+       if (!sink->data.cr_done) {
+               if (!sink_device_request_higher_voltage_swing(sink))
+                       sink_device_mark_cr_done(sink);
+       } else if (!sink->data.channel_eq_done) {
+               if (!sink_device_request_higher_pre_emphasis(sink))
+                       sink_device_mark_channel_eq_done(sink);
+       }
+
+       memcpy(link_status, sink->data.dpcd + DP_LANE0_1_STATUS,
+              DP_LINK_STATUS_SIZE);
+
+       return true;
+}
+
+static struct sink_device simple_sink = {
+       .get_link_status = sink_device_get_link_status,
+       .dpcd_write = sink_device_dpcd_write,
+};
+
+/* Glue code */
+
+void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder)
+{
+}
+
+void intel_dp_set_idle_link_train(struct intel_dp *intel_dp)
+{
+}
+
+bool intel_dp_source_supports_hbr2(struct drm_device *dev)
+{
+       return false;
+}
+
+bool
+intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t 
link_status[DP_LINK_STATUS_SIZE])
+{
+       struct sink_device *sink = intel_dp->priv;
+       return sink->get_link_status(sink, link_status);
+}
+
+void
+intel_dp_update_signal_levels(struct intel_dp *intel_dp)
+{
+}
+
+void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
+                          uint8_t *link_bw, uint8_t *rate_select)
+{
+       *link_bw = intel_dp->link_bw;
+       *rate_select = 0;
+}
+
+void
+intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
+                                      uint8_t dp_train_pat)
+{
+}
+
+ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset,
+                         void *buffer, size_t size)
+{
+       struct intel_dp *intel_dp =
+               container_of(aux, struct intel_dp, aux);
+       struct sink_device *sink = intel_dp->priv;
+
+       return sink->dpcd_write(sink, offset, buffer, size);
+}
+
+/* --- */
+
+static struct intel_dp *
+intel_dp_create(struct drm_device *dev, int lanes, uint8_t link_bw)
+{
+       struct intel_digital_port *dig_port;
+
+       dig_port = calloc(1, sizeof *dig_port);
+       dig_port->base.base.dev = dev;
+       dig_port->dp.lane_count = lanes;
+       dig_port->dp.link_bw = link_bw;
+
+       return &dig_port->dp;
+}
+
+static void
+intel_dp_destroy(struct intel_dp *intel_dp)
+{
+       struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
+       free(dig_port);
+}
+
+/* FIXME: Yikes! */
+#define BITS_PER_LONG 64
+#include <include/linux/mod_devicetable.h>
+#include <i915_pciids.h>
+#include "intel_dev_info.c"
+
+static const struct pci_device_id pciidlist[] = {
+       INTEL_PCI_IDS,
+       {0, 0, 0}
+};
+
+struct drm_device *
+drm_device_for_pci_id(const struct pci_device_id *id)
+{
+       struct drm_device *dev;
+       struct drm_i915_private *dev_priv;
+
+       dev = calloc(1, sizeof *dev);
+       dev_priv = calloc(1, sizeof *dev_priv);
+       assert(dev && dev_priv);
+
+       dev->dev_private = dev_priv;
+
+       memcpy(&dev_priv->info, (void *) id->driver_data, sizeof 
dev_priv->info);
+       dev_priv->info.device_id = id->device;
+
+       /* TODO: set dev_priv->pch_type with an appropriate value */
+       dev_priv->pch_type = PCH_NONE;
+
+       return dev;
+}
+
+void
+drm_device_destroy(struct drm_device *dev)
+{
+       free(dev->dev_private);
+       free(dev);
+}
+
+int
+main(int argc, char *argv[])
+{
+       const struct pci_device_id *id;
+
+       for (id = &pciidlist[0];
+            id->vendor != 0 && id->device != 0;
+            id++) {
+               struct drm_device *dev = drm_device_for_pci_id(id);
+               struct intel_dp *intel_dp =
+                       intel_dp_create(dev, 4, DP_LINK_BW_2_7);
+
+               if (IS_GEN2(dev) || IS_PINEVIEW(dev))
+                       continue;
+
+               printf("Testing with device id %04x, gen %d\n",
+                      INTEL_DEVID(dev), INTEL_INFO(dev)->gen);
+
+               intel_dp->priv = &simple_sink;
+               memset(&simple_sink.data, 0, sizeof simple_sink.data);
+               simple_sink.data.dpcd[DP_MAX_LINK_RATE] = 0x0A;
+               simple_sink.data.dpcd[DP_MAX_LANE_COUNT] = 0x04;
+
+               intel_dp_start_link_train(intel_dp);
+               intel_dp_stop_link_train(intel_dp);
+
+               for (int lane = 0; lane < intel_dp->lane_count; lane++)
+                       printf("lane %i: vswing: %d, pre-emph: %d\n", lane,
+                              sink_device_get_voltage_swing(&simple_sink, 
lane),
+                              sink_device_get_pre_emphasis_level(&simple_sink, 
lane));
+               printf("\n");
+
+               intel_dp_destroy(intel_dp);
+               drm_device_destroy(dev);
+       }
+
+       return 0;
+}
-- 
2.4.3

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to