* [PATCH RFC 0/2] media: imx681: C-PHY for the Surface Pro 11 (Snapdragon)
@ 2026-10-15 21:25 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
` (2 more replies)
0 siblings, 3 replies; 11+ messages in thread
From: George Neacsu via B4 Relay @ 2026-10-15 21:25 UTC (permalink / raw)
To: Sergey Lebedev, Andre Gilerson
Cc: Sakari Ailus, Mauro Carvalho Chehab, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Bryan O'Donoghue,
German Pablo Lindo, linux-media, devicetree, linux-arm-msm,
linux-kernel, George Neacsu
Hi Sergey, Andre,
The Snapdragon X Elite Surface Pro 11 (X1E80100) has the same IMX681 as its
user-facing camera, but wired to the Qualcomm CAMSS over one MIPI C-PHY
trio instead of two D-PHY lanes. These two patches, on top of your v8, are
all your driver needs for it: the endpoint picks the bus, and only the
signalling mode, the lane mode and the output PLL multiplier depend on it.
The mode, the video timing PLL and the 720 MHz pixel rate stay as they are;
Windows on the Snapdragon model programs the same video timing, and its
line and frame lengths come to 720 MHz at 30 fps as well, which agrees
with your measurement.
They apply on top of v8:
https://lore.kernel.org/all/20261002082537.74084-1-lsa.uz@pm.me/
Tested on the Surface Pro 11 (X1E80100) with an out-of-tree CAMSS that
receives C-PHY on CSIPHY2, capturing raw frames through CSID1 and an RDI
interface:
- probe with the endpoint on one C-PHY trio at a 1.2 GHz link frequency
(2.4 Gsym/s, half the symbol rate by the V4L2 convention);
- 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 binding accepts one trio with bus-type 1 and two lanes without a
bus-type, and rejects one D-PHY lane or two C-PHY trios.
Not tested: the D-PHY path on the Intel model. It now also writes
CSI_LANE_MODE = 1 (two lanes), which it did not write before. German, if
you have a moment to run v8 with these two on top, that would settle it.
How would you like to take this? I am happy for you to fold the two into
your next version, or to resend them as follow-ups once v8 is merged,
whichever is easier for you and for Sakari.
The board side, the device tree for the Surface Pro 11 cameras, depends on
Bryan's x1e80100 camera DT and CSI2 PHY series and on C-PHY support for
X1E in the PHY driver, so I will send it separately once those settle.
Thanks for the driver,
George
---
George Neacsu (2):
dt-bindings: media: sony,imx681: Allow a C-PHY bus
media: i2c: imx681: Add C-PHY support
.../devicetree/bindings/media/i2c/sony,imx681.yaml | 26 +++++-
drivers/media/i2c/imx681.c | 95 ++++++++++++++++------
2 files changed, 93 insertions(+), 28 deletions(-)
---
base-commit: 07ec64fc237bfa815b380cc4e0c8b7d01ee11681
change-id: 20261015-imx681-x1e-837dd6b45554
Best regards,
--
George Neacsu <geocausa@gmail.com>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH RFC 1/2] dt-bindings: media: sony,imx681: Allow a C-PHY bus
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 ` George Neacsu via B4 Relay
2026-10-16 15:59 ` Bryan O'Donoghue
2026-10-15 21:25 ` [PATCH RFC 2/2] media: i2c: imx681: Add C-PHY support George Neacsu via B4 Relay
2026-10-16 7:00 ` [PATCH RFC 0/2] media: imx681: C-PHY for the Surface Pro 11 (Snapdragon) Sergey Lebedev
2 siblings, 1 reply; 11+ messages in thread
From: George Neacsu via B4 Relay @ 2026-10-15 21:25 UTC (permalink / raw)
To: Sergey Lebedev, Andre Gilerson
Cc: Sakari Ailus, Mauro Carvalho Chehab, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Bryan O'Donoghue,
German Pablo Lindo, linux-media, devicetree, linux-arm-msm,
linux-kernel, George Neacsu
From: George Neacsu <geocausa@gmail.com>
On the Surface Pro 11 with a Snapdragon X Elite, the IMX681 reaches the
CAMSS CSIPHY over one MIPI C-PHY trio rather than over the two D-PHY
lanes of the Intel model. Let bus-type select C-PHY, and with it a single
data lane, keeping two D-PHY lanes as the default.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: George Neacsu <geocausa@gmail.com>
---
.../devicetree/bindings/media/i2c/sony,imx681.yaml | 26 ++++++++++++++++++++--
1 file changed, 24 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx681.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx681.yaml
index 62e7eae..51256a7 100644
--- a/Documentation/devicetree/bindings/media/i2c/sony,imx681.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/sony,imx681.yaml
@@ -14,8 +14,8 @@ description:
The Sony IMX681 is a CMOS active pixel image sensor with a square pixel
array, used as the user-facing camera on several Microsoft Surface devices.
It operates from analog 2.8 V, digital 1.05 V and interface 1.8 V supplies,
- is programmable over I2C, and outputs 10-bit Bayer data over a two-lane
- MIPI CSI-2 D-PHY interface.
+ is programmable over I2C, and outputs 10-bit Bayer data over MIPI CSI-2,
+ either on two D-PHY lanes or on one C-PHY trio.
allOf:
- $ref: /schemas/media/video-interface-devices.yaml#
@@ -54,7 +54,14 @@ properties:
unevaluatedProperties: false
properties:
+ bus-type:
+ enum:
+ - 1 # MEDIA_BUS_TYPE_CSI2_CPHY
+ - 4 # MEDIA_BUS_TYPE_CSI2_DPHY
+ default: 4
+
data-lanes:
+ minItems: 1
items:
- const: 1
- const: 2
@@ -63,6 +70,21 @@ properties:
- data-lanes
- link-frequencies
+ if:
+ properties:
+ bus-type:
+ const: 1
+ required:
+ - bus-type
+ then:
+ properties:
+ data-lanes:
+ maxItems: 1
+ else:
+ properties:
+ data-lanes:
+ minItems: 2
+
required:
- endpoint
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH RFC 2/2] media: i2c: imx681: Add C-PHY support
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-15 21:25 ` George Neacsu via B4 Relay
2026-10-16 15:54 ` Bryan O'Donoghue
2026-10-16 7:00 ` [PATCH RFC 0/2] media: imx681: C-PHY for the Surface Pro 11 (Snapdragon) Sergey Lebedev
2 siblings, 1 reply; 11+ messages in thread
From: George Neacsu via B4 Relay @ 2026-10-15 21:25 UTC (permalink / raw)
To: Sergey Lebedev, Andre Gilerson
Cc: Sakari Ailus, Mauro Carvalho Chehab, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Bryan O'Donoghue,
German Pablo Lindo, linux-media, devicetree, linux-arm-msm,
linux-kernel, George Neacsu
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
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH RFC 0/2] media: imx681: C-PHY for the Surface Pro 11 (Snapdragon)
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-15 21:25 ` [PATCH RFC 2/2] media: i2c: imx681: Add C-PHY support George Neacsu via B4 Relay
@ 2026-10-16 7:00 ` Sergey Lebedev
2026-10-16 14:55 ` George Neacsu
2 siblings, 1 reply; 11+ messages in thread
From: Sergey Lebedev @ 2026-10-16 7:00 UTC (permalink / raw)
To: George Neacsu, Andre Gilerson
Cc: Sakari Ailus, Mauro Carvalho Chehab, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Bryan O'Donoghue,
German Pablo Lindo, linux-media, devicetree, linux-arm-msm,
linux-kernel
Hi George,
Thank you for this, and for testing it on the Snapdragon model.
The D-PHY path on the Intel model works with 2/2 on top of v8; 1/2 has
nothing to check on ACPI. On media next with IPU7, the sensor streams
3844x2640 at 30.01 fps, and v4l2-compliance passes 46 of 46 on the
subdev and 54 of 54 through the media device. While streaming it reads
back CSI_SIGNALING_MODE 0x02, CSI_LANE_MODE 0x01 and OP_PLL_MULTIPLIER
303. CSI_LANE_MODE is 0x01 here without your write too: neither v8 nor
the driver this machine runs daily writes it, and with the latter it
reads 0x01 while streaming. So writing it changes nothing on this
machine.
For 2/2:
Tested-by: Sergey Lebedev <lsa.uz@pm.me> # Surface Pro 11 (Intel), D-PHY
As for how to take them, I would keep v8 as it is, since it has Andre's
Ack and Krzysztof's review, and have yours follow once it is merged.
Andre, does that suit you?
Sergey
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH RFC 0/2] media: imx681: C-PHY for the Surface Pro 11 (Snapdragon)
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
0 siblings, 0 replies; 11+ messages in thread
From: George Neacsu @ 2026-10-16 14:55 UTC (permalink / raw)
To: Sergey Lebedev, Andre Gilerson
Cc: Sakari Ailus, Mauro Carvalho Chehab, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Bryan O'Donoghue,
German Pablo Lindo, linux-media, devicetree, linux-arm-msm,
linux-kernel
Hi Sergey,
Thank you for testing 2/2 on the Intel model, and for the register
readbacks.
On 16/10/2026 08:00, Sergey Lebedev wrote:
> As for how to take them, I would keep v8 as it is, since it has Andre's
> Ack and Krzysztof's review, and have yours follow once it is merged.
Agreed. I will resend these two on top of v8 once it is merged.
On CSI_LANE_MODE: since it reads 0x01 on your machine without the
write, I will check whether C-PHY on the Snapdragon model streams
without it as well, and drop the write from 2/2 if it does.
Thanks,
George
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH RFC 2/2] media: i2c: imx681: Add C-PHY support
2026-10-15 21:25 ` [PATCH RFC 2/2] media: i2c: imx681: Add C-PHY support George Neacsu via B4 Relay
@ 2026-10-16 15:54 ` Bryan O'Donoghue
2026-10-16 16:08 ` George Neacsu
0 siblings, 1 reply; 11+ messages in thread
From: Bryan O'Donoghue @ 2026-10-16 15:54 UTC (permalink / raw)
To: geocausa, Sergey Lebedev, Andre Gilerson
Cc: Sakari Ailus, Mauro Carvalho Chehab, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, German Pablo Lindo,
linux-media, devicetree, linux-arm-msm, linux-kernel
On 15/10/2026 22:25, George Neacsu via B4 Relay wrote:
> 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.
How do we know that ?
> 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.
How do we know what windows does on the Snapdragon ? Has the file been
analysed ?
Are we inferring what Windows does or asserting what it does ?
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
How do we know 0x03 means CPHY ? Could it bit 0x01 for example ?
The commit log needs to elucidate that information - I guess, I tested,
the LLM told me so, reverse engineered the windows driver binaries -
whatever.
I'm not concerned about the provenance of the information more its
correctness.
> +#define IMX681_REG_CSI_LANE_MODE CCI_REG8(0x0114)
Same here. I _suspect_ the answer is opening the sensor init files on
the windows partition, matching the patterns and inferring the missing
register but, lets document that process somewhere.
> #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
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH RFC 1/2] dt-bindings: media: sony,imx681: Allow a C-PHY bus
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
0 siblings, 1 reply; 11+ messages in thread
From: Bryan O'Donoghue @ 2026-10-16 15:59 UTC (permalink / raw)
To: geocausa, Sergey Lebedev, Andre Gilerson
Cc: Sakari Ailus, Mauro Carvalho Chehab, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, German Pablo Lindo,
linux-media, devicetree, linux-arm-msm, linux-kernel
On 15/10/2026 22:25, George Neacsu via B4 Relay wrote:
> From: George Neacsu <geocausa@gmail.com>
>
> On the Surface Pro 11 with a Snapdragon X Elite, the IMX681 reaches the
> CAMSS CSIPHY over one MIPI C-PHY trio rather than over the two D-PHY
> lanes of the Intel model. Let bus-type select C-PHY, and with it a single
> data lane, keeping two D-PHY lanes as the default.
>
> Assisted-by: Claude:claude-opus-5-5
> Signed-off-by: George Neacsu <geocausa@gmail.com>
> ---
> .../devicetree/bindings/media/i2c/sony,imx681.yaml | 26 ++++++++++++++++++++--
> 1 file changed, 24 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx681.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx681.yaml
> index 62e7eae..51256a7 100644
> --- a/Documentation/devicetree/bindings/media/i2c/sony,imx681.yaml
> +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx681.yaml
> @@ -14,8 +14,8 @@ description:
> The Sony IMX681 is a CMOS active pixel image sensor with a square pixel
> array, used as the user-facing camera on several Microsoft Surface devices.
> It operates from analog 2.8 V, digital 1.05 V and interface 1.8 V supplies,
> - is programmable over I2C, and outputs 10-bit Bayer data over a two-lane
> - MIPI CSI-2 D-PHY interface.
> + is programmable over I2C, and outputs 10-bit Bayer data over MIPI CSI-2,
> + either on two D-PHY lanes or on one C-PHY trio.
>
> allOf:
> - $ref: /schemas/media/video-interface-devices.yaml#
> @@ -54,7 +54,14 @@ properties:
> unevaluatedProperties: false
>
> properties:
> + bus-type:
> + enum:
> + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY
> + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY
> + default: 4
> +
> data-lanes:
> + minItems: 1
> items:
> - const: 1
> - const: 2
> @@ -63,6 +70,21 @@ properties:
> - data-lanes
> - link-frequencies
>
> + if:
> + properties:
> + bus-type:
> + const: 1
> + required:
> + - bus-type
> + then:
> + properties:
> + data-lanes:
> + maxItems: 1
> + else:
> + properties:
> + data-lanes:
> + minItems: 2
> +
I think you should constrain the existing minItems for DPHY mode and
then add CPHY on top of it.
Your commit log says Add CPHY mode but then it goes on to do other
things like this.
Break the two things up into different patches.
> required:
> - endpoint
>
>
> --
> 2.53.0
---
bod
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH RFC 2/2] media: i2c: imx681: Add C-PHY support
2026-10-16 15:54 ` Bryan O'Donoghue
@ 2026-10-16 16:08 ` George Neacsu
2026-10-16 21:14 ` Sergey Lebedev
0 siblings, 1 reply; 11+ messages in thread
From: George Neacsu @ 2026-10-16 16:08 UTC (permalink / raw)
To: Bryan O'Donoghue
Cc: Sergey Lebedev, Andre Gilerson, Sakari Ailus,
Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, German Pablo Lindo, linux-media, devicetree,
linux-arm-msm, linux-kernel
Hi Bryan,
The values come from two places on this machine: the Windows sensor
file, and a kernel debugger attached to Windows while it streamed. They
were then checked against the CCS definitions and on Linux. In detail:
1. The Windows sensor file. The Windows install on this Surface Pro 11
has a Qualcomm sensor module file for the IMX681 (SONY0681). A script
here parses its register tables: an init table of 364 writes and six
per-mode tables of 68 writes. Nothing from the file is
redistributed; only register values are used.
2. Windows observed live. With a second machine as kernel debugger, I
set a breakpoint in the Windows front sensor driver just before it
applies the mode table, started the stock Windows Camera app, and
dumped the 68 writes it sent. They match the file's 3840x2160 30 fps
mode exactly. That live table includes
0x0114 = 0x00 (CSI_LANE_MODE),
0x0306/0x0307 = 225, 0x030d = 3, 0x030e/0x030f = 375,
line length 6752, frame length 3554.
In separate debugger sessions I read the CSIPHY2 registers while
Windows streamed. The receiver was in C-PHY mode, and its settings
match Qualcomm's GPL 2.5 Gsps C-PHY table (121 registers, compared
from two snapshots).
CSI_SIGNALING_MODE = 0x03 is in the init table. I have it from the
file, but I did not catch Windows writing it live.
3. Why 0x03 means C-PHY. 0x0111 is CSI_SIGNALING_MODE and 0x0114
CSI_LANE_MODE in the MIPI CCS register map, as in
drivers/media/i2c/ccs/ccs-regs.h: 2 is CSI-2 D-PHY, 3 is CSI-2 C-PHY
(0 and 1 are the old CCP2 modes), and the lane mode is the lane or
trio count minus one. This agrees with the hardware: Sergey reads
back 0x02 and 0x01 on the Intel model while streaming two D-PHY
lanes; the Windows receiver here runs C-PHY on one trio; and under
Linux, with 0x03 and 0x00, the sensor streams into the PHY in C-PHY
mode.
4. The 2.4 Gsym/s is calculated, not measured: 19.2 MHz / 3 * 375 from
the OP PLL values Windows writes. The receiver does not report the
rate, and it locks over a wide range of settle counts, so a working
capture does not prove the exact value. It does fall in the 2.5 Gsps
bin that Windows programs into the receiver. Likewise,
6752 * 3554 * 30 = 719.9 MHz matches v8's pixel rate, and the pixel
PLL (225) is the one v8 already writes.
In the next version the commit message will say which values come from
the live Windows capture, which come only from the sensor file, which
are calculated, and what was checked on Linux. The extraction and
capture process will also be written up in the project repository and
linked. I will also read the CCS capability register 0x1602 (bit 3 =
C-PHY) here, and Sergey may be able to read it on the Intel model.
Thanks,
George
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH RFC 1/2] dt-bindings: media: sony,imx681: Allow a C-PHY bus
2026-10-16 15:59 ` Bryan O'Donoghue
@ 2026-10-16 16:08 ` George Neacsu
0 siblings, 0 replies; 11+ messages in thread
From: George Neacsu @ 2026-10-16 16:08 UTC (permalink / raw)
To: Bryan O'Donoghue
Cc: Sergey Lebedev, Andre Gilerson, Sakari Ailus,
Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, German Pablo Lindo, linux-media, devicetree,
linux-arm-msm, linux-kernel
Hi Bryan,
On 16/10/2026 16:59, Bryan O'Donoghue wrote:
> I think you should constrain the existing minItems for DPHY mode and
> then add CPHY on top of it.
>
> Your commit log says Add CPHY mode but then it goes on to do other
> things like this.
>
> Break the two things up into different patches.
Will do. The next version will have one patch that constrains the
existing D-PHY data-lanes, and a second that adds bus-type C-PHY with
one trio on top of it. As agreed with Sergey, it will follow once v8 is
merged.
Thanks,
George
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH RFC 2/2] media: i2c: imx681: Add C-PHY support
2026-10-16 16:08 ` George Neacsu
@ 2026-10-16 21:14 ` Sergey Lebedev
2026-10-16 22:25 ` George Neacsu
0 siblings, 1 reply; 11+ messages in thread
From: Sergey Lebedev @ 2026-10-16 21:14 UTC (permalink / raw)
To: George Neacsu, Bryan O'Donoghue
Cc: Andre Gilerson, Sakari Ailus, Mauro Carvalho Chehab, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, German Pablo Lindo,
linux-media, devicetree, linux-arm-msm, linux-kernel
Hi George,
On 16/10/2026 17:08, George Neacsu wrote:
> I will also read the CCS capability register 0x1602 (bit 3 =
> C-PHY) here, and Sergey may be able to read it on the Intel model.
Read on the Intel model while the sensor streams on two D-PHY lanes
(model 0x0681): CSI_SIGNALING_MODE_CAPABILITY, 0x1602, is 0x04, so bit 2
(D-PHY) is set and bit 3 (C-PHY) is not. PHY_CTRL_CAPABILITY, 0x1600,
reads 0xff, every bit set, which is also what an unimplemented register
can return, so I would not lean on this block alone. Either way it does
not show C-PHY here; your reading on the Snapdragon model will tell
whether the two differ.
Sergey
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH RFC 2/2] media: i2c: imx681: Add C-PHY support
2026-10-16 21:14 ` Sergey Lebedev
@ 2026-10-16 22:25 ` George Neacsu
0 siblings, 0 replies; 11+ messages in thread
From: George Neacsu @ 2026-10-16 22:25 UTC (permalink / raw)
To: Sergey Lebedev, Bryan O'Donoghue
Cc: Andre Gilerson, Sakari Ailus, Mauro Carvalho Chehab, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, German Pablo Lindo,
linux-media, devicetree, linux-arm-msm, linux-kernel
Hi Sergey,
On 16/10/2026 22:14, Sergey Lebedev wrote:
> Either way it does not show C-PHY here; your reading on the Snapdragon
> model will tell whether the two differ.
They do not. Read on the Snapdragon model while the sensor streamed
3844x2640 at 30 fps over one C-PHY trio (linux-next with the PHY
series):
0x0016/0x0017 = 0x06 0x81 (model 0x0681)
0x0111 = 0x03 (CSI_SIGNALING_MODE, as written)
0x0114 = 0x00 (CSI_LANE_MODE, as written)
0x1600 = 0xff, 0x1601 = 0x03, 0x1602 = 0x04, 0x1603 = 0x01
So 0x1602 reports D-PHY only even while the part is transmitting C-PHY,
the same values as on the Intel model. The capability block cannot
confirm the mode on this sensor either way, so I will leave it out of
the evidence in the commit message and say why.
Thanks for reading it on your side.
George
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-10-16 22:25 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [PATCH RFC 2/2] media: i2c: imx681: Add C-PHY support George Neacsu via B4 Relay
2026-10-16 15:54 ` 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
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox