usb: gadget: uvc: support zero copy with rockchip encoder

Some rockchip encoder (e.g RV1106/RV1103) support to reserve
UVC Header in the encoded image, this patch improve the uvc
driver to support zero copy for uvc formats MJPEG/H264/H265,
and no support zero copy for YUYV.

By default, the uvc driver disable the zero copy, you can
set the ${USB_FUNCTIONS_DIR}/uvc.gs*/uvc_zero_copy to 1
to enable the zero copy.

Signed-off-by: William Wu <william.wu@rock-chips.com>
Change-Id: I19be26170d51f209f3196f546515be665481b8b5
This commit is contained in:
William Wu
2022-07-18 17:23:16 +08:00
committed by Tao Huang
parent 875964ad5d
commit 5452a06eed
4 changed files with 111 additions and 3 deletions

View File

@@ -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;

View File

@@ -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,

View File

@@ -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

View File

@@ -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;