@@ -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);
Since the chip supports 4 incoming GMSL links allowing for 4 sensors to be connected, we need to add support for streams if we are to use more than one sensor with this deserializer. Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com> --- drivers/staging/media/max96712/max96712.c | 177 +++++++++++++++++++--- 1 file changed, 158 insertions(+), 19 deletions(-)