Commit a418bb3f authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Mauro Carvalho Chehab

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: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarMichal Simek <michal.simek@xilinx.com>
Signed-off-by: default avatarSakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: default avatarJacopo Mondi <jacopo+renesas@jmondi.org>
Signed-off-by: default avatarTomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@kernel.org>
parent ea73eda5
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/version.h> #include <linux/version.h>
#include <linux/v4l2-subdev.h>
#include <linux/videodev2.h> #include <linux/videodev2.h>
#include <media/media-device.h> /* for media_set_bus_info() */ #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, ...@@ -3148,6 +3149,21 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
ret = 1; ret = 1;
break; 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; return ret;
...@@ -3394,8 +3410,15 @@ video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg, ...@@ -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 * Some ioctls can return an error, but still have valid
* results that must be returned. * 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; goto out;
if (has_array_args) { if (has_array_args) {
......
...@@ -23,6 +23,16 @@ ...@@ -23,6 +23,16 @@
#include <media/v4l2-fh.h> #include <media/v4l2-fh.h>
#include <media/v4l2-ioctl.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) #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) 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, ...@@ -432,6 +442,10 @@ subdev_ioctl_get_state(struct v4l2_subdev *sd, struct v4l2_subdev_fh *subdev_fh,
case VIDIOC_SUBDEV_S_SELECTION: case VIDIOC_SUBDEV_S_SELECTION:
which = ((struct v4l2_subdev_selection *)arg)->which; which = ((struct v4l2_subdev_selection *)arg)->which;
break; 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 ? return which == V4L2_SUBDEV_FORMAT_TRY ?
...@@ -748,6 +762,75 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg, ...@@ -748,6 +762,75 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
case VIDIOC_SUBDEV_QUERYSTD: case VIDIOC_SUBDEV_QUERYSTD:
return v4l2_subdev_call(sd, video, querystd, arg); 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: default:
return v4l2_subdev_call(sd, core, ioctl, cmd, arg); return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
} }
...@@ -1031,6 +1114,7 @@ void __v4l2_subdev_state_free(struct v4l2_subdev_state *state) ...@@ -1031,6 +1114,7 @@ void __v4l2_subdev_state_free(struct v4l2_subdev_state *state)
mutex_destroy(&state->_lock); mutex_destroy(&state->_lock);
kfree(state->routing.routes);
kvfree(state->pads); kvfree(state->pads);
kfree(state); kfree(state);
} }
......
...@@ -700,12 +700,26 @@ struct v4l2_subdev_pad_config { ...@@ -700,12 +700,26 @@ struct v4l2_subdev_pad_config {
struct v4l2_rect try_compose; 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. * struct v4l2_subdev_state - Used for storing subdev state information.
* *
* @_lock: default for 'lock' * @_lock: default for 'lock'
* @lock: mutex for the state. May be replaced by the user. * @lock: mutex for the state. May be replaced by the user.
* @pads: &struct v4l2_subdev_pad_config array * @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 * 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 * of the main argument is set to %V4L2_SUBDEV_FORMAT_TRY. For
...@@ -716,6 +730,7 @@ struct v4l2_subdev_state { ...@@ -716,6 +730,7 @@ struct v4l2_subdev_state {
struct mutex _lock; struct mutex _lock;
struct mutex *lock; struct mutex *lock;
struct v4l2_subdev_pad_config *pads; struct v4l2_subdev_pad_config *pads;
struct v4l2_subdev_krouting routing;
}; };
/** /**
...@@ -768,6 +783,9 @@ struct v4l2_subdev_state { ...@@ -768,6 +783,9 @@ struct v4l2_subdev_state {
* this operation as close as possible to stream on time. The * this operation as close as possible to stream on time. The
* operation shall fail if the pad index it has been called on * operation shall fail if the pad index it has been called on
* is not valid or in case of unrecoverable failures. * 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 { struct v4l2_subdev_pad_ops {
int (*init_cfg)(struct v4l2_subdev *sd, int (*init_cfg)(struct v4l2_subdev *sd,
...@@ -810,6 +828,10 @@ struct v4l2_subdev_pad_ops { ...@@ -810,6 +828,10 @@ struct v4l2_subdev_pad_ops {
struct v4l2_mbus_frame_desc *fd); struct v4l2_mbus_frame_desc *fd);
int (*get_mbus_config)(struct v4l2_subdev *sd, unsigned int pad, int (*get_mbus_config)(struct v4l2_subdev *sd, unsigned int pad,
struct v4l2_mbus_config *config); 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);
}; };
/** /**
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#ifndef __LINUX_V4L2_SUBDEV_H #ifndef __LINUX_V4L2_SUBDEV_H
#define __LINUX_V4L2_SUBDEV_H #define __LINUX_V4L2_SUBDEV_H
#include <linux/const.h>
#include <linux/ioctl.h> #include <linux/ioctl.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/v4l2-common.h> #include <linux/v4l2-common.h>
...@@ -178,6 +179,46 @@ struct v4l2_subdev_capability { ...@@ -178,6 +179,46 @@ struct v4l2_subdev_capability {
/* The v4l2 sub-device supports routing and multiplexed streams. */ /* The v4l2 sub-device supports routing and multiplexed streams. */
#define V4L2_SUBDEV_CAP_STREAMS 0x00000002 #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 */ /* Backwards compatibility define --- to be removed */
#define v4l2_subdev_edid v4l2_edid #define v4l2_subdev_edid v4l2_edid
...@@ -193,6 +234,8 @@ struct v4l2_subdev_capability { ...@@ -193,6 +234,8 @@ struct v4l2_subdev_capability {
#define VIDIOC_SUBDEV_S_CROP _IOWR('V', 60, struct v4l2_subdev_crop) #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_G_SELECTION _IOWR('V', 61, struct v4l2_subdev_selection)
#define VIDIOC_SUBDEV_S_SELECTION _IOWR('V', 62, 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 */ /* 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_G_STD _IOR('V', 23, v4l2_std_id)
#define VIDIOC_SUBDEV_S_STD _IOW('V', 24, v4l2_std_id) #define VIDIOC_SUBDEV_S_STD _IOW('V', 24, v4l2_std_id)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment