test_drm_display.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. /*
  2. * Unit test: DRM display subsystem
  3. * Tests connector detection, CRTC, plane enumeration, and framebuffer creation.
  4. */
  5. #define _GNU_SOURCE
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <unistd.h>
  10. #include <fcntl.h>
  11. #include <errno.h>
  12. #include <sys/ioctl.h>
  13. #include <sys/mman.h>
  14. #include <poll.h>
  15. #include <linux/dma-heap.h>
  16. #include <xf86drm.h>
  17. #include <xf86drmMode.h>
  18. #include <drm_fourcc.h>
  19. static int dma_heap_alloc(const char *heap_name, size_t size, int *fd) {
  20. char path[64];
  21. snprintf(path, sizeof(path), "/dev/dma_heap/%s", heap_name);
  22. int heap_fd = open(path, O_RDWR | O_CLOEXEC);
  23. if (heap_fd < 0) return -1;
  24. struct dma_heap_allocation_data data = { .len = size, .fd_flags = O_RDWR | O_CLOEXEC };
  25. int ret = ioctl(heap_fd, DMA_HEAP_IOCTL_ALLOC, &data);
  26. close(heap_fd);
  27. if (ret < 0) return -1;
  28. *fd = data.fd;
  29. return 0;
  30. }
  31. static int create_fb(int drm_fd, int buf_fd, uint32_t w, uint32_t h,
  32. uint32_t stride, uint32_t format, uint32_t *fb_id) {
  33. uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
  34. if (drmPrimeFDToHandle(drm_fd, buf_fd, &handles[0])) {
  35. fprintf(stderr, " drmPrimeFDToHandle failed\n");
  36. return -1;
  37. }
  38. if (format == DRM_FORMAT_NV12) {
  39. handles[1] = handles[0];
  40. pitches[0] = stride;
  41. pitches[1] = stride;
  42. offsets[0] = 0;
  43. offsets[1] = stride * h;
  44. } else {
  45. pitches[0] = stride;
  46. offsets[0] = 0;
  47. }
  48. int ret = drmModeAddFB2(drm_fd, w, h, format, handles, pitches, offsets, fb_id, 0);
  49. if (ret)
  50. fprintf(stderr, " drmModeAddFB2(%ux%u fmt=0x%x stride=%u) failed: %s\n",
  51. w, h, format, stride, strerror(errno));
  52. return ret;
  53. }
  54. static void page_flip_handler(int fd, unsigned int seq,
  55. unsigned int tv_sec, unsigned int tv_usec,
  56. void *user_data) {
  57. (void)fd; (void)seq; (void)tv_sec; (void)tv_usec; (void)user_data;
  58. }
  59. int main(void) {
  60. printf("=== DRM Display Unit Test ===\n\n");
  61. int drm_fd = open("/dev/dri/card0", O_RDWR);
  62. if (drm_fd < 0) {
  63. perror("open /dev/dri/card0");
  64. return 1;
  65. }
  66. printf("Opened /dev/dri/card0 fd=%d\n\n", drm_fd);
  67. /* Try to become DRM master */
  68. if (drmSetMaster(drm_fd)) {
  69. printf("drmSetMaster failed: %s (errno=%d)\n", strerror(errno), errno);
  70. } else {
  71. printf("Acquired DRM master\n");
  72. }
  73. /* Set universal planes capability */
  74. if (drmSetClientCap(drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1))
  75. printf("UNIVERSAL_PLANES cap failed: %s\n", strerror(errno));
  76. else
  77. printf("Set UNIVERSAL_PLANES cap\n");
  78. if (drmSetClientCap(drm_fd, DRM_CLIENT_CAP_ATOMIC, 1))
  79. printf("ATOMIC cap failed: %s\n", strerror(errno));
  80. else
  81. printf("Set ATOMIC cap\n");
  82. /* --- Enumerate connectors --- */
  83. drmModeRes *res = drmModeGetResources(drm_fd);
  84. if (!res) {
  85. fprintf(stderr, "drmModeGetResources failed\n");
  86. return 1;
  87. }
  88. printf("Connectors: %d\n", res->count_connectors);
  89. uint32_t conn_id = 0, crtc_id = 0;
  90. for (int i = 0; i < res->count_connectors; i++) {
  91. drmModeConnector *conn = drmModeGetConnector(drm_fd, res->connectors[i]);
  92. if (!conn) continue;
  93. printf(" [%d] connector %u: %s, %d modes, %d encoders\n",
  94. i, conn->connector_id,
  95. conn->connection == DRM_MODE_CONNECTED ? "CONNECTED" : "disconnected",
  96. conn->count_modes, conn->count_encoders);
  97. if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes > 0 && !conn_id) {
  98. conn_id = conn->connector_id;
  99. printf(" => Selected connector %u, mode: %ux%u\n",
  100. conn_id, conn->modes[0].hdisplay, conn->modes[0].vdisplay);
  101. }
  102. drmModeFreeConnector(conn);
  103. }
  104. /* --- Enumerate CRTCs --- */
  105. printf("\nCRTCs: %d\n", res->count_crtcs);
  106. for (int i = 0; i < res->count_crtcs; i++) {
  107. drmModeCrtc *crtc = drmModeGetCrtc(drm_fd, res->crtcs[i]);
  108. if (!crtc) continue;
  109. printf(" [%d] crtc %u: fb=%u %ux%u\n",
  110. i, crtc->crtc_id, crtc->buffer_id,
  111. crtc->width, crtc->height);
  112. drmModeFreeCrtc(crtc);
  113. }
  114. /* --- Find encoder/CRTC for our connector --- */
  115. if (conn_id) {
  116. drmModeConnector *conn = drmModeGetConnector(drm_fd, conn_id);
  117. if (conn) {
  118. if (conn->encoder_id) {
  119. drmModeEncoder *enc = drmModeGetEncoder(drm_fd, conn->encoder_id);
  120. if (enc) {
  121. crtc_id = enc->crtc_id;
  122. printf("\nEncoder %u => crtc %u\n", enc->encoder_id, crtc_id);
  123. drmModeFreeEncoder(enc);
  124. }
  125. }
  126. if (!crtc_id && conn->count_encoders > 0) {
  127. drmModeEncoder *enc = drmModeGetEncoder(drm_fd, conn->encoders[0]);
  128. if (enc) {
  129. crtc_id = enc->crtc_id;
  130. printf("\nFallback encoder %u => crtc %u\n", enc->encoder_id, crtc_id);
  131. drmModeFreeEncoder(enc);
  132. }
  133. }
  134. drmModeFreeConnector(conn);
  135. }
  136. }
  137. if (!crtc_id && res->count_crtcs > 0) {
  138. crtc_id = res->crtcs[0];
  139. printf("\nFallback: using first crtc %u\n", crtc_id);
  140. }
  141. printf("Selected crtc: %u\n", crtc_id);
  142. /* --- Enumerate all planes --- */
  143. drmModePlaneRes *planes = drmModeGetPlaneResources(drm_fd);
  144. printf("\nPlanes: %d\n", planes ? planes->count_planes : 0);
  145. int crtc_index = -1;
  146. for (int i = 0; i < res->count_crtcs; i++) {
  147. if (res->crtcs[i] == crtc_id) { crtc_index = i; break; }
  148. }
  149. printf("CRTC %u is at index %d\n", crtc_id, crtc_index);
  150. uint32_t overlay_plane_id = 0;
  151. if (planes) {
  152. for (uint32_t i = 0; i < planes->count_planes; i++) {
  153. drmModePlane *p = drmModeGetPlane(drm_fd, planes->planes[i]);
  154. if (!p) continue;
  155. uint32_t plane_id = planes->planes[i];
  156. int compatible = (p->possible_crtcs & (1u << crtc_index)) != 0;
  157. drmModeObjectProperties *props =
  158. drmModeObjectGetProperties(drm_fd, plane_id, DRM_MODE_OBJECT_PLANE);
  159. uint64_t type_val = 0;
  160. uint64_t in_fmt_blob = 0;
  161. if (props) {
  162. for (uint32_t j = 0; j < props->count_props; j++) {
  163. drmModePropertyRes *prop = drmModeGetProperty(drm_fd, props->props[j]);
  164. if (!prop) continue;
  165. if (strcmp(prop->name, "type") == 0)
  166. type_val = props->prop_values[j];
  167. if (strcmp(prop->name, "IN_FORMATS") == 0)
  168. in_fmt_blob = props->prop_values[j];
  169. drmModeFreeProperty(prop);
  170. }
  171. }
  172. int has_nv12 = 0;
  173. if (in_fmt_blob) {
  174. drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(drm_fd, in_fmt_blob);
  175. if (blob && blob->length >= 4) {
  176. uint32_t *data = (uint32_t *)blob->data;
  177. uint32_t num_u32 = blob->length / 4;
  178. /* Dump first 16 u32 values */
  179. printf(" plane %u IN_FORMATS blob: %zu bytes (%u u32s)\n",
  180. plane_id, (size_t)blob->length, num_u32);
  181. for (uint32_t k = 0; k < (num_u32 < 16 ? num_u32 : 16); k++) {
  182. uint32_t v = data[k];
  183. char cc[5] = {(char)(v & 0xff), (char)((v >> 8) & 0xff),
  184. (char)((v >> 16) & 0xff), (char)((v >> 24) & 0xff), 0};
  185. printf(" [%u] = 0x%08x (%s)\n", k, v, cc);
  186. }
  187. /* The blob format is: count_formats(u32), count_modifiers(u32),
  188. formats[count_formats](u32), then modifier entries.
  189. But we might have a different format. Try scanning all u32s. */
  190. for (uint32_t k = 0; k < num_u32; k++) {
  191. if (data[k] == DRM_FORMAT_NV12) { has_nv12 = 1; break; }
  192. }
  193. }
  194. if (blob) drmModeFreePropertyBlob(blob);
  195. }
  196. const char *type_str = "Unknown";
  197. if (type_val == 0) type_str = "Overlay(0)";
  198. else if (type_val == 1) type_str = "Primary(1)";
  199. else if (type_val == 2) type_str = "Cursor(2)";
  200. printf(" plane %u: type=%s compat=%d nv12=%d possible_crtcs=0x%x\n",
  201. plane_id, type_str, compatible, has_nv12, p->possible_crtcs);
  202. /* Select overlay plane with NV12 support */
  203. if (type_val == 2 && has_nv12 && compatible && !overlay_plane_id) {
  204. overlay_plane_id = plane_id;
  205. printf(" => Selected overlay plane %u (NV12, compatible)\n", plane_id);
  206. }
  207. if (props) drmModeFreeObjectProperties(props);
  208. drmModeFreePlane(p);
  209. }
  210. /* If no compatible overlay, try any NV12 overlay */
  211. if (!overlay_plane_id) {
  212. printf("\nNo compatible NV12 overlay. Trying all NV12 overlays:\n");
  213. for (uint32_t i = 0; i < planes->count_planes; i++) {
  214. uint32_t plane_id = planes->planes[i];
  215. drmModeObjectProperties *props =
  216. drmModeObjectGetProperties(drm_fd, plane_id, DRM_MODE_OBJECT_PLANE);
  217. if (!props) continue;
  218. uint64_t type_val = 0, in_fmt_blob = 0;
  219. for (uint32_t j = 0; j < props->count_props; j++) {
  220. drmModePropertyRes *prop = drmModeGetProperty(drm_fd, props->props[j]);
  221. if (!prop) continue;
  222. if (strcmp(prop->name, "type") == 0) type_val = props->prop_values[j];
  223. if (strcmp(prop->name, "IN_FORMATS") == 0) in_fmt_blob = props->prop_values[j];
  224. drmModeFreeProperty(prop);
  225. }
  226. int has_nv12 = 0;
  227. if (in_fmt_blob) {
  228. drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(drm_fd, in_fmt_blob);
  229. if (blob && blob->length > 8) {
  230. uint32_t *data = (uint32_t *)blob->data;
  231. for (uint32_t f = 0; f < data[0]; f++) {
  232. if (data[2 + f] == DRM_FORMAT_NV12) { has_nv12 = 1; break; }
  233. }
  234. }
  235. if (blob) drmModeFreePropertyBlob(blob);
  236. }
  237. if (type_val == 2 && has_nv12 && !overlay_plane_id) {
  238. overlay_plane_id = plane_id;
  239. printf(" => Using plane %u (may not be compatible)\n", plane_id);
  240. }
  241. drmModeFreeObjectProperties(props);
  242. }
  243. }
  244. drmModeFreePlaneResources(planes);
  245. }
  246. printf("\nOverlay plane for display: %u\n", overlay_plane_id);
  247. /* --- Allocate DMA buffer and test FB creation --- */
  248. printf("\n--- FB creation test ---\n");
  249. int buf_fd = -1;
  250. size_t buf_sz = 1920 * 1080 * 3 / 2;
  251. if (dma_heap_alloc("reserved", buf_sz, &buf_fd) < 0) {
  252. if (dma_heap_alloc("system-uncached", buf_sz, &buf_fd) < 0) {
  253. fprintf(stderr, "Cannot allocate DMA buffer\n");
  254. return 1;
  255. }
  256. }
  257. void *map = mmap(NULL, buf_sz, PROT_READ | PROT_WRITE, MAP_SHARED, buf_fd, 0);
  258. if (map != MAP_FAILED) {
  259. memset(map, 0x10, 1920 * 1080); /* Y = black */
  260. memset((uint8_t*)map + 1920 * 1080, 0x80, buf_sz - 1920 * 1080); /* UV */
  261. }
  262. uint32_t fb_id = 0;
  263. int ret;
  264. /* Test 1: drmModeSetCrtc on primary plane (expected to fail with NV12) */
  265. printf("\nTest 1: drmModeSetCrtc with NV12...\n");
  266. ret = create_fb(drm_fd, buf_fd, 1920, 1080, 1920, DRM_FORMAT_NV12, &fb_id);
  267. if (ret == 0) {
  268. drmModeConnector *tc = drmModeGetConnector(drm_fd, conn_id);
  269. drmModeModeInfo mode_info;
  270. if (tc && tc->count_modes > 0) mode_info = tc->modes[0];
  271. else memset(&mode_info, 0, sizeof(mode_info));
  272. if (tc) drmModeFreeConnector(tc);
  273. ret = drmModeSetCrtc(drm_fd, crtc_id, fb_id, 0, 0, &conn_id, 1, &mode_info);
  274. if (ret) {
  275. printf(" FAIL: drmModeSetCrtc: %s (errno=%d)\n", strerror(errno), errno);
  276. } else {
  277. printf(" OK: drmModeSetCrtc succeeded\n");
  278. }
  279. drmModeRmFB(drm_fd, fb_id);
  280. fb_id = 0;
  281. }
  282. /* Test 2: Try Esmart1 (plane 107) on CRTC 121 via drmModeSetPlane */
  283. printf("\nTest 2: Force Esmart1 plane 107 on CRTC %u (ignoring possible_crtcs)...\n", crtc_id);
  284. fb_id = 0;
  285. ret = create_fb(drm_fd, buf_fd, 1920, 1080, 1920, DRM_FORMAT_NV12, &fb_id);
  286. if (ret == 0) {
  287. ret = drmModeSetPlane(drm_fd, 107, crtc_id, fb_id,
  288. 0, 0, 0, 1920, 1080,
  289. 0, 0, 1920 << 16, 1080 << 16);
  290. if (ret) {
  291. printf(" FAIL: drmModeSetPlane(107): %s (errno=%d)\n", strerror(errno), errno);
  292. } else {
  293. printf(" OK: Esmart1 plane 107 on CRTC %u succeeded!\n", crtc_id);
  294. sleep(2);
  295. }
  296. drmModeRmFB(drm_fd, fb_id);
  297. fb_id = 0;
  298. }
  299. /* Test 2b: Try atomic modesetting to force plane 107 on CRTC 121 */
  300. printf("\nTest 2b: Atomic modesetting - force plane 107 on CRTC %u...\n", crtc_id);
  301. fb_id = 0;
  302. ret = create_fb(drm_fd, buf_fd, 1920, 1080, 1920, DRM_FORMAT_NV12, &fb_id);
  303. if (ret == 0) {
  304. drmModeAtomicReq *req = drmModeAtomicAlloc();
  305. if (req) {
  306. /* Set the FB_ID on the plane */
  307. drmModeAtomicAddProperty(req, 107,
  308. /* We need property IDs, not names for atomic. Find them first. */
  309. 0, fb_id);
  310. /* Find property IDs for plane 107 */
  311. drmModeObjectProperties *props =
  312. drmModeObjectGetProperties(drm_fd, 107, DRM_MODE_OBJECT_PLANE);
  313. if (props) {
  314. uint32_t fb_prop_id = 0, crtc_prop_id = 0, src_prop_id = 0, dst_prop_id = 0;
  315. uint32_t src_x_prop = 0, src_y_prop = 0;
  316. for (uint32_t j = 0; j < props->count_props; j++) {
  317. drmModePropertyRes *prop = drmModeGetProperty(drm_fd, props->props[j]);
  318. if (!prop) continue;
  319. if (strcmp(prop->name, "FB_ID") == 0) fb_prop_id = props->props[j];
  320. if (strcmp(prop->name, "CRTC_ID") == 0) crtc_prop_id = props->props[j];
  321. if (strcmp(prop->name, "SRC_X") == 0) src_x_prop = props->props[j];
  322. if (strcmp(prop->name, "SRC_Y") == 0) src_y_prop = props->props[j];
  323. if (strcmp(prop->name, "SRC_W") == 0) src_prop_id = props->props[j];
  324. if (strcmp(prop->name, "SRC_H") == 0) dst_prop_id = props->props[j];
  325. drmModeFreeProperty(prop);
  326. }
  327. printf(" plane 107 props: FB_ID=%u CRTC_ID=%u\n", fb_prop_id, crtc_prop_id);
  328. if (fb_prop_id && crtc_prop_id) {
  329. drmModeAtomicFree(req);
  330. req = drmModeAtomicAlloc();
  331. drmModeAtomicAddProperty(req, 107, fb_prop_id, fb_id);
  332. drmModeAtomicAddProperty(req, 107, crtc_prop_id, crtc_id);
  333. /* Set source/dest rectangles via SRC_W/SRC_H/DST_W/DST_H properties */
  334. uint32_t dst_w_prop = 0, dst_h_prop = 0;
  335. for (uint32_t j = 0; j < props->count_props; j++) {
  336. drmModePropertyRes *prop = drmModeGetProperty(drm_fd, props->props[j]);
  337. if (!prop) continue;
  338. if (strcmp(prop->name, "DST_W") == 0) dst_w_prop = props->props[j];
  339. if (strcmp(prop->name, "DST_H") == 0) dst_h_prop = props->props[j];
  340. drmModeFreeProperty(prop);
  341. }
  342. if (src_x_prop) drmModeAtomicAddProperty(req, 107, src_x_prop, 0);
  343. if (src_y_prop) drmModeAtomicAddProperty(req, 107, src_y_prop, 0);
  344. if (src_prop_id) drmModeAtomicAddProperty(req, 107, src_prop_id, 1920 << 16);
  345. if (dst_prop_id) drmModeAtomicAddProperty(req, 107, dst_prop_id, 1080 << 16);
  346. if (dst_w_prop) drmModeAtomicAddProperty(req, 107, dst_w_prop, 1920);
  347. if (dst_h_prop) drmModeAtomicAddProperty(req, 107, dst_h_prop, 1080);
  348. uint32_t cursor_x_prop = 0, cursor_y_prop = 0;
  349. for (uint32_t j = 0; j < props->count_props; j++) {
  350. drmModePropertyRes *prop = drmModeGetProperty(drm_fd, props->props[j]);
  351. if (!prop) continue;
  352. if (strcmp(prop->name, "CRTC_X") == 0) cursor_x_prop = props->props[j];
  353. if (strcmp(prop->name, "CRTC_Y") == 0) cursor_y_prop = props->props[j];
  354. drmModeFreeProperty(prop);
  355. }
  356. if (cursor_x_prop) drmModeAtomicAddProperty(req, 107, cursor_x_prop, 0);
  357. if (cursor_y_prop) drmModeAtomicAddProperty(req, 107, cursor_y_prop, 0);
  358. printf(" Submitting atomic commit...\n");
  359. ret = drmModeAtomicCommit(drm_fd, req,
  360. DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
  361. if (ret) {
  362. printf(" FAIL: atomic commit: %s (errno=%d)\n", strerror(errno), errno);
  363. } else {
  364. printf(" OK: atomic commit succeeded!\n");
  365. sleep(2);
  366. }
  367. }
  368. drmModeFreeObjectProperties(props);
  369. }
  370. drmModeAtomicFree(req);
  371. }
  372. drmModeRmFB(drm_fd, fb_id);
  373. fb_id = 0;
  374. }
  375. /* Test 3: Use primary plane with XRGB8888 (expected to work) */
  376. printf("\nTest 3: drmModeSetCrtc with XRGB8888...\n");
  377. int xrgb_buf_fd = -1;
  378. {
  379. uint32_t handles2[4] = {0}, pitches2[4] = {0}, offsets2[4] = {0};
  380. size_t xrgb_sz = 1920 * 1080 * 4;
  381. if (dma_heap_alloc("system-uncached", xrgb_sz, &xrgb_buf_fd) == 0) {
  382. void *xrgb_map = mmap(NULL, xrgb_sz, PROT_READ | PROT_WRITE, MAP_SHARED, xrgb_buf_fd, 0);
  383. if (xrgb_map != MAP_FAILED) {
  384. memset(xrgb_map, 0x00, xrgb_sz);
  385. munmap(xrgb_map, xrgb_sz);
  386. }
  387. drmPrimeFDToHandle(drm_fd, xrgb_buf_fd, &handles2[0]);
  388. pitches2[0] = 1920 * 4;
  389. offsets2[0] = 0;
  390. ret = drmModeAddFB2(drm_fd, 1920, 1080, DRM_FORMAT_XRGB8888,
  391. handles2, pitches2, offsets2, &fb_id, 0);
  392. if (ret) {
  393. printf(" FAIL: drmModeAddFB2 XRGB8888: %s\n", strerror(errno));
  394. } else {
  395. drmModeConnector *tc2 = drmModeGetConnector(drm_fd, conn_id);
  396. drmModeModeInfo mode2;
  397. if (tc2 && tc2->count_modes > 0) mode2 = tc2->modes[0];
  398. else memset(&mode2, 0, sizeof(mode2));
  399. if (tc2) drmModeFreeConnector(tc2);
  400. ret = drmModeSetCrtc(drm_fd, crtc_id, fb_id, 0, 0, &conn_id, 1, &mode2);
  401. if (ret) {
  402. printf(" FAIL: drmModeSetCrtc XRGB8888: %s\n", strerror(errno));
  403. } else {
  404. printf(" OK: XRGB8888 modeset works!\n");
  405. }
  406. drmModeRmFB(drm_fd, fb_id);
  407. fb_id = 0;
  408. }
  409. close(xrgb_buf_fd);
  410. }
  411. }
  412. /* Test 4: Page flip test */
  413. if (overlay_plane_id) {
  414. printf("\nTest 4: Page flip via overlay plane...\n");
  415. fb_id = 0;
  416. ret = create_fb(drm_fd, buf_fd, 1920, 1080, 1920, DRM_FORMAT_NV12, &fb_id);
  417. if (ret == 0) {
  418. ret = drmModeSetPlane(drm_fd, overlay_plane_id, crtc_id, fb_id,
  419. 0, 0, 0, 1920, 1080,
  420. 0, 0, 1920 << 16, 1080 << 16);
  421. printf(" drmModeSetPlane: %s\n", ret ? "FAIL" : "OK");
  422. if (!ret) {
  423. sleep(1);
  424. drmEventContext ev = {.version = 2, .page_flip_handler = page_flip_handler};
  425. drmHandleEvent(drm_fd, &ev);
  426. printf(" OK: page flip handler returned\n");
  427. }
  428. drmModeRmFB(drm_fd, fb_id);
  429. }
  430. }
  431. if (map != MAP_FAILED) munmap(map, buf_sz);
  432. close(buf_fd);
  433. drmModeFreeResources(res);
  434. drmDropMaster(drm_fd);
  435. close(drm_fd);
  436. printf("\n=== Done ===\n");
  437. return 0;
  438. }