lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Date:   Mon, 9 Nov 2020 08:22:30 -0800
From:   Sowjanya Komatineni <skomatineni@...dia.com>
To:     Hans Verkuil <hverkuil@...all.nl>, <thierry.reding@...il.com>,
        <jonathanh@...dia.com>
CC:     <mchehab@...nel.org>, <linux-media@...r.kernel.org>,
        <linux-tegra@...r.kernel.org>, <linux-kernel@...r.kernel.org>
Subject: Re: [PATCH v2 10/10] media: tegra-video: Add support for x8 captures
 with gang ports

Thanks Hans. Will address all your below feedback in v3.

Regards,

Sowjanya

On 11/9/20 5:04 AM, Hans Verkuil wrote:
> Hi Sowjanya,
>
> On 27/10/2020 04:19, Sowjanya Komatineni wrote:
>> Tegra VI/CSI hardware don't have native 8 lane capture support.
>>
>> Each CSI port has max 4 lanes only. So for x8 captures, consecutive
>> ports are ganged up for left half and right half captures on to each
>> x4 ports with buffer offsets based on source image split width to align
>> side-by-side.
>>
>> All ports in gang are configured together during the corresponding
>> video device node streaming for x8 captures.
>>
>> x8 capture with gang ports are supported with HDMI-to-CSI bridges
>> where they split 4K image into left half onto one x4 port and
>> right half onto second x4 port.
>>
>> Signed-off-by: Sowjanya Komatineni <skomatineni@...dia.com>
>> ---
>>   drivers/staging/media/tegra-video/csi.c      |  47 ++++-
>>   drivers/staging/media/tegra-video/csi.h      |  14 +-
>>   drivers/staging/media/tegra-video/tegra210.c | 304 ++++++++++++++++++---------
>>   drivers/staging/media/tegra-video/vi.c       | 117 ++++++++---
>>   drivers/staging/media/tegra-video/vi.h       |  17 +-
>>   5 files changed, 347 insertions(+), 152 deletions(-)
>>
>> diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c
>> index a19c85c..a6c54f8 100644
>> --- a/drivers/staging/media/tegra-video/csi.c
>> +++ b/drivers/staging/media/tegra-video/csi.c
>> @@ -253,13 +253,14 @@ static unsigned int csi_get_pixel_rate(struct tegra_csi_channel *csi_chan)
>>   }
>>   
>>   void tegra_csi_calc_settle_time(struct tegra_csi_channel *csi_chan,
>> +				u8 csi_port_num,
>>   				u8 *clk_settle_time,
>>   				u8 *ths_settle_time)
>>   {
>>   	struct tegra_csi *csi = csi_chan->csi;
>>   	unsigned int cil_clk_mhz;
>>   	unsigned int pix_clk_mhz;
>> -	int clk_idx = (csi_chan->csi_port_num >> 1) + 1;
>> +	int clk_idx = (csi_port_num >> 1) + 1;
>>   
>>   	cil_clk_mhz = clk_get_rate(csi->clks[clk_idx].clk) / MHZ;
>>   	pix_clk_mhz = csi_get_pixel_rate(csi_chan) / MHZ;
>> @@ -410,7 +411,7 @@ static int tegra_csi_channel_alloc(struct tegra_csi *csi,
>>   				   unsigned int num_pads)
>>   {
>>   	struct tegra_csi_channel *chan;
>> -	int ret = 0;
>> +	int ret = 0, i;
>>   
>>   	chan = kzalloc(sizeof(*chan), GFP_KERNEL);
>>   	if (!chan)
>> @@ -418,8 +419,21 @@ static int tegra_csi_channel_alloc(struct tegra_csi *csi,
>>   
>>   	list_add_tail(&chan->list, &csi->csi_chans);
>>   	chan->csi = csi;
>> -	chan->csi_port_num = port_num;
>> -	chan->numlanes = lanes;
>> +	/*
>> +	 * Each CSI brick has maximum of 4 lanes.
>> +	 * For lanes more than 4, use multiple of immediate CSI bricks as gang.
>> +	 */
>> +	if (lanes <= CSI_LANES_PER_BRICK) {
>> +		chan->numlanes = lanes;
>> +		chan->numgangports = 1;
>> +	} else {
>> +		chan->numlanes = CSI_LANES_PER_BRICK;
>> +		chan->numgangports = lanes / CSI_LANES_PER_BRICK;
>> +	}
>> +
>> +	for (i = 0; i < chan->numgangports; i++)
>> +		chan->csi_port_nums[i] = port_num + i * CSI_PORTS_PER_BRICK;
>> +
>>   	chan->of_node = node;
>>   	chan->numpads = num_pads;
>>   	if (num_pads & 0x2) {
>> @@ -461,9 +475,6 @@ static int tegra_csi_tpg_channels_alloc(struct tegra_csi *csi)
>>   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;
>> @@ -471,6 +482,8 @@ static int tegra_csi_channels_alloc(struct tegra_csi *csi)
>>   	int ret;
>>   
>>   	for_each_child_of_node(node, channel) {
>> +		struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = 0 };
>> +
>>   		if (!of_node_name_eq(channel, "channel"))
>>   			continue;
>>   
>> @@ -499,8 +512,20 @@ static int tegra_csi_channels_alloc(struct tegra_csi *csi)
>>   			goto err_node_put;
>>   		}
>>   
>> -		lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes;
>> -		if (!lanes || ((lanes & (lanes - 1)) != 0)) {
>> +		if (v4l2_ep.bus_type == V4L2_MBUS_PARALLEL)
>> +			lanes = v4l2_ep.bus.parallel.bus_width;
>> +		else
>> +			lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes;
>> +
>> +		/*
>> +		 * Each CSI brick has maximum 4 data lanes.
>> +		 * For lanes more than 4, validate lanes to be multiple of 4
>> +		 * so multiple of consecutive CSI bricks can be ganged up for
>> +		 * streaming.
>> +		 */
>> +		if (!lanes || ((lanes & (lanes - 1)) != 0) ||
>> +		    (lanes > CSI_LANES_PER_BRICK && (((lanes & 3) != 0) ||
>> +		    ((portno & 1) != 0)))) {
>>   			dev_err(csi->dev, "invalid data-lanes %d for %pOF\n",
>>   				lanes, channel);
>>   			ret = -EINVAL;
>> @@ -544,7 +569,7 @@ static int tegra_csi_channel_init(struct tegra_csi_channel *chan)
>>   	subdev->dev = csi->dev;
>>   	if (IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG))
>>   		snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "%s-%d", "tpg",
>> -			 chan->csi_port_num);
>> +			 chan->csi_port_nums[0]);
>>   	else
>>   		snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "%s",
>>   			 kbasename(chan->of_node->full_name));
>> @@ -596,7 +621,7 @@ static int tegra_csi_channels_init(struct tegra_csi *csi)
>>   		if (ret) {
>>   			dev_err(csi->dev,
>>   				"failed to initialize channel-%d: %d\n",
>> -				chan->csi_port_num, ret);
>> +				chan->csi_port_nums[0], ret);
>>   			return ret;
>>   		}
>>   	}
>> diff --git a/drivers/staging/media/tegra-video/csi.h b/drivers/staging/media/tegra-video/csi.h
>> index c65ff73..386f7c6 100644
>> --- a/drivers/staging/media/tegra-video/csi.h
>> +++ b/drivers/staging/media/tegra-video/csi.h
>> @@ -17,6 +17,10 @@
>>    * CILB.
>>    */
>>   #define CSI_PORTS_PER_BRICK	2
>> +#define CSI_LANES_PER_BRICK	4
>> +
>> +/* Maximum 2 CSI x4 ports can be ganged up for streaming */
>> +#define GANG_PORTS_MAX	2
>>   
>>   /* each CSI channel can have one sink and one source pads */
>>   #define TEGRA_CSI_PADS_NUM	2
>> @@ -43,8 +47,10 @@ struct tegra_csi;
>>    * @numpads: number of pads.
>>    * @csi: Tegra CSI device structure
>>    * @of_node: csi device tree node
>> - * @numlanes: number of lanes used per port/channel
>> - * @csi_port_num: CSI channel port number
>> + * @numgangports: number of immediate ports ganged up to meet the
>> + *             channel bus-width
>> + * @numlanes: number of lanes used per port
>> + * @csi_port_nums: CSI channel port numbers
>>    * @pg_mode: test pattern generator mode for channel
>>    * @format: active format of the channel
>>    * @framerate: active framerate for TPG
>> @@ -60,8 +66,9 @@ struct tegra_csi_channel {
>>   	unsigned int numpads;
>>   	struct tegra_csi *csi;
>>   	struct device_node *of_node;
>> +	u8 numgangports;
>>   	unsigned int numlanes;
>> -	u8 csi_port_num;
>> +	u8 csi_port_nums[GANG_PORTS_MAX];
>>   	u8 pg_mode;
>>   	struct v4l2_mbus_framefmt format;
>>   	unsigned int framerate;
>> @@ -150,6 +157,7 @@ extern const struct tegra_csi_soc tegra210_csi_soc;
>>   
>>   void tegra_csi_error_recover(struct v4l2_subdev *subdev);
>>   void tegra_csi_calc_settle_time(struct tegra_csi_channel *csi_chan,
>> +				u8 csi_port_num,
>>   				u8 *clk_settle_time,
>>   				u8 *ths_settle_time);
>>   #endif
>> diff --git a/drivers/staging/media/tegra-video/tegra210.c b/drivers/staging/media/tegra-video/tegra210.c
>> index 929d277..aff56fc 100644
>> --- a/drivers/staging/media/tegra-video/tegra210.c
>> +++ b/drivers/staging/media/tegra-video/tegra210.c
>> @@ -149,21 +149,22 @@ static u32 tegra_vi_read(struct tegra_vi_channel *chan, unsigned int addr)
>>   }
>>   
>>   /* Tegra210 VI_CSI registers accessors */
>> -static void vi_csi_write(struct tegra_vi_channel *chan, unsigned int addr,
>> -			 u32 val)
>> +static void vi_csi_write(struct tegra_vi_channel *chan, u8 portno,
>> +			 unsigned int addr, u32 val)
>>   {
>>   	void __iomem *vi_csi_base;
>>   
>> -	vi_csi_base = chan->vi->iomem + TEGRA210_VI_CSI_BASE(chan->portno);
>> +	vi_csi_base = chan->vi->iomem + TEGRA210_VI_CSI_BASE(portno);
>>   
>>   	writel_relaxed(val, vi_csi_base + addr);
>>   }
>>   
>> -static u32 vi_csi_read(struct tegra_vi_channel *chan, unsigned int addr)
>> +static u32 vi_csi_read(struct tegra_vi_channel *chan, u8 portno,
>> +		       unsigned int addr)
>>   {
>>   	void __iomem *vi_csi_base;
>>   
>> -	vi_csi_base = chan->vi->iomem + TEGRA210_VI_CSI_BASE(chan->portno);
>> +	vi_csi_base = chan->vi->iomem + TEGRA210_VI_CSI_BASE(portno);
>>   
>>   	return readl_relaxed(vi_csi_base + addr);
>>   }
>> @@ -171,7 +172,8 @@ static u32 vi_csi_read(struct tegra_vi_channel *chan, unsigned int addr)
>>   /*
>>    * Tegra210 VI channel capture operations
>>    */
>> -static int tegra_channel_capture_setup(struct tegra_vi_channel *chan)
>> +static int tegra_channel_capture_setup(struct tegra_vi_channel *chan,
>> +				       u8 portno)
>>   {
>>   	u32 height = chan->format.height;
>>   	u32 width = chan->format.width;
>> @@ -192,19 +194,30 @@ static int tegra_channel_capture_setup(struct tegra_vi_channel *chan)
>>   	    data_type == TEGRA_IMAGE_DT_RGB888)
>>   		bypass_pixel_transform = 0;
>>   
>> -	vi_csi_write(chan, TEGRA_VI_CSI_ERROR_STATUS, 0xffffffff);
>> -	vi_csi_write(chan, TEGRA_VI_CSI_IMAGE_DEF,
>> +	/*
>> +	 * For x8 source streaming, the source image is split onto two x4 ports
>> +	 * with left half to first x4 port and right half to second x4 port.
>> +	 * So, use split width and corresponding word count for each x4 port.
>> +	 */
> This is a mismatch with tc358840: that HDMI receiver only enables ganging mode
> if the width > 1920. Here it is always enabled. As a result streaming will fail
> if the source transmits 1920x1080p60 since the second CSI port will never receive
> data, leading to endless syncpt timeouts.
>
> IMHO it is better to disable ganging for formats with a width <= 1920. There are
> some weird formats that might give issues if the width is devices by 2 (1366x768)
> and in general using just one port for such formats is just simpler.
>
> Regarding the endless syncpt timeouts: in my experience this is an indication that
> something is seriously wrong. If more than X successive timeouts are seen then the
> driver should call vb2_queue_error. I recommend using a V4L2 control to set the
> threshold X (e.g. V4L2_CID_TEGRA_SYNCPT_TIMEOUT_RETRY).
>
>> +	if (chan->numgangports > 1) {
>> +		width = width >> 1;
>> +		word_count = (width * chan->fmtinfo->bit_width) / 8;
>> +	}
>> +
>> +	vi_csi_write(chan, portno, TEGRA_VI_CSI_ERROR_STATUS, 0xffffffff);
>> +	vi_csi_write(chan, portno, TEGRA_VI_CSI_IMAGE_DEF,
>>   		     bypass_pixel_transform |
>>   		     (format << IMAGE_DEF_FORMAT_OFFSET) |
>>   		     IMAGE_DEF_DEST_MEM);
>> -	vi_csi_write(chan, TEGRA_VI_CSI_IMAGE_DT, data_type);
>> -	vi_csi_write(chan, TEGRA_VI_CSI_IMAGE_SIZE_WC, word_count);
>> -	vi_csi_write(chan, TEGRA_VI_CSI_IMAGE_SIZE,
>> +	vi_csi_write(chan, portno, TEGRA_VI_CSI_IMAGE_DT, data_type);
>> +	vi_csi_write(chan, portno, TEGRA_VI_CSI_IMAGE_SIZE_WC, word_count);
>> +	vi_csi_write(chan, portno, TEGRA_VI_CSI_IMAGE_SIZE,
>>   		     (height << IMAGE_SIZE_HEIGHT_OFFSET) | width);
>>   	return 0;
>>   }
>>   
>> -static void tegra_channel_vi_soft_reset(struct tegra_vi_channel *chan)
>> +static void tegra_channel_vi_soft_reset(struct tegra_vi_channel *chan,
>> +					u8 portno)
>>   {
>>   	/* disable clock gating to enable continuous clock */
>>   	tegra_vi_write(chan, TEGRA_VI_CFG_CG_CTRL, 0);
>> @@ -212,15 +225,16 @@ static void tegra_channel_vi_soft_reset(struct tegra_vi_channel *chan)
>>   	 * Soft reset memory client interface, pixel format logic, sensor
>>   	 * control logic, and a shadow copy logic to bring VI to clean state.
>>   	 */
>> -	vi_csi_write(chan, TEGRA_VI_CSI_SW_RESET, 0xf);
>> +	vi_csi_write(chan, portno, TEGRA_VI_CSI_SW_RESET, 0xf);
>>   	usleep_range(100, 200);
>> -	vi_csi_write(chan, TEGRA_VI_CSI_SW_RESET, 0x0);
>> +	vi_csi_write(chan, portno, TEGRA_VI_CSI_SW_RESET, 0x0);
>>   
>>   	/* enable back VI clock gating */
>>   	tegra_vi_write(chan, TEGRA_VI_CFG_CG_CTRL, VI_CG_2ND_LEVEL_EN);
>>   }
>>   
>> -static void tegra_channel_capture_error_recover(struct tegra_vi_channel *chan)
>> +static void tegra_channel_capture_error_recover(struct tegra_vi_channel *chan,
>> +						u8 portno)
>>   {
>>   	struct v4l2_subdev *subdev;
>>   	u32 val;
>> @@ -232,9 +246,9 @@ static void tegra_channel_capture_error_recover(struct tegra_vi_channel *chan)
>>   	 * events which can cause CSI and VI hardware hang.
>>   	 * This helps to have a clean capture for next frame.
>>   	 */
>> -	val = vi_csi_read(chan, TEGRA_VI_CSI_ERROR_STATUS);
>> +	val = vi_csi_read(chan, portno, TEGRA_VI_CSI_ERROR_STATUS);
>>   	dev_dbg(&chan->video.dev, "TEGRA_VI_CSI_ERROR_STATUS 0x%08x\n", val);
>> -	vi_csi_write(chan, TEGRA_VI_CSI_ERROR_STATUS, val);
>> +	vi_csi_write(chan, portno, TEGRA_VI_CSI_ERROR_STATUS, val);
>>   
>>   	val = tegra_vi_read(chan, TEGRA_VI_CFG_VI_INCR_SYNCPT_ERROR);
>>   	dev_dbg(&chan->video.dev,
>> @@ -242,8 +256,8 @@ static void tegra_channel_capture_error_recover(struct tegra_vi_channel *chan)
>>   	tegra_vi_write(chan, TEGRA_VI_CFG_VI_INCR_SYNCPT_ERROR, val);
>>   
>>   	/* recover VI by issuing software reset and re-setup for capture */
>> -	tegra_channel_vi_soft_reset(chan);
>> -	tegra_channel_capture_setup(chan);
>> +	tegra_channel_vi_soft_reset(chan, portno);
>> +	tegra_channel_capture_setup(chan, portno);
>>   
>>   	/* recover CSI block */
>>   	subdev = tegra_channel_get_remote_csi_subdev(chan);
>> @@ -282,80 +296,114 @@ static void release_buffer(struct tegra_vi_channel *chan,
>>   	vb2_buffer_done(&vb->vb2_buf, state);
>>   }
>>   
>> -static int tegra_channel_capture_frame(struct tegra_vi_channel *chan,
>> -				       struct tegra_channel_buffer *buf)
>> +static void tegra_channel_vi_buffer_setup(struct tegra_vi_channel *chan,
>> +					  u8 portno, u32 buf_offset,
>> +					  struct tegra_channel_buffer *buf)
>>   {
>> -	u32 thresh, value, frame_start, mw_ack_done;
>> -	int bytes_per_line = chan->format.bytesperline;
>> +	int bytesperline = chan->format.bytesperline;
>>   	u32 sizeimage = chan->format.sizeimage;
>> -	int err;
>>   
>>   	/* program buffer address by using surface 0 */
>> -	vi_csi_write(chan, TEGRA_VI_CSI_SURFACE0_OFFSET_MSB,
>> -		     (u64)buf->addr >> 32);
>> -	vi_csi_write(chan, TEGRA_VI_CSI_SURFACE0_OFFSET_LSB, buf->addr);
>> -	vi_csi_write(chan, TEGRA_VI_CSI_SURFACE0_STRIDE, bytes_per_line);
>> +	vi_csi_write(chan, portno, TEGRA_VI_CSI_SURFACE0_OFFSET_MSB,
>> +		     ((u64)buf->addr + buf_offset) >> 32);
>> +	vi_csi_write(chan, portno, TEGRA_VI_CSI_SURFACE0_OFFSET_LSB,
>> +		     buf->addr + buf_offset);
>> +	vi_csi_write(chan, portno, TEGRA_VI_CSI_SURFACE0_STRIDE, bytesperline);
>>   
>> +	if (chan->fmtinfo->fourcc != V4L2_PIX_FMT_NV16)
>> +		return;
>>   	/*
>>   	 * Program surface 1 for UV plane with offset sizeimage from Y plane.
>>   	 */
>> -	if (chan->fmtinfo->fourcc == V4L2_PIX_FMT_NV16) {
>> -		vi_csi_write(chan, TEGRA_VI_CSI_SURFACE1_OFFSET_MSB,
>> -			     ((u64)buf->addr + sizeimage / 2) >> 32);
>> -		vi_csi_write(chan, TEGRA_VI_CSI_SURFACE1_OFFSET_LSB,
>> -			     buf->addr + sizeimage / 2);
>> -		vi_csi_write(chan, TEGRA_VI_CSI_SURFACE1_STRIDE,
>> -			     bytes_per_line);
>> -	}
>> -
>> -	/*
>> -	 * Tegra VI block interacts with host1x syncpt for synchronizing
>> -	 * programmed condition of capture state and hardware operation.
>> -	 * Frame start and Memory write acknowledge syncpts has their own
>> -	 * FIFO of depth 2.
>> -	 *
>> -	 * Syncpoint trigger conditions set through VI_INCR_SYNCPT register
>> -	 * are added to HW syncpt FIFO and when the HW triggers, syncpt
>> -	 * condition is removed from the FIFO and counter at syncpoint index
>> -	 * will be incremented by the hardware and software can wait for
>> -	 * counter to reach threshold to synchronize capturing frame with the
>> -	 * hardware capture events.
>> -	 */
>> +	vi_csi_write(chan, portno, TEGRA_VI_CSI_SURFACE1_OFFSET_MSB,
>> +		     (((u64)buf->addr + sizeimage / 2) + buf_offset) >> 32);
>> +	vi_csi_write(chan, portno, TEGRA_VI_CSI_SURFACE1_OFFSET_LSB,
>> +		     buf->addr + sizeimage / 2 + buf_offset);
>> +	vi_csi_write(chan, portno, TEGRA_VI_CSI_SURFACE1_STRIDE, bytesperline);
>> +}
>>   
>> -	/* increase channel syncpoint threshold for FRAME_START */
>> -	thresh = host1x_syncpt_incr_max(chan->frame_start_sp, 1);
>> +static int tegra_channel_capture_frame(struct tegra_vi_channel *chan,
>> +				       struct tegra_channel_buffer *buf)
>> +{
>> +	u32 thresh, value, frame_start, mw_ack_done;
>> +	u32 fs_thresh[GANG_PORTS_MAX];
>> +	u8 *portnos = chan->portnos;
>> +	int gang_bpl = (chan->format.width >> 1) * chan->fmtinfo->bpp;
>> +	u32 buf_offset;
>> +	bool capture_timedout = false;
>> +	int err, i;
>> +
>> +	for (i = 0; i < chan->numgangports; i++) {
>> +		/*
>> +		 * Align buffers side-by-side for all consecutive x4 ports
>> +		 * in gang ports using bytes per line based on source split
>> +		 * width.
>> +		 */
>> +		buf_offset = i * roundup(gang_bpl, SURFACE_ALIGN_BYTES);
>> +		tegra_channel_vi_buffer_setup(chan, portnos[i], buf_offset,
>> +					      buf);
>>   
>> -	/* Program FRAME_START trigger condition syncpt request */
>> -	frame_start = VI_CSI_PP_FRAME_START(chan->portno);
>> -	value = VI_CFG_VI_INCR_SYNCPT_COND(frame_start) |
>> -		host1x_syncpt_id(chan->frame_start_sp);
>> -	tegra_vi_write(chan, TEGRA_VI_CFG_VI_INCR_SYNCPT, value);
>> +		/*
>> +		 * Tegra VI block interacts with host1x syncpt to synchronize
>> +		 * programmed condition and hardware operation for capture.
>> +		 * Frame start and Memory write acknowledge syncpts has their
>> +		 * own FIFO of depth 2.
>> +		 *
>> +		 * Syncpoint trigger conditions set through VI_INCR_SYNCPT
>> +		 * register are added to HW syncpt FIFO and when HW triggers,
>> +		 * syncpt condition is removed from the FIFO and counter at
>> +		 * syncpoint index will be incremented by the hardware and
>> +		 * software can wait for counter to reach threshold to
>> +		 * synchronize capturing frame with hardware capture events.
>> +		 */
>>   
>> -	/* increase channel syncpoint threshold for MW_ACK_DONE */
>> -	buf->mw_ack_sp_thresh = host1x_syncpt_incr_max(chan->mw_ack_sp, 1);
>> +		/* increase channel syncpoint threshold for FRAME_START */
>> +		thresh = host1x_syncpt_incr_max(chan->frame_start_sp[i], 1);
>> +		fs_thresh[i] = thresh;
>> +
>> +		/* Program FRAME_START trigger condition syncpt request */
>> +		frame_start = VI_CSI_PP_FRAME_START(portnos[i]);
>> +		value = VI_CFG_VI_INCR_SYNCPT_COND(frame_start) |
>> +			host1x_syncpt_id(chan->frame_start_sp[i]);
> This fails if this driver is compiled into the kernel. In that case
> tegra_vi_channel_syncpt_init() is called too soon when chan->numgangports
> is still 1, so chan->frame_start_sp[1] will be NULL.
>
> Of course, I tested this driver by compiling it into the kernel, so that
> took me some debugging to figure out why it crashed :-)
>
>> +		tegra_vi_write(chan, TEGRA_VI_CFG_VI_INCR_SYNCPT, value);
>> +
>> +		/* increase channel syncpoint threshold for MW_ACK_DONE */
>> +		thresh = host1x_syncpt_incr_max(chan->mw_ack_sp[i], 1);
>> +		buf->mw_ack_sp_thresh[i] = thresh;
>> +
>> +		/* Program MW_ACK_DONE trigger condition syncpt request */
>> +		mw_ack_done = VI_CSI_MW_ACK_DONE(portnos[i]);
>> +		value = VI_CFG_VI_INCR_SYNCPT_COND(mw_ack_done) |
>> +			host1x_syncpt_id(chan->mw_ack_sp[i]);
>> +		tegra_vi_write(chan, TEGRA_VI_CFG_VI_INCR_SYNCPT, value);
>> +	}
>>   
>> -	/* Program MW_ACK_DONE trigger condition syncpt request */
>> -	mw_ack_done = VI_CSI_MW_ACK_DONE(chan->portno);
>> -	value = VI_CFG_VI_INCR_SYNCPT_COND(mw_ack_done) |
>> -		host1x_syncpt_id(chan->mw_ack_sp);
>> -	tegra_vi_write(chan, TEGRA_VI_CFG_VI_INCR_SYNCPT, value);
>> +	/* enable single shot capture after all ganged ports are ready */
>> +	for (i = 0; i < chan->numgangports; i++)
>> +		vi_csi_write(chan, portnos[i], TEGRA_VI_CSI_SINGLE_SHOT,
>> +			     SINGLE_SHOT_CAPTURE);
>>   
>> -	/* enable single shot capture */
>> -	vi_csi_write(chan, TEGRA_VI_CSI_SINGLE_SHOT, SINGLE_SHOT_CAPTURE);
>> +	for (i = 0; i < chan->numgangports; i++) {
>> +		/*
>> +		 * Wait for syncpt counter to reach frame start event threshold
>> +		 */
>> +		err = host1x_syncpt_wait(chan->frame_start_sp[i], fs_thresh[i],
>> +					 TEGRA_VI_SYNCPT_WAIT_TIMEOUT, &value);
>> +		if (err) {
>> +			capture_timedout = true;
>> +			/* increment syncpoint counter for timedout events */
>> +			host1x_syncpt_incr(chan->frame_start_sp[i]);
>> +			spin_lock(&chan->sp_incr_lock[i]);
>> +			host1x_syncpt_incr(chan->mw_ack_sp[i]);
>> +			spin_unlock(&chan->sp_incr_lock[i]);
>> +			/* clear errors and recover */
>> +			tegra_channel_capture_error_recover(chan, portnos[i]);
>> +		}
>> +	}
>>   
>> -	/* wait for syncpt counter to reach frame start event threshold */
>> -	err = host1x_syncpt_wait(chan->frame_start_sp, thresh,
>> -				 TEGRA_VI_SYNCPT_WAIT_TIMEOUT, &value);
>> -	if (err) {
>> +	if (capture_timedout) {
>>   		dev_err_ratelimited(&chan->video.dev,
>>   				    "frame start syncpt timeout: %d\n", err);
>> -		/* increment syncpoint counter for timedout events */
>> -		host1x_syncpt_incr(chan->frame_start_sp);
>> -		spin_lock(&chan->sp_incr_lock);
>> -		host1x_syncpt_incr(chan->mw_ack_sp);
>> -		spin_unlock(&chan->sp_incr_lock);
>> -		/* clear errors and recover */
>> -		tegra_channel_capture_error_recover(chan);
>>   		release_buffer(chan, buf, VB2_BUF_STATE_ERROR);
>>   		return err;
>>   	}
>> @@ -376,21 +424,29 @@ static void tegra_channel_capture_done(struct tegra_vi_channel *chan,
>>   {
>>   	enum vb2_buffer_state state = VB2_BUF_STATE_DONE;
>>   	u32 value;
>> -	int ret;
>> +	bool capture_timedout = false;
>> +	int ret, i;
>>   
>> -	/* wait for syncpt counter to reach MW_ACK_DONE event threshold */
>> -	ret = host1x_syncpt_wait(chan->mw_ack_sp, buf->mw_ack_sp_thresh,
>> -				 TEGRA_VI_SYNCPT_WAIT_TIMEOUT, &value);
>> -	if (ret) {
>> -		dev_err_ratelimited(&chan->video.dev,
>> -				    "MW_ACK_DONE syncpt timeout: %d\n", ret);
>> -		state = VB2_BUF_STATE_ERROR;
>> -		/* increment syncpoint counter for timedout event */
>> -		spin_lock(&chan->sp_incr_lock);
>> -		host1x_syncpt_incr(chan->mw_ack_sp);
>> -		spin_unlock(&chan->sp_incr_lock);
>> +	for (i = 0; i < chan->numgangports; i++) {
>> +		/*
>> +		 * Wait for syncpt counter to reach MW_ACK_DONE event threshold
>> +		 */
>> +		ret = host1x_syncpt_wait(chan->mw_ack_sp[i],
>> +					 buf->mw_ack_sp_thresh[i],
>> +					 TEGRA_VI_SYNCPT_WAIT_TIMEOUT, &value);
>> +		if (ret) {
>> +			capture_timedout = true;
>> +			state = VB2_BUF_STATE_ERROR;
>> +			/* increment syncpoint counter for timedout event */
>> +			spin_lock(&chan->sp_incr_lock[i]);
>> +			host1x_syncpt_incr(chan->mw_ack_sp[i]);
>> +			spin_unlock(&chan->sp_incr_lock[i]);
>> +		}
>>   	}
>>   
>> +	if (capture_timedout)
>> +		dev_err_ratelimited(&chan->video.dev,
>> +				    "MW_ACK_DONE syncpt timeout: %d\n", ret);
>>   	release_buffer(chan, buf, state);
>>   }
>>   
>> @@ -463,14 +519,12 @@ static int tegra210_vi_start_streaming(struct vb2_queue *vq, u32 count)
>>   	struct tegra_vi_channel *chan = vb2_get_drv_priv(vq);
>>   	struct media_pipeline *pipe = &chan->video.pipe;
>>   	u32 val;
>> -	int ret;
>> +	u8 *portnos = chan->portnos;
>> +	int ret, i;
>>   
>>   	tegra_vi_write(chan, TEGRA_VI_CFG_CG_CTRL, VI_CG_2ND_LEVEL_EN);
>>   
>> -	/* clear errors */
>> -	val = vi_csi_read(chan, TEGRA_VI_CSI_ERROR_STATUS);
>> -	vi_csi_write(chan, TEGRA_VI_CSI_ERROR_STATUS, val);
>> -
>> +	/* clear syncpt errors */
>>   	val = tegra_vi_read(chan, TEGRA_VI_CFG_VI_INCR_SYNCPT_ERROR);
>>   	tegra_vi_write(chan, TEGRA_VI_CFG_VI_INCR_SYNCPT_ERROR, val);
>>   
>> @@ -489,7 +543,14 @@ static int tegra210_vi_start_streaming(struct vb2_queue *vq, u32 count)
>>   	if (ret < 0)
>>   		goto error_pipeline_start;
>>   
>> -	tegra_channel_capture_setup(chan);
>> +	/* clear csi errors and do capture setup for all ports in gang mode */
>> +	for (i = 0; i < chan->numgangports; i++) {
>> +		val = vi_csi_read(chan, portnos[i], TEGRA_VI_CSI_ERROR_STATUS);
>> +		vi_csi_write(chan, portnos[i], TEGRA_VI_CSI_ERROR_STATUS, val);
>> +
>> +		tegra_channel_capture_setup(chan, portnos[i]);
>> +	}
>> +
>>   	ret = tegra_channel_set_stream(chan, true);
>>   	if (ret < 0)
>>   		goto error_set_stream;
>> @@ -743,10 +804,10 @@ static void tpg_write(struct tegra_csi *csi, u8 portno, unsigned int addr,
>>   /*
>>    * Tegra210 CSI operations
>>    */
>> -static void tegra210_csi_error_recover(struct tegra_csi_channel *csi_chan)
>> +static void tegra210_csi_port_recover(struct tegra_csi_channel *csi_chan,
>> +				      u8 portno)
>>   {
>>   	struct tegra_csi *csi = csi_chan->csi;
>> -	unsigned int portno = csi_chan->csi_port_num;
>>   	u32 val;
>>   
>>   	/*
>> @@ -795,16 +856,26 @@ static void tegra210_csi_error_recover(struct tegra_csi_channel *csi_chan)
>>   	}
>>   }
>>   
>> -static int tegra210_csi_start_streaming(struct tegra_csi_channel *csi_chan)
>> +static void tegra210_csi_error_recover(struct tegra_csi_channel *csi_chan)
>> +{
>> +	u8 *portnos = csi_chan->csi_port_nums;
>> +	int i;
>> +
>> +	for (i = 0; i < csi_chan->numgangports; i++)
>> +		tegra210_csi_port_recover(csi_chan, portnos[i]);
>> +}
>> +
>> +static int
>> +tegra210_csi_port_start_streaming(struct tegra_csi_channel *csi_chan,
>> +				  u8 portno)
>>   {
>>   	struct tegra_csi *csi = csi_chan->csi;
>> -	unsigned int portno = csi_chan->csi_port_num;
>>   	u8 clk_settle_time = 0;
>>   	u8 ths_settle_time = 10;
>>   	u32 val;
>>   
>>   	if (!csi_chan->pg_mode)
>> -		tegra_csi_calc_settle_time(csi_chan, &clk_settle_time,
>> +		tegra_csi_calc_settle_time(csi_chan, portno, &clk_settle_time,
>>   					   &ths_settle_time);
>>   
>>   	csi_write(csi, portno, TEGRA_CSI_CLKEN_OVERRIDE, 0);
>> @@ -903,10 +974,10 @@ static int tegra210_csi_start_streaming(struct tegra_csi_channel *csi_chan)
>>   	return 0;
>>   }
>>   
>> -static void tegra210_csi_stop_streaming(struct tegra_csi_channel *csi_chan)
>> +static void
>> +tegra210_csi_port_stop_streaming(struct tegra_csi_channel *csi_chan, u8 portno)
>>   {
>>   	struct tegra_csi *csi = csi_chan->csi;
>> -	unsigned int portno = csi_chan->csi_port_num;
>>   	u32 val;
>>   
>>   	val = pp_read(csi, portno, TEGRA_CSI_PIXEL_PARSER_STATUS);
>> @@ -944,6 +1015,35 @@ static void tegra210_csi_stop_streaming(struct tegra_csi_channel *csi_chan)
>>   	}
>>   }
>>   
>> +static int tegra210_csi_start_streaming(struct tegra_csi_channel *csi_chan)
>> +{
>> +	u8 *portnos = csi_chan->csi_port_nums;
>> +	int ret, i;
>> +
>> +	for (i = 0; i < csi_chan->numgangports; i++) {
>> +		ret = tegra210_csi_port_start_streaming(csi_chan, portnos[i]);
>> +		if (ret)
>> +			goto stream_start_fail;
>> +	}
>> +
>> +	return 0;
>> +
>> +stream_start_fail:
>> +	for (i = i - 1; i >= 0; i--)
>> +		tegra210_csi_port_stop_streaming(csi_chan, portnos[i]);
>> +
>> +	return ret;
>> +}
>> +
>> +static void tegra210_csi_stop_streaming(struct tegra_csi_channel *csi_chan)
>> +{
>> +	u8 *portnos = csi_chan->csi_port_nums;
>> +	int i;
>> +
>> +	for (i = 0; i < csi_chan->numgangports; i++)
>> +		tegra210_csi_port_stop_streaming(csi_chan, portnos[i]);
>> +}
>> +
>>   /*
>>    * Tegra210 CSI TPG frame rate table with horizontal and vertical
>>    * blanking intervals for corresponding format and resolution.
>> diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c
>> index e866f7d..4812f2c 100644
>> --- a/drivers/staging/media/tegra-video/vi.c
>> +++ b/drivers/staging/media/tegra-video/vi.c
>> @@ -30,7 +30,6 @@
>>   #include "vi.h"
>>   #include "video.h"
>>   
>> -#define SURFACE_ALIGN_BYTES		64
>>   #define MAX_CID_CONTROLS		1
>>   
>>   static const struct tegra_video_format tegra_default_format = {
>> @@ -1090,12 +1089,20 @@ static int vi_fmts_bitmap_init(struct tegra_vi_channel *chan)
>>   	return 0;
>>   }
>>   
>> +static void tegra_channel_host1x_syncpts_free(struct tegra_vi_channel *chan)
>> +{
>> +	int i;
>> +
>> +	for (i = 0; i < chan->numgangports; i++) {
>> +		host1x_syncpt_free(chan->mw_ack_sp[i]);
>> +		host1x_syncpt_free(chan->frame_start_sp[i]);
>> +	}
>> +}
>> +
>>   static void tegra_channel_cleanup(struct tegra_vi_channel *chan)
>>   {
>>   	v4l2_ctrl_handler_free(&chan->ctrl_handler);
>>   	media_entity_cleanup(&chan->video.entity);
>> -	host1x_syncpt_free(chan->mw_ack_sp);
>> -	host1x_syncpt_free(chan->frame_start_sp);
>>   	mutex_destroy(&chan->video_lock);
>>   }
>>   
>> @@ -1107,6 +1114,7 @@ void tegra_channels_cleanup(struct tegra_vi *vi)
>>   		return;
>>   
>>   	list_for_each_entry_safe(chan, tmp, &vi->vi_chans, list) {
>> +		tegra_channel_host1x_syncpts_free(chan);
>>   		tegra_channel_cleanup(chan);
>>   		list_del(&chan->list);
>>   		kfree(chan);
>> @@ -1117,7 +1125,6 @@ static int tegra_channel_init(struct tegra_vi_channel *chan)
>>   {
>>   	struct tegra_vi *vi = chan->vi;
>>   	struct tegra_video_device *vid = dev_get_drvdata(vi->client.host);
>> -	unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED;
>>   	int ret;
>>   
>>   	mutex_init(&chan->video_lock);
>> @@ -1125,7 +1132,6 @@ static int tegra_channel_init(struct tegra_vi_channel *chan)
>>   	INIT_LIST_HEAD(&chan->done);
>>   	spin_lock_init(&chan->start_lock);
>>   	spin_lock_init(&chan->done_lock);
>> -	spin_lock_init(&chan->sp_incr_lock);
>>   	init_waitqueue_head(&chan->start_wait);
>>   	init_waitqueue_head(&chan->done_wait);
>>   
>> @@ -1140,26 +1146,13 @@ static int tegra_channel_init(struct tegra_vi_channel *chan)
>>   	chan->format.sizeimage = chan->format.bytesperline * TEGRA_DEF_HEIGHT;
>>   	tegra_channel_fmt_align(chan, &chan->format, chan->fmtinfo->bpp);
>>   
>> -	chan->frame_start_sp = host1x_syncpt_request(&vi->client, flags);
>> -	if (!chan->frame_start_sp) {
>> -		dev_err(vi->dev, "failed to request frame start syncpoint\n");
>> -		return -ENOMEM;
>> -	}
>> -
>> -	chan->mw_ack_sp = host1x_syncpt_request(&vi->client, flags);
>> -	if (!chan->mw_ack_sp) {
>> -		dev_err(vi->dev, "failed to request memory ack syncpoint\n");
>> -		ret = -ENOMEM;
>> -		goto free_fs_syncpt;
>> -	}
>> -
>>   	/* initialize the media entity */
>>   	chan->pad.flags = MEDIA_PAD_FL_SINK;
>>   	ret = media_entity_pads_init(&chan->video.entity, 1, &chan->pad);
>>   	if (ret < 0) {
>>   		dev_err(vi->dev,
>>   			"failed to initialize media entity: %d\n", ret);
>> -		goto free_mw_ack_syncpt;
>> +		return ret;
>>   	}
>>   
>>   	ret = v4l2_ctrl_handler_init(&chan->ctrl_handler, MAX_CID_CONTROLS);
>> @@ -1175,7 +1168,7 @@ static int tegra_channel_init(struct tegra_vi_channel *chan)
>>   	chan->video.release = video_device_release_empty;
>>   	chan->video.queue = &chan->queue;
>>   	snprintf(chan->video.name, sizeof(chan->video.name), "%s-%s-%u",
>> -		 dev_name(vi->dev), "output", chan->portno);
>> +		 dev_name(vi->dev), "output", chan->portnos[0]);
>>   	chan->video.vfl_type = VFL_TYPE_VIDEO;
>>   	chan->video.vfl_dir = VFL_DIR_RX;
>>   	chan->video.ioctl_ops = &tegra_channel_ioctl_ops;
>> @@ -1211,10 +1204,6 @@ static int tegra_channel_init(struct tegra_vi_channel *chan)
>>   	v4l2_ctrl_handler_free(&chan->ctrl_handler);
>>   cleanup_media:
>>   	media_entity_cleanup(&chan->video.entity);
>> -free_mw_ack_syncpt:
>> -	host1x_syncpt_free(chan->mw_ack_sp);
>> -free_fs_syncpt:
>> -	host1x_syncpt_free(chan->frame_start_sp);
>>   	return ret;
>>   }
>>   
>> @@ -1234,7 +1223,8 @@ static int tegra_vi_channel_alloc(struct tegra_vi *vi, unsigned int port_num,
>>   		return -ENOMEM;
>>   
>>   	chan->vi = vi;
>> -	chan->portno = port_num;
>> +	chan->portnos[0] = port_num;
>> +	chan->numgangports = 1;
>>   	chan->of_node = node;
>>   	list_add_tail(&chan->list, &vi->vi_chans);
>>   
>> @@ -1312,7 +1302,7 @@ static int tegra_vi_channels_init(struct tegra_vi *vi)
>>   		if (ret < 0) {
>>   			dev_err(vi->dev,
>>   				"failed to initialize channel-%d: %d\n",
>> -				chan->portno, ret);
>> +				chan->portnos[0], ret);
>>   			goto cleanup;
>>   		}
>>   	}
>> @@ -1326,6 +1316,46 @@ static int tegra_vi_channels_init(struct tegra_vi *vi)
>>   	return ret;
>>   }
>>   
>> +static int tegra_vi_channel_syncpt_init(struct tegra_vi *vi)
>> +{
>> +	struct tegra_vi_channel *chan;
>> +	unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED;
>> +	struct host1x_syncpt *fs_sp;
>> +	struct host1x_syncpt *mw_sp;
>> +	int ret, i;
>> +
>> +	list_for_each_entry(chan, &vi->vi_chans, list) {
>> +		for (i = 0; i < chan->numgangports; i++) {
>> +			fs_sp = host1x_syncpt_request(&vi->client, flags);
>> +			if (!fs_sp) {
>> +				dev_err(vi->dev, "failed to request frame start syncpoint\n");
>> +				ret = -ENOMEM;
>> +				goto free_syncpts;
>> +			}
>> +
>> +			mw_sp = host1x_syncpt_request(&vi->client, flags);
>> +			if (!mw_sp) {
>> +				dev_err(vi->dev, "failed to request memory ack syncpoint\n");
>> +				ret = -ENOMEM;
>> +				host1x_syncpt_free(fs_sp);
>> +				goto free_syncpts;
>> +			}
>> +
>> +			chan->frame_start_sp[i] = fs_sp;
>> +			chan->mw_ack_sp[i] = mw_sp;
>> +			spin_lock_init(&chan->sp_incr_lock[i]);
>> +		}
>> +	}
>> +
>> +	return 0;
>> +
>> +free_syncpts:
>> +	list_for_each_entry_continue_reverse(chan, &vi->vi_chans, list)
>> +		tegra_channel_host1x_syncpts_free(chan);
>> +
>> +	return ret;
>> +}
>> +
>>   void tegra_v4l2_nodes_cleanup_tpg(struct tegra_video_device *vid)
>>   {
>>   	struct tegra_vi *vi = vid->vi;
>> @@ -1576,8 +1606,9 @@ static int tegra_vi_graph_notify_complete(struct v4l2_async_notifier *notifier)
>>   	struct v4l2_async_subdev *asd;
>>   	struct v4l2_subdev *subdev;
>>   	struct tegra_vi_channel *chan;
>> +	struct tegra_csi_channel *csi_chan;
>>   	struct tegra_vi *vi;
>> -	int ret;
>> +	int ret, i;
>>   
>>   	chan = container_of(notifier, struct tegra_vi_channel, notifier);
>>   	vi = chan->vi;
>> @@ -1634,6 +1665,15 @@ static int tegra_vi_graph_notify_complete(struct v4l2_async_notifier *notifier)
>>   
>>   	v4l2_set_subdev_hostdata(subdev, chan);
>>   
>> +	/*
>> +	 * Retrieve number of gang ports from csi channel and add consecutive
>> +	 * ports to vi channel ports.
>> +	 */
>> +	csi_chan = v4l2_get_subdevdata(subdev);
>> +	chan->numgangports = csi_chan->numgangports;
>> +	for (i = 1; i < chan->numgangports; i++)
>> +		chan->portnos[i] = chan->portnos[0] + i * CSI_PORTS_PER_BRICK;
>> +
>>   	subdev = tegra_channel_get_remote_source_subdev(chan);
>>   	v4l2_set_subdev_hostdata(subdev, chan);
>>   
>> @@ -1759,7 +1799,8 @@ static int tegra_vi_graph_init(struct tegra_vi *vi)
>>   	 * next channels.
>>   	 */
>>   	list_for_each_entry(chan, &vi->vi_chans, list) {
>> -		remote = fwnode_graph_get_remote_node(fwnode, chan->portno, 0);
>> +		remote = fwnode_graph_get_remote_node(fwnode, chan->portnos[0],
>> +						      0);
>>   		if (!remote)
>>   			continue;
>>   
>> @@ -1774,7 +1815,7 @@ static int tegra_vi_graph_init(struct tegra_vi *vi)
>>   		if (ret < 0) {
>>   			dev_err(vi->dev,
>>   				"failed to register channel %d notifier: %d\n",
>> -				chan->portno, ret);
>> +				chan->portnos[0], ret);
>>   			v4l2_async_notifier_cleanup(&chan->notifier);
>>   		}
>>   	}
>> @@ -1825,11 +1866,27 @@ static int tegra_vi_init(struct host1x_client *client)
>>   	if (!IS_ENABLED(CONFIG_VIDEO_TEGRA_TPG)) {
>>   		ret = tegra_vi_graph_init(vi);
>>   		if (ret < 0)
>> -			goto free_chans;
>> +			goto cleanup_chans;
>>   	}
>>   
>> +	/*
>> +	 * x8 capture uses multiple ports as gang with simultaneous capture
>> +	 * of left half onto one x4 port and right half onto second x4 port.
>> +	 * Total ports grouped together as gang is retrieved from CSI subdev
>> +	 * during graph build.
>> +	 * So, create host1x syncpts for all ports in a gang after graph init.
>> +	 */
>> +	ret = tegra_vi_channel_syncpt_init(vi);
>> +	if (ret < 0)
>> +		goto cleanup_graph;
>> +
>>   	return 0;
>>   
>> +cleanup_graph:
>> +	tegra_vi_graph_cleanup(vi);
>> +cleanup_chans:
>> +	list_for_each_entry(chan, &vi->vi_chans, list)
>> +		tegra_channel_cleanup(chan);
>>   free_chans:
>>   	list_for_each_entry_safe(chan, tmp, &vi->vi_chans, list) {
>>   		list_del(&chan->list);
>> diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h
>> index 7d6b7a6..947e641 100644
>> --- a/drivers/staging/media/tegra-video/vi.h
>> +++ b/drivers/staging/media/tegra-video/vi.h
>> @@ -21,6 +21,8 @@
>>   #include <media/v4l2-subdev.h>
>>   #include <media/videobuf2-v4l2.h>
>>   
>> +#include "csi.h"
>> +
>>   #define TEGRA_MIN_WIDTH		32U
>>   #define TEGRA_MAX_WIDTH		32768U
>>   #define TEGRA_MIN_HEIGHT	32U
>> @@ -31,6 +33,7 @@
>>   #define TEGRA_IMAGE_FORMAT_DEF	32
>>   
>>   #define MAX_FORMAT_NUM		64
>> +#define SURFACE_ALIGN_BYTES	64
>>   
>>   enum tegra_vi_pg_mode {
>>   	TEGRA_VI_PG_DISABLED = 0,
>> @@ -151,7 +154,8 @@ struct tegra_vi_graph_entity {
>>    * @done: list of capture done queued buffers
>>    * @done_lock: protects the capture done queue list
>>    *
>> - * @portno: VI channel port number
>> + * @portnos: VI channel port numbers
>> + * @numgangports: number of ports combined together as a gang for capture
>>    * @of_node: device node of VI channel
>>    *
>>    * @ctrl_handler: V4L2 control handler of this video channel
>> @@ -168,10 +172,10 @@ struct tegra_vi_channel {
>>   	struct media_pad pad;
>>   
>>   	struct tegra_vi *vi;
>> -	struct host1x_syncpt *frame_start_sp;
>> -	struct host1x_syncpt *mw_ack_sp;
>> +	struct host1x_syncpt *frame_start_sp[GANG_PORTS_MAX];
>> +	struct host1x_syncpt *mw_ack_sp[GANG_PORTS_MAX];
>>   	/* protects the cpu syncpoint increment */
>> -	spinlock_t sp_incr_lock;
>> +	spinlock_t sp_incr_lock[GANG_PORTS_MAX];
>>   
>>   	struct task_struct *kthread_start_capture;
>>   	wait_queue_head_t start_wait;
>> @@ -190,7 +194,8 @@ struct tegra_vi_channel {
>>   	/* protects the capture done queue list */
>>   	spinlock_t done_lock;
>>   
>> -	unsigned char portno;
>> +	unsigned char portnos[GANG_PORTS_MAX];
>> +	u8 numgangports;
>>   	struct device_node *of_node;
>>   
>>   	struct v4l2_ctrl_handler ctrl_handler;
>> @@ -216,7 +221,7 @@ struct tegra_channel_buffer {
>>   	struct list_head queue;
>>   	struct tegra_vi_channel *chan;
>>   	dma_addr_t addr;
>> -	u32 mw_ack_sp_thresh;
>> +	u32 mw_ack_sp_thresh[GANG_PORTS_MAX];
>>   };
>>   
>>   /*
>>
> Regards,
>
> 	Hans

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ