[RFC PATCH] media: v4l: implement virtual channels

From: Cosmin Tanislav
Date: Wed Jan 10 2024 - 07:46:38 EST


With experimental support for multiple streams per pad being added, the
pieces are in place to support a virtual channel id per stream.

This is necessary because stream ids cannot be directly mapped to a virtual
channel id, since the same virtual channel id can be assigned to multiple
streams of data, each with a different data type.

To implement this, the following steps have been taken.

Add subdev ioctls for getting and setting the virtual channel for a
specific pad and stream.

Add pad .get_vc() and .set_vc() ops.

Add the virtual channel to the stream config in V4L2 subdev central state.

Add a default .get_vc() implementation that retrieves the virtual channel
from the central state, or, if that is not supported, default to virtual
channel 0.

Signed-off-by: Cosmin Tanislav <demonsingur@xxxxxxxxx>
---
drivers/media/v4l2-core/v4l2-subdev.c | 57 +++++++++++++++++++++++++++
include/media/v4l2-subdev.h | 39 ++++++++++++++++++
include/uapi/linux/v4l2-subdev.h | 18 +++++++++
3 files changed, 114 insertions(+)

diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index be86b906c985..8945bfd0fe12 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -535,6 +535,9 @@ subdev_ioctl_get_state(struct v4l2_subdev *sd, struct v4l2_subdev_fh *subdev_fh,
case VIDIOC_SUBDEV_S_ROUTING:
which = ((struct v4l2_subdev_routing *)arg)->which;
break;
+ case VIDIOC_SUBDEV_G_VC:
+ case VIDIOC_SUBDEV_S_VC:
+ which = ((struct v4l2_subdev_vc *)arg)->which;
}

return which == V4L2_SUBDEV_FORMAT_TRY ?
@@ -969,6 +972,26 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
routing->which, &krouting);
}

+ case VIDIOC_SUBDEV_G_VC: {
+ struct v4l2_subdev_vc *vc = arg;
+
+ if (!client_supports_streams)
+ vc->stream = 0;
+
+ memset(vc->reserved, 0, sizeof(vc->reserved));
+ return v4l2_subdev_call(sd, pad, get_vc, state, vc);
+ }
+
+ case VIDIOC_SUBDEV_S_VC: {
+ struct v4l2_subdev_vc *vc = arg;
+
+ if (!client_supports_streams)
+ vc->stream = 0;
+
+ memset(vc->reserved, 0, sizeof(vc->reserved));
+ return v4l2_subdev_call(sd, pad, set_vc, state, vc);
+ }
+
case VIDIOC_SUBDEV_G_CLIENT_CAP: {
struct v4l2_subdev_client_capability *client_cap = arg;

@@ -1602,6 +1625,20 @@ int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state,
}
EXPORT_SYMBOL_GPL(v4l2_subdev_get_fmt);

+int v4l2_subdev_get_vc(struct v4l2_subdev *sd, struct v4l2_subdev_state *state,
+ struct v4l2_subdev_vc *vc)
+{
+ u32 vc_id = 0;
+
+ if (sd->flags & V4L2_SUBDEV_FL_STREAMS)
+ vc_id = v4l2_subdev_state_get_stream_vc(state, vc->pad, vc->stream);
+
+ vc->vc = vc_id;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_get_vc);
+
int v4l2_subdev_set_routing(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
const struct v4l2_subdev_krouting *routing)
@@ -1745,6 +1782,26 @@ v4l2_subdev_state_get_stream_compose(struct v4l2_subdev_state *state,
}
EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_stream_compose);

+u32 v4l2_subdev_state_get_stream_vc(struct v4l2_subdev_state *state,
+ unsigned int pad, u32 stream)
+{
+ struct v4l2_subdev_stream_configs *stream_configs;
+ unsigned int i;
+
+ lockdep_assert_held(state->lock);
+
+ stream_configs = &state->stream_configs;
+
+ for (i = 0; i < stream_configs->num_configs; ++i) {
+ if (stream_configs->configs[i].pad == pad &&
+ stream_configs->configs[i].stream == stream)
+ return stream_configs->configs[i].vc;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_stream_vc);
+
int v4l2_subdev_routing_find_opposite_end(const struct v4l2_subdev_krouting *routing,
u32 pad, u32 stream, u32 *other_pad,
u32 *other_stream)
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index c1f90c1223a7..ed1fdd79c2bb 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -722,6 +722,7 @@ struct v4l2_subdev_stream_config {
u32 stream;
bool enabled;

+ u32 vc;
struct v4l2_mbus_framefmt fmt;
struct v4l2_rect crop;
struct v4l2_rect compose;
@@ -858,6 +859,12 @@ struct v4l2_subdev_pad_ops {
int (*set_fmt)(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format);
+ int (*get_vc)(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_vc *vc);
+ int (*set_vc)(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_vc *vc);
int (*get_selection)(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel);
@@ -1494,6 +1501,23 @@ v4l2_subdev_lock_and_get_active_state(struct v4l2_subdev *sd)
int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format);

+/**
+ * v4l2_subdev_get_vc() - Fill virtual channel based on state
+ * @sd: subdevice
+ * @state: subdevice state
+ * @vc: pointer to &struct v4l2_subdev_vc
+ *
+ * Fill @vc->vc field based on the information in the @vc struct.
+ *
+ * This function can be used by the subdev drivers which support active state to
+ * implement v4l2_subdev_pad_ops.get_vc if the subdev driver does not need to
+ * do anything special in their get_vc op.
+ *
+ * Returns 0 on success, error value otherwise.
+ */
+int v4l2_subdev_get_vc(struct v4l2_subdev *sd, struct v4l2_subdev_state *state,
+ struct v4l2_subdev_vc *vc);
+
/**
* v4l2_subdev_set_routing() - Set given routing to subdev state
* @sd: The subdevice
@@ -1585,6 +1609,21 @@ struct v4l2_rect *
v4l2_subdev_state_get_stream_compose(struct v4l2_subdev_state *state,
unsigned int pad, u32 stream);

+/**
+ * v4l2_subdev_state_get_stream_vc() - Get the virtual channel of a stream
+ * @state: subdevice state
+ * @pad: pad id
+ * @stream: stream id
+ *
+ * This returns the virtual channel for the given pad + stream in the
+ * subdev state.
+ *
+ * If the state does not contain the given pad + stream, 0 is returned.
+ */
+u32
+v4l2_subdev_state_get_stream_vc(struct v4l2_subdev_state *state,
+ unsigned int pad, u32 stream);
+
/**
* v4l2_subdev_routing_find_opposite_end() - Find the opposite stream
* @routing: routing used to find the opposite side
diff --git a/include/uapi/linux/v4l2-subdev.h b/include/uapi/linux/v4l2-subdev.h
index b383c2fe0cf3..8e90405bb1e6 100644
--- a/include/uapi/linux/v4l2-subdev.h
+++ b/include/uapi/linux/v4l2-subdev.h
@@ -187,6 +187,22 @@ struct v4l2_subdev_capability {
__u32 reserved[14];
};

+/**
+ * struct v4l2_subdev_vc - Pad-level virtual channel settings
+ * @which: format type (from enum v4l2_subdev_format_whence)
+ * @pad: pad number, as reported by the media API
+ * @vc: virtual channel
+ * @stream: stream number, defined in subdev routing
+ * @reserved: drivers and applications must zero this array
+ */
+struct v4l2_subdev_vc {
+ __u32 which;
+ __u32 pad;
+ __u32 vc;
+ __u32 stream;
+ __u32 reserved[7];
+};
+
/* The v4l2 sub-device video device node is registered in read-only mode. */
#define V4L2_SUBDEV_CAP_RO_SUBDEV 0x00000001

@@ -268,6 +284,8 @@ struct v4l2_subdev_client_capability {
#define VIDIOC_SUBDEV_S_SELECTION _IOWR('V', 62, struct v4l2_subdev_selection)
#define VIDIOC_SUBDEV_G_ROUTING _IOWR('V', 38, struct v4l2_subdev_routing)
#define VIDIOC_SUBDEV_S_ROUTING _IOWR('V', 39, struct v4l2_subdev_routing)
+#define VIDIOC_SUBDEV_G_VC _IOWR('V', 40, struct v4l2_subdev_vc)
+#define VIDIOC_SUBDEV_S_VC _IOWR('V', 41, struct v4l2_subdev_vc)
#define VIDIOC_SUBDEV_G_CLIENT_CAP _IOR('V', 101, struct v4l2_subdev_client_capability)
#define VIDIOC_SUBDEV_S_CLIENT_CAP _IOWR('V', 102, struct v4l2_subdev_client_capability)

--
2.43.0