* [PATCH 0/8] media: qcom: camss: support several cameras behind a CSI-2 bridge
@ 2026-09-14 13:34 Hitesh Patel
2026-09-14 13:34 ` [PATCH 1/8] media: qcom: camss: take the link frequency from the CSI-2 transmitter Hitesh Patel
` (8 more replies)
0 siblings, 9 replies; 34+ messages in thread
From: Hitesh Patel @ 2026-09-14 13:34 UTC (permalink / raw)
To: linux-media
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, linux-arm-msm, linux-kernel, ravi,
Hitesh Patel
This series contains the CAMSS changes needed to run two GMSL cameras
on the RB3 Gen2 (QCS6490 / SC7280) vision mezzanine, where a MAX9296A
deserializer sits between the sensors and the SoC. The deserializer and
serializer drivers (the out-of-tree maxim-serdes work for MAX9296A and
MAX96717) and the AR0234/IMX900 sensor drivers are out of tree and not
part of this submission; only the SoC side is here.
Everything CAMSS has seen so far is one sensor wired straight to one
CSIPHY. A CSI-2 to CSI-2 bridge breaks several assumptions that follow
from that, and both bridge topologies were exercised:
- two cameras aggregated on ONE CSI-2 port as two virtual channels,
demultiplexed by the CSID to RDI0/RDI1 of the same VFE. The CSIPHY
and CSID are then shared by two pipelines (patches 5, 6, 8) and the
VFE 17x has two write masters active (patches 3, 4);
- two cameras on the deserializer's TWO CSI-2 ports, wired to two
CSIPHYs. The same subdev then binds for two endpoints (patch 2) and
heads two independent pipelines (patch 7).
Patch 1 addresses the assumption that the sensor is the CSI-2
transmitter: the rate the receiver has to be programmed for belongs to
whatever drives the bus, and v4l2_get_link_freq() already knows how to
ask it.
Patches 3, 4 and 5 are bug fixes in their own right and are not
specific to a bridge: they are hit by any configuration where two RDI
lines of one VFE stream at the same time.
A CCI fix found during the same bring-up, enabling SCL clock stretching
in standard mode so a sensor reached through the bridge's I2C tunnel
can hold the clock, has been sent separately to linux-i2c. It is
independent of this series.
Tested on RB3 Gen2 with AR0234 and IMX900 cameras on MAX96717
serializers, on the vendor 6.18 tree and on the qualcomm-linux qcom-next
branch (v7.2 based): both cameras streaming concurrently on the two
CSI-2 ports, and each camera started and stopped repeatedly while the
other keeps streaming, without interference. The single-port
virtual-channel aggregation topology is what the original out-of-tree
work targeted; it could not be re-validated in the current state of the
out-of-tree serializer and sensor drivers, so patches 6-8 should be
read with that in mind. Each patch builds and was bisected against
next-20260911 for arm64.
Hitesh Patel (8):
media: qcom: camss: take the link frequency from the CSI-2 transmitter
media: qcom: camss: create the source to CSIPHY link per endpoint
media: qcom: camss: vfe-17x: do not gate write master done on
IRQ_STATUS_0
media: qcom: camss: vfe-17x: use the write master matching the RDI
line
media: qcom: camss: vfe: only reset the VFE when its last line stops
media: qcom: camss: refcount streaming on the shared CSIPHY and CSID
media: qcom: camss: drive streams-aware transmitters through the
streams API
media: qcom: camss: enable only the stream of the pipeline's virtual
channel
.../media/platform/qcom/camss/camss-csid.c | 2 +-
.../media/platform/qcom/camss/camss-csid.h | 2 +
.../media/platform/qcom/camss/camss-csiphy.c | 6 +-
.../media/platform/qcom/camss/camss-csiphy.h | 2 +
.../media/platform/qcom/camss/camss-vfe-17x.c | 33 ++--
drivers/media/platform/qcom/camss/camss-vfe.c | 8 +
.../media/platform/qcom/camss/camss-video.c | 141 ++++++++++++++-
drivers/media/platform/qcom/camss/camss.c | 167 +++++++++++++-----
drivers/media/platform/qcom/camss/camss.h | 8 +-
9 files changed, 310 insertions(+), 59 deletions(-)
base-commit: 68142f986ff04b2b70b31db00f719bf690f64a9a
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH 1/8] media: qcom: camss: take the link frequency from the CSI-2 transmitter
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 ` Hitesh Patel
2026-09-14 14:58 ` Bryan O'Donoghue
2026-09-14 13:34 ` [PATCH 2/8] media: qcom: camss: create the source to CSIPHY link per endpoint Hitesh Patel
` (7 subsequent siblings)
8 siblings, 1 reply; 34+ messages in thread
From: Hitesh Patel @ 2026-09-14 13:34 UTC (permalink / raw)
To: linux-media
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, linux-arm-msm, linux-kernel, ravi,
Hitesh Patel
camss_get_link_freq() walks the pipeline to an entity whose function
is MEDIA_ENT_F_CAM_SENSOR and reads the link frequency from there, to
derive the CSIPHY settle count and the CSID clock.
The rate the receiver has to be programmed for is the rate on the
CSI-2 bus, which is a property of whatever drives that bus, not of
the sensor at the far end of the pipeline. The two coincide only when
the sensor is wired directly to the CSIPHY. With a CSI-2 to CSI-2
bridge in between, a GMSL or FPD-Link deserializer for instance, the
bridge re-times the stream onto its own output: it may aggregate
several sensors onto one link, forward a single sensor at a different
rate, or generate a test pattern with no sensor involved at all. The
sensor's rate is then simply not what arrives at the SoC, and the
PHY does not lock.
Walking to a MEDIA_ENT_F_CAM_SENSOR also fails outright on a
deserializer that has one sink pad per serial link: the walk follows
pad 0, while the sensor may be attached to any of the other sink
pads, and streaming is refused with "Cannot get CSI2 transmitter's
link frequency".
Stop the walk at the first entity that is not a CAMSS receiver, i.e.
at the external subdev feeding the CSIPHY, and query that pad with
v4l2_get_link_freq(). This is what the helper is for: it asks the
transmitter through .get_mbus_config first and falls back to its
V4L2_CID_LINK_FREQ, then V4L2_CID_PIXEL_RATE controls.
For a sensor connected straight to a CSIPHY the transmitter is the
sensor itself, so the pad found and the value returned are the same
as before.
camss_find_sensor_pad() keeps walking to the sensor: its other users,
camss_get_pixel_clock() and the frame skip query, do want the sensor.
Signed-off-by: Hitesh Patel <hitesh@ebytelogic.com>
---
.../media/platform/qcom/camss/camss-csid.c | 2 +-
.../media/platform/qcom/camss/camss-csiphy.c | 6 +-
drivers/media/platform/qcom/camss/camss.c | 74 +++++++++++++++++--
drivers/media/platform/qcom/camss/camss.h | 4 +-
4 files changed, 73 insertions(+), 13 deletions(-)
diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c
index 48459b46a..c631119e2 100644
--- a/drivers/media/platform/qcom/camss/camss-csid.c
+++ b/drivers/media/platform/qcom/camss/camss-csid.c
@@ -546,7 +546,7 @@ static int csid_set_clock_rates(struct csid_device *csid)
fmt = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats,
csid->fmt[MSM_CSIPHY_PAD_SINK].code);
- link_freq = camss_get_link_freq(&csid->subdev.entity, fmt->bpp,
+ link_freq = camss_get_link_freq(csid->camss, &csid->subdev.entity, fmt->bpp,
csid->phy.lane_cnt);
if (link_freq < 0)
link_freq = 0;
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c
index 539ac4888..000fde129 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.c
@@ -145,7 +145,8 @@ static int csiphy_set_clock_rates(struct csiphy_device *csiphy)
csiphy->fmt[MSM_CSIPHY_PAD_SINK].code);
u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data;
- link_freq = camss_get_link_freq(&csiphy->subdev.entity, bpp, num_lanes);
+ link_freq = camss_get_link_freq(csiphy->camss, &csiphy->subdev.entity,
+ bpp, num_lanes);
if (link_freq < 0)
link_freq = 0;
@@ -272,7 +273,8 @@ static int csiphy_stream_on(struct csiphy_device *csiphy)
u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data;
u8 val;
- link_freq = camss_get_link_freq(&csiphy->subdev.entity, bpp, num_lanes);
+ link_freq = camss_get_link_freq(csiphy->camss, &csiphy->subdev.entity,
+ bpp, num_lanes);
if (link_freq < 0) {
dev_err(csiphy->camss->dev,
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 2123f6388..16ad1c26c 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -4619,24 +4619,82 @@ 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: 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.
+ */
+static bool camss_is_receiver_subdev(struct camss *camss,
+ struct v4l2_subdev *sd)
+{
+ unsigned int i;
+
+ for (i = 0; i < camss->res->csiphy_num; i++)
+ if (sd == &camss->csiphy[i].subdev)
+ return true;
+
+ for (i = 0; i < camss->res->csid_num; i++)
+ if (sd == &camss->csid[i].subdev)
+ return true;
+
+ return false;
+}
+
+/*
+ * camss_find_transmitter_pad - Find the pad of the CSI-2 transmitter
+ * @camss: CAMSS device
+ * @entity: Media entity in the current pipeline
+ *
+ * Walk the pipeline upstream through the CAMSS receiver subdevs and return the
+ * source pad of the first entity that is not one of them: the CSI-2
+ * transmitter driving the SoC.
+ *
+ * Return a pointer to the transmitter media pad or NULL if not found
+ */
+static struct media_pad *camss_find_transmitter_pad(struct camss *camss,
+ struct media_entity *entity)
+{
+ struct media_pad *pad;
+
+ while (1) {
+ pad = &entity->pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ return NULL;
+
+ pad = media_pad_remote_pad_first(pad);
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+ return NULL;
+
+ entity = pad->entity;
+
+ if (!camss_is_receiver_subdev(camss,
+ media_entity_to_v4l2_subdev(entity)))
+ return pad;
+ }
+}
+
/**
- * camss_get_link_freq - Get link frequency from sensor
+ * camss_get_link_freq - Get link frequency from the CSI-2 transmitter
+ * @camss: CAMSS device
* @entity: Media entity in the current pipeline
* @bpp: Number of bits per pixel for the current format
- * @lanes: Number of lanes in the link to the sensor
+ * @lanes: Number of lanes in the link to the transmitter
*
* Return link frequency on success or a negative error code otherwise
*/
-s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp,
- unsigned int lanes)
+s64 camss_get_link_freq(struct camss *camss, struct media_entity *entity,
+ unsigned int bpp, unsigned int lanes)
{
- struct media_pad *sensor_pad;
+ struct media_pad *tx_pad;
- sensor_pad = camss_find_sensor_pad(entity);
- if (!sensor_pad)
+ tx_pad = camss_find_transmitter_pad(camss, entity);
+ if (!tx_pad)
return -ENODEV;
- return v4l2_get_link_freq(sensor_pad, bpp, 2 * lanes);
+ return v4l2_get_link_freq(tx_pad, bpp, 2 * lanes);
}
/*
diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h
index 93d691c8a..39ea33e61 100644
--- a/drivers/media/platform/qcom/camss/camss.h
+++ b/drivers/media/platform/qcom/camss/camss.h
@@ -168,8 +168,8 @@ int camss_enable_clocks(int nclocks, struct camss_clock *clock,
struct device *dev);
void camss_disable_clocks(int nclocks, struct camss_clock *clock);
struct media_pad *camss_find_sensor_pad(struct media_entity *entity);
-s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp,
- unsigned int lanes);
+s64 camss_get_link_freq(struct camss *camss, struct media_entity *entity,
+ unsigned int bpp, unsigned int lanes);
int camss_get_pixel_clock(struct media_entity *entity, u64 *pixel_clock);
int camss_pm_domain_on(struct camss *camss, int id);
void camss_pm_domain_off(struct camss *camss, int id);
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH 2/8] media: qcom: camss: create the source to CSIPHY link per endpoint
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 13:34 ` Hitesh Patel
2026-09-14 15:07 ` Bryan O'Donoghue
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
` (6 subsequent siblings)
8 siblings, 1 reply; 34+ messages in thread
From: Hitesh Patel @ 2026-09-14 13:34 UTC (permalink / raw)
To: linux-media
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, linux-arm-msm, linux-kernel, ravi,
Hitesh Patel
The link from the external CSI-2 transmitter to the CSIPHY is created
in the notifier .complete() callback by walking every registered
subdev, reading the CSIPHY it was bound to from sd->host_priv and
linking the subdev's first source pad to that CSIPHY.
This assumes one transmitter feeds exactly one CSIPHY. A GMSL
deserializer such as the MAX9296A has two independent CSI-2 output
ports which, on the RB3 Gen2 vision mezzanine, are wired to two
different SoC CSIPHYs. The same subdev is then bound once per CAMSS
port endpoint, the second .bound() overwrites host_priv, and
.complete() creates a single link from source pad 0 to the last
CSIPHY. The second output port is left with no link at all, so a
second camera can never be routed to the SoC.
Move the link creation into .bound(), where both the endpoint and
the CSIPHY are known, and resolve the transmitter's source pad from
the endpoint fwnode with media_entity_get_fwnode_pad(). Each
endpoint then gets its own link between the right source pad and
the right CSIPHY.
For a subdev that does not implement .get_fwnode_pad,
media_entity_get_fwnode_pad() falls back to the first pad matching
the requested direction, which is exactly what the .complete() loop
did, so ordinary single-output sensors keep the same link as before.
Signed-off-by: Hitesh Patel <hitesh@ebytelogic.com>
---
drivers/media/platform/qcom/camss/camss.c | 51 ++++++++---------------
1 file changed, 18 insertions(+), 33 deletions(-)
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 16ad1c26c..4cf736d80 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -5231,49 +5231,34 @@ static int camss_subdev_notifier_bound(struct v4l2_async_notifier *async,
container_of(asd, struct camss_async_subdev, asd);
u8 id = csd->interface.csiphy_id;
struct csiphy_device *csiphy = &camss->csiphy[id];
+ struct media_entity *input = &csiphy->subdev.entity;
+ struct media_entity *sensor = &subdev->entity;
+ int pad, ret;
csiphy->cfg.csi2 = &csd->interface.csi2;
subdev->host_priv = csiphy;
+ pad = media_entity_get_fwnode_pad(sensor, asd->match.fwnode,
+ MEDIA_PAD_FL_SOURCE);
+ if (pad < 0) {
+ dev_err(camss->dev, "No source pad in external entity %s: %d\n",
+ sensor->name, pad);
+ return pad;
+ }
+
+ ret = media_create_pad_link(sensor, pad, input, MSM_CSIPHY_PAD_SINK,
+ MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
+ if (ret < 0) {
+ camss_link_err(camss, sensor->name, input->name, ret);
+ return ret;
+ }
+
return 0;
}
static int camss_subdev_notifier_complete(struct v4l2_async_notifier *async)
{
struct camss *camss = container_of(async, struct camss, notifier);
- struct v4l2_device *v4l2_dev = &camss->v4l2_dev;
- struct v4l2_subdev *sd;
-
- list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
- struct csiphy_device *csiphy = sd->host_priv;
- struct media_entity *input, *sensor;
- unsigned int i;
- int ret;
-
- if (!csiphy)
- continue;
-
- input = &csiphy->subdev.entity;
- sensor = &sd->entity;
-
- for (i = 0; i < sensor->num_pads; i++) {
- if (sensor->pads[i].flags & MEDIA_PAD_FL_SOURCE)
- break;
- }
- if (i == sensor->num_pads) {
- dev_err(camss->dev,
- "No source pad in external entity\n");
- return -EINVAL;
- }
-
- ret = media_create_pad_link(sensor, i, input,
- MSM_CSIPHY_PAD_SINK,
- MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
- if (ret < 0) {
- camss_link_err(camss, sensor->name, input->name, ret);
- return ret;
- }
- }
return v4l2_device_register_subdev_nodes(&camss->v4l2_dev);
}
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH 3/8] media: qcom: camss: vfe-17x: do not gate write master done on IRQ_STATUS_0
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 13:34 ` [PATCH 2/8] media: qcom: camss: create the source to CSIPHY link per endpoint Hitesh Patel
@ 2026-09-14 13:34 ` Hitesh Patel
2026-09-14 15:18 ` Bryan O'Donoghue
2026-09-14 13:34 ` [PATCH 4/8] media: qcom: camss: vfe-17x: use the write master matching the RDI line Hitesh Patel
` (5 subsequent siblings)
8 siblings, 1 reply; 34+ messages in thread
From: Hitesh Patel @ 2026-09-14 13:34 UTC (permalink / raw)
To: linux-media
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, linux-arm-msm, linux-kernel, ravi,
Hitesh Patel
The VFE 17x interrupt handler reads and clears IRQ_STATUS_0/1 and
then reads and clears every BUS_IRQ_STATUS register, unconditionally.
It only acts on the per write master WM_CLIENT_BUF_DONE bits from
BUS_IRQ_STATUS(1), however, when bit 9 of the previously sampled
IRQ_STATUS_0 was set. That bit is the ping-pong flag of image master
1, which says nothing about the other masters.
Those two reads are not atomic either. A buffer done that becomes
pending after IRQ_STATUS_0 has been sampled but before
BUS_IRQ_STATUS(1) is read, for example while the handler is entered
for another line's RDI SOF or REG_UPDATE, is cleared by the bus
status read and then dropped by the gate: wm_done() is never called
for it, the buffer is never returned to userspace and that line
stalls until the next frame happens to line up with the gate again.
With a single RDI streaming the window is rarely hit. As soon as a
second RDI of the same VFE streams, which is the case when a CSID
demultiplexes two virtual channels to RDI0 and RDI1, the interrupt
rate doubles and one of the two lines loses buffer done events
continuously.
The bus status is read-to-clear in this handler, so once read it is
the authoritative record of which write masters completed. Act on it
directly, as the gen2 VFE handler does, and drop the gate.
The write master of a PIX line is skipped: its buffers are completed
from the composite done above, through vfe_isr_comp_done(), and
completing them here as well would return two buffers per frame.
Signed-off-by: Hitesh Patel <hitesh@ebytelogic.com>
---
drivers/media/platform/qcom/camss/camss-vfe-17x.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-17x.c b/drivers/media/platform/qcom/camss/camss-vfe-17x.c
index e5ee7e717..f62fdabbc 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-17x.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-17x.c
@@ -363,10 +363,13 @@ static irqreturn_t vfe_isr(int irq, void *dev)
if (vfe_bus_status[0] & STATUS0_COMP_BUF_DONE(i))
vfe->isr_ops.comp_done(vfe, i);
- for (wm = 0; wm < MSM_VFE_IMAGE_MASTERS_NUM; wm++)
- if (status0 & BIT(9))
- if (vfe_bus_status[1] & STATUS1_WM_CLIENT_BUF_DONE(wm))
- vfe->isr_ops.wm_done(vfe, wm);
+ for (wm = 0; wm < MSM_VFE_IMAGE_MASTERS_NUM; wm++) {
+ if (vfe->wm_output_map[wm] == VFE_LINE_PIX)
+ continue;
+
+ if (vfe_bus_status[1] & STATUS1_WM_CLIENT_BUF_DONE(wm))
+ vfe->isr_ops.wm_done(vfe, wm);
+ }
return IRQ_HANDLED;
}
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH 4/8] media: qcom: camss: vfe-17x: use the write master matching the RDI line
2026-09-14 13:34 [PATCH 0/8] media: qcom: camss: support several cameras behind a CSI-2 bridge Hitesh Patel
` (2 preceding siblings ...)
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 13:34 ` Hitesh Patel
2026-09-14 15:21 ` Bryan O'Donoghue
2026-09-14 13:34 ` [PATCH 5/8] media: qcom: camss: vfe: only reset the VFE when its last line stops Hitesh Patel
` (4 subsequent siblings)
8 siblings, 1 reply; 34+ messages in thread
From: Hitesh Patel @ 2026-09-14 13:34 UTC (permalink / raw)
To: linux-media
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, linux-arm-msm, linux-kernel, ravi,
Hitesh Patel
vfe_get_output() on VFE 17x reserves a write master with
vfe_reserve_wm(), which hands out the first free entry of
wm_output_map. That only coincides with the line being started when
lines are started in order and RDI0 goes first. There is no crossbar
programmed on this bus: RDI n feeds bus write master client n, so
the write master an RDI line uses is not a free choice.
The mismatch is not visible as long as a single line of the VFE is
streaming: RDI0 always gets write master 0. It breaks when a CSID
demultiplexes two virtual channels to RDI0 and RDI1 on the same VFE.
Whichever line starts second is handed the other line's write
master, and both clients are then programmed with the wrong buffer
address and frame size. Frames are truncated at the smaller of the
two buffer sizes and the SMMU faults on the overrun of the larger
one.
Reserve write master line->id for RDI lines, as the gen2 VFE path in
vfe_get_output_v2() already does, and fail if it is in use. A PIX
line is not an RDI and its write master is not fixed by the same
rule, so it keeps using vfe_reserve_wm().
This also removes the error_get_wm label, which released
output->wm_idx[0] before it had been assigned.
Signed-off-by: Hitesh Patel <hitesh@ebytelogic.com>
---
.../media/platform/qcom/camss/camss-vfe-17x.c | 22 +++++++++++++------
1 file changed, 15 insertions(+), 7 deletions(-)
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-17x.c b/drivers/media/platform/qcom/camss/camss-vfe-17x.c
index f62fdabbc..0cfc24255 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-17x.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-17x.c
@@ -403,10 +403,20 @@ static int vfe_get_output(struct vfe_line *line)
output->wm_num = 1;
- wm_idx = vfe_reserve_wm(vfe, line->id);
- if (wm_idx < 0) {
- dev_err(vfe->camss->dev, "Can not reserve wm\n");
- goto error_get_wm;
+ if (line->id == VFE_LINE_PIX) {
+ wm_idx = vfe_reserve_wm(vfe, line->id);
+ if (wm_idx < 0) {
+ dev_err(vfe->camss->dev, "Can not reserve wm\n");
+ goto error;
+ }
+ } else {
+ wm_idx = line->id;
+ if (vfe->wm_output_map[wm_idx] != VFE_LINE_NONE) {
+ dev_err(vfe->camss->dev, "Can not reserve wm %d\n",
+ wm_idx);
+ goto error;
+ }
+ vfe->wm_output_map[wm_idx] = line->id;
}
output->wm_idx[0] = wm_idx;
@@ -416,10 +426,8 @@ static int vfe_get_output(struct vfe_line *line)
return 0;
-error_get_wm:
- vfe_release_wm(vfe, output->wm_idx[0]);
- output->state = VFE_OUTPUT_OFF;
error:
+ output->state = VFE_OUTPUT_OFF;
spin_unlock_irqrestore(&vfe->output_lock, flags);
return -EINVAL;
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH 5/8] media: qcom: camss: vfe: only reset the VFE when its last line stops
2026-09-14 13:34 [PATCH 0/8] media: qcom: camss: support several cameras behind a CSI-2 bridge Hitesh Patel
` (3 preceding siblings ...)
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 13:34 ` Hitesh Patel
2026-09-14 15:28 ` Bryan O'Donoghue
2026-09-14 13:34 ` [PATCH 6/8] media: qcom: camss: refcount streaming on the shared CSIPHY and CSID Hitesh Patel
` (3 subsequent siblings)
8 siblings, 1 reply; 34+ messages in thread
From: Hitesh Patel @ 2026-09-14 13:34 UTC (permalink / raw)
To: linux-media
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, linux-arm-msm, linux-kernel, ravi,
Hitesh Patel
vfe_disable_output() stops the write masters of the line being
disabled and then issues a global VFE reset. The reset is not scoped
to the line: it resets the whole block.
Several lines of one VFE stream at the same time when a CSID
demultiplexes virtual channels to different RDIs, e.g. two GMSL
cameras aggregated by a MAX9296A onto one CSI-2 port, each on its
own VC and RDI. Stopping one of them then resets the VFE underneath
the other: its in-flight buffers are never completed, its write
master configuration is gone and the block is left in a state where
the next reset is not acknowledged, which surfaces as
VFE reset timeout
when the remaining camera is stopped or restarted.
Only issue the reset when the line being disabled is the last one
streaming on this VFE, as tracked by vfe->stream_count. Stopping the
line's write masters is sufficient to quiesce it while other lines
keep running. The single-line case resets exactly as before.
Signed-off-by: Hitesh Patel <hitesh@ebytelogic.com>
---
drivers/media/platform/qcom/camss/camss-vfe.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c
index 319d19158..9cdf26671 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe.c
@@ -814,6 +814,7 @@ static int vfe_disable_output(struct vfe_line *line)
struct vfe_output *output = &line->output;
unsigned long flags;
unsigned int i;
+ bool last;
spin_lock_irqsave(&vfe->output_lock, flags);
for (i = 0; i < output->wm_num; i++)
@@ -821,6 +822,13 @@ static int vfe_disable_output(struct vfe_line *line)
output->gen2.active_num = 0;
spin_unlock_irqrestore(&vfe->output_lock, flags);
+ mutex_lock(&vfe->stream_lock);
+ last = vfe->stream_count == 1;
+ mutex_unlock(&vfe->stream_lock);
+
+ if (!last)
+ return 0;
+
return vfe_reset(vfe);
}
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH 6/8] media: qcom: camss: refcount streaming on the shared CSIPHY and CSID
2026-09-14 13:34 [PATCH 0/8] media: qcom: camss: support several cameras behind a CSI-2 bridge Hitesh Patel
` (4 preceding siblings ...)
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 13:34 ` Hitesh Patel
2026-09-14 19:13 ` Bryan O'Donoghue
2026-09-14 13:34 ` [PATCH 7/8] media: qcom: camss: drive streams-aware transmitters through the streams API Hitesh Patel
` (2 subsequent siblings)
8 siblings, 1 reply; 34+ messages in thread
From: Hitesh Patel @ 2026-09-14 13:34 UTC (permalink / raw)
To: linux-media
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, linux-arm-msm, linux-kernel, ravi,
Hitesh Patel
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);
+
+ mutex_lock(&mdev->graph_mutex);
+ if (enable)
+ forward = (*users)++ == 0;
+ else
+ forward = !WARN_ON(!*users) && --(*users) == 0;
+ mutex_unlock(&mdev->graph_mutex);
+
+ 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);
+ }
+
+ 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)
{
unsigned int i;
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);
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH 7/8] media: qcom: camss: drive streams-aware transmitters through the streams API
2026-09-14 13:34 [PATCH 0/8] media: qcom: camss: support several cameras behind a CSI-2 bridge Hitesh Patel
` (5 preceding siblings ...)
2026-09-14 13:34 ` [PATCH 6/8] media: qcom: camss: refcount streaming on the shared CSIPHY and CSID Hitesh Patel
@ 2026-09-14 13:34 ` Hitesh Patel
2026-09-14 15:57 ` Loic Poulain
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-15 12:15 ` [PATCH v2 0/5] media: qcom: camss: fixes for several cameras behind a CSI-2 bridge Hitesh Patel
8 siblings, 1 reply; 34+ messages in thread
From: Hitesh Patel @ 2026-09-14 13:34 UTC (permalink / raw)
To: linux-media
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, linux-arm-msm, linux-kernel, ravi,
Hitesh Patel
video_start_streaming() and video_stop_streaming() drive every subdev
of the pipeline through video.s_stream. That op is per subdev, and
the core allows one transition per subdev, so a transmitter that is
shared by two independent pipelines can only be started once and is
stopped by the first pipeline that stops.
A GMSL deserializer with two CSI-2 output ports, one camera routed to
each, is exactly that case: the same subdev sits at the head of two
pipelines that otherwise share nothing (each has its own CSIPHY, CSID
and VFE). The second camera never starts, and stopping either one
kills the other.
Such transmitters implement the V4L2 streams API and advertise it
with V4L2_SUBDEV_FL_STREAMS. For those, enable or disable only the
streams routed to the source pad the pipeline arrived through, using
v4l2_subdev_enable_streams()/v4l2_subdev_disable_streams(). The core
refcounts enabled streams per pad, so the two pipelines no longer
interfere. Enabling streams on a source pad propagates upstream to
the sensor by itself, so the walk ends there rather than starting
the rest of the chain a second time through s_stream.
Subdevs without the flag keep using video.s_stream exactly as
before.
Signed-off-by: Hitesh Patel <hitesh@ebytelogic.com>
---
.../media/platform/qcom/camss/camss-video.c | 56 +++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c
index 16c5f3748..90f22ce76 100644
--- a/drivers/media/platform/qcom/camss/camss-video.c
+++ b/drivers/media/platform/qcom/camss/camss-video.c
@@ -249,6 +249,32 @@ static int video_prepare_streaming(struct vb2_queue *q)
return ret;
}
+/*
+ * video_source_pad_streams - Streams routed to a subdev source pad
+ * @sd: Streams-aware subdevice
+ * @pad: Source pad index on @sd
+ *
+ * Return the mask of streams of the active routes ending on @pad.
+ */
+static u64 video_source_pad_streams(struct v4l2_subdev *sd, u32 pad)
+{
+ struct v4l2_subdev_state *state;
+ struct v4l2_subdev_route *route;
+ u64 mask = 0;
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+ if (!state)
+ return 0;
+
+ for_each_active_route(&state->routing, route)
+ if (route->source_pad == pad)
+ mask |= BIT_ULL(route->source_stream);
+
+ v4l2_subdev_unlock_state(state);
+
+ return mask;
+}
+
/*
* video_subdev_set_stream - Start or stop a subdev of the pipeline
* @video: CAMSS video device
@@ -324,6 +350,20 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count)
entity = pad->entity;
subdev = media_entity_to_v4l2_subdev(entity);
+ if (subdev->flags & V4L2_SUBDEV_FL_STREAMS) {
+ u64 mask = video_source_pad_streams(subdev, pad->index);
+
+ if (!mask)
+ break;
+
+ ret = v4l2_subdev_enable_streams(subdev, pad->index,
+ mask);
+ if (ret && ret != -EALREADY)
+ goto error;
+
+ break;
+ }
+
ret = video_subdev_set_stream(video, subdev, true);
if (ret < 0 && ret != -ENOIOCTLCMD)
goto error;
@@ -362,6 +402,22 @@ static void video_stop_streaming(struct vb2_queue *q)
entity = pad->entity;
subdev = media_entity_to_v4l2_subdev(entity);
+ if (subdev->flags & V4L2_SUBDEV_FL_STREAMS) {
+ u64 mask = video_source_pad_streams(subdev, pad->index);
+
+ if (!mask)
+ break;
+
+ ret = v4l2_subdev_disable_streams(subdev, pad->index,
+ mask);
+ if (ret && ret != -EALREADY)
+ dev_err(video->camss->dev,
+ "Failed to disable streams %#llx on %s:%u: %d\n",
+ mask, subdev->name, pad->index, ret);
+
+ break;
+ }
+
ret = video_subdev_set_stream(video, subdev, false);
if (ret) {
dev_err(video->camss->dev, "Video pipeline stop failed: %d\n", ret);
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH 8/8] media: qcom: camss: enable only the stream of the pipeline's virtual channel
2026-09-14 13:34 [PATCH 0/8] media: qcom: camss: support several cameras behind a CSI-2 bridge Hitesh Patel
` (6 preceding siblings ...)
2026-09-14 13:34 ` [PATCH 7/8] media: qcom: camss: drive streams-aware transmitters through the streams API Hitesh Patel
@ 2026-09-14 13:34 ` 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
8 siblings, 1 reply; 34+ messages in thread
From: Hitesh Patel @ 2026-09-14 13:34 UTC (permalink / raw)
To: linux-media
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, linux-arm-msm, linux-kernel, ravi,
Hitesh Patel
When a streams-aware transmitter is found at the head of the
pipeline, every stream routed to the source pad the pipeline arrived
through is enabled at start and disabled at stop.
That is right for a transmitter driving one camera per output pad,
but not when it aggregates several cameras onto one output. A GMSL
deserializer can send two cameras on a single CSI-2 port as two
virtual channels, which the CSID demultiplexes to two RDIs and thus
two pipelines. Both pipelines then reach the same source pad, and
each of them enables and, worse, disables both cameras' streams:
stopping one video node stops the other camera as well, and
starting the second one has nothing left to enable.
Identify the stream a pipeline owns from the CSID it went through:
CSID source pad MSM_CSID_PAD_FIRST_SRC + n carries virtual channel n.
Remember that virtual channel while walking upstream, ask the
transmitter for the frame descriptor of its source pad, and only
enable or disable the stream(s) it reports on that virtual channel.
A pipeline that did not pass through a CSID source pad, or a
transmitter without a CSI-2 frame descriptor, keeps enabling every
stream of the pad as before.
Signed-off-by: Hitesh Patel <hitesh@ebytelogic.com>
---
.../media/platform/qcom/camss/camss-video.c | 45 +++++++++++++++++--
drivers/media/platform/qcom/camss/camss.c | 30 +++++++++++++
drivers/media/platform/qcom/camss/camss.h | 2 +
3 files changed, 74 insertions(+), 3 deletions(-)
diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c
index 90f22ce76..a261f7692 100644
--- a/drivers/media/platform/qcom/camss/camss-video.c
+++ b/drivers/media/platform/qcom/camss/camss-video.c
@@ -253,14 +253,39 @@ static int video_prepare_streaming(struct vb2_queue *q)
* video_source_pad_streams - Streams routed to a subdev source pad
* @sd: Streams-aware subdevice
* @pad: Source pad index on @sd
+ * @vc: Virtual channel of the pipeline, or -1 if unknown
+ *
+ * When a transmitter aggregates several cameras onto one output, that pad
+ * carries one stream per camera and each of them is a separate pipeline here.
+ * Enabling or disabling the whole pad would start or stop every camera at
+ * once, so pick out the single stream this pipeline owns: the one the frame
+ * descriptor reports on the virtual channel the CSID demultiplexed it from.
+ * With @vc unknown, or without a frame descriptor to map it, the whole pad is
+ * returned, which is the case for a transmitter driving one camera per output
+ * pad.
*
* Return the mask of streams of the active routes ending on @pad.
*/
-static u64 video_source_pad_streams(struct v4l2_subdev *sd, u32 pad)
+static u64 video_source_pad_streams(struct v4l2_subdev *sd, u32 pad, int vc)
{
struct v4l2_subdev_state *state;
struct v4l2_subdev_route *route;
+ struct v4l2_mbus_frame_desc fd;
+ u64 vc_mask = ~0ULL;
u64 mask = 0;
+ int ret;
+
+ if (vc >= 0) {
+ ret = v4l2_subdev_call(sd, pad, get_frame_desc, pad, &fd);
+ if (!ret && fd.type == V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
+ unsigned int i;
+
+ vc_mask = 0;
+ for (i = 0; i < fd.num_entries; i++)
+ if (fd.entry[i].bus.csi2.vc == vc)
+ vc_mask |= BIT_ULL(fd.entry[i].stream);
+ }
+ }
state = v4l2_subdev_lock_and_get_active_state(sd);
if (!state)
@@ -270,6 +295,8 @@ static u64 video_source_pad_streams(struct v4l2_subdev *sd, u32 pad)
if (route->source_pad == pad)
mask |= BIT_ULL(route->source_stream);
+ mask &= vc_mask;
+
v4l2_subdev_unlock_state(state);
return mask;
@@ -325,6 +352,7 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count)
struct media_entity *entity;
struct media_pad *pad;
struct v4l2_subdev *subdev;
+ int vc = -1;
int ret;
ret = video_device_pipeline_alloc_start(vdev);
@@ -350,8 +378,13 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count)
entity = pad->entity;
subdev = media_entity_to_v4l2_subdev(entity);
+ if (vc < 0)
+ vc = camss_csid_source_vc(video->camss, subdev,
+ pad->index);
+
if (subdev->flags & V4L2_SUBDEV_FL_STREAMS) {
- u64 mask = video_source_pad_streams(subdev, pad->index);
+ u64 mask = video_source_pad_streams(subdev, pad->index,
+ vc);
if (!mask)
break;
@@ -387,6 +420,7 @@ static void video_stop_streaming(struct vb2_queue *q)
struct media_entity *entity;
struct media_pad *pad;
struct v4l2_subdev *subdev;
+ int vc = -1;
int ret;
entity = &vdev->entity;
@@ -402,8 +436,13 @@ static void video_stop_streaming(struct vb2_queue *q)
entity = pad->entity;
subdev = media_entity_to_v4l2_subdev(entity);
+ if (vc < 0)
+ vc = camss_csid_source_vc(video->camss, subdev,
+ pad->index);
+
if (subdev->flags & V4L2_SUBDEV_FL_STREAMS) {
- u64 mask = video_source_pad_streams(subdev, pad->index);
+ u64 mask = video_source_pad_streams(subdev, pad->index,
+ vc);
if (!mask)
break;
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index ca8101c2d..63cc81570 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -4655,6 +4655,36 @@ static bool camss_is_receiver_subdev(struct camss *camss,
return camss_subdev_stream_users(camss, sd);
}
+/*
+ * camss_csid_source_vc - Virtual channel behind a CSID source pad
+ * @camss: CAMSS device
+ * @sd: Subdevice to test
+ * @pad: Source pad index on @sd
+ *
+ * The CSID demultiplexes virtual channels to its source pads, source pad
+ * MSM_CSID_PAD_FIRST_SRC + n carrying virtual channel n (see the en_vc mask
+ * maintained by csid_link_setup()). Walking a pipeline upstream, this tells
+ * which virtual channel, and so which stream of a shared transmitter, the
+ * pipeline belongs to.
+ *
+ * Return the virtual channel, or -1 if @sd is not a CSID of @camss or @pad is
+ * not one of its source pads.
+ */
+int camss_csid_source_vc(struct camss *camss, struct v4l2_subdev *sd,
+ unsigned int pad)
+{
+ unsigned int i;
+
+ if (pad < MSM_CSID_PAD_FIRST_SRC)
+ return -1;
+
+ for (i = 0; i < camss->res->csid_num; i++)
+ if (sd == &camss->csid[i].subdev)
+ return pad - MSM_CSID_PAD_FIRST_SRC;
+
+ return -1;
+}
+
/*
* camss_find_transmitter_pad - Find the pad of the CSI-2 transmitter
* @camss: CAMSS device
diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h
index 00b8d5304..14a7c737e 100644
--- a/drivers/media/platform/qcom/camss/camss.h
+++ b/drivers/media/platform/qcom/camss/camss.h
@@ -169,6 +169,8 @@ int camss_enable_clocks(int nclocks, struct camss_clock *clock,
void camss_disable_clocks(int nclocks, struct camss_clock *clock);
unsigned int *camss_subdev_stream_users(struct camss *camss,
struct v4l2_subdev *sd);
+int camss_csid_source_vc(struct camss *camss, struct v4l2_subdev *sd,
+ unsigned int pad);
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);
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 1/8] media: qcom: camss: take the link frequency from the CSI-2 transmitter
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
0 siblings, 1 reply; 34+ messages in thread
From: Bryan O'Donoghue @ 2026-09-14 14:58 UTC (permalink / raw)
To: Hitesh Patel, linux-media
Cc: Vladimir Zapolskiy, Loic Poulain, Mauro Carvalho Chehab,
linux-arm-msm, linux-kernel, ravi
On 14/09/2026 14:34, Hitesh Patel wrote:
> camss_get_link_freq() walks the pipeline to an entity whose function
> is MEDIA_ENT_F_CAM_SENSOR and reads the link frequency from there, to
> derive the CSIPHY settle count and the CSID clock.
>
> The rate the receiver has to be programmed for is the rate on the
> CSI-2 bus, which is a property of whatever drives that bus, not of
> the sensor at the far end of the pipeline. The two coincide only when
> the sensor is wired directly to the CSIPHY. With a CSI-2 to CSI-2
> bridge in between, a GMSL or FPD-Link deserializer for instance, the
> bridge re-times the stream onto its own output: it may aggregate
> several sensors onto one link, forward a single sensor at a different
> rate, or generate a test pattern with no sensor involved at all. The
> sensor's rate is then simply not what arrives at the SoC, and the
> PHY does not lock.
>
> Walking to a MEDIA_ENT_F_CAM_SENSOR also fails outright on a
> deserializer that has one sink pad per serial link: the walk follows
> pad 0, while the sensor may be attached to any of the other sink
> pads, and streaming is refused with "Cannot get CSI2 transmitter's
> link frequency".
>
> Stop the walk at the first entity that is not a CAMSS receiver, i.e.
> at the external subdev feeding the CSIPHY, and query that pad with
> v4l2_get_link_freq(). This is what the helper is for: it asks the
> transmitter through .get_mbus_config first and falls back to its
> V4L2_CID_LINK_FREQ, then V4L2_CID_PIXEL_RATE controls.
>
> For a sensor connected straight to a CSIPHY the transmitter is the
> sensor itself, so the pad found and the value returned are the same
> as before.
>
> camss_find_sensor_pad() keeps walking to the sensor: its other users,
> camss_get_pixel_clock() and the frame skip query, do want the sensor.
>
> Signed-off-by: Hitesh Patel <hitesh@ebytelogic.com>
> ---
> .../media/platform/qcom/camss/camss-csid.c | 2 +-
> .../media/platform/qcom/camss/camss-csiphy.c | 6 +-
> drivers/media/platform/qcom/camss/camss.c | 74 +++++++++++++++++--
> drivers/media/platform/qcom/camss/camss.h | 4 +-
> 4 files changed, 73 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c
> index 48459b46a..c631119e2 100644
> --- a/drivers/media/platform/qcom/camss/camss-csid.c
> +++ b/drivers/media/platform/qcom/camss/camss-csid.c
> @@ -546,7 +546,7 @@ static int csid_set_clock_rates(struct csid_device *csid)
>
> fmt = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats,
> csid->fmt[MSM_CSIPHY_PAD_SINK].code);
> - link_freq = camss_get_link_freq(&csid->subdev.entity, fmt->bpp,
> + link_freq = camss_get_link_freq(csid->camss, &csid->subdev.entity, fmt->bpp,
> csid->phy.lane_cnt);
> if (link_freq < 0)
> link_freq = 0;
> diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c
> index 539ac4888..000fde129 100644
> --- a/drivers/media/platform/qcom/camss/camss-csiphy.c
> +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c
> @@ -145,7 +145,8 @@ static int csiphy_set_clock_rates(struct csiphy_device *csiphy)
> csiphy->fmt[MSM_CSIPHY_PAD_SINK].code);
> u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data;
>
> - link_freq = camss_get_link_freq(&csiphy->subdev.entity, bpp, num_lanes);
> + link_freq = camss_get_link_freq(csiphy->camss, &csiphy->subdev.entity,
> + bpp, num_lanes);
> if (link_freq < 0)
> link_freq = 0;
>
> @@ -272,7 +273,8 @@ static int csiphy_stream_on(struct csiphy_device *csiphy)
> u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data;
> u8 val;
>
> - link_freq = camss_get_link_freq(&csiphy->subdev.entity, bpp, num_lanes);
> + link_freq = camss_get_link_freq(csiphy->camss, &csiphy->subdev.entity,
Its redundant to pass csiphy->camss and &csiphy->subdev.entity
Just pass struct camss_csiphy *csiphy once and then extract the pointers
you need in the routine.
In fact if you line that particular change up as a first patch in this
series, the overall structure will be neater.
> + bpp, num_lanes);
>
> if (link_freq < 0) {
> dev_err(csiphy->camss->dev,
> diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
> index 2123f6388..16ad1c26c 100644
> --- a/drivers/media/platform/qcom/camss/camss.c
> +++ b/drivers/media/platform/qcom/camss/camss.c
> @@ -4619,24 +4619,82 @@ 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: 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.
> + */
> +static bool camss_is_receiver_subdev(struct camss *camss,
> + struct v4l2_subdev *sd)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < camss->res->csiphy_num; i++)
> + if (sd == &camss->csiphy[i].subdev)
> + return true;
> +
> + for (i = 0; i < camss->res->csid_num; i++)
> + if (sd == &camss->csid[i].subdev)
> + return true;
> +
> + return false;
> +}
This seems fine. I wonder how it works with the TPG, probably fine,
needs testing.
> +/*
> + * camss_find_transmitter_pad - Find the pad of the CSI-2 transmitter
> + * @camss: CAMSS device
> + * @entity: Media entity in the current pipeline
> + *
> + * Walk the pipeline upstream through the CAMSS receiver subdevs and return the
> + * source pad of the first entity that is not one of them: the CSI-2
> + * transmitter driving the SoC.
> + *
> + * Return a pointer to the transmitter media pad or NULL if not found
> + */
> +static struct media_pad *camss_find_transmitter_pad(struct camss *camss,
> + struct media_entity *entity)
> +{
> + struct media_pad *pad;
> +
> + while (1) {
> + pad = &entity->pads[0];
> + if (!(pad->flags & MEDIA_PAD_FL_SINK))
> + return NULL;
> +
> + pad = media_pad_remote_pad_first(pad);
> + if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
> + return NULL;
> +
> + entity = pad->entity;
> +
> + if (!camss_is_receiver_subdev(camss,
> + media_entity_to_v4l2_subdev(entity)))
Please fix up that clause a bit so its not spralling multi-line get the
media_entity one line previous and pass.
> + return pad;
> + }
> +}
> +
> /**
> - * camss_get_link_freq - Get link frequency from sensor
> + * camss_get_link_freq - Get link frequency from the CSI-2 transmitter
> + * @camss: CAMSS device
> * @entity: Media entity in the current pipeline
> * @bpp: Number of bits per pixel for the current format
> - * @lanes: Number of lanes in the link to the sensor
> + * @lanes: Number of lanes in the link to the transmitter
> *
> * Return link frequency on success or a negative error code otherwise
> */
> -s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp,
> - unsigned int lanes)
> +s64 camss_get_link_freq(struct camss *camss, struct media_entity *entity,
> + unsigned int bpp, unsigned int lanes)
> {
> - struct media_pad *sensor_pad;
> + struct media_pad *tx_pad;
>
> - sensor_pad = camss_find_sensor_pad(entity);
> - if (!sensor_pad)
> + tx_pad = camss_find_transmitter_pad(camss, entity);
> + if (!tx_pad)
> return -ENODEV;
>
> - return v4l2_get_link_freq(sensor_pad, bpp, 2 * lanes);
> + return v4l2_get_link_freq(tx_pad, bpp, 2 * lanes);
> }
>
> /*
> diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h
> index 93d691c8a..39ea33e61 100644
> --- a/drivers/media/platform/qcom/camss/camss.h
> +++ b/drivers/media/platform/qcom/camss/camss.h
> @@ -168,8 +168,8 @@ int camss_enable_clocks(int nclocks, struct camss_clock *clock,
> struct device *dev);
> void camss_disable_clocks(int nclocks, struct camss_clock *clock);
> struct media_pad *camss_find_sensor_pad(struct media_entity *entity);
> -s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp,
> - unsigned int lanes);
> +s64 camss_get_link_freq(struct camss *camss, struct media_entity *entity,
> + unsigned int bpp, unsigned int lanes);
> int camss_get_pixel_clock(struct media_entity *entity, u64 *pixel_clock);
> int camss_pm_domain_on(struct camss *camss, int id);
> void camss_pm_domain_off(struct camss *camss, int id);
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 2/8] media: qcom: camss: create the source to CSIPHY link per endpoint
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
0 siblings, 1 reply; 34+ messages in thread
From: Bryan O'Donoghue @ 2026-09-14 15:07 UTC (permalink / raw)
To: Hitesh Patel, linux-media
Cc: Vladimir Zapolskiy, Loic Poulain, Mauro Carvalho Chehab,
linux-arm-msm, linux-kernel, ravi
On 14/09/2026 14:34, Hitesh Patel wrote:
> The link from the external CSI-2 transmitter to the CSIPHY is created
> in the notifier .complete() callback by walking every registered
> subdev, reading the CSIPHY it was bound to from sd->host_priv and
> linking the subdev's first source pad to that CSIPHY.
>
> This assumes one transmitter feeds exactly one CSIPHY. A GMSL
> deserializer such as the MAX9296A has two independent CSI-2 output
> ports which, on the RB3 Gen2 vision mezzanine, are wired to two
> different SoC CSIPHYs. The same subdev is then bound once per CAMSS
> port endpoint, the second .bound() overwrites host_priv, and
> .complete() creates a single link from source pad 0 to the last
> CSIPHY. The second output port is left with no link at all, so a
> second camera can never be routed to the SoC.
>
> Move the link creation into .bound(), where both the endpoint and
> the CSIPHY are known, and resolve the transmitter's source pad from
> the endpoint fwnode with media_entity_get_fwnode_pad(). Each
> endpoint then gets its own link between the right source pad and
> the right CSIPHY.
>
> For a subdev that does not implement .get_fwnode_pad,
> media_entity_get_fwnode_pad() falls back to the first pad matching
> the requested direction, which is exactly what the .complete() loop
> did, so ordinary single-output sensors keep the same link as before.
>
> Signed-off-by: Hitesh Patel<hitesh@ebytelogic.com>
Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 3/8] media: qcom: camss: vfe-17x: do not gate write master done on IRQ_STATUS_0
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
0 siblings, 1 reply; 34+ messages in thread
From: Bryan O'Donoghue @ 2026-09-14 15:18 UTC (permalink / raw)
To: Hitesh Patel, linux-media
Cc: Vladimir Zapolskiy, Loic Poulain, Mauro Carvalho Chehab,
linux-arm-msm, linux-kernel, ravi
On 14/09/2026 14:34, Hitesh Patel wrote:
> The VFE 17x interrupt handler reads and clears IRQ_STATUS_0/1 and
> then reads and clears every BUS_IRQ_STATUS register, unconditionally.
> It only acts on the per write master WM_CLIENT_BUF_DONE bits from
> BUS_IRQ_STATUS(1), however, when bit 9 of the previously sampled
> IRQ_STATUS_0 was set. That bit is the ping-pong flag of image master
> 1, which says nothing about the other masters.
This paragraph is disjointed, places a full stop where there was going
to be a comma ? "IRQ_STATUS_0 was set. That bit is the"
>
> Those two reads are not atomic either. A buffer done that becomes
> pending after IRQ_STATUS_0 has been sampled but before
> BUS_IRQ_STATUS(1) is read, for example while the handler is entered
> for another line's RDI SOF or REG_UPDATE, is cleared by the bus
> status read and then dropped by the gate: wm_done() is never called
> for it, the buffer is never returned to userspace and that line
> stalls until the next frame happens to line up with the gate again.
>
> With a single RDI streaming the window is rarely hit. As soon as a
> second RDI of the same VFE streams, which is the case when a CSID
> demultiplexes two virtual channels to RDI0 and RDI1, the interrupt
> rate doubles and one of the two lines loses buffer done events
> continuously.
>
> The bus status is read-to-clear in this handler, so once read it is
> the authoritative record of which write masters completed. Act on it
> directly, as the gen2 VFE handler does, and drop the gate.
>
> The write master of a PIX line is skipped: its buffers are completed
> from the composite done above, through vfe_isr_comp_done(), and
> completing them here as well would return two buffers per frame.
Please rewrite this whole commit log in your own language.
Also since your are describing a bug this needs
1. Fixes:
2. A patch title starting with Fix
3. All fixes in a serious should prefix functionality changes.
>
> Signed-off-by: Hitesh Patel <hitesh@ebytelogic.com>
> ---
> drivers/media/platform/qcom/camss/camss-vfe-17x.c | 11 +++++++----
> 1 file changed, 7 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/media/platform/qcom/camss/camss-vfe-17x.c b/drivers/media/platform/qcom/camss/camss-vfe-17x.c
> index e5ee7e717..f62fdabbc 100644
> --- a/drivers/media/platform/qcom/camss/camss-vfe-17x.c
> +++ b/drivers/media/platform/qcom/camss/camss-vfe-17x.c
> @@ -363,10 +363,13 @@ static irqreturn_t vfe_isr(int irq, void *dev)
> if (vfe_bus_status[0] & STATUS0_COMP_BUF_DONE(i))
> vfe->isr_ops.comp_done(vfe, i);
>
> - for (wm = 0; wm < MSM_VFE_IMAGE_MASTERS_NUM; wm++)
> - if (status0 & BIT(9))
> - if (vfe_bus_status[1] & STATUS1_WM_CLIENT_BUF_DONE(wm))
> - vfe->isr_ops.wm_done(vfe, wm);
> + for (wm = 0; wm < MSM_VFE_IMAGE_MASTERS_NUM; wm++) {
> + if (vfe->wm_output_map[wm] == VFE_LINE_PIX)
> + continue;
Drop this workaround for pix. We will fix PIX a different way instead of
for local specific stuff like this.
> +
> + if (vfe_bus_status[1] & STATUS1_WM_CLIENT_BUF_DONE(wm))
> + vfe->isr_ops.wm_done(vfe, wm);
> + }
OK then this becomes a one-liner to not disjunction BIT(9)
> return IRQ_HANDLED;
> }
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 4/8] media: qcom: camss: vfe-17x: use the write master matching the RDI line
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
0 siblings, 1 reply; 34+ messages in thread
From: Bryan O'Donoghue @ 2026-09-14 15:21 UTC (permalink / raw)
To: Hitesh Patel, linux-media
Cc: Vladimir Zapolskiy, Loic Poulain, Mauro Carvalho Chehab,
linux-arm-msm, linux-kernel, ravi
On 14/09/2026 14:34, Hitesh Patel wrote:
> Signed-off-by: Hitesh Patel<hitesh@ebytelogic.com>
> ---
> .../media/platform/qcom/camss/camss-vfe-17x.c | 22 +++++++++++++------
> 1 file changed, 15 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/media/platform/qcom/camss/camss-vfe-17x.c b/drivers/media/platform/qcom/camss/camss-vfe-17x.c
> index f62fdabbc..0cfc24255 100644
> --- a/drivers/media/platform/qcom/camss/camss-vfe-17x.c
> +++ b/drivers/media/platform/qcom/camss/camss-vfe-17x.c
> @@ -403,10 +403,20 @@ static int vfe_get_output(struct vfe_line *line)
>
> output->wm_num = 1;
>
> - wm_idx = vfe_reserve_wm(vfe, line->id);
> - if (wm_idx < 0) {
> - dev_err(vfe->camss->dev, "Can not reserve wm\n");
> - goto error_get_wm;
> + if (line->id == VFE_LINE_PIX) {
> + wm_idx = vfe_reserve_wm(vfe, line->id);
> + if (wm_idx < 0) {
> + dev_err(vfe->camss->dev, "Can not reserve wm\n");
> + goto error;
> + }
> + } else {
> + wm_idx = line->id;
> + if (vfe->wm_output_map[wm_idx] != VFE_LINE_NONE) {
> + dev_err(vfe->camss->dev, "Can not reserve wm %d\n",
> + wm_idx);
> + goto error;
> + }
> + vfe->wm_output_map[wm_idx] = line->id;
I don't get why you are disjuncting on PIX here.
Also isn't this a problem for ~ every VFE ?
Again.
- Fixes:
- Prefix patch title with Fix
- Fixes go first in the queue of submitted patches before functionality
And since you are going in with the knife for vfe 17x you may as well
fix this for all of them.
---
bod
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 5/8] media: qcom: camss: vfe: only reset the VFE when its last line stops
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
0 siblings, 1 reply; 34+ messages in thread
From: Bryan O'Donoghue @ 2026-09-14 15:28 UTC (permalink / raw)
To: Hitesh Patel, linux-media
Cc: Vladimir Zapolskiy, Loic Poulain, Mauro Carvalho Chehab,
linux-arm-msm, linux-kernel, ravi
On 14/09/2026 14:34, Hitesh Patel wrote:
> vfe_disable_output() stops the write masters of the line being
> disabled and then issues a global VFE reset. The reset is not scoped
> to the line: it resets the whole block.
>
> Several lines of one VFE stream at the same time when a CSID
> demultiplexes virtual channels to different RDIs, e.g. two GMSL
> cameras aggregated by a MAX9296A onto one CSI-2 port, each on its
You can drop references to a particular part - all aggregators would do
this.
> own VC and RDI. Stopping one of them then resets the VFE underneath
> the other: its in-flight buffers are never completed, its write
> master configuration is gone and the block is left in a state where
> the next reset is not acknowledged, which surfaces as
"Stopping one of the streams/VFEs/RDIs" - basically we haven't defined
what the "it" in this paragraph is "its write master configuration is gone"
What it ? Define the object instead of assuming the reader knows what is
meant by it.
IT: a spider that lives in the sewers of Derry Maine.. [1]
> VFE reset timeout
>
> when the remaining camera is stopped or restarted.
>
> Only issue the reset when the line being disabled is the last one
> streaming on this VFE, as tracked by vfe->stream_count. Stopping the
> line's write masters is sufficient to quiesce it while other lines
> keep running. The single-line case resets exactly as before.
>
> Signed-off-by: Hitesh Patel <hitesh@ebytelogic.com>
> ---
> drivers/media/platform/qcom/camss/camss-vfe.c | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c
> index 319d19158..9cdf26671 100644
> --- a/drivers/media/platform/qcom/camss/camss-vfe.c
> +++ b/drivers/media/platform/qcom/camss/camss-vfe.c
> @@ -814,6 +814,7 @@ static int vfe_disable_output(struct vfe_line *line)
> struct vfe_output *output = &line->output;
> unsigned long flags;
> unsigned int i;
> + bool last;
>
> spin_lock_irqsave(&vfe->output_lock, flags);
> for (i = 0; i < output->wm_num; i++)
> @@ -821,6 +822,13 @@ static int vfe_disable_output(struct vfe_line *line)
> output->gen2.active_num = 0;
> spin_unlock_irqrestore(&vfe->output_lock, flags);
>
> + mutex_lock(&vfe->stream_lock);
> + last = vfe->stream_count == 1;
> + mutex_unlock(&vfe->stream_lock);
> +
> + if (!last)
> + return 0;
> +
> return vfe_reset(vfe);
> }
>
Other than the rambling commit log - looks like a valid Fix to me.
Again
- Fixes tag
- Fixes to first
- Fix in the patch title name
[1] We all float down here.
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 7/8] media: qcom: camss: drive streams-aware transmitters through the streams API
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
0 siblings, 2 replies; 34+ messages in thread
From: Loic Poulain @ 2026-09-14 15:57 UTC (permalink / raw)
To: Hitesh Patel
Cc: linux-media, Bryan O'Donoghue, Vladimir Zapolskiy,
Mauro Carvalho Chehab, linux-arm-msm, linux-kernel, ravi
Hi Hitesh,
On Mon, Sep 14, 2026 at 3:34 PM Hitesh Patel <hitesh@ebytelogic.com> wrote:
>
> video_start_streaming() and video_stop_streaming() drive every subdev
> of the pipeline through video.s_stream. That op is per subdev, and
> the core allows one transition per subdev, so a transmitter that is
> shared by two independent pipelines can only be started once and is
> stopped by the first pipeline that stops.
>
> A GMSL deserializer with two CSI-2 output ports, one camera routed to
> each, is exactly that case: the same subdev sits at the head of two
> pipelines that otherwise share nothing (each has its own CSIPHY, CSID
> and VFE). The second camera never starts, and stopping either one
> kills the other.
>
> Such transmitters implement the V4L2 streams API and advertise it
> with V4L2_SUBDEV_FL_STREAMS. For those, enable or disable only the
> streams routed to the source pad the pipeline arrived through, using
> v4l2_subdev_enable_streams()/v4l2_subdev_disable_streams(). The core
> refcounts enabled streams per pad, so the two pipelines no longer
> interfere. Enabling streams on a source pad propagates upstream to
> the sensor by itself, so the walk ends there rather than starting
> the rest of the chain a second time through s_stream.
>
> Subdevs without the flag keep using video.s_stream exactly as
> before.
>
> Signed-off-by: Hitesh Patel <hitesh@ebytelogic.com>
I would like to point out that Gjorgji has recently posted a CAMSS
stream API series [1]. Would that series address your use case, or are
there requirements that it does not cover? It would be good to
understand whether there is an opportunity for a common solution.
[1] https://lore.kernel.org/all/20260911062213.195007-1-gjorgji.rosikopulos@oss.qualcomm.com/
> ---
> .../media/platform/qcom/camss/camss-video.c | 56 +++++++++++++++++++
> 1 file changed, 56 insertions(+)
>
> diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c
> index 16c5f3748..90f22ce76 100644
> --- a/drivers/media/platform/qcom/camss/camss-video.c
> +++ b/drivers/media/platform/qcom/camss/camss-video.c
> @@ -249,6 +249,32 @@ static int video_prepare_streaming(struct vb2_queue *q)
> return ret;
> }
>
> +/*
> + * video_source_pad_streams - Streams routed to a subdev source pad
> + * @sd: Streams-aware subdevice
> + * @pad: Source pad index on @sd
> + *
> + * Return the mask of streams of the active routes ending on @pad.
> + */
> +static u64 video_source_pad_streams(struct v4l2_subdev *sd, u32 pad)
> +{
> + struct v4l2_subdev_state *state;
> + struct v4l2_subdev_route *route;
> + u64 mask = 0;
> +
> + state = v4l2_subdev_lock_and_get_active_state(sd);
> + if (!state)
> + return 0;
> +
> + for_each_active_route(&state->routing, route)
> + if (route->source_pad == pad)
> + mask |= BIT_ULL(route->source_stream);
> +
> + v4l2_subdev_unlock_state(state);
> +
> + return mask;
> +}
> +
> /*
> * video_subdev_set_stream - Start or stop a subdev of the pipeline
> * @video: CAMSS video device
> @@ -324,6 +350,20 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count)
> entity = pad->entity;
> subdev = media_entity_to_v4l2_subdev(entity);
>
> + if (subdev->flags & V4L2_SUBDEV_FL_STREAMS) {
> + u64 mask = video_source_pad_streams(subdev, pad->index);
> +
> + if (!mask)
> + break;
> +
> + ret = v4l2_subdev_enable_streams(subdev, pad->index,
> + mask);
> + if (ret && ret != -EALREADY)
> + goto error;
> +
> + break;
> + }
> +
> ret = video_subdev_set_stream(video, subdev, true);
> if (ret < 0 && ret != -ENOIOCTLCMD)
> goto error;
> @@ -362,6 +402,22 @@ static void video_stop_streaming(struct vb2_queue *q)
> entity = pad->entity;
> subdev = media_entity_to_v4l2_subdev(entity);
>
> + if (subdev->flags & V4L2_SUBDEV_FL_STREAMS) {
> + u64 mask = video_source_pad_streams(subdev, pad->index);
> +
> + if (!mask)
> + break;
> +
> + ret = v4l2_subdev_disable_streams(subdev, pad->index,
> + mask);
> + if (ret && ret != -EALREADY)
> + dev_err(video->camss->dev,
> + "Failed to disable streams %#llx on %s:%u: %d\n",
> + mask, subdev->name, pad->index, ret);
> +
> + break;
> + }
> +
> ret = video_subdev_set_stream(video, subdev, false);
> if (ret) {
> dev_err(video->camss->dev, "Video pipeline stop failed: %d\n", ret);
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 6/8] media: qcom: camss: refcount streaming on the shared CSIPHY and CSID
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
2026-09-15 9:08 ` Hitesh Patel
0 siblings, 1 reply; 34+ messages in thread
From: Bryan O'Donoghue @ 2026-09-14 19:13 UTC (permalink / raw)
To: Hitesh Patel, linux-media
Cc: Vladimir Zapolskiy, Loic Poulain, Mauro Carvalho Chehab,
linux-arm-msm, linux-kernel, ravi
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);
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 7/8] media: qcom: camss: drive streams-aware transmitters through the streams API
2026-09-14 15:57 ` Loic Poulain
@ 2026-09-14 19:16 ` Bryan O'Donoghue
2026-09-15 9:08 ` Hitesh Patel
1 sibling, 0 replies; 34+ messages in thread
From: Bryan O'Donoghue @ 2026-09-14 19:16 UTC (permalink / raw)
To: Loic Poulain, Hitesh Patel
Cc: linux-media, Vladimir Zapolskiy, Mauro Carvalho Chehab,
linux-arm-msm, linux-kernel, ravi
On 14/09/2026 16:57, Loic Poulain wrote:
>> Subdevs without the flag keep using video.s_stream exactly as
>> before.
>>
>> Signed-off-by: Hitesh Patel<hitesh@ebytelogic.com>
> I would like to point out that Gjorgji has recently posted a CAMSS
> stream API series [1]. Would that series address your use case, or are
> there requirements that it does not cover? It would be good to
> understand whether there is an opportunity for a common solution.
>
> [1]https://lore.kernel.org/all/20260911062213.195007-1-
> gjorgji.rosikopulos@oss.qualcomm.com/
There's a fair difference in the volume of the code in both cases too.
---
bod
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 8/8] media: qcom: camss: enable only the stream of the pipeline's virtual channel
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
0 siblings, 0 replies; 34+ messages in thread
From: Bryan O'Donoghue @ 2026-09-14 19:17 UTC (permalink / raw)
To: Hitesh Patel, linux-media
Cc: Vladimir Zapolskiy, Loic Poulain, Mauro Carvalho Chehab,
linux-arm-msm, linux-kernel, ravi
On 14/09/2026 14:34, Hitesh Patel wrote:
> When a streams-aware transmitter is found at the head of the
> pipeline, every stream routed to the source pad the pipeline arrived
> through is enabled at start and disabled at stop.
>
> That is right for a transmitter driving one camera per output pad,
> but not when it aggregates several cameras onto one output. A GMSL
> deserializer can send two cameras on a single CSI-2 port as two
> virtual channels, which the CSID demultiplexes to two RDIs and thus
> two pipelines. Both pipelines then reach the same source pad, and
> each of them enables and, worse, disables both cameras' streams:
> stopping one video node stops the other camera as well, and
> starting the second one has nothing left to enable.
>
> Identify the stream a pipeline owns from the CSID it went through:
> CSID source pad MSM_CSID_PAD_FIRST_SRC + n carries virtual channel n.
> Remember that virtual channel while walking upstream, ask the
> transmitter for the frame descriptor of its source pad, and only
> enable or disable the stream(s) it reports on that virtual channel.
>
> A pipeline that did not pass through a CSID source pad, or a
> transmitter without a CSI-2 frame descriptor, keeps enabling every
> stream of the pad as before.
>
> Signed-off-by: Hitesh Patel <hitesh@ebytelogic.com>
> ---
> .../media/platform/qcom/camss/camss-video.c | 45 +++++++++++++++++--
> drivers/media/platform/qcom/camss/camss.c | 30 +++++++++++++
> drivers/media/platform/qcom/camss/camss.h | 2 +
> 3 files changed, 74 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c
> index 90f22ce76..a261f7692 100644
> --- a/drivers/media/platform/qcom/camss/camss-video.c
> +++ b/drivers/media/platform/qcom/camss/camss-video.c
> @@ -253,14 +253,39 @@ static int video_prepare_streaming(struct vb2_queue *q)
> * video_source_pad_streams - Streams routed to a subdev source pad
> * @sd: Streams-aware subdevice
> * @pad: Source pad index on @sd
> + * @vc: Virtual channel of the pipeline, or -1 if unknown
> + *
> + * When a transmitter aggregates several cameras onto one output, that pad
> + * carries one stream per camera and each of them is a separate pipeline here.
> + * Enabling or disabling the whole pad would start or stop every camera at
> + * once, so pick out the single stream this pipeline owns: the one the frame
> + * descriptor reports on the virtual channel the CSID demultiplexed it from.
> + * With @vc unknown, or without a frame descriptor to map it, the whole pad is
> + * returned, which is the case for a transmitter driving one camera per output
> + * pad.
> *
> * Return the mask of streams of the active routes ending on @pad.
> */
> -static u64 video_source_pad_streams(struct v4l2_subdev *sd, u32 pad)
> +static u64 video_source_pad_streams(struct v4l2_subdev *sd, u32 pad, int vc)
> {
> struct v4l2_subdev_state *state;
> struct v4l2_subdev_route *route;
> + struct v4l2_mbus_frame_desc fd;
> + u64 vc_mask = ~0ULL;
> u64 mask = 0;
> + int ret;
> +
> + if (vc >= 0) {
> + ret = v4l2_subdev_call(sd, pad, get_frame_desc, pad, &fd);
> + if (!ret && fd.type == V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
> + unsigned int i;
> +
> + vc_mask = 0;
> + for (i = 0; i < fd.num_entries; i++)
> + if (fd.entry[i].bus.csi2.vc == vc)
> + vc_mask |= BIT_ULL(fd.entry[i].stream);
> + }
> + }
>
> state = v4l2_subdev_lock_and_get_active_state(sd);
> if (!state)
> @@ -270,6 +295,8 @@ static u64 video_source_pad_streams(struct v4l2_subdev *sd, u32 pad)
> if (route->source_pad == pad)
> mask |= BIT_ULL(route->source_stream);
>
> + mask &= vc_mask;
> +
> v4l2_subdev_unlock_state(state);
>
> return mask;
> @@ -325,6 +352,7 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count)
> struct media_entity *entity;
> struct media_pad *pad;
> struct v4l2_subdev *subdev;
> + int vc = -1;
> int ret;
>
> ret = video_device_pipeline_alloc_start(vdev);
> @@ -350,8 +378,13 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count)
> entity = pad->entity;
> subdev = media_entity_to_v4l2_subdev(entity);
>
> + if (vc < 0)
> + vc = camss_csid_source_vc(video->camss, subdev,
> + pad->index);
> +
> if (subdev->flags & V4L2_SUBDEV_FL_STREAMS) {
> - u64 mask = video_source_pad_streams(subdev, pad->index);
> + u64 mask = video_source_pad_streams(subdev, pad->index,
> + vc);
>
> if (!mask)
> break;
> @@ -387,6 +420,7 @@ static void video_stop_streaming(struct vb2_queue *q)
> struct media_entity *entity;
> struct media_pad *pad;
> struct v4l2_subdev *subdev;
> + int vc = -1;
> int ret;
>
> entity = &vdev->entity;
> @@ -402,8 +436,13 @@ static void video_stop_streaming(struct vb2_queue *q)
> entity = pad->entity;
> subdev = media_entity_to_v4l2_subdev(entity);
>
> + if (vc < 0)
> + vc = camss_csid_source_vc(video->camss, subdev,
> + pad->index);
> +
> if (subdev->flags & V4L2_SUBDEV_FL_STREAMS) {
> - u64 mask = video_source_pad_streams(subdev, pad->index);
> + u64 mask = video_source_pad_streams(subdev, pad->index,
> + vc);
>
> if (!mask)
> break;
> diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
> index ca8101c2d..63cc81570 100644
> --- a/drivers/media/platform/qcom/camss/camss.c
> +++ b/drivers/media/platform/qcom/camss/camss.c
> @@ -4655,6 +4655,36 @@ static bool camss_is_receiver_subdev(struct camss *camss,
> return camss_subdev_stream_users(camss, sd);
> }
>
> +/*
> + * camss_csid_source_vc - Virtual channel behind a CSID source pad
> + * @camss: CAMSS device
> + * @sd: Subdevice to test
> + * @pad: Source pad index on @sd
> + *
> + * The CSID demultiplexes virtual channels to its source pads, source pad
> + * MSM_CSID_PAD_FIRST_SRC + n carrying virtual channel n (see the en_vc mask
> + * maintained by csid_link_setup()). Walking a pipeline upstream, this tells
> + * which virtual channel, and so which stream of a shared transmitter, the
> + * pipeline belongs to.
> + *
> + * Return the virtual channel, or -1 if @sd is not a CSID of @camss or @pad is
> + * not one of its source pads.
> + */
> +int camss_csid_source_vc(struct camss *camss, struct v4l2_subdev *sd,
> + unsigned int pad)
> +{
> + unsigned int i;
> +
> + if (pad < MSM_CSID_PAD_FIRST_SRC)
> + return -1;
> +
> + for (i = 0; i < camss->res->csid_num; i++)
> + if (sd == &camss->csid[i].subdev)
> + return pad - MSM_CSID_PAD_FIRST_SRC;
> +
> + return -1;
> +}
> +
> /*
> * camss_find_transmitter_pad - Find the pad of the CSI-2 transmitter
> * @camss: CAMSS device
> diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h
> index 00b8d5304..14a7c737e 100644
> --- a/drivers/media/platform/qcom/camss/camss.h
> +++ b/drivers/media/platform/qcom/camss/camss.h
> @@ -169,6 +169,8 @@ int camss_enable_clocks(int nclocks, struct camss_clock *clock,
> void camss_disable_clocks(int nclocks, struct camss_clock *clock);
> unsigned int *camss_subdev_stream_users(struct camss *camss,
> struct v4l2_subdev *sd);
> +int camss_csid_source_vc(struct camss *camss, struct v4l2_subdev *sd,
> + unsigned int pad);
> 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);
Instinctively I like that there is less code here than in the other series.
OTOH there is less code here than in the other series.
It'd be great if you and Gjorgji could cross-review each other's
contributions.
---
bod
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 1/8] media: qcom: camss: take the link frequency from the CSI-2 transmitter
2026-09-14 14:58 ` Bryan O'Donoghue
@ 2026-09-15 9:08 ` Hitesh Patel
2026-09-15 9:12 ` Bryan O'Donoghue
0 siblings, 1 reply; 34+ messages in thread
From: Hitesh Patel @ 2026-09-15 9:08 UTC (permalink / raw)
To: Bryan O'Donoghue
Cc: Hitesh Patel, linux-media, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, linux-arm-msm, linux-kernel, ravi
> Its redundant to pass csiphy->camss and &csiphy->subdev.entity
> Just pass struct camss_csiphy *csiphy once and then extract the pointers
> you need in the routine.
The same helper is called from the CSID as well, so a csiphy argument
would not fit both callers. The camss pointer is reachable from the
entity itself though: container_of(entity->graph_obj.mdev, struct camss,
media_dev). I will use that in v2 and drop the extra parameter, which
also leaves the callers untouched. Does that work for you?
> This seems fine. I wonder how it works with the TPG, probably fine,
> needs testing.
With the CSID TPG there is no link on the CSID sink pad, so the walk
returns NULL and camss_get_link_freq() returns -ENODEV, exactly as
camss_find_sensor_pad() did before. I will run the TPG on RB3 Gen2
before sending v2 and mention the result in the cover letter.
> Please fix up that clause a bit so its not spralling multi-line
Will do.
Thanks for the review,
Hitesh
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 2/8] media: qcom: camss: create the source to CSIPHY link per endpoint
2026-09-14 15:07 ` Bryan O'Donoghue
@ 2026-09-15 9:08 ` Hitesh Patel
0 siblings, 0 replies; 34+ messages in thread
From: Hitesh Patel @ 2026-09-15 9:08 UTC (permalink / raw)
To: Bryan O'Donoghue
Cc: Hitesh Patel, linux-media, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, linux-arm-msm, linux-kernel, ravi
> Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
Thanks, I will carry the tag in v2.
Hitesh
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 3/8] media: qcom: camss: vfe-17x: do not gate write master done on IRQ_STATUS_0
2026-09-14 15:18 ` Bryan O'Donoghue
@ 2026-09-15 9:08 ` Hitesh Patel
0 siblings, 0 replies; 34+ messages in thread
From: Hitesh Patel @ 2026-09-15 9:08 UTC (permalink / raw)
To: Bryan O'Donoghue
Cc: Hitesh Patel, linux-media, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, linux-arm-msm, linux-kernel, ravi
> Please rewrite this whole commit log in your own language.
> Also since your are describing a bug this needs
> 1. Fixes:
> 2. A patch title starting with Fix
> 3. All fixes in a serious should prefix functionality changes.
Will do. The gate was there from the start of the VFE 170 support, so:
Fixes: 7319cdf189bb ("media: camss: Add support for VFE hardware version Titan 170")
and v2 will have the three fixes (this one, the write master one and the
VFE reset one) first.
> Drop this workaround for pix. We will fix PIX a different way instead
> of for local specific stuff like this.
OK, dropped. For the record the reason it was there: vfe_isr_comp_done()
already calls wm_done() for the write master mapped to the PIX line, so
without the gate a PIX buffer is completed from both places. Happy to
leave PIX to a separate fix.
> OK then this becomes a one-liner to not disjunction BIT(9)
Yes, v2 is just the removal of the BIT(9) test.
Hitesh
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 4/8] media: qcom: camss: vfe-17x: use the write master matching the RDI line
2026-09-14 15:21 ` Bryan O'Donoghue
@ 2026-09-15 9:08 ` Hitesh Patel
0 siblings, 0 replies; 34+ messages in thread
From: Hitesh Patel @ 2026-09-15 9:08 UTC (permalink / raw)
To: Bryan O'Donoghue
Cc: Hitesh Patel, linux-media, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, linux-arm-msm, linux-kernel, ravi
> I don't get why you are disjuncting on PIX here.
> Also isn't this a problem for ~ every VFE ?
For the other gen2 VFEs (480, 680, gen3) it is already handled: they go
through vfe_get_output_v2(), which maps write master N to line N for
every line. Only vfe-17x kept its own vfe_get_output() with the
first-free-slot reservation. The gen1 VFEs (4-1, 4-7, 4-8) have a bus
crossbar (VFE_0_BUS_XBAR_CFG) so any write master can serve any line
there and vfe_reserve_wm() is correct for them.
So the clean fix is to make vfe-17x use vfe_get_output_v2() like the
other gen2 blocks and delete the duplicate. That drops the PIX
distinction as well. I will do that in v2, with
Fixes: 7319cdf189bb ("media: camss: Add support for VFE hardware version Titan 170")
a "Fix" title, and the fixes ordered first.
Hitesh
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 5/8] media: qcom: camss: vfe: only reset the VFE when its last line stops
2026-09-14 15:28 ` Bryan O'Donoghue
@ 2026-09-15 9:08 ` Hitesh Patel
0 siblings, 0 replies; 34+ messages in thread
From: Hitesh Patel @ 2026-09-15 9:08 UTC (permalink / raw)
To: Bryan O'Donoghue
Cc: Hitesh Patel, linux-media, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, linux-arm-msm, linux-kernel, ravi
> You can drop references to a particular part - all aggregators would
> do this.
> ...
> What it ? Define the object instead of assuming the reader knows what
> is meant by it.
Will rewrite: no part names, and the object spelled out (stopping one
RDI line resets the whole VFE, so the other RDI line still streaming on
the same VFE loses its write master configuration and its in-flight
buffers).
> Other than the rambling commit log - looks like a valid Fix to me.
> - Fixes tag
> - Fixes to first
> - Fix in the patch title name
Will do. The reset-on-every-disable came in with the VFE 170 support:
Fixes: 7319cdf189bb ("media: camss: Add support for VFE hardware version Titan 170")
Thanks,
Hitesh
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 6/8] media: qcom: camss: refcount streaming on the shared CSIPHY and CSID
2026-09-14 19:13 ` Bryan O'Donoghue
@ 2026-09-15 9:08 ` Hitesh Patel
0 siblings, 0 replies; 34+ messages in thread
From: Hitesh Patel @ 2026-09-15 9:08 UTC (permalink / raw)
To: Bryan O'Donoghue
Cc: Hitesh Patel, linux-media, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, linux-arm-msm, linux-kernel, ravi
> This logic is mad and genuinely hurts my head a bit to read.
> ...
> I have to say I distrust locking like this and its racy.
Agreed on both, thanks for the test program. Given Gjorgji's streams API
series covers the shared CSIPHY/CSID case properly (refcounting the
shared upstream stream inside the CSID rather than in camss-video), I
would rather not respin this patch at all: I am going to test his
series on RB3 Gen2 with our two-camera setup and, if it covers the use
case, drop patches 6-8 from v2 and work on top of his series instead
(SC7280 enablement and whatever fixes come out of the testing). See my
reply on 7/8.
Hitesh
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 7/8] media: qcom: camss: drive streams-aware transmitters through the streams API
2026-09-14 15:57 ` Loic Poulain
2026-09-14 19:16 ` Bryan O'Donoghue
@ 2026-09-15 9:08 ` Hitesh Patel
1 sibling, 0 replies; 34+ messages in thread
From: Hitesh Patel @ 2026-09-15 9:08 UTC (permalink / raw)
To: Loic Poulain
Cc: Hitesh Patel, linux-media, Bryan O'Donoghue,
Vladimir Zapolskiy, Mauro Carvalho Chehab, linux-arm-msm,
linux-kernel, ravi, Gjorgji Rosikopulos
Hi Loic, Bryan,
> I would like to point out that Gjorgji has recently posted a CAMSS
> stream API series [1]. Would that series address your use case, or are
> there requirements that it does not cover?
Thanks for the pointer, I had missed it. From the cover letter it
covers what patches 6-8 here do, and does it in the right place: the
first/last-stream handling inside the CSID instead of a refcount in
camss-video, and CSID routing for multi-VC sources. Our use case is two
cameras behind a GMSL deserializer on SC7280, both as two virtual
channels on one CSIPHY and as one camera per CSIPHY, with the cameras
started and stopped independently.
Plan from my side:
- I will apply Gjorgji's series on our tree, enable streams for the
SC7280 resources and run our two-camera tests on RB3 Gen2. I will
report the results on his thread, including anything that does not
work, and review the CSID/CSIPHY parts while at it.
- If it covers the use case, v2 of this series will be only the three
VFE fixes plus the link frequency and CSIPHY link patches, and I will
send the SC7280 enablement and any fixes on top of his series rather
than keep 6-8.
> It'd be great if you and Gjorgji could cross-review each other's
> contributions.
Yes, will do. Gjorgji, if you want to try the fixes on SM8250, the
three VFE ones (3/8, 4/8, 5/8) are independent of everything else and
are the ones that matter for two RDIs streaming on one VFE.
Regards,
Hitesh
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 1/8] media: qcom: camss: take the link frequency from the CSI-2 transmitter
2026-09-15 9:08 ` Hitesh Patel
@ 2026-09-15 9:12 ` Bryan O'Donoghue
0 siblings, 0 replies; 34+ messages in thread
From: Bryan O'Donoghue @ 2026-09-15 9:12 UTC (permalink / raw)
To: Hitesh Patel
Cc: linux-media, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, linux-arm-msm, linux-kernel, ravi
On 15/09/2026 10:08, Hitesh Patel wrote:
> I will use that in v2 and drop the extra parameter, which
> also leaves the callers untouched. Does that work for you?
yes
---
bod
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v2 0/5] media: qcom: camss: fixes for several cameras behind a CSI-2 bridge
2026-09-14 13:34 [PATCH 0/8] media: qcom: camss: support several cameras behind a CSI-2 bridge Hitesh Patel
` (7 preceding siblings ...)
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-15 12:15 ` 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
` (5 more replies)
8 siblings, 6 replies; 34+ messages in thread
From: Hitesh Patel @ 2026-09-15 12:15 UTC (permalink / raw)
To: linux-media
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, Gjorgji Rosikopulos, linux-arm-msm,
linux-kernel, ravi, Hitesh Patel
This series contains the CAMSS changes needed to run two GMSL cameras
on the RB3 Gen2 (QCS6490 / SC7280) vision mezzanine, where a MAX9296A
deserializer sits between the sensors and the SoC. The deserializer and
serializer drivers (the out-of-tree maxim-serdes work for MAX9296A and
MAX96717) and the AR0234/IMX900 sensor drivers are out of tree and not
part of this submission; only the SoC side is here.
Patches 1-3 are fixes for two RDI lines streaming on the same VFE, hit
by any configuration in which the CSID demultiplexes several virtual
channels, not only by a bridge: the VFE 17x interrupt handler drops
write master buffer done events, VFE 17x hands the second line the
wrong write master, and stopping one line resets the VFE underneath
the other.
Patch 4 addresses the assumption that the sensor is the CSI-2
transmitter: the rate the receiver has to be programmed for belongs to
whatever drives the bus, and v4l2_get_link_freq() already knows how to
ask it. Patch 5 lets one transmitter with several CSI-2 outputs link
each output to its own CSIPHY.
The v1 patches 6-8 (shared CSIPHY/CSID refcount and the streams API
handling in camss-video) are dropped. Gjorgji's "add V4L2 subdev streams
API support" series [1] covers that ground properly: tested on RB3 Gen2
with streams enabled for SC7280, both cameras stream and start/stop
independently without them. The SC7280 enablement and one fix for that
series are posted on its thread.
A CCI fix found during the same bring-up, enabling SCL clock stretching
in standard mode, has been sent separately to linux-i2c [2]. It is
independent of this series.
Tested on RB3 Gen2 with AR0234 and IMX900 cameras on MAX96717
serializers, on the vendor 6.18 tree and on the qualcomm-linux qcom-next
branch (v7.2 based): both cameras streaming concurrently on the two
CSI-2 ports, and each camera started and stopped repeatedly while the
other keeps streaming, without interference. v2 was re-run on the
qcom-next tree, including the CSID test pattern generator after patch
4. Each patch builds and was bisected against next-20260911 for arm64.
Changes in v2:
- Fixes first, with Fixes: tags and "Fix" titles, commit logs rewritten
(Bryan)
- 1/5: no PIX special case, plain removal of the IRQ_STATUS_0 gate
(Bryan)
- 2/5: use vfe_get_output_v2() on 17x like the other gen2 VFEs instead
of a 17x-only mapping; no PIX special case (Bryan)
- 3/5: commit log rewritten without part names and with the affected
line spelled out (Bryan)
- 4/5: camss taken from the entity's media device instead of an extra
argument, so the callers are unchanged; multi-line clause split
(Bryan)
- 5/5: Reviewed-by added (Bryan)
- v1 6-8 dropped in favour of [1] (Loic, Bryan)
[1] https://lore.kernel.org/all/20260911062213.195007-1-gjorgji.rosikopulos@oss.qualcomm.com/
[2] https://lore.kernel.org/linux-i2c/20260914132328.976902-1-hitesh@ebytelogic.com/
Hitesh Patel (5):
media: qcom: camss: vfe-17x: Fix write master buffer done being
dropped
media: qcom: camss: vfe-17x: Fix write master selection for RDI lines
media: qcom: camss: vfe: Fix VFE reset while another line is streaming
media: qcom: camss: Take the link frequency from the CSI-2 transmitter
media: qcom: camss: Create the source to CSIPHY link per endpoint
.../media/platform/qcom/camss/camss-vfe-17x.c | 46 +------
drivers/media/platform/qcom/camss/camss-vfe.c | 8 ++
drivers/media/platform/qcom/camss/camss.c | 121 ++++++++++++------
3 files changed, 93 insertions(+), 82 deletions(-)
base-commit: 68142f986ff04b2b70b31db00f719bf690f64a9a
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v2 1/5] media: qcom: camss: vfe-17x: Fix write master buffer done being dropped
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 ` 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
` (4 subsequent siblings)
5 siblings, 0 replies; 34+ messages in thread
From: Hitesh Patel @ 2026-09-15 12:15 UTC (permalink / raw)
To: linux-media
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, Gjorgji Rosikopulos, linux-arm-msm,
linux-kernel, ravi, Hitesh Patel
The VFE 17x interrupt handler reads and clears the bus status
registers on every interrupt, but only acts on the per write master
BUF_DONE bits when bit 9 of IRQ_STATUS_0 is set as well. Bit 9 is the
ping-pong flag of image master 1. It has nothing to do with the other
write masters, and it is not guaranteed to be set in the same
interrupt in which a write master reports a completed buffer.
The bus status is read-to-clear. When a buffer done arrives while bit
9 is not set, the handler clears the status and never calls
wm_done() for it. The buffer stays queued in the driver and the video
node never receives that frame.
With one RDI streaming this is rare. With two RDIs streaming on the
same VFE, for instance two virtual channels demultiplexed by the CSID,
the interrupt rate doubles and one of the two lines loses buffer done
events continuously.
Act on the bus status alone, as the other gen2 VFE handlers do.
Fixes: 7319cdf189bb ("media: camss: Add support for VFE hardware version Titan 170")
Signed-off-by: Hitesh Patel <hitesh@ebytelogic.com>
---
drivers/media/platform/qcom/camss/camss-vfe-17x.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-17x.c b/drivers/media/platform/qcom/camss/camss-vfe-17x.c
index e5ee7e717..c011f64f6 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-17x.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-17x.c
@@ -364,9 +364,8 @@ static irqreturn_t vfe_isr(int irq, void *dev)
vfe->isr_ops.comp_done(vfe, i);
for (wm = 0; wm < MSM_VFE_IMAGE_MASTERS_NUM; wm++)
- if (status0 & BIT(9))
- if (vfe_bus_status[1] & STATUS1_WM_CLIENT_BUF_DONE(wm))
- vfe->isr_ops.wm_done(vfe, wm);
+ if (vfe_bus_status[1] & STATUS1_WM_CLIENT_BUF_DONE(wm))
+ vfe->isr_ops.wm_done(vfe, wm);
return IRQ_HANDLED;
}
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v2 2/5] media: qcom: camss: vfe-17x: Fix write master selection for RDI lines
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 ` 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
` (3 subsequent siblings)
5 siblings, 0 replies; 34+ messages in thread
From: Hitesh Patel @ 2026-09-15 12:15 UTC (permalink / raw)
To: linux-media
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, Gjorgji Rosikopulos, linux-arm-msm,
linux-kernel, ravi, Hitesh Patel
On the gen2 VFE bus there is no crossbar between the RDI paths and
the bus write masters: RDI n is served by write master n. The common
vfe_get_output_v2() reflects this by mapping line->id to write master
line->id, and every gen2 VFE except 17x uses it.
The 17x code kept its own vfe_get_output(), which reserves whichever
write master is free first. That happens to be the right one as long
as a single line streams and it is RDI0. As soon as two lines of the
same VFE stream, for instance two virtual channels demultiplexed by
the CSID to RDI0 and RDI1, the second line to start is given the
write master of the other RDI. Both write masters are then programmed
with the buffer address and frame size of the wrong line. The frames
are truncated to the smaller of the two buffers and the SMMU faults
on the larger one.
Use vfe_get_output_v2() on 17x like the other gen2 VFEs and drop the
duplicate. This also removes an error path that released
output->wm_idx[0] before it had been assigned.
The gen1 VFEs are not affected: they have a bus crossbar and any write
master can serve any line, so vfe_reserve_wm() is correct there.
Fixes: 7319cdf189bb ("media: camss: Add support for VFE hardware version Titan 170")
Signed-off-by: Hitesh Patel <hitesh@ebytelogic.com>
---
.../media/platform/qcom/camss/camss-vfe-17x.c | 41 +------------------
1 file changed, 1 insertion(+), 40 deletions(-)
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-17x.c b/drivers/media/platform/qcom/camss/camss-vfe-17x.c
index c011f64f6..62c240535 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-17x.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-17x.c
@@ -382,45 +382,6 @@ static int vfe_halt(struct vfe_device *vfe)
return 0;
}
-static int vfe_get_output(struct vfe_line *line)
-{
- struct vfe_device *vfe = to_vfe(line);
- struct vfe_output *output;
- unsigned long flags;
- int wm_idx;
-
- spin_lock_irqsave(&vfe->output_lock, flags);
-
- output = &line->output;
- if (output->state > VFE_OUTPUT_RESERVED) {
- dev_err(vfe->camss->dev, "Output is running\n");
- goto error;
- }
-
- output->wm_num = 1;
-
- wm_idx = vfe_reserve_wm(vfe, line->id);
- if (wm_idx < 0) {
- dev_err(vfe->camss->dev, "Can not reserve wm\n");
- goto error_get_wm;
- }
- output->wm_idx[0] = wm_idx;
-
- output->drop_update_idx = 0;
-
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- return 0;
-
-error_get_wm:
- vfe_release_wm(vfe, output->wm_idx[0]);
- output->state = VFE_OUTPUT_OFF;
-error:
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- return -EINVAL;
-}
-
/*
* vfe_enable - Enable streaming on VFE line
* @line: VFE line
@@ -441,7 +402,7 @@ static int vfe_enable(struct vfe_line *line)
mutex_unlock(&vfe->stream_lock);
- ret = vfe_get_output(line);
+ ret = vfe_get_output_v2(line);
if (ret < 0)
goto error_get_output;
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v2 3/5] media: qcom: camss: vfe: Fix VFE reset while another line is streaming
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 ` 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
` (2 subsequent siblings)
5 siblings, 0 replies; 34+ messages in thread
From: Hitesh Patel @ 2026-09-15 12:15 UTC (permalink / raw)
To: linux-media
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, Gjorgji Rosikopulos, linux-arm-msm,
linux-kernel, ravi, Hitesh Patel
vfe_disable_output() stops the write masters of the line being
disabled and then resets the whole VFE. The reset is not limited to
that line.
When two lines of one VFE stream at the same time, which is the case
whenever a CSID demultiplexes several virtual channels to different
RDIs, stopping the first line resets the VFE underneath the second
line. The second line's write master configuration is wiped, the
buffers it had in flight are never completed, and the VFE is left in
a state in which the next global reset is not acknowledged. Stopping
or restarting the second line then fails with:
VFE reset timeout
Only reset the VFE when the line being disabled is the last one
streaming, which vfe->stream_count already tracks. Stopping the
line's write masters is enough to quiesce that line on its own. With
a single line streaming, the reset happens exactly as before.
Fixes: 7319cdf189bb ("media: camss: Add support for VFE hardware version Titan 170")
Signed-off-by: Hitesh Patel <hitesh@ebytelogic.com>
---
drivers/media/platform/qcom/camss/camss-vfe.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c
index 319d19158..9cdf26671 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe.c
@@ -814,6 +814,7 @@ static int vfe_disable_output(struct vfe_line *line)
struct vfe_output *output = &line->output;
unsigned long flags;
unsigned int i;
+ bool last;
spin_lock_irqsave(&vfe->output_lock, flags);
for (i = 0; i < output->wm_num; i++)
@@ -821,6 +822,13 @@ static int vfe_disable_output(struct vfe_line *line)
output->gen2.active_num = 0;
spin_unlock_irqrestore(&vfe->output_lock, flags);
+ mutex_lock(&vfe->stream_lock);
+ last = vfe->stream_count == 1;
+ mutex_unlock(&vfe->stream_lock);
+
+ if (!last)
+ return 0;
+
return vfe_reset(vfe);
}
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v2 4/5] media: qcom: camss: Take the link frequency from the CSI-2 transmitter
2026-09-15 12:15 ` [PATCH v2 0/5] media: qcom: camss: fixes for several cameras behind a CSI-2 bridge Hitesh Patel
` (2 preceding siblings ...)
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 ` 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
5 siblings, 0 replies; 34+ messages in thread
From: Hitesh Patel @ 2026-09-15 12:15 UTC (permalink / raw)
To: linux-media
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, Gjorgji Rosikopulos, linux-arm-msm,
linux-kernel, ravi, Hitesh Patel
camss_get_link_freq() walks the pipeline up to an entity with the
MEDIA_ENT_F_CAM_SENSOR function and reads the link frequency there.
The CSIPHY settle count and the CSID clock are then derived from it.
The frequency the receiver has to be programmed for is the one on the
CSI-2 bus, which belongs to whatever drives that bus. When the sensor
is wired straight to the CSIPHY that is the sensor, and the walk gives
the right answer. When a CSI-2 to CSI-2 bridge sits in between, such
as a GMSL or FPD-Link deserializer, the bridge re-times the data onto
its own output at its own rate: it may aggregate several sensors onto
one link, forward one sensor at a different rate, or generate a test
pattern with no sensor at all. The sensor's rate is then not what
arrives at the SoC, and the CSIPHY does not lock.
The walk can also fail before reaching a sensor. A deserializer has
one sink pad per serial link and the walk always follows pad 0; a
sensor attached to any other link is never found and streaming is
refused with "Cannot get CSI2 transmitter's link frequency".
Stop the walk at the first entity that is not a CAMSS receiver, i.e.
at the external subdev feeding the CSIPHY, and ask that pad with
v4l2_get_link_freq(). The helper queries the transmitter through
.get_mbus_config first and falls back to its V4L2_CID_LINK_FREQ and
V4L2_CID_PIXEL_RATE controls, so a bridge and a bare sensor are both
handled by the standard mechanism.
For a sensor connected directly to a CSIPHY the transmitter is the
sensor, so the pad found and the value returned do not change.
camss_find_sensor_pad() is kept for camss_get_pixel_clock() and the
frame skip query, which do want the sensor.
Signed-off-by: Hitesh Patel <hitesh@ebytelogic.com>
---
drivers/media/platform/qcom/camss/camss.c | 70 +++++++++++++++++++++--
1 file changed, 64 insertions(+), 6 deletions(-)
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 2123f6388..61e98b017 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -4619,24 +4619,82 @@ 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: 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.
+ */
+static bool camss_is_receiver_subdev(struct camss *camss,
+ struct v4l2_subdev *sd)
+{
+ unsigned int i;
+
+ for (i = 0; i < camss->res->csiphy_num; i++)
+ if (sd == &camss->csiphy[i].subdev)
+ return true;
+
+ for (i = 0; i < camss->res->csid_num; i++)
+ if (sd == &camss->csid[i].subdev)
+ return true;
+
+ return false;
+}
+
+/*
+ * camss_find_transmitter_pad - Find the pad of the CSI-2 transmitter
+ * @entity: Media entity in the current pipeline
+ *
+ * Walk the pipeline upstream through the CAMSS receiver subdevs and return the
+ * source pad of the first entity that is not one of them: the CSI-2
+ * transmitter driving the SoC.
+ *
+ * Return a pointer to the transmitter media pad or NULL if not found
+ */
+static struct media_pad *camss_find_transmitter_pad(struct media_entity *entity)
+{
+ struct camss *camss = container_of(entity->graph_obj.mdev,
+ struct camss, media_dev);
+ struct v4l2_subdev *sd;
+ struct media_pad *pad;
+
+ while (1) {
+ pad = &entity->pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ return NULL;
+
+ pad = media_pad_remote_pad_first(pad);
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+ return NULL;
+
+ entity = pad->entity;
+ sd = media_entity_to_v4l2_subdev(entity);
+
+ if (!camss_is_receiver_subdev(camss, sd))
+ return pad;
+ }
+}
+
/**
- * camss_get_link_freq - Get link frequency from sensor
+ * camss_get_link_freq - Get link frequency from the CSI-2 transmitter
* @entity: Media entity in the current pipeline
* @bpp: Number of bits per pixel for the current format
- * @lanes: Number of lanes in the link to the sensor
+ * @lanes: Number of lanes in the link to the transmitter
*
* Return link frequency on success or a negative error code otherwise
*/
s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp,
unsigned int lanes)
{
- struct media_pad *sensor_pad;
+ struct media_pad *tx_pad;
- sensor_pad = camss_find_sensor_pad(entity);
- if (!sensor_pad)
+ tx_pad = camss_find_transmitter_pad(entity);
+ if (!tx_pad)
return -ENODEV;
- return v4l2_get_link_freq(sensor_pad, bpp, 2 * lanes);
+ return v4l2_get_link_freq(tx_pad, bpp, 2 * lanes);
}
/*
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v2 5/5] media: qcom: camss: Create the source to CSIPHY link per endpoint
2026-09-15 12:15 ` [PATCH v2 0/5] media: qcom: camss: fixes for several cameras behind a CSI-2 bridge Hitesh Patel
` (3 preceding siblings ...)
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 ` 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
5 siblings, 0 replies; 34+ messages in thread
From: Hitesh Patel @ 2026-09-15 12:15 UTC (permalink / raw)
To: linux-media
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, Gjorgji Rosikopulos, linux-arm-msm,
linux-kernel, ravi, Hitesh Patel
The link from the external CSI-2 transmitter to the CSIPHY is created
in the notifier .complete() callback by walking every registered
subdev, reading the CSIPHY it was bound to from sd->host_priv and
linking the subdev's first source pad to that CSIPHY.
This assumes one transmitter feeds exactly one CSIPHY. A GMSL
deserializer such as the MAX9296A has two independent CSI-2 output
ports which, on the RB3 Gen2 vision mezzanine, are wired to two
different SoC CSIPHYs. The same subdev is then bound once per CAMSS
port endpoint, the second .bound() overwrites host_priv, and
.complete() creates a single link from source pad 0 to the last
CSIPHY. The second output port is left with no link at all, so a
second camera can never be routed to the SoC.
Move the link creation into .bound(), where both the endpoint and
the CSIPHY are known, and resolve the transmitter's source pad from
the endpoint fwnode with media_entity_get_fwnode_pad(). Each
endpoint then gets its own link between the right source pad and
the right CSIPHY.
For a subdev that does not implement .get_fwnode_pad,
media_entity_get_fwnode_pad() falls back to the first pad matching
the requested direction, which is exactly what the .complete() loop
did, so ordinary single-output sensors keep the same link as before.
Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
Signed-off-by: Hitesh Patel <hitesh@ebytelogic.com>
---
drivers/media/platform/qcom/camss/camss.c | 51 ++++++++---------------
1 file changed, 18 insertions(+), 33 deletions(-)
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 61e98b017..6932211a0 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -5231,49 +5231,34 @@ static int camss_subdev_notifier_bound(struct v4l2_async_notifier *async,
container_of(asd, struct camss_async_subdev, asd);
u8 id = csd->interface.csiphy_id;
struct csiphy_device *csiphy = &camss->csiphy[id];
+ struct media_entity *input = &csiphy->subdev.entity;
+ struct media_entity *sensor = &subdev->entity;
+ int pad, ret;
csiphy->cfg.csi2 = &csd->interface.csi2;
subdev->host_priv = csiphy;
+ pad = media_entity_get_fwnode_pad(sensor, asd->match.fwnode,
+ MEDIA_PAD_FL_SOURCE);
+ if (pad < 0) {
+ dev_err(camss->dev, "No source pad in external entity %s: %d\n",
+ sensor->name, pad);
+ return pad;
+ }
+
+ ret = media_create_pad_link(sensor, pad, input, MSM_CSIPHY_PAD_SINK,
+ MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
+ if (ret < 0) {
+ camss_link_err(camss, sensor->name, input->name, ret);
+ return ret;
+ }
+
return 0;
}
static int camss_subdev_notifier_complete(struct v4l2_async_notifier *async)
{
struct camss *camss = container_of(async, struct camss, notifier);
- struct v4l2_device *v4l2_dev = &camss->v4l2_dev;
- struct v4l2_subdev *sd;
-
- list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
- struct csiphy_device *csiphy = sd->host_priv;
- struct media_entity *input, *sensor;
- unsigned int i;
- int ret;
-
- if (!csiphy)
- continue;
-
- input = &csiphy->subdev.entity;
- sensor = &sd->entity;
-
- for (i = 0; i < sensor->num_pads; i++) {
- if (sensor->pads[i].flags & MEDIA_PAD_FL_SOURCE)
- break;
- }
- if (i == sensor->num_pads) {
- dev_err(camss->dev,
- "No source pad in external entity\n");
- return -EINVAL;
- }
-
- ret = media_create_pad_link(sensor, i, input,
- MSM_CSIPHY_PAD_SINK,
- MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
- if (ret < 0) {
- camss_link_err(camss, sensor->name, input->name, ret);
- return ret;
- }
- }
return v4l2_device_register_subdev_nodes(&camss->v4l2_dev);
}
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v2 0/5] media: qcom: camss: fixes for several cameras behind a CSI-2 bridge
2026-09-15 12:15 ` [PATCH v2 0/5] media: qcom: camss: fixes for several cameras behind a CSI-2 bridge Hitesh Patel
` (4 preceding siblings ...)
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 ` Hitesh Patel
2026-09-26 9:48 ` Bryan O'Donoghue
5 siblings, 1 reply; 34+ messages in thread
From: Hitesh Patel @ 2026-09-25 11:45 UTC (permalink / raw)
To: linux-media
Cc: Bryan O'Donoghue, Bryan O'Donoghue, Vladimir Zapolskiy,
Loic Poulain, Mauro Carvalho Chehab, Gjorgji Rosikopulos,
linux-arm-msm, linux-kernel, ravi
Hi all,
Patchwork has just moved all five patches of this series from Under
Review to Superseded, but there is no newer version: v2 is the latest
one I sent, and I have not posted a v3.
I re-checked the series against next-20260924 today:
- git am applies all five cleanly, no fuzz and no 3-way fallback
- each patch builds on its own with W=1 (arm64 defconfig +
CONFIG_VIDEO_QCOM_CAMSS=m), so the series is bisectable
- checkpatch --strict is clean on all five
so as far as I can tell the series is still current. 5/5 carries
Bryan's Reviewed-by; 1/5 to 4/5 have not been reviewed yet.
Was the state change intentional? If you would like a resend rebased on
current media master I will send a v3 right away; otherwise, could the
patches be put back to Under Review so they stay in the queue?
Thanks,
Hitesh
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v2 0/5] media: qcom: camss: fixes for several cameras behind a CSI-2 bridge
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
0 siblings, 0 replies; 34+ messages in thread
From: Bryan O'Donoghue @ 2026-09-26 9:48 UTC (permalink / raw)
To: Hitesh Patel, linux-media
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
Mauro Carvalho Chehab, Gjorgji Rosikopulos, linux-arm-msm,
linux-kernel, ravi
On 25/09/2026 12:45, Hitesh Patel wrote:
> Hi all,
>
> Patchwork has just moved all five patches of this series from Under
> Review to Superseded, but there is no newer version: v2 is the latest
> one I sent, and I have not posted a v3.
>
> I re-checked the series against next-20260924 today:
>
> - git am applies all five cleanly, no fuzz and no 3-way fallback
> - each patch builds on its own with W=1 (arm64 defconfig +
> CONFIG_VIDEO_QCOM_CAMSS=m), so the series is bisectable
> - checkpatch --strict is clean on all five
>
> so as far as I can tell the series is still current. 5/5 carries
> Bryan's Reviewed-by; 1/5 to 4/5 have not been reviewed yet.
>
> Was the state change intentional? If you would like a resend rebased on
> current media master I will send a v3 right away; otherwise, could the
> patches be put back to Under Review so they stay in the queue?
>
> Thanks,
> Hitesh
OK, sending a series as a response to an existing series is not clear
what is the intent - particularly when the other series about
multi-stream had inline patch responses to Gjorgi's patches.
If you intend for this to be evalulated as a series, send it as a series
not as a response to an existing series.
Too confusing.
---
bod
^ permalink raw reply [flat|nested] 34+ messages in thread
end of thread, other threads:[~2026-09-26 9:48 UTC | newest]
Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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
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
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®