18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci// tm6000-video.c - driver for TM5600/TM6000/TM6010 USB video capture devices
38c2ecf20Sopenharmony_ci//
48c2ecf20Sopenharmony_ci// Copyright (c) 2006-2007 Mauro Carvalho Chehab <mchehab@kernel.org>
58c2ecf20Sopenharmony_ci//
68c2ecf20Sopenharmony_ci// Copyright (c) 2007 Michel Ludwig <michel.ludwig@gmail.com>
78c2ecf20Sopenharmony_ci//	- Fixed module load/unload
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/delay.h>
118c2ecf20Sopenharmony_ci#include <linux/errno.h>
128c2ecf20Sopenharmony_ci#include <linux/fs.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/mm.h>
168c2ecf20Sopenharmony_ci#include <linux/ioport.h>
178c2ecf20Sopenharmony_ci#include <linux/init.h>
188c2ecf20Sopenharmony_ci#include <linux/sched.h>
198c2ecf20Sopenharmony_ci#include <linux/random.h>
208c2ecf20Sopenharmony_ci#include <linux/usb.h>
218c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
228c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h>
238c2ecf20Sopenharmony_ci#include <media/v4l2-event.h>
248c2ecf20Sopenharmony_ci#include <media/tuner.h>
258c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
268c2ecf20Sopenharmony_ci#include <linux/kthread.h>
278c2ecf20Sopenharmony_ci#include <linux/highmem.h>
288c2ecf20Sopenharmony_ci#include <linux/freezer.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include "tm6000-regs.h"
318c2ecf20Sopenharmony_ci#include "tm6000.h"
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define BUFFER_TIMEOUT     msecs_to_jiffies(2000)  /* 2 seconds */
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* Limits minimum and default number of buffers */
368c2ecf20Sopenharmony_ci#define TM6000_MIN_BUF 4
378c2ecf20Sopenharmony_ci#define TM6000_DEF_BUF 8
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define TM6000_NUM_URB_BUF 8
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define TM6000_MAX_ISO_PACKETS	46	/* Max number of ISO packets */
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/* Declare static vars that will be used as parameters */
448c2ecf20Sopenharmony_cistatic unsigned int vid_limit = 16;	/* Video memory limit, in Mb */
458c2ecf20Sopenharmony_cistatic int video_nr = -1;		/* /dev/videoN, -1 for autodetect */
468c2ecf20Sopenharmony_cistatic int radio_nr = -1;		/* /dev/radioN, -1 for autodetect */
478c2ecf20Sopenharmony_cistatic bool keep_urb;			/* keep urb buffers allocated */
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* Debug level */
508c2ecf20Sopenharmony_ciint tm6000_debug;
518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tm6000_debug);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic struct tm6000_fmt format[] = {
548c2ecf20Sopenharmony_ci	{
558c2ecf20Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_YUYV,
568c2ecf20Sopenharmony_ci		.depth    = 16,
578c2ecf20Sopenharmony_ci	}, {
588c2ecf20Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_UYVY,
598c2ecf20Sopenharmony_ci		.depth    = 16,
608c2ecf20Sopenharmony_ci	}, {
618c2ecf20Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_TM6000,
628c2ecf20Sopenharmony_ci		.depth    = 16,
638c2ecf20Sopenharmony_ci	}
648c2ecf20Sopenharmony_ci};
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------
678c2ecf20Sopenharmony_ci *	DMA and thread functions
688c2ecf20Sopenharmony_ci * ------------------------------------------------------------------
698c2ecf20Sopenharmony_ci */
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci#define norm_maxw(a) 720
728c2ecf20Sopenharmony_ci#define norm_maxh(a) 576
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci#define norm_minw(a) norm_maxw(a)
758c2ecf20Sopenharmony_ci#define norm_minh(a) norm_maxh(a)
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/*
788c2ecf20Sopenharmony_ci * video-buf generic routine to get the next available buffer
798c2ecf20Sopenharmony_ci */
808c2ecf20Sopenharmony_cistatic inline void get_next_buf(struct tm6000_dmaqueue *dma_q,
818c2ecf20Sopenharmony_ci			       struct tm6000_buffer   **buf)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	if (list_empty(&dma_q->active)) {
868c2ecf20Sopenharmony_ci		dprintk(dev, V4L2_DEBUG_QUEUE, "No active queue to serve\n");
878c2ecf20Sopenharmony_ci		*buf = NULL;
888c2ecf20Sopenharmony_ci		return;
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	*buf = list_entry(dma_q->active.next,
928c2ecf20Sopenharmony_ci			struct tm6000_buffer, vb.queue);
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/*
968c2ecf20Sopenharmony_ci * Announces that a buffer were filled and request the next
978c2ecf20Sopenharmony_ci */
988c2ecf20Sopenharmony_cistatic inline void buffer_filled(struct tm6000_core *dev,
998c2ecf20Sopenharmony_ci				 struct tm6000_dmaqueue *dma_q,
1008c2ecf20Sopenharmony_ci				 struct tm6000_buffer *buf)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	/* Advice that buffer was filled */
1038c2ecf20Sopenharmony_ci	dprintk(dev, V4L2_DEBUG_ISOC, "[%p/%d] wakeup\n", buf, buf->vb.i);
1048c2ecf20Sopenharmony_ci	buf->vb.state = VIDEOBUF_DONE;
1058c2ecf20Sopenharmony_ci	buf->vb.field_count++;
1068c2ecf20Sopenharmony_ci	buf->vb.ts = ktime_get_ns();
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	list_del(&buf->vb.queue);
1098c2ecf20Sopenharmony_ci	wake_up(&buf->vb.done);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/*
1138c2ecf20Sopenharmony_ci * Identify the tm5600/6000 buffer header type and properly handles
1148c2ecf20Sopenharmony_ci */
1158c2ecf20Sopenharmony_cistatic int copy_streams(u8 *data, unsigned long len,
1168c2ecf20Sopenharmony_ci			struct urb *urb)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	struct tm6000_dmaqueue  *dma_q = urb->context;
1198c2ecf20Sopenharmony_ci	struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
1208c2ecf20Sopenharmony_ci	u8 *ptr = data, *endp = data+len;
1218c2ecf20Sopenharmony_ci	unsigned long header = 0;
1228c2ecf20Sopenharmony_ci	int rc = 0;
1238c2ecf20Sopenharmony_ci	unsigned int cmd, cpysize, pktsize, size, field, block, line, pos = 0;
1248c2ecf20Sopenharmony_ci	struct tm6000_buffer *vbuf = NULL;
1258c2ecf20Sopenharmony_ci	char *voutp = NULL;
1268c2ecf20Sopenharmony_ci	unsigned int linewidth;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (!dev->radio) {
1298c2ecf20Sopenharmony_ci		/* get video buffer */
1308c2ecf20Sopenharmony_ci		get_next_buf(dma_q, &vbuf);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci		if (!vbuf)
1338c2ecf20Sopenharmony_ci			return rc;
1348c2ecf20Sopenharmony_ci		voutp = videobuf_to_vmalloc(&vbuf->vb);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci		if (!voutp)
1378c2ecf20Sopenharmony_ci			return 0;
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	for (ptr = data; ptr < endp;) {
1418c2ecf20Sopenharmony_ci		if (!dev->isoc_ctl.cmd) {
1428c2ecf20Sopenharmony_ci			/* Header */
1438c2ecf20Sopenharmony_ci			if (dev->isoc_ctl.tmp_buf_len > 0) {
1448c2ecf20Sopenharmony_ci				/* from last urb or packet */
1458c2ecf20Sopenharmony_ci				header = dev->isoc_ctl.tmp_buf;
1468c2ecf20Sopenharmony_ci				if (4 - dev->isoc_ctl.tmp_buf_len > 0) {
1478c2ecf20Sopenharmony_ci					memcpy((u8 *)&header +
1488c2ecf20Sopenharmony_ci						dev->isoc_ctl.tmp_buf_len,
1498c2ecf20Sopenharmony_ci						ptr,
1508c2ecf20Sopenharmony_ci						4 - dev->isoc_ctl.tmp_buf_len);
1518c2ecf20Sopenharmony_ci					ptr += 4 - dev->isoc_ctl.tmp_buf_len;
1528c2ecf20Sopenharmony_ci				}
1538c2ecf20Sopenharmony_ci				dev->isoc_ctl.tmp_buf_len = 0;
1548c2ecf20Sopenharmony_ci			} else {
1558c2ecf20Sopenharmony_ci				if (ptr + 3 >= endp) {
1568c2ecf20Sopenharmony_ci					/* have incomplete header */
1578c2ecf20Sopenharmony_ci					dev->isoc_ctl.tmp_buf_len = endp - ptr;
1588c2ecf20Sopenharmony_ci					memcpy(&dev->isoc_ctl.tmp_buf, ptr,
1598c2ecf20Sopenharmony_ci						dev->isoc_ctl.tmp_buf_len);
1608c2ecf20Sopenharmony_ci					return rc;
1618c2ecf20Sopenharmony_ci				}
1628c2ecf20Sopenharmony_ci				/* Seek for sync */
1638c2ecf20Sopenharmony_ci				for (; ptr < endp - 3; ptr++) {
1648c2ecf20Sopenharmony_ci					if (*(ptr + 3) == 0x47)
1658c2ecf20Sopenharmony_ci						break;
1668c2ecf20Sopenharmony_ci				}
1678c2ecf20Sopenharmony_ci				/* Get message header */
1688c2ecf20Sopenharmony_ci				header = *(unsigned long *)ptr;
1698c2ecf20Sopenharmony_ci				ptr += 4;
1708c2ecf20Sopenharmony_ci			}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci			/* split the header fields */
1738c2ecf20Sopenharmony_ci			size = ((header & 0x7e) << 1);
1748c2ecf20Sopenharmony_ci			if (size > 0)
1758c2ecf20Sopenharmony_ci				size -= 4;
1768c2ecf20Sopenharmony_ci			block = (header >> 7) & 0xf;
1778c2ecf20Sopenharmony_ci			field = (header >> 11) & 0x1;
1788c2ecf20Sopenharmony_ci			line  = (header >> 12) & 0x1ff;
1798c2ecf20Sopenharmony_ci			cmd   = (header >> 21) & 0x7;
1808c2ecf20Sopenharmony_ci			/* Validates header fields */
1818c2ecf20Sopenharmony_ci			if (size > TM6000_URB_MSG_LEN)
1828c2ecf20Sopenharmony_ci				size = TM6000_URB_MSG_LEN;
1838c2ecf20Sopenharmony_ci			pktsize = TM6000_URB_MSG_LEN;
1848c2ecf20Sopenharmony_ci			/*
1858c2ecf20Sopenharmony_ci			 * calculate position in buffer and change the buffer
1868c2ecf20Sopenharmony_ci			 */
1878c2ecf20Sopenharmony_ci			switch (cmd) {
1888c2ecf20Sopenharmony_ci			case TM6000_URB_MSG_VIDEO:
1898c2ecf20Sopenharmony_ci				if (!dev->radio) {
1908c2ecf20Sopenharmony_ci					if ((dev->isoc_ctl.vfield != field) &&
1918c2ecf20Sopenharmony_ci						(field == 1)) {
1928c2ecf20Sopenharmony_ci						/*
1938c2ecf20Sopenharmony_ci						 * Announces that a new buffer
1948c2ecf20Sopenharmony_ci						 * were filled
1958c2ecf20Sopenharmony_ci						 */
1968c2ecf20Sopenharmony_ci						buffer_filled(dev, dma_q, vbuf);
1978c2ecf20Sopenharmony_ci						dprintk(dev, V4L2_DEBUG_ISOC,
1988c2ecf20Sopenharmony_ci							"new buffer filled\n");
1998c2ecf20Sopenharmony_ci						get_next_buf(dma_q, &vbuf);
2008c2ecf20Sopenharmony_ci						if (!vbuf)
2018c2ecf20Sopenharmony_ci							return rc;
2028c2ecf20Sopenharmony_ci						voutp = videobuf_to_vmalloc(&vbuf->vb);
2038c2ecf20Sopenharmony_ci						if (!voutp)
2048c2ecf20Sopenharmony_ci							return rc;
2058c2ecf20Sopenharmony_ci						memset(voutp, 0, vbuf->vb.size);
2068c2ecf20Sopenharmony_ci					}
2078c2ecf20Sopenharmony_ci					linewidth = vbuf->vb.width << 1;
2088c2ecf20Sopenharmony_ci					pos = ((line << 1) - field - 1) *
2098c2ecf20Sopenharmony_ci					linewidth + block * TM6000_URB_MSG_LEN;
2108c2ecf20Sopenharmony_ci					/* Don't allow to write out of the buffer */
2118c2ecf20Sopenharmony_ci					if (pos + size > vbuf->vb.size)
2128c2ecf20Sopenharmony_ci						cmd = TM6000_URB_MSG_ERR;
2138c2ecf20Sopenharmony_ci					dev->isoc_ctl.vfield = field;
2148c2ecf20Sopenharmony_ci				}
2158c2ecf20Sopenharmony_ci				break;
2168c2ecf20Sopenharmony_ci			case TM6000_URB_MSG_VBI:
2178c2ecf20Sopenharmony_ci				break;
2188c2ecf20Sopenharmony_ci			case TM6000_URB_MSG_AUDIO:
2198c2ecf20Sopenharmony_ci			case TM6000_URB_MSG_PTS:
2208c2ecf20Sopenharmony_ci				size = pktsize; /* Size is always 180 bytes */
2218c2ecf20Sopenharmony_ci				break;
2228c2ecf20Sopenharmony_ci			}
2238c2ecf20Sopenharmony_ci		} else {
2248c2ecf20Sopenharmony_ci			/* Continue the last copy */
2258c2ecf20Sopenharmony_ci			cmd = dev->isoc_ctl.cmd;
2268c2ecf20Sopenharmony_ci			size = dev->isoc_ctl.size;
2278c2ecf20Sopenharmony_ci			pos = dev->isoc_ctl.pos;
2288c2ecf20Sopenharmony_ci			pktsize = dev->isoc_ctl.pktsize;
2298c2ecf20Sopenharmony_ci			field = dev->isoc_ctl.field;
2308c2ecf20Sopenharmony_ci		}
2318c2ecf20Sopenharmony_ci		cpysize = (endp - ptr > size) ? size : endp - ptr;
2328c2ecf20Sopenharmony_ci		if (cpysize) {
2338c2ecf20Sopenharmony_ci			/* copy data in different buffers */
2348c2ecf20Sopenharmony_ci			switch (cmd) {
2358c2ecf20Sopenharmony_ci			case TM6000_URB_MSG_VIDEO:
2368c2ecf20Sopenharmony_ci				/* Fills video buffer */
2378c2ecf20Sopenharmony_ci				if (vbuf)
2388c2ecf20Sopenharmony_ci					memcpy(&voutp[pos], ptr, cpysize);
2398c2ecf20Sopenharmony_ci				break;
2408c2ecf20Sopenharmony_ci			case TM6000_URB_MSG_AUDIO: {
2418c2ecf20Sopenharmony_ci				int i;
2428c2ecf20Sopenharmony_ci				for (i = 0; i < cpysize; i += 2)
2438c2ecf20Sopenharmony_ci					swab16s((u16 *)(ptr + i));
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci				tm6000_call_fillbuf(dev, TM6000_AUDIO, ptr, cpysize);
2468c2ecf20Sopenharmony_ci				break;
2478c2ecf20Sopenharmony_ci			}
2488c2ecf20Sopenharmony_ci			case TM6000_URB_MSG_VBI:
2498c2ecf20Sopenharmony_ci				/* Need some code to copy vbi buffer */
2508c2ecf20Sopenharmony_ci				break;
2518c2ecf20Sopenharmony_ci			case TM6000_URB_MSG_PTS: {
2528c2ecf20Sopenharmony_ci				/* Need some code to copy pts */
2538c2ecf20Sopenharmony_ci				u32 pts;
2548c2ecf20Sopenharmony_ci				pts = *(u32 *)ptr;
2558c2ecf20Sopenharmony_ci				dprintk(dev, V4L2_DEBUG_ISOC, "field %d, PTS %x",
2568c2ecf20Sopenharmony_ci					field, pts);
2578c2ecf20Sopenharmony_ci				break;
2588c2ecf20Sopenharmony_ci			}
2598c2ecf20Sopenharmony_ci			}
2608c2ecf20Sopenharmony_ci		}
2618c2ecf20Sopenharmony_ci		if (ptr + pktsize > endp) {
2628c2ecf20Sopenharmony_ci			/*
2638c2ecf20Sopenharmony_ci			 * End of URB packet, but cmd processing is not
2648c2ecf20Sopenharmony_ci			 * complete. Preserve the state for a next packet
2658c2ecf20Sopenharmony_ci			 */
2668c2ecf20Sopenharmony_ci			dev->isoc_ctl.pos = pos + cpysize;
2678c2ecf20Sopenharmony_ci			dev->isoc_ctl.size = size - cpysize;
2688c2ecf20Sopenharmony_ci			dev->isoc_ctl.cmd = cmd;
2698c2ecf20Sopenharmony_ci			dev->isoc_ctl.field = field;
2708c2ecf20Sopenharmony_ci			dev->isoc_ctl.pktsize = pktsize - (endp - ptr);
2718c2ecf20Sopenharmony_ci			ptr += endp - ptr;
2728c2ecf20Sopenharmony_ci		} else {
2738c2ecf20Sopenharmony_ci			dev->isoc_ctl.cmd = 0;
2748c2ecf20Sopenharmony_ci			ptr += pktsize;
2758c2ecf20Sopenharmony_ci		}
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci	return 0;
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci/*
2818c2ecf20Sopenharmony_ci * Identify the tm5600/6000 buffer header type and properly handles
2828c2ecf20Sopenharmony_ci */
2838c2ecf20Sopenharmony_cistatic int copy_multiplexed(u8 *ptr, unsigned long len,
2848c2ecf20Sopenharmony_ci			struct urb *urb)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	struct tm6000_dmaqueue  *dma_q = urb->context;
2878c2ecf20Sopenharmony_ci	struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
2888c2ecf20Sopenharmony_ci	unsigned int pos = dev->isoc_ctl.pos, cpysize;
2898c2ecf20Sopenharmony_ci	int rc = 1;
2908c2ecf20Sopenharmony_ci	struct tm6000_buffer *buf;
2918c2ecf20Sopenharmony_ci	char *outp = NULL;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	get_next_buf(dma_q, &buf);
2948c2ecf20Sopenharmony_ci	if (buf)
2958c2ecf20Sopenharmony_ci		outp = videobuf_to_vmalloc(&buf->vb);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	if (!outp)
2988c2ecf20Sopenharmony_ci		return 0;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	while (len > 0) {
3018c2ecf20Sopenharmony_ci		cpysize = min(len, buf->vb.size-pos);
3028c2ecf20Sopenharmony_ci		memcpy(&outp[pos], ptr, cpysize);
3038c2ecf20Sopenharmony_ci		pos += cpysize;
3048c2ecf20Sopenharmony_ci		ptr += cpysize;
3058c2ecf20Sopenharmony_ci		len -= cpysize;
3068c2ecf20Sopenharmony_ci		if (pos >= buf->vb.size) {
3078c2ecf20Sopenharmony_ci			pos = 0;
3088c2ecf20Sopenharmony_ci			/* Announces that a new buffer were filled */
3098c2ecf20Sopenharmony_ci			buffer_filled(dev, dma_q, buf);
3108c2ecf20Sopenharmony_ci			dprintk(dev, V4L2_DEBUG_ISOC, "new buffer filled\n");
3118c2ecf20Sopenharmony_ci			get_next_buf(dma_q, &buf);
3128c2ecf20Sopenharmony_ci			if (!buf)
3138c2ecf20Sopenharmony_ci				break;
3148c2ecf20Sopenharmony_ci			outp = videobuf_to_vmalloc(&(buf->vb));
3158c2ecf20Sopenharmony_ci			if (!outp)
3168c2ecf20Sopenharmony_ci				return rc;
3178c2ecf20Sopenharmony_ci			pos = 0;
3188c2ecf20Sopenharmony_ci		}
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	dev->isoc_ctl.pos = pos;
3228c2ecf20Sopenharmony_ci	return rc;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic inline void print_err_status(struct tm6000_core *dev,
3268c2ecf20Sopenharmony_ci				     int packet, int status)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	char *errmsg = "Unknown";
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	switch (status) {
3318c2ecf20Sopenharmony_ci	case -ENOENT:
3328c2ecf20Sopenharmony_ci		errmsg = "unlinked synchronously";
3338c2ecf20Sopenharmony_ci		break;
3348c2ecf20Sopenharmony_ci	case -ECONNRESET:
3358c2ecf20Sopenharmony_ci		errmsg = "unlinked asynchronously";
3368c2ecf20Sopenharmony_ci		break;
3378c2ecf20Sopenharmony_ci	case -ENOSR:
3388c2ecf20Sopenharmony_ci		errmsg = "Buffer error (overrun)";
3398c2ecf20Sopenharmony_ci		break;
3408c2ecf20Sopenharmony_ci	case -EPIPE:
3418c2ecf20Sopenharmony_ci		errmsg = "Stalled (device not responding)";
3428c2ecf20Sopenharmony_ci		break;
3438c2ecf20Sopenharmony_ci	case -EOVERFLOW:
3448c2ecf20Sopenharmony_ci		errmsg = "Babble (bad cable?)";
3458c2ecf20Sopenharmony_ci		break;
3468c2ecf20Sopenharmony_ci	case -EPROTO:
3478c2ecf20Sopenharmony_ci		errmsg = "Bit-stuff error (bad cable?)";
3488c2ecf20Sopenharmony_ci		break;
3498c2ecf20Sopenharmony_ci	case -EILSEQ:
3508c2ecf20Sopenharmony_ci		errmsg = "CRC/Timeout (could be anything)";
3518c2ecf20Sopenharmony_ci		break;
3528c2ecf20Sopenharmony_ci	case -ETIME:
3538c2ecf20Sopenharmony_ci		errmsg = "Device does not respond";
3548c2ecf20Sopenharmony_ci		break;
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci	if (packet < 0) {
3578c2ecf20Sopenharmony_ci		dprintk(dev, V4L2_DEBUG_QUEUE, "URB status %d [%s].\n",
3588c2ecf20Sopenharmony_ci			status, errmsg);
3598c2ecf20Sopenharmony_ci	} else {
3608c2ecf20Sopenharmony_ci		dprintk(dev, V4L2_DEBUG_QUEUE, "URB packet %d, status %d [%s].\n",
3618c2ecf20Sopenharmony_ci			packet, status, errmsg);
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci/*
3678c2ecf20Sopenharmony_ci * Controls the isoc copy of each urb packet
3688c2ecf20Sopenharmony_ci */
3698c2ecf20Sopenharmony_cistatic inline int tm6000_isoc_copy(struct urb *urb)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	struct tm6000_dmaqueue  *dma_q = urb->context;
3728c2ecf20Sopenharmony_ci	struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
3738c2ecf20Sopenharmony_ci	int i, len = 0, rc = 1, status;
3748c2ecf20Sopenharmony_ci	char *p;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	if (urb->status < 0) {
3778c2ecf20Sopenharmony_ci		print_err_status(dev, -1, urb->status);
3788c2ecf20Sopenharmony_ci		return 0;
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	for (i = 0; i < urb->number_of_packets; i++) {
3828c2ecf20Sopenharmony_ci		status = urb->iso_frame_desc[i].status;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci		if (status < 0) {
3858c2ecf20Sopenharmony_ci			print_err_status(dev, i, status);
3868c2ecf20Sopenharmony_ci			continue;
3878c2ecf20Sopenharmony_ci		}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci		len = urb->iso_frame_desc[i].actual_length;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci		if (len > 0) {
3928c2ecf20Sopenharmony_ci			p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
3938c2ecf20Sopenharmony_ci			if (!urb->iso_frame_desc[i].status) {
3948c2ecf20Sopenharmony_ci				if ((dev->fourcc) == V4L2_PIX_FMT_TM6000) {
3958c2ecf20Sopenharmony_ci					rc = copy_multiplexed(p, len, urb);
3968c2ecf20Sopenharmony_ci					if (rc <= 0)
3978c2ecf20Sopenharmony_ci						return rc;
3988c2ecf20Sopenharmony_ci				} else {
3998c2ecf20Sopenharmony_ci					copy_streams(p, len, urb);
4008c2ecf20Sopenharmony_ci				}
4018c2ecf20Sopenharmony_ci			}
4028c2ecf20Sopenharmony_ci		}
4038c2ecf20Sopenharmony_ci	}
4048c2ecf20Sopenharmony_ci	return rc;
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------
4088c2ecf20Sopenharmony_ci *	URB control
4098c2ecf20Sopenharmony_ci * ------------------------------------------------------------------
4108c2ecf20Sopenharmony_ci */
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci/*
4138c2ecf20Sopenharmony_ci * IRQ callback, called by URB callback
4148c2ecf20Sopenharmony_ci */
4158c2ecf20Sopenharmony_cistatic void tm6000_irq_callback(struct urb *urb)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	struct tm6000_dmaqueue  *dma_q = urb->context;
4188c2ecf20Sopenharmony_ci	struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
4198c2ecf20Sopenharmony_ci	unsigned long flags;
4208c2ecf20Sopenharmony_ci	int i;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	switch (urb->status) {
4238c2ecf20Sopenharmony_ci	case 0:
4248c2ecf20Sopenharmony_ci	case -ETIMEDOUT:
4258c2ecf20Sopenharmony_ci		break;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	case -ECONNRESET:
4288c2ecf20Sopenharmony_ci	case -ENOENT:
4298c2ecf20Sopenharmony_ci	case -ESHUTDOWN:
4308c2ecf20Sopenharmony_ci		return;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	default:
4338c2ecf20Sopenharmony_ci		tm6000_err("urb completion error %d.\n", urb->status);
4348c2ecf20Sopenharmony_ci		break;
4358c2ecf20Sopenharmony_ci	}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->slock, flags);
4388c2ecf20Sopenharmony_ci	tm6000_isoc_copy(urb);
4398c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->slock, flags);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	/* Reset urb buffers */
4428c2ecf20Sopenharmony_ci	for (i = 0; i < urb->number_of_packets; i++) {
4438c2ecf20Sopenharmony_ci		urb->iso_frame_desc[i].status = 0;
4448c2ecf20Sopenharmony_ci		urb->iso_frame_desc[i].actual_length = 0;
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	urb->status = usb_submit_urb(urb, GFP_ATOMIC);
4488c2ecf20Sopenharmony_ci	if (urb->status)
4498c2ecf20Sopenharmony_ci		tm6000_err("urb resubmit failed (error=%i)\n",
4508c2ecf20Sopenharmony_ci			urb->status);
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci/*
4548c2ecf20Sopenharmony_ci * Allocate URB buffers
4558c2ecf20Sopenharmony_ci */
4568c2ecf20Sopenharmony_cistatic int tm6000_alloc_urb_buffers(struct tm6000_core *dev)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	int num_bufs = TM6000_NUM_URB_BUF;
4598c2ecf20Sopenharmony_ci	int i;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	if (dev->urb_buffer)
4628c2ecf20Sopenharmony_ci		return 0;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	dev->urb_buffer = kmalloc_array(num_bufs, sizeof(*dev->urb_buffer),
4658c2ecf20Sopenharmony_ci					GFP_KERNEL);
4668c2ecf20Sopenharmony_ci	if (!dev->urb_buffer)
4678c2ecf20Sopenharmony_ci		return -ENOMEM;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	dev->urb_dma = kmalloc_array(num_bufs, sizeof(*dev->urb_dma),
4708c2ecf20Sopenharmony_ci				     GFP_KERNEL);
4718c2ecf20Sopenharmony_ci	if (!dev->urb_dma)
4728c2ecf20Sopenharmony_ci		return -ENOMEM;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	for (i = 0; i < num_bufs; i++) {
4758c2ecf20Sopenharmony_ci		dev->urb_buffer[i] = usb_alloc_coherent(
4768c2ecf20Sopenharmony_ci					dev->udev, dev->urb_size,
4778c2ecf20Sopenharmony_ci					GFP_KERNEL, &dev->urb_dma[i]);
4788c2ecf20Sopenharmony_ci		if (!dev->urb_buffer[i]) {
4798c2ecf20Sopenharmony_ci			tm6000_err("unable to allocate %i bytes for transfer buffer %i\n",
4808c2ecf20Sopenharmony_ci				    dev->urb_size, i);
4818c2ecf20Sopenharmony_ci			return -ENOMEM;
4828c2ecf20Sopenharmony_ci		}
4838c2ecf20Sopenharmony_ci		memset(dev->urb_buffer[i], 0, dev->urb_size);
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	return 0;
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci/*
4908c2ecf20Sopenharmony_ci * Free URB buffers
4918c2ecf20Sopenharmony_ci */
4928c2ecf20Sopenharmony_cistatic int tm6000_free_urb_buffers(struct tm6000_core *dev)
4938c2ecf20Sopenharmony_ci{
4948c2ecf20Sopenharmony_ci	int i;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	if (!dev->urb_buffer)
4978c2ecf20Sopenharmony_ci		return 0;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	for (i = 0; i < TM6000_NUM_URB_BUF; i++) {
5008c2ecf20Sopenharmony_ci		if (dev->urb_buffer[i]) {
5018c2ecf20Sopenharmony_ci			usb_free_coherent(dev->udev,
5028c2ecf20Sopenharmony_ci					dev->urb_size,
5038c2ecf20Sopenharmony_ci					dev->urb_buffer[i],
5048c2ecf20Sopenharmony_ci					dev->urb_dma[i]);
5058c2ecf20Sopenharmony_ci			dev->urb_buffer[i] = NULL;
5068c2ecf20Sopenharmony_ci		}
5078c2ecf20Sopenharmony_ci	}
5088c2ecf20Sopenharmony_ci	kfree(dev->urb_buffer);
5098c2ecf20Sopenharmony_ci	kfree(dev->urb_dma);
5108c2ecf20Sopenharmony_ci	dev->urb_buffer = NULL;
5118c2ecf20Sopenharmony_ci	dev->urb_dma = NULL;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	return 0;
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci/*
5178c2ecf20Sopenharmony_ci * Stop and Deallocate URBs
5188c2ecf20Sopenharmony_ci */
5198c2ecf20Sopenharmony_cistatic void tm6000_uninit_isoc(struct tm6000_core *dev)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	struct urb *urb;
5228c2ecf20Sopenharmony_ci	int i;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	dev->isoc_ctl.buf = NULL;
5258c2ecf20Sopenharmony_ci	for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
5268c2ecf20Sopenharmony_ci		urb = dev->isoc_ctl.urb[i];
5278c2ecf20Sopenharmony_ci		if (urb) {
5288c2ecf20Sopenharmony_ci			usb_kill_urb(urb);
5298c2ecf20Sopenharmony_ci			usb_unlink_urb(urb);
5308c2ecf20Sopenharmony_ci			usb_free_urb(urb);
5318c2ecf20Sopenharmony_ci			dev->isoc_ctl.urb[i] = NULL;
5328c2ecf20Sopenharmony_ci		}
5338c2ecf20Sopenharmony_ci		dev->isoc_ctl.transfer_buffer[i] = NULL;
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	if (!keep_urb)
5378c2ecf20Sopenharmony_ci		tm6000_free_urb_buffers(dev);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	kfree(dev->isoc_ctl.urb);
5408c2ecf20Sopenharmony_ci	kfree(dev->isoc_ctl.transfer_buffer);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	dev->isoc_ctl.urb = NULL;
5438c2ecf20Sopenharmony_ci	dev->isoc_ctl.transfer_buffer = NULL;
5448c2ecf20Sopenharmony_ci	dev->isoc_ctl.num_bufs = 0;
5458c2ecf20Sopenharmony_ci}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci/*
5488c2ecf20Sopenharmony_ci * Assign URBs and start IRQ
5498c2ecf20Sopenharmony_ci */
5508c2ecf20Sopenharmony_cistatic int tm6000_prepare_isoc(struct tm6000_core *dev)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	struct tm6000_dmaqueue *dma_q = &dev->vidq;
5538c2ecf20Sopenharmony_ci	int i, j, sb_size, pipe, size, max_packets;
5548c2ecf20Sopenharmony_ci	int num_bufs = TM6000_NUM_URB_BUF;
5558c2ecf20Sopenharmony_ci	struct urb *urb;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	/* De-allocates all pending stuff */
5588c2ecf20Sopenharmony_ci	tm6000_uninit_isoc(dev);
5598c2ecf20Sopenharmony_ci	/* Stop interrupt USB pipe */
5608c2ecf20Sopenharmony_ci	tm6000_ir_int_stop(dev);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	usb_set_interface(dev->udev,
5638c2ecf20Sopenharmony_ci			  dev->isoc_in.bInterfaceNumber,
5648c2ecf20Sopenharmony_ci			  dev->isoc_in.bAlternateSetting);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	/* Start interrupt USB pipe */
5678c2ecf20Sopenharmony_ci	tm6000_ir_int_start(dev);
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	pipe = usb_rcvisocpipe(dev->udev,
5708c2ecf20Sopenharmony_ci			       dev->isoc_in.endp->desc.bEndpointAddress &
5718c2ecf20Sopenharmony_ci			       USB_ENDPOINT_NUMBER_MASK);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe));
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	if (size > dev->isoc_in.maxsize)
5768c2ecf20Sopenharmony_ci		size = dev->isoc_in.maxsize;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	dev->isoc_ctl.max_pkt_size = size;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	max_packets = TM6000_MAX_ISO_PACKETS;
5818c2ecf20Sopenharmony_ci	sb_size = max_packets * size;
5828c2ecf20Sopenharmony_ci	dev->urb_size = sb_size;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	dev->isoc_ctl.num_bufs = num_bufs;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	dev->isoc_ctl.urb = kmalloc_array(num_bufs, sizeof(void *),
5878c2ecf20Sopenharmony_ci					  GFP_KERNEL);
5888c2ecf20Sopenharmony_ci	if (!dev->isoc_ctl.urb)
5898c2ecf20Sopenharmony_ci		return -ENOMEM;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	dev->isoc_ctl.transfer_buffer = kmalloc_array(num_bufs,
5928c2ecf20Sopenharmony_ci						      sizeof(void *),
5938c2ecf20Sopenharmony_ci						      GFP_KERNEL);
5948c2ecf20Sopenharmony_ci	if (!dev->isoc_ctl.transfer_buffer) {
5958c2ecf20Sopenharmony_ci		kfree(dev->isoc_ctl.urb);
5968c2ecf20Sopenharmony_ci		return -ENOMEM;
5978c2ecf20Sopenharmony_ci	}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	dprintk(dev, V4L2_DEBUG_QUEUE, "Allocating %d x %d packets (%d bytes) of %d bytes each to handle %u size\n",
6008c2ecf20Sopenharmony_ci		    max_packets, num_bufs, sb_size,
6018c2ecf20Sopenharmony_ci		    dev->isoc_in.maxsize, size);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	if (tm6000_alloc_urb_buffers(dev) < 0) {
6058c2ecf20Sopenharmony_ci		tm6000_err("cannot allocate memory for urb buffers\n");
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci		/* call free, as some buffers might have been allocated */
6088c2ecf20Sopenharmony_ci		tm6000_free_urb_buffers(dev);
6098c2ecf20Sopenharmony_ci		kfree(dev->isoc_ctl.urb);
6108c2ecf20Sopenharmony_ci		kfree(dev->isoc_ctl.transfer_buffer);
6118c2ecf20Sopenharmony_ci		return -ENOMEM;
6128c2ecf20Sopenharmony_ci	}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	/* allocate urbs and transfer buffers */
6158c2ecf20Sopenharmony_ci	for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
6168c2ecf20Sopenharmony_ci		urb = usb_alloc_urb(max_packets, GFP_KERNEL);
6178c2ecf20Sopenharmony_ci		if (!urb) {
6188c2ecf20Sopenharmony_ci			tm6000_uninit_isoc(dev);
6198c2ecf20Sopenharmony_ci			tm6000_free_urb_buffers(dev);
6208c2ecf20Sopenharmony_ci			return -ENOMEM;
6218c2ecf20Sopenharmony_ci		}
6228c2ecf20Sopenharmony_ci		dev->isoc_ctl.urb[i] = urb;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci		urb->transfer_dma = dev->urb_dma[i];
6258c2ecf20Sopenharmony_ci		dev->isoc_ctl.transfer_buffer[i] = dev->urb_buffer[i];
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci		usb_fill_bulk_urb(urb, dev->udev, pipe,
6288c2ecf20Sopenharmony_ci				  dev->isoc_ctl.transfer_buffer[i], sb_size,
6298c2ecf20Sopenharmony_ci				  tm6000_irq_callback, dma_q);
6308c2ecf20Sopenharmony_ci		urb->interval = dev->isoc_in.endp->desc.bInterval;
6318c2ecf20Sopenharmony_ci		urb->number_of_packets = max_packets;
6328c2ecf20Sopenharmony_ci		urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci		for (j = 0; j < max_packets; j++) {
6358c2ecf20Sopenharmony_ci			urb->iso_frame_desc[j].offset = size * j;
6368c2ecf20Sopenharmony_ci			urb->iso_frame_desc[j].length = size;
6378c2ecf20Sopenharmony_ci		}
6388c2ecf20Sopenharmony_ci	}
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	return 0;
6418c2ecf20Sopenharmony_ci}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_cistatic int tm6000_start_thread(struct tm6000_core *dev)
6448c2ecf20Sopenharmony_ci{
6458c2ecf20Sopenharmony_ci	struct tm6000_dmaqueue *dma_q = &dev->vidq;
6468c2ecf20Sopenharmony_ci	int i;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	dma_q->frame = 0;
6498c2ecf20Sopenharmony_ci	dma_q->ini_jiffies = jiffies;
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	init_waitqueue_head(&dma_q->wq);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	/* submit urbs and enables IRQ */
6548c2ecf20Sopenharmony_ci	for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
6558c2ecf20Sopenharmony_ci		int rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC);
6568c2ecf20Sopenharmony_ci		if (rc) {
6578c2ecf20Sopenharmony_ci			tm6000_err("submit of urb %i failed (error=%i)\n", i,
6588c2ecf20Sopenharmony_ci				   rc);
6598c2ecf20Sopenharmony_ci			tm6000_uninit_isoc(dev);
6608c2ecf20Sopenharmony_ci			return rc;
6618c2ecf20Sopenharmony_ci		}
6628c2ecf20Sopenharmony_ci	}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	return 0;
6658c2ecf20Sopenharmony_ci}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------
6688c2ecf20Sopenharmony_ci *	Videobuf operations
6698c2ecf20Sopenharmony_ci * ------------------------------------------------------------------
6708c2ecf20Sopenharmony_ci */
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_cistatic int
6738c2ecf20Sopenharmony_cibuffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
6748c2ecf20Sopenharmony_ci{
6758c2ecf20Sopenharmony_ci	struct tm6000_fh *fh = vq->priv_data;
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	*size = fh->fmt->depth * fh->width * fh->height >> 3;
6788c2ecf20Sopenharmony_ci	if (0 == *count)
6798c2ecf20Sopenharmony_ci		*count = TM6000_DEF_BUF;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	if (*count < TM6000_MIN_BUF)
6828c2ecf20Sopenharmony_ci		*count = TM6000_MIN_BUF;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	while (*size * *count > vid_limit * 1024 * 1024)
6858c2ecf20Sopenharmony_ci		(*count)--;
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	return 0;
6888c2ecf20Sopenharmony_ci}
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_cistatic void free_buffer(struct videobuf_queue *vq, struct tm6000_buffer *buf)
6918c2ecf20Sopenharmony_ci{
6928c2ecf20Sopenharmony_ci	struct tm6000_fh *fh = vq->priv_data;
6938c2ecf20Sopenharmony_ci	struct tm6000_core   *dev = fh->dev;
6948c2ecf20Sopenharmony_ci	unsigned long flags;
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	BUG_ON(in_interrupt());
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	/* We used to wait for the buffer to finish here, but this didn't work
6998c2ecf20Sopenharmony_ci	   because, as we were keeping the state as VIDEOBUF_QUEUED,
7008c2ecf20Sopenharmony_ci	   videobuf_queue_cancel marked it as finished for us.
7018c2ecf20Sopenharmony_ci	   (Also, it could wedge forever if the hardware was misconfigured.)
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	   This should be safe; by the time we get here, the buffer isn't
7048c2ecf20Sopenharmony_ci	   queued anymore. If we ever start marking the buffers as
7058c2ecf20Sopenharmony_ci	   VIDEOBUF_ACTIVE, it won't be, though.
7068c2ecf20Sopenharmony_ci	*/
7078c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->slock, flags);
7088c2ecf20Sopenharmony_ci	if (dev->isoc_ctl.buf == buf)
7098c2ecf20Sopenharmony_ci		dev->isoc_ctl.buf = NULL;
7108c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->slock, flags);
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	videobuf_vmalloc_free(&buf->vb);
7138c2ecf20Sopenharmony_ci	buf->vb.state = VIDEOBUF_NEEDS_INIT;
7148c2ecf20Sopenharmony_ci}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_cistatic int
7178c2ecf20Sopenharmony_cibuffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
7188c2ecf20Sopenharmony_ci						enum v4l2_field field)
7198c2ecf20Sopenharmony_ci{
7208c2ecf20Sopenharmony_ci	struct tm6000_fh     *fh  = vq->priv_data;
7218c2ecf20Sopenharmony_ci	struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb);
7228c2ecf20Sopenharmony_ci	struct tm6000_core   *dev = fh->dev;
7238c2ecf20Sopenharmony_ci	int rc = 0;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	BUG_ON(NULL == fh->fmt);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	/* FIXME: It assumes depth=2 */
7298c2ecf20Sopenharmony_ci	/* The only currently supported format is 16 bits/pixel */
7308c2ecf20Sopenharmony_ci	buf->vb.size = fh->fmt->depth*fh->width*fh->height >> 3;
7318c2ecf20Sopenharmony_ci	if (0 != buf->vb.baddr  &&  buf->vb.bsize < buf->vb.size)
7328c2ecf20Sopenharmony_ci		return -EINVAL;
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	if (buf->fmt       != fh->fmt    ||
7358c2ecf20Sopenharmony_ci	    buf->vb.width  != fh->width  ||
7368c2ecf20Sopenharmony_ci	    buf->vb.height != fh->height ||
7378c2ecf20Sopenharmony_ci	    buf->vb.field  != field) {
7388c2ecf20Sopenharmony_ci		buf->fmt       = fh->fmt;
7398c2ecf20Sopenharmony_ci		buf->vb.width  = fh->width;
7408c2ecf20Sopenharmony_ci		buf->vb.height = fh->height;
7418c2ecf20Sopenharmony_ci		buf->vb.field  = field;
7428c2ecf20Sopenharmony_ci		buf->vb.state = VIDEOBUF_NEEDS_INIT;
7438c2ecf20Sopenharmony_ci	}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
7468c2ecf20Sopenharmony_ci		rc = videobuf_iolock(vq, &buf->vb, NULL);
7478c2ecf20Sopenharmony_ci		if (rc != 0)
7488c2ecf20Sopenharmony_ci			goto fail;
7498c2ecf20Sopenharmony_ci	}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	if (!dev->isoc_ctl.num_bufs) {
7528c2ecf20Sopenharmony_ci		rc = tm6000_prepare_isoc(dev);
7538c2ecf20Sopenharmony_ci		if (rc < 0)
7548c2ecf20Sopenharmony_ci			goto fail;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci		rc = tm6000_start_thread(dev);
7578c2ecf20Sopenharmony_ci		if (rc < 0)
7588c2ecf20Sopenharmony_ci			goto fail;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	}
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	buf->vb.state = VIDEOBUF_PREPARED;
7638c2ecf20Sopenharmony_ci	return 0;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_cifail:
7668c2ecf20Sopenharmony_ci	free_buffer(vq, buf);
7678c2ecf20Sopenharmony_ci	return rc;
7688c2ecf20Sopenharmony_ci}
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_cistatic void
7718c2ecf20Sopenharmony_cibuffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
7728c2ecf20Sopenharmony_ci{
7738c2ecf20Sopenharmony_ci	struct tm6000_buffer    *buf     = container_of(vb, struct tm6000_buffer, vb);
7748c2ecf20Sopenharmony_ci	struct tm6000_fh        *fh      = vq->priv_data;
7758c2ecf20Sopenharmony_ci	struct tm6000_core      *dev     = fh->dev;
7768c2ecf20Sopenharmony_ci	struct tm6000_dmaqueue  *vidq    = &dev->vidq;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	buf->vb.state = VIDEOBUF_QUEUED;
7798c2ecf20Sopenharmony_ci	list_add_tail(&buf->vb.queue, &vidq->active);
7808c2ecf20Sopenharmony_ci}
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_cistatic void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb)
7838c2ecf20Sopenharmony_ci{
7848c2ecf20Sopenharmony_ci	struct tm6000_buffer   *buf  = container_of(vb, struct tm6000_buffer, vb);
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	free_buffer(vq, buf);
7878c2ecf20Sopenharmony_ci}
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_cistatic const struct videobuf_queue_ops tm6000_video_qops = {
7908c2ecf20Sopenharmony_ci	.buf_setup      = buffer_setup,
7918c2ecf20Sopenharmony_ci	.buf_prepare    = buffer_prepare,
7928c2ecf20Sopenharmony_ci	.buf_queue      = buffer_queue,
7938c2ecf20Sopenharmony_ci	.buf_release    = buffer_release,
7948c2ecf20Sopenharmony_ci};
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------
7978c2ecf20Sopenharmony_ci *	IOCTL handling
7988c2ecf20Sopenharmony_ci * ------------------------------------------------------------------
7998c2ecf20Sopenharmony_ci */
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_cistatic bool is_res_read(struct tm6000_core *dev, struct tm6000_fh *fh)
8028c2ecf20Sopenharmony_ci{
8038c2ecf20Sopenharmony_ci	/* Is the current fh handling it? if so, that's OK */
8048c2ecf20Sopenharmony_ci	if (dev->resources == fh && dev->is_res_read)
8058c2ecf20Sopenharmony_ci		return true;
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	return false;
8088c2ecf20Sopenharmony_ci}
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_cistatic bool is_res_streaming(struct tm6000_core *dev, struct tm6000_fh *fh)
8118c2ecf20Sopenharmony_ci{
8128c2ecf20Sopenharmony_ci	/* Is the current fh handling it? if so, that's OK */
8138c2ecf20Sopenharmony_ci	if (dev->resources == fh)
8148c2ecf20Sopenharmony_ci		return true;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	return false;
8178c2ecf20Sopenharmony_ci}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_cistatic bool res_get(struct tm6000_core *dev, struct tm6000_fh *fh,
8208c2ecf20Sopenharmony_ci		   bool is_res_read)
8218c2ecf20Sopenharmony_ci{
8228c2ecf20Sopenharmony_ci	/* Is the current fh handling it? if so, that's OK */
8238c2ecf20Sopenharmony_ci	if (dev->resources == fh && dev->is_res_read == is_res_read)
8248c2ecf20Sopenharmony_ci		return true;
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	/* is it free? */
8278c2ecf20Sopenharmony_ci	if (dev->resources)
8288c2ecf20Sopenharmony_ci		return false;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	/* grab it */
8318c2ecf20Sopenharmony_ci	dev->resources = fh;
8328c2ecf20Sopenharmony_ci	dev->is_res_read = is_res_read;
8338c2ecf20Sopenharmony_ci	dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: get\n");
8348c2ecf20Sopenharmony_ci	return true;
8358c2ecf20Sopenharmony_ci}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_cistatic void res_free(struct tm6000_core *dev, struct tm6000_fh *fh)
8388c2ecf20Sopenharmony_ci{
8398c2ecf20Sopenharmony_ci	/* Is the current fh handling it? if so, that's OK */
8408c2ecf20Sopenharmony_ci	if (dev->resources != fh)
8418c2ecf20Sopenharmony_ci		return;
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	dev->resources = NULL;
8448c2ecf20Sopenharmony_ci	dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: put\n");
8458c2ecf20Sopenharmony_ci}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------
8488c2ecf20Sopenharmony_ci *	IOCTL vidioc handling
8498c2ecf20Sopenharmony_ci * ------------------------------------------------------------------
8508c2ecf20Sopenharmony_ci */
8518c2ecf20Sopenharmony_cistatic int vidioc_querycap(struct file *file, void  *priv,
8528c2ecf20Sopenharmony_ci					struct v4l2_capability *cap)
8538c2ecf20Sopenharmony_ci{
8548c2ecf20Sopenharmony_ci	struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev;
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	strscpy(cap->driver, "tm6000", sizeof(cap->driver));
8578c2ecf20Sopenharmony_ci	strscpy(cap->card, "Trident TM5600/6000/6010", sizeof(cap->card));
8588c2ecf20Sopenharmony_ci	usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
8598c2ecf20Sopenharmony_ci	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
8608c2ecf20Sopenharmony_ci			    V4L2_CAP_DEVICE_CAPS;
8618c2ecf20Sopenharmony_ci	if (dev->tuner_type != TUNER_ABSENT)
8628c2ecf20Sopenharmony_ci		cap->capabilities |= V4L2_CAP_TUNER;
8638c2ecf20Sopenharmony_ci	if (dev->caps.has_radio)
8648c2ecf20Sopenharmony_ci		cap->capabilities |= V4L2_CAP_RADIO;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	return 0;
8678c2ecf20Sopenharmony_ci}
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_cistatic int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
8708c2ecf20Sopenharmony_ci					struct v4l2_fmtdesc *f)
8718c2ecf20Sopenharmony_ci{
8728c2ecf20Sopenharmony_ci	if (f->index >= ARRAY_SIZE(format))
8738c2ecf20Sopenharmony_ci		return -EINVAL;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	f->pixelformat = format[f->index].fourcc;
8768c2ecf20Sopenharmony_ci	return 0;
8778c2ecf20Sopenharmony_ci}
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_cistatic int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
8808c2ecf20Sopenharmony_ci					struct v4l2_format *f)
8818c2ecf20Sopenharmony_ci{
8828c2ecf20Sopenharmony_ci	struct tm6000_fh  *fh = priv;
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	f->fmt.pix.width        = fh->width;
8858c2ecf20Sopenharmony_ci	f->fmt.pix.height       = fh->height;
8868c2ecf20Sopenharmony_ci	f->fmt.pix.field        = fh->vb_vidq.field;
8878c2ecf20Sopenharmony_ci	f->fmt.pix.pixelformat  = fh->fmt->fourcc;
8888c2ecf20Sopenharmony_ci	f->fmt.pix.colorspace   = V4L2_COLORSPACE_SMPTE170M;
8898c2ecf20Sopenharmony_ci	f->fmt.pix.bytesperline =
8908c2ecf20Sopenharmony_ci		(f->fmt.pix.width * fh->fmt->depth) >> 3;
8918c2ecf20Sopenharmony_ci	f->fmt.pix.sizeimage =
8928c2ecf20Sopenharmony_ci		f->fmt.pix.height * f->fmt.pix.bytesperline;
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	return 0;
8958c2ecf20Sopenharmony_ci}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_cistatic struct tm6000_fmt *format_by_fourcc(unsigned int fourcc)
8988c2ecf20Sopenharmony_ci{
8998c2ecf20Sopenharmony_ci	unsigned int i;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(format); i++)
9028c2ecf20Sopenharmony_ci		if (format[i].fourcc == fourcc)
9038c2ecf20Sopenharmony_ci			return format+i;
9048c2ecf20Sopenharmony_ci	return NULL;
9058c2ecf20Sopenharmony_ci}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_cistatic int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
9088c2ecf20Sopenharmony_ci			struct v4l2_format *f)
9098c2ecf20Sopenharmony_ci{
9108c2ecf20Sopenharmony_ci	struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev;
9118c2ecf20Sopenharmony_ci	struct tm6000_fmt *fmt;
9128c2ecf20Sopenharmony_ci	enum v4l2_field field;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
9158c2ecf20Sopenharmony_ci	if (NULL == fmt) {
9168c2ecf20Sopenharmony_ci		dprintk(dev, 2, "Fourcc format (0x%08x) invalid.\n",
9178c2ecf20Sopenharmony_ci			f->fmt.pix.pixelformat);
9188c2ecf20Sopenharmony_ci		return -EINVAL;
9198c2ecf20Sopenharmony_ci	}
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	field = f->fmt.pix.field;
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	field = V4L2_FIELD_INTERLACED;
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	tm6000_get_std_res(dev);
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	f->fmt.pix.width  = dev->width;
9288c2ecf20Sopenharmony_ci	f->fmt.pix.height = dev->height;
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	f->fmt.pix.width &= ~0x01;
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	f->fmt.pix.field = field;
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	f->fmt.pix.bytesperline =
9358c2ecf20Sopenharmony_ci		(f->fmt.pix.width * fmt->depth) >> 3;
9368c2ecf20Sopenharmony_ci	f->fmt.pix.sizeimage =
9378c2ecf20Sopenharmony_ci		f->fmt.pix.height * f->fmt.pix.bytesperline;
9388c2ecf20Sopenharmony_ci	f->fmt.pix.colorspace   = V4L2_COLORSPACE_SMPTE170M;
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	return 0;
9418c2ecf20Sopenharmony_ci}
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci/*FIXME: This seems to be generic enough to be at videodev2 */
9448c2ecf20Sopenharmony_cistatic int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
9458c2ecf20Sopenharmony_ci					struct v4l2_format *f)
9468c2ecf20Sopenharmony_ci{
9478c2ecf20Sopenharmony_ci	struct tm6000_fh  *fh = priv;
9488c2ecf20Sopenharmony_ci	struct tm6000_core *dev = fh->dev;
9498c2ecf20Sopenharmony_ci	int ret = vidioc_try_fmt_vid_cap(file, fh, f);
9508c2ecf20Sopenharmony_ci	if (ret < 0)
9518c2ecf20Sopenharmony_ci		return ret;
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	fh->fmt           = format_by_fourcc(f->fmt.pix.pixelformat);
9548c2ecf20Sopenharmony_ci	fh->width         = f->fmt.pix.width;
9558c2ecf20Sopenharmony_ci	fh->height        = f->fmt.pix.height;
9568c2ecf20Sopenharmony_ci	fh->vb_vidq.field = f->fmt.pix.field;
9578c2ecf20Sopenharmony_ci	fh->type          = f->type;
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	dev->fourcc       = f->fmt.pix.pixelformat;
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	tm6000_set_fourcc_format(dev);
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	return 0;
9648c2ecf20Sopenharmony_ci}
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_cistatic int vidioc_reqbufs(struct file *file, void *priv,
9678c2ecf20Sopenharmony_ci			   struct v4l2_requestbuffers *p)
9688c2ecf20Sopenharmony_ci{
9698c2ecf20Sopenharmony_ci	struct tm6000_fh  *fh = priv;
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	return videobuf_reqbufs(&fh->vb_vidq, p);
9728c2ecf20Sopenharmony_ci}
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_cistatic int vidioc_querybuf(struct file *file, void *priv,
9758c2ecf20Sopenharmony_ci			    struct v4l2_buffer *p)
9768c2ecf20Sopenharmony_ci{
9778c2ecf20Sopenharmony_ci	struct tm6000_fh  *fh = priv;
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	return videobuf_querybuf(&fh->vb_vidq, p);
9808c2ecf20Sopenharmony_ci}
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_cistatic int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
9838c2ecf20Sopenharmony_ci{
9848c2ecf20Sopenharmony_ci	struct tm6000_fh  *fh = priv;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	return videobuf_qbuf(&fh->vb_vidq, p);
9878c2ecf20Sopenharmony_ci}
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_cistatic int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
9908c2ecf20Sopenharmony_ci{
9918c2ecf20Sopenharmony_ci	struct tm6000_fh  *fh = priv;
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	return videobuf_dqbuf(&fh->vb_vidq, p,
9948c2ecf20Sopenharmony_ci				file->f_flags & O_NONBLOCK);
9958c2ecf20Sopenharmony_ci}
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_cistatic int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
9988c2ecf20Sopenharmony_ci{
9998c2ecf20Sopenharmony_ci	struct tm6000_fh *fh = priv;
10008c2ecf20Sopenharmony_ci	struct tm6000_core *dev = fh->dev;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
10038c2ecf20Sopenharmony_ci		return -EINVAL;
10048c2ecf20Sopenharmony_ci	if (i != fh->type)
10058c2ecf20Sopenharmony_ci		return -EINVAL;
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	if (!res_get(dev, fh, false))
10088c2ecf20Sopenharmony_ci		return -EBUSY;
10098c2ecf20Sopenharmony_ci	return videobuf_streamon(&fh->vb_vidq);
10108c2ecf20Sopenharmony_ci}
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_cistatic int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
10138c2ecf20Sopenharmony_ci{
10148c2ecf20Sopenharmony_ci	struct tm6000_fh *fh = priv;
10158c2ecf20Sopenharmony_ci	struct tm6000_core *dev = fh->dev;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
10188c2ecf20Sopenharmony_ci		return -EINVAL;
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	if (i != fh->type)
10218c2ecf20Sopenharmony_ci		return -EINVAL;
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	videobuf_streamoff(&fh->vb_vidq);
10248c2ecf20Sopenharmony_ci	res_free(dev, fh);
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	return 0;
10278c2ecf20Sopenharmony_ci}
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_cistatic int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
10308c2ecf20Sopenharmony_ci{
10318c2ecf20Sopenharmony_ci	int rc = 0;
10328c2ecf20Sopenharmony_ci	struct tm6000_fh *fh = priv;
10338c2ecf20Sopenharmony_ci	struct tm6000_core *dev = fh->dev;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	dev->norm = norm;
10368c2ecf20Sopenharmony_ci	rc = tm6000_init_analog_mode(dev);
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	fh->width  = dev->width;
10398c2ecf20Sopenharmony_ci	fh->height = dev->height;
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	if (rc < 0)
10428c2ecf20Sopenharmony_ci		return rc;
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci	v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm);
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	return 0;
10478c2ecf20Sopenharmony_ci}
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_cistatic int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
10508c2ecf20Sopenharmony_ci{
10518c2ecf20Sopenharmony_ci	struct tm6000_fh *fh = priv;
10528c2ecf20Sopenharmony_ci	struct tm6000_core *dev = fh->dev;
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	*norm = dev->norm;
10558c2ecf20Sopenharmony_ci	return 0;
10568c2ecf20Sopenharmony_ci}
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_cistatic const char *iname[] = {
10598c2ecf20Sopenharmony_ci	[TM6000_INPUT_TV] = "Television",
10608c2ecf20Sopenharmony_ci	[TM6000_INPUT_COMPOSITE1] = "Composite 1",
10618c2ecf20Sopenharmony_ci	[TM6000_INPUT_COMPOSITE2] = "Composite 2",
10628c2ecf20Sopenharmony_ci	[TM6000_INPUT_SVIDEO] = "S-Video",
10638c2ecf20Sopenharmony_ci};
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_cistatic int vidioc_enum_input(struct file *file, void *priv,
10668c2ecf20Sopenharmony_ci				struct v4l2_input *i)
10678c2ecf20Sopenharmony_ci{
10688c2ecf20Sopenharmony_ci	struct tm6000_fh   *fh = priv;
10698c2ecf20Sopenharmony_ci	struct tm6000_core *dev = fh->dev;
10708c2ecf20Sopenharmony_ci	unsigned int n;
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	n = i->index;
10738c2ecf20Sopenharmony_ci	if (n >= 3)
10748c2ecf20Sopenharmony_ci		return -EINVAL;
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	if (!dev->vinput[n].type)
10778c2ecf20Sopenharmony_ci		return -EINVAL;
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	i->index = n;
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	if (dev->vinput[n].type == TM6000_INPUT_TV)
10828c2ecf20Sopenharmony_ci		i->type = V4L2_INPUT_TYPE_TUNER;
10838c2ecf20Sopenharmony_ci	else
10848c2ecf20Sopenharmony_ci		i->type = V4L2_INPUT_TYPE_CAMERA;
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	strscpy(i->name, iname[dev->vinput[n].type], sizeof(i->name));
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	i->std = TM6000_STD;
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	return 0;
10918c2ecf20Sopenharmony_ci}
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_cistatic int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
10948c2ecf20Sopenharmony_ci{
10958c2ecf20Sopenharmony_ci	struct tm6000_fh   *fh = priv;
10968c2ecf20Sopenharmony_ci	struct tm6000_core *dev = fh->dev;
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	*i = dev->input;
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	return 0;
11018c2ecf20Sopenharmony_ci}
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_cistatic int vidioc_s_input(struct file *file, void *priv, unsigned int i)
11048c2ecf20Sopenharmony_ci{
11058c2ecf20Sopenharmony_ci	struct tm6000_fh   *fh = priv;
11068c2ecf20Sopenharmony_ci	struct tm6000_core *dev = fh->dev;
11078c2ecf20Sopenharmony_ci	int rc = 0;
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci	if (i >= 3)
11108c2ecf20Sopenharmony_ci		return -EINVAL;
11118c2ecf20Sopenharmony_ci	if (!dev->vinput[i].type)
11128c2ecf20Sopenharmony_ci		return -EINVAL;
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	dev->input = i;
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	rc = vidioc_s_std(file, priv, dev->norm);
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	return rc;
11198c2ecf20Sopenharmony_ci}
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci/* --- controls ---------------------------------------------- */
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_cistatic int tm6000_s_ctrl(struct v4l2_ctrl *ctrl)
11248c2ecf20Sopenharmony_ci{
11258c2ecf20Sopenharmony_ci	struct tm6000_core *dev = container_of(ctrl->handler, struct tm6000_core, ctrl_handler);
11268c2ecf20Sopenharmony_ci	u8  val = ctrl->val;
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	switch (ctrl->id) {
11298c2ecf20Sopenharmony_ci	case V4L2_CID_CONTRAST:
11308c2ecf20Sopenharmony_ci		tm6000_set_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, val);
11318c2ecf20Sopenharmony_ci		return 0;
11328c2ecf20Sopenharmony_ci	case V4L2_CID_BRIGHTNESS:
11338c2ecf20Sopenharmony_ci		tm6000_set_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, val);
11348c2ecf20Sopenharmony_ci		return 0;
11358c2ecf20Sopenharmony_ci	case V4L2_CID_SATURATION:
11368c2ecf20Sopenharmony_ci		tm6000_set_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, val);
11378c2ecf20Sopenharmony_ci		return 0;
11388c2ecf20Sopenharmony_ci	case V4L2_CID_HUE:
11398c2ecf20Sopenharmony_ci		tm6000_set_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, val);
11408c2ecf20Sopenharmony_ci		return 0;
11418c2ecf20Sopenharmony_ci	}
11428c2ecf20Sopenharmony_ci	return -EINVAL;
11438c2ecf20Sopenharmony_ci}
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops tm6000_ctrl_ops = {
11468c2ecf20Sopenharmony_ci	.s_ctrl = tm6000_s_ctrl,
11478c2ecf20Sopenharmony_ci};
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_cistatic int tm6000_radio_s_ctrl(struct v4l2_ctrl *ctrl)
11508c2ecf20Sopenharmony_ci{
11518c2ecf20Sopenharmony_ci	struct tm6000_core *dev = container_of(ctrl->handler,
11528c2ecf20Sopenharmony_ci			struct tm6000_core, radio_ctrl_handler);
11538c2ecf20Sopenharmony_ci	u8  val = ctrl->val;
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	switch (ctrl->id) {
11568c2ecf20Sopenharmony_ci	case V4L2_CID_AUDIO_MUTE:
11578c2ecf20Sopenharmony_ci		dev->ctl_mute = val;
11588c2ecf20Sopenharmony_ci		tm6000_tvaudio_set_mute(dev, val);
11598c2ecf20Sopenharmony_ci		return 0;
11608c2ecf20Sopenharmony_ci	case V4L2_CID_AUDIO_VOLUME:
11618c2ecf20Sopenharmony_ci		dev->ctl_volume = val;
11628c2ecf20Sopenharmony_ci		tm6000_set_volume(dev, val);
11638c2ecf20Sopenharmony_ci		return 0;
11648c2ecf20Sopenharmony_ci	}
11658c2ecf20Sopenharmony_ci	return -EINVAL;
11668c2ecf20Sopenharmony_ci}
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops tm6000_radio_ctrl_ops = {
11698c2ecf20Sopenharmony_ci	.s_ctrl = tm6000_radio_s_ctrl,
11708c2ecf20Sopenharmony_ci};
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_cistatic int vidioc_g_tuner(struct file *file, void *priv,
11738c2ecf20Sopenharmony_ci				struct v4l2_tuner *t)
11748c2ecf20Sopenharmony_ci{
11758c2ecf20Sopenharmony_ci	struct tm6000_fh   *fh  = priv;
11768c2ecf20Sopenharmony_ci	struct tm6000_core *dev = fh->dev;
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	if (UNSET == dev->tuner_type)
11798c2ecf20Sopenharmony_ci		return -ENOTTY;
11808c2ecf20Sopenharmony_ci	if (0 != t->index)
11818c2ecf20Sopenharmony_ci		return -EINVAL;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	strscpy(t->name, "Television", sizeof(t->name));
11848c2ecf20Sopenharmony_ci	t->type       = V4L2_TUNER_ANALOG_TV;
11858c2ecf20Sopenharmony_ci	t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO;
11868c2ecf20Sopenharmony_ci	t->rangehigh  = 0xffffffffUL;
11878c2ecf20Sopenharmony_ci	t->rxsubchans = V4L2_TUNER_SUB_STEREO;
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci	t->audmode = dev->amode;
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	return 0;
11948c2ecf20Sopenharmony_ci}
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_cistatic int vidioc_s_tuner(struct file *file, void *priv,
11978c2ecf20Sopenharmony_ci				const struct v4l2_tuner *t)
11988c2ecf20Sopenharmony_ci{
11998c2ecf20Sopenharmony_ci	struct tm6000_fh   *fh  = priv;
12008c2ecf20Sopenharmony_ci	struct tm6000_core *dev = fh->dev;
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	if (UNSET == dev->tuner_type)
12038c2ecf20Sopenharmony_ci		return -ENOTTY;
12048c2ecf20Sopenharmony_ci	if (0 != t->index)
12058c2ecf20Sopenharmony_ci		return -EINVAL;
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci	if (t->audmode > V4L2_TUNER_MODE_STEREO)
12088c2ecf20Sopenharmony_ci		dev->amode = V4L2_TUNER_MODE_STEREO;
12098c2ecf20Sopenharmony_ci	else
12108c2ecf20Sopenharmony_ci		dev->amode = t->audmode;
12118c2ecf20Sopenharmony_ci	dprintk(dev, 3, "audio mode: %x\n", t->audmode);
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci	return 0;
12168c2ecf20Sopenharmony_ci}
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_cistatic int vidioc_g_frequency(struct file *file, void *priv,
12198c2ecf20Sopenharmony_ci				struct v4l2_frequency *f)
12208c2ecf20Sopenharmony_ci{
12218c2ecf20Sopenharmony_ci	struct tm6000_fh   *fh  = priv;
12228c2ecf20Sopenharmony_ci	struct tm6000_core *dev = fh->dev;
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	if (UNSET == dev->tuner_type)
12258c2ecf20Sopenharmony_ci		return -ENOTTY;
12268c2ecf20Sopenharmony_ci	if (f->tuner)
12278c2ecf20Sopenharmony_ci		return -EINVAL;
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	f->frequency = dev->freq;
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, f);
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	return 0;
12348c2ecf20Sopenharmony_ci}
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_cistatic int vidioc_s_frequency(struct file *file, void *priv,
12378c2ecf20Sopenharmony_ci				const struct v4l2_frequency *f)
12388c2ecf20Sopenharmony_ci{
12398c2ecf20Sopenharmony_ci	struct tm6000_fh   *fh  = priv;
12408c2ecf20Sopenharmony_ci	struct tm6000_core *dev = fh->dev;
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	if (UNSET == dev->tuner_type)
12438c2ecf20Sopenharmony_ci		return -ENOTTY;
12448c2ecf20Sopenharmony_ci	if (f->tuner != 0)
12458c2ecf20Sopenharmony_ci		return -EINVAL;
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	dev->freq = f->frequency;
12488c2ecf20Sopenharmony_ci	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f);
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	return 0;
12518c2ecf20Sopenharmony_ci}
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_cistatic int radio_g_tuner(struct file *file, void *priv,
12548c2ecf20Sopenharmony_ci					struct v4l2_tuner *t)
12558c2ecf20Sopenharmony_ci{
12568c2ecf20Sopenharmony_ci	struct tm6000_fh *fh = file->private_data;
12578c2ecf20Sopenharmony_ci	struct tm6000_core *dev = fh->dev;
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	if (0 != t->index)
12608c2ecf20Sopenharmony_ci		return -EINVAL;
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	memset(t, 0, sizeof(*t));
12638c2ecf20Sopenharmony_ci	strscpy(t->name, "Radio", sizeof(t->name));
12648c2ecf20Sopenharmony_ci	t->type = V4L2_TUNER_RADIO;
12658c2ecf20Sopenharmony_ci	t->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
12668c2ecf20Sopenharmony_ci	t->rxsubchans = V4L2_TUNER_SUB_STEREO;
12678c2ecf20Sopenharmony_ci	t->audmode = V4L2_TUNER_MODE_STEREO;
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	return 0;
12728c2ecf20Sopenharmony_ci}
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_cistatic int radio_s_tuner(struct file *file, void *priv,
12758c2ecf20Sopenharmony_ci					const struct v4l2_tuner *t)
12768c2ecf20Sopenharmony_ci{
12778c2ecf20Sopenharmony_ci	struct tm6000_fh *fh = file->private_data;
12788c2ecf20Sopenharmony_ci	struct tm6000_core *dev = fh->dev;
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	if (0 != t->index)
12818c2ecf20Sopenharmony_ci		return -EINVAL;
12828c2ecf20Sopenharmony_ci	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
12838c2ecf20Sopenharmony_ci	return 0;
12848c2ecf20Sopenharmony_ci}
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------
12878c2ecf20Sopenharmony_ci	File operations for the device
12888c2ecf20Sopenharmony_ci   ------------------------------------------------------------------*/
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_cistatic int __tm6000_open(struct file *file)
12918c2ecf20Sopenharmony_ci{
12928c2ecf20Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
12938c2ecf20Sopenharmony_ci	struct tm6000_core *dev = video_drvdata(file);
12948c2ecf20Sopenharmony_ci	struct tm6000_fh *fh;
12958c2ecf20Sopenharmony_ci	enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
12968c2ecf20Sopenharmony_ci	int rc;
12978c2ecf20Sopenharmony_ci	int radio = 0;
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: open called (dev=%s)\n",
13008c2ecf20Sopenharmony_ci		video_device_node_name(vdev));
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci	switch (vdev->vfl_type) {
13038c2ecf20Sopenharmony_ci	case VFL_TYPE_VIDEO:
13048c2ecf20Sopenharmony_ci		type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
13058c2ecf20Sopenharmony_ci		break;
13068c2ecf20Sopenharmony_ci	case VFL_TYPE_VBI:
13078c2ecf20Sopenharmony_ci		type = V4L2_BUF_TYPE_VBI_CAPTURE;
13088c2ecf20Sopenharmony_ci		break;
13098c2ecf20Sopenharmony_ci	case VFL_TYPE_RADIO:
13108c2ecf20Sopenharmony_ci		radio = 1;
13118c2ecf20Sopenharmony_ci		break;
13128c2ecf20Sopenharmony_ci	default:
13138c2ecf20Sopenharmony_ci		return -EINVAL;
13148c2ecf20Sopenharmony_ci	}
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci	/* If more than one user, mutex should be added */
13178c2ecf20Sopenharmony_ci	dev->users++;
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci	dprintk(dev, V4L2_DEBUG_OPEN, "open dev=%s type=%s users=%d\n",
13208c2ecf20Sopenharmony_ci		video_device_node_name(vdev), v4l2_type_names[type],
13218c2ecf20Sopenharmony_ci		dev->users);
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	/* allocate + initialize per filehandle data */
13248c2ecf20Sopenharmony_ci	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
13258c2ecf20Sopenharmony_ci	if (NULL == fh) {
13268c2ecf20Sopenharmony_ci		dev->users--;
13278c2ecf20Sopenharmony_ci		return -ENOMEM;
13288c2ecf20Sopenharmony_ci	}
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci	v4l2_fh_init(&fh->fh, vdev);
13318c2ecf20Sopenharmony_ci	file->private_data = fh;
13328c2ecf20Sopenharmony_ci	fh->dev      = dev;
13338c2ecf20Sopenharmony_ci	fh->radio    = radio;
13348c2ecf20Sopenharmony_ci	dev->radio   = radio;
13358c2ecf20Sopenharmony_ci	fh->type     = type;
13368c2ecf20Sopenharmony_ci	dev->fourcc  = format[0].fourcc;
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci	fh->fmt      = format_by_fourcc(dev->fourcc);
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	tm6000_get_std_res(dev);
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci	fh->width = dev->width;
13438c2ecf20Sopenharmony_ci	fh->height = dev->height;
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	dprintk(dev, V4L2_DEBUG_OPEN, "Open: fh=%p, dev=%p, dev->vidq=%p\n",
13468c2ecf20Sopenharmony_ci		fh, dev, &dev->vidq);
13478c2ecf20Sopenharmony_ci	dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty queued=%d\n",
13488c2ecf20Sopenharmony_ci		list_empty(&dev->vidq.queued));
13498c2ecf20Sopenharmony_ci	dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty active=%d\n",
13508c2ecf20Sopenharmony_ci		list_empty(&dev->vidq.active));
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	/* initialize hardware on analog mode */
13538c2ecf20Sopenharmony_ci	rc = tm6000_init_analog_mode(dev);
13548c2ecf20Sopenharmony_ci	if (rc < 0) {
13558c2ecf20Sopenharmony_ci		v4l2_fh_exit(&fh->fh);
13568c2ecf20Sopenharmony_ci		kfree(fh);
13578c2ecf20Sopenharmony_ci		return rc;
13588c2ecf20Sopenharmony_ci	}
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci	dev->mode = TM6000_MODE_ANALOG;
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci	if (!fh->radio) {
13638c2ecf20Sopenharmony_ci		videobuf_queue_vmalloc_init(&fh->vb_vidq, &tm6000_video_qops,
13648c2ecf20Sopenharmony_ci				NULL, &dev->slock,
13658c2ecf20Sopenharmony_ci				fh->type,
13668c2ecf20Sopenharmony_ci				V4L2_FIELD_INTERLACED,
13678c2ecf20Sopenharmony_ci				sizeof(struct tm6000_buffer), fh, &dev->lock);
13688c2ecf20Sopenharmony_ci	} else {
13698c2ecf20Sopenharmony_ci		dprintk(dev, V4L2_DEBUG_OPEN, "video_open: setting radio device\n");
13708c2ecf20Sopenharmony_ci		tm6000_set_audio_rinput(dev);
13718c2ecf20Sopenharmony_ci		v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio);
13728c2ecf20Sopenharmony_ci		tm6000_prepare_isoc(dev);
13738c2ecf20Sopenharmony_ci		tm6000_start_thread(dev);
13748c2ecf20Sopenharmony_ci	}
13758c2ecf20Sopenharmony_ci	v4l2_fh_add(&fh->fh);
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci	return 0;
13788c2ecf20Sopenharmony_ci}
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_cistatic int tm6000_open(struct file *file)
13818c2ecf20Sopenharmony_ci{
13828c2ecf20Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
13838c2ecf20Sopenharmony_ci	int res;
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	mutex_lock(vdev->lock);
13868c2ecf20Sopenharmony_ci	res = __tm6000_open(file);
13878c2ecf20Sopenharmony_ci	mutex_unlock(vdev->lock);
13888c2ecf20Sopenharmony_ci	return res;
13898c2ecf20Sopenharmony_ci}
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_cistatic ssize_t
13928c2ecf20Sopenharmony_citm6000_read(struct file *file, char __user *data, size_t count, loff_t *pos)
13938c2ecf20Sopenharmony_ci{
13948c2ecf20Sopenharmony_ci	struct tm6000_fh *fh = file->private_data;
13958c2ecf20Sopenharmony_ci	struct tm6000_core *dev = fh->dev;
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
13988c2ecf20Sopenharmony_ci		int res;
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci		if (!res_get(fh->dev, fh, true))
14018c2ecf20Sopenharmony_ci			return -EBUSY;
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci		if (mutex_lock_interruptible(&dev->lock))
14048c2ecf20Sopenharmony_ci			return -ERESTARTSYS;
14058c2ecf20Sopenharmony_ci		res = videobuf_read_stream(&fh->vb_vidq, data, count, pos, 0,
14068c2ecf20Sopenharmony_ci					file->f_flags & O_NONBLOCK);
14078c2ecf20Sopenharmony_ci		mutex_unlock(&dev->lock);
14088c2ecf20Sopenharmony_ci		return res;
14098c2ecf20Sopenharmony_ci	}
14108c2ecf20Sopenharmony_ci	return 0;
14118c2ecf20Sopenharmony_ci}
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_cistatic __poll_t
14148c2ecf20Sopenharmony_ci__tm6000_poll(struct file *file, struct poll_table_struct *wait)
14158c2ecf20Sopenharmony_ci{
14168c2ecf20Sopenharmony_ci	__poll_t req_events = poll_requested_events(wait);
14178c2ecf20Sopenharmony_ci	struct tm6000_fh        *fh = file->private_data;
14188c2ecf20Sopenharmony_ci	struct tm6000_buffer    *buf;
14198c2ecf20Sopenharmony_ci	__poll_t res = 0;
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	if (v4l2_event_pending(&fh->fh))
14228c2ecf20Sopenharmony_ci		res = EPOLLPRI;
14238c2ecf20Sopenharmony_ci	else if (req_events & EPOLLPRI)
14248c2ecf20Sopenharmony_ci		poll_wait(file, &fh->fh.wait, wait);
14258c2ecf20Sopenharmony_ci	if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
14268c2ecf20Sopenharmony_ci		return res | EPOLLERR;
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci	if (!!is_res_streaming(fh->dev, fh))
14298c2ecf20Sopenharmony_ci		return res | EPOLLERR;
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci	if (!is_res_read(fh->dev, fh)) {
14328c2ecf20Sopenharmony_ci		/* streaming capture */
14338c2ecf20Sopenharmony_ci		if (list_empty(&fh->vb_vidq.stream))
14348c2ecf20Sopenharmony_ci			return res | EPOLLERR;
14358c2ecf20Sopenharmony_ci		buf = list_entry(fh->vb_vidq.stream.next, struct tm6000_buffer, vb.stream);
14368c2ecf20Sopenharmony_ci		poll_wait(file, &buf->vb.done, wait);
14378c2ecf20Sopenharmony_ci		if (buf->vb.state == VIDEOBUF_DONE ||
14388c2ecf20Sopenharmony_ci		    buf->vb.state == VIDEOBUF_ERROR)
14398c2ecf20Sopenharmony_ci			return res | EPOLLIN | EPOLLRDNORM;
14408c2ecf20Sopenharmony_ci	} else if (req_events & (EPOLLIN | EPOLLRDNORM)) {
14418c2ecf20Sopenharmony_ci		/* read() capture */
14428c2ecf20Sopenharmony_ci		return res | videobuf_poll_stream(file, &fh->vb_vidq, wait);
14438c2ecf20Sopenharmony_ci	}
14448c2ecf20Sopenharmony_ci	return res;
14458c2ecf20Sopenharmony_ci}
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_cistatic __poll_t tm6000_poll(struct file *file, struct poll_table_struct *wait)
14488c2ecf20Sopenharmony_ci{
14498c2ecf20Sopenharmony_ci	struct tm6000_fh *fh = file->private_data;
14508c2ecf20Sopenharmony_ci	struct tm6000_core *dev = fh->dev;
14518c2ecf20Sopenharmony_ci	__poll_t res;
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	mutex_lock(&dev->lock);
14548c2ecf20Sopenharmony_ci	res = __tm6000_poll(file, wait);
14558c2ecf20Sopenharmony_ci	mutex_unlock(&dev->lock);
14568c2ecf20Sopenharmony_ci	return res;
14578c2ecf20Sopenharmony_ci}
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_cistatic int tm6000_release(struct file *file)
14608c2ecf20Sopenharmony_ci{
14618c2ecf20Sopenharmony_ci	struct tm6000_fh         *fh = file->private_data;
14628c2ecf20Sopenharmony_ci	struct tm6000_core      *dev = fh->dev;
14638c2ecf20Sopenharmony_ci	struct video_device    *vdev = video_devdata(file);
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci	dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: close called (dev=%s, users=%d)\n",
14668c2ecf20Sopenharmony_ci		video_device_node_name(vdev), dev->users);
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci	mutex_lock(&dev->lock);
14698c2ecf20Sopenharmony_ci	dev->users--;
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	res_free(dev, fh);
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci	if (!dev->users) {
14748c2ecf20Sopenharmony_ci		tm6000_uninit_isoc(dev);
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci		/* Stop interrupt USB pipe */
14778c2ecf20Sopenharmony_ci		tm6000_ir_int_stop(dev);
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci		usb_reset_configuration(dev->udev);
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci		if (dev->int_in.endp)
14828c2ecf20Sopenharmony_ci			usb_set_interface(dev->udev,
14838c2ecf20Sopenharmony_ci					dev->isoc_in.bInterfaceNumber, 2);
14848c2ecf20Sopenharmony_ci		else
14858c2ecf20Sopenharmony_ci			usb_set_interface(dev->udev,
14868c2ecf20Sopenharmony_ci					dev->isoc_in.bInterfaceNumber, 0);
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci		/* Start interrupt USB pipe */
14898c2ecf20Sopenharmony_ci		tm6000_ir_int_start(dev);
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_ci		if (!fh->radio)
14928c2ecf20Sopenharmony_ci			videobuf_mmap_free(&fh->vb_vidq);
14938c2ecf20Sopenharmony_ci	}
14948c2ecf20Sopenharmony_ci	v4l2_fh_del(&fh->fh);
14958c2ecf20Sopenharmony_ci	v4l2_fh_exit(&fh->fh);
14968c2ecf20Sopenharmony_ci	kfree(fh);
14978c2ecf20Sopenharmony_ci	mutex_unlock(&dev->lock);
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci	return 0;
15008c2ecf20Sopenharmony_ci}
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_cistatic int tm6000_mmap(struct file *file, struct vm_area_struct * vma)
15038c2ecf20Sopenharmony_ci{
15048c2ecf20Sopenharmony_ci	struct tm6000_fh *fh = file->private_data;
15058c2ecf20Sopenharmony_ci	struct tm6000_core *dev = fh->dev;
15068c2ecf20Sopenharmony_ci	int res;
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&dev->lock))
15098c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
15108c2ecf20Sopenharmony_ci	res = videobuf_mmap_mapper(&fh->vb_vidq, vma);
15118c2ecf20Sopenharmony_ci	mutex_unlock(&dev->lock);
15128c2ecf20Sopenharmony_ci	return res;
15138c2ecf20Sopenharmony_ci}
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations tm6000_fops = {
15168c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
15178c2ecf20Sopenharmony_ci	.open = tm6000_open,
15188c2ecf20Sopenharmony_ci	.release = tm6000_release,
15198c2ecf20Sopenharmony_ci	.unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
15208c2ecf20Sopenharmony_ci	.read = tm6000_read,
15218c2ecf20Sopenharmony_ci	.poll = tm6000_poll,
15228c2ecf20Sopenharmony_ci	.mmap = tm6000_mmap,
15238c2ecf20Sopenharmony_ci};
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops video_ioctl_ops = {
15268c2ecf20Sopenharmony_ci	.vidioc_querycap          = vidioc_querycap,
15278c2ecf20Sopenharmony_ci	.vidioc_enum_fmt_vid_cap  = vidioc_enum_fmt_vid_cap,
15288c2ecf20Sopenharmony_ci	.vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,
15298c2ecf20Sopenharmony_ci	.vidioc_try_fmt_vid_cap   = vidioc_try_fmt_vid_cap,
15308c2ecf20Sopenharmony_ci	.vidioc_s_fmt_vid_cap     = vidioc_s_fmt_vid_cap,
15318c2ecf20Sopenharmony_ci	.vidioc_s_std             = vidioc_s_std,
15328c2ecf20Sopenharmony_ci	.vidioc_g_std             = vidioc_g_std,
15338c2ecf20Sopenharmony_ci	.vidioc_enum_input        = vidioc_enum_input,
15348c2ecf20Sopenharmony_ci	.vidioc_g_input           = vidioc_g_input,
15358c2ecf20Sopenharmony_ci	.vidioc_s_input           = vidioc_s_input,
15368c2ecf20Sopenharmony_ci	.vidioc_g_tuner           = vidioc_g_tuner,
15378c2ecf20Sopenharmony_ci	.vidioc_s_tuner           = vidioc_s_tuner,
15388c2ecf20Sopenharmony_ci	.vidioc_g_frequency       = vidioc_g_frequency,
15398c2ecf20Sopenharmony_ci	.vidioc_s_frequency       = vidioc_s_frequency,
15408c2ecf20Sopenharmony_ci	.vidioc_streamon          = vidioc_streamon,
15418c2ecf20Sopenharmony_ci	.vidioc_streamoff         = vidioc_streamoff,
15428c2ecf20Sopenharmony_ci	.vidioc_reqbufs           = vidioc_reqbufs,
15438c2ecf20Sopenharmony_ci	.vidioc_querybuf          = vidioc_querybuf,
15448c2ecf20Sopenharmony_ci	.vidioc_qbuf              = vidioc_qbuf,
15458c2ecf20Sopenharmony_ci	.vidioc_dqbuf             = vidioc_dqbuf,
15468c2ecf20Sopenharmony_ci	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
15478c2ecf20Sopenharmony_ci	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
15488c2ecf20Sopenharmony_ci};
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_cistatic struct video_device tm6000_template = {
15518c2ecf20Sopenharmony_ci	.name		= "tm6000",
15528c2ecf20Sopenharmony_ci	.fops           = &tm6000_fops,
15538c2ecf20Sopenharmony_ci	.ioctl_ops      = &video_ioctl_ops,
15548c2ecf20Sopenharmony_ci	.release	= video_device_release_empty,
15558c2ecf20Sopenharmony_ci	.tvnorms        = TM6000_STD,
15568c2ecf20Sopenharmony_ci};
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations radio_fops = {
15598c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
15608c2ecf20Sopenharmony_ci	.open		= tm6000_open,
15618c2ecf20Sopenharmony_ci	.poll		= v4l2_ctrl_poll,
15628c2ecf20Sopenharmony_ci	.release	= tm6000_release,
15638c2ecf20Sopenharmony_ci	.unlocked_ioctl	= video_ioctl2,
15648c2ecf20Sopenharmony_ci};
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops radio_ioctl_ops = {
15678c2ecf20Sopenharmony_ci	.vidioc_querycap	= vidioc_querycap,
15688c2ecf20Sopenharmony_ci	.vidioc_g_tuner		= radio_g_tuner,
15698c2ecf20Sopenharmony_ci	.vidioc_s_tuner		= radio_s_tuner,
15708c2ecf20Sopenharmony_ci	.vidioc_g_frequency	= vidioc_g_frequency,
15718c2ecf20Sopenharmony_ci	.vidioc_s_frequency	= vidioc_s_frequency,
15728c2ecf20Sopenharmony_ci	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
15738c2ecf20Sopenharmony_ci	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
15748c2ecf20Sopenharmony_ci};
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_cistatic struct video_device tm6000_radio_template = {
15778c2ecf20Sopenharmony_ci	.name			= "tm6000",
15788c2ecf20Sopenharmony_ci	.fops			= &radio_fops,
15798c2ecf20Sopenharmony_ci	.ioctl_ops		= &radio_ioctl_ops,
15808c2ecf20Sopenharmony_ci};
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------
15838c2ecf20Sopenharmony_ci *	Initialization and module stuff
15848c2ecf20Sopenharmony_ci * ------------------------------------------------------------------
15858c2ecf20Sopenharmony_ci */
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_cistatic void vdev_init(struct tm6000_core *dev,
15888c2ecf20Sopenharmony_ci		struct video_device *vfd,
15898c2ecf20Sopenharmony_ci		const struct video_device
15908c2ecf20Sopenharmony_ci		*template, const char *type_name)
15918c2ecf20Sopenharmony_ci{
15928c2ecf20Sopenharmony_ci	*vfd = *template;
15938c2ecf20Sopenharmony_ci	vfd->v4l2_dev = &dev->v4l2_dev;
15948c2ecf20Sopenharmony_ci	vfd->release = video_device_release_empty;
15958c2ecf20Sopenharmony_ci	vfd->lock = &dev->lock;
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci	snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name);
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci	video_set_drvdata(vfd, dev);
16008c2ecf20Sopenharmony_ci}
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_ciint tm6000_v4l2_register(struct tm6000_core *dev)
16038c2ecf20Sopenharmony_ci{
16048c2ecf20Sopenharmony_ci	int ret = 0;
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(&dev->ctrl_handler, 6);
16078c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(&dev->radio_ctrl_handler, 2);
16088c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(&dev->radio_ctrl_handler, &tm6000_radio_ctrl_ops,
16098c2ecf20Sopenharmony_ci			V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
16108c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(&dev->radio_ctrl_handler, &tm6000_radio_ctrl_ops,
16118c2ecf20Sopenharmony_ci			V4L2_CID_AUDIO_VOLUME, -15, 15, 1, 0);
16128c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops,
16138c2ecf20Sopenharmony_ci			V4L2_CID_BRIGHTNESS, 0, 255, 1, 54);
16148c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops,
16158c2ecf20Sopenharmony_ci			V4L2_CID_CONTRAST, 0, 255, 1, 119);
16168c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops,
16178c2ecf20Sopenharmony_ci			V4L2_CID_SATURATION, 0, 255, 1, 112);
16188c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops,
16198c2ecf20Sopenharmony_ci			V4L2_CID_HUE, -128, 127, 1, 0);
16208c2ecf20Sopenharmony_ci	v4l2_ctrl_add_handler(&dev->ctrl_handler,
16218c2ecf20Sopenharmony_ci			&dev->radio_ctrl_handler, NULL, false);
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci	if (dev->radio_ctrl_handler.error)
16248c2ecf20Sopenharmony_ci		ret = dev->radio_ctrl_handler.error;
16258c2ecf20Sopenharmony_ci	if (!ret && dev->ctrl_handler.error)
16268c2ecf20Sopenharmony_ci		ret = dev->ctrl_handler.error;
16278c2ecf20Sopenharmony_ci	if (ret)
16288c2ecf20Sopenharmony_ci		goto free_ctrl;
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_ci	vdev_init(dev, &dev->vfd, &tm6000_template, "video");
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_ci	dev->vfd.ctrl_handler = &dev->ctrl_handler;
16338c2ecf20Sopenharmony_ci	dev->vfd.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
16348c2ecf20Sopenharmony_ci			       V4L2_CAP_READWRITE;
16358c2ecf20Sopenharmony_ci	if (dev->tuner_type != TUNER_ABSENT)
16368c2ecf20Sopenharmony_ci		dev->vfd.device_caps |= V4L2_CAP_TUNER;
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_ci	/* init video dma queues */
16398c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&dev->vidq.active);
16408c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&dev->vidq.queued);
16418c2ecf20Sopenharmony_ci
16428c2ecf20Sopenharmony_ci	ret = video_register_device(&dev->vfd, VFL_TYPE_VIDEO, video_nr);
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci	if (ret < 0) {
16458c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: can't register video device\n",
16468c2ecf20Sopenharmony_ci		       dev->name);
16478c2ecf20Sopenharmony_ci		goto free_ctrl;
16488c2ecf20Sopenharmony_ci	}
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci	printk(KERN_INFO "%s: registered device %s\n",
16518c2ecf20Sopenharmony_ci	       dev->name, video_device_node_name(&dev->vfd));
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_ci	if (dev->caps.has_radio) {
16548c2ecf20Sopenharmony_ci		vdev_init(dev, &dev->radio_dev, &tm6000_radio_template,
16558c2ecf20Sopenharmony_ci							   "radio");
16568c2ecf20Sopenharmony_ci		dev->radio_dev.ctrl_handler = &dev->radio_ctrl_handler;
16578c2ecf20Sopenharmony_ci		dev->radio_dev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER;
16588c2ecf20Sopenharmony_ci		ret = video_register_device(&dev->radio_dev, VFL_TYPE_RADIO,
16598c2ecf20Sopenharmony_ci					    radio_nr);
16608c2ecf20Sopenharmony_ci		if (ret < 0) {
16618c2ecf20Sopenharmony_ci			printk(KERN_INFO "%s: can't register radio device\n",
16628c2ecf20Sopenharmony_ci			       dev->name);
16638c2ecf20Sopenharmony_ci			goto unreg_video;
16648c2ecf20Sopenharmony_ci		}
16658c2ecf20Sopenharmony_ci
16668c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: registered device %s\n",
16678c2ecf20Sopenharmony_ci		       dev->name, video_device_node_name(&dev->radio_dev));
16688c2ecf20Sopenharmony_ci	}
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_ci	printk(KERN_INFO "Trident TVMaster TM5600/TM6000/TM6010 USB2 board (Load status: %d)\n", ret);
16718c2ecf20Sopenharmony_ci	return ret;
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ciunreg_video:
16748c2ecf20Sopenharmony_ci	video_unregister_device(&dev->vfd);
16758c2ecf20Sopenharmony_cifree_ctrl:
16768c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(&dev->ctrl_handler);
16778c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(&dev->radio_ctrl_handler);
16788c2ecf20Sopenharmony_ci	return ret;
16798c2ecf20Sopenharmony_ci}
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ciint tm6000_v4l2_unregister(struct tm6000_core *dev)
16828c2ecf20Sopenharmony_ci{
16838c2ecf20Sopenharmony_ci	video_unregister_device(&dev->vfd);
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_ci	/* if URB buffers are still allocated free them now */
16868c2ecf20Sopenharmony_ci	tm6000_free_urb_buffers(dev);
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_ci	video_unregister_device(&dev->radio_dev);
16898c2ecf20Sopenharmony_ci	return 0;
16908c2ecf20Sopenharmony_ci}
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_ciint tm6000_v4l2_exit(void)
16938c2ecf20Sopenharmony_ci{
16948c2ecf20Sopenharmony_ci	return 0;
16958c2ecf20Sopenharmony_ci}
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_cimodule_param(video_nr, int, 0);
16988c2ecf20Sopenharmony_ciMODULE_PARM_DESC(video_nr, "Allow changing video device number");
16998c2ecf20Sopenharmony_ci
17008c2ecf20Sopenharmony_cimodule_param_named(debug, tm6000_debug, int, 0444);
17018c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "activates debug info");
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_cimodule_param(vid_limit, int, 0644);
17048c2ecf20Sopenharmony_ciMODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_cimodule_param(keep_urb, bool, 0);
17078c2ecf20Sopenharmony_ciMODULE_PARM_DESC(keep_urb, "Keep urb buffers allocated even when the device is closed by the user");
1708