1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include <linux/module.h>
18#include <linux/of.h>
19#include <linux/of_device.h>
20#include <linux/of_graph.h>
21#include <linux/platform_device.h>
22#include <linux/pm_runtime.h>
23#include <linux/slab.h>
24#include <linux/sys_soc.h>
25
26#include <media/v4l2-async.h>
27#include <media/v4l2-fwnode.h>
28#include <media/v4l2-mc.h>
29
30#include "rcar-vin.h"
31
32
33
34
35
36
37
38
39#define rvin_group_csi_pad_to_channel(pad) ((pad) - 1)
40#define rvin_group_csi_channel_to_pad(channel) ((channel) + 1)
41
42
43
44
45
46
47#define rvin_group_id_to_master(vin) ((vin) < 4 ? 0 : 4)
48
49
50
51
52
53
54static int rvin_group_entity_to_csi_id(struct rvin_group *group,
55 struct media_entity *entity)
56{
57 struct v4l2_subdev *sd;
58 unsigned int i;
59
60 sd = media_entity_to_v4l2_subdev(entity);
61
62 for (i = 0; i < RVIN_CSI_MAX; i++)
63 if (group->csi[i].subdev == sd)
64 return i;
65
66 return -ENODEV;
67}
68
69static unsigned int rvin_group_get_mask(struct rvin_dev *vin,
70 enum rvin_csi_id csi_id,
71 unsigned char channel)
72{
73 const struct rvin_group_route *route;
74 unsigned int mask = 0;
75
76 for (route = vin->info->routes; route->mask; route++) {
77 if (route->vin == vin->id &&
78 route->csi == csi_id &&
79 route->channel == channel) {
80 vin_dbg(vin,
81 "Adding route: vin: %d csi: %d channel: %d\n",
82 route->vin, route->csi, route->channel);
83 mask |= route->mask;
84 }
85 }
86
87 return mask;
88}
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114static int rvin_group_link_notify(struct media_link *link, u32 flags,
115 unsigned int notification)
116{
117 struct rvin_group *group = container_of(link->graph_obj.mdev,
118 struct rvin_group, mdev);
119 unsigned int master_id, channel, mask_new, i;
120 unsigned int mask = ~0;
121 struct media_entity *entity;
122 struct video_device *vdev;
123 struct media_pad *csi_pad;
124 struct rvin_dev *vin = NULL;
125 int csi_id, ret;
126
127 ret = v4l2_pipeline_link_notify(link, flags, notification);
128 if (ret)
129 return ret;
130
131
132 if (!(flags & MEDIA_LNK_FL_ENABLED) ||
133 !is_media_entity_v4l2_video_device(link->sink->entity))
134 return 0;
135
136
137 media_device_for_each_entity(entity, &group->mdev)
138 if (entity->use_count)
139 return -EBUSY;
140
141 mutex_lock(&group->lock);
142
143
144 vdev = media_entity_to_video_device(link->sink->entity);
145 vin = container_of(vdev, struct rvin_dev, vdev);
146 master_id = rvin_group_id_to_master(vin->id);
147
148 if (WARN_ON(!group->vin[master_id])) {
149 ret = -ENODEV;
150 goto out;
151 }
152
153
154 for (i = master_id; i < master_id + 4; i++) {
155 if (!group->vin[i])
156 continue;
157
158
159 csi_pad = media_entity_remote_pad(
160 &group->vin[i]->vdev.entity.pads[0]);
161 if (!csi_pad)
162 continue;
163
164 csi_id = rvin_group_entity_to_csi_id(group, csi_pad->entity);
165 channel = rvin_group_csi_pad_to_channel(csi_pad->index);
166
167 mask &= rvin_group_get_mask(group->vin[i], csi_id, channel);
168 }
169
170
171 csi_id = rvin_group_entity_to_csi_id(group, link->source->entity);
172 channel = rvin_group_csi_pad_to_channel(link->source->index);
173 mask_new = mask & rvin_group_get_mask(vin, csi_id, channel);
174
175 vin_dbg(vin, "Try link change mask: 0x%x new: 0x%x\n", mask, mask_new);
176
177 if (!mask_new) {
178 ret = -EMLINK;
179 goto out;
180 }
181
182
183 ret = rvin_set_channel_routing(group->vin[master_id], __ffs(mask_new));
184out:
185 mutex_unlock(&group->lock);
186
187 return ret;
188}
189
190static const struct media_device_ops rvin_media_ops = {
191 .link_notify = rvin_group_link_notify,
192};
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207static DEFINE_MUTEX(rvin_group_lock);
208static struct rvin_group *rvin_group_data;
209
210static void rvin_group_cleanup(struct rvin_group *group)
211{
212 media_device_unregister(&group->mdev);
213 media_device_cleanup(&group->mdev);
214 mutex_destroy(&group->lock);
215}
216
217static int rvin_group_init(struct rvin_group *group, struct rvin_dev *vin)
218{
219 struct media_device *mdev = &group->mdev;
220 const struct of_device_id *match;
221 struct device_node *np;
222 int ret;
223
224 mutex_init(&group->lock);
225
226
227 group->count = 0;
228 for_each_matching_node(np, vin->dev->driver->of_match_table)
229 if (of_device_is_available(np))
230 group->count++;
231
232 vin_dbg(vin, "found %u enabled VIN's in DT", group->count);
233
234 mdev->dev = vin->dev;
235 mdev->ops = &rvin_media_ops;
236
237 match = of_match_node(vin->dev->driver->of_match_table,
238 vin->dev->of_node);
239
240 strlcpy(mdev->driver_name, KBUILD_MODNAME, sizeof(mdev->driver_name));
241 strlcpy(mdev->model, match->compatible, sizeof(mdev->model));
242 snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s",
243 dev_name(mdev->dev));
244
245 media_device_init(mdev);
246
247 ret = media_device_register(&group->mdev);
248 if (ret)
249 rvin_group_cleanup(group);
250
251 return ret;
252}
253
254static void rvin_group_release(struct kref *kref)
255{
256 struct rvin_group *group =
257 container_of(kref, struct rvin_group, refcount);
258
259 mutex_lock(&rvin_group_lock);
260
261 rvin_group_data = NULL;
262
263 rvin_group_cleanup(group);
264
265 kfree(group);
266
267 mutex_unlock(&rvin_group_lock);
268}
269
270static int rvin_group_get(struct rvin_dev *vin)
271{
272 struct rvin_group *group;
273 u32 id;
274 int ret;
275
276
277 ret = of_property_read_u32(vin->dev->of_node, "renesas,id", &id);
278 if (ret) {
279 vin_err(vin, "%pOF: No renesas,id property found\n",
280 vin->dev->of_node);
281 return -EINVAL;
282 }
283
284 if (id >= RCAR_VIN_NUM) {
285 vin_err(vin, "%pOF: Invalid renesas,id '%u'\n",
286 vin->dev->of_node, id);
287 return -EINVAL;
288 }
289
290
291 mutex_lock(&rvin_group_lock);
292 if (rvin_group_data) {
293 group = rvin_group_data;
294 kref_get(&group->refcount);
295 } else {
296 group = kzalloc(sizeof(*group), GFP_KERNEL);
297 if (!group) {
298 ret = -ENOMEM;
299 goto err_group;
300 }
301
302 ret = rvin_group_init(group, vin);
303 if (ret) {
304 kfree(group);
305 vin_err(vin, "Failed to initialize group\n");
306 goto err_group;
307 }
308
309 kref_init(&group->refcount);
310
311 rvin_group_data = group;
312 }
313 mutex_unlock(&rvin_group_lock);
314
315
316 mutex_lock(&group->lock);
317
318 if (group->vin[id]) {
319 vin_err(vin, "Duplicate renesas,id property value %u\n", id);
320 mutex_unlock(&group->lock);
321 kref_put(&group->refcount, rvin_group_release);
322 return -EINVAL;
323 }
324
325 group->vin[id] = vin;
326
327 vin->id = id;
328 vin->group = group;
329 vin->v4l2_dev.mdev = &group->mdev;
330
331 mutex_unlock(&group->lock);
332
333 return 0;
334err_group:
335 mutex_unlock(&rvin_group_lock);
336 return ret;
337}
338
339static void rvin_group_put(struct rvin_dev *vin)
340{
341 struct rvin_group *group = vin->group;
342
343 mutex_lock(&group->lock);
344
345 vin->group = NULL;
346 vin->v4l2_dev.mdev = NULL;
347
348 if (WARN_ON(group->vin[vin->id] != vin))
349 goto out;
350
351 group->vin[vin->id] = NULL;
352out:
353 mutex_unlock(&group->lock);
354
355 kref_put(&group->refcount, rvin_group_release);
356}
357
358
359
360
361
362#define notifier_to_vin(n) container_of(n, struct rvin_dev, notifier)
363
364static int rvin_find_pad(struct v4l2_subdev *sd, int direction)
365{
366 unsigned int pad;
367
368 if (sd->entity.num_pads <= 1)
369 return 0;
370
371 for (pad = 0; pad < sd->entity.num_pads; pad++)
372 if (sd->entity.pads[pad].flags & direction)
373 return pad;
374
375 return -EINVAL;
376}
377
378
379
380
381
382
383static int rvin_digital_subdevice_attach(struct rvin_dev *vin,
384 struct v4l2_subdev *subdev)
385{
386 struct v4l2_subdev_mbus_code_enum code = {
387 .which = V4L2_SUBDEV_FORMAT_ACTIVE,
388 };
389 int ret;
390
391
392 ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SOURCE);
393 if (ret < 0)
394 return ret;
395 vin->digital->source_pad = ret;
396
397 ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SINK);
398 vin->digital->sink_pad = ret < 0 ? 0 : ret;
399
400
401 vin->mbus_code = 0;
402 code.index = 0;
403 code.pad = vin->digital->source_pad;
404 while (!vin->mbus_code &&
405 !v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &code)) {
406 code.index++;
407 switch (code.code) {
408 case MEDIA_BUS_FMT_YUYV8_1X16:
409 case MEDIA_BUS_FMT_UYVY8_1X16:
410 case MEDIA_BUS_FMT_UYVY8_2X8:
411 case MEDIA_BUS_FMT_UYVY10_2X10:
412 case MEDIA_BUS_FMT_RGB888_1X24:
413 vin->mbus_code = code.code;
414 vin_dbg(vin, "Found media bus format for %s: %d\n",
415 subdev->name, vin->mbus_code);
416 break;
417 default:
418 break;
419 }
420 }
421
422 if (!vin->mbus_code) {
423 vin_err(vin, "Unsupported media bus format for %s\n",
424 subdev->name);
425 return -EINVAL;
426 }
427
428
429 ret = v4l2_subdev_call(subdev, video, g_tvnorms, &vin->vdev.tvnorms);
430 if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
431 return ret;
432
433
434 vin->std = V4L2_STD_UNKNOWN;
435 ret = v4l2_subdev_call(subdev, video, g_std, &vin->std);
436 if (ret < 0 && ret != -ENOIOCTLCMD)
437 return ret;
438
439
440 ret = v4l2_ctrl_handler_init(&vin->ctrl_handler, 16);
441 if (ret < 0)
442 return ret;
443
444 ret = v4l2_ctrl_add_handler(&vin->ctrl_handler, subdev->ctrl_handler,
445 NULL);
446 if (ret < 0) {
447 v4l2_ctrl_handler_free(&vin->ctrl_handler);
448 return ret;
449 }
450
451 vin->vdev.ctrl_handler = &vin->ctrl_handler;
452
453 vin->digital->subdev = subdev;
454
455 return 0;
456}
457
458static void rvin_digital_subdevice_detach(struct rvin_dev *vin)
459{
460 rvin_v4l2_unregister(vin);
461 v4l2_ctrl_handler_free(&vin->ctrl_handler);
462
463 vin->vdev.ctrl_handler = NULL;
464 vin->digital->subdev = NULL;
465}
466
467static int rvin_digital_notify_complete(struct v4l2_async_notifier *notifier)
468{
469 struct rvin_dev *vin = notifier_to_vin(notifier);
470 int ret;
471
472 ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev);
473 if (ret < 0) {
474 vin_err(vin, "Failed to register subdev nodes\n");
475 return ret;
476 }
477
478 return rvin_v4l2_register(vin);
479}
480
481static void rvin_digital_notify_unbind(struct v4l2_async_notifier *notifier,
482 struct v4l2_subdev *subdev,
483 struct v4l2_async_subdev *asd)
484{
485 struct rvin_dev *vin = notifier_to_vin(notifier);
486
487 vin_dbg(vin, "unbind digital subdev %s\n", subdev->name);
488
489 mutex_lock(&vin->lock);
490 rvin_digital_subdevice_detach(vin);
491 mutex_unlock(&vin->lock);
492}
493
494static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier,
495 struct v4l2_subdev *subdev,
496 struct v4l2_async_subdev *asd)
497{
498 struct rvin_dev *vin = notifier_to_vin(notifier);
499 int ret;
500
501 mutex_lock(&vin->lock);
502 ret = rvin_digital_subdevice_attach(vin, subdev);
503 mutex_unlock(&vin->lock);
504 if (ret)
505 return ret;
506
507 v4l2_set_subdev_hostdata(subdev, vin);
508
509 vin_dbg(vin, "bound subdev %s source pad: %u sink pad: %u\n",
510 subdev->name, vin->digital->source_pad,
511 vin->digital->sink_pad);
512
513 return 0;
514}
515
516static const struct v4l2_async_notifier_operations rvin_digital_notify_ops = {
517 .bound = rvin_digital_notify_bound,
518 .unbind = rvin_digital_notify_unbind,
519 .complete = rvin_digital_notify_complete,
520};
521
522static int rvin_digital_parse_v4l2(struct device *dev,
523 struct v4l2_fwnode_endpoint *vep,
524 struct v4l2_async_subdev *asd)
525{
526 struct rvin_dev *vin = dev_get_drvdata(dev);
527 struct rvin_graph_entity *rvge =
528 container_of(asd, struct rvin_graph_entity, asd);
529
530 if (vep->base.port || vep->base.id)
531 return -ENOTCONN;
532
533 vin->mbus_cfg.type = vep->bus_type;
534
535 switch (vin->mbus_cfg.type) {
536 case V4L2_MBUS_PARALLEL:
537 vin_dbg(vin, "Found PARALLEL media bus\n");
538 vin->mbus_cfg.flags = vep->bus.parallel.flags;
539 break;
540 case V4L2_MBUS_BT656:
541 vin_dbg(vin, "Found BT656 media bus\n");
542 vin->mbus_cfg.flags = 0;
543 break;
544 default:
545 vin_err(vin, "Unknown media bus type\n");
546 return -EINVAL;
547 }
548
549 vin->digital = rvge;
550
551 return 0;
552}
553
554static int rvin_digital_graph_init(struct rvin_dev *vin)
555{
556 int ret;
557
558 ret = v4l2_async_notifier_parse_fwnode_endpoints(
559 vin->dev, &vin->notifier,
560 sizeof(struct rvin_graph_entity), rvin_digital_parse_v4l2);
561 if (ret)
562 return ret;
563
564 if (!vin->digital)
565 return -ENODEV;
566
567 vin_dbg(vin, "Found digital subdevice %pOF\n",
568 to_of_node(vin->digital->asd.match.fwnode));
569
570 vin->notifier.ops = &rvin_digital_notify_ops;
571 ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier);
572 if (ret < 0) {
573 vin_err(vin, "Notifier registration failed\n");
574 return ret;
575 }
576
577 return 0;
578}
579
580
581
582
583
584static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier)
585{
586 struct rvin_dev *vin = notifier_to_vin(notifier);
587 const struct rvin_group_route *route;
588 unsigned int i;
589 int ret;
590
591 ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev);
592 if (ret) {
593 vin_err(vin, "Failed to register subdev nodes\n");
594 return ret;
595 }
596
597
598 for (i = 0; i < RCAR_VIN_NUM; i++) {
599 if (vin->group->vin[i]) {
600 ret = rvin_v4l2_register(vin->group->vin[i]);
601 if (ret)
602 return ret;
603 }
604 }
605
606
607 mutex_lock(&vin->group->lock);
608 for (route = vin->info->routes; route->mask; route++) {
609 struct media_pad *source_pad, *sink_pad;
610 struct media_entity *source, *sink;
611 unsigned int source_idx;
612
613
614 if (!vin->group->vin[route->vin])
615 continue;
616
617
618 if (!vin->group->vin[rvin_group_id_to_master(route->vin)])
619 continue;
620
621
622 if (!vin->group->csi[route->csi].subdev)
623 continue;
624
625 source = &vin->group->csi[route->csi].subdev->entity;
626 source_idx = rvin_group_csi_channel_to_pad(route->channel);
627 source_pad = &source->pads[source_idx];
628
629 sink = &vin->group->vin[route->vin]->vdev.entity;
630 sink_pad = &sink->pads[0];
631
632
633 if (media_entity_find_link(source_pad, sink_pad))
634 continue;
635
636 ret = media_create_pad_link(source, source_idx, sink, 0, 0);
637 if (ret) {
638 vin_err(vin, "Error adding link from %s to %s\n",
639 source->name, sink->name);
640 break;
641 }
642 }
643 mutex_unlock(&vin->group->lock);
644
645 return ret;
646}
647
648static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier,
649 struct v4l2_subdev *subdev,
650 struct v4l2_async_subdev *asd)
651{
652 struct rvin_dev *vin = notifier_to_vin(notifier);
653 unsigned int i;
654
655 for (i = 0; i < RCAR_VIN_NUM; i++)
656 if (vin->group->vin[i])
657 rvin_v4l2_unregister(vin->group->vin[i]);
658
659 mutex_lock(&vin->group->lock);
660
661 for (i = 0; i < RVIN_CSI_MAX; i++) {
662 if (vin->group->csi[i].fwnode != asd->match.fwnode)
663 continue;
664 vin->group->csi[i].subdev = NULL;
665 vin_dbg(vin, "Unbind CSI-2 %s from slot %u\n", subdev->name, i);
666 break;
667 }
668
669 mutex_unlock(&vin->group->lock);
670}
671
672static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier,
673 struct v4l2_subdev *subdev,
674 struct v4l2_async_subdev *asd)
675{
676 struct rvin_dev *vin = notifier_to_vin(notifier);
677 unsigned int i;
678
679 mutex_lock(&vin->group->lock);
680
681 for (i = 0; i < RVIN_CSI_MAX; i++) {
682 if (vin->group->csi[i].fwnode != asd->match.fwnode)
683 continue;
684 vin->group->csi[i].subdev = subdev;
685 vin_dbg(vin, "Bound CSI-2 %s to slot %u\n", subdev->name, i);
686 break;
687 }
688
689 mutex_unlock(&vin->group->lock);
690
691 return 0;
692}
693
694static const struct v4l2_async_notifier_operations rvin_group_notify_ops = {
695 .bound = rvin_group_notify_bound,
696 .unbind = rvin_group_notify_unbind,
697 .complete = rvin_group_notify_complete,
698};
699
700static int rvin_mc_parse_of_endpoint(struct device *dev,
701 struct v4l2_fwnode_endpoint *vep,
702 struct v4l2_async_subdev *asd)
703{
704 struct rvin_dev *vin = dev_get_drvdata(dev);
705
706 if (vep->base.port != 1 || vep->base.id >= RVIN_CSI_MAX)
707 return -EINVAL;
708
709 if (!of_device_is_available(to_of_node(asd->match.fwnode))) {
710
711 vin_dbg(vin, "OF device %pOF disabled, ignoring\n",
712 to_of_node(asd->match.fwnode));
713 return -ENOTCONN;
714
715 }
716
717 if (vin->group->csi[vep->base.id].fwnode) {
718 vin_dbg(vin, "OF device %pOF already handled\n",
719 to_of_node(asd->match.fwnode));
720 return -ENOTCONN;
721 }
722
723 vin->group->csi[vep->base.id].fwnode = asd->match.fwnode;
724
725 vin_dbg(vin, "Add group OF device %pOF to slot %u\n",
726 to_of_node(asd->match.fwnode), vep->base.id);
727
728 return 0;
729}
730
731static int rvin_mc_parse_of_graph(struct rvin_dev *vin)
732{
733 unsigned int count = 0;
734 unsigned int i;
735 int ret;
736
737 mutex_lock(&vin->group->lock);
738
739
740 if (WARN_ON(vin->group->notifier)) {
741 mutex_unlock(&vin->group->lock);
742 return -EINVAL;
743 }
744
745
746 for (i = 0; i < RCAR_VIN_NUM; i++)
747 if (vin->group->vin[i])
748 count++;
749
750 if (vin->group->count != count) {
751 mutex_unlock(&vin->group->lock);
752 return 0;
753 }
754
755
756
757
758
759
760
761 vin->group->notifier = &vin->notifier;
762
763 for (i = 0; i < RCAR_VIN_NUM; i++) {
764 if (!vin->group->vin[i])
765 continue;
766
767 ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
768 vin->group->vin[i]->dev, vin->group->notifier,
769 sizeof(struct v4l2_async_subdev), 1,
770 rvin_mc_parse_of_endpoint);
771 if (ret) {
772 mutex_unlock(&vin->group->lock);
773 return ret;
774 }
775 }
776
777 mutex_unlock(&vin->group->lock);
778
779 vin->group->notifier->ops = &rvin_group_notify_ops;
780
781 ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier);
782 if (ret < 0) {
783 vin_err(vin, "Notifier registration failed\n");
784 return ret;
785 }
786
787 return 0;
788}
789
790static int rvin_mc_init(struct rvin_dev *vin)
791{
792 int ret;
793
794
795 vin->mbus_cfg.type = V4L2_MBUS_CSI2;
796 vin->mbus_cfg.flags = 0;
797
798 vin->pad.flags = MEDIA_PAD_FL_SINK;
799 ret = media_entity_pads_init(&vin->vdev.entity, 1, &vin->pad);
800 if (ret)
801 return ret;
802
803 ret = rvin_group_get(vin);
804 if (ret)
805 return ret;
806
807 ret = rvin_mc_parse_of_graph(vin);
808 if (ret)
809 rvin_group_put(vin);
810
811 return ret;
812}
813
814
815
816
817
818static const struct rvin_info rcar_info_h1 = {
819 .model = RCAR_H1,
820 .use_mc = false,
821 .max_width = 2048,
822 .max_height = 2048,
823};
824
825static const struct rvin_info rcar_info_m1 = {
826 .model = RCAR_M1,
827 .use_mc = false,
828 .max_width = 2048,
829 .max_height = 2048,
830};
831
832static const struct rvin_info rcar_info_gen2 = {
833 .model = RCAR_GEN2,
834 .use_mc = false,
835 .max_width = 2048,
836 .max_height = 2048,
837};
838
839static const struct rvin_group_route rcar_info_r8a7795_routes[] = {
840 { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) },
841 { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) },
842 { .csi = RVIN_CSI40, .channel = 1, .vin = 0, .mask = BIT(2) },
843 { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) },
844 { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(1) | BIT(3) },
845 { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) },
846 { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) },
847 { .csi = RVIN_CSI20, .channel = 1, .vin = 2, .mask = BIT(0) },
848 { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) },
849 { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) },
850 { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) },
851 { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) },
852 { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) },
853 { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) | BIT(2) },
854 { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) },
855 { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) },
856 { .csi = RVIN_CSI41, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) },
857 { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) },
858 { .csi = RVIN_CSI41, .channel = 1, .vin = 4, .mask = BIT(2) },
859 { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) },
860 { .csi = RVIN_CSI41, .channel = 1, .vin = 5, .mask = BIT(1) | BIT(3) },
861 { .csi = RVIN_CSI41, .channel = 0, .vin = 5, .mask = BIT(2) },
862 { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) },
863 { .csi = RVIN_CSI20, .channel = 1, .vin = 6, .mask = BIT(0) },
864 { .csi = RVIN_CSI41, .channel = 0, .vin = 6, .mask = BIT(1) },
865 { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) },
866 { .csi = RVIN_CSI41, .channel = 2, .vin = 6, .mask = BIT(3) },
867 { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) },
868 { .csi = RVIN_CSI41, .channel = 1, .vin = 7, .mask = BIT(0) },
869 { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) | BIT(2) },
870 { .csi = RVIN_CSI41, .channel = 3, .vin = 7, .mask = BIT(3) },
871 { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) },
872 { }
873};
874
875static const struct rvin_info rcar_info_r8a7795 = {
876 .model = RCAR_GEN3,
877 .use_mc = true,
878 .max_width = 4096,
879 .max_height = 4096,
880 .routes = rcar_info_r8a7795_routes,
881};
882
883static const struct rvin_group_route rcar_info_r8a7795es1_routes[] = {
884 { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) },
885 { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) },
886 { .csi = RVIN_CSI21, .channel = 0, .vin = 0, .mask = BIT(2) | BIT(5) },
887 { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) },
888 { .csi = RVIN_CSI21, .channel = 0, .vin = 1, .mask = BIT(1) },
889 { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) },
890 { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) },
891 { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) },
892 { .csi = RVIN_CSI21, .channel = 1, .vin = 1, .mask = BIT(5) },
893 { .csi = RVIN_CSI21, .channel = 0, .vin = 2, .mask = BIT(0) },
894 { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) },
895 { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) },
896 { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) },
897 { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) },
898 { .csi = RVIN_CSI21, .channel = 2, .vin = 2, .mask = BIT(5) },
899 { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) },
900 { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) },
901 { .csi = RVIN_CSI21, .channel = 1, .vin = 3, .mask = BIT(2) },
902 { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) },
903 { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) },
904 { .csi = RVIN_CSI21, .channel = 3, .vin = 3, .mask = BIT(5) },
905 { .csi = RVIN_CSI41, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) },
906 { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) },
907 { .csi = RVIN_CSI21, .channel = 0, .vin = 4, .mask = BIT(2) | BIT(5) },
908 { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) },
909 { .csi = RVIN_CSI21, .channel = 0, .vin = 5, .mask = BIT(1) },
910 { .csi = RVIN_CSI41, .channel = 0, .vin = 5, .mask = BIT(2) },
911 { .csi = RVIN_CSI41, .channel = 1, .vin = 5, .mask = BIT(3) },
912 { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) },
913 { .csi = RVIN_CSI21, .channel = 1, .vin = 5, .mask = BIT(5) },
914 { .csi = RVIN_CSI21, .channel = 0, .vin = 6, .mask = BIT(0) },
915 { .csi = RVIN_CSI41, .channel = 0, .vin = 6, .mask = BIT(1) },
916 { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) },
917 { .csi = RVIN_CSI41, .channel = 2, .vin = 6, .mask = BIT(3) },
918 { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) },
919 { .csi = RVIN_CSI21, .channel = 2, .vin = 6, .mask = BIT(5) },
920 { .csi = RVIN_CSI41, .channel = 1, .vin = 7, .mask = BIT(0) },
921 { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) },
922 { .csi = RVIN_CSI21, .channel = 1, .vin = 7, .mask = BIT(2) },
923 { .csi = RVIN_CSI41, .channel = 3, .vin = 7, .mask = BIT(3) },
924 { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) },
925 { .csi = RVIN_CSI21, .channel = 3, .vin = 7, .mask = BIT(5) },
926 { }
927};
928
929static const struct rvin_info rcar_info_r8a7795es1 = {
930 .model = RCAR_GEN3,
931 .use_mc = true,
932 .max_width = 4096,
933 .max_height = 4096,
934 .routes = rcar_info_r8a7795es1_routes,
935};
936
937static const struct rvin_group_route rcar_info_r8a7796_routes[] = {
938 { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) },
939 { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) },
940 { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) },
941 { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) },
942 { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) },
943 { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) },
944 { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) },
945 { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) },
946 { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) },
947 { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) },
948 { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) },
949 { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) },
950 { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) },
951 { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) },
952 { .csi = RVIN_CSI40, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) },
953 { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) },
954 { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) },
955 { .csi = RVIN_CSI40, .channel = 0, .vin = 5, .mask = BIT(2) },
956 { .csi = RVIN_CSI40, .channel = 1, .vin = 5, .mask = BIT(3) },
957 { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) },
958 { .csi = RVIN_CSI40, .channel = 0, .vin = 6, .mask = BIT(1) },
959 { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) },
960 { .csi = RVIN_CSI40, .channel = 2, .vin = 6, .mask = BIT(3) },
961 { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) },
962 { .csi = RVIN_CSI40, .channel = 1, .vin = 7, .mask = BIT(0) },
963 { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) },
964 { .csi = RVIN_CSI40, .channel = 3, .vin = 7, .mask = BIT(3) },
965 { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) },
966 { }
967};
968
969static const struct rvin_info rcar_info_r8a7796 = {
970 .model = RCAR_GEN3,
971 .use_mc = true,
972 .max_width = 4096,
973 .max_height = 4096,
974 .routes = rcar_info_r8a7796_routes,
975};
976
977static const struct rvin_group_route _rcar_info_r8a77970_routes[] = {
978 { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) },
979 { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) },
980 { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) },
981 { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) },
982 { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) },
983 { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) },
984 { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) },
985 { }
986};
987
988static const struct rvin_info rcar_info_r8a77970 = {
989 .model = RCAR_GEN3,
990 .use_mc = true,
991 .max_width = 4096,
992 .max_height = 4096,
993 .routes = _rcar_info_r8a77970_routes,
994};
995
996static const struct of_device_id rvin_of_id_table[] = {
997 {
998 .compatible = "renesas,vin-r8a7778",
999 .data = &rcar_info_m1,
1000 },
1001 {
1002 .compatible = "renesas,vin-r8a7779",
1003 .data = &rcar_info_h1,
1004 },
1005 {
1006 .compatible = "renesas,vin-r8a7790",
1007 .data = &rcar_info_gen2,
1008 },
1009 {
1010 .compatible = "renesas,vin-r8a7791",
1011 .data = &rcar_info_gen2,
1012 },
1013 {
1014 .compatible = "renesas,vin-r8a7793",
1015 .data = &rcar_info_gen2,
1016 },
1017 {
1018 .compatible = "renesas,vin-r8a7794",
1019 .data = &rcar_info_gen2,
1020 },
1021 {
1022 .compatible = "renesas,rcar-gen2-vin",
1023 .data = &rcar_info_gen2,
1024 },
1025 {
1026 .compatible = "renesas,vin-r8a7795",
1027 .data = &rcar_info_r8a7795,
1028 },
1029 {
1030 .compatible = "renesas,vin-r8a7796",
1031 .data = &rcar_info_r8a7796,
1032 },
1033 {
1034 .compatible = "renesas,vin-r8a77970",
1035 .data = &rcar_info_r8a77970,
1036 },
1037 { },
1038};
1039MODULE_DEVICE_TABLE(of, rvin_of_id_table);
1040
1041static const struct soc_device_attribute r8a7795es1[] = {
1042 {
1043 .soc_id = "r8a7795", .revision = "ES1.*",
1044 .data = &rcar_info_r8a7795es1,
1045 },
1046 { }
1047};
1048
1049static int rcar_vin_probe(struct platform_device *pdev)
1050{
1051 const struct soc_device_attribute *attr;
1052 struct rvin_dev *vin;
1053 struct resource *mem;
1054 int irq, ret;
1055
1056 vin = devm_kzalloc(&pdev->dev, sizeof(*vin), GFP_KERNEL);
1057 if (!vin)
1058 return -ENOMEM;
1059
1060 vin->dev = &pdev->dev;
1061 vin->info = of_device_get_match_data(&pdev->dev);
1062
1063
1064
1065
1066
1067 attr = soc_device_match(r8a7795es1);
1068 if (attr)
1069 vin->info = attr->data;
1070
1071 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1072 if (mem == NULL)
1073 return -EINVAL;
1074
1075 vin->base = devm_ioremap_resource(vin->dev, mem);
1076 if (IS_ERR(vin->base))
1077 return PTR_ERR(vin->base);
1078
1079 irq = platform_get_irq(pdev, 0);
1080 if (irq < 0)
1081 return irq;
1082
1083 ret = rvin_dma_register(vin, irq);
1084 if (ret)
1085 return ret;
1086
1087 platform_set_drvdata(pdev, vin);
1088 if (vin->info->use_mc)
1089 ret = rvin_mc_init(vin);
1090 else
1091 ret = rvin_digital_graph_init(vin);
1092 if (ret < 0)
1093 goto error;
1094
1095 pm_suspend_ignore_children(&pdev->dev, true);
1096 pm_runtime_enable(&pdev->dev);
1097
1098 return 0;
1099error:
1100 rvin_dma_unregister(vin);
1101 v4l2_async_notifier_cleanup(&vin->notifier);
1102
1103 return ret;
1104}
1105
1106static int rcar_vin_remove(struct platform_device *pdev)
1107{
1108 struct rvin_dev *vin = platform_get_drvdata(pdev);
1109
1110 pm_runtime_disable(&pdev->dev);
1111
1112 rvin_v4l2_unregister(vin);
1113
1114 v4l2_async_notifier_unregister(&vin->notifier);
1115 v4l2_async_notifier_cleanup(&vin->notifier);
1116
1117 if (vin->info->use_mc) {
1118 mutex_lock(&vin->group->lock);
1119 if (vin->group->notifier == &vin->notifier)
1120 vin->group->notifier = NULL;
1121 mutex_unlock(&vin->group->lock);
1122 rvin_group_put(vin);
1123 } else {
1124 v4l2_ctrl_handler_free(&vin->ctrl_handler);
1125 }
1126
1127 rvin_dma_unregister(vin);
1128
1129 return 0;
1130}
1131
1132static struct platform_driver rcar_vin_driver = {
1133 .driver = {
1134 .name = "rcar-vin",
1135 .of_match_table = rvin_of_id_table,
1136 },
1137 .probe = rcar_vin_probe,
1138 .remove = rcar_vin_remove,
1139};
1140
1141module_platform_driver(rcar_vin_driver);
1142
1143MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>");
1144MODULE_DESCRIPTION("Renesas R-Car VIN camera host driver");
1145MODULE_LICENSE("GPL v2");
1146