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