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