From c2a7f7a406117ba616774c89c3a3528a0320568f Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 26 Sep 2022 07:51:45 +0200 Subject: media: v4l2-subdev: Sort includes Sort the includes alphabetically. Signed-off-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-subdev.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 4988a25bd8f4..24acb4aa0895 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -8,20 +8,20 @@ * Sakari Ailus */ +#include #include #include #include #include #include -#include -#include #include +#include #include #include -#include -#include #include +#include +#include #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) -- cgit v1.2.3 From 9a6b5bf4c1bba341eeb010f3ea8b5b48651f65f8 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 22 Nov 2021 16:18:23 +0100 Subject: media: add V4L2_SUBDEV_CAP_STREAMS Add a subdev capability flag to expose to userspace if a subdev supports multiplexed streams. Signed-off-by: Tomi Valkeinen Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-subdev.c | 5 ++++- include/uapi/linux/v4l2-subdev.h | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 24acb4aa0895..3adb186bb742 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -446,6 +446,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); struct v4l2_fh *vfh = file->private_data; bool ro_subdev = test_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags); + bool streams_subdev = sd->flags & V4L2_SUBDEV_FL_STREAMS; int rval; switch (cmd) { @@ -454,7 +455,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, memset(cap->reserved, 0, sizeof(cap->reserved)); cap->version = LINUX_VERSION_CODE; - cap->capabilities = ro_subdev ? V4L2_SUBDEV_CAP_RO_SUBDEV : 0; + cap->capabilities = + (ro_subdev ? V4L2_SUBDEV_CAP_RO_SUBDEV : 0) | + (streams_subdev ? V4L2_SUBDEV_CAP_STREAMS : 0); return 0; } diff --git a/include/uapi/linux/v4l2-subdev.h b/include/uapi/linux/v4l2-subdev.h index ecce4c79f5c5..ffed9047e0be 100644 --- a/include/uapi/linux/v4l2-subdev.h +++ b/include/uapi/linux/v4l2-subdev.h @@ -175,6 +175,9 @@ struct v4l2_subdev_capability { /* The v4l2 sub-device video device node is registered in read-only mode. */ #define V4L2_SUBDEV_CAP_RO_SUBDEV 0x00000001 +/* The v4l2 sub-device supports routing and multiplexed streams. */ +#define V4L2_SUBDEV_CAP_STREAMS 0x00000002 + /* Backwards compatibility define --- to be removed */ #define v4l2_subdev_edid v4l2_edid -- cgit v1.2.3 From a418bb3f30d9ac570d51ff3f700851b78da2a8a9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 23 Apr 2021 09:15:54 +0200 Subject: media: subdev: Add [GS]_ROUTING subdev ioctls and operations Add support for subdev internal routing. A route is defined as a single stream from a sink pad to a source pad. The userspace can configure the routing via two new ioctls, VIDIOC_SUBDEV_G_ROUTING and VIDIOC_SUBDEV_S_ROUTING, and subdevs can implement the functionality with v4l2_subdev_pad_ops.set_routing(). - Add sink and source streams for multiplexed links - Copy the argument back in case of an error. This is needed to let the caller know the number of routes. - Expand and refine documentation. - Make the 'routes' pointer a __u64 __user pointer so that a compat32 version of the ioctl is not required. - Add struct v4l2_subdev_krouting to be used for subdevice operations. - Fix typecasing warnings - Check sink & source pad types - Add 'which' field - Routing to subdev state - Dropped get_routing subdev op Signed-off-by: Laurent Pinchart Signed-off-by: Michal Simek Signed-off-by: Sakari Ailus Signed-off-by: Jacopo Mondi Signed-off-by: Tomi Valkeinen Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 25 ++++++++++- drivers/media/v4l2-core/v4l2-subdev.c | 84 +++++++++++++++++++++++++++++++++++ include/media/v4l2-subdev.h | 22 +++++++++ include/uapi/linux/v4l2-subdev.h | 43 ++++++++++++++++++ 4 files changed, 173 insertions(+), 1 deletion(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 8e0a0ff62a70..b26da2650289 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -16,6 +16,7 @@ #include #include +#include #include #include /* for media_set_bus_info() */ @@ -3148,6 +3149,21 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size, ret = 1; break; } + + case VIDIOC_SUBDEV_G_ROUTING: + case VIDIOC_SUBDEV_S_ROUTING: { + struct v4l2_subdev_routing *routing = parg; + + if (routing->num_routes > 256) + return -E2BIG; + + *user_ptr = u64_to_user_ptr(routing->routes); + *kernel_ptr = (void **)&routing->routes; + *array_size = sizeof(struct v4l2_subdev_route) + * routing->num_routes; + ret = 1; + break; + } } return ret; @@ -3394,8 +3410,15 @@ video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg, /* * Some ioctls can return an error, but still have valid * results that must be returned. + * + * FIXME: subdev IOCTLS are partially handled here and partially in + * v4l2-subdev.c and the 'always_copy' flag can only be set for IOCTLS + * defined here as part of the 'v4l2_ioctls' array. As + * VIDIOC_SUBDEV_G_ROUTING needs to return results to applications even + * in case of failure, but it is not defined here as part of the + * 'v4l2_ioctls' array, insert an ad-hoc check to address that. */ - if (err < 0 && !always_copy) + if (err < 0 && !always_copy && cmd != VIDIOC_SUBDEV_G_ROUTING) goto out; if (has_array_args) { diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 3adb186bb742..f047ac1bf5ef 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -23,6 +23,16 @@ #include #include +/* + * Maximum stream ID is 63 for now, as we use u64 bitmask to represent a set + * of streams. + * + * Note that V4L2_FRAME_DESC_ENTRY_MAX is related: V4L2_FRAME_DESC_ENTRY_MAX + * restricts the total number of streams in a pad, although the stream ID is + * not restricted. + */ +#define V4L2_SUBDEV_MAX_STREAM_ID 63 + #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) { @@ -432,6 +442,10 @@ subdev_ioctl_get_state(struct v4l2_subdev *sd, struct v4l2_subdev_fh *subdev_fh, case VIDIOC_SUBDEV_S_SELECTION: which = ((struct v4l2_subdev_selection *)arg)->which; break; + case VIDIOC_SUBDEV_G_ROUTING: + case VIDIOC_SUBDEV_S_ROUTING: + which = ((struct v4l2_subdev_routing *)arg)->which; + break; } return which == V4L2_SUBDEV_FORMAT_TRY ? @@ -748,6 +762,75 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, case VIDIOC_SUBDEV_QUERYSTD: return v4l2_subdev_call(sd, video, querystd, arg); + case VIDIOC_SUBDEV_G_ROUTING: { + struct v4l2_subdev_routing *routing = arg; + struct v4l2_subdev_krouting *krouting; + + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) + return -ENOIOCTLCMD; + + memset(routing->reserved, 0, sizeof(routing->reserved)); + + krouting = &state->routing; + + if (routing->num_routes < krouting->num_routes) { + routing->num_routes = krouting->num_routes; + return -ENOSPC; + } + + memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes, + krouting->routes, + krouting->num_routes * sizeof(*krouting->routes)); + routing->num_routes = krouting->num_routes; + + return 0; + } + + case VIDIOC_SUBDEV_S_ROUTING: { + struct v4l2_subdev_routing *routing = arg; + struct v4l2_subdev_route *routes = + (struct v4l2_subdev_route *)(uintptr_t)routing->routes; + struct v4l2_subdev_krouting krouting = {}; + unsigned int i; + + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) + return -ENOIOCTLCMD; + + if (routing->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) + return -EPERM; + + memset(routing->reserved, 0, sizeof(routing->reserved)); + + for (i = 0; i < routing->num_routes; ++i) { + const struct v4l2_subdev_route *route = &routes[i]; + const struct media_pad *pads = sd->entity.pads; + + if (route->sink_stream > V4L2_SUBDEV_MAX_STREAM_ID || + route->source_stream > V4L2_SUBDEV_MAX_STREAM_ID) + return -EINVAL; + + if (route->sink_pad >= sd->entity.num_pads) + return -EINVAL; + + if (!(pads[route->sink_pad].flags & + MEDIA_PAD_FL_SINK)) + return -EINVAL; + + if (route->source_pad >= sd->entity.num_pads) + return -EINVAL; + + if (!(pads[route->source_pad].flags & + MEDIA_PAD_FL_SOURCE)) + return -EINVAL; + } + + krouting.num_routes = routing->num_routes; + krouting.routes = routes; + + return v4l2_subdev_call(sd, pad, set_routing, state, + routing->which, &krouting); + } + default: return v4l2_subdev_call(sd, core, ioctl, cmd, arg); } @@ -1031,6 +1114,7 @@ void __v4l2_subdev_state_free(struct v4l2_subdev_state *state) mutex_destroy(&state->_lock); + kfree(state->routing.routes); kvfree(state->pads); kfree(state); } diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 95361b541087..67e55758f037 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -700,12 +700,26 @@ struct v4l2_subdev_pad_config { struct v4l2_rect try_compose; }; +/** + * struct v4l2_subdev_krouting - subdev routing table + * + * @num_routes: number of routes + * @routes: &struct v4l2_subdev_route + * + * This structure contains the routing table for a subdev. + */ +struct v4l2_subdev_krouting { + unsigned int num_routes; + struct v4l2_subdev_route *routes; +}; + /** * struct v4l2_subdev_state - Used for storing subdev state information. * * @_lock: default for 'lock' * @lock: mutex for the state. May be replaced by the user. * @pads: &struct v4l2_subdev_pad_config array + * @routing: routing table for the subdev * * This structure only needs to be passed to the pad op if the 'which' field * of the main argument is set to %V4L2_SUBDEV_FORMAT_TRY. For @@ -716,6 +730,7 @@ struct v4l2_subdev_state { struct mutex _lock; struct mutex *lock; struct v4l2_subdev_pad_config *pads; + struct v4l2_subdev_krouting routing; }; /** @@ -768,6 +783,9 @@ struct v4l2_subdev_state { * this operation as close as possible to stream on time. The * operation shall fail if the pad index it has been called on * is not valid or in case of unrecoverable failures. + * + * @set_routing: enable or disable data connection routes described in the + * subdevice routing table. */ struct v4l2_subdev_pad_ops { int (*init_cfg)(struct v4l2_subdev *sd, @@ -810,6 +828,10 @@ struct v4l2_subdev_pad_ops { struct v4l2_mbus_frame_desc *fd); int (*get_mbus_config)(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_config *config); + int (*set_routing)(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *route); }; /** diff --git a/include/uapi/linux/v4l2-subdev.h b/include/uapi/linux/v4l2-subdev.h index ffed9047e0be..284ae8c95960 100644 --- a/include/uapi/linux/v4l2-subdev.h +++ b/include/uapi/linux/v4l2-subdev.h @@ -11,6 +11,7 @@ #ifndef __LINUX_V4L2_SUBDEV_H #define __LINUX_V4L2_SUBDEV_H +#include #include #include #include @@ -178,6 +179,46 @@ struct v4l2_subdev_capability { /* The v4l2 sub-device supports routing and multiplexed streams. */ #define V4L2_SUBDEV_CAP_STREAMS 0x00000002 +/* + * Is the route active? An active route will start when streaming is enabled + * on a video node. + */ +#define V4L2_SUBDEV_ROUTE_FL_ACTIVE (1U << 0) + +/** + * struct v4l2_subdev_route - A route inside a subdev + * + * @sink_pad: the sink pad index + * @sink_stream: the sink stream identifier + * @source_pad: the source pad index + * @source_stream: the source stream identifier + * @flags: route flags V4L2_SUBDEV_ROUTE_FL_* + * @reserved: drivers and applications must zero this array + */ +struct v4l2_subdev_route { + __u32 sink_pad; + __u32 sink_stream; + __u32 source_pad; + __u32 source_stream; + __u32 flags; + __u32 reserved[5]; +}; + +/** + * struct v4l2_subdev_routing - Subdev routing information + * + * @which: configuration type (from enum v4l2_subdev_format_whence) + * @num_routes: the total number of routes in the routes array + * @routes: pointer to the routes array + * @reserved: drivers and applications must zero this array + */ +struct v4l2_subdev_routing { + __u32 which; + __u32 num_routes; + __u64 routes; + __u32 reserved[6]; +}; + /* Backwards compatibility define --- to be removed */ #define v4l2_subdev_edid v4l2_edid @@ -193,6 +234,8 @@ struct v4l2_subdev_capability { #define VIDIOC_SUBDEV_S_CROP _IOWR('V', 60, struct v4l2_subdev_crop) #define VIDIOC_SUBDEV_G_SELECTION _IOWR('V', 61, struct v4l2_subdev_selection) #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) /* The following ioctls are identical to the ioctls in videodev2.h */ #define VIDIOC_SUBDEV_G_STD _IOR('V', 23, v4l2_std_id) #define VIDIOC_SUBDEV_S_STD _IOW('V', 24, v4l2_std_id) -- cgit v1.2.3 From 8a54644571fed484d55b3807f25f64cba8a9ca77 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Sun, 15 Jan 2023 13:40:08 +0100 Subject: media: subdev: Require code change to enable [GS]_ROUTING The Streams API is an experimental feature. To use the Streams API, the user needs to change a variable in v4l2-subdev.c and recompile the kernel. This commit should be reverted when the Streams API is deemed ready for production use. Signed-off-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-subdev.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index f047ac1bf5ef..6cdb7a89f19c 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -23,6 +23,15 @@ #include #include +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) +/* + * The Streams API is an experimental feature. To use the Streams API, set + * 'v4l2_subdev_enable_streams_api' to 1 below. + */ + +static bool v4l2_subdev_enable_streams_api; +#endif + /* * Maximum stream ID is 63 for now, as we use u64 bitmask to represent a set * of streams. @@ -766,6 +775,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, struct v4l2_subdev_routing *routing = arg; struct v4l2_subdev_krouting *krouting; + if (!v4l2_subdev_enable_streams_api) + return -ENOIOCTLCMD; + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) return -ENOIOCTLCMD; @@ -793,6 +805,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, struct v4l2_subdev_krouting krouting = {}; unsigned int i; + if (!v4l2_subdev_enable_streams_api) + return -ENOIOCTLCMD; + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) return -ENOIOCTLCMD; -- cgit v1.2.3 From 33c0ddbe56905c98b43d7141f2fe67ae69afba3c Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 15 Jul 2021 15:39:19 +0200 Subject: media: subdev: add v4l2_subdev_has_pad_interdep() Add a v4l2_subdev_has_pad_interdep() helper function which can be used for media_entity_operations.has_pad_interdep op. It considers two pads interdependent if there is an active route between pad0 and pad1. Signed-off-by: Tomi Valkeinen Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-subdev.c | 31 +++++++++++++++++++++++++++++++ include/media/v4l2-subdev.h | 18 ++++++++++++++++++ 2 files changed, 49 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 6cdb7a89f19c..dd67b4ae61d7 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1073,6 +1073,37 @@ int v4l2_subdev_link_validate(struct media_link *link) } EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate); +bool v4l2_subdev_has_pad_interdep(struct media_entity *entity, + unsigned int pad0, unsigned int pad1) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct v4l2_subdev_krouting *routing; + struct v4l2_subdev_state *state; + unsigned int i; + + state = v4l2_subdev_lock_and_get_active_state(sd); + + routing = &state->routing; + + for (i = 0; i < routing->num_routes; ++i) { + struct v4l2_subdev_route *route = &routing->routes[i]; + + if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + continue; + + if ((route->sink_pad == pad0 && route->source_pad == pad1) || + (route->source_pad == pad0 && route->sink_pad == pad1)) { + v4l2_subdev_unlock_state(state); + return true; + } + } + + v4l2_subdev_unlock_state(state); + + return false; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_has_pad_interdep); + struct v4l2_subdev_state * __v4l2_subdev_state_alloc(struct v4l2_subdev *sd, const char *lock_name, struct lock_class_key *lock_key) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 67e55758f037..0bd674a8f84d 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1246,6 +1246,24 @@ int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd, */ int v4l2_subdev_link_validate(struct media_link *link); +/** + * v4l2_subdev_has_pad_interdep - MC has_pad_interdep implementation for subdevs + * + * @entity: pointer to &struct media_entity + * @pad0: pad number for the first pad + * @pad1: pad number for the second pad + * + * This function is an implementation of the + * media_entity_operations.has_pad_interdep operation for subdevs that + * implement the multiplexed streams API (as indicated by the + * V4L2_SUBDEV_FL_STREAMS subdev flag). + * + * It considers two pads interdependent if there is an active route between pad0 + * and pad1. + */ +bool v4l2_subdev_has_pad_interdep(struct media_entity *entity, + unsigned int pad0, unsigned int pad1); + /** * __v4l2_subdev_state_alloc - allocate v4l2_subdev_state * -- cgit v1.2.3 From 17bb9bf819c542b41d7dbddd9fe1ec82ac509604 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 21 Dec 2021 11:18:43 +0100 Subject: media: subdev: add v4l2_subdev_set_routing helper() Add a helper function to set the subdev routing. The helper can be used from subdev driver's set_routing op to store the routing table. Signed-off-by: Tomi Valkeinen Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-subdev.c | 31 +++++++++++++++++++++++++++++++ include/media/v4l2-subdev.h | 16 ++++++++++++++++ 2 files changed, 47 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index dd67b4ae61d7..d95cbea1a728 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1208,6 +1209,36 @@ int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, } EXPORT_SYMBOL_GPL(v4l2_subdev_get_fmt); +int v4l2_subdev_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + const struct v4l2_subdev_krouting *routing) +{ + struct v4l2_subdev_krouting *dst = &state->routing; + const struct v4l2_subdev_krouting *src = routing; + struct v4l2_subdev_krouting new_routing = { 0 }; + size_t bytes; + + if (unlikely(check_mul_overflow((size_t)src->num_routes, + sizeof(*src->routes), &bytes))) + return -EOVERFLOW; + + lockdep_assert_held(state->lock); + + if (src->num_routes > 0) { + new_routing.routes = kmemdup(src->routes, bytes, GFP_KERNEL); + if (!new_routing.routes) + return -ENOMEM; + } + + new_routing.num_routes = src->num_routes; + + kfree(dst->routes); + *dst = new_routing; + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_set_routing); + #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ #endif /* CONFIG_MEDIA_CONTROLLER */ diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 0bd674a8f84d..1d2a1a77bdb2 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1424,6 +1424,22 @@ 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_set_routing() - Set given routing to subdev state + * @sd: The subdevice + * @state: The subdevice state + * @routing: Routing that will be copied to subdev state + * + * This will release old routing table (if any) from the state, allocate + * enough space for the given routing, and copy the routing. + * + * This can be used from the subdev driver's set_routing op, after validating + * the routing. + */ +int v4l2_subdev_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + const struct v4l2_subdev_krouting *routing); + #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ #endif /* CONFIG_MEDIA_CONTROLLER */ -- cgit v1.2.3 From 837f92f070f6b7e877143eb168025995688b9756 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Sun, 17 Oct 2021 20:24:42 +0200 Subject: media: subdev: Add for_each_active_route() macro Add a for_each_active_route() macro to replace the repeated pattern of iterating on the active routes of a routing table. Signed-off-by: Jacopo Mondi Signed-off-by: Tomi Valkeinen Signed-off-by: Mauro Carvalho Chehab --- .clang-format | 1 + drivers/media/v4l2-core/v4l2-subdev.c | 20 ++++++++++++++++++++ include/media/v4l2-subdev.h | 13 +++++++++++++ 3 files changed, 34 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/.clang-format b/.clang-format index b62836419ea3..2c61b4553374 100644 --- a/.clang-format +++ b/.clang-format @@ -190,6 +190,7 @@ ForEachMacros: - 'for_each_active_dev_scope' - 'for_each_active_drhd_unit' - 'for_each_active_iommu' + - 'for_each_active_route' - 'for_each_aggr_pgid' - 'for_each_available_child_of_node' - 'for_each_bench' diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index d95cbea1a728..730c31eaa35e 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1239,6 +1239,26 @@ int v4l2_subdev_set_routing(struct v4l2_subdev *sd, } EXPORT_SYMBOL_GPL(v4l2_subdev_set_routing); +struct v4l2_subdev_route * +__v4l2_subdev_next_active_route(const struct v4l2_subdev_krouting *routing, + struct v4l2_subdev_route *route) +{ + if (route) + ++route; + else + route = &routing->routes[0]; + + for (; route < routing->routes + routing->num_routes; ++route) { + if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + continue; + + return route; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(__v4l2_subdev_next_active_route); + #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ #endif /* CONFIG_MEDIA_CONTROLLER */ diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 1d2a1a77bdb2..9e34c70b8d73 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1440,6 +1440,19 @@ int v4l2_subdev_set_routing(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, const struct v4l2_subdev_krouting *routing); +struct v4l2_subdev_route * +__v4l2_subdev_next_active_route(const struct v4l2_subdev_krouting *routing, + struct v4l2_subdev_route *route); + +/** + * for_each_active_route - iterate on all active routes of a routing table + * @routing: The routing table + * @route: The route iterator + */ +#define for_each_active_route(routing, route) \ + for ((route) = NULL; \ + ((route) = __v4l2_subdev_next_active_route((routing), (route)));) + #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ #endif /* CONFIG_MEDIA_CONTROLLER */ -- cgit v1.2.3 From 2f91e10ee6fd4c0c4abba4d36c013a93560cf514 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 21 Dec 2021 11:20:53 +0100 Subject: media: subdev: add stream based configuration Add support to manage configurations (format, crop, compose) per stream, instead of per pad. This is accomplished with data structures that hold an array of all subdev's stream configurations. The number of streams can vary at runtime based on routing. Every time the routing is changed, the stream configurations need to be re-initialized. Signed-off-by: Tomi Valkeinen Signed-off-by: Mauro Carvalho Chehab --- .../v4l/vidioc-subdev-enum-frame-interval.rst | 5 +- .../media/v4l/vidioc-subdev-enum-frame-size.rst | 5 +- .../media/v4l/vidioc-subdev-enum-mbus-code.rst | 5 +- .../media/v4l/vidioc-subdev-g-crop.rst | 5 +- .../media/v4l/vidioc-subdev-g-fmt.rst | 5 +- .../media/v4l/vidioc-subdev-g-frame-interval.rst | 5 +- .../media/v4l/vidioc-subdev-g-selection.rst | 5 +- drivers/media/v4l2-core/v4l2-subdev.c | 150 ++++++++++++++++++++- include/media/v4l2-subdev.h | 79 +++++++++++ include/uapi/linux/v4l2-subdev.h | 28 +++- 10 files changed, 271 insertions(+), 21 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-frame-interval.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-frame-interval.rst index 3703943b412f..8def4c05d3da 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-frame-interval.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-frame-interval.rst @@ -92,7 +92,10 @@ multiple pads of the same sub-device is not defined. - Frame intervals to be enumerated, from enum :ref:`v4l2_subdev_format_whence `. * - __u32 - - ``reserved``\ [8] + - ``stream`` + - Stream identifier. + * - __u32 + - ``reserved``\ [7] - Reserved for future extensions. Applications and drivers must set the array to zero. diff --git a/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-frame-size.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-frame-size.rst index c25a9896df0e..3ef361c0dca7 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-frame-size.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-frame-size.rst @@ -97,7 +97,10 @@ information about try formats. - Frame sizes to be enumerated, from enum :ref:`v4l2_subdev_format_whence `. * - __u32 - - ``reserved``\ [8] + - ``stream`` + - Stream identifier. + * - __u32 + - ``reserved``\ [7] - Reserved for future extensions. Applications and drivers must set the array to zero. diff --git a/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-mbus-code.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-mbus-code.rst index 417f1a19bcc4..248f6f9ee7c5 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-mbus-code.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-enum-mbus-code.rst @@ -73,7 +73,10 @@ information about the try formats. - ``flags`` - See :ref:`v4l2-subdev-mbus-code-flags` * - __u32 - - ``reserved``\ [7] + - ``stream`` + - Stream identifier. + * - __u32 + - ``reserved``\ [6] - Reserved for future extensions. Applications and drivers must set the array to zero. diff --git a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-crop.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-crop.rst index bd15c0a5a66b..1d267f7e7991 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-crop.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-crop.rst @@ -96,7 +96,10 @@ modified format should be as close as possible to the original request. - ``rect`` - Crop rectangle boundaries, in pixels. * - __u32 - - ``reserved``\ [8] + - ``stream`` + - Stream identifier. + * - __u32 + - ``reserved``\ [7] - Reserved for future extensions. Applications and drivers must set the array to zero. diff --git a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-fmt.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-fmt.rst index 7acdbb939d89..ed253a1e44b7 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-fmt.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-fmt.rst @@ -102,7 +102,10 @@ should be as close as possible to the original request. - Definition of an image format, see :c:type:`v4l2_mbus_framefmt` for details. * - __u32 - - ``reserved``\ [8] + - ``stream`` + - Stream identifier. + * - __u32 + - ``reserved``\ [7] - Reserved for future extensions. Applications and drivers must set the array to zero. diff --git a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-frame-interval.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-frame-interval.rst index d7fe7543c506..842f962d2aea 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-frame-interval.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-frame-interval.rst @@ -90,7 +90,10 @@ the same sub-device is not defined. - ``interval`` - Period, in seconds, between consecutive video frames. * - __u32 - - ``reserved``\ [9] + - ``stream`` + - Stream identifier. + * - __u32 + - ``reserved``\ [8] - Reserved for future extensions. Applications and drivers must set the array to zero. diff --git a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-selection.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-selection.rst index f9172a42f036..6b629c19168c 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-selection.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-selection.rst @@ -94,7 +94,10 @@ Selection targets and flags are documented in - ``r`` - Selection rectangle, in pixels. * - __u32 - - ``reserved``\ [8] + - ``stream`` + - Stream identifier. + * - __u32 + - ``reserved``\ [7] - Reserved for future extensions. Applications and drivers must set the array to zero. diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 730c31eaa35e..a3e412c73715 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -168,8 +168,22 @@ static inline int check_pad(struct v4l2_subdev *sd, u32 pad) return 0; } -static int check_state_pads(u32 which, struct v4l2_subdev_state *state) +static int check_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, + u32 which, u32 pad, u32 stream) { + if (sd->flags & V4L2_SUBDEV_FL_STREAMS) { +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) + if (!v4l2_subdev_state_get_stream_format(state, pad, stream)) + return -EINVAL; + return 0; +#else + return -EINVAL; +#endif + } + + if (stream != 0) + return -EINVAL; + if (which == V4L2_SUBDEV_FORMAT_TRY && (!state || !state->pads)) return -EINVAL; @@ -184,7 +198,7 @@ static inline int check_format(struct v4l2_subdev *sd, return -EINVAL; return check_which(format->which) ? : check_pad(sd, format->pad) ? : - check_state_pads(format->which, state); + check_state(sd, state, format->which, format->pad, format->stream); } static int call_get_fmt(struct v4l2_subdev *sd, @@ -211,7 +225,7 @@ static int call_enum_mbus_code(struct v4l2_subdev *sd, return -EINVAL; return check_which(code->which) ? : check_pad(sd, code->pad) ? : - check_state_pads(code->which, state) ? : + check_state(sd, state, code->which, code->pad, code->stream) ? : sd->ops->pad->enum_mbus_code(sd, state, code); } @@ -223,7 +237,7 @@ static int call_enum_frame_size(struct v4l2_subdev *sd, return -EINVAL; return check_which(fse->which) ? : check_pad(sd, fse->pad) ? : - check_state_pads(fse->which, state) ? : + check_state(sd, state, fse->which, fse->pad, fse->stream) ? : sd->ops->pad->enum_frame_size(sd, state, fse); } @@ -258,7 +272,7 @@ static int call_enum_frame_interval(struct v4l2_subdev *sd, return -EINVAL; return check_which(fie->which) ? : check_pad(sd, fie->pad) ? : - check_state_pads(fie->which, state) ? : + check_state(sd, state, fie->which, fie->pad, fie->stream) ? : sd->ops->pad->enum_frame_interval(sd, state, fie); } @@ -270,7 +284,7 @@ static inline int check_selection(struct v4l2_subdev *sd, return -EINVAL; return check_which(sel->which) ? : check_pad(sd, sel->pad) ? : - check_state_pads(sel->which, state); + check_state(sd, state, sel->which, sel->pad, sel->stream); } static int call_get_selection(struct v4l2_subdev *sd, @@ -1122,7 +1136,8 @@ __v4l2_subdev_state_alloc(struct v4l2_subdev *sd, const char *lock_name, else state->lock = &state->_lock; - if (sd->entity.num_pads) { + /* Drivers that support streams do not need the legacy pad config */ + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS) && sd->entity.num_pads) { state->pads = kvcalloc(sd->entity.num_pads, sizeof(*state->pads), GFP_KERNEL); if (!state->pads) { @@ -1162,6 +1177,7 @@ void __v4l2_subdev_state_free(struct v4l2_subdev_state *state) mutex_destroy(&state->_lock); kfree(state->routing.routes); + kvfree(state->stream_configs.configs); kvfree(state->pads); kfree(state); } @@ -1191,6 +1207,55 @@ EXPORT_SYMBOL_GPL(v4l2_subdev_cleanup); #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) +static int +v4l2_subdev_init_stream_configs(struct v4l2_subdev_stream_configs *stream_configs, + const struct v4l2_subdev_krouting *routing) +{ + struct v4l2_subdev_stream_configs new_configs = { 0 }; + struct v4l2_subdev_route *route; + u32 idx; + + /* Count number of formats needed */ + for_each_active_route(routing, route) { + /* + * Each route needs a format on both ends of the route. + */ + new_configs.num_configs += 2; + } + + if (new_configs.num_configs) { + new_configs.configs = kvcalloc(new_configs.num_configs, + sizeof(*new_configs.configs), + GFP_KERNEL); + + if (!new_configs.configs) + return -ENOMEM; + } + + /* + * Fill in the 'pad' and stream' value for each item in the array from + * the routing table + */ + idx = 0; + + for_each_active_route(routing, route) { + new_configs.configs[idx].pad = route->sink_pad; + new_configs.configs[idx].stream = route->sink_stream; + + idx++; + + new_configs.configs[idx].pad = route->source_pad; + new_configs.configs[idx].stream = route->source_stream; + + idx++; + } + + kvfree(stream_configs->configs); + *stream_configs = new_configs; + + return 0; +} + int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_format *format) { @@ -1217,6 +1282,7 @@ int v4l2_subdev_set_routing(struct v4l2_subdev *sd, const struct v4l2_subdev_krouting *src = routing; struct v4l2_subdev_krouting new_routing = { 0 }; size_t bytes; + int r; if (unlikely(check_mul_overflow((size_t)src->num_routes, sizeof(*src->routes), &bytes))) @@ -1232,6 +1298,13 @@ int v4l2_subdev_set_routing(struct v4l2_subdev *sd, new_routing.num_routes = src->num_routes; + r = v4l2_subdev_init_stream_configs(&state->stream_configs, + &new_routing); + if (r) { + kfree(new_routing.routes); + return r; + } + kfree(dst->routes); *dst = new_routing; @@ -1259,6 +1332,69 @@ __v4l2_subdev_next_active_route(const struct v4l2_subdev_krouting *routing, } EXPORT_SYMBOL_GPL(__v4l2_subdev_next_active_route); +struct v4l2_mbus_framefmt * +v4l2_subdev_state_get_stream_format(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].fmt; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_stream_format); + +struct v4l2_rect * +v4l2_subdev_state_get_stream_crop(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].crop; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_stream_crop); + +struct v4l2_rect * +v4l2_subdev_state_get_stream_compose(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].compose; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_stream_compose); + #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ #endif /* CONFIG_MEDIA_CONTROLLER */ diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 9e34c70b8d73..643cdcd69ac6 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -700,6 +700,37 @@ struct v4l2_subdev_pad_config { struct v4l2_rect try_compose; }; +/** + * struct v4l2_subdev_stream_config - Used for storing stream configuration. + * + * @pad: pad number + * @stream: stream number + * @fmt: &struct v4l2_mbus_framefmt + * @crop: &struct v4l2_rect to be used for crop + * @compose: &struct v4l2_rect to be used for compose + * + * This structure stores configuration for a stream. + */ +struct v4l2_subdev_stream_config { + u32 pad; + u32 stream; + + struct v4l2_mbus_framefmt fmt; + struct v4l2_rect crop; + struct v4l2_rect compose; +}; + +/** + * struct v4l2_subdev_stream_configs - A collection of stream configs. + * + * @num_configs: number of entries in @config. + * @configs: an array of &struct v4l2_subdev_stream_configs. + */ +struct v4l2_subdev_stream_configs { + u32 num_configs; + struct v4l2_subdev_stream_config *configs; +}; + /** * struct v4l2_subdev_krouting - subdev routing table * @@ -720,6 +751,7 @@ struct v4l2_subdev_krouting { * @lock: mutex for the state. May be replaced by the user. * @pads: &struct v4l2_subdev_pad_config array * @routing: routing table for the subdev + * @stream_configs: stream configurations (only for V4L2_SUBDEV_FL_STREAMS) * * This structure only needs to be passed to the pad op if the 'which' field * of the main argument is set to %V4L2_SUBDEV_FORMAT_TRY. For @@ -731,6 +763,7 @@ struct v4l2_subdev_state { struct mutex *lock; struct v4l2_subdev_pad_config *pads; struct v4l2_subdev_krouting routing; + struct v4l2_subdev_stream_configs stream_configs; }; /** @@ -1453,6 +1486,52 @@ __v4l2_subdev_next_active_route(const struct v4l2_subdev_krouting *routing, for ((route) = NULL; \ ((route) = __v4l2_subdev_next_active_route((routing), (route)));) +/** + * v4l2_subdev_state_get_stream_format() - Get pointer to a stream format + * @state: subdevice state + * @pad: pad id + * @stream: stream id + * + * This returns a pointer to &struct v4l2_mbus_framefmt for the given pad + + * stream in the subdev state. + * + * If the state does not contain the given pad + stream, NULL is returned. + */ +struct v4l2_mbus_framefmt * +v4l2_subdev_state_get_stream_format(struct v4l2_subdev_state *state, + unsigned int pad, u32 stream); + +/** + * v4l2_subdev_state_get_stream_crop() - Get pointer to a stream crop rectangle + * @state: subdevice state + * @pad: pad id + * @stream: stream id + * + * This returns a pointer to crop rectangle for the given pad + stream in the + * subdev state. + * + * If the state does not contain the given pad + stream, NULL is returned. + */ +struct v4l2_rect * +v4l2_subdev_state_get_stream_crop(struct v4l2_subdev_state *state, + unsigned int pad, u32 stream); + +/** + * v4l2_subdev_state_get_stream_compose() - Get pointer to a stream compose + * rectangle + * @state: subdevice state + * @pad: pad id + * @stream: stream id + * + * This returns a pointer to compose rectangle for the given pad + stream in the + * subdev state. + * + * If the state does not contain the given pad + stream, NULL is returned. + */ +struct v4l2_rect * +v4l2_subdev_state_get_stream_compose(struct v4l2_subdev_state *state, + unsigned int pad, u32 stream); + #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ #endif /* CONFIG_MEDIA_CONTROLLER */ diff --git a/include/uapi/linux/v4l2-subdev.h b/include/uapi/linux/v4l2-subdev.h index 284ae8c95960..654d659de835 100644 --- a/include/uapi/linux/v4l2-subdev.h +++ b/include/uapi/linux/v4l2-subdev.h @@ -32,13 +32,15 @@ enum v4l2_subdev_format_whence { * @which: format type (from enum v4l2_subdev_format_whence) * @pad: pad number, as reported by the media API * @format: media bus format (format code and frame size) + * @stream: stream number, defined in subdev routing * @reserved: drivers and applications must zero this array */ struct v4l2_subdev_format { __u32 which; __u32 pad; struct v4l2_mbus_framefmt format; - __u32 reserved[8]; + __u32 stream; + __u32 reserved[7]; }; /** @@ -46,13 +48,15 @@ struct v4l2_subdev_format { * @which: format type (from enum v4l2_subdev_format_whence) * @pad: pad number, as reported by the media API * @rect: pad crop rectangle boundaries + * @stream: stream number, defined in subdev routing * @reserved: drivers and applications must zero this array */ struct v4l2_subdev_crop { __u32 which; __u32 pad; struct v4l2_rect rect; - __u32 reserved[8]; + __u32 stream; + __u32 reserved[7]; }; #define V4L2_SUBDEV_MBUS_CODE_CSC_COLORSPACE 0x00000001 @@ -68,6 +72,7 @@ struct v4l2_subdev_crop { * @code: format code (MEDIA_BUS_FMT_ definitions) * @which: format type (from enum v4l2_subdev_format_whence) * @flags: flags set by the driver, (V4L2_SUBDEV_MBUS_CODE_*) + * @stream: stream number, defined in subdev routing * @reserved: drivers and applications must zero this array */ struct v4l2_subdev_mbus_code_enum { @@ -76,7 +81,8 @@ struct v4l2_subdev_mbus_code_enum { __u32 code; __u32 which; __u32 flags; - __u32 reserved[7]; + __u32 stream; + __u32 reserved[6]; }; /** @@ -89,6 +95,7 @@ struct v4l2_subdev_mbus_code_enum { * @min_height: minimum frame height, in pixels * @max_height: maximum frame height, in pixels * @which: format type (from enum v4l2_subdev_format_whence) + * @stream: stream number, defined in subdev routing * @reserved: drivers and applications must zero this array */ struct v4l2_subdev_frame_size_enum { @@ -100,19 +107,22 @@ struct v4l2_subdev_frame_size_enum { __u32 min_height; __u32 max_height; __u32 which; - __u32 reserved[8]; + __u32 stream; + __u32 reserved[7]; }; /** * struct v4l2_subdev_frame_interval - Pad-level frame rate * @pad: pad number, as reported by the media API * @interval: frame interval in seconds + * @stream: stream number, defined in subdev routing * @reserved: drivers and applications must zero this array */ struct v4l2_subdev_frame_interval { __u32 pad; struct v4l2_fract interval; - __u32 reserved[9]; + __u32 stream; + __u32 reserved[8]; }; /** @@ -124,6 +134,7 @@ struct v4l2_subdev_frame_interval { * @height: frame height in pixels * @interval: frame interval in seconds * @which: format type (from enum v4l2_subdev_format_whence) + * @stream: stream number, defined in subdev routing * @reserved: drivers and applications must zero this array */ struct v4l2_subdev_frame_interval_enum { @@ -134,7 +145,8 @@ struct v4l2_subdev_frame_interval_enum { __u32 height; struct v4l2_fract interval; __u32 which; - __u32 reserved[8]; + __u32 stream; + __u32 reserved[7]; }; /** @@ -146,6 +158,7 @@ struct v4l2_subdev_frame_interval_enum { * defined in v4l2-common.h; V4L2_SEL_TGT_* . * @flags: constraint flags, defined in v4l2-common.h; V4L2_SEL_FLAG_*. * @r: coordinates of the selection window + * @stream: stream number, defined in subdev routing * @reserved: for future use, set to zero for now * * Hardware may use multiple helper windows to process a video stream. @@ -158,7 +171,8 @@ struct v4l2_subdev_selection { __u32 target; __u32 flags; struct v4l2_rect r; - __u32 reserved[8]; + __u32 stream; + __u32 reserved[7]; }; /** -- cgit v1.2.3 From a6b995ed03ffeb36f06c4a46d8804c24d776de6f Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 15 Jul 2021 15:40:37 +0200 Subject: media: subdev: use streams in v4l2_subdev_link_validate() Update v4l2_subdev_link_validate() to use routing and streams for validation. Instead of just looking at the format on the pad on both ends of the link, the routing tables are used to collect all the streams going from the source to the sink over the link, and the streams' formats on both ends of the link are verified. Signed-off-by: Tomi Valkeinen Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-subdev.c | 185 ++++++++++++++++++++++++++++++---- 1 file changed, 165 insertions(+), 20 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index a3e412c73715..0429da031baf 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1041,7 +1041,7 @@ int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd, EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate_default); static int -v4l2_subdev_link_validate_get_format(struct media_pad *pad, +v4l2_subdev_link_validate_get_format(struct media_pad *pad, u32 stream, struct v4l2_subdev_format *fmt) { if (is_media_entity_v4l2_subdev(pad->entity)) { @@ -1050,7 +1050,11 @@ v4l2_subdev_link_validate_get_format(struct media_pad *pad, fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; fmt->pad = pad->index; - return v4l2_subdev_call_state_active(sd, pad, get_fmt, fmt); + fmt->stream = stream; + + return v4l2_subdev_call(sd, pad, get_fmt, + v4l2_subdev_get_locked_active_state(sd), + fmt); } WARN(pad->entity->function != MEDIA_ENT_F_IO_V4L, @@ -1060,31 +1064,172 @@ v4l2_subdev_link_validate_get_format(struct media_pad *pad, return -EINVAL; } +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) + +static void __v4l2_link_validate_get_streams(struct media_pad *pad, + u64 *streams_mask) +{ + struct v4l2_subdev_route *route; + struct v4l2_subdev_state *state; + struct v4l2_subdev *subdev; + + subdev = media_entity_to_v4l2_subdev(pad->entity); + + *streams_mask = 0; + + state = v4l2_subdev_get_locked_active_state(subdev); + if (WARN_ON(!state)) + return; + + for_each_active_route(&state->routing, route) { + u32 route_pad; + u32 route_stream; + + if (pad->flags & MEDIA_PAD_FL_SOURCE) { + route_pad = route->source_pad; + route_stream = route->source_stream; + } else { + route_pad = route->sink_pad; + route_stream = route->sink_stream; + } + + if (route_pad != pad->index) + continue; + + *streams_mask |= BIT_ULL(route_stream); + } +} + +#endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ + +static void v4l2_link_validate_get_streams(struct media_pad *pad, + u64 *streams_mask) +{ + struct v4l2_subdev *subdev = media_entity_to_v4l2_subdev(pad->entity); + + if (!(subdev->flags & V4L2_SUBDEV_FL_STREAMS)) { + /* Non-streams subdevs have an implicit stream 0 */ + *streams_mask = BIT_ULL(0); + return; + } + +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) + __v4l2_link_validate_get_streams(pad, streams_mask); +#else + /* This shouldn't happen */ + *streams_mask = 0; +#endif +} + +static int v4l2_subdev_link_validate_locked(struct media_link *link) +{ + struct v4l2_subdev *sink_subdev = + media_entity_to_v4l2_subdev(link->sink->entity); + struct device *dev = sink_subdev->entity.graph_obj.mdev->dev; + u64 source_streams_mask; + u64 sink_streams_mask; + u64 dangling_sink_streams; + u32 stream; + int ret; + + dev_dbg(dev, "validating link \"%s\":%u -> \"%s\":%u\n", + link->source->entity->name, link->source->index, + link->sink->entity->name, link->sink->index); + + v4l2_link_validate_get_streams(link->source, &source_streams_mask); + v4l2_link_validate_get_streams(link->sink, &sink_streams_mask); + + /* + * It is ok to have more source streams than sink streams as extra + * source streams can just be ignored by the receiver, but having extra + * sink streams is an error as streams must have a source. + */ + dangling_sink_streams = (source_streams_mask ^ sink_streams_mask) & + sink_streams_mask; + if (dangling_sink_streams) { + dev_err(dev, "Dangling sink streams: mask %#llx\n", + dangling_sink_streams); + return -EINVAL; + } + + /* Validate source and sink stream formats */ + + for (stream = 0; stream < sizeof(sink_streams_mask) * 8; ++stream) { + struct v4l2_subdev_format sink_fmt, source_fmt; + + if (!(sink_streams_mask & BIT_ULL(stream))) + continue; + + dev_dbg(dev, "validating stream \"%s\":%u:%u -> \"%s\":%u:%u\n", + link->source->entity->name, link->source->index, stream, + link->sink->entity->name, link->sink->index, stream); + + ret = v4l2_subdev_link_validate_get_format(link->source, stream, + &source_fmt); + if (ret < 0) { + dev_dbg(dev, + "Failed to get format for \"%s\":%u:%u (but that's ok)\n", + link->source->entity->name, link->source->index, + stream); + continue; + } + + ret = v4l2_subdev_link_validate_get_format(link->sink, stream, + &sink_fmt); + if (ret < 0) { + dev_dbg(dev, + "Failed to get format for \"%s\":%u:%u (but that's ok)\n", + link->sink->entity->name, link->sink->index, + stream); + continue; + } + + /* TODO: add stream number to link_validate() */ + ret = v4l2_subdev_call(sink_subdev, pad, link_validate, link, + &source_fmt, &sink_fmt); + if (!ret) + continue; + + if (ret != -ENOIOCTLCMD) + return ret; + + ret = v4l2_subdev_link_validate_default(sink_subdev, link, + &source_fmt, &sink_fmt); + + if (ret) + return ret; + } + + return 0; +} + int v4l2_subdev_link_validate(struct media_link *link) { - struct v4l2_subdev *sink; - struct v4l2_subdev_format sink_fmt, source_fmt; - int rval; + struct v4l2_subdev *source_sd, *sink_sd; + struct v4l2_subdev_state *source_state, *sink_state; + int ret; - rval = v4l2_subdev_link_validate_get_format( - link->source, &source_fmt); - if (rval < 0) - return 0; + sink_sd = media_entity_to_v4l2_subdev(link->sink->entity); + source_sd = media_entity_to_v4l2_subdev(link->source->entity); - rval = v4l2_subdev_link_validate_get_format( - link->sink, &sink_fmt); - if (rval < 0) - return 0; + sink_state = v4l2_subdev_get_unlocked_active_state(sink_sd); + source_state = v4l2_subdev_get_unlocked_active_state(source_sd); - sink = media_entity_to_v4l2_subdev(link->sink->entity); + if (sink_state) + v4l2_subdev_lock_state(sink_state); - rval = v4l2_subdev_call(sink, pad, link_validate, link, - &source_fmt, &sink_fmt); - if (rval != -ENOIOCTLCMD) - return rval; + if (source_state) + v4l2_subdev_lock_state(source_state); - return v4l2_subdev_link_validate_default( - sink, link, &source_fmt, &sink_fmt); + ret = v4l2_subdev_link_validate_locked(link); + + if (sink_state) + v4l2_subdev_unlock_state(sink_state); + + if (source_state) + v4l2_subdev_unlock_state(source_state); + + return ret; } EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate); -- cgit v1.2.3 From d00f1a075ce13c7d71ca00083be110877874a403 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 21 Dec 2021 11:22:06 +0100 Subject: media: subdev: add "opposite" stream helper funcs Add two helper functions to make dealing with streams easier: v4l2_subdev_routing_find_opposite_end - given a routing table and a pad + stream, return the pad + stream on the opposite side of the subdev. v4l2_subdev_state_get_opposite_stream_format - return a pointer to the format on the pad + stream on the opposite side from the given pad + stream. Signed-off-by: Tomi Valkeinen Reviewed-by: Hans Verkuil Reviewed-by: Jacopo Mondi Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-subdev.c | 49 +++++++++++++++++++++++++++++++++++ include/media/v4l2-subdev.h | 36 +++++++++++++++++++++++++ 2 files changed, 85 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 0429da031baf..7bb7c0db0bb2 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1540,6 +1540,55 @@ v4l2_subdev_state_get_stream_compose(struct v4l2_subdev_state *state, } EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_stream_compose); +int v4l2_subdev_routing_find_opposite_end(const struct v4l2_subdev_krouting *routing, + u32 pad, u32 stream, u32 *other_pad, + u32 *other_stream) +{ + unsigned int i; + + for (i = 0; i < routing->num_routes; ++i) { + struct v4l2_subdev_route *route = &routing->routes[i]; + + if (route->source_pad == pad && + route->source_stream == stream) { + if (other_pad) + *other_pad = route->sink_pad; + if (other_stream) + *other_stream = route->sink_stream; + return 0; + } + + if (route->sink_pad == pad && route->sink_stream == stream) { + if (other_pad) + *other_pad = route->source_pad; + if (other_stream) + *other_stream = route->source_stream; + return 0; + } + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_routing_find_opposite_end); + +struct v4l2_mbus_framefmt * +v4l2_subdev_state_get_opposite_stream_format(struct v4l2_subdev_state *state, + u32 pad, u32 stream) +{ + u32 other_pad, other_stream; + int ret; + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, + pad, stream, + &other_pad, &other_stream); + if (ret) + return NULL; + + return v4l2_subdev_state_get_stream_format(state, other_pad, + other_stream); +} +EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_opposite_stream_format); + #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ #endif /* CONFIG_MEDIA_CONTROLLER */ diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 643cdcd69ac6..cf8cbbf3b772 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1532,6 +1532,42 @@ struct v4l2_rect * v4l2_subdev_state_get_stream_compose(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 + * @pad: pad id + * @stream: stream id + * @other_pad: pointer used to return the opposite pad + * @other_stream: pointer used to return the opposite stream + * + * This function uses the routing table to find the pad + stream which is + * opposite the given pad + stream. + * + * @other_pad and/or @other_stream can be NULL if the caller does not need the + * value. + * + * Returns 0 on success, or -EINVAL if no matching route is found. + */ +int v4l2_subdev_routing_find_opposite_end(const struct v4l2_subdev_krouting *routing, + u32 pad, u32 stream, u32 *other_pad, + u32 *other_stream); + +/** + * v4l2_subdev_state_get_opposite_stream_format() - Get pointer to opposite + * stream format + * @state: subdevice state + * @pad: pad id + * @stream: stream id + * + * This returns a pointer to &struct v4l2_mbus_framefmt for the pad + stream + * that is opposite the given pad + stream in the subdev state. + * + * If the state does not contain the given pad + stream, NULL is returned. + */ +struct v4l2_mbus_framefmt * +v4l2_subdev_state_get_opposite_stream_format(struct v4l2_subdev_state *state, + u32 pad, u32 stream); + #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ #endif /* CONFIG_MEDIA_CONTROLLER */ -- cgit v1.2.3 From 72c5fbcaa33d88b3e642da7c07741bf5364ce12c Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 21 Dec 2021 11:23:13 +0100 Subject: media: subdev: add streams to v4l2_subdev_get_fmt() helper function Add streams support to v4l2_subdev_get_fmt() helper function. Subdev drivers that do not need to do anything special in their get_fmt op can use this helper directly for v4l2_subdev_pad_ops.get_fmt. Signed-off-by: Tomi Valkeinen Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-subdev.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 7bb7c0db0bb2..be9bd0568c44 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1406,10 +1406,14 @@ int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, { struct v4l2_mbus_framefmt *fmt; - if (format->pad >= sd->entity.num_pads) - return -EINVAL; + if (sd->flags & V4L2_SUBDEV_FL_STREAMS) + fmt = v4l2_subdev_state_get_stream_format(state, format->pad, + format->stream); + else if (format->pad < sd->entity.num_pads && format->stream == 0) + fmt = v4l2_subdev_get_pad_format(sd, state, format->pad); + else + fmt = NULL; - fmt = v4l2_subdev_get_pad_format(sd, state, format->pad); if (!fmt) return -EINVAL; -- cgit v1.2.3 From 5b0d85b379747b09d8b4630fdb5c8a8d74122f0f Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 25 Aug 2021 12:24:09 +0200 Subject: media: subdev: add v4l2_subdev_set_routing_with_fmt() helper v4l2_subdev_set_routing_with_fmt() is the same as v4l2_subdev_set_routing(), but additionally initializes all the streams with the given format. Signed-off-by: Tomi Valkeinen Reviewed-by: Hans Verkuil Reviewed-by: Jacopo Mondi Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-subdev.c | 22 ++++++++++++++++++++++ include/media/v4l2-subdev.h | 16 ++++++++++++++++ 2 files changed, 38 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index be9bd0568c44..ebd6ef9fa6c3 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1481,6 +1481,28 @@ __v4l2_subdev_next_active_route(const struct v4l2_subdev_krouting *routing, } EXPORT_SYMBOL_GPL(__v4l2_subdev_next_active_route); +int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing, + const struct v4l2_mbus_framefmt *fmt) +{ + struct v4l2_subdev_stream_configs *stream_configs; + unsigned int i; + int ret; + + ret = v4l2_subdev_set_routing(sd, state, routing); + if (ret) + return ret; + + stream_configs = &state->stream_configs; + + for (i = 0; i < stream_configs->num_configs; ++i) + stream_configs->configs[i].fmt = *fmt; + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_set_routing_with_fmt); + struct v4l2_mbus_framefmt * v4l2_subdev_state_get_stream_format(struct v4l2_subdev_state *state, unsigned int pad, u32 stream) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index cf8cbbf3b772..0055211a579a 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1486,6 +1486,22 @@ __v4l2_subdev_next_active_route(const struct v4l2_subdev_krouting *routing, for ((route) = NULL; \ ((route) = __v4l2_subdev_next_active_route((routing), (route)));) +/** + * v4l2_subdev_set_routing_with_fmt() - Set given routing and format to subdev + * state + * @sd: The subdevice + * @state: The subdevice state + * @routing: Routing that will be copied to subdev state + * @fmt: Format used to initialize all the streams + * + * This is the same as v4l2_subdev_set_routing, but additionally initializes + * all the streams using the given format. + */ +int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing, + const struct v4l2_mbus_framefmt *fmt); + /** * v4l2_subdev_state_get_stream_format() - Get pointer to a stream format * @state: subdevice state -- cgit v1.2.3 From 69c0fe7ae78b9ed696b5a5d8eaae7ca622c6f7ca Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 21 Dec 2021 11:23:58 +0100 Subject: media: subdev: add v4l2_subdev_routing_validate() helper Add a v4l2_subdev_routing_validate() helper for verifying routing for common cases like only allowing non-overlapping 1-to-1 streams. Signed-off-by: Laurent Pinchart Signed-off-by: Tomi Valkeinen Reviewed-by: Jacopo Mondi Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-subdev.c | 102 ++++++++++++++++++++++++++++++++++ include/media/v4l2-subdev.h | 39 +++++++++++++ 2 files changed, 141 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index ebd6ef9fa6c3..d7e157c7e612 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1615,6 +1615,108 @@ v4l2_subdev_state_get_opposite_stream_format(struct v4l2_subdev_state *state, } EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_opposite_stream_format); +int v4l2_subdev_routing_validate(struct v4l2_subdev *sd, + const struct v4l2_subdev_krouting *routing, + enum v4l2_subdev_routing_restriction disallow) +{ + u32 *remote_pads = NULL; + unsigned int i, j; + int ret = -EINVAL; + + if (disallow & V4L2_SUBDEV_ROUTING_NO_STREAM_MIX) { + remote_pads = kcalloc(sd->entity.num_pads, sizeof(*remote_pads), + GFP_KERNEL); + if (!remote_pads) + return -ENOMEM; + + for (i = 0; i < sd->entity.num_pads; ++i) + remote_pads[i] = U32_MAX; + } + + for (i = 0; i < routing->num_routes; ++i) { + const struct v4l2_subdev_route *route = &routing->routes[i]; + + /* Validate the sink and source pad numbers. */ + if (route->sink_pad >= sd->entity.num_pads || + !(sd->entity.pads[route->sink_pad].flags & MEDIA_PAD_FL_SINK)) { + dev_dbg(sd->dev, "route %u sink (%u) is not a sink pad\n", + i, route->sink_pad); + goto out; + } + + if (route->source_pad >= sd->entity.num_pads || + !(sd->entity.pads[route->source_pad].flags & MEDIA_PAD_FL_SOURCE)) { + dev_dbg(sd->dev, "route %u source (%u) is not a source pad\n", + i, route->source_pad); + goto out; + } + + /* + * V4L2_SUBDEV_ROUTING_NO_STREAM_MIX: Streams on the same pad + * may not be routed to streams on different pads. + */ + if (disallow & V4L2_SUBDEV_ROUTING_NO_STREAM_MIX) { + if (remote_pads[route->sink_pad] != U32_MAX && + remote_pads[route->sink_pad] != route->source_pad) { + dev_dbg(sd->dev, + "route %u attempts to mix %s streams\n", + i, "sink"); + goto out; + } + + if (remote_pads[route->source_pad] != U32_MAX && + remote_pads[route->source_pad] != route->sink_pad) { + dev_dbg(sd->dev, + "route %u attempts to mix %s streams\n", + i, "source"); + goto out; + } + + remote_pads[route->sink_pad] = route->source_pad; + remote_pads[route->source_pad] = route->sink_pad; + } + + for (j = i + 1; j < routing->num_routes; ++j) { + const struct v4l2_subdev_route *r = &routing->routes[j]; + + /* + * V4L2_SUBDEV_ROUTING_NO_1_TO_N: No two routes can + * originate from the same (sink) stream. + */ + if ((disallow & V4L2_SUBDEV_ROUTING_NO_1_TO_N) && + route->sink_pad == r->sink_pad && + route->sink_stream == r->sink_stream) { + dev_dbg(sd->dev, + "routes %u and %u originate from same sink (%u/%u)\n", + i, j, route->sink_pad, + route->sink_stream); + goto out; + } + + /* + * V4L2_SUBDEV_ROUTING_NO_N_TO_1: No two routes can end + * at the same (source) stream. + */ + if ((disallow & V4L2_SUBDEV_ROUTING_NO_N_TO_1) && + route->source_pad == r->source_pad && + route->source_stream == r->source_stream) { + dev_dbg(sd->dev, + "routes %u and %u end at same source (%u/%u)\n", + i, j, route->source_pad, + route->source_stream); + goto out; + } + } + } + + ret = 0; + +out: + kfree(remote_pads); + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_routing_validate); + #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ #endif /* CONFIG_MEDIA_CONTROLLER */ diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 0055211a579a..06c254f16b61 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1584,6 +1584,45 @@ struct v4l2_mbus_framefmt * v4l2_subdev_state_get_opposite_stream_format(struct v4l2_subdev_state *state, u32 pad, u32 stream); +/** + * enum v4l2_subdev_routing_restriction - Subdevice internal routing restrictions + * + * @V4L2_SUBDEV_ROUTING_NO_1_TO_N: + * an input stream may not be routed to multiple output streams (stream + * duplication) + * @V4L2_SUBDEV_ROUTING_NO_N_TO_1: + * multiple input streams may not be routed to the same output stream + * (stream merging) + * @V4L2_SUBDEV_ROUTING_NO_STREAM_MIX: + * streams on the same pad may not be routed to streams on different pads + * @V4L2_SUBDEV_ROUTING_ONLY_1_TO_1: + * only non-overlapping 1-to-1 stream routing is allowed (a combination of + * @V4L2_SUBDEV_ROUTING_NO_1_TO_N and @V4L2_SUBDEV_ROUTING_NO_N_TO_1) + */ +enum v4l2_subdev_routing_restriction { + V4L2_SUBDEV_ROUTING_NO_1_TO_N = BIT(0), + V4L2_SUBDEV_ROUTING_NO_N_TO_1 = BIT(1), + V4L2_SUBDEV_ROUTING_NO_STREAM_MIX = BIT(2), + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 = + V4L2_SUBDEV_ROUTING_NO_1_TO_N | + V4L2_SUBDEV_ROUTING_NO_N_TO_1, +}; + +/** + * v4l2_subdev_routing_validate() - Verify that routes comply with driver + * constraints + * @sd: The subdevice + * @routing: Routing to verify + * @disallow: Restrictions on routes + * + * This verifies that the given routing complies with the @disallow constraints. + * + * Returns 0 on success, error value otherwise. + */ +int v4l2_subdev_routing_validate(struct v4l2_subdev *sd, + const struct v4l2_subdev_krouting *routing, + enum v4l2_subdev_routing_restriction disallow); + #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ #endif /* CONFIG_MEDIA_CONTROLLER */ -- cgit v1.2.3 From c4a73f316d04594689dffcae8d800787e4fc9f75 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 18 Jan 2022 00:51:23 +0100 Subject: media: v4l2-subdev: Add v4l2_subdev_state_xlate_streams() helper Add a helper function to translate streams between two pads of a subdev, using the subdev's internal routing table. Signed-off-by: Laurent Pinchart Signed-off-by: Tomi Valkeinen Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-subdev.c | 26 ++++++++++++++++++++++++++ include/media/v4l2-subdev.h | 23 +++++++++++++++++++++++ 2 files changed, 49 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index d7e157c7e612..13f1a100496e 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1615,6 +1615,32 @@ v4l2_subdev_state_get_opposite_stream_format(struct v4l2_subdev_state *state, } EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_opposite_stream_format); +u64 v4l2_subdev_state_xlate_streams(const struct v4l2_subdev_state *state, + u32 pad0, u32 pad1, u64 *streams) +{ + const struct v4l2_subdev_krouting *routing = &state->routing; + struct v4l2_subdev_route *route; + u64 streams0 = 0; + u64 streams1 = 0; + + for_each_active_route(routing, route) { + if (route->sink_pad == pad0 && route->source_pad == pad1 && + (*streams & BIT_ULL(route->sink_stream))) { + streams0 |= BIT_ULL(route->sink_stream); + streams1 |= BIT_ULL(route->source_stream); + } + if (route->source_pad == pad0 && route->sink_pad == pad1 && + (*streams & BIT_ULL(route->source_stream))) { + streams0 |= BIT_ULL(route->source_stream); + streams1 |= BIT_ULL(route->sink_stream); + } + } + + *streams = streams0; + return streams1; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_state_xlate_streams); + int v4l2_subdev_routing_validate(struct v4l2_subdev *sd, const struct v4l2_subdev_krouting *routing, enum v4l2_subdev_routing_restriction disallow) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 06c254f16b61..8ef0d26a7a06 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1584,6 +1584,29 @@ struct v4l2_mbus_framefmt * v4l2_subdev_state_get_opposite_stream_format(struct v4l2_subdev_state *state, u32 pad, u32 stream); +/** + * v4l2_subdev_state_xlate_streams() - Translate streams from one pad to another + * + * @state: Subdevice state + * @pad0: The first pad + * @pad1: The second pad + * @streams: Streams bitmask on the first pad + * + * Streams on sink pads of a subdev are routed to source pads as expressed in + * the subdev state routing table. Stream numbers don't necessarily match on + * the sink and source side of a route. This function translates stream numbers + * on @pad0, expressed as a bitmask in @streams, to the corresponding streams + * on @pad1 using the routing table from the @state. It returns the stream mask + * on @pad1, and updates @streams with the streams that have been found in the + * routing table. + * + * @pad0 and @pad1 must be a sink and a source, in any order. + * + * Return: The bitmask of streams of @pad1 that are routed to @streams on @pad0. + */ +u64 v4l2_subdev_state_xlate_streams(const struct v4l2_subdev_state *state, + u32 pad0, u32 pad1, u64 *streams); + /** * enum v4l2_subdev_routing_restriction - Subdevice internal routing restrictions * -- cgit v1.2.3 From d0749adb30706f4e3af452698db96f671444341a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 13 Dec 2021 15:11:56 +0100 Subject: media: v4l2-subdev: Add subdev .(enable|disable)_streams() operations Add two new subdev pad operations, .enable_streams() and .disable_streams(), to allow control of individual streams per pad. This is a superset of what the video .s_stream() operation implements. To help with handling of backward compatibility, add two wrapper functions around those operations, and require their usage in drivers. Signed-off-by: Tomi Valkeinen Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-subdev.c | 224 ++++++++++++++++++++++++++++++++++ include/media/v4l2-subdev.h | 85 +++++++++++++ 2 files changed, 309 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 13f1a100496e..c94fa2c2cbda 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1743,6 +1743,230 @@ out: } EXPORT_SYMBOL_GPL(v4l2_subdev_routing_validate); +static int v4l2_subdev_enable_streams_fallback(struct v4l2_subdev *sd, u32 pad, + u64 streams_mask) +{ + struct device *dev = sd->entity.graph_obj.mdev->dev; + unsigned int i; + int ret; + + /* + * The subdev doesn't implement pad-based stream enable, fall back + * on the .s_stream() operation. This can only be done for subdevs that + * have a single source pad, as sd->enabled_streams is global to the + * subdev. + */ + if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) + return -EOPNOTSUPP; + + for (i = 0; i < sd->entity.num_pads; ++i) { + if (i != pad && sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) + return -EOPNOTSUPP; + } + + if (sd->enabled_streams & streams_mask) { + dev_dbg(dev, "set of streams %#llx already enabled on %s:%u\n", + streams_mask, sd->entity.name, pad); + return -EALREADY; + } + + /* Start streaming when the first streams are enabled. */ + if (!sd->enabled_streams) { + ret = v4l2_subdev_call(sd, video, s_stream, 1); + if (ret) + return ret; + } + + sd->enabled_streams |= streams_mask; + + return 0; +} + +int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, + u64 streams_mask) +{ + struct device *dev = sd->entity.graph_obj.mdev->dev; + struct v4l2_subdev_state *state; + u64 found_streams = 0; + unsigned int i; + int ret; + + /* A few basic sanity checks first. */ + if (pad >= sd->entity.num_pads) + return -EINVAL; + + if (!streams_mask) + return 0; + + /* Fallback on .s_stream() if .enable_streams() isn't available. */ + if (!sd->ops->pad || !sd->ops->pad->enable_streams) + return v4l2_subdev_enable_streams_fallback(sd, pad, + streams_mask); + + state = v4l2_subdev_lock_and_get_active_state(sd); + + /* + * Verify that the requested streams exist and that they are not + * already enabled. + */ + for (i = 0; i < state->stream_configs.num_configs; ++i) { + struct v4l2_subdev_stream_config *cfg = + &state->stream_configs.configs[i]; + + if (cfg->pad != pad || !(streams_mask & BIT_ULL(cfg->stream))) + continue; + + found_streams |= BIT_ULL(cfg->stream); + + if (cfg->enabled) { + dev_dbg(dev, "stream %u already enabled on %s:%u\n", + cfg->stream, sd->entity.name, pad); + ret = -EALREADY; + goto done; + } + } + + if (found_streams != streams_mask) { + dev_dbg(dev, "streams 0x%llx not found on %s:%u\n", + streams_mask & ~found_streams, sd->entity.name, pad); + ret = -EINVAL; + goto done; + } + + /* Call the .enable_streams() operation. */ + ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad, + streams_mask); + if (ret) + goto done; + + /* Mark the streams as enabled. */ + for (i = 0; i < state->stream_configs.num_configs; ++i) { + struct v4l2_subdev_stream_config *cfg = + &state->stream_configs.configs[i]; + + if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream))) + cfg->enabled = true; + } + +done: + v4l2_subdev_unlock_state(state); + + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_enable_streams); + +static int v4l2_subdev_disable_streams_fallback(struct v4l2_subdev *sd, u32 pad, + u64 streams_mask) +{ + struct device *dev = sd->entity.graph_obj.mdev->dev; + unsigned int i; + int ret; + + /* + * If the subdev doesn't implement pad-based stream enable, fall back + * on the .s_stream() operation. This can only be done for subdevs that + * have a single source pad, as sd->enabled_streams is global to the + * subdev. + */ + if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) + return -EOPNOTSUPP; + + for (i = 0; i < sd->entity.num_pads; ++i) { + if (i != pad && sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) + return -EOPNOTSUPP; + } + + if ((sd->enabled_streams & streams_mask) != streams_mask) { + dev_dbg(dev, "set of streams %#llx already disabled on %s:%u\n", + streams_mask, sd->entity.name, pad); + return -EALREADY; + } + + /* Stop streaming when the last streams are disabled. */ + if (!(sd->enabled_streams & ~streams_mask)) { + ret = v4l2_subdev_call(sd, video, s_stream, 0); + if (ret) + return ret; + } + + sd->enabled_streams &= ~streams_mask; + + return 0; +} + +int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, + u64 streams_mask) +{ + struct device *dev = sd->entity.graph_obj.mdev->dev; + struct v4l2_subdev_state *state; + u64 found_streams = 0; + unsigned int i; + int ret; + + /* A few basic sanity checks first. */ + if (pad >= sd->entity.num_pads) + return -EINVAL; + + if (!streams_mask) + return 0; + + /* Fallback on .s_stream() if .disable_streams() isn't available. */ + if (!sd->ops->pad || !sd->ops->pad->disable_streams) + return v4l2_subdev_disable_streams_fallback(sd, pad, + streams_mask); + + state = v4l2_subdev_lock_and_get_active_state(sd); + + /* + * Verify that the requested streams exist and that they are not + * already disabled. + */ + for (i = 0; i < state->stream_configs.num_configs; ++i) { + struct v4l2_subdev_stream_config *cfg = + &state->stream_configs.configs[i]; + + if (cfg->pad != pad || !(streams_mask & BIT_ULL(cfg->stream))) + continue; + + found_streams |= BIT_ULL(cfg->stream); + + if (!cfg->enabled) { + dev_dbg(dev, "stream %u already disabled on %s:%u\n", + cfg->stream, sd->entity.name, pad); + ret = -EALREADY; + goto done; + } + } + + if (found_streams != streams_mask) { + dev_dbg(dev, "streams 0x%llx not found on %s:%u\n", + streams_mask & ~found_streams, sd->entity.name, pad); + ret = -EINVAL; + goto done; + } + + /* Call the .disable_streams() operation. */ + ret = v4l2_subdev_call(sd, pad, disable_streams, state, pad, + streams_mask); + if (ret) + goto done; + + /* Mark the streams as disabled. */ + for (i = 0; i < state->stream_configs.num_configs; ++i) { + struct v4l2_subdev_stream_config *cfg = + &state->stream_configs.configs[i]; + + if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream))) + cfg->enabled = false; + } + +done: + v4l2_subdev_unlock_state(state); + + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_disable_streams); + #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ #endif /* CONFIG_MEDIA_CONTROLLER */ diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 8ef0d26a7a06..2c293dd136bd 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -705,6 +705,7 @@ struct v4l2_subdev_pad_config { * * @pad: pad number * @stream: stream number + * @enabled: has the stream been enabled with v4l2_subdev_enable_stream() * @fmt: &struct v4l2_mbus_framefmt * @crop: &struct v4l2_rect to be used for crop * @compose: &struct v4l2_rect to be used for compose @@ -714,6 +715,7 @@ struct v4l2_subdev_pad_config { struct v4l2_subdev_stream_config { u32 pad; u32 stream; + bool enabled; struct v4l2_mbus_framefmt fmt; struct v4l2_rect crop; @@ -819,6 +821,18 @@ struct v4l2_subdev_state { * * @set_routing: enable or disable data connection routes described in the * subdevice routing table. + * + * @enable_streams: Enable the streams defined in streams_mask on the given + * source pad. Subdevs that implement this operation must use the active + * state management provided by the subdev core (enabled through a call to + * v4l2_subdev_init_finalize() at initialization time). Do not call + * directly, use v4l2_subdev_enable_streams() instead. + * + * @disable_streams: Disable the streams defined in streams_mask on the given + * source pad. Subdevs that implement this operation must use the active + * state management provided by the subdev core (enabled through a call to + * v4l2_subdev_init_finalize() at initialization time). Do not call + * directly, use v4l2_subdev_disable_streams() instead. */ struct v4l2_subdev_pad_ops { int (*init_cfg)(struct v4l2_subdev *sd, @@ -865,6 +879,12 @@ struct v4l2_subdev_pad_ops { struct v4l2_subdev_state *state, enum v4l2_subdev_format_whence which, struct v4l2_subdev_krouting *route); + int (*enable_streams)(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask); + int (*disable_streams)(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask); }; /** @@ -1010,6 +1030,10 @@ struct v4l2_subdev_platform_data { * @active_state: Active state for the subdev (NULL for subdevs tracking the * state internally). Initialized by calling * v4l2_subdev_init_finalize(). + * @enabled_streams: Bitmask of enabled streams used by + * v4l2_subdev_enable_streams() and + * v4l2_subdev_disable_streams() helper functions for fallback + * cases. * * Each instance of a subdev driver should create this struct, either * stand-alone or embedded in a larger struct. @@ -1055,6 +1079,7 @@ struct v4l2_subdev { * doesn't support it. */ struct v4l2_subdev_state *active_state; + u64 enabled_streams; }; @@ -1646,6 +1671,66 @@ int v4l2_subdev_routing_validate(struct v4l2_subdev *sd, const struct v4l2_subdev_krouting *routing, enum v4l2_subdev_routing_restriction disallow); +/** + * v4l2_subdev_enable_streams() - Enable streams on a pad + * @sd: The subdevice + * @pad: The pad + * @streams_mask: Bitmask of streams to enable + * + * This function enables streams on a source @pad of a subdevice. The pad is + * identified by its index, while the streams are identified by the + * @streams_mask bitmask. This allows enabling multiple streams on a pad at + * once. + * + * Enabling a stream that is already enabled isn't allowed. If @streams_mask + * contains an already enabled stream, this function returns -EALREADY without + * performing any operation. + * + * Per-stream enable is only available for subdevs that implement the + * .enable_streams() and .disable_streams() operations. For other subdevs, this + * function implements a best-effort compatibility by calling the .s_stream() + * operation, limited to subdevs that have a single source pad. + * + * Return: + * * 0: Success + * * -EALREADY: One of the streams in streams_mask is already enabled + * * -EINVAL: The pad index is invalid, or doesn't correspond to a source pad + * * -EOPNOTSUPP: Falling back to the legacy .s_stream() operation is + * impossible because the subdev has multiple source pads + */ +int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, + u64 streams_mask); + +/** + * v4l2_subdev_disable_streams() - Disable streams on a pad + * @sd: The subdevice + * @pad: The pad + * @streams_mask: Bitmask of streams to disable + * + * This function disables streams on a source @pad of a subdevice. The pad is + * identified by its index, while the streams are identified by the + * @streams_mask bitmask. This allows disabling multiple streams on a pad at + * once. + * + * Disabling a streams that is not enabled isn't allowed. If @streams_mask + * contains a disabled stream, this function returns -EALREADY without + * performing any operation. + * + * Per-stream disable is only available for subdevs that implement the + * .enable_streams() and .disable_streams() operations. For other subdevs, this + * function implements a best-effort compatibility by calling the .s_stream() + * operation, limited to subdevs that have a single source pad. + * + * Return: + * * 0: Success + * * -EALREADY: One of the streams in streams_mask is not enabled + * * -EINVAL: The pad index is invalid, or doesn't correspond to a source pad + * * -EOPNOTSUPP: Falling back to the legacy .s_stream() operation is + * impossible because the subdev has multiple source pads + */ +int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, + u64 streams_mask); + #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ #endif /* CONFIG_MEDIA_CONTROLLER */ -- cgit v1.2.3 From 34a315ce0e1c03120dd2438875af1a897c039ea0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 18 Jan 2022 01:16:14 +0100 Subject: media: v4l2-subdev: Add v4l2_subdev_s_stream_helper() function The v4l2_subdev_s_stream_helper() helper can be used by subdevs that implement the stream-aware .enable_streams() and .disable_streams() operations to implement .s_stream(). This is limited to subdevs that have a single source pad. Signed-off-by: Laurent Pinchart Signed-off-by: Tomi Valkeinen Reviewed-by: Jacopo Mondi Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-subdev.c | 40 +++++++++++++++++++++++++++++++++++ include/media/v4l2-subdev.h | 17 +++++++++++++++ 2 files changed, 57 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index c94fa2c2cbda..1bebcda2bd20 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1967,6 +1967,46 @@ done: } EXPORT_SYMBOL_GPL(v4l2_subdev_disable_streams); +int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable) +{ + struct v4l2_subdev_state *state; + struct v4l2_subdev_route *route; + struct media_pad *pad; + u64 source_mask = 0; + int pad_index = -1; + + /* + * Find the source pad. This helper is meant for subdevs that have a + * single source pad, so failures shouldn't happen, but catch them + * loudly nonetheless as they indicate a driver bug. + */ + media_entity_for_each_pad(&sd->entity, pad) { + if (pad->flags & MEDIA_PAD_FL_SOURCE) { + pad_index = pad->index; + break; + } + } + + if (WARN_ON(pad_index == -1)) + return -EINVAL; + + /* + * As there's a single source pad, just collect all the source streams. + */ + state = v4l2_subdev_lock_and_get_active_state(sd); + + for_each_active_route(&state->routing, route) + source_mask |= BIT_ULL(route->source_stream); + + v4l2_subdev_unlock_state(state); + + if (enable) + return v4l2_subdev_enable_streams(sd, pad_index, source_mask); + else + return v4l2_subdev_disable_streams(sd, pad_index, source_mask); +} +EXPORT_SYMBOL_GPL(v4l2_subdev_s_stream_helper); + #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ #endif /* CONFIG_MEDIA_CONTROLLER */ diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 2c293dd136bd..1dd579740faf 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1731,6 +1731,23 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, u64 streams_mask); +/** + * v4l2_subdev_s_stream_helper() - Helper to implement the subdev s_stream + * operation using enable_streams and disable_streams + * @sd: The subdevice + * @enable: Enable or disable streaming + * + * Subdevice drivers that implement the streams-aware + * &v4l2_subdev_pad_ops.enable_streams and &v4l2_subdev_pad_ops.disable_streams + * operations can use this helper to implement the legacy + * &v4l2_subdev_video_ops.s_stream operation. + * + * This helper can only be used by subdevs that have a single source pad. + * + * Return: 0 on success, or a negative error code otherwise. + */ +int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable); + #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ #endif /* CONFIG_MEDIA_CONTROLLER */ -- cgit v1.2.3 From c43784c856483dded7956175b5786e27af6d87f1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 8 Dec 2022 08:51:32 +0100 Subject: media: v4l2-mem2mem: use vb2_is_streaming() Don't touch q->streaming directly, use the vb2_is_streaming() function instead. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index be7fde1ed3ea..0cc30397fbad 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -922,9 +922,9 @@ static __poll_t v4l2_m2m_poll_for_data(struct file *file, * means either in driver already or waiting for driver to claim it * and start processing. */ - if ((!src_q->streaming || src_q->error || + if ((!vb2_is_streaming(src_q) || src_q->error || list_empty(&src_q->queued_list)) && - (!dst_q->streaming || dst_q->error || + (!vb2_is_streaming(dst_q) || dst_q->error || (list_empty(&dst_q->queued_list) && !dst_q->last_buffer_dequeued))) return EPOLLERR; -- cgit v1.2.3 From 41959c4f973b837a12061b84d3a436fc64c73a30 Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Fri, 16 Dec 2022 09:30:33 +0100 Subject: media: v4l2-jpeg: correct the skip count in jpeg_parse_app14_data The curr pointer has advanced 14 bytes in jpeg_parse_app14_data. 1. jpeg_get_word_be(stream), it goes forward 2 bytes. 2. jpeg_skip(stream, 11), it goes forward 11 bytes. 3. jpeg_get_byte(stream), it goes forward 1 bytes. so the remain bytes of this segment should be (lp - 2 - 11 - 1), but not (lp - 2 - 11). if driver skip 1 extra bytes, the following parsing may go wrong. Fixes: b8035f7988a8 ("media: Add parsing for APP14 data segment in jpeg helpers") Signed-off-by: Ming Qian Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-jpeg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-jpeg.c b/drivers/media/v4l2-core/v4l2-jpeg.c index c2513b775f6a..75c2af763d55 100644 --- a/drivers/media/v4l2-core/v4l2-jpeg.c +++ b/drivers/media/v4l2-core/v4l2-jpeg.c @@ -474,7 +474,7 @@ static int jpeg_parse_app14_data(struct jpeg_stream *stream, *tf = ret; /* skip the rest of the segment, this ensures at least it is complete */ - skip = lp - 2 - 11; + skip = lp - 2 - 11 - 1; return jpeg_skip(stream, skip); } -- cgit v1.2.3 From 251c0ea6efd3c3ea0f8a55fdd96c749a98639bd3 Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Fri, 16 Dec 2022 10:08:44 +0100 Subject: media: v4l2-jpeg: ignore the unknown APP14 marker The legal identifier of APP14 is "Adobe\0", but sometimes it may be "This is an unknown APP marker . Compliant decoders must ignore it." In this case, just ignore it. It won't affect the decode result. Fixes: b8035f7988a8 ("media: Add parsing for APP14 data segment in jpeg helpers") Signed-off-by: Ming Qian Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-jpeg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-jpeg.c b/drivers/media/v4l2-core/v4l2-jpeg.c index 75c2af763d55..94435a7b6816 100644 --- a/drivers/media/v4l2-core/v4l2-jpeg.c +++ b/drivers/media/v4l2-core/v4l2-jpeg.c @@ -460,7 +460,7 @@ static int jpeg_parse_app14_data(struct jpeg_stream *stream, /* Check for "Adobe\0" in Ap1..6 */ if (stream->curr + 6 > stream->end || strncmp(stream->curr, "Adobe\0", 6)) - return -EINVAL; + return jpeg_skip(stream, lp - 2); /* get to Ap12 */ ret = jpeg_skip(stream, 11); -- cgit v1.2.3 From be3ae7cf4326e95bb1d5413b63baabc26f4a1324 Mon Sep 17 00:00:00 2001 From: Dong Chuanjian Date: Tue, 27 Dec 2022 03:36:25 +0100 Subject: media: drivers/media/v4l2-core/v4l2-h264 : add detection of null pointers When the pointer variable is judged to be null, null is returned directly. [hverkuil: fix two checkpatch warnings] Signed-off-by: Dong Chuanjian Acked-by: Nicolas Dufresne Fixes: d3f756ad629b ("media: v4l2: Trace calculated p/b0/b1 initial reflist") Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-h264.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-h264.c b/drivers/media/v4l2-core/v4l2-h264.c index 72bd64f65198..c00197d095e7 100644 --- a/drivers/media/v4l2-core/v4l2-h264.c +++ b/drivers/media/v4l2-core/v4l2-h264.c @@ -305,6 +305,8 @@ static const char *format_ref_list_p(const struct v4l2_h264_reflist_builder *bui int n = 0, i; *out_str = kmalloc(tmp_str_size, GFP_KERNEL); + if (!(*out_str)) + return NULL; n += snprintf(*out_str + n, tmp_str_size - n, "|"); @@ -343,6 +345,8 @@ static const char *format_ref_list_b(const struct v4l2_h264_reflist_builder *bui int n = 0, i; *out_str = kmalloc(tmp_str_size, GFP_KERNEL); + if (!(*out_str)) + return NULL; n += snprintf(*out_str + n, tmp_str_size - n, "|"); -- cgit v1.2.3 From 05165248df6552daaacf5621e3a5e2369117a8a9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 28 Dec 2022 18:02:27 +0100 Subject: media: v4l2-ctrls-api.c: move ctrl->is_new = 1 to the correct line The patch that fixed string control support somehow got mangled when it was merged in mainline: the added line ended up in the wrong place. Fix this. Fixes: 73278d483378 ("media: v4l2-ctrls-api.c: add back dropped ctrl->is_new = 1") Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls-api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c index 3d3b6dc24ca6..002ea6588edf 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c @@ -150,8 +150,8 @@ static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl) * then return an error. */ if (strlen(ctrl->p_new.p_char) == ctrl->maximum && last) - ctrl->is_new = 1; return -ERANGE; + ctrl->is_new = 1; } return ret; default: -- cgit v1.2.3 From 68e87ebf2605643072264ed452f4e560417ddb1b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 15 Feb 2023 15:48:17 +0100 Subject: media: v4l2-subdev.c: clear stream field Both userspace and kernelspace can pass structs with an uninitialized 'stream' field. Since the check_state() function checks for a non-zero stream field, suddenly these calls will fails with -EINVAL. So check in the wrapper functions in v4l2-subdev.c (which are used by both the kernel and userspace API) if V4L2_SUBDEV_FL_STREAMS is set, and if not, then zero the stream field. Currently no drivers set V4L2_SUBDEV_FL_STREAMS, so the stream field will always be set to 0. This patch might well be reverted in the future when streams support is fully enabled and we finalized the userspace API support for this feature. Signed-off-by: Hans Verkuil Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-subdev.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 1bebcda2bd20..dff1d9be7841 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -197,6 +197,9 @@ static inline int check_format(struct v4l2_subdev *sd, if (!format) return -EINVAL; + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) + format->stream = 0; + return check_which(format->which) ? : check_pad(sd, format->pad) ? : check_state(sd, state, format->which, format->pad, format->stream); } @@ -224,6 +227,9 @@ static int call_enum_mbus_code(struct v4l2_subdev *sd, if (!code) return -EINVAL; + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) + code->stream = 0; + return check_which(code->which) ? : check_pad(sd, code->pad) ? : check_state(sd, state, code->which, code->pad, code->stream) ? : sd->ops->pad->enum_mbus_code(sd, state, code); @@ -236,6 +242,9 @@ static int call_enum_frame_size(struct v4l2_subdev *sd, if (!fse) return -EINVAL; + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) + fse->stream = 0; + return check_which(fse->which) ? : check_pad(sd, fse->pad) ? : check_state(sd, state, fse->which, fse->pad, fse->stream) ? : sd->ops->pad->enum_frame_size(sd, state, fse); @@ -271,6 +280,9 @@ static int call_enum_frame_interval(struct v4l2_subdev *sd, if (!fie) return -EINVAL; + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) + fie->stream = 0; + return check_which(fie->which) ? : check_pad(sd, fie->pad) ? : check_state(sd, state, fie->which, fie->pad, fie->stream) ? : sd->ops->pad->enum_frame_interval(sd, state, fie); @@ -283,6 +295,9 @@ static inline int check_selection(struct v4l2_subdev *sd, if (!sel) return -EINVAL; + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) + sel->stream = 0; + return check_which(sel->which) ? : check_pad(sd, sel->pad) ? : check_state(sd, state, sel->which, sel->pad, sel->stream); } -- cgit v1.2.3