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 <airlied@xxxxxxxx> + * 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.oliveira@xxxxxxxxx> + * + */ + +#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@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/intel-gfx