Message ID | 20230525104036.2050245-2-j-luthra@ti.com |
---|---|
State | Accepted |
Delegated to: | Ryan Eatmon |
Headers | show |
Series | [master/kirkstone,v2,1/2] v4l-utils: Add recipe for v1.24.1 | expand |
Hi Jai, Thanks for the patch. On 25/05/23 16:10, Jai Luthra wrote: > Apply patches for routing and multistream APIs, and the new 4x4 RGB-IR > bayer format used by the OV2312 camera sensor in TI's 6.1 linux kernel. > > Signed-off-by: Jai Luthra <j-luthra@ti.com> Reviewed-by: Devarsh Thakkar <devarsht@ti.com> Regards Devarsh > --- > > No change in v2 > > ...-add-support-for-RGBIr-bayer-formats.patch | 62 + > ...-ctl-Add-routing-and-streams-support.patch | 619 ++++++++++ > ...l-add-support-for-routes-and-streams.patch | 1022 +++++++++++++++++ > ...nce-add-routing-and-streams-multiple.patch | 460 ++++++++ > .../v4l-utils/v4l-utils_1.24.1.bb | 4 + > 5 files changed, 2167 insertions(+) > create mode 100644 meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0001-media-ctl-add-support-for-RGBIr-bayer-formats.patch > create mode 100644 meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0001-v4l2-ctl-Add-routing-and-streams-support.patch > create mode 100644 meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0002-media-ctl-add-support-for-routes-and-streams.patch > create mode 100644 meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0003-v4l2-ctl-compliance-add-routing-and-streams-multiple.patch > > diff --git a/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0001-media-ctl-add-support-for-RGBIr-bayer-formats.patch b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0001-media-ctl-add-support-for-RGBIr-bayer-formats.patch > new file mode 100644 > index 00000000..00f54456 > --- /dev/null > +++ b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0001-media-ctl-add-support-for-RGBIr-bayer-formats.patch > @@ -0,0 +1,62 @@ > +From 26e2a60d29456a9cc6acb16ea19039414808bc5e Mon Sep 17 00:00:00 2001 > +From: Jai Luthra <j-luthra@ti.com> > +Date: Tue, 5 Jul 2022 16:23:39 +0530 > +Subject: [PATCH] media-ctl: add support for RGBIr bayer formats > + > +Signed-off-by: Jai Luthra <j-luthra@ti.com> > +--- > + include/linux/media-bus-format.h | 10 +++++++++- > + include/linux/videodev2.h | 9 +++++++++ > + 2 files changed, 18 insertions(+), 1 deletion(-) > + > +diff --git a/include/linux/media-bus-format.h b/include/linux/media-bus-format.h > +index ca9a24c8..cbdf3798 100644 > +--- a/include/linux/media-bus-format.h > ++++ b/include/linux/media-bus-format.h > +@@ -117,7 +117,7 @@ > + #define MEDIA_BUS_FMT_YUV16_1X48 0x202a > + #define MEDIA_BUS_FMT_UYYVYY16_0_5X48 0x202b > + > +-/* Bayer - next is 0x3021 */ > ++/* Bayer - next is 0x3029 */ > + #define MEDIA_BUS_FMT_SBGGR8_1X8 0x3001 > + #define MEDIA_BUS_FMT_SGBRG8_1X8 0x3013 > + #define MEDIA_BUS_FMT_SGRBG8_1X8 0x3002 > +@@ -150,6 +150,14 @@ > + #define MEDIA_BUS_FMT_SGBRG16_1X16 0x301e > + #define MEDIA_BUS_FMT_SGRBG16_1X16 0x301f > + #define MEDIA_BUS_FMT_SRGGB16_1X16 0x3020 > ++#define MEDIA_BUS_FMT_SRGGI10_1X10 0x3021 > ++#define MEDIA_BUS_FMT_SGRIG10_1X10 0x3022 > ++#define MEDIA_BUS_FMT_SBGGI10_1X10 0x3023 > ++#define MEDIA_BUS_FMT_SGBIG10_1X10 0x3024 > ++#define MEDIA_BUS_FMT_SGIRG10_1X10 0x3025 > ++#define MEDIA_BUS_FMT_SIGGR10_1X10 0x3026 > ++#define MEDIA_BUS_FMT_SGIBG10_1X10 0x3027 > ++#define MEDIA_BUS_FMT_SIGGB10_1X10 0x3028 > + > + /* JPEG compressed formats - next is 0x4002 */ > + #define MEDIA_BUS_FMT_JPEG_1X8 0x4001 > +diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h > +index 5eb96692..093104ab 100644 > +--- a/include/linux/videodev2.h > ++++ b/include/linux/videodev2.h > +@@ -682,6 +682,15 @@ struct v4l2_pix_format { > + #define V4L2_PIX_FMT_SGBRG16 v4l2_fourcc('G', 'B', '1', '6') /* 16 GBGB.. RGRG.. */ > + #define V4L2_PIX_FMT_SGRBG16 v4l2_fourcc('G', 'R', '1', '6') /* 16 GRGR.. BGBG.. */ > + #define V4L2_PIX_FMT_SRGGB16 v4l2_fourcc('R', 'G', '1', '6') /* 16 RGRG.. GBGB.. */ > ++ /* 10bit raw bayer with IR (4x4) */ > ++#define V4L2_PIX_FMT_SRGGI10 v4l2_fourcc('R', 'G', 'I', '0') /* 10 RGBG.. GIrGIr.. */ > ++#define V4L2_PIX_FMT_SGRIG10 v4l2_fourcc('G', 'R', 'I', '0') /* 10 GRGB.. IrGIrG.. */ > ++#define V4L2_PIX_FMT_SBGGI10 v4l2_fourcc('B', 'G', 'I', '0') /* 10 BGRG.. GIrGIr.. */ > ++#define V4L2_PIX_FMT_SGBIG10 v4l2_fourcc('G', 'B', 'I', '0') /* 10 GBGR.. IrGIrG.. */ > ++#define V4L2_PIX_FMT_SGIRG10 v4l2_fourcc('G', 'I', 'R', '0') /* 10 GIrGIr.. RGBG.. */ > ++#define V4L2_PIX_FMT_SIGGR10 v4l2_fourcc('I', 'G', 'R', '0') /* 10 IrGIrG.. GRGB.. */ > ++#define V4L2_PIX_FMT_SGIBG10 v4l2_fourcc('G', 'I', 'B', '0') /* 10 GIrGIr.. BGRG.. */ > ++#define V4L2_PIX_FMT_SIGGB10 v4l2_fourcc('I', 'G', 'B', '0') /* 10 IrGIrG.. GBGR.. */ > + > + /* HSV formats */ > + #define V4L2_PIX_FMT_HSV24 v4l2_fourcc('H', 'S', 'V', '3') > +-- > +2.40.0 > + > diff --git a/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0001-v4l2-ctl-Add-routing-and-streams-support.patch b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0001-v4l2-ctl-Add-routing-and-streams-support.patch > new file mode 100644 > index 00000000..90f15485 > --- /dev/null > +++ b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0001-v4l2-ctl-Add-routing-and-streams-support.patch > @@ -0,0 +1,619 @@ > +From 3b57a10f899403acd877683ca0247f2a9eba8850 Mon Sep 17 00:00:00 2001 > +From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> > +Date: Fri, 10 Feb 2023 13:55:44 +0200 > +Subject: [PATCH 1/3] v4l2-ctl: Add routing and streams support > +MIME-Version: 1.0 > +Content-Type: text/plain; charset=UTF-8 > +Content-Transfer-Encoding: 8bit > + > +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 | 288 +++++++++++++++++++++++++---- > + utils/v4l2-ctl/v4l2-ctl.cpp | 2 + > + utils/v4l2-ctl/v4l2-ctl.h | 2 + > + 3 files changed, 259 insertions(+), 33 deletions(-) > + > +diff --git a/utils/v4l2-ctl/v4l2-ctl-subdev.cpp b/utils/v4l2-ctl/v4l2-ctl-subdev.cpp > +index 33cc1342..81236451 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)) { > ++ 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,55 @@ 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, "ACTIVE" }, > ++ }; > ++ > ++ 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 +712,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 +726,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 +747,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 +760,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 +787,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 +845,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 +860,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 8585278f..1cfb50f7 100644 > +--- a/utils/v4l2-ctl/v4l2-ctl.cpp > ++++ b/utils/v4l2-ctl/v4l2-ctl.cpp > +@@ -64,6 +64,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 70a80ade..51a68b92 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.40.0 > + > diff --git a/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0002-media-ctl-add-support-for-routes-and-streams.patch b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0002-media-ctl-add-support-for-routes-and-streams.patch > new file mode 100644 > index 00000000..b2d4eded > --- /dev/null > +++ b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0002-media-ctl-add-support-for-routes-and-streams.patch > @@ -0,0 +1,1022 @@ > +From 868c176e0de433777d5eed3e6d6d8dc03b9145a6 Mon Sep 17 00:00:00 2001 > +From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> > +Date: Fri, 10 Feb 2023 13:55:45 +0200 > +Subject: [PATCH 2/3] media-ctl: add support for routes and streams > + > +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 | 283 ++++++++++++++++++++++++++++---- > + utils/media-ctl/media-ctl.c | 121 ++++++++++++-- > + 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, 478 insertions(+), 57 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 63bb3d75..d203e5b4 100644 > +--- a/utils/media-ctl/libv4l2subdev.c > ++++ b/utils/media-ctl/libv4l2subdev.c > +@@ -64,7 +64,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; > +@@ -76,6 +76,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); > +@@ -88,6 +89,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; > +@@ -99,6 +101,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; > + > +@@ -111,8 +114,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; > +@@ -150,8 +153,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; > +@@ -165,6 +168,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; > +@@ -179,6 +183,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; > + > +@@ -190,6 +195,69 @@ 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; > ++ > ++ if (!routing.num_routes) { > ++ *routes = NULL; > ++ *num_routes = 0; > ++ return 0; > ++ } > ++ > ++ 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) > + { > +@@ -264,7 +332,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; > +@@ -275,6 +343,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) > +@@ -286,7 +355,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; > +@@ -297,6 +366,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); > +@@ -307,6 +377,155 @@ int v4l2_subdev_set_frame_interval(struct media_entity *entity, > + return 0; > + } > + > ++static int v4l2_subdev_parse_setup_route(struct media_device *media, > ++ struct v4l2_subdev_route *r, > ++ const char *p, char **endp) > ++{ > ++ char *end; > ++ > ++ /* 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)) { > ++ 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++; > ++ > ++ *endp = end; > ++ > ++ 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])); > ++ if (!routes) > ++ return -ENOMEM; > ++ > ++ num_routes = 0; > ++ > ++ while (*p != 0) { > ++ struct v4l2_subdev_route *r = &routes[num_routes]; > ++ > ++ ret = v4l2_subdev_parse_setup_route(media, r, p, &end); > ++ if (ret) > ++ goto out; > ++ > ++ p = end; > ++ > ++ num_routes++; > ++ > ++ if (*p == ',') { > ++ p++; > ++ continue; > ++ } > ++ > ++ break; > ++ } > ++ > ++ if (*p != ']') { > ++ media_dbg(media, "Expected ']'\n"); > ++ ret = -EINVAL; > ++ goto out; > ++ } > ++ > ++ 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); > ++ goto out; > ++ } > ++ > ++out: > ++ free(routes); > ++ > ++ return ret; > ++} > ++ > + static int v4l2_subdev_parse_format(struct media_device *media, > + struct v4l2_mbus_framefmt *format, > + const char *p, char **endp) > +@@ -442,7 +661,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) > + { > +@@ -453,7 +673,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; > +@@ -675,6 +895,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; > +@@ -683,12 +904,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, > +@@ -705,8 +926,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; > + > +@@ -714,11 +935,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, > +@@ -734,7 +955,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; > +@@ -743,11 +964,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)", > +@@ -770,11 +992,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); > +@@ -783,30 +1007,29 @@ 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; > + > +- > + /* If the pad is an output pad, automatically set the same format and > + * frame interval on the remote subdev input pads, if any. > + */ > +@@ -821,9 +1044,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..831136a0 100644 > +--- a/utils/media-ctl/media-ctl.c > ++++ b/utils/media-ctl/media-ctl.c > +@@ -28,6 +28,7 @@ > + #include <errno.h> > + #include <fcntl.h> > + #include <stdbool.h> > ++#include <stdint.h> > + #include <stdio.h> > + #include <stdlib.h> > + #include <string.h> > +@@ -75,23 +76,43 @@ 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]\n", > ++ r->sink_pad, r->sink_stream, > ++ r->source_pad, r->source_stream, > ++ r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE ? "ACTIVE" : "INACTIVE"); > ++ } > ++} > ++ > + 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 +139,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 +476,58 @@ 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; > ++ uint64_t printed_streams_mask; > ++ > + 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; > ++ } > ++ > ++ printed_streams_mask = 0; > ++ > ++ for (i = 0; i < num_routes; ++i) { > ++ const struct v4l2_subdev_route *r = &routes[i]; > ++ unsigned int stream; > + > +- if (pad->flags & MEDIA_PAD_FL_SOURCE) > +- v4l2_subdev_print_subdev_dv(entity); > ++ if (!(r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) > ++ continue; > ++ > ++ 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; > ++ } > ++ > ++ if (printed_streams_mask & (1 << stream)) > ++ continue; > ++ > ++ 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); > ++ > ++ printed_streams_mask |= (1 << stream); > ++ } > + } > + > + static void media_print_topology_text_entity(struct media_device *media, > +@@ -480,11 +543,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 +561,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); > +@@ -521,6 +593,8 @@ static void media_print_topology_text_entity(struct media_device *media, > + } > + } > + printf("\n"); > ++ > ++ free(routes); > + } > + > + static void media_print_topology_text(struct media_device *media) > +@@ -594,14 +668,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 +761,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.40.0 > + > diff --git a/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0003-v4l2-ctl-compliance-add-routing-and-streams-multiple.patch b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0003-v4l2-ctl-compliance-add-routing-and-streams-multiple.patch > new file mode 100644 > index 00000000..32eeb02a > --- /dev/null > +++ b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0003-v4l2-ctl-compliance-add-routing-and-streams-multiple.patch > @@ -0,0 +1,460 @@ > +From 2866c81d2597f47ed976928bc9c27942bbf095f0 Mon Sep 17 00:00:00 2001 > +From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> > +Date: Fri, 10 Feb 2023 13:55:46 +0200 > +Subject: [PATCH 3/3] v4l2-ctl/compliance: add routing and streams multiplexed > + streams > + > +Add basic support for routing and streams. > + > +Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> > +--- > + utils/v4l2-compliance/v4l2-compliance.cpp | 120 ++++++++++++++++---- > + utils/v4l2-compliance/v4l2-compliance.h | 8 +- > + utils/v4l2-compliance/v4l2-test-subdevs.cpp | 43 ++++++- > + 3 files changed, 137 insertions(+), 34 deletions(-) > + > +diff --git a/utils/v4l2-compliance/v4l2-compliance.cpp b/utils/v4l2-compliance/v4l2-compliance.cpp > +index 8aebae2e..63b5fbbb 100644 > +--- a/utils/v4l2-compliance/v4l2-compliance.cpp > ++++ b/utils/v4l2-compliance/v4l2-compliance.cpp > +@@ -1224,6 +1224,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_STREAMS); > ++ int ret; > + > + node.frame_interval_pad = -1; > + node.enum_frame_interval_pad = -1; > +@@ -1235,6 +1239,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) ? > +@@ -1244,32 +1264,82 @@ 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->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 e574c06c..67b3521e 100644 > +--- a/utils/v4l2-compliance/v4l2-compliance.h > ++++ b/utils/v4l2-compliance/v4l2-compliance.h > +@@ -373,10 +373,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 f3d85771..07192bda 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_STREAMS) > + > + int testSubDevCap(struct node *node) > + { > +@@ -54,6 +54,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; > +@@ -83,6 +84,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; > +@@ -128,6 +130,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; > +@@ -137,6 +140,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; > +@@ -152,6 +156,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)); > +@@ -195,7 +200,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; > +@@ -204,6 +209,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) { > +@@ -214,8 +220,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; > +@@ -226,16 +234,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 { > +@@ -252,6 +263,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); > +@@ -260,7 +272,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; > +@@ -268,6 +280,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); > +@@ -279,6 +292,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); > +@@ -290,20 +304,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)); > +@@ -340,7 +359,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; > +@@ -349,6 +368,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) { > +@@ -359,14 +379,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)); > +@@ -379,6 +402,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; > +@@ -423,7 +447,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; > +@@ -435,10 +459,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; > +@@ -451,6 +477,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++) { > +@@ -458,6 +485,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; > +@@ -469,6 +497,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); > +@@ -480,9 +509,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); > +@@ -496,6 +527,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))); > + } > + } > +@@ -504,6 +536,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.40.0 > + > diff --git a/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils_1.24.1.bb b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils_1.24.1.bb > index c604ebbc..a2ebb0ea 100644 > --- a/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils_1.24.1.bb > +++ b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils_1.24.1.bb > @@ -31,6 +31,10 @@ SRC_URI = "\ > file://0002-original-patch-mediactl-pkgconfig.patch \ > file://0003-original-patch-export-mediactl-headers.patch \ > file://0004-Do-not-use-getsubopt.patch \ > + file://0001-v4l2-ctl-Add-routing-and-streams-support.patch \ > + file://0002-media-ctl-add-support-for-routes-and-streams.patch \ > + file://0003-v4l2-ctl-compliance-add-routing-and-streams-multiple.patch \ > + file://0001-media-ctl-add-support-for-RGBIr-bayer-formats.patch \ > " > > SRC_URI[md5sum] = "8ba9c73c4319b6afab5fa4358edc43de"
diff --git a/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0001-media-ctl-add-support-for-RGBIr-bayer-formats.patch b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0001-media-ctl-add-support-for-RGBIr-bayer-formats.patch new file mode 100644 index 00000000..00f54456 --- /dev/null +++ b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0001-media-ctl-add-support-for-RGBIr-bayer-formats.patch @@ -0,0 +1,62 @@ +From 26e2a60d29456a9cc6acb16ea19039414808bc5e Mon Sep 17 00:00:00 2001 +From: Jai Luthra <j-luthra@ti.com> +Date: Tue, 5 Jul 2022 16:23:39 +0530 +Subject: [PATCH] media-ctl: add support for RGBIr bayer formats + +Signed-off-by: Jai Luthra <j-luthra@ti.com> +--- + include/linux/media-bus-format.h | 10 +++++++++- + include/linux/videodev2.h | 9 +++++++++ + 2 files changed, 18 insertions(+), 1 deletion(-) + +diff --git a/include/linux/media-bus-format.h b/include/linux/media-bus-format.h +index ca9a24c8..cbdf3798 100644 +--- a/include/linux/media-bus-format.h ++++ b/include/linux/media-bus-format.h +@@ -117,7 +117,7 @@ + #define MEDIA_BUS_FMT_YUV16_1X48 0x202a + #define MEDIA_BUS_FMT_UYYVYY16_0_5X48 0x202b + +-/* Bayer - next is 0x3021 */ ++/* Bayer - next is 0x3029 */ + #define MEDIA_BUS_FMT_SBGGR8_1X8 0x3001 + #define MEDIA_BUS_FMT_SGBRG8_1X8 0x3013 + #define MEDIA_BUS_FMT_SGRBG8_1X8 0x3002 +@@ -150,6 +150,14 @@ + #define MEDIA_BUS_FMT_SGBRG16_1X16 0x301e + #define MEDIA_BUS_FMT_SGRBG16_1X16 0x301f + #define MEDIA_BUS_FMT_SRGGB16_1X16 0x3020 ++#define MEDIA_BUS_FMT_SRGGI10_1X10 0x3021 ++#define MEDIA_BUS_FMT_SGRIG10_1X10 0x3022 ++#define MEDIA_BUS_FMT_SBGGI10_1X10 0x3023 ++#define MEDIA_BUS_FMT_SGBIG10_1X10 0x3024 ++#define MEDIA_BUS_FMT_SGIRG10_1X10 0x3025 ++#define MEDIA_BUS_FMT_SIGGR10_1X10 0x3026 ++#define MEDIA_BUS_FMT_SGIBG10_1X10 0x3027 ++#define MEDIA_BUS_FMT_SIGGB10_1X10 0x3028 + + /* JPEG compressed formats - next is 0x4002 */ + #define MEDIA_BUS_FMT_JPEG_1X8 0x4001 +diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h +index 5eb96692..093104ab 100644 +--- a/include/linux/videodev2.h ++++ b/include/linux/videodev2.h +@@ -682,6 +682,15 @@ struct v4l2_pix_format { + #define V4L2_PIX_FMT_SGBRG16 v4l2_fourcc('G', 'B', '1', '6') /* 16 GBGB.. RGRG.. */ + #define V4L2_PIX_FMT_SGRBG16 v4l2_fourcc('G', 'R', '1', '6') /* 16 GRGR.. BGBG.. */ + #define V4L2_PIX_FMT_SRGGB16 v4l2_fourcc('R', 'G', '1', '6') /* 16 RGRG.. GBGB.. */ ++ /* 10bit raw bayer with IR (4x4) */ ++#define V4L2_PIX_FMT_SRGGI10 v4l2_fourcc('R', 'G', 'I', '0') /* 10 RGBG.. GIrGIr.. */ ++#define V4L2_PIX_FMT_SGRIG10 v4l2_fourcc('G', 'R', 'I', '0') /* 10 GRGB.. IrGIrG.. */ ++#define V4L2_PIX_FMT_SBGGI10 v4l2_fourcc('B', 'G', 'I', '0') /* 10 BGRG.. GIrGIr.. */ ++#define V4L2_PIX_FMT_SGBIG10 v4l2_fourcc('G', 'B', 'I', '0') /* 10 GBGR.. IrGIrG.. */ ++#define V4L2_PIX_FMT_SGIRG10 v4l2_fourcc('G', 'I', 'R', '0') /* 10 GIrGIr.. RGBG.. */ ++#define V4L2_PIX_FMT_SIGGR10 v4l2_fourcc('I', 'G', 'R', '0') /* 10 IrGIrG.. GRGB.. */ ++#define V4L2_PIX_FMT_SGIBG10 v4l2_fourcc('G', 'I', 'B', '0') /* 10 GIrGIr.. BGRG.. */ ++#define V4L2_PIX_FMT_SIGGB10 v4l2_fourcc('I', 'G', 'B', '0') /* 10 IrGIrG.. GBGR.. */ + + /* HSV formats */ + #define V4L2_PIX_FMT_HSV24 v4l2_fourcc('H', 'S', 'V', '3') +-- +2.40.0 + diff --git a/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0001-v4l2-ctl-Add-routing-and-streams-support.patch b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0001-v4l2-ctl-Add-routing-and-streams-support.patch new file mode 100644 index 00000000..90f15485 --- /dev/null +++ b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0001-v4l2-ctl-Add-routing-and-streams-support.patch @@ -0,0 +1,619 @@ +From 3b57a10f899403acd877683ca0247f2a9eba8850 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> +Date: Fri, 10 Feb 2023 13:55:44 +0200 +Subject: [PATCH 1/3] v4l2-ctl: Add routing and streams support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +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 | 288 +++++++++++++++++++++++++---- + utils/v4l2-ctl/v4l2-ctl.cpp | 2 + + utils/v4l2-ctl/v4l2-ctl.h | 2 + + 3 files changed, 259 insertions(+), 33 deletions(-) + +diff --git a/utils/v4l2-ctl/v4l2-ctl-subdev.cpp b/utils/v4l2-ctl/v4l2-ctl-subdev.cpp +index 33cc1342..81236451 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)) { ++ 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,55 @@ 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, "ACTIVE" }, ++ }; ++ ++ 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 +712,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 +726,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 +747,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 +760,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 +787,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 +845,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 +860,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 8585278f..1cfb50f7 100644 +--- a/utils/v4l2-ctl/v4l2-ctl.cpp ++++ b/utils/v4l2-ctl/v4l2-ctl.cpp +@@ -64,6 +64,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 70a80ade..51a68b92 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.40.0 + diff --git a/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0002-media-ctl-add-support-for-routes-and-streams.patch b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0002-media-ctl-add-support-for-routes-and-streams.patch new file mode 100644 index 00000000..b2d4eded --- /dev/null +++ b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0002-media-ctl-add-support-for-routes-and-streams.patch @@ -0,0 +1,1022 @@ +From 868c176e0de433777d5eed3e6d6d8dc03b9145a6 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> +Date: Fri, 10 Feb 2023 13:55:45 +0200 +Subject: [PATCH 2/3] media-ctl: add support for routes and streams + +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 | 283 ++++++++++++++++++++++++++++---- + utils/media-ctl/media-ctl.c | 121 ++++++++++++-- + 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, 478 insertions(+), 57 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 63bb3d75..d203e5b4 100644 +--- a/utils/media-ctl/libv4l2subdev.c ++++ b/utils/media-ctl/libv4l2subdev.c +@@ -64,7 +64,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; +@@ -76,6 +76,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); +@@ -88,6 +89,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; +@@ -99,6 +101,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; + +@@ -111,8 +114,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; +@@ -150,8 +153,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; +@@ -165,6 +168,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; +@@ -179,6 +183,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; + +@@ -190,6 +195,69 @@ 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; ++ ++ if (!routing.num_routes) { ++ *routes = NULL; ++ *num_routes = 0; ++ return 0; ++ } ++ ++ 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) + { +@@ -264,7 +332,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; +@@ -275,6 +343,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) +@@ -286,7 +355,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; +@@ -297,6 +366,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); +@@ -307,6 +377,155 @@ int v4l2_subdev_set_frame_interval(struct media_entity *entity, + return 0; + } + ++static int v4l2_subdev_parse_setup_route(struct media_device *media, ++ struct v4l2_subdev_route *r, ++ const char *p, char **endp) ++{ ++ char *end; ++ ++ /* 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)) { ++ 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++; ++ ++ *endp = end; ++ ++ 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])); ++ if (!routes) ++ return -ENOMEM; ++ ++ num_routes = 0; ++ ++ while (*p != 0) { ++ struct v4l2_subdev_route *r = &routes[num_routes]; ++ ++ ret = v4l2_subdev_parse_setup_route(media, r, p, &end); ++ if (ret) ++ goto out; ++ ++ p = end; ++ ++ num_routes++; ++ ++ if (*p == ',') { ++ p++; ++ continue; ++ } ++ ++ break; ++ } ++ ++ if (*p != ']') { ++ media_dbg(media, "Expected ']'\n"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ 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); ++ goto out; ++ } ++ ++out: ++ free(routes); ++ ++ return ret; ++} ++ + static int v4l2_subdev_parse_format(struct media_device *media, + struct v4l2_mbus_framefmt *format, + const char *p, char **endp) +@@ -442,7 +661,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) + { +@@ -453,7 +673,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; +@@ -675,6 +895,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; +@@ -683,12 +904,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, +@@ -705,8 +926,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; + +@@ -714,11 +935,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, +@@ -734,7 +955,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; +@@ -743,11 +964,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)", +@@ -770,11 +992,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); +@@ -783,30 +1007,29 @@ 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; + +- + /* If the pad is an output pad, automatically set the same format and + * frame interval on the remote subdev input pads, if any. + */ +@@ -821,9 +1044,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..831136a0 100644 +--- a/utils/media-ctl/media-ctl.c ++++ b/utils/media-ctl/media-ctl.c +@@ -28,6 +28,7 @@ + #include <errno.h> + #include <fcntl.h> + #include <stdbool.h> ++#include <stdint.h> + #include <stdio.h> + #include <stdlib.h> + #include <string.h> +@@ -75,23 +76,43 @@ 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]\n", ++ r->sink_pad, r->sink_stream, ++ r->source_pad, r->source_stream, ++ r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE ? "ACTIVE" : "INACTIVE"); ++ } ++} ++ + 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 +139,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 +476,58 @@ 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; ++ uint64_t printed_streams_mask; ++ + 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; ++ } ++ ++ printed_streams_mask = 0; ++ ++ for (i = 0; i < num_routes; ++i) { ++ const struct v4l2_subdev_route *r = &routes[i]; ++ unsigned int stream; + +- if (pad->flags & MEDIA_PAD_FL_SOURCE) +- v4l2_subdev_print_subdev_dv(entity); ++ if (!(r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) ++ continue; ++ ++ 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; ++ } ++ ++ if (printed_streams_mask & (1 << stream)) ++ continue; ++ ++ 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); ++ ++ printed_streams_mask |= (1 << stream); ++ } + } + + static void media_print_topology_text_entity(struct media_device *media, +@@ -480,11 +543,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 +561,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); +@@ -521,6 +593,8 @@ static void media_print_topology_text_entity(struct media_device *media, + } + } + printf("\n"); ++ ++ free(routes); + } + + static void media_print_topology_text(struct media_device *media) +@@ -594,14 +668,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 +761,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.40.0 + diff --git a/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0003-v4l2-ctl-compliance-add-routing-and-streams-multiple.patch b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0003-v4l2-ctl-compliance-add-routing-and-streams-multiple.patch new file mode 100644 index 00000000..32eeb02a --- /dev/null +++ b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0003-v4l2-ctl-compliance-add-routing-and-streams-multiple.patch @@ -0,0 +1,460 @@ +From 2866c81d2597f47ed976928bc9c27942bbf095f0 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> +Date: Fri, 10 Feb 2023 13:55:46 +0200 +Subject: [PATCH 3/3] v4l2-ctl/compliance: add routing and streams multiplexed + streams + +Add basic support for routing and streams. + +Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> +--- + utils/v4l2-compliance/v4l2-compliance.cpp | 120 ++++++++++++++++---- + utils/v4l2-compliance/v4l2-compliance.h | 8 +- + utils/v4l2-compliance/v4l2-test-subdevs.cpp | 43 ++++++- + 3 files changed, 137 insertions(+), 34 deletions(-) + +diff --git a/utils/v4l2-compliance/v4l2-compliance.cpp b/utils/v4l2-compliance/v4l2-compliance.cpp +index 8aebae2e..63b5fbbb 100644 +--- a/utils/v4l2-compliance/v4l2-compliance.cpp ++++ b/utils/v4l2-compliance/v4l2-compliance.cpp +@@ -1224,6 +1224,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_STREAMS); ++ int ret; + + node.frame_interval_pad = -1; + node.enum_frame_interval_pad = -1; +@@ -1235,6 +1239,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) ? +@@ -1244,32 +1264,82 @@ 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->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 e574c06c..67b3521e 100644 +--- a/utils/v4l2-compliance/v4l2-compliance.h ++++ b/utils/v4l2-compliance/v4l2-compliance.h +@@ -373,10 +373,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 f3d85771..07192bda 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_STREAMS) + + int testSubDevCap(struct node *node) + { +@@ -54,6 +54,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; +@@ -83,6 +84,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; +@@ -128,6 +130,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; +@@ -137,6 +140,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; +@@ -152,6 +156,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)); +@@ -195,7 +200,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; +@@ -204,6 +209,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) { +@@ -214,8 +220,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; +@@ -226,16 +234,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 { +@@ -252,6 +263,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); +@@ -260,7 +272,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; +@@ -268,6 +280,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); +@@ -279,6 +292,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); +@@ -290,20 +304,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)); +@@ -340,7 +359,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; +@@ -349,6 +368,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) { +@@ -359,14 +379,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)); +@@ -379,6 +402,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; +@@ -423,7 +447,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; +@@ -435,10 +459,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; +@@ -451,6 +477,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++) { +@@ -458,6 +485,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; +@@ -469,6 +497,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); +@@ -480,9 +509,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); +@@ -496,6 +527,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))); + } + } +@@ -504,6 +536,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.40.0 + diff --git a/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils_1.24.1.bb b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils_1.24.1.bb index c604ebbc..a2ebb0ea 100644 --- a/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils_1.24.1.bb +++ b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils_1.24.1.bb @@ -31,6 +31,10 @@ SRC_URI = "\ file://0002-original-patch-mediactl-pkgconfig.patch \ file://0003-original-patch-export-mediactl-headers.patch \ file://0004-Do-not-use-getsubopt.patch \ + file://0001-v4l2-ctl-Add-routing-and-streams-support.patch \ + file://0002-media-ctl-add-support-for-routes-and-streams.patch \ + file://0003-v4l2-ctl-compliance-add-routing-and-streams-multiple.patch \ + file://0001-media-ctl-add-support-for-RGBIr-bayer-formats.patch \ " SRC_URI[md5sum] = "8ba9c73c4319b6afab5fa4358edc43de"
Apply patches for routing and multistream APIs, and the new 4x4 RGB-IR bayer format used by the OV2312 camera sensor in TI's 6.1 linux kernel. Signed-off-by: Jai Luthra <j-luthra@ti.com> --- No change in v2 ...-add-support-for-RGBIr-bayer-formats.patch | 62 + ...-ctl-Add-routing-and-streams-support.patch | 619 ++++++++++ ...l-add-support-for-routes-and-streams.patch | 1022 +++++++++++++++++ ...nce-add-routing-and-streams-multiple.patch | 460 ++++++++ .../v4l-utils/v4l-utils_1.24.1.bb | 4 + 5 files changed, 2167 insertions(+) create mode 100644 meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0001-media-ctl-add-support-for-RGBIr-bayer-formats.patch create mode 100644 meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0001-v4l2-ctl-Add-routing-and-streams-support.patch create mode 100644 meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0002-media-ctl-add-support-for-routes-and-streams.patch create mode 100644 meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0003-v4l2-ctl-compliance-add-routing-and-streams-multiple.patch