Hi Everyone,
This email contains a test application showing DMABUF sharing
between DRM/KMS display and camera capture node. It show simple
camera preview on LCD display. The similar application showing
DMABUF sharing between two V4L devices is available at link:

http://thread.gmane.org/gmane.linux.drivers.video-input-infrastructure/43793/focus=43952

The program is written in C99 and it was tested using Exynos/DRM
and FIMC capture for M5MOLS and S5K6AAFX sensors on UniversalC210 board.

This application shows how buffer sharing between V4L2/DRM may look like.
Please let me know if/where I use DRM/V4L2 incorrectly.

The application was tested against 3.3-rc5 kernel with patches:
http://thread.gmane.org/gmane.linux.kernel.cross-arch/12819
        [redesign of DMA mapping]
http://thread.gmane.org/gmane.linux.drivers.video-input-infrastructure/43793/focus=43803
        [support for dma_get_pages, PoC generic API for transforming DMA object 
into list of pages]
http://thread.gmane.org/gmane.comp.video.dri.devel/65583/focus=65703
        [DRM prime support]
http://git.infradead.org/users/kmpark/linux-samsung/shortlog/refs/heads/exynos-drm-dmabuf
        [DRM prime support for Exynos DRM]
http://thread.gmane.org/gmane.comp.video.dri.devel/65992
        [fix to DRM prime in Exynos DRM]
http://thread.gmane.org/gmane.linux.drivers.video-input-infrastructure/42966/focus=42968
        [support for DMABUF importing in V4L2]
http://thread.gmane.org/gmane.linux.drivers.video-input-infrastructure/45394
        [integrate V4L2 with DMABUF]

Regards,
Tomasz Stanislawski

---
#include <errno.h>
#include <fcntl.h>
#include <linux/videodev2.h>
#include <math.h>
#include <poll.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <xf86drm.h>
#include <xf86drmMode.h>
#include <exynos_drm.h>

#define ERRSTR strerror(errno)

#define BYE_ON(cond, ...) \
do { \
        if (cond) { \
                int errsv = errno; \
                fprintf(stderr, "ERROR(%s:%d) : ", \
                        __FILE__, __LINE__); \
                errno = errsv; \
                fprintf(stderr,  __VA_ARGS__); \
                abort(); \
        } \
} while(0)

static inline int warn(const char *file, int line, const char *fmt, ...)
{
        int errsv = errno;
        va_list va;
        va_start(va, fmt);
        fprintf(stderr, "WARN(%s:%d): ", file, line);
        vfprintf(stderr, fmt, va);
        va_end(va);
        errno = errsv;
        return 1;
}

#define WARN_ON(cond, ...) \
        ((cond) ? warn(__FILE__, __LINE__, __VA_ARGS__) : 0)

struct setup {
        char module[32];
        uint32_t conId;
        uint32_t crtId;
        char modestr[32];
        char video[32];
        unsigned int w, h;
        unsigned int use_wh : 1;
        unsigned int in_fourcc;
        unsigned int out_fourcc;
        unsigned int buffer_count;
        unsigned int use_crop : 1;
        unsigned int use_compose : 1;
        struct v4l2_rect crop;
        struct v4l2_rect compose;
};

struct buffer {
        unsigned int bo_handle;
        unsigned int fb_handle;
        int dbuf_fd;
};

struct stream {
        int v4lfd;
        int current_buffer;
        int buffer_count;
        struct buffer *buffer;
} stream;

static void usage(char *name)
{
        fprintf(stderr, "usage: %s [-Moisth]\n", name);
        fprintf(stderr, "\t-M <drm-module>\tset DRM module\n");
        fprintf(stderr, "\t-o <connector_id>:<crtc_id>:<mode>\tset a mode\n");
        fprintf(stderr, "\t-i <video-node>\tset video node like /dev/video*\n");
        fprintf(stderr, "\t-S <width,height>\tset input resolution\n");
        fprintf(stderr, "\t-f <fourcc>\tset input format using 4cc\n");
        fprintf(stderr, "\t-F <fourcc>\tset output format using 4cc\n");
        fprintf(stderr, "\t-s <width,height>@<left,top>\tset crop area\n");
        fprintf(stderr, "\t-t <width,height>@<left,top>\tset compose area\n");
        fprintf(stderr, "\t-b buffer_count\tset number of buffers\n");
        fprintf(stderr, "\t-h\tshow this help\n");
        fprintf(stderr, "\n\tDefault is to dump all info.\n");
}

static inline int parse_rect(char *s, struct v4l2_rect *r)
{
        return sscanf(s, "%d,%d@%d,%d", &r->width, &r->height,
                &r->top, &r->left) != 4;
}

static int parse_args(int argc, char *argv[], struct setup *s)
{
        if (argc <= 1)
                usage(argv[0]);

        int c, ret;
        memset(s, 0, sizeof(*s));

        while ((c = getopt(argc, argv, "M:o:i:S:f:F:s:t:b:h")) != -1) {
                switch (c) {
                case 'M':
                        strncpy(s->module, optarg, 31);
                        break;
                case 'o':
                        ret = sscanf(optarg, "%u:%u:%31s", &s->conId, &s->crtId,
                                s->modestr);
                        if (WARN_ON(ret != 3, "incorrect mode description\n"))
                                return -1;
                        break;
                case 'i':
                        strncpy(s->video, optarg, 31);
                        break;
                case 'S':
                        ret = sscanf(optarg, "%u,%u", &s->w, &s->h);
                        if (WARN_ON(ret != 2, "incorrect input size\n"))
                                return -1;
                        s->use_wh = 1;
                        break;
                case 'f':
                        if (WARN_ON(strlen(optarg) != 4, "invalid fourcc\n"))
                                return -1;
                        s->in_fourcc = ((unsigned)optarg[0] << 0) |
                                ((unsigned)optarg[1] << 8) |
                                ((unsigned)optarg[2] << 16) |
                                ((unsigned)optarg[3] << 24);
                        break;
                case 'F':
                        if (WARN_ON(strlen(optarg) != 4, "invalid fourcc\n"))
                                return -1;
                        s->out_fourcc = ((unsigned)optarg[0] << 0) |
                                ((unsigned)optarg[1] << 8) |
                                ((unsigned)optarg[2] << 16) |
                                ((unsigned)optarg[3] << 24);
                        break;
                case 's':
                        ret = parse_rect(optarg, &s->crop);
                        if (WARN_ON(ret, "incorrect crop area\n"))
                                return -1;
                        s->use_crop = 1;
                        break;
                case 't':
                        ret = parse_rect(optarg, &s->compose);
                        if (WARN_ON(ret, "incorrect compose area\n"))
                                return -1;
                        s->use_compose = 1;
                        break;
                case 'b':
                        ret = sscanf(optarg, "%u", &s->buffer_count);
                        if (WARN_ON(ret != 1, "incorrect buffer count\n"))
                                return -1;
                        break;
                case '?':
                case 'h':
                        usage(argv[0]);
                        return -1;
                }
        }

        return 0;
}

static int buffer_create(struct buffer *b, int drmfd, struct setup *s,
        uint64_t size, uint32_t pitch)
{
        int ret = strncmp(s->module, "exynos", 6);
        if (WARN_ON(ret, "drm: only exynos GEM is supported\n"))
                return -1;

        struct drm_exynos_gem_create gem;
        struct drm_gem_close gem_close;

        memset(&gem, 0, sizeof gem);
        gem.size = size;
        ret = ioctl(drmfd, DRM_IOCTL_EXYNOS_GEM_CREATE, &gem);
        if (WARN_ON(ret, "EXYNOS_GEM_CREATE failed: %s\n", ERRSTR))
                return -1;
        b->bo_handle = gem.handle;

        struct drm_prime_handle prime;
        memset(&prime, 0, sizeof prime);
        prime.handle = b->bo_handle;

        ret = ioctl(drmfd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &prime);
        if (WARN_ON(ret, "PRIME_HANDLE_TO_FD failed: %s\n", ERRSTR))
                goto fail_gem;
        printf("dbuf_fd = %d\n", prime.fd);
        b->dbuf_fd = prime.fd;

        uint32_t offsets[4] = { 0 };
        uint32_t pitches[4] = { pitch };
        uint32_t bo_handles[4] = { b->bo_handle };
        unsigned int fourcc = s->out_fourcc;
        if (!fourcc)
                fourcc = s->in_fourcc;
        ret = drmModeAddFB2(drmfd, s->w, s->h, fourcc, bo_handles,
                pitches, offsets, &b->fb_handle, 0);
        if (WARN_ON(ret, "drmModeAddFB2 failed: %s\n", ERRSTR))
                goto fail_prime;

        return 0;

fail_prime:
        close(b->dbuf_fd);

fail_gem:
        memset(&gem_close, 0, sizeof gem_close);
        gem_close.handle = b->bo_handle,
        ret = ioctl(drmfd, DRM_IOCTL_GEM_CLOSE, gem_close);
        WARN_ON(ret, "GEM_CLOSE failed: %s\n", ERRSTR);

        return -1;
}

static int find_mode(drmModeModeInfo *m, int drmfd, struct setup *s,
        uint32_t *con)
{
        int ret = -1;
        drmModeRes *res = drmModeGetResources(drmfd);
        if (WARN_ON(!res, "drmModeGetResources failed: %s\n", ERRSTR))
                return -1;

        if (WARN_ON(res->count_crtcs <= 0, "drm: no crts\n"))
                goto fail_res;

        if (WARN_ON(res->count_connectors <= 0, "drm: no connectors\n"))
                goto fail_res;

        if (WARN_ON(s->conId >= res->count_connectors, "connector %d "
                "is not supported\n", s->conId))
                goto fail_res;

        drmModeConnector *c;
        c = drmModeGetConnector(drmfd, res->connectors[s->conId]);

        if (WARN_ON(!c, "drmModeGetConnector failed: %s\n", ERRSTR))
                goto fail_res;

        if (WARN_ON(!c->count_modes, "connector supports no mode\n"))
                goto fail_conn;

        drmModeModeInfo *found = NULL;
        for (int i = 0; i < c->count_modes; ++i)
                if (strcmp(c->modes[i].name, s->modestr) == 0)
                        found = &c->modes[i];

        if (WARN_ON(!found, "mode %s not supported\n", s->modestr)) {
                fprintf(stderr, "Valid modes:");
                for (int i = 0; i < c->count_modes; ++i)
                        fprintf(stderr, " %s", c->modes[i].name);
                fprintf(stderr, "\n");
                goto fail_conn;
        }

        memcpy(m, found, sizeof *found);
        if (con)
                *con = c->connector_id;
        ret = 0;

fail_conn:
        drmModeFreeConnector(c);

fail_res:
        drmModeFreeResources(res);

        return ret;
}

static void page_flip_handler(int fd, unsigned int frame,
        unsigned int sec, unsigned int usec, void *data)
{
        int index = stream.current_buffer;
        struct v4l2_buffer buf;
        struct v4l2_plane plane;
        int ret;

        stream.current_buffer = (int)data;
        if (index < 0)
                return;

        memset(&buf, 0, sizeof buf);
        memset(&plane, 0, sizeof plane);
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
        buf.memory = V4L2_MEMORY_DMABUF;
        buf.index = index;
        buf.m.planes = &plane;
        buf.length = 1;
        plane.m.fd = stream.buffer[index].dbuf_fd;

        ret = ioctl(stream.v4lfd, VIDIOC_QBUF, &buf);
        BYE_ON(ret, "VIDIOC_QBUF(index = %d) failed: %s\n", index, ERRSTR);
}

int main(int argc, char *argv[])
{
        int ret;
        struct setup s;

        ret = parse_args(argc, argv, &s);
        BYE_ON(ret, "failed to parse arguments\n");
        BYE_ON(s.module[0] == 0, "DRM module is missing\n");
        BYE_ON(s.video[0] == 0, "video node is missing\n");

        int drmfd = drmOpen(s.module, NULL);
        BYE_ON(drmfd < 0, "drmOpen(%s) failed: %s\n", s.module, ERRSTR);

        int v4lfd = open(s.video, O_RDWR);
        BYE_ON(v4lfd < 0, "failed to open %s: %s\n", s.video, ERRSTR);

        struct v4l2_capability caps;
        memset(&caps, 0, sizeof caps);

        ret = ioctl(v4lfd, VIDIOC_QUERYCAP, &caps);
        BYE_ON(ret, "VIDIOC_QUERYCAP failed: %s\n", ERRSTR);

        /* TODO: add single plane support */
        BYE_ON(~caps.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE,
                "video: multiplanar capture is not supported\n");

        struct v4l2_format fmt;
        memset(&fmt, 0, sizeof fmt);
        fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;

        ret = ioctl(v4lfd, VIDIOC_G_FMT, &fmt);
        BYE_ON(ret < 0, "VIDIOC_G_FMT failed: %s\n", ERRSTR);
        printf("G_FMT(start): width = %u, height = %u, 4cc = %.4s\n",
                fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height,
                (char*)&fmt.fmt.pix_mp.pixelformat);

        if (s.use_wh) {
                fmt.fmt.pix_mp.width = s.w;
                fmt.fmt.pix_mp.height = s.h;
        }
        if (s.in_fourcc)
                fmt.fmt.pix_mp.pixelformat = s.in_fourcc;

        ret = ioctl(v4lfd, VIDIOC_S_FMT, &fmt);
        BYE_ON(ret < 0, "VIDIOC_S_FMT failed: %s\n", ERRSTR);

        ret = ioctl(v4lfd, VIDIOC_G_FMT, &fmt);
        BYE_ON(ret < 0, "VIDIOC_G_FMT failed: %s\n", ERRSTR);
        printf("G_FMT(final): width = %u, height = %u, 4cc = %.4s\n",
                fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height,
                (char*)&fmt.fmt.pix_mp.pixelformat);

        BYE_ON(fmt.fmt.pix_mp.num_planes > 1,
                "multiplanar formats are not supported\n");

        struct v4l2_requestbuffers rqbufs;
        memset(&rqbufs, 0, sizeof(rqbufs));
        rqbufs.count = s.buffer_count;
        rqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
        rqbufs.memory = V4L2_MEMORY_DMABUF;

        ret = ioctl(v4lfd, VIDIOC_REQBUFS, &rqbufs);
        BYE_ON(ret < 0, "VIDIOC_REQBUFS failed: %s\n", ERRSTR);
        BYE_ON(rqbufs.count < s.buffer_count, "video node allocated only "
                "%u of %u buffers\n", rqbufs.count, s.buffer_count);

        s.in_fourcc = fmt.fmt.pix_mp.pixelformat;
        s.w = fmt.fmt.pix_mp.width;
        s.h = fmt.fmt.pix_mp.height;

        /* TODO: add support for multiplanar formats */
        struct buffer buffer[s.buffer_count];
        uint64_t size = fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
        uint32_t pitch = fmt.fmt.pix_mp.plane_fmt[0].bytesperline;
        printf("size = %llu pitch = %u\n", size, pitch);
        for (int i = 0; i < s.buffer_count; ++i) {
                ret = buffer_create(&buffer[i], drmfd, &s, size, pitch);
                BYE_ON(ret, "failed to create buffer%d\n", i);
        }
        printf("buffers ready\n");

        drmModeModeInfo drmmode;
        uint32_t con;
        ret = find_mode(&drmmode, drmfd, &s, &con);
        BYE_ON(ret, "failed to find valid mode\n");

        ret = drmModeSetCrtc(drmfd, s.crtId, buffer[0].fb_handle, 0, 0,
                &con, 1, &drmmode);
        BYE_ON(ret, "drmModeSetCrtc failed: %s\n", ERRSTR);

        /* enqueueing first buffer to DRM */
        ret = drmModePageFlip(drmfd, s.crtId, buffer[0].fb_handle,
                DRM_MODE_PAGE_FLIP_EVENT, 0);
        BYE_ON(ret, "drmModePageFlip failed: %s\n", ERRSTR);

        for (int i = 1; i < s.buffer_count; ++i) {
                struct v4l2_plane plane;
                memset(&plane, 0, sizeof plane);
                struct v4l2_buffer buf;
                memset(&buf, 0, sizeof buf);

                buf.index = i;
                buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
                buf.memory = V4L2_MEMORY_DMABUF;
                buf.m.planes = &plane;
                buf.length = 1;
                plane.m.fd = buffer[i].dbuf_fd;
                ret = ioctl(v4lfd, VIDIOC_QBUF, &buf);
                BYE_ON(ret < 0, "VIDIOC_QBUF for buffer %d failed: %s\n",
                        buf.index, ERRSTR);
        }

        int type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
        ret = ioctl(v4lfd, VIDIOC_STREAMON, &type);
        BYE_ON(ret < 0, "STREAMON failed: %s\n", ERRSTR);

        struct pollfd fds[] = {
                { .fd = v4lfd, .events = POLLIN },
                { .fd = drmfd, .events = POLLIN },
        };

        /* buffer currently used by drm */
        stream.v4lfd = v4lfd;
        stream.current_buffer = -1;
        stream.buffer = buffer;

        while ((ret = poll(fds, 2, 5000)) > 0) {
                if (fds[0].revents & POLLIN) {
                        struct v4l2_buffer buf;
                        memset(&buf, 0, sizeof buf);
                        /* dequeue buffer */
                        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
                        buf.memory = V4L2_MEMORY_DMABUF;
                        ret = ioctl(v4lfd, VIDIOC_DQBUF, &buf);
                        BYE_ON(ret, "VIDIOC_DQBUF failed: %s\n", ERRSTR);

                        ret = drmModePageFlip(drmfd, s.crtId, 
buffer[buf.index].fb_handle,
                                DRM_MODE_PAGE_FLIP_EVENT, (void*)buf.index);
                        BYE_ON(ret, "drmModePageFlip failed: %s\n", ERRSTR);

                }
                if (fds[1].revents & POLLIN) {
                        drmEventContext evctx;
                        memset(&evctx, 0, sizeof evctx);
                        evctx.version = DRM_EVENT_CONTEXT_VERSION;
                        evctx.page_flip_handler = page_flip_handler;

                        ret = drmHandleEvent(drmfd, &evctx);
                        BYE_ON(ret, "drmHandleEvent failed: %s\n", ERRSTR);
                }
        }

        return 0;
}

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to