/* * Copyright © 2017 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 * () * * * 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 * * Elements of the modeset code adapted from David Herrmann's * DRM modeset example * */ #include "igt.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "intel_dp_compliance.h" #include #include /* 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 FILE *fopenat(int dir, const char *name, const char *mode) { int fd = openat(dir, name, O_RDWR); return fdopen(fd, mode); } static void setup_debugfs_files(void) { int dir = igt_debugfs_dir(drm_fd); test_type_fp = fopenat(dir, INTEL_DP_TEST_TYPE_FILE, "r"); igt_require(test_type_fp); test_data_fp = fopenat(dir, INTEL_DP_TEST_DATA_FILE, "r"); igt_require(test_data_fp); test_active_fp = fopenat(dir, INTEL_DP_TEST_ACTIVE_FILE, "w+"); igt_require(test_active_fp); close(dir); /* Reset the active flag for safety */ clear_test_active(); } static unsigned long get_test_type(void) { unsigned long test_type; int ret; if (!test_type_fp) fprintf(stderr, "Invalid test_type file\n"); rewind(test_type_fp); ret = fscanf(test_type_fp, "%lx", &test_type); if (ret < 1 || test_type <= 0) { igt_warn("test_type read failed - %lx\n", test_type); return 0; } return test_type; } static unsigned long get_test_edid_data(void) { unsigned long test_data; int ret; if (!test_data_fp) fprintf(stderr, "Invalid test_data file\r\n"); rewind(test_data_fp); ret = fscanf(test_data_fp, "%lx", &test_data); if (ret < 1 || 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]; int ret; if (!test_data_fp) fprintf(stderr, "Invalid test_data file\n"); rewind(test_data_fp); while (!feof(test_data_fp) && count < 3) { ret = fscanf(test_data_fp, "%s %u\n", video_pattern_attribute, (unsigned int *)&video_pattern_value[count++]); if (ret < 2) { igt_warn("test_data read failed\n"); return; } } hdisplay = video_pattern_value[0]; vdisplay = video_pattern_value[1]; bitdepth = video_pattern_value[2]; igt_info("Hdisplay = %d\n", hdisplay); igt_info("Vdisplay = %d\n", vdisplay); igt_info("BitDepth = %u\n", bitdepth); } static int process_test_request(int test_type) { int mode; unsigned long test_data_edid; bool valid = false; switch (test_type) { case DP_TEST_LINK_VIDEO_PATTERN: video_pattern_flag = true; get_test_videopattern_data(); mode = INTEL_MODE_VIDEO_PATTERN_TEST; valid = true; 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; valid = true; break; default: /* Unknown test type */ fprintf(stderr, "Invalid test request, ignored.\n"); break; } if (valid) return update_display(mode, true); return -1; } 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; mode_resources = drmModeGetResources(drmfd); if (!mode_resources) { igt_warn("drmModeGetResources failed: %s\n", strerror(errno)); return; } 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); if (dp_conn->pixmap == NULL) return -1; 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); if (dp_conn->failsafe_pixmap == NULL) return -1; 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); if (dp_conn->test_pattern.pixmap == NULL) return -1; 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\n", c->connector_id); return -ENOENT; } igt_info("Connector setup:\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\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\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\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("Creating framebuffer for connector %u failed (%d)\n", c->connector_id, ret); return ret; } if (found_fs) { ret = setup_failsafe_framebuffer(dp_conn); if (ret) { igt_warn("Creating failsafe framebuffer for connector %u failed (%d)\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("Creating framebuffer for connector %u failed (%d)\n", c->connector_id, ret); return ret; } ret = fill_framebuffer(dp_conn); if (ret) { igt_warn("Filling framebuffer for connector %u failed (%d)\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\n"); ret = drmModeSetCrtc(drm_fd, test_connector->crtc, -1, 0, 0, NULL, 0, NULL); goto out; case INTEL_MODE_PREFERRED: igt_info("PREFERRED\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\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\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\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!\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 1 sec for DPR-120 to detect it */ sleep(1); out: if (ret) { igt_warn("Failed to set CRTC for connector %u\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++) { drmModeConnector *c; conn = &connectors[cnt]; 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; int ret; rewind(test_active_fp); ret = fscanf(test_active_fp, "%lx", &test_active); if (ret < 1) return FALSE; 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(); 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 GMainLoop\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 GIOChannel\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 GIOChannel\n"); goto out_close; } stdinchannel = g_io_channel_unix_new(0); if (!stdinchannel) { igt_warn("Failed to create stdin GIOChannel\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 GIOChannel\n"); goto out_stdio; } ret = 0; igt_info("*************DP Compliance Testing using DPR-120*************\n"); igt_info("Waiting for test request......\n"); 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(); }