From patchwork Tue Sep 15 13:11:36 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: 7184801 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 18709BEEC1 for ; Tue, 15 Sep 2015 13:12:01 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id C0EE5205E9 for ; Tue, 15 Sep 2015 13:11:54 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id CB01620694 for ; Tue, 15 Sep 2015 13:11:50 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 349B46E337; Tue, 15 Sep 2015 06:11:50 -0700 (PDT) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) by gabe.freedesktop.org (Postfix) with ESMTP id 26AAC6E0A4 for ; Tue, 15 Sep 2015 06:11:48 -0700 (PDT) Received: from orsmga001.jf.intel.com ([10.7.209.18]) by fmsmga102.fm.intel.com with ESMTP; 15 Sep 2015 06:11:47 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.17,536,1437462000"; d="scan'208";a="769420548" Received: from linux.intel.com ([10.23.219.25]) by orsmga001.jf.intel.com with ESMTP; 15 Sep 2015 06:11:48 -0700 Received: from localhost (aconselv-mobl3.fi.intel.com [10.237.66.163]) by linux.intel.com (Postfix) with ESMTP id 190A76A4005; Tue, 15 Sep 2015 06:10:53 -0700 (PDT) From: Ander Conselvan de Oliveira To: intel-gfx@lists.freedesktop.org Date: Tue, 15 Sep 2015 16:11:36 +0300 Message-Id: <1442322696-31286-2-git-send-email-ander.conselvan.de.oliveira@intel.com> X-Mailer: git-send-email 2.4.3 In-Reply-To: <1442322696-31286-1-git-send-email-ander.conselvan.de.oliveira@intel.com> References: <1442237891.2755.24.camel@gmail.com> <1442322696-31286-1-git-send-email-ander.conselvan.de.oliveira@intel.com> MIME-Version: 1.0 Cc: Ander Conselvan de Oliveira , thomas.wood@intel.com Subject: [Intel-gfx] [PATCH 2/2] drm/i915: 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 --- drivers/gpu/drm/i915/Kconfig | 10 + drivers/gpu/drm/i915/Makefile | 5 + drivers/gpu/drm/i915/i915_drv.c | 2 + drivers/gpu/drm/i915/i915_drv.h | 8 + .../drm/i915/tests/test_intel_dp_link_training.c | 467 +++++++++++++++++++++ drivers/gpu/drm/i915/tests/tests.c | 7 + drivers/gpu/drm/i915/tests/tests.h | 6 + 7 files changed, 505 insertions(+) create mode 100644 drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c create mode 100644 drivers/gpu/drm/i915/tests/tests.c create mode 100644 drivers/gpu/drm/i915/tests/tests.h diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig index 051eab3..fb42a9f 100644 --- a/drivers/gpu/drm/i915/Kconfig +++ b/drivers/gpu/drm/i915/Kconfig @@ -47,3 +47,13 @@ config DRM_I915_PRELIMINARY_HW_SUPPORT option changes the default for that module option. If in doubt, say "N". + +config DRM_I915_UNIT_TESTS + bool "Enable execution of unit tests on module load" + depends on DRM_I915 + default n + help + Choose this option to run a series of unit tests when the i915 + module is loaded. + + If in doubt, say "N". diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 0851de07..bd7e283 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -97,6 +97,11 @@ i915-y += i915_vgpu.o # legacy horrors i915-y += i915_dma.o +# Unit tests +i915-$(CONFIG_DRM_I915_UNIT_TESTS) += \ + tests/tests.o \ + tests/test_intel_dp_link_training.o + obj-$(CONFIG_DRM_I915) += i915.o CFLAGS_i915_trace_points.o := -I$(src) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index bdec64c..2494667 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1707,6 +1707,8 @@ static int __init i915_init(void) { driver.num_ioctls = i915_max_ioctl; + i915_run_unit_tests(); + /* * Enable KMS by default, unless explicitly overriden by * either the i915.modeset prarameter or by the diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 3bf8a9b..39c58d1 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -3502,4 +3502,12 @@ static inline void i915_trace_irq_get(struct intel_engine_cs *ring, i915_gem_request_assign(&ring->trace_irq_req, req); } +#ifdef CONFIG_DRM_I915_UNIT_TESTS +void i915_run_unit_tests(void); +#else +static inline void i915_run_unit_tests(void) +{ +} +#endif + #endif diff --git a/drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c b/drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c new file mode 100644 index 0000000..ab5de74 --- /dev/null +++ b/drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c @@ -0,0 +1,467 @@ +/* + * 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 "../intel_drv.h" + +#define assert(x) WARN_ON(!(x)) + +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) >> DP_TRAIN_PRE_EMPHASIS_SHIFT; +} + +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) +{ + int lane; + + 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 (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 void +sink_device_set_adjust_voltage(struct sink_device *sink, + int lane, uint8_t level) +{ + int shift = DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 1); + + sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] &= + ~(DP_ADJUST_VOLTAGE_SWING_LANE0_MASK << shift); + sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |= + level << shift; +} + +static void +sink_device_set_adjust_pre_emphasis(struct sink_device *sink, + int lane, uint8_t level) +{ + int shift = DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 1); + + sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] &= + ~(DP_ADJUST_PRE_EMPHASIS_LANE0_MASK << shift); + sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |= + level << (DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT + shift); +} + +static bool +sink_device_request_higher_voltage_swing(struct sink_device *sink) +{ + bool max_reached = false; + int lane; + + for (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 (lane = 0; lane < sink_device_lane_count(sink); lane++) { + uint8_t new_voltage = + sink_device_get_voltage_swing(sink, lane) + 1; + + sink_device_set_adjust_voltage(sink, lane, new_voltage); + } + + return true; +} + +static bool +sink_device_request_higher_pre_emphasis(struct sink_device *sink) +{ + bool max_reached = false; + int lane; + + for (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 (lane = 0; lane < sink_device_lane_count(sink); lane++) { + uint8_t new_pre_emphasis = + sink_device_get_pre_emphasis_level(sink, lane) + 1; + + sink_device_set_adjust_pre_emphasis(sink, lane, new_pre_emphasis); + } + + return true; +} + +static void +sink_device_mark_cr_done(struct sink_device *sink) +{ + int lane; + + for (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) +{ + int lane; + + for (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 void +sink_device_reset(struct sink_device *sink, int lanes, uint8_t link_bw) +{ + memset(&sink->data, 0, sizeof sink->data); + sink->data.dpcd[DP_MAX_LINK_RATE] = link_bw; + sink->data.dpcd[DP_MAX_LANE_COUNT] = lanes; +} + +static struct sink_device simple_sink = { + .get_link_status = sink_device_get_link_status, + .dpcd_write = sink_device_dpcd_write, +}; + +/* Glue code */ + +struct test_intel_dp { + struct intel_dp dp; + struct sink_device *sink; + uint8_t link_bw; + + uint8_t max_voltage; + uint8_t max_pre_emphasis; +}; + +static struct test_intel_dp * +to_test_intel_dp(struct intel_dp *dp) +{ + return container_of(dp, struct test_intel_dp, dp); +} + +void test_dp_set_idle_link_train(struct intel_dp *intel_dp) +{ +} + +bool +test_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE]) +{ + struct sink_device *sink = to_test_intel_dp(intel_dp)->sink; + return sink->get_link_status(sink, link_status); +} + +void +test_dp_update_signal_levels(struct intel_dp *intel_dp) +{ +} + +void test_dp_compute_rate(struct intel_dp *intel_dp, int port_clock, + uint8_t *link_bw, uint8_t *rate_select) +{ + *link_bw = to_test_intel_dp(intel_dp)->link_bw; + *rate_select = 0; +} + +void +test_dp_program_link_training_pattern(struct intel_dp *intel_dp, + uint8_t dp_train_pat) +{ +} + +uint8_t +test_dp_voltage_max(struct intel_dp *intel_dp) +{ + return to_test_intel_dp(intel_dp)->max_voltage << + DP_TRAIN_VOLTAGE_SWING_SHIFT; +} + +uint8_t +test_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing) +{ + return to_test_intel_dp(intel_dp)->max_pre_emphasis << + DP_TRAIN_PRE_EMPHASIS_SHIFT; +} + +ssize_t test_dp_dpcd_write(struct intel_dp *intel_dp, unsigned int offset, + void *buffer, size_t size) +{ + struct sink_device *sink = to_test_intel_dp(intel_dp)->sink; + return sink->dpcd_write(sink, offset, buffer, size); +} + +static bool test_dp_source_supports_hbr2(struct intel_dp *dp) +{ + return false; +} + +/* --- */ + +static struct test_intel_dp test_dp; + +static void +do_test(struct sink_device *sink, int lanes, uint8_t link_bw, + uint8_t max_voltage, uint8_t max_pre_emphasis) +{ + int lane; + + memset(&test_dp, 0, sizeof test_dp); + test_dp.dp.lane_count = lanes; + test_dp.link_bw = link_bw; + test_dp.sink = sink; + test_dp.max_voltage = + max_voltage >> DP_TRAIN_VOLTAGE_SWING_SHIFT; + test_dp.max_pre_emphasis = + max_pre_emphasis >> DP_TRAIN_PRE_EMPHASIS_SHIFT; + + test_dp.dp.get_link_status = test_dp_get_link_status; + test_dp.dp.set_idle_link_train = test_dp_set_idle_link_train; + test_dp.dp.update_signal_levels = test_dp_update_signal_levels; + test_dp.dp.compute_rate = test_dp_compute_rate; + test_dp.dp.program_link_training_pattern = + test_dp_program_link_training_pattern; + test_dp.dp.voltage_max = test_dp_voltage_max; + test_dp.dp.pre_emphasis_max = test_dp_pre_emphasis_max; + test_dp.dp.dpcd_write = test_dp_dpcd_write; + test_dp.dp.source_supports_hbr2 = test_dp_source_supports_hbr2; + + sink_device_reset(sink, lanes, link_bw); + + intel_dp_start_link_train(&test_dp.dp); + intel_dp_stop_link_train(&test_dp.dp); + + assert(sink->data.cr_done); + assert(sink->data.channel_eq_done); + + for (lane = 0; lane < test_dp.dp.lane_count; lane++) { + uint8_t cur_v = sink_device_get_voltage_swing(sink, lane); + uint8_t cur_p = sink_device_get_pre_emphasis_level(sink, lane); + + assert(cur_v == test_dp.max_voltage); + assert(cur_p == test_dp.max_pre_emphasis); + } +} + +int test_lanes[] = { + 1, 2, 4, +}; + +uint8_t test_bw[] = { + DP_LINK_BW_1_62, + DP_LINK_BW_2_7, +}; + +uint8_t test_max_voltage[] = { + DP_TRAIN_VOLTAGE_SWING_LEVEL_0, + DP_TRAIN_VOLTAGE_SWING_LEVEL_1, + DP_TRAIN_VOLTAGE_SWING_LEVEL_2, + DP_TRAIN_VOLTAGE_SWING_LEVEL_3, +}; + +uint8_t test_max_pre_emphasis[] = { + DP_TRAIN_PRE_EMPH_LEVEL_0, + DP_TRAIN_PRE_EMPH_LEVEL_1, + DP_TRAIN_PRE_EMPH_LEVEL_2, + DP_TRAIN_PRE_EMPH_LEVEL_3, +}; + +void +run_link_training_tests(void) +{ + int lane, bw, voltage, emph; + + for (lane = 0; lane < ARRAY_SIZE(test_lanes); lane++) + for (bw = 0; bw < ARRAY_SIZE(test_bw); bw++) + for (voltage = 0; voltage < ARRAY_SIZE(test_max_voltage); voltage++) + for (emph = 0; emph < ARRAY_SIZE(test_max_pre_emphasis); emph++) { + DRM_DEBUG_KMS("l%d-bw%d-v%d-pe%d", + test_lanes[lane], + test_bw[bw], + test_max_voltage[voltage], + test_max_pre_emphasis[emph]); + do_test(&simple_sink, + test_lanes[lane], + test_bw[bw], + test_max_voltage[voltage], + test_max_pre_emphasis[emph]); + } +} diff --git a/drivers/gpu/drm/i915/tests/tests.c b/drivers/gpu/drm/i915/tests/tests.c new file mode 100644 index 0000000..8e4344a --- /dev/null +++ b/drivers/gpu/drm/i915/tests/tests.c @@ -0,0 +1,7 @@ +#include "tests.h" + +void +i915_run_unit_tests(void) +{ + run_link_training_tests(); +} diff --git a/drivers/gpu/drm/i915/tests/tests.h b/drivers/gpu/drm/i915/tests/tests.h new file mode 100644 index 0000000..400b523 --- /dev/null +++ b/drivers/gpu/drm/i915/tests/tests.h @@ -0,0 +1,6 @@ +#ifndef _I915_TESTS_H_ +#define _I915_TESTS_H_ + +void run_link_training_tests(void); + +#endif /* _I915_TESTS_H_ */