From: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
To: Hitesh Patel <hitesh@ebytelogic.com>, linux-media@vger.kernel.org
Cc: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>,
Loic Poulain <loic.poulain@oss.qualcomm.com>,
Mauro Carvalho Chehab <mchehab@kernel.org>,
linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org,
ravi@ebytelogic.com
Subject: Re: [PATCH 6/8] media: qcom: camss: refcount streaming on the shared CSIPHY and CSID
Date: Mon, 14 Sep 2026 20:13:14 +0100 [thread overview]
Message-ID: <f9c2a736-d7db-4d7f-9fb3-d8cd77d45c0a@linaro.org> (raw)
In-Reply-To: <20260914133416.1030231-7-hitesh@ebytelogic.com>
On 14/09/2026 14:34, Hitesh Patel wrote:
> video_start_streaming() walks the pipeline from the video node
> upstream and calls video.s_stream on every subdev it finds;
> video_stop_streaming() does the same to stop. The core allows one
> transition per subdev: call_s_stream() keeps a single
> sd->s_stream_enabled flag and warns about, and drops, a start of a
> subdev that is already started or a stop of one already stopped.
>
> That is correct for a subdev with a single user, but the CSIPHY and
> the CSID are shared when a CSI-2 transmitter aggregates several
> cameras onto one port. A MAX9296A GMSL deserializer sends two
> cameras on one CSI-2 output as two virtual channels; the CSID
> demultiplexes them to RDI0 and RDI1, each of which is its own VFE
> line, video node and thus pipeline, and both pipelines traverse the
> same CSIPHY and CSID. Starting the second camera hits the core check:
> the CSIPHY and CSID s_stream(1) are dropped with a WARN, and while
> the hardware happens to be already running, stopping the first
> camera then calls s_stream(0) on both and tears the CSIPHY and CSID
> down underneath the second camera, which stops receiving frames.
>
> Count the pipelines streaming through each CSIPHY and CSID and only
> forward the first start and the last stop to the subdev. The count
> is updated under the media graph mutex, which serialises the two
> pipelines' start/stop against each other. All other subdevs of the
> pipeline are driven exactly as before, so the ordinary one camera
> per port case does not change.
>
> csid_set_stream() programs every virtual channel of the en_vc mask
> in one go, so a single start already covers all demultiplexed RDIs;
> nothing needs to change on the CSID or CSIPHY side.
>
> Signed-off-by: Hitesh Patel <hitesh@ebytelogic.com>
> ---
> .../media/platform/qcom/camss/camss-csid.h | 2 +
> .../media/platform/qcom/camss/camss-csiphy.h | 2 +
> .../media/platform/qcom/camss/camss-video.c | 48 +++++++++++++++++--
> drivers/media/platform/qcom/camss/camss.c | 28 +++++++----
> drivers/media/platform/qcom/camss/camss.h | 2 +
> 5 files changed, 71 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h
> index 5296b10f6..9e612ae99 100644
> --- a/drivers/media/platform/qcom/camss/camss-csid.h
> +++ b/drivers/media/platform/qcom/camss/camss-csid.h
> @@ -167,6 +167,8 @@ struct csid_device {
> struct v4l2_ctrl_handler ctrls;
> struct v4l2_ctrl *testgen_mode;
> const struct csid_subdev_resources *res;
> + /* Number of pipelines streaming through this CSID */
> + unsigned int stream_users;
> };
>
> struct camss_subdev_resources;
> diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h
> index 9d9657b82..b920fe670 100644
> --- a/drivers/media/platform/qcom/camss/camss-csiphy.h
> +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h
> @@ -114,6 +114,8 @@ struct csiphy_device {
> struct v4l2_mbus_framefmt fmt[MSM_CSIPHY_PADS_NUM];
> const struct csiphy_subdev_resources *res;
> struct csiphy_device_regs *regs;
> + /* Number of pipelines streaming through this CSIPHY */
> + unsigned int stream_users;
> };
>
> struct camss_subdev_resources;
> diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c
> index 0852eb6f1..16c5f3748 100644
> --- a/drivers/media/platform/qcom/camss/camss-video.c
> +++ b/drivers/media/platform/qcom/camss/camss-video.c
> @@ -249,6 +249,49 @@ static int video_prepare_streaming(struct vb2_queue *q)
> return ret;
> }
>
> +/*
> + * video_subdev_set_stream - Start or stop a subdev of the pipeline
> + * @video: CAMSS video device
> + * @subdev: Subdevice to start or stop
> + * @enable: Start when true, stop when false
> + *
> + * CSIPHY and CSID are shared between pipelines when a transmitter aggregates
> + * several cameras onto one CSI-2 port. The core allows a single s_stream
> + * transition per subdev, so only forward the first start and the last stop
> + * to them. Every other subdev is driven unconditionally as before.
> + */
> +static int video_subdev_set_stream(struct camss_video *video,
> + struct v4l2_subdev *subdev, bool enable)
> +{
> + struct media_device *mdev = &video->camss->media_dev;
> + unsigned int *users;
> + bool forward;
> + int ret;
> +
> + users = camss_subdev_stream_users(video->camss, subdev);
> + if (!users)
> + return v4l2_subdev_call(subdev, video, s_stream, enable);
This logic is mad and genuinely hurts my head a bit to read.
> + mutex_lock(&mdev->graph_mutex);
> + if (enable)
> + forward = (*users)++ == 0;
> + else
> + forward = !WARN_ON(!*users) && --(*users) == 0;
> + mutex_unlock(&mdev->graph_mutex);
You can radically simplify the logic here to
if (enable) {
forward = *users == 0;
*users++;
} else {
forward = *users == 1;
*users--;
}
I checked ;)
[] ~ gcc -o zed zed.c
[] ~ ./zed
Enable users 0 = 1
Enable users simple 0 = 1
Disable users 0 = 0
Disable users simple 0 = 0
Enable users 1 = 0
Enable users simple 1 = 0
Disable users 1 = 1
Disable users simple 1 = 1
Enable users 2 = 0
Enable users simple 2 = 0
Disable users 2 = 0
Disable users simple 2 = 0
Enable users 3 = 0
Enable users simple 3 = 0
Disable users 3 = 0
Disable users simple 3 = 0
[] ~ cat zed.c
#include <stdio.h>
bool fw(unsigned int users, bool enable)
{
bool forward;
if (enable)
forward = (users)++ == 0;
else
forward = !(!users) && --(users) == 0;
return forward;
}
bool simple_fw(unsigned int users, bool enable)
{
if (enable)
return users == 0;
else
return users == 1;
}
int main(int argc, char *argv[])
{
unsigned int users[] = {0, 1, 2, 3};
int i;
for (i = 0; i < sizeof(users)/sizeof(unsigned int); i++) {
printf("Enable users %d = %d\n", users[i], fw(users[i], true));
printf("Enable users simple %d = %d\n", users[i], simple_fw(users[i],
true));
printf("Disable users %d = %d\n", users[i], fw(users[i], false));
printf("Disable users simple %d = %d\n", users[i], simple_fw(users[i],
false));
}
return 0;
}
In other words please make the code less cryptographic and more human
readable.
> +
> + if (!forward)
> + return 0;
> +
> + ret = v4l2_subdev_call(subdev, video, s_stream, enable);
> + if (enable && ret < 0 && ret != -ENOIOCTLCMD) {
> + mutex_lock(&mdev->graph_mutex);
> + (*users)--;
> + mutex_unlock(&mdev->graph_mutex);
> + }
So taking the graph_mutex here is to stop re-entrancy of two parallel
streams and only in this function too.
I have to say I distrust locking like this and its racy.
thread0: thread1:
mutex_lock();
*users = 1
forward = 1;
mutex_unlock();
sleep
mutex_lock();
*users == 1;
forward = 0;
mutex_unlock();
if (!forward)
return 0;
/* sometime later */
v4l2_subdev_call();
Thread1 has returned from set_stream in a non-error state but nothing
has been set to stream.
The set_stream() call associated with thread0 is reporting a truthful
state, the set_stream() call from thread1 is not.
Since this is the only ? place you want to modify *users you can add a
new mutex for that and hold it for the duration of the critical section.
> +
> + return ret;
> +}
> +
> static int video_start_streaming(struct vb2_queue *q, unsigned int count)
> {
> struct camss_video *video = vb2_get_drv_priv(q);
> @@ -281,7 +324,7 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count)
> entity = pad->entity;
> subdev = media_entity_to_v4l2_subdev(entity);
>
> - ret = v4l2_subdev_call(subdev, video, s_stream, 1);
> + ret = video_subdev_set_stream(video, subdev, true);
> if (ret < 0 && ret != -ENOIOCTLCMD)
> goto error;
> }
> @@ -319,8 +362,7 @@ static void video_stop_streaming(struct vb2_queue *q)
> entity = pad->entity;
> subdev = media_entity_to_v4l2_subdev(entity);
>
> - ret = v4l2_subdev_call(subdev, video, s_stream, 0);
> -
> + ret = video_subdev_set_stream(video, subdev, false);
> if (ret) {
> dev_err(video->camss->dev, "Video pipeline stop failed: %d\n", ret);
> return;
> diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
> index 4cf736d80..ca8101c2d 100644
> --- a/drivers/media/platform/qcom/camss/camss.c
> +++ b/drivers/media/platform/qcom/camss/camss.c
> @@ -4620,27 +4620,39 @@ struct media_pad *camss_find_sensor_pad(struct media_entity *entity)
> }
>
> /*
> - * camss_is_receiver_subdev - Test whether a subdev is a CAMSS CSI-2 receiver
> + * camss_subdev_stream_users - Streaming user count of a CAMSS receiver subdev
> * @camss: CAMSS device
> * @sd: Subdevice to test
> *
> - * Return true for a CSIPHY or CSID belonging to @camss, false for anything
> - * else, in particular for the external subdev transmitting to them.
> + * CSIPHY and CSID are traversed by several pipelines at once when a CSI-2
> + * transmitter aggregates several cameras onto one port: every virtual channel
> + * is demultiplexed to its own RDI and forms its own pipeline. The hardware
> + * must only be started by the first of them and stopped by the last.
> + *
> + * Return a pointer to the user count of @sd if it is a CSIPHY or CSID of
> + * @camss, NULL for any other subdev, in particular for the external subdev
> + * transmitting to them.
> */
> -static bool camss_is_receiver_subdev(struct camss *camss,
> - struct v4l2_subdev *sd)
> +unsigned int *camss_subdev_stream_users(struct camss *camss,
> + struct v4l2_subdev *sd)
I'm all for progressive changes but I wonder if you could have dispensed
with introducing camss_is_receiver_subdev() given you only added it ~ 2
patches ago.
> {
> unsigned int i;
You need to assert lockdep held for the new mutex here.
> for (i = 0; i < camss->res->csiphy_num; i++)
> if (sd == &camss->csiphy[i].subdev)
> - return true;
> + return &camss->csiphy[i].stream_users;
>
> for (i = 0; i < camss->res->csid_num; i++)
> if (sd == &camss->csid[i].subdev)
> - return true;
> + return &camss->csid[i].stream_users;
>
> - return false;
> + return NULL;
> +}
> +
> +static bool camss_is_receiver_subdev(struct camss *camss,
> + struct v4l2_subdev *sd)
> +{
> + return camss_subdev_stream_users(camss, sd);
> }
>
> /*
> diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h
> index 39ea33e61..00b8d5304 100644
> --- a/drivers/media/platform/qcom/camss/camss.h
> +++ b/drivers/media/platform/qcom/camss/camss.h
> @@ -167,6 +167,8 @@ void camss_add_clock_margin(u64 *rate);
> int camss_enable_clocks(int nclocks, struct camss_clock *clock,
> struct device *dev);
> void camss_disable_clocks(int nclocks, struct camss_clock *clock);
> +unsigned int *camss_subdev_stream_users(struct camss *camss,
> + struct v4l2_subdev *sd);
> struct media_pad *camss_find_sensor_pad(struct media_entity *entity);
> s64 camss_get_link_freq(struct camss *camss, struct media_entity *entity,
> unsigned int bpp, unsigned int lanes);
next prev parent reply other threads:[~2026-09-14 19:13 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-14 13:34 [PATCH 0/8] media: qcom: camss: support several cameras behind a CSI-2 bridge Hitesh Patel
2026-09-14 13:34 ` [PATCH 1/8] media: qcom: camss: take the link frequency from the CSI-2 transmitter Hitesh Patel
2026-09-14 14:58 ` Bryan O'Donoghue
2026-09-15 9:08 ` Hitesh Patel
2026-09-15 9:12 ` Bryan O'Donoghue
2026-09-14 13:34 ` [PATCH 2/8] media: qcom: camss: create the source to CSIPHY link per endpoint Hitesh Patel
2026-09-14 15:07 ` Bryan O'Donoghue
2026-09-15 9:08 ` Hitesh Patel
2026-09-14 13:34 ` [PATCH 3/8] media: qcom: camss: vfe-17x: do not gate write master done on IRQ_STATUS_0 Hitesh Patel
2026-09-14 15:18 ` Bryan O'Donoghue
2026-09-15 9:08 ` Hitesh Patel
2026-09-14 13:34 ` [PATCH 4/8] media: qcom: camss: vfe-17x: use the write master matching the RDI line Hitesh Patel
2026-09-14 15:21 ` Bryan O'Donoghue
2026-09-15 9:08 ` Hitesh Patel
2026-09-14 13:34 ` [PATCH 5/8] media: qcom: camss: vfe: only reset the VFE when its last line stops Hitesh Patel
2026-09-14 15:28 ` Bryan O'Donoghue
2026-09-15 9:08 ` Hitesh Patel
2026-09-14 13:34 ` [PATCH 6/8] media: qcom: camss: refcount streaming on the shared CSIPHY and CSID Hitesh Patel
2026-09-14 19:13 ` Bryan O'Donoghue [this message]
2026-09-15 9:08 ` Hitesh Patel
2026-09-14 13:34 ` [PATCH 7/8] media: qcom: camss: drive streams-aware transmitters through the streams API Hitesh Patel
2026-09-14 15:57 ` Loic Poulain
2026-09-14 19:16 ` Bryan O'Donoghue
2026-09-15 9:08 ` Hitesh Patel
2026-09-14 13:34 ` [PATCH 8/8] media: qcom: camss: enable only the stream of the pipeline's virtual channel Hitesh Patel
2026-09-14 19:17 ` Bryan O'Donoghue
2026-09-15 12:15 ` [PATCH v2 0/5] media: qcom: camss: fixes for several cameras behind a CSI-2 bridge Hitesh Patel
2026-09-15 12:15 ` [PATCH v2 1/5] media: qcom: camss: vfe-17x: Fix write master buffer done being dropped Hitesh Patel
2026-09-15 12:15 ` [PATCH v2 2/5] media: qcom: camss: vfe-17x: Fix write master selection for RDI lines Hitesh Patel
2026-09-15 12:15 ` [PATCH v2 3/5] media: qcom: camss: vfe: Fix VFE reset while another line is streaming Hitesh Patel
2026-09-15 12:15 ` [PATCH v2 4/5] media: qcom: camss: Take the link frequency from the CSI-2 transmitter Hitesh Patel
2026-09-15 12:15 ` [PATCH v2 5/5] media: qcom: camss: Create the source to CSIPHY link per endpoint Hitesh Patel
2026-09-25 11:45 ` [PATCH v2 0/5] media: qcom: camss: fixes for several cameras behind a CSI-2 bridge Hitesh Patel
2026-09-26 9:48 ` Bryan O'Donoghue
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=f9c2a736-d7db-4d7f-9fb3-d8cd77d45c0a@linaro.org \
--to=bryan.odonoghue@linaro.org \
--cc=hitesh@ebytelogic.com \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=loic.poulain@oss.qualcomm.com \
--cc=mchehab@kernel.org \
--cc=ravi@ebytelogic.com \
--cc=vladimir.zapolskiy@linaro.org \
/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
all inboxes | Powered by JetHome®