1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2b285192aSMauro Carvalho Chehab /*
3b285192aSMauro Carvalho Chehab  *  cx18 ioctl system call
4b285192aSMauro Carvalho Chehab  *
5b285192aSMauro Carvalho Chehab  *  Derived from ivtv-ioctl.c
6b285192aSMauro Carvalho Chehab  *
7b285192aSMauro Carvalho Chehab  *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
8b285192aSMauro Carvalho Chehab  *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.net>
9b285192aSMauro Carvalho Chehab  */
10b285192aSMauro Carvalho Chehab 
11b285192aSMauro Carvalho Chehab #include "cx18-driver.h"
12b285192aSMauro Carvalho Chehab #include "cx18-io.h"
13b285192aSMauro Carvalho Chehab #include "cx18-version.h"
14b285192aSMauro Carvalho Chehab #include "cx18-mailbox.h"
15b285192aSMauro Carvalho Chehab #include "cx18-i2c.h"
16b285192aSMauro Carvalho Chehab #include "cx18-queue.h"
17b285192aSMauro Carvalho Chehab #include "cx18-fileops.h"
18b285192aSMauro Carvalho Chehab #include "cx18-vbi.h"
19b285192aSMauro Carvalho Chehab #include "cx18-audio.h"
20b285192aSMauro Carvalho Chehab #include "cx18-video.h"
21b285192aSMauro Carvalho Chehab #include "cx18-streams.h"
22b285192aSMauro Carvalho Chehab #include "cx18-ioctl.h"
23b285192aSMauro Carvalho Chehab #include "cx18-gpio.h"
24b285192aSMauro Carvalho Chehab #include "cx18-controls.h"
25b285192aSMauro Carvalho Chehab #include "cx18-cards.h"
26b285192aSMauro Carvalho Chehab #include "cx18-av-core.h"
27b285192aSMauro Carvalho Chehab #include <media/tveeprom.h>
28eaa80c44SHans Verkuil #include <media/v4l2-event.h>
29b285192aSMauro Carvalho Chehab 
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;
81*13de5a51SHans Verkuil 	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
82*13de5a51SHans Verkuil 	int w = pixfmt->width;
83*13de5a51SHans Verkuil 	int h = pixfmt->height;
8400d08584SHans Verkuil 
8500d08584SHans Verkuil 	w = min(w, 720);
86*13de5a51SHans Verkuil 	w = max(w, 720 / 16);
8700d08584SHans Verkuil 
8800d08584SHans Verkuil 	h = min(h, cx->is_50hz ? 576 : 480);
89*13de5a51SHans Verkuil 	h = max(h, (cx->is_50hz ? 576 : 480) / 8);
9000d08584SHans Verkuil 
91*13de5a51SHans Verkuil 	if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
92*13de5a51SHans Verkuil 		if (pixfmt->pixelformat != V4L2_PIX_FMT_NV12_16L16 &&
93*13de5a51SHans Verkuil 		    pixfmt->pixelformat != V4L2_PIX_FMT_UYVY)
94*13de5a51SHans Verkuil 			pixfmt->pixelformat = V4L2_PIX_FMT_UYVY;
95*13de5a51SHans Verkuil 		/* YUV height must be a multiple of 32 */
96*13de5a51SHans Verkuil 		h = round_up(h, 32);
97*13de5a51SHans Verkuil 		/*
98*13de5a51SHans Verkuil 		 * HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
99*13de5a51SHans Verkuil 		 * UYUV YUV size is (Y=(h*720) + UV=(h*(720)))
100*13de5a51SHans Verkuil 		 */
101*13de5a51SHans Verkuil 		if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12_16L16) {
102*13de5a51SHans Verkuil 			pixfmt->sizeimage = h * 720 * 3 / 2;
103*13de5a51SHans Verkuil 			pixfmt->bytesperline = 720; /* First plane */
104*13de5a51SHans Verkuil 		} else {
105*13de5a51SHans Verkuil 			pixfmt->sizeimage = h * 720 * 2;
106*13de5a51SHans Verkuil 			pixfmt->bytesperline = 1440; /* Packed */
107*13de5a51SHans Verkuil 		}
108*13de5a51SHans Verkuil 	} else {
109*13de5a51SHans Verkuil 		pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
110*13de5a51SHans Verkuil 		pixfmt->sizeimage = 128 * 1024;
111*13de5a51SHans Verkuil 		pixfmt->bytesperline = 0;
112*13de5a51SHans Verkuil 	}
113*13de5a51SHans Verkuil 
114*13de5a51SHans Verkuil 	pixfmt->width = w;
115*13de5a51SHans Verkuil 	pixfmt->height = h;
116*13de5a51SHans Verkuil 	pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
117*13de5a51SHans 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;
147*13de5a51SHans Verkuil 	s->vb_bytes_per_frame = fmt->fmt.pix.sizeimage;
148*13de5a51SHans 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)157b285192aSMauro Carvalho Chehab u16 cx18_service2vbi(int type)
158b285192aSMauro Carvalho Chehab {
159b285192aSMauro Carvalho Chehab 	switch (type) {
160b285192aSMauro Carvalho Chehab 	case V4L2_SLICED_TELETEXT_B:
161b285192aSMauro Carvalho Chehab 		return CX18_SLICED_TYPE_TELETEXT_B;
162b285192aSMauro Carvalho Chehab 	case V4L2_SLICED_CAPTION_525:
163b285192aSMauro Carvalho Chehab 		return CX18_SLICED_TYPE_CAPTION_525;
164b285192aSMauro Carvalho Chehab 	case V4L2_SLICED_WSS_625:
165b285192aSMauro Carvalho Chehab 		return CX18_SLICED_TYPE_WSS_625;
166b285192aSMauro Carvalho Chehab 	case V4L2_SLICED_VPS:
167b285192aSMauro Carvalho Chehab 		return CX18_SLICED_TYPE_VPS;
168b285192aSMauro Carvalho Chehab 	default:
169b285192aSMauro Carvalho Chehab 		return 0;
170b285192aSMauro Carvalho Chehab 	}
171b285192aSMauro Carvalho Chehab }
172b285192aSMauro Carvalho Chehab 
173b285192aSMauro Carvalho Chehab /* Check if VBI services are allowed on the (field, line) for the video std */
valid_service_line(int field,int line,int is_pal)174b285192aSMauro Carvalho Chehab static int valid_service_line(int field, int line, int is_pal)
175b285192aSMauro Carvalho Chehab {
176b285192aSMauro Carvalho Chehab 	return (is_pal && line >= 6 &&
177b285192aSMauro Carvalho Chehab 		((field == 0 && line <= 23) || (field == 1 && line <= 22))) ||
178b285192aSMauro Carvalho Chehab 	       (!is_pal && line >= 10 && line < 22);
179b285192aSMauro Carvalho Chehab }
180b285192aSMauro Carvalho Chehab 
181b285192aSMauro Carvalho Chehab /*
182b285192aSMauro Carvalho Chehab  * For a (field, line, std) and inbound potential set of services for that line,
183b285192aSMauro Carvalho Chehab  * return the first valid service of those passed in the incoming set for that
184b285192aSMauro Carvalho Chehab  * line in priority order:
185b285192aSMauro Carvalho Chehab  * CC, VPS, or WSS over TELETEXT for well known lines
186b285192aSMauro Carvalho Chehab  * TELETEXT, before VPS, before CC, before WSS, for other lines
187b285192aSMauro Carvalho Chehab  */
select_service_from_set(int field,int line,u16 set,int is_pal)188b285192aSMauro Carvalho Chehab static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
189b285192aSMauro Carvalho Chehab {
190b285192aSMauro Carvalho Chehab 	u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525);
191b285192aSMauro Carvalho Chehab 	int i;
192b285192aSMauro Carvalho Chehab 
193b285192aSMauro Carvalho Chehab 	set = set & valid_set;
194b285192aSMauro Carvalho Chehab 	if (set == 0 || !valid_service_line(field, line, is_pal))
195b285192aSMauro Carvalho Chehab 		return 0;
196b285192aSMauro Carvalho Chehab 	if (!is_pal) {
197b285192aSMauro Carvalho Chehab 		if (line == 21 && (set & V4L2_SLICED_CAPTION_525))
198b285192aSMauro Carvalho Chehab 			return V4L2_SLICED_CAPTION_525;
199b285192aSMauro Carvalho Chehab 	} else {
200b285192aSMauro Carvalho Chehab 		if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS))
201b285192aSMauro Carvalho Chehab 			return V4L2_SLICED_VPS;
202b285192aSMauro Carvalho Chehab 		if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625))
203b285192aSMauro Carvalho Chehab 			return V4L2_SLICED_WSS_625;
204b285192aSMauro Carvalho Chehab 		if (line == 23)
205b285192aSMauro Carvalho Chehab 			return 0;
206b285192aSMauro Carvalho Chehab 	}
207b285192aSMauro Carvalho Chehab 	for (i = 0; i < 32; i++) {
20895c52069SMauro Carvalho Chehab 		if (BIT(i) & set)
209b285192aSMauro Carvalho Chehab 			return 1 << i;
210b285192aSMauro Carvalho Chehab 	}
211b285192aSMauro Carvalho Chehab 	return 0;
212b285192aSMauro Carvalho Chehab }
213b285192aSMauro Carvalho Chehab 
214b285192aSMauro Carvalho Chehab /*
215b285192aSMauro Carvalho Chehab  * Expand the service_set of *fmt into valid service_lines for the std,
216b285192aSMauro Carvalho Chehab  * and clear the passed in fmt->service_set
217b285192aSMauro Carvalho Chehab  */
cx18_expand_service_set(struct v4l2_sliced_vbi_format * fmt,int is_pal)218b285192aSMauro Carvalho Chehab void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
219b285192aSMauro Carvalho Chehab {
220b285192aSMauro Carvalho Chehab 	u16 set = fmt->service_set;
221b285192aSMauro Carvalho Chehab 	int f, l;
222b285192aSMauro Carvalho Chehab 
223b285192aSMauro Carvalho Chehab 	fmt->service_set = 0;
224b285192aSMauro Carvalho Chehab 	for (f = 0; f < 2; f++) {
225b285192aSMauro Carvalho Chehab 		for (l = 0; l < 24; l++)
226b285192aSMauro Carvalho Chehab 			fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal);
227b285192aSMauro Carvalho Chehab 	}
228b285192aSMauro Carvalho Chehab }
229b285192aSMauro Carvalho Chehab 
230b285192aSMauro Carvalho Chehab /*
231b285192aSMauro Carvalho Chehab  * Sanitize the service_lines in *fmt per the video std, and return 1
232b285192aSMauro Carvalho Chehab  * if any service_line is left as valid after santization
233b285192aSMauro Carvalho Chehab  */
check_service_set(struct v4l2_sliced_vbi_format * fmt,int is_pal)234b285192aSMauro Carvalho Chehab static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
235b285192aSMauro Carvalho Chehab {
236b285192aSMauro Carvalho Chehab 	int f, l;
237b285192aSMauro Carvalho Chehab 	u16 set = 0;
238b285192aSMauro Carvalho Chehab 
239b285192aSMauro Carvalho Chehab 	for (f = 0; f < 2; f++) {
240b285192aSMauro Carvalho Chehab 		for (l = 0; l < 24; l++) {
241b285192aSMauro Carvalho Chehab 			fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);
242b285192aSMauro Carvalho Chehab 			set |= fmt->service_lines[f][l];
243b285192aSMauro Carvalho Chehab 		}
244b285192aSMauro Carvalho Chehab 	}
245b285192aSMauro Carvalho Chehab 	return set != 0;
246b285192aSMauro Carvalho Chehab }
247b285192aSMauro Carvalho Chehab 
248b285192aSMauro Carvalho Chehab /* Compute the service_set from the assumed valid service_lines of *fmt */
cx18_get_service_set(struct v4l2_sliced_vbi_format * fmt)249b285192aSMauro Carvalho Chehab u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt)
250b285192aSMauro Carvalho Chehab {
251b285192aSMauro Carvalho Chehab 	int f, l;
252b285192aSMauro Carvalho Chehab 	u16 set = 0;
253b285192aSMauro Carvalho Chehab 
254b285192aSMauro Carvalho Chehab 	for (f = 0; f < 2; f++) {
255b285192aSMauro Carvalho Chehab 		for (l = 0; l < 24; l++)
256b285192aSMauro Carvalho Chehab 			set |= fmt->service_lines[f][l];
257b285192aSMauro Carvalho Chehab 	}
258b285192aSMauro Carvalho Chehab 	return set;
259b285192aSMauro Carvalho Chehab }
260b285192aSMauro Carvalho Chehab 
cx18_g_fmt_vbi_cap(struct file * file,void * fh,struct v4l2_format * fmt)261b285192aSMauro Carvalho Chehab static int cx18_g_fmt_vbi_cap(struct file *file, void *fh,
262b285192aSMauro Carvalho Chehab 				struct v4l2_format *fmt)
263b285192aSMauro Carvalho Chehab {
264b285192aSMauro Carvalho Chehab 	struct cx18 *cx = fh2id(fh)->cx;
265b285192aSMauro Carvalho Chehab 	struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi;
266b285192aSMauro Carvalho Chehab 
267b285192aSMauro Carvalho Chehab 	vbifmt->sampling_rate = 27000000;
268b285192aSMauro Carvalho Chehab 	vbifmt->offset = 248; /* FIXME - slightly wrong for both 50 & 60 Hz */
269318de791SMauro Carvalho Chehab 	vbifmt->samples_per_line = VBI_ACTIVE_SAMPLES - 4;
270b285192aSMauro Carvalho Chehab 	vbifmt->sample_format = V4L2_PIX_FMT_GREY;
271b285192aSMauro Carvalho Chehab 	vbifmt->start[0] = cx->vbi.start[0];
272b285192aSMauro Carvalho Chehab 	vbifmt->start[1] = cx->vbi.start[1];
273b285192aSMauro Carvalho Chehab 	vbifmt->count[0] = vbifmt->count[1] = cx->vbi.count;
274b285192aSMauro Carvalho Chehab 	vbifmt->flags = 0;
275b285192aSMauro Carvalho Chehab 	vbifmt->reserved[0] = 0;
276b285192aSMauro Carvalho Chehab 	vbifmt->reserved[1] = 0;
277b285192aSMauro Carvalho Chehab 	return 0;
278b285192aSMauro Carvalho Chehab }
279b285192aSMauro Carvalho Chehab 
cx18_g_fmt_sliced_vbi_cap(struct file * file,void * fh,struct v4l2_format * fmt)280b285192aSMauro Carvalho Chehab static int cx18_g_fmt_sliced_vbi_cap(struct file *file, void *fh,
281b285192aSMauro Carvalho Chehab 					struct v4l2_format *fmt)
282b285192aSMauro Carvalho Chehab {
283b285192aSMauro Carvalho Chehab 	struct cx18 *cx = fh2id(fh)->cx;
284b285192aSMauro Carvalho Chehab 	struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
285b285192aSMauro Carvalho Chehab 
286b285192aSMauro Carvalho Chehab 	/* sane, V4L2 spec compliant, defaults */
287b285192aSMauro Carvalho Chehab 	vbifmt->reserved[0] = 0;
288b285192aSMauro Carvalho Chehab 	vbifmt->reserved[1] = 0;
289b285192aSMauro Carvalho Chehab 	vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
290b285192aSMauro Carvalho Chehab 	memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
291b285192aSMauro Carvalho Chehab 	vbifmt->service_set = 0;
292b285192aSMauro Carvalho Chehab 
293b285192aSMauro Carvalho Chehab 	/*
294b285192aSMauro Carvalho Chehab 	 * Fetch the configured service_lines and total service_set from the
295b285192aSMauro Carvalho Chehab 	 * digitizer/slicer.  Note, cx18_av_vbi() wipes the passed in
296b285192aSMauro Carvalho Chehab 	 * fmt->fmt.sliced under valid calling conditions
297b285192aSMauro Carvalho Chehab 	 */
298b285192aSMauro Carvalho Chehab 	if (v4l2_subdev_call(cx->sd_av, vbi, g_sliced_fmt, &fmt->fmt.sliced))
299b285192aSMauro Carvalho Chehab 		return -EINVAL;
300b285192aSMauro Carvalho Chehab 
301b285192aSMauro Carvalho Chehab 	vbifmt->service_set = cx18_get_service_set(vbifmt);
302b285192aSMauro Carvalho Chehab 	return 0;
303b285192aSMauro Carvalho Chehab }
304b285192aSMauro Carvalho Chehab 
cx18_try_fmt_vbi_cap(struct file * file,void * fh,struct v4l2_format * fmt)305b285192aSMauro Carvalho Chehab static int cx18_try_fmt_vbi_cap(struct file *file, void *fh,
306b285192aSMauro Carvalho Chehab 				struct v4l2_format *fmt)
307b285192aSMauro Carvalho Chehab {
308b285192aSMauro Carvalho Chehab 	return cx18_g_fmt_vbi_cap(file, fh, fmt);
309b285192aSMauro Carvalho Chehab }
310b285192aSMauro Carvalho Chehab 
cx18_try_fmt_sliced_vbi_cap(struct file * file,void * fh,struct v4l2_format * fmt)311b285192aSMauro Carvalho Chehab static int cx18_try_fmt_sliced_vbi_cap(struct file *file, void *fh,
312b285192aSMauro Carvalho Chehab 					struct v4l2_format *fmt)
313b285192aSMauro Carvalho Chehab {
314b285192aSMauro Carvalho Chehab 	struct cx18 *cx = fh2id(fh)->cx;
315b285192aSMauro Carvalho Chehab 	struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
316b285192aSMauro Carvalho Chehab 
317b285192aSMauro Carvalho Chehab 	vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
318b285192aSMauro Carvalho Chehab 	vbifmt->reserved[0] = 0;
319b285192aSMauro Carvalho Chehab 	vbifmt->reserved[1] = 0;
320b285192aSMauro Carvalho Chehab 
321b285192aSMauro Carvalho Chehab 	/* If given a service set, expand it validly & clear passed in set */
322b285192aSMauro Carvalho Chehab 	if (vbifmt->service_set)
323b285192aSMauro Carvalho Chehab 		cx18_expand_service_set(vbifmt, cx->is_50hz);
324b285192aSMauro Carvalho Chehab 	/* Sanitize the service_lines, and compute the new set if any valid */
325b285192aSMauro Carvalho Chehab 	if (check_service_set(vbifmt, cx->is_50hz))
326b285192aSMauro Carvalho Chehab 		vbifmt->service_set = cx18_get_service_set(vbifmt);
327b285192aSMauro Carvalho Chehab 	return 0;
328b285192aSMauro Carvalho Chehab }
329b285192aSMauro Carvalho Chehab 
cx18_s_fmt_vbi_cap(struct file * file,void * fh,struct v4l2_format * fmt)330b285192aSMauro Carvalho Chehab static int cx18_s_fmt_vbi_cap(struct file *file, void *fh,
331b285192aSMauro Carvalho Chehab 				struct v4l2_format *fmt)
332b285192aSMauro Carvalho Chehab {
333b285192aSMauro Carvalho Chehab 	struct cx18_open_id *id = fh2id(fh);
334b285192aSMauro Carvalho Chehab 	struct cx18 *cx = id->cx;
335b285192aSMauro Carvalho Chehab 	int ret;
336b285192aSMauro Carvalho Chehab 
337b285192aSMauro Carvalho Chehab 	/*
338b285192aSMauro Carvalho Chehab 	 * Changing the Encoder's Raw VBI parameters won't have any effect
339b285192aSMauro Carvalho Chehab 	 * if any analog capture is ongoing
340b285192aSMauro Carvalho Chehab 	 */
341b285192aSMauro Carvalho Chehab 	if (!cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
342b285192aSMauro Carvalho Chehab 		return -EBUSY;
343b285192aSMauro Carvalho Chehab 
344b285192aSMauro Carvalho Chehab 	/*
345b285192aSMauro Carvalho Chehab 	 * Set the digitizer registers for raw active VBI.
346b285192aSMauro Carvalho Chehab 	 * Note cx18_av_vbi_wipes out a lot of the passed in fmt under valid
347b285192aSMauro Carvalho Chehab 	 * calling conditions
348b285192aSMauro Carvalho Chehab 	 */
349b285192aSMauro Carvalho Chehab 	ret = v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &fmt->fmt.vbi);
350b285192aSMauro Carvalho Chehab 	if (ret)
351b285192aSMauro Carvalho Chehab 		return ret;
352b285192aSMauro Carvalho Chehab 
353b285192aSMauro Carvalho Chehab 	/* Store our new v4l2 (non-)sliced VBI state */
354b285192aSMauro Carvalho Chehab 	cx->vbi.sliced_in->service_set = 0;
355b285192aSMauro Carvalho Chehab 	cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
356b285192aSMauro Carvalho Chehab 
357b285192aSMauro Carvalho Chehab 	return cx18_g_fmt_vbi_cap(file, fh, fmt);
358b285192aSMauro Carvalho Chehab }
359b285192aSMauro Carvalho Chehab 
cx18_s_fmt_sliced_vbi_cap(struct file * file,void * fh,struct v4l2_format * fmt)360b285192aSMauro Carvalho Chehab static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh,
361b285192aSMauro Carvalho Chehab 					struct v4l2_format *fmt)
362b285192aSMauro Carvalho Chehab {
363b285192aSMauro Carvalho Chehab 	struct cx18_open_id *id = fh2id(fh);
364b285192aSMauro Carvalho Chehab 	struct cx18 *cx = id->cx;
365b285192aSMauro Carvalho Chehab 	int ret;
366b285192aSMauro Carvalho Chehab 	struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
367b285192aSMauro Carvalho Chehab 
368b285192aSMauro Carvalho Chehab 	cx18_try_fmt_sliced_vbi_cap(file, fh, fmt);
369b285192aSMauro Carvalho Chehab 
370b285192aSMauro Carvalho Chehab 	/*
371b285192aSMauro Carvalho Chehab 	 * Changing the Encoder's Raw VBI parameters won't have any effect
372b285192aSMauro Carvalho Chehab 	 * if any analog capture is ongoing
373b285192aSMauro Carvalho Chehab 	 */
374b285192aSMauro Carvalho Chehab 	if (cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
375b285192aSMauro Carvalho Chehab 		return -EBUSY;
376b285192aSMauro Carvalho Chehab 
377b285192aSMauro Carvalho Chehab 	/*
378b285192aSMauro Carvalho Chehab 	 * Set the service_lines requested in the digitizer/slicer registers.
379b285192aSMauro Carvalho Chehab 	 * Note, cx18_av_vbi() wipes some "impossible" service lines in the
380b285192aSMauro Carvalho Chehab 	 * passed in fmt->fmt.sliced under valid calling conditions
381b285192aSMauro Carvalho Chehab 	 */
382b285192aSMauro Carvalho Chehab 	ret = v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &fmt->fmt.sliced);
383b285192aSMauro Carvalho Chehab 	if (ret)
384b285192aSMauro Carvalho Chehab 		return ret;
385b285192aSMauro Carvalho Chehab 	/* Store our current v4l2 sliced VBI settings */
386b285192aSMauro Carvalho Chehab 	cx->vbi.in.type =  V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
387b285192aSMauro Carvalho Chehab 	memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in));
388b285192aSMauro Carvalho Chehab 	return 0;
389b285192aSMauro Carvalho Chehab }
390b285192aSMauro Carvalho Chehab 
391b285192aSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
cx18_g_register(struct file * file,void * fh,struct v4l2_dbg_register * reg)392b285192aSMauro Carvalho Chehab static int cx18_g_register(struct file *file, void *fh,
393b285192aSMauro Carvalho Chehab 				struct v4l2_dbg_register *reg)
394b285192aSMauro Carvalho Chehab {
395b285192aSMauro Carvalho Chehab 	struct cx18 *cx = fh2id(fh)->cx;
396b285192aSMauro Carvalho Chehab 
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 }
405b285192aSMauro Carvalho Chehab 
cx18_s_register(struct file * file,void * fh,const struct v4l2_dbg_register * reg)406b285192aSMauro Carvalho Chehab static int cx18_s_register(struct file *file, void *fh,
407977ba3b1SHans Verkuil 				const struct v4l2_dbg_register *reg)
408b285192aSMauro Carvalho Chehab {
409b285192aSMauro Carvalho Chehab 	struct cx18 *cx = fh2id(fh)->cx;
410b285192aSMauro Carvalho Chehab 
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 }
418b285192aSMauro Carvalho Chehab #endif
419b285192aSMauro Carvalho Chehab 
cx18_querycap(struct file * file,void * fh,struct v4l2_capability * vcap)420b285192aSMauro Carvalho Chehab static int cx18_querycap(struct file *file, void *fh,
421b285192aSMauro Carvalho Chehab 				struct v4l2_capability *vcap)
422b285192aSMauro Carvalho Chehab {
423b285192aSMauro Carvalho Chehab 	struct cx18_open_id *id = fh2id(fh);
424b285192aSMauro Carvalho Chehab 	struct cx18 *cx = id->cx;
425b285192aSMauro Carvalho Chehab 
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;
429b285192aSMauro Carvalho Chehab 	return 0;
430b285192aSMauro Carvalho Chehab }
431b285192aSMauro Carvalho Chehab 
cx18_enumaudio(struct file * file,void * fh,struct v4l2_audio * vin)432b285192aSMauro Carvalho Chehab static int cx18_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin)
433b285192aSMauro Carvalho Chehab {
434b285192aSMauro Carvalho Chehab 	struct cx18 *cx = fh2id(fh)->cx;
435b285192aSMauro Carvalho Chehab 
436b285192aSMauro Carvalho Chehab 	return cx18_get_audio_input(cx, vin->index, vin);
437b285192aSMauro Carvalho Chehab }
438b285192aSMauro Carvalho Chehab 
cx18_g_audio(struct file * file,void * fh,struct v4l2_audio * vin)439b285192aSMauro Carvalho Chehab static int cx18_g_audio(struct file *file, void *fh, struct v4l2_audio *vin)
440b285192aSMauro Carvalho Chehab {
441b285192aSMauro Carvalho Chehab 	struct cx18 *cx = fh2id(fh)->cx;
442b285192aSMauro Carvalho Chehab 
443b285192aSMauro Carvalho Chehab 	vin->index = cx->audio_input;
444b285192aSMauro Carvalho Chehab 	return cx18_get_audio_input(cx, vin->index, vin);
445b285192aSMauro Carvalho Chehab }
446b285192aSMauro Carvalho Chehab 
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)
448b285192aSMauro Carvalho Chehab {
449b285192aSMauro Carvalho Chehab 	struct cx18 *cx = fh2id(fh)->cx;
450b285192aSMauro Carvalho Chehab 
451b285192aSMauro Carvalho Chehab 	if (vout->index >= cx->nof_audio_inputs)
452b285192aSMauro Carvalho Chehab 		return -EINVAL;
453b285192aSMauro Carvalho Chehab 	cx->audio_input = vout->index;
454b285192aSMauro Carvalho Chehab 	cx18_audio_set_io(cx);
455b285192aSMauro Carvalho Chehab 	return 0;
456b285192aSMauro Carvalho Chehab }
457b285192aSMauro Carvalho Chehab 
cx18_enum_input(struct file * file,void * fh,struct v4l2_input * vin)458b285192aSMauro Carvalho Chehab static int cx18_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
459b285192aSMauro Carvalho Chehab {
460b285192aSMauro Carvalho Chehab 	struct cx18 *cx = fh2id(fh)->cx;
461b285192aSMauro Carvalho Chehab 
462b285192aSMauro Carvalho Chehab 	/* set it to defaults from our table */
463b285192aSMauro Carvalho Chehab 	return cx18_get_input(cx, vin->index, vin);
464b285192aSMauro Carvalho Chehab }
465b285192aSMauro Carvalho Chehab 
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)
468b285192aSMauro Carvalho Chehab {
469b285192aSMauro Carvalho Chehab 	struct cx18 *cx = fh2id(fh)->cx;
470b285192aSMauro Carvalho Chehab 
4715200ab6aSHans Verkuil 	if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
472b285192aSMauro Carvalho Chehab 		return -EINVAL;
4735200ab6aSHans Verkuil 
4745200ab6aSHans Verkuil 	f->numerator = cx->is_50hz ? 54 : 11;
4755200ab6aSHans Verkuil 	f->denominator = cx->is_50hz ? 59 : 10;
476b285192aSMauro Carvalho Chehab 	return 0;
477b285192aSMauro Carvalho Chehab }
478b285192aSMauro Carvalho Chehab 
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)
481b285192aSMauro Carvalho Chehab {
482b285192aSMauro Carvalho Chehab 	struct cx18 *cx = fh2id(fh)->cx;
483b285192aSMauro Carvalho Chehab 
48455cda4abSHans Verkuil 	if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
485b285192aSMauro Carvalho Chehab 		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:
494b285192aSMauro Carvalho Chehab 		return -EINVAL;
495b285192aSMauro Carvalho Chehab 	}
49655cda4abSHans Verkuil 	return 0;
49755cda4abSHans Verkuil }
498b285192aSMauro Carvalho Chehab 
cx18_enum_fmt_vid_cap(struct file * file,void * fh,struct v4l2_fmtdesc * fmt)499b285192aSMauro Carvalho Chehab static int cx18_enum_fmt_vid_cap(struct file *file, void *fh,
500b285192aSMauro Carvalho Chehab 					struct v4l2_fmtdesc *fmt)
501b285192aSMauro Carvalho Chehab {
502651640f6SHans Verkuil 	struct cx18_open_id *id = fh2id(fh);
503b285192aSMauro Carvalho Chehab 
504651640f6SHans Verkuil 	if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
505651640f6SHans Verkuil 		if (fmt->index >= ARRAY_SIZE(cx18_formats_yuv))
506b285192aSMauro Carvalho Chehab 			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];
513b285192aSMauro Carvalho Chehab 	return 0;
514b285192aSMauro Carvalho Chehab }
515b285192aSMauro Carvalho Chehab 
cx18_g_input(struct file * file,void * fh,unsigned int * i)516b285192aSMauro Carvalho Chehab static int cx18_g_input(struct file *file, void *fh, unsigned int *i)
517b285192aSMauro Carvalho Chehab {
518b285192aSMauro Carvalho Chehab 	struct cx18 *cx = fh2id(fh)->cx;
519b285192aSMauro Carvalho Chehab 
520b285192aSMauro Carvalho Chehab 	*i = cx->active_input;
521b285192aSMauro Carvalho Chehab 	return 0;
522b285192aSMauro Carvalho Chehab }
523b285192aSMauro Carvalho Chehab 
cx18_s_input(struct file * file,void * fh,unsigned int inp)524b285192aSMauro Carvalho Chehab int cx18_s_input(struct file *file, void *fh, unsigned int inp)
525b285192aSMauro Carvalho Chehab {
526b285192aSMauro Carvalho Chehab 	struct cx18_open_id *id = fh2id(fh);
527b285192aSMauro Carvalho Chehab 	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;
531b285192aSMauro Carvalho Chehab 
532b285192aSMauro Carvalho Chehab 	if (inp >= cx->nof_inputs)
533b285192aSMauro Carvalho Chehab 		return -EINVAL;
534b285192aSMauro Carvalho Chehab 
535b285192aSMauro Carvalho Chehab 	if (inp == cx->active_input) {
536b285192aSMauro Carvalho Chehab 		CX18_DEBUG_INFO("Input unchanged\n");
537b285192aSMauro Carvalho Chehab 		return 0;
538b285192aSMauro Carvalho Chehab 	}
539b285192aSMauro Carvalho Chehab 
540b285192aSMauro Carvalho Chehab 	CX18_DEBUG_INFO("Changing input from %d to %d\n",
541b285192aSMauro Carvalho Chehab 			cx->active_input, inp);
542b285192aSMauro Carvalho Chehab 
543b285192aSMauro Carvalho Chehab 	cx->active_input = inp;
544b285192aSMauro Carvalho Chehab 	/* Set the audio input to whatever is appropriate for the input type. */
545b285192aSMauro Carvalho Chehab 	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;
551b285192aSMauro Carvalho Chehab 
552b285192aSMauro Carvalho Chehab 	/* prevent others from messing with the streams until
553b285192aSMauro Carvalho Chehab 	   we're finished changing inputs. */
554b285192aSMauro Carvalho Chehab 	cx18_mute(cx);
555b285192aSMauro Carvalho Chehab 	cx18_video_set_io(cx);
556b285192aSMauro Carvalho Chehab 	cx18_audio_set_io(cx);
557b285192aSMauro Carvalho Chehab 	cx18_unmute(cx);
558b285192aSMauro Carvalho Chehab 	return 0;
559b285192aSMauro Carvalho Chehab }
560b285192aSMauro Carvalho Chehab 
cx18_g_frequency(struct file * file,void * fh,struct v4l2_frequency * vf)561b285192aSMauro Carvalho Chehab static int cx18_g_frequency(struct file *file, void *fh,
562b285192aSMauro Carvalho Chehab 				struct v4l2_frequency *vf)
563b285192aSMauro Carvalho Chehab {
564b285192aSMauro Carvalho Chehab 	struct cx18 *cx = fh2id(fh)->cx;
565b285192aSMauro Carvalho Chehab 
566b285192aSMauro Carvalho Chehab 	if (vf->tuner != 0)
567b285192aSMauro Carvalho Chehab 		return -EINVAL;
568b285192aSMauro Carvalho Chehab 
569b285192aSMauro Carvalho Chehab 	cx18_call_all(cx, tuner, g_frequency, vf);
570b285192aSMauro Carvalho Chehab 	return 0;
571b285192aSMauro Carvalho Chehab }
572b285192aSMauro Carvalho Chehab 
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)
574b285192aSMauro Carvalho Chehab {
575b285192aSMauro Carvalho Chehab 	struct cx18_open_id *id = fh2id(fh);
576b285192aSMauro Carvalho Chehab 	struct cx18 *cx = id->cx;
577b285192aSMauro Carvalho Chehab 
578b285192aSMauro Carvalho Chehab 	if (vf->tuner != 0)
579b285192aSMauro Carvalho Chehab 		return -EINVAL;
580b285192aSMauro Carvalho Chehab 
581b285192aSMauro Carvalho Chehab 	cx18_mute(cx);
582b285192aSMauro Carvalho Chehab 	CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency);
583b285192aSMauro Carvalho Chehab 	cx18_call_all(cx, tuner, s_frequency, vf);
584b285192aSMauro Carvalho Chehab 	cx18_unmute(cx);
585b285192aSMauro Carvalho Chehab 	return 0;
586b285192aSMauro Carvalho Chehab }
587b285192aSMauro Carvalho Chehab 
cx18_g_std(struct file * file,void * fh,v4l2_std_id * std)588b285192aSMauro Carvalho Chehab static int cx18_g_std(struct file *file, void *fh, v4l2_std_id *std)
589b285192aSMauro Carvalho Chehab {
590b285192aSMauro Carvalho Chehab 	struct cx18 *cx = fh2id(fh)->cx;
591b285192aSMauro Carvalho Chehab 
592b285192aSMauro Carvalho Chehab 	*std = cx->std;
593b285192aSMauro Carvalho Chehab 	return 0;
594b285192aSMauro Carvalho Chehab }
595b285192aSMauro Carvalho Chehab 
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)
597b285192aSMauro Carvalho Chehab {
598b285192aSMauro Carvalho Chehab 	struct cx18_open_id *id = fh2id(fh);
599b285192aSMauro Carvalho Chehab 	struct cx18 *cx = id->cx;
600b285192aSMauro Carvalho Chehab 
601314527acSHans Verkuil 	if ((std & V4L2_STD_ALL) == 0)
602b285192aSMauro Carvalho Chehab 		return -EINVAL;
603b285192aSMauro Carvalho Chehab 
604314527acSHans Verkuil 	if (std == cx->std)
605b285192aSMauro Carvalho Chehab 		return 0;
606b285192aSMauro Carvalho Chehab 
607b285192aSMauro Carvalho Chehab 	if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ||
608b285192aSMauro Carvalho Chehab 	    atomic_read(&cx->ana_capturing) > 0) {
609b285192aSMauro Carvalho Chehab 		/* Switching standard would turn off the radio or mess
610b285192aSMauro Carvalho Chehab 		   with already running streams, prevent that by
611b285192aSMauro Carvalho Chehab 		   returning EBUSY. */
612b285192aSMauro Carvalho Chehab 		return -EBUSY;
613b285192aSMauro Carvalho Chehab 	}
614b285192aSMauro Carvalho Chehab 
615314527acSHans Verkuil 	cx->std = std;
616314527acSHans Verkuil 	cx->is_60hz = (std & V4L2_STD_525_60) ? 1 : 0;
617b285192aSMauro Carvalho Chehab 	cx->is_50hz = !cx->is_60hz;
618b285192aSMauro Carvalho Chehab 	cx2341x_handler_set_50hz(&cx->cxhdl, cx->is_50hz);
619b285192aSMauro Carvalho Chehab 	cx->cxhdl.width = 720;
620b285192aSMauro Carvalho Chehab 	cx->cxhdl.height = cx->is_50hz ? 576 : 480;
621*13de5a51SHans Verkuil 	/*
622*13de5a51SHans Verkuil 	 * HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
623*13de5a51SHans Verkuil 	 * UYUV YUV size is (Y=(h*720) + UV=(h*(720)))
624*13de5a51SHans Verkuil 	 */
625*13de5a51SHans Verkuil 	if (cx->streams[CX18_ENC_STREAM_TYPE_YUV].pixelformat == V4L2_PIX_FMT_NV12_16L16) {
626*13de5a51SHans Verkuil 		cx->streams[CX18_ENC_STREAM_TYPE_YUV].vb_bytes_per_frame =
627*13de5a51SHans Verkuil 			cx->cxhdl.height * 720 * 3 / 2;
628*13de5a51SHans Verkuil 		cx->streams[CX18_ENC_STREAM_TYPE_YUV].vb_bytes_per_line = 720;
629*13de5a51SHans Verkuil 	} else {
630*13de5a51SHans Verkuil 		cx->streams[CX18_ENC_STREAM_TYPE_YUV].vb_bytes_per_frame =
631*13de5a51SHans Verkuil 			cx->cxhdl.height * 720 * 2;
632*13de5a51SHans Verkuil 		cx->streams[CX18_ENC_STREAM_TYPE_YUV].vb_bytes_per_line = 1440;
633*13de5a51SHans Verkuil 	}
634b285192aSMauro Carvalho Chehab 	cx->vbi.count = cx->is_50hz ? 18 : 12;
635b285192aSMauro Carvalho Chehab 	cx->vbi.start[0] = cx->is_50hz ? 6 : 10;
636b285192aSMauro Carvalho Chehab 	cx->vbi.start[1] = cx->is_50hz ? 318 : 273;
637b285192aSMauro Carvalho Chehab 	CX18_DEBUG_INFO("Switching standard to %llx.\n",
638b285192aSMauro Carvalho Chehab 			(unsigned long long) cx->std);
639b285192aSMauro Carvalho Chehab 
640b285192aSMauro Carvalho Chehab 	/* Tuner */
6418774bed9SLaurent Pinchart 	cx18_call_all(cx, video, s_std, cx->std);
642b285192aSMauro Carvalho Chehab 	return 0;
643b285192aSMauro Carvalho Chehab }
644b285192aSMauro Carvalho Chehab 
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)
646b285192aSMauro Carvalho Chehab {
647b285192aSMauro Carvalho Chehab 	struct cx18_open_id *id = fh2id(fh);
648b285192aSMauro Carvalho Chehab 	struct cx18 *cx = id->cx;
649b285192aSMauro Carvalho Chehab 
650b285192aSMauro Carvalho Chehab 	if (vt->index != 0)
651b285192aSMauro Carvalho Chehab 		return -EINVAL;
652b285192aSMauro Carvalho Chehab 
653b285192aSMauro Carvalho Chehab 	cx18_call_all(cx, tuner, s_tuner, vt);
654b285192aSMauro Carvalho Chehab 	return 0;
655b285192aSMauro Carvalho Chehab }
656b285192aSMauro Carvalho Chehab 
cx18_g_tuner(struct file * file,void * fh,struct v4l2_tuner * vt)657b285192aSMauro Carvalho Chehab static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
658b285192aSMauro Carvalho Chehab {
659b285192aSMauro Carvalho Chehab 	struct cx18 *cx = fh2id(fh)->cx;
660b285192aSMauro Carvalho Chehab 
661b285192aSMauro Carvalho Chehab 	if (vt->index != 0)
662b285192aSMauro Carvalho Chehab 		return -EINVAL;
663b285192aSMauro Carvalho Chehab 
664b285192aSMauro Carvalho Chehab 	cx18_call_all(cx, tuner, g_tuner, vt);
665b285192aSMauro Carvalho Chehab 
666b285192aSMauro Carvalho Chehab 	if (vt->type == V4L2_TUNER_RADIO)
667c0decac1SMauro Carvalho Chehab 		strscpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name));
668b285192aSMauro Carvalho Chehab 	else
669c0decac1SMauro Carvalho Chehab 		strscpy(vt->name, "cx18 TV Tuner", sizeof(vt->name));
670b285192aSMauro Carvalho Chehab 	return 0;
671b285192aSMauro Carvalho Chehab }
672b285192aSMauro Carvalho Chehab 
cx18_g_sliced_vbi_cap(struct file * file,void * fh,struct v4l2_sliced_vbi_cap * cap)673b285192aSMauro Carvalho Chehab static int cx18_g_sliced_vbi_cap(struct file *file, void *fh,
674b285192aSMauro Carvalho Chehab 					struct v4l2_sliced_vbi_cap *cap)
675b285192aSMauro Carvalho Chehab {
676b285192aSMauro Carvalho Chehab 	struct cx18 *cx = fh2id(fh)->cx;
677b285192aSMauro Carvalho Chehab 	int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
678b285192aSMauro Carvalho Chehab 	int f, l;
679b285192aSMauro Carvalho Chehab 
680b285192aSMauro Carvalho Chehab 	if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
681b285192aSMauro Carvalho Chehab 		return -EINVAL;
682b285192aSMauro Carvalho Chehab 
683b285192aSMauro Carvalho Chehab 	cap->service_set = 0;
684b285192aSMauro Carvalho Chehab 	for (f = 0; f < 2; f++) {
685b285192aSMauro Carvalho Chehab 		for (l = 0; l < 24; l++) {
686b285192aSMauro Carvalho Chehab 			if (valid_service_line(f, l, cx->is_50hz)) {
687b285192aSMauro Carvalho Chehab 				/*
688b285192aSMauro Carvalho Chehab 				 * We can find all v4l2 supported vbi services
689b285192aSMauro Carvalho Chehab 				 * for the standard, on a valid line for the std
690b285192aSMauro Carvalho Chehab 				 */
691b285192aSMauro Carvalho Chehab 				cap->service_lines[f][l] = set;
692b285192aSMauro Carvalho Chehab 				cap->service_set |= set;
693b285192aSMauro Carvalho Chehab 			} else
694b285192aSMauro Carvalho Chehab 				cap->service_lines[f][l] = 0;
695b285192aSMauro Carvalho Chehab 		}
696b285192aSMauro Carvalho Chehab 	}
697b285192aSMauro Carvalho Chehab 	for (f = 0; f < 3; f++)
698b285192aSMauro Carvalho Chehab 		cap->reserved[f] = 0;
699b285192aSMauro Carvalho Chehab 	return 0;
700b285192aSMauro Carvalho Chehab }
701b285192aSMauro Carvalho Chehab 
_cx18_process_idx_data(struct cx18_buffer * buf,struct v4l2_enc_idx * idx)702b285192aSMauro Carvalho Chehab static int _cx18_process_idx_data(struct cx18_buffer *buf,
703b285192aSMauro Carvalho Chehab 				  struct v4l2_enc_idx *idx)
704b285192aSMauro Carvalho Chehab {
705b285192aSMauro Carvalho Chehab 	int consumed, remaining;
706b285192aSMauro Carvalho Chehab 	struct v4l2_enc_idx_entry *e_idx;
707b285192aSMauro Carvalho Chehab 	struct cx18_enc_idx_entry *e_buf;
708b285192aSMauro Carvalho Chehab 
709b285192aSMauro Carvalho Chehab 	/* Frame type lookup: 1=I, 2=P, 4=B */
71027dbc2e6SColin Ian King 	static const int mapping[8] = {
711b285192aSMauro Carvalho Chehab 		-1, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_P,
712b285192aSMauro Carvalho Chehab 		-1, V4L2_ENC_IDX_FRAME_B, -1, -1, -1
713b285192aSMauro Carvalho Chehab 	};
714b285192aSMauro Carvalho Chehab 
715b285192aSMauro Carvalho Chehab 	/*
716b285192aSMauro Carvalho Chehab 	 * Assumption here is that a buf holds an integral number of
717b285192aSMauro Carvalho Chehab 	 * struct cx18_enc_idx_entry objects and is properly aligned.
718b285192aSMauro Carvalho Chehab 	 * This is enforced by the module options on IDX buffer sizes.
719b285192aSMauro Carvalho Chehab 	 */
720b285192aSMauro Carvalho Chehab 	remaining = buf->bytesused - buf->readpos;
721b285192aSMauro Carvalho Chehab 	consumed = 0;
722b285192aSMauro Carvalho Chehab 	e_idx = &idx->entry[idx->entries];
723b285192aSMauro Carvalho Chehab 	e_buf = (struct cx18_enc_idx_entry *) &buf->buf[buf->readpos];
724b285192aSMauro Carvalho Chehab 
725b285192aSMauro Carvalho Chehab 	while (remaining >= sizeof(struct cx18_enc_idx_entry) &&
726b285192aSMauro Carvalho Chehab 	       idx->entries < V4L2_ENC_IDX_ENTRIES) {
727b285192aSMauro Carvalho Chehab 
728b285192aSMauro Carvalho Chehab 		e_idx->offset = (((u64) le32_to_cpu(e_buf->offset_high)) << 32)
729b285192aSMauro Carvalho Chehab 				| le32_to_cpu(e_buf->offset_low);
730b285192aSMauro Carvalho Chehab 
731b285192aSMauro Carvalho Chehab 		e_idx->pts = (((u64) (le32_to_cpu(e_buf->pts_high) & 1)) << 32)
732b285192aSMauro Carvalho Chehab 			     | le32_to_cpu(e_buf->pts_low);
733b285192aSMauro Carvalho Chehab 
734b285192aSMauro Carvalho Chehab 		e_idx->length = le32_to_cpu(e_buf->length);
735b285192aSMauro Carvalho Chehab 
736b285192aSMauro Carvalho Chehab 		e_idx->flags = mapping[le32_to_cpu(e_buf->flags) & 0x7];
737b285192aSMauro Carvalho Chehab 
738b285192aSMauro Carvalho Chehab 		e_idx->reserved[0] = 0;
739b285192aSMauro Carvalho Chehab 		e_idx->reserved[1] = 0;
740b285192aSMauro Carvalho Chehab 
741b285192aSMauro Carvalho Chehab 		idx->entries++;
742b285192aSMauro Carvalho Chehab 		e_idx = &idx->entry[idx->entries];
743b285192aSMauro Carvalho Chehab 		e_buf++;
744b285192aSMauro Carvalho Chehab 
745b285192aSMauro Carvalho Chehab 		remaining -= sizeof(struct cx18_enc_idx_entry);
746b285192aSMauro Carvalho Chehab 		consumed += sizeof(struct cx18_enc_idx_entry);
747b285192aSMauro Carvalho Chehab 	}
748b285192aSMauro Carvalho Chehab 
749b285192aSMauro Carvalho Chehab 	/* Swallow any partial entries at the end, if there are any */
750b285192aSMauro Carvalho Chehab 	if (remaining > 0 && remaining < sizeof(struct cx18_enc_idx_entry))
751b285192aSMauro Carvalho Chehab 		consumed += remaining;
752b285192aSMauro Carvalho Chehab 
753b285192aSMauro Carvalho Chehab 	buf->readpos += consumed;
754b285192aSMauro Carvalho Chehab 	return consumed;
755b285192aSMauro Carvalho Chehab }
756b285192aSMauro Carvalho Chehab 
cx18_process_idx_data(struct cx18_stream * s,struct cx18_mdl * mdl,struct v4l2_enc_idx * idx)757b285192aSMauro Carvalho Chehab static int cx18_process_idx_data(struct cx18_stream *s, struct cx18_mdl *mdl,
758b285192aSMauro Carvalho Chehab 				 struct v4l2_enc_idx *idx)
759b285192aSMauro Carvalho Chehab {
760b285192aSMauro Carvalho Chehab 	if (s->type != CX18_ENC_STREAM_TYPE_IDX)
761b285192aSMauro Carvalho Chehab 		return -EINVAL;
762b285192aSMauro Carvalho Chehab 
763b285192aSMauro Carvalho Chehab 	if (mdl->curr_buf == NULL)
764b285192aSMauro Carvalho Chehab 		mdl->curr_buf = list_first_entry(&mdl->buf_list,
765b285192aSMauro Carvalho Chehab 						 struct cx18_buffer, list);
766b285192aSMauro Carvalho Chehab 
767b285192aSMauro Carvalho Chehab 	if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) {
768b285192aSMauro Carvalho Chehab 		/*
769b285192aSMauro Carvalho Chehab 		 * For some reason we've exhausted the buffers, but the MDL
770b285192aSMauro Carvalho Chehab 		 * object still said some data was unread.
771b285192aSMauro Carvalho Chehab 		 * Fix that and bail out.
772b285192aSMauro Carvalho Chehab 		 */
773b285192aSMauro Carvalho Chehab 		mdl->readpos = mdl->bytesused;
774b285192aSMauro Carvalho Chehab 		return 0;
775b285192aSMauro Carvalho Chehab 	}
776b285192aSMauro Carvalho Chehab 
777b285192aSMauro Carvalho Chehab 	list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) {
778b285192aSMauro Carvalho Chehab 
779b285192aSMauro Carvalho Chehab 		/* Skip any empty buffers in the MDL */
780b285192aSMauro Carvalho Chehab 		if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused)
781b285192aSMauro Carvalho Chehab 			continue;
782b285192aSMauro Carvalho Chehab 
783b285192aSMauro Carvalho Chehab 		mdl->readpos += _cx18_process_idx_data(mdl->curr_buf, idx);
784b285192aSMauro Carvalho Chehab 
785b285192aSMauro Carvalho Chehab 		/* exit when MDL drained or request satisfied */
786b285192aSMauro Carvalho Chehab 		if (idx->entries >= V4L2_ENC_IDX_ENTRIES ||
787b285192aSMauro Carvalho Chehab 		    mdl->curr_buf->readpos < mdl->curr_buf->bytesused ||
788b285192aSMauro Carvalho Chehab 		    mdl->readpos >= mdl->bytesused)
789b285192aSMauro Carvalho Chehab 			break;
790b285192aSMauro Carvalho Chehab 	}
791b285192aSMauro Carvalho Chehab 	return 0;
792b285192aSMauro Carvalho Chehab }
793b285192aSMauro Carvalho Chehab 
cx18_g_enc_index(struct file * file,void * fh,struct v4l2_enc_idx * idx)794b285192aSMauro Carvalho Chehab static int cx18_g_enc_index(struct file *file, void *fh,
795b285192aSMauro Carvalho Chehab 				struct v4l2_enc_idx *idx)
796b285192aSMauro Carvalho Chehab {
797b285192aSMauro Carvalho Chehab 	struct cx18 *cx = fh2id(fh)->cx;
798b285192aSMauro Carvalho Chehab 	struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
799b285192aSMauro Carvalho Chehab 	s32 tmp;
800b285192aSMauro Carvalho Chehab 	struct cx18_mdl *mdl;
801b285192aSMauro Carvalho Chehab 
802b285192aSMauro Carvalho Chehab 	if (!cx18_stream_enabled(s)) /* Module options inhibited IDX stream */
803b285192aSMauro Carvalho Chehab 		return -EINVAL;
804b285192aSMauro Carvalho Chehab 
805b285192aSMauro Carvalho Chehab 	/* Compute the best case number of entries we can buffer */
806b285192aSMauro Carvalho Chehab 	tmp = s->buffers -
807b285192aSMauro Carvalho Chehab 			  s->bufs_per_mdl * CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN;
808b285192aSMauro Carvalho Chehab 	if (tmp <= 0)
809b285192aSMauro Carvalho Chehab 		tmp = 1;
810b285192aSMauro Carvalho Chehab 	tmp = tmp * s->buf_size / sizeof(struct cx18_enc_idx_entry);
811b285192aSMauro Carvalho Chehab 
812b285192aSMauro Carvalho Chehab 	/* Fill out the header of the return structure */
813b285192aSMauro Carvalho Chehab 	idx->entries = 0;
814b285192aSMauro Carvalho Chehab 	idx->entries_cap = tmp;
815b285192aSMauro Carvalho Chehab 	memset(idx->reserved, 0, sizeof(idx->reserved));
816b285192aSMauro Carvalho Chehab 
817b285192aSMauro Carvalho Chehab 	/* Pull IDX MDLs and buffers from q_full and populate the entries */
818b285192aSMauro Carvalho Chehab 	do {
819b285192aSMauro Carvalho Chehab 		mdl = cx18_dequeue(s, &s->q_full);
820b285192aSMauro Carvalho Chehab 		if (mdl == NULL) /* No more IDX data right now */
821b285192aSMauro Carvalho Chehab 			break;
822b285192aSMauro Carvalho Chehab 
823b285192aSMauro Carvalho Chehab 		/* Extract the Index entry data from the MDL and buffers */
824b285192aSMauro Carvalho Chehab 		cx18_process_idx_data(s, mdl, idx);
825b285192aSMauro Carvalho Chehab 		if (mdl->readpos < mdl->bytesused) {
826b285192aSMauro Carvalho Chehab 			/* We finished with data remaining, push the MDL back */
827b285192aSMauro Carvalho Chehab 			cx18_push(s, mdl, &s->q_full);
828b285192aSMauro Carvalho Chehab 			break;
829b285192aSMauro Carvalho Chehab 		}
830b285192aSMauro Carvalho Chehab 
831b285192aSMauro Carvalho Chehab 		/* We drained this MDL, schedule it to go to the firmware */
832b285192aSMauro Carvalho Chehab 		cx18_enqueue(s, mdl, &s->q_free);
833b285192aSMauro Carvalho Chehab 
834b285192aSMauro Carvalho Chehab 	} while (idx->entries < V4L2_ENC_IDX_ENTRIES);
835b285192aSMauro Carvalho Chehab 
836b285192aSMauro Carvalho Chehab 	/* Tell the work handler to send free IDX MDLs to the firmware */
837b285192aSMauro Carvalho Chehab 	cx18_stream_load_fw_queue(s);
838b285192aSMauro Carvalho Chehab 	return 0;
839b285192aSMauro Carvalho Chehab }
840b285192aSMauro Carvalho Chehab 
cx18_encoder_cmd(struct file * file,void * fh,struct v4l2_encoder_cmd * enc)841b285192aSMauro Carvalho Chehab static int cx18_encoder_cmd(struct file *file, void *fh,
842b285192aSMauro Carvalho Chehab 				struct v4l2_encoder_cmd *enc)
843b285192aSMauro Carvalho Chehab {
844b285192aSMauro Carvalho Chehab 	struct cx18_open_id *id = fh2id(fh);
845b285192aSMauro Carvalho Chehab 	struct cx18 *cx = id->cx;
846b285192aSMauro Carvalho Chehab 	u32 h;
847b285192aSMauro Carvalho Chehab 
848b285192aSMauro Carvalho Chehab 	switch (enc->cmd) {
849b285192aSMauro Carvalho Chehab 	case V4L2_ENC_CMD_START:
850b285192aSMauro Carvalho Chehab 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
851b285192aSMauro Carvalho Chehab 		enc->flags = 0;
852b285192aSMauro Carvalho Chehab 		return cx18_start_capture(id);
853b285192aSMauro Carvalho Chehab 
854b285192aSMauro Carvalho Chehab 	case V4L2_ENC_CMD_STOP:
855b285192aSMauro Carvalho Chehab 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
856b285192aSMauro Carvalho Chehab 		enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
857643e8350SHans Verkuil 		cx18_stop_capture(&cx->streams[id->type],
858b285192aSMauro Carvalho Chehab 				  enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);
859b285192aSMauro Carvalho Chehab 		break;
860b285192aSMauro Carvalho Chehab 
861b285192aSMauro Carvalho Chehab 	case V4L2_ENC_CMD_PAUSE:
862b285192aSMauro Carvalho Chehab 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
863b285192aSMauro Carvalho Chehab 		enc->flags = 0;
864b285192aSMauro Carvalho Chehab 		if (!atomic_read(&cx->ana_capturing))
865b285192aSMauro Carvalho Chehab 			return -EPERM;
866b285192aSMauro Carvalho Chehab 		if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
867b285192aSMauro Carvalho Chehab 			return 0;
868b285192aSMauro Carvalho Chehab 		h = cx18_find_handle(cx);
869b285192aSMauro Carvalho Chehab 		if (h == CX18_INVALID_TASK_HANDLE) {
8706beb1388SMauro Carvalho Chehab 			CX18_ERR("Can't find valid task handle for V4L2_ENC_CMD_PAUSE\n");
871b285192aSMauro Carvalho Chehab 			return -EBADFD;
872b285192aSMauro Carvalho Chehab 		}
873b285192aSMauro Carvalho Chehab 		cx18_mute(cx);
874b285192aSMauro Carvalho Chehab 		cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, h);
875b285192aSMauro Carvalho Chehab 		break;
876b285192aSMauro Carvalho Chehab 
877b285192aSMauro Carvalho Chehab 	case V4L2_ENC_CMD_RESUME:
878b285192aSMauro Carvalho Chehab 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
879b285192aSMauro Carvalho Chehab 		enc->flags = 0;
880b285192aSMauro Carvalho Chehab 		if (!atomic_read(&cx->ana_capturing))
881b285192aSMauro Carvalho Chehab 			return -EPERM;
882b285192aSMauro Carvalho Chehab 		if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
883b285192aSMauro Carvalho Chehab 			return 0;
884b285192aSMauro Carvalho Chehab 		h = cx18_find_handle(cx);
885b285192aSMauro Carvalho Chehab 		if (h == CX18_INVALID_TASK_HANDLE) {
8866beb1388SMauro Carvalho Chehab 			CX18_ERR("Can't find valid task handle for V4L2_ENC_CMD_RESUME\n");
887b285192aSMauro Carvalho Chehab 			return -EBADFD;
888b285192aSMauro Carvalho Chehab 		}
889b285192aSMauro Carvalho Chehab 		cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, h);
890b285192aSMauro Carvalho Chehab 		cx18_unmute(cx);
891b285192aSMauro Carvalho Chehab 		break;
892b285192aSMauro Carvalho Chehab 
893b285192aSMauro Carvalho Chehab 	default:
894b285192aSMauro Carvalho Chehab 		CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
895b285192aSMauro Carvalho Chehab 		return -EINVAL;
896b285192aSMauro Carvalho Chehab 	}
897b285192aSMauro Carvalho Chehab 	return 0;
898b285192aSMauro Carvalho Chehab }
899b285192aSMauro Carvalho Chehab 
cx18_try_encoder_cmd(struct file * file,void * fh,struct v4l2_encoder_cmd * enc)900b285192aSMauro Carvalho Chehab static int cx18_try_encoder_cmd(struct file *file, void *fh,
901b285192aSMauro Carvalho Chehab 				struct v4l2_encoder_cmd *enc)
902b285192aSMauro Carvalho Chehab {
903b285192aSMauro Carvalho Chehab 	struct cx18 *cx = fh2id(fh)->cx;
904b285192aSMauro Carvalho Chehab 
905b285192aSMauro Carvalho Chehab 	switch (enc->cmd) {
906b285192aSMauro Carvalho Chehab 	case V4L2_ENC_CMD_START:
907b285192aSMauro Carvalho Chehab 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
908b285192aSMauro Carvalho Chehab 		enc->flags = 0;
909b285192aSMauro Carvalho Chehab 		break;
910b285192aSMauro Carvalho Chehab 
911b285192aSMauro Carvalho Chehab 	case V4L2_ENC_CMD_STOP:
912b285192aSMauro Carvalho Chehab 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
913b285192aSMauro Carvalho Chehab 		enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
914b285192aSMauro Carvalho Chehab 		break;
915b285192aSMauro Carvalho Chehab 
916b285192aSMauro Carvalho Chehab 	case V4L2_ENC_CMD_PAUSE:
917b285192aSMauro Carvalho Chehab 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
918b285192aSMauro Carvalho Chehab 		enc->flags = 0;
919b285192aSMauro Carvalho Chehab 		break;
920b285192aSMauro Carvalho Chehab 
921b285192aSMauro Carvalho Chehab 	case V4L2_ENC_CMD_RESUME:
922b285192aSMauro Carvalho Chehab 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
923b285192aSMauro Carvalho Chehab 		enc->flags = 0;
924b285192aSMauro Carvalho Chehab 		break;
925b285192aSMauro Carvalho Chehab 
926b285192aSMauro Carvalho Chehab 	default:
927b285192aSMauro Carvalho Chehab 		CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
928b285192aSMauro Carvalho Chehab 		return -EINVAL;
929b285192aSMauro Carvalho Chehab 	}
930b285192aSMauro Carvalho Chehab 	return 0;
931b285192aSMauro Carvalho Chehab }
932b285192aSMauro Carvalho Chehab 
cx18_log_status(struct file * file,void * fh)933b285192aSMauro Carvalho Chehab static int cx18_log_status(struct file *file, void *fh)
934b285192aSMauro Carvalho Chehab {
935b285192aSMauro Carvalho Chehab 	struct cx18 *cx = fh2id(fh)->cx;
936b285192aSMauro Carvalho Chehab 	struct v4l2_input vidin;
937b285192aSMauro Carvalho Chehab 	struct v4l2_audio audin;
938b285192aSMauro Carvalho Chehab 	int i;
939b285192aSMauro Carvalho Chehab 
940b285192aSMauro Carvalho Chehab 	CX18_INFO("Version: %s  Card: %s\n", CX18_VERSION, cx->card_name);
941b285192aSMauro Carvalho Chehab 	if (cx->hw_flags & CX18_HW_TVEEPROM) {
942b285192aSMauro Carvalho Chehab 		struct tveeprom tv;
943b285192aSMauro Carvalho Chehab 
944b285192aSMauro Carvalho Chehab 		cx18_read_eeprom(cx, &tv);
945b285192aSMauro Carvalho Chehab 	}
946b285192aSMauro Carvalho Chehab 	cx18_call_all(cx, core, log_status);
947b285192aSMauro Carvalho Chehab 	cx18_get_input(cx, cx->active_input, &vidin);
948b285192aSMauro Carvalho Chehab 	cx18_get_audio_input(cx, cx->audio_input, &audin);
949b285192aSMauro Carvalho Chehab 	CX18_INFO("Video Input: %s\n", vidin.name);
950b285192aSMauro Carvalho Chehab 	CX18_INFO("Audio Input: %s\n", audin.name);
951b285192aSMauro Carvalho Chehab 	mutex_lock(&cx->gpio_lock);
952b285192aSMauro Carvalho Chehab 	CX18_INFO("GPIO:  direction 0x%08x, value 0x%08x\n",
953b285192aSMauro Carvalho Chehab 		cx->gpio_dir, cx->gpio_val);
954b285192aSMauro Carvalho Chehab 	mutex_unlock(&cx->gpio_lock);
955b285192aSMauro Carvalho Chehab 	CX18_INFO("Tuner: %s\n",
956b285192aSMauro Carvalho Chehab 		test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ?  "Radio" : "TV");
957b285192aSMauro Carvalho Chehab 	v4l2_ctrl_handler_log_status(&cx->cxhdl.hdl, cx->v4l2_dev.name);
958b285192aSMauro Carvalho Chehab 	CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags);
959b285192aSMauro Carvalho Chehab 	for (i = 0; i < CX18_MAX_STREAMS; i++) {
960b285192aSMauro Carvalho Chehab 		struct cx18_stream *s = &cx->streams[i];
961b285192aSMauro Carvalho Chehab 
96208569d64SHans Verkuil 		if (s->video_dev.v4l2_dev == NULL || s->buffers == 0)
963b285192aSMauro Carvalho Chehab 			continue;
964b285192aSMauro Carvalho Chehab 		CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
965b285192aSMauro Carvalho Chehab 			  s->name, s->s_flags,
966b285192aSMauro Carvalho Chehab 			  atomic_read(&s->q_full.depth) * s->bufs_per_mdl * 100
967b285192aSMauro Carvalho Chehab 			   / s->buffers,
968b285192aSMauro Carvalho Chehab 			  (s->buffers * s->buf_size) / 1024, s->buffers);
969b285192aSMauro Carvalho Chehab 	}
970b285192aSMauro Carvalho Chehab 	CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
971b285192aSMauro Carvalho Chehab 			(long long)cx->mpg_data_received,
972b285192aSMauro Carvalho Chehab 			(long long)cx->vbi_data_inserted);
973b285192aSMauro Carvalho Chehab 	return 0;
974b285192aSMauro Carvalho Chehab }
975b285192aSMauro Carvalho Chehab 
cx18_default(struct file * file,void * fh,bool valid_prio,unsigned int cmd,void * arg)976b285192aSMauro Carvalho Chehab static long cx18_default(struct file *file, void *fh, bool valid_prio,
9776d43be77SMauro Carvalho Chehab 			 unsigned int cmd, void *arg)
978b285192aSMauro Carvalho Chehab {
979b285192aSMauro Carvalho Chehab 	struct cx18 *cx = fh2id(fh)->cx;
980b285192aSMauro Carvalho Chehab 
981b285192aSMauro Carvalho Chehab 	switch (cmd) {
982b285192aSMauro Carvalho Chehab 	case VIDIOC_INT_RESET: {
983b285192aSMauro Carvalho Chehab 		u32 val = *(u32 *)arg;
984b285192aSMauro Carvalho Chehab 
985b285192aSMauro Carvalho Chehab 		if ((val == 0) || (val & 0x01))
986b285192aSMauro Carvalho Chehab 			cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL, core, reset,
987b285192aSMauro Carvalho Chehab 				     (u32) CX18_GPIO_RESET_Z8F0811);
988b285192aSMauro Carvalho Chehab 		break;
989b285192aSMauro Carvalho Chehab 	}
990b285192aSMauro Carvalho Chehab 
991b285192aSMauro Carvalho Chehab 	default:
992b285192aSMauro Carvalho Chehab 		return -ENOTTY;
993b285192aSMauro Carvalho Chehab 	}
994b285192aSMauro Carvalho Chehab 	return 0;
995b285192aSMauro Carvalho Chehab }
996b285192aSMauro Carvalho Chehab 
997b285192aSMauro Carvalho Chehab static const struct v4l2_ioctl_ops cx18_ioctl_ops = {
998b285192aSMauro Carvalho Chehab 	.vidioc_querycap                = cx18_querycap,
999b285192aSMauro Carvalho Chehab 	.vidioc_s_audio                 = cx18_s_audio,
1000b285192aSMauro Carvalho Chehab 	.vidioc_g_audio                 = cx18_g_audio,
1001b285192aSMauro Carvalho Chehab 	.vidioc_enumaudio               = cx18_enumaudio,
1002b285192aSMauro Carvalho Chehab 	.vidioc_enum_input              = cx18_enum_input,
10035200ab6aSHans Verkuil 	.vidioc_g_pixelaspect           = cx18_g_pixelaspect,
100455cda4abSHans Verkuil 	.vidioc_g_selection             = cx18_g_selection,
1005b285192aSMauro Carvalho Chehab 	.vidioc_g_input                 = cx18_g_input,
1006b285192aSMauro Carvalho Chehab 	.vidioc_s_input                 = cx18_s_input,
1007b285192aSMauro Carvalho Chehab 	.vidioc_g_frequency             = cx18_g_frequency,
1008b285192aSMauro Carvalho Chehab 	.vidioc_s_frequency             = cx18_s_frequency,
1009b285192aSMauro Carvalho Chehab 	.vidioc_s_tuner                 = cx18_s_tuner,
1010b285192aSMauro Carvalho Chehab 	.vidioc_g_tuner                 = cx18_g_tuner,
1011b285192aSMauro Carvalho Chehab 	.vidioc_g_enc_index             = cx18_g_enc_index,
1012b285192aSMauro Carvalho Chehab 	.vidioc_g_std                   = cx18_g_std,
1013b285192aSMauro Carvalho Chehab 	.vidioc_s_std                   = cx18_s_std,
1014b285192aSMauro Carvalho Chehab 	.vidioc_log_status              = cx18_log_status,
1015b285192aSMauro Carvalho Chehab 	.vidioc_enum_fmt_vid_cap        = cx18_enum_fmt_vid_cap,
1016b285192aSMauro Carvalho Chehab 	.vidioc_encoder_cmd             = cx18_encoder_cmd,
1017b285192aSMauro Carvalho Chehab 	.vidioc_try_encoder_cmd         = cx18_try_encoder_cmd,
1018b285192aSMauro Carvalho Chehab 	.vidioc_g_fmt_vid_cap           = cx18_g_fmt_vid_cap,
1019b285192aSMauro Carvalho Chehab 	.vidioc_g_fmt_vbi_cap           = cx18_g_fmt_vbi_cap,
1020b285192aSMauro Carvalho Chehab 	.vidioc_g_fmt_sliced_vbi_cap    = cx18_g_fmt_sliced_vbi_cap,
1021b285192aSMauro Carvalho Chehab 	.vidioc_s_fmt_vid_cap           = cx18_s_fmt_vid_cap,
1022b285192aSMauro Carvalho Chehab 	.vidioc_s_fmt_vbi_cap           = cx18_s_fmt_vbi_cap,
1023b285192aSMauro Carvalho Chehab 	.vidioc_s_fmt_sliced_vbi_cap    = cx18_s_fmt_sliced_vbi_cap,
1024b285192aSMauro Carvalho Chehab 	.vidioc_try_fmt_vid_cap         = cx18_try_fmt_vid_cap,
1025b285192aSMauro Carvalho Chehab 	.vidioc_try_fmt_vbi_cap         = cx18_try_fmt_vbi_cap,
1026b285192aSMauro Carvalho Chehab 	.vidioc_try_fmt_sliced_vbi_cap  = cx18_try_fmt_sliced_vbi_cap,
1027b285192aSMauro Carvalho Chehab 	.vidioc_g_sliced_vbi_cap        = cx18_g_sliced_vbi_cap,
1028b285192aSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
1029b285192aSMauro Carvalho Chehab 	.vidioc_g_register              = cx18_g_register,
1030b285192aSMauro Carvalho Chehab 	.vidioc_s_register              = cx18_s_register,
1031b285192aSMauro Carvalho Chehab #endif
1032b285192aSMauro Carvalho Chehab 	.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,
1044b285192aSMauro Carvalho Chehab };
1045b285192aSMauro Carvalho Chehab 
cx18_set_funcs(struct video_device * vdev)1046b285192aSMauro Carvalho Chehab void cx18_set_funcs(struct video_device *vdev)
1047b285192aSMauro Carvalho Chehab {
1048b285192aSMauro Carvalho Chehab 	vdev->ioctl_ops = &cx18_ioctl_ops;
1049b285192aSMauro Carvalho Chehab }
1050