]>
Commit | Line | Data |
---|---|---|
f00add96 NS |
1 | /* |
2 | * Driver for Renesas R-Car VIN | |
3 | * | |
4 | * Copyright (C) 2016 Renesas Electronics Corp. | |
5 | * Copyright (C) 2011-2013 Renesas Solutions Corp. | |
6 | * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com> | |
7 | * Copyright (C) 2008 Magnus Damm | |
8 | * | |
9 | * Based on the soc-camera rcar_vin driver | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or modify it | |
12 | * under the terms of the GNU General Public License as published by the | |
13 | * Free Software Foundation; either version 2 of the License, or (at your | |
14 | * option) any later version. | |
15 | */ | |
16 | ||
17 | #include <linux/pm_runtime.h> | |
18 | ||
19 | #include <media/v4l2-event.h> | |
20 | #include <media/v4l2-ioctl.h> | |
21 | #include <media/v4l2-rect.h> | |
22 | ||
23 | #include "rcar-vin.h" | |
24 | ||
25 | #define RVIN_DEFAULT_FORMAT V4L2_PIX_FMT_YUYV | |
26 | #define RVIN_MAX_WIDTH 2048 | |
27 | #define RVIN_MAX_HEIGHT 2048 | |
28 | ||
29 | /* ----------------------------------------------------------------------------- | |
30 | * Format Conversions | |
31 | */ | |
32 | ||
33 | static const struct rvin_video_format rvin_formats[] = { | |
34 | { | |
35 | .fourcc = V4L2_PIX_FMT_NV16, | |
36 | .bpp = 1, | |
37 | }, | |
38 | { | |
39 | .fourcc = V4L2_PIX_FMT_YUYV, | |
40 | .bpp = 2, | |
41 | }, | |
42 | { | |
43 | .fourcc = V4L2_PIX_FMT_UYVY, | |
44 | .bpp = 2, | |
45 | }, | |
46 | { | |
47 | .fourcc = V4L2_PIX_FMT_RGB565, | |
48 | .bpp = 2, | |
49 | }, | |
50 | { | |
51 | .fourcc = V4L2_PIX_FMT_XRGB555, | |
52 | .bpp = 2, | |
53 | }, | |
54 | { | |
55 | .fourcc = V4L2_PIX_FMT_XBGR32, | |
56 | .bpp = 4, | |
57 | }, | |
58 | }; | |
59 | ||
60 | const struct rvin_video_format *rvin_format_from_pixel(u32 pixelformat) | |
61 | { | |
62 | int i; | |
63 | ||
64 | for (i = 0; i < ARRAY_SIZE(rvin_formats); i++) | |
65 | if (rvin_formats[i].fourcc == pixelformat) | |
66 | return rvin_formats + i; | |
67 | ||
68 | return NULL; | |
69 | } | |
70 | ||
71 | static u32 rvin_format_bytesperline(struct v4l2_pix_format *pix) | |
72 | { | |
73 | const struct rvin_video_format *fmt; | |
74 | ||
75 | fmt = rvin_format_from_pixel(pix->pixelformat); | |
76 | ||
77 | if (WARN_ON(!fmt)) | |
78 | return -EINVAL; | |
79 | ||
80 | return pix->width * fmt->bpp; | |
81 | } | |
82 | ||
83 | static u32 rvin_format_sizeimage(struct v4l2_pix_format *pix) | |
84 | { | |
85 | if (pix->pixelformat == V4L2_PIX_FMT_NV16) | |
86 | return pix->bytesperline * pix->height * 2; | |
87 | ||
88 | return pix->bytesperline * pix->height; | |
89 | } | |
90 | ||
91 | /* ----------------------------------------------------------------------------- | |
92 | * V4L2 | |
93 | */ | |
94 | ||
d6482537 NS |
95 | static void rvin_reset_crop_compose(struct rvin_dev *vin) |
96 | { | |
97 | vin->crop.top = vin->crop.left = 0; | |
98 | vin->crop.width = vin->source.width; | |
99 | vin->crop.height = vin->source.height; | |
100 | ||
101 | vin->compose.top = vin->compose.left = 0; | |
102 | vin->compose.width = vin->format.width; | |
103 | vin->compose.height = vin->format.height; | |
104 | } | |
105 | ||
106 | static int rvin_reset_format(struct rvin_dev *vin) | |
107 | { | |
108 | struct v4l2_subdev_format fmt = { | |
109 | .which = V4L2_SUBDEV_FORMAT_ACTIVE, | |
110 | }; | |
111 | struct v4l2_mbus_framefmt *mf = &fmt.format; | |
112 | int ret; | |
113 | ||
1eb36419 | 114 | fmt.pad = vin->digital.source_pad; |
d6482537 NS |
115 | |
116 | ret = v4l2_subdev_call(vin_to_source(vin), pad, get_fmt, NULL, &fmt); | |
117 | if (ret) | |
118 | return ret; | |
119 | ||
120 | vin->format.width = mf->width; | |
121 | vin->format.height = mf->height; | |
122 | vin->format.colorspace = mf->colorspace; | |
123 | vin->format.field = mf->field; | |
124 | ||
b6f556cb NS |
125 | /* |
126 | * If the subdevice uses ALTERNATE field mode and G_STD is | |
127 | * implemented use the VIN HW to combine the two fields to | |
128 | * one INTERLACED frame. The ALTERNATE field mode can still | |
129 | * be requested in S_FMT and be respected, this is just the | |
130 | * default which is applied at probing or when S_STD is called. | |
131 | */ | |
132 | if (vin->format.field == V4L2_FIELD_ALTERNATE && | |
133 | v4l2_subdev_has_op(vin_to_source(vin), video, g_std)) | |
134 | vin->format.field = V4L2_FIELD_INTERLACED; | |
135 | ||
d6482537 NS |
136 | switch (vin->format.field) { |
137 | case V4L2_FIELD_TOP: | |
138 | case V4L2_FIELD_BOTTOM: | |
b6f556cb | 139 | case V4L2_FIELD_ALTERNATE: |
c6b3d8fc NS |
140 | vin->format.height /= 2; |
141 | break; | |
d6482537 NS |
142 | case V4L2_FIELD_NONE: |
143 | case V4L2_FIELD_INTERLACED_TB: | |
144 | case V4L2_FIELD_INTERLACED_BT: | |
145 | case V4L2_FIELD_INTERLACED: | |
146 | break; | |
147 | default: | |
148 | vin->format.field = V4L2_FIELD_NONE; | |
149 | break; | |
150 | } | |
151 | ||
152 | rvin_reset_crop_compose(vin); | |
153 | ||
6afaaab0 NS |
154 | vin->format.bytesperline = rvin_format_bytesperline(&vin->format); |
155 | vin->format.sizeimage = rvin_format_sizeimage(&vin->format); | |
156 | ||
d6482537 NS |
157 | return 0; |
158 | } | |
159 | ||
f00add96 | 160 | static int __rvin_try_format_source(struct rvin_dev *vin, |
7eb95877 NS |
161 | u32 which, |
162 | struct v4l2_pix_format *pix, | |
163 | struct rvin_source_fmt *source) | |
f00add96 NS |
164 | { |
165 | struct v4l2_subdev *sd; | |
181905e0 | 166 | struct v4l2_subdev_pad_config *pad_cfg; |
f00add96 NS |
167 | struct v4l2_subdev_format format = { |
168 | .which = which, | |
169 | }; | |
52a1b4e9 | 170 | enum v4l2_field field; |
f00add96 NS |
171 | int ret; |
172 | ||
173 | sd = vin_to_source(vin); | |
174 | ||
b50b77e6 | 175 | v4l2_fill_mbus_format(&format.format, pix, vin->digital.code); |
f00add96 | 176 | |
181905e0 UH |
177 | pad_cfg = v4l2_subdev_alloc_pad_config(sd); |
178 | if (pad_cfg == NULL) | |
179 | return -ENOMEM; | |
180 | ||
1eb36419 | 181 | format.pad = vin->digital.source_pad; |
181905e0 | 182 | |
52a1b4e9 NS |
183 | field = pix->field; |
184 | ||
64663531 NS |
185 | ret = v4l2_subdev_call(sd, pad, set_fmt, pad_cfg, &format); |
186 | if (ret < 0 && ret != -ENOIOCTLCMD) | |
187 | goto done; | |
f00add96 NS |
188 | |
189 | v4l2_fill_pix_format(pix, &format.format); | |
190 | ||
52a1b4e9 NS |
191 | pix->field = field; |
192 | ||
f00add96 NS |
193 | source->width = pix->width; |
194 | source->height = pix->height; | |
195 | ||
196 | vin_dbg(vin, "Source resolution: %ux%u\n", source->width, | |
197 | source->height); | |
198 | ||
64663531 | 199 | done: |
181905e0 | 200 | v4l2_subdev_free_pad_config(pad_cfg); |
64663531 | 201 | return ret; |
f00add96 NS |
202 | } |
203 | ||
204 | static int __rvin_try_format(struct rvin_dev *vin, | |
7eb95877 NS |
205 | u32 which, |
206 | struct v4l2_pix_format *pix, | |
207 | struct rvin_source_fmt *source) | |
f00add96 NS |
208 | { |
209 | const struct rvin_video_format *info; | |
210 | u32 rwidth, rheight, walign; | |
211 | ||
212 | /* Requested */ | |
213 | rwidth = pix->width; | |
214 | rheight = pix->height; | |
215 | ||
52a1b4e9 NS |
216 | /* Keep current field if no specific one is asked for */ |
217 | if (pix->field == V4L2_FIELD_ANY) | |
218 | pix->field = vin->format.field; | |
219 | ||
f00add96 NS |
220 | /* |
221 | * Retrieve format information and select the current format if the | |
222 | * requested format isn't supported. | |
223 | */ | |
224 | info = rvin_format_from_pixel(pix->pixelformat); | |
225 | if (!info) { | |
226 | vin_dbg(vin, "Format %x not found, keeping %x\n", | |
227 | pix->pixelformat, vin->format.pixelformat); | |
228 | *pix = vin->format; | |
229 | pix->width = rwidth; | |
230 | pix->height = rheight; | |
231 | } | |
232 | ||
233 | /* Always recalculate */ | |
234 | pix->bytesperline = 0; | |
235 | pix->sizeimage = 0; | |
236 | ||
237 | /* Limit to source capabilities */ | |
238 | __rvin_try_format_source(vin, which, pix, source); | |
239 | ||
f00add96 | 240 | switch (pix->field) { |
f00add96 NS |
241 | case V4L2_FIELD_TOP: |
242 | case V4L2_FIELD_BOTTOM: | |
b6f556cb | 243 | case V4L2_FIELD_ALTERNATE: |
c6b3d8fc NS |
244 | pix->height /= 2; |
245 | source->height /= 2; | |
246 | break; | |
247 | case V4L2_FIELD_NONE: | |
f00add96 NS |
248 | case V4L2_FIELD_INTERLACED_TB: |
249 | case V4L2_FIELD_INTERLACED_BT: | |
250 | case V4L2_FIELD_INTERLACED: | |
251 | break; | |
252 | default: | |
253 | pix->field = V4L2_FIELD_NONE; | |
254 | break; | |
255 | } | |
256 | ||
c6b3d8fc NS |
257 | /* If source can't match format try if VIN can scale */ |
258 | if (source->width != rwidth || source->height != rheight) | |
259 | rvin_scale_try(vin, pix, rwidth, rheight); | |
260 | ||
261 | /* HW limit width to a multiple of 32 (2^5) for NV16 else 2 (2^1) */ | |
262 | walign = vin->format.pixelformat == V4L2_PIX_FMT_NV16 ? 5 : 1; | |
263 | ||
264 | /* Limit to VIN capabilities */ | |
265 | v4l_bound_align_image(&pix->width, 2, RVIN_MAX_WIDTH, walign, | |
266 | &pix->height, 4, RVIN_MAX_HEIGHT, 2, 0); | |
267 | ||
f00add96 NS |
268 | pix->bytesperline = max_t(u32, pix->bytesperline, |
269 | rvin_format_bytesperline(pix)); | |
270 | pix->sizeimage = max_t(u32, pix->sizeimage, | |
271 | rvin_format_sizeimage(pix)); | |
272 | ||
ee9e2a52 NS |
273 | if (vin->chip == RCAR_M1 && pix->pixelformat == V4L2_PIX_FMT_XBGR32) { |
274 | vin_err(vin, "pixel format XBGR32 not supported on M1\n"); | |
275 | return -EINVAL; | |
276 | } | |
277 | ||
f00add96 NS |
278 | vin_dbg(vin, "Requested %ux%u Got %ux%u bpl: %d size: %d\n", |
279 | rwidth, rheight, pix->width, pix->height, | |
280 | pix->bytesperline, pix->sizeimage); | |
281 | ||
282 | return 0; | |
283 | } | |
284 | ||
285 | static int rvin_querycap(struct file *file, void *priv, | |
286 | struct v4l2_capability *cap) | |
287 | { | |
288 | struct rvin_dev *vin = video_drvdata(file); | |
289 | ||
290 | strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); | |
291 | strlcpy(cap->card, "R_Car_VIN", sizeof(cap->card)); | |
292 | snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", | |
293 | dev_name(vin->dev)); | |
294 | return 0; | |
295 | } | |
296 | ||
297 | static int rvin_try_fmt_vid_cap(struct file *file, void *priv, | |
298 | struct v4l2_format *f) | |
299 | { | |
300 | struct rvin_dev *vin = video_drvdata(file); | |
301 | struct rvin_source_fmt source; | |
302 | ||
303 | return __rvin_try_format(vin, V4L2_SUBDEV_FORMAT_TRY, &f->fmt.pix, | |
7eb95877 | 304 | &source); |
f00add96 NS |
305 | } |
306 | ||
307 | static int rvin_s_fmt_vid_cap(struct file *file, void *priv, | |
308 | struct v4l2_format *f) | |
309 | { | |
310 | struct rvin_dev *vin = video_drvdata(file); | |
311 | struct rvin_source_fmt source; | |
312 | int ret; | |
313 | ||
314 | if (vb2_is_busy(&vin->queue)) | |
315 | return -EBUSY; | |
316 | ||
317 | ret = __rvin_try_format(vin, V4L2_SUBDEV_FORMAT_ACTIVE, &f->fmt.pix, | |
7eb95877 | 318 | &source); |
f00add96 NS |
319 | if (ret) |
320 | return ret; | |
321 | ||
322 | vin->source.width = source.width; | |
323 | vin->source.height = source.height; | |
324 | ||
325 | vin->format = f->fmt.pix; | |
326 | ||
d6482537 NS |
327 | rvin_reset_crop_compose(vin); |
328 | ||
f00add96 NS |
329 | return 0; |
330 | } | |
331 | ||
332 | static int rvin_g_fmt_vid_cap(struct file *file, void *priv, | |
333 | struct v4l2_format *f) | |
334 | { | |
335 | struct rvin_dev *vin = video_drvdata(file); | |
336 | ||
337 | f->fmt.pix = vin->format; | |
338 | ||
339 | return 0; | |
340 | } | |
341 | ||
342 | static int rvin_enum_fmt_vid_cap(struct file *file, void *priv, | |
343 | struct v4l2_fmtdesc *f) | |
344 | { | |
345 | if (f->index >= ARRAY_SIZE(rvin_formats)) | |
346 | return -EINVAL; | |
347 | ||
348 | f->pixelformat = rvin_formats[f->index].fourcc; | |
349 | ||
350 | return 0; | |
351 | } | |
352 | ||
353 | static int rvin_g_selection(struct file *file, void *fh, | |
354 | struct v4l2_selection *s) | |
355 | { | |
356 | struct rvin_dev *vin = video_drvdata(file); | |
357 | ||
358 | if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
359 | return -EINVAL; | |
360 | ||
361 | switch (s->target) { | |
362 | case V4L2_SEL_TGT_CROP_BOUNDS: | |
363 | case V4L2_SEL_TGT_CROP_DEFAULT: | |
364 | s->r.left = s->r.top = 0; | |
365 | s->r.width = vin->source.width; | |
366 | s->r.height = vin->source.height; | |
367 | break; | |
368 | case V4L2_SEL_TGT_CROP: | |
369 | s->r = vin->crop; | |
370 | break; | |
371 | case V4L2_SEL_TGT_COMPOSE_BOUNDS: | |
372 | case V4L2_SEL_TGT_COMPOSE_DEFAULT: | |
373 | s->r.left = s->r.top = 0; | |
374 | s->r.width = vin->format.width; | |
375 | s->r.height = vin->format.height; | |
376 | break; | |
377 | case V4L2_SEL_TGT_COMPOSE: | |
378 | s->r = vin->compose; | |
379 | break; | |
380 | default: | |
381 | return -EINVAL; | |
382 | } | |
383 | ||
384 | return 0; | |
385 | } | |
386 | ||
387 | static int rvin_s_selection(struct file *file, void *fh, | |
388 | struct v4l2_selection *s) | |
389 | { | |
390 | struct rvin_dev *vin = video_drvdata(file); | |
391 | const struct rvin_video_format *fmt; | |
392 | struct v4l2_rect r = s->r; | |
393 | struct v4l2_rect max_rect; | |
394 | struct v4l2_rect min_rect = { | |
395 | .width = 6, | |
396 | .height = 2, | |
397 | }; | |
398 | ||
399 | if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
400 | return -EINVAL; | |
401 | ||
402 | v4l2_rect_set_min_size(&r, &min_rect); | |
403 | ||
404 | switch (s->target) { | |
405 | case V4L2_SEL_TGT_CROP: | |
406 | /* Can't crop outside of source input */ | |
407 | max_rect.top = max_rect.left = 0; | |
408 | max_rect.width = vin->source.width; | |
409 | max_rect.height = vin->source.height; | |
410 | v4l2_rect_map_inside(&r, &max_rect); | |
411 | ||
412 | v4l_bound_align_image(&r.width, 2, vin->source.width, 1, | |
413 | &r.height, 4, vin->source.height, 2, 0); | |
414 | ||
415 | r.top = clamp_t(s32, r.top, 0, vin->source.height - r.height); | |
416 | r.left = clamp_t(s32, r.left, 0, vin->source.width - r.width); | |
417 | ||
418 | vin->crop = s->r = r; | |
419 | ||
420 | vin_dbg(vin, "Cropped %dx%d@%d:%d of %dx%d\n", | |
7eb95877 NS |
421 | r.width, r.height, r.left, r.top, |
422 | vin->source.width, vin->source.height); | |
f00add96 NS |
423 | break; |
424 | case V4L2_SEL_TGT_COMPOSE: | |
425 | /* Make sure compose rect fits inside output format */ | |
426 | max_rect.top = max_rect.left = 0; | |
427 | max_rect.width = vin->format.width; | |
428 | max_rect.height = vin->format.height; | |
429 | v4l2_rect_map_inside(&r, &max_rect); | |
430 | ||
431 | /* | |
432 | * Composing is done by adding a offset to the buffer address, | |
433 | * the HW wants this address to be aligned to HW_BUFFER_MASK. | |
434 | * Make sure the top and left values meets this requirement. | |
435 | */ | |
436 | while ((r.top * vin->format.bytesperline) & HW_BUFFER_MASK) | |
437 | r.top--; | |
438 | ||
439 | fmt = rvin_format_from_pixel(vin->format.pixelformat); | |
440 | while ((r.left * fmt->bpp) & HW_BUFFER_MASK) | |
441 | r.left--; | |
442 | ||
443 | vin->compose = s->r = r; | |
444 | ||
445 | vin_dbg(vin, "Compose %dx%d@%d:%d in %dx%d\n", | |
7eb95877 NS |
446 | r.width, r.height, r.left, r.top, |
447 | vin->format.width, vin->format.height); | |
f00add96 NS |
448 | break; |
449 | default: | |
450 | return -EINVAL; | |
451 | } | |
452 | ||
453 | /* HW supports modifying configuration while running */ | |
454 | rvin_crop_scale_comp(vin); | |
455 | ||
456 | return 0; | |
457 | } | |
458 | ||
459 | static int rvin_cropcap(struct file *file, void *priv, | |
460 | struct v4l2_cropcap *crop) | |
461 | { | |
462 | struct rvin_dev *vin = video_drvdata(file); | |
463 | struct v4l2_subdev *sd = vin_to_source(vin); | |
464 | ||
465 | if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | |
466 | return -EINVAL; | |
467 | ||
ecf37493 | 468 | return v4l2_subdev_call(sd, video, g_pixelaspect, &crop->pixelaspect); |
f00add96 NS |
469 | } |
470 | ||
471 | static int rvin_enum_input(struct file *file, void *priv, | |
472 | struct v4l2_input *i) | |
473 | { | |
474 | struct rvin_dev *vin = video_drvdata(file); | |
475 | struct v4l2_subdev *sd = vin_to_source(vin); | |
476 | int ret; | |
477 | ||
478 | if (i->index != 0) | |
479 | return -EINVAL; | |
480 | ||
481 | ret = v4l2_subdev_call(sd, video, g_input_status, &i->status); | |
482 | if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) | |
483 | return ret; | |
484 | ||
485 | i->type = V4L2_INPUT_TYPE_CAMERA; | |
80aa2659 | 486 | |
0ad17864 | 487 | if (v4l2_subdev_has_op(sd, pad, dv_timings_cap)) { |
80aa2659 | 488 | i->capabilities = V4L2_IN_CAP_DV_TIMINGS; |
0ad17864 NS |
489 | i->std = 0; |
490 | } else { | |
491 | i->capabilities = V4L2_IN_CAP_STD; | |
492 | i->std = vin->vdev.tvnorms; | |
493 | } | |
80aa2659 | 494 | |
f00add96 NS |
495 | strlcpy(i->name, "Camera", sizeof(i->name)); |
496 | ||
497 | return 0; | |
498 | } | |
499 | ||
500 | static int rvin_g_input(struct file *file, void *priv, unsigned int *i) | |
501 | { | |
502 | *i = 0; | |
503 | return 0; | |
504 | } | |
505 | ||
506 | static int rvin_s_input(struct file *file, void *priv, unsigned int i) | |
507 | { | |
508 | if (i > 0) | |
509 | return -EINVAL; | |
510 | return 0; | |
511 | } | |
512 | ||
513 | static int rvin_querystd(struct file *file, void *priv, v4l2_std_id *a) | |
514 | { | |
515 | struct rvin_dev *vin = video_drvdata(file); | |
516 | struct v4l2_subdev *sd = vin_to_source(vin); | |
517 | ||
518 | return v4l2_subdev_call(sd, video, querystd, a); | |
519 | } | |
520 | ||
521 | static int rvin_s_std(struct file *file, void *priv, v4l2_std_id a) | |
522 | { | |
523 | struct rvin_dev *vin = video_drvdata(file); | |
d6482537 | 524 | int ret; |
f00add96 | 525 | |
d6482537 | 526 | ret = v4l2_subdev_call(vin_to_source(vin), video, s_std, a); |
f00add96 NS |
527 | if (ret < 0) |
528 | return ret; | |
529 | ||
530 | /* Changing the standard will change the width/height */ | |
d6482537 | 531 | return rvin_reset_format(vin); |
f00add96 NS |
532 | } |
533 | ||
534 | static int rvin_g_std(struct file *file, void *priv, v4l2_std_id *a) | |
535 | { | |
536 | struct rvin_dev *vin = video_drvdata(file); | |
537 | struct v4l2_subdev *sd = vin_to_source(vin); | |
538 | ||
539 | return v4l2_subdev_call(sd, video, g_std, a); | |
540 | } | |
541 | ||
542 | static int rvin_subscribe_event(struct v4l2_fh *fh, | |
543 | const struct v4l2_event_subscription *sub) | |
544 | { | |
545 | switch (sub->type) { | |
546 | case V4L2_EVENT_SOURCE_CHANGE: | |
547 | return v4l2_event_subscribe(fh, sub, 4, NULL); | |
548 | } | |
549 | return v4l2_ctrl_subscribe_event(fh, sub); | |
550 | } | |
551 | ||
80aa2659 | 552 | static int rvin_enum_dv_timings(struct file *file, void *priv_fh, |
7eb95877 | 553 | struct v4l2_enum_dv_timings *timings) |
80aa2659 UH |
554 | { |
555 | struct rvin_dev *vin = video_drvdata(file); | |
556 | struct v4l2_subdev *sd = vin_to_source(vin); | |
83108162 NS |
557 | int ret; |
558 | ||
559 | if (timings->pad) | |
560 | return -EINVAL; | |
80aa2659 | 561 | |
1eb36419 | 562 | timings->pad = vin->digital.sink_pad; |
80aa2659 UH |
563 | |
564 | ret = v4l2_subdev_call(sd, pad, enum_dv_timings, timings); | |
565 | ||
83108162 | 566 | timings->pad = 0; |
80aa2659 UH |
567 | |
568 | return ret; | |
569 | } | |
570 | ||
571 | static int rvin_s_dv_timings(struct file *file, void *priv_fh, | |
7eb95877 | 572 | struct v4l2_dv_timings *timings) |
80aa2659 UH |
573 | { |
574 | struct rvin_dev *vin = video_drvdata(file); | |
575 | struct v4l2_subdev *sd = vin_to_source(vin); | |
325527a6 NS |
576 | int ret; |
577 | ||
578 | ret = v4l2_subdev_call(sd, video, s_dv_timings, timings); | |
579 | if (ret) | |
580 | return ret; | |
581 | ||
b655741f NS |
582 | /* Changing the timings will change the width/height */ |
583 | return rvin_reset_format(vin); | |
80aa2659 UH |
584 | } |
585 | ||
586 | static int rvin_g_dv_timings(struct file *file, void *priv_fh, | |
7eb95877 | 587 | struct v4l2_dv_timings *timings) |
80aa2659 UH |
588 | { |
589 | struct rvin_dev *vin = video_drvdata(file); | |
590 | struct v4l2_subdev *sd = vin_to_source(vin); | |
591 | ||
7eb95877 | 592 | return v4l2_subdev_call(sd, video, g_dv_timings, timings); |
80aa2659 UH |
593 | } |
594 | ||
595 | static int rvin_query_dv_timings(struct file *file, void *priv_fh, | |
7eb95877 | 596 | struct v4l2_dv_timings *timings) |
80aa2659 UH |
597 | { |
598 | struct rvin_dev *vin = video_drvdata(file); | |
599 | struct v4l2_subdev *sd = vin_to_source(vin); | |
600 | ||
7eb95877 | 601 | return v4l2_subdev_call(sd, video, query_dv_timings, timings); |
80aa2659 UH |
602 | } |
603 | ||
604 | static int rvin_dv_timings_cap(struct file *file, void *priv_fh, | |
7eb95877 | 605 | struct v4l2_dv_timings_cap *cap) |
80aa2659 UH |
606 | { |
607 | struct rvin_dev *vin = video_drvdata(file); | |
608 | struct v4l2_subdev *sd = vin_to_source(vin); | |
83108162 NS |
609 | int ret; |
610 | ||
611 | if (cap->pad) | |
612 | return -EINVAL; | |
80aa2659 | 613 | |
1eb36419 | 614 | cap->pad = vin->digital.sink_pad; |
80aa2659 UH |
615 | |
616 | ret = v4l2_subdev_call(sd, pad, dv_timings_cap, cap); | |
617 | ||
83108162 | 618 | cap->pad = 0; |
80aa2659 UH |
619 | |
620 | return ret; | |
621 | } | |
622 | ||
8a2192be UH |
623 | static int rvin_g_edid(struct file *file, void *fh, struct v4l2_edid *edid) |
624 | { | |
625 | struct rvin_dev *vin = video_drvdata(file); | |
626 | struct v4l2_subdev *sd = vin_to_source(vin); | |
83108162 | 627 | int ret; |
8a2192be UH |
628 | |
629 | if (edid->pad) | |
630 | return -EINVAL; | |
631 | ||
1eb36419 | 632 | edid->pad = vin->digital.sink_pad; |
8a2192be UH |
633 | |
634 | ret = v4l2_subdev_call(sd, pad, get_edid, edid); | |
635 | ||
83108162 | 636 | edid->pad = 0; |
8a2192be UH |
637 | |
638 | return ret; | |
639 | } | |
640 | ||
641 | static int rvin_s_edid(struct file *file, void *fh, struct v4l2_edid *edid) | |
642 | { | |
643 | struct rvin_dev *vin = video_drvdata(file); | |
644 | struct v4l2_subdev *sd = vin_to_source(vin); | |
83108162 | 645 | int ret; |
8a2192be UH |
646 | |
647 | if (edid->pad) | |
648 | return -EINVAL; | |
649 | ||
1eb36419 | 650 | edid->pad = vin->digital.sink_pad; |
8a2192be UH |
651 | |
652 | ret = v4l2_subdev_call(sd, pad, set_edid, edid); | |
653 | ||
83108162 | 654 | edid->pad = 0; |
8a2192be UH |
655 | |
656 | return ret; | |
657 | } | |
658 | ||
f00add96 NS |
659 | static const struct v4l2_ioctl_ops rvin_ioctl_ops = { |
660 | .vidioc_querycap = rvin_querycap, | |
661 | .vidioc_try_fmt_vid_cap = rvin_try_fmt_vid_cap, | |
662 | .vidioc_g_fmt_vid_cap = rvin_g_fmt_vid_cap, | |
663 | .vidioc_s_fmt_vid_cap = rvin_s_fmt_vid_cap, | |
664 | .vidioc_enum_fmt_vid_cap = rvin_enum_fmt_vid_cap, | |
665 | ||
666 | .vidioc_g_selection = rvin_g_selection, | |
667 | .vidioc_s_selection = rvin_s_selection, | |
668 | ||
669 | .vidioc_cropcap = rvin_cropcap, | |
670 | ||
671 | .vidioc_enum_input = rvin_enum_input, | |
672 | .vidioc_g_input = rvin_g_input, | |
673 | .vidioc_s_input = rvin_s_input, | |
674 | ||
80aa2659 UH |
675 | .vidioc_dv_timings_cap = rvin_dv_timings_cap, |
676 | .vidioc_enum_dv_timings = rvin_enum_dv_timings, | |
677 | .vidioc_g_dv_timings = rvin_g_dv_timings, | |
678 | .vidioc_s_dv_timings = rvin_s_dv_timings, | |
679 | .vidioc_query_dv_timings = rvin_query_dv_timings, | |
680 | ||
8a2192be UH |
681 | .vidioc_g_edid = rvin_g_edid, |
682 | .vidioc_s_edid = rvin_s_edid, | |
683 | ||
f00add96 NS |
684 | .vidioc_querystd = rvin_querystd, |
685 | .vidioc_g_std = rvin_g_std, | |
686 | .vidioc_s_std = rvin_s_std, | |
687 | ||
688 | .vidioc_reqbufs = vb2_ioctl_reqbufs, | |
689 | .vidioc_create_bufs = vb2_ioctl_create_bufs, | |
690 | .vidioc_querybuf = vb2_ioctl_querybuf, | |
691 | .vidioc_qbuf = vb2_ioctl_qbuf, | |
692 | .vidioc_dqbuf = vb2_ioctl_dqbuf, | |
693 | .vidioc_expbuf = vb2_ioctl_expbuf, | |
694 | .vidioc_prepare_buf = vb2_ioctl_prepare_buf, | |
695 | .vidioc_streamon = vb2_ioctl_streamon, | |
696 | .vidioc_streamoff = vb2_ioctl_streamoff, | |
697 | ||
698 | .vidioc_log_status = v4l2_ctrl_log_status, | |
699 | .vidioc_subscribe_event = rvin_subscribe_event, | |
700 | .vidioc_unsubscribe_event = v4l2_event_unsubscribe, | |
701 | }; | |
702 | ||
703 | /* ----------------------------------------------------------------------------- | |
704 | * File Operations | |
705 | */ | |
706 | ||
707 | static int rvin_power_on(struct rvin_dev *vin) | |
708 | { | |
709 | int ret; | |
710 | struct v4l2_subdev *sd = vin_to_source(vin); | |
711 | ||
712 | pm_runtime_get_sync(vin->v4l2_dev.dev); | |
713 | ||
714 | ret = v4l2_subdev_call(sd, core, s_power, 1); | |
715 | if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) | |
716 | return ret; | |
717 | return 0; | |
718 | } | |
719 | ||
720 | static int rvin_power_off(struct rvin_dev *vin) | |
721 | { | |
722 | int ret; | |
723 | struct v4l2_subdev *sd = vin_to_source(vin); | |
724 | ||
725 | ret = v4l2_subdev_call(sd, core, s_power, 0); | |
726 | ||
727 | pm_runtime_put(vin->v4l2_dev.dev); | |
728 | ||
729 | if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) | |
730 | return ret; | |
731 | ||
732 | return 0; | |
733 | } | |
734 | ||
735 | static int rvin_initialize_device(struct file *file) | |
736 | { | |
737 | struct rvin_dev *vin = video_drvdata(file); | |
738 | int ret; | |
739 | ||
740 | struct v4l2_format f = { | |
741 | .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, | |
742 | .fmt.pix = { | |
743 | .width = vin->format.width, | |
744 | .height = vin->format.height, | |
745 | .field = vin->format.field, | |
746 | .colorspace = vin->format.colorspace, | |
747 | .pixelformat = vin->format.pixelformat, | |
748 | }, | |
749 | }; | |
750 | ||
751 | ret = rvin_power_on(vin); | |
752 | if (ret < 0) | |
753 | return ret; | |
754 | ||
755 | pm_runtime_enable(&vin->vdev.dev); | |
756 | ret = pm_runtime_resume(&vin->vdev.dev); | |
757 | if (ret < 0 && ret != -ENOSYS) | |
758 | goto eresume; | |
759 | ||
760 | /* | |
761 | * Try to configure with default parameters. Notice: this is the | |
762 | * very first open, so, we cannot race against other calls, | |
763 | * apart from someone else calling open() simultaneously, but | |
764 | * .host_lock is protecting us against it. | |
765 | */ | |
766 | ret = rvin_s_fmt_vid_cap(file, NULL, &f); | |
767 | if (ret < 0) | |
768 | goto esfmt; | |
769 | ||
770 | v4l2_ctrl_handler_setup(&vin->ctrl_handler); | |
771 | ||
772 | return 0; | |
773 | esfmt: | |
774 | pm_runtime_disable(&vin->vdev.dev); | |
775 | eresume: | |
776 | rvin_power_off(vin); | |
777 | ||
778 | return ret; | |
779 | } | |
780 | ||
781 | static int rvin_open(struct file *file) | |
782 | { | |
783 | struct rvin_dev *vin = video_drvdata(file); | |
784 | int ret; | |
785 | ||
786 | mutex_lock(&vin->lock); | |
787 | ||
788 | file->private_data = vin; | |
789 | ||
790 | ret = v4l2_fh_open(file); | |
791 | if (ret) | |
792 | goto unlock; | |
793 | ||
794 | if (!v4l2_fh_is_singular_file(file)) | |
795 | goto unlock; | |
796 | ||
797 | if (rvin_initialize_device(file)) { | |
798 | v4l2_fh_release(file); | |
799 | ret = -ENODEV; | |
800 | } | |
801 | ||
802 | unlock: | |
803 | mutex_unlock(&vin->lock); | |
804 | return ret; | |
805 | } | |
806 | ||
807 | static int rvin_release(struct file *file) | |
808 | { | |
809 | struct rvin_dev *vin = video_drvdata(file); | |
810 | bool fh_singular; | |
811 | int ret; | |
812 | ||
813 | mutex_lock(&vin->lock); | |
814 | ||
815 | /* Save the singular status before we call the clean-up helper */ | |
816 | fh_singular = v4l2_fh_is_singular_file(file); | |
817 | ||
818 | /* the release helper will cleanup any on-going streaming */ | |
819 | ret = _vb2_fop_release(file, NULL); | |
820 | ||
821 | /* | |
822 | * If this was the last open file. | |
823 | * Then de-initialize hw module. | |
824 | */ | |
825 | if (fh_singular) { | |
826 | pm_runtime_suspend(&vin->vdev.dev); | |
827 | pm_runtime_disable(&vin->vdev.dev); | |
828 | rvin_power_off(vin); | |
829 | } | |
830 | ||
831 | mutex_unlock(&vin->lock); | |
832 | ||
833 | return ret; | |
834 | } | |
835 | ||
836 | static const struct v4l2_file_operations rvin_fops = { | |
837 | .owner = THIS_MODULE, | |
838 | .unlocked_ioctl = video_ioctl2, | |
839 | .open = rvin_open, | |
840 | .release = rvin_release, | |
841 | .poll = vb2_fop_poll, | |
842 | .mmap = vb2_fop_mmap, | |
843 | .read = vb2_fop_read, | |
844 | }; | |
845 | ||
846 | void rvin_v4l2_remove(struct rvin_dev *vin) | |
847 | { | |
848 | v4l2_info(&vin->v4l2_dev, "Removing %s\n", | |
849 | video_device_node_name(&vin->vdev)); | |
850 | ||
851 | /* Checks internaly if handlers have been init or not */ | |
852 | v4l2_ctrl_handler_free(&vin->ctrl_handler); | |
853 | ||
854 | /* Checks internaly if vdev have been init or not */ | |
855 | video_unregister_device(&vin->vdev); | |
856 | } | |
857 | ||
858 | static void rvin_notify(struct v4l2_subdev *sd, | |
859 | unsigned int notification, void *arg) | |
860 | { | |
861 | struct rvin_dev *vin = | |
862 | container_of(sd->v4l2_dev, struct rvin_dev, v4l2_dev); | |
863 | ||
864 | switch (notification) { | |
865 | case V4L2_DEVICE_NOTIFY_EVENT: | |
866 | v4l2_event_queue(&vin->vdev, arg); | |
867 | break; | |
868 | default: | |
869 | break; | |
870 | } | |
871 | } | |
872 | ||
873 | int rvin_v4l2_probe(struct rvin_dev *vin) | |
874 | { | |
f00add96 NS |
875 | struct video_device *vdev = &vin->vdev; |
876 | struct v4l2_subdev *sd = vin_to_source(vin); | |
5de75b1d | 877 | int ret; |
f00add96 NS |
878 | |
879 | v4l2_set_subdev_hostdata(sd, vin); | |
880 | ||
881 | vin->v4l2_dev.notify = rvin_notify; | |
882 | ||
883 | ret = v4l2_subdev_call(sd, video, g_tvnorms, &vin->vdev.tvnorms); | |
884 | if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) | |
885 | return ret; | |
886 | ||
887 | if (vin->vdev.tvnorms == 0) { | |
888 | /* Disable the STD API if there are no tvnorms defined */ | |
889 | v4l2_disable_ioctl(&vin->vdev, VIDIOC_G_STD); | |
890 | v4l2_disable_ioctl(&vin->vdev, VIDIOC_S_STD); | |
891 | v4l2_disable_ioctl(&vin->vdev, VIDIOC_QUERYSTD); | |
892 | v4l2_disable_ioctl(&vin->vdev, VIDIOC_ENUMSTD); | |
893 | } | |
894 | ||
895 | /* Add the controls */ | |
896 | /* | |
897 | * Currently the subdev with the largest number of controls (13) is | |
898 | * ov6550. So let's pick 16 as a hint for the control handler. Note | |
899 | * that this is a hint only: too large and you waste some memory, too | |
900 | * small and there is a (very) small performance hit when looking up | |
901 | * controls in the internal hash. | |
902 | */ | |
903 | ret = v4l2_ctrl_handler_init(&vin->ctrl_handler, 16); | |
904 | if (ret < 0) | |
905 | return ret; | |
906 | ||
907 | ret = v4l2_ctrl_add_handler(&vin->ctrl_handler, sd->ctrl_handler, NULL); | |
908 | if (ret < 0) | |
909 | return ret; | |
910 | ||
911 | /* video node */ | |
912 | vdev->fops = &rvin_fops; | |
913 | vdev->v4l2_dev = &vin->v4l2_dev; | |
914 | vdev->queue = &vin->queue; | |
915 | strlcpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name)); | |
916 | vdev->release = video_device_release_empty; | |
917 | vdev->ioctl_ops = &rvin_ioctl_ops; | |
918 | vdev->lock = &vin->lock; | |
919 | vdev->ctrl_handler = &vin->ctrl_handler; | |
920 | vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | | |
921 | V4L2_CAP_READWRITE; | |
922 | ||
f00add96 | 923 | vin->format.pixelformat = RVIN_DEFAULT_FORMAT; |
d6482537 | 924 | rvin_reset_format(vin); |
f00add96 NS |
925 | |
926 | ret = video_register_device(&vin->vdev, VFL_TYPE_GRABBER, -1); | |
927 | if (ret) { | |
928 | vin_err(vin, "Failed to register video device\n"); | |
929 | return ret; | |
930 | } | |
931 | ||
932 | video_set_drvdata(&vin->vdev, vin); | |
933 | ||
934 | v4l2_info(&vin->v4l2_dev, "Device registered as %s\n", | |
935 | video_device_node_name(&vin->vdev)); | |
936 | ||
937 | return ret; | |
938 | } |