* [PATCH v1 1/4] v4l2-utils: update Linux headers for multiplexed streams
2021-11-30 14:18 [PATCH v1 0/4] v4l-utils: support multiplexed streams Tomi Valkeinen
@ 2021-11-30 14:18 ` Tomi Valkeinen
2021-12-01 17:50 ` Laurent Pinchart
2021-11-30 14:18 ` [PATCH v1 2/4] v4l2-ctl: Add routing and streams support Tomi Valkeinen
` (3 subsequent siblings)
4 siblings, 1 reply; 12+ messages in thread
From: Tomi Valkeinen @ 2021-11-30 14:18 UTC (permalink / raw)
To: linux-media, sakari.ailus, Jacopo Mondi, Laurent Pinchart,
niklas.soderlund+renesas, Mauro Carvalho Chehab, Hans Verkuil,
Pratyush Yadav
Cc: Tomi Valkeinen
Update the Linux kernel headers for multiplexed streams.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
include/linux/v4l2-subdev.h | 88 ++++++++++++++++++++++++++++++++++---
1 file changed, 81 insertions(+), 7 deletions(-)
diff --git a/include/linux/v4l2-subdev.h b/include/linux/v4l2-subdev.h
index 658106f5..480891db 100644
--- a/include/linux/v4l2-subdev.h
+++ b/include/linux/v4l2-subdev.h
@@ -44,13 +44,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];
};
/**
@@ -58,13 +60,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
@@ -80,6 +84,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 {
@@ -88,7 +93,8 @@ struct v4l2_subdev_mbus_code_enum {
__u32 code;
__u32 which;
__u32 flags;
- __u32 reserved[7];
+ __u32 stream;
+ __u32 reserved[6];
};
/**
@@ -101,6 +107,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 {
@@ -112,19 +119,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];
};
/**
@@ -136,6 +146,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 {
@@ -146,7 +157,8 @@ struct v4l2_subdev_frame_interval_enum {
__u32 height;
struct v4l2_fract interval;
__u32 which;
- __u32 reserved[8];
+ __u32 stream;
+ __u32 reserved[7];
};
/**
@@ -158,6 +170,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.
@@ -170,7 +183,8 @@ struct v4l2_subdev_selection {
__u32 target;
__u32 flags;
struct v4l2_rect r;
- __u32 reserved[8];
+ __u32 stream;
+ __u32 reserved[7];
};
/**
@@ -188,6 +202,64 @@ 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 multiplexed streams. */
+#define V4L2_SUBDEV_CAP_MPLEXED 0x00000002
+
+/*
+ * Is the route active? An active route will start when streaming is enabled
+ * on a video node.
+ */
+#define V4L2_SUBDEV_ROUTE_FL_ACTIVE _BITUL(0)
+
+/*
+ * Is the route immutable, i.e. can it be activated and inactivated?
+ * Set by the driver.
+ */
+#define V4L2_SUBDEV_ROUTE_FL_IMMUTABLE _BITUL(1)
+
+/*
+ * Is the route a source endpoint? A source endpoint route refers to a stream
+ * generated internally by the subdevice (usually a sensor), and thus there
+ * is no sink-side endpoint for the route. The sink_pad and sink_stream
+ * fields are unused.
+ * Set by the driver.
+ */
+#define V4L2_SUBDEV_ROUTE_FL_SOURCE _BITUL(2)
+
+/**
+ * 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
@@ -203,6 +275,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)
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v1 1/4] v4l2-utils: update Linux headers for multiplexed streams
2021-11-30 14:18 ` [PATCH v1 1/4] v4l2-utils: update Linux headers for " Tomi Valkeinen
@ 2021-12-01 17:50 ` Laurent Pinchart
2021-12-01 18:53 ` Tomi Valkeinen
0 siblings, 1 reply; 12+ messages in thread
From: Laurent Pinchart @ 2021-12-01 17:50 UTC (permalink / raw)
To: Tomi Valkeinen
Cc: linux-media, sakari.ailus, Jacopo Mondi,
niklas.soderlund+renesas, Mauro Carvalho Chehab, Hans Verkuil,
Pratyush Yadav
Hi Tomi,
Thank you for the patch.
On Tue, Nov 30, 2021 at 04:18:12PM +0200, Tomi Valkeinen wrote:
> Update the Linux kernel headers for multiplexed streams.
I suppose the patch is for testing only, and will be regenerated once
the API is merged in the kernel, before merging it in v4l-utils. It
should then use the sync-with-kernel.sh script to pull in all kernel
header changes.
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
> ---
> include/linux/v4l2-subdev.h | 88 ++++++++++++++++++++++++++++++++++---
> 1 file changed, 81 insertions(+), 7 deletions(-)
>
> diff --git a/include/linux/v4l2-subdev.h b/include/linux/v4l2-subdev.h
> index 658106f5..480891db 100644
> --- a/include/linux/v4l2-subdev.h
> +++ b/include/linux/v4l2-subdev.h
> @@ -44,13 +44,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];
> };
>
> /**
> @@ -58,13 +60,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
> @@ -80,6 +84,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 {
> @@ -88,7 +93,8 @@ struct v4l2_subdev_mbus_code_enum {
> __u32 code;
> __u32 which;
> __u32 flags;
> - __u32 reserved[7];
> + __u32 stream;
> + __u32 reserved[6];
> };
>
> /**
> @@ -101,6 +107,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 {
> @@ -112,19 +119,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];
> };
>
> /**
> @@ -136,6 +146,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 {
> @@ -146,7 +157,8 @@ struct v4l2_subdev_frame_interval_enum {
> __u32 height;
> struct v4l2_fract interval;
> __u32 which;
> - __u32 reserved[8];
> + __u32 stream;
> + __u32 reserved[7];
> };
>
> /**
> @@ -158,6 +170,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.
> @@ -170,7 +183,8 @@ struct v4l2_subdev_selection {
> __u32 target;
> __u32 flags;
> struct v4l2_rect r;
> - __u32 reserved[8];
> + __u32 stream;
> + __u32 reserved[7];
> };
>
> /**
> @@ -188,6 +202,64 @@ 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 multiplexed streams. */
> +#define V4L2_SUBDEV_CAP_MPLEXED 0x00000002
> +
> +/*
> + * Is the route active? An active route will start when streaming is enabled
> + * on a video node.
> + */
> +#define V4L2_SUBDEV_ROUTE_FL_ACTIVE _BITUL(0)
> +
> +/*
> + * Is the route immutable, i.e. can it be activated and inactivated?
> + * Set by the driver.
> + */
> +#define V4L2_SUBDEV_ROUTE_FL_IMMUTABLE _BITUL(1)
> +
> +/*
> + * Is the route a source endpoint? A source endpoint route refers to a stream
> + * generated internally by the subdevice (usually a sensor), and thus there
> + * is no sink-side endpoint for the route. The sink_pad and sink_stream
> + * fields are unused.
> + * Set by the driver.
> + */
> +#define V4L2_SUBDEV_ROUTE_FL_SOURCE _BITUL(2)
> +
> +/**
> + * 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
>
> @@ -203,6 +275,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)
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v1 1/4] v4l2-utils: update Linux headers for multiplexed streams
2021-12-01 17:50 ` Laurent Pinchart
@ 2021-12-01 18:53 ` Tomi Valkeinen
0 siblings, 0 replies; 12+ messages in thread
From: Tomi Valkeinen @ 2021-12-01 18:53 UTC (permalink / raw)
To: Laurent Pinchart
Cc: linux-media, sakari.ailus, Jacopo Mondi,
niklas.soderlund+renesas, Mauro Carvalho Chehab, Hans Verkuil,
Pratyush Yadav
On 01/12/2021 19:50, Laurent Pinchart wrote:
> Hi Tomi,
>
> Thank you for the patch.
>
> On Tue, Nov 30, 2021 at 04:18:12PM +0200, Tomi Valkeinen wrote:
>> Update the Linux kernel headers for multiplexed streams.
>
> I suppose the patch is for testing only, and will be regenerated once
> the API is merged in the kernel, before merging it in v4l-utils. It
> should then use the sync-with-kernel.sh script to pull in all kernel
> header changes.
You're right. I should have mentioned that this series is not for merging.
Tomi
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v1 2/4] v4l2-ctl: Add routing and streams support
2021-11-30 14:18 [PATCH v1 0/4] v4l-utils: support multiplexed streams Tomi Valkeinen
2021-11-30 14:18 ` [PATCH v1 1/4] v4l2-utils: update Linux headers for " Tomi Valkeinen
@ 2021-11-30 14:18 ` Tomi Valkeinen
2021-11-30 14:18 ` [PATCH v1 3/4] media-ctl: add support for routes and streams Tomi Valkeinen
` (2 subsequent siblings)
4 siblings, 0 replies; 12+ messages in thread
From: Tomi Valkeinen @ 2021-11-30 14:18 UTC (permalink / raw)
To: linux-media, sakari.ailus, Jacopo Mondi, Laurent Pinchart,
niklas.soderlund+renesas, Mauro Carvalho Chehab, Hans Verkuil,
Pratyush Yadav
Cc: Tomi Valkeinen
Add support to get and set subdev routes and to get and set
configurations per stream.
Based on work from Jacopo Mondi <jacopo@jmondi.org> and
Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
utils/v4l2-ctl/v4l2-ctl-subdev.cpp | 289 +++++++++++++++++++++++++----
utils/v4l2-ctl/v4l2-ctl.cpp | 2 +
utils/v4l2-ctl/v4l2-ctl.h | 2 +
3 files changed, 260 insertions(+), 33 deletions(-)
diff --git a/utils/v4l2-ctl/v4l2-ctl-subdev.cpp b/utils/v4l2-ctl/v4l2-ctl-subdev.cpp
index 33cc1342..dff16936 100644
--- a/utils/v4l2-ctl/v4l2-ctl-subdev.cpp
+++ b/utils/v4l2-ctl/v4l2-ctl-subdev.cpp
@@ -1,5 +1,13 @@
#include "v4l2-ctl.h"
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
+
+/*
+ * The max value comes from a check in the kernel source code
+ * drivers/media/v4l2-core/v4l2-ioctl.c check_array_args()
+ */
+#define NUM_ROUTES_MAX 256
+
struct mbus_name {
const char *name;
__u32 code;
@@ -19,45 +27,57 @@ static const struct mbus_name mbus_names[] = {
#define SelectionFlags (1L<<4)
static __u32 list_mbus_codes_pad;
+static __u32 list_mbus_codes_stream = 0;
static __u32 get_fmt_pad;
+static __u32 get_fmt_stream = 0;
static __u32 get_sel_pad;
+static __u32 get_sel_stream = 0;
static __u32 get_fps_pad;
+static __u32 get_fps_stream = 0;
static int get_sel_target = -1;
static unsigned int set_selection;
static struct v4l2_subdev_selection vsel;
static unsigned int set_fmt;
static __u32 set_fmt_pad;
+static __u32 set_fmt_stream = 0;
static struct v4l2_mbus_framefmt ffmt;
static struct v4l2_subdev_frame_size_enum frmsize;
static struct v4l2_subdev_frame_interval_enum frmival;
static __u32 set_fps_pad;
+static __u32 set_fps_stream = 0;
static double set_fps;
+static struct v4l2_subdev_routing routing;
+static struct v4l2_subdev_route routes[NUM_ROUTES_MAX];
void subdev_usage()
{
printf("\nSub-Device options:\n"
- " --list-subdev-mbus-codes <pad>\n"
+ " --list-subdev-mbus-codes pad=<pad>,stream=<stream>\n"
" display supported mediabus codes for this pad (0 is default)\n"
" [VIDIOC_SUBDEV_ENUM_MBUS_CODE]\n"
- " --list-subdev-framesizes pad=<pad>,code=<code>\n"
+ " --list-subdev-framesizes pad=<pad>,stream=<stream>,code=<code>\n"
" list supported framesizes for this pad and code\n"
" [VIDIOC_SUBDEV_ENUM_FRAME_SIZE]\n"
" <code> is the value of the mediabus code\n"
- " --list-subdev-frameintervals pad=<pad>,width=<w>,height=<h>,code=<code>\n"
+ " --list-subdev-frameintervals pad=<pad>,stream=<stream>,width=<w>,height=<h>,code=<code>\n"
" list supported frame intervals for this pad and code and\n"
" the given width and height [VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL]\n"
" <code> is the value of the mediabus code\n"
- " --get-subdev-fmt [<pad>]\n"
- " query the frame format for the given pad [VIDIOC_SUBDEV_G_FMT]\n"
- " --get-subdev-selection pad=<pad>,target=<target>\n"
+ " --get-subdev-fmt pad=<pad>,stream=<stream>\n"
+ " query the frame format for the given pad and optional stream [VIDIOC_SUBDEV_G_FMT]\n"
+ " <pad> the pad to get the format from\n"
+ " <stream> the stream to get the format from (0 if not specified)\n"
+ " --get-subdev-selection pad=<pad>,stream=<stream>,target=<target>\n"
" query the frame selection rectangle [VIDIOC_SUBDEV_G_SELECTION]\n"
" See --set-subdev-selection command for the valid <target> values.\n"
- " --get-subdev-fps [<pad>]\n"
+ " --get-subdev-fps pad=<pad>,stream=<stream>\n"
" query the frame rate [VIDIOC_SUBDEV_G_FRAME_INTERVAL]\n"
" --set-subdev-fmt (for testing only, otherwise use media-ctl)\n"
- " --try-subdev-fmt pad=<pad>,width=<w>,height=<h>,code=<code>,field=<f>,colorspace=<c>,\n"
+ " --try-subdev-fmt pad=<pad>,stream=<stream>,width=<w>,height=<h>,code=<code>,field=<f>,colorspace=<c>,\n"
" xfer=<xf>,ycbcr=<y>,hsv=<hsv>,quantization=<q>\n"
- " set the frame format [VIDIOC_SUBDEV_S_FMT]\n"
+ " set the frame format for the given pad and optional stream [VIDIOC_SUBDEV_S_FMT]\n"
+ " <pad> the pad to get the format from\n"
+ " <stream> the stream to get the format from (0 if not specified)\n"
" <code> is the value of the mediabus code\n"
" <f> can be one of the following field layouts:\n"
" any, none, top, bottom, interlaced, seq_tb, seq_bt,\n"
@@ -74,14 +94,30 @@ void subdev_usage()
" <q> can be one of the following quantization methods:\n"
" default, full-range, lim-range\n"
" --set-subdev-selection (for testing only, otherwise use media-ctl)\n"
- " --try-subdev-selection pad=<pad>,target=<target>,flags=<flags>,\n"
+ " --try-subdev-selection pad=<pad>,stream=<stream>,target=<target>,flags=<flags>,\n"
" top=<x>,left=<y>,width=<w>,height=<h>\n"
" set the video capture selection rectangle [VIDIOC_SUBDEV_S_SELECTION]\n"
" target=crop|crop_bounds|crop_default|compose|compose_bounds|\n"
" compose_default|compose_padded|native_size\n"
" flags=le|ge|keep-config\n"
- " --set-subdev-fps pad=<pad>,fps=<fps> (for testing only, otherwise use media-ctl)\n"
+ " --set-subdev-fps pad=<pad>,stream=<stream>,fps=<fps> (for testing only, otherwise use media-ctl)\n"
" set the frame rate [VIDIOC_SUBDEV_S_FRAME_INTERVAL]\n"
+ " --get-routing Print the route topology\n"
+ " --set-routing <routes>\n"
+ " Comma-separated list of route descriptors to setup\n"
+ "\n"
+ "Routes are defined as\n"
+ " routes = route { ',' route } ;\n"
+ " route = sink '->' source '[' flags ']' ;\n"
+ " sink = sink-pad '/' sink-stream ;\n"
+ " source = source-pad '/' source-stream ;\n"
+ "\n"
+ "where\n"
+ " sink-pad = Pad numeric identifier for sink\n"
+ " sink-stream = Stream numeric identifier for sink\n"
+ " source-pad = Pad numeric identifier for source\n"
+ " source-stream = Stream numeric identifier for source\n"
+ " flags = Route flags (0: inactive, 1: active)\n"
);
}
@@ -91,14 +127,33 @@ void subdev_cmd(int ch, char *optarg)
switch (ch) {
case OptListSubDevMBusCodes:
- if (optarg)
- list_mbus_codes_pad = strtoul(optarg, nullptr, 0);
+ subs = optarg;
+ while (subs && *subs != '\0') {
+ static constexpr const char *subopts[] = {
+ "pad",
+ "stream",
+ nullptr
+ };
+
+ switch (parse_subopt(&subs, subopts, &value)) {
+ case 0:
+ list_mbus_codes_pad = strtoul(value, nullptr, 0);
+ break;
+ case 1:
+ list_mbus_codes_stream = strtoul(value, nullptr, 0);
+ break;
+ default:
+ subdev_usage();
+ std::exit(EXIT_FAILURE);
+ }
+ }
break;
case OptListSubDevFrameSizes:
subs = optarg;
while (*subs != '\0') {
static constexpr const char *subopts[] = {
"pad",
+ "stream",
"code",
nullptr
};
@@ -108,6 +163,9 @@ void subdev_cmd(int ch, char *optarg)
frmsize.pad = strtoul(value, nullptr, 0);
break;
case 1:
+ frmsize.stream = strtoul(value, nullptr, 0);
+ break;
+ case 2:
frmsize.code = strtoul(value, nullptr, 0);
break;
default:
@@ -121,6 +179,7 @@ void subdev_cmd(int ch, char *optarg)
while (*subs != '\0') {
static constexpr const char *subopts[] = {
"pad",
+ "stream",
"code",
"width",
"height",
@@ -132,12 +191,15 @@ void subdev_cmd(int ch, char *optarg)
frmival.pad = strtoul(value, nullptr, 0);
break;
case 1:
- frmival.code = strtoul(value, nullptr, 0);
+ frmival.stream = strtoul(value, nullptr, 0);
break;
case 2:
- frmival.width = strtoul(value, nullptr, 0);
+ frmival.code = strtoul(value, nullptr, 0);
break;
case 3:
+ frmival.width = strtoul(value, nullptr, 0);
+ break;
+ case 4:
frmival.height = strtoul(value, nullptr, 0);
break;
default:
@@ -147,14 +209,33 @@ void subdev_cmd(int ch, char *optarg)
}
break;
case OptGetSubDevFormat:
- if (optarg)
- get_fmt_pad = strtoul(optarg, nullptr, 0);
+ subs = optarg;
+ while (subs && *subs != '\0') {
+ static constexpr const char *subopts[] = {
+ "pad",
+ "stream",
+ nullptr
+ };
+
+ switch (parse_subopt(&subs, subopts, &value)) {
+ case 0:
+ get_fmt_pad = strtoul(value, nullptr, 0);
+ break;
+ case 1:
+ get_fmt_stream = strtoul(value, nullptr, 0);
+ break;
+ default:
+ subdev_usage();
+ std::exit(EXIT_FAILURE);
+ }
+ }
break;
case OptGetSubDevSelection:
subs = optarg;
while (*subs != '\0') {
static constexpr const char *subopts[] = {
"pad",
+ "stream",
"target",
nullptr
};
@@ -165,6 +246,9 @@ void subdev_cmd(int ch, char *optarg)
get_sel_pad = strtoul(value, nullptr, 0);
break;
case 1:
+ get_sel_stream = strtoul(value, nullptr, 0);
+ break;
+ case 2:
if (parse_selection_target(value, target)) {
fprintf(stderr, "Unknown selection target\n");
subdev_usage();
@@ -179,8 +263,26 @@ void subdev_cmd(int ch, char *optarg)
}
break;
case OptGetSubDevFPS:
- if (optarg)
- get_fps_pad = strtoul(optarg, nullptr, 0);
+ subs = optarg;
+ while (subs && *subs != '\0') {
+ static constexpr const char *subopts[] = {
+ "pad",
+ "stream",
+ nullptr
+ };
+
+ switch (parse_subopt(&subs, subopts, &value)) {
+ case 0:
+ get_fps_pad = strtoul(value, nullptr, 0);
+ break;
+ case 1:
+ get_fps_stream = strtoul(value, nullptr, 0);
+ break;
+ default:
+ subdev_usage();
+ std::exit(EXIT_FAILURE);
+ }
+ }
break;
case OptSetSubDevFormat:
case OptTrySubDevFormat:
@@ -198,6 +300,7 @@ void subdev_cmd(int ch, char *optarg)
"quantization",
"xfer",
"pad",
+ "stream",
nullptr
};
@@ -244,6 +347,9 @@ void subdev_cmd(int ch, char *optarg)
case 9:
set_fmt_pad = strtoul(value, nullptr, 0);
break;
+ case 10:
+ set_fmt_stream = strtoul(value, nullptr, 0);
+ break;
default:
fprintf(stderr, "Unknown option\n");
subdev_usage();
@@ -264,6 +370,7 @@ void subdev_cmd(int ch, char *optarg)
"width",
"height",
"pad",
+ "stream",
nullptr
};
@@ -298,6 +405,9 @@ void subdev_cmd(int ch, char *optarg)
case 6:
vsel.pad = strtoul(value, nullptr, 0);
break;
+ case 7:
+ vsel.stream = strtoul(value, nullptr, 0);
+ break;
default:
fprintf(stderr, "Unknown option\n");
subdev_usage();
@@ -311,6 +421,7 @@ void subdev_cmd(int ch, char *optarg)
while (*subs != '\0') {
static constexpr const char *subopts[] = {
"pad",
+ "stream",
"fps",
nullptr
};
@@ -320,6 +431,9 @@ void subdev_cmd(int ch, char *optarg)
set_fps_pad = strtoul(value, nullptr, 0);
break;
case 1:
+ set_fps_stream = strtoul(value, nullptr, 0);
+ break;
+ case 2:
set_fps = strtod(value, nullptr);
break;
default:
@@ -329,6 +443,47 @@ void subdev_cmd(int ch, char *optarg)
}
}
break;
+ case OptSetRouting: {
+ struct v4l2_subdev_route *r;
+ char *end, *ref, *tok;
+ unsigned int flags;
+
+ memset(&routing, 0, sizeof(routing));
+ memset(routes, 0, sizeof(routes[0]) * NUM_ROUTES_MAX);
+ routing.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ routing.num_routes = 0;
+ routing.routes = (__u64)routes;
+
+ if (!optarg)
+ break;
+
+ r = (v4l2_subdev_route *)routing.routes;
+ ref = end = strdup(optarg);
+ while ((tok = strsep(&end, ",")) != NULL) {
+ if (sscanf(tok, "%u/%u -> %u/%u [%u]",
+ &r->sink_pad, &r->sink_stream,
+ &r->source_pad, &r->source_stream,
+ &flags) != 5) {
+ free(ref);
+ fprintf(stderr, "Invalid route information specified\n");
+ subdev_usage();
+ std::exit(EXIT_FAILURE);
+ }
+
+ if (flags & ~(V4L2_SUBDEV_ROUTE_FL_ACTIVE | V4L2_SUBDEV_ROUTE_FL_IMMUTABLE | V4L2_SUBDEV_ROUTE_FL_SOURCE)) {
+ fprintf(stderr, "Invalid route flags specified: %#x\n", flags);
+ subdev_usage();
+ std::exit(EXIT_FAILURE);
+ }
+
+ r->flags = flags;
+
+ r++;
+ routing.num_routes++;
+ }
+ free(ref);
+ break;
+ }
default:
break;
}
@@ -394,6 +549,7 @@ void subdev_set(cv4l_fd &_fd)
memset(&fmt, 0, sizeof(fmt));
fmt.pad = set_fmt_pad;
+ fmt.stream = set_fmt_stream;
fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
if (doioctl(fd, VIDIOC_SUBDEV_G_FMT, &fmt) == 0) {
@@ -430,7 +586,7 @@ void subdev_set(cv4l_fd &_fd)
else
fmt.which = V4L2_SUBDEV_FORMAT_TRY;
- printf("ioctl: VIDIOC_SUBDEV_S_FMT (pad=%u)\n", fmt.pad);
+ printf("ioctl: VIDIOC_SUBDEV_S_FMT (pad=%u,stream=%u)\n", fmt.pad, fmt.stream);
ret = doioctl(fd, VIDIOC_SUBDEV_S_FMT, &fmt);
if (ret == 0 && (verbose || !options[OptSetSubDevFormat]))
print_framefmt(fmt.format);
@@ -441,6 +597,7 @@ void subdev_set(cv4l_fd &_fd)
memset(&sel, 0, sizeof(sel));
sel.pad = vsel.pad;
+ sel.stream = vsel.stream;
sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
sel.target = vsel.target;
@@ -461,7 +618,7 @@ void subdev_set(cv4l_fd &_fd)
else
sel.which = V4L2_SUBDEV_FORMAT_TRY;
- printf("ioctl: VIDIOC_SUBDEV_S_SELECTION (pad=%u)\n", sel.pad);
+ printf("ioctl: VIDIOC_SUBDEV_S_SELECTION (pad=%u,stream=%u)\n", sel.pad, sel.stream);
int ret = doioctl(fd, VIDIOC_SUBDEV_S_SELECTION, &sel);
if (ret == 0 && (verbose || !options[OptSetSubDevSelection]))
print_subdev_selection(sel);
@@ -472,6 +629,7 @@ void subdev_set(cv4l_fd &_fd)
memset(&fival, 0, sizeof(fival));
fival.pad = set_fps_pad;
+ fival.stream = set_fps_stream;
if (set_fps <= 0) {
fprintf(stderr, "invalid fps %f\n", set_fps);
@@ -482,7 +640,7 @@ void subdev_set(cv4l_fd &_fd)
fival.interval.denominator = static_cast<uint32_t>(set_fps * fival.interval.numerator);
printf("Note: --set-subdev-fps is only for testing.\n"
"Normally media-ctl is used to configure the video pipeline.\n");
- printf("ioctl: VIDIOC_SUBDEV_S_FRAME_INTERVAL (pad=%u)\n", fival.pad);
+ printf("ioctl: VIDIOC_SUBDEV_S_FRAME_INTERVAL (pad=%u,stream=%u)\n", fival.pad, fival.stream);
if (doioctl(fd, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival) == 0) {
if (!fival.interval.denominator || !fival.interval.numerator)
printf("\tFrames per second: invalid (%d/%d)\n",
@@ -493,6 +651,56 @@ void subdev_set(cv4l_fd &_fd)
fival.interval.denominator, fival.interval.numerator);
}
}
+ if (options[OptSetRouting]) {
+ if (doioctl(fd, VIDIOC_SUBDEV_S_ROUTING, &routing) == 0)
+ printf("Routing set\n");
+ }
+}
+
+struct flag_name {
+ __u32 flag;
+ const char *name;
+};
+
+static void print_flags(const struct flag_name *flag_names, unsigned int num_entries, __u32 flags)
+{
+ bool first = true;
+ unsigned int i;
+
+ for (i = 0; i < num_entries; i++) {
+ if (!(flags & flag_names[i].flag))
+ continue;
+ if (!first)
+ printf(",");
+ printf("%s", flag_names[i].name);
+ flags &= ~flag_names[i].flag;
+ first = false;
+ }
+
+ if (flags) {
+ if (!first)
+ printf(",");
+ printf("0x%x", flags);
+ }
+}
+
+static void print_routes(const struct v4l2_subdev_routing *r)
+{
+ unsigned int i;
+ struct v4l2_subdev_route *routes = (struct v4l2_subdev_route *)r->routes;
+
+ static const struct flag_name route_flags[] = {
+ { V4L2_SUBDEV_ROUTE_FL_ACTIVE, "ENABLED" },
+ { V4L2_SUBDEV_ROUTE_FL_IMMUTABLE, "IMMUTABLE" },
+ };
+
+ for (i = 0; i < r->num_routes; i++) {
+ printf("%d/%d -> %d/%d [",
+ routes[i].sink_pad, routes[i].sink_stream,
+ routes[i].source_pad, routes[i].source_stream);
+ print_flags(route_flags, ARRAY_SIZE(route_flags), routes[i].flags);
+ printf("]\n");
+ }
}
void subdev_get(cv4l_fd &_fd)
@@ -505,8 +713,9 @@ void subdev_get(cv4l_fd &_fd)
memset(&fmt, 0, sizeof(fmt));
fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
fmt.pad = get_fmt_pad;
+ fmt.stream = get_fmt_stream;
- printf("ioctl: VIDIOC_SUBDEV_G_FMT (pad=%u)\n", fmt.pad);
+ printf("ioctl: VIDIOC_SUBDEV_G_FMT (pad=%u, stream=%u)\n", fmt.pad, fmt.stream);
if (doioctl(fd, VIDIOC_SUBDEV_G_FMT, &fmt) == 0)
print_framefmt(fmt.format);
}
@@ -518,8 +727,9 @@ void subdev_get(cv4l_fd &_fd)
memset(&sel, 0, sizeof(sel));
sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
sel.pad = get_sel_pad;
+ sel.stream = get_sel_stream;
- printf("ioctl: VIDIOC_SUBDEV_G_SELECTION (pad=%u)\n", sel.pad);
+ printf("ioctl: VIDIOC_SUBDEV_G_SELECTION (pad=%u,stream=%u)\n", sel.pad, sel.stream);
if (options[OptAll] || get_sel_target == -1) {
while (valid_seltarget_at_idx(idx)) {
sel.target = seltarget_at_idx(idx);
@@ -538,8 +748,9 @@ void subdev_get(cv4l_fd &_fd)
memset(&fival, 0, sizeof(fival));
fival.pad = get_fps_pad;
+ fival.stream = get_fps_stream;
- printf("ioctl: VIDIOC_SUBDEV_G_FRAME_INTERVAL (pad=%u)\n", fival.pad);
+ printf("ioctl: VIDIOC_SUBDEV_G_FRAME_INTERVAL (pad=%u,stream=%u)\n", fival.pad, fival.stream);
if (doioctl(fd, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &fival) == 0) {
if (!fival.interval.denominator || !fival.interval.numerator)
printf("\tFrames per second: invalid (%d/%d)\n",
@@ -550,6 +761,17 @@ void subdev_get(cv4l_fd &_fd)
fival.interval.denominator, fival.interval.numerator);
}
}
+
+ if (options[OptGetRouting]) {
+ memset(&routing, 0, sizeof(routing));
+ memset(routes, 0, sizeof(routes[0]) * NUM_ROUTES_MAX);
+ routing.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ routing.num_routes = NUM_ROUTES_MAX;
+ routing.routes = (__u64)routes;
+
+ if (doioctl(fd, VIDIOC_SUBDEV_G_ROUTING, &routing) == 0)
+ print_routes(&routing);
+ }
}
static void print_mbus_code(__u32 code)
@@ -566,11 +788,12 @@ static void print_mbus_code(__u32 code)
printf("\t0x%04x", code);
}
-static void print_mbus_codes(int fd, __u32 pad)
+static void print_mbus_codes(int fd, __u32 pad, __u32 stream)
{
struct v4l2_subdev_mbus_code_enum mbus_code = {};
mbus_code.pad = pad;
+ mbus_code.stream = stream;
mbus_code.which = V4L2_SUBDEV_FORMAT_TRY;
for (;;) {
@@ -623,13 +846,13 @@ void subdev_list(cv4l_fd &_fd)
int fd = _fd.g_fd();
if (options[OptListSubDevMBusCodes]) {
- printf("ioctl: VIDIOC_SUBDEV_ENUM_MBUS_CODE (pad=%u)\n",
- list_mbus_codes_pad);
- print_mbus_codes(fd, list_mbus_codes_pad);
+ printf("ioctl: VIDIOC_SUBDEV_ENUM_MBUS_CODE (pad=%u,stream=%u)\n",
+ list_mbus_codes_pad, list_mbus_codes_stream);
+ print_mbus_codes(fd, list_mbus_codes_pad, list_mbus_codes_stream);
}
if (options[OptListSubDevFrameSizes]) {
- printf("ioctl: VIDIOC_SUBDEV_ENUM_FRAME_SIZE (pad=%u)\n",
- frmsize.pad);
+ printf("ioctl: VIDIOC_SUBDEV_ENUM_FRAME_SIZE (pad=%u,stream=%u)\n",
+ frmsize.pad, frmsize.stream);
frmsize.index = 0;
frmsize.which = V4L2_SUBDEV_FORMAT_TRY;
while (test_ioctl(fd, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &frmsize) >= 0) {
@@ -638,8 +861,8 @@ void subdev_list(cv4l_fd &_fd)
}
}
if (options[OptListSubDevFrameIntervals]) {
- printf("ioctl: VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL (pad=%u)\n",
- frmival.pad);
+ printf("ioctl: VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL (pad=%u,stream=%u)\n",
+ frmival.pad, frmival.stream);
frmival.index = 0;
frmival.which = V4L2_SUBDEV_FORMAT_TRY;
while (test_ioctl(fd, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &frmival) >= 0) {
diff --git a/utils/v4l2-ctl/v4l2-ctl.cpp b/utils/v4l2-ctl/v4l2-ctl.cpp
index 764a7c71..b99c87dd 100644
--- a/utils/v4l2-ctl/v4l2-ctl.cpp
+++ b/utils/v4l2-ctl/v4l2-ctl.cpp
@@ -63,6 +63,8 @@ static struct option long_options[] = {
{"get-fmt-video-out", no_argument, nullptr, OptGetVideoOutFormat},
{"set-fmt-video-out", required_argument, nullptr, OptSetVideoOutFormat},
{"try-fmt-video-out", required_argument, nullptr, OptTryVideoOutFormat},
+ {"set-routing", required_argument, 0, OptSetRouting},
+ {"get-routing", no_argument, 0, OptGetRouting},
{"help", no_argument, nullptr, OptHelp},
{"help-tuner", no_argument, nullptr, OptHelpTuner},
{"help-io", no_argument, nullptr, OptHelpIO},
diff --git a/utils/v4l2-ctl/v4l2-ctl.h b/utils/v4l2-ctl/v4l2-ctl.h
index 39161466..bd1c89d0 100644
--- a/utils/v4l2-ctl/v4l2-ctl.h
+++ b/utils/v4l2-ctl/v4l2-ctl.h
@@ -197,6 +197,8 @@ enum Option {
OptInfoEdid,
OptShowEdid,
OptFixEdidChecksums,
+ OptSetRouting,
+ OptGetRouting,
OptFreqSeek,
OptEncoderCmd,
OptTryEncoderCmd,
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v1 3/4] media-ctl: add support for routes and streams
2021-11-30 14:18 [PATCH v1 0/4] v4l-utils: support multiplexed streams Tomi Valkeinen
2021-11-30 14:18 ` [PATCH v1 1/4] v4l2-utils: update Linux headers for " Tomi Valkeinen
2021-11-30 14:18 ` [PATCH v1 2/4] v4l2-ctl: Add routing and streams support Tomi Valkeinen
@ 2021-11-30 14:18 ` Tomi Valkeinen
2022-03-21 10:37 ` Pratyush Yadav
2022-03-21 10:45 ` Laurent Pinchart
2021-11-30 14:18 ` [PATCH v1 4/4] v4l2-ctl/compliance: add routing and streams multiplexed streams Tomi Valkeinen
2021-12-01 3:46 ` [PATCH v1 0/4] v4l-utils: support " Laurent Pinchart
4 siblings, 2 replies; 12+ messages in thread
From: Tomi Valkeinen @ 2021-11-30 14:18 UTC (permalink / raw)
To: linux-media, sakari.ailus, Jacopo Mondi, Laurent Pinchart,
niklas.soderlund+renesas, Mauro Carvalho Chehab, Hans Verkuil,
Pratyush Yadav
Cc: Tomi Valkeinen
Add support to get and set subdev routes and to get and set
configurations per stream.
Based on work from Sakari Ailus <sakari.ailus@linux.intel.com>.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
utils/media-ctl/libmediactl.c | 41 +++++
utils/media-ctl/libv4l2subdev.c | 256 ++++++++++++++++++++++++++++----
utils/media-ctl/media-ctl.c | 113 +++++++++++---
utils/media-ctl/mediactl.h | 16 ++
utils/media-ctl/options.c | 15 +-
utils/media-ctl/options.h | 1 +
utils/media-ctl/v4l2subdev.h | 58 +++++++-
7 files changed, 444 insertions(+), 56 deletions(-)
diff --git a/utils/media-ctl/libmediactl.c b/utils/media-ctl/libmediactl.c
index 1fd6525b..537365d0 100644
--- a/utils/media-ctl/libmediactl.c
+++ b/utils/media-ctl/libmediactl.c
@@ -876,6 +876,47 @@ struct media_pad *media_parse_pad(struct media_device *media,
return &entity->pads[pad];
}
+struct media_pad *media_parse_pad_stream(struct media_device *media,
+ const char *p, unsigned int *stream,
+ char **endp)
+{
+ struct media_pad *pad;
+ const char *orig_p = p;
+ char *ep;
+
+ pad = media_parse_pad(media, p, &ep);
+ if (pad == NULL)
+ return NULL;
+
+ p = ep;
+
+ if (*p == '/') {
+ unsigned int s;
+
+ p++;
+
+ s = strtoul(p, &ep, 10);
+
+ if (ep == p) {
+ printf("Unable to parse stream: '%s'\n", orig_p);
+ if (endp)
+ *endp = (char*)p;
+ return NULL;
+ }
+
+ *stream = s;
+
+ p++;
+ } else {
+ *stream = 0;
+ }
+
+ if (endp)
+ *endp = (char*)p;
+
+ return pad;
+}
+
struct media_link *media_parse_link(struct media_device *media,
const char *p, char **endp)
{
diff --git a/utils/media-ctl/libv4l2subdev.c b/utils/media-ctl/libv4l2subdev.c
index 0d0afbe7..eb9e1cc4 100644
--- a/utils/media-ctl/libv4l2subdev.c
+++ b/utils/media-ctl/libv4l2subdev.c
@@ -63,7 +63,7 @@ void v4l2_subdev_close(struct media_entity *entity)
}
int v4l2_subdev_get_format(struct media_entity *entity,
- struct v4l2_mbus_framefmt *format, unsigned int pad,
+ struct v4l2_mbus_framefmt *format, unsigned int pad, unsigned int stream,
enum v4l2_subdev_format_whence which)
{
struct v4l2_subdev_format fmt;
@@ -75,6 +75,7 @@ int v4l2_subdev_get_format(struct media_entity *entity,
memset(&fmt, 0, sizeof(fmt));
fmt.pad = pad;
+ fmt.stream = stream;
fmt.which = which;
ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_FMT, &fmt);
@@ -87,6 +88,7 @@ int v4l2_subdev_get_format(struct media_entity *entity,
int v4l2_subdev_set_format(struct media_entity *entity,
struct v4l2_mbus_framefmt *format, unsigned int pad,
+ unsigned int stream,
enum v4l2_subdev_format_whence which)
{
struct v4l2_subdev_format fmt;
@@ -98,6 +100,7 @@ int v4l2_subdev_set_format(struct media_entity *entity,
memset(&fmt, 0, sizeof(fmt));
fmt.pad = pad;
+ fmt.stream = stream;
fmt.which = which;
fmt.format = *format;
@@ -110,8 +113,8 @@ int v4l2_subdev_set_format(struct media_entity *entity,
}
int v4l2_subdev_get_selection(struct media_entity *entity,
- struct v4l2_rect *rect, unsigned int pad, unsigned int target,
- enum v4l2_subdev_format_whence which)
+ struct v4l2_rect *rect, unsigned int pad, unsigned int stream,
+ unsigned int target, enum v4l2_subdev_format_whence which)
{
union {
struct v4l2_subdev_selection sel;
@@ -149,8 +152,8 @@ int v4l2_subdev_get_selection(struct media_entity *entity,
}
int v4l2_subdev_set_selection(struct media_entity *entity,
- struct v4l2_rect *rect, unsigned int pad, unsigned int target,
- enum v4l2_subdev_format_whence which)
+ struct v4l2_rect *rect, unsigned int pad, unsigned int stream,
+ unsigned int target, enum v4l2_subdev_format_whence which)
{
union {
struct v4l2_subdev_selection sel;
@@ -164,6 +167,7 @@ int v4l2_subdev_set_selection(struct media_entity *entity,
memset(&u.sel, 0, sizeof(u.sel));
u.sel.pad = pad;
+ u.sel.stream = stream;
u.sel.target = target;
u.sel.which = which;
u.sel.r = *rect;
@@ -178,6 +182,7 @@ int v4l2_subdev_set_selection(struct media_entity *entity,
memset(&u.crop, 0, sizeof(u.crop));
u.crop.pad = pad;
+ u.crop.stream = stream;
u.crop.which = which;
u.crop.rect = *rect;
@@ -189,6 +194,63 @@ int v4l2_subdev_set_selection(struct media_entity *entity,
return 0;
}
+int v4l2_subdev_set_routing(struct media_entity *entity,
+ struct v4l2_subdev_route *routes,
+ unsigned int num_routes)
+{
+ struct v4l2_subdev_routing routing = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .routes = (uintptr_t)routes,
+ .num_routes = num_routes,
+ };
+ int ret;
+
+ ret = v4l2_subdev_open(entity);
+ if (ret < 0)
+ return ret;
+
+ ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_ROUTING, &routing);
+ if (ret == -1)
+ return -errno;
+
+ return 0;
+}
+
+int v4l2_subdev_get_routing(struct media_entity *entity,
+ struct v4l2_subdev_route **routes,
+ unsigned int *num_routes)
+{
+ struct v4l2_subdev_routing routing = { 0 };
+ struct v4l2_subdev_route *r;
+ int ret;
+
+ ret = v4l2_subdev_open(entity);
+ if (ret < 0)
+ return ret;
+
+ routing.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+
+ ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_ROUTING, &routing);
+ if (ret == -1 && errno != ENOSPC)
+ return -errno;
+
+ r = calloc(routing.num_routes, sizeof(*r));
+ if (!r)
+ return -ENOMEM;
+
+ routing.routes = (uintptr_t)r;
+ ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_ROUTING, &routing);
+ if (ret) {
+ free(r);
+ return ret;
+ }
+
+ *num_routes = routing.num_routes;
+ *routes = r;
+
+ return 0;
+}
+
int v4l2_subdev_get_dv_timings_caps(struct media_entity *entity,
struct v4l2_dv_timings_cap *caps)
{
@@ -263,7 +325,7 @@ int v4l2_subdev_set_dv_timings(struct media_entity *entity,
int v4l2_subdev_get_frame_interval(struct media_entity *entity,
struct v4l2_fract *interval,
- unsigned int pad)
+ unsigned int pad, unsigned int stream)
{
struct v4l2_subdev_frame_interval ival;
int ret;
@@ -274,6 +336,7 @@ int v4l2_subdev_get_frame_interval(struct media_entity *entity,
memset(&ival, 0, sizeof(ival));
ival.pad = pad;
+ ival.stream = stream;
ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &ival);
if (ret < 0)
@@ -285,7 +348,7 @@ int v4l2_subdev_get_frame_interval(struct media_entity *entity,
int v4l2_subdev_set_frame_interval(struct media_entity *entity,
struct v4l2_fract *interval,
- unsigned int pad)
+ unsigned int pad, unsigned int stream)
{
struct v4l2_subdev_frame_interval ival;
int ret;
@@ -296,6 +359,7 @@ int v4l2_subdev_set_frame_interval(struct media_entity *entity,
memset(&ival, 0, sizeof(ival));
ival.pad = pad;
+ ival.stream = stream;
ival.interval = *interval;
ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &ival);
@@ -306,6 +370,135 @@ int v4l2_subdev_set_frame_interval(struct media_entity *entity,
return 0;
}
+int v4l2_subdev_parse_setup_routes(struct media_device *media, const char *p)
+{
+ struct media_entity *entity;
+ struct v4l2_subdev_route *routes;
+ unsigned int num_routes;
+ char *end;
+ int ret;
+ int i;
+
+ entity = media_parse_entity(media, p, &end);
+ if (!entity)
+ return -EINVAL;
+
+ p = end;
+
+ if (*p != '[') {
+ media_dbg(media, "Expected '['\n");
+ return -EINVAL;
+ }
+
+ p++;
+
+ routes = calloc(256, sizeof(routes[0]));
+ num_routes = 0;
+
+ while (*p != 0) {
+ struct v4l2_subdev_route *r = &routes[num_routes];
+
+ /* sink pad/stream */
+
+ r->sink_pad = strtoul(p, &end, 10);
+
+ if (*end != '/') {
+ media_dbg(media, "Expected '/'\n");
+ return -EINVAL;
+ }
+
+ p = end + 1;
+
+ r->sink_stream = strtoul(p, &end, 10);
+
+ for (; isspace(*end); ++end);
+
+ if (end[0] != '-' || end[1] != '>') {
+ media_dbg(media, "Expected '->'\n");
+ return -EINVAL;
+ }
+ p = end + 2;
+
+ /* source pad/stream */
+
+ r->source_pad = strtoul(p, &end, 10);
+
+ if (*end != '/') {
+ media_dbg(media, "Expected '/'\n");
+ return -EINVAL;
+ }
+
+ p = end + 1;
+
+ r->source_stream = strtoul(p, &end, 10);
+
+ /* flags */
+
+ for (; isspace(*end); ++end);
+
+ if (*end != '[') {
+ media_dbg(media, "Expected '['\n");
+ return -EINVAL;
+ }
+
+ for (end++; isspace(*end); ++end);
+
+ p = end;
+
+ r->flags = strtoul(p, &end, 0);
+
+ if (r->flags & ~(V4L2_SUBDEV_ROUTE_FL_ACTIVE |
+ V4L2_SUBDEV_ROUTE_FL_IMMUTABLE |
+ V4L2_SUBDEV_ROUTE_FL_SOURCE)) {
+ media_dbg(media, "Bad route flags %#x\n", r->flags);
+ return -EINVAL;
+ }
+
+ for (; isspace(*end); ++end);
+
+ if (*end != ']') {
+ media_dbg(media, "Expected ']'\n");
+ return -EINVAL;
+ }
+ end++;
+
+ p = end;
+
+ num_routes++;
+
+ if (*p == ',') {
+ p++;
+ continue;
+ }
+
+ break;
+ }
+
+ if (*p != ']') {
+ media_dbg(media, "Expected ']'\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num_routes; ++i) {
+ struct v4l2_subdev_route *r = &routes[i];
+
+ media_dbg(entity->media,
+ "Setting up route %s : %u/%u -> %u/%u, flags 0x%8.8x\n",
+ entity->info.name,
+ r->sink_pad, r->sink_stream,
+ r->source_pad, r->source_stream,
+ r->flags);
+ }
+
+ ret = v4l2_subdev_set_routing(entity, routes, num_routes);
+ if (ret) {
+ printf("VIDIOC_SUBDEV_S_ROUTING failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static int v4l2_subdev_parse_format(struct media_device *media,
struct v4l2_mbus_framefmt *format,
const char *p, char **endp)
@@ -441,7 +634,8 @@ static bool strhazit(const char *str, const char **p)
}
static struct media_pad *v4l2_subdev_parse_pad_format(
- struct media_device *media, struct v4l2_mbus_framefmt *format,
+ struct media_device *media, unsigned int *stream,
+ struct v4l2_mbus_framefmt *format,
struct v4l2_rect *crop, struct v4l2_rect *compose,
struct v4l2_fract *interval, const char *p, char **endp)
{
@@ -452,7 +646,7 @@ static struct media_pad *v4l2_subdev_parse_pad_format(
for (; isspace(*p); ++p);
- pad = media_parse_pad(media, p, &end);
+ pad = media_parse_pad_stream(media, p, stream, &end);
if (pad == NULL) {
*endp = end;
return NULL;
@@ -674,6 +868,7 @@ static struct media_pad *v4l2_subdev_parse_pad_format(
}
static int set_format(struct media_pad *pad,
+ unsigned int stream,
struct v4l2_mbus_framefmt *format)
{
int ret;
@@ -682,12 +877,12 @@ static int set_format(struct media_pad *pad,
return 0;
media_dbg(pad->entity->media,
- "Setting up format %s %ux%u on pad %s/%u\n",
+ "Setting up format %s %ux%u on pad %s/%u/%u\n",
v4l2_subdev_pixelcode_to_string(format->code),
format->width, format->height,
- pad->entity->info.name, pad->index);
+ pad->entity->info.name, pad->index, stream);
- ret = v4l2_subdev_set_format(pad->entity, format, pad->index,
+ ret = v4l2_subdev_set_format(pad->entity, format, pad->index, stream,
V4L2_SUBDEV_FORMAT_ACTIVE);
if (ret < 0) {
media_dbg(pad->entity->media,
@@ -704,8 +899,8 @@ static int set_format(struct media_pad *pad,
return 0;
}
-static int set_selection(struct media_pad *pad, unsigned int target,
- struct v4l2_rect *rect)
+static int set_selection(struct media_pad *pad, unsigned int stream,
+ unsigned int target, struct v4l2_rect *rect)
{
int ret;
@@ -713,11 +908,11 @@ static int set_selection(struct media_pad *pad, unsigned int target,
return 0;
media_dbg(pad->entity->media,
- "Setting up selection target %u rectangle (%u,%u)/%ux%u on pad %s/%u\n",
+ "Setting up selection target %u rectangle (%u,%u)/%ux%u on pad %s/%u/%u\n",
target, rect->left, rect->top, rect->width, rect->height,
- pad->entity->info.name, pad->index);
+ pad->entity->info.name, pad->index, stream);
- ret = v4l2_subdev_set_selection(pad->entity, rect, pad->index,
+ ret = v4l2_subdev_set_selection(pad->entity, rect, pad->index, stream,
target, V4L2_SUBDEV_FORMAT_ACTIVE);
if (ret < 0) {
media_dbg(pad->entity->media,
@@ -733,7 +928,7 @@ static int set_selection(struct media_pad *pad, unsigned int target,
return 0;
}
-static int set_frame_interval(struct media_pad *pad,
+static int set_frame_interval(struct media_pad *pad, unsigned int stream,
struct v4l2_fract *interval)
{
int ret;
@@ -742,11 +937,12 @@ static int set_frame_interval(struct media_pad *pad,
return 0;
media_dbg(pad->entity->media,
- "Setting up frame interval %u/%u on pad %s/%u\n",
+ "Setting up frame interval %u/%u on pad %s/%u/%u\n",
interval->numerator, interval->denominator,
- pad->entity->info.name, pad->index);
+ pad->entity->info.name, pad->index, stream);
- ret = v4l2_subdev_set_frame_interval(pad->entity, interval, pad->index);
+ ret = v4l2_subdev_set_frame_interval(pad->entity, interval, pad->index,
+ stream);
if (ret < 0) {
media_dbg(pad->entity->media,
"Unable to set frame interval: %s (%d)",
@@ -769,11 +965,13 @@ static int v4l2_subdev_parse_setup_format(struct media_device *media,
struct v4l2_rect crop = { -1, -1, -1, -1 };
struct v4l2_rect compose = crop;
struct v4l2_fract interval = { 0, 0 };
+ unsigned int stream;
unsigned int i;
char *end;
int ret;
- pad = v4l2_subdev_parse_pad_format(media, &format, &crop, &compose,
+ pad = v4l2_subdev_parse_pad_format(media, &stream,
+ &format, &crop, &compose,
&interval, p, &end);
if (pad == NULL) {
media_print_streampos(media, p, end);
@@ -782,26 +980,26 @@ static int v4l2_subdev_parse_setup_format(struct media_device *media,
}
if (pad->flags & MEDIA_PAD_FL_SINK) {
- ret = set_format(pad, &format);
+ ret = set_format(pad, stream, &format);
if (ret < 0)
return ret;
}
- ret = set_selection(pad, V4L2_SEL_TGT_CROP, &crop);
+ ret = set_selection(pad, stream, V4L2_SEL_TGT_CROP, &crop);
if (ret < 0)
return ret;
- ret = set_selection(pad, V4L2_SEL_TGT_COMPOSE, &compose);
+ ret = set_selection(pad, stream, V4L2_SEL_TGT_COMPOSE, &compose);
if (ret < 0)
return ret;
if (pad->flags & MEDIA_PAD_FL_SOURCE) {
- ret = set_format(pad, &format);
+ ret = set_format(pad, stream, &format);
if (ret < 0)
return ret;
}
- ret = set_frame_interval(pad, &interval);
+ ret = set_frame_interval(pad, stream, &interval);
if (ret < 0)
return ret;
@@ -820,9 +1018,9 @@ static int v4l2_subdev_parse_setup_format(struct media_device *media,
if (link->source == pad &&
link->sink->entity->info.type == MEDIA_ENT_T_V4L2_SUBDEV) {
remote_format = format;
- set_format(link->sink, &remote_format);
+ set_format(link->sink, stream, &remote_format);
- ret = set_frame_interval(link->sink, &interval);
+ ret = set_frame_interval(link->sink, stream, &interval);
if (ret < 0 && ret != -EINVAL && ret != -ENOTTY)
return ret;
}
diff --git a/utils/media-ctl/media-ctl.c b/utils/media-ctl/media-ctl.c
index 84ee7a83..cff08428 100644
--- a/utils/media-ctl/media-ctl.c
+++ b/utils/media-ctl/media-ctl.c
@@ -75,23 +75,45 @@ static void print_flags(const struct flag_name *flag_names, unsigned int num_ent
}
}
+static void v4l2_subdev_print_routes(struct media_entity *entity,
+ struct v4l2_subdev_route *routes,
+ unsigned int num_routes)
+{
+ unsigned int i;
+
+ for (i = 0; i < num_routes; i++) {
+ const struct v4l2_subdev_route *r = &routes[i];
+
+ if (i == 0)
+ printf("\troutes:\n");
+
+ printf("\t\t%u/%u -> %u/%u [%s%s%s]\n",
+ r->sink_pad, r->sink_stream,
+ r->source_pad, r->source_stream,
+ r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE ? "ACTIVE" : "INACTIVE",
+ r->flags & V4L2_SUBDEV_ROUTE_FL_IMMUTABLE ? ", IMMUTABLE" : "",
+ r->flags & V4L2_SUBDEV_ROUTE_FL_SOURCE ? ", SOURCE" : "");
+ }
+}
+
static void v4l2_subdev_print_format(struct media_entity *entity,
- unsigned int pad, enum v4l2_subdev_format_whence which)
+ unsigned int pad, unsigned int stream,
+ enum v4l2_subdev_format_whence which)
{
struct v4l2_mbus_framefmt format;
struct v4l2_fract interval = { 0, 0 };
struct v4l2_rect rect;
int ret;
- ret = v4l2_subdev_get_format(entity, &format, pad, which);
+ ret = v4l2_subdev_get_format(entity, &format, pad, stream, which);
if (ret != 0)
return;
- ret = v4l2_subdev_get_frame_interval(entity, &interval, pad);
+ ret = v4l2_subdev_get_frame_interval(entity, &interval, pad, stream);
if (ret != 0 && ret != -ENOTTY && ret != -EINVAL)
return;
- printf("\t\t[fmt:%s/%ux%u",
+ printf("\t\t[stream:%u fmt:%s/%ux%u", stream,
v4l2_subdev_pixelcode_to_string(format.code),
format.width, format.height);
@@ -118,28 +140,28 @@ static void v4l2_subdev_print_format(struct media_entity *entity,
v4l2_subdev_quantization_to_string(format.quantization));
}
- ret = v4l2_subdev_get_selection(entity, &rect, pad,
+ ret = v4l2_subdev_get_selection(entity, &rect, pad, stream,
V4L2_SEL_TGT_CROP_BOUNDS,
which);
if (ret == 0)
printf("\n\t\t crop.bounds:(%u,%u)/%ux%u", rect.left, rect.top,
rect.width, rect.height);
- ret = v4l2_subdev_get_selection(entity, &rect, pad,
+ ret = v4l2_subdev_get_selection(entity, &rect, pad, stream,
V4L2_SEL_TGT_CROP,
which);
if (ret == 0)
printf("\n\t\t crop:(%u,%u)/%ux%u", rect.left, rect.top,
rect.width, rect.height);
- ret = v4l2_subdev_get_selection(entity, &rect, pad,
+ ret = v4l2_subdev_get_selection(entity, &rect, pad, stream,
V4L2_SEL_TGT_COMPOSE_BOUNDS,
which);
if (ret == 0)
printf("\n\t\t compose.bounds:(%u,%u)/%ux%u",
rect.left, rect.top, rect.width, rect.height);
- ret = v4l2_subdev_get_selection(entity, &rect, pad,
+ ret = v4l2_subdev_get_selection(entity, &rect, pad, stream,
V4L2_SEL_TGT_COMPOSE,
which);
if (ret == 0)
@@ -455,16 +477,51 @@ static void media_print_topology_dot(struct media_device *media)
}
static void media_print_pad_text(struct media_entity *entity,
- const struct media_pad *pad)
+ const struct media_pad *pad,
+ struct v4l2_subdev_route *routes,
+ unsigned int num_routes)
{
+ unsigned int i;
+
if (media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV)
return;
- v4l2_subdev_print_format(entity, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE);
- v4l2_subdev_print_pad_dv(entity, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE);
+ if (!routes) {
+ v4l2_subdev_print_format(entity, pad->index, 0, V4L2_SUBDEV_FORMAT_ACTIVE);
+ v4l2_subdev_print_pad_dv(entity, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE);
+
+ if (pad->flags & MEDIA_PAD_FL_SOURCE)
+ v4l2_subdev_print_subdev_dv(entity);
+
+ return;
+ }
+
+ for (i = 0; i < num_routes; ++i) {
+ const struct v4l2_subdev_route *r = &routes[i];
+ unsigned int stream;
+
+ if (!(r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE))
+ continue;
- if (pad->flags & MEDIA_PAD_FL_SOURCE)
- v4l2_subdev_print_subdev_dv(entity);
+ if (pad->flags & MEDIA_PAD_FL_SINK) {
+ if (r->sink_pad != pad->index)
+ continue;
+
+ stream = r->sink_stream;
+ } else {
+ if (r->source_pad != pad->index)
+ continue;
+
+ stream = r->source_stream;
+ }
+
+ v4l2_subdev_print_format(entity, pad->index, stream, V4L2_SUBDEV_FORMAT_ACTIVE);
+ v4l2_subdev_print_pad_dv(entity, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE);
+
+ if (pad->flags & MEDIA_PAD_FL_SOURCE)
+ v4l2_subdev_print_subdev_dv(entity);
+
+ }
}
static void media_print_topology_text_entity(struct media_device *media,
@@ -480,11 +537,17 @@ static void media_print_topology_text_entity(struct media_device *media,
unsigned int num_links = media_entity_get_links_count(entity);
unsigned int j, k;
unsigned int padding;
+ struct v4l2_subdev_route *routes = NULL;
+ unsigned int num_routes = 0;
+
+ if (media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV)
+ v4l2_subdev_get_routing(entity, &routes, &num_routes);
padding = printf("- entity %u: ", info->id);
- printf("%s (%u pad%s, %u link%s)\n", info->name,
+ printf("%s (%u pad%s, %u link%s, %u route%s)\n", info->name,
info->pads, info->pads > 1 ? "s" : "",
- num_links, num_links > 1 ? "s" : "");
+ num_links, num_links > 1 ? "s" : "",
+ num_routes, num_routes > 1 ? "s" : "");
printf("%*ctype %s subtype %s flags %x\n", padding, ' ',
media_entity_type_to_string(info->type),
media_entity_subtype_to_string(info->type),
@@ -492,12 +555,15 @@ static void media_print_topology_text_entity(struct media_device *media,
if (devname)
printf("%*cdevice node name %s\n", padding, ' ', devname);
+ if (media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV)
+ v4l2_subdev_print_routes(entity, routes, num_routes);
+
for (j = 0; j < info->pads; j++) {
const struct media_pad *pad = media_entity_get_pad(entity, j);
printf("\tpad%u: %s\n", j, media_pad_type_to_string(pad->flags));
- media_print_pad_text(entity, pad);
+ media_print_pad_text(entity, pad, routes, num_routes);
for (k = 0; k < num_links; k++) {
const struct media_link *link = media_entity_get_link(entity, k);
@@ -594,14 +660,16 @@ int main(int argc, char **argv)
if (media_opts.fmt_pad) {
struct media_pad *pad;
+ unsigned int stream;
+ char *p;
- pad = media_parse_pad(media, media_opts.fmt_pad, NULL);
+ pad = media_parse_pad_stream(media, media_opts.fmt_pad, &stream, &p);
if (pad == NULL) {
printf("Pad '%s' not found\n", media_opts.fmt_pad);
goto out;
}
- v4l2_subdev_print_format(pad->entity, pad->index,
+ v4l2_subdev_print_format(pad->entity, pad->index, stream,
V4L2_SUBDEV_FORMAT_ACTIVE);
}
@@ -685,6 +753,15 @@ int main(int argc, char **argv)
}
}
+ if (media_opts.routes) {
+ ret = v4l2_subdev_parse_setup_routes(media, media_opts.routes);
+ if (ret) {
+ printf("Unable to setup routes: %s (%d)\n",
+ strerror(-ret), -ret);
+ goto out;
+ }
+ }
+
if (media_opts.interactive) {
while (1) {
char buffer[32];
diff --git a/utils/media-ctl/mediactl.h b/utils/media-ctl/mediactl.h
index af360518..c0fc2962 100644
--- a/utils/media-ctl/mediactl.h
+++ b/utils/media-ctl/mediactl.h
@@ -394,6 +394,22 @@ struct media_entity *media_parse_entity(struct media_device *media,
struct media_pad *media_parse_pad(struct media_device *media,
const char *p, char **endp);
+/**
+ * @brief Parse string to a pad and stream on the media device.
+ * @param media - media device.
+ * @param p - input string
+ * @param stream - pointer to uint where the stream number is stored
+ * @param endp - pointer to string where parsing ended
+ *
+ * Parse NULL terminated string describing a pad and stream and return its struct
+ * media_pad instance and the stream number.
+ *
+ * @return Pointer to struct media_pad on success, NULL on failure.
+ */
+struct media_pad *media_parse_pad_stream(struct media_device *media,
+ const char *p, unsigned int *stream,
+ char **endp);
+
/**
* @brief Parse string to a link on the media device.
* @param media - media device.
diff --git a/utils/media-ctl/options.c b/utils/media-ctl/options.c
index 6d30d3dc..58ddec3c 100644
--- a/utils/media-ctl/options.c
+++ b/utils/media-ctl/options.c
@@ -63,6 +63,7 @@ static void usage(const char *argv0)
printf(" --get-v4l2 pad Print the active format on a given pad\n");
printf(" --get-dv pad Print detected and current DV timings on a given pad\n");
printf(" --set-dv pad Configure DV timings on a given pad\n");
+ printf("-R, --set-routes routes Configure routes on a given subdev entity\n");
printf("-h, --help Show verbose help and exit\n");
printf("-i, --interactive Modify links interactively\n");
printf("-l, --links links Comma-separated list of link descriptors to setup\n");
@@ -78,7 +79,7 @@ static void usage(const char *argv0)
printf("Links and formats are defined as\n");
printf("\tlinks = link { ',' link } ;\n");
printf("\tlink = pad '->' pad '[' flags ']' ;\n");
- printf("\tpad = entity ':' pad-number ;\n");
+ printf("\tpad = entity ':' pad-number { '/' stream-number } ;\n");
printf("\tentity = entity-number | ( '\"' entity-name '\"' ) ;\n");
printf("\n");
printf("\tv4l2 = pad '[' v4l2-properties ']' ;\n");
@@ -95,11 +96,16 @@ static void usage(const char *argv0)
printf("\trectangle = '(' left ',' top, ')' '/' size ;\n");
printf("\tsize = width 'x' height ;\n");
printf("\n");
+ printf("\troutes = entity '[' route { ',' route } ']' ;\n");
+ printf("\troute = pad-number '/' stream-number '->' pad-number '/' stream-number '[' route-flags ']' ;\n");
+ printf("\n");
printf("where the fields are\n");
printf("\tentity-number Entity numeric identifier\n");
printf("\tentity-name Entity name (string) \n");
printf("\tpad-number Pad numeric identifier\n");
+ printf("\tstream-number Stream numeric identifier\n");
printf("\tflags Link flags (0: inactive, 1: active)\n");
+ printf("\troute-flags Route flags (bitmask of route flags: active - 0x1, immutable - 0x2, source - 0x4)\n");
printf("\tfcc Format FourCC\n");
printf("\twidth Image width in pixels\n");
printf("\theight Image height in pixels\n");
@@ -152,6 +158,7 @@ static struct option opts[] = {
{"get-v4l2", 1, 0, OPT_GET_FORMAT},
{"get-dv", 1, 0, OPT_GET_DV},
{"set-dv", 1, 0, OPT_SET_DV},
+ {"set-routes", 1, 0, 'R'},
{"help", 0, 0, 'h'},
{"interactive", 0, 0, 'i'},
{"links", 1, 0, 'l'},
@@ -237,7 +244,7 @@ int parse_cmdline(int argc, char **argv)
}
/* parse options */
- while ((opt = getopt_long(argc, argv, "d:e:f:hil:prvV:",
+ while ((opt = getopt_long(argc, argv, "d:e:f:hil:prvV:R:",
opts, NULL)) != -1) {
switch (opt) {
case 'd':
@@ -283,6 +290,10 @@ int parse_cmdline(int argc, char **argv)
media_opts.verbose = 1;
break;
+ case 'R':
+ media_opts.routes = optarg;
+ break;
+
case OPT_PRINT_DOT:
media_opts.print_dot = 1;
break;
diff --git a/utils/media-ctl/options.h b/utils/media-ctl/options.h
index b1751f56..8796f1b6 100644
--- a/utils/media-ctl/options.h
+++ b/utils/media-ctl/options.h
@@ -38,6 +38,7 @@ struct media_options
const char *fmt_pad;
const char *get_dv_pad;
const char *dv_pad;
+ const char *routes;
};
extern struct media_options media_opts;
diff --git a/utils/media-ctl/v4l2subdev.h b/utils/media-ctl/v4l2subdev.h
index a1813911..a8a6e7ad 100644
--- a/utils/media-ctl/v4l2subdev.h
+++ b/utils/media-ctl/v4l2subdev.h
@@ -64,7 +64,7 @@ void v4l2_subdev_close(struct media_entity *entity);
* @return 0 on success, or a negative error code on failure.
*/
int v4l2_subdev_get_format(struct media_entity *entity,
- struct v4l2_mbus_framefmt *format, unsigned int pad,
+ struct v4l2_mbus_framefmt *format, unsigned int pad, unsigned int stream,
enum v4l2_subdev_format_whence which);
/**
@@ -86,6 +86,7 @@ int v4l2_subdev_get_format(struct media_entity *entity,
*/
int v4l2_subdev_set_format(struct media_entity *entity,
struct v4l2_mbus_framefmt *format, unsigned int pad,
+ unsigned int stream,
enum v4l2_subdev_format_whence which);
/**
@@ -107,8 +108,8 @@ int v4l2_subdev_set_format(struct media_entity *entity,
* @return 0 on success, or a negative error code on failure.
*/
int v4l2_subdev_get_selection(struct media_entity *entity,
- struct v4l2_rect *rect, unsigned int pad, unsigned int target,
- enum v4l2_subdev_format_whence which);
+ struct v4l2_rect *rect, unsigned int pad, unsigned int stream,
+ unsigned int target, enum v4l2_subdev_format_whence which);
/**
* @brief Set a selection rectangle on a pad.
@@ -129,8 +130,40 @@ int v4l2_subdev_get_selection(struct media_entity *entity,
* @return 0 on success, or a negative error code on failure.
*/
int v4l2_subdev_set_selection(struct media_entity *entity,
- struct v4l2_rect *rect, unsigned int pad, unsigned int target,
- enum v4l2_subdev_format_whence which);
+ struct v4l2_rect *rect, unsigned int pad, unsigned int stream,
+ unsigned int target, enum v4l2_subdev_format_whence which);
+
+/**
+ * @brief Get the routing table of a subdev media entity.
+ * @param entity - subdev-device media entity.
+ * @param routes - routes of the subdev.
+ * @param num_routes - number of routes.
+ *
+ * Get the routes of @a entity and return them in an allocated array in @a routes
+ * and the number of routes in @a num_routes.
+ *
+ * The caller is responsible for freeing the routes array after use.
+ *
+ * @return 0 on success, or a negative error code on failure.
+ */
+int v4l2_subdev_get_routing(struct media_entity *entity,
+ struct v4l2_subdev_route **routes,
+ unsigned int *num_routes);
+
+/**
+ * @brief Set the routing table of a subdev media entity.
+ * @param entity - subdev-device media entity.
+ * @param routes - routes of the subdev.
+ * @param num_routes - number of routes.
+ *
+ * Set the routes of @a entity. The routes are given in @a routes with the
+ * length of @a num_routes.
+ *
+ * @return 0 on success, or a negative error code on failure.
+ */
+int v4l2_subdev_set_routing(struct media_entity *entity,
+ struct v4l2_subdev_route *route,
+ unsigned int num_routes);
/**
* @brief Query the digital video capabilities of a pad.
@@ -200,7 +233,7 @@ int v4l2_subdev_set_dv_timings(struct media_entity *entity,
*/
int v4l2_subdev_get_frame_interval(struct media_entity *entity,
- struct v4l2_fract *interval, unsigned int pad);
+ struct v4l2_fract *interval, unsigned int pad, unsigned int stream);
/**
* @brief Set the frame interval on a sub-device.
@@ -217,7 +250,7 @@ int v4l2_subdev_get_frame_interval(struct media_entity *entity,
* @return 0 on success, or a negative error code on failure.
*/
int v4l2_subdev_set_frame_interval(struct media_entity *entity,
- struct v4l2_fract *interval, unsigned int pad);
+ struct v4l2_fract *interval, unsigned int pad, unsigned int stream);
/**
* @brief Parse a string and apply format, crop and frame interval settings.
@@ -235,6 +268,17 @@ int v4l2_subdev_set_frame_interval(struct media_entity *entity,
*/
int v4l2_subdev_parse_setup_formats(struct media_device *media, const char *p);
+/**
+ * @brief Parse a string and apply route settings.
+ * @param media - media device.
+ * @param p - input string
+ *
+ * Parse string @a p and apply route settings to a subdev.
+ *
+ * @return 0 on success, or a negative error code on failure.
+ */
+int v4l2_subdev_parse_setup_routes(struct media_device *media, const char *p);
+
/**
* @brief Convert media bus pixel code to string.
* @param code - input string
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v1 3/4] media-ctl: add support for routes and streams
2021-11-30 14:18 ` [PATCH v1 3/4] media-ctl: add support for routes and streams Tomi Valkeinen
@ 2022-03-21 10:37 ` Pratyush Yadav
2022-03-21 10:45 ` Laurent Pinchart
1 sibling, 0 replies; 12+ messages in thread
From: Pratyush Yadav @ 2022-03-21 10:37 UTC (permalink / raw)
To: Tomi Valkeinen
Cc: linux-media, sakari.ailus, Jacopo Mondi, Laurent Pinchart,
niklas.soderlund+renesas, Mauro Carvalho Chehab, Hans Verkuil
Hi Tomi,
On 30/11/21 04:18PM, Tomi Valkeinen wrote:
> Add support to get and set subdev routes and to get and set
> configurations per stream.
>
> Based on work from Sakari Ailus <sakari.ailus@linux.intel.com>.
>
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
I tried this on TI's downstream fork with your routing patches. It works
fine for setting and viewing formats and routes.
Tested-by: Pratyush Yadav <p.yadav@ti.com>
I have not gone through the code too thoroughly. A few small things I
noticed below.
> ---
> utils/media-ctl/libmediactl.c | 41 +++++
> utils/media-ctl/libv4l2subdev.c | 256 ++++++++++++++++++++++++++++----
> utils/media-ctl/media-ctl.c | 113 +++++++++++---
> utils/media-ctl/mediactl.h | 16 ++
> utils/media-ctl/options.c | 15 +-
> utils/media-ctl/options.h | 1 +
> utils/media-ctl/v4l2subdev.h | 58 +++++++-
> 7 files changed, 444 insertions(+), 56 deletions(-)
>
[...]
> +int v4l2_subdev_get_routing(struct media_entity *entity,
> + struct v4l2_subdev_route **routes,
> + unsigned int *num_routes)
> +{
> + struct v4l2_subdev_routing routing = { 0 };
> + struct v4l2_subdev_route *r;
> + int ret;
> +
> + ret = v4l2_subdev_open(entity);
> + if (ret < 0)
> + return ret;
> +
> + routing.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +
> + ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_ROUTING, &routing);
> + if (ret == -1 && errno != ENOSPC)
> + return -errno;
> +
> + r = calloc(routing.num_routes, sizeof(*r));
> + if (!r)
calloc man page says:
If nmemb or size is 0, then calloc() returns either NULL, or a unique
pointer value that can later be successfully passed to free().
So if a subdev reports 0 routes then you could end up with a non-NULL
pointer that you should not use, other than to pass to free(). I don't
know if any implementation out there does actually return anything other
than NULL though. I suggest explicitly checking for num_routes == 0 to
avoid all this.
> + return -ENOMEM;
> +
> + routing.routes = (uintptr_t)r;
> + ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_ROUTING, &routing);
> + if (ret) {
> + free(r);
> + return ret;
> + }
> +
> + *num_routes = routing.num_routes;
> + *routes = r;
> +
> + return 0;
> +}
> +
> int v4l2_subdev_get_dv_timings_caps(struct media_entity *entity,
> struct v4l2_dv_timings_cap *caps)
> {
[...]
> @@ -306,6 +370,135 @@ int v4l2_subdev_set_frame_interval(struct media_entity *entity,
> return 0;
> }
>
> +int v4l2_subdev_parse_setup_routes(struct media_device *media, const char *p)
> +{
> + struct media_entity *entity;
> + struct v4l2_subdev_route *routes;
> + unsigned int num_routes;
> + char *end;
> + int ret;
> + int i;
> +
> + entity = media_parse_entity(media, p, &end);
> + if (!entity)
> + return -EINVAL;
> +
> + p = end;
> +
> + if (*p != '[') {
> + media_dbg(media, "Expected '['\n");
> + return -EINVAL;
> + }
> +
> + p++;
> +
> + routes = calloc(256, sizeof(routes[0]));
You are not checking for NULL here.
> + num_routes = 0;
> +
> + while (*p != 0) {
> + struct v4l2_subdev_route *r = &routes[num_routes];
> +
> + /* sink pad/stream */
> +
> + r->sink_pad = strtoul(p, &end, 10);
> +
> + if (*end != '/') {
> + media_dbg(media, "Expected '/'\n");
> + return -EINVAL;
> + }
> +
> + p = end + 1;
> +
> + r->sink_stream = strtoul(p, &end, 10);
> +
> + for (; isspace(*end); ++end);
> +
> + if (end[0] != '-' || end[1] != '>') {
> + media_dbg(media, "Expected '->'\n");
> + return -EINVAL;
> + }
> + p = end + 2;
> +
> + /* source pad/stream */
> +
> + r->source_pad = strtoul(p, &end, 10);
> +
> + if (*end != '/') {
> + media_dbg(media, "Expected '/'\n");
> + return -EINVAL;
> + }
> +
> + p = end + 1;
> +
> + r->source_stream = strtoul(p, &end, 10);
> +
> + /* flags */
> +
> + for (; isspace(*end); ++end);
> +
> + if (*end != '[') {
> + media_dbg(media, "Expected '['\n");
> + return -EINVAL;
> + }
> +
> + for (end++; isspace(*end); ++end);
> +
> + p = end;
> +
> + r->flags = strtoul(p, &end, 0);
> +
> + if (r->flags & ~(V4L2_SUBDEV_ROUTE_FL_ACTIVE |
> + V4L2_SUBDEV_ROUTE_FL_IMMUTABLE |
> + V4L2_SUBDEV_ROUTE_FL_SOURCE)) {
> + media_dbg(media, "Bad route flags %#x\n", r->flags);
> + return -EINVAL;
> + }
> +
> + for (; isspace(*end); ++end);
> +
> + if (*end != ']') {
> + media_dbg(media, "Expected ']'\n");
> + return -EINVAL;
> + }
> + end++;
> +
> + p = end;
> +
> + num_routes++;
> +
> + if (*p == ',') {
> + p++;
> + continue;
> + }
> +
> + break;
> + }
> +
> + if (*p != ']') {
> + media_dbg(media, "Expected ']'\n");
> + return -EINVAL;
> + }
> +
> + for (i = 0; i < num_routes; ++i) {
> + struct v4l2_subdev_route *r = &routes[i];
> +
> + media_dbg(entity->media,
> + "Setting up route %s : %u/%u -> %u/%u, flags 0x%8.8x\n",
> + entity->info.name,
> + r->sink_pad, r->sink_stream,
> + r->source_pad, r->source_stream,
> + r->flags);
> + }
> +
> + ret = v4l2_subdev_set_routing(entity, routes, num_routes);
> + if (ret) {
> + printf("VIDIOC_SUBDEV_S_ROUTING failed: %d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> static int v4l2_subdev_parse_format(struct media_device *media,
> struct v4l2_mbus_framefmt *format,
> const char *p, char **endp)
[...]
--
Regards,
Pratyush Yadav
Texas Instruments Inc.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v1 3/4] media-ctl: add support for routes and streams
2021-11-30 14:18 ` [PATCH v1 3/4] media-ctl: add support for routes and streams Tomi Valkeinen
2022-03-21 10:37 ` Pratyush Yadav
@ 2022-03-21 10:45 ` Laurent Pinchart
2022-07-14 12:04 ` Tomi Valkeinen
1 sibling, 1 reply; 12+ messages in thread
From: Laurent Pinchart @ 2022-03-21 10:45 UTC (permalink / raw)
To: Tomi Valkeinen
Cc: linux-media, sakari.ailus, Jacopo Mondi,
niklas.soderlund+renesas, Mauro Carvalho Chehab, Hans Verkuil,
Pratyush Yadav
Hi Tomi,
Thank you for the patch.
On Tue, Nov 30, 2021 at 04:18:14PM +0200, Tomi Valkeinen wrote:
> Add support to get and set subdev routes and to get and set
> configurations per stream.
>
> Based on work from Sakari Ailus <sakari.ailus@linux.intel.com>.
>
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
> ---
> utils/media-ctl/libmediactl.c | 41 +++++
> utils/media-ctl/libv4l2subdev.c | 256 ++++++++++++++++++++++++++++----
> utils/media-ctl/media-ctl.c | 113 +++++++++++---
> utils/media-ctl/mediactl.h | 16 ++
> utils/media-ctl/options.c | 15 +-
> utils/media-ctl/options.h | 1 +
> utils/media-ctl/v4l2subdev.h | 58 +++++++-
> 7 files changed, 444 insertions(+), 56 deletions(-)
>
> diff --git a/utils/media-ctl/libmediactl.c b/utils/media-ctl/libmediactl.c
> index 1fd6525b..537365d0 100644
> --- a/utils/media-ctl/libmediactl.c
> +++ b/utils/media-ctl/libmediactl.c
> @@ -876,6 +876,47 @@ struct media_pad *media_parse_pad(struct media_device *media,
> return &entity->pads[pad];
> }
>
> +struct media_pad *media_parse_pad_stream(struct media_device *media,
> + const char *p, unsigned int *stream,
> + char **endp)
> +{
> + struct media_pad *pad;
> + const char *orig_p = p;
> + char *ep;
> +
> + pad = media_parse_pad(media, p, &ep);
> + if (pad == NULL)
> + return NULL;
> +
> + p = ep;
> +
> + if (*p == '/') {
> + unsigned int s;
> +
> + p++;
> +
> + s = strtoul(p, &ep, 10);
> +
> + if (ep == p) {
> + printf("Unable to parse stream: '%s'\n", orig_p);
> + if (endp)
> + *endp = (char*)p;
> + return NULL;
> + }
> +
> + *stream = s;
> +
> + p++;
> + } else {
> + *stream = 0;
> + }
> +
> + if (endp)
> + *endp = (char*)p;
> +
> + return pad;
> +}
> +
> struct media_link *media_parse_link(struct media_device *media,
> const char *p, char **endp)
> {
> diff --git a/utils/media-ctl/libv4l2subdev.c b/utils/media-ctl/libv4l2subdev.c
> index 0d0afbe7..eb9e1cc4 100644
> --- a/utils/media-ctl/libv4l2subdev.c
> +++ b/utils/media-ctl/libv4l2subdev.c
> @@ -63,7 +63,7 @@ void v4l2_subdev_close(struct media_entity *entity)
> }
>
> int v4l2_subdev_get_format(struct media_entity *entity,
> - struct v4l2_mbus_framefmt *format, unsigned int pad,
> + struct v4l2_mbus_framefmt *format, unsigned int pad, unsigned int stream,
> enum v4l2_subdev_format_whence which)
> {
> struct v4l2_subdev_format fmt;
> @@ -75,6 +75,7 @@ int v4l2_subdev_get_format(struct media_entity *entity,
>
> memset(&fmt, 0, sizeof(fmt));
> fmt.pad = pad;
> + fmt.stream = stream;
> fmt.which = which;
>
> ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_FMT, &fmt);
> @@ -87,6 +88,7 @@ int v4l2_subdev_get_format(struct media_entity *entity,
>
> int v4l2_subdev_set_format(struct media_entity *entity,
> struct v4l2_mbus_framefmt *format, unsigned int pad,
> + unsigned int stream,
> enum v4l2_subdev_format_whence which)
> {
> struct v4l2_subdev_format fmt;
> @@ -98,6 +100,7 @@ int v4l2_subdev_set_format(struct media_entity *entity,
>
> memset(&fmt, 0, sizeof(fmt));
> fmt.pad = pad;
> + fmt.stream = stream;
> fmt.which = which;
> fmt.format = *format;
>
> @@ -110,8 +113,8 @@ int v4l2_subdev_set_format(struct media_entity *entity,
> }
>
> int v4l2_subdev_get_selection(struct media_entity *entity,
> - struct v4l2_rect *rect, unsigned int pad, unsigned int target,
> - enum v4l2_subdev_format_whence which)
> + struct v4l2_rect *rect, unsigned int pad, unsigned int stream,
> + unsigned int target, enum v4l2_subdev_format_whence which)
> {
> union {
> struct v4l2_subdev_selection sel;
> @@ -149,8 +152,8 @@ int v4l2_subdev_get_selection(struct media_entity *entity,
> }
>
> int v4l2_subdev_set_selection(struct media_entity *entity,
> - struct v4l2_rect *rect, unsigned int pad, unsigned int target,
> - enum v4l2_subdev_format_whence which)
> + struct v4l2_rect *rect, unsigned int pad, unsigned int stream,
> + unsigned int target, enum v4l2_subdev_format_whence which)
> {
> union {
> struct v4l2_subdev_selection sel;
> @@ -164,6 +167,7 @@ int v4l2_subdev_set_selection(struct media_entity *entity,
>
> memset(&u.sel, 0, sizeof(u.sel));
> u.sel.pad = pad;
> + u.sel.stream = stream;
> u.sel.target = target;
> u.sel.which = which;
> u.sel.r = *rect;
> @@ -178,6 +182,7 @@ int v4l2_subdev_set_selection(struct media_entity *entity,
>
> memset(&u.crop, 0, sizeof(u.crop));
> u.crop.pad = pad;
> + u.crop.stream = stream;
> u.crop.which = which;
> u.crop.rect = *rect;
>
> @@ -189,6 +194,63 @@ int v4l2_subdev_set_selection(struct media_entity *entity,
> return 0;
> }
>
> +int v4l2_subdev_set_routing(struct media_entity *entity,
> + struct v4l2_subdev_route *routes,
> + unsigned int num_routes)
> +{
> + struct v4l2_subdev_routing routing = {
> + .which = V4L2_SUBDEV_FORMAT_ACTIVE,
> + .routes = (uintptr_t)routes,
> + .num_routes = num_routes,
> + };
> + int ret;
> +
> + ret = v4l2_subdev_open(entity);
> + if (ret < 0)
> + return ret;
> +
> + ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_ROUTING, &routing);
> + if (ret == -1)
> + return -errno;
> +
> + return 0;
> +}
> +
> +int v4l2_subdev_get_routing(struct media_entity *entity,
> + struct v4l2_subdev_route **routes,
> + unsigned int *num_routes)
> +{
> + struct v4l2_subdev_routing routing = { 0 };
> + struct v4l2_subdev_route *r;
> + int ret;
> +
> + ret = v4l2_subdev_open(entity);
> + if (ret < 0)
> + return ret;
> +
> + routing.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +
> + ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_ROUTING, &routing);
> + if (ret == -1 && errno != ENOSPC)
> + return -errno;
> +
> + r = calloc(routing.num_routes, sizeof(*r));
> + if (!r)
> + return -ENOMEM;
> +
> + routing.routes = (uintptr_t)r;
> + ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_ROUTING, &routing);
> + if (ret) {
> + free(r);
> + return ret;
> + }
> +
> + *num_routes = routing.num_routes;
> + *routes = r;
> +
> + return 0;
> +}
> +
> int v4l2_subdev_get_dv_timings_caps(struct media_entity *entity,
> struct v4l2_dv_timings_cap *caps)
> {
> @@ -263,7 +325,7 @@ int v4l2_subdev_set_dv_timings(struct media_entity *entity,
>
> int v4l2_subdev_get_frame_interval(struct media_entity *entity,
> struct v4l2_fract *interval,
> - unsigned int pad)
> + unsigned int pad, unsigned int stream)
> {
> struct v4l2_subdev_frame_interval ival;
> int ret;
> @@ -274,6 +336,7 @@ int v4l2_subdev_get_frame_interval(struct media_entity *entity,
>
> memset(&ival, 0, sizeof(ival));
> ival.pad = pad;
> + ival.stream = stream;
>
> ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &ival);
> if (ret < 0)
> @@ -285,7 +348,7 @@ int v4l2_subdev_get_frame_interval(struct media_entity *entity,
>
> int v4l2_subdev_set_frame_interval(struct media_entity *entity,
> struct v4l2_fract *interval,
> - unsigned int pad)
> + unsigned int pad, unsigned int stream)
> {
> struct v4l2_subdev_frame_interval ival;
> int ret;
> @@ -296,6 +359,7 @@ int v4l2_subdev_set_frame_interval(struct media_entity *entity,
>
> memset(&ival, 0, sizeof(ival));
> ival.pad = pad;
> + ival.stream = stream;
> ival.interval = *interval;
>
> ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &ival);
> @@ -306,6 +370,135 @@ int v4l2_subdev_set_frame_interval(struct media_entity *entity,
> return 0;
> }
>
> +int v4l2_subdev_parse_setup_routes(struct media_device *media, const char *p)
> +{
> + struct media_entity *entity;
> + struct v4l2_subdev_route *routes;
> + unsigned int num_routes;
> + char *end;
> + int ret;
> + int i;
> +
> + entity = media_parse_entity(media, p, &end);
> + if (!entity)
> + return -EINVAL;
> +
> + p = end;
> +
> + if (*p != '[') {
> + media_dbg(media, "Expected '['\n");
> + return -EINVAL;
> + }
> +
> + p++;
> +
> + routes = calloc(256, sizeof(routes[0]));
> + num_routes = 0;
> +
> + while (*p != 0) {
> + struct v4l2_subdev_route *r = &routes[num_routes];
> +
> + /* sink pad/stream */
> +
> + r->sink_pad = strtoul(p, &end, 10);
> +
> + if (*end != '/') {
> + media_dbg(media, "Expected '/'\n");
> + return -EINVAL;
> + }
> +
> + p = end + 1;
> +
> + r->sink_stream = strtoul(p, &end, 10);
> +
> + for (; isspace(*end); ++end);
> +
> + if (end[0] != '-' || end[1] != '>') {
> + media_dbg(media, "Expected '->'\n");
> + return -EINVAL;
> + }
> + p = end + 2;
> +
> + /* source pad/stream */
> +
> + r->source_pad = strtoul(p, &end, 10);
> +
> + if (*end != '/') {
> + media_dbg(media, "Expected '/'\n");
> + return -EINVAL;
> + }
> +
> + p = end + 1;
> +
> + r->source_stream = strtoul(p, &end, 10);
> +
> + /* flags */
> +
> + for (; isspace(*end); ++end);
> +
> + if (*end != '[') {
> + media_dbg(media, "Expected '['\n");
> + return -EINVAL;
> + }
> +
> + for (end++; isspace(*end); ++end);
> +
> + p = end;
> +
> + r->flags = strtoul(p, &end, 0);
> +
> + if (r->flags & ~(V4L2_SUBDEV_ROUTE_FL_ACTIVE |
> + V4L2_SUBDEV_ROUTE_FL_IMMUTABLE |
> + V4L2_SUBDEV_ROUTE_FL_SOURCE)) {
> + media_dbg(media, "Bad route flags %#x\n", r->flags);
> + return -EINVAL;
> + }
> +
> + for (; isspace(*end); ++end);
> +
> + if (*end != ']') {
> + media_dbg(media, "Expected ']'\n");
> + return -EINVAL;
> + }
> + end++;
> +
> + p = end;
> +
> + num_routes++;
> +
> + if (*p == ',') {
> + p++;
> + continue;
> + }
> +
> + break;
> + }
> +
> + if (*p != ']') {
> + media_dbg(media, "Expected ']'\n");
> + return -EINVAL;
> + }
> +
> + for (i = 0; i < num_routes; ++i) {
> + struct v4l2_subdev_route *r = &routes[i];
> +
> + media_dbg(entity->media,
> + "Setting up route %s : %u/%u -> %u/%u, flags 0x%8.8x\n",
> + entity->info.name,
> + r->sink_pad, r->sink_stream,
> + r->source_pad, r->source_stream,
> + r->flags);
> + }
> +
> + ret = v4l2_subdev_set_routing(entity, routes, num_routes);
> + if (ret) {
> + printf("VIDIOC_SUBDEV_S_ROUTING failed: %d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> static int v4l2_subdev_parse_format(struct media_device *media,
> struct v4l2_mbus_framefmt *format,
> const char *p, char **endp)
> @@ -441,7 +634,8 @@ static bool strhazit(const char *str, const char **p)
> }
>
> static struct media_pad *v4l2_subdev_parse_pad_format(
> - struct media_device *media, struct v4l2_mbus_framefmt *format,
> + struct media_device *media, unsigned int *stream,
> + struct v4l2_mbus_framefmt *format,
> struct v4l2_rect *crop, struct v4l2_rect *compose,
> struct v4l2_fract *interval, const char *p, char **endp)
> {
> @@ -452,7 +646,7 @@ static struct media_pad *v4l2_subdev_parse_pad_format(
>
> for (; isspace(*p); ++p);
>
> - pad = media_parse_pad(media, p, &end);
> + pad = media_parse_pad_stream(media, p, stream, &end);
> if (pad == NULL) {
> *endp = end;
> return NULL;
> @@ -674,6 +868,7 @@ static struct media_pad *v4l2_subdev_parse_pad_format(
> }
>
> static int set_format(struct media_pad *pad,
> + unsigned int stream,
> struct v4l2_mbus_framefmt *format)
> {
> int ret;
> @@ -682,12 +877,12 @@ static int set_format(struct media_pad *pad,
> return 0;
>
> media_dbg(pad->entity->media,
> - "Setting up format %s %ux%u on pad %s/%u\n",
> + "Setting up format %s %ux%u on pad %s/%u/%u\n",
> v4l2_subdev_pixelcode_to_string(format->code),
> format->width, format->height,
> - pad->entity->info.name, pad->index);
> + pad->entity->info.name, pad->index, stream);
>
> - ret = v4l2_subdev_set_format(pad->entity, format, pad->index,
> + ret = v4l2_subdev_set_format(pad->entity, format, pad->index, stream,
> V4L2_SUBDEV_FORMAT_ACTIVE);
> if (ret < 0) {
> media_dbg(pad->entity->media,
> @@ -704,8 +899,8 @@ static int set_format(struct media_pad *pad,
> return 0;
> }
>
> -static int set_selection(struct media_pad *pad, unsigned int target,
> - struct v4l2_rect *rect)
> +static int set_selection(struct media_pad *pad, unsigned int stream,
> + unsigned int target, struct v4l2_rect *rect)
> {
> int ret;
>
> @@ -713,11 +908,11 @@ static int set_selection(struct media_pad *pad, unsigned int target,
> return 0;
>
> media_dbg(pad->entity->media,
> - "Setting up selection target %u rectangle (%u,%u)/%ux%u on pad %s/%u\n",
> + "Setting up selection target %u rectangle (%u,%u)/%ux%u on pad %s/%u/%u\n",
> target, rect->left, rect->top, rect->width, rect->height,
> - pad->entity->info.name, pad->index);
> + pad->entity->info.name, pad->index, stream);
>
> - ret = v4l2_subdev_set_selection(pad->entity, rect, pad->index,
> + ret = v4l2_subdev_set_selection(pad->entity, rect, pad->index, stream,
> target, V4L2_SUBDEV_FORMAT_ACTIVE);
> if (ret < 0) {
> media_dbg(pad->entity->media,
> @@ -733,7 +928,7 @@ static int set_selection(struct media_pad *pad, unsigned int target,
> return 0;
> }
>
> -static int set_frame_interval(struct media_pad *pad,
> +static int set_frame_interval(struct media_pad *pad, unsigned int stream,
> struct v4l2_fract *interval)
> {
> int ret;
> @@ -742,11 +937,12 @@ static int set_frame_interval(struct media_pad *pad,
> return 0;
>
> media_dbg(pad->entity->media,
> - "Setting up frame interval %u/%u on pad %s/%u\n",
> + "Setting up frame interval %u/%u on pad %s/%u/%u\n",
> interval->numerator, interval->denominator,
> - pad->entity->info.name, pad->index);
> + pad->entity->info.name, pad->index, stream);
>
> - ret = v4l2_subdev_set_frame_interval(pad->entity, interval, pad->index);
> + ret = v4l2_subdev_set_frame_interval(pad->entity, interval, pad->index,
> + stream);
> if (ret < 0) {
> media_dbg(pad->entity->media,
> "Unable to set frame interval: %s (%d)",
> @@ -769,11 +965,13 @@ static int v4l2_subdev_parse_setup_format(struct media_device *media,
> struct v4l2_rect crop = { -1, -1, -1, -1 };
> struct v4l2_rect compose = crop;
> struct v4l2_fract interval = { 0, 0 };
> + unsigned int stream;
> unsigned int i;
> char *end;
> int ret;
>
> - pad = v4l2_subdev_parse_pad_format(media, &format, &crop, &compose,
> + pad = v4l2_subdev_parse_pad_format(media, &stream,
> + &format, &crop, &compose,
> &interval, p, &end);
> if (pad == NULL) {
> media_print_streampos(media, p, end);
> @@ -782,26 +980,26 @@ static int v4l2_subdev_parse_setup_format(struct media_device *media,
> }
>
> if (pad->flags & MEDIA_PAD_FL_SINK) {
> - ret = set_format(pad, &format);
> + ret = set_format(pad, stream, &format);
> if (ret < 0)
> return ret;
> }
>
> - ret = set_selection(pad, V4L2_SEL_TGT_CROP, &crop);
> + ret = set_selection(pad, stream, V4L2_SEL_TGT_CROP, &crop);
> if (ret < 0)
> return ret;
>
> - ret = set_selection(pad, V4L2_SEL_TGT_COMPOSE, &compose);
> + ret = set_selection(pad, stream, V4L2_SEL_TGT_COMPOSE, &compose);
> if (ret < 0)
> return ret;
>
> if (pad->flags & MEDIA_PAD_FL_SOURCE) {
> - ret = set_format(pad, &format);
> + ret = set_format(pad, stream, &format);
> if (ret < 0)
> return ret;
> }
>
> - ret = set_frame_interval(pad, &interval);
> + ret = set_frame_interval(pad, stream, &interval);
> if (ret < 0)
> return ret;
>
> @@ -820,9 +1018,9 @@ static int v4l2_subdev_parse_setup_format(struct media_device *media,
> if (link->source == pad &&
> link->sink->entity->info.type == MEDIA_ENT_T_V4L2_SUBDEV) {
> remote_format = format;
> - set_format(link->sink, &remote_format);
> + set_format(link->sink, stream, &remote_format);
>
> - ret = set_frame_interval(link->sink, &interval);
> + ret = set_frame_interval(link->sink, stream, &interval);
> if (ret < 0 && ret != -EINVAL && ret != -ENOTTY)
> return ret;
> }
> diff --git a/utils/media-ctl/media-ctl.c b/utils/media-ctl/media-ctl.c
> index 84ee7a83..cff08428 100644
> --- a/utils/media-ctl/media-ctl.c
> +++ b/utils/media-ctl/media-ctl.c
> @@ -75,23 +75,45 @@ static void print_flags(const struct flag_name *flag_names, unsigned int num_ent
> }
> }
>
> +static void v4l2_subdev_print_routes(struct media_entity *entity,
> + struct v4l2_subdev_route *routes,
> + unsigned int num_routes)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < num_routes; i++) {
> + const struct v4l2_subdev_route *r = &routes[i];
> +
> + if (i == 0)
> + printf("\troutes:\n");
> +
> + printf("\t\t%u/%u -> %u/%u [%s%s%s]\n",
> + r->sink_pad, r->sink_stream,
> + r->source_pad, r->source_stream,
> + r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE ? "ACTIVE" : "INACTIVE",
> + r->flags & V4L2_SUBDEV_ROUTE_FL_IMMUTABLE ? ", IMMUTABLE" : "",
> + r->flags & V4L2_SUBDEV_ROUTE_FL_SOURCE ? ", SOURCE" : "");
> + }
> +}
> +
> static void v4l2_subdev_print_format(struct media_entity *entity,
> - unsigned int pad, enum v4l2_subdev_format_whence which)
> + unsigned int pad, unsigned int stream,
> + enum v4l2_subdev_format_whence which)
> {
> struct v4l2_mbus_framefmt format;
> struct v4l2_fract interval = { 0, 0 };
> struct v4l2_rect rect;
> int ret;
>
> - ret = v4l2_subdev_get_format(entity, &format, pad, which);
> + ret = v4l2_subdev_get_format(entity, &format, pad, stream, which);
> if (ret != 0)
> return;
>
> - ret = v4l2_subdev_get_frame_interval(entity, &interval, pad);
> + ret = v4l2_subdev_get_frame_interval(entity, &interval, pad, stream);
> if (ret != 0 && ret != -ENOTTY && ret != -EINVAL)
> return;
>
> - printf("\t\t[fmt:%s/%ux%u",
> + printf("\t\t[stream:%u fmt:%s/%ux%u", stream,
> v4l2_subdev_pixelcode_to_string(format.code),
> format.width, format.height);
>
> @@ -118,28 +140,28 @@ static void v4l2_subdev_print_format(struct media_entity *entity,
> v4l2_subdev_quantization_to_string(format.quantization));
> }
>
> - ret = v4l2_subdev_get_selection(entity, &rect, pad,
> + ret = v4l2_subdev_get_selection(entity, &rect, pad, stream,
> V4L2_SEL_TGT_CROP_BOUNDS,
> which);
> if (ret == 0)
> printf("\n\t\t crop.bounds:(%u,%u)/%ux%u", rect.left, rect.top,
> rect.width, rect.height);
>
> - ret = v4l2_subdev_get_selection(entity, &rect, pad,
> + ret = v4l2_subdev_get_selection(entity, &rect, pad, stream,
> V4L2_SEL_TGT_CROP,
> which);
> if (ret == 0)
> printf("\n\t\t crop:(%u,%u)/%ux%u", rect.left, rect.top,
> rect.width, rect.height);
>
> - ret = v4l2_subdev_get_selection(entity, &rect, pad,
> + ret = v4l2_subdev_get_selection(entity, &rect, pad, stream,
> V4L2_SEL_TGT_COMPOSE_BOUNDS,
> which);
> if (ret == 0)
> printf("\n\t\t compose.bounds:(%u,%u)/%ux%u",
> rect.left, rect.top, rect.width, rect.height);
>
> - ret = v4l2_subdev_get_selection(entity, &rect, pad,
> + ret = v4l2_subdev_get_selection(entity, &rect, pad, stream,
> V4L2_SEL_TGT_COMPOSE,
> which);
> if (ret == 0)
> @@ -455,16 +477,51 @@ static void media_print_topology_dot(struct media_device *media)
> }
>
> static void media_print_pad_text(struct media_entity *entity,
> - const struct media_pad *pad)
> + const struct media_pad *pad,
> + struct v4l2_subdev_route *routes,
> + unsigned int num_routes)
> {
> + unsigned int i;
> +
> if (media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV)
> return;
>
> - v4l2_subdev_print_format(entity, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE);
> - v4l2_subdev_print_pad_dv(entity, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE);
> + if (!routes) {
> + v4l2_subdev_print_format(entity, pad->index, 0, V4L2_SUBDEV_FORMAT_ACTIVE);
> + v4l2_subdev_print_pad_dv(entity, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE);
> +
> + if (pad->flags & MEDIA_PAD_FL_SOURCE)
> + v4l2_subdev_print_subdev_dv(entity);
> +
> + return;
> + }
> +
> + for (i = 0; i < num_routes; ++i) {
> + const struct v4l2_subdev_route *r = &routes[i];
> + unsigned int stream;
> +
> + if (!(r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE))
> + continue;
>
> - if (pad->flags & MEDIA_PAD_FL_SOURCE)
> - v4l2_subdev_print_subdev_dv(entity);
> + if (pad->flags & MEDIA_PAD_FL_SINK) {
> + if (r->sink_pad != pad->index)
> + continue;
> +
> + stream = r->sink_stream;
> + } else {
> + if (r->source_pad != pad->index)
> + continue;
> +
> + stream = r->source_stream;
> + }
> +
> + v4l2_subdev_print_format(entity, pad->index, stream, V4L2_SUBDEV_FORMAT_ACTIVE);
> + v4l2_subdev_print_pad_dv(entity, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE);
> +
> + if (pad->flags & MEDIA_PAD_FL_SOURCE)
> + v4l2_subdev_print_subdev_dv(entity);
If a subdev has multiple routes coming from the same pad/stream
(corresponding to the 1-N stream duplication use case), you will print
the same format for the sink pad/stream multiple times.
> +
Extra blank line.
> + }
> }
>
> static void media_print_topology_text_entity(struct media_device *media,
> @@ -480,11 +537,17 @@ static void media_print_topology_text_entity(struct media_device *media,
> unsigned int num_links = media_entity_get_links_count(entity);
> unsigned int j, k;
> unsigned int padding;
> + struct v4l2_subdev_route *routes = NULL;
> + unsigned int num_routes = 0;
> +
> + if (media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV)
> + v4l2_subdev_get_routing(entity, &routes, &num_routes);
>
> padding = printf("- entity %u: ", info->id);
> - printf("%s (%u pad%s, %u link%s)\n", info->name,
> + printf("%s (%u pad%s, %u link%s, %u route%s)\n", info->name,
> info->pads, info->pads > 1 ? "s" : "",
> - num_links, num_links > 1 ? "s" : "");
> + num_links, num_links > 1 ? "s" : "",
> + num_routes, num_routes > 1 ? "s" : "");
> printf("%*ctype %s subtype %s flags %x\n", padding, ' ',
> media_entity_type_to_string(info->type),
> media_entity_subtype_to_string(info->type),
> @@ -492,12 +555,15 @@ static void media_print_topology_text_entity(struct media_device *media,
> if (devname)
> printf("%*cdevice node name %s\n", padding, ' ', devname);
>
> + if (media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV)
> + v4l2_subdev_print_routes(entity, routes, num_routes);
> +
> for (j = 0; j < info->pads; j++) {
> const struct media_pad *pad = media_entity_get_pad(entity, j);
>
> printf("\tpad%u: %s\n", j, media_pad_type_to_string(pad->flags));
>
> - media_print_pad_text(entity, pad);
> + media_print_pad_text(entity, pad, routes, num_routes);
>
> for (k = 0; k < num_links; k++) {
> const struct media_link *link = media_entity_get_link(entity, k);
> @@ -594,14 +660,16 @@ int main(int argc, char **argv)
>
> if (media_opts.fmt_pad) {
> struct media_pad *pad;
> + unsigned int stream;
> + char *p;
>
> - pad = media_parse_pad(media, media_opts.fmt_pad, NULL);
> + pad = media_parse_pad_stream(media, media_opts.fmt_pad, &stream, &p);
> if (pad == NULL) {
> printf("Pad '%s' not found\n", media_opts.fmt_pad);
> goto out;
> }
>
> - v4l2_subdev_print_format(pad->entity, pad->index,
> + v4l2_subdev_print_format(pad->entity, pad->index, stream,
> V4L2_SUBDEV_FORMAT_ACTIVE);
> }
>
> @@ -685,6 +753,15 @@ int main(int argc, char **argv)
> }
> }
>
> + if (media_opts.routes) {
> + ret = v4l2_subdev_parse_setup_routes(media, media_opts.routes);
> + if (ret) {
> + printf("Unable to setup routes: %s (%d)\n",
> + strerror(-ret), -ret);
> + goto out;
> + }
> + }
> +
> if (media_opts.interactive) {
> while (1) {
> char buffer[32];
> diff --git a/utils/media-ctl/mediactl.h b/utils/media-ctl/mediactl.h
> index af360518..c0fc2962 100644
> --- a/utils/media-ctl/mediactl.h
> +++ b/utils/media-ctl/mediactl.h
> @@ -394,6 +394,22 @@ struct media_entity *media_parse_entity(struct media_device *media,
> struct media_pad *media_parse_pad(struct media_device *media,
> const char *p, char **endp);
>
> +/**
> + * @brief Parse string to a pad and stream on the media device.
> + * @param media - media device.
> + * @param p - input string
> + * @param stream - pointer to uint where the stream number is stored
> + * @param endp - pointer to string where parsing ended
> + *
> + * Parse NULL terminated string describing a pad and stream and return its struct
> + * media_pad instance and the stream number.
> + *
> + * @return Pointer to struct media_pad on success, NULL on failure.
> + */
> +struct media_pad *media_parse_pad_stream(struct media_device *media,
> + const char *p, unsigned int *stream,
> + char **endp);
> +
> /**
> * @brief Parse string to a link on the media device.
> * @param media - media device.
> diff --git a/utils/media-ctl/options.c b/utils/media-ctl/options.c
> index 6d30d3dc..58ddec3c 100644
> --- a/utils/media-ctl/options.c
> +++ b/utils/media-ctl/options.c
> @@ -63,6 +63,7 @@ static void usage(const char *argv0)
> printf(" --get-v4l2 pad Print the active format on a given pad\n");
> printf(" --get-dv pad Print detected and current DV timings on a given pad\n");
> printf(" --set-dv pad Configure DV timings on a given pad\n");
> + printf("-R, --set-routes routes Configure routes on a given subdev entity\n");
> printf("-h, --help Show verbose help and exit\n");
> printf("-i, --interactive Modify links interactively\n");
> printf("-l, --links links Comma-separated list of link descriptors to setup\n");
> @@ -78,7 +79,7 @@ static void usage(const char *argv0)
> printf("Links and formats are defined as\n");
> printf("\tlinks = link { ',' link } ;\n");
> printf("\tlink = pad '->' pad '[' flags ']' ;\n");
> - printf("\tpad = entity ':' pad-number ;\n");
> + printf("\tpad = entity ':' pad-number { '/' stream-number } ;\n");
> printf("\tentity = entity-number | ( '\"' entity-name '\"' ) ;\n");
> printf("\n");
> printf("\tv4l2 = pad '[' v4l2-properties ']' ;\n");
> @@ -95,11 +96,16 @@ static void usage(const char *argv0)
> printf("\trectangle = '(' left ',' top, ')' '/' size ;\n");
> printf("\tsize = width 'x' height ;\n");
> printf("\n");
> + printf("\troutes = entity '[' route { ',' route } ']' ;\n");
> + printf("\troute = pad-number '/' stream-number '->' pad-number '/' stream-number '[' route-flags ']' ;\n");
> + printf("\n");
> printf("where the fields are\n");
> printf("\tentity-number Entity numeric identifier\n");
> printf("\tentity-name Entity name (string) \n");
> printf("\tpad-number Pad numeric identifier\n");
> + printf("\tstream-number Stream numeric identifier\n");
> printf("\tflags Link flags (0: inactive, 1: active)\n");
> + printf("\troute-flags Route flags (bitmask of route flags: active - 0x1, immutable - 0x2, source - 0x4)\n");
> printf("\tfcc Format FourCC\n");
> printf("\twidth Image width in pixels\n");
> printf("\theight Image height in pixels\n");
> @@ -152,6 +158,7 @@ static struct option opts[] = {
> {"get-v4l2", 1, 0, OPT_GET_FORMAT},
> {"get-dv", 1, 0, OPT_GET_DV},
> {"set-dv", 1, 0, OPT_SET_DV},
> + {"set-routes", 1, 0, 'R'},
> {"help", 0, 0, 'h'},
> {"interactive", 0, 0, 'i'},
> {"links", 1, 0, 'l'},
> @@ -237,7 +244,7 @@ int parse_cmdline(int argc, char **argv)
> }
>
> /* parse options */
> - while ((opt = getopt_long(argc, argv, "d:e:f:hil:prvV:",
> + while ((opt = getopt_long(argc, argv, "d:e:f:hil:prvV:R:",
> opts, NULL)) != -1) {
> switch (opt) {
> case 'd':
> @@ -283,6 +290,10 @@ int parse_cmdline(int argc, char **argv)
> media_opts.verbose = 1;
> break;
>
> + case 'R':
> + media_opts.routes = optarg;
> + break;
> +
> case OPT_PRINT_DOT:
> media_opts.print_dot = 1;
> break;
> diff --git a/utils/media-ctl/options.h b/utils/media-ctl/options.h
> index b1751f56..8796f1b6 100644
> --- a/utils/media-ctl/options.h
> +++ b/utils/media-ctl/options.h
> @@ -38,6 +38,7 @@ struct media_options
> const char *fmt_pad;
> const char *get_dv_pad;
> const char *dv_pad;
> + const char *routes;
> };
>
> extern struct media_options media_opts;
> diff --git a/utils/media-ctl/v4l2subdev.h b/utils/media-ctl/v4l2subdev.h
> index a1813911..a8a6e7ad 100644
> --- a/utils/media-ctl/v4l2subdev.h
> +++ b/utils/media-ctl/v4l2subdev.h
> @@ -64,7 +64,7 @@ void v4l2_subdev_close(struct media_entity *entity);
> * @return 0 on success, or a negative error code on failure.
> */
> int v4l2_subdev_get_format(struct media_entity *entity,
> - struct v4l2_mbus_framefmt *format, unsigned int pad,
> + struct v4l2_mbus_framefmt *format, unsigned int pad, unsigned int stream,
> enum v4l2_subdev_format_whence which);
>
> /**
> @@ -86,6 +86,7 @@ int v4l2_subdev_get_format(struct media_entity *entity,
> */
> int v4l2_subdev_set_format(struct media_entity *entity,
> struct v4l2_mbus_framefmt *format, unsigned int pad,
> + unsigned int stream,
> enum v4l2_subdev_format_whence which);
>
> /**
> @@ -107,8 +108,8 @@ int v4l2_subdev_set_format(struct media_entity *entity,
> * @return 0 on success, or a negative error code on failure.
> */
> int v4l2_subdev_get_selection(struct media_entity *entity,
> - struct v4l2_rect *rect, unsigned int pad, unsigned int target,
> - enum v4l2_subdev_format_whence which);
> + struct v4l2_rect *rect, unsigned int pad, unsigned int stream,
> + unsigned int target, enum v4l2_subdev_format_whence which);
>
> /**
> * @brief Set a selection rectangle on a pad.
> @@ -129,8 +130,40 @@ int v4l2_subdev_get_selection(struct media_entity *entity,
> * @return 0 on success, or a negative error code on failure.
> */
> int v4l2_subdev_set_selection(struct media_entity *entity,
> - struct v4l2_rect *rect, unsigned int pad, unsigned int target,
> - enum v4l2_subdev_format_whence which);
> + struct v4l2_rect *rect, unsigned int pad, unsigned int stream,
> + unsigned int target, enum v4l2_subdev_format_whence which);
> +
> +/**
> + * @brief Get the routing table of a subdev media entity.
> + * @param entity - subdev-device media entity.
> + * @param routes - routes of the subdev.
> + * @param num_routes - number of routes.
> + *
> + * Get the routes of @a entity and return them in an allocated array in @a routes
> + * and the number of routes in @a num_routes.
> + *
> + * The caller is responsible for freeing the routes array after use.
> + *
> + * @return 0 on success, or a negative error code on failure.
> + */
> +int v4l2_subdev_get_routing(struct media_entity *entity,
> + struct v4l2_subdev_route **routes,
> + unsigned int *num_routes);
> +
> +/**
> + * @brief Set the routing table of a subdev media entity.
> + * @param entity - subdev-device media entity.
> + * @param routes - routes of the subdev.
> + * @param num_routes - number of routes.
> + *
> + * Set the routes of @a entity. The routes are given in @a routes with the
> + * length of @a num_routes.
> + *
> + * @return 0 on success, or a negative error code on failure.
> + */
> +int v4l2_subdev_set_routing(struct media_entity *entity,
> + struct v4l2_subdev_route *route,
> + unsigned int num_routes);
>
> /**
> * @brief Query the digital video capabilities of a pad.
> @@ -200,7 +233,7 @@ int v4l2_subdev_set_dv_timings(struct media_entity *entity,
> */
>
> int v4l2_subdev_get_frame_interval(struct media_entity *entity,
> - struct v4l2_fract *interval, unsigned int pad);
> + struct v4l2_fract *interval, unsigned int pad, unsigned int stream);
>
> /**
> * @brief Set the frame interval on a sub-device.
> @@ -217,7 +250,7 @@ int v4l2_subdev_get_frame_interval(struct media_entity *entity,
> * @return 0 on success, or a negative error code on failure.
> */
> int v4l2_subdev_set_frame_interval(struct media_entity *entity,
> - struct v4l2_fract *interval, unsigned int pad);
> + struct v4l2_fract *interval, unsigned int pad, unsigned int stream);
>
> /**
> * @brief Parse a string and apply format, crop and frame interval settings.
> @@ -235,6 +268,17 @@ int v4l2_subdev_set_frame_interval(struct media_entity *entity,
> */
> int v4l2_subdev_parse_setup_formats(struct media_device *media, const char *p);
>
> +/**
> + * @brief Parse a string and apply route settings.
> + * @param media - media device.
> + * @param p - input string
> + *
> + * Parse string @a p and apply route settings to a subdev.
> + *
> + * @return 0 on success, or a negative error code on failure.
> + */
> +int v4l2_subdev_parse_setup_routes(struct media_device *media, const char *p);
> +
> /**
> * @brief Convert media bus pixel code to string.
> * @param code - input string
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v1 3/4] media-ctl: add support for routes and streams
2022-03-21 10:45 ` Laurent Pinchart
@ 2022-07-14 12:04 ` Tomi Valkeinen
0 siblings, 0 replies; 12+ messages in thread
From: Tomi Valkeinen @ 2022-07-14 12:04 UTC (permalink / raw)
To: Laurent Pinchart
Cc: linux-media, sakari.ailus, Jacopo Mondi,
niklas.soderlund+renesas, Mauro Carvalho Chehab, Hans Verkuil,
Pratyush Yadav
On 21/03/2022 12:45, Laurent Pinchart wrote:
>> @@ -455,16 +477,51 @@ static void media_print_topology_dot(struct media_device *media)
>> }
>>
>> static void media_print_pad_text(struct media_entity *entity,
>> - const struct media_pad *pad)
>> + const struct media_pad *pad,
>> + struct v4l2_subdev_route *routes,
>> + unsigned int num_routes)
>> {
>> + unsigned int i;
>> +
>> if (media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV)
>> return;
>>
>> - v4l2_subdev_print_format(entity, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE);
>> - v4l2_subdev_print_pad_dv(entity, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE);
>> + if (!routes) {
>> + v4l2_subdev_print_format(entity, pad->index, 0, V4L2_SUBDEV_FORMAT_ACTIVE);
>> + v4l2_subdev_print_pad_dv(entity, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE);
>> +
>> + if (pad->flags & MEDIA_PAD_FL_SOURCE)
>> + v4l2_subdev_print_subdev_dv(entity);
>> +
>> + return;
>> + }
>> +
>> + for (i = 0; i < num_routes; ++i) {
>> + const struct v4l2_subdev_route *r = &routes[i];
>> + unsigned int stream;
>> +
>> + if (!(r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE))
>> + continue;
>>
>> - if (pad->flags & MEDIA_PAD_FL_SOURCE)
>> - v4l2_subdev_print_subdev_dv(entity);
>> + if (pad->flags & MEDIA_PAD_FL_SINK) {
>> + if (r->sink_pad != pad->index)
>> + continue;
>> +
>> + stream = r->sink_stream;
>> + } else {
>> + if (r->source_pad != pad->index)
>> + continue;
>> +
>> + stream = r->source_stream;
>> + }
>> +
>> + v4l2_subdev_print_format(entity, pad->index, stream, V4L2_SUBDEV_FORMAT_ACTIVE);
>> + v4l2_subdev_print_pad_dv(entity, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE);
>> +
>> + if (pad->flags & MEDIA_PAD_FL_SOURCE)
>> + v4l2_subdev_print_subdev_dv(entity);
>
> If a subdev has multiple routes coming from the same pad/stream
> (corresponding to the 1-N stream duplication use case), you will print
> the same format for the sink pad/stream multiple times.
Right. I'll change it to print only streams that haven't been printed
before.
Tomi
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v1 4/4] v4l2-ctl/compliance: add routing and streams multiplexed streams
2021-11-30 14:18 [PATCH v1 0/4] v4l-utils: support multiplexed streams Tomi Valkeinen
` (2 preceding siblings ...)
2021-11-30 14:18 ` [PATCH v1 3/4] media-ctl: add support for routes and streams Tomi Valkeinen
@ 2021-11-30 14:18 ` Tomi Valkeinen
2021-12-01 3:46 ` [PATCH v1 0/4] v4l-utils: support " Laurent Pinchart
4 siblings, 0 replies; 12+ messages in thread
From: Tomi Valkeinen @ 2021-11-30 14:18 UTC (permalink / raw)
To: linux-media, sakari.ailus, Jacopo Mondi, Laurent Pinchart,
niklas.soderlund+renesas, Mauro Carvalho Chehab, Hans Verkuil,
Pratyush Yadav
Cc: Tomi Valkeinen
Add basic support for routing and streams.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
utils/common/v4l2-info.cpp | 2 +
utils/v4l2-compliance/v4l2-compliance.cpp | 124 ++++++++++++++++----
utils/v4l2-compliance/v4l2-compliance.h | 8 +-
utils/v4l2-compliance/v4l2-test-subdevs.cpp | 43 ++++++-
4 files changed, 143 insertions(+), 34 deletions(-)
diff --git a/utils/common/v4l2-info.cpp b/utils/common/v4l2-info.cpp
index b8f2c865..62c61844 100644
--- a/utils/common/v4l2-info.cpp
+++ b/utils/common/v4l2-info.cpp
@@ -111,6 +111,8 @@ static std::string subdevcap2s(unsigned cap)
if (cap & V4L2_SUBDEV_CAP_RO_SUBDEV)
s += "\t\tRead-Only Sub-Device\n";
+ if (cap & V4L2_SUBDEV_CAP_MPLEXED)
+ s += "\t\tSub-Device supports Multiplexed Streams\n";
return s;
}
diff --git a/utils/v4l2-compliance/v4l2-compliance.cpp b/utils/v4l2-compliance/v4l2-compliance.cpp
index 0eeabb2d..e4d21efe 100644
--- a/utils/v4l2-compliance/v4l2-compliance.cpp
+++ b/utils/v4l2-compliance/v4l2-compliance.cpp
@@ -1210,6 +1210,10 @@ void testNode(struct node &node, struct node &node_m2m_cap, struct node &expbuf_
if (node.is_subdev()) {
bool has_source = false;
bool has_sink = false;
+ struct v4l2_subdev_routing sd_routing[2] = {};
+ struct v4l2_subdev_route sd_routes[2][256] = {};
+ bool has_routes = !!(subdevcap.capabilities & V4L2_SUBDEV_CAP_MPLEXED);
+ int ret;
node.frame_interval_pad = -1;
node.enum_frame_interval_pad = -1;
@@ -1221,6 +1225,22 @@ void testNode(struct node &node, struct node &node_m2m_cap, struct node &expbuf_
}
node.is_passthrough_subdev = has_source && has_sink;
+ if (has_routes) {
+ for (unsigned which = V4L2_SUBDEV_FORMAT_TRY;
+ which <= V4L2_SUBDEV_FORMAT_ACTIVE; which++) {
+
+ sd_routing[which].which = which;
+ sd_routing[which].routes = (__u64)sd_routes[which];
+ sd_routing[which].num_routes = 256;
+
+ ret = doioctl(&node, VIDIOC_SUBDEV_G_ROUTING, &sd_routing[which]);
+ if (ret) {
+ fail("VIDIOC_SUBDEV_G_ROUTING: failed to get routing\n");
+ sd_routing[which].num_routes = 0;
+ }
+ }
+ }
+
for (unsigned pad = 0; pad < node.entity.pads; pad++) {
printf("Sub-Device ioctls (%s Pad %u):\n",
(node.pads[pad].flags & MEDIA_PAD_FL_SINK) ?
@@ -1230,32 +1250,86 @@ void testNode(struct node &node, struct node &node_m2m_cap, struct node &expbuf_
node.has_subdev_enum_fival = 0;
for (unsigned which = V4L2_SUBDEV_FORMAT_TRY;
which <= V4L2_SUBDEV_FORMAT_ACTIVE; which++) {
- printf("\ttest %s VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: %s\n",
- which ? "Active" : "Try",
- ok(testSubDevEnum(&node, which, pad)));
- printf("\ttest %s VIDIOC_SUBDEV_G/S_FMT: %s\n",
- which ? "Active" : "Try",
- ok(testSubDevFormat(&node, which, pad)));
- printf("\ttest %s VIDIOC_SUBDEV_G/S_SELECTION/CROP: %s\n",
- which ? "Active" : "Try",
- ok(testSubDevSelection(&node, which, pad)));
- if (which)
- printf("\ttest VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: %s\n",
- ok(testSubDevFrameInterval(&node, pad)));
+ struct v4l2_subdev_routing dummy_routing;
+ struct v4l2_subdev_route dummy_routes[1];
+
+ const struct v4l2_subdev_routing *routing;
+ const struct v4l2_subdev_route *routes;
+
+ if (has_routes) {
+ routing = &sd_routing[which];
+ routes = sd_routes[which];
+ } else {
+ dummy_routing.num_routes = 1;
+ dummy_routing.routes = (__u64)&dummy_routes;
+ dummy_routes[0].source_pad = pad;
+ dummy_routes[0].source_stream = 0;
+ dummy_routes[0].sink_pad = pad;
+ dummy_routes[0].sink_stream = 0;
+ dummy_routes[0].flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE;
+
+ routing = &dummy_routing;
+ routes = dummy_routes;
+ }
+
+ for (unsigned i = 0; i < routing->num_routes; ++i) {
+ const struct v4l2_subdev_route *r = &routes[i];
+ unsigned stream;
+
+ if (!(r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE))
+ continue;
+
+ if ((node.pads[pad].flags & MEDIA_PAD_FL_SINK) &&
+ (r->flags & V4L2_SUBDEV_ROUTE_FL_SOURCE))
+ continue;
+
+ if ((node.pads[pad].flags & MEDIA_PAD_FL_SINK) &&
+ (r->sink_pad == pad))
+ stream = r->sink_stream;
+ else if ((node.pads[pad].flags & MEDIA_PAD_FL_SOURCE) &&
+ (r->source_pad == pad))
+ stream = r->source_stream;
+ else
+ continue;
+
+ printf("\t%s Stream %u\n",which ? "Active" : "Try",
+ stream);
+
+ printf("\ttest %s VIDIOC_SUBDEV_ENUM_MBUS_CODE/FRAME_SIZE/FRAME_INTERVAL: %s\n",
+ which ? "Active" : "Try",
+ ok(testSubDevEnum(&node, which, pad, stream)));
+ printf("\ttest %s VIDIOC_SUBDEV_G/S_FMT: %s\n",
+ which ? "Active" : "Try",
+ ok(testSubDevFormat(&node, which, pad, stream)));
+ printf("\ttest %s VIDIOC_SUBDEV_G/S_SELECTION/CROP: %s\n",
+ which ? "Active" : "Try",
+ ok(testSubDevSelection(&node, which, pad, stream)));
+ if (which)
+ printf("\ttest VIDIOC_SUBDEV_G/S_FRAME_INTERVAL: %s\n",
+ ok(testSubDevFrameInterval(&node, pad, stream)));
+ }
+ }
+
+ /*
+ * These tests do not make sense for subdevs with multiplexed streams,
+ * as the try & active cases may have different routing and thus different
+ * behavior.
+ */
+ if (!has_routes) {
+ if (node.has_subdev_enum_code && node.has_subdev_enum_code < 3)
+ fail("VIDIOC_SUBDEV_ENUM_MBUS_CODE: try/active mismatch\n");
+ if (node.has_subdev_enum_fsize && node.has_subdev_enum_fsize < 3)
+ fail("VIDIOC_SUBDEV_ENUM_FRAME_SIZE: try/active mismatch\n");
+ if (node.has_subdev_enum_fival && node.has_subdev_enum_fival < 3)
+ fail("VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: try/active mismatch\n");
+ if (node.has_subdev_fmt && node.has_subdev_fmt < 3)
+ fail("VIDIOC_SUBDEV_G/S_FMT: try/active mismatch\n");
+ if (node.has_subdev_selection && node.has_subdev_selection < 3)
+ fail("VIDIOC_SUBDEV_G/S_SELECTION: try/active mismatch\n");
+ if (node.has_subdev_selection &&
+ node.has_subdev_selection != node.has_subdev_fmt)
+ fail("VIDIOC_SUBDEV_G/S_SELECTION: fmt/selection mismatch\n");
}
- if (node.has_subdev_enum_code && node.has_subdev_enum_code < 3)
- fail("VIDIOC_SUBDEV_ENUM_MBUS_CODE: try/active mismatch\n");
- if (node.has_subdev_enum_fsize && node.has_subdev_enum_fsize < 3)
- fail("VIDIOC_SUBDEV_ENUM_FRAME_SIZE: try/active mismatch\n");
- if (node.has_subdev_enum_fival && node.has_subdev_enum_fival < 3)
- fail("VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: try/active mismatch\n");
- if (node.has_subdev_fmt && node.has_subdev_fmt < 3)
- fail("VIDIOC_SUBDEV_G/S_FMT: try/active mismatch\n");
- if (node.has_subdev_selection && node.has_subdev_selection < 3)
- fail("VIDIOC_SUBDEV_G/S_SELECTION: try/active mismatch\n");
- if (node.has_subdev_selection &&
- node.has_subdev_selection != node.has_subdev_fmt)
- fail("VIDIOC_SUBDEV_G/S_SELECTION: fmt/selection mismatch\n");
printf("\n");
}
}
diff --git a/utils/v4l2-compliance/v4l2-compliance.h b/utils/v4l2-compliance/v4l2-compliance.h
index e73ebdd3..16caeae3 100644
--- a/utils/v4l2-compliance/v4l2-compliance.h
+++ b/utils/v4l2-compliance/v4l2-compliance.h
@@ -364,10 +364,10 @@ int testDecoder(struct node *node);
// SubDev ioctl tests
int testSubDevCap(struct node *node);
-int testSubDevEnum(struct node *node, unsigned which, unsigned pad);
-int testSubDevFormat(struct node *node, unsigned which, unsigned pad);
-int testSubDevSelection(struct node *node, unsigned which, unsigned pad);
-int testSubDevFrameInterval(struct node *node, unsigned pad);
+int testSubDevEnum(struct node *node, unsigned which, unsigned pad, unsigned stream);
+int testSubDevFormat(struct node *node, unsigned which, unsigned pad, unsigned stream);
+int testSubDevSelection(struct node *node, unsigned which, unsigned pad, unsigned stream);
+int testSubDevFrameInterval(struct node *node, unsigned pad, unsigned stream);
// Buffer ioctl tests
int testReqBufs(struct node *node);
diff --git a/utils/v4l2-compliance/v4l2-test-subdevs.cpp b/utils/v4l2-compliance/v4l2-test-subdevs.cpp
index 68f97205..f688b377 100644
--- a/utils/v4l2-compliance/v4l2-test-subdevs.cpp
+++ b/utils/v4l2-compliance/v4l2-test-subdevs.cpp
@@ -25,7 +25,7 @@
#include "v4l2-compliance.h"
-#define VALID_SUBDEV_CAPS (V4L2_SUBDEV_CAP_RO_SUBDEV)
+#define VALID_SUBDEV_CAPS (V4L2_SUBDEV_CAP_RO_SUBDEV | V4L2_SUBDEV_CAP_MPLEXED)
int testSubDevCap(struct node *node)
{
@@ -53,6 +53,7 @@ static int testSubDevEnumFrameInterval(struct node *node, unsigned which,
memset(&fie, 0, sizeof(fie));
fie.which = which;
fie.pad = pad;
+ fie.stream = 0;
fie.code = code;
fie.width = width;
fie.height = height;
@@ -82,6 +83,7 @@ static int testSubDevEnumFrameInterval(struct node *node, unsigned which,
memset(&fie, 0xff, sizeof(fie));
fie.which = which;
fie.pad = pad;
+ fie.stream = 0;
fie.code = code;
fie.width = width;
fie.height = height;
@@ -127,6 +129,7 @@ static int testSubDevEnumFrameSize(struct node *node, unsigned which,
memset(&fse, 0, sizeof(fse));
fse.which = which;
fse.pad = pad;
+ fse.stream = 0;
fse.code = code;
ret = doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &fse);
node->has_subdev_enum_fsize |= (ret != ENOTTY) << which;
@@ -136,6 +139,7 @@ static int testSubDevEnumFrameSize(struct node *node, unsigned which,
memset(&fie, 0, sizeof(fie));
fie.which = which;
fie.pad = pad;
+ fie.stream = 0;
fie.code = code;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie) != ENOTTY);
return ret;
@@ -151,6 +155,7 @@ static int testSubDevEnumFrameSize(struct node *node, unsigned which,
memset(&fse, 0xff, sizeof(fse));
fse.which = which;
fse.pad = pad;
+ fse.stream = 0;
fse.code = code;
fse.index = 0;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &fse));
@@ -194,7 +199,7 @@ static int testSubDevEnumFrameSize(struct node *node, unsigned which,
return 0;
}
-int testSubDevEnum(struct node *node, unsigned which, unsigned pad)
+int testSubDevEnum(struct node *node, unsigned which, unsigned pad, unsigned stream)
{
struct v4l2_subdev_mbus_code_enum mbus_core_enum;
unsigned num_codes;
@@ -203,6 +208,7 @@ int testSubDevEnum(struct node *node, unsigned which, unsigned pad)
memset(&mbus_core_enum, 0, sizeof(mbus_core_enum));
mbus_core_enum.which = which;
mbus_core_enum.pad = pad;
+ mbus_core_enum.stream = stream;
ret = doioctl(node, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_core_enum);
node->has_subdev_enum_code |= (ret != ENOTTY) << which;
if (ret == ENOTTY) {
@@ -213,8 +219,10 @@ int testSubDevEnum(struct node *node, unsigned which, unsigned pad)
memset(&fie, 0, sizeof(fie));
fse.which = which;
fse.pad = pad;
+ fse.stream = stream;
fie.which = which;
fie.pad = pad;
+ fie.stream = stream;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &fse) != ENOTTY);
fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, &fie) != ENOTTY);
return ret;
@@ -225,16 +233,19 @@ int testSubDevEnum(struct node *node, unsigned which, unsigned pad)
mbus_core_enum.index = ~0;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_core_enum) != EINVAL);
mbus_core_enum.pad = node->entity.pads;
+ mbus_core_enum.stream = stream;
mbus_core_enum.index = 0;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_core_enum) != EINVAL);
memset(&mbus_core_enum, 0xff, sizeof(mbus_core_enum));
mbus_core_enum.which = which;
mbus_core_enum.pad = pad;
+ mbus_core_enum.stream = stream;
mbus_core_enum.index = 0;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbus_core_enum));
fail_on_test(check_0(mbus_core_enum.reserved, sizeof(mbus_core_enum.reserved)));
fail_on_test(mbus_core_enum.code == ~0U);
fail_on_test(mbus_core_enum.pad != pad);
+ fail_on_test(mbus_core_enum.stream != stream);
fail_on_test(mbus_core_enum.index);
fail_on_test(mbus_core_enum.which != which);
do {
@@ -251,6 +262,7 @@ int testSubDevEnum(struct node *node, unsigned which, unsigned pad)
fail_on_test(!mbus_core_enum.code);
fail_on_test(mbus_core_enum.which != which);
fail_on_test(mbus_core_enum.pad != pad);
+ fail_on_test(mbus_core_enum.stream != stream);
fail_on_test(mbus_core_enum.index != i);
ret = testSubDevEnumFrameSize(node, which, pad, mbus_core_enum.code);
@@ -259,7 +271,7 @@ int testSubDevEnum(struct node *node, unsigned which, unsigned pad)
return 0;
}
-int testSubDevFrameInterval(struct node *node, unsigned pad)
+int testSubDevFrameInterval(struct node *node, unsigned pad, unsigned stream)
{
struct v4l2_subdev_frame_interval fival;
struct v4l2_fract ival;
@@ -267,6 +279,7 @@ int testSubDevFrameInterval(struct node *node, unsigned pad)
memset(&fival, 0xff, sizeof(fival));
fival.pad = pad;
+ fival.stream = stream;
ret = doioctl(node, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &fival);
if (ret == ENOTTY) {
fail_on_test(node->enum_frame_interval_pad >= 0);
@@ -278,6 +291,7 @@ int testSubDevFrameInterval(struct node *node, unsigned pad)
node->frame_interval_pad = pad;
fail_on_test(check_0(fival.reserved, sizeof(fival.reserved)));
fail_on_test(fival.pad != pad);
+ fail_on_test(fival.stream != stream);
fail_on_test(!fival.interval.numerator);
fail_on_test(!fival.interval.denominator);
fail_on_test(fival.interval.numerator == ~0U || fival.interval.denominator == ~0U);
@@ -289,20 +303,25 @@ int testSubDevFrameInterval(struct node *node, unsigned pad)
}
fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival));
fail_on_test(fival.pad != pad);
+ fail_on_test(fival.stream != stream);
fail_on_test(ival.numerator != fival.interval.numerator);
fail_on_test(ival.denominator != fival.interval.denominator);
fail_on_test(check_0(fival.reserved, sizeof(fival.reserved)));
memset(&fival, 0, sizeof(fival));
fival.pad = pad;
+ fival.stream = stream;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &fival));
fail_on_test(fival.pad != pad);
+ fail_on_test(fival.stream != stream);
fail_on_test(ival.numerator != fival.interval.numerator);
fail_on_test(ival.denominator != fival.interval.denominator);
fival.pad = node->entity.pads;
+ fival.stream = stream;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &fival) != EINVAL);
fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival) != EINVAL);
fival.pad = pad;
+ fival.stream = stream;
fival.interval = ival;
fival.interval.numerator = 0;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &fival));
@@ -339,7 +358,7 @@ static int checkMBusFrameFmt(struct node *node, struct v4l2_mbus_framefmt &fmt)
return 0;
}
-int testSubDevFormat(struct node *node, unsigned which, unsigned pad)
+int testSubDevFormat(struct node *node, unsigned which, unsigned pad, unsigned stream)
{
struct v4l2_subdev_format fmt;
struct v4l2_subdev_format s_fmt;
@@ -348,6 +367,7 @@ int testSubDevFormat(struct node *node, unsigned which, unsigned pad)
memset(&fmt, 0, sizeof(fmt));
fmt.which = which;
fmt.pad = pad;
+ fmt.stream = stream;
ret = doioctl(node, VIDIOC_SUBDEV_G_FMT, &fmt);
node->has_subdev_fmt |= (ret != ENOTTY) << which;
if (ret == ENOTTY) {
@@ -358,14 +378,17 @@ int testSubDevFormat(struct node *node, unsigned which, unsigned pad)
fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_FMT, &fmt) != EINVAL);
fmt.which = 0;
fmt.pad = node->entity.pads;
+ fmt.stream = stream;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_FMT, &fmt) != EINVAL);
memset(&fmt, 0xff, sizeof(fmt));
fmt.which = which;
fmt.pad = pad;
+ fmt.stream = stream;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_FMT, &fmt));
fail_on_test(check_0(fmt.reserved, sizeof(fmt.reserved)));
fail_on_test(fmt.which != which);
fail_on_test(fmt.pad != pad);
+ fail_on_test(fmt.stream != stream);
fail_on_test(checkMBusFrameFmt(node, fmt.format));
s_fmt = fmt;
memset(s_fmt.reserved, 0xff, sizeof(s_fmt.reserved));
@@ -378,6 +401,7 @@ int testSubDevFormat(struct node *node, unsigned which, unsigned pad)
fail_on_test(ret && ret != ENOTTY);
fail_on_test(s_fmt.which != which);
fail_on_test(s_fmt.pad != pad);
+ fail_on_test(s_fmt.stream != stream);
if (ret) {
warn("VIDIOC_SUBDEV_G_FMT is supported but not VIDIOC_SUBDEV_S_FMT\n");
return 0;
@@ -422,7 +446,7 @@ static target_info targets[] = {
{ ~0U },
};
-int testSubDevSelection(struct node *node, unsigned which, unsigned pad)
+int testSubDevSelection(struct node *node, unsigned which, unsigned pad, unsigned stream)
{
struct v4l2_subdev_selection sel;
struct v4l2_subdev_selection s_sel;
@@ -434,10 +458,12 @@ int testSubDevSelection(struct node *node, unsigned which, unsigned pad)
targets[V4L2_SEL_TGT_NATIVE_SIZE].readonly = is_sink;
memset(&crop, 0, sizeof(crop));
crop.pad = pad;
+ crop.stream = stream;
crop.which = which;
memset(&sel, 0, sizeof(sel));
sel.which = which;
sel.pad = pad;
+ sel.stream = stream;
sel.target = V4L2_SEL_TGT_CROP;
ret = doioctl(node, VIDIOC_SUBDEV_G_SELECTION, &sel);
node->has_subdev_selection |= (ret != ENOTTY) << which;
@@ -450,6 +476,7 @@ int testSubDevSelection(struct node *node, unsigned which, unsigned pad)
fail_on_test(check_0(crop.reserved, sizeof(crop.reserved)));
fail_on_test(crop.which != which);
fail_on_test(crop.pad != pad);
+ fail_on_test(crop.stream != stream);
fail_on_test(memcmp(&crop.rect, &sel.r, sizeof(sel.r)));
for (unsigned tgt = 0; targets[tgt].target != ~0U; tgt++) {
@@ -457,6 +484,7 @@ int testSubDevSelection(struct node *node, unsigned which, unsigned pad)
memset(&sel, 0xff, sizeof(sel));
sel.which = which;
sel.pad = pad;
+ sel.stream = stream;
sel.target = tgt;
ret = doioctl(node, VIDIOC_SUBDEV_G_SELECTION, &sel);
targets[tgt].found = !ret;
@@ -468,6 +496,7 @@ int testSubDevSelection(struct node *node, unsigned which, unsigned pad)
fail_on_test(check_0(sel.reserved, sizeof(sel.reserved)));
fail_on_test(sel.which != which);
fail_on_test(sel.pad != pad);
+ fail_on_test(sel.stream != stream);
fail_on_test(sel.target != tgt);
fail_on_test(!sel.r.width);
fail_on_test(sel.r.width == ~0U);
@@ -479,9 +508,11 @@ int testSubDevSelection(struct node *node, unsigned which, unsigned pad)
fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_SELECTION, &sel) != EINVAL);
sel.which = 0;
sel.pad = node->entity.pads;
+ sel.stream = stream;
fail_on_test(doioctl(node, VIDIOC_SUBDEV_G_SELECTION, &sel) != EINVAL);
sel.which = which;
sel.pad = pad;
+ sel.stream = stream;
s_sel = sel;
memset(s_sel.reserved, 0xff, sizeof(s_sel.reserved));
ret = doioctl(node, VIDIOC_SUBDEV_S_SELECTION, &s_sel);
@@ -495,6 +526,7 @@ int testSubDevSelection(struct node *node, unsigned which, unsigned pad)
fail_on_test(check_0(crop.reserved, sizeof(crop.reserved)));
fail_on_test(crop.which != which);
fail_on_test(crop.pad != pad);
+ fail_on_test(crop.stream != stream);
fail_on_test(memcmp(&crop.rect, &sel.r, sizeof(sel.r)));
}
}
@@ -503,6 +535,7 @@ int testSubDevSelection(struct node *node, unsigned which, unsigned pad)
fail_on_test(!ret && targets[tgt].readonly);
fail_on_test(s_sel.which != which);
fail_on_test(s_sel.pad != pad);
+ fail_on_test(s_sel.stream != stream);
if (ret && !targets[tgt].readonly && tgt != V4L2_SEL_TGT_NATIVE_SIZE)
warn("VIDIOC_SUBDEV_G_SELECTION is supported for target %u but not VIDIOC_SUBDEV_S_SELECTION\n", tgt);
if (ret)
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v1 0/4] v4l-utils: support multiplexed streams
2021-11-30 14:18 [PATCH v1 0/4] v4l-utils: support multiplexed streams Tomi Valkeinen
` (3 preceding siblings ...)
2021-11-30 14:18 ` [PATCH v1 4/4] v4l2-ctl/compliance: add routing and streams multiplexed streams Tomi Valkeinen
@ 2021-12-01 3:46 ` Laurent Pinchart
2021-12-01 6:28 ` Tomi Valkeinen
4 siblings, 1 reply; 12+ messages in thread
From: Laurent Pinchart @ 2021-12-01 3:46 UTC (permalink / raw)
To: Tomi Valkeinen
Cc: linux-media, sakari.ailus, Jacopo Mondi,
niklas.soderlund+renesas, Mauro Carvalho Chehab, Hans Verkuil,
Pratyush Yadav
Hi Tomi,
Thank you for the patch.
On Tue, Nov 30, 2021 at 04:18:11PM +0200, Tomi Valkeinen wrote:
> Hi,
>
> This series adds support to multiplexed streams.
>
> v4l2-ctl and media-ctl are updated to allow configuring routes and
> setting configs per stream.
>
> v4l2-compliance is updated to always set the new stream field, and to do
> some testing for multiplexed subdevs.
What device(s) have you tested this with ?
> Tomi Valkeinen (4):
> v4l2-utils: update Linux headers for multiplexed streams
> v4l2-ctl: Add routing and streams support
> media-ctl: add support for routes and streams
> v4l2-ctl/compliance: add routing and streams multiplexed streams
>
> include/linux/v4l2-subdev.h | 88 +++++-
> utils/common/v4l2-info.cpp | 2 +
> utils/media-ctl/libmediactl.c | 41 +++
> utils/media-ctl/libv4l2subdev.c | 256 +++++++++++++++--
> utils/media-ctl/media-ctl.c | 113 ++++++--
> utils/media-ctl/mediactl.h | 16 ++
> utils/media-ctl/options.c | 15 +-
> utils/media-ctl/options.h | 1 +
> utils/media-ctl/v4l2subdev.h | 58 +++-
> utils/v4l2-compliance/v4l2-compliance.cpp | 124 +++++++--
> utils/v4l2-compliance/v4l2-compliance.h | 8 +-
> utils/v4l2-compliance/v4l2-test-subdevs.cpp | 43 ++-
> utils/v4l2-ctl/v4l2-ctl-subdev.cpp | 289 +++++++++++++++++---
> utils/v4l2-ctl/v4l2-ctl.cpp | 2 +
> utils/v4l2-ctl/v4l2-ctl.h | 2 +
> 15 files changed, 928 insertions(+), 130 deletions(-)
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v1 0/4] v4l-utils: support multiplexed streams
2021-12-01 3:46 ` [PATCH v1 0/4] v4l-utils: support " Laurent Pinchart
@ 2021-12-01 6:28 ` Tomi Valkeinen
0 siblings, 0 replies; 12+ messages in thread
From: Tomi Valkeinen @ 2021-12-01 6:28 UTC (permalink / raw)
To: Laurent Pinchart
Cc: linux-media, sakari.ailus, Jacopo Mondi,
niklas.soderlund+renesas, Mauro Carvalho Chehab, Hans Verkuil,
Pratyush Yadav
On 01/12/2021 05:46, Laurent Pinchart wrote:
> Hi Tomi,
>
> Thank you for the patch.
>
> On Tue, Nov 30, 2021 at 04:18:11PM +0200, Tomi Valkeinen wrote:
>> Hi,
>>
>> This series adds support to multiplexed streams.
>>
>> v4l2-ctl and media-ctl are updated to allow configuring routes and
>> setting configs per stream.
>>
>> v4l2-compliance is updated to always set the new stream field, and to do
>> some testing for multiplexed subdevs.
>
> What device(s) have you tested this with ?
TI DRA76 EVM with fpdlink setup.
Later yesterday I realized I should also test without the kernel
multiplexed streams series.
Tomi
^ permalink raw reply [flat|nested] 12+ messages in thread