xref: /linux/drivers/media/usb/cx231xx/cx231xx-video.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20c0d06caSMauro Carvalho Chehab /*
30c0d06caSMauro Carvalho Chehab    cx231xx-video.c - driver for Conexant Cx23100/101/102
40c0d06caSMauro Carvalho Chehab 		     USB video capture devices
50c0d06caSMauro Carvalho Chehab 
60c0d06caSMauro Carvalho Chehab    Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
70c0d06caSMauro Carvalho Chehab 	Based on em28xx driver
80c0d06caSMauro Carvalho Chehab 	Based on cx23885 driver
90c0d06caSMauro Carvalho Chehab 	Based on cx88 driver
100c0d06caSMauro Carvalho Chehab 
110c0d06caSMauro Carvalho Chehab  */
120c0d06caSMauro Carvalho Chehab 
13589dadf2SMauro Carvalho Chehab #include "cx231xx.h"
140c0d06caSMauro Carvalho Chehab #include <linux/init.h>
150c0d06caSMauro Carvalho Chehab #include <linux/list.h>
160c0d06caSMauro Carvalho Chehab #include <linux/module.h>
170c0d06caSMauro Carvalho Chehab #include <linux/kernel.h>
180c0d06caSMauro Carvalho Chehab #include <linux/bitmap.h>
190c0d06caSMauro Carvalho Chehab #include <linux/i2c.h>
200c0d06caSMauro Carvalho Chehab #include <linux/mm.h>
210c0d06caSMauro Carvalho Chehab #include <linux/mutex.h>
220c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
230c0d06caSMauro Carvalho Chehab 
240c0d06caSMauro Carvalho Chehab #include <media/v4l2-common.h>
250c0d06caSMauro Carvalho Chehab #include <media/v4l2-ioctl.h>
261d08a4faSHans Verkuil #include <media/v4l2-event.h>
27d647f0b7SMauro Carvalho Chehab #include <media/drv-intf/msp3400.h>
280c0d06caSMauro Carvalho Chehab #include <media/tuner.h>
290c0d06caSMauro Carvalho Chehab 
30fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h>
310c0d06caSMauro Carvalho Chehab 
320c0d06caSMauro Carvalho Chehab #include "cx231xx-vbi.h"
330c0d06caSMauro Carvalho Chehab 
343d28cf3eSMauro Carvalho Chehab #define CX231XX_VERSION "0.0.3"
350c0d06caSMauro Carvalho Chehab 
360c0d06caSMauro Carvalho Chehab #define DRIVER_AUTHOR   "Srinivasa Deevi <srinivasa.deevi@conexant.com>"
370c0d06caSMauro Carvalho Chehab #define DRIVER_DESC     "Conexant cx231xx based USB video device driver"
380c0d06caSMauro Carvalho Chehab 
390c0d06caSMauro Carvalho Chehab #define cx231xx_videodbg(fmt, arg...) do {\
400c0d06caSMauro Carvalho Chehab 	if (video_debug) \
410c0d06caSMauro Carvalho Chehab 		printk(KERN_INFO "%s %s :"fmt, \
420c0d06caSMauro Carvalho Chehab 			 dev->name, __func__ , ##arg); } while (0)
430c0d06caSMauro Carvalho Chehab 
440c0d06caSMauro Carvalho Chehab static unsigned int isoc_debug;
450c0d06caSMauro Carvalho Chehab module_param(isoc_debug, int, 0644);
460c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]");
470c0d06caSMauro Carvalho Chehab 
480c0d06caSMauro Carvalho Chehab #define cx231xx_isocdbg(fmt, arg...) \
490c0d06caSMauro Carvalho Chehab do {\
500c0d06caSMauro Carvalho Chehab 	if (isoc_debug) { \
510c0d06caSMauro Carvalho Chehab 		printk(KERN_INFO "%s %s :"fmt, \
520c0d06caSMauro Carvalho Chehab 			 dev->name, __func__ , ##arg); \
530c0d06caSMauro Carvalho Chehab 	} \
540c0d06caSMauro Carvalho Chehab   } while (0)
550c0d06caSMauro Carvalho Chehab 
560c0d06caSMauro Carvalho Chehab MODULE_AUTHOR(DRIVER_AUTHOR);
570c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION(DRIVER_DESC);
580c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL");
590c0d06caSMauro Carvalho Chehab MODULE_VERSION(CX231XX_VERSION);
600c0d06caSMauro Carvalho Chehab 
617c617138SHans Verkuil static unsigned int card[]     = {[0 ... (CX231XX_MAXBOARDS - 1)] = -1U };
627c617138SHans Verkuil static unsigned int video_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = -1U };
637c617138SHans Verkuil static unsigned int vbi_nr[]   = {[0 ... (CX231XX_MAXBOARDS - 1)] = -1U };
647c617138SHans Verkuil static unsigned int radio_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = -1U };
650c0d06caSMauro Carvalho Chehab 
660c0d06caSMauro Carvalho Chehab module_param_array(card, int, NULL, 0444);
670c0d06caSMauro Carvalho Chehab module_param_array(video_nr, int, NULL, 0444);
680c0d06caSMauro Carvalho Chehab module_param_array(vbi_nr, int, NULL, 0444);
690c0d06caSMauro Carvalho Chehab module_param_array(radio_nr, int, NULL, 0444);
700c0d06caSMauro Carvalho Chehab 
710c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(card, "card type");
720c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(video_nr, "video device numbers");
730c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
740c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(radio_nr, "radio device numbers");
750c0d06caSMauro Carvalho Chehab 
760c0d06caSMauro Carvalho Chehab static unsigned int video_debug;
770c0d06caSMauro Carvalho Chehab module_param(video_debug, int, 0644);
780c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
790c0d06caSMauro Carvalho Chehab 
800c0d06caSMauro Carvalho Chehab /* supported video standards */
810c0d06caSMauro Carvalho Chehab static struct cx231xx_fmt format[] = {
820c0d06caSMauro Carvalho Chehab 	{
830c0d06caSMauro Carvalho Chehab 	 .fourcc = V4L2_PIX_FMT_YUYV,
840c0d06caSMauro Carvalho Chehab 	 .depth = 16,
850c0d06caSMauro Carvalho Chehab 	 .reg = 0,
860c0d06caSMauro Carvalho Chehab 	 },
870c0d06caSMauro Carvalho Chehab };
880c0d06caSMauro Carvalho Chehab 
890c0d06caSMauro Carvalho Chehab 
cx231xx_enable_analog_tuner(struct cx231xx * dev)903d263114SMauro Carvalho Chehab static int cx231xx_enable_analog_tuner(struct cx231xx *dev)
913d263114SMauro Carvalho Chehab {
923d263114SMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER
933d263114SMauro Carvalho Chehab 	struct media_device *mdev = dev->media_dev;
943d263114SMauro Carvalho Chehab 	struct media_entity  *entity, *decoder = NULL, *source;
953d263114SMauro Carvalho Chehab 	struct media_link *link, *found_link = NULL;
9657208e5eSMauro Carvalho Chehab 	int ret, active_links = 0;
973d263114SMauro Carvalho Chehab 
983d263114SMauro Carvalho Chehab 	if (!mdev)
993d263114SMauro Carvalho Chehab 		return 0;
1003d263114SMauro Carvalho Chehab 
1013d263114SMauro Carvalho Chehab 	/*
1023d263114SMauro Carvalho Chehab 	 * This will find the tuner that is connected into the decoder.
1033d263114SMauro Carvalho Chehab 	 * Technically, this is not 100% correct, as the device may be
1043d263114SMauro Carvalho Chehab 	 * using an analog input instead of the tuner. However, as we can't
1053d263114SMauro Carvalho Chehab 	 * do DVB streaming while the DMA engine is being used for V4L2,
1063d263114SMauro Carvalho Chehab 	 * this should be enough for the actual needs.
1073d263114SMauro Carvalho Chehab 	 */
1083d263114SMauro Carvalho Chehab 	media_device_for_each_entity(entity, mdev) {
1094ca72efaSMauro Carvalho Chehab 		if (entity->function == MEDIA_ENT_F_ATV_DECODER) {
1103d263114SMauro Carvalho Chehab 			decoder = entity;
1113d263114SMauro Carvalho Chehab 			break;
1123d263114SMauro Carvalho Chehab 		}
1133d263114SMauro Carvalho Chehab 	}
1143d263114SMauro Carvalho Chehab 	if (!decoder)
1153d263114SMauro Carvalho Chehab 		return 0;
1163d263114SMauro Carvalho Chehab 
11757208e5eSMauro Carvalho Chehab 	list_for_each_entry(link, &decoder->links, list) {
1183d263114SMauro Carvalho Chehab 		if (link->sink->entity == decoder) {
1193d263114SMauro Carvalho Chehab 			found_link = link;
1203d263114SMauro Carvalho Chehab 			if (link->flags & MEDIA_LNK_FL_ENABLED)
1213d263114SMauro Carvalho Chehab 				active_links++;
1223d263114SMauro Carvalho Chehab 			break;
1233d263114SMauro Carvalho Chehab 		}
1243d263114SMauro Carvalho Chehab 	}
1253d263114SMauro Carvalho Chehab 
1263d263114SMauro Carvalho Chehab 	if (active_links == 1 || !found_link)
1273d263114SMauro Carvalho Chehab 		return 0;
1283d263114SMauro Carvalho Chehab 
1293d263114SMauro Carvalho Chehab 	source = found_link->source->entity;
13057208e5eSMauro Carvalho Chehab 	list_for_each_entry(link, &source->links, list) {
1313d263114SMauro Carvalho Chehab 		struct media_entity *sink;
1323d263114SMauro Carvalho Chehab 		int flags = 0;
1333d263114SMauro Carvalho Chehab 
1343d263114SMauro Carvalho Chehab 		sink = link->sink->entity;
1353d263114SMauro Carvalho Chehab 
1363d263114SMauro Carvalho Chehab 		if (sink == entity)
1373d263114SMauro Carvalho Chehab 			flags = MEDIA_LNK_FL_ENABLED;
1383d263114SMauro Carvalho Chehab 
1393d263114SMauro Carvalho Chehab 		ret = media_entity_setup_link(link, flags);
1403d263114SMauro Carvalho Chehab 		if (ret) {
1413d263114SMauro Carvalho Chehab 			dev_err(dev->dev,
1423d263114SMauro Carvalho Chehab 				"Couldn't change link %s->%s to %s. Error %d\n",
1433d263114SMauro Carvalho Chehab 				source->name, sink->name,
1443d263114SMauro Carvalho Chehab 				flags ? "enabled" : "disabled",
1453d263114SMauro Carvalho Chehab 				ret);
1463d263114SMauro Carvalho Chehab 			return ret;
1473d263114SMauro Carvalho Chehab 		} else
1483d263114SMauro Carvalho Chehab 			dev_dbg(dev->dev,
1493d263114SMauro Carvalho Chehab 				"link %s->%s was %s\n",
1503d263114SMauro Carvalho Chehab 				source->name, sink->name,
1513d263114SMauro Carvalho Chehab 				flags ? "ENABLED" : "disabled");
1523d263114SMauro Carvalho Chehab 	}
1533d263114SMauro Carvalho Chehab #endif
1543d263114SMauro Carvalho Chehab 	return 0;
1553d263114SMauro Carvalho Chehab }
1563d263114SMauro Carvalho Chehab 
1570c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------
1580c0d06caSMauro Carvalho Chehab 	Video buffer and parser functions
1590c0d06caSMauro Carvalho Chehab    ------------------------------------------------------------------*/
1600c0d06caSMauro Carvalho Chehab 
1610c0d06caSMauro Carvalho Chehab /*
1620c0d06caSMauro Carvalho Chehab  * Announces that a buffer were filled and request the next
1630c0d06caSMauro Carvalho Chehab  */
buffer_filled(struct cx231xx * dev,struct cx231xx_dmaqueue * dma_q,struct cx231xx_buffer * buf)1640c0d06caSMauro Carvalho Chehab static inline void buffer_filled(struct cx231xx *dev,
1650c0d06caSMauro Carvalho Chehab 				 struct cx231xx_dmaqueue *dma_q,
1660c0d06caSMauro Carvalho Chehab 				 struct cx231xx_buffer *buf)
1670c0d06caSMauro Carvalho Chehab {
1680c0d06caSMauro Carvalho Chehab 	/* Advice that buffer was filled */
1697c617138SHans Verkuil 	cx231xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.vb2_buf.index);
1707c617138SHans Verkuil 	buf->vb.sequence = dma_q->sequence++;
1717c617138SHans Verkuil 	buf->vb.field = V4L2_FIELD_INTERLACED;
1727c617138SHans Verkuil 	buf->vb.vb2_buf.timestamp = ktime_get_ns();
1737c617138SHans Verkuil 	vb2_set_plane_payload(&buf->vb.vb2_buf, 0, dev->size);
1740c0d06caSMauro Carvalho Chehab 
1750c0d06caSMauro Carvalho Chehab 	if (dev->USE_ISO)
1760c0d06caSMauro Carvalho Chehab 		dev->video_mode.isoc_ctl.buf = NULL;
1770c0d06caSMauro Carvalho Chehab 	else
1780c0d06caSMauro Carvalho Chehab 		dev->video_mode.bulk_ctl.buf = NULL;
1790c0d06caSMauro Carvalho Chehab 
1807c617138SHans Verkuil 	list_del(&buf->list);
1817c617138SHans Verkuil 	vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
1820c0d06caSMauro Carvalho Chehab }
1830c0d06caSMauro Carvalho Chehab 
print_err_status(struct cx231xx * dev,int packet,int status)1840c0d06caSMauro Carvalho Chehab static inline void print_err_status(struct cx231xx *dev, int packet, int status)
1850c0d06caSMauro Carvalho Chehab {
1860c0d06caSMauro Carvalho Chehab 	char *errmsg = "Unknown";
1870c0d06caSMauro Carvalho Chehab 
1880c0d06caSMauro Carvalho Chehab 	switch (status) {
1890c0d06caSMauro Carvalho Chehab 	case -ENOENT:
190b436e26eSColin Ian King 		errmsg = "unlinked synchronously";
1910c0d06caSMauro Carvalho Chehab 		break;
1920c0d06caSMauro Carvalho Chehab 	case -ECONNRESET:
193b436e26eSColin Ian King 		errmsg = "unlinked asynchronously";
1940c0d06caSMauro Carvalho Chehab 		break;
1950c0d06caSMauro Carvalho Chehab 	case -ENOSR:
1960c0d06caSMauro Carvalho Chehab 		errmsg = "Buffer error (overrun)";
1970c0d06caSMauro Carvalho Chehab 		break;
1980c0d06caSMauro Carvalho Chehab 	case -EPIPE:
1990c0d06caSMauro Carvalho Chehab 		errmsg = "Stalled (device not responding)";
2000c0d06caSMauro Carvalho Chehab 		break;
2010c0d06caSMauro Carvalho Chehab 	case -EOVERFLOW:
2020c0d06caSMauro Carvalho Chehab 		errmsg = "Babble (bad cable?)";
2030c0d06caSMauro Carvalho Chehab 		break;
2040c0d06caSMauro Carvalho Chehab 	case -EPROTO:
2050c0d06caSMauro Carvalho Chehab 		errmsg = "Bit-stuff error (bad cable?)";
2060c0d06caSMauro Carvalho Chehab 		break;
2070c0d06caSMauro Carvalho Chehab 	case -EILSEQ:
2080c0d06caSMauro Carvalho Chehab 		errmsg = "CRC/Timeout (could be anything)";
2090c0d06caSMauro Carvalho Chehab 		break;
2100c0d06caSMauro Carvalho Chehab 	case -ETIME:
2110c0d06caSMauro Carvalho Chehab 		errmsg = "Device does not respond";
2120c0d06caSMauro Carvalho Chehab 		break;
2130c0d06caSMauro Carvalho Chehab 	}
2140c0d06caSMauro Carvalho Chehab 	if (packet < 0) {
2150c0d06caSMauro Carvalho Chehab 		cx231xx_isocdbg("URB status %d [%s].\n", status, errmsg);
2160c0d06caSMauro Carvalho Chehab 	} else {
2170c0d06caSMauro Carvalho Chehab 		cx231xx_isocdbg("URB packet %d, status %d [%s].\n",
2180c0d06caSMauro Carvalho Chehab 				packet, status, errmsg);
2190c0d06caSMauro Carvalho Chehab 	}
2200c0d06caSMauro Carvalho Chehab }
2210c0d06caSMauro Carvalho Chehab 
2220c0d06caSMauro Carvalho Chehab /*
223f068a6ceSHans Verkuil  * generic routine to get the next available buffer
2240c0d06caSMauro Carvalho Chehab  */
get_next_buf(struct cx231xx_dmaqueue * dma_q,struct cx231xx_buffer ** buf)2250c0d06caSMauro Carvalho Chehab static inline void get_next_buf(struct cx231xx_dmaqueue *dma_q,
2260c0d06caSMauro Carvalho Chehab 				struct cx231xx_buffer **buf)
2270c0d06caSMauro Carvalho Chehab {
2280c0d06caSMauro Carvalho Chehab 	struct cx231xx_video_mode *vmode =
2290c0d06caSMauro Carvalho Chehab 	    container_of(dma_q, struct cx231xx_video_mode, vidq);
2300c0d06caSMauro Carvalho Chehab 	struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode);
2310c0d06caSMauro Carvalho Chehab 
2320c0d06caSMauro Carvalho Chehab 	char *outp;
2330c0d06caSMauro Carvalho Chehab 
2340c0d06caSMauro Carvalho Chehab 	if (list_empty(&dma_q->active)) {
2350c0d06caSMauro Carvalho Chehab 		cx231xx_isocdbg("No active queue to serve\n");
2360c0d06caSMauro Carvalho Chehab 		if (dev->USE_ISO)
2370c0d06caSMauro Carvalho Chehab 			dev->video_mode.isoc_ctl.buf = NULL;
2380c0d06caSMauro Carvalho Chehab 		else
2390c0d06caSMauro Carvalho Chehab 			dev->video_mode.bulk_ctl.buf = NULL;
2400c0d06caSMauro Carvalho Chehab 		*buf = NULL;
2410c0d06caSMauro Carvalho Chehab 		return;
2420c0d06caSMauro Carvalho Chehab 	}
2430c0d06caSMauro Carvalho Chehab 
2440c0d06caSMauro Carvalho Chehab 	/* Get the next buffer */
2457c617138SHans Verkuil 	*buf = list_entry(dma_q->active.next, struct cx231xx_buffer, list);
2460c0d06caSMauro Carvalho Chehab 
2470c0d06caSMauro Carvalho Chehab 	/* Cleans up buffer - Useful for testing for frame/URB loss */
2487c617138SHans Verkuil 	outp = vb2_plane_vaddr(&(*buf)->vb.vb2_buf, 0);
2497c617138SHans Verkuil 	memset(outp, 0, dev->size);
2500c0d06caSMauro Carvalho Chehab 
2510c0d06caSMauro Carvalho Chehab 	if (dev->USE_ISO)
2520c0d06caSMauro Carvalho Chehab 		dev->video_mode.isoc_ctl.buf = *buf;
2530c0d06caSMauro Carvalho Chehab 	else
2540c0d06caSMauro Carvalho Chehab 		dev->video_mode.bulk_ctl.buf = *buf;
2550c0d06caSMauro Carvalho Chehab 
2560c0d06caSMauro Carvalho Chehab 	return;
2570c0d06caSMauro Carvalho Chehab }
2580c0d06caSMauro Carvalho Chehab 
2590c0d06caSMauro Carvalho Chehab /*
2600c0d06caSMauro Carvalho Chehab  * Controls the isoc copy of each urb packet
2610c0d06caSMauro Carvalho Chehab  */
cx231xx_isoc_copy(struct cx231xx * dev,struct urb * urb)2620c0d06caSMauro Carvalho Chehab static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb)
2630c0d06caSMauro Carvalho Chehab {
2640c0d06caSMauro Carvalho Chehab 	struct cx231xx_dmaqueue *dma_q = urb->context;
2654df16f70SPeter Senna Tschudin 	int i;
2660c0d06caSMauro Carvalho Chehab 	unsigned char *p_buffer;
2670c0d06caSMauro Carvalho Chehab 	u32 bytes_parsed = 0, buffer_size = 0;
2680c0d06caSMauro Carvalho Chehab 	u8 sav_eav = 0;
2690c0d06caSMauro Carvalho Chehab 
2700c0d06caSMauro Carvalho Chehab 	if (!dev)
2710c0d06caSMauro Carvalho Chehab 		return 0;
2720c0d06caSMauro Carvalho Chehab 
2730c0d06caSMauro Carvalho Chehab 	if (dev->state & DEV_DISCONNECTED)
2740c0d06caSMauro Carvalho Chehab 		return 0;
2750c0d06caSMauro Carvalho Chehab 
2760c0d06caSMauro Carvalho Chehab 	if (urb->status < 0) {
2770c0d06caSMauro Carvalho Chehab 		print_err_status(dev, -1, urb->status);
2780c0d06caSMauro Carvalho Chehab 		if (urb->status == -ENOENT)
2790c0d06caSMauro Carvalho Chehab 			return 0;
2800c0d06caSMauro Carvalho Chehab 	}
2810c0d06caSMauro Carvalho Chehab 
2820c0d06caSMauro Carvalho Chehab 	for (i = 0; i < urb->number_of_packets; i++) {
2830c0d06caSMauro Carvalho Chehab 		int status = urb->iso_frame_desc[i].status;
2840c0d06caSMauro Carvalho Chehab 
2850c0d06caSMauro Carvalho Chehab 		if (status < 0) {
2860c0d06caSMauro Carvalho Chehab 			print_err_status(dev, i, status);
2870c0d06caSMauro Carvalho Chehab 			if (urb->iso_frame_desc[i].status != -EPROTO)
2880c0d06caSMauro Carvalho Chehab 				continue;
2890c0d06caSMauro Carvalho Chehab 		}
2900c0d06caSMauro Carvalho Chehab 
2910c0d06caSMauro Carvalho Chehab 		if (urb->iso_frame_desc[i].actual_length <= 0) {
2920c0d06caSMauro Carvalho Chehab 			/* cx231xx_isocdbg("packet %d is empty",i); - spammy */
2930c0d06caSMauro Carvalho Chehab 			continue;
2940c0d06caSMauro Carvalho Chehab 		}
2950c0d06caSMauro Carvalho Chehab 		if (urb->iso_frame_desc[i].actual_length >
2960c0d06caSMauro Carvalho Chehab 		    dev->video_mode.max_pkt_size) {
2970c0d06caSMauro Carvalho Chehab 			cx231xx_isocdbg("packet bigger than packet size");
2980c0d06caSMauro Carvalho Chehab 			continue;
2990c0d06caSMauro Carvalho Chehab 		}
3000c0d06caSMauro Carvalho Chehab 
3010c0d06caSMauro Carvalho Chehab 		/*  get buffer pointer and length */
3020c0d06caSMauro Carvalho Chehab 		p_buffer = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
3030c0d06caSMauro Carvalho Chehab 		buffer_size = urb->iso_frame_desc[i].actual_length;
3040c0d06caSMauro Carvalho Chehab 		bytes_parsed = 0;
3050c0d06caSMauro Carvalho Chehab 
3060c0d06caSMauro Carvalho Chehab 		if (dma_q->is_partial_line) {
3070c0d06caSMauro Carvalho Chehab 			/* Handle the case of a partial line */
3080c0d06caSMauro Carvalho Chehab 			sav_eav = dma_q->last_sav;
3090c0d06caSMauro Carvalho Chehab 		} else {
3100c0d06caSMauro Carvalho Chehab 			/* Check for a SAV/EAV overlapping
3110c0d06caSMauro Carvalho Chehab 				the buffer boundary */
3120c0d06caSMauro Carvalho Chehab 			sav_eav =
3130c0d06caSMauro Carvalho Chehab 			    cx231xx_find_boundary_SAV_EAV(p_buffer,
3140c0d06caSMauro Carvalho Chehab 							  dma_q->partial_buf,
3150c0d06caSMauro Carvalho Chehab 							  &bytes_parsed);
3160c0d06caSMauro Carvalho Chehab 		}
3170c0d06caSMauro Carvalho Chehab 
3180c0d06caSMauro Carvalho Chehab 		sav_eav &= 0xF0;
3190c0d06caSMauro Carvalho Chehab 		/* Get the first line if we have some portion of an SAV/EAV from
3200c0d06caSMauro Carvalho Chehab 		   the last buffer or a partial line  */
3210c0d06caSMauro Carvalho Chehab 		if (sav_eav) {
3220c0d06caSMauro Carvalho Chehab 			bytes_parsed += cx231xx_get_video_line(dev, dma_q,
3230c0d06caSMauro Carvalho Chehab 				sav_eav,	/* SAV/EAV */
3240c0d06caSMauro Carvalho Chehab 				p_buffer + bytes_parsed,	/* p_buffer */
3250c0d06caSMauro Carvalho Chehab 				buffer_size - bytes_parsed);/* buf size */
3260c0d06caSMauro Carvalho Chehab 		}
3270c0d06caSMauro Carvalho Chehab 
3280c0d06caSMauro Carvalho Chehab 		/* Now parse data that is completely in this buffer */
3290c0d06caSMauro Carvalho Chehab 		/* dma_q->is_partial_line = 0;  */
3300c0d06caSMauro Carvalho Chehab 
3310c0d06caSMauro Carvalho Chehab 		while (bytes_parsed < buffer_size) {
3320c0d06caSMauro Carvalho Chehab 			u32 bytes_used = 0;
3330c0d06caSMauro Carvalho Chehab 
3340c0d06caSMauro Carvalho Chehab 			sav_eav = cx231xx_find_next_SAV_EAV(
3350c0d06caSMauro Carvalho Chehab 				p_buffer + bytes_parsed,	/* p_buffer */
3360c0d06caSMauro Carvalho Chehab 				buffer_size - bytes_parsed,	/* buf size */
3370c0d06caSMauro Carvalho Chehab 				&bytes_used);/* bytes used to get SAV/EAV */
3380c0d06caSMauro Carvalho Chehab 
3390c0d06caSMauro Carvalho Chehab 			bytes_parsed += bytes_used;
3400c0d06caSMauro Carvalho Chehab 
3410c0d06caSMauro Carvalho Chehab 			sav_eav &= 0xF0;
3420c0d06caSMauro Carvalho Chehab 			if (sav_eav && (bytes_parsed < buffer_size)) {
3430c0d06caSMauro Carvalho Chehab 				bytes_parsed += cx231xx_get_video_line(dev,
3440c0d06caSMauro Carvalho Chehab 					dma_q, sav_eav,	/* SAV/EAV */
3450c0d06caSMauro Carvalho Chehab 					p_buffer + bytes_parsed,/* p_buffer */
3460c0d06caSMauro Carvalho Chehab 					buffer_size - bytes_parsed);/*buf size*/
3470c0d06caSMauro Carvalho Chehab 			}
3480c0d06caSMauro Carvalho Chehab 		}
3490c0d06caSMauro Carvalho Chehab 
3500c0d06caSMauro Carvalho Chehab 		/* Save the last four bytes of the buffer so we can check the
3510c0d06caSMauro Carvalho Chehab 		   buffer boundary condition next time */
3520c0d06caSMauro Carvalho Chehab 		memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
3530c0d06caSMauro Carvalho Chehab 		bytes_parsed = 0;
3540c0d06caSMauro Carvalho Chehab 
3550c0d06caSMauro Carvalho Chehab 	}
3564df16f70SPeter Senna Tschudin 	return 1;
3570c0d06caSMauro Carvalho Chehab }
3580c0d06caSMauro Carvalho Chehab 
cx231xx_bulk_copy(struct cx231xx * dev,struct urb * urb)3590c0d06caSMauro Carvalho Chehab static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb)
3600c0d06caSMauro Carvalho Chehab {
3610c0d06caSMauro Carvalho Chehab 	struct cx231xx_dmaqueue *dma_q = urb->context;
3620c0d06caSMauro Carvalho Chehab 	unsigned char *p_buffer;
3630c0d06caSMauro Carvalho Chehab 	u32 bytes_parsed = 0, buffer_size = 0;
3640c0d06caSMauro Carvalho Chehab 	u8 sav_eav = 0;
3650c0d06caSMauro Carvalho Chehab 
3660c0d06caSMauro Carvalho Chehab 	if (!dev)
3670c0d06caSMauro Carvalho Chehab 		return 0;
3680c0d06caSMauro Carvalho Chehab 
3690c0d06caSMauro Carvalho Chehab 	if (dev->state & DEV_DISCONNECTED)
3700c0d06caSMauro Carvalho Chehab 		return 0;
3710c0d06caSMauro Carvalho Chehab 
3720c0d06caSMauro Carvalho Chehab 	if (urb->status < 0) {
3730c0d06caSMauro Carvalho Chehab 		print_err_status(dev, -1, urb->status);
3740c0d06caSMauro Carvalho Chehab 		if (urb->status == -ENOENT)
3750c0d06caSMauro Carvalho Chehab 			return 0;
3760c0d06caSMauro Carvalho Chehab 	}
3770c0d06caSMauro Carvalho Chehab 
3780c0d06caSMauro Carvalho Chehab 	if (1) {
3790c0d06caSMauro Carvalho Chehab 
3800c0d06caSMauro Carvalho Chehab 		/*  get buffer pointer and length */
3810c0d06caSMauro Carvalho Chehab 		p_buffer = urb->transfer_buffer;
3820c0d06caSMauro Carvalho Chehab 		buffer_size = urb->actual_length;
3830c0d06caSMauro Carvalho Chehab 		bytes_parsed = 0;
3840c0d06caSMauro Carvalho Chehab 
3850c0d06caSMauro Carvalho Chehab 		if (dma_q->is_partial_line) {
3860c0d06caSMauro Carvalho Chehab 			/* Handle the case of a partial line */
3870c0d06caSMauro Carvalho Chehab 			sav_eav = dma_q->last_sav;
3880c0d06caSMauro Carvalho Chehab 		} else {
3890c0d06caSMauro Carvalho Chehab 			/* Check for a SAV/EAV overlapping
3900c0d06caSMauro Carvalho Chehab 				the buffer boundary */
3910c0d06caSMauro Carvalho Chehab 			sav_eav =
3920c0d06caSMauro Carvalho Chehab 			    cx231xx_find_boundary_SAV_EAV(p_buffer,
3930c0d06caSMauro Carvalho Chehab 							  dma_q->partial_buf,
3940c0d06caSMauro Carvalho Chehab 							  &bytes_parsed);
3950c0d06caSMauro Carvalho Chehab 		}
3960c0d06caSMauro Carvalho Chehab 
3970c0d06caSMauro Carvalho Chehab 		sav_eav &= 0xF0;
3980c0d06caSMauro Carvalho Chehab 		/* Get the first line if we have some portion of an SAV/EAV from
3990c0d06caSMauro Carvalho Chehab 		   the last buffer or a partial line  */
4000c0d06caSMauro Carvalho Chehab 		if (sav_eav) {
4010c0d06caSMauro Carvalho Chehab 			bytes_parsed += cx231xx_get_video_line(dev, dma_q,
4020c0d06caSMauro Carvalho Chehab 				sav_eav,	/* SAV/EAV */
4030c0d06caSMauro Carvalho Chehab 				p_buffer + bytes_parsed,	/* p_buffer */
4040c0d06caSMauro Carvalho Chehab 				buffer_size - bytes_parsed);/* buf size */
4050c0d06caSMauro Carvalho Chehab 		}
4060c0d06caSMauro Carvalho Chehab 
4070c0d06caSMauro Carvalho Chehab 		/* Now parse data that is completely in this buffer */
4080c0d06caSMauro Carvalho Chehab 		/* dma_q->is_partial_line = 0;  */
4090c0d06caSMauro Carvalho Chehab 
4100c0d06caSMauro Carvalho Chehab 		while (bytes_parsed < buffer_size) {
4110c0d06caSMauro Carvalho Chehab 			u32 bytes_used = 0;
4120c0d06caSMauro Carvalho Chehab 
4130c0d06caSMauro Carvalho Chehab 			sav_eav = cx231xx_find_next_SAV_EAV(
4140c0d06caSMauro Carvalho Chehab 				p_buffer + bytes_parsed,	/* p_buffer */
4150c0d06caSMauro Carvalho Chehab 				buffer_size - bytes_parsed,	/* buf size */
4160c0d06caSMauro Carvalho Chehab 				&bytes_used);/* bytes used to get SAV/EAV */
4170c0d06caSMauro Carvalho Chehab 
4180c0d06caSMauro Carvalho Chehab 			bytes_parsed += bytes_used;
4190c0d06caSMauro Carvalho Chehab 
4200c0d06caSMauro Carvalho Chehab 			sav_eav &= 0xF0;
4210c0d06caSMauro Carvalho Chehab 			if (sav_eav && (bytes_parsed < buffer_size)) {
4220c0d06caSMauro Carvalho Chehab 				bytes_parsed += cx231xx_get_video_line(dev,
4230c0d06caSMauro Carvalho Chehab 					dma_q, sav_eav,	/* SAV/EAV */
4240c0d06caSMauro Carvalho Chehab 					p_buffer + bytes_parsed,/* p_buffer */
4250c0d06caSMauro Carvalho Chehab 					buffer_size - bytes_parsed);/*buf size*/
4260c0d06caSMauro Carvalho Chehab 			}
4270c0d06caSMauro Carvalho Chehab 		}
4280c0d06caSMauro Carvalho Chehab 
4290c0d06caSMauro Carvalho Chehab 		/* Save the last four bytes of the buffer so we can check the
4300c0d06caSMauro Carvalho Chehab 		   buffer boundary condition next time */
4310c0d06caSMauro Carvalho Chehab 		memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
4320c0d06caSMauro Carvalho Chehab 		bytes_parsed = 0;
4330c0d06caSMauro Carvalho Chehab 
4340c0d06caSMauro Carvalho Chehab 	}
4354df16f70SPeter Senna Tschudin 	return 1;
4360c0d06caSMauro Carvalho Chehab }
4370c0d06caSMauro Carvalho Chehab 
4380c0d06caSMauro Carvalho Chehab 
cx231xx_find_boundary_SAV_EAV(u8 * p_buffer,u8 * partial_buf,u32 * p_bytes_used)4390c0d06caSMauro Carvalho Chehab u8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf,
4400c0d06caSMauro Carvalho Chehab 				 u32 *p_bytes_used)
4410c0d06caSMauro Carvalho Chehab {
4420c0d06caSMauro Carvalho Chehab 	u32 bytes_used;
4430c0d06caSMauro Carvalho Chehab 	u8 boundary_bytes[8];
4440c0d06caSMauro Carvalho Chehab 	u8 sav_eav = 0;
4450c0d06caSMauro Carvalho Chehab 
4460c0d06caSMauro Carvalho Chehab 	*p_bytes_used = 0;
4470c0d06caSMauro Carvalho Chehab 
4480c0d06caSMauro Carvalho Chehab 	/* Create an array of the last 4 bytes of the last buffer and the first
4490c0d06caSMauro Carvalho Chehab 	   4 bytes of the current buffer. */
4500c0d06caSMauro Carvalho Chehab 
4510c0d06caSMauro Carvalho Chehab 	memcpy(boundary_bytes, partial_buf, 4);
4520c0d06caSMauro Carvalho Chehab 	memcpy(boundary_bytes + 4, p_buffer, 4);
4530c0d06caSMauro Carvalho Chehab 
4540c0d06caSMauro Carvalho Chehab 	/* Check for the SAV/EAV in the boundary buffer */
4550c0d06caSMauro Carvalho Chehab 	sav_eav = cx231xx_find_next_SAV_EAV((u8 *)&boundary_bytes, 8,
4560c0d06caSMauro Carvalho Chehab 					    &bytes_used);
4570c0d06caSMauro Carvalho Chehab 
4580c0d06caSMauro Carvalho Chehab 	if (sav_eav) {
4590c0d06caSMauro Carvalho Chehab 		/* found a boundary SAV/EAV.  Updates the bytes used to reflect
4600c0d06caSMauro Carvalho Chehab 		   only those used in the new buffer */
4610c0d06caSMauro Carvalho Chehab 		*p_bytes_used = bytes_used - 4;
4620c0d06caSMauro Carvalho Chehab 	}
4630c0d06caSMauro Carvalho Chehab 
4640c0d06caSMauro Carvalho Chehab 	return sav_eav;
4650c0d06caSMauro Carvalho Chehab }
4660c0d06caSMauro Carvalho Chehab 
cx231xx_find_next_SAV_EAV(u8 * p_buffer,u32 buffer_size,u32 * p_bytes_used)4670c0d06caSMauro Carvalho Chehab u8 cx231xx_find_next_SAV_EAV(u8 *p_buffer, u32 buffer_size, u32 *p_bytes_used)
4680c0d06caSMauro Carvalho Chehab {
4690c0d06caSMauro Carvalho Chehab 	u32 i;
4700c0d06caSMauro Carvalho Chehab 	u8 sav_eav = 0;
4710c0d06caSMauro Carvalho Chehab 
4720c0d06caSMauro Carvalho Chehab 	/*
4730c0d06caSMauro Carvalho Chehab 	 * Don't search if the buffer size is less than 4.  It causes a page
4740c0d06caSMauro Carvalho Chehab 	 * fault since buffer_size - 4 evaluates to a large number in that
4750c0d06caSMauro Carvalho Chehab 	 * case.
4760c0d06caSMauro Carvalho Chehab 	 */
4770c0d06caSMauro Carvalho Chehab 	if (buffer_size < 4) {
4780c0d06caSMauro Carvalho Chehab 		*p_bytes_used = buffer_size;
4790c0d06caSMauro Carvalho Chehab 		return 0;
4800c0d06caSMauro Carvalho Chehab 	}
4810c0d06caSMauro Carvalho Chehab 
4820c0d06caSMauro Carvalho Chehab 	for (i = 0; i < (buffer_size - 3); i++) {
4830c0d06caSMauro Carvalho Chehab 
4840c0d06caSMauro Carvalho Chehab 		if ((p_buffer[i] == 0xFF) &&
4850c0d06caSMauro Carvalho Chehab 		    (p_buffer[i + 1] == 0x00) && (p_buffer[i + 2] == 0x00)) {
4860c0d06caSMauro Carvalho Chehab 
4870c0d06caSMauro Carvalho Chehab 			*p_bytes_used = i + 4;
4880c0d06caSMauro Carvalho Chehab 			sav_eav = p_buffer[i + 3];
4890c0d06caSMauro Carvalho Chehab 			return sav_eav;
4900c0d06caSMauro Carvalho Chehab 		}
4910c0d06caSMauro Carvalho Chehab 	}
4920c0d06caSMauro Carvalho Chehab 
4930c0d06caSMauro Carvalho Chehab 	*p_bytes_used = buffer_size;
4940c0d06caSMauro Carvalho Chehab 	return 0;
4950c0d06caSMauro Carvalho Chehab }
4960c0d06caSMauro Carvalho Chehab 
cx231xx_get_video_line(struct cx231xx * dev,struct cx231xx_dmaqueue * dma_q,u8 sav_eav,u8 * p_buffer,u32 buffer_size)4970c0d06caSMauro Carvalho Chehab u32 cx231xx_get_video_line(struct cx231xx *dev,
4980c0d06caSMauro Carvalho Chehab 			   struct cx231xx_dmaqueue *dma_q, u8 sav_eav,
4990c0d06caSMauro Carvalho Chehab 			   u8 *p_buffer, u32 buffer_size)
5000c0d06caSMauro Carvalho Chehab {
5010c0d06caSMauro Carvalho Chehab 	u32 bytes_copied = 0;
5020c0d06caSMauro Carvalho Chehab 	int current_field = -1;
5030c0d06caSMauro Carvalho Chehab 
5040c0d06caSMauro Carvalho Chehab 	switch (sav_eav) {
5050c0d06caSMauro Carvalho Chehab 	case SAV_ACTIVE_VIDEO_FIELD1:
5060c0d06caSMauro Carvalho Chehab 		/* looking for skipped line which occurred in PAL 720x480 mode.
5070c0d06caSMauro Carvalho Chehab 		   In this case, there will be no active data contained
5080c0d06caSMauro Carvalho Chehab 		   between the SAV and EAV */
5090c0d06caSMauro Carvalho Chehab 		if ((buffer_size > 3) && (p_buffer[0] == 0xFF) &&
5100c0d06caSMauro Carvalho Chehab 		    (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) &&
5110c0d06caSMauro Carvalho Chehab 		    ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) ||
5120c0d06caSMauro Carvalho Chehab 		     (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) ||
5130c0d06caSMauro Carvalho Chehab 		     (p_buffer[3] == EAV_VBLANK_FIELD1) ||
5140c0d06caSMauro Carvalho Chehab 		     (p_buffer[3] == EAV_VBLANK_FIELD2)))
5150c0d06caSMauro Carvalho Chehab 			return bytes_copied;
5160c0d06caSMauro Carvalho Chehab 		current_field = 1;
5170c0d06caSMauro Carvalho Chehab 		break;
5180c0d06caSMauro Carvalho Chehab 
5190c0d06caSMauro Carvalho Chehab 	case SAV_ACTIVE_VIDEO_FIELD2:
5200c0d06caSMauro Carvalho Chehab 		/* looking for skipped line which occurred in PAL 720x480 mode.
5210c0d06caSMauro Carvalho Chehab 		   In this case, there will be no active data contained between
5220c0d06caSMauro Carvalho Chehab 		   the SAV and EAV */
5230c0d06caSMauro Carvalho Chehab 		if ((buffer_size > 3) && (p_buffer[0] == 0xFF) &&
5240c0d06caSMauro Carvalho Chehab 		    (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) &&
5250c0d06caSMauro Carvalho Chehab 		    ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) ||
5260c0d06caSMauro Carvalho Chehab 		     (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) ||
5270c0d06caSMauro Carvalho Chehab 		     (p_buffer[3] == EAV_VBLANK_FIELD1)       ||
5280c0d06caSMauro Carvalho Chehab 		     (p_buffer[3] == EAV_VBLANK_FIELD2)))
5290c0d06caSMauro Carvalho Chehab 			return bytes_copied;
5300c0d06caSMauro Carvalho Chehab 		current_field = 2;
5310c0d06caSMauro Carvalho Chehab 		break;
5320c0d06caSMauro Carvalho Chehab 	}
5330c0d06caSMauro Carvalho Chehab 
5340c0d06caSMauro Carvalho Chehab 	dma_q->last_sav = sav_eav;
5350c0d06caSMauro Carvalho Chehab 
5360c0d06caSMauro Carvalho Chehab 	bytes_copied = cx231xx_copy_video_line(dev, dma_q, p_buffer,
5370c0d06caSMauro Carvalho Chehab 					       buffer_size, current_field);
5380c0d06caSMauro Carvalho Chehab 
5390c0d06caSMauro Carvalho Chehab 	return bytes_copied;
5400c0d06caSMauro Carvalho Chehab }
5410c0d06caSMauro Carvalho Chehab 
cx231xx_copy_video_line(struct cx231xx * dev,struct cx231xx_dmaqueue * dma_q,u8 * p_line,u32 length,int field_number)5420c0d06caSMauro Carvalho Chehab u32 cx231xx_copy_video_line(struct cx231xx *dev,
5430c0d06caSMauro Carvalho Chehab 			    struct cx231xx_dmaqueue *dma_q, u8 *p_line,
5440c0d06caSMauro Carvalho Chehab 			    u32 length, int field_number)
5450c0d06caSMauro Carvalho Chehab {
5460c0d06caSMauro Carvalho Chehab 	u32 bytes_to_copy;
5470c0d06caSMauro Carvalho Chehab 	struct cx231xx_buffer *buf;
5480c0d06caSMauro Carvalho Chehab 	u32 _line_size = dev->width * 2;
5490c0d06caSMauro Carvalho Chehab 
5500c0d06caSMauro Carvalho Chehab 	if (dma_q->current_field != field_number)
5510c0d06caSMauro Carvalho Chehab 		cx231xx_reset_video_buffer(dev, dma_q);
5520c0d06caSMauro Carvalho Chehab 
5530c0d06caSMauro Carvalho Chehab 	/* get the buffer pointer */
5540c0d06caSMauro Carvalho Chehab 	if (dev->USE_ISO)
5550c0d06caSMauro Carvalho Chehab 		buf = dev->video_mode.isoc_ctl.buf;
5560c0d06caSMauro Carvalho Chehab 	else
5570c0d06caSMauro Carvalho Chehab 		buf = dev->video_mode.bulk_ctl.buf;
5580c0d06caSMauro Carvalho Chehab 
5590c0d06caSMauro Carvalho Chehab 	/* Remember the field number for next time */
5600c0d06caSMauro Carvalho Chehab 	dma_q->current_field = field_number;
5610c0d06caSMauro Carvalho Chehab 
5620c0d06caSMauro Carvalho Chehab 	bytes_to_copy = dma_q->bytes_left_in_line;
5630c0d06caSMauro Carvalho Chehab 	if (bytes_to_copy > length)
5640c0d06caSMauro Carvalho Chehab 		bytes_to_copy = length;
5650c0d06caSMauro Carvalho Chehab 
5660c0d06caSMauro Carvalho Chehab 	if (dma_q->lines_completed >= dma_q->lines_per_field) {
5670c0d06caSMauro Carvalho Chehab 		dma_q->bytes_left_in_line -= bytes_to_copy;
5680c0d06caSMauro Carvalho Chehab 		dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0) ?
5690c0d06caSMauro Carvalho Chehab 					  0 : 1;
5700c0d06caSMauro Carvalho Chehab 		return 0;
5710c0d06caSMauro Carvalho Chehab 	}
5720c0d06caSMauro Carvalho Chehab 
5730c0d06caSMauro Carvalho Chehab 	dma_q->is_partial_line = 1;
5740c0d06caSMauro Carvalho Chehab 
5750c0d06caSMauro Carvalho Chehab 	/* If we don't have a buffer, just return the number of bytes we would
5760c0d06caSMauro Carvalho Chehab 	   have copied if we had a buffer. */
5770c0d06caSMauro Carvalho Chehab 	if (!buf) {
5780c0d06caSMauro Carvalho Chehab 		dma_q->bytes_left_in_line -= bytes_to_copy;
5790c0d06caSMauro Carvalho Chehab 		dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0)
5800c0d06caSMauro Carvalho Chehab 					 ? 0 : 1;
5810c0d06caSMauro Carvalho Chehab 		return bytes_to_copy;
5820c0d06caSMauro Carvalho Chehab 	}
5830c0d06caSMauro Carvalho Chehab 
5840c0d06caSMauro Carvalho Chehab 	/* copy the data to video buffer */
5850c0d06caSMauro Carvalho Chehab 	cx231xx_do_copy(dev, dma_q, p_line, bytes_to_copy);
5860c0d06caSMauro Carvalho Chehab 
5870c0d06caSMauro Carvalho Chehab 	dma_q->pos += bytes_to_copy;
5880c0d06caSMauro Carvalho Chehab 	dma_q->bytes_left_in_line -= bytes_to_copy;
5890c0d06caSMauro Carvalho Chehab 
5900c0d06caSMauro Carvalho Chehab 	if (dma_q->bytes_left_in_line == 0) {
5910c0d06caSMauro Carvalho Chehab 		dma_q->bytes_left_in_line = _line_size;
5920c0d06caSMauro Carvalho Chehab 		dma_q->lines_completed++;
5930c0d06caSMauro Carvalho Chehab 		dma_q->is_partial_line = 0;
5940c0d06caSMauro Carvalho Chehab 
5950c0d06caSMauro Carvalho Chehab 		if (cx231xx_is_buffer_done(dev, dma_q) && buf) {
5960c0d06caSMauro Carvalho Chehab 			buffer_filled(dev, dma_q, buf);
5970c0d06caSMauro Carvalho Chehab 
5980c0d06caSMauro Carvalho Chehab 			dma_q->pos = 0;
5990c0d06caSMauro Carvalho Chehab 			buf = NULL;
6000c0d06caSMauro Carvalho Chehab 			dma_q->lines_completed = 0;
6010c0d06caSMauro Carvalho Chehab 		}
6020c0d06caSMauro Carvalho Chehab 	}
6030c0d06caSMauro Carvalho Chehab 
6040c0d06caSMauro Carvalho Chehab 	return bytes_to_copy;
6050c0d06caSMauro Carvalho Chehab }
6060c0d06caSMauro Carvalho Chehab 
cx231xx_reset_video_buffer(struct cx231xx * dev,struct cx231xx_dmaqueue * dma_q)6070c0d06caSMauro Carvalho Chehab void cx231xx_reset_video_buffer(struct cx231xx *dev,
6080c0d06caSMauro Carvalho Chehab 				struct cx231xx_dmaqueue *dma_q)
6090c0d06caSMauro Carvalho Chehab {
6100c0d06caSMauro Carvalho Chehab 	struct cx231xx_buffer *buf;
6110c0d06caSMauro Carvalho Chehab 
6120c0d06caSMauro Carvalho Chehab 	/* handle the switch from field 1 to field 2 */
6130c0d06caSMauro Carvalho Chehab 	if (dma_q->current_field == 1) {
6140c0d06caSMauro Carvalho Chehab 		if (dma_q->lines_completed >= dma_q->lines_per_field)
6150c0d06caSMauro Carvalho Chehab 			dma_q->field1_done = 1;
6160c0d06caSMauro Carvalho Chehab 		else
6170c0d06caSMauro Carvalho Chehab 			dma_q->field1_done = 0;
6180c0d06caSMauro Carvalho Chehab 	}
6190c0d06caSMauro Carvalho Chehab 
6200c0d06caSMauro Carvalho Chehab 	if (dev->USE_ISO)
6210c0d06caSMauro Carvalho Chehab 		buf = dev->video_mode.isoc_ctl.buf;
6220c0d06caSMauro Carvalho Chehab 	else
6230c0d06caSMauro Carvalho Chehab 		buf = dev->video_mode.bulk_ctl.buf;
6240c0d06caSMauro Carvalho Chehab 
6250c0d06caSMauro Carvalho Chehab 	if (buf == NULL) {
6260c0d06caSMauro Carvalho Chehab 		/* first try to get the buffer */
6270c0d06caSMauro Carvalho Chehab 		get_next_buf(dma_q, &buf);
6280c0d06caSMauro Carvalho Chehab 
6290c0d06caSMauro Carvalho Chehab 		dma_q->pos = 0;
6300c0d06caSMauro Carvalho Chehab 		dma_q->field1_done = 0;
6310c0d06caSMauro Carvalho Chehab 		dma_q->current_field = -1;
6320c0d06caSMauro Carvalho Chehab 	}
6330c0d06caSMauro Carvalho Chehab 
6340c0d06caSMauro Carvalho Chehab 	/* reset the counters */
6350c0d06caSMauro Carvalho Chehab 	dma_q->bytes_left_in_line = dev->width << 1;
6360c0d06caSMauro Carvalho Chehab 	dma_q->lines_completed = 0;
6370c0d06caSMauro Carvalho Chehab }
6380c0d06caSMauro Carvalho Chehab 
cx231xx_do_copy(struct cx231xx * dev,struct cx231xx_dmaqueue * dma_q,u8 * p_buffer,u32 bytes_to_copy)6390c0d06caSMauro Carvalho Chehab int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
6400c0d06caSMauro Carvalho Chehab 		    u8 *p_buffer, u32 bytes_to_copy)
6410c0d06caSMauro Carvalho Chehab {
6420c0d06caSMauro Carvalho Chehab 	u8 *p_out_buffer = NULL;
6430c0d06caSMauro Carvalho Chehab 	u32 current_line_bytes_copied = 0;
6440c0d06caSMauro Carvalho Chehab 	struct cx231xx_buffer *buf;
6450c0d06caSMauro Carvalho Chehab 	u32 _line_size = dev->width << 1;
6460c0d06caSMauro Carvalho Chehab 	void *startwrite;
6470c0d06caSMauro Carvalho Chehab 	int offset, lencopy;
6480c0d06caSMauro Carvalho Chehab 
6490c0d06caSMauro Carvalho Chehab 	if (dev->USE_ISO)
6500c0d06caSMauro Carvalho Chehab 		buf = dev->video_mode.isoc_ctl.buf;
6510c0d06caSMauro Carvalho Chehab 	else
6520c0d06caSMauro Carvalho Chehab 		buf = dev->video_mode.bulk_ctl.buf;
6530c0d06caSMauro Carvalho Chehab 
6540c0d06caSMauro Carvalho Chehab 	if (buf == NULL)
6550c0d06caSMauro Carvalho Chehab 		return -1;
6560c0d06caSMauro Carvalho Chehab 
6577c617138SHans Verkuil 	p_out_buffer = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
6580c0d06caSMauro Carvalho Chehab 
6590c0d06caSMauro Carvalho Chehab 	current_line_bytes_copied = _line_size - dma_q->bytes_left_in_line;
6600c0d06caSMauro Carvalho Chehab 
6610c0d06caSMauro Carvalho Chehab 	/* Offset field 2 one line from the top of the buffer */
6620c0d06caSMauro Carvalho Chehab 	offset = (dma_q->current_field == 1) ? 0 : _line_size;
6630c0d06caSMauro Carvalho Chehab 
6640c0d06caSMauro Carvalho Chehab 	/* Offset for field 2 */
6650c0d06caSMauro Carvalho Chehab 	startwrite = p_out_buffer + offset;
6660c0d06caSMauro Carvalho Chehab 
6670c0d06caSMauro Carvalho Chehab 	/* lines already completed in the current field */
6680c0d06caSMauro Carvalho Chehab 	startwrite += (dma_q->lines_completed * _line_size * 2);
6690c0d06caSMauro Carvalho Chehab 
6700c0d06caSMauro Carvalho Chehab 	/* bytes already completed in the current line */
6710c0d06caSMauro Carvalho Chehab 	startwrite += current_line_bytes_copied;
6720c0d06caSMauro Carvalho Chehab 
6730c0d06caSMauro Carvalho Chehab 	lencopy = dma_q->bytes_left_in_line > bytes_to_copy ?
6740c0d06caSMauro Carvalho Chehab 		  bytes_to_copy : dma_q->bytes_left_in_line;
6750c0d06caSMauro Carvalho Chehab 
6767c617138SHans Verkuil 	if ((u8 *)(startwrite + lencopy) > (u8 *)(p_out_buffer + dev->size))
6770c0d06caSMauro Carvalho Chehab 		return 0;
6780c0d06caSMauro Carvalho Chehab 
6790c0d06caSMauro Carvalho Chehab 	/* The below copies the UYVY data straight into video buffer */
6800c0d06caSMauro Carvalho Chehab 	cx231xx_swab((u16 *) p_buffer, (u16 *) startwrite, (u16) lencopy);
6810c0d06caSMauro Carvalho Chehab 
6820c0d06caSMauro Carvalho Chehab 	return 0;
6830c0d06caSMauro Carvalho Chehab }
6840c0d06caSMauro Carvalho Chehab 
cx231xx_swab(u16 * from,u16 * to,u16 len)6850c0d06caSMauro Carvalho Chehab void cx231xx_swab(u16 *from, u16 *to, u16 len)
6860c0d06caSMauro Carvalho Chehab {
6870c0d06caSMauro Carvalho Chehab 	u16 i;
6880c0d06caSMauro Carvalho Chehab 
6890c0d06caSMauro Carvalho Chehab 	if (len <= 0)
6900c0d06caSMauro Carvalho Chehab 		return;
6910c0d06caSMauro Carvalho Chehab 
6920c0d06caSMauro Carvalho Chehab 	for (i = 0; i < len / 2; i++)
6930c0d06caSMauro Carvalho Chehab 		to[i] = (from[i] << 8) | (from[i] >> 8);
6940c0d06caSMauro Carvalho Chehab }
6950c0d06caSMauro Carvalho Chehab 
cx231xx_is_buffer_done(struct cx231xx * dev,struct cx231xx_dmaqueue * dma_q)6960c0d06caSMauro Carvalho Chehab u8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q)
6970c0d06caSMauro Carvalho Chehab {
6980c0d06caSMauro Carvalho Chehab 	u8 buffer_complete = 0;
6990c0d06caSMauro Carvalho Chehab 
7000c0d06caSMauro Carvalho Chehab 	/* Dual field stream */
7010c0d06caSMauro Carvalho Chehab 	buffer_complete = ((dma_q->current_field == 2) &&
7020c0d06caSMauro Carvalho Chehab 			   (dma_q->lines_completed >= dma_q->lines_per_field) &&
7030c0d06caSMauro Carvalho Chehab 			    dma_q->field1_done);
7040c0d06caSMauro Carvalho Chehab 
7050c0d06caSMauro Carvalho Chehab 	return buffer_complete;
7060c0d06caSMauro Carvalho Chehab }
7070c0d06caSMauro Carvalho Chehab 
7080c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------
7090c0d06caSMauro Carvalho Chehab 	Videobuf operations
7100c0d06caSMauro Carvalho Chehab    ------------------------------------------------------------------*/
7110c0d06caSMauro Carvalho Chehab 
queue_setup(struct vb2_queue * vq,unsigned int * nbuffers,unsigned int * nplanes,unsigned int sizes[],struct device * alloc_devs[])7127c617138SHans Verkuil static int queue_setup(struct vb2_queue *vq,
7137c617138SHans Verkuil 		       unsigned int *nbuffers, unsigned int *nplanes,
7147c617138SHans Verkuil 		       unsigned int sizes[], struct device *alloc_devs[])
7150c0d06caSMauro Carvalho Chehab {
7167c617138SHans Verkuil 	struct cx231xx *dev = vb2_get_drv_priv(vq);
717ca44d57aSBenjamin Gaignard 	unsigned int q_num_bufs = vb2_get_num_buffers(vq);
7180c0d06caSMauro Carvalho Chehab 
7197c617138SHans Verkuil 	dev->size = (dev->width * dev->height * dev->format->depth + 7) >> 3;
7200c0d06caSMauro Carvalho Chehab 
721ca44d57aSBenjamin Gaignard 	if (q_num_bufs + *nbuffers < CX231XX_MIN_BUF)
722ca44d57aSBenjamin Gaignard 		*nbuffers = CX231XX_MIN_BUF - q_num_bufs;
7230c0d06caSMauro Carvalho Chehab 
7247c617138SHans Verkuil 	if (*nplanes)
7257c617138SHans Verkuil 		return sizes[0] < dev->size ? -EINVAL : 0;
7267c617138SHans Verkuil 	*nplanes = 1;
7277c617138SHans Verkuil 	sizes[0] = dev->size;
7283d263114SMauro Carvalho Chehab 
7290c0d06caSMauro Carvalho Chehab 	return 0;
7300c0d06caSMauro Carvalho Chehab }
7310c0d06caSMauro Carvalho Chehab 
buffer_queue(struct vb2_buffer * vb)7327c617138SHans Verkuil static void buffer_queue(struct vb2_buffer *vb)
7330c0d06caSMauro Carvalho Chehab {
7347c617138SHans Verkuil 	struct cx231xx_buffer *buf =
7357c617138SHans Verkuil 	    container_of(vb, struct cx231xx_buffer, vb.vb2_buf);
7367c617138SHans Verkuil 	struct cx231xx *dev = vb2_get_drv_priv(vb->vb2_queue);
7377c617138SHans Verkuil 	struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq;
7387c617138SHans Verkuil 	unsigned long flags;
7390c0d06caSMauro Carvalho Chehab 
7400c0d06caSMauro Carvalho Chehab 	spin_lock_irqsave(&dev->video_mode.slock, flags);
7417c617138SHans Verkuil 	list_add_tail(&buf->list, &vidq->active);
7427c617138SHans Verkuil 	spin_unlock_irqrestore(&dev->video_mode.slock, flags);
7437c617138SHans Verkuil }
7447c617138SHans Verkuil 
return_all_buffers(struct cx231xx * dev,enum vb2_buffer_state state)7457c617138SHans Verkuil static void return_all_buffers(struct cx231xx *dev,
7467c617138SHans Verkuil 			       enum vb2_buffer_state state)
7477c617138SHans Verkuil {
7487c617138SHans Verkuil 	struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq;
7497c617138SHans Verkuil 	struct cx231xx_buffer *buf, *node;
7507c617138SHans Verkuil 	unsigned long flags;
7517c617138SHans Verkuil 
7527c617138SHans Verkuil 	spin_lock_irqsave(&dev->video_mode.slock, flags);
7537c617138SHans Verkuil 	if (dev->USE_ISO)
7540c0d06caSMauro Carvalho Chehab 		dev->video_mode.isoc_ctl.buf = NULL;
7557c617138SHans Verkuil 	else
7560c0d06caSMauro Carvalho Chehab 		dev->video_mode.bulk_ctl.buf = NULL;
7577c617138SHans Verkuil 	list_for_each_entry_safe(buf, node, &vidq->active, list) {
7587c617138SHans Verkuil 		list_del(&buf->list);
7597c617138SHans Verkuil 		vb2_buffer_done(&buf->vb.vb2_buf, state);
7600c0d06caSMauro Carvalho Chehab 	}
7610c0d06caSMauro Carvalho Chehab 	spin_unlock_irqrestore(&dev->video_mode.slock, flags);
7620c0d06caSMauro Carvalho Chehab }
7630c0d06caSMauro Carvalho Chehab 
start_streaming(struct vb2_queue * vq,unsigned int count)7647c617138SHans Verkuil static int start_streaming(struct vb2_queue *vq, unsigned int count)
7650c0d06caSMauro Carvalho Chehab {
7667c617138SHans Verkuil 	struct cx231xx *dev = vb2_get_drv_priv(vq);
7677c617138SHans Verkuil 	struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq;
7687c617138SHans Verkuil 	int ret = 0;
7690c0d06caSMauro Carvalho Chehab 
7707c617138SHans Verkuil 	vidq->sequence = 0;
7710c0d06caSMauro Carvalho Chehab 	dev->mode_tv = 0;
7727c617138SHans Verkuil 
7737c617138SHans Verkuil 	cx231xx_enable_analog_tuner(dev);
7740c0d06caSMauro Carvalho Chehab 	if (dev->USE_ISO)
7757c617138SHans Verkuil 		ret = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS,
7760c0d06caSMauro Carvalho Chehab 					CX231XX_NUM_BUFS,
7770c0d06caSMauro Carvalho Chehab 					dev->video_mode.max_pkt_size,
7780c0d06caSMauro Carvalho Chehab 					cx231xx_isoc_copy);
7790c0d06caSMauro Carvalho Chehab 	else
7807c617138SHans Verkuil 		ret = cx231xx_init_bulk(dev, CX231XX_NUM_PACKETS,
7810c0d06caSMauro Carvalho Chehab 					CX231XX_NUM_BUFS,
7820c0d06caSMauro Carvalho Chehab 					dev->video_mode.max_pkt_size,
7830c0d06caSMauro Carvalho Chehab 					cx231xx_bulk_copy);
7847c617138SHans Verkuil 	if (ret)
7857c617138SHans Verkuil 		return_all_buffers(dev, VB2_BUF_STATE_QUEUED);
7867c617138SHans Verkuil 	call_all(dev, video, s_stream, 1);
7877c617138SHans Verkuil 	return ret;
7880c0d06caSMauro Carvalho Chehab }
7890c0d06caSMauro Carvalho Chehab 
stop_streaming(struct vb2_queue * vq)7907c617138SHans Verkuil static void stop_streaming(struct vb2_queue *vq)
7910c0d06caSMauro Carvalho Chehab {
7927c617138SHans Verkuil 	struct cx231xx *dev = vb2_get_drv_priv(vq);
7930c0d06caSMauro Carvalho Chehab 
7947c617138SHans Verkuil 	call_all(dev, video, s_stream, 0);
7957c617138SHans Verkuil 	return_all_buffers(dev, VB2_BUF_STATE_ERROR);
7960c0d06caSMauro Carvalho Chehab }
7970c0d06caSMauro Carvalho Chehab 
798*d2ae63c2SChristophe JAILLET static const struct vb2_ops cx231xx_video_qops = {
7997c617138SHans Verkuil 	.queue_setup		= queue_setup,
8000c0d06caSMauro Carvalho Chehab 	.buf_queue		= buffer_queue,
8017c617138SHans Verkuil 	.start_streaming	= start_streaming,
8027c617138SHans Verkuil 	.stop_streaming		= stop_streaming,
8037c617138SHans Verkuil 	.wait_prepare		= vb2_ops_wait_prepare,
8047c617138SHans Verkuil 	.wait_finish		= vb2_ops_wait_finish,
8050c0d06caSMauro Carvalho Chehab };
8060c0d06caSMauro Carvalho Chehab 
8070c0d06caSMauro Carvalho Chehab /*********************  v4l2 interface  **************************************/
8080c0d06caSMauro Carvalho Chehab 
video_mux(struct cx231xx * dev,int index)8090c0d06caSMauro Carvalho Chehab void video_mux(struct cx231xx *dev, int index)
8100c0d06caSMauro Carvalho Chehab {
8110c0d06caSMauro Carvalho Chehab 	dev->video_input = index;
8120c0d06caSMauro Carvalho Chehab 	dev->ctl_ainput = INPUT(index)->amux;
8130c0d06caSMauro Carvalho Chehab 
8140c0d06caSMauro Carvalho Chehab 	cx231xx_set_video_input_mux(dev, index);
8150c0d06caSMauro Carvalho Chehab 
8160c0d06caSMauro Carvalho Chehab 	cx25840_call(dev, video, s_routing, INPUT(index)->vmux, 0, 0);
8170c0d06caSMauro Carvalho Chehab 
8180c0d06caSMauro Carvalho Chehab 	cx231xx_set_audio_input(dev, dev->ctl_ainput);
8190c0d06caSMauro Carvalho Chehab 
820336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev, "video_mux : %d\n", index);
8210c0d06caSMauro Carvalho Chehab 
8220c0d06caSMauro Carvalho Chehab 	/* do mode control overrides if required */
8230c0d06caSMauro Carvalho Chehab 	cx231xx_do_mode_ctrl_overrides(dev);
8240c0d06caSMauro Carvalho Chehab }
8250c0d06caSMauro Carvalho Chehab 
8260c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------
8270c0d06caSMauro Carvalho Chehab 	IOCTL vidioc handling
8280c0d06caSMauro Carvalho Chehab    ------------------------------------------------------------------*/
8290c0d06caSMauro Carvalho Chehab 
vidioc_g_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)8300c0d06caSMauro Carvalho Chehab static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
8310c0d06caSMauro Carvalho Chehab 				struct v4l2_format *f)
8320c0d06caSMauro Carvalho Chehab {
8337c617138SHans Verkuil 	struct cx231xx *dev = video_drvdata(file);
8340c0d06caSMauro Carvalho Chehab 
8350c0d06caSMauro Carvalho Chehab 	f->fmt.pix.width = dev->width;
8360c0d06caSMauro Carvalho Chehab 	f->fmt.pix.height = dev->height;
8370c0d06caSMauro Carvalho Chehab 	f->fmt.pix.pixelformat = dev->format->fourcc;
8380c0d06caSMauro Carvalho Chehab 	f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3;
8390c0d06caSMauro Carvalho Chehab 	f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height;
8400c0d06caSMauro Carvalho Chehab 	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
8410c0d06caSMauro Carvalho Chehab 
8420c0d06caSMauro Carvalho Chehab 	f->fmt.pix.field = V4L2_FIELD_INTERLACED;
8430c0d06caSMauro Carvalho Chehab 
8440c0d06caSMauro Carvalho Chehab 	return 0;
8450c0d06caSMauro Carvalho Chehab }
8460c0d06caSMauro Carvalho Chehab 
format_by_fourcc(unsigned int fourcc)8470c0d06caSMauro Carvalho Chehab static struct cx231xx_fmt *format_by_fourcc(unsigned int fourcc)
8480c0d06caSMauro Carvalho Chehab {
8490c0d06caSMauro Carvalho Chehab 	unsigned int i;
8500c0d06caSMauro Carvalho Chehab 
8510c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(format); i++)
8520c0d06caSMauro Carvalho Chehab 		if (format[i].fourcc == fourcc)
8530c0d06caSMauro Carvalho Chehab 			return &format[i];
8540c0d06caSMauro Carvalho Chehab 
8550c0d06caSMauro Carvalho Chehab 	return NULL;
8560c0d06caSMauro Carvalho Chehab }
8570c0d06caSMauro Carvalho Chehab 
vidioc_try_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)8580c0d06caSMauro Carvalho Chehab static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
8590c0d06caSMauro Carvalho Chehab 				  struct v4l2_format *f)
8600c0d06caSMauro Carvalho Chehab {
8617c617138SHans Verkuil 	struct cx231xx *dev = video_drvdata(file);
8620c0d06caSMauro Carvalho Chehab 	unsigned int width = f->fmt.pix.width;
8630c0d06caSMauro Carvalho Chehab 	unsigned int height = f->fmt.pix.height;
8640c0d06caSMauro Carvalho Chehab 	unsigned int maxw = norm_maxw(dev);
8650c0d06caSMauro Carvalho Chehab 	unsigned int maxh = norm_maxh(dev);
8660c0d06caSMauro Carvalho Chehab 	struct cx231xx_fmt *fmt;
8670c0d06caSMauro Carvalho Chehab 
8680c0d06caSMauro Carvalho Chehab 	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
8690c0d06caSMauro Carvalho Chehab 	if (!fmt) {
8700c0d06caSMauro Carvalho Chehab 		cx231xx_videodbg("Fourcc format (%08x) invalid.\n",
8710c0d06caSMauro Carvalho Chehab 				 f->fmt.pix.pixelformat);
8720c0d06caSMauro Carvalho Chehab 		return -EINVAL;
8730c0d06caSMauro Carvalho Chehab 	}
8740c0d06caSMauro Carvalho Chehab 
8750c0d06caSMauro Carvalho Chehab 	/* width must even because of the YUYV format
8760c0d06caSMauro Carvalho Chehab 	   height must be even because of interlacing */
8770c0d06caSMauro Carvalho Chehab 	v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 1, 0);
8780c0d06caSMauro Carvalho Chehab 
8790c0d06caSMauro Carvalho Chehab 	f->fmt.pix.width = width;
8800c0d06caSMauro Carvalho Chehab 	f->fmt.pix.height = height;
8810c0d06caSMauro Carvalho Chehab 	f->fmt.pix.pixelformat = fmt->fourcc;
8828b735c13SHans Verkuil 	f->fmt.pix.bytesperline = (width * fmt->depth + 7) >> 3;
8830c0d06caSMauro Carvalho Chehab 	f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height;
8840c0d06caSMauro Carvalho Chehab 	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
8850c0d06caSMauro Carvalho Chehab 	f->fmt.pix.field = V4L2_FIELD_INTERLACED;
8860c0d06caSMauro Carvalho Chehab 
8870c0d06caSMauro Carvalho Chehab 	return 0;
8880c0d06caSMauro Carvalho Chehab }
8890c0d06caSMauro Carvalho Chehab 
vidioc_s_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)8900c0d06caSMauro Carvalho Chehab static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
8910c0d06caSMauro Carvalho Chehab 				struct v4l2_format *f)
8920c0d06caSMauro Carvalho Chehab {
8937c617138SHans Verkuil 	struct cx231xx *dev = video_drvdata(file);
894ebf984bbSHans Verkuil 	struct v4l2_subdev_format format = {
895ebf984bbSHans Verkuil 		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
896ebf984bbSHans Verkuil 	};
8977c617138SHans Verkuil 	int rc;
8980c0d06caSMauro Carvalho Chehab 
8997c617138SHans Verkuil 	rc = vidioc_try_fmt_vid_cap(file, priv, f);
9007c617138SHans Verkuil 	if (rc)
9010c0d06caSMauro Carvalho Chehab 		return rc;
9020c0d06caSMauro Carvalho Chehab 
9037c617138SHans Verkuil 	if (vb2_is_busy(&dev->vidq)) {
904336fea92SMauro Carvalho Chehab 		dev_err(dev->dev, "%s: queue busy\n", __func__);
9050c0d06caSMauro Carvalho Chehab 		return -EBUSY;
9060c0d06caSMauro Carvalho Chehab 	}
9070c0d06caSMauro Carvalho Chehab 
9080c0d06caSMauro Carvalho Chehab 	/* set new image size */
9090c0d06caSMauro Carvalho Chehab 	dev->width = f->fmt.pix.width;
9100c0d06caSMauro Carvalho Chehab 	dev->height = f->fmt.pix.height;
9117c617138SHans Verkuil 	dev->format = format_by_fourcc(f->fmt.pix.pixelformat);
9120c0d06caSMauro Carvalho Chehab 
913ebf984bbSHans Verkuil 	v4l2_fill_mbus_format(&format.format, &f->fmt.pix, MEDIA_BUS_FMT_FIXED);
914ebf984bbSHans Verkuil 	call_all(dev, pad, set_fmt, NULL, &format);
915ebf984bbSHans Verkuil 	v4l2_fill_pix_format(&f->fmt.pix, &format.format);
9160c0d06caSMauro Carvalho Chehab 
9170c0d06caSMauro Carvalho Chehab 	return rc;
9180c0d06caSMauro Carvalho Chehab }
9190c0d06caSMauro Carvalho Chehab 
vidioc_g_std(struct file * file,void * priv,v4l2_std_id * id)9200c0d06caSMauro Carvalho Chehab static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
9210c0d06caSMauro Carvalho Chehab {
9227c617138SHans Verkuil 	struct cx231xx *dev = video_drvdata(file);
9230c0d06caSMauro Carvalho Chehab 
9240c0d06caSMauro Carvalho Chehab 	*id = dev->norm;
9250c0d06caSMauro Carvalho Chehab 	return 0;
9260c0d06caSMauro Carvalho Chehab }
9270c0d06caSMauro Carvalho Chehab 
vidioc_s_std(struct file * file,void * priv,v4l2_std_id norm)928314527acSHans Verkuil static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
9290c0d06caSMauro Carvalho Chehab {
9307c617138SHans Verkuil 	struct cx231xx *dev = video_drvdata(file);
931ebf984bbSHans Verkuil 	struct v4l2_subdev_format format = {
932ebf984bbSHans Verkuil 		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
933ebf984bbSHans Verkuil 	};
9340c0d06caSMauro Carvalho Chehab 
935314527acSHans Verkuil 	if (dev->norm == norm)
936d61072a4SHans Verkuil 		return 0;
937d61072a4SHans Verkuil 
9387c617138SHans Verkuil 	if (vb2_is_busy(&dev->vidq))
939d61072a4SHans Verkuil 		return -EBUSY;
9400c0d06caSMauro Carvalho Chehab 
941314527acSHans Verkuil 	dev->norm = norm;
9420c0d06caSMauro Carvalho Chehab 
9430c0d06caSMauro Carvalho Chehab 	/* Adjusts width/height, if needed */
944d61072a4SHans Verkuil 	dev->width = 720;
945d61072a4SHans Verkuil 	dev->height = (dev->norm & V4L2_STD_625_50) ? 576 : 480;
9460c0d06caSMauro Carvalho Chehab 
9478774bed9SLaurent Pinchart 	call_all(dev, video, s_std, dev->norm);
9480c0d06caSMauro Carvalho Chehab 
9490c0d06caSMauro Carvalho Chehab 	/* We need to reset basic properties in the decoder related to
9500c0d06caSMauro Carvalho Chehab 	   resolution (since a standard change effects things like the number
9510c0d06caSMauro Carvalho Chehab 	   of lines in VACT, etc) */
952ebf984bbSHans Verkuil 	format.format.code = MEDIA_BUS_FMT_FIXED;
953ebf984bbSHans Verkuil 	format.format.width = dev->width;
954ebf984bbSHans Verkuil 	format.format.height = dev->height;
955ebf984bbSHans Verkuil 	call_all(dev, pad, set_fmt, NULL, &format);
9560c0d06caSMauro Carvalho Chehab 
9570c0d06caSMauro Carvalho Chehab 	/* do mode control overrides */
9580c0d06caSMauro Carvalho Chehab 	cx231xx_do_mode_ctrl_overrides(dev);
9590c0d06caSMauro Carvalho Chehab 
9600c0d06caSMauro Carvalho Chehab 	return 0;
9610c0d06caSMauro Carvalho Chehab }
9620c0d06caSMauro Carvalho Chehab 
9630c0d06caSMauro Carvalho Chehab static const char *iname[] = {
9640c0d06caSMauro Carvalho Chehab 	[CX231XX_VMUX_COMPOSITE1] = "Composite1",
9650c0d06caSMauro Carvalho Chehab 	[CX231XX_VMUX_SVIDEO]     = "S-Video",
9660c0d06caSMauro Carvalho Chehab 	[CX231XX_VMUX_TELEVISION] = "Television",
9670c0d06caSMauro Carvalho Chehab 	[CX231XX_VMUX_CABLE]      = "Cable TV",
9680c0d06caSMauro Carvalho Chehab 	[CX231XX_VMUX_DVB]        = "DVB",
9690c0d06caSMauro Carvalho Chehab };
9700c0d06caSMauro Carvalho Chehab 
cx231xx_v4l2_create_entities(struct cx231xx * dev)9716168309aSMauro Carvalho Chehab void cx231xx_v4l2_create_entities(struct cx231xx *dev)
9726168309aSMauro Carvalho Chehab {
9736168309aSMauro Carvalho Chehab #if defined(CONFIG_MEDIA_CONTROLLER)
9746168309aSMauro Carvalho Chehab 	int ret, i;
9756168309aSMauro Carvalho Chehab 
9766168309aSMauro Carvalho Chehab 	/* Create entities for each input connector */
9776168309aSMauro Carvalho Chehab 	for (i = 0; i < MAX_CX231XX_INPUT; i++) {
9786168309aSMauro Carvalho Chehab 		struct media_entity *ent = &dev->input_ent[i];
9796168309aSMauro Carvalho Chehab 
9806168309aSMauro Carvalho Chehab 		if (!INPUT(i)->type)
9816168309aSMauro Carvalho Chehab 			break;
9826168309aSMauro Carvalho Chehab 
9836168309aSMauro Carvalho Chehab 		ent->name = iname[INPUT(i)->type];
9846168309aSMauro Carvalho Chehab 		ent->flags = MEDIA_ENT_FL_CONNECTOR;
9856168309aSMauro Carvalho Chehab 		dev->input_pad[i].flags = MEDIA_PAD_FL_SOURCE;
9866168309aSMauro Carvalho Chehab 
9876168309aSMauro Carvalho Chehab 		switch (INPUT(i)->type) {
9886168309aSMauro Carvalho Chehab 		case CX231XX_VMUX_COMPOSITE1:
9896168309aSMauro Carvalho Chehab 			ent->function = MEDIA_ENT_F_CONN_COMPOSITE;
9906168309aSMauro Carvalho Chehab 			break;
9916168309aSMauro Carvalho Chehab 		case CX231XX_VMUX_SVIDEO:
9926168309aSMauro Carvalho Chehab 			ent->function = MEDIA_ENT_F_CONN_SVIDEO;
9936168309aSMauro Carvalho Chehab 			break;
9946168309aSMauro Carvalho Chehab 		case CX231XX_VMUX_TELEVISION:
9956168309aSMauro Carvalho Chehab 		case CX231XX_VMUX_CABLE:
9966168309aSMauro Carvalho Chehab 		case CX231XX_VMUX_DVB:
9976168309aSMauro Carvalho Chehab 			/* The DVB core will handle it */
9986168309aSMauro Carvalho Chehab 			if (dev->tuner_type == TUNER_ABSENT)
9996168309aSMauro Carvalho Chehab 				continue;
1000df561f66SGustavo A. R. Silva 			fallthrough;
100122d50e9aSMauro Carvalho Chehab 		default: /* just to shut up a gcc warning */
10026168309aSMauro Carvalho Chehab 			ent->function = MEDIA_ENT_F_CONN_RF;
10036168309aSMauro Carvalho Chehab 			break;
10046168309aSMauro Carvalho Chehab 		}
10056168309aSMauro Carvalho Chehab 
10066168309aSMauro Carvalho Chehab 		ret = media_entity_pads_init(ent, 1, &dev->input_pad[i]);
10076168309aSMauro Carvalho Chehab 		if (ret < 0)
10086168309aSMauro Carvalho Chehab 			pr_err("failed to initialize input pad[%d]!\n", i);
10096168309aSMauro Carvalho Chehab 
10106168309aSMauro Carvalho Chehab 		ret = media_device_register_entity(dev->media_dev, ent);
10116168309aSMauro Carvalho Chehab 		if (ret < 0)
10126168309aSMauro Carvalho Chehab 			pr_err("failed to register input entity %d!\n", i);
10136168309aSMauro Carvalho Chehab 	}
10146168309aSMauro Carvalho Chehab #endif
10156168309aSMauro Carvalho Chehab }
10166168309aSMauro Carvalho Chehab 
cx231xx_enum_input(struct file * file,void * priv,struct v4l2_input * i)1017b86d1544SHans Verkuil int cx231xx_enum_input(struct file *file, void *priv,
10180c0d06caSMauro Carvalho Chehab 			     struct v4l2_input *i)
10190c0d06caSMauro Carvalho Chehab {
10207c617138SHans Verkuil 	struct cx231xx *dev = video_drvdata(file);
10210c0d06caSMauro Carvalho Chehab 	u32 gen_stat;
1022e54560d9SAndrzej Hajda 	unsigned int n;
1023e54560d9SAndrzej Hajda 	int ret;
10240c0d06caSMauro Carvalho Chehab 
10250c0d06caSMauro Carvalho Chehab 	n = i->index;
10260c0d06caSMauro Carvalho Chehab 	if (n >= MAX_CX231XX_INPUT)
10270c0d06caSMauro Carvalho Chehab 		return -EINVAL;
10280c0d06caSMauro Carvalho Chehab 	if (0 == INPUT(n)->type)
10290c0d06caSMauro Carvalho Chehab 		return -EINVAL;
10300c0d06caSMauro Carvalho Chehab 
10310c0d06caSMauro Carvalho Chehab 	i->index = n;
10320c0d06caSMauro Carvalho Chehab 	i->type = V4L2_INPUT_TYPE_CAMERA;
10330c0d06caSMauro Carvalho Chehab 
1034cc1e6315SMauro Carvalho Chehab 	strscpy(i->name, iname[INPUT(n)->type], sizeof(i->name));
10350c0d06caSMauro Carvalho Chehab 
10360c0d06caSMauro Carvalho Chehab 	if ((CX231XX_VMUX_TELEVISION == INPUT(n)->type) ||
10370c0d06caSMauro Carvalho Chehab 	    (CX231XX_VMUX_CABLE == INPUT(n)->type))
10380c0d06caSMauro Carvalho Chehab 		i->type = V4L2_INPUT_TYPE_TUNER;
10390c0d06caSMauro Carvalho Chehab 
104060acf187SHans Verkuil 	i->std = dev->vdev.tvnorms;
10410c0d06caSMauro Carvalho Chehab 
10420c0d06caSMauro Carvalho Chehab 	/* If they are asking about the active input, read signal status */
10430c0d06caSMauro Carvalho Chehab 	if (n == dev->video_input) {
10440c0d06caSMauro Carvalho Chehab 		ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
10450c0d06caSMauro Carvalho Chehab 					    GEN_STAT, 2, &gen_stat, 4);
10460c0d06caSMauro Carvalho Chehab 		if (ret > 0) {
10470c0d06caSMauro Carvalho Chehab 			if ((gen_stat & FLD_VPRES) == 0x00)
10480c0d06caSMauro Carvalho Chehab 				i->status |= V4L2_IN_ST_NO_SIGNAL;
10490c0d06caSMauro Carvalho Chehab 			if ((gen_stat & FLD_HLOCK) == 0x00)
10500c0d06caSMauro Carvalho Chehab 				i->status |= V4L2_IN_ST_NO_H_LOCK;
10510c0d06caSMauro Carvalho Chehab 		}
10520c0d06caSMauro Carvalho Chehab 	}
10530c0d06caSMauro Carvalho Chehab 
10540c0d06caSMauro Carvalho Chehab 	return 0;
10550c0d06caSMauro Carvalho Chehab }
10560c0d06caSMauro Carvalho Chehab 
cx231xx_g_input(struct file * file,void * priv,unsigned int * i)1057b86d1544SHans Verkuil int cx231xx_g_input(struct file *file, void *priv, unsigned int *i)
10580c0d06caSMauro Carvalho Chehab {
10597c617138SHans Verkuil 	struct cx231xx *dev = video_drvdata(file);
10600c0d06caSMauro Carvalho Chehab 
10610c0d06caSMauro Carvalho Chehab 	*i = dev->video_input;
10620c0d06caSMauro Carvalho Chehab 
10630c0d06caSMauro Carvalho Chehab 	return 0;
10640c0d06caSMauro Carvalho Chehab }
10650c0d06caSMauro Carvalho Chehab 
cx231xx_s_input(struct file * file,void * priv,unsigned int i)1066b86d1544SHans Verkuil int cx231xx_s_input(struct file *file, void *priv, unsigned int i)
10670c0d06caSMauro Carvalho Chehab {
10687c617138SHans Verkuil 	struct cx231xx *dev = video_drvdata(file);
10690c0d06caSMauro Carvalho Chehab 
10700c0d06caSMauro Carvalho Chehab 	dev->mode_tv = 0;
10710c0d06caSMauro Carvalho Chehab 
10720c0d06caSMauro Carvalho Chehab 	if (i >= MAX_CX231XX_INPUT)
10730c0d06caSMauro Carvalho Chehab 		return -EINVAL;
10740c0d06caSMauro Carvalho Chehab 	if (0 == INPUT(i)->type)
10750c0d06caSMauro Carvalho Chehab 		return -EINVAL;
10760c0d06caSMauro Carvalho Chehab 
10770c0d06caSMauro Carvalho Chehab 	video_mux(dev, i);
10780c0d06caSMauro Carvalho Chehab 
10790c0d06caSMauro Carvalho Chehab 	if (INPUT(i)->type == CX231XX_VMUX_TELEVISION ||
10800c0d06caSMauro Carvalho Chehab 	    INPUT(i)->type == CX231XX_VMUX_CABLE) {
10810c0d06caSMauro Carvalho Chehab 		/* There's a tuner, so reset the standard and put it on the
10820c0d06caSMauro Carvalho Chehab 		   last known frequency (since it was probably powered down
10830c0d06caSMauro Carvalho Chehab 		   until now */
10848774bed9SLaurent Pinchart 		call_all(dev, video, s_std, dev->norm);
10850c0d06caSMauro Carvalho Chehab 	}
10860c0d06caSMauro Carvalho Chehab 
10870c0d06caSMauro Carvalho Chehab 	return 0;
10880c0d06caSMauro Carvalho Chehab }
10890c0d06caSMauro Carvalho Chehab 
cx231xx_g_tuner(struct file * file,void * priv,struct v4l2_tuner * t)1090b86d1544SHans Verkuil int cx231xx_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
10910c0d06caSMauro Carvalho Chehab {
10927c617138SHans Verkuil 	struct cx231xx *dev = video_drvdata(file);
10930c0d06caSMauro Carvalho Chehab 
10940c0d06caSMauro Carvalho Chehab 	if (0 != t->index)
10950c0d06caSMauro Carvalho Chehab 		return -EINVAL;
10960c0d06caSMauro Carvalho Chehab 
1097cc1e6315SMauro Carvalho Chehab 	strscpy(t->name, "Tuner", sizeof(t->name));
10980c0d06caSMauro Carvalho Chehab 
10990c0d06caSMauro Carvalho Chehab 	t->type = V4L2_TUNER_ANALOG_TV;
11000c0d06caSMauro Carvalho Chehab 	t->capability = V4L2_TUNER_CAP_NORM;
11010c0d06caSMauro Carvalho Chehab 	t->rangehigh = 0xffffffffUL;
11020c0d06caSMauro Carvalho Chehab 	t->signal = 0xffff;	/* LOCKED */
1103b251f957SHans Verkuil 	call_all(dev, tuner, g_tuner, t);
11040c0d06caSMauro Carvalho Chehab 
11050c0d06caSMauro Carvalho Chehab 	return 0;
11060c0d06caSMauro Carvalho Chehab }
11070c0d06caSMauro Carvalho Chehab 
cx231xx_s_tuner(struct file * file,void * priv,const struct v4l2_tuner * t)11082f73c7c5SHans Verkuil int cx231xx_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t)
11090c0d06caSMauro Carvalho Chehab {
11100c0d06caSMauro Carvalho Chehab 	if (0 != t->index)
11110c0d06caSMauro Carvalho Chehab 		return -EINVAL;
11120c0d06caSMauro Carvalho Chehab 	return 0;
11130c0d06caSMauro Carvalho Chehab }
11140c0d06caSMauro Carvalho Chehab 
cx231xx_g_frequency(struct file * file,void * priv,struct v4l2_frequency * f)1115b86d1544SHans Verkuil int cx231xx_g_frequency(struct file *file, void *priv,
11160c0d06caSMauro Carvalho Chehab 			      struct v4l2_frequency *f)
11170c0d06caSMauro Carvalho Chehab {
11187c617138SHans Verkuil 	struct cx231xx *dev = video_drvdata(file);
11190c0d06caSMauro Carvalho Chehab 
1120b251f957SHans Verkuil 	if (f->tuner)
1121b251f957SHans Verkuil 		return -EINVAL;
1122b251f957SHans Verkuil 
11230c0d06caSMauro Carvalho Chehab 	f->frequency = dev->ctl_freq;
11240c0d06caSMauro Carvalho Chehab 
11250c0d06caSMauro Carvalho Chehab 	return 0;
11260c0d06caSMauro Carvalho Chehab }
11270c0d06caSMauro Carvalho Chehab 
cx231xx_s_frequency(struct file * file,void * priv,const struct v4l2_frequency * f)1128b86d1544SHans Verkuil int cx231xx_s_frequency(struct file *file, void *priv,
1129b530a447SHans Verkuil 			      const struct v4l2_frequency *f)
11300c0d06caSMauro Carvalho Chehab {
11317c617138SHans Verkuil 	struct cx231xx *dev = video_drvdata(file);
1132b530a447SHans Verkuil 	struct v4l2_frequency new_freq = *f;
11333c1ccbadSBrad Love 	int rc, need_if_freq = 0;
11340c0d06caSMauro Carvalho Chehab 	u32 if_frequency = 5400000;
11350c0d06caSMauro Carvalho Chehab 
1136336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev,
1137b7085c08SMauro Carvalho Chehab 		"Enter vidioc_s_frequency()f->frequency=%d;f->type=%d\n",
11380c0d06caSMauro Carvalho Chehab 		f->frequency, f->type);
11390c0d06caSMauro Carvalho Chehab 
11400c0d06caSMauro Carvalho Chehab 	if (0 != f->tuner)
11410c0d06caSMauro Carvalho Chehab 		return -EINVAL;
11420c0d06caSMauro Carvalho Chehab 
11430c0d06caSMauro Carvalho Chehab 	/* set pre channel change settings in DIF first */
11440c0d06caSMauro Carvalho Chehab 	rc = cx231xx_tuner_pre_channel_change(dev);
11450c0d06caSMauro Carvalho Chehab 
11463c1ccbadSBrad Love 	switch (dev->model) { /* i2c device tuners */
11473c1ccbadSBrad Love 	case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx:
11483c1ccbadSBrad Love 	case CX231XX_BOARD_HAUPPAUGE_935C:
11493c1ccbadSBrad Love 	case CX231XX_BOARD_HAUPPAUGE_955Q:
11503c1ccbadSBrad Love 	case CX231XX_BOARD_HAUPPAUGE_975:
11513c1ccbadSBrad Love 	case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD:
11523c1ccbadSBrad Love 		if (dev->cx231xx_set_analog_freq)
11533c1ccbadSBrad Love 			dev->cx231xx_set_analog_freq(dev, f->frequency);
11543c1ccbadSBrad Love 		dev->ctl_freq = f->frequency;
11553c1ccbadSBrad Love 		need_if_freq = 1;
11563c1ccbadSBrad Love 		break;
11573c1ccbadSBrad Love 	default:
11580c0d06caSMauro Carvalho Chehab 		call_all(dev, tuner, s_frequency, f);
1159b530a447SHans Verkuil 		call_all(dev, tuner, g_frequency, &new_freq);
1160b530a447SHans Verkuil 		dev->ctl_freq = new_freq.frequency;
11613c1ccbadSBrad Love 		break;
11623c1ccbadSBrad Love 	}
11633c1ccbadSBrad Love 
11643c1ccbadSBrad Love 	pr_debug("%s() %u  :  %u\n", __func__, f->frequency, dev->ctl_freq);
11650c0d06caSMauro Carvalho Chehab 
11660c0d06caSMauro Carvalho Chehab 	/* set post channel change settings in DIF first */
11670c0d06caSMauro Carvalho Chehab 	rc = cx231xx_tuner_post_channel_change(dev);
11680c0d06caSMauro Carvalho Chehab 
11693c1ccbadSBrad Love 	if (need_if_freq || dev->tuner_type == TUNER_NXP_TDA18271) {
11700c0d06caSMauro Carvalho Chehab 		if (dev->norm & (V4L2_STD_MN | V4L2_STD_NTSC_443))
11710c0d06caSMauro Carvalho Chehab 			if_frequency = 5400000;  /*5.4MHz	*/
11720c0d06caSMauro Carvalho Chehab 		else if (dev->norm & V4L2_STD_B)
11730c0d06caSMauro Carvalho Chehab 			if_frequency = 6000000;  /*6.0MHz	*/
11740c0d06caSMauro Carvalho Chehab 		else if (dev->norm & (V4L2_STD_PAL_DK | V4L2_STD_SECAM_DK))
11750c0d06caSMauro Carvalho Chehab 			if_frequency = 6900000;  /*6.9MHz	*/
11760c0d06caSMauro Carvalho Chehab 		else if (dev->norm & V4L2_STD_GH)
11770c0d06caSMauro Carvalho Chehab 			if_frequency = 7100000;  /*7.1MHz	*/
11780c0d06caSMauro Carvalho Chehab 		else if (dev->norm & V4L2_STD_PAL_I)
11790c0d06caSMauro Carvalho Chehab 			if_frequency = 7250000;  /*7.25MHz	*/
11800c0d06caSMauro Carvalho Chehab 		else if (dev->norm & V4L2_STD_SECAM_L)
11810c0d06caSMauro Carvalho Chehab 			if_frequency = 6900000;  /*6.9MHz	*/
11820c0d06caSMauro Carvalho Chehab 		else if (dev->norm & V4L2_STD_SECAM_LC)
11830c0d06caSMauro Carvalho Chehab 			if_frequency = 1250000;  /*1.25MHz	*/
11840c0d06caSMauro Carvalho Chehab 
1185336fea92SMauro Carvalho Chehab 		dev_dbg(dev->dev,
1186b7085c08SMauro Carvalho Chehab 			"if_frequency is set to %d\n", if_frequency);
11870c0d06caSMauro Carvalho Chehab 		cx231xx_set_Colibri_For_LowIF(dev, if_frequency, 1, 1);
11880c0d06caSMauro Carvalho Chehab 
11890c0d06caSMauro Carvalho Chehab 		update_HH_register_after_set_DIF(dev);
11900c0d06caSMauro Carvalho Chehab 	}
11910c0d06caSMauro Carvalho Chehab 
1192336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev, "Set New FREQUENCY to %d\n", f->frequency);
11930c0d06caSMauro Carvalho Chehab 
11940c0d06caSMauro Carvalho Chehab 	return rc;
11950c0d06caSMauro Carvalho Chehab }
11960c0d06caSMauro Carvalho Chehab 
119708fe9f7dSHans Verkuil #ifdef CONFIG_VIDEO_ADV_DEBUG
119808fe9f7dSHans Verkuil 
cx231xx_g_chip_info(struct file * file,void * fh,struct v4l2_dbg_chip_info * chip)119908fe9f7dSHans Verkuil int cx231xx_g_chip_info(struct file *file, void *fh,
120008fe9f7dSHans Verkuil 			struct v4l2_dbg_chip_info *chip)
1201fddd14c8SHans Verkuil {
120208fe9f7dSHans Verkuil 	switch (chip->match.addr) {
120308fe9f7dSHans Verkuil 	case 0:	/* Cx231xx - internal registers */
120408fe9f7dSHans Verkuil 		return 0;
120508fe9f7dSHans Verkuil 	case 1:	/* AFE - read byte */
1206c0decac1SMauro Carvalho Chehab 		strscpy(chip->name, "AFE (byte)", sizeof(chip->name));
120708fe9f7dSHans Verkuil 		return 0;
120808fe9f7dSHans Verkuil 	case 2:	/* Video Block - read byte */
1209c0decac1SMauro Carvalho Chehab 		strscpy(chip->name, "Video (byte)", sizeof(chip->name));
121008fe9f7dSHans Verkuil 		return 0;
121108fe9f7dSHans Verkuil 	case 3:	/* I2S block - read byte */
1212c0decac1SMauro Carvalho Chehab 		strscpy(chip->name, "I2S (byte)", sizeof(chip->name));
121308fe9f7dSHans Verkuil 		return 0;
121408fe9f7dSHans Verkuil 	case 4: /* AFE - read dword */
1215c0decac1SMauro Carvalho Chehab 		strscpy(chip->name, "AFE (dword)", sizeof(chip->name));
121608fe9f7dSHans Verkuil 		return 0;
121708fe9f7dSHans Verkuil 	case 5: /* Video Block - read dword */
1218c0decac1SMauro Carvalho Chehab 		strscpy(chip->name, "Video (dword)", sizeof(chip->name));
121908fe9f7dSHans Verkuil 		return 0;
122008fe9f7dSHans Verkuil 	case 6: /* I2S Block - read dword */
1221c0decac1SMauro Carvalho Chehab 		strscpy(chip->name, "I2S (dword)", sizeof(chip->name));
1222fddd14c8SHans Verkuil 		return 0;
1223fddd14c8SHans Verkuil 	}
1224fddd14c8SHans Verkuil 	return -EINVAL;
1225fddd14c8SHans Verkuil }
1226fddd14c8SHans Verkuil 
cx231xx_g_register(struct file * file,void * priv,struct v4l2_dbg_register * reg)1227b86d1544SHans Verkuil int cx231xx_g_register(struct file *file, void *priv,
12280c0d06caSMauro Carvalho Chehab 			     struct v4l2_dbg_register *reg)
12290c0d06caSMauro Carvalho Chehab {
12307c617138SHans Verkuil 	struct cx231xx *dev = video_drvdata(file);
123108fe9f7dSHans Verkuil 	int ret;
12320c0d06caSMauro Carvalho Chehab 	u8 value[4] = { 0, 0, 0, 0 };
12330c0d06caSMauro Carvalho Chehab 	u32 data = 0;
12340c0d06caSMauro Carvalho Chehab 
12350c0d06caSMauro Carvalho Chehab 	switch (reg->match.addr) {
12360c0d06caSMauro Carvalho Chehab 	case 0:	/* Cx231xx - internal registers */
12370c0d06caSMauro Carvalho Chehab 		ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
12380c0d06caSMauro Carvalho Chehab 				(u16)reg->reg, value, 4);
12390c0d06caSMauro Carvalho Chehab 		reg->val = value[0] | value[1] << 8 |
124032ae5920SColin Ian King 			value[2] << 16 | (u32)value[3] << 24;
124104ae4cf2SHans Verkuil 		reg->size = 4;
12420c0d06caSMauro Carvalho Chehab 		break;
12430c0d06caSMauro Carvalho Chehab 	case 1:	/* AFE - read byte */
12440c0d06caSMauro Carvalho Chehab 		ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
12450c0d06caSMauro Carvalho Chehab 				(u16)reg->reg, 2, &data, 1);
124608fe9f7dSHans Verkuil 		reg->val = data;
124704ae4cf2SHans Verkuil 		reg->size = 1;
12480c0d06caSMauro Carvalho Chehab 		break;
12490c0d06caSMauro Carvalho Chehab 	case 2:	/* Video Block - read byte */
12500c0d06caSMauro Carvalho Chehab 		ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
12510c0d06caSMauro Carvalho Chehab 				(u16)reg->reg, 2, &data, 1);
125208fe9f7dSHans Verkuil 		reg->val = data;
125304ae4cf2SHans Verkuil 		reg->size = 1;
12540c0d06caSMauro Carvalho Chehab 		break;
12550c0d06caSMauro Carvalho Chehab 	case 3:	/* I2S block - read byte */
125608fe9f7dSHans Verkuil 		ret = cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
125708fe9f7dSHans Verkuil 				(u16)reg->reg, 1, &data, 1);
125808fe9f7dSHans Verkuil 		reg->val = data;
125904ae4cf2SHans Verkuil 		reg->size = 1;
12600c0d06caSMauro Carvalho Chehab 		break;
126108fe9f7dSHans Verkuil 	case 4: /* AFE - read dword */
126208fe9f7dSHans Verkuil 		ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
126308fe9f7dSHans Verkuil 				(u16)reg->reg, 2, &data, 4);
126408fe9f7dSHans Verkuil 		reg->val = data;
126504ae4cf2SHans Verkuil 		reg->size = 4;
126608fe9f7dSHans Verkuil 		break;
126708fe9f7dSHans Verkuil 	case 5: /* Video Block - read dword */
126808fe9f7dSHans Verkuil 		ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
126908fe9f7dSHans Verkuil 				(u16)reg->reg, 2, &data, 4);
127008fe9f7dSHans Verkuil 		reg->val = data;
127104ae4cf2SHans Verkuil 		reg->size = 4;
127208fe9f7dSHans Verkuil 		break;
127308fe9f7dSHans Verkuil 	case 6: /* I2S Block - read dword */
127408fe9f7dSHans Verkuil 		ret = cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
12750c0d06caSMauro Carvalho Chehab 				(u16)reg->reg, 1, &data, 4);
127608fe9f7dSHans Verkuil 		reg->val = data;
127704ae4cf2SHans Verkuil 		reg->size = 4;
12780c0d06caSMauro Carvalho Chehab 		break;
12790c0d06caSMauro Carvalho Chehab 	default:
12800c0d06caSMauro Carvalho Chehab 		return -EINVAL;
12810c0d06caSMauro Carvalho Chehab 	}
128208fe9f7dSHans Verkuil 	return ret < 0 ? ret : 0;
12830c0d06caSMauro Carvalho Chehab }
12840c0d06caSMauro Carvalho Chehab 
cx231xx_s_register(struct file * file,void * priv,const struct v4l2_dbg_register * reg)1285b86d1544SHans Verkuil int cx231xx_s_register(struct file *file, void *priv,
1286977ba3b1SHans Verkuil 			     const struct v4l2_dbg_register *reg)
12870c0d06caSMauro Carvalho Chehab {
12887c617138SHans Verkuil 	struct cx231xx *dev = video_drvdata(file);
128908fe9f7dSHans Verkuil 	int ret;
12900c0d06caSMauro Carvalho Chehab 	u8 data[4] = { 0, 0, 0, 0 };
12910c0d06caSMauro Carvalho Chehab 
12920c0d06caSMauro Carvalho Chehab 	switch (reg->match.addr) {
12930c0d06caSMauro Carvalho Chehab 	case 0:	/* cx231xx internal registers */
129408fe9f7dSHans Verkuil 		data[0] = (u8) reg->val;
129508fe9f7dSHans Verkuil 		data[1] = (u8) (reg->val >> 8);
129608fe9f7dSHans Verkuil 		data[2] = (u8) (reg->val >> 16);
129708fe9f7dSHans Verkuil 		data[3] = (u8) (reg->val >> 24);
129808fe9f7dSHans Verkuil 		ret = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
129908fe9f7dSHans Verkuil 				(u16)reg->reg, data, 4);
13000c0d06caSMauro Carvalho Chehab 		break;
130108fe9f7dSHans Verkuil 	case 1:	/* AFE - write byte */
130208fe9f7dSHans Verkuil 		ret = cx231xx_write_i2c_data(dev, AFE_DEVICE_ADDRESS,
130308fe9f7dSHans Verkuil 				(u16)reg->reg, 2, reg->val, 1);
13040c0d06caSMauro Carvalho Chehab 		break;
130508fe9f7dSHans Verkuil 	case 2:	/* Video Block - write byte */
130608fe9f7dSHans Verkuil 		ret = cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS,
130708fe9f7dSHans Verkuil 				(u16)reg->reg, 2, reg->val, 1);
13080c0d06caSMauro Carvalho Chehab 		break;
130908fe9f7dSHans Verkuil 	case 3:	/* I2S block - write byte */
131008fe9f7dSHans Verkuil 		ret = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
131108fe9f7dSHans Verkuil 				(u16)reg->reg, 1, reg->val, 1);
13120c0d06caSMauro Carvalho Chehab 		break;
131308fe9f7dSHans Verkuil 	case 4: /* AFE - write dword */
131408fe9f7dSHans Verkuil 		ret = cx231xx_write_i2c_data(dev, AFE_DEVICE_ADDRESS,
131508fe9f7dSHans Verkuil 				(u16)reg->reg, 2, reg->val, 4);
13160c0d06caSMauro Carvalho Chehab 		break;
131708fe9f7dSHans Verkuil 	case 5: /* Video Block - write dword */
131808fe9f7dSHans Verkuil 		ret = cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS,
131908fe9f7dSHans Verkuil 				(u16)reg->reg, 2, reg->val, 4);
13200c0d06caSMauro Carvalho Chehab 		break;
132108fe9f7dSHans Verkuil 	case 6: /* I2S block - write dword */
132208fe9f7dSHans Verkuil 		ret = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
132308fe9f7dSHans Verkuil 				(u16)reg->reg, 1, reg->val, 4);
13240c0d06caSMauro Carvalho Chehab 		break;
132508fe9f7dSHans Verkuil 	default:
132608fe9f7dSHans Verkuil 		return -EINVAL;
13270c0d06caSMauro Carvalho Chehab 	}
13280c0d06caSMauro Carvalho Chehab 	return ret < 0 ? ret : 0;
13290c0d06caSMauro Carvalho Chehab }
13300c0d06caSMauro Carvalho Chehab #endif
13310c0d06caSMauro Carvalho Chehab 
vidioc_g_pixelaspect(struct file * file,void * priv,int type,struct v4l2_fract * f)13325200ab6aSHans Verkuil static int vidioc_g_pixelaspect(struct file *file, void *priv,
13335200ab6aSHans Verkuil 				int type, struct v4l2_fract *f)
13340c0d06caSMauro Carvalho Chehab {
13357c617138SHans Verkuil 	struct cx231xx *dev = video_drvdata(file);
1336e25cb200SHans Verkuil 	bool is_50hz = dev->norm & V4L2_STD_625_50;
13370c0d06caSMauro Carvalho Chehab 
13385200ab6aSHans Verkuil 	if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
13390c0d06caSMauro Carvalho Chehab 		return -EINVAL;
13400c0d06caSMauro Carvalho Chehab 
13415200ab6aSHans Verkuil 	f->numerator = is_50hz ? 54 : 11;
13425200ab6aSHans Verkuil 	f->denominator = is_50hz ? 59 : 10;
13430c0d06caSMauro Carvalho Chehab 
13440c0d06caSMauro Carvalho Chehab 	return 0;
13450c0d06caSMauro Carvalho Chehab }
13460c0d06caSMauro Carvalho Chehab 
vidioc_g_selection(struct file * file,void * priv,struct v4l2_selection * s)1347ee10dc36SHans Verkuil static int vidioc_g_selection(struct file *file, void *priv,
1348ee10dc36SHans Verkuil 			      struct v4l2_selection *s)
1349ee10dc36SHans Verkuil {
13507c617138SHans Verkuil 	struct cx231xx *dev = video_drvdata(file);
1351ee10dc36SHans Verkuil 
1352ee10dc36SHans Verkuil 	if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1353ee10dc36SHans Verkuil 		return -EINVAL;
1354ee10dc36SHans Verkuil 
1355ee10dc36SHans Verkuil 	switch (s->target) {
1356ee10dc36SHans Verkuil 	case V4L2_SEL_TGT_CROP_BOUNDS:
1357ee10dc36SHans Verkuil 	case V4L2_SEL_TGT_CROP_DEFAULT:
1358ee10dc36SHans Verkuil 		s->r.left = 0;
1359ee10dc36SHans Verkuil 		s->r.top = 0;
1360ee10dc36SHans Verkuil 		s->r.width = dev->width;
1361ee10dc36SHans Verkuil 		s->r.height = dev->height;
1362ee10dc36SHans Verkuil 		break;
1363ee10dc36SHans Verkuil 	default:
1364ee10dc36SHans Verkuil 		return -EINVAL;
1365ee10dc36SHans Verkuil 	}
1366ee10dc36SHans Verkuil 	return 0;
1367ee10dc36SHans Verkuil }
1368ee10dc36SHans Verkuil 
cx231xx_querycap(struct file * file,void * priv,struct v4l2_capability * cap)1369bc08734cSHans Verkuil int cx231xx_querycap(struct file *file, void *priv,
13700c0d06caSMauro Carvalho Chehab 			   struct v4l2_capability *cap)
13710c0d06caSMauro Carvalho Chehab {
13727c617138SHans Verkuil 	struct cx231xx *dev = video_drvdata(file);
13730c0d06caSMauro Carvalho Chehab 
1374c0decac1SMauro Carvalho Chehab 	strscpy(cap->driver, "cx231xx", sizeof(cap->driver));
1375c0decac1SMauro Carvalho Chehab 	strscpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card));
13760c0d06caSMauro Carvalho Chehab 	usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
13778c3854d0SHans Verkuil 	cap->capabilities = V4L2_CAP_READWRITE |
13784bc837d4SHans Verkuil 		V4L2_CAP_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE |
1379530e01e7SHans Verkuil 		V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS;
138060acf187SHans Verkuil 	if (video_is_registered(&dev->radio_dev))
1381530e01e7SHans Verkuil 		cap->capabilities |= V4L2_CAP_RADIO;
13823c1ccbadSBrad Love 
13833c1ccbadSBrad Love 	switch (dev->model) {
13843c1ccbadSBrad Love 	case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx:
13853c1ccbadSBrad Love 	case CX231XX_BOARD_HAUPPAUGE_935C:
13863c1ccbadSBrad Love 	case CX231XX_BOARD_HAUPPAUGE_955Q:
13873c1ccbadSBrad Love 	case CX231XX_BOARD_HAUPPAUGE_975:
13883c1ccbadSBrad Love 	case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD:
13893c1ccbadSBrad Love 		cap->capabilities |= V4L2_CAP_TUNER;
13903c1ccbadSBrad Love 		break;
13913c1ccbadSBrad Love 	default:
13928c3854d0SHans Verkuil 		if (dev->tuner_type != TUNER_ABSENT)
13938c3854d0SHans Verkuil 			cap->capabilities |= V4L2_CAP_TUNER;
13943c1ccbadSBrad Love 		break;
13953c1ccbadSBrad Love 	}
13960c0d06caSMauro Carvalho Chehab 	return 0;
13970c0d06caSMauro Carvalho Chehab }
13980c0d06caSMauro Carvalho Chehab 
vidioc_enum_fmt_vid_cap(struct file * file,void * priv,struct v4l2_fmtdesc * f)13990c0d06caSMauro Carvalho Chehab static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
14000c0d06caSMauro Carvalho Chehab 				   struct v4l2_fmtdesc *f)
14010c0d06caSMauro Carvalho Chehab {
14020c0d06caSMauro Carvalho Chehab 	if (unlikely(f->index >= ARRAY_SIZE(format)))
14030c0d06caSMauro Carvalho Chehab 		return -EINVAL;
14040c0d06caSMauro Carvalho Chehab 
14050c0d06caSMauro Carvalho Chehab 	f->pixelformat = format[f->index].fourcc;
14060c0d06caSMauro Carvalho Chehab 
14070c0d06caSMauro Carvalho Chehab 	return 0;
14080c0d06caSMauro Carvalho Chehab }
14090c0d06caSMauro Carvalho Chehab 
14100c0d06caSMauro Carvalho Chehab /* RAW VBI ioctls */
14110c0d06caSMauro Carvalho Chehab 
vidioc_g_fmt_vbi_cap(struct file * file,void * priv,struct v4l2_format * f)14120c0d06caSMauro Carvalho Chehab static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
14130c0d06caSMauro Carvalho Chehab 				struct v4l2_format *f)
14140c0d06caSMauro Carvalho Chehab {
14157c617138SHans Verkuil 	struct cx231xx *dev = video_drvdata(file);
14166264722cSHans Verkuil 
14170c0d06caSMauro Carvalho Chehab 	f->fmt.vbi.sampling_rate = 6750000 * 4;
14180c0d06caSMauro Carvalho Chehab 	f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
14190c0d06caSMauro Carvalho Chehab 	f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
14200c0d06caSMauro Carvalho Chehab 	f->fmt.vbi.offset = 0;
14210c0d06caSMauro Carvalho Chehab 	f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ?
14220c0d06caSMauro Carvalho Chehab 	    PAL_VBI_START_LINE : NTSC_VBI_START_LINE;
14230c0d06caSMauro Carvalho Chehab 	f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ?
14240c0d06caSMauro Carvalho Chehab 	    PAL_VBI_LINES : NTSC_VBI_LINES;
14250c0d06caSMauro Carvalho Chehab 	f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ?
14260c0d06caSMauro Carvalho Chehab 	    PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263;
14270c0d06caSMauro Carvalho Chehab 	f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
14286264722cSHans Verkuil 	memset(f->fmt.vbi.reserved, 0, sizeof(f->fmt.vbi.reserved));
14290c0d06caSMauro Carvalho Chehab 
14300c0d06caSMauro Carvalho Chehab 	return 0;
14310c0d06caSMauro Carvalho Chehab 
14320c0d06caSMauro Carvalho Chehab }
14330c0d06caSMauro Carvalho Chehab 
vidioc_try_fmt_vbi_cap(struct file * file,void * priv,struct v4l2_format * f)14340c0d06caSMauro Carvalho Chehab static int vidioc_try_fmt_vbi_cap(struct file *file, void *priv,
14350c0d06caSMauro Carvalho Chehab 				  struct v4l2_format *f)
14360c0d06caSMauro Carvalho Chehab {
14377c617138SHans Verkuil 	struct cx231xx *dev = video_drvdata(file);
14380c0d06caSMauro Carvalho Chehab 
14390c0d06caSMauro Carvalho Chehab 	f->fmt.vbi.sampling_rate = 6750000 * 4;
14400c0d06caSMauro Carvalho Chehab 	f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
14410c0d06caSMauro Carvalho Chehab 	f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
14420c0d06caSMauro Carvalho Chehab 	f->fmt.vbi.offset = 0;
14430c0d06caSMauro Carvalho Chehab 	f->fmt.vbi.flags = 0;
14440c0d06caSMauro Carvalho Chehab 	f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ?
14450c0d06caSMauro Carvalho Chehab 	    PAL_VBI_START_LINE : NTSC_VBI_START_LINE;
14460c0d06caSMauro Carvalho Chehab 	f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ?
14470c0d06caSMauro Carvalho Chehab 	    PAL_VBI_LINES : NTSC_VBI_LINES;
14480c0d06caSMauro Carvalho Chehab 	f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ?
14490c0d06caSMauro Carvalho Chehab 	    PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263;
14500c0d06caSMauro Carvalho Chehab 	f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
14516264722cSHans Verkuil 	memset(f->fmt.vbi.reserved, 0, sizeof(f->fmt.vbi.reserved));
14520c0d06caSMauro Carvalho Chehab 
14530c0d06caSMauro Carvalho Chehab 	return 0;
14540c0d06caSMauro Carvalho Chehab 
14550c0d06caSMauro Carvalho Chehab }
14560c0d06caSMauro Carvalho Chehab 
vidioc_s_fmt_vbi_cap(struct file * file,void * priv,struct v4l2_format * f)14576264722cSHans Verkuil static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv,
14586264722cSHans Verkuil 				  struct v4l2_format *f)
14596264722cSHans Verkuil {
14606264722cSHans Verkuil 	return vidioc_try_fmt_vbi_cap(file, priv, f);
14616264722cSHans Verkuil }
14626264722cSHans Verkuil 
14630c0d06caSMauro Carvalho Chehab /* ----------------------------------------------------------- */
14640c0d06caSMauro Carvalho Chehab /* RADIO ESPECIFIC IOCTLS                                      */
14650c0d06caSMauro Carvalho Chehab /* ----------------------------------------------------------- */
14660c0d06caSMauro Carvalho Chehab 
radio_g_tuner(struct file * file,void * priv,struct v4l2_tuner * t)14670c0d06caSMauro Carvalho Chehab static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
14680c0d06caSMauro Carvalho Chehab {
14697c617138SHans Verkuil 	struct cx231xx *dev = video_drvdata(file);
14700c0d06caSMauro Carvalho Chehab 
1471530e01e7SHans Verkuil 	if (t->index)
14720c0d06caSMauro Carvalho Chehab 		return -EINVAL;
14730c0d06caSMauro Carvalho Chehab 
1474cc1e6315SMauro Carvalho Chehab 	strscpy(t->name, "Radio", sizeof(t->name));
14750c0d06caSMauro Carvalho Chehab 
1476530e01e7SHans Verkuil 	call_all(dev, tuner, g_tuner, t);
14770c0d06caSMauro Carvalho Chehab 
14780c0d06caSMauro Carvalho Chehab 	return 0;
14790c0d06caSMauro Carvalho Chehab }
radio_s_tuner(struct file * file,void * priv,const struct v4l2_tuner * t)14802f73c7c5SHans Verkuil static int radio_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t)
14810c0d06caSMauro Carvalho Chehab {
14827c617138SHans Verkuil 	struct cx231xx *dev = video_drvdata(file);
14830c0d06caSMauro Carvalho Chehab 
1484b86d1544SHans Verkuil 	if (t->index)
14850c0d06caSMauro Carvalho Chehab 		return -EINVAL;
14860c0d06caSMauro Carvalho Chehab 
14870c0d06caSMauro Carvalho Chehab 	call_all(dev, tuner, s_tuner, t);
14880c0d06caSMauro Carvalho Chehab 
14890c0d06caSMauro Carvalho Chehab 	return 0;
14900c0d06caSMauro Carvalho Chehab }
14910c0d06caSMauro Carvalho Chehab 
14920c0d06caSMauro Carvalho Chehab /*
14930c0d06caSMauro Carvalho Chehab  * cx231xx_v4l2_open()
14940c0d06caSMauro Carvalho Chehab  * inits the device and starts isoc transfer
14950c0d06caSMauro Carvalho Chehab  */
cx231xx_v4l2_open(struct file * filp)14960c0d06caSMauro Carvalho Chehab static int cx231xx_v4l2_open(struct file *filp)
14970c0d06caSMauro Carvalho Chehab {
14980c0d06caSMauro Carvalho Chehab 	struct video_device *vdev = video_devdata(filp);
14990c0d06caSMauro Carvalho Chehab 	struct cx231xx *dev = video_drvdata(filp);
15007c617138SHans Verkuil 	int ret;
15010c0d06caSMauro Carvalho Chehab 
15027c617138SHans Verkuil 	if (mutex_lock_interruptible(&dev->lock))
15030c0d06caSMauro Carvalho Chehab 		return -ERESTARTSYS;
15040c0d06caSMauro Carvalho Chehab 
15057c617138SHans Verkuil 	ret = v4l2_fh_open(filp);
15067c617138SHans Verkuil 	if (ret) {
15077c617138SHans Verkuil 		mutex_unlock(&dev->lock);
15087c617138SHans Verkuil 		return ret;
15097c617138SHans Verkuil 	}
15107c617138SHans Verkuil 
15117c617138SHans Verkuil 	if (dev->users++ == 0) {
15120c0d06caSMauro Carvalho Chehab 		/* Power up in Analog TV mode */
15130c0d06caSMauro Carvalho Chehab 		if (dev->board.external_av)
15140c0d06caSMauro Carvalho Chehab 			cx231xx_set_power_mode(dev,
15150c0d06caSMauro Carvalho Chehab 				 POLARIS_AVMODE_ENXTERNAL_AV);
15160c0d06caSMauro Carvalho Chehab 		else
15170c0d06caSMauro Carvalho Chehab 			cx231xx_set_power_mode(dev, POLARIS_AVMODE_ANALOGT_TV);
15180c0d06caSMauro Carvalho Chehab 
15190c0d06caSMauro Carvalho Chehab 		/* set video alternate setting */
15200c0d06caSMauro Carvalho Chehab 		cx231xx_set_video_alternate(dev);
15210c0d06caSMauro Carvalho Chehab 
15220c0d06caSMauro Carvalho Chehab 		/* Needed, since GPIO might have disabled power of
15230c0d06caSMauro Carvalho Chehab 		   some i2c device */
15240c0d06caSMauro Carvalho Chehab 		cx231xx_config_i2c(dev);
15250c0d06caSMauro Carvalho Chehab 
15260c0d06caSMauro Carvalho Chehab 		/* device needs to be initialized before isoc transfer */
15270c0d06caSMauro Carvalho Chehab 		dev->video_input = dev->video_input > 2 ? 2 : dev->video_input;
15280c0d06caSMauro Carvalho Chehab 	}
15297c617138SHans Verkuil 
15307c617138SHans Verkuil 	if (vdev->vfl_type == VFL_TYPE_RADIO) {
15310c0d06caSMauro Carvalho Chehab 		cx231xx_videodbg("video_open: setting radio device\n");
15320c0d06caSMauro Carvalho Chehab 
15330c0d06caSMauro Carvalho Chehab 		/* cx231xx_start_radio(dev); */
15340c0d06caSMauro Carvalho Chehab 
15350c0d06caSMauro Carvalho Chehab 		call_all(dev, tuner, s_radio);
15360c0d06caSMauro Carvalho Chehab 	}
15377c617138SHans Verkuil 	if (vdev->vfl_type == VFL_TYPE_VBI) {
15380c0d06caSMauro Carvalho Chehab 		/* Set the required alternate setting  VBI interface works in
15390c0d06caSMauro Carvalho Chehab 		   Bulk mode only */
15400c0d06caSMauro Carvalho Chehab 		cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
15410c0d06caSMauro Carvalho Chehab 	}
15420c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
15434df16f70SPeter Senna Tschudin 	return 0;
15440c0d06caSMauro Carvalho Chehab }
15450c0d06caSMauro Carvalho Chehab 
15460c0d06caSMauro Carvalho Chehab /*
15470c0d06caSMauro Carvalho Chehab  * cx231xx_realease_resources()
15480c0d06caSMauro Carvalho Chehab  * unregisters the v4l2,i2c and usb devices
15496b338c72SGeert Uytterhoeven  * called when the device gets disconnected or at module unload
15500c0d06caSMauro Carvalho Chehab */
cx231xx_release_analog_resources(struct cx231xx * dev)15510c0d06caSMauro Carvalho Chehab void cx231xx_release_analog_resources(struct cx231xx *dev)
15520c0d06caSMauro Carvalho Chehab {
15530c0d06caSMauro Carvalho Chehab 
15540c0d06caSMauro Carvalho Chehab 	/*FIXME: I2C IR should be disconnected */
15550c0d06caSMauro Carvalho Chehab 
155660acf187SHans Verkuil 	if (video_is_registered(&dev->radio_dev))
155760acf187SHans Verkuil 		video_unregister_device(&dev->radio_dev);
155860acf187SHans Verkuil 	if (video_is_registered(&dev->vbi_dev)) {
1559336fea92SMauro Carvalho Chehab 		dev_info(dev->dev, "V4L2 device %s deregistered\n",
156060acf187SHans Verkuil 			video_device_node_name(&dev->vbi_dev));
156160acf187SHans Verkuil 		video_unregister_device(&dev->vbi_dev);
15620c0d06caSMauro Carvalho Chehab 	}
156360acf187SHans Verkuil 	if (video_is_registered(&dev->vdev)) {
1564336fea92SMauro Carvalho Chehab 		dev_info(dev->dev, "V4L2 device %s deregistered\n",
156560acf187SHans Verkuil 			video_device_node_name(&dev->vdev));
15660c0d06caSMauro Carvalho Chehab 
15670c0d06caSMauro Carvalho Chehab 		if (dev->board.has_417)
15680c0d06caSMauro Carvalho Chehab 			cx231xx_417_unregister(dev);
15690c0d06caSMauro Carvalho Chehab 
157060acf187SHans Verkuil 		video_unregister_device(&dev->vdev);
15710c0d06caSMauro Carvalho Chehab 	}
1572d2370f8eSHans Verkuil 	v4l2_ctrl_handler_free(&dev->ctrl_handler);
1573d2370f8eSHans Verkuil 	v4l2_ctrl_handler_free(&dev->radio_ctrl_handler);
15740c0d06caSMauro Carvalho Chehab }
15750c0d06caSMauro Carvalho Chehab 
15760c0d06caSMauro Carvalho Chehab /*
15770c0d06caSMauro Carvalho Chehab  * cx231xx_close()
15780c0d06caSMauro Carvalho Chehab  * stops streaming and deallocates all resources allocated by the v4l2
15790c0d06caSMauro Carvalho Chehab  * calls and ioctls
15800c0d06caSMauro Carvalho Chehab  */
cx231xx_close(struct file * filp)15810c0d06caSMauro Carvalho Chehab static int cx231xx_close(struct file *filp)
15820c0d06caSMauro Carvalho Chehab {
15837c617138SHans Verkuil 	struct cx231xx *dev = video_drvdata(filp);
15847c617138SHans Verkuil 	struct video_device *vdev = video_devdata(filp);
15850c0d06caSMauro Carvalho Chehab 
15867c617138SHans Verkuil 	_vb2_fop_release(filp, NULL);
15870c0d06caSMauro Carvalho Chehab 
15887c617138SHans Verkuil 	if (--dev->users == 0) {
15890c0d06caSMauro Carvalho Chehab 		/* Save some power by putting tuner to sleep */
15903aab15afSHans Verkuil 		call_all(dev, tuner, standby);
15910c0d06caSMauro Carvalho Chehab 
15920c0d06caSMauro Carvalho Chehab 		/* do this before setting alternate! */
15930c0d06caSMauro Carvalho Chehab 		if (dev->USE_ISO)
15940c0d06caSMauro Carvalho Chehab 			cx231xx_uninit_isoc(dev);
15950c0d06caSMauro Carvalho Chehab 		else
15960c0d06caSMauro Carvalho Chehab 			cx231xx_uninit_bulk(dev);
15970c0d06caSMauro Carvalho Chehab 		cx231xx_set_mode(dev, CX231XX_SUSPEND);
15987c617138SHans Verkuil 	}
15990c0d06caSMauro Carvalho Chehab 
16007c617138SHans Verkuil 	/*
16017c617138SHans Verkuil 	 * To workaround error number=-71 on EP0 for VideoGrabber,
16027c617138SHans Verkuil 	 *	 need exclude following.
16037c617138SHans Verkuil 	 * FIXME: It is probably safe to remove most of these, as we're
16047c617138SHans Verkuil 	 * now avoiding the alternate setting for INDEX_VANC
16057c617138SHans Verkuil 	 */
16067c617138SHans Verkuil 	if (!dev->board.no_alt_vanc && vdev->vfl_type == VFL_TYPE_VBI) {
16077c617138SHans Verkuil 		/* do this before setting alternate! */
16087c617138SHans Verkuil 		cx231xx_uninit_vbi_isoc(dev);
16097c617138SHans Verkuil 
16107c617138SHans Verkuil 		/* set alternate 0 */
16117c617138SHans Verkuil 		if (!dev->vbi_or_sliced_cc_mode)
16127c617138SHans Verkuil 			cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
16137c617138SHans Verkuil 		else
16147c617138SHans Verkuil 			cx231xx_set_alt_setting(dev, INDEX_HANC, 0);
16157c617138SHans Verkuil 
16167c617138SHans Verkuil 		wake_up_interruptible_nr(&dev->open, 1);
16177c617138SHans Verkuil 		return 0;
16187c617138SHans Verkuil 	}
16197c617138SHans Verkuil 
16207c617138SHans Verkuil 	if (dev->users == 0) {
16210c0d06caSMauro Carvalho Chehab 		/* set alternate 0 */
16220c0d06caSMauro Carvalho Chehab 		cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0);
16230c0d06caSMauro Carvalho Chehab 	}
16247c617138SHans Verkuil 
1625543409a2SSteven Rostedt 	wake_up_interruptible(&dev->open);
16260c0d06caSMauro Carvalho Chehab 	return 0;
16270c0d06caSMauro Carvalho Chehab }
16280c0d06caSMauro Carvalho Chehab 
cx231xx_v4l2_close(struct file * filp)16290c0d06caSMauro Carvalho Chehab static int cx231xx_v4l2_close(struct file *filp)
16300c0d06caSMauro Carvalho Chehab {
16317c617138SHans Verkuil 	struct cx231xx *dev = video_drvdata(filp);
16320c0d06caSMauro Carvalho Chehab 	int rc;
16330c0d06caSMauro Carvalho Chehab 
16340c0d06caSMauro Carvalho Chehab 	mutex_lock(&dev->lock);
16350c0d06caSMauro Carvalho Chehab 	rc = cx231xx_close(filp);
16360c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
16370c0d06caSMauro Carvalho Chehab 	return rc;
16380c0d06caSMauro Carvalho Chehab }
16390c0d06caSMauro Carvalho Chehab 
16400c0d06caSMauro Carvalho Chehab static const struct v4l2_file_operations cx231xx_v4l_fops = {
16410c0d06caSMauro Carvalho Chehab 	.owner   = THIS_MODULE,
16420c0d06caSMauro Carvalho Chehab 	.open    = cx231xx_v4l2_open,
16430c0d06caSMauro Carvalho Chehab 	.release = cx231xx_v4l2_close,
16447c617138SHans Verkuil 	.read    = vb2_fop_read,
16457c617138SHans Verkuil 	.poll    = vb2_fop_poll,
16467c617138SHans Verkuil 	.mmap    = vb2_fop_mmap,
16470c0d06caSMauro Carvalho Chehab 	.unlocked_ioctl   = video_ioctl2,
16480c0d06caSMauro Carvalho Chehab };
16490c0d06caSMauro Carvalho Chehab 
16500c0d06caSMauro Carvalho Chehab static const struct v4l2_ioctl_ops video_ioctl_ops = {
1651bc08734cSHans Verkuil 	.vidioc_querycap               = cx231xx_querycap,
16520c0d06caSMauro Carvalho Chehab 	.vidioc_enum_fmt_vid_cap       = vidioc_enum_fmt_vid_cap,
16530c0d06caSMauro Carvalho Chehab 	.vidioc_g_fmt_vid_cap          = vidioc_g_fmt_vid_cap,
16540c0d06caSMauro Carvalho Chehab 	.vidioc_try_fmt_vid_cap        = vidioc_try_fmt_vid_cap,
16550c0d06caSMauro Carvalho Chehab 	.vidioc_s_fmt_vid_cap          = vidioc_s_fmt_vid_cap,
16560c0d06caSMauro Carvalho Chehab 	.vidioc_g_fmt_vbi_cap          = vidioc_g_fmt_vbi_cap,
16570c0d06caSMauro Carvalho Chehab 	.vidioc_try_fmt_vbi_cap        = vidioc_try_fmt_vbi_cap,
16586264722cSHans Verkuil 	.vidioc_s_fmt_vbi_cap          = vidioc_s_fmt_vbi_cap,
16595200ab6aSHans Verkuil 	.vidioc_g_pixelaspect          = vidioc_g_pixelaspect,
1660ee10dc36SHans Verkuil 	.vidioc_g_selection            = vidioc_g_selection,
16617c617138SHans Verkuil 	.vidioc_reqbufs                = vb2_ioctl_reqbufs,
16627c617138SHans Verkuil 	.vidioc_querybuf               = vb2_ioctl_querybuf,
16637c617138SHans Verkuil 	.vidioc_qbuf                   = vb2_ioctl_qbuf,
16647c617138SHans Verkuil 	.vidioc_dqbuf                  = vb2_ioctl_dqbuf,
16650c0d06caSMauro Carvalho Chehab 	.vidioc_s_std                  = vidioc_s_std,
16660c0d06caSMauro Carvalho Chehab 	.vidioc_g_std                  = vidioc_g_std,
1667b86d1544SHans Verkuil 	.vidioc_enum_input             = cx231xx_enum_input,
1668b86d1544SHans Verkuil 	.vidioc_g_input                = cx231xx_g_input,
1669b86d1544SHans Verkuil 	.vidioc_s_input                = cx231xx_s_input,
16707c617138SHans Verkuil 	.vidioc_streamon               = vb2_ioctl_streamon,
16717c617138SHans Verkuil 	.vidioc_streamoff              = vb2_ioctl_streamoff,
1672b86d1544SHans Verkuil 	.vidioc_g_tuner                = cx231xx_g_tuner,
1673b86d1544SHans Verkuil 	.vidioc_s_tuner                = cx231xx_s_tuner,
1674b86d1544SHans Verkuil 	.vidioc_g_frequency            = cx231xx_g_frequency,
1675b86d1544SHans Verkuil 	.vidioc_s_frequency            = cx231xx_s_frequency,
16760c0d06caSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
167708fe9f7dSHans Verkuil 	.vidioc_g_chip_info            = cx231xx_g_chip_info,
1678b86d1544SHans Verkuil 	.vidioc_g_register             = cx231xx_g_register,
1679b86d1544SHans Verkuil 	.vidioc_s_register             = cx231xx_s_register,
16800c0d06caSMauro Carvalho Chehab #endif
16811d08a4faSHans Verkuil 	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
16821d08a4faSHans Verkuil 	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
16830c0d06caSMauro Carvalho Chehab };
16840c0d06caSMauro Carvalho Chehab 
16850c0d06caSMauro Carvalho Chehab static struct video_device cx231xx_vbi_template;
16860c0d06caSMauro Carvalho Chehab 
16870c0d06caSMauro Carvalho Chehab static const struct video_device cx231xx_video_template = {
16880c0d06caSMauro Carvalho Chehab 	.fops         = &cx231xx_v4l_fops,
168960acf187SHans Verkuil 	.release      = video_device_release_empty,
16900c0d06caSMauro Carvalho Chehab 	.ioctl_ops    = &video_ioctl_ops,
16910c0d06caSMauro Carvalho Chehab 	.tvnorms      = V4L2_STD_ALL,
16920c0d06caSMauro Carvalho Chehab };
16930c0d06caSMauro Carvalho Chehab 
16940c0d06caSMauro Carvalho Chehab static const struct v4l2_file_operations radio_fops = {
16950c0d06caSMauro Carvalho Chehab 	.owner   = THIS_MODULE,
16960c0d06caSMauro Carvalho Chehab 	.open   = cx231xx_v4l2_open,
16970c0d06caSMauro Carvalho Chehab 	.release = cx231xx_v4l2_close,
16981d08a4faSHans Verkuil 	.poll = v4l2_ctrl_poll,
16991265f080SHans Verkuil 	.unlocked_ioctl = video_ioctl2,
17000c0d06caSMauro Carvalho Chehab };
17010c0d06caSMauro Carvalho Chehab 
17020c0d06caSMauro Carvalho Chehab static const struct v4l2_ioctl_ops radio_ioctl_ops = {
1703bc08734cSHans Verkuil 	.vidioc_querycap    = cx231xx_querycap,
17040c0d06caSMauro Carvalho Chehab 	.vidioc_g_tuner     = radio_g_tuner,
17050c0d06caSMauro Carvalho Chehab 	.vidioc_s_tuner     = radio_s_tuner,
1706b86d1544SHans Verkuil 	.vidioc_g_frequency = cx231xx_g_frequency,
1707b86d1544SHans Verkuil 	.vidioc_s_frequency = cx231xx_s_frequency,
17080c0d06caSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
170908fe9f7dSHans Verkuil 	.vidioc_g_chip_info = cx231xx_g_chip_info,
1710b86d1544SHans Verkuil 	.vidioc_g_register  = cx231xx_g_register,
1711b86d1544SHans Verkuil 	.vidioc_s_register  = cx231xx_s_register,
17120c0d06caSMauro Carvalho Chehab #endif
17131d08a4faSHans Verkuil 	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
17141d08a4faSHans Verkuil 	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
17150c0d06caSMauro Carvalho Chehab };
17160c0d06caSMauro Carvalho Chehab 
17170c0d06caSMauro Carvalho Chehab static struct video_device cx231xx_radio_template = {
17180c0d06caSMauro Carvalho Chehab 	.name      = "cx231xx-radio",
17190c0d06caSMauro Carvalho Chehab 	.fops      = &radio_fops,
17200c0d06caSMauro Carvalho Chehab 	.ioctl_ops = &radio_ioctl_ops,
17210c0d06caSMauro Carvalho Chehab };
17220c0d06caSMauro Carvalho Chehab 
17230c0d06caSMauro Carvalho Chehab /******************************** usb interface ******************************/
17240c0d06caSMauro Carvalho Chehab 
cx231xx_vdev_init(struct cx231xx * dev,struct video_device * vfd,const struct video_device * template,const char * type_name)172560acf187SHans Verkuil static void cx231xx_vdev_init(struct cx231xx *dev,
172660acf187SHans Verkuil 		struct video_device *vfd,
172760acf187SHans Verkuil 		const struct video_device *template,
172860acf187SHans Verkuil 		const char *type_name)
17290c0d06caSMauro Carvalho Chehab {
17300c0d06caSMauro Carvalho Chehab 	*vfd = *template;
17310c0d06caSMauro Carvalho Chehab 	vfd->v4l2_dev = &dev->v4l2_dev;
173260acf187SHans Verkuil 	vfd->release = video_device_release_empty;
17330c0d06caSMauro Carvalho Chehab 	vfd->lock = &dev->lock;
17340c0d06caSMauro Carvalho Chehab 
17350c0d06caSMauro Carvalho Chehab 	snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name);
17360c0d06caSMauro Carvalho Chehab 
17370c0d06caSMauro Carvalho Chehab 	video_set_drvdata(vfd, dev);
173806c46003SHans Verkuil 	if (dev->tuner_type == TUNER_ABSENT) {
17393c1ccbadSBrad Love 		switch (dev->model) {
17403c1ccbadSBrad Love 		case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx:
17413c1ccbadSBrad Love 		case CX231XX_BOARD_HAUPPAUGE_935C:
17423c1ccbadSBrad Love 		case CX231XX_BOARD_HAUPPAUGE_955Q:
17433c1ccbadSBrad Love 		case CX231XX_BOARD_HAUPPAUGE_975:
17443c1ccbadSBrad Love 		case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD:
17453c1ccbadSBrad Love 			break;
17463c1ccbadSBrad Love 		default:
174706c46003SHans Verkuil 			v4l2_disable_ioctl(vfd, VIDIOC_G_FREQUENCY);
174806c46003SHans Verkuil 			v4l2_disable_ioctl(vfd, VIDIOC_S_FREQUENCY);
174906c46003SHans Verkuil 			v4l2_disable_ioctl(vfd, VIDIOC_G_TUNER);
175006c46003SHans Verkuil 			v4l2_disable_ioctl(vfd, VIDIOC_S_TUNER);
17513c1ccbadSBrad Love 			break;
17523c1ccbadSBrad Love 		}
175306c46003SHans Verkuil 	}
17540c0d06caSMauro Carvalho Chehab }
17550c0d06caSMauro Carvalho Chehab 
cx231xx_register_analog_devices(struct cx231xx * dev)17560c0d06caSMauro Carvalho Chehab int cx231xx_register_analog_devices(struct cx231xx *dev)
17570c0d06caSMauro Carvalho Chehab {
17587c617138SHans Verkuil 	struct vb2_queue *q;
17590c0d06caSMauro Carvalho Chehab 	int ret;
17600c0d06caSMauro Carvalho Chehab 
1761336fea92SMauro Carvalho Chehab 	dev_info(dev->dev, "v4l2 driver version %s\n", CX231XX_VERSION);
17620c0d06caSMauro Carvalho Chehab 
17630c0d06caSMauro Carvalho Chehab 	/* set default norm */
1764a25a7012SHans Verkuil 	dev->norm = V4L2_STD_PAL;
17650c0d06caSMauro Carvalho Chehab 	dev->width = norm_maxw(dev);
17660c0d06caSMauro Carvalho Chehab 	dev->height = norm_maxh(dev);
17670c0d06caSMauro Carvalho Chehab 	dev->interlaced = 0;
17680c0d06caSMauro Carvalho Chehab 
17690c0d06caSMauro Carvalho Chehab 	/* Analog specific initialization */
17700c0d06caSMauro Carvalho Chehab 	dev->format = &format[0];
17710c0d06caSMauro Carvalho Chehab 
17720c0d06caSMauro Carvalho Chehab 	/* Set the initial input */
17730c0d06caSMauro Carvalho Chehab 	video_mux(dev, dev->video_input);
17740c0d06caSMauro Carvalho Chehab 
17758774bed9SLaurent Pinchart 	call_all(dev, video, s_std, dev->norm);
1776d61072a4SHans Verkuil 
1777d2370f8eSHans Verkuil 	v4l2_ctrl_handler_init(&dev->ctrl_handler, 10);
1778d2370f8eSHans Verkuil 	v4l2_ctrl_handler_init(&dev->radio_ctrl_handler, 5);
1779d2370f8eSHans Verkuil 
1780d2370f8eSHans Verkuil 	if (dev->sd_cx25840) {
1781d2370f8eSHans Verkuil 		v4l2_ctrl_add_handler(&dev->ctrl_handler,
1782da1b1aeaSHans Verkuil 				dev->sd_cx25840->ctrl_handler, NULL, true);
1783d2370f8eSHans Verkuil 		v4l2_ctrl_add_handler(&dev->radio_ctrl_handler,
1784d2370f8eSHans Verkuil 				dev->sd_cx25840->ctrl_handler,
1785da1b1aeaSHans Verkuil 				v4l2_ctrl_radio_filter, true);
1786d2370f8eSHans Verkuil 	}
1787d2370f8eSHans Verkuil 
1788d2370f8eSHans Verkuil 	if (dev->ctrl_handler.error)
1789d2370f8eSHans Verkuil 		return dev->ctrl_handler.error;
1790d2370f8eSHans Verkuil 	if (dev->radio_ctrl_handler.error)
1791d2370f8eSHans Verkuil 		return dev->radio_ctrl_handler.error;
17920c0d06caSMauro Carvalho Chehab 
17930c0d06caSMauro Carvalho Chehab 	/* enable vbi capturing */
17940c0d06caSMauro Carvalho Chehab 	/* write code here...  */
17950c0d06caSMauro Carvalho Chehab 
17960c0d06caSMauro Carvalho Chehab 	/* allocate and fill video video_device struct */
179760acf187SHans Verkuil 	cx231xx_vdev_init(dev, &dev->vdev, &cx231xx_video_template, "video");
1798b6a40e72SMauro Carvalho Chehab #if defined(CONFIG_MEDIA_CONTROLLER)
1799b6a40e72SMauro Carvalho Chehab 	dev->video_pad.flags = MEDIA_PAD_FL_SINK;
1800ab22e77cSMauro Carvalho Chehab 	ret = media_entity_pads_init(&dev->vdev.entity, 1, &dev->video_pad);
1801b6a40e72SMauro Carvalho Chehab 	if (ret < 0)
1802b6a40e72SMauro Carvalho Chehab 		dev_err(dev->dev, "failed to initialize video media entity!\n");
1803b6a40e72SMauro Carvalho Chehab #endif
180460acf187SHans Verkuil 	dev->vdev.ctrl_handler = &dev->ctrl_handler;
18057c617138SHans Verkuil 
18067c617138SHans Verkuil 	q = &dev->vidq;
18077c617138SHans Verkuil 	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
18087c617138SHans Verkuil 	q->io_modes = VB2_USERPTR | VB2_MMAP | VB2_DMABUF | VB2_READ;
18097c617138SHans Verkuil 	q->drv_priv = dev;
18107c617138SHans Verkuil 	q->buf_struct_size = sizeof(struct cx231xx_buffer);
18117c617138SHans Verkuil 	q->ops = &cx231xx_video_qops;
18127c617138SHans Verkuil 	q->mem_ops = &vb2_vmalloc_memops;
18137c617138SHans Verkuil 	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
181480c2b40aSBenjamin Gaignard 	q->min_queued_buffers = 1;
18157c617138SHans Verkuil 	q->lock = &dev->lock;
18167c617138SHans Verkuil 	ret = vb2_queue_init(q);
18177c617138SHans Verkuil 	if (ret)
18187c617138SHans Verkuil 		return ret;
18197c617138SHans Verkuil 	dev->vdev.queue = q;
18208c3854d0SHans Verkuil 	dev->vdev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
18218c3854d0SHans Verkuil 				V4L2_CAP_VIDEO_CAPTURE;
18223c1ccbadSBrad Love 
18233c1ccbadSBrad Love 	switch (dev->model) { /* i2c device tuners */
18243c1ccbadSBrad Love 	case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx:
18253c1ccbadSBrad Love 	case CX231XX_BOARD_HAUPPAUGE_935C:
18263c1ccbadSBrad Love 	case CX231XX_BOARD_HAUPPAUGE_955Q:
18273c1ccbadSBrad Love 	case CX231XX_BOARD_HAUPPAUGE_975:
18283c1ccbadSBrad Love 	case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD:
18293c1ccbadSBrad Love 		dev->vdev.device_caps |= V4L2_CAP_TUNER;
18303c1ccbadSBrad Love 		break;
18313c1ccbadSBrad Love 	default:
18328c3854d0SHans Verkuil 		if (dev->tuner_type != TUNER_ABSENT)
18338c3854d0SHans Verkuil 			dev->vdev.device_caps |= V4L2_CAP_TUNER;
18343c1ccbadSBrad Love 		break;
18353c1ccbadSBrad Love 	}
18368c3854d0SHans Verkuil 
18370c0d06caSMauro Carvalho Chehab 	/* register v4l2 video video_device */
18387fbbbc78SHans Verkuil 	ret = video_register_device(&dev->vdev, VFL_TYPE_VIDEO,
18390c0d06caSMauro Carvalho Chehab 				    video_nr[dev->devno]);
18400c0d06caSMauro Carvalho Chehab 	if (ret) {
1841336fea92SMauro Carvalho Chehab 		dev_err(dev->dev,
1842b7085c08SMauro Carvalho Chehab 			"unable to register video device (error=%i).\n",
18430c0d06caSMauro Carvalho Chehab 			ret);
18440c0d06caSMauro Carvalho Chehab 		return ret;
18450c0d06caSMauro Carvalho Chehab 	}
18460c0d06caSMauro Carvalho Chehab 
1847336fea92SMauro Carvalho Chehab 	dev_info(dev->dev, "Registered video device %s [v4l2]\n",
184860acf187SHans Verkuil 		video_device_node_name(&dev->vdev));
18490c0d06caSMauro Carvalho Chehab 
18500c0d06caSMauro Carvalho Chehab 	/* Initialize VBI template */
18513724dde9SEzequiel Garcia 	cx231xx_vbi_template = cx231xx_video_template;
1852cc1e6315SMauro Carvalho Chehab 	strscpy(cx231xx_vbi_template.name, "cx231xx-vbi",
1853cc1e6315SMauro Carvalho Chehab 		sizeof(cx231xx_vbi_template.name));
18540c0d06caSMauro Carvalho Chehab 
18550c0d06caSMauro Carvalho Chehab 	/* Allocate and fill vbi video_device struct */
185660acf187SHans Verkuil 	cx231xx_vdev_init(dev, &dev->vbi_dev, &cx231xx_vbi_template, "vbi");
18570c0d06caSMauro Carvalho Chehab 
1858b6a40e72SMauro Carvalho Chehab #if defined(CONFIG_MEDIA_CONTROLLER)
1859b6a40e72SMauro Carvalho Chehab 	dev->vbi_pad.flags = MEDIA_PAD_FL_SINK;
1860ab22e77cSMauro Carvalho Chehab 	ret = media_entity_pads_init(&dev->vbi_dev.entity, 1, &dev->vbi_pad);
1861b6a40e72SMauro Carvalho Chehab 	if (ret < 0)
1862b6a40e72SMauro Carvalho Chehab 		dev_err(dev->dev, "failed to initialize vbi media entity!\n");
1863b6a40e72SMauro Carvalho Chehab #endif
186460acf187SHans Verkuil 	dev->vbi_dev.ctrl_handler = &dev->ctrl_handler;
18657c617138SHans Verkuil 
18667c617138SHans Verkuil 	q = &dev->vbiq;
18677c617138SHans Verkuil 	q->type = V4L2_BUF_TYPE_VBI_CAPTURE;
18687c617138SHans Verkuil 	q->io_modes = VB2_USERPTR | VB2_MMAP | VB2_DMABUF | VB2_READ;
18697c617138SHans Verkuil 	q->drv_priv = dev;
18707c617138SHans Verkuil 	q->buf_struct_size = sizeof(struct cx231xx_buffer);
18717c617138SHans Verkuil 	q->ops = &cx231xx_vbi_qops;
18727c617138SHans Verkuil 	q->mem_ops = &vb2_vmalloc_memops;
18737c617138SHans Verkuil 	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
187480c2b40aSBenjamin Gaignard 	q->min_queued_buffers = 1;
18757c617138SHans Verkuil 	q->lock = &dev->lock;
18767c617138SHans Verkuil 	ret = vb2_queue_init(q);
18777c617138SHans Verkuil 	if (ret)
18787c617138SHans Verkuil 		return ret;
18797c617138SHans Verkuil 	dev->vbi_dev.queue = q;
18808c3854d0SHans Verkuil 	dev->vbi_dev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
18818c3854d0SHans Verkuil 				   V4L2_CAP_VBI_CAPTURE;
18823c1ccbadSBrad Love 	switch (dev->model) { /* i2c device tuners */
18833c1ccbadSBrad Love 	case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx:
18843c1ccbadSBrad Love 	case CX231XX_BOARD_HAUPPAUGE_935C:
18853c1ccbadSBrad Love 	case CX231XX_BOARD_HAUPPAUGE_955Q:
18863c1ccbadSBrad Love 	case CX231XX_BOARD_HAUPPAUGE_975:
18873c1ccbadSBrad Love 	case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD:
18883c1ccbadSBrad Love 		dev->vbi_dev.device_caps |= V4L2_CAP_TUNER;
18893c1ccbadSBrad Love 		break;
18903c1ccbadSBrad Love 	default:
18918c3854d0SHans Verkuil 		if (dev->tuner_type != TUNER_ABSENT)
18928c3854d0SHans Verkuil 			dev->vbi_dev.device_caps |= V4L2_CAP_TUNER;
18933c1ccbadSBrad Love 	}
18948c3854d0SHans Verkuil 
18950c0d06caSMauro Carvalho Chehab 	/* register v4l2 vbi video_device */
189660acf187SHans Verkuil 	ret = video_register_device(&dev->vbi_dev, VFL_TYPE_VBI,
18970c0d06caSMauro Carvalho Chehab 				    vbi_nr[dev->devno]);
18980c0d06caSMauro Carvalho Chehab 	if (ret < 0) {
1899336fea92SMauro Carvalho Chehab 		dev_err(dev->dev, "unable to register vbi device\n");
19000c0d06caSMauro Carvalho Chehab 		return ret;
19010c0d06caSMauro Carvalho Chehab 	}
19020c0d06caSMauro Carvalho Chehab 
1903336fea92SMauro Carvalho Chehab 	dev_info(dev->dev, "Registered VBI device %s\n",
190460acf187SHans Verkuil 		video_device_node_name(&dev->vbi_dev));
19050c0d06caSMauro Carvalho Chehab 
19060c0d06caSMauro Carvalho Chehab 	if (cx231xx_boards[dev->model].radio.type == CX231XX_RADIO) {
190760acf187SHans Verkuil 		cx231xx_vdev_init(dev, &dev->radio_dev,
190860acf187SHans Verkuil 				&cx231xx_radio_template, "radio");
190960acf187SHans Verkuil 		dev->radio_dev.ctrl_handler = &dev->radio_ctrl_handler;
19108c3854d0SHans Verkuil 		dev->radio_dev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER;
191160acf187SHans Verkuil 		ret = video_register_device(&dev->radio_dev, VFL_TYPE_RADIO,
19120c0d06caSMauro Carvalho Chehab 					    radio_nr[dev->devno]);
19130c0d06caSMauro Carvalho Chehab 		if (ret < 0) {
1914336fea92SMauro Carvalho Chehab 			dev_err(dev->dev,
1915b7085c08SMauro Carvalho Chehab 				"can't register radio device\n");
19160c0d06caSMauro Carvalho Chehab 			return ret;
19170c0d06caSMauro Carvalho Chehab 		}
1918336fea92SMauro Carvalho Chehab 		dev_info(dev->dev, "Registered radio device as %s\n",
191960acf187SHans Verkuil 			video_device_node_name(&dev->radio_dev));
19200c0d06caSMauro Carvalho Chehab 	}
19210c0d06caSMauro Carvalho Chehab 
19220c0d06caSMauro Carvalho Chehab 	return 0;
19230c0d06caSMauro Carvalho Chehab }
1924