From patchwork Tue Sep 8 12:28:00 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Ander Conselvan de Oliveira X-Patchwork-Id: 7140661 Return-Path: X-Original-To: patchwork-intel-gfx@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 9C232BF036 for ; Tue, 8 Sep 2015 12:28:35 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id D1898206F2 for ; Tue, 8 Sep 2015 12:28:33 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id C1711206EB for ; Tue, 8 Sep 2015 12:28:31 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 1809D6E624; Tue, 8 Sep 2015 05:28:31 -0700 (PDT) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mga01.intel.com (mga01.intel.com [192.55.52.88]) by gabe.freedesktop.org (Postfix) with ESMTP id 0D91D6E624 for ; Tue, 8 Sep 2015 05:28:29 -0700 (PDT) Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by fmsmga101.fm.intel.com with ESMTP; 08 Sep 2015 05:28:28 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.17,489,1437462000"; d="scan'208";a="557507347" Received: from linux.intel.com ([10.23.219.25]) by FMSMGA003.fm.intel.com with ESMTP; 08 Sep 2015 05:28:28 -0700 Received: from localhost (aconselv-mobl3.ger.corp.intel.com [10.252.9.149]) by linux.intel.com (Postfix) with ESMTP id 625BE6A408F; Tue, 8 Sep 2015 05:27:34 -0700 (PDT) From: Ander Conselvan de Oliveira To: intel-gfx@lists.freedesktop.org Date: Tue, 8 Sep 2015 15:28:00 +0300 Message-Id: <1441715280-11623-10-git-send-email-ander.conselvan.de.oliveira@intel.com> X-Mailer: git-send-email 2.4.3 In-Reply-To: <1441715280-11623-1-git-send-email-ander.conselvan.de.oliveira@intel.com> References: <1441715280-11623-1-git-send-email-ander.conselvan.de.oliveira@intel.com> MIME-Version: 1.0 Cc: jani.nikula@intel.com, Ander Conselvan de Oliveira Subject: [Intel-gfx] [PATCH i-g-t] Add a link training test X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 + * 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 +#include +#include +#include +#include + +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 + +#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 + * + */ + +#include +#include + +#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 +#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; +}