1 /*
2 * Copyright 2014 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 #define _GNU_SOURCE
8 #include <assert.h>
9 #include <fcntl.h>
10 #include <stdbool.h>
11 #include <stddef.h>
12 #include <stdint.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/mman.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <unistd.h>
20 #include <xf86drm.h>
21 #include <xf86drmMode.h>
22
23 #include <gbm.h>
24
25 #define CHECK(cond) do {\
26 if (!(cond)) {\
27 printf("CHECK failed in %s() %s:%d\n", __func__, __FILE__, __LINE__);\
28 return 0;\
29 }\
30 } while(0)
31
32 #define ARRAY_SIZE(A) (sizeof(A)/sizeof(*(A)))
33
34 #define ENODRM -1
35 #define ENODISPLAY -2
36
37 static int fd;
38 static struct gbm_device *gbm;
39
40 static const uint32_t format_list[] = {
41 GBM_FORMAT_C8,
42 GBM_FORMAT_RGB332,
43 GBM_FORMAT_BGR233,
44 GBM_FORMAT_XRGB4444,
45 GBM_FORMAT_XBGR4444,
46 GBM_FORMAT_RGBX4444,
47 GBM_FORMAT_BGRX4444,
48 GBM_FORMAT_ARGB4444,
49 GBM_FORMAT_ABGR4444,
50 GBM_FORMAT_RGBA4444,
51 GBM_FORMAT_BGRA4444,
52 GBM_FORMAT_XRGB1555,
53 GBM_FORMAT_XBGR1555,
54 GBM_FORMAT_RGBX5551,
55 GBM_FORMAT_BGRX5551,
56 GBM_FORMAT_ARGB1555,
57 GBM_FORMAT_ABGR1555,
58 GBM_FORMAT_RGBA5551,
59 GBM_FORMAT_BGRA5551,
60 GBM_FORMAT_RGB565,
61 GBM_FORMAT_BGR565,
62 GBM_FORMAT_RGB888,
63 GBM_FORMAT_BGR888,
64 GBM_FORMAT_XRGB8888,
65 GBM_FORMAT_XBGR8888,
66 GBM_FORMAT_RGBX8888,
67 GBM_FORMAT_BGRX8888,
68 GBM_FORMAT_ARGB8888,
69 GBM_FORMAT_ABGR8888,
70 GBM_FORMAT_RGBA8888,
71 GBM_FORMAT_BGRA8888,
72 GBM_FORMAT_XRGB2101010,
73 GBM_FORMAT_XBGR2101010,
74 GBM_FORMAT_RGBX1010102,
75 GBM_FORMAT_BGRX1010102,
76 GBM_FORMAT_ARGB2101010,
77 GBM_FORMAT_ABGR2101010,
78 GBM_FORMAT_RGBA1010102,
79 GBM_FORMAT_BGRA1010102,
80 GBM_FORMAT_YUYV,
81 GBM_FORMAT_YVYU,
82 GBM_FORMAT_UYVY,
83 GBM_FORMAT_VYUY,
84 GBM_FORMAT_AYUV,
85 GBM_FORMAT_NV12,
86 GBM_FORMAT_YVU420,
87 };
88
89 static const uint32_t usage_list[] = {
90 GBM_BO_USE_SCANOUT,
91 GBM_BO_USE_CURSOR_64X64,
92 GBM_BO_USE_RENDERING,
93 GBM_BO_USE_LINEAR,
94 };
95
check_bo(struct gbm_bo * bo)96 static int check_bo(struct gbm_bo *bo)
97 {
98 uint32_t format;
99 size_t num_planes, plane;
100 int fd;
101 int i;
102
103 CHECK(bo);
104 CHECK(gbm_bo_get_width(bo) >= 0);
105 CHECK(gbm_bo_get_height(bo) >= 0);
106 CHECK(gbm_bo_get_stride(bo) >= gbm_bo_get_width(bo));
107
108 format = gbm_bo_get_format(bo);
109 for (i = 0; i < ARRAY_SIZE(format_list); i++)
110 if (format_list[i] == format)
111 break;
112 CHECK(i < ARRAY_SIZE(format_list));
113
114 num_planes = gbm_bo_get_num_planes(bo);
115 if (format == GBM_FORMAT_NV12)
116 CHECK(num_planes == 2);
117 else if (format == GBM_FORMAT_YVU420)
118 CHECK(num_planes == 3);
119 else
120 CHECK(num_planes == 1);
121
122 CHECK(gbm_bo_get_plane_handle(bo, 0).u32 == gbm_bo_get_handle(bo).u32);
123
124 CHECK(gbm_bo_get_plane_offset(bo, 0) == 0);
125 CHECK(gbm_bo_get_plane_size(bo, 0) >=
126 gbm_bo_get_width(bo) * gbm_bo_get_height(bo));
127 CHECK(gbm_bo_get_plane_stride(bo, 0) == gbm_bo_get_stride(bo));
128
129 for (plane = 0; plane < num_planes; plane++) {
130 CHECK(gbm_bo_get_plane_handle(bo, plane).u32);
131
132 fd = gbm_bo_get_plane_fd(bo, plane);
133 CHECK(fd > 0);
134 close(fd);
135
136 gbm_bo_get_plane_offset(bo, plane);
137 CHECK(gbm_bo_get_plane_size(bo, plane));
138 CHECK(gbm_bo_get_plane_stride(bo, plane));
139 }
140
141 return 1;
142 }
143
find_first_connected_connector(int fd,drmModeRes * resources)144 static drmModeConnector *find_first_connected_connector(int fd,
145 drmModeRes *resources)
146 {
147 int i;
148 for (i = 0; i < resources->count_connectors; i++) {
149 drmModeConnector *connector;
150
151 connector = drmModeGetConnector(fd, resources->connectors[i]);
152 if (connector) {
153 if ((connector->count_modes > 0) &&
154 (connector->connection == DRM_MODE_CONNECTED))
155 return connector;
156
157 drmModeFreeConnector(connector);
158 }
159 }
160 return NULL;
161 }
162
drm_open()163 static int drm_open()
164 {
165 int fd;
166 unsigned i;
167 bool has_drm_device = false;
168
169 for (i = 0; i < DRM_MAX_MINOR; i++) {
170 char* dev_name;
171 drmModeRes *res = NULL;
172 int ret;
173
174 ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i);
175 if (ret < 0)
176 continue;
177
178 fd = open(dev_name, O_RDWR, 0);
179 free(dev_name);
180 if (fd < 0)
181 continue;
182
183 res = drmModeGetResources(fd);
184 if (!res) {
185 drmClose(fd);
186 continue;
187 }
188
189 if (res->count_crtcs > 0 && res->count_connectors > 0) {
190 has_drm_device = true;
191 if (find_first_connected_connector(fd, res)) {
192 drmModeFreeResources(res);
193 return fd;
194 }
195 }
196
197 drmClose(fd);
198 drmModeFreeResources(res);
199 }
200
201 if (has_drm_device)
202 return ENODISPLAY;
203 else
204 return ENODRM;
205 }
206
drm_open_vgem()207 static int drm_open_vgem()
208 {
209 const char g_sys_card_path_format[] =
210 "/sys/bus/platform/devices/vgem/drm/card%d";
211 const char g_dev_card_path_format[] =
212 "/dev/dri/card%d";
213 char *name;
214 int i, fd;
215
216 for (i = 0; i < 16; i++) {
217 struct stat _stat;
218 int ret;
219 ret = asprintf(&name, g_sys_card_path_format, i);
220 assert(ret != -1);
221
222 if (stat(name, &_stat) == -1) {
223 free(name);
224 continue;
225 }
226
227 free(name);
228 ret = asprintf(&name, g_dev_card_path_format, i);
229 assert(ret != -1);
230
231 fd = open(name, O_RDWR);
232 free(name);
233 if (fd == -1) {
234 return -1;
235 }
236 return fd;
237 }
238 return -1;
239 }
240
create_vgem_bo(int fd,size_t size,uint32_t * handle)241 static int create_vgem_bo(int fd, size_t size, uint32_t * handle)
242 {
243 struct drm_mode_create_dumb create;
244 int ret;
245
246 memset(&create, 0, sizeof(create));
247 create.height = size;
248 create.width = 1;
249 create.bpp = 8;
250
251 ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create);
252 if (ret)
253 return ret;
254
255 assert(create.size >= size);
256
257 *handle = create.handle;
258
259 return 0;
260 }
261
262 /*
263 * Tests initialization.
264 */
test_init()265 static int test_init()
266 {
267 fd = drm_open();
268 if (fd == ENODISPLAY)
269 return ENODISPLAY;
270 CHECK(fd >= 0);
271
272 gbm = gbm_create_device(fd);
273
274 CHECK(gbm_device_get_fd(gbm) == fd);
275
276 const char* backend_name = gbm_device_get_backend_name(gbm);
277
278 CHECK(backend_name);
279
280 return 1;
281 }
282
283 /*
284 * Tests reinitialization.
285 */
test_reinit()286 static int test_reinit()
287 {
288 gbm_device_destroy(gbm);
289 close(fd);
290
291 fd = drm_open();
292 CHECK(fd >= 0);
293
294 gbm = gbm_create_device(fd);
295
296 CHECK(gbm_device_get_fd(gbm) == fd);
297
298 struct gbm_bo *bo;
299 bo = gbm_bo_create(gbm, 1024, 1024, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING);
300 CHECK(check_bo(bo));
301 gbm_bo_destroy(bo);
302
303 return 1;
304 }
305
306 /*
307 * Tests repeated alloc/free.
308 */
test_alloc_free()309 static int test_alloc_free()
310 {
311 int i;
312 for(i = 0; i < 1000; i++) {
313 struct gbm_bo *bo;
314 bo = gbm_bo_create(gbm, 1024, 1024, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING);
315 CHECK(check_bo(bo));
316 gbm_bo_destroy(bo);
317 }
318 return 1;
319 }
320
321 /*
322 * Tests that we can allocate different buffer dimensions.
323 */
test_alloc_free_sizes()324 static int test_alloc_free_sizes()
325 {
326 int i;
327 for(i = 1; i < 1920; i++) {
328 struct gbm_bo *bo;
329 bo = gbm_bo_create(gbm, i, i, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING);
330 CHECK(check_bo(bo));
331 gbm_bo_destroy(bo);
332 }
333
334 for(i = 1; i < 1920; i++) {
335 struct gbm_bo *bo;
336 bo = gbm_bo_create(gbm, i, 1, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING);
337 CHECK(check_bo(bo));
338 gbm_bo_destroy(bo);
339 }
340
341 for(i = 1; i < 1920; i++) {
342 struct gbm_bo *bo;
343 bo = gbm_bo_create(gbm, 1, i, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING);
344 CHECK(check_bo(bo));
345 gbm_bo_destroy(bo);
346 }
347
348 return 1;
349 }
350
351 /*
352 * Tests that we can allocate different buffer formats.
353 */
test_alloc_free_formats()354 static int test_alloc_free_formats()
355 {
356 int i;
357
358 for(i = 0; i < ARRAY_SIZE(format_list); i++) {
359 uint32_t format = format_list[i];
360 if (gbm_device_is_format_supported(gbm, format, GBM_BO_USE_RENDERING)) {
361 struct gbm_bo *bo;
362 bo = gbm_bo_create(gbm, 1024, 1024, format, GBM_BO_USE_RENDERING);
363 CHECK(check_bo(bo));
364 }
365 }
366
367 return 1;
368 }
369
370 /*
371 * Tests that we find at least one working format for each usage.
372 */
test_alloc_free_usage()373 static int test_alloc_free_usage()
374 {
375 int i, j;
376
377 for(i = 0; i < ARRAY_SIZE(usage_list); i++) {
378 uint32_t usage = usage_list[i];
379 int found = 0;
380 for(j = 0; j < ARRAY_SIZE(format_list); j++) {
381 uint32_t format = format_list[j];
382 if (gbm_device_is_format_supported(gbm, format, usage)) {
383 struct gbm_bo *bo;
384 bo = gbm_bo_create(gbm, 1024, 1024, format, usage);
385 CHECK(check_bo(bo));
386 found = 1;
387 }
388 }
389 CHECK(found);
390 }
391
392 return 1;
393 }
394
395 /*
396 * Tests user data.
397 */
398 static int been_there1;
399 static int been_there2;
400
destroy_data1(struct gbm_bo * bo,void * data)401 void destroy_data1(struct gbm_bo *bo, void *data)
402 {
403 been_there1 = 1;
404 }
405
destroy_data2(struct gbm_bo * bo,void * data)406 void destroy_data2(struct gbm_bo *bo, void *data)
407 {
408 been_there2 = 1;
409 }
410
test_user_data()411 static int test_user_data()
412 {
413 struct gbm_bo *bo1, *bo2;
414 char *data1, *data2;
415
416 been_there1 = 0;
417 been_there2 = 0;
418
419 bo1 = gbm_bo_create(gbm, 1024, 1024, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING);
420 bo2 = gbm_bo_create(gbm, 1024, 1024, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING);
421 data1 = (char*)malloc(1);
422 data2 = (char*)malloc(1);
423 CHECK(data1);
424 CHECK(data2);
425
426 gbm_bo_set_user_data(bo1, data1, destroy_data1);
427 gbm_bo_set_user_data(bo2, data2, destroy_data2);
428
429 CHECK((char*)gbm_bo_get_user_data(bo1) == data1);
430 CHECK((char*)gbm_bo_get_user_data(bo2) == data2);
431
432 gbm_bo_destroy(bo1);
433 CHECK(been_there1 == 1);
434
435 gbm_bo_set_user_data(bo2, NULL, NULL);
436 gbm_bo_destroy(bo2);
437 CHECK(been_there2 == 0);
438
439 free(data1);
440 free(data2);
441
442 return 1;
443 }
444
445 /*
446 * Tests destruction.
447 */
test_destroy()448 static int test_destroy()
449 {
450 gbm_device_destroy(gbm);
451 close(fd);
452
453 return 1;
454 }
455
456 /*
457 * Tests prime export.
458 */
test_export()459 static int test_export()
460 {
461 struct gbm_bo *bo;
462 int prime_fd;
463
464 bo = gbm_bo_create(gbm, 1024, 1024, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING);
465 CHECK(check_bo(bo));
466
467 prime_fd = gbm_bo_get_fd(bo);
468 CHECK(prime_fd > 0);
469 close(prime_fd);
470
471 gbm_bo_destroy(bo);
472
473 return 1;
474 }
475
476 /*
477 * Tests prime import using VGEM sharing buffer.
478 */
test_import_vgem()479 static int test_import_vgem()
480 {
481 struct gbm_import_fd_data fd_data;
482 int vgem_fd = drm_open_vgem();
483 struct drm_prime_handle prime_handle;
484 struct gbm_bo *bo;
485 const int width = 123;
486 const int height = 456;
487 const int bytes_per_pixel = 4;
488 const int size = width * height * bytes_per_pixel;
489
490 if (vgem_fd <= 0)
491 return 1;
492
493 CHECK(create_vgem_bo(vgem_fd, size, &prime_handle.handle) == 0);
494 prime_handle.flags = DRM_CLOEXEC;
495 CHECK(drmIoctl(vgem_fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &prime_handle) == 0);
496
497 fd_data.fd = prime_handle.fd;
498 fd_data.width = width;
499 fd_data.height = height;
500 fd_data.stride = width * bytes_per_pixel;
501 fd_data.format = GBM_FORMAT_XRGB8888;
502
503 bo = gbm_bo_import(gbm, GBM_BO_IMPORT_FD, &fd_data, GBM_BO_USE_RENDERING);
504 CHECK(check_bo(bo));
505 gbm_bo_destroy(bo);
506 close(prime_handle.fd);
507
508 close(vgem_fd);
509
510 return 1;
511 }
512
513 /*
514 * Tests prime import using dma-buf API.
515 */
test_import_dmabuf()516 static int test_import_dmabuf()
517 {
518 struct gbm_import_fd_data fd_data;
519 struct gbm_bo *bo1, *bo2;
520 const int width = 123;
521 const int height = 456;
522 int prime_fd;
523
524 bo1 = gbm_bo_create(gbm, width, height, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING);
525 CHECK(check_bo(bo1));
526
527 prime_fd = gbm_bo_get_fd(bo1);
528 CHECK(prime_fd >= 0);
529
530 fd_data.fd = prime_fd;
531 fd_data.width = width;
532 fd_data.height = height;
533 fd_data.stride = gbm_bo_get_stride(bo1);
534 fd_data.format = GBM_FORMAT_XRGB8888;
535
536 gbm_bo_destroy(bo1);
537
538 bo2 = gbm_bo_import(gbm, GBM_BO_IMPORT_FD, &fd_data, GBM_BO_USE_RENDERING);
539 CHECK(check_bo(bo2));
540 CHECK(fd_data.width == gbm_bo_get_width(bo2));
541 CHECK(fd_data.height == gbm_bo_get_height(bo2));
542 CHECK(fd_data.stride == gbm_bo_get_stride(bo2));
543
544 gbm_bo_destroy(bo2);
545 close(prime_fd);
546
547 return 1;
548 }
549
550
551 /*
552 * Tests GBM_BO_IMPORT_FD_PLANAR entry point.
553 */
test_import_planar()554 static int test_import_planar()
555 {
556 struct gbm_import_fd_planar_data fd_data;
557 struct gbm_bo *bo1, *bo2;
558 const int width = 567;
559 const int height = 891;
560 size_t num_planes, p;
561 int i;
562
563 for (i = 0; i < ARRAY_SIZE(format_list); i++) {
564 uint32_t format = format_list[i];
565 if (gbm_device_is_format_supported(gbm, format, GBM_BO_USE_RENDERING)) {
566 bo1 = gbm_bo_create(gbm, width, height, format, GBM_BO_USE_RENDERING);
567 CHECK(check_bo(bo1));
568
569 num_planes = gbm_bo_get_num_planes(bo1);
570 for (p = 0; p < num_planes; p++) {
571 fd_data.fds[p] = gbm_bo_get_plane_fd(bo1, p);
572 CHECK(fd_data.fds[p] >= 0);
573
574 fd_data.strides[p] = gbm_bo_get_plane_stride(bo1, p);
575 fd_data.offsets[p] = gbm_bo_get_plane_offset(bo1, p);
576 fd_data.format_modifiers[p] =
577 gbm_bo_get_plane_format_modifier(bo1, p);
578 }
579
580 fd_data.width = width;
581 fd_data.height = height;
582 fd_data.format = format;
583
584 gbm_bo_destroy(bo1);
585
586 bo2 = gbm_bo_import(gbm, GBM_BO_IMPORT_FD_PLANAR, &fd_data,
587 GBM_BO_USE_RENDERING);
588
589 CHECK(check_bo(bo2));
590 CHECK(fd_data.width == gbm_bo_get_width(bo2));
591 CHECK(fd_data.height == gbm_bo_get_height(bo2));
592
593 for (p = 0; p < num_planes; p++) {
594 CHECK(fd_data.strides[p] == gbm_bo_get_plane_stride(bo2, p));
595 CHECK(fd_data.offsets[p] == gbm_bo_get_plane_offset(bo2, p));
596 CHECK(fd_data.format_modifiers[p] ==
597 gbm_bo_get_plane_format_modifier(bo2, p));
598 }
599
600 gbm_bo_destroy(bo2);
601
602 for (p = 0; p < num_planes; p++)
603 close(fd_data.fds[p]);
604 }
605 }
606
607 return 1;
608 }
609
test_gem_map()610 static int test_gem_map()
611 {
612 uint32_t *pixel, pixel_size;
613 struct gbm_bo *bo;
614 void *map_data, *addr;
615
616 uint32_t stride = 0;
617 const int width = 666;
618 const int height = 777;
619
620 addr = map_data = NULL;
621
622 bo = gbm_bo_create(gbm, width, height, GBM_FORMAT_ARGB8888, GBM_BO_USE_LINEAR);
623 CHECK(check_bo(bo));
624
625 addr = gbm_bo_map(bo, 0, 0, width, height, GBM_BO_TRANSFER_READ_WRITE, &stride,
626 &map_data, 0);
627
628 CHECK(addr != MAP_FAILED);
629 CHECK(map_data);
630 CHECK(stride > 0);
631
632 pixel = (uint32_t *)addr;
633 pixel_size = sizeof(*pixel);
634
635 pixel[(height / 2) * (stride / pixel_size) + width / 2] = 0xABBAABBA;
636 gbm_bo_unmap(bo, map_data);
637
638 /* Re-map and verify written previously data. */
639 stride = 0;
640 addr = map_data = NULL;
641
642 addr = gbm_bo_map(bo, 0, 0, width, height, GBM_BO_TRANSFER_READ_WRITE, &stride,
643 &map_data, 0);
644
645 CHECK(addr != MAP_FAILED);
646 CHECK(map_data);
647 CHECK(stride > 0);
648
649 pixel = (uint32_t *)addr;
650 CHECK(pixel[(height / 2) * (stride / pixel_size) + width / 2] == 0xABBAABBA);
651
652 gbm_bo_unmap(bo, map_data);
653 gbm_bo_destroy(bo);
654
655 return 1;
656 }
657
main(int argc,char * argv[])658 int main(int argc, char *argv[])
659 {
660 int result;
661
662 result = test_init();
663 if (result == ENODISPLAY) {
664 printf("[ PASSED ] graphics_Gbm test no connected display found\n");
665 return EXIT_SUCCESS;
666 } else if (!result) {
667 printf("[ FAILED ] graphics_Gbm test initialization failed\n");
668 return EXIT_FAILURE;
669 }
670
671 result &= test_reinit();
672 result &= test_alloc_free();
673 result &= test_alloc_free_sizes();
674 result &= test_alloc_free_formats();
675 result &= test_alloc_free_usage();
676 result &= test_user_data();
677 result &= test_export();
678 result &= test_import_vgem();
679 result &= test_import_dmabuf();
680 result &= test_import_planar();
681 result &= test_gem_map();
682 result &= test_destroy();
683
684 if (!result) {
685 printf("[ FAILED ] graphics_Gbm test failed\n");
686 return EXIT_FAILURE;
687 } else {
688 printf("[ PASSED ] graphics_Gbm test success\n");
689 return EXIT_SUCCESS;
690 }
691 }
692
693