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 <[email protected]>
---
 ...-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.23.0.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 <[email protected]>
+Date: Tue, 5 Jul 2022 16:23:39 +0530
+Subject: [PATCH] media-ctl: add support for RGBIr bayer formats
+
+Signed-off-by: Jai Luthra <[email protected]>
+---
+ 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 <[email protected]>
+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 <[email protected]> and
+Niklas Söderlund <[email protected]>.
+
+Signed-off-by: Tomi Valkeinen <[email protected]>
+---
+ 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 <[email protected]>
+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 <[email protected]>.
+
+Signed-off-by: Tomi Valkeinen <[email protected]>
+---
+ 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 <[email protected]>
+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 <[email protected]>
+---
+ 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.23.0.bb 
b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils_1.23.0.bb
index 21a08022..1c157ed5 100644
--- a/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils_1.23.0.bb
+++ b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils_1.23.0.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 \
 "
 
 SRCREV = "9431e4b26b4842d1401e80ada9f14593dca3a94c"
-- 
2.40.1



-=-=-=-=-=-=-=-=-=-=-=-
Links: You receive all messages sent to this group.
View/Reply Online (#14446): 
https://lists.yoctoproject.org/g/meta-arago/message/14446
Mute This Topic: https://lists.yoctoproject.org/mt/99083825/21656
Group Owner: [email protected]
Unsubscribe: https://lists.yoctoproject.org/g/meta-arago/unsub 
[[email protected]]
-=-=-=-=-=-=-=-=-=-=-=-


Reply via email to