Re: [RFC 2/3] media: i2c: ov5640: Rework CSI-2 clock tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Jacopo,

On 10/28/20 11:57 PM, Jacopo Mondi wrote:
> Re-work the ov5640_set_mipi_pclk() function to calculate the
> SYSCLK function based on the CSI-2 link frequency.
> 
> Take into account and more clearly document the clock tree constraints
> reported in the PLL diagrams. Most values are still fixed and only tested
> with 16bpp YUYV formats with a 2 lanes CSI-2 setup.
> 
> Tested by capturing and validating images in all the sensor supported
> resolutions except full resolution 2592x1944.
> 
> Signed-off-by: Jacopo Mondi <jacopo+renesas@xxxxxxxxxx>
> ---
>   drivers/media/i2c/ov5640.c | 125 ++++++++++++++++++++++---------------
>   1 file changed, 75 insertions(+), 50 deletions(-)
> 
> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> index 117ac22683ad..236c684ca20b 100644
> --- a/drivers/media/i2c/ov5640.c
> +++ b/drivers/media/i2c/ov5640.c
> @@ -88,6 +88,7 @@
>   #define OV5640_REG_POLARITY_CTRL00	0x4740
>   #define OV5640_REG_MIPI_CTRL00		0x4800
>   #define OV5640_REG_DEBUG_MODE		0x4814
> +#define OV5640_REG_PCLK_PERIOD		0x4837
>   #define OV5640_REG_ISP_FORMAT_MUX_CTRL	0x501f
>   #define OV5640_REG_PRE_ISP_TEST_SET1	0x503d
>   #define OV5640_REG_SDE_CTRL0		0x5580
> @@ -919,67 +920,88 @@ static unsigned long ov5640_calc_sys_clk(struct ov5640_dev *sensor,
>   /*
>    * ov5640_set_mipi_pclk() - Calculate the clock tree configuration values
>    *			    for the MIPI CSI-2 output.
> - *
> - * @rate: The requested bandwidth per lane in bytes per second.
> - *	  'Bandwidth Per Lane' is calculated as:
> - *	  bpl = HTOT * VTOT * FPS * bpp / num_lanes;
> - *
> - * This function use the requested bandwidth to calculate:
> - * - sample_rate = bpl / (bpp / num_lanes);
> - *	         = bpl / (PLL_RDIV * BIT_DIV * PCLK_DIV * MIPI_DIV / num_lanes);
> - *
> - * - mipi_sclk   = bpl / MIPI_DIV / 2; ( / 2 is for CSI-2 DDR)
> - *
> - * with these fixed parameters:
> - *	PLL_RDIV	= 2;
> - *	BIT_DIVIDER	= 2; (MIPI_BIT_MODE == 8 ? 2 : 2,5);
> - *	PCLK_DIV	= 1;
> - *
> - * The MIPI clock generation differs for modes that use the scaler and modes
> - * that do not. In case the scaler is in use, the MIPI_SCLK generates the MIPI
> - * BIT CLk, and thus:
> - *
> - * - mipi_sclk = bpl / MIPI_DIV / 2;
> - *   MIPI_DIV = 1;
> - *
> - * For modes that do not go through the scaler, the MIPI BIT CLOCK is generated
> - * from the pixel clock, and thus:
> - *
> - * - sample_rate = bpl / (bpp / num_lanes);
> - *	         = bpl / (2 * 2 * 1 * MIPI_DIV / num_lanes);
> - *		 = bpl / (4 * MIPI_DIV / num_lanes);
> - * - MIPI_DIV	 = bpp / (4 * num_lanes);
> + * @rate: The requested bitrate in bits per second.
>    *
>    * FIXME: this have been tested with 16bpp and 2 lanes setup only.
> - * MIPI_DIV is fixed to value 2, but it -might- be changed according to the
> - * above formula for setups with 1 lane or image formats with different bpp.
> - *
> - * FIXME: this deviates from the sensor manual documentation which is quite
> - * thin on the MIPI clock tree generation part.
>    */
>   static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor,
>   				unsigned long rate)
>   {
> -	const struct ov5640_mode_info *mode = sensor->current_mode;
> +	u8 bit_div, mipi_div, pclk_div, sclk_div, sclk2x_div, root_div;
>   	u8 prediv, mult, sysdiv;
> -	u8 mipi_div;
> +	unsigned long link_freq;
> +	unsigned long sysclk;
> +	u8 pclk_period;
>   	int ret;
> 
>   	/*
> -	 * 1280x720 is reported to use 'SUBSAMPLING' only,
> -	 * but according to the sensor manual it goes through the
> -	 * scaler before subsampling.
> +	 * The 'rate' parameter is the bitrate = VTOT * HTOT * FPS * BPP
> +	 *
> +	 * Adjust it to represent the CSI-2 link frequency and use it to
> +	 * update the associated control.
>   	 */
> -	if (mode->dn_mode == SCALING ||
> -	   (mode->id == OV5640_MODE_720P_1280_720))
> -		mipi_div = OV5640_MIPI_DIV_SCLK;
> +	link_freq = rate / sensor->ep.bus.mipi_csi2.num_data_lanes / 2;

framerate is broken, it is almost 2 times greater that expected. 
Checking code it seems that mipi_div is missing when computing link_freq:

-	link_freq = rate / sensor->ep.bus.mipi_csi2.num_data_lanes / 2;
+	link_freq = rate / sensor->ep.bus.mipi_csi2.num_data_lanes / 2 / mipi_div;

To test the setup I have patched the link frequency control to report 
dynamically the frequency instead of hardcoded value:
+#if 0
  	freq_index = OV5640_LINK_FREQS_NUM - 1;
  	for (i = 0; i < OV5640_LINK_FREQS_NUM; ++i) {
  		if (ov5640_link_freqs[i] == link_freq) {
@@ -966,18 +979,12 @@ static int ov5640_set_mipi_pclk(struct ov5640_dev 
*sensor,
  	ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.link_freq, freq_index);
  	if (ret < 0)
  		return ret;
+#else
+	ov5640_link_freqs[0] = link_freq;
+	ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.link_freq, 0);
+#endif

> +
> +	/*
> +	 * - mipi_div - Assumptions not supported by documentation
> +	 *
> +	 *   The MIPI clock generation differs for modes that use the scaler and
> +	 *   modes that do not.
> +	 */
> +	if (sensor->current_mode->dn_mode == SCALING)
> +		mipi_div = 1;
>   	else
> -		mipi_div = OV5640_MIPI_DIV_PCLK;
> +		mipi_div = 2;
> +
> +	sysclk = link_freq * 2 * mipi_div;
> +	ov5640_calc_sys_clk(sensor, sysclk, &prediv, &mult, &sysdiv);
> +
> +	/*
> +	 * Adjust PLL parameters to maintain the MIPI_SCLK-to-PCLK ratio;
> +	 *
> +	 * - root_div = 2 (fixed)
> +	 * - bit_div : MIPI 8-bit = 2
> +	 *	       MIPI 10-bit = 2,5
> +	 * - pclk_div = 1 (fixed)
> +	 * - pll_div  = (2 lanes ? mipi_div : 2 * mipi_div)
> +	 *   2 lanes: MIPI_SCLK = (4 or 5) * PCLK
> +	 *   1 lanes: MIPI_SCLK = (8 or 10) * PCLK
> +	 *
> +	 * TODO: support 10-bit formats
> +	 * TODO: support 1 lane
> +	 */
> +	root_div = OV5640_PLL_CTRL3_PLL_ROOT_DIV_2;
> +	bit_div =  OV5640_PLL_CTRL0_MIPI_MODE_8BIT;
> +	pclk_div = OV5640_PLL_SYS_ROOT_DIVIDER_BYPASS;
> 
> -	ov5640_calc_sys_clk(sensor, rate, &prediv, &mult, &sysdiv);
> +	/*
> +	 * Scaler clock:
> +	 * - YUV: PCLK >= 2 * SCLK
> +	 * - RAW or JPEG: PCLK >= SCLK
> +	 * - sclk2x_div = sclk_div / 2
> +	 *
> +	 * TODO: add support for RAW and JPEG modes. To maintain the
> +	 * SCLK to PCLK ratio, the sclk_div should probably be
> +	 * adjusted.
> +	 */
> +	sclk_div = ilog2(OV5640_SCLK_ROOT_DIV);
> +	sclk2x_div = ilog2(OV5640_SCLK2X_ROOT_DIV);
> +
> +	/*
> +	 * This is called pclk period, but it actually represents the
> +	 * sample period expressed in ns with 1-bit decimal (0x01=0.5ns).
> +	 *
> +	 * - pclk = link_freq * 2 * lanes / bpp
> +	 *
> +	 * TODO: support 1 data lane; support modes with bpp != 16.
> +	 */
> +	pclk_period = 2000000000UL / (link_freq / 2);
> 
> +	/* Program the clock tree registers. */
>   	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0,
> -			     0x0f, OV5640_PLL_CTRL0_MIPI_MODE_8BIT);
> +			     0x0f, bit_div);
> +	if (ret)
> +		return ret;
> 
>   	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
>   			     0xff, sysdiv << 4 | mipi_div);
> @@ -991,12 +1013,16 @@ static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor,
>   		return ret;
> 
>   	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3,
> -			     0x1f, OV5640_PLL_CTRL3_PLL_ROOT_DIV_2 | prediv);
> +			     0x1f, root_div | prediv);
> +	if (ret)
> +		return ret;
> +
> +	ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x3f,
> +			      (pclk_div << 4) | (sclk2x_div << 2) | sclk_div);
>   	if (ret)
>   		return ret;
> 
> -	return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER,
> -			      0x30, OV5640_PLL_SYS_ROOT_DIVIDER_BYPASS);
> +	return ov5640_write_reg(sensor, OV5640_REG_PCLK_PERIOD, pclk_period);
>   }
> 
>   static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor,
> @@ -1775,7 +1801,6 @@ static int ov5640_set_mode(struct ov5640_dev *sensor)
>   	 */
>   	rate = ov5640_calc_pixel_rate(sensor) * 16;
>   	if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY) {
> -		rate = rate / sensor->ep.bus.mipi_csi2.num_data_lanes;
>   		ret = ov5640_set_mipi_pclk(sensor, rate);
>   	} else {
>   		rate = rate / sensor->ep.bus.parallel.bus_width;
> --
> 2.28.0
> 

BR,
Hugues.




[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux