Hi Tomasz, How about to add this program to tools directory? tools/drm or tools/media? Thank you, Kyungmin Park On Wed, Apr 11, 2012 at 1:13 AM, Tomasz Stanislawski <t.stanislaws@xxxxxxxxxxx> wrote: > Hi Everyone, > This email contains a test application showing DMABUF sharing > between DRM/KMS display and capture node including VIVI. > It shows a simple preview on LCD display. The similar application > was posted in thread: > http://thread.gmane.org/gmane.comp.video.dri.devel/65997 > > This version makes use of single-plane API for V4L2 capture instead > of multiplanar. This change allows VIVI driver to be tested as > DMABUF importer. > > The program is written in C99 and it was tested using Exynos/DRM > and VIVI capture. > > 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.4-rc1 kernel with patches: > > - Integration of videobuf2 with dmabuf > http://thread.gmane.org/gmane.linux.drivers.video-input-infrastructure/46586 > > - Integration of vb2-vmalloc and VIVI with dmabuf > http://thread.gmane.org/gmane.linux.drivers.video-input-infrastructure/46713 > > - Support for DRM prime for Exynos by Inki Dae > http://git.infradead.org/users/kmpark/linux-2.6-samsung/shortlog/refs/heads/exynos-drm-prime > > - patch fixing kmap/vmap support for EXYNOS-DRM prime. > http://git.infradead.org/users/kmpark/linux-2.6-samsung/commit/3c483f24e418f342eac40dc5fb3991e058deb270 > > > The branch containing all mentioned patches (without platform code) > rebased on 3.4-rc1 is available at link: > > http://git.infradead.org/users/kmpark/linux-2.6-samsung/shortlog/refs/heads/v3.4-rc1-v4l-drm-dmabuf-for-test > > 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; > int ret; > > stream.current_buffer = (int)data; > if (index < 0) > return; > > memset(&buf, 0, sizeof buf); > buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; > buf.memory = V4L2_MEMORY_DMABUF; > buf.index = index; > buf.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, > "video: singleplanar capture is not supported\n"); > > struct v4l2_format fmt; > memset(&fmt, 0, sizeof fmt); > fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; > > 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.width, fmt.fmt.pix.height, > (char*)&fmt.fmt.pix.pixelformat); > > if (s.use_wh) { > fmt.fmt.pix.width = s.w; > fmt.fmt.pix.height = s.h; > } > if (s.in_fourcc) > fmt.fmt.pix.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.width, fmt.fmt.pix.height, > (char*)&fmt.fmt.pix.pixelformat); > > struct v4l2_requestbuffers rqbufs; > memset(&rqbufs, 0, sizeof(rqbufs)); > rqbufs.count = s.buffer_count; > rqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; > 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.pixelformat; > s.w = fmt.fmt.pix.width; > s.h = fmt.fmt.pix.height; > > /* TODO: add support for multiplanar formats */ > struct buffer buffer[s.buffer_count]; > uint64_t size = fmt.fmt.pix.sizeimage; > uint32_t pitch = fmt.fmt.pix.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_buffer buf; > memset(&buf, 0, sizeof buf); > > buf.index = i; > buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; > buf.memory = V4L2_MEMORY_DMABUF; > buf.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; > 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; > 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; > } > > > > > > > > > > > -- > To unsubscribe from this list: send the line "unsubscribe linux-media" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html