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"
23 #include <libdce.h>
25 #include <xf86drmMode.h>
28 /* NOTE: healthy dose of recycling from libdrm modetest app.. */
30 /*
31 * Mode setting with the kernel interfaces is a bit of a chore.
32 * First you have to find the connector in question and make sure the
33 * requested mode is available.
34 * Then you need to find the encoder attached to that connector so you
35 * can bind it with a free crtc.
36 */
37 struct connector {
38 uint32_t id;
39 char mode_str[64];
40 drmModeModeInfo *mode;
41 drmModeEncoder *encoder;
42 int crtc;
43 int pipe;
44 };
46 #define to_display_kms(x) container_of(x, struct display_kms, base)
47 struct display_kms {
48 struct display base;
50 uint32_t connectors_count;
51 struct connector connector[10];
52 drmModePlane *ovr[10];
54 int scheduled_flips, completed_flips;
55 uint32_t bo_flags;
56 drmModeResPtr resources;
57 drmModePlaneRes *plane_resources;
58 struct buffer *current;
59 };
61 #define to_buffer_kms(x) container_of(x, struct buffer_kms, base)
62 struct buffer_kms {
63 struct buffer base;
64 uint32_t fb_id;
65 };
67 static int global_fd = 0;
68 static uint32_t used_planes = 0;
69 static int ndisplays = 0;
71 static struct omap_bo *
72 alloc_bo(struct display *disp, uint32_t bpp, uint32_t width, uint32_t height,
73 uint32_t *bo_handle, uint32_t *pitch)
74 {
75 struct display_kms *disp_kms = to_display_kms(disp);
76 struct omap_bo *bo;
77 uint32_t bo_flags = disp_kms->bo_flags;
79 if ((bo_flags & OMAP_BO_TILED) == OMAP_BO_TILED) {
80 bo_flags &= ~OMAP_BO_TILED;
81 if (bpp == 8) {
82 bo_flags |= OMAP_BO_TILED_8;
83 } else if (bpp == 16) {
84 bo_flags |= OMAP_BO_TILED_16;
85 } else if (bpp == 32) {
86 bo_flags |= OMAP_BO_TILED_32;
87 }
88 }
89 bo_flags |= OMAP_BO_WC;
91 if (bo_flags & OMAP_BO_TILED) {
92 bo = omap_bo_new_tiled(disp->dev, ALIGN2(width,7), height, bo_flags);
93 } else {
94 bo = omap_bo_new(disp->dev, width * height * bpp / 8, bo_flags);
95 }
97 if (bo) {
98 *bo_handle = omap_bo_handle(bo);
99 *pitch = width * bpp / 8;
100 if (bo_flags & OMAP_BO_TILED)
101 *pitch = ALIGN2(*pitch, PAGE_SHIFT);
102 }
104 return bo;
105 }
107 static struct buffer *
108 alloc_buffer(struct display *disp, uint32_t fourcc, uint32_t w, uint32_t h)
109 {
110 struct buffer_kms *buf_kms;
111 struct buffer *buf;
112 uint32_t bo_handles[4] = {0}, offsets[4] = {0};
113 int ret;
115 buf_kms = calloc(1, sizeof(*buf_kms));
116 if (!buf_kms) {
117 ERROR("allocation failed");
118 return NULL;
119 }
120 buf = &buf_kms->base;
122 buf->fourcc = fourcc;
123 buf->width = w;
124 buf->height = h;
125 buf->multiplanar = true;
127 buf->nbo = 1;
129 if (!fourcc)
130 fourcc = FOURCC('A','R','2','4');
132 switch(fourcc) {
133 case FOURCC('A','R','2','4'):
134 buf->nbo = 1;
135 buf->bo[0] = alloc_bo(disp, 32, buf->width, buf->height,
136 &bo_handles[0], &buf->pitches[0]);
137 break;
138 case FOURCC('U','Y','V','Y'):
139 case FOURCC('Y','U','Y','V'):
140 buf->nbo = 1;
141 buf->bo[0] = alloc_bo(disp, 16, buf->width, buf->height,
142 &bo_handles[0], &buf->pitches[0]);
143 break;
144 case FOURCC('N','V','1','2'):
145 if (disp->multiplanar) {
146 buf->nbo = 2;
147 buf->bo[0] = alloc_bo(disp, 8, buf->width, buf->height,
148 &bo_handles[0], &buf->pitches[0]);
149 buf->fd[0] = omap_bo_dmabuf(buf->bo[0]);
150 buf->bo[1] = alloc_bo(disp, 16, buf->width/2, buf->height/2,
151 &bo_handles[1], &buf->pitches[1]);
152 buf->fd[1] = omap_bo_dmabuf(buf->bo[1]);
153 } else {
154 buf->nbo = 1;
155 buf->bo[0] = alloc_bo(disp, 8, buf->width, buf->height * 3 / 2,
156 &bo_handles[0], &buf->pitches[0]);
157 buf->fd[0] = omap_bo_dmabuf(buf->bo[0]);
158 bo_handles[1] = bo_handles[0];
159 buf->pitches[1] = buf->pitches[0];
160 offsets[1] = buf->width * buf->height;
161 buf->multiplanar = false;
162 }
163 break;
164 case FOURCC('I','4','2','0'):
165 buf->nbo = 3;
166 buf->bo[0] = alloc_bo(disp, 8, buf->width, buf->height,
167 &bo_handles[0], &buf->pitches[0]);
168 buf->bo[1] = alloc_bo(disp, 8, buf->width/2, buf->height/2,
169 &bo_handles[1], &buf->pitches[1]);
170 buf->bo[2] = alloc_bo(disp, 8, buf->width/2, buf->height/2,
171 &bo_handles[2], &buf->pitches[2]);
172 break;
173 default:
174 ERROR("invalid format: 0x%08x", fourcc);
175 goto fail;
176 }
178 ret = drmModeAddFB2(disp->fd, buf->width, buf->height, fourcc,
179 bo_handles, buf->pitches, offsets, &buf_kms->fb_id, 0);
180 if (ret) {
181 ERROR("drmModeAddFB2 failed: %s (%d)", strerror(errno), ret);
182 goto fail;
183 }
185 return buf;
187 fail:
188 // XXX cleanup
189 return NULL;
190 }
192 void
193 free_buffers(struct display *disp, uint32_t n)
194 {
195 uint32_t i;
196 for (i = 0; i < n; i++) {
197 if (disp->buf[i]) {
198 close(disp->buf[i]->fd[0]);
199 omap_bo_del(disp->buf[i]->bo[0]);
200 if(disp->multiplanar){
201 close(disp->buf[i]->fd[1]);
202 omap_bo_del(disp->buf[i]->bo[1]);
203 }
204 }
205 }
206 free(disp->buf);
207 }
209 static struct buffer **
210 alloc_buffers(struct display *disp, uint32_t n,
211 uint32_t fourcc, uint32_t w, uint32_t h)
212 {
213 struct buffer **bufs;
214 uint32_t i = 0;
216 bufs = calloc(n, sizeof(*bufs));
217 if (!bufs) {
218 ERROR("allocation failed");
219 goto fail;
220 }
222 for (i = 0; i < n; i++) {
223 bufs[i] = alloc_buffer(disp, fourcc, w, h);
224 if (!bufs[i]) {
225 ERROR("allocation failed");
226 goto fail;
227 }
228 }
229 disp->buf=bufs;
230 return bufs;
232 fail:
233 // XXX cleanup
234 return NULL;
235 }
237 static struct buffer **
238 get_buffers(struct display *disp, uint32_t n)
239 {
240 return alloc_buffers(disp, n, 0, disp->width, disp->height);
241 }
243 static struct buffer **
244 get_vid_buffers(struct display *disp, uint32_t n,
245 uint32_t fourcc, uint32_t w, uint32_t h)
246 {
247 return alloc_buffers(disp, n, fourcc, w, h);
248 }
250 static void
251 page_flip_handler(int fd, unsigned int frame,
252 unsigned int sec, unsigned int usec, void *data)
253 {
254 struct display *disp = data;
255 struct display_kms *disp_kms = to_display_kms(disp);
257 disp_kms->completed_flips++;
259 MSG("Page flip: frame=%d, sec=%d, usec=%d, remaining=%d", frame, sec, usec,
260 disp_kms->scheduled_flips - disp_kms->completed_flips);
261 }
263 static int
264 post_buffer(struct display *disp, struct buffer *buf)
265 {
266 struct display_kms *disp_kms = to_display_kms(disp);
267 struct buffer_kms *buf_kms = to_buffer_kms(buf);
268 int ret, last_err = 0, x = 0;
269 uint32_t i;
271 for (i = 0; i < disp_kms->connectors_count; i++) {
272 struct connector *connector = &disp_kms->connector[i];
274 if (! connector->mode) {
275 continue;
276 }
278 if (! disp_kms->current) {
279 /* first buffer we flip to, setup the mode (since this can't
280 * be done earlier without a buffer to scanout)
281 */
282 MSG("Setting mode %s on connector %d, crtc %d",
283 connector->mode_str, connector->id, connector->crtc);
285 ret = drmModeSetCrtc(disp->fd, connector->crtc, buf_kms->fb_id,
286 x, 0, &connector->id, 1, connector->mode);
288 x += connector->mode->hdisplay;
289 } else {
290 ret = drmModePageFlip(disp->fd, connector->crtc, buf_kms->fb_id,
291 DRM_MODE_PAGE_FLIP_EVENT, disp);
292 disp_kms->scheduled_flips++;
293 }
295 if (ret) {
296 ERROR("Could not post buffer on crtc %d: %s (%d)",
297 connector->crtc, strerror(errno), ret);
298 last_err = ret;
299 /* well, keep trying the reset of the connectors.. */
300 }
301 }
303 /* if we flipped, wait for all flips to complete! */
304 while (disp_kms->scheduled_flips > disp_kms->completed_flips) {
305 drmEventContext evctx = {
306 .version = DRM_EVENT_CONTEXT_VERSION,
307 .page_flip_handler = page_flip_handler,
308 };
309 struct timeval timeout = {
310 .tv_sec = 3,
311 .tv_usec = 0,
312 };
313 fd_set fds;
315 FD_ZERO(&fds);
316 FD_SET(disp->fd, &fds);
318 ret = select(disp->fd + 1, &fds, NULL, NULL, &timeout);
319 if (ret <= 0) {
320 if (errno == EAGAIN) {
321 continue; /* keep going */
322 } else {
323 ERROR("Timeout waiting for flip complete: %s (%d)",
324 strerror(errno), ret);
325 last_err = ret;
326 break;
327 }
328 }
330 drmHandleEvent(disp->fd, &evctx);
331 }
333 disp_kms->current = buf;
335 return last_err;
336 }
338 int
339 get_overlay_plane(struct display *disp, struct buffer *buf)
340 {
341 struct display_kms *disp_kms = to_display_kms(disp);
342 uint32_t i, ret;
344 for (i = 0; i < disp_kms->connectors_count; i++) {
345 struct connector *connector = &disp_kms->connector[i];
346 drmModeModeInfo *mode = connector->mode;
348 disp_kms->ovr[i] = drmModeGetPlane(disp->fd,
349 disp_kms->plane_resources->planes[1]);
351 ret = drmModeObjectSetProperty(disp->fd,
352 disp_kms->ovr[i]->plane_id, DRM_MODE_OBJECT_PLANE, 7, 3);
353 if (ret < 0) {
354 MSG("Could not set Z order for plane");
355 return ret;
356 }
357 }
358 return 0;
359 }
361 static int
362 post_vid_buffer(struct display *disp, struct buffer *buf,
363 uint32_t x, uint32_t y, uint32_t w, uint32_t h)
364 {
365 struct display_kms *disp_kms = to_display_kms(disp);
366 struct buffer_kms *buf_kms = to_buffer_kms(buf);
367 int ret = 0;
368 uint32_t i, j;
370 /* ensure we have the overlay setup: */
371 for (i = 0; i < disp_kms->connectors_count; i++) {
372 struct connector *connector = &disp_kms->connector[i];
373 drmModeModeInfo *mode = connector->mode;
375 if (! mode) {
376 continue;
377 }
379 if (! disp_kms->ovr[i]) {
381 for (j = 0; j < disp_kms->plane_resources->count_planes; j++) {
382 if (used_planes & (1 << j)) {
383 continue;
384 }
385 drmModePlane *ovr = drmModeGetPlane(disp->fd,
386 disp_kms->plane_resources->planes[j]);
387 if (ovr->possible_crtcs & (1 << connector->pipe)) {
388 disp_kms->ovr[i] = ovr;
389 used_planes |= (1 << j);
390 break;
391 }
392 }
393 }
395 if (! disp_kms->ovr[i]) {
396 MSG("Could not find plane for crtc %d", connector->crtc);
397 ret = -1;
398 /* carry on and see if we can find at least one usable plane */
399 continue;
400 }
402 if(buf->noScale) {
403 ret = drmModeSetPlane(disp->fd, disp_kms->ovr[i]->plane_id,
404 connector->crtc, buf_kms->fb_id, 0,
405 /* Use x and y as co-ordinates of overlay */
406 x, y, buf->width, buf->height,
407 /* Consider source x and y is 0 always */
408 0, 0, w << 16, h << 16);
409 } else {
410 ret = drmModeSetPlane(disp->fd, disp_kms->ovr[i]->plane_id,
411 connector->crtc, buf_kms->fb_id, 0,
412 /* make video fullscreen: */
413 0, 0, mode->hdisplay, mode->vdisplay,
414 /* source/cropping coordinates are given in Q16 */
415 x << 16, y << 16, w << 16, h << 16);
416 }
417 if (ret) {
418 ERROR("failed to enable plane %d: %s",
419 disp_kms->ovr[i]->plane_id, strerror(errno));
420 }
421 }
423 return ret;
424 }
426 static void
427 close_kms(struct display *disp)
428 {
429 omap_device_del(disp->dev);
430 disp->dev = NULL;
431 if (used_planes) {
432 used_planes >>= 1;
433 }
434 if (--ndisplays == 0) {
435 close(global_fd);
436 }
437 }
439 static void
440 connector_find_mode(struct display *disp, struct connector *c)
441 {
442 struct display_kms *disp_kms = to_display_kms(disp);
443 drmModeConnector *connector;
444 int i, j;
446 /* First, find the connector & mode */
447 c->mode = NULL;
448 for (i = 0; i < disp_kms->resources->count_connectors; i++) {
449 connector = drmModeGetConnector(disp->fd,
450 disp_kms->resources->connectors[i]);
452 if (!connector) {
453 ERROR("could not get connector %i: %s",
454 disp_kms->resources->connectors[i], strerror(errno));
455 drmModeFreeConnector(connector);
456 continue;
457 }
459 if (!connector->count_modes) {
460 drmModeFreeConnector(connector);
461 continue;
462 }
464 if (connector->connector_id != c->id) {
465 drmModeFreeConnector(connector);
466 continue;
467 }
469 for (j = 0; j < connector->count_modes; j++) {
470 c->mode = &connector->modes[j];
471 if (!strcmp(c->mode->name, c->mode_str))
472 break;
473 }
475 /* Found it, break out */
476 if (c->mode)
477 break;
479 drmModeFreeConnector(connector);
480 }
482 if (!c->mode) {
483 ERROR("failed to find mode \"%s\"", c->mode_str);
484 return;
485 }
487 /* Now get the encoder */
488 for (i = 0; i < disp_kms->resources->count_encoders; i++) {
489 c->encoder = drmModeGetEncoder(disp->fd,
490 disp_kms->resources->encoders[i]);
492 if (!c->encoder) {
493 ERROR("could not get encoder %i: %s",
494 disp_kms->resources->encoders[i], strerror(errno));
495 drmModeFreeEncoder(c->encoder);
496 continue;
497 }
499 if (c->encoder->encoder_id == connector->encoder_id)
500 break;
502 drmModeFreeEncoder(c->encoder);
503 }
505 if (c->crtc == -1)
506 c->crtc = c->encoder->crtc_id;
508 /* and figure out which crtc index it is: */
509 for (i = 0; i < disp_kms->resources->count_crtcs; i++) {
510 if (c->crtc == (int)disp_kms->resources->crtcs[i]) {
511 c->pipe = i;
512 break;
513 }
514 }
515 }
517 void
518 disp_kms_usage(void)
519 {
520 MSG("KMS Display Options:");
521 MSG("\t-1 \t\tforce single-plane buffers");
522 MSG("\t-t <tiled-mode>\t8, 16, 32, or auto");
523 MSG("\t-s <connector_id>:<mode>\tset a mode");
524 MSG("\t-s <connector_id>@<crtc_id>:<mode>\tset a mode");
525 }
527 struct display *
528 disp_kms_open(int argc, char **argv)
529 {
530 struct display_kms *disp_kms = NULL;
531 struct display *disp;
532 int i;
534 disp_kms = calloc(1, sizeof(*disp_kms));
535 if (!disp_kms) {
536 ERROR("allocation failed");
537 goto fail;
538 }
539 disp = &disp_kms->base;
541 if (!global_fd) {
542 global_fd = drmOpen("omapdrm", NULL);
543 if (global_fd < 0) {
544 ERROR("could not open drm device: %s (%d)", strerror(errno), errno);
545 goto fail;
546 }
547 }
549 disp->fd = global_fd;
550 ndisplays++; /* increment the number of displays counter */
552 disp->dev = omap_device_new(disp->fd);
553 if (!disp->dev) {
554 ERROR("couldn't create device");
555 goto fail;
556 }
558 disp->get_buffers = get_buffers;
559 disp->get_vid_buffers = get_vid_buffers;
560 disp->post_buffer = post_buffer;
561 disp->post_vid_buffer = post_vid_buffer;
562 disp->close = close_kms;
563 disp->disp_free_buf = free_buffers ;
564 disp_kms->resources = drmModeGetResources(disp->fd);
565 if (!disp_kms->resources) {
566 ERROR("drmModeGetResources failed: %s", strerror(errno));
567 goto fail;
568 }
570 disp_kms->plane_resources = drmModeGetPlaneResources(disp->fd);
571 if (!disp_kms->plane_resources) {
572 ERROR("drmModeGetPlaneResources failed: %s", strerror(errno));
573 goto fail;
574 }
576 disp->multiplanar = true;
578 /* note: set args to NULL after we've parsed them so other modules know
579 * that it is already parsed (since the arg parsing is decentralized)
580 */
581 for (i = 1; i < argc; i++) {
582 if (!argv[i]) {
583 continue;
584 }
585 if (!strcmp("-1", argv[i])) {
586 disp->multiplanar = false;
587 } else if (!strcmp("-t", argv[i])) {
588 int n;
589 argv[i++] = NULL;
590 if (!strcmp(argv[i], "auto")) {
591 n = 0;
592 } else if (sscanf(argv[i], "%d", &n) != 1) {
593 ERROR("invalid arg: %s", argv[i]);
594 goto fail;
595 }
597 disp_kms->bo_flags &= ~OMAP_BO_TILED;
599 if (n == 8) {
600 disp_kms->bo_flags |= OMAP_BO_TILED_8;
601 } else if (n == 16) {
602 disp_kms->bo_flags |= OMAP_BO_TILED_16;
603 } else if (n == 32) {
604 disp_kms->bo_flags |= OMAP_BO_TILED_32;
605 } else if (n == 0) {
606 disp_kms->bo_flags |= OMAP_BO_TILED;
607 } else {
608 ERROR("invalid arg: %s", argv[i]);
609 goto fail;
610 }
611 } else if (!strcmp("-s", argv[i])) {
612 struct connector *connector =
613 &disp_kms->connector[disp_kms->connectors_count++];
614 connector->crtc = -1;
615 argv[i++] = NULL;
616 if (sscanf(argv[i], "%d:%64s",
617 &connector->id,
618 connector->mode_str) != 2 &&
619 sscanf(argv[i], "%d@%d:%64s",
620 &connector->id,
621 &connector->crtc,
622 connector->mode_str) != 3) {
623 // TODO: we could support connector specified as a name too, I suppose
624 ERROR("invalid arg: %s", argv[i]);
625 goto fail;
626 }
627 disp_kms->bo_flags |= OMAP_BO_SCANOUT;
628 } else {
629 /* ignore */
630 continue;
631 }
632 argv[i] = NULL;
633 }
635 disp->width = 0;
636 disp->height = 0;
637 for (i = 0; i < (int)disp_kms->connectors_count; i++) {
638 struct connector *c = &disp_kms->connector[i];
639 connector_find_mode(disp, c);
640 if (c->mode == NULL)
641 continue;
642 /* setup side-by-side virtual display */
643 disp->width += c->mode->hdisplay;
644 if (disp->height < c->mode->vdisplay) {
645 disp->height = c->mode->vdisplay;
646 }
647 }
649 MSG("using %d connectors, %dx%d display, multiplanar: %d",
650 disp_kms->connectors_count, disp->width, disp->height, disp->multiplanar);
652 return disp;
654 fail:
655 // XXX cleanup
656 return NULL;
657 }