4265090450711b81dd0e9e36a7e12a7dfb5a0e51
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 struct omap_bo *
68 alloc_bo(struct display *disp, uint32_t bpp, uint32_t width, uint32_t height,
69 uint32_t *bo_handle, uint32_t *pitch)
70 {
71 struct display_kms *disp_kms = to_display_kms(disp);
72 struct omap_bo *bo;
73 uint32_t bo_flags = disp_kms->bo_flags;
75 if ((bo_flags & OMAP_BO_TILED) == OMAP_BO_TILED) {
76 bo_flags &= ~OMAP_BO_TILED;
77 if (bpp == 8) {
78 bo_flags |= OMAP_BO_TILED_8;
79 } else if (bpp == 16) {
80 bo_flags |= OMAP_BO_TILED_16;
81 } else if (bpp == 32) {
82 bo_flags |= OMAP_BO_TILED_32;
83 }
84 }
85 bo_flags |= OMAP_BO_WC;
87 if (bo_flags & OMAP_BO_TILED) {
88 bo = omap_bo_new_tiled(disp->dev, ALIGN2(width,7), height, bo_flags);
89 } else {
90 bo = omap_bo_new(disp->dev, width * height * bpp / 8, bo_flags);
91 }
93 if (bo) {
94 *bo_handle = omap_bo_handle(bo);
95 *pitch = width * bpp / 8;
96 if (bo_flags & OMAP_BO_TILED)
97 *pitch = ALIGN2(*pitch, PAGE_SHIFT);
98 }
100 return bo;
101 }
103 static struct buffer *
104 alloc_buffer(struct display *disp, uint32_t fourcc, uint32_t w, uint32_t h)
105 {
106 struct buffer_kms *buf_kms;
107 struct buffer *buf;
108 uint32_t bo_handles[4] = {0}, offsets[4] = {0};
109 int ret;
111 buf_kms = calloc(1, sizeof(*buf_kms));
112 if (!buf_kms) {
113 ERROR("allocation failed");
114 return NULL;
115 }
116 buf = &buf_kms->base;
118 buf->fourcc = fourcc;
119 buf->width = w;
120 buf->height = h;
121 buf->multiplanar = true;
123 buf->nbo = 1;
125 if (!fourcc)
126 fourcc = FOURCC('A','R','2','4');
128 switch(fourcc) {
129 case FOURCC('A','R','2','4'):
130 buf->nbo = 1;
131 buf->bo[0] = alloc_bo(disp, 32, buf->width, buf->height,
132 &bo_handles[0], &buf->pitches[0]);
133 break;
134 case FOURCC('U','Y','V','Y'):
135 case FOURCC('Y','U','Y','V'):
136 buf->nbo = 1;
137 buf->bo[0] = alloc_bo(disp, 16, buf->width, buf->height,
138 &bo_handles[0], &buf->pitches[0]);
139 break;
140 case FOURCC('N','V','1','2'):
141 if (disp->multiplanar) {
142 buf->nbo = 2;
143 buf->bo[0] = alloc_bo(disp, 8, buf->width, buf->height,
144 &bo_handles[0], &buf->pitches[0]);
145 buf->fd[0] = omap_bo_dmabuf(buf->bo[0]);
146 buf->bo[1] = alloc_bo(disp, 16, buf->width/2, buf->height/2,
147 &bo_handles[1], &buf->pitches[1]);
148 buf->fd[1] = omap_bo_dmabuf(buf->bo[1]);
149 } else {
150 buf->nbo = 1;
151 buf->bo[0] = alloc_bo(disp, 8, buf->width, buf->height * 3 / 2,
152 &bo_handles[0], &buf->pitches[0]);
153 buf->fd[0] = omap_bo_dmabuf(buf->bo[0]);
154 bo_handles[1] = bo_handles[0];
155 buf->pitches[1] = buf->pitches[0];
156 offsets[1] = buf->width * buf->height;
157 buf->multiplanar = false;
158 }
159 break;
160 case FOURCC('I','4','2','0'):
161 buf->nbo = 3;
162 buf->bo[0] = alloc_bo(disp, 8, buf->width, buf->height,
163 &bo_handles[0], &buf->pitches[0]);
164 buf->bo[1] = alloc_bo(disp, 8, buf->width/2, buf->height/2,
165 &bo_handles[1], &buf->pitches[1]);
166 buf->bo[2] = alloc_bo(disp, 8, buf->width/2, buf->height/2,
167 &bo_handles[2], &buf->pitches[2]);
168 break;
169 default:
170 ERROR("invalid format: 0x%08x", fourcc);
171 goto fail;
172 }
174 ret = drmModeAddFB2(disp->fd, buf->width, buf->height, fourcc,
175 bo_handles, buf->pitches, offsets, &buf_kms->fb_id, 0);
176 if (ret) {
177 ERROR("drmModeAddFB2 failed: %s (%d)", strerror(errno), ret);
178 goto fail;
179 }
181 return buf;
183 fail:
184 // XXX cleanup
185 return NULL;
186 }
188 void
189 free_buffers(struct display *disp, uint32_t n)
190 {
191 uint32_t i;
192 for (i = 0; i < n; i++) {
193 if (disp->buf[i]) {
194 omap_bo_del(disp->buf[i]->bo[0]);
195 if(disp->multiplanar){
196 omap_bo_del(disp->buf[i]->bo[1]);
197 }
198 }
199 }
200 free(disp->buf);
201 }
203 static struct buffer **
204 alloc_buffers(struct display *disp, uint32_t n,
205 uint32_t fourcc, uint32_t w, uint32_t h)
206 {
207 struct buffer **bufs;
208 uint32_t i = 0;
210 bufs = calloc(n, sizeof(*bufs));
211 if (!bufs) {
212 ERROR("allocation failed");
213 goto fail;
214 }
216 for (i = 0; i < n; i++) {
217 bufs[i] = alloc_buffer(disp, fourcc, w, h);
218 if (!bufs[i]) {
219 ERROR("allocation failed");
220 goto fail;
221 }
222 }
223 disp->buf=bufs;
224 return bufs;
226 fail:
227 // XXX cleanup
228 return NULL;
229 }
231 static struct buffer **
232 get_buffers(struct display *disp, uint32_t n)
233 {
234 return alloc_buffers(disp, n, 0, disp->width, disp->height);
235 }
237 static struct buffer **
238 get_vid_buffers(struct display *disp, uint32_t n,
239 uint32_t fourcc, uint32_t w, uint32_t h)
240 {
241 return alloc_buffers(disp, n, fourcc, w, h);
242 }
244 static void
245 page_flip_handler(int fd, unsigned int frame,
246 unsigned int sec, unsigned int usec, void *data)
247 {
248 struct display *disp = data;
249 struct display_kms *disp_kms = to_display_kms(disp);
251 disp_kms->completed_flips++;
253 MSG("Page flip: frame=%d, sec=%d, usec=%d, remaining=%d", frame, sec, usec,
254 disp_kms->scheduled_flips - disp_kms->completed_flips);
255 }
257 static int
258 post_buffer(struct display *disp, struct buffer *buf)
259 {
260 struct display_kms *disp_kms = to_display_kms(disp);
261 struct buffer_kms *buf_kms = to_buffer_kms(buf);
262 int ret, last_err = 0, x = 0;
263 uint32_t i;
265 for (i = 0; i < disp_kms->connectors_count; i++) {
266 struct connector *connector = &disp_kms->connector[i];
268 if (! connector->mode) {
269 continue;
270 }
272 if (! disp_kms->current) {
273 /* first buffer we flip to, setup the mode (since this can't
274 * be done earlier without a buffer to scanout)
275 */
276 MSG("Setting mode %s on connector %d, crtc %d",
277 connector->mode_str, connector->id, connector->crtc);
279 ret = drmModeSetCrtc(disp->fd, connector->crtc, buf_kms->fb_id,
280 x, 0, &connector->id, 1, connector->mode);
282 x += connector->mode->hdisplay;
283 } else {
284 ret = drmModePageFlip(disp->fd, connector->crtc, buf_kms->fb_id,
285 DRM_MODE_PAGE_FLIP_EVENT, disp);
286 disp_kms->scheduled_flips++;
287 }
289 if (ret) {
290 ERROR("Could not post buffer on crtc %d: %s (%d)",
291 connector->crtc, strerror(errno), ret);
292 last_err = ret;
293 /* well, keep trying the reset of the connectors.. */
294 }
295 }
297 /* if we flipped, wait for all flips to complete! */
298 while (disp_kms->scheduled_flips > disp_kms->completed_flips) {
299 drmEventContext evctx = {
300 .version = DRM_EVENT_CONTEXT_VERSION,
301 .page_flip_handler = page_flip_handler,
302 };
303 struct timeval timeout = {
304 .tv_sec = 3,
305 .tv_usec = 0,
306 };
307 fd_set fds;
309 FD_ZERO(&fds);
310 FD_SET(disp->fd, &fds);
312 ret = select(disp->fd + 1, &fds, NULL, NULL, &timeout);
313 if (ret <= 0) {
314 if (errno == EAGAIN) {
315 continue; /* keep going */
316 } else {
317 ERROR("Timeout waiting for flip complete: %s (%d)",
318 strerror(errno), ret);
319 last_err = ret;
320 break;
321 }
322 }
324 drmHandleEvent(disp->fd, &evctx);
325 }
327 disp_kms->current = buf;
329 return last_err;
330 }
332 static int
333 post_vid_buffer(struct display *disp, struct buffer *buf,
334 uint32_t x, uint32_t y, uint32_t w, uint32_t h)
335 {
336 struct display_kms *disp_kms = to_display_kms(disp);
337 struct buffer_kms *buf_kms = to_buffer_kms(buf);
338 int ret = 0;
339 uint32_t i, j;
340 int busy_planes = 0;
342 /* ensure we have the overlay setup: */
343 for (i = 0; i < disp_kms->connectors_count; i++) {
344 struct connector *connector = &disp_kms->connector[i];
345 uint32_t used_planes = 0;
346 drmModeModeInfo *mode = connector->mode;
348 if (! mode) {
349 continue;
350 }
352 if (! disp_kms->ovr[i]) {
354 for (j = 0; j + used_planes < disp_kms->plane_resources->count_planes; j++) {
355 drmModePlane *ovr = drmModeGetPlane(disp->fd,
356 disp_kms->plane_resources->planes[j + busy_planes]);
357 if ((ovr->possible_crtcs & (1 << connector->pipe)) &&
358 !(used_planes & (1 << j))) {
359 disp_kms->ovr[i] = ovr;
360 used_planes |= (1 << j);
361 printf("\nused_planes = %d\n", used_planes);
362 break;
363 }
364 }
365 }
367 if (! disp_kms->ovr[i]) {
368 MSG("Could not find plane for crtc %d", connector->crtc);
369 ret = -1;
370 /* carry on and see if we can find at least one usable plane */
371 continue;
372 }
374 ret = drmModeSetPlane(disp->fd, disp_kms->ovr[i]->plane_id,
375 connector->crtc, buf_kms->fb_id, 0,
376 /* make video fullscreen: */
377 0, 0, mode->hdisplay, mode->vdisplay,
378 /* source/cropping coordinates are given in Q16 */
379 x << 16, y << 16, w << 16, h << 16);
380 if (ret) {
381 ERROR("failed to enable plane %d: %s",
382 disp_kms->ovr[i]->plane_id, strerror(errno));
383 }
384 }
386 return ret;
387 }
389 static void
390 close_kms(struct display *disp)
391 {
392 }
394 static void
395 connector_find_mode(struct display *disp, struct connector *c)
396 {
397 struct display_kms *disp_kms = to_display_kms(disp);
398 drmModeConnector *connector;
399 int i, j;
401 /* First, find the connector & mode */
402 c->mode = NULL;
403 for (i = 0; i < disp_kms->resources->count_connectors; i++) {
404 connector = drmModeGetConnector(disp->fd,
405 disp_kms->resources->connectors[i]);
407 if (!connector) {
408 ERROR("could not get connector %i: %s",
409 disp_kms->resources->connectors[i], strerror(errno));
410 drmModeFreeConnector(connector);
411 continue;
412 }
414 if (!connector->count_modes) {
415 drmModeFreeConnector(connector);
416 continue;
417 }
419 if (connector->connector_id != c->id) {
420 drmModeFreeConnector(connector);
421 continue;
422 }
424 for (j = 0; j < connector->count_modes; j++) {
425 c->mode = &connector->modes[j];
426 if (!strcmp(c->mode->name, c->mode_str))
427 break;
428 }
430 /* Found it, break out */
431 if (c->mode)
432 break;
434 drmModeFreeConnector(connector);
435 }
437 if (!c->mode) {
438 ERROR("failed to find mode \"%s\"", c->mode_str);
439 return;
440 }
442 /* Now get the encoder */
443 for (i = 0; i < disp_kms->resources->count_encoders; i++) {
444 c->encoder = drmModeGetEncoder(disp->fd,
445 disp_kms->resources->encoders[i]);
447 if (!c->encoder) {
448 ERROR("could not get encoder %i: %s",
449 disp_kms->resources->encoders[i], strerror(errno));
450 drmModeFreeEncoder(c->encoder);
451 continue;
452 }
454 if (c->encoder->encoder_id == connector->encoder_id)
455 break;
457 drmModeFreeEncoder(c->encoder);
458 }
460 if (c->crtc == -1)
461 c->crtc = c->encoder->crtc_id;
463 /* and figure out which crtc index it is: */
464 for (i = 0; i < disp_kms->resources->count_crtcs; i++) {
465 if (c->crtc == (int)disp_kms->resources->crtcs[i]) {
466 c->pipe = i;
467 break;
468 }
469 }
470 }
472 void
473 disp_kms_usage(void)
474 {
475 MSG("KMS Display Options:");
476 MSG("\t-1 \t\tforce single-plane buffers");
477 MSG("\t-t <tiled-mode>\t8, 16, 32, or auto");
478 MSG("\t-s <connector_id>:<mode>\tset a mode");
479 MSG("\t-s <connector_id>@<crtc_id>:<mode>\tset a mode");
480 }
482 struct display *
483 disp_kms_open(int argc, char **argv)
484 {
485 struct display_kms *disp_kms = NULL;
486 struct display *disp;
487 int i;
489 disp_kms = calloc(1, sizeof(*disp_kms));
490 if (!disp_kms) {
491 ERROR("allocation failed");
492 goto fail;
493 }
494 disp = &disp_kms->base;
496 disp->fd = drmOpen("omapdrm", NULL);
497 if (disp->fd < 0) {
498 ERROR("could not open drm device: %s (%d)", strerror(errno), errno);
499 goto fail;
500 }
502 disp->dev = omap_device_new(disp->fd);
503 if (!disp->dev) {
504 ERROR("couldn't create device");
505 goto fail;
506 }
508 disp->get_buffers = get_buffers;
509 disp->get_vid_buffers = get_vid_buffers;
510 disp->post_buffer = post_buffer;
511 disp->post_vid_buffer = post_vid_buffer;
512 disp->close = close_kms;
513 disp->disp_free_buf = free_buffers ;
514 disp_kms->resources = drmModeGetResources(disp->fd);
515 if (!disp_kms->resources) {
516 ERROR("drmModeGetResources failed: %s", strerror(errno));
517 goto fail;
518 }
520 disp_kms->plane_resources = drmModeGetPlaneResources(disp->fd);
521 if (!disp_kms->plane_resources) {
522 ERROR("drmModeGetPlaneResources failed: %s", strerror(errno));
523 goto fail;
524 }
526 disp->multiplanar = true;
528 /* note: set args to NULL after we've parsed them so other modules know
529 * that it is already parsed (since the arg parsing is decentralized)
530 */
531 for (i = 1; i < argc; i++) {
532 if (!argv[i]) {
533 continue;
534 }
535 if (!strcmp("-1", argv[i])) {
536 disp->multiplanar = false;
537 } else if (!strcmp("-t", argv[i])) {
538 int n;
539 argv[i++] = NULL;
540 if (!strcmp(argv[i], "auto")) {
541 n = 0;
542 } else if (sscanf(argv[i], "%d", &n) != 1) {
543 ERROR("invalid arg: %s", argv[i]);
544 goto fail;
545 }
547 disp_kms->bo_flags &= ~OMAP_BO_TILED;
549 if (n == 8) {
550 disp_kms->bo_flags |= OMAP_BO_TILED_8;
551 } else if (n == 16) {
552 disp_kms->bo_flags |= OMAP_BO_TILED_16;
553 } else if (n == 32) {
554 disp_kms->bo_flags |= OMAP_BO_TILED_32;
555 } else if (n == 0) {
556 disp_kms->bo_flags |= OMAP_BO_TILED;
557 } else {
558 ERROR("invalid arg: %s", argv[i]);
559 goto fail;
560 }
561 } else if (!strcmp("-s", argv[i])) {
562 struct connector *connector =
563 &disp_kms->connector[disp_kms->connectors_count++];
564 connector->crtc = -1;
565 argv[i++] = NULL;
566 if (sscanf(argv[i], "%d:%64s",
567 &connector->id,
568 connector->mode_str) != 2 &&
569 sscanf(argv[i], "%d@%d:%64s",
570 &connector->id,
571 &connector->crtc,
572 connector->mode_str) != 3) {
573 // TODO: we could support connector specified as a name too, I suppose
574 ERROR("invalid arg: %s", argv[i]);
575 goto fail;
576 }
577 disp_kms->bo_flags |= OMAP_BO_SCANOUT;
578 } else {
579 /* ignore */
580 continue;
581 }
582 argv[i] = NULL;
583 }
585 disp->width = 0;
586 disp->height = 0;
587 for (i = 0; i < (int)disp_kms->connectors_count; i++) {
588 struct connector *c = &disp_kms->connector[i];
589 connector_find_mode(disp, c);
590 if (c->mode == NULL)
591 continue;
592 /* setup side-by-side virtual display */
593 disp->width += c->mode->hdisplay;
594 if (disp->height < c->mode->vdisplay) {
595 disp->height = c->mode->vdisplay;
596 }
597 }
599 MSG("using %d connectors, %dx%d display, multiplanar: %d",
600 disp_kms->connectors_count, disp->width, disp->height, disp->multiplanar);
602 return disp;
604 fail:
605 // XXX cleanup
606 return NULL;
607 }