xref: /OK3568_Linux_fs/kernel/drivers/media/pci/cx18/cx18-ioctl.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  cx18 ioctl system call
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *  Derived from ivtv-ioctl.c
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
8*4882a593Smuzhiyun  *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.net>
9*4882a593Smuzhiyun  */
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun #include "cx18-driver.h"
12*4882a593Smuzhiyun #include "cx18-io.h"
13*4882a593Smuzhiyun #include "cx18-version.h"
14*4882a593Smuzhiyun #include "cx18-mailbox.h"
15*4882a593Smuzhiyun #include "cx18-i2c.h"
16*4882a593Smuzhiyun #include "cx18-queue.h"
17*4882a593Smuzhiyun #include "cx18-fileops.h"
18*4882a593Smuzhiyun #include "cx18-vbi.h"
19*4882a593Smuzhiyun #include "cx18-audio.h"
20*4882a593Smuzhiyun #include "cx18-video.h"
21*4882a593Smuzhiyun #include "cx18-streams.h"
22*4882a593Smuzhiyun #include "cx18-ioctl.h"
23*4882a593Smuzhiyun #include "cx18-gpio.h"
24*4882a593Smuzhiyun #include "cx18-controls.h"
25*4882a593Smuzhiyun #include "cx18-cards.h"
26*4882a593Smuzhiyun #include "cx18-av-core.h"
27*4882a593Smuzhiyun #include <media/tveeprom.h>
28*4882a593Smuzhiyun #include <media/v4l2-event.h>
29*4882a593Smuzhiyun 
cx18_service2vbi(int type)30*4882a593Smuzhiyun u16 cx18_service2vbi(int type)
31*4882a593Smuzhiyun {
32*4882a593Smuzhiyun 	switch (type) {
33*4882a593Smuzhiyun 	case V4L2_SLICED_TELETEXT_B:
34*4882a593Smuzhiyun 		return CX18_SLICED_TYPE_TELETEXT_B;
35*4882a593Smuzhiyun 	case V4L2_SLICED_CAPTION_525:
36*4882a593Smuzhiyun 		return CX18_SLICED_TYPE_CAPTION_525;
37*4882a593Smuzhiyun 	case V4L2_SLICED_WSS_625:
38*4882a593Smuzhiyun 		return CX18_SLICED_TYPE_WSS_625;
39*4882a593Smuzhiyun 	case V4L2_SLICED_VPS:
40*4882a593Smuzhiyun 		return CX18_SLICED_TYPE_VPS;
41*4882a593Smuzhiyun 	default:
42*4882a593Smuzhiyun 		return 0;
43*4882a593Smuzhiyun 	}
44*4882a593Smuzhiyun }
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun /* Check if VBI services are allowed on the (field, line) for the video std */
valid_service_line(int field,int line,int is_pal)47*4882a593Smuzhiyun static int valid_service_line(int field, int line, int is_pal)
48*4882a593Smuzhiyun {
49*4882a593Smuzhiyun 	return (is_pal && line >= 6 &&
50*4882a593Smuzhiyun 		((field == 0 && line <= 23) || (field == 1 && line <= 22))) ||
51*4882a593Smuzhiyun 	       (!is_pal && line >= 10 && line < 22);
52*4882a593Smuzhiyun }
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun /*
55*4882a593Smuzhiyun  * For a (field, line, std) and inbound potential set of services for that line,
56*4882a593Smuzhiyun  * return the first valid service of those passed in the incoming set for that
57*4882a593Smuzhiyun  * line in priority order:
58*4882a593Smuzhiyun  * CC, VPS, or WSS over TELETEXT for well known lines
59*4882a593Smuzhiyun  * TELETEXT, before VPS, before CC, before WSS, for other lines
60*4882a593Smuzhiyun  */
select_service_from_set(int field,int line,u16 set,int is_pal)61*4882a593Smuzhiyun static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun 	u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525);
64*4882a593Smuzhiyun 	int i;
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	set = set & valid_set;
67*4882a593Smuzhiyun 	if (set == 0 || !valid_service_line(field, line, is_pal))
68*4882a593Smuzhiyun 		return 0;
69*4882a593Smuzhiyun 	if (!is_pal) {
70*4882a593Smuzhiyun 		if (line == 21 && (set & V4L2_SLICED_CAPTION_525))
71*4882a593Smuzhiyun 			return V4L2_SLICED_CAPTION_525;
72*4882a593Smuzhiyun 	} else {
73*4882a593Smuzhiyun 		if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS))
74*4882a593Smuzhiyun 			return V4L2_SLICED_VPS;
75*4882a593Smuzhiyun 		if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625))
76*4882a593Smuzhiyun 			return V4L2_SLICED_WSS_625;
77*4882a593Smuzhiyun 		if (line == 23)
78*4882a593Smuzhiyun 			return 0;
79*4882a593Smuzhiyun 	}
80*4882a593Smuzhiyun 	for (i = 0; i < 32; i++) {
81*4882a593Smuzhiyun 		if (BIT(i) & set)
82*4882a593Smuzhiyun 			return 1 << i;
83*4882a593Smuzhiyun 	}
84*4882a593Smuzhiyun 	return 0;
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun /*
88*4882a593Smuzhiyun  * Expand the service_set of *fmt into valid service_lines for the std,
89*4882a593Smuzhiyun  * and clear the passed in fmt->service_set
90*4882a593Smuzhiyun  */
cx18_expand_service_set(struct v4l2_sliced_vbi_format * fmt,int is_pal)91*4882a593Smuzhiyun void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun 	u16 set = fmt->service_set;
94*4882a593Smuzhiyun 	int f, l;
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	fmt->service_set = 0;
97*4882a593Smuzhiyun 	for (f = 0; f < 2; f++) {
98*4882a593Smuzhiyun 		for (l = 0; l < 24; l++)
99*4882a593Smuzhiyun 			fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal);
100*4882a593Smuzhiyun 	}
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun /*
104*4882a593Smuzhiyun  * Sanitize the service_lines in *fmt per the video std, and return 1
105*4882a593Smuzhiyun  * if any service_line is left as valid after santization
106*4882a593Smuzhiyun  */
check_service_set(struct v4l2_sliced_vbi_format * fmt,int is_pal)107*4882a593Smuzhiyun static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
108*4882a593Smuzhiyun {
109*4882a593Smuzhiyun 	int f, l;
110*4882a593Smuzhiyun 	u16 set = 0;
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	for (f = 0; f < 2; f++) {
113*4882a593Smuzhiyun 		for (l = 0; l < 24; l++) {
114*4882a593Smuzhiyun 			fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);
115*4882a593Smuzhiyun 			set |= fmt->service_lines[f][l];
116*4882a593Smuzhiyun 		}
117*4882a593Smuzhiyun 	}
118*4882a593Smuzhiyun 	return set != 0;
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun /* Compute the service_set from the assumed valid service_lines of *fmt */
cx18_get_service_set(struct v4l2_sliced_vbi_format * fmt)122*4882a593Smuzhiyun u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt)
123*4882a593Smuzhiyun {
124*4882a593Smuzhiyun 	int f, l;
125*4882a593Smuzhiyun 	u16 set = 0;
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 	for (f = 0; f < 2; f++) {
128*4882a593Smuzhiyun 		for (l = 0; l < 24; l++)
129*4882a593Smuzhiyun 			set |= fmt->service_lines[f][l];
130*4882a593Smuzhiyun 	}
131*4882a593Smuzhiyun 	return set;
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun 
cx18_g_fmt_vid_cap(struct file * file,void * fh,struct v4l2_format * fmt)134*4882a593Smuzhiyun static int cx18_g_fmt_vid_cap(struct file *file, void *fh,
135*4882a593Smuzhiyun 				struct v4l2_format *fmt)
136*4882a593Smuzhiyun {
137*4882a593Smuzhiyun 	struct cx18_open_id *id = fh2id(fh);
138*4882a593Smuzhiyun 	struct cx18 *cx = id->cx;
139*4882a593Smuzhiyun 	struct cx18_stream *s = &cx->streams[id->type];
140*4882a593Smuzhiyun 	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	pixfmt->width = cx->cxhdl.width;
143*4882a593Smuzhiyun 	pixfmt->height = cx->cxhdl.height;
144*4882a593Smuzhiyun 	pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
145*4882a593Smuzhiyun 	pixfmt->field = V4L2_FIELD_INTERLACED;
146*4882a593Smuzhiyun 	if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
147*4882a593Smuzhiyun 		pixfmt->pixelformat = s->pixelformat;
148*4882a593Smuzhiyun 		pixfmt->sizeimage = s->vb_bytes_per_frame;
149*4882a593Smuzhiyun 		pixfmt->bytesperline = s->vb_bytes_per_line;
150*4882a593Smuzhiyun 	} else {
151*4882a593Smuzhiyun 		pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
152*4882a593Smuzhiyun 		pixfmt->sizeimage = 128 * 1024;
153*4882a593Smuzhiyun 		pixfmt->bytesperline = 0;
154*4882a593Smuzhiyun 	}
155*4882a593Smuzhiyun 	return 0;
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun 
cx18_g_fmt_vbi_cap(struct file * file,void * fh,struct v4l2_format * fmt)158*4882a593Smuzhiyun static int cx18_g_fmt_vbi_cap(struct file *file, void *fh,
159*4882a593Smuzhiyun 				struct v4l2_format *fmt)
160*4882a593Smuzhiyun {
161*4882a593Smuzhiyun 	struct cx18 *cx = fh2id(fh)->cx;
162*4882a593Smuzhiyun 	struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi;
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	vbifmt->sampling_rate = 27000000;
165*4882a593Smuzhiyun 	vbifmt->offset = 248; /* FIXME - slightly wrong for both 50 & 60 Hz */
166*4882a593Smuzhiyun 	vbifmt->samples_per_line = VBI_ACTIVE_SAMPLES - 4;
167*4882a593Smuzhiyun 	vbifmt->sample_format = V4L2_PIX_FMT_GREY;
168*4882a593Smuzhiyun 	vbifmt->start[0] = cx->vbi.start[0];
169*4882a593Smuzhiyun 	vbifmt->start[1] = cx->vbi.start[1];
170*4882a593Smuzhiyun 	vbifmt->count[0] = vbifmt->count[1] = cx->vbi.count;
171*4882a593Smuzhiyun 	vbifmt->flags = 0;
172*4882a593Smuzhiyun 	vbifmt->reserved[0] = 0;
173*4882a593Smuzhiyun 	vbifmt->reserved[1] = 0;
174*4882a593Smuzhiyun 	return 0;
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun 
cx18_g_fmt_sliced_vbi_cap(struct file * file,void * fh,struct v4l2_format * fmt)177*4882a593Smuzhiyun static int cx18_g_fmt_sliced_vbi_cap(struct file *file, void *fh,
178*4882a593Smuzhiyun 					struct v4l2_format *fmt)
179*4882a593Smuzhiyun {
180*4882a593Smuzhiyun 	struct cx18 *cx = fh2id(fh)->cx;
181*4882a593Smuzhiyun 	struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun 	/* sane, V4L2 spec compliant, defaults */
184*4882a593Smuzhiyun 	vbifmt->reserved[0] = 0;
185*4882a593Smuzhiyun 	vbifmt->reserved[1] = 0;
186*4882a593Smuzhiyun 	vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
187*4882a593Smuzhiyun 	memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
188*4882a593Smuzhiyun 	vbifmt->service_set = 0;
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 	/*
191*4882a593Smuzhiyun 	 * Fetch the configured service_lines and total service_set from the
192*4882a593Smuzhiyun 	 * digitizer/slicer.  Note, cx18_av_vbi() wipes the passed in
193*4882a593Smuzhiyun 	 * fmt->fmt.sliced under valid calling conditions
194*4882a593Smuzhiyun 	 */
195*4882a593Smuzhiyun 	if (v4l2_subdev_call(cx->sd_av, vbi, g_sliced_fmt, &fmt->fmt.sliced))
196*4882a593Smuzhiyun 		return -EINVAL;
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	vbifmt->service_set = cx18_get_service_set(vbifmt);
199*4882a593Smuzhiyun 	return 0;
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun 
cx18_try_fmt_vid_cap(struct file * file,void * fh,struct v4l2_format * fmt)202*4882a593Smuzhiyun static int cx18_try_fmt_vid_cap(struct file *file, void *fh,
203*4882a593Smuzhiyun 				struct v4l2_format *fmt)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun 	struct cx18_open_id *id = fh2id(fh);
206*4882a593Smuzhiyun 	struct cx18 *cx = id->cx;
207*4882a593Smuzhiyun 	int w = fmt->fmt.pix.width;
208*4882a593Smuzhiyun 	int h = fmt->fmt.pix.height;
209*4882a593Smuzhiyun 	int min_h = 2;
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 	w = min(w, 720);
212*4882a593Smuzhiyun 	w = max(w, 2);
213*4882a593Smuzhiyun 	if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
214*4882a593Smuzhiyun 		/* YUV height must be a multiple of 32 */
215*4882a593Smuzhiyun 		h &= ~0x1f;
216*4882a593Smuzhiyun 		min_h = 32;
217*4882a593Smuzhiyun 	}
218*4882a593Smuzhiyun 	h = min(h, cx->is_50hz ? 576 : 480);
219*4882a593Smuzhiyun 	h = max(h, min_h);
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 	fmt->fmt.pix.width = w;
222*4882a593Smuzhiyun 	fmt->fmt.pix.height = h;
223*4882a593Smuzhiyun 	return 0;
224*4882a593Smuzhiyun }
225*4882a593Smuzhiyun 
cx18_try_fmt_vbi_cap(struct file * file,void * fh,struct v4l2_format * fmt)226*4882a593Smuzhiyun static int cx18_try_fmt_vbi_cap(struct file *file, void *fh,
227*4882a593Smuzhiyun 				struct v4l2_format *fmt)
228*4882a593Smuzhiyun {
229*4882a593Smuzhiyun 	return cx18_g_fmt_vbi_cap(file, fh, fmt);
230*4882a593Smuzhiyun }
231*4882a593Smuzhiyun 
cx18_try_fmt_sliced_vbi_cap(struct file * file,void * fh,struct v4l2_format * fmt)232*4882a593Smuzhiyun static int cx18_try_fmt_sliced_vbi_cap(struct file *file, void *fh,
233*4882a593Smuzhiyun 					struct v4l2_format *fmt)
234*4882a593Smuzhiyun {
235*4882a593Smuzhiyun 	struct cx18 *cx = fh2id(fh)->cx;
236*4882a593Smuzhiyun 	struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun 	vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
239*4882a593Smuzhiyun 	vbifmt->reserved[0] = 0;
240*4882a593Smuzhiyun 	vbifmt->reserved[1] = 0;
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 	/* If given a service set, expand it validly & clear passed in set */
243*4882a593Smuzhiyun 	if (vbifmt->service_set)
244*4882a593Smuzhiyun 		cx18_expand_service_set(vbifmt, cx->is_50hz);
245*4882a593Smuzhiyun 	/* Sanitize the service_lines, and compute the new set if any valid */
246*4882a593Smuzhiyun 	if (check_service_set(vbifmt, cx->is_50hz))
247*4882a593Smuzhiyun 		vbifmt->service_set = cx18_get_service_set(vbifmt);
248*4882a593Smuzhiyun 	return 0;
249*4882a593Smuzhiyun }
250*4882a593Smuzhiyun 
cx18_s_fmt_vid_cap(struct file * file,void * fh,struct v4l2_format * fmt)251*4882a593Smuzhiyun static int cx18_s_fmt_vid_cap(struct file *file, void *fh,
252*4882a593Smuzhiyun 				struct v4l2_format *fmt)
253*4882a593Smuzhiyun {
254*4882a593Smuzhiyun 	struct cx18_open_id *id = fh2id(fh);
255*4882a593Smuzhiyun 	struct cx18 *cx = id->cx;
256*4882a593Smuzhiyun 	struct v4l2_subdev_format format = {
257*4882a593Smuzhiyun 		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
258*4882a593Smuzhiyun 	};
259*4882a593Smuzhiyun 	struct cx18_stream *s = &cx->streams[id->type];
260*4882a593Smuzhiyun 	int ret;
261*4882a593Smuzhiyun 	int w, h;
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	ret = cx18_try_fmt_vid_cap(file, fh, fmt);
264*4882a593Smuzhiyun 	if (ret)
265*4882a593Smuzhiyun 		return ret;
266*4882a593Smuzhiyun 	w = fmt->fmt.pix.width;
267*4882a593Smuzhiyun 	h = fmt->fmt.pix.height;
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	if (cx->cxhdl.width == w && cx->cxhdl.height == h &&
270*4882a593Smuzhiyun 	    s->pixelformat == fmt->fmt.pix.pixelformat)
271*4882a593Smuzhiyun 		return 0;
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun 	if (atomic_read(&cx->ana_capturing) > 0)
274*4882a593Smuzhiyun 		return -EBUSY;
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun 	s->pixelformat = fmt->fmt.pix.pixelformat;
277*4882a593Smuzhiyun 	/* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
278*4882a593Smuzhiyun 	   UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
279*4882a593Smuzhiyun 	if (s->pixelformat == V4L2_PIX_FMT_HM12) {
280*4882a593Smuzhiyun 		s->vb_bytes_per_frame = h * 720 * 3 / 2;
281*4882a593Smuzhiyun 		s->vb_bytes_per_line = 720; /* First plane */
282*4882a593Smuzhiyun 	} else {
283*4882a593Smuzhiyun 		s->vb_bytes_per_frame = h * 720 * 2;
284*4882a593Smuzhiyun 		s->vb_bytes_per_line = 1440; /* Packed */
285*4882a593Smuzhiyun 	}
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 	format.format.width = cx->cxhdl.width = w;
288*4882a593Smuzhiyun 	format.format.height = cx->cxhdl.height = h;
289*4882a593Smuzhiyun 	format.format.code = MEDIA_BUS_FMT_FIXED;
290*4882a593Smuzhiyun 	v4l2_subdev_call(cx->sd_av, pad, set_fmt, NULL, &format);
291*4882a593Smuzhiyun 	return cx18_g_fmt_vid_cap(file, fh, fmt);
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun 
cx18_s_fmt_vbi_cap(struct file * file,void * fh,struct v4l2_format * fmt)294*4882a593Smuzhiyun static int cx18_s_fmt_vbi_cap(struct file *file, void *fh,
295*4882a593Smuzhiyun 				struct v4l2_format *fmt)
296*4882a593Smuzhiyun {
297*4882a593Smuzhiyun 	struct cx18_open_id *id = fh2id(fh);
298*4882a593Smuzhiyun 	struct cx18 *cx = id->cx;
299*4882a593Smuzhiyun 	int ret;
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 	/*
302*4882a593Smuzhiyun 	 * Changing the Encoder's Raw VBI parameters won't have any effect
303*4882a593Smuzhiyun 	 * if any analog capture is ongoing
304*4882a593Smuzhiyun 	 */
305*4882a593Smuzhiyun 	if (!cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
306*4882a593Smuzhiyun 		return -EBUSY;
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 	/*
309*4882a593Smuzhiyun 	 * Set the digitizer registers for raw active VBI.
310*4882a593Smuzhiyun 	 * Note cx18_av_vbi_wipes out a lot of the passed in fmt under valid
311*4882a593Smuzhiyun 	 * calling conditions
312*4882a593Smuzhiyun 	 */
313*4882a593Smuzhiyun 	ret = v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &fmt->fmt.vbi);
314*4882a593Smuzhiyun 	if (ret)
315*4882a593Smuzhiyun 		return ret;
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun 	/* Store our new v4l2 (non-)sliced VBI state */
318*4882a593Smuzhiyun 	cx->vbi.sliced_in->service_set = 0;
319*4882a593Smuzhiyun 	cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
320*4882a593Smuzhiyun 
321*4882a593Smuzhiyun 	return cx18_g_fmt_vbi_cap(file, fh, fmt);
322*4882a593Smuzhiyun }
323*4882a593Smuzhiyun 
cx18_s_fmt_sliced_vbi_cap(struct file * file,void * fh,struct v4l2_format * fmt)324*4882a593Smuzhiyun static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh,
325*4882a593Smuzhiyun 					struct v4l2_format *fmt)
326*4882a593Smuzhiyun {
327*4882a593Smuzhiyun 	struct cx18_open_id *id = fh2id(fh);
328*4882a593Smuzhiyun 	struct cx18 *cx = id->cx;
329*4882a593Smuzhiyun 	int ret;
330*4882a593Smuzhiyun 	struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun 	cx18_try_fmt_sliced_vbi_cap(file, fh, fmt);
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun 	/*
335*4882a593Smuzhiyun 	 * Changing the Encoder's Raw VBI parameters won't have any effect
336*4882a593Smuzhiyun 	 * if any analog capture is ongoing
337*4882a593Smuzhiyun 	 */
338*4882a593Smuzhiyun 	if (cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
339*4882a593Smuzhiyun 		return -EBUSY;
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun 	/*
342*4882a593Smuzhiyun 	 * Set the service_lines requested in the digitizer/slicer registers.
343*4882a593Smuzhiyun 	 * Note, cx18_av_vbi() wipes some "impossible" service lines in the
344*4882a593Smuzhiyun 	 * passed in fmt->fmt.sliced under valid calling conditions
345*4882a593Smuzhiyun 	 */
346*4882a593Smuzhiyun 	ret = v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &fmt->fmt.sliced);
347*4882a593Smuzhiyun 	if (ret)
348*4882a593Smuzhiyun 		return ret;
349*4882a593Smuzhiyun 	/* Store our current v4l2 sliced VBI settings */
350*4882a593Smuzhiyun 	cx->vbi.in.type =  V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
351*4882a593Smuzhiyun 	memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in));
352*4882a593Smuzhiyun 	return 0;
353*4882a593Smuzhiyun }
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun #ifdef CONFIG_VIDEO_ADV_DEBUG
cx18_g_register(struct file * file,void * fh,struct v4l2_dbg_register * reg)356*4882a593Smuzhiyun static int cx18_g_register(struct file *file, void *fh,
357*4882a593Smuzhiyun 				struct v4l2_dbg_register *reg)
358*4882a593Smuzhiyun {
359*4882a593Smuzhiyun 	struct cx18 *cx = fh2id(fh)->cx;
360*4882a593Smuzhiyun 
361*4882a593Smuzhiyun 	if (reg->reg & 0x3)
362*4882a593Smuzhiyun 		return -EINVAL;
363*4882a593Smuzhiyun 	if (reg->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE)
364*4882a593Smuzhiyun 		return -EINVAL;
365*4882a593Smuzhiyun 	reg->size = 4;
366*4882a593Smuzhiyun 	reg->val = cx18_read_enc(cx, reg->reg);
367*4882a593Smuzhiyun 	return 0;
368*4882a593Smuzhiyun }
369*4882a593Smuzhiyun 
cx18_s_register(struct file * file,void * fh,const struct v4l2_dbg_register * reg)370*4882a593Smuzhiyun static int cx18_s_register(struct file *file, void *fh,
371*4882a593Smuzhiyun 				const struct v4l2_dbg_register *reg)
372*4882a593Smuzhiyun {
373*4882a593Smuzhiyun 	struct cx18 *cx = fh2id(fh)->cx;
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 	if (reg->reg & 0x3)
376*4882a593Smuzhiyun 		return -EINVAL;
377*4882a593Smuzhiyun 	if (reg->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE)
378*4882a593Smuzhiyun 		return -EINVAL;
379*4882a593Smuzhiyun 	cx18_write_enc(cx, reg->val, reg->reg);
380*4882a593Smuzhiyun 	return 0;
381*4882a593Smuzhiyun }
382*4882a593Smuzhiyun #endif
383*4882a593Smuzhiyun 
cx18_querycap(struct file * file,void * fh,struct v4l2_capability * vcap)384*4882a593Smuzhiyun static int cx18_querycap(struct file *file, void *fh,
385*4882a593Smuzhiyun 				struct v4l2_capability *vcap)
386*4882a593Smuzhiyun {
387*4882a593Smuzhiyun 	struct cx18_open_id *id = fh2id(fh);
388*4882a593Smuzhiyun 	struct cx18 *cx = id->cx;
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun 	strscpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver));
391*4882a593Smuzhiyun 	strscpy(vcap->card, cx->card_name, sizeof(vcap->card));
392*4882a593Smuzhiyun 	snprintf(vcap->bus_info, sizeof(vcap->bus_info),
393*4882a593Smuzhiyun 		 "PCI:%s", pci_name(cx->pci_dev));
394*4882a593Smuzhiyun 	vcap->capabilities = cx->v4l2_cap | V4L2_CAP_DEVICE_CAPS;
395*4882a593Smuzhiyun 	return 0;
396*4882a593Smuzhiyun }
397*4882a593Smuzhiyun 
cx18_enumaudio(struct file * file,void * fh,struct v4l2_audio * vin)398*4882a593Smuzhiyun static int cx18_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin)
399*4882a593Smuzhiyun {
400*4882a593Smuzhiyun 	struct cx18 *cx = fh2id(fh)->cx;
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	return cx18_get_audio_input(cx, vin->index, vin);
403*4882a593Smuzhiyun }
404*4882a593Smuzhiyun 
cx18_g_audio(struct file * file,void * fh,struct v4l2_audio * vin)405*4882a593Smuzhiyun static int cx18_g_audio(struct file *file, void *fh, struct v4l2_audio *vin)
406*4882a593Smuzhiyun {
407*4882a593Smuzhiyun 	struct cx18 *cx = fh2id(fh)->cx;
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun 	vin->index = cx->audio_input;
410*4882a593Smuzhiyun 	return cx18_get_audio_input(cx, vin->index, vin);
411*4882a593Smuzhiyun }
412*4882a593Smuzhiyun 
cx18_s_audio(struct file * file,void * fh,const struct v4l2_audio * vout)413*4882a593Smuzhiyun static int cx18_s_audio(struct file *file, void *fh, const struct v4l2_audio *vout)
414*4882a593Smuzhiyun {
415*4882a593Smuzhiyun 	struct cx18 *cx = fh2id(fh)->cx;
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun 	if (vout->index >= cx->nof_audio_inputs)
418*4882a593Smuzhiyun 		return -EINVAL;
419*4882a593Smuzhiyun 	cx->audio_input = vout->index;
420*4882a593Smuzhiyun 	cx18_audio_set_io(cx);
421*4882a593Smuzhiyun 	return 0;
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun 
cx18_enum_input(struct file * file,void * fh,struct v4l2_input * vin)424*4882a593Smuzhiyun static int cx18_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
425*4882a593Smuzhiyun {
426*4882a593Smuzhiyun 	struct cx18 *cx = fh2id(fh)->cx;
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun 	/* set it to defaults from our table */
429*4882a593Smuzhiyun 	return cx18_get_input(cx, vin->index, vin);
430*4882a593Smuzhiyun }
431*4882a593Smuzhiyun 
cx18_g_pixelaspect(struct file * file,void * fh,int type,struct v4l2_fract * f)432*4882a593Smuzhiyun static int cx18_g_pixelaspect(struct file *file, void *fh,
433*4882a593Smuzhiyun 			      int type, struct v4l2_fract *f)
434*4882a593Smuzhiyun {
435*4882a593Smuzhiyun 	struct cx18 *cx = fh2id(fh)->cx;
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun 	if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
438*4882a593Smuzhiyun 		return -EINVAL;
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun 	f->numerator = cx->is_50hz ? 54 : 11;
441*4882a593Smuzhiyun 	f->denominator = cx->is_50hz ? 59 : 10;
442*4882a593Smuzhiyun 	return 0;
443*4882a593Smuzhiyun }
444*4882a593Smuzhiyun 
cx18_g_selection(struct file * file,void * fh,struct v4l2_selection * sel)445*4882a593Smuzhiyun static int cx18_g_selection(struct file *file, void *fh,
446*4882a593Smuzhiyun 			    struct v4l2_selection *sel)
447*4882a593Smuzhiyun {
448*4882a593Smuzhiyun 	struct cx18 *cx = fh2id(fh)->cx;
449*4882a593Smuzhiyun 
450*4882a593Smuzhiyun 	if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
451*4882a593Smuzhiyun 		return -EINVAL;
452*4882a593Smuzhiyun 	switch (sel->target) {
453*4882a593Smuzhiyun 	case V4L2_SEL_TGT_CROP_BOUNDS:
454*4882a593Smuzhiyun 	case V4L2_SEL_TGT_CROP_DEFAULT:
455*4882a593Smuzhiyun 		sel->r.top = sel->r.left = 0;
456*4882a593Smuzhiyun 		sel->r.width = 720;
457*4882a593Smuzhiyun 		sel->r.height = cx->is_50hz ? 576 : 480;
458*4882a593Smuzhiyun 		break;
459*4882a593Smuzhiyun 	default:
460*4882a593Smuzhiyun 		return -EINVAL;
461*4882a593Smuzhiyun 	}
462*4882a593Smuzhiyun 	return 0;
463*4882a593Smuzhiyun }
464*4882a593Smuzhiyun 
cx18_enum_fmt_vid_cap(struct file * file,void * fh,struct v4l2_fmtdesc * fmt)465*4882a593Smuzhiyun static int cx18_enum_fmt_vid_cap(struct file *file, void *fh,
466*4882a593Smuzhiyun 					struct v4l2_fmtdesc *fmt)
467*4882a593Smuzhiyun {
468*4882a593Smuzhiyun 	static const struct v4l2_fmtdesc formats[] = {
469*4882a593Smuzhiyun 		{
470*4882a593Smuzhiyun 			.index = 0,
471*4882a593Smuzhiyun 			.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
472*4882a593Smuzhiyun 			.description = "HM12 (YUV 4:1:1)",
473*4882a593Smuzhiyun 			.pixelformat = V4L2_PIX_FMT_HM12,
474*4882a593Smuzhiyun 		},
475*4882a593Smuzhiyun 		{
476*4882a593Smuzhiyun 			.index = 1,
477*4882a593Smuzhiyun 			.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
478*4882a593Smuzhiyun 			.flags = V4L2_FMT_FLAG_COMPRESSED,
479*4882a593Smuzhiyun 			.description = "MPEG",
480*4882a593Smuzhiyun 			.pixelformat = V4L2_PIX_FMT_MPEG,
481*4882a593Smuzhiyun 		},
482*4882a593Smuzhiyun 		{
483*4882a593Smuzhiyun 			.index = 2,
484*4882a593Smuzhiyun 			.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
485*4882a593Smuzhiyun 			.description = "UYVY 4:2:2",
486*4882a593Smuzhiyun 			.pixelformat = V4L2_PIX_FMT_UYVY,
487*4882a593Smuzhiyun 		},
488*4882a593Smuzhiyun 	};
489*4882a593Smuzhiyun 
490*4882a593Smuzhiyun 	if (fmt->index > ARRAY_SIZE(formats) - 1)
491*4882a593Smuzhiyun 		return -EINVAL;
492*4882a593Smuzhiyun 	*fmt = formats[fmt->index];
493*4882a593Smuzhiyun 	return 0;
494*4882a593Smuzhiyun }
495*4882a593Smuzhiyun 
cx18_g_input(struct file * file,void * fh,unsigned int * i)496*4882a593Smuzhiyun static int cx18_g_input(struct file *file, void *fh, unsigned int *i)
497*4882a593Smuzhiyun {
498*4882a593Smuzhiyun 	struct cx18 *cx = fh2id(fh)->cx;
499*4882a593Smuzhiyun 
500*4882a593Smuzhiyun 	*i = cx->active_input;
501*4882a593Smuzhiyun 	return 0;
502*4882a593Smuzhiyun }
503*4882a593Smuzhiyun 
cx18_s_input(struct file * file,void * fh,unsigned int inp)504*4882a593Smuzhiyun int cx18_s_input(struct file *file, void *fh, unsigned int inp)
505*4882a593Smuzhiyun {
506*4882a593Smuzhiyun 	struct cx18_open_id *id = fh2id(fh);
507*4882a593Smuzhiyun 	struct cx18 *cx = id->cx;
508*4882a593Smuzhiyun 	v4l2_std_id std = V4L2_STD_ALL;
509*4882a593Smuzhiyun 	const struct cx18_card_video_input *card_input =
510*4882a593Smuzhiyun 				cx->card->video_inputs + inp;
511*4882a593Smuzhiyun 
512*4882a593Smuzhiyun 	if (inp >= cx->nof_inputs)
513*4882a593Smuzhiyun 		return -EINVAL;
514*4882a593Smuzhiyun 
515*4882a593Smuzhiyun 	if (inp == cx->active_input) {
516*4882a593Smuzhiyun 		CX18_DEBUG_INFO("Input unchanged\n");
517*4882a593Smuzhiyun 		return 0;
518*4882a593Smuzhiyun 	}
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun 	CX18_DEBUG_INFO("Changing input from %d to %d\n",
521*4882a593Smuzhiyun 			cx->active_input, inp);
522*4882a593Smuzhiyun 
523*4882a593Smuzhiyun 	cx->active_input = inp;
524*4882a593Smuzhiyun 	/* Set the audio input to whatever is appropriate for the input type. */
525*4882a593Smuzhiyun 	cx->audio_input = cx->card->video_inputs[inp].audio_index;
526*4882a593Smuzhiyun 	if (card_input->video_type == V4L2_INPUT_TYPE_TUNER)
527*4882a593Smuzhiyun 		std = cx->tuner_std;
528*4882a593Smuzhiyun 	cx->streams[CX18_ENC_STREAM_TYPE_MPG].video_dev.tvnorms = std;
529*4882a593Smuzhiyun 	cx->streams[CX18_ENC_STREAM_TYPE_YUV].video_dev.tvnorms = std;
530*4882a593Smuzhiyun 	cx->streams[CX18_ENC_STREAM_TYPE_VBI].video_dev.tvnorms = std;
531*4882a593Smuzhiyun 
532*4882a593Smuzhiyun 	/* prevent others from messing with the streams until
533*4882a593Smuzhiyun 	   we're finished changing inputs. */
534*4882a593Smuzhiyun 	cx18_mute(cx);
535*4882a593Smuzhiyun 	cx18_video_set_io(cx);
536*4882a593Smuzhiyun 	cx18_audio_set_io(cx);
537*4882a593Smuzhiyun 	cx18_unmute(cx);
538*4882a593Smuzhiyun 	return 0;
539*4882a593Smuzhiyun }
540*4882a593Smuzhiyun 
cx18_g_frequency(struct file * file,void * fh,struct v4l2_frequency * vf)541*4882a593Smuzhiyun static int cx18_g_frequency(struct file *file, void *fh,
542*4882a593Smuzhiyun 				struct v4l2_frequency *vf)
543*4882a593Smuzhiyun {
544*4882a593Smuzhiyun 	struct cx18 *cx = fh2id(fh)->cx;
545*4882a593Smuzhiyun 
546*4882a593Smuzhiyun 	if (vf->tuner != 0)
547*4882a593Smuzhiyun 		return -EINVAL;
548*4882a593Smuzhiyun 
549*4882a593Smuzhiyun 	cx18_call_all(cx, tuner, g_frequency, vf);
550*4882a593Smuzhiyun 	return 0;
551*4882a593Smuzhiyun }
552*4882a593Smuzhiyun 
cx18_s_frequency(struct file * file,void * fh,const struct v4l2_frequency * vf)553*4882a593Smuzhiyun int cx18_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf)
554*4882a593Smuzhiyun {
555*4882a593Smuzhiyun 	struct cx18_open_id *id = fh2id(fh);
556*4882a593Smuzhiyun 	struct cx18 *cx = id->cx;
557*4882a593Smuzhiyun 
558*4882a593Smuzhiyun 	if (vf->tuner != 0)
559*4882a593Smuzhiyun 		return -EINVAL;
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun 	cx18_mute(cx);
562*4882a593Smuzhiyun 	CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency);
563*4882a593Smuzhiyun 	cx18_call_all(cx, tuner, s_frequency, vf);
564*4882a593Smuzhiyun 	cx18_unmute(cx);
565*4882a593Smuzhiyun 	return 0;
566*4882a593Smuzhiyun }
567*4882a593Smuzhiyun 
cx18_g_std(struct file * file,void * fh,v4l2_std_id * std)568*4882a593Smuzhiyun static int cx18_g_std(struct file *file, void *fh, v4l2_std_id *std)
569*4882a593Smuzhiyun {
570*4882a593Smuzhiyun 	struct cx18 *cx = fh2id(fh)->cx;
571*4882a593Smuzhiyun 
572*4882a593Smuzhiyun 	*std = cx->std;
573*4882a593Smuzhiyun 	return 0;
574*4882a593Smuzhiyun }
575*4882a593Smuzhiyun 
cx18_s_std(struct file * file,void * fh,v4l2_std_id std)576*4882a593Smuzhiyun int cx18_s_std(struct file *file, void *fh, v4l2_std_id std)
577*4882a593Smuzhiyun {
578*4882a593Smuzhiyun 	struct cx18_open_id *id = fh2id(fh);
579*4882a593Smuzhiyun 	struct cx18 *cx = id->cx;
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun 	if ((std & V4L2_STD_ALL) == 0)
582*4882a593Smuzhiyun 		return -EINVAL;
583*4882a593Smuzhiyun 
584*4882a593Smuzhiyun 	if (std == cx->std)
585*4882a593Smuzhiyun 		return 0;
586*4882a593Smuzhiyun 
587*4882a593Smuzhiyun 	if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ||
588*4882a593Smuzhiyun 	    atomic_read(&cx->ana_capturing) > 0) {
589*4882a593Smuzhiyun 		/* Switching standard would turn off the radio or mess
590*4882a593Smuzhiyun 		   with already running streams, prevent that by
591*4882a593Smuzhiyun 		   returning EBUSY. */
592*4882a593Smuzhiyun 		return -EBUSY;
593*4882a593Smuzhiyun 	}
594*4882a593Smuzhiyun 
595*4882a593Smuzhiyun 	cx->std = std;
596*4882a593Smuzhiyun 	cx->is_60hz = (std & V4L2_STD_525_60) ? 1 : 0;
597*4882a593Smuzhiyun 	cx->is_50hz = !cx->is_60hz;
598*4882a593Smuzhiyun 	cx2341x_handler_set_50hz(&cx->cxhdl, cx->is_50hz);
599*4882a593Smuzhiyun 	cx->cxhdl.width = 720;
600*4882a593Smuzhiyun 	cx->cxhdl.height = cx->is_50hz ? 576 : 480;
601*4882a593Smuzhiyun 	cx->vbi.count = cx->is_50hz ? 18 : 12;
602*4882a593Smuzhiyun 	cx->vbi.start[0] = cx->is_50hz ? 6 : 10;
603*4882a593Smuzhiyun 	cx->vbi.start[1] = cx->is_50hz ? 318 : 273;
604*4882a593Smuzhiyun 	CX18_DEBUG_INFO("Switching standard to %llx.\n",
605*4882a593Smuzhiyun 			(unsigned long long) cx->std);
606*4882a593Smuzhiyun 
607*4882a593Smuzhiyun 	/* Tuner */
608*4882a593Smuzhiyun 	cx18_call_all(cx, video, s_std, cx->std);
609*4882a593Smuzhiyun 	return 0;
610*4882a593Smuzhiyun }
611*4882a593Smuzhiyun 
cx18_s_tuner(struct file * file,void * fh,const struct v4l2_tuner * vt)612*4882a593Smuzhiyun static int cx18_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt)
613*4882a593Smuzhiyun {
614*4882a593Smuzhiyun 	struct cx18_open_id *id = fh2id(fh);
615*4882a593Smuzhiyun 	struct cx18 *cx = id->cx;
616*4882a593Smuzhiyun 
617*4882a593Smuzhiyun 	if (vt->index != 0)
618*4882a593Smuzhiyun 		return -EINVAL;
619*4882a593Smuzhiyun 
620*4882a593Smuzhiyun 	cx18_call_all(cx, tuner, s_tuner, vt);
621*4882a593Smuzhiyun 	return 0;
622*4882a593Smuzhiyun }
623*4882a593Smuzhiyun 
cx18_g_tuner(struct file * file,void * fh,struct v4l2_tuner * vt)624*4882a593Smuzhiyun static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
625*4882a593Smuzhiyun {
626*4882a593Smuzhiyun 	struct cx18 *cx = fh2id(fh)->cx;
627*4882a593Smuzhiyun 
628*4882a593Smuzhiyun 	if (vt->index != 0)
629*4882a593Smuzhiyun 		return -EINVAL;
630*4882a593Smuzhiyun 
631*4882a593Smuzhiyun 	cx18_call_all(cx, tuner, g_tuner, vt);
632*4882a593Smuzhiyun 
633*4882a593Smuzhiyun 	if (vt->type == V4L2_TUNER_RADIO)
634*4882a593Smuzhiyun 		strscpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name));
635*4882a593Smuzhiyun 	else
636*4882a593Smuzhiyun 		strscpy(vt->name, "cx18 TV Tuner", sizeof(vt->name));
637*4882a593Smuzhiyun 	return 0;
638*4882a593Smuzhiyun }
639*4882a593Smuzhiyun 
cx18_g_sliced_vbi_cap(struct file * file,void * fh,struct v4l2_sliced_vbi_cap * cap)640*4882a593Smuzhiyun static int cx18_g_sliced_vbi_cap(struct file *file, void *fh,
641*4882a593Smuzhiyun 					struct v4l2_sliced_vbi_cap *cap)
642*4882a593Smuzhiyun {
643*4882a593Smuzhiyun 	struct cx18 *cx = fh2id(fh)->cx;
644*4882a593Smuzhiyun 	int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
645*4882a593Smuzhiyun 	int f, l;
646*4882a593Smuzhiyun 
647*4882a593Smuzhiyun 	if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
648*4882a593Smuzhiyun 		return -EINVAL;
649*4882a593Smuzhiyun 
650*4882a593Smuzhiyun 	cap->service_set = 0;
651*4882a593Smuzhiyun 	for (f = 0; f < 2; f++) {
652*4882a593Smuzhiyun 		for (l = 0; l < 24; l++) {
653*4882a593Smuzhiyun 			if (valid_service_line(f, l, cx->is_50hz)) {
654*4882a593Smuzhiyun 				/*
655*4882a593Smuzhiyun 				 * We can find all v4l2 supported vbi services
656*4882a593Smuzhiyun 				 * for the standard, on a valid line for the std
657*4882a593Smuzhiyun 				 */
658*4882a593Smuzhiyun 				cap->service_lines[f][l] = set;
659*4882a593Smuzhiyun 				cap->service_set |= set;
660*4882a593Smuzhiyun 			} else
661*4882a593Smuzhiyun 				cap->service_lines[f][l] = 0;
662*4882a593Smuzhiyun 		}
663*4882a593Smuzhiyun 	}
664*4882a593Smuzhiyun 	for (f = 0; f < 3; f++)
665*4882a593Smuzhiyun 		cap->reserved[f] = 0;
666*4882a593Smuzhiyun 	return 0;
667*4882a593Smuzhiyun }
668*4882a593Smuzhiyun 
_cx18_process_idx_data(struct cx18_buffer * buf,struct v4l2_enc_idx * idx)669*4882a593Smuzhiyun static int _cx18_process_idx_data(struct cx18_buffer *buf,
670*4882a593Smuzhiyun 				  struct v4l2_enc_idx *idx)
671*4882a593Smuzhiyun {
672*4882a593Smuzhiyun 	int consumed, remaining;
673*4882a593Smuzhiyun 	struct v4l2_enc_idx_entry *e_idx;
674*4882a593Smuzhiyun 	struct cx18_enc_idx_entry *e_buf;
675*4882a593Smuzhiyun 
676*4882a593Smuzhiyun 	/* Frame type lookup: 1=I, 2=P, 4=B */
677*4882a593Smuzhiyun 	static const int mapping[8] = {
678*4882a593Smuzhiyun 		-1, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_P,
679*4882a593Smuzhiyun 		-1, V4L2_ENC_IDX_FRAME_B, -1, -1, -1
680*4882a593Smuzhiyun 	};
681*4882a593Smuzhiyun 
682*4882a593Smuzhiyun 	/*
683*4882a593Smuzhiyun 	 * Assumption here is that a buf holds an integral number of
684*4882a593Smuzhiyun 	 * struct cx18_enc_idx_entry objects and is properly aligned.
685*4882a593Smuzhiyun 	 * This is enforced by the module options on IDX buffer sizes.
686*4882a593Smuzhiyun 	 */
687*4882a593Smuzhiyun 	remaining = buf->bytesused - buf->readpos;
688*4882a593Smuzhiyun 	consumed = 0;
689*4882a593Smuzhiyun 	e_idx = &idx->entry[idx->entries];
690*4882a593Smuzhiyun 	e_buf = (struct cx18_enc_idx_entry *) &buf->buf[buf->readpos];
691*4882a593Smuzhiyun 
692*4882a593Smuzhiyun 	while (remaining >= sizeof(struct cx18_enc_idx_entry) &&
693*4882a593Smuzhiyun 	       idx->entries < V4L2_ENC_IDX_ENTRIES) {
694*4882a593Smuzhiyun 
695*4882a593Smuzhiyun 		e_idx->offset = (((u64) le32_to_cpu(e_buf->offset_high)) << 32)
696*4882a593Smuzhiyun 				| le32_to_cpu(e_buf->offset_low);
697*4882a593Smuzhiyun 
698*4882a593Smuzhiyun 		e_idx->pts = (((u64) (le32_to_cpu(e_buf->pts_high) & 1)) << 32)
699*4882a593Smuzhiyun 			     | le32_to_cpu(e_buf->pts_low);
700*4882a593Smuzhiyun 
701*4882a593Smuzhiyun 		e_idx->length = le32_to_cpu(e_buf->length);
702*4882a593Smuzhiyun 
703*4882a593Smuzhiyun 		e_idx->flags = mapping[le32_to_cpu(e_buf->flags) & 0x7];
704*4882a593Smuzhiyun 
705*4882a593Smuzhiyun 		e_idx->reserved[0] = 0;
706*4882a593Smuzhiyun 		e_idx->reserved[1] = 0;
707*4882a593Smuzhiyun 
708*4882a593Smuzhiyun 		idx->entries++;
709*4882a593Smuzhiyun 		e_idx = &idx->entry[idx->entries];
710*4882a593Smuzhiyun 		e_buf++;
711*4882a593Smuzhiyun 
712*4882a593Smuzhiyun 		remaining -= sizeof(struct cx18_enc_idx_entry);
713*4882a593Smuzhiyun 		consumed += sizeof(struct cx18_enc_idx_entry);
714*4882a593Smuzhiyun 	}
715*4882a593Smuzhiyun 
716*4882a593Smuzhiyun 	/* Swallow any partial entries at the end, if there are any */
717*4882a593Smuzhiyun 	if (remaining > 0 && remaining < sizeof(struct cx18_enc_idx_entry))
718*4882a593Smuzhiyun 		consumed += remaining;
719*4882a593Smuzhiyun 
720*4882a593Smuzhiyun 	buf->readpos += consumed;
721*4882a593Smuzhiyun 	return consumed;
722*4882a593Smuzhiyun }
723*4882a593Smuzhiyun 
cx18_process_idx_data(struct cx18_stream * s,struct cx18_mdl * mdl,struct v4l2_enc_idx * idx)724*4882a593Smuzhiyun static int cx18_process_idx_data(struct cx18_stream *s, struct cx18_mdl *mdl,
725*4882a593Smuzhiyun 				 struct v4l2_enc_idx *idx)
726*4882a593Smuzhiyun {
727*4882a593Smuzhiyun 	if (s->type != CX18_ENC_STREAM_TYPE_IDX)
728*4882a593Smuzhiyun 		return -EINVAL;
729*4882a593Smuzhiyun 
730*4882a593Smuzhiyun 	if (mdl->curr_buf == NULL)
731*4882a593Smuzhiyun 		mdl->curr_buf = list_first_entry(&mdl->buf_list,
732*4882a593Smuzhiyun 						 struct cx18_buffer, list);
733*4882a593Smuzhiyun 
734*4882a593Smuzhiyun 	if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) {
735*4882a593Smuzhiyun 		/*
736*4882a593Smuzhiyun 		 * For some reason we've exhausted the buffers, but the MDL
737*4882a593Smuzhiyun 		 * object still said some data was unread.
738*4882a593Smuzhiyun 		 * Fix that and bail out.
739*4882a593Smuzhiyun 		 */
740*4882a593Smuzhiyun 		mdl->readpos = mdl->bytesused;
741*4882a593Smuzhiyun 		return 0;
742*4882a593Smuzhiyun 	}
743*4882a593Smuzhiyun 
744*4882a593Smuzhiyun 	list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) {
745*4882a593Smuzhiyun 
746*4882a593Smuzhiyun 		/* Skip any empty buffers in the MDL */
747*4882a593Smuzhiyun 		if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused)
748*4882a593Smuzhiyun 			continue;
749*4882a593Smuzhiyun 
750*4882a593Smuzhiyun 		mdl->readpos += _cx18_process_idx_data(mdl->curr_buf, idx);
751*4882a593Smuzhiyun 
752*4882a593Smuzhiyun 		/* exit when MDL drained or request satisfied */
753*4882a593Smuzhiyun 		if (idx->entries >= V4L2_ENC_IDX_ENTRIES ||
754*4882a593Smuzhiyun 		    mdl->curr_buf->readpos < mdl->curr_buf->bytesused ||
755*4882a593Smuzhiyun 		    mdl->readpos >= mdl->bytesused)
756*4882a593Smuzhiyun 			break;
757*4882a593Smuzhiyun 	}
758*4882a593Smuzhiyun 	return 0;
759*4882a593Smuzhiyun }
760*4882a593Smuzhiyun 
cx18_g_enc_index(struct file * file,void * fh,struct v4l2_enc_idx * idx)761*4882a593Smuzhiyun static int cx18_g_enc_index(struct file *file, void *fh,
762*4882a593Smuzhiyun 				struct v4l2_enc_idx *idx)
763*4882a593Smuzhiyun {
764*4882a593Smuzhiyun 	struct cx18 *cx = fh2id(fh)->cx;
765*4882a593Smuzhiyun 	struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
766*4882a593Smuzhiyun 	s32 tmp;
767*4882a593Smuzhiyun 	struct cx18_mdl *mdl;
768*4882a593Smuzhiyun 
769*4882a593Smuzhiyun 	if (!cx18_stream_enabled(s)) /* Module options inhibited IDX stream */
770*4882a593Smuzhiyun 		return -EINVAL;
771*4882a593Smuzhiyun 
772*4882a593Smuzhiyun 	/* Compute the best case number of entries we can buffer */
773*4882a593Smuzhiyun 	tmp = s->buffers -
774*4882a593Smuzhiyun 			  s->bufs_per_mdl * CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN;
775*4882a593Smuzhiyun 	if (tmp <= 0)
776*4882a593Smuzhiyun 		tmp = 1;
777*4882a593Smuzhiyun 	tmp = tmp * s->buf_size / sizeof(struct cx18_enc_idx_entry);
778*4882a593Smuzhiyun 
779*4882a593Smuzhiyun 	/* Fill out the header of the return structure */
780*4882a593Smuzhiyun 	idx->entries = 0;
781*4882a593Smuzhiyun 	idx->entries_cap = tmp;
782*4882a593Smuzhiyun 	memset(idx->reserved, 0, sizeof(idx->reserved));
783*4882a593Smuzhiyun 
784*4882a593Smuzhiyun 	/* Pull IDX MDLs and buffers from q_full and populate the entries */
785*4882a593Smuzhiyun 	do {
786*4882a593Smuzhiyun 		mdl = cx18_dequeue(s, &s->q_full);
787*4882a593Smuzhiyun 		if (mdl == NULL) /* No more IDX data right now */
788*4882a593Smuzhiyun 			break;
789*4882a593Smuzhiyun 
790*4882a593Smuzhiyun 		/* Extract the Index entry data from the MDL and buffers */
791*4882a593Smuzhiyun 		cx18_process_idx_data(s, mdl, idx);
792*4882a593Smuzhiyun 		if (mdl->readpos < mdl->bytesused) {
793*4882a593Smuzhiyun 			/* We finished with data remaining, push the MDL back */
794*4882a593Smuzhiyun 			cx18_push(s, mdl, &s->q_full);
795*4882a593Smuzhiyun 			break;
796*4882a593Smuzhiyun 		}
797*4882a593Smuzhiyun 
798*4882a593Smuzhiyun 		/* We drained this MDL, schedule it to go to the firmware */
799*4882a593Smuzhiyun 		cx18_enqueue(s, mdl, &s->q_free);
800*4882a593Smuzhiyun 
801*4882a593Smuzhiyun 	} while (idx->entries < V4L2_ENC_IDX_ENTRIES);
802*4882a593Smuzhiyun 
803*4882a593Smuzhiyun 	/* Tell the work handler to send free IDX MDLs to the firmware */
804*4882a593Smuzhiyun 	cx18_stream_load_fw_queue(s);
805*4882a593Smuzhiyun 	return 0;
806*4882a593Smuzhiyun }
807*4882a593Smuzhiyun 
cx18_vb_queue(struct cx18_open_id * id)808*4882a593Smuzhiyun static struct videobuf_queue *cx18_vb_queue(struct cx18_open_id *id)
809*4882a593Smuzhiyun {
810*4882a593Smuzhiyun 	struct videobuf_queue *q = NULL;
811*4882a593Smuzhiyun 	struct cx18 *cx = id->cx;
812*4882a593Smuzhiyun 	struct cx18_stream *s = &cx->streams[id->type];
813*4882a593Smuzhiyun 
814*4882a593Smuzhiyun 	switch (s->vb_type) {
815*4882a593Smuzhiyun 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
816*4882a593Smuzhiyun 		q = &s->vbuf_q;
817*4882a593Smuzhiyun 		break;
818*4882a593Smuzhiyun 	case V4L2_BUF_TYPE_VBI_CAPTURE:
819*4882a593Smuzhiyun 		break;
820*4882a593Smuzhiyun 	default:
821*4882a593Smuzhiyun 		break;
822*4882a593Smuzhiyun 	}
823*4882a593Smuzhiyun 	return q;
824*4882a593Smuzhiyun }
825*4882a593Smuzhiyun 
cx18_streamon(struct file * file,void * priv,enum v4l2_buf_type type)826*4882a593Smuzhiyun static int cx18_streamon(struct file *file, void *priv,
827*4882a593Smuzhiyun 	enum v4l2_buf_type type)
828*4882a593Smuzhiyun {
829*4882a593Smuzhiyun 	struct cx18_open_id *id = file->private_data;
830*4882a593Smuzhiyun 	struct cx18 *cx = id->cx;
831*4882a593Smuzhiyun 	struct cx18_stream *s = &cx->streams[id->type];
832*4882a593Smuzhiyun 
833*4882a593Smuzhiyun 	/* Start the hardware only if we're the video device */
834*4882a593Smuzhiyun 	if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
835*4882a593Smuzhiyun 		(s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
836*4882a593Smuzhiyun 		return -EINVAL;
837*4882a593Smuzhiyun 
838*4882a593Smuzhiyun 	if (id->type != CX18_ENC_STREAM_TYPE_YUV)
839*4882a593Smuzhiyun 		return -EINVAL;
840*4882a593Smuzhiyun 
841*4882a593Smuzhiyun 	/* Establish a buffer timeout */
842*4882a593Smuzhiyun 	mod_timer(&s->vb_timeout, msecs_to_jiffies(2000) + jiffies);
843*4882a593Smuzhiyun 
844*4882a593Smuzhiyun 	return videobuf_streamon(cx18_vb_queue(id));
845*4882a593Smuzhiyun }
846*4882a593Smuzhiyun 
cx18_streamoff(struct file * file,void * priv,enum v4l2_buf_type type)847*4882a593Smuzhiyun static int cx18_streamoff(struct file *file, void *priv,
848*4882a593Smuzhiyun 	enum v4l2_buf_type type)
849*4882a593Smuzhiyun {
850*4882a593Smuzhiyun 	struct cx18_open_id *id = file->private_data;
851*4882a593Smuzhiyun 	struct cx18 *cx = id->cx;
852*4882a593Smuzhiyun 	struct cx18_stream *s = &cx->streams[id->type];
853*4882a593Smuzhiyun 
854*4882a593Smuzhiyun 	/* Start the hardware only if we're the video device */
855*4882a593Smuzhiyun 	if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
856*4882a593Smuzhiyun 		(s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
857*4882a593Smuzhiyun 		return -EINVAL;
858*4882a593Smuzhiyun 
859*4882a593Smuzhiyun 	if (id->type != CX18_ENC_STREAM_TYPE_YUV)
860*4882a593Smuzhiyun 		return -EINVAL;
861*4882a593Smuzhiyun 
862*4882a593Smuzhiyun 	return videobuf_streamoff(cx18_vb_queue(id));
863*4882a593Smuzhiyun }
864*4882a593Smuzhiyun 
cx18_reqbufs(struct file * file,void * priv,struct v4l2_requestbuffers * rb)865*4882a593Smuzhiyun static int cx18_reqbufs(struct file *file, void *priv,
866*4882a593Smuzhiyun 	struct v4l2_requestbuffers *rb)
867*4882a593Smuzhiyun {
868*4882a593Smuzhiyun 	struct cx18_open_id *id = file->private_data;
869*4882a593Smuzhiyun 	struct cx18 *cx = id->cx;
870*4882a593Smuzhiyun 	struct cx18_stream *s = &cx->streams[id->type];
871*4882a593Smuzhiyun 
872*4882a593Smuzhiyun 	if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
873*4882a593Smuzhiyun 		(s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
874*4882a593Smuzhiyun 		return -EINVAL;
875*4882a593Smuzhiyun 
876*4882a593Smuzhiyun 	return videobuf_reqbufs(cx18_vb_queue(id), rb);
877*4882a593Smuzhiyun }
878*4882a593Smuzhiyun 
cx18_querybuf(struct file * file,void * priv,struct v4l2_buffer * b)879*4882a593Smuzhiyun static int cx18_querybuf(struct file *file, void *priv,
880*4882a593Smuzhiyun 	struct v4l2_buffer *b)
881*4882a593Smuzhiyun {
882*4882a593Smuzhiyun 	struct cx18_open_id *id = file->private_data;
883*4882a593Smuzhiyun 	struct cx18 *cx = id->cx;
884*4882a593Smuzhiyun 	struct cx18_stream *s = &cx->streams[id->type];
885*4882a593Smuzhiyun 
886*4882a593Smuzhiyun 	if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
887*4882a593Smuzhiyun 		(s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
888*4882a593Smuzhiyun 		return -EINVAL;
889*4882a593Smuzhiyun 
890*4882a593Smuzhiyun 	return videobuf_querybuf(cx18_vb_queue(id), b);
891*4882a593Smuzhiyun }
892*4882a593Smuzhiyun 
cx18_qbuf(struct file * file,void * priv,struct v4l2_buffer * b)893*4882a593Smuzhiyun static int cx18_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
894*4882a593Smuzhiyun {
895*4882a593Smuzhiyun 	struct cx18_open_id *id = file->private_data;
896*4882a593Smuzhiyun 	struct cx18 *cx = id->cx;
897*4882a593Smuzhiyun 	struct cx18_stream *s = &cx->streams[id->type];
898*4882a593Smuzhiyun 
899*4882a593Smuzhiyun 	if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
900*4882a593Smuzhiyun 		(s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
901*4882a593Smuzhiyun 		return -EINVAL;
902*4882a593Smuzhiyun 
903*4882a593Smuzhiyun 	return videobuf_qbuf(cx18_vb_queue(id), b);
904*4882a593Smuzhiyun }
905*4882a593Smuzhiyun 
cx18_dqbuf(struct file * file,void * priv,struct v4l2_buffer * b)906*4882a593Smuzhiyun static int cx18_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
907*4882a593Smuzhiyun {
908*4882a593Smuzhiyun 	struct cx18_open_id *id = file->private_data;
909*4882a593Smuzhiyun 	struct cx18 *cx = id->cx;
910*4882a593Smuzhiyun 	struct cx18_stream *s = &cx->streams[id->type];
911*4882a593Smuzhiyun 
912*4882a593Smuzhiyun 	if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
913*4882a593Smuzhiyun 		(s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
914*4882a593Smuzhiyun 		return -EINVAL;
915*4882a593Smuzhiyun 
916*4882a593Smuzhiyun 	return videobuf_dqbuf(cx18_vb_queue(id), b, file->f_flags & O_NONBLOCK);
917*4882a593Smuzhiyun }
918*4882a593Smuzhiyun 
cx18_encoder_cmd(struct file * file,void * fh,struct v4l2_encoder_cmd * enc)919*4882a593Smuzhiyun static int cx18_encoder_cmd(struct file *file, void *fh,
920*4882a593Smuzhiyun 				struct v4l2_encoder_cmd *enc)
921*4882a593Smuzhiyun {
922*4882a593Smuzhiyun 	struct cx18_open_id *id = fh2id(fh);
923*4882a593Smuzhiyun 	struct cx18 *cx = id->cx;
924*4882a593Smuzhiyun 	u32 h;
925*4882a593Smuzhiyun 
926*4882a593Smuzhiyun 	switch (enc->cmd) {
927*4882a593Smuzhiyun 	case V4L2_ENC_CMD_START:
928*4882a593Smuzhiyun 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
929*4882a593Smuzhiyun 		enc->flags = 0;
930*4882a593Smuzhiyun 		return cx18_start_capture(id);
931*4882a593Smuzhiyun 
932*4882a593Smuzhiyun 	case V4L2_ENC_CMD_STOP:
933*4882a593Smuzhiyun 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
934*4882a593Smuzhiyun 		enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
935*4882a593Smuzhiyun 		cx18_stop_capture(id,
936*4882a593Smuzhiyun 				  enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);
937*4882a593Smuzhiyun 		break;
938*4882a593Smuzhiyun 
939*4882a593Smuzhiyun 	case V4L2_ENC_CMD_PAUSE:
940*4882a593Smuzhiyun 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
941*4882a593Smuzhiyun 		enc->flags = 0;
942*4882a593Smuzhiyun 		if (!atomic_read(&cx->ana_capturing))
943*4882a593Smuzhiyun 			return -EPERM;
944*4882a593Smuzhiyun 		if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
945*4882a593Smuzhiyun 			return 0;
946*4882a593Smuzhiyun 		h = cx18_find_handle(cx);
947*4882a593Smuzhiyun 		if (h == CX18_INVALID_TASK_HANDLE) {
948*4882a593Smuzhiyun 			CX18_ERR("Can't find valid task handle for V4L2_ENC_CMD_PAUSE\n");
949*4882a593Smuzhiyun 			return -EBADFD;
950*4882a593Smuzhiyun 		}
951*4882a593Smuzhiyun 		cx18_mute(cx);
952*4882a593Smuzhiyun 		cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, h);
953*4882a593Smuzhiyun 		break;
954*4882a593Smuzhiyun 
955*4882a593Smuzhiyun 	case V4L2_ENC_CMD_RESUME:
956*4882a593Smuzhiyun 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
957*4882a593Smuzhiyun 		enc->flags = 0;
958*4882a593Smuzhiyun 		if (!atomic_read(&cx->ana_capturing))
959*4882a593Smuzhiyun 			return -EPERM;
960*4882a593Smuzhiyun 		if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
961*4882a593Smuzhiyun 			return 0;
962*4882a593Smuzhiyun 		h = cx18_find_handle(cx);
963*4882a593Smuzhiyun 		if (h == CX18_INVALID_TASK_HANDLE) {
964*4882a593Smuzhiyun 			CX18_ERR("Can't find valid task handle for V4L2_ENC_CMD_RESUME\n");
965*4882a593Smuzhiyun 			return -EBADFD;
966*4882a593Smuzhiyun 		}
967*4882a593Smuzhiyun 		cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, h);
968*4882a593Smuzhiyun 		cx18_unmute(cx);
969*4882a593Smuzhiyun 		break;
970*4882a593Smuzhiyun 
971*4882a593Smuzhiyun 	default:
972*4882a593Smuzhiyun 		CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
973*4882a593Smuzhiyun 		return -EINVAL;
974*4882a593Smuzhiyun 	}
975*4882a593Smuzhiyun 	return 0;
976*4882a593Smuzhiyun }
977*4882a593Smuzhiyun 
cx18_try_encoder_cmd(struct file * file,void * fh,struct v4l2_encoder_cmd * enc)978*4882a593Smuzhiyun static int cx18_try_encoder_cmd(struct file *file, void *fh,
979*4882a593Smuzhiyun 				struct v4l2_encoder_cmd *enc)
980*4882a593Smuzhiyun {
981*4882a593Smuzhiyun 	struct cx18 *cx = fh2id(fh)->cx;
982*4882a593Smuzhiyun 
983*4882a593Smuzhiyun 	switch (enc->cmd) {
984*4882a593Smuzhiyun 	case V4L2_ENC_CMD_START:
985*4882a593Smuzhiyun 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
986*4882a593Smuzhiyun 		enc->flags = 0;
987*4882a593Smuzhiyun 		break;
988*4882a593Smuzhiyun 
989*4882a593Smuzhiyun 	case V4L2_ENC_CMD_STOP:
990*4882a593Smuzhiyun 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
991*4882a593Smuzhiyun 		enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
992*4882a593Smuzhiyun 		break;
993*4882a593Smuzhiyun 
994*4882a593Smuzhiyun 	case V4L2_ENC_CMD_PAUSE:
995*4882a593Smuzhiyun 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
996*4882a593Smuzhiyun 		enc->flags = 0;
997*4882a593Smuzhiyun 		break;
998*4882a593Smuzhiyun 
999*4882a593Smuzhiyun 	case V4L2_ENC_CMD_RESUME:
1000*4882a593Smuzhiyun 		CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
1001*4882a593Smuzhiyun 		enc->flags = 0;
1002*4882a593Smuzhiyun 		break;
1003*4882a593Smuzhiyun 
1004*4882a593Smuzhiyun 	default:
1005*4882a593Smuzhiyun 		CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
1006*4882a593Smuzhiyun 		return -EINVAL;
1007*4882a593Smuzhiyun 	}
1008*4882a593Smuzhiyun 	return 0;
1009*4882a593Smuzhiyun }
1010*4882a593Smuzhiyun 
cx18_log_status(struct file * file,void * fh)1011*4882a593Smuzhiyun static int cx18_log_status(struct file *file, void *fh)
1012*4882a593Smuzhiyun {
1013*4882a593Smuzhiyun 	struct cx18 *cx = fh2id(fh)->cx;
1014*4882a593Smuzhiyun 	struct v4l2_input vidin;
1015*4882a593Smuzhiyun 	struct v4l2_audio audin;
1016*4882a593Smuzhiyun 	int i;
1017*4882a593Smuzhiyun 
1018*4882a593Smuzhiyun 	CX18_INFO("Version: %s  Card: %s\n", CX18_VERSION, cx->card_name);
1019*4882a593Smuzhiyun 	if (cx->hw_flags & CX18_HW_TVEEPROM) {
1020*4882a593Smuzhiyun 		struct tveeprom tv;
1021*4882a593Smuzhiyun 
1022*4882a593Smuzhiyun 		cx18_read_eeprom(cx, &tv);
1023*4882a593Smuzhiyun 	}
1024*4882a593Smuzhiyun 	cx18_call_all(cx, core, log_status);
1025*4882a593Smuzhiyun 	cx18_get_input(cx, cx->active_input, &vidin);
1026*4882a593Smuzhiyun 	cx18_get_audio_input(cx, cx->audio_input, &audin);
1027*4882a593Smuzhiyun 	CX18_INFO("Video Input: %s\n", vidin.name);
1028*4882a593Smuzhiyun 	CX18_INFO("Audio Input: %s\n", audin.name);
1029*4882a593Smuzhiyun 	mutex_lock(&cx->gpio_lock);
1030*4882a593Smuzhiyun 	CX18_INFO("GPIO:  direction 0x%08x, value 0x%08x\n",
1031*4882a593Smuzhiyun 		cx->gpio_dir, cx->gpio_val);
1032*4882a593Smuzhiyun 	mutex_unlock(&cx->gpio_lock);
1033*4882a593Smuzhiyun 	CX18_INFO("Tuner: %s\n",
1034*4882a593Smuzhiyun 		test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ?  "Radio" : "TV");
1035*4882a593Smuzhiyun 	v4l2_ctrl_handler_log_status(&cx->cxhdl.hdl, cx->v4l2_dev.name);
1036*4882a593Smuzhiyun 	CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags);
1037*4882a593Smuzhiyun 	for (i = 0; i < CX18_MAX_STREAMS; i++) {
1038*4882a593Smuzhiyun 		struct cx18_stream *s = &cx->streams[i];
1039*4882a593Smuzhiyun 
1040*4882a593Smuzhiyun 		if (s->video_dev.v4l2_dev == NULL || s->buffers == 0)
1041*4882a593Smuzhiyun 			continue;
1042*4882a593Smuzhiyun 		CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
1043*4882a593Smuzhiyun 			  s->name, s->s_flags,
1044*4882a593Smuzhiyun 			  atomic_read(&s->q_full.depth) * s->bufs_per_mdl * 100
1045*4882a593Smuzhiyun 			   / s->buffers,
1046*4882a593Smuzhiyun 			  (s->buffers * s->buf_size) / 1024, s->buffers);
1047*4882a593Smuzhiyun 	}
1048*4882a593Smuzhiyun 	CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
1049*4882a593Smuzhiyun 			(long long)cx->mpg_data_received,
1050*4882a593Smuzhiyun 			(long long)cx->vbi_data_inserted);
1051*4882a593Smuzhiyun 	return 0;
1052*4882a593Smuzhiyun }
1053*4882a593Smuzhiyun 
cx18_default(struct file * file,void * fh,bool valid_prio,unsigned int cmd,void * arg)1054*4882a593Smuzhiyun static long cx18_default(struct file *file, void *fh, bool valid_prio,
1055*4882a593Smuzhiyun 			 unsigned int cmd, void *arg)
1056*4882a593Smuzhiyun {
1057*4882a593Smuzhiyun 	struct cx18 *cx = fh2id(fh)->cx;
1058*4882a593Smuzhiyun 
1059*4882a593Smuzhiyun 	switch (cmd) {
1060*4882a593Smuzhiyun 	case VIDIOC_INT_RESET: {
1061*4882a593Smuzhiyun 		u32 val = *(u32 *)arg;
1062*4882a593Smuzhiyun 
1063*4882a593Smuzhiyun 		if ((val == 0) || (val & 0x01))
1064*4882a593Smuzhiyun 			cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL, core, reset,
1065*4882a593Smuzhiyun 				     (u32) CX18_GPIO_RESET_Z8F0811);
1066*4882a593Smuzhiyun 		break;
1067*4882a593Smuzhiyun 	}
1068*4882a593Smuzhiyun 
1069*4882a593Smuzhiyun 	default:
1070*4882a593Smuzhiyun 		return -ENOTTY;
1071*4882a593Smuzhiyun 	}
1072*4882a593Smuzhiyun 	return 0;
1073*4882a593Smuzhiyun }
1074*4882a593Smuzhiyun 
1075*4882a593Smuzhiyun static const struct v4l2_ioctl_ops cx18_ioctl_ops = {
1076*4882a593Smuzhiyun 	.vidioc_querycap                = cx18_querycap,
1077*4882a593Smuzhiyun 	.vidioc_s_audio                 = cx18_s_audio,
1078*4882a593Smuzhiyun 	.vidioc_g_audio                 = cx18_g_audio,
1079*4882a593Smuzhiyun 	.vidioc_enumaudio               = cx18_enumaudio,
1080*4882a593Smuzhiyun 	.vidioc_enum_input              = cx18_enum_input,
1081*4882a593Smuzhiyun 	.vidioc_g_pixelaspect           = cx18_g_pixelaspect,
1082*4882a593Smuzhiyun 	.vidioc_g_selection             = cx18_g_selection,
1083*4882a593Smuzhiyun 	.vidioc_g_input                 = cx18_g_input,
1084*4882a593Smuzhiyun 	.vidioc_s_input                 = cx18_s_input,
1085*4882a593Smuzhiyun 	.vidioc_g_frequency             = cx18_g_frequency,
1086*4882a593Smuzhiyun 	.vidioc_s_frequency             = cx18_s_frequency,
1087*4882a593Smuzhiyun 	.vidioc_s_tuner                 = cx18_s_tuner,
1088*4882a593Smuzhiyun 	.vidioc_g_tuner                 = cx18_g_tuner,
1089*4882a593Smuzhiyun 	.vidioc_g_enc_index             = cx18_g_enc_index,
1090*4882a593Smuzhiyun 	.vidioc_g_std                   = cx18_g_std,
1091*4882a593Smuzhiyun 	.vidioc_s_std                   = cx18_s_std,
1092*4882a593Smuzhiyun 	.vidioc_log_status              = cx18_log_status,
1093*4882a593Smuzhiyun 	.vidioc_enum_fmt_vid_cap        = cx18_enum_fmt_vid_cap,
1094*4882a593Smuzhiyun 	.vidioc_encoder_cmd             = cx18_encoder_cmd,
1095*4882a593Smuzhiyun 	.vidioc_try_encoder_cmd         = cx18_try_encoder_cmd,
1096*4882a593Smuzhiyun 	.vidioc_g_fmt_vid_cap           = cx18_g_fmt_vid_cap,
1097*4882a593Smuzhiyun 	.vidioc_g_fmt_vbi_cap           = cx18_g_fmt_vbi_cap,
1098*4882a593Smuzhiyun 	.vidioc_g_fmt_sliced_vbi_cap    = cx18_g_fmt_sliced_vbi_cap,
1099*4882a593Smuzhiyun 	.vidioc_s_fmt_vid_cap           = cx18_s_fmt_vid_cap,
1100*4882a593Smuzhiyun 	.vidioc_s_fmt_vbi_cap           = cx18_s_fmt_vbi_cap,
1101*4882a593Smuzhiyun 	.vidioc_s_fmt_sliced_vbi_cap    = cx18_s_fmt_sliced_vbi_cap,
1102*4882a593Smuzhiyun 	.vidioc_try_fmt_vid_cap         = cx18_try_fmt_vid_cap,
1103*4882a593Smuzhiyun 	.vidioc_try_fmt_vbi_cap         = cx18_try_fmt_vbi_cap,
1104*4882a593Smuzhiyun 	.vidioc_try_fmt_sliced_vbi_cap  = cx18_try_fmt_sliced_vbi_cap,
1105*4882a593Smuzhiyun 	.vidioc_g_sliced_vbi_cap        = cx18_g_sliced_vbi_cap,
1106*4882a593Smuzhiyun #ifdef CONFIG_VIDEO_ADV_DEBUG
1107*4882a593Smuzhiyun 	.vidioc_g_register              = cx18_g_register,
1108*4882a593Smuzhiyun 	.vidioc_s_register              = cx18_s_register,
1109*4882a593Smuzhiyun #endif
1110*4882a593Smuzhiyun 	.vidioc_default                 = cx18_default,
1111*4882a593Smuzhiyun 	.vidioc_streamon                = cx18_streamon,
1112*4882a593Smuzhiyun 	.vidioc_streamoff               = cx18_streamoff,
1113*4882a593Smuzhiyun 	.vidioc_reqbufs                 = cx18_reqbufs,
1114*4882a593Smuzhiyun 	.vidioc_querybuf                = cx18_querybuf,
1115*4882a593Smuzhiyun 	.vidioc_qbuf                    = cx18_qbuf,
1116*4882a593Smuzhiyun 	.vidioc_dqbuf                   = cx18_dqbuf,
1117*4882a593Smuzhiyun 	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
1118*4882a593Smuzhiyun 	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
1119*4882a593Smuzhiyun };
1120*4882a593Smuzhiyun 
cx18_set_funcs(struct video_device * vdev)1121*4882a593Smuzhiyun void cx18_set_funcs(struct video_device *vdev)
1122*4882a593Smuzhiyun {
1123*4882a593Smuzhiyun 	vdev->ioctl_ops = &cx18_ioctl_ops;
1124*4882a593Smuzhiyun }
1125