From patchwork Fri Apr 10 15:54:19 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Todd Previte X-Patchwork-Id: 6197751 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 AFDB1BF4A6 for ; Fri, 10 Apr 2015 15:54:44 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 165292024C for ; Fri, 10 Apr 2015 15:54:41 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id 3F56C203C4 for ; Fri, 10 Apr 2015 15:54:37 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id CFAC56E121; Fri, 10 Apr 2015 08:54:36 -0700 (PDT) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mail-pd0-f176.google.com (mail-pd0-f176.google.com [209.85.192.176]) by gabe.freedesktop.org (Postfix) with ESMTP id F22EC6E12D for ; Fri, 10 Apr 2015 08:54:35 -0700 (PDT) Received: by pdea3 with SMTP id a3so26524475pde.3 for ; Fri, 10 Apr 2015 08:54:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-type:content-transfer-encoding; bh=ZSwQwaOm+tYTUeSkYjKawRj4d/kWodCqtraZunkxUFM=; b=V57rOB0HAGElCP2m5+BkORL4XrdEOLFbkblqnbm/wi8omxIQHm0K8cgYiLa51EPZOd eAEOioNYOl0BiVHea6Ev+Iukw8OWyTd2WOHGxytkADU6kEZizRfqHsrmWKqrkM1hSNIQ L5+ikFZz/RtHL8RkvHc1+Ywlf1sm3N84Lp6WbCWI0IgqKMcAQB+49brgH4Et66uo1bvL rXhtHrpbbccwiyvyYMBzFGwO+ugZFB8rZr8D1J58CZmgGi8tNwE8N0vlm82LaYA2x+oS 6Pf715VYqVW0UX7M6E/mOUH3jhQclbybu1XS+HVLsT47bfDIdGgMXsUoPEIPbg9yobVy xWNg== X-Received: by 10.68.192.193 with SMTP id hi1mr3740296pbc.142.1428681275639; Fri, 10 Apr 2015 08:54:35 -0700 (PDT) Received: from localhost.localdomain (ip70-162-72-208.ph.ph.cox.net. [70.162.72.208]) by mx.google.com with ESMTPSA id oo3sm2677059pdb.26.2015.04.10.08.54.34 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 10 Apr 2015 08:54:34 -0700 (PDT) From: Todd Previte To: intel-gfx@lists.freedesktop.org Date: Fri, 10 Apr 2015 08:54:19 -0700 Message-Id: <1428681259-21013-2-git-send-email-tprevite@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1428681259-21013-1-git-send-email-tprevite@gmail.com> References: <1428681259-21013-1-git-send-email-tprevite@gmail.com> MIME-Version: 1.0 Subject: [Intel-gfx] [PATCH] igt/dp: Displayport Compliance Testing - Userspace Component 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.1 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_MED, T_DKIM_INVALID, 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 is the userspace component of the Displayport compliance testing software requried for compliance testing of the i915 driver. The README included in the dp_compliance/ directory contains the most up to date information on the use and operation of the user app. Signed-off-by: Todd Previte --- dp_compliance/README | 102 ++++++ dp_compliance/build.sh | 6 + dp_compliance/dp_compliance.c | 782 ++++++++++++++++++++++++++++++++++++++++++ dp_compliance/input.c | 48 +++ 4 files changed, 938 insertions(+) create mode 100644 dp_compliance/README create mode 100755 dp_compliance/build.sh create mode 100644 dp_compliance/dp_compliance.c create mode 100644 dp_compliance/input.c diff --git a/dp_compliance/README b/dp_compliance/README new file mode 100644 index 0000000..1ef4913 --- /dev/null +++ b/dp_compliance/README @@ -0,0 +1,102 @@ + +Displayport Compliance Testing +Userspace Application + +This is the userspace portion of the Displayport Compliance testing suite for the +i915 driver. It must be running in order to successfully complete Displayport +compliance testing. This file contains the latest available information about +the user app and its operation. + +This app and the kernel code that accompanies it has been written to satisfy the +requirements of the Displayport Link CTS Core 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 + - A Displayport compliance testing appliance such as the Unigraf DPR-100, + DPR-120 or Quantum Data 882EDP + - This user application + - A Windows host machine to run the test software + - Root access on the DUT + +Test setup: + It is strongly recommended that the Windows host, test appliance and DUT be freshly + restarted before testing begins. This ensures that any previous configurations and + settings will not interfere with the 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. The easiest way to accomplish this is to place the word “text” + on the command line for booting the kernel. The recommended boot parameters for + the kernel are as follows: + “test debug drm.debug=0x0e” + This enables debug output in the logs and will boot the system to a command prompt. + + You must be logged in as root in order to run the user app. + + Connections (required): + - Test appliance connected to the external Displayport connector on the DUT + - Test appliance connected to the Windows host (usually via USB) + + Connections (optional): + - SSH/telnet connection to the DUT from the Windows host or other system + - A display attached to the DUT for monitoring the console or running + the user app. + + The user app can be run directly from the console on the DUT (if a non-Displayport + display is attached) or from an SSH/telnet session. Generally speaking, it’s best + to run from an SSH session, but testing will operate identically regardless of how + the user app is launched. + + Once the user application is up and running, waiting for a test request, the software + on the Windows host can now be used to execute the compliance tests. + + TL;DR version: + - Install test appliance software on Windows host + - Adjust command line on Linux DUT as necessary to enable debug and boot + to the console + - Connect the test appliance via USB to the Windows host and a Displayport + cable to the DUT + - Reboot all systems, including the test appliance + - SSH into the DUT or login at the command prompt + - Ensure that the booted kernel contains the compliance test patch set + and that a version of IGT with this user app is also present + - Launch the user app, as root. Follow the onscreen instructions until + it says it’s waiting for a test to begin + - Execute the tests via the software on the Windows host per the + documentation for the test appliance + +Debugfs Files: + +The file root for all the debugfs files is: + +/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. + +i915_dp_test_data + Test data is used by the kernel to pass parameters to the user app. Currently, the + only parameter that is delivered is the video mode to set for the test. As more tests + are implemented, this value may be used for additional values or information. + +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 and must be kept in sync with the values defined in + drm_dp_helper.h. + +Test operations: + +Notes: + If the test_active flag is stuck, it can be cleared with this simple command: + echo 0 > /sys/kernel/debug/dri/0/i915_dp_test_active + This can happen when the test appliance is connected and a test is run, but the + user app isn’t running. + diff --git a/dp_compliance/build.sh b/dp_compliance/build.sh new file mode 100755 index 0000000..0b317a9 --- /dev/null +++ b/dp_compliance/build.sh @@ -0,0 +1,6 @@ +#!/bin/sh +# Simple build script for the compliance app +# Eventually should be a makefile in IGT + +gcc -g3 dp_compliance.c input.c -o dp_compliance_app -Wall `pkg-config --libs --cflags libdrm` + diff --git a/dp_compliance/dp_compliance.c b/dp_compliance/dp_compliance.c new file mode 100644 index 0000000..7d5febb --- /dev/null +++ b/dp_compliance/dp_compliance.c @@ -0,0 +1,782 @@ +/* + * Displayport Compliance Testing Application + * Performs Displayport compliance testing for the Intel i915 driver + * + * 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. + * + * Authors: + * Todd Previte + * + * Elements of the modeset code adapted from David Herrmann's + * DRM modeset example + * +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define I915_DRIVER_NODE "/dev/dri/card0" +#define INTEL_DP_DEBUGFS_ROOT "/sys/kernel/debug/dri/0/" + +#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 4 +#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) + +enum +{ + INTEL_MODE_INVALID = -1, + INTEL_MODE_NONE = 0, + INTEL_MODE_PREFERRED, + INTEL_MODE_STANDARD, + INTEL_MODE_FAILSAFE +} intel_display_mode; + +struct dp_connector { + drmModeModeInfo mode_standard, mode_preferred, mode_failsafe; + /* Standard and preferred frame buffer*/ + uint32_t fb, fb_width, fb_height, fb_handle, fb_stride, fb_size; + uint8_t *pixmap; + /* Failsafe framebuffer - note this is a 16-bit buffer */ + uint32_t failsafe_fb, failsafe_width, failsafe_height; + uint32_t failsafe_handle, failsafe_stride, failsafe_size; + uint8_t *failsafe_pixmap; + + uint32_t conn; + uint32_t crtc; + + drmModeCrtc *saved_crtc; + struct dp_connector *next; +}; + +/* Input handling from input.c */ +extern void reset_terminal_mode(void); +extern void set_conio_terminal_mode(void); +extern int kbhit(void); +extern int getch(void); + +void setup_debugfs_files(int *test_active_fd, int *test_type_fd, + int *test_data_fd); + int setup_crtc_for_connector(int fd, drmModeRes *mr, drmModeConnector *c, + struct dp_connector *dp_conn); + int setup_framebuffers(int drv_fd, struct dp_connector *dp_conn); + int setup_failsafe_framebuffer(int drv_fd, struct dp_connector *dp_conn); + int setup_dp_connector(int drv_fd, drmModeRes *mr, drmModeConnector *c, + struct dp_connector *dp_conn); + int setup_connectors(int drv_fd, drmModeResPtr pmr); + int setup_drm(int *drv_fd, const char *node); + +void shutdown(int drv_fd); +void cleanup_debugfs(void); + + int set_video_mode(int drv_fd, int mode, struct dp_connector *test_connector); + int check_test_active(void); +void clear_test_active(void); + int get_test_data(void); + int get_test_type(void); + + int process_test_request(int drv_fd, int test_type, int test_data, + struct dp_connector *connector); + +/* Global connector list */ +struct dp_connector *dp_connector_list; +unsigned short dp_connector_count; + +/* Global file descriptors */ +int test_active_fd, test_data_fd, test_type_fd; + +int get_test_data(void) +{ + char data_in[9]; + int bytes_read; + int data; + + assert(test_data_fd > 0); + lseek(test_data_fd, 0, SEEK_SET); + + bytes_read = read(test_data_fd, data_in, 8); + if (bytes_read <= 0) { + printf("Test type read failed - %d\r\n", bytes_read); + return 0; + } + data_in[8] = '\0'; + data = strtol(data_in, NULL, 16); + printf("Test data = %08x\r\n", data); + return data; +} + +int get_test_type(void) +{ + char data_in[5]; + int bytes_read; + int data; + + assert(test_type_fd > 0); + lseek(test_type_fd, 0, SEEK_SET); + + bytes_read = read(test_type_fd, data_in, 4); + if (bytes_read <= 0) { + printf("Test type read failed - %d\r\n", bytes_read); + return 0; + } + data_in[4] = '\0'; + data = strtol(data_in, NULL, 16); + printf("Test type = %04x\r\n", data); + return data; +} + +int process_test_request(int drv_fd, int test_type, int test_data, + struct dp_connector *connector) +{ + int status = 0; + int mode; + + /* 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. + */ + set_video_mode(drv_fd, INTEL_MODE_NONE, connector); + + switch (test_type) { + case DP_TEST_LINK_TRAINING: + /* Link training = future implementation */ + break; + case DP_TEST_LINK_VIDEO_PATTERN: + /* Video pattern = future implementation */ + break; + case DP_TEST_LINK_EDID_READ: + mode = (test_data & DP_COMPLIANCE_VIDEO_MODE_MASK) >> + INTEL_DP_RESOLUTION_SHIFT_MASK; + printf("EDID test received, mode %d\r\n", mode); + status = set_video_mode(drv_fd, mode, connector); + clear_test_active(); + break; + case DP_TEST_LINK_PHY_TEST_PATTERN: + /* PHY Test Pattern = future implementation */ + break; + default: + /* Unknown test type */ + printf("Invalid test request. Ignored.\r\n"); + clear_test_active(); + break; + } + + return status; +} + +void cleanup_debugfs(void) +{ + close(test_active_fd); + close(test_data_fd); + close(test_type_fd); +} + +int check_test_active(void) +{ + char data_in[2]; + int bytes_read; + + assert(test_active_fd > 0); + lseek(test_active_fd, 0, SEEK_SET); + + bytes_read = read(test_active_fd, data_in, 2); + if (bytes_read <= 0) + return 0; + + if (data_in[0] == '1') + return 1; + + return 0; +} + +void clear_test_active(void) +{ + int bytes_written; + char data_out[2]; + + assert(test_active_fd > 0); + lseek(test_active_fd, 0, SEEK_SET); + + data_out[0] = '0'; + data_out[1] = '\0'; + + bytes_written = write(test_active_fd, data_out, 1); + if (bytes_written <= 0) + printf("Could not clear test active flag. Write failed!\r\n"); +} + +void setup_debugfs_files(int *test_active_fd, int *test_type_fd, + int *test_data_fd) +{ + char debugfs_path[256]; + char *index; + int offset; + + offset = strlen(INTEL_DP_DEBUGFS_ROOT); + index = &debugfs_path[offset]; + + memset(debugfs_path, 0, 256); + strcpy(debugfs_path, INTEL_DP_DEBUGFS_ROOT); + + strcpy(index, INTEL_DP_TEST_TYPE_FILE); + *test_type_fd = open(debugfs_path, O_RDONLY); + assert(*test_type_fd > 0); + printf("Test type path : %s\r\n", debugfs_path); + + strcpy(index, INTEL_DP_TEST_DATA_FILE); + *test_data_fd = open(debugfs_path, O_RDONLY); + assert(*test_data_fd > 0); + printf("Test data path : %s\r\n", debugfs_path); + + strcpy(index, INTEL_DP_TEST_ACTIVE_FILE); + *test_active_fd = open(debugfs_path, O_RDWR); + assert(*test_active_fd > 0); + printf("Test active path : %s\r\n", debugfs_path); + + /* Reset the active flag for safety */ + clear_test_active(); +} + +int setup_drm(int *drv_fd, const char *node) +{ + int fd, ret; + uint64_t use_dumb_buffers; + + if (!drmAvailable()) { + printf("Error: DRM not available\r\n"); + return -EOPNOTSUPP; + } + + fd = drmOpen("i915", NULL); + + if (fd < 0) { + fd = open(node, O_RDWR | O_CLOEXEC); + if (fd < 0) { + ret = -errno; + printf("Error: could not open '%s'\r\n", node); + return ret; + } + } + + drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &use_dumb_buffers); + + if (!use_dumb_buffers) { + printf("'%s' does not support dumb buffers\r\n", node); + close(fd); + return -EOPNOTSUPP; + } + *drv_fd = fd; + + return 0; +} + +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; + + /* Setup the DP connector*/ + ret = setup_dp_connector(drv_fd, pmr, c, dp_conn); + if (ret) { + if (ret != -ENOENT) { + errno = -ret; + 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); + dp_conn->next = dp_connector_list; + dp_connector_list = dp_conn; + } + + return 0; +} + +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; + printf("\tPreferred mode (mode 0) for connector %u is %ux%u\r\n", + c->connector_id, c->modes[0].hdisplay, c->modes[0].vdisplay); + + 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("\tStandard 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("\tFailsafe mode (%d) for connector %u is %ux%u\r\n", + i, + c->connector_id, + c->modes[i].hdisplay, + c->modes[i].vdisplay); + } + } + + 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; + } + + 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; + } + + return ret; +} + +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) { + if (drm_encoder->crtc_id) { + crtc = drm_encoder->crtc_id; + 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 -ENOENT; +} + +int setup_framebuffers(int drv_fd, struct dp_connector *dp_conn) +{ + struct drm_mode_create_dumb creq; + struct drm_mode_destroy_dumb dreq; + struct drm_mode_map_dumb mreq; + int ret; + + memset(&creq, 0, sizeof(creq)); + memset(&mreq, 0, sizeof(mreq)); + + creq.width = dp_conn->fb_width; + creq.height = dp_conn->fb_height; + creq.bpp = 32; + ret = drmIoctl(drv_fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq); + if (ret < 0) { + printf("Create dumb buffer failed\r\n"); + return -errno; + } + dp_conn->fb_stride = creq.pitch; + dp_conn->fb_size = creq.size; + dp_conn->fb_handle = creq.handle; + + ret = drmModeAddFB(drv_fd, dp_conn->fb_width, dp_conn->fb_height, + 24, 32, dp_conn->fb_stride, dp_conn->fb_handle, + &dp_conn->fb); + if (ret) { + printf("Create framebuffer failed\r\n"); + ret = -errno; + goto cleanup; + } + + mreq.handle = dp_conn->fb_handle; + ret = drmIoctl(drv_fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq); + if (ret) { + printf("Map dumb buffer failed\r\n"); + ret = -errno; + goto fb_fail; + } + + dp_conn->pixmap = mmap(0, dp_conn->fb_size, PROT_READ | PROT_WRITE, + MAP_SHARED, drv_fd, mreq.offset); + if (dp_conn->pixmap == MAP_FAILED) { + printf("Mmap failed\r\n"); + ret = -errno; + goto fb_fail; + } + memset(dp_conn->pixmap, 0, dp_conn->fb_size); + return 0; + +fb_fail: + drmModeRmFB(drv_fd, dp_conn->fb); + +cleanup: + memset(&dreq, 0, sizeof(dreq)); + dreq.handle = dp_conn->fb_handle; + drmIoctl(drv_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + return ret; +} + +int setup_failsafe_framebuffer(int drv_fd, struct dp_connector *dp_conn) +{ + struct drm_mode_create_dumb creq; + struct drm_mode_destroy_dumb dreq; + struct drm_mode_map_dumb mreq; + int ret; + + memset(&creq, 0, sizeof(creq)); + creq.width = dp_conn->failsafe_width; + creq.height = dp_conn->failsafe_height; + creq.bpp = 16; + ret = drmIoctl(drv_fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq); + if (ret < 0) { + printf("Create dumb buffer failed\r\n"); + return -errno; + } + + dp_conn->failsafe_stride = creq.pitch; + dp_conn->failsafe_size = creq.size; + dp_conn->failsafe_handle = creq.handle; + + ret = drmModeAddFB(drv_fd, dp_conn->failsafe_width, + dp_conn->failsafe_height, 16, 16, + dp_conn->failsafe_stride, dp_conn->failsafe_handle, + &dp_conn->failsafe_fb); + if (ret) { + printf("Create framebuffer failed\r\n"); + ret = -errno; + goto cleanup; + } + + memset(&mreq, 0, sizeof(mreq)); + mreq.handle = dp_conn->failsafe_handle; + ret = drmIoctl(drv_fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq); + if (ret) { + printf("Map dumb buffer failed\r\n"); + ret = -errno; + goto fb_fail; + } + + dp_conn->failsafe_pixmap = mmap(0, dp_conn->failsafe_size, + PROT_READ | PROT_WRITE, MAP_SHARED, + drv_fd, mreq.offset); + if (dp_conn->failsafe_pixmap == MAP_FAILED) { + printf("cannot mmap dumb buffer (%d): %m\r\n", errno); + ret = -errno; + goto fb_fail; + } + + memset(dp_conn->failsafe_pixmap, 0, dp_conn->failsafe_size); + return 0; + +fb_fail: + drmModeRmFB(drv_fd, dp_conn->failsafe_fb); + +cleanup: + memset(&dreq, 0, sizeof(dreq)); + dreq.handle = dp_conn->failsafe_handle; + drmIoctl(drv_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + return ret; +} + +int set_video_mode(int drv_fd, int mode, struct dp_connector *test_connector) +{ + drmModeModeInfo *requested_mode; + uint32_t required_fb; + int ret = 0; + + printf("Mode requested: "); + 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; + break; + 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_INVALID: + default: + printf("INVALID! (%08x) Mode set aborted!\r\n", mode); + return -1; + break; + } + test_connector->saved_crtc = drmModeGetCrtc(drv_fd, test_connector->crtc); + 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; +} + +void shutdown(int drv_fd) +{ + int i; + struct dp_connector *index = dp_connector_list, *prev; + struct drm_mode_destroy_dumb dreq; + + for (i = 0; i < dp_connector_count; i++) { + if (index->saved_crtc) { + drmModeSetCrtc(drv_fd, index->saved_crtc->crtc_id, + index->saved_crtc->buffer_id, + index->saved_crtc->x, + index->saved_crtc->y, &index->conn, 1, + &index->saved_crtc->mode); + drmModeFreeCrtc(index->saved_crtc); + } + drmModeRmFB(drv_fd, index->fb); + /* Clean up dumb buffer */ + memset(&dreq, 0, sizeof(dreq)); + dreq.handle = index->fb_handle; + drmIoctl(drv_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + prev = index; + index = index->next; + free(prev); + } +} + +int main(int argc, char *argv[]) +{ + int status = 0; + int drv_fd = 0; + drmModeResPtr mode_resources = NULL; + int done = 0; + char input; + struct dp_connector *active_connector; + int test_data, test_type; + + /* 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)" + ); + input = getchar(); + + if (input != 'Y' && input != 'y') + goto exit; + + /* All-in-one setup function to get DRM off the ground */ + setup_drm(&drv_fd, I915_DRIVER_NODE); + + /* From moderes, get the connectors, encoders, CRTCs, etc */ + setup_connectors(drv_fd, mode_resources); + + active_connector = &dp_connector_list[0]; + + setup_debugfs_files(&test_active_fd, &test_type_fd, &test_data_fd); + + printf("\r\n\r\nWaiting for test requests. 'X' or 'Q' exits\r\n"); + + while (!done) { + if (kbhit()) { + input = getch(); + switch (input) { + case 'q': + case 'Q': + case 'x': + case 'X': + done = 1; + break; + } + } + usleep(1000); + if (check_test_active()) { + test_data = get_test_data(); + test_type = get_test_type(); + process_test_request(drv_fd, test_type, test_data, active_connector); + usleep(10000); + } + } + +exit: + cleanup_debugfs(); + + shutdown(drv_fd); + + if (drv_fd > 0) { + status = drmClose(drv_fd); + if (status) + printf("Error: Failed to close i915 driver\r\n"); + } + + 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"); + + return status; +} + diff --git a/dp_compliance/input.c b/dp_compliance/input.c new file mode 100644 index 0000000..7281ce6 --- /dev/null +++ b/dp_compliance/input.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include + +/* Code sourced from stackoverflow.com - + http://stackoverflow.com/questions/448944/c-non-blocking-keyboard-input +*/ + +struct termios orig_termios; + +void reset_terminal_mode() +{ + tcsetattr(0, TCSANOW, &orig_termios); +} + +void set_conio_terminal_mode() +{ + struct termios new_termios; + + /* take two copies - one for now, 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); +} + +int kbhit() +{ + struct timeval tv = { 0L, 0L }; + fd_set fds; + FD_ZERO(&fds); + FD_SET(0, &fds); + return select(1, &fds, NULL, NULL, &tv); +} + +int getch() +{ + int r; + unsigned char c = 0; + if ((r = read(0, &c, sizeof(c))) < 0) { + return r; + } else return c; +}