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