From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-11.4 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,NICE_REPLY_A,SIGNED_OFF_BY,SPF_HELO_NONE, SPF_PASS,URIBL_BLOCKED,USER_AGENT_SANE_1 autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9D048C2D0A3 for ; Mon, 9 Nov 2020 16:22:29 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 2E78820E65 for ; Mon, 9 Nov 2020 16:22:29 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=nvidia.com header.i=@nvidia.com header.b="SblyS+Ui" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730181AbgKIQW2 (ORCPT ); Mon, 9 Nov 2020 11:22:28 -0500 Received: from hqnvemgate25.nvidia.com ([216.228.121.64]:13995 "EHLO hqnvemgate25.nvidia.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729921AbgKIQW2 (ORCPT ); Mon, 9 Nov 2020 11:22:28 -0500 Received: from hqmail.nvidia.com (Not Verified[216.228.121.13]) by hqnvemgate25.nvidia.com (using TLS: TLSv1.2, AES256-SHA) id ; Mon, 09 Nov 2020 08:22:23 -0800 Received: from [10.2.59.206] (10.124.1.5) by HQMAIL107.nvidia.com (172.20.187.13) with Microsoft SMTP Server (TLS) id 15.0.1473.3; Mon, 9 Nov 2020 16:22:27 +0000 Subject: Re: [PATCH v2 10/10] media: tegra-video: Add support for x8 captures with gang ports To: Hans Verkuil , , CC: , , , References: <1603768763-25590-1-git-send-email-skomatineni@nvidia.com> <1603768763-25590-11-git-send-email-skomatineni@nvidia.com> <49b14870-66bd-7fa2-7dee-e70f449e81be@xs4all.nl> From: Sowjanya Komatineni Message-ID: Date: Mon, 9 Nov 2020 08:22:30 -0800 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.10.0 MIME-Version: 1.0 In-Reply-To: <49b14870-66bd-7fa2-7dee-e70f449e81be@xs4all.nl> Content-Type: text/plain; charset="utf-8"; format=flowed Content-Transfer-Encoding: 7bit Content-Language: en-US X-Originating-IP: [10.124.1.5] X-ClientProxiedBy: HQMAIL105.nvidia.com (172.20.187.12) To HQMAIL107.nvidia.com (172.20.187.13) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nvidia.com; s=n1; t=1604938943; bh=6FEVMOLe1jVD1bm/XKFPrVrKQZb8dFSeMYmG3j9H2yA=; h=Subject:To:CC:References:From:Message-ID:Date:User-Agent: MIME-Version:In-Reply-To:Content-Type:Content-Transfer-Encoding: Content-Language:X-Originating-IP:X-ClientProxiedBy; b=SblyS+UiThrnICM3sPg5O+9MOfn6RKNERekkYGoAMs/PAyXf2byZotpKYD3y6U65c 9vO7ZY5jj4G5fjhvTvXVs53rXyUeAF0ElTMwL80ja8BH9vGCzMrNbCpxQR/+y+cuEg BKqdwTIXpdXoRafoZNwnwYeVPJAWoLFpYrj2qVpLtnDARsWwK14WukrfKbBaClhSXL ZYC8PDhvnzsBMRpTmF+300Nme7X1sQjESDfC+gZOx+pODKI4mYa+dSoj41F3rpbkS/ VO0l7Ogj9JH0dBhYVhlw9CQ5cb96PYs0WRdVAy3si+A49KKrx1RJv2aAebhv0NB/d2 ld8/RIY44Bppg== Precedence: bulk List-ID: X-Mailing-List: linux-tegra@vger.kernel.org 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 >> --- >> 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 >> #include >> >> +#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