diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h index 67d7be969156..e4992ae9c88c 100644 --- a/drivers/usb/gadget/function/u_uvc.h +++ b/drivers/usb/gadget/function/u_uvc.h @@ -30,6 +30,7 @@ struct f_uvc_opts { bool device_name_allocated; const char *device_name; unsigned int uvc_num_request; + unsigned int uvc_zero_copy; #endif unsigned int control_interface; diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index b510fd4c7234..61a75e1a886d 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -2769,6 +2769,7 @@ UVCG_OPTS_ATTR(streaming_maxburst, streaming_maxburst, 15); UVCG_OPTS_ATTR(pm_qos_latency, pm_qos_latency, PM_QOS_LATENCY_ANY); #if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI) UVCG_OPTS_ATTR(uvc_num_request, uvc_num_request, 64); +UVCG_OPTS_ATTR(uvc_zero_copy, uvc_zero_copy, 1); #endif #undef UVCG_OPTS_ATTR @@ -2878,6 +2879,7 @@ static struct configfs_attribute *uvc_attrs[] = { #if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI) &f_uvc_opts_attr_device_name, &f_uvc_opts_attr_uvc_num_request, + &f_uvc_opts_attr_uvc_zero_copy, #endif &f_uvc_opts_string_attr_function_name, NULL, diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c index 57c1be9013dd..d6383ac4d331 100644 --- a/drivers/usb/gadget/function/uvc_queue.c +++ b/drivers/usb/gadget/function/uvc_queue.c @@ -79,6 +79,72 @@ static int uvc_queue_setup(struct vb2_queue *vq, return 0; } +#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI) +/* + * uvc_dma_buf_phys_to_virt - Get the physical address of the dma_buf and + * translate it to virtual address. + * + * @dbuf: the dma_buf of vb2_plane + * @dev: the device to the actual usb controller + * + * This function is used for dma buf allocated by Contiguous Memory Allocator. + * + * Returns: + * The virtual addresses of the dma_buf. + */ +static void *uvc_dma_buf_phys_to_virt(struct uvc_device *uvc, + struct dma_buf *dbuf) +{ + struct usb_gadget *gadget = uvc->func.config->cdev->gadget; + struct dma_buf_attachment *attachment; + struct sg_table *table; + struct scatterlist *sgl; + dma_addr_t phys = 0; + int i; + + attachment = dma_buf_attach(dbuf, gadget->dev.parent); + if (IS_ERR(attachment)) + return ERR_PTR(-ENOMEM); + + table = dma_buf_map_attachment(attachment, DMA_BIDIRECTIONAL); + if (IS_ERR(table)) { + dma_buf_detach(dbuf, attachment); + return ERR_PTR(-ENOMEM); + } + + for_each_sgtable_sg(table, sgl, i) + phys = sg_phys(sgl); + + dma_buf_unmap_attachment(attachment, table, DMA_BIDIRECTIONAL); + dma_buf_detach(dbuf, attachment); + + if (i > 1) { + uvcg_err(&uvc->func, "Not support mult sgl for uvc zero copy\n"); + return ERR_PTR(-ENOMEM); + } + + return phys_to_virt(phys); +} + +static void *uvc_buffer_mem_prepare(struct vb2_buffer *vb, + struct uvc_video_queue *queue) +{ + struct uvc_video *video = container_of(queue, struct uvc_video, queue); + struct uvc_device *uvc = container_of(video, struct uvc_device, video); + struct f_uvc_opts *opts = fi_to_f_uvc_opts(uvc->func.fi); + void *mem; + + if (!opts->uvc_zero_copy || video->fcc == V4L2_PIX_FMT_YUYV) + return (vb2_plane_vaddr(vb, 0) + vb2_plane_data_offset(vb, 0)); + + mem = uvc_dma_buf_phys_to_virt(uvc, vb->planes[0].dbuf); + if (IS_ERR(mem)) + return ERR_PTR(-ENOMEM); + + return (mem + vb2_plane_data_offset(vb, 0)); +} +#endif + static int uvc_buffer_prepare(struct vb2_buffer *vb) { struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); @@ -95,8 +161,10 @@ static int uvc_buffer_prepare(struct vb2_buffer *vb) return -ENODEV; buf->state = UVC_BUF_STATE_QUEUED; -#ifdef CONFIG_ARCH_ROCKCHIP - buf->mem = vb2_plane_vaddr(vb, 0) + vb2_plane_data_offset(vb, 0); +#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI) + buf->mem = uvc_buffer_mem_prepare(vb, queue); + if (IS_ERR(buf->mem)) + return -ENOMEM; #else buf->mem = vb2_plane_vaddr(vb, 0); #endif diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index d4b204a3c211..4962b5562ba7 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -21,6 +21,24 @@ #include "uvc_video.h" #include "u_uvc.h" +#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI) +static bool uvc_using_zero_copy(struct uvc_video *video) +{ + struct uvc_device *uvc = container_of(video, struct uvc_device, video); + struct f_uvc_opts *opts = fi_to_f_uvc_opts(uvc->func.fi); + + if (opts && opts->uvc_zero_copy && video->fcc != V4L2_PIX_FMT_YUYV) + return true; + else + return false; +} +#else +static inline bool uvc_using_zero_copy(struct uvc_video *video) +{ + return false; +} +#endif + /* -------------------------------------------------------------------------- * Video codecs */ @@ -29,6 +47,20 @@ static int uvc_video_encode_header(struct uvc_video *video, struct uvc_buffer *buf, u8 *data, int len) { + if (uvc_using_zero_copy(video)) { + u8 *mem; + + mem = buf->mem + video->queue.buf_used + + (video->queue.buf_used / (video->req_size - 2)) * 2; + + mem[0] = 2; + mem[1] = UVC_STREAM_EOH | video->fid; + if (buf->bytesused - video->queue.buf_used <= len - 2) + mem[1] |= UVC_STREAM_EOF; + + return 2; + } + data[0] = 2; data[1] = UVC_STREAM_EOH | video->fid; @@ -50,7 +82,8 @@ uvc_video_encode_data(struct uvc_video *video, struct uvc_buffer *buf, mem = buf->mem + queue->buf_used; nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used); - memcpy(data, mem, nbytes); + if (!uvc_using_zero_copy(video)) + memcpy(data, mem, nbytes); queue->buf_used += nbytes; return nbytes; @@ -105,6 +138,10 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, int len = video->req_size; int ret; + if (uvc_using_zero_copy(video)) + req->buf = buf->mem + video->queue.buf_used + + (video->queue.buf_used / (video->req_size - 2)) * 2; + /* Add the header. */ ret = uvc_video_encode_header(video, buf, mem, len); mem += ret;