mirror of https://github.com/OpenIPC/firmware.git
251 lines
6.6 KiB
Diff
251 lines
6.6 KiB
Diff
--- linux-4.9.37/drivers/usb/gadget/function/uvc_video.c 2017-07-12 16:42:41.000000000 +0300
|
|
+++ linux-4.9.y/drivers/usb/gadget/function/uvc_video.c 2021-06-07 13:01:34.000000000 +0300
|
|
@@ -23,6 +23,8 @@
|
|
#include "uvc_queue.h"
|
|
#include "uvc_video.h"
|
|
|
|
+#include <linux/scatterlist.h>
|
|
+#include <linux/io.h>
|
|
/* --------------------------------------------------------------------------
|
|
* Video codecs
|
|
*/
|
|
@@ -102,9 +104,45 @@
|
|
uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
|
|
struct uvc_buffer *buf)
|
|
{
|
|
+ int ret;
|
|
+#ifdef UVC_SG_REQ
|
|
+ int len;
|
|
+ int ttllen = 0;
|
|
+ unsigned int sg_idx;
|
|
+ u8 *mem = NULL;
|
|
+
|
|
+ for (sg_idx = 0; sg_idx < video->num_sgs; sg_idx++) {
|
|
+ mem = sg_virt(&req->sg[sg_idx]);
|
|
+ len = video->req_size;
|
|
+
|
|
+ /* Add the header. */
|
|
+ ret = uvc_video_encode_header(video, buf, mem, len);
|
|
+ mem += ret;
|
|
+ len -= ret;
|
|
+
|
|
+ /* Process video data. */
|
|
+ ret = uvc_video_encode_data(video, buf, mem, len);
|
|
+ len -= ret;
|
|
+
|
|
+ /* Sync sg buffer len , default is 1024 or 3072 */
|
|
+ sg_set_buf(&req->sg[sg_idx], sg_virt(&req->sg[sg_idx]),
|
|
+ video->req_size - len);
|
|
+ ttllen += video->req_size - len;
|
|
+
|
|
+ if (buf->bytesused == video->queue.buf_used) {
|
|
+ video->queue.buf_used = 0;
|
|
+ buf->state = UVC_BUF_STATE_DONE;
|
|
+ uvcg_queue_next_buffer(&video->queue, buf);
|
|
+ video->fid ^= UVC_STREAM_FID;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ req->num_sgs = sg_idx + 1;
|
|
+ sg_mark_end(&req->sg[sg_idx]);
|
|
+ req->length = ttllen;
|
|
+#else
|
|
void *mem = req->buf;
|
|
int len = video->req_size;
|
|
- int ret;
|
|
|
|
/* Add the header. */
|
|
ret = uvc_video_encode_header(video, buf, mem, len);
|
|
@@ -123,12 +161,33 @@
|
|
uvcg_queue_next_buffer(&video->queue, buf);
|
|
video->fid ^= UVC_STREAM_FID;
|
|
}
|
|
+#endif
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------
|
|
* Request handling
|
|
*/
|
|
|
|
+static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ /*
|
|
+ * Fixme, this is just to workaround the warning by udc core when the ep
|
|
+ * is disabled, this may happens when the uvc application is still
|
|
+ * streaming new data while the uvc gadget driver has already recieved
|
|
+ * the streamoff but the streamoff event is not yet received by the app
|
|
+ */
|
|
+ if (!video->ep->enabled)
|
|
+ return -EINVAL;
|
|
+
|
|
+ ret = usb_ep_queue(video->ep, req, GFP_ATOMIC);
|
|
+ if (ret < 0)
|
|
+ printk(KERN_INFO "Failed to queue request (%d).\n", ret);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
/*
|
|
* I somehow feel that synchronisation won't be easy to achieve here. We have
|
|
* three events that control USB requests submission:
|
|
@@ -190,22 +249,26 @@
|
|
spin_unlock_irqrestore(&video->queue.irqlock, flags);
|
|
goto requeue;
|
|
}
|
|
-
|
|
+#ifdef UVC_SG_REQ
|
|
+ sg_unmark_end(&req->sg[req->num_sgs - 1]);
|
|
+#endif
|
|
video->encode(req, video, buf);
|
|
|
|
- if ((ret = usb_ep_queue(ep, req, GFP_ATOMIC)) < 0) {
|
|
- printk(KERN_INFO "Failed to queue request (%d).\n", ret);
|
|
- usb_ep_set_halt(ep);
|
|
- spin_unlock_irqrestore(&video->queue.irqlock, flags);
|
|
+ ret = uvcg_video_ep_queue(video, req);
|
|
+ spin_unlock_irqrestore(&video->queue.irqlock, flags);
|
|
+
|
|
+ if (ret < 0) {
|
|
uvcg_queue_cancel(queue, 0);
|
|
goto requeue;
|
|
}
|
|
- spin_unlock_irqrestore(&video->queue.irqlock, flags);
|
|
|
|
return;
|
|
|
|
requeue:
|
|
spin_lock_irqsave(&video->req_lock, flags);
|
|
+#ifdef UVC_SG_REQ
|
|
+ sg_unmark_end(&req->sg[req->num_sgs - 1]);
|
|
+#endif
|
|
list_add_tail(&req->list, &video->req_free);
|
|
spin_unlock_irqrestore(&video->req_lock, flags);
|
|
}
|
|
@@ -214,9 +277,22 @@
|
|
uvc_video_free_requests(struct uvc_video *video)
|
|
{
|
|
unsigned int i;
|
|
+#ifdef UVC_SG_REQ
|
|
+ unsigned int sg_idx;
|
|
+#endif
|
|
|
|
for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
|
|
if (video->req[i]) {
|
|
+#ifdef UVC_SG_REQ
|
|
+ for (sg_idx = 0; sg_idx < video->num_sgs; sg_idx++)
|
|
+ if (sg_page(&video->req[i]->sg[sg_idx]))
|
|
+ kfree(sg_virt(&video->req[i]->sg[sg_idx]));
|
|
+
|
|
+ if (video->req[i]->sg) {
|
|
+ kfree(video->req[i]->sg);
|
|
+ video->req[i]->sg = NULL;
|
|
+ }
|
|
+#endif
|
|
usb_ep_free_request(video->ep, video->req[i]);
|
|
video->req[i] = NULL;
|
|
}
|
|
@@ -238,6 +314,11 @@
|
|
unsigned int req_size;
|
|
unsigned int i;
|
|
int ret = -ENOMEM;
|
|
+#ifdef UVC_SG_REQ
|
|
+ struct scatterlist *sg;
|
|
+ unsigned int num_sgs;
|
|
+ unsigned int sg_idx;
|
|
+#endif
|
|
|
|
BUG_ON(video->req_size);
|
|
|
|
@@ -245,6 +326,35 @@
|
|
* max_t(unsigned int, video->ep->maxburst, 1)
|
|
* (video->ep->mult);
|
|
|
|
+#ifdef UVC_SG_REQ
|
|
+ num_sgs = ((video->imagesize / (req_size - 2)) + 1);
|
|
+ video->num_sgs = num_sgs;
|
|
+
|
|
+ for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
|
|
+ sg = kmalloc(num_sgs * sizeof(struct scatterlist), GFP_ATOMIC);
|
|
+ if (sg == NULL)
|
|
+ goto error;
|
|
+ sg_init_table(sg, num_sgs);
|
|
+
|
|
+ video->req[i] = usb_ep_alloc_request(video->ep, GFP_KERNEL);
|
|
+ if (video->req[i] == NULL)
|
|
+ goto error;
|
|
+
|
|
+ for (sg_idx = 0 ; sg_idx < num_sgs ; sg_idx++) {
|
|
+ video->sg_buf = kmalloc(req_size, GFP_KERNEL);
|
|
+ if (video->sg_buf == NULL)
|
|
+ goto error;
|
|
+ sg_set_buf(&sg[sg_idx], video->sg_buf, req_size);
|
|
+ }
|
|
+ video->req[i]->sg = sg;
|
|
+ video->req[i]->num_sgs = num_sgs;
|
|
+ video->req[i]->length = 0;
|
|
+ video->req[i]->complete = uvc_video_complete;
|
|
+ video->req[i]->context = video;
|
|
+
|
|
+ list_add_tail(&video->req[i]->list, &video->req_free);
|
|
+ }
|
|
+#else
|
|
for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
|
|
video->req_buffer[i] = kmalloc(req_size, GFP_KERNEL);
|
|
if (video->req_buffer[i] == NULL)
|
|
@@ -261,7 +371,7 @@
|
|
|
|
list_add_tail(&video->req[i]->list, &video->req_free);
|
|
}
|
|
-
|
|
+#endif
|
|
video->req_size = req_size;
|
|
|
|
return 0;
|
|
@@ -320,15 +430,13 @@
|
|
video->encode(req, video, buf);
|
|
|
|
/* Queue the USB request */
|
|
- ret = usb_ep_queue(video->ep, req, GFP_ATOMIC);
|
|
+ ret = uvcg_video_ep_queue(video, req);
|
|
+ spin_unlock_irqrestore(&queue->irqlock, flags);
|
|
+
|
|
if (ret < 0) {
|
|
- printk(KERN_INFO "Failed to queue request (%d)\n", ret);
|
|
- usb_ep_set_halt(video->ep);
|
|
- spin_unlock_irqrestore(&queue->irqlock, flags);
|
|
uvcg_queue_cancel(queue, 0);
|
|
break;
|
|
}
|
|
- spin_unlock_irqrestore(&queue->irqlock, flags);
|
|
}
|
|
|
|
spin_lock_irqsave(&video->req_lock, flags);
|
|
@@ -379,16 +487,22 @@
|
|
/*
|
|
* Initialize the UVC video stream.
|
|
*/
|
|
-int uvcg_video_init(struct uvc_video *video)
|
|
+int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
|
|
{
|
|
INIT_LIST_HEAD(&video->req_free);
|
|
spin_lock_init(&video->req_lock);
|
|
-
|
|
+#ifndef CONFIG_GOKE_MC
|
|
video->fcc = V4L2_PIX_FMT_YUYV;
|
|
+ video->imagesize = 320 * 240 * 2;
|
|
video->bpp = 16;
|
|
+#else
|
|
+ video->fcc = V4L2_PIX_FMT_NV21;
|
|
+ video->imagesize = 320 * 240 * 3 / 2; /* YUV420: w*h*1.5 */
|
|
+ video->bpp = 12;
|
|
+#endif
|
|
video->width = 320;
|
|
video->height = 240;
|
|
- video->imagesize = 320 * 240 * 2;
|
|
+ video->uvc = uvc;
|
|
|
|
/* Initialize the video buffers queue. */
|
|
uvcg_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT,
|