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