Commit 1ebaeb09 authored by Sowjanya Komatineni's avatar Sowjanya Komatineni Committed by Mauro Carvalho Chehab
Browse files

media: tegra-video: Add support for external sensor capture



This patch adds support to capture from the external sensor
based on device graph in the device tree.

Driver walks through the device graph to create media links
between the entities and registers and unregisters video devices
when the corresponding sub-devices are bound and unbound.

Channel formats are enumerated based on available formats from
the sensor and the corresponding matched formats from the Tegra
supported video formats list.

Each Tegra CSI instance can be configured as 4-lane or 2-lane
based on supported lane configuration from the sensor through
the device tree.

Currently this driver supports V4L2 video node centric only.

[hverkuil: changed video_unregister_device to vb2_video_unregister_device]

Signed-off-by: default avatarSowjanya Komatineni <skomatineni@nvidia.com>
Signed-off-by: default avatarHans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+huawei@kernel.org>
parent 654c433b
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ config VIDEO_TEGRA
	depends on VIDEO_V4L2
	select MEDIA_CONTROLLER
	select VIDEOBUF2_DMA_CONTIG
	select V4L2_FWNODE
	help
	  Choose this option if you have an NVIDIA Tegra SoC.

+0 −4
Original line number Diff line number Diff line
TODO list
* Currently driver supports Tegra build-in TPG only with direct media links
  from CSI to VI. Add kernel config CONFIG_VIDEO_TEGRA_TPG and update the
  driver to do TPG Vs Sensor media links based on CONFIG_VIDEO_TEGRA_TPG.
* Add real camera sensor capture support.
* Add Tegra CSI MIPI pads calibration.
* Add MIPI clock Settle time computation based on the data rate.
* Add support for Ganged mode.
+120 −19
Original line number Diff line number Diff line
@@ -9,10 +9,13 @@
#include <linux/host1x.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>

#include <media/v4l2-fwnode.h>

#include "csi.h"
#include "video.h"

@@ -304,15 +307,13 @@ static const struct v4l2_subdev_ops tegra_csi_ops = {
	.pad    = &tegra_csi_pad_ops,
};

static int tegra_csi_tpg_channels_alloc(struct tegra_csi *csi)
static int tegra_csi_channel_alloc(struct tegra_csi *csi,
				   struct device_node *node,
				   unsigned int port_num, unsigned int lanes,
				   unsigned int num_pads)
{
	struct device_node *node = csi->dev->of_node;
	unsigned int port_num;
	struct tegra_csi_channel *chan;
	unsigned int tpg_channels = csi->soc->csi_max_channels;

	/* allocate CSI channel for each CSI x2 ports */
	for (port_num = 0; port_num < tpg_channels; port_num++) {
	chan = kzalloc(sizeof(*chan), GFP_KERNEL);
	if (!chan)
		return -ENOMEM;
@@ -320,15 +321,101 @@ static int tegra_csi_tpg_channels_alloc(struct tegra_csi *csi)
	list_add_tail(&chan->list, &csi->csi_chans);
	chan->csi = csi;
	chan->csi_port_num = port_num;
		chan->numlanes = 2;
	chan->numlanes = lanes;
	chan->of_node = node;
		chan->numpads = 1;
	chan->numpads = num_pads;
	if (num_pads & 0x2) {
		chan->pads[0].flags = MEDIA_PAD_FL_SINK;
		chan->pads[1].flags = MEDIA_PAD_FL_SOURCE;
	} else {
		chan->pads[0].flags = MEDIA_PAD_FL_SOURCE;
	}

	return 0;
}

static int tegra_csi_tpg_channels_alloc(struct tegra_csi *csi)
{
	struct device_node *node = csi->dev->of_node;
	unsigned int port_num;
	unsigned int tpg_channels = csi->soc->csi_max_channels;
	int ret;

	/* allocate CSI channel for each CSI x2 ports */
	for (port_num = 0; port_num < tpg_channels; port_num++) {
		ret = tegra_csi_channel_alloc(csi, node, port_num, 2, 1);
		if (ret < 0)
			return ret;
	}

	return 0;
}

static int tegra_csi_channels_alloc(struct tegra_csi *csi)
{
	struct device_node *node = csi->dev->of_node;
	struct v4l2_fwnode_endpoint v4l2_ep = {
		.bus_type = V4L2_MBUS_CSI2_DPHY
	};
	struct fwnode_handle *fwh;
	struct device_node *channel;
	struct device_node *ep;
	unsigned int lanes, portno, num_pads;
	int ret;

	for_each_child_of_node(node, channel) {
		if (!of_node_name_eq(channel, "channel"))
			continue;

		ret = of_property_read_u32(channel, "reg", &portno);
		if (ret < 0)
			continue;

		if (portno >= csi->soc->csi_max_channels) {
			dev_err(csi->dev, "invalid port num %d for %pOF\n",
				portno, channel);
			ret = -EINVAL;
			goto err_node_put;
		}

		ep = of_graph_get_endpoint_by_regs(channel, 0, 0);
		if (!ep)
			continue;

		fwh = of_fwnode_handle(ep);
		ret = v4l2_fwnode_endpoint_parse(fwh, &v4l2_ep);
		of_node_put(ep);
		if (ret) {
			dev_err(csi->dev,
				"failed to parse v4l2 endpoint for %pOF: %d\n",
				channel, ret);
			goto err_node_put;
		}

		lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes;
		if (!lanes || ((lanes & (lanes - 1)) != 0)) {
			dev_err(csi->dev, "invalid data-lanes %d for %pOF\n",
				lanes, channel);
			ret = -EINVAL;
			goto err_node_put;
		}

		num_pads = of_graph_get_endpoint_count(channel);
		if (num_pads == TEGRA_CSI_PADS_NUM) {
			ret = tegra_csi_channel_alloc(csi, channel, portno,
						      lanes, num_pads);
			if (ret < 0)
				goto err_node_put;
		}
	}

	return 0;

err_node_put:
	of_node_put(channel);
	return ret;
}

static int tegra_csi_channel_init(struct tegra_csi_channel *chan)
{
	struct tegra_csi *csi = chan->csi;
@@ -369,6 +456,15 @@ static int tegra_csi_channel_init(struct tegra_csi_channel *chan)
		return ret;
	}

	if (!IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG)) {
		ret = v4l2_async_register_subdev(subdev);
		if (ret < 0) {
			dev_err(csi->dev,
				"failed to register subdev: %d\n", ret);
			return ret;
		}
	}

	return 0;
}

@@ -408,8 +504,12 @@ static void tegra_csi_channels_cleanup(struct tegra_csi *csi)

	list_for_each_entry_safe(chan, tmp, &csi->csi_chans, list) {
		subdev = &chan->subdev;
		if (subdev->dev)
		if (subdev->dev) {
			if (!IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG))
				v4l2_async_unregister_subdev(subdev);
			media_entity_cleanup(&subdev->entity);
		}

		list_del(&chan->list);
		kfree(chan);
	}
@@ -446,14 +546,15 @@ static int tegra_csi_init(struct host1x_client *client)

	INIT_LIST_HEAD(&csi->csi_chans);

	if (IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG)) {
	if (IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG))
		ret = tegra_csi_tpg_channels_alloc(csi);
	else
		ret = tegra_csi_channels_alloc(csi);
	if (ret < 0) {
		dev_err(csi->dev,
				"failed to allocate tpg channels: %d\n", ret);
			"failed to allocate channels: %d\n", ret);
		goto cleanup;
	}
	}

	ret = tegra_csi_channels_init(csi);
	if (ret < 0)
+1 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@
#define __TEGRA_CSI_H__

#include <media/media-entity.h>
#include <media/v4l2-async.h>
#include <media/v4l2-subdev.h>

/*
+1 −1
Original line number Diff line number Diff line
@@ -230,7 +230,7 @@ static void tegra_channel_capture_error_recover(struct tegra_vi_channel *chan)
	tegra_channel_capture_setup(chan);

	/* recover CSI block */
	subdev = tegra_channel_get_remote_subdev(chan);
	subdev = tegra_channel_get_remote_csi_subdev(chan);
	tegra_csi_error_recover(subdev);
}

Loading