display-kms: Removed unnecessary include of libdce
[glsdk/omapdrmtest.git] / util / display-kms.c
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 };
60 #define to_buffer_kms(x) container_of(x, struct buffer_kms, base)
61 struct buffer_kms {
62         struct buffer base;
63         uint32_t fb_id;
64 };
66 static int global_fd = 0;
67 static uint32_t used_planes = 0;
68 static int ndisplays = 0;
70 static struct omap_bo *
71 alloc_bo(struct display *disp, uint32_t bpp, uint32_t width, uint32_t height,
72                 uint32_t *bo_handle, uint32_t *pitch)
73 {
74         struct display_kms *disp_kms = to_display_kms(disp);
75         struct omap_bo *bo;
76         uint32_t bo_flags = disp_kms->bo_flags;
78         if ((bo_flags & OMAP_BO_TILED) == OMAP_BO_TILED) {
79                 bo_flags &= ~OMAP_BO_TILED;
80                 if (bpp == 8) {
81                         bo_flags |= OMAP_BO_TILED_8;
82                 } else if (bpp == 16) {
83                         bo_flags |= OMAP_BO_TILED_16;
84                 } else if (bpp == 32) {
85                         bo_flags |= OMAP_BO_TILED_32;
86                 }
87         }
88         bo_flags |= OMAP_BO_WC;
90         if (bo_flags & OMAP_BO_TILED) {
91                 bo = omap_bo_new_tiled(disp->dev, ALIGN2(width,7), height, bo_flags);
92         } else {
93                 bo = omap_bo_new(disp->dev, width * height * bpp / 8, bo_flags);
94         }
96         if (bo) {
97                 *bo_handle = omap_bo_handle(bo);
98                 *pitch = width * bpp / 8;
99                 if (bo_flags & OMAP_BO_TILED)
100                         *pitch = ALIGN2(*pitch, PAGE_SHIFT);
101         }
103         return bo;
106 static struct buffer *
107 alloc_buffer(struct display *disp, uint32_t fourcc, uint32_t w, uint32_t h)
109         struct buffer_kms *buf_kms;
110         struct buffer *buf;
111         uint32_t bo_handles[4] = {0}, offsets[4] = {0};
112         int ret;
114         buf_kms = calloc(1, sizeof(*buf_kms));
115         if (!buf_kms) {
116                 ERROR("allocation failed");
117                 return NULL;
118         }
119         buf = &buf_kms->base;
121         buf->fourcc = fourcc;
122         buf->width = w;
123         buf->height = h;
124         buf->multiplanar = true;
126         buf->nbo = 1;
128         if (!fourcc)
129                 fourcc = FOURCC('A','R','2','4');
131         switch(fourcc) {
132         case FOURCC('A','R','2','4'):
133                 buf->nbo = 1;
134                 buf->bo[0] = alloc_bo(disp, 32, buf->width, buf->height,
135                                 &bo_handles[0], &buf->pitches[0]);
136                 break;
137         case FOURCC('U','Y','V','Y'):
138         case FOURCC('Y','U','Y','V'):
139                 buf->nbo = 1;
140                 buf->bo[0] = alloc_bo(disp, 16, buf->width, buf->height,
141                                 &bo_handles[0], &buf->pitches[0]);
142                 break;
143         case FOURCC('N','V','1','2'):
144                 if (disp->multiplanar) {
145                         buf->nbo = 2;
146                         buf->bo[0] = alloc_bo(disp, 8, buf->width, buf->height,
147                                         &bo_handles[0], &buf->pitches[0]);
148                         buf->fd[0] = omap_bo_dmabuf(buf->bo[0]);
149                         buf->bo[1] = alloc_bo(disp, 16, buf->width/2, buf->height/2,
150                                         &bo_handles[1], &buf->pitches[1]);
151                         buf->fd[1] = omap_bo_dmabuf(buf->bo[1]);
152                 } else {
153                         buf->nbo = 1;
154                         buf->bo[0] = alloc_bo(disp, 8, buf->width, buf->height * 3 / 2,
155                                         &bo_handles[0], &buf->pitches[0]);
156                         buf->fd[0] = omap_bo_dmabuf(buf->bo[0]);
157                         bo_handles[1] = bo_handles[0];
158                         buf->pitches[1] = buf->pitches[0];
159                         offsets[1] = buf->width * buf->height;
160                         buf->multiplanar = false;
161                 }
162                 break;
163         case FOURCC('I','4','2','0'):
164                 buf->nbo = 3;
165                 buf->bo[0] = alloc_bo(disp, 8, buf->width, buf->height,
166                                 &bo_handles[0], &buf->pitches[0]);
167                 buf->bo[1] = alloc_bo(disp, 8, buf->width/2, buf->height/2,
168                                 &bo_handles[1], &buf->pitches[1]);
169                 buf->bo[2] = alloc_bo(disp, 8, buf->width/2, buf->height/2,
170                                 &bo_handles[2], &buf->pitches[2]);
171                 break;
172         default:
173                 ERROR("invalid format: 0x%08x", fourcc);
174                 goto fail;
175         }
177         ret = drmModeAddFB2(disp->fd, buf->width, buf->height, fourcc,
178                         bo_handles, buf->pitches, offsets, &buf_kms->fb_id, 0);
179         if (ret) {
180                 ERROR("drmModeAddFB2 failed: %s (%d)", strerror(errno), ret);
181                 goto fail;
182         }
184         return buf;
186 fail:
187         // XXX cleanup
188         return NULL;
191 void
192 free_buffers(struct display *disp, uint32_t n)
194         uint32_t i;
195         for (i = 0; i < n; i++) {
196                 if (disp->buf[i]) {
197                         close(disp->buf[i]->fd[0]);
198                         omap_bo_del(disp->buf[i]->bo[0]);
199                         if(disp->multiplanar){
200                                 close(disp->buf[i]->fd[1]);
201                                 omap_bo_del(disp->buf[i]->bo[1]);
202                         }
203                 }
204         }
205 free(disp->buf);
208 static struct buffer **
209 alloc_buffers(struct display *disp, uint32_t n,
210                 uint32_t fourcc, uint32_t w, uint32_t h)
212         struct buffer **bufs;
213         uint32_t i = 0;
215         bufs = calloc(n, sizeof(*bufs));
216         if (!bufs) {
217                 ERROR("allocation failed");
218                 goto fail;
219         }
221         for (i = 0; i < n; i++) {
222                 bufs[i] = alloc_buffer(disp, fourcc, w, h);
223                 if (!bufs[i]) {
224                         ERROR("allocation failed");
225                         goto fail;
226                 }
227         }
228         disp->buf=bufs;
229         return bufs;
231 fail:
232         // XXX cleanup
233         return NULL;
236 static struct buffer **
237 get_buffers(struct display *disp, uint32_t n)
239         return alloc_buffers(disp, n, 0, disp->width, disp->height);
242 static struct buffer **
243 get_vid_buffers(struct display *disp, uint32_t n,
244                 uint32_t fourcc, uint32_t w, uint32_t h)
246         return alloc_buffers(disp, n, fourcc, w, h);
249 static void
250 page_flip_handler(int fd, unsigned int frame,
251                 unsigned int sec, unsigned int usec, void *data)
253         struct display *disp = data;
254         struct display_kms *disp_kms = to_display_kms(disp);
256         disp_kms->completed_flips++;
258         MSG("Page flip: frame=%d, sec=%d, usec=%d, remaining=%d", frame, sec, usec,
259                         disp_kms->scheduled_flips - disp_kms->completed_flips);
262 static int
263 post_buffer(struct display *disp, struct buffer *buf)
265         struct display_kms *disp_kms = to_display_kms(disp);
266         struct buffer_kms *buf_kms = to_buffer_kms(buf);
267         int ret, last_err = 0, x = 0;
268         uint32_t i;
270         for (i = 0; i < disp_kms->connectors_count; i++) {
271                 struct connector *connector = &disp_kms->connector[i];
273                 if (! connector->mode) {
274                         continue;
275                 }
277                 if (! disp_kms->current) {
278                         /* first buffer we flip to, setup the mode (since this can't
279                          * be done earlier without a buffer to scanout)
280                          */
281                         MSG("Setting mode %s on connector %d, crtc %d",
282                                         connector->mode_str, connector->id, connector->crtc);
284                         ret = drmModeSetCrtc(disp->fd, connector->crtc, buf_kms->fb_id,
285                                         x, 0, &connector->id, 1, connector->mode);
287                         x += connector->mode->hdisplay;
288                 } else {
289                         ret = drmModePageFlip(disp->fd, connector->crtc, buf_kms->fb_id,
290                                         DRM_MODE_PAGE_FLIP_EVENT, disp);
291                         disp_kms->scheduled_flips++;
292                 }
294                 if (ret) {
295                         ERROR("Could not post buffer on crtc %d: %s (%d)",
296                                         connector->crtc, strerror(errno), ret);
297                         last_err = ret;
298                         /* well, keep trying the reset of the connectors.. */
299                 }
300         }
302         /* if we flipped, wait for all flips to complete! */
303         while (disp_kms->scheduled_flips > disp_kms->completed_flips) {
304                 drmEventContext evctx = {
305                                 .version = DRM_EVENT_CONTEXT_VERSION,
306                                 .page_flip_handler = page_flip_handler,
307                 };
308                 struct timeval timeout = {
309                                 .tv_sec = 3,
310                                 .tv_usec = 0,
311                 };
312                 fd_set fds;
314                 FD_ZERO(&fds);
315                 FD_SET(disp->fd, &fds);
317                 ret = select(disp->fd + 1, &fds, NULL, NULL, &timeout);
318                 if (ret <= 0) {
319                         if (errno == EAGAIN) {
320                                 continue;    /* keep going */
321                         } else {
322                                 ERROR("Timeout waiting for flip complete: %s (%d)",
323                                                 strerror(errno), ret);
324                                 last_err = ret;
325                                 break;
326                         }
327                 }
329                 drmHandleEvent(disp->fd, &evctx);
330         }
332         disp_kms->current = buf;
334         return last_err;
337 int
338 get_overlay_plane(struct display *disp, struct buffer *buf)
340         struct display_kms *disp_kms = to_display_kms(disp);
341         uint32_t i, ret;
343         for (i = 0; i < disp_kms->connectors_count; i++) {
344                 struct connector *connector = &disp_kms->connector[i];
345                 drmModeModeInfo *mode = connector->mode;
347                 disp_kms->ovr[i] = drmModeGetPlane(disp->fd,
348                                         disp_kms->plane_resources->planes[1]);
350                 ret = drmModeObjectSetProperty(disp->fd,
351                         disp_kms->ovr[i]->plane_id, DRM_MODE_OBJECT_PLANE, 7, 3);
352                 if (ret < 0) {
353                         MSG("Could not set Z order for plane");
354                         return ret;
355                 }
356         }
357         return 0;
360 static int
361 post_vid_buffer(struct display *disp, struct buffer *buf,
362                 uint32_t x, uint32_t y, uint32_t w, uint32_t h)
364         struct display_kms *disp_kms = to_display_kms(disp);
365         struct buffer_kms *buf_kms = to_buffer_kms(buf);
366         int ret = 0;
367         uint32_t i, j;
369         /* ensure we have the overlay setup: */
370         for (i = 0; i < disp_kms->connectors_count; i++) {
371                 struct connector *connector = &disp_kms->connector[i];
372                 drmModeModeInfo *mode = connector->mode;
374                 if (! mode) {
375                         continue;
376                 }
378                 if (! disp_kms->ovr[i]) {
380                         for (j = 0; j < disp_kms->plane_resources->count_planes; j++) {
381                                 if (used_planes & (1 << j)) {
382                                         continue;
383                                 }
384                                 drmModePlane *ovr = drmModeGetPlane(disp->fd,
385                                                 disp_kms->plane_resources->planes[j]);
386                                 if (ovr->possible_crtcs & (1 << connector->pipe)) {
387                                         disp_kms->ovr[i] = ovr;
388                                         used_planes |= (1 << j);
389                                         break;
390                                 }
391                         }
392                 }
394                 if (! disp_kms->ovr[i]) {
395                         MSG("Could not find plane for crtc %d", connector->crtc);
396                         ret = -1;
397                         /* carry on and see if we can find at least one usable plane */
398                         continue;
399                 }
401                 if(buf->noScale) {
402                         ret = drmModeSetPlane(disp->fd, disp_kms->ovr[i]->plane_id,
403                                 connector->crtc, buf_kms->fb_id, 0,
404                                 /* Use x and y as co-ordinates of overlay */
405                                 x, y, buf->width, buf->height,
406                                 /* Consider source x and y is 0 always */
407                                 0, 0, w << 16, h << 16);
408                 } else {
409                         ret = drmModeSetPlane(disp->fd, disp_kms->ovr[i]->plane_id,
410                                 connector->crtc, buf_kms->fb_id, 0,
411                                 /* make video fullscreen: */
412                                 0, 0, mode->hdisplay, mode->vdisplay,
413                                 /* source/cropping coordinates are given in Q16 */
414                                 x << 16, y << 16, w << 16, h << 16);
415                 }
416                 if (ret) {
417                         ERROR("failed to enable plane %d: %s",
418                                         disp_kms->ovr[i]->plane_id, strerror(errno));
419                 }
420         }
422         return ret;
425 static void
426 close_kms(struct display *disp)
428         omap_device_del(disp->dev);
429         disp->dev = NULL;
430         if (used_planes) {
431                 used_planes >>= 1;
432         }
433         if (--ndisplays == 0) {
434                 close(global_fd);
435         }
438 static void
439 connector_find_mode(struct display *disp, struct connector *c)
441         struct display_kms *disp_kms = to_display_kms(disp);
442         drmModeConnector *connector;
443         int i, j;
445         /* First, find the connector & mode */
446         c->mode = NULL;
447         for (i = 0; i < disp_kms->resources->count_connectors; i++) {
448                 connector = drmModeGetConnector(disp->fd,
449                                 disp_kms->resources->connectors[i]);
451                 if (!connector) {
452                         ERROR("could not get connector %i: %s",
453                                         disp_kms->resources->connectors[i], strerror(errno));
454                         drmModeFreeConnector(connector);
455                         continue;
456                 }
458                 if (!connector->count_modes) {
459                         drmModeFreeConnector(connector);
460                         continue;
461                 }
463                 if (connector->connector_id != c->id) {
464                         drmModeFreeConnector(connector);
465                         continue;
466                 }
468                 for (j = 0; j < connector->count_modes; j++) {
469                         c->mode = &connector->modes[j];
470                         if (!strcmp(c->mode->name, c->mode_str))
471                                 break;
472                 }
474                 /* Found it, break out */
475                 if (c->mode)
476                         break;
478                 drmModeFreeConnector(connector);
479         }
481         if (!c->mode) {
482                 ERROR("failed to find mode \"%s\"", c->mode_str);
483                 return;
484         }
486         /* Now get the encoder */
487         for (i = 0; i < disp_kms->resources->count_encoders; i++) {
488                 c->encoder = drmModeGetEncoder(disp->fd,
489                                 disp_kms->resources->encoders[i]);
491                 if (!c->encoder) {
492                         ERROR("could not get encoder %i: %s",
493                                         disp_kms->resources->encoders[i], strerror(errno));
494                         drmModeFreeEncoder(c->encoder);
495                         continue;
496                 }
498                 if (c->encoder->encoder_id  == connector->encoder_id)
499                         break;
501                 drmModeFreeEncoder(c->encoder);
502         }
504         if (c->crtc == -1)
505                 c->crtc = c->encoder->crtc_id;
507         /* and figure out which crtc index it is: */
508         for (i = 0; i < disp_kms->resources->count_crtcs; i++) {
509                 if (c->crtc == (int)disp_kms->resources->crtcs[i]) {
510                         c->pipe = i;
511                         break;
512                 }
513         }
516 void
517 disp_kms_usage(void)
519         MSG("KMS Display Options:");
520         MSG("\t-1 \t\tforce single-plane buffers");
521         MSG("\t-t <tiled-mode>\t8, 16, 32, or auto");
522         MSG("\t-s <connector_id>:<mode>\tset a mode");
523         MSG("\t-s <connector_id>@<crtc_id>:<mode>\tset a mode");
526 struct display *
527 disp_kms_open(int argc, char **argv)
529         struct display_kms *disp_kms = NULL;
530         struct display *disp;
531         int i;
533         disp_kms = calloc(1, sizeof(*disp_kms));
534         if (!disp_kms) {
535                 ERROR("allocation failed");
536                 goto fail;
537         }
538         disp = &disp_kms->base;
540         if (!global_fd) {
541                 global_fd = drmOpen("omapdrm", NULL);
542                 if (global_fd < 0) {
543                         ERROR("could not open drm device: %s (%d)", strerror(errno), errno);
544                         goto fail;
545                 }
546         }
548         disp->fd = global_fd;
549         ndisplays++;  /* increment the number of displays counter */
551         disp->dev = omap_device_new(disp->fd);
552         if (!disp->dev) {
553                 ERROR("couldn't create device");
554                 goto fail;
555         }
557         disp->get_buffers = get_buffers;
558         disp->get_vid_buffers = get_vid_buffers;
559         disp->post_buffer = post_buffer;
560         disp->post_vid_buffer = post_vid_buffer;
561         disp->close = close_kms;
562         disp->disp_free_buf = free_buffers ;
563         disp_kms->resources = drmModeGetResources(disp->fd);
564         if (!disp_kms->resources) {
565                 ERROR("drmModeGetResources failed: %s", strerror(errno));
566                 goto fail;
567         }
569         disp_kms->plane_resources = drmModeGetPlaneResources(disp->fd);
570         if (!disp_kms->plane_resources) {
571                 ERROR("drmModeGetPlaneResources failed: %s", strerror(errno));
572                 goto fail;
573         }
575         disp->multiplanar = true;
577         /* note: set args to NULL after we've parsed them so other modules know
578          * that it is already parsed (since the arg parsing is decentralized)
579          */
580         for (i = 1; i < argc; i++) {
581                 if (!argv[i]) {
582                         continue;
583                 }
584                 if (!strcmp("-1", argv[i])) {
585                         disp->multiplanar = false;
586                 } else if (!strcmp("-t", argv[i])) {
587                         int n;
588                         argv[i++] = NULL;
589                         if (!strcmp(argv[i], "auto")) {
590                                 n = 0;
591                         } else if (sscanf(argv[i], "%d", &n) != 1) {
592                                 ERROR("invalid arg: %s", argv[i]);
593                                 goto fail;
594                         }
596                         disp_kms->bo_flags &= ~OMAP_BO_TILED;
598                         if (n == 8) {
599                                 disp_kms->bo_flags |= OMAP_BO_TILED_8;
600                         } else if (n == 16) {
601                                 disp_kms->bo_flags |= OMAP_BO_TILED_16;
602                         } else if (n == 32) {
603                                 disp_kms->bo_flags |= OMAP_BO_TILED_32;
604                         } else if (n == 0) {
605                                 disp_kms->bo_flags |= OMAP_BO_TILED;
606                         } else {
607                                 ERROR("invalid arg: %s", argv[i]);
608                                 goto fail;
609                         }
610                 } else if (!strcmp("-s", argv[i])) {
611                         struct connector *connector =
612                                         &disp_kms->connector[disp_kms->connectors_count++];
613                         connector->crtc = -1;
614                         argv[i++] = NULL;
615                         if (sscanf(argv[i], "%d:%64s",
616                                    &connector->id,
617                                    connector->mode_str) != 2 &&
618                             sscanf(argv[i], "%d@%d:%64s",
619                                    &connector->id,
620                                    &connector->crtc,
621                                    connector->mode_str) != 3) {
622                                 // TODO: we could support connector specified as a name too, I suppose
623                                 ERROR("invalid arg: %s", argv[i]);
624                                 goto fail;
625                         }
626                         disp_kms->bo_flags |= OMAP_BO_SCANOUT;
627                 } else {
628                         /* ignore */
629                         continue;
630                 }
631                 argv[i] = NULL;
632         }
634         disp->width = 0;
635         disp->height = 0;
636         for (i = 0; i < (int)disp_kms->connectors_count; i++) {
637                 struct connector *c = &disp_kms->connector[i];
638                 connector_find_mode(disp, c);
639                 if (c->mode == NULL)
640                         continue;
641                 /* setup side-by-side virtual display */
642                 disp->width += c->mode->hdisplay;
643                 if (disp->height < c->mode->vdisplay) {
644                         disp->height = c->mode->vdisplay;
645                 }
646         }
648         MSG("using %d connectors, %dx%d display, multiplanar: %d",
649                         disp_kms->connectors_count, disp->width, disp->height, disp->multiplanar);
651         return disp;
653 fail:
654         // XXX cleanup
655         return NULL;