162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci   cx231xx-video.c - driver for Conexant Cx23100/101/102
462306a36Sopenharmony_ci		     USB video capture devices
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci   Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
762306a36Sopenharmony_ci	Based on em28xx driver
862306a36Sopenharmony_ci	Based on cx23885 driver
962306a36Sopenharmony_ci	Based on cx88 driver
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "cx231xx.h"
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci#include <linux/list.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/kernel.h>
1862306a36Sopenharmony_ci#include <linux/bitmap.h>
1962306a36Sopenharmony_ci#include <linux/i2c.h>
2062306a36Sopenharmony_ci#include <linux/mm.h>
2162306a36Sopenharmony_ci#include <linux/mutex.h>
2262306a36Sopenharmony_ci#include <linux/slab.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <media/v4l2-common.h>
2562306a36Sopenharmony_ci#include <media/v4l2-ioctl.h>
2662306a36Sopenharmony_ci#include <media/v4l2-event.h>
2762306a36Sopenharmony_ci#include <media/drv-intf/msp3400.h>
2862306a36Sopenharmony_ci#include <media/tuner.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include <media/dvb_frontend.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include "cx231xx-vbi.h"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define CX231XX_VERSION "0.0.3"
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define DRIVER_AUTHOR   "Srinivasa Deevi <srinivasa.deevi@conexant.com>"
3762306a36Sopenharmony_ci#define DRIVER_DESC     "Conexant cx231xx based USB video device driver"
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define cx231xx_videodbg(fmt, arg...) do {\
4062306a36Sopenharmony_ci	if (video_debug) \
4162306a36Sopenharmony_ci		printk(KERN_INFO "%s %s :"fmt, \
4262306a36Sopenharmony_ci			 dev->name, __func__ , ##arg); } while (0)
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic unsigned int isoc_debug;
4562306a36Sopenharmony_cimodule_param(isoc_debug, int, 0644);
4662306a36Sopenharmony_ciMODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]");
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define cx231xx_isocdbg(fmt, arg...) \
4962306a36Sopenharmony_cido {\
5062306a36Sopenharmony_ci	if (isoc_debug) { \
5162306a36Sopenharmony_ci		printk(KERN_INFO "%s %s :"fmt, \
5262306a36Sopenharmony_ci			 dev->name, __func__ , ##arg); \
5362306a36Sopenharmony_ci	} \
5462306a36Sopenharmony_ci  } while (0)
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
5762306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
5862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
5962306a36Sopenharmony_ciMODULE_VERSION(CX231XX_VERSION);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic unsigned int card[]     = {[0 ... (CX231XX_MAXBOARDS - 1)] = -1U };
6262306a36Sopenharmony_cistatic unsigned int video_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = -1U };
6362306a36Sopenharmony_cistatic unsigned int vbi_nr[]   = {[0 ... (CX231XX_MAXBOARDS - 1)] = -1U };
6462306a36Sopenharmony_cistatic unsigned int radio_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = -1U };
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cimodule_param_array(card, int, NULL, 0444);
6762306a36Sopenharmony_cimodule_param_array(video_nr, int, NULL, 0444);
6862306a36Sopenharmony_cimodule_param_array(vbi_nr, int, NULL, 0444);
6962306a36Sopenharmony_cimodule_param_array(radio_nr, int, NULL, 0444);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ciMODULE_PARM_DESC(card, "card type");
7262306a36Sopenharmony_ciMODULE_PARM_DESC(video_nr, "video device numbers");
7362306a36Sopenharmony_ciMODULE_PARM_DESC(vbi_nr, "vbi device numbers");
7462306a36Sopenharmony_ciMODULE_PARM_DESC(radio_nr, "radio device numbers");
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic unsigned int video_debug;
7762306a36Sopenharmony_cimodule_param(video_debug, int, 0644);
7862306a36Sopenharmony_ciMODULE_PARM_DESC(video_debug, "enable debug messages [video]");
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/* supported video standards */
8162306a36Sopenharmony_cistatic struct cx231xx_fmt format[] = {
8262306a36Sopenharmony_ci	{
8362306a36Sopenharmony_ci	 .fourcc = V4L2_PIX_FMT_YUYV,
8462306a36Sopenharmony_ci	 .depth = 16,
8562306a36Sopenharmony_ci	 .reg = 0,
8662306a36Sopenharmony_ci	 },
8762306a36Sopenharmony_ci};
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic int cx231xx_enable_analog_tuner(struct cx231xx *dev)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER
9362306a36Sopenharmony_ci	struct media_device *mdev = dev->media_dev;
9462306a36Sopenharmony_ci	struct media_entity  *entity, *decoder = NULL, *source;
9562306a36Sopenharmony_ci	struct media_link *link, *found_link = NULL;
9662306a36Sopenharmony_ci	int ret, active_links = 0;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (!mdev)
9962306a36Sopenharmony_ci		return 0;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	/*
10262306a36Sopenharmony_ci	 * This will find the tuner that is connected into the decoder.
10362306a36Sopenharmony_ci	 * Technically, this is not 100% correct, as the device may be
10462306a36Sopenharmony_ci	 * using an analog input instead of the tuner. However, as we can't
10562306a36Sopenharmony_ci	 * do DVB streaming while the DMA engine is being used for V4L2,
10662306a36Sopenharmony_ci	 * this should be enough for the actual needs.
10762306a36Sopenharmony_ci	 */
10862306a36Sopenharmony_ci	media_device_for_each_entity(entity, mdev) {
10962306a36Sopenharmony_ci		if (entity->function == MEDIA_ENT_F_ATV_DECODER) {
11062306a36Sopenharmony_ci			decoder = entity;
11162306a36Sopenharmony_ci			break;
11262306a36Sopenharmony_ci		}
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci	if (!decoder)
11562306a36Sopenharmony_ci		return 0;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	list_for_each_entry(link, &decoder->links, list) {
11862306a36Sopenharmony_ci		if (link->sink->entity == decoder) {
11962306a36Sopenharmony_ci			found_link = link;
12062306a36Sopenharmony_ci			if (link->flags & MEDIA_LNK_FL_ENABLED)
12162306a36Sopenharmony_ci				active_links++;
12262306a36Sopenharmony_ci			break;
12362306a36Sopenharmony_ci		}
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (active_links == 1 || !found_link)
12762306a36Sopenharmony_ci		return 0;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	source = found_link->source->entity;
13062306a36Sopenharmony_ci	list_for_each_entry(link, &source->links, list) {
13162306a36Sopenharmony_ci		struct media_entity *sink;
13262306a36Sopenharmony_ci		int flags = 0;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci		sink = link->sink->entity;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		if (sink == entity)
13762306a36Sopenharmony_ci			flags = MEDIA_LNK_FL_ENABLED;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci		ret = media_entity_setup_link(link, flags);
14062306a36Sopenharmony_ci		if (ret) {
14162306a36Sopenharmony_ci			dev_err(dev->dev,
14262306a36Sopenharmony_ci				"Couldn't change link %s->%s to %s. Error %d\n",
14362306a36Sopenharmony_ci				source->name, sink->name,
14462306a36Sopenharmony_ci				flags ? "enabled" : "disabled",
14562306a36Sopenharmony_ci				ret);
14662306a36Sopenharmony_ci			return ret;
14762306a36Sopenharmony_ci		} else
14862306a36Sopenharmony_ci			dev_dbg(dev->dev,
14962306a36Sopenharmony_ci				"link %s->%s was %s\n",
15062306a36Sopenharmony_ci				source->name, sink->name,
15162306a36Sopenharmony_ci				flags ? "ENABLED" : "disabled");
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci#endif
15462306a36Sopenharmony_ci	return 0;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci/* ------------------------------------------------------------------
15862306a36Sopenharmony_ci	Video buffer and parser functions
15962306a36Sopenharmony_ci   ------------------------------------------------------------------*/
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci/*
16262306a36Sopenharmony_ci * Announces that a buffer were filled and request the next
16362306a36Sopenharmony_ci */
16462306a36Sopenharmony_cistatic inline void buffer_filled(struct cx231xx *dev,
16562306a36Sopenharmony_ci				 struct cx231xx_dmaqueue *dma_q,
16662306a36Sopenharmony_ci				 struct cx231xx_buffer *buf)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	/* Advice that buffer was filled */
16962306a36Sopenharmony_ci	cx231xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.vb2_buf.index);
17062306a36Sopenharmony_ci	buf->vb.sequence = dma_q->sequence++;
17162306a36Sopenharmony_ci	buf->vb.field = V4L2_FIELD_INTERLACED;
17262306a36Sopenharmony_ci	buf->vb.vb2_buf.timestamp = ktime_get_ns();
17362306a36Sopenharmony_ci	vb2_set_plane_payload(&buf->vb.vb2_buf, 0, dev->size);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (dev->USE_ISO)
17662306a36Sopenharmony_ci		dev->video_mode.isoc_ctl.buf = NULL;
17762306a36Sopenharmony_ci	else
17862306a36Sopenharmony_ci		dev->video_mode.bulk_ctl.buf = NULL;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	list_del(&buf->list);
18162306a36Sopenharmony_ci	vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic inline void print_err_status(struct cx231xx *dev, int packet, int status)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	char *errmsg = "Unknown";
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	switch (status) {
18962306a36Sopenharmony_ci	case -ENOENT:
19062306a36Sopenharmony_ci		errmsg = "unlinked synchronously";
19162306a36Sopenharmony_ci		break;
19262306a36Sopenharmony_ci	case -ECONNRESET:
19362306a36Sopenharmony_ci		errmsg = "unlinked asynchronously";
19462306a36Sopenharmony_ci		break;
19562306a36Sopenharmony_ci	case -ENOSR:
19662306a36Sopenharmony_ci		errmsg = "Buffer error (overrun)";
19762306a36Sopenharmony_ci		break;
19862306a36Sopenharmony_ci	case -EPIPE:
19962306a36Sopenharmony_ci		errmsg = "Stalled (device not responding)";
20062306a36Sopenharmony_ci		break;
20162306a36Sopenharmony_ci	case -EOVERFLOW:
20262306a36Sopenharmony_ci		errmsg = "Babble (bad cable?)";
20362306a36Sopenharmony_ci		break;
20462306a36Sopenharmony_ci	case -EPROTO:
20562306a36Sopenharmony_ci		errmsg = "Bit-stuff error (bad cable?)";
20662306a36Sopenharmony_ci		break;
20762306a36Sopenharmony_ci	case -EILSEQ:
20862306a36Sopenharmony_ci		errmsg = "CRC/Timeout (could be anything)";
20962306a36Sopenharmony_ci		break;
21062306a36Sopenharmony_ci	case -ETIME:
21162306a36Sopenharmony_ci		errmsg = "Device does not respond";
21262306a36Sopenharmony_ci		break;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci	if (packet < 0) {
21562306a36Sopenharmony_ci		cx231xx_isocdbg("URB status %d [%s].\n", status, errmsg);
21662306a36Sopenharmony_ci	} else {
21762306a36Sopenharmony_ci		cx231xx_isocdbg("URB packet %d, status %d [%s].\n",
21862306a36Sopenharmony_ci				packet, status, errmsg);
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci/*
22362306a36Sopenharmony_ci * generic routine to get the next available buffer
22462306a36Sopenharmony_ci */
22562306a36Sopenharmony_cistatic inline void get_next_buf(struct cx231xx_dmaqueue *dma_q,
22662306a36Sopenharmony_ci				struct cx231xx_buffer **buf)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	struct cx231xx_video_mode *vmode =
22962306a36Sopenharmony_ci	    container_of(dma_q, struct cx231xx_video_mode, vidq);
23062306a36Sopenharmony_ci	struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	char *outp;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (list_empty(&dma_q->active)) {
23562306a36Sopenharmony_ci		cx231xx_isocdbg("No active queue to serve\n");
23662306a36Sopenharmony_ci		if (dev->USE_ISO)
23762306a36Sopenharmony_ci			dev->video_mode.isoc_ctl.buf = NULL;
23862306a36Sopenharmony_ci		else
23962306a36Sopenharmony_ci			dev->video_mode.bulk_ctl.buf = NULL;
24062306a36Sopenharmony_ci		*buf = NULL;
24162306a36Sopenharmony_ci		return;
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/* Get the next buffer */
24562306a36Sopenharmony_ci	*buf = list_entry(dma_q->active.next, struct cx231xx_buffer, list);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	/* Cleans up buffer - Useful for testing for frame/URB loss */
24862306a36Sopenharmony_ci	outp = vb2_plane_vaddr(&(*buf)->vb.vb2_buf, 0);
24962306a36Sopenharmony_ci	memset(outp, 0, dev->size);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (dev->USE_ISO)
25262306a36Sopenharmony_ci		dev->video_mode.isoc_ctl.buf = *buf;
25362306a36Sopenharmony_ci	else
25462306a36Sopenharmony_ci		dev->video_mode.bulk_ctl.buf = *buf;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	return;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci/*
26062306a36Sopenharmony_ci * Controls the isoc copy of each urb packet
26162306a36Sopenharmony_ci */
26262306a36Sopenharmony_cistatic inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct cx231xx_dmaqueue *dma_q = urb->context;
26562306a36Sopenharmony_ci	int i;
26662306a36Sopenharmony_ci	unsigned char *p_buffer;
26762306a36Sopenharmony_ci	u32 bytes_parsed = 0, buffer_size = 0;
26862306a36Sopenharmony_ci	u8 sav_eav = 0;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (!dev)
27162306a36Sopenharmony_ci		return 0;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	if (dev->state & DEV_DISCONNECTED)
27462306a36Sopenharmony_ci		return 0;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (urb->status < 0) {
27762306a36Sopenharmony_ci		print_err_status(dev, -1, urb->status);
27862306a36Sopenharmony_ci		if (urb->status == -ENOENT)
27962306a36Sopenharmony_ci			return 0;
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	for (i = 0; i < urb->number_of_packets; i++) {
28362306a36Sopenharmony_ci		int status = urb->iso_frame_desc[i].status;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci		if (status < 0) {
28662306a36Sopenharmony_ci			print_err_status(dev, i, status);
28762306a36Sopenharmony_ci			if (urb->iso_frame_desc[i].status != -EPROTO)
28862306a36Sopenharmony_ci				continue;
28962306a36Sopenharmony_ci		}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci		if (urb->iso_frame_desc[i].actual_length <= 0) {
29262306a36Sopenharmony_ci			/* cx231xx_isocdbg("packet %d is empty",i); - spammy */
29362306a36Sopenharmony_ci			continue;
29462306a36Sopenharmony_ci		}
29562306a36Sopenharmony_ci		if (urb->iso_frame_desc[i].actual_length >
29662306a36Sopenharmony_ci		    dev->video_mode.max_pkt_size) {
29762306a36Sopenharmony_ci			cx231xx_isocdbg("packet bigger than packet size");
29862306a36Sopenharmony_ci			continue;
29962306a36Sopenharmony_ci		}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci		/*  get buffer pointer and length */
30262306a36Sopenharmony_ci		p_buffer = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
30362306a36Sopenharmony_ci		buffer_size = urb->iso_frame_desc[i].actual_length;
30462306a36Sopenharmony_ci		bytes_parsed = 0;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci		if (dma_q->is_partial_line) {
30762306a36Sopenharmony_ci			/* Handle the case of a partial line */
30862306a36Sopenharmony_ci			sav_eav = dma_q->last_sav;
30962306a36Sopenharmony_ci		} else {
31062306a36Sopenharmony_ci			/* Check for a SAV/EAV overlapping
31162306a36Sopenharmony_ci				the buffer boundary */
31262306a36Sopenharmony_ci			sav_eav =
31362306a36Sopenharmony_ci			    cx231xx_find_boundary_SAV_EAV(p_buffer,
31462306a36Sopenharmony_ci							  dma_q->partial_buf,
31562306a36Sopenharmony_ci							  &bytes_parsed);
31662306a36Sopenharmony_ci		}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		sav_eav &= 0xF0;
31962306a36Sopenharmony_ci		/* Get the first line if we have some portion of an SAV/EAV from
32062306a36Sopenharmony_ci		   the last buffer or a partial line  */
32162306a36Sopenharmony_ci		if (sav_eav) {
32262306a36Sopenharmony_ci			bytes_parsed += cx231xx_get_video_line(dev, dma_q,
32362306a36Sopenharmony_ci				sav_eav,	/* SAV/EAV */
32462306a36Sopenharmony_ci				p_buffer + bytes_parsed,	/* p_buffer */
32562306a36Sopenharmony_ci				buffer_size - bytes_parsed);/* buf size */
32662306a36Sopenharmony_ci		}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci		/* Now parse data that is completely in this buffer */
32962306a36Sopenharmony_ci		/* dma_q->is_partial_line = 0;  */
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci		while (bytes_parsed < buffer_size) {
33262306a36Sopenharmony_ci			u32 bytes_used = 0;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci			sav_eav = cx231xx_find_next_SAV_EAV(
33562306a36Sopenharmony_ci				p_buffer + bytes_parsed,	/* p_buffer */
33662306a36Sopenharmony_ci				buffer_size - bytes_parsed,	/* buf size */
33762306a36Sopenharmony_ci				&bytes_used);/* bytes used to get SAV/EAV */
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci			bytes_parsed += bytes_used;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci			sav_eav &= 0xF0;
34262306a36Sopenharmony_ci			if (sav_eav && (bytes_parsed < buffer_size)) {
34362306a36Sopenharmony_ci				bytes_parsed += cx231xx_get_video_line(dev,
34462306a36Sopenharmony_ci					dma_q, sav_eav,	/* SAV/EAV */
34562306a36Sopenharmony_ci					p_buffer + bytes_parsed,/* p_buffer */
34662306a36Sopenharmony_ci					buffer_size - bytes_parsed);/*buf size*/
34762306a36Sopenharmony_ci			}
34862306a36Sopenharmony_ci		}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci		/* Save the last four bytes of the buffer so we can check the
35162306a36Sopenharmony_ci		   buffer boundary condition next time */
35262306a36Sopenharmony_ci		memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
35362306a36Sopenharmony_ci		bytes_parsed = 0;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci	return 1;
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	struct cx231xx_dmaqueue *dma_q = urb->context;
36262306a36Sopenharmony_ci	unsigned char *p_buffer;
36362306a36Sopenharmony_ci	u32 bytes_parsed = 0, buffer_size = 0;
36462306a36Sopenharmony_ci	u8 sav_eav = 0;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	if (!dev)
36762306a36Sopenharmony_ci		return 0;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	if (dev->state & DEV_DISCONNECTED)
37062306a36Sopenharmony_ci		return 0;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (urb->status < 0) {
37362306a36Sopenharmony_ci		print_err_status(dev, -1, urb->status);
37462306a36Sopenharmony_ci		if (urb->status == -ENOENT)
37562306a36Sopenharmony_ci			return 0;
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	if (1) {
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci		/*  get buffer pointer and length */
38162306a36Sopenharmony_ci		p_buffer = urb->transfer_buffer;
38262306a36Sopenharmony_ci		buffer_size = urb->actual_length;
38362306a36Sopenharmony_ci		bytes_parsed = 0;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci		if (dma_q->is_partial_line) {
38662306a36Sopenharmony_ci			/* Handle the case of a partial line */
38762306a36Sopenharmony_ci			sav_eav = dma_q->last_sav;
38862306a36Sopenharmony_ci		} else {
38962306a36Sopenharmony_ci			/* Check for a SAV/EAV overlapping
39062306a36Sopenharmony_ci				the buffer boundary */
39162306a36Sopenharmony_ci			sav_eav =
39262306a36Sopenharmony_ci			    cx231xx_find_boundary_SAV_EAV(p_buffer,
39362306a36Sopenharmony_ci							  dma_q->partial_buf,
39462306a36Sopenharmony_ci							  &bytes_parsed);
39562306a36Sopenharmony_ci		}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci		sav_eav &= 0xF0;
39862306a36Sopenharmony_ci		/* Get the first line if we have some portion of an SAV/EAV from
39962306a36Sopenharmony_ci		   the last buffer or a partial line  */
40062306a36Sopenharmony_ci		if (sav_eav) {
40162306a36Sopenharmony_ci			bytes_parsed += cx231xx_get_video_line(dev, dma_q,
40262306a36Sopenharmony_ci				sav_eav,	/* SAV/EAV */
40362306a36Sopenharmony_ci				p_buffer + bytes_parsed,	/* p_buffer */
40462306a36Sopenharmony_ci				buffer_size - bytes_parsed);/* buf size */
40562306a36Sopenharmony_ci		}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci		/* Now parse data that is completely in this buffer */
40862306a36Sopenharmony_ci		/* dma_q->is_partial_line = 0;  */
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci		while (bytes_parsed < buffer_size) {
41162306a36Sopenharmony_ci			u32 bytes_used = 0;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci			sav_eav = cx231xx_find_next_SAV_EAV(
41462306a36Sopenharmony_ci				p_buffer + bytes_parsed,	/* p_buffer */
41562306a36Sopenharmony_ci				buffer_size - bytes_parsed,	/* buf size */
41662306a36Sopenharmony_ci				&bytes_used);/* bytes used to get SAV/EAV */
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci			bytes_parsed += bytes_used;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci			sav_eav &= 0xF0;
42162306a36Sopenharmony_ci			if (sav_eav && (bytes_parsed < buffer_size)) {
42262306a36Sopenharmony_ci				bytes_parsed += cx231xx_get_video_line(dev,
42362306a36Sopenharmony_ci					dma_q, sav_eav,	/* SAV/EAV */
42462306a36Sopenharmony_ci					p_buffer + bytes_parsed,/* p_buffer */
42562306a36Sopenharmony_ci					buffer_size - bytes_parsed);/*buf size*/
42662306a36Sopenharmony_ci			}
42762306a36Sopenharmony_ci		}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci		/* Save the last four bytes of the buffer so we can check the
43062306a36Sopenharmony_ci		   buffer boundary condition next time */
43162306a36Sopenharmony_ci		memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
43262306a36Sopenharmony_ci		bytes_parsed = 0;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci	return 1;
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ciu8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf,
44062306a36Sopenharmony_ci				 u32 *p_bytes_used)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	u32 bytes_used;
44362306a36Sopenharmony_ci	u8 boundary_bytes[8];
44462306a36Sopenharmony_ci	u8 sav_eav = 0;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	*p_bytes_used = 0;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/* Create an array of the last 4 bytes of the last buffer and the first
44962306a36Sopenharmony_ci	   4 bytes of the current buffer. */
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	memcpy(boundary_bytes, partial_buf, 4);
45262306a36Sopenharmony_ci	memcpy(boundary_bytes + 4, p_buffer, 4);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	/* Check for the SAV/EAV in the boundary buffer */
45562306a36Sopenharmony_ci	sav_eav = cx231xx_find_next_SAV_EAV((u8 *)&boundary_bytes, 8,
45662306a36Sopenharmony_ci					    &bytes_used);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	if (sav_eav) {
45962306a36Sopenharmony_ci		/* found a boundary SAV/EAV.  Updates the bytes used to reflect
46062306a36Sopenharmony_ci		   only those used in the new buffer */
46162306a36Sopenharmony_ci		*p_bytes_used = bytes_used - 4;
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	return sav_eav;
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ciu8 cx231xx_find_next_SAV_EAV(u8 *p_buffer, u32 buffer_size, u32 *p_bytes_used)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	u32 i;
47062306a36Sopenharmony_ci	u8 sav_eav = 0;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	/*
47362306a36Sopenharmony_ci	 * Don't search if the buffer size is less than 4.  It causes a page
47462306a36Sopenharmony_ci	 * fault since buffer_size - 4 evaluates to a large number in that
47562306a36Sopenharmony_ci	 * case.
47662306a36Sopenharmony_ci	 */
47762306a36Sopenharmony_ci	if (buffer_size < 4) {
47862306a36Sopenharmony_ci		*p_bytes_used = buffer_size;
47962306a36Sopenharmony_ci		return 0;
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	for (i = 0; i < (buffer_size - 3); i++) {
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci		if ((p_buffer[i] == 0xFF) &&
48562306a36Sopenharmony_ci		    (p_buffer[i + 1] == 0x00) && (p_buffer[i + 2] == 0x00)) {
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci			*p_bytes_used = i + 4;
48862306a36Sopenharmony_ci			sav_eav = p_buffer[i + 3];
48962306a36Sopenharmony_ci			return sav_eav;
49062306a36Sopenharmony_ci		}
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	*p_bytes_used = buffer_size;
49462306a36Sopenharmony_ci	return 0;
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ciu32 cx231xx_get_video_line(struct cx231xx *dev,
49862306a36Sopenharmony_ci			   struct cx231xx_dmaqueue *dma_q, u8 sav_eav,
49962306a36Sopenharmony_ci			   u8 *p_buffer, u32 buffer_size)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	u32 bytes_copied = 0;
50262306a36Sopenharmony_ci	int current_field = -1;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	switch (sav_eav) {
50562306a36Sopenharmony_ci	case SAV_ACTIVE_VIDEO_FIELD1:
50662306a36Sopenharmony_ci		/* looking for skipped line which occurred in PAL 720x480 mode.
50762306a36Sopenharmony_ci		   In this case, there will be no active data contained
50862306a36Sopenharmony_ci		   between the SAV and EAV */
50962306a36Sopenharmony_ci		if ((buffer_size > 3) && (p_buffer[0] == 0xFF) &&
51062306a36Sopenharmony_ci		    (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) &&
51162306a36Sopenharmony_ci		    ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) ||
51262306a36Sopenharmony_ci		     (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) ||
51362306a36Sopenharmony_ci		     (p_buffer[3] == EAV_VBLANK_FIELD1) ||
51462306a36Sopenharmony_ci		     (p_buffer[3] == EAV_VBLANK_FIELD2)))
51562306a36Sopenharmony_ci			return bytes_copied;
51662306a36Sopenharmony_ci		current_field = 1;
51762306a36Sopenharmony_ci		break;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	case SAV_ACTIVE_VIDEO_FIELD2:
52062306a36Sopenharmony_ci		/* looking for skipped line which occurred in PAL 720x480 mode.
52162306a36Sopenharmony_ci		   In this case, there will be no active data contained between
52262306a36Sopenharmony_ci		   the SAV and EAV */
52362306a36Sopenharmony_ci		if ((buffer_size > 3) && (p_buffer[0] == 0xFF) &&
52462306a36Sopenharmony_ci		    (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) &&
52562306a36Sopenharmony_ci		    ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) ||
52662306a36Sopenharmony_ci		     (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) ||
52762306a36Sopenharmony_ci		     (p_buffer[3] == EAV_VBLANK_FIELD1)       ||
52862306a36Sopenharmony_ci		     (p_buffer[3] == EAV_VBLANK_FIELD2)))
52962306a36Sopenharmony_ci			return bytes_copied;
53062306a36Sopenharmony_ci		current_field = 2;
53162306a36Sopenharmony_ci		break;
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	dma_q->last_sav = sav_eav;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	bytes_copied = cx231xx_copy_video_line(dev, dma_q, p_buffer,
53762306a36Sopenharmony_ci					       buffer_size, current_field);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	return bytes_copied;
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ciu32 cx231xx_copy_video_line(struct cx231xx *dev,
54362306a36Sopenharmony_ci			    struct cx231xx_dmaqueue *dma_q, u8 *p_line,
54462306a36Sopenharmony_ci			    u32 length, int field_number)
54562306a36Sopenharmony_ci{
54662306a36Sopenharmony_ci	u32 bytes_to_copy;
54762306a36Sopenharmony_ci	struct cx231xx_buffer *buf;
54862306a36Sopenharmony_ci	u32 _line_size = dev->width * 2;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	if (dma_q->current_field != field_number)
55162306a36Sopenharmony_ci		cx231xx_reset_video_buffer(dev, dma_q);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	/* get the buffer pointer */
55462306a36Sopenharmony_ci	if (dev->USE_ISO)
55562306a36Sopenharmony_ci		buf = dev->video_mode.isoc_ctl.buf;
55662306a36Sopenharmony_ci	else
55762306a36Sopenharmony_ci		buf = dev->video_mode.bulk_ctl.buf;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	/* Remember the field number for next time */
56062306a36Sopenharmony_ci	dma_q->current_field = field_number;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	bytes_to_copy = dma_q->bytes_left_in_line;
56362306a36Sopenharmony_ci	if (bytes_to_copy > length)
56462306a36Sopenharmony_ci		bytes_to_copy = length;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	if (dma_q->lines_completed >= dma_q->lines_per_field) {
56762306a36Sopenharmony_ci		dma_q->bytes_left_in_line -= bytes_to_copy;
56862306a36Sopenharmony_ci		dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0) ?
56962306a36Sopenharmony_ci					  0 : 1;
57062306a36Sopenharmony_ci		return 0;
57162306a36Sopenharmony_ci	}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	dma_q->is_partial_line = 1;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	/* If we don't have a buffer, just return the number of bytes we would
57662306a36Sopenharmony_ci	   have copied if we had a buffer. */
57762306a36Sopenharmony_ci	if (!buf) {
57862306a36Sopenharmony_ci		dma_q->bytes_left_in_line -= bytes_to_copy;
57962306a36Sopenharmony_ci		dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0)
58062306a36Sopenharmony_ci					 ? 0 : 1;
58162306a36Sopenharmony_ci		return bytes_to_copy;
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	/* copy the data to video buffer */
58562306a36Sopenharmony_ci	cx231xx_do_copy(dev, dma_q, p_line, bytes_to_copy);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	dma_q->pos += bytes_to_copy;
58862306a36Sopenharmony_ci	dma_q->bytes_left_in_line -= bytes_to_copy;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	if (dma_q->bytes_left_in_line == 0) {
59162306a36Sopenharmony_ci		dma_q->bytes_left_in_line = _line_size;
59262306a36Sopenharmony_ci		dma_q->lines_completed++;
59362306a36Sopenharmony_ci		dma_q->is_partial_line = 0;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci		if (cx231xx_is_buffer_done(dev, dma_q) && buf) {
59662306a36Sopenharmony_ci			buffer_filled(dev, dma_q, buf);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci			dma_q->pos = 0;
59962306a36Sopenharmony_ci			buf = NULL;
60062306a36Sopenharmony_ci			dma_q->lines_completed = 0;
60162306a36Sopenharmony_ci		}
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	return bytes_to_copy;
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_civoid cx231xx_reset_video_buffer(struct cx231xx *dev,
60862306a36Sopenharmony_ci				struct cx231xx_dmaqueue *dma_q)
60962306a36Sopenharmony_ci{
61062306a36Sopenharmony_ci	struct cx231xx_buffer *buf;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	/* handle the switch from field 1 to field 2 */
61362306a36Sopenharmony_ci	if (dma_q->current_field == 1) {
61462306a36Sopenharmony_ci		if (dma_q->lines_completed >= dma_q->lines_per_field)
61562306a36Sopenharmony_ci			dma_q->field1_done = 1;
61662306a36Sopenharmony_ci		else
61762306a36Sopenharmony_ci			dma_q->field1_done = 0;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	if (dev->USE_ISO)
62162306a36Sopenharmony_ci		buf = dev->video_mode.isoc_ctl.buf;
62262306a36Sopenharmony_ci	else
62362306a36Sopenharmony_ci		buf = dev->video_mode.bulk_ctl.buf;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	if (buf == NULL) {
62662306a36Sopenharmony_ci		/* first try to get the buffer */
62762306a36Sopenharmony_ci		get_next_buf(dma_q, &buf);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci		dma_q->pos = 0;
63062306a36Sopenharmony_ci		dma_q->field1_done = 0;
63162306a36Sopenharmony_ci		dma_q->current_field = -1;
63262306a36Sopenharmony_ci	}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	/* reset the counters */
63562306a36Sopenharmony_ci	dma_q->bytes_left_in_line = dev->width << 1;
63662306a36Sopenharmony_ci	dma_q->lines_completed = 0;
63762306a36Sopenharmony_ci}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ciint cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
64062306a36Sopenharmony_ci		    u8 *p_buffer, u32 bytes_to_copy)
64162306a36Sopenharmony_ci{
64262306a36Sopenharmony_ci	u8 *p_out_buffer = NULL;
64362306a36Sopenharmony_ci	u32 current_line_bytes_copied = 0;
64462306a36Sopenharmony_ci	struct cx231xx_buffer *buf;
64562306a36Sopenharmony_ci	u32 _line_size = dev->width << 1;
64662306a36Sopenharmony_ci	void *startwrite;
64762306a36Sopenharmony_ci	int offset, lencopy;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	if (dev->USE_ISO)
65062306a36Sopenharmony_ci		buf = dev->video_mode.isoc_ctl.buf;
65162306a36Sopenharmony_ci	else
65262306a36Sopenharmony_ci		buf = dev->video_mode.bulk_ctl.buf;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	if (buf == NULL)
65562306a36Sopenharmony_ci		return -1;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	p_out_buffer = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	current_line_bytes_copied = _line_size - dma_q->bytes_left_in_line;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	/* Offset field 2 one line from the top of the buffer */
66262306a36Sopenharmony_ci	offset = (dma_q->current_field == 1) ? 0 : _line_size;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	/* Offset for field 2 */
66562306a36Sopenharmony_ci	startwrite = p_out_buffer + offset;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	/* lines already completed in the current field */
66862306a36Sopenharmony_ci	startwrite += (dma_q->lines_completed * _line_size * 2);
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	/* bytes already completed in the current line */
67162306a36Sopenharmony_ci	startwrite += current_line_bytes_copied;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	lencopy = dma_q->bytes_left_in_line > bytes_to_copy ?
67462306a36Sopenharmony_ci		  bytes_to_copy : dma_q->bytes_left_in_line;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	if ((u8 *)(startwrite + lencopy) > (u8 *)(p_out_buffer + dev->size))
67762306a36Sopenharmony_ci		return 0;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	/* The below copies the UYVY data straight into video buffer */
68062306a36Sopenharmony_ci	cx231xx_swab((u16 *) p_buffer, (u16 *) startwrite, (u16) lencopy);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	return 0;
68362306a36Sopenharmony_ci}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_civoid cx231xx_swab(u16 *from, u16 *to, u16 len)
68662306a36Sopenharmony_ci{
68762306a36Sopenharmony_ci	u16 i;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	if (len <= 0)
69062306a36Sopenharmony_ci		return;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	for (i = 0; i < len / 2; i++)
69362306a36Sopenharmony_ci		to[i] = (from[i] << 8) | (from[i] >> 8);
69462306a36Sopenharmony_ci}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ciu8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q)
69762306a36Sopenharmony_ci{
69862306a36Sopenharmony_ci	u8 buffer_complete = 0;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	/* Dual field stream */
70162306a36Sopenharmony_ci	buffer_complete = ((dma_q->current_field == 2) &&
70262306a36Sopenharmony_ci			   (dma_q->lines_completed >= dma_q->lines_per_field) &&
70362306a36Sopenharmony_ci			    dma_q->field1_done);
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	return buffer_complete;
70662306a36Sopenharmony_ci}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci/* ------------------------------------------------------------------
70962306a36Sopenharmony_ci	Videobuf operations
71062306a36Sopenharmony_ci   ------------------------------------------------------------------*/
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cistatic int queue_setup(struct vb2_queue *vq,
71362306a36Sopenharmony_ci		       unsigned int *nbuffers, unsigned int *nplanes,
71462306a36Sopenharmony_ci		       unsigned int sizes[], struct device *alloc_devs[])
71562306a36Sopenharmony_ci{
71662306a36Sopenharmony_ci	struct cx231xx *dev = vb2_get_drv_priv(vq);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	dev->size = (dev->width * dev->height * dev->format->depth + 7) >> 3;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	if (vq->num_buffers + *nbuffers < CX231XX_MIN_BUF)
72162306a36Sopenharmony_ci		*nbuffers = CX231XX_MIN_BUF - vq->num_buffers;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	if (*nplanes)
72462306a36Sopenharmony_ci		return sizes[0] < dev->size ? -EINVAL : 0;
72562306a36Sopenharmony_ci	*nplanes = 1;
72662306a36Sopenharmony_ci	sizes[0] = dev->size;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	return 0;
72962306a36Sopenharmony_ci}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_cistatic void buffer_queue(struct vb2_buffer *vb)
73262306a36Sopenharmony_ci{
73362306a36Sopenharmony_ci	struct cx231xx_buffer *buf =
73462306a36Sopenharmony_ci	    container_of(vb, struct cx231xx_buffer, vb.vb2_buf);
73562306a36Sopenharmony_ci	struct cx231xx *dev = vb2_get_drv_priv(vb->vb2_queue);
73662306a36Sopenharmony_ci	struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq;
73762306a36Sopenharmony_ci	unsigned long flags;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	spin_lock_irqsave(&dev->video_mode.slock, flags);
74062306a36Sopenharmony_ci	list_add_tail(&buf->list, &vidq->active);
74162306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->video_mode.slock, flags);
74262306a36Sopenharmony_ci}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_cistatic void return_all_buffers(struct cx231xx *dev,
74562306a36Sopenharmony_ci			       enum vb2_buffer_state state)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq;
74862306a36Sopenharmony_ci	struct cx231xx_buffer *buf, *node;
74962306a36Sopenharmony_ci	unsigned long flags;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	spin_lock_irqsave(&dev->video_mode.slock, flags);
75262306a36Sopenharmony_ci	if (dev->USE_ISO)
75362306a36Sopenharmony_ci		dev->video_mode.isoc_ctl.buf = NULL;
75462306a36Sopenharmony_ci	else
75562306a36Sopenharmony_ci		dev->video_mode.bulk_ctl.buf = NULL;
75662306a36Sopenharmony_ci	list_for_each_entry_safe(buf, node, &vidq->active, list) {
75762306a36Sopenharmony_ci		list_del(&buf->list);
75862306a36Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, state);
75962306a36Sopenharmony_ci	}
76062306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->video_mode.slock, flags);
76162306a36Sopenharmony_ci}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_cistatic int start_streaming(struct vb2_queue *vq, unsigned int count)
76462306a36Sopenharmony_ci{
76562306a36Sopenharmony_ci	struct cx231xx *dev = vb2_get_drv_priv(vq);
76662306a36Sopenharmony_ci	struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq;
76762306a36Sopenharmony_ci	int ret = 0;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	vidq->sequence = 0;
77062306a36Sopenharmony_ci	dev->mode_tv = 0;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	cx231xx_enable_analog_tuner(dev);
77362306a36Sopenharmony_ci	if (dev->USE_ISO)
77462306a36Sopenharmony_ci		ret = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS,
77562306a36Sopenharmony_ci					CX231XX_NUM_BUFS,
77662306a36Sopenharmony_ci					dev->video_mode.max_pkt_size,
77762306a36Sopenharmony_ci					cx231xx_isoc_copy);
77862306a36Sopenharmony_ci	else
77962306a36Sopenharmony_ci		ret = cx231xx_init_bulk(dev, CX231XX_NUM_PACKETS,
78062306a36Sopenharmony_ci					CX231XX_NUM_BUFS,
78162306a36Sopenharmony_ci					dev->video_mode.max_pkt_size,
78262306a36Sopenharmony_ci					cx231xx_bulk_copy);
78362306a36Sopenharmony_ci	if (ret)
78462306a36Sopenharmony_ci		return_all_buffers(dev, VB2_BUF_STATE_QUEUED);
78562306a36Sopenharmony_ci	call_all(dev, video, s_stream, 1);
78662306a36Sopenharmony_ci	return ret;
78762306a36Sopenharmony_ci}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_cistatic void stop_streaming(struct vb2_queue *vq)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	struct cx231xx *dev = vb2_get_drv_priv(vq);
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	call_all(dev, video, s_stream, 0);
79462306a36Sopenharmony_ci	return_all_buffers(dev, VB2_BUF_STATE_ERROR);
79562306a36Sopenharmony_ci}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_cistatic struct vb2_ops cx231xx_video_qops = {
79862306a36Sopenharmony_ci	.queue_setup		= queue_setup,
79962306a36Sopenharmony_ci	.buf_queue		= buffer_queue,
80062306a36Sopenharmony_ci	.start_streaming	= start_streaming,
80162306a36Sopenharmony_ci	.stop_streaming		= stop_streaming,
80262306a36Sopenharmony_ci	.wait_prepare		= vb2_ops_wait_prepare,
80362306a36Sopenharmony_ci	.wait_finish		= vb2_ops_wait_finish,
80462306a36Sopenharmony_ci};
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci/*********************  v4l2 interface  **************************************/
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_civoid video_mux(struct cx231xx *dev, int index)
80962306a36Sopenharmony_ci{
81062306a36Sopenharmony_ci	dev->video_input = index;
81162306a36Sopenharmony_ci	dev->ctl_ainput = INPUT(index)->amux;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	cx231xx_set_video_input_mux(dev, index);
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	cx25840_call(dev, video, s_routing, INPUT(index)->vmux, 0, 0);
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	cx231xx_set_audio_input(dev, dev->ctl_ainput);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	dev_dbg(dev->dev, "video_mux : %d\n", index);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	/* do mode control overrides if required */
82262306a36Sopenharmony_ci	cx231xx_do_mode_ctrl_overrides(dev);
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci/* ------------------------------------------------------------------
82662306a36Sopenharmony_ci	IOCTL vidioc handling
82762306a36Sopenharmony_ci   ------------------------------------------------------------------*/
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_cistatic int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
83062306a36Sopenharmony_ci				struct v4l2_format *f)
83162306a36Sopenharmony_ci{
83262306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(file);
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	f->fmt.pix.width = dev->width;
83562306a36Sopenharmony_ci	f->fmt.pix.height = dev->height;
83662306a36Sopenharmony_ci	f->fmt.pix.pixelformat = dev->format->fourcc;
83762306a36Sopenharmony_ci	f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3;
83862306a36Sopenharmony_ci	f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height;
83962306a36Sopenharmony_ci	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	f->fmt.pix.field = V4L2_FIELD_INTERLACED;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	return 0;
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cistatic struct cx231xx_fmt *format_by_fourcc(unsigned int fourcc)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	unsigned int i;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(format); i++)
85162306a36Sopenharmony_ci		if (format[i].fourcc == fourcc)
85262306a36Sopenharmony_ci			return &format[i];
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	return NULL;
85562306a36Sopenharmony_ci}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_cistatic int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
85862306a36Sopenharmony_ci				  struct v4l2_format *f)
85962306a36Sopenharmony_ci{
86062306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(file);
86162306a36Sopenharmony_ci	unsigned int width = f->fmt.pix.width;
86262306a36Sopenharmony_ci	unsigned int height = f->fmt.pix.height;
86362306a36Sopenharmony_ci	unsigned int maxw = norm_maxw(dev);
86462306a36Sopenharmony_ci	unsigned int maxh = norm_maxh(dev);
86562306a36Sopenharmony_ci	struct cx231xx_fmt *fmt;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
86862306a36Sopenharmony_ci	if (!fmt) {
86962306a36Sopenharmony_ci		cx231xx_videodbg("Fourcc format (%08x) invalid.\n",
87062306a36Sopenharmony_ci				 f->fmt.pix.pixelformat);
87162306a36Sopenharmony_ci		return -EINVAL;
87262306a36Sopenharmony_ci	}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	/* width must even because of the YUYV format
87562306a36Sopenharmony_ci	   height must be even because of interlacing */
87662306a36Sopenharmony_ci	v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 1, 0);
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	f->fmt.pix.width = width;
87962306a36Sopenharmony_ci	f->fmt.pix.height = height;
88062306a36Sopenharmony_ci	f->fmt.pix.pixelformat = fmt->fourcc;
88162306a36Sopenharmony_ci	f->fmt.pix.bytesperline = (width * fmt->depth + 7) >> 3;
88262306a36Sopenharmony_ci	f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height;
88362306a36Sopenharmony_ci	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
88462306a36Sopenharmony_ci	f->fmt.pix.field = V4L2_FIELD_INTERLACED;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	return 0;
88762306a36Sopenharmony_ci}
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_cistatic int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
89062306a36Sopenharmony_ci				struct v4l2_format *f)
89162306a36Sopenharmony_ci{
89262306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(file);
89362306a36Sopenharmony_ci	struct v4l2_subdev_format format = {
89462306a36Sopenharmony_ci		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
89562306a36Sopenharmony_ci	};
89662306a36Sopenharmony_ci	int rc;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	rc = vidioc_try_fmt_vid_cap(file, priv, f);
89962306a36Sopenharmony_ci	if (rc)
90062306a36Sopenharmony_ci		return rc;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	if (vb2_is_busy(&dev->vidq)) {
90362306a36Sopenharmony_ci		dev_err(dev->dev, "%s: queue busy\n", __func__);
90462306a36Sopenharmony_ci		return -EBUSY;
90562306a36Sopenharmony_ci	}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	/* set new image size */
90862306a36Sopenharmony_ci	dev->width = f->fmt.pix.width;
90962306a36Sopenharmony_ci	dev->height = f->fmt.pix.height;
91062306a36Sopenharmony_ci	dev->format = format_by_fourcc(f->fmt.pix.pixelformat);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	v4l2_fill_mbus_format(&format.format, &f->fmt.pix, MEDIA_BUS_FMT_FIXED);
91362306a36Sopenharmony_ci	call_all(dev, pad, set_fmt, NULL, &format);
91462306a36Sopenharmony_ci	v4l2_fill_pix_format(&f->fmt.pix, &format.format);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	return rc;
91762306a36Sopenharmony_ci}
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_cistatic int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
92062306a36Sopenharmony_ci{
92162306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(file);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	*id = dev->norm;
92462306a36Sopenharmony_ci	return 0;
92562306a36Sopenharmony_ci}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_cistatic int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
92862306a36Sopenharmony_ci{
92962306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(file);
93062306a36Sopenharmony_ci	struct v4l2_subdev_format format = {
93162306a36Sopenharmony_ci		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
93262306a36Sopenharmony_ci	};
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	if (dev->norm == norm)
93562306a36Sopenharmony_ci		return 0;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	if (vb2_is_busy(&dev->vidq))
93862306a36Sopenharmony_ci		return -EBUSY;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	dev->norm = norm;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	/* Adjusts width/height, if needed */
94362306a36Sopenharmony_ci	dev->width = 720;
94462306a36Sopenharmony_ci	dev->height = (dev->norm & V4L2_STD_625_50) ? 576 : 480;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	call_all(dev, video, s_std, dev->norm);
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	/* We need to reset basic properties in the decoder related to
94962306a36Sopenharmony_ci	   resolution (since a standard change effects things like the number
95062306a36Sopenharmony_ci	   of lines in VACT, etc) */
95162306a36Sopenharmony_ci	format.format.code = MEDIA_BUS_FMT_FIXED;
95262306a36Sopenharmony_ci	format.format.width = dev->width;
95362306a36Sopenharmony_ci	format.format.height = dev->height;
95462306a36Sopenharmony_ci	call_all(dev, pad, set_fmt, NULL, &format);
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	/* do mode control overrides */
95762306a36Sopenharmony_ci	cx231xx_do_mode_ctrl_overrides(dev);
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	return 0;
96062306a36Sopenharmony_ci}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_cistatic const char *iname[] = {
96362306a36Sopenharmony_ci	[CX231XX_VMUX_COMPOSITE1] = "Composite1",
96462306a36Sopenharmony_ci	[CX231XX_VMUX_SVIDEO]     = "S-Video",
96562306a36Sopenharmony_ci	[CX231XX_VMUX_TELEVISION] = "Television",
96662306a36Sopenharmony_ci	[CX231XX_VMUX_CABLE]      = "Cable TV",
96762306a36Sopenharmony_ci	[CX231XX_VMUX_DVB]        = "DVB",
96862306a36Sopenharmony_ci};
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_civoid cx231xx_v4l2_create_entities(struct cx231xx *dev)
97162306a36Sopenharmony_ci{
97262306a36Sopenharmony_ci#if defined(CONFIG_MEDIA_CONTROLLER)
97362306a36Sopenharmony_ci	int ret, i;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	/* Create entities for each input connector */
97662306a36Sopenharmony_ci	for (i = 0; i < MAX_CX231XX_INPUT; i++) {
97762306a36Sopenharmony_ci		struct media_entity *ent = &dev->input_ent[i];
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci		if (!INPUT(i)->type)
98062306a36Sopenharmony_ci			break;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci		ent->name = iname[INPUT(i)->type];
98362306a36Sopenharmony_ci		ent->flags = MEDIA_ENT_FL_CONNECTOR;
98462306a36Sopenharmony_ci		dev->input_pad[i].flags = MEDIA_PAD_FL_SOURCE;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci		switch (INPUT(i)->type) {
98762306a36Sopenharmony_ci		case CX231XX_VMUX_COMPOSITE1:
98862306a36Sopenharmony_ci			ent->function = MEDIA_ENT_F_CONN_COMPOSITE;
98962306a36Sopenharmony_ci			break;
99062306a36Sopenharmony_ci		case CX231XX_VMUX_SVIDEO:
99162306a36Sopenharmony_ci			ent->function = MEDIA_ENT_F_CONN_SVIDEO;
99262306a36Sopenharmony_ci			break;
99362306a36Sopenharmony_ci		case CX231XX_VMUX_TELEVISION:
99462306a36Sopenharmony_ci		case CX231XX_VMUX_CABLE:
99562306a36Sopenharmony_ci		case CX231XX_VMUX_DVB:
99662306a36Sopenharmony_ci			/* The DVB core will handle it */
99762306a36Sopenharmony_ci			if (dev->tuner_type == TUNER_ABSENT)
99862306a36Sopenharmony_ci				continue;
99962306a36Sopenharmony_ci			fallthrough;
100062306a36Sopenharmony_ci		default: /* just to shut up a gcc warning */
100162306a36Sopenharmony_ci			ent->function = MEDIA_ENT_F_CONN_RF;
100262306a36Sopenharmony_ci			break;
100362306a36Sopenharmony_ci		}
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci		ret = media_entity_pads_init(ent, 1, &dev->input_pad[i]);
100662306a36Sopenharmony_ci		if (ret < 0)
100762306a36Sopenharmony_ci			pr_err("failed to initialize input pad[%d]!\n", i);
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci		ret = media_device_register_entity(dev->media_dev, ent);
101062306a36Sopenharmony_ci		if (ret < 0)
101162306a36Sopenharmony_ci			pr_err("failed to register input entity %d!\n", i);
101262306a36Sopenharmony_ci	}
101362306a36Sopenharmony_ci#endif
101462306a36Sopenharmony_ci}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ciint cx231xx_enum_input(struct file *file, void *priv,
101762306a36Sopenharmony_ci			     struct v4l2_input *i)
101862306a36Sopenharmony_ci{
101962306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(file);
102062306a36Sopenharmony_ci	u32 gen_stat;
102162306a36Sopenharmony_ci	unsigned int n;
102262306a36Sopenharmony_ci	int ret;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	n = i->index;
102562306a36Sopenharmony_ci	if (n >= MAX_CX231XX_INPUT)
102662306a36Sopenharmony_ci		return -EINVAL;
102762306a36Sopenharmony_ci	if (0 == INPUT(n)->type)
102862306a36Sopenharmony_ci		return -EINVAL;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	i->index = n;
103162306a36Sopenharmony_ci	i->type = V4L2_INPUT_TYPE_CAMERA;
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	strscpy(i->name, iname[INPUT(n)->type], sizeof(i->name));
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	if ((CX231XX_VMUX_TELEVISION == INPUT(n)->type) ||
103662306a36Sopenharmony_ci	    (CX231XX_VMUX_CABLE == INPUT(n)->type))
103762306a36Sopenharmony_ci		i->type = V4L2_INPUT_TYPE_TUNER;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	i->std = dev->vdev.tvnorms;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	/* If they are asking about the active input, read signal status */
104262306a36Sopenharmony_ci	if (n == dev->video_input) {
104362306a36Sopenharmony_ci		ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
104462306a36Sopenharmony_ci					    GEN_STAT, 2, &gen_stat, 4);
104562306a36Sopenharmony_ci		if (ret > 0) {
104662306a36Sopenharmony_ci			if ((gen_stat & FLD_VPRES) == 0x00)
104762306a36Sopenharmony_ci				i->status |= V4L2_IN_ST_NO_SIGNAL;
104862306a36Sopenharmony_ci			if ((gen_stat & FLD_HLOCK) == 0x00)
104962306a36Sopenharmony_ci				i->status |= V4L2_IN_ST_NO_H_LOCK;
105062306a36Sopenharmony_ci		}
105162306a36Sopenharmony_ci	}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	return 0;
105462306a36Sopenharmony_ci}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ciint cx231xx_g_input(struct file *file, void *priv, unsigned int *i)
105762306a36Sopenharmony_ci{
105862306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(file);
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	*i = dev->video_input;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	return 0;
106362306a36Sopenharmony_ci}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ciint cx231xx_s_input(struct file *file, void *priv, unsigned int i)
106662306a36Sopenharmony_ci{
106762306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(file);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	dev->mode_tv = 0;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	if (i >= MAX_CX231XX_INPUT)
107262306a36Sopenharmony_ci		return -EINVAL;
107362306a36Sopenharmony_ci	if (0 == INPUT(i)->type)
107462306a36Sopenharmony_ci		return -EINVAL;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	video_mux(dev, i);
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	if (INPUT(i)->type == CX231XX_VMUX_TELEVISION ||
107962306a36Sopenharmony_ci	    INPUT(i)->type == CX231XX_VMUX_CABLE) {
108062306a36Sopenharmony_ci		/* There's a tuner, so reset the standard and put it on the
108162306a36Sopenharmony_ci		   last known frequency (since it was probably powered down
108262306a36Sopenharmony_ci		   until now */
108362306a36Sopenharmony_ci		call_all(dev, video, s_std, dev->norm);
108462306a36Sopenharmony_ci	}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	return 0;
108762306a36Sopenharmony_ci}
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ciint cx231xx_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
109062306a36Sopenharmony_ci{
109162306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(file);
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	if (0 != t->index)
109462306a36Sopenharmony_ci		return -EINVAL;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	strscpy(t->name, "Tuner", sizeof(t->name));
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	t->type = V4L2_TUNER_ANALOG_TV;
109962306a36Sopenharmony_ci	t->capability = V4L2_TUNER_CAP_NORM;
110062306a36Sopenharmony_ci	t->rangehigh = 0xffffffffUL;
110162306a36Sopenharmony_ci	t->signal = 0xffff;	/* LOCKED */
110262306a36Sopenharmony_ci	call_all(dev, tuner, g_tuner, t);
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	return 0;
110562306a36Sopenharmony_ci}
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ciint cx231xx_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t)
110862306a36Sopenharmony_ci{
110962306a36Sopenharmony_ci	if (0 != t->index)
111062306a36Sopenharmony_ci		return -EINVAL;
111162306a36Sopenharmony_ci	return 0;
111262306a36Sopenharmony_ci}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ciint cx231xx_g_frequency(struct file *file, void *priv,
111562306a36Sopenharmony_ci			      struct v4l2_frequency *f)
111662306a36Sopenharmony_ci{
111762306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(file);
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	if (f->tuner)
112062306a36Sopenharmony_ci		return -EINVAL;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	f->frequency = dev->ctl_freq;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	return 0;
112562306a36Sopenharmony_ci}
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ciint cx231xx_s_frequency(struct file *file, void *priv,
112862306a36Sopenharmony_ci			      const struct v4l2_frequency *f)
112962306a36Sopenharmony_ci{
113062306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(file);
113162306a36Sopenharmony_ci	struct v4l2_frequency new_freq = *f;
113262306a36Sopenharmony_ci	int rc, need_if_freq = 0;
113362306a36Sopenharmony_ci	u32 if_frequency = 5400000;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	dev_dbg(dev->dev,
113662306a36Sopenharmony_ci		"Enter vidioc_s_frequency()f->frequency=%d;f->type=%d\n",
113762306a36Sopenharmony_ci		f->frequency, f->type);
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	if (0 != f->tuner)
114062306a36Sopenharmony_ci		return -EINVAL;
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	/* set pre channel change settings in DIF first */
114362306a36Sopenharmony_ci	rc = cx231xx_tuner_pre_channel_change(dev);
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	switch (dev->model) { /* i2c device tuners */
114662306a36Sopenharmony_ci	case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx:
114762306a36Sopenharmony_ci	case CX231XX_BOARD_HAUPPAUGE_935C:
114862306a36Sopenharmony_ci	case CX231XX_BOARD_HAUPPAUGE_955Q:
114962306a36Sopenharmony_ci	case CX231XX_BOARD_HAUPPAUGE_975:
115062306a36Sopenharmony_ci	case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD:
115162306a36Sopenharmony_ci		if (dev->cx231xx_set_analog_freq)
115262306a36Sopenharmony_ci			dev->cx231xx_set_analog_freq(dev, f->frequency);
115362306a36Sopenharmony_ci		dev->ctl_freq = f->frequency;
115462306a36Sopenharmony_ci		need_if_freq = 1;
115562306a36Sopenharmony_ci		break;
115662306a36Sopenharmony_ci	default:
115762306a36Sopenharmony_ci		call_all(dev, tuner, s_frequency, f);
115862306a36Sopenharmony_ci		call_all(dev, tuner, g_frequency, &new_freq);
115962306a36Sopenharmony_ci		dev->ctl_freq = new_freq.frequency;
116062306a36Sopenharmony_ci		break;
116162306a36Sopenharmony_ci	}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	pr_debug("%s() %u  :  %u\n", __func__, f->frequency, dev->ctl_freq);
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	/* set post channel change settings in DIF first */
116662306a36Sopenharmony_ci	rc = cx231xx_tuner_post_channel_change(dev);
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	if (need_if_freq || dev->tuner_type == TUNER_NXP_TDA18271) {
116962306a36Sopenharmony_ci		if (dev->norm & (V4L2_STD_MN | V4L2_STD_NTSC_443))
117062306a36Sopenharmony_ci			if_frequency = 5400000;  /*5.4MHz	*/
117162306a36Sopenharmony_ci		else if (dev->norm & V4L2_STD_B)
117262306a36Sopenharmony_ci			if_frequency = 6000000;  /*6.0MHz	*/
117362306a36Sopenharmony_ci		else if (dev->norm & (V4L2_STD_PAL_DK | V4L2_STD_SECAM_DK))
117462306a36Sopenharmony_ci			if_frequency = 6900000;  /*6.9MHz	*/
117562306a36Sopenharmony_ci		else if (dev->norm & V4L2_STD_GH)
117662306a36Sopenharmony_ci			if_frequency = 7100000;  /*7.1MHz	*/
117762306a36Sopenharmony_ci		else if (dev->norm & V4L2_STD_PAL_I)
117862306a36Sopenharmony_ci			if_frequency = 7250000;  /*7.25MHz	*/
117962306a36Sopenharmony_ci		else if (dev->norm & V4L2_STD_SECAM_L)
118062306a36Sopenharmony_ci			if_frequency = 6900000;  /*6.9MHz	*/
118162306a36Sopenharmony_ci		else if (dev->norm & V4L2_STD_SECAM_LC)
118262306a36Sopenharmony_ci			if_frequency = 1250000;  /*1.25MHz	*/
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci		dev_dbg(dev->dev,
118562306a36Sopenharmony_ci			"if_frequency is set to %d\n", if_frequency);
118662306a36Sopenharmony_ci		cx231xx_set_Colibri_For_LowIF(dev, if_frequency, 1, 1);
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci		update_HH_register_after_set_DIF(dev);
118962306a36Sopenharmony_ci	}
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	dev_dbg(dev->dev, "Set New FREQUENCY to %d\n", f->frequency);
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	return rc;
119462306a36Sopenharmony_ci}
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ciint cx231xx_g_chip_info(struct file *file, void *fh,
119962306a36Sopenharmony_ci			struct v4l2_dbg_chip_info *chip)
120062306a36Sopenharmony_ci{
120162306a36Sopenharmony_ci	switch (chip->match.addr) {
120262306a36Sopenharmony_ci	case 0:	/* Cx231xx - internal registers */
120362306a36Sopenharmony_ci		return 0;
120462306a36Sopenharmony_ci	case 1:	/* AFE - read byte */
120562306a36Sopenharmony_ci		strscpy(chip->name, "AFE (byte)", sizeof(chip->name));
120662306a36Sopenharmony_ci		return 0;
120762306a36Sopenharmony_ci	case 2:	/* Video Block - read byte */
120862306a36Sopenharmony_ci		strscpy(chip->name, "Video (byte)", sizeof(chip->name));
120962306a36Sopenharmony_ci		return 0;
121062306a36Sopenharmony_ci	case 3:	/* I2S block - read byte */
121162306a36Sopenharmony_ci		strscpy(chip->name, "I2S (byte)", sizeof(chip->name));
121262306a36Sopenharmony_ci		return 0;
121362306a36Sopenharmony_ci	case 4: /* AFE - read dword */
121462306a36Sopenharmony_ci		strscpy(chip->name, "AFE (dword)", sizeof(chip->name));
121562306a36Sopenharmony_ci		return 0;
121662306a36Sopenharmony_ci	case 5: /* Video Block - read dword */
121762306a36Sopenharmony_ci		strscpy(chip->name, "Video (dword)", sizeof(chip->name));
121862306a36Sopenharmony_ci		return 0;
121962306a36Sopenharmony_ci	case 6: /* I2S Block - read dword */
122062306a36Sopenharmony_ci		strscpy(chip->name, "I2S (dword)", sizeof(chip->name));
122162306a36Sopenharmony_ci		return 0;
122262306a36Sopenharmony_ci	}
122362306a36Sopenharmony_ci	return -EINVAL;
122462306a36Sopenharmony_ci}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ciint cx231xx_g_register(struct file *file, void *priv,
122762306a36Sopenharmony_ci			     struct v4l2_dbg_register *reg)
122862306a36Sopenharmony_ci{
122962306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(file);
123062306a36Sopenharmony_ci	int ret;
123162306a36Sopenharmony_ci	u8 value[4] = { 0, 0, 0, 0 };
123262306a36Sopenharmony_ci	u32 data = 0;
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	switch (reg->match.addr) {
123562306a36Sopenharmony_ci	case 0:	/* Cx231xx - internal registers */
123662306a36Sopenharmony_ci		ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
123762306a36Sopenharmony_ci				(u16)reg->reg, value, 4);
123862306a36Sopenharmony_ci		reg->val = value[0] | value[1] << 8 |
123962306a36Sopenharmony_ci			value[2] << 16 | (u32)value[3] << 24;
124062306a36Sopenharmony_ci		reg->size = 4;
124162306a36Sopenharmony_ci		break;
124262306a36Sopenharmony_ci	case 1:	/* AFE - read byte */
124362306a36Sopenharmony_ci		ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
124462306a36Sopenharmony_ci				(u16)reg->reg, 2, &data, 1);
124562306a36Sopenharmony_ci		reg->val = data;
124662306a36Sopenharmony_ci		reg->size = 1;
124762306a36Sopenharmony_ci		break;
124862306a36Sopenharmony_ci	case 2:	/* Video Block - read byte */
124962306a36Sopenharmony_ci		ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
125062306a36Sopenharmony_ci				(u16)reg->reg, 2, &data, 1);
125162306a36Sopenharmony_ci		reg->val = data;
125262306a36Sopenharmony_ci		reg->size = 1;
125362306a36Sopenharmony_ci		break;
125462306a36Sopenharmony_ci	case 3:	/* I2S block - read byte */
125562306a36Sopenharmony_ci		ret = cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
125662306a36Sopenharmony_ci				(u16)reg->reg, 1, &data, 1);
125762306a36Sopenharmony_ci		reg->val = data;
125862306a36Sopenharmony_ci		reg->size = 1;
125962306a36Sopenharmony_ci		break;
126062306a36Sopenharmony_ci	case 4: /* AFE - read dword */
126162306a36Sopenharmony_ci		ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
126262306a36Sopenharmony_ci				(u16)reg->reg, 2, &data, 4);
126362306a36Sopenharmony_ci		reg->val = data;
126462306a36Sopenharmony_ci		reg->size = 4;
126562306a36Sopenharmony_ci		break;
126662306a36Sopenharmony_ci	case 5: /* Video Block - read dword */
126762306a36Sopenharmony_ci		ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
126862306a36Sopenharmony_ci				(u16)reg->reg, 2, &data, 4);
126962306a36Sopenharmony_ci		reg->val = data;
127062306a36Sopenharmony_ci		reg->size = 4;
127162306a36Sopenharmony_ci		break;
127262306a36Sopenharmony_ci	case 6: /* I2S Block - read dword */
127362306a36Sopenharmony_ci		ret = cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
127462306a36Sopenharmony_ci				(u16)reg->reg, 1, &data, 4);
127562306a36Sopenharmony_ci		reg->val = data;
127662306a36Sopenharmony_ci		reg->size = 4;
127762306a36Sopenharmony_ci		break;
127862306a36Sopenharmony_ci	default:
127962306a36Sopenharmony_ci		return -EINVAL;
128062306a36Sopenharmony_ci	}
128162306a36Sopenharmony_ci	return ret < 0 ? ret : 0;
128262306a36Sopenharmony_ci}
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ciint cx231xx_s_register(struct file *file, void *priv,
128562306a36Sopenharmony_ci			     const struct v4l2_dbg_register *reg)
128662306a36Sopenharmony_ci{
128762306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(file);
128862306a36Sopenharmony_ci	int ret;
128962306a36Sopenharmony_ci	u8 data[4] = { 0, 0, 0, 0 };
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	switch (reg->match.addr) {
129262306a36Sopenharmony_ci	case 0:	/* cx231xx internal registers */
129362306a36Sopenharmony_ci		data[0] = (u8) reg->val;
129462306a36Sopenharmony_ci		data[1] = (u8) (reg->val >> 8);
129562306a36Sopenharmony_ci		data[2] = (u8) (reg->val >> 16);
129662306a36Sopenharmony_ci		data[3] = (u8) (reg->val >> 24);
129762306a36Sopenharmony_ci		ret = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
129862306a36Sopenharmony_ci				(u16)reg->reg, data, 4);
129962306a36Sopenharmony_ci		break;
130062306a36Sopenharmony_ci	case 1:	/* AFE - write byte */
130162306a36Sopenharmony_ci		ret = cx231xx_write_i2c_data(dev, AFE_DEVICE_ADDRESS,
130262306a36Sopenharmony_ci				(u16)reg->reg, 2, reg->val, 1);
130362306a36Sopenharmony_ci		break;
130462306a36Sopenharmony_ci	case 2:	/* Video Block - write byte */
130562306a36Sopenharmony_ci		ret = cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS,
130662306a36Sopenharmony_ci				(u16)reg->reg, 2, reg->val, 1);
130762306a36Sopenharmony_ci		break;
130862306a36Sopenharmony_ci	case 3:	/* I2S block - write byte */
130962306a36Sopenharmony_ci		ret = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
131062306a36Sopenharmony_ci				(u16)reg->reg, 1, reg->val, 1);
131162306a36Sopenharmony_ci		break;
131262306a36Sopenharmony_ci	case 4: /* AFE - write dword */
131362306a36Sopenharmony_ci		ret = cx231xx_write_i2c_data(dev, AFE_DEVICE_ADDRESS,
131462306a36Sopenharmony_ci				(u16)reg->reg, 2, reg->val, 4);
131562306a36Sopenharmony_ci		break;
131662306a36Sopenharmony_ci	case 5: /* Video Block - write dword */
131762306a36Sopenharmony_ci		ret = cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS,
131862306a36Sopenharmony_ci				(u16)reg->reg, 2, reg->val, 4);
131962306a36Sopenharmony_ci		break;
132062306a36Sopenharmony_ci	case 6: /* I2S block - write dword */
132162306a36Sopenharmony_ci		ret = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
132262306a36Sopenharmony_ci				(u16)reg->reg, 1, reg->val, 4);
132362306a36Sopenharmony_ci		break;
132462306a36Sopenharmony_ci	default:
132562306a36Sopenharmony_ci		return -EINVAL;
132662306a36Sopenharmony_ci	}
132762306a36Sopenharmony_ci	return ret < 0 ? ret : 0;
132862306a36Sopenharmony_ci}
132962306a36Sopenharmony_ci#endif
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_cistatic int vidioc_g_pixelaspect(struct file *file, void *priv,
133262306a36Sopenharmony_ci				int type, struct v4l2_fract *f)
133362306a36Sopenharmony_ci{
133462306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(file);
133562306a36Sopenharmony_ci	bool is_50hz = dev->norm & V4L2_STD_625_50;
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
133862306a36Sopenharmony_ci		return -EINVAL;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	f->numerator = is_50hz ? 54 : 11;
134162306a36Sopenharmony_ci	f->denominator = is_50hz ? 59 : 10;
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	return 0;
134462306a36Sopenharmony_ci}
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_cistatic int vidioc_g_selection(struct file *file, void *priv,
134762306a36Sopenharmony_ci			      struct v4l2_selection *s)
134862306a36Sopenharmony_ci{
134962306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(file);
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
135262306a36Sopenharmony_ci		return -EINVAL;
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	switch (s->target) {
135562306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP_BOUNDS:
135662306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP_DEFAULT:
135762306a36Sopenharmony_ci		s->r.left = 0;
135862306a36Sopenharmony_ci		s->r.top = 0;
135962306a36Sopenharmony_ci		s->r.width = dev->width;
136062306a36Sopenharmony_ci		s->r.height = dev->height;
136162306a36Sopenharmony_ci		break;
136262306a36Sopenharmony_ci	default:
136362306a36Sopenharmony_ci		return -EINVAL;
136462306a36Sopenharmony_ci	}
136562306a36Sopenharmony_ci	return 0;
136662306a36Sopenharmony_ci}
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ciint cx231xx_querycap(struct file *file, void *priv,
136962306a36Sopenharmony_ci			   struct v4l2_capability *cap)
137062306a36Sopenharmony_ci{
137162306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(file);
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	strscpy(cap->driver, "cx231xx", sizeof(cap->driver));
137462306a36Sopenharmony_ci	strscpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card));
137562306a36Sopenharmony_ci	usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
137662306a36Sopenharmony_ci	cap->capabilities = V4L2_CAP_READWRITE |
137762306a36Sopenharmony_ci		V4L2_CAP_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE |
137862306a36Sopenharmony_ci		V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS;
137962306a36Sopenharmony_ci	if (video_is_registered(&dev->radio_dev))
138062306a36Sopenharmony_ci		cap->capabilities |= V4L2_CAP_RADIO;
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	switch (dev->model) {
138362306a36Sopenharmony_ci	case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx:
138462306a36Sopenharmony_ci	case CX231XX_BOARD_HAUPPAUGE_935C:
138562306a36Sopenharmony_ci	case CX231XX_BOARD_HAUPPAUGE_955Q:
138662306a36Sopenharmony_ci	case CX231XX_BOARD_HAUPPAUGE_975:
138762306a36Sopenharmony_ci	case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD:
138862306a36Sopenharmony_ci		cap->capabilities |= V4L2_CAP_TUNER;
138962306a36Sopenharmony_ci		break;
139062306a36Sopenharmony_ci	default:
139162306a36Sopenharmony_ci		if (dev->tuner_type != TUNER_ABSENT)
139262306a36Sopenharmony_ci			cap->capabilities |= V4L2_CAP_TUNER;
139362306a36Sopenharmony_ci		break;
139462306a36Sopenharmony_ci	}
139562306a36Sopenharmony_ci	return 0;
139662306a36Sopenharmony_ci}
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_cistatic int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
139962306a36Sopenharmony_ci				   struct v4l2_fmtdesc *f)
140062306a36Sopenharmony_ci{
140162306a36Sopenharmony_ci	if (unlikely(f->index >= ARRAY_SIZE(format)))
140262306a36Sopenharmony_ci		return -EINVAL;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	f->pixelformat = format[f->index].fourcc;
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	return 0;
140762306a36Sopenharmony_ci}
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci/* RAW VBI ioctls */
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_cistatic int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
141262306a36Sopenharmony_ci				struct v4l2_format *f)
141362306a36Sopenharmony_ci{
141462306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(file);
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	f->fmt.vbi.sampling_rate = 6750000 * 4;
141762306a36Sopenharmony_ci	f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
141862306a36Sopenharmony_ci	f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
141962306a36Sopenharmony_ci	f->fmt.vbi.offset = 0;
142062306a36Sopenharmony_ci	f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ?
142162306a36Sopenharmony_ci	    PAL_VBI_START_LINE : NTSC_VBI_START_LINE;
142262306a36Sopenharmony_ci	f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ?
142362306a36Sopenharmony_ci	    PAL_VBI_LINES : NTSC_VBI_LINES;
142462306a36Sopenharmony_ci	f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ?
142562306a36Sopenharmony_ci	    PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263;
142662306a36Sopenharmony_ci	f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
142762306a36Sopenharmony_ci	memset(f->fmt.vbi.reserved, 0, sizeof(f->fmt.vbi.reserved));
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	return 0;
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci}
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_cistatic int vidioc_try_fmt_vbi_cap(struct file *file, void *priv,
143462306a36Sopenharmony_ci				  struct v4l2_format *f)
143562306a36Sopenharmony_ci{
143662306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(file);
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	f->fmt.vbi.sampling_rate = 6750000 * 4;
143962306a36Sopenharmony_ci	f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
144062306a36Sopenharmony_ci	f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
144162306a36Sopenharmony_ci	f->fmt.vbi.offset = 0;
144262306a36Sopenharmony_ci	f->fmt.vbi.flags = 0;
144362306a36Sopenharmony_ci	f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ?
144462306a36Sopenharmony_ci	    PAL_VBI_START_LINE : NTSC_VBI_START_LINE;
144562306a36Sopenharmony_ci	f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ?
144662306a36Sopenharmony_ci	    PAL_VBI_LINES : NTSC_VBI_LINES;
144762306a36Sopenharmony_ci	f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ?
144862306a36Sopenharmony_ci	    PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263;
144962306a36Sopenharmony_ci	f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
145062306a36Sopenharmony_ci	memset(f->fmt.vbi.reserved, 0, sizeof(f->fmt.vbi.reserved));
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	return 0;
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci}
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_cistatic int vidioc_s_fmt_vbi_cap(struct file *file, void *priv,
145762306a36Sopenharmony_ci				  struct v4l2_format *f)
145862306a36Sopenharmony_ci{
145962306a36Sopenharmony_ci	return vidioc_try_fmt_vbi_cap(file, priv, f);
146062306a36Sopenharmony_ci}
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci/* ----------------------------------------------------------- */
146362306a36Sopenharmony_ci/* RADIO ESPECIFIC IOCTLS                                      */
146462306a36Sopenharmony_ci/* ----------------------------------------------------------- */
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_cistatic int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
146762306a36Sopenharmony_ci{
146862306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(file);
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	if (t->index)
147162306a36Sopenharmony_ci		return -EINVAL;
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	strscpy(t->name, "Radio", sizeof(t->name));
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	call_all(dev, tuner, g_tuner, t);
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	return 0;
147862306a36Sopenharmony_ci}
147962306a36Sopenharmony_cistatic int radio_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t)
148062306a36Sopenharmony_ci{
148162306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(file);
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	if (t->index)
148462306a36Sopenharmony_ci		return -EINVAL;
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	call_all(dev, tuner, s_tuner, t);
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	return 0;
148962306a36Sopenharmony_ci}
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci/*
149262306a36Sopenharmony_ci * cx231xx_v4l2_open()
149362306a36Sopenharmony_ci * inits the device and starts isoc transfer
149462306a36Sopenharmony_ci */
149562306a36Sopenharmony_cistatic int cx231xx_v4l2_open(struct file *filp)
149662306a36Sopenharmony_ci{
149762306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(filp);
149862306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(filp);
149962306a36Sopenharmony_ci	int ret;
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	if (mutex_lock_interruptible(&dev->lock))
150262306a36Sopenharmony_ci		return -ERESTARTSYS;
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	ret = v4l2_fh_open(filp);
150562306a36Sopenharmony_ci	if (ret) {
150662306a36Sopenharmony_ci		mutex_unlock(&dev->lock);
150762306a36Sopenharmony_ci		return ret;
150862306a36Sopenharmony_ci	}
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	if (dev->users++ == 0) {
151162306a36Sopenharmony_ci		/* Power up in Analog TV mode */
151262306a36Sopenharmony_ci		if (dev->board.external_av)
151362306a36Sopenharmony_ci			cx231xx_set_power_mode(dev,
151462306a36Sopenharmony_ci				 POLARIS_AVMODE_ENXTERNAL_AV);
151562306a36Sopenharmony_ci		else
151662306a36Sopenharmony_ci			cx231xx_set_power_mode(dev, POLARIS_AVMODE_ANALOGT_TV);
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci		/* set video alternate setting */
151962306a36Sopenharmony_ci		cx231xx_set_video_alternate(dev);
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci		/* Needed, since GPIO might have disabled power of
152262306a36Sopenharmony_ci		   some i2c device */
152362306a36Sopenharmony_ci		cx231xx_config_i2c(dev);
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci		/* device needs to be initialized before isoc transfer */
152662306a36Sopenharmony_ci		dev->video_input = dev->video_input > 2 ? 2 : dev->video_input;
152762306a36Sopenharmony_ci	}
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	if (vdev->vfl_type == VFL_TYPE_RADIO) {
153062306a36Sopenharmony_ci		cx231xx_videodbg("video_open: setting radio device\n");
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci		/* cx231xx_start_radio(dev); */
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci		call_all(dev, tuner, s_radio);
153562306a36Sopenharmony_ci	}
153662306a36Sopenharmony_ci	if (vdev->vfl_type == VFL_TYPE_VBI) {
153762306a36Sopenharmony_ci		/* Set the required alternate setting  VBI interface works in
153862306a36Sopenharmony_ci		   Bulk mode only */
153962306a36Sopenharmony_ci		cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
154062306a36Sopenharmony_ci	}
154162306a36Sopenharmony_ci	mutex_unlock(&dev->lock);
154262306a36Sopenharmony_ci	return 0;
154362306a36Sopenharmony_ci}
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci/*
154662306a36Sopenharmony_ci * cx231xx_realease_resources()
154762306a36Sopenharmony_ci * unregisters the v4l2,i2c and usb devices
154862306a36Sopenharmony_ci * called when the device gets disconnected or at module unload
154962306a36Sopenharmony_ci*/
155062306a36Sopenharmony_civoid cx231xx_release_analog_resources(struct cx231xx *dev)
155162306a36Sopenharmony_ci{
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	/*FIXME: I2C IR should be disconnected */
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	if (video_is_registered(&dev->radio_dev))
155662306a36Sopenharmony_ci		video_unregister_device(&dev->radio_dev);
155762306a36Sopenharmony_ci	if (video_is_registered(&dev->vbi_dev)) {
155862306a36Sopenharmony_ci		dev_info(dev->dev, "V4L2 device %s deregistered\n",
155962306a36Sopenharmony_ci			video_device_node_name(&dev->vbi_dev));
156062306a36Sopenharmony_ci		video_unregister_device(&dev->vbi_dev);
156162306a36Sopenharmony_ci	}
156262306a36Sopenharmony_ci	if (video_is_registered(&dev->vdev)) {
156362306a36Sopenharmony_ci		dev_info(dev->dev, "V4L2 device %s deregistered\n",
156462306a36Sopenharmony_ci			video_device_node_name(&dev->vdev));
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci		if (dev->board.has_417)
156762306a36Sopenharmony_ci			cx231xx_417_unregister(dev);
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci		video_unregister_device(&dev->vdev);
157062306a36Sopenharmony_ci	}
157162306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&dev->ctrl_handler);
157262306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&dev->radio_ctrl_handler);
157362306a36Sopenharmony_ci}
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci/*
157662306a36Sopenharmony_ci * cx231xx_close()
157762306a36Sopenharmony_ci * stops streaming and deallocates all resources allocated by the v4l2
157862306a36Sopenharmony_ci * calls and ioctls
157962306a36Sopenharmony_ci */
158062306a36Sopenharmony_cistatic int cx231xx_close(struct file *filp)
158162306a36Sopenharmony_ci{
158262306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(filp);
158362306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(filp);
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	_vb2_fop_release(filp, NULL);
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	if (--dev->users == 0) {
158862306a36Sopenharmony_ci		/* Save some power by putting tuner to sleep */
158962306a36Sopenharmony_ci		call_all(dev, tuner, standby);
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci		/* do this before setting alternate! */
159262306a36Sopenharmony_ci		if (dev->USE_ISO)
159362306a36Sopenharmony_ci			cx231xx_uninit_isoc(dev);
159462306a36Sopenharmony_ci		else
159562306a36Sopenharmony_ci			cx231xx_uninit_bulk(dev);
159662306a36Sopenharmony_ci		cx231xx_set_mode(dev, CX231XX_SUSPEND);
159762306a36Sopenharmony_ci	}
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	/*
160062306a36Sopenharmony_ci	 * To workaround error number=-71 on EP0 for VideoGrabber,
160162306a36Sopenharmony_ci	 *	 need exclude following.
160262306a36Sopenharmony_ci	 * FIXME: It is probably safe to remove most of these, as we're
160362306a36Sopenharmony_ci	 * now avoiding the alternate setting for INDEX_VANC
160462306a36Sopenharmony_ci	 */
160562306a36Sopenharmony_ci	if (!dev->board.no_alt_vanc && vdev->vfl_type == VFL_TYPE_VBI) {
160662306a36Sopenharmony_ci		/* do this before setting alternate! */
160762306a36Sopenharmony_ci		cx231xx_uninit_vbi_isoc(dev);
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci		/* set alternate 0 */
161062306a36Sopenharmony_ci		if (!dev->vbi_or_sliced_cc_mode)
161162306a36Sopenharmony_ci			cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
161262306a36Sopenharmony_ci		else
161362306a36Sopenharmony_ci			cx231xx_set_alt_setting(dev, INDEX_HANC, 0);
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci		wake_up_interruptible_nr(&dev->open, 1);
161662306a36Sopenharmony_ci		return 0;
161762306a36Sopenharmony_ci	}
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	if (dev->users == 0) {
162062306a36Sopenharmony_ci		/* set alternate 0 */
162162306a36Sopenharmony_ci		cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0);
162262306a36Sopenharmony_ci	}
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci	wake_up_interruptible(&dev->open);
162562306a36Sopenharmony_ci	return 0;
162662306a36Sopenharmony_ci}
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_cistatic int cx231xx_v4l2_close(struct file *filp)
162962306a36Sopenharmony_ci{
163062306a36Sopenharmony_ci	struct cx231xx *dev = video_drvdata(filp);
163162306a36Sopenharmony_ci	int rc;
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	mutex_lock(&dev->lock);
163462306a36Sopenharmony_ci	rc = cx231xx_close(filp);
163562306a36Sopenharmony_ci	mutex_unlock(&dev->lock);
163662306a36Sopenharmony_ci	return rc;
163762306a36Sopenharmony_ci}
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_cistatic const struct v4l2_file_operations cx231xx_v4l_fops = {
164062306a36Sopenharmony_ci	.owner   = THIS_MODULE,
164162306a36Sopenharmony_ci	.open    = cx231xx_v4l2_open,
164262306a36Sopenharmony_ci	.release = cx231xx_v4l2_close,
164362306a36Sopenharmony_ci	.read    = vb2_fop_read,
164462306a36Sopenharmony_ci	.poll    = vb2_fop_poll,
164562306a36Sopenharmony_ci	.mmap    = vb2_fop_mmap,
164662306a36Sopenharmony_ci	.unlocked_ioctl   = video_ioctl2,
164762306a36Sopenharmony_ci};
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops video_ioctl_ops = {
165062306a36Sopenharmony_ci	.vidioc_querycap               = cx231xx_querycap,
165162306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_cap       = vidioc_enum_fmt_vid_cap,
165262306a36Sopenharmony_ci	.vidioc_g_fmt_vid_cap          = vidioc_g_fmt_vid_cap,
165362306a36Sopenharmony_ci	.vidioc_try_fmt_vid_cap        = vidioc_try_fmt_vid_cap,
165462306a36Sopenharmony_ci	.vidioc_s_fmt_vid_cap          = vidioc_s_fmt_vid_cap,
165562306a36Sopenharmony_ci	.vidioc_g_fmt_vbi_cap          = vidioc_g_fmt_vbi_cap,
165662306a36Sopenharmony_ci	.vidioc_try_fmt_vbi_cap        = vidioc_try_fmt_vbi_cap,
165762306a36Sopenharmony_ci	.vidioc_s_fmt_vbi_cap          = vidioc_s_fmt_vbi_cap,
165862306a36Sopenharmony_ci	.vidioc_g_pixelaspect          = vidioc_g_pixelaspect,
165962306a36Sopenharmony_ci	.vidioc_g_selection            = vidioc_g_selection,
166062306a36Sopenharmony_ci	.vidioc_reqbufs                = vb2_ioctl_reqbufs,
166162306a36Sopenharmony_ci	.vidioc_querybuf               = vb2_ioctl_querybuf,
166262306a36Sopenharmony_ci	.vidioc_qbuf                   = vb2_ioctl_qbuf,
166362306a36Sopenharmony_ci	.vidioc_dqbuf                  = vb2_ioctl_dqbuf,
166462306a36Sopenharmony_ci	.vidioc_s_std                  = vidioc_s_std,
166562306a36Sopenharmony_ci	.vidioc_g_std                  = vidioc_g_std,
166662306a36Sopenharmony_ci	.vidioc_enum_input             = cx231xx_enum_input,
166762306a36Sopenharmony_ci	.vidioc_g_input                = cx231xx_g_input,
166862306a36Sopenharmony_ci	.vidioc_s_input                = cx231xx_s_input,
166962306a36Sopenharmony_ci	.vidioc_streamon               = vb2_ioctl_streamon,
167062306a36Sopenharmony_ci	.vidioc_streamoff              = vb2_ioctl_streamoff,
167162306a36Sopenharmony_ci	.vidioc_g_tuner                = cx231xx_g_tuner,
167262306a36Sopenharmony_ci	.vidioc_s_tuner                = cx231xx_s_tuner,
167362306a36Sopenharmony_ci	.vidioc_g_frequency            = cx231xx_g_frequency,
167462306a36Sopenharmony_ci	.vidioc_s_frequency            = cx231xx_s_frequency,
167562306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG
167662306a36Sopenharmony_ci	.vidioc_g_chip_info            = cx231xx_g_chip_info,
167762306a36Sopenharmony_ci	.vidioc_g_register             = cx231xx_g_register,
167862306a36Sopenharmony_ci	.vidioc_s_register             = cx231xx_s_register,
167962306a36Sopenharmony_ci#endif
168062306a36Sopenharmony_ci	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
168162306a36Sopenharmony_ci	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
168262306a36Sopenharmony_ci};
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_cistatic struct video_device cx231xx_vbi_template;
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_cistatic const struct video_device cx231xx_video_template = {
168762306a36Sopenharmony_ci	.fops         = &cx231xx_v4l_fops,
168862306a36Sopenharmony_ci	.release      = video_device_release_empty,
168962306a36Sopenharmony_ci	.ioctl_ops    = &video_ioctl_ops,
169062306a36Sopenharmony_ci	.tvnorms      = V4L2_STD_ALL,
169162306a36Sopenharmony_ci};
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_cistatic const struct v4l2_file_operations radio_fops = {
169462306a36Sopenharmony_ci	.owner   = THIS_MODULE,
169562306a36Sopenharmony_ci	.open   = cx231xx_v4l2_open,
169662306a36Sopenharmony_ci	.release = cx231xx_v4l2_close,
169762306a36Sopenharmony_ci	.poll = v4l2_ctrl_poll,
169862306a36Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,
169962306a36Sopenharmony_ci};
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops radio_ioctl_ops = {
170262306a36Sopenharmony_ci	.vidioc_querycap    = cx231xx_querycap,
170362306a36Sopenharmony_ci	.vidioc_g_tuner     = radio_g_tuner,
170462306a36Sopenharmony_ci	.vidioc_s_tuner     = radio_s_tuner,
170562306a36Sopenharmony_ci	.vidioc_g_frequency = cx231xx_g_frequency,
170662306a36Sopenharmony_ci	.vidioc_s_frequency = cx231xx_s_frequency,
170762306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG
170862306a36Sopenharmony_ci	.vidioc_g_chip_info = cx231xx_g_chip_info,
170962306a36Sopenharmony_ci	.vidioc_g_register  = cx231xx_g_register,
171062306a36Sopenharmony_ci	.vidioc_s_register  = cx231xx_s_register,
171162306a36Sopenharmony_ci#endif
171262306a36Sopenharmony_ci	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
171362306a36Sopenharmony_ci	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
171462306a36Sopenharmony_ci};
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_cistatic struct video_device cx231xx_radio_template = {
171762306a36Sopenharmony_ci	.name      = "cx231xx-radio",
171862306a36Sopenharmony_ci	.fops      = &radio_fops,
171962306a36Sopenharmony_ci	.ioctl_ops = &radio_ioctl_ops,
172062306a36Sopenharmony_ci};
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci/******************************** usb interface ******************************/
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_cistatic void cx231xx_vdev_init(struct cx231xx *dev,
172562306a36Sopenharmony_ci		struct video_device *vfd,
172662306a36Sopenharmony_ci		const struct video_device *template,
172762306a36Sopenharmony_ci		const char *type_name)
172862306a36Sopenharmony_ci{
172962306a36Sopenharmony_ci	*vfd = *template;
173062306a36Sopenharmony_ci	vfd->v4l2_dev = &dev->v4l2_dev;
173162306a36Sopenharmony_ci	vfd->release = video_device_release_empty;
173262306a36Sopenharmony_ci	vfd->lock = &dev->lock;
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name);
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci	video_set_drvdata(vfd, dev);
173762306a36Sopenharmony_ci	if (dev->tuner_type == TUNER_ABSENT) {
173862306a36Sopenharmony_ci		switch (dev->model) {
173962306a36Sopenharmony_ci		case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx:
174062306a36Sopenharmony_ci		case CX231XX_BOARD_HAUPPAUGE_935C:
174162306a36Sopenharmony_ci		case CX231XX_BOARD_HAUPPAUGE_955Q:
174262306a36Sopenharmony_ci		case CX231XX_BOARD_HAUPPAUGE_975:
174362306a36Sopenharmony_ci		case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD:
174462306a36Sopenharmony_ci			break;
174562306a36Sopenharmony_ci		default:
174662306a36Sopenharmony_ci			v4l2_disable_ioctl(vfd, VIDIOC_G_FREQUENCY);
174762306a36Sopenharmony_ci			v4l2_disable_ioctl(vfd, VIDIOC_S_FREQUENCY);
174862306a36Sopenharmony_ci			v4l2_disable_ioctl(vfd, VIDIOC_G_TUNER);
174962306a36Sopenharmony_ci			v4l2_disable_ioctl(vfd, VIDIOC_S_TUNER);
175062306a36Sopenharmony_ci			break;
175162306a36Sopenharmony_ci		}
175262306a36Sopenharmony_ci	}
175362306a36Sopenharmony_ci}
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ciint cx231xx_register_analog_devices(struct cx231xx *dev)
175662306a36Sopenharmony_ci{
175762306a36Sopenharmony_ci	struct vb2_queue *q;
175862306a36Sopenharmony_ci	int ret;
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	dev_info(dev->dev, "v4l2 driver version %s\n", CX231XX_VERSION);
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	/* set default norm */
176362306a36Sopenharmony_ci	dev->norm = V4L2_STD_PAL;
176462306a36Sopenharmony_ci	dev->width = norm_maxw(dev);
176562306a36Sopenharmony_ci	dev->height = norm_maxh(dev);
176662306a36Sopenharmony_ci	dev->interlaced = 0;
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	/* Analog specific initialization */
176962306a36Sopenharmony_ci	dev->format = &format[0];
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	/* Set the initial input */
177262306a36Sopenharmony_ci	video_mux(dev, dev->video_input);
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	call_all(dev, video, s_std, dev->norm);
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci	v4l2_ctrl_handler_init(&dev->ctrl_handler, 10);
177762306a36Sopenharmony_ci	v4l2_ctrl_handler_init(&dev->radio_ctrl_handler, 5);
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci	if (dev->sd_cx25840) {
178062306a36Sopenharmony_ci		v4l2_ctrl_add_handler(&dev->ctrl_handler,
178162306a36Sopenharmony_ci				dev->sd_cx25840->ctrl_handler, NULL, true);
178262306a36Sopenharmony_ci		v4l2_ctrl_add_handler(&dev->radio_ctrl_handler,
178362306a36Sopenharmony_ci				dev->sd_cx25840->ctrl_handler,
178462306a36Sopenharmony_ci				v4l2_ctrl_radio_filter, true);
178562306a36Sopenharmony_ci	}
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci	if (dev->ctrl_handler.error)
178862306a36Sopenharmony_ci		return dev->ctrl_handler.error;
178962306a36Sopenharmony_ci	if (dev->radio_ctrl_handler.error)
179062306a36Sopenharmony_ci		return dev->radio_ctrl_handler.error;
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci	/* enable vbi capturing */
179362306a36Sopenharmony_ci	/* write code here...  */
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci	/* allocate and fill video video_device struct */
179662306a36Sopenharmony_ci	cx231xx_vdev_init(dev, &dev->vdev, &cx231xx_video_template, "video");
179762306a36Sopenharmony_ci#if defined(CONFIG_MEDIA_CONTROLLER)
179862306a36Sopenharmony_ci	dev->video_pad.flags = MEDIA_PAD_FL_SINK;
179962306a36Sopenharmony_ci	ret = media_entity_pads_init(&dev->vdev.entity, 1, &dev->video_pad);
180062306a36Sopenharmony_ci	if (ret < 0)
180162306a36Sopenharmony_ci		dev_err(dev->dev, "failed to initialize video media entity!\n");
180262306a36Sopenharmony_ci#endif
180362306a36Sopenharmony_ci	dev->vdev.ctrl_handler = &dev->ctrl_handler;
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci	q = &dev->vidq;
180662306a36Sopenharmony_ci	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
180762306a36Sopenharmony_ci	q->io_modes = VB2_USERPTR | VB2_MMAP | VB2_DMABUF | VB2_READ;
180862306a36Sopenharmony_ci	q->drv_priv = dev;
180962306a36Sopenharmony_ci	q->buf_struct_size = sizeof(struct cx231xx_buffer);
181062306a36Sopenharmony_ci	q->ops = &cx231xx_video_qops;
181162306a36Sopenharmony_ci	q->mem_ops = &vb2_vmalloc_memops;
181262306a36Sopenharmony_ci	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
181362306a36Sopenharmony_ci	q->min_buffers_needed = 1;
181462306a36Sopenharmony_ci	q->lock = &dev->lock;
181562306a36Sopenharmony_ci	ret = vb2_queue_init(q);
181662306a36Sopenharmony_ci	if (ret)
181762306a36Sopenharmony_ci		return ret;
181862306a36Sopenharmony_ci	dev->vdev.queue = q;
181962306a36Sopenharmony_ci	dev->vdev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
182062306a36Sopenharmony_ci				V4L2_CAP_VIDEO_CAPTURE;
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	switch (dev->model) { /* i2c device tuners */
182362306a36Sopenharmony_ci	case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx:
182462306a36Sopenharmony_ci	case CX231XX_BOARD_HAUPPAUGE_935C:
182562306a36Sopenharmony_ci	case CX231XX_BOARD_HAUPPAUGE_955Q:
182662306a36Sopenharmony_ci	case CX231XX_BOARD_HAUPPAUGE_975:
182762306a36Sopenharmony_ci	case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD:
182862306a36Sopenharmony_ci		dev->vdev.device_caps |= V4L2_CAP_TUNER;
182962306a36Sopenharmony_ci		break;
183062306a36Sopenharmony_ci	default:
183162306a36Sopenharmony_ci		if (dev->tuner_type != TUNER_ABSENT)
183262306a36Sopenharmony_ci			dev->vdev.device_caps |= V4L2_CAP_TUNER;
183362306a36Sopenharmony_ci		break;
183462306a36Sopenharmony_ci	}
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci	/* register v4l2 video video_device */
183762306a36Sopenharmony_ci	ret = video_register_device(&dev->vdev, VFL_TYPE_VIDEO,
183862306a36Sopenharmony_ci				    video_nr[dev->devno]);
183962306a36Sopenharmony_ci	if (ret) {
184062306a36Sopenharmony_ci		dev_err(dev->dev,
184162306a36Sopenharmony_ci			"unable to register video device (error=%i).\n",
184262306a36Sopenharmony_ci			ret);
184362306a36Sopenharmony_ci		return ret;
184462306a36Sopenharmony_ci	}
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	dev_info(dev->dev, "Registered video device %s [v4l2]\n",
184762306a36Sopenharmony_ci		video_device_node_name(&dev->vdev));
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci	/* Initialize VBI template */
185062306a36Sopenharmony_ci	cx231xx_vbi_template = cx231xx_video_template;
185162306a36Sopenharmony_ci	strscpy(cx231xx_vbi_template.name, "cx231xx-vbi",
185262306a36Sopenharmony_ci		sizeof(cx231xx_vbi_template.name));
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci	/* Allocate and fill vbi video_device struct */
185562306a36Sopenharmony_ci	cx231xx_vdev_init(dev, &dev->vbi_dev, &cx231xx_vbi_template, "vbi");
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci#if defined(CONFIG_MEDIA_CONTROLLER)
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		dev_err(dev->dev, "failed to initialize vbi media entity!\n");
186262306a36Sopenharmony_ci#endif
186362306a36Sopenharmony_ci	dev->vbi_dev.ctrl_handler = &dev->ctrl_handler;
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	q = &dev->vbiq;
186662306a36Sopenharmony_ci	q->type = V4L2_BUF_TYPE_VBI_CAPTURE;
186762306a36Sopenharmony_ci	q->io_modes = VB2_USERPTR | VB2_MMAP | VB2_DMABUF | VB2_READ;
186862306a36Sopenharmony_ci	q->drv_priv = dev;
186962306a36Sopenharmony_ci	q->buf_struct_size = sizeof(struct cx231xx_buffer);
187062306a36Sopenharmony_ci	q->ops = &cx231xx_vbi_qops;
187162306a36Sopenharmony_ci	q->mem_ops = &vb2_vmalloc_memops;
187262306a36Sopenharmony_ci	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
187362306a36Sopenharmony_ci	q->min_buffers_needed = 1;
187462306a36Sopenharmony_ci	q->lock = &dev->lock;
187562306a36Sopenharmony_ci	ret = vb2_queue_init(q);
187662306a36Sopenharmony_ci	if (ret)
187762306a36Sopenharmony_ci		return ret;
187862306a36Sopenharmony_ci	dev->vbi_dev.queue = q;
187962306a36Sopenharmony_ci	dev->vbi_dev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
188062306a36Sopenharmony_ci				   V4L2_CAP_VBI_CAPTURE;
188162306a36Sopenharmony_ci	switch (dev->model) { /* i2c device tuners */
188262306a36Sopenharmony_ci	case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx:
188362306a36Sopenharmony_ci	case CX231XX_BOARD_HAUPPAUGE_935C:
188462306a36Sopenharmony_ci	case CX231XX_BOARD_HAUPPAUGE_955Q:
188562306a36Sopenharmony_ci	case CX231XX_BOARD_HAUPPAUGE_975:
188662306a36Sopenharmony_ci	case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD:
188762306a36Sopenharmony_ci		dev->vbi_dev.device_caps |= V4L2_CAP_TUNER;
188862306a36Sopenharmony_ci		break;
188962306a36Sopenharmony_ci	default:
189062306a36Sopenharmony_ci		if (dev->tuner_type != TUNER_ABSENT)
189162306a36Sopenharmony_ci			dev->vbi_dev.device_caps |= V4L2_CAP_TUNER;
189262306a36Sopenharmony_ci	}
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci	/* register v4l2 vbi video_device */
189562306a36Sopenharmony_ci	ret = video_register_device(&dev->vbi_dev, VFL_TYPE_VBI,
189662306a36Sopenharmony_ci				    vbi_nr[dev->devno]);
189762306a36Sopenharmony_ci	if (ret < 0) {
189862306a36Sopenharmony_ci		dev_err(dev->dev, "unable to register vbi device\n");
189962306a36Sopenharmony_ci		return ret;
190062306a36Sopenharmony_ci	}
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci	dev_info(dev->dev, "Registered VBI device %s\n",
190362306a36Sopenharmony_ci		video_device_node_name(&dev->vbi_dev));
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci	if (cx231xx_boards[dev->model].radio.type == CX231XX_RADIO) {
190662306a36Sopenharmony_ci		cx231xx_vdev_init(dev, &dev->radio_dev,
190762306a36Sopenharmony_ci				&cx231xx_radio_template, "radio");
190862306a36Sopenharmony_ci		dev->radio_dev.ctrl_handler = &dev->radio_ctrl_handler;
190962306a36Sopenharmony_ci		dev->radio_dev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER;
191062306a36Sopenharmony_ci		ret = video_register_device(&dev->radio_dev, VFL_TYPE_RADIO,
191162306a36Sopenharmony_ci					    radio_nr[dev->devno]);
191262306a36Sopenharmony_ci		if (ret < 0) {
191362306a36Sopenharmony_ci			dev_err(dev->dev,
191462306a36Sopenharmony_ci				"can't register radio device\n");
191562306a36Sopenharmony_ci			return ret;
191662306a36Sopenharmony_ci		}
191762306a36Sopenharmony_ci		dev_info(dev->dev, "Registered radio device as %s\n",
191862306a36Sopenharmony_ci			video_device_node_name(&dev->radio_dev));
191962306a36Sopenharmony_ci	}
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci	return 0;
192262306a36Sopenharmony_ci}
1923