Linux-PHY Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Bryan O'Donoghue <bod@kernel.org>
To: geocausa@gmail.com, Vinod Koul <vkoul@kernel.org>,
	David Heidelberg <david@ixit.cz>
Cc: Neil Armstrong <neil.armstrong@linaro.org>,
	Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>,
	Loic Poulain <loic.poulain@oss.qualcomm.com>,
	Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>,
	Nihal Kumar Gupta <nihal.gupta@oss.qualcomm.com>,
	Vikram Sharma <vikram.sharma@oss.qualcomm.com>,
	Suresh Vankadara <svankada@qti.qualcomm.com>,
	tjose@qti.qualcomm.com,
	Mauro Carvalho Chehab <mchehab@kernel.org>,
	Manivannan Sadhasivam <mani@kernel.org>,
	linux-phy@lists.infradead.org, linux-media@vger.kernel.org,
	linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH RFC v2 4/4] phy: qcom-mipi-csi2: Add C-PHY support for X1E80100
Date: Fri, 16 Oct 2026 12:15:31 +0100	[thread overview]
Message-ID: <85393f60-d89a-4e31-b398-722e5414fe58@kernel.org> (raw)
In-Reply-To: <20261016-x1e-cphy-rfc-v2-4-377654253193@gmail.com>

On 16/10/2026 11:57, George Neacsu via B4 Relay wrote:
> From: George Neacsu <geocausa@gmail.com>
> 
> The CSI2 PHY schema already allows bus-type C-PHY, but the driver
> rejects it. Accept it on SoCs that provide a C-PHY sequence, with up to
> three trios and no lane polarities. The endpoint's bus-type now sets the
> PHY mode, set_mode() only accepts that mode, and in C-PHY mode
> configure() takes the symbol rate and keeps the matching link frequency,
> so clock scaling works as for D-PHY.
> 
> lanes_enable() picks the sequence for the mode and is split into
> helpers: the CTRL5 lane mask, where the trios are the odd bits and the
> D-PHY data lanes the even ones plus the clock bit, and the D-PHY lane
> polarities. The settle count is computed in both modes. With a C-PHY
> link frequency of half the symbol rate, the unit interval in the
> existing formula is one symbol, which is also how David Heidelberg's
> CAMSS C-PHY series computes it. Sequence delays now use fsleep(), as
> the C-PHY sequence waits 3 ms after its reset exit.
> 
> The X1E80100 sequence is Qualcomm's for the v2.1.2 3PH PHY at 2.5 Gsps,
> taken from the downstream camera driver's GPL-2.0
> cam_csiphy_2_1_2_hwreg.h (csiphy_3ph_v2_1_2_reg, datarate_212_2p5Gsps
> and the 3PH reset exit), as the D-PHY sequence above it is. Its three
> settle count entries are the ones downstream marks as such.
> 
> Tested on the Surface Pro 11 (X1E80100) with an IMX681 on CSIPHY2, one
> trio at 2.4 Gsym/s, and raw capture through CSID1 and an RDI interface,
> with David's CAMSS C-PHY series and two CAMSS patches on top:
> 3844x2640 at 30 fps, and the sensor's colour bars are byte-identical to
> the same pattern captured with an out-of-tree driver. The timer runs at
> 400 MHz and the computed settle count is 28; forcing anything from 6 to
> 48 gives the same frames. D-PHY is unchanged: an OV13858 on CSIPHY1
> over four lanes streams at 29.9 fps with the same settle count as
> before.
> 
> Assisted-by: Claude:claude-opus-5-5
> Signed-off-by: George Neacsu <geocausa@gmail.com>
> ---
>   drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c | 216 ++++++++++++++++++---
>   drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c     |  53 ++++-
>   drivers/phy/qualcomm/phy-qcom-mipi-csi2.h          |   2 +
>   3 files changed, 242 insertions(+), 29 deletions(-)
> 
> diff --git a/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c
> index b2d4ec3e8..39e48e7ac 100644
> --- a/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c
> +++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c
> @@ -161,6 +161,137 @@ mipi_csi2phy_lane_regs lane_regs_x1e80100[] = {
>   	{.reg_addr = 0x0c64, .reg_data = 0x7f, .param_type = CSIPHY_SKEW_CAL},
>   };
> 
> +/* Qualcomm's 2.5 Gsps settings, from the GPL-2.0 cam_csiphy_2_1_2_hwreg.h */
> +static const struct
> +mipi_csi2phy_lane_regs lane_regs_v2_1_2_cphy[] = {
> +	/* Power up lanes 3ph mode */
> +	{.reg_addr = 0x101c, .reg_data = 0x7a, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x1018, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
> +
> +	/* datarate_212_2p5Gsps: AFE settings */
> +	{.reg_addr = 0x0268, .reg_data = 0xf1, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0294, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0288, .reg_data = 0x20, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0278, .reg_data = 0x20, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x026c, .reg_data = 0x3d, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x028c, .reg_data = 0x30, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0270, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0274, .reg_data = 0x03, .param_type = CSIPHY_DEFAULT_PARAMS},
> +
> +	{.reg_addr = 0x0668, .reg_data = 0xf1, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0694, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0688, .reg_data = 0x20, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0678, .reg_data = 0x20, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x066c, .reg_data = 0x3d, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x068c, .reg_data = 0x30, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0670, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0674, .reg_data = 0x03, .param_type = CSIPHY_DEFAULT_PARAMS},
> +
> +	{.reg_addr = 0x0a68, .reg_data = 0xf1, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a94, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a88, .reg_data = 0x20, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a78, .reg_data = 0x20, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a6c, .reg_data = 0x3d, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a8c, .reg_data = 0x30, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a70, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a74, .reg_data = 0x03, .delay_us = 10, .param_type = CSIPHY_DEFAULT_PARAMS},
> +
> +	/* csiphy_3ph_v2_1_2_reg */
> +	{.reg_addr = 0x02f4, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x02f8, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x02fc, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x02f0, .reg_data = 0xef, .delay_us = 3, .param_type = CSIPHY_DEFAULT_PARAMS},
> +
> +	{.reg_addr = 0x06f4, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x06f8, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x06fc, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x06f0, .reg_data = 0xef, .delay_us = 3, .param_type = CSIPHY_DEFAULT_PARAMS},
> +
> +	{.reg_addr = 0x0af4, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0af8, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0afc, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0af0, .reg_data = 0xef, .delay_us = 3, .param_type = CSIPHY_DEFAULT_PARAMS},
> +
> +	/* datarate_212_2p5Gsps, then csiphy_3ph_v2_1_2_reg */
> +	{.reg_addr = 0x020c, .reg_data = 0x22, .param_type = CSIPHY_SETTLE_CNT_LOWER_BYTE},
> +	{.reg_addr = 0x0208, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0210, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0214, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0204, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x02e4, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x02e8, .reg_data = 0x7f, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x02ec, .reg_data = 0x7f, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0218, .reg_data = 0x3e, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x021c, .reg_data = 0x41, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0220, .reg_data = 0x41, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0224, .reg_data = 0x7f, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0228, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x022c, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0264, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0244, .reg_data = 0xb2, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0310, .reg_data = 0x35, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x02bc, .reg_data = 0xd0, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0254, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0240, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0260, .reg_data = 0xa8, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0284, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0290, .reg_data = 0x02, .param_type = CSIPHY_DEFAULT_PARAMS},
> +
> +	/* datarate_212_2p5Gsps, then csiphy_3ph_v2_1_2_reg */
> +	{.reg_addr = 0x060c, .reg_data = 0x22, .param_type = CSIPHY_SETTLE_CNT_LOWER_BYTE},
> +	{.reg_addr = 0x0608, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0610, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0614, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0604, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x06e4, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x06e8, .reg_data = 0x7f, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x06ec, .reg_data = 0x7f, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0618, .reg_data = 0x3e, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x061c, .reg_data = 0x41, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0620, .reg_data = 0x41, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0624, .reg_data = 0x7f, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0628, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x062c, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0664, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0644, .reg_data = 0xb2, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0710, .reg_data = 0x35, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x06bc, .reg_data = 0xd0, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0654, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0640, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0660, .reg_data = 0xa8, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0684, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0690, .reg_data = 0x02, .param_type = CSIPHY_DEFAULT_PARAMS},
> +
> +	/* datarate_212_2p5Gsps, then csiphy_3ph_v2_1_2_reg */
> +	{.reg_addr = 0x0a0c, .reg_data = 0x22, .param_type = CSIPHY_SETTLE_CNT_LOWER_BYTE},
> +	{.reg_addr = 0x0a08, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a10, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a14, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a04, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0ae4, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0ae8, .reg_data = 0x7f, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0aec, .reg_data = 0x7f, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a18, .reg_data = 0x3e, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a1c, .reg_data = 0x41, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a20, .reg_data = 0x41, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a24, .reg_data = 0x7f, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a28, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a2c, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a64, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a44, .reg_data = 0xb2, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0b10, .reg_data = 0x35, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0abc, .reg_data = 0xd0, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a54, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a40, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a60, .reg_data = 0xa8, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a84, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
> +	{.reg_addr = 0x0a90, .reg_data = 0x02, .param_type = CSIPHY_DEFAULT_PARAMS},
> +
> +	/* csiphy_reset_exit_reg_2_1_2, 3PH */
> +	{.reg_addr = 0x1000, .reg_data = 0x0e, .delay_us = 3048,
> +	 .param_type = CSIPHY_DEFAULT_PARAMS},
> +};
> +
>   static inline const struct mipi_csi2phy_device_regs *
>   csi2phy_dev_to_regs(struct mipi_csi2phy_device *csi2phy)
>   {
> @@ -246,9 +377,18 @@ phy_qcom_mipi_csi2_gen2_config_lanes(struct mipi_csi2phy_device *csi2phy,
>   				     u8 settle_cnt)
>   {
>   	const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy);
> -	const struct mipi_csi2phy_lane_regs *r = regs->dphy_init_seq;
> -	int i, array_size = regs->dphy_lane_array_size;
> +	const struct mipi_csi2phy_lane_regs *r;
> +	int array_size;
>   	u32 val;
> +	int i;
> +
> +	if (csi2phy->phy_mode == PHY_MODE_MIPI_CPHY) {
> +		r = regs->cphy_init_seq;
> +		array_size = regs->cphy_lane_array_size;
> +	} else {
> +		r = regs->dphy_init_seq;
> +		array_size = regs->dphy_lane_array_size;
> +	}
> 
>   	for (i = 0; i < array_size; i++, r++) {
>   		switch (r->param_type) {
> @@ -264,38 +404,41 @@ phy_qcom_mipi_csi2_gen2_config_lanes(struct mipi_csi2phy_device *csi2phy,
>   		}
>   		writel(val, csi2phy->base + r->reg_addr);
>   		if (r->delay_us)
> -			udelay(r->delay_us);
> +			fsleep(r->delay_us);

Here's one that is trying to sneak in under the radar.

A change like this needs a distinct patch with a description why instead 
of inlined in a "make CPHY work" patch.

A delay is a delay is a delay - changing the nature of that delay is a 
functional change in itself which is ! a CPHY thing.

>   	}
>   }
> 
> -static int phy_qcom_mipi_csi2_lanes_enable(struct mipi_csi2phy_device *csi2phy,
> -					   struct mipi_csi2phy_stream_cfg *cfg)
> +/*
> + * CSI_COMMON_CTRL5 is a physical lane power-up bitmap:
> + * - Bits [0,2,4,6] → D-PHY data lanes(LN0, LN2, LN4, LN6)
> + * - Bits [1,3,5] → C-PHY trio lanes(LN1, LN3, LN5)
> + * - Bit [7] → D-PHY clock lane(LNCK) dedicated clock enable
> + */
> +static u8 phy_qcom_mipi_csi2_lane_mask(struct mipi_csi2phy_device *csi2phy,
> +				       struct mipi_csi2phy_stream_cfg *cfg)
>   {
> -	const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy);
>   	struct mipi_csi2phy_lanes_cfg *lane_cfg = &cfg->lane_cfg;
> -	u8 settle_cnt;
> -	u8 val;
> +	u8 lane_mask = 0;
> +	u8 offset = 0;
>   	int i;
> 
> -	if (cfg->link_freq <= 0)
> -		return -EINVAL;
> -
> -	settle_cnt = phy_qcom_mipi_csi2_settle_cnt_calc(cfg->link_freq, csi2phy->timer_clk_rate);
> -	if (!settle_cnt)
> -		return -ENODEV;

To make the decomposition more readable, please bundle the deomp into 
its own precursor patch "add helper function x"

> +	if (csi2phy->phy_mode == PHY_MODE_MIPI_CPHY)
> +		offset = 1;
> +	else
> +		lane_mask = BIT(lane_cfg->clk.pos);
> 
> -	/*
> -	 * CSI_COMMON_CTRL5 is a physical lane power-up bitmap:
> -	 * - Bits [0,2,4,6] → D-PHY data lanes(LN0, LN2, LN4, LN6)
> -	 * - Bits [1,3,5] → C-PHY trio lanes(LN1, LN3, LN5)
> -	 * - Bit [7] → D-PHY clock lane(LNCK) dedicated clock enable
> -	 */
> -	val = BIT(lane_cfg->clk.pos);
>   	for (i = 0; i < cfg->num_data_lanes; i++)
> -		val |= BIT(lane_cfg->data[i].pos * 2);
> +		lane_mask |= BIT(lane_cfg->data[i].pos * 2 + offset);
> 
> -	writel(val, csi2phy->base +
> -	       CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 5));
> +	return lane_mask;
> +}
> +
> +static void
> +phy_qcom_mipi_csi2_dphy_lane_polarities(struct mipi_csi2phy_device *csi2phy,
> +					struct mipi_csi2phy_stream_cfg *cfg)
> +{
> +	struct mipi_csi2phy_lanes_cfg *lane_cfg = &cfg->lane_cfg;
> +	int i;
> 
>   	/* Lane configuration for polarity @ CSIPHY-base + CTRL9 */
>   	for (i = 0; i < cfg->num_data_lanes; i++) {
> @@ -308,6 +451,29 @@ static int phy_qcom_mipi_csi2_lanes_enable(struct mipi_csi2phy_device *csi2phy,
> 
>   	if (lane_cfg->clk.pol)
>   		writel(BIT(2), csi2phy->base + CSIPHY_2PH_LN_CSI_2PHASE_CTRL9n(lane_cfg->clk.pos));
> +}
> +
> +static int phy_qcom_mipi_csi2_lanes_enable(struct mipi_csi2phy_device *csi2phy,
> +					   struct mipi_csi2phy_stream_cfg *cfg)
> +{
> +	const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy);
> +	u8 settle_cnt;
> +	u8 val;
> +	int i;
> +
> +	if (cfg->link_freq <= 0)
> +		return -EINVAL;
> +
> +	settle_cnt = phy_qcom_mipi_csi2_settle_cnt_calc(cfg->link_freq, csi2phy->timer_clk_rate);
> +	if (!settle_cnt)
> +		return -ENODEV;
> +
> +	val = phy_qcom_mipi_csi2_lane_mask(csi2phy, cfg);
> +	writel(val, csi2phy->base +
> +	       CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 5));
> +
> +	if (csi2phy->phy_mode == PHY_MODE_MIPI_DPHY)
> +		phy_qcom_mipi_csi2_dphy_lane_polarities(csi2phy, cfg);
> 
>   	val = CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B;
>   	writel(val, csi2phy->base +
> @@ -374,6 +540,8 @@ const struct mipi_csi2phy_soc_cfg mipi_csi2_dphy_4nm_x1e = {
>   	.reg_info = {
>   		.dphy_init_seq = lane_regs_x1e80100,
>   		.dphy_lane_array_size = ARRAY_SIZE(lane_regs_x1e80100),
> +		.cphy_init_seq = lane_regs_v2_1_2_cphy,
> +		.cphy_lane_array_size = ARRAY_SIZE(lane_regs_v2_1_2_cphy),
>   		.common_regs_offset = 0x1000,
>   	},
>   	.supply_names = (const char **)x1e_supplies,
> diff --git a/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c
> index c0a4fa20e..1185561a1 100644
> --- a/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c
> +++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c
> @@ -96,6 +96,25 @@ phy_qcom_mipi_csi2_set_clock_rates(struct mipi_csi2phy_device *csi2phy,
>   	return ret;
>   }
> 
> +static int
> +phy_qcom_mipi_csi2_configure_cphy(struct mipi_csi2phy_device *csi2phy,
> +				  struct phy_configure_opts_mipi_cphy *cphy_cfg)
> +{
> +	struct mipi_csi2phy_stream_cfg *stream_cfg = &csi2phy->stream_cfg;
> +	int ret;
> +
> +	ret = phy_mipi_cphy_config_validate(cphy_cfg);
> +	if (ret)
> +		return ret;
> +
> +	if (cphy_cfg->trios != stream_cfg->num_data_lanes)
> +		return -EINVAL;
> +
> +	stream_cfg->link_freq = phy_mipi_cphy_link_freq(cphy_cfg);
> +
> +	return 0;
> +}
> +
>   static int phy_qcom_mipi_csi2_configure(struct phy *phy,
>   					union phy_configure_opts *opts)
>   {
> @@ -104,6 +123,9 @@ static int phy_qcom_mipi_csi2_configure(struct phy *phy,
>   	struct mipi_csi2phy_stream_cfg *stream_cfg = &csi2phy->stream_cfg;
>   	int ret;
> 
> +	if (csi2phy->phy_mode == PHY_MODE_MIPI_CPHY)
> +		return phy_qcom_mipi_csi2_configure_cphy(csi2phy, &opts->mipi_cphy);
> +
>   	ret = phy_mipi_dphy_config_validate(dphy_cfg);
>   	if (ret)
>   		return ret;
> @@ -124,10 +146,13 @@ static int phy_qcom_mipi_csi2_set_mode(struct phy *phy,
>   {
>   	struct mipi_csi2phy_device *csi2phy = phy_get_drvdata(phy);
> 
> -	if (mode != PHY_MODE_MIPI_DPHY)
> +	if (mode != PHY_MODE_MIPI_DPHY && mode != PHY_MODE_MIPI_CPHY)
>   		return -EOPNOTSUPP;
> 
> -	csi2phy->phy_mode = mode;
> +	/* The endpoint's bus-type sets the mode; only that one is accepted. */
> +	if (mode != csi2phy->phy_mode)
> +		return -EINVAL;
> +
>   	return 0;
>   }
> 
> @@ -258,6 +283,7 @@ static int phy_qcom_mipi_csi2_parse_routing(struct mipi_csi2phy_device *csi2phy)
>   	struct fwnode_handle *ep;
>   	int num_polarities;
>   	int num_data_lanes;
> +	int max_lanes;
>   	u32 bus_type;
>   	int i, ret;
> 
> @@ -278,14 +304,26 @@ static int phy_qcom_mipi_csi2_parse_routing(struct mipi_csi2phy_device *csi2phy)
>   	bus_type = MEDIA_BUS_TYPE_CSI2_DPHY;
>   	fwnode_property_read_u32(ep, "bus-type", &bus_type);
> 
> -	if (bus_type != MEDIA_BUS_TYPE_CSI2_DPHY) {
> +	switch (bus_type) {
> +	case MEDIA_BUS_TYPE_CSI2_DPHY:
> +		csi2phy->phy_mode = PHY_MODE_MIPI_DPHY;
> +		max_lanes = CSI2_MAX_DATA_LANES;
> +		break;
> +	case MEDIA_BUS_TYPE_CSI2_CPHY:
> +		if (csi2phy->soc_cfg->reg_info.cphy_init_seq) {
> +			csi2phy->phy_mode = PHY_MODE_MIPI_CPHY;
> +			max_lanes = PHY_MIPI_CPHY_MAX_TRIOS;
> +			break;
> +		}
> +		fallthrough;
> +	default:
>   		ret = -EOPNOTSUPP;
>   		dev_err(dev, "Unsupported bus-type %u\n", bus_type);
>   		goto out_put;
>   	}
> 
>   	num_data_lanes = fwnode_property_count_u32(ep, "data-lanes");
> -	if (num_data_lanes < 1 || num_data_lanes > CSI2_MAX_DATA_LANES) {
> +	if (num_data_lanes < 1 || num_data_lanes > max_lanes) {
>   		ret = -EINVAL;
>   		dev_err(dev, "Invalid data-lanes count: %d\n", num_data_lanes);
>   		goto out_put;
> @@ -302,6 +340,11 @@ static int phy_qcom_mipi_csi2_parse_routing(struct mipi_csi2phy_device *csi2phy)
>   	/* lane-polarities: optional, up to num_data_lanes + 1 entries */
>   	memset(lane_polarities, 0x00, sizeof(lane_polarities));
>   	num_polarities = fwnode_property_count_u32(ep, "lane-polarities");
> +	if (num_polarities > 0 && bus_type == MEDIA_BUS_TYPE_CSI2_CPHY) {
> +		ret = -EINVAL;
> +		dev_err(dev, "lane-polarities do not apply to C-PHY\n");
> +		goto out_put;
> +	}
>   	if (num_polarities > 0) {
>   		if (num_polarities != stream_cfg->num_data_lanes + 1) {
>   			ret = -EINVAL;
> @@ -322,7 +365,7 @@ static int phy_qcom_mipi_csi2_parse_routing(struct mipi_csi2phy_device *csi2phy)
>   	csi2phy->stream_cfg.lane_cfg.clk.pol = lane_polarities[0];
> 
>   	for (i = 0; i < csi2phy->stream_cfg.num_data_lanes; i++) {
> -		if (data_lanes[i] < 1 || data_lanes[i] > CSI2_MAX_DATA_LANES) {
> +		if (data_lanes[i] < 1 || data_lanes[i] > max_lanes) {
>   			dev_err(dev, "Invalid lane %d\n", data_lanes[i]);
>   			ret = -EINVAL;
>   			goto out_put;
> diff --git a/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h b/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h
> index 0d6031a4d..f49f334fd 100644
> --- a/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h
> +++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h
> @@ -50,6 +50,8 @@ struct mipi_csi2phy_lane_regs {
>   struct mipi_csi2phy_device_regs {
>   	const struct mipi_csi2phy_lane_regs *dphy_init_seq;
>   	const int dphy_lane_array_size;
> +	const struct mipi_csi2phy_lane_regs *cphy_init_seq;
> +	const int cphy_lane_array_size;
>   	const u32 common_regs_offset;
>   };
> 
> 
> --
> 2.53.0


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

  reply	other threads:[~2026-10-16 11:15 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <K9h_NQSPlrzzoVDIqqNPGT6KDZNScVi2mlqy6yNs8VOOS_I4KXf0QLy4k3Cwo2qKhnfL_qUK5wnY4h6wX24L4A==@protonmail.internalid>
2026-10-16 10:57 ` [PATCH RFC v2 0/4] phy: qcom-mipi-csi2: C-PHY for X1E80100 George Neacsu via B4 Relay
2026-10-16 10:57   ` [PATCH RFC v2 1/4] phy: Add MIPI C-PHY mode and configuration options George Neacsu via B4 Relay
2026-10-16 11:04     ` sashiko-bot
2026-10-16 11:35     ` Nihal Kumar Gupta
2026-10-16 14:16     ` Loic Poulain
2026-10-16 10:57   ` [PATCH RFC v2 2/4] phy: Add MIPI C-PHY configuration helpers George Neacsu via B4 Relay
2026-10-16 11:06     ` Bryan O'Donoghue
2026-10-16 10:57   ` [PATCH RFC v2 3/4] phy: qcom-mipi-csi2: Rename the D-PHY init sequence George Neacsu via B4 Relay
2026-10-16 11:07     ` Bryan O'Donoghue
2026-10-16 10:57   ` [PATCH RFC v2 4/4] phy: qcom-mipi-csi2: Add C-PHY support for X1E80100 George Neacsu via B4 Relay
2026-10-16 11:15     ` Bryan O'Donoghue [this message]
2026-10-16 11:35     ` Nihal Kumar Gupta
2026-10-16 12:05       ` Konrad Dybcio
2026-10-16 12:25         ` Nihal Kumar Gupta
2026-10-16 12:24     ` David Heidelberg
2026-10-16 11:02   ` [PATCH RFC v2 0/4] phy: qcom-mipi-csi2: C-PHY " Bryan O'Donoghue
2026-10-16 11:32     ` George Neacsu
2026-10-16 13:27       ` Bryan O'Donoghue
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=85393f60-d89a-4e31-b398-722e5414fe58@kernel.org \
    --to=bod@kernel.org \
    --cc=david@ixit.cz \
    --cc=geocausa@gmail.com \
    --cc=konrad.dybcio@oss.qualcomm.com \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=linux-phy@lists.infradead.org \
    --cc=loic.poulain@oss.qualcomm.com \
    --cc=mani@kernel.org \
    --cc=mchehab@kernel.org \
    --cc=neil.armstrong@linaro.org \
    --cc=nihal.gupta@oss.qualcomm.com \
    --cc=svankada@qti.qualcomm.com \
    --cc=tjose@qti.qualcomm.com \
    --cc=vikram.sharma@oss.qualcomm.com \
    --cc=vkoul@kernel.org \
    --cc=vladimir.zapolskiy@linaro.org \
    /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