From patchwork Tue May 17 00:50:40 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Navare, Manasi" X-Patchwork-Id: 9106351 Return-Path: X-Original-To: patchwork-intel-gfx@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 295149F1C1 for ; Tue, 17 May 2016 00:40:25 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id B70082026C for ; Tue, 17 May 2016 00:40:22 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id 17F57201FE for ; Tue, 17 May 2016 00:40:20 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 020166E5DC; Tue, 17 May 2016 00:40:19 +0000 (UTC) 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 6659E6E1A2 for ; Tue, 17 May 2016 00:40:16 +0000 (UTC) Received: from orsmga002.jf.intel.com ([10.7.209.21]) by fmsmga101.fm.intel.com with ESMTP; 16 May 2016 17:40:16 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.26,322,1459839600"; d="scan'208";a="978315734" Received: from manasi-otcmedia.jf.intel.com ([10.7.199.175]) by orsmga002.jf.intel.com with ESMTP; 16 May 2016 17:40:15 -0700 From: Manasi Navare To: intel-gfx@lists.freedesktop.org Date: Mon, 16 May 2016 17:50:40 -0700 Message-Id: <1463446240-19967-1-git-send-email-manasi.d.navare@intel.com> X-Mailer: git-send-email 1.9.1 Cc: Todd Previte Subject: [Intel-gfx] [PATCH i-g-t] tools: Add intel_dp_compliance for DisplayPort 1.2 compliance automation 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: , MIME-Version: 1.0 Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Spam-Status: No, score=-5.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, 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 is the userspace component of the Displayport Compliance testing software required for compliance testing of the I915 Display Port driver. This must be running in order to successfully complete Display Port compliance testing. This app and the kernel code that accompanies it has been written to satify the requirements of the Displayport Link CTS 1.2 rev1.1 specification from VESA. Note that this application does not support eDP compliance testing. This utility ahs an automation support for the EDID tests (4.2.2.3 - 4.2.2.6) and Video Pattern generation tests (400.3.3.1) from CTS specification 1.2 Rev 1.1. Currently the video pattern automation has been explicitly disabled until the link training automation is fully supported in the kernel. The Linux DUT running this utility must be in text (console) mode and cannot have any other display manager running. Since this uses sysfs nodes for kernel interaction, this utility should be run as Root. Once this user application is up and running, waiting for test requests, the test appliance software on the windows host can now be used to execute the compliance tests. This app is based on some prior work done in April 2015 (by Todd Previte ) v1: * Reworked gem_mmap/unmap logic so these calls go together (by Marius Vlad) * Cleaned up the setup_drm/restore/close calls so that it happens before the tests, after every test and at exit (by Marius Vlad) Signed-off-by: Marius Vlad Signed-off-by: Todd Previte Signed-off-by: Manasi Navare --- tools/Makefile.sources | 1 + tools/intel_dp_compliance.c | 1089 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1090 insertions(+) create mode 100644 tools/intel_dp_compliance.c diff --git a/tools/Makefile.sources b/tools/Makefile.sources index 5d5958d..3054856 100644 --- a/tools/Makefile.sources +++ b/tools/Makefile.sources @@ -13,6 +13,7 @@ bin_PROGRAMS = \ intel_bios_reader \ intel_display_crc \ intel_display_poller \ + intel_dp_compliance \ intel_dump_decode \ intel_error_decode \ intel_forcewaked \ diff --git a/tools/intel_dp_compliance.c b/tools/intel_dp_compliance.c new file mode 100644 index 0000000..31996ba --- /dev/null +++ b/tools/intel_dp_compliance.c @@ -0,0 +1,1089 @@ +/* + * Copyright ? 2014-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. + * + * Displayport Compliance Testing Application + * + * This is the userspace component of the Displayport Compliance testing + * software required for compliance testing of the I915 Display Port driver. + * This must be running in order to successfully complete Display Port + * compliance testing. This app and the kernel code that accompanies it has been + * written to satify the requirements of the Displayport Link CTS 1.2 rev1.1 + * specification from VESA. Note that this application does not support eDP + * compliance testing. + * + * Compliance Testing requires several components: + * - A kernel build that contains the patch set for DP compliance support + * Make sure it has the EDID compliance automation patch + * - A Displayport Compliance Testing appliance such as Unigraf-DPR120 + * - This user application + * - A windows host machine to run the DPR test software + * - Root access on the DUT due to the use of sysfs utility + * + * Test Setup: + * It is strongly recommended that the windows host, test appliance and DUT + * be freshly restarted before any testing begins to ensure that any previous + * configurations and settings will not interfere with test process. Refer to + * the test appliance documentation for setup, software installation and + * operation specific to that device. + * + * The Linux DUT must be in text (console) mode and cannot have any other + * display manager running. You must be logged in as root to run this user app. + * Once the user application is up and running, waiting for test requests, the + * software on the windows host can now be used to execute the compliance tests. + * + * This userspace application supports following tests from the DP CTS Spec + * Rev 1.1: + * - EDID Tests: Supports EDID read (4.2.2.3),EDID Read failure and corruption + * detection tests (4.2.2.4, 4.2.2.5, 4.2.2.6) + * - Video Pattern generation tests: This supports only the 24 and 18bpp color + * ramp test pattern (400.3.3.1). + * + * Connections (required): + * - Test Appliance connected to the external Displayport connector on the DUT + * - Test Appliance Monitor Out connected to Displayport connector on the + * monitor + * - Test appliance connected to the Windows Host via USB + * + * Debugfs Files: + * The file root for all the debugfs file: + * /sys/kernel/debug/dri/0/ + * + * The specific files are as follows: + * + * i915_dp_test_active + * A simple flag that indicates whether or not compliance testing is currently + * active in the kernel. This flag is polled by userspace and once set, invokes + * the test handler in the user app. This flag is set by the test handler in the + * kernel after reading the registers requested by the test appliance. + * + * i915_dp_test_data + * Test data is used by the kernel to pass parameters to the user app. Eg: In + * case of EDID tests, the data that is delivered to the userspace is the video + * mode to be set for the test. + * In case of video pattern test, the data that is delivered to the userspace is + * the width and height of the test pattern and the bits per color value. + * + * i915_dp_test_type + * The test type variable instructs the user app as to what the requested test + * was from the sink device. These values defined at the top of the application's + * main implementation file must be kept in sync with the values defined in the + * kernel's drm_dp_helper.h file. + * This app is based on some prior work submitted in April 2015 by Todd Previte + * () + * + * Authors: + * Todd Previte + * Manasi Navare + * + * Elements of the modeset code adapted from David Herrmann's + * DRM modeset example + * +*/ +#include "igt.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "drm_fourcc.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define I915_DRIVER_NODE "/dev/dri/card0" +#define INTEL_DP_TEST_TYPE_FILE "i915_dp_test_type" +#define INTEL_DP_TEST_ACTIVE_FILE "i915_dp_test_active" +#define INTEL_DP_TEST_DATA_FILE "i915_dp_test_data" + +/* DRM definitions - must be kept in sync with the DRM header */ +#define DP_TEST_LINK_TRAINING (1 << 0) +#define DP_TEST_LINK_VIDEO_PATTERN (1 << 1) +#define DP_TEST_LINK_EDID_READ (1 << 2) +#define DP_TEST_LINK_PHY_TEST_PATTERN (1 << 3) /* DPCD >= 1.1 */ + +#define DP_COMPLIANCE_TEST_TYPE_MASK (DP_TEST_LINK_TRAINING | \ + DP_TEST_LINK_VIDEO_PATTERN | \ + DP_TEST_LINK_EDID_READ | \ + DP_TEST_LINK_PHY_TEST_PATTERN) + +/* NOTE: These must be kept in sync with the definitions in intel_dp.c */ +#define INTEL_DP_EDID_SHIFT_MASK 0 +#define INTEL_DP_EDID_OK (0 << INTEL_DP_EDID_SHIFT_MASK) +#define INTEL_DP_EDID_CORRUPT (1 << INTEL_DP_EDID_SHIFT_MASK) +#define INTEL_DP_RESOLUTION_SHIFT_MASK 0 +#define INTEL_DP_RESOLUTION_PREFERRED (1 << INTEL_DP_RESOLUTION_SHIFT_MASK) +#define INTEL_DP_RESOLUTION_STANDARD (2 << INTEL_DP_RESOLUTION_SHIFT_MASK) +#define INTEL_DP_RESOLUTION_FAILSAFE (3 << INTEL_DP_RESOLUTION_SHIFT_MASK) +#define DP_COMPLIANCE_VIDEO_MODE_MASK (INTEL_DP_RESOLUTION_PREFERRED |\ + INTEL_DP_RESOLUTION_STANDARD |\ + INTEL_DP_RESOLUTION_FAILSAFE) + + +/* Global Variables */ +enum { + INTEL_MODE_INVALID = -1, + INTEL_MODE_NONE = 0, + INTEL_MODE_PREFERRED, + INTEL_MODE_STANDARD, + INTEL_MODE_FAILSAFE, + INTEL_MODE_VIDEO_PATTERN_TEST +} intel_display_mode; + +struct test_video_pattern { + uint16_t hdisplay; + uint16_t vdisplay; + uint8_t bitdepth; + uint32_t fb; + uint32_t size; + struct igt_fb fb_pattern; + drmModeModeInfo mode; + uint32_t *pixmap; +}; + + +struct dp_connector { + drmModeModeInfo mode_standard, mode_preferred, mode_failsafe; + drmModeEncoder *encoder; + /* Standard and preferred frame buffer*/ + uint32_t fb, fb_width, fb_height, fb_size; + uint8_t *pixmap; + struct igt_fb fb_video_pattern; + /* Failsafe framebuffer - note this is a 16-bit buffer */ + uint32_t failsafe_fb, failsafe_width, failsafe_height; + uint32_t failsafe_size; + uint8_t *failsafe_pixmap; + struct igt_fb fb_failsafe_pattern; + struct test_video_pattern test_pattern; + uint32_t conn; + uint32_t crtc; + struct dp_connector *next; +}; + +/* CRTC to restore the default mode */ +drmModeCrtc *default_crtc; +/* HEAD of the DP Connectors Linked List */ +struct dp_connector *dp_connector_list; +/* Number of DP connector nodes in linked list */ +unsigned short dp_connector_count; +struct termios orig_termios; +/* Global file pointers for the sysfs files */ +FILE *test_active_fp, *test_data_fp, *test_type_fp; +/* Flag needed to mmap for Video pattern test */ +bool video_pattern_flag = false; + +/*Input handling*/ +static void reset_terminal_mode(void); +static void set_conio_terminal_mode(void); +static int kbhit(void); + +/* Test Execution function declearations */ +static void setup_debugfs_files(void); +static int setup_crtc_for_connector(int fd, drmModeRes *mr, drmModeConnector *c, + struct dp_connector *dp_conn); +static int setup_framebuffers(int drv_fd, struct dp_connector *dp_conn); +static int setup_failsafe_framebuffer(int drv_fd, struct dp_connector *dp_conn); +static int setup_video_pattern_framebuffer(int drv_fd, + struct dp_connector *dp_conn); +static int fill_framebuffer(int drv_fd, struct dp_connector *dp_conn); +static int setup_dp_connector(int drv_fd, drmModeRes *mr, drmModeConnector *c, + struct dp_connector *dp_conn); +static int setup_connectors(int drv_fd, drmModeResPtr pmr); +static int setup_drm(int *drv_fd); +static void shutdown(int drv_fd); +static void cleanup_debugfs(void); +static int set_video_mode(int drv_fd, int mode, + struct dp_connector *test_connector); +static bool check_test_active(void); +static void clear_test_active(void); +static unsigned long get_test_edid_data(void); +static unsigned long get_test_type(void); +static void get_test_videoPattern_data(struct dp_connector *connector); +static int save_default_mode(int drv_fd); +static int restore_default_mode(int drv_fd); +static int process_test_request(int drv_fd, int test_type, + drmModeResPtr pmr); + + +static void reset_terminal_mode(void) +{ + tcsetattr(0, TCSANOW, &orig_termios); +} + +static void set_conio_terminal_mode(void) +{ + struct termios new_termios; + /* take two copies - one for now or one for later*/ + tcgetattr(0, &orig_termios); + memcpy(&new_termios, &orig_termios, sizeof(new_termios)); + + /* register cleanup handler, and set the new terminal mode */ + atexit(reset_terminal_mode); + cfmakeraw(&new_termios); + tcsetattr(0, TCSANOW, &new_termios); +} + +static int kbhit(void) +{ + struct timeval tv = { 0L, 0L }; + fd_set fds; + + FD_ZERO(&fds); + FD_SET(0, &fds); + + return select(1, &fds, NULL, NULL, &tv); +} + + +static int save_default_mode(int drv_fd) +{ + drmModeConnector *c; + int i, j; + drmModeRes *mode_resources = drmModeGetResources(drv_fd); + drmModeEncoder *drm_encoder = NULL; + int32_t crtc; + + if (!mode_resources) { + igt_warn("drmModeGetResources failed: %s\n", strerror(errno)); + return -1; + } + + for (i = 0; i < mode_resources->count_connectors; i++) { + + c = drmModeGetConnector(drv_fd, mode_resources->connectors[i]); + if (!c) { + printf("Failed to retrieve connector %u:%u\r\n", + i, mode_resources->connectors[i]); + continue; + } + + if (c->connector_type != DRM_MODE_CONNECTOR_DisplayPort) + continue; + printf("Supported modes for Connector %d are: \r\n", + c->connector_id); + printf("name refresh (Hz) hdisp hss hse htot vdisp" + "vss vse vtot flags type clock\r\n"); + for (j = 0; j < c->count_modes; j++) { + printf("[%d] ", j); + printf("%s %d %d %d %d %d %d %d %d %d 0x%x 0x%x %d\r\n", + c->modes[j].name, c->modes[j].vrefresh, + c->modes[j].hdisplay, c->modes[j].hsync_start, + c->modes[j].hsync_end, c->modes[j].htotal, + c->modes[j].vdisplay, c->modes[j].vsync_start, + c->modes[j].vsync_end, c->modes[j].vtotal, + c->modes[j].flags, c->modes[j].type, + c->modes[j].clock); + } + /* Use attached encoder if possible */ + if (c->encoder_id) + drm_encoder = drmModeGetEncoder(drv_fd, c->encoder_id); + if (drm_encoder) { + if (drm_encoder->crtc_id) { + crtc = drm_encoder->crtc_id; + } + } + default_crtc = drmModeGetCrtc(drv_fd, crtc); + drmModeFreeEncoder(drm_encoder); + drmModeFreeConnector(c); + + } + drmModeFreeResources(mode_resources); + printf("Default Mode is:\r\n"); + printf("%s %d %d %d %d %d %d %d %d %d 0x%x 0x%x %d\r\n", + default_crtc->mode.name, default_crtc->mode.vrefresh, + default_crtc->mode.hdisplay, default_crtc->mode.hsync_start, + default_crtc->mode.hsync_end, default_crtc->mode.htotal, + default_crtc->mode.vdisplay, default_crtc->mode.vsync_start, + default_crtc->mode.vsync_end, default_crtc->mode.vtotal, + default_crtc->mode.flags, default_crtc->mode.type, + default_crtc->mode.clock); + + return 0; +} + +static int restore_default_mode(int drv_fd) +{ + drmModeConnector *c; + int i, j; + drmModeRes *mode_resources = drmModeGetResources(drv_fd); + drmModeEncoder *drm_encoder = NULL; + int32_t crtc; + + if (!mode_resources) { + igt_warn("drmModeGetResources failed: %s\n", strerror(errno)); + return -1; + } + + for (i = 0; i < mode_resources->count_connectors; i++) { + + c = drmModeGetConnector(drv_fd, mode_resources->connectors[i]); + if (!c) { + printf("Failed to retrieve connector %u:%u\r\n", + i, mode_resources->connectors[i]); + continue; + } + + if (c->connector_type != DRM_MODE_CONNECTOR_DisplayPort) + continue; + printf("Supported modes for Connector %d are: \r\n", + c->connector_id); + printf("name refresh (Hz) hdisp hss hse htot vdisp " + "vss vse vtot flags type clock\r\n"); + for (j = 0; j < c->count_modes; j++) { + printf("[%d] ", j); + printf("%s %d %d %d %d %d %d %d %d %d 0x%x 0x%x %d\r\n", + c->modes[j].name, c->modes[j].vrefresh, + c->modes[j].hdisplay, c->modes[j].hsync_start, + c->modes[j].hsync_end, c->modes[j].htotal, + c->modes[j].vdisplay, c->modes[j].vsync_start, + c->modes[j].vsync_end, c->modes[j].vtotal, + c->modes[j].flags, c->modes[j].type, + c->modes[j].clock); + } + /* Use attached encoder if possible */ + if (c->encoder_id) + drm_encoder = drmModeGetEncoder(drv_fd, c->encoder_id); + if (drm_encoder) { + if (drm_encoder->crtc_id) { + crtc = drm_encoder->crtc_id; + } + } + drmModeSetCrtc(drv_fd, default_crtc->crtc_id, + default_crtc->buffer_id, + default_crtc->x, + default_crtc->y, &c->connector_id, 1, + &default_crtc->mode); + drmModeFreeEncoder(drm_encoder); + drmModeFreeConnector(c); + + } + drmModeFreeResources(mode_resources); + printf("Default Mode is: \r\n"); + printf("%s %d %d %d %d %d %d %d %d %d 0x%x 0x%x %d\r\n", + default_crtc->mode.name, default_crtc->mode.vrefresh, + default_crtc->mode.hdisplay, default_crtc->mode.hsync_start, + default_crtc->mode.hsync_end, default_crtc->mode.htotal, + default_crtc->mode.vdisplay, default_crtc->mode.vsync_start, + default_crtc->mode.vsync_end, default_crtc->mode.vtotal, + default_crtc->mode.flags, default_crtc->mode.type, + default_crtc->mode.clock); + + return 0; +} + +static unsigned long get_test_edid_data(void) +{ + unsigned long test_data; + + if (!test_data_fp) + fprintf(stderr, "Invalid Test data File Pointer\r\n"); + + rewind(test_data_fp); + fscanf(test_data_fp, "%lx", &test_data); + if (test_data <= 0) { + printf("Test data read failed - %lx\r\n", test_data); + return 0; + } + + return test_data; +} + +static void get_test_videoPattern_data(struct dp_connector *connector) +{ + int count = 0; + uint16_t video_pattern_value[3]; + char video_pattern_attribute[15]; + + if (!test_data_fp) + fprintf(stderr, "Invalid Test data File pointer\r\n"); + + rewind(test_data_fp); + while (!feof(test_data_fp) && count < 3) + fscanf(test_data_fp, "%s %u\n", video_pattern_attribute, + &video_pattern_value[count++]); + + connector->test_pattern.hdisplay = video_pattern_value[0]; + connector->test_pattern.vdisplay = video_pattern_value[1]; + connector->test_pattern.bitdepth = video_pattern_value[2]; + printf("Hdisplay = %u\r\n", connector->test_pattern.hdisplay); + printf("Vdisplay = %u\r\n", connector->test_pattern.vdisplay); + printf("BitDepth = %u\r\n", connector->test_pattern.bitdepth); + +} + +static unsigned long get_test_type(void) +{ + unsigned long test_type; + + if (!test_type_fp) + fprintf(stderr, "Invalid Test Type File pointr\r\n"); + rewind(test_type_fp); + fscanf(test_type_fp, "%02lx", &test_type); + if (test_type <= 0) { + printf("Test type read failed - %02lx\r\n", test_type); + return 0; + } + + return test_type; +} + +static int process_test_request(int drv_fd, int test_type, + drmModeResPtr pmr) +{ + int status = -1; + int mode; + unsigned long test_data_edid; + struct dp_connector *connector; + + /* Disable the main link before starting the test + * Note that this should probably be signaled through a debugfs file + * from the kernel at the start of testing. + */ + switch (test_type) { + case DP_TEST_LINK_VIDEO_PATTERN: + video_pattern_flag = true; + setup_connectors(drv_fd, pmr); + connector = &dp_connector_list[0]; + get_test_videoPattern_data(connector); + fill_framebuffer(drv_fd, connector); + mode = INTEL_MODE_VIDEO_PATTERN_TEST; + set_video_mode(drv_fd, INTEL_MODE_NONE, connector); + status = set_video_mode(drv_fd, mode, connector); + break; + case DP_TEST_LINK_EDID_READ: + setup_connectors(drv_fd, pmr); + connector = &dp_connector_list[0]; + test_data_edid = get_test_edid_data(); + mode = (test_data_edid & DP_COMPLIANCE_VIDEO_MODE_MASK) >> + INTEL_DP_RESOLUTION_SHIFT_MASK; + set_video_mode(drv_fd, INTEL_MODE_NONE, connector); + status = set_video_mode(drv_fd, mode, connector); + break; + case DP_TEST_LINK_PHY_TEST_PATTERN: + /* PHY Test Pattern = future implementation */ + break; + default: + /* Unknown test type */ + fprintf(stderr, "Invalid test request. Ignored.\r\n"); + break; + } + clear_test_active(); + + /* Return 0 on success and -1 on failure */ + return status; +} + +static void cleanup_debugfs(void) +{ + fclose(test_active_fp); + fclose(test_data_fp); + fclose(test_type_fp); +} + +static bool check_test_active(void) +{ + unsigned long test_active; + + if (!test_type_fp) + fprintf(stderr, "Invalid test type file pointer\r\n"); + rewind(test_active_fp); + fscanf(test_active_fp, "%lx", &test_active); + if (test_active == 1) + return 1; + + return 0; +} + +static void clear_test_active(void) +{ + rewind(test_active_fp); + fprintf(test_active_fp, "%d", 0); +} + +static void setup_debugfs_files(void) +{ + test_type_fp = igt_debugfs_fopen(INTEL_DP_TEST_TYPE_FILE, "r"); + igt_require(test_type_fp); + + test_data_fp = igt_debugfs_fopen(INTEL_DP_TEST_DATA_FILE, "r"); + igt_require(test_data_fp); + + test_active_fp = igt_debugfs_fopen(INTEL_DP_TEST_ACTIVE_FILE, "w+"); + igt_require(test_active_fp); + + /* Reset the active flag for safety */ + clear_test_active(); +} + + +static int setup_drm(int *drv_fd) +{ + int drm_fd; + + if (!drmAvailable()) { + printf("Error: DRM not available\r\n"); + return -EOPNOTSUPP; + } + + drm_fd = drm_open_driver(DRIVER_INTEL); + if (drm_fd == -1) { + fprintf(stderr, "Cannot Open DRM Device\r\n"); + return -1; + } + *drv_fd = drm_fd; + + return 0; +} + + +static int setup_connectors(int drv_fd, drmModeResPtr pmr) +{ + int ret, i; + drmModeConnector *c; + struct dp_connector *dp_conn; + + pmr = drmModeGetResources(drv_fd); + if (!pmr) { + printf("ERROR: Failed to retrieve DRM resources\r\n"); + return -1; + } + + for (i = 0; i < pmr->count_connectors; i++) { + + c = drmModeGetConnector(drv_fd, pmr->connectors[i]); + if (!c) { + printf("Failed to retrieve connector %u:%u\r\n", + i, pmr->connectors[i]); + continue; + } + + if (c->connector_type != DRM_MODE_CONNECTOR_DisplayPort) + continue; + + dp_conn = malloc(sizeof(*dp_conn)); + memset(dp_conn, 0, sizeof(*dp_conn)); + dp_conn->conn = c->connector_id; + dp_conn->next = dp_connector_list; + dp_connector_list = dp_conn; + + /* Setup the DP connector*/ + ret = setup_dp_connector(drv_fd, pmr, c, dp_conn); + if (ret == -1) { + printf("Failed to setup DP connector %u:%u\r\n", + i, + pmr->connectors[i]); + free(dp_conn); + drmModeFreeConnector(c); + continue; + } else + dp_connector_count++; + + /* free connector data and link device into global list */ + drmModeFreeConnector(c); + } + drmModeFreeResources(pmr); + + return 0; +} + +static int setup_dp_connector(int drv_fd, drmModeRes *mr, drmModeConnector *c, + struct dp_connector *dp_conn) +{ + int ret, i; + bool found_std = false, found_fs = false; + + /* Ignore any disconnected devices */ + if (c->connection != DRM_MODE_CONNECTED) { + printf("Connector %u disconnected\r\n", c->connector_id); + return -ENOENT; + } + printf("Connector Setup:\r\n"); + /* Setup preferred mode - should be mode[0] in the list */ + dp_conn->mode_preferred = c->modes[0]; + dp_conn->fb_width = c->modes[0].hdisplay; + dp_conn->fb_height = c->modes[0].vdisplay; + + dp_conn->test_pattern.mode = c->modes[0]; + dp_conn->test_pattern.mode.hdisplay = c->modes[0].hdisplay; + dp_conn->test_pattern.mode.vdisplay = c->modes[0].vdisplay; + + printf("Preferred mode (mode 0) for connector %u is %ux%u\r\n", + c->connector_id, c->modes[0].hdisplay, c->modes[0].vdisplay); + fflush(stdin); + + for (i = 1; i < c->count_modes; i++) { + /* Standard mode is 800x600@60 */ + if (c->modes[i].hdisplay == 800 && + c->modes[i].vdisplay == 600 && + c->modes[i].vrefresh == 60 && + found_std == false) { + dp_conn->mode_standard = c->modes[i]; + printf("Standard mode (%d) for connector %u is %ux%u\r\n", + i, + c->connector_id, + c->modes[i].hdisplay, + c->modes[i].vdisplay); + found_std = true; + } + /* Failsafe mode is 640x480@60 */ + if (c->modes[i].hdisplay == 640 && + c->modes[i].vdisplay == 480 && + c->modes[i].vrefresh == 60 && + found_fs == false) { + dp_conn->mode_failsafe = c->modes[i]; + dp_conn->failsafe_width = c->modes[i].hdisplay; + dp_conn->failsafe_height = c->modes[i].vdisplay; + printf("Failsafe mode (%d) for connector %u is %ux%u\r\n", + i, + c->connector_id, + c->modes[i].hdisplay, + c->modes[i].vdisplay); + found_fs = true; + } + } + + ret = setup_crtc_for_connector(drv_fd, mr, c, dp_conn); + if (ret) { + printf("Set CRTC for connector %u failed (%d)\r\n", + c->connector_id, ret); + return ret; + } + + ret = setup_framebuffers(drv_fd, dp_conn); + if (ret) { + printf("Create framebuffer for connector %u failed (%d)\r\n", + c->connector_id, ret); + return ret; + } + + if (found_fs) { + ret = setup_failsafe_framebuffer(drv_fd, dp_conn); + if (ret) { + printf("Create failsafe framebuffer for connector %u" + "failed (%d)\r\n", + c->connector_id, ret); + return ret; + } + } + + if (video_pattern_flag) { + ret = setup_video_pattern_framebuffer(drv_fd, dp_conn); + if (ret) { + printf("Create framebuffer for connector %u failed (%d)\r\n", + c->connector_id, ret); + return ret; + } + } + + return ret; +} + +static int setup_crtc_for_connector(int fd, drmModeRes *mr, drmModeConnector *c, + struct dp_connector *dp_conn) +{ + drmModeEncoder *drm_encoder = NULL; + struct dp_connector *dpc; + int i, j; + int32_t crtc; + + /* Use attached encoder if possible */ + if (c->encoder_id) + drm_encoder = drmModeGetEncoder(fd, c->encoder_id); + + if (drm_encoder && drm_encoder->crtc_id) { + + crtc = drm_encoder->crtc_id; + printf("Encoder CRTC = %d\r\n", crtc); + for (dpc = dp_connector_list; dpc; dpc = dpc->next) { + if (dpc->crtc == crtc) { + crtc = -1; + break; + } + } + if (crtc >= 0) { + drmModeFreeEncoder(drm_encoder); + dp_conn->crtc = crtc; + return 0; + } + drmModeFreeEncoder(drm_encoder); + } + + /* Check all encoder/crtc combinations */ + for (i = 0; i < c->count_encoders; i++) { + drm_encoder = drmModeGetEncoder(fd, c->encoders[i]); + if (!drm_encoder) + continue; + for (j = 0; j < mr->count_crtcs; j++) { + if (!(drm_encoder->possible_crtcs & (1 << j))) + continue; + crtc = mr->crtcs[j]; + for (dpc = dp_connector_list; dpc; dpc = dpc->next) { + if (dpc->crtc == crtc) { + crtc = -1; + break; + } + } + if (crtc >= 0) { + drmModeFreeEncoder(drm_encoder); + dp_conn->crtc = crtc; + return 0; + } + } + drmModeFreeEncoder(drm_encoder); + } + printf("No CRTC available for connector %u\r\n", c->connector_id); + return -1; +} + +static int setup_framebuffers(int drv_fd, struct dp_connector *dp_conn) +{ + + dp_conn->fb = igt_create_fb(drv_fd, + dp_conn->fb_width, dp_conn->fb_height, + DRM_FORMAT_XRGB8888, + LOCAL_DRM_FORMAT_MOD_NONE, + &dp_conn->fb_video_pattern); + igt_assert(dp_conn->fb); + + /* Map the mapping of GEM object into the virtual address space */ + dp_conn->pixmap = gem_mmap__gtt(drv_fd, + dp_conn->fb_video_pattern.gem_handle, + dp_conn->fb_video_pattern.size, + PROT_READ | PROT_WRITE); + gem_set_domain(drv_fd, dp_conn->fb_video_pattern.gem_handle, + I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT); + dp_conn->fb_size = dp_conn->fb_video_pattern.size; + + /* After filling the device memory with 0s it needs to be unmapped */ + memset(dp_conn->pixmap, 0, dp_conn->fb_size); + munmap(dp_conn->pixmap, dp_conn->fb_size); + + return 0; +} + +static int setup_failsafe_framebuffer(int drv_fd, struct dp_connector *dp_conn) +{ + + dp_conn->failsafe_fb = igt_create_fb(drv_fd, + dp_conn->failsafe_width, + dp_conn->failsafe_height, + DRM_FORMAT_XRGB8888, + LOCAL_DRM_FORMAT_MOD_NONE, + &dp_conn->fb_failsafe_pattern); + igt_assert(dp_conn->failsafe_fb); + + /* Map the mapping of GEM object into the virtual address space */ + dp_conn->failsafe_pixmap = gem_mmap__gtt(drv_fd, + dp_conn->fb_failsafe_pattern.gem_handle, + dp_conn->fb_failsafe_pattern.size, + PROT_READ | PROT_WRITE); + gem_set_domain(drv_fd, dp_conn->fb_failsafe_pattern.gem_handle, + I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT); + dp_conn->failsafe_size = dp_conn->fb_failsafe_pattern.size; + + /* After filling the device framebuffer the mapped memory needs to be freed */ + memset(dp_conn->failsafe_pixmap, 0, dp_conn->failsafe_size); + munmap(dp_conn->failsafe_pixmap, dp_conn->failsafe_size); + + return 0; + +} + + +static int setup_video_pattern_framebuffer(int drv_fd, struct dp_connector *dp_conn) +{ + uint32_t width, height; + + width = dp_conn->test_pattern.mode.hdisplay; + height = dp_conn->test_pattern.mode.vdisplay; + dp_conn->test_pattern.fb = igt_create_fb(drv_fd, + width, height, + DRM_FORMAT_XRGB8888, + LOCAL_DRM_FORMAT_MOD_NONE, + &dp_conn->test_pattern.fb_pattern); + igt_assert(dp_conn->test_pattern.fb); + + /* Map the mapping of GEM object into the virtual address space */ + dp_conn->test_pattern.pixmap = gem_mmap__gtt(drv_fd, + dp_conn->test_pattern.fb_pattern.gem_handle, + dp_conn->test_pattern.fb_pattern.size, + PROT_READ | PROT_WRITE); + gem_set_domain(drv_fd, dp_conn->test_pattern.fb_pattern.gem_handle, + I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT); + + dp_conn->test_pattern.size = dp_conn->test_pattern.fb_pattern.size; + + memset(dp_conn->test_pattern.pixmap, 0, dp_conn->test_pattern.size); + return 0; + +} + + +static int fill_framebuffer(int drv_fd, struct dp_connector *dp_conn) +{ + int ret; + uint32_t tile_height, tile_width, width, height; + uint32_t *red_ptr, *green_ptr, *blue_ptr, *white_ptr, *src_ptr, *dst_ptr; + int x, y; + int32_t pixel_val; + + width = dp_conn->test_pattern.mode.hdisplay; + height = dp_conn->test_pattern.mode.vdisplay; + + tile_height = 64; + tile_width = pow(2, dp_conn->test_pattern.bitdepth); + + red_ptr = dp_conn->test_pattern.pixmap; + green_ptr = red_ptr + (width * tile_height); + blue_ptr = green_ptr + (width * tile_height); + white_ptr = blue_ptr + (width * tile_height); + x = 0; + + /* Fill the frame buffer with video pattern from CTS 3.1.5 */ + while (x < width) { + for (pixel_val = 0; pixel_val < 256; + pixel_val = pixel_val + (256 / tile_width)) { + red_ptr[x] = pixel_val << 16; + green_ptr[x] = pixel_val << 8; + blue_ptr[x] = pixel_val << 0; + white_ptr[x] = red_ptr[x] | green_ptr[x] | blue_ptr[x]; + if (++x >= width) + break; + } + } + for (y = 0; y < height; y++) { + if (y == 0 || y == 64 || y == 128 || y == 192) + continue; + switch ((y / tile_height) % 4) { + case 0: + src_ptr = red_ptr; + break; + case 1: + src_ptr = green_ptr; + break; + case 2: + src_ptr = blue_ptr; + break; + case 3: + src_ptr = white_ptr; + break; + } + dst_ptr = dp_conn->test_pattern.pixmap + (y * width); + memcpy(dst_ptr, src_ptr, (width * 4)); + } + munmap(dp_conn->test_pattern.pixmap, + dp_conn->test_pattern.size); + return 0; +} + + +static int set_video_mode(int drv_fd, int mode, struct dp_connector *test_connector) +{ + drmModeModeInfo *requested_mode; + uint32_t required_fb; + int ret = 0; + + switch (mode) { + case INTEL_MODE_NONE: + printf("NONE\r\n"); + ret = drmModeSetCrtc(drv_fd, test_connector->crtc, + -1, 0, 0, NULL, 0, NULL); + goto out; + case INTEL_MODE_PREFERRED: + printf("PREFERRED\r\n"); + requested_mode = &test_connector->mode_preferred; + required_fb = test_connector->fb; + break; + case INTEL_MODE_STANDARD: + printf("STANDARD\r\n"); + requested_mode = &test_connector->mode_standard; + required_fb = test_connector->fb; + break; + case INTEL_MODE_FAILSAFE: + printf("FAILSAFE\r\n"); + requested_mode = &test_connector->mode_failsafe; + required_fb = test_connector->failsafe_fb; + break; + case INTEL_MODE_VIDEO_PATTERN_TEST: + printf("VIDEO PATTERN TEST\r\n"); + requested_mode = &test_connector->test_pattern.mode; + required_fb = test_connector->test_pattern.fb; + break; + case INTEL_MODE_INVALID: + default: + printf("INVALID! (%08x) Mode set aborted!\r\n", mode); + return -1; + } + + ret = drmModeSetCrtc(drv_fd, test_connector->crtc, required_fb, 0, 0, + &test_connector->conn, 1, requested_mode); + +out: + if (ret) { + printf("Failed to set CRTC for connector %u\r\n", + test_connector->conn); + } + + return ret; +} + +static void shutdown(int drv_fd) +{ + int i; + struct dp_connector *index = dp_connector_list, *prev; + + for (i = 0; i < dp_connector_count; i++) { + + kmstest_dump_mode(&default_crtc->mode); + + prev = index; + index = index->next; + free(prev); + prev = NULL; + } + dp_connector_list = NULL; + dp_connector_count = 0; + +} + +int main(int argc, char *argv[]) +{ + int status = 0; + int drv_fd = 0; + drmModeResPtr mode_resources = NULL; + int done = 0; + char input; + unsigned long test_type; + + dp_connector_list = NULL; + dp_connector_count = 0; + + /* Setup input for keyboard handling - from input.c */ + set_conio_terminal_mode(); + + printf("ATTENTION:\r\n" + " Ensure that no display manager is running\r\n" + " App must be run as root in console/text mode\r\n" + " Continue? (Y/N)\r\n" + ); + input = getchar(); + + if (input != 'Y' && input != 'y') + goto exit; + + /* Set up DRM device before starting any tests */ + status = setup_drm(&drv_fd); + if (status == -1) + goto exit; + /* Save the deafult mode that needs to be restored after every test */ + save_default_mode(drv_fd); + /* Close the DRM device so it does not interfere with Link training */ + status = close(drv_fd); + if (status) + printf("Error: Failed to close i915 driver\r\n"); + setup_debugfs_files(); + + printf("Waiting for test requests. 'X' or 'Q' exits\r\n"); + + while (!done) { + if (kbhit()) { + input = getchar(); + switch (input) { + case 'q': + case 'Q': + case 'x': + case 'X': + done = 1; + break; + } + } + + usleep(1000); + + if (check_test_active()) { + + /* Open the DRM device and restore default mode before + * each test + */ + status = setup_drm(&drv_fd); + if (status == -1) + goto exit; + restore_default_mode(drv_fd); + + test_type = get_test_type(); + if (/*test_type == DP_TEST_LINK_VIDEO_PATTERN || */ + test_type == DP_TEST_LINK_EDID_READ) { + process_test_request(drv_fd, test_type, + mode_resources); + shutdown(drv_fd); + drmModeFreeResources(mode_resources); + mode_resources = NULL; + video_pattern_flag = false; + } else { + printf("Test type received is not EDID or" + "Video Pattern\r\n"); + clear_test_active(); + } + /* Close drm device after each test */ + close(drv_fd); + usleep(10000); + } + + } + +exit: + cleanup_debugfs(); + /* Set Up DRM device and restore default mode before exit */ + setup_drm(&drv_fd); + restore_default_mode(drv_fd); + status = close(drv_fd); + if (status) + printf("Error: Failed to close i915 driver\r\n"); + drmModeFreeCrtc(default_crtc); + + printf("Compliance testing application exiting.\r\n" + "If testing has been performed, you may want to restart\r\n" + "the system to ensure it returns to normal operation.\r\n"); + igt_exit(); + + return status; + +}