• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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:
8  *
9  * The above copyright notice and this permission notice shall be included in
10  * all copies or substantial portions of the Software.
11  *
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
18  * IN THE SOFTWARE.
19  *
20  * Authors:
21  *    Imre Deak <imre.deak@intel.com>
22  */
23 #include "config.h"
24 
25 #include "igt.h"
26 #include <cairo.h>
27 #include <errno.h>
28 #include <stdint.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <sys/time.h>
32 #include <math.h>
33 #include "intel_bufmgr.h"
34 
35 #define MAX_CONNECTORS  10
36 #define MAX_CRTCS       6
37 
38 /* max combinations with repetitions */
39 #define MAX_COMBINATION_ELEMS   MAX_CRTCS
40 
41 static int drm_fd;
42 static drmModeRes *drm_resources;
43 static int filter_test_id;
44 static bool dry_run;
45 
46 const drmModeModeInfo mode_640_480 = {
47 	.name		= "640x480",
48 	.vrefresh	= 60,
49 	.clock		= 25200,
50 
51 	.hdisplay	= 640,
52 	.hsync_start	= 656,
53 	.hsync_end	= 752,
54 	.htotal		= 800,
55 
56 	.vdisplay	= 480,
57 	.vsync_start	= 490,
58 	.vsync_end	= 492,
59 	.vtotal		= 525,
60 
61 	.flags		= DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
62 };
63 
64 enum test_flags {
65 	TEST_INVALID			= 0x01,
66 	TEST_CLONE			= 0x02,
67 	TEST_SINGLE_CRTC_CLONE		= 0x04,
68 	TEST_EXCLUSIVE_CRTC_CLONE	= 0x08,
69 	TEST_STEALING			= 0x10,
70 	TEST_TIMINGS			= 0x20,
71 };
72 
73 struct test_config {
74 	const char *name;
75 	enum test_flags flags;
76 	drmModeRes *resources;
77 };
78 
79 struct connector_config {
80 	drmModeConnector *connector;
81 	int crtc_idx;
82 	drmModeModeInfo default_mode;
83 };
84 
85 struct crtc_config {
86 	int crtc_idx;
87 	int crtc_id;
88 	int pipe_id;
89 	int connector_count;
90 	struct connector_config *cconfs;
91 	struct igt_fb fb_info;
92 	drmModeModeInfo mode;
93 };
94 
drm_mode_equal(drmModeModeInfo * m1,drmModeModeInfo * m2)95 static bool drm_mode_equal(drmModeModeInfo *m1, drmModeModeInfo *m2)
96 {
97 #define COMP(x) do { if (m1->x != m2->x) return false; } while (0)
98 	COMP(vrefresh);
99 	COMP(clock);
100 	COMP(hdisplay);
101 	COMP(hsync_start);
102 	COMP(hsync_end);
103 	COMP(htotal);
104 	COMP(vdisplay);
105 	COMP(vsync_start);
106 	COMP(vsync_end);
107 	COMP(vtotal);
108 	COMP(flags);
109 
110 	return true;
111 }
112 
connector_supports_mode(drmModeConnector * connector,drmModeModeInfo * mode)113 static bool connector_supports_mode(drmModeConnector *connector,
114 				    drmModeModeInfo *mode)
115 {
116 	int i;
117 
118 	for (i = 0; i < connector->count_modes; i++)
119 		if (drm_mode_equal(&connector->modes[i], mode))
120 			return true;
121 
122 	return false;
123 }
124 
crtc_supports_mode(struct crtc_config * crtc,drmModeModeInfo * mode)125 static bool crtc_supports_mode(struct crtc_config *crtc, drmModeModeInfo *mode)
126 {
127 	int i;
128 
129 	for (i = 0; i < crtc->connector_count; i++) {
130 		if (!connector_supports_mode(crtc->cconfs[i].connector, mode))
131 			return false;
132 	}
133 
134 	return true;
135 }
136 
paint_fb(struct igt_fb * fb,const char * test_name,const char ** crtc_str,int crtc_count,int current_crtc_idx)137 static int paint_fb(struct igt_fb *fb, const char *test_name,
138 		    const char **crtc_str, int crtc_count, int current_crtc_idx)
139 {
140 	double x, y;
141 	cairo_t *cr;
142 	int i;
143 
144 	cr = igt_get_cairo_ctx(drm_fd, fb);
145 
146 	cairo_move_to(cr, fb->width / 2, fb->height / 2);
147 	cairo_set_font_size(cr, 24);
148 	igt_cairo_printf_line(cr, align_hcenter, 40, "%s", test_name);
149 
150 	cairo_get_current_point(cr, &x, &y);
151 	cairo_move_to(cr, 60, y);
152 
153 	for (i = 0; i < crtc_count; i++) {
154 		if (i == current_crtc_idx) {
155 			cairo_get_current_point(cr, &x, &y);
156 			cairo_move_to(cr, x - 20, y);
157 			igt_cairo_printf_line(cr, align_right, 20, "X");
158 			cairo_move_to(cr, x, y);
159 		}
160 		igt_cairo_printf_line(cr, align_left, 20, "%s",
161 					  crtc_str[i]);
162 	}
163 
164 	igt_put_cairo_ctx(drm_fd, fb, cr);
165 
166 	return 0;
167 }
168 
create_fb_for_crtc(struct crtc_config * crtc,struct igt_fb * fb_info)169 static void create_fb_for_crtc(struct crtc_config *crtc,
170 			       struct igt_fb *fb_info)
171 {
172 	int bpp;
173 	int depth;
174 	int fb_id;
175 
176 	bpp = 32;
177 	depth = 24;
178 	fb_id = igt_create_pattern_fb(drm_fd, crtc->mode.hdisplay,
179 				      crtc->mode.vdisplay,
180 				      igt_bpp_depth_to_drm_format(bpp, depth),
181 				      LOCAL_DRM_FORMAT_MOD_NONE,
182 				      fb_info);
183 	igt_assert_lt(0, fb_id);
184 }
185 
get_mode_for_crtc(struct crtc_config * crtc,drmModeModeInfo * mode_ret)186 static void get_mode_for_crtc(struct crtc_config *crtc,
187 			      drmModeModeInfo *mode_ret)
188 {
189 	drmModeModeInfo *mode;
190 	int i;
191 
192 	/*
193 	 * First try to select a default mode that is supported by all
194 	 * connectors.
195 	 */
196 	for (i = 0; i < crtc->connector_count; i++) {
197 		mode = &crtc->cconfs[i].default_mode;
198 		if (crtc_supports_mode(crtc, mode))
199 			goto found;
200 	}
201 
202 	/*
203 	 * Then just fall back to find any that is supported by all
204 	 * connectors.
205 	 */
206 	for (i = 0; i < crtc->cconfs[0].connector->count_modes; i++) {
207 		mode = &crtc->cconfs[0].connector->modes[i];
208 		if (crtc_supports_mode(crtc, mode))
209 			goto found;
210 	}
211 
212 	/*
213 	 * If none is found then just pick the default mode from all connectors
214 	 * with the smallest clock, hope the other connectors can support it by
215 	 * scaling etc.
216 	 */
217 	mode = &crtc->cconfs[0].default_mode;
218 	for (i = 1; i < crtc->connector_count; i++)
219 		if (crtc->cconfs[i].default_mode.clock < mode->clock)
220 			mode = &crtc->cconfs[i].default_mode;
221 found:
222 	*mode_ret = *mode;
223 }
224 
get_encoder_idx(drmModeRes * resources,drmModeEncoder * encoder)225 static int get_encoder_idx(drmModeRes *resources, drmModeEncoder *encoder)
226 {
227 	int i;
228 
229 	for (i = 0; i < resources->count_encoders; i++)
230 		if (resources->encoders[i] == encoder->encoder_id)
231 			return i;
232 	igt_assert(0);
233 }
234 
get_crtc_config_str(struct crtc_config * crtc,char * buf,size_t buf_size)235 static void get_crtc_config_str(struct crtc_config *crtc, char *buf,
236 				size_t buf_size)
237 {
238 	int pos;
239 	int i;
240 
241 	pos = snprintf(buf, buf_size,
242 		       "CRTC[%d] [Pipe %s] Mode: %s@%dHz Connectors: ",
243 		       crtc->crtc_id, kmstest_pipe_name(crtc->pipe_id),
244 		       crtc->mode.name, crtc->mode.vrefresh);
245 	if (pos > buf_size)
246 		return;
247 	for (i = 0; i < crtc->connector_count; i++) {
248 		drmModeConnector *connector = crtc->cconfs[i].connector;
249 
250 		pos += snprintf(&buf[pos], buf_size - pos,
251 			"%s%s-%d[%d]", i ? ", " : "",
252 			kmstest_connector_type_str(connector->connector_type),
253 			connector->connector_type_id, connector->connector_id);
254 		if (pos > buf_size)
255 			return;
256 	}
257 }
258 
setup_crtcs(drmModeRes * resources,struct connector_config * cconf,int connector_count,struct crtc_config * crtcs,int * crtc_count_ret,bool * config_valid_ret)259 static void setup_crtcs(drmModeRes *resources, struct connector_config *cconf,
260 			int connector_count, struct crtc_config *crtcs,
261 			int *crtc_count_ret, bool *config_valid_ret)
262 {
263 	struct crtc_config *crtc;
264 	int crtc_count;
265 	bool config_valid;
266 	int i;
267 	int encoder_usage_count[resources->count_encoders];
268 
269 	kmstest_unset_all_crtcs(drm_fd, resources);
270 
271 	i = 0;
272 	crtc_count = 0;
273 	crtc = crtcs;
274 	config_valid = true;
275 
276 	while (i < connector_count) {
277 		drmModeCrtc *drm_crtc;
278 		unsigned long encoder_mask;
279 		int j;
280 
281 		igt_assert_lt(crtc_count, MAX_CRTCS);
282 
283 		crtc->crtc_idx = cconf[i].crtc_idx;
284 		drm_crtc = drmModeGetCrtc(drm_fd,
285 					  resources->crtcs[crtc->crtc_idx]);
286 		crtc->crtc_id = drm_crtc->crtc_id;
287 		drmModeFreeCrtc(drm_crtc);
288 		crtc->pipe_id = kmstest_get_pipe_from_crtc_id(drm_fd,
289 							      crtc->crtc_id);
290 
291 		crtc->connector_count = 1;
292 		for (j = i + 1; j < connector_count; j++)
293 			if (cconf[j].crtc_idx == crtc->crtc_idx)
294 				crtc->connector_count++;
295 
296 		crtc->cconfs = malloc(sizeof(*crtc->cconfs) *
297 				      crtc->connector_count);
298 		igt_assert(crtc->cconfs);
299 
300 		encoder_mask = 0;
301 		for (j = 0; j < crtc->connector_count; j++) {
302 			drmModeConnector *connector;
303 			drmModeEncoder *encoder;
304 
305 			crtc->cconfs[j] = cconf[i + j];
306 			connector = cconf[i + j].connector;
307 
308 			/* Intel connectors have only a single encoder */
309 			if (connector->count_encoders == 1) {
310 				encoder = drmModeGetEncoder(drm_fd,
311 							    connector->encoders[0]);
312 			} else {
313 				igt_assert_eq(connector->connector_type,
314 					      DRM_MODE_CONNECTOR_DisplayPort);
315 
316 				igt_assert(connector->count_encoders >= crtc->crtc_idx);
317 				encoder = drmModeGetEncoder(drm_fd,
318 					connector->encoders[crtc_count]);
319 			}
320 			igt_assert(encoder);
321 
322 			config_valid &= !!(encoder->possible_crtcs &
323 					  (1 << crtc->crtc_idx));
324 
325 			encoder_mask |= 1 << get_encoder_idx(resources,
326 							     encoder);
327 			config_valid &= !(encoder_mask &
328 					  ~encoder->possible_clones);
329 
330 			drmModeFreeEncoder(encoder);
331 		}
332 		get_mode_for_crtc(crtc, &crtc->mode);
333 		create_fb_for_crtc(crtc, &crtc->fb_info);
334 
335 		i += crtc->connector_count;
336 		crtc_count++;
337 		crtc++;
338 	}
339 
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;
344 		int idx = 0;
345 
346 		/* DP MST configs are presumed valid */
347 		if (connector->count_encoders > 1)
348 			idx = cconf[i].crtc_idx;
349 
350 		encoder = drmModeGetEncoder(drm_fd, connector->encoders[idx]);
351 		encoder_usage_count[get_encoder_idx(resources, encoder)]++;
352 		drmModeFreeEncoder(encoder);
353 	}
354 	for (i = 0; i < resources->count_encoders; i++)
355 		if (encoder_usage_count[i] > 1)
356 			config_valid = false;
357 
358 	*crtc_count_ret = crtc_count;
359 	*config_valid_ret = config_valid;
360 }
361 
cleanup_crtcs(struct crtc_config * crtcs,int crtc_count)362 static void cleanup_crtcs(struct crtc_config *crtcs, int crtc_count)
363 {
364 	int i;
365 
366 	for (i = 0; i < crtc_count; i++) {
367 		igt_remove_fb(drm_fd, &crtcs[i].fb_info);
368 		drmModeSetCrtc(drm_fd, crtcs[i].crtc_id, 0, 0, 0, NULL, 0, NULL);
369 
370 		free(crtcs[i].cconfs);
371 	}
372 }
373 
get_connector_ids(struct crtc_config * crtc)374 static uint32_t *get_connector_ids(struct crtc_config *crtc)
375 {
376 	uint32_t *ids;
377 	int i;
378 
379 	ids = malloc(sizeof(*ids) * crtc->connector_count);
380 	igt_assert(ids);
381 	for (i = 0; i < crtc->connector_count; i++)
382 		ids[i] = crtc->cconfs[i].connector->connector_id;
383 
384 	return ids;
385 }
386 
test_stealing(int fd,struct crtc_config * crtc,uint32_t * ids)387 static int test_stealing(int fd, struct crtc_config *crtc, uint32_t *ids)
388 {
389 	int i, ret = 0;
390 
391 	if (!crtc->connector_count)
392 		return drmModeSetCrtc(fd, crtc->crtc_id,
393 				     crtc->fb_info.fb_id, 0, 0,
394 				     ids, crtc->connector_count, &crtc->mode);
395 
396 	for (i = 0; i < crtc->connector_count; ++i) {
397 		ret = drmModeSetCrtc(fd, crtc->crtc_id,
398 				     crtc->fb_info.fb_id, 0, 0,
399 				     &ids[i], 1, &crtc->mode);
400 
401 		igt_assert_eq(ret, 0);
402 
403 		ret = drmModeSetCrtc(fd, crtc->crtc_id,
404 				     crtc->fb_info.fb_id, 0, 0,
405 				     ids, crtc->connector_count, &crtc->mode);
406 
407 		/* This should fail with -EINVAL */
408 		if (!ret)
409 			return 0;
410 	}
411 
412 	return ret;
413 }
414 
frame_time(const drmModeModeInfo * kmode)415 static double frame_time(const drmModeModeInfo *kmode)
416 {
417 	return 1000.0 * kmode->htotal * kmode->vtotal / kmode->clock;
418 }
419 
line_time(const drmModeModeInfo * kmode)420 static double line_time(const drmModeModeInfo *kmode)
421 {
422 	return 1000.0 * kmode->htotal / kmode->clock;
423 }
424 
check_timings(int crtc_idx,const drmModeModeInfo * kmode)425 static void check_timings(int crtc_idx, const drmModeModeInfo *kmode)
426 {
427 #define CALIBRATE_TS_STEPS 120 /* ~2s has to be less than 128! */
428 	drmVBlank wait;
429 	igt_stats_t stats;
430 	uint32_t last_seq;
431 	uint64_t last_timestamp;
432 	double expected;
433 	double accuracy;
434 	double mean;
435 	double stddev;
436 	int n;
437 
438 	memset(&wait, 0, sizeof(wait));
439 	wait.request.type = kmstest_get_vbl_flag(crtc_idx);
440 	wait.request.type |= DRM_VBLANK_RELATIVE | DRM_VBLANK_NEXTONMISS;
441 	do_or_die(drmWaitVBlank(drm_fd, &wait));
442 
443 	last_seq = wait.reply.sequence;
444 	last_timestamp = wait.reply.tval_sec;
445 	last_timestamp *= 1000000;
446 	last_timestamp += wait.reply.tval_usec;
447 
448 	memset(&wait, 0, sizeof(wait));
449 	wait.request.type = kmstest_get_vbl_flag(crtc_idx);
450 	wait.request.type |= DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
451 	wait.request.sequence = last_seq;
452 	for (n = 0; n < CALIBRATE_TS_STEPS; n++) {
453 		drmVBlank check = {};
454 
455 		++wait.request.sequence;
456 		do_or_die(drmWaitVBlank(drm_fd, &wait));
457 
458 		/* Double check that haven't already missed the vblank */
459 		check.request.type = kmstest_get_vbl_flag(crtc_idx);
460 		check.request.type |= DRM_VBLANK_RELATIVE;
461 		do_or_die(drmWaitVBlank(drm_fd, &check));
462 
463 		igt_assert(!igt_vblank_after(check.reply.sequence, wait.request.sequence));
464 	}
465 
466 	igt_stats_init_with_size(&stats, CALIBRATE_TS_STEPS);
467 	for (n = 0; n < CALIBRATE_TS_STEPS; n++) {
468 		struct drm_event_vblank ev;
469 		uint64_t now;
470 
471 		igt_assert(read(drm_fd, &ev, sizeof(ev)) == sizeof(ev));
472 		igt_assert_eq(ev.sequence, last_seq + 1);
473 
474 		now = ev.tv_sec;
475 		now *= 1000000;
476 		now += ev.tv_usec;
477 
478 		igt_stats_push(&stats, now - last_timestamp);
479 
480 		last_timestamp = now;
481 		last_seq = ev.sequence;
482 	}
483 
484 	expected = frame_time(kmode);
485 
486 	mean = igt_stats_get_mean(&stats);
487 	stddev = igt_stats_get_std_deviation(&stats);
488 
489 	/* 99.7% samples fall within `accuracy` on both sides of mean in normal
490 	 * distribution if `accuracy = 3 * sigma`.
491 	 * https://en.wikipedia.org/wiki/68%E2%80%9395%E2%80%9399.7_rule
492 	 *
493 	 * The value of 99.7% was chosen to suit requirements of test cases
494 	 * which depend on timing, giving the lowest acceptable MTBF of 5.6s
495 	 * for 60Hz sampling rate.
496 	 */
497 	accuracy = 3. * stddev;
498 
499 	igt_info("Expected frametime: %.0fus; measured %.1fus +- %.3fus accuracy %.2f%% [%.2f scanlines]\n",
500 		 expected, mean, stddev,
501 		 100 * accuracy / mean, accuracy / line_time(kmode));
502 
503 	/* 99.7% samples within one scanline on each side of mean */
504 	igt_assert_f(accuracy < line_time(kmode),
505 		     "vblank accuracy (%.3fus, %.1f%%) worse than a scanline (%.3fus)\n",
506 		     accuracy, 100 * accuracy / mean, line_time(kmode));
507 
508 	/* At least 90% of frame times fall within the one scanline on each
509 	 * side of expected mean.
510 	 *
511 	 * Expected scanline duration:
512 	 * 	(expected - accuracy, expected + accuracy).
513 	 * Assuming maximum difference allowed:
514 	 * 	expected = mean + n * sigma
515 	 * the scanline duration becomes:
516 	 * 	(mean - accuracy + n * sigma, mean + accuracy + n * sigma)
517 	 * The expected scanline captures the following number of samples
518 	 * from each side of expected:
519 	 * 	(erf(abs(-(accuracy/sigma) + n) / sqrt(2))
520 	 * 	+ erf((accuracy/sigma) + n) / sqrt(2))) / 2
521 	 * 	= samples
522 	 *
523 	 * Solving for samples = 0.9:
524 	 * 	n = 1.718
525 	 *
526 	 * See:
527 	 * https://en.wikipedia.org/wiki/Standard_deviation#Rules_for_normally_distributed_data
528 	 */
529 	igt_assert_f(fabs(mean - expected) < 1.718 * stddev,
530 		     "vblank interval differs from modeline! expected %.1fus, measured %1.fus +- %.3fus, difference %.1fus (%.1f sigma)\n",
531 		     expected, mean, stddev,
532 		     fabs(mean - expected), fabs(mean - expected) / stddev);
533 }
534 
test_crtc_config(const struct test_config * tconf,struct crtc_config * crtcs,int crtc_count)535 static void test_crtc_config(const struct test_config *tconf,
536 			     struct crtc_config *crtcs, int crtc_count)
537 {
538 	char str_buf[MAX_CRTCS][1024];
539 	const char *crtc_strs[MAX_CRTCS];
540 	struct crtc_config *crtc;
541 	static int test_id;
542 	bool config_failed = false;
543 	int ret = 0;
544 	int i;
545 
546 	test_id++;
547 
548 	if (filter_test_id && filter_test_id != test_id)
549 		return;
550 
551 	igt_info("  Test id#%d CRTC count %d\n", test_id, crtc_count);
552 
553 	for (i = 0; i < crtc_count; i++) {
554 		get_crtc_config_str(&crtcs[i], str_buf[i], sizeof(str_buf[i]));
555 		crtc_strs[i] = &str_buf[i][0];
556 	}
557 
558 	if (dry_run) {
559 		for (i = 0; i < crtc_count; i++)
560 			igt_info("    %s\n", crtc_strs[i]);
561 		return;
562 	}
563 
564 	for (i = 0; i < crtc_count; i++) {
565 		uint32_t *ids;
566 
567 		crtc = &crtcs[i];
568 
569 		igt_info("    %s\n", crtc_strs[i]);
570 
571 		create_fb_for_crtc(crtc, &crtc->fb_info);
572 		paint_fb(&crtc->fb_info, tconf->name, crtc_strs, crtc_count, i);
573 
574 		ids = get_connector_ids(crtc);
575 		if (tconf->flags & TEST_STEALING)
576 			ret = test_stealing(drm_fd, crtc, ids);
577 		else
578 			ret = drmModeSetCrtc(drm_fd, crtc->crtc_id,
579 					     crtc->fb_info.fb_id, 0, 0, ids,
580 					     crtc->connector_count, &crtc->mode);
581 
582 		free(ids);
583 
584 		if (ret < 0) {
585 			igt_assert_eq(errno, EINVAL);
586 			config_failed = true;
587 		}
588 	}
589 
590 	igt_assert(config_failed == !!(tconf->flags & TEST_INVALID));
591 
592 	if (ret == 0 && tconf->flags & TEST_TIMINGS)
593 		check_timings(crtcs[0].crtc_idx, &crtcs[0].mode);
594 
595 	return;
596 }
597 
test_one_combination(const struct test_config * tconf,struct connector_config * cconfs,int connector_count)598 static void test_one_combination(const struct test_config *tconf,
599 				 struct connector_config *cconfs,
600 				 int connector_count)
601 {
602 	struct crtc_config crtcs[MAX_CRTCS];
603 	int crtc_count;
604 	bool config_valid;
605 
606 	setup_crtcs(tconf->resources, cconfs, connector_count, crtcs,
607 		    &crtc_count, &config_valid);
608 
609 	if (config_valid == !(tconf->flags & TEST_INVALID))
610 		test_crtc_config(tconf, crtcs, crtc_count);
611 
612 	cleanup_crtcs(crtcs, crtc_count);
613 }
614 
assign_crtc_to_connectors(const struct test_config * tconf,int * crtc_idxs,int connector_count,struct connector_config * cconfs)615 static int assign_crtc_to_connectors(const struct test_config *tconf,
616 				     int *crtc_idxs, int connector_count,
617 				     struct connector_config *cconfs)
618 {
619 	unsigned long crtc_idx_mask;
620 	int i;
621 
622 	crtc_idx_mask = 0;
623 	for (i = 0; i < connector_count; i++) {
624 		int crtc_idx = crtc_idxs[i];
625 
626 		if ((tconf->flags & TEST_SINGLE_CRTC_CLONE) &&
627 		    crtc_idx_mask & ~(1 << crtc_idx))
628 			return -1;
629 
630 		if ((tconf->flags & TEST_EXCLUSIVE_CRTC_CLONE) &&
631 		    crtc_idx_mask & (1 << crtc_idx))
632 			return -1;
633 
634 		crtc_idx_mask |= 1 << crtc_idx;
635 
636 		cconfs[i].crtc_idx = crtc_idx;
637 	}
638 
639 	return 0;
640 }
641 
get_one_connector(drmModeRes * resources,int connector_id,struct connector_config * cconf)642 static int get_one_connector(drmModeRes *resources, int connector_id,
643 			     struct connector_config *cconf)
644 {
645 	drmModeConnector *connector;
646 
647 	connector = drmModeGetConnectorCurrent(drm_fd, connector_id);
648 	igt_assert(connector);
649 	cconf->connector = connector;
650 
651 	if (connector->connection != DRM_MODE_CONNECTED) {
652 		drmModeFreeConnector(connector);
653 		return -1;
654 	}
655 
656 	if (!kmstest_get_connector_default_mode(drm_fd, connector,
657 						&cconf->default_mode)) {
658 		drmModeFreeConnector(connector);
659 		return -1;
660 	}
661 
662 	return 0;
663 }
664 
get_connectors(drmModeRes * resources,int * connector_idxs,int connector_count,struct connector_config * cconfs)665 static int get_connectors(drmModeRes *resources, int *connector_idxs,
666 			  int connector_count, struct connector_config *cconfs)
667 {
668 	int i;
669 
670 	for (i = 0; i < connector_count; i++) {
671 		int connector_idx;
672 		int connector_id;
673 
674 		connector_idx = connector_idxs[i];
675 		igt_assert_lt(connector_idx, resources->count_connectors);
676 		connector_id = resources->connectors[connector_idx];
677 
678 		if (get_one_connector(resources, connector_id, &cconfs[i]) < 0)
679 			goto err;
680 
681 	}
682 
683 	return 0;
684 
685 err:
686 	while (i--)
687 		drmModeFreeConnector(cconfs[i].connector);
688 
689 	return -1;
690 }
691 
free_connectors(struct connector_config * cconfs,int connector_count)692 static void free_connectors(struct connector_config *cconfs,
693 			    int connector_count)
694 {
695 	int i;
696 
697 	for (i = 0; i < connector_count; i++)
698 		drmModeFreeConnector(cconfs[i].connector);
699 }
700 
701 struct combination {
702 	int elems[MAX_COMBINATION_ELEMS];
703 };
704 
705 struct combination_set {
706 	int count;
707 	int capacity;
708 	struct combination *items;
709 };
710 
711 /*
712  * Get all possible selection of k elements from n elements with or without
713  * repetitions.
714  */
iterate_combinations(int n,int k,bool allow_repetitions,int depth,int base,struct combination * comb,struct combination_set * set)715 static void iterate_combinations(int n, int k, bool allow_repetitions,
716 				 int depth, int base, struct combination *comb,
717 				 struct combination_set *set)
718 {
719 	int v;
720 
721 	if (!k) {
722 		igt_assert(set->count < set->capacity);
723 		set->items[set->count++] = *comb;
724 		return;
725 	}
726 
727 	for (v = base; v < n; v++) {
728 		comb->elems[depth] = v;
729 		iterate_combinations(n, k - 1, allow_repetitions,
730 				     depth + 1, allow_repetitions ? 0 : v + 1,
731 				     comb, set);
732 	}
733 
734 }
735 
get_combinations(int n,int k,bool allow_repetitions,struct combination_set * set)736 static void get_combinations(int n, int k, bool allow_repetitions,
737 			     struct combination_set *set)
738 {
739 	struct combination comb;
740 
741 	igt_assert(k <= ARRAY_SIZE(set->items[0].elems));
742 	set->count = 0;
743 	iterate_combinations(n, k, allow_repetitions, 0, 0, &comb, set);
744 }
745 
test_combinations(const struct test_config * tconf,int connector_count)746 static void test_combinations(const struct test_config *tconf,
747 			      int connector_count)
748 {
749 	struct combination_set connector_combs;
750 	struct combination_set crtc_combs;
751 	struct connector_config *cconfs;
752 	int i;
753 
754 	if (connector_count > 2 && (tconf->flags & TEST_STEALING))
755 		return;
756 
757 	igt_assert(tconf->resources);
758 
759 	connector_combs.capacity = pow(tconf->resources->count_connectors,
760 				       tconf->resources->count_crtcs + 1);
761 	crtc_combs.capacity = pow(tconf->resources->count_crtcs,
762 				  tconf->resources->count_crtcs + 1);
763 
764 	connector_combs.items = malloc(connector_combs.capacity * sizeof(struct combination));
765 	crtc_combs.items = malloc(crtc_combs.capacity * sizeof(struct combination));
766 
767 	get_combinations(tconf->resources->count_connectors, connector_count,
768 			 false, &connector_combs);
769 	get_combinations(tconf->resources->count_crtcs, connector_count,
770 			 true, &crtc_combs);
771 
772 	igt_info("Testing: %s %d connector combinations\n", tconf->name,
773 		 connector_count);
774 	for (i = 0; i < connector_combs.count; i++) {
775 		int *connector_idxs;
776 		int ret;
777 		int j;
778 
779 		cconfs = malloc(sizeof(*cconfs) * connector_count);
780 		igt_assert(cconfs);
781 
782 		connector_idxs = &connector_combs.items[i].elems[0];
783 		ret = get_connectors(tconf->resources, connector_idxs,
784 				     connector_count, cconfs);
785 		if (ret < 0)
786 			goto free_cconfs;
787 
788 		for (j = 0; j < crtc_combs.count; j++) {
789 			int *crtc_idxs = &crtc_combs.items[j].elems[0];
790 			ret = assign_crtc_to_connectors(tconf, crtc_idxs,
791 							connector_count,
792 						        cconfs);
793 			if (ret < 0)
794 				continue;
795 
796 			test_one_combination(tconf, cconfs, connector_count);
797 		}
798 
799 		free_connectors(cconfs, connector_count);
800 free_cconfs:
801 		free(cconfs);
802 	}
803 
804 	free(connector_combs.items);
805 	free(crtc_combs.items);
806 }
807 
run_test(const struct test_config * tconf)808 static void run_test(const struct test_config *tconf)
809 {
810 	int connector_num;
811 
812 	connector_num = tconf->flags & TEST_CLONE ? 2 : 1;
813 	for (; connector_num <= tconf->resources->count_crtcs; connector_num++)
814 		test_combinations(tconf, connector_num);
815 }
816 
opt_handler(int opt,int opt_index,void * data)817 static int opt_handler(int opt, int opt_index, void *data)
818 {
819 	switch (opt) {
820 	case 'd':
821 		dry_run = true;
822 		break;
823 	case 't':
824 		filter_test_id = atoi(optarg);
825 		break;
826 	default:
827 		return IGT_OPT_HANDLER_ERROR;
828 	}
829 
830 	return IGT_OPT_HANDLER_SUCCESS;
831 }
832 
833 const char *help_str =
834 	"  -d\t\tDon't run any test, only print what would be done. (still needs DRM access)\n"
835 	"  -t <test id>\tRun only the test with this id.";
836 
837 igt_main_args("dt:", NULL, help_str, opt_handler, NULL)
838 {
839 	const struct {
840 		enum test_flags flags;
841 		const char *name;
842 	} tests[] = {
843 		{ TEST_TIMINGS, "basic" },
844 		{ TEST_CLONE | TEST_SINGLE_CRTC_CLONE,
845 					"basic-clone-single-crtc" },
846 		{ TEST_INVALID | TEST_CLONE | TEST_SINGLE_CRTC_CLONE,
847 					"invalid-clone-single-crtc" },
848 		{ TEST_INVALID | TEST_CLONE | TEST_EXCLUSIVE_CRTC_CLONE,
849 					"invalid-clone-exclusive-crtc" },
850 		{ TEST_CLONE | TEST_EXCLUSIVE_CRTC_CLONE,
851 					"clone-exclusive-crtc" },
852 		{ TEST_INVALID | TEST_CLONE | TEST_SINGLE_CRTC_CLONE | TEST_STEALING,
853 					"invalid-clone-single-crtc-stealing" }
854 	};
855 	int i;
856 
857 	igt_skip_on_simulation();
858 
859 	igt_assert_f(!(dry_run && filter_test_id),
860 		     "only one of -d and -t is accepted\n");
861 
862 	igt_fixture {
863 		drm_fd = drm_open_driver_master(DRIVER_ANY);
864 		if (!dry_run)
865 			kmstest_set_vt_graphics_mode();
866 
867 		drm_resources = drmModeGetResources(drm_fd);
868 		igt_require(drm_resources);
869 	}
870 
871 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
872 		igt_subtest(tests[i].name) {
873 			struct test_config tconf = {
874 				.flags		= tests[i].flags,
875 				.name		= tests[i].name,
876 				.resources	= drm_resources,
877 			};
878 			run_test(&tconf);
879 		}
880 	}
881 
882 	igt_fixture {
883 		drmModeFreeResources(drm_resources);
884 
885 		close(drm_fd);
886 	}
887 }
888