15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
200a2430fSAndrzej Pietrasiewicz /*
300a2430fSAndrzej Pietrasiewicz * uvc_video.c -- USB Video Class Gadget driver
400a2430fSAndrzej Pietrasiewicz *
500a2430fSAndrzej Pietrasiewicz * Copyright (C) 2009-2010
600a2430fSAndrzej Pietrasiewicz * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
700a2430fSAndrzej Pietrasiewicz */
800a2430fSAndrzej Pietrasiewicz
900a2430fSAndrzej Pietrasiewicz #include <linux/kernel.h>
1000a2430fSAndrzej Pietrasiewicz #include <linux/device.h>
1100a2430fSAndrzej Pietrasiewicz #include <linux/errno.h>
1200a2430fSAndrzej Pietrasiewicz #include <linux/usb/ch9.h>
1300a2430fSAndrzej Pietrasiewicz #include <linux/usb/gadget.h>
143a83c16eSAndrzej Pietrasiewicz #include <linux/usb/video.h>
15fd03af27SMichael Olbrich #include <asm/unaligned.h>
1600a2430fSAndrzej Pietrasiewicz
1700a2430fSAndrzej Pietrasiewicz #include <media/v4l2-dev.h>
1800a2430fSAndrzej Pietrasiewicz
1900a2430fSAndrzej Pietrasiewicz #include "uvc.h"
2000a2430fSAndrzej Pietrasiewicz #include "uvc_queue.h"
2170685711SLad, Prabhakar #include "uvc_video.h"
2200a2430fSAndrzej Pietrasiewicz
2300a2430fSAndrzej Pietrasiewicz /* --------------------------------------------------------------------------
2400a2430fSAndrzej Pietrasiewicz * Video codecs
2500a2430fSAndrzej Pietrasiewicz */
2600a2430fSAndrzej Pietrasiewicz
2700a2430fSAndrzej Pietrasiewicz static int
uvc_video_encode_header(struct uvc_video * video,struct uvc_buffer * buf,u8 * data,int len)2800a2430fSAndrzej Pietrasiewicz uvc_video_encode_header(struct uvc_video *video, struct uvc_buffer *buf,
2900a2430fSAndrzej Pietrasiewicz u8 *data, int len)
3000a2430fSAndrzej Pietrasiewicz {
31fd03af27SMichael Olbrich struct uvc_device *uvc = container_of(video, struct uvc_device, video);
32fd03af27SMichael Olbrich struct usb_composite_dev *cdev = uvc->func.config->cdev;
33fd03af27SMichael Olbrich struct timespec64 ts = ns_to_timespec64(buf->buf.vb2_buf.timestamp);
34fd03af27SMichael Olbrich int pos = 2;
35fd03af27SMichael Olbrich
3600a2430fSAndrzej Pietrasiewicz data[1] = UVC_STREAM_EOH | video->fid;
3700a2430fSAndrzej Pietrasiewicz
38*0d52e185SMichael Grzeschik if (video->queue.flags & UVC_QUEUE_DROP_INCOMPLETE)
39*0d52e185SMichael Grzeschik data[1] |= UVC_STREAM_ERR;
40*0d52e185SMichael Grzeschik
41fd03af27SMichael Olbrich if (video->queue.buf_used == 0 && ts.tv_sec) {
42fd03af27SMichael Olbrich /* dwClockFrequency is 48 MHz */
43fd03af27SMichael Olbrich u32 pts = ((u64)ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / NSEC_PER_USEC) * 48;
44fd03af27SMichael Olbrich
45fd03af27SMichael Olbrich data[1] |= UVC_STREAM_PTS;
46fd03af27SMichael Olbrich put_unaligned_le32(pts, &data[pos]);
47fd03af27SMichael Olbrich pos += 4;
48fd03af27SMichael Olbrich }
49fd03af27SMichael Olbrich
50fd03af27SMichael Olbrich if (cdev->gadget->ops->get_frame) {
51fd03af27SMichael Olbrich u32 sof, stc;
52fd03af27SMichael Olbrich
53fd03af27SMichael Olbrich sof = usb_gadget_frame_number(cdev->gadget);
54fd03af27SMichael Olbrich ktime_get_ts64(&ts);
55fd03af27SMichael Olbrich stc = ((u64)ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / NSEC_PER_USEC) * 48;
56fd03af27SMichael Olbrich
57fd03af27SMichael Olbrich data[1] |= UVC_STREAM_SCR;
58fd03af27SMichael Olbrich put_unaligned_le32(stc, &data[pos]);
59fd03af27SMichael Olbrich put_unaligned_le16(sof, &data[pos+4]);
60fd03af27SMichael Olbrich pos += 6;
61fd03af27SMichael Olbrich }
62fd03af27SMichael Olbrich
63fd03af27SMichael Olbrich data[0] = pos;
64fd03af27SMichael Olbrich
65fd03af27SMichael Olbrich if (buf->bytesused - video->queue.buf_used <= len - pos)
6600a2430fSAndrzej Pietrasiewicz data[1] |= UVC_STREAM_EOF;
6700a2430fSAndrzej Pietrasiewicz
68fd03af27SMichael Olbrich return pos;
6900a2430fSAndrzej Pietrasiewicz }
7000a2430fSAndrzej Pietrasiewicz
7100a2430fSAndrzej Pietrasiewicz static int
uvc_video_encode_data(struct uvc_video * video,struct uvc_buffer * buf,u8 * data,int len)7200a2430fSAndrzej Pietrasiewicz uvc_video_encode_data(struct uvc_video *video, struct uvc_buffer *buf,
7300a2430fSAndrzej Pietrasiewicz u8 *data, int len)
7400a2430fSAndrzej Pietrasiewicz {
7500a2430fSAndrzej Pietrasiewicz struct uvc_video_queue *queue = &video->queue;
7600a2430fSAndrzej Pietrasiewicz unsigned int nbytes;
7700a2430fSAndrzej Pietrasiewicz void *mem;
7800a2430fSAndrzej Pietrasiewicz
7900a2430fSAndrzej Pietrasiewicz /* Copy video data to the USB buffer. */
8000a2430fSAndrzej Pietrasiewicz mem = buf->mem + queue->buf_used;
8100a2430fSAndrzej Pietrasiewicz nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used);
8200a2430fSAndrzej Pietrasiewicz
8300a2430fSAndrzej Pietrasiewicz memcpy(data, mem, nbytes);
8400a2430fSAndrzej Pietrasiewicz queue->buf_used += nbytes;
8500a2430fSAndrzej Pietrasiewicz
8600a2430fSAndrzej Pietrasiewicz return nbytes;
8700a2430fSAndrzej Pietrasiewicz }
8800a2430fSAndrzej Pietrasiewicz
8900a2430fSAndrzej Pietrasiewicz static void
uvc_video_encode_bulk(struct usb_request * req,struct uvc_video * video,struct uvc_buffer * buf)9000a2430fSAndrzej Pietrasiewicz uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video,
9100a2430fSAndrzej Pietrasiewicz struct uvc_buffer *buf)
9200a2430fSAndrzej Pietrasiewicz {
9300a2430fSAndrzej Pietrasiewicz void *mem = req->buf;
940a0a2760SDan Vacura struct uvc_request *ureq = req->context;
9500a2430fSAndrzej Pietrasiewicz int len = video->req_size;
9600a2430fSAndrzej Pietrasiewicz int ret;
9700a2430fSAndrzej Pietrasiewicz
9800a2430fSAndrzej Pietrasiewicz /* Add a header at the beginning of the payload. */
9900a2430fSAndrzej Pietrasiewicz if (video->payload_size == 0) {
10000a2430fSAndrzej Pietrasiewicz ret = uvc_video_encode_header(video, buf, mem, len);
10100a2430fSAndrzej Pietrasiewicz video->payload_size += ret;
10200a2430fSAndrzej Pietrasiewicz mem += ret;
10300a2430fSAndrzej Pietrasiewicz len -= ret;
10400a2430fSAndrzej Pietrasiewicz }
10500a2430fSAndrzej Pietrasiewicz
10600a2430fSAndrzej Pietrasiewicz /* Process video data. */
10700a2430fSAndrzej Pietrasiewicz len = min((int)(video->max_payload_size - video->payload_size), len);
10800a2430fSAndrzej Pietrasiewicz ret = uvc_video_encode_data(video, buf, mem, len);
10900a2430fSAndrzej Pietrasiewicz
11000a2430fSAndrzej Pietrasiewicz video->payload_size += ret;
11100a2430fSAndrzej Pietrasiewicz len -= ret;
11200a2430fSAndrzej Pietrasiewicz
11300a2430fSAndrzej Pietrasiewicz req->length = video->req_size - len;
11400a2430fSAndrzej Pietrasiewicz req->zero = video->payload_size == video->max_payload_size;
11500a2430fSAndrzej Pietrasiewicz
11600a2430fSAndrzej Pietrasiewicz if (buf->bytesused == video->queue.buf_used) {
11700a2430fSAndrzej Pietrasiewicz video->queue.buf_used = 0;
11800a2430fSAndrzej Pietrasiewicz buf->state = UVC_BUF_STATE_DONE;
1199b969f93SMichael Grzeschik list_del(&buf->queue);
12000a2430fSAndrzej Pietrasiewicz video->fid ^= UVC_STREAM_FID;
1210a0a2760SDan Vacura ureq->last_buf = buf;
12200a2430fSAndrzej Pietrasiewicz
12300a2430fSAndrzej Pietrasiewicz video->payload_size = 0;
12400a2430fSAndrzej Pietrasiewicz }
12500a2430fSAndrzej Pietrasiewicz
12600a2430fSAndrzej Pietrasiewicz if (video->payload_size == video->max_payload_size ||
1270a0a2760SDan Vacura video->queue.flags & UVC_QUEUE_DROP_INCOMPLETE ||
12800a2430fSAndrzej Pietrasiewicz buf->bytesused == video->queue.buf_used)
12900a2430fSAndrzej Pietrasiewicz video->payload_size = 0;
13000a2430fSAndrzej Pietrasiewicz }
13100a2430fSAndrzej Pietrasiewicz
13200a2430fSAndrzej Pietrasiewicz static void
uvc_video_encode_isoc_sg(struct usb_request * req,struct uvc_video * video,struct uvc_buffer * buf)133e81e7f9aSMichael Grzeschik uvc_video_encode_isoc_sg(struct usb_request *req, struct uvc_video *video,
134e81e7f9aSMichael Grzeschik struct uvc_buffer *buf)
135e81e7f9aSMichael Grzeschik {
136e81e7f9aSMichael Grzeschik unsigned int pending = buf->bytesused - video->queue.buf_used;
137e81e7f9aSMichael Grzeschik struct uvc_request *ureq = req->context;
138e81e7f9aSMichael Grzeschik struct scatterlist *sg, *iter;
139e81e7f9aSMichael Grzeschik unsigned int len = video->req_size;
140e81e7f9aSMichael Grzeschik unsigned int sg_left, part = 0;
141e81e7f9aSMichael Grzeschik unsigned int i;
142f262ce66SMichael Grzeschik int header_len;
143e81e7f9aSMichael Grzeschik
144e81e7f9aSMichael Grzeschik sg = ureq->sgt.sgl;
145e81e7f9aSMichael Grzeschik sg_init_table(sg, ureq->sgt.nents);
146e81e7f9aSMichael Grzeschik
147e81e7f9aSMichael Grzeschik /* Init the header. */
148f262ce66SMichael Grzeschik header_len = uvc_video_encode_header(video, buf, ureq->header,
149e81e7f9aSMichael Grzeschik video->req_size);
150f262ce66SMichael Grzeschik sg_set_buf(sg, ureq->header, header_len);
151f262ce66SMichael Grzeschik len -= header_len;
152e81e7f9aSMichael Grzeschik
153e81e7f9aSMichael Grzeschik if (pending <= len)
154e81e7f9aSMichael Grzeschik len = pending;
155e81e7f9aSMichael Grzeschik
156e81e7f9aSMichael Grzeschik req->length = (len == pending) ?
157f262ce66SMichael Grzeschik len + header_len : video->req_size;
158e81e7f9aSMichael Grzeschik
159e81e7f9aSMichael Grzeschik /* Init the pending sgs with payload */
160e81e7f9aSMichael Grzeschik sg = sg_next(sg);
161e81e7f9aSMichael Grzeschik
162e81e7f9aSMichael Grzeschik for_each_sg(sg, iter, ureq->sgt.nents - 1, i) {
163b57b08e6SJeff Vanhoof if (!len || !buf->sg || !buf->sg->length)
164e81e7f9aSMichael Grzeschik break;
165e81e7f9aSMichael Grzeschik
166b57b08e6SJeff Vanhoof sg_left = buf->sg->length - buf->offset;
167e81e7f9aSMichael Grzeschik part = min_t(unsigned int, len, sg_left);
168e81e7f9aSMichael Grzeschik
169e81e7f9aSMichael Grzeschik sg_set_page(iter, sg_page(buf->sg), part, buf->offset);
170e81e7f9aSMichael Grzeschik
171e81e7f9aSMichael Grzeschik if (part == sg_left) {
172e81e7f9aSMichael Grzeschik buf->offset = 0;
173e81e7f9aSMichael Grzeschik buf->sg = sg_next(buf->sg);
174e81e7f9aSMichael Grzeschik } else {
175e81e7f9aSMichael Grzeschik buf->offset += part;
176e81e7f9aSMichael Grzeschik }
177e81e7f9aSMichael Grzeschik len -= part;
178e81e7f9aSMichael Grzeschik }
179e81e7f9aSMichael Grzeschik
180e81e7f9aSMichael Grzeschik /* Assign the video data with header. */
181e81e7f9aSMichael Grzeschik req->buf = NULL;
182e81e7f9aSMichael Grzeschik req->sg = ureq->sgt.sgl;
183e81e7f9aSMichael Grzeschik req->num_sgs = i + 1;
184e81e7f9aSMichael Grzeschik
185e81e7f9aSMichael Grzeschik req->length -= len;
186f262ce66SMichael Grzeschik video->queue.buf_used += req->length - header_len;
187e81e7f9aSMichael Grzeschik
1880a0a2760SDan Vacura if (buf->bytesused == video->queue.buf_used || !buf->sg ||
1890a0a2760SDan Vacura video->queue.flags & UVC_QUEUE_DROP_INCOMPLETE) {
190e81e7f9aSMichael Grzeschik video->queue.buf_used = 0;
191e81e7f9aSMichael Grzeschik buf->state = UVC_BUF_STATE_DONE;
192e81e7f9aSMichael Grzeschik buf->offset = 0;
1939b969f93SMichael Grzeschik list_del(&buf->queue);
194e81e7f9aSMichael Grzeschik video->fid ^= UVC_STREAM_FID;
1959b969f93SMichael Grzeschik ureq->last_buf = buf;
196e81e7f9aSMichael Grzeschik }
197e81e7f9aSMichael Grzeschik }
198e81e7f9aSMichael Grzeschik
199e81e7f9aSMichael Grzeschik static void
uvc_video_encode_isoc(struct usb_request * req,struct uvc_video * video,struct uvc_buffer * buf)20000a2430fSAndrzej Pietrasiewicz uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
20100a2430fSAndrzej Pietrasiewicz struct uvc_buffer *buf)
20200a2430fSAndrzej Pietrasiewicz {
20300a2430fSAndrzej Pietrasiewicz void *mem = req->buf;
2040a0a2760SDan Vacura struct uvc_request *ureq = req->context;
20500a2430fSAndrzej Pietrasiewicz int len = video->req_size;
20600a2430fSAndrzej Pietrasiewicz int ret;
20700a2430fSAndrzej Pietrasiewicz
20800a2430fSAndrzej Pietrasiewicz /* Add the header. */
20900a2430fSAndrzej Pietrasiewicz ret = uvc_video_encode_header(video, buf, mem, len);
21000a2430fSAndrzej Pietrasiewicz mem += ret;
21100a2430fSAndrzej Pietrasiewicz len -= ret;
21200a2430fSAndrzej Pietrasiewicz
21300a2430fSAndrzej Pietrasiewicz /* Process video data. */
21400a2430fSAndrzej Pietrasiewicz ret = uvc_video_encode_data(video, buf, mem, len);
21500a2430fSAndrzej Pietrasiewicz len -= ret;
21600a2430fSAndrzej Pietrasiewicz
21700a2430fSAndrzej Pietrasiewicz req->length = video->req_size - len;
21800a2430fSAndrzej Pietrasiewicz
2190a0a2760SDan Vacura if (buf->bytesused == video->queue.buf_used ||
2200a0a2760SDan Vacura video->queue.flags & UVC_QUEUE_DROP_INCOMPLETE) {
22100a2430fSAndrzej Pietrasiewicz video->queue.buf_used = 0;
22200a2430fSAndrzej Pietrasiewicz buf->state = UVC_BUF_STATE_DONE;
2239b969f93SMichael Grzeschik list_del(&buf->queue);
22400a2430fSAndrzej Pietrasiewicz video->fid ^= UVC_STREAM_FID;
2250a0a2760SDan Vacura ureq->last_buf = buf;
22600a2430fSAndrzej Pietrasiewicz }
22700a2430fSAndrzej Pietrasiewicz }
22800a2430fSAndrzej Pietrasiewicz
22900a2430fSAndrzej Pietrasiewicz /* --------------------------------------------------------------------------
23000a2430fSAndrzej Pietrasiewicz * Request handling
23100a2430fSAndrzej Pietrasiewicz */
23200a2430fSAndrzej Pietrasiewicz
uvcg_video_ep_queue(struct uvc_video * video,struct usb_request * req)2339d1ff5dcSLaurent Pinchart static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req)
2349d1ff5dcSLaurent Pinchart {
2359d1ff5dcSLaurent Pinchart int ret;
2369d1ff5dcSLaurent Pinchart
2379d1ff5dcSLaurent Pinchart ret = usb_ep_queue(video->ep, req, GFP_ATOMIC);
2389d1ff5dcSLaurent Pinchart if (ret < 0) {
239dc0f755bSLaurent Pinchart uvcg_err(&video->uvc->func, "Failed to queue request (%d).\n",
240dc0f755bSLaurent Pinchart ret);
241dc0f755bSLaurent Pinchart
24238db3716SMichael Grzeschik /* If the endpoint is disabled the descriptor may be NULL. */
24338db3716SMichael Grzeschik if (video->ep->desc) {
2448dbf9c7aSLaurent Pinchart /* Isochronous endpoints can't be halted. */
2458dbf9c7aSLaurent Pinchart if (usb_endpoint_xfer_bulk(video->ep->desc))
2469d1ff5dcSLaurent Pinchart usb_ep_set_halt(video->ep);
2479d1ff5dcSLaurent Pinchart }
24838db3716SMichael Grzeschik }
2499d1ff5dcSLaurent Pinchart
2509d1ff5dcSLaurent Pinchart return ret;
2519d1ff5dcSLaurent Pinchart }
2529d1ff5dcSLaurent Pinchart
25300a2430fSAndrzej Pietrasiewicz static void
uvc_video_complete(struct usb_ep * ep,struct usb_request * req)25400a2430fSAndrzej Pietrasiewicz uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
25500a2430fSAndrzej Pietrasiewicz {
2569973772dSMichael Grzeschik struct uvc_request *ureq = req->context;
2579973772dSMichael Grzeschik struct uvc_video *video = ureq->video;
25800a2430fSAndrzej Pietrasiewicz struct uvc_video_queue *queue = &video->queue;
2595fc49d8bSMichael Grzeschik struct uvc_device *uvc = video->uvc;
26000a2430fSAndrzej Pietrasiewicz unsigned long flags;
26100a2430fSAndrzej Pietrasiewicz
26200a2430fSAndrzej Pietrasiewicz switch (req->status) {
26300a2430fSAndrzej Pietrasiewicz case 0:
26400a2430fSAndrzej Pietrasiewicz break;
26500a2430fSAndrzej Pietrasiewicz
2660a0a2760SDan Vacura case -EXDEV:
2670a0a2760SDan Vacura uvcg_dbg(&video->uvc->func, "VS request missed xfer.\n");
2680a0a2760SDan Vacura queue->flags |= UVC_QUEUE_DROP_INCOMPLETE;
2690a0a2760SDan Vacura break;
2700a0a2760SDan Vacura
27100a2430fSAndrzej Pietrasiewicz case -ESHUTDOWN: /* disconnect from host. */
272dc0f755bSLaurent Pinchart uvcg_dbg(&video->uvc->func, "VS request cancelled.\n");
2737ea95b11SAndrzej Pietrasiewicz uvcg_queue_cancel(queue, 1);
27443cd0023SMichael Grzeschik break;
27500a2430fSAndrzej Pietrasiewicz
27600a2430fSAndrzej Pietrasiewicz default:
277a725d0f6SMichael Grzeschik uvcg_warn(&video->uvc->func,
278dc0f755bSLaurent Pinchart "VS request completed with status %d.\n",
27900a2430fSAndrzej Pietrasiewicz req->status);
2807ea95b11SAndrzej Pietrasiewicz uvcg_queue_cancel(queue, 0);
28100a2430fSAndrzej Pietrasiewicz }
28200a2430fSAndrzej Pietrasiewicz
2839b969f93SMichael Grzeschik if (ureq->last_buf) {
2849b969f93SMichael Grzeschik uvcg_complete_buffer(&video->queue, ureq->last_buf);
2859b969f93SMichael Grzeschik ureq->last_buf = NULL;
2869b969f93SMichael Grzeschik }
2879b969f93SMichael Grzeschik
28800a2430fSAndrzej Pietrasiewicz spin_lock_irqsave(&video->req_lock, flags);
28900a2430fSAndrzej Pietrasiewicz list_add_tail(&req->list, &video->req_free);
29000a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&video->req_lock, flags);
29143cd0023SMichael Grzeschik
2925fc49d8bSMichael Grzeschik if (uvc->state == UVC_STATE_STREAMING)
2939b91a652SMichael Grzeschik queue_work(video->async_wq, &video->pump);
29400a2430fSAndrzej Pietrasiewicz }
29500a2430fSAndrzej Pietrasiewicz
29600a2430fSAndrzej Pietrasiewicz static int
uvc_video_free_requests(struct uvc_video * video)29700a2430fSAndrzej Pietrasiewicz uvc_video_free_requests(struct uvc_video *video)
29800a2430fSAndrzej Pietrasiewicz {
29900a2430fSAndrzej Pietrasiewicz unsigned int i;
30000a2430fSAndrzej Pietrasiewicz
3019973772dSMichael Grzeschik if (video->ureq) {
3029973772dSMichael Grzeschik for (i = 0; i < video->uvc_num_requests; ++i) {
303e81e7f9aSMichael Grzeschik sg_free_table(&video->ureq[i].sgt);
304e81e7f9aSMichael Grzeschik
3059973772dSMichael Grzeschik if (video->ureq[i].req) {
3069973772dSMichael Grzeschik usb_ep_free_request(video->ep, video->ureq[i].req);
3079973772dSMichael Grzeschik video->ureq[i].req = NULL;
30800a2430fSAndrzej Pietrasiewicz }
30900a2430fSAndrzej Pietrasiewicz
3109973772dSMichael Grzeschik if (video->ureq[i].req_buffer) {
3119973772dSMichael Grzeschik kfree(video->ureq[i].req_buffer);
3129973772dSMichael Grzeschik video->ureq[i].req_buffer = NULL;
31300a2430fSAndrzej Pietrasiewicz }
31400a2430fSAndrzej Pietrasiewicz }
31500a2430fSAndrzej Pietrasiewicz
3169973772dSMichael Grzeschik kfree(video->ureq);
3179973772dSMichael Grzeschik video->ureq = NULL;
3189973772dSMichael Grzeschik }
3199973772dSMichael Grzeschik
32000a2430fSAndrzej Pietrasiewicz INIT_LIST_HEAD(&video->req_free);
32100a2430fSAndrzej Pietrasiewicz video->req_size = 0;
32200a2430fSAndrzej Pietrasiewicz return 0;
32300a2430fSAndrzej Pietrasiewicz }
32400a2430fSAndrzej Pietrasiewicz
32500a2430fSAndrzej Pietrasiewicz static int
uvc_video_alloc_requests(struct uvc_video * video)32600a2430fSAndrzej Pietrasiewicz uvc_video_alloc_requests(struct uvc_video *video)
32700a2430fSAndrzej Pietrasiewicz {
32800a2430fSAndrzej Pietrasiewicz unsigned int req_size;
32900a2430fSAndrzej Pietrasiewicz unsigned int i;
33000a2430fSAndrzej Pietrasiewicz int ret = -ENOMEM;
33100a2430fSAndrzej Pietrasiewicz
33200a2430fSAndrzej Pietrasiewicz BUG_ON(video->req_size);
33300a2430fSAndrzej Pietrasiewicz
33400a2430fSAndrzej Pietrasiewicz req_size = video->ep->maxpacket
33500a2430fSAndrzej Pietrasiewicz * max_t(unsigned int, video->ep->maxburst, 1)
336eaa496ffSFelipe Balbi * (video->ep->mult);
33700a2430fSAndrzej Pietrasiewicz
3389973772dSMichael Grzeschik video->ureq = kcalloc(video->uvc_num_requests, sizeof(struct uvc_request), GFP_KERNEL);
3399973772dSMichael Grzeschik if (video->ureq == NULL)
3409973772dSMichael Grzeschik return -ENOMEM;
3419973772dSMichael Grzeschik
3429973772dSMichael Grzeschik for (i = 0; i < video->uvc_num_requests; ++i) {
3439973772dSMichael Grzeschik video->ureq[i].req_buffer = kmalloc(req_size, GFP_KERNEL);
3449973772dSMichael Grzeschik if (video->ureq[i].req_buffer == NULL)
34500a2430fSAndrzej Pietrasiewicz goto error;
34600a2430fSAndrzej Pietrasiewicz
3479973772dSMichael Grzeschik video->ureq[i].req = usb_ep_alloc_request(video->ep, GFP_KERNEL);
3489973772dSMichael Grzeschik if (video->ureq[i].req == NULL)
34900a2430fSAndrzej Pietrasiewicz goto error;
35000a2430fSAndrzej Pietrasiewicz
3519973772dSMichael Grzeschik video->ureq[i].req->buf = video->ureq[i].req_buffer;
3529973772dSMichael Grzeschik video->ureq[i].req->length = 0;
3539973772dSMichael Grzeschik video->ureq[i].req->complete = uvc_video_complete;
3549973772dSMichael Grzeschik video->ureq[i].req->context = &video->ureq[i];
3559973772dSMichael Grzeschik video->ureq[i].video = video;
3569b969f93SMichael Grzeschik video->ureq[i].last_buf = NULL;
35700a2430fSAndrzej Pietrasiewicz
3589973772dSMichael Grzeschik list_add_tail(&video->ureq[i].req->list, &video->req_free);
359e81e7f9aSMichael Grzeschik /* req_size/PAGE_SIZE + 1 for overruns and + 1 for header */
360e81e7f9aSMichael Grzeschik sg_alloc_table(&video->ureq[i].sgt,
361859c675dSMichael Grzeschik DIV_ROUND_UP(req_size - UVCG_REQUEST_HEADER_LEN,
362859c675dSMichael Grzeschik PAGE_SIZE) + 2, GFP_KERNEL);
36300a2430fSAndrzej Pietrasiewicz }
36400a2430fSAndrzej Pietrasiewicz
36500a2430fSAndrzej Pietrasiewicz video->req_size = req_size;
36600a2430fSAndrzej Pietrasiewicz
36700a2430fSAndrzej Pietrasiewicz return 0;
36800a2430fSAndrzej Pietrasiewicz
36900a2430fSAndrzej Pietrasiewicz error:
37000a2430fSAndrzej Pietrasiewicz uvc_video_free_requests(video);
37100a2430fSAndrzej Pietrasiewicz return ret;
37200a2430fSAndrzej Pietrasiewicz }
37300a2430fSAndrzej Pietrasiewicz
37400a2430fSAndrzej Pietrasiewicz /* --------------------------------------------------------------------------
37500a2430fSAndrzej Pietrasiewicz * Video streaming
37600a2430fSAndrzej Pietrasiewicz */
37700a2430fSAndrzej Pietrasiewicz
37800a2430fSAndrzej Pietrasiewicz /*
3797ea95b11SAndrzej Pietrasiewicz * uvcg_video_pump - Pump video data into the USB requests
38000a2430fSAndrzej Pietrasiewicz *
38100a2430fSAndrzej Pietrasiewicz * This function fills the available USB requests (listed in req_free) with
38200a2430fSAndrzej Pietrasiewicz * video data from the queued buffers.
38300a2430fSAndrzej Pietrasiewicz */
uvcg_video_pump(struct work_struct * work)38443cd0023SMichael Grzeschik static void uvcg_video_pump(struct work_struct *work)
38500a2430fSAndrzej Pietrasiewicz {
38643cd0023SMichael Grzeschik struct uvc_video *video = container_of(work, struct uvc_video, pump);
387bd52b813SMichael Grzeschik struct uvc_video_queue *queue = &video->queue;
3885ae8a354SAvichal Rakesh /* video->max_payload_size is only set when using bulk transfer */
3895ae8a354SAvichal Rakesh bool is_bulk = video->max_payload_size;
390f9897ec0SMichael Grzeschik struct usb_request *req = NULL;
39100a2430fSAndrzej Pietrasiewicz struct uvc_buffer *buf;
39200a2430fSAndrzej Pietrasiewicz unsigned long flags;
3935ae8a354SAvichal Rakesh bool buf_done;
39400a2430fSAndrzej Pietrasiewicz int ret;
39500a2430fSAndrzej Pietrasiewicz
396f9897ec0SMichael Grzeschik while (video->ep->enabled) {
397c5d337a3SLaurent Pinchart /*
398c5d337a3SLaurent Pinchart * Retrieve the first available USB request, protected by the
39900a2430fSAndrzej Pietrasiewicz * request lock.
40000a2430fSAndrzej Pietrasiewicz */
40100a2430fSAndrzej Pietrasiewicz spin_lock_irqsave(&video->req_lock, flags);
40200a2430fSAndrzej Pietrasiewicz if (list_empty(&video->req_free)) {
40300a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&video->req_lock, flags);
40443cd0023SMichael Grzeschik return;
40500a2430fSAndrzej Pietrasiewicz }
40600a2430fSAndrzej Pietrasiewicz req = list_first_entry(&video->req_free, struct usb_request,
40700a2430fSAndrzej Pietrasiewicz list);
40800a2430fSAndrzej Pietrasiewicz list_del(&req->list);
40900a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&video->req_lock, flags);
41000a2430fSAndrzej Pietrasiewicz
411c5d337a3SLaurent Pinchart /*
412c5d337a3SLaurent Pinchart * Retrieve the first available video buffer and fill the
41300a2430fSAndrzej Pietrasiewicz * request, protected by the video queue irqlock.
41400a2430fSAndrzej Pietrasiewicz */
4156dd5b021SLaurent Pinchart spin_lock_irqsave(&queue->irqlock, flags);
4166dd5b021SLaurent Pinchart buf = uvcg_queue_head(queue);
417c3ff12a9SAvichal Rakesh
418c3ff12a9SAvichal Rakesh if (buf != NULL) {
419c3ff12a9SAvichal Rakesh video->encode(req, video, buf);
4205ae8a354SAvichal Rakesh buf_done = buf->state == UVC_BUF_STATE_DONE;
421c3ff12a9SAvichal Rakesh } else if (!(queue->flags & UVC_QUEUE_DISCONNECTED) && !is_bulk) {
422c3ff12a9SAvichal Rakesh /*
423c3ff12a9SAvichal Rakesh * No video buffer available; the queue is still connected and
4245ae8a354SAvichal Rakesh * we're transferring over ISOC. Queue a 0 length request to
425c3ff12a9SAvichal Rakesh * prevent missed ISOC transfers.
426c3ff12a9SAvichal Rakesh */
427c3ff12a9SAvichal Rakesh req->length = 0;
4285ae8a354SAvichal Rakesh buf_done = false;
429c3ff12a9SAvichal Rakesh } else {
430c3ff12a9SAvichal Rakesh /*
4315ae8a354SAvichal Rakesh * Either the queue has been disconnected or no video buffer
4325ae8a354SAvichal Rakesh * available for bulk transfer. Either way, stop processing
433c3ff12a9SAvichal Rakesh * further.
434c3ff12a9SAvichal Rakesh */
4356dd5b021SLaurent Pinchart spin_unlock_irqrestore(&queue->irqlock, flags);
43600a2430fSAndrzej Pietrasiewicz break;
43700a2430fSAndrzej Pietrasiewicz }
43800a2430fSAndrzej Pietrasiewicz
439c5d337a3SLaurent Pinchart /*
4405ae8a354SAvichal Rakesh * With USB3 handling more requests at a higher speed, we can't
4415ae8a354SAvichal Rakesh * afford to generate an interrupt for every request. Decide to
4425ae8a354SAvichal Rakesh * interrupt:
4435ae8a354SAvichal Rakesh *
4445ae8a354SAvichal Rakesh * - When no more requests are available in the free queue, as
4455ae8a354SAvichal Rakesh * this may be our last chance to refill the endpoint's
4465ae8a354SAvichal Rakesh * request queue.
4475ae8a354SAvichal Rakesh *
4485ae8a354SAvichal Rakesh * - When this is request is the last request for the video
4495ae8a354SAvichal Rakesh * buffer, as we want to start sending the next video buffer
4505ae8a354SAvichal Rakesh * ASAP in case it doesn't get started already in the next
4515ae8a354SAvichal Rakesh * iteration of this loop.
4525ae8a354SAvichal Rakesh *
4535ae8a354SAvichal Rakesh * - Four times over the length of the requests queue (as
4545ae8a354SAvichal Rakesh * indicated by video->uvc_num_requests), as a trade-off
4555ae8a354SAvichal Rakesh * between latency and interrupt load.
456c5d337a3SLaurent Pinchart */
4575ae8a354SAvichal Rakesh if (list_empty(&video->req_free) || buf_done ||
458fc78941dSMichael Grzeschik !(video->req_int_count %
459fc78941dSMichael Grzeschik DIV_ROUND_UP(video->uvc_num_requests, 4))) {
460fc78941dSMichael Grzeschik video->req_int_count = 0;
461fc78941dSMichael Grzeschik req->no_interrupt = 0;
462fc78941dSMichael Grzeschik } else {
463fc78941dSMichael Grzeschik req->no_interrupt = 1;
464fc78941dSMichael Grzeschik }
465fc78941dSMichael Grzeschik
46600a2430fSAndrzej Pietrasiewicz /* Queue the USB request */
4679d1ff5dcSLaurent Pinchart ret = uvcg_video_ep_queue(video, req);
4686dd5b021SLaurent Pinchart spin_unlock_irqrestore(&queue->irqlock, flags);
4699d1ff5dcSLaurent Pinchart
4709d1ff5dcSLaurent Pinchart if (ret < 0) {
4717ea95b11SAndrzej Pietrasiewicz uvcg_queue_cancel(queue, 0);
47200a2430fSAndrzej Pietrasiewicz break;
47300a2430fSAndrzej Pietrasiewicz }
47496163f83SDan Vacura
47596163f83SDan Vacura /* Endpoint now owns the request */
47696163f83SDan Vacura req = NULL;
477fc78941dSMichael Grzeschik video->req_int_count++;
47800a2430fSAndrzej Pietrasiewicz }
47900a2430fSAndrzej Pietrasiewicz
480f9897ec0SMichael Grzeschik if (!req)
481f9897ec0SMichael Grzeschik return;
482f9897ec0SMichael Grzeschik
48300a2430fSAndrzej Pietrasiewicz spin_lock_irqsave(&video->req_lock, flags);
48400a2430fSAndrzej Pietrasiewicz list_add_tail(&req->list, &video->req_free);
48500a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&video->req_lock, flags);
48643cd0023SMichael Grzeschik return;
48700a2430fSAndrzej Pietrasiewicz }
48800a2430fSAndrzej Pietrasiewicz
48900a2430fSAndrzej Pietrasiewicz /*
49000a2430fSAndrzej Pietrasiewicz * Enable or disable the video stream.
49100a2430fSAndrzej Pietrasiewicz */
uvcg_video_enable(struct uvc_video * video,int enable)4923a83c16eSAndrzej Pietrasiewicz int uvcg_video_enable(struct uvc_video *video, int enable)
49300a2430fSAndrzej Pietrasiewicz {
49400a2430fSAndrzej Pietrasiewicz unsigned int i;
49500a2430fSAndrzej Pietrasiewicz int ret;
49600a2430fSAndrzej Pietrasiewicz
49700a2430fSAndrzej Pietrasiewicz if (video->ep == NULL) {
498dc0f755bSLaurent Pinchart uvcg_info(&video->uvc->func,
499dc0f755bSLaurent Pinchart "Video enable failed, device is uninitialized.\n");
50000a2430fSAndrzej Pietrasiewicz return -ENODEV;
50100a2430fSAndrzej Pietrasiewicz }
50200a2430fSAndrzej Pietrasiewicz
50300a2430fSAndrzej Pietrasiewicz if (!enable) {
50443cd0023SMichael Grzeschik cancel_work_sync(&video->pump);
50543cd0023SMichael Grzeschik uvcg_queue_cancel(&video->queue, 0);
50643cd0023SMichael Grzeschik
5079973772dSMichael Grzeschik for (i = 0; i < video->uvc_num_requests; ++i)
5089973772dSMichael Grzeschik if (video->ureq && video->ureq[i].req)
5099973772dSMichael Grzeschik usb_ep_dequeue(video->ep, video->ureq[i].req);
51000a2430fSAndrzej Pietrasiewicz
51100a2430fSAndrzej Pietrasiewicz uvc_video_free_requests(video);
5127ea95b11SAndrzej Pietrasiewicz uvcg_queue_enable(&video->queue, 0);
51300a2430fSAndrzej Pietrasiewicz return 0;
51400a2430fSAndrzej Pietrasiewicz }
51500a2430fSAndrzej Pietrasiewicz
5167ea95b11SAndrzej Pietrasiewicz if ((ret = uvcg_queue_enable(&video->queue, 1)) < 0)
51700a2430fSAndrzej Pietrasiewicz return ret;
51800a2430fSAndrzej Pietrasiewicz
51900a2430fSAndrzej Pietrasiewicz if ((ret = uvc_video_alloc_requests(video)) < 0)
52000a2430fSAndrzej Pietrasiewicz return ret;
52100a2430fSAndrzej Pietrasiewicz
52200a2430fSAndrzej Pietrasiewicz if (video->max_payload_size) {
52300a2430fSAndrzej Pietrasiewicz video->encode = uvc_video_encode_bulk;
52400a2430fSAndrzej Pietrasiewicz video->payload_size = 0;
52588c8e05eSGreg Kroah-Hartman } else
52688c8e05eSGreg Kroah-Hartman video->encode = video->queue.use_sg ?
527e81e7f9aSMichael Grzeschik uvc_video_encode_isoc_sg : uvc_video_encode_isoc;
52800a2430fSAndrzej Pietrasiewicz
529fc78941dSMichael Grzeschik video->req_int_count = 0;
530fc78941dSMichael Grzeschik
5319b91a652SMichael Grzeschik queue_work(video->async_wq, &video->pump);
53243cd0023SMichael Grzeschik
53343cd0023SMichael Grzeschik return ret;
53400a2430fSAndrzej Pietrasiewicz }
53500a2430fSAndrzej Pietrasiewicz
53600a2430fSAndrzej Pietrasiewicz /*
53700a2430fSAndrzej Pietrasiewicz * Initialize the UVC video stream.
53800a2430fSAndrzej Pietrasiewicz */
uvcg_video_init(struct uvc_video * video,struct uvc_device * uvc)539dc0f755bSLaurent Pinchart int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
54000a2430fSAndrzej Pietrasiewicz {
54100a2430fSAndrzej Pietrasiewicz INIT_LIST_HEAD(&video->req_free);
54200a2430fSAndrzej Pietrasiewicz spin_lock_init(&video->req_lock);
54343cd0023SMichael Grzeschik INIT_WORK(&video->pump, uvcg_video_pump);
54400a2430fSAndrzej Pietrasiewicz
5459b91a652SMichael Grzeschik /* Allocate a work queue for asynchronous video pump handler. */
5469b91a652SMichael Grzeschik video->async_wq = alloc_workqueue("uvcgadget", WQ_UNBOUND | WQ_HIGHPRI, 0);
5479b91a652SMichael Grzeschik if (!video->async_wq)
5489b91a652SMichael Grzeschik return -EINVAL;
5499b91a652SMichael Grzeschik
550dc0f755bSLaurent Pinchart video->uvc = uvc;
55100a2430fSAndrzej Pietrasiewicz video->fcc = V4L2_PIX_FMT_YUYV;
55200a2430fSAndrzej Pietrasiewicz video->bpp = 16;
55300a2430fSAndrzej Pietrasiewicz video->width = 320;
55400a2430fSAndrzej Pietrasiewicz video->height = 240;
55500a2430fSAndrzej Pietrasiewicz video->imagesize = 320 * 240 * 2;
55600a2430fSAndrzej Pietrasiewicz
55700a2430fSAndrzej Pietrasiewicz /* Initialize the video buffers queue. */
558e81e7f9aSMichael Grzeschik uvcg_queue_init(&video->queue, uvc->v4l2_dev.dev->parent,
559e81e7f9aSMichael Grzeschik V4L2_BUF_TYPE_VIDEO_OUTPUT, &video->mutex);
56000a2430fSAndrzej Pietrasiewicz return 0;
56100a2430fSAndrzej Pietrasiewicz }
562