From patchwork Sat Sep 18 15:05:03 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 12503885 X-Patchwork-Delegate: kieran@bingham.xyz Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B0256C4332F for ; Sat, 18 Sep 2021 15:04:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8F06E61100 for ; Sat, 18 Sep 2021 15:04:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239483AbhIRPGK (ORCPT ); Sat, 18 Sep 2021 11:06:10 -0400 Received: from relay2-d.mail.gandi.net ([217.70.183.194]:54619 "EHLO relay2-d.mail.gandi.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231336AbhIRPGK (ORCPT ); Sat, 18 Sep 2021 11:06:10 -0400 Received: (Authenticated sender: jacopo@jmondi.org) by relay2-d.mail.gandi.net (Postfix) with ESMTPSA id BB2A840009; Sat, 18 Sep 2021 15:04:43 +0000 (UTC) From: Jacopo Mondi To: tomi.valkeinen@ideasonboard.com, sakari.ailus@linux.intel.com, laurent.pinchart@ideasonboard.com, niklas.soderlund@ragnatech.se, kieran.bingham@ideasonboard.com Cc: Jacopo Mondi , Mauro Carvalho Chehab , Hans Verkuil , Manivannan Sadhasivam , Thomas NIZAN , linux-media@vger.kernel.org, linux-renesas-soc@vger.kernel.org Subject: [PATCH 1/5] media: max9286: Implement multiplexed support Date: Sat, 18 Sep 2021 17:05:03 +0200 Message-Id: <20210918150507.987294-2-jacopo+renesas@jmondi.org> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210918150507.987294-1-jacopo+renesas@jmondi.org> References: <20210918150507.987294-1-jacopo+renesas@jmondi.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-renesas-soc@vger.kernel.org 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 --- 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); } From patchwork Sat Sep 18 15:05:04 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 12503887 X-Patchwork-Delegate: kieran@bingham.xyz Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9D989C433EF for ; Sat, 18 Sep 2021 15:04:49 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7ACD86128A for ; Sat, 18 Sep 2021 15:04:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231336AbhIRPGM (ORCPT ); Sat, 18 Sep 2021 11:06:12 -0400 Received: from relay2-d.mail.gandi.net ([217.70.183.194]:41733 "EHLO relay2-d.mail.gandi.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239564AbhIRPGM (ORCPT ); Sat, 18 Sep 2021 11:06:12 -0400 Received: (Authenticated sender: jacopo@jmondi.org) by relay2-d.mail.gandi.net (Postfix) with ESMTPSA id BB3CC40006; Sat, 18 Sep 2021 15:04:45 +0000 (UTC) From: Jacopo Mondi To: tomi.valkeinen@ideasonboard.com, sakari.ailus@linux.intel.com, laurent.pinchart@ideasonboard.com, niklas.soderlund@ragnatech.se, kieran.bingham@ideasonboard.com Cc: Jacopo Mondi , Mauro Carvalho Chehab , Hans Verkuil , Manivannan Sadhasivam , Thomas NIZAN , linux-media@vger.kernel.org, linux-renesas-soc@vger.kernel.org Subject: [PATCH 2/5] media: max9286: Apply routing configuration Date: Sat, 18 Sep 2021 17:05:04 +0200 Message-Id: <20210918150507.987294-3-jacopo+renesas@jmondi.org> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210918150507.987294-1-jacopo+renesas@jmondi.org> References: <20210918150507.987294-1-jacopo+renesas@jmondi.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-renesas-soc@vger.kernel.org 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 --- drivers/media/i2c/max9286.c | 189 +++++++++++++++++++++++++----------- 1 file changed, 133 insertions(+), 56 deletions(-) -- 2.32.0 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"); From patchwork Sat Sep 18 15:05:05 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 12503889 X-Patchwork-Delegate: kieran@bingham.xyz Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 847B7C433F5 for ; Sat, 18 Sep 2021 15:04:51 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 62A2C61108 for ; Sat, 18 Sep 2021 15:04:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239564AbhIRPGO (ORCPT ); Sat, 18 Sep 2021 11:06:14 -0400 Received: from relay2-d.mail.gandi.net ([217.70.183.194]:51125 "EHLO relay2-d.mail.gandi.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239611AbhIRPGO (ORCPT ); Sat, 18 Sep 2021 11:06:14 -0400 Received: (Authenticated sender: jacopo@jmondi.org) by relay2-d.mail.gandi.net (Postfix) with ESMTPSA id B7A304000C; Sat, 18 Sep 2021 15:04:47 +0000 (UTC) From: Jacopo Mondi To: tomi.valkeinen@ideasonboard.com, sakari.ailus@linux.intel.com, laurent.pinchart@ideasonboard.com, niklas.soderlund@ragnatech.se, kieran.bingham@ideasonboard.com Cc: Jacopo Mondi , Mauro Carvalho Chehab , Hans Verkuil , Manivannan Sadhasivam , Thomas NIZAN , linux-media@vger.kernel.org, linux-renesas-soc@vger.kernel.org Subject: [PATCH 3/5] media: max9286: Implement routing validation Date: Sat, 18 Sep 2021 17:05:05 +0200 Message-Id: <20210918150507.987294-4-jacopo+renesas@jmondi.org> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210918150507.987294-1-jacopo+renesas@jmondi.org> References: <20210918150507.987294-1-jacopo+renesas@jmondi.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-renesas-soc@vger.kernel.org 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 --- 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; From patchwork Sat Sep 18 15:05:06 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 12503891 X-Patchwork-Delegate: kieran@bingham.xyz Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BAA67C433F5 for ; Sat, 18 Sep 2021 15:04:53 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9CEA561283 for ; Sat, 18 Sep 2021 15:04:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239663AbhIRPGQ (ORCPT ); Sat, 18 Sep 2021 11:06:16 -0400 Received: from relay2-d.mail.gandi.net ([217.70.183.194]:56909 "EHLO relay2-d.mail.gandi.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239611AbhIRPGQ (ORCPT ); Sat, 18 Sep 2021 11:06:16 -0400 Received: (Authenticated sender: jacopo@jmondi.org) by relay2-d.mail.gandi.net (Postfix) with ESMTPSA id BE48640002; Sat, 18 Sep 2021 15:04:49 +0000 (UTC) From: Jacopo Mondi To: tomi.valkeinen@ideasonboard.com, sakari.ailus@linux.intel.com, laurent.pinchart@ideasonboard.com, niklas.soderlund@ragnatech.se, kieran.bingham@ideasonboard.com Cc: Jacopo Mondi , Mauro Carvalho Chehab , Hans Verkuil , Manivannan Sadhasivam , Thomas NIZAN , linux-media@vger.kernel.org, linux-renesas-soc@vger.kernel.org, =?utf-8?q?Niklas_S=C3=B6derlund?= Subject: [PATCH 4/5] media: rcar-csi2: Implement multiplexed support Date: Sat, 18 Sep 2021 17:05:06 +0200 Message-Id: <20210918150507.987294-5-jacopo+renesas@jmondi.org> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210918150507.987294-1-jacopo+renesas@jmondi.org> References: <20210918150507.987294-1-jacopo+renesas@jmondi.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-renesas-soc@vger.kernel.org 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 Signed-off-by: Jacopo Mondi --- 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); From patchwork Sat Sep 18 15:05:07 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 12503893 X-Patchwork-Delegate: kieran@bingham.xyz Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A2418C4332F for ; Sat, 18 Sep 2021 15:04:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7FEE761100 for ; Sat, 18 Sep 2021 15:04:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239711AbhIRPGS (ORCPT ); Sat, 18 Sep 2021 11:06:18 -0400 Received: from relay2-d.mail.gandi.net ([217.70.183.194]:37855 "EHLO relay2-d.mail.gandi.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239611AbhIRPGS (ORCPT ); Sat, 18 Sep 2021 11:06:18 -0400 Received: (Authenticated sender: jacopo@jmondi.org) by relay2-d.mail.gandi.net (Postfix) with ESMTPSA id D129A4000A; Sat, 18 Sep 2021 15:04:51 +0000 (UTC) From: Jacopo Mondi To: tomi.valkeinen@ideasonboard.com, sakari.ailus@linux.intel.com, laurent.pinchart@ideasonboard.com, niklas.soderlund@ragnatech.se, kieran.bingham@ideasonboard.com Cc: Jacopo Mondi , Mauro Carvalho Chehab , Hans Verkuil , Manivannan Sadhasivam , Thomas NIZAN , linux-media@vger.kernel.org, linux-renesas-soc@vger.kernel.org Subject: [PATCH 5/5] media: rcar-vin: Support multiplexed CSI-2 receiver Date: Sat, 18 Sep 2021 17:05:07 +0200 Message-Id: <20210918150507.987294-6-jacopo+renesas@jmondi.org> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210918150507.987294-1-jacopo+renesas@jmondi.org> References: <20210918150507.987294-1-jacopo+renesas@jmondi.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-renesas-soc@vger.kernel.org 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 Reviewed-by: Laurent Pinchart --- 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) {