xref: /linux/drivers/media/pci/cx18/cx18-ioctl.c (revision 0cdee263bc5e7b20f657ea09f9272f50c568f35b)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21c1e45d1SHans Verkuil /*
31c1e45d1SHans Verkuil  *  cx18 ioctl system call
41c1e45d1SHans Verkuil  *
51c1e45d1SHans Verkuil  *  Derived from ivtv-ioctl.c
61c1e45d1SHans Verkuil  *
71c1e45d1SHans Verkuil  *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
86afdeaf8SAndy Walls  *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.net>
91c1e45d1SHans Verkuil  */
101c1e45d1SHans Verkuil 
111c1e45d1SHans Verkuil #include "cx18-driver.h"
12b1526421SAndy Walls #include "cx18-io.h"
131c1e45d1SHans Verkuil #include "cx18-version.h"
141c1e45d1SHans Verkuil #include "cx18-mailbox.h"
151c1e45d1SHans Verkuil #include "cx18-i2c.h"
161c1e45d1SHans Verkuil #include "cx18-queue.h"
171c1e45d1SHans Verkuil #include "cx18-fileops.h"
181c1e45d1SHans Verkuil #include "cx18-vbi.h"
191c1e45d1SHans Verkuil #include "cx18-audio.h"
201c1e45d1SHans Verkuil #include "cx18-video.h"
211c1e45d1SHans Verkuil #include "cx18-streams.h"
221c1e45d1SHans Verkuil #include "cx18-ioctl.h"
231c1e45d1SHans Verkuil #include "cx18-gpio.h"
241c1e45d1SHans Verkuil #include "cx18-controls.h"
251c1e45d1SHans Verkuil #include "cx18-cards.h"
261c1e45d1SHans Verkuil #include "cx18-av-core.h"
271c1e45d1SHans Verkuil #include <media/tveeprom.h>
28eaa80c44SHans Verkuil #include <media/v4l2-event.h>
291c1e45d1SHans Verkuil 
30651640f6SHans Verkuil static const struct v4l2_fmtdesc cx18_formats_yuv[] = {
31651640f6SHans Verkuil 	{
32651640f6SHans Verkuil 		.index = 0,
33651640f6SHans Verkuil 		.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
34651640f6SHans Verkuil 		.pixelformat = V4L2_PIX_FMT_NV12_16L16,
35651640f6SHans Verkuil 	},
36651640f6SHans Verkuil 	{
37651640f6SHans Verkuil 		.index = 1,
38651640f6SHans Verkuil 		.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
39651640f6SHans Verkuil 		.pixelformat = V4L2_PIX_FMT_UYVY,
40651640f6SHans Verkuil 	},
41651640f6SHans Verkuil };
42651640f6SHans Verkuil 
43651640f6SHans Verkuil static const struct v4l2_fmtdesc cx18_formats_mpeg[] = {
44651640f6SHans Verkuil 	{
45651640f6SHans Verkuil 		.index = 0,
46651640f6SHans Verkuil 		.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
47651640f6SHans Verkuil 		.flags = V4L2_FMT_FLAG_COMPRESSED,
48651640f6SHans Verkuil 		.pixelformat = V4L2_PIX_FMT_MPEG,
49651640f6SHans Verkuil 	},
50651640f6SHans Verkuil };
51651640f6SHans Verkuil 
cx18_g_fmt_vid_cap(struct file * file,void * fh,struct v4l2_format * fmt)5200d08584SHans Verkuil static int cx18_g_fmt_vid_cap(struct file *file, void *fh,
5300d08584SHans Verkuil 			      struct v4l2_format *fmt)
5400d08584SHans Verkuil {
5500d08584SHans Verkuil 	struct cx18_open_id *id = fh2id(fh);
5600d08584SHans Verkuil 	struct cx18 *cx = id->cx;
5700d08584SHans Verkuil 	struct cx18_stream *s = &cx->streams[id->type];
5800d08584SHans Verkuil 	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
5900d08584SHans Verkuil 
6000d08584SHans Verkuil 	pixfmt->width = cx->cxhdl.width;
6100d08584SHans Verkuil 	pixfmt->height = cx->cxhdl.height;
6200d08584SHans Verkuil 	pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
6300d08584SHans Verkuil 	pixfmt->field = V4L2_FIELD_INTERLACED;
6400d08584SHans Verkuil 	if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
6500d08584SHans Verkuil 		pixfmt->pixelformat = s->pixelformat;
6600d08584SHans Verkuil 		pixfmt->sizeimage = s->vb_bytes_per_frame;
6700d08584SHans Verkuil 		pixfmt->bytesperline = s->vb_bytes_per_line;
6800d08584SHans Verkuil 	} else {
6900d08584SHans Verkuil 		pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
7000d08584SHans Verkuil 		pixfmt->sizeimage = 128 * 1024;
7100d08584SHans Verkuil 		pixfmt->bytesperline = 0;
7200d08584SHans Verkuil 	}
7300d08584SHans Verkuil 	return 0;
7400d08584SHans Verkuil }
7500d08584SHans Verkuil 
cx18_try_fmt_vid_cap(struct file * file,void * fh,struct v4l2_format * fmt)7600d08584SHans Verkuil static int cx18_try_fmt_vid_cap(struct file *file, void *fh,
7700d08584SHans Verkuil 				struct v4l2_format *fmt)
7800d08584SHans Verkuil {
7900d08584SHans Verkuil 	struct cx18_open_id *id = fh2id(fh);
8000d08584SHans Verkuil 	struct cx18 *cx = id->cx;
8113de5a51SHans Verkuil 	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
8213de5a51SHans Verkuil 	int w = pixfmt->width;
8313de5a51SHans Verkuil 	int h = pixfmt->height;
8400d08584SHans Verkuil 
8500d08584SHans Verkuil 	w = min(w, 720);
8613de5a51SHans Verkuil 	w = max(w, 720 / 16);
8700d08584SHans Verkuil 
8800d08584SHans Verkuil 	h = min(h, cx->is_50hz ? 576 : 480);
8913de5a51SHans Verkuil 	h = max(h, (cx->is_50hz ? 576 : 480) / 8);
9000d08584SHans Verkuil 
9113de5a51SHans Verkuil 	if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
9213de5a51SHans Verkuil 		if (pixfmt->pixelformat != V4L2_PIX_FMT_NV12_16L16 &&
9313de5a51SHans Verkuil 		    pixfmt->pixelformat != V4L2_PIX_FMT_UYVY)
9413de5a51SHans Verkuil 			pixfmt->pixelformat = V4L2_PIX_FMT_UYVY;
9513de5a51SHans Verkuil 		/* YUV height must be a multiple of 32 */
9613de5a51SHans Verkuil 		h = round_up(h, 32);
9713de5a51SHans Verkuil 		/*
9813de5a51SHans Verkuil 		 * HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
9913de5a51SHans Verkuil 		 * UYUV YUV size is (Y=(h*720) + UV=(h*(720)))
10013de5a51SHans Verkuil 		 */
10113de5a51SHans Verkuil 		if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12_16L16) {
10213de5a51SHans Verkuil 			pixfmt->sizeimage = h * 720 * 3 / 2;
10313de5a51SHans Verkuil 			pixfmt->bytesperline = 720; /* First plane */
10413de5a51SHans Verkuil 		} else {
10513de5a51SHans Verkuil 			pixfmt->sizeimage = h * 720 * 2;
10613de5a51SHans Verkuil 			pixfmt->bytesperline = 1440; /* Packed */
10713de5a51SHans Verkuil 		}
10813de5a51SHans Verkuil 	} else {
10913de5a51SHans Verkuil 		pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
11013de5a51SHans Verkuil 		pixfmt->sizeimage = 128 * 1024;
11113de5a51SHans Verkuil 		pixfmt->bytesperline = 0;
11213de5a51SHans Verkuil 	}
11313de5a51SHans Verkuil 
11413de5a51SHans Verkuil 	pixfmt->width = w;
11513de5a51SHans Verkuil 	pixfmt->height = h;
11613de5a51SHans Verkuil 	pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
11713de5a51SHans Verkuil 	pixfmt->field = V4L2_FIELD_INTERLACED;
11800d08584SHans Verkuil 	return 0;
11900d08584SHans Verkuil }
12000d08584SHans Verkuil 
cx18_s_fmt_vid_cap(struct file * file,void * fh,struct v4l2_format * fmt)12100d08584SHans Verkuil static int cx18_s_fmt_vid_cap(struct file *file, void *fh,
12200d08584SHans Verkuil 			      struct v4l2_format *fmt)
12300d08584SHans Verkuil {
12400d08584SHans Verkuil 	struct cx18_open_id *id = fh2id(fh);
12500d08584SHans Verkuil 	struct cx18 *cx = id->cx;
12600d08584SHans Verkuil 	struct v4l2_subdev_format format = {
12700d08584SHans Verkuil 		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
12800d08584SHans Verkuil 	};
12900d08584SHans Verkuil 	struct cx18_stream *s = &cx->streams[id->type];
13000d08584SHans Verkuil 	int ret;
13100d08584SHans Verkuil 	int w, h;
13200d08584SHans Verkuil 
13300d08584SHans Verkuil 	ret = cx18_try_fmt_vid_cap(file, fh, fmt);
13400d08584SHans Verkuil 	if (ret)
13500d08584SHans Verkuil 		return ret;
13600d08584SHans Verkuil 	w = fmt->fmt.pix.width;
13700d08584SHans Verkuil 	h = fmt->fmt.pix.height;
13800d08584SHans Verkuil 
13900d08584SHans Verkuil 	if (cx->cxhdl.width == w && cx->cxhdl.height == h &&
14000d08584SHans Verkuil 	    s->pixelformat == fmt->fmt.pix.pixelformat)
14100d08584SHans Verkuil 		return 0;
14200d08584SHans Verkuil 
14300d08584SHans Verkuil 	if (atomic_read(&cx->ana_capturing) > 0)
14400d08584SHans Verkuil 		return -EBUSY;
14500d08584SHans Verkuil 
14600d08584SHans Verkuil 	s->pixelformat = fmt->fmt.pix.pixelformat;
14713de5a51SHans Verkuil 	s->vb_bytes_per_frame = fmt->fmt.pix.sizeimage;
14813de5a51SHans Verkuil 	s->vb_bytes_per_line = fmt->fmt.pix.bytesperline;
14900d08584SHans Verkuil 
15000d08584SHans Verkuil 	format.format.width = cx->cxhdl.width = w;
15100d08584SHans Verkuil 	format.format.height = cx->cxhdl.height = h;
15200d08584SHans Verkuil 	format.format.code = MEDIA_BUS_FMT_FIXED;
15300d08584SHans Verkuil 	v4l2_subdev_call(cx->sd_av, pad, set_fmt, NULL, &format);
15400d08584SHans Verkuil 	return cx18_g_fmt_vid_cap(file, fh, fmt);
15500d08584SHans Verkuil }
15600d08584SHans Verkuil 
cx18_service2vbi(int type)157aed6abd6SMauro Carvalho Chehab u16 cx18_service2vbi(int type)
1581c1e45d1SHans Verkuil {
1591c1e45d1SHans Verkuil 	switch (type) {
1601c1e45d1SHans Verkuil 	case V4L2_SLICED_TELETEXT_B:
1611c1e45d1SHans Verkuil 		return CX18_SLICED_TYPE_TELETEXT_B;
1621c1e45d1SHans Verkuil 	case V4L2_SLICED_CAPTION_525:
1631c1e45d1SHans Verkuil 		return CX18_SLICED_TYPE_CAPTION_525;
1641c1e45d1SHans Verkuil 	case V4L2_SLICED_WSS_625:
1651c1e45d1SHans Verkuil 		return CX18_SLICED_TYPE_WSS_625;
1661c1e45d1SHans Verkuil 	case V4L2_SLICED_VPS:
1671c1e45d1SHans Verkuil 		return CX18_SLICED_TYPE_VPS;
1681c1e45d1SHans Verkuil 	default:
1691c1e45d1SHans Verkuil 		return 0;
1701c1e45d1SHans Verkuil 	}
1711c1e45d1SHans Verkuil }
1721c1e45d1SHans Verkuil 
173c1994084SAndy Walls /* Check if VBI services are allowed on the (field, line) for the video std */
valid_service_line(int field,int line,int is_pal)1741c1e45d1SHans Verkuil static int valid_service_line(int field, int line, int is_pal)
1751c1e45d1SHans Verkuil {
176c1994084SAndy Walls 	return (is_pal && line >= 6 &&
177c1994084SAndy Walls 		((field == 0 && line <= 23) || (field == 1 && line <= 22))) ||
1781c1e45d1SHans Verkuil 	       (!is_pal && line >= 10 && line < 22);
1791c1e45d1SHans Verkuil }
1801c1e45d1SHans Verkuil 
181c1994084SAndy Walls /*
182c1994084SAndy Walls  * For a (field, line, std) and inbound potential set of services for that line,
183c1994084SAndy Walls  * return the first valid service of those passed in the incoming set for that
184c1994084SAndy Walls  * line in priority order:
185c1994084SAndy Walls  * CC, VPS, or WSS over TELETEXT for well known lines
186c1994084SAndy Walls  * TELETEXT, before VPS, before CC, before WSS, for other lines
187c1994084SAndy Walls  */
select_service_from_set(int field,int line,u16 set,int is_pal)1881c1e45d1SHans Verkuil static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
1891c1e45d1SHans Verkuil {
1901c1e45d1SHans Verkuil 	u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525);
1911c1e45d1SHans Verkuil 	int i;
1921c1e45d1SHans Verkuil 
1931c1e45d1SHans Verkuil 	set = set & valid_set;
1941c1e45d1SHans Verkuil 	if (set == 0 || !valid_service_line(field, line, is_pal))
1951c1e45d1SHans Verkuil 		return 0;
1961c1e45d1SHans Verkuil 	if (!is_pal) {
1971c1e45d1SHans Verkuil 		if (line == 21 && (set & V4L2_SLICED_CAPTION_525))
1981c1e45d1SHans Verkuil 			return V4L2_SLICED_CAPTION_525;
1991c1e45d1SHans Verkuil 	} else {
2001c1e45d1SHans Verkuil 		if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS))
2011c1e45d1SHans Verkuil 			return V4L2_SLICED_VPS;
2021c1e45d1SHans Verkuil 		if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625))
2031c1e45d1SHans Verkuil 			return V4L2_SLICED_WSS_625;
2041c1e45d1SHans Verkuil 		if (line == 23)
2051c1e45d1SHans Verkuil 			return 0;
2061c1e45d1SHans Verkuil 	}
2071c1e45d1SHans Verkuil 	for (i = 0; i < 32; i++) {
20895c52069SMauro Carvalho Chehab 		if (BIT(i) & set)
2091c1e45d1SHans Verkuil 			return 1 << i;
2101c1e45d1SHans Verkuil 	}
2111c1e45d1SHans Verkuil 	return 0;
2121c1e45d1SHans Verkuil }
2131c1e45d1SHans Verkuil 
214c1994084SAndy Walls /*
215c1994084SAndy Walls  * Expand the service_set of *fmt into valid service_lines for the std,
216c1994084SAndy Walls  * and clear the passed in fmt->service_set
217c1994084SAndy Walls  */
cx18_expand_service_set(struct v4l2_sliced_vbi_format * fmt,int is_pal)218aed6abd6SMauro Carvalho Chehab void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
2191c1e45d1SHans Verkuil {
2201c1e45d1SHans Verkuil 	u16 set = fmt->service_set;
2211c1e45d1SHans Verkuil 	int f, l;
2221c1e45d1SHans Verkuil 
2231c1e45d1SHans Verkuil 	fmt->service_set = 0;
2241c1e45d1SHans Verkuil 	for (f = 0; f < 2; f++) {
2251c1e45d1SHans Verkuil 		for (l = 0; l < 24; l++)
2261c1e45d1SHans Verkuil 			fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal);
2271c1e45d1SHans Verkuil 	}
2281c1e45d1SHans Verkuil }
2291c1e45d1SHans Verkuil 
230c1994084SAndy Walls /*
231c1994084SAndy Walls  * Sanitize the service_lines in *fmt per the video std, and return 1
232c1994084SAndy Walls  * if any service_line is left as valid after santization
233c1994084SAndy Walls  */
check_service_set(struct v4l2_sliced_vbi_format * fmt,int is_pal)234302df970SAndy Walls static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
235302df970SAndy Walls {
236302df970SAndy Walls 	int f, l;
237302df970SAndy Walls 	u16 set = 0;
238302df970SAndy Walls 
239302df970SAndy Walls 	for (f = 0; f < 2; f++) {
240302df970SAndy Walls 		for (l = 0; l < 24; l++) {
241302df970SAndy Walls 			fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);
242302df970SAndy Walls 			set |= fmt->service_lines[f][l];
243302df970SAndy Walls 		}
244302df970SAndy Walls 	}
245302df970SAndy Walls 	return set != 0;
246302df970SAndy Walls }
2471c1e45d1SHans Verkuil 
248c1994084SAndy Walls /* Compute the service_set from the assumed valid service_lines of *fmt */
cx18_get_service_set(struct v4l2_sliced_vbi_format * fmt)249aed6abd6SMauro Carvalho Chehab u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt)
2501c1e45d1SHans Verkuil {
2511c1e45d1SHans Verkuil 	int f, l;
2521c1e45d1SHans Verkuil 	u16 set = 0;
2531c1e45d1SHans Verkuil 
2541c1e45d1SHans Verkuil 	for (f = 0; f < 2; f++) {
2551c1e45d1SHans Verkuil 		for (l = 0; l < 24; l++)
2561c1e45d1SHans Verkuil 			set |= fmt->service_lines[f][l];
2571c1e45d1SHans Verkuil 	}
2581c1e45d1SHans Verkuil 	return set;
2591c1e45d1SHans Verkuil }
2601c1e45d1SHans Verkuil 
cx18_g_fmt_vbi_cap(struct file * file,void * fh,struct v4l2_format * fmt)2613b6fe58fSAndy Walls static int cx18_g_fmt_vbi_cap(struct file *file, void *fh,
2623b6fe58fSAndy Walls 				struct v4l2_format *fmt)
2633b6fe58fSAndy Walls {
2640b5f265aSHans Verkuil 	struct cx18 *cx = fh2id(fh)->cx;
2650b5a30e9SHans Verkuil 	struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi;
2663b6fe58fSAndy Walls 
2670b5a30e9SHans Verkuil 	vbifmt->sampling_rate = 27000000;
268c1994084SAndy Walls 	vbifmt->offset = 248; /* FIXME - slightly wrong for both 50 & 60 Hz */
269318de791SMauro Carvalho Chehab 	vbifmt->samples_per_line = VBI_ACTIVE_SAMPLES - 4;
2700b5a30e9SHans Verkuil 	vbifmt->sample_format = V4L2_PIX_FMT_GREY;
2710b5a30e9SHans Verkuil 	vbifmt->start[0] = cx->vbi.start[0];
2720b5a30e9SHans Verkuil 	vbifmt->start[1] = cx->vbi.start[1];
2730b5a30e9SHans Verkuil 	vbifmt->count[0] = vbifmt->count[1] = cx->vbi.count;
2740b5a30e9SHans Verkuil 	vbifmt->flags = 0;
2750b5a30e9SHans Verkuil 	vbifmt->reserved[0] = 0;
2760b5a30e9SHans Verkuil 	vbifmt->reserved[1] = 0;
2771c1e45d1SHans Verkuil 	return 0;
2781c1e45d1SHans Verkuil }
2791c1e45d1SHans Verkuil 
cx18_g_fmt_sliced_vbi_cap(struct file * file,void * fh,struct v4l2_format * fmt)2803b6fe58fSAndy Walls static int cx18_g_fmt_sliced_vbi_cap(struct file *file, void *fh,
2813b6fe58fSAndy Walls 					struct v4l2_format *fmt)
2821c1e45d1SHans Verkuil {
2830b5f265aSHans Verkuil 	struct cx18 *cx = fh2id(fh)->cx;
284302df970SAndy Walls 	struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
285302df970SAndy Walls 
286c1994084SAndy Walls 	/* sane, V4L2 spec compliant, defaults */
287302df970SAndy Walls 	vbifmt->reserved[0] = 0;
288302df970SAndy Walls 	vbifmt->reserved[1] = 0;
289302df970SAndy Walls 	vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
290302df970SAndy Walls 	memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
291c1994084SAndy Walls 	vbifmt->service_set = 0;
292302df970SAndy Walls 
293c1994084SAndy Walls 	/*
294c1994084SAndy Walls 	 * Fetch the configured service_lines and total service_set from the
295c1994084SAndy Walls 	 * digitizer/slicer.  Note, cx18_av_vbi() wipes the passed in
296c1994084SAndy Walls 	 * fmt->fmt.sliced under valid calling conditions
297c1994084SAndy Walls 	 */
298add632cdSHans Verkuil 	if (v4l2_subdev_call(cx->sd_av, vbi, g_sliced_fmt, &fmt->fmt.sliced))
299c1994084SAndy Walls 		return -EINVAL;
300c1994084SAndy Walls 
301302df970SAndy Walls 	vbifmt->service_set = cx18_get_service_set(vbifmt);
302302df970SAndy Walls 	return 0;
3033b6fe58fSAndy Walls }
3041c1e45d1SHans Verkuil 
cx18_try_fmt_vbi_cap(struct file * file,void * fh,struct v4l2_format * fmt)3053b6fe58fSAndy Walls static int cx18_try_fmt_vbi_cap(struct file *file, void *fh,
3063b6fe58fSAndy Walls 				struct v4l2_format *fmt)
3073b6fe58fSAndy Walls {
3083b6fe58fSAndy Walls 	return cx18_g_fmt_vbi_cap(file, fh, fmt);
3093b6fe58fSAndy Walls }
3103b6fe58fSAndy Walls 
cx18_try_fmt_sliced_vbi_cap(struct file * file,void * fh,struct v4l2_format * fmt)3113b6fe58fSAndy Walls static int cx18_try_fmt_sliced_vbi_cap(struct file *file, void *fh,
3123b6fe58fSAndy Walls 					struct v4l2_format *fmt)
3133b6fe58fSAndy Walls {
3140b5f265aSHans Verkuil 	struct cx18 *cx = fh2id(fh)->cx;
315302df970SAndy Walls 	struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
316302df970SAndy Walls 
317302df970SAndy Walls 	vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
318302df970SAndy Walls 	vbifmt->reserved[0] = 0;
319302df970SAndy Walls 	vbifmt->reserved[1] = 0;
320302df970SAndy Walls 
321c1994084SAndy Walls 	/* If given a service set, expand it validly & clear passed in set */
322302df970SAndy Walls 	if (vbifmt->service_set)
323302df970SAndy Walls 		cx18_expand_service_set(vbifmt, cx->is_50hz);
324c1994084SAndy Walls 	/* Sanitize the service_lines, and compute the new set if any valid */
325c1994084SAndy Walls 	if (check_service_set(vbifmt, cx->is_50hz))
326302df970SAndy Walls 		vbifmt->service_set = cx18_get_service_set(vbifmt);
327302df970SAndy Walls 	return 0;
3283b6fe58fSAndy Walls }
3293b6fe58fSAndy Walls 
cx18_s_fmt_vbi_cap(struct file * file,void * fh,struct v4l2_format * fmt)3303b6fe58fSAndy Walls static int cx18_s_fmt_vbi_cap(struct file *file, void *fh,
3313b6fe58fSAndy Walls 				struct v4l2_format *fmt)
3323b6fe58fSAndy Walls {
3330b5f265aSHans Verkuil 	struct cx18_open_id *id = fh2id(fh);
3343b6fe58fSAndy Walls 	struct cx18 *cx = id->cx;
3353b6fe58fSAndy Walls 	int ret;
3363b6fe58fSAndy Walls 
337dcc0ef88SAndy Walls 	/*
338dcc0ef88SAndy Walls 	 * Changing the Encoder's Raw VBI parameters won't have any effect
339dcc0ef88SAndy Walls 	 * if any analog capture is ongoing
340dcc0ef88SAndy Walls 	 */
341dcc0ef88SAndy Walls 	if (!cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
3421c1e45d1SHans Verkuil 		return -EBUSY;
3433b6fe58fSAndy Walls 
344c1994084SAndy Walls 	/*
345c1994084SAndy Walls 	 * Set the digitizer registers for raw active VBI.
346c1994084SAndy Walls 	 * Note cx18_av_vbi_wipes out a lot of the passed in fmt under valid
347c1994084SAndy Walls 	 * calling conditions
348c1994084SAndy Walls 	 */
349add632cdSHans Verkuil 	ret = v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &fmt->fmt.vbi);
350c1994084SAndy Walls 	if (ret)
351c1994084SAndy Walls 		return ret;
352c1994084SAndy Walls 
353c1994084SAndy Walls 	/* Store our new v4l2 (non-)sliced VBI state */
3541c1e45d1SHans Verkuil 	cx->vbi.sliced_in->service_set = 0;
355dd073434SAndy Walls 	cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
356c1994084SAndy Walls 
3573b6fe58fSAndy Walls 	return cx18_g_fmt_vbi_cap(file, fh, fmt);
3581c1e45d1SHans Verkuil }
3591c1e45d1SHans Verkuil 
cx18_s_fmt_sliced_vbi_cap(struct file * file,void * fh,struct v4l2_format * fmt)3603b6fe58fSAndy Walls static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh,
3613b6fe58fSAndy Walls 					struct v4l2_format *fmt)
3621c1e45d1SHans Verkuil {
3630b5f265aSHans Verkuil 	struct cx18_open_id *id = fh2id(fh);
364302df970SAndy Walls 	struct cx18 *cx = id->cx;
365302df970SAndy Walls 	int ret;
366302df970SAndy Walls 	struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
367302df970SAndy Walls 
368c1994084SAndy Walls 	cx18_try_fmt_sliced_vbi_cap(file, fh, fmt);
369c1994084SAndy Walls 
370dcc0ef88SAndy Walls 	/*
371dcc0ef88SAndy Walls 	 * Changing the Encoder's Raw VBI parameters won't have any effect
372dcc0ef88SAndy Walls 	 * if any analog capture is ongoing
373dcc0ef88SAndy Walls 	 */
374dcc0ef88SAndy Walls 	if (cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
375c1994084SAndy Walls 		return -EBUSY;
376dcc0ef88SAndy Walls 
377c1994084SAndy Walls 	/*
378c1994084SAndy Walls 	 * Set the service_lines requested in the digitizer/slicer registers.
379c1994084SAndy Walls 	 * Note, cx18_av_vbi() wipes some "impossible" service lines in the
380c1994084SAndy Walls 	 * passed in fmt->fmt.sliced under valid calling conditions
381c1994084SAndy Walls 	 */
382add632cdSHans Verkuil 	ret = v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &fmt->fmt.sliced);
383302df970SAndy Walls 	if (ret)
384302df970SAndy Walls 		return ret;
385c1994084SAndy Walls 	/* Store our current v4l2 sliced VBI settings */
386302df970SAndy Walls 	cx->vbi.in.type =  V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
387302df970SAndy Walls 	memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in));
388302df970SAndy Walls 	return 0;
3893b6fe58fSAndy Walls }
3901c1e45d1SHans Verkuil 
39136ecd495SHans Verkuil #ifdef CONFIG_VIDEO_ADV_DEBUG
cx18_g_register(struct file * file,void * fh,struct v4l2_dbg_register * reg)3923b6fe58fSAndy Walls static int cx18_g_register(struct file *file, void *fh,
393aecde8b5SHans Verkuil 				struct v4l2_dbg_register *reg)
3943b6fe58fSAndy Walls {
3950b5f265aSHans Verkuil 	struct cx18 *cx = fh2id(fh)->cx;
3963b6fe58fSAndy Walls 
397771d7733SHans Verkuil 	if (reg->reg & 0x3)
398771d7733SHans Verkuil 		return -EINVAL;
399977ba3b1SHans Verkuil 	if (reg->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE)
400977ba3b1SHans Verkuil 		return -EINVAL;
401977ba3b1SHans Verkuil 	reg->size = 4;
402977ba3b1SHans Verkuil 	reg->val = cx18_read_enc(cx, reg->reg);
403977ba3b1SHans Verkuil 	return 0;
404977ba3b1SHans Verkuil }
4051c1e45d1SHans Verkuil 
cx18_s_register(struct file * file,void * fh,const struct v4l2_dbg_register * reg)4063b6fe58fSAndy Walls static int cx18_s_register(struct file *file, void *fh,
407977ba3b1SHans Verkuil 				const struct v4l2_dbg_register *reg)
4081c1e45d1SHans Verkuil {
4090b5f265aSHans Verkuil 	struct cx18 *cx = fh2id(fh)->cx;
4101c1e45d1SHans Verkuil 
411771d7733SHans Verkuil 	if (reg->reg & 0x3)
412771d7733SHans Verkuil 		return -EINVAL;
413977ba3b1SHans Verkuil 	if (reg->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE)
414977ba3b1SHans Verkuil 		return -EINVAL;
415977ba3b1SHans Verkuil 	cx18_write_enc(cx, reg->val, reg->reg);
416977ba3b1SHans Verkuil 	return 0;
417977ba3b1SHans Verkuil }
41836ecd495SHans Verkuil #endif
4193b6fe58fSAndy Walls 
cx18_querycap(struct file * file,void * fh,struct v4l2_capability * vcap)4203b6fe58fSAndy Walls static int cx18_querycap(struct file *file, void *fh,
4213b6fe58fSAndy Walls 				struct v4l2_capability *vcap)
4223b6fe58fSAndy Walls {
42309fc9802SSimon Farnsworth 	struct cx18_open_id *id = fh2id(fh);
42409fc9802SSimon Farnsworth 	struct cx18 *cx = id->cx;
4251c1e45d1SHans Verkuil 
426c0decac1SMauro Carvalho Chehab 	strscpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver));
427c0decac1SMauro Carvalho Chehab 	strscpy(vcap->card, cx->card_name, sizeof(vcap->card));
42821615365SHans Verkuil 	vcap->capabilities = cx->v4l2_cap | V4L2_CAP_DEVICE_CAPS;
4293b6fe58fSAndy Walls 	return 0;
4301c1e45d1SHans Verkuil }
4311c1e45d1SHans Verkuil 
cx18_enumaudio(struct file * file,void * fh,struct v4l2_audio * vin)4323b6fe58fSAndy Walls static int cx18_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin)
4333b6fe58fSAndy Walls {
4340b5f265aSHans Verkuil 	struct cx18 *cx = fh2id(fh)->cx;
4353b6fe58fSAndy Walls 
4361c1e45d1SHans Verkuil 	return cx18_get_audio_input(cx, vin->index, vin);
4371c1e45d1SHans Verkuil }
4381c1e45d1SHans Verkuil 
cx18_g_audio(struct file * file,void * fh,struct v4l2_audio * vin)4393b6fe58fSAndy Walls static int cx18_g_audio(struct file *file, void *fh, struct v4l2_audio *vin)
4403b6fe58fSAndy Walls {
4410b5f265aSHans Verkuil 	struct cx18 *cx = fh2id(fh)->cx;
4423b6fe58fSAndy Walls 
4431c1e45d1SHans Verkuil 	vin->index = cx->audio_input;
4441c1e45d1SHans Verkuil 	return cx18_get_audio_input(cx, vin->index, vin);
4451c1e45d1SHans Verkuil }
4461c1e45d1SHans Verkuil 
cx18_s_audio(struct file * file,void * fh,const struct v4l2_audio * vout)4470e8025b9SHans Verkuil static int cx18_s_audio(struct file *file, void *fh, const struct v4l2_audio *vout)
4483b6fe58fSAndy Walls {
4490b5f265aSHans Verkuil 	struct cx18 *cx = fh2id(fh)->cx;
4503b6fe58fSAndy Walls 
4511c1e45d1SHans Verkuil 	if (vout->index >= cx->nof_audio_inputs)
4521c1e45d1SHans Verkuil 		return -EINVAL;
4531c1e45d1SHans Verkuil 	cx->audio_input = vout->index;
4541c1e45d1SHans Verkuil 	cx18_audio_set_io(cx);
4553b6fe58fSAndy Walls 	return 0;
4561c1e45d1SHans Verkuil }
4571c1e45d1SHans Verkuil 
cx18_enum_input(struct file * file,void * fh,struct v4l2_input * vin)4583b6fe58fSAndy Walls static int cx18_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
4593b6fe58fSAndy Walls {
4600b5f265aSHans Verkuil 	struct cx18 *cx = fh2id(fh)->cx;
4613b6fe58fSAndy Walls 
4621c1e45d1SHans Verkuil 	/* set it to defaults from our table */
4631c1e45d1SHans Verkuil 	return cx18_get_input(cx, vin->index, vin);
4641c1e45d1SHans Verkuil }
4651c1e45d1SHans Verkuil 
cx18_g_pixelaspect(struct file * file,void * fh,int type,struct v4l2_fract * f)4665200ab6aSHans Verkuil static int cx18_g_pixelaspect(struct file *file, void *fh,
4675200ab6aSHans Verkuil 			      int type, struct v4l2_fract *f)
4683b6fe58fSAndy Walls {
4690b5f265aSHans Verkuil 	struct cx18 *cx = fh2id(fh)->cx;
4701c1e45d1SHans Verkuil 
4715200ab6aSHans Verkuil 	if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
4721c1e45d1SHans Verkuil 		return -EINVAL;
4735200ab6aSHans Verkuil 
4745200ab6aSHans Verkuil 	f->numerator = cx->is_50hz ? 54 : 11;
4755200ab6aSHans Verkuil 	f->denominator = cx->is_50hz ? 59 : 10;
4761c1e45d1SHans Verkuil 	return 0;
4771c1e45d1SHans Verkuil }
4781c1e45d1SHans Verkuil 
cx18_g_selection(struct file * file,void * fh,struct v4l2_selection * sel)47955cda4abSHans Verkuil static int cx18_g_selection(struct file *file, void *fh,
48055cda4abSHans Verkuil 			    struct v4l2_selection *sel)
4813b6fe58fSAndy Walls {
4820b5f265aSHans Verkuil 	struct cx18 *cx = fh2id(fh)->cx;
4833b6fe58fSAndy Walls 
48455cda4abSHans Verkuil 	if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
4851c1e45d1SHans Verkuil 		return -EINVAL;
48655cda4abSHans Verkuil 	switch (sel->target) {
48755cda4abSHans Verkuil 	case V4L2_SEL_TGT_CROP_BOUNDS:
48855cda4abSHans Verkuil 	case V4L2_SEL_TGT_CROP_DEFAULT:
48955cda4abSHans Verkuil 		sel->r.top = sel->r.left = 0;
49055cda4abSHans Verkuil 		sel->r.width = 720;
49155cda4abSHans Verkuil 		sel->r.height = cx->is_50hz ? 576 : 480;
49255cda4abSHans Verkuil 		break;
49355cda4abSHans Verkuil 	default:
494fa3e7036SAndy Walls 		return -EINVAL;
4951c1e45d1SHans Verkuil 	}
49655cda4abSHans Verkuil 	return 0;
49755cda4abSHans Verkuil }
4981c1e45d1SHans Verkuil 
cx18_enum_fmt_vid_cap(struct file * file,void * fh,struct v4l2_fmtdesc * fmt)4993b6fe58fSAndy Walls static int cx18_enum_fmt_vid_cap(struct file *file, void *fh,
5003b6fe58fSAndy Walls 					struct v4l2_fmtdesc *fmt)
5013b6fe58fSAndy Walls {
502651640f6SHans Verkuil 	struct cx18_open_id *id = fh2id(fh);
5031bf5842fSSimon Farnsworth 
504651640f6SHans Verkuil 	if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
505651640f6SHans Verkuil 		if (fmt->index >= ARRAY_SIZE(cx18_formats_yuv))
5061c1e45d1SHans Verkuil 			return -EINVAL;
507651640f6SHans Verkuil 		*fmt = cx18_formats_yuv[fmt->index];
508651640f6SHans Verkuil 		return 0;
509651640f6SHans Verkuil 	}
510651640f6SHans Verkuil 	if (fmt->index)
511651640f6SHans Verkuil 		return -EINVAL;
512651640f6SHans Verkuil 	*fmt = cx18_formats_mpeg[0];
5131c1e45d1SHans Verkuil 	return 0;
5141c1e45d1SHans Verkuil }
5151c1e45d1SHans Verkuil 
cx18_g_input(struct file * file,void * fh,unsigned int * i)5163b6fe58fSAndy Walls static int cx18_g_input(struct file *file, void *fh, unsigned int *i)
5173b6fe58fSAndy Walls {
5180b5f265aSHans Verkuil 	struct cx18 *cx = fh2id(fh)->cx;
5193b6fe58fSAndy Walls 
5203b6fe58fSAndy Walls 	*i = cx->active_input;
5213b6fe58fSAndy Walls 	return 0;
5221c1e45d1SHans Verkuil }
5231c1e45d1SHans Verkuil 
cx18_s_input(struct file * file,void * fh,unsigned int inp)5243b6fe58fSAndy Walls int cx18_s_input(struct file *file, void *fh, unsigned int inp)
5253b6fe58fSAndy Walls {
5260b5f265aSHans Verkuil 	struct cx18_open_id *id = fh2id(fh);
5273b6fe58fSAndy Walls 	struct cx18 *cx = id->cx;
5283a29a4f1SHans Verkuil 	v4l2_std_id std = V4L2_STD_ALL;
5293a29a4f1SHans Verkuil 	const struct cx18_card_video_input *card_input =
5303a29a4f1SHans Verkuil 				cx->card->video_inputs + inp;
5313b6fe58fSAndy Walls 
532de81c3c3SRoel Kluin 	if (inp >= cx->nof_inputs)
5331c1e45d1SHans Verkuil 		return -EINVAL;
5341c1e45d1SHans Verkuil 
5351c1e45d1SHans Verkuil 	if (inp == cx->active_input) {
5361c1e45d1SHans Verkuil 		CX18_DEBUG_INFO("Input unchanged\n");
5373b6fe58fSAndy Walls 		return 0;
5381c1e45d1SHans Verkuil 	}
5393b6fe58fSAndy Walls 
5401c1e45d1SHans Verkuil 	CX18_DEBUG_INFO("Changing input from %d to %d\n",
5411c1e45d1SHans Verkuil 			cx->active_input, inp);
5421c1e45d1SHans Verkuil 
5431c1e45d1SHans Verkuil 	cx->active_input = inp;
5443b6fe58fSAndy Walls 	/* Set the audio input to whatever is appropriate for the input type. */
5451c1e45d1SHans Verkuil 	cx->audio_input = cx->card->video_inputs[inp].audio_index;
5463a29a4f1SHans Verkuil 	if (card_input->video_type == V4L2_INPUT_TYPE_TUNER)
5473a29a4f1SHans Verkuil 		std = cx->tuner_std;
5483a29a4f1SHans Verkuil 	cx->streams[CX18_ENC_STREAM_TYPE_MPG].video_dev.tvnorms = std;
5493a29a4f1SHans Verkuil 	cx->streams[CX18_ENC_STREAM_TYPE_YUV].video_dev.tvnorms = std;
5503a29a4f1SHans Verkuil 	cx->streams[CX18_ENC_STREAM_TYPE_VBI].video_dev.tvnorms = std;
5511c1e45d1SHans Verkuil 
5521c1e45d1SHans Verkuil 	/* prevent others from messing with the streams until
5531c1e45d1SHans Verkuil 	   we're finished changing inputs. */
5541c1e45d1SHans Verkuil 	cx18_mute(cx);
5551c1e45d1SHans Verkuil 	cx18_video_set_io(cx);
5561c1e45d1SHans Verkuil 	cx18_audio_set_io(cx);
5571c1e45d1SHans Verkuil 	cx18_unmute(cx);
5583b6fe58fSAndy Walls 	return 0;
5591c1e45d1SHans Verkuil }
5601c1e45d1SHans Verkuil 
cx18_g_frequency(struct file * file,void * fh,struct v4l2_frequency * vf)5613b6fe58fSAndy Walls static int cx18_g_frequency(struct file *file, void *fh,
5623b6fe58fSAndy Walls 				struct v4l2_frequency *vf)
5633b6fe58fSAndy Walls {
5640b5f265aSHans Verkuil 	struct cx18 *cx = fh2id(fh)->cx;
5653b6fe58fSAndy Walls 
5661c1e45d1SHans Verkuil 	if (vf->tuner != 0)
5671c1e45d1SHans Verkuil 		return -EINVAL;
5683b6fe58fSAndy Walls 
569ff2a2001SAndy Walls 	cx18_call_all(cx, tuner, g_frequency, vf);
5703b6fe58fSAndy Walls 	return 0;
5711c1e45d1SHans Verkuil }
5721c1e45d1SHans Verkuil 
cx18_s_frequency(struct file * file,void * fh,const struct v4l2_frequency * vf)573b530a447SHans Verkuil int cx18_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf)
5743b6fe58fSAndy Walls {
5750b5f265aSHans Verkuil 	struct cx18_open_id *id = fh2id(fh);
5763b6fe58fSAndy Walls 	struct cx18 *cx = id->cx;
5773b6fe58fSAndy Walls 
5783b6fe58fSAndy Walls 	if (vf->tuner != 0)
5791c1e45d1SHans Verkuil 		return -EINVAL;
5801c1e45d1SHans Verkuil 
5811c1e45d1SHans Verkuil 	cx18_mute(cx);
5823b6fe58fSAndy Walls 	CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency);
583ff2a2001SAndy Walls 	cx18_call_all(cx, tuner, s_frequency, vf);
5841c1e45d1SHans Verkuil 	cx18_unmute(cx);
5853b6fe58fSAndy Walls 	return 0;
5861c1e45d1SHans Verkuil }
5871c1e45d1SHans Verkuil 
cx18_g_std(struct file * file,void * fh,v4l2_std_id * std)5883b6fe58fSAndy Walls static int cx18_g_std(struct file *file, void *fh, v4l2_std_id *std)
5893b6fe58fSAndy Walls {
5900b5f265aSHans Verkuil 	struct cx18 *cx = fh2id(fh)->cx;
5911c1e45d1SHans Verkuil 
5923b6fe58fSAndy Walls 	*std = cx->std;
5933b6fe58fSAndy Walls 	return 0;
5943b6fe58fSAndy Walls }
5953b6fe58fSAndy Walls 
cx18_s_std(struct file * file,void * fh,v4l2_std_id std)596314527acSHans Verkuil int cx18_s_std(struct file *file, void *fh, v4l2_std_id std)
5973b6fe58fSAndy Walls {
5980b5f265aSHans Verkuil 	struct cx18_open_id *id = fh2id(fh);
5993b6fe58fSAndy Walls 	struct cx18 *cx = id->cx;
6003b6fe58fSAndy Walls 
601314527acSHans Verkuil 	if ((std & V4L2_STD_ALL) == 0)
6021c1e45d1SHans Verkuil 		return -EINVAL;
6031c1e45d1SHans Verkuil 
604314527acSHans Verkuil 	if (std == cx->std)
6053b6fe58fSAndy Walls 		return 0;
6061c1e45d1SHans Verkuil 
6071c1e45d1SHans Verkuil 	if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ||
60831554ae5SHans Verkuil 	    atomic_read(&cx->ana_capturing) > 0) {
6091c1e45d1SHans Verkuil 		/* Switching standard would turn off the radio or mess
6101c1e45d1SHans Verkuil 		   with already running streams, prevent that by
6111c1e45d1SHans Verkuil 		   returning EBUSY. */
6121c1e45d1SHans Verkuil 		return -EBUSY;
6131c1e45d1SHans Verkuil 	}
6141c1e45d1SHans Verkuil 
615314527acSHans Verkuil 	cx->std = std;
616314527acSHans Verkuil 	cx->is_60hz = (std & V4L2_STD_525_60) ? 1 : 0;
617a75b9be1SHans Verkuil 	cx->is_50hz = !cx->is_60hz;
618a75b9be1SHans Verkuil 	cx2341x_handler_set_50hz(&cx->cxhdl, cx->is_50hz);
619a75b9be1SHans Verkuil 	cx->cxhdl.width = 720;
620a75b9be1SHans Verkuil 	cx->cxhdl.height = cx->is_50hz ? 576 : 480;
62113de5a51SHans Verkuil 	/*
62213de5a51SHans Verkuil 	 * HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
62313de5a51SHans Verkuil 	 * UYUV YUV size is (Y=(h*720) + UV=(h*(720)))
62413de5a51SHans Verkuil 	 */
62513de5a51SHans Verkuil 	if (cx->streams[CX18_ENC_STREAM_TYPE_YUV].pixelformat == V4L2_PIX_FMT_NV12_16L16) {
62613de5a51SHans Verkuil 		cx->streams[CX18_ENC_STREAM_TYPE_YUV].vb_bytes_per_frame =
62713de5a51SHans Verkuil 			cx->cxhdl.height * 720 * 3 / 2;
62813de5a51SHans Verkuil 		cx->streams[CX18_ENC_STREAM_TYPE_YUV].vb_bytes_per_line = 720;
62913de5a51SHans Verkuil 	} else {
63013de5a51SHans Verkuil 		cx->streams[CX18_ENC_STREAM_TYPE_YUV].vb_bytes_per_frame =
63113de5a51SHans Verkuil 			cx->cxhdl.height * 720 * 2;
63213de5a51SHans Verkuil 		cx->streams[CX18_ENC_STREAM_TYPE_YUV].vb_bytes_per_line = 1440;
63313de5a51SHans Verkuil 	}
6341c1e45d1SHans Verkuil 	cx->vbi.count = cx->is_50hz ? 18 : 12;
6351c1e45d1SHans Verkuil 	cx->vbi.start[0] = cx->is_50hz ? 6 : 10;
6361c1e45d1SHans Verkuil 	cx->vbi.start[1] = cx->is_50hz ? 318 : 273;
6373b6fe58fSAndy Walls 	CX18_DEBUG_INFO("Switching standard to %llx.\n",
6383b6fe58fSAndy Walls 			(unsigned long long) cx->std);
6391c1e45d1SHans Verkuil 
6401c1e45d1SHans Verkuil 	/* Tuner */
6418774bed9SLaurent Pinchart 	cx18_call_all(cx, video, s_std, cx->std);
6423b6fe58fSAndy Walls 	return 0;
6431c1e45d1SHans Verkuil }
6441c1e45d1SHans Verkuil 
cx18_s_tuner(struct file * file,void * fh,const struct v4l2_tuner * vt)6452f73c7c5SHans Verkuil static int cx18_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt)
6463b6fe58fSAndy Walls {
6470b5f265aSHans Verkuil 	struct cx18_open_id *id = fh2id(fh);
6483b6fe58fSAndy Walls 	struct cx18 *cx = id->cx;
6493b6fe58fSAndy Walls 
6501c1e45d1SHans Verkuil 	if (vt->index != 0)
6511c1e45d1SHans Verkuil 		return -EINVAL;
6521c1e45d1SHans Verkuil 
653ff2a2001SAndy Walls 	cx18_call_all(cx, tuner, s_tuner, vt);
6543b6fe58fSAndy Walls 	return 0;
6551c1e45d1SHans Verkuil }
6561c1e45d1SHans Verkuil 
cx18_g_tuner(struct file * file,void * fh,struct v4l2_tuner * vt)6573b6fe58fSAndy Walls static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
6583b6fe58fSAndy Walls {
6590b5f265aSHans Verkuil 	struct cx18 *cx = fh2id(fh)->cx;
6603b6fe58fSAndy Walls 
6611c1e45d1SHans Verkuil 	if (vt->index != 0)
6621c1e45d1SHans Verkuil 		return -EINVAL;
6631c1e45d1SHans Verkuil 
664ff2a2001SAndy Walls 	cx18_call_all(cx, tuner, g_tuner, vt);
6651c1e45d1SHans Verkuil 
666d118e294SHans Verkuil 	if (vt->type == V4L2_TUNER_RADIO)
667c0decac1SMauro Carvalho Chehab 		strscpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name));
668d118e294SHans Verkuil 	else
669c0decac1SMauro Carvalho Chehab 		strscpy(vt->name, "cx18 TV Tuner", sizeof(vt->name));
6701c1e45d1SHans Verkuil 	return 0;
6711c1e45d1SHans Verkuil }
6723b6fe58fSAndy Walls 
cx18_g_sliced_vbi_cap(struct file * file,void * fh,struct v4l2_sliced_vbi_cap * cap)6733b6fe58fSAndy Walls static int cx18_g_sliced_vbi_cap(struct file *file, void *fh,
6743b6fe58fSAndy Walls 					struct v4l2_sliced_vbi_cap *cap)
6753b6fe58fSAndy Walls {
6760b5f265aSHans Verkuil 	struct cx18 *cx = fh2id(fh)->cx;
677302df970SAndy Walls 	int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
678302df970SAndy Walls 	int f, l;
679302df970SAndy Walls 
680c1994084SAndy Walls 	if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
681c1994084SAndy Walls 		return -EINVAL;
682c1994084SAndy Walls 
683c1994084SAndy Walls 	cap->service_set = 0;
684302df970SAndy Walls 	for (f = 0; f < 2; f++) {
685302df970SAndy Walls 		for (l = 0; l < 24; l++) {
686c1994084SAndy Walls 			if (valid_service_line(f, l, cx->is_50hz)) {
687c1994084SAndy Walls 				/*
688c1994084SAndy Walls 				 * We can find all v4l2 supported vbi services
689c1994084SAndy Walls 				 * for the standard, on a valid line for the std
690c1994084SAndy Walls 				 */
691302df970SAndy Walls 				cap->service_lines[f][l] = set;
692c1994084SAndy Walls 				cap->service_set |= set;
693c1994084SAndy Walls 			} else
694c1994084SAndy Walls 				cap->service_lines[f][l] = 0;
695302df970SAndy Walls 		}
696302df970SAndy Walls 	}
697c1994084SAndy Walls 	for (f = 0; f < 3; f++)
698c1994084SAndy Walls 		cap->reserved[f] = 0;
699302df970SAndy Walls 	return 0;
700302df970SAndy Walls }
7011c1e45d1SHans Verkuil 
_cx18_process_idx_data(struct cx18_buffer * buf,struct v4l2_enc_idx * idx)70282acdc84SAndy Walls static int _cx18_process_idx_data(struct cx18_buffer *buf,
70382acdc84SAndy Walls 				  struct v4l2_enc_idx *idx)
70482acdc84SAndy Walls {
70582acdc84SAndy Walls 	int consumed, remaining;
70682acdc84SAndy Walls 	struct v4l2_enc_idx_entry *e_idx;
70782acdc84SAndy Walls 	struct cx18_enc_idx_entry *e_buf;
70882acdc84SAndy Walls 
70982acdc84SAndy Walls 	/* Frame type lookup: 1=I, 2=P, 4=B */
71027dbc2e6SColin Ian King 	static const int mapping[8] = {
71182acdc84SAndy Walls 		-1, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_P,
71282acdc84SAndy Walls 		-1, V4L2_ENC_IDX_FRAME_B, -1, -1, -1
71382acdc84SAndy Walls 	};
71482acdc84SAndy Walls 
71582acdc84SAndy Walls 	/*
71682acdc84SAndy Walls 	 * Assumption here is that a buf holds an integral number of
71782acdc84SAndy Walls 	 * struct cx18_enc_idx_entry objects and is properly aligned.
71882acdc84SAndy Walls 	 * This is enforced by the module options on IDX buffer sizes.
71982acdc84SAndy Walls 	 */
72082acdc84SAndy Walls 	remaining = buf->bytesused - buf->readpos;
72182acdc84SAndy Walls 	consumed = 0;
72282acdc84SAndy Walls 	e_idx = &idx->entry[idx->entries];
72382acdc84SAndy Walls 	e_buf = (struct cx18_enc_idx_entry *) &buf->buf[buf->readpos];
72482acdc84SAndy Walls 
72582acdc84SAndy Walls 	while (remaining >= sizeof(struct cx18_enc_idx_entry) &&
72682acdc84SAndy Walls 	       idx->entries < V4L2_ENC_IDX_ENTRIES) {
72782acdc84SAndy Walls 
72882acdc84SAndy Walls 		e_idx->offset = (((u64) le32_to_cpu(e_buf->offset_high)) << 32)
72982acdc84SAndy Walls 				| le32_to_cpu(e_buf->offset_low);
73082acdc84SAndy Walls 
73182acdc84SAndy Walls 		e_idx->pts = (((u64) (le32_to_cpu(e_buf->pts_high) & 1)) << 32)
73282acdc84SAndy Walls 			     | le32_to_cpu(e_buf->pts_low);
73382acdc84SAndy Walls 
73482acdc84SAndy Walls 		e_idx->length = le32_to_cpu(e_buf->length);
73582acdc84SAndy Walls 
73682acdc84SAndy Walls 		e_idx->flags = mapping[le32_to_cpu(e_buf->flags) & 0x7];
73782acdc84SAndy Walls 
73882acdc84SAndy Walls 		e_idx->reserved[0] = 0;
73982acdc84SAndy Walls 		e_idx->reserved[1] = 0;
74082acdc84SAndy Walls 
74182acdc84SAndy Walls 		idx->entries++;
74282acdc84SAndy Walls 		e_idx = &idx->entry[idx->entries];
74382acdc84SAndy Walls 		e_buf++;
74482acdc84SAndy Walls 
74582acdc84SAndy Walls 		remaining -= sizeof(struct cx18_enc_idx_entry);
74682acdc84SAndy Walls 		consumed += sizeof(struct cx18_enc_idx_entry);
74782acdc84SAndy Walls 	}
74882acdc84SAndy Walls 
74982acdc84SAndy Walls 	/* Swallow any partial entries at the end, if there are any */
75082acdc84SAndy Walls 	if (remaining > 0 && remaining < sizeof(struct cx18_enc_idx_entry))
75182acdc84SAndy Walls 		consumed += remaining;
75282acdc84SAndy Walls 
75382acdc84SAndy Walls 	buf->readpos += consumed;
75482acdc84SAndy Walls 	return consumed;
75582acdc84SAndy Walls }
75682acdc84SAndy Walls 
cx18_process_idx_data(struct cx18_stream * s,struct cx18_mdl * mdl,struct v4l2_enc_idx * idx)75782acdc84SAndy Walls static int cx18_process_idx_data(struct cx18_stream *s, struct cx18_mdl *mdl,
75882acdc84SAndy Walls 				 struct v4l2_enc_idx *idx)
75982acdc84SAndy Walls {
76082acdc84SAndy Walls 	if (s->type != CX18_ENC_STREAM_TYPE_IDX)
76182acdc84SAndy Walls 		return -EINVAL;
76282acdc84SAndy Walls 
76382acdc84SAndy Walls 	if (mdl->curr_buf == NULL)
76482acdc84SAndy Walls 		mdl->curr_buf = list_first_entry(&mdl->buf_list,
76582acdc84SAndy Walls 						 struct cx18_buffer, list);
76682acdc84SAndy Walls 
767*d8fbfcabSAndy Shevchenko 	if (list_entry_is_head(mdl->curr_buf, &mdl->buf_list, list)) {
76882acdc84SAndy Walls 		/*
76982acdc84SAndy Walls 		 * For some reason we've exhausted the buffers, but the MDL
77082acdc84SAndy Walls 		 * object still said some data was unread.
77182acdc84SAndy Walls 		 * Fix that and bail out.
77282acdc84SAndy Walls 		 */
77382acdc84SAndy Walls 		mdl->readpos = mdl->bytesused;
77482acdc84SAndy Walls 		return 0;
77582acdc84SAndy Walls 	}
77682acdc84SAndy Walls 
77782acdc84SAndy Walls 	list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) {
77882acdc84SAndy Walls 
77982acdc84SAndy Walls 		/* Skip any empty buffers in the MDL */
78082acdc84SAndy Walls 		if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused)
78182acdc84SAndy Walls 			continue;
78282acdc84SAndy Walls 
78382acdc84SAndy Walls 		mdl->readpos += _cx18_process_idx_data(mdl->curr_buf, idx);
78482acdc84SAndy Walls 
78582acdc84SAndy Walls 		/* exit when MDL drained or request satisfied */
78682acdc84SAndy Walls 		if (idx->entries >= V4L2_ENC_IDX_ENTRIES ||
78782acdc84SAndy Walls 		    mdl->curr_buf->readpos < mdl->curr_buf->bytesused ||
78882acdc84SAndy Walls 		    mdl->readpos >= mdl->bytesused)
78982acdc84SAndy Walls 			break;
79082acdc84SAndy Walls 	}
79182acdc84SAndy Walls 	return 0;
79282acdc84SAndy Walls }
79382acdc84SAndy Walls 
cx18_g_enc_index(struct file * file,void * fh,struct v4l2_enc_idx * idx)7943b6fe58fSAndy Walls static int cx18_g_enc_index(struct file *file, void *fh,
7953b6fe58fSAndy Walls 				struct v4l2_enc_idx *idx)
7963b6fe58fSAndy Walls {
7970b5f265aSHans Verkuil 	struct cx18 *cx = fh2id(fh)->cx;
79882acdc84SAndy Walls 	struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
79982acdc84SAndy Walls 	s32 tmp;
80082acdc84SAndy Walls 	struct cx18_mdl *mdl;
80182acdc84SAndy Walls 
80282acdc84SAndy Walls 	if (!cx18_stream_enabled(s)) /* Module options inhibited IDX stream */
8033b6fe58fSAndy Walls 		return -EINVAL;
80482acdc84SAndy Walls 
80582acdc84SAndy Walls 	/* Compute the best case number of entries we can buffer */
80682acdc84SAndy Walls 	tmp = s->buffers -
80782acdc84SAndy Walls 			  s->bufs_per_mdl * CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN;
80882acdc84SAndy Walls 	if (tmp <= 0)
80982acdc84SAndy Walls 		tmp = 1;
81082acdc84SAndy Walls 	tmp = tmp * s->buf_size / sizeof(struct cx18_enc_idx_entry);
81182acdc84SAndy Walls 
81282acdc84SAndy Walls 	/* Fill out the header of the return structure */
81382acdc84SAndy Walls 	idx->entries = 0;
81482acdc84SAndy Walls 	idx->entries_cap = tmp;
81582acdc84SAndy Walls 	memset(idx->reserved, 0, sizeof(idx->reserved));
81682acdc84SAndy Walls 
81782acdc84SAndy Walls 	/* Pull IDX MDLs and buffers from q_full and populate the entries */
81882acdc84SAndy Walls 	do {
81982acdc84SAndy Walls 		mdl = cx18_dequeue(s, &s->q_full);
82082acdc84SAndy Walls 		if (mdl == NULL) /* No more IDX data right now */
82182acdc84SAndy Walls 			break;
82282acdc84SAndy Walls 
82382acdc84SAndy Walls 		/* Extract the Index entry data from the MDL and buffers */
82482acdc84SAndy Walls 		cx18_process_idx_data(s, mdl, idx);
82582acdc84SAndy Walls 		if (mdl->readpos < mdl->bytesused) {
82682acdc84SAndy Walls 			/* We finished with data remaining, push the MDL back */
82782acdc84SAndy Walls 			cx18_push(s, mdl, &s->q_full);
82882acdc84SAndy Walls 			break;
82982acdc84SAndy Walls 		}
83082acdc84SAndy Walls 
83182acdc84SAndy Walls 		/* We drained this MDL, schedule it to go to the firmware */
83282acdc84SAndy Walls 		cx18_enqueue(s, mdl, &s->q_free);
83382acdc84SAndy Walls 
83482acdc84SAndy Walls 	} while (idx->entries < V4L2_ENC_IDX_ENTRIES);
83582acdc84SAndy Walls 
83682acdc84SAndy Walls 	/* Tell the work handler to send free IDX MDLs to the firmware */
83782acdc84SAndy Walls 	cx18_stream_load_fw_queue(s);
83882acdc84SAndy Walls 	return 0;
8393b6fe58fSAndy Walls }
8403b6fe58fSAndy Walls 
cx18_encoder_cmd(struct file * file,void * fh,struct v4l2_encoder_cmd * enc)8413b6fe58fSAndy Walls static int cx18_encoder_cmd(struct file *file, void *fh,
8423b6fe58fSAndy Walls 				struct v4l2_encoder_cmd *enc)
8433b6fe58fSAndy Walls {
8440b5f265aSHans Verkuil 	struct cx18_open_id *id = fh2id(fh);
8453b6fe58fSAndy Walls 	struct cx18 *cx = id->cx;
846d3c5e707SAndy Walls 	u32 h;
8473b6fe58fSAndy Walls 
8481c1e45d1SHans Verkuil 	switch (enc->cmd) {
8491c1e45d1SHans Verkuil 	case V4L2_ENC_CMD_START:
8503b6fe58fSAndy Walls 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
8511c1e45d1SHans Verkuil 		enc->flags = 0;
8521c1e45d1SHans Verkuil 		return cx18_start_capture(id);
8531c1e45d1SHans Verkuil 
8541c1e45d1SHans Verkuil 	case V4L2_ENC_CMD_STOP:
8553b6fe58fSAndy Walls 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
8561c1e45d1SHans Verkuil 		enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
857643e8350SHans Verkuil 		cx18_stop_capture(&cx->streams[id->type],
8583b6fe58fSAndy Walls 				  enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);
8593b6fe58fSAndy Walls 		break;
8601c1e45d1SHans Verkuil 
8611c1e45d1SHans Verkuil 	case V4L2_ENC_CMD_PAUSE:
8623b6fe58fSAndy Walls 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
8631c1e45d1SHans Verkuil 		enc->flags = 0;
86431554ae5SHans Verkuil 		if (!atomic_read(&cx->ana_capturing))
8651c1e45d1SHans Verkuil 			return -EPERM;
8661c1e45d1SHans Verkuil 		if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
8671c1e45d1SHans Verkuil 			return 0;
868d3c5e707SAndy Walls 		h = cx18_find_handle(cx);
869d3c5e707SAndy Walls 		if (h == CX18_INVALID_TASK_HANDLE) {
8706beb1388SMauro Carvalho Chehab 			CX18_ERR("Can't find valid task handle for V4L2_ENC_CMD_PAUSE\n");
871d3c5e707SAndy Walls 			return -EBADFD;
872d3c5e707SAndy Walls 		}
8731c1e45d1SHans Verkuil 		cx18_mute(cx);
874d3c5e707SAndy Walls 		cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, h);
8751c1e45d1SHans Verkuil 		break;
8761c1e45d1SHans Verkuil 
8771c1e45d1SHans Verkuil 	case V4L2_ENC_CMD_RESUME:
8783b6fe58fSAndy Walls 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
8791c1e45d1SHans Verkuil 		enc->flags = 0;
88031554ae5SHans Verkuil 		if (!atomic_read(&cx->ana_capturing))
8811c1e45d1SHans Verkuil 			return -EPERM;
8821c1e45d1SHans Verkuil 		if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
8831c1e45d1SHans Verkuil 			return 0;
884d3c5e707SAndy Walls 		h = cx18_find_handle(cx);
885d3c5e707SAndy Walls 		if (h == CX18_INVALID_TASK_HANDLE) {
8866beb1388SMauro Carvalho Chehab 			CX18_ERR("Can't find valid task handle for V4L2_ENC_CMD_RESUME\n");
887d3c5e707SAndy Walls 			return -EBADFD;
888d3c5e707SAndy Walls 		}
889d3c5e707SAndy Walls 		cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, h);
8901c1e45d1SHans Verkuil 		cx18_unmute(cx);
8911c1e45d1SHans Verkuil 		break;
8923b6fe58fSAndy Walls 
8931c1e45d1SHans Verkuil 	default:
8943b6fe58fSAndy Walls 		CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
8951c1e45d1SHans Verkuil 		return -EINVAL;
8961c1e45d1SHans Verkuil 	}
8973b6fe58fSAndy Walls 	return 0;
8981c1e45d1SHans Verkuil }
8991c1e45d1SHans Verkuil 
cx18_try_encoder_cmd(struct file * file,void * fh,struct v4l2_encoder_cmd * enc)9003b6fe58fSAndy Walls static int cx18_try_encoder_cmd(struct file *file, void *fh,
9013b6fe58fSAndy Walls 				struct v4l2_encoder_cmd *enc)
9021c1e45d1SHans Verkuil {
9030b5f265aSHans Verkuil 	struct cx18 *cx = fh2id(fh)->cx;
9043b6fe58fSAndy Walls 
9053b6fe58fSAndy Walls 	switch (enc->cmd) {
9063b6fe58fSAndy Walls 	case V4L2_ENC_CMD_START:
9073b6fe58fSAndy Walls 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
9083b6fe58fSAndy Walls 		enc->flags = 0;
9093b6fe58fSAndy Walls 		break;
9103b6fe58fSAndy Walls 
9113b6fe58fSAndy Walls 	case V4L2_ENC_CMD_STOP:
9123b6fe58fSAndy Walls 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
9133b6fe58fSAndy Walls 		enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
9143b6fe58fSAndy Walls 		break;
9153b6fe58fSAndy Walls 
9163b6fe58fSAndy Walls 	case V4L2_ENC_CMD_PAUSE:
9173b6fe58fSAndy Walls 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
9183b6fe58fSAndy Walls 		enc->flags = 0;
9193b6fe58fSAndy Walls 		break;
9203b6fe58fSAndy Walls 
9213b6fe58fSAndy Walls 	case V4L2_ENC_CMD_RESUME:
9223b6fe58fSAndy Walls 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
9233b6fe58fSAndy Walls 		enc->flags = 0;
9243b6fe58fSAndy Walls 		break;
9253b6fe58fSAndy Walls 
9263b6fe58fSAndy Walls 	default:
9273b6fe58fSAndy Walls 		CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
9283b6fe58fSAndy Walls 		return -EINVAL;
9293b6fe58fSAndy Walls 	}
9303b6fe58fSAndy Walls 	return 0;
9313b6fe58fSAndy Walls }
9323b6fe58fSAndy Walls 
cx18_log_status(struct file * file,void * fh)9333b6fe58fSAndy Walls static int cx18_log_status(struct file *file, void *fh)
9343b6fe58fSAndy Walls {
9350b5f265aSHans Verkuil 	struct cx18 *cx = fh2id(fh)->cx;
9361c1e45d1SHans Verkuil 	struct v4l2_input vidin;
9371c1e45d1SHans Verkuil 	struct v4l2_audio audin;
9381c1e45d1SHans Verkuil 	int i;
9391c1e45d1SHans Verkuil 
940e0f28b6aSAndy Walls 	CX18_INFO("Version: %s  Card: %s\n", CX18_VERSION, cx->card_name);
9411c1e45d1SHans Verkuil 	if (cx->hw_flags & CX18_HW_TVEEPROM) {
9421c1e45d1SHans Verkuil 		struct tveeprom tv;
9431c1e45d1SHans Verkuil 
9441c1e45d1SHans Verkuil 		cx18_read_eeprom(cx, &tv);
9451c1e45d1SHans Verkuil 	}
946ff2a2001SAndy Walls 	cx18_call_all(cx, core, log_status);
9471c1e45d1SHans Verkuil 	cx18_get_input(cx, cx->active_input, &vidin);
9481c1e45d1SHans Verkuil 	cx18_get_audio_input(cx, cx->audio_input, &audin);
9491c1e45d1SHans Verkuil 	CX18_INFO("Video Input: %s\n", vidin.name);
9501c1e45d1SHans Verkuil 	CX18_INFO("Audio Input: %s\n", audin.name);
9518abdd00dSAndy Walls 	mutex_lock(&cx->gpio_lock);
9523d66c405SHans Verkuil 	CX18_INFO("GPIO:  direction 0x%08x, value 0x%08x\n",
9533d66c405SHans Verkuil 		cx->gpio_dir, cx->gpio_val);
9548abdd00dSAndy Walls 	mutex_unlock(&cx->gpio_lock);
9551c1e45d1SHans Verkuil 	CX18_INFO("Tuner: %s\n",
9563b6fe58fSAndy Walls 		test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ?  "Radio" : "TV");
957a75b9be1SHans Verkuil 	v4l2_ctrl_handler_log_status(&cx->cxhdl.hdl, cx->v4l2_dev.name);
9581c1e45d1SHans Verkuil 	CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags);
9591c1e45d1SHans Verkuil 	for (i = 0; i < CX18_MAX_STREAMS; i++) {
9601c1e45d1SHans Verkuil 		struct cx18_stream *s = &cx->streams[i];
9611c1e45d1SHans Verkuil 
96208569d64SHans Verkuil 		if (s->video_dev.v4l2_dev == NULL || s->buffers == 0)
9631c1e45d1SHans Verkuil 			continue;
9641c1e45d1SHans Verkuil 		CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
9651c1e45d1SHans Verkuil 			  s->name, s->s_flags,
96652fcb3ecSAndy Walls 			  atomic_read(&s->q_full.depth) * s->bufs_per_mdl * 100
96752fcb3ecSAndy Walls 			   / s->buffers,
9681c1e45d1SHans Verkuil 			  (s->buffers * s->buf_size) / 1024, s->buffers);
9691c1e45d1SHans Verkuil 	}
9701c1e45d1SHans Verkuil 	CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
9711c1e45d1SHans Verkuil 			(long long)cx->mpg_data_received,
9721c1e45d1SHans Verkuil 			(long long)cx->vbi_data_inserted);
9731c1e45d1SHans Verkuil 	return 0;
9741c1e45d1SHans Verkuil }
9751c1e45d1SHans Verkuil 
cx18_default(struct file * file,void * fh,bool valid_prio,unsigned int cmd,void * arg)97699cd47bcSHans Verkuil static long cx18_default(struct file *file, void *fh, bool valid_prio,
9776d43be77SMauro Carvalho Chehab 			 unsigned int cmd, void *arg)
9781c1e45d1SHans Verkuil {
9790b5f265aSHans Verkuil 	struct cx18 *cx = fh2id(fh)->cx;
9801c1e45d1SHans Verkuil 
9811c1e45d1SHans Verkuil 	switch (cmd) {
98202fa272fSAndy Walls 	case VIDIOC_INT_RESET: {
98302fa272fSAndy Walls 		u32 val = *(u32 *)arg;
98402fa272fSAndy Walls 
98502fa272fSAndy Walls 		if ((val == 0) || (val & 0x01))
986eefe1010SAndy Walls 			cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL, core, reset,
987eefe1010SAndy Walls 				     (u32) CX18_GPIO_RESET_Z8F0811);
98802fa272fSAndy Walls 		break;
98902fa272fSAndy Walls 	}
99002fa272fSAndy Walls 
9911c1e45d1SHans Verkuil 	default:
992d1c754a9SHans Verkuil 		return -ENOTTY;
9931c1e45d1SHans Verkuil 	}
9941c1e45d1SHans Verkuil 	return 0;
9951c1e45d1SHans Verkuil }
9961c1e45d1SHans Verkuil 
997a399810cSHans Verkuil static const struct v4l2_ioctl_ops cx18_ioctl_ops = {
998a399810cSHans Verkuil 	.vidioc_querycap                = cx18_querycap,
999a399810cSHans Verkuil 	.vidioc_s_audio                 = cx18_s_audio,
1000a399810cSHans Verkuil 	.vidioc_g_audio                 = cx18_g_audio,
1001a399810cSHans Verkuil 	.vidioc_enumaudio               = cx18_enumaudio,
1002a399810cSHans Verkuil 	.vidioc_enum_input              = cx18_enum_input,
10035200ab6aSHans Verkuil 	.vidioc_g_pixelaspect           = cx18_g_pixelaspect,
100455cda4abSHans Verkuil 	.vidioc_g_selection             = cx18_g_selection,
1005a399810cSHans Verkuil 	.vidioc_g_input                 = cx18_g_input,
1006a399810cSHans Verkuil 	.vidioc_s_input                 = cx18_s_input,
1007a399810cSHans Verkuil 	.vidioc_g_frequency             = cx18_g_frequency,
1008a399810cSHans Verkuil 	.vidioc_s_frequency             = cx18_s_frequency,
1009a399810cSHans Verkuil 	.vidioc_s_tuner                 = cx18_s_tuner,
1010a399810cSHans Verkuil 	.vidioc_g_tuner                 = cx18_g_tuner,
1011a399810cSHans Verkuil 	.vidioc_g_enc_index             = cx18_g_enc_index,
1012a399810cSHans Verkuil 	.vidioc_g_std                   = cx18_g_std,
1013a399810cSHans Verkuil 	.vidioc_s_std                   = cx18_s_std,
1014a399810cSHans Verkuil 	.vidioc_log_status              = cx18_log_status,
1015a399810cSHans Verkuil 	.vidioc_enum_fmt_vid_cap        = cx18_enum_fmt_vid_cap,
1016a399810cSHans Verkuil 	.vidioc_encoder_cmd             = cx18_encoder_cmd,
1017a399810cSHans Verkuil 	.vidioc_try_encoder_cmd         = cx18_try_encoder_cmd,
1018a399810cSHans Verkuil 	.vidioc_g_fmt_vid_cap           = cx18_g_fmt_vid_cap,
1019a399810cSHans Verkuil 	.vidioc_g_fmt_vbi_cap           = cx18_g_fmt_vbi_cap,
1020a399810cSHans Verkuil 	.vidioc_g_fmt_sliced_vbi_cap    = cx18_g_fmt_sliced_vbi_cap,
1021a399810cSHans Verkuil 	.vidioc_s_fmt_vid_cap           = cx18_s_fmt_vid_cap,
1022a399810cSHans Verkuil 	.vidioc_s_fmt_vbi_cap           = cx18_s_fmt_vbi_cap,
1023a399810cSHans Verkuil 	.vidioc_s_fmt_sliced_vbi_cap    = cx18_s_fmt_sliced_vbi_cap,
1024a399810cSHans Verkuil 	.vidioc_try_fmt_vid_cap         = cx18_try_fmt_vid_cap,
1025a399810cSHans Verkuil 	.vidioc_try_fmt_vbi_cap         = cx18_try_fmt_vbi_cap,
1026a399810cSHans Verkuil 	.vidioc_try_fmt_sliced_vbi_cap  = cx18_try_fmt_sliced_vbi_cap,
1027a399810cSHans Verkuil 	.vidioc_g_sliced_vbi_cap        = cx18_g_sliced_vbi_cap,
1028a399810cSHans Verkuil #ifdef CONFIG_VIDEO_ADV_DEBUG
1029a399810cSHans Verkuil 	.vidioc_g_register              = cx18_g_register,
1030a399810cSHans Verkuil 	.vidioc_s_register              = cx18_s_register,
1031a399810cSHans Verkuil #endif
1032a399810cSHans Verkuil 	.vidioc_default                 = cx18_default,
1033643e8350SHans Verkuil 
1034643e8350SHans Verkuil 	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
1035643e8350SHans Verkuil 	.vidioc_querybuf		= vb2_ioctl_querybuf,
1036643e8350SHans Verkuil 	.vidioc_qbuf			= vb2_ioctl_qbuf,
1037643e8350SHans Verkuil 	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
1038643e8350SHans Verkuil 	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
1039643e8350SHans Verkuil 	.vidioc_streamon		= vb2_ioctl_streamon,
1040643e8350SHans Verkuil 	.vidioc_streamoff		= vb2_ioctl_streamoff,
1041643e8350SHans Verkuil 	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
1042eaa80c44SHans Verkuil 	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
1043eaa80c44SHans Verkuil 	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
1044a399810cSHans Verkuil };
1045a399810cSHans Verkuil 
cx18_set_funcs(struct video_device * vdev)10463b6fe58fSAndy Walls void cx18_set_funcs(struct video_device *vdev)
10473b6fe58fSAndy Walls {
1048a399810cSHans Verkuil 	vdev->ioctl_ops = &cx18_ioctl_ops;
10493b6fe58fSAndy Walls }
1050