summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget/function/uvc_queue.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget/function/uvc_queue.c')
-rw-r--r--drivers/usb/gadget/function/uvc_queue.c85
1 files changed, 45 insertions, 40 deletions
diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c
index 61e2c94cc0b0..9a1bbd79ff5a 100644
--- a/drivers/usb/gadget/function/uvc_queue.c
+++ b/drivers/usb/gadget/function/uvc_queue.c
@@ -17,9 +17,11 @@
#include <linux/wait.h>
#include <media/v4l2-common.h>
+#include <media/videobuf2-dma-sg.h>
#include <media/videobuf2-vmalloc.h>
#include "uvc.h"
+#include "uvc_video.h"
/* ------------------------------------------------------------------------
* Video buffers queue management.
@@ -46,6 +48,8 @@ static int uvc_queue_setup(struct vb2_queue *vq,
if (*nbuffers > UVC_MAX_VIDEO_BUFFERS)
*nbuffers = UVC_MAX_VIDEO_BUFFERS;
+ if (*nbuffers < UVCG_STREAMING_MIN_BUFFERS)
+ *nbuffers = UVCG_STREAMING_MIN_BUFFERS;
*nplanes = 1;
@@ -57,6 +61,7 @@ static int uvc_queue_setup(struct vb2_queue *vq,
static int uvc_buffer_prepare(struct vb2_buffer *vb)
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
+ struct uvc_video *video = container_of(queue, struct uvc_video, queue);
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct uvc_buffer *buf = container_of(vbuf, struct uvc_buffer, buf);
@@ -70,12 +75,22 @@ static int uvc_buffer_prepare(struct vb2_buffer *vb)
return -ENODEV;
buf->state = UVC_BUF_STATE_QUEUED;
- buf->mem = vb2_plane_vaddr(vb, 0);
+ if (queue->use_sg) {
+ buf->sgt = vb2_dma_sg_plane_desc(vb, 0);
+ buf->sg = buf->sgt->sgl;
+ } else {
+ buf->mem = vb2_plane_vaddr(vb, 0);
+ }
buf->length = vb2_plane_size(vb, 0);
- if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
buf->bytesused = 0;
- else
+ } else {
buf->bytesused = vb2_get_plane_payload(vb, 0);
+ buf->req_payload_size =
+ DIV_ROUND_UP(buf->bytesused +
+ (video->reqs_per_frame * UVCG_REQUEST_HEADER_LEN),
+ video->reqs_per_frame);
+ }
return 0;
}
@@ -92,7 +107,8 @@ static void uvc_buffer_queue(struct vb2_buffer *vb)
if (likely(!(queue->flags & UVC_QUEUE_DISCONNECTED))) {
list_add_tail(&buf->queue, &queue->irqqueue);
} else {
- /* If the device is disconnected return the buffer to userspace
+ /*
+ * If the device is disconnected return the buffer to userspace
* directly. The next QBUF call will fail with -ENODEV.
*/
buf->state = UVC_BUF_STATE_ERROR;
@@ -106,13 +122,13 @@ static const struct vb2_ops uvc_queue_qops = {
.queue_setup = uvc_queue_setup,
.buf_prepare = uvc_buffer_prepare,
.buf_queue = uvc_buffer_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
-int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
+int uvcg_queue_init(struct uvc_video_queue *queue, struct device *dev, enum v4l2_buf_type type,
struct mutex *lock)
{
+ struct uvc_video *video = container_of(queue, struct uvc_video, queue);
+ struct usb_composite_dev *cdev = video->uvc->func.config->cdev;
int ret;
queue->queue.type = type;
@@ -121,9 +137,17 @@ int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
queue->queue.buf_struct_size = sizeof(struct uvc_buffer);
queue->queue.ops = &uvc_queue_qops;
queue->queue.lock = lock;
- queue->queue.mem_ops = &vb2_vmalloc_memops;
- queue->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
+ if (cdev->gadget->sg_supported) {
+ queue->queue.mem_ops = &vb2_dma_sg_memops;
+ queue->use_sg = 1;
+ } else {
+ queue->queue.mem_ops = &vb2_vmalloc_memops;
+ }
+
+ queue->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY
| V4L2_BUF_FLAG_TSTAMP_SRC_EOF;
+ queue->queue.dev = dev;
+
ret = vb2_queue_init(&queue->queue);
if (ret)
return ret;
@@ -163,18 +187,7 @@ int uvcg_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
int uvcg_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
{
- unsigned long flags;
- int ret;
-
- ret = vb2_qbuf(&queue->queue, NULL, buf);
- if (ret < 0)
- return ret;
-
- spin_lock_irqsave(&queue->irqlock, flags);
- ret = (queue->flags & UVC_QUEUE_PAUSED) != 0;
- queue->flags &= ~UVC_QUEUE_PAUSED;
- spin_unlock_irqrestore(&queue->irqlock, flags);
- return ret;
+ return vb2_qbuf(&queue->queue, NULL, buf);
}
/*
@@ -242,7 +255,10 @@ void uvcg_queue_cancel(struct uvc_video_queue *queue, int disconnect)
buf->state = UVC_BUF_STATE_ERROR;
vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_ERROR);
}
- /* This must be protected by the irqlock spinlock to avoid race
+ queue->buf_used = 0;
+
+ /*
+ * This must be protected by the irqlock spinlock to avoid race
* conditions between uvc_queue_buffer and the disconnection event that
* could result in an interruptible wait in uvc_dequeue_buffer. Do not
* blindly replace this logic by checking for the UVC_DEV_DISCONNECTED
@@ -282,6 +298,7 @@ int uvcg_queue_enable(struct uvc_video_queue *queue, int enable)
queue->sequence = 0;
queue->buf_used = 0;
+ queue->flags &= ~UVC_QUEUE_DROP_INCOMPLETE;
} else {
ret = vb2_streamoff(&queue->queue, queue->queue.type);
if (ret < 0)
@@ -304,33 +321,23 @@ int uvcg_queue_enable(struct uvc_video_queue *queue, int enable)
}
/* called with &queue_irqlock held.. */
-struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue,
+void uvcg_complete_buffer(struct uvc_video_queue *queue,
struct uvc_buffer *buf)
{
- struct uvc_buffer *nextbuf;
-
- if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) &&
- buf->length != buf->bytesused) {
- buf->state = UVC_BUF_STATE_QUEUED;
+ if (queue->flags & UVC_QUEUE_DROP_INCOMPLETE) {
+ queue->flags &= ~UVC_QUEUE_DROP_INCOMPLETE;
+ buf->state = UVC_BUF_STATE_ERROR;
vb2_set_plane_payload(&buf->buf.vb2_buf, 0, 0);
- return buf;
+ vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_ERROR);
+ return;
}
- list_del(&buf->queue);
- if (!list_empty(&queue->irqqueue))
- nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
- queue);
- else
- nextbuf = NULL;
-
buf->buf.field = V4L2_FIELD_NONE;
buf->buf.sequence = queue->sequence++;
buf->buf.vb2_buf.timestamp = ktime_get_ns();
vb2_set_plane_payload(&buf->buf.vb2_buf, 0, buf->bytesused);
vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE);
-
- return nextbuf;
}
struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue)
@@ -340,8 +347,6 @@ struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue)
if (!list_empty(&queue->irqqueue))
buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
queue);
- else
- queue->flags |= UVC_QUEUE_PAUSED;
return buf;
}