162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	uvc_video.c  --  USB Video Class Gadget driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *	Copyright (C) 2009-2010
662306a36Sopenharmony_ci *	    Laurent Pinchart (laurent.pinchart@ideasonboard.com)
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/device.h>
1162306a36Sopenharmony_ci#include <linux/errno.h>
1262306a36Sopenharmony_ci#include <linux/usb/ch9.h>
1362306a36Sopenharmony_ci#include <linux/usb/gadget.h>
1462306a36Sopenharmony_ci#include <linux/usb/video.h>
1562306a36Sopenharmony_ci#include <asm/unaligned.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <media/v4l2-dev.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "uvc.h"
2062306a36Sopenharmony_ci#include "uvc_queue.h"
2162306a36Sopenharmony_ci#include "uvc_video.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* --------------------------------------------------------------------------
2462306a36Sopenharmony_ci * Video codecs
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic int
2862306a36Sopenharmony_ciuvc_video_encode_header(struct uvc_video *video, struct uvc_buffer *buf,
2962306a36Sopenharmony_ci		u8 *data, int len)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	struct uvc_device *uvc = container_of(video, struct uvc_device, video);
3262306a36Sopenharmony_ci	struct usb_composite_dev *cdev = uvc->func.config->cdev;
3362306a36Sopenharmony_ci	struct timespec64 ts = ns_to_timespec64(buf->buf.vb2_buf.timestamp);
3462306a36Sopenharmony_ci	int pos = 2;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	data[1] = UVC_STREAM_EOH | video->fid;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (video->queue.buf_used == 0 && ts.tv_sec) {
3962306a36Sopenharmony_ci		/* dwClockFrequency is 48 MHz */
4062306a36Sopenharmony_ci		u32 pts = ((u64)ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / NSEC_PER_USEC) * 48;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci		data[1] |= UVC_STREAM_PTS;
4362306a36Sopenharmony_ci		put_unaligned_le32(pts, &data[pos]);
4462306a36Sopenharmony_ci		pos += 4;
4562306a36Sopenharmony_ci	}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	if (cdev->gadget->ops->get_frame) {
4862306a36Sopenharmony_ci		u32 sof, stc;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci		sof = usb_gadget_frame_number(cdev->gadget);
5162306a36Sopenharmony_ci		ktime_get_ts64(&ts);
5262306a36Sopenharmony_ci		stc = ((u64)ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / NSEC_PER_USEC) * 48;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci		data[1] |= UVC_STREAM_SCR;
5562306a36Sopenharmony_ci		put_unaligned_le32(stc, &data[pos]);
5662306a36Sopenharmony_ci		put_unaligned_le16(sof, &data[pos+4]);
5762306a36Sopenharmony_ci		pos += 6;
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	data[0] = pos;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (buf->bytesused - video->queue.buf_used <= len - pos)
6362306a36Sopenharmony_ci		data[1] |= UVC_STREAM_EOF;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	return pos;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic int
6962306a36Sopenharmony_ciuvc_video_encode_data(struct uvc_video *video, struct uvc_buffer *buf,
7062306a36Sopenharmony_ci		u8 *data, int len)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct uvc_video_queue *queue = &video->queue;
7362306a36Sopenharmony_ci	unsigned int nbytes;
7462306a36Sopenharmony_ci	void *mem;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/* Copy video data to the USB buffer. */
7762306a36Sopenharmony_ci	mem = buf->mem + queue->buf_used;
7862306a36Sopenharmony_ci	nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	memcpy(data, mem, nbytes);
8162306a36Sopenharmony_ci	queue->buf_used += nbytes;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return nbytes;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic void
8762306a36Sopenharmony_ciuvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video,
8862306a36Sopenharmony_ci		struct uvc_buffer *buf)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	void *mem = req->buf;
9162306a36Sopenharmony_ci	struct uvc_request *ureq = req->context;
9262306a36Sopenharmony_ci	int len = video->req_size;
9362306a36Sopenharmony_ci	int ret;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	/* Add a header at the beginning of the payload. */
9662306a36Sopenharmony_ci	if (video->payload_size == 0) {
9762306a36Sopenharmony_ci		ret = uvc_video_encode_header(video, buf, mem, len);
9862306a36Sopenharmony_ci		video->payload_size += ret;
9962306a36Sopenharmony_ci		mem += ret;
10062306a36Sopenharmony_ci		len -= ret;
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/* Process video data. */
10462306a36Sopenharmony_ci	len = min((int)(video->max_payload_size - video->payload_size), len);
10562306a36Sopenharmony_ci	ret = uvc_video_encode_data(video, buf, mem, len);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	video->payload_size += ret;
10862306a36Sopenharmony_ci	len -= ret;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	req->length = video->req_size - len;
11162306a36Sopenharmony_ci	req->zero = video->payload_size == video->max_payload_size;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (buf->bytesused == video->queue.buf_used) {
11462306a36Sopenharmony_ci		video->queue.buf_used = 0;
11562306a36Sopenharmony_ci		buf->state = UVC_BUF_STATE_DONE;
11662306a36Sopenharmony_ci		list_del(&buf->queue);
11762306a36Sopenharmony_ci		video->fid ^= UVC_STREAM_FID;
11862306a36Sopenharmony_ci		ureq->last_buf = buf;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		video->payload_size = 0;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (video->payload_size == video->max_payload_size ||
12462306a36Sopenharmony_ci	    video->queue.flags & UVC_QUEUE_DROP_INCOMPLETE ||
12562306a36Sopenharmony_ci	    buf->bytesused == video->queue.buf_used)
12662306a36Sopenharmony_ci		video->payload_size = 0;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic void
13062306a36Sopenharmony_ciuvc_video_encode_isoc_sg(struct usb_request *req, struct uvc_video *video,
13162306a36Sopenharmony_ci		struct uvc_buffer *buf)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	unsigned int pending = buf->bytesused - video->queue.buf_used;
13462306a36Sopenharmony_ci	struct uvc_request *ureq = req->context;
13562306a36Sopenharmony_ci	struct scatterlist *sg, *iter;
13662306a36Sopenharmony_ci	unsigned int len = video->req_size;
13762306a36Sopenharmony_ci	unsigned int sg_left, part = 0;
13862306a36Sopenharmony_ci	unsigned int i;
13962306a36Sopenharmony_ci	int header_len;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	sg = ureq->sgt.sgl;
14262306a36Sopenharmony_ci	sg_init_table(sg, ureq->sgt.nents);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/* Init the header. */
14562306a36Sopenharmony_ci	header_len = uvc_video_encode_header(video, buf, ureq->header,
14662306a36Sopenharmony_ci				      video->req_size);
14762306a36Sopenharmony_ci	sg_set_buf(sg, ureq->header, header_len);
14862306a36Sopenharmony_ci	len -= header_len;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	if (pending <= len)
15162306a36Sopenharmony_ci		len = pending;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	req->length = (len == pending) ?
15462306a36Sopenharmony_ci		len + header_len : video->req_size;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	/* Init the pending sgs with payload */
15762306a36Sopenharmony_ci	sg = sg_next(sg);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	for_each_sg(sg, iter, ureq->sgt.nents - 1, i) {
16062306a36Sopenharmony_ci		if (!len || !buf->sg || !buf->sg->length)
16162306a36Sopenharmony_ci			break;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci		sg_left = buf->sg->length - buf->offset;
16462306a36Sopenharmony_ci		part = min_t(unsigned int, len, sg_left);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci		sg_set_page(iter, sg_page(buf->sg), part, buf->offset);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci		if (part == sg_left) {
16962306a36Sopenharmony_ci			buf->offset = 0;
17062306a36Sopenharmony_ci			buf->sg = sg_next(buf->sg);
17162306a36Sopenharmony_ci		} else {
17262306a36Sopenharmony_ci			buf->offset += part;
17362306a36Sopenharmony_ci		}
17462306a36Sopenharmony_ci		len -= part;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	/* Assign the video data with header. */
17862306a36Sopenharmony_ci	req->buf = NULL;
17962306a36Sopenharmony_ci	req->sg	= ureq->sgt.sgl;
18062306a36Sopenharmony_ci	req->num_sgs = i + 1;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	req->length -= len;
18362306a36Sopenharmony_ci	video->queue.buf_used += req->length - header_len;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	if (buf->bytesused == video->queue.buf_used || !buf->sg ||
18662306a36Sopenharmony_ci			video->queue.flags & UVC_QUEUE_DROP_INCOMPLETE) {
18762306a36Sopenharmony_ci		video->queue.buf_used = 0;
18862306a36Sopenharmony_ci		buf->state = UVC_BUF_STATE_DONE;
18962306a36Sopenharmony_ci		buf->offset = 0;
19062306a36Sopenharmony_ci		list_del(&buf->queue);
19162306a36Sopenharmony_ci		video->fid ^= UVC_STREAM_FID;
19262306a36Sopenharmony_ci		ureq->last_buf = buf;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic void
19762306a36Sopenharmony_ciuvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
19862306a36Sopenharmony_ci		struct uvc_buffer *buf)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	void *mem = req->buf;
20162306a36Sopenharmony_ci	struct uvc_request *ureq = req->context;
20262306a36Sopenharmony_ci	int len = video->req_size;
20362306a36Sopenharmony_ci	int ret;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/* Add the header. */
20662306a36Sopenharmony_ci	ret = uvc_video_encode_header(video, buf, mem, len);
20762306a36Sopenharmony_ci	mem += ret;
20862306a36Sopenharmony_ci	len -= ret;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	/* Process video data. */
21162306a36Sopenharmony_ci	ret = uvc_video_encode_data(video, buf, mem, len);
21262306a36Sopenharmony_ci	len -= ret;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	req->length = video->req_size - len;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if (buf->bytesused == video->queue.buf_used ||
21762306a36Sopenharmony_ci			video->queue.flags & UVC_QUEUE_DROP_INCOMPLETE) {
21862306a36Sopenharmony_ci		video->queue.buf_used = 0;
21962306a36Sopenharmony_ci		buf->state = UVC_BUF_STATE_DONE;
22062306a36Sopenharmony_ci		list_del(&buf->queue);
22162306a36Sopenharmony_ci		video->fid ^= UVC_STREAM_FID;
22262306a36Sopenharmony_ci		ureq->last_buf = buf;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci/* --------------------------------------------------------------------------
22762306a36Sopenharmony_ci * Request handling
22862306a36Sopenharmony_ci */
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	int ret;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	ret = usb_ep_queue(video->ep, req, GFP_ATOMIC);
23562306a36Sopenharmony_ci	if (ret < 0) {
23662306a36Sopenharmony_ci		uvcg_err(&video->uvc->func, "Failed to queue request (%d).\n",
23762306a36Sopenharmony_ci			 ret);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci		/* If the endpoint is disabled the descriptor may be NULL. */
24062306a36Sopenharmony_ci		if (video->ep->desc) {
24162306a36Sopenharmony_ci			/* Isochronous endpoints can't be halted. */
24262306a36Sopenharmony_ci			if (usb_endpoint_xfer_bulk(video->ep->desc))
24362306a36Sopenharmony_ci				usb_ep_set_halt(video->ep);
24462306a36Sopenharmony_ci		}
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	return ret;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic void
25162306a36Sopenharmony_ciuvc_video_complete(struct usb_ep *ep, struct usb_request *req)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct uvc_request *ureq = req->context;
25462306a36Sopenharmony_ci	struct uvc_video *video = ureq->video;
25562306a36Sopenharmony_ci	struct uvc_video_queue *queue = &video->queue;
25662306a36Sopenharmony_ci	struct uvc_device *uvc = video->uvc;
25762306a36Sopenharmony_ci	unsigned long flags;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	switch (req->status) {
26062306a36Sopenharmony_ci	case 0:
26162306a36Sopenharmony_ci		break;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	case -EXDEV:
26462306a36Sopenharmony_ci		uvcg_dbg(&video->uvc->func, "VS request missed xfer.\n");
26562306a36Sopenharmony_ci		queue->flags |= UVC_QUEUE_DROP_INCOMPLETE;
26662306a36Sopenharmony_ci		break;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	case -ESHUTDOWN:	/* disconnect from host. */
26962306a36Sopenharmony_ci		uvcg_dbg(&video->uvc->func, "VS request cancelled.\n");
27062306a36Sopenharmony_ci		uvcg_queue_cancel(queue, 1);
27162306a36Sopenharmony_ci		break;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	default:
27462306a36Sopenharmony_ci		uvcg_warn(&video->uvc->func,
27562306a36Sopenharmony_ci			  "VS request completed with status %d.\n",
27662306a36Sopenharmony_ci			  req->status);
27762306a36Sopenharmony_ci		uvcg_queue_cancel(queue, 0);
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	if (ureq->last_buf) {
28162306a36Sopenharmony_ci		uvcg_complete_buffer(&video->queue, ureq->last_buf);
28262306a36Sopenharmony_ci		ureq->last_buf = NULL;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	spin_lock_irqsave(&video->req_lock, flags);
28662306a36Sopenharmony_ci	list_add_tail(&req->list, &video->req_free);
28762306a36Sopenharmony_ci	spin_unlock_irqrestore(&video->req_lock, flags);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (uvc->state == UVC_STATE_STREAMING)
29062306a36Sopenharmony_ci		queue_work(video->async_wq, &video->pump);
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic int
29462306a36Sopenharmony_ciuvc_video_free_requests(struct uvc_video *video)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	unsigned int i;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	if (video->ureq) {
29962306a36Sopenharmony_ci		for (i = 0; i < video->uvc_num_requests; ++i) {
30062306a36Sopenharmony_ci			sg_free_table(&video->ureq[i].sgt);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci			if (video->ureq[i].req) {
30362306a36Sopenharmony_ci				usb_ep_free_request(video->ep, video->ureq[i].req);
30462306a36Sopenharmony_ci				video->ureq[i].req = NULL;
30562306a36Sopenharmony_ci			}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci			if (video->ureq[i].req_buffer) {
30862306a36Sopenharmony_ci				kfree(video->ureq[i].req_buffer);
30962306a36Sopenharmony_ci				video->ureq[i].req_buffer = NULL;
31062306a36Sopenharmony_ci			}
31162306a36Sopenharmony_ci		}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		kfree(video->ureq);
31462306a36Sopenharmony_ci		video->ureq = NULL;
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	INIT_LIST_HEAD(&video->req_free);
31862306a36Sopenharmony_ci	video->req_size = 0;
31962306a36Sopenharmony_ci	return 0;
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic int
32362306a36Sopenharmony_ciuvc_video_alloc_requests(struct uvc_video *video)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	unsigned int req_size;
32662306a36Sopenharmony_ci	unsigned int i;
32762306a36Sopenharmony_ci	int ret = -ENOMEM;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	BUG_ON(video->req_size);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	req_size = video->ep->maxpacket
33262306a36Sopenharmony_ci		 * max_t(unsigned int, video->ep->maxburst, 1)
33362306a36Sopenharmony_ci		 * (video->ep->mult);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	video->ureq = kcalloc(video->uvc_num_requests, sizeof(struct uvc_request), GFP_KERNEL);
33662306a36Sopenharmony_ci	if (video->ureq == NULL)
33762306a36Sopenharmony_ci		return -ENOMEM;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	for (i = 0; i < video->uvc_num_requests; ++i) {
34062306a36Sopenharmony_ci		video->ureq[i].req_buffer = kmalloc(req_size, GFP_KERNEL);
34162306a36Sopenharmony_ci		if (video->ureq[i].req_buffer == NULL)
34262306a36Sopenharmony_ci			goto error;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci		video->ureq[i].req = usb_ep_alloc_request(video->ep, GFP_KERNEL);
34562306a36Sopenharmony_ci		if (video->ureq[i].req == NULL)
34662306a36Sopenharmony_ci			goto error;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci		video->ureq[i].req->buf = video->ureq[i].req_buffer;
34962306a36Sopenharmony_ci		video->ureq[i].req->length = 0;
35062306a36Sopenharmony_ci		video->ureq[i].req->complete = uvc_video_complete;
35162306a36Sopenharmony_ci		video->ureq[i].req->context = &video->ureq[i];
35262306a36Sopenharmony_ci		video->ureq[i].video = video;
35362306a36Sopenharmony_ci		video->ureq[i].last_buf = NULL;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci		list_add_tail(&video->ureq[i].req->list, &video->req_free);
35662306a36Sopenharmony_ci		/* req_size/PAGE_SIZE + 1 for overruns and + 1 for header */
35762306a36Sopenharmony_ci		sg_alloc_table(&video->ureq[i].sgt,
35862306a36Sopenharmony_ci			       DIV_ROUND_UP(req_size - UVCG_REQUEST_HEADER_LEN,
35962306a36Sopenharmony_ci					    PAGE_SIZE) + 2, GFP_KERNEL);
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	video->req_size = req_size;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	return 0;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cierror:
36762306a36Sopenharmony_ci	uvc_video_free_requests(video);
36862306a36Sopenharmony_ci	return ret;
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci/* --------------------------------------------------------------------------
37262306a36Sopenharmony_ci * Video streaming
37362306a36Sopenharmony_ci */
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci/*
37662306a36Sopenharmony_ci * uvcg_video_pump - Pump video data into the USB requests
37762306a36Sopenharmony_ci *
37862306a36Sopenharmony_ci * This function fills the available USB requests (listed in req_free) with
37962306a36Sopenharmony_ci * video data from the queued buffers.
38062306a36Sopenharmony_ci */
38162306a36Sopenharmony_cistatic void uvcg_video_pump(struct work_struct *work)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct uvc_video *video = container_of(work, struct uvc_video, pump);
38462306a36Sopenharmony_ci	struct uvc_video_queue *queue = &video->queue;
38562306a36Sopenharmony_ci	/* video->max_payload_size is only set when using bulk transfer */
38662306a36Sopenharmony_ci	bool is_bulk = video->max_payload_size;
38762306a36Sopenharmony_ci	struct usb_request *req = NULL;
38862306a36Sopenharmony_ci	struct uvc_buffer *buf;
38962306a36Sopenharmony_ci	unsigned long flags;
39062306a36Sopenharmony_ci	bool buf_done;
39162306a36Sopenharmony_ci	int ret;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	while (video->ep->enabled) {
39462306a36Sopenharmony_ci		/*
39562306a36Sopenharmony_ci		 * Retrieve the first available USB request, protected by the
39662306a36Sopenharmony_ci		 * request lock.
39762306a36Sopenharmony_ci		 */
39862306a36Sopenharmony_ci		spin_lock_irqsave(&video->req_lock, flags);
39962306a36Sopenharmony_ci		if (list_empty(&video->req_free)) {
40062306a36Sopenharmony_ci			spin_unlock_irqrestore(&video->req_lock, flags);
40162306a36Sopenharmony_ci			return;
40262306a36Sopenharmony_ci		}
40362306a36Sopenharmony_ci		req = list_first_entry(&video->req_free, struct usb_request,
40462306a36Sopenharmony_ci					list);
40562306a36Sopenharmony_ci		list_del(&req->list);
40662306a36Sopenharmony_ci		spin_unlock_irqrestore(&video->req_lock, flags);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci		/*
40962306a36Sopenharmony_ci		 * Retrieve the first available video buffer and fill the
41062306a36Sopenharmony_ci		 * request, protected by the video queue irqlock.
41162306a36Sopenharmony_ci		 */
41262306a36Sopenharmony_ci		spin_lock_irqsave(&queue->irqlock, flags);
41362306a36Sopenharmony_ci		buf = uvcg_queue_head(queue);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		if (buf != NULL) {
41662306a36Sopenharmony_ci			video->encode(req, video, buf);
41762306a36Sopenharmony_ci			buf_done = buf->state == UVC_BUF_STATE_DONE;
41862306a36Sopenharmony_ci		} else if (!(queue->flags & UVC_QUEUE_DISCONNECTED) && !is_bulk) {
41962306a36Sopenharmony_ci			/*
42062306a36Sopenharmony_ci			 * No video buffer available; the queue is still connected and
42162306a36Sopenharmony_ci			 * we're transferring over ISOC. Queue a 0 length request to
42262306a36Sopenharmony_ci			 * prevent missed ISOC transfers.
42362306a36Sopenharmony_ci			 */
42462306a36Sopenharmony_ci			req->length = 0;
42562306a36Sopenharmony_ci			buf_done = false;
42662306a36Sopenharmony_ci		} else {
42762306a36Sopenharmony_ci			/*
42862306a36Sopenharmony_ci			 * Either the queue has been disconnected or no video buffer
42962306a36Sopenharmony_ci			 * available for bulk transfer. Either way, stop processing
43062306a36Sopenharmony_ci			 * further.
43162306a36Sopenharmony_ci			 */
43262306a36Sopenharmony_ci			spin_unlock_irqrestore(&queue->irqlock, flags);
43362306a36Sopenharmony_ci			break;
43462306a36Sopenharmony_ci		}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci		/*
43762306a36Sopenharmony_ci		 * With USB3 handling more requests at a higher speed, we can't
43862306a36Sopenharmony_ci		 * afford to generate an interrupt for every request. Decide to
43962306a36Sopenharmony_ci		 * interrupt:
44062306a36Sopenharmony_ci		 *
44162306a36Sopenharmony_ci		 * - When no more requests are available in the free queue, as
44262306a36Sopenharmony_ci		 *   this may be our last chance to refill the endpoint's
44362306a36Sopenharmony_ci		 *   request queue.
44462306a36Sopenharmony_ci		 *
44562306a36Sopenharmony_ci		 * - When this is request is the last request for the video
44662306a36Sopenharmony_ci		 *   buffer, as we want to start sending the next video buffer
44762306a36Sopenharmony_ci		 *   ASAP in case it doesn't get started already in the next
44862306a36Sopenharmony_ci		 *   iteration of this loop.
44962306a36Sopenharmony_ci		 *
45062306a36Sopenharmony_ci		 * - Four times over the length of the requests queue (as
45162306a36Sopenharmony_ci		 *   indicated by video->uvc_num_requests), as a trade-off
45262306a36Sopenharmony_ci		 *   between latency and interrupt load.
45362306a36Sopenharmony_ci		 */
45462306a36Sopenharmony_ci		if (list_empty(&video->req_free) || buf_done ||
45562306a36Sopenharmony_ci		    !(video->req_int_count %
45662306a36Sopenharmony_ci		       DIV_ROUND_UP(video->uvc_num_requests, 4))) {
45762306a36Sopenharmony_ci			video->req_int_count = 0;
45862306a36Sopenharmony_ci			req->no_interrupt = 0;
45962306a36Sopenharmony_ci		} else {
46062306a36Sopenharmony_ci			req->no_interrupt = 1;
46162306a36Sopenharmony_ci		}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci		/* Queue the USB request */
46462306a36Sopenharmony_ci		ret = uvcg_video_ep_queue(video, req);
46562306a36Sopenharmony_ci		spin_unlock_irqrestore(&queue->irqlock, flags);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci		if (ret < 0) {
46862306a36Sopenharmony_ci			uvcg_queue_cancel(queue, 0);
46962306a36Sopenharmony_ci			break;
47062306a36Sopenharmony_ci		}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci		/* Endpoint now owns the request */
47362306a36Sopenharmony_ci		req = NULL;
47462306a36Sopenharmony_ci		video->req_int_count++;
47562306a36Sopenharmony_ci	}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	if (!req)
47862306a36Sopenharmony_ci		return;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	spin_lock_irqsave(&video->req_lock, flags);
48162306a36Sopenharmony_ci	list_add_tail(&req->list, &video->req_free);
48262306a36Sopenharmony_ci	spin_unlock_irqrestore(&video->req_lock, flags);
48362306a36Sopenharmony_ci	return;
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci/*
48762306a36Sopenharmony_ci * Enable or disable the video stream.
48862306a36Sopenharmony_ci */
48962306a36Sopenharmony_ciint uvcg_video_enable(struct uvc_video *video, int enable)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	unsigned int i;
49262306a36Sopenharmony_ci	int ret;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	if (video->ep == NULL) {
49562306a36Sopenharmony_ci		uvcg_info(&video->uvc->func,
49662306a36Sopenharmony_ci			  "Video enable failed, device is uninitialized.\n");
49762306a36Sopenharmony_ci		return -ENODEV;
49862306a36Sopenharmony_ci	}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	if (!enable) {
50162306a36Sopenharmony_ci		cancel_work_sync(&video->pump);
50262306a36Sopenharmony_ci		uvcg_queue_cancel(&video->queue, 0);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci		for (i = 0; i < video->uvc_num_requests; ++i)
50562306a36Sopenharmony_ci			if (video->ureq && video->ureq[i].req)
50662306a36Sopenharmony_ci				usb_ep_dequeue(video->ep, video->ureq[i].req);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci		uvc_video_free_requests(video);
50962306a36Sopenharmony_ci		uvcg_queue_enable(&video->queue, 0);
51062306a36Sopenharmony_ci		return 0;
51162306a36Sopenharmony_ci	}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	if ((ret = uvcg_queue_enable(&video->queue, 1)) < 0)
51462306a36Sopenharmony_ci		return ret;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	if ((ret = uvc_video_alloc_requests(video)) < 0)
51762306a36Sopenharmony_ci		return ret;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	if (video->max_payload_size) {
52062306a36Sopenharmony_ci		video->encode = uvc_video_encode_bulk;
52162306a36Sopenharmony_ci		video->payload_size = 0;
52262306a36Sopenharmony_ci	} else
52362306a36Sopenharmony_ci		video->encode = video->queue.use_sg ?
52462306a36Sopenharmony_ci			uvc_video_encode_isoc_sg : uvc_video_encode_isoc;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	video->req_int_count = 0;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	queue_work(video->async_wq, &video->pump);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	return ret;
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci/*
53462306a36Sopenharmony_ci * Initialize the UVC video stream.
53562306a36Sopenharmony_ci */
53662306a36Sopenharmony_ciint uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	INIT_LIST_HEAD(&video->req_free);
53962306a36Sopenharmony_ci	spin_lock_init(&video->req_lock);
54062306a36Sopenharmony_ci	INIT_WORK(&video->pump, uvcg_video_pump);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	/* Allocate a work queue for asynchronous video pump handler. */
54362306a36Sopenharmony_ci	video->async_wq = alloc_workqueue("uvcgadget", WQ_UNBOUND | WQ_HIGHPRI, 0);
54462306a36Sopenharmony_ci	if (!video->async_wq)
54562306a36Sopenharmony_ci		return -EINVAL;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	video->uvc = uvc;
54862306a36Sopenharmony_ci	video->fcc = V4L2_PIX_FMT_YUYV;
54962306a36Sopenharmony_ci	video->bpp = 16;
55062306a36Sopenharmony_ci	video->width = 320;
55162306a36Sopenharmony_ci	video->height = 240;
55262306a36Sopenharmony_ci	video->imagesize = 320 * 240 * 2;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	/* Initialize the video buffers queue. */
55562306a36Sopenharmony_ci	uvcg_queue_init(&video->queue, uvc->v4l2_dev.dev->parent,
55662306a36Sopenharmony_ci			V4L2_BUF_TYPE_VIDEO_OUTPUT, &video->mutex);
55762306a36Sopenharmony_ci	return 0;
55862306a36Sopenharmony_ci}
559