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