// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for Renesas R-Car VIN
*
* Copyright (C) 2016 Renesas Electronics Corp.
* Copyright (C) 2011-2013 Renesas Solutions Corp.
* Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com>
* Copyright (C) 2008 Magnus Damm
*
* Based on the soc-camera rcar_vin driver
*/
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/sys_soc.h>
#include <media/v4l2-async.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-mc.h>
#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)
#define v4l2_dev_to_vin(d) container_of(d, struct rvin_dev, v4l2_dev)
/* -----------------------------------------------------------------------------
* Media Controller link notification
*/
/* group lock should be held when calling this function. */
static int rvin_group_entity_to_csi_id(struct rvin_group *group,
struct media_entity *entity)
{
struct v4l2_subdev *sd;
unsigned int i;
sd = media_entity_to_v4l2_subdev(entity);
for (i = 0; i < RVIN_CSI_MAX; i++)
if (group->csi[i].subdev == sd)
return i;
return -ENODEV;
}
static unsigned int rvin_group_get_mask(struct rvin_dev *vin,
enum rvin_csi_id csi_id,
unsigned char channel)
{
const struct rvin_group_route *route;
unsigned int mask = 0;
for (route = vin->info->routes; route->mask; route++) {
if (route->vin == vin->id &&
route->csi == csi_id &&
route->channel == channel) {
vin_dbg(vin,
"Adding route: vin: %d csi: %d channel: %d\n",
route->vin, route->csi, route->channel);
mask |= route->mask;
}
}
return mask;
}
/*
* Link setup for the links between a VIN and a CSI-2 receiver is a bit
* complex. The reason for this is that the register controlling routing
* is not present in each VIN instance. There are special VINs which
* control routing for themselves and other VINs. There are not many
* different possible links combinations that can be enabled at the same
* time, therefor all already enabled links which are controlled by a
* master VIN need to be taken into account when making the decision
* if a new link can be enabled or not.
*
* 1. Find out which VIN the link the user tries to enable is connected to.
* 2. Lookup which master VIN controls the links for this VIN.
* 3. Start with a bitmask with all bits set.
* 4. For each previously enabled link from the master VIN bitwise AND its
* route mask (see documentation for mask in struct rvin_group_route)
* with the bitmask.
* 5. Bitwise AND the mask for the link the user tries to enable to the bitmask.
* 6. If the bitmask is not empty at this point the new link can be enabled
* while keeping all previous links enabled. Update the CHSEL value of the
* master VIN and inform the user that the link could be enabled.
*
* Please note that no link can be enabled if any VIN in the group is
* currently open.
*/
static int rvin_group_link_notify(struct media_link *link, u32 flags,
unsigned int notification)
{