13d8a97eaSSowjanya Komatineni // SPDX-License-Identifier: GPL-2.0-only
23d8a97eaSSowjanya Komatineni /*
33d8a97eaSSowjanya Komatineni  * Copyright (C) 2020 NVIDIA CORPORATION.  All rights reserved.
43d8a97eaSSowjanya Komatineni  */
53d8a97eaSSowjanya Komatineni 
63d8a97eaSSowjanya Komatineni #include <linux/clk.h>
73d8a97eaSSowjanya Komatineni #include <linux/clk/tegra.h>
83d8a97eaSSowjanya Komatineni #include <linux/device.h>
93d8a97eaSSowjanya Komatineni #include <linux/host1x.h>
103d8a97eaSSowjanya Komatineni #include <linux/module.h>
113d8a97eaSSowjanya Komatineni #include <linux/of.h>
121ebaeb09SSowjanya Komatineni #include <linux/of_graph.h>
133d8a97eaSSowjanya Komatineni #include <linux/platform_device.h>
143d8a97eaSSowjanya Komatineni #include <linux/pm_runtime.h>
153d8a97eaSSowjanya Komatineni 
161ebaeb09SSowjanya Komatineni #include <media/v4l2-fwnode.h>
171ebaeb09SSowjanya Komatineni 
183d8a97eaSSowjanya Komatineni #include "csi.h"
193d8a97eaSSowjanya Komatineni #include "video.h"
203d8a97eaSSowjanya Komatineni 
21cc9d3fa2SSowjanya Komatineni #define MHZ			1000000
22cc9d3fa2SSowjanya Komatineni 
233d8a97eaSSowjanya Komatineni static inline struct tegra_csi *
host1x_client_to_csi(struct host1x_client * client)243d8a97eaSSowjanya Komatineni host1x_client_to_csi(struct host1x_client *client)
253d8a97eaSSowjanya Komatineni {
263d8a97eaSSowjanya Komatineni 	return container_of(client, struct tegra_csi, client);
273d8a97eaSSowjanya Komatineni }
283d8a97eaSSowjanya Komatineni 
to_csi_chan(struct v4l2_subdev * subdev)293d8a97eaSSowjanya Komatineni static inline struct tegra_csi_channel *to_csi_chan(struct v4l2_subdev *subdev)
303d8a97eaSSowjanya Komatineni {
313d8a97eaSSowjanya Komatineni 	return container_of(subdev, struct tegra_csi_channel, subdev);
323d8a97eaSSowjanya Komatineni }
333d8a97eaSSowjanya Komatineni 
343d8a97eaSSowjanya Komatineni /*
353d8a97eaSSowjanya Komatineni  * CSI is a separate subdevice which has 6 source pads to generate
363d8a97eaSSowjanya Komatineni  * test pattern. CSI subdevice pad ops are used only for TPG and
373d8a97eaSSowjanya Komatineni  * allows below TPG formats.
383d8a97eaSSowjanya Komatineni  */
393d8a97eaSSowjanya Komatineni static const struct v4l2_mbus_framefmt tegra_csi_tpg_fmts[] = {
403d8a97eaSSowjanya Komatineni 	{
413d8a97eaSSowjanya Komatineni 		TEGRA_DEF_WIDTH,
423d8a97eaSSowjanya Komatineni 		TEGRA_DEF_HEIGHT,
433d8a97eaSSowjanya Komatineni 		MEDIA_BUS_FMT_SRGGB10_1X10,
443d8a97eaSSowjanya Komatineni 		V4L2_FIELD_NONE,
453d8a97eaSSowjanya Komatineni 		V4L2_COLORSPACE_SRGB
463d8a97eaSSowjanya Komatineni 	},
473d8a97eaSSowjanya Komatineni 	{
483d8a97eaSSowjanya Komatineni 		TEGRA_DEF_WIDTH,
493d8a97eaSSowjanya Komatineni 		TEGRA_DEF_HEIGHT,
503d8a97eaSSowjanya Komatineni 		MEDIA_BUS_FMT_RGB888_1X32_PADHI,
513d8a97eaSSowjanya Komatineni 		V4L2_FIELD_NONE,
523d8a97eaSSowjanya Komatineni 		V4L2_COLORSPACE_SRGB
533d8a97eaSSowjanya Komatineni 	},
543d8a97eaSSowjanya Komatineni };
553d8a97eaSSowjanya Komatineni 
563d8a97eaSSowjanya Komatineni static const struct v4l2_frmsize_discrete tegra_csi_tpg_sizes[] = {
573d8a97eaSSowjanya Komatineni 	{ 1280, 720 },
583d8a97eaSSowjanya Komatineni 	{ 1920, 1080 },
593d8a97eaSSowjanya Komatineni 	{ 3840, 2160 },
603d8a97eaSSowjanya Komatineni };
613d8a97eaSSowjanya Komatineni 
623d8a97eaSSowjanya Komatineni /*
633d8a97eaSSowjanya Komatineni  * V4L2 Subdevice Pad Operations
643d8a97eaSSowjanya Komatineni  */
csi_enum_bus_code(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)653d8a97eaSSowjanya Komatineni static int csi_enum_bus_code(struct v4l2_subdev *subdev,
660d346d2aSTomi Valkeinen 			     struct v4l2_subdev_state *sd_state,
673d8a97eaSSowjanya Komatineni 			     struct v4l2_subdev_mbus_code_enum *code)
683d8a97eaSSowjanya Komatineni {
69341187bfSSowjanya Komatineni 	if (!IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG))
70341187bfSSowjanya Komatineni 		return -ENOIOCTLCMD;
71341187bfSSowjanya Komatineni 
723d8a97eaSSowjanya Komatineni 	if (code->index >= ARRAY_SIZE(tegra_csi_tpg_fmts))
733d8a97eaSSowjanya Komatineni 		return -EINVAL;
743d8a97eaSSowjanya Komatineni 
753d8a97eaSSowjanya Komatineni 	code->code = tegra_csi_tpg_fmts[code->index].code;
763d8a97eaSSowjanya Komatineni 
773d8a97eaSSowjanya Komatineni 	return 0;
783d8a97eaSSowjanya Komatineni }
793d8a97eaSSowjanya Komatineni 
csi_get_format(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)803d8a97eaSSowjanya Komatineni static int csi_get_format(struct v4l2_subdev *subdev,
810d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
823d8a97eaSSowjanya Komatineni 			  struct v4l2_subdev_format *fmt)
833d8a97eaSSowjanya Komatineni {
843d8a97eaSSowjanya Komatineni 	struct tegra_csi_channel *csi_chan = to_csi_chan(subdev);
853d8a97eaSSowjanya Komatineni 
86341187bfSSowjanya Komatineni 	if (!IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG))
87341187bfSSowjanya Komatineni 		return -ENOIOCTLCMD;
88341187bfSSowjanya Komatineni 
893d8a97eaSSowjanya Komatineni 	fmt->format = csi_chan->format;
903d8a97eaSSowjanya Komatineni 
913d8a97eaSSowjanya Komatineni 	return 0;
923d8a97eaSSowjanya Komatineni }
933d8a97eaSSowjanya Komatineni 
csi_get_frmrate_table_index(struct tegra_csi * csi,u32 code,u32 width,u32 height)943d8a97eaSSowjanya Komatineni static int csi_get_frmrate_table_index(struct tegra_csi *csi, u32 code,
953d8a97eaSSowjanya Komatineni 				       u32 width, u32 height)
963d8a97eaSSowjanya Komatineni {
973d8a97eaSSowjanya Komatineni 	const struct tpg_framerate *frmrate;
983d8a97eaSSowjanya Komatineni 	unsigned int i;
993d8a97eaSSowjanya Komatineni 
1003d8a97eaSSowjanya Komatineni 	frmrate = csi->soc->tpg_frmrate_table;
1013d8a97eaSSowjanya Komatineni 	for (i = 0; i < csi->soc->tpg_frmrate_table_size; i++) {
1023d8a97eaSSowjanya Komatineni 		if (frmrate[i].code == code &&
1033d8a97eaSSowjanya Komatineni 		    frmrate[i].frmsize.width == width &&
1043d8a97eaSSowjanya Komatineni 		    frmrate[i].frmsize.height == height) {
1053d8a97eaSSowjanya Komatineni 			return i;
1063d8a97eaSSowjanya Komatineni 		}
1073d8a97eaSSowjanya Komatineni 	}
1083d8a97eaSSowjanya Komatineni 
1093d8a97eaSSowjanya Komatineni 	return -EINVAL;
1103d8a97eaSSowjanya Komatineni }
1113d8a97eaSSowjanya Komatineni 
csi_chan_update_blank_intervals(struct tegra_csi_channel * csi_chan,u32 code,u32 width,u32 height)1123d8a97eaSSowjanya Komatineni static void csi_chan_update_blank_intervals(struct tegra_csi_channel *csi_chan,
1133d8a97eaSSowjanya Komatineni 					    u32 code, u32 width, u32 height)
1143d8a97eaSSowjanya Komatineni {
1153d8a97eaSSowjanya Komatineni 	struct tegra_csi *csi = csi_chan->csi;
1163d8a97eaSSowjanya Komatineni 	const struct tpg_framerate *frmrate = csi->soc->tpg_frmrate_table;
1173d8a97eaSSowjanya Komatineni 	int index;
1183d8a97eaSSowjanya Komatineni 
1193d8a97eaSSowjanya Komatineni 	index = csi_get_frmrate_table_index(csi_chan->csi, code,
1203d8a97eaSSowjanya Komatineni 					    width, height);
1213d8a97eaSSowjanya Komatineni 	if (index >= 0) {
1223d8a97eaSSowjanya Komatineni 		csi_chan->h_blank = frmrate[index].h_blank;
1233d8a97eaSSowjanya Komatineni 		csi_chan->v_blank = frmrate[index].v_blank;
1243d8a97eaSSowjanya Komatineni 		csi_chan->framerate = frmrate[index].framerate;
1253d8a97eaSSowjanya Komatineni 	}
1263d8a97eaSSowjanya Komatineni }
1273d8a97eaSSowjanya Komatineni 
csi_enum_framesizes(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_size_enum * fse)1283d8a97eaSSowjanya Komatineni static int csi_enum_framesizes(struct v4l2_subdev *subdev,
1290d346d2aSTomi Valkeinen 			       struct v4l2_subdev_state *sd_state,
1303d8a97eaSSowjanya Komatineni 			       struct v4l2_subdev_frame_size_enum *fse)
1313d8a97eaSSowjanya Komatineni {
1323d8a97eaSSowjanya Komatineni 	unsigned int i;
1333d8a97eaSSowjanya Komatineni 
134341187bfSSowjanya Komatineni 	if (!IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG))
135341187bfSSowjanya Komatineni 		return -ENOIOCTLCMD;
136341187bfSSowjanya Komatineni 
1373d8a97eaSSowjanya Komatineni 	if (fse->index >= ARRAY_SIZE(tegra_csi_tpg_sizes))
1383d8a97eaSSowjanya Komatineni 		return -EINVAL;
1393d8a97eaSSowjanya Komatineni 
1403d8a97eaSSowjanya Komatineni 	for (i = 0; i < ARRAY_SIZE(tegra_csi_tpg_fmts); i++)
1413d8a97eaSSowjanya Komatineni 		if (fse->code == tegra_csi_tpg_fmts[i].code)
1423d8a97eaSSowjanya Komatineni 			break;
1433d8a97eaSSowjanya Komatineni 
1443d8a97eaSSowjanya Komatineni 	if (i == ARRAY_SIZE(tegra_csi_tpg_fmts))
1453d8a97eaSSowjanya Komatineni 		return -EINVAL;
1463d8a97eaSSowjanya Komatineni 
1473d8a97eaSSowjanya Komatineni 	fse->min_width = tegra_csi_tpg_sizes[fse->index].width;
1483d8a97eaSSowjanya Komatineni 	fse->max_width = tegra_csi_tpg_sizes[fse->index].width;
1493d8a97eaSSowjanya Komatineni 	fse->min_height = tegra_csi_tpg_sizes[fse->index].height;
1503d8a97eaSSowjanya Komatineni 	fse->max_height = tegra_csi_tpg_sizes[fse->index].height;
1513d8a97eaSSowjanya Komatineni 
1523d8a97eaSSowjanya Komatineni 	return 0;
1533d8a97eaSSowjanya Komatineni }
1543d8a97eaSSowjanya Komatineni 
csi_enum_frameintervals(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_interval_enum * fie)1553d8a97eaSSowjanya Komatineni static int csi_enum_frameintervals(struct v4l2_subdev *subdev,
1560d346d2aSTomi Valkeinen 				   struct v4l2_subdev_state *sd_state,
1573d8a97eaSSowjanya Komatineni 				   struct v4l2_subdev_frame_interval_enum *fie)
1583d8a97eaSSowjanya Komatineni {
1593d8a97eaSSowjanya Komatineni 	struct tegra_csi_channel *csi_chan = to_csi_chan(subdev);
1603d8a97eaSSowjanya Komatineni 	struct tegra_csi *csi = csi_chan->csi;
1613d8a97eaSSowjanya Komatineni 	const struct tpg_framerate *frmrate = csi->soc->tpg_frmrate_table;
1623d8a97eaSSowjanya Komatineni 	int index;
1633d8a97eaSSowjanya Komatineni 
164341187bfSSowjanya Komatineni 	if (!IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG))
165341187bfSSowjanya Komatineni 		return -ENOIOCTLCMD;
166341187bfSSowjanya Komatineni 
1673d8a97eaSSowjanya Komatineni 	/* one framerate per format and resolution */
1683d8a97eaSSowjanya Komatineni 	if (fie->index > 0)
1693d8a97eaSSowjanya Komatineni 		return -EINVAL;
1703d8a97eaSSowjanya Komatineni 
1713d8a97eaSSowjanya Komatineni 	index = csi_get_frmrate_table_index(csi_chan->csi, fie->code,
1723d8a97eaSSowjanya Komatineni 					    fie->width, fie->height);
1733d8a97eaSSowjanya Komatineni 	if (index < 0)
1743d8a97eaSSowjanya Komatineni 		return -EINVAL;
1753d8a97eaSSowjanya Komatineni 
1763d8a97eaSSowjanya Komatineni 	fie->interval.numerator = 1;
1773d8a97eaSSowjanya Komatineni 	fie->interval.denominator = frmrate[index].framerate;
1783d8a97eaSSowjanya Komatineni 
1793d8a97eaSSowjanya Komatineni 	return 0;
1803d8a97eaSSowjanya Komatineni }
1813d8a97eaSSowjanya Komatineni 
csi_set_format(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)1823d8a97eaSSowjanya Komatineni static int csi_set_format(struct v4l2_subdev *subdev,
1830d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
1843d8a97eaSSowjanya Komatineni 			  struct v4l2_subdev_format *fmt)
1853d8a97eaSSowjanya Komatineni {
1863d8a97eaSSowjanya Komatineni 	struct tegra_csi_channel *csi_chan = to_csi_chan(subdev);
1873d8a97eaSSowjanya Komatineni 	struct v4l2_mbus_framefmt *format = &fmt->format;
1883d8a97eaSSowjanya Komatineni 	const struct v4l2_frmsize_discrete *sizes;
1893d8a97eaSSowjanya Komatineni 	unsigned int i;
1903d8a97eaSSowjanya Komatineni 
191341187bfSSowjanya Komatineni 	if (!IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG))
192341187bfSSowjanya Komatineni 		return -ENOIOCTLCMD;
193341187bfSSowjanya Komatineni 
1943d8a97eaSSowjanya Komatineni 	sizes = v4l2_find_nearest_size(tegra_csi_tpg_sizes,
1953d8a97eaSSowjanya Komatineni 				       ARRAY_SIZE(tegra_csi_tpg_sizes),
1963d8a97eaSSowjanya Komatineni 				       width, height,
1973d8a97eaSSowjanya Komatineni 				       format->width, format->width);
1983d8a97eaSSowjanya Komatineni 	format->width = sizes->width;
1993d8a97eaSSowjanya Komatineni 	format->height = sizes->height;
2003d8a97eaSSowjanya Komatineni 
2013d8a97eaSSowjanya Komatineni 	for (i = 0; i < ARRAY_SIZE(tegra_csi_tpg_fmts); i++)
2023d8a97eaSSowjanya Komatineni 		if (format->code == tegra_csi_tpg_fmts[i].code)
2033d8a97eaSSowjanya Komatineni 			break;
2043d8a97eaSSowjanya Komatineni 
2053d8a97eaSSowjanya Komatineni 	if (i == ARRAY_SIZE(tegra_csi_tpg_fmts))
2063d8a97eaSSowjanya Komatineni 		i = 0;
2073d8a97eaSSowjanya Komatineni 
2083d8a97eaSSowjanya Komatineni 	format->code = tegra_csi_tpg_fmts[i].code;
2093d8a97eaSSowjanya Komatineni 	format->field = V4L2_FIELD_NONE;
2103d8a97eaSSowjanya Komatineni 
2113d8a97eaSSowjanya Komatineni 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
2123d8a97eaSSowjanya Komatineni 		return 0;
2133d8a97eaSSowjanya Komatineni 
2143d8a97eaSSowjanya Komatineni 	/* update blanking intervals from frame rate table and format */
2153d8a97eaSSowjanya Komatineni 	csi_chan_update_blank_intervals(csi_chan, format->code,
2163d8a97eaSSowjanya Komatineni 					format->width, format->height);
2173d8a97eaSSowjanya Komatineni 	csi_chan->format = *format;
2183d8a97eaSSowjanya Komatineni 
2193d8a97eaSSowjanya Komatineni 	return 0;
2203d8a97eaSSowjanya Komatineni }
2213d8a97eaSSowjanya Komatineni 
2223d8a97eaSSowjanya Komatineni /*
2233d8a97eaSSowjanya Komatineni  * V4L2 Subdevice Video Operations
2243d8a97eaSSowjanya Komatineni  */
tegra_csi_g_frame_interval(struct v4l2_subdev * subdev,struct v4l2_subdev_frame_interval * vfi)2253d8a97eaSSowjanya Komatineni static int tegra_csi_g_frame_interval(struct v4l2_subdev *subdev,
2263d8a97eaSSowjanya Komatineni 				      struct v4l2_subdev_frame_interval *vfi)
2273d8a97eaSSowjanya Komatineni {
2283d8a97eaSSowjanya Komatineni 	struct tegra_csi_channel *csi_chan = to_csi_chan(subdev);
2293d8a97eaSSowjanya Komatineni 
230341187bfSSowjanya Komatineni 	if (!IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG))
231341187bfSSowjanya Komatineni 		return -ENOIOCTLCMD;
232341187bfSSowjanya Komatineni 
2333d8a97eaSSowjanya Komatineni 	vfi->interval.numerator = 1;
2343d8a97eaSSowjanya Komatineni 	vfi->interval.denominator = csi_chan->framerate;
2353d8a97eaSSowjanya Komatineni 
2363d8a97eaSSowjanya Komatineni 	return 0;
2373d8a97eaSSowjanya Komatineni }
2383d8a97eaSSowjanya Komatineni 
csi_get_pixel_rate(struct tegra_csi_channel * csi_chan)239cc9d3fa2SSowjanya Komatineni static unsigned int csi_get_pixel_rate(struct tegra_csi_channel *csi_chan)
240cc9d3fa2SSowjanya Komatineni {
241cc9d3fa2SSowjanya Komatineni 	struct tegra_vi_channel *chan;
242cc9d3fa2SSowjanya Komatineni 	struct v4l2_subdev *src_subdev;
243cc9d3fa2SSowjanya Komatineni 	struct v4l2_ctrl *ctrl;
244cc9d3fa2SSowjanya Komatineni 
245cc9d3fa2SSowjanya Komatineni 	chan = v4l2_get_subdev_hostdata(&csi_chan->subdev);
246cc9d3fa2SSowjanya Komatineni 	src_subdev = tegra_channel_get_remote_source_subdev(chan);
247cc9d3fa2SSowjanya Komatineni 	ctrl = v4l2_ctrl_find(src_subdev->ctrl_handler, V4L2_CID_PIXEL_RATE);
248cc9d3fa2SSowjanya Komatineni 	if (ctrl)
249cc9d3fa2SSowjanya Komatineni 		return v4l2_ctrl_g_ctrl_int64(ctrl);
250cc9d3fa2SSowjanya Komatineni 
251cc9d3fa2SSowjanya Komatineni 	return 0;
252cc9d3fa2SSowjanya Komatineni }
253cc9d3fa2SSowjanya Komatineni 
tegra_csi_calc_settle_time(struct tegra_csi_channel * csi_chan,u8 csi_port_num,u8 * clk_settle_time,u8 * ths_settle_time)254cc9d3fa2SSowjanya Komatineni void tegra_csi_calc_settle_time(struct tegra_csi_channel *csi_chan,
2552ac4035aSSowjanya Komatineni 				u8 csi_port_num,
256cc9d3fa2SSowjanya Komatineni 				u8 *clk_settle_time,
257cc9d3fa2SSowjanya Komatineni 				u8 *ths_settle_time)
258cc9d3fa2SSowjanya Komatineni {
259cc9d3fa2SSowjanya Komatineni 	struct tegra_csi *csi = csi_chan->csi;
260cc9d3fa2SSowjanya Komatineni 	unsigned int cil_clk_mhz;
261cc9d3fa2SSowjanya Komatineni 	unsigned int pix_clk_mhz;
2622ac4035aSSowjanya Komatineni 	int clk_idx = (csi_port_num >> 1) + 1;
263cc9d3fa2SSowjanya Komatineni 
264cc9d3fa2SSowjanya Komatineni 	cil_clk_mhz = clk_get_rate(csi->clks[clk_idx].clk) / MHZ;
265cc9d3fa2SSowjanya Komatineni 	pix_clk_mhz = csi_get_pixel_rate(csi_chan) / MHZ;
266cc9d3fa2SSowjanya Komatineni 
267cc9d3fa2SSowjanya Komatineni 	/*
268cc9d3fa2SSowjanya Komatineni 	 * CLK Settle time is the interval during which HS receiver should
269cc9d3fa2SSowjanya Komatineni 	 * ignore any clock lane HS transitions, starting from the beginning
270cc9d3fa2SSowjanya Komatineni 	 * of T-CLK-PREPARE.
271cc9d3fa2SSowjanya Komatineni 	 * Per DPHY specification, T-CLK-SETTLE should be between 95ns ~ 300ns
272cc9d3fa2SSowjanya Komatineni 	 *
273cc9d3fa2SSowjanya Komatineni 	 * 95ns < (clk-settle-programmed + 7) * lp clk period < 300ns
274cc9d3fa2SSowjanya Komatineni 	 * midpoint = 197.5 ns
275cc9d3fa2SSowjanya Komatineni 	 */
276cc9d3fa2SSowjanya Komatineni 	*clk_settle_time = ((95 + 300) * cil_clk_mhz - 14000) / 2000;
277cc9d3fa2SSowjanya Komatineni 
278cc9d3fa2SSowjanya Komatineni 	/*
279cc9d3fa2SSowjanya Komatineni 	 * THS Settle time is the interval during which HS receiver should
280cc9d3fa2SSowjanya Komatineni 	 * ignore any data lane HS transitions, starting from the beginning
281cc9d3fa2SSowjanya Komatineni 	 * of THS-PREPARE.
282cc9d3fa2SSowjanya Komatineni 	 *
283cc9d3fa2SSowjanya Komatineni 	 * Per DPHY specification, T-HS-SETTLE should be between 85ns + 6UI
284cc9d3fa2SSowjanya Komatineni 	 * and 145ns+10UI.
285cc9d3fa2SSowjanya Komatineni 	 * 85ns + 6UI < (Ths-settle-prog + 5) * lp_clk_period < 145ns + 10UI
286cc9d3fa2SSowjanya Komatineni 	 * midpoint = 115ns + 8UI
287cc9d3fa2SSowjanya Komatineni 	 */
288cc9d3fa2SSowjanya Komatineni 	if (pix_clk_mhz)
289cc9d3fa2SSowjanya Komatineni 		*ths_settle_time = (115 * cil_clk_mhz + 8000 * cil_clk_mhz
290cc9d3fa2SSowjanya Komatineni 				   / (2 * pix_clk_mhz) - 5000) / 1000;
291cc9d3fa2SSowjanya Komatineni }
292cc9d3fa2SSowjanya Komatineni 
tegra_csi_enable_stream(struct v4l2_subdev * subdev)293654c433bSSowjanya Komatineni static int tegra_csi_enable_stream(struct v4l2_subdev *subdev)
2943d8a97eaSSowjanya Komatineni {
2953d8a97eaSSowjanya Komatineni 	struct tegra_vi_channel *chan = v4l2_get_subdev_hostdata(subdev);
2963d8a97eaSSowjanya Komatineni 	struct tegra_csi_channel *csi_chan = to_csi_chan(subdev);
2973d8a97eaSSowjanya Komatineni 	struct tegra_csi *csi = csi_chan->csi;
298523c857eSSowjanya Komatineni 	int ret, err;
2993d8a97eaSSowjanya Komatineni 
300c09ffca5SMauro Carvalho Chehab 	ret = pm_runtime_resume_and_get(csi->dev);
3013d8a97eaSSowjanya Komatineni 	if (ret < 0) {
302654c433bSSowjanya Komatineni 		dev_err(csi->dev, "failed to get runtime PM: %d\n", ret);
3033d8a97eaSSowjanya Komatineni 		return ret;
3043d8a97eaSSowjanya Komatineni 	}
3053d8a97eaSSowjanya Komatineni 
306523c857eSSowjanya Komatineni 	if (csi_chan->mipi) {
307523c857eSSowjanya Komatineni 		ret = tegra_mipi_enable(csi_chan->mipi);
308523c857eSSowjanya Komatineni 		if (ret < 0) {
309523c857eSSowjanya Komatineni 			dev_err(csi->dev,
310523c857eSSowjanya Komatineni 				"failed to enable MIPI pads: %d\n", ret);
311523c857eSSowjanya Komatineni 			goto rpm_put;
312523c857eSSowjanya Komatineni 		}
313523c857eSSowjanya Komatineni 
314523c857eSSowjanya Komatineni 		/*
315523c857eSSowjanya Komatineni 		 * CSI MIPI pads PULLUP, PULLDN and TERM impedances need to
316523c857eSSowjanya Komatineni 		 * be calibrated after power on.
317523c857eSSowjanya Komatineni 		 * So, trigger the calibration start here and results will
318523c857eSSowjanya Komatineni 		 * be latched and applied to the pads when link is in LP11
319523c857eSSowjanya Komatineni 		 * state during start of sensor streaming.
320523c857eSSowjanya Komatineni 		 */
321523c857eSSowjanya Komatineni 		ret = tegra_mipi_start_calibration(csi_chan->mipi);
322523c857eSSowjanya Komatineni 		if (ret < 0) {
323523c857eSSowjanya Komatineni 			dev_err(csi->dev,
324523c857eSSowjanya Komatineni 				"failed to start MIPI calibration: %d\n", ret);
325523c857eSSowjanya Komatineni 			goto disable_mipi;
326523c857eSSowjanya Komatineni 		}
327523c857eSSowjanya Komatineni 	}
328523c857eSSowjanya Komatineni 
329654c433bSSowjanya Komatineni 	csi_chan->pg_mode = chan->pg_mode;
330*3bc46d75SLuca Ceresoli 
331*3bc46d75SLuca Ceresoli 	/*
332*3bc46d75SLuca Ceresoli 	 * Tegra CSI receiver can detect the first LP to HS transition.
333*3bc46d75SLuca Ceresoli 	 * So, start the CSI stream-on prior to sensor stream-on and
334*3bc46d75SLuca Ceresoli 	 * vice-versa for stream-off.
335*3bc46d75SLuca Ceresoli 	 */
3363d8a97eaSSowjanya Komatineni 	ret = csi->ops->csi_start_streaming(csi_chan);
3373d8a97eaSSowjanya Komatineni 	if (ret < 0)
338523c857eSSowjanya Komatineni 		goto finish_calibration;
3393d8a97eaSSowjanya Komatineni 
340*3bc46d75SLuca Ceresoli 	if (csi_chan->mipi) {
341*3bc46d75SLuca Ceresoli 		struct v4l2_subdev *src_subdev;
342*3bc46d75SLuca Ceresoli 		/*
343*3bc46d75SLuca Ceresoli 		 * TRM has incorrectly documented to wait for done status from
344*3bc46d75SLuca Ceresoli 		 * calibration logic after CSI interface power on.
345*3bc46d75SLuca Ceresoli 		 * As per the design, calibration results are latched and applied
346*3bc46d75SLuca Ceresoli 		 * to the pads only when the link is in LP11 state which will happen
347*3bc46d75SLuca Ceresoli 		 * during the sensor stream-on.
348*3bc46d75SLuca Ceresoli 		 * CSI subdev stream-on triggers start of MIPI pads calibration.
349*3bc46d75SLuca Ceresoli 		 * Wait for calibration to finish here after sensor subdev stream-on.
350*3bc46d75SLuca Ceresoli 		 */
351*3bc46d75SLuca Ceresoli 		src_subdev = tegra_channel_get_remote_source_subdev(chan);
352*3bc46d75SLuca Ceresoli 		ret = v4l2_subdev_call(src_subdev, video, s_stream, true);
353*3bc46d75SLuca Ceresoli 
354*3bc46d75SLuca Ceresoli 		if (ret < 0 && ret != -ENOIOCTLCMD)
355*3bc46d75SLuca Ceresoli 			goto disable_csi_stream;
356*3bc46d75SLuca Ceresoli 
357*3bc46d75SLuca Ceresoli 		err = tegra_mipi_finish_calibration(csi_chan->mipi);
358*3bc46d75SLuca Ceresoli 		if (err < 0)
359*3bc46d75SLuca Ceresoli 			dev_warn(csi->dev, "MIPI calibration failed: %d\n", err);
360*3bc46d75SLuca Ceresoli 	}
361*3bc46d75SLuca Ceresoli 
3623d8a97eaSSowjanya Komatineni 	return 0;
3633d8a97eaSSowjanya Komatineni 
364*3bc46d75SLuca Ceresoli disable_csi_stream:
365*3bc46d75SLuca Ceresoli 	csi->ops->csi_stop_streaming(csi_chan);
366523c857eSSowjanya Komatineni finish_calibration:
367523c857eSSowjanya Komatineni 	if (csi_chan->mipi)
368523c857eSSowjanya Komatineni 		tegra_mipi_finish_calibration(csi_chan->mipi);
369523c857eSSowjanya Komatineni disable_mipi:
370523c857eSSowjanya Komatineni 	if (csi_chan->mipi) {
371523c857eSSowjanya Komatineni 		err = tegra_mipi_disable(csi_chan->mipi);
372523c857eSSowjanya Komatineni 		if (err < 0)
373523c857eSSowjanya Komatineni 			dev_err(csi->dev,
374523c857eSSowjanya Komatineni 				"failed to disable MIPI pads: %d\n", err);
375523c857eSSowjanya Komatineni 	}
376523c857eSSowjanya Komatineni 
3773d8a97eaSSowjanya Komatineni rpm_put:
3783d8a97eaSSowjanya Komatineni 	pm_runtime_put(csi->dev);
3793d8a97eaSSowjanya Komatineni 	return ret;
3803d8a97eaSSowjanya Komatineni }
3813d8a97eaSSowjanya Komatineni 
tegra_csi_disable_stream(struct v4l2_subdev * subdev)382654c433bSSowjanya Komatineni static int tegra_csi_disable_stream(struct v4l2_subdev *subdev)
383654c433bSSowjanya Komatineni {
384*3bc46d75SLuca Ceresoli 	struct tegra_vi_channel *chan = v4l2_get_subdev_hostdata(subdev);
385654c433bSSowjanya Komatineni 	struct tegra_csi_channel *csi_chan = to_csi_chan(subdev);
386654c433bSSowjanya Komatineni 	struct tegra_csi *csi = csi_chan->csi;
387523c857eSSowjanya Komatineni 	int err;
388654c433bSSowjanya Komatineni 
389*3bc46d75SLuca Ceresoli 	/*
390*3bc46d75SLuca Ceresoli 	 * Stream-off subdevices in reverse order to stream-on.
391*3bc46d75SLuca Ceresoli 	 * Remote source subdev in TPG mode is same as CSI subdev.
392*3bc46d75SLuca Ceresoli 	 */
393*3bc46d75SLuca Ceresoli 	if (csi_chan->mipi) {
394*3bc46d75SLuca Ceresoli 		struct v4l2_subdev *src_subdev;
395*3bc46d75SLuca Ceresoli 
396*3bc46d75SLuca Ceresoli 		src_subdev = tegra_channel_get_remote_source_subdev(chan);
397*3bc46d75SLuca Ceresoli 		err = v4l2_subdev_call(src_subdev, video, s_stream, false);
398*3bc46d75SLuca Ceresoli 		if (err < 0 && err != -ENOIOCTLCMD)
399*3bc46d75SLuca Ceresoli 			dev_err_probe(csi->dev, err, "source subdev stream off failed\n");
400*3bc46d75SLuca Ceresoli 	}
401*3bc46d75SLuca Ceresoli 
402654c433bSSowjanya Komatineni 	csi->ops->csi_stop_streaming(csi_chan);
403654c433bSSowjanya Komatineni 
404523c857eSSowjanya Komatineni 	if (csi_chan->mipi) {
405523c857eSSowjanya Komatineni 		err = tegra_mipi_disable(csi_chan->mipi);
406523c857eSSowjanya Komatineni 		if (err < 0)
407523c857eSSowjanya Komatineni 			dev_err(csi->dev,
408523c857eSSowjanya Komatineni 				"failed to disable MIPI pads: %d\n", err);
409523c857eSSowjanya Komatineni 	}
410523c857eSSowjanya Komatineni 
411654c433bSSowjanya Komatineni 	pm_runtime_put(csi->dev);
412654c433bSSowjanya Komatineni 
413654c433bSSowjanya Komatineni 	return 0;
414654c433bSSowjanya Komatineni }
415654c433bSSowjanya Komatineni 
tegra_csi_s_stream(struct v4l2_subdev * subdev,int enable)416654c433bSSowjanya Komatineni static int tegra_csi_s_stream(struct v4l2_subdev *subdev, int enable)
417654c433bSSowjanya Komatineni {
418654c433bSSowjanya Komatineni 	int ret;
419654c433bSSowjanya Komatineni 
420654c433bSSowjanya Komatineni 	if (enable)
421654c433bSSowjanya Komatineni 		ret = tegra_csi_enable_stream(subdev);
422654c433bSSowjanya Komatineni 	else
423654c433bSSowjanya Komatineni 		ret = tegra_csi_disable_stream(subdev);
424654c433bSSowjanya Komatineni 
425654c433bSSowjanya Komatineni 	return ret;
426654c433bSSowjanya Komatineni }
427654c433bSSowjanya Komatineni 
4283d8a97eaSSowjanya Komatineni /*
4293d8a97eaSSowjanya Komatineni  * V4L2 Subdevice Operations
4303d8a97eaSSowjanya Komatineni  */
4313d8a97eaSSowjanya Komatineni static const struct v4l2_subdev_video_ops tegra_csi_video_ops = {
4323d8a97eaSSowjanya Komatineni 	.s_stream = tegra_csi_s_stream,
4333d8a97eaSSowjanya Komatineni 	.g_frame_interval = tegra_csi_g_frame_interval,
4343d8a97eaSSowjanya Komatineni 	.s_frame_interval = tegra_csi_g_frame_interval,
4353d8a97eaSSowjanya Komatineni };
4363d8a97eaSSowjanya Komatineni 
4373d8a97eaSSowjanya Komatineni static const struct v4l2_subdev_pad_ops tegra_csi_pad_ops = {
4383d8a97eaSSowjanya Komatineni 	.enum_mbus_code		= csi_enum_bus_code,
4393d8a97eaSSowjanya Komatineni 	.enum_frame_size	= csi_enum_framesizes,
4403d8a97eaSSowjanya Komatineni 	.enum_frame_interval	= csi_enum_frameintervals,
4413d8a97eaSSowjanya Komatineni 	.get_fmt		= csi_get_format,
4423d8a97eaSSowjanya Komatineni 	.set_fmt		= csi_set_format,
4433d8a97eaSSowjanya Komatineni };
4443d8a97eaSSowjanya Komatineni 
4453d8a97eaSSowjanya Komatineni static const struct v4l2_subdev_ops tegra_csi_ops = {
4463d8a97eaSSowjanya Komatineni 	.video  = &tegra_csi_video_ops,
4473d8a97eaSSowjanya Komatineni 	.pad    = &tegra_csi_pad_ops,
4483d8a97eaSSowjanya Komatineni };
4493d8a97eaSSowjanya Komatineni 
tegra_csi_channel_alloc(struct tegra_csi * csi,struct device_node * node,unsigned int port_num,unsigned int lanes,unsigned int num_pads)4501ebaeb09SSowjanya Komatineni static int tegra_csi_channel_alloc(struct tegra_csi *csi,
4511ebaeb09SSowjanya Komatineni 				   struct device_node *node,
4521ebaeb09SSowjanya Komatineni 				   unsigned int port_num, unsigned int lanes,
4531ebaeb09SSowjanya Komatineni 				   unsigned int num_pads)
4543d8a97eaSSowjanya Komatineni {
4553d8a97eaSSowjanya Komatineni 	struct tegra_csi_channel *chan;
4562ac4035aSSowjanya Komatineni 	int ret = 0, i;
4573d8a97eaSSowjanya Komatineni 
4583d8a97eaSSowjanya Komatineni 	chan = kzalloc(sizeof(*chan), GFP_KERNEL);
4593d8a97eaSSowjanya Komatineni 	if (!chan)
4603d8a97eaSSowjanya Komatineni 		return -ENOMEM;
4613d8a97eaSSowjanya Komatineni 
4623d8a97eaSSowjanya Komatineni 	list_add_tail(&chan->list, &csi->csi_chans);
4633d8a97eaSSowjanya Komatineni 	chan->csi = csi;
4642ac4035aSSowjanya Komatineni 	/*
4652ac4035aSSowjanya Komatineni 	 * Each CSI brick has maximum of 4 lanes.
4662ac4035aSSowjanya Komatineni 	 * For lanes more than 4, use multiple of immediate CSI bricks as gang.
4672ac4035aSSowjanya Komatineni 	 */
4682ac4035aSSowjanya Komatineni 	if (lanes <= CSI_LANES_PER_BRICK) {
4691ebaeb09SSowjanya Komatineni 		chan->numlanes = lanes;
4702ac4035aSSowjanya Komatineni 		chan->numgangports = 1;
4712ac4035aSSowjanya Komatineni 	} else {
4722ac4035aSSowjanya Komatineni 		chan->numlanes = CSI_LANES_PER_BRICK;
4732ac4035aSSowjanya Komatineni 		chan->numgangports = lanes / CSI_LANES_PER_BRICK;
4742ac4035aSSowjanya Komatineni 	}
4752ac4035aSSowjanya Komatineni 
4762ac4035aSSowjanya Komatineni 	for (i = 0; i < chan->numgangports; i++)
4772ac4035aSSowjanya Komatineni 		chan->csi_port_nums[i] = port_num + i * CSI_PORTS_PER_BRICK;
4782ac4035aSSowjanya Komatineni 
479c4d34416SLuca Ceresoli 	chan->of_node = of_node_get(node);
4801ebaeb09SSowjanya Komatineni 	chan->numpads = num_pads;
4811ebaeb09SSowjanya Komatineni 	if (num_pads & 0x2) {
4821ebaeb09SSowjanya Komatineni 		chan->pads[0].flags = MEDIA_PAD_FL_SINK;
4831ebaeb09SSowjanya Komatineni 		chan->pads[1].flags = MEDIA_PAD_FL_SOURCE;
4841ebaeb09SSowjanya Komatineni 	} else {
4853d8a97eaSSowjanya Komatineni 		chan->pads[0].flags = MEDIA_PAD_FL_SOURCE;
4863d8a97eaSSowjanya Komatineni 	}
4873d8a97eaSSowjanya Komatineni 
488523c857eSSowjanya Komatineni 	if (IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG))
4893d8a97eaSSowjanya Komatineni 		return 0;
490523c857eSSowjanya Komatineni 
491523c857eSSowjanya Komatineni 	chan->mipi = tegra_mipi_request(csi->dev, node);
492523c857eSSowjanya Komatineni 	if (IS_ERR(chan->mipi)) {
493523c857eSSowjanya Komatineni 		ret = PTR_ERR(chan->mipi);
49410b5ce67SLuca Ceresoli 		chan->mipi = NULL;
495523c857eSSowjanya Komatineni 		dev_err(csi->dev, "failed to get mipi device: %d\n", ret);
496523c857eSSowjanya Komatineni 	}
497523c857eSSowjanya Komatineni 
498523c857eSSowjanya Komatineni 	return ret;
4993d8a97eaSSowjanya Komatineni }
5003d8a97eaSSowjanya Komatineni 
tegra_csi_tpg_channels_alloc(struct tegra_csi * csi)5011ebaeb09SSowjanya Komatineni static int tegra_csi_tpg_channels_alloc(struct tegra_csi *csi)
5021ebaeb09SSowjanya Komatineni {
5031ebaeb09SSowjanya Komatineni 	struct device_node *node = csi->dev->of_node;
5041ebaeb09SSowjanya Komatineni 	unsigned int port_num;
5051ebaeb09SSowjanya Komatineni 	unsigned int tpg_channels = csi->soc->csi_max_channels;
5061ebaeb09SSowjanya Komatineni 	int ret;
5071ebaeb09SSowjanya Komatineni 
5081ebaeb09SSowjanya Komatineni 	/* allocate CSI channel for each CSI x2 ports */
5091ebaeb09SSowjanya Komatineni 	for (port_num = 0; port_num < tpg_channels; port_num++) {
5101ebaeb09SSowjanya Komatineni 		ret = tegra_csi_channel_alloc(csi, node, port_num, 2, 1);
5111ebaeb09SSowjanya Komatineni 		if (ret < 0)
5121ebaeb09SSowjanya Komatineni 			return ret;
5131ebaeb09SSowjanya Komatineni 	}
5141ebaeb09SSowjanya Komatineni 
5151ebaeb09SSowjanya Komatineni 	return 0;
5161ebaeb09SSowjanya Komatineni }
5171ebaeb09SSowjanya Komatineni 
tegra_csi_channels_alloc(struct tegra_csi * csi)5181ebaeb09SSowjanya Komatineni static int tegra_csi_channels_alloc(struct tegra_csi *csi)
5191ebaeb09SSowjanya Komatineni {
5201ebaeb09SSowjanya Komatineni 	struct device_node *node = csi->dev->of_node;
5211ebaeb09SSowjanya Komatineni 	struct v4l2_fwnode_endpoint v4l2_ep = {
5221ebaeb09SSowjanya Komatineni 		.bus_type = V4L2_MBUS_CSI2_DPHY
5231ebaeb09SSowjanya Komatineni 	};
5241ebaeb09SSowjanya Komatineni 	struct fwnode_handle *fwh;
5251ebaeb09SSowjanya Komatineni 	struct device_node *channel;
5261ebaeb09SSowjanya Komatineni 	struct device_node *ep;
5271ebaeb09SSowjanya Komatineni 	unsigned int lanes, portno, num_pads;
5281ebaeb09SSowjanya Komatineni 	int ret;
5291ebaeb09SSowjanya Komatineni 
5301ebaeb09SSowjanya Komatineni 	for_each_child_of_node(node, channel) {
5311ebaeb09SSowjanya Komatineni 		if (!of_node_name_eq(channel, "channel"))
5321ebaeb09SSowjanya Komatineni 			continue;
5331ebaeb09SSowjanya Komatineni 
5341ebaeb09SSowjanya Komatineni 		ret = of_property_read_u32(channel, "reg", &portno);
5351ebaeb09SSowjanya Komatineni 		if (ret < 0)
5361ebaeb09SSowjanya Komatineni 			continue;
5371ebaeb09SSowjanya Komatineni 
5381ebaeb09SSowjanya Komatineni 		if (portno >= csi->soc->csi_max_channels) {
5391ebaeb09SSowjanya Komatineni 			dev_err(csi->dev, "invalid port num %d for %pOF\n",
5401ebaeb09SSowjanya Komatineni 				portno, channel);
5411ebaeb09SSowjanya Komatineni 			ret = -EINVAL;
5421ebaeb09SSowjanya Komatineni 			goto err_node_put;
5431ebaeb09SSowjanya Komatineni 		}
5441ebaeb09SSowjanya Komatineni 
5451ebaeb09SSowjanya Komatineni 		ep = of_graph_get_endpoint_by_regs(channel, 0, 0);
5461ebaeb09SSowjanya Komatineni 		if (!ep)
5471ebaeb09SSowjanya Komatineni 			continue;
5481ebaeb09SSowjanya Komatineni 
5491ebaeb09SSowjanya Komatineni 		fwh = of_fwnode_handle(ep);
5501ebaeb09SSowjanya Komatineni 		ret = v4l2_fwnode_endpoint_parse(fwh, &v4l2_ep);
5511ebaeb09SSowjanya Komatineni 		of_node_put(ep);
5521ebaeb09SSowjanya Komatineni 		if (ret) {
5531ebaeb09SSowjanya Komatineni 			dev_err(csi->dev,
5541ebaeb09SSowjanya Komatineni 				"failed to parse v4l2 endpoint for %pOF: %d\n",
5551ebaeb09SSowjanya Komatineni 				channel, ret);
5561ebaeb09SSowjanya Komatineni 			goto err_node_put;
5571ebaeb09SSowjanya Komatineni 		}
5581ebaeb09SSowjanya Komatineni 
5591ebaeb09SSowjanya Komatineni 		lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes;
5602ac4035aSSowjanya Komatineni 		/*
5612ac4035aSSowjanya Komatineni 		 * Each CSI brick has maximum 4 data lanes.
5622ac4035aSSowjanya Komatineni 		 * For lanes more than 4, validate lanes to be multiple of 4
5632ac4035aSSowjanya Komatineni 		 * so multiple of consecutive CSI bricks can be ganged up for
5642ac4035aSSowjanya Komatineni 		 * streaming.
5652ac4035aSSowjanya Komatineni 		 */
5662ac4035aSSowjanya Komatineni 		if (!lanes || ((lanes & (lanes - 1)) != 0) ||
5672ac4035aSSowjanya Komatineni 		    (lanes > CSI_LANES_PER_BRICK && ((portno & 1) != 0))) {
5681ebaeb09SSowjanya Komatineni 			dev_err(csi->dev, "invalid data-lanes %d for %pOF\n",
5691ebaeb09SSowjanya Komatineni 				lanes, channel);
5701ebaeb09SSowjanya Komatineni 			ret = -EINVAL;
5711ebaeb09SSowjanya Komatineni 			goto err_node_put;
5721ebaeb09SSowjanya Komatineni 		}
5731ebaeb09SSowjanya Komatineni 
5741ebaeb09SSowjanya Komatineni 		num_pads = of_graph_get_endpoint_count(channel);
5751ebaeb09SSowjanya Komatineni 		if (num_pads == TEGRA_CSI_PADS_NUM) {
5761ebaeb09SSowjanya Komatineni 			ret = tegra_csi_channel_alloc(csi, channel, portno,
5771ebaeb09SSowjanya Komatineni 						      lanes, num_pads);
5781ebaeb09SSowjanya Komatineni 			if (ret < 0)
5791ebaeb09SSowjanya Komatineni 				goto err_node_put;
5801ebaeb09SSowjanya Komatineni 		}
5811ebaeb09SSowjanya Komatineni 	}
5821ebaeb09SSowjanya Komatineni 
5831ebaeb09SSowjanya Komatineni 	return 0;
5841ebaeb09SSowjanya Komatineni 
5851ebaeb09SSowjanya Komatineni err_node_put:
5861ebaeb09SSowjanya Komatineni 	of_node_put(channel);
5871ebaeb09SSowjanya Komatineni 	return ret;
5881ebaeb09SSowjanya Komatineni }
5891ebaeb09SSowjanya Komatineni 
tegra_csi_channel_init(struct tegra_csi_channel * chan)5903d8a97eaSSowjanya Komatineni static int tegra_csi_channel_init(struct tegra_csi_channel *chan)
5913d8a97eaSSowjanya Komatineni {
5923d8a97eaSSowjanya Komatineni 	struct tegra_csi *csi = chan->csi;
5933d8a97eaSSowjanya Komatineni 	struct v4l2_subdev *subdev;
5943d8a97eaSSowjanya Komatineni 	int ret;
5953d8a97eaSSowjanya Komatineni 
5963d8a97eaSSowjanya Komatineni 	/* initialize the default format */
5973d8a97eaSSowjanya Komatineni 	chan->format.code = MEDIA_BUS_FMT_SRGGB10_1X10;
5983d8a97eaSSowjanya Komatineni 	chan->format.field = V4L2_FIELD_NONE;
5993d8a97eaSSowjanya Komatineni 	chan->format.colorspace = V4L2_COLORSPACE_SRGB;
6003d8a97eaSSowjanya Komatineni 	chan->format.width = TEGRA_DEF_WIDTH;
6013d8a97eaSSowjanya Komatineni 	chan->format.height = TEGRA_DEF_HEIGHT;
6023d8a97eaSSowjanya Komatineni 	csi_chan_update_blank_intervals(chan, chan->format.code,
6033d8a97eaSSowjanya Komatineni 					chan->format.width,
6043d8a97eaSSowjanya Komatineni 					chan->format.height);
6053d8a97eaSSowjanya Komatineni 	/* initialize V4L2 subdevice and media entity */
6063d8a97eaSSowjanya Komatineni 	subdev = &chan->subdev;
6073d8a97eaSSowjanya Komatineni 	v4l2_subdev_init(subdev, &tegra_csi_ops);
6083d8a97eaSSowjanya Komatineni 	subdev->dev = csi->dev;
609341187bfSSowjanya Komatineni 	if (IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG))
6103d8a97eaSSowjanya Komatineni 		snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "%s-%d", "tpg",
6112ac4035aSSowjanya Komatineni 			 chan->csi_port_nums[0]);
612341187bfSSowjanya Komatineni 	else
613341187bfSSowjanya Komatineni 		snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "%s",
614341187bfSSowjanya Komatineni 			 kbasename(chan->of_node->full_name));
6153d8a97eaSSowjanya Komatineni 
6163d8a97eaSSowjanya Komatineni 	v4l2_set_subdevdata(subdev, chan);
6173d8a97eaSSowjanya Komatineni 	subdev->fwnode = of_fwnode_handle(chan->of_node);
6183d8a97eaSSowjanya Komatineni 	subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
6193d8a97eaSSowjanya Komatineni 
6203d8a97eaSSowjanya Komatineni 	/* initialize media entity pads */
6213d8a97eaSSowjanya Komatineni 	ret = media_entity_pads_init(&subdev->entity, chan->numpads,
6223d8a97eaSSowjanya Komatineni 				     chan->pads);
6233d8a97eaSSowjanya Komatineni 	if (ret < 0) {
6243d8a97eaSSowjanya Komatineni 		dev_err(csi->dev,
6253d8a97eaSSowjanya Komatineni 			"failed to initialize media entity: %d\n", ret);
6263d8a97eaSSowjanya Komatineni 		subdev->dev = NULL;
6273d8a97eaSSowjanya Komatineni 		return ret;
6283d8a97eaSSowjanya Komatineni 	}
6293d8a97eaSSowjanya Komatineni 
6301ebaeb09SSowjanya Komatineni 	if (!IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG)) {
6311ebaeb09SSowjanya Komatineni 		ret = v4l2_async_register_subdev(subdev);
6321ebaeb09SSowjanya Komatineni 		if (ret < 0) {
6331ebaeb09SSowjanya Komatineni 			dev_err(csi->dev,
6341ebaeb09SSowjanya Komatineni 				"failed to register subdev: %d\n", ret);
6351ebaeb09SSowjanya Komatineni 			return ret;
6361ebaeb09SSowjanya Komatineni 		}
6371ebaeb09SSowjanya Komatineni 	}
6381ebaeb09SSowjanya Komatineni 
6393d8a97eaSSowjanya Komatineni 	return 0;
6403d8a97eaSSowjanya Komatineni }
6413d8a97eaSSowjanya Komatineni 
tegra_csi_error_recover(struct v4l2_subdev * sd)6423d8a97eaSSowjanya Komatineni void tegra_csi_error_recover(struct v4l2_subdev *sd)
6433d8a97eaSSowjanya Komatineni {
6443d8a97eaSSowjanya Komatineni 	struct tegra_csi_channel *csi_chan = to_csi_chan(sd);
6453d8a97eaSSowjanya Komatineni 	struct tegra_csi *csi = csi_chan->csi;
6463d8a97eaSSowjanya Komatineni 
6473d8a97eaSSowjanya Komatineni 	/* stop streaming during error recovery */
6483d8a97eaSSowjanya Komatineni 	csi->ops->csi_stop_streaming(csi_chan);
6493d8a97eaSSowjanya Komatineni 	csi->ops->csi_err_recover(csi_chan);
6503d8a97eaSSowjanya Komatineni 	csi->ops->csi_start_streaming(csi_chan);
6513d8a97eaSSowjanya Komatineni }
6523d8a97eaSSowjanya Komatineni 
tegra_csi_channels_init(struct tegra_csi * csi)6533d8a97eaSSowjanya Komatineni static int tegra_csi_channels_init(struct tegra_csi *csi)
6543d8a97eaSSowjanya Komatineni {
6553d8a97eaSSowjanya Komatineni 	struct tegra_csi_channel *chan;
6563d8a97eaSSowjanya Komatineni 	int ret;
6573d8a97eaSSowjanya Komatineni 
6583d8a97eaSSowjanya Komatineni 	list_for_each_entry(chan, &csi->csi_chans, list) {
6593d8a97eaSSowjanya Komatineni 		ret = tegra_csi_channel_init(chan);
6603d8a97eaSSowjanya Komatineni 		if (ret) {
6613d8a97eaSSowjanya Komatineni 			dev_err(csi->dev,
6623d8a97eaSSowjanya Komatineni 				"failed to initialize channel-%d: %d\n",
6632ac4035aSSowjanya Komatineni 				chan->csi_port_nums[0], ret);
6643d8a97eaSSowjanya Komatineni 			return ret;
6653d8a97eaSSowjanya Komatineni 		}
6663d8a97eaSSowjanya Komatineni 	}
6673d8a97eaSSowjanya Komatineni 
6683d8a97eaSSowjanya Komatineni 	return 0;
6693d8a97eaSSowjanya Komatineni }
6703d8a97eaSSowjanya Komatineni 
tegra_csi_channels_cleanup(struct tegra_csi * csi)6713d8a97eaSSowjanya Komatineni static void tegra_csi_channels_cleanup(struct tegra_csi *csi)
6723d8a97eaSSowjanya Komatineni {
6733d8a97eaSSowjanya Komatineni 	struct v4l2_subdev *subdev;
6743d8a97eaSSowjanya Komatineni 	struct tegra_csi_channel *chan, *tmp;
6753d8a97eaSSowjanya Komatineni 
6763d8a97eaSSowjanya Komatineni 	list_for_each_entry_safe(chan, tmp, &csi->csi_chans, list) {
677523c857eSSowjanya Komatineni 		if (chan->mipi)
678523c857eSSowjanya Komatineni 			tegra_mipi_free(chan->mipi);
679523c857eSSowjanya Komatineni 
6803d8a97eaSSowjanya Komatineni 		subdev = &chan->subdev;
6811ebaeb09SSowjanya Komatineni 		if (subdev->dev) {
6821ebaeb09SSowjanya Komatineni 			if (!IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG))
6831ebaeb09SSowjanya Komatineni 				v4l2_async_unregister_subdev(subdev);
6843d8a97eaSSowjanya Komatineni 			media_entity_cleanup(&subdev->entity);
6851ebaeb09SSowjanya Komatineni 		}
6861ebaeb09SSowjanya Komatineni 
687c4d34416SLuca Ceresoli 		of_node_put(chan->of_node);
6883d8a97eaSSowjanya Komatineni 		list_del(&chan->list);
6893d8a97eaSSowjanya Komatineni 		kfree(chan);
6903d8a97eaSSowjanya Komatineni 	}
6913d8a97eaSSowjanya Komatineni }
6923d8a97eaSSowjanya Komatineni 
csi_runtime_suspend(struct device * dev)6933d8a97eaSSowjanya Komatineni static int __maybe_unused csi_runtime_suspend(struct device *dev)
6943d8a97eaSSowjanya Komatineni {
6953d8a97eaSSowjanya Komatineni 	struct tegra_csi *csi = dev_get_drvdata(dev);
6963d8a97eaSSowjanya Komatineni 
6973d8a97eaSSowjanya Komatineni 	clk_bulk_disable_unprepare(csi->soc->num_clks, csi->clks);
6983d8a97eaSSowjanya Komatineni 
6993d8a97eaSSowjanya Komatineni 	return 0;
7003d8a97eaSSowjanya Komatineni }
7013d8a97eaSSowjanya Komatineni 
csi_runtime_resume(struct device * dev)7023d8a97eaSSowjanya Komatineni static int __maybe_unused csi_runtime_resume(struct device *dev)
7033d8a97eaSSowjanya Komatineni {
7043d8a97eaSSowjanya Komatineni 	struct tegra_csi *csi = dev_get_drvdata(dev);
7053d8a97eaSSowjanya Komatineni 	int ret;
7063d8a97eaSSowjanya Komatineni 
7073d8a97eaSSowjanya Komatineni 	ret = clk_bulk_prepare_enable(csi->soc->num_clks, csi->clks);
7083d8a97eaSSowjanya Komatineni 	if (ret < 0) {
7093d8a97eaSSowjanya Komatineni 		dev_err(csi->dev, "failed to enable clocks: %d\n", ret);
7103d8a97eaSSowjanya Komatineni 		return ret;
7113d8a97eaSSowjanya Komatineni 	}
7123d8a97eaSSowjanya Komatineni 
7133d8a97eaSSowjanya Komatineni 	return 0;
7143d8a97eaSSowjanya Komatineni }
7153d8a97eaSSowjanya Komatineni 
tegra_csi_init(struct host1x_client * client)7163d8a97eaSSowjanya Komatineni static int tegra_csi_init(struct host1x_client *client)
7173d8a97eaSSowjanya Komatineni {
7183d8a97eaSSowjanya Komatineni 	struct tegra_csi *csi = host1x_client_to_csi(client);
7193d8a97eaSSowjanya Komatineni 	struct tegra_video_device *vid = dev_get_drvdata(client->host);
7203d8a97eaSSowjanya Komatineni 	int ret;
7213d8a97eaSSowjanya Komatineni 
7223d8a97eaSSowjanya Komatineni 	INIT_LIST_HEAD(&csi->csi_chans);
7233d8a97eaSSowjanya Komatineni 
7241ebaeb09SSowjanya Komatineni 	if (IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG))
7253d8a97eaSSowjanya Komatineni 		ret = tegra_csi_tpg_channels_alloc(csi);
7261ebaeb09SSowjanya Komatineni 	else
7271ebaeb09SSowjanya Komatineni 		ret = tegra_csi_channels_alloc(csi);
7283d8a97eaSSowjanya Komatineni 	if (ret < 0) {
7293d8a97eaSSowjanya Komatineni 		dev_err(csi->dev,
7301ebaeb09SSowjanya Komatineni 			"failed to allocate channels: %d\n", ret);
7313d8a97eaSSowjanya Komatineni 		goto cleanup;
7323d8a97eaSSowjanya Komatineni 	}
7333d8a97eaSSowjanya Komatineni 
7343d8a97eaSSowjanya Komatineni 	ret = tegra_csi_channels_init(csi);
7353d8a97eaSSowjanya Komatineni 	if (ret < 0)
7363d8a97eaSSowjanya Komatineni 		goto cleanup;
7373d8a97eaSSowjanya Komatineni 
7383d8a97eaSSowjanya Komatineni 	vid->csi = csi;
7393d8a97eaSSowjanya Komatineni 
7403d8a97eaSSowjanya Komatineni 	return 0;
7413d8a97eaSSowjanya Komatineni 
7423d8a97eaSSowjanya Komatineni cleanup:
7433d8a97eaSSowjanya Komatineni 	tegra_csi_channels_cleanup(csi);
7443d8a97eaSSowjanya Komatineni 	return ret;
7453d8a97eaSSowjanya Komatineni }
7463d8a97eaSSowjanya Komatineni 
tegra_csi_exit(struct host1x_client * client)7473d8a97eaSSowjanya Komatineni static int tegra_csi_exit(struct host1x_client *client)
7483d8a97eaSSowjanya Komatineni {
7493d8a97eaSSowjanya Komatineni 	struct tegra_csi *csi = host1x_client_to_csi(client);
7503d8a97eaSSowjanya Komatineni 
7513d8a97eaSSowjanya Komatineni 	tegra_csi_channels_cleanup(csi);
7523d8a97eaSSowjanya Komatineni 
7533d8a97eaSSowjanya Komatineni 	return 0;
7543d8a97eaSSowjanya Komatineni }
7553d8a97eaSSowjanya Komatineni 
7563d8a97eaSSowjanya Komatineni static const struct host1x_client_ops csi_client_ops = {
7573d8a97eaSSowjanya Komatineni 	.init = tegra_csi_init,
7583d8a97eaSSowjanya Komatineni 	.exit = tegra_csi_exit,
7593d8a97eaSSowjanya Komatineni };
7603d8a97eaSSowjanya Komatineni 
tegra_csi_probe(struct platform_device * pdev)7613d8a97eaSSowjanya Komatineni static int tegra_csi_probe(struct platform_device *pdev)
7623d8a97eaSSowjanya Komatineni {
7633d8a97eaSSowjanya Komatineni 	struct tegra_csi *csi;
7643d8a97eaSSowjanya Komatineni 	unsigned int i;
7653d8a97eaSSowjanya Komatineni 	int ret;
7663d8a97eaSSowjanya Komatineni 
7673d8a97eaSSowjanya Komatineni 	csi = devm_kzalloc(&pdev->dev, sizeof(*csi), GFP_KERNEL);
7683d8a97eaSSowjanya Komatineni 	if (!csi)
7693d8a97eaSSowjanya Komatineni 		return -ENOMEM;
7703d8a97eaSSowjanya Komatineni 
7713d8a97eaSSowjanya Komatineni 	csi->iomem = devm_platform_ioremap_resource(pdev, 0);
7723d8a97eaSSowjanya Komatineni 	if (IS_ERR(csi->iomem))
7733d8a97eaSSowjanya Komatineni 		return PTR_ERR(csi->iomem);
7743d8a97eaSSowjanya Komatineni 
7753d8a97eaSSowjanya Komatineni 	csi->soc = of_device_get_match_data(&pdev->dev);
7763d8a97eaSSowjanya Komatineni 
7773d8a97eaSSowjanya Komatineni 	csi->clks = devm_kcalloc(&pdev->dev, csi->soc->num_clks,
7783d8a97eaSSowjanya Komatineni 				 sizeof(*csi->clks), GFP_KERNEL);
7793d8a97eaSSowjanya Komatineni 	if (!csi->clks)
7803d8a97eaSSowjanya Komatineni 		return -ENOMEM;
7813d8a97eaSSowjanya Komatineni 
7823d8a97eaSSowjanya Komatineni 	for (i = 0; i < csi->soc->num_clks; i++)
7833d8a97eaSSowjanya Komatineni 		csi->clks[i].id = csi->soc->clk_names[i];
7843d8a97eaSSowjanya Komatineni 
7853d8a97eaSSowjanya Komatineni 	ret = devm_clk_bulk_get(&pdev->dev, csi->soc->num_clks, csi->clks);
7863d8a97eaSSowjanya Komatineni 	if (ret) {
7873d8a97eaSSowjanya Komatineni 		dev_err(&pdev->dev, "failed to get the clocks: %d\n", ret);
7883d8a97eaSSowjanya Komatineni 		return ret;
7893d8a97eaSSowjanya Komatineni 	}
7903d8a97eaSSowjanya Komatineni 
7913d8a97eaSSowjanya Komatineni 	if (!pdev->dev.pm_domain) {
7923d8a97eaSSowjanya Komatineni 		ret = -ENOENT;
7933d8a97eaSSowjanya Komatineni 		dev_warn(&pdev->dev, "PM domain is not attached: %d\n", ret);
7943d8a97eaSSowjanya Komatineni 		return ret;
7953d8a97eaSSowjanya Komatineni 	}
7963d8a97eaSSowjanya Komatineni 
7973d8a97eaSSowjanya Komatineni 	csi->dev = &pdev->dev;
7983d8a97eaSSowjanya Komatineni 	csi->ops = csi->soc->ops;
7993d8a97eaSSowjanya Komatineni 	platform_set_drvdata(pdev, csi);
8003d8a97eaSSowjanya Komatineni 	pm_runtime_enable(&pdev->dev);
8013d8a97eaSSowjanya Komatineni 
8023d8a97eaSSowjanya Komatineni 	/* initialize host1x interface */
8033d8a97eaSSowjanya Komatineni 	INIT_LIST_HEAD(&csi->client.list);
8043d8a97eaSSowjanya Komatineni 	csi->client.ops = &csi_client_ops;
8053d8a97eaSSowjanya Komatineni 	csi->client.dev = &pdev->dev;
8063d8a97eaSSowjanya Komatineni 
8073d8a97eaSSowjanya Komatineni 	ret = host1x_client_register(&csi->client);
8083d8a97eaSSowjanya Komatineni 	if (ret < 0) {
8093d8a97eaSSowjanya Komatineni 		dev_err(&pdev->dev,
8103d8a97eaSSowjanya Komatineni 			"failed to register host1x client: %d\n", ret);
8113d8a97eaSSowjanya Komatineni 		goto rpm_disable;
8123d8a97eaSSowjanya Komatineni 	}
8133d8a97eaSSowjanya Komatineni 
8143d8a97eaSSowjanya Komatineni 	return 0;
8153d8a97eaSSowjanya Komatineni 
8163d8a97eaSSowjanya Komatineni rpm_disable:
8173d8a97eaSSowjanya Komatineni 	pm_runtime_disable(&pdev->dev);
8183d8a97eaSSowjanya Komatineni 	return ret;
8193d8a97eaSSowjanya Komatineni }
8203d8a97eaSSowjanya Komatineni 
tegra_csi_remove(struct platform_device * pdev)8213d8a97eaSSowjanya Komatineni static int tegra_csi_remove(struct platform_device *pdev)
8223d8a97eaSSowjanya Komatineni {
8233d8a97eaSSowjanya Komatineni 	struct tegra_csi *csi = platform_get_drvdata(pdev);
8243d8a97eaSSowjanya Komatineni 
8251d83d1a2SUwe Kleine-König 	host1x_client_unregister(&csi->client);
8263d8a97eaSSowjanya Komatineni 
8273d8a97eaSSowjanya Komatineni 	pm_runtime_disable(&pdev->dev);
8283d8a97eaSSowjanya Komatineni 
8293d8a97eaSSowjanya Komatineni 	return 0;
8303d8a97eaSSowjanya Komatineni }
8313d8a97eaSSowjanya Komatineni 
83244977bdcSLuca Ceresoli #if defined(CONFIG_ARCH_TEGRA_210_SOC)
83344977bdcSLuca Ceresoli extern const struct tegra_csi_soc tegra210_csi_soc;
83444977bdcSLuca Ceresoli #endif
83544977bdcSLuca Ceresoli 
8363d8a97eaSSowjanya Komatineni static const struct of_device_id tegra_csi_of_id_table[] = {
8373d8a97eaSSowjanya Komatineni #if defined(CONFIG_ARCH_TEGRA_210_SOC)
8383d8a97eaSSowjanya Komatineni 	{ .compatible = "nvidia,tegra210-csi", .data = &tegra210_csi_soc },
8393d8a97eaSSowjanya Komatineni #endif
8403d8a97eaSSowjanya Komatineni 	{ }
8413d8a97eaSSowjanya Komatineni };
8423d8a97eaSSowjanya Komatineni MODULE_DEVICE_TABLE(of, tegra_csi_of_id_table);
8433d8a97eaSSowjanya Komatineni 
8443d8a97eaSSowjanya Komatineni static const struct dev_pm_ops tegra_csi_pm_ops = {
8453d8a97eaSSowjanya Komatineni 	SET_RUNTIME_PM_OPS(csi_runtime_suspend, csi_runtime_resume, NULL)
8463d8a97eaSSowjanya Komatineni };
8473d8a97eaSSowjanya Komatineni 
8483d8a97eaSSowjanya Komatineni struct platform_driver tegra_csi_driver = {
8493d8a97eaSSowjanya Komatineni 	.driver = {
8503d8a97eaSSowjanya Komatineni 		.name		= "tegra-csi",
8513d8a97eaSSowjanya Komatineni 		.of_match_table	= tegra_csi_of_id_table,
8523d8a97eaSSowjanya Komatineni 		.pm		= &tegra_csi_pm_ops,
8533d8a97eaSSowjanya Komatineni 	},
8543d8a97eaSSowjanya Komatineni 	.probe			= tegra_csi_probe,
8553d8a97eaSSowjanya Komatineni 	.remove			= tegra_csi_remove,
8563d8a97eaSSowjanya Komatineni };
857