blob: 7af7232dc3941b5e3b8a3903001a2de2d9eb4cee [file] [log] [blame]
Gurchetan Singh0d714272017-01-26 11:58:49 -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 */
Haixia Shifc476462015-04-22 16:25:04 -07006
Gurchetan Singh0d714272017-01-26 11:58:49 -08007/*
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 */
Haixia Shifc476462015-04-22 16:25:04 -070017
Haixia Shifc476462015-04-22 16:25:04 -070018
Gurchetan Singh0d714272017-01-26 11:58:49 -080019#include "bs_drm.h"
Haixia Shifc476462015-04-22 16:25:04 -070020
Gurchetan Singh0d714272017-01-26 11:58:49 -080021#define CHECK(cond) \
22 do { \
23 if (!(cond)) { \
24 bs_debug_error("check %s failed", #cond); \
25 return -1; \
26 } \
27 } while (0)
28
29#define CHECK_RESULT(ret) \
30 do { \
31 if ((ret) < 0) { \
32 bs_debug_error("failed with error: %d", ret); \
33 return -1; \
34 } \
35 } while (0)
36
37#define CURSOR_SIZE 64
38
39static const uint32_t yuv_formats[] = {
40 DRM_FORMAT_MT21, DRM_FORMAT_NV12, DRM_FORMAT_YUYV, DRM_FORMAT_YVU420,
41};
42
43static struct gbm_device *gbm = NULL;
44
45static void page_flip_handler(int fd, unsigned int sequence, unsigned int tv_sec,
46 unsigned int tv_usec, void *user_data)
Haixia Shifc476462015-04-22 16:25:04 -070047{
Gurchetan Singh0d714272017-01-26 11:58:49 -080048 // Nothing to do.
Haixia Shifc476462015-04-22 16:25:04 -070049}
50
Gurchetan Singh0d714272017-01-26 11:58:49 -080051struct atomictest_property {
52 uint32_t pid;
53 uint32_t value;
54};
Haixia Shifc476462015-04-22 16:25:04 -070055
Gurchetan Singh0d714272017-01-26 11:58:49 -080056struct atomictest_plane {
57 drmModePlane drm_plane;
58 struct gbm_bo *bo;
Haixia Shifc476462015-04-22 16:25:04 -070059
Gurchetan Singh0d714272017-01-26 11:58:49 -080060 uint32_t format_idx;
61
62 /* Properties. */
63 struct atomictest_property crtc_id;
64 struct atomictest_property crtc_x;
65 struct atomictest_property crtc_y;
66 struct atomictest_property crtc_w;
67 struct atomictest_property crtc_h;
68 struct atomictest_property fb_id;
69 struct atomictest_property src_x;
70 struct atomictest_property src_y;
71 struct atomictest_property src_w;
72 struct atomictest_property src_h;
73 struct atomictest_property type;
74 struct atomictest_property zpos;
75};
76
77struct atomictest_connector {
78 uint32_t connector_id;
79 struct atomictest_property crtc_id;
80 struct atomictest_property edid;
81 struct atomictest_property dpms;
82};
83
84struct atomictest_crtc {
85 uint32_t crtc_id;
86 uint32_t width;
87 uint32_t height;
88 uint32_t *primary_idx;
89 uint32_t *cursor_idx;
90 uint32_t *overlay_idx;
91 uint32_t num_primary;
92 uint32_t num_cursor;
93 uint32_t num_overlay;
94
95 struct atomictest_plane *planes;
96 struct atomictest_property mode_id;
97 struct atomictest_property active;
98};
99
100struct atomictest_mode {
101 uint32_t height;
102 uint32_t width;
103 uint32_t id;
104};
105
106struct atomictest_context {
107 int fd;
108 uint32_t num_crtcs;
109 uint32_t num_connectors;
110 uint32_t num_modes;
111
112 struct atomictest_connector *connectors;
113 struct atomictest_crtc *crtcs;
114 struct atomictest_mode *modes;
Dominik Behre0f1c182016-01-25 14:38:22 -0800115 drmModeAtomicReqPtr pset;
Gurchetan Singh0d714272017-01-26 11:58:49 -0800116 drmEventContext drm_event_ctx;
117};
Haixia Shifc476462015-04-22 16:25:04 -0700118
Gurchetan Singh0d714272017-01-26 11:58:49 -0800119struct atomictest {
120 const char *name;
121 int (*run_test)(struct atomictest_context *ctx, struct atomictest_crtc *crtc);
122};
Haixia Shifc476462015-04-22 16:25:04 -0700123
Gurchetan Singh0d714272017-01-26 11:58:49 -0800124static int32_t get_format_idx(struct atomictest_plane *plane, uint32_t format)
125{
126 for (int32_t i = 0; i < plane->drm_plane.count_formats; i++)
127 if (plane->drm_plane.formats[i] == format)
128 return i;
129 return -1;
130}
131
132static void copy_drm_plane(drmModePlane *dest, drmModePlane *src)
133{
134 memcpy(dest, src, sizeof(drmModePlane));
135 dest->formats = calloc(src->count_formats, sizeof(uint32_t));
136 dest->format_modifiers =
137 calloc(src->count_format_modifiers, sizeof(struct drm_format_modifier));
138 memcpy(dest->formats, src->formats, src->count_formats * sizeof(uint32_t));
139 memcpy(dest->format_modifiers, src->format_modifiers,
140 src->count_format_modifiers * sizeof(struct drm_format_modifier));
141}
142
143static struct atomictest_plane *get_plane(struct atomictest_crtc *crtc, uint32_t idx, uint64_t type)
144{
145 uint32_t index;
146 switch (type) {
147 case DRM_PLANE_TYPE_OVERLAY:
148 index = crtc->overlay_idx[idx];
149 break;
150 case DRM_PLANE_TYPE_PRIMARY:
151 index = crtc->primary_idx[idx];
152 break;
153 case DRM_PLANE_TYPE_CURSOR:
154 index = crtc->cursor_idx[idx];
155 break;
156 default:
157 bs_debug_error("invalid plane type returned");
158 return NULL;
Haixia Shifc476462015-04-22 16:25:04 -0700159 }
160
Gurchetan Singh0d714272017-01-26 11:58:49 -0800161 return &crtc->planes[index];
162}
Haixia Shifc476462015-04-22 16:25:04 -0700163
Gurchetan Singh0d714272017-01-26 11:58:49 -0800164static void write_to_buffer(struct gbm_bo *bo, uint32_t u32, uint16_t u16)
165{
166 void *map_data;
167 uint32_t stride;
168 uint32_t num_ints;
169 uint32_t format = gbm_bo_get_format(bo);
170 void *addr = gbm_bo_map(bo, 0, 0, gbm_bo_get_width(bo), gbm_bo_get_height(bo), 0, &stride,
171 &map_data, 0);
172
173 if (format == GBM_FORMAT_RGB565 || format == GBM_FORMAT_BGR565) {
174 num_ints = gbm_bo_get_plane_size(bo, 0) / sizeof(uint16_t);
175 uint16_t *pixel = (uint16_t *)addr;
176 for (uint32_t i = 0; i < num_ints; i++)
177 pixel[i] = u16;
178 } else {
179 num_ints = gbm_bo_get_plane_size(bo, 0) / sizeof(uint32_t);
180 uint32_t *pixel = (uint32_t *)addr;
181 for (uint32_t i = 0; i < num_ints; i++)
182 pixel[i] = u32;
Haixia Shifc476462015-04-22 16:25:04 -0700183 }
184
Gurchetan Singh0d714272017-01-26 11:58:49 -0800185 gbm_bo_unmap(bo, map_data);
186}
187
188static void draw_cursor(struct gbm_bo *bo)
189{
190 void *map_data;
191 uint32_t stride;
192 uint32_t *cursor_ptr = gbm_bo_map(bo, 0, 0, gbm_bo_get_width(bo), gbm_bo_get_height(bo), 0,
193 &stride, &map_data, 0);
194 for (size_t y = 0; y < gbm_bo_get_height(bo); y++) {
195 for (size_t x = 0; x < gbm_bo_get_width(bo); x++) {
196 // A white triangle pointing right
197 bool color_white = y > x / 2 && y < (gbm_bo_get_width(bo) - x / 2);
198 cursor_ptr[y * gbm_bo_get_height(bo) + x] = (color_white) ? 0xFFFFFFFF
199 : 0x00000000;
200 }
201 }
202
203 gbm_bo_unmap(bo, map_data);
204}
205
206static int get_prop(int fd, drmModeObjectPropertiesPtr props, const char *name,
207 struct atomictest_property *bs_prop)
208{
209 /* Property ID should always be > 0. */
210 bs_prop->pid = 0;
211 drmModePropertyPtr prop;
212 for (uint32_t i = 0; i < props->count_props; i++) {
213 if (bs_prop->pid)
214 break;
215
216 prop = drmModeGetProperty(fd, props->props[i]);
217 if (prop) {
218 if (!strcmp(prop->name, name)) {
219 bs_prop->pid = prop->prop_id;
220 bs_prop->value = props->prop_values[i];
221 }
222 drmModeFreeProperty(prop);
223 }
224 }
225
226 return (bs_prop->pid == 0) ? -1 : 0;
227}
228
229static int get_connector_props(int fd, struct atomictest_connector *connector,
230 drmModeObjectPropertiesPtr props)
231{
232 CHECK_RESULT(get_prop(fd, props, "EDID", &connector->edid));
233 CHECK_RESULT(get_prop(fd, props, "DPMS", &connector->dpms));
234 return 0;
235}
236
237static int get_crtc_props(int fd, struct atomictest_crtc *crtc, drmModeObjectPropertiesPtr props)
238{
239 CHECK_RESULT(get_prop(fd, props, "MODE_ID", &crtc->mode_id));
240 CHECK_RESULT(get_prop(fd, props, "ACTIVE", &crtc->active));
241 return 0;
242}
243
244static int get_plane_props(int fd, struct atomictest_plane *plane, drmModeObjectPropertiesPtr props)
245{
246 CHECK_RESULT(get_prop(fd, props, "CRTC_ID", &plane->crtc_id));
247 CHECK_RESULT(get_prop(fd, props, "FB_ID", &plane->fb_id));
248 CHECK_RESULT(get_prop(fd, props, "CRTC_X", &plane->crtc_x));
249 CHECK_RESULT(get_prop(fd, props, "CRTC_Y", &plane->crtc_y));
250 CHECK_RESULT(get_prop(fd, props, "CRTC_W", &plane->crtc_w));
251 CHECK_RESULT(get_prop(fd, props, "CRTC_H", &plane->crtc_h));
252 CHECK_RESULT(get_prop(fd, props, "SRC_X", &plane->src_x));
253 CHECK_RESULT(get_prop(fd, props, "SRC_Y", &plane->src_y));
254 CHECK_RESULT(get_prop(fd, props, "SRC_W", &plane->src_w));
255 CHECK_RESULT(get_prop(fd, props, "SRC_H", &plane->src_h));
256 CHECK_RESULT(get_prop(fd, props, "type", &plane->type));
257 return 0;
258}
259
260int set_connector_props(struct atomictest_connector *conn, drmModeAtomicReqPtr pset)
261{
262 uint32_t id = conn->connector_id;
263
264 /*
265 * Currently, kernel v4.4 doesn't have CRTC_ID as a property of the connector. It's
266 * required for the modeset to work, so we currently just take it from a plane. Also
267 * setting EDID or DPMS (even w/o modification) makes the kernel return -EINVAL, so
268 * let's keep them unset for now.
269 */
270 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, conn->crtc_id.pid, conn->crtc_id.value));
271 return 0;
272}
273
274int set_crtc_props(struct atomictest_crtc *crtc, drmModeAtomicReqPtr pset)
275{
276 uint32_t id = crtc->crtc_id;
277 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, crtc->mode_id.pid, crtc->mode_id.value));
278 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, crtc->active.pid, crtc->active.value));
279 return 0;
280}
281
282int set_plane_props(struct atomictest_plane *plane, drmModeAtomicReqPtr pset)
283{
284 uint32_t id = plane->drm_plane.plane_id;
285 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->crtc_id.pid, plane->crtc_id.value));
286 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->fb_id.pid, plane->fb_id.value));
287 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->crtc_x.pid, plane->crtc_x.value));
288 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->crtc_y.pid, plane->crtc_y.value));
289 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->crtc_w.pid, plane->crtc_w.value));
290 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->crtc_h.pid, plane->crtc_h.value));
291 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->src_x.pid, plane->src_x.value));
292 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->src_y.pid, plane->src_y.value));
293 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->src_w.pid, plane->src_w.value));
294 CHECK_RESULT(drmModeAtomicAddProperty(pset, id, plane->src_h.pid, plane->src_h.value));
295 return 0;
296}
297
298static int remove_plane_fb(struct atomictest_context *ctx, struct atomictest_plane *plane)
299{
300 if (plane->bo && plane->fb_id.value) {
301 CHECK_RESULT(drmModeRmFB(ctx->fd, plane->fb_id.value));
302 gbm_bo_destroy(plane->bo);
303 plane->bo = NULL;
304 plane->fb_id.value = 0;
305 }
306
307 return 0;
308}
309
310static int add_plane_fb(struct atomictest_context *ctx, struct atomictest_plane *plane)
311{
312 if (plane->format_idx < plane->drm_plane.count_formats) {
313 CHECK_RESULT(remove_plane_fb(ctx, plane));
314 uint32_t flags = (plane->type.value == DRM_PLANE_TYPE_CURSOR) ? GBM_BO_USE_CURSOR
315 : GBM_BO_USE_SCANOUT;
316 /* TODO(gsingh): add create with modifiers option. */
317 plane->bo = gbm_bo_create(gbm, plane->crtc_w.value, plane->crtc_h.value,
318 plane->drm_plane.formats[plane->format_idx], flags);
319
320 CHECK(plane->bo);
321 plane->fb_id.value = bs_drm_fb_create_gbm(plane->bo);
322 CHECK(plane->fb_id.value);
323 CHECK_RESULT(set_plane_props(plane, ctx->pset));
324 }
325
326 return 0;
327}
328
329static int init_plane(struct atomictest_context *ctx, struct atomictest_plane *plane,
330 uint32_t format, uint32_t x, uint32_t y, uint32_t w, uint32_t h,
331 uint32_t zpos, uint32_t crtc_id)
332{
333 int32_t idx = get_format_idx(plane, format);
334 if (idx < 0)
335 return -EINVAL;
336
337 plane->format_idx = idx;
338 plane->crtc_x.value = x;
339 plane->crtc_y.value = y;
340 plane->crtc_w.value = w;
341 plane->crtc_h.value = h;
342 plane->src_w.value = plane->crtc_w.value << 16;
343 plane->src_h.value = plane->crtc_h.value << 16;
344 plane->zpos.value = zpos;
345 plane->crtc_id.value = crtc_id;
346
347 CHECK_RESULT(add_plane_fb(ctx, plane));
348 return 0;
349}
350
351static int disable_plane(struct atomictest_context *ctx, struct atomictest_plane *plane)
352{
353 plane->format_idx = 0;
354 plane->crtc_x.value = 0;
355 plane->crtc_y.value = 0;
356 plane->crtc_w.value = 0;
357 plane->crtc_h.value = 0;
358 plane->src_w.value = 0;
359 plane->src_h.value = 0;
360 plane->zpos.value = 0;
361 plane->crtc_id.value = 0;
362
363 CHECK_RESULT(remove_plane_fb(ctx, plane));
364 CHECK_RESULT(set_plane_props(plane, ctx->pset));
365 return 0;
366}
367
368static int move_plane(struct atomictest_context *ctx, struct atomictest_crtc *crtc,
369 struct atomictest_plane *plane, uint32_t dx, uint32_t dy)
370{
371 if (plane->crtc_x.value < (crtc->width - plane->crtc_w.value) &&
372 plane->crtc_y.value < (crtc->height - plane->crtc_h.value)) {
373 plane->crtc_x.value += dx;
374 plane->crtc_y.value += dy;
375 CHECK_RESULT(set_plane_props(plane, ctx->pset));
376 return 0;
377 }
378
379 return -1;
380}
381
382static int commit(struct atomictest_context *ctx)
383{
384 int ret;
385 fd_set fds;
386 FD_ZERO(&fds);
387 FD_SET(ctx->fd, &fds);
388
389 ret = drmModeAtomicCommit(ctx->fd, ctx->pset,
390 DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
391 CHECK_RESULT(ret);
392 do {
393 ret = select(ctx->fd + 1, &fds, NULL, NULL, NULL);
394 } while (ret == -1 && errno == EINTR);
395
396 CHECK_RESULT(ret);
397 if (FD_ISSET(ctx->fd, &fds))
398 drmHandleEvent(ctx->fd, &ctx->drm_event_ctx);
399
400 return 0;
401}
402
403static int pageflip(struct atomictest_context *ctx, struct atomictest_plane *plane, uint32_t x,
404 uint32_t y, uint32_t w, uint32_t h, uint32_t zpos, uint32_t crtc_id,
405 uint32_t *formats, uint32_t count_formats)
406{
407 /* Check if plane support specified formats. */
408 for (uint32_t i = 0; i < count_formats; i++)
409 CHECK_RESULT(get_format_idx(plane, formats[i]));
410
411 for (uint32_t i = 0; i < count_formats; i++) {
412 CHECK_RESULT(init_plane(ctx, plane, formats[i], x, y, w, h, zpos, crtc_id));
413 write_to_buffer(plane->bo, 0x00FF0000, 0xF800);
414 CHECK_RESULT(commit(ctx));
415 usleep(1e6);
416 }
417
418 return 0;
419}
420
421static int check_mode(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
422{
423 drmModeAtomicSetCursor(ctx->pset, 0);
424
425 for (uint32_t i = 0; i < ctx->num_crtcs; i++) {
426 if (&ctx->crtcs[i] != crtc) {
427 ctx->crtcs[i].mode_id.value = 0;
428 ctx->crtcs[i].active.value = 0;
429 set_crtc_props(&ctx->crtcs[i], ctx->pset);
430 }
431 }
432
433 for (uint32_t i = 0; i < ctx->num_connectors; i++) {
434 ctx->connectors[i].crtc_id.value = crtc->crtc_id;
435 set_connector_props(&ctx->connectors[i], ctx->pset);
436 }
437
438 int ret = -EINVAL;
439 int cursor = drmModeAtomicGetCursor(ctx->pset);
440
441 for (uint32_t i = 0; i < ctx->num_modes; i++) {
442 struct atomictest_mode *mode = &ctx->modes[i];
443 drmModeAtomicSetCursor(ctx->pset, cursor);
444
445 crtc->mode_id.value = mode->id;
446 crtc->active.value = 1;
447 crtc->width = mode->width;
448 crtc->height = mode->height;
449
450 set_crtc_props(crtc, ctx->pset);
451 ret = drmModeAtomicCommit(ctx->fd, ctx->pset,
452 DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET,
453 NULL);
454 if (!ret)
455 return 0;
456 }
457
458 bs_debug_error("[CRTC:%d]: failed to find mode", crtc->crtc_id);
459 return ret;
460}
461
462static struct atomictest_context *new_context(uint32_t num_connectors, uint32_t num_crtcs,
463 uint32_t num_planes)
464{
465 struct atomictest_context *ctx = calloc(1, sizeof(*ctx));
466 ctx->connectors = calloc(num_connectors, sizeof(*ctx->connectors));
467 ctx->crtcs = calloc(num_crtcs, sizeof(*ctx->crtcs));
468 ctx->modes = calloc(1, sizeof(*ctx->modes));
469 for (uint32_t i = 0; i < num_crtcs; i++) {
470 ctx->crtcs[i].planes = calloc(num_planes, sizeof(*ctx->crtcs[i].planes));
471 ctx->crtcs[i].overlay_idx = calloc(num_planes, sizeof(uint32_t));
472 ctx->crtcs[i].primary_idx = calloc(num_planes, sizeof(uint32_t));
473 ctx->crtcs[i].cursor_idx = calloc(num_planes, sizeof(uint32_t));
474 }
475
476 ctx->num_connectors = num_connectors;
477 ctx->num_crtcs = num_crtcs;
478 ctx->num_modes = 1;
479 ctx->pset = drmModeAtomicAlloc();
480 ctx->drm_event_ctx.version = DRM_EVENT_CONTEXT_VERSION;
481 ctx->drm_event_ctx.page_flip_handler = page_flip_handler;
482
483 return ctx;
484}
485
486static void free_context(struct atomictest_context *ctx)
487{
488 for (uint32_t i = 0; i < ctx->num_crtcs; i++) {
489 uint32_t num_planes = ctx->crtcs[i].num_primary + ctx->crtcs[i].num_cursor +
490 ctx->crtcs[i].num_overlay;
491
492 for (uint32_t j = 0; j < num_planes; j++) {
493 remove_plane_fb(ctx, &ctx->crtcs[i].planes[j]);
494 free(ctx->crtcs[i].planes[j].drm_plane.formats);
495 free(ctx->crtcs[i].planes[j].drm_plane.format_modifiers);
Haixia Shifc476462015-04-22 16:25:04 -0700496 }
497
Gurchetan Singh0d714272017-01-26 11:58:49 -0800498 free(ctx->crtcs[i].planes);
499 free(ctx->crtcs[i].overlay_idx);
500 free(ctx->crtcs[i].cursor_idx);
501 free(ctx->crtcs[i].primary_idx);
502 }
503
504 drmModeAtomicFree(ctx->pset);
505 free(ctx->modes);
506 free(ctx->crtcs);
507 free(ctx->connectors);
508 free(ctx);
509}
510
511static struct atomictest_context *init_atomictest(int fd)
512{
513 drmModeRes *res = drmModeGetResources(fd);
514 if (res == NULL) {
515 bs_debug_error("failed to get drm resources");
516 return false;
517 }
518
519 drmModePlaneRes *plane_res = drmModeGetPlaneResources(fd);
520 if (plane_res == NULL) {
521 bs_debug_error("failed to get plane resources");
522 drmModeFreeResources(res);
523 return NULL;
524 }
525
526 struct atomictest_context *ctx =
527 new_context(res->count_connectors, res->count_crtcs, plane_res->count_planes);
528 if (ctx == NULL) {
529 bs_debug_error("failed to allocate atomic context");
530 drmModeFreePlaneResources(plane_res);
531 drmModeFreeResources(res);
532 return NULL;
533 }
534
535 ctx->num_modes = 0;
536 ctx->fd = fd;
537 drmModeObjectPropertiesPtr props = NULL;
538
539 for (uint32_t conn_index = 0; conn_index < res->count_connectors; conn_index++) {
540 uint32_t conn_id = res->connectors[conn_index];
541 ctx->connectors[conn_index].connector_id = conn_id;
542 props = drmModeObjectGetProperties(fd, conn_id, DRM_MODE_OBJECT_CONNECTOR);
543 get_connector_props(fd, &ctx->connectors[conn_index], props);
544
545 drmModeConnector *connector = drmModeGetConnector(fd, conn_id);
546 for (uint32_t mode_index = 0; mode_index < connector->count_modes; mode_index++) {
547 drmModeCreatePropertyBlob(fd, &connector->modes[mode_index],
548 sizeof(drmModeModeInfo),
549 &ctx->modes[ctx->num_modes].id);
550 ctx->modes[ctx->num_modes].width = connector->modes[mode_index].hdisplay;
551 ctx->modes[ctx->num_modes].height = connector->modes[mode_index].vdisplay;
552 ctx->num_modes++;
553 ctx->modes = realloc(ctx->modes, ctx->num_modes * sizeof(*ctx->modes));
Haixia Shifc476462015-04-22 16:25:04 -0700554 }
555
Gurchetan Singh0d714272017-01-26 11:58:49 -0800556 drmModeFreeConnector(connector);
557 drmModeFreeObjectProperties(props);
558 props = NULL;
Haixia Shifc476462015-04-22 16:25:04 -0700559 }
560
Gurchetan Singh0d714272017-01-26 11:58:49 -0800561 uint32_t crtc_index;
562 for (crtc_index = 0; crtc_index < res->count_crtcs; crtc_index++) {
563 ctx->crtcs[crtc_index].crtc_id = res->crtcs[crtc_index];
564 props =
565 drmModeObjectGetProperties(fd, res->crtcs[crtc_index], DRM_MODE_OBJECT_CRTC);
566 get_crtc_props(fd, &ctx->crtcs[crtc_index], props);
567
568 drmModeFreeObjectProperties(props);
569 props = NULL;
Haixia Shifc476462015-04-22 16:25:04 -0700570 }
571
Gurchetan Singh0d714272017-01-26 11:58:49 -0800572 uint32_t overlay_idx, primary_idx, cursor_idx, idx;
Haixia Shifc476462015-04-22 16:25:04 -0700573
Gurchetan Singh0d714272017-01-26 11:58:49 -0800574 for (uint32_t plane_index = 0; plane_index < plane_res->count_planes; plane_index++) {
575 drmModePlane *plane = drmModeGetPlane(fd, plane_res->planes[plane_index]);
576 if (plane == NULL) {
577 bs_debug_error("failed to get plane id %u", plane_res->planes[plane_index]);
578 continue;
579 }
Haixia Shifc476462015-04-22 16:25:04 -0700580
Gurchetan Singh0d714272017-01-26 11:58:49 -0800581 uint32_t crtc_mask = 0;
582
583 drmModeObjectPropertiesPtr props = drmModeObjectGetProperties(
584 fd, plane_res->planes[plane_index], DRM_MODE_OBJECT_PLANE);
585
586 for (crtc_index = 0; crtc_index < res->count_crtcs; crtc_index++) {
587 crtc_mask = (1 << crtc_index);
588 if (plane->possible_crtcs & crtc_mask) {
589 struct atomictest_crtc *crtc = &ctx->crtcs[crtc_index];
590 cursor_idx = crtc->num_cursor;
591 primary_idx = crtc->num_primary;
592 overlay_idx = crtc->num_overlay;
593 idx = cursor_idx + primary_idx + overlay_idx;
594 copy_drm_plane(&crtc->planes[idx].drm_plane, plane);
595 get_plane_props(fd, &crtc->planes[idx], props);
596 switch (crtc->planes[idx].type.value) {
597 case DRM_PLANE_TYPE_OVERLAY:
598 crtc->overlay_idx[overlay_idx] = idx;
599 crtc->num_overlay++;
600 break;
601 case DRM_PLANE_TYPE_PRIMARY:
602 crtc->primary_idx[primary_idx] = idx;
603 crtc->num_primary++;
604 break;
605 case DRM_PLANE_TYPE_CURSOR:
606 crtc->cursor_idx[cursor_idx] = idx;
607 crtc->num_cursor++;
608 break;
609 default:
610 bs_debug_error("invalid plane type returned");
611 return NULL;
612 }
Haixia Shifc476462015-04-22 16:25:04 -0700613 }
614 }
615
Gurchetan Singh0d714272017-01-26 11:58:49 -0800616 drmModeFreePlane(plane);
617 drmModeFreeObjectProperties(props);
618 props = NULL;
Haixia Shifc476462015-04-22 16:25:04 -0700619 }
620
Gurchetan Singh0d714272017-01-26 11:58:49 -0800621 /* HACK: Set connector CRTC pid to plane CRTC pid. */
622 for (uint32_t i = 0; i < ctx->num_connectors; i++)
623 ctx->connectors[i].crtc_id.pid = ctx->crtcs[0].planes[0].crtc_id.pid;
Haixia Shifc476462015-04-22 16:25:04 -0700624
Gurchetan Singh0d714272017-01-26 11:58:49 -0800625 drmModeFreePlaneResources(plane_res);
626 drmModeFreeResources(res);
627 return ctx;
628}
Haixia Shifc476462015-04-22 16:25:04 -0700629
Gurchetan Singh0d714272017-01-26 11:58:49 -0800630static int run_atomictest(const struct atomictest *test)
631{
632 int ret = 0;
633 int fd = bs_drm_open_main_display();
634 CHECK_RESULT(fd);
635
636 gbm = gbm_create_device(fd);
637 if (!gbm) {
638 bs_debug_error("failed to create gbm device");
639 ret = 1;
640 goto destroy_fd;
641 }
642
643 ret = drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
644 if (ret) {
645 bs_debug_error("failed to enable DRM_CLIENT_CAP_UNIVERSAL_PLANES");
646 ret = 1;
647 goto destroy_gbm_device;
648 }
649
650 ret = drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1);
651 if (ret) {
652 bs_debug_error("failed to enable DRM_CLIENT_CAP_ATOMIC");
653 ret = 1;
654 goto destroy_gbm_device;
655 }
656
657 struct atomictest_context *ctx = init_atomictest(fd);
658 if (!ctx) {
659 bs_debug_error("initializing atomictest failed.");
660 ret = 1;
661 goto destroy_gbm_device;
662 }
663
664 struct atomictest_crtc *crtc;
665 for (uint32_t crtc_index = 0; crtc_index < ctx->num_crtcs; crtc_index++) {
666 crtc = &ctx->crtcs[crtc_index];
667 if (!check_mode(ctx, crtc))
668 ret |= test->run_test(ctx, crtc);
669 else
670 ret |= 1;
671 }
672
673 free_context(ctx);
674
675destroy_gbm_device:
676 gbm_device_destroy(gbm);
677destroy_fd:
678 close(fd);
679
Haixia Shifc476462015-04-22 16:25:04 -0700680 return ret;
681}
Gurchetan Singh0d714272017-01-26 11:58:49 -0800682
683static int test_multiple_planes(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
684{
685 struct atomictest_plane *primary, *overlay, *cursor;
686 for (uint32_t i = 0; i < crtc->num_primary; i++) {
687 bool has_video = false;
688 uint32_t x, y;
689 for (uint32_t j = 0; j < crtc->num_overlay; j++) {
690 overlay = get_plane(crtc, j, DRM_PLANE_TYPE_OVERLAY);
691 x = crtc->width >> (j + 2);
692 y = crtc->height >> (j + 2);
693 bool added_video = false;
694 if (!has_video) {
695 uint32_t k = 0;
696 for (k = 0; k < BS_ARRAY_LEN(yuv_formats); k++) {
697 if (!init_plane(ctx, overlay, yuv_formats[k], x, y, x, y, j,
698 crtc->crtc_id)) {
699 has_video = true;
700 added_video = true;
701 break;
702 }
703 }
704
705 if (added_video) {
706 const struct bs_draw_format *draw_format =
707 bs_get_draw_format(yuv_formats[k]);
708 CHECK(draw_format);
709 CHECK(bs_draw_pattern(overlay->bo, draw_format));
710 }
711 }
712
713 if (!added_video) {
714 added_video = true;
715 CHECK_RESULT(init_plane(ctx, overlay, DRM_FORMAT_XRGB8888, x, y, x,
716 y, i, crtc->crtc_id));
717 write_to_buffer(overlay->bo, 0x00FF0000, 0);
718 }
719 }
720
721 for (uint32_t j = 0; j < crtc->num_cursor; j++) {
722 x = crtc->width >> (j + 2);
723 y = crtc->height >> (j + 2);
724 cursor = get_plane(crtc, j, DRM_PLANE_TYPE_CURSOR);
725 CHECK_RESULT(init_plane(ctx, cursor, DRM_FORMAT_XRGB8888, x, y, CURSOR_SIZE,
726 CURSOR_SIZE, crtc->num_overlay + j, crtc->crtc_id));
727 draw_cursor(cursor->bo);
728 }
729
730 primary = get_plane(crtc, i, DRM_PLANE_TYPE_PRIMARY);
731 CHECK_RESULT(init_plane(ctx, primary, DRM_FORMAT_XRGB8888, 0, 0, crtc->width,
732 crtc->height, 0, crtc->crtc_id));
733 write_to_buffer(primary->bo, 0x00000FF, 0);
734
735 uint32_t num_planes = crtc->num_primary + crtc->num_cursor + crtc->num_overlay;
736 int done = 0;
737 struct atomictest_plane *plane;
738 while (!done) {
739 done = 1;
740 for (uint32_t j = 0; j < num_planes; j++) {
741 plane = &crtc->planes[j];
742 if (plane->type.value != DRM_PLANE_TYPE_PRIMARY)
743 done &= move_plane(ctx, crtc, plane, 20, 20);
744 }
745
746 CHECK_RESULT(commit(ctx));
747 usleep(1e6 / 60);
748 }
749
750 CHECK_RESULT(commit(ctx));
751 usleep(1e6);
752
753 /* Disable primary plane and verify overlays show up. */
754 CHECK_RESULT(disable_plane(ctx, primary));
755 CHECK_RESULT(commit(ctx));
756 usleep(1e6);
757 }
758
759 return 0;
760}
761
762static int test_video_overlay(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
763{
764 struct atomictest_plane *overlay;
765 for (uint32_t i = 0; i < crtc->num_overlay; i++) {
766 overlay = get_plane(crtc, i, DRM_PLANE_TYPE_OVERLAY);
767 for (uint32_t j = 0; j < BS_ARRAY_LEN(yuv_formats); j++) {
768 if (init_plane(ctx, overlay, yuv_formats[j], 0, 0, 800, 800, 0,
769 crtc->crtc_id))
770 continue;
771
772 const struct bs_draw_format *draw_format =
773 bs_get_draw_format(yuv_formats[j]);
774 CHECK(draw_format);
775 CHECK(bs_draw_pattern(overlay->bo, draw_format));
776 while (!move_plane(ctx, crtc, overlay, 20, 20)) {
777 CHECK_RESULT(commit(ctx));
778 usleep(1e6 / 60);
779 }
780 }
781 }
782
783 return 0;
784}
785
786static int test_fullscreen_video(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
787{
788 struct atomictest_plane *primary;
789 for (uint32_t i = 0; i < crtc->num_primary; i++) {
790 primary = get_plane(crtc, i, DRM_PLANE_TYPE_PRIMARY);
791 for (uint32_t j = 0; j < BS_ARRAY_LEN(yuv_formats); j++) {
792 if (init_plane(ctx, primary, yuv_formats[j], 0, 0, crtc->width,
793 crtc->height, 0, crtc->crtc_id))
794 continue;
795 const struct bs_draw_format *draw_format =
796 bs_get_draw_format(yuv_formats[j]);
797 CHECK(draw_format);
798
799 CHECK(bs_draw_pattern(primary->bo, draw_format));
800 CHECK_RESULT(commit(ctx));
801 usleep(1e6);
802 }
803 }
804
805 return 0;
806}
807
808static int test_disable_primary(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
809{
810 int cursor;
811 struct atomictest_plane *primary, *overlay;
812 for (uint32_t i = 0; i < crtc->num_primary; i++) {
813 for (uint32_t j = 0; j < crtc->num_overlay; j++) {
814 overlay = get_plane(crtc, j, DRM_PLANE_TYPE_OVERLAY);
815 uint32_t x = crtc->width >> (j + 2);
816 uint32_t y = crtc->height >> (j + 2);
817 CHECK_RESULT(init_plane(ctx, overlay, DRM_FORMAT_XRGB8888, x, y, x, y, i,
818 crtc->crtc_id));
819 write_to_buffer(overlay->bo, 0x00FF0000, 0);
820 }
821
822 cursor = drmModeAtomicGetCursor(ctx->pset);
823 primary = get_plane(crtc, i, DRM_PLANE_TYPE_PRIMARY);
824 CHECK_RESULT(init_plane(ctx, primary, DRM_FORMAT_XRGB8888, 0, 0, crtc->width,
825 crtc->height, 0, crtc->crtc_id));
826 write_to_buffer(primary->bo, 0x00000FF, 0);
827 CHECK_RESULT(commit(ctx));
828 usleep(1e6);
829
830 /* Disable primary plane. */
831 disable_plane(ctx, primary);
832 CHECK_RESULT(commit(ctx));
833 usleep(1e6);
834 drmModeAtomicSetCursor(ctx->pset, cursor);
835 }
836
837 return 0;
838}
839
840static int test_overlay_pageflip(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
841{
842 struct atomictest_plane *overlay;
843 uint32_t formats[3] = { DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888, DRM_FORMAT_RGB565 };
844 for (uint32_t i = 0; i < crtc->num_overlay; i++) {
845 overlay = get_plane(crtc, i, DRM_PLANE_TYPE_OVERLAY);
846 uint32_t x = crtc->width >> (i + 1);
847 uint32_t y = crtc->height >> (i + 1);
848
849 CHECK_RESULT(pageflip(ctx, overlay, x, y, x, y, i, crtc->crtc_id, formats,
850 BS_ARRAY_LEN(formats)));
851 }
852
853 return 0;
854}
855
856static int test_primary_pageflip(struct atomictest_context *ctx, struct atomictest_crtc *crtc)
857{
858 int cursor;
859 struct atomictest_plane *primary;
860 uint32_t formats[3] = { DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888, DRM_FORMAT_RGB565 };
861 for (uint32_t i = 0; i < crtc->num_primary; i++) {
862 primary = get_plane(crtc, i, DRM_PLANE_TYPE_PRIMARY);
863 cursor = drmModeAtomicGetCursor(ctx->pset);
864 CHECK_RESULT(pageflip(ctx, primary, 0, 0, crtc->width, crtc->height, 0,
865 crtc->crtc_id, formats, BS_ARRAY_LEN(formats)));
866
867 drmModeAtomicSetCursor(ctx->pset, cursor);
868 }
869
870 return 0;
871}
872
873static const struct atomictest tests[] = {
874 { "disable_primary", test_disable_primary },
875 { "fullscreen_video", test_fullscreen_video },
876 { "multiple_planes", test_multiple_planes },
877 { "overlay_pageflip", test_overlay_pageflip },
878 { "primary_pageflip", test_primary_pageflip },
879 { "video_overlay", test_video_overlay },
880};
881
882static void print_help(const char *argv0)
883{
884 printf("usage: %s <test_name>\n\n", argv0);
885 printf("A valid name test is one the following:\n");
886 for (uint32_t i = 0; i < BS_ARRAY_LEN(tests); i++)
887 printf("%s\n", tests[i].name);
888}
889
890int main(int argc, char **argv)
891{
892 if (argc == 2) {
893 char *name = argv[1];
894 for (uint32_t i = 0; i < BS_ARRAY_LEN(tests); i++) {
895 if (strcmp(tests[i].name, name))
896 continue;
897
898 int ret = run_atomictest(&tests[i]);
899 if (ret) {
900 printf("[ FAILED ] atomictest.%s\n", name);
901 return -1;
902 } else {
903 printf("[ PASSED ] atomictest.%s\n", name);
904 return 0;
905 }
906 }
907 }
908
909 print_help(argv[0]);
910
911 return -1;
912}