#include "rcar-vin.h"
+/*
+ * The companion CSI-2 receiver driver (rcar-csi2) is known
+ * and we know it has one source pad (pad 0) and four sink
+ * pads (pad 1-4). So to translate a pad on the remote
+ * CSI-2 receiver to/from the VIN internal channel number simply
+ * subtract/add one from the pad/channel number.
+ */
+#define rvin_group_csi_pad_to_channel(pad) ((pad) - 1)
+#define rvin_group_csi_channel_to_pad(channel) ((channel) + 1)
+
+/*
+ * Not all VINs are created equal, master VINs control the
+ * routing for other VIN's. We can figure out which VIN is
+ * master by looking at a VINs id.
+ */
+#define rvin_group_id_to_master(vin) ((vin) < 4 ? 0 : 4)
+
/* -----------------------------------------------------------------------------
* Gen3 CSI2 Group Allocator
*/
return 0;
}
+/* -----------------------------------------------------------------------------
+ * Group async notifier
+ */
+
+static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier)
+{
+ struct rvin_dev *vin = notifier_to_vin(notifier);
+ const struct rvin_group_route *route;
+ unsigned int i;
+ int ret;
+
+ ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev);
+ if (ret) {
+ vin_err(vin, "Failed to register subdev nodes\n");
+ return ret;
+ }
+
+ /* Register all video nodes for the group. */
+ for (i = 0; i < RCAR_VIN_NUM; i++) {
+ if (vin->group->vin[i]) {
+ ret = rvin_v4l2_register(vin->group->vin[i]);
+ if (ret)
+ return ret;
+ }
+ }
+
+ /* Create all media device links between VINs and CSI-2's. */
+ mutex_lock(&vin->group->lock);
+ for (route = vin->info->routes; route->mask; route++) {
+ struct media_pad *source_pad, *sink_pad;
+ struct media_entity *source, *sink;
+ unsigned int source_idx;
+
+ /* Check that VIN is part of the group. */
+ if (!vin->group->vin[route->vin])
+ continue;
+
+ /* Check that VIN' master is part of the group. */
+ if (!vin->group->vin[rvin_group_id_to_master(route->vin)])
+ continue;
+
+ /* Check that CSI-2 is part of the group. */
+ if (!vin->group->csi[route->csi].subdev)
+ continue;
+
+ source = &vin->group->csi[route->csi].subdev->entity;
+ source_idx = rvin_group_csi_channel_to_pad(route->channel);
+ source_pad = &source->pads[source_idx];
+
+ sink = &vin->group->vin[route->vin]->vdev.entity;
+ sink_pad = &sink->pads[0];
+
+ /* Skip if link already exists. */
+ if (media_entity_find_link(source_pad, sink_pad))
+ continue;
+
+ ret = media_create_pad_link(source, source_idx, sink, 0, 0);
+ if (ret) {
+ vin_err(vin, "Error adding link from %s to %s\n",
+ source->name, sink->name);
+ break;
+ }
+ }
+ mutex_unlock(&vin->group->lock);
+
+ return ret;
+}
+
+static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct rvin_dev *vin = notifier_to_vin(notifier);
+ unsigned int i;
+
+ for (i = 0; i < RCAR_VIN_NUM; i++)
+ if (vin->group->vin[i])
+ rvin_v4l2_unregister(vin->group->vin[i]);
+
+ mutex_lock(&vin->group->lock);
+
+ for (i = 0; i < RVIN_CSI_MAX; i++) {
+ if (vin->group->csi[i].fwnode != asd->match.fwnode)
+ continue;
+ vin->group->csi[i].subdev = NULL;
+ vin_dbg(vin, "Unbind CSI-2 %s from slot %u\n", subdev->name, i);
+ break;
+ }
+
+ mutex_unlock(&vin->group->lock);
+}
+
+static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct rvin_dev *vin = notifier_to_vin(notifier);
+ unsigned int i;
+
+ mutex_lock(&vin->group->lock);
+
+ for (i = 0; i < RVIN_CSI_MAX; i++) {
+ if (vin->group->csi[i].fwnode != asd->match.fwnode)
+ continue;
+ vin->group->csi[i].subdev = subdev;
+ vin_dbg(vin, "Bound CSI-2 %s to slot %u\n", subdev->name, i);
+ break;
+ }
+
+ mutex_unlock(&vin->group->lock);
+
+ return 0;
+}
+
+static const struct v4l2_async_notifier_operations rvin_group_notify_ops = {
+ .bound = rvin_group_notify_bound,
+ .unbind = rvin_group_notify_unbind,
+ .complete = rvin_group_notify_complete,
+};
+
+static int rvin_mc_parse_of_endpoint(struct device *dev,
+ struct v4l2_fwnode_endpoint *vep,
+ struct v4l2_async_subdev *asd)
+{
+ struct rvin_dev *vin = dev_get_drvdata(dev);
+
+ if (vep->base.port != 1 || vep->base.id >= RVIN_CSI_MAX)
+ return -EINVAL;
+
+ if (!of_device_is_available(to_of_node(asd->match.fwnode))) {
+
+ vin_dbg(vin, "OF device %pOF disabled, ignoring\n",
+ to_of_node(asd->match.fwnode));
+ return -ENOTCONN;
+
+ }
+
+ if (vin->group->csi[vep->base.id].fwnode) {
+ vin_dbg(vin, "OF device %pOF already handled\n",
+ to_of_node(asd->match.fwnode));
+ return -ENOTCONN;
+ }
+
+ vin->group->csi[vep->base.id].fwnode = asd->match.fwnode;
+
+ vin_dbg(vin, "Add group OF device %pOF to slot %u\n",
+ to_of_node(asd->match.fwnode), vep->base.id);
+
+ return 0;
+}
+
+static int rvin_mc_parse_of_graph(struct rvin_dev *vin)
+{
+ unsigned int count = 0;
+ unsigned int i;
+ int ret;
+
+ mutex_lock(&vin->group->lock);
+
+ /* If there already is a notifier something has gone wrong, bail out. */
+ if (WARN_ON(vin->group->notifier)) {
+ mutex_unlock(&vin->group->lock);
+ return -EINVAL;
+ }
+
+ /* If not all VIN's are registered don't register the notifier. */
+ for (i = 0; i < RCAR_VIN_NUM; i++)
+ if (vin->group->vin[i])
+ count++;
+
+ if (vin->group->count != count) {
+ mutex_unlock(&vin->group->lock);
+ return 0;
+ }
+
+ /*
+ * Have all VIN's look for subdevices. Some subdevices will overlap
+ * but the parser function can handle it, so each subdevice will
+ * only be registered once with the notifier.
+ */
+
+ vin->group->notifier = &vin->notifier;
+
+ for (i = 0; i < RCAR_VIN_NUM; i++) {
+ if (!vin->group->vin[i])
+ continue;
+
+ ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
+ vin->group->vin[i]->dev, vin->group->notifier,
+ sizeof(struct v4l2_async_subdev), 1,
+ rvin_mc_parse_of_endpoint);
+ if (ret) {
+ mutex_unlock(&vin->group->lock);
+ return ret;
+ }
+ }
+
+ mutex_unlock(&vin->group->lock);
+
+ vin->group->notifier->ops = &rvin_group_notify_ops;
+
+ ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier);
+ if (ret < 0) {
+ vin_err(vin, "Notifier registration failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
static int rvin_mc_init(struct rvin_dev *vin)
{
int ret;
if (ret)
return ret;
- return rvin_group_get(vin);
+ ret = rvin_group_get(vin);
+ if (ret)
+ return ret;
+
+ ret = rvin_mc_parse_of_graph(vin);
+ if (ret)
+ rvin_group_put(vin);
+
+ return ret;
}
/* -----------------------------------------------------------------------------
v4l2_async_notifier_unregister(&vin->notifier);
v4l2_async_notifier_cleanup(&vin->notifier);
- if (vin->info->use_mc)
+ if (vin->info->use_mc) {
+ mutex_lock(&vin->group->lock);
+ if (vin->group->notifier == &vin->notifier)
+ vin->group->notifier = NULL;
+ mutex_unlock(&vin->group->lock);
rvin_group_put(vin);
- else
+ } else {
v4l2_ctrl_handler_free(&vin->ctrl_handler);
+ }
rvin_dma_unregister(vin);