Devicetree
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Rishikesh Donadkar" <r-donadkar@ti.com>
Cc: devicetree@vger.kernel.org, conor+dt@kernel.org, robh@kernel.org
Subject: Re: [PATCH v13 12/17] media: ti: j721e-csi2rx: add multistream support
Date: Wed, 20 May 2026 12:48:32 +0000	[thread overview]
Message-ID: <20260520124832.87EA71F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260520120022.539913-13-r-donadkar@ti.com>

Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] Global pixel interface reset is unconditionally asserted on stream start failure, breaking other active multiplexed streams.
- [High] Fallback initialization for missing `get_frame_desc` support only configures the first context, breaking secondary multiplexed streams.
- [High] Incorrect NULL check on `media_entity_remote_source_pad_unique` leads to ERR_PTR dereference.
- [High] Failing to disable the upstream subdev permanently desynchronizes the driver's stream enable counter from the hardware state.
- [Medium] `ti_csi2rx_sd_set_routing` improperly rejects TRY routing configurations while streaming is active.
- [Medium] Unreachable error check inside `ti_csi2rx_get_vc_and_dt` loop prevents returning `-EINVAL` on stream mismatch.
- [Medium] `ti_csi2rx_sd_set_fmt` leaves state partially modified when returning error for unrouted streams.
--

commit e0451de187b4fd8470a4a9aee7ab731e7acd8ee7
Author: Jai Luthra <jai.luthra@linux.dev>

media: ti: j721e-csi2rx: add multistream support

Each CSI2 stream can be multiplexed into 32 independent streams, each
identified by its virtual channel number and data type. The incoming
data from these streams can be filtered on the basis of either the
virtual channel or the data type. This patch adds routing APIs to allow
application-specified data routing and handles pixel interface reset
assert/de-assert on stream start/stop.

Reviewed-by: Yemike Abhilash Chandra <y-abhilashchandra@ti.com>
Co-developed-by: Pratyush Yadav <p.yadav@ti.com>
Signed-off-by: Pratyush Yadav <p.yadav@ti.com>
Signed-off-by: Jai Luthra <j-luthra@ti.com>
Co-developed-by: Rishikesh Donadkar <r-donadkar@ti.com>
Signed-off-by: Rishikesh Donadkar <r-donadkar@ti.com>
Reviewed-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>

> diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
> index d0a681ba78ebf..1ec63715baf2c 100644
> --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
> +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c

[ ... ]

> @@ -887,34 +872,82 @@ static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb)
>  static int ti_csi2rx_get_stream(struct ti_csi2rx_ctx *ctx)
>  {
>  	struct ti_csi2rx_dev *csi = ctx->csi;
>  	struct media_pad *pad;
>  	struct v4l2_subdev_state *state;
>  	struct v4l2_subdev_route *r;
>  
>  	/* Get the source pad connected to this ctx */
>  	pad = media_entity_remote_source_pad_unique(ctx->pad.entity);
>  	if (!pad) {

[Severity: High]
Could this lead to an invalid pointer dereference?

Looking at media_entity_remote_source_pad_unique(), it returns an ERR_PTR
(like ERR_PTR(-ENOLINK)) on failure rather than NULL. Since error pointers
would bypass this !pad check, would they be immediately dereferenced below?

>  		dev_err(csi->dev, "No pad connected to ctx %d\n", ctx->idx);
>  		return -ENODEV;
>  	}
>  

[ ... ]

>  static int ti_csi2rx_get_vc_and_dt(struct ti_csi2rx_ctx *ctx)
>  {
>  	struct ti_csi2rx_dev *csi = ctx->csi;
> +	struct ti_csi2rx_ctx *curr_ctx;
>  	struct v4l2_mbus_frame_desc fd;
> -	struct media_pad *pad;
> -	int ret, i;
> +	struct media_pad *source_pad;
> +	const struct ti_csi2rx_fmt *fmt;
> +	int ret;
> +	unsigned int i, j;
>  
> -	pad = media_entity_remote_pad_unique(&csi->subdev.entity, MEDIA_PAD_FL_SOURCE);
> -	if (IS_ERR(pad))
> -		return PTR_ERR(pad);
> +	/* Get the frame desc from source */
> +	source_pad = media_entity_remote_pad_unique(&csi->subdev.entity, MEDIA_PAD_FL_SOURCE);
> +	if (IS_ERR(source_pad))
> +		return PTR_ERR(source_pad);
>  
> -	ret = v4l2_subdev_call(csi->source, pad, get_frame_desc, pad->index, &fd);
> -	if (ret)
> +	ret = v4l2_subdev_call(csi->source, pad, get_frame_desc, source_pad->index, &fd);
> +	if (ret) {
> +		if (ret == -ENOIOCTLCMD) {
> +			ctx->vc = 0;
> +			fmt = find_format_by_fourcc(ctx->v_fmt.fmt.pix.pixelformat);
> +			ctx->dt = fmt->csi_dt;
> +		}
>  		return ret;
> +	}

[Severity: High]
Does this fallback logic properly initialize multiplexed streams?

Because ti_csi2rx_get_vc_and_dt() is only called on the very first stream
start (when !csi->enable_count), if get_frame_desc returns -ENOIOCTLCMD,
only the current context's virtual channel and data type are configured.

Would subsequent multiplexed streams be left entirely uninitialized
(defaulting to 0) since this function won't be called again for them?

>  
>  	if (fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2)
>  		return -EINVAL;
>  
> -	for (i = 0; i < fd.num_entries; i++) {
> -		if (ctx->stream == fd.entry[i].stream) {
> -			ctx->vc = fd.entry[i].bus.csi2.vc;
> -			ctx->dt = fd.entry[i].bus.csi2.dt;
> -			break;
> -		}
> +	for (i = 0; i < csi->num_ctx; i++) {
> +		curr_ctx = &csi->ctx[i];
>  
> -		/* Return error if no matching stream found */
> -		if (i == fd.num_entries)
> -			return -EINVAL;
> +		/* Capture VC 0 by default */
> +		curr_ctx->vc = 0;
> +
> +		ret = ti_csi2rx_get_stream(curr_ctx);
> +		if (ret)
> +			continue;
> +
> +		for (j = 0; j < fd.num_entries; j++) {
> +			if (curr_ctx->stream == fd.entry[j].stream) {
> +				curr_ctx->vc = fd.entry[j].bus.csi2.vc;
> +				curr_ctx->dt = fd.entry[j].bus.csi2.dt;
> +				break;
> +			}
> +
> +			/* Return error if no matching stream found */
> +			if (j == fd.num_entries)
> +				return -EINVAL;

[Severity: Medium]
Is it possible to ever reach this error path?

Since this check is placed inside the body of the loop, j will always be
strictly less than fd.num_entries. If no matching stream is found, the
loop will naturally exit without ever returning -EINVAL.

> +		}
>  	}
>  
>  	return 0;

[ ... ]

> @@ -925,8 +958,6 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
>  	struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vq);
>  	struct ti_csi2rx_dev *csi = ctx->csi;
>  	struct ti_csi2rx_dma *dma = &ctx->dma;
> -	struct ti_csi2rx_buffer *buf;
> -	const struct ti_csi2rx_fmt *fmt;
>  	unsigned long flags;
>  	int ret = 0;
>  

[ ... ]

>  err_dma:
>  	ti_csi2rx_stop_dma(ctx);
> -err_pipeline:
>  	video_device_pipeline_stop(&ctx->vdev);
>  	writel(0, csi->shim + SHIM_CNTL);

[Severity: High]
Can this error path disrupt actively running streams?

If we fail to enable the stream in v4l2_subdev_enable_streams(), we
unconditionally write 0 to SHIM_CNTL here, which causes a global reset of
the pixel interface.

If one context is already streaming (enable_count > 0) and another context
fails to start, will this reset break the actively running stream?

>  	writel(0, csi->shim + SHIM_DMACNTX(ctx->idx));
> @@ -995,11 +999,8 @@ static void ti_csi2rx_stop_streaming(struct vb2_queue *vq)

[ ... ]

> @@ -1046,25 +1047,84 @@ static int ti_csi2rx_sd_set_fmt(struct v4l2_subdev *sd,
>  	fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);
>  	*fmt = format->format;
>  
> -	fmt = v4l2_subdev_state_get_format(state, TI_CSI2RX_PAD_FIRST_SOURCE,
> -					   format->stream);
> +	fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
> +							   format->stream);
> +	if (!fmt)
> +		return -EINVAL;

[Severity: Medium]
Does this leave the subdevice state partially modified on error?

The requested format is written to the state right before this step:

	fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);
	*fmt = format->format;

If v4l2_subdev_state_get_opposite_stream_format() fails (for example,
if the stream is unrouted), this returns -EINVAL while leaving the format
partially modified. Should we avoid updating the state until we know all
operations will succeed?

> +
>  	*fmt = format->format;
>  
>  	return 0;
>  }
>  

[ ... ]

> +static int ti_csi2rx_sd_set_routing(struct v4l2_subdev *sd,
> +				    struct v4l2_subdev_state *state,
> +				    enum v4l2_subdev_format_whence which,
> +				    struct v4l2_subdev_krouting *routing)
> +{
> +	struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd);
> +
> +	if (csi->enable_count > 0)
> +		return -EBUSY;

[Severity: Medium]
Is it intended to block TRY configurations while streaming?

If userspace calls this with which == V4L2_SUBDEV_FORMAT_TRY to probe
valid configurations while another stream is active, it will be rejected
with -EBUSY. Does the V4L2 API mandate that TRY operations must be allowed
regardless of the active streaming state?

> +
> +	return _ti_csi2rx_sd_set_routing(sd, state, routing);
> +}
> +

[ ... ]

>  static int ti_csi2rx_sd_enable_streams(struct v4l2_subdev *sd,
> @@ -1072,15 +1132,56 @@ static int ti_csi2rx_sd_enable_streams(struct v4l2_subdev *sd,
>  				       u32 pad, u64 streams_mask)
>  {

[ ... ]

>  	remote_pad = media_entity_remote_source_pad_unique(&csi->subdev.entity);
>  	if (!remote_pad)

[Severity: High]
Could this check miss an error pointer, similar to the instance in
ti_csi2rx_get_stream() above?

media_entity_remote_source_pad_unique() returns an ERR_PTR on failure, not
NULL. Will this bypass the !remote_pad check and cause a dereference issue?

>  		return -ENODEV;
> +	sink_streams = v4l2_subdev_state_xlate_streams(state, pad,
> +						       TI_CSI2RX_PAD_SINK,
> +						       &streams_mask);
>  
>  	ret = v4l2_subdev_enable_streams(csi->source, remote_pad->index,
> -					 BIT_U64(0));
> +					 sink_streams);
>  	if (ret)
>  		return ret;
>  
> @@ -1094,18 +1195,28 @@ static int ti_csi2rx_sd_disable_streams(struct v4l2_subdev *sd,
>  					u32 pad, u64 streams_mask)
>  {
>  	struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd);
> +	struct ti_csi2rx_ctx *ctx = &csi->ctx[pad - TI_CSI2RX_PAD_FIRST_SOURCE];
>  	struct media_pad *remote_pad;
> +	u64 sink_streams;
>  	int ret = 0;
>  
> +	WARN_ON(csi->enable_count == 0);
> +
> +	writel(0, csi->shim + SHIM_DMACNTX(ctx->idx));
> +
> +	/* assert pixel reset to prevent stale data */
> +	if (csi->enable_count == 1)
> +		writel(0, csi->shim + SHIM_CNTL);
> +
>  	remote_pad = media_entity_remote_source_pad_unique(&csi->subdev.entity);
>  	if (!remote_pad)
>  		return -ENODEV;
> -
> -	if (csi->enable_count == 0)
> -		return -EINVAL;
> +	sink_streams = v4l2_subdev_state_xlate_streams(state, pad,
> +						       TI_CSI2RX_PAD_SINK,
> +						       &streams_mask);
>  
>  	ret = v4l2_subdev_disable_streams(csi->source, remote_pad->index,
> -					  BIT_U64(0));
> +					  sink_streams);
>  	if (!ret)
>  		--csi->enable_count;
>  
> @@ -1114,6 +1225,7 @@ static int ti_csi2rx_sd_disable_streams(struct v4l2_subdev *sd,

[Severity: High]
Can this error path permanently desynchronize the enable counter?

If v4l2_subdev_disable_streams() fails, the --csi->enable_count line is
skipped, but the function still returns 0. Since the hardware has already
been put into reset (if enable_count was 1), the software counter will
remain > 0.

On the next start attempt, the driver will skip de-asserting the pixel reset
because it thinks a stream is already running, which could break capture
completely until the driver is reloaded.

>  
>  static const struct v4l2_subdev_pad_ops ti_csi2rx_subdev_pad_ops = {
>  	.enum_mbus_code	= ti_csi2rx_enum_mbus_code,

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260520120022.539913-1-r-donadkar@ti.com?part=12

  reply	other threads:[~2026-05-20 12:48 UTC|newest]

Thread overview: 35+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-20 12:00 [PATCH v13 00/17] media: cadence,ti: CSI2RX Multistream Support Rishikesh Donadkar
2026-05-20 12:00 ` [PATCH v13 01/17] media: ti: j721e-csi2rx: Remove word size alignment on frame width Rishikesh Donadkar
2026-05-20 12:27   ` sashiko-bot
2026-05-20 12:00 ` [PATCH v13 02/17] dt-bindings: media: ti,j721e-csi2rx-shim: Support 32 dma chans Rishikesh Donadkar
2026-05-20 12:00 ` [PATCH v13 03/17] media: ti: j721e-csi2rx: separate out device and context Rishikesh Donadkar
2026-05-20 12:37   ` sashiko-bot
2026-05-20 12:00 ` [PATCH v13 04/17] media: ti: j721e-csi2rx: prepare SHIM code for multiple contexts Rishikesh Donadkar
2026-05-20 12:00 ` [PATCH v13 05/17] media: ti: j721e-csi2rx: allocate DMA channel based on context index Rishikesh Donadkar
2026-05-20 12:32   ` sashiko-bot
2026-05-20 12:00 ` [PATCH v13 06/17] media: ti: j721e-csi2rx: add a subdev for the core device Rishikesh Donadkar
2026-05-20 12:28   ` Sakari Ailus
2026-05-20 12:49   ` sashiko-bot
2026-05-20 12:00 ` [PATCH v13 07/17] media: cadence: csi2rx: Move to .enable/disable_streams API Rishikesh Donadkar
2026-05-20 12:39   ` sashiko-bot
2026-05-20 12:00 ` [PATCH v13 08/17] media: ti: j721e-csi2rx: get number of contexts from device tree Rishikesh Donadkar
2026-05-20 12:45   ` sashiko-bot
2026-05-20 12:00 ` [PATCH v13 09/17] media: cadence: csi2rx: Add .get_frame_desc op Rishikesh Donadkar
2026-05-20 14:25   ` Jai Luthra
2026-05-20 12:00 ` [PATCH v13 10/17] media: ti: j721e-csi2rx: add support for processing virtual channels Rishikesh Donadkar
2026-05-20 12:44   ` sashiko-bot
2026-05-20 12:00 ` [PATCH v13 11/17] media: cadence: csi2rx: add multistream support Rishikesh Donadkar
2026-05-20 12:58   ` sashiko-bot
2026-05-20 12:00 ` [PATCH v13 12/17] media: ti: j721e-csi2rx: " Rishikesh Donadkar
2026-05-20 12:48   ` sashiko-bot [this message]
2026-05-20 12:00 ` [PATCH v13 13/17] media: ti: j721e-csi2rx: Submit all available buffers Rishikesh Donadkar
2026-05-20 12:59   ` sashiko-bot
2026-05-20 12:00 ` [PATCH v13 14/17] media: ti: j721e-csi2rx: Change the drain architecture for multistream Rishikesh Donadkar
2026-05-20 13:01   ` sashiko-bot
2026-05-20 12:00 ` [PATCH v13 15/17] media: cadence: csi2rx: Support runtime PM Rishikesh Donadkar
2026-05-20 13:00   ` sashiko-bot
2026-05-20 12:00 ` [PATCH v13 16/17] media: ti: j721e-csi2rx: Support runtime suspend Rishikesh Donadkar
2026-05-20 13:01   ` sashiko-bot
2026-05-20 12:00 ` [PATCH v13 17/17] media: ti: j721e-csi2rx: Support system suspend using pm_notifier Rishikesh Donadkar
2026-05-20 12:25   ` Sakari Ailus
2026-05-20 13:14   ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260520124832.87EA71F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=r-donadkar@ti.com \
    --cc=robh@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox