1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Video Capture Subdev for Freescale i.MX5/6 SOC |
4 | * |
5 | * Copyright (c) 2012-2016 Mentor Graphics Inc. |
6 | */ |
7 | #include <linux/delay.h> |
8 | #include <linux/fs.h> |
9 | #include <linux/module.h> |
10 | #include <linux/pinctrl/consumer.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/sched.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/spinlock.h> |
15 | #include <linux/timer.h> |
16 | #include <media/v4l2-ctrls.h> |
17 | #include <media/v4l2-device.h> |
18 | #include <media/v4l2-event.h> |
19 | #include <media/v4l2-fwnode.h> |
20 | #include <media/v4l2-ioctl.h> |
21 | #include <media/v4l2-mc.h> |
22 | #include <media/v4l2-subdev.h> |
23 | #include <media/videobuf2-dma-contig.h> |
24 | #include <video/imx-ipu-v3.h> |
25 | #include <media/imx.h> |
26 | #include "imx-media.h" |
27 | |
28 | #define IMX_CAPTURE_NAME "imx-capture" |
29 | |
30 | struct capture_priv { |
31 | struct imx_media_dev *md; /* Media device */ |
32 | struct device *dev; /* Physical device */ |
33 | |
34 | struct imx_media_video_dev vdev; /* Video device */ |
35 | struct media_pad vdev_pad; /* Video device pad */ |
36 | |
37 | struct v4l2_subdev *src_sd; /* Source subdev */ |
38 | int src_sd_pad; /* Source subdev pad */ |
39 | |
40 | struct mutex mutex; /* Protect vdev operations */ |
41 | |
42 | struct vb2_queue q; /* The videobuf2 queue */ |
43 | struct list_head ready_q; /* List of queued buffers */ |
44 | spinlock_t q_lock; /* Protect ready_q */ |
45 | |
46 | struct v4l2_ctrl_handler ctrl_hdlr; /* Controls inherited from subdevs */ |
47 | |
48 | bool legacy_api; /* Use the legacy (pre-MC) API */ |
49 | }; |
50 | |
51 | #define to_capture_priv(v) container_of(v, struct capture_priv, vdev) |
52 | |
53 | /* In bytes, per queue */ |
54 | #define VID_MEM_LIMIT SZ_64M |
55 | |
56 | /* ----------------------------------------------------------------------------- |
57 | * MC-Centric Video IOCTLs |
58 | */ |
59 | |
60 | static const struct imx_media_pixfmt *capture_find_format(u32 code, u32 fourcc) |
61 | { |
62 | const struct imx_media_pixfmt *cc; |
63 | |
64 | cc = imx_media_find_ipu_format(code, fmt_sel: PIXFMT_SEL_YUV_RGB); |
65 | if (cc) { |
66 | enum imx_pixfmt_sel fmt_sel = cc->cs == IPUV3_COLORSPACE_YUV |
67 | ? PIXFMT_SEL_YUV : PIXFMT_SEL_RGB; |
68 | |
69 | cc = imx_media_find_pixel_format(fourcc, sel: fmt_sel); |
70 | if (!cc) { |
71 | imx_media_enum_pixel_formats(fourcc: &fourcc, index: 0, sel: fmt_sel, code: 0); |
72 | cc = imx_media_find_pixel_format(fourcc, sel: fmt_sel); |
73 | } |
74 | |
75 | return cc; |
76 | } |
77 | |
78 | return imx_media_find_mbus_format(code, sel: PIXFMT_SEL_ANY); |
79 | } |
80 | |
81 | static int capture_querycap(struct file *file, void *fh, |
82 | struct v4l2_capability *cap) |
83 | { |
84 | struct capture_priv *priv = video_drvdata(file); |
85 | |
86 | strscpy(cap->driver, IMX_CAPTURE_NAME, sizeof(cap->driver)); |
87 | strscpy(cap->card, IMX_CAPTURE_NAME, sizeof(cap->card)); |
88 | snprintf(buf: cap->bus_info, size: sizeof(cap->bus_info), |
89 | fmt: "platform:%s" , dev_name(dev: priv->dev)); |
90 | |
91 | return 0; |
92 | } |
93 | |
94 | static int capture_enum_fmt_vid_cap(struct file *file, void *fh, |
95 | struct v4l2_fmtdesc *f) |
96 | { |
97 | return imx_media_enum_pixel_formats(fourcc: &f->pixelformat, index: f->index, |
98 | sel: PIXFMT_SEL_ANY, code: f->mbus_code); |
99 | } |
100 | |
101 | static int capture_enum_framesizes(struct file *file, void *fh, |
102 | struct v4l2_frmsizeenum *fsize) |
103 | { |
104 | const struct imx_media_pixfmt *cc; |
105 | |
106 | if (fsize->index > 0) |
107 | return -EINVAL; |
108 | |
109 | cc = imx_media_find_pixel_format(fourcc: fsize->pixel_format, sel: PIXFMT_SEL_ANY); |
110 | if (!cc) |
111 | return -EINVAL; |
112 | |
113 | /* |
114 | * TODO: The constraints are hardware-specific and may depend on the |
115 | * pixel format. This should come from the driver using |
116 | * imx_media_capture. |
117 | */ |
118 | fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; |
119 | fsize->stepwise.min_width = 1; |
120 | fsize->stepwise.max_width = 65535; |
121 | fsize->stepwise.min_height = 1; |
122 | fsize->stepwise.max_height = 65535; |
123 | fsize->stepwise.step_width = 1; |
124 | fsize->stepwise.step_height = 1; |
125 | |
126 | return 0; |
127 | } |
128 | |
129 | static int capture_g_fmt_vid_cap(struct file *file, void *fh, |
130 | struct v4l2_format *f) |
131 | { |
132 | struct capture_priv *priv = video_drvdata(file); |
133 | |
134 | f->fmt.pix = priv->vdev.fmt; |
135 | |
136 | return 0; |
137 | } |
138 | |
139 | static const struct imx_media_pixfmt * |
140 | __capture_try_fmt(struct v4l2_pix_format *pixfmt, struct v4l2_rect *compose) |
141 | { |
142 | struct v4l2_mbus_framefmt fmt_src; |
143 | const struct imx_media_pixfmt *cc; |
144 | |
145 | /* |
146 | * Find the pixel format, default to the first supported format if not |
147 | * found. |
148 | */ |
149 | cc = imx_media_find_pixel_format(fourcc: pixfmt->pixelformat, sel: PIXFMT_SEL_ANY); |
150 | if (!cc) { |
151 | imx_media_enum_pixel_formats(fourcc: &pixfmt->pixelformat, index: 0, |
152 | sel: PIXFMT_SEL_ANY, code: 0); |
153 | cc = imx_media_find_pixel_format(fourcc: pixfmt->pixelformat, |
154 | sel: PIXFMT_SEL_ANY); |
155 | } |
156 | |
157 | /* Allow IDMAC interweave but enforce field order from source. */ |
158 | if (V4L2_FIELD_IS_INTERLACED(pixfmt->field)) { |
159 | switch (pixfmt->field) { |
160 | case V4L2_FIELD_SEQ_TB: |
161 | pixfmt->field = V4L2_FIELD_INTERLACED_TB; |
162 | break; |
163 | case V4L2_FIELD_SEQ_BT: |
164 | pixfmt->field = V4L2_FIELD_INTERLACED_BT; |
165 | break; |
166 | default: |
167 | break; |
168 | } |
169 | } |
170 | |
171 | v4l2_fill_mbus_format(mbus_fmt: &fmt_src, pix_fmt: pixfmt, code: 0); |
172 | imx_media_mbus_fmt_to_pix_fmt(pix: pixfmt, mbus: &fmt_src, cc); |
173 | |
174 | if (compose) { |
175 | compose->width = fmt_src.width; |
176 | compose->height = fmt_src.height; |
177 | } |
178 | |
179 | return cc; |
180 | } |
181 | |
182 | static int capture_try_fmt_vid_cap(struct file *file, void *fh, |
183 | struct v4l2_format *f) |
184 | { |
185 | __capture_try_fmt(pixfmt: &f->fmt.pix, NULL); |
186 | return 0; |
187 | } |
188 | |
189 | static int capture_s_fmt_vid_cap(struct file *file, void *fh, |
190 | struct v4l2_format *f) |
191 | { |
192 | struct capture_priv *priv = video_drvdata(file); |
193 | const struct imx_media_pixfmt *cc; |
194 | |
195 | if (vb2_is_busy(q: &priv->q)) { |
196 | dev_err(priv->dev, "%s queue busy\n" , __func__); |
197 | return -EBUSY; |
198 | } |
199 | |
200 | cc = __capture_try_fmt(pixfmt: &f->fmt.pix, compose: &priv->vdev.compose); |
201 | |
202 | priv->vdev.cc = cc; |
203 | priv->vdev.fmt = f->fmt.pix; |
204 | |
205 | return 0; |
206 | } |
207 | |
208 | static int capture_g_selection(struct file *file, void *fh, |
209 | struct v4l2_selection *s) |
210 | { |
211 | struct capture_priv *priv = video_drvdata(file); |
212 | |
213 | switch (s->target) { |
214 | case V4L2_SEL_TGT_COMPOSE: |
215 | case V4L2_SEL_TGT_COMPOSE_DEFAULT: |
216 | case V4L2_SEL_TGT_COMPOSE_BOUNDS: |
217 | /* The compose rectangle is fixed to the source format. */ |
218 | s->r = priv->vdev.compose; |
219 | break; |
220 | case V4L2_SEL_TGT_COMPOSE_PADDED: |
221 | /* |
222 | * The hardware writes with a configurable but fixed DMA burst |
223 | * size. If the source format width is not burst size aligned, |
224 | * the written frame contains padding to the right. |
225 | */ |
226 | s->r.left = 0; |
227 | s->r.top = 0; |
228 | s->r.width = priv->vdev.fmt.width; |
229 | s->r.height = priv->vdev.fmt.height; |
230 | break; |
231 | default: |
232 | return -EINVAL; |
233 | } |
234 | |
235 | return 0; |
236 | } |
237 | |
238 | static int capture_subscribe_event(struct v4l2_fh *fh, |
239 | const struct v4l2_event_subscription *sub) |
240 | { |
241 | switch (sub->type) { |
242 | case V4L2_EVENT_IMX_FRAME_INTERVAL_ERROR: |
243 | return v4l2_event_subscribe(fh, sub, elems: 0, NULL); |
244 | default: |
245 | return -EINVAL; |
246 | } |
247 | } |
248 | |
249 | static const struct v4l2_ioctl_ops capture_ioctl_ops = { |
250 | .vidioc_querycap = capture_querycap, |
251 | |
252 | .vidioc_enum_fmt_vid_cap = capture_enum_fmt_vid_cap, |
253 | .vidioc_enum_framesizes = capture_enum_framesizes, |
254 | |
255 | .vidioc_g_fmt_vid_cap = capture_g_fmt_vid_cap, |
256 | .vidioc_try_fmt_vid_cap = capture_try_fmt_vid_cap, |
257 | .vidioc_s_fmt_vid_cap = capture_s_fmt_vid_cap, |
258 | |
259 | .vidioc_g_selection = capture_g_selection, |
260 | |
261 | .vidioc_reqbufs = vb2_ioctl_reqbufs, |
262 | .vidioc_create_bufs = vb2_ioctl_create_bufs, |
263 | .vidioc_prepare_buf = vb2_ioctl_prepare_buf, |
264 | .vidioc_querybuf = vb2_ioctl_querybuf, |
265 | .vidioc_qbuf = vb2_ioctl_qbuf, |
266 | .vidioc_dqbuf = vb2_ioctl_dqbuf, |
267 | .vidioc_expbuf = vb2_ioctl_expbuf, |
268 | .vidioc_streamon = vb2_ioctl_streamon, |
269 | .vidioc_streamoff = vb2_ioctl_streamoff, |
270 | |
271 | .vidioc_subscribe_event = capture_subscribe_event, |
272 | .vidioc_unsubscribe_event = v4l2_event_unsubscribe, |
273 | }; |
274 | |
275 | /* ----------------------------------------------------------------------------- |
276 | * Legacy Video IOCTLs |
277 | */ |
278 | |
279 | static int capture_legacy_enum_framesizes(struct file *file, void *fh, |
280 | struct v4l2_frmsizeenum *fsize) |
281 | { |
282 | struct capture_priv *priv = video_drvdata(file); |
283 | const struct imx_media_pixfmt *cc; |
284 | struct v4l2_subdev_frame_size_enum fse = { |
285 | .index = fsize->index, |
286 | .pad = priv->src_sd_pad, |
287 | .which = V4L2_SUBDEV_FORMAT_ACTIVE, |
288 | }; |
289 | int ret; |
290 | |
291 | cc = imx_media_find_pixel_format(fourcc: fsize->pixel_format, sel: PIXFMT_SEL_ANY); |
292 | if (!cc) |
293 | return -EINVAL; |
294 | |
295 | fse.code = cc->codes ? cc->codes[0] : 0; |
296 | |
297 | ret = v4l2_subdev_call(priv->src_sd, pad, enum_frame_size, NULL, &fse); |
298 | if (ret) |
299 | return ret; |
300 | |
301 | if (fse.min_width == fse.max_width && |
302 | fse.min_height == fse.max_height) { |
303 | fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; |
304 | fsize->discrete.width = fse.min_width; |
305 | fsize->discrete.height = fse.min_height; |
306 | } else { |
307 | fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; |
308 | fsize->stepwise.min_width = fse.min_width; |
309 | fsize->stepwise.max_width = fse.max_width; |
310 | fsize->stepwise.min_height = fse.min_height; |
311 | fsize->stepwise.max_height = fse.max_height; |
312 | fsize->stepwise.step_width = 1; |
313 | fsize->stepwise.step_height = 1; |
314 | } |
315 | |
316 | return 0; |
317 | } |
318 | |
319 | static int capture_legacy_enum_frameintervals(struct file *file, void *fh, |
320 | struct v4l2_frmivalenum *fival) |
321 | { |
322 | struct capture_priv *priv = video_drvdata(file); |
323 | const struct imx_media_pixfmt *cc; |
324 | struct v4l2_subdev_frame_interval_enum fie = { |
325 | .index = fival->index, |
326 | .pad = priv->src_sd_pad, |
327 | .width = fival->width, |
328 | .height = fival->height, |
329 | .which = V4L2_SUBDEV_FORMAT_ACTIVE, |
330 | }; |
331 | int ret; |
332 | |
333 | cc = imx_media_find_pixel_format(fourcc: fival->pixel_format, sel: PIXFMT_SEL_ANY); |
334 | if (!cc) |
335 | return -EINVAL; |
336 | |
337 | fie.code = cc->codes ? cc->codes[0] : 0; |
338 | |
339 | ret = v4l2_subdev_call(priv->src_sd, pad, enum_frame_interval, |
340 | NULL, &fie); |
341 | if (ret) |
342 | return ret; |
343 | |
344 | fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; |
345 | fival->discrete = fie.interval; |
346 | |
347 | return 0; |
348 | } |
349 | |
350 | static int capture_legacy_enum_fmt_vid_cap(struct file *file, void *fh, |
351 | struct v4l2_fmtdesc *f) |
352 | { |
353 | struct capture_priv *priv = video_drvdata(file); |
354 | const struct imx_media_pixfmt *cc_src; |
355 | struct v4l2_subdev_format fmt_src = { |
356 | .pad = priv->src_sd_pad, |
357 | .which = V4L2_SUBDEV_FORMAT_ACTIVE, |
358 | }; |
359 | u32 fourcc; |
360 | int ret; |
361 | |
362 | ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL, &fmt_src); |
363 | if (ret) { |
364 | dev_err(priv->dev, "failed to get src_sd format\n" ); |
365 | return ret; |
366 | } |
367 | |
368 | cc_src = imx_media_find_ipu_format(code: fmt_src.format.code, |
369 | fmt_sel: PIXFMT_SEL_YUV_RGB); |
370 | if (cc_src) { |
371 | enum imx_pixfmt_sel fmt_sel = |
372 | (cc_src->cs == IPUV3_COLORSPACE_YUV) ? |
373 | PIXFMT_SEL_YUV : PIXFMT_SEL_RGB; |
374 | |
375 | ret = imx_media_enum_pixel_formats(fourcc: &fourcc, index: f->index, sel: fmt_sel, |
376 | code: 0); |
377 | if (ret) |
378 | return ret; |
379 | } else { |
380 | cc_src = imx_media_find_mbus_format(code: fmt_src.format.code, |
381 | sel: PIXFMT_SEL_ANY); |
382 | if (WARN_ON(!cc_src)) |
383 | return -EINVAL; |
384 | |
385 | if (f->index != 0) |
386 | return -EINVAL; |
387 | fourcc = cc_src->fourcc; |
388 | } |
389 | |
390 | f->pixelformat = fourcc; |
391 | |
392 | return 0; |
393 | } |
394 | |
395 | static const struct imx_media_pixfmt * |
396 | __capture_legacy_try_fmt(struct capture_priv *priv, |
397 | struct v4l2_subdev_format *fmt_src, |
398 | struct v4l2_pix_format *pixfmt) |
399 | { |
400 | const struct imx_media_pixfmt *cc; |
401 | |
402 | cc = capture_find_format(code: fmt_src->format.code, fourcc: pixfmt->pixelformat); |
403 | if (WARN_ON(!cc)) |
404 | return NULL; |
405 | |
406 | /* allow IDMAC interweave but enforce field order from source */ |
407 | if (V4L2_FIELD_IS_INTERLACED(pixfmt->field)) { |
408 | switch (fmt_src->format.field) { |
409 | case V4L2_FIELD_SEQ_TB: |
410 | fmt_src->format.field = V4L2_FIELD_INTERLACED_TB; |
411 | break; |
412 | case V4L2_FIELD_SEQ_BT: |
413 | fmt_src->format.field = V4L2_FIELD_INTERLACED_BT; |
414 | break; |
415 | default: |
416 | break; |
417 | } |
418 | } |
419 | |
420 | imx_media_mbus_fmt_to_pix_fmt(pix: pixfmt, mbus: &fmt_src->format, cc); |
421 | |
422 | return cc; |
423 | } |
424 | |
425 | static int capture_legacy_try_fmt_vid_cap(struct file *file, void *fh, |
426 | struct v4l2_format *f) |
427 | { |
428 | struct capture_priv *priv = video_drvdata(file); |
429 | struct v4l2_subdev_format fmt_src = { |
430 | .pad = priv->src_sd_pad, |
431 | .which = V4L2_SUBDEV_FORMAT_ACTIVE, |
432 | }; |
433 | int ret; |
434 | |
435 | ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL, &fmt_src); |
436 | if (ret) |
437 | return ret; |
438 | |
439 | if (!__capture_legacy_try_fmt(priv, fmt_src: &fmt_src, pixfmt: &f->fmt.pix)) |
440 | return -EINVAL; |
441 | |
442 | return 0; |
443 | } |
444 | |
445 | static int capture_legacy_s_fmt_vid_cap(struct file *file, void *fh, |
446 | struct v4l2_format *f) |
447 | { |
448 | struct capture_priv *priv = video_drvdata(file); |
449 | struct v4l2_subdev_format fmt_src = { |
450 | .pad = priv->src_sd_pad, |
451 | .which = V4L2_SUBDEV_FORMAT_ACTIVE, |
452 | }; |
453 | const struct imx_media_pixfmt *cc; |
454 | int ret; |
455 | |
456 | if (vb2_is_busy(q: &priv->q)) { |
457 | dev_err(priv->dev, "%s queue busy\n" , __func__); |
458 | return -EBUSY; |
459 | } |
460 | |
461 | ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL, &fmt_src); |
462 | if (ret) |
463 | return ret; |
464 | |
465 | cc = __capture_legacy_try_fmt(priv, fmt_src: &fmt_src, pixfmt: &f->fmt.pix); |
466 | if (!cc) |
467 | return -EINVAL; |
468 | |
469 | priv->vdev.cc = cc; |
470 | priv->vdev.fmt = f->fmt.pix; |
471 | priv->vdev.compose.width = fmt_src.format.width; |
472 | priv->vdev.compose.height = fmt_src.format.height; |
473 | |
474 | return 0; |
475 | } |
476 | |
477 | static int capture_legacy_querystd(struct file *file, void *fh, |
478 | v4l2_std_id *std) |
479 | { |
480 | struct capture_priv *priv = video_drvdata(file); |
481 | |
482 | return v4l2_subdev_call(priv->src_sd, video, querystd, std); |
483 | } |
484 | |
485 | static int capture_legacy_g_std(struct file *file, void *fh, v4l2_std_id *std) |
486 | { |
487 | struct capture_priv *priv = video_drvdata(file); |
488 | |
489 | return v4l2_subdev_call(priv->src_sd, video, g_std, std); |
490 | } |
491 | |
492 | static int capture_legacy_s_std(struct file *file, void *fh, v4l2_std_id std) |
493 | { |
494 | struct capture_priv *priv = video_drvdata(file); |
495 | |
496 | if (vb2_is_busy(q: &priv->q)) |
497 | return -EBUSY; |
498 | |
499 | return v4l2_subdev_call(priv->src_sd, video, s_std, std); |
500 | } |
501 | |
502 | static int capture_legacy_g_parm(struct file *file, void *fh, |
503 | struct v4l2_streamparm *a) |
504 | { |
505 | struct capture_priv *priv = video_drvdata(file); |
506 | struct v4l2_subdev_frame_interval fi = { |
507 | .pad = priv->src_sd_pad, |
508 | }; |
509 | int ret; |
510 | |
511 | if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) |
512 | return -EINVAL; |
513 | |
514 | ret = v4l2_subdev_call_state_active(priv->src_sd, pad, |
515 | get_frame_interval, &fi); |
516 | if (ret < 0) |
517 | return ret; |
518 | |
519 | a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; |
520 | a->parm.capture.timeperframe = fi.interval; |
521 | |
522 | return 0; |
523 | } |
524 | |
525 | static int capture_legacy_s_parm(struct file *file, void *fh, |
526 | struct v4l2_streamparm *a) |
527 | { |
528 | struct capture_priv *priv = video_drvdata(file); |
529 | struct v4l2_subdev_frame_interval fi = { |
530 | .pad = priv->src_sd_pad, |
531 | }; |
532 | int ret; |
533 | |
534 | if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) |
535 | return -EINVAL; |
536 | |
537 | fi.interval = a->parm.capture.timeperframe; |
538 | ret = v4l2_subdev_call_state_active(priv->src_sd, pad, |
539 | set_frame_interval, &fi); |
540 | if (ret < 0) |
541 | return ret; |
542 | |
543 | a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; |
544 | a->parm.capture.timeperframe = fi.interval; |
545 | |
546 | return 0; |
547 | } |
548 | |
549 | static int capture_legacy_subscribe_event(struct v4l2_fh *fh, |
550 | const struct v4l2_event_subscription *sub) |
551 | { |
552 | switch (sub->type) { |
553 | case V4L2_EVENT_IMX_FRAME_INTERVAL_ERROR: |
554 | return v4l2_event_subscribe(fh, sub, elems: 0, NULL); |
555 | case V4L2_EVENT_SOURCE_CHANGE: |
556 | return v4l2_src_change_event_subscribe(fh, sub); |
557 | case V4L2_EVENT_CTRL: |
558 | return v4l2_ctrl_subscribe_event(fh, sub); |
559 | default: |
560 | return -EINVAL; |
561 | } |
562 | } |
563 | |
564 | static const struct v4l2_ioctl_ops capture_legacy_ioctl_ops = { |
565 | .vidioc_querycap = capture_querycap, |
566 | |
567 | .vidioc_enum_framesizes = capture_legacy_enum_framesizes, |
568 | .vidioc_enum_frameintervals = capture_legacy_enum_frameintervals, |
569 | |
570 | .vidioc_enum_fmt_vid_cap = capture_legacy_enum_fmt_vid_cap, |
571 | .vidioc_g_fmt_vid_cap = capture_g_fmt_vid_cap, |
572 | .vidioc_try_fmt_vid_cap = capture_legacy_try_fmt_vid_cap, |
573 | .vidioc_s_fmt_vid_cap = capture_legacy_s_fmt_vid_cap, |
574 | |
575 | .vidioc_querystd = capture_legacy_querystd, |
576 | .vidioc_g_std = capture_legacy_g_std, |
577 | .vidioc_s_std = capture_legacy_s_std, |
578 | |
579 | .vidioc_g_selection = capture_g_selection, |
580 | |
581 | .vidioc_g_parm = capture_legacy_g_parm, |
582 | .vidioc_s_parm = capture_legacy_s_parm, |
583 | |
584 | .vidioc_reqbufs = vb2_ioctl_reqbufs, |
585 | .vidioc_create_bufs = vb2_ioctl_create_bufs, |
586 | .vidioc_prepare_buf = vb2_ioctl_prepare_buf, |
587 | .vidioc_querybuf = vb2_ioctl_querybuf, |
588 | .vidioc_qbuf = vb2_ioctl_qbuf, |
589 | .vidioc_dqbuf = vb2_ioctl_dqbuf, |
590 | .vidioc_expbuf = vb2_ioctl_expbuf, |
591 | .vidioc_streamon = vb2_ioctl_streamon, |
592 | .vidioc_streamoff = vb2_ioctl_streamoff, |
593 | |
594 | .vidioc_subscribe_event = capture_legacy_subscribe_event, |
595 | .vidioc_unsubscribe_event = v4l2_event_unsubscribe, |
596 | }; |
597 | |
598 | /* ----------------------------------------------------------------------------- |
599 | * Queue Operations |
600 | */ |
601 | |
602 | static int capture_queue_setup(struct vb2_queue *vq, |
603 | unsigned int *nbuffers, |
604 | unsigned int *nplanes, |
605 | unsigned int sizes[], |
606 | struct device *alloc_devs[]) |
607 | { |
608 | struct capture_priv *priv = vb2_get_drv_priv(q: vq); |
609 | struct v4l2_pix_format *pix = &priv->vdev.fmt; |
610 | unsigned int q_num_bufs = vb2_get_num_buffers(q: vq); |
611 | unsigned int count = *nbuffers; |
612 | |
613 | if (vq->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) |
614 | return -EINVAL; |
615 | |
616 | if (*nplanes) { |
617 | if (*nplanes != 1 || sizes[0] < pix->sizeimage) |
618 | return -EINVAL; |
619 | count += q_num_bufs; |
620 | } |
621 | |
622 | count = min_t(__u32, VID_MEM_LIMIT / pix->sizeimage, count); |
623 | |
624 | if (*nplanes) |
625 | *nbuffers = (count < q_num_bufs) ? 0 : |
626 | count - q_num_bufs; |
627 | else |
628 | *nbuffers = count; |
629 | |
630 | *nplanes = 1; |
631 | sizes[0] = pix->sizeimage; |
632 | |
633 | return 0; |
634 | } |
635 | |
636 | static int capture_buf_init(struct vb2_buffer *vb) |
637 | { |
638 | struct imx_media_buffer *buf = to_imx_media_vb(vb); |
639 | |
640 | INIT_LIST_HEAD(list: &buf->list); |
641 | |
642 | return 0; |
643 | } |
644 | |
645 | static int capture_buf_prepare(struct vb2_buffer *vb) |
646 | { |
647 | struct vb2_queue *vq = vb->vb2_queue; |
648 | struct capture_priv *priv = vb2_get_drv_priv(q: vq); |
649 | struct v4l2_pix_format *pix = &priv->vdev.fmt; |
650 | |
651 | if (vb2_plane_size(vb, plane_no: 0) < pix->sizeimage) { |
652 | dev_err(priv->dev, |
653 | "data will not fit into plane (%lu < %lu)\n" , |
654 | vb2_plane_size(vb, 0), (long)pix->sizeimage); |
655 | return -EINVAL; |
656 | } |
657 | |
658 | vb2_set_plane_payload(vb, plane_no: 0, size: pix->sizeimage); |
659 | |
660 | return 0; |
661 | } |
662 | |
663 | static void capture_buf_queue(struct vb2_buffer *vb) |
664 | { |
665 | struct capture_priv *priv = vb2_get_drv_priv(q: vb->vb2_queue); |
666 | struct imx_media_buffer *buf = to_imx_media_vb(vb); |
667 | unsigned long flags; |
668 | |
669 | spin_lock_irqsave(&priv->q_lock, flags); |
670 | |
671 | list_add_tail(new: &buf->list, head: &priv->ready_q); |
672 | |
673 | spin_unlock_irqrestore(lock: &priv->q_lock, flags); |
674 | } |
675 | |
676 | static int capture_validate_fmt(struct capture_priv *priv) |
677 | { |
678 | struct v4l2_subdev_format fmt_src = { |
679 | .pad = priv->src_sd_pad, |
680 | .which = V4L2_SUBDEV_FORMAT_ACTIVE, |
681 | }; |
682 | const struct imx_media_pixfmt *cc; |
683 | int ret; |
684 | |
685 | /* Retrieve the media bus format on the source subdev. */ |
686 | ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL, &fmt_src); |
687 | if (ret) |
688 | return ret; |
689 | |
690 | /* |
691 | * Verify that the media bus size matches the size set on the video |
692 | * node. It is sufficient to check the compose rectangle size without |
693 | * checking the rounded size from vdev.fmt, as the rounded size is |
694 | * derived directly from the compose rectangle size, and will thus |
695 | * always match if the compose rectangle matches. |
696 | */ |
697 | if (priv->vdev.compose.width != fmt_src.format.width || |
698 | priv->vdev.compose.height != fmt_src.format.height) |
699 | return -EPIPE; |
700 | |
701 | /* |
702 | * Verify that the media bus code is compatible with the pixel format |
703 | * set on the video node. |
704 | */ |
705 | cc = capture_find_format(code: fmt_src.format.code, fourcc: 0); |
706 | if (!cc || priv->vdev.cc->cs != cc->cs) |
707 | return -EPIPE; |
708 | |
709 | return 0; |
710 | } |
711 | |
712 | static int capture_start_streaming(struct vb2_queue *vq, unsigned int count) |
713 | { |
714 | struct capture_priv *priv = vb2_get_drv_priv(q: vq); |
715 | struct imx_media_buffer *buf, *tmp; |
716 | unsigned long flags; |
717 | int ret; |
718 | |
719 | ret = capture_validate_fmt(priv); |
720 | if (ret) { |
721 | dev_err(priv->dev, "capture format not valid\n" ); |
722 | goto return_bufs; |
723 | } |
724 | |
725 | ret = imx_media_pipeline_set_stream(imxmd: priv->md, entity: &priv->src_sd->entity, |
726 | on: true); |
727 | if (ret) { |
728 | dev_err(priv->dev, "pipeline start failed with %d\n" , ret); |
729 | goto return_bufs; |
730 | } |
731 | |
732 | return 0; |
733 | |
734 | return_bufs: |
735 | spin_lock_irqsave(&priv->q_lock, flags); |
736 | list_for_each_entry_safe(buf, tmp, &priv->ready_q, list) { |
737 | list_del(entry: &buf->list); |
738 | vb2_buffer_done(vb: &buf->vbuf.vb2_buf, state: VB2_BUF_STATE_QUEUED); |
739 | } |
740 | spin_unlock_irqrestore(lock: &priv->q_lock, flags); |
741 | return ret; |
742 | } |
743 | |
744 | static void capture_stop_streaming(struct vb2_queue *vq) |
745 | { |
746 | struct capture_priv *priv = vb2_get_drv_priv(q: vq); |
747 | struct imx_media_buffer *frame; |
748 | struct imx_media_buffer *tmp; |
749 | unsigned long flags; |
750 | int ret; |
751 | |
752 | ret = imx_media_pipeline_set_stream(imxmd: priv->md, entity: &priv->src_sd->entity, |
753 | on: false); |
754 | if (ret) |
755 | dev_warn(priv->dev, "pipeline stop failed with %d\n" , ret); |
756 | |
757 | /* release all active buffers */ |
758 | spin_lock_irqsave(&priv->q_lock, flags); |
759 | list_for_each_entry_safe(frame, tmp, &priv->ready_q, list) { |
760 | list_del(entry: &frame->list); |
761 | vb2_buffer_done(vb: &frame->vbuf.vb2_buf, state: VB2_BUF_STATE_ERROR); |
762 | } |
763 | spin_unlock_irqrestore(lock: &priv->q_lock, flags); |
764 | } |
765 | |
766 | static const struct vb2_ops capture_qops = { |
767 | .queue_setup = capture_queue_setup, |
768 | .buf_init = capture_buf_init, |
769 | .buf_prepare = capture_buf_prepare, |
770 | .buf_queue = capture_buf_queue, |
771 | .wait_prepare = vb2_ops_wait_prepare, |
772 | .wait_finish = vb2_ops_wait_finish, |
773 | .start_streaming = capture_start_streaming, |
774 | .stop_streaming = capture_stop_streaming, |
775 | }; |
776 | |
777 | /* ----------------------------------------------------------------------------- |
778 | * File Operations |
779 | */ |
780 | |
781 | static int capture_open(struct file *file) |
782 | { |
783 | struct capture_priv *priv = video_drvdata(file); |
784 | struct video_device *vfd = priv->vdev.vfd; |
785 | int ret; |
786 | |
787 | if (mutex_lock_interruptible(&priv->mutex)) |
788 | return -ERESTARTSYS; |
789 | |
790 | ret = v4l2_fh_open(filp: file); |
791 | if (ret) { |
792 | dev_err(priv->dev, "v4l2_fh_open failed\n" ); |
793 | goto out; |
794 | } |
795 | |
796 | ret = v4l2_pipeline_pm_get(entity: &vfd->entity); |
797 | if (ret) |
798 | v4l2_fh_release(filp: file); |
799 | |
800 | out: |
801 | mutex_unlock(lock: &priv->mutex); |
802 | return ret; |
803 | } |
804 | |
805 | static int capture_release(struct file *file) |
806 | { |
807 | struct capture_priv *priv = video_drvdata(file); |
808 | struct video_device *vfd = priv->vdev.vfd; |
809 | struct vb2_queue *vq = &priv->q; |
810 | |
811 | mutex_lock(&priv->mutex); |
812 | |
813 | if (file->private_data == vq->owner) { |
814 | vb2_queue_release(q: vq); |
815 | vq->owner = NULL; |
816 | } |
817 | |
818 | v4l2_pipeline_pm_put(entity: &vfd->entity); |
819 | |
820 | v4l2_fh_release(filp: file); |
821 | mutex_unlock(lock: &priv->mutex); |
822 | return 0; |
823 | } |
824 | |
825 | static const struct v4l2_file_operations capture_fops = { |
826 | .owner = THIS_MODULE, |
827 | .open = capture_open, |
828 | .release = capture_release, |
829 | .poll = vb2_fop_poll, |
830 | .unlocked_ioctl = video_ioctl2, |
831 | .mmap = vb2_fop_mmap, |
832 | }; |
833 | |
834 | /* ----------------------------------------------------------------------------- |
835 | * Public API |
836 | */ |
837 | |
838 | struct imx_media_buffer * |
839 | imx_media_capture_device_next_buf(struct imx_media_video_dev *vdev) |
840 | { |
841 | struct capture_priv *priv = to_capture_priv(vdev); |
842 | struct imx_media_buffer *buf = NULL; |
843 | unsigned long flags; |
844 | |
845 | spin_lock_irqsave(&priv->q_lock, flags); |
846 | |
847 | /* get next queued buffer */ |
848 | if (!list_empty(head: &priv->ready_q)) { |
849 | buf = list_entry(priv->ready_q.next, struct imx_media_buffer, |
850 | list); |
851 | list_del(entry: &buf->list); |
852 | } |
853 | |
854 | spin_unlock_irqrestore(lock: &priv->q_lock, flags); |
855 | |
856 | return buf; |
857 | } |
858 | EXPORT_SYMBOL_GPL(imx_media_capture_device_next_buf); |
859 | |
860 | void imx_media_capture_device_error(struct imx_media_video_dev *vdev) |
861 | { |
862 | struct capture_priv *priv = to_capture_priv(vdev); |
863 | struct vb2_queue *vq = &priv->q; |
864 | unsigned long flags; |
865 | |
866 | if (!vb2_is_streaming(q: vq)) |
867 | return; |
868 | |
869 | spin_lock_irqsave(&priv->q_lock, flags); |
870 | vb2_queue_error(q: vq); |
871 | spin_unlock_irqrestore(lock: &priv->q_lock, flags); |
872 | } |
873 | EXPORT_SYMBOL_GPL(imx_media_capture_device_error); |
874 | |
875 | static int capture_init_format(struct capture_priv *priv) |
876 | { |
877 | struct v4l2_subdev_format fmt_src = { |
878 | .pad = priv->src_sd_pad, |
879 | .which = V4L2_SUBDEV_FORMAT_ACTIVE, |
880 | }; |
881 | struct imx_media_video_dev *vdev = &priv->vdev; |
882 | int ret; |
883 | |
884 | if (priv->legacy_api) { |
885 | ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL, |
886 | &fmt_src); |
887 | if (ret) { |
888 | dev_err(priv->dev, "failed to get source format\n" ); |
889 | return ret; |
890 | } |
891 | } else { |
892 | fmt_src.format.code = MEDIA_BUS_FMT_UYVY8_2X8; |
893 | fmt_src.format.width = IMX_MEDIA_DEF_PIX_WIDTH; |
894 | fmt_src.format.height = IMX_MEDIA_DEF_PIX_HEIGHT; |
895 | } |
896 | |
897 | imx_media_mbus_fmt_to_pix_fmt(pix: &vdev->fmt, mbus: &fmt_src.format, NULL); |
898 | vdev->compose.width = fmt_src.format.width; |
899 | vdev->compose.height = fmt_src.format.height; |
900 | |
901 | vdev->cc = imx_media_find_pixel_format(fourcc: vdev->fmt.pixelformat, |
902 | sel: PIXFMT_SEL_ANY); |
903 | |
904 | return 0; |
905 | } |
906 | |
907 | int imx_media_capture_device_register(struct imx_media_video_dev *vdev, |
908 | u32 link_flags) |
909 | { |
910 | struct capture_priv *priv = to_capture_priv(vdev); |
911 | struct v4l2_subdev *sd = priv->src_sd; |
912 | struct v4l2_device *v4l2_dev = sd->v4l2_dev; |
913 | struct video_device *vfd = vdev->vfd; |
914 | int ret; |
915 | |
916 | /* get media device */ |
917 | priv->md = container_of(v4l2_dev->mdev, struct imx_media_dev, md); |
918 | |
919 | vfd->v4l2_dev = v4l2_dev; |
920 | |
921 | /* Initialize the default format and compose rectangle. */ |
922 | ret = capture_init_format(priv); |
923 | if (ret < 0) |
924 | return ret; |
925 | |
926 | /* Register the video device. */ |
927 | ret = video_register_device(vdev: vfd, type: VFL_TYPE_VIDEO, nr: -1); |
928 | if (ret) { |
929 | dev_err(priv->dev, "Failed to register video device\n" ); |
930 | return ret; |
931 | } |
932 | |
933 | dev_info(priv->dev, "Registered %s as /dev/%s\n" , vfd->name, |
934 | video_device_node_name(vfd)); |
935 | |
936 | /* Create the link from the src_sd devnode pad to device node. */ |
937 | if (link_flags & MEDIA_LNK_FL_IMMUTABLE) |
938 | link_flags |= MEDIA_LNK_FL_ENABLED; |
939 | ret = media_create_pad_link(source: &sd->entity, source_pad: priv->src_sd_pad, |
940 | sink: &vfd->entity, sink_pad: 0, flags: link_flags); |
941 | if (ret) { |
942 | dev_err(priv->dev, "failed to create link to device node\n" ); |
943 | video_unregister_device(vdev: vfd); |
944 | return ret; |
945 | } |
946 | |
947 | /* Add vdev to the video devices list. */ |
948 | imx_media_add_video_device(imxmd: priv->md, vdev); |
949 | |
950 | return 0; |
951 | } |
952 | EXPORT_SYMBOL_GPL(imx_media_capture_device_register); |
953 | |
954 | void imx_media_capture_device_unregister(struct imx_media_video_dev *vdev) |
955 | { |
956 | struct capture_priv *priv = to_capture_priv(vdev); |
957 | struct video_device *vfd = priv->vdev.vfd; |
958 | |
959 | media_entity_cleanup(entity: &vfd->entity); |
960 | video_unregister_device(vdev: vfd); |
961 | } |
962 | EXPORT_SYMBOL_GPL(imx_media_capture_device_unregister); |
963 | |
964 | struct imx_media_video_dev * |
965 | imx_media_capture_device_init(struct device *dev, struct v4l2_subdev *src_sd, |
966 | int pad, bool legacy_api) |
967 | { |
968 | struct capture_priv *priv; |
969 | struct video_device *vfd; |
970 | struct vb2_queue *vq; |
971 | int ret; |
972 | |
973 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
974 | if (!priv) |
975 | return ERR_PTR(error: -ENOMEM); |
976 | |
977 | priv->src_sd = src_sd; |
978 | priv->src_sd_pad = pad; |
979 | priv->dev = dev; |
980 | priv->legacy_api = legacy_api; |
981 | |
982 | mutex_init(&priv->mutex); |
983 | INIT_LIST_HEAD(list: &priv->ready_q); |
984 | spin_lock_init(&priv->q_lock); |
985 | |
986 | /* Allocate and initialize the video device. */ |
987 | vfd = video_device_alloc(); |
988 | if (!vfd) |
989 | return ERR_PTR(error: -ENOMEM); |
990 | |
991 | vfd->fops = &capture_fops; |
992 | vfd->ioctl_ops = legacy_api ? &capture_legacy_ioctl_ops |
993 | : &capture_ioctl_ops; |
994 | vfd->minor = -1; |
995 | vfd->release = video_device_release; |
996 | vfd->vfl_dir = VFL_DIR_RX; |
997 | vfd->tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; |
998 | vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
999 | | (!legacy_api ? V4L2_CAP_IO_MC : 0); |
1000 | vfd->lock = &priv->mutex; |
1001 | vfd->queue = &priv->q; |
1002 | |
1003 | snprintf(buf: vfd->name, size: sizeof(vfd->name), fmt: "%s capture" , src_sd->name); |
1004 | |
1005 | video_set_drvdata(vdev: vfd, data: priv); |
1006 | priv->vdev.vfd = vfd; |
1007 | INIT_LIST_HEAD(list: &priv->vdev.list); |
1008 | |
1009 | /* Initialize the video device pad. */ |
1010 | priv->vdev_pad.flags = MEDIA_PAD_FL_SINK; |
1011 | ret = media_entity_pads_init(entity: &vfd->entity, num_pads: 1, pads: &priv->vdev_pad); |
1012 | if (ret) { |
1013 | video_device_release(vdev: vfd); |
1014 | return ERR_PTR(error: ret); |
1015 | } |
1016 | |
1017 | /* Initialize the vb2 queue. */ |
1018 | vq = &priv->q; |
1019 | vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
1020 | vq->io_modes = VB2_MMAP | VB2_DMABUF; |
1021 | vq->drv_priv = priv; |
1022 | vq->buf_struct_size = sizeof(struct imx_media_buffer); |
1023 | vq->ops = &capture_qops; |
1024 | vq->mem_ops = &vb2_dma_contig_memops; |
1025 | vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; |
1026 | vq->lock = &priv->mutex; |
1027 | vq->min_queued_buffers = 2; |
1028 | vq->dev = priv->dev; |
1029 | |
1030 | ret = vb2_queue_init(q: vq); |
1031 | if (ret) { |
1032 | dev_err(priv->dev, "vb2_queue_init failed\n" ); |
1033 | video_device_release(vdev: vfd); |
1034 | return ERR_PTR(error: ret); |
1035 | } |
1036 | |
1037 | if (legacy_api) { |
1038 | /* Initialize the control handler. */ |
1039 | v4l2_ctrl_handler_init(&priv->ctrl_hdlr, 0); |
1040 | vfd->ctrl_handler = &priv->ctrl_hdlr; |
1041 | } |
1042 | |
1043 | return &priv->vdev; |
1044 | } |
1045 | EXPORT_SYMBOL_GPL(imx_media_capture_device_init); |
1046 | |
1047 | void imx_media_capture_device_remove(struct imx_media_video_dev *vdev) |
1048 | { |
1049 | struct capture_priv *priv = to_capture_priv(vdev); |
1050 | |
1051 | v4l2_ctrl_handler_free(hdl: &priv->ctrl_hdlr); |
1052 | } |
1053 | EXPORT_SYMBOL_GPL(imx_media_capture_device_remove); |
1054 | |
1055 | MODULE_DESCRIPTION("i.MX5/6 v4l2 video capture interface driver" ); |
1056 | MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>" ); |
1057 | MODULE_LICENSE("GPL" ); |
1058 | |