xref: /aosp_15_r20/external/igt-gpu-tools/tests/kms_concurrent.c (revision d83cc019efdc2edc6c4b16e9034a3ceb8d35d77c)
1 /*
2  * Copyright © 2017 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  *
23  */
24 
25 #include "igt.h"
26 #include "drmtest.h"
27 
28 IGT_TEST_DESCRIPTION("Test atomic mode setting concurrently with multiple planes and screen resolution");
29 
30 #define SIZE_PLANE      256
31 #define SIZE_CURSOR     128
32 #define LOOP_FOREVER     -1
33 
34 typedef struct {
35 	int drm_fd;
36 	igt_display_t display;
37 	igt_plane_t **plane;
38 	struct igt_fb *fb;
39 } data_t;
40 
41 /* Command line parameters. */
42 struct {
43 	int iterations;
44 	bool user_seed;
45 	int seed;
46 	bool run;
47 } opt = {
48 	.iterations = 1,
49 	.user_seed = false,
50 	.seed = 1,
51 	.run = true,
52 };
53 
54 /*
55  * Common code across all tests, acting on data_t
56  */
test_init(data_t * data,enum pipe pipe,int n_planes,igt_output_t * output)57 static void test_init(data_t *data, enum pipe pipe, int n_planes,
58 		      igt_output_t *output)
59 {
60 	drmModeModeInfo *mode;
61 	igt_plane_t *primary;
62 	int ret;
63 
64 	data->plane = calloc(n_planes, sizeof(*data->plane));
65 	igt_assert_f(data->plane != NULL, "Failed to allocate memory for planes\n");
66 
67 	data->fb = calloc(n_planes, sizeof(struct igt_fb));
68 	igt_assert_f(data->fb != NULL, "Failed to allocate memory for FBs\n");
69 
70 	igt_output_set_pipe(output, pipe);
71 
72 	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
73 	data->plane[primary->index] = primary;
74 
75 	mode = igt_output_get_mode(output);
76 
77 	igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay,
78 			    DRM_FORMAT_XRGB8888,
79 			    LOCAL_I915_FORMAT_MOD_X_TILED,
80 			    0.0f, 0.0f, 1.0f,
81 			    &data->fb[primary->index]);
82 
83 	igt_plane_set_fb(data->plane[primary->index], &data->fb[primary->index]);
84 
85 	ret = igt_display_try_commit2(&data->display, COMMIT_ATOMIC);
86 	igt_skip_on(ret != 0);
87 }
88 
test_fini(data_t * data,enum pipe pipe,int n_planes,igt_output_t * output)89 static void test_fini(data_t *data, enum pipe pipe, int n_planes,
90 		      igt_output_t *output)
91 {
92 	int i;
93 
94 	for (i = 0; i < n_planes; i++) {
95 		igt_plane_t *plane = data->plane[i];
96 
97 		if (!plane)
98 			continue;
99 
100 		if (plane->type == DRM_PLANE_TYPE_PRIMARY)
101 			continue;
102 
103 		igt_plane_set_fb(plane, NULL);
104 
105 		data->plane[i] = NULL;
106 	}
107 
108 	/* reset the constraint on the pipe */
109 	igt_output_set_pipe(output, PIPE_ANY);
110 
111 	free(data->plane);
112 	data->plane = NULL;
113 
114 	free(data->fb);
115 	data->fb = NULL;
116 }
117 
118 static void
create_fb_for_mode_position(data_t * data,drmModeModeInfo * mode,int * rect_x,int * rect_y,int * rect_w,int * rect_h,uint64_t tiling,int max_planes,igt_output_t * output)119 create_fb_for_mode_position(data_t *data, drmModeModeInfo *mode,
120 			    int *rect_x, int *rect_y,
121 			    int *rect_w, int *rect_h,
122 			    uint64_t tiling, int max_planes,
123 			    igt_output_t *output)
124 {
125 	unsigned int fb_id;
126 	cairo_t *cr;
127 	igt_plane_t *primary;
128 
129 	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
130 
131 	fb_id = igt_create_fb(data->drm_fd,
132 			      mode->hdisplay, mode->vdisplay,
133 			      DRM_FORMAT_XRGB8888,
134 			      tiling,
135 			      &data->fb[primary->index]);
136 	igt_assert(fb_id);
137 
138 	cr = igt_get_cairo_ctx(data->drm_fd, &data->fb[primary->index]);
139 	igt_paint_color(cr, rect_x[0], rect_y[0],
140 			mode->hdisplay, mode->vdisplay,
141 			0.0f, 0.0f, 1.0f);
142 
143 	for (int i = 0; i < max_planes; i++) {
144 		if (data->plane[i]->type == DRM_PLANE_TYPE_PRIMARY)
145 			continue;
146 
147 		igt_paint_color(cr, rect_x[i], rect_y[i],
148 				rect_w[i], rect_h[i], 0.0, 0.0, 0.0);
149 	}
150 
151 	igt_put_cairo_ctx(data->drm_fd, &data->fb[primary->index], cr);
152 }
153 
154 static void
prepare_planes(data_t * data,enum pipe pipe,int max_planes,igt_output_t * output)155 prepare_planes(data_t *data, enum pipe pipe, int max_planes,
156 	       igt_output_t *output)
157 {
158 	drmModeModeInfo *mode;
159 	igt_pipe_t *p;
160 	igt_plane_t *primary;
161 	int *x;
162 	int *y;
163 	int *size;
164 	int i;
165 
166 	igt_output_set_pipe(output, pipe);
167 
168 	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
169 	p = primary->pipe;
170 
171 	x = malloc(p->n_planes * sizeof(*x));
172 	igt_assert_f(x, "Failed to allocate %ld bytes for variable x\n", (long int) (p->n_planes * sizeof(*x)));
173 
174 	y = malloc(p->n_planes * sizeof(*y));
175 	igt_assert_f(y, "Failed to allocate %ld bytes for variable y\n", (long int) (p->n_planes * sizeof(*y)));
176 
177 	size = malloc(p->n_planes * sizeof(*size));
178 	igt_assert_f(size, "Failed to allocate %ld bytes for variable size\n", (long int) (p->n_planes * sizeof(*size)));
179 
180 	mode = igt_output_get_mode(output);
181 
182 	/* planes with random positions */
183 	x[primary->index] = 0;
184 	y[primary->index] = 0;
185 	for (i = 0; i < max_planes; i++) {
186 		igt_plane_t *plane = igt_output_get_plane(output, i);
187 		int ret;
188 
189 		if (plane->type == DRM_PLANE_TYPE_PRIMARY)
190 			continue;
191 		else if (plane->type == DRM_PLANE_TYPE_CURSOR)
192 			size[i] = SIZE_CURSOR;
193 		else
194 			size[i] = SIZE_PLANE;
195 
196 		x[i] = rand() % (mode->hdisplay - size[i]);
197 		y[i] = rand() % (mode->vdisplay - size[i]);
198 
199 		data->plane[i] = plane;
200 
201 		igt_create_color_fb(data->drm_fd,
202 				    size[i], size[i],
203 				    data->plane[i]->type == DRM_PLANE_TYPE_CURSOR ? DRM_FORMAT_ARGB8888 : DRM_FORMAT_XRGB8888,
204 				    data->plane[i]->type == DRM_PLANE_TYPE_CURSOR ? LOCAL_DRM_FORMAT_MOD_NONE : LOCAL_I915_FORMAT_MOD_X_TILED,
205 				    0.0f, 0.0f, 1.0f,
206 				    &data->fb[i]);
207 
208 		igt_plane_set_position(data->plane[i], x[i], y[i]);
209 		igt_plane_set_fb(data->plane[i], &data->fb[i]);
210 
211 		ret = igt_display_try_commit_atomic(&data->display, DRM_MODE_ATOMIC_TEST_ONLY, NULL);
212 		if (ret) {
213 			igt_plane_set_fb(data->plane[i], NULL);
214 			igt_remove_fb(data->drm_fd, &data->fb[i]);
215 			data->plane[i] = NULL;
216 			break;
217 		}
218 	}
219 	max_planes = i;
220 
221 	igt_assert_lt(0, max_planes);
222 
223 	/* primary plane */
224 	data->plane[primary->index] = primary;
225 	create_fb_for_mode_position(data, mode, x, y, size, size,
226 				    LOCAL_I915_FORMAT_MOD_X_TILED,
227 				    max_planes, output);
228 
229 	igt_plane_set_fb(data->plane[primary->index], &data->fb[primary->index]);
230 }
231 
232 static void
test_plane_position_with_output(data_t * data,enum pipe pipe,igt_output_t * output)233 test_plane_position_with_output(data_t *data, enum pipe pipe, igt_output_t *output)
234 {
235 	int i;
236 	int iterations = opt.iterations < 1 ? 1 : opt.iterations;
237 	bool loop_forever = opt.iterations == LOOP_FOREVER ? true : false;
238 	int max_planes = data->display.pipes[pipe].n_planes;
239 
240 	igt_pipe_refresh(&data->display, pipe, true);
241 
242 	i = 0;
243 	while (i < iterations || loop_forever) {
244 		prepare_planes(data, pipe, max_planes, output);
245 		igt_display_commit2(&data->display, COMMIT_ATOMIC);
246 
247 		i++;
248 	}
249 }
250 
251 static const drmModeModeInfo *
get_lowres_mode(data_t * data,const drmModeModeInfo * mode_default,igt_output_t * output)252 get_lowres_mode(data_t *data, const drmModeModeInfo *mode_default,
253 		igt_output_t *output)
254 {
255 	const drmModeModeInfo *mode = igt_std_1024_mode_get();
256 	drmModeConnector *connector = output->config.connector;
257 	int limit = mode_default->vdisplay - SIZE_PLANE;
258 	bool found;
259 
260 	if (!connector)
261 		return mode;
262 
263 	found = false;
264 	for (int i = 0; i < connector->count_modes; i++) {
265 		mode = &connector->modes[i];
266 
267 		if (mode->vdisplay < limit) {
268 			found = true;
269 			break;
270 		}
271 	}
272 
273 	if (!found)
274 		mode = igt_std_1024_mode_get();
275 
276 	return mode;
277 }
278 
279 static void
test_resolution_with_output(data_t * data,enum pipe pipe,igt_output_t * output)280 test_resolution_with_output(data_t *data, enum pipe pipe, igt_output_t *output)
281 {
282 	const drmModeModeInfo *mode_hi, *mode_lo;
283 	int iterations = opt.iterations < 1 ? 1 : opt.iterations;
284 	bool loop_forever = opt.iterations == LOOP_FOREVER ? true : false;
285 	int i;
286 
287 	i = 0;
288 	while (i < iterations || loop_forever) {
289 		mode_hi = igt_output_get_mode(output);
290 		mode_lo = get_lowres_mode(data, mode_hi, output);
291 
292 		/* switch to lower resolution */
293 		igt_output_override_mode(output, mode_lo);
294 		igt_display_commit2(&data->display, COMMIT_ATOMIC);
295 
296 		/* switch back to higher resolution */
297 		igt_output_override_mode(output, NULL);
298 		igt_display_commit2(&data->display, COMMIT_ATOMIC);
299 
300 		i++;
301 	}
302 }
303 
304 static void
run_test(data_t * data,enum pipe pipe,igt_output_t * output)305 run_test(data_t *data, enum pipe pipe, igt_output_t *output)
306 {
307 	int connected_outs;
308 	int n_planes = data->display.pipes[pipe].n_planes;
309 
310 	if (!opt.user_seed)
311 		opt.seed = time(NULL);
312 
313 	connected_outs = 0;
314 	for_each_valid_output_on_pipe(&data->display, pipe, output) {
315 		igt_info("Testing resolution with connector %s using pipe %s with seed %d\n",
316 			 igt_output_name(output), kmstest_pipe_name(pipe), opt.seed);
317 
318 		test_init(data, pipe, n_planes, output);
319 
320 		igt_fork(child, 1) {
321 			test_plane_position_with_output(data, pipe, output);
322 		}
323 
324 		test_resolution_with_output(data, pipe, output);
325 
326 		igt_waitchildren();
327 
328 		test_fini(data, pipe, n_planes, output);
329 
330 		connected_outs++;
331 	}
332 
333 	igt_skip_on(connected_outs == 0);
334 }
335 
336 static void
run_tests_for_pipe(data_t * data,enum pipe pipe)337 run_tests_for_pipe(data_t *data, enum pipe pipe)
338 {
339 	igt_output_t *output;
340 
341 	igt_fixture {
342 		int valid_tests = 0;
343 
344 		igt_skip_on(pipe >= data->display.n_pipes);
345 		igt_require(data->display.pipes[pipe].n_planes > 0);
346 
347 		for_each_valid_output_on_pipe(&data->display, pipe, output)
348 			valid_tests++;
349 
350 		igt_require_f(valid_tests, "no valid crtc/connector combinations found\n");
351 	}
352 
353 	igt_subtest_f("pipe-%s", kmstest_pipe_name(pipe))
354 		for_each_valid_output_on_pipe(&data->display, pipe, output)
355 			run_test(data, pipe, output);
356 }
357 
opt_handler(int option,int option_index,void * input)358 static int opt_handler(int option, int option_index, void *input)
359 {
360 	switch (option) {
361 	case 'i':
362 		opt.iterations = strtol(optarg, NULL, 0);
363 
364 		if (opt.iterations < LOOP_FOREVER || opt.iterations == 0) {
365 			igt_info("incorrect number of iterations\n");
366 			igt_assert(false);
367 		}
368 
369 		break;
370 	case 's':
371 		opt.user_seed = true;
372 		opt.seed = strtol(optarg, NULL, 0);
373 		break;
374 	default:
375 		return IGT_OPT_HANDLER_ERROR;
376 	}
377 
378 	return IGT_OPT_HANDLER_SUCCESS;
379 }
380 
381 const char *help_str =
382 	"  --iterations Number of iterations for test coverage. -1 loop forever, default 1 iteration\n"
383 	"  --seed       Seed for random number generator\n";
384 struct option long_options[] = {
385 	{ "iterations", required_argument, NULL, 'i'},
386 	{ "seed",    required_argument, NULL, 's'},
387 	{ 0, 0, 0, 0 }
388 };
389 
390 static data_t data;
391 
392 igt_main_args("", long_options, help_str, opt_handler, NULL)
393 {
394 	enum pipe pipe;
395 
396 	igt_skip_on_simulation();
397 
398 	igt_fixture {
399 		data.drm_fd = drm_open_driver_master(DRIVER_ANY);
400 		kmstest_set_vt_graphics_mode();
401 		igt_display_require(&data.display, data.drm_fd);
402 		igt_require(data.display.is_atomic);
403 	}
404 
for_each_pipe_static(pipe)405 	for_each_pipe_static(pipe) {
406 		igt_subtest_group
407 			run_tests_for_pipe(&data, pipe);
408 	}
409 
410 	igt_fixture {
411 		igt_display_fini(&data.display);
412 		close(data.drm_fd);
413 	}
414 }
415