aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/cx18/cx18-ioctl.c
diff options
context:
space:
mode:
authorHans Verkuil <hverkuil@xs4all.nl>2008-04-28 19:24:33 -0400
committerMauro Carvalho Chehab <mchehab@infradead.org>2008-04-29 17:41:41 -0400
commit1c1e45d17b663d4749af456ab7c2fc1f36405ef8 (patch)
tree03704d6fd888c4c617baa81a60df0d80815b2607 /drivers/media/video/cx18/cx18-ioctl.c
parentd74bee8b4776b5051c650a90f49a2022d46d8588 (diff)
V4L/DVB (7786): cx18: new driver for the Conexant CX23418 MPEG encoder chip
Many thanks to Steve Toth from Hauppauge and Nattu Dakshinamurthy from Conexant for their support. I am in particular thankful to Hauppauge since without their help this driver would not exist. It should also be noted that Steve did the work to get the DVB part up and running. Thank you! Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Steven Toth <stoth@hauppauge.com> Signed-off-by: Michael Krufky <mkrufky@linuxtv.org> Signed-off-by: G. Andrew Walls <awalls@radix.net> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/video/cx18/cx18-ioctl.c')
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.c851
1 files changed, 851 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 000000000000..4a93af2e4bb6
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -0,0 +1,851 @@
1/*
2 * cx18 ioctl system call
3 *
4 * Derived from ivtv-ioctl.c
5 *
6 * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 * 02111-1307 USA
22 */
23
24#include "cx18-driver.h"
25#include "cx18-version.h"
26#include "cx18-mailbox.h"
27#include "cx18-i2c.h"
28#include "cx18-queue.h"
29#include "cx18-fileops.h"
30#include "cx18-vbi.h"
31#include "cx18-audio.h"
32#include "cx18-video.h"
33#include "cx18-streams.h"
34#include "cx18-ioctl.h"
35#include "cx18-gpio.h"
36#include "cx18-controls.h"
37#include "cx18-cards.h"
38#include "cx18-av-core.h"
39#include <media/tveeprom.h>
40#include <media/v4l2-chip-ident.h>
41#include <linux/i2c-id.h>
42
43u16 service2vbi(int type)
44{
45 switch (type) {
46 case V4L2_SLICED_TELETEXT_B:
47 return CX18_SLICED_TYPE_TELETEXT_B;
48 case V4L2_SLICED_CAPTION_525:
49 return CX18_SLICED_TYPE_CAPTION_525;
50 case V4L2_SLICED_WSS_625:
51 return CX18_SLICED_TYPE_WSS_625;
52 case V4L2_SLICED_VPS:
53 return CX18_SLICED_TYPE_VPS;
54 default:
55 return 0;
56 }
57}
58
59static int valid_service_line(int field, int line, int is_pal)
60{
61 return (is_pal && line >= 6 && (line != 23 || field == 0)) ||
62 (!is_pal && line >= 10 && line < 22);
63}
64
65static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
66{
67 u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525);
68 int i;
69
70 set = set & valid_set;
71 if (set == 0 || !valid_service_line(field, line, is_pal))
72 return 0;
73 if (!is_pal) {
74 if (line == 21 && (set & V4L2_SLICED_CAPTION_525))
75 return V4L2_SLICED_CAPTION_525;
76 } else {
77 if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS))
78 return V4L2_SLICED_VPS;
79 if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625))
80 return V4L2_SLICED_WSS_625;
81 if (line == 23)
82 return 0;
83 }
84 for (i = 0; i < 32; i++) {
85 if ((1 << i) & set)
86 return 1 << i;
87 }
88 return 0;
89}
90
91void expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
92{
93 u16 set = fmt->service_set;
94 int f, l;
95
96 fmt->service_set = 0;
97 for (f = 0; f < 2; f++) {
98 for (l = 0; l < 24; l++)
99 fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal);
100 }
101}
102
103static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
104{
105 int f, l;
106 u16 set = 0;
107
108 for (f = 0; f < 2; f++) {
109 for (l = 0; l < 24; l++) {
110 fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);
111 set |= fmt->service_lines[f][l];
112 }
113 }
114 return set != 0;
115}
116
117u16 get_service_set(struct v4l2_sliced_vbi_format *fmt)
118{
119 int f, l;
120 u16 set = 0;
121
122 for (f = 0; f < 2; f++) {
123 for (l = 0; l < 24; l++)
124 set |= fmt->service_lines[f][l];
125 }
126 return set;
127}
128
129static const struct {
130 v4l2_std_id std;
131 char *name;
132} enum_stds[] = {
133 { V4L2_STD_PAL_BG | V4L2_STD_PAL_H, "PAL-BGH" },
134 { V4L2_STD_PAL_DK, "PAL-DK" },
135 { V4L2_STD_PAL_I, "PAL-I" },
136 { V4L2_STD_PAL_M, "PAL-M" },
137 { V4L2_STD_PAL_N, "PAL-N" },
138 { V4L2_STD_PAL_Nc, "PAL-Nc" },
139 { V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, "SECAM-BGH" },
140 { V4L2_STD_SECAM_DK, "SECAM-DK" },
141 { V4L2_STD_SECAM_L, "SECAM-L" },
142 { V4L2_STD_SECAM_LC, "SECAM-L'" },
143 { V4L2_STD_NTSC_M, "NTSC-M" },
144 { V4L2_STD_NTSC_M_JP, "NTSC-J" },
145 { V4L2_STD_NTSC_M_KR, "NTSC-K" },
146};
147
148static const struct v4l2_standard cx18_std_60hz = {
149 .frameperiod = {.numerator = 1001, .denominator = 30000},
150 .framelines = 525,
151};
152
153static const struct v4l2_standard cx18_std_50hz = {
154 .frameperiod = { .numerator = 1, .denominator = 25 },
155 .framelines = 625,
156};
157
158static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg)
159{
160 struct v4l2_register *regs = arg;
161 unsigned long flags;
162
163 if (!capable(CAP_SYS_ADMIN))
164 return -EPERM;
165 if (regs->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE)
166 return -EINVAL;
167
168 spin_lock_irqsave(&cx18_cards_lock, flags);
169 if (cmd == VIDIOC_DBG_G_REGISTER)
170 regs->val = read_enc(regs->reg);
171 else
172 write_enc(regs->val, regs->reg);
173 spin_unlock_irqrestore(&cx18_cards_lock, flags);
174 return 0;
175}
176
177static int cx18_get_fmt(struct cx18 *cx, int streamtype, struct v4l2_format *fmt)
178{
179 switch (fmt->type) {
180 case V4L2_BUF_TYPE_VIDEO_CAPTURE:
181 fmt->fmt.pix.width = cx->params.width;
182 fmt->fmt.pix.height = cx->params.height;
183 fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
184 fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
185 if (streamtype == CX18_ENC_STREAM_TYPE_YUV) {
186 fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12;
187 /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
188 fmt->fmt.pix.sizeimage =
189 fmt->fmt.pix.height * fmt->fmt.pix.width +
190 fmt->fmt.pix.height * (fmt->fmt.pix.width / 2);
191 } else {
192 fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
193 fmt->fmt.pix.sizeimage = 128 * 1024;
194 }
195 break;
196
197 case V4L2_BUF_TYPE_VBI_CAPTURE:
198 fmt->fmt.vbi.sampling_rate = 27000000;
199 fmt->fmt.vbi.offset = 248;
200 fmt->fmt.vbi.samples_per_line = cx->vbi.raw_decoder_line_size - 4;
201 fmt->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
202 fmt->fmt.vbi.start[0] = cx->vbi.start[0];
203 fmt->fmt.vbi.start[1] = cx->vbi.start[1];
204 fmt->fmt.vbi.count[0] = fmt->fmt.vbi.count[1] = cx->vbi.count;
205 break;
206
207 case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
208 {
209 struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
210
211 vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
212 memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved));
213 memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
214
215 cx18_av_cmd(cx, VIDIOC_G_FMT, fmt);
216 vbifmt->service_set = get_service_set(vbifmt);
217 break;
218 }
219 default:
220 return -EINVAL;
221 }
222 return 0;
223}
224
225static int cx18_try_or_set_fmt(struct cx18 *cx, int streamtype,
226 struct v4l2_format *fmt, int set_fmt)
227{
228 struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
229 u16 set;
230
231 /* set window size */
232 if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
233 int w = fmt->fmt.pix.width;
234 int h = fmt->fmt.pix.height;
235
236 if (w > 720)
237 w = 720;
238 else if (w < 1)
239 w = 1;
240 if (h > (cx->is_50hz ? 576 : 480))
241 h = (cx->is_50hz ? 576 : 480);
242 else if (h < 2)
243 h = 2;
244 cx18_get_fmt(cx, streamtype, fmt);
245 fmt->fmt.pix.width = w;
246 fmt->fmt.pix.height = h;
247
248 if (!set_fmt || (cx->params.width == w && cx->params.height == h))
249 return 0;
250 if (atomic_read(&cx->capturing) > 0)
251 return -EBUSY;
252
253 cx->params.width = w;
254 cx->params.height = h;
255 if (w != 720 || h != (cx->is_50hz ? 576 : 480))
256 cx->params.video_temporal_filter = 0;
257 else
258 cx->params.video_temporal_filter = 8;
259 cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
260 return cx18_get_fmt(cx, streamtype, fmt);
261 }
262
263 /* set raw VBI format */
264 if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
265 if (set_fmt && streamtype == CX18_ENC_STREAM_TYPE_VBI &&
266 cx->vbi.sliced_in->service_set &&
267 atomic_read(&cx->capturing) > 0)
268 return -EBUSY;
269 if (set_fmt) {
270 cx->vbi.sliced_in->service_set = 0;
271 cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in);
272 }
273 return cx18_get_fmt(cx, streamtype, fmt);
274 }
275
276 /* any else but sliced VBI capture is an error */
277 if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
278 return -EINVAL;
279
280 /* TODO: implement sliced VBI, for now silently return 0 */
281 return 0;
282
283 /* set sliced VBI capture format */
284 vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
285 memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved));
286
287 if (vbifmt->service_set)
288 expand_service_set(vbifmt, cx->is_50hz);
289 set = check_service_set(vbifmt, cx->is_50hz);
290 vbifmt->service_set = get_service_set(vbifmt);
291
292 if (!set_fmt)
293 return 0;
294 if (set == 0)
295 return -EINVAL;
296 if (atomic_read(&cx->capturing) > 0 && cx->vbi.sliced_in->service_set == 0)
297 return -EBUSY;
298 cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
299 memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in));
300 return 0;
301}
302
303static int cx18_debug_ioctls(struct file *filp, unsigned int cmd, void *arg)
304{
305 struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data;
306 struct cx18 *cx = id->cx;
307 struct v4l2_register *reg = arg;
308
309 switch (cmd) {
310 /* ioctls to allow direct access to the encoder registers for testing */
311 case VIDIOC_DBG_G_REGISTER:
312 if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
313 return cx18_cxc(cx, cmd, arg);
314 if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
315 return cx18_i2c_id(cx, reg->match_chip, cmd, arg);
316 return cx18_call_i2c_client(cx, reg->match_chip, cmd, arg);
317
318 case VIDIOC_DBG_S_REGISTER:
319 if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
320 return cx18_cxc(cx, cmd, arg);
321 if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
322 return cx18_i2c_id(cx, reg->match_chip, cmd, arg);
323 return cx18_call_i2c_client(cx, reg->match_chip, cmd, arg);
324
325 case VIDIOC_G_CHIP_IDENT: {
326 struct v4l2_chip_ident *chip = arg;
327
328 chip->ident = V4L2_IDENT_NONE;
329 chip->revision = 0;
330 if (reg->match_type == V4L2_CHIP_MATCH_HOST) {
331 if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) {
332 struct v4l2_chip_ident *chip = arg;
333
334 chip->ident = V4L2_IDENT_CX23418;
335 }
336 return 0;
337 }
338 if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
339 return cx18_i2c_id(cx, reg->match_chip, cmd, arg);
340 if (reg->match_type == V4L2_CHIP_MATCH_I2C_ADDR)
341 return cx18_call_i2c_client(cx, reg->match_chip, cmd, arg);
342 return -EINVAL;
343 }
344
345 case VIDIOC_INT_S_AUDIO_ROUTING: {
346 struct v4l2_routing *route = arg;
347
348 cx18_audio_set_route(cx, route);
349 break;
350 }
351
352 default:
353 return -EINVAL;
354 }
355 return 0;
356}
357
358int cx18_v4l2_ioctls(struct cx18 *cx, struct file *filp, unsigned cmd, void *arg)
359{
360 struct cx18_open_id *id = NULL;
361
362 if (filp)
363 id = (struct cx18_open_id *)filp->private_data;
364
365 switch (cmd) {
366 case VIDIOC_G_PRIORITY:
367 {
368 enum v4l2_priority *p = arg;
369
370 *p = v4l2_prio_max(&cx->prio);
371 break;
372 }
373
374 case VIDIOC_S_PRIORITY:
375 {
376 enum v4l2_priority *prio = arg;
377
378 return v4l2_prio_change(&cx->prio, &id->prio, *prio);
379 }
380
381 case VIDIOC_QUERYCAP:{
382 struct v4l2_capability *vcap = arg;
383
384 memset(vcap, 0, sizeof(*vcap));
385 strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver));
386 strlcpy(vcap->card, cx->card_name, sizeof(vcap->card));
387 strlcpy(vcap->bus_info, pci_name(cx->dev), sizeof(vcap->bus_info));
388 vcap->version = CX18_DRIVER_VERSION; /* version */
389 vcap->capabilities = cx->v4l2_cap; /* capabilities */
390
391 /* reserved.. must set to 0! */
392 vcap->reserved[0] = vcap->reserved[1] =
393 vcap->reserved[2] = vcap->reserved[3] = 0;
394 break;
395 }
396
397 case VIDIOC_ENUMAUDIO:{
398 struct v4l2_audio *vin = arg;
399
400 return cx18_get_audio_input(cx, vin->index, vin);
401 }
402
403 case VIDIOC_G_AUDIO:{
404 struct v4l2_audio *vin = arg;
405
406 vin->index = cx->audio_input;
407 return cx18_get_audio_input(cx, vin->index, vin);
408 }
409
410 case VIDIOC_S_AUDIO:{
411 struct v4l2_audio *vout = arg;
412
413 if (vout->index >= cx->nof_audio_inputs)
414 return -EINVAL;
415 cx->audio_input = vout->index;
416 cx18_audio_set_io(cx);
417 break;
418 }
419
420 case VIDIOC_ENUMINPUT:{
421 struct v4l2_input *vin = arg;
422
423 /* set it to defaults from our table */
424 return cx18_get_input(cx, vin->index, vin);
425 }
426
427 case VIDIOC_TRY_FMT:
428 case VIDIOC_S_FMT: {
429 struct v4l2_format *fmt = arg;
430
431 return cx18_try_or_set_fmt(cx, id->type, fmt, cmd == VIDIOC_S_FMT);
432 }
433
434 case VIDIOC_G_FMT: {
435 struct v4l2_format *fmt = arg;
436 int type = fmt->type;
437
438 memset(fmt, 0, sizeof(*fmt));
439 fmt->type = type;
440 return cx18_get_fmt(cx, id->type, fmt);
441 }
442
443 case VIDIOC_CROPCAP: {
444 struct v4l2_cropcap *cropcap = arg;
445
446 if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
447 return -EINVAL;
448 cropcap->bounds.top = cropcap->bounds.left = 0;
449 cropcap->bounds.width = 720;
450 cropcap->bounds.height = cx->is_50hz ? 576 : 480;
451 cropcap->pixelaspect.numerator = cx->is_50hz ? 59 : 10;
452 cropcap->pixelaspect.denominator = cx->is_50hz ? 54 : 11;
453 cropcap->defrect = cropcap->bounds;
454 return 0;
455 }
456
457 case VIDIOC_S_CROP: {
458 struct v4l2_crop *crop = arg;
459
460 if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
461 return -EINVAL;
462 return cx18_av_cmd(cx, VIDIOC_S_CROP, arg);
463 }
464
465 case VIDIOC_G_CROP: {
466 struct v4l2_crop *crop = arg;
467
468 if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
469 return -EINVAL;
470 return cx18_av_cmd(cx, VIDIOC_G_CROP, arg);
471 }
472
473 case VIDIOC_ENUM_FMT: {
474 static struct v4l2_fmtdesc formats[] = {
475 { 0, 0, 0,
476 "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12,
477 { 0, 0, 0, 0 }
478 },
479 { 1, 0, V4L2_FMT_FLAG_COMPRESSED,
480 "MPEG", V4L2_PIX_FMT_MPEG,
481 { 0, 0, 0, 0 }
482 }
483 };
484 struct v4l2_fmtdesc *fmt = arg;
485 enum v4l2_buf_type type = fmt->type;
486
487 switch (type) {
488 case V4L2_BUF_TYPE_VIDEO_CAPTURE:
489 break;
490 default:
491 return -EINVAL;
492 }
493 if (fmt->index > 1)
494 return -EINVAL;
495 *fmt = formats[fmt->index];
496 fmt->type = type;
497 return 0;
498 }
499
500 case VIDIOC_G_INPUT:{
501 *(int *)arg = cx->active_input;
502 break;
503 }
504
505 case VIDIOC_S_INPUT:{
506 int inp = *(int *)arg;
507
508 if (inp < 0 || inp >= cx->nof_inputs)
509 return -EINVAL;
510
511 if (inp == cx->active_input) {
512 CX18_DEBUG_INFO("Input unchanged\n");
513 break;
514 }
515 CX18_DEBUG_INFO("Changing input from %d to %d\n",
516 cx->active_input, inp);
517
518 cx->active_input = inp;
519 /* Set the audio input to whatever is appropriate for the
520 input type. */
521 cx->audio_input = cx->card->video_inputs[inp].audio_index;
522
523 /* prevent others from messing with the streams until
524 we're finished changing inputs. */
525 cx18_mute(cx);
526 cx18_video_set_io(cx);
527 cx18_audio_set_io(cx);
528 cx18_unmute(cx);
529 break;
530 }
531
532 case VIDIOC_G_FREQUENCY:{
533 struct v4l2_frequency *vf = arg;
534
535 if (vf->tuner != 0)
536 return -EINVAL;
537 cx18_call_i2c_clients(cx, cmd, arg);
538 break;
539 }
540
541 case VIDIOC_S_FREQUENCY:{
542 struct v4l2_frequency vf = *(struct v4l2_frequency *)arg;
543
544 if (vf.tuner != 0)
545 return -EINVAL;
546
547 cx18_mute(cx);
548 CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf.frequency);
549 cx18_call_i2c_clients(cx, cmd, &vf);
550 cx18_unmute(cx);
551 break;
552 }
553
554 case VIDIOC_ENUMSTD:{
555 struct v4l2_standard *vs = arg;
556 int idx = vs->index;
557
558 if (idx < 0 || idx >= ARRAY_SIZE(enum_stds))
559 return -EINVAL;
560
561 *vs = (enum_stds[idx].std & V4L2_STD_525_60) ?
562 cx18_std_60hz : cx18_std_50hz;
563 vs->index = idx;
564 vs->id = enum_stds[idx].std;
565 strlcpy(vs->name, enum_stds[idx].name, sizeof(vs->name));
566 break;
567 }
568
569 case VIDIOC_G_STD:{
570 *(v4l2_std_id *) arg = cx->std;
571 break;
572 }
573
574 case VIDIOC_S_STD: {
575 v4l2_std_id std = *(v4l2_std_id *) arg;
576
577 if ((std & V4L2_STD_ALL) == 0)
578 return -EINVAL;
579
580 if (std == cx->std)
581 break;
582
583 if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ||
584 atomic_read(&cx->capturing) > 0) {
585 /* Switching standard would turn off the radio or mess
586 with already running streams, prevent that by
587 returning EBUSY. */
588 return -EBUSY;
589 }
590
591 cx->std = std;
592 cx->is_60hz = (std & V4L2_STD_525_60) ? 1 : 0;
593 cx->params.is_50hz = cx->is_50hz = !cx->is_60hz;
594 cx->params.width = 720;
595 cx->params.height = cx->is_50hz ? 576 : 480;
596 cx->vbi.count = cx->is_50hz ? 18 : 12;
597 cx->vbi.start[0] = cx->is_50hz ? 6 : 10;
598 cx->vbi.start[1] = cx->is_50hz ? 318 : 273;
599 cx->vbi.sliced_decoder_line_size = cx->is_60hz ? 272 : 284;
600 CX18_DEBUG_INFO("Switching standard to %llx.\n", (unsigned long long)cx->std);
601
602 /* Tuner */
603 cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std);
604 break;
605 }
606
607 case VIDIOC_S_TUNER: { /* Setting tuner can only set audio mode */
608 struct v4l2_tuner *vt = arg;
609
610 if (vt->index != 0)
611 return -EINVAL;
612
613 cx18_call_i2c_clients(cx, VIDIOC_S_TUNER, vt);
614 break;
615 }
616
617 case VIDIOC_G_TUNER: {
618 struct v4l2_tuner *vt = arg;
619
620 if (vt->index != 0)
621 return -EINVAL;
622
623 memset(vt, 0, sizeof(*vt));
624 cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, vt);
625
626 if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
627 strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name));
628 vt->type = V4L2_TUNER_RADIO;
629 } else {
630 strlcpy(vt->name, "cx18 TV Tuner", sizeof(vt->name));
631 vt->type = V4L2_TUNER_ANALOG_TV;
632 }
633 break;
634 }
635
636 case VIDIOC_G_SLICED_VBI_CAP: {
637 struct v4l2_sliced_vbi_cap *cap = arg;
638 int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
639 int f, l;
640 enum v4l2_buf_type type = cap->type;
641
642 memset(cap, 0, sizeof(*cap));
643 cap->type = type;
644 if (type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
645 for (f = 0; f < 2; f++) {
646 for (l = 0; l < 24; l++) {
647 if (valid_service_line(f, l, cx->is_50hz))
648 cap->service_lines[f][l] = set;
649 }
650 }
651 return 0;
652 }
653 return -EINVAL;
654 }
655
656 case VIDIOC_ENCODER_CMD:
657 case VIDIOC_TRY_ENCODER_CMD: {
658 struct v4l2_encoder_cmd *enc = arg;
659 int try = cmd == VIDIOC_TRY_ENCODER_CMD;
660
661 memset(&enc->raw, 0, sizeof(enc->raw));
662 switch (enc->cmd) {
663 case V4L2_ENC_CMD_START:
664 enc->flags = 0;
665 if (try)
666 return 0;
667 return cx18_start_capture(id);
668
669 case V4L2_ENC_CMD_STOP:
670 enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
671 if (try)
672 return 0;
673 cx18_stop_capture(id, enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);
674 return 0;
675
676 case V4L2_ENC_CMD_PAUSE:
677 enc->flags = 0;
678 if (try)
679 return 0;
680 if (!atomic_read(&cx->capturing))
681 return -EPERM;
682 if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
683 return 0;
684 cx18_mute(cx);
685 cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, cx18_find_handle(cx));
686 break;
687
688 case V4L2_ENC_CMD_RESUME:
689 enc->flags = 0;
690 if (try)
691 return 0;
692 if (!atomic_read(&cx->capturing))
693 return -EPERM;
694 if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
695 return 0;
696 cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, cx18_find_handle(cx));
697 cx18_unmute(cx);
698 break;
699 default:
700 return -EINVAL;
701 }
702 break;
703 }
704
705 case VIDIOC_LOG_STATUS:
706 {
707 struct v4l2_input vidin;
708 struct v4l2_audio audin;
709 int i;
710
711 CX18_INFO("================= START STATUS CARD #%d =================\n", cx->num);
712 if (cx->hw_flags & CX18_HW_TVEEPROM) {
713 struct tveeprom tv;
714
715 cx18_read_eeprom(cx, &tv);
716 }
717 cx18_call_i2c_clients(cx, VIDIOC_LOG_STATUS, NULL);
718 cx18_get_input(cx, cx->active_input, &vidin);
719 cx18_get_audio_input(cx, cx->audio_input, &audin);
720 CX18_INFO("Video Input: %s\n", vidin.name);
721 CX18_INFO("Audio Input: %s\n", audin.name);
722 CX18_INFO("Tuner: %s\n",
723 test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ?
724 "Radio" : "TV");
725 cx2341x_log_status(&cx->params, cx->name);
726 CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags);
727 for (i = 0; i < CX18_MAX_STREAMS; i++) {
728 struct cx18_stream *s = &cx->streams[i];
729
730 if (s->v4l2dev == NULL || s->buffers == 0)
731 continue;
732 CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
733 s->name, s->s_flags,
734 (s->buffers - s->q_free.buffers) * 100 / s->buffers,
735 (s->buffers * s->buf_size) / 1024, s->buffers);
736 }
737 CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
738 (long long)cx->mpg_data_received,
739 (long long)cx->vbi_data_inserted);
740 CX18_INFO("================== END STATUS CARD #%d ==================\n", cx->num);
741 break;
742 }
743
744 default:
745 return -EINVAL;
746 }
747 return 0;
748}
749
750static int cx18_v4l2_do_ioctl(struct inode *inode, struct file *filp,
751 unsigned int cmd, void *arg)
752{
753 struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data;
754 struct cx18 *cx = id->cx;
755 int ret;
756
757 /* check priority */
758 switch (cmd) {
759 case VIDIOC_S_CTRL:
760 case VIDIOC_S_STD:
761 case VIDIOC_S_INPUT:
762 case VIDIOC_S_TUNER:
763 case VIDIOC_S_FREQUENCY:
764 case VIDIOC_S_FMT:
765 case VIDIOC_S_CROP:
766 case VIDIOC_S_EXT_CTRLS:
767 ret = v4l2_prio_check(&cx->prio, &id->prio);
768 if (ret)
769 return ret;
770 }
771
772 switch (cmd) {
773 case VIDIOC_DBG_G_REGISTER:
774 case VIDIOC_DBG_S_REGISTER:
775 case VIDIOC_G_CHIP_IDENT:
776 case VIDIOC_INT_S_AUDIO_ROUTING:
777 case VIDIOC_INT_RESET:
778 if (cx18_debug & CX18_DBGFLG_IOCTL) {
779 printk(KERN_INFO "cx18%d ioctl: ", cx->num);
780 v4l_printk_ioctl(cmd);
781 }
782 return cx18_debug_ioctls(filp, cmd, arg);
783
784 case VIDIOC_G_PRIORITY:
785 case VIDIOC_S_PRIORITY:
786 case VIDIOC_QUERYCAP:
787 case VIDIOC_ENUMINPUT:
788 case VIDIOC_G_INPUT:
789 case VIDIOC_S_INPUT:
790 case VIDIOC_G_FMT:
791 case VIDIOC_S_FMT:
792 case VIDIOC_TRY_FMT:
793 case VIDIOC_ENUM_FMT:
794 case VIDIOC_CROPCAP:
795 case VIDIOC_G_CROP:
796 case VIDIOC_S_CROP:
797 case VIDIOC_G_FREQUENCY:
798 case VIDIOC_S_FREQUENCY:
799 case VIDIOC_ENUMSTD:
800 case VIDIOC_G_STD:
801 case VIDIOC_S_STD:
802 case VIDIOC_S_TUNER:
803 case VIDIOC_G_TUNER:
804 case VIDIOC_ENUMAUDIO:
805 case VIDIOC_S_AUDIO:
806 case VIDIOC_G_AUDIO:
807 case VIDIOC_G_SLICED_VBI_CAP:
808 case VIDIOC_LOG_STATUS:
809 case VIDIOC_G_ENC_INDEX:
810 case VIDIOC_ENCODER_CMD:
811 case VIDIOC_TRY_ENCODER_CMD:
812 if (cx18_debug & CX18_DBGFLG_IOCTL) {
813 printk(KERN_INFO "cx18%d ioctl: ", cx->num);
814 v4l_printk_ioctl(cmd);
815 }
816 return cx18_v4l2_ioctls(cx, filp, cmd, arg);
817
818 case VIDIOC_QUERYMENU:
819 case VIDIOC_QUERYCTRL:
820 case VIDIOC_S_CTRL:
821 case VIDIOC_G_CTRL:
822 case VIDIOC_S_EXT_CTRLS:
823 case VIDIOC_G_EXT_CTRLS:
824 case VIDIOC_TRY_EXT_CTRLS:
825 if (cx18_debug & CX18_DBGFLG_IOCTL) {
826 printk(KERN_INFO "cx18%d ioctl: ", cx->num);
827 v4l_printk_ioctl(cmd);
828 }
829 return cx18_control_ioctls(cx, cmd, arg);
830
831 case 0x00005401: /* Handle isatty() calls */
832 return -EINVAL;
833 default:
834 return v4l_compat_translate_ioctl(inode, filp, cmd, arg,
835 cx18_v4l2_do_ioctl);
836 }
837 return 0;
838}
839
840int cx18_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
841 unsigned long arg)
842{
843 struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data;
844 struct cx18 *cx = id->cx;
845 int res;
846
847 mutex_lock(&cx->serialize_lock);
848 res = video_usercopy(inode, filp, cmd, arg, cx18_v4l2_do_ioctl);
849 mutex_unlock(&cx->serialize_lock);
850 return res;
851}