Hello everyone, This patchset adds support for DMABUF [2] importing and exporting to V4L2 stack.
v9: - rebase on 3.6 - change type for fs to __s32 - add support for vb2_ioctl_expbuf - remove patch 'v4l: vb2: add support for DMA_ATTR_NO_KERNEL_MAPPING', it will be posted as a separate patch - fix typos and style in Documentation (from Hans Verkuil) - only vb2-core and vb2-dma-contig selects DMA_SHARED_BUFFER in Kconfig - use data_offset and length while queueing DMABUF - return the most recently used fd at VIDIOC_DQBUF - use (buffer-type, index, plane) tuple instead of mem_offset to identify a for exporting (from Hans Verkuil) - support for DMABUF mmap in vb2-dma-contig (from Laurent Pinchart) - add testing alignment of vaddr and size while verifying userptr against DMA capabilities (from Laurent Pinchart) - substitute VM_DONTDUMP with VM_RESERVED - simplify error handling in vb2_dc_get_dmabuf (from Laurent Pinchart)
v8: - rebased on 3.6-rc1 - merged importer and exporter patchsets - fixed missing fields in v4l2_plane32 and v4l2_buffer32 structs - fixed typos/style in documentation - significant reduction of warnings from checkpatch.pl - fixed STREAMOFF issues reported by Dima Zavin [4] by adding __vb2_dqbuf helper to vb2-core - DC fails if userptr is not correctly aligned - add support for DMA attributes in DC - add support for buffers with no kernel mapping - add reference counting on device from allocator context - dummy support for mmap - use dma_get_sgtable, drop vb2_dc_kaddr_to_pages hack and vb2_dc_get_base_sgt helper
v7: - support for V4L2_MEMORY_DMABUF in v4l2-compact-ioctl32.c - cosmetic fixes to the documentation - added importing for vmalloc because vmap support in dmabuf for 3.5 was pull-requested - support for dmabuf importing for VIVI - resurrect allocation of dma-contig context - remove reference of alloc_ctx in dma-contig buffer - use sg_alloc_table_from_pages - fix DMA scatterlist calls to use orig_nents instead of nents - fix memleak in vb2_dc_sgt_foreach_page (use orig_nents instead of nents)
v6: - fixed missing entry in v4l2_memory_names - fixed a bug occuring after get_user_pages failure - fixed a bug caused by using invalid vma for get_user_pages - prepare/finish no longer call dma_sync for dmabuf buffers
v5: - removed change of importer/exporter behaviour - fixes vb2_dc_pages_to_sgt basing on Laurent's hints - changed pin/unpin words to lock/unlock in Doc
v4: - rebased on mainline 3.4-rc2 - included missing importing support for s5p-fimc and s5p-tv - added patch for changing map/unmap for importers - fixes to Documentation part - coding style fixes - pairing {map/unmap}_dmabuf in vb2-core - fixing variable types and semantic of arguments in videobufb2-dma-contig.c
v3: - rebased on mainline 3.4-rc1 - split 'code refactor' patch to multiple smaller patches - squashed fixes to Sumit's patches - patchset is no longer dependant on 'DMA mapping redesign' - separated path for handling IO and non-IO mappings - add documentation for DMABUF importing to V4L - removed all DMABUF exporter related code - removed usage of dma_get_pages extension
v2: - extended VIDIOC_EXPBUF argument from integer memoffset to struct v4l2_exportbuffer - added patch that breaks DMABUF spec on (un)map_atachment callcacks but allows to work with existing implementation of DMABUF prime in DRM - all dma-contig code refactoring patches were squashed - bugfixes
v1: List of changes since [1]. - support for DMA api extension dma_get_pages, the function is used to retrieve pages used to create DMA mapping. - small fixes/code cleanup to videobuf2 - added prepare and finish callbacks to vb2 allocators, it is used keep consistency between dma-cpu acess to the memory (by Marek Szyprowski) - support for exporting of DMABUF buffer in V4L2 and Videobuf2, originated from [3]. - support for dma-buf exporting in vb2-dma-contig allocator - support for DMABUF for s5p-tv and s5p-fimc (capture interface) drivers, originated from [3] - changed handling for userptr buffers (by Marek Szyprowski, Andrzej Pietrasiewicz) - let mmap method to use dma_mmap_writecombine call (by Marek Szyprowski)
[1] http://thread.gmane.org/gmane.linux.drivers.video-input-infrastructure/42966... [2] https://lkml.org/lkml/2011/12/26/29 [3] http://thread.gmane.org/gmane.linux.kernel.cross-arch/12819 [4] http://article.gmane.org/gmane.linux.drivers.video-input-infrastructure/4970...
Laurent Pinchart (2): v4l: vb2-dma-contig: shorten vb2_dma_contig prefix to vb2_dc v4l: vb2-dma-contig: reorder functions
Marek Szyprowski (4): v4l: vb2: add prepare/finish callbacks to allocators v4l: vb2-dma-contig: add prepare/finish to dma-contig allocator v4l: vb2-dma-contig: let mmap method to use dma_mmap_coherent call v4l: vb2-dma-contig: fail if user ptr buffer is not correctly aligned
Sumit Semwal (4): v4l: Add DMABUF as a memory type v4l: vb2: add support for shared buffer (dma_buf) v4l: vb: remove warnings about MEMORY_DMABUF v4l: vb2-dma-contig: add support for dma_buf importing
Tomasz Stanislawski (15): Documentation: media: description of DMABUF importing in V4L2 v4l: vb2-dma-contig: remove reference of alloc_ctx from a buffer v4l: vb2-dma-contig: add support for scatterlist in userptr mode v4l: vb2-vmalloc: add support for dmabuf importing v4l: vivi: support for dmabuf importing v4l: s5p-tv: mixer: support for dmabuf importing v4l: s5p-fimc: support for dmabuf importing Documentation: media: description of DMABUF exporting in V4L2 v4l: add buffer exporting via dmabuf v4l: vb2: add buffer exporting via dmabuf v4l: vb2-dma-contig: add support for DMABUF exporting v4l: vb2-dma-contig: add reference counting for a device from allocator context v4l: s5p-fimc: support for dmabuf exporting v4l: s5p-tv: mixer: support for dmabuf exporting v4l: s5p-mfc: support for dmabuf exporting
Documentation/DocBook/media/v4l/compat.xml | 7 + Documentation/DocBook/media/v4l/io.xml | 183 +++++- Documentation/DocBook/media/v4l/v4l2.xml | 1 + .../DocBook/media/v4l/vidioc-create-bufs.xml | 16 +- Documentation/DocBook/media/v4l/vidioc-expbuf.xml | 212 ++++++ Documentation/DocBook/media/v4l/vidioc-qbuf.xml | 17 + Documentation/DocBook/media/v4l/vidioc-reqbufs.xml | 47 +- drivers/media/video/Kconfig | 2 + drivers/media/video/s5p-fimc/fimc-capture.c | 11 +- drivers/media/video/s5p-mfc/s5p_mfc_dec.c | 14 + drivers/media/video/s5p-mfc/s5p_mfc_enc.c | 14 + drivers/media/video/s5p-tv/mixer_video.c | 12 +- drivers/media/video/v4l2-compat-ioctl32.c | 19 + drivers/media/video/v4l2-dev.c | 1 + drivers/media/video/v4l2-ioctl.c | 11 + drivers/media/video/videobuf-core.c | 4 + drivers/media/video/videobuf2-core.c | 300 ++++++++- drivers/media/video/videobuf2-dma-contig.c | 695 ++++++++++++++++++-- drivers/media/video/videobuf2-memops.c | 40 -- drivers/media/video/videobuf2-vmalloc.c | 56 ++ drivers/media/video/vivi.c | 2 +- include/linux/videodev2.h | 35 + include/media/v4l2-ioctl.h | 2 + include/media/videobuf2-core.h | 38 ++ include/media/videobuf2-memops.h | 5 - 25 files changed, 1608 insertions(+), 136 deletions(-) create mode 100644 Documentation/DocBook/media/v4l/vidioc-expbuf.xml
From: Sumit Semwal sumit.semwal@ti.com
Adds DMABUF memory type to v4l framework. Also adds the related file descriptor in v4l2_plane and v4l2_buffer.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com [original work in the PoC for buffer sharing] Signed-off-by: Sumit Semwal sumit.semwal@ti.com Signed-off-by: Sumit Semwal sumit.semwal@linaro.org Acked-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/media/video/v4l2-compat-ioctl32.c | 18 ++++++++++++++++++ drivers/media/video/v4l2-ioctl.c | 1 + include/linux/videodev2.h | 7 +++++++ 3 files changed, 26 insertions(+)
diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index 9ebd5c5..f0b5aba 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -304,6 +304,7 @@ struct v4l2_plane32 { union { __u32 mem_offset; compat_long_t userptr; + __s32 fd; } m; __u32 data_offset; __u32 reserved[11]; @@ -325,6 +326,7 @@ struct v4l2_buffer32 { __u32 offset; compat_long_t userptr; compat_caddr_t planes; + __s32 fd; } m; __u32 length; __u32 reserved2; @@ -348,6 +350,9 @@ static int get_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32, up_pln = compat_ptr(p); if (put_user((unsigned long)up_pln, &up->m.userptr)) return -EFAULT; + } else if (memory == V4L2_MEMORY_DMABUF) { + if (copy_in_user(&up->m.fd, &up32->m.fd, sizeof(int))) + return -EFAULT; } else { if (copy_in_user(&up->m.mem_offset, &up32->m.mem_offset, sizeof(__u32))) @@ -371,6 +376,11 @@ static int put_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32, if (copy_in_user(&up32->m.mem_offset, &up->m.mem_offset, sizeof(__u32))) return -EFAULT; + /* For DMABUF, driver might've set up the fd, so copy it back. */ + if (memory == V4L2_MEMORY_DMABUF) + if (copy_in_user(&up32->m.fd, &up->m.fd, + sizeof(int))) + return -EFAULT;
return 0; } @@ -453,6 +463,10 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user if (get_user(kp->m.offset, &up->m.offset)) return -EFAULT; break; + case V4L2_MEMORY_DMABUF: + if (get_user(kp->m.fd, &up->m.fd)) + return -EFAULT; + break; } }
@@ -517,6 +531,10 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user if (put_user(kp->m.offset, &up->m.offset)) return -EFAULT; break; + case V4L2_MEMORY_DMABUF: + if (put_user(kp->m.fd, &up->m.fd)) + return -EFAULT; + break; } }
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 6bc47fc..dffd3c9 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -155,6 +155,7 @@ static const char *v4l2_memory_names[] = { [V4L2_MEMORY_MMAP] = "mmap", [V4L2_MEMORY_USERPTR] = "userptr", [V4L2_MEMORY_OVERLAY] = "overlay", + [V4L2_MEMORY_DMABUF] = "dmabuf", };
#define prt_names(a, arr) ((((a) >= 0) && ((a) < ARRAY_SIZE(arr))) ? \ diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 7a147c8..e04a73e 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -186,6 +186,7 @@ enum v4l2_memory { V4L2_MEMORY_MMAP = 1, V4L2_MEMORY_USERPTR = 2, V4L2_MEMORY_OVERLAY = 3, + V4L2_MEMORY_DMABUF = 4, };
/* see also http://vektor.theorem.ca/graphics/ycbcr/ */ @@ -596,6 +597,8 @@ struct v4l2_requestbuffers { * should be passed to mmap() called on the video node) * @userptr: when memory is V4L2_MEMORY_USERPTR, a userspace pointer * pointing to this plane + * @fd: when memory is V4L2_MEMORY_DMABUF, a userspace file + * descriptor associated with this plane * @data_offset: offset in the plane to the start of data; usually 0, * unless there is a header in front of the data * @@ -610,6 +613,7 @@ struct v4l2_plane { union { __u32 mem_offset; unsigned long userptr; + __s32 fd; } m; __u32 data_offset; __u32 reserved[11]; @@ -634,6 +638,8 @@ struct v4l2_plane { * (or a "cookie" that should be passed to mmap() as offset) * @userptr: for non-multiplanar buffers with memory == V4L2_MEMORY_USERPTR; * a userspace pointer pointing to this buffer + * @fd: for non-multiplanar buffers with memory == V4L2_MEMORY_DMABUF; + * a userspace file descriptor associated with this buffer * @planes: for multiplanar buffers; userspace pointer to the array of plane * info structs for this buffer * @length: size in bytes of the buffer (NOT its payload) for single-plane @@ -660,6 +666,7 @@ struct v4l2_buffer { __u32 offset; unsigned long userptr; struct v4l2_plane *planes; + __s32 fd; } m; __u32 length; __u32 reserved2;
This patch adds description and usage examples for importing DMABUF file descriptor in V4L2.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com CC: linux-doc@vger.kernel.org --- Documentation/DocBook/media/v4l/compat.xml | 4 + Documentation/DocBook/media/v4l/io.xml | 180 +++++++++++++++++++- .../DocBook/media/v4l/vidioc-create-bufs.xml | 16 +- Documentation/DocBook/media/v4l/vidioc-qbuf.xml | 17 ++ Documentation/DocBook/media/v4l/vidioc-reqbufs.xml | 47 ++--- 5 files changed, 234 insertions(+), 30 deletions(-)
diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index faa0fd1..a46f95b 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2621,6 +2621,10 @@ ioctls.</para> <listitem> <para>Support for frequency band enumeration: &VIDIOC-ENUM-FREQ-BANDS; ioctl.</para> </listitem> + <listitem> + <para>Importing DMABUF file descriptors as a new IO method described + in <xref linkend="dmabuf" />.</para> + </listitem> </itemizedlist> </section>
diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml index 1885cc0..5b58657 100644 --- a/Documentation/DocBook/media/v4l/io.xml +++ b/Documentation/DocBook/media/v4l/io.xml @@ -331,7 +331,7 @@ application until one or more buffers can be dequeued. By default outgoing queue. When the <constant>O_NONBLOCK</constant> flag was given to the &func-open; function, <constant>VIDIOC_DQBUF</constant> returns immediately with an &EAGAIN; when no buffer is available. The -&func-select; or &func-poll; function are always available.</para> +&func-select; or &func-poll; functions are always available.</para>
<para>To start and stop capturing or output applications call the &VIDIOC-STREAMON; and &VIDIOC-STREAMOFF; ioctl. Note @@ -472,6 +472,161 @@ rest should be evident.</para> </footnote></para> </section>
+ <section id="dmabuf"> + <title>Streaming I/O (DMA buffer importing)</title> + + <note> + <title>Experimental</title> + <para>This is an <link linkend="experimental"> experimental </link> + interface and may change in the future.</para> + </note> + +<para>The DMABUF framework provides a generic method for sharing buffers +between multiple devices. Device drivers that support DMABUF can export a DMA +buffer to userspace as a file descriptor (known as the exporter role), import a +DMA buffer from userspace using a file descriptor previously exported for a +different or the same device (known as the importer role), or both. This +section describes the DMABUF importer role API in V4L2.</para> + +<para>Input and output devices support the streaming I/O method when the +<constant>V4L2_CAP_STREAMING</constant> flag in the +<structfield>capabilities</structfield> field of &v4l2-capability; returned by +the &VIDIOC-QUERYCAP; ioctl is set. Whether importing DMA buffers through +DMABUF file descriptors is supported is determined by calling the +&VIDIOC-REQBUFS; ioctl with the memory type set to +<constant>V4L2_MEMORY_DMABUF</constant>.</para> + + <para>This I/O method is dedicated for sharing DMA buffers between V4L and +other APIs. Buffers (planes) are allocated by a driver on behalf of an +application. Next, these buffers are exported to the application as file +descriptors using an API which is specific for an allocator driver. Only such +file descriptor are exchanged. The descriptors and meta-information are passed +in &v4l2-buffer; (or in &v4l2-plane; in the multi-planar API case). The driver +must be switched into DMABUF I/O mode by calling the &VIDIOC-REQBUFS; with the +desired buffer type.</para> + + <example> + <title>Initiating streaming I/O with DMABUF file descriptors</title> + + <programlisting> +&v4l2-requestbuffers; reqbuf; + +memset (&reqbuf, 0, sizeof (reqbuf)); +reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; +reqbuf.memory = V4L2_MEMORY_DMABUF; +reqbuf.count = 1; + +if (ioctl (fd, &VIDIOC-REQBUFS;, &reqbuf) == -1) { + if (errno == EINVAL) + printf("Video capturing or DMABUF streaming is not supported\n"); + else + perror("VIDIOC_REQBUFS"); + + exit(EXIT_FAILURE); +} + </programlisting> + </example> + + <para>The buffer (plane) file descriptor is passed on the fly with the +&VIDIOC-QBUF; ioctl. In case of multiplanar buffers, every plane can be +associated with a different DMABUF descriptor. Although buffers are commonly +cycled, applications can pass a different DMABUF descriptor at each +<constant>VIDIOC_QBUF</constant> call.</para> + + <example> + <title>Queueing DMABUF using single plane API</title> + + <programlisting> +int buffer_queue(int v4lfd, int index, int dmafd) +{ + &v4l2-buffer; buf; + + memset(&buf, 0, sizeof buf); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_DMABUF; + buf.index = index; + buf.m.fd = dmafd; + + if (ioctl(v4lfd, &VIDIOC-QBUF;, &buf) == -1) { + perror("VIDIOC_QBUF"); + return -1; + } + + return 0; +} + </programlisting> + </example> + + <example> + <title>Queueing DMABUF using multi plane API</title> + + <programlisting> +int buffer_queue_mp(int v4lfd, int index, int dmafd[], int n_planes) +{ + &v4l2-buffer; buf; + &v4l2-plane; planes[VIDEO_MAX_PLANES]; + int i; + + memset(&buf, 0, sizeof buf); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + buf.memory = V4L2_MEMORY_DMABUF; + buf.index = index; + buf.m.planes = planes; + buf.length = n_planes; + + memset(&planes, 0, sizeof planes); + + for (i = 0; i < n_planes; ++i) + buf.m.planes[i].m.fd = dmafd[i]; + + if (ioctl(v4lfd, &VIDIOC-QBUF;, &buf) == -1) { + perror("VIDIOC_QBUF"); + return -1; + } + + return 0; +} + </programlisting> + </example> + + <para>Filled or displayed buffers are dequeued with the +&VIDIOC-DQBUF; ioctl. The driver can unlock the buffer at any +time between the completion of the DMA and this ioctl. The memory is +also unlocked when &VIDIOC-STREAMOFF; is called, &VIDIOC-REQBUFS;, or +when the device is closed.</para> + + <para>For capturing applications it is customary to enqueue a +number of empty buffers, to start capturing and enter the read loop. +Here the application waits until a filled buffer can be dequeued, and +re-enqueues the buffer when the data is no longer needed. Output +applications fill and enqueue buffers, when enough buffers are stacked +up output is started. In the write loop, when the application +runs out of free buffers it must wait until an empty buffer can be +dequeued and reused. Two methods exist to suspend execution of the +application until one or more buffers can be dequeued. By default +<constant>VIDIOC_DQBUF</constant> blocks when no buffer is in the +outgoing queue. When the <constant>O_NONBLOCK</constant> flag was +given to the &func-open; function, <constant>VIDIOC_DQBUF</constant> +returns immediately with an &EAGAIN; when no buffer is available. The +&func-select; or &func-poll; function are always available.</para> + + <para>To start and stop capturing or output applications call the +&VIDIOC-STREAMON; and &VIDIOC-STREAMOFF; ioctls. Note that +<constant>VIDIOC_STREAMOFF</constant> removes all buffers from both queues and +unlocks all buffers as a side effect. Since there is no notion of doing +anything "now" on a multitasking system, if an application needs to synchronize +with another event it should examine the &v4l2-buffer; +<structfield>timestamp</structfield> of captured buffers, or set the field +before enqueuing buffers for output.</para> + + <para>Drivers implementing DMABUF importing I/O must support the +<constant>VIDIOC_REQBUFS</constant>, <constant>VIDIOC_QBUF</constant>, +<constant>VIDIOC_DQBUF</constant>, <constant>VIDIOC_STREAMON</constant> and +<constant>VIDIOC_STREAMOFF</constant> ioctls, and the +<function>select()</function> and <function>poll()</function> functions.</para> + + </section> + <section id="async"> <title>Asynchronous I/O</title>
@@ -673,6 +828,14 @@ memory, set by the application. See <xref linkend="userp" /> for details. <structname>v4l2_buffer</structname> structure.</entry> </row> <row> + <entry></entry> + <entry>int</entry> + <entry><structfield>fd</structfield></entry> + <entry>For the single-plane API and when +<structfield>memory</structfield> is <constant>V4L2_MEMORY_DMABUF</constant> this +is the file descriptor associated with a DMABUF buffer.</entry> + </row> + <row> <entry>__u32</entry> <entry><structfield>length</structfield></entry> <entry></entry> @@ -746,6 +909,15 @@ should set this to 0.</entry> </entry> </row> <row> + <entry></entry> + <entry>int</entry> + <entry><structfield>fd</structfield></entry> + <entry>When the memory type in the containing &v4l2-buffer; is + <constant>V4L2_MEMORY_DMABUF</constant>, this is a file + descriptor associated with a DMABUF buffer, similar to the + <structfield>fd</structfield> field in &v4l2-buffer;.</entry> + </row> + <row> <entry>__u32</entry> <entry><structfield>data_offset</structfield></entry> <entry></entry> @@ -973,6 +1145,12 @@ pointer</link> I/O.</entry> <entry>3</entry> <entry>[to do]</entry> </row> + <row> + <entry><constant>V4L2_MEMORY_DMABUF</constant></entry> + <entry>4</entry> + <entry>The buffer is used for <link linkend="dmabuf">DMA shared +buffer</link> I/O.</entry> + </row> </tbody> </tgroup> </table> diff --git a/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml b/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml index a8cda1a..cd99436 100644 --- a/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml +++ b/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml @@ -6,7 +6,8 @@
<refnamediv> <refname>VIDIOC_CREATE_BUFS</refname> - <refpurpose>Create buffers for Memory Mapped or User Pointer I/O</refpurpose> + <refpurpose>Create buffers for Memory Mapped or User Pointer or DMA Buffer + I/O</refpurpose> </refnamediv>
<refsynopsisdiv> @@ -55,11 +56,11 @@ </note>
<para>This ioctl is used to create buffers for <link linkend="mmap">memory -mapped</link> or <link linkend="userp">user pointer</link> -I/O. It can be used as an alternative or in addition to the -<constant>VIDIOC_REQBUFS</constant> ioctl, when a tighter control over buffers -is required. This ioctl can be called multiple times to create buffers of -different sizes.</para> +mapped</link> or <link linkend="userp">user pointer</link> or <link +linkend="dmabuf">DMA buffer</link> I/O. It can be used as an alternative or in +addition to the <constant>VIDIOC_REQBUFS</constant> ioctl, when a tighter +control over buffers is required. This ioctl can be called multiple times to +create buffers of different sizes.</para>
<para>To allocate device buffers applications initialize relevant fields of the <structname>v4l2_create_buffers</structname> structure. They set the @@ -109,7 +110,8 @@ information.</para> <entry>__u32</entry> <entry><structfield>memory</structfield></entry> <entry>Applications set this field to -<constant>V4L2_MEMORY_MMAP</constant> or +<constant>V4L2_MEMORY_MMAP</constant>, +<constant>V4L2_MEMORY_DMABUF</constant> or <constant>V4L2_MEMORY_USERPTR</constant>. See <xref linkend="v4l2-memory" /></entry> </row> diff --git a/Documentation/DocBook/media/v4l/vidioc-qbuf.xml b/Documentation/DocBook/media/v4l/vidioc-qbuf.xml index 77ff5be..f40dfa0 100644 --- a/Documentation/DocBook/media/v4l/vidioc-qbuf.xml +++ b/Documentation/DocBook/media/v4l/vidioc-qbuf.xml @@ -109,6 +109,23 @@ they cannot be swapped out to disk. Buffers remain locked until dequeued, until the &VIDIOC-STREAMOFF; or &VIDIOC-REQBUFS; ioctl is called, or until the device is closed.</para>
+ <para>To enqueue a <link linkend="dmabuf">DMABUF</link> buffer applications +set the <structfield>memory</structfield> field to +<constant>V4L2_MEMORY_DMABUF</constant> and the <structfield>m.fd</structfield> +field to a file descriptor associated with a DMABUF buffer. When the +multi-planar API is used the <structfield>m.fd</structfield> fields of the +passed array of &v4l2-plane; have to be used instead. When +<constant>VIDIOC_QBUF</constant> is called with a pointer to this structure the +driver sets the <constant>V4L2_BUF_FLAG_QUEUED</constant> flag and clears the +<constant>V4L2_BUF_FLAG_MAPPED</constant> and +<constant>V4L2_BUF_FLAG_DONE</constant> flags in the +<structfield>flags</structfield> field, or it returns an error code. This +ioctl locks the buffer. Locking a buffer means passing it to a driver for an +access by hardware. If an application accesses (reads/writes) a locked buffer +then the result is undefined. Buffers remain locked until dequeued, until the +&VIDIOC-STREAMOFF; or &VIDIOC-REQBUFS; ioctl is called, or until the device is +closed.</para> + <para>Applications call the <constant>VIDIOC_DQBUF</constant> ioctl to dequeue a filled (capturing) or displayed (output) buffer from the driver's outgoing queue. They just set the diff --git a/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml b/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml index d7c9505..20f4323 100644 --- a/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml +++ b/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml @@ -48,28 +48,30 @@ <refsect1> <title>Description</title>
- <para>This ioctl is used to initiate <link linkend="mmap">memory -mapped</link> or <link linkend="userp">user pointer</link> -I/O. Memory mapped buffers are located in device memory and must be -allocated with this ioctl before they can be mapped into the -application's address space. User buffers are allocated by -applications themselves, and this ioctl is merely used to switch the -driver into user pointer I/O mode and to setup some internal structures.</para> +<para>This ioctl is used to initiate <link linkend="mmap">memory mapped</link>, +<link linkend="userp">user pointer</link> or <link +linkend="dmabuf">DMABUF</link> based I/O. Memory mapped buffers are located in +device memory and must be allocated with this ioctl before they can be mapped +into the application's address space. User buffers are allocated by +applications themselves, and this ioctl is merely used to switch the driver +into user pointer I/O mode and to setup some internal structures. +Similarly, DMABUF buffers are allocated by applications through a device +driver, and this ioctl only configures the driver into DMABUF I/O mode without +performing any direct allocation.</para>
- <para>To allocate device buffers applications initialize all -fields of the <structname>v4l2_requestbuffers</structname> structure. -They set the <structfield>type</structfield> field to the respective -stream or buffer type, the <structfield>count</structfield> field to -the desired number of buffers, <structfield>memory</structfield> -must be set to the requested I/O method and the <structfield>reserved</structfield> array -must be zeroed. When the ioctl -is called with a pointer to this structure the driver will attempt to allocate -the requested number of buffers and it stores the actual number -allocated in the <structfield>count</structfield> field. It can be -smaller than the number requested, even zero, when the driver runs out -of free memory. A larger number is also possible when the driver requires -more buffers to function correctly. For example video output requires at least two buffers, -one displayed and one filled by the application.</para> + <para>To allocate device buffers applications initialize all fields of the +<structname>v4l2_requestbuffers</structname> structure. They set the +<structfield>type</structfield> field to the respective stream or buffer type, +the <structfield>count</structfield> field to the desired number of buffers, +<structfield>memory</structfield> must be set to the requested I/O method and +the <structfield>reserved</structfield> array must be zeroed. When the ioctl is +called with a pointer to this structure the driver will attempt to allocate the +requested number of buffers and it stores the actual number allocated in the +<structfield>count</structfield> field. It can be smaller than the number +requested, even zero, when the driver runs out of free memory. A larger number +is also possible when the driver requires more buffers to function correctly. +For example video output requires at least two buffers, one displayed and one +filled by the application.</para> <para>When the I/O method is not supported the ioctl returns an &EINVAL;.</para>
@@ -102,7 +104,8 @@ as the &v4l2-format; <structfield>type</structfield> field. See <xref <entry>__u32</entry> <entry><structfield>memory</structfield></entry> <entry>Applications set this field to -<constant>V4L2_MEMORY_MMAP</constant> or +<constant>V4L2_MEMORY_MMAP</constant>, +<constant>V4L2_MEMORY_DMABUF</constant> or <constant>V4L2_MEMORY_USERPTR</constant>. See <xref linkend="v4l2-memory" />.</entry> </row>
Hi Tomasz,
Just a few more stylistic issues...
On Tue October 2 2012 16:27:13 Tomasz Stanislawski wrote:
This patch adds description and usage examples for importing DMABUF file descriptor in V4L2.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com CC: linux-doc@vger.kernel.org
Documentation/DocBook/media/v4l/compat.xml | 4 + Documentation/DocBook/media/v4l/io.xml | 180 +++++++++++++++++++- .../DocBook/media/v4l/vidioc-create-bufs.xml | 16 +- Documentation/DocBook/media/v4l/vidioc-qbuf.xml | 17 ++ Documentation/DocBook/media/v4l/vidioc-reqbufs.xml | 47 ++--- 5 files changed, 234 insertions(+), 30 deletions(-)
diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index faa0fd1..a46f95b 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2621,6 +2621,10 @@ ioctls.</para> <listitem> <para>Support for frequency band enumeration: &VIDIOC-ENUM-FREQ-BANDS; ioctl.</para> </listitem>
<listitem>
<para>Importing DMABUF file descriptors as a new IO method described
in <xref linkend="dmabuf" />.</para>
</listitem> </itemizedlist>
</section>
diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml index 1885cc0..5b58657 100644 --- a/Documentation/DocBook/media/v4l/io.xml +++ b/Documentation/DocBook/media/v4l/io.xml @@ -331,7 +331,7 @@ application until one or more buffers can be dequeued. By default outgoing queue. When the <constant>O_NONBLOCK</constant> flag was given to the &func-open; function, <constant>VIDIOC_DQBUF</constant> returns immediately with an &EAGAIN; when no buffer is available. The -&func-select; or &func-poll; function are always available.</para> +&func-select; or &func-poll; functions are always available.</para> <para>To start and stop capturing or output applications call the &VIDIOC-STREAMON; and &VIDIOC-STREAMOFF; ioctl. Note @@ -472,6 +472,161 @@ rest should be evident.</para> </footnote></para>
</section> + <section id="dmabuf"> + <title>Streaming I/O (DMA buffer importing)</title> + + <note> + <title>Experimental</title> + <para>This is an <link linkend="experimental"> experimental </link> + interface and may change in the future.</para> + </note> + +<para>The DMABUF framework provides a generic method for sharing buffers +between multiple devices. Device drivers that support DMABUF can export a DMA +buffer to userspace as a file descriptor (known as the exporter role), import a +DMA buffer from userspace using a file descriptor previously exported for a +different or the same device (known as the importer role), or both. This +section describes the DMABUF importer role API in V4L2.</para> + +<para>Input and output devices support the streaming I/O method when the +<constant>V4L2_CAP_STREAMING</constant> flag in the +<structfield>capabilities</structfield> field of &v4l2-capability; returned by +the &VIDIOC-QUERYCAP; ioctl is set. Whether importing DMA buffers through +DMABUF file descriptors is supported is determined by calling the +&VIDIOC-REQBUFS; ioctl with the memory type set to +<constant>V4L2_MEMORY_DMABUF</constant>.</para> + + <para>This I/O method is dedicated for sharing DMA buffers between V4L and +other APIs.
It's more accurate to say something along the lines of: "...is dedicated to sharing DMA buffers between different devices, which may be V4L devices or other video-related devices (e.g. DRM)."
One of the uses of this API is to share buffers between different V4L devices, so that's a bit more generic than the original sentence suggests.
Buffers (planes) are allocated by a driver on behalf of an +application. Next, these buffers are exported to the application as file +descriptors using an API which is specific for an allocator driver. Only such +file descriptor are exchanged. The descriptors and meta-information are passed +in &v4l2-buffer; (or in &v4l2-plane; in the multi-planar API case). The driver +must be switched into DMABUF I/O mode by calling the &VIDIOC-REQBUFS; with the +desired buffer type.</para>
<example>
<title>Initiating streaming I/O with DMABUF file descriptors</title>
<programlisting>
+&v4l2-requestbuffers; reqbuf;
+memset (&reqbuf, 0, sizeof (reqbuf));
no space after memset
+reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; +reqbuf.memory = V4L2_MEMORY_DMABUF; +reqbuf.count = 1;
+if (ioctl (fd, &VIDIOC-REQBUFS;, &reqbuf) == -1) {
no space after ioctl
- if (errno == EINVAL)
printf("Video capturing or DMABUF streaming is not supported\n");
- else
perror("VIDIOC_REQBUFS");
- exit(EXIT_FAILURE);
+}
</programlisting>
</example>
- <para>The buffer (plane) file descriptor is passed on the fly with the
+&VIDIOC-QBUF; ioctl. In case of multiplanar buffers, every plane can be +associated with a different DMABUF descriptor. Although buffers are commonly +cycled, applications can pass a different DMABUF descriptor at each +<constant>VIDIOC_QBUF</constant> call.</para>
<example>
<title>Queueing DMABUF using single plane API</title>
<programlisting>
+int buffer_queue(int v4lfd, int index, int dmafd) +{
- &v4l2-buffer; buf;
- memset(&buf, 0, sizeof buf);
- buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf.memory = V4L2_MEMORY_DMABUF;
- buf.index = index;
- buf.m.fd = dmafd;
- if (ioctl(v4lfd, &VIDIOC-QBUF;, &buf) == -1) {
perror("VIDIOC_QBUF");
return -1;
- }
- return 0;
+}
</programlisting>
</example>
<example>
<title>Queueing DMABUF using multi plane API</title>
<programlisting>
+int buffer_queue_mp(int v4lfd, int index, int dmafd[], int n_planes) +{
- &v4l2-buffer; buf;
- &v4l2-plane; planes[VIDEO_MAX_PLANES];
- int i;
- memset(&buf, 0, sizeof buf);
- buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
- buf.memory = V4L2_MEMORY_DMABUF;
- buf.index = index;
- buf.m.planes = planes;
- buf.length = n_planes;
- memset(&planes, 0, sizeof planes);
- for (i = 0; i < n_planes; ++i)
buf.m.planes[i].m.fd = dmafd[i];
- if (ioctl(v4lfd, &VIDIOC-QBUF;, &buf) == -1) {
perror("VIDIOC_QBUF");
return -1;
- }
- return 0;
+}
</programlisting>
</example>
- <para>Filled or displayed buffers are dequeued with the
Filled -> Captured
+&VIDIOC-DQBUF; ioctl. The driver can unlock the buffer at any +time between the completion of the DMA and this ioctl. The memory is +also unlocked when &VIDIOC-STREAMOFF; is called, &VIDIOC-REQBUFS;, or +when the device is closed.</para>
- <para>For capturing applications it is customary to enqueue a
+number of empty buffers, to start capturing and enter the read loop. +Here the application waits until a filled buffer can be dequeued, and +re-enqueues the buffer when the data is no longer needed. Output +applications fill and enqueue buffers, when enough buffers are stacked +up output is started. In the write loop, when the application +runs out of free buffers it must wait until an empty buffer can be +dequeued and reused. Two methods exist to suspend execution of the +application until one or more buffers can be dequeued. By default +<constant>VIDIOC_DQBUF</constant> blocks when no buffer is in the +outgoing queue. When the <constant>O_NONBLOCK</constant> flag was +given to the &func-open; function, <constant>VIDIOC_DQBUF</constant> +returns immediately with an &EAGAIN; when no buffer is available. The +&func-select; or &func-poll; function are always available.</para>
or -> and function -> functions
- <para>To start and stop capturing or output applications call the
output -> displaying
+&VIDIOC-STREAMON; and &VIDIOC-STREAMOFF; ioctls. Note that +<constant>VIDIOC_STREAMOFF</constant> removes all buffers from both queues and +unlocks all buffers as a side effect. Since there is no notion of doing +anything "now" on a multitasking system, if an application needs to synchronize +with another event it should examine the &v4l2-buffer; +<structfield>timestamp</structfield> of captured buffers, or set the field +before enqueuing buffers for output.</para>
- <para>Drivers implementing DMABUF importing I/O must support the
+<constant>VIDIOC_REQBUFS</constant>, <constant>VIDIOC_QBUF</constant>, +<constant>VIDIOC_DQBUF</constant>, <constant>VIDIOC_STREAMON</constant> and +<constant>VIDIOC_STREAMOFF</constant> ioctls, and the +<function>select()</function> and <function>poll()</function> functions.</para>
</section>
<section id="async"> <title>Asynchronous I/O</title>
@@ -673,6 +828,14 @@ memory, set by the application. See <xref linkend="userp" /> for details. <structname>v4l2_buffer</structname> structure.</entry> </row> <row>
<entry></entry>
<entry>int</entry>
<entry><structfield>fd</structfield></entry>
<entry>For the single-plane API and when
+<structfield>memory</structfield> is <constant>V4L2_MEMORY_DMABUF</constant> this +is the file descriptor associated with a DMABUF buffer.</entry>
</row>
<row> <entry>__u32</entry> <entry><structfield>length</structfield></entry> <entry></entry>
@@ -746,6 +909,15 @@ should set this to 0.</entry> </entry> </row> <row>
<entry></entry>
<entry>int</entry>
<entry><structfield>fd</structfield></entry>
<entry>When the memory type in the containing &v4l2-buffer; is
<constant>V4L2_MEMORY_DMABUF</constant>, this is a file
descriptor associated with a DMABUF buffer, similar to the
<structfield>fd</structfield> field in &v4l2-buffer;.</entry>
</row>
<row> <entry>__u32</entry> <entry><structfield>data_offset</structfield></entry> <entry></entry>
@@ -973,6 +1145,12 @@ pointer</link> I/O.</entry> <entry>3</entry> <entry>[to do]</entry> </row>
<row>
<entry><constant>V4L2_MEMORY_DMABUF</constant></entry>
<entry>4</entry>
<entry>The buffer is used for <link linkend="dmabuf">DMA shared
+buffer</link> I/O.</entry>
</row>
</tbody> </tgroup> </table>
diff --git a/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml b/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml index a8cda1a..cd99436 100644 --- a/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml +++ b/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml @@ -6,7 +6,8 @@
<refnamediv> <refname>VIDIOC_CREATE_BUFS</refname> - <refpurpose>Create buffers for Memory Mapped or User Pointer I/O</refpurpose> + <refpurpose>Create buffers for Memory Mapped or User Pointer or DMA Buffer + I/O</refpurpose> </refnamediv> <refsynopsisdiv> @@ -55,11 +56,11 @@ </note> <para>This ioctl is used to create buffers for <link linkend="mmap">memory -mapped</link> or <link linkend="userp">user pointer</link> -I/O. It can be used as an alternative or in addition to the -<constant>VIDIOC_REQBUFS</constant> ioctl, when a tighter control over buffers -is required. This ioctl can be called multiple times to create buffers of -different sizes.</para> +mapped</link> or <link linkend="userp">user pointer</link> or <link +linkend="dmabuf">DMA buffer</link> I/O. It can be used as an alternative or in +addition to the <constant>VIDIOC_REQBUFS</constant> ioctl, when a tighter +control over buffers is required. This ioctl can be called multiple times to +create buffers of different sizes.</para> <para>To allocate device buffers applications initialize relevant fields of the <structname>v4l2_create_buffers</structname> structure. They set the @@ -109,7 +110,8 @@ information.</para> <entry>__u32</entry> <entry><structfield>memory</structfield></entry> <entry>Applications set this field to -<constant>V4L2_MEMORY_MMAP</constant> or +<constant>V4L2_MEMORY_MMAP</constant>, +<constant>V4L2_MEMORY_DMABUF</constant> or <constant>V4L2_MEMORY_USERPTR</constant>. See <xref linkend="v4l2-memory" /></entry> </row> diff --git a/Documentation/DocBook/media/v4l/vidioc-qbuf.xml b/Documentation/DocBook/media/v4l/vidioc-qbuf.xml index 77ff5be..f40dfa0 100644 --- a/Documentation/DocBook/media/v4l/vidioc-qbuf.xml +++ b/Documentation/DocBook/media/v4l/vidioc-qbuf.xml @@ -109,6 +109,23 @@ they cannot be swapped out to disk. Buffers remain locked until dequeued, until the &VIDIOC-STREAMOFF; or &VIDIOC-REQBUFS; ioctl is called, or until the device is closed.</para> + <para>To enqueue a <link linkend="dmabuf">DMABUF</link> buffer applications +set the <structfield>memory</structfield> field to +<constant>V4L2_MEMORY_DMABUF</constant> and the <structfield>m.fd</structfield> +field to a file descriptor associated with a DMABUF buffer. When the +multi-planar API is used the <structfield>m.fd</structfield> fields of the +passed array of &v4l2-plane; have to be used instead. When +<constant>VIDIOC_QBUF</constant> is called with a pointer to this structure the +driver sets the <constant>V4L2_BUF_FLAG_QUEUED</constant> flag and clears the +<constant>V4L2_BUF_FLAG_MAPPED</constant> and +<constant>V4L2_BUF_FLAG_DONE</constant> flags in the +<structfield>flags</structfield> field, or it returns an error code. This +ioctl locks the buffer. Locking a buffer means passing it to a driver for an +access by hardware.
"an access by hardware" -> "hardware access (usually DMA)"
If an application accesses (reads/writes) a locked buffer +then the result is undefined. Buffers remain locked until dequeued, until the +&VIDIOC-STREAMOFF; or &VIDIOC-REQBUFS; ioctl is called, or until the device is +closed.</para>
- <para>Applications call the <constant>VIDIOC_DQBUF</constant>
ioctl to dequeue a filled (capturing) or displayed (output) buffer from the driver's outgoing queue. They just set the diff --git a/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml b/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml index d7c9505..20f4323 100644 --- a/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml +++ b/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml @@ -48,28 +48,30 @@
<refsect1> <title>Description</title> - <para>This ioctl is used to initiate <link linkend="mmap">memory -mapped</link> or <link linkend="userp">user pointer</link> -I/O. Memory mapped buffers are located in device memory and must be -allocated with this ioctl before they can be mapped into the -application's address space. User buffers are allocated by -applications themselves, and this ioctl is merely used to switch the -driver into user pointer I/O mode and to setup some internal structures.</para> +<para>This ioctl is used to initiate <link linkend="mmap">memory mapped</link>, +<link linkend="userp">user pointer</link> or <link +linkend="dmabuf">DMABUF</link> based I/O. Memory mapped buffers are located in +device memory and must be allocated with this ioctl before they can be mapped +into the application's address space. User buffers are allocated by +applications themselves, and this ioctl is merely used to switch the driver +into user pointer I/O mode and to setup some internal structures. +Similarly, DMABUF buffers are allocated by applications through a device +driver, and this ioctl only configures the driver into DMABUF I/O mode without +performing any direct allocation.</para> - <para>To allocate device buffers applications initialize all -fields of the <structname>v4l2_requestbuffers</structname> structure. -They set the <structfield>type</structfield> field to the respective -stream or buffer type, the <structfield>count</structfield> field to -the desired number of buffers, <structfield>memory</structfield> -must be set to the requested I/O method and the <structfield>reserved</structfield> array -must be zeroed. When the ioctl -is called with a pointer to this structure the driver will attempt to allocate -the requested number of buffers and it stores the actual number -allocated in the <structfield>count</structfield> field. It can be -smaller than the number requested, even zero, when the driver runs out -of free memory. A larger number is also possible when the driver requires -more buffers to function correctly. For example video output requires at least two buffers, -one displayed and one filled by the application.</para> + <para>To allocate device buffers applications initialize all fields of the +<structname>v4l2_requestbuffers</structname> structure. They set the +<structfield>type</structfield> field to the respective stream or buffer type, +the <structfield>count</structfield> field to the desired number of buffers, +<structfield>memory</structfield> must be set to the requested I/O method and +the <structfield>reserved</structfield> array must be zeroed. When the ioctl is +called with a pointer to this structure the driver will attempt to allocate the +requested number of buffers and it stores the actual number allocated in the +<structfield>count</structfield> field. It can be smaller than the number +requested, even zero, when the driver runs out of free memory. A larger number +is also possible when the driver requires more buffers to function correctly. +For example video output requires at least two buffers, one displayed and one +filled by the application.</para> <para>When the I/O method is not supported the ioctl returns an &EINVAL;.</para> @@ -102,7 +104,8 @@ as the &v4l2-format; <structfield>type</structfield> field. See <xref <entry>__u32</entry> <entry><structfield>memory</structfield></entry> <entry>Applications set this field to -<constant>V4L2_MEMORY_MMAP</constant> or +<constant>V4L2_MEMORY_MMAP</constant>, +<constant>V4L2_MEMORY_DMABUF</constant> or <constant>V4L2_MEMORY_USERPTR</constant>. See <xref linkend="v4l2-memory" />.</entry> </row>
Regards,
Hans
From: Sumit Semwal sumit.semwal@ti.com
This patch adds support for DMABUF memory type in videobuf2. It calls relevant APIs of dma_buf for v4l reqbuf / qbuf / dqbuf operations.
For this version, the support is for videobuf2 as a user of the shared buffer; so the allocation of the buffer is done outside of V4L2. [A sample allocator of dma-buf shared buffer is given at [1]]
[1]: Rob Clark's DRM: https://github.com/robclark/kernel-omap4/commits/drmplane-dmabuf
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com [original work in the PoC for buffer sharing] Signed-off-by: Sumit Semwal sumit.semwal@ti.com Signed-off-by: Sumit Semwal sumit.semwal@linaro.org Acked-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/media/video/Kconfig | 1 + drivers/media/video/videobuf2-core.c | 207 +++++++++++++++++++++++++++++++++- include/media/videobuf2-core.h | 27 +++++ 3 files changed, 232 insertions(+), 3 deletions(-)
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index c128fac..c558d37 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -45,6 +45,7 @@ config V4L2_MEM2MEM_DEV depends on VIDEOBUF2_CORE
config VIDEOBUF2_CORE + select DMA_SHARED_BUFFER tristate
config VIDEOBUF2_MEMOPS diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 268c7dd..901bc56 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -109,6 +109,36 @@ static void __vb2_buf_userptr_put(struct vb2_buffer *vb) }
/** + * __vb2_plane_dmabuf_put() - release memory associated with + * a DMABUF shared plane + */ +static void __vb2_plane_dmabuf_put(struct vb2_queue *q, struct vb2_plane *p) +{ + if (!p->mem_priv) + return; + + if (p->dbuf_mapped) + call_memop(q, unmap_dmabuf, p->mem_priv); + + call_memop(q, detach_dmabuf, p->mem_priv); + dma_buf_put(p->dbuf); + memset(p, 0, sizeof(*p)); +} + +/** + * __vb2_buf_dmabuf_put() - release memory associated with + * a DMABUF shared buffer + */ +static void __vb2_buf_dmabuf_put(struct vb2_buffer *vb) +{ + struct vb2_queue *q = vb->vb2_queue; + unsigned int plane; + + for (plane = 0; plane < vb->num_planes; ++plane) + __vb2_plane_dmabuf_put(q, &vb->planes[plane]); +} + +/** * __setup_offsets() - setup unique offsets ("cookies") for every plane in * every buffer on the queue */ @@ -230,6 +260,8 @@ static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers) /* Free MMAP buffers or release USERPTR buffers */ if (q->memory == V4L2_MEMORY_MMAP) __vb2_buf_mem_free(vb); + else if (q->memory == V4L2_MEMORY_DMABUF) + __vb2_buf_dmabuf_put(vb); else __vb2_buf_userptr_put(vb); } @@ -363,6 +395,8 @@ static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) b->m.offset = vb->v4l2_planes[0].m.mem_offset; else if (q->memory == V4L2_MEMORY_USERPTR) b->m.userptr = vb->v4l2_planes[0].m.userptr; + else if (q->memory == V4L2_MEMORY_DMABUF) + b->m.fd = vb->v4l2_planes[0].m.fd; }
/* @@ -454,13 +488,28 @@ static int __verify_mmap_ops(struct vb2_queue *q) }
/** + * __verify_dmabuf_ops() - verify that all memory operations required for + * DMABUF queue type have been provided + */ +static int __verify_dmabuf_ops(struct vb2_queue *q) +{ + if (!(q->io_modes & VB2_DMABUF) || !q->mem_ops->attach_dmabuf || + !q->mem_ops->detach_dmabuf || !q->mem_ops->map_dmabuf || + !q->mem_ops->unmap_dmabuf) + return -EINVAL; + + return 0; +} + +/** * __verify_memory_type() - Check whether the memory type and buffer type * passed to a buffer operation are compatible with the queue. */ static int __verify_memory_type(struct vb2_queue *q, enum v4l2_memory memory, enum v4l2_buf_type type) { - if (memory != V4L2_MEMORY_MMAP && memory != V4L2_MEMORY_USERPTR) { + if (memory != V4L2_MEMORY_MMAP && memory != V4L2_MEMORY_USERPTR && + memory != V4L2_MEMORY_DMABUF) { dprintk(1, "reqbufs: unsupported memory type\n"); return -EINVAL; } @@ -484,6 +533,11 @@ static int __verify_memory_type(struct vb2_queue *q, return -EINVAL; }
+ if (memory == V4L2_MEMORY_DMABUF && __verify_dmabuf_ops(q)) { + dprintk(1, "reqbufs: DMABUF for current setup unsupported\n"); + return -EINVAL; + } + /* * Place the busy tests at the end: -EBUSY can be ignored when * create_bufs is called with count == 0, but count == 0 should still @@ -853,6 +907,16 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b, b->m.planes[plane].length; } } + if (b->memory == V4L2_MEMORY_DMABUF) { + for (plane = 0; plane < vb->num_planes; ++plane) { + v4l2_planes[plane].m.fd = + b->m.planes[plane].m.fd; + v4l2_planes[plane].length = + b->m.planes[plane].length; + v4l2_planes[plane].data_offset = + b->m.planes[plane].data_offset; + } + } } else { /* * Single-planar buffers do not use planes array, @@ -867,6 +931,12 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b, v4l2_planes[0].m.userptr = b->m.userptr; v4l2_planes[0].length = b->length; } + + if (b->memory == V4L2_MEMORY_DMABUF) { + v4l2_planes[0].m.fd = b->m.fd; + v4l2_planes[0].length = b->length; + } + }
vb->v4l2_buf.field = b->field; @@ -970,6 +1040,109 @@ static int __qbuf_mmap(struct vb2_buffer *vb, const struct v4l2_buffer *b) }
/** + * __qbuf_dmabuf() - handle qbuf of a DMABUF buffer + */ +static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b) +{ + struct v4l2_plane planes[VIDEO_MAX_PLANES]; + struct vb2_queue *q = vb->vb2_queue; + void *mem_priv; + unsigned int plane; + int ret; + int write = !V4L2_TYPE_IS_OUTPUT(q->type); + + /* Verify and copy relevant information provided by the userspace */ + ret = __fill_vb2_buffer(vb, b, planes); + if (ret) + return ret; + + for (plane = 0; plane < vb->num_planes; ++plane) { + struct dma_buf *dbuf = dma_buf_get(planes[plane].m.fd); + + if (IS_ERR_OR_NULL(dbuf)) { + dprintk(1, "qbuf: invalid dmabuf fd for plane %d\n", + plane); + ret = -EINVAL; + goto err; + } + + /* use DMABUF size if length is not provided */ + if (planes[plane].length == 0) + planes[plane].length = dbuf->size; + + if (planes[plane].length < planes[plane].data_offset + + q->plane_sizes[plane]) { + ret = -EINVAL; + goto err; + } + + /* Skip the plane if already verified */ + if (dbuf == vb->planes[plane].dbuf && + vb->v4l2_planes[plane].length == planes[plane].length) { + dma_buf_put(dbuf); + continue; + } + + dprintk(1, "qbuf: buffer for plane %d changed\n", plane); + + /* Release previously acquired memory if present */ + __vb2_plane_dmabuf_put(q, &vb->planes[plane]); + memset(&vb->v4l2_planes[plane], 0, sizeof(struct v4l2_plane)); + + /* Acquire each plane's memory */ + mem_priv = call_memop(q, attach_dmabuf, q->alloc_ctx[plane], + dbuf, planes[plane].length, write); + if (IS_ERR(mem_priv)) { + dprintk(1, "qbuf: failed to attach dmabuf\n"); + ret = PTR_ERR(mem_priv); + dma_buf_put(dbuf); + goto err; + } + + vb->planes[plane].dbuf = dbuf; + vb->planes[plane].mem_priv = mem_priv; + } + + /* TODO: This pins the buffer(s) with dma_buf_map_attachment()).. but + * really we want to do this just before the DMA, not while queueing + * the buffer(s).. + */ + for (plane = 0; plane < vb->num_planes; ++plane) { + ret = call_memop(q, map_dmabuf, vb->planes[plane].mem_priv); + if (ret) { + dprintk(1, "qbuf: failed to map dmabuf for plane %d\n", + plane); + goto err; + } + vb->planes[plane].dbuf_mapped = 1; + } + + /* + * Call driver-specific initialization on the newly acquired buffer, + * if provided. + */ + ret = call_qop(q, buf_init, vb); + if (ret) { + dprintk(1, "qbuf: buffer initialization failed\n"); + goto err; + } + + /* + * Now that everything is in order, copy relevant information + * provided by userspace. + */ + for (plane = 0; plane < vb->num_planes; ++plane) + vb->v4l2_planes[plane] = planes[plane]; + + return 0; +err: + /* In case of errors, release planes that were already acquired */ + __vb2_buf_dmabuf_put(vb); + + return ret; +} + +/** * __enqueue_in_driver() - enqueue a vb2_buffer in driver for processing */ static void __enqueue_in_driver(struct vb2_buffer *vb) @@ -993,6 +1166,9 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) case V4L2_MEMORY_USERPTR: ret = __qbuf_userptr(vb, b); break; + case V4L2_MEMORY_DMABUF: + ret = __qbuf_dmabuf(vb, b); + break; default: WARN(1, "Invalid queue type\n"); ret = -EINVAL; @@ -1301,6 +1477,30 @@ int vb2_wait_for_all_buffers(struct vb2_queue *q) EXPORT_SYMBOL_GPL(vb2_wait_for_all_buffers);
/** + * __vb2_dqbuf() - bring back the buffer to the DEQUEUED state + */ +static void __vb2_dqbuf(struct vb2_buffer *vb) +{ + struct vb2_queue *q = vb->vb2_queue; + unsigned int i; + + /* nothing to do if the buffer is already dequeued */ + if (vb->state == VB2_BUF_STATE_DEQUEUED) + return; + + vb->state = VB2_BUF_STATE_DEQUEUED; + + /* unmap DMABUF buffer */ + if (q->memory == V4L2_MEMORY_DMABUF) + for (i = 0; i < vb->num_planes; ++i) { + if (!vb->planes[i].dbuf_mapped) + continue; + call_memop(q, unmap_dmabuf, vb->planes[i].mem_priv); + vb->planes[i].dbuf_mapped = 0; + } +} + +/** * vb2_dqbuf() - Dequeue a buffer to the userspace * @q: videobuf2 queue * @b: buffer structure passed from userspace to vidioc_dqbuf handler @@ -1364,11 +1564,12 @@ int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) __fill_v4l2_buffer(vb, b); /* Remove from videobuf queue */ list_del(&vb->queued_entry); + /* go back to dequeued state */ + __vb2_dqbuf(vb);
dprintk(1, "dqbuf of buffer %d, with state %d\n", vb->v4l2_buf.index, vb->state);
- vb->state = VB2_BUF_STATE_DEQUEUED; return 0; } EXPORT_SYMBOL_GPL(vb2_dqbuf); @@ -1407,7 +1608,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) * Reinitialize all buffers for next use. */ for (i = 0; i < q->num_buffers; ++i) - q->bufs[i]->state = VB2_BUF_STATE_DEQUEUED; + __vb2_dqbuf(q->bufs[i]); }
/** diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 8dd9b6c..84f11f2 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -16,6 +16,7 @@ #include <linux/mutex.h> #include <linux/poll.h> #include <linux/videodev2.h> +#include <linux/dma-buf.h>
struct vb2_alloc_ctx; struct vb2_fileio_data; @@ -41,6 +42,20 @@ struct vb2_fileio_data; * argument to other ops in this structure * @put_userptr: inform the allocator that a USERPTR buffer will no longer * be used + * @attach_dmabuf: attach a shared struct dma_buf for a hardware operation; + * used for DMABUF memory types; alloc_ctx is the alloc context + * dbuf is the shared dma_buf; returns NULL on failure; + * allocator private per-buffer structure on success; + * this needs to be used for further accesses to the buffer + * @detach_dmabuf: inform the exporter of the buffer that the current DMABUF + * buffer is no longer used; the buf_priv argument is the + * allocator private per-buffer structure previously returned + * from the attach_dmabuf callback + * @map_dmabuf: request for access to the dmabuf from allocator; the allocator + * of dmabuf is informed that this driver is going to use the + * dmabuf + * @unmap_dmabuf: releases access control to the dmabuf - allocator is notified + * that this driver is done using the dmabuf for now * @vaddr: return a kernel virtual address to a given memory buffer * associated with the passed private structure or NULL if no * such mapping exists @@ -56,6 +71,8 @@ struct vb2_fileio_data; * Required ops for USERPTR types: get_userptr, put_userptr. * Required ops for MMAP types: alloc, put, num_users, mmap. * Required ops for read/write access types: alloc, put, num_users, vaddr + * Required ops for DMABUF types: attach_dmabuf, detach_dmabuf, map_dmabuf, + * unmap_dmabuf. */ struct vb2_mem_ops { void *(*alloc)(void *alloc_ctx, unsigned long size); @@ -65,6 +82,12 @@ struct vb2_mem_ops { unsigned long size, int write); void (*put_userptr)(void *buf_priv);
+ void *(*attach_dmabuf)(void *alloc_ctx, struct dma_buf *dbuf, + unsigned long size, int write); + void (*detach_dmabuf)(void *buf_priv); + int (*map_dmabuf)(void *buf_priv); + void (*unmap_dmabuf)(void *buf_priv); + void *(*vaddr)(void *buf_priv); void *(*cookie)(void *buf_priv);
@@ -75,6 +98,8 @@ struct vb2_mem_ops {
struct vb2_plane { void *mem_priv; + struct dma_buf *dbuf; + unsigned int dbuf_mapped; };
/** @@ -83,12 +108,14 @@ struct vb2_plane { * @VB2_USERPTR: driver supports USERPTR with streaming API * @VB2_READ: driver supports read() style access * @VB2_WRITE: driver supports write() style access + * @VB2_DMABUF: driver supports DMABUF with streaming API */ enum vb2_io_modes { VB2_MMAP = (1 << 0), VB2_USERPTR = (1 << 1), VB2_READ = (1 << 2), VB2_WRITE = (1 << 3), + VB2_DMABUF = (1 << 4), };
/**
Just a small heads-up for an upcoming change...
On Tue October 2 2012 16:27:14 Tomasz Stanislawski wrote:
From: Sumit Semwal sumit.semwal@ti.com
This patch adds support for DMABUF memory type in videobuf2. It calls relevant APIs of dma_buf for v4l reqbuf / qbuf / dqbuf operations.
For this version, the support is for videobuf2 as a user of the shared buffer; so the allocation of the buffer is done outside of V4L2. [A sample allocator of dma-buf shared buffer is given at [1]]
[1]: Rob Clark's DRM: https://github.com/robclark/kernel-omap4/commits/drmplane-dmabuf
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com [original work in the PoC for buffer sharing] Signed-off-by: Sumit Semwal sumit.semwal@ti.com Signed-off-by: Sumit Semwal sumit.semwal@linaro.org Acked-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
drivers/media/video/Kconfig | 1 + drivers/media/video/videobuf2-core.c | 207 +++++++++++++++++++++++++++++++++- include/media/videobuf2-core.h | 27 +++++ 3 files changed, 232 insertions(+), 3 deletions(-)
<snip>
@@ -970,6 +1040,109 @@ static int __qbuf_mmap(struct vb2_buffer *vb, const struct v4l2_buffer *b) } /**
- __qbuf_dmabuf() - handle qbuf of a DMABUF buffer
- */
+static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b) +{
- struct v4l2_plane planes[VIDEO_MAX_PLANES];
- struct vb2_queue *q = vb->vb2_queue;
- void *mem_priv;
- unsigned int plane;
- int ret;
- int write = !V4L2_TYPE_IS_OUTPUT(q->type);
- /* Verify and copy relevant information provided by the userspace */
- ret = __fill_vb2_buffer(vb, b, planes);
Note that this code will have to change a bit when my multiplanar fixes go in:
http://www.spinics.net/lists/linux-media/msg54601.html
__fill_vb2_buffer is now a void function, so there won't be any need to check the error.
- if (ret)
return ret;
- for (plane = 0; plane < vb->num_planes; ++plane) {
struct dma_buf *dbuf = dma_buf_get(planes[plane].m.fd);
if (IS_ERR_OR_NULL(dbuf)) {
dprintk(1, "qbuf: invalid dmabuf fd for plane %d\n",
plane);
ret = -EINVAL;
goto err;
}
/* use DMABUF size if length is not provided */
if (planes[plane].length == 0)
planes[plane].length = dbuf->size;
if (planes[plane].length < planes[plane].data_offset +
q->plane_sizes[plane]) {
ret = -EINVAL;
goto err;
}
/* Skip the plane if already verified */
if (dbuf == vb->planes[plane].dbuf &&
vb->v4l2_planes[plane].length == planes[plane].length) {
dma_buf_put(dbuf);
continue;
}
dprintk(1, "qbuf: buffer for plane %d changed\n", plane);
/* Release previously acquired memory if present */
__vb2_plane_dmabuf_put(q, &vb->planes[plane]);
memset(&vb->v4l2_planes[plane], 0, sizeof(struct v4l2_plane));
/* Acquire each plane's memory */
mem_priv = call_memop(q, attach_dmabuf, q->alloc_ctx[plane],
dbuf, planes[plane].length, write);
if (IS_ERR(mem_priv)) {
dprintk(1, "qbuf: failed to attach dmabuf\n");
ret = PTR_ERR(mem_priv);
dma_buf_put(dbuf);
goto err;
}
vb->planes[plane].dbuf = dbuf;
vb->planes[plane].mem_priv = mem_priv;
- }
- /* TODO: This pins the buffer(s) with dma_buf_map_attachment()).. but
* really we want to do this just before the DMA, not while queueing
* the buffer(s)..
*/
- for (plane = 0; plane < vb->num_planes; ++plane) {
ret = call_memop(q, map_dmabuf, vb->planes[plane].mem_priv);
if (ret) {
dprintk(1, "qbuf: failed to map dmabuf for plane %d\n",
plane);
goto err;
}
vb->planes[plane].dbuf_mapped = 1;
- }
- /*
* Call driver-specific initialization on the newly acquired buffer,
* if provided.
*/
- ret = call_qop(q, buf_init, vb);
- if (ret) {
dprintk(1, "qbuf: buffer initialization failed\n");
goto err;
- }
- /*
* Now that everything is in order, copy relevant information
* provided by userspace.
*/
- for (plane = 0; plane < vb->num_planes; ++plane)
vb->v4l2_planes[plane] = planes[plane];
- return 0;
+err:
- /* In case of errors, release planes that were already acquired */
- __vb2_buf_dmabuf_put(vb);
- return ret;
+}
From: Sumit Semwal sumit.semwal@ti.com
Adding DMABUF memory type causes videobuf to complain about not using it in some switch cases. This patch removes these warnings.
Signed-off-by: Sumit Semwal sumit.semwal@ti.com Acked-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/media/video/videobuf-core.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index bf7a326..5449e8a 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -335,6 +335,9 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, case V4L2_MEMORY_OVERLAY: b->m.offset = vb->boff; break; + case V4L2_MEMORY_DMABUF: + /* DMABUF is not handled in videobuf framework */ + break; }
b->flags = 0; @@ -405,6 +408,7 @@ int __videobuf_mmap_setup(struct videobuf_queue *q, break; case V4L2_MEMORY_USERPTR: case V4L2_MEMORY_OVERLAY: + case V4L2_MEMORY_DMABUF: /* nothing */ break; }
From: Laurent Pinchart laurent.pinchart@ideasonboard.com
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/media/video/videobuf2-dma-contig.c | 36 ++++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-)
diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c index 4b71326..a05784f 100644 --- a/drivers/media/video/videobuf2-dma-contig.c +++ b/drivers/media/video/videobuf2-dma-contig.c @@ -32,9 +32,9 @@ struct vb2_dc_buf { struct vb2_vmarea_handler handler; };
-static void vb2_dma_contig_put(void *buf_priv); +static void vb2_dc_put(void *buf_priv);
-static void *vb2_dma_contig_alloc(void *alloc_ctx, unsigned long size) +static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size) { struct vb2_dc_conf *conf = alloc_ctx; struct vb2_dc_buf *buf; @@ -56,7 +56,7 @@ static void *vb2_dma_contig_alloc(void *alloc_ctx, unsigned long size) buf->size = size;
buf->handler.refcount = &buf->refcount; - buf->handler.put = vb2_dma_contig_put; + buf->handler.put = vb2_dc_put; buf->handler.arg = buf;
atomic_inc(&buf->refcount); @@ -64,7 +64,7 @@ static void *vb2_dma_contig_alloc(void *alloc_ctx, unsigned long size) return buf; }
-static void vb2_dma_contig_put(void *buf_priv) +static void vb2_dc_put(void *buf_priv) { struct vb2_dc_buf *buf = buf_priv;
@@ -75,14 +75,14 @@ static void vb2_dma_contig_put(void *buf_priv) } }
-static void *vb2_dma_contig_cookie(void *buf_priv) +static void *vb2_dc_cookie(void *buf_priv) { struct vb2_dc_buf *buf = buf_priv;
return &buf->dma_addr; }
-static void *vb2_dma_contig_vaddr(void *buf_priv) +static void *vb2_dc_vaddr(void *buf_priv) { struct vb2_dc_buf *buf = buf_priv; if (!buf) @@ -91,14 +91,14 @@ static void *vb2_dma_contig_vaddr(void *buf_priv) return buf->vaddr; }
-static unsigned int vb2_dma_contig_num_users(void *buf_priv) +static unsigned int vb2_dc_num_users(void *buf_priv) { struct vb2_dc_buf *buf = buf_priv;
return atomic_read(&buf->refcount); }
-static int vb2_dma_contig_mmap(void *buf_priv, struct vm_area_struct *vma) +static int vb2_dc_mmap(void *buf_priv, struct vm_area_struct *vma) { struct vb2_dc_buf *buf = buf_priv;
@@ -111,7 +111,7 @@ static int vb2_dma_contig_mmap(void *buf_priv, struct vm_area_struct *vma) &vb2_common_vm_ops, &buf->handler); }
-static void *vb2_dma_contig_get_userptr(void *alloc_ctx, unsigned long vaddr, +static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, unsigned long size, int write) { struct vb2_dc_buf *buf; @@ -138,7 +138,7 @@ static void *vb2_dma_contig_get_userptr(void *alloc_ctx, unsigned long vaddr, return buf; }
-static void vb2_dma_contig_put_userptr(void *mem_priv) +static void vb2_dc_put_userptr(void *mem_priv) { struct vb2_dc_buf *buf = mem_priv;
@@ -150,14 +150,14 @@ static void vb2_dma_contig_put_userptr(void *mem_priv) }
const struct vb2_mem_ops vb2_dma_contig_memops = { - .alloc = vb2_dma_contig_alloc, - .put = vb2_dma_contig_put, - .cookie = vb2_dma_contig_cookie, - .vaddr = vb2_dma_contig_vaddr, - .mmap = vb2_dma_contig_mmap, - .get_userptr = vb2_dma_contig_get_userptr, - .put_userptr = vb2_dma_contig_put_userptr, - .num_users = vb2_dma_contig_num_users, + .alloc = vb2_dc_alloc, + .put = vb2_dc_put, + .cookie = vb2_dc_cookie, + .vaddr = vb2_dc_vaddr, + .mmap = vb2_dc_mmap, + .get_userptr = vb2_dc_get_userptr, + .put_userptr = vb2_dc_put_userptr, + .num_users = vb2_dc_num_users, }; EXPORT_SYMBOL_GPL(vb2_dma_contig_memops);
This patch removes a reference to alloc_ctx from an instance of a DMA contiguous buffer. It helps to avoid a risk of a dangling pointer if the context is released while the buffer is still valid. Moreover it removes one dereference step while accessing a device structure.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Acked-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/media/video/videobuf2-dma-contig.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c index a05784f..20c95da 100644 --- a/drivers/media/video/videobuf2-dma-contig.c +++ b/drivers/media/video/videobuf2-dma-contig.c @@ -23,7 +23,7 @@ struct vb2_dc_conf { };
struct vb2_dc_buf { - struct vb2_dc_conf *conf; + struct device *dev; void *vaddr; dma_addr_t dma_addr; unsigned long size; @@ -37,22 +37,21 @@ static void vb2_dc_put(void *buf_priv); static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size) { struct vb2_dc_conf *conf = alloc_ctx; + struct device *dev = conf->dev; struct vb2_dc_buf *buf;
buf = kzalloc(sizeof *buf, GFP_KERNEL); if (!buf) return ERR_PTR(-ENOMEM);
- buf->vaddr = dma_alloc_coherent(conf->dev, size, &buf->dma_addr, - GFP_KERNEL); + buf->vaddr = dma_alloc_coherent(dev, size, &buf->dma_addr, GFP_KERNEL); if (!buf->vaddr) { - dev_err(conf->dev, "dma_alloc_coherent of size %ld failed\n", - size); + dev_err(dev, "dma_alloc_coherent of size %ld failed\n", size); kfree(buf); return ERR_PTR(-ENOMEM); }
- buf->conf = conf; + buf->dev = dev; buf->size = size;
buf->handler.refcount = &buf->refcount; @@ -69,7 +68,7 @@ static void vb2_dc_put(void *buf_priv) struct vb2_dc_buf *buf = buf_priv;
if (atomic_dec_and_test(&buf->refcount)) { - dma_free_coherent(buf->conf->dev, buf->size, buf->vaddr, + dma_free_coherent(buf->dev, buf->size, buf->vaddr, buf->dma_addr); kfree(buf); }
From: Laurent Pinchart laurent.pinchart@ideasonboard.com
Group functions by buffer type.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/media/video/videobuf2-dma-contig.c | 92 ++++++++++++++++------------ 1 file changed, 54 insertions(+), 38 deletions(-)
diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c index 20c95da..daac2b2 100644 --- a/drivers/media/video/videobuf2-dma-contig.c +++ b/drivers/media/video/videobuf2-dma-contig.c @@ -25,14 +25,56 @@ struct vb2_dc_conf { struct vb2_dc_buf { struct device *dev; void *vaddr; - dma_addr_t dma_addr; unsigned long size; - struct vm_area_struct *vma; - atomic_t refcount; + dma_addr_t dma_addr; + + /* MMAP related */ struct vb2_vmarea_handler handler; + atomic_t refcount; + + /* USERPTR related */ + struct vm_area_struct *vma; };
-static void vb2_dc_put(void *buf_priv); +/*********************************************/ +/* callbacks for all buffers */ +/*********************************************/ + +static void *vb2_dc_cookie(void *buf_priv) +{ + struct vb2_dc_buf *buf = buf_priv; + + return &buf->dma_addr; +} + +static void *vb2_dc_vaddr(void *buf_priv) +{ + struct vb2_dc_buf *buf = buf_priv; + + return buf->vaddr; +} + +static unsigned int vb2_dc_num_users(void *buf_priv) +{ + struct vb2_dc_buf *buf = buf_priv; + + return atomic_read(&buf->refcount); +} + +/*********************************************/ +/* callbacks for MMAP buffers */ +/*********************************************/ + +static void vb2_dc_put(void *buf_priv) +{ + struct vb2_dc_buf *buf = buf_priv; + + if (!atomic_dec_and_test(&buf->refcount)) + return; + + dma_free_coherent(buf->dev, buf->size, buf->vaddr, buf->dma_addr); + kfree(buf); +}
static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size) { @@ -63,40 +105,6 @@ static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size) return buf; }
-static void vb2_dc_put(void *buf_priv) -{ - struct vb2_dc_buf *buf = buf_priv; - - if (atomic_dec_and_test(&buf->refcount)) { - dma_free_coherent(buf->dev, buf->size, buf->vaddr, - buf->dma_addr); - kfree(buf); - } -} - -static void *vb2_dc_cookie(void *buf_priv) -{ - struct vb2_dc_buf *buf = buf_priv; - - return &buf->dma_addr; -} - -static void *vb2_dc_vaddr(void *buf_priv) -{ - struct vb2_dc_buf *buf = buf_priv; - if (!buf) - return NULL; - - return buf->vaddr; -} - -static unsigned int vb2_dc_num_users(void *buf_priv) -{ - struct vb2_dc_buf *buf = buf_priv; - - return atomic_read(&buf->refcount); -} - static int vb2_dc_mmap(void *buf_priv, struct vm_area_struct *vma) { struct vb2_dc_buf *buf = buf_priv; @@ -110,6 +118,10 @@ static int vb2_dc_mmap(void *buf_priv, struct vm_area_struct *vma) &vb2_common_vm_ops, &buf->handler); }
+/*********************************************/ +/* callbacks for USERPTR buffers */ +/*********************************************/ + static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, unsigned long size, int write) { @@ -148,6 +160,10 @@ static void vb2_dc_put_userptr(void *mem_priv) kfree(buf); }
+/*********************************************/ +/* DMA CONTIG exported functions */ +/*********************************************/ + const struct vb2_mem_ops vb2_dma_contig_memops = { .alloc = vb2_dc_alloc, .put = vb2_dc_put,
This patch introduces usage of dma_map_sg to map memory behind a userspace pointer to a device as dma-contiguous mapping.
This patch contains some of the code kindly provided by Marek Szyprowski m.szyprowski@samsung.com and Kamil Debski k.debski@samsung.com and Andrzej Pietrasiewicz andrzej.p@samsung.com. Kind thanks for bug reports from Laurent Pinchart laurent.pinchart@ideasonboard.com and Seung-Woo Kim sw0312.kim@samsung.com.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Acked-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/media/video/videobuf2-dma-contig.c | 226 ++++++++++++++++++++++++++-- 1 file changed, 210 insertions(+), 16 deletions(-)
diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c index daac2b2..8486e06 100644 --- a/drivers/media/video/videobuf2-dma-contig.c +++ b/drivers/media/video/videobuf2-dma-contig.c @@ -11,6 +11,8 @@ */
#include <linux/module.h> +#include <linux/scatterlist.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/dma-mapping.h>
@@ -27,6 +29,8 @@ struct vb2_dc_buf { void *vaddr; unsigned long size; dma_addr_t dma_addr; + enum dma_data_direction dma_dir; + struct sg_table *dma_sgt;
/* MMAP related */ struct vb2_vmarea_handler handler; @@ -37,6 +41,44 @@ struct vb2_dc_buf { };
/*********************************************/ +/* scatterlist table functions */ +/*********************************************/ + + +static void vb2_dc_sgt_foreach_page(struct sg_table *sgt, + void (*cb)(struct page *pg)) +{ + struct scatterlist *s; + unsigned int i; + + for_each_sg(sgt->sgl, s, sgt->orig_nents, i) { + struct page *page = sg_page(s); + unsigned int n_pages = PAGE_ALIGN(s->offset + s->length) + >> PAGE_SHIFT; + unsigned int j; + + for (j = 0; j < n_pages; ++j, ++page) + cb(page); + } +} + +static unsigned long vb2_dc_get_contiguous_size(struct sg_table *sgt) +{ + struct scatterlist *s; + dma_addr_t expected = sg_dma_address(sgt->sgl); + unsigned int i; + unsigned long size = 0; + + for_each_sg(sgt->sgl, s, sgt->nents, i) { + if (sg_dma_address(s) != expected) + break; + expected = sg_dma_address(s) + sg_dma_len(s); + size += sg_dma_len(s); + } + return size; +} + +/*********************************************/ /* callbacks for all buffers */ /*********************************************/
@@ -122,42 +164,194 @@ static int vb2_dc_mmap(void *buf_priv, struct vm_area_struct *vma) /* callbacks for USERPTR buffers */ /*********************************************/
+static inline int vma_is_io(struct vm_area_struct *vma) +{ + return !!(vma->vm_flags & (VM_IO | VM_PFNMAP)); +} + +static int vb2_dc_get_user_pages(unsigned long start, struct page **pages, + int n_pages, struct vm_area_struct *vma, int write) +{ + if (vma_is_io(vma)) { + unsigned int i; + + for (i = 0; i < n_pages; ++i, start += PAGE_SIZE) { + unsigned long pfn; + int ret = follow_pfn(vma, start, &pfn); + + if (ret) { + pr_err("no page for address %lu\n", start); + return ret; + } + pages[i] = pfn_to_page(pfn); + } + } else { + int n; + + n = get_user_pages(current, current->mm, start & PAGE_MASK, + n_pages, write, 1, pages, NULL); + /* negative error means that no page was pinned */ + n = max(n, 0); + if (n != n_pages) { + pr_err("got only %d of %d user pages\n", n, n_pages); + while (n) + put_page(pages[--n]); + return -EFAULT; + } + } + + return 0; +} + +static void vb2_dc_put_dirty_page(struct page *page) +{ + set_page_dirty_lock(page); + put_page(page); +} + +static void vb2_dc_put_userptr(void *buf_priv) +{ + struct vb2_dc_buf *buf = buf_priv; + struct sg_table *sgt = buf->dma_sgt; + + dma_unmap_sg(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir); + if (!vma_is_io(buf->vma)) + vb2_dc_sgt_foreach_page(sgt, vb2_dc_put_dirty_page); + + sg_free_table(sgt); + kfree(sgt); + vb2_put_vma(buf->vma); + kfree(buf); +} + static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, - unsigned long size, int write) + unsigned long size, int write) { + struct vb2_dc_conf *conf = alloc_ctx; struct vb2_dc_buf *buf; + unsigned long start; + unsigned long end; + unsigned long offset; + struct page **pages; + int n_pages; + int ret = 0; struct vm_area_struct *vma; - dma_addr_t dma_addr = 0; - int ret; + struct sg_table *sgt; + unsigned long contig_size;
buf = kzalloc(sizeof *buf, GFP_KERNEL); if (!buf) return ERR_PTR(-ENOMEM);
- ret = vb2_get_contig_userptr(vaddr, size, &vma, &dma_addr); + buf->dev = conf->dev; + buf->dma_dir = write ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + start = vaddr & PAGE_MASK; + offset = vaddr & ~PAGE_MASK; + end = PAGE_ALIGN(vaddr + size); + n_pages = (end - start) >> PAGE_SHIFT; + + pages = kmalloc(n_pages * sizeof(pages[0]), GFP_KERNEL); + if (!pages) { + ret = -ENOMEM; + pr_err("failed to allocate pages table\n"); + goto fail_buf; + } + + /* current->mm->mmap_sem is taken by videobuf2 core */ + vma = find_vma(current->mm, vaddr); + if (!vma) { + pr_err("no vma for address %lu\n", vaddr); + ret = -EFAULT; + goto fail_pages; + } + + if (vma->vm_end < vaddr + size) { + pr_err("vma at %lu is too small for %lu bytes\n", vaddr, size); + ret = -EFAULT; + goto fail_pages; + } + + buf->vma = vb2_get_vma(vma); + if (!buf->vma) { + pr_err("failed to copy vma\n"); + ret = -ENOMEM; + goto fail_pages; + } + + /* extract page list from userspace mapping */ + ret = vb2_dc_get_user_pages(start, pages, n_pages, vma, write); if (ret) { - printk(KERN_ERR "Failed acquiring VMA for vaddr 0x%08lx\n", - vaddr); - kfree(buf); - return ERR_PTR(ret); + pr_err("failed to get user pages\n"); + goto fail_vma; + } + + sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); + if (!sgt) { + pr_err("failed to allocate sg table\n"); + ret = -ENOMEM; + goto fail_get_user_pages; + } + + ret = sg_alloc_table_from_pages(sgt, pages, n_pages, + offset, size, GFP_KERNEL); + if (ret) { + pr_err("failed to initialize sg table\n"); + goto fail_sgt; + } + + /* pages are no longer needed */ + kfree(pages); + pages = NULL; + + sgt->nents = dma_map_sg(buf->dev, sgt->sgl, sgt->orig_nents, + buf->dma_dir); + if (sgt->nents <= 0) { + pr_err("failed to map scatterlist\n"); + ret = -EIO; + goto fail_sgt_init; + } + + contig_size = vb2_dc_get_contiguous_size(sgt); + if (contig_size < size) { + pr_err("contiguous mapping is too small %lu/%lu\n", + contig_size, size); + ret = -EFAULT; + goto fail_map_sg; }
+ buf->dma_addr = sg_dma_address(sgt->sgl); buf->size = size; - buf->dma_addr = dma_addr; - buf->vma = vma; + buf->dma_sgt = sgt;
return buf; -}
-static void vb2_dc_put_userptr(void *mem_priv) -{ - struct vb2_dc_buf *buf = mem_priv; +fail_map_sg: + dma_unmap_sg(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir);
- if (!buf) - return; +fail_sgt_init: + if (!vma_is_io(buf->vma)) + vb2_dc_sgt_foreach_page(sgt, put_page); + sg_free_table(sgt); + +fail_sgt: + kfree(sgt);
+fail_get_user_pages: + if (pages && !vma_is_io(buf->vma)) + while (n_pages) + put_page(pages[--n_pages]); + +fail_vma: vb2_put_vma(buf->vma); + +fail_pages: + kfree(pages); /* kfree is NULL-proof */ + +fail_buf: kfree(buf); + + return ERR_PTR(ret); }
/*********************************************/
From: Marek Szyprowski m.szyprowski@samsung.com
This patch adds support for prepare/finish callbacks in VB2 allocators. These callback are used for buffer flushing.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com Acked-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/media/video/videobuf2-core.c | 11 +++++++++++ include/media/videobuf2-core.h | 7 +++++++ 2 files changed, 18 insertions(+)
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 901bc56..05da3b4 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -844,6 +844,7 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) { struct vb2_queue *q = vb->vb2_queue; unsigned long flags; + unsigned int plane;
if (vb->state != VB2_BUF_STATE_ACTIVE) return; @@ -854,6 +855,10 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) dprintk(4, "Done processing on buffer %d, state: %d\n", vb->v4l2_buf.index, vb->state);
+ /* sync buffers */ + for (plane = 0; plane < vb->num_planes; ++plane) + call_memop(q, finish, vb->planes[plane].mem_priv); + /* Add the buffer to the done buffers list */ spin_lock_irqsave(&q->done_lock, flags); vb->state = state; @@ -1148,9 +1153,15 @@ err: static void __enqueue_in_driver(struct vb2_buffer *vb) { struct vb2_queue *q = vb->vb2_queue; + unsigned int plane;
vb->state = VB2_BUF_STATE_ACTIVE; atomic_inc(&q->queued_count); + + /* sync buffers */ + for (plane = 0; plane < vb->num_planes; ++plane) + call_memop(q, prepare, vb->planes[plane].mem_priv); + q->ops->buf_queue(vb); }
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 84f11f2..c306fec 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -56,6 +56,10 @@ struct vb2_fileio_data; * dmabuf * @unmap_dmabuf: releases access control to the dmabuf - allocator is notified * that this driver is done using the dmabuf for now + * @prepare: called everytime the buffer is passed from userspace to the + * driver, usefull for cache synchronisation, optional + * @finish: called everytime the buffer is passed back from the driver + * to the userspace, also optional * @vaddr: return a kernel virtual address to a given memory buffer * associated with the passed private structure or NULL if no * such mapping exists @@ -82,6 +86,9 @@ struct vb2_mem_ops { unsigned long size, int write); void (*put_userptr)(void *buf_priv);
+ void (*prepare)(void *buf_priv); + void (*finish)(void *buf_priv); + void *(*attach_dmabuf)(void *alloc_ctx, struct dma_buf *dbuf, unsigned long size, int write); void (*detach_dmabuf)(void *buf_priv);
Some small typos...
On Tue October 2 2012 16:27:20 Tomasz Stanislawski wrote:
From: Marek Szyprowski m.szyprowski@samsung.com
This patch adds support for prepare/finish callbacks in VB2 allocators. These callback are used for buffer flushing.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com Acked-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
drivers/media/video/videobuf2-core.c | 11 +++++++++++ include/media/videobuf2-core.h | 7 +++++++ 2 files changed, 18 insertions(+)
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 901bc56..05da3b4 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -844,6 +844,7 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) { struct vb2_queue *q = vb->vb2_queue; unsigned long flags;
- unsigned int plane;
if (vb->state != VB2_BUF_STATE_ACTIVE) return; @@ -854,6 +855,10 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) dprintk(4, "Done processing on buffer %d, state: %d\n", vb->v4l2_buf.index, vb->state);
- /* sync buffers */
- for (plane = 0; plane < vb->num_planes; ++plane)
call_memop(q, finish, vb->planes[plane].mem_priv);
- /* Add the buffer to the done buffers list */ spin_lock_irqsave(&q->done_lock, flags); vb->state = state;
@@ -1148,9 +1153,15 @@ err: static void __enqueue_in_driver(struct vb2_buffer *vb) { struct vb2_queue *q = vb->vb2_queue;
- unsigned int plane;
vb->state = VB2_BUF_STATE_ACTIVE; atomic_inc(&q->queued_count);
- /* sync buffers */
- for (plane = 0; plane < vb->num_planes; ++plane)
call_memop(q, prepare, vb->planes[plane].mem_priv);
- q->ops->buf_queue(vb);
} diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 84f11f2..c306fec 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -56,6 +56,10 @@ struct vb2_fileio_data;
dmabuf
- @unmap_dmabuf: releases access control to the dmabuf - allocator is notified
that this driver is done using the dmabuf for now
- @prepare: called everytime the buffer is passed from userspace to the
driver, usefull for cache synchronisation, optional
everytime -> every time usefull -> useful
- @finish: called everytime the buffer is passed back from the driver
everytime -> every time
to the userspace, also optional
- @vaddr: return a kernel virtual address to a given memory buffer
associated with the passed private structure or NULL if no
such mapping exists
@@ -82,6 +86,9 @@ struct vb2_mem_ops { unsigned long size, int write); void (*put_userptr)(void *buf_priv);
- void (*prepare)(void *buf_priv);
- void (*finish)(void *buf_priv);
- void *(*attach_dmabuf)(void *alloc_ctx, struct dma_buf *dbuf, unsigned long size, int write); void (*detach_dmabuf)(void *buf_priv);
From: Marek Szyprowski m.szyprowski@samsung.com
Add prepare/finish callbacks to vb2-dma-contig allocator.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com Acked-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/media/video/videobuf2-dma-contig.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c index 8486e06..494a824 100644 --- a/drivers/media/video/videobuf2-dma-contig.c +++ b/drivers/media/video/videobuf2-dma-contig.c @@ -103,6 +103,28 @@ static unsigned int vb2_dc_num_users(void *buf_priv) return atomic_read(&buf->refcount); }
+static void vb2_dc_prepare(void *buf_priv) +{ + struct vb2_dc_buf *buf = buf_priv; + struct sg_table *sgt = buf->dma_sgt; + + if (!sgt) + return; + + dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); +} + +static void vb2_dc_finish(void *buf_priv) +{ + struct vb2_dc_buf *buf = buf_priv; + struct sg_table *sgt = buf->dma_sgt; + + if (!sgt) + return; + + dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); +} + /*********************************************/ /* callbacks for MMAP buffers */ /*********************************************/ @@ -366,6 +388,8 @@ const struct vb2_mem_ops vb2_dma_contig_memops = { .mmap = vb2_dc_mmap, .get_userptr = vb2_dc_get_userptr, .put_userptr = vb2_dc_put_userptr, + .prepare = vb2_dc_prepare, + .finish = vb2_dc_finish, .num_users = vb2_dc_num_users, }; EXPORT_SYMBOL_GPL(vb2_dma_contig_memops);
From: Sumit Semwal sumit.semwal@ti.com
This patch makes changes for adding dma-contig as a dma_buf user. It provides function implementations for the {attach, detach, map, unmap}_dmabuf() mem_ops of DMABUF memory type.
Signed-off-by: Sumit Semwal sumit.semwal@ti.com Signed-off-by: Sumit Semwal sumit.semwal@linaro.org [author of the original patch] Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com [integration with refactored dma-contig allocator] Acked-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/media/video/Kconfig | 1 + drivers/media/video/videobuf2-dma-contig.c | 120 +++++++++++++++++++++++++++- 2 files changed, 119 insertions(+), 2 deletions(-)
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index c558d37..ec089da 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -54,6 +54,7 @@ config VIDEOBUF2_MEMOPS config VIDEOBUF2_DMA_CONTIG select VIDEOBUF2_CORE select VIDEOBUF2_MEMOPS + select DMA_SHARED_BUFFER tristate
config VIDEOBUF2_VMALLOC diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c index 494a824..a5804cf 100644 --- a/drivers/media/video/videobuf2-dma-contig.c +++ b/drivers/media/video/videobuf2-dma-contig.c @@ -10,6 +10,7 @@ * the Free Software Foundation. */
+#include <linux/dma-buf.h> #include <linux/module.h> #include <linux/scatterlist.h> #include <linux/sched.h> @@ -38,6 +39,9 @@ struct vb2_dc_buf {
/* USERPTR related */ struct vm_area_struct *vma; + + /* DMABUF related */ + struct dma_buf_attachment *db_attach; };
/*********************************************/ @@ -108,7 +112,8 @@ static void vb2_dc_prepare(void *buf_priv) struct vb2_dc_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt;
- if (!sgt) + /* DMABUF exporter will flush the cache for us */ + if (!sgt || buf->db_attach) return;
dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); @@ -119,7 +124,8 @@ static void vb2_dc_finish(void *buf_priv) struct vb2_dc_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt;
- if (!sgt) + /* DMABUF exporter will flush the cache for us */ + if (!sgt || buf->db_attach) return;
dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); @@ -377,6 +383,112 @@ fail_buf: }
/*********************************************/ +/* callbacks for DMABUF buffers */ +/*********************************************/ + +static int vb2_dc_map_dmabuf(void *mem_priv) +{ + struct vb2_dc_buf *buf = mem_priv; + struct sg_table *sgt; + unsigned long contig_size; + + if (WARN_ON(!buf->db_attach)) { + pr_err("trying to pin a non attached buffer\n"); + return -EINVAL; + } + + if (WARN_ON(buf->dma_sgt)) { + pr_err("dmabuf buffer is already pinned\n"); + return 0; + } + + /* get the associated scatterlist for this buffer */ + sgt = dma_buf_map_attachment(buf->db_attach, buf->dma_dir); + if (IS_ERR_OR_NULL(sgt)) { + pr_err("Error getting dmabuf scatterlist\n"); + return -EINVAL; + } + + /* checking if dmabuf is big enough to store contiguous chunk */ + contig_size = vb2_dc_get_contiguous_size(sgt); + if (contig_size < buf->size) { + pr_err("contiguous chunk is too small %lu/%lu b\n", + contig_size, buf->size); + dma_buf_unmap_attachment(buf->db_attach, sgt, buf->dma_dir); + return -EFAULT; + } + + buf->dma_addr = sg_dma_address(sgt->sgl); + buf->dma_sgt = sgt; + + return 0; +} + +static void vb2_dc_unmap_dmabuf(void *mem_priv) +{ + struct vb2_dc_buf *buf = mem_priv; + struct sg_table *sgt = buf->dma_sgt; + + if (WARN_ON(!buf->db_attach)) { + pr_err("trying to unpin a not attached buffer\n"); + return; + } + + if (WARN_ON(!sgt)) { + pr_err("dmabuf buffer is already unpinned\n"); + return; + } + + dma_buf_unmap_attachment(buf->db_attach, sgt, buf->dma_dir); + + buf->dma_addr = 0; + buf->dma_sgt = NULL; +} + +static void vb2_dc_detach_dmabuf(void *mem_priv) +{ + struct vb2_dc_buf *buf = mem_priv; + + /* if vb2 works correctly you should never detach mapped buffer */ + if (WARN_ON(buf->dma_addr)) + vb2_dc_unmap_dmabuf(buf); + + /* detach this attachment */ + dma_buf_detach(buf->db_attach->dmabuf, buf->db_attach); + kfree(buf); +} + +static void *vb2_dc_attach_dmabuf(void *alloc_ctx, struct dma_buf *dbuf, + unsigned long size, int write) +{ + struct vb2_dc_conf *conf = alloc_ctx; + struct vb2_dc_buf *buf; + struct dma_buf_attachment *dba; + + if (dbuf->size < size) + return ERR_PTR(-EFAULT); + + buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + buf->dev = conf->dev; + /* create attachment for the dmabuf with the user device */ + dba = dma_buf_attach(dbuf, buf->dev); + if (IS_ERR(dba)) { + pr_err("failed to attach dmabuf\n"); + kfree(buf); + return dba; + } + + buf->dma_dir = write ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + buf->size = size; + buf->db_attach = dba; + + return buf; +} + +/*********************************************/ /* DMA CONTIG exported functions */ /*********************************************/
@@ -390,6 +502,10 @@ const struct vb2_mem_ops vb2_dma_contig_memops = { .put_userptr = vb2_dc_put_userptr, .prepare = vb2_dc_prepare, .finish = vb2_dc_finish, + .map_dmabuf = vb2_dc_map_dmabuf, + .unmap_dmabuf = vb2_dc_unmap_dmabuf, + .attach_dmabuf = vb2_dc_attach_dmabuf, + .detach_dmabuf = vb2_dc_detach_dmabuf, .num_users = vb2_dc_num_users, }; EXPORT_SYMBOL_GPL(vb2_dma_contig_memops);
This patch adds support for importing DMABUF files for vmalloc allocator in Videobuf2.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Acked-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/media/video/videobuf2-vmalloc.c | 56 +++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+)
diff --git a/drivers/media/video/videobuf2-vmalloc.c b/drivers/media/video/videobuf2-vmalloc.c index 6b5ca6c..e91449c 100644 --- a/drivers/media/video/videobuf2-vmalloc.c +++ b/drivers/media/video/videobuf2-vmalloc.c @@ -29,6 +29,7 @@ struct vb2_vmalloc_buf { unsigned int n_pages; atomic_t refcount; struct vb2_vmarea_handler handler; + struct dma_buf *dbuf; };
static void vb2_vmalloc_put(void *buf_priv); @@ -206,11 +207,66 @@ static int vb2_vmalloc_mmap(void *buf_priv, struct vm_area_struct *vma) return 0; }
+/*********************************************/ +/* callbacks for DMABUF buffers */ +/*********************************************/ + +static int vb2_vmalloc_map_dmabuf(void *mem_priv) +{ + struct vb2_vmalloc_buf *buf = mem_priv; + + buf->vaddr = dma_buf_vmap(buf->dbuf); + + return buf->vaddr ? 0 : -EFAULT; +} + +static void vb2_vmalloc_unmap_dmabuf(void *mem_priv) +{ + struct vb2_vmalloc_buf *buf = mem_priv; + + dma_buf_vunmap(buf->dbuf, buf->vaddr); + buf->vaddr = NULL; +} + +static void vb2_vmalloc_detach_dmabuf(void *mem_priv) +{ + struct vb2_vmalloc_buf *buf = mem_priv; + + if (buf->vaddr) + dma_buf_vunmap(buf->dbuf, buf->vaddr); + + kfree(buf); +} + +static void *vb2_vmalloc_attach_dmabuf(void *alloc_ctx, struct dma_buf *dbuf, + unsigned long size, int write) +{ + struct vb2_vmalloc_buf *buf; + + if (dbuf->size < size) + return ERR_PTR(-EFAULT); + + buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + buf->dbuf = dbuf; + buf->write = write; + buf->size = size; + + return buf; +} + + const struct vb2_mem_ops vb2_vmalloc_memops = { .alloc = vb2_vmalloc_alloc, .put = vb2_vmalloc_put, .get_userptr = vb2_vmalloc_get_userptr, .put_userptr = vb2_vmalloc_put_userptr, + .map_dmabuf = vb2_vmalloc_map_dmabuf, + .unmap_dmabuf = vb2_vmalloc_unmap_dmabuf, + .attach_dmabuf = vb2_vmalloc_attach_dmabuf, + .detach_dmabuf = vb2_vmalloc_detach_dmabuf, .vaddr = vb2_vmalloc_vaddr, .mmap = vb2_vmalloc_mmap, .num_users = vb2_vmalloc_num_users,
This patch enhances VIVI driver with a support for importing a buffer from DMABUF file descriptors.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/media/video/vivi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index a05494b..4fb38b9 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -1284,7 +1284,7 @@ static int __init vivi_create_instance(int inst) q = &dev->vb_vidq; memset(q, 0, sizeof(dev->vb_vidq)); q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; q->drv_priv = dev; q->buf_struct_size = sizeof(struct vivi_buffer); q->ops = &vivi_video_qops;
This patch enhances s5p-tv with support for DMABUF importing via V4L2_MEMORY_DMABUF memory type.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/media/video/s5p-tv/mixer_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c index 6c74b05..2c299db 100644 --- a/drivers/media/video/s5p-tv/mixer_video.c +++ b/drivers/media/video/s5p-tv/mixer_video.c @@ -1078,7 +1078,7 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev,
layer->vb_queue = (struct vb2_queue) { .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - .io_modes = VB2_MMAP | VB2_USERPTR, + .io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF, .drv_priv = layer, .buf_struct_size = sizeof(struct mxr_buffer), .ops = &mxr_video_qops,
This patch enhances s5p-fimc with support for DMABUF importing via V4L2_MEMORY_DMABUF memory type.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Acked-by: Sylwester Nawrocki s.nawrocki@samsung.com --- drivers/media/video/s5p-fimc/fimc-capture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 8e413dd..3fcaf7d 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -1634,7 +1634,7 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, q = &fimc->vid_cap.vbq; memset(q, 0, sizeof(*q)); q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - q->io_modes = VB2_MMAP | VB2_USERPTR; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; q->drv_priv = fimc->vid_cap.ctx; q->ops = &fimc_capture_qops; q->mem_ops = &vb2_dma_contig_memops;
From: Marek Szyprowski m.szyprowski@samsung.com
Let mmap method to use dma_mmap_coherent call. Moreover, this patch removes vb2_mmap_pfn_range from videobuf2 helpers as it was suggested by Laurent Pinchart. The function is no longer used in vb2 code.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Acked-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/media/video/videobuf2-dma-contig.c | 28 +++++++++++++++++-- drivers/media/video/videobuf2-memops.c | 40 ---------------------------- include/media/videobuf2-memops.h | 5 ---- 3 files changed, 26 insertions(+), 47 deletions(-)
diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c index a5804cf..0e065ce 100644 --- a/drivers/media/video/videobuf2-dma-contig.c +++ b/drivers/media/video/videobuf2-dma-contig.c @@ -178,14 +178,38 @@ static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size) static int vb2_dc_mmap(void *buf_priv, struct vm_area_struct *vma) { struct vb2_dc_buf *buf = buf_priv; + int ret;
if (!buf) { printk(KERN_ERR "No buffer to map\n"); return -EINVAL; }
- return vb2_mmap_pfn_range(vma, buf->dma_addr, buf->size, - &vb2_common_vm_ops, &buf->handler); + /* + * dma_mmap_* uses vm_pgoff as in-buffer offset, but we want to + * map whole buffer + */ + vma->vm_pgoff = 0; + + ret = dma_mmap_coherent(buf->dev, vma, buf->vaddr, + buf->dma_addr, buf->size); + + if (ret) { + pr_err("Remapping memory failed, error: %d\n", ret); + return ret; + } + + vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED; + vma->vm_private_data = &buf->handler; + vma->vm_ops = &vb2_common_vm_ops; + + vma->vm_ops->open(vma); + + pr_debug("%s: mapped dma addr 0x%08lx at 0x%08lx, size %ld\n", + __func__, (unsigned long)buf->dma_addr, vma->vm_start, + buf->size); + + return 0; }
/*********************************************/ diff --git a/drivers/media/video/videobuf2-memops.c b/drivers/media/video/videobuf2-memops.c index 504cd4c..81c1ad8 100644 --- a/drivers/media/video/videobuf2-memops.c +++ b/drivers/media/video/videobuf2-memops.c @@ -137,46 +137,6 @@ int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, EXPORT_SYMBOL_GPL(vb2_get_contig_userptr);
/** - * vb2_mmap_pfn_range() - map physical pages to userspace - * @vma: virtual memory region for the mapping - * @paddr: starting physical address of the memory to be mapped - * @size: size of the memory to be mapped - * @vm_ops: vm operations to be assigned to the created area - * @priv: private data to be associated with the area - * - * Returns 0 on success. - */ -int vb2_mmap_pfn_range(struct vm_area_struct *vma, unsigned long paddr, - unsigned long size, - const struct vm_operations_struct *vm_ops, - void *priv) -{ - int ret; - - size = min_t(unsigned long, vma->vm_end - vma->vm_start, size); - - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - ret = remap_pfn_range(vma, vma->vm_start, paddr >> PAGE_SHIFT, - size, vma->vm_page_prot); - if (ret) { - printk(KERN_ERR "Remapping memory failed, error: %d\n", ret); - return ret; - } - - vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED; - vma->vm_private_data = priv; - vma->vm_ops = vm_ops; - - vma->vm_ops->open(vma); - - pr_debug("%s: mapped paddr 0x%08lx at 0x%08lx, size %ld\n", - __func__, paddr, vma->vm_start, size); - - return 0; -} -EXPORT_SYMBOL_GPL(vb2_mmap_pfn_range); - -/** * vb2_common_vm_open() - increase refcount of the vma * @vma: virtual memory region for the mapping * diff --git a/include/media/videobuf2-memops.h b/include/media/videobuf2-memops.h index 84e1f6c..f05444c 100644 --- a/include/media/videobuf2-memops.h +++ b/include/media/videobuf2-memops.h @@ -33,11 +33,6 @@ extern const struct vm_operations_struct vb2_common_vm_ops; int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, struct vm_area_struct **res_vma, dma_addr_t *res_pa);
-int vb2_mmap_pfn_range(struct vm_area_struct *vma, unsigned long paddr, - unsigned long size, - const struct vm_operations_struct *vm_ops, - void *priv); - struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma); void vb2_put_vma(struct vm_area_struct *vma);
This patch adds description and usage examples for exporting DMABUF file descriptor in V4L2.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com CC: linux-doc@vger.kernel.org --- Documentation/DocBook/media/v4l/compat.xml | 3 + Documentation/DocBook/media/v4l/io.xml | 3 + Documentation/DocBook/media/v4l/v4l2.xml | 1 + Documentation/DocBook/media/v4l/vidioc-expbuf.xml | 212 +++++++++++++++++++++ 4 files changed, 219 insertions(+) create mode 100644 Documentation/DocBook/media/v4l/vidioc-expbuf.xml
diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index a46f95b..f0512aa 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2625,6 +2625,9 @@ ioctls.</para> <para>Importing DMABUF file descriptors as a new IO method described in <xref linkend="dmabuf" />.</para> </listitem> + <listitem> + <para>Exporting DMABUF files using &VIDIOC-EXPBUF; ioctl.</para> + </listitem> </itemizedlist> </section>
diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml index 5b58657..476f448 100644 --- a/Documentation/DocBook/media/v4l/io.xml +++ b/Documentation/DocBook/media/v4l/io.xml @@ -488,6 +488,9 @@ DMA buffer from userspace using a file descriptor previously exported for a different or the same device (known as the importer role), or both. This section describes the DMABUF importer role API in V4L2.</para>
+ <para>Refer to <link linked="vidioc-expbuf"> DMABUF exporting </link> for +details about exporting a V4L2 buffers as DMABUF file descriptors.</para> + <para>Input and output devices support the streaming I/O method when the <constant>V4L2_CAP_STREAMING</constant> flag in the <structfield>capabilities</structfield> field of &v4l2-capability; returned by diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index eee6908..d8c2597 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -543,6 +543,7 @@ and discussions on the V4L mailing list.</revremark> &sub-enuminput; &sub-enumoutput; &sub-enumstd; + &sub-expbuf; &sub-g-audio; &sub-g-audioout; &sub-g-crop; diff --git a/Documentation/DocBook/media/v4l/vidioc-expbuf.xml b/Documentation/DocBook/media/v4l/vidioc-expbuf.xml new file mode 100644 index 0000000..bf28e7d --- /dev/null +++ b/Documentation/DocBook/media/v4l/vidioc-expbuf.xml @@ -0,0 +1,212 @@ +<refentry id="vidioc-expbuf"> + + <refmeta> + <refentrytitle>ioctl VIDIOC_EXPBUF</refentrytitle> + &manvol; + </refmeta> + + <refnamediv> + <refname>VIDIOC_EXPBUF</refname> + <refpurpose>Export a buffer as a DMABUF file descriptor.</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <funcsynopsis> + <funcprototype> + <funcdef>int <function>ioctl</function></funcdef> + <paramdef>int <parameter>fd</parameter></paramdef> + <paramdef>int <parameter>request</parameter></paramdef> + <paramdef>struct v4l2_exportbuffer *<parameter>argp</parameter></paramdef> + </funcprototype> + </funcsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Arguments</title> + + <variablelist> + <varlistentry> + <term><parameter>fd</parameter></term> + <listitem> + <para>&fd;</para> + </listitem> + </varlistentry> + <varlistentry> + <term><parameter>request</parameter></term> + <listitem> + <para>VIDIOC_EXPBUF</para> + </listitem> + </varlistentry> + <varlistentry> + <term><parameter>argp</parameter></term> + <listitem> + <para></para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Description</title> + + <note> + <title>Experimental</title> + <para>This is an <link linkend="experimental"> experimental </link> + interface and may change in the future.</para> + </note> + +<para>This ioctl is an extension to the <link linkend="mmap">memory +mapping</link> I/O method therefore it is available only for +<constant>V4L2_MEMORY_MMAP</constant> buffers. It can be used to export a +buffer as a DMABUF file at any time after buffers have been allocated with the +&VIDIOC-REQBUFS; ioctl.</para> + +<para> To export a buffer, applicationis fill &v4l2-exportbuffer;. The +<structfield> type </structfield> field is set to the same buffer type as was +previously used with &v4l2-requestbuffers;<structfield> type </structfield>. +Applications must also set the <structfield> index </structfield> field. Valid +index numbers range from zero to the number of buffers allocated with +&VIDIOC-REQBUFS; (&v4l2-requestbuffers;<structfield> count </structfield>) +minus one. For multi plane API, applications set the <structfield> plane +</structfield> field to the index of the plane to be exported. Valid planes +range from zero to the maximal number of valid planes for currently active +format. For single plane API, applications must set <structfield> plane +</structfield> to zero. Additional flags may be posted in the <structfield> +flags </structfield> field. Refer to a manual for open() for details. +Currently only O_CLOEXEC is supported. All other fields must be set to zero. +In a case of multi-planar API, every plane is exported separately using +multiple <constant> VIDIOC_EXPBUF </constant> calls. </para> + +<para> After calling <constant>VIDIOC_EXPBUF</constant> the <structfield> fd +</structfield> field will be set by a driver. This is a DMABUF file +descriptor. The application may pass it to other API. Refer to <link +linkend="dmabuf">DMABUF importing</link> for details about importing DMABUF +files into V4L2 nodes. A developer is encouraged to close a DMABUF file when it +is no longer used to avoid a resource leak. </para> + + </refsect1> + <refsect1> + <section> + <title>Examples</title> + + <example> + <title>Exporting a buffer.</title> + <programlisting> +int buffer_export(int v4lfd, &v4l2-buf-type; bt, int index, int *dmafd) +{ + &v4l2-exportbuffer; expbuf; + + memset(&expbuf, 0, sizeof expbuf); + expbuf.type = bt; + expbuf.index = index; + if (ioctl(v4lfd, &VIDIOC-EXPBUF;, &expbuf) == -1) { + perror("VIDIOC_EXPBUF"); + return -1; + } + + *dmafd = expbuf.fd; + + return 0; +} + </programlisting> + </example> + + <example> + <title>Exporting a buffer using multi plane API.</title> + <programlisting> +int buffer_export_mp(int v4lfd, &v4l2-buf-type; bt, int index, + int dmafd[], int n_planes) +{ + int i; + + for (i = 0; i < n_planes; ++i) { + &v4l2-exportbuffer; expbuf; + + memset(&expbuf, 0, sizeof expbuf); + expbuf.type = bt; + expbuf.index = index; + expbuf.plane = i; + if (ioctl(v4lfd, &VIDIOC-EXPBUF;, &expbuf) == -1) { + perror("VIDIOC_EXPBUF"); + while (i) + close(dmafd[--i]); + return -1; + } + dmafd[i] = expbuf.fd; + } + + return 0; +} + </programlisting> + </example> + </section> + </refsect1> + + <refsect1> + <table pgwide="1" frame="none" id="v4l2-exportbuffer"> + <title>struct <structname>v4l2_exportbuffer</structname></title> + <tgroup cols="3"> + &cs-str; + <tbody valign="top"> + <row> + <entry>__s32</entry> + <entry><structfield>fd</structfield></entry> + <entry>The DMABUF file descriptor associated with a buffer. Set by + a driver.</entry> + </row> + <row> + <entry>__u32</entry> + <entry><structfield>flags</structfield></entry> + <entry>Flags for newly created file, currently only <constant> +O_CLOEXEC </constant> is supported, refer to the manual of open() for more +details.</entry> + </row> + <row> + <entry>__u32</entry> + <entry><structfield>type</structfield></entry> + <entry>Type of the buffer, same as &v4l2-format; +<structfield>type</structfield> or &v4l2-requestbuffers; +<structfield>type</structfield>, set by the application. See <xref +linkend="v4l2-buf-type" /></entry> + </row> + <row> + <entry>__u32</entry> + <entry><structfield>index</structfield></entry> + <entry>Number of the buffer, set by the application. This field is +only used for <link linkend="mmap">memory mapping</link> I/O and can range from +zero to the number of buffers allocated with the &VIDIOC-REQBUFS; ioctl +(&v4l2-requestbuffers; <structfield>count</structfield>) minus one.</entry> + </row> + <row> + <entry>__u32</entry> + <entry><structfield>plane</structfield></entry> + <entry>Index of the plane to be exported when using multi plane +API. Otherwise this value must be set to zero. </entry> + </row> + <row> + <entry>__u32</entry> + <entry><structfield>reserved[11]</structfield></entry> + <entry>Reserved field for future use. Must be set to zero.</entry> + </row> + </tbody> + </tgroup> + </table> + + </refsect1> + + <refsect1> + &return-value; + <variablelist> + <varlistentry> + <term><errorcode>EINVAL</errorcode></term> + <listitem> + <para>A queue is not in MMAP mode or DMABUF exporting is not +supported or <structfield> flags </structfield> or <structfield> type +</structfield> or <structfield> index </structfield> or <structfield> plane +</structfield> fields are invalid.</para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + +</refentry>
More stylistic comments and a suggestion for re-ordering the fields in the expbuf struct.
On Tue October 2 2012 16:27:28 Tomasz Stanislawski wrote:
This patch adds description and usage examples for exporting DMABUF file descriptor in V4L2.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com CC: linux-doc@vger.kernel.org
Documentation/DocBook/media/v4l/compat.xml | 3 + Documentation/DocBook/media/v4l/io.xml | 3 + Documentation/DocBook/media/v4l/v4l2.xml | 1 + Documentation/DocBook/media/v4l/vidioc-expbuf.xml | 212 +++++++++++++++++++++ 4 files changed, 219 insertions(+) create mode 100644 Documentation/DocBook/media/v4l/vidioc-expbuf.xml
diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index a46f95b..f0512aa 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2625,6 +2625,9 @@ ioctls.</para> <para>Importing DMABUF file descriptors as a new IO method described in <xref linkend="dmabuf" />.</para> </listitem>
<listitem>
<para>Exporting DMABUF files using &VIDIOC-EXPBUF; ioctl.</para>
</listitem> </itemizedlist>
</section>
diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml index 5b58657..476f448 100644 --- a/Documentation/DocBook/media/v4l/io.xml +++ b/Documentation/DocBook/media/v4l/io.xml @@ -488,6 +488,9 @@ DMA buffer from userspace using a file descriptor previously exported for a different or the same device (known as the importer role), or both. This section describes the DMABUF importer role API in V4L2.</para>
- <para>Refer to <link linked="vidioc-expbuf"> DMABUF exporting </link> for
+details about exporting a V4L2 buffers as DMABUF file descriptors.</para>
"exporting a" -> "exporting"
<para>Input and output devices support the streaming I/O method when the <constant>V4L2_CAP_STREAMING</constant> flag in the <structfield>capabilities</structfield> field of &v4l2-capability; returned by diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index eee6908..d8c2597 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -543,6 +543,7 @@ and discussions on the V4L mailing list.</revremark> &sub-enuminput; &sub-enumoutput; &sub-enumstd;
- &sub-expbuf; &sub-g-audio; &sub-g-audioout; &sub-g-crop;
diff --git a/Documentation/DocBook/media/v4l/vidioc-expbuf.xml b/Documentation/DocBook/media/v4l/vidioc-expbuf.xml new file mode 100644 index 0000000..bf28e7d --- /dev/null +++ b/Documentation/DocBook/media/v4l/vidioc-expbuf.xml @@ -0,0 +1,212 @@ +<refentry id="vidioc-expbuf">
<refmeta>
- <refentrytitle>ioctl VIDIOC_EXPBUF</refentrytitle>
- &manvol;
</refmeta>
<refnamediv>
- <refname>VIDIOC_EXPBUF</refname>
- <refpurpose>Export a buffer as a DMABUF file descriptor.</refpurpose>
</refnamediv>
<refsynopsisdiv>
<funcsynopsis>
<funcprototype>
- <funcdef>int <function>ioctl</function></funcdef>
- <paramdef>int <parameter>fd</parameter></paramdef>
- <paramdef>int <parameter>request</parameter></paramdef>
- <paramdef>struct v4l2_exportbuffer *<parameter>argp</parameter></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Arguments</title>
<variablelist>
<varlistentry>
- <term><parameter>fd</parameter></term>
<listitem>
<para>&fd;</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><parameter>request</parameter></term>
<listitem>
<para>VIDIOC_EXPBUF</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><parameter>argp</parameter></term>
<listitem>
<para></para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Description</title>
<note>
<title>Experimental</title>
<para>This is an <link linkend="experimental"> experimental </link>
interface and may change in the future.</para>
</note>
+<para>This ioctl is an extension to the <link linkend="mmap">memory +mapping</link> I/O method therefore it is available only for
"method therefore" -> "method, therefore"
+<constant>V4L2_MEMORY_MMAP</constant> buffers. It can be used to export a +buffer as a DMABUF file at any time after buffers have been allocated with the +&VIDIOC-REQBUFS; ioctl.</para>
+<para> To export a buffer, applicationis fill &v4l2-exportbuffer;. The
applicationis -> applications
+<structfield> type </structfield> field is set to the same buffer type as was +previously used with &v4l2-requestbuffers;<structfield> type </structfield>. +Applications must also set the <structfield> index </structfield> field. Valid +index numbers range from zero to the number of buffers allocated with +&VIDIOC-REQBUFS; (&v4l2-requestbuffers;<structfield> count </structfield>) +minus one. For multi plane API, applications set the <structfield> plane
"multi plane" -> "the multi-planar"
+</structfield> field to the index of the plane to be exported. Valid planes +range from zero to the maximal number of valid planes for currently active
"for" -> "for the"
+format. For single plane API, applications must set <structfield> plane
"single plane" -> "the single-planar"
+</structfield> to zero. Additional flags may be posted in the <structfield> +flags </structfield> field. Refer to a manual for open() for details. +Currently only O_CLOEXEC is supported. All other fields must be set to zero. +In a case of multi-planar API, every plane is exported separately using
"a case of" -> "the case of the"
+multiple <constant> VIDIOC_EXPBUF </constant> calls. </para>
+<para> After calling <constant>VIDIOC_EXPBUF</constant> the <structfield> fd +</structfield> field will be set by a driver. This is a DMABUF file +descriptor. The application may pass it to other API. Refer to <link
"API" -> "APIs"
or alternatively (and I like that better):
"API" -> "DMABUF-aware devices"
+linkend="dmabuf">DMABUF importing</link> for details about importing DMABUF +files into V4L2 nodes. A developer is encouraged to close a DMABUF file when it +is no longer used to avoid a resource leak. </para>
I'm not sure I like that last sentence. Perhaps something like this:
"It is recommended to close a DMABUF file when it is no longer used to allow the associated memory to be reclaimed."
</refsect1>
<refsect1>
<section>
<title>Examples</title>
<example>
<title>Exporting a buffer.</title>
<programlisting>
+int buffer_export(int v4lfd, &v4l2-buf-type; bt, int index, int *dmafd) +{
- &v4l2-exportbuffer; expbuf;
- memset(&expbuf, 0, sizeof expbuf);
sizeof expbuf -> sizeof(expbuf)
- expbuf.type = bt;
- expbuf.index = index;
- if (ioctl(v4lfd, &VIDIOC-EXPBUF;, &expbuf) == -1) {
perror("VIDIOC_EXPBUF");
return -1;
- }
- *dmafd = expbuf.fd;
- return 0;
+}
</programlisting>
</example>
<example>
<title>Exporting a buffer using multi plane API.</title>
"multi plane" -> "the multi-planar"
<programlisting>
+int buffer_export_mp(int v4lfd, &v4l2-buf-type; bt, int index,
- int dmafd[], int n_planes)
+{
- int i;
- for (i = 0; i < n_planes; ++i) {
&v4l2-exportbuffer; expbuf;
memset(&expbuf, 0, sizeof expbuf);
sizeof(expbuf)
expbuf.type = bt;
expbuf.index = index;
expbuf.plane = i;
if (ioctl(v4lfd, &VIDIOC-EXPBUF;, &expbuf) == -1) {
perror("VIDIOC_EXPBUF");
while (i)
close(dmafd[--i]);
return -1;
}
dmafd[i] = expbuf.fd;
- }
- return 0;
+}
</programlisting>
</example>
</section>
</refsect1>
<refsect1>
<table pgwide="1" frame="none" id="v4l2-exportbuffer">
<title>struct <structname>v4l2_exportbuffer</structname></title>
<tgroup cols="3">
- &cs-str;
<tbody valign="top">
<row>
<entry>__s32</entry>
<entry><structfield>fd</structfield></entry>
<entry>The DMABUF file descriptor associated with a buffer. Set by
a driver.</entry>
a -> the
</row>
<row>
<entry>__u32</entry>
<entry><structfield>flags</structfield></entry>
<entry>Flags for newly created file, currently only <constant>
for -> for the
+O_CLOEXEC </constant> is supported, refer to the manual of open() for more +details.</entry>
</row>
<row>
<entry>__u32</entry>
<entry><structfield>type</structfield></entry>
<entry>Type of the buffer, same as &v4l2-format;
+<structfield>type</structfield> or &v4l2-requestbuffers; +<structfield>type</structfield>, set by the application. See <xref +linkend="v4l2-buf-type" /></entry>
</row>
<row>
<entry>__u32</entry>
<entry><structfield>index</structfield></entry>
<entry>Number of the buffer, set by the application. This field is
+only used for <link linkend="mmap">memory mapping</link> I/O and can range from +zero to the number of buffers allocated with the &VIDIOC-REQBUFS; ioctl
This really also includes any buffers allocated with VIDIOC_CREATE_BUFS.
+(&v4l2-requestbuffers; <structfield>count</structfield>) minus one.</entry>
</row>
<row>
<entry>__u32</entry>
<entry><structfield>plane</structfield></entry>
<entry>Index of the plane to be exported when using multi plane
multi plane -> the multi-planar
+API. Otherwise this value must be set to zero. </entry>
</row>
Shouldn't type, index and plane comes first in this struct? That feels more natural.
<row>
<entry>__u32</entry>
<entry><structfield>reserved[11]</structfield></entry>
<entry>Reserved field for future use. Must be set to zero.</entry>
</row>
</tbody>
</tgroup>
</table>
</refsect1>
<refsect1>
- &return-value;
<variablelist>
<varlistentry>
- <term><errorcode>EINVAL</errorcode></term>
<listitem>
<para>A queue is not in MMAP mode or DMABUF exporting is not
+supported or <structfield> flags </structfield> or <structfield> type +</structfield> or <structfield> index </structfield> or <structfield> plane +</structfield> fields are invalid.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
+</refentry>
This patch adds extension to V4L2 api. It allow to export a mmap buffer as file descriptor. New ioctl VIDIOC_EXPBUF is added. It takes a buffer offset used by mmap and return a file descriptor on success.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/media/video/v4l2-compat-ioctl32.c | 1 + drivers/media/video/v4l2-dev.c | 1 + drivers/media/video/v4l2-ioctl.c | 10 ++++++++++ include/linux/videodev2.h | 28 ++++++++++++++++++++++++++++ include/media/v4l2-ioctl.h | 2 ++ 5 files changed, 42 insertions(+)
diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index f0b5aba..8788000 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -971,6 +971,7 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_S_FBUF32: case VIDIOC_OVERLAY32: case VIDIOC_QBUF32: + case VIDIOC_EXPBUF: case VIDIOC_DQBUF32: case VIDIOC_STREAMON32: case VIDIOC_STREAMOFF32: diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 07aeafc..c43127c 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -638,6 +638,7 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs); SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf); SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf); + SET_VALID_IOCTL(ops, VIDIOC_EXPBUF, vidioc_expbuf); SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf); SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay); SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf); diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index dffd3c9..f3ec8c0 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -458,6 +458,15 @@ static void v4l_print_buffer(const void *arg, bool write_only) tc->type, tc->flags, tc->frames, *(__u32 *)tc->userbits); }
+static void v4l_print_exportbuffer(const void *arg, bool write_only) +{ + const struct v4l2_exportbuffer *p = arg; + + pr_cont("fd=%d, type=%s, index=%u, plane=%u, flags=0x%08x\n", + p->fd, prt_names(p->type, v4l2_type_names), + p->index, p->plane, p->flags); +} + static void v4l_print_create_buffers(const void *arg, bool write_only) { const struct v4l2_create_buffers *p = arg; @@ -1947,6 +1956,7 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_STD(VIDIOC_S_FBUF, vidioc_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO), IOCTL_INFO_STD(VIDIOC_OVERLAY, vidioc_overlay, v4l_print_u32, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE), + IOCTL_INFO_STD(VIDIOC_EXPBUF, vidioc_expbuf, v4l_print_exportbuffer, 0), IOCTL_INFO_FNC(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE), IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE), IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE), diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index e04a73e..f429b6a 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -688,6 +688,33 @@ struct v4l2_buffer { #define V4L2_BUF_FLAG_NO_CACHE_INVALIDATE 0x0800 #define V4L2_BUF_FLAG_NO_CACHE_CLEAN 0x1000
+/** + * struct v4l2_exportbuffer - export of video buffer as DMABUF file descriptor + * + * @fd: file descriptor associated with DMABUF (set by driver) + * @flags: flags for newly created file, currently only O_CLOEXEC is + * supported, refer to manual of open syscall for more details + * @index: id number of the buffer + * @type: enum v4l2_buf_type; buffer type (type == *_MPLANE for + * multiplanar buffers); + * @plane: index of the plane to be exported, 0 for single plane queues + * + * Contains data used for exporting a video buffer as DMABUF file descriptor. + * The buffer is identified by a 'cookie' returned by VIDIOC_QUERYBUF + * (identical to the cookie used to mmap() the buffer to userspace). All + * reserved fields must be set to zero. The field reserved0 is expected to + * become a structure 'type' allowing an alternative layout of the structure + * content. Therefore this field should not be used for any other extensions. + */ +struct v4l2_exportbuffer { + __s32 fd; + __u32 flags; + __u32 type; /* enum v4l2_buf_type */ + __u32 index; + __u32 plane; + __u32 reserved[11]; +}; + /* * O V E R L A Y P R E V I E W */ @@ -2558,6 +2585,7 @@ struct v4l2_create_buffers { #define VIDIOC_S_FBUF _IOW('V', 11, struct v4l2_framebuffer) #define VIDIOC_OVERLAY _IOW('V', 14, int) #define VIDIOC_QBUF _IOWR('V', 15, struct v4l2_buffer) +#define VIDIOC_EXPBUF _IOWR('V', 16, struct v4l2_exportbuffer) #define VIDIOC_DQBUF _IOWR('V', 17, struct v4l2_buffer) #define VIDIOC_STREAMON _IOW('V', 18, int) #define VIDIOC_STREAMOFF _IOW('V', 19, int) diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index e614c9c..38fb139 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -119,6 +119,8 @@ struct v4l2_ioctl_ops { int (*vidioc_reqbufs) (struct file *file, void *fh, struct v4l2_requestbuffers *b); int (*vidioc_querybuf)(struct file *file, void *fh, struct v4l2_buffer *b); int (*vidioc_qbuf) (struct file *file, void *fh, struct v4l2_buffer *b); + int (*vidioc_expbuf) (struct file *file, void *fh, + struct v4l2_exportbuffer *e); int (*vidioc_dqbuf) (struct file *file, void *fh, struct v4l2_buffer *b);
int (*vidioc_create_bufs)(struct file *file, void *fh, struct v4l2_create_buffers *b);
On Tue October 2 2012 16:27:29 Tomasz Stanislawski wrote:
This patch adds extension to V4L2 api. It allow to export a mmap buffer as file descriptor. New ioctl VIDIOC_EXPBUF is added. It takes a buffer offset used by mmap and return a file descriptor on success.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
drivers/media/video/v4l2-compat-ioctl32.c | 1 + drivers/media/video/v4l2-dev.c | 1 + drivers/media/video/v4l2-ioctl.c | 10 ++++++++++ include/linux/videodev2.h | 28 ++++++++++++++++++++++++++++ include/media/v4l2-ioctl.h | 2 ++ 5 files changed, 42 insertions(+)
diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index f0b5aba..8788000 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -971,6 +971,7 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_S_FBUF32: case VIDIOC_OVERLAY32: case VIDIOC_QBUF32:
- case VIDIOC_EXPBUF: case VIDIOC_DQBUF32: case VIDIOC_STREAMON32: case VIDIOC_STREAMOFF32:
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 07aeafc..c43127c 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -638,6 +638,7 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs); SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf); SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf);
- SET_VALID_IOCTL(ops, VIDIOC_EXPBUF, vidioc_expbuf); SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf); SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay); SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf);
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index dffd3c9..f3ec8c0 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -458,6 +458,15 @@ static void v4l_print_buffer(const void *arg, bool write_only) tc->type, tc->flags, tc->frames, *(__u32 *)tc->userbits); } +static void v4l_print_exportbuffer(const void *arg, bool write_only) +{
- const struct v4l2_exportbuffer *p = arg;
- pr_cont("fd=%d, type=%s, index=%u, plane=%u, flags=0x%08x\n",
p->fd, prt_names(p->type, v4l2_type_names),
p->index, p->plane, p->flags);
+}
static void v4l_print_create_buffers(const void *arg, bool write_only) { const struct v4l2_create_buffers *p = arg; @@ -1947,6 +1956,7 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_STD(VIDIOC_S_FBUF, vidioc_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO), IOCTL_INFO_STD(VIDIOC_OVERLAY, vidioc_overlay, v4l_print_u32, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE),
- IOCTL_INFO_STD(VIDIOC_EXPBUF, vidioc_expbuf, v4l_print_exportbuffer, 0),
This needs the INFO_FL_QUEUE flag, that way this call is serialized with the other queuing ioctls.
You can also add INFO_FL_CLEAR(v4l2_expbuf, flags). This assumes a field order in the struct as given in my comment below. The FL_CLEAR flag will zero all fields after 'flags'.
IOCTL_INFO_FNC(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE), IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE), IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE), diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index e04a73e..f429b6a 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -688,6 +688,33 @@ struct v4l2_buffer { #define V4L2_BUF_FLAG_NO_CACHE_INVALIDATE 0x0800 #define V4L2_BUF_FLAG_NO_CACHE_CLEAN 0x1000 +/**
- struct v4l2_exportbuffer - export of video buffer as DMABUF file descriptor
- @fd: file descriptor associated with DMABUF (set by driver)
- @flags: flags for newly created file, currently only O_CLOEXEC is
supported, refer to manual of open syscall for more details
- @index: id number of the buffer
- @type: enum v4l2_buf_type; buffer type (type == *_MPLANE for
multiplanar buffers);
- @plane: index of the plane to be exported, 0 for single plane queues
- Contains data used for exporting a video buffer as DMABUF file descriptor.
- The buffer is identified by a 'cookie' returned by VIDIOC_QUERYBUF
- (identical to the cookie used to mmap() the buffer to userspace). All
- reserved fields must be set to zero. The field reserved0 is expected to
- become a structure 'type' allowing an alternative layout of the structure
- content. Therefore this field should not be used for any other extensions.
- */
+struct v4l2_exportbuffer {
- __s32 fd;
- __u32 flags;
- __u32 type; /* enum v4l2_buf_type */
- __u32 index;
- __u32 plane;
As suggested in my comments in the previous patch, I think it is a more natural order to have the type/index/plane fields first in this struct.
Actually, I think that flags should also come before fd:
struct v4l2_exportbuffer { __u32 type; /* enum v4l2_buf_type */ __u32 index; __u32 plane; __u32 flags; __s32 fd; __u32 reserved[11]; };
- __u32 reserved[11];
+};
/*
- O V E R L A Y P R E V I E W
*/ @@ -2558,6 +2585,7 @@ struct v4l2_create_buffers { #define VIDIOC_S_FBUF _IOW('V', 11, struct v4l2_framebuffer) #define VIDIOC_OVERLAY _IOW('V', 14, int) #define VIDIOC_QBUF _IOWR('V', 15, struct v4l2_buffer) +#define VIDIOC_EXPBUF _IOWR('V', 16, struct v4l2_exportbuffer) #define VIDIOC_DQBUF _IOWR('V', 17, struct v4l2_buffer) #define VIDIOC_STREAMON _IOW('V', 18, int) #define VIDIOC_STREAMOFF _IOW('V', 19, int) diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index e614c9c..38fb139 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -119,6 +119,8 @@ struct v4l2_ioctl_ops { int (*vidioc_reqbufs) (struct file *file, void *fh, struct v4l2_requestbuffers *b); int (*vidioc_querybuf)(struct file *file, void *fh, struct v4l2_buffer *b); int (*vidioc_qbuf) (struct file *file, void *fh, struct v4l2_buffer *b);
- int (*vidioc_expbuf) (struct file *file, void *fh,
int (*vidioc_dqbuf) (struct file *file, void *fh, struct v4l2_buffer *b);struct v4l2_exportbuffer *e);
int (*vidioc_create_bufs)(struct file *file, void *fh, struct v4l2_create_buffers *b);
Hi Hans,
On Friday 05 October 2012 10:55:40 Hans Verkuil wrote:
On Tue October 2 2012 16:27:29 Tomasz Stanislawski wrote:
This patch adds extension to V4L2 api. It allow to export a mmap buffer as file descriptor. New ioctl VIDIOC_EXPBUF is added. It takes a buffer offset used by mmap and return a file descriptor on success.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
[snip]
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index e04a73e..f429b6a 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -688,6 +688,33 @@ struct v4l2_buffer {
#define V4L2_BUF_FLAG_NO_CACHE_INVALIDATE 0x0800 #define V4L2_BUF_FLAG_NO_CACHE_CLEAN 0x1000
+/**
- struct v4l2_exportbuffer - export of video buffer as DMABUF file
descriptor + *
- @fd: file descriptor associated with DMABUF (set by driver)
- @flags: flags for newly created file, currently only O_CLOEXEC is
supported, refer to manual of open syscall for more details
- @index: id number of the buffer
- @type: enum v4l2_buf_type; buffer type (type == *_MPLANE for
multiplanar buffers);
- @plane: index of the plane to be exported, 0 for single plane queues
- Contains data used for exporting a video buffer as DMABUF file
descriptor. + * The buffer is identified by a 'cookie' returned by VIDIOC_QUERYBUF + * (identical to the cookie used to mmap() the buffer to userspace). All + * reserved fields must be set to zero. The field reserved0 is expected to + * become a structure 'type' allowing an alternative layout of the structure + * content. Therefore this field should not be used for any other extensions. + */ +struct v4l2_exportbuffer {
- __s32 fd;
- __u32 flags;
- __u32 type; /* enum v4l2_buf_type */
- __u32 index;
- __u32 plane;
As suggested in my comments in the previous patch, I think it is a more natural order to have the type/index/plane fields first in this struct.
Actually, I think that flags should also come before fd:
struct v4l2_exportbuffer { __u32 type; /* enum v4l2_buf_type */ __u32 index; __u32 plane; __u32 flags; __s32 fd; __u32 reserved[11]; };
It would indeed feel more natural, but putting them right before the reserved fields allows creating an anonymous union around type, index and plane and extending it with reserved fields if needed. That's (at least to my understanding) the rationale behind the current structure layout.
- __u32 reserved[11];
+};
/*
- O V E R L A Y P R E V I E W
*/
On Sun October 7 2012 15:38:30 Laurent Pinchart wrote:
Hi Hans,
On Friday 05 October 2012 10:55:40 Hans Verkuil wrote:
On Tue October 2 2012 16:27:29 Tomasz Stanislawski wrote:
This patch adds extension to V4L2 api. It allow to export a mmap buffer as file descriptor. New ioctl VIDIOC_EXPBUF is added. It takes a buffer offset used by mmap and return a file descriptor on success.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
[snip]
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index e04a73e..f429b6a 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -688,6 +688,33 @@ struct v4l2_buffer {
#define V4L2_BUF_FLAG_NO_CACHE_INVALIDATE 0x0800 #define V4L2_BUF_FLAG_NO_CACHE_CLEAN 0x1000
+/**
- struct v4l2_exportbuffer - export of video buffer as DMABUF file
descriptor + *
- @fd: file descriptor associated with DMABUF (set by driver)
- @flags: flags for newly created file, currently only O_CLOEXEC is
supported, refer to manual of open syscall for more details
- @index: id number of the buffer
- @type: enum v4l2_buf_type; buffer type (type == *_MPLANE for
multiplanar buffers);
- @plane: index of the plane to be exported, 0 for single plane queues
- Contains data used for exporting a video buffer as DMABUF file
descriptor. + * The buffer is identified by a 'cookie' returned by VIDIOC_QUERYBUF + * (identical to the cookie used to mmap() the buffer to userspace). All + * reserved fields must be set to zero. The field reserved0 is expected to + * become a structure 'type' allowing an alternative layout of the structure + * content. Therefore this field should not be used for any other extensions. + */ +struct v4l2_exportbuffer {
- __s32 fd;
- __u32 flags;
- __u32 type; /* enum v4l2_buf_type */
- __u32 index;
- __u32 plane;
As suggested in my comments in the previous patch, I think it is a more natural order to have the type/index/plane fields first in this struct.
Actually, I think that flags should also come before fd:
struct v4l2_exportbuffer { __u32 type; /* enum v4l2_buf_type */ __u32 index; __u32 plane; __u32 flags; __s32 fd; __u32 reserved[11]; };
It would indeed feel more natural, but putting them right before the reserved fields allows creating an anonymous union around type, index and plane and extending it with reserved fields if needed. That's (at least to my understanding) the rationale behind the current structure layout.
The anonymous union argument makes no sense to me, to be honest. It's standard practice within V4L2 to have the IN fields first, then the OUT fields, followed by reserved fields for future expansion. Should we ever need a, say, sub-plane index (whatever that might be), then we can use one of the reserved fields.
Regards,
Hans
- __u32 reserved[11];
+};
/*
- O V E R L A Y P R E V I E W
*/
Hi Hans,
On 10/07/2012 04:17 PM, Hans Verkuil wrote:
On Sun October 7 2012 15:38:30 Laurent Pinchart wrote:
Hi Hans,
On Friday 05 October 2012 10:55:40 Hans Verkuil wrote:
On Tue October 2 2012 16:27:29 Tomasz Stanislawski wrote:
This patch adds extension to V4L2 api. It allow to export a mmap buffer as file descriptor. New ioctl VIDIOC_EXPBUF is added. It takes a buffer offset used by mmap and return a file descriptor on success.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
[snip]
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index e04a73e..f429b6a 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -688,6 +688,33 @@ struct v4l2_buffer {
#define V4L2_BUF_FLAG_NO_CACHE_INVALIDATE 0x0800 #define V4L2_BUF_FLAG_NO_CACHE_CLEAN 0x1000
+/**
- struct v4l2_exportbuffer - export of video buffer as DMABUF file
descriptor + *
- @fd: file descriptor associated with DMABUF (set by driver)
- @flags: flags for newly created file, currently only O_CLOEXEC is
supported, refer to manual of open syscall for more details
- @index: id number of the buffer
- @type: enum v4l2_buf_type; buffer type (type == *_MPLANE for
multiplanar buffers);
- @plane: index of the plane to be exported, 0 for single plane queues
- Contains data used for exporting a video buffer as DMABUF file
descriptor. + * The buffer is identified by a 'cookie' returned by VIDIOC_QUERYBUF + * (identical to the cookie used to mmap() the buffer to userspace). All + * reserved fields must be set to zero. The field reserved0 is expected to + * become a structure 'type' allowing an alternative layout of the structure + * content. Therefore this field should not be used for any other extensions. + */ +struct v4l2_exportbuffer {
- __s32 fd;
- __u32 flags;
- __u32 type; /* enum v4l2_buf_type */
- __u32 index;
- __u32 plane;
As suggested in my comments in the previous patch, I think it is a more natural order to have the type/index/plane fields first in this struct.
Actually, I think that flags should also come before fd:
struct v4l2_exportbuffer { __u32 type; /* enum v4l2_buf_type */ __u32 index; __u32 plane; __u32 flags; __s32 fd; __u32 reserved[11]; };
It would indeed feel more natural, but putting them right before the reserved fields allows creating an anonymous union around type, index and plane and extending it with reserved fields if needed. That's (at least to my understanding) the rationale behind the current structure layout.
The anonymous union argument makes no sense to me, to be honest.
I agree that the anonymous unions are not good solutions because they are not supported in many C dialects. However I have nothing against using named unions.
It's standard practice within V4L2 to have the IN fields first, then the OUT fields, followed by reserved fields for future expansion.
IMO, the "input/output/reserved rule" is only a recommendation. The are places in V4L2 where this rule is violated with structure v4l2_buffer being the most notable example.
Notice that if at least one of the reserved fields becomes an input file then "the rule" will be violated anyway.
Should we ever need a, say, sub-plane index (whatever that might be), then we can use one of the reserved fields.
Maybe not subplane :). But maybe some data_offset for exporting only a part of the buffer will be needed some day. Moreover, the integration of DMABUF with the DMA synchronization framework may involve passing additional parameters from the userspace.
Notice that flags and fd fields are not logically connected with (type/index/plane) tuple. Therefore both field sets should be separated by some reserved fields to ensure that any of them can be extended if needed.
This was the rationale for the structure layout in v9.
Regards, Tomasz Stanislawski
Regards,
Hans
- __u32 reserved[11];
+};
/*
- O V E R L A Y P R E V I E W
*/
On Mon October 8 2012 11:40:37 Tomasz Stanislawski wrote:
Hi Hans,
On 10/07/2012 04:17 PM, Hans Verkuil wrote:
On Sun October 7 2012 15:38:30 Laurent Pinchart wrote:
Hi Hans,
On Friday 05 October 2012 10:55:40 Hans Verkuil wrote:
On Tue October 2 2012 16:27:29 Tomasz Stanislawski wrote:
This patch adds extension to V4L2 api. It allow to export a mmap buffer as file descriptor. New ioctl VIDIOC_EXPBUF is added. It takes a buffer offset used by mmap and return a file descriptor on success.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
[snip]
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index e04a73e..f429b6a 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -688,6 +688,33 @@ struct v4l2_buffer {
#define V4L2_BUF_FLAG_NO_CACHE_INVALIDATE 0x0800 #define V4L2_BUF_FLAG_NO_CACHE_CLEAN 0x1000
+/**
- struct v4l2_exportbuffer - export of video buffer as DMABUF file
descriptor + *
- @fd: file descriptor associated with DMABUF (set by driver)
- @flags: flags for newly created file, currently only O_CLOEXEC is
supported, refer to manual of open syscall for more details
- @index: id number of the buffer
- @type: enum v4l2_buf_type; buffer type (type == *_MPLANE for
multiplanar buffers);
- @plane: index of the plane to be exported, 0 for single plane queues
- Contains data used for exporting a video buffer as DMABUF file
descriptor. + * The buffer is identified by a 'cookie' returned by VIDIOC_QUERYBUF + * (identical to the cookie used to mmap() the buffer to userspace). All + * reserved fields must be set to zero. The field reserved0 is expected to + * become a structure 'type' allowing an alternative layout of the structure + * content. Therefore this field should not be used for any other extensions. + */ +struct v4l2_exportbuffer {
- __s32 fd;
- __u32 flags;
- __u32 type; /* enum v4l2_buf_type */
- __u32 index;
- __u32 plane;
As suggested in my comments in the previous patch, I think it is a more natural order to have the type/index/plane fields first in this struct.
Actually, I think that flags should also come before fd:
struct v4l2_exportbuffer { __u32 type; /* enum v4l2_buf_type */ __u32 index; __u32 plane; __u32 flags; __s32 fd; __u32 reserved[11]; };
It would indeed feel more natural, but putting them right before the reserved fields allows creating an anonymous union around type, index and plane and extending it with reserved fields if needed. That's (at least to my understanding) the rationale behind the current structure layout.
The anonymous union argument makes no sense to me, to be honest.
I agree that the anonymous unions are not good solutions because they are not supported in many C dialects. However I have nothing against using named unions.
Named or unnamed, I don't see how a union will help. What do you want to do with a union?
It's standard practice within V4L2 to have the IN fields first, then the OUT fields, followed by reserved fields for future expansion.
IMO, the "input/output/reserved rule" is only a recommendation. The are places in V4L2 where this rule is violated with structure v4l2_buffer being the most notable example.
Notice that if at least one of the reserved fields becomes an input file then "the rule" will be violated anyway.
Sure, but there is no legacy yet, so why not keep to the recommendation?
Should we ever need a, say, sub-plane index (whatever that might be), then we can use one of the reserved fields.
Maybe not subplane :). But maybe some data_offset for exporting only a part of the buffer will be needed some day. Moreover, the integration of DMABUF with the DMA synchronization framework may involve passing additional parameters from the userspace.
Notice that flags and fd fields are not logically connected with (type/index/plane) tuple. Therefore both field sets should be separated by some reserved fields to ensure that any of them can be extended if needed.
This was the rationale for the structure layout in v9.
It's a bad idea to add multiple 'reserved' arrays, that makes userspace harder since it has to zero all of them instead of just one. Actually, the same applies to kernel space, which has to zero them as well.
I still don't know why you want to use a non-standard field order.
Regards,
Hans
Hi Hans,
On 10/08/2012 11:54 AM, Hans Verkuil wrote:
On Mon October 8 2012 11:40:37 Tomasz Stanislawski wrote:
Hi Hans,
On 10/07/2012 04:17 PM, Hans Verkuil wrote:
On Sun October 7 2012 15:38:30 Laurent Pinchart wrote:
Hi Hans,
On Friday 05 October 2012 10:55:40 Hans Verkuil wrote:
On Tue October 2 2012 16:27:29 Tomasz Stanislawski wrote:
This patch adds extension to V4L2 api. It allow to export a mmap buffer as file descriptor. New ioctl VIDIOC_EXPBUF is added. It takes a buffer offset used by mmap and return a file descriptor on success.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
[snip]
+struct v4l2_exportbuffer {
- __s32 fd;
- __u32 flags;
- __u32 type; /* enum v4l2_buf_type */
- __u32 index;
- __u32 plane;
As suggested in my comments in the previous patch, I think it is a more natural order to have the type/index/plane fields first in this struct.
Actually, I think that flags should also come before fd:
struct v4l2_exportbuffer { __u32 type; /* enum v4l2_buf_type */ __u32 index; __u32 plane; __u32 flags; __s32 fd; __u32 reserved[11]; };
It would indeed feel more natural, but putting them right before the reserved fields allows creating an anonymous union around type, index and plane and extending it with reserved fields if needed. That's (at least to my understanding) the rationale behind the current structure layout.
The anonymous union argument makes no sense to me, to be honest.
I agree that the anonymous unions are not good solutions because they are not supported in many C dialects. However I have nothing against using named unions.
Named or unnamed, I don't see how a union will help. What do you want to do with a union?
Currently, there exist three sane layouts of the structure, that use only one reserved field:
A) struct v4l2_exportbuffer { __s32 fd; __u32 flags; __u32 type; /* enum v4l2_buf_type */ __u32 index; __u32 plane; __u32 reserved[11]; }
B) struct v4l2_exportbuffer { __u32 type; /* enum v4l2_buf_type */ __u32 index; __u32 plane; __u32 flags; __s32 fd; __u32 reserved[11]; }
C) struct v4l2_exportbuffer { __u32 type; /* enum v4l2_buf_type */ __u32 index; __u32 plane; __u32 reserved[11]; __u32 flags; __s32 fd; }
Only the layout B follows 'input/output/reserved' rule.
The layouts A and C allows to extend (type/index/plane) tuple without mixing it with (flags,fd).
For layouts A and C it is possible to use unions to provide new means of describing a buffer to be exported.
struct v4l2_exportbuffer { __s32 fd; __u32 flags; union { struct by_tip { /* type, index, plane */ __u32 type; /* enum v4l2_buf_type */ __u32 index; __u32 plane; } by_tip; struct by_userptr { u64 userptr; u64 length; } by_userptr; __u32 reserved[6]; } b; __u32 union_type; /* BY_TIP or BY_USERPTR */ __u32 reserved[4]; };
No such an extension can be applied for layout B.
The similar scheme can be used for layout C. Moreover it support extensions and variants for (flags/fd) tuple. It might be useful if one day we would like to export a buffer as something different from DMABUF file descriptors.
Anyway, we have to choose between the elegance of the layout and the extensibility.
I think that layout A is a good trade-off. We could swap fd and flags to get little closer to "the rule".
It's standard practice within V4L2 to have the IN fields first, then the OUT fields, followed by reserved fields for future expansion.
IMO, the "input/output/reserved rule" is only a recommendation. The are places in V4L2 where this rule is violated with structure v4l2_buffer being the most notable example.
Notice that if at least one of the reserved fields becomes an input file then "the rule" will be violated anyway.
Sure, but there is no legacy yet, so why not keep to the recommendation?
Should we ever need a, say, sub-plane index (whatever that might be), then we can use one of the reserved fields.
Maybe not subplane :). But maybe some data_offset for exporting only a part of the buffer will be needed some day. Moreover, the integration of DMABUF with the DMA synchronization framework may involve passing additional parameters from the userspace.
Notice that flags and fd fields are not logically connected with (type/index/plane) tuple. Therefore both field sets should be separated by some reserved fields to ensure that any of them can be extended if needed.
This was the rationale for the structure layout in v9.
It's a bad idea to add multiple 'reserved' arrays, that makes userspace harder since it has to zero all of them instead of just one. Actually, the same applies to kernel space, which has to zero them as well.
Userspace usually cleans the whole structure using memset call. Notice that memset is a build-in functions therefore fields are not zeroed if they are initialized just below memset.
The number of reserved fields has no impact on initialization code. There has also negligible impact on performance (if any at all).
Regards, Tomasz Stanislawski
I still don't know why you want to use a non-standard field order.
Regards,
Hans
On Mon October 8 2012 12:41:28 Tomasz Stanislawski wrote:
Hi Hans,
On 10/08/2012 11:54 AM, Hans Verkuil wrote:
On Mon October 8 2012 11:40:37 Tomasz Stanislawski wrote:
Hi Hans,
On 10/07/2012 04:17 PM, Hans Verkuil wrote:
On Sun October 7 2012 15:38:30 Laurent Pinchart wrote:
Hi Hans,
On Friday 05 October 2012 10:55:40 Hans Verkuil wrote:
On Tue October 2 2012 16:27:29 Tomasz Stanislawski wrote: > This patch adds extension to V4L2 api. It allow to export a mmap buffer as > file descriptor. New ioctl VIDIOC_EXPBUF is added. It takes a buffer > offset used by mmap and return a file descriptor on success. > > Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com > Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
[snip]
> +struct v4l2_exportbuffer { > + __s32 fd; > + __u32 flags; > + __u32 type; /* enum v4l2_buf_type */ > + __u32 index; > + __u32 plane;
As suggested in my comments in the previous patch, I think it is a more natural order to have the type/index/plane fields first in this struct.
Actually, I think that flags should also come before fd:
struct v4l2_exportbuffer { __u32 type; /* enum v4l2_buf_type */ __u32 index; __u32 plane; __u32 flags; __s32 fd; __u32 reserved[11]; };
It would indeed feel more natural, but putting them right before the reserved fields allows creating an anonymous union around type, index and plane and extending it with reserved fields if needed. That's (at least to my understanding) the rationale behind the current structure layout.
The anonymous union argument makes no sense to me, to be honest.
I agree that the anonymous unions are not good solutions because they are not supported in many C dialects. However I have nothing against using named unions.
Named or unnamed, I don't see how a union will help. What do you want to do with a union?
Currently, there exist three sane layouts of the structure, that use only one reserved field:
A) struct v4l2_exportbuffer { __s32 fd; __u32 flags; __u32 type; /* enum v4l2_buf_type */ __u32 index; __u32 plane; __u32 reserved[11]; }
B) struct v4l2_exportbuffer { __u32 type; /* enum v4l2_buf_type */ __u32 index; __u32 plane; __u32 flags; __s32 fd; __u32 reserved[11]; }
C) struct v4l2_exportbuffer { __u32 type; /* enum v4l2_buf_type */ __u32 index; __u32 plane; __u32 reserved[11]; __u32 flags; __s32 fd; }
Only the layout B follows 'input/output/reserved' rule.
The layouts A and C allows to extend (type/index/plane) tuple without mixing it with (flags,fd).
For layouts A and C it is possible to use unions to provide new means of describing a buffer to be exported.
struct v4l2_exportbuffer { __s32 fd; __u32 flags; union { struct by_tip { /* type, index, plane */ __u32 type; /* enum v4l2_buf_type */ __u32 index; __u32 plane; } by_tip; struct by_userptr { u64 userptr; u64 length; } by_userptr; __u32 reserved[6]; } b; __u32 union_type; /* BY_TIP or BY_USERPTR */ __u32 reserved[4]; };
No such an extension can be applied for layout B.
You are overengineering. If we ever want to export something that is *not* a buffer created with REQBUFS/CREATE_BUFS, then you want to do that with a new ioctl. If we want for some reason to export a userptr, then that should either be from a queued/prepared buffer, or we need a separate API to create a dmabuf from a userptr. After all, that would be completely generic and not V4L2 specific.
Anyway, introducing such a union later won't work either because then instead of writing expbuf.type you'd have to write expbuf.by_tip.type: API change.
BTW, I think it makes sense as well in the case of a userptr to only export the buffer if it is under control of the kernel (e.g. after QBUF or PREPARE_BUF). When exporting the buffer the driver has all the information it needs to do so safely.
The similar scheme can be used for layout C. Moreover it support extensions and variants for (flags/fd) tuple. It might be useful if one day we would like to export a buffer as something different from DMABUF file descriptors.
Anyway, we have to choose between the elegance of the layout and the extensibility.
I think that layout A is a good trade-off. We could swap fd and flags to get little closer to "the rule".
It's standard practice within V4L2 to have the IN fields first, then the OUT fields, followed by reserved fields for future expansion.
IMO, the "input/output/reserved rule" is only a recommendation. The are places in V4L2 where this rule is violated with structure v4l2_buffer being the most notable example.
Notice that if at least one of the reserved fields becomes an input file then "the rule" will be violated anyway.
Sure, but there is no legacy yet, so why not keep to the recommendation?
Should we ever need a, say, sub-plane index (whatever that might be), then we can use one of the reserved fields.
Maybe not subplane :). But maybe some data_offset for exporting only a part of the buffer will be needed some day. Moreover, the integration of DMABUF with the DMA synchronization framework may involve passing additional parameters from the userspace.
Notice that flags and fd fields are not logically connected with (type/index/plane) tuple. Therefore both field sets should be separated by some reserved fields to ensure that any of them can be extended if needed.
This was the rationale for the structure layout in v9.
It's a bad idea to add multiple 'reserved' arrays, that makes userspace harder since it has to zero all of them instead of just one. Actually, the same applies to kernel space, which has to zero them as well.
Userspace usually cleans the whole structure using memset call.
'usually', yes. :-)
Notice that memset is a build-in functions therefore fields are not zeroed if they are initialized just below memset.
The number of reserved fields has no impact on initialization code. There has also negligible impact on performance (if any at all).
Performance is not an issue here. It's about conforming to existing conventions and making it possible to use INFO_FL_CLEAR() in v4l2-ioctl.c to have the struct zeroed automatically after all the IN arguments, thus making it easier on drivers.
Regards,
Hans
This patch adds extension to videobuf2-core. It allow to export a mmap buffer as a file descriptor.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Acked-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/media/video/videobuf2-core.c | 82 ++++++++++++++++++++++++++++++++++ include/media/videobuf2-core.h | 4 ++ 2 files changed, 86 insertions(+)
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 05da3b4..a97815b 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -1753,6 +1753,79 @@ static int __find_plane_by_offset(struct vb2_queue *q, unsigned long off, }
/** + * vb2_expbuf() - Export a buffer as a file descriptor + * @q: videobuf2 queue + * @eb: export buffer structure passed from userspace to vidioc_expbuf + * handler in driver + * + * The return values from this function are intended to be directly returned + * from vidioc_expbuf handler in driver. + */ +int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb) +{ + struct vb2_buffer *vb = NULL; + struct vb2_plane *vb_plane; + int ret; + struct dma_buf *dbuf; + + if (q->memory != V4L2_MEMORY_MMAP) { + dprintk(1, "Queue is not currently set up for mmap\n"); + return -EINVAL; + } + + if (!q->mem_ops->get_dmabuf) { + dprintk(1, "Queue does not support DMA buffer exporting\n"); + return -EINVAL; + } + + if (eb->flags & ~O_CLOEXEC) { + dprintk(1, "Queue does support only O_CLOEXEC flag\n"); + return -EINVAL; + } + + if (eb->type != q->type) { + dprintk(1, "qbuf: invalid buffer type\n"); + return -EINVAL; + } + + if (eb->index >= q->num_buffers) { + dprintk(1, "buffer index out of range\n"); + return -EINVAL; + } + + vb = q->bufs[eb->index]; + + if (eb->plane >= vb->num_planes) { + dprintk(1, "buffer plane out of range\n"); + return -EINVAL; + } + + vb_plane = &vb->planes[eb->plane]; + + dbuf = call_memop(q, get_dmabuf, vb_plane->mem_priv); + if (IS_ERR_OR_NULL(dbuf)) { + dprintk(1, "Failed to export buffer %d, plane %d\n", + eb->index, eb->plane); + return -EINVAL; + } + + ret = dma_buf_fd(dbuf, eb->flags); + if (ret < 0) { + dprintk(3, "buffer %d, plane %d failed to export (%d)\n", + eb->index, eb->plane, ret); + dma_buf_put(dbuf); + return ret; + } + + dprintk(3, "buffer %d, plane %d exported as %d descriptor\n", + eb->index, eb->plane, ret); + eb->fd = ret; + + return 0; +} +EXPORT_SYMBOL_GPL(vb2_expbuf); + +/** * vb2_mmap() - map video buffers into application address space * @q: videobuf2 queue * @vma: vma passed to the mmap file operation handler in the driver @@ -2455,6 +2528,15 @@ int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) } EXPORT_SYMBOL_GPL(vb2_ioctl_streamoff);
+int vb2_ioctl_expbuf(struct file *file, void *priv, struct v4l2_exportbuffer *p) +{ + struct video_device *vdev = video_devdata(file); + + /* No need to call vb2_queue_is_busy(), anyone can export buffers. */ + return vb2_expbuf(vdev->queue, p); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_expbuf); + /* v4l2_file_operations helpers */
int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma) diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index c306fec..3f57ccb 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -81,6 +81,7 @@ struct vb2_fileio_data; struct vb2_mem_ops { void *(*alloc)(void *alloc_ctx, unsigned long size); void (*put)(void *buf_priv); + struct dma_buf *(*get_dmabuf)(void *buf_priv);
void *(*get_userptr)(void *alloc_ctx, unsigned long vaddr, unsigned long size, int write); @@ -363,6 +364,7 @@ int vb2_queue_init(struct vb2_queue *q); void vb2_queue_release(struct vb2_queue *q);
int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b); +int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb); int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking);
int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type); @@ -472,6 +474,8 @@ int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p); int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p); int vb2_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type i); int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i); +int vb2_ioctl_expbuf(struct file *file, void *priv, + struct v4l2_exportbuffer *p);
/* struct v4l2_file_operations helpers */
On Tue October 2 2012 16:27:30 Tomasz Stanislawski wrote:
This patch adds extension to videobuf2-core. It allow to export a mmap buffer as a file descriptor.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Acked-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
drivers/media/video/videobuf2-core.c | 82 ++++++++++++++++++++++++++++++++++ include/media/videobuf2-core.h | 4 ++ 2 files changed, 86 insertions(+)
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 05da3b4..a97815b 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c
<snip>
@@ -2455,6 +2528,15 @@ int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) } EXPORT_SYMBOL_GPL(vb2_ioctl_streamoff); +int vb2_ioctl_expbuf(struct file *file, void *priv, struct v4l2_exportbuffer *p) +{
- struct video_device *vdev = video_devdata(file);
- /* No need to call vb2_queue_is_busy(), anyone can export buffers. */
After thinking about this some more I'm not so sure we should allow this. Exporting a buffer also means that the memory can't be freed as long as the exported filehandle remains open.
That means that it is possible to make a malicious application that exports the buffers and never frees them, which can cause havoc. I think that only the filehandle that called REQBUFS/CREATE_BUFS should be allowed to export buffers.
What do you think?
Regards,
Hans
- return vb2_expbuf(vdev->queue, p);
+} +EXPORT_SYMBOL_GPL(vb2_ioctl_expbuf);
/* v4l2_file_operations helpers */ int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma)
On 10/06/2012 02:22 PM, Hans Verkuil wrote:
On Tue October 2 2012 16:27:30 Tomasz Stanislawski wrote:
This patch adds extension to videobuf2-core. It allow to export a mmap buffer as a file descriptor.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com Acked-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
drivers/media/video/videobuf2-core.c | 82 ++++++++++++++++++++++++++++++++++ include/media/videobuf2-core.h | 4 ++ 2 files changed, 86 insertions(+)
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 05da3b4..a97815b 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c
<snip>
@@ -2455,6 +2528,15 @@ int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) } EXPORT_SYMBOL_GPL(vb2_ioctl_streamoff); +int vb2_ioctl_expbuf(struct file *file, void *priv, struct v4l2_exportbuffer *p) +{
- struct video_device *vdev = video_devdata(file);
- /* No need to call vb2_queue_is_busy(), anyone can export buffers. */
Hi Hans,
After thinking about this some more I'm not so sure we should allow this. Exporting a buffer also means that the memory can't be freed as long as the exported filehandle remains open.
You are completely right. But the problem is much more complex ... see below.
That means that it is possible to make a malicious application that exports the buffers and never frees them, which can cause havoc.
What kind of havoc do you mean? Resource leakage?
I think that only the filehandle that called REQBUFS/CREATE_BUFS should be allowed to export buffers.
Notice that you should allow to call mmap() only for the file handles that called REQBUFS/CREATE_BUFS. The mmap() creates a vma that holds a reference to a buffer. As long as the mapping exists, the buffers cannot be freed and REQBUFS(count=0) returns -EBUSY. According to V4L2 spec all the nodes share the same context, therefore one process can call REQBUFS, and the other can call QUERYBUF and mmap().
Therefore if EXPBUF has to check vb2_queue_is_busy() then vb2_fop_mmap() should do the same check too.
IMO, it is very difficult to develop a useful multi-client API that would protect V4L2 from creating non-freeable buffers by a rogue applications.
I think that the requirements below:
- the buffers should be sharable between processes by mmap(), or DMABUF exporting - the REQBUFS(count=0) should release the buffers
are contradictory and cannot be *reliably* satisfied at the same time. Notice that REQBUFS(count=0) that unexpectedly return -EBUSY is not a reliable solution. The application cannot do anything fix the problem after receiving -EBUSY.
Anyway, I will apply the check for vb2_queue_is_busy() in v2b_ioctl_expbuf().
Regards, Tomasz Stanislawski
What do you think?
Regards,
Hans
- return vb2_expbuf(vdev->queue, p);
+} +EXPORT_SYMBOL_GPL(vb2_ioctl_expbuf);
/* v4l2_file_operations helpers */ int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma)
This patch adds support for exporting a dma-contig buffer using DMABUF interface.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/media/video/videobuf2-dma-contig.c | 200 ++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+)
diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c index 0e065ce..b138b5c 100644 --- a/drivers/media/video/videobuf2-dma-contig.c +++ b/drivers/media/video/videobuf2-dma-contig.c @@ -36,6 +36,7 @@ struct vb2_dc_buf { /* MMAP related */ struct vb2_vmarea_handler handler; atomic_t refcount; + struct sg_table *sgt_base;
/* USERPTR related */ struct vm_area_struct *vma; @@ -142,6 +143,10 @@ static void vb2_dc_put(void *buf_priv) if (!atomic_dec_and_test(&buf->refcount)) return;
+ if (buf->sgt_base) { + sg_free_table(buf->sgt_base); + kfree(buf->sgt_base); + } dma_free_coherent(buf->dev, buf->size, buf->vaddr, buf->dma_addr); kfree(buf); } @@ -213,6 +218,200 @@ static int vb2_dc_mmap(void *buf_priv, struct vm_area_struct *vma) }
/*********************************************/ +/* DMABUF ops for exporters */ +/*********************************************/ + +struct vb2_dc_attachment { + struct sg_table sgt; + enum dma_data_direction dir; +}; + +static int vb2_dc_dmabuf_ops_attach(struct dma_buf *dbuf, struct device *dev, + struct dma_buf_attachment *dbuf_attach) +{ + struct vb2_dc_attachment *attach; + unsigned int i; + struct scatterlist *rd, *wr; + struct sg_table *sgt; + struct vb2_dc_buf *buf = dbuf->priv; + int ret; + + attach = kzalloc(sizeof(*attach), GFP_KERNEL); + if (!attach) + return -ENOMEM; + + sgt = &attach->sgt; + /* Copy the buf->base_sgt scatter list to the attachment, as we can't + * map the same scatter list to multiple attachments at the same time. + */ + ret = sg_alloc_table(sgt, buf->sgt_base->orig_nents, GFP_KERNEL); + if (ret) { + kfree(attach); + return -ENOMEM; + } + + rd = buf->sgt_base->sgl; + wr = sgt->sgl; + for (i = 0; i < sgt->orig_nents; ++i) { + sg_set_page(wr, sg_page(rd), rd->length, rd->offset); + rd = sg_next(rd); + wr = sg_next(wr); + } + + attach->dir = DMA_NONE; + dbuf_attach->priv = attach; + + return 0; +} + +static void vb2_dc_dmabuf_ops_detach(struct dma_buf *dbuf, + struct dma_buf_attachment *db_attach) +{ + struct vb2_dc_attachment *attach = db_attach->priv; + struct sg_table *sgt; + + if (!attach) + return; + + sgt = &attach->sgt; + + /* release the scatterlist cache */ + if (attach->dir != DMA_NONE) + dma_unmap_sg(db_attach->dev, sgt->sgl, sgt->orig_nents, + attach->dir); + sg_free_table(sgt); + kfree(attach); + db_attach->priv = NULL; +} + +static struct sg_table *vb2_dc_dmabuf_ops_map( + struct dma_buf_attachment *db_attach, enum dma_data_direction dir) +{ + struct vb2_dc_attachment *attach = db_attach->priv; + /* stealing dmabuf mutex to serialize map/unmap operations */ + struct mutex *lock = &db_attach->dmabuf->lock; + struct sg_table *sgt; + int ret; + + mutex_lock(lock); + + sgt = &attach->sgt; + /* return previously mapped sg table */ + if (attach->dir == dir) { + mutex_unlock(lock); + return sgt; + } + + /* release any previous cache */ + if (attach->dir != DMA_NONE) { + dma_unmap_sg(db_attach->dev, sgt->sgl, sgt->orig_nents, + attach->dir); + attach->dir = DMA_NONE; + } + + /* mapping to the client with new direction */ + ret = dma_map_sg(db_attach->dev, sgt->sgl, sgt->orig_nents, dir); + if (ret <= 0) { + pr_err("failed to map scatterlist\n"); + mutex_unlock(lock); + return ERR_PTR(-EIO); + } + + attach->dir = dir; + + mutex_unlock(lock); + + return sgt; +} + +static void vb2_dc_dmabuf_ops_unmap(struct dma_buf_attachment *db_attach, + struct sg_table *sgt, enum dma_data_direction dir) +{ + /* nothing to be done here */ +} + +static void vb2_dc_dmabuf_ops_release(struct dma_buf *dbuf) +{ + /* drop reference obtained in vb2_dc_get_dmabuf */ + vb2_dc_put(dbuf->priv); +} + +static void *vb2_dc_dmabuf_ops_kmap(struct dma_buf *dbuf, unsigned long pgnum) +{ + struct vb2_dc_buf *buf = dbuf->priv; + + return buf->vaddr + pgnum * PAGE_SIZE; +} + +static void *vb2_dc_dmabuf_ops_vmap(struct dma_buf *dbuf) +{ + struct vb2_dc_buf *buf = dbuf->priv; + + return buf->vaddr; +} + +static int vb2_dc_dmabuf_ops_mmap(struct dma_buf *dbuf, + struct vm_area_struct *vma) +{ + return vb2_dc_mmap(dbuf->priv, vma); +} + +static struct dma_buf_ops vb2_dc_dmabuf_ops = { + .attach = vb2_dc_dmabuf_ops_attach, + .detach = vb2_dc_dmabuf_ops_detach, + .map_dma_buf = vb2_dc_dmabuf_ops_map, + .unmap_dma_buf = vb2_dc_dmabuf_ops_unmap, + .kmap = vb2_dc_dmabuf_ops_kmap, + .kmap_atomic = vb2_dc_dmabuf_ops_kmap, + .vmap = vb2_dc_dmabuf_ops_vmap, + .mmap = vb2_dc_dmabuf_ops_mmap, + .release = vb2_dc_dmabuf_ops_release, +}; + +static struct sg_table *vb2_dc_get_base_sgt(struct vb2_dc_buf *buf) +{ + int ret; + struct sg_table *sgt; + + sgt = kmalloc(sizeof(*sgt), GFP_KERNEL); + if (!sgt) { + dev_err(buf->dev, "failed to alloc sg table\n"); + return NULL; + } + + ret = dma_get_sgtable(buf->dev, sgt, buf->vaddr, buf->dma_addr, + buf->size); + if (ret < 0) { + dev_err(buf->dev, "failed to get scatterlist from DMA API\n"); + kfree(sgt); + return NULL; + } + + return sgt; +} + +static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv) +{ + struct vb2_dc_buf *buf = buf_priv; + struct dma_buf *dbuf; + + if (!buf->sgt_base) + buf->sgt_base = vb2_dc_get_base_sgt(buf); + + if (WARN_ON(!buf->sgt_base)) + return NULL; + + dbuf = dma_buf_export(buf, &vb2_dc_dmabuf_ops, buf->size, 0); + if (IS_ERR(dbuf)) + return NULL; + + /* dmabuf keeps reference to vb2 buffer */ + atomic_inc(&buf->refcount); + + return dbuf; +} + +/*********************************************/ /* callbacks for USERPTR buffers */ /*********************************************/
@@ -519,6 +718,7 @@ static void *vb2_dc_attach_dmabuf(void *alloc_ctx, struct dma_buf *dbuf, const struct vb2_mem_ops vb2_dma_contig_memops = { .alloc = vb2_dc_alloc, .put = vb2_dc_put, + .get_dmabuf = vb2_dc_get_dmabuf, .cookie = vb2_dc_cookie, .vaddr = vb2_dc_vaddr, .mmap = vb2_dc_mmap,
Hi Tomasz,
Thanks for the patch.
On Tuesday 02 October 2012 16:27:31 Tomasz Stanislawski wrote:
This patch adds support for exporting a dma-contig buffer using DMABUF interface.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
Acked-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
drivers/media/video/videobuf2-dma-contig.c | 200 +++++++++++++++++++++++++ 1 file changed, 200 insertions(+)
diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c index 0e065ce..b138b5c 100644 --- a/drivers/media/video/videobuf2-dma-contig.c +++ b/drivers/media/video/videobuf2-dma-contig.c @@ -36,6 +36,7 @@ struct vb2_dc_buf { /* MMAP related */ struct vb2_vmarea_handler handler; atomic_t refcount;
struct sg_table *sgt_base;
/* USERPTR related */ struct vm_area_struct *vma;
@@ -142,6 +143,10 @@ static void vb2_dc_put(void *buf_priv) if (!atomic_dec_and_test(&buf->refcount)) return;
- if (buf->sgt_base) {
sg_free_table(buf->sgt_base);
kfree(buf->sgt_base);
- } dma_free_coherent(buf->dev, buf->size, buf->vaddr, buf->dma_addr); kfree(buf);
} @@ -213,6 +218,200 @@ static int vb2_dc_mmap(void *buf_priv, struct vm_area_struct *vma) }
/*********************************************/ +/* DMABUF ops for exporters */ +/*********************************************/
+struct vb2_dc_attachment {
- struct sg_table sgt;
- enum dma_data_direction dir;
+};
+static int vb2_dc_dmabuf_ops_attach(struct dma_buf *dbuf, struct device *dev, + struct dma_buf_attachment *dbuf_attach) +{
- struct vb2_dc_attachment *attach;
- unsigned int i;
- struct scatterlist *rd, *wr;
- struct sg_table *sgt;
- struct vb2_dc_buf *buf = dbuf->priv;
- int ret;
- attach = kzalloc(sizeof(*attach), GFP_KERNEL);
- if (!attach)
return -ENOMEM;
- sgt = &attach->sgt;
- /* Copy the buf->base_sgt scatter list to the attachment, as we can't
* map the same scatter list to multiple attachments at the same time.
*/
- ret = sg_alloc_table(sgt, buf->sgt_base->orig_nents, GFP_KERNEL);
- if (ret) {
kfree(attach);
return -ENOMEM;
- }
- rd = buf->sgt_base->sgl;
- wr = sgt->sgl;
- for (i = 0; i < sgt->orig_nents; ++i) {
sg_set_page(wr, sg_page(rd), rd->length, rd->offset);
rd = sg_next(rd);
wr = sg_next(wr);
- }
- attach->dir = DMA_NONE;
- dbuf_attach->priv = attach;
- return 0;
+}
+static void vb2_dc_dmabuf_ops_detach(struct dma_buf *dbuf,
- struct dma_buf_attachment *db_attach)
+{
- struct vb2_dc_attachment *attach = db_attach->priv;
- struct sg_table *sgt;
- if (!attach)
return;
- sgt = &attach->sgt;
- /* release the scatterlist cache */
- if (attach->dir != DMA_NONE)
dma_unmap_sg(db_attach->dev, sgt->sgl, sgt->orig_nents,
attach->dir);
- sg_free_table(sgt);
- kfree(attach);
- db_attach->priv = NULL;
+}
+static struct sg_table *vb2_dc_dmabuf_ops_map(
- struct dma_buf_attachment *db_attach, enum dma_data_direction dir)
+{
- struct vb2_dc_attachment *attach = db_attach->priv;
- /* stealing dmabuf mutex to serialize map/unmap operations */
- struct mutex *lock = &db_attach->dmabuf->lock;
- struct sg_table *sgt;
- int ret;
- mutex_lock(lock);
- sgt = &attach->sgt;
- /* return previously mapped sg table */
- if (attach->dir == dir) {
mutex_unlock(lock);
return sgt;
- }
- /* release any previous cache */
- if (attach->dir != DMA_NONE) {
dma_unmap_sg(db_attach->dev, sgt->sgl, sgt->orig_nents,
attach->dir);
attach->dir = DMA_NONE;
- }
- /* mapping to the client with new direction */
- ret = dma_map_sg(db_attach->dev, sgt->sgl, sgt->orig_nents, dir);
- if (ret <= 0) {
pr_err("failed to map scatterlist\n");
mutex_unlock(lock);
return ERR_PTR(-EIO);
- }
- attach->dir = dir;
- mutex_unlock(lock);
- return sgt;
+}
+static void vb2_dc_dmabuf_ops_unmap(struct dma_buf_attachment *db_attach,
- struct sg_table *sgt, enum dma_data_direction dir)
+{
- /* nothing to be done here */
+}
+static void vb2_dc_dmabuf_ops_release(struct dma_buf *dbuf) +{
- /* drop reference obtained in vb2_dc_get_dmabuf */
- vb2_dc_put(dbuf->priv);
+}
+static void *vb2_dc_dmabuf_ops_kmap(struct dma_buf *dbuf, unsigned long pgnum) +{
- struct vb2_dc_buf *buf = dbuf->priv;
- return buf->vaddr + pgnum * PAGE_SIZE;
+}
+static void *vb2_dc_dmabuf_ops_vmap(struct dma_buf *dbuf) +{
- struct vb2_dc_buf *buf = dbuf->priv;
- return buf->vaddr;
+}
+static int vb2_dc_dmabuf_ops_mmap(struct dma_buf *dbuf,
- struct vm_area_struct *vma)
+{
- return vb2_dc_mmap(dbuf->priv, vma);
+}
+static struct dma_buf_ops vb2_dc_dmabuf_ops = {
- .attach = vb2_dc_dmabuf_ops_attach,
- .detach = vb2_dc_dmabuf_ops_detach,
- .map_dma_buf = vb2_dc_dmabuf_ops_map,
- .unmap_dma_buf = vb2_dc_dmabuf_ops_unmap,
- .kmap = vb2_dc_dmabuf_ops_kmap,
- .kmap_atomic = vb2_dc_dmabuf_ops_kmap,
- .vmap = vb2_dc_dmabuf_ops_vmap,
- .mmap = vb2_dc_dmabuf_ops_mmap,
- .release = vb2_dc_dmabuf_ops_release,
+};
+static struct sg_table *vb2_dc_get_base_sgt(struct vb2_dc_buf *buf) +{
- int ret;
- struct sg_table *sgt;
- sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
- if (!sgt) {
dev_err(buf->dev, "failed to alloc sg table\n");
return NULL;
- }
- ret = dma_get_sgtable(buf->dev, sgt, buf->vaddr, buf->dma_addr,
buf->size);
- if (ret < 0) {
dev_err(buf->dev, "failed to get scatterlist from DMA API\n");
kfree(sgt);
return NULL;
- }
- return sgt;
+}
+static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv) +{
- struct vb2_dc_buf *buf = buf_priv;
- struct dma_buf *dbuf;
- if (!buf->sgt_base)
buf->sgt_base = vb2_dc_get_base_sgt(buf);
- if (WARN_ON(!buf->sgt_base))
return NULL;
- dbuf = dma_buf_export(buf, &vb2_dc_dmabuf_ops, buf->size, 0);
- if (IS_ERR(dbuf))
return NULL;
- /* dmabuf keeps reference to vb2 buffer */
- atomic_inc(&buf->refcount);
- return dbuf;
+}
+/*********************************************/ /* callbacks for USERPTR buffers */ /*********************************************/
@@ -519,6 +718,7 @@ static void *vb2_dc_attach_dmabuf(void *alloc_ctx, struct dma_buf *dbuf, const struct vb2_mem_ops vb2_dma_contig_memops = { .alloc = vb2_dc_alloc, .put = vb2_dc_put,
- .get_dmabuf = vb2_dc_get_dmabuf, .cookie = vb2_dc_cookie, .vaddr = vb2_dc_vaddr, .mmap = vb2_dc_mmap,
This patch adds taking reference to the device for MMAP buffers.
Such buffers, may be exported using DMABUF mechanism. If the driver that created a queue is unloaded then the queue is released, the device might be released too. However, buffers cannot be released if they are referenced by DMABUF descriptor(s). The device pointer kept in a buffer must be valid for the whole buffer's lifetime. Therefore MMAP buffers should take a reference to the device to avoid risk of dangling pointers.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com --- drivers/media/video/videobuf2-dma-contig.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c index b138b5c..b4d287a 100644 --- a/drivers/media/video/videobuf2-dma-contig.c +++ b/drivers/media/video/videobuf2-dma-contig.c @@ -148,6 +148,7 @@ static void vb2_dc_put(void *buf_priv) kfree(buf->sgt_base); } dma_free_coherent(buf->dev, buf->size, buf->vaddr, buf->dma_addr); + put_device(buf->dev); kfree(buf); }
@@ -161,9 +162,13 @@ static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size) if (!buf) return ERR_PTR(-ENOMEM);
+ /* prevent the device from release while the buffer is exported */ + get_device(dev); + buf->vaddr = dma_alloc_coherent(dev, size, &buf->dma_addr, GFP_KERNEL); if (!buf->vaddr) { dev_err(dev, "dma_alloc_coherent of size %ld failed\n", size); + put_device(dev); kfree(buf); return ERR_PTR(-ENOMEM); }
Hi Tomasz,
Thanks for the patch.
On Tuesday 02 October 2012 16:27:32 Tomasz Stanislawski wrote:
This patch adds taking reference to the device for MMAP buffers.
Such buffers, may be exported using DMABUF mechanism. If the driver that created a queue is unloaded then the queue is released, the device might be released too. However, buffers cannot be released if they are referenced by DMABUF descriptor(s). The device pointer kept in a buffer must be valid for the whole buffer's lifetime. Therefore MMAP buffers should take a reference to the device to avoid risk of dangling pointers.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com
drivers/media/video/videobuf2-dma-contig.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c index b138b5c..b4d287a 100644 --- a/drivers/media/video/videobuf2-dma-contig.c +++ b/drivers/media/video/videobuf2-dma-contig.c @@ -148,6 +148,7 @@ static void vb2_dc_put(void *buf_priv) kfree(buf->sgt_base); } dma_free_coherent(buf->dev, buf->size, buf->vaddr, buf->dma_addr);
- put_device(buf->dev); kfree(buf);
}
@@ -161,9 +162,13 @@ static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size) if (!buf) return ERR_PTR(-ENOMEM);
- /* prevent the device from release while the buffer is exported */
- get_device(dev);
What about moving this below the dma_alloc_coherent() call ? You could then avoid the put_device() call in the error path.
buf->vaddr = dma_alloc_coherent(dev, size, &buf->dma_addr, GFP_KERNEL); if (!buf->vaddr) { dev_err(dev, "dma_alloc_coherent of size %ld failed\n", size);
kfree(buf); return ERR_PTR(-ENOMEM); }put_device(dev);
Something like
- buf->dev = dev; + buf->dev = get_device(dev); buf->size = size
From: Marek Szyprowski m.szyprowski@samsung.com
The DMA transfer must be aligned to a specific value. If userptr is not aligned to DMA requirements then unexpected corruptions of the memory may occur before or after a buffer. To prevent such situations, all unligned userptr buffers are rejected at VIDIOC_QBUF.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- drivers/media/video/videobuf2-dma-contig.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c index b4d287a..55f8c80 100644 --- a/drivers/media/video/videobuf2-dma-contig.c +++ b/drivers/media/video/videobuf2-dma-contig.c @@ -494,6 +494,13 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, struct vm_area_struct *vma; struct sg_table *sgt; unsigned long contig_size; + unsigned long dma_align = dma_get_cache_alignment(); + + /* Only cache aligned DMA transfers are reliable */ + if (!IS_ALIGNED(vaddr | size, dma_align)) { + pr_err("user data must be aligned to %lu bytes\n", dma_align); + return ERR_PTR(-EINVAL); + }
buf = kzalloc(sizeof *buf, GFP_KERNEL); if (!buf)
Hi Tomasz,
Thanks for the patch.
On Tuesday 02 October 2012 16:27:33 Tomasz Stanislawski wrote:
From: Marek Szyprowski m.szyprowski@samsung.com
The DMA transfer must be aligned to a specific value. If userptr is not aligned to DMA requirements then unexpected corruptions of the memory may occur before or after a buffer. To prevent such situations, all unligned userptr buffers are rejected at VIDIOC_QBUF.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com
drivers/media/video/videobuf2-dma-contig.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c index b4d287a..55f8c80 100644 --- a/drivers/media/video/videobuf2-dma-contig.c +++ b/drivers/media/video/videobuf2-dma-contig.c @@ -494,6 +494,13 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, struct vm_area_struct *vma; struct sg_table *sgt; unsigned long contig_size;
- unsigned long dma_align = dma_get_cache_alignment();
- /* Only cache aligned DMA transfers are reliable */
- if (!IS_ALIGNED(vaddr | size, dma_align)) {
pr_err("user data must be aligned to %lu bytes\n", dma_align);
return ERR_PTR(-EINVAL);
Do you think EFAULT would be more descriptive ? EINVAL is already used quite extensively. We could then possibly turn pr_err() into pr_dbg() to avoid flooding the kernel log.
}
buf = kzalloc(sizeof *buf, GFP_KERNEL); if (!buf)
This patch enhances s5p-fimc with support for DMABUF exporting via VIDIOC_EXPBUF ioctl.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com CC: Sylwester Nawrocki s.nawrocki@samsung.com --- drivers/media/video/s5p-fimc/fimc-capture.c | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 3fcaf7d..bdd2de5 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -1129,6 +1129,14 @@ static int fimc_cap_qbuf(struct file *file, void *priv, return vb2_qbuf(&fimc->vid_cap.vbq, buf); }
+static int fimc_cap_expbuf(struct file *file, void *priv, + struct v4l2_exportbuffer *eb) +{ + struct fimc_dev *fimc = video_drvdata(file); + + return vb2_expbuf(&fimc->vid_cap.vbq, eb); +} + static int fimc_cap_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { @@ -1253,6 +1261,7 @@ static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = {
.vidioc_qbuf = fimc_cap_qbuf, .vidioc_dqbuf = fimc_cap_dqbuf, + .vidioc_expbuf = fimc_cap_expbuf,
.vidioc_prepare_buf = fimc_cap_prepare_buf, .vidioc_create_bufs = fimc_cap_create_bufs,
This patch enhances s5p-tv with support for DMABUF exporting via VIDIOC_EXPBUF ioctl.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/media/video/s5p-tv/mixer_video.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c index 2c299db..d1641b6 100644 --- a/drivers/media/video/s5p-tv/mixer_video.c +++ b/drivers/media/video/s5p-tv/mixer_video.c @@ -697,6 +697,15 @@ static int mxr_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) return vb2_dqbuf(&layer->vb_queue, p, file->f_flags & O_NONBLOCK); }
+static int mxr_expbuf(struct file *file, void *priv, + struct v4l2_exportbuffer *eb) +{ + struct mxr_layer *layer = video_drvdata(file); + + mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); + return vb2_expbuf(&layer->vb_queue, eb); +} + static int mxr_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { struct mxr_layer *layer = video_drvdata(file); @@ -724,6 +733,7 @@ static const struct v4l2_ioctl_ops mxr_ioctl_ops = { .vidioc_querybuf = mxr_querybuf, .vidioc_qbuf = mxr_qbuf, .vidioc_dqbuf = mxr_dqbuf, + .vidioc_expbuf = mxr_expbuf, /* Streaming control */ .vidioc_streamon = mxr_streamon, .vidioc_streamoff = mxr_streamoff,
This patch enhances s5p-mfc with support for DMABUF exporting via VIDIOC_EXPBUF ioctl.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com CC: Kamil Debski k.debski@samsung.com --- drivers/media/video/s5p-mfc/s5p_mfc_dec.c | 14 ++++++++++++++ drivers/media/video/s5p-mfc/s5p_mfc_enc.c | 14 ++++++++++++++ 2 files changed, 28 insertions(+)
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c index c5d567f..c2f4f49 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c @@ -570,6 +570,19 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) return -EINVAL; }
+/* Export DMA buffer */ +static int vidioc_expbuf(struct file *file, void *priv, + struct v4l2_exportbuffer *eb) +{ + struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); + + if (eb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return vb2_expbuf(&ctx->vq_src, eb); + if (eb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return vb2_expbuf(&ctx->vq_dst, eb); + return -EINVAL; +} + /* Stream on */ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type type) @@ -745,6 +758,7 @@ static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = { .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_expbuf = vidioc_expbuf, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_g_crop = vidioc_g_crop, diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c index aa1c244..a08ce0a 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c @@ -1142,6 +1142,19 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) return -EINVAL; }
+/* Export DMA buffer */ +static int vidioc_expbuf(struct file *file, void *priv, + struct v4l2_exportbuffer *eb) +{ + struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); + + if (eb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return vb2_expbuf(&ctx->vq_src, eb); + if (eb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return vb2_expbuf(&ctx->vq_dst, eb); + return -EINVAL; +} + /* Stream on */ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type type) @@ -1487,6 +1500,7 @@ static const struct v4l2_ioctl_ops s5p_mfc_enc_ioctl_ops = { .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_expbuf = vidioc_expbuf, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_s_parm = vidioc_s_parm,
On Tue October 2 2012 16:27:11 Tomasz Stanislawski wrote:
Hello everyone, This patchset adds support for DMABUF [2] importing and exporting to V4L2 stack.
Hi Tomasz,
Thanks for this patch series! I've finished reviewing it, and only found minor things. The only (minor) API issue I found is in patch 18 where I would suggest that reordering the fields will make things a bit more natural.
For patches 1, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, 19, 20, 21, 22, 23, 24, 25:
Acked-by: Hans Verkuil hans.verkuil@cisco.com
After fixing the typos you can also add my Acked-by for patch 9.
Regards,
Hans
v9:
- rebase on 3.6
- change type for fs to __s32
- add support for vb2_ioctl_expbuf
- remove patch 'v4l: vb2: add support for DMA_ATTR_NO_KERNEL_MAPPING', it will be posted as a separate patch
- fix typos and style in Documentation (from Hans Verkuil)
- only vb2-core and vb2-dma-contig selects DMA_SHARED_BUFFER in Kconfig
- use data_offset and length while queueing DMABUF
- return the most recently used fd at VIDIOC_DQBUF
- use (buffer-type, index, plane) tuple instead of mem_offset to identify a for exporting (from Hans Verkuil)
- support for DMABUF mmap in vb2-dma-contig (from Laurent Pinchart)
- add testing alignment of vaddr and size while verifying userptr against DMA capabilities (from Laurent Pinchart)
- substitute VM_DONTDUMP with VM_RESERVED
- simplify error handling in vb2_dc_get_dmabuf (from Laurent Pinchart)
v8:
- rebased on 3.6-rc1
- merged importer and exporter patchsets
- fixed missing fields in v4l2_plane32 and v4l2_buffer32 structs
- fixed typos/style in documentation
- significant reduction of warnings from checkpatch.pl
- fixed STREAMOFF issues reported by Dima Zavin [4] by adding __vb2_dqbuf helper to vb2-core
- DC fails if userptr is not correctly aligned
- add support for DMA attributes in DC
- add support for buffers with no kernel mapping
- add reference counting on device from allocator context
- dummy support for mmap
- use dma_get_sgtable, drop vb2_dc_kaddr_to_pages hack and vb2_dc_get_base_sgt helper
v7:
- support for V4L2_MEMORY_DMABUF in v4l2-compact-ioctl32.c
- cosmetic fixes to the documentation
- added importing for vmalloc because vmap support in dmabuf for 3.5 was pull-requested
- support for dmabuf importing for VIVI
- resurrect allocation of dma-contig context
- remove reference of alloc_ctx in dma-contig buffer
- use sg_alloc_table_from_pages
- fix DMA scatterlist calls to use orig_nents instead of nents
- fix memleak in vb2_dc_sgt_foreach_page (use orig_nents instead of nents)
v6:
- fixed missing entry in v4l2_memory_names
- fixed a bug occuring after get_user_pages failure
- fixed a bug caused by using invalid vma for get_user_pages
- prepare/finish no longer call dma_sync for dmabuf buffers
v5:
- removed change of importer/exporter behaviour
- fixes vb2_dc_pages_to_sgt basing on Laurent's hints
- changed pin/unpin words to lock/unlock in Doc
v4:
- rebased on mainline 3.4-rc2
- included missing importing support for s5p-fimc and s5p-tv
- added patch for changing map/unmap for importers
- fixes to Documentation part
- coding style fixes
- pairing {map/unmap}_dmabuf in vb2-core
- fixing variable types and semantic of arguments in videobufb2-dma-contig.c
v3:
- rebased on mainline 3.4-rc1
- split 'code refactor' patch to multiple smaller patches
- squashed fixes to Sumit's patches
- patchset is no longer dependant on 'DMA mapping redesign'
- separated path for handling IO and non-IO mappings
- add documentation for DMABUF importing to V4L
- removed all DMABUF exporter related code
- removed usage of dma_get_pages extension
v2:
- extended VIDIOC_EXPBUF argument from integer memoffset to struct v4l2_exportbuffer
- added patch that breaks DMABUF spec on (un)map_atachment callcacks but allows to work with existing implementation of DMABUF prime in DRM
- all dma-contig code refactoring patches were squashed
- bugfixes
v1: List of changes since [1].
- support for DMA api extension dma_get_pages, the function is used to retrieve pages used to create DMA mapping.
- small fixes/code cleanup to videobuf2
- added prepare and finish callbacks to vb2 allocators, it is used keep consistency between dma-cpu acess to the memory (by Marek Szyprowski)
- support for exporting of DMABUF buffer in V4L2 and Videobuf2, originated from [3].
- support for dma-buf exporting in vb2-dma-contig allocator
- support for DMABUF for s5p-tv and s5p-fimc (capture interface) drivers, originated from [3]
- changed handling for userptr buffers (by Marek Szyprowski, Andrzej Pietrasiewicz)
- let mmap method to use dma_mmap_writecombine call (by Marek Szyprowski)
[1] http://thread.gmane.org/gmane.linux.drivers.video-input-infrastructure/42966... [2] https://lkml.org/lkml/2011/12/26/29 [3] http://thread.gmane.org/gmane.linux.kernel.cross-arch/12819 [4] http://article.gmane.org/gmane.linux.drivers.video-input-infrastructure/4970...
Laurent Pinchart (2): v4l: vb2-dma-contig: shorten vb2_dma_contig prefix to vb2_dc v4l: vb2-dma-contig: reorder functions
Marek Szyprowski (4): v4l: vb2: add prepare/finish callbacks to allocators v4l: vb2-dma-contig: add prepare/finish to dma-contig allocator v4l: vb2-dma-contig: let mmap method to use dma_mmap_coherent call v4l: vb2-dma-contig: fail if user ptr buffer is not correctly aligned
Sumit Semwal (4): v4l: Add DMABUF as a memory type v4l: vb2: add support for shared buffer (dma_buf) v4l: vb: remove warnings about MEMORY_DMABUF v4l: vb2-dma-contig: add support for dma_buf importing
Tomasz Stanislawski (15): Documentation: media: description of DMABUF importing in V4L2 v4l: vb2-dma-contig: remove reference of alloc_ctx from a buffer v4l: vb2-dma-contig: add support for scatterlist in userptr mode v4l: vb2-vmalloc: add support for dmabuf importing v4l: vivi: support for dmabuf importing v4l: s5p-tv: mixer: support for dmabuf importing v4l: s5p-fimc: support for dmabuf importing Documentation: media: description of DMABUF exporting in V4L2 v4l: add buffer exporting via dmabuf v4l: vb2: add buffer exporting via dmabuf v4l: vb2-dma-contig: add support for DMABUF exporting v4l: vb2-dma-contig: add reference counting for a device from allocator context v4l: s5p-fimc: support for dmabuf exporting v4l: s5p-tv: mixer: support for dmabuf exporting v4l: s5p-mfc: support for dmabuf exporting
Documentation/DocBook/media/v4l/compat.xml | 7 + Documentation/DocBook/media/v4l/io.xml | 183 +++++- Documentation/DocBook/media/v4l/v4l2.xml | 1 + .../DocBook/media/v4l/vidioc-create-bufs.xml | 16 +- Documentation/DocBook/media/v4l/vidioc-expbuf.xml | 212 ++++++ Documentation/DocBook/media/v4l/vidioc-qbuf.xml | 17 + Documentation/DocBook/media/v4l/vidioc-reqbufs.xml | 47 +- drivers/media/video/Kconfig | 2 + drivers/media/video/s5p-fimc/fimc-capture.c | 11 +- drivers/media/video/s5p-mfc/s5p_mfc_dec.c | 14 + drivers/media/video/s5p-mfc/s5p_mfc_enc.c | 14 + drivers/media/video/s5p-tv/mixer_video.c | 12 +- drivers/media/video/v4l2-compat-ioctl32.c | 19 + drivers/media/video/v4l2-dev.c | 1 + drivers/media/video/v4l2-ioctl.c | 11 + drivers/media/video/videobuf-core.c | 4 + drivers/media/video/videobuf2-core.c | 300 ++++++++- drivers/media/video/videobuf2-dma-contig.c | 695 ++++++++++++++++++-- drivers/media/video/videobuf2-memops.c | 40 -- drivers/media/video/videobuf2-vmalloc.c | 56 ++ drivers/media/video/vivi.c | 2 +- include/linux/videodev2.h | 35 + include/media/v4l2-ioctl.h | 2 + include/media/videobuf2-core.h | 38 ++ include/media/videobuf2-memops.h | 5 - 25 files changed, 1608 insertions(+), 136 deletions(-) create mode 100644 Documentation/DocBook/media/v4l/vidioc-expbuf.xml
Hi all!
On Tue October 2 2012 16:27:11 Tomasz Stanislawski wrote:
Hello everyone, This patchset adds support for DMABUF [2] importing and exporting to V4L2 stack.
I see that there is a lot of interest to getting this into 3.7. I understand that, but IMHO this is a bit too soon. I would like to see the review comments I made for v9 addressed, and more importantly, I would *really* like to see mem2mem_testdev.c be dmabuf aware. One of the problems today is that it is hard to test this, but if vivi and mem2mem_testdev can do dmabuf, then it is easy to setup a pipeline that everyone can use for testing and application development without requiring special hardware.
My suggestion would be to get a v10 out, and merge that as soon as the for_v3.8 branch opens. Then get mem2mem_testdev ready for dmabuf as well for 3.8.
Regards,
Hans
v9:
- rebase on 3.6
- change type for fs to __s32
- add support for vb2_ioctl_expbuf
- remove patch 'v4l: vb2: add support for DMA_ATTR_NO_KERNEL_MAPPING', it will be posted as a separate patch
- fix typos and style in Documentation (from Hans Verkuil)
- only vb2-core and vb2-dma-contig selects DMA_SHARED_BUFFER in Kconfig
- use data_offset and length while queueing DMABUF
- return the most recently used fd at VIDIOC_DQBUF
- use (buffer-type, index, plane) tuple instead of mem_offset to identify a for exporting (from Hans Verkuil)
- support for DMABUF mmap in vb2-dma-contig (from Laurent Pinchart)
- add testing alignment of vaddr and size while verifying userptr against DMA capabilities (from Laurent Pinchart)
- substitute VM_DONTDUMP with VM_RESERVED
- simplify error handling in vb2_dc_get_dmabuf (from Laurent Pinchart)
v8:
- rebased on 3.6-rc1
- merged importer and exporter patchsets
- fixed missing fields in v4l2_plane32 and v4l2_buffer32 structs
- fixed typos/style in documentation
- significant reduction of warnings from checkpatch.pl
- fixed STREAMOFF issues reported by Dima Zavin [4] by adding __vb2_dqbuf helper to vb2-core
- DC fails if userptr is not correctly aligned
- add support for DMA attributes in DC
- add support for buffers with no kernel mapping
- add reference counting on device from allocator context
- dummy support for mmap
- use dma_get_sgtable, drop vb2_dc_kaddr_to_pages hack and vb2_dc_get_base_sgt helper
v7:
- support for V4L2_MEMORY_DMABUF in v4l2-compact-ioctl32.c
- cosmetic fixes to the documentation
- added importing for vmalloc because vmap support in dmabuf for 3.5 was pull-requested
- support for dmabuf importing for VIVI
- resurrect allocation of dma-contig context
- remove reference of alloc_ctx in dma-contig buffer
- use sg_alloc_table_from_pages
- fix DMA scatterlist calls to use orig_nents instead of nents
- fix memleak in vb2_dc_sgt_foreach_page (use orig_nents instead of nents)
v6:
- fixed missing entry in v4l2_memory_names
- fixed a bug occuring after get_user_pages failure
- fixed a bug caused by using invalid vma for get_user_pages
- prepare/finish no longer call dma_sync for dmabuf buffers
v5:
- removed change of importer/exporter behaviour
- fixes vb2_dc_pages_to_sgt basing on Laurent's hints
- changed pin/unpin words to lock/unlock in Doc
v4:
- rebased on mainline 3.4-rc2
- included missing importing support for s5p-fimc and s5p-tv
- added patch for changing map/unmap for importers
- fixes to Documentation part
- coding style fixes
- pairing {map/unmap}_dmabuf in vb2-core
- fixing variable types and semantic of arguments in videobufb2-dma-contig.c
v3:
- rebased on mainline 3.4-rc1
- split 'code refactor' patch to multiple smaller patches
- squashed fixes to Sumit's patches
- patchset is no longer dependant on 'DMA mapping redesign'
- separated path for handling IO and non-IO mappings
- add documentation for DMABUF importing to V4L
- removed all DMABUF exporter related code
- removed usage of dma_get_pages extension
v2:
- extended VIDIOC_EXPBUF argument from integer memoffset to struct v4l2_exportbuffer
- added patch that breaks DMABUF spec on (un)map_atachment callcacks but allows to work with existing implementation of DMABUF prime in DRM
- all dma-contig code refactoring patches were squashed
- bugfixes
v1: List of changes since [1].
- support for DMA api extension dma_get_pages, the function is used to retrieve pages used to create DMA mapping.
- small fixes/code cleanup to videobuf2
- added prepare and finish callbacks to vb2 allocators, it is used keep consistency between dma-cpu acess to the memory (by Marek Szyprowski)
- support for exporting of DMABUF buffer in V4L2 and Videobuf2, originated from [3].
- support for dma-buf exporting in vb2-dma-contig allocator
- support for DMABUF for s5p-tv and s5p-fimc (capture interface) drivers, originated from [3]
- changed handling for userptr buffers (by Marek Szyprowski, Andrzej Pietrasiewicz)
- let mmap method to use dma_mmap_writecombine call (by Marek Szyprowski)
[1] http://thread.gmane.org/gmane.linux.drivers.video-input-infrastructure/42966... [2] https://lkml.org/lkml/2011/12/26/29 [3] http://thread.gmane.org/gmane.linux.kernel.cross-arch/12819 [4] http://article.gmane.org/gmane.linux.drivers.video-input-infrastructure/4970...
Laurent Pinchart (2): v4l: vb2-dma-contig: shorten vb2_dma_contig prefix to vb2_dc v4l: vb2-dma-contig: reorder functions
Marek Szyprowski (4): v4l: vb2: add prepare/finish callbacks to allocators v4l: vb2-dma-contig: add prepare/finish to dma-contig allocator v4l: vb2-dma-contig: let mmap method to use dma_mmap_coherent call v4l: vb2-dma-contig: fail if user ptr buffer is not correctly aligned
Sumit Semwal (4): v4l: Add DMABUF as a memory type v4l: vb2: add support for shared buffer (dma_buf) v4l: vb: remove warnings about MEMORY_DMABUF v4l: vb2-dma-contig: add support for dma_buf importing
Tomasz Stanislawski (15): Documentation: media: description of DMABUF importing in V4L2 v4l: vb2-dma-contig: remove reference of alloc_ctx from a buffer v4l: vb2-dma-contig: add support for scatterlist in userptr mode v4l: vb2-vmalloc: add support for dmabuf importing v4l: vivi: support for dmabuf importing v4l: s5p-tv: mixer: support for dmabuf importing v4l: s5p-fimc: support for dmabuf importing Documentation: media: description of DMABUF exporting in V4L2 v4l: add buffer exporting via dmabuf v4l: vb2: add buffer exporting via dmabuf v4l: vb2-dma-contig: add support for DMABUF exporting v4l: vb2-dma-contig: add reference counting for a device from allocator context v4l: s5p-fimc: support for dmabuf exporting v4l: s5p-tv: mixer: support for dmabuf exporting v4l: s5p-mfc: support for dmabuf exporting
Documentation/DocBook/media/v4l/compat.xml | 7 + Documentation/DocBook/media/v4l/io.xml | 183 +++++- Documentation/DocBook/media/v4l/v4l2.xml | 1 + .../DocBook/media/v4l/vidioc-create-bufs.xml | 16 +- Documentation/DocBook/media/v4l/vidioc-expbuf.xml | 212 ++++++ Documentation/DocBook/media/v4l/vidioc-qbuf.xml | 17 + Documentation/DocBook/media/v4l/vidioc-reqbufs.xml | 47 +- drivers/media/video/Kconfig | 2 + drivers/media/video/s5p-fimc/fimc-capture.c | 11 +- drivers/media/video/s5p-mfc/s5p_mfc_dec.c | 14 + drivers/media/video/s5p-mfc/s5p_mfc_enc.c | 14 + drivers/media/video/s5p-tv/mixer_video.c | 12 +- drivers/media/video/v4l2-compat-ioctl32.c | 19 + drivers/media/video/v4l2-dev.c | 1 + drivers/media/video/v4l2-ioctl.c | 11 + drivers/media/video/videobuf-core.c | 4 + drivers/media/video/videobuf2-core.c | 300 ++++++++- drivers/media/video/videobuf2-dma-contig.c | 695 ++++++++++++++++++-- drivers/media/video/videobuf2-memops.c | 40 -- drivers/media/video/videobuf2-vmalloc.c | 56 ++ drivers/media/video/vivi.c | 2 +- include/linux/videodev2.h | 35 + include/media/v4l2-ioctl.h | 2 + include/media/videobuf2-core.h | 38 ++ include/media/videobuf2-memops.h | 5 - 25 files changed, 1608 insertions(+), 136 deletions(-) create mode 100644 Documentation/DocBook/media/v4l/vidioc-expbuf.xml
Hi,
Em Tue, 02 Oct 2012 16:27:11 +0200 Tomasz Stanislawski t.stanislaws@samsung.com escreveu:
Hello everyone, This patchset adds support for DMABUF [2] importing and exporting to V4L2 stack.
v9:
- rebase on 3.6
- change type for fs to __s32
- add support for vb2_ioctl_expbuf
- remove patch 'v4l: vb2: add support for DMA_ATTR_NO_KERNEL_MAPPING', it will be posted as a separate patch
- fix typos and style in Documentation (from Hans Verkuil)
- only vb2-core and vb2-dma-contig selects DMA_SHARED_BUFFER in Kconfig
- use data_offset and length while queueing DMABUF
- return the most recently used fd at VIDIOC_DQBUF
- use (buffer-type, index, plane) tuple instead of mem_offset to identify a for exporting (from Hans Verkuil)
- support for DMABUF mmap in vb2-dma-contig (from Laurent Pinchart)
- add testing alignment of vaddr and size while verifying userptr against DMA capabilities (from Laurent Pinchart)
- substitute VM_DONTDUMP with VM_RESERVED
- simplify error handling in vb2_dc_get_dmabuf (from Laurent Pinchart)
For now, NACK. See below.
I spent 4 days trying to setup an environment that would allow testing DMABUF with video4linux without success (long story, see below). Also, Laurent tried the same without any luck, and it seems that it even corrupted his test machine.
Basically Samsung generously donated me two boards that it could be used on this test (Origen and SMDK310). None of them actually worked with the upstream kernel: patches are needed to avoid OOPSes on Origen; both Origen/SMDK310 defconfigs are completely broken, and drivers don't even boot if someone tries to use the Kernel's defconfigs.
Even after spending _days_ trying to figure out the needed .config options (as the config files are not easily available), both boards have... issues:
- lack of any display output driver at SMDK310;
- lack of any network driver at Origen: it seems that none of the available network options on Origen was upstreamed: no Bluetooth, no OTG, no Wifi.
The only test I was able to do (yesterday, late night), the DMABUF caused an OOPS at the Origen board. So, for sure it is not ready for upstream.
It is now, too late for 3.7. I might consider it to 3.8, if something can be done to fix the existing issues, and setup a proper setup, using the upstream Kernel.
Regards, Mauro
On 10/10/12, Mauro Carvalho Chehab mchehab@redhat.com wrote:
Hi,
Em Tue, 02 Oct 2012 16:27:11 +0200 Tomasz Stanislawski t.stanislaws@samsung.com escreveu:
Hello everyone, This patchset adds support for DMABUF [2] importing and exporting to V4L2 stack.
v9:
- rebase on 3.6
- change type for fs to __s32
- add support for vb2_ioctl_expbuf
- remove patch 'v4l: vb2: add support for DMA_ATTR_NO_KERNEL_MAPPING', it will be posted as a separate patch
- fix typos and style in Documentation (from Hans Verkuil)
- only vb2-core and vb2-dma-contig selects DMA_SHARED_BUFFER in Kconfig
- use data_offset and length while queueing DMABUF
- return the most recently used fd at VIDIOC_DQBUF
- use (buffer-type, index, plane) tuple instead of mem_offset to identify a for exporting (from Hans Verkuil)
- support for DMABUF mmap in vb2-dma-contig (from Laurent Pinchart)
- add testing alignment of vaddr and size while verifying userptr against DMA capabilities (from Laurent Pinchart)
- substitute VM_DONTDUMP with VM_RESERVED
- simplify error handling in vb2_dc_get_dmabuf (from Laurent Pinchart)
For now, NACK. See below.
Sad news! It's failed to merge by very poor samsung board support at mainline.
CC arm & samsung mailing list.
Thank you, Kyungmin Park
I spent 4 days trying to setup an environment that would allow testing DMABUF with video4linux without success (long story, see below). Also, Laurent tried the same without any luck, and it seems that it even corrupted his test machine.
Basically Samsung generously donated me two boards that it could be used on this test (Origen and SMDK310). None of them actually worked with the upstream kernel: patches are needed to avoid OOPSes on Origen; both Origen/SMDK310 defconfigs are completely broken, and drivers don't even boot if someone tries to use the Kernel's defconfigs.
Even after spending _days_ trying to figure out the needed .config options (as the config files are not easily available), both boards have... issues:
lack of any display output driver at SMDK310;
lack of any network driver at Origen: it seems that none of
the available network options on Origen was upstreamed: no Bluetooth, no OTG, no Wifi.
The only test I was able to do (yesterday, late night), the DMABUF caused an OOPS at the Origen board. So, for sure it is not ready for upstream.
It is now, too late for 3.7. I might consider it to 3.8, if something can be done to fix the existing issues, and setup a proper setup, using the upstream Kernel.
Regards, Mauro
Linaro-mm-sig mailing list Linaro-mm-sig@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-mm-sig
Hi Mauro,
On Wednesday 10 October 2012 07:54:18 Mauro Carvalho Chehab wrote:
Em Tue, 02 Oct 2012 16:27:11 +0200 Tomasz Stanislawski escreveu:
Hello everyone, This patchset adds support for DMABUF [2] importing and exporting to V4L2 stack.
v9:
- rebase on 3.6
- change type for fs to __s32
- add support for vb2_ioctl_expbuf
- remove patch 'v4l: vb2: add support for DMA_ATTR_NO_KERNEL_MAPPING', it will be posted as a separate patch
- fix typos and style in Documentation (from Hans Verkuil)
- only vb2-core and vb2-dma-contig selects DMA_SHARED_BUFFER in Kconfig
- use data_offset and length while queueing DMABUF
- return the most recently used fd at VIDIOC_DQBUF
- use (buffer-type, index, plane) tuple instead of mem_offset to identify a for exporting (from Hans Verkuil)
- support for DMABUF mmap in vb2-dma-contig (from Laurent Pinchart)
- add testing alignment of vaddr and size while verifying userptr against DMA capabilities (from Laurent Pinchart)
- substitute VM_DONTDUMP with VM_RESERVED
- simplify error handling in vb2_dc_get_dmabuf (from Laurent Pinchart)
For now, NACK. See below.
I spent 4 days trying to setup an environment that would allow testing DMABUF with video4linux without success (long story, see below). Also, Laurent tried the same without any luck, and it seems that it even corrupted his test machine.
To be exact, the i915 driver crashed, not due to DMABUF but because it lacks support for YUV frame buffer on the CRTC (YUV is only supported in extra planes) and failed to properly reject the set mode ioctl.
My test machine is doing fine :-)
I'll modify the test application to create a YUV plane and will let you know about the result.
Basically Samsung generously donated me two boards that it could be used on this test (Origen and SMDK310). None of them actually worked with the upstream kernel: patches are needed to avoid OOPSes on Origen; both Origen/SMDK310 defconfigs are completely broken, and drivers don't even boot if someone tries to use the Kernel's defconfigs.
Even after spending _days_ trying to figure out the needed .config options (as the config files are not easily available), both boards have... issues:
lack of any display output driver at SMDK310;
lack of any network driver at Origen: it seems that none of
the available network options on Origen was upstreamed: no Bluetooth, no OTG, no Wifi.
The only test I was able to do (yesterday, late night), the DMABUF caused an OOPS at the Origen board. So, for sure it is not ready for upstream.
It is now, too late for 3.7. I might consider it to 3.8, if something can be done to fix the existing issues, and setup a proper setup, using the upstream Kernel.
linaro-mm-sig@lists.linaro.org