1
2
3
4
5
6
7
8
9
10
11#include <linux/delay.h>
12#include <linux/interrupt.h>
13#include <linux/module.h>
14#include <linux/platform_device.h>
15#include <linux/sched.h>
16#include <linux/slab.h>
17#include <linux/timer.h>
18#include <media/v4l2-ctrls.h>
19#include <media/v4l2-device.h>
20#include <media/v4l2-ioctl.h>
21#include <media/v4l2-mc.h>
22#include <media/v4l2-subdev.h>
23#include <media/imx.h>
24#include "imx-media.h"
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51struct vdic_priv;
52
53struct vdic_pipeline_ops {
54 int (*setup)(struct vdic_priv *priv);
55 void (*start)(struct vdic_priv *priv);
56 void (*stop)(struct vdic_priv *priv);
57 void (*disable)(struct vdic_priv *priv);
58};
59
60
61
62
63#define MIN_W 176
64#define MIN_H 144
65#define MAX_W_VDIC 968
66#define MAX_H_VDIC 2048
67#define W_ALIGN 4
68#define H_ALIGN 1
69#define S_ALIGN 1
70
71struct vdic_priv {
72 struct device *dev;
73 struct ipu_soc *ipu;
74 struct imx_media_dev *md;
75 struct v4l2_subdev sd;
76 struct media_pad pad[VDIC_NUM_PADS];
77 int ipu_id;
78
79
80 struct mutex lock;
81
82
83 struct ipu_vdi *vdi;
84
85 int active_input_pad;
86
87 struct ipuv3_channel *vdi_in_ch_p;
88 struct ipuv3_channel *vdi_in_ch;
89 struct ipuv3_channel *vdi_in_ch_n;
90
91
92 struct vdic_pipeline_ops *ops;
93
94
95 struct imx_media_buffer *curr_in_buf;
96 struct imx_media_buffer *prev_in_buf;
97
98
99
100
101
102 u32 fieldtype;
103 u32 in_stride;
104 u32 field_size;
105
106
107 struct media_entity *src;
108
109 struct v4l2_subdev *sink_sd;
110
111 struct v4l2_mbus_framefmt format_mbus[VDIC_NUM_PADS];
112 const struct imx_media_pixfmt *cc[VDIC_NUM_PADS];
113 struct v4l2_fract frame_interval[VDIC_NUM_PADS];
114
115
116 struct imx_media_video_dev *vdev;
117
118 bool csi_direct;
119
120
121 struct v4l2_ctrl_handler ctrl_hdlr;
122 enum ipu_motion_sel motion;
123
124 int stream_count;
125};
126
127static void vdic_put_ipu_resources(struct vdic_priv *priv)
128{
129 if (priv->vdi_in_ch_p)
130 ipu_idmac_put(priv->vdi_in_ch_p);
131 priv->vdi_in_ch_p = NULL;
132
133 if (priv->vdi_in_ch)
134 ipu_idmac_put(priv->vdi_in_ch);
135 priv->vdi_in_ch = NULL;
136
137 if (priv->vdi_in_ch_n)
138 ipu_idmac_put(priv->vdi_in_ch_n);
139 priv->vdi_in_ch_n = NULL;
140
141 if (!IS_ERR_OR_NULL(priv->vdi))
142 ipu_vdi_put(priv->vdi);
143 priv->vdi = NULL;
144}
145
146static int vdic_get_ipu_resources(struct vdic_priv *priv)
147{
148 int ret, err_chan;
149 struct ipuv3_channel *ch;
150 struct ipu_vdi *vdi;
151
152 priv->ipu = priv->md->ipu[priv->ipu_id];
153
154 vdi = ipu_vdi_get(priv->ipu);
155 if (IS_ERR(vdi)) {
156 v4l2_err(&priv->sd, "failed to get VDIC\n");
157 ret = PTR_ERR(vdi);
158 goto out;
159 }
160 priv->vdi = vdi;
161
162 if (!priv->csi_direct) {
163 ch = ipu_idmac_get(priv->ipu, IPUV3_CHANNEL_MEM_VDI_PREV);
164 if (IS_ERR(ch)) {
165 err_chan = IPUV3_CHANNEL_MEM_VDI_PREV;
166 ret = PTR_ERR(ch);
167 goto out_err_chan;
168 }
169 priv->vdi_in_ch_p = ch;
170
171 ch = ipu_idmac_get(priv->ipu, IPUV3_CHANNEL_MEM_VDI_CUR);
172 if (IS_ERR(ch)) {
173 err_chan = IPUV3_CHANNEL_MEM_VDI_CUR;
174 ret = PTR_ERR(ch);
175 goto out_err_chan;
176 }
177 priv->vdi_in_ch = ch;
178
179 ch = ipu_idmac_get(priv->ipu, IPUV3_CHANNEL_MEM_VDI_NEXT);
180 if (IS_ERR(ch)) {
181 err_chan = IPUV3_CHANNEL_MEM_VDI_NEXT;
182 ret = PTR_ERR(ch);
183 goto out_err_chan;
184 }
185 priv->vdi_in_ch_n = ch;
186 }
187
188 return 0;
189
190out_err_chan:
191 v4l2_err(&priv->sd, "could not get IDMAC channel %u\n", err_chan);
192out:
193 vdic_put_ipu_resources(priv);
194 return ret;
195}
196
197
198
199
200
201
202
203static void __maybe_unused prepare_vdi_in_buffers(struct vdic_priv *priv,
204 struct imx_media_buffer *curr)
205{
206 dma_addr_t prev_phys, curr_phys, next_phys;
207 struct imx_media_buffer *prev;
208 struct vb2_buffer *curr_vb, *prev_vb;
209 u32 fs = priv->field_size;
210 u32 is = priv->in_stride;
211
212
213 priv->prev_in_buf = priv->curr_in_buf;
214 priv->curr_in_buf = curr;
215 prev = priv->prev_in_buf ? priv->prev_in_buf : curr;
216
217 prev_vb = &prev->vbuf.vb2_buf;
218 curr_vb = &curr->vbuf.vb2_buf;
219
220 switch (priv->fieldtype) {
221 case V4L2_FIELD_SEQ_TB:
222 prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0);
223 curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs;
224 next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0);
225 break;
226 case V4L2_FIELD_SEQ_BT:
227 prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + fs;
228 curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0);
229 next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs;
230 break;
231 case V4L2_FIELD_INTERLACED_BT:
232 prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + is;
233 curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0);
234 next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is;
235 break;
236 default:
237
238 prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0);
239 curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is;
240 next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0);
241 break;
242 }
243
244 ipu_cpmem_set_buffer(priv->vdi_in_ch_p, 0, prev_phys);
245 ipu_cpmem_set_buffer(priv->vdi_in_ch, 0, curr_phys);
246 ipu_cpmem_set_buffer(priv->vdi_in_ch_n, 0, next_phys);
247
248 ipu_idmac_select_buffer(priv->vdi_in_ch_p, 0);
249 ipu_idmac_select_buffer(priv->vdi_in_ch, 0);
250 ipu_idmac_select_buffer(priv->vdi_in_ch_n, 0);
251}
252
253static int setup_vdi_channel(struct vdic_priv *priv,
254 struct ipuv3_channel *channel,
255 dma_addr_t phys0, dma_addr_t phys1)
256{
257 struct imx_media_video_dev *vdev = priv->vdev;
258 unsigned int burst_size;
259 struct ipu_image image;
260 int ret;
261
262 ipu_cpmem_zero(channel);
263
264 memset(&image, 0, sizeof(image));
265 image.pix = vdev->fmt.fmt.pix;
266
267 image.pix.height /= 2;
268 image.rect.width = image.pix.width;
269 image.rect.height = image.pix.height;
270 image.phys0 = phys0;
271 image.phys1 = phys1;
272
273 ret = ipu_cpmem_set_image(channel, &image);
274 if (ret)
275 return ret;
276
277 burst_size = (image.pix.width & 0xf) ? 8 : 16;
278 ipu_cpmem_set_burstsize(channel, burst_size);
279
280 ipu_cpmem_set_axi_id(channel, 1);
281
282 ipu_idmac_set_double_buffer(channel, false);
283
284 return 0;
285}
286
287static int vdic_setup_direct(struct vdic_priv *priv)
288{
289
290 ipu_fsu_link(priv->ipu, IPUV3_CHANNEL_CSI_DIRECT,
291 IPUV3_CHANNEL_CSI_VDI_PREV);
292
293 return 0;
294}
295
296static void vdic_start_direct(struct vdic_priv *priv)
297{
298}
299
300static void vdic_stop_direct(struct vdic_priv *priv)
301{
302}
303
304static void vdic_disable_direct(struct vdic_priv *priv)
305{
306 ipu_fsu_unlink(priv->ipu, IPUV3_CHANNEL_CSI_DIRECT,
307 IPUV3_CHANNEL_CSI_VDI_PREV);
308}
309
310static int vdic_setup_indirect(struct vdic_priv *priv)
311{
312 struct v4l2_mbus_framefmt *infmt;
313 const struct imx_media_pixfmt *incc;
314 int in_size, ret;
315
316 infmt = &priv->format_mbus[VDIC_SINK_PAD_IDMAC];
317 incc = priv->cc[VDIC_SINK_PAD_IDMAC];
318
319 in_size = (infmt->width * incc->bpp * infmt->height) >> 3;
320
321
322 priv->field_size = in_size / 2;
323 priv->in_stride = incc->planar ?
324 infmt->width : (infmt->width * incc->bpp) >> 3;
325
326 priv->prev_in_buf = NULL;
327 priv->curr_in_buf = NULL;
328
329 priv->fieldtype = infmt->field;
330
331
332 ret = setup_vdi_channel(priv, priv->vdi_in_ch_p, 0, 0);
333 if (ret)
334 return ret;
335 ret = setup_vdi_channel(priv, priv->vdi_in_ch, 0, 0);
336 if (ret)
337 return ret;
338 return setup_vdi_channel(priv, priv->vdi_in_ch_n, 0, 0);
339}
340
341static void vdic_start_indirect(struct vdic_priv *priv)
342{
343
344 ipu_idmac_enable_channel(priv->vdi_in_ch_p);
345 ipu_idmac_enable_channel(priv->vdi_in_ch);
346 ipu_idmac_enable_channel(priv->vdi_in_ch_n);
347}
348
349static void vdic_stop_indirect(struct vdic_priv *priv)
350{
351
352 ipu_idmac_disable_channel(priv->vdi_in_ch_p);
353 ipu_idmac_disable_channel(priv->vdi_in_ch);
354 ipu_idmac_disable_channel(priv->vdi_in_ch_n);
355}
356
357static void vdic_disable_indirect(struct vdic_priv *priv)
358{
359}
360
361static struct vdic_pipeline_ops direct_ops = {
362 .setup = vdic_setup_direct,
363 .start = vdic_start_direct,
364 .stop = vdic_stop_direct,
365 .disable = vdic_disable_direct,
366};
367
368static struct vdic_pipeline_ops indirect_ops = {
369 .setup = vdic_setup_indirect,
370 .start = vdic_start_indirect,
371 .stop = vdic_stop_indirect,
372 .disable = vdic_disable_indirect,
373};
374
375static int vdic_start(struct vdic_priv *priv)
376{
377 struct v4l2_mbus_framefmt *infmt;
378 int ret;
379
380 infmt = &priv->format_mbus[priv->active_input_pad];
381
382 priv->ops = priv->csi_direct ? &direct_ops : &indirect_ops;
383
384 ret = vdic_get_ipu_resources(priv);
385 if (ret)
386 return ret;
387
388
389
390
391
392
393
394
395 ipu_vdi_setup(priv->vdi, MEDIA_BUS_FMT_UYVY8_2X8,
396 infmt->width, infmt->height);
397 ipu_vdi_set_field_order(priv->vdi, V4L2_STD_UNKNOWN, infmt->field);
398 ipu_vdi_set_motion(priv->vdi, priv->motion);
399
400 ret = priv->ops->setup(priv);
401 if (ret)
402 goto out_put_ipu;
403
404 ipu_vdi_enable(priv->vdi);
405
406 priv->ops->start(priv);
407
408 return 0;
409
410out_put_ipu:
411 vdic_put_ipu_resources(priv);
412 return ret;
413}
414
415static void vdic_stop(struct vdic_priv *priv)
416{
417 priv->ops->stop(priv);
418 ipu_vdi_disable(priv->vdi);
419 priv->ops->disable(priv);
420
421 vdic_put_ipu_resources(priv);
422}
423
424
425
426
427
428static int vdic_s_ctrl(struct v4l2_ctrl *ctrl)
429{
430 struct vdic_priv *priv = container_of(ctrl->handler,
431 struct vdic_priv, ctrl_hdlr);
432 enum ipu_motion_sel motion;
433 int ret = 0;
434
435 mutex_lock(&priv->lock);
436
437 switch (ctrl->id) {
438 case V4L2_CID_DEINTERLACING_MODE:
439 motion = ctrl->val;
440 if (motion != priv->motion) {
441
442 if (priv->stream_count > 0) {
443 ret = -EBUSY;
444 goto out;
445 }
446 priv->motion = motion;
447 }
448 break;
449 default:
450 v4l2_err(&priv->sd, "Invalid control\n");
451 ret = -EINVAL;
452 }
453
454out:
455 mutex_unlock(&priv->lock);
456 return ret;
457}
458
459static const struct v4l2_ctrl_ops vdic_ctrl_ops = {
460 .s_ctrl = vdic_s_ctrl,
461};
462
463static const char * const vdic_ctrl_motion_menu[] = {
464 "No Motion Compensation",
465 "Low Motion",
466 "Medium Motion",
467 "High Motion",
468};
469
470static int vdic_init_controls(struct vdic_priv *priv)
471{
472 struct v4l2_ctrl_handler *hdlr = &priv->ctrl_hdlr;
473 int ret;
474
475 v4l2_ctrl_handler_init(hdlr, 1);
476
477 v4l2_ctrl_new_std_menu_items(hdlr, &vdic_ctrl_ops,
478 V4L2_CID_DEINTERLACING_MODE,
479 HIGH_MOTION, 0, HIGH_MOTION,
480 vdic_ctrl_motion_menu);
481
482 priv->sd.ctrl_handler = hdlr;
483
484 if (hdlr->error) {
485 ret = hdlr->error;
486 goto out_free;
487 }
488
489 v4l2_ctrl_handler_setup(hdlr);
490 return 0;
491
492out_free:
493 v4l2_ctrl_handler_free(hdlr);
494 return ret;
495}
496
497static int vdic_s_stream(struct v4l2_subdev *sd, int enable)
498{
499 struct vdic_priv *priv = v4l2_get_subdevdata(sd);
500 struct v4l2_subdev *src_sd = NULL;
501 int ret = 0;
502
503 mutex_lock(&priv->lock);
504
505 if (!priv->src || !priv->sink_sd) {
506 ret = -EPIPE;
507 goto out;
508 }
509
510 if (priv->csi_direct)
511 src_sd = media_entity_to_v4l2_subdev(priv->src);
512
513
514
515
516
517 if (priv->stream_count != !enable)
518 goto update_count;
519
520 dev_dbg(priv->dev, "stream %s\n", enable ? "ON" : "OFF");
521
522 if (enable)
523 ret = vdic_start(priv);
524 else
525 vdic_stop(priv);
526 if (ret)
527 goto out;
528
529 if (src_sd) {
530
531 ret = v4l2_subdev_call(src_sd, video, s_stream, enable);
532 ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0;
533 if (ret) {
534 if (enable)
535 vdic_stop(priv);
536 goto out;
537 }
538 }
539
540update_count:
541 priv->stream_count += enable ? 1 : -1;
542 if (priv->stream_count < 0)
543 priv->stream_count = 0;
544out:
545 mutex_unlock(&priv->lock);
546 return ret;
547}
548
549static struct v4l2_mbus_framefmt *
550__vdic_get_fmt(struct vdic_priv *priv, struct v4l2_subdev_pad_config *cfg,
551 unsigned int pad, enum v4l2_subdev_format_whence which)
552{
553 if (which == V4L2_SUBDEV_FORMAT_TRY)
554 return v4l2_subdev_get_try_format(&priv->sd, cfg, pad);
555 else
556 return &priv->format_mbus[pad];
557}
558
559static int vdic_enum_mbus_code(struct v4l2_subdev *sd,
560 struct v4l2_subdev_pad_config *cfg,
561 struct v4l2_subdev_mbus_code_enum *code)
562{
563 if (code->pad >= VDIC_NUM_PADS)
564 return -EINVAL;
565
566 return imx_media_enum_ipu_format(&code->code, code->index, CS_SEL_YUV);
567}
568
569static int vdic_get_fmt(struct v4l2_subdev *sd,
570 struct v4l2_subdev_pad_config *cfg,
571 struct v4l2_subdev_format *sdformat)
572{
573 struct vdic_priv *priv = v4l2_get_subdevdata(sd);
574 struct v4l2_mbus_framefmt *fmt;
575 int ret = 0;
576
577 if (sdformat->pad >= VDIC_NUM_PADS)
578 return -EINVAL;
579
580 mutex_lock(&priv->lock);
581
582 fmt = __vdic_get_fmt(priv, cfg, sdformat->pad, sdformat->which);
583 if (!fmt) {
584 ret = -EINVAL;
585 goto out;
586 }
587
588 sdformat->format = *fmt;
589out:
590 mutex_unlock(&priv->lock);
591 return ret;
592}
593
594static void vdic_try_fmt(struct vdic_priv *priv,
595 struct v4l2_subdev_pad_config *cfg,
596 struct v4l2_subdev_format *sdformat,
597 const struct imx_media_pixfmt **cc)
598{
599 struct v4l2_mbus_framefmt *infmt;
600
601 *cc = imx_media_find_ipu_format(sdformat->format.code, CS_SEL_YUV);
602 if (!*cc) {
603 u32 code;
604
605 imx_media_enum_ipu_format(&code, 0, CS_SEL_YUV);
606 *cc = imx_media_find_ipu_format(code, CS_SEL_YUV);
607 sdformat->format.code = (*cc)->codes[0];
608 }
609
610 infmt = __vdic_get_fmt(priv, cfg, priv->active_input_pad,
611 sdformat->which);
612
613 switch (sdformat->pad) {
614 case VDIC_SRC_PAD_DIRECT:
615 sdformat->format = *infmt;
616
617 sdformat->format.field = V4L2_FIELD_NONE;
618 break;
619 case VDIC_SINK_PAD_DIRECT:
620 case VDIC_SINK_PAD_IDMAC:
621 v4l_bound_align_image(&sdformat->format.width,
622 MIN_W, MAX_W_VDIC, W_ALIGN,
623 &sdformat->format.height,
624 MIN_H, MAX_H_VDIC, H_ALIGN, S_ALIGN);
625
626 imx_media_fill_default_mbus_fields(&sdformat->format, infmt,
627 true);
628
629
630 if (!V4L2_FIELD_HAS_BOTH(sdformat->format.field))
631 sdformat->format.field = V4L2_FIELD_SEQ_TB;
632 break;
633 }
634}
635
636static int vdic_set_fmt(struct v4l2_subdev *sd,
637 struct v4l2_subdev_pad_config *cfg,
638 struct v4l2_subdev_format *sdformat)
639{
640 struct vdic_priv *priv = v4l2_get_subdevdata(sd);
641 const struct imx_media_pixfmt *cc;
642 struct v4l2_mbus_framefmt *fmt;
643 int ret = 0;
644
645 if (sdformat->pad >= VDIC_NUM_PADS)
646 return -EINVAL;
647
648 mutex_lock(&priv->lock);
649
650 if (priv->stream_count > 0) {
651 ret = -EBUSY;
652 goto out;
653 }
654
655 vdic_try_fmt(priv, cfg, sdformat, &cc);
656
657 fmt = __vdic_get_fmt(priv, cfg, sdformat->pad, sdformat->which);
658 *fmt = sdformat->format;
659
660
661 if (sdformat->pad == VDIC_SINK_PAD_DIRECT ||
662 sdformat->pad == VDIC_SINK_PAD_IDMAC) {
663 const struct imx_media_pixfmt *outcc;
664 struct v4l2_mbus_framefmt *outfmt;
665 struct v4l2_subdev_format format;
666
667 format.pad = VDIC_SRC_PAD_DIRECT;
668 format.which = sdformat->which;
669 format.format = sdformat->format;
670 vdic_try_fmt(priv, cfg, &format, &outcc);
671
672 outfmt = __vdic_get_fmt(priv, cfg, VDIC_SRC_PAD_DIRECT,
673 sdformat->which);
674 *outfmt = format.format;
675 if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
676 priv->cc[VDIC_SRC_PAD_DIRECT] = outcc;
677 }
678
679 if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
680 priv->cc[sdformat->pad] = cc;
681out:
682 mutex_unlock(&priv->lock);
683 return ret;
684}
685
686static int vdic_link_setup(struct media_entity *entity,
687 const struct media_pad *local,
688 const struct media_pad *remote, u32 flags)
689{
690 struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
691 struct vdic_priv *priv = v4l2_get_subdevdata(sd);
692 struct v4l2_subdev *remote_sd;
693 int ret = 0;
694
695 dev_dbg(priv->dev, "link setup %s -> %s", remote->entity->name,
696 local->entity->name);
697
698 mutex_lock(&priv->lock);
699
700 if (local->flags & MEDIA_PAD_FL_SOURCE) {
701 if (!is_media_entity_v4l2_subdev(remote->entity)) {
702 ret = -EINVAL;
703 goto out;
704 }
705
706 remote_sd = media_entity_to_v4l2_subdev(remote->entity);
707
708 if (flags & MEDIA_LNK_FL_ENABLED) {
709 if (priv->sink_sd) {
710 ret = -EBUSY;
711 goto out;
712 }
713 priv->sink_sd = remote_sd;
714 } else {
715 priv->sink_sd = NULL;
716 }
717
718 goto out;
719 }
720
721
722
723 if (flags & MEDIA_LNK_FL_ENABLED) {
724 if (priv->src) {
725 ret = -EBUSY;
726 goto out;
727 }
728 } else {
729 priv->src = NULL;
730 goto out;
731 }
732
733 if (local->index == VDIC_SINK_PAD_IDMAC) {
734 struct imx_media_video_dev *vdev = priv->vdev;
735
736 if (!is_media_entity_v4l2_video_device(remote->entity)) {
737 ret = -EINVAL;
738 goto out;
739 }
740 if (!vdev) {
741 ret = -ENODEV;
742 goto out;
743 }
744
745 priv->csi_direct = false;
746 } else {
747 if (!is_media_entity_v4l2_subdev(remote->entity)) {
748 ret = -EINVAL;
749 goto out;
750 }
751
752 remote_sd = media_entity_to_v4l2_subdev(remote->entity);
753
754
755 if (!(remote_sd->grp_id & IMX_MEDIA_GRP_ID_CSI) ||
756 remote->index != CSI_SRC_PAD_DIRECT) {
757 ret = -EINVAL;
758 goto out;
759 }
760
761 priv->csi_direct = true;
762 }
763
764 priv->src = remote->entity;
765
766 priv->active_input_pad = local->index;
767out:
768 mutex_unlock(&priv->lock);
769 return ret;
770}
771
772static int vdic_link_validate(struct v4l2_subdev *sd,
773 struct media_link *link,
774 struct v4l2_subdev_format *source_fmt,
775 struct v4l2_subdev_format *sink_fmt)
776{
777 struct vdic_priv *priv = v4l2_get_subdevdata(sd);
778 int ret;
779
780 ret = v4l2_subdev_link_validate_default(sd, link,
781 source_fmt, sink_fmt);
782 if (ret)
783 return ret;
784
785 mutex_lock(&priv->lock);
786
787 if (priv->csi_direct && priv->motion != HIGH_MOTION) {
788 v4l2_err(&priv->sd,
789 "direct CSI pipeline requires high motion\n");
790 ret = -EINVAL;
791 }
792
793 mutex_unlock(&priv->lock);
794 return ret;
795}
796
797static int vdic_g_frame_interval(struct v4l2_subdev *sd,
798 struct v4l2_subdev_frame_interval *fi)
799{
800 struct vdic_priv *priv = v4l2_get_subdevdata(sd);
801
802 if (fi->pad >= VDIC_NUM_PADS)
803 return -EINVAL;
804
805 mutex_lock(&priv->lock);
806
807 fi->interval = priv->frame_interval[fi->pad];
808
809 mutex_unlock(&priv->lock);
810
811 return 0;
812}
813
814static int vdic_s_frame_interval(struct v4l2_subdev *sd,
815 struct v4l2_subdev_frame_interval *fi)
816{
817 struct vdic_priv *priv = v4l2_get_subdevdata(sd);
818 struct v4l2_fract *input_fi, *output_fi;
819 int ret = 0;
820
821 mutex_lock(&priv->lock);
822
823 input_fi = &priv->frame_interval[priv->active_input_pad];
824 output_fi = &priv->frame_interval[VDIC_SRC_PAD_DIRECT];
825
826 switch (fi->pad) {
827 case VDIC_SINK_PAD_DIRECT:
828 case VDIC_SINK_PAD_IDMAC:
829
830
831 *output_fi = fi->interval;
832 if (priv->csi_direct)
833 output_fi->denominator *= 2;
834 break;
835 case VDIC_SRC_PAD_DIRECT:
836
837
838
839
840
841
842 fi->interval = *input_fi;
843 if (priv->csi_direct)
844 fi->interval.denominator *= 2;
845 break;
846 default:
847 ret = -EINVAL;
848 goto out;
849 }
850
851 priv->frame_interval[fi->pad] = fi->interval;
852out:
853 mutex_unlock(&priv->lock);
854 return ret;
855}
856
857
858
859
860static int vdic_registered(struct v4l2_subdev *sd)
861{
862 struct vdic_priv *priv = v4l2_get_subdevdata(sd);
863 int i, ret;
864 u32 code;
865
866
867 priv->md = dev_get_drvdata(sd->v4l2_dev->dev);
868
869 for (i = 0; i < VDIC_NUM_PADS; i++) {
870 priv->pad[i].flags = (i == VDIC_SRC_PAD_DIRECT) ?
871 MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK;
872
873 code = 0;
874 if (i != VDIC_SINK_PAD_IDMAC)
875 imx_media_enum_ipu_format(&code, 0, CS_SEL_YUV);
876
877
878 ret = imx_media_init_mbus_fmt(&priv->format_mbus[i],
879 640, 480, code, V4L2_FIELD_NONE,
880 &priv->cc[i]);
881 if (ret)
882 return ret;
883
884
885 priv->frame_interval[i].numerator = 1;
886 priv->frame_interval[i].denominator = 30;
887 if (i == VDIC_SRC_PAD_DIRECT)
888 priv->frame_interval[i].denominator *= 2;
889 }
890
891 priv->active_input_pad = VDIC_SINK_PAD_DIRECT;
892
893 ret = vdic_init_controls(priv);
894 if (ret)
895 return ret;
896
897 ret = media_entity_pads_init(&sd->entity, VDIC_NUM_PADS, priv->pad);
898 if (ret)
899 v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
900
901 return ret;
902}
903
904static void vdic_unregistered(struct v4l2_subdev *sd)
905{
906 struct vdic_priv *priv = v4l2_get_subdevdata(sd);
907
908 v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
909}
910
911static const struct v4l2_subdev_pad_ops vdic_pad_ops = {
912 .init_cfg = imx_media_init_cfg,
913 .enum_mbus_code = vdic_enum_mbus_code,
914 .get_fmt = vdic_get_fmt,
915 .set_fmt = vdic_set_fmt,
916 .link_validate = vdic_link_validate,
917};
918
919static const struct v4l2_subdev_video_ops vdic_video_ops = {
920 .g_frame_interval = vdic_g_frame_interval,
921 .s_frame_interval = vdic_s_frame_interval,
922 .s_stream = vdic_s_stream,
923};
924
925static const struct media_entity_operations vdic_entity_ops = {
926 .link_setup = vdic_link_setup,
927 .link_validate = v4l2_subdev_link_validate,
928};
929
930static const struct v4l2_subdev_ops vdic_subdev_ops = {
931 .video = &vdic_video_ops,
932 .pad = &vdic_pad_ops,
933};
934
935static const struct v4l2_subdev_internal_ops vdic_internal_ops = {
936 .registered = vdic_registered,
937 .unregistered = vdic_unregistered,
938};
939
940static int imx_vdic_probe(struct platform_device *pdev)
941{
942 struct imx_media_internal_sd_platformdata *pdata;
943 struct vdic_priv *priv;
944 int ret;
945
946 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
947 if (!priv)
948 return -ENOMEM;
949
950 platform_set_drvdata(pdev, &priv->sd);
951 priv->dev = &pdev->dev;
952
953 pdata = priv->dev->platform_data;
954 priv->ipu_id = pdata->ipu_id;
955
956 v4l2_subdev_init(&priv->sd, &vdic_subdev_ops);
957 v4l2_set_subdevdata(&priv->sd, priv);
958 priv->sd.internal_ops = &vdic_internal_ops;
959 priv->sd.entity.ops = &vdic_entity_ops;
960 priv->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
961 priv->sd.dev = &pdev->dev;
962 priv->sd.owner = THIS_MODULE;
963 priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
964
965 priv->sd.grp_id = pdata->grp_id;
966 strncpy(priv->sd.name, pdata->sd_name, sizeof(priv->sd.name));
967
968 mutex_init(&priv->lock);
969
970 ret = v4l2_async_register_subdev(&priv->sd);
971 if (ret)
972 goto free;
973
974 return 0;
975free:
976 mutex_destroy(&priv->lock);
977 return ret;
978}
979
980static int imx_vdic_remove(struct platform_device *pdev)
981{
982 struct v4l2_subdev *sd = platform_get_drvdata(pdev);
983 struct vdic_priv *priv = v4l2_get_subdevdata(sd);
984
985 v4l2_info(sd, "Removing\n");
986
987 v4l2_async_unregister_subdev(sd);
988 mutex_destroy(&priv->lock);
989 media_entity_cleanup(&sd->entity);
990
991 return 0;
992}
993
994static const struct platform_device_id imx_vdic_ids[] = {
995 { .name = "imx-ipuv3-vdic" },
996 { },
997};
998MODULE_DEVICE_TABLE(platform, imx_vdic_ids);
999
1000static struct platform_driver imx_vdic_driver = {
1001 .probe = imx_vdic_probe,
1002 .remove = imx_vdic_remove,
1003 .id_table = imx_vdic_ids,
1004 .driver = {
1005 .name = "imx-ipuv3-vdic",
1006 },
1007};
1008module_platform_driver(imx_vdic_driver);
1009
1010MODULE_DESCRIPTION("i.MX VDIC subdev driver");
1011MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>");
1012MODULE_LICENSE("GPL");
1013MODULE_ALIAS("platform:imx-ipuv3-vdic");
1014