From patchwork Wed Mar 26 04:32:42 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: akash.goel@intel.com X-Patchwork-Id: 3891971 Return-Path: X-Original-To: patchwork-intel-gfx@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 891819F2B6 for ; Wed, 26 Mar 2014 04:30:32 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 745B4201C0 for ; Wed, 26 Mar 2014 04:30:30 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id 27D85201BB for ; Wed, 26 Mar 2014 04:30:28 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 855B56E4B8; Tue, 25 Mar 2014 21:30:26 -0700 (PDT) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) by gabe.freedesktop.org (Postfix) with ESMTP id 64B2B6E4B8 for ; Tue, 25 Mar 2014 21:30:25 -0700 (PDT) Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by fmsmga102.fm.intel.com with ESMTP; 25 Mar 2014 21:30:25 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.97,733,1389772800"; d="scan'208";a="506862106" Received: from akashgoe-desktop.iind.intel.com ([10.223.82.34]) by fmsmga002.fm.intel.com with ESMTP; 25 Mar 2014 21:30:21 -0700 From: akash.goel@intel.com To: intel-gfx@lists.freedesktop.org Date: Wed, 26 Mar 2014 10:02:42 +0530 Message-Id: <1395808362-22265-1-git-send-email-akash.goel@intel.com> X-Mailer: git-send-email 1.8.5.2 In-Reply-To: <1394543531-22596-1-git-send-email-akash.goel@intel.com> References: <1394543531-22596-1-git-send-email-akash.goel@intel.com> Cc: Akash Goel Subject: [Intel-gfx] [PATCH v2] tests/kms_panel_fitter: Test to verify the 2 new drm crtc properties X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Spam-Status: No, score=-4.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Akash Goel This test is a derivative of kms_setmode. This will verify the 2 new drm crtc properties, added to control the Panel fitter's input & output. v2: Modified the setting of 'border size' property. As now the 33rd bit can be used to forcefully enable the Panel fitter. Signed-off-by: Akash Goel Tested-by: Akash Goel --- tests/Makefile.sources | 1 + tests/kms_panel_fitter.c | 1215 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1216 insertions(+) create mode 100644 tests/kms_panel_fitter.c diff --git a/tests/Makefile.sources b/tests/Makefile.sources index 88866ac..05ee06c 100644 --- a/tests/Makefile.sources +++ b/tests/Makefile.sources @@ -62,6 +62,7 @@ TESTS_progs_M = \ kms_plane \ kms_render \ kms_setmode \ + kms_panel_fitter \ pm_lpsp \ pm_pc8 \ pm_rps \ diff --git a/tests/kms_panel_fitter.c b/tests/kms_panel_fitter.c new file mode 100644 index 0000000..3134ba8 --- /dev/null +++ b/tests/kms_panel_fitter.c @@ -0,0 +1,1215 @@ +/* + * 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. + * + * Authors: + * ????? + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "drm_fourcc.h" +#include "drmtest.h" +#include "intel_bufmgr.h" +#include "intel_batchbuffer.h" +#include "intel_gpu_tools.h" +#include "igt_kms.h" +#include "igt_debugfs.h" + +#define MAX_CONNECTORS 10 +#define MAX_CRTCS 3 + +/* max combinations with repetitions */ +#define MAX_COMBINATION_COUNT \ + (MAX_CONNECTORS * MAX_CONNECTORS * MAX_CONNECTORS) +#define MAX_COMBINATION_ELEMS MAX_CRTCS + +static int drm_fd; +static drmModeRes *drm_resources; +static int filter_test_id; +static bool dry_run; + +/*const*/ drmModeModeInfo mode_640_480 = { + .name = "640x480", + .vrefresh = 60, + .clock = 25200, + + .hdisplay = 640, + .hsync_start = 656, + .hsync_end = 752, + .htotal = 800, + + .vdisplay = 480, + .vsync_start = 490, + .vsync_end = 492, + .vtotal = 525, + + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, +}; + +enum test_flags { + TEST_INVALID = 0x01, + TEST_CLONE = 0x02, + TEST_SINGLE_CRTC_CLONE = 0x04, + TEST_EXCLUSIVE_CRTC_CLONE = 0x08, + TEST_VARY_INPUT = 0x10, + TEST_VARY_BORDER = 0x20, +}; + +struct test_config { + const char *name; + enum test_flags flags; + drmModeRes *resources; +}; + +struct connector_config { + drmModeConnector *connector; + int crtc_idx; + bool connected; + drmModeModeInfo default_mode; +}; + +struct crtc_config { + int crtc_idx; + int crtc_id; + int pipe_id; + int connector_count; + struct connector_config *cconfs; + struct kmstest_fb fb_info; + drmModeModeInfo mode; +}; + +igt_debugfs_t debugfs; +igt_pipe_crc_t **pipe_crc; +static void init_crc(void) +{ + int j; + igt_debugfs_init(&debugfs); + igt_pipe_crc_check(&debugfs); + pipe_crc = calloc(2, sizeof(pipe_crc[0])); + for (j = 0; j < 2; j++) { + int crtc_idx = j; + igt_pipe_crc_t *pipe_crc_p = igt_pipe_crc_new(&debugfs, drm_fd, crtc_idx,INTEL_PIPE_CRC_SOURCE_AUTO); + if (!pipe_crc_p) { + fprintf(stdout, "auto crc not supported on this connector with crtc %i\n", + crtc_idx); + return; + } + pipe_crc[crtc_idx] = pipe_crc_p; + } +} + +static void read_crc(int crtc_idx) +{ + igt_pipe_crc_t *pipe_crc_cur = pipe_crc[crtc_idx]; + igt_crc_t *crcs = NULL; + igt_pipe_crc_start(pipe_crc_cur); + igt_pipe_crc_get_crcs(pipe_crc_cur, 1, &crcs); + igt_pipe_crc_stop(pipe_crc_cur); + fprintf(stdout, "\n Output CRCS: %u %s ", crcs[0].frame, igt_crc_to_string(&crcs[0])); + fflush(stdout); +} + +static void dump_planes( + int gfx_fd, drmModeRes *resources) +{ + drmModePlaneRes *plane_resources; + drmModePlane *ovr; + int i; + + plane_resources = drmModeGetPlaneResources(gfx_fd); + if (plane_resources == NULL) { + printf("drmModeGetPlaneResources failed: %s\n", + strerror(errno)); + return; + } + + printf("Planes:\n"); + printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\n"); + for (i = 0; i < plane_resources->count_planes; i++) { + ovr = drmModeGetPlane(gfx_fd, plane_resources->planes[i]); + if (ovr == NULL) { + printf("drmModeGetPlane failed to find overlay: %s\n", + strerror(errno)); + continue; + } + + printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%d\n", + ovr->plane_id, ovr->crtc_id, ovr->fb_id, + ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y, + ovr->gamma_size); + + drmModeFreePlane(ovr); + } + printf("\n"); + + return; +} + +static void dump_encoders ( + int gfx_fd, drmModeRes *resources) +{ + int i; + drmModeEncoder *encoder; + // Dump data on each of the display encoders + for (i = 0; i < resources->count_encoders; i++) + { + encoder = drmModeGetEncoder(gfx_fd, resources->encoders[i]); + printf("Encoder %s ...\n", kmstest_encoder_type_str(encoder->encoder_type)); + printf("\t.encoder_id = %d\n" + "\t.encoder_type = %d\n" + "\t.crtc_id = %d\n" + "\t.possible_crtcs = %d\n" + "\t.possible_clones = %d\n", + encoder->encoder_id, + encoder->encoder_type, + encoder->crtc_id, + encoder->possible_crtcs, + encoder->possible_clones); + drmModeFreeEncoder(encoder); + } +} + +static void dump_mode(drmModeModeInfo *mode) +{ + printf(" %s %d %d %d %d %d %d %d %d %d 0x%x 0x%x %d\n", + mode->name, + mode->vrefresh, + mode->hdisplay, + mode->hsync_start, + mode->hsync_end, + mode->htotal, + mode->vdisplay, + mode->vsync_start, + mode->vsync_end, + mode->vtotal, + mode->flags, + mode->type, + mode->clock); +} + +static void dump_connectors( + int gfx_fd, drmModeRes *resources) +{ + int i, j; + + printf("Connectors:\n"); + printf("id\tencoder\tstatus\t\ttype\tsize (mm)\tmodes\n"); + for (i = 0; i < resources->count_connectors; i++) { + drmModeConnector *connector; + connector = drmModeGetConnector(gfx_fd, resources->connectors[i]); + if (connector == NULL) { + printf("could not get connector %i: %s\n", + resources->connectors[i], strerror(errno)); + continue; + } + printf("%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 == 0) { + drmModeFreeConnector(connector); + continue; + } + + printf(" modes:\n"); + printf(" name refresh (Hz) hdisp hss hse htot vdisp " + "vss vse vtot flags type clock\n"); + for (j = 0; j < connector->count_modes; j++) + dump_mode(&connector->modes[j]); + + drmModeFreeConnector(connector); + } + printf("\n"); +} +static void dump_crtcs( + int gfx_fd, + drmModeRes *resources) +{ + int i; + + printf("CRTCs:\n"); + printf("id\tfb\tpos\tsize\n"); + for (i = 0; i < resources->count_crtcs; i++) { + drmModeCrtc *crtc; + crtc = drmModeGetCrtc(gfx_fd, resources->crtcs[i]); + if (crtc == NULL) { + printf("could not get crtc %i: %s\n", + resources->crtcs[i], + strerror(errno)); + continue; + } + printf("%d\t%d\t(%d,%d)\t(%dx%d)\t[Pipe %s]\n", + crtc->crtc_id, + crtc->buffer_id, + crtc->x, crtc->y, + crtc->width, crtc->height, + kmstest_pipe_str(kmstest_get_pipe_from_crtc_id(drm_fd, + crtc->crtc_id))); + dump_mode(&crtc->mode); + + drmModeFreeCrtc(crtc); + } + printf("\n"); +} + +static void dump_current_display_configuration(void) +{ + dump_crtcs(drm_fd, drm_resources); + dump_connectors(drm_fd, drm_resources); + dump_encoders(drm_fd, drm_resources); + dump_planes(drm_fd, drm_resources); +} + +static bool drm_mode_equal(drmModeModeInfo *m1, drmModeModeInfo *m2) +{ +#define COMP(x) do { if (m1->x != m2->x) return false; } while (0) + COMP(vrefresh); + COMP(clock); + COMP(hdisplay); + COMP(hsync_start); + COMP(hsync_end); + COMP(htotal); + COMP(vdisplay); + COMP(vsync_start); + COMP(vsync_end); + COMP(vtotal); + COMP(flags); + + return true; +} + +static bool connector_supports_mode(drmModeConnector *connector, + drmModeModeInfo *mode) +{ + int i; + + for (i = 0; i < connector->count_modes; i++) + if (drm_mode_equal(&connector->modes[i], mode)) + return true; + + return false; +} + +static bool crtc_supports_mode(struct crtc_config *crtc, drmModeModeInfo *mode) +{ + int i; + + for (i = 0; i < crtc->connector_count; i++) { + if (!connector_supports_mode(crtc->cconfs[i].connector, mode)) + return false; + } + + return true; +} + +static int paint_fb(struct kmstest_fb *fb, const char *test_name, + const char **crtc_str, int crtc_count, int current_crtc_idx) +{ + double x, y; + cairo_t *cr; + int i; + + cr = kmstest_get_cairo_ctx(drm_fd, fb); + + kmstest_paint_test_pattern(cr, fb->width, fb->height); + + cairo_select_font_face(cr, "Helvetica", CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL); + cairo_move_to(cr, fb->width / 2, fb->height / 2); + cairo_set_font_size(cr, 20); + kmstest_cairo_printf_line(cr, align_hcenter, 40, "%s", test_name); + + cairo_get_current_point(cr, &x, &y); + cairo_move_to(cr, 60, y); + + for (i = 0; i < crtc_count; i++) { + if (i == current_crtc_idx) { + cairo_get_current_point(cr, &x, &y); + cairo_move_to(cr, x - 10, y); + kmstest_cairo_printf_line(cr, align_right, 10, "X"); + cairo_move_to(cr, x, y); + } + kmstest_cairo_printf_line(cr, align_left, 20, "%s", + crtc_str[i]); + } + + cairo_destroy(cr); + + return 0; +} + +static void create_fb_for_crtc(struct crtc_config *crtc, + struct kmstest_fb *fb_info, int hdisplay, int vdisplay) +{ + int bpp; + int depth; + bool enable_tiling; + int fb_id; + + bpp = 32; + depth = 24; + enable_tiling = false; + + fb_id = kmstest_create_fb(drm_fd, hdisplay, vdisplay, bpp, depth, + enable_tiling, fb_info); + assert(fb_id > 0); +} + +static drmModePropertyPtr get_input_size_prop(int crtc_id) +{ + uint32_t k; + drmModeObjectPropertiesPtr props = NULL; + drmModePropertyPtr prop = NULL; + + props = drmModeObjectGetProperties(drm_fd, crtc_id, DRM_MODE_OBJECT_CRTC); + fprintf(stdout, "CRTC %s (id: %x), Count_props=%d\n", + kmstest_connector_type_str(crtc_id),crtc_id,props->count_props); + for (k = 0; k < props->count_props; k++) { + prop = drmModeGetProperty(drm_fd, props->props[k]); + fprintf(stdout, "Prop->name=%s\n", prop->name); + if (strcmp(prop->name, "input size") == 0) { + fprintf(stdout, "input size prop found\n"); + igt_assert(prop->flags & DRM_MODE_PROP_RANGE); + break; + } else + prop = NULL; + } + + if (props) { + drmFree(props->props); + drmFree(props->prop_values); + } + + drmFree(props); + return prop; +} + +static drmModePropertyPtr get_border_size_prop(int crtc_id) +{ + uint32_t k; + drmModeObjectPropertiesPtr props = NULL; + drmModePropertyPtr prop = NULL; + + props = drmModeObjectGetProperties(drm_fd, crtc_id, DRM_MODE_OBJECT_CRTC); + fprintf(stdout, "CRTC %s (id: %x), Count_props=%d\n", + kmstest_connector_type_str(crtc_id),crtc_id,props->count_props); + for (k = 0; k < props->count_props; k++) { + prop = drmModeGetProperty(drm_fd, props->props[k]); + fprintf(stdout, "Prop->name=%s\n", prop->name); + if (strcmp(prop->name, "border size") == 0) { + fprintf(stdout, "border size prop found\n"); + igt_assert(prop->flags & DRM_MODE_PROP_RANGE); + break; + } else + prop = NULL; + } + + if (props) { + drmFree(props->props); + drmFree(props->prop_values); + } + + drmFree(props); + return prop; +} + +#define MAX_NUM_CASES 10 +static int border[MAX_NUM_CASES][2] = {{64,60}, {128,120}, {192,180}, {256,240}, {320,300}}; +#define FORCE_PFIT_ENABLE (((uint64_t)1) << 32) + +static void test_output_borders(struct crtc_config *crtc, const char *test_name, + const char** crtc_str, int crtc_count, int current_crtc_idx) +{ + int q; + drmModePropertyPtr border_size_prop; + int ret = 0; + + border_size_prop = get_border_size_prop(crtc->crtc_id); + if (!border_size_prop) + return; + + for(q=0; qcrtc_id, DRM_MODE_OBJECT_CRTC, + (uint32_t)border_size_prop->prop_id, + (FORCE_PFIT_ENABLE | (border[q][0] << 16) | border[q][1])); + + if (ret) { + fprintf(stdout, "border size setting for (%dx%d) failed with retcode %x !!!\n", + border[q][0], border[q][1], ret); + break; + } + + sleep(5); + } + + drmFree(border_size_prop); +} + +static int res[MAX_NUM_CASES][2] = {{640,480}, {720,540}, {800,600}, {960,720}, {1024,768}, {1200,900}, {1440,1080}}; + +static void test_page_flips(struct crtc_config *crtc, const char *test_name, + const char** crtc_str, int crtc_count, int current_crtc_idx) +{ + int q; + struct kmstest_fb fb_info[MAX_NUM_CASES]; + drmModePropertyPtr input_size_prop; + drmModePropertyPtr border_size_prop; + int ret = 0; + + input_size_prop = get_input_size_prop(crtc->crtc_id); + if (!input_size_prop) + return; + + border_size_prop = get_border_size_prop(crtc->crtc_id); + if (!border_size_prop) { + fprintf(stdout, "border size property not found\n"); + return; + } + + /* + * Before starting the page flip test, we need to enable + * Panel fitter, so that we can test the flipping of + * frame buffers of different resolution. By default + * Panel fitter remains disabled as initially modeset + * is done as per the native resolution only. + * Forcefully enable the Panel fitter through the border + * size property. + */ + ret = drmModeObjectSetProperty(drm_fd, crtc->crtc_id, DRM_MODE_OBJECT_CRTC, + (uint32_t)border_size_prop->prop_id, FORCE_PFIT_ENABLE); + if (ret) { + fprintf(stdout, "border size setting failed with retcode %x !!!\n", + ret); + } + sleep(5); + + for(q=0; qpipe_id); + ret = drmModeObjectSetProperty(drm_fd, crtc->crtc_id, DRM_MODE_OBJECT_CRTC, + (uint32_t)input_size_prop->prop_id, + ((res[q][0] << 16) | res[q][1])); + + if (ret) { + fprintf(stdout, "Input size setting for fb(%dx%d) failed with retcode %x !!!\n", + res[q][0], res[q][1], ret); + break; + } + + ret = drmModePageFlip(drm_fd, crtc->crtc_id, fb_info[q].fb_id, 0, NULL); + + if (ret < 0) { + fprintf(stdout, "Page flip for fb(%dx%d) failed, CRTC[%d][Pipe %s] returned %d\n", + res[q][0],res[q][1], crtc->crtc_id, + kmstest_pipe_str(crtc->pipe_id), ret); + fflush(stdout); + break; + } + + sleep(5); + + if(q>=1) + drmModeRmFB(drm_fd, fb_info[q-1].fb_id); + } + + drmModeRmFB(drm_fd, fb_info[q-1].fb_id); + drmFree(input_size_prop); + drmFree(border_size_prop); +} + +static void get_mode_for_crtc(struct crtc_config *crtc, + drmModeModeInfo *mode_ret) +{ + drmModeModeInfo mode; + int i; + + /* + * First try to select a default mode that is supported by all + * connectors. + */ + for (i = 0; i < crtc->connector_count; i++) { + mode = crtc->cconfs[i].default_mode; + if (crtc_supports_mode(crtc, &mode)) + goto found; + } + + /* + * Then just fall back to find any that is supported by all + * connectors. + */ + for (i = 0; i < crtc->cconfs[0].connector->count_modes; i++) { + mode = crtc->cconfs[0].connector->modes[i]; + if (crtc_supports_mode(crtc, &mode)) + goto found; + } + + /* + * If none is found then just pick the default mode of the first + * connector and hope the other connectors can support it by scaling + * etc. + */ + mode = crtc->cconfs[0].default_mode; +found: + *mode_ret = mode; +} + +static int get_encoder_idx(drmModeRes *resources, drmModeEncoder *encoder) +{ + int i; + + for (i = 0; i < resources->count_encoders; i++) + if (resources->encoders[i] == encoder->encoder_id) + return i; + assert(0); +} + +static void get_crtc_config_str(struct crtc_config *crtc, char *buf, + size_t buf_size) +{ + int pos; + int i; + + pos = snprintf(buf, buf_size, + "CRTC[%d][Pipe %s] Mode:%s@%dHz Connectors: ", + crtc->crtc_id, kmstest_pipe_str(crtc->pipe_id), + crtc->mode.name, crtc->mode.vrefresh); + if (pos > buf_size) + return; + for (i = 0; i < crtc->connector_count; i++) { + drmModeConnector *connector = crtc->cconfs[i].connector; + + pos += snprintf(&buf[pos], buf_size - pos, + "%s%s-%d[%d]%s", i ? ", " : "", + kmstest_connector_type_str(connector->connector_type), + connector->connector_type_id, connector->connector_id, + crtc->cconfs[i].connected ? "" : " (NC)"); + if (pos > buf_size) + return; + } +} + +static void setup_crtcs(drmModeRes *resources, struct connector_config *cconf, + int connector_count, struct crtc_config *crtcs, + int *crtc_count_ret, bool *config_valid_ret) +{ + struct crtc_config *crtc; + int crtc_count; + bool config_valid; + int i; + int encoder_usage_count[resources->count_encoders]; + + i = 0; + crtc_count = 0; + crtc = crtcs; + config_valid = true; + + while (i < connector_count) { + drmModeCrtc *drm_crtc; + unsigned long encoder_mask; + int j; + + igt_assert(crtc_count < MAX_CRTCS); + + crtc->crtc_idx = cconf[i].crtc_idx; + drm_crtc = drmModeGetCrtc(drm_fd, + resources->crtcs[crtc->crtc_idx]); + crtc->crtc_id = drm_crtc->crtc_id; + drmModeFreeCrtc(drm_crtc); + crtc->pipe_id = kmstest_get_pipe_from_crtc_id(drm_fd, + crtc->crtc_id); + + crtc->connector_count = 1; + for (j = i + 1; j < connector_count; j++) + if (cconf[j].crtc_idx == crtc->crtc_idx) + crtc->connector_count++; + + crtc->cconfs = malloc(sizeof(*crtc->cconfs) * + crtc->connector_count); + assert(crtc->cconfs); + + encoder_mask = 0; + for (j = 0; j < crtc->connector_count; j++) { + drmModeConnector *connector; + drmModeEncoder *encoder; + + crtc->cconfs[j] = cconf[i + j]; + connector = cconf[i + j].connector; + + /* Intel connectors have only a single encoder */ + igt_assert(connector->count_encoders == 1); + encoder = drmModeGetEncoder(drm_fd, + connector->encoders[0]); + assert(encoder); + + config_valid &= !!(encoder->possible_crtcs & + (1 << crtc->crtc_idx)); + + if (config_valid == 0) { + fprintf(stdout, "invalid : connector %s, encoder %s, possible_crtcs %x, crtc_idx %x\n", + kmstest_connector_type_str(connector->connector_type), + kmstest_encoder_type_str(encoder->encoder_type), + encoder->possible_crtcs, crtc->crtc_idx); + } + + encoder_mask |= 1 << get_encoder_idx(resources, + encoder); + config_valid &= !(encoder_mask & + ~encoder->possible_clones); + + if (config_valid == 0) { + fprintf(stdout, "invalid : connector %s, encoder %s, possible_clones %x, encoder_mask %x\n", + kmstest_connector_type_str(connector->connector_type), + kmstest_encoder_type_str(encoder->encoder_type), + (unsigned int)encoder->possible_clones, (unsigned int)encoder_mask); + } + + drmModeFreeEncoder(encoder); + } + get_mode_for_crtc(crtc, &crtc->mode); + create_fb_for_crtc(crtc, &crtc->fb_info, crtc->mode.hdisplay, crtc->mode.vdisplay); + + i += crtc->connector_count; + crtc_count++; + crtc++; + } + + memset(encoder_usage_count, 0, sizeof(encoder_usage_count)); + for (i = 0; i < connector_count; i++) { + drmModeConnector *connector = cconf[i].connector; + drmModeEncoder *encoder; + + igt_assert(connector->count_encoders == 1); + encoder = drmModeGetEncoder(drm_fd, connector->encoders[0]); + encoder_usage_count[get_encoder_idx(resources, encoder)]++; + + if (encoder_usage_count[i] > 1) { + fprintf(stdout, "invalid : connector %s, encoder %s, encoder_usage_count %x\n", + kmstest_connector_type_str(connector->connector_type), + kmstest_encoder_type_str(encoder->encoder_type), + encoder_usage_count[get_encoder_idx(resources, encoder)]); + } + drmModeFreeEncoder(encoder); + } + for (i = 0; i < resources->count_encoders; i++) + if (encoder_usage_count[i] > 1) + config_valid = false; + + *crtc_count_ret = crtc_count; + *config_valid_ret = config_valid; +} + +static void cleanup_crtcs(struct crtc_config *crtcs, int crtc_count) +{ + int i; + + for (i = 0; i < crtc_count; i++) { + free(crtcs[i].cconfs); + } +} + +static uint32_t *get_connector_ids(struct crtc_config *crtc) +{ + uint32_t *ids; + int i; + + ids = malloc(sizeof(*ids) * crtc->connector_count); + assert(ids); + for (i = 0; i < crtc->connector_count; i++) + ids[i] = crtc->cconfs[i].connector->connector_id; + + return ids; +} + +static void force_modeset(struct crtc_config *crtc, const char *test_name, + const char** crtc_str, int crtc_count, int current_crtc_idx) +{ + struct kmstest_fb fb_info; + uint32_t *ids; + int ret = 0; + + create_fb_for_crtc(crtc, &fb_info, mode_640_480.hdisplay, mode_640_480.vdisplay); + paint_fb(&fb_info, test_name, crtc_str, crtc_count, current_crtc_idx); + + ids = get_connector_ids(crtc); + + /* + * Do a dummy modeset with a 640x480 fb to forcefully enable + * the Panel fitter. This is effective only for fixed mode + * panels like edp + */ + ret = drmModeSetCrtc(drm_fd, crtc->crtc_id, + fb_info.fb_id, 0, 0, ids, + crtc->connector_count, &mode_640_480); + + fprintf(stdout, "Dummy ModeSet CRTC[%d][Pipe %s] returned %d\n", + crtc->crtc_id, kmstest_pipe_str(crtc->pipe_id), ret); + fflush(stdout); + if (ret < 0) + igt_assert(errno == EINVAL); + + sleep(5); + + free(ids); +} + +static void test_crtc_config(const struct test_config *tconf, + struct crtc_config *crtcs, int crtc_count) +{ + char str_buf[MAX_CRTCS][1024]; + const char *crtc_strs[MAX_CRTCS]; + struct crtc_config *crtc; + static int test_id; + bool connector_connected = false; + int ret = 0; + int i; + test_id++; + + if (filter_test_id && filter_test_id != test_id) + return; + + printf(" Test id#%d CRTC count %d\n", test_id, crtc_count); + + for (i = 0; i < crtc_count; i++) { + get_crtc_config_str(&crtcs[i], str_buf[i], sizeof(str_buf[i])); + crtc_strs[i] = &str_buf[i][0]; + } + + if (dry_run) { + for (i = 0; i < crtc_count; i++) + printf(" %s\n", crtc_strs[i]); + return; + } + + for (i = 0; i < crtc_count; i++) { + uint32_t *ids; + int j; + + crtc = &crtcs[i]; + printf(" %s\n", crtc_strs[i]); + + /* + * Only do the test on a crtc which at least have one connector + * in a connected state, otherwise skip + */ + for (j = 0; j < crtc->connector_count; j++) + if(crtc->cconfs[j].connected) + break; + if (j == crtc->connector_count) { + fprintf(stdout, "No connector connected, Skipping ModeSet for CRTC[%d][Pipe %s] \n", + crtc->crtc_id, kmstest_pipe_str(crtc->pipe_id)); + fflush(stdout); + continue; + } + + create_fb_for_crtc(crtc, &crtc->fb_info, crtc->mode.hdisplay, crtc->mode.vdisplay); + paint_fb(&crtc->fb_info, tconf->name, crtc_strs, crtc_count, i); + + ids = get_connector_ids(crtc); + ret = drmModeSetCrtc(drm_fd, crtc->crtc_id, + crtc->fb_info.fb_id, 0, 0, ids, + crtc->connector_count, &crtc->mode); + + fprintf(stdout, "ModeSet CRTC[%d][Pipe %s] returned %d\n", + crtc->crtc_id, kmstest_pipe_str(crtc->pipe_id), ret); + fflush(stdout); + + if (ret < 0) + igt_assert(errno == EINVAL); + + sleep(10); + + /*read_crc(crtc->pipe_id);*/ + + if (tconf->flags & TEST_VARY_INPUT) { + test_page_flips(crtc, tconf->name, crtc_strs, crtc_count, i); + } else if (tconf->flags & TEST_VARY_BORDER) { + /* Two cases to be checked when applying the borders */ + + /* + * First check for downscaling actions by panel fitter, as + * applying borders on native resolution will lead to downscaling + */ + test_output_borders(crtc, tconf->name, crtc_strs, crtc_count, i); + + /* + * Check upscaling action by Panel fitter when applying + * borders. For that do a modeset with a lower resolution + * (640x480) buffer. But this action will work on fixed + * mode panels only. For panels like HDMI, the Pipe + * timings is set as per the User's resolution only on + * modeset. And for varying the borders, Driver has to + * internally do a modeset, so pipe timings always gets + * set as per the User resolution. Hence when border is + * applied, it actually leads to downscaling at + * Pipe level. + */ + force_modeset(crtc, tconf->name, crtc_strs, crtc_count, i); + test_output_borders(crtc, tconf->name, crtc_strs, crtc_count, i); + } + + free(ids); + } + + if (ret == 0 && connector_connected) + sleep(5); + + for (i = 0; i < crtc_count; i++) { + if (crtcs[i].fb_info.fb_id) { + drmModeSetCrtc(drm_fd, crtcs[i].crtc_id, 0, 0, 0, NULL, + 0, NULL); + drmModeRmFB(drm_fd, crtcs[i].fb_info.fb_id); + crtcs[i].fb_info.fb_id = 0; + } + } + + return; +} + +static void test_one_combination(const struct test_config *tconf, + struct connector_config *cconfs, + int connector_count) +{ + struct crtc_config crtcs[MAX_CRTCS]; + int crtc_count; + bool config_valid; + + setup_crtcs(tconf->resources, cconfs, connector_count, crtcs, + &crtc_count, &config_valid); + + if (config_valid == !(tconf->flags & TEST_INVALID)) + test_crtc_config(tconf, crtcs, crtc_count); + + cleanup_crtcs(crtcs, crtc_count); +} + +static int assign_crtc_to_connectors(const struct test_config *tconf, + int *crtc_idxs, int connector_count, + struct connector_config *cconfs) +{ + unsigned long crtc_idx_mask; + int i; + + crtc_idx_mask = 0; + for (i = 0; i < connector_count; i++) { + int crtc_idx = crtc_idxs[i]; + + if ((tconf->flags & TEST_SINGLE_CRTC_CLONE) && + crtc_idx_mask & ~(1 << crtc_idx)) + return -1; + + if ((tconf->flags & TEST_EXCLUSIVE_CRTC_CLONE) && + crtc_idx_mask & (1 << crtc_idx)) + return -1; + + crtc_idx_mask |= 1 << crtc_idx; + + cconfs[i].crtc_idx = crtc_idx; + } + + return 0; +} + +static int get_one_connector(drmModeRes *resources, int connector_id, + struct connector_config *cconf) +{ + drmModeConnector *connector; + drmModeModeInfo mode; + + connector = drmModeGetConnector(drm_fd, connector_id); + assert(connector); + cconf->connector = connector; + + cconf->connected = connector->connection == DRM_MODE_CONNECTED; + + /* + * For DP/eDP we need a connected sink, since mode setting depends + * on successful link training and retrieved DPCD parameters. + */ + switch (connector->connector_type) { + case DRM_MODE_CONNECTOR_DisplayPort: + case DRM_MODE_CONNECTOR_eDP: + if (!cconf->connected) { + drmModeFreeConnector(connector); + return -1; + } + } + + if (cconf->connected) { + if (kmstest_get_connector_default_mode(drm_fd, connector, + &mode) < 0) + mode = mode_640_480; + } else { + mode = mode_640_480; + } + + cconf->default_mode = mode; + + return 0; +} + +static int get_connectors(drmModeRes *resources, int *connector_idxs, + int connector_count, struct connector_config *cconfs) +{ + int i; + + for (i = 0; i < connector_count; i++) { + int connector_idx; + int connector_id; + + connector_idx = connector_idxs[i]; + assert(connector_idx < resources->count_connectors); + connector_id = resources->connectors[connector_idx]; + + if (get_one_connector(resources, connector_id, &cconfs[i]) < 0) + goto err; + + } + + return 0; + +err: + while (i--) + drmModeFreeConnector(cconfs[i].connector); + + return -1; +} + +static void free_connectors(struct connector_config *cconfs, + int connector_count) +{ + int i; + + for (i = 0; i < connector_count; i++) + drmModeFreeConnector(cconfs[i].connector); +} + +struct combination { + int elems[MAX_COMBINATION_ELEMS];/*ex:(planeA->edp,planeB->mipi,planeC->hdmi),(planeA->mipi,planeB->edp,planeC->hdmi) etc*/ +}; + +struct combination_set { + int count; + struct combination items[MAX_COMBINATION_COUNT];/*all possible combinations generated as above comment*/ +}; + +/* + * Get all possible selection of k elements from n elements with or without + * repetitions. + */ +static void iterate_combinations(int n, int k, bool allow_repetitions, + int depth, int base, struct combination *comb, + struct combination_set *set) +{ + int v; + + if (!k) { + assert(set->count < ARRAY_SIZE(set->items)); + set->items[set->count++] = *comb; + return; + } + + for (v = base; v < n; v++) { + comb->elems[depth] = v; + iterate_combinations(n, k - 1, allow_repetitions, + depth + 1, allow_repetitions ? 0 : v + 1, + comb, set); + } + +} + +static void get_combinations(int n, int k, bool allow_repetitions, + struct combination_set *set) +{ + struct combination comb; + + assert(k <= ARRAY_SIZE(set->items[0].elems)); + set->count = 0; + iterate_combinations(n, k, allow_repetitions, 0, 0, &comb, set); +} + +static void test_combinations(const struct test_config *tconf, + int connector_count) +{ + struct combination_set connector_combs; + struct combination_set crtc_combs; + struct connector_config *cconfs; + int i; + + get_combinations(tconf->resources->count_connectors, connector_count, + false, &connector_combs); + get_combinations(tconf->resources->count_crtcs, connector_count, + true, &crtc_combs); + + printf("Testing: %s %d connector combinations\n", tconf->name, + connector_count); + for (i = 0; i < connector_combs.count; i++) { + int *connector_idxs; + int ret; + int j; + + cconfs = malloc(sizeof(*cconfs) * connector_count); + assert(cconfs); + + connector_idxs = &connector_combs.items[i].elems[0]; + ret = get_connectors(tconf->resources, connector_idxs, + connector_count, cconfs); + if (ret < 0) + goto free_cconfs; + + for (j = 0; j < crtc_combs.count; j++) { + int *crtc_idxs = &crtc_combs.items[j].elems[0]; + ret = assign_crtc_to_connectors(tconf, crtc_idxs, + connector_count, + cconfs); + if (ret < 0) + continue; + + test_one_combination(tconf, cconfs, connector_count); + } + + free_connectors(cconfs, connector_count); +free_cconfs: + free(cconfs); + } +} + +static void run_test(const struct test_config *tconf) +{ + int connector_num; + + connector_num = tconf->flags & TEST_CLONE ? 2 : 1; + for (; connector_num <= tconf->resources->count_crtcs; connector_num++) + test_combinations(tconf, connector_num); +} + +static int opt_handler(int opt, int opt_index) +{ + switch (opt) { + case 'd': + dry_run = true; + break; + case 't': + filter_test_id = atoi(optarg); + break; + default: + assert(0); + } + + return 0; +} + +int main(int argc, char **argv) +{ + const struct { + enum test_flags flags; + const char *name; + } tests[] = { + { TEST_CLONE | TEST_EXCLUSIVE_CRTC_CLONE | TEST_VARY_INPUT, + "clone-exclusive-crtc-with-varying-input" }, + { TEST_CLONE | TEST_EXCLUSIVE_CRTC_CLONE | TEST_VARY_BORDER, + "clone-exclusive-crtc-with-varying-output" }, + }; + const char *help_str = + " -d\t\tDon't run any test, only print what would be done. (still needs DRM access)\n" + " -t \tRun only the test with this id."; + int i; + int ret; + + ret = igt_subtest_init_parse_opts(argc, argv, "dt:", NULL, help_str, + opt_handler); + if (ret < 0) + return ret == -1 ? 0 : ret; + + igt_skip_on_simulation(); + + if (dry_run && filter_test_id) { + fprintf(stderr, "only one of -d and -t is accepted\n"); + exit(1); + } + + igt_fixture { + drm_fd = drm_open_any(); + if (!dry_run) + igt_set_vt_graphics_mode(); + + drm_resources = drmModeGetResources(drm_fd); + assert(drm_resources); + } + + if (dry_run) + dump_current_display_configuration(); + + fprintf(stdout, "Tests starting\n"); + fflush(stdout); + + init_crc(); + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + igt_subtest(tests[i].name) { + struct test_config tconf = { + .flags = tests[i].flags, + .name = tests[i].name, + .resources = drm_resources, + }; + run_test(&tconf); + } + } + + fprintf(stdout, "All tests completed\n"); + fflush(stdout); + + igt_fixture { + drmModeFreeResources(drm_resources); + + close(drm_fd); + } + + fprintf(stdout, "Test about to exit\n"); + fflush(stdout); + + igt_exit(); + + fprintf(stdout, "End of main\n"); + fflush(stdout); +}