Re: [PATCH v2 2/9] media: qcom: camss: Introduce SOF sequence tracking

From: Loic Poulain

Date: Mon Nov 02 2026 - 12:18:51 EST


On Mon, Nov 2, 2026 at 2:57 PM <bryan.odonoghue@xxxxxxxxxx> wrote:
>
> From: Bryan O'Donoghue <bryan.odonoghue@xxxxxxxxxx>
>
> In order to communicate to user-space CSI2 SOF events we will need to hook
> into and control v4l2_ctrl_subdev_subscribe_event. In-line with other
> implementations of FRAME_SYNC have CSI decoders which support the event
> always generate it in our case if and only if an atomic user count
> indicates it should be so.
>
> A flag in the CSID descriptor indicates if frame_sync_en is supported so
> for each SoC that supports it this flag should be true. Since bool is false
> by default no negative enumeration of existing or in-flight CSID is
> required.
>
> Subsequent patches will enable on a per-CSID basis.
>
> Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@xxxxxxxxxx>
> ---
> drivers/media/platform/qcom/camss/camss-csid.c | 54 +++++++++++++++++++++++++-
> drivers/media/platform/qcom/camss/camss-csid.h | 12 ++++++
> 2 files changed, 65 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c
> index a00791b00feb7..e9b399fcb9c7a 100644
> --- a/drivers/media/platform/qcom/camss/camss-csid.c
> +++ b/drivers/media/platform/qcom/camss/camss-csid.c
> @@ -760,6 +760,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
> static int csid_set_stream(struct v4l2_subdev *sd, int enable)
> {
> struct csid_device *csid = v4l2_get_subdevdata(sd);
> + unsigned int i;
> int ret;
>
> if (enable) {
> @@ -778,6 +779,9 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable)
> }
>
> if (csid->phy.need_vc_update) {
> + for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++)
> + atomic_set(&csid->sof_seq[i], -1);

Should we introduce a `csid_sof_reset()` helper and only invoke it on
the enable path?
For example, on disable we currently reset the value before stopping
the CSID, so a late IRQ could still arrive and update `seq`. In
practice this is probably harmless since the value gets reset again on
the next enable...

Also, a short comment explaining why the sequence is initialized to
`-1` would be helpful.

> +
> csid->res->hw_ops->configure_stream(csid, enable);
> csid->phy.need_vc_update = false;
> }
> @@ -1316,9 +1320,57 @@ static int csid_link_setup(struct media_entity *entity,
> return 0;
> }
>
> +static int csid_frame_sync_add(struct v4l2_subscribed_event *sev, unsigned int elems)
> +{
> + struct csid_device *csid = v4l2_get_subdevdata(vdev_to_v4l2_subdev(sev->fh->vdev));
> +
> + atomic_inc(&csid->frame_sync_users[sev->id]);
> +
> + return 0;
> +}
> +
> +static void csid_frame_sync_del(struct v4l2_subscribed_event *sev)
> +{
> + struct csid_device *csid = v4l2_get_subdevdata(vdev_to_v4l2_subdev(sev->fh->vdev));
> +
> + atomic_dec(&csid->frame_sync_users[sev->id]);
> +}
> +
> +static const struct v4l2_subscribed_event_ops csid_frame_sync_ops = {
> + .add = csid_frame_sync_add,
> + .del = csid_frame_sync_del,
> +};
> +
> +static int csid_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
> + struct v4l2_event_subscription *sub)
> +{
> + struct csid_device *csid = v4l2_get_subdevdata(sd);
> +
> + if (sub->type != V4L2_EVENT_FRAME_SYNC)
> + return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub);
> +
> + if (sub->id >= MSM_CSID_MAX_SRC_STREAMS || !csid->res->frame_sync_en)
> + return -EINVAL;
> +
> + return v4l2_event_subscribe(fh, sub, 0, &csid_frame_sync_ops);
> +}
> +
> +void csid_sof_event(struct csid_device *csid, unsigned int id)
> +{
> + struct v4l2_event event = {
> + .type = V4L2_EVENT_FRAME_SYNC,
> + .id = id,
> + .u.frame_sync.frame_sequence =
> + atomic_inc_return(&csid->sof_seq[id]),
> + };
> +
> + if (atomic_read(&csid->frame_sync_users[id]))
> + v4l2_event_queue(csid->subdev.devnode, &event);
> +}
> +
> static const struct v4l2_subdev_core_ops csid_core_ops = {
> .s_power = csid_set_power,
> - .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
> + .subscribe_event = csid_subscribe_event,
> .unsubscribe_event = v4l2_event_subdev_unsubscribe,
> };
>
> diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h
> index 5296b10f6bac8..2e48b08b93592 100644
> --- a/drivers/media/platform/qcom/camss/camss-csid.h
> +++ b/drivers/media/platform/qcom/camss/camss-csid.h
> @@ -141,6 +141,7 @@ struct csid_hw_ops {
>
> struct csid_subdev_resources {
> bool is_lite;
> + bool frame_sync_en;
> const struct csid_hw_ops *hw_ops;
> const struct parent_dev_ops *parent_dev_ops;
> const struct csid_formats *formats;
> @@ -167,6 +168,8 @@ struct csid_device {
> struct v4l2_ctrl_handler ctrls;
> struct v4l2_ctrl *testgen_mode;
> const struct csid_subdev_resources *res;
> + atomic_t frame_sync_users[MSM_CSID_MAX_SRC_STREAMS];
> + atomic_t sof_seq[MSM_CSID_MAX_SRC_STREAMS];
> };
>
> struct camss_subdev_resources;
> @@ -248,4 +251,13 @@ u32 csid_hw_version(struct csid_device *csid);
> u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code,
> unsigned int match_format_idx, u32 match_code);
>
> +/*
> + * csid_sof_event - Track SOF generate V4L2_EVENT_FRAME_SYNC
> + * @csid: CSID device
> + * @id: CSID src-stream ID
> + *
> + * Called from the CSID interrupt handler on SOF of the path driving the event.
> + */
> +void csid_sof_event(struct csid_device *csid, unsigned int id);
> +
> #endif /* QC_MSM_CAMSS_CSID_H */
>
> --
> 2.56.0
>