blob: e36f054eaa3e7c9538fb1f19d778d161dd8da9a4 [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
Yalong Liu257f41b2018-05-04 10:15:20 +080049#define TEST_COMMIT_FAIL 1
50
Yalong Liu1df84372018-01-24 17:10:12 +080051static const uint32_t yuv_formats[] = {
Yalong Liu257f41b2018-05-04 10:15:20 +080052 DRM_FORMAT_NV12,
53 DRM_FORMAT_YVU420,
Yalong Liu1df84372018-01-24 17:10:12 +080054};
55
Yalong Liu257f41b2018-05-04 10:15:20 +080056/*
57 * The blob for the CTM propery is a drm_color_ctm.
58 * drm_color_ctm contains a 3x3 u64 matrix. Every element is represented as
59 * sign and U31.32. The sign is the MSB.
60 */
61// clang-format off
62static int64_t identity_ctm[9] = {
63 0x100000000, 0x0, 0x0,
64 0x0, 0x100000000, 0x0,
65 0x0, 0x0, 0x100000000
66};
67static int64_t red_shift_ctm[9] = {
68 0x140000000, 0x0, 0x0,
69 0x0, 0xC0000000, 0x0,
70 0x0, 0x0, 0xC0000000
71};
72// clang-format on
73
74static bool automatic = false;
Yalong Liu1df84372018-01-24 17:10:12 +080075static struct gbm_device *gbm = NULL;
76
77static void page_flip_handler(int fd, unsigned int sequence, unsigned int tv_sec,
78 unsigned int tv_usec, void *user_data)
79{
80 // Nothing to do.
81}
82
83struct atomictest_property {
84 uint32_t pid;
85 uint32_t value;
86};
87
88struct atomictest_plane {
89 drmModePlane drm_plane;
90 struct gbm_bo *bo;
91
92 uint32_t format_idx;
93
94 /* Properties. */
95 struct atomictest_property crtc_id;
96 struct atomictest_property crtc_x;
97 struct atomictest_property crtc_y;
98 struct atomictest_property crtc_w;
99 struct atomictest_property crtc_h;
100 struct atomictest_property fb_id;
101 struct atomictest_property src_x;
102 struct atomictest_property src_y;
103 struct atomictest_property src_w;
104 struct atomictest_property src_h;
105 struct atomictest_property type;
106 struct atomictest_property rotation;
107 struct atomictest_property ctm;
108};
109
110struct atomictest_connector {
111 uint32_t connector_id;
112 struct atomictest_property crtc_id;
113 struct atomictest_property edid;
114 struct atomictest_property dpms;
115};
116
117struct atomictest_crtc {
118 uint32_t crtc_id;
119 uint32_t width;
120 uint32_t height;
121 uint32_t *primary_idx;
122 uint32_t *cursor_idx;
123 uint32_t *overlay_idx;
124 uint32_t num_primary;
125 uint32_t num_cursor;
126 uint32_t num_overlay;
127
128 struct atomictest_plane *planes;
129 struct atomictest_property mode_id;
130 struct atomictest_property active;
Yalong Liu257f41b2018-05-04 10:15:20 +0800131 struct atomictest_property ctm;
132 struct atomictest_property gamma_lut;
133 struct atomictest_property gamma_lut_size;
Yalong Liu1df84372018-01-24 17:10:12 +0800134};
135
136struct atomictest_mode {
137 uint32_t height;
138 uint32_t width;
139 uint32_t id;
140};
141
142struct atomictest_context {
143 int fd;
144 uint32_t num_crtcs;
145 uint32_t num_connectors;
146 uint32_t num_modes;
147
148 struct atomictest_connector *connectors;
149 struct atomictest_crtc *crtcs;
150 struct atomictest_mode *modes;
151 drmModeAtomicReqPtr pset;
152 drmEventContext drm_event_ctx;
153
154 struct bs_mapper *mapper;
155};
156
157typedef int (*test_function)(struct atomictest_context *ctx, struct atomictest_crtc *crtc);
158
159struct atomictest_testcase {
160 const char *name;
161 test_function test_func;
162};
163
164// clang-format off
165enum draw_format_type {
166 DRAW_NONE = 0,
167 DRAW_STRIPE = 1,
Yalong Liu257f41b2018-05-04 10:15:20 +0800168 DRAW_TRANSPARENT_HOLE = 2,
169 DRAW_ELLIPSE = 3,
170 DRAW_CURSOR = 4,
171 DRAW_LINES = 5,
Yalong Liu1df84372018-01-24 17:10:12 +0800172};
173// clang-format on
174
175static int32_t get_format_idx(struct atomictest_plane *plane, uint32_t format)
176{
177 for (int32_t i = 0; i < plane->drm_plane.count_formats; i++)
178 if (plane->drm_plane.formats[i] == format)
179 return i;
180 return -1;
181}
182
183static void copy_drm_plane(drmModePlane *dest, drmModePlane *src)
184{
185 memcpy(dest, src, sizeof(drmModePlane));
186 dest->formats = calloc(src->count_formats, sizeof(uint32_t));
187 memcpy(dest->formats, src->formats, src->count_formats * sizeof(uint32_t));
188}
189
190static struct atomictest_plane *get_plane(struct atomictest_crtc *crtc, uint32_t idx, uint64_t type)
191{
192 uint32_t index;
193 switch (type) {
194 case DRM_PLANE_TYPE_OVERLAY:
195 index = crtc->overlay_idx[idx];
196 break;
197 case DRM_PLANE_TYPE_PRIMARY:
198 index = crtc->primary_idx[idx];
199 break;
200 case DRM_PLANE_TYPE_CURSOR:
201 index = crtc->cursor_idx[idx];
202 break;
203 default:
204 bs_debug_error("invalid plane type returned");
205 return NULL;
206 }
207
208 return &crtc->planes[index];
209}
210
211static int draw_to_plane(struct bs_mapper *mapper, struct atomictest_plane *plane,
212 enum draw_format_type pattern)
213{
214 struct gbm_bo *bo = plane->bo;
215 uint32_t format = gbm_bo_get_format(bo);
216 const struct bs_draw_format *draw_format = bs_get_draw_format(format);
217
218 if (draw_format && pattern) {
219 switch (pattern) {
220 case DRAW_STRIPE:
221 CHECK(bs_draw_stripe(mapper, bo, draw_format));
222 break;
Yalong Liu257f41b2018-05-04 10:15:20 +0800223 case DRAW_TRANSPARENT_HOLE:
224 CHECK(bs_draw_transparent_hole(mapper, bo, draw_format));
225 break;
Yalong Liu1df84372018-01-24 17:10:12 +0800226 case DRAW_ELLIPSE:
227 CHECK(bs_draw_ellipse(mapper, bo, draw_format, 0));
228 break;
229 case DRAW_CURSOR:
230 CHECK(bs_draw_cursor(mapper, bo, draw_format));
231 break;
232 case DRAW_LINES:
233 CHECK(bs_draw_lines(mapper, bo, draw_format));
234 break;
235 default:
236 bs_debug_error("invalid draw type");
237 return -1;
238 }
239 } else {
240 // DRM_FORMAT_RGB565 --> red, DRM_FORMAT_BGR565 --> blue,
241 // everything else --> something
242 void *map_data;
243 uint16_t value = 0xF800;
244 void *addr = bs_mapper_map(mapper, bo, 0, &map_data);
245 uint32_t num_shorts = gbm_bo_get_plane_size(bo, 0) / sizeof(uint16_t);
246 uint16_t *pixel = (uint16_t *)addr;
247
248 CHECK(addr);
249 for (uint32_t i = 0; i < num_shorts; i++)
250 pixel[i] = value;
251
252 bs_mapper_unmap(mapper, bo, map_data);
253 }
254
255 return 0;
256}
257
258static int get_prop(int fd, drmModeObjectPropertiesPtr props, const char *name,
259 struct atomictest_property *bs_prop)
260{
261 /* Property ID should always be > 0. */
262 bs_prop->pid = 0;
263 drmModePropertyPtr prop;
264 for (uint32_t i = 0; i < props->count_props; i++) {
265 if (bs_prop->pid)
266 break;
267
268 prop = drmModeGetProperty(fd, props->props[i]);
269 if (prop) {
270 if (!strcmp(prop->name, name)) {
271 bs_prop->pid = prop->prop_id;
272 bs_prop->value = props->prop_values[i];
273 }
274 drmModeFreeProperty(prop);
275 }
276 }
277
278 return (bs_prop->pid == 0) ? -1 : 0;
279}
280
281static int get_connector_props(int fd, struct atomictest_connector *connector,
282 drmModeObjectPropertiesPtr props)
283{
284 CHECK_RESULT(get_prop(fd, props, "CRTC_ID", &connector->crtc_id));
285 CHECK_RESULT(get_prop(fd, props, "EDID", &connector->edid));
286 CHECK_RESULT(get_prop(fd, props, "DPMS", &connector->dpms));
287 return 0;
288}
289
290static int get_crtc_props(int fd, struct atomictest_crtc *crtc, drmModeObjectPropertiesPtr props)
291{
292 CHECK_RESULT(get_prop(fd, props, "MODE_ID", &crtc->mode_id));
293 CHECK_RESULT(get_prop(fd, props, "ACTIVE", &crtc->active));
Yalong Liu257f41b2018-05-04 10:15:20 +0800294
295 /*
296 * The atomic API makes no guarantee a property is present in object. This test
297 * requires the above common properties since a plane is undefined without them.
298 * Other properties (i.e: ctm) are optional.
299 */
300 get_prop(fd, props, "CTM", &crtc->ctm);
301 get_prop(fd, props, "GAMMA_LUT", &crtc->gamma_lut);
302 get_prop(fd, props, "GAMMA_LUT_SIZE", &crtc->gamma_lut_size);
Yalong Liu1df84372018-01-24 17:10:12 +0800303 return 0;
304}
305
306static int get_plane_props(int fd, struct atomictest_plane *plane, drmModeObjectPropertiesPtr props)
307{
308 CHECK_RESULT(get_prop(fd, props, "CRTC_ID", &plane->crtc_id));
309 CHECK_RESULT(get_prop(fd, props, "FB_ID", &plane->fb_id));
310 CHECK_RESULT(get_prop(fd, props, "CRTC_X", &plane->crtc_x));
311 CHECK_RESULT(get_prop(fd, props, "CRTC_Y", &plane->crtc_y));
312 CHECK_RESULT(get_prop(fd, props, "CRTC_W", &plane->crtc_w));
313 CHECK_RESULT(get_prop(fd, props, "CRTC_H", &plane->crtc_h));
314 CHECK_RESULT(get_prop(fd, props, "SRC_X", &plane->src_x));
315 CHECK_RESULT(get_prop(fd, props, "SRC_Y", &plane->src_y));
316 CHECK_RESULT(get_prop(fd, props, "SRC_W", &plane->src_w));
317 CHECK_RESULT(get_prop(fd, props, "SRC_H", &plane->src_h));
318 CHECK_RESULT(get_prop(fd, props, "type", &plane->type));
319
320 /*
321 * The atomic API makes no guarantee a property is present in object. This test
322 * requires the above common properties since a plane is undefined without them.
323 * Other properties (i.e: rotation and ctm) are optional.
324 */
325 get_prop(fd, props, "rotation", &plane->rotation);
326 get_prop(fd, props, "PLANE_CTM", &plane->ctm);
327 return 0;
328}
329
330int set_connector_props(struct atomictest_connector *conn, drmModeAtomicReqPtr pset)
331{
332 uint32_t id = conn->connector_id;
333 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, conn->crtc_id.pid, conn->crtc_id.value));
334 return 0;
335}
336
337int set_crtc_props(struct atomictest_crtc *crtc, drmModeAtomicReqPtr pset)
338{
339 uint32_t id = crtc->crtc_id;
340 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, crtc->mode_id.pid, crtc->mode_id.value));
341 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, crtc->active.pid, crtc->active.value));
Yalong Liu257f41b2018-05-04 10:15:20 +0800342 if (crtc->ctm.pid)
343 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, crtc->ctm.pid, crtc->ctm.value));
344 if (crtc->gamma_lut.pid)
345 CHECK_RESULT(
346 drmModeAtomicAddProperty(pset, id, crtc->gamma_lut.pid, crtc->gamma_lut.value));
Yalong Liu1df84372018-01-24 17:10:12 +0800347 return 0;
348}
349
350int set_plane_props(struct atomictest_plane *plane, drmModeAtomicReqPtr pset)
351{
352 uint32_t id = plane->drm_plane.plane_id;
353 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->crtc_id.pid, plane->crtc_id.value));
354 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->fb_id.pid, plane->fb_id.value));
355 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->crtc_x.pid, plane->crtc_x.value));
356 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->crtc_y.pid, plane->crtc_y.value));
357 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->crtc_w.pid, plane->crtc_w.value));
358 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->crtc_h.pid, plane->crtc_h.value));
359 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->src_x.pid, plane->src_x.value));
360 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->src_y.pid, plane->src_y.value));
361 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->src_w.pid, plane->src_w.value));
362 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->src_h.pid, plane->src_h.value));
363 if (plane->rotation.pid)
364 CHECK_RESULT(
365 drmModeAtomicAddProperty(pset, id, plane->rotation.pid, plane->rotation.value));
366 if (plane->ctm.pid)
367 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->ctm.pid, plane->ctm.value));
368
369 return 0;
370}
371
372static int remove_plane_fb(struct atomictest_context *ctx, struct atomictest_plane *plane)
373{
374 if (plane->bo && plane->fb_id.value) {
375 CHECK_RESULT(drmModeRmFB(ctx->fd, plane->fb_id.value));
376 gbm_bo_destroy(plane->bo);
377 plane->bo = NULL;
378 plane->fb_id.value = 0;
379 }
380
381 return 0;
382}
383
384static int add_plane_fb(struct atomictest_context *ctx, struct atomictest_plane *plane)
385{
386 if (plane->format_idx < plane->drm_plane.count_formats) {
387 CHECK_RESULT(remove_plane_fb(ctx, plane));
388 uint32_t flags = (plane->type.value == DRM_PLANE_TYPE_CURSOR) ? GBM_BO_USE_CURSOR
389 : GBM_BO_USE_SCANOUT;
390 flags |= GBM_BO_USE_SW_WRITE_RARELY;
391 /* TODO(gsingh): add create with modifiers option. */
392 plane->bo = gbm_bo_create(gbm, plane->crtc_w.value, plane->crtc_h.value,
393 plane->drm_plane.formats[plane->format_idx], flags);
394
395 CHECK(plane->bo);
396 plane->fb_id.value = bs_drm_fb_create_gbm(plane->bo);
397 CHECK(plane->fb_id.value);
398 CHECK_RESULT(set_plane_props(plane, ctx->pset));
399 }
400
401 return 0;
402}
403
404static int init_plane(struct atomictest_context *ctx, struct atomictest_plane *plane,
405 uint32_t format, uint32_t x, uint32_t y, uint32_t w, uint32_t h,
406 uint32_t crtc_id)
407{
408 int32_t idx = get_format_idx(plane, format);
409 if (idx < 0)
410 return -EINVAL;
411
412 plane->format_idx = idx;
413 plane->crtc_x.value = x;
414 plane->crtc_y.value = y;
415 plane->crtc_w.value = w;
416 plane->crtc_h.value = h;
417 plane->src_w.value = plane->crtc_w.value << 16;
418 plane->src_h.value = plane->crtc_h.value << 16;
419 plane->crtc_id.value = crtc_id;
420
421 CHECK_RESULT(add_plane_fb(ctx, plane));
422 return 0;
423}
424
Yalong Liu257f41b2018-05-04 10:15:20 +0800425static int init_plane_any_format(struct atomictest_context *ctx, struct atomictest_plane *plane,
426 uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t crtc_id,
427 bool yuv)
Yalong Liu1df84372018-01-24 17:10:12 +0800428{
Yalong Liu257f41b2018-05-04 10:15:20 +0800429 if (yuv) {
430 uint32_t i;
431 for (i = 0; i < BS_ARRAY_LEN(yuv_formats); i++)
432 if (!init_plane(ctx, plane, yuv_formats[i], x, y, w, h, crtc_id))
433 return 0;
434 } else {
435 // XRGB888 works well with our draw code, so try that first.
436 if (!init_plane(ctx, plane, DRM_FORMAT_XRGB8888, x, y, w, h, crtc_id))
Yalong Liu1df84372018-01-24 17:10:12 +0800437 return 0;
438
Yalong Liu257f41b2018-05-04 10:15:20 +0800439 for (uint32_t format_idx = 0; format_idx < plane->drm_plane.count_formats;
440 format_idx++) {
441 if (!gbm_device_is_format_supported(
442 gbm, plane->drm_plane.formats[format_idx], GBM_BO_USE_SCANOUT))
443 continue;
444
445 if (!init_plane(ctx, plane, plane->drm_plane.formats[format_idx], x, y, w,
446 h, crtc_id))
447 return 0;
448 }
449 }
450
Yalong Liu1df84372018-01-24 17:10:12 +0800451 return -EINVAL;
452}
453
454static int disable_plane(struct atomictest_context *ctx, struct atomictest_plane *plane)
455{
456 plane->format_idx = 0;
457 plane->crtc_x.value = 0;
458 plane->crtc_y.value = 0;
459 plane->crtc_w.value = 0;
460 plane->crtc_h.value = 0;
461 plane->src_w.value = 0;
462 plane->src_h.value = 0;
463 plane->crtc_id.value = 0;
464
465 if (plane->rotation.pid)
466 plane->rotation.value = DRM_ROTATE_0;
467 if (plane->ctm.pid)
468 plane->ctm.value = 0;
469
470 CHECK_RESULT(remove_plane_fb(ctx, plane));
471 CHECK_RESULT(set_plane_props(plane, ctx->pset));
472 return 0;
473}
474
475static int move_plane(struct atomictest_context *ctx, struct atomictest_crtc *crtc,
476 struct atomictest_plane *plane, uint32_t dx, uint32_t dy)
477{
478 if (plane->crtc_x.value < (crtc->width - plane->crtc_w.value) &&
479 plane->crtc_y.value < (crtc->height - plane->crtc_h.value)) {
480 plane->crtc_x.value += dx;
481 plane->crtc_y.value += dy;
482 CHECK_RESULT(set_plane_props(plane, ctx->pset));
483 return 0;
484 }
485
486 return -1;
487}
488
489static int scale_plane(struct atomictest_context *ctx, struct atomictest_crtc *crtc,
490 struct atomictest_plane *plane, float dw, float dh)
491{
492 int32_t plane_w = (int32_t)plane->crtc_w.value + dw * plane->crtc_w.value;
493 int32_t plane_h = (int32_t)plane->crtc_h.value + dh * plane->crtc_h.value;
494 if (plane_w > 0 && plane_h > 0 && (plane->crtc_x.value + plane_w < crtc->width) &&
495 (plane->crtc_h.value + plane_h < crtc->height)) {
496 plane->crtc_w.value = BS_ALIGN((uint32_t)plane_w, 2);
497 plane->crtc_h.value = BS_ALIGN((uint32_t)plane_h, 2);
498 CHECK_RESULT(set_plane_props(plane, ctx->pset));
499 return 0;
500 }
501
502 return -1;
503}
504
505static void log(struct atomictest_context *ctx)
506{
507 printf("Committing the following configuration: \n");
508 for (uint32_t i = 0; i < ctx->num_crtcs; i++) {
509 struct atomictest_plane *plane;
510 struct atomictest_crtc *crtc = &ctx->crtcs[i];
511 uint32_t num_planes = crtc->num_primary + crtc->num_cursor + crtc->num_overlay;
512 if (!crtc->active.value)
513 continue;
514
515 printf("----- [CRTC: %u] -----\n", crtc->crtc_id);
516 for (uint32_t j = 0; j < num_planes; j++) {
517 plane = &crtc->planes[j];
518 if (plane->crtc_id.value == crtc->crtc_id && plane->fb_id.value) {
519 uint32_t format = gbm_bo_get_format(plane->bo);
520 char *fourcc = (char *)&format;
521 printf("\t{Plane ID: %u, ", plane->drm_plane.plane_id);
522 printf("Plane format: %c%c%c%c, ", fourcc[0], fourcc[1], fourcc[2],
523 fourcc[3]);
524 printf("Plane type: ");
525 switch (plane->type.value) {
526 case DRM_PLANE_TYPE_OVERLAY:
527 printf("overlay, ");
528 break;
529 case DRM_PLANE_TYPE_PRIMARY:
530 printf("primary, ");
531 break;
532 case DRM_PLANE_TYPE_CURSOR:
533 printf("cursor, ");
534 break;
535 }
536
537 printf("CRTC_X: %u, CRTC_Y: %u, CRTC_W: %u, CRTC_H: %u}\n",
538 plane->crtc_x.value, plane->crtc_y.value,
539 plane->crtc_w.value, plane->crtc_h.value);
540 }
541 }
542 }
543}
544
545static int test_commit(struct atomictest_context *ctx)
546{
Yalong Liu257f41b2018-05-04 10:15:20 +0800547 return drmModeAtomicCommit(ctx->fd, ctx->pset,
548 DRM_MODE_ATOMIC_ALLOW_MODESET | DRM_MODE_ATOMIC_TEST_ONLY, NULL);
Yalong Liu1df84372018-01-24 17:10:12 +0800549}
550
551static int commit(struct atomictest_context *ctx)
552{
553 int ret;
554 fd_set fds;
555 FD_ZERO(&fds);
556 FD_SET(ctx->fd, &fds);
557
558 log(ctx);
559 ret = drmModeAtomicCommit(ctx->fd, ctx->pset,
560 DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
561 CHECK_RESULT(ret);
562 do {
563 ret = select(ctx->fd + 1, &fds, NULL, NULL, NULL);
564 } while (ret == -1 && errno == EINTR);
565
566 CHECK_RESULT(ret);
567 if (FD_ISSET(ctx->fd, &fds))
568 drmHandleEvent(ctx->fd, &ctx->drm_event_ctx);
569
570 return 0;
571}
572
Yalong Liu257f41b2018-05-04 10:15:20 +0800573static int test_and_commit(struct atomictest_context *ctx, uint32_t sleep_micro_secs)
574{
575 sleep_micro_secs = automatic ? 0 : sleep_micro_secs;
576 if (!test_commit(ctx)) {
577 CHECK_RESULT(commit(ctx));
578 usleep(sleep_micro_secs);
579 } else {
580 return TEST_COMMIT_FAIL;
581 }
582
583 return 0;
584}
585
Yalong Liu1df84372018-01-24 17:10:12 +0800586static int pageflip_formats(struct atomictest_context *ctx, struct atomictest_crtc *crtc,
587 struct atomictest_plane *plane)
588{
Yalong Liu257f41b2018-05-04 10:15:20 +0800589 int ret = 0;
Yalong Liu1df84372018-01-24 17:10:12 +0800590 uint32_t flags;
591 for (uint32_t i = 0; i < plane->drm_plane.count_formats; i++) {
592 flags = (plane->type.value == DRM_PLANE_TYPE_CURSOR) ? GBM_BO_USE_CURSOR
593 : GBM_BO_USE_SCANOUT;
594 if (!gbm_device_is_format_supported(gbm, plane->drm_plane.formats[i], flags))
595 continue;
596
597 CHECK_RESULT(init_plane(ctx, plane, plane->drm_plane.formats[i], 0, 0, crtc->width,
598 crtc->height, crtc->crtc_id));
599 CHECK_RESULT(draw_to_plane(ctx->mapper, plane, DRAW_ELLIPSE));
Yalong Liu257f41b2018-05-04 10:15:20 +0800600 ret |= test_and_commit(ctx, 1e6);
Yalong Liu1df84372018-01-24 17:10:12 +0800601
602 // disable, but don't commit, since we can't have an active CRTC without any planes.
603 CHECK_RESULT(disable_plane(ctx, plane));
604 }
605
Yalong Liu257f41b2018-05-04 10:15:20 +0800606 return ret;
Yalong Liu1df84372018-01-24 17:10:12 +0800607}
608
609static uint32_t get_connection(struct atomictest_crtc *crtc, uint32_t crtc_index)
610{
611 uint32_t connector_id = 0;
612 uint32_t crtc_mask = 1u << crtc_index;
613 struct bs_drm_pipe pipe = { 0 };
614 struct bs_drm_pipe_plumber *plumber = bs_drm_pipe_plumber_new();
615 bs_drm_pipe_plumber_crtc_mask(plumber, crtc_mask);
616 if (bs_drm_pipe_plumber_make(plumber, &pipe))
617 connector_id = pipe.connector_id;
618
619 bs_drm_pipe_plumber_destroy(&plumber);
620 return connector_id;
621}
622
623static int enable_crtc(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
624{
625 drmModeAtomicSetCursor(ctx->pset, 0);
626
627 for (uint32_t i = 0; i < ctx->num_connectors; i++) {
628 ctx->connectors[i].crtc_id.value = 0;
629 set_connector_props(&ctx->connectors[i], ctx->pset);
630 }
631
632 for (uint32_t i = 0; i < ctx->num_crtcs; i++) {
633 if (&ctx->crtcs[i] == crtc) {
634 uint32_t connector_id = get_connection(crtc, i);
635 CHECK(connector_id);
636 for (uint32_t j = 0; j < ctx->num_connectors; j++) {
637 if (connector_id == ctx->connectors[j].connector_id) {
638 ctx->connectors[j].crtc_id.value = crtc->crtc_id;
639 set_connector_props(&ctx->connectors[j], ctx->pset);
640 break;
641 }
642 }
643
644 break;
645 }
646 }
647
648 int ret = -EINVAL;
649 int cursor = drmModeAtomicGetCursor(ctx->pset);
650
651 for (uint32_t i = 0; i < ctx->num_modes; i++) {
652 struct atomictest_mode *mode = &ctx->modes[i];
653 drmModeAtomicSetCursor(ctx->pset, cursor);
654
655 crtc->mode_id.value = mode->id;
656 crtc->active.value = 1;
657 crtc->width = mode->width;
658 crtc->height = mode->height;
659
660 set_crtc_props(crtc, ctx->pset);
661 ret = drmModeAtomicCommit(ctx->fd, ctx->pset,
662 DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET,
663 NULL);
664 if (!ret)
665 return 0;
666 }
667
668 bs_debug_error("[CRTC:%d]: failed to find mode", crtc->crtc_id);
669 return ret;
670}
671
672static int disable_crtc(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
673{
674 for (uint32_t i = 0; i < ctx->num_connectors; i++) {
675 ctx->connectors[i].crtc_id.value = 0;
676 set_connector_props(&ctx->connectors[i], ctx->pset);
677 }
678
679 crtc->mode_id.value = 0;
680 crtc->active.value = 0;
Yalong Liu257f41b2018-05-04 10:15:20 +0800681 if (crtc->ctm.pid)
682 crtc->ctm.value = 0;
683 if (crtc->gamma_lut.pid)
684 crtc->gamma_lut.value = 0;
685
Yalong Liu1df84372018-01-24 17:10:12 +0800686 set_crtc_props(crtc, ctx->pset);
687 int ret = drmModeAtomicCommit(ctx->fd, ctx->pset, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
688 CHECK_RESULT(ret);
689 return ret;
690}
691
692static struct atomictest_context *new_context(uint32_t num_connectors, uint32_t num_crtcs,
693 uint32_t num_planes)
694{
695 struct atomictest_context *ctx = calloc(1, sizeof(*ctx));
696
697 ctx->mapper = bs_mapper_gem_new();
698 if (ctx->mapper == NULL) {
699 bs_debug_error("failed to create mapper object");
700 free(ctx);
701 return NULL;
702 }
703
704 ctx->connectors = calloc(num_connectors, sizeof(*ctx->connectors));
705 ctx->crtcs = calloc(num_crtcs, sizeof(*ctx->crtcs));
706 for (uint32_t i = 0; i < num_crtcs; i++) {
707 ctx->crtcs[i].planes = calloc(num_planes, sizeof(*ctx->crtcs[i].planes));
708 ctx->crtcs[i].overlay_idx = calloc(num_planes, sizeof(uint32_t));
709 ctx->crtcs[i].primary_idx = calloc(num_planes, sizeof(uint32_t));
710 ctx->crtcs[i].cursor_idx = calloc(num_planes, sizeof(uint32_t));
711 }
712
713 ctx->num_connectors = num_connectors;
714 ctx->num_crtcs = num_crtcs;
715 ctx->num_modes = 0;
716 ctx->modes = NULL;
717 ctx->pset = drmModeAtomicAlloc();
718 ctx->drm_event_ctx.version = DRM_EVENT_CONTEXT_VERSION;
719 ctx->drm_event_ctx.page_flip_handler = page_flip_handler;
720
721 return ctx;
722}
723
724static void free_context(struct atomictest_context *ctx)
725{
726 for (uint32_t i = 0; i < ctx->num_crtcs; i++) {
727 uint32_t num_planes = ctx->crtcs[i].num_primary + ctx->crtcs[i].num_cursor +
728 ctx->crtcs[i].num_overlay;
729
730 for (uint32_t j = 0; j < num_planes; j++) {
731 remove_plane_fb(ctx, &ctx->crtcs[i].planes[j]);
732 free(ctx->crtcs[i].planes[j].drm_plane.formats);
733 }
734
735 free(ctx->crtcs[i].planes);
736 free(ctx->crtcs[i].overlay_idx);
737 free(ctx->crtcs[i].cursor_idx);
738 free(ctx->crtcs[i].primary_idx);
739 }
740
741 drmModeAtomicFree(ctx->pset);
742 free(ctx->modes);
743 free(ctx->crtcs);
744 free(ctx->connectors);
745 bs_mapper_destroy(ctx->mapper);
746 free(ctx);
747}
748
749static struct atomictest_context *query_kms(int fd)
750{
751 drmModeRes *res = drmModeGetResources(fd);
752 if (res == NULL) {
753 bs_debug_error("failed to get drm resources");
754 return false;
755 }
756
757 drmModePlaneRes *plane_res = drmModeGetPlaneResources(fd);
758 if (plane_res == NULL) {
759 bs_debug_error("failed to get plane resources");
760 drmModeFreeResources(res);
761 return NULL;
762 }
763
764 struct atomictest_context *ctx =
765 new_context(res->count_connectors, res->count_crtcs, plane_res->count_planes);
766 if (ctx == NULL) {
767 bs_debug_error("failed to allocate atomic context");
768 drmModeFreePlaneResources(plane_res);
769 drmModeFreeResources(res);
770 return NULL;
771 }
772
773 ctx->fd = fd;
774 drmModeObjectPropertiesPtr props = NULL;
775
776 for (uint32_t conn_index = 0; conn_index < res->count_connectors; conn_index++) {
777 uint32_t conn_id = res->connectors[conn_index];
778 ctx->connectors[conn_index].connector_id = conn_id;
779 props = drmModeObjectGetProperties(fd, conn_id, DRM_MODE_OBJECT_CONNECTOR);
780 get_connector_props(fd, &ctx->connectors[conn_index], props);
781
782 drmModeConnector *connector = drmModeGetConnector(fd, conn_id);
783 for (uint32_t mode_index = 0; mode_index < connector->count_modes; mode_index++) {
784 ctx->modes =
785 realloc(ctx->modes, (ctx->num_modes + 1) * sizeof(*ctx->modes));
786 drmModeCreatePropertyBlob(fd, &connector->modes[mode_index],
787 sizeof(drmModeModeInfo),
788 &ctx->modes[ctx->num_modes].id);
789 ctx->modes[ctx->num_modes].width = connector->modes[mode_index].hdisplay;
790 ctx->modes[ctx->num_modes].height = connector->modes[mode_index].vdisplay;
791 ctx->num_modes++;
792 }
793
794 drmModeFreeConnector(connector);
795 drmModeFreeObjectProperties(props);
796 props = NULL;
797 }
798
799 uint32_t crtc_index;
800 for (crtc_index = 0; crtc_index < res->count_crtcs; crtc_index++) {
801 ctx->crtcs[crtc_index].crtc_id = res->crtcs[crtc_index];
802 props =
803 drmModeObjectGetProperties(fd, res->crtcs[crtc_index], DRM_MODE_OBJECT_CRTC);
804 get_crtc_props(fd, &ctx->crtcs[crtc_index], props);
805
806 drmModeFreeObjectProperties(props);
807 props = NULL;
808 }
809
810 uint32_t overlay_idx, primary_idx, cursor_idx, idx;
811
812 for (uint32_t plane_index = 0; plane_index < plane_res->count_planes; plane_index++) {
813 drmModePlane *plane = drmModeGetPlane(fd, plane_res->planes[plane_index]);
814 if (plane == NULL) {
815 bs_debug_error("failed to get plane id %u", plane_res->planes[plane_index]);
816 continue;
817 }
818
819 uint32_t crtc_mask = 0;
820
821 drmModeObjectPropertiesPtr props = drmModeObjectGetProperties(
822 fd, plane_res->planes[plane_index], DRM_MODE_OBJECT_PLANE);
823
824 for (crtc_index = 0; crtc_index < res->count_crtcs; crtc_index++) {
825 crtc_mask = (1 << crtc_index);
826 if (plane->possible_crtcs & crtc_mask) {
827 struct atomictest_crtc *crtc = &ctx->crtcs[crtc_index];
828 cursor_idx = crtc->num_cursor;
829 primary_idx = crtc->num_primary;
830 overlay_idx = crtc->num_overlay;
831 idx = cursor_idx + primary_idx + overlay_idx;
832 copy_drm_plane(&crtc->planes[idx].drm_plane, plane);
833 get_plane_props(fd, &crtc->planes[idx], props);
834 switch (crtc->planes[idx].type.value) {
835 case DRM_PLANE_TYPE_OVERLAY:
836 crtc->overlay_idx[overlay_idx] = idx;
837 crtc->num_overlay++;
838 break;
839 case DRM_PLANE_TYPE_PRIMARY:
840 crtc->primary_idx[primary_idx] = idx;
841 crtc->num_primary++;
842 break;
843 case DRM_PLANE_TYPE_CURSOR:
844 crtc->cursor_idx[cursor_idx] = idx;
845 crtc->num_cursor++;
846 break;
847 default:
848 bs_debug_error("invalid plane type returned");
849 return NULL;
850 }
851
852 /*
853 * The DRM UAPI states that cursor and overlay framebuffers may be
854 * present after a CRTC disable, so zero this out so we can get a
855 * clean slate.
856 */
857 crtc->planes[idx].fb_id.value = 0;
858 }
859 }
860
861 drmModeFreePlane(plane);
862 drmModeFreeObjectProperties(props);
863 props = NULL;
864 }
865
866 drmModeFreePlaneResources(plane_res);
867 drmModeFreeResources(res);
868 return ctx;
869}
870
871static int test_multiple_planes(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
872{
Yalong Liu257f41b2018-05-04 10:15:20 +0800873 int ret = 0;
Yalong Liu1df84372018-01-24 17:10:12 +0800874 struct atomictest_plane *primary, *overlay, *cursor;
875 for (uint32_t i = 0; i < crtc->num_primary; i++) {
876 bool video = true;
877 uint32_t x, y;
878 for (uint32_t j = 0; j < crtc->num_overlay; j++) {
879 overlay = get_plane(crtc, j, DRM_PLANE_TYPE_OVERLAY);
880 x = crtc->width >> (j + 2);
881 y = crtc->height >> (j + 2);
882 // drmModeAddFB2 requires the height and width are even for sub-sampled YUV
883 // formats.
884 x = BS_ALIGN(x, 2);
885 y = BS_ALIGN(y, 2);
Yalong Liu257f41b2018-05-04 10:15:20 +0800886 if (video &&
887 !init_plane_any_format(ctx, overlay, x, y, x, y, crtc->crtc_id, true)) {
Yalong Liu1df84372018-01-24 17:10:12 +0800888 CHECK_RESULT(draw_to_plane(ctx->mapper, overlay, DRAW_STRIPE));
889 video = false;
890 } else {
Yalong Liu257f41b2018-05-04 10:15:20 +0800891 CHECK_RESULT(init_plane_any_format(ctx, overlay, x, y, x, y,
892 crtc->crtc_id, false));
Yalong Liu1df84372018-01-24 17:10:12 +0800893 CHECK_RESULT(draw_to_plane(ctx->mapper, overlay, DRAW_LINES));
894 }
895 }
896
897 for (uint32_t j = 0; j < crtc->num_cursor; j++) {
898 x = crtc->width >> (j + 2);
899 y = crtc->height >> (j + 2);
900 cursor = get_plane(crtc, j, DRM_PLANE_TYPE_CURSOR);
901 CHECK_RESULT(init_plane(ctx, cursor, DRM_FORMAT_ARGB8888, x, y, CURSOR_SIZE,
902 CURSOR_SIZE, crtc->crtc_id));
903 CHECK_RESULT(draw_to_plane(ctx->mapper, cursor, DRAW_CURSOR));
904 }
905
906 primary = get_plane(crtc, i, DRM_PLANE_TYPE_PRIMARY);
Yalong Liu257f41b2018-05-04 10:15:20 +0800907 CHECK_RESULT(init_plane_any_format(ctx, primary, 0, 0, crtc->width, crtc->height,
908 crtc->crtc_id, false));
Yalong Liu1df84372018-01-24 17:10:12 +0800909 CHECK_RESULT(draw_to_plane(ctx->mapper, primary, DRAW_ELLIPSE));
910
911 uint32_t num_planes = crtc->num_primary + crtc->num_cursor + crtc->num_overlay;
912 int done = 0;
913 struct atomictest_plane *plane;
914 while (!done) {
915 done = 1;
916 for (uint32_t j = 0; j < num_planes; j++) {
917 plane = &crtc->planes[j];
918 if (plane->type.value != DRM_PLANE_TYPE_PRIMARY)
Yalong Liu257f41b2018-05-04 10:15:20 +0800919 done &= move_plane(ctx, crtc, plane, 40, 40);
Yalong Liu1df84372018-01-24 17:10:12 +0800920 }
921
Yalong Liu257f41b2018-05-04 10:15:20 +0800922 ret |= test_and_commit(ctx, 1e6 / 60);
Yalong Liu1df84372018-01-24 17:10:12 +0800923 }
924
Yalong Liu257f41b2018-05-04 10:15:20 +0800925 ret |= test_and_commit(ctx, 1e6);
Yalong Liu1df84372018-01-24 17:10:12 +0800926
927 /* Disable primary plane and verify overlays show up. */
928 CHECK_RESULT(disable_plane(ctx, primary));
Yalong Liu257f41b2018-05-04 10:15:20 +0800929 ret |= test_and_commit(ctx, 1e6);
Yalong Liu1df84372018-01-24 17:10:12 +0800930 }
931
Yalong Liu257f41b2018-05-04 10:15:20 +0800932 return ret;
Yalong Liu1df84372018-01-24 17:10:12 +0800933}
934
935static int test_video_overlay(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
936{
Yalong Liu257f41b2018-05-04 10:15:20 +0800937 int ret = 0;
Yalong Liu1df84372018-01-24 17:10:12 +0800938 struct atomictest_plane *overlay;
939 for (uint32_t i = 0; i < crtc->num_overlay; i++) {
940 overlay = get_plane(crtc, i, DRM_PLANE_TYPE_OVERLAY);
Yalong Liu257f41b2018-05-04 10:15:20 +0800941 if (init_plane_any_format(ctx, overlay, 0, 0, 800, 800, crtc->crtc_id, true))
Yalong Liu1df84372018-01-24 17:10:12 +0800942 continue;
943
944 CHECK_RESULT(draw_to_plane(ctx->mapper, overlay, DRAW_STRIPE));
Yalong Liu257f41b2018-05-04 10:15:20 +0800945 while (!move_plane(ctx, crtc, overlay, 40, 40))
946 ret |= test_and_commit(ctx, 1e6 / 60);
Yalong Liu1df84372018-01-24 17:10:12 +0800947 }
948
Yalong Liu257f41b2018-05-04 10:15:20 +0800949 return ret;
Yalong Liu1df84372018-01-24 17:10:12 +0800950}
951
952static int test_orientation(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
953{
Yalong Liu257f41b2018-05-04 10:15:20 +0800954 int ret = 0;
Yalong Liu1df84372018-01-24 17:10:12 +0800955 struct atomictest_plane *primary, *overlay;
956 for (uint32_t i = 0; i < crtc->num_overlay; i++) {
957 overlay = get_plane(crtc, i, DRM_PLANE_TYPE_OVERLAY);
958 if (!overlay->rotation.pid)
959 continue;
960
Yalong Liu257f41b2018-05-04 10:15:20 +0800961 CHECK_RESULT(init_plane_any_format(ctx, overlay, 0, 0, crtc->width, crtc->height,
962 crtc->crtc_id, false));
Yalong Liu1df84372018-01-24 17:10:12 +0800963 overlay->rotation.value = DRM_ROTATE_0;
964 set_plane_props(overlay, ctx->pset);
965 CHECK_RESULT(draw_to_plane(ctx->mapper, overlay, DRAW_LINES));
Yalong Liu257f41b2018-05-04 10:15:20 +0800966 ret |= test_and_commit(ctx, 1e6);
Yalong Liu1df84372018-01-24 17:10:12 +0800967
968 overlay->rotation.value = DRM_REFLECT_Y;
969 set_plane_props(overlay, ctx->pset);
Yalong Liu257f41b2018-05-04 10:15:20 +0800970 ret |= test_and_commit(ctx, 1e6);
Yalong Liu1df84372018-01-24 17:10:12 +0800971
972 CHECK_RESULT(disable_plane(ctx, overlay));
973 }
974
975 for (uint32_t i = 0; i < crtc->num_primary; i++) {
976 primary = get_plane(crtc, i, DRM_PLANE_TYPE_PRIMARY);
977 if (!primary->rotation.pid)
978 continue;
979
Yalong Liu257f41b2018-05-04 10:15:20 +0800980 CHECK_RESULT(init_plane_any_format(ctx, primary, 0, 0, crtc->width, crtc->height,
981 crtc->crtc_id, false));
Yalong Liu1df84372018-01-24 17:10:12 +0800982
983 primary->rotation.value = DRM_ROTATE_0;
984 set_plane_props(primary, ctx->pset);
985 CHECK_RESULT(draw_to_plane(ctx->mapper, primary, DRAW_LINES));
Yalong Liu257f41b2018-05-04 10:15:20 +0800986 ret |= test_and_commit(ctx, 1e6);
Yalong Liu1df84372018-01-24 17:10:12 +0800987
988 primary->rotation.value = DRM_REFLECT_Y;
989 set_plane_props(primary, ctx->pset);
Yalong Liu257f41b2018-05-04 10:15:20 +0800990 ret |= test_and_commit(ctx, 1e6);
Yalong Liu1df84372018-01-24 17:10:12 +0800991
992 CHECK_RESULT(disable_plane(ctx, primary));
993 }
994
Yalong Liu257f41b2018-05-04 10:15:20 +0800995 return ret;
Yalong Liu1df84372018-01-24 17:10:12 +0800996}
997
998static int test_plane_ctm(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
999{
Yalong Liu257f41b2018-05-04 10:15:20 +08001000 int ret = 0;
Yalong Liu1df84372018-01-24 17:10:12 +08001001 struct atomictest_plane *primary, *overlay;
Yalong Liu1df84372018-01-24 17:10:12 +08001002
1003 for (uint32_t i = 0; i < crtc->num_overlay; i++) {
1004 overlay = get_plane(crtc, i, DRM_PLANE_TYPE_OVERLAY);
1005 if (!overlay->ctm.pid)
1006 continue;
1007
1008 CHECK_RESULT(init_plane(ctx, overlay, DRM_FORMAT_XRGB8888, 0, 0, crtc->width,
1009 crtc->height, crtc->crtc_id));
1010
1011 CHECK_RESULT(drmModeCreatePropertyBlob(ctx->fd, identity_ctm, sizeof(identity_ctm),
1012 &overlay->ctm.value));
1013 set_plane_props(overlay, ctx->pset);
1014 CHECK_RESULT(draw_to_plane(ctx->mapper, overlay, DRAW_LINES));
Yalong Liu257f41b2018-05-04 10:15:20 +08001015 ret |= test_and_commit(ctx, 1e6);
Yalong Liu1df84372018-01-24 17:10:12 +08001016 CHECK_RESULT(drmModeDestroyPropertyBlob(ctx->fd, overlay->ctm.value));
Yalong Liu1df84372018-01-24 17:10:12 +08001017
1018 CHECK_RESULT(drmModeCreatePropertyBlob(ctx->fd, red_shift_ctm,
1019 sizeof(red_shift_ctm), &overlay->ctm.value));
1020 set_plane_props(overlay, ctx->pset);
Yalong Liu257f41b2018-05-04 10:15:20 +08001021 ret |= test_and_commit(ctx, 1e6);
Yalong Liu1df84372018-01-24 17:10:12 +08001022 CHECK_RESULT(drmModeDestroyPropertyBlob(ctx->fd, overlay->ctm.value));
Yalong Liu1df84372018-01-24 17:10:12 +08001023
1024 CHECK_RESULT(disable_plane(ctx, overlay));
1025 }
1026
1027 for (uint32_t i = 0; i < crtc->num_primary; i++) {
1028 primary = get_plane(crtc, i, DRM_PLANE_TYPE_PRIMARY);
1029 if (!primary->ctm.pid)
1030 continue;
1031
Yalong Liu257f41b2018-05-04 10:15:20 +08001032 CHECK_RESULT(init_plane_any_format(ctx, primary, 0, 0, crtc->width, crtc->height,
1033 crtc->crtc_id, false));
Yalong Liu1df84372018-01-24 17:10:12 +08001034 CHECK_RESULT(drmModeCreatePropertyBlob(ctx->fd, identity_ctm, sizeof(identity_ctm),
1035 &primary->ctm.value));
1036 set_plane_props(primary, ctx->pset);
1037 CHECK_RESULT(draw_to_plane(ctx->mapper, primary, DRAW_LINES));
Yalong Liu257f41b2018-05-04 10:15:20 +08001038 ret |= test_and_commit(ctx, 1e6);
Yalong Liu1df84372018-01-24 17:10:12 +08001039 CHECK_RESULT(drmModeDestroyPropertyBlob(ctx->fd, primary->ctm.value));
Yalong Liu1df84372018-01-24 17:10:12 +08001040
1041 CHECK_RESULT(drmModeCreatePropertyBlob(ctx->fd, red_shift_ctm,
1042 sizeof(red_shift_ctm), &primary->ctm.value));
1043 set_plane_props(primary, ctx->pset);
Yalong Liu257f41b2018-05-04 10:15:20 +08001044 ret |= test_and_commit(ctx, 1e6);
Yalong Liu1df84372018-01-24 17:10:12 +08001045 CHECK_RESULT(drmModeDestroyPropertyBlob(ctx->fd, primary->ctm.value));
Yalong Liu1df84372018-01-24 17:10:12 +08001046
1047 CHECK_RESULT(disable_plane(ctx, primary));
1048 }
1049
Yalong Liu257f41b2018-05-04 10:15:20 +08001050 return ret;
1051}
1052
1053static int test_video_underlay(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
1054{
1055 int ret = 0;
1056 int i = 0;
1057 struct atomictest_plane *underlay = 0;
1058 struct atomictest_plane *primary = 0;
1059
1060 for (; i < crtc->num_primary + crtc->num_overlay; ++i) {
1061 if (crtc->planes[i].type.value != DRM_PLANE_TYPE_CURSOR) {
1062 if (!underlay) {
1063 underlay = &crtc->planes[i];
1064 } else {
1065 primary = &crtc->planes[i];
1066 break;
1067 }
1068 }
1069 }
1070 if (!underlay || !primary)
1071 return 0;
1072
1073 CHECK_RESULT(init_plane_any_format(ctx, underlay, 0, 0, crtc->width >> 2, crtc->height >> 2,
1074 crtc->crtc_id, true));
1075 CHECK_RESULT(draw_to_plane(ctx->mapper, underlay, DRAW_LINES));
1076
1077 CHECK_RESULT(init_plane(ctx, primary, DRM_FORMAT_ARGB8888, 0, 0, crtc->width, crtc->height,
1078 crtc->crtc_id));
1079 CHECK_RESULT(draw_to_plane(ctx->mapper, primary, DRAW_TRANSPARENT_HOLE));
1080
1081 while (!move_plane(ctx, crtc, underlay, 50, 20))
1082 ret |= test_and_commit(ctx, 1e6 / 60);
1083
1084 return ret;
Yalong Liu1df84372018-01-24 17:10:12 +08001085}
1086
1087static int test_fullscreen_video(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
1088{
Yalong Liu257f41b2018-05-04 10:15:20 +08001089 int ret = 0;
Yalong Liu1df84372018-01-24 17:10:12 +08001090 struct atomictest_plane *primary;
1091 for (uint32_t i = 0; i < crtc->num_primary; i++) {
1092 primary = get_plane(crtc, i, DRM_PLANE_TYPE_PRIMARY);
Yalong Liu257f41b2018-05-04 10:15:20 +08001093 if (init_plane_any_format(ctx, primary, 0, 0, crtc->width, crtc->height,
1094 crtc->crtc_id, true))
Yalong Liu1df84372018-01-24 17:10:12 +08001095 continue;
1096
1097 CHECK_RESULT(draw_to_plane(ctx->mapper, primary, DRAW_STRIPE));
Yalong Liu257f41b2018-05-04 10:15:20 +08001098 ret |= test_and_commit(ctx, 1e6);
Yalong Liu1df84372018-01-24 17:10:12 +08001099 }
1100
Yalong Liu257f41b2018-05-04 10:15:20 +08001101 return ret;
Yalong Liu1df84372018-01-24 17:10:12 +08001102}
1103
1104static int test_disable_primary(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
1105{
Yalong Liu257f41b2018-05-04 10:15:20 +08001106 int ret = 0;
Yalong Liu1df84372018-01-24 17:10:12 +08001107 struct atomictest_plane *primary, *overlay;
1108 for (uint32_t i = 0; i < crtc->num_primary; i++) {
1109 for (uint32_t j = 0; j < crtc->num_overlay; j++) {
1110 overlay = get_plane(crtc, j, DRM_PLANE_TYPE_OVERLAY);
1111 uint32_t x = crtc->width >> (j + 2);
1112 uint32_t y = crtc->height >> (j + 2);
Yalong Liu257f41b2018-05-04 10:15:20 +08001113 CHECK_RESULT(
1114 init_plane_any_format(ctx, overlay, x, y, x, y, crtc->crtc_id, false));
Yalong Liu1df84372018-01-24 17:10:12 +08001115 CHECK_RESULT(draw_to_plane(ctx->mapper, overlay, DRAW_LINES));
1116 }
1117
1118 primary = get_plane(crtc, i, DRM_PLANE_TYPE_PRIMARY);
Yalong Liu257f41b2018-05-04 10:15:20 +08001119 CHECK_RESULT(init_plane_any_format(ctx, primary, 0, 0, crtc->width, crtc->height,
1120 crtc->crtc_id, false));
Yalong Liu1df84372018-01-24 17:10:12 +08001121 CHECK_RESULT(draw_to_plane(ctx->mapper, primary, DRAW_ELLIPSE));
Yalong Liu257f41b2018-05-04 10:15:20 +08001122 ret |= test_and_commit(ctx, 1e6);
Yalong Liu1df84372018-01-24 17:10:12 +08001123
1124 /* Disable primary plane. */
1125 disable_plane(ctx, primary);
Yalong Liu257f41b2018-05-04 10:15:20 +08001126 ret |= test_and_commit(ctx, 1e6);
Yalong Liu1df84372018-01-24 17:10:12 +08001127 }
1128
Yalong Liu257f41b2018-05-04 10:15:20 +08001129 return ret;
1130}
1131
1132static int test_rgba_primary(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
1133{
1134 int ret = 0;
1135 struct atomictest_plane *primary;
1136 for (uint32_t i = 0; i < crtc->num_primary; i++) {
1137 primary = get_plane(crtc, i, DRM_PLANE_TYPE_PRIMARY);
1138 CHECK_RESULT(init_plane(ctx, primary, DRM_FORMAT_ARGB8888, 0, 0, crtc->width,
1139 crtc->height, crtc->crtc_id));
1140
1141 CHECK_RESULT(draw_to_plane(ctx->mapper, primary, DRAW_LINES));
1142
1143 ret |= test_and_commit(ctx, 1e6);
1144 }
1145
1146 return ret;
Yalong Liu1df84372018-01-24 17:10:12 +08001147}
1148
1149static int test_overlay_pageflip(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
1150{
1151 struct atomictest_plane *overlay;
1152 for (uint32_t i = 0; i < crtc->num_overlay; i++) {
1153 overlay = get_plane(crtc, i, DRM_PLANE_TYPE_OVERLAY);
1154 CHECK_RESULT(pageflip_formats(ctx, crtc, overlay));
1155 }
1156
1157 return 0;
1158}
1159
1160static int test_overlay_downscaling(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
1161{
Yalong Liu257f41b2018-05-04 10:15:20 +08001162 int ret = 0;
Yalong Liu1df84372018-01-24 17:10:12 +08001163 struct atomictest_plane *overlay;
1164 uint32_t w = BS_ALIGN(crtc->width / 2, 2);
1165 uint32_t h = BS_ALIGN(crtc->height / 2, 2);
1166 for (uint32_t i = 0; i < crtc->num_overlay; i++) {
1167 overlay = get_plane(crtc, i, DRM_PLANE_TYPE_OVERLAY);
Yalong Liu257f41b2018-05-04 10:15:20 +08001168 if (init_plane_any_format(ctx, overlay, 0, 0, w, h, crtc->crtc_id, true))
Yalong Liu1df84372018-01-24 17:10:12 +08001169 CHECK_RESULT(init_plane(ctx, overlay, DRM_FORMAT_XRGB8888, 0, 0, w, h,
1170 crtc->crtc_id));
Yalong Liu1df84372018-01-24 17:10:12 +08001171 CHECK_RESULT(draw_to_plane(ctx->mapper, overlay, DRAW_LINES));
Yalong Liu257f41b2018-05-04 10:15:20 +08001172 ret |= test_and_commit(ctx, 1e6);
Yalong Liu1df84372018-01-24 17:10:12 +08001173
1174 while (!scale_plane(ctx, crtc, overlay, -.1f, -.1f) && !test_commit(ctx)) {
1175 CHECK_RESULT(commit(ctx));
1176 usleep(1e6);
1177 }
1178
1179 disable_plane(ctx, overlay);
1180 }
1181
Yalong Liu257f41b2018-05-04 10:15:20 +08001182 return ret;
Yalong Liu1df84372018-01-24 17:10:12 +08001183}
1184
1185static int test_overlay_upscaling(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
1186{
Yalong Liu257f41b2018-05-04 10:15:20 +08001187 int ret = 0;
Yalong Liu1df84372018-01-24 17:10:12 +08001188 struct atomictest_plane *overlay;
1189 uint32_t w = BS_ALIGN(crtc->width / 4, 2);
1190 uint32_t h = BS_ALIGN(crtc->height / 4, 2);
1191 for (uint32_t i = 0; i < crtc->num_overlay; i++) {
1192 overlay = get_plane(crtc, i, DRM_PLANE_TYPE_OVERLAY);
Yalong Liu257f41b2018-05-04 10:15:20 +08001193 if (init_plane_any_format(ctx, overlay, 0, 0, w, h, crtc->crtc_id, true))
Yalong Liu1df84372018-01-24 17:10:12 +08001194 CHECK_RESULT(init_plane(ctx, overlay, DRM_FORMAT_XRGB8888, 0, 0, w, h,
1195 crtc->crtc_id));
Yalong Liu1df84372018-01-24 17:10:12 +08001196 CHECK_RESULT(draw_to_plane(ctx->mapper, overlay, DRAW_LINES));
Yalong Liu257f41b2018-05-04 10:15:20 +08001197 ret |= test_and_commit(ctx, 1e6);
Yalong Liu1df84372018-01-24 17:10:12 +08001198
1199 while (!scale_plane(ctx, crtc, overlay, .1f, .1f) && !test_commit(ctx)) {
1200 CHECK_RESULT(commit(ctx));
1201 usleep(1e6);
1202 }
1203
1204 disable_plane(ctx, overlay);
1205 }
1206
Yalong Liu257f41b2018-05-04 10:15:20 +08001207 return ret;
Yalong Liu1df84372018-01-24 17:10:12 +08001208}
1209
1210static int test_primary_pageflip(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
1211{
1212 struct atomictest_plane *primary;
1213 for (uint32_t i = 0; i < crtc->num_primary; i++) {
1214 primary = get_plane(crtc, i, DRM_PLANE_TYPE_PRIMARY);
1215 CHECK_RESULT(pageflip_formats(ctx, crtc, primary));
1216 }
1217
1218 return 0;
1219}
1220
Yalong Liu257f41b2018-05-04 10:15:20 +08001221static int test_crtc_ctm(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
1222{
1223 int ret = 0;
1224 struct atomictest_plane *primary;
1225 if (!crtc->ctm.pid)
1226 return 0;
1227
1228 CHECK_RESULT(drmModeCreatePropertyBlob(ctx->fd, identity_ctm, sizeof(identity_ctm),
1229 &crtc->ctm.value));
1230 set_crtc_props(crtc, ctx->pset);
1231 for (uint32_t i = 0; i < crtc->num_primary; i++) {
1232 primary = get_plane(crtc, i, DRM_PLANE_TYPE_PRIMARY);
1233
1234 CHECK_RESULT(init_plane(ctx, primary, DRM_FORMAT_XRGB8888, 0, 0, crtc->width,
1235 crtc->height, crtc->crtc_id));
1236 CHECK_RESULT(draw_to_plane(ctx->mapper, primary, DRAW_LINES));
1237 ret |= test_and_commit(ctx, 1e6);
1238
1239 primary->crtc_id.value = 0;
1240 CHECK_RESULT(set_plane_props(primary, ctx->pset));
1241 }
1242
1243 CHECK_RESULT(drmModeDestroyPropertyBlob(ctx->fd, crtc->ctm.value));
1244
1245 CHECK_RESULT(drmModeCreatePropertyBlob(ctx->fd, red_shift_ctm, sizeof(red_shift_ctm),
1246 &crtc->ctm.value));
1247 set_crtc_props(crtc, ctx->pset);
1248 for (uint32_t i = 0; i < crtc->num_primary; i++) {
1249 primary = get_plane(crtc, i, DRM_PLANE_TYPE_PRIMARY);
1250 primary->crtc_id.value = crtc->crtc_id;
1251 CHECK_RESULT(set_plane_props(primary, ctx->pset));
1252
1253 ret |= test_and_commit(ctx, 1e6);
1254
1255 primary->crtc_id.value = 0;
1256 CHECK_RESULT(disable_plane(ctx, primary));
1257 }
1258
1259 CHECK_RESULT(drmModeDestroyPropertyBlob(ctx->fd, crtc->ctm.value));
1260
1261 return ret;
1262}
1263
1264static void gamma_linear(struct drm_color_lut *table, int size)
1265{
1266 for (int i = 0; i < size; i++) {
1267 float v = (float)(i) / (float)(size - 1);
1268 v *= 65535.0f;
1269 table[i].red = (uint16_t)v;
1270 table[i].green = (uint16_t)v;
1271 table[i].blue = (uint16_t)v;
1272 }
1273}
1274
1275static void gamma_step(struct drm_color_lut *table, int size)
1276{
1277 for (int i = 0; i < size; i++) {
1278 float v = (i < size / 2) ? 0 : 65535;
1279 table[i].red = (uint16_t)v;
1280 table[i].green = (uint16_t)v;
1281 table[i].blue = (uint16_t)v;
1282 }
1283}
1284
1285static int test_crtc_gamma(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
1286{
1287 int ret = 0;
1288 struct atomictest_plane *primary;
1289 printf("[ test_crtc_gamma ] gamma_lut.pid %d\n", crtc->gamma_lut.pid);
1290 printf("[ test_crtc_gamma ] gamma_lut_size.pid %d\n", crtc->gamma_lut_size.pid);
1291 printf("[ test_crtc_gamma ] gamma_lut_size.value %d\n", crtc->gamma_lut_size.value);
1292 if (!crtc->gamma_lut.pid || !crtc->gamma_lut_size.pid)
1293 return 0;
1294
1295 if (crtc->gamma_lut_size.value == 0)
1296 return 0;
1297
1298 struct drm_color_lut *gamma_table =
1299 calloc(crtc->gamma_lut_size.value, sizeof(*gamma_table));
1300
1301 gamma_linear(gamma_table, crtc->gamma_lut_size.value);
1302 CHECK_RESULT(drmModeCreatePropertyBlob(
1303 ctx->fd, gamma_table, sizeof(struct drm_color_lut) * crtc->gamma_lut_size.value,
1304 &crtc->gamma_lut.value));
1305
1306 for (uint32_t i = 0; i < crtc->num_primary; i++) {
1307 primary = get_plane(crtc, i, DRM_PLANE_TYPE_PRIMARY);
1308
1309 CHECK_RESULT(init_plane(ctx, primary, DRM_FORMAT_XRGB8888, 0, 0, crtc->width,
1310 crtc->height, crtc->crtc_id));
1311 set_crtc_props(crtc, ctx->pset);
1312
1313 CHECK_RESULT(draw_to_plane(ctx->mapper, primary, DRAW_STRIPE));
1314 ret |= test_and_commit(ctx, 1e6);
1315
1316 CHECK_RESULT(disable_plane(ctx, primary));
1317 }
1318
1319 gamma_step(gamma_table, crtc->gamma_lut_size.value);
1320 CHECK_RESULT(drmModeCreatePropertyBlob(
1321 ctx->fd, gamma_table, sizeof(struct drm_color_lut) * crtc->gamma_lut_size.value,
1322 &crtc->gamma_lut.value));
1323
1324 for (uint32_t i = 0; i < crtc->num_primary; i++) {
1325 primary = get_plane(crtc, i, DRM_PLANE_TYPE_PRIMARY);
1326
1327 CHECK_RESULT(init_plane(ctx, primary, DRM_FORMAT_XRGB8888, 0, 0, crtc->width,
1328 crtc->height, crtc->crtc_id));
1329 set_crtc_props(crtc, ctx->pset);
1330
1331 CHECK_RESULT(draw_to_plane(ctx->mapper, primary, DRAW_STRIPE));
1332 ret |= test_and_commit(ctx, 1e6);
1333
1334 CHECK_RESULT(disable_plane(ctx, primary));
1335 }
1336
1337 CHECK_RESULT(drmModeDestroyPropertyBlob(ctx->fd, crtc->gamma_lut.value));
1338 free(gamma_table);
1339
1340 return ret;
1341}
1342
Yalong Liu1df84372018-01-24 17:10:12 +08001343static const struct atomictest_testcase cases[] = {
1344 { "disable_primary", test_disable_primary },
Yalong Liu257f41b2018-05-04 10:15:20 +08001345 { "rgba_primary", test_rgba_primary },
Yalong Liu1df84372018-01-24 17:10:12 +08001346 { "fullscreen_video", test_fullscreen_video },
1347 { "multiple_planes", test_multiple_planes },
1348 { "overlay_pageflip", test_overlay_pageflip },
1349 { "overlay_downscaling", test_overlay_downscaling },
1350 { "overlay_upscaling", test_overlay_upscaling },
1351 { "primary_pageflip", test_primary_pageflip },
1352 { "video_overlay", test_video_overlay },
1353 { "orientation", test_orientation },
Yalong Liu257f41b2018-05-04 10:15:20 +08001354 { "video_underlay", test_video_underlay },
Yalong Liu1df84372018-01-24 17:10:12 +08001355 /* CTM stands for Color Transform Matrix. */
1356 { "plane_ctm", test_plane_ctm },
Yalong Liu257f41b2018-05-04 10:15:20 +08001357 { "crtc_ctm", test_crtc_ctm },
1358 { "crtc_gamma", test_crtc_gamma },
Yalong Liu1df84372018-01-24 17:10:12 +08001359};
1360
1361static int run_testcase(struct atomictest_context *ctx, struct atomictest_crtc *crtc,
1362 test_function func)
1363{
1364 int cursor = drmModeAtomicGetCursor(ctx->pset);
1365 uint32_t num_planes = crtc->num_primary + crtc->num_cursor + crtc->num_overlay;
1366
1367 int ret = func(ctx, crtc);
1368
1369 for (uint32_t i = 0; i < num_planes; i++)
1370 disable_plane(ctx, &crtc->planes[i]);
1371
1372 drmModeAtomicSetCursor(ctx->pset, cursor);
1373
1374 CHECK_RESULT(commit(ctx));
1375 usleep(1e6 / 60);
1376
1377 return ret;
1378}
1379
1380static int run_atomictest(const char *name, uint32_t crtc_mask)
1381{
1382 int ret = 0;
1383 uint32_t num_run = 0;
1384 int fd = bs_drm_open_main_display();
1385 CHECK_RESULT(fd);
1386
1387 gbm = gbm_create_device(fd);
1388 if (!gbm) {
1389 bs_debug_error("failed to create gbm device");
1390 ret = -1;
1391 goto destroy_fd;
1392 }
1393
1394 ret = drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
1395 if (ret) {
1396 bs_debug_error("failed to enable DRM_CLIENT_CAP_UNIVERSAL_PLANES");
1397 ret = -1;
1398 goto destroy_gbm_device;
1399 }
1400
1401 ret = drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1);
1402 if (ret) {
1403 bs_debug_error("failed to enable DRM_CLIENT_CAP_ATOMIC");
1404 ret = -1;
1405 goto destroy_gbm_device;
1406 }
1407
1408 struct atomictest_context *ctx = query_kms(fd);
1409 if (!ctx) {
1410 bs_debug_error("querying atomictest failed.");
1411 ret = -1;
1412 goto destroy_gbm_device;
1413 }
1414
1415 struct atomictest_crtc *crtc;
1416 for (uint32_t crtc_index = 0; crtc_index < ctx->num_crtcs; crtc_index++) {
1417 crtc = &ctx->crtcs[crtc_index];
1418 if (!((1 << crtc_index) & crtc_mask))
1419 continue;
1420
1421 for (uint32_t i = 0; i < BS_ARRAY_LEN(cases); i++) {
1422 if (strcmp(cases[i].name, name) && strcmp("all", name))
1423 continue;
1424
1425 num_run++;
1426 ret = enable_crtc(ctx, crtc);
1427 if (ret)
1428 goto out;
1429
1430 ret = run_testcase(ctx, crtc, cases[i].test_func);
Yalong Liu257f41b2018-05-04 10:15:20 +08001431 if (ret < 0)
Yalong Liu1df84372018-01-24 17:10:12 +08001432 goto out;
Yalong Liu257f41b2018-05-04 10:15:20 +08001433 else if (ret == TEST_COMMIT_FAIL)
1434 bs_debug_warning("%s failed test commit, testcase not run.",
1435 cases[i].name);
Yalong Liu1df84372018-01-24 17:10:12 +08001436
1437 ret = disable_crtc(ctx, crtc);
1438 if (ret)
1439 goto out;
1440 }
1441 }
1442
1443 ret = (num_run == 0);
1444
1445out:
1446 free_context(ctx);
1447destroy_gbm_device:
1448 gbm_device_destroy(gbm);
1449destroy_fd:
1450 close(fd);
1451
1452 return ret;
1453}
1454
1455static const struct option longopts[] = {
1456 { "crtc", required_argument, NULL, 'c' },
1457 { "test_name", required_argument, NULL, 't' },
1458 { "help", no_argument, NULL, 'h' },
Yalong Liu257f41b2018-05-04 10:15:20 +08001459 { "automatic", no_argument, NULL, 'a' },
Yalong Liu1df84372018-01-24 17:10:12 +08001460 { 0, 0, 0, 0 },
1461};
1462
1463static void print_help(const char *argv0)
1464{
Yalong Liu257f41b2018-05-04 10:15:20 +08001465 printf("usage: %s -t <test_name> -c <crtc_index> -a (if running automatically)\n", argv0);
Yalong Liu1df84372018-01-24 17:10:12 +08001466 printf("A valid name test is one the following:\n");
1467 for (uint32_t i = 0; i < BS_ARRAY_LEN(cases); i++)
1468 printf("%s\n", cases[i].name);
1469 printf("all\n");
1470}
1471
1472int main(int argc, char **argv)
1473{
1474 int c;
1475 char *name = NULL;
1476 int32_t crtc_idx = -1;
1477 uint32_t crtc_mask = ~0;
Yalong Liu257f41b2018-05-04 10:15:20 +08001478 while ((c = getopt_long(argc, argv, "c:t:h:a", longopts, NULL)) != -1) {
Yalong Liu1df84372018-01-24 17:10:12 +08001479 switch (c) {
Yalong Liu257f41b2018-05-04 10:15:20 +08001480 case 'a':
1481 automatic = true;
1482 break;
Yalong Liu1df84372018-01-24 17:10:12 +08001483 case 'c':
1484 if (sscanf(optarg, "%d", &crtc_idx) != 1)
1485 goto print;
1486 break;
1487 case 't':
1488 if (name) {
1489 free(name);
1490 name = NULL;
1491 }
1492
1493 name = strdup(optarg);
1494 break;
1495 case 'h':
1496 goto print;
1497 default:
1498 goto print;
1499 }
1500 }
1501
1502 if (!name)
1503 goto print;
1504
1505 if (crtc_idx >= 0)
1506 crtc_mask = 1 << crtc_idx;
1507
1508 int ret = run_atomictest(name, crtc_mask);
1509 if (ret == 0)
1510 printf("[ PASSED ] atomictest.%s\n", name);
1511 else if (ret < 0)
1512 printf("[ FAILED ] atomictest.%s\n", name);
1513
1514 free(name);
1515
1516 if (ret > 0)
1517 goto print;
1518
1519 return ret;
1520
1521print:
1522 print_help(argv[0]);
1523 return 0;
1524}