mbox series

[v2,0/3] media: rcar-csi2: Add support for V4H

Message ID 20230424185934.438059-1-niklas.soderlund+renesas@ragnatech.se
Headers show
Series media: rcar-csi2: Add support for V4H | expand

Message

Niklas Söderlund April 24, 2023, 6:59 p.m. UTC
Hello,

This series adds support for Renesas R-Car V4H.

Patch 1/3 prepares the driver for R-Car Gen4 by creating function
pointers for differences with Gen3. Patch 2/3 prepares for parsing CSI-2
C-PHY properties. Finally patch 3/3 adds V4H support.

Tested on both Gen3 and Gen4 hardware without any regressions. Tested
using both regular capture and v4l-compliance.

Niklas Söderlund (3):
  media: rcar-csi2: Prepare for Gen4 support
  media: rcar-csi2: Prepare for C-PHY support
  media: rcar-csi2: Add support for C-PHY on R-Car V4H

 .../platform/renesas/rcar-vin/rcar-csi2.c     | 391 +++++++++++++++++-
 1 file changed, 376 insertions(+), 15 deletions(-)

Comments

Niklas Söderlund May 16, 2023, 2:27 p.m. UTC | #1
Hej Sakari.

On 2023-05-16 16:12:59 +0300, Sakari Ailus wrote:
> Hejssan!
> 
> On Mon, Apr 24, 2023 at 08:59:34PM +0200, Niklas Söderlund wrote:
> > @@ -645,6 +755,10 @@ static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp,
> >  	mbps = v4l2_ctrl_g_ctrl_int64(ctrl) * bpp;
> >  	do_div(mbps, lanes * 1000000);
> >  
> > +	/* Adjust for C-PHY */
> > +	if (priv->cphy)
> > +		do_div(mbps, 2.8);
> 
> This will divide by 2. I suppose that's not intentional??

Indeed, this looks wrong. I will recheck my work and post a v2.

> 
> > +
> >  	return mbps;
> >  }
> >  
> > @@ -833,6 +947,170 @@ static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv)
> >  	return 0;
> >  }
> >  
> > +static int rcsi2_wait_phy_start_v4h(struct rcar_csi2 *priv, u32 match)
> > +{
> > +	unsigned int timeout;
> > +	u32 status;
> > +
> > +	for (timeout = 0; timeout <= 10; timeout++) {
> > +		status = rcsi2_read(priv, V4H_ST_PHYST_REG);
> > +		if ((status & match) == match)
> > +			return 0;
> > +
> > +		usleep_range(1000, 2000);
> > +	}
> > +
> > +	return -ETIMEDOUT;
> > +}
> > +
> > +static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps)
> > +{
> > +	const struct rcsi2_cphy_setting *conf;
> > +
> > +	for (conf = cphy_setting_table_r8a779g0; conf->msps != 0; conf++) {
> > +		if (conf->msps > msps)
> > +			break;
> > +	}
> > +
> > +	if (!conf->msps) {
> > +		dev_err(priv->dev, "Unsupported PHY speed for msps setting (%u Msps)", msps);
> > +		return -ERANGE;
> > +	}
> > +
> > +	/* C-PHY specific */
> > +	rcsi2_write16(priv, V4H_CORE_DIG_RW_COMMON_REG(7), 0x0155);
> > +	rcsi2_write16(priv, V4H_PPI_STARTUP_RW_COMMON_DPHY_REG(7), 0x0068);
> > +	rcsi2_write16(priv, V4H_PPI_STARTUP_RW_COMMON_DPHY_REG(8), 0x0010);
> > +
> > +	rcsi2_write16(priv, V4H_CORE_DIG_CLANE_0_RW_LP_0_REG, 0x463c);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_LP_0_REG, 0x463c);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_CLANE_2_RW_LP_0_REG, 0x463c);
> > +
> > +	rcsi2_write16(priv, V4H_CORE_DIG_CLANE_0_RW_HS_RX_REG(0), 0x00d5);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_HS_RX_REG(0), 0x00d5);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_CLANE_2_RW_HS_RX_REG(0), 0x00d5);
> > +
> > +	rcsi2_write16(priv, V4H_CORE_DIG_CLANE_0_RW_HS_RX_REG(1), 0x0013);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_HS_RX_REG(1), 0x0013);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_CLANE_2_RW_HS_RX_REG(1), 0x0013);
> > +
> > +	rcsi2_write16(priv, V4H_CORE_DIG_CLANE_0_RW_HS_RX_REG(5), 0x0013);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_HS_RX_REG(5), 0x0013);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_CLANE_2_RW_HS_RX_REG(5), 0x0013);
> > +
> > +	rcsi2_write16(priv, V4H_CORE_DIG_CLANE_0_RW_HS_RX_REG(6), 0x000a);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_HS_RX_REG(6), 0x000a);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_CLANE_2_RW_HS_RX_REG(6), 0x000a);
> > +
> > +	rcsi2_write16(priv, V4H_CORE_DIG_CLANE_0_RW_HS_RX_REG(2), conf->rx2);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_HS_RX_REG(2), conf->rx2);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_CLANE_2_RW_HS_RX_REG(2), conf->rx2);
> > +
> > +	rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(2), 0x0001);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_REG(2), 0);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_REG(2), 0x0001);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_REG(2), 0x0001);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_REG(2), 0);
> > +
> > +	rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO0_REG(0), conf->trio0);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO1_REG(0), conf->trio0);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO2_REG(0), conf->trio0);
> > +
> > +	rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO0_REG(2), conf->trio2);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO1_REG(2), conf->trio2);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO2_REG(2), conf->trio2);
> > +
> > +	rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO0_REG(1), conf->trio1);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO1_REG(1), conf->trio1);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO2_REG(1), conf->trio1);
> > +
> > +	/*
> > +	 * Configure pin-swap.
> > +	 * TODO: This registers is not documented yet, the values should depend
> > +	 * on the 'clock-lanes' and 'data-lanes' devicetree properties.
> > +	 */
> > +	rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG, 0xf5);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_HS_TX_6_REG, 0x5000);
> > +
> > +	/* Leave Shutdown mode */
> > +	rcsi2_write(priv, V4H_DPHY_RSTZ_REG, BIT(0));
> > +	rcsi2_write(priv, V4H_PHY_SHUTDOWNZ_REG, BIT(0));
> > +
> > +	/* Wait for calibration */
> > +	if (rcsi2_wait_phy_start_v4h(priv, V4H_ST_PHYST_ST_PHY_READY)) {
> > +		dev_err(priv->dev, "PHY calibration failed\n");
> > +		return -ETIMEDOUT;
> > +	}
> > +
> > +	/* C-PHY setting - analog programing*/
> > +	rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(9), conf->lane29);
> > +	rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(7), conf->lane27);
> > +
> > +	return 0;
> > +}
> > +
> > +static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv)
> > +{
> > +	const struct rcar_csi2_format *format;
> > +	unsigned int msps, lanes;
> > +	int ret;
> > +
> > +	/* Calculate parameters */
> > +	format = rcsi2_code_to_fmt(priv->mf.code);
> 
> Can this not be NULL? I guess not?

Indeed a NULL check should be added here, will fix in a v2. Thanks for 
spotting this.

> 
> > +
> > +	ret = rcsi2_get_active_lanes(priv, &lanes);
> > +	if (ret)
> > +		return ret;
> > +
> > +	msps = rcsi2_calc_mbps(priv, format->bpp, lanes);
> > +	if (msps < 0)
> > +		return msps;
> > +
> > +	/* Reset LINK and PHY*/
> > +	rcsi2_write(priv, V4H_CSI2_RESETN_REG, 0);
> > +	rcsi2_write(priv, V4H_DPHY_RSTZ_REG, 0);
> > +	rcsi2_write(priv, V4H_PHY_SHUTDOWNZ_REG, 0);
> 
> -- 
> Hälsningar,
> 
> Sakari Ailus