Linux-Media Archive mirror
 help / color / mirror / Atom feed
From: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
To: "Laurent Pinchart" <laurent.pinchart@ideasonboard.com>,
	"Niklas Söderlund" <niklas.soderlund+renesas@ragnatech.se>,
	"Sakari Ailus" <sakari.ailus@linux.intel.com>,
	"Kieran Bingham" <kieran.bingham+renesas@ideasonboard.com>,
	"Tomi Valkeinen" <tomi.valkeinen@ideasonboard.com>
Cc: Jacopo Mondi <jacopo.mondi@ideasonboard.com>,
	linux-media@vger.kernel.org, linux-renesas-soc@vger.kernel.org
Subject: [PATCH 16/19] media: rcar-csi2: Support multiplexed transmitters
Date: Tue, 30 Apr 2024 12:39:52 +0200	[thread overview]
Message-ID: <20240430103956.60190-17-jacopo.mondi@ideasonboard.com> (raw)
In-Reply-To: <20240430103956.60190-1-jacopo.mondi@ideasonboard.com>

Rework the R-Car CSI-2 start routine to support multiplexed
transmitters.

Configure the CSI-2 receiver MIPI CSI-2 Data Type filtering by inspecting
the remote subdev frame_desc instead of relying on the image format
configured on the sink pad.

Enable MIPI CSI-2 Data Type filtering by inspecting the remote subdevice
frame descriptor to discern which Data Type is transmitted on which
Virtual Channel. If multiple Data Types are transmitted on the same VC
then Data Type filtering is disabled.

Rework the per-lane bandwidth calculation to use the LINK_FREQ control
if a transmitter sends multiple streams on the same data link. The
current usage of the PIXEL_RATE control does not support multiplexed
transmitters, as there's not a unique pixel rate among all the possible
source streams.

This change makes mandatory for any subdevice that operates with
the R-Car CSI-2 receiver to implement the .get_frame_desc() operation.

Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
---
 drivers/media/platform/renesas/rcar-csi2.c | 280 ++++++++++++++-------
 1 file changed, 191 insertions(+), 89 deletions(-)

diff --git a/drivers/media/platform/renesas/rcar-csi2.c b/drivers/media/platform/renesas/rcar-csi2.c
index 82dc0b92b8d3..ffb73272543b 100644
--- a/drivers/media/platform/renesas/rcar-csi2.c
+++ b/drivers/media/platform/renesas/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
@@ -575,6 +572,16 @@ static const struct rcar_csi2_format *rcsi2_code_to_fmt(unsigned int code)
 	return NULL;
 }
 
+static const struct rcar_csi2_format *rcsi2_datatype_to_fmt(unsigned int dt)
+{
+	for (unsigned int i = 0; i < ARRAY_SIZE(rcar_csi2_formats); i++) {
+		if (rcar_csi2_formats[i].datatype == dt)
+			return &rcar_csi2_formats[i];
+	}
+
+	return NULL;
+}
+
 enum rcar_csi2_pads {
 	RCAR_CSI2_SINK,
 	RCAR_CSI2_SOURCE_VC0,
@@ -587,7 +594,8 @@ enum rcar_csi2_pads {
 struct rcar_csi2_info {
 	int (*init_phtw)(struct rcar_csi2 *priv, unsigned int mbps);
 	int (*phy_post_init)(struct rcar_csi2 *priv);
-	int (*start_receiver)(struct rcar_csi2 *priv);
+	int (*start_receiver)(struct rcar_csi2 *priv,
+			      struct v4l2_subdev_state *state);
 	void (*enter_standby)(struct rcar_csi2 *priv);
 	const struct rcsi2_mbps_reg *hsfreqrange;
 	unsigned int csi0clkfreqrange;
@@ -676,6 +684,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 -ENOLINK;
+
+	pad = media_pad_remote_pad_unique(&priv->pads[RCAR_CSI2_SINK]);
+	if (IS_ERR(pad))
+		return PTR_ERR(pad);
+
+	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 does not describe a CSI-2 link");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int rcsi2_wait_phy_start(struct rcar_csi2 *priv,
 				unsigned int lanes)
 {
@@ -726,41 +760,6 @@ 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)
-{
-	struct v4l2_subdev *source;
-	struct v4l2_ctrl *ctrl;
-	u64 mbps;
-
-	if (!priv->remote)
-		return -ENODEV;
-
-	source = priv->remote;
-
-	/* Read the pixel rate control from remote. */
-	ctrl = v4l2_ctrl_find(source->ctrl_handler, V4L2_CID_PIXEL_RATE);
-	if (!ctrl) {
-		dev_err(priv->dev, "no pixel rate control in subdev %s\n",
-			source->name);
-		return -EINVAL;
-	}
-
-	/*
-	 * 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;
-	do_div(mbps, lanes * 1000000);
-
-	/* Adjust for C-PHY, divide by 2.8. */
-	if (priv->cphy)
-		mbps = div_u64(mbps * 5, 14);
-
-	return mbps;
-}
-
 static int rcsi2_get_active_lanes(struct rcar_csi2 *priv,
 				  unsigned int *lanes)
 {
@@ -808,52 +807,146 @@ static int rcsi2_get_active_lanes(struct rcar_csi2 *priv,
 	return 0;
 }
 
-static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv)
+static int rcsi2_calc_mbps(struct rcar_csi2 *priv,
+			   struct v4l2_mbus_frame_desc *fd, unsigned int lanes)
 {
-	const struct rcar_csi2_format *format;
+	struct v4l2_subdev *source;
+	unsigned int bpp;
+	s64 link_freq;
+	u64 mbps;
+
+	if (!priv->remote)
+		return -ENODEV;
+
+	source = priv->remote;
+
+	/*
+	 * v4l2_get_link_freq() uses V4L2_CID_LINK_FREQ first, and falls back
+	 * to V4L2_CID_PIXEL_RATE if V4L2_CID_LINK_FREQ is not available.
+	 *
+	 * With multistream input there is no single pixel rate, and thus we
+	 * cannot use V4L2_CID_PIXEL_RATE, so we pass 0 as the bpp which
+	 * causes v4l2_get_link_freq() to return an error if it falls back to
+	 * V4L2_CID_PIXEL_RATE.
+	 */
+
+	if (fd->num_entries > 1) {
+		bpp = 0;
+	} else {
+		struct v4l2_mbus_frame_desc_entry *entry = &fd->entry[0];
+		const struct rcar_csi2_format *format;
+
+		format = rcsi2_datatype_to_fmt(entry->bus.csi2.dt);
+		if (WARN_ON(!format))
+			return -EINVAL;
+
+		bpp = format->bpp;
+	}
+
+	/*
+	 * Calculate the phypll in mbps.
+	 * link_freq = (pixel_rate * bits_per_sample) / (2 * nr_of_lanes)
+	 * bps = link_freq * 2
+	 */
+	link_freq = v4l2_get_link_freq(source->ctrl_handler, bpp, 2 * lanes);
+	if (link_freq < 0) {
+		dev_err(priv->dev, "Failed to get remote subdev link freq\n");
+		return link_freq;
+	}
+
+	mbps = 2 * link_freq;
+	do_div(mbps, 1000000);
+
+	/* Adjust for C-PHY, divide by 2.8. */
+	if (priv->cphy)
+		mbps = div_u64(mbps * 5, 14);
+
+	return mbps;
+}
+
+static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv,
+				     struct v4l2_subdev_state *state)
+{
+	const struct v4l2_subdev_stream_configs *configs;
 	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);
-	if (!format)
-		return -EINVAL;
+	/* 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.
+	 * Configure and enable the R-Car CSI-2 channels.
 	 *
-	 * 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.
+	 * The VC transmitted on the channel is configured by the [CSI-2->VIN]
+	 * link_setup operation, while the data type to match comes from the
+	 * remote subdevice.
 	 */
-	for (i = 0; i < priv->info->num_channels; i++) {
-		u32 vcdt_part;
+	for (unsigned int i = 0; i < priv->info->num_channels; i++) {
+		struct v4l2_mbus_frame_desc_entry *entry = NULL;
 
+		/* CSI-2 channel disabled (not linked to any VIN). */
 		if (priv->channel_vc[i] < 0)
 			continue;
 
-		vcdt_part = VCDT_SEL_VC(priv->channel_vc[i]) | VCDT_VCDTN_EN |
-			VCDT_SEL_DTN_ON | VCDT_SEL_DT(format->datatype);
+		u32 vcdt_part = VCDT_SEL_VC(priv->channel_vc[i]) |
+				VCDT_VCDTN_EN | VCDT_SEL_DTN_ON;
+
+		/*
+		 * Search the entries that describe the data types on the
+		 * MIPI CSI-2 Virtual Channel assigned to this CSI-2 channel.
+		 */
+		for (unsigned int e = 0; e < fd.num_entries; e++) {
+			if (fd.entry[e].bus.csi2.vc != priv->channel_vc[i])
+				continue;
+
+			/*
+			 * If multiple data types are sent on the same MIPI
+			 * CSI-2 Virtual Channel, disable Data Type matching.
+			 */
+			if (entry) {
+				vcdt_part &= ~VCDT_SEL_DTN_ON;
+				break;
+			}
+
+			entry = &fd.entry[e];
+			vcdt_part |= VCDT_SEL_DT(entry->bus.csi2.dt);
+		}
+
+		if (!entry)
+			continue;
 
 		/* Store in correct reg and offset. */
 		if (i < 2)
 			vcdt |= vcdt_part << ((i % 2) * 16);
 		else
 			vcdt2 |= vcdt_part << ((i % 2) * 16);
+
+		dev_dbg(priv->dev, "channel %u: VC = %d, datatype = 0x%x\n",
+			i, priv->channel_vc[i], 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
+	 * sink pad streams.
+	 */
+	configs = &state->stream_configs;
+	for (unsigned int i = 0; i < configs->num_configs; ++i) {
+		const struct v4l2_subdev_stream_config *config = configs->configs;
+
+		if (config->pad != RCAR_CSI2_SINK)
+			continue;
 
-		if (priv->mf.height == 240)
+		if (config->fmt.field != V4L2_FIELD_ALTERNATE)
+			continue;
+
+		fld |= FLD_DET_SEL(1) | FLD_FLD_EN(config->stream);
+
+		/* PAL vs NTSC. */
+		if (config->fmt.height == 240)
 			fld |= FLD_FLD_NUM(0);
 		else
 			fld |= FLD_FLD_NUM(1);
@@ -870,7 +963,7 @@ static int rcsi2_start_receiver_gen3(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;
 
@@ -1049,23 +1142,24 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps)
 	return 0;
 }
 
-static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv)
+static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv,
+				    struct v4l2_subdev_state *state)
 {
-	const struct rcar_csi2_format *format;
+	struct v4l2_mbus_frame_desc fd;
 	unsigned int lanes;
 	int msps;
 	int ret;
 
 	/* Calculate parameters */
-	format = rcsi2_code_to_fmt(priv->mf.code);
-	if (!format)
-		return -EINVAL;
-
 	ret = rcsi2_get_active_lanes(priv, &lanes);
 	if (ret)
 		return ret;
 
-	msps = rcsi2_calc_mbps(priv, format->bpp, lanes);
+	ret = rcsi2_get_remote_frame_desc(priv, &fd);
+	if (ret)
+		return ret;
+
+	msps = rcsi2_calc_mbps(priv, &fd, lanes);
 	if (msps < 0)
 		return msps;
 
@@ -1114,7 +1208,7 @@ static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv)
 	return 0;
 }
 
-static int rcsi2_start(struct rcar_csi2 *priv)
+static int rcsi2_start(struct rcar_csi2 *priv, struct v4l2_subdev_state *state)
 {
 	int ret;
 
@@ -1122,7 +1216,7 @@ static int rcsi2_start(struct rcar_csi2 *priv)
 	if (ret < 0)
 		return ret;
 
-	ret = priv->info->start_receiver(priv);
+	ret = priv->info->start_receiver(priv, state);
 	if (ret) {
 		rcsi2_enter_standby(priv);
 		return ret;
@@ -1146,26 +1240,24 @@ static void rcsi2_stop(struct rcar_csi2 *priv)
 static int rcsi2_s_stream(struct v4l2_subdev *sd, int enable)
 {
 	struct rcar_csi2 *priv = sd_to_csi2(sd);
+	struct v4l2_subdev_state *state;
 	int ret = 0;
 
-	mutex_lock(&priv->lock);
+	if (!priv->remote)
+		return -ENODEV;
 
-	if (!priv->remote) {
-		ret = -ENODEV;
-		goto out;
-	}
+	state = v4l2_subdev_lock_and_get_active_state(&priv->subdev);
 
-	if (enable && priv->stream_count == 0) {
-		ret = rcsi2_start(priv);
-		if (ret)
-			goto out;
-	} else if (!enable && priv->stream_count == 1) {
+	if (enable && priv->stream_count == 0)
+		ret = rcsi2_start(priv, state);
+	else if (!enable && priv->stream_count == 1)
 		rcsi2_stop(priv);
-	}
+	if (ret)
+		goto out;
 
 	priv->stream_count += enable ? 1 : -1;
 out:
-	mutex_unlock(&priv->lock);
+	v4l2_subdev_unlock_state(state);
 
 	return ret;
 }
@@ -1310,14 +1402,17 @@ static irqreturn_t rcsi2_irq(int irq, void *data)
 
 static irqreturn_t rcsi2_irq_thread(int irq, void *data)
 {
+	struct v4l2_subdev_state *state;
 	struct rcar_csi2 *priv = data;
 
-	mutex_lock(&priv->lock);
+	state = v4l2_subdev_lock_and_get_active_state(&priv->subdev);
+
 	rcsi2_stop(priv);
 	usleep_range(1000, 2000);
-	if (rcsi2_start(priv))
+	if (rcsi2_start(priv, state))
 		dev_warn(priv->dev, "Failed to restart CSI-2 receiver\n");
-	mutex_unlock(&priv->lock);
+
+	v4l2_subdev_unlock_state(state);
 
 	return IRQ_HANDLED;
 }
@@ -1340,6 +1435,13 @@ static int rcsi2_notify_bound(struct v4l2_async_notifier *notifier,
 		return 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;
+	}
+
 	priv->remote = subdev;
 	priv->remote_pad = pad;
 
-- 
2.44.0


  parent reply	other threads:[~2024-04-30 10:40 UTC|newest]

Thread overview: 56+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-04-30 10:39 [PATCH 00/19] media: renesas: rcar-csi2: Support multiple streams Jacopo Mondi
2024-04-30 10:39 ` [PATCH 01/19] media: adv748x: Add support for active state Jacopo Mondi
2024-05-02 17:34   ` Laurent Pinchart
2024-05-03  7:55     ` Jacopo Mondi
2024-05-03  8:24       ` Laurent Pinchart
2024-04-30 10:39 ` [PATCH 02/19] media: adv748x: Add flags to adv748x_subdev_init() Jacopo Mondi
2024-05-02 17:37   ` Laurent Pinchart
2024-04-30 10:39 ` [PATCH 03/19] media: adv748x: Use V4L2 streams Jacopo Mondi
2024-05-02 17:40   ` Laurent Pinchart
2024-05-03  7:59     ` Jacopo Mondi
2024-05-03  8:31       ` Laurent Pinchart
2024-05-03  8:46         ` Tomi Valkeinen
2024-04-30 10:39 ` [PATCH 04/19] media: adv748x: Propagate format to opposite stream Jacopo Mondi
2024-05-02 17:41   ` Laurent Pinchart
2024-04-30 10:39 ` [PATCH 05/19] media: adv748x: Implement set_routing() Jacopo Mondi
2024-05-02 17:49   ` Laurent Pinchart
2024-05-03  8:02     ` Jacopo Mondi
2024-04-30 10:39 ` [PATCH 06/19] media: adv748x: Use routes to configure VC Jacopo Mondi
2024-05-02 17:51   ` Laurent Pinchart
2024-05-02 17:52     ` Laurent Pinchart
2024-05-03  8:05       ` Jacopo Mondi
2024-04-30 10:39 ` [PATCH 07/19] media: adv748x: Implement .get_frame_desc() Jacopo Mondi
2024-05-02 17:57   ` Laurent Pinchart
2024-04-30 10:39 ` [PATCH 08/19] media: max9286: Add support for subdev active state Jacopo Mondi
2024-05-02 18:10   ` Laurent Pinchart
2024-04-30 10:39 ` [PATCH 09/19] media: max9286: Fix enum_mbus_code Jacopo Mondi
2024-05-02 18:14   ` Laurent Pinchart
2024-04-30 10:39 ` [PATCH 10/19] media: max9286: Use frame interval from subdev state Jacopo Mondi
2024-05-02 18:23   ` Laurent Pinchart
2024-04-30 10:39 ` [PATCH 11/19] media: max9286: Use V4L2 Streams Jacopo Mondi
2024-05-02 18:25   ` Laurent Pinchart
2024-04-30 10:39 ` [PATCH 12/19] media: max9286: Implement .get_frame_desc() Jacopo Mondi
2024-05-02 18:32   ` Laurent Pinchart
2024-04-30 10:39 ` [PATCH 13/19] media: max9286: Implement support for LINK_FREQ Jacopo Mondi
2024-05-02 18:36   ` Laurent Pinchart
2024-04-30 10:39 ` [PATCH 14/19] media: max9286: Implement .get_mbus_config() Jacopo Mondi
2024-05-02 18:37   ` Laurent Pinchart
2024-04-30 10:39 ` [PATCH 15/19] media: rcar-csi2: Add support for multiplexed streams Jacopo Mondi
2024-05-02 14:23   ` Niklas Söderlund
2024-05-02 18:41     ` Laurent Pinchart
2024-05-03  8:05       ` Jacopo Mondi
2024-04-30 10:39 ` Jacopo Mondi [this message]
2024-05-02 14:30   ` [PATCH 16/19] media: rcar-csi2: Support multiplexed transmitters Niklas Söderlund
2024-05-02 21:56     ` Laurent Pinchart
2024-05-03  8:07       ` Jacopo Mondi
2024-04-30 10:39 ` [PATCH 17/19] media: rcar-csi2: Store format in the subdev state Jacopo Mondi
2024-05-02 14:32   ` Niklas Söderlund
2024-05-02 22:00     ` Laurent Pinchart
2024-04-30 10:39 ` [PATCH 18/19] media: rcar-csi2: Implement set_routing Jacopo Mondi
2024-05-02 22:16   ` Laurent Pinchart
2024-04-30 10:39 ` [PATCH 19/19] media: rcar-vin: Fix YUYV8_1X16 handling for CSI-2 Jacopo Mondi
2024-05-02 14:33   ` Niklas Söderlund
2024-05-02 22:16     ` Laurent Pinchart
2024-04-30 11:17 ` [PATCH 00/19] media: renesas: rcar-csi2: Support multiple streams Niklas Söderlund
2024-04-30 11:51   ` Jacopo Mondi
2024-05-02 17:35     ` Laurent Pinchart

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240430103956.60190-17-jacopo.mondi@ideasonboard.com \
    --to=jacopo.mondi@ideasonboard.com \
    --cc=kieran.bingham+renesas@ideasonboard.com \
    --cc=laurent.pinchart@ideasonboard.com \
    --cc=linux-media@vger.kernel.org \
    --cc=linux-renesas-soc@vger.kernel.org \
    --cc=niklas.soderlund+renesas@ragnatech.se \
    --cc=sakari.ailus@linux.intel.com \
    --cc=tomi.valkeinen@ideasonboard.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).