mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: George Neacsu via B4 Relay <devnull+geocausa.gmail.com@kernel.org>
To: Sergey Lebedev <lsa.uz@pm.me>,
	 Andre Gilerson <andre.gilerson@gmail.com>
Cc: Sakari Ailus <sakari.ailus@linux.intel.com>,
	 Mauro Carvalho Chehab <mchehab@kernel.org>,
	Rob Herring <robh@kernel.org>,
	 Krzysztof Kozlowski <krzk+dt@kernel.org>,
	 Conor Dooley <conor+dt@kernel.org>,
	Bryan O'Donoghue <bod@kernel.org>,
	 German Pablo Lindo <germanpapulindez@gmail.com>,
	 linux-media@vger.kernel.org, devicetree@vger.kernel.org,
	 linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org,
	 George Neacsu <geocausa@gmail.com>
Subject: [PATCH RFC 2/2] media: i2c: imx681: Add C-PHY support
Date: Thu, 15 Oct 2026 22:25:10 +0100	[thread overview]
Message-ID: <20261015-imx681-x1e-v1-2-fd22f5f3b0c5@gmail.com> (raw)
In-Reply-To: <20261015-imx681-x1e-v1-0-fd22f5f3b0c5@gmail.com>

From: George Neacsu <geocausa@gmail.com>

The IMX681 also drives MIPI C-PHY. On the Surface Pro 11 with a
Snapdragon X Elite it reaches the CAMSS CSIPHY over one C-PHY trio,
which Windows runs at 2.4 Gsym/s.

Only the signalling, the lane count and the output PLL differ from the
two-lane D-PHY set-up of the Intel model. The read-out window and the
pixel array PLL stay as they are, and so does the pixel rate: Windows on
the Snapdragon model programs the same video timing PLL, and its line and
frame lengths give 720 MHz at 30 fps too. Describe each bus by those
three values and its link frequency, pick the one the endpoint asks for,
and write them after the register list:

- CSI_SIGNALING_MODE: 2 for D-PHY, 3 for C-PHY, as CCS numbers them;
- CSI_LANE_MODE: the number of lanes or trios, minus one;
- OP_PLL_MULTIPLIER: 303 for D-PHY as before, 375 for C-PHY, so
  19.2 MHz / 3 * 375 = 2400 Msym/s, a 1.2 GHz link frequency by the V4L2
  convention for C-PHY (half the symbol rate).

CSI_LANE_MODE was not written before. It is now written for D-PHY too,
with the value two lanes need.

Tested on the Surface Pro 11 (X1E80100), with the endpoint on one C-PHY
trio at 1.2 GHz and CAMSS capturing raw frames over CSIPHY2, CSID1 and an
RDI interface: 3844x2640 at 29.99 fps, the colour-bar test pattern
arrives intact, a scene captures, and VBLANK still sets the frame rate
(8.19 fps at 9000 lines). The D-PHY path is not tested here.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: George Neacsu <geocausa@gmail.com>
---
 drivers/media/i2c/imx681.c | 95 +++++++++++++++++++++++++++++++++-------------
 1 file changed, 69 insertions(+), 26 deletions(-)

diff --git a/drivers/media/i2c/imx681.c b/drivers/media/i2c/imx681.c
index 6c8e4b3..c660f06 100644
--- a/drivers/media/i2c/imx681.c
+++ b/drivers/media/i2c/imx681.c
@@ -77,6 +77,9 @@
 #define IMX681_REG_LINE_LENGTH		CCI_REG16(0x0342)
 #define IMX681_REG_ORIENTATION		CCI_REG8(0x0101)
 #define IMX681_REG_CSI_SIGNALING_MODE	CCI_REG8(0x0111)
+#define IMX681_CSI_SIGNALING_DPHY	0x02
+#define IMX681_CSI_SIGNALING_CPHY	0x03
+#define IMX681_REG_CSI_LANE_MODE	CCI_REG8(0x0114)
 #define IMX681_REG_EXTCLK_FREQ_MHZ	CCI_REG16(0x0136)
 #define IMX681_REG_PLL_MULTIPLIER	CCI_REG16(0x0306)
 #define IMX681_REG_OP_PRE_PLL_CLK_DIV	CCI_REG16(0x030c)
@@ -93,7 +96,6 @@
 /* PLL, as the Windows traces configure it and the part reads back. */
 #define IMX681_PLL_MULTIPLIER		225
 #define IMX681_OP_PRE_PLL_CLK_DIV	3
-#define IMX681_OP_PLL_MULTIPLIER	303
 
 /*
  * Where the read-out window sits in the pixel array, and how much array
@@ -119,16 +121,44 @@
 #define IMX681_FRAME_LENGTH_LINES	3177	/* 0x0C69 */
 #define IMX681_FRAME_LENGTH_MAX		0xFFFF	/* 24-bit reg, limit to 16-bit */
 
-/* MIPI lanes */
-#define IMX681_NUM_LANES		2
-
 /*
- * Link frequency derived from PLL settings in Windows trace:
- * EXCK=19.2MHz, PLL2_MUL=303, PLL2_PRE_DIV=3
- * OP output = 19.2 * 303 / 3 = 1939.2 MHz (MIPI bit rate)
- * Link freq = 1939.2 / 2 (DDR) = 969.6 MHz
+ * The part drives either bus. Only the signalling, the lane count and the
+ * output PLL differ between them: the pixel array, its PLL and the read-out
+ * window stay as they are, so the pixel rate below holds for both.
  */
-#define IMX681_LINK_FREQ		969600000LL
+struct imx681_bus {
+	enum v4l2_mbus_type type;
+	unsigned int lanes;
+	u8 signaling_mode;
+	u16 op_pll_multiplier;
+	s64 link_freq;
+};
+
+static const struct imx681_bus imx681_buses[] = {
+	/*
+	 * D-PHY, two lanes, as on the Surface Pro 11 for Business (Intel):
+	 * 19.2 MHz / 3 * 303 = 1939.2 Mbps per lane, so a 969.6 MHz link.
+	 */
+	{
+		.type = V4L2_MBUS_CSI2_DPHY,
+		.lanes = 2,
+		.signaling_mode = IMX681_CSI_SIGNALING_DPHY,
+		.op_pll_multiplier = 303,
+		.link_freq = 969600000,
+	},
+	/*
+	 * C-PHY, one trio, as on the Surface Pro 11 (Snapdragon X Elite):
+	 * 19.2 MHz / 3 * 375 = 2400 Msym/s, and a C-PHY link frequency is half
+	 * the symbol rate.
+	 */
+	{
+		.type = V4L2_MBUS_CSI2_CPHY,
+		.lanes = 1,
+		.signaling_mode = IMX681_CSI_SIGNALING_CPHY,
+		.op_pll_multiplier = 375,
+		.link_freq = 1200000000,
+	},
+};
 
 /*
  * The pixel rate cannot be derived from the CSI-2 link: this part runs two
@@ -154,21 +184,15 @@
 
 #define IMAGE_PAD			0
 
-static const s64 imx681_link_frequencies[] = {
-	IMX681_LINK_FREQ,
-};
-
 /*
  * Sensor init register sequence, captured from Windows I2C traces.
  * This configures the sensor for 3844x2640 RAW10 output at ~30fps
- * with 2-lane MIPI CSI-2, 19.2MHz input clock.
+ * from a 19.2MHz input clock; imx681_start_streaming() adds the bus.
  */
 static const struct cci_reg_sequence imx681_init_regs[] = {
 	{ IMX681_REG_EXTCLK_FREQ_MHZ, IMX681_XCLK_FREQ_ENC },
 	/* Vendor specific configuration */
 	{ CCI_REG16(0x002C), 0x0505 },
-	/* 0x02 is D-PHY, which is the only mode this part offers. */
-	{ IMX681_REG_CSI_SIGNALING_MODE, 0x02 },
 	/* Vendor access unlock sequence */
 	{ CCI_REG8(0x30EB),  0x05 },
 	{ CCI_REG8(0x30EB),  0x0C },
@@ -197,11 +221,10 @@ static const struct cci_reg_sequence imx681_init_regs[] = {
 	 * low byte of PLL_MULTIPLIER and 0x030d the low byte of
 	 * OP_PRE_PLL_CLK_DIV, and each was right only while its high byte
 	 * held the reset value. Written whole, and the part reads back
-	 * 225, 3 and 303 at the operating point either way.
+	 * 225 and 3 at the operating point either way.
 	 */
 	{ IMX681_REG_PLL_MULTIPLIER, IMX681_PLL_MULTIPLIER },
 	{ IMX681_REG_OP_PRE_PLL_CLK_DIV, IMX681_OP_PRE_PLL_CLK_DIV },
-	{ IMX681_REG_OP_PLL_MULTIPLIER, IMX681_OP_PLL_MULTIPLIER },
 	/* Vendor specific registers */
 	{ CCI_REG8(0x7E9B),  0x02 },
 	{ CCI_REG8(0x0368),  0x00 },
@@ -259,6 +282,7 @@ struct imx681 {
 	struct v4l2_ctrl *hflip;
 	struct v4l2_ctrl *vflip;
 
+	const struct imx681_bus *bus;
 	unsigned long link_freq_bitmap;
 };
 
@@ -508,6 +532,17 @@ static int imx681_start_streaming(struct imx681 *imx681)
 		return ret;
 	}
 
+	cci_write(imx681->cci, IMX681_REG_CSI_SIGNALING_MODE,
+		  imx681->bus->signaling_mode, &ret);
+	cci_write(imx681->cci, IMX681_REG_CSI_LANE_MODE,
+		  imx681->bus->lanes - 1, &ret);
+	cci_write(imx681->cci, IMX681_REG_OP_PLL_MULTIPLIER,
+		  imx681->bus->op_pll_multiplier, &ret);
+	if (ret) {
+		dev_err(imx681->dev, "failed to configure the bus: %d\n", ret);
+		return ret;
+	}
+
 	/* Wait for sensor to stabilise after configuration */
 	usleep_range(IMX681_INIT_DELAY_US, IMX681_INIT_DELAY_US + 1000);
 
@@ -711,7 +746,7 @@ static int imx681_init_controls(struct imx681 *imx681)
 					   V4L2_CID_LINK_FREQ,
 					   __fls(imx681->link_freq_bitmap),
 					   __ffs(imx681->link_freq_bitmap),
-					   imx681_link_frequencies);
+					   &imx681->bus->link_freq);
 	if (link_freq)
 		link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
 
@@ -814,9 +849,10 @@ static int imx681_parse_endpoint(struct imx681 *imx681)
 {
 	struct fwnode_handle *fwnode = dev_fwnode(imx681->dev);
 	struct v4l2_fwnode_endpoint bus_cfg = {
-		.bus_type = V4L2_MBUS_CSI2_DPHY,
+		.bus_type = V4L2_MBUS_UNKNOWN,
 	};
 	struct fwnode_handle *ep;
+	unsigned int lanes;
 	int ret;
 
 	ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0, 0);
@@ -827,19 +863,26 @@ static int imx681_parse_endpoint(struct imx681 *imx681)
 		return dev_err_probe(imx681->dev, ret,
 				     "failed to parse endpoint\n");
 
-	if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX681_NUM_LANES) {
+	/* Without a bus-type, an endpoint with data-lanes parses as D-PHY. */
+	lanes = bus_cfg.bus.mipi_csi2.num_data_lanes;
+	for (unsigned int i = 0; i < ARRAY_SIZE(imx681_buses); i++) {
+		if (imx681_buses[i].type == bus_cfg.bus_type &&
+		    imx681_buses[i].lanes == lanes)
+			imx681->bus = &imx681_buses[i];
+	}
+
+	if (!imx681->bus) {
 		ret = dev_err_probe(imx681->dev, -EINVAL,
-				    "expected %d data lanes, got %d\n",
-				    IMX681_NUM_LANES,
-				    bus_cfg.bus.mipi_csi2.num_data_lanes);
+				    "unsupported bus: %s with %u lanes\n",
+				    bus_cfg.bus_type == V4L2_MBUS_CSI2_CPHY ?
+				    "C-PHY" : "D-PHY", lanes);
 		goto done;
 	}
 
 	ret = v4l2_link_freq_to_bitmap(imx681->dev,
 				       bus_cfg.link_frequencies,
 				       bus_cfg.nr_of_link_frequencies,
-				       imx681_link_frequencies,
-				       ARRAY_SIZE(imx681_link_frequencies),
+				       &imx681->bus->link_freq, 1,
 				       &imx681->link_freq_bitmap);
 	if (ret)
 		ret = dev_err_probe(imx681->dev, ret,

-- 
2.53.0



  parent reply	other threads:[~2026-10-15 21:25 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-15 21:25 [PATCH RFC 0/2] media: imx681: C-PHY for the Surface Pro 11 (Snapdragon) George Neacsu via B4 Relay
2026-10-15 21:25 ` [PATCH RFC 1/2] dt-bindings: media: sony,imx681: Allow a C-PHY bus George Neacsu via B4 Relay
2026-10-16 15:59   ` Bryan O'Donoghue
2026-10-16 16:08     ` George Neacsu
2026-10-15 21:25 ` George Neacsu via B4 Relay [this message]
2026-10-16 15:54   ` [PATCH RFC 2/2] media: i2c: imx681: Add C-PHY support Bryan O'Donoghue
2026-10-16 16:08     ` George Neacsu
2026-10-16 21:14       ` Sergey Lebedev
2026-10-16 22:25         ` George Neacsu
2026-10-16  7:00 ` [PATCH RFC 0/2] media: imx681: C-PHY for the Surface Pro 11 (Snapdragon) Sergey Lebedev
2026-10-16 14:55   ` George Neacsu

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=20261015-imx681-x1e-v1-2-fd22f5f3b0c5@gmail.com \
    --to=devnull+geocausa.gmail.com@kernel.org \
    --cc=andre.gilerson@gmail.com \
    --cc=bod@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=geocausa@gmail.com \
    --cc=germanpapulindez@gmail.com \
    --cc=krzk+dt@kernel.org \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=lsa.uz@pm.me \
    --cc=mchehab@kernel.org \
    --cc=robh@kernel.org \
    --cc=sakari.ailus@linux.intel.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

all inboxes | Powered by JetHome®