From: Akash Goel <akash.g...@intel.com>

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 <akash.g...@intel.com>
Tested-by: Akash Goel <akash.g...@intel.com>
---
 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:
+ *   ????? <????@intel.com>
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <cairo.h>
+#include <errno.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/time.h>
+
+#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; q<MAX_NUM_CASES; q++){
+               if ((border[q][0] == 0) || (border[q][1] == 0))
+                       break;
+
+               ret = drmModeObjectSetProperty(drm_fd, crtc->crtc_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; q<MAX_NUM_CASES; q++){
+               if ((res[q][0] == 0) || (res[q][1] == 0))
+                       break;
+
+               create_fb_for_crtc(crtc, &fb_info[q], res[q][0], res[q][1]);
+               paint_fb(&fb_info[q], test_name, crtc_str, crtc_count, 
current_crtc_idx);
+
+               /* Wait for a Vblank before updating the Pipesrc, so that both
+                  Pipesrc update & subsequent page flip queueing can be 
performed
+                  before the next vblank */
+               igt_wait_for_vblank(drm_fd, crtc->pipe_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 <test id>\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);
+}
-- 
1.8.5.2

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to