You've already forked linux-rockchip
mirror of
https://github.com/armbian/linux-rockchip.git
synced 2026-01-06 11:08:10 -08:00
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:
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user