blob: 3a75cf1bcab0386b41f16cd99f761256352442d4 [file] [log] [blame]
Zach Reiznerc09812f2015-01-12 16:49:49 -08001/*
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 <errno.h>
10#include <fcntl.h>
11#include <stdbool.h>
12#include <stdio.h>
13#include <string.h>
14#include <stdlib.h>
15#include <sys/mman.h>
16#include <sys/stat.h>
17#include <sys/types.h>
18#include <unistd.h>
19
20#include <gbm.h>
21#include <drm_fourcc.h>
22#include <xf86drm.h>
23#include <xf86drmMode.h>
24
25#define BUFFERS 2
26
27struct context {
28 int drm_card_fd;
29 int vgem_card_fd;
30 struct gbm_device *drm_gbm;
31
32 drmModeRes *resources;
33 drmModeConnector *connector;
34 drmModeEncoder *encoder;
35 drmModeModeInfo *mode;
36
37 struct gbm_bo *gbm_buffer[BUFFERS];
38 uint32_t vgem_bo_handle[BUFFERS];
39 uint32_t drm_fb_id[BUFFERS];
40
41};
42
43const char g_sys_card_path_format[] =
44 "/sys/bus/platform/devices/vgem/drm/card%d";
45const char g_dev_card_path_format[] =
46 "/dev/dri/card%d";
47
48int drm_open_vgem()
49{
50 char *name;
51 int i, fd;
52
53 for (i = 0; i < 16; i++) {
54 struct stat _stat;
55 int ret;
56 ret = asprintf(&name, g_sys_card_path_format, i);
57 assert(ret != -1);
58
59 if (stat(name, &_stat) == -1) {
60 free(name);
61 continue;
62 }
63
64 free(name);
65 ret = asprintf(&name, g_dev_card_path_format, i);
66 assert(ret != -1);
67
68 fd = open(name, O_RDWR);
69 free(name);
70 if (fd == -1) {
71 continue;
72 }
73 return fd;
74 }
75 return -1;
76}
77
78void * mmap_dumb_bo(int fd, int handle, size_t size)
79{
80 struct drm_mode_map_dumb mmap_arg;
81 void *ptr;
82 int ret;
83
84 memset(&mmap_arg, 0, sizeof(mmap_arg));
85
86 mmap_arg.handle = handle;
87
88 ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mmap_arg);
89 assert(ret == 0);
90 assert(mmap_arg.offset != 0);
91
92 ptr = mmap(NULL, size, (PROT_READ|PROT_WRITE), MAP_SHARED, fd,
93 mmap_arg.offset);
94
95 assert(ptr != MAP_FAILED);
96
97 return ptr;
98}
99
100bool setup_drm(struct context *ctx)
101{
102 int fd = ctx->drm_card_fd;
103 drmModeRes *resources = NULL;
104 drmModeConnector *connector = NULL;
105 drmModeEncoder *encoder = NULL;
106 int i, j;
107
108 resources = drmModeGetResources(fd);
109 if (!resources) {
110 fprintf(stderr, "drmModeGetResources failed\n");
111 return false;
112 }
113
114 for (i = 0; i < resources->count_connectors; i++) {
115 connector = drmModeGetConnector(fd, resources->connectors[i]);
116 if (connector == NULL)
117 continue;
118
119 if (connector->connection == DRM_MODE_CONNECTED &&
120 connector->count_modes > 0)
121 break;
122
123 drmModeFreeConnector(connector);
124 }
125
126 if (i == resources->count_connectors) {
127 fprintf(stderr, "no currently active connector found\n");
128 drmModeFreeResources(resources);
129 return false;
130 }
131
132 for (i = 0; i < resources->count_encoders; i++) {
133 encoder = drmModeGetEncoder(fd, resources->encoders[i]);
134
135 if (encoder == NULL)
136 continue;
137
138 for (j = 0; j < connector->count_encoders; j++) {
139 if (encoder->encoder_id == connector->encoders[j])
140 break;
141 }
142
143 if (j == connector->count_encoders) {
144 drmModeFreeEncoder(encoder);
145 continue;
146 }
147
148 break;
149 }
150
151 if (i == resources->count_encoders) {
152 fprintf(stderr, "no supported encoder found\n");
153 drmModeFreeConnector(connector);
154 drmModeFreeResources(resources);
155 return false;
156 }
157
158 for (i = 0; i < resources->count_crtcs; i++) {
159 if (encoder->possible_crtcs & (1 << i)) {
160 encoder->crtc_id = resources->crtcs[i];
161 break;
162 }
163 }
164
165 if (i == resources->count_crtcs) {
166 fprintf(stderr, "no possible crtc found\n");
167 drmModeFreeEncoder(encoder);
168 drmModeFreeConnector(connector);
169 drmModeFreeResources(resources);
170 return false;
171 }
172
173 ctx->resources = resources;
174 ctx->connector = connector;
175 ctx->encoder = encoder;
176 ctx->mode = &connector->modes[0];
177
178 return true;
179}
180
181#define STEP_SKIP 0
182#define STEP_MMAP 1
183#define STEP_FAULT 2
184#define STEP_FLIP 3
185#define STEP_DRAW 4
186
187void show_sequence(const int *sequence)
188{
189 int sequence_subindex;
190 fprintf(stderr, "starting sequence: ");
191 for (sequence_subindex = 0; sequence_subindex < 4; sequence_subindex++) {
192 switch (sequence[sequence_subindex]) {
193 case STEP_SKIP:
194 break;
195 case STEP_MMAP:
196 fprintf(stderr, "mmap ");
197 break;
198 case STEP_FAULT:
199 fprintf(stderr, "fault ");
200 break;
201 case STEP_FLIP:
202 fprintf(stderr, "flip ");
203 break;
204 case STEP_DRAW:
205 fprintf(stderr, "draw ");
206 break;
207 default:
208 fprintf(stderr, "<unknown step %d> (aborting!)\n", sequence[sequence_subindex]);
209 abort();
210 break;
211 }
212 }
213 fprintf(stderr, "\n");
214}
215
216void draw(struct context *ctx)
217{
218 int i;
219
220 // Run the drawing routine with the key driver events in different
221 // sequences.
222 const int sequences[4][4] = {
223 { STEP_MMAP, STEP_FAULT, STEP_FLIP, STEP_DRAW },
224 { STEP_MMAP, STEP_FLIP, STEP_DRAW, STEP_SKIP },
225 { STEP_MMAP, STEP_DRAW, STEP_FLIP, STEP_SKIP },
226 { STEP_FLIP, STEP_MMAP, STEP_DRAW, STEP_SKIP },
227 };
228
229 int sequence_index = 0;
230 int sequence_subindex = 0;
231
232 int fb_idx = 1;
233
234 for (sequence_index = 0; sequence_index < 4; sequence_index++) {
235 show_sequence(sequences[sequence_index]);
236 for (i = 0; i < 0x100; i++) {
237 size_t bo_stride = gbm_bo_get_stride(ctx->gbm_buffer[fb_idx]);
238 size_t bo_size = gbm_bo_get_stride(ctx->gbm_buffer[fb_idx]) * gbm_bo_get_height(ctx->gbm_buffer[fb_idx]);
239 uint32_t *bo_ptr;
240 volatile uint32_t *ptr;
241
242 for (sequence_subindex = 0; sequence_subindex < 4; sequence_subindex++) {
243 switch (sequences[sequence_index][sequence_subindex]) {
244 case STEP_MMAP:
245 bo_ptr = mmap_dumb_bo(ctx->vgem_card_fd, ctx->vgem_bo_handle[fb_idx], bo_size);
246 ptr = bo_ptr;
247 break;
248
249 case STEP_FAULT:
250 *ptr = 1234567;
251 break;
252
253 case STEP_FLIP:
254 drmModePageFlip(ctx->drm_card_fd, ctx->encoder->crtc_id,
255 ctx->drm_fb_id[fb_idx],
256 0,
257 NULL);
258 break;
259
260 case STEP_DRAW:
261 for (ptr = bo_ptr; ptr < bo_ptr + (bo_size / sizeof(*bo_ptr)); ptr++) {
262 int y = ((void*)ptr - (void*)bo_ptr) / bo_stride;
263 int x = ((void*)ptr - (void*)bo_ptr - bo_stride * y) / sizeof(*ptr);
264 x -= 100;
265 y -= 100;
266 *ptr = 0xff000000;
267 if (x * x + y * y < i * i)
268 *ptr |= (i % 0x100) << 8;
269 else
270 *ptr |= 0xff | (sequence_index * 64 << 16);
271 }
272 break;
273
274 case STEP_SKIP:
275 default:
276 break;
277 }
278 }
279
280 munmap(bo_ptr, bo_size);
281
282 usleep(1e6 / 120); /* 120 Hz */
283
284 fb_idx = fb_idx ^ 1;
285 }
286 }
287}
288
289int main(int argc, char **argv)
290{
291 int ret = 0;
292 struct context ctx;
293 uint32_t bo_handle;
294 uint32_t bo_stride;
295 int drm_prime_fd;
296 size_t i;
297 char *drm_card_path = "/dev/dri/card0";
298
299 if (argc >= 2)
300 drm_card_path = argv[1];
301
302 ctx.drm_card_fd = open(drm_card_path, O_RDWR);
303 if (ctx.drm_card_fd < 0) {
304 fprintf(stderr, "failed to open %s\n", drm_card_path);
305 ret = 1;
306 goto fail;
307 }
308
309 ctx.vgem_card_fd = drm_open_vgem();
310 if (ctx.vgem_card_fd < 0) {
311 fprintf(stderr, "failed to open vgem card\n");
312 ret = 1;
313 goto close_drm_card;
314 }
315
316 ctx.drm_gbm = gbm_create_device(ctx.drm_card_fd);
317 if (!ctx.drm_gbm) {
318 fprintf(stderr, "failed to create gbm device on %s\n", drm_card_path);
319 ret = 1;
320 goto close_vgem_card;
321 }
322
323 if (!setup_drm(&ctx)) {
324 fprintf(stderr, "failed to setup drm resources\n");
325 ret = 1;
326 goto destroy_drm_gbm;
327 }
328
329 fprintf(stderr, "display size: %dx%d\n",
330 ctx.mode->hdisplay, ctx.mode->vdisplay);
331
332
333 for (i = 0; i < BUFFERS; ++i) {
334 ctx.gbm_buffer[i] = gbm_bo_create(ctx.drm_gbm,
335 ctx.mode->hdisplay, ctx.mode->vdisplay, GBM_BO_FORMAT_XRGB8888,
336 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
337
338 if (!ctx.gbm_buffer[i]) {
339 fprintf(stderr, "failed to create buffer object\n");
340 ret = 1;
341 goto free_buffers;
342 }
343
344 bo_handle = gbm_bo_get_handle(ctx.gbm_buffer[i]).u32;
345 bo_stride = gbm_bo_get_stride(ctx.gbm_buffer[i]);
346
347 drm_prime_fd = gbm_bo_get_fd(ctx.gbm_buffer[i]);
348
349 if (drm_prime_fd < 0) {
350 fprintf(stderr, "failed to turn handle into fd\n");
351 ret = 1;
352 goto free_buffers;
353 }
354
355 ret = drmPrimeFDToHandle(ctx.vgem_card_fd, drm_prime_fd,
356 &ctx.vgem_bo_handle[i]);
357 if (ret) {
358 fprintf(stderr, "failed to import handle\n");
359 ret = 1;
360 goto free_buffers;
361 }
362
363 ret = drmModeAddFB(ctx.drm_card_fd, ctx.mode->hdisplay, ctx.mode->vdisplay,
364 24, 32, bo_stride, bo_handle, &ctx.drm_fb_id[i]);
365
366 if (ret) {
367 fprintf(stderr, "failed to add fb\n");
368 ret = 1;
369 goto free_buffers;
370 }
371 }
372
373 if (drmModeSetCrtc(ctx.drm_card_fd, ctx.encoder->crtc_id, ctx.drm_fb_id[0],
374 0, 0, &ctx.connector->connector_id, 1, ctx.mode)) {
375 fprintf(stderr, "failed to set CRTC\n");
376 ret = 1;
377 goto free_buffers;
378 }
379
380 draw(&ctx);
381
382free_buffers:
383 for (i = 0; i < BUFFERS; ++i) {
384 if (ctx.drm_fb_id[i])
385 drmModeRmFB(ctx.drm_card_fd, ctx.drm_fb_id[i]);
386 if (ctx.gbm_buffer[i])
387 gbm_bo_destroy(ctx.gbm_buffer[i]);
388 }
389
390 drmModeFreeConnector(ctx.connector);
391 drmModeFreeEncoder(ctx.encoder);
392 drmModeFreeResources(ctx.resources);
393destroy_drm_gbm:
394 gbm_device_destroy(ctx.drm_gbm);
395close_vgem_card:
396 close(ctx.vgem_card_fd);
397close_drm_card:
398 close(ctx.drm_card_fd);
399fail:
400 return ret;
401}