162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Auvitek AU0828 USB Bridge (Analog video support)
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2009 Devin Heitmueller <dheitmueller@linuxtv.org>
662306a36Sopenharmony_ci * Copyright (C) 2005-2008 Auvitek International, Ltd.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci/* Developer Notes:
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * The hardware scaler supported is unimplemented
1262306a36Sopenharmony_ci * AC97 audio support is unimplemented (only i2s audio mode)
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "au0828.h"
1762306a36Sopenharmony_ci#include "au8522.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci#include <linux/init.h>
2262306a36Sopenharmony_ci#include <linux/device.h>
2362306a36Sopenharmony_ci#include <media/v4l2-common.h>
2462306a36Sopenharmony_ci#include <media/v4l2-mc.h>
2562306a36Sopenharmony_ci#include <media/v4l2-ioctl.h>
2662306a36Sopenharmony_ci#include <media/v4l2-event.h>
2762306a36Sopenharmony_ci#include <media/tuner.h>
2862306a36Sopenharmony_ci#include "au0828-reg.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic DEFINE_MUTEX(au0828_sysfs_lock);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/* ------------------------------------------------------------------
3362306a36Sopenharmony_ci	Videobuf operations
3462306a36Sopenharmony_ci   ------------------------------------------------------------------*/
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic unsigned int isoc_debug;
3762306a36Sopenharmony_cimodule_param(isoc_debug, int, 0644);
3862306a36Sopenharmony_ciMODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]");
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define au0828_isocdbg(fmt, arg...) \
4162306a36Sopenharmony_cido {\
4262306a36Sopenharmony_ci	if (isoc_debug) { \
4362306a36Sopenharmony_ci		pr_info("au0828 %s :"fmt, \
4462306a36Sopenharmony_ci		       __func__ , ##arg);	   \
4562306a36Sopenharmony_ci	} \
4662306a36Sopenharmony_ci  } while (0)
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic inline void i2c_gate_ctrl(struct au0828_dev *dev, int val)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl)
5162306a36Sopenharmony_ci		dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, val);
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic inline void print_err_status(struct au0828_dev *dev,
5562306a36Sopenharmony_ci				    int packet, int status)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	char *errmsg = "Unknown";
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	switch (status) {
6062306a36Sopenharmony_ci	case -ENOENT:
6162306a36Sopenharmony_ci		errmsg = "unlinked synchronously";
6262306a36Sopenharmony_ci		break;
6362306a36Sopenharmony_ci	case -ECONNRESET:
6462306a36Sopenharmony_ci		errmsg = "unlinked asynchronously";
6562306a36Sopenharmony_ci		break;
6662306a36Sopenharmony_ci	case -ENOSR:
6762306a36Sopenharmony_ci		errmsg = "Buffer error (overrun)";
6862306a36Sopenharmony_ci		break;
6962306a36Sopenharmony_ci	case -EPIPE:
7062306a36Sopenharmony_ci		errmsg = "Stalled (device not responding)";
7162306a36Sopenharmony_ci		break;
7262306a36Sopenharmony_ci	case -EOVERFLOW:
7362306a36Sopenharmony_ci		errmsg = "Babble (bad cable?)";
7462306a36Sopenharmony_ci		break;
7562306a36Sopenharmony_ci	case -EPROTO:
7662306a36Sopenharmony_ci		errmsg = "Bit-stuff error (bad cable?)";
7762306a36Sopenharmony_ci		break;
7862306a36Sopenharmony_ci	case -EILSEQ:
7962306a36Sopenharmony_ci		errmsg = "CRC/Timeout (could be anything)";
8062306a36Sopenharmony_ci		break;
8162306a36Sopenharmony_ci	case -ETIME:
8262306a36Sopenharmony_ci		errmsg = "Device does not respond";
8362306a36Sopenharmony_ci		break;
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci	if (packet < 0) {
8662306a36Sopenharmony_ci		au0828_isocdbg("URB status %d [%s].\n",	status, errmsg);
8762306a36Sopenharmony_ci	} else {
8862306a36Sopenharmony_ci		au0828_isocdbg("URB packet %d, status %d [%s].\n",
8962306a36Sopenharmony_ci			       packet, status, errmsg);
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int check_dev(struct au0828_dev *dev)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	if (test_bit(DEV_DISCONNECTED, &dev->dev_state)) {
9662306a36Sopenharmony_ci		pr_info("v4l2 ioctl: device not present\n");
9762306a36Sopenharmony_ci		return -ENODEV;
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if (test_bit(DEV_MISCONFIGURED, &dev->dev_state)) {
10162306a36Sopenharmony_ci		pr_info("v4l2 ioctl: device is misconfigured; close and open it again\n");
10262306a36Sopenharmony_ci		return -EIO;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci	return 0;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/*
10862306a36Sopenharmony_ci * IRQ callback, called by URB callback
10962306a36Sopenharmony_ci */
11062306a36Sopenharmony_cistatic void au0828_irq_callback(struct urb *urb)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	struct au0828_dmaqueue  *dma_q = urb->context;
11362306a36Sopenharmony_ci	struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq);
11462306a36Sopenharmony_ci	unsigned long flags = 0;
11562306a36Sopenharmony_ci	int i;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	switch (urb->status) {
11862306a36Sopenharmony_ci	case 0:             /* success */
11962306a36Sopenharmony_ci	case -ETIMEDOUT:    /* NAK */
12062306a36Sopenharmony_ci		break;
12162306a36Sopenharmony_ci	case -ECONNRESET:   /* kill */
12262306a36Sopenharmony_ci	case -ENOENT:
12362306a36Sopenharmony_ci	case -ESHUTDOWN:
12462306a36Sopenharmony_ci		au0828_isocdbg("au0828_irq_callback called: status kill\n");
12562306a36Sopenharmony_ci		return;
12662306a36Sopenharmony_ci	default:            /* unknown error */
12762306a36Sopenharmony_ci		au0828_isocdbg("urb completion error %d.\n", urb->status);
12862306a36Sopenharmony_ci		break;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* Copy data from URB */
13262306a36Sopenharmony_ci	spin_lock_irqsave(&dev->slock, flags);
13362306a36Sopenharmony_ci	dev->isoc_ctl.isoc_copy(dev, urb);
13462306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->slock, flags);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/* Reset urb buffers */
13762306a36Sopenharmony_ci	for (i = 0; i < urb->number_of_packets; i++) {
13862306a36Sopenharmony_ci		urb->iso_frame_desc[i].status = 0;
13962306a36Sopenharmony_ci		urb->iso_frame_desc[i].actual_length = 0;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci	urb->status = 0;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	urb->status = usb_submit_urb(urb, GFP_ATOMIC);
14462306a36Sopenharmony_ci	if (urb->status) {
14562306a36Sopenharmony_ci		au0828_isocdbg("urb resubmit failed (error=%i)\n",
14662306a36Sopenharmony_ci			       urb->status);
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci	dev->stream_state = STREAM_ON;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci/*
15262306a36Sopenharmony_ci * Stop and Deallocate URBs
15362306a36Sopenharmony_ci */
15462306a36Sopenharmony_cistatic void au0828_uninit_isoc(struct au0828_dev *dev)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	struct urb *urb;
15762306a36Sopenharmony_ci	int i;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	au0828_isocdbg("au0828: called au0828_uninit_isoc\n");
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	dev->isoc_ctl.nfields = -1;
16262306a36Sopenharmony_ci	for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
16362306a36Sopenharmony_ci		urb = dev->isoc_ctl.urb[i];
16462306a36Sopenharmony_ci		if (urb) {
16562306a36Sopenharmony_ci			if (!irqs_disabled())
16662306a36Sopenharmony_ci				usb_kill_urb(urb);
16762306a36Sopenharmony_ci			else
16862306a36Sopenharmony_ci				usb_unlink_urb(urb);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci			if (dev->isoc_ctl.transfer_buffer[i]) {
17162306a36Sopenharmony_ci				usb_free_coherent(dev->usbdev,
17262306a36Sopenharmony_ci					urb->transfer_buffer_length,
17362306a36Sopenharmony_ci					dev->isoc_ctl.transfer_buffer[i],
17462306a36Sopenharmony_ci					urb->transfer_dma);
17562306a36Sopenharmony_ci			}
17662306a36Sopenharmony_ci			usb_free_urb(urb);
17762306a36Sopenharmony_ci			dev->isoc_ctl.urb[i] = NULL;
17862306a36Sopenharmony_ci		}
17962306a36Sopenharmony_ci		dev->isoc_ctl.transfer_buffer[i] = NULL;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	kfree(dev->isoc_ctl.urb);
18362306a36Sopenharmony_ci	kfree(dev->isoc_ctl.transfer_buffer);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	dev->isoc_ctl.urb = NULL;
18662306a36Sopenharmony_ci	dev->isoc_ctl.transfer_buffer = NULL;
18762306a36Sopenharmony_ci	dev->isoc_ctl.num_bufs = 0;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	dev->stream_state = STREAM_OFF;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci/*
19362306a36Sopenharmony_ci * Allocate URBs and start IRQ
19462306a36Sopenharmony_ci */
19562306a36Sopenharmony_cistatic int au0828_init_isoc(struct au0828_dev *dev, int max_packets,
19662306a36Sopenharmony_ci			    int num_bufs, int max_pkt_size,
19762306a36Sopenharmony_ci			    int (*isoc_copy) (struct au0828_dev *dev, struct urb *urb))
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct au0828_dmaqueue *dma_q = &dev->vidq;
20062306a36Sopenharmony_ci	int i;
20162306a36Sopenharmony_ci	int sb_size, pipe;
20262306a36Sopenharmony_ci	struct urb *urb;
20362306a36Sopenharmony_ci	int j, k;
20462306a36Sopenharmony_ci	int rc;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	au0828_isocdbg("au0828: called au0828_prepare_isoc\n");
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	dev->isoc_ctl.isoc_copy = isoc_copy;
20962306a36Sopenharmony_ci	dev->isoc_ctl.num_bufs = num_bufs;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	dev->isoc_ctl.urb = kcalloc(num_bufs, sizeof(void *),  GFP_KERNEL);
21262306a36Sopenharmony_ci	if (!dev->isoc_ctl.urb) {
21362306a36Sopenharmony_ci		au0828_isocdbg("cannot alloc memory for usb buffers\n");
21462306a36Sopenharmony_ci		return -ENOMEM;
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	dev->isoc_ctl.transfer_buffer = kcalloc(num_bufs, sizeof(void *),
21862306a36Sopenharmony_ci						GFP_KERNEL);
21962306a36Sopenharmony_ci	if (!dev->isoc_ctl.transfer_buffer) {
22062306a36Sopenharmony_ci		au0828_isocdbg("cannot allocate memory for usb transfer\n");
22162306a36Sopenharmony_ci		kfree(dev->isoc_ctl.urb);
22262306a36Sopenharmony_ci		return -ENOMEM;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	dev->isoc_ctl.max_pkt_size = max_pkt_size;
22662306a36Sopenharmony_ci	dev->isoc_ctl.buf = NULL;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	sb_size = max_packets * dev->isoc_ctl.max_pkt_size;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	/* allocate urbs and transfer buffers */
23162306a36Sopenharmony_ci	for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
23262306a36Sopenharmony_ci		urb = usb_alloc_urb(max_packets, GFP_KERNEL);
23362306a36Sopenharmony_ci		if (!urb) {
23462306a36Sopenharmony_ci			au0828_isocdbg("cannot allocate URB\n");
23562306a36Sopenharmony_ci			au0828_uninit_isoc(dev);
23662306a36Sopenharmony_ci			return -ENOMEM;
23762306a36Sopenharmony_ci		}
23862306a36Sopenharmony_ci		dev->isoc_ctl.urb[i] = urb;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci		dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->usbdev,
24162306a36Sopenharmony_ci			sb_size, GFP_KERNEL, &urb->transfer_dma);
24262306a36Sopenharmony_ci		if (!dev->isoc_ctl.transfer_buffer[i]) {
24362306a36Sopenharmony_ci			au0828_isocdbg("cannot allocate transfer buffer\n");
24462306a36Sopenharmony_ci			au0828_uninit_isoc(dev);
24562306a36Sopenharmony_ci			return -ENOMEM;
24662306a36Sopenharmony_ci		}
24762306a36Sopenharmony_ci		memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci		pipe = usb_rcvisocpipe(dev->usbdev,
25062306a36Sopenharmony_ci				       dev->isoc_in_endpointaddr);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci		usb_fill_int_urb(urb, dev->usbdev, pipe,
25362306a36Sopenharmony_ci				 dev->isoc_ctl.transfer_buffer[i], sb_size,
25462306a36Sopenharmony_ci				 au0828_irq_callback, dma_q, 1);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci		urb->number_of_packets = max_packets;
25762306a36Sopenharmony_ci		urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci		k = 0;
26062306a36Sopenharmony_ci		for (j = 0; j < max_packets; j++) {
26162306a36Sopenharmony_ci			urb->iso_frame_desc[j].offset = k;
26262306a36Sopenharmony_ci			urb->iso_frame_desc[j].length =
26362306a36Sopenharmony_ci						dev->isoc_ctl.max_pkt_size;
26462306a36Sopenharmony_ci			k += dev->isoc_ctl.max_pkt_size;
26562306a36Sopenharmony_ci		}
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	/* submit urbs and enables IRQ */
26962306a36Sopenharmony_ci	for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
27062306a36Sopenharmony_ci		rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC);
27162306a36Sopenharmony_ci		if (rc) {
27262306a36Sopenharmony_ci			au0828_isocdbg("submit of urb %i failed (error=%i)\n",
27362306a36Sopenharmony_ci				       i, rc);
27462306a36Sopenharmony_ci			au0828_uninit_isoc(dev);
27562306a36Sopenharmony_ci			return rc;
27662306a36Sopenharmony_ci		}
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	return 0;
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci/*
28362306a36Sopenharmony_ci * Announces that a buffer were filled and request the next
28462306a36Sopenharmony_ci */
28562306a36Sopenharmony_cistatic inline void buffer_filled(struct au0828_dev *dev,
28662306a36Sopenharmony_ci				 struct au0828_dmaqueue *dma_q,
28762306a36Sopenharmony_ci				 struct au0828_buffer *buf)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vb = &buf->vb;
29062306a36Sopenharmony_ci	struct vb2_queue *q = vb->vb2_buf.vb2_queue;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	/* Advice that buffer was filled */
29362306a36Sopenharmony_ci	au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->top_field);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
29662306a36Sopenharmony_ci		vb->sequence = dev->frame_count++;
29762306a36Sopenharmony_ci	else
29862306a36Sopenharmony_ci		vb->sequence = dev->vbi_frame_count++;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	vb->field = V4L2_FIELD_INTERLACED;
30162306a36Sopenharmony_ci	vb->vb2_buf.timestamp = ktime_get_ns();
30262306a36Sopenharmony_ci	vb2_buffer_done(&vb->vb2_buf, VB2_BUF_STATE_DONE);
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci/*
30662306a36Sopenharmony_ci * Identify the buffer header type and properly handles
30762306a36Sopenharmony_ci */
30862306a36Sopenharmony_cistatic void au0828_copy_video(struct au0828_dev *dev,
30962306a36Sopenharmony_ci			      struct au0828_dmaqueue  *dma_q,
31062306a36Sopenharmony_ci			      struct au0828_buffer *buf,
31162306a36Sopenharmony_ci			      unsigned char *p,
31262306a36Sopenharmony_ci			      unsigned char *outp, unsigned long len)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	void *fieldstart, *startwrite, *startread;
31562306a36Sopenharmony_ci	int  linesdone, currlinedone, offset, lencopy, remain;
31662306a36Sopenharmony_ci	int bytesperline = dev->width << 1; /* Assumes 16-bit depth @@@@ */
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	if (len == 0)
31962306a36Sopenharmony_ci		return;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	if (dma_q->pos + len > buf->length)
32262306a36Sopenharmony_ci		len = buf->length - dma_q->pos;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	startread = p;
32562306a36Sopenharmony_ci	remain = len;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	/* Interlaces frame */
32862306a36Sopenharmony_ci	if (buf->top_field)
32962306a36Sopenharmony_ci		fieldstart = outp;
33062306a36Sopenharmony_ci	else
33162306a36Sopenharmony_ci		fieldstart = outp + bytesperline;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	linesdone = dma_q->pos / bytesperline;
33462306a36Sopenharmony_ci	currlinedone = dma_q->pos % bytesperline;
33562306a36Sopenharmony_ci	offset = linesdone * bytesperline * 2 + currlinedone;
33662306a36Sopenharmony_ci	startwrite = fieldstart + offset;
33762306a36Sopenharmony_ci	lencopy = bytesperline - currlinedone;
33862306a36Sopenharmony_ci	lencopy = lencopy > remain ? remain : lencopy;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	if ((char *)startwrite + lencopy > (char *)outp + buf->length) {
34162306a36Sopenharmony_ci		au0828_isocdbg("Overflow of %zi bytes past buffer end (1)\n",
34262306a36Sopenharmony_ci			       ((char *)startwrite + lencopy) -
34362306a36Sopenharmony_ci			       ((char *)outp + buf->length));
34462306a36Sopenharmony_ci		remain = (char *)outp + buf->length - (char *)startwrite;
34562306a36Sopenharmony_ci		lencopy = remain;
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci	if (lencopy <= 0)
34862306a36Sopenharmony_ci		return;
34962306a36Sopenharmony_ci	memcpy(startwrite, startread, lencopy);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	remain -= lencopy;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	while (remain > 0) {
35462306a36Sopenharmony_ci		startwrite += lencopy + bytesperline;
35562306a36Sopenharmony_ci		startread += lencopy;
35662306a36Sopenharmony_ci		if (bytesperline > remain)
35762306a36Sopenharmony_ci			lencopy = remain;
35862306a36Sopenharmony_ci		else
35962306a36Sopenharmony_ci			lencopy = bytesperline;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci		if ((char *)startwrite + lencopy > (char *)outp +
36262306a36Sopenharmony_ci		    buf->length) {
36362306a36Sopenharmony_ci			au0828_isocdbg("Overflow %zi bytes past buf end (2)\n",
36462306a36Sopenharmony_ci				       ((char *)startwrite + lencopy) -
36562306a36Sopenharmony_ci				       ((char *)outp + buf->length));
36662306a36Sopenharmony_ci			lencopy = remain = (char *)outp + buf->length -
36762306a36Sopenharmony_ci					   (char *)startwrite;
36862306a36Sopenharmony_ci		}
36962306a36Sopenharmony_ci		if (lencopy <= 0)
37062306a36Sopenharmony_ci			break;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci		memcpy(startwrite, startread, lencopy);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci		remain -= lencopy;
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (offset > 1440) {
37862306a36Sopenharmony_ci		/* We have enough data to check for greenscreen */
37962306a36Sopenharmony_ci		if (outp[0] < 0x60 && outp[1440] < 0x60)
38062306a36Sopenharmony_ci			dev->greenscreen_detected = 1;
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	dma_q->pos += len;
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci/*
38762306a36Sopenharmony_ci * generic routine to get the next available buffer
38862306a36Sopenharmony_ci */
38962306a36Sopenharmony_cistatic inline void get_next_buf(struct au0828_dmaqueue *dma_q,
39062306a36Sopenharmony_ci				struct au0828_buffer **buf)
39162306a36Sopenharmony_ci{
39262306a36Sopenharmony_ci	struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	if (list_empty(&dma_q->active)) {
39562306a36Sopenharmony_ci		au0828_isocdbg("No active queue to serve\n");
39662306a36Sopenharmony_ci		dev->isoc_ctl.buf = NULL;
39762306a36Sopenharmony_ci		*buf = NULL;
39862306a36Sopenharmony_ci		return;
39962306a36Sopenharmony_ci	}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	/* Get the next buffer */
40262306a36Sopenharmony_ci	*buf = list_entry(dma_q->active.next, struct au0828_buffer, list);
40362306a36Sopenharmony_ci	/* Cleans up buffer - Useful for testing for frame/URB loss */
40462306a36Sopenharmony_ci	list_del(&(*buf)->list);
40562306a36Sopenharmony_ci	dma_q->pos = 0;
40662306a36Sopenharmony_ci	(*buf)->vb_buf = (*buf)->mem;
40762306a36Sopenharmony_ci	dev->isoc_ctl.buf = *buf;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	return;
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistatic void au0828_copy_vbi(struct au0828_dev *dev,
41362306a36Sopenharmony_ci			      struct au0828_dmaqueue  *dma_q,
41462306a36Sopenharmony_ci			      struct au0828_buffer *buf,
41562306a36Sopenharmony_ci			      unsigned char *p,
41662306a36Sopenharmony_ci			      unsigned char *outp, unsigned long len)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	unsigned char *startwrite, *startread;
41962306a36Sopenharmony_ci	int bytesperline;
42062306a36Sopenharmony_ci	int i, j = 0;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	if (dev == NULL) {
42362306a36Sopenharmony_ci		au0828_isocdbg("dev is null\n");
42462306a36Sopenharmony_ci		return;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	if (dma_q == NULL) {
42862306a36Sopenharmony_ci		au0828_isocdbg("dma_q is null\n");
42962306a36Sopenharmony_ci		return;
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci	if (buf == NULL)
43262306a36Sopenharmony_ci		return;
43362306a36Sopenharmony_ci	if (p == NULL) {
43462306a36Sopenharmony_ci		au0828_isocdbg("p is null\n");
43562306a36Sopenharmony_ci		return;
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci	if (outp == NULL) {
43862306a36Sopenharmony_ci		au0828_isocdbg("outp is null\n");
43962306a36Sopenharmony_ci		return;
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	bytesperline = dev->vbi_width;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	if (dma_q->pos + len > buf->length)
44562306a36Sopenharmony_ci		len = buf->length - dma_q->pos;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	startread = p;
44862306a36Sopenharmony_ci	startwrite = outp + (dma_q->pos / 2);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	/* Make sure the bottom field populates the second half of the frame */
45162306a36Sopenharmony_ci	if (buf->top_field == 0)
45262306a36Sopenharmony_ci		startwrite += bytesperline * dev->vbi_height;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	for (i = 0; i < len; i += 2)
45562306a36Sopenharmony_ci		startwrite[j++] = startread[i+1];
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	dma_q->pos += len;
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci/*
46262306a36Sopenharmony_ci * generic routine to get the next available VBI buffer
46362306a36Sopenharmony_ci */
46462306a36Sopenharmony_cistatic inline void vbi_get_next_buf(struct au0828_dmaqueue *dma_q,
46562306a36Sopenharmony_ci				    struct au0828_buffer **buf)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vbiq);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	if (list_empty(&dma_q->active)) {
47062306a36Sopenharmony_ci		au0828_isocdbg("No active queue to serve\n");
47162306a36Sopenharmony_ci		dev->isoc_ctl.vbi_buf = NULL;
47262306a36Sopenharmony_ci		*buf = NULL;
47362306a36Sopenharmony_ci		return;
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	/* Get the next buffer */
47762306a36Sopenharmony_ci	*buf = list_entry(dma_q->active.next, struct au0828_buffer, list);
47862306a36Sopenharmony_ci	/* Cleans up buffer - Useful for testing for frame/URB loss */
47962306a36Sopenharmony_ci	list_del(&(*buf)->list);
48062306a36Sopenharmony_ci	dma_q->pos = 0;
48162306a36Sopenharmony_ci	(*buf)->vb_buf = (*buf)->mem;
48262306a36Sopenharmony_ci	dev->isoc_ctl.vbi_buf = *buf;
48362306a36Sopenharmony_ci	return;
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci/*
48762306a36Sopenharmony_ci * Controls the isoc copy of each urb packet
48862306a36Sopenharmony_ci */
48962306a36Sopenharmony_cistatic inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	struct au0828_buffer    *buf;
49262306a36Sopenharmony_ci	struct au0828_buffer    *vbi_buf;
49362306a36Sopenharmony_ci	struct au0828_dmaqueue  *dma_q = urb->context;
49462306a36Sopenharmony_ci	struct au0828_dmaqueue  *vbi_dma_q = &dev->vbiq;
49562306a36Sopenharmony_ci	unsigned char *outp = NULL;
49662306a36Sopenharmony_ci	unsigned char *vbioutp = NULL;
49762306a36Sopenharmony_ci	int i, len = 0, rc = 1;
49862306a36Sopenharmony_ci	unsigned char *p;
49962306a36Sopenharmony_ci	unsigned char fbyte;
50062306a36Sopenharmony_ci	unsigned int vbi_field_size;
50162306a36Sopenharmony_ci	unsigned int remain, lencopy;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	if (!dev)
50462306a36Sopenharmony_ci		return 0;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	if (test_bit(DEV_DISCONNECTED, &dev->dev_state) ||
50762306a36Sopenharmony_ci	    test_bit(DEV_MISCONFIGURED, &dev->dev_state))
50862306a36Sopenharmony_ci		return 0;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	if (urb->status < 0) {
51162306a36Sopenharmony_ci		print_err_status(dev, -1, urb->status);
51262306a36Sopenharmony_ci		if (urb->status == -ENOENT)
51362306a36Sopenharmony_ci			return 0;
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	buf = dev->isoc_ctl.buf;
51762306a36Sopenharmony_ci	if (buf != NULL)
51862306a36Sopenharmony_ci		outp = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	vbi_buf = dev->isoc_ctl.vbi_buf;
52162306a36Sopenharmony_ci	if (vbi_buf != NULL)
52262306a36Sopenharmony_ci		vbioutp = vb2_plane_vaddr(&vbi_buf->vb.vb2_buf, 0);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	for (i = 0; i < urb->number_of_packets; i++) {
52562306a36Sopenharmony_ci		int status = urb->iso_frame_desc[i].status;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci		if (status < 0) {
52862306a36Sopenharmony_ci			print_err_status(dev, i, status);
52962306a36Sopenharmony_ci			if (urb->iso_frame_desc[i].status != -EPROTO)
53062306a36Sopenharmony_ci				continue;
53162306a36Sopenharmony_ci		}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci		if (urb->iso_frame_desc[i].actual_length <= 0)
53462306a36Sopenharmony_ci			continue;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci		if (urb->iso_frame_desc[i].actual_length >
53762306a36Sopenharmony_ci						dev->max_pkt_size) {
53862306a36Sopenharmony_ci			au0828_isocdbg("packet bigger than packet size");
53962306a36Sopenharmony_ci			continue;
54062306a36Sopenharmony_ci		}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci		p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
54362306a36Sopenharmony_ci		fbyte = p[0];
54462306a36Sopenharmony_ci		len = urb->iso_frame_desc[i].actual_length - 4;
54562306a36Sopenharmony_ci		p += 4;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci		if (fbyte & 0x80) {
54862306a36Sopenharmony_ci			len -= 4;
54962306a36Sopenharmony_ci			p += 4;
55062306a36Sopenharmony_ci			au0828_isocdbg("Video frame %s\n",
55162306a36Sopenharmony_ci				       (fbyte & 0x40) ? "odd" : "even");
55262306a36Sopenharmony_ci			if (fbyte & 0x40) {
55362306a36Sopenharmony_ci				/* VBI */
55462306a36Sopenharmony_ci				if (vbi_buf != NULL)
55562306a36Sopenharmony_ci					buffer_filled(dev, vbi_dma_q, vbi_buf);
55662306a36Sopenharmony_ci				vbi_get_next_buf(vbi_dma_q, &vbi_buf);
55762306a36Sopenharmony_ci				if (vbi_buf == NULL)
55862306a36Sopenharmony_ci					vbioutp = NULL;
55962306a36Sopenharmony_ci				else
56062306a36Sopenharmony_ci					vbioutp = vb2_plane_vaddr(
56162306a36Sopenharmony_ci						&vbi_buf->vb.vb2_buf, 0);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci				/* Video */
56462306a36Sopenharmony_ci				if (buf != NULL)
56562306a36Sopenharmony_ci					buffer_filled(dev, dma_q, buf);
56662306a36Sopenharmony_ci				get_next_buf(dma_q, &buf);
56762306a36Sopenharmony_ci				if (buf == NULL)
56862306a36Sopenharmony_ci					outp = NULL;
56962306a36Sopenharmony_ci				else
57062306a36Sopenharmony_ci					outp = vb2_plane_vaddr(
57162306a36Sopenharmony_ci						&buf->vb.vb2_buf, 0);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci				/* As long as isoc traffic is arriving, keep
57462306a36Sopenharmony_ci				   resetting the timer */
57562306a36Sopenharmony_ci				if (dev->vid_timeout_running)
57662306a36Sopenharmony_ci					mod_timer(&dev->vid_timeout,
57762306a36Sopenharmony_ci						  jiffies + (HZ / 10));
57862306a36Sopenharmony_ci				if (dev->vbi_timeout_running)
57962306a36Sopenharmony_ci					mod_timer(&dev->vbi_timeout,
58062306a36Sopenharmony_ci						  jiffies + (HZ / 10));
58162306a36Sopenharmony_ci			}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci			if (buf != NULL) {
58462306a36Sopenharmony_ci				if (fbyte & 0x40)
58562306a36Sopenharmony_ci					buf->top_field = 1;
58662306a36Sopenharmony_ci				else
58762306a36Sopenharmony_ci					buf->top_field = 0;
58862306a36Sopenharmony_ci			}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci			if (vbi_buf != NULL) {
59162306a36Sopenharmony_ci				if (fbyte & 0x40)
59262306a36Sopenharmony_ci					vbi_buf->top_field = 1;
59362306a36Sopenharmony_ci				else
59462306a36Sopenharmony_ci					vbi_buf->top_field = 0;
59562306a36Sopenharmony_ci			}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci			dev->vbi_read = 0;
59862306a36Sopenharmony_ci			vbi_dma_q->pos = 0;
59962306a36Sopenharmony_ci			dma_q->pos = 0;
60062306a36Sopenharmony_ci		}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci		vbi_field_size = dev->vbi_width * dev->vbi_height * 2;
60362306a36Sopenharmony_ci		if (dev->vbi_read < vbi_field_size) {
60462306a36Sopenharmony_ci			remain  = vbi_field_size - dev->vbi_read;
60562306a36Sopenharmony_ci			if (len < remain)
60662306a36Sopenharmony_ci				lencopy = len;
60762306a36Sopenharmony_ci			else
60862306a36Sopenharmony_ci				lencopy = remain;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci			if (vbi_buf != NULL)
61162306a36Sopenharmony_ci				au0828_copy_vbi(dev, vbi_dma_q, vbi_buf, p,
61262306a36Sopenharmony_ci						vbioutp, len);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci			len -= lencopy;
61562306a36Sopenharmony_ci			p += lencopy;
61662306a36Sopenharmony_ci			dev->vbi_read += lencopy;
61762306a36Sopenharmony_ci		}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci		if (dev->vbi_read >= vbi_field_size && buf != NULL)
62062306a36Sopenharmony_ci			au0828_copy_video(dev, dma_q, buf, p, outp, len);
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci	return rc;
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_civoid au0828_usb_v4l2_media_release(struct au0828_dev *dev)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER
62862306a36Sopenharmony_ci	int i;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	for (i = 0; i < AU0828_MAX_INPUT; i++) {
63162306a36Sopenharmony_ci		if (AUVI_INPUT(i).type == AU0828_VMUX_UNDEFINED)
63262306a36Sopenharmony_ci			return;
63362306a36Sopenharmony_ci		media_device_unregister_entity(&dev->input_ent[i]);
63462306a36Sopenharmony_ci	}
63562306a36Sopenharmony_ci#endif
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_cistatic void au0828_usb_v4l2_release(struct v4l2_device *v4l2_dev)
63962306a36Sopenharmony_ci{
64062306a36Sopenharmony_ci	struct au0828_dev *dev =
64162306a36Sopenharmony_ci		container_of(v4l2_dev, struct au0828_dev, v4l2_dev);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&dev->v4l2_ctrl_hdl);
64462306a36Sopenharmony_ci	v4l2_device_unregister(&dev->v4l2_dev);
64562306a36Sopenharmony_ci	au0828_usb_v4l2_media_release(dev);
64662306a36Sopenharmony_ci	au0828_usb_release(dev);
64762306a36Sopenharmony_ci}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ciint au0828_v4l2_device_register(struct usb_interface *interface,
65062306a36Sopenharmony_ci				struct au0828_dev *dev)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	int retval;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	if (AUVI_INPUT(0).type == AU0828_VMUX_UNDEFINED)
65562306a36Sopenharmony_ci		return 0;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	/* Create the v4l2_device */
65862306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER
65962306a36Sopenharmony_ci	dev->v4l2_dev.mdev = dev->media_dev;
66062306a36Sopenharmony_ci#endif
66162306a36Sopenharmony_ci	retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
66262306a36Sopenharmony_ci	if (retval) {
66362306a36Sopenharmony_ci		pr_err("%s() v4l2_device_register failed\n",
66462306a36Sopenharmony_ci		       __func__);
66562306a36Sopenharmony_ci		return retval;
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	dev->v4l2_dev.release = au0828_usb_v4l2_release;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	/* This control handler will inherit the controls from au8522 */
67162306a36Sopenharmony_ci	retval = v4l2_ctrl_handler_init(&dev->v4l2_ctrl_hdl, 4);
67262306a36Sopenharmony_ci	if (retval) {
67362306a36Sopenharmony_ci		pr_err("%s() v4l2_ctrl_handler_init failed\n",
67462306a36Sopenharmony_ci		       __func__);
67562306a36Sopenharmony_ci		return retval;
67662306a36Sopenharmony_ci	}
67762306a36Sopenharmony_ci	dev->v4l2_dev.ctrl_handler = &dev->v4l2_ctrl_hdl;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	return 0;
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic int queue_setup(struct vb2_queue *vq,
68362306a36Sopenharmony_ci		       unsigned int *nbuffers, unsigned int *nplanes,
68462306a36Sopenharmony_ci		       unsigned int sizes[], struct device *alloc_devs[])
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	struct au0828_dev *dev = vb2_get_drv_priv(vq);
68762306a36Sopenharmony_ci	unsigned long size = dev->height * dev->bytesperline;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	if (*nplanes)
69062306a36Sopenharmony_ci		return sizes[0] < size ? -EINVAL : 0;
69162306a36Sopenharmony_ci	*nplanes = 1;
69262306a36Sopenharmony_ci	sizes[0] = size;
69362306a36Sopenharmony_ci	return 0;
69462306a36Sopenharmony_ci}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_cistatic int
69762306a36Sopenharmony_cibuffer_prepare(struct vb2_buffer *vb)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
70062306a36Sopenharmony_ci	struct au0828_buffer *buf = container_of(vbuf,
70162306a36Sopenharmony_ci				struct au0828_buffer, vb);
70262306a36Sopenharmony_ci	struct au0828_dev    *dev = vb2_get_drv_priv(vb->vb2_queue);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	buf->length = dev->height * dev->bytesperline;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	if (vb2_plane_size(vb, 0) < buf->length) {
70762306a36Sopenharmony_ci		pr_err("%s data will not fit into plane (%lu < %lu)\n",
70862306a36Sopenharmony_ci			__func__, vb2_plane_size(vb, 0), buf->length);
70962306a36Sopenharmony_ci		return -EINVAL;
71062306a36Sopenharmony_ci	}
71162306a36Sopenharmony_ci	vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->length);
71262306a36Sopenharmony_ci	return 0;
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic void
71662306a36Sopenharmony_cibuffer_queue(struct vb2_buffer *vb)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
71962306a36Sopenharmony_ci	struct au0828_buffer    *buf     = container_of(vbuf,
72062306a36Sopenharmony_ci							struct au0828_buffer,
72162306a36Sopenharmony_ci							vb);
72262306a36Sopenharmony_ci	struct au0828_dev       *dev     = vb2_get_drv_priv(vb->vb2_queue);
72362306a36Sopenharmony_ci	struct au0828_dmaqueue  *vidq    = &dev->vidq;
72462306a36Sopenharmony_ci	unsigned long flags = 0;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	buf->mem = vb2_plane_vaddr(vb, 0);
72762306a36Sopenharmony_ci	buf->length = vb2_plane_size(vb, 0);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	spin_lock_irqsave(&dev->slock, flags);
73062306a36Sopenharmony_ci	list_add_tail(&buf->list, &vidq->active);
73162306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->slock, flags);
73262306a36Sopenharmony_ci}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_cistatic int au0828_i2s_init(struct au0828_dev *dev)
73562306a36Sopenharmony_ci{
73662306a36Sopenharmony_ci	/* Enable i2s mode */
73762306a36Sopenharmony_ci	au0828_writereg(dev, AU0828_AUDIOCTRL_50C, 0x01);
73862306a36Sopenharmony_ci	return 0;
73962306a36Sopenharmony_ci}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci/*
74262306a36Sopenharmony_ci * Auvitek au0828 analog stream enable
74362306a36Sopenharmony_ci */
74462306a36Sopenharmony_cistatic int au0828_analog_stream_enable(struct au0828_dev *d)
74562306a36Sopenharmony_ci{
74662306a36Sopenharmony_ci	struct usb_interface *iface;
74762306a36Sopenharmony_ci	int ret, h, w;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	dprintk(1, "au0828_analog_stream_enable called\n");
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	if (test_bit(DEV_DISCONNECTED, &d->dev_state))
75262306a36Sopenharmony_ci		return -ENODEV;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	iface = usb_ifnum_to_if(d->usbdev, 0);
75562306a36Sopenharmony_ci	if (iface && iface->cur_altsetting->desc.bAlternateSetting != 5) {
75662306a36Sopenharmony_ci		dprintk(1, "Changing intf#0 to alt 5\n");
75762306a36Sopenharmony_ci		/* set au0828 interface0 to AS5 here again */
75862306a36Sopenharmony_ci		ret = usb_set_interface(d->usbdev, 0, 5);
75962306a36Sopenharmony_ci		if (ret < 0) {
76062306a36Sopenharmony_ci			pr_info("Au0828 can't set alt setting to 5!\n");
76162306a36Sopenharmony_ci			return -EBUSY;
76262306a36Sopenharmony_ci		}
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	h = d->height / 2 + 2;
76662306a36Sopenharmony_ci	w = d->width * 2;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	au0828_writereg(d, AU0828_SENSORCTRL_VBI_103, 0x00);
76962306a36Sopenharmony_ci	au0828_writereg(d, 0x106, 0x00);
77062306a36Sopenharmony_ci	/* set x position */
77162306a36Sopenharmony_ci	au0828_writereg(d, 0x110, 0x00);
77262306a36Sopenharmony_ci	au0828_writereg(d, 0x111, 0x00);
77362306a36Sopenharmony_ci	au0828_writereg(d, 0x114, w & 0xff);
77462306a36Sopenharmony_ci	au0828_writereg(d, 0x115, w >> 8);
77562306a36Sopenharmony_ci	/* set y position */
77662306a36Sopenharmony_ci	au0828_writereg(d, 0x112, 0x00);
77762306a36Sopenharmony_ci	au0828_writereg(d, 0x113, 0x00);
77862306a36Sopenharmony_ci	au0828_writereg(d, 0x116, h & 0xff);
77962306a36Sopenharmony_ci	au0828_writereg(d, 0x117, h >> 8);
78062306a36Sopenharmony_ci	au0828_writereg(d, AU0828_SENSORCTRL_100, 0xb3);
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	return 0;
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_cistatic int au0828_analog_stream_disable(struct au0828_dev *d)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	dprintk(1, "au0828_analog_stream_disable called\n");
78862306a36Sopenharmony_ci	au0828_writereg(d, AU0828_SENSORCTRL_100, 0x0);
78962306a36Sopenharmony_ci	return 0;
79062306a36Sopenharmony_ci}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_cistatic void au0828_analog_stream_reset(struct au0828_dev *dev)
79362306a36Sopenharmony_ci{
79462306a36Sopenharmony_ci	dprintk(1, "au0828_analog_stream_reset called\n");
79562306a36Sopenharmony_ci	au0828_writereg(dev, AU0828_SENSORCTRL_100, 0x0);
79662306a36Sopenharmony_ci	mdelay(30);
79762306a36Sopenharmony_ci	au0828_writereg(dev, AU0828_SENSORCTRL_100, 0xb3);
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci/*
80162306a36Sopenharmony_ci * Some operations needs to stop current streaming
80262306a36Sopenharmony_ci */
80362306a36Sopenharmony_cistatic int au0828_stream_interrupt(struct au0828_dev *dev)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	dev->stream_state = STREAM_INTERRUPT;
80662306a36Sopenharmony_ci	if (test_bit(DEV_DISCONNECTED, &dev->dev_state))
80762306a36Sopenharmony_ci		return -ENODEV;
80862306a36Sopenharmony_ci	return 0;
80962306a36Sopenharmony_ci}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ciint au0828_start_analog_streaming(struct vb2_queue *vq, unsigned int count)
81262306a36Sopenharmony_ci{
81362306a36Sopenharmony_ci	struct au0828_dev *dev = vb2_get_drv_priv(vq);
81462306a36Sopenharmony_ci	int rc = 0;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	dprintk(1, "au0828_start_analog_streaming called %d\n",
81762306a36Sopenharmony_ci		dev->streaming_users);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
82062306a36Sopenharmony_ci		dev->frame_count = 0;
82162306a36Sopenharmony_ci	else
82262306a36Sopenharmony_ci		dev->vbi_frame_count = 0;
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	if (dev->streaming_users == 0) {
82562306a36Sopenharmony_ci		/* If we were doing ac97 instead of i2s, it would go here...*/
82662306a36Sopenharmony_ci		au0828_i2s_init(dev);
82762306a36Sopenharmony_ci		rc = au0828_init_isoc(dev, AU0828_ISO_PACKETS_PER_URB,
82862306a36Sopenharmony_ci				   AU0828_MAX_ISO_BUFS, dev->max_pkt_size,
82962306a36Sopenharmony_ci				   au0828_isoc_copy);
83062306a36Sopenharmony_ci		if (rc < 0) {
83162306a36Sopenharmony_ci			pr_info("au0828_init_isoc failed\n");
83262306a36Sopenharmony_ci			return rc;
83362306a36Sopenharmony_ci		}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci		v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci		if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
83862306a36Sopenharmony_ci			dev->vid_timeout_running = 1;
83962306a36Sopenharmony_ci			mod_timer(&dev->vid_timeout, jiffies + (HZ / 10));
84062306a36Sopenharmony_ci		} else if (vq->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
84162306a36Sopenharmony_ci			dev->vbi_timeout_running = 1;
84262306a36Sopenharmony_ci			mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10));
84362306a36Sopenharmony_ci		}
84462306a36Sopenharmony_ci	}
84562306a36Sopenharmony_ci	dev->streaming_users++;
84662306a36Sopenharmony_ci	return rc;
84762306a36Sopenharmony_ci}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_cistatic void au0828_stop_streaming(struct vb2_queue *vq)
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci	struct au0828_dev *dev = vb2_get_drv_priv(vq);
85262306a36Sopenharmony_ci	struct au0828_dmaqueue *vidq = &dev->vidq;
85362306a36Sopenharmony_ci	unsigned long flags = 0;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	dprintk(1, "au0828_stop_streaming called %d\n", dev->streaming_users);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	if (dev->streaming_users-- == 1) {
85862306a36Sopenharmony_ci		au0828_uninit_isoc(dev);
85962306a36Sopenharmony_ci		v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
86062306a36Sopenharmony_ci	}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	dev->vid_timeout_running = 0;
86362306a36Sopenharmony_ci	del_timer_sync(&dev->vid_timeout);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	spin_lock_irqsave(&dev->slock, flags);
86662306a36Sopenharmony_ci	if (dev->isoc_ctl.buf != NULL) {
86762306a36Sopenharmony_ci		vb2_buffer_done(&dev->isoc_ctl.buf->vb.vb2_buf,
86862306a36Sopenharmony_ci				VB2_BUF_STATE_ERROR);
86962306a36Sopenharmony_ci		dev->isoc_ctl.buf = NULL;
87062306a36Sopenharmony_ci	}
87162306a36Sopenharmony_ci	while (!list_empty(&vidq->active)) {
87262306a36Sopenharmony_ci		struct au0828_buffer *buf;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci		buf = list_entry(vidq->active.next, struct au0828_buffer, list);
87562306a36Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
87662306a36Sopenharmony_ci		list_del(&buf->list);
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->slock, flags);
87962306a36Sopenharmony_ci}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_civoid au0828_stop_vbi_streaming(struct vb2_queue *vq)
88262306a36Sopenharmony_ci{
88362306a36Sopenharmony_ci	struct au0828_dev *dev = vb2_get_drv_priv(vq);
88462306a36Sopenharmony_ci	struct au0828_dmaqueue *vbiq = &dev->vbiq;
88562306a36Sopenharmony_ci	unsigned long flags = 0;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	dprintk(1, "au0828_stop_vbi_streaming called %d\n",
88862306a36Sopenharmony_ci		dev->streaming_users);
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	if (dev->streaming_users-- == 1) {
89162306a36Sopenharmony_ci		au0828_uninit_isoc(dev);
89262306a36Sopenharmony_ci		v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	spin_lock_irqsave(&dev->slock, flags);
89662306a36Sopenharmony_ci	if (dev->isoc_ctl.vbi_buf != NULL) {
89762306a36Sopenharmony_ci		vb2_buffer_done(&dev->isoc_ctl.vbi_buf->vb.vb2_buf,
89862306a36Sopenharmony_ci				VB2_BUF_STATE_ERROR);
89962306a36Sopenharmony_ci		dev->isoc_ctl.vbi_buf = NULL;
90062306a36Sopenharmony_ci	}
90162306a36Sopenharmony_ci	while (!list_empty(&vbiq->active)) {
90262306a36Sopenharmony_ci		struct au0828_buffer *buf;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci		buf = list_entry(vbiq->active.next, struct au0828_buffer, list);
90562306a36Sopenharmony_ci		list_del(&buf->list);
90662306a36Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
90762306a36Sopenharmony_ci	}
90862306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->slock, flags);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	dev->vbi_timeout_running = 0;
91162306a36Sopenharmony_ci	del_timer_sync(&dev->vbi_timeout);
91262306a36Sopenharmony_ci}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_cistatic const struct vb2_ops au0828_video_qops = {
91562306a36Sopenharmony_ci	.queue_setup     = queue_setup,
91662306a36Sopenharmony_ci	.buf_prepare     = buffer_prepare,
91762306a36Sopenharmony_ci	.buf_queue       = buffer_queue,
91862306a36Sopenharmony_ci	.prepare_streaming = v4l_vb2q_enable_media_source,
91962306a36Sopenharmony_ci	.start_streaming = au0828_start_analog_streaming,
92062306a36Sopenharmony_ci	.stop_streaming  = au0828_stop_streaming,
92162306a36Sopenharmony_ci	.wait_prepare    = vb2_ops_wait_prepare,
92262306a36Sopenharmony_ci	.wait_finish     = vb2_ops_wait_finish,
92362306a36Sopenharmony_ci};
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci/* ------------------------------------------------------------------
92662306a36Sopenharmony_ci   V4L2 interface
92762306a36Sopenharmony_ci   ------------------------------------------------------------------*/
92862306a36Sopenharmony_ci/*
92962306a36Sopenharmony_ci * au0828_analog_unregister
93062306a36Sopenharmony_ci * unregister v4l2 devices
93162306a36Sopenharmony_ci */
93262306a36Sopenharmony_ciint au0828_analog_unregister(struct au0828_dev *dev)
93362306a36Sopenharmony_ci{
93462306a36Sopenharmony_ci	dprintk(1, "au0828_analog_unregister called\n");
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	/* No analog TV */
93762306a36Sopenharmony_ci	if (AUVI_INPUT(0).type == AU0828_VMUX_UNDEFINED)
93862306a36Sopenharmony_ci		return 0;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	mutex_lock(&au0828_sysfs_lock);
94162306a36Sopenharmony_ci	vb2_video_unregister_device(&dev->vdev);
94262306a36Sopenharmony_ci	vb2_video_unregister_device(&dev->vbi_dev);
94362306a36Sopenharmony_ci	mutex_unlock(&au0828_sysfs_lock);
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	v4l2_device_disconnect(&dev->v4l2_dev);
94662306a36Sopenharmony_ci	v4l2_device_put(&dev->v4l2_dev);
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	return 1;
94962306a36Sopenharmony_ci}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci/* This function ensures that video frames continue to be delivered even if
95262306a36Sopenharmony_ci   the ITU-656 input isn't receiving any data (thereby preventing applications
95362306a36Sopenharmony_ci   such as tvtime from hanging) */
95462306a36Sopenharmony_cistatic void au0828_vid_buffer_timeout(struct timer_list *t)
95562306a36Sopenharmony_ci{
95662306a36Sopenharmony_ci	struct au0828_dev *dev = from_timer(dev, t, vid_timeout);
95762306a36Sopenharmony_ci	struct au0828_dmaqueue *dma_q = &dev->vidq;
95862306a36Sopenharmony_ci	struct au0828_buffer *buf;
95962306a36Sopenharmony_ci	unsigned char *vid_data;
96062306a36Sopenharmony_ci	unsigned long flags = 0;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	spin_lock_irqsave(&dev->slock, flags);
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	buf = dev->isoc_ctl.buf;
96562306a36Sopenharmony_ci	if (buf != NULL) {
96662306a36Sopenharmony_ci		vid_data = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
96762306a36Sopenharmony_ci		memset(vid_data, 0x00, buf->length); /* Blank green frame */
96862306a36Sopenharmony_ci		buffer_filled(dev, dma_q, buf);
96962306a36Sopenharmony_ci	}
97062306a36Sopenharmony_ci	get_next_buf(dma_q, &buf);
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	if (dev->vid_timeout_running == 1)
97362306a36Sopenharmony_ci		mod_timer(&dev->vid_timeout, jiffies + (HZ / 10));
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->slock, flags);
97662306a36Sopenharmony_ci}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_cistatic void au0828_vbi_buffer_timeout(struct timer_list *t)
97962306a36Sopenharmony_ci{
98062306a36Sopenharmony_ci	struct au0828_dev *dev = from_timer(dev, t, vbi_timeout);
98162306a36Sopenharmony_ci	struct au0828_dmaqueue *dma_q = &dev->vbiq;
98262306a36Sopenharmony_ci	struct au0828_buffer *buf;
98362306a36Sopenharmony_ci	unsigned char *vbi_data;
98462306a36Sopenharmony_ci	unsigned long flags = 0;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	spin_lock_irqsave(&dev->slock, flags);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	buf = dev->isoc_ctl.vbi_buf;
98962306a36Sopenharmony_ci	if (buf != NULL) {
99062306a36Sopenharmony_ci		vbi_data = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
99162306a36Sopenharmony_ci		memset(vbi_data, 0x00, buf->length);
99262306a36Sopenharmony_ci		buffer_filled(dev, dma_q, buf);
99362306a36Sopenharmony_ci	}
99462306a36Sopenharmony_ci	vbi_get_next_buf(dma_q, &buf);
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	if (dev->vbi_timeout_running == 1)
99762306a36Sopenharmony_ci		mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10));
99862306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->slock, flags);
99962306a36Sopenharmony_ci}
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_cistatic int au0828_v4l2_open(struct file *filp)
100262306a36Sopenharmony_ci{
100362306a36Sopenharmony_ci	struct au0828_dev *dev = video_drvdata(filp);
100462306a36Sopenharmony_ci	int ret;
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	dprintk(1,
100762306a36Sopenharmony_ci		"%s called std_set %d dev_state %ld stream users %d users %d\n",
100862306a36Sopenharmony_ci		__func__, dev->std_set_in_tuner_core, dev->dev_state,
100962306a36Sopenharmony_ci		dev->streaming_users, dev->users);
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	if (mutex_lock_interruptible(&dev->lock))
101262306a36Sopenharmony_ci		return -ERESTARTSYS;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	ret = v4l2_fh_open(filp);
101562306a36Sopenharmony_ci	if (ret) {
101662306a36Sopenharmony_ci		au0828_isocdbg("%s: v4l2_fh_open() returned error %d\n",
101762306a36Sopenharmony_ci				__func__, ret);
101862306a36Sopenharmony_ci		mutex_unlock(&dev->lock);
101962306a36Sopenharmony_ci		return ret;
102062306a36Sopenharmony_ci	}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	if (dev->users == 0) {
102362306a36Sopenharmony_ci		au0828_analog_stream_enable(dev);
102462306a36Sopenharmony_ci		au0828_analog_stream_reset(dev);
102562306a36Sopenharmony_ci		dev->stream_state = STREAM_OFF;
102662306a36Sopenharmony_ci		set_bit(DEV_INITIALIZED, &dev->dev_state);
102762306a36Sopenharmony_ci	}
102862306a36Sopenharmony_ci	dev->users++;
102962306a36Sopenharmony_ci	mutex_unlock(&dev->lock);
103062306a36Sopenharmony_ci	return ret;
103162306a36Sopenharmony_ci}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_cistatic int au0828_v4l2_close(struct file *filp)
103462306a36Sopenharmony_ci{
103562306a36Sopenharmony_ci	int ret;
103662306a36Sopenharmony_ci	struct au0828_dev *dev = video_drvdata(filp);
103762306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(filp);
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	dprintk(1,
104062306a36Sopenharmony_ci		"%s called std_set %d dev_state %ld stream users %d users %d\n",
104162306a36Sopenharmony_ci		__func__, dev->std_set_in_tuner_core, dev->dev_state,
104262306a36Sopenharmony_ci		dev->streaming_users, dev->users);
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	mutex_lock(&dev->lock);
104562306a36Sopenharmony_ci	if (vdev->vfl_type == VFL_TYPE_VIDEO && dev->vid_timeout_running) {
104662306a36Sopenharmony_ci		/* Cancel timeout thread in case they didn't call streamoff */
104762306a36Sopenharmony_ci		dev->vid_timeout_running = 0;
104862306a36Sopenharmony_ci		del_timer_sync(&dev->vid_timeout);
104962306a36Sopenharmony_ci	} else if (vdev->vfl_type == VFL_TYPE_VBI &&
105062306a36Sopenharmony_ci			dev->vbi_timeout_running) {
105162306a36Sopenharmony_ci		/* Cancel timeout thread in case they didn't call streamoff */
105262306a36Sopenharmony_ci		dev->vbi_timeout_running = 0;
105362306a36Sopenharmony_ci		del_timer_sync(&dev->vbi_timeout);
105462306a36Sopenharmony_ci	}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	if (test_bit(DEV_DISCONNECTED, &dev->dev_state))
105762306a36Sopenharmony_ci		goto end;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	if (dev->users == 1) {
106062306a36Sopenharmony_ci		/*
106162306a36Sopenharmony_ci		 * Avoid putting tuner in sleep if DVB or ALSA are
106262306a36Sopenharmony_ci		 * streaming.
106362306a36Sopenharmony_ci		 *
106462306a36Sopenharmony_ci		 * On most USB devices  like au0828 the tuner can
106562306a36Sopenharmony_ci		 * be safely put in sleep state here if ALSA isn't
106662306a36Sopenharmony_ci		 * streaming. Exceptions are some very old USB tuner
106762306a36Sopenharmony_ci		 * models such as em28xx-based WinTV USB2 which have
106862306a36Sopenharmony_ci		 * a separate audio output jack. The devices that have
106962306a36Sopenharmony_ci		 * a separate audio output jack have analog tuners,
107062306a36Sopenharmony_ci		 * like Philips FM1236. Those devices are always on,
107162306a36Sopenharmony_ci		 * so the s_power callback are silently ignored.
107262306a36Sopenharmony_ci		 * So, the current logic here does the following:
107362306a36Sopenharmony_ci		 * Disable (put tuner to sleep) when
107462306a36Sopenharmony_ci		 * - ALSA and DVB aren't streaming.
107562306a36Sopenharmony_ci		 * - the last V4L2 file handler is closed.
107662306a36Sopenharmony_ci		 *
107762306a36Sopenharmony_ci		 * FIXME:
107862306a36Sopenharmony_ci		 *
107962306a36Sopenharmony_ci		 * Additionally, this logic could be improved to
108062306a36Sopenharmony_ci		 * disable the media source if the above conditions
108162306a36Sopenharmony_ci		 * are met and if the device:
108262306a36Sopenharmony_ci		 * - doesn't have a separate audio out plug (or
108362306a36Sopenharmony_ci		 * - doesn't use a silicon tuner like xc2028/3028/4000/5000).
108462306a36Sopenharmony_ci		 *
108562306a36Sopenharmony_ci		 * Once this additional logic is in place, a callback
108662306a36Sopenharmony_ci		 * is needed to enable the media source and power on
108762306a36Sopenharmony_ci		 * the tuner, for radio to work.
108862306a36Sopenharmony_ci		*/
108962306a36Sopenharmony_ci		ret = v4l_enable_media_source(vdev);
109062306a36Sopenharmony_ci		if (ret == 0)
109162306a36Sopenharmony_ci			v4l2_device_call_all(&dev->v4l2_dev, 0, tuner,
109262306a36Sopenharmony_ci					     standby);
109362306a36Sopenharmony_ci		dev->std_set_in_tuner_core = 0;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci		/* When close the device, set the usb intf0 into alt0 to free
109662306a36Sopenharmony_ci		   USB bandwidth */
109762306a36Sopenharmony_ci		ret = usb_set_interface(dev->usbdev, 0, 0);
109862306a36Sopenharmony_ci		if (ret < 0)
109962306a36Sopenharmony_ci			pr_info("Au0828 can't set alternate to 0!\n");
110062306a36Sopenharmony_ci	}
110162306a36Sopenharmony_ciend:
110262306a36Sopenharmony_ci	_vb2_fop_release(filp, NULL);
110362306a36Sopenharmony_ci	dev->users--;
110462306a36Sopenharmony_ci	mutex_unlock(&dev->lock);
110562306a36Sopenharmony_ci	return 0;
110662306a36Sopenharmony_ci}
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci/* Must be called with dev->lock held */
110962306a36Sopenharmony_cistatic void au0828_init_tuner(struct au0828_dev *dev)
111062306a36Sopenharmony_ci{
111162306a36Sopenharmony_ci	struct v4l2_frequency f = {
111262306a36Sopenharmony_ci		.frequency = dev->ctrl_freq,
111362306a36Sopenharmony_ci		.type = V4L2_TUNER_ANALOG_TV,
111462306a36Sopenharmony_ci	};
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
111762306a36Sopenharmony_ci		dev->std_set_in_tuner_core, dev->dev_state);
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	if (dev->std_set_in_tuner_core)
112062306a36Sopenharmony_ci		return;
112162306a36Sopenharmony_ci	dev->std_set_in_tuner_core = 1;
112262306a36Sopenharmony_ci	i2c_gate_ctrl(dev, 1);
112362306a36Sopenharmony_ci	/* If we've never sent the standard in tuner core, do so now.
112462306a36Sopenharmony_ci	   We don't do this at device probe because we don't want to
112562306a36Sopenharmony_ci	   incur the cost of a firmware load */
112662306a36Sopenharmony_ci	v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->std);
112762306a36Sopenharmony_ci	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
112862306a36Sopenharmony_ci	i2c_gate_ctrl(dev, 0);
112962306a36Sopenharmony_ci}
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_cistatic int au0828_set_format(struct au0828_dev *dev, unsigned int cmd,
113262306a36Sopenharmony_ci			     struct v4l2_format *format)
113362306a36Sopenharmony_ci{
113462306a36Sopenharmony_ci	int ret;
113562306a36Sopenharmony_ci	int width = format->fmt.pix.width;
113662306a36Sopenharmony_ci	int height = format->fmt.pix.height;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	/* If they are demanding a format other than the one we support,
113962306a36Sopenharmony_ci	   bail out (tvtime asks for UYVY and then retries with YUYV) */
114062306a36Sopenharmony_ci	if (format->fmt.pix.pixelformat != V4L2_PIX_FMT_UYVY)
114162306a36Sopenharmony_ci		return -EINVAL;
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	/* format->fmt.pix.width only support 720 and height 480 */
114462306a36Sopenharmony_ci	if (width != 720)
114562306a36Sopenharmony_ci		width = 720;
114662306a36Sopenharmony_ci	if (height != 480)
114762306a36Sopenharmony_ci		height = 480;
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	format->fmt.pix.width = width;
115062306a36Sopenharmony_ci	format->fmt.pix.height = height;
115162306a36Sopenharmony_ci	format->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
115262306a36Sopenharmony_ci	format->fmt.pix.bytesperline = width * 2;
115362306a36Sopenharmony_ci	format->fmt.pix.sizeimage = width * height * 2;
115462306a36Sopenharmony_ci	format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
115562306a36Sopenharmony_ci	format->fmt.pix.field = V4L2_FIELD_INTERLACED;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	if (cmd == VIDIOC_TRY_FMT)
115862306a36Sopenharmony_ci		return 0;
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	/* maybe set new image format, driver current only support 720*480 */
116162306a36Sopenharmony_ci	dev->width = width;
116262306a36Sopenharmony_ci	dev->height = height;
116362306a36Sopenharmony_ci	dev->frame_size = width * height * 2;
116462306a36Sopenharmony_ci	dev->field_size = width * height;
116562306a36Sopenharmony_ci	dev->bytesperline = width * 2;
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	if (dev->stream_state == STREAM_ON) {
116862306a36Sopenharmony_ci		dprintk(1, "VIDIOC_SET_FMT: interrupting stream!\n");
116962306a36Sopenharmony_ci		ret = au0828_stream_interrupt(dev);
117062306a36Sopenharmony_ci		if (ret != 0) {
117162306a36Sopenharmony_ci			dprintk(1, "error interrupting video stream!\n");
117262306a36Sopenharmony_ci			return ret;
117362306a36Sopenharmony_ci		}
117462306a36Sopenharmony_ci	}
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	au0828_analog_stream_enable(dev);
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	return 0;
117962306a36Sopenharmony_ci}
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_cistatic int vidioc_querycap(struct file *file, void  *priv,
118262306a36Sopenharmony_ci			   struct v4l2_capability *cap)
118362306a36Sopenharmony_ci{
118462306a36Sopenharmony_ci	struct au0828_dev *dev = video_drvdata(file);
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
118762306a36Sopenharmony_ci		dev->std_set_in_tuner_core, dev->dev_state);
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	strscpy(cap->driver, "au0828", sizeof(cap->driver));
119062306a36Sopenharmony_ci	strscpy(cap->card, dev->board.name, sizeof(cap->card));
119162306a36Sopenharmony_ci	usb_make_path(dev->usbdev, cap->bus_info, sizeof(cap->bus_info));
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	/* set the device capabilities */
119462306a36Sopenharmony_ci	cap->capabilities =
119562306a36Sopenharmony_ci		V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
119662306a36Sopenharmony_ci		V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE |
119762306a36Sopenharmony_ci		V4L2_CAP_DEVICE_CAPS;
119862306a36Sopenharmony_ci	return 0;
119962306a36Sopenharmony_ci}
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_cistatic int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
120262306a36Sopenharmony_ci					struct v4l2_fmtdesc *f)
120362306a36Sopenharmony_ci{
120462306a36Sopenharmony_ci	if (f->index)
120562306a36Sopenharmony_ci		return -EINVAL;
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	dprintk(1, "%s called\n", __func__);
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	f->pixelformat = V4L2_PIX_FMT_UYVY;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	return 0;
121262306a36Sopenharmony_ci}
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_cistatic int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
121562306a36Sopenharmony_ci					struct v4l2_format *f)
121662306a36Sopenharmony_ci{
121762306a36Sopenharmony_ci	struct au0828_dev *dev = video_drvdata(file);
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
122062306a36Sopenharmony_ci		dev->std_set_in_tuner_core, dev->dev_state);
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	f->fmt.pix.width = dev->width;
122362306a36Sopenharmony_ci	f->fmt.pix.height = dev->height;
122462306a36Sopenharmony_ci	f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
122562306a36Sopenharmony_ci	f->fmt.pix.bytesperline = dev->bytesperline;
122662306a36Sopenharmony_ci	f->fmt.pix.sizeimage = dev->frame_size;
122762306a36Sopenharmony_ci	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* NTSC/PAL */
122862306a36Sopenharmony_ci	f->fmt.pix.field = V4L2_FIELD_INTERLACED;
122962306a36Sopenharmony_ci	return 0;
123062306a36Sopenharmony_ci}
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_cistatic int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
123362306a36Sopenharmony_ci				  struct v4l2_format *f)
123462306a36Sopenharmony_ci{
123562306a36Sopenharmony_ci	struct au0828_dev *dev = video_drvdata(file);
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
123862306a36Sopenharmony_ci		dev->std_set_in_tuner_core, dev->dev_state);
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	return au0828_set_format(dev, VIDIOC_TRY_FMT, f);
124162306a36Sopenharmony_ci}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_cistatic int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
124462306a36Sopenharmony_ci				struct v4l2_format *f)
124562306a36Sopenharmony_ci{
124662306a36Sopenharmony_ci	struct au0828_dev *dev = video_drvdata(file);
124762306a36Sopenharmony_ci	int rc;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
125062306a36Sopenharmony_ci		dev->std_set_in_tuner_core, dev->dev_state);
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	rc = check_dev(dev);
125362306a36Sopenharmony_ci	if (rc < 0)
125462306a36Sopenharmony_ci		return rc;
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	if (vb2_is_busy(&dev->vb_vidq)) {
125762306a36Sopenharmony_ci		pr_info("%s queue busy\n", __func__);
125862306a36Sopenharmony_ci		rc = -EBUSY;
125962306a36Sopenharmony_ci		goto out;
126062306a36Sopenharmony_ci	}
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	rc = au0828_set_format(dev, VIDIOC_S_FMT, f);
126362306a36Sopenharmony_ciout:
126462306a36Sopenharmony_ci	return rc;
126562306a36Sopenharmony_ci}
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_cistatic int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
126862306a36Sopenharmony_ci{
126962306a36Sopenharmony_ci	struct au0828_dev *dev = video_drvdata(file);
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
127262306a36Sopenharmony_ci		dev->std_set_in_tuner_core, dev->dev_state);
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	if (norm == dev->std)
127562306a36Sopenharmony_ci		return 0;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	if (dev->streaming_users > 0)
127862306a36Sopenharmony_ci		return -EBUSY;
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	dev->std = norm;
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	au0828_init_tuner(dev);
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	i2c_gate_ctrl(dev, 1);
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	/*
128762306a36Sopenharmony_ci	 * FIXME: when we support something other than 60Hz standards,
128862306a36Sopenharmony_ci	 * we are going to have to make the au0828 bridge adjust the size
128962306a36Sopenharmony_ci	 * of its capture buffer, which is currently hardcoded at 720x480
129062306a36Sopenharmony_ci	 */
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, norm);
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	i2c_gate_ctrl(dev, 0);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	return 0;
129762306a36Sopenharmony_ci}
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_cistatic int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
130062306a36Sopenharmony_ci{
130162306a36Sopenharmony_ci	struct au0828_dev *dev = video_drvdata(file);
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
130462306a36Sopenharmony_ci		dev->std_set_in_tuner_core, dev->dev_state);
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	*norm = dev->std;
130762306a36Sopenharmony_ci	return 0;
130862306a36Sopenharmony_ci}
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_cistatic int vidioc_enum_input(struct file *file, void *priv,
131162306a36Sopenharmony_ci				struct v4l2_input *input)
131262306a36Sopenharmony_ci{
131362306a36Sopenharmony_ci	struct au0828_dev *dev = video_drvdata(file);
131462306a36Sopenharmony_ci	unsigned int tmp;
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	static const char *inames[] = {
131762306a36Sopenharmony_ci		[AU0828_VMUX_UNDEFINED] = "Undefined",
131862306a36Sopenharmony_ci		[AU0828_VMUX_COMPOSITE] = "Composite",
131962306a36Sopenharmony_ci		[AU0828_VMUX_SVIDEO] = "S-Video",
132062306a36Sopenharmony_ci		[AU0828_VMUX_CABLE] = "Cable TV",
132162306a36Sopenharmony_ci		[AU0828_VMUX_TELEVISION] = "Television",
132262306a36Sopenharmony_ci		[AU0828_VMUX_DVB] = "DVB",
132362306a36Sopenharmony_ci	};
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
132662306a36Sopenharmony_ci		dev->std_set_in_tuner_core, dev->dev_state);
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	tmp = input->index;
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	if (tmp >= AU0828_MAX_INPUT)
133162306a36Sopenharmony_ci		return -EINVAL;
133262306a36Sopenharmony_ci	if (AUVI_INPUT(tmp).type == 0)
133362306a36Sopenharmony_ci		return -EINVAL;
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	input->index = tmp;
133662306a36Sopenharmony_ci	strscpy(input->name, inames[AUVI_INPUT(tmp).type], sizeof(input->name));
133762306a36Sopenharmony_ci	if ((AUVI_INPUT(tmp).type == AU0828_VMUX_TELEVISION) ||
133862306a36Sopenharmony_ci	    (AUVI_INPUT(tmp).type == AU0828_VMUX_CABLE)) {
133962306a36Sopenharmony_ci		input->type |= V4L2_INPUT_TYPE_TUNER;
134062306a36Sopenharmony_ci		input->audioset = 1;
134162306a36Sopenharmony_ci	} else {
134262306a36Sopenharmony_ci		input->type |= V4L2_INPUT_TYPE_CAMERA;
134362306a36Sopenharmony_ci		input->audioset = 2;
134462306a36Sopenharmony_ci	}
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	input->std = dev->vdev.tvnorms;
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	return 0;
134962306a36Sopenharmony_ci}
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_cistatic int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
135262306a36Sopenharmony_ci{
135362306a36Sopenharmony_ci	struct au0828_dev *dev = video_drvdata(file);
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
135662306a36Sopenharmony_ci		dev->std_set_in_tuner_core, dev->dev_state);
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	*i = dev->ctrl_input;
135962306a36Sopenharmony_ci	return 0;
136062306a36Sopenharmony_ci}
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_cistatic void au0828_s_input(struct au0828_dev *dev, int index)
136362306a36Sopenharmony_ci{
136462306a36Sopenharmony_ci	int i;
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
136762306a36Sopenharmony_ci		dev->std_set_in_tuner_core, dev->dev_state);
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	switch (AUVI_INPUT(index).type) {
137062306a36Sopenharmony_ci	case AU0828_VMUX_SVIDEO:
137162306a36Sopenharmony_ci		dev->input_type = AU0828_VMUX_SVIDEO;
137262306a36Sopenharmony_ci		dev->ctrl_ainput = 1;
137362306a36Sopenharmony_ci		break;
137462306a36Sopenharmony_ci	case AU0828_VMUX_COMPOSITE:
137562306a36Sopenharmony_ci		dev->input_type = AU0828_VMUX_COMPOSITE;
137662306a36Sopenharmony_ci		dev->ctrl_ainput = 1;
137762306a36Sopenharmony_ci		break;
137862306a36Sopenharmony_ci	case AU0828_VMUX_TELEVISION:
137962306a36Sopenharmony_ci		dev->input_type = AU0828_VMUX_TELEVISION;
138062306a36Sopenharmony_ci		dev->ctrl_ainput = 0;
138162306a36Sopenharmony_ci		break;
138262306a36Sopenharmony_ci	default:
138362306a36Sopenharmony_ci		dprintk(1, "unknown input type set [%d]\n",
138462306a36Sopenharmony_ci			AUVI_INPUT(index).type);
138562306a36Sopenharmony_ci		return;
138662306a36Sopenharmony_ci	}
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci	dev->ctrl_input = index;
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
139162306a36Sopenharmony_ci			AUVI_INPUT(index).vmux, 0, 0);
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	for (i = 0; i < AU0828_MAX_INPUT; i++) {
139462306a36Sopenharmony_ci		int enable = 0;
139562306a36Sopenharmony_ci		if (AUVI_INPUT(i).audio_setup == NULL)
139662306a36Sopenharmony_ci			continue;
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci		if (i == index)
139962306a36Sopenharmony_ci			enable = 1;
140062306a36Sopenharmony_ci		else
140162306a36Sopenharmony_ci			enable = 0;
140262306a36Sopenharmony_ci		if (enable) {
140362306a36Sopenharmony_ci			(AUVI_INPUT(i).audio_setup)(dev, enable);
140462306a36Sopenharmony_ci		} else {
140562306a36Sopenharmony_ci			/* Make sure we leave it turned on if some
140662306a36Sopenharmony_ci			   other input is routed to this callback */
140762306a36Sopenharmony_ci			if ((AUVI_INPUT(i).audio_setup) !=
140862306a36Sopenharmony_ci			    ((AUVI_INPUT(index).audio_setup))) {
140962306a36Sopenharmony_ci				(AUVI_INPUT(i).audio_setup)(dev, enable);
141062306a36Sopenharmony_ci			}
141162306a36Sopenharmony_ci		}
141262306a36Sopenharmony_ci	}
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing,
141562306a36Sopenharmony_ci			AUVI_INPUT(index).amux, 0, 0);
141662306a36Sopenharmony_ci}
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_cistatic int vidioc_s_input(struct file *file, void *priv, unsigned int index)
141962306a36Sopenharmony_ci{
142062306a36Sopenharmony_ci	struct au0828_dev *dev = video_drvdata(file);
142162306a36Sopenharmony_ci	struct video_device *vfd = video_devdata(file);
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	dprintk(1, "VIDIOC_S_INPUT in function %s, input=%d\n", __func__,
142462306a36Sopenharmony_ci		index);
142562306a36Sopenharmony_ci	if (index >= AU0828_MAX_INPUT)
142662306a36Sopenharmony_ci		return -EINVAL;
142762306a36Sopenharmony_ci	if (AUVI_INPUT(index).type == 0)
142862306a36Sopenharmony_ci		return -EINVAL;
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	if (dev->ctrl_input == index)
143162306a36Sopenharmony_ci		return 0;
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	au0828_s_input(dev, index);
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	/*
143662306a36Sopenharmony_ci	 * Input has been changed. Disable the media source
143762306a36Sopenharmony_ci	 * associated with the old input and enable source
143862306a36Sopenharmony_ci	 * for the newly set input
143962306a36Sopenharmony_ci	 */
144062306a36Sopenharmony_ci	v4l_disable_media_source(vfd);
144162306a36Sopenharmony_ci	return v4l_enable_media_source(vfd);
144262306a36Sopenharmony_ci}
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_cistatic int vidioc_enumaudio(struct file *file, void *priv, struct v4l2_audio *a)
144562306a36Sopenharmony_ci{
144662306a36Sopenharmony_ci	if (a->index > 1)
144762306a36Sopenharmony_ci		return -EINVAL;
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	dprintk(1, "%s called\n", __func__);
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	if (a->index == 0)
145262306a36Sopenharmony_ci		strscpy(a->name, "Television", sizeof(a->name));
145362306a36Sopenharmony_ci	else
145462306a36Sopenharmony_ci		strscpy(a->name, "Line in", sizeof(a->name));
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	a->capability = V4L2_AUDCAP_STEREO;
145762306a36Sopenharmony_ci	return 0;
145862306a36Sopenharmony_ci}
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_cistatic int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
146162306a36Sopenharmony_ci{
146262306a36Sopenharmony_ci	struct au0828_dev *dev = video_drvdata(file);
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
146562306a36Sopenharmony_ci		dev->std_set_in_tuner_core, dev->dev_state);
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	a->index = dev->ctrl_ainput;
146862306a36Sopenharmony_ci	if (a->index == 0)
146962306a36Sopenharmony_ci		strscpy(a->name, "Television", sizeof(a->name));
147062306a36Sopenharmony_ci	else
147162306a36Sopenharmony_ci		strscpy(a->name, "Line in", sizeof(a->name));
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	a->capability = V4L2_AUDCAP_STEREO;
147462306a36Sopenharmony_ci	return 0;
147562306a36Sopenharmony_ci}
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_cistatic int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *a)
147862306a36Sopenharmony_ci{
147962306a36Sopenharmony_ci	struct au0828_dev *dev = video_drvdata(file);
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	if (a->index != dev->ctrl_ainput)
148262306a36Sopenharmony_ci		return -EINVAL;
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
148562306a36Sopenharmony_ci		dev->std_set_in_tuner_core, dev->dev_state);
148662306a36Sopenharmony_ci	return 0;
148762306a36Sopenharmony_ci}
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_cistatic int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
149062306a36Sopenharmony_ci{
149162306a36Sopenharmony_ci	struct au0828_dev *dev = video_drvdata(file);
149262306a36Sopenharmony_ci	struct video_device *vfd = video_devdata(file);
149362306a36Sopenharmony_ci	int ret;
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	if (t->index != 0)
149662306a36Sopenharmony_ci		return -EINVAL;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	ret = v4l_enable_media_source(vfd);
149962306a36Sopenharmony_ci	if (ret)
150062306a36Sopenharmony_ci		return ret;
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
150362306a36Sopenharmony_ci		dev->std_set_in_tuner_core, dev->dev_state);
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	strscpy(t->name, "Auvitek tuner", sizeof(t->name));
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	au0828_init_tuner(dev);
150862306a36Sopenharmony_ci	i2c_gate_ctrl(dev, 1);
150962306a36Sopenharmony_ci	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
151062306a36Sopenharmony_ci	i2c_gate_ctrl(dev, 0);
151162306a36Sopenharmony_ci	return 0;
151262306a36Sopenharmony_ci}
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_cistatic int vidioc_s_tuner(struct file *file, void *priv,
151562306a36Sopenharmony_ci				const struct v4l2_tuner *t)
151662306a36Sopenharmony_ci{
151762306a36Sopenharmony_ci	struct au0828_dev *dev = video_drvdata(file);
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	if (t->index != 0)
152062306a36Sopenharmony_ci		return -EINVAL;
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
152362306a36Sopenharmony_ci		dev->std_set_in_tuner_core, dev->dev_state);
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	au0828_init_tuner(dev);
152662306a36Sopenharmony_ci	i2c_gate_ctrl(dev, 1);
152762306a36Sopenharmony_ci	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
152862306a36Sopenharmony_ci	i2c_gate_ctrl(dev, 0);
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	dprintk(1, "VIDIOC_S_TUNER: signal = %x, afc = %x\n", t->signal,
153162306a36Sopenharmony_ci		t->afc);
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	return 0;
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci}
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_cistatic int vidioc_g_frequency(struct file *file, void *priv,
153862306a36Sopenharmony_ci				struct v4l2_frequency *freq)
153962306a36Sopenharmony_ci{
154062306a36Sopenharmony_ci	struct au0828_dev *dev = video_drvdata(file);
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	if (freq->tuner != 0)
154362306a36Sopenharmony_ci		return -EINVAL;
154462306a36Sopenharmony_ci	dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
154562306a36Sopenharmony_ci		dev->std_set_in_tuner_core, dev->dev_state);
154662306a36Sopenharmony_ci	freq->frequency = dev->ctrl_freq;
154762306a36Sopenharmony_ci	return 0;
154862306a36Sopenharmony_ci}
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_cistatic int vidioc_s_frequency(struct file *file, void *priv,
155162306a36Sopenharmony_ci				const struct v4l2_frequency *freq)
155262306a36Sopenharmony_ci{
155362306a36Sopenharmony_ci	struct au0828_dev *dev = video_drvdata(file);
155462306a36Sopenharmony_ci	struct v4l2_frequency new_freq = *freq;
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	if (freq->tuner != 0)
155762306a36Sopenharmony_ci		return -EINVAL;
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
156062306a36Sopenharmony_ci		dev->std_set_in_tuner_core, dev->dev_state);
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	au0828_init_tuner(dev);
156362306a36Sopenharmony_ci	i2c_gate_ctrl(dev, 1);
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, freq);
156662306a36Sopenharmony_ci	/* Get the actual set (and possibly clamped) frequency */
156762306a36Sopenharmony_ci	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, &new_freq);
156862306a36Sopenharmony_ci	dev->ctrl_freq = new_freq.frequency;
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	i2c_gate_ctrl(dev, 0);
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	au0828_analog_stream_reset(dev);
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci	return 0;
157562306a36Sopenharmony_ci}
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci/* RAW VBI ioctls */
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_cistatic int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
158162306a36Sopenharmony_ci				struct v4l2_format *format)
158262306a36Sopenharmony_ci{
158362306a36Sopenharmony_ci	struct au0828_dev *dev = video_drvdata(file);
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
158662306a36Sopenharmony_ci		dev->std_set_in_tuner_core, dev->dev_state);
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	format->fmt.vbi.samples_per_line = dev->vbi_width;
158962306a36Sopenharmony_ci	format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
159062306a36Sopenharmony_ci	format->fmt.vbi.offset = 0;
159162306a36Sopenharmony_ci	format->fmt.vbi.flags = 0;
159262306a36Sopenharmony_ci	format->fmt.vbi.sampling_rate = 6750000 * 4 / 2;
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	format->fmt.vbi.count[0] = dev->vbi_height;
159562306a36Sopenharmony_ci	format->fmt.vbi.count[1] = dev->vbi_height;
159662306a36Sopenharmony_ci	format->fmt.vbi.start[0] = 21;
159762306a36Sopenharmony_ci	format->fmt.vbi.start[1] = 284;
159862306a36Sopenharmony_ci	memset(format->fmt.vbi.reserved, 0, sizeof(format->fmt.vbi.reserved));
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci	return 0;
160162306a36Sopenharmony_ci}
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_cistatic int vidioc_g_pixelaspect(struct file *file, void *priv,
160462306a36Sopenharmony_ci				int type, struct v4l2_fract *f)
160562306a36Sopenharmony_ci{
160662306a36Sopenharmony_ci	struct au0828_dev *dev = video_drvdata(file);
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
160962306a36Sopenharmony_ci		return -EINVAL;
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
161262306a36Sopenharmony_ci		dev->std_set_in_tuner_core, dev->dev_state);
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	f->numerator = 54;
161562306a36Sopenharmony_ci	f->denominator = 59;
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	return 0;
161862306a36Sopenharmony_ci}
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_cistatic int vidioc_g_selection(struct file *file, void *priv,
162162306a36Sopenharmony_ci			      struct v4l2_selection *s)
162262306a36Sopenharmony_ci{
162362306a36Sopenharmony_ci	struct au0828_dev *dev = video_drvdata(file);
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
162662306a36Sopenharmony_ci		return -EINVAL;
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	switch (s->target) {
162962306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP_BOUNDS:
163062306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP_DEFAULT:
163162306a36Sopenharmony_ci		s->r.left = 0;
163262306a36Sopenharmony_ci		s->r.top = 0;
163362306a36Sopenharmony_ci		s->r.width = dev->width;
163462306a36Sopenharmony_ci		s->r.height = dev->height;
163562306a36Sopenharmony_ci		break;
163662306a36Sopenharmony_ci	default:
163762306a36Sopenharmony_ci		return -EINVAL;
163862306a36Sopenharmony_ci	}
163962306a36Sopenharmony_ci	return 0;
164062306a36Sopenharmony_ci}
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG
164362306a36Sopenharmony_cistatic int vidioc_g_register(struct file *file, void *priv,
164462306a36Sopenharmony_ci			     struct v4l2_dbg_register *reg)
164562306a36Sopenharmony_ci{
164662306a36Sopenharmony_ci	struct au0828_dev *dev = video_drvdata(file);
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
164962306a36Sopenharmony_ci		dev->std_set_in_tuner_core, dev->dev_state);
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	reg->val = au0828_read(dev, reg->reg);
165262306a36Sopenharmony_ci	reg->size = 1;
165362306a36Sopenharmony_ci	return 0;
165462306a36Sopenharmony_ci}
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_cistatic int vidioc_s_register(struct file *file, void *priv,
165762306a36Sopenharmony_ci			     const struct v4l2_dbg_register *reg)
165862306a36Sopenharmony_ci{
165962306a36Sopenharmony_ci	struct au0828_dev *dev = video_drvdata(file);
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
166262306a36Sopenharmony_ci		dev->std_set_in_tuner_core, dev->dev_state);
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci	return au0828_writereg(dev, reg->reg, reg->val);
166562306a36Sopenharmony_ci}
166662306a36Sopenharmony_ci#endif
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_cistatic int vidioc_log_status(struct file *file, void *fh)
166962306a36Sopenharmony_ci{
167062306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	dprintk(1, "%s called\n", __func__);
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci	v4l2_ctrl_log_status(file, fh);
167562306a36Sopenharmony_ci	v4l2_device_call_all(vdev->v4l2_dev, 0, core, log_status);
167662306a36Sopenharmony_ci	return 0;
167762306a36Sopenharmony_ci}
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_civoid au0828_v4l2_suspend(struct au0828_dev *dev)
168062306a36Sopenharmony_ci{
168162306a36Sopenharmony_ci	struct urb *urb;
168262306a36Sopenharmony_ci	int i;
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci	pr_info("stopping V4L2\n");
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	if (dev->stream_state == STREAM_ON) {
168762306a36Sopenharmony_ci		pr_info("stopping V4L2 active URBs\n");
168862306a36Sopenharmony_ci		au0828_analog_stream_disable(dev);
168962306a36Sopenharmony_ci		/* stop urbs */
169062306a36Sopenharmony_ci		for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
169162306a36Sopenharmony_ci			urb = dev->isoc_ctl.urb[i];
169262306a36Sopenharmony_ci			if (urb) {
169362306a36Sopenharmony_ci				if (!irqs_disabled())
169462306a36Sopenharmony_ci					usb_kill_urb(urb);
169562306a36Sopenharmony_ci				else
169662306a36Sopenharmony_ci					usb_unlink_urb(urb);
169762306a36Sopenharmony_ci			}
169862306a36Sopenharmony_ci		}
169962306a36Sopenharmony_ci	}
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	if (dev->vid_timeout_running)
170262306a36Sopenharmony_ci		del_timer_sync(&dev->vid_timeout);
170362306a36Sopenharmony_ci	if (dev->vbi_timeout_running)
170462306a36Sopenharmony_ci		del_timer_sync(&dev->vbi_timeout);
170562306a36Sopenharmony_ci}
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_civoid au0828_v4l2_resume(struct au0828_dev *dev)
170862306a36Sopenharmony_ci{
170962306a36Sopenharmony_ci	int i, rc;
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci	pr_info("restarting V4L2\n");
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	if (dev->stream_state == STREAM_ON) {
171462306a36Sopenharmony_ci		au0828_stream_interrupt(dev);
171562306a36Sopenharmony_ci		au0828_init_tuner(dev);
171662306a36Sopenharmony_ci	}
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	if (dev->vid_timeout_running)
171962306a36Sopenharmony_ci		mod_timer(&dev->vid_timeout, jiffies + (HZ / 10));
172062306a36Sopenharmony_ci	if (dev->vbi_timeout_running)
172162306a36Sopenharmony_ci		mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10));
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci	/* If we were doing ac97 instead of i2s, it would go here...*/
172462306a36Sopenharmony_ci	au0828_i2s_init(dev);
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	au0828_analog_stream_enable(dev);
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	if (!(dev->stream_state == STREAM_ON)) {
172962306a36Sopenharmony_ci		au0828_analog_stream_reset(dev);
173062306a36Sopenharmony_ci		/* submit urbs */
173162306a36Sopenharmony_ci		for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
173262306a36Sopenharmony_ci			rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC);
173362306a36Sopenharmony_ci			if (rc) {
173462306a36Sopenharmony_ci				au0828_isocdbg("submit of urb %i failed (error=%i)\n",
173562306a36Sopenharmony_ci					       i, rc);
173662306a36Sopenharmony_ci				au0828_uninit_isoc(dev);
173762306a36Sopenharmony_ci			}
173862306a36Sopenharmony_ci		}
173962306a36Sopenharmony_ci	}
174062306a36Sopenharmony_ci}
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_cistatic const struct v4l2_file_operations au0828_v4l_fops = {
174362306a36Sopenharmony_ci	.owner      = THIS_MODULE,
174462306a36Sopenharmony_ci	.open       = au0828_v4l2_open,
174562306a36Sopenharmony_ci	.release    = au0828_v4l2_close,
174662306a36Sopenharmony_ci	.read       = vb2_fop_read,
174762306a36Sopenharmony_ci	.poll       = vb2_fop_poll,
174862306a36Sopenharmony_ci	.mmap       = vb2_fop_mmap,
174962306a36Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,
175062306a36Sopenharmony_ci};
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops video_ioctl_ops = {
175362306a36Sopenharmony_ci	.vidioc_querycap            = vidioc_querycap,
175462306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_cap    = vidioc_enum_fmt_vid_cap,
175562306a36Sopenharmony_ci	.vidioc_g_fmt_vid_cap       = vidioc_g_fmt_vid_cap,
175662306a36Sopenharmony_ci	.vidioc_try_fmt_vid_cap     = vidioc_try_fmt_vid_cap,
175762306a36Sopenharmony_ci	.vidioc_s_fmt_vid_cap       = vidioc_s_fmt_vid_cap,
175862306a36Sopenharmony_ci	.vidioc_g_fmt_vbi_cap       = vidioc_g_fmt_vbi_cap,
175962306a36Sopenharmony_ci	.vidioc_try_fmt_vbi_cap     = vidioc_g_fmt_vbi_cap,
176062306a36Sopenharmony_ci	.vidioc_s_fmt_vbi_cap       = vidioc_g_fmt_vbi_cap,
176162306a36Sopenharmony_ci	.vidioc_enumaudio           = vidioc_enumaudio,
176262306a36Sopenharmony_ci	.vidioc_g_audio             = vidioc_g_audio,
176362306a36Sopenharmony_ci	.vidioc_s_audio             = vidioc_s_audio,
176462306a36Sopenharmony_ci	.vidioc_g_pixelaspect       = vidioc_g_pixelaspect,
176562306a36Sopenharmony_ci	.vidioc_g_selection         = vidioc_g_selection,
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	.vidioc_reqbufs             = vb2_ioctl_reqbufs,
176862306a36Sopenharmony_ci	.vidioc_create_bufs         = vb2_ioctl_create_bufs,
176962306a36Sopenharmony_ci	.vidioc_prepare_buf         = vb2_ioctl_prepare_buf,
177062306a36Sopenharmony_ci	.vidioc_querybuf            = vb2_ioctl_querybuf,
177162306a36Sopenharmony_ci	.vidioc_qbuf                = vb2_ioctl_qbuf,
177262306a36Sopenharmony_ci	.vidioc_dqbuf               = vb2_ioctl_dqbuf,
177362306a36Sopenharmony_ci	.vidioc_expbuf               = vb2_ioctl_expbuf,
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci	.vidioc_s_std               = vidioc_s_std,
177662306a36Sopenharmony_ci	.vidioc_g_std               = vidioc_g_std,
177762306a36Sopenharmony_ci	.vidioc_enum_input          = vidioc_enum_input,
177862306a36Sopenharmony_ci	.vidioc_g_input             = vidioc_g_input,
177962306a36Sopenharmony_ci	.vidioc_s_input             = vidioc_s_input,
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci	.vidioc_streamon            = vb2_ioctl_streamon,
178262306a36Sopenharmony_ci	.vidioc_streamoff           = vb2_ioctl_streamoff,
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	.vidioc_g_tuner             = vidioc_g_tuner,
178562306a36Sopenharmony_ci	.vidioc_s_tuner             = vidioc_s_tuner,
178662306a36Sopenharmony_ci	.vidioc_g_frequency         = vidioc_g_frequency,
178762306a36Sopenharmony_ci	.vidioc_s_frequency         = vidioc_s_frequency,
178862306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG
178962306a36Sopenharmony_ci	.vidioc_g_register          = vidioc_g_register,
179062306a36Sopenharmony_ci	.vidioc_s_register          = vidioc_s_register,
179162306a36Sopenharmony_ci#endif
179262306a36Sopenharmony_ci	.vidioc_log_status	    = vidioc_log_status,
179362306a36Sopenharmony_ci	.vidioc_subscribe_event     = v4l2_ctrl_subscribe_event,
179462306a36Sopenharmony_ci	.vidioc_unsubscribe_event   = v4l2_event_unsubscribe,
179562306a36Sopenharmony_ci};
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_cistatic const struct video_device au0828_video_template = {
179862306a36Sopenharmony_ci	.fops                       = &au0828_v4l_fops,
179962306a36Sopenharmony_ci	.release                    = video_device_release_empty,
180062306a36Sopenharmony_ci	.ioctl_ops		    = &video_ioctl_ops,
180162306a36Sopenharmony_ci	.tvnorms                    = V4L2_STD_NTSC_M | V4L2_STD_PAL_M,
180262306a36Sopenharmony_ci};
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_cistatic int au0828_vb2_setup(struct au0828_dev *dev)
180562306a36Sopenharmony_ci{
180662306a36Sopenharmony_ci	int rc;
180762306a36Sopenharmony_ci	struct vb2_queue *q;
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	/* Setup Videobuf2 for Video capture */
181062306a36Sopenharmony_ci	q = &dev->vb_vidq;
181162306a36Sopenharmony_ci	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
181262306a36Sopenharmony_ci	q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
181362306a36Sopenharmony_ci	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
181462306a36Sopenharmony_ci	q->drv_priv = dev;
181562306a36Sopenharmony_ci	q->buf_struct_size = sizeof(struct au0828_buffer);
181662306a36Sopenharmony_ci	q->ops = &au0828_video_qops;
181762306a36Sopenharmony_ci	q->mem_ops = &vb2_vmalloc_memops;
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	rc = vb2_queue_init(q);
182062306a36Sopenharmony_ci	if (rc < 0)
182162306a36Sopenharmony_ci		return rc;
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_ci	/* Setup Videobuf2 for VBI capture */
182462306a36Sopenharmony_ci	q = &dev->vb_vbiq;
182562306a36Sopenharmony_ci	q->type = V4L2_BUF_TYPE_VBI_CAPTURE;
182662306a36Sopenharmony_ci	q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
182762306a36Sopenharmony_ci	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
182862306a36Sopenharmony_ci	q->drv_priv = dev;
182962306a36Sopenharmony_ci	q->buf_struct_size = sizeof(struct au0828_buffer);
183062306a36Sopenharmony_ci	q->ops = &au0828_vbi_qops;
183162306a36Sopenharmony_ci	q->mem_ops = &vb2_vmalloc_memops;
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	rc = vb2_queue_init(q);
183462306a36Sopenharmony_ci	if (rc < 0)
183562306a36Sopenharmony_ci		return rc;
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	return 0;
183862306a36Sopenharmony_ci}
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_cistatic void au0828_analog_create_entities(struct au0828_dev *dev)
184162306a36Sopenharmony_ci{
184262306a36Sopenharmony_ci#if defined(CONFIG_MEDIA_CONTROLLER)
184362306a36Sopenharmony_ci	static const char * const inames[] = {
184462306a36Sopenharmony_ci		[AU0828_VMUX_COMPOSITE] = "Composite",
184562306a36Sopenharmony_ci		[AU0828_VMUX_SVIDEO] = "S-Video",
184662306a36Sopenharmony_ci		[AU0828_VMUX_CABLE] = "Cable TV",
184762306a36Sopenharmony_ci		[AU0828_VMUX_TELEVISION] = "Television",
184862306a36Sopenharmony_ci		[AU0828_VMUX_DVB] = "DVB",
184962306a36Sopenharmony_ci	};
185062306a36Sopenharmony_ci	int ret, i;
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	/* Initialize Video and VBI pads */
185362306a36Sopenharmony_ci	dev->video_pad.flags = MEDIA_PAD_FL_SINK;
185462306a36Sopenharmony_ci	ret = media_entity_pads_init(&dev->vdev.entity, 1, &dev->video_pad);
185562306a36Sopenharmony_ci	if (ret < 0)
185662306a36Sopenharmony_ci		pr_err("failed to initialize video media entity!\n");
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci	dev->vbi_pad.flags = MEDIA_PAD_FL_SINK;
185962306a36Sopenharmony_ci	ret = media_entity_pads_init(&dev->vbi_dev.entity, 1, &dev->vbi_pad);
186062306a36Sopenharmony_ci	if (ret < 0)
186162306a36Sopenharmony_ci		pr_err("failed to initialize vbi media entity!\n");
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	/* Create entities for each input connector */
186462306a36Sopenharmony_ci	for (i = 0; i < AU0828_MAX_INPUT; i++) {
186562306a36Sopenharmony_ci		struct media_entity *ent = &dev->input_ent[i];
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci		if (AUVI_INPUT(i).type == AU0828_VMUX_UNDEFINED)
186862306a36Sopenharmony_ci			break;
186962306a36Sopenharmony_ci
187062306a36Sopenharmony_ci		ent->name = inames[AUVI_INPUT(i).type];
187162306a36Sopenharmony_ci		ent->flags = MEDIA_ENT_FL_CONNECTOR;
187262306a36Sopenharmony_ci		dev->input_pad[i].flags = MEDIA_PAD_FL_SOURCE;
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci		switch (AUVI_INPUT(i).type) {
187562306a36Sopenharmony_ci		case AU0828_VMUX_COMPOSITE:
187662306a36Sopenharmony_ci			ent->function = MEDIA_ENT_F_CONN_COMPOSITE;
187762306a36Sopenharmony_ci			break;
187862306a36Sopenharmony_ci		case AU0828_VMUX_SVIDEO:
187962306a36Sopenharmony_ci			ent->function = MEDIA_ENT_F_CONN_SVIDEO;
188062306a36Sopenharmony_ci			break;
188162306a36Sopenharmony_ci		case AU0828_VMUX_CABLE:
188262306a36Sopenharmony_ci		case AU0828_VMUX_TELEVISION:
188362306a36Sopenharmony_ci		case AU0828_VMUX_DVB:
188462306a36Sopenharmony_ci		default: /* Just to shut up a warning */
188562306a36Sopenharmony_ci			ent->function = MEDIA_ENT_F_CONN_RF;
188662306a36Sopenharmony_ci			break;
188762306a36Sopenharmony_ci		}
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci		ret = media_entity_pads_init(ent, 1, &dev->input_pad[i]);
189062306a36Sopenharmony_ci		if (ret < 0)
189162306a36Sopenharmony_ci			pr_err("failed to initialize input pad[%d]!\n", i);
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci		ret = media_device_register_entity(dev->media_dev, ent);
189462306a36Sopenharmony_ci		if (ret < 0)
189562306a36Sopenharmony_ci			pr_err("failed to register input entity %d!\n", i);
189662306a36Sopenharmony_ci	}
189762306a36Sopenharmony_ci#endif
189862306a36Sopenharmony_ci}
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_ci/**************************************************************************/
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ciint au0828_analog_register(struct au0828_dev *dev,
190362306a36Sopenharmony_ci			   struct usb_interface *interface)
190462306a36Sopenharmony_ci{
190562306a36Sopenharmony_ci	int retval = -ENOMEM;
190662306a36Sopenharmony_ci	struct usb_host_interface *iface_desc;
190762306a36Sopenharmony_ci	struct usb_endpoint_descriptor *endpoint;
190862306a36Sopenharmony_ci	int i, ret;
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	dprintk(1, "au0828_analog_register called for intf#%d!\n",
191162306a36Sopenharmony_ci		interface->cur_altsetting->desc.bInterfaceNumber);
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	/* No analog TV */
191462306a36Sopenharmony_ci	if (AUVI_INPUT(0).type == AU0828_VMUX_UNDEFINED)
191562306a36Sopenharmony_ci		return 0;
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_ci	/* set au0828 usb interface0 to as5 */
191862306a36Sopenharmony_ci	retval = usb_set_interface(dev->usbdev,
191962306a36Sopenharmony_ci			interface->cur_altsetting->desc.bInterfaceNumber, 5);
192062306a36Sopenharmony_ci	if (retval != 0) {
192162306a36Sopenharmony_ci		pr_info("Failure setting usb interface0 to as5\n");
192262306a36Sopenharmony_ci		return retval;
192362306a36Sopenharmony_ci	}
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_ci	/* Figure out which endpoint has the isoc interface */
192662306a36Sopenharmony_ci	iface_desc = interface->cur_altsetting;
192762306a36Sopenharmony_ci	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
192862306a36Sopenharmony_ci		endpoint = &iface_desc->endpoint[i].desc;
192962306a36Sopenharmony_ci		if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
193062306a36Sopenharmony_ci		     == USB_DIR_IN) &&
193162306a36Sopenharmony_ci		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
193262306a36Sopenharmony_ci		     == USB_ENDPOINT_XFER_ISOC)) {
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci			/* we find our isoc in endpoint */
193562306a36Sopenharmony_ci			u16 tmp = le16_to_cpu(endpoint->wMaxPacketSize);
193662306a36Sopenharmony_ci			dev->max_pkt_size = (tmp & 0x07ff) *
193762306a36Sopenharmony_ci				(((tmp & 0x1800) >> 11) + 1);
193862306a36Sopenharmony_ci			dev->isoc_in_endpointaddr = endpoint->bEndpointAddress;
193962306a36Sopenharmony_ci			dprintk(1,
194062306a36Sopenharmony_ci				"Found isoc endpoint 0x%02x, max size = %d\n",
194162306a36Sopenharmony_ci				dev->isoc_in_endpointaddr, dev->max_pkt_size);
194262306a36Sopenharmony_ci		}
194362306a36Sopenharmony_ci	}
194462306a36Sopenharmony_ci	if (!(dev->isoc_in_endpointaddr)) {
194562306a36Sopenharmony_ci		pr_info("Could not locate isoc endpoint\n");
194662306a36Sopenharmony_ci		return -ENODEV;
194762306a36Sopenharmony_ci	}
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci	init_waitqueue_head(&dev->open);
195062306a36Sopenharmony_ci	spin_lock_init(&dev->slock);
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_ci	/* init video dma queues */
195362306a36Sopenharmony_ci	INIT_LIST_HEAD(&dev->vidq.active);
195462306a36Sopenharmony_ci	INIT_LIST_HEAD(&dev->vbiq.active);
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_ci	timer_setup(&dev->vid_timeout, au0828_vid_buffer_timeout, 0);
195762306a36Sopenharmony_ci	timer_setup(&dev->vbi_timeout, au0828_vbi_buffer_timeout, 0);
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci	dev->width = NTSC_STD_W;
196062306a36Sopenharmony_ci	dev->height = NTSC_STD_H;
196162306a36Sopenharmony_ci	dev->field_size = dev->width * dev->height;
196262306a36Sopenharmony_ci	dev->frame_size = dev->field_size << 1;
196362306a36Sopenharmony_ci	dev->bytesperline = dev->width << 1;
196462306a36Sopenharmony_ci	dev->vbi_width = 720;
196562306a36Sopenharmony_ci	dev->vbi_height = 1;
196662306a36Sopenharmony_ci	dev->ctrl_ainput = 0;
196762306a36Sopenharmony_ci	dev->ctrl_freq = 960;
196862306a36Sopenharmony_ci	dev->std = V4L2_STD_NTSC_M;
196962306a36Sopenharmony_ci	/* Default input is TV Tuner */
197062306a36Sopenharmony_ci	au0828_s_input(dev, 0);
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci	mutex_init(&dev->vb_queue_lock);
197362306a36Sopenharmony_ci	mutex_init(&dev->vb_vbi_queue_lock);
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci	/* Fill the video capture device struct */
197662306a36Sopenharmony_ci	dev->vdev = au0828_video_template;
197762306a36Sopenharmony_ci	dev->vdev.v4l2_dev = &dev->v4l2_dev;
197862306a36Sopenharmony_ci	dev->vdev.lock = &dev->lock;
197962306a36Sopenharmony_ci	dev->vdev.queue = &dev->vb_vidq;
198062306a36Sopenharmony_ci	dev->vdev.queue->lock = &dev->vb_queue_lock;
198162306a36Sopenharmony_ci	dev->vdev.device_caps =
198262306a36Sopenharmony_ci		V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
198362306a36Sopenharmony_ci		V4L2_CAP_TUNER | V4L2_CAP_VIDEO_CAPTURE;
198462306a36Sopenharmony_ci	strscpy(dev->vdev.name, "au0828a video", sizeof(dev->vdev.name));
198562306a36Sopenharmony_ci
198662306a36Sopenharmony_ci	/* Setup the VBI device */
198762306a36Sopenharmony_ci	dev->vbi_dev = au0828_video_template;
198862306a36Sopenharmony_ci	dev->vbi_dev.v4l2_dev = &dev->v4l2_dev;
198962306a36Sopenharmony_ci	dev->vbi_dev.lock = &dev->lock;
199062306a36Sopenharmony_ci	dev->vbi_dev.queue = &dev->vb_vbiq;
199162306a36Sopenharmony_ci	dev->vbi_dev.queue->lock = &dev->vb_vbi_queue_lock;
199262306a36Sopenharmony_ci	dev->vbi_dev.device_caps =
199362306a36Sopenharmony_ci		V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
199462306a36Sopenharmony_ci		V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE;
199562306a36Sopenharmony_ci	strscpy(dev->vbi_dev.name, "au0828a vbi", sizeof(dev->vbi_dev.name));
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci	/* Init entities at the Media Controller */
199862306a36Sopenharmony_ci	au0828_analog_create_entities(dev);
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_ci	/* initialize videobuf2 stuff */
200162306a36Sopenharmony_ci	retval = au0828_vb2_setup(dev);
200262306a36Sopenharmony_ci	if (retval != 0) {
200362306a36Sopenharmony_ci		dprintk(1, "unable to setup videobuf2 queues (error = %d).\n",
200462306a36Sopenharmony_ci			retval);
200562306a36Sopenharmony_ci		return -ENODEV;
200662306a36Sopenharmony_ci	}
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci	/* Register the v4l2 device */
200962306a36Sopenharmony_ci	video_set_drvdata(&dev->vdev, dev);
201062306a36Sopenharmony_ci	retval = video_register_device(&dev->vdev, VFL_TYPE_VIDEO, -1);
201162306a36Sopenharmony_ci	if (retval != 0) {
201262306a36Sopenharmony_ci		dprintk(1, "unable to register video device (error = %d).\n",
201362306a36Sopenharmony_ci			retval);
201462306a36Sopenharmony_ci		return -ENODEV;
201562306a36Sopenharmony_ci	}
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	/* Register the vbi device */
201862306a36Sopenharmony_ci	video_set_drvdata(&dev->vbi_dev, dev);
201962306a36Sopenharmony_ci	retval = video_register_device(&dev->vbi_dev, VFL_TYPE_VBI, -1);
202062306a36Sopenharmony_ci	if (retval != 0) {
202162306a36Sopenharmony_ci		dprintk(1, "unable to register vbi device (error = %d).\n",
202262306a36Sopenharmony_ci			retval);
202362306a36Sopenharmony_ci		ret = -ENODEV;
202462306a36Sopenharmony_ci		goto err_reg_vbi_dev;
202562306a36Sopenharmony_ci	}
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER
202862306a36Sopenharmony_ci	retval = v4l2_mc_create_media_graph(dev->media_dev);
202962306a36Sopenharmony_ci	if (retval) {
203062306a36Sopenharmony_ci		pr_err("%s() au0282_dev_register failed to create graph\n",
203162306a36Sopenharmony_ci			__func__);
203262306a36Sopenharmony_ci		ret = -ENODEV;
203362306a36Sopenharmony_ci		goto err_reg_vbi_dev;
203462306a36Sopenharmony_ci	}
203562306a36Sopenharmony_ci#endif
203662306a36Sopenharmony_ci
203762306a36Sopenharmony_ci	dprintk(1, "%s completed!\n", __func__);
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ci	return 0;
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_cierr_reg_vbi_dev:
204262306a36Sopenharmony_ci	vb2_video_unregister_device(&dev->vdev);
204362306a36Sopenharmony_ci	return ret;
204462306a36Sopenharmony_ci}
204562306a36Sopenharmony_ci
2046