media: subdev: Add [GS]_ROUTING subdev ioctls and operations
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Fri, 23 Apr 2021 07:15:54 +0000 (09:15 +0200)
committerMauro Carvalho Chehab <mchehab@kernel.org>
Sun, 22 Jan 2023 08:33:33 +0000 (09:33 +0100)
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 <laurent.pinchart@ideasonboard.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
drivers/media/v4l2-core/v4l2-ioctl.c
drivers/media/v4l2-core/v4l2-subdev.c
include/media/v4l2-subdev.h
include/uapi/linux/v4l2-subdev.h

index 8e0a0ff62a704ec4bc07a2ba0cdacf80d51d4a98..b26da2650289090e6fbc2ebf7846c847bfb0d0c0 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/kernel.h>
 #include <linux/version.h>
 
+#include <linux/v4l2-subdev.h>
 #include <linux/videodev2.h>
 
 #include <media/media-device.h> /* 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) {
index 3adb186bb742c0deed19788cc0f09becb2d45c1a..f047ac1bf5efd896a897725bafaf00d32720a27a 100644 (file)
 #include <media/v4l2-fh.h>
 #include <media/v4l2-ioctl.h>
 
+/*
+ * 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);
 }
index 95361b54108792ccff2f2dd7d909f6f9fa7fb3a7..67e55758f037a1bf854f9e74753750ac23f514ae 100644 (file)
@@ -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);
 };
 
 /**
index ffed9047e0befe001be18f82dba83b5280334e36..284ae8c95960bc7c7af4f74c6b0a15071f3202f4 100644 (file)
@@ -11,6 +11,7 @@
 #ifndef __LINUX_V4L2_SUBDEV_H
 #define __LINUX_V4L2_SUBDEV_H
 
+#include <linux/const.h>
 #include <linux/ioctl.h>
 #include <linux/types.h>
 #include <linux/v4l2-common.h>
@@ -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)