1 /*
2 * Copyright (C) 2011 Texas Instruments
3 * Author: Rob Clark <rob.clark@linaro.org>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
22 #include "util.h"
24 #include <xf86drmMode.h>
27 /* NOTE: healthy dose of recycling from libdrm modetest app.. */
29 /*
30 * Mode setting with the kernel interfaces is a bit of a chore.
31 * First you have to find the connector in question and make sure the
32 * requested mode is available.
33 * Then you need to find the encoder attached to that connector so you
34 * can bind it with a free crtc.
35 */
36 struct connector {
37 uint32_t id;
38 char mode_str[64];
39 drmModeModeInfo *mode;
40 drmModeEncoder *encoder;
41 int crtc;
42 int pipe;
43 };
45 #define to_display_kms(x) container_of(x, struct display_kms, base)
46 struct display_kms {
47 struct display base;
49 uint32_t connectors_count;
50 struct connector connector[10];
51 drmModePlane *ovr[10];
53 int scheduled_flips, completed_flips;
54 uint32_t bo_flags;
55 drmModeResPtr resources;
56 drmModePlaneRes *plane_resources;
57 struct buffer *current;
58 bool no_master;
59 int mastership;
60 };
62 #define to_buffer_kms(x) container_of(x, struct buffer_kms, base)
63 struct buffer_kms {
64 struct buffer base;
65 uint32_t fb_id;
66 };
68 static int global_fd = 0;
69 static uint32_t used_planes = 0;
70 static int ndisplays = 0;
72 static struct omap_bo *
73 alloc_bo(struct display *disp, uint32_t bpp, uint32_t width, uint32_t height,
74 uint32_t *bo_handle, uint32_t *pitch)
75 {
76 struct display_kms *disp_kms = to_display_kms(disp);
77 struct omap_bo *bo;
78 uint32_t bo_flags = disp_kms->bo_flags;
80 if ((bo_flags & OMAP_BO_TILED) == OMAP_BO_TILED) {
81 bo_flags &= ~OMAP_BO_TILED;
82 if (bpp == 8) {
83 bo_flags |= OMAP_BO_TILED_8;
84 } else if (bpp == 16) {
85 bo_flags |= OMAP_BO_TILED_16;
86 } else if (bpp == 32) {
87 bo_flags |= OMAP_BO_TILED_32;
88 }
89 }
90 bo_flags |= OMAP_BO_WC;
92 if (bo_flags & OMAP_BO_TILED) {
93 bo = omap_bo_new_tiled(disp->dev, ALIGN2(width,7), height, bo_flags);
94 } else {
95 bo = omap_bo_new(disp->dev, width * height * bpp / 8, bo_flags);
96 }
98 if (bo) {
99 *bo_handle = omap_bo_handle(bo);
100 *pitch = width * bpp / 8;
101 if (bo_flags & OMAP_BO_TILED)
102 *pitch = ALIGN2(*pitch, PAGE_SHIFT);
103 }
105 return bo;
106 }
108 static struct buffer *
109 alloc_buffer(struct display *disp, uint32_t fourcc, uint32_t w, uint32_t h)
110 {
111 struct buffer_kms *buf_kms;
112 struct buffer *buf;
113 uint32_t bo_handles[4] = {0}, offsets[4] = {0};
114 int ret;
116 buf_kms = calloc(1, sizeof(*buf_kms));
117 if (!buf_kms) {
118 ERROR("allocation failed");
119 return NULL;
120 }
121 buf = &buf_kms->base;
123 buf->fourcc = fourcc;
124 buf->width = w;
125 buf->height = h;
126 buf->multiplanar = true;
128 buf->nbo = 1;
130 if (!fourcc)
131 fourcc = FOURCC('A','R','2','4');
133 switch(fourcc) {
134 case FOURCC('A','R','2','4'):
135 buf->nbo = 1;
136 buf->bo[0] = alloc_bo(disp, 32, buf->width, buf->height,
137 &bo_handles[0], &buf->pitches[0]);
138 break;
139 case FOURCC('U','Y','V','Y'):
140 case FOURCC('Y','U','Y','V'):
141 buf->nbo = 1;
142 buf->bo[0] = alloc_bo(disp, 16, buf->width, buf->height,
143 &bo_handles[0], &buf->pitches[0]);
144 break;
145 case FOURCC('N','V','1','2'):
146 if (disp->multiplanar) {
147 buf->nbo = 2;
148 buf->bo[0] = alloc_bo(disp, 8, buf->width, buf->height,
149 &bo_handles[0], &buf->pitches[0]);
150 buf->fd[0] = omap_bo_dmabuf(buf->bo[0]);
151 buf->bo[1] = alloc_bo(disp, 16, buf->width/2, buf->height/2,
152 &bo_handles[1], &buf->pitches[1]);
153 buf->fd[1] = omap_bo_dmabuf(buf->bo[1]);
154 } else {
155 buf->nbo = 1;
156 buf->bo[0] = alloc_bo(disp, 8, buf->width, buf->height * 3 / 2,
157 &bo_handles[0], &buf->pitches[0]);
158 buf->fd[0] = omap_bo_dmabuf(buf->bo[0]);
159 bo_handles[1] = bo_handles[0];
160 buf->pitches[1] = buf->pitches[0];
161 offsets[1] = buf->width * buf->height;
162 buf->multiplanar = false;
163 }
164 break;
165 case FOURCC('I','4','2','0'):
166 buf->nbo = 3;
167 buf->bo[0] = alloc_bo(disp, 8, buf->width, buf->height,
168 &bo_handles[0], &buf->pitches[0]);
169 buf->bo[1] = alloc_bo(disp, 8, buf->width/2, buf->height/2,
170 &bo_handles[1], &buf->pitches[1]);
171 buf->bo[2] = alloc_bo(disp, 8, buf->width/2, buf->height/2,
172 &bo_handles[2], &buf->pitches[2]);
173 break;
174 default:
175 ERROR("invalid format: 0x%08x", fourcc);
176 goto fail;
177 }
179 ret = drmModeAddFB2(disp->fd, buf->width, buf->height, fourcc,
180 bo_handles, buf->pitches, offsets, &buf_kms->fb_id, 0);
181 if (ret) {
182 ERROR("drmModeAddFB2 failed: %s (%d)", strerror(errno), ret);
183 goto fail;
184 }
186 return buf;
188 fail:
189 // XXX cleanup
190 return NULL;
191 }
193 void
194 free_buffers(struct display *disp, uint32_t n)
195 {
196 uint32_t i;
197 for (i = 0; i < n; i++) {
198 if (disp->buf[i]) {
199 close(disp->buf[i]->fd[0]);
200 omap_bo_del(disp->buf[i]->bo[0]);
201 if(disp->multiplanar){
202 close(disp->buf[i]->fd[1]);
203 omap_bo_del(disp->buf[i]->bo[1]);
204 }
205 }
206 }
207 free(disp->buf);
208 }
210 static struct buffer **
211 alloc_buffers(struct display *disp, uint32_t n,
212 uint32_t fourcc, uint32_t w, uint32_t h)
213 {
214 struct buffer **bufs;
215 uint32_t i = 0;
217 bufs = calloc(n, sizeof(*bufs));
218 if (!bufs) {
219 ERROR("allocation failed");
220 goto fail;
221 }
223 for (i = 0; i < n; i++) {
224 bufs[i] = alloc_buffer(disp, fourcc, w, h);
225 if (!bufs[i]) {
226 ERROR("allocation failed");
227 goto fail;
228 }
229 }
230 disp->buf=bufs;
231 return bufs;
233 fail:
234 // XXX cleanup
235 return NULL;
236 }
238 static struct buffer **
239 get_buffers(struct display *disp, uint32_t n)
240 {
241 return alloc_buffers(disp, n, 0, disp->width, disp->height);
242 }
244 static struct buffer **
245 get_vid_buffers(struct display *disp, uint32_t n,
246 uint32_t fourcc, uint32_t w, uint32_t h)
247 {
248 return alloc_buffers(disp, n, fourcc, w, h);
249 }
251 static void
252 page_flip_handler(int fd, unsigned int frame,
253 unsigned int sec, unsigned int usec, void *data)
254 {
255 struct display *disp = data;
256 struct display_kms *disp_kms = to_display_kms(disp);
258 disp_kms->completed_flips++;
260 MSG("Page flip: frame=%d, sec=%d, usec=%d, remaining=%d", frame, sec, usec,
261 disp_kms->scheduled_flips - disp_kms->completed_flips);
262 }
264 static int
265 post_buffer(struct display *disp, struct buffer *buf)
266 {
267 struct display_kms *disp_kms = to_display_kms(disp);
268 struct buffer_kms *buf_kms = to_buffer_kms(buf);
269 int ret, last_err = 0, x = 0;
270 uint32_t i;
272 for (i = 0; i < disp_kms->connectors_count; i++) {
273 struct connector *connector = &disp_kms->connector[i];
275 if (! connector->mode) {
276 continue;
277 }
279 if (! disp_kms->current) {
280 /* first buffer we flip to, setup the mode (since this can't
281 * be done earlier without a buffer to scanout)
282 */
283 MSG("Setting mode %s on connector %d, crtc %d",
284 connector->mode_str, connector->id, connector->crtc);
286 ret = drmModeSetCrtc(disp->fd, connector->crtc, buf_kms->fb_id,
287 x, 0, &connector->id, 1, connector->mode);
289 x += connector->mode->hdisplay;
290 } else {
291 ret = drmModePageFlip(disp->fd, connector->crtc, buf_kms->fb_id,
292 DRM_MODE_PAGE_FLIP_EVENT, disp);
293 disp_kms->scheduled_flips++;
294 }
296 if (ret) {
297 ERROR("Could not post buffer on crtc %d: %s (%d)",
298 connector->crtc, strerror(errno), ret);
299 last_err = ret;
300 /* well, keep trying the reset of the connectors.. */
301 }
302 }
304 /* if we flipped, wait for all flips to complete! */
305 while (disp_kms->scheduled_flips > disp_kms->completed_flips) {
306 drmEventContext evctx = {
307 .version = DRM_EVENT_CONTEXT_VERSION,
308 .page_flip_handler = page_flip_handler,
309 };
310 struct timeval timeout = {
311 .tv_sec = 3,
312 .tv_usec = 0,
313 };
314 fd_set fds;
316 FD_ZERO(&fds);
317 FD_SET(disp->fd, &fds);
319 ret = select(disp->fd + 1, &fds, NULL, NULL, &timeout);
320 if (ret <= 0) {
321 if (errno == EAGAIN) {
322 continue; /* keep going */
323 } else {
324 ERROR("Timeout waiting for flip complete: %s (%d)",
325 strerror(errno), ret);
326 last_err = ret;
327 break;
328 }
329 }
331 drmHandleEvent(disp->fd, &evctx);
332 }
334 disp_kms->current = buf;
336 return last_err;
337 }
339 int
340 get_overlay_plane(struct display *disp, struct buffer *buf)
341 {
342 struct display_kms *disp_kms = to_display_kms(disp);
343 uint32_t i, ret;
345 for (i = 0; i < disp_kms->connectors_count; i++) {
346 struct connector *connector = &disp_kms->connector[i];
347 drmModeModeInfo *mode = connector->mode;
349 disp_kms->ovr[i] = drmModeGetPlane(disp->fd,
350 disp_kms->plane_resources->planes[1]);
352 ret = drmModeObjectSetProperty(disp->fd,
353 disp_kms->ovr[i]->plane_id, DRM_MODE_OBJECT_PLANE, 7, 3);
354 if (ret < 0) {
355 MSG("Could not set Z order for plane");
356 return ret;
357 }
358 }
359 return 0;
360 }
362 static int
363 post_vid_buffer(struct display *disp, struct buffer *buf,
364 uint32_t x, uint32_t y, uint32_t w, uint32_t h)
365 {
366 struct display_kms *disp_kms = to_display_kms(disp);
367 struct buffer_kms *buf_kms = to_buffer_kms(buf);
368 int ret = 0;
369 uint32_t i, j;
371 /* ensure we have the overlay setup: */
372 for (i = 0; i < disp_kms->connectors_count; i++) {
373 struct connector *connector = &disp_kms->connector[i];
374 drmModeModeInfo *mode = connector->mode;
376 if (! mode) {
377 continue;
378 }
380 if (! disp_kms->ovr[i]) {
382 for (j = 0; j < disp_kms->plane_resources->count_planes; j++) {
383 if (used_planes & (1 << j)) {
384 continue;
385 }
386 drmModePlane *ovr = drmModeGetPlane(disp->fd,
387 disp_kms->plane_resources->planes[j]);
388 if (ovr->possible_crtcs & (1 << connector->pipe)) {
389 disp_kms->ovr[i] = ovr;
390 used_planes |= (1 << j);
391 break;
392 }
393 }
394 }
396 if (! disp_kms->ovr[i]) {
397 MSG("Could not find plane for crtc %d", connector->crtc);
398 ret = -1;
399 /* carry on and see if we can find at least one usable plane */
400 continue;
401 }
403 if(buf->noScale) {
404 ret = drmModeSetPlane(disp->fd, disp_kms->ovr[i]->plane_id,
405 connector->crtc, buf_kms->fb_id, 0,
406 /* Use x and y as co-ordinates of overlay */
407 x, y, buf->width, buf->height,
408 /* Consider source x and y is 0 always */
409 0, 0, w << 16, h << 16);
410 } else {
411 ret = drmModeSetPlane(disp->fd, disp_kms->ovr[i]->plane_id,
412 connector->crtc, buf_kms->fb_id, 0,
413 /* make video fullscreen: */
414 0, 0, mode->hdisplay, mode->vdisplay,
415 /* source/cropping coordinates are given in Q16 */
416 x << 16, y << 16, w << 16, h << 16);
417 }
418 if (ret) {
419 ERROR("failed to enable plane %d: %s",
420 disp_kms->ovr[i]->plane_id, strerror(errno));
421 }
422 }
424 if (disp_kms->no_master && disp_kms->mastership) {
425 /* Drop mastership after the first buffer on each plane is
426 * displayed. This will lock these planes for us and allow
427 * others to consume remaining
428 */
429 disp_kms->mastership = 0;
430 drmDropMaster(disp->fd);
431 }
433 return ret;
434 }
436 static void
437 close_kms(struct display *disp)
438 {
439 omap_device_del(disp->dev);
440 disp->dev = NULL;
441 if (used_planes) {
442 used_planes >>= 1;
443 }
444 if (--ndisplays == 0) {
445 close(global_fd);
446 }
447 }
449 static void
450 connector_find_mode(struct display *disp, struct connector *c)
451 {
452 struct display_kms *disp_kms = to_display_kms(disp);
453 drmModeConnector *connector;
454 int i, j;
456 /* First, find the connector & mode */
457 c->mode = NULL;
458 for (i = 0; i < disp_kms->resources->count_connectors; i++) {
459 connector = drmModeGetConnector(disp->fd,
460 disp_kms->resources->connectors[i]);
462 if (!connector) {
463 ERROR("could not get connector %i: %s",
464 disp_kms->resources->connectors[i], strerror(errno));
465 drmModeFreeConnector(connector);
466 continue;
467 }
469 if (!connector->count_modes) {
470 drmModeFreeConnector(connector);
471 continue;
472 }
474 if (connector->connector_id != c->id) {
475 drmModeFreeConnector(connector);
476 continue;
477 }
479 for (j = 0; j < connector->count_modes; j++) {
480 c->mode = &connector->modes[j];
481 if (!strcmp(c->mode->name, c->mode_str))
482 break;
483 }
485 /* Found it, break out */
486 if (c->mode)
487 break;
489 drmModeFreeConnector(connector);
490 }
492 if (!c->mode) {
493 ERROR("failed to find mode \"%s\"", c->mode_str);
494 return;
495 }
497 /* Now get the encoder */
498 for (i = 0; i < disp_kms->resources->count_encoders; i++) {
499 c->encoder = drmModeGetEncoder(disp->fd,
500 disp_kms->resources->encoders[i]);
502 if (!c->encoder) {
503 ERROR("could not get encoder %i: %s",
504 disp_kms->resources->encoders[i], strerror(errno));
505 drmModeFreeEncoder(c->encoder);
506 continue;
507 }
509 if (c->encoder->encoder_id == connector->encoder_id)
510 break;
512 drmModeFreeEncoder(c->encoder);
513 }
515 if (c->crtc == -1)
516 c->crtc = c->encoder->crtc_id;
518 /* and figure out which crtc index it is: */
519 for (i = 0; i < disp_kms->resources->count_crtcs; i++) {
520 if (c->crtc == (int)disp_kms->resources->crtcs[i]) {
521 c->pipe = i;
522 break;
523 }
524 }
525 }
527 void
528 disp_kms_usage(void)
529 {
530 MSG("KMS Display Options:");
531 MSG("\t-1 \t\tforce single-plane buffers");
532 MSG("\t-t <tiled-mode>\t8, 16, 32, or auto");
533 MSG("\t-s <connector_id>:<mode>\tset a mode");
534 MSG("\t-s <connector_id>@<crtc_id>:<mode>\tset a mode");
535 }
537 struct display *
538 disp_kms_open(int argc, char **argv)
539 {
540 struct display_kms *disp_kms = NULL;
541 struct display *disp;
542 int i;
544 disp_kms = calloc(1, sizeof(*disp_kms));
545 if (!disp_kms) {
546 ERROR("allocation failed");
547 goto fail;
548 }
549 disp = &disp_kms->base;
551 if (!global_fd) {
552 global_fd = drmOpen("omapdrm", NULL);
553 if (global_fd < 0) {
554 ERROR("could not open drm device: %s (%d)", strerror(errno), errno);
555 goto fail;
556 }
557 }
559 disp->fd = global_fd;
560 ndisplays++; /* increment the number of displays counter */
562 disp->dev = omap_device_new(disp->fd);
563 if (!disp->dev) {
564 ERROR("couldn't create device");
565 goto fail;
566 }
568 disp->get_buffers = get_buffers;
569 disp->get_vid_buffers = get_vid_buffers;
570 disp->post_buffer = post_buffer;
571 disp->post_vid_buffer = post_vid_buffer;
572 disp->close = close_kms;
573 disp->disp_free_buf = free_buffers ;
574 disp_kms->resources = drmModeGetResources(disp->fd);
575 if (!disp_kms->resources) {
576 ERROR("drmModeGetResources failed: %s", strerror(errno));
577 goto fail;
578 }
580 disp_kms->plane_resources = drmModeGetPlaneResources(disp->fd);
581 if (!disp_kms->plane_resources) {
582 ERROR("drmModeGetPlaneResources failed: %s", strerror(errno));
583 goto fail;
584 }
586 disp->multiplanar = true;
588 /* note: set args to NULL after we've parsed them so other modules know
589 * that it is already parsed (since the arg parsing is decentralized)
590 */
591 for (i = 1; i < argc; i++) {
592 if (!argv[i]) {
593 continue;
594 }
595 if (!strcmp("-1", argv[i])) {
596 disp->multiplanar = false;
597 } else if (!strcmp("-t", argv[i])) {
598 int n;
599 argv[i++] = NULL;
600 if (!strcmp(argv[i], "auto")) {
601 n = 0;
602 } else if (sscanf(argv[i], "%d", &n) != 1) {
603 ERROR("invalid arg: %s", argv[i]);
604 goto fail;
605 }
607 disp_kms->bo_flags &= ~OMAP_BO_TILED;
609 if (n == 8) {
610 disp_kms->bo_flags |= OMAP_BO_TILED_8;
611 } else if (n == 16) {
612 disp_kms->bo_flags |= OMAP_BO_TILED_16;
613 } else if (n == 32) {
614 disp_kms->bo_flags |= OMAP_BO_TILED_32;
615 } else if (n == 0) {
616 disp_kms->bo_flags |= OMAP_BO_TILED;
617 } else {
618 ERROR("invalid arg: %s", argv[i]);
619 goto fail;
620 }
621 } else if (!strcmp("-s", argv[i])) {
622 struct connector *connector =
623 &disp_kms->connector[disp_kms->connectors_count++];
624 connector->crtc = -1;
625 argv[i++] = NULL;
626 if (sscanf(argv[i], "%d:%64s",
627 &connector->id,
628 connector->mode_str) != 2 &&
629 sscanf(argv[i], "%d@%d:%64s",
630 &connector->id,
631 &connector->crtc,
632 connector->mode_str) != 3) {
633 // TODO: we could support connector specified as a name too, I suppose
634 ERROR("invalid arg: %s", argv[i]);
635 goto fail;
636 }
637 disp_kms->bo_flags |= OMAP_BO_SCANOUT;
638 } else if (!strcmp("-nm", argv[i])) {
639 disp_kms->no_master = true;
640 disp_kms->mastership = 1;
641 } else {
642 /* ignore */
643 continue;
644 }
645 argv[i] = NULL;
646 }
648 disp->width = 0;
649 disp->height = 0;
650 for (i = 0; i < (int)disp_kms->connectors_count; i++) {
651 struct connector *c = &disp_kms->connector[i];
652 connector_find_mode(disp, c);
653 if (c->mode == NULL)
654 continue;
655 /* setup side-by-side virtual display */
656 disp->width += c->mode->hdisplay;
657 if (disp->height < c->mode->vdisplay) {
658 disp->height = c->mode->vdisplay;
659 }
660 }
662 MSG("using %d connectors, %dx%d display, multiplanar: %d",
663 disp_kms->connectors_count, disp->width, disp->height, disp->multiplanar);
665 return disp;
667 fail:
668 // XXX cleanup
669 return NULL;
670 }