This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 2fcb797e45b41286a38ea0c91caa731fc0d80f14 Author: niyinghao <[email protected]> AuthorDate: Thu Apr 30 14:03:47 2026 +0800 video/v4l2_cap: add V4L2_MEMORY_DMABUF import support Add dma-buf import path to the V4L2 capture driver. Applications can now use V4L2_MEMORY_DMABUF in REQBUFS and pass dma_buf file descriptors via QBUF. The driver attaches, maps, and tracks imported buffers per slot, releasing them on DQBUF or stream cleanup. This enables zero-copy buffer sharing between camera HAL and downstream consumers through the dma-buf framework. QBUF rejects a dma-buf smaller than one frame, which the lower half could otherwise write past the end of. A slot's previous import is released by the same helper as the stream cleanup uses. Signed-off-by: niyinghao <[email protected]> --- drivers/video/v4l2_cap.c | 177 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 155 insertions(+), 22 deletions(-) diff --git a/drivers/video/v4l2_cap.c b/drivers/video/v4l2_cap.c index fc46f4293e0..0aa5b49e6b2 100644 --- a/drivers/video/v4l2_cap.c +++ b/drivers/video/v4l2_cap.c @@ -105,6 +105,15 @@ struct capture_wait_capture_s typedef struct capture_wait_capture_s capture_wait_capture_t; +#ifdef CONFIG_DMA_SHARED_BUFFER +struct capture_dmabuf_s +{ + FAR struct dma_buf *dmabuf; + FAR struct dma_buf_attachment *attachment; + uintptr_t dmaaddr; +}; +#endif + struct capture_type_inf_s { mutex_t lock_state; @@ -119,6 +128,9 @@ struct capture_type_inf_s FAR uint8_t *bufheap; /* for V4L2_MEMORY_MMAP buffers */ FAR struct pollfd_s *fds; uint32_t seqnum; +#ifdef CONFIG_DMA_SHARED_BUFFER + struct capture_dmabuf_s dmabufinf[V4L2_REQBUFS_COUNT_MAX]; +#endif }; typedef struct capture_type_inf_s capture_type_inf_t; @@ -636,6 +648,35 @@ static void capture_dma_release(FAR struct dma_buf *dmabuf) } } +#ifdef CONFIG_DMA_SHARED_BUFFER +static void capture_dmabuf_release(FAR capture_type_inf_t *type_inf, + uint32_t index) +{ + FAR struct capture_dmabuf_s *db = &type_inf->dmabufinf[index]; + + if (db->dmabuf != NULL && db->attachment != NULL) + { + dma_buf_end_cpu_access(db->dmabuf, DMA_BIDIRECTIONAL); + dma_buf_unmap_attachment(db->attachment, db->dmaaddr, + DMA_BIDIRECTIONAL); + dma_buf_detach(db->dmabuf, db->attachment); + dma_buf_put(db->dmabuf); + } + + memset(db, 0, sizeof(*db)); +} + +static void capture_dmabuf_clear(FAR capture_type_inf_t *type_inf) +{ + int i; + + for (i = 0; i < V4L2_REQBUFS_COUNT_MAX; i++) + { + capture_dmabuf_release(type_inf, i); + } +} +#endif + static int capture_dma_begin_cpu_access(FAR struct dma_buf *dmabuf, enum dma_data_direction direction) { @@ -1369,6 +1410,10 @@ static void cleanup_streamresources(FAR capture_type_inf_t *type_inf, type_inf->bufheap = NULL; } + +#ifdef CONFIG_DMA_SHARED_BUFFER + capture_dmabuf_clear(type_inf); +#endif } static void cleanup_scene_parameter(FAR capture_scene_params_t **vsp) @@ -2377,36 +2422,63 @@ static int capture_reqbufs(FAR struct file *filep, video_framebuff_change_mode(&type_inf->bufinf, reqbufs->mode); ret = video_framebuff_realloc_container(&type_inf->bufinf, reqbufs->count); - if (ret == OK && reqbufs->memory == V4L2_MEMORY_MMAP) + if (ret == OK) { - if (type_inf->bufheap != NULL) + if (reqbufs->memory == V4L2_MEMORY_MMAP) { - if (imgdata->ops->free) + if (type_inf->bufheap != NULL) { - imgdata->ops->free(imgdata, type_inf->bufheap); + if (imgdata->ops->free) + { + imgdata->ops->free(imgdata, type_inf->bufheap); + } + else + { + kumm_free(type_inf->bufheap); + } + } + + if (imgdata->ops->alloc) + { + type_inf->bufheap = imgdata->ops->alloc(imgdata, 32, + reqbufs->count * + get_bufsize(&type_inf->fmt[CAPTURE_FMT_MAIN])); } else { - kumm_free(type_inf->bufheap); + type_inf->bufheap = kumm_memalign(32, reqbufs->count * + get_bufsize(&type_inf->fmt[CAPTURE_FMT_MAIN])); } - } - if (imgdata->ops->alloc) - { - type_inf->bufheap = imgdata->ops->alloc(imgdata, 32, - reqbufs->count * - get_bufsize(&type_inf->fmt[CAPTURE_FMT_MAIN])); + if (type_inf->bufheap == NULL) + { + ret = -ENOMEM; + } } - else +#ifdef CONFIG_DMA_SHARED_BUFFER + else if (reqbufs->memory == V4L2_MEMORY_DMABUF) { - type_inf->bufheap = kumm_memalign(32, reqbufs->count * - get_bufsize(&type_inf->fmt[CAPTURE_FMT_MAIN])); - } + /* For DMABUF import, no heap allocation needed. + * Buffers will be provided via fd in QBUF. + */ - if (type_inf->bufheap == NULL) - { - ret = -ENOMEM; + if (type_inf->bufheap != NULL) + { + if (imgdata->ops->free) + { + imgdata->ops->free(imgdata, type_inf->bufheap); + } + else + { + kumm_free(type_inf->bufheap); + } + + type_inf->bufheap = NULL; + } + + capture_dmabuf_clear(type_inf); } +#endif } } @@ -2485,11 +2557,7 @@ static int capture_qbuf(FAR struct file *filep, } memcpy(&container->buf, buf, sizeof(struct v4l2_buffer)); -#ifdef CONFIG_DMA_SHARED_BUFFER - if (buf->memory == V4L2_MEMORY_MMAP || buf->memory == V4L2_MEMORY_DMABUF) -#else if (buf->memory == V4L2_MEMORY_MMAP) -#endif { /* only use userptr inside the container */ @@ -2497,6 +2565,71 @@ static int capture_qbuf(FAR struct file *filep, container->buf.m.userptr = (unsigned long)(type_inf->bufheap + container->buf.length * buf->index); } +#ifdef CONFIG_DMA_SHARED_BUFFER + else if (buf->memory == V4L2_MEMORY_DMABUF) + { + /* Import dmabuf: get buffer from fd, attach, and map */ + + FAR struct capture_dmabuf_s *dbinfo; + + if (buf->index >= V4L2_REQBUFS_COUNT_MAX) + { + video_framebuff_free_container(&type_inf->bufinf, container); + return -EINVAL; + } + + dbinfo = &type_inf->dmabufinf[buf->index]; + + /* Release previous import if any */ + + capture_dmabuf_release(type_inf, buf->index); + + dbinfo->dmabuf = dma_buf_get(buf->m.fd); + if (dbinfo->dmabuf == NULL) + { + video_framebuff_free_container(&type_inf->bufinf, container); + return -EINVAL; + } + + /* A dmabuf smaller than one frame would hand the lower half a target + * it may write past the end of. + */ + + if (dbinfo->dmabuf->size < + get_bufsize(&type_inf->fmt[CAPTURE_FMT_MAIN])) + { + dma_buf_put(dbinfo->dmabuf); + memset(dbinfo, 0, sizeof(*dbinfo)); + video_framebuff_free_container(&type_inf->bufinf, container); + return -EINVAL; + } + + dbinfo->attachment = dma_buf_attach(dbinfo->dmabuf); + if (dbinfo->attachment == NULL) + { + dma_buf_put(dbinfo->dmabuf); + memset(dbinfo, 0, sizeof(*dbinfo)); + video_framebuff_free_container(&type_inf->bufinf, container); + return -ENOMEM; + } + + dbinfo->dmaaddr = dma_buf_map_attachment(dbinfo->attachment, + DMA_BIDIRECTIONAL); + if (dbinfo->dmaaddr == 0) + { + dma_buf_detach(dbinfo->dmabuf, dbinfo->attachment); + dma_buf_put(dbinfo->dmabuf); + memset(dbinfo, 0, sizeof(*dbinfo)); + video_framebuff_free_container(&type_inf->bufinf, container); + return -EFAULT; + } + + dma_buf_begin_cpu_access(dbinfo->dmabuf, DMA_BIDIRECTIONAL); + + container->buf.length = dbinfo->dmabuf->size; + container->buf.m.userptr = (unsigned long)dbinfo->dmaaddr; + } +#endif video_framebuff_queue_container(&type_inf->bufinf, container);
