1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdint.h>
5 #include <stdbool.h>
7 #include <fcntl.h>
8 #include <unistd.h>
9 #include <sys/mman.h>
10 #include <sys/ioctl.h>
12 #include <xf86drm.h>
13 #include <xf86drmMode.h>
15 #include <drm_mode.h>
16 #include <drm_fourcc.h>
18 #include <list.h>
19 #include <biqueue.h>
20 #include <drm_util.h>
22 #define NUM_BUFFERS_PER_LAYER (6)
24 #if defined (CONFIG_DISPLAY_SPLIT)
25 extern bool plane_split;
26 #endif
28 static struct {
29 int fd;
30 int connector_id;
31 int crtc_id;
32 drmModeModeInfo mode;
33 uint32_t mode_blob;
35 struct {
36 struct biqueue *bq;
37 int plane_id;
38 int width;
39 int height;
40 int zorder;
41 bool opaque;
42 int pos_x;
43 int pos_y;
44 } layers[32];
45 int num_layers;
46 } drm;
48 int setup_drm(char *dev_fname)
49 {
50 int r;
52 drm.fd = open(dev_fname, O_RDWR);
53 if(drm.fd < 0) {
54 printf("unable to open card %s\n", dev_fname);
55 return -1;
56 }
58 r = drmSetMaster(drm.fd);
59 if(r) {
60 printf("unable to set master\n");
61 goto err1;
62 }
64 r = drmSetClientCap(drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
65 if(r) {
66 printf("unable to set universal planes\n");
67 goto err1;
68 }
70 r = drmSetClientCap(drm.fd, DRM_CLIENT_CAP_ATOMIC, 1);
71 if(r) {
72 printf("unable to set atomic\n");
73 goto err1;
74 }
76 return 0;
78 err1:
79 close(drm.fd);
80 return -1;
81 }
83 static int get_zorder(int fd, uint32_t plane_id)
84 {
85 int i, r;
86 drmModeObjectPropertiesPtr props = NULL;
88 props = drmModeObjectGetProperties(fd, plane_id, DRM_MODE_OBJECT_PLANE);
89 if(!props) {
90 printf("drmModeObjectGetProperties for plane %u failed\n", plane_id);
91 return -1;
92 }
94 for(i = 0; i < props->count_props; i++) {
95 drmModePropertyPtr prop = drmModeGetProperty(fd, props->props[i]);
96 if(!prop) {
97 printf("getting prop = %u for plane %u failed\n", props->props[i], plane_id);
98 drmModeFreeObjectProperties(props);
99 return -1;
100 }
101 if(strcmp("zpos", prop->name) == 0) {
102 r = props->prop_values[i];
103 drmModeFreeProperty(prop);
104 drmModeFreeObjectProperties(props);
105 return r;
106 }
107 drmModeFreeProperty(prop);
109 }
110 printf("could not find property zpos for plane = %u\n", plane_id);
111 drmModeFreeObjectProperties(props);
113 return -1;
114 }
116 static int add_property(int fd, drmModeAtomicReqPtr req, uint32_t id, uint32_t type, char *prop_name, uint64_t value)
117 {
118 int i, r;
119 drmModeObjectPropertiesPtr props = NULL;
121 props = drmModeObjectGetProperties(fd, id, type);
122 if(!props) {
123 printf("drmModeObjectGetProperties for id = %u failed\n", id);
124 return -1;
125 }
127 for(i = 0; i < props->count_props; i++) {
128 drmModePropertyPtr prop = drmModeGetProperty(fd, props->props[i]);
129 if(!prop) {
130 printf("getting prop = %u for id = %u failed\n", props->props[i], id);
131 drmModeFreeObjectProperties(props);
132 return -1;
133 }
134 if(strcmp(prop_name, prop->name) == 0) {
135 r = drmModeAtomicAddProperty(req, id, props->props[i], value);
136 if(r < 0) {
137 printf("set property %s for id = %u failed\n", prop_name, id);
138 }
139 drmModeFreeProperty(prop);
140 drmModeFreeObjectProperties(props);
141 return r < 0 ? -1 : 0;
142 }
143 drmModeFreeProperty(prop);
144 }
146 printf("could not find property %s for id = %u\n", prop_name, id);
147 drmModeFreeObjectProperties(props);
148 return -1;
149 }
151 static struct buffer *drm_new_buffer(uint32_t w, uint32_t h, uint32_t format)
152 {
153 struct drm_mode_destroy_dumb close_req;
154 struct drm_mode_create_dumb create_req;
155 struct drm_mode_map_dumb map_req;
156 struct buffer *buf;
157 void *vaddr;
158 uint32_t fb_id;
159 int r;
161 buf = calloc(sizeof(*buf), 1);
162 if(!buf) {
163 printf("unable to calloc buffer\n");
164 return NULL;
165 }
167 memset(&create_req, 0, sizeof(create_req));
168 create_req.width = w;
169 create_req.height =h;
170 create_req.bpp = 32;
171 r = ioctl(drm.fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_req);
172 if(r) {
173 printf("unable to create dumb buffer\n");
174 goto err1;
175 }
177 memset(&map_req, 0, sizeof(map_req));
178 map_req.handle = create_req.handle;
179 r = ioctl(drm.fd, DRM_IOCTL_MODE_MAP_DUMB, &map_req);
180 if(r) {
181 printf("unable to get map info for dumb buffer\n");
182 goto err2;
183 }
185 vaddr = mmap(0, create_req.size, PROT_READ | PROT_WRITE, MAP_SHARED, drm.fd, map_req.offset);
186 if(vaddr == MAP_FAILED) {
187 printf("unable to map dumb buffer\n");
188 goto err2;
189 }
191 r = drmModeAddFB(drm.fd, w, h, format == DRM_FORMAT_XRGB8888 ? 24 : 32,
192 32, create_req.pitch, create_req.handle, &fb_id);
193 if(r) {
194 printf("unable to create FB\n");
195 exit(-1);
196 }
198 buf->width = w;
199 buf->height = h;
200 buf->handle = create_req.handle;
201 buf->vaddr = vaddr;
202 buf->format = format;
203 buf->stride = create_req.pitch;
204 buf->fb_id = fb_id;
206 return buf;
208 err2:
209 memset(&close_req, 0, sizeof(close_req));
210 close_req.handle = create_req.handle;
211 ioctl(drm.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &close_req);
212 err1:
213 free(buf);
214 return NULL;
215 }
217 int drm_setup_display(int w, int h, int *rw, int *rh)
218 {
219 int i, j;
220 drmModeResPtr res;
221 drmModeConnectorPtr conn = NULL;
222 drmModeModeInfoPtr mode = NULL;
223 drmModeEncoderPtr enc = NULL;
224 drmModeCrtcPtr crtc = NULL;
226 res = drmModeGetResources(drm.fd);
227 if(!res)
228 return -1;
230 for(i = 0; i < res->count_connectors; i++) {
231 conn = drmModeGetConnector(drm.fd, res->connectors[i]);
232 if(!conn)
233 return -1;
235 for(j = 0; j < conn->count_modes; j++)
236 if(conn->modes[j].hdisplay == w && conn->modes[j].vdisplay == h) {
237 mode = &conn->modes[j];
238 break;
239 }
241 if(mode)
242 break;
244 drmModeFreeConnector(conn);
245 conn = NULL;
246 }
248 if(!conn || !mode)
249 return -1;
251 for(i = 0; i < conn->count_encoders; i++) {
252 enc = drmModeGetEncoder(drm.fd, conn->encoders[i]);
253 if(!enc)
254 return -1;
256 for(j = 0; j < res->count_crtcs; j++) {
257 int crtc_id;
258 uint32_t crtc_mask;
260 crtc_id = res->crtcs[j];
261 crtc_mask = 1 << j;
262 if(enc->possible_crtcs & crtc_mask) {
263 crtc = drmModeGetCrtc(drm.fd, res->crtcs[j]);
264 if(!crtc)
265 return -1;
266 break;
267 }
268 }
270 if(crtc)
271 break;
272 }
275 if(!enc || !crtc)
276 return -1;
279 drm.connector_id = conn->connector_id;
280 drm.crtc_id = crtc->crtc_id;
281 memcpy(&drm.mode, mode, sizeof(*mode));
282 drmModeCreatePropertyBlob(drm.fd, &drm.mode, sizeof(drmModeModeInfo), &drm.mode_blob);
284 drmModeFreeConnector(conn);
285 drmModeFreeEncoder(enc);
286 drmModeFreeCrtc(crtc);
287 drmModeFreeResources(res);
289 if(rw)
290 *rw = drm.mode.hdisplay;
291 if(rh)
292 *rh = drm.mode.vdisplay;
294 return 0;
295 }
297 int drm_reset()
298 {
299 int i, r;
300 drmModeResPtr res;
301 drmModePlaneResPtr pres;
302 drmModeAtomicReqPtr disable_req, enable_req;
304 res = drmModeGetResources(drm.fd);
305 pres = drmModeGetPlaneResources(drm.fd);
307 disable_req = drmModeAtomicAlloc();
308 enable_req = drmModeAtomicAlloc();
310 r = 0;
311 for(i = 0; i < res->count_crtcs; i++)
312 r += add_property(drm.fd, disable_req, res->crtcs[i], DRM_MODE_OBJECT_CRTC, "ACTIVE", 0);
313 for(i = 0; i < pres->count_planes; i++) {
314 r += add_property(drm.fd, disable_req, pres->planes[i], DRM_MODE_OBJECT_PLANE, "FB_ID", 0);
315 r += add_property(drm.fd, disable_req, pres->planes[i], DRM_MODE_OBJECT_PLANE, "CRTC_ID", 0);
316 }
317 if(r)
318 return -1;
320 r = drmModeAtomicCommit(drm.fd, disable_req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
321 if(r) {
322 printf("disable atomic commit failed\n");
323 return -1;
324 }
326 r = 0;
327 r += add_property(drm.fd, enable_req, drm.connector_id, DRM_MODE_OBJECT_CONNECTOR, "CRTC_ID", drm.crtc_id);
328 r += add_property(drm.fd, enable_req, drm.crtc_id, DRM_MODE_OBJECT_CRTC, "ACTIVE", 1);
329 r += add_property(drm.fd, enable_req, drm.crtc_id, DRM_MODE_OBJECT_CRTC, "MODE_ID", drm.mode_blob);
330 if(r)
331 return -1;
333 r = drmModeAtomicCommit(drm.fd, enable_req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
334 if(r) {
335 printf("enable atomic commit failed\n");
336 exit(-1);
337 }
339 drmModeAtomicFree(disable_req);
340 drmModeAtomicFree(enable_req);
342 return 0;
343 }
345 static int drm_get_new_plane(int zorder)
346 {
347 int i, j;
348 drmModeResPtr res;
349 drmModePlaneResPtr pres;
350 uint32_t crtc_mask;
351 int plane_id;
352 drmModePlanePtr plane = NULL;
354 pres = drmModeGetPlaneResources(drm.fd);
355 res = drmModeGetResources(drm.fd);
356 if(!res || !pres)
357 return -1;
359 for(i = 0; i < res->count_crtcs; i++)
360 if(res->crtcs[i] == drm.crtc_id)
361 crtc_mask = 1 << i;
363 for(i = 0; i < pres->count_planes; i++) {
364 plane = drmModeGetPlane(drm.fd, pres->planes[i]);
365 if(!plane)
366 return -1;
368 if((plane->possible_crtcs & crtc_mask) == 0) {
369 drmModeFreePlane(plane);
370 continue;
371 }
373 bool occupied = false;
374 for(j = 0; j < drm.num_layers; j++)
375 if(drm.layers[j].plane_id == plane->plane_id) {
376 occupied = true;
377 break;
378 }
380 if(occupied)
381 continue;
383 if(get_zorder(drm.fd, plane->plane_id) == zorder)
384 break;
385 }
387 if(!plane)
388 return -1;
390 plane_id = plane->plane_id;
391 drmModeFreePlane(plane);
393 return plane_id;
394 }
396 int drm_add_layer(int w, int h, int pos_x, int pos_y, int zorder, bool opaque)
397 {
398 int i;
399 int count = drm.num_layers;
400 struct list_head buffers;
403 drm.layers[count].plane_id = drm_get_new_plane(zorder);
404 if(drm.layers[count].plane_id < 0) {
405 printf("could not get a free plane\n");
406 return -1;
407 }
409 INIT_LIST_HEAD(&buffers);
410 for(i = 0; i < NUM_BUFFERS_PER_LAYER; i++) {
411 struct buffer *buf = drm_new_buffer(w, h, opaque ? DRM_FORMAT_XRGB8888 : DRM_FORMAT_ARGB8888);
412 list_add_tail(&buf->link, &buffers);
413 }
414 drm.layers[count].bq = bq_init(&buffers);
416 drm.layers[count].zorder = zorder;
417 drm.layers[count].width = w;
418 drm.layers[count].height = h;
419 drm.layers[count].pos_x = pos_x;
420 drm.layers[count].pos_y = pos_y;
421 drm.layers[count].opaque = opaque;
423 drm.num_layers++;
425 return count;
427 }
429 void drm_print_allocations()
430 {
431 int i;
433 printf("%s: using connector_id %d\n", __func__, drm.connector_id);
434 printf("%s: using crtc_id %d\n", __func__, drm.crtc_id);
435 printf("%s: using mode %s\n", __func__, drm.mode.name);
436 for(i = 0; i < drm.num_layers; i++)
437 printf("%s: using plane_id %d for layer %d\n", __func__, drm.layers[i].plane_id, i);
438 }
440 struct biqueue *drm_get_bq(int index)
441 {
442 return drm.layers[index].bq;
443 }
445 void *drm_loop(void *arg)
446 {
447 int i, r;
448 int count;
449 drmModeAtomicReqPtr commit_req;
450 struct buffer *old_buffers[32] = {NULL};
451 struct buffer *mid_buffers[32] = {NULL};
452 struct buffer *new_buffers[32];
454 while(true) {
455 commit_req = drmModeAtomicAlloc();
456 count = drm.num_layers;
458 r = 0;
459 for(i = 0; i < count; i++) {
460 struct buffer *buf = list_entry(bq_next_full(drm.layers[i].bq), struct buffer, link);
461 new_buffers[i] = buf;
463 r += add_property(drm.fd, commit_req, drm.layers[i].plane_id, DRM_MODE_OBJECT_PLANE, "FB_ID", buf->fb_id);
464 r += add_property(drm.fd, commit_req, drm.layers[i].plane_id, DRM_MODE_OBJECT_PLANE, "CRTC_ID", drm.crtc_id);
465 r += add_property(drm.fd, commit_req, drm.layers[i].plane_id, DRM_MODE_OBJECT_PLANE, "SRC_X", 0 << 16);
466 r += add_property(drm.fd, commit_req, drm.layers[i].plane_id, DRM_MODE_OBJECT_PLANE, "SRC_Y", 0 << 16);
467 r += add_property(drm.fd, commit_req, drm.layers[i].plane_id, DRM_MODE_OBJECT_PLANE, "SRC_W", buf->width << 16);
468 r += add_property(drm.fd, commit_req, drm.layers[i].plane_id, DRM_MODE_OBJECT_PLANE, "SRC_H", buf->height << 16);
469 #if defined (CONFIG_DISPLAY_SPLIT)
470 if(plane_split == false) {
471 r += add_property(drm.fd, commit_req, drm.layers[i].plane_id, DRM_MODE_OBJECT_PLANE, "CRTC_X", drm.layers[i].pos_x);
472 r += add_property(drm.fd, commit_req, drm.layers[i].plane_id, DRM_MODE_OBJECT_PLANE, "CRTC_Y", drm.layers[i].pos_y);
473 } else {
474 int x, y;
475 if(i == 0) {
476 x = drm.mode.hdisplay - buf->width;
477 y = drm.layers[i].pos_y;
478 } else if(i == 1) {
479 x = drm.layers[i].pos_x;
480 y = drm.mode.vdisplay - buf->height;
481 } else if(i == 2) {
482 x = drm.mode.hdisplay - buf->width;
483 y = drm.mode.vdisplay - buf->height;
484 } else {
485 x = drm.layers[i].pos_x;
486 y = drm.layers[i].pos_y;
487 }
488 r += add_property(drm.fd, commit_req, drm.layers[i].plane_id, DRM_MODE_OBJECT_PLANE, "CRTC_X", x);
489 r += add_property(drm.fd, commit_req, drm.layers[i].plane_id, DRM_MODE_OBJECT_PLANE, "CRTC_Y", y);
490 }
491 #else
492 r += add_property(drm.fd, commit_req, drm.layers[i].plane_id, DRM_MODE_OBJECT_PLANE, "CRTC_X", drm.layers[i].pos_x);
493 r += add_property(drm.fd, commit_req, drm.layers[i].plane_id, DRM_MODE_OBJECT_PLANE, "CRTC_Y", drm.layers[i].pos_y);
494 #endif
495 r += add_property(drm.fd, commit_req, drm.layers[i].plane_id, DRM_MODE_OBJECT_PLANE, "CRTC_W", drm.layers[i].width);
496 r += add_property(drm.fd, commit_req, drm.layers[i].plane_id, DRM_MODE_OBJECT_PLANE, "CRTC_H", drm.layers[i].height);
497 }
499 if(r) {
500 printf("add_property_failed\n");
501 goto next_iter;
502 }
504 r = drmModeAtomicCommit(drm.fd, commit_req, 0, NULL);
505 if(r) {
506 printf("atomic commit failed\n");
507 }
508 next_iter:
509 drmModeAtomicFree(commit_req);
511 for(i = 0; i < count; i++) {
512 if(old_buffers[i])
513 bq_queue_empty(drm.layers[i].bq, &old_buffers[i]->link);
514 old_buffers[i] = mid_buffers[i];
515 mid_buffers[i] = new_buffers[i];
516 }
517 }
518 }
520 void *drm_buffer_get_priv(struct buffer *b)
521 {
522 return b->priv_data;
523 }
525 void drm_buffer_set_priv(struct buffer *b, void *priv)
526 {
527 b->priv_data = priv;
528 }