diff --git a/drivers/staging/media/max96712/max96712.c b/drivers/staging/media/max96712/max96712.c
index f68a1d241b846..a078e4c67c360 100644
--- a/drivers/staging/media/max96712/max96712.c
+++ b/drivers/staging/media/max96712/max96712.c
@@ -13,6 +13,7 @@
 #include <linux/of_graph.h>
 #include <linux/regmap.h>
 
+#include <media/mipi-csi2.h>
 #include <media/v4l2-cci.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-fwnode.h>
@@ -153,6 +154,10 @@
 							 MAX96712_MAX_TX_PORTS + \
 							 MAX96712_MAX_VPG_PORTS)
 
+#define MAX96712_FIRST_SOURCE_PAD			MAX96712_MAX_RX_PORTS
+#define MAX96712_VPG_PAD				(MAX96712_FIRST_SOURCE_PAD + \
+							 MAX96712_MAX_TX_PORTS)
+
 #define MHZ(f)						((f) * 1000000U)
 
 enum max96712_pattern {
@@ -194,6 +199,16 @@ struct max96712_priv {
 	enum max96712_pattern pattern;
 };
 
+static inline bool max96712_pad_is_sink(u32 pad)
+{
+	return pad < MAX96712_FIRST_SOURCE_PAD || pad == MAX96712_VPG_PAD;
+}
+
+static inline bool max96712_pad_is_source(u32 pad)
+{
+	return pad >= MAX96712_FIRST_SOURCE_PAD && pad < MAX96712_VPG_PAD;
+}
+
 static int max96712_write(struct max96712_priv *priv, unsigned int reg, u64 val)
 {
 	int ret;
@@ -364,27 +379,119 @@ static void max96712_pattern_enable(struct max96712_priv *priv, bool enable)
 	}
 }
 
-static int max96712_s_stream(struct v4l2_subdev *sd, int enable)
+static int max96712_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
+				   struct v4l2_mbus_frame_desc *fd)
 {
-	struct max96712_priv *priv = v4l2_get_subdevdata(sd);
+	struct max96712_priv *priv = container_of(sd, struct max96712_priv, sd);
+	struct device *dev = &priv->client->dev;
+	struct v4l2_subdev_state *state;
+	struct v4l2_subdev_route *route;
+	struct media_pad *remote_pad;
+	int ret = 0;
+	int i;
 
-	if (enable) {
-		max96712_pattern_enable(priv, true);
-		max96712_mipi_enable(priv, true);
-	} else {
-		max96712_mipi_enable(priv, false);
-		max96712_pattern_enable(priv, false);
+	if (!max96712_pad_is_source(pad))
+		return -EINVAL;
+
+	memset(fd, 0, sizeof(*fd));
+
+	fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
+
+	state = v4l2_subdev_lock_and_get_active_state(sd);
+
+	for_each_active_route(&state->routing, route) {
+		struct v4l2_mbus_frame_desc_entry *source_entry = NULL;
+		struct v4l2_mbus_frame_desc source_fd = {0};
+
+		if (route->source_pad != pad)
+			continue;
+
+		if (route->sink_pad == MAX96712_VPG_PAD) {
+			fd->entry[fd->num_entries].stream = route->source_stream;
+			fd->entry[fd->num_entries].pixelcode = MEDIA_BUS_FMT_RGB888_1X24;
+			fd->entry[fd->num_entries].bus.csi2.vc = 0;
+			fd->entry[fd->num_entries].bus.csi2.dt = MIPI_CSI2_DT_RGB888;
+			fd->num_entries++;
+			continue;
+		}
+
+		remote_pad = media_pad_remote_pad_first(&priv->pads[route->sink_pad]);
+		if (!remote_pad) {
+			dev_dbg(dev, "no remote pad found for sink pad\n");
+			ret = -EPIPE;
+			goto unlock_state;
+		}
+
+		ret = v4l2_subdev_call(priv->rx_ports[route->sink_pad].sd, pad, get_frame_desc,
+				       remote_pad->index, &source_fd);
+		if (ret) {
+			dev_err(dev, "Failed to get source frame desc for pad %u\n",
+				route->sink_pad);
+
+			goto unlock_state;
+		}
+
+		for (i = 0; i < source_fd.num_entries; i++) {
+			if (source_fd.entry[i].stream == route->sink_stream) {
+				source_entry = &source_fd.entry[i];
+				break;
+			}
+		}
+
+		if (!source_entry) {
+			dev_err(dev, "Failed to find stream from source frame desc\n");
+
+			ret = -EPIPE;
+			goto unlock_state;
+		}
+
+		fd->entry[fd->num_entries].stream = route->source_stream;
+		fd->entry[fd->num_entries].flags = source_entry->flags;
+		fd->entry[fd->num_entries].length = source_entry->length;
+		fd->entry[fd->num_entries].pixelcode = source_entry->pixelcode;
+		fd->entry[fd->num_entries].bus.csi2.vc = source_entry->bus.csi2.vc;
+		fd->entry[fd->num_entries].bus.csi2.dt = source_entry->bus.csi2.dt;
+
+		fd->num_entries++;
 	}
 
+unlock_state:
+	v4l2_subdev_unlock_state(state);
+
+	return ret;
+}
+
+static int max96712_enable_streams(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_state *state,
+				   u32 source_pad, u64 streams_mask)
+{
+	struct max96712_priv *priv = v4l2_get_subdevdata(sd);
+
+	max96712_pattern_enable(priv, true);
+	max96712_mipi_enable(priv, true);
+
+	return 0;
+}
+
+static int max96712_disable_streams(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_state *state,
+				    u32 source_pad, u64 streams_mask)
+{
+	struct max96712_priv *priv = v4l2_get_subdevdata(sd);
+
+	max96712_mipi_enable(priv, false);
+	max96712_pattern_enable(priv, false);
+
 	return 0;
 }
 
 static const struct v4l2_subdev_video_ops max96712_video_ops = {
-	.s_stream = max96712_s_stream,
+	.s_stream = v4l2_subdev_s_stream_helper,
 };
 
-static int max96712_init_state(struct v4l2_subdev *sd,
-			       struct v4l2_subdev_state *state)
+static int _max96712_set_routing(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_state *state,
+				 struct v4l2_subdev_krouting *routing)
 {
 	static const struct v4l2_mbus_framefmt default_fmt = {
 		.width          = 1920,
@@ -396,15 +503,43 @@ static int max96712_init_state(struct v4l2_subdev *sd,
 		.quantization   = V4L2_QUANTIZATION_DEFAULT,
 		.xfer_func      = V4L2_XFER_FUNC_DEFAULT,
 	};
-	struct v4l2_mbus_framefmt *fmt;
-	int i;
+	int ret;
 
-	for (i = 0; i < MAX96712_MAX_PORTS; i++) {
-		fmt = v4l2_subdev_state_get_format(state, i);
-		*fmt = default_fmt;
-	}
+	ret = v4l2_subdev_routing_validate(sd, routing, V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
+	if (ret)
+		return ret;
 
-	return 0;
+	return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &default_fmt);
+}
+
+static int max96712_set_routing(struct v4l2_subdev *sd, struct v4l2_subdev_state *state,
+				enum v4l2_subdev_format_whence which,
+				struct v4l2_subdev_krouting *routing)
+{
+	if (which == V4L2_SUBDEV_FORMAT_ACTIVE && media_entity_is_streaming(&sd->entity))
+		return -EBUSY;
+
+	return _max96712_set_routing(sd, state, routing);
+}
+
+static int max96712_init_state(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_state *state)
+{
+	struct v4l2_subdev_route routes[] = {
+		{
+			.sink_pad = MAX96712_VPG_PAD,
+			.sink_stream = 0,
+			.source_pad = MAX96712_FIRST_SOURCE_PAD,
+			.source_stream = 0,
+			.flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+		},
+	};
+	struct v4l2_subdev_krouting routing = {
+		.num_routes = ARRAY_SIZE(routes),
+		.routes = routes,
+	};
+
+	return _max96712_set_routing(sd, state, &routing);
 }
 
 static const struct v4l2_subdev_internal_ops max96712_internal_ops = {
@@ -414,6 +549,10 @@ static const struct v4l2_subdev_internal_ops max96712_internal_ops = {
 static const struct v4l2_subdev_pad_ops max96712_pad_ops = {
 	.get_fmt = v4l2_subdev_get_fmt,
 	.set_fmt = v4l2_subdev_get_fmt,
+	.enable_streams = max96712_enable_streams,
+	.disable_streams = max96712_disable_streams,
+	.set_routing = max96712_set_routing,
+	.get_frame_desc = max96712_get_frame_desc,
 };
 
 static const struct v4l2_subdev_ops max96712_subdev_ops = {
@@ -453,7 +592,7 @@ static int max96712_v4l2_register(struct max96712_priv *priv)
 
 	priv->sd.internal_ops = &max96712_internal_ops;
 	v4l2_i2c_subdev_init(&priv->sd, priv->client, &max96712_subdev_ops);
-	priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
 	priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
 
 	v4l2_ctrl_handler_init(&priv->ctrl_handler, 2);
