blob: 19e6570a61c3d4962a1e8ccf93f56a8a83d6d577 [file] [log] [blame]
Yalong Liu1df84372018-01-24 17:10:12 +08001/*
2 * Copyright 2017 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7/*
8 * This file performs some sanity checks on the DRM atomic API. To run a test, please run the
9 * following command:
10 *
11 * atomictest <testname>
12 *
13 * To get a list of possible tests, run:
14 *
15 * atomictest
16 */
17
18#include <getopt.h>
19
20#include "bs_drm.h"
21
22#define CHECK(cond) \
23 do { \
24 if (!(cond)) { \
25 bs_debug_error("check %s failed", #cond); \
26 return -1; \
27 } \
28 } while (0)
29
30#define CHECK_RESULT(ret) \
31 do { \
32 if ((ret) < 0) { \
33 bs_debug_error("failed with error: %d", ret); \
34 return -1; \
35 } \
36 } while (0)
37
38#define CURSOR_SIZE 64
39
40// TODO(gsingh) This is defined in upstream libdrm -- remove when CROS libdrm is updated.
41#ifndef DRM_ROTATE_0
42#define DRM_ROTATE_0 (1UL << 0)
43#endif
44
45#ifndef DRM_REFLECT_Y
46#define DRM_REFLECT_Y (1UL << 5)
47#endif
48
49static const uint32_t yuv_formats[] = {
50 DRM_FORMAT_NV12, DRM_FORMAT_YVU420,
51};
52
53static struct gbm_device *gbm = NULL;
54
55static void page_flip_handler(int fd, unsigned int sequence, unsigned int tv_sec,
56 unsigned int tv_usec, void *user_data)
57{
58 // Nothing to do.
59}
60
61struct atomictest_property {
62 uint32_t pid;
63 uint32_t value;
64};
65
66struct atomictest_plane {
67 drmModePlane drm_plane;
68 struct gbm_bo *bo;
69
70 uint32_t format_idx;
71
72 /* Properties. */
73 struct atomictest_property crtc_id;
74 struct atomictest_property crtc_x;
75 struct atomictest_property crtc_y;
76 struct atomictest_property crtc_w;
77 struct atomictest_property crtc_h;
78 struct atomictest_property fb_id;
79 struct atomictest_property src_x;
80 struct atomictest_property src_y;
81 struct atomictest_property src_w;
82 struct atomictest_property src_h;
83 struct atomictest_property type;
84 struct atomictest_property rotation;
85 struct atomictest_property ctm;
86};
87
88struct atomictest_connector {
89 uint32_t connector_id;
90 struct atomictest_property crtc_id;
91 struct atomictest_property edid;
92 struct atomictest_property dpms;
93};
94
95struct atomictest_crtc {
96 uint32_t crtc_id;
97 uint32_t width;
98 uint32_t height;
99 uint32_t *primary_idx;
100 uint32_t *cursor_idx;
101 uint32_t *overlay_idx;
102 uint32_t num_primary;
103 uint32_t num_cursor;
104 uint32_t num_overlay;
105
106 struct atomictest_plane *planes;
107 struct atomictest_property mode_id;
108 struct atomictest_property active;
109};
110
111struct atomictest_mode {
112 uint32_t height;
113 uint32_t width;
114 uint32_t id;
115};
116
117struct atomictest_context {
118 int fd;
119 uint32_t num_crtcs;
120 uint32_t num_connectors;
121 uint32_t num_modes;
122
123 struct atomictest_connector *connectors;
124 struct atomictest_crtc *crtcs;
125 struct atomictest_mode *modes;
126 drmModeAtomicReqPtr pset;
127 drmEventContext drm_event_ctx;
128
129 struct bs_mapper *mapper;
130};
131
132typedef int (*test_function)(struct atomictest_context *ctx, struct atomictest_crtc *crtc);
133
134struct atomictest_testcase {
135 const char *name;
136 test_function test_func;
137};
138
139// clang-format off
140enum draw_format_type {
141 DRAW_NONE = 0,
142 DRAW_STRIPE = 1,
143 DRAW_ELLIPSE = 2,
144 DRAW_CURSOR = 3,
145 DRAW_LINES = 4,
146};
147// clang-format on
148
149static int32_t get_format_idx(struct atomictest_plane *plane, uint32_t format)
150{
151 for (int32_t i = 0; i < plane->drm_plane.count_formats; i++)
152 if (plane->drm_plane.formats[i] == format)
153 return i;
154 return -1;
155}
156
157static void copy_drm_plane(drmModePlane *dest, drmModePlane *src)
158{
159 memcpy(dest, src, sizeof(drmModePlane));
160 dest->formats = calloc(src->count_formats, sizeof(uint32_t));
161 memcpy(dest->formats, src->formats, src->count_formats * sizeof(uint32_t));
162}
163
164static struct atomictest_plane *get_plane(struct atomictest_crtc *crtc, uint32_t idx, uint64_t type)
165{
166 uint32_t index;
167 switch (type) {
168 case DRM_PLANE_TYPE_OVERLAY:
169 index = crtc->overlay_idx[idx];
170 break;
171 case DRM_PLANE_TYPE_PRIMARY:
172 index = crtc->primary_idx[idx];
173 break;
174 case DRM_PLANE_TYPE_CURSOR:
175 index = crtc->cursor_idx[idx];
176 break;
177 default:
178 bs_debug_error("invalid plane type returned");
179 return NULL;
180 }
181
182 return &crtc->planes[index];
183}
184
185static int draw_to_plane(struct bs_mapper *mapper, struct atomictest_plane *plane,
186 enum draw_format_type pattern)
187{
188 struct gbm_bo *bo = plane->bo;
189 uint32_t format = gbm_bo_get_format(bo);
190 const struct bs_draw_format *draw_format = bs_get_draw_format(format);
191
192 if (draw_format && pattern) {
193 switch (pattern) {
194 case DRAW_STRIPE:
195 CHECK(bs_draw_stripe(mapper, bo, draw_format));
196 break;
197 case DRAW_ELLIPSE:
198 CHECK(bs_draw_ellipse(mapper, bo, draw_format, 0));
199 break;
200 case DRAW_CURSOR:
201 CHECK(bs_draw_cursor(mapper, bo, draw_format));
202 break;
203 case DRAW_LINES:
204 CHECK(bs_draw_lines(mapper, bo, draw_format));
205 break;
206 default:
207 bs_debug_error("invalid draw type");
208 return -1;
209 }
210 } else {
211 // DRM_FORMAT_RGB565 --> red, DRM_FORMAT_BGR565 --> blue,
212 // everything else --> something
213 void *map_data;
214 uint16_t value = 0xF800;
215 void *addr = bs_mapper_map(mapper, bo, 0, &map_data);
216 uint32_t num_shorts = gbm_bo_get_plane_size(bo, 0) / sizeof(uint16_t);
217 uint16_t *pixel = (uint16_t *)addr;
218
219 CHECK(addr);
220 for (uint32_t i = 0; i < num_shorts; i++)
221 pixel[i] = value;
222
223 bs_mapper_unmap(mapper, bo, map_data);
224 }
225
226 return 0;
227}
228
229static int get_prop(int fd, drmModeObjectPropertiesPtr props, const char *name,
230 struct atomictest_property *bs_prop)
231{
232 /* Property ID should always be > 0. */
233 bs_prop->pid = 0;
234 drmModePropertyPtr prop;
235 for (uint32_t i = 0; i < props->count_props; i++) {
236 if (bs_prop->pid)
237 break;
238
239 prop = drmModeGetProperty(fd, props->props[i]);
240 if (prop) {
241 if (!strcmp(prop->name, name)) {
242 bs_prop->pid = prop->prop_id;
243 bs_prop->value = props->prop_values[i];
244 }
245 drmModeFreeProperty(prop);
246 }
247 }
248
249 return (bs_prop->pid == 0) ? -1 : 0;
250}
251
252static int get_connector_props(int fd, struct atomictest_connector *connector,
253 drmModeObjectPropertiesPtr props)
254{
255 CHECK_RESULT(get_prop(fd, props, "CRTC_ID", &connector->crtc_id));
256 CHECK_RESULT(get_prop(fd, props, "EDID", &connector->edid));
257 CHECK_RESULT(get_prop(fd, props, "DPMS", &connector->dpms));
258 return 0;
259}
260
261static int get_crtc_props(int fd, struct atomictest_crtc *crtc, drmModeObjectPropertiesPtr props)
262{
263 CHECK_RESULT(get_prop(fd, props, "MODE_ID", &crtc->mode_id));
264 CHECK_RESULT(get_prop(fd, props, "ACTIVE", &crtc->active));
265 return 0;
266}
267
268static int get_plane_props(int fd, struct atomictest_plane *plane, drmModeObjectPropertiesPtr props)
269{
270 CHECK_RESULT(get_prop(fd, props, "CRTC_ID", &plane->crtc_id));
271 CHECK_RESULT(get_prop(fd, props, "FB_ID", &plane->fb_id));
272 CHECK_RESULT(get_prop(fd, props, "CRTC_X", &plane->crtc_x));
273 CHECK_RESULT(get_prop(fd, props, "CRTC_Y", &plane->crtc_y));
274 CHECK_RESULT(get_prop(fd, props, "CRTC_W", &plane->crtc_w));
275 CHECK_RESULT(get_prop(fd, props, "CRTC_H", &plane->crtc_h));
276 CHECK_RESULT(get_prop(fd, props, "SRC_X", &plane->src_x));
277 CHECK_RESULT(get_prop(fd, props, "SRC_Y", &plane->src_y));
278 CHECK_RESULT(get_prop(fd, props, "SRC_W", &plane->src_w));
279 CHECK_RESULT(get_prop(fd, props, "SRC_H", &plane->src_h));
280 CHECK_RESULT(get_prop(fd, props, "type", &plane->type));
281
282 /*
283 * The atomic API makes no guarantee a property is present in object. This test
284 * requires the above common properties since a plane is undefined without them.
285 * Other properties (i.e: rotation and ctm) are optional.
286 */
287 get_prop(fd, props, "rotation", &plane->rotation);
288 get_prop(fd, props, "PLANE_CTM", &plane->ctm);
289 return 0;
290}
291
292int set_connector_props(struct atomictest_connector *conn, drmModeAtomicReqPtr pset)
293{
294 uint32_t id = conn->connector_id;
295 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, conn->crtc_id.pid, conn->crtc_id.value));
296 return 0;
297}
298
299int set_crtc_props(struct atomictest_crtc *crtc, drmModeAtomicReqPtr pset)
300{
301 uint32_t id = crtc->crtc_id;
302 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, crtc->mode_id.pid, crtc->mode_id.value));
303 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, crtc->active.pid, crtc->active.value));
304 return 0;
305}
306
307int set_plane_props(struct atomictest_plane *plane, drmModeAtomicReqPtr pset)
308{
309 uint32_t id = plane->drm_plane.plane_id;
310 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->crtc_id.pid, plane->crtc_id.value));
311 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->fb_id.pid, plane->fb_id.value));
312 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->crtc_x.pid, plane->crtc_x.value));
313 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->crtc_y.pid, plane->crtc_y.value));
314 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->crtc_w.pid, plane->crtc_w.value));
315 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->crtc_h.pid, plane->crtc_h.value));
316 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->src_x.pid, plane->src_x.value));
317 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->src_y.pid, plane->src_y.value));
318 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->src_w.pid, plane->src_w.value));
319 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->src_h.pid, plane->src_h.value));
320 if (plane->rotation.pid)
321 CHECK_RESULT(
322 drmModeAtomicAddProperty(pset, id, plane->rotation.pid, plane->rotation.value));
323 if (plane->ctm.pid)
324 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->ctm.pid, plane->ctm.value));
325
326 return 0;
327}
328
329static int remove_plane_fb(struct atomictest_context *ctx, struct atomictest_plane *plane)
330{
331 if (plane->bo && plane->fb_id.value) {
332 CHECK_RESULT(drmModeRmFB(ctx->fd, plane->fb_id.value));
333 gbm_bo_destroy(plane->bo);
334 plane->bo = NULL;
335 plane->fb_id.value = 0;
336 }
337
338 return 0;
339}
340
341static int add_plane_fb(struct atomictest_context *ctx, struct atomictest_plane *plane)
342{
343 if (plane->format_idx < plane->drm_plane.count_formats) {
344 CHECK_RESULT(remove_plane_fb(ctx, plane));
345 uint32_t flags = (plane->type.value == DRM_PLANE_TYPE_CURSOR) ? GBM_BO_USE_CURSOR
346 : GBM_BO_USE_SCANOUT;
347 flags |= GBM_BO_USE_SW_WRITE_RARELY;
348 /* TODO(gsingh): add create with modifiers option. */
349 plane->bo = gbm_bo_create(gbm, plane->crtc_w.value, plane->crtc_h.value,
350 plane->drm_plane.formats[plane->format_idx], flags);
351
352 CHECK(plane->bo);
353 plane->fb_id.value = bs_drm_fb_create_gbm(plane->bo);
354 CHECK(plane->fb_id.value);
355 CHECK_RESULT(set_plane_props(plane, ctx->pset));
356 }
357
358 return 0;
359}
360
361static int init_plane(struct atomictest_context *ctx, struct atomictest_plane *plane,
362 uint32_t format, uint32_t x, uint32_t y, uint32_t w, uint32_t h,
363 uint32_t crtc_id)
364{
365 int32_t idx = get_format_idx(plane, format);
366 if (idx < 0)
367 return -EINVAL;
368
369 plane->format_idx = idx;
370 plane->crtc_x.value = x;
371 plane->crtc_y.value = y;
372 plane->crtc_w.value = w;
373 plane->crtc_h.value = h;
374 plane->src_w.value = plane->crtc_w.value << 16;
375 plane->src_h.value = plane->crtc_h.value << 16;
376 plane->crtc_id.value = crtc_id;
377
378 CHECK_RESULT(add_plane_fb(ctx, plane));
379 return 0;
380}
381
382static int init_yuv_plane(struct atomictest_context *ctx, struct atomictest_plane *plane,
383 uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t crtc_id)
384{
385 uint32_t i;
386 for (i = 0; i < BS_ARRAY_LEN(yuv_formats); i++)
387 if (!init_plane(ctx, plane, yuv_formats[i], x, y, w, h, crtc_id))
388 return 0;
389
390 return -EINVAL;
391}
392
393static int disable_plane(struct atomictest_context *ctx, struct atomictest_plane *plane)
394{
395 plane->format_idx = 0;
396 plane->crtc_x.value = 0;
397 plane->crtc_y.value = 0;
398 plane->crtc_w.value = 0;
399 plane->crtc_h.value = 0;
400 plane->src_w.value = 0;
401 plane->src_h.value = 0;
402 plane->crtc_id.value = 0;
403
404 if (plane->rotation.pid)
405 plane->rotation.value = DRM_ROTATE_0;
406 if (plane->ctm.pid)
407 plane->ctm.value = 0;
408
409 CHECK_RESULT(remove_plane_fb(ctx, plane));
410 CHECK_RESULT(set_plane_props(plane, ctx->pset));
411 return 0;
412}
413
414static int move_plane(struct atomictest_context *ctx, struct atomictest_crtc *crtc,
415 struct atomictest_plane *plane, uint32_t dx, uint32_t dy)
416{
417 if (plane->crtc_x.value < (crtc->width - plane->crtc_w.value) &&
418 plane->crtc_y.value < (crtc->height - plane->crtc_h.value)) {
419 plane->crtc_x.value += dx;
420 plane->crtc_y.value += dy;
421 CHECK_RESULT(set_plane_props(plane, ctx->pset));
422 return 0;
423 }
424
425 return -1;
426}
427
428static int scale_plane(struct atomictest_context *ctx, struct atomictest_crtc *crtc,
429 struct atomictest_plane *plane, float dw, float dh)
430{
431 int32_t plane_w = (int32_t)plane->crtc_w.value + dw * plane->crtc_w.value;
432 int32_t plane_h = (int32_t)plane->crtc_h.value + dh * plane->crtc_h.value;
433 if (plane_w > 0 && plane_h > 0 && (plane->crtc_x.value + plane_w < crtc->width) &&
434 (plane->crtc_h.value + plane_h < crtc->height)) {
435 plane->crtc_w.value = BS_ALIGN((uint32_t)plane_w, 2);
436 plane->crtc_h.value = BS_ALIGN((uint32_t)plane_h, 2);
437 CHECK_RESULT(set_plane_props(plane, ctx->pset));
438 return 0;
439 }
440
441 return -1;
442}
443
444static void log(struct atomictest_context *ctx)
445{
446 printf("Committing the following configuration: \n");
447 for (uint32_t i = 0; i < ctx->num_crtcs; i++) {
448 struct atomictest_plane *plane;
449 struct atomictest_crtc *crtc = &ctx->crtcs[i];
450 uint32_t num_planes = crtc->num_primary + crtc->num_cursor + crtc->num_overlay;
451 if (!crtc->active.value)
452 continue;
453
454 printf("----- [CRTC: %u] -----\n", crtc->crtc_id);
455 for (uint32_t j = 0; j < num_planes; j++) {
456 plane = &crtc->planes[j];
457 if (plane->crtc_id.value == crtc->crtc_id && plane->fb_id.value) {
458 uint32_t format = gbm_bo_get_format(plane->bo);
459 char *fourcc = (char *)&format;
460 printf("\t{Plane ID: %u, ", plane->drm_plane.plane_id);
461 printf("Plane format: %c%c%c%c, ", fourcc[0], fourcc[1], fourcc[2],
462 fourcc[3]);
463 printf("Plane type: ");
464 switch (plane->type.value) {
465 case DRM_PLANE_TYPE_OVERLAY:
466 printf("overlay, ");
467 break;
468 case DRM_PLANE_TYPE_PRIMARY:
469 printf("primary, ");
470 break;
471 case DRM_PLANE_TYPE_CURSOR:
472 printf("cursor, ");
473 break;
474 }
475
476 printf("CRTC_X: %u, CRTC_Y: %u, CRTC_W: %u, CRTC_H: %u}\n",
477 plane->crtc_x.value, plane->crtc_y.value,
478 plane->crtc_w.value, plane->crtc_h.value);
479 }
480 }
481 }
482}
483
484static int test_commit(struct atomictest_context *ctx)
485{
486 return drmModeAtomicCommit(ctx->fd, ctx->pset, DRM_MODE_ATOMIC_TEST_ONLY, NULL);
487}
488
489static int commit(struct atomictest_context *ctx)
490{
491 int ret;
492 fd_set fds;
493 FD_ZERO(&fds);
494 FD_SET(ctx->fd, &fds);
495
496 log(ctx);
497 ret = drmModeAtomicCommit(ctx->fd, ctx->pset,
498 DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
499 CHECK_RESULT(ret);
500 do {
501 ret = select(ctx->fd + 1, &fds, NULL, NULL, NULL);
502 } while (ret == -1 && errno == EINTR);
503
504 CHECK_RESULT(ret);
505 if (FD_ISSET(ctx->fd, &fds))
506 drmHandleEvent(ctx->fd, &ctx->drm_event_ctx);
507
508 return 0;
509}
510
511static int pageflip_formats(struct atomictest_context *ctx, struct atomictest_crtc *crtc,
512 struct atomictest_plane *plane)
513{
514 uint32_t flags;
515 for (uint32_t i = 0; i < plane->drm_plane.count_formats; i++) {
516 flags = (plane->type.value == DRM_PLANE_TYPE_CURSOR) ? GBM_BO_USE_CURSOR
517 : GBM_BO_USE_SCANOUT;
518 if (!gbm_device_is_format_supported(gbm, plane->drm_plane.formats[i], flags))
519 continue;
520
521 CHECK_RESULT(init_plane(ctx, plane, plane->drm_plane.formats[i], 0, 0, crtc->width,
522 crtc->height, crtc->crtc_id));
523 CHECK_RESULT(draw_to_plane(ctx->mapper, plane, DRAW_ELLIPSE));
524 CHECK_RESULT(commit(ctx));
525 usleep(1e6);
526
527 // disable, but don't commit, since we can't have an active CRTC without any planes.
528 CHECK_RESULT(disable_plane(ctx, plane));
529 }
530
531 return 0;
532}
533
534static uint32_t get_connection(struct atomictest_crtc *crtc, uint32_t crtc_index)
535{
536 uint32_t connector_id = 0;
537 uint32_t crtc_mask = 1u << crtc_index;
538 struct bs_drm_pipe pipe = { 0 };
539 struct bs_drm_pipe_plumber *plumber = bs_drm_pipe_plumber_new();
540 bs_drm_pipe_plumber_crtc_mask(plumber, crtc_mask);
541 if (bs_drm_pipe_plumber_make(plumber, &pipe))
542 connector_id = pipe.connector_id;
543
544 bs_drm_pipe_plumber_destroy(&plumber);
545 return connector_id;
546}
547
548static int enable_crtc(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
549{
550 drmModeAtomicSetCursor(ctx->pset, 0);
551
552 for (uint32_t i = 0; i < ctx->num_connectors; i++) {
553 ctx->connectors[i].crtc_id.value = 0;
554 set_connector_props(&ctx->connectors[i], ctx->pset);
555 }
556
557 for (uint32_t i = 0; i < ctx->num_crtcs; i++) {
558 if (&ctx->crtcs[i] == crtc) {
559 uint32_t connector_id = get_connection(crtc, i);
560 CHECK(connector_id);
561 for (uint32_t j = 0; j < ctx->num_connectors; j++) {
562 if (connector_id == ctx->connectors[j].connector_id) {
563 ctx->connectors[j].crtc_id.value = crtc->crtc_id;
564 set_connector_props(&ctx->connectors[j], ctx->pset);
565 break;
566 }
567 }
568
569 break;
570 }
571 }
572
573 int ret = -EINVAL;
574 int cursor = drmModeAtomicGetCursor(ctx->pset);
575
576 for (uint32_t i = 0; i < ctx->num_modes; i++) {
577 struct atomictest_mode *mode = &ctx->modes[i];
578 drmModeAtomicSetCursor(ctx->pset, cursor);
579
580 crtc->mode_id.value = mode->id;
581 crtc->active.value = 1;
582 crtc->width = mode->width;
583 crtc->height = mode->height;
584
585 set_crtc_props(crtc, ctx->pset);
586 ret = drmModeAtomicCommit(ctx->fd, ctx->pset,
587 DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET,
588 NULL);
589 if (!ret)
590 return 0;
591 }
592
593 bs_debug_error("[CRTC:%d]: failed to find mode", crtc->crtc_id);
594 return ret;
595}
596
597static int disable_crtc(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
598{
599 for (uint32_t i = 0; i < ctx->num_connectors; i++) {
600 ctx->connectors[i].crtc_id.value = 0;
601 set_connector_props(&ctx->connectors[i], ctx->pset);
602 }
603
604 crtc->mode_id.value = 0;
605 crtc->active.value = 0;
606 set_crtc_props(crtc, ctx->pset);
607 int ret = drmModeAtomicCommit(ctx->fd, ctx->pset, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
608 CHECK_RESULT(ret);
609 return ret;
610}
611
612static struct atomictest_context *new_context(uint32_t num_connectors, uint32_t num_crtcs,
613 uint32_t num_planes)
614{
615 struct atomictest_context *ctx = calloc(1, sizeof(*ctx));
616
617 ctx->mapper = bs_mapper_gem_new();
618 if (ctx->mapper == NULL) {
619 bs_debug_error("failed to create mapper object");
620 free(ctx);
621 return NULL;
622 }
623
624 ctx->connectors = calloc(num_connectors, sizeof(*ctx->connectors));
625 ctx->crtcs = calloc(num_crtcs, sizeof(*ctx->crtcs));
626 for (uint32_t i = 0; i < num_crtcs; i++) {
627 ctx->crtcs[i].planes = calloc(num_planes, sizeof(*ctx->crtcs[i].planes));
628 ctx->crtcs[i].overlay_idx = calloc(num_planes, sizeof(uint32_t));
629 ctx->crtcs[i].primary_idx = calloc(num_planes, sizeof(uint32_t));
630 ctx->crtcs[i].cursor_idx = calloc(num_planes, sizeof(uint32_t));
631 }
632
633 ctx->num_connectors = num_connectors;
634 ctx->num_crtcs = num_crtcs;
635 ctx->num_modes = 0;
636 ctx->modes = NULL;
637 ctx->pset = drmModeAtomicAlloc();
638 ctx->drm_event_ctx.version = DRM_EVENT_CONTEXT_VERSION;
639 ctx->drm_event_ctx.page_flip_handler = page_flip_handler;
640
641 return ctx;
642}
643
644static void free_context(struct atomictest_context *ctx)
645{
646 for (uint32_t i = 0; i < ctx->num_crtcs; i++) {
647 uint32_t num_planes = ctx->crtcs[i].num_primary + ctx->crtcs[i].num_cursor +
648 ctx->crtcs[i].num_overlay;
649
650 for (uint32_t j = 0; j < num_planes; j++) {
651 remove_plane_fb(ctx, &ctx->crtcs[i].planes[j]);
652 free(ctx->crtcs[i].planes[j].drm_plane.formats);
653 }
654
655 free(ctx->crtcs[i].planes);
656 free(ctx->crtcs[i].overlay_idx);
657 free(ctx->crtcs[i].cursor_idx);
658 free(ctx->crtcs[i].primary_idx);
659 }
660
661 drmModeAtomicFree(ctx->pset);
662 free(ctx->modes);
663 free(ctx->crtcs);
664 free(ctx->connectors);
665 bs_mapper_destroy(ctx->mapper);
666 free(ctx);
667}
668
669static struct atomictest_context *query_kms(int fd)
670{
671 drmModeRes *res = drmModeGetResources(fd);
672 if (res == NULL) {
673 bs_debug_error("failed to get drm resources");
674 return false;
675 }
676
677 drmModePlaneRes *plane_res = drmModeGetPlaneResources(fd);
678 if (plane_res == NULL) {
679 bs_debug_error("failed to get plane resources");
680 drmModeFreeResources(res);
681 return NULL;
682 }
683
684 struct atomictest_context *ctx =
685 new_context(res->count_connectors, res->count_crtcs, plane_res->count_planes);
686 if (ctx == NULL) {
687 bs_debug_error("failed to allocate atomic context");
688 drmModeFreePlaneResources(plane_res);
689 drmModeFreeResources(res);
690 return NULL;
691 }
692
693 ctx->fd = fd;
694 drmModeObjectPropertiesPtr props = NULL;
695
696 for (uint32_t conn_index = 0; conn_index < res->count_connectors; conn_index++) {
697 uint32_t conn_id = res->connectors[conn_index];
698 ctx->connectors[conn_index].connector_id = conn_id;
699 props = drmModeObjectGetProperties(fd, conn_id, DRM_MODE_OBJECT_CONNECTOR);
700 get_connector_props(fd, &ctx->connectors[conn_index], props);
701
702 drmModeConnector *connector = drmModeGetConnector(fd, conn_id);
703 for (uint32_t mode_index = 0; mode_index < connector->count_modes; mode_index++) {
704 ctx->modes =
705 realloc(ctx->modes, (ctx->num_modes + 1) * sizeof(*ctx->modes));
706 drmModeCreatePropertyBlob(fd, &connector->modes[mode_index],
707 sizeof(drmModeModeInfo),
708 &ctx->modes[ctx->num_modes].id);
709 ctx->modes[ctx->num_modes].width = connector->modes[mode_index].hdisplay;
710 ctx->modes[ctx->num_modes].height = connector->modes[mode_index].vdisplay;
711 ctx->num_modes++;
712 }
713
714 drmModeFreeConnector(connector);
715 drmModeFreeObjectProperties(props);
716 props = NULL;
717 }
718
719 uint32_t crtc_index;
720 for (crtc_index = 0; crtc_index < res->count_crtcs; crtc_index++) {
721 ctx->crtcs[crtc_index].crtc_id = res->crtcs[crtc_index];
722 props =
723 drmModeObjectGetProperties(fd, res->crtcs[crtc_index], DRM_MODE_OBJECT_CRTC);
724 get_crtc_props(fd, &ctx->crtcs[crtc_index], props);
725
726 drmModeFreeObjectProperties(props);
727 props = NULL;
728 }
729
730 uint32_t overlay_idx, primary_idx, cursor_idx, idx;
731
732 for (uint32_t plane_index = 0; plane_index < plane_res->count_planes; plane_index++) {
733 drmModePlane *plane = drmModeGetPlane(fd, plane_res->planes[plane_index]);
734 if (plane == NULL) {
735 bs_debug_error("failed to get plane id %u", plane_res->planes[plane_index]);
736 continue;
737 }
738
739 uint32_t crtc_mask = 0;
740
741 drmModeObjectPropertiesPtr props = drmModeObjectGetProperties(
742 fd, plane_res->planes[plane_index], DRM_MODE_OBJECT_PLANE);
743
744 for (crtc_index = 0; crtc_index < res->count_crtcs; crtc_index++) {
745 crtc_mask = (1 << crtc_index);
746 if (plane->possible_crtcs & crtc_mask) {
747 struct atomictest_crtc *crtc = &ctx->crtcs[crtc_index];
748 cursor_idx = crtc->num_cursor;
749 primary_idx = crtc->num_primary;
750 overlay_idx = crtc->num_overlay;
751 idx = cursor_idx + primary_idx + overlay_idx;
752 copy_drm_plane(&crtc->planes[idx].drm_plane, plane);
753 get_plane_props(fd, &crtc->planes[idx], props);
754 switch (crtc->planes[idx].type.value) {
755 case DRM_PLANE_TYPE_OVERLAY:
756 crtc->overlay_idx[overlay_idx] = idx;
757 crtc->num_overlay++;
758 break;
759 case DRM_PLANE_TYPE_PRIMARY:
760 crtc->primary_idx[primary_idx] = idx;
761 crtc->num_primary++;
762 break;
763 case DRM_PLANE_TYPE_CURSOR:
764 crtc->cursor_idx[cursor_idx] = idx;
765 crtc->num_cursor++;
766 break;
767 default:
768 bs_debug_error("invalid plane type returned");
769 return NULL;
770 }
771
772 /*
773 * The DRM UAPI states that cursor and overlay framebuffers may be
774 * present after a CRTC disable, so zero this out so we can get a
775 * clean slate.
776 */
777 crtc->planes[idx].fb_id.value = 0;
778 }
779 }
780
781 drmModeFreePlane(plane);
782 drmModeFreeObjectProperties(props);
783 props = NULL;
784 }
785
786 drmModeFreePlaneResources(plane_res);
787 drmModeFreeResources(res);
788 return ctx;
789}
790
791static int test_multiple_planes(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
792{
793 struct atomictest_plane *primary, *overlay, *cursor;
794 for (uint32_t i = 0; i < crtc->num_primary; i++) {
795 bool video = true;
796 uint32_t x, y;
797 for (uint32_t j = 0; j < crtc->num_overlay; j++) {
798 overlay = get_plane(crtc, j, DRM_PLANE_TYPE_OVERLAY);
799 x = crtc->width >> (j + 2);
800 y = crtc->height >> (j + 2);
801 // drmModeAddFB2 requires the height and width are even for sub-sampled YUV
802 // formats.
803 x = BS_ALIGN(x, 2);
804 y = BS_ALIGN(y, 2);
805 if (video && !init_yuv_plane(ctx, overlay, x, y, x, y, crtc->crtc_id)) {
806 CHECK_RESULT(draw_to_plane(ctx->mapper, overlay, DRAW_STRIPE));
807 video = false;
808 } else {
809 CHECK_RESULT(init_plane(ctx, overlay, DRM_FORMAT_XRGB8888, x, y, x,
810 y, crtc->crtc_id));
811 CHECK_RESULT(draw_to_plane(ctx->mapper, overlay, DRAW_LINES));
812 }
813 }
814
815 for (uint32_t j = 0; j < crtc->num_cursor; j++) {
816 x = crtc->width >> (j + 2);
817 y = crtc->height >> (j + 2);
818 cursor = get_plane(crtc, j, DRM_PLANE_TYPE_CURSOR);
819 CHECK_RESULT(init_plane(ctx, cursor, DRM_FORMAT_ARGB8888, x, y, CURSOR_SIZE,
820 CURSOR_SIZE, crtc->crtc_id));
821 CHECK_RESULT(draw_to_plane(ctx->mapper, cursor, DRAW_CURSOR));
822 }
823
824 primary = get_plane(crtc, i, DRM_PLANE_TYPE_PRIMARY);
825 CHECK_RESULT(init_plane(ctx, primary, DRM_FORMAT_XRGB8888, 0, 0, crtc->width,
826 crtc->height, crtc->crtc_id));
827 CHECK_RESULT(draw_to_plane(ctx->mapper, primary, DRAW_ELLIPSE));
828
829 uint32_t num_planes = crtc->num_primary + crtc->num_cursor + crtc->num_overlay;
830 int done = 0;
831 struct atomictest_plane *plane;
832 while (!done) {
833 done = 1;
834 for (uint32_t j = 0; j < num_planes; j++) {
835 plane = &crtc->planes[j];
836 if (plane->type.value != DRM_PLANE_TYPE_PRIMARY)
837 done &= move_plane(ctx, crtc, plane, 20, 20);
838 }
839
840 CHECK_RESULT(commit(ctx));
841 usleep(1e6 / 60);
842 }
843
844 CHECK_RESULT(commit(ctx));
845 usleep(1e6);
846
847 /* Disable primary plane and verify overlays show up. */
848 CHECK_RESULT(disable_plane(ctx, primary));
849 CHECK_RESULT(commit(ctx));
850 usleep(1e6);
851 }
852
853 return 0;
854}
855
856static int test_video_overlay(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
857{
858 struct atomictest_plane *overlay;
859 for (uint32_t i = 0; i < crtc->num_overlay; i++) {
860 overlay = get_plane(crtc, i, DRM_PLANE_TYPE_OVERLAY);
861 if (init_yuv_plane(ctx, overlay, 0, 0, 800, 800, crtc->crtc_id))
862 continue;
863
864 CHECK_RESULT(draw_to_plane(ctx->mapper, overlay, DRAW_STRIPE));
865 while (!move_plane(ctx, crtc, overlay, 20, 20)) {
866 CHECK_RESULT(commit(ctx));
867 usleep(1e6 / 60);
868 }
869 }
870
871 return 0;
872}
873
874static int test_orientation(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
875{
876 struct atomictest_plane *primary, *overlay;
877 for (uint32_t i = 0; i < crtc->num_overlay; i++) {
878 overlay = get_plane(crtc, i, DRM_PLANE_TYPE_OVERLAY);
879 if (!overlay->rotation.pid)
880 continue;
881
882 CHECK_RESULT(init_plane(ctx, overlay, DRM_FORMAT_XRGB8888, 0, 0, crtc->width,
883 crtc->height, crtc->crtc_id));
884
885 overlay->rotation.value = DRM_ROTATE_0;
886 set_plane_props(overlay, ctx->pset);
887 CHECK_RESULT(draw_to_plane(ctx->mapper, overlay, DRAW_LINES));
888 CHECK_RESULT(commit(ctx));
889 usleep(1e6);
890
891 overlay->rotation.value = DRM_REFLECT_Y;
892 set_plane_props(overlay, ctx->pset);
893 if (!test_commit(ctx)) {
894 CHECK_RESULT(commit(ctx));
895 usleep(1e6);
896 }
897
898 CHECK_RESULT(disable_plane(ctx, overlay));
899 }
900
901 for (uint32_t i = 0; i < crtc->num_primary; i++) {
902 primary = get_plane(crtc, i, DRM_PLANE_TYPE_PRIMARY);
903 if (!primary->rotation.pid)
904 continue;
905
906 CHECK_RESULT(init_plane(ctx, primary, DRM_FORMAT_XRGB8888, 0, 0, crtc->width,
907 crtc->height, crtc->crtc_id));
908
909 primary->rotation.value = DRM_ROTATE_0;
910 set_plane_props(primary, ctx->pset);
911 CHECK_RESULT(draw_to_plane(ctx->mapper, primary, DRAW_LINES));
912 CHECK_RESULT(commit(ctx));
913 usleep(1e6);
914
915 primary->rotation.value = DRM_REFLECT_Y;
916 set_plane_props(primary, ctx->pset);
917 if (!test_commit(ctx)) {
918 CHECK_RESULT(commit(ctx));
919 usleep(1e6);
920 }
921
922 CHECK_RESULT(disable_plane(ctx, primary));
923 }
924
925 return 0;
926}
927
928static int test_plane_ctm(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
929{
930 struct atomictest_plane *primary, *overlay;
931 /*
932 * The blob for the PLANE_CTM propery is a drm_color_ctm.
933 * drm_color_ctm contains a 3x3 u64 matrix, where every element
934 * represents a S31.32 fixed point number.
935 */
936 int64_t identity_ctm[9] = { 0x100000000, 0x0, 0x0, 0x0, 0x100000000,
937 0x0, 0x0, 0x0, 0x100000000 };
938 int64_t red_shift_ctm[9] = { 0x140000000, 0x0, 0x0, 0x0, 0xC0000000,
939 0x0, 0x0, 0x0, 0xC0000000 };
940
941 for (uint32_t i = 0; i < crtc->num_overlay; i++) {
942 overlay = get_plane(crtc, i, DRM_PLANE_TYPE_OVERLAY);
943 if (!overlay->ctm.pid)
944 continue;
945
946 CHECK_RESULT(init_plane(ctx, overlay, DRM_FORMAT_XRGB8888, 0, 0, crtc->width,
947 crtc->height, crtc->crtc_id));
948
949 CHECK_RESULT(drmModeCreatePropertyBlob(ctx->fd, identity_ctm, sizeof(identity_ctm),
950 &overlay->ctm.value));
951 set_plane_props(overlay, ctx->pset);
952 CHECK_RESULT(draw_to_plane(ctx->mapper, overlay, DRAW_LINES));
953 CHECK_RESULT(commit(ctx));
954 CHECK_RESULT(drmModeDestroyPropertyBlob(ctx->fd, overlay->ctm.value));
955 usleep(1e6);
956
957 CHECK_RESULT(drmModeCreatePropertyBlob(ctx->fd, red_shift_ctm,
958 sizeof(red_shift_ctm), &overlay->ctm.value));
959 set_plane_props(overlay, ctx->pset);
960 CHECK_RESULT(commit(ctx));
961 CHECK_RESULT(drmModeDestroyPropertyBlob(ctx->fd, overlay->ctm.value));
962 usleep(1e6);
963
964 CHECK_RESULT(disable_plane(ctx, overlay));
965 }
966
967 for (uint32_t i = 0; i < crtc->num_primary; i++) {
968 primary = get_plane(crtc, i, DRM_PLANE_TYPE_PRIMARY);
969 if (!primary->ctm.pid)
970 continue;
971
972 CHECK_RESULT(init_plane(ctx, primary, DRM_FORMAT_XRGB8888, 0, 0, crtc->width,
973 crtc->height, crtc->crtc_id));
974 CHECK_RESULT(drmModeCreatePropertyBlob(ctx->fd, identity_ctm, sizeof(identity_ctm),
975 &primary->ctm.value));
976 set_plane_props(primary, ctx->pset);
977 CHECK_RESULT(draw_to_plane(ctx->mapper, primary, DRAW_LINES));
978 CHECK_RESULT(commit(ctx));
979 CHECK_RESULT(drmModeDestroyPropertyBlob(ctx->fd, primary->ctm.value));
980 usleep(1e6);
981
982 CHECK_RESULT(drmModeCreatePropertyBlob(ctx->fd, red_shift_ctm,
983 sizeof(red_shift_ctm), &primary->ctm.value));
984 set_plane_props(primary, ctx->pset);
985 CHECK_RESULT(commit(ctx));
986 CHECK_RESULT(drmModeDestroyPropertyBlob(ctx->fd, primary->ctm.value));
987 usleep(1e6);
988
989 CHECK_RESULT(disable_plane(ctx, primary));
990 }
991
992 return 0;
993}
994
995static int test_fullscreen_video(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
996{
997 struct atomictest_plane *primary;
998 for (uint32_t i = 0; i < crtc->num_primary; i++) {
999 primary = get_plane(crtc, i, DRM_PLANE_TYPE_PRIMARY);
1000 if (init_yuv_plane(ctx, primary, 0, 0, crtc->width, crtc->height, crtc->crtc_id))
1001 continue;
1002
1003 CHECK_RESULT(draw_to_plane(ctx->mapper, primary, DRAW_STRIPE));
1004 CHECK_RESULT(commit(ctx));
1005 usleep(1e6);
1006 }
1007
1008 return 0;
1009}
1010
1011static int test_disable_primary(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
1012{
1013 struct atomictest_plane *primary, *overlay;
1014 for (uint32_t i = 0; i < crtc->num_primary; i++) {
1015 for (uint32_t j = 0; j < crtc->num_overlay; j++) {
1016 overlay = get_plane(crtc, j, DRM_PLANE_TYPE_OVERLAY);
1017 uint32_t x = crtc->width >> (j + 2);
1018 uint32_t y = crtc->height >> (j + 2);
1019 CHECK_RESULT(init_plane(ctx, overlay, DRM_FORMAT_XRGB8888, x, y, x, y,
1020 crtc->crtc_id));
1021 CHECK_RESULT(draw_to_plane(ctx->mapper, overlay, DRAW_LINES));
1022 }
1023
1024 primary = get_plane(crtc, i, DRM_PLANE_TYPE_PRIMARY);
1025 CHECK_RESULT(init_plane(ctx, primary, DRM_FORMAT_XRGB8888, 0, 0, crtc->width,
1026 crtc->height, crtc->crtc_id));
1027 CHECK_RESULT(draw_to_plane(ctx->mapper, primary, DRAW_ELLIPSE));
1028 CHECK_RESULT(commit(ctx));
1029 usleep(1e6);
1030
1031 /* Disable primary plane. */
1032 disable_plane(ctx, primary);
1033 CHECK_RESULT(commit(ctx));
1034 usleep(1e6);
1035 }
1036
1037 return 0;
1038}
1039
1040static int test_overlay_pageflip(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
1041{
1042 struct atomictest_plane *overlay;
1043 for (uint32_t i = 0; i < crtc->num_overlay; i++) {
1044 overlay = get_plane(crtc, i, DRM_PLANE_TYPE_OVERLAY);
1045 CHECK_RESULT(pageflip_formats(ctx, crtc, overlay));
1046 }
1047
1048 return 0;
1049}
1050
1051static int test_overlay_downscaling(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
1052{
1053 struct atomictest_plane *overlay;
1054 uint32_t w = BS_ALIGN(crtc->width / 2, 2);
1055 uint32_t h = BS_ALIGN(crtc->height / 2, 2);
1056 for (uint32_t i = 0; i < crtc->num_overlay; i++) {
1057 overlay = get_plane(crtc, i, DRM_PLANE_TYPE_OVERLAY);
1058 if (init_yuv_plane(ctx, overlay, 0, 0, w, h, crtc->crtc_id)) {
1059 CHECK_RESULT(init_plane(ctx, overlay, DRM_FORMAT_XRGB8888, 0, 0, w, h,
1060 crtc->crtc_id));
1061 }
1062
1063 CHECK_RESULT(draw_to_plane(ctx->mapper, overlay, DRAW_LINES));
1064 CHECK_RESULT(commit(ctx));
1065 usleep(1e6);
1066
1067 while (!scale_plane(ctx, crtc, overlay, -.1f, -.1f) && !test_commit(ctx)) {
1068 CHECK_RESULT(commit(ctx));
1069 usleep(1e6);
1070 }
1071
1072 disable_plane(ctx, overlay);
1073 }
1074
1075 return 0;
1076}
1077
1078static int test_overlay_upscaling(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
1079{
1080 struct atomictest_plane *overlay;
1081 uint32_t w = BS_ALIGN(crtc->width / 4, 2);
1082 uint32_t h = BS_ALIGN(crtc->height / 4, 2);
1083 for (uint32_t i = 0; i < crtc->num_overlay; i++) {
1084 overlay = get_plane(crtc, i, DRM_PLANE_TYPE_OVERLAY);
1085 if (init_yuv_plane(ctx, overlay, 0, 0, w, h, crtc->crtc_id)) {
1086 CHECK_RESULT(init_plane(ctx, overlay, DRM_FORMAT_XRGB8888, 0, 0, w, h,
1087 crtc->crtc_id));
1088 }
1089
1090 CHECK_RESULT(draw_to_plane(ctx->mapper, overlay, DRAW_LINES));
1091 CHECK_RESULT(commit(ctx));
1092 usleep(1e6);
1093
1094 while (!scale_plane(ctx, crtc, overlay, .1f, .1f) && !test_commit(ctx)) {
1095 CHECK_RESULT(commit(ctx));
1096 usleep(1e6);
1097 }
1098
1099 disable_plane(ctx, overlay);
1100 }
1101
1102 return 0;
1103}
1104
1105static int test_primary_pageflip(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
1106{
1107 struct atomictest_plane *primary;
1108 for (uint32_t i = 0; i < crtc->num_primary; i++) {
1109 primary = get_plane(crtc, i, DRM_PLANE_TYPE_PRIMARY);
1110 CHECK_RESULT(pageflip_formats(ctx, crtc, primary));
1111 }
1112
1113 return 0;
1114}
1115
1116static const struct atomictest_testcase cases[] = {
1117 { "disable_primary", test_disable_primary },
1118 { "fullscreen_video", test_fullscreen_video },
1119 { "multiple_planes", test_multiple_planes },
1120 { "overlay_pageflip", test_overlay_pageflip },
1121 { "overlay_downscaling", test_overlay_downscaling },
1122 { "overlay_upscaling", test_overlay_upscaling },
1123 { "primary_pageflip", test_primary_pageflip },
1124 { "video_overlay", test_video_overlay },
1125 { "orientation", test_orientation },
1126 /* CTM stands for Color Transform Matrix. */
1127 { "plane_ctm", test_plane_ctm },
1128};
1129
1130static int run_testcase(struct atomictest_context *ctx, struct atomictest_crtc *crtc,
1131 test_function func)
1132{
1133 int cursor = drmModeAtomicGetCursor(ctx->pset);
1134 uint32_t num_planes = crtc->num_primary + crtc->num_cursor + crtc->num_overlay;
1135
1136 int ret = func(ctx, crtc);
1137
1138 for (uint32_t i = 0; i < num_planes; i++)
1139 disable_plane(ctx, &crtc->planes[i]);
1140
1141 drmModeAtomicSetCursor(ctx->pset, cursor);
1142
1143 CHECK_RESULT(commit(ctx));
1144 usleep(1e6 / 60);
1145
1146 return ret;
1147}
1148
1149static int run_atomictest(const char *name, uint32_t crtc_mask)
1150{
1151 int ret = 0;
1152 uint32_t num_run = 0;
1153 int fd = bs_drm_open_main_display();
1154 CHECK_RESULT(fd);
1155
1156 gbm = gbm_create_device(fd);
1157 if (!gbm) {
1158 bs_debug_error("failed to create gbm device");
1159 ret = -1;
1160 goto destroy_fd;
1161 }
1162
1163 ret = drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
1164 if (ret) {
1165 bs_debug_error("failed to enable DRM_CLIENT_CAP_UNIVERSAL_PLANES");
1166 ret = -1;
1167 goto destroy_gbm_device;
1168 }
1169
1170 ret = drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1);
1171 if (ret) {
1172 bs_debug_error("failed to enable DRM_CLIENT_CAP_ATOMIC");
1173 ret = -1;
1174 goto destroy_gbm_device;
1175 }
1176
1177 struct atomictest_context *ctx = query_kms(fd);
1178 if (!ctx) {
1179 bs_debug_error("querying atomictest failed.");
1180 ret = -1;
1181 goto destroy_gbm_device;
1182 }
1183
1184 struct atomictest_crtc *crtc;
1185 for (uint32_t crtc_index = 0; crtc_index < ctx->num_crtcs; crtc_index++) {
1186 crtc = &ctx->crtcs[crtc_index];
1187 if (!((1 << crtc_index) & crtc_mask))
1188 continue;
1189
1190 for (uint32_t i = 0; i < BS_ARRAY_LEN(cases); i++) {
1191 if (strcmp(cases[i].name, name) && strcmp("all", name))
1192 continue;
1193
1194 num_run++;
1195 ret = enable_crtc(ctx, crtc);
1196 if (ret)
1197 goto out;
1198
1199 ret = run_testcase(ctx, crtc, cases[i].test_func);
1200 if (ret)
1201 goto out;
1202
1203 ret = disable_crtc(ctx, crtc);
1204 if (ret)
1205 goto out;
1206 }
1207 }
1208
1209 ret = (num_run == 0);
1210
1211out:
1212 free_context(ctx);
1213destroy_gbm_device:
1214 gbm_device_destroy(gbm);
1215destroy_fd:
1216 close(fd);
1217
1218 return ret;
1219}
1220
1221static const struct option longopts[] = {
1222 { "crtc", required_argument, NULL, 'c' },
1223 { "test_name", required_argument, NULL, 't' },
1224 { "help", no_argument, NULL, 'h' },
1225 { 0, 0, 0, 0 },
1226};
1227
1228static void print_help(const char *argv0)
1229{
1230 printf("usage: %s -t <test_name> -c <crtc_index>\n", argv0);
1231 printf("A valid name test is one the following:\n");
1232 for (uint32_t i = 0; i < BS_ARRAY_LEN(cases); i++)
1233 printf("%s\n", cases[i].name);
1234 printf("all\n");
1235}
1236
1237int main(int argc, char **argv)
1238{
1239 int c;
1240 char *name = NULL;
1241 int32_t crtc_idx = -1;
1242 uint32_t crtc_mask = ~0;
1243 while ((c = getopt_long(argc, argv, "c:t:h", longopts, NULL)) != -1) {
1244 switch (c) {
1245 case 'c':
1246 if (sscanf(optarg, "%d", &crtc_idx) != 1)
1247 goto print;
1248 break;
1249 case 't':
1250 if (name) {
1251 free(name);
1252 name = NULL;
1253 }
1254
1255 name = strdup(optarg);
1256 break;
1257 case 'h':
1258 goto print;
1259 default:
1260 goto print;
1261 }
1262 }
1263
1264 if (!name)
1265 goto print;
1266
1267 if (crtc_idx >= 0)
1268 crtc_mask = 1 << crtc_idx;
1269
1270 int ret = run_atomictest(name, crtc_mask);
1271 if (ret == 0)
1272 printf("[ PASSED ] atomictest.%s\n", name);
1273 else if (ret < 0)
1274 printf("[ FAILED ] atomictest.%s\n", name);
1275
1276 free(name);
1277
1278 if (ret > 0)
1279 goto print;
1280
1281 return ret;
1282
1283print:
1284 print_help(argv[0]);
1285 return 0;
1286}