Hi Jai,

Thanks for the patch.

On 25/05/23 16:10, Jai Luthra wrote:
> Apply patches for routing and multistream APIs, and the new 4x4 RGB-IR
> bayer format used by the OV2312 camera sensor in TI's 6.1 linux kernel.
> 
> Signed-off-by: Jai Luthra <[email protected]>

Reviewed-by: Devarsh Thakkar <[email protected]>

Regards
Devarsh

> ---
> 
> No change in v2
> 
>  ...-add-support-for-RGBIr-bayer-formats.patch |   62 +
>  ...-ctl-Add-routing-and-streams-support.patch |  619 ++++++++++
>  ...l-add-support-for-routes-and-streams.patch | 1022 +++++++++++++++++
>  ...nce-add-routing-and-streams-multiple.patch |  460 ++++++++
>  .../v4l-utils/v4l-utils_1.24.1.bb             |    4 +
>  5 files changed, 2167 insertions(+)
>  create mode 100644 
> meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0001-media-ctl-add-support-for-RGBIr-bayer-formats.patch
>  create mode 100644 
> meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0001-v4l2-ctl-Add-routing-and-streams-support.patch
>  create mode 100644 
> meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0002-media-ctl-add-support-for-routes-and-streams.patch
>  create mode 100644 
> meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0003-v4l2-ctl-compliance-add-routing-and-streams-multiple.patch
> 
> diff --git 
> a/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0001-media-ctl-add-support-for-RGBIr-bayer-formats.patch
>  
> b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0001-media-ctl-add-support-for-RGBIr-bayer-formats.patch
> new file mode 100644
> index 00000000..00f54456
> --- /dev/null
> +++ 
> b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils/0001-media-ctl-add-support-for-RGBIr-bayer-formats.patch
> @@ -0,0 +1,62 @@
> +From 26e2a60d29456a9cc6acb16ea19039414808bc5e Mon Sep 17 00:00:00 2001
> +From: Jai Luthra <[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.24.1.bb 
> b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils_1.24.1.bb
> index c604ebbc..a2ebb0ea 100644
> --- a/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils_1.24.1.bb
> +++ b/meta-arago-extras/recipes-multimedia/v4l-utils/v4l-utils_1.24.1.bb
> @@ -31,6 +31,10 @@ SRC_URI = "\
>      file://0002-original-patch-mediactl-pkgconfig.patch \
>      file://0003-original-patch-export-mediactl-headers.patch \
>      file://0004-Do-not-use-getsubopt.patch \
> +    file://0001-v4l2-ctl-Add-routing-and-streams-support.patch \
> +    file://0002-media-ctl-add-support-for-routes-and-streams.patch \
> +    file://0003-v4l2-ctl-compliance-add-routing-and-streams-multiple.patch \
> +    file://0001-media-ctl-add-support-for-RGBIr-bayer-formats.patch \
>  "
>  
>  SRC_URI[md5sum] = "8ba9c73c4319b6afab5fa4358edc43de"


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


Reply via email to