[Intel-gfx] [PATCH i-g-t] Add a link training test

Ville Syrjälä ville.syrjala at linux.intel.com
Tue Sep 8 06:11:46 PDT 2015


On Tue, Sep 08, 2015 at 03:28:00PM +0300, Ander Conselvan de Oliveira wrote:
> 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.

Hmm. So the only device spefic information you really need is:
- supported link rates
- max vswing/pre-emph settings
- DDI vs. not

Cooking up a few configurations with varying options would seem fairly
easy without having to pull in the whole device info, and the hw
specific max vswing/pre-emphasis stuff.

> ---
>  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 at 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.oliveira at 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

-- 
Ville Syrjälä
Intel OTC


More information about the Intel-gfx mailing list