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