Message ID | 1481148292-25848-1-git-send-email-manasi.d.navare@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Wed, Dec 07, 2016 at 02:04:52PM -0800, Manasi Navare wrote: > 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 has an automation support for the Link training tests > (4.3.1.1. - 4.3.2.3), EDID tests (4.2.2.3 > - 4.2.2.6) and Video Pattern generation tests (4.3.3.1) from CTS > specification 1.2 Rev 1.1. > > This tool has the support for responding to the hotplug uevents > sent by compliance testting unit after each test. > > 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 <tprevite@gmail.com>) > > v2: > * Add mode unset on hotplug uevent on disconnect (Manasi Navare) > > Cc: Petri Latvala <petri.latvala@intel.com> > Cc: Marius Vlad <marius.c.vlad@intel.com> > Cc: Daniel Vetter <daniel.vetter@ffwll.ch> > Signed-off-by: Manasi Navare <manasi.d.navare@intel.com> Some general suggestions (more inline as well): * Please audit all output messages for proper spelling, grammar, caps, etc. * Many of the lines are quite long. It would be nice to keep the line lengths short (under 100 chars for sure, but IMHO under 78 is even better.) * Many of the functions only return '0'; consider changing them to void. * Perhaps this is excess paranoia, but I would check the validity of any pointer passed into a function before dereferencing it. This isn't done in several cases. * Make sure and check out Petri's feedback, all of which I agree with. Jim > --- > tools/Makefile.am | 3 +- > tools/Makefile.sources | 7 + > tools/intel_dp_compliance.c | 1060 +++++++++++++++++++++++++++++++++++ > tools/intel_dp_compliance.h | 35 ++ > tools/intel_dp_compliance_hotplug.c | 123 ++++ > 5 files changed, 1227 insertions(+), 1 deletion(-) > create mode 100644 tools/intel_dp_compliance.c > create mode 100644 tools/intel_dp_compliance.h > create mode 100644 tools/intel_dp_compliance_hotplug.c > > diff --git a/tools/Makefile.am b/tools/Makefile.am > index 18f86f6..eac6d64 100644 > --- a/tools/Makefile.am > +++ b/tools/Makefile.am > @@ -13,7 +13,7 @@ AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib > AM_CFLAGS = $(DEBUG_CFLAGS) $(DRM_CFLAGS) $(PCIACCESS_CFLAGS) $(CWARNFLAGS) \ > $(CAIRO_CFLAGS) $(LIBUNWIND_CFLAGS) -DPKGDATADIR=\"$(pkgdatadir)\" \ > $(WERROR_CFLAGS) > -LDADD = $(top_builddir)/lib/libintel_tools.la > +LDADD = $(top_builddir)/lib/libintel_tools.la $(GLIB_LIBS) > AM_LDFLAGS = -Wl,--as-needed > > > @@ -24,6 +24,7 @@ moduledir = $(libdir) > intel_aubdump_la_LDFLAGS = -module -avoid-version -no-undefined > intel_aubdump_la_SOURCES = aubdump.c > intel_aubdump_la_LIBADD = $(top_builddir)/lib/libintel_tools.la -ldl > +intel_dp_compliance_la_LIBADD = $(LDADD) > > bin_SCRIPTS = intel_aubdump > CLEANFILES = $(bin_SCRIPTS) > diff --git a/tools/Makefile.sources b/tools/Makefile.sources > index be58871..09c0667 100644 > --- a/tools/Makefile.sources > +++ b/tools/Makefile.sources > @@ -13,6 +13,7 @@ tools_prog_lists = \ > intel_bios_reader \ > intel_display_crc \ > intel_display_poller \ > + intel_dp_compliance \ > intel_forcewaked \ > intel_gpu_frequency \ > intel_firmware_decode \ > @@ -55,3 +56,9 @@ intel_l3_parity_SOURCES = \ > intel_l3_parity.h \ > intel_l3_udev_listener.c > > +intel_dp_compliance_SOURCES = \ > + intel_dp_compliance.c \ > + intel_dp_compliance.h \ > + intel_dp_compliance_hotplug.c \ > + $(NULL) > + > diff --git a/tools/intel_dp_compliance.c b/tools/intel_dp_compliance.c > new file mode 100644 > index 0000000..807d3f4 > --- /dev/null > +++ b/tools/intel_dp_compliance.c > @@ -0,0 +1,1060 @@ > +/* > + * 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 > + * - 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: > + * - Link Training Tests: Supports tests 4.3.1.1 to 4.3.2.3 > + * - 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 (4.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 > + * (<tprevite@gmail.com>) > + * > + * > + * This tool can be run as: > + * ./intel_dp_compliance It will wait till you start compliance suite from > + * DPR 120. > + * ./intel_dp_compliance -h This will open the help > + * ./intel_dp_compliance -i This will provide information about current > + * connectors/CRTCs. This can be used for debugging purpose. > + * > + * Authors: > + * Manasi Navare <manasi.d.navare@intel.com> > + * > + * Elements of the modeset code adapted from David Herrmann's > + * DRM modeset example > + * > + */ > +#include "igt.h" > +#include <errno.h> > +#include <getopt.h> > +#include <math.h> > +#include <stdint.h> > +#include <stdbool.h> > +#include <strings.h> > +#include <unistd.h> > +#include <termios.h> > +#include <sys/poll.h> > +#include <sys/time.h> > +#include <sys/ioctl.h> > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <sys/select.h> > +#include <assert.h> > +#include <signal.h> > +#include <fcntl.h> > +#include <time.h> > + > +#include "intel_dp_compliance.h" > + > +#include <stdlib.h> > +#include <signal.h> > + > +/* User Input definitions */ > +#define HELP_DESCRIPTION 1 > + > +/* Debugfs file definitions */ > +#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 file pointers for the sysfs files */ > +FILE *test_active_fp, *test_data_fp, *test_type_fp; > + > +bool video_pattern_flag; > + > +/* Video pattern test globals */ > +uint16_t hdisplay; > +uint16_t vdisplay; > +uint8_t bitdepth; > + > +static int tio_fd; > +struct termios saved_tio; > + > +drmModeRes *resources; > +int drm_fd, modes; > +uint64_t tiling = LOCAL_DRM_FORMAT_MOD_NONE; > +uint32_t depth = 24, stride, bpp; > +int specified_mode_num = -1, specified_disp_id = -1; > +int width, height; > +uint32_t test_crtc; > +uint32_t test_connector_id; > +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 connector { > + uint32_t id; > + int mode_valid; > + drmModeModeInfo mode, mode_standard, mode_preferred, mode_failsafe; > + drmModeConnector *connector; > + int crtc; > + /* 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; > +}; > + > +static void clear_test_active(void) > +{ > + rewind(test_active_fp); > + fprintf(test_active_fp, "%d", 0); > + fflush(test_active_fp); > +} > + > +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 unsigned long get_test_type(void) > +{ > + unsigned long test_type; > + > + if (!test_type_fp) > + fprintf(stderr, "Invalid Test Type File pointr\r\n"); Spelling 'pointr', funky caps. > + rewind(test_type_fp); > + fscanf(test_type_fp, "%02lx", &test_type); > + if (test_type <= 0) { > + igt_warn("Test type read failed - %02lx\r\n", test_type); > + return 0; > + } > + > + return test_type; > +} > + > +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) { > + igt_warn("Test data read failed - %lx\r\n", test_data); > + return 0; > + } > + > + return test_data; > +} > + > +static void get_test_videopattern_data(void) > +{ > + 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, > + (unsigned int *)&video_pattern_value[count++]); > + > + hdisplay = video_pattern_value[0]; > + vdisplay = video_pattern_value[1]; > + bitdepth = video_pattern_value[2]; > + igt_info("Hdisplay = %d\r\n", hdisplay); > + igt_info("Vdisplay = %d\r\n", vdisplay); > + igt_info("BitDepth = %u\r\n", bitdepth); > + > +} > + > +static int process_test_request(int test_type) > +{ > + int mode; > + unsigned long test_data_edid; > + int status = 0; I'd rework this function a bit, since the failure case is not correct. We should not call update_display() if we hit the default case of the switch below is hit. If you start with status = -1, and then set status to 0 when you hit one of the valid test types, and then only call update_display() if status == 0, then everything should work right. For that matter, since there's really only one failure mode you might consider having this function return a bool. > + > + switch (test_type) { > + case DP_TEST_LINK_VIDEO_PATTERN: > + video_pattern_flag = true; > + get_test_videopattern_data(); > + mode = INTEL_MODE_VIDEO_PATTERN_TEST; > + break; > + case DP_TEST_LINK_EDID_READ: > + test_data_edid = get_test_edid_data(); > + mode = (test_data_edid & DP_COMPLIANCE_VIDEO_MODE_MASK) >> > + INTEL_DP_RESOLUTION_SHIFT_MASK; > + break; > + default: > + /* Unknown test type */ > + fprintf(stderr, "Invalid test request. Ignored.\r\n"); > + break; > + } > + status = update_display(mode, true); > + > + /* Return 0 on success and -1 on failure */ > + return status; > +} > + > +static void dump_connectors_fd(int drmfd) > +{ > + int i, j; > + > + drmModeRes *mode_resources = drmModeGetResources(drmfd); > + > + if (!mode_resources) { > + igt_warn("drmModeGetResources failed: %s\n", strerror(errno)); > + return; > + } > + > + igt_info("Connectors:\n"); > + igt_info("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\n"); > + for (i = 0; i < mode_resources->count_connectors; i++) { > + drmModeConnector *connector; > + > + connector = drmModeGetConnectorCurrent(drmfd, > + mode_resources->connectors[i]); > + if (!connector) { > + igt_warn("could not get connector %i: %s\n", mode_resources->connectors[i], strerror(errno)); > + continue; > + } > + > + igt_info("%d\t%d\t%s\t%s\t%dx%d\t\t%d\n", connector->connector_id, connector->encoder_id, kmstest_connector_status_str(connector->connection), kmstest_connector_type_str(connector->connector_type), connector->mmWidth, connector->mmHeight, connector->count_modes); > + > + if (!connector->count_modes) > + continue; > + > + igt_info(" modes:\n"); > + igt_info(" name refresh (Hz) hdisp hss hse htot vdisp ""vss vse vtot flags type clock\n"); > + for (j = 0; j < connector->count_modes; j++) { > + igt_info("[%d]", j); > + kmstest_dump_mode(&connector->modes[j]); > + } > + > + drmModeFreeConnector(connector); > + } > + igt_info("\n"); > + > + drmModeFreeResources(mode_resources); > +} > + > +static void dump_crtcs_fd(int drmfd) > +{ > + int i; > + drmModeRes *mode_resources = drmModeGetResources(drmfd); > + Need to check for mode_resources == NULL before using it. > + igt_info("CRTCs:\n"); > + igt_info("id\tfb\tpos\tsize\n"); > + for (i = 0; i < mode_resources->count_crtcs; i++) { > + drmModeCrtc *crtc; > + > + crtc = drmModeGetCrtc(drmfd, mode_resources->crtcs[i]); > + if (!crtc) { > + igt_warn("could not get crtc %i: %s\n", mode_resources->crtcs[i], strerror(errno)); > + continue; > + } > + igt_info("%d\t%d\t(%d,%d)\t(%dx%d)\n", crtc->crtc_id, crtc->buffer_id, crtc->x, crtc->y, crtc->width, crtc->height); > + kmstest_dump_mode(&crtc->mode); > + > + drmModeFreeCrtc(crtc); > + } > + igt_info("\n"); > + > + drmModeFreeResources(mode_resources); > +} > + > +static void dump_info(void) > +{ > + dump_connectors_fd(drm_fd); > + dump_crtcs_fd(drm_fd); > +} > + > +static int setup_framebuffers(struct connector *dp_conn) static void? I don't see any failure casees here. > +{ > + Paranoia: check for non-NULL dp_conn? > + dp_conn->fb = igt_create_fb(drm_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(drm_fd, > + dp_conn->fb_video_pattern.gem_handle, > + dp_conn->fb_video_pattern.size, > + PROT_READ | PROT_WRITE); > + gem_set_domain(drm_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(struct connector *dp_conn) > +{ > + > + dp_conn->failsafe_fb = igt_create_fb(drm_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(drm_fd, > + dp_conn->fb_failsafe_pattern.gem_handle, > + dp_conn->fb_failsafe_pattern.size, > + PROT_READ | PROT_WRITE); > + gem_set_domain(drm_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(struct connector *dp_conn) > +{ > + uint32_t video_width, video_height; > + > + video_width = dp_conn->test_pattern.hdisplay; > + video_height = dp_conn->test_pattern.vdisplay; > + dp_conn->test_pattern.fb = igt_create_fb(drm_fd, > + video_width, video_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(drm_fd, > + dp_conn->test_pattern.fb_pattern.gem_handle, > + dp_conn->test_pattern.fb_pattern.size, > + PROT_READ | PROT_WRITE); > + gem_set_domain(drm_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(struct connector *dp_conn) > +{ > + uint32_t tile_height, tile_width, video_width, video_height; > + uint32_t *red_ptr, *green_ptr, *blue_ptr, *white_ptr, *src_ptr, *dst_ptr; > + int x, y; > + int32_t pixel_val; > + > + video_width = dp_conn->test_pattern.hdisplay; > + video_height = dp_conn->test_pattern.vdisplay; > + > + tile_height = 64; > + tile_width = 1 << (dp_conn->test_pattern.bitdepth); > + > + red_ptr = dp_conn->test_pattern.pixmap; > + green_ptr = red_ptr + (video_width * tile_height); > + blue_ptr = green_ptr + (video_width * tile_height); > + white_ptr = blue_ptr + (video_width * tile_height); > + x = 0; > + > + /* Fill the frame buffer with video pattern from CTS 3.1.5 */ > + while (x < video_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 >= video_width) > + break; > + } > + } > + for (y = 0; y < video_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 * video_width); > + memcpy(dst_ptr, src_ptr, (video_width * 4)); > + } > + munmap(dp_conn->test_pattern.pixmap, > + dp_conn->test_pattern.size); > + return 0; > +} > + > +static int set_test_mode(struct connector *dp_conn) > +{ > + int ret = 0; > + int i; > + bool found_std = false, found_fs = false; > + drmModeConnector *c = dp_conn->connector; > + > + /* Ignore any disconnected devices */ > + if (c->connection != DRM_MODE_CONNECTED) { > + igt_warn("Connector %u disconnected\r\n", c->connector_id); > + return -ENOENT; > + } > + igt_info("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; > + > + igt_info("Preferred mode (mode 0) for connector %u is %ux%u\r\n", > + dp_conn->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]; > + igt_info("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; > + igt_info("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_framebuffers(dp_conn); > + if (ret) { > + igt_warn("Create framebuffer for connector %u failed (%d)\r\n", > + c->connector_id, ret); > + return ret; > + } > + > + if (found_fs) { > + ret = setup_failsafe_framebuffer(dp_conn); > + if (ret) { > + igt_warn("Create failsafe framebuffer for connector %u" > + "failed (%d)\r\n", > + c->connector_id, ret); > + return ret; > + } > + } > + > + if (video_pattern_flag) { > + dp_conn->test_pattern.hdisplay = hdisplay; > + dp_conn->test_pattern.vdisplay = vdisplay; > + dp_conn->test_pattern.bitdepth = bitdepth; > + ret = setup_video_pattern_framebuffer(dp_conn); > + if (ret) { > + igt_warn("Create framebuffer for connector %u failed (%d)\r\n", > + c->connector_id, ret); > + return ret; > + } > + ret = fill_framebuffer(dp_conn); > + if (ret) { > + igt_warn("Fill framebuffer for connector %u failed (%d)\r\n", > + c->connector_id, ret); > + return ret; > + } > + } > + > + return ret; > +} > + > +static int set_video(int mode, struct connector *test_connector) > +{ > + drmModeModeInfo *requested_mode; > + uint32_t required_fb_id; > + struct igt_fb required_fb; > + int ret = 0; > + > + switch (mode) { > + case INTEL_MODE_NONE: > + igt_info("NONE\r\n"); > + ret = drmModeSetCrtc(drm_fd, test_connector->crtc, > + -1, 0, 0, NULL, 0, NULL); > + goto out; > + case INTEL_MODE_PREFERRED: > + igt_info("PREFERRED\r\n"); > + requested_mode = &test_connector->mode_preferred; > + required_fb_id = test_connector->fb; > + required_fb = test_connector->fb_video_pattern; > + break; > + case INTEL_MODE_STANDARD: > + igt_info("STANDARD\r\n"); > + requested_mode = &test_connector->mode_standard; > + required_fb_id = test_connector->fb; > + required_fb = test_connector->fb_video_pattern; > + break; > + case INTEL_MODE_FAILSAFE: > + igt_info("FAILSAFE\r\n"); > + requested_mode = &test_connector->mode_failsafe; > + required_fb_id = test_connector->failsafe_fb; > + required_fb = test_connector->fb_failsafe_pattern; > + break; > + case INTEL_MODE_VIDEO_PATTERN_TEST: > + igt_info("VIDEO PATTERN TEST\r\n"); > + requested_mode = &test_connector->test_pattern.mode; > + required_fb_id = test_connector->test_pattern.fb; > + required_fb = test_connector->test_pattern.fb_pattern; > + break; > + case INTEL_MODE_INVALID: > + default: > + igt_warn("INVALID! (%08x) Mode set aborted!\r\n", mode); > + return -1; > + } > + > + igt_info("CRTC(%u):", test_connector->crtc); > + kmstest_dump_mode(requested_mode); > + ret = drmModeSetCrtc(drm_fd, test_connector->crtc, required_fb_id, 0, 0, > + &test_connector->id, 1, requested_mode); > + if (ret) { > + igt_warn("failed to set mode (%dx%d@%dHz): %s\n", > + requested_mode->hdisplay, requested_mode->vdisplay, > + requested_mode->vrefresh, strerror(errno)); > + igt_remove_fb(drm_fd, &required_fb); > + > + } > + /* Keep the pattern on output lines for atleast 0.5 secs for > + * DPR-120 to detect it. > + */ > + usleep(1000000); Consider a #define for this value? > + > +out: > + if (ret) { > + igt_warn("Failed to set CRTC for connector %u\r\n", > + test_connector->id); > + } > + > + return ret; > +} > + > +static int > +set_default_mode(struct connector *c, bool set_mode) > +{ > + unsigned int fb_id = 0; > + struct igt_fb fb_info; > + int ret = 0; > + > + if (!set_mode) { > + ret = drmModeSetCrtc(drm_fd, c->crtc, 0, 0, 0, > + NULL, 0, NULL); > + if (ret) > + igt_warn("failed to unset mode"); > + return ret; > + } > + > + c->mode = c->connector->modes[0]; > + > + width = c->mode.hdisplay; > + height = c->mode.vdisplay; > + > + fb_id = igt_create_pattern_fb(drm_fd, width, height, > + DRM_FORMAT_XRGB8888, > + tiling, &fb_info); > + > + igt_info("CRTC(%u):[%d]", c->crtc, 0); > + kmstest_dump_mode(&c->mode); > + drmModeSetCrtc(drm_fd, c->crtc, -1, 0, 0, NULL, 0, NULL); > + ret = drmModeSetCrtc(drm_fd, c->crtc, fb_id, 0, 0, > + &c->id, 1, &c->mode); > + if (ret) { > + igt_warn("failed to set mode (%dx%d@%dHz): %s\n", width, height, c->mode.vrefresh, strerror(errno)); > + igt_remove_fb(drm_fd, &fb_info); > + > + } > + > + return ret; > +} > + > +static uint32_t find_crtc_for_connector(drmModeConnector *c) > +{ > + drmModeEncoder *e; > + uint32_t possible_crtcs; > + int i, j; > + > + for (i = 0; i < c->count_encoders; i++) { > + e = drmModeGetEncoder(drm_fd, c->encoders[i]); > + possible_crtcs = e->possible_crtcs; > + drmModeFreeEncoder(e); > + > + for (j = 0; possible_crtcs >> j; j++) > + if (possible_crtcs & (1 << j)) > + return resources->crtcs[j]; > + } > + return 0; > +} > + > +/* > + * Re-probe connectors and do a modeset based on test request or > + * in case of a hotplug uevent. > + * > + * @mode: Video mode requested by the test > + * @is_compliance_test: 1: If it is a compliance test > + * 0: If it is a hotplug event > + * > + * Returns: > + * 0: On Success > + * -1: On failure > + */ > +int update_display(int mode, bool is_compliance_test) > +{ > + struct connector *connectors, *conn; > + int cnt, ret = 0; > + bool set_mode; > + > + resources = drmModeGetResources(drm_fd); > + if (!resources) { > + igt_warn("drmModeGetResources failed: %s\n", strerror(errno)); > + return -1; > + } > + > + connectors = calloc(resources->count_connectors, > + sizeof(struct connector)); > + if (!connectors) > + return -1; > + > + /* Find any connected displays */ > + for (cnt = 0; cnt < resources->count_connectors; cnt++) { > + > + conn = &connectors[cnt]; > + drmModeConnector *c; > + > + conn->id = resources->connectors[cnt]; > + c = drmModeGetConnector(drm_fd, conn->id); > + if (c->connector_type == DRM_MODE_CONNECTOR_DisplayPort && > + c->connection == DRM_MODE_CONNECTED) { > + test_connector_id = c->connector_id; > + conn->connector = c; > + conn->crtc = find_crtc_for_connector(c); > + test_crtc = conn->crtc; > + set_mode = true; > + break; > + } else if (c->connector_id == test_connector_id && > + c->connection == DRM_MODE_DISCONNECTED) { > + conn->connector = c; > + conn->crtc = test_crtc; > + set_mode = false; > + break; > + } > + } > + > + if (cnt == resources->count_connectors) { > + ret = -1; > + goto err; > + } > + > + if (is_compliance_test) { > + set_test_mode(conn); > + ret = set_video(INTEL_MODE_NONE, conn); > + ret = set_video(mode, conn); > + > + } else > + ret = set_default_mode(conn, set_mode); > + > + err: > + drmModeFreeConnector(conn->connector); > + /* Error condition if no connected displays */ > + free(connectors); > + drmModeFreeResources(resources); > + return ret; > +} > + > +static const char optstr[] = "hi"; > + > +static void __attribute__((noreturn)) usage(char *name, char opt) > +{ > + igt_info("usage: %s [-hi]\n", name); > + igt_info("\t-i\tdump info\n"); > + igt_info("\tDefault is to respond to DPR-120 tests.\n"); > + exit((opt != 'h') ? -1 : 0); > +} > + > +static void cleanup_debugfs(void) > +{ > + fclose(test_active_fp); > + fclose(test_data_fp); > + fclose(test_type_fp); > +} > + > +static void __attribute__((noreturn)) cleanup_and_exit(int ret) > +{ > + cleanup_debugfs(); > + close(drm_fd); > + igt_info("Compliance testing Application Exiting\n"); > + exit(ret); > +} > + > +static void cleanup_test(void) > +{ > + video_pattern_flag = false; > + hdisplay = 0; > + vdisplay = 0; > + bitdepth = 0; > +} > + > +static void read_test_request(void) > +{ > + unsigned long test_type; > + > + test_type = get_test_type(); > + process_test_request(test_type); > + cleanup_test(); > + clear_test_active(); > +} > + > +static gboolean test_handler(GIOChannel *source, GIOCondition condition, > + gpointer data) > +{ > + unsigned long test_active; > + > + rewind(test_active_fp); > + fscanf(test_active_fp, "%lx", &test_active); > + if (test_active) > + read_test_request(); > + return TRUE; > +} > + > +static gboolean input_event(GIOChannel *source, GIOCondition condition, > + gpointer data) > +{ > + gchar buf[2]; > + gsize count; > + > + count = read(g_io_channel_unix_get_fd(source), buf, sizeof(buf)); > + if (buf[0] == 'q' && (count == 1 || buf[1] == '\n')) { > + cleanup_and_exit(0); > + } > + > + return TRUE; > +} > + > +static void enter_exec_path(char **argv) > +{ > + char *exec_path = NULL; > + char *pos = NULL; > + short len_path = 0; > + int ret; > + > + len_path = strlen(argv[0]); > + exec_path = (char *) malloc(len_path); > + > + memcpy(exec_path, argv[0], len_path); > + pos = strrchr(exec_path, '/'); > + if (pos != NULL) > + *(pos+1) = '\0'; > + > + ret = chdir(exec_path); > + igt_assert_eq(ret, 0); > + free(exec_path); > +} > + > +static void restore_termio_mode(int sig) > +{ > + tcsetattr(tio_fd, TCSANOW, &saved_tio); > + close(tio_fd); > +} > + > +static void set_termio_mode(void) > +{ > + struct termios tio; > + > + /* don't attempt to set terminal attributes if not in the foreground > + * process group > + */ > + if (getpgrp() != tcgetpgrp(STDOUT_FILENO)) > + return; > + > + tio_fd = dup(STDIN_FILENO); > + tcgetattr(tio_fd, &saved_tio); > + igt_install_exit_handler(restore_termio_mode); > + tio = saved_tio; > + tio.c_lflag &= ~(ICANON | ECHO); > + tcsetattr(tio_fd, TCSANOW, &tio); > +} > + > +int main(int argc, char **argv) > +{ > + int c; > + int ret = 0; > + GIOChannel *stdinchannel, *testactive_channel; > + GMainLoop *mainloop; > + bool opt_dump_info = false; > + struct option long_opts[] = { > + {"help-description", 0, 0, HELP_DESCRIPTION}, > + {"help", 0, 0, 'h'}, > + }; > + > + igt_skip_on_simulation(); > + > + enter_exec_path(argv); > + > + while ((c = getopt_long(argc, argv, optstr, long_opts, NULL)) != -1) { > + switch (c) { > + case 'i': > + opt_dump_info = true; > + break; > + case HELP_DESCRIPTION: > + igt_info("DP Compliance Test Suite using DPR-120\n"); > + igt_info("EDID Tests\n"); > + igt_info("Video Pattern Generation Tests\n"); > + exit(0); > + break; > + default: > + /* fall through */ > + case 'h': > + usage(argv[0], c); > + break; > + } > + } > + > + set_termio_mode(); > + > + igt_info("*************DP Compliance Testing using DPR-120*************\n"); > + igt_info("Waiting for Test Request Input......\n"); > + drm_fd = drm_open_driver(DRIVER_ANY); > + > + kmstest_set_vt_graphics_mode(); > + setup_debugfs_files(); > + cleanup_test(); > + > + if (opt_dump_info) { > + dump_info(); > + goto out_close; > + } > + > + /* Get the DP connector ID and CRTC */ > + if (update_display(0, false)) { > + igt_warn("Failed to set default mode\n"); > + ret = -1; > + goto out_close; > + } > + > + mainloop = g_main_loop_new(NULL, FALSE); > + if (!mainloop) { > + igt_warn("failed to create glib mainloop\n"); > + ret = -1; > + goto out_close; > + } > + > + if (!intel_dp_compliance_setup_hotplug()) { > + igt_warn("failed to initialize hotplug support\n"); > + goto out_mainloop; > + } > + > + testactive_channel = g_io_channel_unix_new(fileno(test_active_fp)); > + if (!testactive_channel) { > + igt_warn("failed to create Test Active GIO channel\n"); > + goto out_close; > + } > + > + ret = g_io_add_watch(testactive_channel, G_IO_IN | G_IO_ERR, test_handler, > + NULL); > + if (ret < 0) { > + igt_warn("failed to add watch on udev GIO channel\n"); > + goto out_close; > + } > + > + stdinchannel = g_io_channel_unix_new(0); > + if (!stdinchannel) { > + igt_warn("failed to create stdin GIO channel\n"); > + goto out_hotplug; > + } > + > + ret = g_io_add_watch(stdinchannel, G_IO_IN | G_IO_ERR, input_event, > + NULL); > + if (ret < 0) { > + igt_warn("failed to add watch on stdin GIO channel\n"); > + goto out_stdio; > + } > + > + ret = 0; > + > + g_main_loop_run(mainloop); > + > +out_stdio: > + g_io_channel_shutdown(stdinchannel, TRUE, NULL); > +out_hotplug: > + intel_dp_compliance_cleanup_hotplug(); > +out_mainloop: > + g_main_loop_unref(mainloop); > +out_close: > + cleanup_debugfs(); > + close(drm_fd); > + > + igt_assert_eq(ret, 0); > + > + igt_info("Compliance testing application exiting.\n"); > + igt_exit(); > +} > diff --git a/tools/intel_dp_compliance.h b/tools/intel_dp_compliance.h > new file mode 100644 > index 0000000..ade1e2f > --- /dev/null > +++ b/tools/intel_dp_compliance.h > @@ -0,0 +1,35 @@ > +/* > + * Copyright 2010 Intel Corporation > + * Manasi Navare <manasi.d.navare@intel.com> > + * > + * 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 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. > + */ > + > +#include <stdio.h> > +#include <string.h> > +#include <stdlib.h> > +#include <glib.h> > + > +extern int drm_fd; > + > +gboolean intel_dp_compliance_setup_hotplug(void); > +void intel_dp_compliance_cleanup_hotplug(void); > + > +/* called by the hotplug code */ > +int update_display(int mode, bool is_compliance_test); > diff --git a/tools/intel_dp_compliance_hotplug.c b/tools/intel_dp_compliance_hotplug.c > new file mode 100644 > index 0000000..d489026 > --- /dev/null > +++ b/tools/intel_dp_compliance_hotplug.c > @@ -0,0 +1,123 @@ > +/* > + * Copyright 2010 Intel Corporation > + * Jesse Barnes <jesse.barnes@intel.com> > + * > + * 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 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. > + */ > + > +#include "igt.h" > +#include <stdio.h> > +#include <string.h> > +#include <stdlib.h> > + > +#include <sys/stat.h> > + > +#include "intel_dp_compliance.h" > +#include <libudev.h> > +static struct udev_monitor *uevent_monitor; > +static struct udev *udev; > +static GIOChannel *udevchannel; > + > +static gboolean hotplug_event(GIOChannel *source, GIOCondition condition, > + gpointer data) > +{ > + struct udev_device *dev; > + dev_t udev_devnum; > + struct stat s; > + const char *hotplug; > + > + dev = udev_monitor_receive_device(uevent_monitor); > + if (!dev) > + goto out; > + > + udev_devnum = udev_device_get_devnum(dev); > + fstat(drm_fd, &s); > + > + hotplug = udev_device_get_property_value(dev, "HOTPLUG"); > + > + if (memcmp(&s.st_rdev, &udev_devnum, sizeof(dev_t)) == 0 && > + hotplug && atoi(hotplug) == 1) > + update_display(0, false); > + > + udev_device_unref(dev); > +out: > + return TRUE; > +} > + > + > +gboolean intel_dp_compliance_setup_hotplug(void) > +{ > + int ret; > + > + udev = udev_new(); > + if (!udev) { > + igt_warn("failed to create udev object\n"); > + goto out; > + } > + > + uevent_monitor = udev_monitor_new_from_netlink(udev, "udev"); > + if (!uevent_monitor) { > + igt_warn("failed to create udev event monitor\n"); > + goto out; > + } > + > + ret = udev_monitor_filter_add_match_subsystem_devtype(uevent_monitor, > + "drm", > + "drm_minor"); > + if (ret < 0) { > + igt_warn("failed to filter for drm events\n"); > + goto out; > + } > + > + ret = udev_monitor_enable_receiving(uevent_monitor); > + if (ret < 0) { > + igt_warn("failed to enable udev event reception\n"); > + goto out; > + } > + > + udevchannel = > + g_io_channel_unix_new(udev_monitor_get_fd(uevent_monitor)); > + if (!udevchannel) { > + igt_warn("failed to create udev GIO channel\n"); > + goto out; > + } > + > + ret = g_io_add_watch(udevchannel, G_IO_IN | G_IO_ERR, hotplug_event, > + udev); > + if (ret < 0) { > + igt_warn("failed to add watch on udev GIO channel\n"); > + goto out; > + } > + > + return TRUE; > + > +out: > + intel_dp_compliance_cleanup_hotplug(); > + return FALSE; > +} > + > +void intel_dp_compliance_cleanup_hotplug(void) > +{ > + if (udevchannel) > + g_io_channel_shutdown(udevchannel, TRUE, NULL); > + if (uevent_monitor) > + udev_monitor_unref(uevent_monitor); > + if (udev) > + udev_unref(udev); > +} > -- > 1.9.1 > > _______________________________________________ > Intel-gfx mailing list > Intel-gfx@lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/intel-gfx
diff --git a/tools/Makefile.am b/tools/Makefile.am index 18f86f6..eac6d64 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -13,7 +13,7 @@ AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib AM_CFLAGS = $(DEBUG_CFLAGS) $(DRM_CFLAGS) $(PCIACCESS_CFLAGS) $(CWARNFLAGS) \ $(CAIRO_CFLAGS) $(LIBUNWIND_CFLAGS) -DPKGDATADIR=\"$(pkgdatadir)\" \ $(WERROR_CFLAGS) -LDADD = $(top_builddir)/lib/libintel_tools.la +LDADD = $(top_builddir)/lib/libintel_tools.la $(GLIB_LIBS) AM_LDFLAGS = -Wl,--as-needed @@ -24,6 +24,7 @@ moduledir = $(libdir) intel_aubdump_la_LDFLAGS = -module -avoid-version -no-undefined intel_aubdump_la_SOURCES = aubdump.c intel_aubdump_la_LIBADD = $(top_builddir)/lib/libintel_tools.la -ldl +intel_dp_compliance_la_LIBADD = $(LDADD) bin_SCRIPTS = intel_aubdump CLEANFILES = $(bin_SCRIPTS) diff --git a/tools/Makefile.sources b/tools/Makefile.sources index be58871..09c0667 100644 --- a/tools/Makefile.sources +++ b/tools/Makefile.sources @@ -13,6 +13,7 @@ tools_prog_lists = \ intel_bios_reader \ intel_display_crc \ intel_display_poller \ + intel_dp_compliance \ intel_forcewaked \ intel_gpu_frequency \ intel_firmware_decode \ @@ -55,3 +56,9 @@ intel_l3_parity_SOURCES = \ intel_l3_parity.h \ intel_l3_udev_listener.c +intel_dp_compliance_SOURCES = \ + intel_dp_compliance.c \ + intel_dp_compliance.h \ + intel_dp_compliance_hotplug.c \ + $(NULL) + diff --git a/tools/intel_dp_compliance.c b/tools/intel_dp_compliance.c new file mode 100644 index 0000000..807d3f4 --- /dev/null +++ b/tools/intel_dp_compliance.c @@ -0,0 +1,1060 @@ +/* + * 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 + * - 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: + * - Link Training Tests: Supports tests 4.3.1.1 to 4.3.2.3 + * - 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 (4.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 + * (<tprevite@gmail.com>) + * + * + * This tool can be run as: + * ./intel_dp_compliance It will wait till you start compliance suite from + * DPR 120. + * ./intel_dp_compliance -h This will open the help + * ./intel_dp_compliance -i This will provide information about current + * connectors/CRTCs. This can be used for debugging purpose. + * + * Authors: + * Manasi Navare <manasi.d.navare@intel.com> + * + * Elements of the modeset code adapted from David Herrmann's + * DRM modeset example + * + */ +#include "igt.h" +#include <errno.h> +#include <getopt.h> +#include <math.h> +#include <stdint.h> +#include <stdbool.h> +#include <strings.h> +#include <unistd.h> +#include <termios.h> +#include <sys/poll.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/select.h> +#include <assert.h> +#include <signal.h> +#include <fcntl.h> +#include <time.h> + +#include "intel_dp_compliance.h" + +#include <stdlib.h> +#include <signal.h> + +/* User Input definitions */ +#define HELP_DESCRIPTION 1 + +/* Debugfs file definitions */ +#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 file pointers for the sysfs files */ +FILE *test_active_fp, *test_data_fp, *test_type_fp; + +bool video_pattern_flag; + +/* Video pattern test globals */ +uint16_t hdisplay; +uint16_t vdisplay; +uint8_t bitdepth; + +static int tio_fd; +struct termios saved_tio; + +drmModeRes *resources; +int drm_fd, modes; +uint64_t tiling = LOCAL_DRM_FORMAT_MOD_NONE; +uint32_t depth = 24, stride, bpp; +int specified_mode_num = -1, specified_disp_id = -1; +int width, height; +uint32_t test_crtc; +uint32_t test_connector_id; +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 connector { + uint32_t id; + int mode_valid; + drmModeModeInfo mode, mode_standard, mode_preferred, mode_failsafe; + drmModeConnector *connector; + int crtc; + /* 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; +}; + +static void clear_test_active(void) +{ + rewind(test_active_fp); + fprintf(test_active_fp, "%d", 0); + fflush(test_active_fp); +} + +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 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) { + igt_warn("Test type read failed - %02lx\r\n", test_type); + return 0; + } + + return test_type; +} + +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) { + igt_warn("Test data read failed - %lx\r\n", test_data); + return 0; + } + + return test_data; +} + +static void get_test_videopattern_data(void) +{ + 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, + (unsigned int *)&video_pattern_value[count++]); + + hdisplay = video_pattern_value[0]; + vdisplay = video_pattern_value[1]; + bitdepth = video_pattern_value[2]; + igt_info("Hdisplay = %d\r\n", hdisplay); + igt_info("Vdisplay = %d\r\n", vdisplay); + igt_info("BitDepth = %u\r\n", bitdepth); + +} + +static int process_test_request(int test_type) +{ + int mode; + unsigned long test_data_edid; + int status = 0; + + switch (test_type) { + case DP_TEST_LINK_VIDEO_PATTERN: + video_pattern_flag = true; + get_test_videopattern_data(); + mode = INTEL_MODE_VIDEO_PATTERN_TEST; + break; + case DP_TEST_LINK_EDID_READ: + test_data_edid = get_test_edid_data(); + mode = (test_data_edid & DP_COMPLIANCE_VIDEO_MODE_MASK) >> + INTEL_DP_RESOLUTION_SHIFT_MASK; + break; + default: + /* Unknown test type */ + fprintf(stderr, "Invalid test request. Ignored.\r\n"); + break; + } + status = update_display(mode, true); + + /* Return 0 on success and -1 on failure */ + return status; +} + +static void dump_connectors_fd(int drmfd) +{ + int i, j; + + drmModeRes *mode_resources = drmModeGetResources(drmfd); + + if (!mode_resources) { + igt_warn("drmModeGetResources failed: %s\n", strerror(errno)); + return; + } + + igt_info("Connectors:\n"); + igt_info("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\n"); + for (i = 0; i < mode_resources->count_connectors; i++) { + drmModeConnector *connector; + + connector = drmModeGetConnectorCurrent(drmfd, + mode_resources->connectors[i]); + if (!connector) { + igt_warn("could not get connector %i: %s\n", mode_resources->connectors[i], strerror(errno)); + continue; + } + + igt_info("%d\t%d\t%s\t%s\t%dx%d\t\t%d\n", connector->connector_id, connector->encoder_id, kmstest_connector_status_str(connector->connection), kmstest_connector_type_str(connector->connector_type), connector->mmWidth, connector->mmHeight, connector->count_modes); + + if (!connector->count_modes) + continue; + + igt_info(" modes:\n"); + igt_info(" name refresh (Hz) hdisp hss hse htot vdisp ""vss vse vtot flags type clock\n"); + for (j = 0; j < connector->count_modes; j++) { + igt_info("[%d]", j); + kmstest_dump_mode(&connector->modes[j]); + } + + drmModeFreeConnector(connector); + } + igt_info("\n"); + + drmModeFreeResources(mode_resources); +} + +static void dump_crtcs_fd(int drmfd) +{ + int i; + drmModeRes *mode_resources = drmModeGetResources(drmfd); + + igt_info("CRTCs:\n"); + igt_info("id\tfb\tpos\tsize\n"); + for (i = 0; i < mode_resources->count_crtcs; i++) { + drmModeCrtc *crtc; + + crtc = drmModeGetCrtc(drmfd, mode_resources->crtcs[i]); + if (!crtc) { + igt_warn("could not get crtc %i: %s\n", mode_resources->crtcs[i], strerror(errno)); + continue; + } + igt_info("%d\t%d\t(%d,%d)\t(%dx%d)\n", crtc->crtc_id, crtc->buffer_id, crtc->x, crtc->y, crtc->width, crtc->height); + kmstest_dump_mode(&crtc->mode); + + drmModeFreeCrtc(crtc); + } + igt_info("\n"); + + drmModeFreeResources(mode_resources); +} + +static void dump_info(void) +{ + dump_connectors_fd(drm_fd); + dump_crtcs_fd(drm_fd); +} + +static int setup_framebuffers(struct connector *dp_conn) +{ + + dp_conn->fb = igt_create_fb(drm_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(drm_fd, + dp_conn->fb_video_pattern.gem_handle, + dp_conn->fb_video_pattern.size, + PROT_READ | PROT_WRITE); + gem_set_domain(drm_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(struct connector *dp_conn) +{ + + dp_conn->failsafe_fb = igt_create_fb(drm_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(drm_fd, + dp_conn->fb_failsafe_pattern.gem_handle, + dp_conn->fb_failsafe_pattern.size, + PROT_READ | PROT_WRITE); + gem_set_domain(drm_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(struct connector *dp_conn) +{ + uint32_t video_width, video_height; + + video_width = dp_conn->test_pattern.hdisplay; + video_height = dp_conn->test_pattern.vdisplay; + dp_conn->test_pattern.fb = igt_create_fb(drm_fd, + video_width, video_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(drm_fd, + dp_conn->test_pattern.fb_pattern.gem_handle, + dp_conn->test_pattern.fb_pattern.size, + PROT_READ | PROT_WRITE); + gem_set_domain(drm_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(struct connector *dp_conn) +{ + uint32_t tile_height, tile_width, video_width, video_height; + uint32_t *red_ptr, *green_ptr, *blue_ptr, *white_ptr, *src_ptr, *dst_ptr; + int x, y; + int32_t pixel_val; + + video_width = dp_conn->test_pattern.hdisplay; + video_height = dp_conn->test_pattern.vdisplay; + + tile_height = 64; + tile_width = 1 << (dp_conn->test_pattern.bitdepth); + + red_ptr = dp_conn->test_pattern.pixmap; + green_ptr = red_ptr + (video_width * tile_height); + blue_ptr = green_ptr + (video_width * tile_height); + white_ptr = blue_ptr + (video_width * tile_height); + x = 0; + + /* Fill the frame buffer with video pattern from CTS 3.1.5 */ + while (x < video_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 >= video_width) + break; + } + } + for (y = 0; y < video_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 * video_width); + memcpy(dst_ptr, src_ptr, (video_width * 4)); + } + munmap(dp_conn->test_pattern.pixmap, + dp_conn->test_pattern.size); + return 0; +} + +static int set_test_mode(struct connector *dp_conn) +{ + int ret = 0; + int i; + bool found_std = false, found_fs = false; + drmModeConnector *c = dp_conn->connector; + + /* Ignore any disconnected devices */ + if (c->connection != DRM_MODE_CONNECTED) { + igt_warn("Connector %u disconnected\r\n", c->connector_id); + return -ENOENT; + } + igt_info("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; + + igt_info("Preferred mode (mode 0) for connector %u is %ux%u\r\n", + dp_conn->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]; + igt_info("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; + igt_info("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_framebuffers(dp_conn); + if (ret) { + igt_warn("Create framebuffer for connector %u failed (%d)\r\n", + c->connector_id, ret); + return ret; + } + + if (found_fs) { + ret = setup_failsafe_framebuffer(dp_conn); + if (ret) { + igt_warn("Create failsafe framebuffer for connector %u" + "failed (%d)\r\n", + c->connector_id, ret); + return ret; + } + } + + if (video_pattern_flag) { + dp_conn->test_pattern.hdisplay = hdisplay; + dp_conn->test_pattern.vdisplay = vdisplay; + dp_conn->test_pattern.bitdepth = bitdepth; + ret = setup_video_pattern_framebuffer(dp_conn); + if (ret) { + igt_warn("Create framebuffer for connector %u failed (%d)\r\n", + c->connector_id, ret); + return ret; + } + ret = fill_framebuffer(dp_conn); + if (ret) { + igt_warn("Fill framebuffer for connector %u failed (%d)\r\n", + c->connector_id, ret); + return ret; + } + } + + return ret; +} + +static int set_video(int mode, struct connector *test_connector) +{ + drmModeModeInfo *requested_mode; + uint32_t required_fb_id; + struct igt_fb required_fb; + int ret = 0; + + switch (mode) { + case INTEL_MODE_NONE: + igt_info("NONE\r\n"); + ret = drmModeSetCrtc(drm_fd, test_connector->crtc, + -1, 0, 0, NULL, 0, NULL); + goto out; + case INTEL_MODE_PREFERRED: + igt_info("PREFERRED\r\n"); + requested_mode = &test_connector->mode_preferred; + required_fb_id = test_connector->fb; + required_fb = test_connector->fb_video_pattern; + break; + case INTEL_MODE_STANDARD: + igt_info("STANDARD\r\n"); + requested_mode = &test_connector->mode_standard; + required_fb_id = test_connector->fb; + required_fb = test_connector->fb_video_pattern; + break; + case INTEL_MODE_FAILSAFE: + igt_info("FAILSAFE\r\n"); + requested_mode = &test_connector->mode_failsafe; + required_fb_id = test_connector->failsafe_fb; + required_fb = test_connector->fb_failsafe_pattern; + break; + case INTEL_MODE_VIDEO_PATTERN_TEST: + igt_info("VIDEO PATTERN TEST\r\n"); + requested_mode = &test_connector->test_pattern.mode; + required_fb_id = test_connector->test_pattern.fb; + required_fb = test_connector->test_pattern.fb_pattern; + break; + case INTEL_MODE_INVALID: + default: + igt_warn("INVALID! (%08x) Mode set aborted!\r\n", mode); + return -1; + } + + igt_info("CRTC(%u):", test_connector->crtc); + kmstest_dump_mode(requested_mode); + ret = drmModeSetCrtc(drm_fd, test_connector->crtc, required_fb_id, 0, 0, + &test_connector->id, 1, requested_mode); + if (ret) { + igt_warn("failed to set mode (%dx%d@%dHz): %s\n", + requested_mode->hdisplay, requested_mode->vdisplay, + requested_mode->vrefresh, strerror(errno)); + igt_remove_fb(drm_fd, &required_fb); + + } + /* Keep the pattern on output lines for atleast 0.5 secs for + * DPR-120 to detect it. + */ + usleep(1000000); + +out: + if (ret) { + igt_warn("Failed to set CRTC for connector %u\r\n", + test_connector->id); + } + + return ret; +} + +static int +set_default_mode(struct connector *c, bool set_mode) +{ + unsigned int fb_id = 0; + struct igt_fb fb_info; + int ret = 0; + + if (!set_mode) { + ret = drmModeSetCrtc(drm_fd, c->crtc, 0, 0, 0, + NULL, 0, NULL); + if (ret) + igt_warn("failed to unset mode"); + return ret; + } + + c->mode = c->connector->modes[0]; + + width = c->mode.hdisplay; + height = c->mode.vdisplay; + + fb_id = igt_create_pattern_fb(drm_fd, width, height, + DRM_FORMAT_XRGB8888, + tiling, &fb_info); + + igt_info("CRTC(%u):[%d]", c->crtc, 0); + kmstest_dump_mode(&c->mode); + drmModeSetCrtc(drm_fd, c->crtc, -1, 0, 0, NULL, 0, NULL); + ret = drmModeSetCrtc(drm_fd, c->crtc, fb_id, 0, 0, + &c->id, 1, &c->mode); + if (ret) { + igt_warn("failed to set mode (%dx%d@%dHz): %s\n", width, height, c->mode.vrefresh, strerror(errno)); + igt_remove_fb(drm_fd, &fb_info); + + } + + return ret; +} + +static uint32_t find_crtc_for_connector(drmModeConnector *c) +{ + drmModeEncoder *e; + uint32_t possible_crtcs; + int i, j; + + for (i = 0; i < c->count_encoders; i++) { + e = drmModeGetEncoder(drm_fd, c->encoders[i]); + possible_crtcs = e->possible_crtcs; + drmModeFreeEncoder(e); + + for (j = 0; possible_crtcs >> j; j++) + if (possible_crtcs & (1 << j)) + return resources->crtcs[j]; + } + return 0; +} + +/* + * Re-probe connectors and do a modeset based on test request or + * in case of a hotplug uevent. + * + * @mode: Video mode requested by the test + * @is_compliance_test: 1: If it is a compliance test + * 0: If it is a hotplug event + * + * Returns: + * 0: On Success + * -1: On failure + */ +int update_display(int mode, bool is_compliance_test) +{ + struct connector *connectors, *conn; + int cnt, ret = 0; + bool set_mode; + + resources = drmModeGetResources(drm_fd); + if (!resources) { + igt_warn("drmModeGetResources failed: %s\n", strerror(errno)); + return -1; + } + + connectors = calloc(resources->count_connectors, + sizeof(struct connector)); + if (!connectors) + return -1; + + /* Find any connected displays */ + for (cnt = 0; cnt < resources->count_connectors; cnt++) { + + conn = &connectors[cnt]; + drmModeConnector *c; + + conn->id = resources->connectors[cnt]; + c = drmModeGetConnector(drm_fd, conn->id); + if (c->connector_type == DRM_MODE_CONNECTOR_DisplayPort && + c->connection == DRM_MODE_CONNECTED) { + test_connector_id = c->connector_id; + conn->connector = c; + conn->crtc = find_crtc_for_connector(c); + test_crtc = conn->crtc; + set_mode = true; + break; + } else if (c->connector_id == test_connector_id && + c->connection == DRM_MODE_DISCONNECTED) { + conn->connector = c; + conn->crtc = test_crtc; + set_mode = false; + break; + } + } + + if (cnt == resources->count_connectors) { + ret = -1; + goto err; + } + + if (is_compliance_test) { + set_test_mode(conn); + ret = set_video(INTEL_MODE_NONE, conn); + ret = set_video(mode, conn); + + } else + ret = set_default_mode(conn, set_mode); + + err: + drmModeFreeConnector(conn->connector); + /* Error condition if no connected displays */ + free(connectors); + drmModeFreeResources(resources); + return ret; +} + +static const char optstr[] = "hi"; + +static void __attribute__((noreturn)) usage(char *name, char opt) +{ + igt_info("usage: %s [-hi]\n", name); + igt_info("\t-i\tdump info\n"); + igt_info("\tDefault is to respond to DPR-120 tests.\n"); + exit((opt != 'h') ? -1 : 0); +} + +static void cleanup_debugfs(void) +{ + fclose(test_active_fp); + fclose(test_data_fp); + fclose(test_type_fp); +} + +static void __attribute__((noreturn)) cleanup_and_exit(int ret) +{ + cleanup_debugfs(); + close(drm_fd); + igt_info("Compliance testing Application Exiting\n"); + exit(ret); +} + +static void cleanup_test(void) +{ + video_pattern_flag = false; + hdisplay = 0; + vdisplay = 0; + bitdepth = 0; +} + +static void read_test_request(void) +{ + unsigned long test_type; + + test_type = get_test_type(); + process_test_request(test_type); + cleanup_test(); + clear_test_active(); +} + +static gboolean test_handler(GIOChannel *source, GIOCondition condition, + gpointer data) +{ + unsigned long test_active; + + rewind(test_active_fp); + fscanf(test_active_fp, "%lx", &test_active); + if (test_active) + read_test_request(); + return TRUE; +} + +static gboolean input_event(GIOChannel *source, GIOCondition condition, + gpointer data) +{ + gchar buf[2]; + gsize count; + + count = read(g_io_channel_unix_get_fd(source), buf, sizeof(buf)); + if (buf[0] == 'q' && (count == 1 || buf[1] == '\n')) { + cleanup_and_exit(0); + } + + return TRUE; +} + +static void enter_exec_path(char **argv) +{ + char *exec_path = NULL; + char *pos = NULL; + short len_path = 0; + int ret; + + len_path = strlen(argv[0]); + exec_path = (char *) malloc(len_path); + + memcpy(exec_path, argv[0], len_path); + pos = strrchr(exec_path, '/'); + if (pos != NULL) + *(pos+1) = '\0'; + + ret = chdir(exec_path); + igt_assert_eq(ret, 0); + free(exec_path); +} + +static void restore_termio_mode(int sig) +{ + tcsetattr(tio_fd, TCSANOW, &saved_tio); + close(tio_fd); +} + +static void set_termio_mode(void) +{ + struct termios tio; + + /* don't attempt to set terminal attributes if not in the foreground + * process group + */ + if (getpgrp() != tcgetpgrp(STDOUT_FILENO)) + return; + + tio_fd = dup(STDIN_FILENO); + tcgetattr(tio_fd, &saved_tio); + igt_install_exit_handler(restore_termio_mode); + tio = saved_tio; + tio.c_lflag &= ~(ICANON | ECHO); + tcsetattr(tio_fd, TCSANOW, &tio); +} + +int main(int argc, char **argv) +{ + int c; + int ret = 0; + GIOChannel *stdinchannel, *testactive_channel; + GMainLoop *mainloop; + bool opt_dump_info = false; + struct option long_opts[] = { + {"help-description", 0, 0, HELP_DESCRIPTION}, + {"help", 0, 0, 'h'}, + }; + + igt_skip_on_simulation(); + + enter_exec_path(argv); + + while ((c = getopt_long(argc, argv, optstr, long_opts, NULL)) != -1) { + switch (c) { + case 'i': + opt_dump_info = true; + break; + case HELP_DESCRIPTION: + igt_info("DP Compliance Test Suite using DPR-120\n"); + igt_info("EDID Tests\n"); + igt_info("Video Pattern Generation Tests\n"); + exit(0); + break; + default: + /* fall through */ + case 'h': + usage(argv[0], c); + break; + } + } + + set_termio_mode(); + + igt_info("*************DP Compliance Testing using DPR-120*************\n"); + igt_info("Waiting for Test Request Input......\n"); + drm_fd = drm_open_driver(DRIVER_ANY); + + kmstest_set_vt_graphics_mode(); + setup_debugfs_files(); + cleanup_test(); + + if (opt_dump_info) { + dump_info(); + goto out_close; + } + + /* Get the DP connector ID and CRTC */ + if (update_display(0, false)) { + igt_warn("Failed to set default mode\n"); + ret = -1; + goto out_close; + } + + mainloop = g_main_loop_new(NULL, FALSE); + if (!mainloop) { + igt_warn("failed to create glib mainloop\n"); + ret = -1; + goto out_close; + } + + if (!intel_dp_compliance_setup_hotplug()) { + igt_warn("failed to initialize hotplug support\n"); + goto out_mainloop; + } + + testactive_channel = g_io_channel_unix_new(fileno(test_active_fp)); + if (!testactive_channel) { + igt_warn("failed to create Test Active GIO channel\n"); + goto out_close; + } + + ret = g_io_add_watch(testactive_channel, G_IO_IN | G_IO_ERR, test_handler, + NULL); + if (ret < 0) { + igt_warn("failed to add watch on udev GIO channel\n"); + goto out_close; + } + + stdinchannel = g_io_channel_unix_new(0); + if (!stdinchannel) { + igt_warn("failed to create stdin GIO channel\n"); + goto out_hotplug; + } + + ret = g_io_add_watch(stdinchannel, G_IO_IN | G_IO_ERR, input_event, + NULL); + if (ret < 0) { + igt_warn("failed to add watch on stdin GIO channel\n"); + goto out_stdio; + } + + ret = 0; + + g_main_loop_run(mainloop); + +out_stdio: + g_io_channel_shutdown(stdinchannel, TRUE, NULL); +out_hotplug: + intel_dp_compliance_cleanup_hotplug(); +out_mainloop: + g_main_loop_unref(mainloop); +out_close: + cleanup_debugfs(); + close(drm_fd); + + igt_assert_eq(ret, 0); + + igt_info("Compliance testing application exiting.\n"); + igt_exit(); +} diff --git a/tools/intel_dp_compliance.h b/tools/intel_dp_compliance.h new file mode 100644 index 0000000..ade1e2f --- /dev/null +++ b/tools/intel_dp_compliance.h @@ -0,0 +1,35 @@ +/* + * Copyright 2010 Intel Corporation + * Manasi Navare <manasi.d.navare@intel.com> + * + * 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 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. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <glib.h> + +extern int drm_fd; + +gboolean intel_dp_compliance_setup_hotplug(void); +void intel_dp_compliance_cleanup_hotplug(void); + +/* called by the hotplug code */ +int update_display(int mode, bool is_compliance_test); diff --git a/tools/intel_dp_compliance_hotplug.c b/tools/intel_dp_compliance_hotplug.c new file mode 100644 index 0000000..d489026 --- /dev/null +++ b/tools/intel_dp_compliance_hotplug.c @@ -0,0 +1,123 @@ +/* + * Copyright 2010 Intel Corporation + * Jesse Barnes <jesse.barnes@intel.com> + * + * 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 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. + */ + +#include "igt.h" +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <sys/stat.h> + +#include "intel_dp_compliance.h" +#include <libudev.h> +static struct udev_monitor *uevent_monitor; +static struct udev *udev; +static GIOChannel *udevchannel; + +static gboolean hotplug_event(GIOChannel *source, GIOCondition condition, + gpointer data) +{ + struct udev_device *dev; + dev_t udev_devnum; + struct stat s; + const char *hotplug; + + dev = udev_monitor_receive_device(uevent_monitor); + if (!dev) + goto out; + + udev_devnum = udev_device_get_devnum(dev); + fstat(drm_fd, &s); + + hotplug = udev_device_get_property_value(dev, "HOTPLUG"); + + if (memcmp(&s.st_rdev, &udev_devnum, sizeof(dev_t)) == 0 && + hotplug && atoi(hotplug) == 1) + update_display(0, false); + + udev_device_unref(dev); +out: + return TRUE; +} + + +gboolean intel_dp_compliance_setup_hotplug(void) +{ + int ret; + + udev = udev_new(); + if (!udev) { + igt_warn("failed to create udev object\n"); + goto out; + } + + uevent_monitor = udev_monitor_new_from_netlink(udev, "udev"); + if (!uevent_monitor) { + igt_warn("failed to create udev event monitor\n"); + goto out; + } + + ret = udev_monitor_filter_add_match_subsystem_devtype(uevent_monitor, + "drm", + "drm_minor"); + if (ret < 0) { + igt_warn("failed to filter for drm events\n"); + goto out; + } + + ret = udev_monitor_enable_receiving(uevent_monitor); + if (ret < 0) { + igt_warn("failed to enable udev event reception\n"); + goto out; + } + + udevchannel = + g_io_channel_unix_new(udev_monitor_get_fd(uevent_monitor)); + if (!udevchannel) { + igt_warn("failed to create udev GIO channel\n"); + goto out; + } + + ret = g_io_add_watch(udevchannel, G_IO_IN | G_IO_ERR, hotplug_event, + udev); + if (ret < 0) { + igt_warn("failed to add watch on udev GIO channel\n"); + goto out; + } + + return TRUE; + +out: + intel_dp_compliance_cleanup_hotplug(); + return FALSE; +} + +void intel_dp_compliance_cleanup_hotplug(void) +{ + if (udevchannel) + g_io_channel_shutdown(udevchannel, TRUE, NULL); + if (uevent_monitor) + udev_monitor_unref(uevent_monitor); + if (udev) + udev_unref(udev); +}
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 has an automation support for the Link training tests (4.3.1.1. - 4.3.2.3), EDID tests (4.2.2.3 - 4.2.2.6) and Video Pattern generation tests (4.3.3.1) from CTS specification 1.2 Rev 1.1. This tool has the support for responding to the hotplug uevents sent by compliance testting unit after each test. 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 <tprevite@gmail.com>) v2: * Add mode unset on hotplug uevent on disconnect (Manasi Navare) Cc: Petri Latvala <petri.latvala@intel.com> Cc: Marius Vlad <marius.c.vlad@intel.com> Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Signed-off-by: Manasi Navare <manasi.d.navare@intel.com> --- tools/Makefile.am | 3 +- tools/Makefile.sources | 7 + tools/intel_dp_compliance.c | 1060 +++++++++++++++++++++++++++++++++++ tools/intel_dp_compliance.h | 35 ++ tools/intel_dp_compliance_hotplug.c | 123 ++++ 5 files changed, 1227 insertions(+), 1 deletion(-) create mode 100644 tools/intel_dp_compliance.c create mode 100644 tools/intel_dp_compliance.h create mode 100644 tools/intel_dp_compliance_hotplug.c