* [PATCH 1/5] media: max9286: Implement multiplexed support
2021-09-18 15:05 [PATCH 0/5] media: Add multiplexed support to R-Car and GMSL Jacopo Mondi
@ 2021-09-18 15:05 ` Jacopo Mondi
2021-09-23 1:31 ` Laurent Pinchart
2021-09-18 15:05 ` [PATCH 2/5] media: max9286: Apply routing configuration Jacopo Mondi
` (3 subsequent siblings)
4 siblings, 1 reply; 8+ messages in thread
From: Jacopo Mondi @ 2021-09-18 15:05 UTC (permalink / raw)
To: tomi.valkeinen, sakari.ailus, laurent.pinchart, niklas.soderlund,
kieran.bingham
Cc: Jacopo Mondi, Mauro Carvalho Chehab, Hans Verkuil,
Manivannan Sadhasivam, Thomas NIZAN, linux-media,
linux-renesas-soc
Implement support for multiplexed streams to the max9286 driver.
Add support for the init_cfg, get_frame_desc and set_routing operations
and move format handling to the subdevice state.
Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
---
drivers/media/i2c/max9286.c | 235 ++++++++++++++++++++++++------------
1 file changed, 160 insertions(+), 75 deletions(-)
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index 1b92d18a1f94..baff86a4dcec 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -476,6 +476,18 @@ static int max9286_check_config_link(struct max9286_priv *priv,
return 0;
}
+static void max9286_init_format(struct v4l2_mbus_framefmt *fmt)
+{
+ fmt->width = 1280;
+ fmt->height = 800;
+ fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ fmt->quantization = V4L2_QUANTIZATION_DEFAULT;
+ fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+}
+
/* -----------------------------------------------------------------------------
* V4L2 Subdev
*/
@@ -745,28 +757,18 @@ static int max9286_enum_mbus_code(struct v4l2_subdev *sd,
return 0;
}
-static struct v4l2_mbus_framefmt *
-max9286_get_pad_format(struct max9286_priv *priv,
- struct v4l2_subdev_state *sd_state,
- unsigned int pad, u32 which)
-{
- switch (which) {
- case V4L2_SUBDEV_FORMAT_TRY:
- return v4l2_subdev_get_try_format(&priv->sd, sd_state, pad);
- case V4L2_SUBDEV_FORMAT_ACTIVE:
- return &priv->fmt[pad];
- default:
- return NULL;
- }
-}
-
static int max9286_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
- struct max9286_priv *priv = sd_to_max9286(sd);
- struct v4l2_mbus_framefmt *cfg_fmt;
+ struct v4l2_mbus_framefmt *fmt;
+ struct v4l2_subdev_state *state;
+ int ret = 0;
+ /*
+ * Refuse to set format on the multiplexed source pad.
+ * Format is propagated from sinks streams to source streams.
+ */
if (format->pad == MAX9286_SRC_PAD)
return -EINVAL;
@@ -782,44 +784,143 @@ static int max9286_set_fmt(struct v4l2_subdev *sd,
break;
}
- cfg_fmt = max9286_get_pad_format(priv, sd_state, format->pad,
- format->which);
- if (!cfg_fmt)
+ state = v4l2_subdev_validate_and_lock_state(sd, sd_state);
+ fmt = v4l2_state_get_stream_format(state, format->pad,
+ format->stream);
+ if (!fmt) {
+ ret = -EINVAL;
+ goto out;
+ }
+ *fmt = format->format;
+
+ /* Propagate format to the other end of the route. */
+ fmt = v4l2_state_get_opposite_stream_format(state, format->pad,
+ format->stream);
+ if (!fmt) {
+ ret = -EINVAL;
+ goto out;
+ }
+ *fmt = format->format;
+
+out:
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+}
+
+static int max9286_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_mbus_frame_desc *fd)
+{
+ const struct v4l2_subdev_krouting *routing;
+ struct v4l2_subdev_state *state;
+ unsigned int i;
+ int ret = 0;
+
+ if (pad != MAX9286_SRC_PAD)
return -EINVAL;
- mutex_lock(&priv->mutex);
- *cfg_fmt = format->format;
- mutex_unlock(&priv->mutex);
+ state = v4l2_subdev_lock_active_state(sd);
+ routing = &state->routing;
- return 0;
+ /* One stream per each connected route. */
+ memset(fd, 0, sizeof(*fd));
+ fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
+
+ for (i = 0; i < routing->num_routes; ++i) {
+ const struct v4l2_subdev_route *route = &routing->routes[i];
+ unsigned int idx = fd->num_entries;
+ struct v4l2_mbus_framefmt *fmt;
+
+ if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE))
+ continue;
+
+ if (route->source_pad != pad)
+ continue;
+
+ fmt = v4l2_state_get_stream_format(state, pad,
+ route->source_stream);
+ if (!fmt) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Assume a YUYV format (0x1e DT) and 16 bpp: we only support
+ * these formats at the moment.
+ */
+ fd->entry[idx].stream = idx;
+ fd->entry[idx].flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX;
+ fd->entry[idx].length = fmt->width * fmt->height * 16 / 8;
+ fd->entry[idx].pixelcode = fmt->code;
+
+ fd->entry[idx].bus.csi2.vc = route->source_stream;
+ fd->entry[idx].bus.csi2.dt = 0x1e;
+
+ fd->num_entries++;
+ }
+
+out:
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
}
-static int max9286_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *format)
+static int max9286_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_krouting *routing)
{
- struct max9286_priv *priv = sd_to_max9286(sd);
- struct v4l2_mbus_framefmt *cfg_fmt;
- unsigned int pad = format->pad;
+ struct v4l2_mbus_framefmt format;
+ int ret;
/*
- * Multiplexed Stream Support: Support link validation by returning the
- * format of the first bound link. All links must have the same format,
- * as we do not support mixing and matching of cameras connected to the
- * max9286.
+ * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until
+ * frame desc is made dynamically allocated.
*/
- if (pad == MAX9286_SRC_PAD)
- pad = __ffs(priv->bound_sources);
- cfg_fmt = max9286_get_pad_format(priv, sd_state, pad, format->which);
- if (!cfg_fmt)
+ if (routing->num_routes >= V4L2_FRAME_DESC_ENTRY_MAX)
return -EINVAL;
- mutex_lock(&priv->mutex);
- format->format = *cfg_fmt;
- mutex_unlock(&priv->mutex);
+ ret = v4l2_routing_simple_verify(routing);
+ if (ret)
+ return ret;
- return 0;
+ state = v4l2_subdev_validate_and_lock_state(sd, state);
+
+ max9286_init_format(&format);
+ ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format);
+
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+}
+
+static int max9286_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_subdev_route routes[MAX9286_NUM_GMSL];
+ struct max9286_priv *priv = sd_to_max9286(sd);
+ struct v4l2_subdev_krouting routing;
+ struct max9286_source *source;
+ unsigned int num_routes = 0;
+ u32 which = state->which;
+
+ /* Create a route for each enable source. */
+ for_each_source(priv, source) {
+ struct v4l2_subdev_route *route = &routes[num_routes++];
+ unsigned int idx = to_index(priv, source);
+
+ route->sink_pad = idx;
+ route->sink_stream = 0;
+ route->source_pad = MAX9286_SRC_PAD;
+ route->source_stream = idx;
+ route->flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE;
+ }
+
+ routing.which = which;
+ routing.num_routes = num_routes;
+ routing.routes = routes;
+
+ return max9286_set_routing(sd, state, &routing);
}
static const struct v4l2_subdev_video_ops max9286_video_ops = {
@@ -827,9 +928,12 @@ static const struct v4l2_subdev_video_ops max9286_video_ops = {
};
static const struct v4l2_subdev_pad_ops max9286_pad_ops = {
+ .init_cfg = max9286_init_cfg,
.enum_mbus_code = max9286_enum_mbus_code,
- .get_fmt = max9286_get_fmt,
+ .get_fmt = v4l2_subdev_get_fmt,
.set_fmt = max9286_set_fmt,
+ .get_frame_desc = max9286_get_frame_desc,
+ .set_routing = max9286_set_routing,
};
static const struct v4l2_subdev_ops max9286_subdev_ops = {
@@ -837,35 +941,6 @@ static const struct v4l2_subdev_ops max9286_subdev_ops = {
.pad = &max9286_pad_ops,
};
-static void max9286_init_format(struct v4l2_mbus_framefmt *fmt)
-{
- fmt->width = 1280;
- fmt->height = 800;
- fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
- fmt->colorspace = V4L2_COLORSPACE_SRGB;
- fmt->field = V4L2_FIELD_NONE;
- fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
- fmt->quantization = V4L2_QUANTIZATION_DEFAULT;
- fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
-}
-
-static int max9286_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
-{
- struct v4l2_mbus_framefmt *format;
- unsigned int i;
-
- for (i = 0; i < MAX9286_N_SINKS; i++) {
- format = v4l2_subdev_get_try_format(subdev, fh->state, i);
- max9286_init_format(format);
- }
-
- return 0;
-}
-
-static const struct v4l2_subdev_internal_ops max9286_subdev_internal_ops = {
- .open = max9286_open,
-};
-
static int max9286_s_ctrl(struct v4l2_ctrl *ctrl)
{
switch (ctrl->id) {
@@ -900,8 +975,8 @@ static int max9286_v4l2_register(struct max9286_priv *priv)
max9286_init_format(&priv->fmt[i]);
v4l2_i2c_subdev_init(&priv->sd, priv->client, &max9286_subdev_ops);
- priv->sd.internal_ops = &max9286_subdev_internal_ops;
- priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+ V4L2_SUBDEV_FL_MULTIPLEXED;
v4l2_ctrl_handler_init(&priv->ctrls, 1);
priv->pixelrate = v4l2_ctrl_new_std(&priv->ctrls,
@@ -933,14 +1008,23 @@ static int max9286_v4l2_register(struct max9286_priv *priv)
}
priv->sd.fwnode = ep;
+ ret = v4l2_subdev_alloc_state(&priv->sd);
+ if (ret) {
+ dev_err(dev, "Unable to allocate subdevice state/\n");
+ ret = -ENOENT;
+ goto err_put_node;
+ }
+
ret = v4l2_async_register_subdev(&priv->sd);
if (ret < 0) {
dev_err(dev, "Unable to register subdevice\n");
- goto err_put_node;
+ goto err_free_state;
}
return 0;
+err_free_state:
+ v4l2_subdev_free_state(&priv->sd);
err_put_node:
fwnode_handle_put(ep);
err_async:
@@ -952,6 +1036,7 @@ static int max9286_v4l2_register(struct max9286_priv *priv)
static void max9286_v4l2_unregister(struct max9286_priv *priv)
{
fwnode_handle_put(priv->sd.fwnode);
+ v4l2_subdev_free_state(&priv->sd);
v4l2_async_unregister_subdev(&priv->sd);
max9286_v4l2_notifier_unregister(priv);
}
--
2.32.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 1/5] media: max9286: Implement multiplexed support
2021-09-18 15:05 ` [PATCH 1/5] media: max9286: Implement multiplexed support Jacopo Mondi
@ 2021-09-23 1:31 ` Laurent Pinchart
0 siblings, 0 replies; 8+ messages in thread
From: Laurent Pinchart @ 2021-09-23 1:31 UTC (permalink / raw)
To: Jacopo Mondi
Cc: tomi.valkeinen, sakari.ailus, niklas.soderlund, kieran.bingham,
Mauro Carvalho Chehab, Hans Verkuil, Manivannan Sadhasivam,
Thomas NIZAN, linux-media, linux-renesas-soc
Hi Jacopo,
Thank you for the patch.
On Sat, Sep 18, 2021 at 05:05:03PM +0200, Jacopo Mondi wrote:
> Implement support for multiplexed streams to the max9286 driver.
>
> Add support for the init_cfg, get_frame_desc and set_routing operations
> and move format handling to the subdevice state.
It may make sense to split part of this out (namely the switch to
.init_cfg() and the move to subdevice state, either as separate patches
or grouped together).
> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> ---
> drivers/media/i2c/max9286.c | 235 ++++++++++++++++++++++++------------
> 1 file changed, 160 insertions(+), 75 deletions(-)
>
> diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
> index 1b92d18a1f94..baff86a4dcec 100644
> --- a/drivers/media/i2c/max9286.c
> +++ b/drivers/media/i2c/max9286.c
> @@ -476,6 +476,18 @@ static int max9286_check_config_link(struct max9286_priv *priv,
> return 0;
> }
>
> +static void max9286_init_format(struct v4l2_mbus_framefmt *fmt)
> +{
> + fmt->width = 1280;
> + fmt->height = 800;
> + fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
> + fmt->colorspace = V4L2_COLORSPACE_SRGB;
> + fmt->field = V4L2_FIELD_NONE;
> + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> + fmt->quantization = V4L2_QUANTIZATION_DEFAULT;
> + fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
> +}
> +
> /* -----------------------------------------------------------------------------
> * V4L2 Subdev
> */
> @@ -745,28 +757,18 @@ static int max9286_enum_mbus_code(struct v4l2_subdev *sd,
> return 0;
> }
>
> -static struct v4l2_mbus_framefmt *
> -max9286_get_pad_format(struct max9286_priv *priv,
> - struct v4l2_subdev_state *sd_state,
> - unsigned int pad, u32 which)
> -{
> - switch (which) {
> - case V4L2_SUBDEV_FORMAT_TRY:
> - return v4l2_subdev_get_try_format(&priv->sd, sd_state, pad);
> - case V4L2_SUBDEV_FORMAT_ACTIVE:
> - return &priv->fmt[pad];
> - default:
> - return NULL;
> - }
> -}
> -
> static int max9286_set_fmt(struct v4l2_subdev *sd,
> struct v4l2_subdev_state *sd_state,
> struct v4l2_subdev_format *format)
> {
> - struct max9286_priv *priv = sd_to_max9286(sd);
> - struct v4l2_mbus_framefmt *cfg_fmt;
> + struct v4l2_mbus_framefmt *fmt;
> + struct v4l2_subdev_state *state;
> + int ret = 0;
>
> + /*
> + * Refuse to set format on the multiplexed source pad.
> + * Format is propagated from sinks streams to source streams.
> + */
> if (format->pad == MAX9286_SRC_PAD)
> return -EINVAL;
>
> @@ -782,44 +784,143 @@ static int max9286_set_fmt(struct v4l2_subdev *sd,
> break;
> }
>
> - cfg_fmt = max9286_get_pad_format(priv, sd_state, format->pad,
> - format->which);
> - if (!cfg_fmt)
> + state = v4l2_subdev_validate_and_lock_state(sd, sd_state);
I really want RAII semantics... __attribute__((__cleanup__)) would give
us that. It's used in two places in the kernel (lib/locking-selftest.c
and include/linux/kcsan-checks.h), but I'm not sure it's supported by
all compilers (namely ICC) :-S
> + fmt = v4l2_state_get_stream_format(state, format->pad,
> + format->stream);
> + if (!fmt) {
> + ret = -EINVAL;
> + goto out;
> + }
> + *fmt = format->format;
> +
> + /* Propagate format to the other end of the route. */
> + fmt = v4l2_state_get_opposite_stream_format(state, format->pad,
> + format->stream);
> + if (!fmt) {
> + ret = -EINVAL;
> + goto out;
> + }
> + *fmt = format->format;
> +
> +out:
> + v4l2_subdev_unlock_state(state);
> +
> + return ret;
> +}
> +
> +static int max9286_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
> + struct v4l2_mbus_frame_desc *fd)
> +{
> + const struct v4l2_subdev_krouting *routing;
> + struct v4l2_subdev_state *state;
> + unsigned int i;
> + int ret = 0;
> +
> + if (pad != MAX9286_SRC_PAD)
> return -EINVAL;
>
> - mutex_lock(&priv->mutex);
> - *cfg_fmt = format->format;
> - mutex_unlock(&priv->mutex);
> + state = v4l2_subdev_lock_active_state(sd);
> + routing = &state->routing;
>
> - return 0;
> + /* One stream per each connected route. */
> + memset(fd, 0, sizeof(*fd));
Should the memset() be done by the caller ?
> + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
> +
> + for (i = 0; i < routing->num_routes; ++i) {
> + const struct v4l2_subdev_route *route = &routing->routes[i];
> + unsigned int idx = fd->num_entries;
> + struct v4l2_mbus_framefmt *fmt;
> +
> + if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE))
> + continue;
Do we need a for_each_active_route(routing, route) macro ?
> +
> + if (route->source_pad != pad)
> + continue;
As we have a single source pad, is this possible ?
> +
> + fmt = v4l2_state_get_stream_format(state, pad,
> + route->source_stream);
> + if (!fmt) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + /*
> + * Assume a YUYV format (0x1e DT) and 16 bpp: we only support
> + * these formats at the moment.
> + */
I would have defined a
struct v4l2_mbus_frame_desc_entry *entry;
with
entry = &fd->entry[fd->num_entries];
here.
> + fd->entry[idx].stream = idx;
> + fd->entry[idx].flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX;
> + fd->entry[idx].length = fmt->width * fmt->height * 16 / 8;
> + fd->entry[idx].pixelcode = fmt->code;
> +
> + fd->entry[idx].bus.csi2.vc = route->source_stream;
A comment to explain that we hardcode the VC of each input to the input
number would be useful.
> + fd->entry[idx].bus.csi2.dt = 0x1e;
We should define macros for the CSI-2 data types in a common header in
include/media/.
> +
> + fd->num_entries++;
> + }
> +
> +out:
> + v4l2_subdev_unlock_state(state);
> +
> + return ret;
> }
>
> -static int max9286_get_fmt(struct v4l2_subdev *sd,
> - struct v4l2_subdev_state *sd_state,
> - struct v4l2_subdev_format *format)
> +static int max9286_set_routing(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *state,
> + struct v4l2_subdev_krouting *routing)
> {
> - struct max9286_priv *priv = sd_to_max9286(sd);
> - struct v4l2_mbus_framefmt *cfg_fmt;
> - unsigned int pad = format->pad;
> + struct v4l2_mbus_framefmt format;
> + int ret;
>
> /*
> - * Multiplexed Stream Support: Support link validation by returning the
> - * format of the first bound link. All links must have the same format,
> - * as we do not support mixing and matching of cameras connected to the
> - * max9286.
> + * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until
> + * frame desc is made dynamically allocated.
> */
> - if (pad == MAX9286_SRC_PAD)
> - pad = __ffs(priv->bound_sources);
>
> - cfg_fmt = max9286_get_pad_format(priv, sd_state, pad, format->which);
> - if (!cfg_fmt)
> + if (routing->num_routes >= V4L2_FRAME_DESC_ENTRY_MAX)
> return -EINVAL;
Where does this limitation come from ? It doesn't seem to originate from
the max9286 driver itself. If it's related to one of the helpers below,
maybe that's where it should be checked ?
>
> - mutex_lock(&priv->mutex);
> - format->format = *cfg_fmt;
> - mutex_unlock(&priv->mutex);
> + ret = v4l2_routing_simple_verify(routing);
> + if (ret)
> + return ret;
>
> - return 0;
> + state = v4l2_subdev_validate_and_lock_state(sd, state);
> +
> + max9286_init_format(&format);
You could replace this with a static const v4l2_mbus_framefmt variable
that contains the default format.
> + ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format);
> +
> + v4l2_subdev_unlock_state(state);
> +
> + return ret;
> +}
> +
> +static int max9286_init_cfg(struct v4l2_subdev *sd,
> + struct v4l2_subdev_state *state)
> +{
> + struct v4l2_subdev_route routes[MAX9286_NUM_GMSL];
> + struct max9286_priv *priv = sd_to_max9286(sd);
> + struct v4l2_subdev_krouting routing;
> + struct max9286_source *source;
> + unsigned int num_routes = 0;
> + u32 which = state->which;
> +
> + /* Create a route for each enable source. */
> + for_each_source(priv, source) {
> + struct v4l2_subdev_route *route = &routes[num_routes++];
> + unsigned int idx = to_index(priv, source);
> +
> + route->sink_pad = idx;
> + route->sink_stream = 0;
> + route->source_pad = MAX9286_SRC_PAD;
> + route->source_stream = idx;
> + route->flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE;
> + }
> +
> + routing.which = which;
> + routing.num_routes = num_routes;
> + routing.routes = routes;
> +
> + return max9286_set_routing(sd, state, &routing);
> }
>
> static const struct v4l2_subdev_video_ops max9286_video_ops = {
> @@ -827,9 +928,12 @@ static const struct v4l2_subdev_video_ops max9286_video_ops = {
> };
>
> static const struct v4l2_subdev_pad_ops max9286_pad_ops = {
> + .init_cfg = max9286_init_cfg,
> .enum_mbus_code = max9286_enum_mbus_code,
> - .get_fmt = max9286_get_fmt,
> + .get_fmt = v4l2_subdev_get_fmt,
That's so nice :-)
> .set_fmt = max9286_set_fmt,
> + .get_frame_desc = max9286_get_frame_desc,
> + .set_routing = max9286_set_routing,
> };
>
> static const struct v4l2_subdev_ops max9286_subdev_ops = {
> @@ -837,35 +941,6 @@ static const struct v4l2_subdev_ops max9286_subdev_ops = {
> .pad = &max9286_pad_ops,
> };
>
> -static void max9286_init_format(struct v4l2_mbus_framefmt *fmt)
> -{
> - fmt->width = 1280;
> - fmt->height = 800;
> - fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
> - fmt->colorspace = V4L2_COLORSPACE_SRGB;
> - fmt->field = V4L2_FIELD_NONE;
> - fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> - fmt->quantization = V4L2_QUANTIZATION_DEFAULT;
> - fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
> -}
> -
> -static int max9286_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
> -{
> - struct v4l2_mbus_framefmt *format;
> - unsigned int i;
> -
> - for (i = 0; i < MAX9286_N_SINKS; i++) {
> - format = v4l2_subdev_get_try_format(subdev, fh->state, i);
> - max9286_init_format(format);
> - }
> -
> - return 0;
> -}
> -
> -static const struct v4l2_subdev_internal_ops max9286_subdev_internal_ops = {
> - .open = max9286_open,
> -};
> -
> static int max9286_s_ctrl(struct v4l2_ctrl *ctrl)
> {
> switch (ctrl->id) {
> @@ -900,8 +975,8 @@ static int max9286_v4l2_register(struct max9286_priv *priv)
> max9286_init_format(&priv->fmt[i]);
As far as I can tell, priv->fmt isn't used anymore, so you can drop it,
as well as the code above. The MAX9286_N_SINKS macro can also be
dropped.
>
> v4l2_i2c_subdev_init(&priv->sd, priv->client, &max9286_subdev_ops);
> - priv->sd.internal_ops = &max9286_subdev_internal_ops;
> - priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> + priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
> + V4L2_SUBDEV_FL_MULTIPLEXED;
>
> v4l2_ctrl_handler_init(&priv->ctrls, 1);
> priv->pixelrate = v4l2_ctrl_new_std(&priv->ctrls,
> @@ -933,14 +1008,23 @@ static int max9286_v4l2_register(struct max9286_priv *priv)
> }
> priv->sd.fwnode = ep;
>
> + ret = v4l2_subdev_alloc_state(&priv->sd);
> + if (ret) {
> + dev_err(dev, "Unable to allocate subdevice state/\n");
> + ret = -ENOENT;
> + goto err_put_node;
> + }
> +
> ret = v4l2_async_register_subdev(&priv->sd);
> if (ret < 0) {
> dev_err(dev, "Unable to register subdevice\n");
> - goto err_put_node;
> + goto err_free_state;
> }
>
> return 0;
>
> +err_free_state:
> + v4l2_subdev_free_state(&priv->sd);
> err_put_node:
> fwnode_handle_put(ep);
> err_async:
> @@ -952,6 +1036,7 @@ static int max9286_v4l2_register(struct max9286_priv *priv)
> static void max9286_v4l2_unregister(struct max9286_priv *priv)
> {
> fwnode_handle_put(priv->sd.fwnode);
> + v4l2_subdev_free_state(&priv->sd);
> v4l2_async_unregister_subdev(&priv->sd);
> max9286_v4l2_notifier_unregister(priv);
> }
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 2/5] media: max9286: Apply routing configuration
2021-09-18 15:05 [PATCH 0/5] media: Add multiplexed support to R-Car and GMSL Jacopo Mondi
2021-09-18 15:05 ` [PATCH 1/5] media: max9286: Implement multiplexed support Jacopo Mondi
@ 2021-09-18 15:05 ` Jacopo Mondi
2021-09-18 15:05 ` [PATCH 3/5] media: max9286: Implement routing validation Jacopo Mondi
` (2 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Jacopo Mondi @ 2021-09-18 15:05 UTC (permalink / raw)
To: tomi.valkeinen, sakari.ailus, laurent.pinchart, niklas.soderlund,
kieran.bingham
Cc: Jacopo Mondi, Mauro Carvalho Chehab, Hans Verkuil,
Manivannan Sadhasivam, Thomas NIZAN, linux-media,
linux-renesas-soc
Use the routing table to configure the CSI-2 output.
Assing to each enabled GMSL link a CSI-2 Virtual Channel identifier and
mask links with not route associated. Update the pixel rate control
according to the active routes.
Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
---
drivers/media/i2c/max9286.c | 189 +++++++++++++++++++++++++-----------
1 file changed, 133 insertions(+), 56 deletions(-)
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index baff86a4dcec..79af880147d0 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -189,8 +189,11 @@ struct max9286_priv {
struct v4l2_async_notifier notifier;
};
+#define to_index(priv, source) ((source) - &(priv)->sources[0])
+
static struct max9286_source *next_source(struct max9286_priv *priv,
- struct max9286_source *source)
+ struct max9286_source *source,
+ bool routed)
{
if (!source)
source = &priv->sources[0];
@@ -198,17 +201,27 @@ static struct max9286_source *next_source(struct max9286_priv *priv,
source++;
for (; source < &priv->sources[MAX9286_NUM_GMSL]; source++) {
- if (source->fwnode)
+ unsigned int index = to_index(priv, source);
+
+ if (!source->fwnode)
+ continue;
+
+ /*
+ * Careful here! A very unfortunate call to set_routing() can
+ * change priv->route_mask behind our back!
+ */
+ if (!routed || priv->route_mask & BIT(index))
return source;
}
return NULL;
}
-#define for_each_source(priv, source) \
- for ((source) = NULL; ((source) = next_source((priv), (source))); )
+#define for_each_route(priv, source) \
+ for ((source) = NULL; ((source) = next_source((priv), (source), true)); )
-#define to_index(priv, source) ((source) - &(priv)->sources[0])
+#define for_each_source(priv, source) \
+ for ((source) = NULL; ((source) = next_source((priv), (source), false)); )
static inline struct max9286_priv *sd_to_max9286(struct v4l2_subdev *sd)
{
@@ -399,7 +412,7 @@ static int max9286_check_video_links(struct max9286_priv *priv)
if (ret < 0)
return -EIO;
- if ((ret & MAX9286_VIDEO_DETECT_MASK) == priv->source_mask)
+ if ((ret & MAX9286_VIDEO_DETECT_MASK) == priv->route_mask)
break;
usleep_range(350, 500);
@@ -476,6 +489,67 @@ static int max9286_check_config_link(struct max9286_priv *priv,
return 0;
}
+/*
+ * Configure the links output order (aka CSI-2 VC) and update the enabled routes
+ * mask according to the routing configuration.
+ */
+static void max9286_config_routes(struct max9286_priv *priv,
+ struct v4l2_subdev_state *state)
+{
+ const struct v4l2_subdev_krouting *routing;
+ u8 link_order = 0;
+ u8 vc_mask = 0xf;
+ unsigned int i;
+
+ routing = &state->routing;
+ priv->route_mask = 0;
+
+ for (i = 0; i < routing->num_routes; ++i) {
+ struct v4l2_subdev_route *route = &routing->routes[i];
+
+ if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE))
+ continue;
+
+ /*
+ * Mark the route enabled and assign the CSI-2 VC using the
+ * source stream number.
+ */
+ priv->route_mask |= BIT(route->sink_pad);
+ link_order |= route->source_stream << (2 * route->sink_pad);
+ vc_mask &= ~BIT(route->source_stream);
+ }
+
+ /*
+ * This might look rather silly, but now that we have assigned a
+ * VC to the enabled routes, we have to assign one to the disabled
+ * routes as well, as there cannot be two sources with the same VC.
+ */
+ for (i = 0; i < MAX9286_NUM_GMSL; ++i) {
+ unsigned int vc;
+
+ if (priv->route_mask & BIT(i))
+ continue;
+
+ /*
+ * ffs() counts from 1; assign it here to avoid subtracting 1
+ * from 0.
+ */
+ vc = ffs(vc_mask) - 1;
+ link_order |= vc << 2 * i;
+ vc_mask &= ~BIT(vc);
+ }
+
+ /*
+ * Use the enabled routes to enable GMSL links, configure the CSI-2
+ * output order, mask unused links and autodetect link used as CSI
+ * clock source.
+ */
+ max9286_write(priv, 0x00, MAX9286_MSTLINKSEL_AUTO | priv->route_mask);
+ max9286_write(priv, 0x0b, link_order);
+ max9286_write(priv, 0x14, link_order);
+ max9286_write(priv, 0x69, 0xf & ~priv->route_mask);
+}
+
static void max9286_init_format(struct v4l2_mbus_framefmt *fmt)
{
fmt->width = 1280;
@@ -495,9 +569,10 @@ static void max9286_init_format(struct v4l2_mbus_framefmt *fmt)
static int max9286_set_pixelrate(struct max9286_priv *priv)
{
struct max9286_source *source = NULL;
+ unsigned int num_routes = 0;
u64 pixelrate = 0;
- for_each_source(priv, source) {
+ for_each_route(priv, source) {
struct v4l2_ctrl *ctrl;
u64 source_rate = 0;
@@ -518,6 +593,8 @@ static int max9286_set_pixelrate(struct max9286_priv *priv)
"Unable to calculate pixel rate\n");
return -EINVAL;
}
+
+ num_routes++;
}
if (!pixelrate) {
@@ -531,7 +608,7 @@ static int max9286_set_pixelrate(struct max9286_priv *priv)
* by the number of available sources.
*/
return v4l2_ctrl_s_ctrl_int64(priv->pixelrate,
- pixelrate * priv->nsources);
+ pixelrate * num_routes);
}
static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
@@ -693,8 +770,8 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
*/
max9286_i2c_mux_open(priv);
- /* Start all cameras. */
- for_each_source(priv, source) {
+ /* Start cameras. */
+ for_each_route(priv, source) {
ret = v4l2_subdev_call(source->sd, video, s_stream, 1);
if (ret)
return ret;
@@ -735,8 +812,8 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
} else {
max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV);
- /* Stop all cameras. */
- for_each_source(priv, source)
+ /* Stop cameras. */
+ for_each_route(priv, source)
v4l2_subdev_call(source->sd, video, s_stream, 0);
max9286_i2c_mux_close(priv);
@@ -865,10 +942,11 @@ static int max9286_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
return ret;
}
-static int max9286_set_routing(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *state,
- struct v4l2_subdev_krouting *routing)
+static int __max9286_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_krouting *routing)
{
+ struct max9286_priv *priv = sd_to_max9286(sd);
struct v4l2_mbus_framefmt format;
int ret;
@@ -884,11 +962,33 @@ static int max9286_set_routing(struct v4l2_subdev *sd,
if (ret)
return ret;
- state = v4l2_subdev_validate_and_lock_state(sd, state);
-
max9286_init_format(&format);
ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format);
+ if (ret)
+ return ret;
+
+ if (state->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ max9286_config_routes(priv, state);
+
+ return 0;
+}
+
+static int max9286_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_krouting *routing)
+{
+ struct max9286_priv *priv = sd_to_max9286(sd);
+ int ret;
+
+ state = v4l2_subdev_validate_and_lock_state(sd, state);
+ ret = __max9286_set_routing(sd, state, routing);
+ if (ret)
+ goto out;
+ if (state->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ max9286_set_pixelrate(priv);
+
+out:
v4l2_subdev_unlock_state(state);
return ret;
@@ -903,6 +1003,7 @@ static int max9286_init_cfg(struct v4l2_subdev *sd,
struct max9286_source *source;
unsigned int num_routes = 0;
u32 which = state->which;
+ int ret;
/* Create a route for each enable source. */
for_each_source(priv, source) {
@@ -920,7 +1021,11 @@ static int max9286_init_cfg(struct v4l2_subdev *sd,
routing.num_routes = num_routes;
routing.routes = routes;
- return max9286_set_routing(sd, state, &routing);
+ state = v4l2_subdev_validate_and_lock_state(sd, state);
+ ret = __max9286_set_routing(sd, state, &routing);
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
}
static const struct v4l2_subdev_video_ops max9286_video_ops = {
@@ -1047,31 +1152,7 @@ static void max9286_v4l2_unregister(struct max9286_priv *priv)
static int max9286_setup(struct max9286_priv *priv)
{
- /*
- * Link ordering values for all enabled links combinations. Orders must
- * be assigned sequentially from 0 to the number of enabled links
- * without leaving any hole for disabled links. We thus assign orders to
- * enabled links first, and use the remaining order values for disabled
- * links are all links must have a different order value;
- */
- static const u8 link_order[] = {
- (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxxx */
- (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxx0 */
- (3 << 6) | (2 << 4) | (0 << 2) | (1 << 0), /* xx0x */
- (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xx10 */
- (3 << 6) | (0 << 4) | (2 << 2) | (1 << 0), /* x0xx */
- (3 << 6) | (1 << 4) | (2 << 2) | (0 << 0), /* x1x0 */
- (3 << 6) | (1 << 4) | (0 << 2) | (2 << 0), /* x10x */
- (3 << 6) | (1 << 4) | (1 << 2) | (0 << 0), /* x210 */
- (0 << 6) | (3 << 4) | (2 << 2) | (1 << 0), /* 0xxx */
- (1 << 6) | (3 << 4) | (2 << 2) | (0 << 0), /* 1xx0 */
- (1 << 6) | (3 << 4) | (0 << 2) | (2 << 0), /* 1x0x */
- (2 << 6) | (3 << 4) | (1 << 2) | (0 << 0), /* 2x10 */
- (1 << 6) | (0 << 4) | (3 << 2) | (2 << 0), /* 10xx */
- (2 << 6) | (1 << 4) | (3 << 2) | (0 << 0), /* 21x0 */
- (2 << 6) | (1 << 4) | (0 << 2) | (3 << 0), /* 210x */
- (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* 3210 */
- };
+ struct v4l2_subdev_state *state;
/*
* Set the I2C bus speed.
@@ -1082,13 +1163,9 @@ static int max9286_setup(struct max9286_priv *priv)
max9286_configure_i2c(priv, true);
max9286_reverse_channel_setup(priv, priv->init_rev_chan_mv);
- /*
- * Enable GMSL links, mask unused ones and autodetect link
- * used as CSI clock source.
- */
- max9286_write(priv, 0x00, MAX9286_MSTLINKSEL_AUTO | priv->route_mask);
- max9286_write(priv, 0x0b, link_order[priv->route_mask]);
- max9286_write(priv, 0x69, (0xf & ~priv->route_mask));
+ state = v4l2_subdev_lock_active_state(&priv->sd);
+ max9286_config_routes(priv, state);
+ v4l2_subdev_unlock_state(state);
/*
* Video format setup:
@@ -1262,12 +1339,6 @@ static int max9286_init(struct device *dev)
if (ret)
return ret;
- ret = max9286_setup(priv);
- if (ret) {
- dev_err(dev, "Unable to setup max9286\n");
- goto err_poc_disable;
- }
-
/*
* Register all V4L2 interactions for the MAX9286 and notifiers for
* any subdevices connected.
@@ -1278,6 +1349,12 @@ static int max9286_init(struct device *dev)
goto err_poc_disable;
}
+ ret = max9286_setup(priv);
+ if (ret) {
+ dev_err(dev, "Unable to setup max9286\n");
+ goto err_v4l2_register;
+ }
+
ret = max9286_i2c_mux_init(priv);
if (ret) {
dev_err(dev, "Unable to initialize I2C multiplexer\n");
--
2.32.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 3/5] media: max9286: Implement routing validation
2021-09-18 15:05 [PATCH 0/5] media: Add multiplexed support to R-Car and GMSL Jacopo Mondi
2021-09-18 15:05 ` [PATCH 1/5] media: max9286: Implement multiplexed support Jacopo Mondi
2021-09-18 15:05 ` [PATCH 2/5] media: max9286: Apply routing configuration Jacopo Mondi
@ 2021-09-18 15:05 ` Jacopo Mondi
2021-09-18 15:05 ` [PATCH 4/5] media: rcar-csi2: Implement multiplexed support Jacopo Mondi
2021-09-18 15:05 ` [PATCH 5/5] media: rcar-vin: Support multiplexed CSI-2 receiver Jacopo Mondi
4 siblings, 0 replies; 8+ messages in thread
From: Jacopo Mondi @ 2021-09-18 15:05 UTC (permalink / raw)
To: tomi.valkeinen, sakari.ailus, laurent.pinchart, niklas.soderlund,
kieran.bingham
Cc: Jacopo Mondi, Mauro Carvalho Chehab, Hans Verkuil,
Manivannan Sadhasivam, Thomas NIZAN, linux-media,
linux-renesas-soc
Validate the routing table by making sure all the routes start from a
sink pad and end in a source pad and that the stream numbers are
correct.
Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
---
drivers/media/i2c/max9286.c | 50 ++++++++++++++++++++++++++++++++++++-
1 file changed, 49 insertions(+), 1 deletion(-)
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index 79af880147d0..931c4f542c77 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -942,6 +942,54 @@ static int max9286_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
return ret;
}
+static int max9286_routing_verify(struct max9286_priv *priv,
+ struct v4l2_subdev_krouting *routing)
+{
+ unsigned int i;
+ int ret;
+
+ ret = v4l2_routing_simple_verify(routing);
+ if (ret)
+ return ret;
+
+ /*
+ * Make sure all routes points to the single source pad which can have
+ * up to 4 streams. All routes shall start from a sink pad and shall not
+ * have more than one sink stream. The GMSL link for the sink has to be
+ * enabled.
+ */
+ for (i = 0; i < routing->num_routes; ++i) {
+ const struct v4l2_subdev_route *route = &routing->routes[i];
+ struct max9286_source *source = &priv->sources[i];
+
+ if (route->source_pad != MAX9286_SRC_PAD ||
+ route->source_stream > 4) {
+ dev_err(&priv->client->dev,
+ "Invalid (%u,%u) source in route %u\n",
+ route->source_pad, route->source_stream, i);
+ return -EINVAL;
+ }
+
+ if (route->sink_pad >= MAX9286_N_SINKS ||
+ route->sink_stream != 0) {
+ dev_err(&priv->client->dev,
+ "Invalid (%u,%u) sink in route %u\n",
+ route->sink_pad, route->sink_stream, i);
+ return -EINVAL;
+ }
+
+ source = &priv->sources[route->sink_pad];
+ if (!source->fwnode) {
+ dev_err(&priv->client->dev,
+ "Cannot set route for non-active source %u\n",
+ route->sink_pad);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
static int __max9286_set_routing(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_krouting *routing)
@@ -958,7 +1006,7 @@ static int __max9286_set_routing(struct v4l2_subdev *sd,
if (routing->num_routes >= V4L2_FRAME_DESC_ENTRY_MAX)
return -EINVAL;
- ret = v4l2_routing_simple_verify(routing);
+ ret = max9286_routing_verify(priv, routing);
if (ret)
return ret;
--
2.32.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 4/5] media: rcar-csi2: Implement multiplexed support
2021-09-18 15:05 [PATCH 0/5] media: Add multiplexed support to R-Car and GMSL Jacopo Mondi
` (2 preceding siblings ...)
2021-09-18 15:05 ` [PATCH 3/5] media: max9286: Implement routing validation Jacopo Mondi
@ 2021-09-18 15:05 ` Jacopo Mondi
2021-09-18 15:05 ` [PATCH 5/5] media: rcar-vin: Support multiplexed CSI-2 receiver Jacopo Mondi
4 siblings, 0 replies; 8+ messages in thread
From: Jacopo Mondi @ 2021-09-18 15:05 UTC (permalink / raw)
To: tomi.valkeinen, sakari.ailus, laurent.pinchart, niklas.soderlund,
kieran.bingham
Cc: Jacopo Mondi, Mauro Carvalho Chehab, Hans Verkuil,
Manivannan Sadhasivam, Thomas NIZAN, linux-media,
linux-renesas-soc, Niklas Söderlund
Implement support for multiplexed streams to the R-Car CSI-2 driver.
Implement the init_cfg and set_routing operations and move format
handling to the subdev state.
Use the transmitter's frame descriptor to configure the CSI-2 bus and enable
the requested virtual channel and data types.
[based on a patch from]
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
---
drivers/media/platform/rcar-vin/rcar-csi2.c | 289 +++++++++++++++-----
1 file changed, 219 insertions(+), 70 deletions(-)
diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c
index e28eff039688..db582633d415 100644
--- a/drivers/media/platform/rcar-vin/rcar-csi2.c
+++ b/drivers/media/platform/rcar-vin/rcar-csi2.c
@@ -69,10 +69,7 @@ struct rcar_csi2;
#define FLD_REG 0x1c
#define FLD_FLD_NUM(n) (((n) & 0xff) << 16)
#define FLD_DET_SEL(n) (((n) & 0x3) << 4)
-#define FLD_FLD_EN4 BIT(3)
-#define FLD_FLD_EN3 BIT(2)
-#define FLD_FLD_EN2 BIT(1)
-#define FLD_FLD_EN BIT(0)
+#define FLD_FLD_EN(n) BIT((n) & 0xf)
/* Automatic Standby Control */
#define ASTBY_REG 0x20
@@ -309,31 +306,23 @@ static const struct rcsi2_mbps_reg hsfreqrange_m3w_h3es1[] = {
#define CSI0CLKFREQRANGE(n) ((n & 0x3f) << 16)
struct rcar_csi2_format {
- u32 code;
unsigned int datatype;
unsigned int bpp;
};
static const struct rcar_csi2_format rcar_csi2_formats[] = {
- { .code = MEDIA_BUS_FMT_RGB888_1X24, .datatype = 0x24, .bpp = 24 },
- { .code = MEDIA_BUS_FMT_UYVY8_1X16, .datatype = 0x1e, .bpp = 16 },
- { .code = MEDIA_BUS_FMT_YUYV8_1X16, .datatype = 0x1e, .bpp = 16 },
- { .code = MEDIA_BUS_FMT_UYVY8_2X8, .datatype = 0x1e, .bpp = 16 },
- { .code = MEDIA_BUS_FMT_YUYV10_2X10, .datatype = 0x1e, .bpp = 20 },
- { .code = MEDIA_BUS_FMT_Y10_1X10, .datatype = 0x2b, .bpp = 10 },
- { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .datatype = 0x2a, .bpp = 8 },
- { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .datatype = 0x2a, .bpp = 8 },
- { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .datatype = 0x2a, .bpp = 8 },
- { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .datatype = 0x2a, .bpp = 8 },
- { .code = MEDIA_BUS_FMT_Y8_1X8, .datatype = 0x2a, .bpp = 8 },
+ { .datatype = 0x1e, .bpp = 16 },
+ { .datatype = 0x24, .bpp = 24 },
+ { .datatype = 0x2a, .bpp = 8 },
+ { .datatype = 0x2b, .bpp = 10 },
};
-static const struct rcar_csi2_format *rcsi2_code_to_fmt(unsigned int code)
+static const struct rcar_csi2_format *rcsi2_datatype_to_fmt(unsigned int dt)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(rcar_csi2_formats); i++)
- if (rcar_csi2_formats[i].code == code)
+ if (rcar_csi2_formats[i].datatype == dt)
return &rcar_csi2_formats[i];
return NULL;
@@ -370,8 +359,6 @@ struct rcar_csi2 {
struct v4l2_subdev *remote;
unsigned int remote_pad;
- struct v4l2_mbus_framefmt mf;
-
struct mutex lock;
int stream_count;
@@ -421,6 +408,32 @@ static int rcsi2_exit_standby(struct rcar_csi2 *priv)
return 0;
}
+static int rcsi2_get_remote_frame_desc(struct rcar_csi2 *priv,
+ struct v4l2_mbus_frame_desc *fd)
+{
+ struct media_pad *pad;
+ int ret;
+
+ if (!priv->remote)
+ return -ENODEV;
+
+ pad = media_entity_remote_pad(&priv->pads[RCAR_CSI2_SINK]);
+ if (!pad)
+ return -ENODEV;
+
+ ret = v4l2_subdev_call(priv->remote, pad, get_frame_desc,
+ pad->index, fd);
+ if (ret)
+ return ret;
+
+ if (fd->type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
+ dev_err(priv->dev, "Frame desc do not describe CSI-2 link");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int rcsi2_wait_phy_start(struct rcar_csi2 *priv,
unsigned int lanes)
{
@@ -460,11 +473,14 @@ static int rcsi2_set_phypll(struct rcar_csi2 *priv, unsigned int mbps)
return 0;
}
-static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp,
- unsigned int lanes)
+static int rcsi2_calc_mbps(struct rcar_csi2 *priv,
+ struct v4l2_mbus_frame_desc *fd, unsigned int lanes)
{
+ const struct v4l2_mbus_frame_desc_entry_csi2 *csi2_desc;
+ const struct rcar_csi2_format *format;
struct v4l2_subdev *source;
struct v4l2_ctrl *ctrl;
+ unsigned int i;
u64 mbps;
if (!priv->remote)
@@ -480,12 +496,30 @@ static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp,
return -EINVAL;
}
+ /* Verify that all remote streams send the same datatype. */
+ csi2_desc = NULL;
+ for (i = 0; i < fd->num_entries; i++) {
+ struct v4l2_mbus_frame_desc_entry_csi2 *stream_desc;
+
+ stream_desc = &fd->entry[i].bus.csi2;
+ if (!csi2_desc)
+ csi2_desc = stream_desc;
+
+ if (csi2_desc->dt != stream_desc->dt) {
+ dev_err(priv->dev,
+ "Remote streams with different DT: %u - %u\n",
+ csi2_desc->dt, stream_desc->dt);
+ return -EINVAL;
+ }
+ }
+ format = rcsi2_datatype_to_fmt(csi2_desc->dt);
+
/*
* Calculate the phypll in mbps.
* link_freq = (pixel_rate * bits_per_sample) / (2 * nr_of_lanes)
* bps = link_freq * 2
*/
- mbps = v4l2_ctrl_g_ctrl_int64(ctrl) * bpp;
+ mbps = v4l2_ctrl_g_ctrl_int64(ctrl) * format->bpp;
do_div(mbps, lanes * 1000000);
return mbps;
@@ -541,49 +575,64 @@ static int rcsi2_get_active_lanes(struct rcar_csi2 *priv,
static int rcsi2_start_receiver(struct rcar_csi2 *priv)
{
- const struct rcar_csi2_format *format;
+ const struct v4l2_subdev_stream_configs *configs;
+ struct v4l2_subdev_state *state;
u32 phycnt, vcdt = 0, vcdt2 = 0, fld = 0;
+ struct v4l2_mbus_frame_desc fd;
unsigned int lanes;
unsigned int i;
int mbps, ret;
- dev_dbg(priv->dev, "Input size (%ux%u%c)\n",
- priv->mf.width, priv->mf.height,
- priv->mf.field == V4L2_FIELD_NONE ? 'p' : 'i');
-
- /* Code is validated in set_fmt. */
- format = rcsi2_code_to_fmt(priv->mf.code);
+ /* Get information about multiplexed link. */
+ ret = rcsi2_get_remote_frame_desc(priv, &fd);
+ if (ret)
+ return ret;
- /*
- * Enable all supported CSI-2 channels with virtual channel and
- * data type matching.
- *
- * NOTE: It's not possible to get individual datatype for each
- * source virtual channel. Once this is possible in V4L2
- * it should be used here.
- */
- for (i = 0; i < priv->info->num_channels; i++) {
+ for (i = 0; i < fd.num_entries; i++) {
+ struct v4l2_mbus_frame_desc_entry *entry = &fd.entry[i];
u32 vcdt_part;
- vcdt_part = VCDT_SEL_VC(i) | VCDT_VCDTN_EN | VCDT_SEL_DTN_ON |
- VCDT_SEL_DT(format->datatype);
+ vcdt_part = VCDT_SEL_VC(entry->bus.csi2.vc) |
+ VCDT_VCDTN_EN | VCDT_SEL_DTN_ON |
+ VCDT_SEL_DT(entry->bus.csi2.dt);
/* Store in correct reg and offset. */
- if (i < 2)
- vcdt |= vcdt_part << ((i % 2) * 16);
+ if (entry->bus.csi2.vc < 2)
+ vcdt |= vcdt_part <<
+ ((entry->bus.csi2.vc % 2) * 16);
else
- vcdt2 |= vcdt_part << ((i % 2) * 16);
+ vcdt2 |= vcdt_part <<
+ ((entry->bus.csi2.vc % 2) * 16);
+
+ dev_dbg(priv->dev, "VC: %d datatype: 0x%x\n",
+ entry->bus.csi2.vc, entry->bus.csi2.dt);
}
- if (priv->mf.field == V4L2_FIELD_ALTERNATE) {
- fld = FLD_DET_SEL(1) | FLD_FLD_EN4 | FLD_FLD_EN3 | FLD_FLD_EN2
- | FLD_FLD_EN;
+ /*
+ * Configure field handling inspecting the formats of the
+ * single sink pad streams.
+ */
+ state = v4l2_subdev_lock_active_state(&priv->subdev);
+ configs = &state->stream_configs;
+ for (i = 0; i < configs->num_configs; ++i) {
+ const struct v4l2_subdev_stream_config *config = configs->configs;
+
+ if (config->pad != RCAR_CSI2_SINK)
+ continue;
+
+ if (config->fmt.field != V4L2_FIELD_ALTERNATE)
+ continue;
+
+ fld |= FLD_DET_SEL(1);
+ fld |= FLD_FLD_EN(config->stream);
- if (priv->mf.height == 240)
+ /* PAL vs NTSC. */
+ if (config->fmt.height == 240)
fld |= FLD_FLD_NUM(0);
else
fld |= FLD_FLD_NUM(1);
}
+ v4l2_subdev_unlock_state(state);
/*
* Get the number of active data lanes inspecting the remote mbus
@@ -596,7 +645,7 @@ static int rcsi2_start_receiver(struct rcar_csi2 *priv)
phycnt = PHYCNT_ENABLECLK;
phycnt |= (1 << lanes) - 1;
- mbps = rcsi2_calc_mbps(priv, format->bpp, lanes);
+ mbps = rcsi2_calc_mbps(priv, &fd, lanes);
if (mbps < 0)
return mbps;
@@ -722,43 +771,127 @@ static int rcsi2_set_pad_format(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
- struct rcar_csi2 *priv = sd_to_csi2(sd);
- struct v4l2_mbus_framefmt *framefmt;
+ struct v4l2_mbus_framefmt *fmt;
+ struct v4l2_subdev_state *state;
+ int ret = 0;
- if (!rcsi2_code_to_fmt(format->format.code))
- format->format.code = rcar_csi2_formats[0].code;
+ /*
+ * Format is propagated from sink streams to source streams, so
+ * disallow setting format on the single source pad.
+ */
+ if (format->pad < RCAR_CSI2_SOURCE_VC0)
+ return -EINVAL;
- if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- priv->mf = format->format;
- } else {
- framefmt = v4l2_subdev_get_try_format(sd, sd_state, 0);
- *framefmt = format->format;
+ state = v4l2_subdev_validate_and_lock_state(sd, sd_state);
+ fmt = v4l2_state_get_stream_format(state, format->pad,
+ format->stream);
+ if (!fmt) {
+ ret = -EINVAL;
+ goto out;
}
+ *fmt = format->format;
- return 0;
+ /* Propagate format to the other end of the route. */
+ fmt = v4l2_state_get_opposite_stream_format(state, format->pad,
+ format->stream);
+ if (!fmt) {
+ ret = -EINVAL;
+ goto out;
+ }
+ *fmt = format->format;
+
+out:
+ v4l2_subdev_unlock_state(state);
+ return ret;
}
-static int rcsi2_get_pad_format(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *format)
+static int rcsi2_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_krouting *routing)
{
- struct rcar_csi2 *priv = sd_to_csi2(sd);
+ static const struct v4l2_mbus_framefmt format = {
+ .width = 640,
+ .height = 480,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ycbcr_enc = V4L2_YCBCR_ENC_601,
+ .quantization = V4L2_QUANTIZATION_LIM_RANGE,
+ .xfer_func = V4L2_XFER_FUNC_SRGB,
+ };
+ int ret;
+
+ ret = v4l2_routing_simple_verify(routing);
+ if (ret)
+ return ret;
+
+ state = v4l2_subdev_validate_and_lock_state(sd, state);
+
+ ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format);
- if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- format->format = priv->mf;
- else
- format->format = *v4l2_subdev_get_try_format(sd, sd_state, 0);
+ v4l2_subdev_unlock_state(state);
+
+ if (ret)
+ return ret;
return 0;
}
+static int rcsi2_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ u32 which = state->which;
+
+ /* Initialize 4 routes from each source pad to the single sink pad. */
+ struct v4l2_subdev_route routes[] = {
+ {
+ .sink_pad = RCAR_CSI2_SINK,
+ .sink_stream = 0,
+ .source_pad = RCAR_CSI2_SOURCE_VC0,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ },
+ {
+ .sink_pad = RCAR_CSI2_SINK,
+ .sink_stream = 1,
+ .source_pad = RCAR_CSI2_SOURCE_VC1,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ },
+ {
+ .sink_pad = RCAR_CSI2_SINK,
+ .sink_stream = 2,
+ .source_pad = RCAR_CSI2_SOURCE_VC2,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ },
+ {
+ .sink_pad = RCAR_CSI2_SINK,
+ .sink_stream = 3,
+ .source_pad = RCAR_CSI2_SOURCE_VC3,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ },
+ };
+
+ struct v4l2_subdev_krouting routing = {
+ .which = which,
+ .num_routes = ARRAY_SIZE(routes),
+ .routes = routes,
+ };
+
+ return rcsi2_set_routing(sd, state, &routing);
+}
+
static const struct v4l2_subdev_video_ops rcar_csi2_video_ops = {
.s_stream = rcsi2_s_stream,
};
static const struct v4l2_subdev_pad_ops rcar_csi2_pad_ops = {
+ .init_cfg = rcsi2_init_cfg,
.set_fmt = rcsi2_set_pad_format,
- .get_fmt = rcsi2_get_pad_format,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_routing = rcsi2_set_routing,
};
static const struct v4l2_subdev_ops rcar_csi2_subdev_ops = {
@@ -814,6 +947,13 @@ static int rcsi2_notify_bound(struct v4l2_async_notifier *notifier,
struct rcar_csi2 *priv = notifier_to_csi2(notifier);
int pad;
+ if (!v4l2_subdev_has_op(subdev, pad, get_frame_desc)) {
+ dev_err(priv->dev,
+ "Subdev %s bound failed: missing get_frame_desc()\n",
+ subdev->name);
+ return -EINVAL;
+ }
+
pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode,
MEDIA_PAD_FL_SOURCE);
if (pad < 0) {
@@ -1260,7 +1400,8 @@ static int rcsi2_probe(struct platform_device *pdev)
v4l2_set_subdevdata(&priv->subdev, &pdev->dev);
snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s %s",
KBUILD_MODNAME, dev_name(&pdev->dev));
- priv->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ priv->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE |
+ V4L2_SUBDEV_FL_MULTIPLEXED;
priv->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
priv->subdev.entity.ops = &rcar_csi2_entity_ops;
@@ -1274,16 +1415,23 @@ static int rcsi2_probe(struct platform_device *pdev)
if (ret)
goto error;
+ ret = v4l2_subdev_alloc_state(&priv->subdev);
+ if (ret)
+ goto error;
+
pm_runtime_enable(&pdev->dev);
ret = v4l2_async_register_subdev(&priv->subdev);
if (ret < 0)
- goto error;
+ goto error_state;
dev_info(priv->dev, "%d lanes found\n", priv->lanes);
return 0;
+error_state:
+ v4l2_subdev_free_state(&priv->subdev);
+
error:
v4l2_async_notifier_unregister(&priv->notifier);
v4l2_async_notifier_cleanup(&priv->notifier);
@@ -1297,6 +1445,7 @@ static int rcsi2_remove(struct platform_device *pdev)
v4l2_async_notifier_unregister(&priv->notifier);
v4l2_async_notifier_cleanup(&priv->notifier);
+ v4l2_subdev_free_state(&priv->subdev);
v4l2_async_unregister_subdev(&priv->subdev);
pm_runtime_disable(&pdev->dev);
--
2.32.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 5/5] media: rcar-vin: Support multiplexed CSI-2 receiver
2021-09-18 15:05 [PATCH 0/5] media: Add multiplexed support to R-Car and GMSL Jacopo Mondi
` (3 preceding siblings ...)
2021-09-18 15:05 ` [PATCH 4/5] media: rcar-csi2: Implement multiplexed support Jacopo Mondi
@ 2021-09-18 15:05 ` Jacopo Mondi
2021-09-23 1:39 ` Laurent Pinchart
4 siblings, 1 reply; 8+ messages in thread
From: Jacopo Mondi @ 2021-09-18 15:05 UTC (permalink / raw)
To: tomi.valkeinen, sakari.ailus, laurent.pinchart, niklas.soderlund,
kieran.bingham
Cc: Jacopo Mondi, Mauro Carvalho Chehab, Hans Verkuil,
Manivannan Sadhasivam, Thomas NIZAN, linux-media,
linux-renesas-soc
The R-Car CSI-2 receiver supports multiplexed streams.
The VIN driver inspects the CSI-2 subdevice format with the intent of
validating formats at stream start time.
Each VIN instance is connected to a CSI-2 receiver which exposes a
single stream and since it has acquired support for multiplexed streams
maintains the format information in its v4l2_subdev_state per-stream.
Instrument the VIN driver to fetch the CSI-2 receiver format from the
streams configuration by setting the stream identifier to 0 and pass to
the subdevice its own state where to retrieve format information from.
Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
---
drivers/media/platform/rcar-vin/rcar-dma.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c
index 83b2f923cf98..a6f3701f3f3f 100644
--- a/drivers/media/platform/rcar-vin/rcar-dma.c
+++ b/drivers/media/platform/rcar-vin/rcar-dma.c
@@ -1119,7 +1119,8 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd,
};
fmt.pad = pad->index;
- if (v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt))
+ fmt.stream = 0;
+ if (v4l2_subdev_call(sd, pad, get_fmt, sd->state, &fmt))
return -EPIPE;
switch (fmt.format.code) {
--
2.32.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 5/5] media: rcar-vin: Support multiplexed CSI-2 receiver
2021-09-18 15:05 ` [PATCH 5/5] media: rcar-vin: Support multiplexed CSI-2 receiver Jacopo Mondi
@ 2021-09-23 1:39 ` Laurent Pinchart
0 siblings, 0 replies; 8+ messages in thread
From: Laurent Pinchart @ 2021-09-23 1:39 UTC (permalink / raw)
To: Jacopo Mondi
Cc: tomi.valkeinen, sakari.ailus, niklas.soderlund, kieran.bingham,
Mauro Carvalho Chehab, Hans Verkuil, Manivannan Sadhasivam,
Thomas NIZAN, linux-media, linux-renesas-soc
Hi Jacopo,
Thank you for the patch.
On Sat, Sep 18, 2021 at 05:05:07PM +0200, Jacopo Mondi wrote:
> The R-Car CSI-2 receiver supports multiplexed streams.
>
> The VIN driver inspects the CSI-2 subdevice format with the intent of
> validating formats at stream start time.
>
> Each VIN instance is connected to a CSI-2 receiver which exposes a
> single stream and since it has acquired support for multiplexed streams
> maintains the format information in its v4l2_subdev_state per-stream.
>
> Instrument the VIN driver to fetch the CSI-2 receiver format from the
> streams configuration by setting the stream identifier to 0 and pass to
> the subdevice its own state where to retrieve format information from.
>
> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> ---
> drivers/media/platform/rcar-vin/rcar-dma.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c
> index 83b2f923cf98..a6f3701f3f3f 100644
> --- a/drivers/media/platform/rcar-vin/rcar-dma.c
> +++ b/drivers/media/platform/rcar-vin/rcar-dma.c
> @@ -1119,7 +1119,8 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd,
> };
>
> fmt.pad = pad->index;
> - if (v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt))
> + fmt.stream = 0;
> + if (v4l2_subdev_call(sd, pad, get_fmt, sd->state, &fmt))
Should we have a helper function for this ?
int v4l2_subdev_get_pad_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_format *format);
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> return -EPIPE;
>
> switch (fmt.format.code) {
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 8+ messages in thread