3c9ed10e15f75b032a1730416bcb3ea9e10054da
[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"
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;
107 static struct buffer *
108 alloc_buffer(struct display *disp, uint32_t fourcc, uint32_t w, uint32_t h)
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;
192 void
193 free_buffers(struct display *disp, uint32_t n)
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);
209 static struct buffer **
210 alloc_buffers(struct display *disp, uint32_t n,
211                 uint32_t fourcc, uint32_t w, uint32_t h)
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;
237 static struct buffer **
238 get_buffers(struct display *disp, uint32_t n)
240         return alloc_buffers(disp, n, 0, disp->width, disp->height);
243 static struct buffer **
244 get_vid_buffers(struct display *disp, uint32_t n,
245                 uint32_t fourcc, uint32_t w, uint32_t h)
247         return alloc_buffers(disp, n, fourcc, w, h);
250 static void
251 page_flip_handler(int fd, unsigned int frame,
252                 unsigned int sec, unsigned int usec, void *data)
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);
263 static int
264 post_buffer(struct display *disp, struct buffer *buf)
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;
338 int
339 get_overlay_plane(struct display *disp, struct buffer *buf)
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;
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)
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;
426 static void
427 close_kms(struct display *disp)
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         }
439 static void
440 connector_find_mode(struct display *disp, struct connector *c)
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         }
517 void
518 disp_kms_usage(void)
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");
527 struct display *
528 disp_kms_open(int argc, char **argv)
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;