f81232: switch to ->get_serial()
[linux/fpc-iii.git] / drivers / media / platform / rcar-vin / rcar-core.c
blobce09799976efe1b506598123f9cf82997fb041ca
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Driver for Renesas R-Car VIN
5 * Copyright (C) 2016 Renesas Electronics Corp.
6 * Copyright (C) 2011-2013 Renesas Solutions Corp.
7 * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com>
8 * Copyright (C) 2008 Magnus Damm
10 * Based on the soc-camera rcar_vin driver
13 #include <linux/module.h>
14 #include <linux/of.h>
15 #include <linux/of_device.h>
16 #include <linux/of_graph.h>
17 #include <linux/platform_device.h>
18 #include <linux/pm_runtime.h>
19 #include <linux/slab.h>
20 #include <linux/sys_soc.h>
22 #include <media/v4l2-async.h>
23 #include <media/v4l2-fwnode.h>
24 #include <media/v4l2-mc.h>
26 #include "rcar-vin.h"
29 * The companion CSI-2 receiver driver (rcar-csi2) is known
30 * and we know it has one source pad (pad 0) and four sink
31 * pads (pad 1-4). So to translate a pad on the remote
32 * CSI-2 receiver to/from the VIN internal channel number simply
33 * subtract/add one from the pad/channel number.
35 #define rvin_group_csi_pad_to_channel(pad) ((pad) - 1)
36 #define rvin_group_csi_channel_to_pad(channel) ((channel) + 1)
39 * Not all VINs are created equal, master VINs control the
40 * routing for other VIN's. We can figure out which VIN is
41 * master by looking at a VINs id.
43 #define rvin_group_id_to_master(vin) ((vin) < 4 ? 0 : 4)
45 #define v4l2_dev_to_vin(d) container_of(d, struct rvin_dev, v4l2_dev)
47 /* -----------------------------------------------------------------------------
48 * Media Controller link notification
51 /* group lock should be held when calling this function. */
52 static int rvin_group_entity_to_csi_id(struct rvin_group *group,
53 struct media_entity *entity)
55 struct v4l2_subdev *sd;
56 unsigned int i;
58 sd = media_entity_to_v4l2_subdev(entity);
60 for (i = 0; i < RVIN_CSI_MAX; i++)
61 if (group->csi[i].subdev == sd)
62 return i;
64 return -ENODEV;
67 static unsigned int rvin_group_get_mask(struct rvin_dev *vin,
68 enum rvin_csi_id csi_id,
69 unsigned char channel)
71 const struct rvin_group_route *route;
72 unsigned int mask = 0;
74 for (route = vin->info->routes; route->mask; route++) {
75 if (route->vin == vin->id &&
76 route->csi == csi_id &&
77 route->channel == channel) {
78 vin_dbg(vin,
79 "Adding route: vin: %d csi: %d channel: %d\n",
80 route->vin, route->csi, route->channel);
81 mask |= route->mask;
85 return mask;
89 * Link setup for the links between a VIN and a CSI-2 receiver is a bit
90 * complex. The reason for this is that the register controlling routing
91 * is not present in each VIN instance. There are special VINs which
92 * control routing for themselves and other VINs. There are not many
93 * different possible links combinations that can be enabled at the same
94 * time, therefor all already enabled links which are controlled by a
95 * master VIN need to be taken into account when making the decision
96 * if a new link can be enabled or not.
98 * 1. Find out which VIN the link the user tries to enable is connected to.
99 * 2. Lookup which master VIN controls the links for this VIN.
100 * 3. Start with a bitmask with all bits set.
101 * 4. For each previously enabled link from the master VIN bitwise AND its
102 * route mask (see documentation for mask in struct rvin_group_route)
103 * with the bitmask.
104 * 5. Bitwise AND the mask for the link the user tries to enable to the bitmask.
105 * 6. If the bitmask is not empty at this point the new link can be enabled
106 * while keeping all previous links enabled. Update the CHSEL value of the
107 * master VIN and inform the user that the link could be enabled.
109 * Please note that no link can be enabled if any VIN in the group is
110 * currently open.
112 static int rvin_group_link_notify(struct media_link *link, u32 flags,
113 unsigned int notification)
115 struct rvin_group *group = container_of(link->graph_obj.mdev,
116 struct rvin_group, mdev);
117 unsigned int master_id, channel, mask_new, i;
118 unsigned int mask = ~0;
119 struct media_entity *entity;
120 struct video_device *vdev;
121 struct media_pad *csi_pad;
122 struct rvin_dev *vin = NULL;
123 int csi_id, ret;
125 ret = v4l2_pipeline_link_notify(link, flags, notification);
126 if (ret)
127 return ret;
129 /* Only care about link enablement for VIN nodes. */
130 if (!(flags & MEDIA_LNK_FL_ENABLED) ||
131 !is_media_entity_v4l2_video_device(link->sink->entity))
132 return 0;
134 /* If any entity is in use don't allow link changes. */
135 media_device_for_each_entity(entity, &group->mdev)
136 if (entity->use_count)
137 return -EBUSY;
139 mutex_lock(&group->lock);
141 /* Find the master VIN that controls the routes. */
142 vdev = media_entity_to_video_device(link->sink->entity);
143 vin = container_of(vdev, struct rvin_dev, vdev);
144 master_id = rvin_group_id_to_master(vin->id);
146 if (WARN_ON(!group->vin[master_id])) {
147 ret = -ENODEV;
148 goto out;
151 /* Build a mask for already enabled links. */
152 for (i = master_id; i < master_id + 4; i++) {
153 if (!group->vin[i])
154 continue;
156 /* Get remote CSI-2, if any. */
157 csi_pad = media_entity_remote_pad(
158 &group->vin[i]->vdev.entity.pads[0]);
159 if (!csi_pad)
160 continue;
162 csi_id = rvin_group_entity_to_csi_id(group, csi_pad->entity);
163 channel = rvin_group_csi_pad_to_channel(csi_pad->index);
165 mask &= rvin_group_get_mask(group->vin[i], csi_id, channel);
168 /* Add the new link to the existing mask and check if it works. */
169 csi_id = rvin_group_entity_to_csi_id(group, link->source->entity);
171 if (csi_id == -ENODEV) {
172 struct v4l2_subdev *sd;
173 unsigned int i;
176 * Make sure the source entity subdevice is registered as
177 * a parallel input of one of the enabled VINs if it is not
178 * one of the CSI-2 subdevices.
180 * No hardware configuration required for parallel inputs,
181 * we can return here.
183 sd = media_entity_to_v4l2_subdev(link->source->entity);
184 for (i = 0; i < RCAR_VIN_NUM; i++) {
185 if (group->vin[i] && group->vin[i]->parallel &&
186 group->vin[i]->parallel->subdev == sd) {
187 group->vin[i]->is_csi = false;
188 ret = 0;
189 goto out;
193 vin_err(vin, "Subdevice %s not registered to any VIN\n",
194 link->source->entity->name);
195 ret = -ENODEV;
196 goto out;
199 channel = rvin_group_csi_pad_to_channel(link->source->index);
200 mask_new = mask & rvin_group_get_mask(vin, csi_id, channel);
201 vin_dbg(vin, "Try link change mask: 0x%x new: 0x%x\n", mask, mask_new);
203 if (!mask_new) {
204 ret = -EMLINK;
205 goto out;
208 /* New valid CHSEL found, set the new value. */
209 ret = rvin_set_channel_routing(group->vin[master_id], __ffs(mask_new));
210 if (ret)
211 goto out;
213 vin->is_csi = true;
215 out:
216 mutex_unlock(&group->lock);
218 return ret;
221 static const struct media_device_ops rvin_media_ops = {
222 .link_notify = rvin_group_link_notify,
225 /* -----------------------------------------------------------------------------
226 * Gen3 CSI2 Group Allocator
229 /* FIXME: This should if we find a system that supports more
230 * than one group for the whole system be replaced with a linked
231 * list of groups. And eventually all of this should be replaced
232 * with a global device allocator API.
234 * But for now this works as on all supported systems there will
235 * be only one group for all instances.
238 static DEFINE_MUTEX(rvin_group_lock);
239 static struct rvin_group *rvin_group_data;
241 static void rvin_group_cleanup(struct rvin_group *group)
243 media_device_unregister(&group->mdev);
244 media_device_cleanup(&group->mdev);
245 mutex_destroy(&group->lock);
248 static int rvin_group_init(struct rvin_group *group, struct rvin_dev *vin)
250 struct media_device *mdev = &group->mdev;
251 const struct of_device_id *match;
252 struct device_node *np;
253 int ret;
255 mutex_init(&group->lock);
257 /* Count number of VINs in the system */
258 group->count = 0;
259 for_each_matching_node(np, vin->dev->driver->of_match_table)
260 if (of_device_is_available(np))
261 group->count++;
263 vin_dbg(vin, "found %u enabled VIN's in DT", group->count);
265 mdev->dev = vin->dev;
266 mdev->ops = &rvin_media_ops;
268 match = of_match_node(vin->dev->driver->of_match_table,
269 vin->dev->of_node);
271 strlcpy(mdev->driver_name, KBUILD_MODNAME, sizeof(mdev->driver_name));
272 strlcpy(mdev->model, match->compatible, sizeof(mdev->model));
273 snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s",
274 dev_name(mdev->dev));
276 media_device_init(mdev);
278 ret = media_device_register(&group->mdev);
279 if (ret)
280 rvin_group_cleanup(group);
282 return ret;
285 static void rvin_group_release(struct kref *kref)
287 struct rvin_group *group =
288 container_of(kref, struct rvin_group, refcount);
290 mutex_lock(&rvin_group_lock);
292 rvin_group_data = NULL;
294 rvin_group_cleanup(group);
296 kfree(group);
298 mutex_unlock(&rvin_group_lock);
301 static int rvin_group_get(struct rvin_dev *vin)
303 struct rvin_group *group;
304 u32 id;
305 int ret;
307 /* Make sure VIN id is present and sane */
308 ret = of_property_read_u32(vin->dev->of_node, "renesas,id", &id);
309 if (ret) {
310 vin_err(vin, "%pOF: No renesas,id property found\n",
311 vin->dev->of_node);
312 return -EINVAL;
315 if (id >= RCAR_VIN_NUM) {
316 vin_err(vin, "%pOF: Invalid renesas,id '%u'\n",
317 vin->dev->of_node, id);
318 return -EINVAL;
321 /* Join or create a VIN group */
322 mutex_lock(&rvin_group_lock);
323 if (rvin_group_data) {
324 group = rvin_group_data;
325 kref_get(&group->refcount);
326 } else {
327 group = kzalloc(sizeof(*group), GFP_KERNEL);
328 if (!group) {
329 ret = -ENOMEM;
330 goto err_group;
333 ret = rvin_group_init(group, vin);
334 if (ret) {
335 kfree(group);
336 vin_err(vin, "Failed to initialize group\n");
337 goto err_group;
340 kref_init(&group->refcount);
342 rvin_group_data = group;
344 mutex_unlock(&rvin_group_lock);
346 /* Add VIN to group */
347 mutex_lock(&group->lock);
349 if (group->vin[id]) {
350 vin_err(vin, "Duplicate renesas,id property value %u\n", id);
351 mutex_unlock(&group->lock);
352 kref_put(&group->refcount, rvin_group_release);
353 return -EINVAL;
356 group->vin[id] = vin;
358 vin->id = id;
359 vin->group = group;
360 vin->v4l2_dev.mdev = &group->mdev;
362 mutex_unlock(&group->lock);
364 return 0;
365 err_group:
366 mutex_unlock(&rvin_group_lock);
367 return ret;
370 static void rvin_group_put(struct rvin_dev *vin)
372 struct rvin_group *group = vin->group;
374 mutex_lock(&group->lock);
376 vin->group = NULL;
377 vin->v4l2_dev.mdev = NULL;
379 if (WARN_ON(group->vin[vin->id] != vin))
380 goto out;
382 group->vin[vin->id] = NULL;
383 out:
384 mutex_unlock(&group->lock);
386 kref_put(&group->refcount, rvin_group_release);
389 /* -----------------------------------------------------------------------------
390 * Async notifier
393 static int rvin_find_pad(struct v4l2_subdev *sd, int direction)
395 unsigned int pad;
397 if (sd->entity.num_pads <= 1)
398 return 0;
400 for (pad = 0; pad < sd->entity.num_pads; pad++)
401 if (sd->entity.pads[pad].flags & direction)
402 return pad;
404 return -EINVAL;
407 /* -----------------------------------------------------------------------------
408 * Parallel async notifier
411 /* The vin lock should be held when calling the subdevice attach and detach */
412 static int rvin_parallel_subdevice_attach(struct rvin_dev *vin,
413 struct v4l2_subdev *subdev)
415 struct v4l2_subdev_mbus_code_enum code = {
416 .which = V4L2_SUBDEV_FORMAT_ACTIVE,
418 int ret;
420 /* Find source and sink pad of remote subdevice */
421 ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SOURCE);
422 if (ret < 0)
423 return ret;
424 vin->parallel->source_pad = ret;
426 ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SINK);
427 vin->parallel->sink_pad = ret < 0 ? 0 : ret;
429 if (vin->info->use_mc) {
430 vin->parallel->subdev = subdev;
431 return 0;
434 /* Find compatible subdevices mbus format */
435 vin->mbus_code = 0;
436 code.index = 0;
437 code.pad = vin->parallel->source_pad;
438 while (!vin->mbus_code &&
439 !v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &code)) {
440 code.index++;
441 switch (code.code) {
442 case MEDIA_BUS_FMT_YUYV8_1X16:
443 case MEDIA_BUS_FMT_UYVY8_1X16:
444 case MEDIA_BUS_FMT_UYVY8_2X8:
445 case MEDIA_BUS_FMT_UYVY10_2X10:
446 case MEDIA_BUS_FMT_RGB888_1X24:
447 vin->mbus_code = code.code;
448 vin_dbg(vin, "Found media bus format for %s: %d\n",
449 subdev->name, vin->mbus_code);
450 break;
451 default:
452 break;
456 if (!vin->mbus_code) {
457 vin_err(vin, "Unsupported media bus format for %s\n",
458 subdev->name);
459 return -EINVAL;
462 /* Read tvnorms */
463 ret = v4l2_subdev_call(subdev, video, g_tvnorms, &vin->vdev.tvnorms);
464 if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
465 return ret;
467 /* Read standard */
468 vin->std = V4L2_STD_UNKNOWN;
469 ret = v4l2_subdev_call(subdev, video, g_std, &vin->std);
470 if (ret < 0 && ret != -ENOIOCTLCMD)
471 return ret;
473 /* Add the controls */
474 ret = v4l2_ctrl_handler_init(&vin->ctrl_handler, 16);
475 if (ret < 0)
476 return ret;
478 ret = v4l2_ctrl_add_handler(&vin->ctrl_handler, subdev->ctrl_handler,
479 NULL);
480 if (ret < 0) {
481 v4l2_ctrl_handler_free(&vin->ctrl_handler);
482 return ret;
485 vin->vdev.ctrl_handler = &vin->ctrl_handler;
487 vin->parallel->subdev = subdev;
489 return 0;
492 static void rvin_parallel_subdevice_detach(struct rvin_dev *vin)
494 rvin_v4l2_unregister(vin);
495 vin->parallel->subdev = NULL;
497 if (!vin->info->use_mc) {
498 v4l2_ctrl_handler_free(&vin->ctrl_handler);
499 vin->vdev.ctrl_handler = NULL;
503 static int rvin_parallel_notify_complete(struct v4l2_async_notifier *notifier)
505 struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev);
506 struct media_entity *source;
507 struct media_entity *sink;
508 int ret;
510 ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev);
511 if (ret < 0) {
512 vin_err(vin, "Failed to register subdev nodes\n");
513 return ret;
516 if (!video_is_registered(&vin->vdev)) {
517 ret = rvin_v4l2_register(vin);
518 if (ret < 0)
519 return ret;
522 if (!vin->info->use_mc)
523 return 0;
525 /* If we're running with media-controller, link the subdevs. */
526 source = &vin->parallel->subdev->entity;
527 sink = &vin->vdev.entity;
529 ret = media_create_pad_link(source, vin->parallel->source_pad,
530 sink, vin->parallel->sink_pad, 0);
531 if (ret)
532 vin_err(vin, "Error adding link from %s to %s: %d\n",
533 source->name, sink->name, ret);
535 return ret;
538 static void rvin_parallel_notify_unbind(struct v4l2_async_notifier *notifier,
539 struct v4l2_subdev *subdev,
540 struct v4l2_async_subdev *asd)
542 struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev);
544 vin_dbg(vin, "unbind parallel subdev %s\n", subdev->name);
546 mutex_lock(&vin->lock);
547 rvin_parallel_subdevice_detach(vin);
548 mutex_unlock(&vin->lock);
551 static int rvin_parallel_notify_bound(struct v4l2_async_notifier *notifier,
552 struct v4l2_subdev *subdev,
553 struct v4l2_async_subdev *asd)
555 struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev);
556 int ret;
558 mutex_lock(&vin->lock);
559 ret = rvin_parallel_subdevice_attach(vin, subdev);
560 mutex_unlock(&vin->lock);
561 if (ret)
562 return ret;
564 v4l2_set_subdev_hostdata(subdev, vin);
566 vin_dbg(vin, "bound subdev %s source pad: %u sink pad: %u\n",
567 subdev->name, vin->parallel->source_pad,
568 vin->parallel->sink_pad);
570 return 0;
573 static const struct v4l2_async_notifier_operations rvin_parallel_notify_ops = {
574 .bound = rvin_parallel_notify_bound,
575 .unbind = rvin_parallel_notify_unbind,
576 .complete = rvin_parallel_notify_complete,
579 static int rvin_parallel_parse_v4l2(struct device *dev,
580 struct v4l2_fwnode_endpoint *vep,
581 struct v4l2_async_subdev *asd)
583 struct rvin_dev *vin = dev_get_drvdata(dev);
584 struct rvin_parallel_entity *rvpe =
585 container_of(asd, struct rvin_parallel_entity, asd);
587 if (vep->base.port || vep->base.id)
588 return -ENOTCONN;
590 vin->parallel = rvpe;
591 vin->parallel->mbus_type = vep->bus_type;
593 switch (vin->parallel->mbus_type) {
594 case V4L2_MBUS_PARALLEL:
595 vin_dbg(vin, "Found PARALLEL media bus\n");
596 vin->parallel->mbus_flags = vep->bus.parallel.flags;
597 break;
598 case V4L2_MBUS_BT656:
599 vin_dbg(vin, "Found BT656 media bus\n");
600 vin->parallel->mbus_flags = 0;
601 break;
602 default:
603 vin_err(vin, "Unknown media bus type\n");
604 return -EINVAL;
607 return 0;
610 static int rvin_parallel_init(struct rvin_dev *vin)
612 int ret;
614 ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
615 vin->dev, &vin->notifier, sizeof(struct rvin_parallel_entity),
616 0, rvin_parallel_parse_v4l2);
617 if (ret)
618 return ret;
620 /* If using mc, it's fine not to have any input registered. */
621 if (!vin->parallel)
622 return vin->info->use_mc ? 0 : -ENODEV;
624 vin_dbg(vin, "Found parallel subdevice %pOF\n",
625 to_of_node(vin->parallel->asd.match.fwnode));
627 vin->notifier.ops = &rvin_parallel_notify_ops;
628 ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier);
629 if (ret < 0) {
630 vin_err(vin, "Notifier registration failed\n");
631 v4l2_async_notifier_cleanup(&vin->group->notifier);
632 return ret;
635 return 0;
638 /* -----------------------------------------------------------------------------
639 * Group async notifier
642 static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier)
644 struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev);
645 const struct rvin_group_route *route;
646 unsigned int i;
647 int ret;
649 ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev);
650 if (ret) {
651 vin_err(vin, "Failed to register subdev nodes\n");
652 return ret;
655 /* Register all video nodes for the group. */
656 for (i = 0; i < RCAR_VIN_NUM; i++) {
657 if (vin->group->vin[i] &&
658 !video_is_registered(&vin->group->vin[i]->vdev)) {
659 ret = rvin_v4l2_register(vin->group->vin[i]);
660 if (ret)
661 return ret;
665 /* Create all media device links between VINs and CSI-2's. */
666 mutex_lock(&vin->group->lock);
667 for (route = vin->info->routes; route->mask; route++) {
668 struct media_pad *source_pad, *sink_pad;
669 struct media_entity *source, *sink;
670 unsigned int source_idx;
672 /* Check that VIN is part of the group. */
673 if (!vin->group->vin[route->vin])
674 continue;
676 /* Check that VIN' master is part of the group. */
677 if (!vin->group->vin[rvin_group_id_to_master(route->vin)])
678 continue;
680 /* Check that CSI-2 is part of the group. */
681 if (!vin->group->csi[route->csi].subdev)
682 continue;
684 source = &vin->group->csi[route->csi].subdev->entity;
685 source_idx = rvin_group_csi_channel_to_pad(route->channel);
686 source_pad = &source->pads[source_idx];
688 sink = &vin->group->vin[route->vin]->vdev.entity;
689 sink_pad = &sink->pads[0];
691 /* Skip if link already exists. */
692 if (media_entity_find_link(source_pad, sink_pad))
693 continue;
695 ret = media_create_pad_link(source, source_idx, sink, 0, 0);
696 if (ret) {
697 vin_err(vin, "Error adding link from %s to %s\n",
698 source->name, sink->name);
699 break;
702 mutex_unlock(&vin->group->lock);
704 return ret;
707 static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier,
708 struct v4l2_subdev *subdev,
709 struct v4l2_async_subdev *asd)
711 struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev);
712 unsigned int i;
714 for (i = 0; i < RCAR_VIN_NUM; i++)
715 if (vin->group->vin[i])
716 rvin_v4l2_unregister(vin->group->vin[i]);
718 mutex_lock(&vin->group->lock);
720 for (i = 0; i < RVIN_CSI_MAX; i++) {
721 if (vin->group->csi[i].fwnode != asd->match.fwnode)
722 continue;
723 vin->group->csi[i].subdev = NULL;
724 vin_dbg(vin, "Unbind CSI-2 %s from slot %u\n", subdev->name, i);
725 break;
728 mutex_unlock(&vin->group->lock);
731 static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier,
732 struct v4l2_subdev *subdev,
733 struct v4l2_async_subdev *asd)
735 struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev);
736 unsigned int i;
738 mutex_lock(&vin->group->lock);
740 for (i = 0; i < RVIN_CSI_MAX; i++) {
741 if (vin->group->csi[i].fwnode != asd->match.fwnode)
742 continue;
743 vin->group->csi[i].subdev = subdev;
744 vin_dbg(vin, "Bound CSI-2 %s to slot %u\n", subdev->name, i);
745 break;
748 mutex_unlock(&vin->group->lock);
750 return 0;
753 static const struct v4l2_async_notifier_operations rvin_group_notify_ops = {
754 .bound = rvin_group_notify_bound,
755 .unbind = rvin_group_notify_unbind,
756 .complete = rvin_group_notify_complete,
759 static int rvin_mc_parse_of_endpoint(struct device *dev,
760 struct v4l2_fwnode_endpoint *vep,
761 struct v4l2_async_subdev *asd)
763 struct rvin_dev *vin = dev_get_drvdata(dev);
765 if (vep->base.port != 1 || vep->base.id >= RVIN_CSI_MAX)
766 return -EINVAL;
768 if (!of_device_is_available(to_of_node(asd->match.fwnode))) {
769 vin_dbg(vin, "OF device %pOF disabled, ignoring\n",
770 to_of_node(asd->match.fwnode));
771 return -ENOTCONN;
774 if (vin->group->csi[vep->base.id].fwnode) {
775 vin_dbg(vin, "OF device %pOF already handled\n",
776 to_of_node(asd->match.fwnode));
777 return -ENOTCONN;
780 vin->group->csi[vep->base.id].fwnode = asd->match.fwnode;
782 vin_dbg(vin, "Add group OF device %pOF to slot %u\n",
783 to_of_node(asd->match.fwnode), vep->base.id);
785 return 0;
788 static int rvin_mc_parse_of_graph(struct rvin_dev *vin)
790 unsigned int count = 0;
791 unsigned int i;
792 int ret;
794 mutex_lock(&vin->group->lock);
796 /* If not all VIN's are registered don't register the notifier. */
797 for (i = 0; i < RCAR_VIN_NUM; i++)
798 if (vin->group->vin[i])
799 count++;
801 if (vin->group->count != count) {
802 mutex_unlock(&vin->group->lock);
803 return 0;
807 * Have all VIN's look for CSI-2 subdevices. Some subdevices will
808 * overlap but the parser function can handle it, so each subdevice
809 * will only be registered once with the group notifier.
811 for (i = 0; i < RCAR_VIN_NUM; i++) {
812 if (!vin->group->vin[i])
813 continue;
815 ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
816 vin->group->vin[i]->dev, &vin->group->notifier,
817 sizeof(struct v4l2_async_subdev), 1,
818 rvin_mc_parse_of_endpoint);
819 if (ret) {
820 mutex_unlock(&vin->group->lock);
821 return ret;
825 mutex_unlock(&vin->group->lock);
827 if (!vin->group->notifier.num_subdevs)
828 return 0;
830 vin->group->notifier.ops = &rvin_group_notify_ops;
831 ret = v4l2_async_notifier_register(&vin->v4l2_dev,
832 &vin->group->notifier);
833 if (ret < 0) {
834 vin_err(vin, "Notifier registration failed\n");
835 v4l2_async_notifier_cleanup(&vin->group->notifier);
836 return ret;
839 return 0;
842 static int rvin_mc_init(struct rvin_dev *vin)
844 int ret;
846 vin->pad.flags = MEDIA_PAD_FL_SINK;
847 ret = media_entity_pads_init(&vin->vdev.entity, 1, &vin->pad);
848 if (ret)
849 return ret;
851 ret = rvin_group_get(vin);
852 if (ret)
853 return ret;
855 ret = rvin_mc_parse_of_graph(vin);
856 if (ret)
857 rvin_group_put(vin);
859 return ret;
862 /* -----------------------------------------------------------------------------
863 * Platform Device Driver
866 static const struct rvin_info rcar_info_h1 = {
867 .model = RCAR_H1,
868 .use_mc = false,
869 .max_width = 2048,
870 .max_height = 2048,
873 static const struct rvin_info rcar_info_m1 = {
874 .model = RCAR_M1,
875 .use_mc = false,
876 .max_width = 2048,
877 .max_height = 2048,
880 static const struct rvin_info rcar_info_gen2 = {
881 .model = RCAR_GEN2,
882 .use_mc = false,
883 .max_width = 2048,
884 .max_height = 2048,
887 static const struct rvin_group_route rcar_info_r8a7795_routes[] = {
888 { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) },
889 { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) },
890 { .csi = RVIN_CSI40, .channel = 1, .vin = 0, .mask = BIT(2) },
891 { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) },
892 { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(1) | BIT(3) },
893 { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) },
894 { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) },
895 { .csi = RVIN_CSI20, .channel = 1, .vin = 2, .mask = BIT(0) },
896 { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) },
897 { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) },
898 { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) },
899 { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) },
900 { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) },
901 { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) | 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_CSI41, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) },
905 { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) },
906 { .csi = RVIN_CSI41, .channel = 1, .vin = 4, .mask = BIT(2) },
907 { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) },
908 { .csi = RVIN_CSI41, .channel = 1, .vin = 5, .mask = BIT(1) | BIT(3) },
909 { .csi = RVIN_CSI41, .channel = 0, .vin = 5, .mask = BIT(2) },
910 { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) },
911 { .csi = RVIN_CSI20, .channel = 1, .vin = 6, .mask = BIT(0) },
912 { .csi = RVIN_CSI41, .channel = 0, .vin = 6, .mask = BIT(1) },
913 { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) },
914 { .csi = RVIN_CSI41, .channel = 2, .vin = 6, .mask = BIT(3) },
915 { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) },
916 { .csi = RVIN_CSI41, .channel = 1, .vin = 7, .mask = BIT(0) },
917 { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) | BIT(2) },
918 { .csi = RVIN_CSI41, .channel = 3, .vin = 7, .mask = BIT(3) },
919 { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) },
920 { /* Sentinel */ }
923 static const struct rvin_info rcar_info_r8a7795 = {
924 .model = RCAR_GEN3,
925 .use_mc = true,
926 .max_width = 4096,
927 .max_height = 4096,
928 .routes = rcar_info_r8a7795_routes,
931 static const struct rvin_group_route rcar_info_r8a7795es1_routes[] = {
932 { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) },
933 { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) },
934 { .csi = RVIN_CSI21, .channel = 0, .vin = 0, .mask = BIT(2) | BIT(5) },
935 { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) },
936 { .csi = RVIN_CSI21, .channel = 0, .vin = 1, .mask = BIT(1) },
937 { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) },
938 { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) },
939 { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) },
940 { .csi = RVIN_CSI21, .channel = 1, .vin = 1, .mask = BIT(5) },
941 { .csi = RVIN_CSI21, .channel = 0, .vin = 2, .mask = BIT(0) },
942 { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) },
943 { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) },
944 { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) },
945 { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) },
946 { .csi = RVIN_CSI21, .channel = 2, .vin = 2, .mask = BIT(5) },
947 { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) },
948 { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) },
949 { .csi = RVIN_CSI21, .channel = 1, .vin = 3, .mask = BIT(2) },
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_CSI21, .channel = 3, .vin = 3, .mask = BIT(5) },
953 { .csi = RVIN_CSI41, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) },
954 { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) },
955 { .csi = RVIN_CSI21, .channel = 0, .vin = 4, .mask = BIT(2) | BIT(5) },
956 { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) },
957 { .csi = RVIN_CSI21, .channel = 0, .vin = 5, .mask = BIT(1) },
958 { .csi = RVIN_CSI41, .channel = 0, .vin = 5, .mask = BIT(2) },
959 { .csi = RVIN_CSI41, .channel = 1, .vin = 5, .mask = BIT(3) },
960 { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) },
961 { .csi = RVIN_CSI21, .channel = 1, .vin = 5, .mask = BIT(5) },
962 { .csi = RVIN_CSI21, .channel = 0, .vin = 6, .mask = BIT(0) },
963 { .csi = RVIN_CSI41, .channel = 0, .vin = 6, .mask = BIT(1) },
964 { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) },
965 { .csi = RVIN_CSI41, .channel = 2, .vin = 6, .mask = BIT(3) },
966 { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) },
967 { .csi = RVIN_CSI21, .channel = 2, .vin = 6, .mask = BIT(5) },
968 { .csi = RVIN_CSI41, .channel = 1, .vin = 7, .mask = BIT(0) },
969 { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) },
970 { .csi = RVIN_CSI21, .channel = 1, .vin = 7, .mask = BIT(2) },
971 { .csi = RVIN_CSI41, .channel = 3, .vin = 7, .mask = BIT(3) },
972 { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) },
973 { .csi = RVIN_CSI21, .channel = 3, .vin = 7, .mask = BIT(5) },
974 { /* Sentinel */ }
977 static const struct rvin_info rcar_info_r8a7795es1 = {
978 .model = RCAR_GEN3,
979 .use_mc = true,
980 .max_width = 4096,
981 .max_height = 4096,
982 .routes = rcar_info_r8a7795es1_routes,
985 static const struct rvin_group_route rcar_info_r8a7796_routes[] = {
986 { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) },
987 { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) },
988 { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) },
989 { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) },
990 { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) },
991 { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) },
992 { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) },
993 { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) },
994 { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) },
995 { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) },
996 { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) },
997 { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) },
998 { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) },
999 { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) },
1000 { .csi = RVIN_CSI40, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) },
1001 { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) },
1002 { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) },
1003 { .csi = RVIN_CSI40, .channel = 0, .vin = 5, .mask = BIT(2) },
1004 { .csi = RVIN_CSI40, .channel = 1, .vin = 5, .mask = BIT(3) },
1005 { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) },
1006 { .csi = RVIN_CSI40, .channel = 0, .vin = 6, .mask = BIT(1) },
1007 { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) },
1008 { .csi = RVIN_CSI40, .channel = 2, .vin = 6, .mask = BIT(3) },
1009 { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) },
1010 { .csi = RVIN_CSI40, .channel = 1, .vin = 7, .mask = BIT(0) },
1011 { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) },
1012 { .csi = RVIN_CSI40, .channel = 3, .vin = 7, .mask = BIT(3) },
1013 { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) },
1014 { /* Sentinel */ }
1017 static const struct rvin_info rcar_info_r8a7796 = {
1018 .model = RCAR_GEN3,
1019 .use_mc = true,
1020 .max_width = 4096,
1021 .max_height = 4096,
1022 .routes = rcar_info_r8a7796_routes,
1025 static const struct rvin_group_route rcar_info_r8a77965_routes[] = {
1026 { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) },
1027 { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) },
1028 { .csi = RVIN_CSI40, .channel = 1, .vin = 0, .mask = BIT(2) },
1029 { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) },
1030 { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(1) | BIT(3) },
1031 { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) },
1032 { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) },
1033 { .csi = RVIN_CSI20, .channel = 1, .vin = 2, .mask = BIT(0) },
1034 { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) },
1035 { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) },
1036 { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) },
1037 { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) },
1038 { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) },
1039 { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) | BIT(2) },
1040 { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) },
1041 { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) },
1042 { .csi = RVIN_CSI40, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) },
1043 { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) },
1044 { .csi = RVIN_CSI40, .channel = 1, .vin = 4, .mask = BIT(2) },
1045 { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) },
1046 { .csi = RVIN_CSI40, .channel = 1, .vin = 5, .mask = BIT(1) | BIT(3) },
1047 { .csi = RVIN_CSI40, .channel = 0, .vin = 5, .mask = BIT(2) },
1048 { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) },
1049 { .csi = RVIN_CSI20, .channel = 1, .vin = 6, .mask = BIT(0) },
1050 { .csi = RVIN_CSI40, .channel = 0, .vin = 6, .mask = BIT(1) },
1051 { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) },
1052 { .csi = RVIN_CSI40, .channel = 2, .vin = 6, .mask = BIT(3) },
1053 { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) },
1054 { .csi = RVIN_CSI40, .channel = 1, .vin = 7, .mask = BIT(0) },
1055 { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) | BIT(2) },
1056 { .csi = RVIN_CSI40, .channel = 3, .vin = 7, .mask = BIT(3) },
1057 { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) },
1058 { /* Sentinel */ }
1061 static const struct rvin_info rcar_info_r8a77965 = {
1062 .model = RCAR_GEN3,
1063 .use_mc = true,
1064 .max_width = 4096,
1065 .max_height = 4096,
1066 .routes = rcar_info_r8a77965_routes,
1069 static const struct rvin_group_route rcar_info_r8a77970_routes[] = {
1070 { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) },
1071 { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) },
1072 { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) },
1073 { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) },
1074 { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) },
1075 { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) },
1076 { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) },
1077 { /* Sentinel */ }
1080 static const struct rvin_info rcar_info_r8a77970 = {
1081 .model = RCAR_GEN3,
1082 .use_mc = true,
1083 .max_width = 4096,
1084 .max_height = 4096,
1085 .routes = rcar_info_r8a77970_routes,
1088 static const struct rvin_group_route rcar_info_r8a77995_routes[] = {
1089 { /* Sentinel */ }
1092 static const struct rvin_info rcar_info_r8a77995 = {
1093 .model = RCAR_GEN3,
1094 .use_mc = true,
1095 .max_width = 4096,
1096 .max_height = 4096,
1097 .routes = rcar_info_r8a77995_routes,
1100 static const struct of_device_id rvin_of_id_table[] = {
1102 .compatible = "renesas,vin-r8a7778",
1103 .data = &rcar_info_m1,
1106 .compatible = "renesas,vin-r8a7779",
1107 .data = &rcar_info_h1,
1110 .compatible = "renesas,vin-r8a7790",
1111 .data = &rcar_info_gen2,
1114 .compatible = "renesas,vin-r8a7791",
1115 .data = &rcar_info_gen2,
1118 .compatible = "renesas,vin-r8a7793",
1119 .data = &rcar_info_gen2,
1122 .compatible = "renesas,vin-r8a7794",
1123 .data = &rcar_info_gen2,
1126 .compatible = "renesas,rcar-gen2-vin",
1127 .data = &rcar_info_gen2,
1130 .compatible = "renesas,vin-r8a7795",
1131 .data = &rcar_info_r8a7795,
1134 .compatible = "renesas,vin-r8a7796",
1135 .data = &rcar_info_r8a7796,
1138 .compatible = "renesas,vin-r8a77965",
1139 .data = &rcar_info_r8a77965,
1142 .compatible = "renesas,vin-r8a77970",
1143 .data = &rcar_info_r8a77970,
1146 .compatible = "renesas,vin-r8a77995",
1147 .data = &rcar_info_r8a77995,
1149 { /* Sentinel */ },
1151 MODULE_DEVICE_TABLE(of, rvin_of_id_table);
1153 static const struct soc_device_attribute r8a7795es1[] = {
1155 .soc_id = "r8a7795", .revision = "ES1.*",
1156 .data = &rcar_info_r8a7795es1,
1158 { /* Sentinel */ }
1161 static int rcar_vin_probe(struct platform_device *pdev)
1163 const struct soc_device_attribute *attr;
1164 struct rvin_dev *vin;
1165 struct resource *mem;
1166 int irq, ret;
1168 vin = devm_kzalloc(&pdev->dev, sizeof(*vin), GFP_KERNEL);
1169 if (!vin)
1170 return -ENOMEM;
1172 vin->dev = &pdev->dev;
1173 vin->info = of_device_get_match_data(&pdev->dev);
1176 * Special care is needed on r8a7795 ES1.x since it
1177 * uses different routing than r8a7795 ES2.0.
1179 attr = soc_device_match(r8a7795es1);
1180 if (attr)
1181 vin->info = attr->data;
1183 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1184 if (mem == NULL)
1185 return -EINVAL;
1187 vin->base = devm_ioremap_resource(vin->dev, mem);
1188 if (IS_ERR(vin->base))
1189 return PTR_ERR(vin->base);
1191 irq = platform_get_irq(pdev, 0);
1192 if (irq < 0)
1193 return irq;
1195 ret = rvin_dma_register(vin, irq);
1196 if (ret)
1197 return ret;
1199 platform_set_drvdata(pdev, vin);
1201 if (vin->info->use_mc) {
1202 ret = rvin_mc_init(vin);
1203 if (ret)
1204 goto error_dma_unregister;
1207 ret = rvin_parallel_init(vin);
1208 if (ret)
1209 goto error_group_unregister;
1211 pm_suspend_ignore_children(&pdev->dev, true);
1212 pm_runtime_enable(&pdev->dev);
1214 return 0;
1216 error_group_unregister:
1217 if (vin->info->use_mc) {
1218 mutex_lock(&vin->group->lock);
1219 if (&vin->v4l2_dev == vin->group->notifier.v4l2_dev) {
1220 v4l2_async_notifier_unregister(&vin->group->notifier);
1221 v4l2_async_notifier_cleanup(&vin->group->notifier);
1223 mutex_unlock(&vin->group->lock);
1224 rvin_group_put(vin);
1227 error_dma_unregister:
1228 rvin_dma_unregister(vin);
1230 return ret;
1233 static int rcar_vin_remove(struct platform_device *pdev)
1235 struct rvin_dev *vin = platform_get_drvdata(pdev);
1237 pm_runtime_disable(&pdev->dev);
1239 rvin_v4l2_unregister(vin);
1241 v4l2_async_notifier_unregister(&vin->notifier);
1242 v4l2_async_notifier_cleanup(&vin->notifier);
1244 if (vin->info->use_mc) {
1245 mutex_lock(&vin->group->lock);
1246 if (&vin->v4l2_dev == vin->group->notifier.v4l2_dev) {
1247 v4l2_async_notifier_unregister(&vin->group->notifier);
1248 v4l2_async_notifier_cleanup(&vin->group->notifier);
1250 mutex_unlock(&vin->group->lock);
1251 rvin_group_put(vin);
1252 } else {
1253 v4l2_ctrl_handler_free(&vin->ctrl_handler);
1256 rvin_dma_unregister(vin);
1258 return 0;
1261 static struct platform_driver rcar_vin_driver = {
1262 .driver = {
1263 .name = "rcar-vin",
1264 .of_match_table = rvin_of_id_table,
1266 .probe = rcar_vin_probe,
1267 .remove = rcar_vin_remove,
1270 module_platform_driver(rcar_vin_driver);
1272 MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>");
1273 MODULE_DESCRIPTION("Renesas R-Car VIN camera host driver");
1274 MODULE_LICENSE("GPL");