2 * Permission is hereby granted, free of charge, to any person obtaining a
3 * copy of this software and associated documentation files (the "Software"),
4 * to deal in the Software without restriction, including without limitation
5 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
6 * and/or sell copies of the Software, and to permit persons to whom the
7 * Software is furnished to do so, subject to the following conditions:
9 * The above copyright notice and this permission notice shall be included in
10 * all copies or substantial portions of the Software.
12 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
17 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * Imre Deak <imre.deak@intel.com>
35 #include "drm_fourcc.h"
37 #include "intel_bufmgr.h"
38 #include "intel_batchbuffer.h"
42 #define MAX_CONNECTORS 10
45 /* max combinations with repetitions */
46 #define MAX_COMBINATION_COUNT \
47 (MAX_CONNECTORS * MAX_CONNECTORS * MAX_CONNECTORS)
48 #define MAX_COMBINATION_ELEMS MAX_CRTCS
51 static drmModeRes *drm_resources;
52 static int filter_test_id;
55 const drmModeModeInfo mode_640_480 = {
70 .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
76 TEST_SINGLE_CRTC_CLONE = 0x04,
77 TEST_EXCLUSIVE_CRTC_CLONE = 0x08,
82 enum test_flags flags;
83 drmModeRes *resources;
86 struct connector_config {
87 drmModeConnector *connector;
90 drmModeModeInfo default_mode;
98 struct connector_config *cconfs;
99 struct kmstest_fb fb_info;
100 drmModeModeInfo mode;
103 static bool drm_mode_equal(drmModeModeInfo *m1, drmModeModeInfo *m2)
105 #define COMP(x) do { if (m1->x != m2->x) return false; } while (0)
121 static bool connector_supports_mode(drmModeConnector *connector,
122 drmModeModeInfo *mode)
126 for (i = 0; i < connector->count_modes; i++)
127 if (drm_mode_equal(&connector->modes[i], mode))
133 static bool crtc_supports_mode(struct crtc_config *crtc, drmModeModeInfo *mode)
137 for (i = 0; i < crtc->connector_count; i++) {
138 if (!connector_supports_mode(crtc->cconfs[i].connector, mode))
145 static int paint_fb(struct kmstest_fb *fb, const char *test_name,
146 const char **crtc_str, int crtc_count, int current_crtc_idx)
152 cr = kmstest_get_cairo_ctx(drm_fd, fb);
154 kmstest_paint_test_pattern(cr, fb->width, fb->height);
156 cairo_select_font_face(cr, "Helvetica", CAIRO_FONT_SLANT_NORMAL,
157 CAIRO_FONT_WEIGHT_NORMAL);
158 cairo_move_to(cr, fb->width / 2, fb->height / 2);
159 cairo_set_font_size(cr, 24);
160 kmstest_cairo_printf_line(cr, align_hcenter, 40, "%s", test_name);
162 cairo_get_current_point(cr, &x, &y);
163 cairo_move_to(cr, 60, y);
165 for (i = 0; i < crtc_count; i++) {
166 if (i == current_crtc_idx) {
167 cairo_get_current_point(cr, &x, &y);
168 cairo_move_to(cr, x - 20, y);
169 kmstest_cairo_printf_line(cr, align_right, 20, "X");
170 cairo_move_to(cr, x, y);
172 kmstest_cairo_printf_line(cr, align_left, 20, "%s",
181 static void create_fb_for_crtc(struct crtc_config *crtc,
182 struct kmstest_fb *fb_info)
191 enable_tiling = false;
192 fb_id = kmstest_create_fb(drm_fd, crtc->mode.hdisplay,
193 crtc->mode.vdisplay, bpp, depth,
194 enable_tiling, fb_info);
195 igt_assert(fb_id > 0);
198 static void get_mode_for_crtc(struct crtc_config *crtc,
199 drmModeModeInfo *mode_ret)
201 drmModeModeInfo mode;
205 * First try to select a default mode that is supported by all
208 for (i = 0; i < crtc->connector_count; i++) {
209 mode = crtc->cconfs[i].default_mode;
210 if (crtc_supports_mode(crtc, &mode))
215 * Then just fall back to find any that is supported by all
218 for (i = 0; i < crtc->cconfs[0].connector->count_modes; i++) {
219 mode = crtc->cconfs[0].connector->modes[i];
220 if (crtc_supports_mode(crtc, &mode))
225 * If none is found then just pick the default mode of the first
226 * connector and hope the other connectors can support it by scaling
229 mode = crtc->cconfs[0].default_mode;
234 static int get_encoder_idx(drmModeRes *resources, drmModeEncoder *encoder)
238 for (i = 0; i < resources->count_encoders; i++)
239 if (resources->encoders[i] == encoder->encoder_id)
244 static void get_crtc_config_str(struct crtc_config *crtc, char *buf,
250 pos = snprintf(buf, buf_size,
251 "CRTC[%d] [Pipe %s] Mode: %s@%dHz Connectors: ",
252 crtc->crtc_id, kmstest_pipe_str(crtc->pipe_id),
253 crtc->mode.name, crtc->mode.vrefresh);
256 for (i = 0; i < crtc->connector_count; i++) {
257 drmModeConnector *connector = crtc->cconfs[i].connector;
259 pos += snprintf(&buf[pos], buf_size - pos,
260 "%s%s-%d[%d]%s", i ? ", " : "",
261 kmstest_connector_type_str(connector->connector_type),
262 connector->connector_type_id, connector->connector_id,
263 crtc->cconfs[i].connected ? "" : " (NC)");
269 static void setup_crtcs(drmModeRes *resources, struct connector_config *cconf,
270 int connector_count, struct crtc_config *crtcs,
271 int *crtc_count_ret, bool *config_valid_ret)
273 struct crtc_config *crtc;
277 int encoder_usage_count[resources->count_encoders];
284 while (i < connector_count) {
285 drmModeCrtc *drm_crtc;
286 unsigned long encoder_mask;
289 igt_assert(crtc_count < MAX_CRTCS);
291 crtc->crtc_idx = cconf[i].crtc_idx;
292 drm_crtc = drmModeGetCrtc(drm_fd,
293 resources->crtcs[crtc->crtc_idx]);
294 crtc->crtc_id = drm_crtc->crtc_id;
295 drmModeFreeCrtc(drm_crtc);
296 crtc->pipe_id = kmstest_get_pipe_from_crtc_id(drm_fd,
299 crtc->connector_count = 1;
300 for (j = i + 1; j < connector_count; j++)
301 if (cconf[j].crtc_idx == crtc->crtc_idx)
302 crtc->connector_count++;
304 crtc->cconfs = malloc(sizeof(*crtc->cconfs) *
305 crtc->connector_count);
306 igt_assert(crtc->cconfs);
309 for (j = 0; j < crtc->connector_count; j++) {
310 drmModeConnector *connector;
311 drmModeEncoder *encoder;
313 crtc->cconfs[j] = cconf[i + j];
314 connector = cconf[i + j].connector;
316 /* Intel connectors have only a single encoder */
317 igt_assert(connector->count_encoders == 1);
318 encoder = drmModeGetEncoder(drm_fd,
319 connector->encoders[0]);
322 config_valid &= !!(encoder->possible_crtcs &
323 (1 << crtc->crtc_idx));
325 encoder_mask |= 1 << get_encoder_idx(resources,
327 config_valid &= !(encoder_mask &
328 ~encoder->possible_clones);
330 drmModeFreeEncoder(encoder);
332 get_mode_for_crtc(crtc, &crtc->mode);
333 create_fb_for_crtc(crtc, &crtc->fb_info);
335 i += crtc->connector_count;
340 memset(encoder_usage_count, 0, sizeof(encoder_usage_count));
341 for (i = 0; i < connector_count; i++) {
342 drmModeConnector *connector = cconf[i].connector;
343 drmModeEncoder *encoder;
345 igt_assert(connector->count_encoders == 1);
346 encoder = drmModeGetEncoder(drm_fd, connector->encoders[0]);
347 encoder_usage_count[get_encoder_idx(resources, encoder)]++;
348 drmModeFreeEncoder(encoder);
350 for (i = 0; i < resources->count_encoders; i++)
351 if (encoder_usage_count[i] > 1)
352 config_valid = false;
354 *crtc_count_ret = crtc_count;
355 *config_valid_ret = config_valid;
358 static void cleanup_crtcs(struct crtc_config *crtcs, int crtc_count)
362 for (i = 0; i < crtc_count; i++) {
363 free(crtcs[i].cconfs);
367 static uint32_t *get_connector_ids(struct crtc_config *crtc)
372 ids = malloc(sizeof(*ids) * crtc->connector_count);
374 for (i = 0; i < crtc->connector_count; i++)
375 ids[i] = crtc->cconfs[i].connector->connector_id;
380 static void test_crtc_config(const struct test_config *tconf,
381 struct crtc_config *crtcs, int crtc_count)
383 char str_buf[MAX_CRTCS][1024];
384 const char *crtc_strs[MAX_CRTCS];
385 struct crtc_config *crtc;
387 bool config_failed = false;
388 bool connector_connected = false;
394 if (filter_test_id && filter_test_id != test_id)
397 printf(" Test id#%d CRTC count %d\n", test_id, crtc_count);
399 for (i = 0; i < crtc_count; i++) {
400 get_crtc_config_str(&crtcs[i], str_buf[i], sizeof(str_buf[i]));
401 crtc_strs[i] = &str_buf[i][0];
405 for (i = 0; i < crtc_count; i++)
406 printf(" %s\n", crtc_strs[i]);
410 for (i = 0; i < crtc_count; i++) {
416 printf(" %s\n", crtc_strs[i]);
418 create_fb_for_crtc(crtc, &crtc->fb_info);
419 paint_fb(&crtc->fb_info, tconf->name, crtc_strs, crtc_count, i);
421 ids = get_connector_ids(crtc);
422 ret = drmModeSetCrtc(drm_fd, crtc->crtc_id,
423 crtc->fb_info.fb_id, 0, 0, ids,
424 crtc->connector_count, &crtc->mode);
428 igt_assert(errno == EINVAL);
429 config_failed = true;
432 for (j = 0; j < crtc->connector_count; j++)
433 connector_connected |= crtc->cconfs[j].connected;
436 igt_assert(config_failed == !!(tconf->flags & TEST_INVALID));
438 if (ret == 0 && connector_connected && !(tconf->flags & TEST_INVALID))
441 for (i = 0; i < crtc_count; i++) {
442 if (crtcs[i].fb_info.fb_id) {
443 drmModeSetCrtc(drm_fd, crtcs[i].crtc_id, 0, 0, 0, NULL,
445 drmModeRmFB(drm_fd, crtcs[i].fb_info.fb_id);
446 crtcs[i].fb_info.fb_id = 0;
453 static void test_one_combination(const struct test_config *tconf,
454 struct connector_config *cconfs,
457 struct crtc_config crtcs[MAX_CRTCS];
461 setup_crtcs(tconf->resources, cconfs, connector_count, crtcs,
462 &crtc_count, &config_valid);
464 if (config_valid == !(tconf->flags & TEST_INVALID))
465 test_crtc_config(tconf, crtcs, crtc_count);
467 cleanup_crtcs(crtcs, crtc_count);
470 static int assign_crtc_to_connectors(const struct test_config *tconf,
471 int *crtc_idxs, int connector_count,
472 struct connector_config *cconfs)
474 unsigned long crtc_idx_mask;
478 for (i = 0; i < connector_count; i++) {
479 int crtc_idx = crtc_idxs[i];
481 if ((tconf->flags & TEST_SINGLE_CRTC_CLONE) &&
482 crtc_idx_mask & ~(1 << crtc_idx))
485 if ((tconf->flags & TEST_EXCLUSIVE_CRTC_CLONE) &&
486 crtc_idx_mask & (1 << crtc_idx))
489 crtc_idx_mask |= 1 << crtc_idx;
491 cconfs[i].crtc_idx = crtc_idx;
497 static int get_one_connector(drmModeRes *resources, int connector_id,
498 struct connector_config *cconf)
500 drmModeConnector *connector;
501 drmModeModeInfo mode;
503 connector = drmModeGetConnector(drm_fd, connector_id);
504 igt_assert(connector);
505 cconf->connector = connector;
507 cconf->connected = connector->connection == DRM_MODE_CONNECTED;
510 * For DP/eDP we need a connected sink, since mode setting depends
511 * on successful link training and retrieved DPCD parameters.
513 switch (connector->connector_type) {
514 case DRM_MODE_CONNECTOR_DisplayPort:
515 case DRM_MODE_CONNECTOR_eDP:
516 if (!cconf->connected) {
517 drmModeFreeConnector(connector);
522 if (cconf->connected) {
523 if (kmstest_get_connector_default_mode(drm_fd, connector,
530 cconf->default_mode = mode;
535 static int get_connectors(drmModeRes *resources, int *connector_idxs,
536 int connector_count, struct connector_config *cconfs)
540 for (i = 0; i < connector_count; i++) {
544 connector_idx = connector_idxs[i];
545 igt_assert(connector_idx < resources->count_connectors);
546 connector_id = resources->connectors[connector_idx];
548 if (get_one_connector(resources, connector_id, &cconfs[i]) < 0)
557 drmModeFreeConnector(cconfs[i].connector);
562 static void free_connectors(struct connector_config *cconfs,
567 for (i = 0; i < connector_count; i++)
568 drmModeFreeConnector(cconfs[i].connector);
572 int elems[MAX_COMBINATION_ELEMS];
575 struct combination_set {
577 struct combination items[MAX_COMBINATION_COUNT];
581 * Get all possible selection of k elements from n elements with or without
584 static void iterate_combinations(int n, int k, bool allow_repetitions,
585 int depth, int base, struct combination *comb,
586 struct combination_set *set)
591 igt_assert(set->count < ARRAY_SIZE(set->items));
592 set->items[set->count++] = *comb;
596 for (v = base; v < n; v++) {
597 comb->elems[depth] = v;
598 iterate_combinations(n, k - 1, allow_repetitions,
599 depth + 1, allow_repetitions ? 0 : v + 1,
605 static void get_combinations(int n, int k, bool allow_repetitions,
606 struct combination_set *set)
608 struct combination comb;
610 igt_assert(k <= ARRAY_SIZE(set->items[0].elems));
612 iterate_combinations(n, k, allow_repetitions, 0, 0, &comb, set);
615 static void test_combinations(const struct test_config *tconf,
618 struct combination_set connector_combs;
619 struct combination_set crtc_combs;
620 struct connector_config *cconfs;
623 get_combinations(tconf->resources->count_connectors, connector_count,
624 false, &connector_combs);
625 get_combinations(tconf->resources->count_crtcs, connector_count,
628 printf("Testing: %s %d connector combinations\n", tconf->name,
630 for (i = 0; i < connector_combs.count; i++) {
635 cconfs = malloc(sizeof(*cconfs) * connector_count);
638 connector_idxs = &connector_combs.items[i].elems[0];
639 ret = get_connectors(tconf->resources, connector_idxs,
640 connector_count, cconfs);
644 for (j = 0; j < crtc_combs.count; j++) {
645 int *crtc_idxs = &crtc_combs.items[j].elems[0];
646 ret = assign_crtc_to_connectors(tconf, crtc_idxs,
652 test_one_combination(tconf, cconfs, connector_count);
655 free_connectors(cconfs, connector_count);
661 static void run_test(const struct test_config *tconf)
665 connector_num = tconf->flags & TEST_CLONE ? 2 : 1;
666 for (; connector_num <= tconf->resources->count_crtcs; connector_num++)
667 test_combinations(tconf, connector_num);
670 static int opt_handler(int opt, int opt_index)
677 filter_test_id = atoi(optarg);
686 int main(int argc, char **argv)
689 enum test_flags flags;
692 { TEST_CLONE | TEST_SINGLE_CRTC_CLONE,
693 "clone-single-crtc" },
694 { TEST_INVALID | TEST_CLONE | TEST_SINGLE_CRTC_CLONE,
695 "invalid-clone-single-crtc" },
696 { TEST_INVALID | TEST_CLONE | TEST_EXCLUSIVE_CRTC_CLONE,
697 "invalid-clone-exclusive-crtc" },
698 { TEST_CLONE | TEST_EXCLUSIVE_CRTC_CLONE,
699 "clone-exclusive-crtc" },
701 const char *help_str =
702 " -d\t\tDon't run any test, only print what would be done. (still needs DRM access)\n"
703 " -t <test id>\tRun only the test with this id.";
707 ret = igt_subtest_init_parse_opts(argc, argv, "dt:", NULL, help_str,
710 return ret == -1 ? 0 : ret;
712 igt_skip_on_simulation();
714 if (dry_run && filter_test_id) {
715 fprintf(stderr, "only one of -d and -t is accepted\n");
720 drm_fd = drm_open_any();
722 igt_set_vt_graphics_mode();
724 drm_resources = drmModeGetResources(drm_fd);
725 igt_assert(drm_resources);
728 for (i = 0; i < ARRAY_SIZE(tests); i++) {
729 igt_subtest(tests[i].name) {
730 struct test_config tconf = {
731 .flags = tests[i].flags,
732 .name = tests[i].name,
733 .resources = drm_resources,
740 drmModeFreeResources(drm_resources);