Initial commit
authorRob Clark <rob@ti.com>
Mon, 5 Dec 2011 21:13:10 +0000 (15:13 -0600)
committerRob Clark <rob@ti.com>
Fri, 9 Dec 2011 00:56:32 +0000 (18:56 -0600)
13 files changed:
.gitignore [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
README [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
configure.ac [new file with mode: 0644]
dmabuftest.c [new file with mode: 0644]
fliptest.c [new file with mode: 0644]
util/Makefile.am [new file with mode: 0644]
util/display-kms.c [new file with mode: 0644]
util/display-x11.c [new file with mode: 0644]
util/util.c [new file with mode: 0644]
util/util.h [new file with mode: 0644]
util/v4l2.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..b4f9d7d
--- /dev/null
@@ -0,0 +1,31 @@
+aclocal.m4
+autom4te.cache
+Makefile.in
+Makefile
+.deps
+.libs
+*.o
+*.lo
+*.la
+libtool
+*.pc
+config.log
+config.status
+config.guess
+config.h
+config.h.in
+config.sub
+config
+configure
+install-sh
+ltmain.sh
+missing
+stamp-h1
+depcomp
+.cproject
+.project
+.settings
+
+# test executables:
+dmabuftest
+fliptest
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..95e8e22
--- /dev/null
@@ -0,0 +1,28 @@
+#  
+#  Copyright (C) 2011 Texas Instruments
+#  Author: Rob Clark <rob.clark@linaro.org>
+#  
+#  This program is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU General Public License version 2 as published by
+#  the Free Software Foundation.
+#  
+#  This program is distributed in the hope that it will be useful, but WITHOUT
+#  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+#  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+#  more details.
+#  
+#  You should have received a copy of the GNU General Public License along with
+#  this program.  If not, see <http://www.gnu.org/licenses/>.
+#  
+
+SUBDIRS = util
+bin_PROGRAMS = fliptest dmabuftest
+
+LDADD_COMMON = @DRM_LIBS@ @X11_LIBS@ util/libutil.la
+CFLAGS = @DRM_CFLAGS@ @X11_CFLAGS@ @WARN_CFLAGS@ -I$(top_srcdir)/util
+
+fliptest_SOURCES = fliptest.c
+fliptest_LDADD = $(LDADD_COMMON) util/libutil.la
+
+dmabuftest_SOURCES = dmabuftest.c
+dmabuftest_LDADD = $(LDADD_COMMON) util/libutil.la
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..7c80a3d
--- /dev/null
+++ b/README
@@ -0,0 +1 @@
+A home for omapdrm tests.
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..904cd67
--- /dev/null
@@ -0,0 +1,12 @@
+#! /bin/sh
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+autoreconf -v --install || exit 1
+cd $ORIGDIR || exit $?
+
+$srcdir/configure --enable-maintainer-mode "$@"
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..47a3565
--- /dev/null
@@ -0,0 +1,109 @@
+#  
+#  Copyright (C) 2011 Texas Instruments
+#  Author: Rob Clark <rob.clark@linaro.org>
+#  
+#  This program is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU General Public License version 2 as published by
+#  the Free Software Foundation.
+#  
+#  This program is distributed in the hope that it will be useful, but WITHOUT
+#  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+#  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+#  more details.
+#  
+#  You should have received a copy of the GNU General Public License along with
+#  this program.  If not, see <http://www.gnu.org/licenses/>.
+#  
+
+# Initialize Autoconf
+AC_PREREQ([2.60])
+AC_INIT([omapdrmtest], [1.0.0], [https://www.ti.com], [omapdrmtest])
+AC_CONFIG_SRCDIR([Makefile.am])
+AC_CONFIG_HEADERS([config.h])
+
+# Initialize Automake
+AM_INIT_AUTOMAKE([foreign dist-bzip2])
+AM_MAINTAINER_MODE
+
+# Enable quiet compiles on automake 1.11.
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+# Initialize libtool
+AC_PROG_LIBTOOL
+
+# Obtain compiler/linker options for depedencies
+PKG_CHECK_MODULES(DRM, libdrm libdrm_omap)
+
+# Check optional X11:
+PKG_CHECK_MODULES(X11, x11 libdri2, [HAVE_X11=yes], [HAVE_X11=no])
+
+if test "x$HAVE_X11" = "xyes"; then
+       AC_DEFINE(HAVE_X11, 1, [Have X11 support])
+else
+       AC_MSG_WARN([No X11 support detected, disabling X11 support])
+fi
+AM_CONDITIONAL(ENABLE_X11, [test "x$HAVE_X11" = xyes])
+
+
+dnl ===========================================================================
+dnl check compiler flags
+AC_DEFUN([LIBDRM_CC_TRY_FLAG], [
+  AC_MSG_CHECKING([whether $CC supports $1])
+
+  libdrm_save_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS $1"
+
+  AC_COMPILE_IFELSE([ ], [libdrm_cc_flag=yes], [libdrm_cc_flag=no])
+  CFLAGS="$libdrm_save_CFLAGS"
+
+  if test "x$libdrm_cc_flag" = "xyes"; then
+    ifelse([$2], , :, [$2])
+  else
+    ifelse([$3], , :, [$3])
+  fi
+  AC_MSG_RESULT([$libdrm_cc_flag])
+])
+
+MAYBE_WARN="-Wall -Wextra \
+-Wsign-compare -Werror-implicit-function-declaration \
+-Wpointer-arith -Wwrite-strings -Wstrict-prototypes \
+-Wnested-externs \
+-Wpacked -Wswitch-enum -Wmissing-format-attribute \
+-Wstrict-aliasing=2 -Winit-self -Wunsafe-loop-optimizations \
+-Wdeclaration-after-statement -Wold-style-definition \
+-Wno-missing-field-initializers -Wno-unused-parameter \
+-Wno-attributes -Wno-long-long -Winline"
+
+# invalidate cached value if MAYBE_WARN has changed
+if test "x$libdrm_cv_warn_maybe" != "x$MAYBE_WARN"; then
+        unset libdrm_cv_warn_cflags
+fi
+AC_CACHE_CHECK([for supported warning flags], libdrm_cv_warn_cflags, [
+        echo
+        WARN_CFLAGS=""
+
+        # Some warning options are not supported by all versions of
+        # gcc, so test all desired options against the current
+        # compiler.
+        #
+        # Note that there are some order dependencies
+        # here. Specifically, an option that disables a warning will
+        # have no net effect if a later option then enables that
+        # warnings, (perhaps implicitly). So we put some grouped
+        # options (-Wall and -Wextra) up front and the -Wno options
+        # last.
+
+        for W in $MAYBE_WARN; do
+                LIBDRM_CC_TRY_FLAG([$W], [WARN_CFLAGS="$WARN_CFLAGS $W"])
+        done
+
+        libdrm_cv_warn_cflags=$WARN_CFLAGS
+        libdrm_cv_warn_maybe=$MAYBE_WARN
+
+        AC_MSG_CHECKING([which warning flags were supported])])
+WARN_CFLAGS="$libdrm_cv_warn_cflags"
+AC_SUBST(WARN_CFLAGS)
+
+
+AC_CONFIG_FILES([Makefile util/Makefile])
+AC_OUTPUT
diff --git a/dmabuftest.c b/dmabuftest.c
new file mode 100644 (file)
index 0000000..1bb3c39
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob.clark@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "util.h"
+
+#define NBUF 3
+#define CNT  500
+
+void
+usage(char *name)
+{
+       MSG("Usage: %s [OPTION]...", name);
+       MSG("Test of buffer passing between v4l2 camera and display.");
+       MSG("");
+       disp_usage();
+       v4l2_usage();
+}
+
+int
+main(int argc, char **argv)
+{
+       struct display *disp;
+       struct v4l2 *v4l2;
+       struct buffer **buffers;
+       int ret, i;
+
+       MSG("Opening Display..");
+       disp = disp_open(argc, argv);
+       if (!disp) {
+               usage(argv[0]);
+               return 1;
+       }
+
+       MSG("Opening V4L2..");
+       v4l2 = v4l2_open(argc, argv);
+       if (!v4l2) {
+               usage(argv[0]);
+               return 1;
+       }
+
+       if (check_args(argc, argv)) {
+               /* remaining args.. print usage msg */
+               usage(argv[0]);
+               return 0;
+       }
+
+       buffers = disp_get_buffers(disp, NBUF);
+       if (!buffers) {
+               return 1;
+       }
+
+       ret = v4l2_reqbufs(v4l2, buffers, NBUF);
+       if (ret) {
+               return 1;
+       }
+
+       v4l2_qbuf(v4l2, buffers[0]);
+       v4l2_streamon(v4l2);
+       for (i = 1; i < CNT; i++) {
+               v4l2_qbuf(v4l2, buffers[i % NBUF]);
+               ret = disp_post_buffer(disp, v4l2_dqbuf(v4l2));
+               if (ret) {
+                       return ret;
+               }
+       }
+       v4l2_streamoff(v4l2);
+       v4l2_dqbuf(v4l2);
+
+       MSG("Ok!");
+
+       return ret;
+}
diff --git a/fliptest.c b/fliptest.c
new file mode 100644 (file)
index 0000000..ea424a8
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob.clark@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "util.h"
+
+#define NBUF 3
+#define CNT  500
+
+void
+usage(char *name)
+{
+       MSG("Usage: %s [OPTION]...", name);
+       MSG("Simple page-flip test, similar to 'modetest' but with option to use tiled buffers.");
+       MSG("");
+       disp_usage();
+}
+
+int
+main(int argc, char **argv)
+{
+       struct display *disp;
+       struct buffer **buffers;
+       int ret, i;
+
+       MSG("Opening Display..");
+       disp = disp_open(argc, argv);
+       if (!disp) {
+               usage(argv[0]);
+               return 1;
+       }
+
+       if (check_args(argc, argv)) {
+               /* remaining args.. print usage msg */
+               usage(argv[0]);
+               return 0;
+       }
+
+       buffers = disp_get_buffers(disp, NBUF);
+       if (!buffers) {
+               return 1;
+       }
+
+       for (i = 0; i < CNT; i++) {
+               struct buffer *buf = buffers[i % NBUF];
+               fill(buf, i * 2);
+               ret = disp_post_buffer(disp, buf);
+               if (ret) {
+                       return ret;
+               }
+       }
+
+       MSG("Ok!");
+
+       return 0;
+}
diff --git a/util/Makefile.am b/util/Makefile.am
new file mode 100644 (file)
index 0000000..864ee25
--- /dev/null
@@ -0,0 +1,31 @@
+#  
+#  Copyright (C) 2011 Texas Instruments
+#  Author: Rob Clark <rob.clark@linaro.org>
+#  
+#  This program is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU General Public License version 2 as published by
+#  the Free Software Foundation.
+#  
+#  This program is distributed in the hope that it will be useful, but WITHOUT
+#  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+#  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+#  more details.
+#  
+#  You should have received a copy of the GNU General Public License along with
+#  this program.  If not, see <http://www.gnu.org/licenses/>.
+#  
+
+noinst_LTLIBRARIES = libutil.la
+
+libutil_la_SOURCES = \
+       display-kms.c \
+       display-x11.c \
+       v4l2.c \
+       util.c
+
+if ENABLE_X11
+libutil_la_SOURCES += x11.c
+endif
+
+libutil_la_LIBADD = @DRM_LIBS@ @X11_LIBS@
+libutil_la_CFLAGS = @DRM_CFLAGS@ @X11_CFLAGS@ @WARN_CFLAGS@
diff --git a/util/display-kms.c b/util/display-kms.c
new file mode 100644 (file)
index 0000000..57998b4
--- /dev/null
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob.clark@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "util.h"
+
+#include <xf86drmMode.h>
+
+
+/* NOTE: healthy dose of recycling from libdrm modetest app.. */
+
+/*
+ * Mode setting with the kernel interfaces is a bit of a chore.
+ * First you have to find the connector in question and make sure the
+ * requested mode is available.
+ * Then you need to find the encoder attached to that connector so you
+ * can bind it with a free crtc.
+ */
+struct connector {
+       uint32_t id;
+       char mode_str[64];
+       drmModeModeInfo *mode;
+       drmModeEncoder *encoder;
+       int crtc;
+};
+
+#define to_display_kms(x) container_of(x, struct display_kms, base)
+struct display_kms {
+       struct display base;
+
+       int connectors_count;
+       struct connector connector[10];
+
+       int scheduled_flips, completed_flips;
+       uint32_t bo_flags;
+       drmModeResPtr resources;
+       struct buffer *current;
+};
+
+#define to_buffer_kms(x) container_of(x, struct buffer_kms, base)
+struct buffer_kms {
+       struct buffer base;
+       uint32_t fb_id;
+};
+
+static struct buffer *
+alloc_buffer(struct display *disp, uint32_t fourcc, uint32_t w, uint32_t h)
+{
+       struct display_kms *disp_kms = to_display_kms(disp);
+       struct buffer_kms *buf_kms;
+       struct buffer *buf;
+       uint32_t depth, bpp;
+       int ret;
+
+       buf_kms = calloc(1, sizeof(*buf_kms));
+       if (!buf_kms) {
+               ERROR("allocation failed");
+               return NULL;
+       }
+       buf = &buf_kms->base;
+
+       buf->fourcc = fourcc;
+       buf->width = w;
+       buf->height = h;
+
+       switch(fourcc) {
+       case 0:
+               /* native fb format: */
+               buf->stride = 4 * buf->width;
+               depth = 24;
+               bpp = 32;
+               break;
+       /*TODO add YUV formats.. */
+       default:
+               ERROR("invalid format: 0x%08x", fourcc);
+               goto fail;
+       }
+
+       if (disp_kms->bo_flags & OMAP_BO_TILED) {
+               buf->bo = omap_bo_new_tiled(disp->dev, buf->width, buf->height,
+                               disp_kms->bo_flags);
+       } else {
+               uint32_t sz = buf->stride * buf->height;
+               buf->bo = omap_bo_new(disp->dev, sz, disp_kms->bo_flags);
+       }
+
+       if (!buf->bo) {
+               ERROR("allocation failed");
+               goto fail;
+       }
+
+       if (!fourcc) {
+               ret = drmModeAddFB(disp->fd, buf->width, buf->height, depth, bpp,
+                               buf->stride, omap_bo_handle(buf->bo), &buf_kms->fb_id);
+       } else {
+               // XXX use drmModeAddFB2()..
+               ERROR("TODO");
+               goto fail;
+       }
+
+       if (ret) {
+               ERROR("drmModeAddFB(2) failed: %s (%d)", strerror(errno), ret);
+               goto fail;
+       }
+
+       return buf;
+
+fail:
+       // XXX cleanup
+       return NULL;
+}
+
+static struct buffer **
+alloc_buffers(struct display *disp, uint32_t n,
+               uint32_t fourcc, uint32_t w, uint32_t h)
+{
+       struct buffer **bufs;
+       uint32_t i = 0;
+
+       bufs = calloc(n, sizeof(*bufs));
+       if (!bufs) {
+               ERROR("allocation failed");
+               goto fail;
+       }
+
+       for (i = 0; i < n; i++) {
+               bufs[i] = alloc_buffer(disp, fourcc, w, h);
+               if (!bufs[i]) {
+                       ERROR("allocation failed");
+                       goto fail;
+               }
+       }
+
+       return bufs;
+
+fail:
+       // XXX cleanup
+       return NULL;
+}
+
+static struct buffer **
+get_buffers(struct display *disp, uint32_t n)
+{
+       return alloc_buffers(disp, n, 0, disp->width, disp->height);
+}
+
+static struct buffer **
+get_vid_buffers(struct display *disp, uint32_t n,
+               uint32_t fourcc, uint32_t w, uint32_t h)
+{
+       return alloc_buffers(disp, n, fourcc, w, h);
+}
+
+static void
+page_flip_handler(int fd, unsigned int frame,
+               unsigned int sec, unsigned int usec, void *data)
+{
+       struct display *disp = data;
+       struct display_kms *disp_kms = to_display_kms(disp);
+
+       disp_kms->completed_flips++;
+
+       MSG("Page flip: frame=%d, sec=%d, usec=%d, remaining=%d", frame, sec, usec,
+                       disp_kms->scheduled_flips - disp_kms->completed_flips);
+}
+
+static int
+post_buffer(struct display *disp, struct buffer *buf)
+{
+       struct display_kms *disp_kms = to_display_kms(disp);
+       struct buffer_kms *buf_kms = to_buffer_kms(buf);
+       int i, ret, last_err = 0, x = 0;
+
+       for (i = 0; i < disp_kms->connectors_count; i++) {
+               struct connector *connector = &disp_kms->connector[i];
+
+               if (! connector->mode) {
+                       continue;
+               }
+
+               if (! disp_kms->current) {
+                       /* first buffer we flip to, setup the mode (since this can't
+                        * be done earlier without a buffer to scanout)
+                        */
+                       MSG("Setting mode %s on connector %d, crtc %d",
+                                       connector->mode_str, connector->id, connector->crtc);
+
+                       ret = drmModeSetCrtc(disp->fd, connector->crtc, buf_kms->fb_id,
+                                       x, 0, &connector->id, 1, connector->mode);
+
+                       x += connector->mode->hdisplay;
+               } else {
+                       ret = drmModePageFlip(disp->fd, connector->crtc, buf_kms->fb_id,
+                                       DRM_MODE_PAGE_FLIP_EVENT, disp);
+                       disp_kms->scheduled_flips++;
+               }
+
+               if (ret) {
+                       ERROR("Could not post buffer on crtc %d: %s (%d)",
+                                       connector->crtc, strerror(errno), ret);
+                       last_err = ret;
+                       /* well, keep trying the reset of the connectors.. */
+               }
+       }
+
+       /* if we flipped, wait for all flips to complete! */
+       while (disp_kms->scheduled_flips > disp_kms->completed_flips) {
+               drmEventContext evctx = {
+                               .version = DRM_EVENT_CONTEXT_VERSION,
+                               .page_flip_handler = page_flip_handler,
+               };
+               struct timeval timeout = {
+                               .tv_sec = 3,
+                               .tv_usec = 0,
+               };
+               fd_set fds;
+
+               FD_ZERO(&fds);
+               FD_SET(disp->fd, &fds);
+
+               ret = select(disp->fd + 1, &fds, NULL, NULL, &timeout);
+               if (ret <= 0) {
+                       if (errno == EAGAIN) {
+                               continue;    /* keep going */
+                       } else {
+                               ERROR("Timeout waiting for flip complete: %s (%d)",
+                                               strerror(errno), ret);
+                               last_err = ret;
+                               break;
+                       }
+               }
+
+               drmHandleEvent(disp->fd, &evctx);
+       }
+
+       disp_kms->current = buf;
+
+       return last_err;
+}
+
+
+static void
+connector_find_mode(struct display *disp, struct connector *c)
+{
+       struct display_kms *disp_kms = to_display_kms(disp);
+       drmModeConnector *connector;
+       int i, j;
+
+       /* First, find the connector & mode */
+       c->mode = NULL;
+       for (i = 0; i < disp_kms->resources->count_connectors; i++) {
+               connector = drmModeGetConnector(disp->fd,
+                               disp_kms->resources->connectors[i]);
+
+               if (!connector) {
+                       ERROR("could not get connector %i: %s",
+                                       disp_kms->resources->connectors[i], strerror(errno));
+                       drmModeFreeConnector(connector);
+                       continue;
+               }
+
+               if (!connector->count_modes) {
+                       drmModeFreeConnector(connector);
+                       continue;
+               }
+
+               if (connector->connector_id != c->id) {
+                       drmModeFreeConnector(connector);
+                       continue;
+               }
+
+               for (j = 0; j < connector->count_modes; j++) {
+                       c->mode = &connector->modes[j];
+                       if (!strcmp(c->mode->name, c->mode_str))
+                               break;
+               }
+
+               /* Found it, break out */
+               if (c->mode)
+                       break;
+
+               drmModeFreeConnector(connector);
+       }
+
+       if (!c->mode) {
+               ERROR("failed to find mode \"%s\"", c->mode_str);
+               return;
+       }
+
+       /* Now get the encoder */
+       for (i = 0; i < disp_kms->resources->count_encoders; i++) {
+               c->encoder = drmModeGetEncoder(disp->fd,
+                               disp_kms->resources->encoders[i]);
+
+               if (!c->encoder) {
+                       ERROR("could not get encoder %i: %s",
+                                       disp_kms->resources->encoders[i], strerror(errno));
+                       drmModeFreeEncoder(c->encoder);
+                       continue;
+               }
+
+               if (c->encoder->encoder_id  == connector->encoder_id)
+                       break;
+
+               drmModeFreeEncoder(c->encoder);
+       }
+
+       if (c->crtc == -1)
+               c->crtc = c->encoder->crtc_id;
+}
+
+void
+disp_kms_usage(void)
+{
+       MSG("KMS Display Options:");
+       MSG("\t-t <tiled-mode>\t8, 16, or 32");
+       MSG("\t-s <connector_id>:<mode>\tset a mode");
+       MSG("\t-s <connector_id>@<crtc_id>:<mode>\tset a mode");
+}
+
+struct display *
+disp_kms_open(int argc, char **argv)
+{
+       struct display_kms *disp_kms = NULL;
+       struct display *disp;
+       int i;
+
+       disp_kms = calloc(1, sizeof(*disp_kms));
+       if (!disp_kms) {
+               ERROR("allocation failed");
+               goto fail;
+       }
+       disp = &disp_kms->base;
+
+       disp->fd = drmOpen("omapdrm", NULL);
+       if (disp->fd < 0) {
+               ERROR("could not open drm device: %s (%d)", strerror(errno), errno);
+               goto fail;
+       }
+
+       disp->dev = omap_device_new(disp->fd);
+       if (!disp->dev) {
+               ERROR("couldn't create device");
+               goto fail;
+       }
+
+       disp->get_buffers = get_buffers;
+       disp->get_vid_buffers = get_vid_buffers;
+       disp->post_buffer = post_buffer;
+
+       disp_kms->resources = drmModeGetResources(disp->fd);
+       if (!disp_kms->resources) {
+               ERROR("drmModeGetResources failed: %s", strerror(errno));
+               goto fail;
+       }
+
+       /* note: set args to NULL after we've parsed them so other modules know
+        * that it is already parsed (since the arg parsing is decentralized)
+        */
+       for (i = 1; i < argc; i++) {
+               if (!argv[i]) {
+                       continue;
+               }
+               if (!strcmp("-t", argv[i])) {
+                       int n;
+                       argv[i++] = NULL;
+                       if (sscanf(argv[i], "%d", &n) != 1) {
+                               ERROR("invalid arg: %s", argv[i]);
+                               goto fail;
+                       }
+
+                       disp_kms->bo_flags &= ~OMAP_BO_TILED;
+
+                       if (n == 8) {
+                               disp_kms->bo_flags |= OMAP_BO_TILED_8;
+                       } else if (n == 16) {
+                               disp_kms->bo_flags |= OMAP_BO_TILED_16;
+                       } else if (n == 32) {
+                               disp_kms->bo_flags |= OMAP_BO_TILED_32;
+                       } else {
+                               ERROR("invalid arg: %s", argv[i]);
+                               goto fail;
+                       }
+               } else if (!strcmp("-s", argv[i])) {
+                       struct connector *connector =
+                                       &disp_kms->connector[disp_kms->connectors_count++];
+                       connector->crtc = -1;
+                       argv[i++] = NULL;
+                       if (sscanf(argv[i], "%d:%64s",
+                                  &connector->id,
+                                  connector->mode_str) != 2 &&
+                           sscanf(argv[i], "%d@%d:%64s",
+                                  &connector->id,
+                                  &connector->crtc,
+                                  connector->mode_str) != 3) {
+                               // TODO: we could support connector specified as a name too, I suppose
+                               ERROR("invalid arg: %s", argv[i]);
+                               goto fail;
+                       }
+               } else {
+                       /* ignore */
+                       continue;
+               }
+               argv[i] = NULL;
+       }
+
+       disp->width = 0;
+       disp->height = 0;
+       for (i = 0; i < disp_kms->connectors_count; i++) {
+               struct connector *c = &disp_kms->connector[i];
+               connector_find_mode(disp, c);
+               if (c->mode == NULL)
+                       continue;
+               /* setup side-by-side virtual display */
+               disp->width += c->mode->hdisplay;
+               if (disp->height < c->mode->vdisplay) {
+                       disp->height = c->mode->vdisplay;
+               }
+       }
+
+       MSG("using %d connectors, %dx%d display",
+                       disp_kms->connectors_count, disp->width, disp->height);
+
+       return disp;
+
+fail:
+       // XXX cleanup
+       return NULL;
+}
diff --git a/util/display-x11.c b/util/display-x11.c
new file mode 100644 (file)
index 0000000..919456c
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob.clark@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "util.h"
+
+
+void
+disp_x11_usage(void)
+{
+       MSG("X11 Display Options:");
+//     MSG("\t-t <tiled-mode>\t8, 16, or 32");
+//     MSG("\t-s <connector_id>:<mode>\tset a mode");
+//     MSG("\t-s <connector_id>@<crtc_id>:<mode>\tset a mode");
+}
+
+struct display *
+disp_x11_open(int argc, char **argv)
+{
+       ERROR("unimplemented");
+       return NULL;
+}
diff --git a/util/util.c b/util/util.c
new file mode 100644 (file)
index 0000000..ab12a68
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob.clark@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "util.h"
+
+#include <drm.h>
+
+void disp_kms_usage(void);
+struct display * disp_kms_open(int argc, char **argv);
+
+#ifdef HAVE_X11
+void disp_x11_usage(void);
+struct display * disp_x11_open(int argc, char **argv);
+#endif
+
+void
+disp_usage(void)
+{
+#ifdef HAVE_X11
+       disp_x11_usage();
+#endif
+       disp_kms_usage();
+}
+
+struct display *
+disp_open(int argc, char **argv)
+{
+       struct display *disp;
+
+#ifdef HAVE_X11
+       disp = disp_x11_open(argc, argv);
+       if (disp)
+               return disp;
+#endif
+
+       disp = disp_kms_open(argc, argv);
+
+       if (!disp) {
+               ERROR("unable to create display");
+       }
+
+       return disp;
+}
+
+int
+check_args(int argc, char **argv)
+{
+       int i;
+       for (i = 1; i < argc; i++) {
+               if (argv[i]) {
+                       ERROR("invalid arg: %s", argv[i]);
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+/* stolen from modetest.c */
+static void
+fillRGB4(char *virtual, int n, int width, int height, int stride)
+{
+       int i, j;
+       /* paint the buffer with colored tiles */
+       for (j = 0; j < height; j++) {
+               uint32_t *fb_ptr = (uint32_t*)((char*)virtual + j * stride);
+               for (i = 0; i < width; i++) {
+                       div_t d = div(n+i+j, width);
+                       fb_ptr[i] =
+                                       0x00130502 * (d.quot >> 6) +
+                                       0x000a1120 * (d.rem >> 6);
+               }
+       }
+}
+
+
+/* swap these for big endian.. */
+#define RED   2
+#define GREEN 1
+#define BLUE  0
+
+static void
+fill420(unsigned char *y, unsigned char *u, unsigned char *v,
+               int cs /*chroma pixel stride */,
+               int n, int width, int height, int stride)
+{
+       int i, j;
+
+       /* paint the buffer with colored tiles, in blocks of 2x2 */
+       for (j = 0; j < height; j+=2) {
+               unsigned char *y1p = y + j * stride;
+               unsigned char *y2p = y1p + stride;
+               unsigned char *up = u + (j/2) * stride * cs / 2;
+               unsigned char *vp = v + (j/2) * stride * cs / 2;
+
+               for (i = 0; i < width; i+=2) {
+                       div_t d = div(n+i+j, width);
+                       uint32_t rgb = 0x00130502 * (d.quot >> 6) + 0x000a1120 * (d.rem >> 6);
+                       unsigned char *rgbp = (unsigned char *)&rgb;
+                       unsigned char y = (0.299 * rgbp[RED]) + (0.587 * rgbp[GREEN]) + (0.114 * rgbp[BLUE]);
+
+                       *(y2p++) = *(y1p++) = y;
+                       *(y2p++) = *(y1p++) = y;
+
+                       *up = (rgbp[BLUE] - y) * 0.565 + 128;
+                       *vp = (rgbp[RED] - y) * 0.713 + 128;
+                       up += cs;
+                       vp += cs;
+               }
+       }
+}
+
+static void
+fill422(unsigned char *virtual, int n, int width, int height, int stride)
+{
+       int i, j;
+       /* paint the buffer with colored tiles */
+       for (j = 0; j < height; j++) {
+               uint8_t *ptr = (uint8_t*)((char*)virtual + j * stride);
+               for (i = 0; i < width; i++) {
+                       div_t d = div(n+i+j, width);
+                       uint32_t rgb = 0x00130502 * (d.quot >> 6) + 0x000a1120 * (d.rem >> 6);
+                       unsigned char *rgbp = (unsigned char *)&rgb;
+                       unsigned char y = (0.299 * rgbp[RED]) + (0.587 * rgbp[GREEN]) + (0.114 * rgbp[BLUE]);
+
+                       *(ptr++) = y;
+                       *(ptr++) = (rgbp[BLUE] - y) * 0.565 + 128;
+                       *(ptr++) = y;
+                       *(ptr++) = (rgbp[RED] - y) * 0.713 + 128;
+               }
+       }
+}
+
+
+void
+fill(struct buffer *buf, int i)
+{
+       void *ptr;
+
+       omap_bo_cpu_prep(buf->bo, OMAP_GEM_WRITE);
+       ptr = omap_bo_map(buf->bo);
+
+       switch(buf->fourcc) {
+       case 0: {
+               fillRGB4(ptr, i, buf->width, buf->height, buf->stride);
+               break;
+       }
+       case FOURCC('Y','U','Y','V'): {
+               fill422(ptr, i, buf->width, buf->height, buf->stride);
+               break;
+       }
+       case FOURCC('N','V','1','2'): {
+               unsigned char *y = ptr;
+               unsigned char *u = y + (buf->width * buf->stride);
+               unsigned char *v = u + 1;
+               fill420(y, u, v, 2, i, buf->width, buf->height, buf->stride);
+               break;
+       }
+       case FOURCC('I','4','2','0'): {
+               unsigned char *y = ptr;
+               unsigned char *u = y + (buf->width * buf->stride);
+               unsigned char *v = u + (buf->width * buf->stride) / 4;
+               fill420(y, u, v, 1, i, buf->width, buf->height, buf->stride);
+               break;
+       }
+       default:
+               ERROR("invalid format: 0x%08x", buf->fourcc);
+               break;
+       }
+
+       omap_bo_cpu_fini(buf->bo, OMAP_GEM_WRITE);
+}
diff --git a/util/util.h b/util/util.h
new file mode 100644 (file)
index 0000000..1e64b9c
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob.clark@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UTIL_H_
+#define UTIL_H_
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <omap_drm.h>
+#include <omap_drmif.h>
+
+
+/* Display Interface:
+ *
+ * Could be either KMS or X11 depending on build and
+ * environment.  Some of details of opening/connecting, allocating buffers,
+ * etc, differ.  The intention is just to provide as simple as possible
+ * abstraction to avoid lots of duplication in each test app to handle
+ * both cases.
+ */
+
+struct buffer {
+       uint32_t fourcc, width, height, stride;
+       struct omap_bo *bo;
+};
+
+struct display {
+       int fd;
+       uint32_t width, height;
+       struct omap_device *dev;
+
+       struct buffer ** (*get_buffers)(struct display *disp, uint32_t n);
+       struct buffer ** (*get_vid_buffers)(struct display *disp,
+                       uint32_t n, uint32_t fourcc, uint32_t w, uint32_t h);
+       int (*post_buffer)(struct display *disp, struct buffer *buf);
+};
+
+/* Print display related help */
+void disp_usage(void);
+
+/* Open display.. X11 or KMS depending on cmdline args, environment,
+ * and build args
+ */
+struct display * disp_open(int argc, char **argv);
+
+/* Get normal RGB/UI buffers (ie. not scaled, not YUV) */
+static inline struct buffer **
+disp_get_buffers(struct display *disp, uint32_t n)
+{
+       return disp->get_buffers(disp, n);
+}
+
+/* Get video/overlay buffers (ie. can be YUV, scaled, etc) */
+static inline struct buffer **
+disp_get_vid_buffers(struct display *disp, uint32_t n,
+               uint32_t fourcc, uint32_t w, uint32_t h)
+{
+       return disp->get_vid_buffers(disp, n, fourcc, w, h);
+}
+
+/* flip to / post the specified buffer */
+static inline int
+disp_post_buffer(struct display *disp, struct buffer *buf)
+{
+       return disp->post_buffer(disp, buf);
+}
+
+/* V4L2 utilities:
+ */
+
+struct v4l2;
+
+/* Print v4l2 related help */
+void v4l2_usage(void);
+
+/* Open v4l2 (and media0??) XXX */
+struct v4l2 * v4l2_open(int argc, char **argv);
+
+/* Share the buffers w/ v4l2 via dmabuf */
+int v4l2_reqbufs(struct v4l2 *v4l2, struct buffer **bufs, uint32_t n);
+
+int v4l2_streamon(struct v4l2 *v4l2);
+int v4l2_streamoff(struct v4l2 *v4l2);
+
+/* Queue a buffer to the camera */
+int v4l2_qbuf(struct v4l2 *v4l2, struct buffer *buf);
+
+/* Dequeue buffer from camera */
+struct buffer * v4l2_dqbuf(struct v4l2 *v4l2);
+
+/* Other utilities..
+ */
+
+int check_args(int argc, char **argv);
+
+void fill(struct buffer *buf, int i);
+
+#define FOURCC(a, b, c, d) ((uint32_t)(uint8_t)(a) | ((uint32_t)(uint8_t)(b) << 8) | ((uint32_t)(uint8_t)(c) << 16) | ((uint32_t)(uint8_t)(d) << 24 ))
+#define FOURCC_STR(str)    FOURCC(str[0], str[1], str[2], str[3])
+
+#define MSG(fmt, ...) \
+               do { fprintf(stderr, fmt "\n", ##__VA_ARGS__); } while (0)
+#define ERROR(fmt, ...) \
+               do { fprintf(stderr, "ERROR:%s:%d: " fmt "\n", __func__, __LINE__, ##__VA_ARGS__); } while (0)
+
+#ifndef container_of
+#define container_of(ptr, type, member) \
+    (type *)((char *)(ptr) - (char *) &((type *)0)->member)
+#endif
+
+
+#endif /* UTIL_H_ */
diff --git a/util/v4l2.c b/util/v4l2.c
new file mode 100644 (file)
index 0000000..4b008d3
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob.clark@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/videodev2.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include "util.h"
+
+struct v4l2 {
+       int fd;
+       int nbufs;
+       struct v4l2_buffer *v4l2bufs;
+       struct buffer **bufs;
+};
+
+void
+v4l2_usage(void)
+{
+       MSG("V4L2 Capture Options:");
+       MSG("\t-c WxH@fourcc\tset capture dimensions/format");
+}
+
+/* Open v4l2 (and media0??) XXX */
+struct v4l2 *
+v4l2_open(int argc, char **argv)
+{
+       struct v4l2_format format = {
+                       .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+       };
+       struct v4l2 *v4l2;
+       int i, ret;
+
+       v4l2 = calloc(1, sizeof(*v4l2));
+       v4l2->fd = open("/dev/video0", O_RDWR);
+
+       ret = ioctl(v4l2->fd, VIDIOC_G_FMT, &format);
+       if (ret < 0) {
+               ERROR("VIDIOC_G_FMT failed: %s (%d)", strerror(errno), ret);
+               goto fail;
+       }
+
+       /* note: set args to NULL after we've parsed them so other modules know
+        * that it is already parsed (since the arg parsing is decentralized)
+        */
+       for (i = 1; i < argc; i++) {
+               if (!argv[i]) {
+                       continue;
+               }
+               if (!strcmp("-c", argv[i])) {
+                       char fourccstr[5];
+                       argv[i++] = NULL;
+                       if (sscanf(argv[i], "%ux%u@%4s",
+                                       &format.fmt.pix.width,
+                                       &format.fmt.pix.height,
+                                       fourccstr) != 3) {
+                               ERROR("invalid arg: %s", argv[i]);
+                               goto fail;
+                       }
+                       format.fmt.pix.pixelformat = FOURCC_STR(fourccstr);
+               } else {
+                       continue;
+               }
+               argv[i] = NULL;
+       }
+
+       if ((format.fmt.pix.width == 0) ||
+                       (format.fmt.pix.height == 0) ||
+                       (format.fmt.pix.pixelformat == 0)) {
+               ERROR("invalid capture settings '%dx%d@%4s' (did you not use '-c'?)",
+                               format.fmt.pix.width, format.fmt.pix.height,
+                               (char *)&format.fmt.pix.pixelformat);
+               goto fail;
+       }
+
+       ret = ioctl(v4l2->fd, VIDIOC_S_FMT, &format);
+       if (ret < 0) {
+               ERROR("VIDIOC_S_FMT failed: %s (%d)", strerror(errno), ret);
+               goto fail;
+       }
+
+       return v4l2;
+
+fail:
+       // XXX cleanup
+       return NULL;
+}
+
+int
+v4l2_reqbufs(struct v4l2 *v4l2, struct buffer **bufs, uint32_t n)
+{
+       struct v4l2_requestbuffers reqbuf = {
+                       .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+                       .memory = V4L2_MEMORY_DMABUF,
+                       .count = n,
+       };
+       uint32_t i;
+       int ret;
+
+       if (v4l2->v4l2bufs) {
+               // maybe eventually need to support this?
+               ERROR("already reqbuf'd");
+               return -1;
+       }
+
+       ret = ioctl(v4l2->fd, VIDIOC_REQBUFS, &reqbuf);
+       if (ret < 0) {
+               ERROR("VIDIOC_REQBUFS failed: %s (%d)", strerror(errno), ret);
+               return ret;
+       }
+
+       if ((reqbuf.count != n) ||
+                       (reqbuf.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+                       (reqbuf.memory != V4L2_MEMORY_DMABUF)) {
+               ERROR("unsupported..");
+               return -1;
+       }
+
+       v4l2->nbufs = reqbuf.count;
+       v4l2->v4l2bufs = calloc(v4l2->nbufs, sizeof(*v4l2->v4l2bufs));
+       if (!v4l2->v4l2bufs) {
+               ERROR("allocation failed");
+               return -1;
+       }
+
+       for (i = 0; i < reqbuf.count; i++) {
+               v4l2->v4l2bufs[i] = (struct v4l2_buffer){
+                       .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+                                       .memory = V4L2_MEMORY_DMABUF,
+                                       .index = i,
+                                       .m.fd = omap_bo_dmabuf(bufs[i]->bo),
+               };
+               ret = ioctl(v4l2->fd, VIDIOC_QUERYBUF, &v4l2->v4l2bufs[i]);
+               v4l2->v4l2bufs[i].m.fd = omap_bo_dmabuf(bufs[i]->bo);
+               if (ret) {
+                       ERROR("VIDIOC_QUERYBUF failed: %s (%d)", strerror(errno), ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+int
+v4l2_streamon(struct v4l2 *v4l2)
+{
+       enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       int ret;
+
+    ret = ioctl(v4l2->fd, VIDIOC_STREAMON, &type);
+
+    if (ret) {
+               ERROR("VIDIOC_STREAMON failed: %s (%d)", strerror(errno), ret);
+    }
+
+    return ret;
+}
+
+int
+v4l2_streamoff(struct v4l2 *v4l2)
+{
+       enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       int ret;
+
+    ret = ioctl(v4l2->fd, VIDIOC_STREAMOFF, &type);
+
+    if (ret) {
+               ERROR("VIDIOC_STREAMOFF failed: %s (%d)", strerror(errno), ret);
+    }
+
+    return ret;
+}
+
+int
+v4l2_qbuf(struct v4l2 *v4l2, struct buffer *buf)
+{
+       struct v4l2_buffer *v4l2buf = NULL;
+       int i, ret, fd;
+
+       fd = omap_bo_dmabuf(buf->bo);
+
+       for (i = 0; i < v4l2->nbufs; i++) {
+               if (v4l2->v4l2bufs[i].m.fd == fd) {
+                       v4l2buf = &v4l2->v4l2bufs[i];
+               }
+       }
+
+       if (!v4l2buf) {
+               ERROR("invalid buffer");
+               return -1;
+       }
+
+       MSG("QBUF: idx=%d, fd=%d", v4l2buf->index, v4l2buf->m.fd);
+
+       ret = ioctl(v4l2->fd, VIDIOC_QBUF, v4l2buf);
+       if (ret) {
+               ERROR("VIDIOC_QBUF failed: %s (%d)", strerror(errno), ret);
+       }
+
+       return ret;
+}
+
+struct buffer *
+v4l2_dqbuf(struct v4l2 *v4l2)
+{
+       struct buffer *buf;
+       struct v4l2_buffer v4l2buf = {
+                       .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+                       .memory = V4L2_MEMORY_DMABUF,
+       };
+       int ret;
+
+       ret = ioctl(v4l2->fd, VIDIOC_DQBUF, &v4l2buf);
+       if (ret) {
+               ERROR("VIDIOC_DQBUF failed: %s (%d)", strerror(errno), ret);
+       }
+
+       MSG("DQBUF: idx=%d, fd=%d", v4l2buf.index, v4l2buf.m.fd);
+
+       buf = v4l2->bufs[v4l2buf.index];
+
+       if (omap_bo_dmabuf(buf->bo) != v4l2buf.m.fd) {
+               MSG("WARNING: camera gave us incorrect buffer: %d vs %d",
+                               omap_bo_dmabuf(buf->bo), v4l2buf.m.fd);
+       }
+
+       return buf;
+}