All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 00/12] media: ov5640: Misc cleanup and improvements
@ 2018-10-11  9:20 Maxime Ripard
  2018-10-11  9:20 ` [PATCH v4 01/12] media: ov5640: Adjust the clock based on the expected rate Maxime Ripard
                   ` (12 more replies)
  0 siblings, 13 replies; 35+ messages in thread
From: Maxime Ripard @ 2018-10-11  9:20 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Laurent Pinchart, linux-media, Thomas Petazzoni,
	Mylene Josserand, Hans Verkuil, Sakari Ailus, Hugues Fruchet,
	Loic Poulain, Samuel Bobrowicz, Steve Longerbeam, Daniel Mack,
	Jacopo Mondi, Maxime Ripard

Hi,

Here is a "small" series that mostly cleans up the ov5640 driver code,
slowly getting rid of the big data array for more understandable code
(hopefully).

The biggest addition would be the clock rate computation at runtime,
instead of relying on those arrays to setup the clock tree
properly. As a side effect, it fixes the framerate that was off by
around 10% on the smaller resolutions, and we now support 60fps.

This also introduces a bunch of new features.

Let me know what you think,
Maxime

Changes from v3:
  - Rebased on current Sakari tree
  - Fixed an error when changing only the framerate

Changes from v2:
  - Rebased on latest Sakari PR
  - Fixed the issues reported by Hugues: improper FPS returned for
    formats, improper rounding of the FPS, some with his suggestions,
    some by simplifying the logic.
  - Expanded the clock tree comments based on the feedback from Samuel
    Bobrowicz and Loic Poulain
  - Merged some of the changes made by Samuel Bobrowicz to fix the
    MIPI rate computation, fix the call sites of the
    ov5640_set_timings function, the auto-exposure calculation call,
    etc.
  - Split the patches into smaller ones in order to make it more
    readable (hopefully)

Changes from v1:
  - Integrated Hugues' suggestions to fix v4l2-compliance
  - Fixed the bus width with JPEG
  - Dropped the clock rate calculation loops for something simpler as
    suggested by Sakari
  - Cache the exposure value instead of using the control value
  - Rebased on top of 4.17

Maxime Ripard (12):
  media: ov5640: Adjust the clock based on the expected rate
  media: ov5640: Remove the clocks registers initialization
  media: ov5640: Remove redundant defines
  media: ov5640: Remove redundant register setup
  media: ov5640: Compute the clock rate at runtime
  media: ov5640: Remove pixel clock rates
  media: ov5640: Enhance FPS handling
  media: ov5640: Make the return rate type more explicit
  media: ov5640: Make the FPS clamping / rounding more extendable
  media: ov5640: Add 60 fps support
  media: ov5640: Remove duplicate auto-exposure setup
  ov5640: Enforce a mode change when changing the framerate

 drivers/media/i2c/ov5640.c | 679 ++++++++++++++++++++-----------------
 1 file changed, 374 insertions(+), 305 deletions(-)

-- 
2.19.1

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v4 01/12] media: ov5640: Adjust the clock based on the expected rate
  2018-10-11  9:20 [PATCH v4 00/12] media: ov5640: Misc cleanup and improvements Maxime Ripard
@ 2018-10-11  9:20 ` Maxime Ripard
  2018-10-15 14:05   ` Hugues FRUCHET
  2018-10-16 16:54   ` jacopo mondi
  2018-10-11  9:20 ` [PATCH v4 02/12] media: ov5640: Remove the clocks registers initialization Maxime Ripard
                   ` (11 subsequent siblings)
  12 siblings, 2 replies; 35+ messages in thread
From: Maxime Ripard @ 2018-10-11  9:20 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Laurent Pinchart, linux-media, Thomas Petazzoni,
	Mylene Josserand, Hans Verkuil, Sakari Ailus, Hugues Fruchet,
	Loic Poulain, Samuel Bobrowicz, Steve Longerbeam, Daniel Mack,
	Jacopo Mondi, Maxime Ripard

The clock structure for the PCLK is quite obscure in the documentation, and
was hardcoded through the bytes array of each and every mode.

This is troublesome, since we cannot adjust it at runtime based on other
parameters (such as the number of bytes per pixel), and we can't support
either framerates that have not been used by the various vendors, since we
don't have the needed initialization sequence.

We can however understand how the clock tree works, and then implement some
functions to derive the various parameters from a given rate. And now that
those parameters are calculated at runtime, we can remove them from the
initialization sequence.

The modes also gained a new parameter which is the clock that they are
running at, from the register writes they were doing, so for now the switch
to the new algorithm should be transparent.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/media/i2c/ov5640.c | 289 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 288 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 30b15e91d8be..88fb16341466 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -175,6 +175,7 @@ struct ov5640_mode_info {
 	u32 htot;
 	u32 vact;
 	u32 vtot;
+	u32 pixel_clock;
 	const struct reg_value *reg_data;
 	u32 reg_data_size;
 };
@@ -700,6 +701,7 @@ static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = {
 /* power-on sensor init reg table */
 static const struct ov5640_mode_info ov5640_mode_init_data = {
 	0, SUBSAMPLING, 640, 1896, 480, 984,
+	56000000,
 	ov5640_init_setting_30fps_VGA,
 	ARRAY_SIZE(ov5640_init_setting_30fps_VGA),
 };
@@ -709,74 +711,91 @@ ov5640_mode_data[OV5640_NUM_FRAMERATES][OV5640_NUM_MODES] = {
 	{
 		{OV5640_MODE_QCIF_176_144, SUBSAMPLING,
 		 176, 1896, 144, 984,
+		 28000000,
 		 ov5640_setting_15fps_QCIF_176_144,
 		 ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)},
 		{OV5640_MODE_QVGA_320_240, SUBSAMPLING,
 		 320, 1896, 240, 984,
+		 28000000,
 		 ov5640_setting_15fps_QVGA_320_240,
 		 ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)},
 		{OV5640_MODE_VGA_640_480, SUBSAMPLING,
 		 640, 1896, 480, 1080,
+		 28000000,
 		 ov5640_setting_15fps_VGA_640_480,
 		 ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)},
 		{OV5640_MODE_NTSC_720_480, SUBSAMPLING,
 		 720, 1896, 480, 984,
+		 28000000,
 		 ov5640_setting_15fps_NTSC_720_480,
 		 ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)},
 		{OV5640_MODE_PAL_720_576, SUBSAMPLING,
 		 720, 1896, 576, 984,
+		 28000000,
 		 ov5640_setting_15fps_PAL_720_576,
 		 ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)},
 		{OV5640_MODE_XGA_1024_768, SUBSAMPLING,
 		 1024, 1896, 768, 1080,
+		 28000000,
 		 ov5640_setting_15fps_XGA_1024_768,
 		 ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)},
 		{OV5640_MODE_720P_1280_720, SUBSAMPLING,
 		 1280, 1892, 720, 740,
+		 21000000,
 		 ov5640_setting_15fps_720P_1280_720,
 		 ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)},
 		{OV5640_MODE_1080P_1920_1080, SCALING,
 		 1920, 2500, 1080, 1120,
+		 42000000,
 		 ov5640_setting_15fps_1080P_1920_1080,
 		 ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)},
 		{OV5640_MODE_QSXGA_2592_1944, SCALING,
 		 2592, 2844, 1944, 1968,
+		 84000000,
 		 ov5640_setting_15fps_QSXGA_2592_1944,
 		 ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)},
 	}, {
 		{OV5640_MODE_QCIF_176_144, SUBSAMPLING,
 		 176, 1896, 144, 984,
+		 56000000,
 		 ov5640_setting_30fps_QCIF_176_144,
 		 ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)},
 		{OV5640_MODE_QVGA_320_240, SUBSAMPLING,
 		 320, 1896, 240, 984,
+		 56000000,
 		 ov5640_setting_30fps_QVGA_320_240,
 		 ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)},
 		{OV5640_MODE_VGA_640_480, SUBSAMPLING,
 		 640, 1896, 480, 1080,
+		 56000000,
 		 ov5640_setting_30fps_VGA_640_480,
 		 ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)},
 		{OV5640_MODE_NTSC_720_480, SUBSAMPLING,
 		 720, 1896, 480, 984,
+		 56000000,
 		 ov5640_setting_30fps_NTSC_720_480,
 		 ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)},
 		{OV5640_MODE_PAL_720_576, SUBSAMPLING,
 		 720, 1896, 576, 984,
+		 56000000,
 		 ov5640_setting_30fps_PAL_720_576,
 		 ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)},
 		{OV5640_MODE_XGA_1024_768, SUBSAMPLING,
 		 1024, 1896, 768, 1080,
+		 56000000,
 		 ov5640_setting_30fps_XGA_1024_768,
 		 ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)},
 		{OV5640_MODE_720P_1280_720, SUBSAMPLING,
 		 1280, 1892, 720, 740,
+		 42000000,
 		 ov5640_setting_30fps_720P_1280_720,
 		 ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)},
 		{OV5640_MODE_1080P_1920_1080, SCALING,
 		 1920, 2500, 1080, 1120,
+		 84000000,
 		 ov5640_setting_30fps_1080P_1920_1080,
 		 ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)},
-		{OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, 0, 0, NULL, 0},
+		{OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, 0, 0, 0, NULL, 0},
 	},
 };
 
@@ -909,6 +928,255 @@ static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg,
 	return ov5640_write_reg(sensor, reg, val);
 }
 
+/*
+ * After trying the various combinations, reading various
+ * documentations spreaded around the net, and from the various
+ * feedback, the clock tree is probably as follows:
+ *
+ *   +--------------+
+ *   |  Ext. Clock  |
+ *   +-+------------+
+ *     |  +----------+
+ *     +->|   PLL1   | - reg 0x3036, for the multiplier
+ *        +-+--------+ - reg 0x3037, bits 0-3 for the pre-divider
+ *          |  +--------------+
+ *          +->| System Clock |  - reg 0x3035, bits 4-7
+ *             +-+------------+
+ *               |  +--------------+
+ *               +->| MIPI Divider | - reg 0x3035, bits 0-3
+ *               |  +-+------------+
+ *               |    +----------------> MIPI SCLK
+ *               |  +--------------+
+ *               +->| PLL Root Div | - reg 0x3037, bit 4
+ *                  +-+------------+
+ *                    |  +---------+
+ *                    +->| Bit Div | - reg 0x3035, bits 0-3
+ *                       +-+-------+
+ *                         |  +-------------+
+ *                         +->| SCLK Div    | - reg 0x3108, bits 0-1
+ *                         |  +-+-----------+
+ *                         |    +---------------> SCLK
+ *                         |  +-------------+
+ *                         +->| SCLK 2X Div | - reg 0x3108, bits 2-3
+ *                         |  +-+-----------+
+ *                         |    +---------------> SCLK 2X
+ *                         |  +-------------+
+ *                         +->| PCLK Div    | - reg 0x3108, bits 4-5
+ *                            +-+-----------+
+ *                              +---------------> PCLK
+ *
+ * This is deviating from the datasheet at least for the register
+ * 0x3108, since it's said here that the PCLK would be clocked from
+ * the PLL.
+ *
+ * There seems to be also (unverified) constraints:
+ *  - the PLL pre-divider output rate should be in the 4-27MHz range
+ *  - the PLL multiplier output rate should be in the 500-1000MHz range
+ *  - PCLK >= SCLK * 2 in YUV, >= SCLK in Raw or JPEG
+ *  - MIPI SCLK = (bpp / lanes) / PCLK
+ *
+ * In the two latter cases, these constraints are met since our
+ * factors are hardcoded. If we were to change that, we would need to
+ * take this into account. The only varying parts are the PLL
+ * multiplier and the system clock divider, which are shared between
+ * all these clocks so won't cause any issue.
+ */
+
+/*
+ * This is supposed to be ranging from 1 to 8, but the value is always
+ * set to 3 in the vendor kernels.
+ */
+#define OV5640_PLL_PREDIV	3
+
+#define OV5640_PLL_MULT_MIN	4
+#define OV5640_PLL_MULT_MAX	252
+
+/*
+ * This is supposed to be ranging from 1 to 16, but the value is
+ * always set to either 1 or 2 in the vendor kernels.
+ */
+#define OV5640_SYSDIV_MIN	1
+#define OV5640_SYSDIV_MAX	2
+
+/*
+ * This is supposed to be ranging from 1 to 16, but the value is always
+ * set to 2 in the vendor kernels.
+ */
+#define OV5640_MIPI_DIV		2
+
+/*
+ * This is supposed to be ranging from 1 to 2, but the value is always
+ * set to 2 in the vendor kernels.
+ */
+#define OV5640_PLL_ROOT_DIV	2
+
+/*
+ * This is supposed to be either 1, 2 or 2.5, but the value is always
+ * set to 2 in the vendor kernels.
+ */
+#define OV5640_BIT_DIV		2
+
+/*
+ * This is supposed to be ranging from 1 to 8, but the value is always
+ * set to 2 in the vendor kernels.
+ */
+#define OV5640_SCLK_ROOT_DIV	2
+
+/*
+ * This is hardcoded so that the consistency is maintained between SCLK and
+ * SCLK 2x.
+ */
+#define OV5640_SCLK2X_ROOT_DIV (OV5640_SCLK_ROOT_DIV / 2)
+
+/*
+ * This is supposed to be ranging from 1 to 8, but the value is always
+ * set to 1 in the vendor kernels.
+ */
+#define OV5640_PCLK_ROOT_DIV	1
+
+static unsigned long ov5640_compute_sys_clk(struct ov5640_dev *sensor,
+					    u8 pll_prediv, u8 pll_mult,
+					    u8 sysdiv)
+{
+	unsigned long rate = clk_get_rate(sensor->xclk);
+
+	return rate / pll_prediv * pll_mult / sysdiv;
+}
+
+static unsigned long ov5640_calc_sys_clk(struct ov5640_dev *sensor,
+					 unsigned long rate,
+					 u8 *pll_prediv, u8 *pll_mult,
+					 u8 *sysdiv)
+{
+	unsigned long best = ~0;
+	u8 best_sysdiv = 1, best_mult = 1;
+	u8 _sysdiv, _pll_mult;
+
+	for (_sysdiv = OV5640_SYSDIV_MIN;
+	     _sysdiv <= OV5640_SYSDIV_MAX;
+	     _sysdiv++) {
+		for (_pll_mult = OV5640_PLL_MULT_MIN;
+		     _pll_mult <= OV5640_PLL_MULT_MAX;
+		     _pll_mult++) {
+			unsigned long _rate;
+
+			/*
+			 * The PLL multiplier cannot be odd if above
+			 * 127.
+			 */
+			if (_pll_mult > 127 && (_pll_mult % 2))
+				continue;
+
+			_rate = ov5640_compute_sys_clk(sensor,
+						       OV5640_PLL_PREDIV,
+						       _pll_mult, _sysdiv);
+			if (abs(rate - _rate) < abs(rate - best)) {
+				best = _rate;
+				best_sysdiv = _sysdiv;
+				best_mult = _pll_mult;
+			}
+
+			if (_rate == rate)
+				goto out;
+		}
+	}
+
+out:
+	*sysdiv = best_sysdiv;
+	*pll_prediv = OV5640_PLL_PREDIV;
+	*pll_mult = best_mult;
+	return best;
+}
+
+static unsigned long ov5640_calc_mipi_clk(struct ov5640_dev *sensor,
+					  unsigned long rate,
+					  u8 *pll_prediv, u8 *pll_mult,
+					  u8 *sysdiv, u8 *mipi_div)
+{
+	unsigned long _rate = rate * OV5640_MIPI_DIV;
+
+	_rate = ov5640_calc_sys_clk(sensor, _rate, pll_prediv, pll_mult,
+				    sysdiv);
+	*mipi_div = OV5640_MIPI_DIV;
+
+	return _rate / *mipi_div;
+}
+
+static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor, unsigned long rate)
+{
+	u8 prediv, mult, sysdiv, mipi_div;
+	int ret;
+
+	ov5640_calc_mipi_clk(sensor, rate, &prediv, &mult, &sysdiv, &mipi_div);
+
+	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
+			     0xff, sysdiv << 4 | (mipi_div - 1));
+	if (ret)
+		return ret;
+
+	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2, 0xff, mult);
+	if (ret)
+		return ret;
+
+	return ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3, 0xf, prediv);
+}
+
+static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor,
+				      unsigned long rate,
+				      u8 *pll_prediv, u8 *pll_mult, u8 *sysdiv,
+				      u8 *pll_rdiv, u8 *bit_div, u8 *pclk_div)
+{
+	unsigned long _rate = rate * OV5640_PLL_ROOT_DIV * OV5640_BIT_DIV *
+				OV5640_PCLK_ROOT_DIV;
+
+	_rate = ov5640_calc_sys_clk(sensor, _rate, pll_prediv, pll_mult,
+				    sysdiv);
+	*pll_rdiv = OV5640_PLL_ROOT_DIV;
+	*bit_div = OV5640_BIT_DIV;
+	*pclk_div = OV5640_PCLK_ROOT_DIV;
+
+	return _rate / *pll_rdiv / *bit_div / *pclk_div;
+}
+
+static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate)
+{
+	u8 prediv, mult, sysdiv, pll_rdiv, bit_div, pclk_div;
+	int ret;
+
+	ov5640_calc_pclk(sensor, rate, &prediv, &mult, &sysdiv, &pll_rdiv,
+			 &bit_div, &pclk_div);
+
+	if (bit_div == 2)
+		bit_div = 8;
+
+	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0,
+			     0x0f, bit_div);
+	if (ret)
+		return ret;
+
+	/*
+	 * We need to set sysdiv according to the clock, and to clear
+	 * the MIPI divider.
+	 */
+	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
+			     0xff, sysdiv << 4);
+	if (ret)
+		return ret;
+
+	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2,
+			     0xff, mult);
+	if (ret)
+		return ret;
+
+	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3,
+			     0x1f, prediv | ((pll_rdiv - 1) << 4));
+	if (ret)
+		return ret;
+
+	return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x30,
+			      (ilog2(pclk_div) << 4));
+}
+
 /* download ov5640 settings to sensor through i2c */
 static int ov5640_set_timings(struct ov5640_dev *sensor,
 			      const struct ov5640_mode_info *mode)
@@ -1637,6 +1905,8 @@ static int ov5640_set_mode(struct ov5640_dev *sensor)
 	enum ov5640_downsize_mode dn_mode, orig_dn_mode;
 	bool auto_gain = sensor->ctrls.auto_gain->val == 1;
 	bool auto_exp =  sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
+	unsigned long rate;
+	unsigned char bpp;
 	int ret;
 
 	dn_mode = mode->dn_mode;
@@ -1655,6 +1925,23 @@ static int ov5640_set_mode(struct ov5640_dev *sensor)
 			goto restore_auto_gain;
 	}
 
+	/*
+	 * All the formats we support have 16 bits per pixel, except for JPEG
+	 * which is 8 bits per pixel.
+	 */
+	bpp = sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8 ? 8 : 16;
+	rate = mode->pixel_clock * bpp;
+	if (sensor->ep.bus_type == V4L2_MBUS_CSI2) {
+		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;
+		ret = ov5640_set_dvp_pclk(sensor, rate);
+	}
+
+	if (ret < 0)
+		return 0;
+
 	if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
 	    (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
 		/*
-- 
2.19.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 02/12] media: ov5640: Remove the clocks registers initialization
  2018-10-11  9:20 [PATCH v4 00/12] media: ov5640: Misc cleanup and improvements Maxime Ripard
  2018-10-11  9:20 ` [PATCH v4 01/12] media: ov5640: Adjust the clock based on the expected rate Maxime Ripard
@ 2018-10-11  9:20 ` Maxime Ripard
  2018-10-11  9:20 ` [PATCH v4 03/12] media: ov5640: Remove redundant defines Maxime Ripard
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 35+ messages in thread
From: Maxime Ripard @ 2018-10-11  9:20 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Laurent Pinchart, linux-media, Thomas Petazzoni,
	Mylene Josserand, Hans Verkuil, Sakari Ailus, Hugues Fruchet,
	Loic Poulain, Samuel Bobrowicz, Steve Longerbeam, Daniel Mack,
	Jacopo Mondi, Maxime Ripard

Part of the hardcoded initialization sequence is to set up the proper clock
dividers. However, this is now done dynamically through proper code and as
such, the static one is now redundant.

Let's remove it.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/media/i2c/ov5640.c | 46 ++++++++++++++++++--------------------
 1 file changed, 22 insertions(+), 24 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 88fb16341466..e8544cd8298f 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -262,8 +262,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
 static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
 	{0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0},
 	{0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0},
-	{0x3034, 0x18, 0, 0}, {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0},
-	{0x3037, 0x13, 0, 0}, {0x3630, 0x36, 0, 0},
+	{0x3630, 0x36, 0, 0},
 	{0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0},
 	{0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0},
 	{0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0},
@@ -346,7 +345,7 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
 };
 
 static const struct reg_value ov5640_setting_30fps_VGA_640_480[] = {
-	{0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
 	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
@@ -365,7 +364,7 @@ static const struct reg_value ov5640_setting_30fps_VGA_640_480[] = {
 };
 
 static const struct reg_value ov5640_setting_15fps_VGA_640_480[] = {
-	{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
 	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
@@ -384,7 +383,7 @@ static const struct reg_value ov5640_setting_15fps_VGA_640_480[] = {
 };
 
 static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = {
-	{0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
 	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
@@ -400,11 +399,10 @@ static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = {
 	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
 	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
 	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0},
-	{0x3035, 0x12, 0, 0},
 };
 
 static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = {
-	{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
 	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
@@ -423,7 +421,7 @@ static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = {
 };
 
 static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = {
-	{0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
 	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
@@ -442,7 +440,7 @@ static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = {
 };
 
 static const struct reg_value ov5640_setting_15fps_QVGA_320_240[] = {
-	{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
 	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
@@ -461,7 +459,7 @@ static const struct reg_value ov5640_setting_15fps_QVGA_320_240[] = {
 };
 
 static const struct reg_value ov5640_setting_30fps_QCIF_176_144[] = {
-	{0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
 	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
@@ -480,7 +478,7 @@ static const struct reg_value ov5640_setting_30fps_QCIF_176_144[] = {
 };
 
 static const struct reg_value ov5640_setting_15fps_QCIF_176_144[] = {
-	{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
 	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
@@ -499,7 +497,7 @@ static const struct reg_value ov5640_setting_15fps_QCIF_176_144[] = {
 };
 
 static const struct reg_value ov5640_setting_30fps_NTSC_720_480[] = {
-	{0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
 	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
@@ -518,7 +516,7 @@ static const struct reg_value ov5640_setting_30fps_NTSC_720_480[] = {
 };
 
 static const struct reg_value ov5640_setting_15fps_NTSC_720_480[] = {
-	{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
 	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
@@ -537,7 +535,7 @@ static const struct reg_value ov5640_setting_15fps_NTSC_720_480[] = {
 };
 
 static const struct reg_value ov5640_setting_30fps_PAL_720_576[] = {
-	{0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
 	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
@@ -556,7 +554,7 @@ static const struct reg_value ov5640_setting_30fps_PAL_720_576[] = {
 };
 
 static const struct reg_value ov5640_setting_15fps_PAL_720_576[] = {
-	{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
 	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
@@ -576,7 +574,7 @@ static const struct reg_value ov5640_setting_15fps_PAL_720_576[] = {
 
 static const struct reg_value ov5640_setting_30fps_720P_1280_720[] = {
 	{0x3008, 0x42, 0, 0},
-	{0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0},
+	{0x3c07, 0x07, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
 	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
@@ -596,7 +594,7 @@ static const struct reg_value ov5640_setting_30fps_720P_1280_720[] = {
 };
 
 static const struct reg_value ov5640_setting_15fps_720P_1280_720[] = {
-	{0x3035, 0x41, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0},
+	{0x3c07, 0x07, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
 	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
@@ -616,7 +614,7 @@ static const struct reg_value ov5640_setting_15fps_720P_1280_720[] = {
 
 static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = {
 	{0x3008, 0x42, 0, 0},
-	{0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x11, 0, 0},
 	{0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
@@ -631,8 +629,8 @@ static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = {
 	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
 	{0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
 	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
-	{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x11, 0, 0},
-	{0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0},
+	{0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
 	{0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0},
@@ -649,7 +647,7 @@ static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = {
 
 static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = {
 	{0x3008, 0x42, 0, 0},
-	{0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x11, 0, 0},
 	{0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
@@ -664,8 +662,8 @@ static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = {
 	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
 	{0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
 	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
-	{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x21, 0, 0},
-	{0x3036, 0x54, 0, 1}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0},
+	{0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
 	{0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0},
@@ -680,7 +678,7 @@ static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = {
 };
 
 static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = {
-	{0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x11, 0, 0},
 	{0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
-- 
2.19.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 03/12] media: ov5640: Remove redundant defines
  2018-10-11  9:20 [PATCH v4 00/12] media: ov5640: Misc cleanup and improvements Maxime Ripard
  2018-10-11  9:20 ` [PATCH v4 01/12] media: ov5640: Adjust the clock based on the expected rate Maxime Ripard
  2018-10-11  9:20 ` [PATCH v4 02/12] media: ov5640: Remove the clocks registers initialization Maxime Ripard
@ 2018-10-11  9:20 ` Maxime Ripard
  2018-10-11  9:20 ` [PATCH v4 04/12] media: ov5640: Remove redundant register setup Maxime Ripard
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 35+ messages in thread
From: Maxime Ripard @ 2018-10-11  9:20 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Laurent Pinchart, linux-media, Thomas Petazzoni,
	Mylene Josserand, Hans Verkuil, Sakari Ailus, Hugues Fruchet,
	Loic Poulain, Samuel Bobrowicz, Steve Longerbeam, Daniel Mack,
	Jacopo Mondi, Maxime Ripard

The OV5640_SCLK2X_ROOT_DIVIDER_DEFAULT and OV5640_SCLK_ROOT_DIVIDER_DEFAULT
defines represent exactly the same setup, and are at the same value, than
the more consistent with the rest of the driver OV5640_SCLK2X_ROOT_DIV and
OV5640_SCLK_ROOT_DIV.

Remove them.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/media/i2c/ov5640.c | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index e8544cd8298f..481406de8c55 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -94,9 +94,6 @@
 #define OV5640_REG_SDE_CTRL5		0x5585
 #define OV5640_REG_AVG_READOUT		0x56a1
 
-#define OV5640_SCLK2X_ROOT_DIVIDER_DEFAULT	1
-#define OV5640_SCLK_ROOT_DIVIDER_DEFAULT	2
-
 enum ov5640_mode_id {
 	OV5640_MODE_QCIF_176_144 = 0,
 	OV5640_MODE_QVGA_320_240,
@@ -2009,8 +2006,8 @@ static int ov5640_restore_mode(struct ov5640_dev *sensor)
 	sensor->last_mode = &ov5640_mode_init_data;
 
 	ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x3f,
-			     (ilog2(OV5640_SCLK2X_ROOT_DIVIDER_DEFAULT) << 2) |
-			     ilog2(OV5640_SCLK_ROOT_DIVIDER_DEFAULT));
+			     (ilog2(OV5640_SCLK2X_ROOT_DIV) << 2) |
+			     ilog2(OV5640_SCLK_ROOT_DIV));
 	if (ret)
 		return ret;
 
-- 
2.19.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 04/12] media: ov5640: Remove redundant register setup
  2018-10-11  9:20 [PATCH v4 00/12] media: ov5640: Misc cleanup and improvements Maxime Ripard
                   ` (2 preceding siblings ...)
  2018-10-11  9:20 ` [PATCH v4 03/12] media: ov5640: Remove redundant defines Maxime Ripard
@ 2018-10-11  9:20 ` Maxime Ripard
  2018-10-11  9:21 ` [PATCH v4 05/12] media: ov5640: Compute the clock rate at runtime Maxime Ripard
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 35+ messages in thread
From: Maxime Ripard @ 2018-10-11  9:20 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Laurent Pinchart, linux-media, Thomas Petazzoni,
	Mylene Josserand, Hans Verkuil, Sakari Ailus, Hugues Fruchet,
	Loic Poulain, Samuel Bobrowicz, Steve Longerbeam, Daniel Mack,
	Jacopo Mondi, Maxime Ripard

The MIPI divider is also cleared as part of the clock setup sequence, so we
can remove that code.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/media/i2c/ov5640.c | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 481406de8c55..5114d401b8eb 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -1324,16 +1324,6 @@ static int ov5640_set_stream_dvp(struct ov5640_dev *sensor, bool on)
 	 */
 
 	if (on) {
-		/*
-		 * reset MIPI PCLK/SERCLK divider
-		 *
-		 * SC PLL CONTRL1 0
-		 * - [3..0]:	MIPI PCLK/SERCLK divider
-		 */
-		ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1, 0x0f, 0);
-		if (ret)
-			return ret;
-
 		/*
 		 * configure parallel port control lines polarity
 		 *
-- 
2.19.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 05/12] media: ov5640: Compute the clock rate at runtime
  2018-10-11  9:20 [PATCH v4 00/12] media: ov5640: Misc cleanup and improvements Maxime Ripard
                   ` (3 preceding siblings ...)
  2018-10-11  9:20 ` [PATCH v4 04/12] media: ov5640: Remove redundant register setup Maxime Ripard
@ 2018-10-11  9:21 ` Maxime Ripard
  2019-02-21 16:20   ` Benoit Parrot
  2018-10-11  9:21 ` [PATCH v4 06/12] media: ov5640: Remove pixel clock rates Maxime Ripard
                   ` (7 subsequent siblings)
  12 siblings, 1 reply; 35+ messages in thread
From: Maxime Ripard @ 2018-10-11  9:21 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Laurent Pinchart, linux-media, Thomas Petazzoni,
	Mylene Josserand, Hans Verkuil, Sakari Ailus, Hugues Fruchet,
	Loic Poulain, Samuel Bobrowicz, Steve Longerbeam, Daniel Mack,
	Jacopo Mondi, Maxime Ripard

The clock rate, while hardcoded until now, is actually a function of the
resolution, framerate and bytes per pixel. Now that we have an algorithm to
adjust our clock rate, we can select it dynamically when we change the
mode.

This changes a bit the clock rate being used, with the following effect:

+------+------+------+------+-----+-----------------+----------------+-----------+
| Hact | Vact | Htot | Vtot | FPS | Hardcoded clock | Computed clock | Deviation |
+------+------+------+------+-----+-----------------+----------------+-----------+
|  640 |  480 | 1896 | 1080 |  15 |        56000000 |       61430400 | 8.84 %    |
|  640 |  480 | 1896 | 1080 |  30 |       112000000 |      122860800 | 8.84 %    |
| 1024 |  768 | 1896 | 1080 |  15 |        56000000 |       61430400 | 8.84 %    |
| 1024 |  768 | 1896 | 1080 |  30 |       112000000 |      122860800 | 8.84 %    |
|  320 |  240 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
|  320 |  240 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
|  176 |  144 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
|  176 |  144 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
|  720 |  480 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
|  720 |  480 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
|  720 |  576 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
|  720 |  576 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
| 1280 |  720 | 1892 |  740 |  15 |        42000000 |       42002400 | 0.01 %    |
| 1280 |  720 | 1892 |  740 |  30 |        84000000 |       84004800 | 0.01 %    |
| 1920 | 1080 | 2500 | 1120 |  15 |        84000000 |       84000000 | 0.00 %    |
| 1920 | 1080 | 2500 | 1120 |  30 |       168000000 |      168000000 | 0.00 %    |
| 2592 | 1944 | 2844 | 1944 |  15 |        84000000 |      165862080 | 49.36 %   |
+------+------+------+------+-----+-----------------+----------------+-----------+

Only the 640x480, 1024x768 and 2592x1944 modes are significantly affected
by the new formula.

In this case, 640x480 and 1024x768 are actually fixed by this change.
Indeed, the sensor was sending data at, for example, 27.33fps instead of
30fps. This is -9%, which is roughly what we're seeing in the array.
Testing these modes with the new clock setup actually fix that error, and
data are now sent at around 30fps.

2592x1944, on the other hand, is probably due to the fact that this mode
can only be used using MIPI-CSI2, in a two lane mode, and never really
tested with a DVP bus.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/media/i2c/ov5640.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 5114d401b8eb..34eaa9dd5237 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -1915,7 +1915,8 @@ static int ov5640_set_mode(struct ov5640_dev *sensor)
 	 * which is 8 bits per pixel.
 	 */
 	bpp = sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8 ? 8 : 16;
-	rate = mode->pixel_clock * bpp;
+	rate = mode->vtot * mode->htot * bpp;
+	rate *= ov5640_framerates[sensor->current_fr];
 	if (sensor->ep.bus_type == V4L2_MBUS_CSI2) {
 		rate = rate / sensor->ep.bus.mipi_csi2.num_data_lanes;
 		ret = ov5640_set_mipi_pclk(sensor, rate);
-- 
2.19.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 06/12] media: ov5640: Remove pixel clock rates
  2018-10-11  9:20 [PATCH v4 00/12] media: ov5640: Misc cleanup and improvements Maxime Ripard
                   ` (4 preceding siblings ...)
  2018-10-11  9:21 ` [PATCH v4 05/12] media: ov5640: Compute the clock rate at runtime Maxime Ripard
@ 2018-10-11  9:21 ` Maxime Ripard
  2018-10-11  9:21 ` [PATCH v4 07/12] media: ov5640: Enhance FPS handling Maxime Ripard
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 35+ messages in thread
From: Maxime Ripard @ 2018-10-11  9:21 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Laurent Pinchart, linux-media, Thomas Petazzoni,
	Mylene Josserand, Hans Verkuil, Sakari Ailus, Hugues Fruchet,
	Loic Poulain, Samuel Bobrowicz, Steve Longerbeam, Daniel Mack,
	Jacopo Mondi, Maxime Ripard

The pixel clock rates were introduced to report the initially static clock
rate.

Since this is now handled dynamically, we can remove them entirely.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/media/i2c/ov5640.c | 21 +--------------------
 1 file changed, 1 insertion(+), 20 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 34eaa9dd5237..6bf6b4ccad6b 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -172,7 +172,6 @@ struct ov5640_mode_info {
 	u32 htot;
 	u32 vact;
 	u32 vtot;
-	u32 pixel_clock;
 	const struct reg_value *reg_data;
 	u32 reg_data_size;
 };
@@ -696,7 +695,6 @@ static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = {
 /* power-on sensor init reg table */
 static const struct ov5640_mode_info ov5640_mode_init_data = {
 	0, SUBSAMPLING, 640, 1896, 480, 984,
-	56000000,
 	ov5640_init_setting_30fps_VGA,
 	ARRAY_SIZE(ov5640_init_setting_30fps_VGA),
 };
@@ -706,91 +704,74 @@ ov5640_mode_data[OV5640_NUM_FRAMERATES][OV5640_NUM_MODES] = {
 	{
 		{OV5640_MODE_QCIF_176_144, SUBSAMPLING,
 		 176, 1896, 144, 984,
-		 28000000,
 		 ov5640_setting_15fps_QCIF_176_144,
 		 ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)},
 		{OV5640_MODE_QVGA_320_240, SUBSAMPLING,
 		 320, 1896, 240, 984,
-		 28000000,
 		 ov5640_setting_15fps_QVGA_320_240,
 		 ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)},
 		{OV5640_MODE_VGA_640_480, SUBSAMPLING,
 		 640, 1896, 480, 1080,
-		 28000000,
 		 ov5640_setting_15fps_VGA_640_480,
 		 ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)},
 		{OV5640_MODE_NTSC_720_480, SUBSAMPLING,
 		 720, 1896, 480, 984,
-		 28000000,
 		 ov5640_setting_15fps_NTSC_720_480,
 		 ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)},
 		{OV5640_MODE_PAL_720_576, SUBSAMPLING,
 		 720, 1896, 576, 984,
-		 28000000,
 		 ov5640_setting_15fps_PAL_720_576,
 		 ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)},
 		{OV5640_MODE_XGA_1024_768, SUBSAMPLING,
 		 1024, 1896, 768, 1080,
-		 28000000,
 		 ov5640_setting_15fps_XGA_1024_768,
 		 ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)},
 		{OV5640_MODE_720P_1280_720, SUBSAMPLING,
 		 1280, 1892, 720, 740,
-		 21000000,
 		 ov5640_setting_15fps_720P_1280_720,
 		 ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)},
 		{OV5640_MODE_1080P_1920_1080, SCALING,
 		 1920, 2500, 1080, 1120,
-		 42000000,
 		 ov5640_setting_15fps_1080P_1920_1080,
 		 ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)},
 		{OV5640_MODE_QSXGA_2592_1944, SCALING,
 		 2592, 2844, 1944, 1968,
-		 84000000,
 		 ov5640_setting_15fps_QSXGA_2592_1944,
 		 ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)},
 	}, {
 		{OV5640_MODE_QCIF_176_144, SUBSAMPLING,
 		 176, 1896, 144, 984,
-		 56000000,
 		 ov5640_setting_30fps_QCIF_176_144,
 		 ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)},
 		{OV5640_MODE_QVGA_320_240, SUBSAMPLING,
 		 320, 1896, 240, 984,
-		 56000000,
 		 ov5640_setting_30fps_QVGA_320_240,
 		 ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)},
 		{OV5640_MODE_VGA_640_480, SUBSAMPLING,
 		 640, 1896, 480, 1080,
-		 56000000,
 		 ov5640_setting_30fps_VGA_640_480,
 		 ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)},
 		{OV5640_MODE_NTSC_720_480, SUBSAMPLING,
 		 720, 1896, 480, 984,
-		 56000000,
 		 ov5640_setting_30fps_NTSC_720_480,
 		 ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)},
 		{OV5640_MODE_PAL_720_576, SUBSAMPLING,
 		 720, 1896, 576, 984,
-		 56000000,
 		 ov5640_setting_30fps_PAL_720_576,
 		 ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)},
 		{OV5640_MODE_XGA_1024_768, SUBSAMPLING,
 		 1024, 1896, 768, 1080,
-		 56000000,
 		 ov5640_setting_30fps_XGA_1024_768,
 		 ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)},
 		{OV5640_MODE_720P_1280_720, SUBSAMPLING,
 		 1280, 1892, 720, 740,
-		 42000000,
 		 ov5640_setting_30fps_720P_1280_720,
 		 ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)},
 		{OV5640_MODE_1080P_1920_1080, SCALING,
 		 1920, 2500, 1080, 1120,
-		 84000000,
 		 ov5640_setting_30fps_1080P_1920_1080,
 		 ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)},
-		{OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, 0, 0, 0, NULL, 0},
+		{OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, 0, 0, NULL, 0},
 	},
 };
 
-- 
2.19.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 07/12] media: ov5640: Enhance FPS handling
  2018-10-11  9:20 [PATCH v4 00/12] media: ov5640: Misc cleanup and improvements Maxime Ripard
                   ` (5 preceding siblings ...)
  2018-10-11  9:21 ` [PATCH v4 06/12] media: ov5640: Remove pixel clock rates Maxime Ripard
@ 2018-10-11  9:21 ` Maxime Ripard
  2018-10-11  9:21 ` [PATCH v4 08/12] media: ov5640: Make the return rate type more explicit Maxime Ripard
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 35+ messages in thread
From: Maxime Ripard @ 2018-10-11  9:21 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Laurent Pinchart, linux-media, Thomas Petazzoni,
	Mylene Josserand, Hans Verkuil, Sakari Ailus, Hugues Fruchet,
	Loic Poulain, Samuel Bobrowicz, Steve Longerbeam, Daniel Mack,
	Jacopo Mondi, Maxime Ripard

Now that we have moved the clock generation logic out of the bytes array,
these arrays are identical between the 15fps and 30fps variants.

Remove the duplicate entries, and convert the code accordingly.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/media/i2c/ov5640.c | 306 +++++++------------------------------
 1 file changed, 51 insertions(+), 255 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 6bf6b4ccad6b..84c0aae74420 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -340,64 +340,7 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
 	{0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300},
 };
 
-static const struct reg_value ov5640_setting_30fps_VGA_640_480[] = {
-	{0x3c07, 0x08, 0, 0},
-	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-	{0x3814, 0x31, 0, 0},
-	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
-	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
-	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
-	{0x3810, 0x00, 0, 0},
-	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
-	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
-	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
-	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0},
-	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
-	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
-	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
-	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
-	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0},
-};
-
-static const struct reg_value ov5640_setting_15fps_VGA_640_480[] = {
-	{0x3c07, 0x08, 0, 0},
-	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-	{0x3814, 0x31, 0, 0},
-	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
-	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
-	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
-	{0x3810, 0x00, 0, 0},
-	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
-	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
-	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
-	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
-	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
-	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
-	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
-	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
-	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
-};
-
-static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = {
-	{0x3c07, 0x08, 0, 0},
-	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-	{0x3814, 0x31, 0, 0},
-	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
-	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
-	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
-	{0x3810, 0x00, 0, 0},
-	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
-	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
-	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
-	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0},
-	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
-	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
-	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
-	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
-	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0},
-};
-
-static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = {
+static const struct reg_value ov5640_setting_VGA_640_480[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
@@ -416,7 +359,7 @@ static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = {
 	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
 };
 
-static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = {
+static const struct reg_value ov5640_setting_XGA_1024_768[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
@@ -435,7 +378,7 @@ static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = {
 	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
 };
 
-static const struct reg_value ov5640_setting_15fps_QVGA_320_240[] = {
+static const struct reg_value ov5640_setting_QVGA_320_240[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
@@ -454,7 +397,7 @@ static const struct reg_value ov5640_setting_15fps_QVGA_320_240[] = {
 	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
 };
 
-static const struct reg_value ov5640_setting_30fps_QCIF_176_144[] = {
+static const struct reg_value ov5640_setting_QCIF_176_144[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
@@ -473,26 +416,7 @@ static const struct reg_value ov5640_setting_30fps_QCIF_176_144[] = {
 	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
 };
 
-static const struct reg_value ov5640_setting_15fps_QCIF_176_144[] = {
-	{0x3c07, 0x08, 0, 0},
-	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-	{0x3814, 0x31, 0, 0},
-	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
-	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
-	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
-	{0x3810, 0x00, 0, 0},
-	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
-	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
-	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
-	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
-	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
-	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
-	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
-	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
-	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
-};
-
-static const struct reg_value ov5640_setting_30fps_NTSC_720_480[] = {
+static const struct reg_value ov5640_setting_NTSC_720_480[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
@@ -511,26 +435,7 @@ static const struct reg_value ov5640_setting_30fps_NTSC_720_480[] = {
 	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
 };
 
-static const struct reg_value ov5640_setting_15fps_NTSC_720_480[] = {
-	{0x3c07, 0x08, 0, 0},
-	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-	{0x3814, 0x31, 0, 0},
-	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
-	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
-	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
-	{0x3810, 0x00, 0, 0},
-	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0},
-	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
-	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
-	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
-	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
-	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
-	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
-	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
-	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
-};
-
-static const struct reg_value ov5640_setting_30fps_PAL_720_576[] = {
+static const struct reg_value ov5640_setting_PAL_720_576[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
@@ -549,47 +454,7 @@ static const struct reg_value ov5640_setting_30fps_PAL_720_576[] = {
 	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
 };
 
-static const struct reg_value ov5640_setting_15fps_PAL_720_576[] = {
-	{0x3c07, 0x08, 0, 0},
-	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-	{0x3814, 0x31, 0, 0},
-	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
-	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
-	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
-	{0x3810, 0x00, 0, 0},
-	{0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
-	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
-	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
-	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
-	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
-	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
-	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
-	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
-	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
-};
-
-static const struct reg_value ov5640_setting_30fps_720P_1280_720[] = {
-	{0x3008, 0x42, 0, 0},
-	{0x3c07, 0x07, 0, 0},
-	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-	{0x3814, 0x31, 0, 0},
-	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
-	{0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
-	{0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
-	{0x3810, 0x00, 0, 0},
-	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
-	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
-	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0},
-	{0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0},
-	{0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0},
-	{0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0},
-	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0},
-	{0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
-	{0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, {0x4005, 0x1a, 0, 0},
-	{0x3008, 0x02, 0, 0}, {0x3503, 0,    0, 0},
-};
-
-static const struct reg_value ov5640_setting_15fps_720P_1280_720[] = {
+static const struct reg_value ov5640_setting_720P_1280_720[] = {
 	{0x3c07, 0x07, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x31, 0, 0},
@@ -608,40 +473,7 @@ static const struct reg_value ov5640_setting_15fps_720P_1280_720[] = {
 	{0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0},
 };
 
-static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = {
-	{0x3008, 0x42, 0, 0},
-	{0x3c07, 0x08, 0, 0},
-	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-	{0x3814, 0x11, 0, 0},
-	{0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
-	{0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
-	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
-	{0x3810, 0x00, 0, 0},
-	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
-	{0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
-	{0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
-	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
-	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
-	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
-	{0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
-	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
-	{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0},
-	{0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
-	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-	{0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
-	{0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0},
-	{0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0},
-	{0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0},
-	{0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0},
-	{0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
-	{0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
-	{0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0},
-	{0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
-	{0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0},
-	{0x3503, 0, 0, 0},
-};
-
-static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = {
+static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
 	{0x3008, 0x42, 0, 0},
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
@@ -673,7 +505,7 @@ static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = {
 	{0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0},
 };
 
-static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = {
+static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = {
 	{0x3c07, 0x08, 0, 0},
 	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
 	{0x3814, 0x11, 0, 0},
@@ -700,79 +532,43 @@ static const struct ov5640_mode_info ov5640_mode_init_data = {
 };
 
 static const struct ov5640_mode_info
-ov5640_mode_data[OV5640_NUM_FRAMERATES][OV5640_NUM_MODES] = {
-	{
-		{OV5640_MODE_QCIF_176_144, SUBSAMPLING,
-		 176, 1896, 144, 984,
-		 ov5640_setting_15fps_QCIF_176_144,
-		 ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)},
-		{OV5640_MODE_QVGA_320_240, SUBSAMPLING,
-		 320, 1896, 240, 984,
-		 ov5640_setting_15fps_QVGA_320_240,
-		 ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)},
-		{OV5640_MODE_VGA_640_480, SUBSAMPLING,
-		 640, 1896, 480, 1080,
-		 ov5640_setting_15fps_VGA_640_480,
-		 ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)},
-		{OV5640_MODE_NTSC_720_480, SUBSAMPLING,
-		 720, 1896, 480, 984,
-		 ov5640_setting_15fps_NTSC_720_480,
-		 ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)},
-		{OV5640_MODE_PAL_720_576, SUBSAMPLING,
-		 720, 1896, 576, 984,
-		 ov5640_setting_15fps_PAL_720_576,
-		 ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)},
-		{OV5640_MODE_XGA_1024_768, SUBSAMPLING,
-		 1024, 1896, 768, 1080,
-		 ov5640_setting_15fps_XGA_1024_768,
-		 ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)},
-		{OV5640_MODE_720P_1280_720, SUBSAMPLING,
-		 1280, 1892, 720, 740,
-		 ov5640_setting_15fps_720P_1280_720,
-		 ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)},
-		{OV5640_MODE_1080P_1920_1080, SCALING,
-		 1920, 2500, 1080, 1120,
-		 ov5640_setting_15fps_1080P_1920_1080,
-		 ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)},
-		{OV5640_MODE_QSXGA_2592_1944, SCALING,
-		 2592, 2844, 1944, 1968,
-		 ov5640_setting_15fps_QSXGA_2592_1944,
-		 ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)},
-	}, {
-		{OV5640_MODE_QCIF_176_144, SUBSAMPLING,
-		 176, 1896, 144, 984,
-		 ov5640_setting_30fps_QCIF_176_144,
-		 ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)},
-		{OV5640_MODE_QVGA_320_240, SUBSAMPLING,
-		 320, 1896, 240, 984,
-		 ov5640_setting_30fps_QVGA_320_240,
-		 ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)},
-		{OV5640_MODE_VGA_640_480, SUBSAMPLING,
-		 640, 1896, 480, 1080,
-		 ov5640_setting_30fps_VGA_640_480,
-		 ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)},
-		{OV5640_MODE_NTSC_720_480, SUBSAMPLING,
-		 720, 1896, 480, 984,
-		 ov5640_setting_30fps_NTSC_720_480,
-		 ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)},
-		{OV5640_MODE_PAL_720_576, SUBSAMPLING,
-		 720, 1896, 576, 984,
-		 ov5640_setting_30fps_PAL_720_576,
-		 ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)},
-		{OV5640_MODE_XGA_1024_768, SUBSAMPLING,
-		 1024, 1896, 768, 1080,
-		 ov5640_setting_30fps_XGA_1024_768,
-		 ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)},
-		{OV5640_MODE_720P_1280_720, SUBSAMPLING,
-		 1280, 1892, 720, 740,
-		 ov5640_setting_30fps_720P_1280_720,
-		 ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)},
-		{OV5640_MODE_1080P_1920_1080, SCALING,
-		 1920, 2500, 1080, 1120,
-		 ov5640_setting_30fps_1080P_1920_1080,
-		 ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)},
-		{OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, 0, 0, NULL, 0},
-	},
+ov5640_mode_data[OV5640_NUM_MODES] = {
+	{OV5640_MODE_QCIF_176_144, SUBSAMPLING,
+	 176, 1896, 144, 984,
+	 ov5640_setting_QCIF_176_144,
+	 ARRAY_SIZE(ov5640_setting_QCIF_176_144)},
+	{OV5640_MODE_QVGA_320_240, SUBSAMPLING,
+	 320, 1896, 240, 984,
+	 ov5640_setting_QVGA_320_240,
+	 ARRAY_SIZE(ov5640_setting_QVGA_320_240)},
+	{OV5640_MODE_VGA_640_480, SUBSAMPLING,
+	 640, 1896, 480, 1080,
+	 ov5640_setting_VGA_640_480,
+	 ARRAY_SIZE(ov5640_setting_VGA_640_480)},
+	{OV5640_MODE_NTSC_720_480, SUBSAMPLING,
+	 720, 1896, 480, 984,
+	 ov5640_setting_NTSC_720_480,
+	 ARRAY_SIZE(ov5640_setting_NTSC_720_480)},
+	{OV5640_MODE_PAL_720_576, SUBSAMPLING,
+	 720, 1896, 576, 984,
+	 ov5640_setting_PAL_720_576,
+	 ARRAY_SIZE(ov5640_setting_PAL_720_576)},
+	{OV5640_MODE_XGA_1024_768, SUBSAMPLING,
+	 1024, 1896, 768, 1080,
+	 ov5640_setting_XGA_1024_768,
+	 ARRAY_SIZE(ov5640_setting_XGA_1024_768)},
+	{OV5640_MODE_720P_1280_720, SUBSAMPLING,
+	 1280, 1892, 720, 740,
+	 ov5640_setting_720P_1280_720,
+	 ARRAY_SIZE(ov5640_setting_720P_1280_720)},
+	{OV5640_MODE_1080P_1920_1080, SCALING,
+	 1920, 2500, 1080, 1120,
+	 ov5640_setting_1080P_1920_1080,
+	 ARRAY_SIZE(ov5640_setting_1080P_1920_1080)},
+	{OV5640_MODE_QSXGA_2592_1944, SCALING,
+	 2592, 2844, 1944, 1968,
+	 ov5640_setting_QSXGA_2592_1944,
+	 ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944)},
 };
 
 static int ov5640_init_slave_id(struct ov5640_dev *sensor)
@@ -1678,8 +1474,8 @@ ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr,
 {
 	const struct ov5640_mode_info *mode;
 
-	mode = v4l2_find_nearest_size(ov5640_mode_data[fr],
-				      ARRAY_SIZE(ov5640_mode_data[fr]),
+	mode = v4l2_find_nearest_size(ov5640_mode_data,
+				      ARRAY_SIZE(ov5640_mode_data),
 				      hact, vact,
 				      width, height);
 
@@ -2756,10 +2552,10 @@ static int ov5640_enum_frame_size(struct v4l2_subdev *sd,
 		return -EINVAL;
 
 	fse->min_width =
-		ov5640_mode_data[0][fse->index].hact;
+		ov5640_mode_data[fse->index].hact;
 	fse->max_width = fse->min_width;
 	fse->min_height =
-		ov5640_mode_data[0][fse->index].vact;
+		ov5640_mode_data[fse->index].vact;
 	fse->max_height = fse->min_height;
 
 	return 0;
@@ -2989,7 +2785,7 @@ static int ov5640_probe(struct i2c_client *client,
 	sensor->frame_interval.denominator = ov5640_framerates[OV5640_30_FPS];
 	sensor->current_fr = OV5640_30_FPS;
 	sensor->current_mode =
-		&ov5640_mode_data[OV5640_30_FPS][OV5640_MODE_VGA_640_480];
+		&ov5640_mode_data[OV5640_MODE_VGA_640_480];
 	sensor->last_mode = sensor->current_mode;
 
 	sensor->ae_target = 52;
-- 
2.19.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 08/12] media: ov5640: Make the return rate type more explicit
  2018-10-11  9:20 [PATCH v4 00/12] media: ov5640: Misc cleanup and improvements Maxime Ripard
                   ` (6 preceding siblings ...)
  2018-10-11  9:21 ` [PATCH v4 07/12] media: ov5640: Enhance FPS handling Maxime Ripard
@ 2018-10-11  9:21 ` Maxime Ripard
  2018-10-11  9:21 ` [PATCH v4 09/12] media: ov5640: Make the FPS clamping / rounding more extendable Maxime Ripard
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 35+ messages in thread
From: Maxime Ripard @ 2018-10-11  9:21 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Laurent Pinchart, linux-media, Thomas Petazzoni,
	Mylene Josserand, Hans Verkuil, Sakari Ailus, Hugues Fruchet,
	Loic Poulain, Samuel Bobrowicz, Steve Longerbeam, Daniel Mack,
	Jacopo Mondi, Maxime Ripard

In the ov5640_try_frame_interval function, the ret variable actually holds
the frame rate index to use, which is represented by the enum
ov5640_frame_rate in the driver.

Make it more obvious.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/media/i2c/ov5640.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 84c0aae74420..d7a1e1928baf 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -1975,8 +1975,8 @@ static int ov5640_try_frame_interval(struct ov5640_dev *sensor,
 				     u32 width, u32 height)
 {
 	const struct ov5640_mode_info *mode;
+	enum ov5640_frame_rate rate = OV5640_30_FPS;
 	u32 minfps, maxfps, fps;
-	int ret;
 
 	minfps = ov5640_framerates[OV5640_15_FPS];
 	maxfps = ov5640_framerates[OV5640_30_FPS];
@@ -1999,10 +1999,10 @@ static int ov5640_try_frame_interval(struct ov5640_dev *sensor,
 	else
 		fi->denominator = minfps;
 
-	ret = (fi->denominator == minfps) ? OV5640_15_FPS : OV5640_30_FPS;
+	rate = (fi->denominator == minfps) ? OV5640_15_FPS : OV5640_30_FPS;
 
-	mode = ov5640_find_mode(sensor, ret, width, height, false);
-	return mode ? ret : -EINVAL;
+	mode = ov5640_find_mode(sensor, rate, width, height, false);
+	return mode ? rate : -EINVAL;
 }
 
 static int ov5640_get_fmt(struct v4l2_subdev *sd,
-- 
2.19.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 09/12] media: ov5640: Make the FPS clamping / rounding more extendable
  2018-10-11  9:20 [PATCH v4 00/12] media: ov5640: Misc cleanup and improvements Maxime Ripard
                   ` (7 preceding siblings ...)
  2018-10-11  9:21 ` [PATCH v4 08/12] media: ov5640: Make the return rate type more explicit Maxime Ripard
@ 2018-10-11  9:21 ` Maxime Ripard
  2018-10-11  9:21 ` [PATCH v4 10/12] media: ov5640: Add 60 fps support Maxime Ripard
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 35+ messages in thread
From: Maxime Ripard @ 2018-10-11  9:21 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Laurent Pinchart, linux-media, Thomas Petazzoni,
	Mylene Josserand, Hans Verkuil, Sakari Ailus, Hugues Fruchet,
	Loic Poulain, Samuel Bobrowicz, Steve Longerbeam, Daniel Mack,
	Jacopo Mondi, Maxime Ripard

The current code uses an algorithm to clamp the FPS values and round them
to the closest supported one that isn't really allows to be extended to
more than two values.

Rework it a bit to make it much easier to extend the amount of FPS options
we support.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/media/i2c/ov5640.c | 27 +++++++++++++++------------
 1 file changed, 15 insertions(+), 12 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index d7a1e1928baf..b2206fa71b0d 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -1976,7 +1976,8 @@ static int ov5640_try_frame_interval(struct ov5640_dev *sensor,
 {
 	const struct ov5640_mode_info *mode;
 	enum ov5640_frame_rate rate = OV5640_30_FPS;
-	u32 minfps, maxfps, fps;
+	int minfps, maxfps, best_fps, fps;
+	int i;
 
 	minfps = ov5640_framerates[OV5640_15_FPS];
 	maxfps = ov5640_framerates[OV5640_30_FPS];
@@ -1987,19 +1988,21 @@ static int ov5640_try_frame_interval(struct ov5640_dev *sensor,
 		return OV5640_30_FPS;
 	}
 
-	fps = DIV_ROUND_CLOSEST(fi->denominator, fi->numerator);
+	fps = clamp_val(DIV_ROUND_CLOSEST(fi->denominator, fi->numerator),
+			minfps, maxfps);
+
+	best_fps = minfps;
+	for (i = 0; i < ARRAY_SIZE(ov5640_framerates); i++) {
+		int curr_fps = ov5640_framerates[i];
+
+		if (abs(curr_fps - fps) < abs(best_fps - fps)) {
+			best_fps = curr_fps;
+			rate = i;
+		}
+	}
 
 	fi->numerator = 1;
-	if (fps > maxfps)
-		fi->denominator = maxfps;
-	else if (fps < minfps)
-		fi->denominator = minfps;
-	else if (2 * fps >= 2 * minfps + (maxfps - minfps))
-		fi->denominator = maxfps;
-	else
-		fi->denominator = minfps;
-
-	rate = (fi->denominator == minfps) ? OV5640_15_FPS : OV5640_30_FPS;
+	fi->denominator = best_fps;
 
 	mode = ov5640_find_mode(sensor, rate, width, height, false);
 	return mode ? rate : -EINVAL;
-- 
2.19.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 10/12] media: ov5640: Add 60 fps support
  2018-10-11  9:20 [PATCH v4 00/12] media: ov5640: Misc cleanup and improvements Maxime Ripard
                   ` (8 preceding siblings ...)
  2018-10-11  9:21 ` [PATCH v4 09/12] media: ov5640: Make the FPS clamping / rounding more extendable Maxime Ripard
@ 2018-10-11  9:21 ` Maxime Ripard
  2018-10-11  9:21 ` [PATCH v4 11/12] media: ov5640: Remove duplicate auto-exposure setup Maxime Ripard
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 35+ messages in thread
From: Maxime Ripard @ 2018-10-11  9:21 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Laurent Pinchart, linux-media, Thomas Petazzoni,
	Mylene Josserand, Hans Verkuil, Sakari Ailus, Hugues Fruchet,
	Loic Poulain, Samuel Bobrowicz, Steve Longerbeam, Daniel Mack,
	Jacopo Mondi, Maxime Ripard

Now that we have everything in place to compute the clock rate at runtime,
we can enable the 60fps framerate for the mode we tested it with.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/media/i2c/ov5640.c | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index b2206fa71b0d..9ce12c3cf7c7 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -110,6 +110,7 @@ enum ov5640_mode_id {
 enum ov5640_frame_rate {
 	OV5640_15_FPS = 0,
 	OV5640_30_FPS,
+	OV5640_60_FPS,
 	OV5640_NUM_FRAMERATES,
 };
 
@@ -138,6 +139,7 @@ MODULE_PARM_DESC(virtual_channel,
 static const int ov5640_framerates[] = {
 	[OV5640_15_FPS] = 15,
 	[OV5640_30_FPS] = 30,
+	[OV5640_60_FPS] = 60,
 };
 
 /* regulator supplies */
@@ -1483,6 +1485,11 @@ ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr,
 	    (!nearest && (mode->hact != width || mode->vact != height)))
 		return NULL;
 
+	/* Only 640x480 can operate at 60fps (for now) */
+	if (fr == OV5640_60_FPS &&
+	    !(mode->hact == 640 && mode->vact == 480))
+		return NULL;
+
 	return mode;
 }
 
@@ -1980,12 +1987,13 @@ static int ov5640_try_frame_interval(struct ov5640_dev *sensor,
 	int i;
 
 	minfps = ov5640_framerates[OV5640_15_FPS];
-	maxfps = ov5640_framerates[OV5640_30_FPS];
+	maxfps = ov5640_framerates[OV5640_60_FPS];
 
 	if (fi->numerator == 0) {
 		fi->denominator = maxfps;
 		fi->numerator = 1;
-		return OV5640_30_FPS;
+		rate = OV5640_60_FPS;
+		goto find_mode;
 	}
 
 	fps = clamp_val(DIV_ROUND_CLOSEST(fi->denominator, fi->numerator),
@@ -2004,6 +2012,7 @@ static int ov5640_try_frame_interval(struct ov5640_dev *sensor,
 	fi->numerator = 1;
 	fi->denominator = best_fps;
 
+find_mode:
 	mode = ov5640_find_mode(sensor, rate, width, height, false);
 	return mode ? rate : -EINVAL;
 }
@@ -2623,8 +2632,11 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
 
 	frame_rate = ov5640_try_frame_interval(sensor, &fi->interval,
 					       mode->hact, mode->vact);
-	if (frame_rate < 0)
-		frame_rate = OV5640_15_FPS;
+	if (frame_rate < 0) {
+		/* Always return a valid frame interval value */
+		fi->interval = sensor->frame_interval;
+		goto out;
+	}
 
 	sensor->current_fr = frame_rate;
 	sensor->frame_interval = fi->interval;
-- 
2.19.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 11/12] media: ov5640: Remove duplicate auto-exposure setup
  2018-10-11  9:20 [PATCH v4 00/12] media: ov5640: Misc cleanup and improvements Maxime Ripard
                   ` (9 preceding siblings ...)
  2018-10-11  9:21 ` [PATCH v4 10/12] media: ov5640: Add 60 fps support Maxime Ripard
@ 2018-10-11  9:21 ` Maxime Ripard
  2018-10-11  9:21 ` [PATCH v4 12/12] ov5640: Enforce a mode change when changing the framerate Maxime Ripard
  2018-10-16 15:57 ` [PATCH v4 00/12] media: ov5640: Misc cleanup and improvements jacopo mondi
  12 siblings, 0 replies; 35+ messages in thread
From: Maxime Ripard @ 2018-10-11  9:21 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Laurent Pinchart, linux-media, Thomas Petazzoni,
	Mylene Josserand, Hans Verkuil, Sakari Ailus, Hugues Fruchet,
	Loic Poulain, Samuel Bobrowicz, Steve Longerbeam, Daniel Mack,
	Jacopo Mondi, Maxime Ripard

The autoexposure setup in the 1080p init array is redundant with the
default value of the sensor.

Remove it.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/media/i2c/ov5640.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 9ce12c3cf7c7..818411400ef6 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -504,7 +504,7 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
 	{0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
 	{0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0},
 	{0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
-	{0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0},
+	{0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0},
 };
 
 static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = {
-- 
2.19.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4 12/12] ov5640: Enforce a mode change when changing the framerate
  2018-10-11  9:20 [PATCH v4 00/12] media: ov5640: Misc cleanup and improvements Maxime Ripard
                   ` (10 preceding siblings ...)
  2018-10-11  9:21 ` [PATCH v4 11/12] media: ov5640: Remove duplicate auto-exposure setup Maxime Ripard
@ 2018-10-11  9:21 ` Maxime Ripard
  2018-10-15 13:57   ` Hugues FRUCHET
  2018-10-16 15:57 ` [PATCH v4 00/12] media: ov5640: Misc cleanup and improvements jacopo mondi
  12 siblings, 1 reply; 35+ messages in thread
From: Maxime Ripard @ 2018-10-11  9:21 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Laurent Pinchart, linux-media, Thomas Petazzoni,
	Mylene Josserand, Hans Verkuil, Sakari Ailus, Hugues Fruchet,
	Loic Poulain, Samuel Bobrowicz, Steve Longerbeam, Daniel Mack,
	Jacopo Mondi, Maxime Ripard

The current logic only requires to call ov5640_set_mode, which will in turn
change the clock rates according to the mode and frame interval, when a new
mode is set up.

However, when only the frame interval is changed but the mode isn't,
ov5640_set_mode is never called and the resulting frame rate will be old or
default one. Fix this by requiring that ov5640_set_mode is called when the
frame interval is changed as well.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/media/i2c/ov5640.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 818411400ef6..e01d2cb93c67 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -2638,8 +2638,12 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
 		goto out;
 	}
 
-	sensor->current_fr = frame_rate;
-	sensor->frame_interval = fi->interval;
+	if (frame_rate != sensor->current_fr) {
+		sensor->current_fr = frame_rate;
+		sensor->frame_interval = fi->interval;
+		sensor->pending_mode_change = true;
+	}
+
 	mode = ov5640_find_mode(sensor, frame_rate, mode->hact,
 				mode->vact, true);
 	if (!mode) {
-- 
2.19.1

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 12/12] ov5640: Enforce a mode change when changing the framerate
  2018-10-11  9:21 ` [PATCH v4 12/12] ov5640: Enforce a mode change when changing the framerate Maxime Ripard
@ 2018-10-15 13:57   ` Hugues FRUCHET
  2018-10-16  7:10     ` Maxime Ripard
  0 siblings, 1 reply; 35+ messages in thread
From: Hugues FRUCHET @ 2018-10-15 13:57 UTC (permalink / raw)
  To: Maxime Ripard, Mauro Carvalho Chehab
  Cc: Laurent Pinchart, linux-media, Thomas Petazzoni,
	Mylene Josserand, Hans Verkuil, Sakari Ailus, Loic Poulain,
	Samuel Bobrowicz, Steve Longerbeam, Daniel Mack, Jacopo Mondi

Hi Maxime,

This is already fixed in media tree:
0929983e49c81c1d413702cd9b83bb06c4a2555c media: ov5640: fix framerate update


On 10/11/2018 11:21 AM, Maxime Ripard wrote:
> The current logic only requires to call ov5640_set_mode, which will in turn
> change the clock rates according to the mode and frame interval, when a new
> mode is set up.
> 
> However, when only the frame interval is changed but the mode isn't,
> ov5640_set_mode is never called and the resulting frame rate will be old or
> default one. Fix this by requiring that ov5640_set_mode is called when the
> frame interval is changed as well.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> ---
>   drivers/media/i2c/ov5640.c | 8 ++++++--
>   1 file changed, 6 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> index 818411400ef6..e01d2cb93c67 100644
> --- a/drivers/media/i2c/ov5640.c
> +++ b/drivers/media/i2c/ov5640.c
> @@ -2638,8 +2638,12 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
>   		goto out;
>   	}
>   
> -	sensor->current_fr = frame_rate;
> -	sensor->frame_interval = fi->interval;
> +	if (frame_rate != sensor->current_fr) {
> +		sensor->current_fr = frame_rate;
> +		sensor->frame_interval = fi->interval;
> +		sensor->pending_mode_change = true;
> +	}
> +
>   	mode = ov5640_find_mode(sensor, frame_rate, mode->hact,
>   				mode->vact, true);
>   	if (!mode) {
> 

BR,
Hugues.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 01/12] media: ov5640: Adjust the clock based on the expected rate
  2018-10-11  9:20 ` [PATCH v4 01/12] media: ov5640: Adjust the clock based on the expected rate Maxime Ripard
@ 2018-10-15 14:05   ` Hugues FRUCHET
  2018-10-16 16:54   ` jacopo mondi
  1 sibling, 0 replies; 35+ messages in thread
From: Hugues FRUCHET @ 2018-10-15 14:05 UTC (permalink / raw)
  To: Maxime Ripard, Mauro Carvalho Chehab
  Cc: Laurent Pinchart, linux-media, Thomas Petazzoni,
	Mylene Josserand, Hans Verkuil, Sakari Ailus, Loic Poulain,
	Samuel Bobrowicz, Steve Longerbeam, Daniel Mack, Jacopo Mondi

Hi Maxime,

I've recently found a problem around JPEG framerate, see below:

On 10/11/2018 11:20 AM, Maxime Ripard wrote:
> The clock structure for the PCLK is quite obscure in the documentation, and
> was hardcoded through the bytes array of each and every mode.
> 
> This is troublesome, since we cannot adjust it at runtime based on other
> parameters (such as the number of bytes per pixel), and we can't support
> either framerates that have not been used by the various vendors, since we
> don't have the needed initialization sequence.
> 
> We can however understand how the clock tree works, and then implement some
> functions to derive the various parameters from a given rate. And now that
> those parameters are calculated at runtime, we can remove them from the
> initialization sequence.
> 
> The modes also gained a new parameter which is the clock that they are
> running at, from the register writes they were doing, so for now the switch
> to the new algorithm should be transparent.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> ---
>   drivers/media/i2c/ov5640.c | 289 ++++++++++++++++++++++++++++++++++++-
>   1 file changed, 288 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> index 30b15e91d8be..88fb16341466 100644
> --- a/drivers/media/i2c/ov5640.c
> +++ b/drivers/media/i2c/ov5640.c
> @@ -175,6 +175,7 @@ struct ov5640_mode_info {
>   	u32 htot;
>   	u32 vact;
>   	u32 vtot;
> +	u32 pixel_clock;
>   	const struct reg_value *reg_data;
>   	u32 reg_data_size;
>   };
> @@ -700,6 +701,7 @@ static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = {
>   /* power-on sensor init reg table */
>   static const struct ov5640_mode_info ov5640_mode_init_data = {
>   	0, SUBSAMPLING, 640, 1896, 480, 984,
> +	56000000,
>   	ov5640_init_setting_30fps_VGA,
>   	ARRAY_SIZE(ov5640_init_setting_30fps_VGA),
>   };
> @@ -709,74 +711,91 @@ ov5640_mode_data[OV5640_NUM_FRAMERATES][OV5640_NUM_MODES] = {
>   	{
>   		{OV5640_MODE_QCIF_176_144, SUBSAMPLING,
>   		 176, 1896, 144, 984,
> +		 28000000,
>   		 ov5640_setting_15fps_QCIF_176_144,
>   		 ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)},
>   		{OV5640_MODE_QVGA_320_240, SUBSAMPLING,
>   		 320, 1896, 240, 984,
> +		 28000000,
>   		 ov5640_setting_15fps_QVGA_320_240,
>   		 ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)},
>   		{OV5640_MODE_VGA_640_480, SUBSAMPLING,
>   		 640, 1896, 480, 1080,
> +		 28000000,
>   		 ov5640_setting_15fps_VGA_640_480,
>   		 ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)},
>   		{OV5640_MODE_NTSC_720_480, SUBSAMPLING,
>   		 720, 1896, 480, 984,
> +		 28000000,
>   		 ov5640_setting_15fps_NTSC_720_480,
>   		 ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)},
>   		{OV5640_MODE_PAL_720_576, SUBSAMPLING,
>   		 720, 1896, 576, 984,
> +		 28000000,
>   		 ov5640_setting_15fps_PAL_720_576,
>   		 ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)},
>   		{OV5640_MODE_XGA_1024_768, SUBSAMPLING,
>   		 1024, 1896, 768, 1080,
> +		 28000000,
>   		 ov5640_setting_15fps_XGA_1024_768,
>   		 ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)},
>   		{OV5640_MODE_720P_1280_720, SUBSAMPLING,
>   		 1280, 1892, 720, 740,
> +		 21000000,
>   		 ov5640_setting_15fps_720P_1280_720,
>   		 ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)},
>   		{OV5640_MODE_1080P_1920_1080, SCALING,
>   		 1920, 2500, 1080, 1120,
> +		 42000000,
>   		 ov5640_setting_15fps_1080P_1920_1080,
>   		 ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)},
>   		{OV5640_MODE_QSXGA_2592_1944, SCALING,
>   		 2592, 2844, 1944, 1968,
> +		 84000000,
>   		 ov5640_setting_15fps_QSXGA_2592_1944,
>   		 ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)},
>   	}, {
>   		{OV5640_MODE_QCIF_176_144, SUBSAMPLING,
>   		 176, 1896, 144, 984,
> +		 56000000,
>   		 ov5640_setting_30fps_QCIF_176_144,
>   		 ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)},
>   		{OV5640_MODE_QVGA_320_240, SUBSAMPLING,
>   		 320, 1896, 240, 984,
> +		 56000000,
>   		 ov5640_setting_30fps_QVGA_320_240,
>   		 ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)},
>   		{OV5640_MODE_VGA_640_480, SUBSAMPLING,
>   		 640, 1896, 480, 1080,
> +		 56000000,
>   		 ov5640_setting_30fps_VGA_640_480,
>   		 ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)},
>   		{OV5640_MODE_NTSC_720_480, SUBSAMPLING,
>   		 720, 1896, 480, 984,
> +		 56000000,
>   		 ov5640_setting_30fps_NTSC_720_480,
>   		 ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)},
>   		{OV5640_MODE_PAL_720_576, SUBSAMPLING,
>   		 720, 1896, 576, 984,
> +		 56000000,
>   		 ov5640_setting_30fps_PAL_720_576,
>   		 ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)},
>   		{OV5640_MODE_XGA_1024_768, SUBSAMPLING,
>   		 1024, 1896, 768, 1080,
> +		 56000000,
>   		 ov5640_setting_30fps_XGA_1024_768,
>   		 ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)},
>   		{OV5640_MODE_720P_1280_720, SUBSAMPLING,
>   		 1280, 1892, 720, 740,
> +		 42000000,
>   		 ov5640_setting_30fps_720P_1280_720,
>   		 ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)},
>   		{OV5640_MODE_1080P_1920_1080, SCALING,
>   		 1920, 2500, 1080, 1120,
> +		 84000000,
>   		 ov5640_setting_30fps_1080P_1920_1080,
>   		 ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)},
> -		{OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, 0, 0, NULL, 0},
> +		{OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, 0, 0, 0, NULL, 0},
>   	},
>   };
>   
> @@ -909,6 +928,255 @@ static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg,
>   	return ov5640_write_reg(sensor, reg, val);
>   }
>   
> +/*
> + * After trying the various combinations, reading various
> + * documentations spreaded around the net, and from the various
> + * feedback, the clock tree is probably as follows:
> + *
> + *   +--------------+
> + *   |  Ext. Clock  |
> + *   +-+------------+
> + *     |  +----------+
> + *     +->|   PLL1   | - reg 0x3036, for the multiplier
> + *        +-+--------+ - reg 0x3037, bits 0-3 for the pre-divider
> + *          |  +--------------+
> + *          +->| System Clock |  - reg 0x3035, bits 4-7
> + *             +-+------------+
> + *               |  +--------------+
> + *               +->| MIPI Divider | - reg 0x3035, bits 0-3
> + *               |  +-+------------+
> + *               |    +----------------> MIPI SCLK
> + *               |  +--------------+
> + *               +->| PLL Root Div | - reg 0x3037, bit 4
> + *                  +-+------------+
> + *                    |  +---------+
> + *                    +->| Bit Div | - reg 0x3035, bits 0-3
> + *                       +-+-------+
> + *                         |  +-------------+
> + *                         +->| SCLK Div    | - reg 0x3108, bits 0-1
> + *                         |  +-+-----------+
> + *                         |    +---------------> SCLK
> + *                         |  +-------------+
> + *                         +->| SCLK 2X Div | - reg 0x3108, bits 2-3
> + *                         |  +-+-----------+
> + *                         |    +---------------> SCLK 2X
> + *                         |  +-------------+
> + *                         +->| PCLK Div    | - reg 0x3108, bits 4-5
> + *                            +-+-----------+
> + *                              +---------------> PCLK
> + *
> + * This is deviating from the datasheet at least for the register
> + * 0x3108, since it's said here that the PCLK would be clocked from
> + * the PLL.
> + *
> + * There seems to be also (unverified) constraints:
> + *  - the PLL pre-divider output rate should be in the 4-27MHz range
> + *  - the PLL multiplier output rate should be in the 500-1000MHz range
> + *  - PCLK >= SCLK * 2 in YUV, >= SCLK in Raw or JPEG
> + *  - MIPI SCLK = (bpp / lanes) / PCLK
> + *
> + * In the two latter cases, these constraints are met since our
> + * factors are hardcoded. If we were to change that, we would need to
> + * take this into account. The only varying parts are the PLL
> + * multiplier and the system clock divider, which are shared between
> + * all these clocks so won't cause any issue.
> + */
> +
> +/*
> + * This is supposed to be ranging from 1 to 8, but the value is always
> + * set to 3 in the vendor kernels.
> + */
> +#define OV5640_PLL_PREDIV	3
> +
> +#define OV5640_PLL_MULT_MIN	4
> +#define OV5640_PLL_MULT_MAX	252
> +
> +/*
> + * This is supposed to be ranging from 1 to 16, but the value is
> + * always set to either 1 or 2 in the vendor kernels.
> + */
> +#define OV5640_SYSDIV_MIN	1
> +#define OV5640_SYSDIV_MAX	2
> +
> +/*
> + * This is supposed to be ranging from 1 to 16, but the value is always
> + * set to 2 in the vendor kernels.
> + */
> +#define OV5640_MIPI_DIV		2
> +
> +/*
> + * This is supposed to be ranging from 1 to 2, but the value is always
> + * set to 2 in the vendor kernels.
> + */
> +#define OV5640_PLL_ROOT_DIV	2
> +
> +/*
> + * This is supposed to be either 1, 2 or 2.5, but the value is always
> + * set to 2 in the vendor kernels.
> + */
> +#define OV5640_BIT_DIV		2
> +
> +/*
> + * This is supposed to be ranging from 1 to 8, but the value is always
> + * set to 2 in the vendor kernels.
> + */
> +#define OV5640_SCLK_ROOT_DIV	2
> +
> +/*
> + * This is hardcoded so that the consistency is maintained between SCLK and
> + * SCLK 2x.
> + */
> +#define OV5640_SCLK2X_ROOT_DIV (OV5640_SCLK_ROOT_DIV / 2)
> +
> +/*
> + * This is supposed to be ranging from 1 to 8, but the value is always
> + * set to 1 in the vendor kernels.
> + */
> +#define OV5640_PCLK_ROOT_DIV	1
> +
> +static unsigned long ov5640_compute_sys_clk(struct ov5640_dev *sensor,
> +					    u8 pll_prediv, u8 pll_mult,
> +					    u8 sysdiv)
> +{
> +	unsigned long rate = clk_get_rate(sensor->xclk);
> +
> +	return rate / pll_prediv * pll_mult / sysdiv;
> +}
> +
> +static unsigned long ov5640_calc_sys_clk(struct ov5640_dev *sensor,
> +					 unsigned long rate,
> +					 u8 *pll_prediv, u8 *pll_mult,
> +					 u8 *sysdiv)
> +{
> +	unsigned long best = ~0;
> +	u8 best_sysdiv = 1, best_mult = 1;
> +	u8 _sysdiv, _pll_mult;
> +
> +	for (_sysdiv = OV5640_SYSDIV_MIN;
> +	     _sysdiv <= OV5640_SYSDIV_MAX;
> +	     _sysdiv++) {
> +		for (_pll_mult = OV5640_PLL_MULT_MIN;
> +		     _pll_mult <= OV5640_PLL_MULT_MAX;
> +		     _pll_mult++) {
> +			unsigned long _rate;
> +
> +			/*
> +			 * The PLL multiplier cannot be odd if above
> +			 * 127.
> +			 */
> +			if (_pll_mult > 127 && (_pll_mult % 2))
> +				continue;
> +
> +			_rate = ov5640_compute_sys_clk(sensor,
> +						       OV5640_PLL_PREDIV,
> +						       _pll_mult, _sysdiv);
> +			if (abs(rate - _rate) < abs(rate - best)) {
> +				best = _rate;
> +				best_sysdiv = _sysdiv;
> +				best_mult = _pll_mult;
> +			}
> +
> +			if (_rate == rate)
> +				goto out;
> +		}
> +	}
> +
> +out:
> +	*sysdiv = best_sysdiv;
> +	*pll_prediv = OV5640_PLL_PREDIV;
> +	*pll_mult = best_mult;
> +	return best;
> +}
> +
> +static unsigned long ov5640_calc_mipi_clk(struct ov5640_dev *sensor,
> +					  unsigned long rate,
> +					  u8 *pll_prediv, u8 *pll_mult,
> +					  u8 *sysdiv, u8 *mipi_div)
> +{
> +	unsigned long _rate = rate * OV5640_MIPI_DIV;
> +
> +	_rate = ov5640_calc_sys_clk(sensor, _rate, pll_prediv, pll_mult,
> +				    sysdiv);
> +	*mipi_div = OV5640_MIPI_DIV;
> +
> +	return _rate / *mipi_div;
> +}
> +
> +static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor, unsigned long rate)
> +{
> +	u8 prediv, mult, sysdiv, mipi_div;
> +	int ret;
> +
> +	ov5640_calc_mipi_clk(sensor, rate, &prediv, &mult, &sysdiv, &mipi_div);
> +
> +	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
> +			     0xff, sysdiv << 4 | (mipi_div - 1));
> +	if (ret)
> +		return ret;
> +
> +	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2, 0xff, mult);
> +	if (ret)
> +		return ret;
> +
> +	return ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3, 0xf, prediv);
> +}
> +
> +static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor,
> +				      unsigned long rate,
> +				      u8 *pll_prediv, u8 *pll_mult, u8 *sysdiv,
> +				      u8 *pll_rdiv, u8 *bit_div, u8 *pclk_div)
> +{
> +	unsigned long _rate = rate * OV5640_PLL_ROOT_DIV * OV5640_BIT_DIV *
> +				OV5640_PCLK_ROOT_DIV;
> +
> +	_rate = ov5640_calc_sys_clk(sensor, _rate, pll_prediv, pll_mult,
> +				    sysdiv);
> +	*pll_rdiv = OV5640_PLL_ROOT_DIV;
> +	*bit_div = OV5640_BIT_DIV;
> +	*pclk_div = OV5640_PCLK_ROOT_DIV;
> +
> +	return _rate / *pll_rdiv / *bit_div / *pclk_div;
> +}
> +
> +static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate)
> +{
> +	u8 prediv, mult, sysdiv, pll_rdiv, bit_div, pclk_div;
> +	int ret;
> +
> +	ov5640_calc_pclk(sensor, rate, &prediv, &mult, &sysdiv, &pll_rdiv,
> +			 &bit_div, &pclk_div);
> +
> +	if (bit_div == 2)
> +		bit_div = 8;
> +
> +	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0,
> +			     0x0f, bit_div);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * We need to set sysdiv according to the clock, and to clear
> +	 * the MIPI divider.
> +	 */
> +	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
> +			     0xff, sysdiv << 4);
> +	if (ret)
> +		return ret;
> +
> +	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2,
> +			     0xff, mult);
> +	if (ret)
> +		return ret;
> +
> +	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3,
> +			     0x1f, prediv | ((pll_rdiv - 1) << 4));
> +	if (ret)
> +		return ret;
> +
> +	return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x30,
> +			      (ilog2(pclk_div) << 4));
> +}
> +
>   /* download ov5640 settings to sensor through i2c */
>   static int ov5640_set_timings(struct ov5640_dev *sensor,
>   			      const struct ov5640_mode_info *mode)
> @@ -1637,6 +1905,8 @@ static int ov5640_set_mode(struct ov5640_dev *sensor)
>   	enum ov5640_downsize_mode dn_mode, orig_dn_mode;
>   	bool auto_gain = sensor->ctrls.auto_gain->val == 1;
>   	bool auto_exp =  sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
> +	unsigned long rate;
> +	unsigned char bpp;
>   	int ret;
>   
>   	dn_mode = mode->dn_mode;
> @@ -1655,6 +1925,23 @@ static int ov5640_set_mode(struct ov5640_dev *sensor)
>   			goto restore_auto_gain;
>   	}
>   
> +	/*
> +	 * All the formats we support have 16 bits per pixel, except for JPEG
> +	 * which is 8 bits per pixel.
> +	 */
> +	bpp = sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8 ? 8 : 16;																																						

It was a remark from me that JPEG was broken on my side without this 
JPEG exception, but after investigations and checks with oscilloscope, 
I've found that rate for JPEG must be the same than for YUV, otherwise 
JPEG framerate is twice time less than expected.
It seems that OV5640 is encoding the JPEG on the fly and lot of blanking 
is there at end of each line, so to respect target framerate, we have to 
stick to YUV rate case.
Here is what I've done on my side:
																																																																								 
																																					
  	/*
  	 * All the formats we support have 2 bytes per pixel, except for JPEG
-	 * which is 1 byte per pixel.
+	 * which is 1 byte per pixel, but JPEG requires the same rate
+	 * than YUV (horizontal lines blanking).
  	 */
-	bpp = sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8 ? 1 : 2;
-	rate = mode->vtot * mode->htot * bpp;
+	rate = mode->vtot * mode->htot * 2;
  	rate *= ov5640_framerates[sensor->current_fr];


> +	rate = mode->pixel_clock * bpp;
> +	if (sensor->ep.bus_type == V4L2_MBUS_CSI2) {
> +		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;
> +		ret = ov5640_set_dvp_pclk(sensor, rate);
> +	}
> +
> +	if (ret < 0)
> +		return 0;
> +
>   	if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
>   	    (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
>   		/*
> 

BR,
Hugues.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 12/12] ov5640: Enforce a mode change when changing the framerate
  2018-10-15 13:57   ` Hugues FRUCHET
@ 2018-10-16  7:10     ` Maxime Ripard
  2018-10-16  8:45       ` Hugues FRUCHET
  0 siblings, 1 reply; 35+ messages in thread
From: Maxime Ripard @ 2018-10-16  7:10 UTC (permalink / raw)
  To: Hugues FRUCHET
  Cc: Mauro Carvalho Chehab, Laurent Pinchart, linux-media,
	Thomas Petazzoni, Mylene Josserand, Hans Verkuil, Sakari Ailus,
	Loic Poulain, Samuel Bobrowicz, Steve Longerbeam, Daniel Mack,
	Jacopo Mondi

Hi Hugues,

On Mon, Oct 15, 2018 at 01:57:40PM +0000, Hugues FRUCHET wrote:
> This is already fixed in media tree:
> 0929983e49c81c1d413702cd9b83bb06c4a2555c media: ov5640: fix framerate update

My bad then, I missed it, thanks!
Maxime

-- 
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 12/12] ov5640: Enforce a mode change when changing the framerate
  2018-10-16  7:10     ` Maxime Ripard
@ 2018-10-16  8:45       ` Hugues FRUCHET
  0 siblings, 0 replies; 35+ messages in thread
From: Hugues FRUCHET @ 2018-10-16  8:45 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Mauro Carvalho Chehab, Laurent Pinchart, linux-media,
	Thomas Petazzoni, Mylene Josserand, Hans Verkuil, Sakari Ailus,
	Loic Poulain, Samuel Bobrowicz, Steve Longerbeam, Daniel Mack,
	Jacopo Mondi

You're welcome ;)

On 10/16/2018 09:10 AM, Maxime Ripard wrote:
> Hi Hugues,
> 
> On Mon, Oct 15, 2018 at 01:57:40PM +0000, Hugues FRUCHET wrote:
>> This is already fixed in media tree:
>> 0929983e49c81c1d413702cd9b83bb06c4a2555c media: ov5640: fix framerate update
> 
> My bad then, I missed it, thanks!
> Maxime
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 00/12] media: ov5640: Misc cleanup and improvements
  2018-10-11  9:20 [PATCH v4 00/12] media: ov5640: Misc cleanup and improvements Maxime Ripard
                   ` (11 preceding siblings ...)
  2018-10-11  9:21 ` [PATCH v4 12/12] ov5640: Enforce a mode change when changing the framerate Maxime Ripard
@ 2018-10-16 15:57 ` jacopo mondi
  12 siblings, 0 replies; 35+ messages in thread
From: jacopo mondi @ 2018-10-16 15:57 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Mauro Carvalho Chehab, Laurent Pinchart, linux-media,
	Thomas Petazzoni, Mylene Josserand, Hans Verkuil, Sakari Ailus,
	Hugues Fruchet, Loic Poulain, Samuel Bobrowicz, Steve Longerbeam,
	Daniel Mack

[-- Attachment #1: Type: text/plain, Size: 2800 bytes --]

Hello Maxime,

On Thu, Oct 11, 2018 at 11:20:55AM +0200, Maxime Ripard wrote:
> Hi,
>
> Here is a "small" series that mostly cleans up the ov5640 driver code,
> slowly getting rid of the big data array for more understandable code
> (hopefully).
>
> The biggest addition would be the clock rate computation at runtime,
> instead of relying on those arrays to setup the clock tree
> properly. As a side effect, it fixes the framerate that was off by
> around 10% on the smaller resolutions, and we now support 60fps.
>
> This also introduces a bunch of new features.
>
> Let me know what you think,

I'm sorry to report this breaks CSI-2 capture on my i.MX6 testing
platform.

I have been testing the whole afternoon with different configurations,
but I have not been able yet to find out the root of the problem.

In the meantime, I have some comments on the patches, and I'll reply
to them singularly.

Thanks
   j

> Maxime
>
> Changes from v3:
>   - Rebased on current Sakari tree
>   - Fixed an error when changing only the framerate
>
> Changes from v2:
>   - Rebased on latest Sakari PR
>   - Fixed the issues reported by Hugues: improper FPS returned for
>     formats, improper rounding of the FPS, some with his suggestions,
>     some by simplifying the logic.
>   - Expanded the clock tree comments based on the feedback from Samuel
>     Bobrowicz and Loic Poulain
>   - Merged some of the changes made by Samuel Bobrowicz to fix the
>     MIPI rate computation, fix the call sites of the
>     ov5640_set_timings function, the auto-exposure calculation call,
>     etc.
>   - Split the patches into smaller ones in order to make it more
>     readable (hopefully)
>
> Changes from v1:
>   - Integrated Hugues' suggestions to fix v4l2-compliance
>   - Fixed the bus width with JPEG
>   - Dropped the clock rate calculation loops for something simpler as
>     suggested by Sakari
>   - Cache the exposure value instead of using the control value
>   - Rebased on top of 4.17
>
> Maxime Ripard (12):
>   media: ov5640: Adjust the clock based on the expected rate
>   media: ov5640: Remove the clocks registers initialization
>   media: ov5640: Remove redundant defines
>   media: ov5640: Remove redundant register setup
>   media: ov5640: Compute the clock rate at runtime
>   media: ov5640: Remove pixel clock rates
>   media: ov5640: Enhance FPS handling
>   media: ov5640: Make the return rate type more explicit
>   media: ov5640: Make the FPS clamping / rounding more extendable
>   media: ov5640: Add 60 fps support
>   media: ov5640: Remove duplicate auto-exposure setup
>   ov5640: Enforce a mode change when changing the framerate
>
>  drivers/media/i2c/ov5640.c | 679 ++++++++++++++++++++-----------------
>  1 file changed, 374 insertions(+), 305 deletions(-)
>
> --
> 2.19.1
>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 01/12] media: ov5640: Adjust the clock based on the expected rate
  2018-10-11  9:20 ` [PATCH v4 01/12] media: ov5640: Adjust the clock based on the expected rate Maxime Ripard
  2018-10-15 14:05   ` Hugues FRUCHET
@ 2018-10-16 16:54   ` jacopo mondi
  2018-10-17 17:54     ` Sam Bobrowicz
  2018-10-18  9:29     ` Maxime Ripard
  1 sibling, 2 replies; 35+ messages in thread
From: jacopo mondi @ 2018-10-16 16:54 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Mauro Carvalho Chehab, Laurent Pinchart, linux-media,
	Thomas Petazzoni, Mylene Josserand, Hans Verkuil, Sakari Ailus,
	Hugues Fruchet, Loic Poulain, Samuel Bobrowicz, Steve Longerbeam,
	Daniel Mack

[-- Attachment #1: Type: text/plain, Size: 19298 bytes --]

Hello Maxime,
   a few comments I have collected while testing the series.

Please see below.

On Thu, Oct 11, 2018 at 11:20:56AM +0200, Maxime Ripard wrote:
> The clock structure for the PCLK is quite obscure in the documentation, and
> was hardcoded through the bytes array of each and every mode.
>
> This is troublesome, since we cannot adjust it at runtime based on other
> parameters (such as the number of bytes per pixel), and we can't support
> either framerates that have not been used by the various vendors, since we
> don't have the needed initialization sequence.
>
> We can however understand how the clock tree works, and then implement some
> functions to derive the various parameters from a given rate. And now that
> those parameters are calculated at runtime, we can remove them from the
> initialization sequence.
>
> The modes also gained a new parameter which is the clock that they are
> running at, from the register writes they were doing, so for now the switch
> to the new algorithm should be transparent.
>
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> ---
>  drivers/media/i2c/ov5640.c | 289 ++++++++++++++++++++++++++++++++++++-
>  1 file changed, 288 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> index 30b15e91d8be..88fb16341466 100644
> --- a/drivers/media/i2c/ov5640.c
> +++ b/drivers/media/i2c/ov5640.c
> @@ -175,6 +175,7 @@ struct ov5640_mode_info {
>  	u32 htot;
>  	u32 vact;
>  	u32 vtot;
> +	u32 pixel_clock;
>  	const struct reg_value *reg_data;
>  	u32 reg_data_size;
>  };
> @@ -700,6 +701,7 @@ static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = {
>  /* power-on sensor init reg table */
>  static const struct ov5640_mode_info ov5640_mode_init_data = {
>  	0, SUBSAMPLING, 640, 1896, 480, 984,
> +	56000000,
>  	ov5640_init_setting_30fps_VGA,
>  	ARRAY_SIZE(ov5640_init_setting_30fps_VGA),
>  };
> @@ -709,74 +711,91 @@ ov5640_mode_data[OV5640_NUM_FRAMERATES][OV5640_NUM_MODES] = {
>  	{
>  		{OV5640_MODE_QCIF_176_144, SUBSAMPLING,
>  		 176, 1896, 144, 984,
> +		 28000000,
>  		 ov5640_setting_15fps_QCIF_176_144,
>  		 ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)},
>  		{OV5640_MODE_QVGA_320_240, SUBSAMPLING,
>  		 320, 1896, 240, 984,
> +		 28000000,
>  		 ov5640_setting_15fps_QVGA_320_240,
>  		 ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)},
>  		{OV5640_MODE_VGA_640_480, SUBSAMPLING,
>  		 640, 1896, 480, 1080,
> +		 28000000,
>  		 ov5640_setting_15fps_VGA_640_480,
>  		 ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)},
>  		{OV5640_MODE_NTSC_720_480, SUBSAMPLING,
>  		 720, 1896, 480, 984,
> +		 28000000,
>  		 ov5640_setting_15fps_NTSC_720_480,
>  		 ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)},
>  		{OV5640_MODE_PAL_720_576, SUBSAMPLING,
>  		 720, 1896, 576, 984,
> +		 28000000,
>  		 ov5640_setting_15fps_PAL_720_576,
>  		 ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)},
>  		{OV5640_MODE_XGA_1024_768, SUBSAMPLING,
>  		 1024, 1896, 768, 1080,
> +		 28000000,
>  		 ov5640_setting_15fps_XGA_1024_768,
>  		 ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)},
>  		{OV5640_MODE_720P_1280_720, SUBSAMPLING,
>  		 1280, 1892, 720, 740,
> +		 21000000,
>  		 ov5640_setting_15fps_720P_1280_720,
>  		 ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)},
>  		{OV5640_MODE_1080P_1920_1080, SCALING,
>  		 1920, 2500, 1080, 1120,
> +		 42000000,
>  		 ov5640_setting_15fps_1080P_1920_1080,
>  		 ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)},
>  		{OV5640_MODE_QSXGA_2592_1944, SCALING,
>  		 2592, 2844, 1944, 1968,
> +		 84000000,
>  		 ov5640_setting_15fps_QSXGA_2592_1944,
>  		 ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)},
>  	}, {
>  		{OV5640_MODE_QCIF_176_144, SUBSAMPLING,
>  		 176, 1896, 144, 984,
> +		 56000000,
>  		 ov5640_setting_30fps_QCIF_176_144,
>  		 ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)},
>  		{OV5640_MODE_QVGA_320_240, SUBSAMPLING,
>  		 320, 1896, 240, 984,
> +		 56000000,
>  		 ov5640_setting_30fps_QVGA_320_240,
>  		 ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)},
>  		{OV5640_MODE_VGA_640_480, SUBSAMPLING,
>  		 640, 1896, 480, 1080,
> +		 56000000,
>  		 ov5640_setting_30fps_VGA_640_480,
>  		 ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)},
>  		{OV5640_MODE_NTSC_720_480, SUBSAMPLING,
>  		 720, 1896, 480, 984,
> +		 56000000,
>  		 ov5640_setting_30fps_NTSC_720_480,
>  		 ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)},
>  		{OV5640_MODE_PAL_720_576, SUBSAMPLING,
>  		 720, 1896, 576, 984,
> +		 56000000,
>  		 ov5640_setting_30fps_PAL_720_576,
>  		 ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)},
>  		{OV5640_MODE_XGA_1024_768, SUBSAMPLING,
>  		 1024, 1896, 768, 1080,
> +		 56000000,
>  		 ov5640_setting_30fps_XGA_1024_768,
>  		 ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)},
>  		{OV5640_MODE_720P_1280_720, SUBSAMPLING,
>  		 1280, 1892, 720, 740,
> +		 42000000,
>  		 ov5640_setting_30fps_720P_1280_720,
>  		 ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)},
>  		{OV5640_MODE_1080P_1920_1080, SCALING,
>  		 1920, 2500, 1080, 1120,
> +		 84000000,
>  		 ov5640_setting_30fps_1080P_1920_1080,
>  		 ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)},
> -		{OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, 0, 0, NULL, 0},
> +		{OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, 0, 0, 0, NULL, 0},
>  	},
>  };
>
> @@ -909,6 +928,255 @@ static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg,
>  	return ov5640_write_reg(sensor, reg, val);
>  }
>
> +/*
> + * After trying the various combinations, reading various
> + * documentations spreaded around the net, and from the various
> + * feedback, the clock tree is probably as follows:
> + *
> + *   +--------------+
> + *   |  Ext. Clock  |
> + *   +-+------------+
> + *     |  +----------+
> + *     +->|   PLL1   | - reg 0x3036, for the multiplier
> + *        +-+--------+ - reg 0x3037, bits 0-3 for the pre-divider
> + *          |  +--------------+
> + *          +->| System Clock |  - reg 0x3035, bits 4-7
> + *             +-+------------+
> + *               |  +--------------+
> + *               +->| MIPI Divider | - reg 0x3035, bits 0-3
> + *               |  +-+------------+
> + *               |    +----------------> MIPI SCLK
> + *               |  +--------------+
> + *               +->| PLL Root Div | - reg 0x3037, bit 4
> + *                  +-+------------+
> + *                    |  +---------+
> + *                    +->| Bit Div | - reg 0x3035, bits 0-3
> + *                       +-+-------+
> + *                         |  +-------------+
> + *                         +->| SCLK Div    | - reg 0x3108, bits 0-1
> + *                         |  +-+-----------+
> + *                         |    +---------------> SCLK
> + *                         |  +-------------+
> + *                         +->| SCLK 2X Div | - reg 0x3108, bits 2-3
> + *                         |  +-+-----------+
> + *                         |    +---------------> SCLK 2X
> + *                         |  +-------------+
> + *                         +->| PCLK Div    | - reg 0x3108, bits 4-5
> + *                            +-+-----------+
> + *                              +---------------> PCLK
> + *
> + * This is deviating from the datasheet at least for the register
> + * 0x3108, since it's said here that the PCLK would be clocked from
> + * the PLL.
> + *
> + * There seems to be also (unverified) constraints:
> + *  - the PLL pre-divider output rate should be in the 4-27MHz range
> + *  - the PLL multiplier output rate should be in the 500-1000MHz range
> + *  - PCLK >= SCLK * 2 in YUV, >= SCLK in Raw or JPEG
> + *  - MIPI SCLK = (bpp / lanes) / PCLK

This last line probably wrong...

> + *
> + * In the two latter cases, these constraints are met since our
> + * factors are hardcoded. If we were to change that, we would need to
> + * take this into account. The only varying parts are the PLL
> + * multiplier and the system clock divider, which are shared between
> + * all these clocks so won't cause any issue.
> + */
> +
> +/*
> + * This is supposed to be ranging from 1 to 8, but the value is always
> + * set to 3 in the vendor kernels.
> + */
> +#define OV5640_PLL_PREDIV	3
> +
> +#define OV5640_PLL_MULT_MIN	4
> +#define OV5640_PLL_MULT_MAX	252
> +
> +/*
> + * This is supposed to be ranging from 1 to 16, but the value is
> + * always set to either 1 or 2 in the vendor kernels.
> + */
> +#define OV5640_SYSDIV_MIN	1
> +#define OV5640_SYSDIV_MAX	2
> +
> +/*
> + * This is supposed to be ranging from 1 to 16, but the value is always
> + * set to 2 in the vendor kernels.
> + */
> +#define OV5640_MIPI_DIV		2
> +
> +/*
> + * This is supposed to be ranging from 1 to 2, but the value is always
> + * set to 2 in the vendor kernels.
> + */
> +#define OV5640_PLL_ROOT_DIV	2
> +
> +/*
> + * This is supposed to be either 1, 2 or 2.5, but the value is always
> + * set to 2 in the vendor kernels.
> + */
> +#define OV5640_BIT_DIV		2
> +
> +/*
> + * This is supposed to be ranging from 1 to 8, but the value is always
> + * set to 2 in the vendor kernels.
> + */
> +#define OV5640_SCLK_ROOT_DIV	2
> +
> +/*
> + * This is hardcoded so that the consistency is maintained between SCLK and
> + * SCLK 2x.
> + */
> +#define OV5640_SCLK2X_ROOT_DIV (OV5640_SCLK_ROOT_DIV / 2)
> +
> +/*
> + * This is supposed to be ranging from 1 to 8, but the value is always
> + * set to 1 in the vendor kernels.
> + */
> +#define OV5640_PCLK_ROOT_DIV	1
> +
> +static unsigned long ov5640_compute_sys_clk(struct ov5640_dev *sensor,
> +					    u8 pll_prediv, u8 pll_mult,
> +					    u8 sysdiv)
> +{
> +	unsigned long rate = clk_get_rate(sensor->xclk);

The clock rate is stored in sensor->xclk at probe time, no need to
query it every iteration.

> +
> +	return rate / pll_prediv * pll_mult / sysdiv;
> +}
> +
> +static unsigned long ov5640_calc_sys_clk(struct ov5640_dev *sensor,
> +					 unsigned long rate,
> +					 u8 *pll_prediv, u8 *pll_mult,
> +					 u8 *sysdiv)
> +{
> +	unsigned long best = ~0;
> +	u8 best_sysdiv = 1, best_mult = 1;
> +	u8 _sysdiv, _pll_mult;
> +
> +	for (_sysdiv = OV5640_SYSDIV_MIN;
> +	     _sysdiv <= OV5640_SYSDIV_MAX;
> +	     _sysdiv++) {
> +		for (_pll_mult = OV5640_PLL_MULT_MIN;
> +		     _pll_mult <= OV5640_PLL_MULT_MAX;
> +		     _pll_mult++) {
> +			unsigned long _rate;
> +
> +			/*
> +			 * The PLL multiplier cannot be odd if above
> +			 * 127.
> +			 */
> +			if (_pll_mult > 127 && (_pll_mult % 2))
> +				continue;
> +
> +			_rate = ov5640_compute_sys_clk(sensor,
> +						       OV5640_PLL_PREDIV,
> +						       _pll_mult, _sysdiv);

I'm under the impression a system clock slower than the requested one, even
if more accurate is not good.

I'm still working on understanding how all CSI-2 related timing
parameters play together, but since the system clock is calculated
from the pixel clock (which comes from the frame dimensions, bpp, and
rate), and it is then used to calculate the MIPI BIT clock frequency,
I think it would be better to be a little faster than a bit slower,
otherwise the serial lane clock wouldn't be fast enough to output
frames generated by the sensor core (or maybe it would just decrease
the frame rate and that's it, but I don't think it is just this).

What do you think of adding the following here:

                if (_rate < rate)
                        continue


> +			if (abs(rate - _rate) < abs(rate - best)) {
> +				best = _rate;
> +				best_sysdiv = _sysdiv;
> +				best_mult = _pll_mult;
> +			}
> +
> +			if (_rate == rate)
> +				goto out;
> +		}
> +	}
> +
> +out:
> +	*sysdiv = best_sysdiv;
> +	*pll_prediv = OV5640_PLL_PREDIV;
> +	*pll_mult = best_mult;
> +	return best;
> +}

These function gets called at s_stream time, and cycle for a while,
and I'm under the impression the MIPI state machine doesn't like
delays too much, as I see timeouts on the receiver side.

I have tried to move this function at set_fmt() time, every time a new
mode is selected, sysdiv, pll_prediv and pll_mult gets recalculated
(and stored in the ov5640_dev structure). I now have other timeouts on
missing EOF, but not anymore at startup time it seems.


On a general testing note: I have tried hardcoding all PLL
configuration paramters to their values as specified in the
initialization blob you have removed, and still, I have EOF timeouts
on the CSI-2 bus. There is something we're still missing on the MIPI
clock generation part, even if the documentation I have matches your
understandings..

> +
> +static unsigned long ov5640_calc_mipi_clk(struct ov5640_dev *sensor,
> +					  unsigned long rate,
> +					  u8 *pll_prediv, u8 *pll_mult,
> +					  u8 *sysdiv, u8 *mipi_div)
> +{
> +	unsigned long _rate = rate * OV5640_MIPI_DIV;
> +
> +	_rate = ov5640_calc_sys_clk(sensor, _rate, pll_prediv, pll_mult,
> +				    sysdiv);
> +	*mipi_div = OV5640_MIPI_DIV;
> +
> +	return _rate / *mipi_div;
> +}
> +
> +static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor, unsigned long rate)
> +{
> +	u8 prediv, mult, sysdiv, mipi_div;
> +	int ret;
> +
> +	ov5640_calc_mipi_clk(sensor, rate, &prediv, &mult, &sysdiv, &mipi_div);

Confusingly, register PLL_CTRL0 (0x3034) controls the "MIPI bit mode",
which I'm not a 100% sure what represents, and it is not written here
in the MIPI clock configuration function.

As in the clock diagram I have it reads as:
 0x3034 MIPI BIT MODE
        0x08 = / 2
        0x0A = / 2.5

I suspect it is used to maintain a correct relationship between the
pixel clock (in samples/second) and the total bandwidth in bytes and
thus it has to be written here.

Reading the clock tree in the opposite direction I see:

 ------------------------------
|pixel clk (htot * vtot * rate)|
 -----------------------------
            |   --------------------------------------------
            |->| P_DIV: 2lanes ? MIPI_DIV : 2 * MIPI_DIV   |
                --------------------------------------------
                                   |   --------
                                   |->|PCLK_DIV|
                                       --------
                                          |   ------------
                                          |->|   BIT_DIV  |
                                              ------------
                                                    |   ----------
                                                    |->| PLL_R DIV|
                                                        ---------
                                                            |
                                                            |--------->SYSCLK

And I then suspect that:
   P_DIV * PCLK_DIV * BIT_DIV = bpp

So that, if we hardcode PLL_R div to 1, the system clock is
actually the total bandwidth in bytes.

This would be then be:
  bandwidth = pixel_clk * rate * bpp
            = pixel_clk * rate * (MIPI_DIV * PCLK_DIV * BIT_DIV)
            = SYSCLK

and then depending on the current format's bbp:
  PCLK_DIV = bpp / (BIT_DIV * num_lanes)

This matches the other part of the MIPI clock tree, the MIPI_CLK
generation part, where the SYSCLK is divided again by MIPI_DIV and
then by 2 to take into account the CSI-2 DDR.

  MIPI BIT CLK = bandwidth / num_lanes / 2
               = SYS_CLK / MIPI_DIV / 2

While this works pretty well in my head, it does not on the sensor :/

So I might have mis-intrpreted something, or we're missing some part
of the clock tree that impacts the CSI-2 bus.

I recall Sam had a pretty well understanding of this part.
I wonder if he has comments... :)

Thanks
   j
> +
> +	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
> +			     0xff, sysdiv << 4 | (mipi_div - 1));
> +	if (ret)
> +		return ret;
> +
> +	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2, 0xff, mult);
> +	if (ret)
> +		return ret;
> +
> +	return ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3, 0xf, prediv);
> +}
> +
> +static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor,
> +				      unsigned long rate,
> +				      u8 *pll_prediv, u8 *pll_mult, u8 *sysdiv,
> +				      u8 *pll_rdiv, u8 *bit_div, u8 *pclk_div)
> +{
> +	unsigned long _rate = rate * OV5640_PLL_ROOT_DIV * OV5640_BIT_DIV *
> +				OV5640_PCLK_ROOT_DIV;
> +
> +	_rate = ov5640_calc_sys_clk(sensor, _rate, pll_prediv, pll_mult,
> +				    sysdiv);
> +	*pll_rdiv = OV5640_PLL_ROOT_DIV;
> +	*bit_div = OV5640_BIT_DIV;
> +	*pclk_div = OV5640_PCLK_ROOT_DIV;
> +
> +	return _rate / *pll_rdiv / *bit_div / *pclk_div;
> +}
> +
> +static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate)
> +{
> +	u8 prediv, mult, sysdiv, pll_rdiv, bit_div, pclk_div;
> +	int ret;
> +
> +	ov5640_calc_pclk(sensor, rate, &prediv, &mult, &sysdiv, &pll_rdiv,
> +			 &bit_div, &pclk_div);
> +
> +	if (bit_div == 2)
> +		bit_div = 8;
> +
> +	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0,
> +			     0x0f, bit_div);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * We need to set sysdiv according to the clock, and to clear
> +	 * the MIPI divider.
> +	 */
> +	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
> +			     0xff, sysdiv << 4);
> +	if (ret)
> +		return ret;
> +
> +	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2,
> +			     0xff, mult);
> +	if (ret)
> +		return ret;
> +
> +	ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3,
> +			     0x1f, prediv | ((pll_rdiv - 1) << 4));
> +	if (ret)
> +		return ret;
> +
> +	return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x30,
> +			      (ilog2(pclk_div) << 4));
> +}
> +
>  /* download ov5640 settings to sensor through i2c */
>  static int ov5640_set_timings(struct ov5640_dev *sensor,
>  			      const struct ov5640_mode_info *mode)
> @@ -1637,6 +1905,8 @@ static int ov5640_set_mode(struct ov5640_dev *sensor)
>  	enum ov5640_downsize_mode dn_mode, orig_dn_mode;
>  	bool auto_gain = sensor->ctrls.auto_gain->val == 1;
>  	bool auto_exp =  sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
> +	unsigned long rate;
> +	unsigned char bpp;
>  	int ret;
>
>  	dn_mode = mode->dn_mode;
> @@ -1655,6 +1925,23 @@ static int ov5640_set_mode(struct ov5640_dev *sensor)
>  			goto restore_auto_gain;
>  	}
>
> +	/*
> +	 * All the formats we support have 16 bits per pixel, except for JPEG
> +	 * which is 8 bits per pixel.
> +	 */
> +	bpp = sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8 ? 8 : 16;
> +	rate = mode->pixel_clock * bpp;
> +	if (sensor->ep.bus_type == V4L2_MBUS_CSI2) {
> +		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;
> +		ret = ov5640_set_dvp_pclk(sensor, rate);
> +	}
> +
> +	if (ret < 0)
> +		return 0;
> +
>  	if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
>  	    (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
>  		/*
> --
> 2.19.1
>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 01/12] media: ov5640: Adjust the clock based on the expected rate
  2018-10-16 16:54   ` jacopo mondi
@ 2018-10-17 17:54     ` Sam Bobrowicz
  2018-10-17 19:51       ` jacopo mondi
  2018-10-18  9:29     ` Maxime Ripard
  1 sibling, 1 reply; 35+ messages in thread
From: Sam Bobrowicz @ 2018-10-17 17:54 UTC (permalink / raw)
  To: jacopo mondi
  Cc: Maxime Ripard, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media, Thomas Petazzoni, Mylene Josserand, Hans Verkuil,
	Sakari Ailus, Hugues Fruchet, Loic Poulain, Steve Longerbeam,
	Daniel Mack

Hello Maxime and Jacopo (and other ov5640-ers),

I just submitted my version of this patch to the mailing list as RFC.
It is working on my MIPI platform. Please try it if you have time.
Hopefully we can merge these two into a single patch that doesn't
break any platforms.

Thanks,
Sam

Additional notes below.

On Tue, Oct 16, 2018 at 9:54 AM jacopo mondi <jacopo@jmondi.org> wrote:
>
> Hello Maxime,
>    a few comments I have collected while testing the series.
>
> Please see below.
>
> On Thu, Oct 11, 2018 at 11:20:56AM +0200, Maxime Ripard wrote:
> > The clock structure for the PCLK is quite obscure in the documentation, and
> > was hardcoded through the bytes array of each and every mode.
> >
> > This is troublesome, since we cannot adjust it at runtime based on other
> > parameters (such as the number of bytes per pixel), and we can't support
> > either framerates that have not been used by the various vendors, since we
> > don't have the needed initialization sequence.
> >
> > We can however understand how the clock tree works, and then implement some
> > functions to derive the various parameters from a given rate. And now that
> > those parameters are calculated at runtime, we can remove them from the
> > initialization sequence.
> >
> > The modes also gained a new parameter which is the clock that they are
> > running at, from the register writes they were doing, so for now the switch
> > to the new algorithm should be transparent.
> >
> > Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> > ---
> >  drivers/media/i2c/ov5640.c | 289 ++++++++++++++++++++++++++++++++++++-
> >  1 file changed, 288 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> > index 30b15e91d8be..88fb16341466 100644
> > --- a/drivers/media/i2c/ov5640.c
> > +++ b/drivers/media/i2c/ov5640.c
> > @@ -175,6 +175,7 @@ struct ov5640_mode_info {
> >       u32 htot;
> >       u32 vact;
> >       u32 vtot;
> > +     u32 pixel_clock;
> >       const struct reg_value *reg_data;
> >       u32 reg_data_size;
> >  };
> > @@ -700,6 +701,7 @@ static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = {
> >  /* power-on sensor init reg table */
> >  static const struct ov5640_mode_info ov5640_mode_init_data = {
> >       0, SUBSAMPLING, 640, 1896, 480, 984,
> > +     56000000,
> >       ov5640_init_setting_30fps_VGA,
> >       ARRAY_SIZE(ov5640_init_setting_30fps_VGA),
> >  };
> > @@ -709,74 +711,91 @@ ov5640_mode_data[OV5640_NUM_FRAMERATES][OV5640_NUM_MODES] = {
> >       {
> >               {OV5640_MODE_QCIF_176_144, SUBSAMPLING,
> >                176, 1896, 144, 984,
> > +              28000000,
> >                ov5640_setting_15fps_QCIF_176_144,
> >                ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)},
> >               {OV5640_MODE_QVGA_320_240, SUBSAMPLING,
> >                320, 1896, 240, 984,
> > +              28000000,
> >                ov5640_setting_15fps_QVGA_320_240,
> >                ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)},
> >               {OV5640_MODE_VGA_640_480, SUBSAMPLING,
> >                640, 1896, 480, 1080,
> > +              28000000,
> >                ov5640_setting_15fps_VGA_640_480,
> >                ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)},
> >               {OV5640_MODE_NTSC_720_480, SUBSAMPLING,
> >                720, 1896, 480, 984,
> > +              28000000,
> >                ov5640_setting_15fps_NTSC_720_480,
> >                ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)},
> >               {OV5640_MODE_PAL_720_576, SUBSAMPLING,
> >                720, 1896, 576, 984,
> > +              28000000,
> >                ov5640_setting_15fps_PAL_720_576,
> >                ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)},
> >               {OV5640_MODE_XGA_1024_768, SUBSAMPLING,
> >                1024, 1896, 768, 1080,
> > +              28000000,
> >                ov5640_setting_15fps_XGA_1024_768,
> >                ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)},
> >               {OV5640_MODE_720P_1280_720, SUBSAMPLING,
> >                1280, 1892, 720, 740,
> > +              21000000,
> >                ov5640_setting_15fps_720P_1280_720,
> >                ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)},
> >               {OV5640_MODE_1080P_1920_1080, SCALING,
> >                1920, 2500, 1080, 1120,
> > +              42000000,
> >                ov5640_setting_15fps_1080P_1920_1080,
> >                ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)},
> >               {OV5640_MODE_QSXGA_2592_1944, SCALING,
> >                2592, 2844, 1944, 1968,
> > +              84000000,
> >                ov5640_setting_15fps_QSXGA_2592_1944,
> >                ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)},
> >       }, {
> >               {OV5640_MODE_QCIF_176_144, SUBSAMPLING,
> >                176, 1896, 144, 984,
> > +              56000000,
> >                ov5640_setting_30fps_QCIF_176_144,
> >                ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)},
> >               {OV5640_MODE_QVGA_320_240, SUBSAMPLING,
> >                320, 1896, 240, 984,
> > +              56000000,
> >                ov5640_setting_30fps_QVGA_320_240,
> >                ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)},
> >               {OV5640_MODE_VGA_640_480, SUBSAMPLING,
> >                640, 1896, 480, 1080,
> > +              56000000,
> >                ov5640_setting_30fps_VGA_640_480,
> >                ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)},
> >               {OV5640_MODE_NTSC_720_480, SUBSAMPLING,
> >                720, 1896, 480, 984,
> > +              56000000,
> >                ov5640_setting_30fps_NTSC_720_480,
> >                ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)},
> >               {OV5640_MODE_PAL_720_576, SUBSAMPLING,
> >                720, 1896, 576, 984,
> > +              56000000,
> >                ov5640_setting_30fps_PAL_720_576,
> >                ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)},
> >               {OV5640_MODE_XGA_1024_768, SUBSAMPLING,
> >                1024, 1896, 768, 1080,
> > +              56000000,
> >                ov5640_setting_30fps_XGA_1024_768,
> >                ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)},
> >               {OV5640_MODE_720P_1280_720, SUBSAMPLING,
> >                1280, 1892, 720, 740,
> > +              42000000,
> >                ov5640_setting_30fps_720P_1280_720,
> >                ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)},
> >               {OV5640_MODE_1080P_1920_1080, SCALING,
> >                1920, 2500, 1080, 1120,
> > +              84000000,
> >                ov5640_setting_30fps_1080P_1920_1080,
> >                ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)},
> > -             {OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, 0, 0, NULL, 0},
> > +             {OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, 0, 0, 0, NULL, 0},
> >       },
> >  };
> >
> > @@ -909,6 +928,255 @@ static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg,
> >       return ov5640_write_reg(sensor, reg, val);
> >  }
> >
> > +/*
> > + * After trying the various combinations, reading various
> > + * documentations spreaded around the net, and from the various
> > + * feedback, the clock tree is probably as follows:
> > + *
> > + *   +--------------+
> > + *   |  Ext. Clock  |
> > + *   +-+------------+
> > + *     |  +----------+
> > + *     +->|   PLL1   | - reg 0x3036, for the multiplier
> > + *        +-+--------+ - reg 0x3037, bits 0-3 for the pre-divider
> > + *          |  +--------------+
> > + *          +->| System Clock |  - reg 0x3035, bits 4-7
> > + *             +-+------------+
> > + *               |  +--------------+
> > + *               +->| MIPI Divider | - reg 0x3035, bits 0-3
> > + *               |  +-+------------+
> > + *               |    +----------------> MIPI SCLK
> > + *               |  +--------------+
> > + *               +->| PLL Root Div | - reg 0x3037, bit 4
> > + *                  +-+------------+
> > + *                    |  +---------+
> > + *                    +->| Bit Div | - reg 0x3035, bits 0-3
> > + *                       +-+-------+
> > + *                         |  +-------------+
> > + *                         +->| SCLK Div    | - reg 0x3108, bits 0-1
> > + *                         |  +-+-----------+
> > + *                         |    +---------------> SCLK
> > + *                         |  +-------------+
> > + *                         +->| SCLK 2X Div | - reg 0x3108, bits 2-3
> > + *                         |  +-+-----------+
> > + *                         |    +---------------> SCLK 2X
> > + *                         |  +-------------+
> > + *                         +->| PCLK Div    | - reg 0x3108, bits 4-5
> > + *                            +-+-----------+
> > + *                              +---------------> PCLK
> > + *
> > + * This is deviating from the datasheet at least for the register
> > + * 0x3108, since it's said here that the PCLK would be clocked from
> > + * the PLL.
> > + *
> > + * There seems to be also (unverified) constraints:
> > + *  - the PLL pre-divider output rate should be in the 4-27MHz range
> > + *  - the PLL multiplier output rate should be in the 500-1000MHz range
> > + *  - PCLK >= SCLK * 2 in YUV, >= SCLK in Raw or JPEG
> > + *  - MIPI SCLK = (bpp / lanes) / PCLK
>
> This last line probably wrong...
>
> > + *
> > + * In the two latter cases, these constraints are met since our
> > + * factors are hardcoded. If we were to change that, we would need to
> > + * take this into account. The only varying parts are the PLL
> > + * multiplier and the system clock divider, which are shared between
> > + * all these clocks so won't cause any issue.
> > + */
> > +
> > +/*
> > + * This is supposed to be ranging from 1 to 8, but the value is always
> > + * set to 3 in the vendor kernels.
> > + */
> > +#define OV5640_PLL_PREDIV    3
> > +
> > +#define OV5640_PLL_MULT_MIN  4
> > +#define OV5640_PLL_MULT_MAX  252
> > +
> > +/*
> > + * This is supposed to be ranging from 1 to 16, but the value is
> > + * always set to either 1 or 2 in the vendor kernels.
> > + */
> > +#define OV5640_SYSDIV_MIN    1
> > +#define OV5640_SYSDIV_MAX    2
> > +
> > +/*
> > + * This is supposed to be ranging from 1 to 16, but the value is always
> > + * set to 2 in the vendor kernels.
> > + */
> > +#define OV5640_MIPI_DIV              2
> > +
> > +/*
> > + * This is supposed to be ranging from 1 to 2, but the value is always
> > + * set to 2 in the vendor kernels.
> > + */
> > +#define OV5640_PLL_ROOT_DIV  2
> > +
> > +/*
> > + * This is supposed to be either 1, 2 or 2.5, but the value is always
> > + * set to 2 in the vendor kernels.
> > + */
> > +#define OV5640_BIT_DIV               2
> > +
> > +/*
> > + * This is supposed to be ranging from 1 to 8, but the value is always
> > + * set to 2 in the vendor kernels.
> > + */
> > +#define OV5640_SCLK_ROOT_DIV 2
> > +
> > +/*
> > + * This is hardcoded so that the consistency is maintained between SCLK and
> > + * SCLK 2x.
> > + */
> > +#define OV5640_SCLK2X_ROOT_DIV (OV5640_SCLK_ROOT_DIV / 2)
> > +
> > +/*
> > + * This is supposed to be ranging from 1 to 8, but the value is always
> > + * set to 1 in the vendor kernels.
> > + */
> > +#define OV5640_PCLK_ROOT_DIV 1
> > +
> > +static unsigned long ov5640_compute_sys_clk(struct ov5640_dev *sensor,
> > +                                         u8 pll_prediv, u8 pll_mult,
> > +                                         u8 sysdiv)
> > +{
> > +     unsigned long rate = clk_get_rate(sensor->xclk);
>
> The clock rate is stored in sensor->xclk at probe time, no need to
> query it every iteration.
>
> > +
> > +     return rate / pll_prediv * pll_mult / sysdiv;
> > +}
> > +
> > +static unsigned long ov5640_calc_sys_clk(struct ov5640_dev *sensor,
> > +                                      unsigned long rate,
> > +                                      u8 *pll_prediv, u8 *pll_mult,
> > +                                      u8 *sysdiv)
> > +{
> > +     unsigned long best = ~0;
> > +     u8 best_sysdiv = 1, best_mult = 1;
> > +     u8 _sysdiv, _pll_mult;
> > +
> > +     for (_sysdiv = OV5640_SYSDIV_MIN;
> > +          _sysdiv <= OV5640_SYSDIV_MAX;
> > +          _sysdiv++) {
> > +             for (_pll_mult = OV5640_PLL_MULT_MIN;
> > +                  _pll_mult <= OV5640_PLL_MULT_MAX;
> > +                  _pll_mult++) {
> > +                     unsigned long _rate;
> > +
> > +                     /*
> > +                      * The PLL multiplier cannot be odd if above
> > +                      * 127.
> > +                      */
> > +                     if (_pll_mult > 127 && (_pll_mult % 2))
> > +                             continue;
> > +
> > +                     _rate = ov5640_compute_sys_clk(sensor,
> > +                                                    OV5640_PLL_PREDIV,
> > +                                                    _pll_mult, _sysdiv);
>
> I'm under the impression a system clock slower than the requested one, even
> if more accurate is not good.
>
> I'm still working on understanding how all CSI-2 related timing
> parameters play together, but since the system clock is calculated
> from the pixel clock (which comes from the frame dimensions, bpp, and
> rate), and it is then used to calculate the MIPI BIT clock frequency,
> I think it would be better to be a little faster than a bit slower,
> otherwise the serial lane clock wouldn't be fast enough to output
> frames generated by the sensor core (or maybe it would just decrease
> the frame rate and that's it, but I don't think it is just this).
>
> What do you think of adding the following here:
>
>                 if (_rate < rate)
>                         continue
>
>
Good point, I second this.
> > +                     if (abs(rate - _rate) < abs(rate - best)) {
> > +                             best = _rate;
> > +                             best_sysdiv = _sysdiv;
> > +                             best_mult = _pll_mult;
> > +                     }
> > +
> > +                     if (_rate == rate)
> > +                             goto out;
> > +             }
> > +     }
> > +
> > +out:
> > +     *sysdiv = best_sysdiv;
> > +     *pll_prediv = OV5640_PLL_PREDIV;
> > +     *pll_mult = best_mult;
> > +     return best;
> > +}
>
> These function gets called at s_stream time, and cycle for a while,
> and I'm under the impression the MIPI state machine doesn't like
> delays too much, as I see timeouts on the receiver side.
>
> I have tried to move this function at set_fmt() time, every time a new
> mode is selected, sysdiv, pll_prediv and pll_mult gets recalculated
> (and stored in the ov5640_dev structure). I now have other timeouts on
> missing EOF, but not anymore at startup time it seems.
>
I'm open to this solution, but ideally we should just cut down the
execution time. I calculate the min's and max's for the PLL values
dynamically and also include some additional breaks in my loop that
should cut execution time quite a bit (even though I check many more
sysdiv values. My algorithm still has plenty of room for further
optimization too.
>
> On a general testing note: I have tried hardcoding all PLL
> configuration paramters to their values as specified in the
> initialization blob you have removed, and still, I have EOF timeouts
> on the CSI-2 bus. There is something we're still missing on the MIPI
> clock generation part, even if the documentation I have matches your
> understandings..
>
> > +
> > +static unsigned long ov5640_calc_mipi_clk(struct ov5640_dev *sensor,
> > +                                       unsigned long rate,
> > +                                       u8 *pll_prediv, u8 *pll_mult,
> > +                                       u8 *sysdiv, u8 *mipi_div)
> > +{
> > +     unsigned long _rate = rate * OV5640_MIPI_DIV;
> > +
> > +     _rate = ov5640_calc_sys_clk(sensor, _rate, pll_prediv, pll_mult,
> > +                                 sysdiv);
> > +     *mipi_div = OV5640_MIPI_DIV;
> > +
> > +     return _rate / *mipi_div;
> > +}
> > +
> > +static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor, unsigned long rate)
> > +{
> > +     u8 prediv, mult, sysdiv, mipi_div;
> > +     int ret;
> > +
> > +     ov5640_calc_mipi_clk(sensor, rate, &prediv, &mult, &sysdiv, &mipi_div);
>
> Confusingly, register PLL_CTRL0 (0x3034) controls the "MIPI bit mode",
> which I'm not a 100% sure what represents, and it is not written here
> in the MIPI clock configuration function.
>
> As in the clock diagram I have it reads as:
>  0x3034 MIPI BIT MODE
>         0x08 = / 2
>         0x0A = / 2.5
>
> I suspect it is used to maintain a correct relationship between the
> pixel clock (in samples/second) and the total bandwidth in bytes and
> thus it has to be written here.
>
> Reading the clock tree in the opposite direction I see:
>
>  ------------------------------
> |pixel clk (htot * vtot * rate)|
>  -----------------------------
>             |   --------------------------------------------
>             |->| P_DIV: 2lanes ? MIPI_DIV : 2 * MIPI_DIV   |
>                 --------------------------------------------
>                                    |   --------
>                                    |->|PCLK_DIV|
>                                        --------
>                                           |   ------------
>                                           |->|   BIT_DIV  |
>                                               ------------
>                                                     |   ----------
>                                                     |->| PLL_R DIV|
>                                                         ---------
>                                                             |
>                                                             |--------->SYSCLK
>
> And I then suspect that:
>    P_DIV * PCLK_DIV * BIT_DIV = bpp
>
> So that, if we hardcode PLL_R div to 1, the system clock is
> actually the total bandwidth in bytes.
>
> This would be then be:
>   bandwidth = pixel_clk * rate * bpp
>             = pixel_clk * rate * (MIPI_DIV * PCLK_DIV * BIT_DIV)
>             = SYSCLK
>
> and then depending on the current format's bbp:
>   PCLK_DIV = bpp / (BIT_DIV * num_lanes)
>
> This matches the other part of the MIPI clock tree, the MIPI_CLK
> generation part, where the SYSCLK is divided again by MIPI_DIV and
> then by 2 to take into account the CSI-2 DDR.
>
>   MIPI BIT CLK = bandwidth / num_lanes / 2
>                = SYS_CLK / MIPI_DIV / 2
>
> While this works pretty well in my head, it does not on the sensor :/
>
> So I might have mis-intrpreted something, or we're missing some part
> of the clock tree that impacts the CSI-2 bus.
>
> I recall Sam had a pretty well understanding of this part.
> I wonder if he has comments... :)
>
Check out the comments in my patch (as well as the algorithms I use)
and see if that makes more sense. Then let's continue the discussion.
> Thanks
>    j
> > +
> > +     ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
> > +                          0xff, sysdiv << 4 | (mipi_div - 1));
> > +     if (ret)
> > +             return ret;
> > +
> > +     ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2, 0xff, mult);
> > +     if (ret)
> > +             return ret;
> > +
> > +     return ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3, 0xf, prediv);
> > +}
> > +
> > +static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor,
> > +                                   unsigned long rate,
> > +                                   u8 *pll_prediv, u8 *pll_mult, u8 *sysdiv,
> > +                                   u8 *pll_rdiv, u8 *bit_div, u8 *pclk_div)
> > +{
> > +     unsigned long _rate = rate * OV5640_PLL_ROOT_DIV * OV5640_BIT_DIV *
> > +                             OV5640_PCLK_ROOT_DIV;
> > +
> > +     _rate = ov5640_calc_sys_clk(sensor, _rate, pll_prediv, pll_mult,
> > +                                 sysdiv);
> > +     *pll_rdiv = OV5640_PLL_ROOT_DIV;
> > +     *bit_div = OV5640_BIT_DIV;
> > +     *pclk_div = OV5640_PCLK_ROOT_DIV;
> > +
> > +     return _rate / *pll_rdiv / *bit_div / *pclk_div;
> > +}
> > +
> > +static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate)
> > +{
> > +     u8 prediv, mult, sysdiv, pll_rdiv, bit_div, pclk_div;
> > +     int ret;
> > +
> > +     ov5640_calc_pclk(sensor, rate, &prediv, &mult, &sysdiv, &pll_rdiv,
> > +                      &bit_div, &pclk_div);
> > +
> > +     if (bit_div == 2)
> > +             bit_div = 8;
> > +
> > +     ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0,
> > +                          0x0f, bit_div);
> > +     if (ret)
> > +             return ret;
> > +
> > +     /*
> > +      * We need to set sysdiv according to the clock, and to clear
> > +      * the MIPI divider.
> > +      */
> > +     ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
> > +                          0xff, sysdiv << 4);
> > +     if (ret)
> > +             return ret;
> > +
> > +     ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2,
> > +                          0xff, mult);
> > +     if (ret)
> > +             return ret;
> > +
> > +     ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3,
> > +                          0x1f, prediv | ((pll_rdiv - 1) << 4));
> > +     if (ret)
> > +             return ret;
> > +
> > +     return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x30,
> > +                           (ilog2(pclk_div) << 4));
> > +}
> > +
> >  /* download ov5640 settings to sensor through i2c */
> >  static int ov5640_set_timings(struct ov5640_dev *sensor,
> >                             const struct ov5640_mode_info *mode)
> > @@ -1637,6 +1905,8 @@ static int ov5640_set_mode(struct ov5640_dev *sensor)
> >       enum ov5640_downsize_mode dn_mode, orig_dn_mode;
> >       bool auto_gain = sensor->ctrls.auto_gain->val == 1;
> >       bool auto_exp =  sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
> > +     unsigned long rate;
> > +     unsigned char bpp;
> >       int ret;
> >
> >       dn_mode = mode->dn_mode;
> > @@ -1655,6 +1925,23 @@ static int ov5640_set_mode(struct ov5640_dev *sensor)
> >                       goto restore_auto_gain;
> >       }
> >
> > +     /*
> > +      * All the formats we support have 16 bits per pixel, except for JPEG
> > +      * which is 8 bits per pixel.
> > +      */
> > +     bpp = sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8 ? 8 : 16;
> > +     rate = mode->pixel_clock * bpp;
> > +     if (sensor->ep.bus_type == V4L2_MBUS_CSI2) {
> > +             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;
> > +             ret = ov5640_set_dvp_pclk(sensor, rate);
> > +     }
> > +
> > +     if (ret < 0)
> > +             return 0;
> > +
> >       if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
> >           (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
> >               /*
> > --
> > 2.19.1
> >

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 01/12] media: ov5640: Adjust the clock based on the expected rate
  2018-10-17 17:54     ` Sam Bobrowicz
@ 2018-10-17 19:51       ` jacopo mondi
  2018-10-18  9:31         ` Maxime Ripard
  2018-10-18 20:15         ` Samuel Bobrowicz
  0 siblings, 2 replies; 35+ messages in thread
From: jacopo mondi @ 2018-10-17 19:51 UTC (permalink / raw)
  To: Sam Bobrowicz
  Cc: Maxime Ripard, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media, Thomas Petazzoni, Mylene Josserand, Hans Verkuil,
	Sakari Ailus, Hugues Fruchet, Loic Poulain, Steve Longerbeam,
	Daniel Mack

[-- Attachment #1: Type: text/plain, Size: 25501 bytes --]

Hello Sam and Maxime (and other ov5640-ers :)

On Wed, Oct 17, 2018 at 10:54:01AM -0700, Sam Bobrowicz wrote:
> Hello Maxime and Jacopo (and other ov5640-ers),
>
> I just submitted my version of this patch to the mailing list as RFC.
> It is working on my MIPI platform. Please try it if you have time.
> Hopefully we can merge these two into a single patch that doesn't
> break any platforms.

Thanks, I have seen your patch but it seems to contain a lot of things
already part of Maxime's series. Was this intentional?

Now the un-pleaseant part: I have just sent out my re-implementation
of the MIPI clock tree configuration, based on top of Maxime's series.
Both you and me have spent a looot of time on this I'm sure, and now
we have two competing implementations.

I had a quick look at yours, and for sure there are things I am not
taking care of (I'm thinking about the 0x4837 register that seems to
be important for your platform), so I think both our implementations
can benefits from a comparison. What is important to me is that both
you and me don't feel like our work has been wasted, so let's try to
find out a way to get the better of the two put together, and possibly
applied on top of Maxime's series, so that a v5 of this will work for
both MIPI and DVP interfaces. How to do that I'm not sure atm, I think
other reviewers might help in that if they want to have a look at both
our series :)

Thanks everyone for the hard work on this sensor for now!

Thanks
   j

>
> Thanks,
> Sam
>
> Additional notes below.
>
> On Tue, Oct 16, 2018 at 9:54 AM jacopo mondi <jacopo@jmondi.org> wrote:
> >
> > Hello Maxime,
> >    a few comments I have collected while testing the series.
> >
> > Please see below.
> >
> > On Thu, Oct 11, 2018 at 11:20:56AM +0200, Maxime Ripard wrote:
> > > The clock structure for the PCLK is quite obscure in the documentation, and
> > > was hardcoded through the bytes array of each and every mode.
> > >
> > > This is troublesome, since we cannot adjust it at runtime based on other
> > > parameters (such as the number of bytes per pixel), and we can't support
> > > either framerates that have not been used by the various vendors, since we
> > > don't have the needed initialization sequence.
> > >
> > > We can however understand how the clock tree works, and then implement some
> > > functions to derive the various parameters from a given rate. And now that
> > > those parameters are calculated at runtime, we can remove them from the
> > > initialization sequence.
> > >
> > > The modes also gained a new parameter which is the clock that they are
> > > running at, from the register writes they were doing, so for now the switch
> > > to the new algorithm should be transparent.
> > >
> > > Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> > > ---
> > >  drivers/media/i2c/ov5640.c | 289 ++++++++++++++++++++++++++++++++++++-
> > >  1 file changed, 288 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> > > index 30b15e91d8be..88fb16341466 100644
> > > --- a/drivers/media/i2c/ov5640.c
> > > +++ b/drivers/media/i2c/ov5640.c
> > > @@ -175,6 +175,7 @@ struct ov5640_mode_info {
> > >       u32 htot;
> > >       u32 vact;
> > >       u32 vtot;
> > > +     u32 pixel_clock;
> > >       const struct reg_value *reg_data;
> > >       u32 reg_data_size;
> > >  };
> > > @@ -700,6 +701,7 @@ static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = {
> > >  /* power-on sensor init reg table */
> > >  static const struct ov5640_mode_info ov5640_mode_init_data = {
> > >       0, SUBSAMPLING, 640, 1896, 480, 984,
> > > +     56000000,
> > >       ov5640_init_setting_30fps_VGA,
> > >       ARRAY_SIZE(ov5640_init_setting_30fps_VGA),
> > >  };
> > > @@ -709,74 +711,91 @@ ov5640_mode_data[OV5640_NUM_FRAMERATES][OV5640_NUM_MODES] = {
> > >       {
> > >               {OV5640_MODE_QCIF_176_144, SUBSAMPLING,
> > >                176, 1896, 144, 984,
> > > +              28000000,
> > >                ov5640_setting_15fps_QCIF_176_144,
> > >                ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)},
> > >               {OV5640_MODE_QVGA_320_240, SUBSAMPLING,
> > >                320, 1896, 240, 984,
> > > +              28000000,
> > >                ov5640_setting_15fps_QVGA_320_240,
> > >                ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)},
> > >               {OV5640_MODE_VGA_640_480, SUBSAMPLING,
> > >                640, 1896, 480, 1080,
> > > +              28000000,
> > >                ov5640_setting_15fps_VGA_640_480,
> > >                ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)},
> > >               {OV5640_MODE_NTSC_720_480, SUBSAMPLING,
> > >                720, 1896, 480, 984,
> > > +              28000000,
> > >                ov5640_setting_15fps_NTSC_720_480,
> > >                ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)},
> > >               {OV5640_MODE_PAL_720_576, SUBSAMPLING,
> > >                720, 1896, 576, 984,
> > > +              28000000,
> > >                ov5640_setting_15fps_PAL_720_576,
> > >                ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)},
> > >               {OV5640_MODE_XGA_1024_768, SUBSAMPLING,
> > >                1024, 1896, 768, 1080,
> > > +              28000000,
> > >                ov5640_setting_15fps_XGA_1024_768,
> > >                ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)},
> > >               {OV5640_MODE_720P_1280_720, SUBSAMPLING,
> > >                1280, 1892, 720, 740,
> > > +              21000000,
> > >                ov5640_setting_15fps_720P_1280_720,
> > >                ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)},
> > >               {OV5640_MODE_1080P_1920_1080, SCALING,
> > >                1920, 2500, 1080, 1120,
> > > +              42000000,
> > >                ov5640_setting_15fps_1080P_1920_1080,
> > >                ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)},
> > >               {OV5640_MODE_QSXGA_2592_1944, SCALING,
> > >                2592, 2844, 1944, 1968,
> > > +              84000000,
> > >                ov5640_setting_15fps_QSXGA_2592_1944,
> > >                ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)},
> > >       }, {
> > >               {OV5640_MODE_QCIF_176_144, SUBSAMPLING,
> > >                176, 1896, 144, 984,
> > > +              56000000,
> > >                ov5640_setting_30fps_QCIF_176_144,
> > >                ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)},
> > >               {OV5640_MODE_QVGA_320_240, SUBSAMPLING,
> > >                320, 1896, 240, 984,
> > > +              56000000,
> > >                ov5640_setting_30fps_QVGA_320_240,
> > >                ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)},
> > >               {OV5640_MODE_VGA_640_480, SUBSAMPLING,
> > >                640, 1896, 480, 1080,
> > > +              56000000,
> > >                ov5640_setting_30fps_VGA_640_480,
> > >                ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)},
> > >               {OV5640_MODE_NTSC_720_480, SUBSAMPLING,
> > >                720, 1896, 480, 984,
> > > +              56000000,
> > >                ov5640_setting_30fps_NTSC_720_480,
> > >                ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)},
> > >               {OV5640_MODE_PAL_720_576, SUBSAMPLING,
> > >                720, 1896, 576, 984,
> > > +              56000000,
> > >                ov5640_setting_30fps_PAL_720_576,
> > >                ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)},
> > >               {OV5640_MODE_XGA_1024_768, SUBSAMPLING,
> > >                1024, 1896, 768, 1080,
> > > +              56000000,
> > >                ov5640_setting_30fps_XGA_1024_768,
> > >                ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)},
> > >               {OV5640_MODE_720P_1280_720, SUBSAMPLING,
> > >                1280, 1892, 720, 740,
> > > +              42000000,
> > >                ov5640_setting_30fps_720P_1280_720,
> > >                ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)},
> > >               {OV5640_MODE_1080P_1920_1080, SCALING,
> > >                1920, 2500, 1080, 1120,
> > > +              84000000,
> > >                ov5640_setting_30fps_1080P_1920_1080,
> > >                ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)},
> > > -             {OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, 0, 0, NULL, 0},
> > > +             {OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, 0, 0, 0, NULL, 0},
> > >       },
> > >  };
> > >
> > > @@ -909,6 +928,255 @@ static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg,
> > >       return ov5640_write_reg(sensor, reg, val);
> > >  }
> > >
> > > +/*
> > > + * After trying the various combinations, reading various
> > > + * documentations spreaded around the net, and from the various
> > > + * feedback, the clock tree is probably as follows:
> > > + *
> > > + *   +--------------+
> > > + *   |  Ext. Clock  |
> > > + *   +-+------------+
> > > + *     |  +----------+
> > > + *     +->|   PLL1   | - reg 0x3036, for the multiplier
> > > + *        +-+--------+ - reg 0x3037, bits 0-3 for the pre-divider
> > > + *          |  +--------------+
> > > + *          +->| System Clock |  - reg 0x3035, bits 4-7
> > > + *             +-+------------+
> > > + *               |  +--------------+
> > > + *               +->| MIPI Divider | - reg 0x3035, bits 0-3
> > > + *               |  +-+------------+
> > > + *               |    +----------------> MIPI SCLK
> > > + *               |  +--------------+
> > > + *               +->| PLL Root Div | - reg 0x3037, bit 4
> > > + *                  +-+------------+
> > > + *                    |  +---------+
> > > + *                    +->| Bit Div | - reg 0x3035, bits 0-3
> > > + *                       +-+-------+
> > > + *                         |  +-------------+
> > > + *                         +->| SCLK Div    | - reg 0x3108, bits 0-1
> > > + *                         |  +-+-----------+
> > > + *                         |    +---------------> SCLK
> > > + *                         |  +-------------+
> > > + *                         +->| SCLK 2X Div | - reg 0x3108, bits 2-3
> > > + *                         |  +-+-----------+
> > > + *                         |    +---------------> SCLK 2X
> > > + *                         |  +-------------+
> > > + *                         +->| PCLK Div    | - reg 0x3108, bits 4-5
> > > + *                            +-+-----------+
> > > + *                              +---------------> PCLK
> > > + *
> > > + * This is deviating from the datasheet at least for the register
> > > + * 0x3108, since it's said here that the PCLK would be clocked from
> > > + * the PLL.
> > > + *
> > > + * There seems to be also (unverified) constraints:
> > > + *  - the PLL pre-divider output rate should be in the 4-27MHz range
> > > + *  - the PLL multiplier output rate should be in the 500-1000MHz range
> > > + *  - PCLK >= SCLK * 2 in YUV, >= SCLK in Raw or JPEG
> > > + *  - MIPI SCLK = (bpp / lanes) / PCLK
> >
> > This last line probably wrong...
> >
> > > + *
> > > + * In the two latter cases, these constraints are met since our
> > > + * factors are hardcoded. If we were to change that, we would need to
> > > + * take this into account. The only varying parts are the PLL
> > > + * multiplier and the system clock divider, which are shared between
> > > + * all these clocks so won't cause any issue.
> > > + */
> > > +
> > > +/*
> > > + * This is supposed to be ranging from 1 to 8, but the value is always
> > > + * set to 3 in the vendor kernels.
> > > + */
> > > +#define OV5640_PLL_PREDIV    3
> > > +
> > > +#define OV5640_PLL_MULT_MIN  4
> > > +#define OV5640_PLL_MULT_MAX  252
> > > +
> > > +/*
> > > + * This is supposed to be ranging from 1 to 16, but the value is
> > > + * always set to either 1 or 2 in the vendor kernels.
> > > + */
> > > +#define OV5640_SYSDIV_MIN    1
> > > +#define OV5640_SYSDIV_MAX    2
> > > +
> > > +/*
> > > + * This is supposed to be ranging from 1 to 16, but the value is always
> > > + * set to 2 in the vendor kernels.
> > > + */
> > > +#define OV5640_MIPI_DIV              2
> > > +
> > > +/*
> > > + * This is supposed to be ranging from 1 to 2, but the value is always
> > > + * set to 2 in the vendor kernels.
> > > + */
> > > +#define OV5640_PLL_ROOT_DIV  2
> > > +
> > > +/*
> > > + * This is supposed to be either 1, 2 or 2.5, but the value is always
> > > + * set to 2 in the vendor kernels.
> > > + */
> > > +#define OV5640_BIT_DIV               2
> > > +
> > > +/*
> > > + * This is supposed to be ranging from 1 to 8, but the value is always
> > > + * set to 2 in the vendor kernels.
> > > + */
> > > +#define OV5640_SCLK_ROOT_DIV 2
> > > +
> > > +/*
> > > + * This is hardcoded so that the consistency is maintained between SCLK and
> > > + * SCLK 2x.
> > > + */
> > > +#define OV5640_SCLK2X_ROOT_DIV (OV5640_SCLK_ROOT_DIV / 2)
> > > +
> > > +/*
> > > + * This is supposed to be ranging from 1 to 8, but the value is always
> > > + * set to 1 in the vendor kernels.
> > > + */
> > > +#define OV5640_PCLK_ROOT_DIV 1
> > > +
> > > +static unsigned long ov5640_compute_sys_clk(struct ov5640_dev *sensor,
> > > +                                         u8 pll_prediv, u8 pll_mult,
> > > +                                         u8 sysdiv)
> > > +{
> > > +     unsigned long rate = clk_get_rate(sensor->xclk);
> >
> > The clock rate is stored in sensor->xclk at probe time, no need to
> > query it every iteration.
> >
> > > +
> > > +     return rate / pll_prediv * pll_mult / sysdiv;
> > > +}
> > > +
> > > +static unsigned long ov5640_calc_sys_clk(struct ov5640_dev *sensor,
> > > +                                      unsigned long rate,
> > > +                                      u8 *pll_prediv, u8 *pll_mult,
> > > +                                      u8 *sysdiv)
> > > +{
> > > +     unsigned long best = ~0;
> > > +     u8 best_sysdiv = 1, best_mult = 1;
> > > +     u8 _sysdiv, _pll_mult;
> > > +
> > > +     for (_sysdiv = OV5640_SYSDIV_MIN;
> > > +          _sysdiv <= OV5640_SYSDIV_MAX;
> > > +          _sysdiv++) {
> > > +             for (_pll_mult = OV5640_PLL_MULT_MIN;
> > > +                  _pll_mult <= OV5640_PLL_MULT_MAX;
> > > +                  _pll_mult++) {
> > > +                     unsigned long _rate;
> > > +
> > > +                     /*
> > > +                      * The PLL multiplier cannot be odd if above
> > > +                      * 127.
> > > +                      */
> > > +                     if (_pll_mult > 127 && (_pll_mult % 2))
> > > +                             continue;
> > > +
> > > +                     _rate = ov5640_compute_sys_clk(sensor,
> > > +                                                    OV5640_PLL_PREDIV,
> > > +                                                    _pll_mult, _sysdiv);
> >
> > I'm under the impression a system clock slower than the requested one, even
> > if more accurate is not good.
> >
> > I'm still working on understanding how all CSI-2 related timing
> > parameters play together, but since the system clock is calculated
> > from the pixel clock (which comes from the frame dimensions, bpp, and
> > rate), and it is then used to calculate the MIPI BIT clock frequency,
> > I think it would be better to be a little faster than a bit slower,
> > otherwise the serial lane clock wouldn't be fast enough to output
> > frames generated by the sensor core (or maybe it would just decrease
> > the frame rate and that's it, but I don't think it is just this).
> >
> > What do you think of adding the following here:
> >
> >                 if (_rate < rate)
> >                         continue
> >
> >
> Good point, I second this.
> > > +                     if (abs(rate - _rate) < abs(rate - best)) {
> > > +                             best = _rate;
> > > +                             best_sysdiv = _sysdiv;
> > > +                             best_mult = _pll_mult;
> > > +                     }
> > > +
> > > +                     if (_rate == rate)
> > > +                             goto out;
> > > +             }
> > > +     }
> > > +
> > > +out:
> > > +     *sysdiv = best_sysdiv;
> > > +     *pll_prediv = OV5640_PLL_PREDIV;
> > > +     *pll_mult = best_mult;
> > > +     return best;
> > > +}
> >
> > These function gets called at s_stream time, and cycle for a while,
> > and I'm under the impression the MIPI state machine doesn't like
> > delays too much, as I see timeouts on the receiver side.
> >
> > I have tried to move this function at set_fmt() time, every time a new
> > mode is selected, sysdiv, pll_prediv and pll_mult gets recalculated
> > (and stored in the ov5640_dev structure). I now have other timeouts on
> > missing EOF, but not anymore at startup time it seems.
> >
> I'm open to this solution, but ideally we should just cut down the
> execution time. I calculate the min's and max's for the PLL values
> dynamically and also include some additional breaks in my loop that
> should cut execution time quite a bit (even though I check many more
> sysdiv values. My algorithm still has plenty of room for further
> optimization too.
> >
> > On a general testing note: I have tried hardcoding all PLL
> > configuration paramters to their values as specified in the
> > initialization blob you have removed, and still, I have EOF timeouts
> > on the CSI-2 bus. There is something we're still missing on the MIPI
> > clock generation part, even if the documentation I have matches your
> > understandings..
> >
> > > +
> > > +static unsigned long ov5640_calc_mipi_clk(struct ov5640_dev *sensor,
> > > +                                       unsigned long rate,
> > > +                                       u8 *pll_prediv, u8 *pll_mult,
> > > +                                       u8 *sysdiv, u8 *mipi_div)
> > > +{
> > > +     unsigned long _rate = rate * OV5640_MIPI_DIV;
> > > +
> > > +     _rate = ov5640_calc_sys_clk(sensor, _rate, pll_prediv, pll_mult,
> > > +                                 sysdiv);
> > > +     *mipi_div = OV5640_MIPI_DIV;
> > > +
> > > +     return _rate / *mipi_div;
> > > +}
> > > +
> > > +static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor, unsigned long rate)
> > > +{
> > > +     u8 prediv, mult, sysdiv, mipi_div;
> > > +     int ret;
> > > +
> > > +     ov5640_calc_mipi_clk(sensor, rate, &prediv, &mult, &sysdiv, &mipi_div);
> >
> > Confusingly, register PLL_CTRL0 (0x3034) controls the "MIPI bit mode",
> > which I'm not a 100% sure what represents, and it is not written here
> > in the MIPI clock configuration function.
> >
> > As in the clock diagram I have it reads as:
> >  0x3034 MIPI BIT MODE
> >         0x08 = / 2
> >         0x0A = / 2.5
> >
> > I suspect it is used to maintain a correct relationship between the
> > pixel clock (in samples/second) and the total bandwidth in bytes and
> > thus it has to be written here.
> >
> > Reading the clock tree in the opposite direction I see:
> >
> >  ------------------------------
> > |pixel clk (htot * vtot * rate)|
> >  -----------------------------
> >             |   --------------------------------------------
> >             |->| P_DIV: 2lanes ? MIPI_DIV : 2 * MIPI_DIV   |
> >                 --------------------------------------------
> >                                    |   --------
> >                                    |->|PCLK_DIV|
> >                                        --------
> >                                           |   ------------
> >                                           |->|   BIT_DIV  |
> >                                               ------------
> >                                                     |   ----------
> >                                                     |->| PLL_R DIV|
> >                                                         ---------
> >                                                             |
> >                                                             |--------->SYSCLK
> >
> > And I then suspect that:
> >    P_DIV * PCLK_DIV * BIT_DIV = bpp
> >
> > So that, if we hardcode PLL_R div to 1, the system clock is
> > actually the total bandwidth in bytes.
> >
> > This would be then be:
> >   bandwidth = pixel_clk * rate * bpp
> >             = pixel_clk * rate * (MIPI_DIV * PCLK_DIV * BIT_DIV)
> >             = SYSCLK
> >
> > and then depending on the current format's bbp:
> >   PCLK_DIV = bpp / (BIT_DIV * num_lanes)
> >
> > This matches the other part of the MIPI clock tree, the MIPI_CLK
> > generation part, where the SYSCLK is divided again by MIPI_DIV and
> > then by 2 to take into account the CSI-2 DDR.
> >
> >   MIPI BIT CLK = bandwidth / num_lanes / 2
> >                = SYS_CLK / MIPI_DIV / 2
> >
> > While this works pretty well in my head, it does not on the sensor :/
> >
> > So I might have mis-intrpreted something, or we're missing some part
> > of the clock tree that impacts the CSI-2 bus.
> >
> > I recall Sam had a pretty well understanding of this part.
> > I wonder if he has comments... :)
> >
> Check out the comments in my patch (as well as the algorithms I use)
> and see if that makes more sense. Then let's continue the discussion.
> > Thanks
> >    j
> > > +
> > > +     ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
> > > +                          0xff, sysdiv << 4 | (mipi_div - 1));
> > > +     if (ret)
> > > +             return ret;
> > > +
> > > +     ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2, 0xff, mult);
> > > +     if (ret)
> > > +             return ret;
> > > +
> > > +     return ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3, 0xf, prediv);
> > > +}
> > > +
> > > +static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor,
> > > +                                   unsigned long rate,
> > > +                                   u8 *pll_prediv, u8 *pll_mult, u8 *sysdiv,
> > > +                                   u8 *pll_rdiv, u8 *bit_div, u8 *pclk_div)
> > > +{
> > > +     unsigned long _rate = rate * OV5640_PLL_ROOT_DIV * OV5640_BIT_DIV *
> > > +                             OV5640_PCLK_ROOT_DIV;
> > > +
> > > +     _rate = ov5640_calc_sys_clk(sensor, _rate, pll_prediv, pll_mult,
> > > +                                 sysdiv);
> > > +     *pll_rdiv = OV5640_PLL_ROOT_DIV;
> > > +     *bit_div = OV5640_BIT_DIV;
> > > +     *pclk_div = OV5640_PCLK_ROOT_DIV;
> > > +
> > > +     return _rate / *pll_rdiv / *bit_div / *pclk_div;
> > > +}
> > > +
> > > +static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate)
> > > +{
> > > +     u8 prediv, mult, sysdiv, pll_rdiv, bit_div, pclk_div;
> > > +     int ret;
> > > +
> > > +     ov5640_calc_pclk(sensor, rate, &prediv, &mult, &sysdiv, &pll_rdiv,
> > > +                      &bit_div, &pclk_div);
> > > +
> > > +     if (bit_div == 2)
> > > +             bit_div = 8;
> > > +
> > > +     ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0,
> > > +                          0x0f, bit_div);
> > > +     if (ret)
> > > +             return ret;
> > > +
> > > +     /*
> > > +      * We need to set sysdiv according to the clock, and to clear
> > > +      * the MIPI divider.
> > > +      */
> > > +     ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
> > > +                          0xff, sysdiv << 4);
> > > +     if (ret)
> > > +             return ret;
> > > +
> > > +     ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2,
> > > +                          0xff, mult);
> > > +     if (ret)
> > > +             return ret;
> > > +
> > > +     ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3,
> > > +                          0x1f, prediv | ((pll_rdiv - 1) << 4));
> > > +     if (ret)
> > > +             return ret;
> > > +
> > > +     return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x30,
> > > +                           (ilog2(pclk_div) << 4));
> > > +}
> > > +
> > >  /* download ov5640 settings to sensor through i2c */
> > >  static int ov5640_set_timings(struct ov5640_dev *sensor,
> > >                             const struct ov5640_mode_info *mode)
> > > @@ -1637,6 +1905,8 @@ static int ov5640_set_mode(struct ov5640_dev *sensor)
> > >       enum ov5640_downsize_mode dn_mode, orig_dn_mode;
> > >       bool auto_gain = sensor->ctrls.auto_gain->val == 1;
> > >       bool auto_exp =  sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
> > > +     unsigned long rate;
> > > +     unsigned char bpp;
> > >       int ret;
> > >
> > >       dn_mode = mode->dn_mode;
> > > @@ -1655,6 +1925,23 @@ static int ov5640_set_mode(struct ov5640_dev *sensor)
> > >                       goto restore_auto_gain;
> > >       }
> > >
> > > +     /*
> > > +      * All the formats we support have 16 bits per pixel, except for JPEG
> > > +      * which is 8 bits per pixel.
> > > +      */
> > > +     bpp = sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8 ? 8 : 16;
> > > +     rate = mode->pixel_clock * bpp;
> > > +     if (sensor->ep.bus_type == V4L2_MBUS_CSI2) {
> > > +             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;
> > > +             ret = ov5640_set_dvp_pclk(sensor, rate);
> > > +     }
> > > +
> > > +     if (ret < 0)
> > > +             return 0;
> > > +
> > >       if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
> > >           (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
> > >               /*
> > > --
> > > 2.19.1
> > >

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 01/12] media: ov5640: Adjust the clock based on the expected rate
  2018-10-16 16:54   ` jacopo mondi
  2018-10-17 17:54     ` Sam Bobrowicz
@ 2018-10-18  9:29     ` Maxime Ripard
  2018-10-18 13:46       ` jacopo mondi
  1 sibling, 1 reply; 35+ messages in thread
From: Maxime Ripard @ 2018-10-18  9:29 UTC (permalink / raw)
  To: jacopo mondi
  Cc: Mauro Carvalho Chehab, Laurent Pinchart, linux-media,
	Thomas Petazzoni, Mylene Josserand, Hans Verkuil, Sakari Ailus,
	Hugues Fruchet, Loic Poulain, Samuel Bobrowicz, Steve Longerbeam,
	Daniel Mack

[-- Attachment #1: Type: text/plain, Size: 4120 bytes --]

Hi Jacopo,

Thanks for reviewing this patch

On Tue, Oct 16, 2018 at 06:54:50PM +0200, jacopo mondi wrote:
> > +static unsigned long ov5640_compute_sys_clk(struct ov5640_dev *sensor,
> > +					    u8 pll_prediv, u8 pll_mult,
> > +					    u8 sysdiv)
> > +{
> > +	unsigned long rate = clk_get_rate(sensor->xclk);
> 
> The clock rate is stored in sensor->xclk at probe time, no need to
> query it every iteration.

From a clk API point of view though, there's nothing that guarantees
that the clock rate hasn't changed between the probe and the time
where this function is called.

I appreciate that we're probably connected to an oscillator, but even
then, on the Allwinner SoCs we've had the issue recently that one
oscillator feeding the BT chip was actually had a muxer, with each
option having a slightly different rate, which was bad enough for the
BT chip to be non-functional.

I can definitely imagine the same case happening here for some
SoCs. Plus, the clock framework will cache the rate as well when
possible, so we're not losing anything here.

> > +
> > +	return rate / pll_prediv * pll_mult / sysdiv;
> > +}
> > +
> > +static unsigned long ov5640_calc_sys_clk(struct ov5640_dev *sensor,
> > +					 unsigned long rate,
> > +					 u8 *pll_prediv, u8 *pll_mult,
> > +					 u8 *sysdiv)
> > +{
> > +	unsigned long best = ~0;
> > +	u8 best_sysdiv = 1, best_mult = 1;
> > +	u8 _sysdiv, _pll_mult;
> > +
> > +	for (_sysdiv = OV5640_SYSDIV_MIN;
> > +	     _sysdiv <= OV5640_SYSDIV_MAX;
> > +	     _sysdiv++) {
> > +		for (_pll_mult = OV5640_PLL_MULT_MIN;
> > +		     _pll_mult <= OV5640_PLL_MULT_MAX;
> > +		     _pll_mult++) {
> > +			unsigned long _rate;
> > +
> > +			/*
> > +			 * The PLL multiplier cannot be odd if above
> > +			 * 127.
> > +			 */
> > +			if (_pll_mult > 127 && (_pll_mult % 2))
> > +				continue;
> > +
> > +			_rate = ov5640_compute_sys_clk(sensor,
> > +						       OV5640_PLL_PREDIV,
> > +						       _pll_mult, _sysdiv);
> 
> I'm under the impression a system clock slower than the requested one, even
> if more accurate is not good.
> 
> I'm still working on understanding how all CSI-2 related timing
> parameters play together, but since the system clock is calculated
> from the pixel clock (which comes from the frame dimensions, bpp, and
> rate), and it is then used to calculate the MIPI BIT clock frequency,
> I think it would be better to be a little faster than a bit slower,
> otherwise the serial lane clock wouldn't be fast enough to output
> frames generated by the sensor core (or maybe it would just decrease
> the frame rate and that's it, but I don't think it is just this).
> 
> What do you think of adding the following here:
> 
>                 if (_rate < rate)
>                         continue

I really don't know MIPI-CSI2 enough to be able to comment on your
concerns, but when reaching the end of the operating limit of the
clock, it would prevent us from having any rate at all, which seems
bad too.

> > +			if (abs(rate - _rate) < abs(rate - best)) {
> > +				best = _rate;
> > +				best_sysdiv = _sysdiv;
> > +				best_mult = _pll_mult;
> > +			}
> > +
> > +			if (_rate == rate)
> > +				goto out;
> > +		}
> > +	}
> > +
> > +out:
> > +	*sysdiv = best_sysdiv;
> > +	*pll_prediv = OV5640_PLL_PREDIV;
> > +	*pll_mult = best_mult;
> > +	return best;
> > +}
> 
> These function gets called at s_stream time, and cycle for a while,
> and I'm under the impression the MIPI state machine doesn't like
> delays too much, as I see timeouts on the receiver side.
> 
> I have tried to move this function at set_fmt() time, every time a new
> mode is selected, sysdiv, pll_prediv and pll_mult gets recalculated
> (and stored in the ov5640_dev structure). I now have other timeouts on
> missing EOF, but not anymore at startup time it seems.

I have no objection caching the values if it solves issues with CSI :)

Can you send that patch?

Thanks!
Maxime

-- 
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 01/12] media: ov5640: Adjust the clock based on the expected rate
  2018-10-17 19:51       ` jacopo mondi
@ 2018-10-18  9:31         ` Maxime Ripard
  2018-10-18 10:03           ` jacopo mondi
  2018-10-18 20:15         ` Samuel Bobrowicz
  1 sibling, 1 reply; 35+ messages in thread
From: Maxime Ripard @ 2018-10-18  9:31 UTC (permalink / raw)
  To: jacopo mondi
  Cc: Sam Bobrowicz, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media, Thomas Petazzoni, Mylene Josserand, Hans Verkuil,
	Sakari Ailus, Hugues Fruchet, Loic Poulain, Steve Longerbeam,
	Daniel Mack

[-- Attachment #1: Type: text/plain, Size: 2000 bytes --]

On Wed, Oct 17, 2018 at 09:51:43PM +0200, jacopo mondi wrote:
> Hello Sam and Maxime (and other ov5640-ers :)
> 
> On Wed, Oct 17, 2018 at 10:54:01AM -0700, Sam Bobrowicz wrote:
> > Hello Maxime and Jacopo (and other ov5640-ers),
> >
> > I just submitted my version of this patch to the mailing list as RFC.
> > It is working on my MIPI platform. Please try it if you have time.
> > Hopefully we can merge these two into a single patch that doesn't
> > break any platforms.
> 
> Thanks, I have seen your patch but it seems to contain a lot of things
> already part of Maxime's series. Was this intentional?
> 
> Now the un-pleaseant part: I have just sent out my re-implementation
> of the MIPI clock tree configuration, based on top of Maxime's series.
> Both you and me have spent a looot of time on this I'm sure, and now
> we have two competing implementations.
> 
> I had a quick look at yours, and for sure there are things I am not
> taking care of (I'm thinking about the 0x4837 register that seems to
> be important for your platform), so I think both our implementations
> can benefits from a comparison. What is important to me is that both
> you and me don't feel like our work has been wasted, so let's try to
> find out a way to get the better of the two put together, and possibly
> applied on top of Maxime's series, so that a v5 of this will work for
> both MIPI and DVP interfaces. How to do that I'm not sure atm, I think
> other reviewers might help in that if they want to have a look at both
> our series :)

IIRC, Sam's system has never worked with the ov5640 driver, and his
patches now make it work.

Your patches on the other hand make sure that the current series
doesn't break existing users. So I guess we could merge your current
patches into the v5 of my rework, and have Sam send his work on top of
that.

Does that make sense?

Maxime

-- 
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 01/12] media: ov5640: Adjust the clock based on the expected rate
  2018-10-18  9:31         ` Maxime Ripard
@ 2018-10-18 10:03           ` jacopo mondi
  2018-10-18 20:25             ` Samuel Bobrowicz
  0 siblings, 1 reply; 35+ messages in thread
From: jacopo mondi @ 2018-10-18 10:03 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Sam Bobrowicz, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media, Thomas Petazzoni, Mylene Josserand, Hans Verkuil,
	Sakari Ailus, Hugues Fruchet, Loic Poulain, Steve Longerbeam,
	Daniel Mack

[-- Attachment #1: Type: text/plain, Size: 2594 bytes --]

On Thu, Oct 18, 2018 at 11:31:52AM +0200, Maxime Ripard wrote:
> On Wed, Oct 17, 2018 at 09:51:43PM +0200, jacopo mondi wrote:
> > Hello Sam and Maxime (and other ov5640-ers :)
> >
> > On Wed, Oct 17, 2018 at 10:54:01AM -0700, Sam Bobrowicz wrote:
> > > Hello Maxime and Jacopo (and other ov5640-ers),
> > >
> > > I just submitted my version of this patch to the mailing list as RFC.
> > > It is working on my MIPI platform. Please try it if you have time.
> > > Hopefully we can merge these two into a single patch that doesn't
> > > break any platforms.
> >
> > Thanks, I have seen your patch but it seems to contain a lot of things
> > already part of Maxime's series. Was this intentional?
> >
> > Now the un-pleaseant part: I have just sent out my re-implementation
> > of the MIPI clock tree configuration, based on top of Maxime's series.
> > Both you and me have spent a looot of time on this I'm sure, and now
> > we have two competing implementations.
> >
> > I had a quick look at yours, and for sure there are things I am not
> > taking care of (I'm thinking about the 0x4837 register that seems to
> > be important for your platform), so I think both our implementations
> > can benefits from a comparison. What is important to me is that both
> > you and me don't feel like our work has been wasted, so let's try to
> > find out a way to get the better of the two put together, and possibly
> > applied on top of Maxime's series, so that a v5 of this will work for
> > both MIPI and DVP interfaces. How to do that I'm not sure atm, I think
> > other reviewers might help in that if they want to have a look at both
> > our series :)
>
> IIRC, Sam's system has never worked with the ov5640 driver, and his
> patches now make it work.
>
> Your patches on the other hand make sure that the current series
> doesn't break existing users. So I guess we could merge your current
> patches into the v5 of my rework, and have Sam send his work on top of
> that.
>
> Does that make sense?

It does for me, but it puts the burden on Sam to re-apply his work
on top of [yours+mine] (which is something he would have had to do
anyhow to have his patches accepted, as he would have had to rebase on
top of your series).

I hope to find some more time to look into his series and find out how
hard it would be to add his changes on top of mine, and hopefully help
with this.

Also, testing my patches with DVP would be nice (it should not be
affected at all, but still...)

Thanks
   j

>
> Maxime
>
> --
> Maxime Ripard, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com



[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 01/12] media: ov5640: Adjust the clock based on the expected rate
  2018-10-18  9:29     ` Maxime Ripard
@ 2018-10-18 13:46       ` jacopo mondi
  2018-10-18 16:56         ` Maxime Ripard
  0 siblings, 1 reply; 35+ messages in thread
From: jacopo mondi @ 2018-10-18 13:46 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Mauro Carvalho Chehab, Laurent Pinchart, linux-media,
	Thomas Petazzoni, Mylene Josserand, Hans Verkuil, Sakari Ailus,
	Hugues Fruchet, Loic Poulain, Samuel Bobrowicz, Steve Longerbeam,
	Daniel Mack

[-- Attachment #1: Type: text/plain, Size: 5270 bytes --]

Hello Maxime,

On Thu, Oct 18, 2018 at 11:29:12AM +0200, Maxime Ripard wrote:
> Hi Jacopo,
>
> Thanks for reviewing this patch
>
> On Tue, Oct 16, 2018 at 06:54:50PM +0200, jacopo mondi wrote:
> > > +static unsigned long ov5640_compute_sys_clk(struct ov5640_dev *sensor,
> > > +					    u8 pll_prediv, u8 pll_mult,
> > > +					    u8 sysdiv)
> > > +{
> > > +	unsigned long rate = clk_get_rate(sensor->xclk);
> >
> > The clock rate is stored in sensor->xclk at probe time, no need to
> > query it every iteration.
>
> From a clk API point of view though, there's nothing that guarantees
> that the clock rate hasn't changed between the probe and the time
> where this function is called.

Correct, bell, it can be queried in the caller and re-used here :)
>
> I appreciate that we're probably connected to an oscillator, but even
> then, on the Allwinner SoCs we've had the issue recently that one
> oscillator feeding the BT chip was actually had a muxer, with each
> option having a slightly different rate, which was bad enough for the
> BT chip to be non-functional.
>
> I can definitely imagine the same case happening here for some
> SoCs. Plus, the clock framework will cache the rate as well when
> possible, so we're not losing anything here.

I see, so please ignore this comment :)

>
> > > +
> > > +	return rate / pll_prediv * pll_mult / sysdiv;
> > > +}
> > > +
> > > +static unsigned long ov5640_calc_sys_clk(struct ov5640_dev *sensor,
> > > +					 unsigned long rate,
> > > +					 u8 *pll_prediv, u8 *pll_mult,
> > > +					 u8 *sysdiv)
> > > +{
> > > +	unsigned long best = ~0;
> > > +	u8 best_sysdiv = 1, best_mult = 1;
> > > +	u8 _sysdiv, _pll_mult;
> > > +
> > > +	for (_sysdiv = OV5640_SYSDIV_MIN;
> > > +	     _sysdiv <= OV5640_SYSDIV_MAX;
> > > +	     _sysdiv++) {
> > > +		for (_pll_mult = OV5640_PLL_MULT_MIN;
> > > +		     _pll_mult <= OV5640_PLL_MULT_MAX;
> > > +		     _pll_mult++) {
> > > +			unsigned long _rate;
> > > +
> > > +			/*
> > > +			 * The PLL multiplier cannot be odd if above
> > > +			 * 127.
> > > +			 */
> > > +			if (_pll_mult > 127 && (_pll_mult % 2))
> > > +				continue;
> > > +
> > > +			_rate = ov5640_compute_sys_clk(sensor,
> > > +						       OV5640_PLL_PREDIV,
> > > +						       _pll_mult, _sysdiv);
> >
> > I'm under the impression a system clock slower than the requested one, even
> > if more accurate is not good.
> >
> > I'm still working on understanding how all CSI-2 related timing
> > parameters play together, but since the system clock is calculated
> > from the pixel clock (which comes from the frame dimensions, bpp, and
> > rate), and it is then used to calculate the MIPI BIT clock frequency,
> > I think it would be better to be a little faster than a bit slower,
> > otherwise the serial lane clock wouldn't be fast enough to output
> > frames generated by the sensor core (or maybe it would just decrease
> > the frame rate and that's it, but I don't think it is just this).
> >
> > What do you think of adding the following here:
> >
> >                 if (_rate < rate)
> >                         continue
>
> I really don't know MIPI-CSI2 enough to be able to comment on your
> concerns, but when reaching the end of the operating limit of the
> clock, it would prevent us from having any rate at all, which seems
> bad too.

Are you referring to the 1GHz limit of the (xvlkc / pre_div * mult)
output here? If that's your concern we should adjust the requested
SYSCLK rate then (and I added a check for that in my patches on top of
yours, but it could be improved to be honest, as it just refuses the
current rate, while it should increment the pre_divider instead, now
that I think better about that).

>
> > > +			if (abs(rate - _rate) < abs(rate - best)) {
> > > +				best = _rate;
> > > +				best_sysdiv = _sysdiv;
> > > +				best_mult = _pll_mult;
> > > +			}
> > > +
> > > +			if (_rate == rate)
> > > +				goto out;
> > > +		}
> > > +	}
> > > +
> > > +out:
> > > +	*sysdiv = best_sysdiv;
> > > +	*pll_prediv = OV5640_PLL_PREDIV;
> > > +	*pll_mult = best_mult;
> > > +	return best;
> > > +}
> >
> > These function gets called at s_stream time, and cycle for a while,
> > and I'm under the impression the MIPI state machine doesn't like
> > delays too much, as I see timeouts on the receiver side.
> >
> > I have tried to move this function at set_fmt() time, every time a new
> > mode is selected, sysdiv, pll_prediv and pll_mult gets recalculated
> > (and stored in the ov5640_dev structure). I now have other timeouts on
> > missing EOF, but not anymore at startup time it seems.
>
> I have no objection caching the values if it solves issues with CSI :)
>
> Can you send that patch?

Actually I think I was wrong. The timeout I saw have gone with the
last version I sent, even with this computation performed at
s_stream() time. And re-thinking this, the MIPI state machine should
get started after the data lanes are put in LP11 state, which happens
after this function ends.

We can discuss however if it is better to do this calculations at
s_fmt time or s_stream time as a general thing, but I think (also)
this comment might be ignored for now :)

Thanks
  j
>
> Thanks!
> Maxime
>
> --
> Maxime Ripard, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com



[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 01/12] media: ov5640: Adjust the clock based on the expected rate
  2018-10-18 13:46       ` jacopo mondi
@ 2018-10-18 16:56         ` Maxime Ripard
  0 siblings, 0 replies; 35+ messages in thread
From: Maxime Ripard @ 2018-10-18 16:56 UTC (permalink / raw)
  To: jacopo mondi
  Cc: Mauro Carvalho Chehab, Laurent Pinchart, linux-media,
	Thomas Petazzoni, Mylene Josserand, Hans Verkuil, Sakari Ailus,
	Hugues Fruchet, Loic Poulain, Samuel Bobrowicz, Steve Longerbeam,
	Daniel Mack

Hi!

On Thu, Oct 18, 2018 at 03:46:05PM +0200, jacopo mondi wrote:
> Hello Maxime,
> 
> On Thu, Oct 18, 2018 at 11:29:12AM +0200, Maxime Ripard wrote:
> > Hi Jacopo,
> >
> > Thanks for reviewing this patch
> >
> > On Tue, Oct 16, 2018 at 06:54:50PM +0200, jacopo mondi wrote:
> > > > +static unsigned long ov5640_compute_sys_clk(struct ov5640_dev *sensor,
> > > > +					    u8 pll_prediv, u8 pll_mult,
> > > > +					    u8 sysdiv)
> > > > +{
> > > > +	unsigned long rate = clk_get_rate(sensor->xclk);
> > >
> > > The clock rate is stored in sensor->xclk at probe time, no need to
> > > query it every iteration.
> >
> > From a clk API point of view though, there's nothing that guarantees
> > that the clock rate hasn't changed between the probe and the time
> > where this function is called.
> 
> Correct, bell, it can be queried in the caller and re-used here :)
> >
> > I appreciate that we're probably connected to an oscillator, but even
> > then, on the Allwinner SoCs we've had the issue recently that one
> > oscillator feeding the BT chip was actually had a muxer, with each
> > option having a slightly different rate, which was bad enough for the
> > BT chip to be non-functional.
> >
> > I can definitely imagine the same case happening here for some
> > SoCs. Plus, the clock framework will cache the rate as well when
> > possible, so we're not losing anything here.
> 
> I see, so please ignore this comment :)
> 
> >
> > > > +
> > > > +	return rate / pll_prediv * pll_mult / sysdiv;
> > > > +}
> > > > +
> > > > +static unsigned long ov5640_calc_sys_clk(struct ov5640_dev *sensor,
> > > > +					 unsigned long rate,
> > > > +					 u8 *pll_prediv, u8 *pll_mult,
> > > > +					 u8 *sysdiv)
> > > > +{
> > > > +	unsigned long best = ~0;
> > > > +	u8 best_sysdiv = 1, best_mult = 1;
> > > > +	u8 _sysdiv, _pll_mult;
> > > > +
> > > > +	for (_sysdiv = OV5640_SYSDIV_MIN;
> > > > +	     _sysdiv <= OV5640_SYSDIV_MAX;
> > > > +	     _sysdiv++) {
> > > > +		for (_pll_mult = OV5640_PLL_MULT_MIN;
> > > > +		     _pll_mult <= OV5640_PLL_MULT_MAX;
> > > > +		     _pll_mult++) {
> > > > +			unsigned long _rate;
> > > > +
> > > > +			/*
> > > > +			 * The PLL multiplier cannot be odd if above
> > > > +			 * 127.
> > > > +			 */
> > > > +			if (_pll_mult > 127 && (_pll_mult % 2))
> > > > +				continue;
> > > > +
> > > > +			_rate = ov5640_compute_sys_clk(sensor,
> > > > +						       OV5640_PLL_PREDIV,
> > > > +						       _pll_mult, _sysdiv);
> > >
> > > I'm under the impression a system clock slower than the requested one, even
> > > if more accurate is not good.
> > >
> > > I'm still working on understanding how all CSI-2 related timing
> > > parameters play together, but since the system clock is calculated
> > > from the pixel clock (which comes from the frame dimensions, bpp, and
> > > rate), and it is then used to calculate the MIPI BIT clock frequency,
> > > I think it would be better to be a little faster than a bit slower,
> > > otherwise the serial lane clock wouldn't be fast enough to output
> > > frames generated by the sensor core (or maybe it would just decrease
> > > the frame rate and that's it, but I don't think it is just this).
> > >
> > > What do you think of adding the following here:
> > >
> > >                 if (_rate < rate)
> > >                         continue
> >
> > I really don't know MIPI-CSI2 enough to be able to comment on your
> > concerns, but when reaching the end of the operating limit of the
> > clock, it would prevent us from having any rate at all, which seems
> > bad too.
> 
> Are you referring to the 1GHz limit of the (xvlkc / pre_div * mult)
> output here? If that's your concern we should adjust the requested
> SYSCLK rate then (and I added a check for that in my patches on top of
> yours, but it could be improved to be honest, as it just refuses the
> current rate, while it should increment the pre_divider instead, now
> that I think better about that).

I meant to the limits of the PCLK / MIPI bit clock, so the rate we are
expected to reach. But I really don't have any opinion on this, so
I'll just merge your suggestion for the next version.

> >
> > > > +			if (abs(rate - _rate) < abs(rate - best)) {
> > > > +				best = _rate;
> > > > +				best_sysdiv = _sysdiv;
> > > > +				best_mult = _pll_mult;
> > > > +			}
> > > > +
> > > > +			if (_rate == rate)
> > > > +				goto out;
> > > > +		}
> > > > +	}
> > > > +
> > > > +out:
> > > > +	*sysdiv = best_sysdiv;
> > > > +	*pll_prediv = OV5640_PLL_PREDIV;
> > > > +	*pll_mult = best_mult;
> > > > +	return best;
> > > > +}
> > >
> > > These function gets called at s_stream time, and cycle for a while,
> > > and I'm under the impression the MIPI state machine doesn't like
> > > delays too much, as I see timeouts on the receiver side.
> > >
> > > I have tried to move this function at set_fmt() time, every time a new
> > > mode is selected, sysdiv, pll_prediv and pll_mult gets recalculated
> > > (and stored in the ov5640_dev structure). I now have other timeouts on
> > > missing EOF, but not anymore at startup time it seems.
> >
> > I have no objection caching the values if it solves issues with CSI :)
> >
> > Can you send that patch?
> 
> Actually I think I was wrong. The timeout I saw have gone with the
> last version I sent, even with this computation performed at
> s_stream() time. And re-thinking this, the MIPI state machine should
> get started after the data lanes are put in LP11 state, which happens
> after this function ends.
> 
> We can discuss however if it is better to do this calculations at
> s_fmt time or s_stream time as a general thing, but I think (also)
> this comment might be ignored for now :)

That works for me :)

Thanks!
Maxime

-- 
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 01/12] media: ov5640: Adjust the clock based on the expected rate
  2018-10-17 19:51       ` jacopo mondi
  2018-10-18  9:31         ` Maxime Ripard
@ 2018-10-18 20:15         ` Samuel Bobrowicz
  1 sibling, 0 replies; 35+ messages in thread
From: Samuel Bobrowicz @ 2018-10-18 20:15 UTC (permalink / raw)
  To: jacopo mondi
  Cc: Maxime Ripard, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media, Thomas Petazzoni, Mylene Josserand, Hans Verkuil,
	Sakari Ailus, Hugues Fruchet, Loic Poulain, Steve Longerbeam,
	Daniel Mack

Hey Jacobi,

 Not a lot of time to respond right now, these days I’m bouncing around between a couple jobs. I’ll be trying your and Maximes patches and responding to tech details this weekend.

Just want to say that as long as we get the driver working I’m happy :) The VAST majority of time put into this has been the reverse engineering effort to understand how the part works. Iterating through various  versions takes time as we get on the same page, but will be necessary to make sure we don’t introduce any regressions on any platforms. I don’t think it is time wasted.

I don’t think my patch is competing, submitting it was just the best way to communicate what works on my platform. I don’t expect the final patch we land on to be designated as “authored by” me, especially since it was copied mostly from Maximes patch. Also, FYI, I’ve put my other patch series about the ov5640 on hold until we figure this out.

Sam

Sent from my iPhone

> On Oct 17, 2018, at 12:51 PM, jacopo mondi <jacopo@jmondi.org> wrote:
> 
> Hello Sam and Maxime (and other ov5640-ers :)
> 
>> On Wed, Oct 17, 2018 at 10:54:01AM -0700, Sam Bobrowicz wrote:
>> Hello Maxime and Jacopo (and other ov5640-ers),
>> 
>> I just submitted my version of this patch to the mailing list as RFC.
>> It is working on my MIPI platform. Please try it if you have time.
>> Hopefully we can merge these two into a single patch that doesn't
>> break any platforms.
> 
> Thanks, I have seen your patch but it seems to contain a lot of things
> already part of Maxime's series. Was this intentional?
> 
> Now the un-pleaseant part: I have just sent out my re-implementation
> of the MIPI clock tree configuration, based on top of Maxime's series.
> Both you and me have spent a looot of time on this I'm sure, and now
> we have two competing implementations.
> 
> I had a quick look at yours, and for sure there are things I am not
> taking care of (I'm thinking about the 0x4837 register that seems to
> be important for your platform), so I think both our implementations
> can benefits from a comparison. What is important to me is that both
> you and me don't feel like our work has been wasted, so let's try to
> find out a way to get the better of the two put together, and possibly
> applied on top of Maxime's series, so that a v5 of this will work for
> both MIPI and DVP interfaces. How to do that I'm not sure atm, I think
> other reviewers might help in that if they want to have a look at both
> our series :)
> 
> Thanks everyone for the hard work on this sensor for now!
> 
> Thanks
>   j
> 
>> 
>> Thanks,
>> Sam
>> 
>> Additional notes below.
>> 
>>> On Tue, Oct 16, 2018 at 9:54 AM jacopo mondi <jacopo@jmondi.org> wrote:
>>> 
>>> Hello Maxime,
>>>   a few comments I have collected while testing the series.
>>> 
>>> Please see below.
>>> 
>>>> On Thu, Oct 11, 2018 at 11:20:56AM +0200, Maxime Ripard wrote:
>>>> The clock structure for the PCLK is quite obscure in the documentation, and
>>>> was hardcoded through the bytes array of each and every mode.
>>>> 
>>>> This is troublesome, since we cannot adjust it at runtime based on other
>>>> parameters (such as the number of bytes per pixel), and we can't support
>>>> either framerates that have not been used by the various vendors, since we
>>>> don't have the needed initialization sequence.
>>>> 
>>>> We can however understand how the clock tree works, and then implement some
>>>> functions to derive the various parameters from a given rate. And now that
>>>> those parameters are calculated at runtime, we can remove them from the
>>>> initialization sequence.
>>>> 
>>>> The modes also gained a new parameter which is the clock that they are
>>>> running at, from the register writes they were doing, so for now the switch
>>>> to the new algorithm should be transparent.
>>>> 
>>>> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
>>>> ---
>>>> drivers/media/i2c/ov5640.c | 289 ++++++++++++++++++++++++++++++++++++-
>>>> 1 file changed, 288 insertions(+), 1 deletion(-)
>>>> 
>>>> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
>>>> index 30b15e91d8be..88fb16341466 100644
>>>> --- a/drivers/media/i2c/ov5640.c
>>>> +++ b/drivers/media/i2c/ov5640.c
>>>> @@ -175,6 +175,7 @@ struct ov5640_mode_info {
>>>>      u32 htot;
>>>>      u32 vact;
>>>>      u32 vtot;
>>>> +     u32 pixel_clock;
>>>>      const struct reg_value *reg_data;
>>>>      u32 reg_data_size;
>>>> };
>>>> @@ -700,6 +701,7 @@ static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = {
>>>> /* power-on sensor init reg table */
>>>> static const struct ov5640_mode_info ov5640_mode_init_data = {
>>>>      0, SUBSAMPLING, 640, 1896, 480, 984,
>>>> +     56000000,
>>>>      ov5640_init_setting_30fps_VGA,
>>>>      ARRAY_SIZE(ov5640_init_setting_30fps_VGA),
>>>> };
>>>> @@ -709,74 +711,91 @@ ov5640_mode_data[OV5640_NUM_FRAMERATES][OV5640_NUM_MODES] = {
>>>>      {
>>>>              {OV5640_MODE_QCIF_176_144, SUBSAMPLING,
>>>>               176, 1896, 144, 984,
>>>> +              28000000,
>>>>               ov5640_setting_15fps_QCIF_176_144,
>>>>               ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)},
>>>>              {OV5640_MODE_QVGA_320_240, SUBSAMPLING,
>>>>               320, 1896, 240, 984,
>>>> +              28000000,
>>>>               ov5640_setting_15fps_QVGA_320_240,
>>>>               ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)},
>>>>              {OV5640_MODE_VGA_640_480, SUBSAMPLING,
>>>>               640, 1896, 480, 1080,
>>>> +              28000000,
>>>>               ov5640_setting_15fps_VGA_640_480,
>>>>               ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)},
>>>>              {OV5640_MODE_NTSC_720_480, SUBSAMPLING,
>>>>               720, 1896, 480, 984,
>>>> +              28000000,
>>>>               ov5640_setting_15fps_NTSC_720_480,
>>>>               ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)},
>>>>              {OV5640_MODE_PAL_720_576, SUBSAMPLING,
>>>>               720, 1896, 576, 984,
>>>> +              28000000,
>>>>               ov5640_setting_15fps_PAL_720_576,
>>>>               ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)},
>>>>              {OV5640_MODE_XGA_1024_768, SUBSAMPLING,
>>>>               1024, 1896, 768, 1080,
>>>> +              28000000,
>>>>               ov5640_setting_15fps_XGA_1024_768,
>>>>               ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)},
>>>>              {OV5640_MODE_720P_1280_720, SUBSAMPLING,
>>>>               1280, 1892, 720, 740,
>>>> +              21000000,
>>>>               ov5640_setting_15fps_720P_1280_720,
>>>>               ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)},
>>>>              {OV5640_MODE_1080P_1920_1080, SCALING,
>>>>               1920, 2500, 1080, 1120,
>>>> +              42000000,
>>>>               ov5640_setting_15fps_1080P_1920_1080,
>>>>               ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)},
>>>>              {OV5640_MODE_QSXGA_2592_1944, SCALING,
>>>>               2592, 2844, 1944, 1968,
>>>> +              84000000,
>>>>               ov5640_setting_15fps_QSXGA_2592_1944,
>>>>               ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)},
>>>>      }, {
>>>>              {OV5640_MODE_QCIF_176_144, SUBSAMPLING,
>>>>               176, 1896, 144, 984,
>>>> +              56000000,
>>>>               ov5640_setting_30fps_QCIF_176_144,
>>>>               ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)},
>>>>              {OV5640_MODE_QVGA_320_240, SUBSAMPLING,
>>>>               320, 1896, 240, 984,
>>>> +              56000000,
>>>>               ov5640_setting_30fps_QVGA_320_240,
>>>>               ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)},
>>>>              {OV5640_MODE_VGA_640_480, SUBSAMPLING,
>>>>               640, 1896, 480, 1080,
>>>> +              56000000,
>>>>               ov5640_setting_30fps_VGA_640_480,
>>>>               ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)},
>>>>              {OV5640_MODE_NTSC_720_480, SUBSAMPLING,
>>>>               720, 1896, 480, 984,
>>>> +              56000000,
>>>>               ov5640_setting_30fps_NTSC_720_480,
>>>>               ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)},
>>>>              {OV5640_MODE_PAL_720_576, SUBSAMPLING,
>>>>               720, 1896, 576, 984,
>>>> +              56000000,
>>>>               ov5640_setting_30fps_PAL_720_576,
>>>>               ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)},
>>>>              {OV5640_MODE_XGA_1024_768, SUBSAMPLING,
>>>>               1024, 1896, 768, 1080,
>>>> +              56000000,
>>>>               ov5640_setting_30fps_XGA_1024_768,
>>>>               ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)},
>>>>              {OV5640_MODE_720P_1280_720, SUBSAMPLING,
>>>>               1280, 1892, 720, 740,
>>>> +              42000000,
>>>>               ov5640_setting_30fps_720P_1280_720,
>>>>               ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)},
>>>>              {OV5640_MODE_1080P_1920_1080, SCALING,
>>>>               1920, 2500, 1080, 1120,
>>>> +              84000000,
>>>>               ov5640_setting_30fps_1080P_1920_1080,
>>>>               ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)},
>>>> -             {OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, 0, 0, NULL, 0},
>>>> +             {OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, 0, 0, 0, NULL, 0},
>>>>      },
>>>> };
>>>> 
>>>> @@ -909,6 +928,255 @@ static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg,
>>>>      return ov5640_write_reg(sensor, reg, val);
>>>> }
>>>> 
>>>> +/*
>>>> + * After trying the various combinations, reading various
>>>> + * documentations spreaded around the net, and from the various
>>>> + * feedback, the clock tree is probably as follows:
>>>> + *
>>>> + *   +--------------+
>>>> + *   |  Ext. Clock  |
>>>> + *   +-+------------+
>>>> + *     |  +----------+
>>>> + *     +->|   PLL1   | - reg 0x3036, for the multiplier
>>>> + *        +-+--------+ - reg 0x3037, bits 0-3 for the pre-divider
>>>> + *          |  +--------------+
>>>> + *          +->| System Clock |  - reg 0x3035, bits 4-7
>>>> + *             +-+------------+
>>>> + *               |  +--------------+
>>>> + *               +->| MIPI Divider | - reg 0x3035, bits 0-3
>>>> + *               |  +-+------------+
>>>> + *               |    +----------------> MIPI SCLK
>>>> + *               |  +--------------+
>>>> + *               +->| PLL Root Div | - reg 0x3037, bit 4
>>>> + *                  +-+------------+
>>>> + *                    |  +---------+
>>>> + *                    +->| Bit Div | - reg 0x3035, bits 0-3
>>>> + *                       +-+-------+
>>>> + *                         |  +-------------+
>>>> + *                         +->| SCLK Div    | - reg 0x3108, bits 0-1
>>>> + *                         |  +-+-----------+
>>>> + *                         |    +---------------> SCLK
>>>> + *                         |  +-------------+
>>>> + *                         +->| SCLK 2X Div | - reg 0x3108, bits 2-3
>>>> + *                         |  +-+-----------+
>>>> + *                         |    +---------------> SCLK 2X
>>>> + *                         |  +-------------+
>>>> + *                         +->| PCLK Div    | - reg 0x3108, bits 4-5
>>>> + *                            +-+-----------+
>>>> + *                              +---------------> PCLK
>>>> + *
>>>> + * This is deviating from the datasheet at least for the register
>>>> + * 0x3108, since it's said here that the PCLK would be clocked from
>>>> + * the PLL.
>>>> + *
>>>> + * There seems to be also (unverified) constraints:
>>>> + *  - the PLL pre-divider output rate should be in the 4-27MHz range
>>>> + *  - the PLL multiplier output rate should be in the 500-1000MHz range
>>>> + *  - PCLK >= SCLK * 2 in YUV, >= SCLK in Raw or JPEG
>>>> + *  - MIPI SCLK = (bpp / lanes) / PCLK
>>> 
>>> This last line probably wrong...
>>> 
>>>> + *
>>>> + * In the two latter cases, these constraints are met since our
>>>> + * factors are hardcoded. If we were to change that, we would need to
>>>> + * take this into account. The only varying parts are the PLL
>>>> + * multiplier and the system clock divider, which are shared between
>>>> + * all these clocks so won't cause any issue.
>>>> + */
>>>> +
>>>> +/*
>>>> + * This is supposed to be ranging from 1 to 8, but the value is always
>>>> + * set to 3 in the vendor kernels.
>>>> + */
>>>> +#define OV5640_PLL_PREDIV    3
>>>> +
>>>> +#define OV5640_PLL_MULT_MIN  4
>>>> +#define OV5640_PLL_MULT_MAX  252
>>>> +
>>>> +/*
>>>> + * This is supposed to be ranging from 1 to 16, but the value is
>>>> + * always set to either 1 or 2 in the vendor kernels.
>>>> + */
>>>> +#define OV5640_SYSDIV_MIN    1
>>>> +#define OV5640_SYSDIV_MAX    2
>>>> +
>>>> +/*
>>>> + * This is supposed to be ranging from 1 to 16, but the value is always
>>>> + * set to 2 in the vendor kernels.
>>>> + */
>>>> +#define OV5640_MIPI_DIV              2
>>>> +
>>>> +/*
>>>> + * This is supposed to be ranging from 1 to 2, but the value is always
>>>> + * set to 2 in the vendor kernels.
>>>> + */
>>>> +#define OV5640_PLL_ROOT_DIV  2
>>>> +
>>>> +/*
>>>> + * This is supposed to be either 1, 2 or 2.5, but the value is always
>>>> + * set to 2 in the vendor kernels.
>>>> + */
>>>> +#define OV5640_BIT_DIV               2
>>>> +
>>>> +/*
>>>> + * This is supposed to be ranging from 1 to 8, but the value is always
>>>> + * set to 2 in the vendor kernels.
>>>> + */
>>>> +#define OV5640_SCLK_ROOT_DIV 2
>>>> +
>>>> +/*
>>>> + * This is hardcoded so that the consistency is maintained between SCLK and
>>>> + * SCLK 2x.
>>>> + */
>>>> +#define OV5640_SCLK2X_ROOT_DIV (OV5640_SCLK_ROOT_DIV / 2)
>>>> +
>>>> +/*
>>>> + * This is supposed to be ranging from 1 to 8, but the value is always
>>>> + * set to 1 in the vendor kernels.
>>>> + */
>>>> +#define OV5640_PCLK_ROOT_DIV 1
>>>> +
>>>> +static unsigned long ov5640_compute_sys_clk(struct ov5640_dev *sensor,
>>>> +                                         u8 pll_prediv, u8 pll_mult,
>>>> +                                         u8 sysdiv)
>>>> +{
>>>> +     unsigned long rate = clk_get_rate(sensor->xclk);
>>> 
>>> The clock rate is stored in sensor->xclk at probe time, no need to
>>> query it every iteration.
>>> 
>>>> +
>>>> +     return rate / pll_prediv * pll_mult / sysdiv;
>>>> +}
>>>> +
>>>> +static unsigned long ov5640_calc_sys_clk(struct ov5640_dev *sensor,
>>>> +                                      unsigned long rate,
>>>> +                                      u8 *pll_prediv, u8 *pll_mult,
>>>> +                                      u8 *sysdiv)
>>>> +{
>>>> +     unsigned long best = ~0;
>>>> +     u8 best_sysdiv = 1, best_mult = 1;
>>>> +     u8 _sysdiv, _pll_mult;
>>>> +
>>>> +     for (_sysdiv = OV5640_SYSDIV_MIN;
>>>> +          _sysdiv <= OV5640_SYSDIV_MAX;
>>>> +          _sysdiv++) {
>>>> +             for (_pll_mult = OV5640_PLL_MULT_MIN;
>>>> +                  _pll_mult <= OV5640_PLL_MULT_MAX;
>>>> +                  _pll_mult++) {
>>>> +                     unsigned long _rate;
>>>> +
>>>> +                     /*
>>>> +                      * The PLL multiplier cannot be odd if above
>>>> +                      * 127.
>>>> +                      */
>>>> +                     if (_pll_mult > 127 && (_pll_mult % 2))
>>>> +                             continue;
>>>> +
>>>> +                     _rate = ov5640_compute_sys_clk(sensor,
>>>> +                                                    OV5640_PLL_PREDIV,
>>>> +                                                    _pll_mult, _sysdiv);
>>> 
>>> I'm under the impression a system clock slower than the requested one, even
>>> if more accurate is not good.
>>> 
>>> I'm still working on understanding how all CSI-2 related timing
>>> parameters play together, but since the system clock is calculated
>>> from the pixel clock (which comes from the frame dimensions, bpp, and
>>> rate), and it is then used to calculate the MIPI BIT clock frequency,
>>> I think it would be better to be a little faster than a bit slower,
>>> otherwise the serial lane clock wouldn't be fast enough to output
>>> frames generated by the sensor core (or maybe it would just decrease
>>> the frame rate and that's it, but I don't think it is just this).
>>> 
>>> What do you think of adding the following here:
>>> 
>>>                if (_rate < rate)
>>>                        continue
>>> 
>>> 
>> Good point, I second this.
>>>> +                     if (abs(rate - _rate) < abs(rate - best)) {
>>>> +                             best = _rate;
>>>> +                             best_sysdiv = _sysdiv;
>>>> +                             best_mult = _pll_mult;
>>>> +                     }
>>>> +
>>>> +                     if (_rate == rate)
>>>> +                             goto out;
>>>> +             }
>>>> +     }
>>>> +
>>>> +out:
>>>> +     *sysdiv = best_sysdiv;
>>>> +     *pll_prediv = OV5640_PLL_PREDIV;
>>>> +     *pll_mult = best_mult;
>>>> +     return best;
>>>> +}
>>> 
>>> These function gets called at s_stream time, and cycle for a while,
>>> and I'm under the impression the MIPI state machine doesn't like
>>> delays too much, as I see timeouts on the receiver side.
>>> 
>>> I have tried to move this function at set_fmt() time, every time a new
>>> mode is selected, sysdiv, pll_prediv and pll_mult gets recalculated
>>> (and stored in the ov5640_dev structure). I now have other timeouts on
>>> missing EOF, but not anymore at startup time it seems.
>>> 
>> I'm open to this solution, but ideally we should just cut down the
>> execution time. I calculate the min's and max's for the PLL values
>> dynamically and also include some additional breaks in my loop that
>> should cut execution time quite a bit (even though I check many more
>> sysdiv values. My algorithm still has plenty of room for further
>> optimization too.
>>> 
>>> On a general testing note: I have tried hardcoding all PLL
>>> configuration paramters to their values as specified in the
>>> initialization blob you have removed, and still, I have EOF timeouts
>>> on the CSI-2 bus. There is something we're still missing on the MIPI
>>> clock generation part, even if the documentation I have matches your
>>> understandings..
>>> 
>>>> +
>>>> +static unsigned long ov5640_calc_mipi_clk(struct ov5640_dev *sensor,
>>>> +                                       unsigned long rate,
>>>> +                                       u8 *pll_prediv, u8 *pll_mult,
>>>> +                                       u8 *sysdiv, u8 *mipi_div)
>>>> +{
>>>> +     unsigned long _rate = rate * OV5640_MIPI_DIV;
>>>> +
>>>> +     _rate = ov5640_calc_sys_clk(sensor, _rate, pll_prediv, pll_mult,
>>>> +                                 sysdiv);
>>>> +     *mipi_div = OV5640_MIPI_DIV;
>>>> +
>>>> +     return _rate / *mipi_div;
>>>> +}
>>>> +
>>>> +static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor, unsigned long rate)
>>>> +{
>>>> +     u8 prediv, mult, sysdiv, mipi_div;
>>>> +     int ret;
>>>> +
>>>> +     ov5640_calc_mipi_clk(sensor, rate, &prediv, &mult, &sysdiv, &mipi_div);
>>> 
>>> Confusingly, register PLL_CTRL0 (0x3034) controls the "MIPI bit mode",
>>> which I'm not a 100% sure what represents, and it is not written here
>>> in the MIPI clock configuration function.
>>> 
>>> As in the clock diagram I have it reads as:
>>> 0x3034 MIPI BIT MODE
>>>        0x08 = / 2
>>>        0x0A = / 2.5
>>> 
>>> I suspect it is used to maintain a correct relationship between the
>>> pixel clock (in samples/second) and the total bandwidth in bytes and
>>> thus it has to be written here.
>>> 
>>> Reading the clock tree in the opposite direction I see:
>>> 
>>> ------------------------------
>>> |pixel clk (htot * vtot * rate)|
>>> -----------------------------
>>>            |   --------------------------------------------
>>>            |->| P_DIV: 2lanes ? MIPI_DIV : 2 * MIPI_DIV   |
>>>                --------------------------------------------
>>>                                   |   --------
>>>                                   |->|PCLK_DIV|
>>>                                       --------
>>>                                          |   ------------
>>>                                          |->|   BIT_DIV  |
>>>                                              ------------
>>>                                                    |   ----------
>>>                                                    |->| PLL_R DIV|
>>>                                                        ---------
>>>                                                            |
>>>                                                            |--------->SYSCLK
>>> 
>>> And I then suspect that:
>>>   P_DIV * PCLK_DIV * BIT_DIV = bpp
>>> 
>>> So that, if we hardcode PLL_R div to 1, the system clock is
>>> actually the total bandwidth in bytes.
>>> 
>>> This would be then be:
>>>  bandwidth = pixel_clk * rate * bpp
>>>            = pixel_clk * rate * (MIPI_DIV * PCLK_DIV * BIT_DIV)
>>>            = SYSCLK
>>> 
>>> and then depending on the current format's bbp:
>>>  PCLK_DIV = bpp / (BIT_DIV * num_lanes)
>>> 
>>> This matches the other part of the MIPI clock tree, the MIPI_CLK
>>> generation part, where the SYSCLK is divided again by MIPI_DIV and
>>> then by 2 to take into account the CSI-2 DDR.
>>> 
>>>  MIPI BIT CLK = bandwidth / num_lanes / 2
>>>               = SYS_CLK / MIPI_DIV / 2
>>> 
>>> While this works pretty well in my head, it does not on the sensor :/
>>> 
>>> So I might have mis-intrpreted something, or we're missing some part
>>> of the clock tree that impacts the CSI-2 bus.
>>> 
>>> I recall Sam had a pretty well understanding of this part.
>>> I wonder if he has comments... :)
>>> 
>> Check out the comments in my patch (as well as the algorithms I use)
>> and see if that makes more sense. Then let's continue the discussion.
>>> Thanks
>>>   j
>>>> +
>>>> +     ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
>>>> +                          0xff, sysdiv << 4 | (mipi_div - 1));
>>>> +     if (ret)
>>>> +             return ret;
>>>> +
>>>> +     ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2, 0xff, mult);
>>>> +     if (ret)
>>>> +             return ret;
>>>> +
>>>> +     return ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3, 0xf, prediv);
>>>> +}
>>>> +
>>>> +static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor,
>>>> +                                   unsigned long rate,
>>>> +                                   u8 *pll_prediv, u8 *pll_mult, u8 *sysdiv,
>>>> +                                   u8 *pll_rdiv, u8 *bit_div, u8 *pclk_div)
>>>> +{
>>>> +     unsigned long _rate = rate * OV5640_PLL_ROOT_DIV * OV5640_BIT_DIV *
>>>> +                             OV5640_PCLK_ROOT_DIV;
>>>> +
>>>> +     _rate = ov5640_calc_sys_clk(sensor, _rate, pll_prediv, pll_mult,
>>>> +                                 sysdiv);
>>>> +     *pll_rdiv = OV5640_PLL_ROOT_DIV;
>>>> +     *bit_div = OV5640_BIT_DIV;
>>>> +     *pclk_div = OV5640_PCLK_ROOT_DIV;
>>>> +
>>>> +     return _rate / *pll_rdiv / *bit_div / *pclk_div;
>>>> +}
>>>> +
>>>> +static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate)
>>>> +{
>>>> +     u8 prediv, mult, sysdiv, pll_rdiv, bit_div, pclk_div;
>>>> +     int ret;
>>>> +
>>>> +     ov5640_calc_pclk(sensor, rate, &prediv, &mult, &sysdiv, &pll_rdiv,
>>>> +                      &bit_div, &pclk_div);
>>>> +
>>>> +     if (bit_div == 2)
>>>> +             bit_div = 8;
>>>> +
>>>> +     ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0,
>>>> +                          0x0f, bit_div);
>>>> +     if (ret)
>>>> +             return ret;
>>>> +
>>>> +     /*
>>>> +      * We need to set sysdiv according to the clock, and to clear
>>>> +      * the MIPI divider.
>>>> +      */
>>>> +     ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1,
>>>> +                          0xff, sysdiv << 4);
>>>> +     if (ret)
>>>> +             return ret;
>>>> +
>>>> +     ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2,
>>>> +                          0xff, mult);
>>>> +     if (ret)
>>>> +             return ret;
>>>> +
>>>> +     ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3,
>>>> +                          0x1f, prediv | ((pll_rdiv - 1) << 4));
>>>> +     if (ret)
>>>> +             return ret;
>>>> +
>>>> +     return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x30,
>>>> +                           (ilog2(pclk_div) << 4));
>>>> +}
>>>> +
>>>> /* download ov5640 settings to sensor through i2c */
>>>> static int ov5640_set_timings(struct ov5640_dev *sensor,
>>>>                            const struct ov5640_mode_info *mode)
>>>> @@ -1637,6 +1905,8 @@ static int ov5640_set_mode(struct ov5640_dev *sensor)
>>>>      enum ov5640_downsize_mode dn_mode, orig_dn_mode;
>>>>      bool auto_gain = sensor->ctrls.auto_gain->val == 1;
>>>>      bool auto_exp =  sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
>>>> +     unsigned long rate;
>>>> +     unsigned char bpp;
>>>>      int ret;
>>>> 
>>>>      dn_mode = mode->dn_mode;
>>>> @@ -1655,6 +1925,23 @@ static int ov5640_set_mode(struct ov5640_dev *sensor)
>>>>                      goto restore_auto_gain;
>>>>      }
>>>> 
>>>> +     /*
>>>> +      * All the formats we support have 16 bits per pixel, except for JPEG
>>>> +      * which is 8 bits per pixel.
>>>> +      */
>>>> +     bpp = sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8 ? 8 : 16;
>>>> +     rate = mode->pixel_clock * bpp;
>>>> +     if (sensor->ep.bus_type == V4L2_MBUS_CSI2) {
>>>> +             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;
>>>> +             ret = ov5640_set_dvp_pclk(sensor, rate);
>>>> +     }
>>>> +
>>>> +     if (ret < 0)
>>>> +             return 0;
>>>> +
>>>>      if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
>>>>          (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
>>>>              /*
>>>> --
>>>> 2.19.1
>>>> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 01/12] media: ov5640: Adjust the clock based on the expected rate
  2018-10-18 10:03           ` jacopo mondi
@ 2018-10-18 20:25             ` Samuel Bobrowicz
  0 siblings, 0 replies; 35+ messages in thread
From: Samuel Bobrowicz @ 2018-10-18 20:25 UTC (permalink / raw)
  To: jacopo mondi
  Cc: Maxime Ripard, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media, Thomas Petazzoni, Mylene Josserand, Hans Verkuil,
	Sakari Ailus, Hugues Fruchet, Loic Poulain, Steve Longerbeam,
	Daniel Mack



> On Oct 18, 2018, at 3:03 AM, jacopo mondi <jacopo@jmondi.org> wrote:
> 
>> On Thu, Oct 18, 2018 at 11:31:52AM +0200, Maxime Ripard wrote:
>>> On Wed, Oct 17, 2018 at 09:51:43PM +0200, jacopo mondi wrote:
>>> Hello Sam and Maxime (and other ov5640-ers :)
>>> 
>>>> On Wed, Oct 17, 2018 at 10:54:01AM -0700, Sam Bobrowicz wrote:
>>>> Hello Maxime and Jacopo (and other ov5640-ers),
>>>> 
>>>> I just submitted my version of this patch to the mailing list as RFC.
>>>> It is working on my MIPI platform. Please try it if you have time.
>>>> Hopefully we can merge these two into a single patch that doesn't
>>>> break any platforms.
>>> 
>>> Thanks, I have seen your patch but it seems to contain a lot of things
>>> already part of Maxime's series. Was this intentional?
>>> 
>>> Now the un-pleaseant part: I have just sent out my re-implementation
>>> of the MIPI clock tree configuration, based on top of Maxime's series.
>>> Both you and me have spent a looot of time on this I'm sure, and now
>>> we have two competing implementations.
>>> 
>>> I had a quick look at yours, and for sure there are things I am not
>>> taking care of (I'm thinking about the 0x4837 register that seems to
>>> be important for your platform), so I think both our implementations
>>> can benefits from a comparison. What is important to me is that both
>>> you and me don't feel like our work has been wasted, so let's try to
>>> find out a way to get the better of the two put together, and possibly
>>> applied on top of Maxime's series, so that a v5 of this will work for
>>> both MIPI and DVP interfaces. How to do that I'm not sure atm, I think
>>> other reviewers might help in that if they want to have a look at both
>>> our series :)
>> 
>> IIRC, Sam's system has never worked with the ov5640 driver, and his
>> patches now make it work.
>> 
>> Your patches on the other hand make sure that the current series
>> doesn't break existing users. So I guess we could merge your current
>> patches into the v5 of my rework, and have Sam send his work on top of
>> that.
>> 
>> Does that make sense?
> 
> It does for me, but it puts the burden on Sam to re-apply his work
> on top of [yours+mine] (which is something he would have had to do
> anyhow to have his patches accepted, as he would have had to rebase on
> top of your series).
> 
Don’t worry about it :)

> I hope to find some more time to look into his series and find out how
> hard it would be to add his changes on top of mine, and hopefully help
> with this.
> Also, testing my patches with DVP would be nice (it should not be
> affected at all, but still...)
> 
> Thanks
>   j
> 
>> 
>> Maxime
>> 
>> --
>> Maxime Ripard, Bootlin
>> Embedded Linux and Kernel engineering
>> https://bootlin.com
> 
> 

I’m fine with this approach, but it takes my ability to easily test your changes on my MIPI platform off the table. I will be around to run some manual tests on your algorithms and answer tech details about my experiments with the sensor, but it will fall on Jacobi to ensure that whatever patch you land on doesn’t introduce a regression for MIPI platforms. I can then submit a PCLK period patch on top of what you end up with, which will then put my platform in the game. 

Sam

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 05/12] media: ov5640: Compute the clock rate at runtime
  2018-10-11  9:21 ` [PATCH v4 05/12] media: ov5640: Compute the clock rate at runtime Maxime Ripard
@ 2019-02-21 16:20   ` Benoit Parrot
  2019-02-22 14:39     ` Maxime Ripard
  0 siblings, 1 reply; 35+ messages in thread
From: Benoit Parrot @ 2019-02-21 16:20 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Mauro Carvalho Chehab, Laurent Pinchart, linux-media,
	Thomas Petazzoni, Mylene Josserand, Hans Verkuil, Sakari Ailus,
	Hugues Fruchet, Loic Poulain, Samuel Bobrowicz, Steve Longerbeam,
	Daniel Mack, Jacopo Mondi

Hi Maxime,

A couple of questions,

Maxime Ripard <maxime.ripard@bootlin.com> wrote on Thu [2018-Oct-11 04:21:00 -0500]:
> The clock rate, while hardcoded until now, is actually a function of the
> resolution, framerate and bytes per pixel. Now that we have an algorithm to
> adjust our clock rate, we can select it dynamically when we change the
> mode.
> 
> This changes a bit the clock rate being used, with the following effect:
> 
> +------+------+------+------+-----+-----------------+----------------+-----------+
> | Hact | Vact | Htot | Vtot | FPS | Hardcoded clock | Computed clock | Deviation |
> +------+------+------+------+-----+-----------------+----------------+-----------+
> |  640 |  480 | 1896 | 1080 |  15 |        56000000 |       61430400 | 8.84 %    |
> |  640 |  480 | 1896 | 1080 |  30 |       112000000 |      122860800 | 8.84 %    |
> | 1024 |  768 | 1896 | 1080 |  15 |        56000000 |       61430400 | 8.84 %    |
> | 1024 |  768 | 1896 | 1080 |  30 |       112000000 |      122860800 | 8.84 %    |
> |  320 |  240 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> |  320 |  240 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> |  176 |  144 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> |  176 |  144 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> |  720 |  480 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> |  720 |  480 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> |  720 |  576 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> |  720 |  576 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> | 1280 |  720 | 1892 |  740 |  15 |        42000000 |       42002400 | 0.01 %    |
> | 1280 |  720 | 1892 |  740 |  30 |        84000000 |       84004800 | 0.01 %    |
> | 1920 | 1080 | 2500 | 1120 |  15 |        84000000 |       84000000 | 0.00 %    |
> | 1920 | 1080 | 2500 | 1120 |  30 |       168000000 |      168000000 | 0.00 %    |
> | 2592 | 1944 | 2844 | 1944 |  15 |        84000000 |      165862080 | 49.36 %   |
> +------+------+------+------+-----+-----------------+----------------+-----------+

Is the computed clock above the same for both parallel and CSI2?

I want to add controls for PIXEL_RATE and LINK_FREQ, would you have any
quick pointer on taking the computed clock and translating that into the
PIXEL_RATE and LINK_FREQ values?

I am trying to use this sensor with TI CAL driver which at the moment uses
the PIXEL_RATE values in order to compute ths_settle and ths_term values
needed to program the DPHY properly. This is similar in behavior as the way
omap3isp relies on this info as well.

Regards,
Benoit 

> 
> Only the 640x480, 1024x768 and 2592x1944 modes are significantly affected
> by the new formula.
> 
> In this case, 640x480 and 1024x768 are actually fixed by this change.
> Indeed, the sensor was sending data at, for example, 27.33fps instead of
> 30fps. This is -9%, which is roughly what we're seeing in the array.
> Testing these modes with the new clock setup actually fix that error, and
> data are now sent at around 30fps.
> 
> 2592x1944, on the other hand, is probably due to the fact that this mode
> can only be used using MIPI-CSI2, in a two lane mode, and never really
> tested with a DVP bus.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> ---
>  drivers/media/i2c/ov5640.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> index 5114d401b8eb..34eaa9dd5237 100644
> --- a/drivers/media/i2c/ov5640.c
> +++ b/drivers/media/i2c/ov5640.c
> @@ -1915,7 +1915,8 @@ static int ov5640_set_mode(struct ov5640_dev *sensor)
>  	 * which is 8 bits per pixel.
>  	 */
>  	bpp = sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8 ? 8 : 16;
> -	rate = mode->pixel_clock * bpp;
> +	rate = mode->vtot * mode->htot * bpp;
> +	rate *= ov5640_framerates[sensor->current_fr];
>  	if (sensor->ep.bus_type == V4L2_MBUS_CSI2) {
>  		rate = rate / sensor->ep.bus.mipi_csi2.num_data_lanes;
>  		ret = ov5640_set_mipi_pclk(sensor, rate);
> -- 
> 2.19.1
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 05/12] media: ov5640: Compute the clock rate at runtime
  2019-02-21 16:20   ` Benoit Parrot
@ 2019-02-22 14:39     ` Maxime Ripard
  2019-02-22 14:54       ` Benoit Parrot
  0 siblings, 1 reply; 35+ messages in thread
From: Maxime Ripard @ 2019-02-22 14:39 UTC (permalink / raw)
  To: Benoit Parrot
  Cc: Mauro Carvalho Chehab, Laurent Pinchart, linux-media,
	Thomas Petazzoni, Mylene Josserand, Hans Verkuil, Sakari Ailus,
	Hugues Fruchet, Loic Poulain, Samuel Bobrowicz, Steve Longerbeam,
	Daniel Mack, Jacopo Mondi

[-- Attachment #1: Type: text/plain, Size: 3126 bytes --]

On Thu, Feb 21, 2019 at 10:20:20AM -0600, Benoit Parrot wrote:
> Hi Maxime,
> 
> A couple of questions,
> 
> Maxime Ripard <maxime.ripard@bootlin.com> wrote on Thu [2018-Oct-11 04:21:00 -0500]:
> > The clock rate, while hardcoded until now, is actually a function of the
> > resolution, framerate and bytes per pixel. Now that we have an algorithm to
> > adjust our clock rate, we can select it dynamically when we change the
> > mode.
> > 
> > This changes a bit the clock rate being used, with the following effect:
> > 
> > +------+------+------+------+-----+-----------------+----------------+-----------+
> > | Hact | Vact | Htot | Vtot | FPS | Hardcoded clock | Computed clock | Deviation |
> > +------+------+------+------+-----+-----------------+----------------+-----------+
> > |  640 |  480 | 1896 | 1080 |  15 |        56000000 |       61430400 | 8.84 %    |
> > |  640 |  480 | 1896 | 1080 |  30 |       112000000 |      122860800 | 8.84 %    |
> > | 1024 |  768 | 1896 | 1080 |  15 |        56000000 |       61430400 | 8.84 %    |
> > | 1024 |  768 | 1896 | 1080 |  30 |       112000000 |      122860800 | 8.84 %    |
> > |  320 |  240 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > |  320 |  240 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > |  176 |  144 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > |  176 |  144 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > |  720 |  480 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > |  720 |  480 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > |  720 |  576 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > |  720 |  576 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > | 1280 |  720 | 1892 |  740 |  15 |        42000000 |       42002400 | 0.01 %    |
> > | 1280 |  720 | 1892 |  740 |  30 |        84000000 |       84004800 | 0.01 %    |
> > | 1920 | 1080 | 2500 | 1120 |  15 |        84000000 |       84000000 | 0.00 %    |
> > | 1920 | 1080 | 2500 | 1120 |  30 |       168000000 |      168000000 | 0.00 %    |
> > | 2592 | 1944 | 2844 | 1944 |  15 |        84000000 |      165862080 | 49.36 %   |
> > +------+------+------+------+-----+-----------------+----------------+-----------+
> 
> Is the computed clock above the same for both parallel and CSI2?
> 
> I want to add controls for PIXEL_RATE and LINK_FREQ, would you have any
> quick pointer on taking the computed clock and translating that into the
> PIXEL_RATE and LINK_FREQ values?
> 
> I am trying to use this sensor with TI CAL driver which at the moment uses
> the PIXEL_RATE values in order to compute ths_settle and ths_term values
> needed to program the DPHY properly. This is similar in behavior as the way
> omap3isp relies on this info as well.

I haven't looked that much into the csi-2 case, but the pixel rate
should be the same at least.

Maxime

-- 
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 05/12] media: ov5640: Compute the clock rate at runtime
  2019-02-22 14:39     ` Maxime Ripard
@ 2019-02-22 14:54       ` Benoit Parrot
  2019-02-22 15:04         ` Maxime Ripard
  0 siblings, 1 reply; 35+ messages in thread
From: Benoit Parrot @ 2019-02-22 14:54 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Mauro Carvalho Chehab, Laurent Pinchart, linux-media,
	Thomas Petazzoni, Mylene Josserand, Hans Verkuil, Sakari Ailus,
	Hugues Fruchet, Loic Poulain, Samuel Bobrowicz, Steve Longerbeam,
	Daniel Mack, Jacopo Mondi

Maxime Ripard <maxime.ripard@bootlin.com> wrote on Fri [2019-Feb-22 15:39:59 +0100]:
> On Thu, Feb 21, 2019 at 10:20:20AM -0600, Benoit Parrot wrote:
> > Hi Maxime,
> > 
> > A couple of questions,
> > 
> > Maxime Ripard <maxime.ripard@bootlin.com> wrote on Thu [2018-Oct-11 04:21:00 -0500]:
> > > The clock rate, while hardcoded until now, is actually a function of the
> > > resolution, framerate and bytes per pixel. Now that we have an algorithm to
> > > adjust our clock rate, we can select it dynamically when we change the
> > > mode.
> > > 
> > > This changes a bit the clock rate being used, with the following effect:
> > > 
> > > +------+------+------+------+-----+-----------------+----------------+-----------+
> > > | Hact | Vact | Htot | Vtot | FPS | Hardcoded clock | Computed clock | Deviation |
> > > +------+------+------+------+-----+-----------------+----------------+-----------+
> > > |  640 |  480 | 1896 | 1080 |  15 |        56000000 |       61430400 | 8.84 %    |
> > > |  640 |  480 | 1896 | 1080 |  30 |       112000000 |      122860800 | 8.84 %    |
> > > | 1024 |  768 | 1896 | 1080 |  15 |        56000000 |       61430400 | 8.84 %    |
> > > | 1024 |  768 | 1896 | 1080 |  30 |       112000000 |      122860800 | 8.84 %    |
> > > |  320 |  240 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > > |  320 |  240 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > > |  176 |  144 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > > |  176 |  144 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > > |  720 |  480 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > > |  720 |  480 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > > |  720 |  576 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > > |  720 |  576 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > > | 1280 |  720 | 1892 |  740 |  15 |        42000000 |       42002400 | 0.01 %    |
> > > | 1280 |  720 | 1892 |  740 |  30 |        84000000 |       84004800 | 0.01 %    |
> > > | 1920 | 1080 | 2500 | 1120 |  15 |        84000000 |       84000000 | 0.00 %    |
> > > | 1920 | 1080 | 2500 | 1120 |  30 |       168000000 |      168000000 | 0.00 %    |
> > > | 2592 | 1944 | 2844 | 1944 |  15 |        84000000 |      165862080 | 49.36 %   |
> > > +------+------+------+------+-----+-----------------+----------------+-----------+
> > 
> > Is the computed clock above the same for both parallel and CSI2?
> > 
> > I want to add controls for PIXEL_RATE and LINK_FREQ, would you have any
> > quick pointer on taking the computed clock and translating that into the
> > PIXEL_RATE and LINK_FREQ values?
> > 
> > I am trying to use this sensor with TI CAL driver which at the moment uses
> > the PIXEL_RATE values in order to compute ths_settle and ths_term values
> > needed to program the DPHY properly. This is similar in behavior as the way
> > omap3isp relies on this info as well.
> 
> I haven't looked that much into the csi-2 case, but the pixel rate
> should be the same at least.

I'll have to study the way the computed clock is actually calculated for
either case, but if they yield the same number then I would be surprised
that the pixel rate would be the same as in parallel mode you get 8 data
bits per clock whereas in CSI2 using 2 data lanes you get 4 data bits per
clock.

So just to be certain here the "Computed clock" column above would be the
pixel clock frequency?

Benoit

> 
> Maxime
> 
> -- 
> Maxime Ripard, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com



^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 05/12] media: ov5640: Compute the clock rate at runtime
  2019-02-22 14:54       ` Benoit Parrot
@ 2019-02-22 15:04         ` Maxime Ripard
  2019-02-25  9:21           ` Jacopo Mondi
  0 siblings, 1 reply; 35+ messages in thread
From: Maxime Ripard @ 2019-02-22 15:04 UTC (permalink / raw)
  To: Benoit Parrot
  Cc: Mauro Carvalho Chehab, Laurent Pinchart, linux-media,
	Thomas Petazzoni, Mylene Josserand, Hans Verkuil, Sakari Ailus,
	Hugues Fruchet, Loic Poulain, Samuel Bobrowicz, Steve Longerbeam,
	Daniel Mack, Jacopo Mondi

[-- Attachment #1: Type: text/plain, Size: 4069 bytes --]

On Fri, Feb 22, 2019 at 08:54:56AM -0600, Benoit Parrot wrote:
> Maxime Ripard <maxime.ripard@bootlin.com> wrote on Fri [2019-Feb-22 15:39:59 +0100]:
> > On Thu, Feb 21, 2019 at 10:20:20AM -0600, Benoit Parrot wrote:
> > > Hi Maxime,
> > > 
> > > A couple of questions,
> > > 
> > > Maxime Ripard <maxime.ripard@bootlin.com> wrote on Thu [2018-Oct-11 04:21:00 -0500]:
> > > > The clock rate, while hardcoded until now, is actually a function of the
> > > > resolution, framerate and bytes per pixel. Now that we have an algorithm to
> > > > adjust our clock rate, we can select it dynamically when we change the
> > > > mode.
> > > > 
> > > > This changes a bit the clock rate being used, with the following effect:
> > > > 
> > > > +------+------+------+------+-----+-----------------+----------------+-----------+
> > > > | Hact | Vact | Htot | Vtot | FPS | Hardcoded clock | Computed clock | Deviation |
> > > > +------+------+------+------+-----+-----------------+----------------+-----------+
> > > > |  640 |  480 | 1896 | 1080 |  15 |        56000000 |       61430400 | 8.84 %    |
> > > > |  640 |  480 | 1896 | 1080 |  30 |       112000000 |      122860800 | 8.84 %    |
> > > > | 1024 |  768 | 1896 | 1080 |  15 |        56000000 |       61430400 | 8.84 %    |
> > > > | 1024 |  768 | 1896 | 1080 |  30 |       112000000 |      122860800 | 8.84 %    |
> > > > |  320 |  240 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > > > |  320 |  240 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > > > |  176 |  144 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > > > |  176 |  144 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > > > |  720 |  480 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > > > |  720 |  480 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > > > |  720 |  576 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > > > |  720 |  576 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > > > | 1280 |  720 | 1892 |  740 |  15 |        42000000 |       42002400 | 0.01 %    |
> > > > | 1280 |  720 | 1892 |  740 |  30 |        84000000 |       84004800 | 0.01 %    |
> > > > | 1920 | 1080 | 2500 | 1120 |  15 |        84000000 |       84000000 | 0.00 %    |
> > > > | 1920 | 1080 | 2500 | 1120 |  30 |       168000000 |      168000000 | 0.00 %    |
> > > > | 2592 | 1944 | 2844 | 1944 |  15 |        84000000 |      165862080 | 49.36 %   |
> > > > +------+------+------+------+-----+-----------------+----------------+-----------+
> > > 
> > > Is the computed clock above the same for both parallel and CSI2?
> > > 
> > > I want to add controls for PIXEL_RATE and LINK_FREQ, would you have any
> > > quick pointer on taking the computed clock and translating that into the
> > > PIXEL_RATE and LINK_FREQ values?
> > > 
> > > I am trying to use this sensor with TI CAL driver which at the moment uses
> > > the PIXEL_RATE values in order to compute ths_settle and ths_term values
> > > needed to program the DPHY properly. This is similar in behavior as the way
> > > omap3isp relies on this info as well.
> > 
> > I haven't looked that much into the csi-2 case, but the pixel rate
> > should be the same at least.
> 
> I'll have to study the way the computed clock is actually calculated for
> either case, but if they yield the same number then I would be surprised
> that the pixel rate would be the same as in parallel mode you get 8 data
> bits per clock whereas in CSI2 using 2 data lanes you get 4 data bits per
> clock.

The bus rate will be different, but the pixel rate is the same: you
have as many pixels per frames and as many frames per seconds in the
parallel and CSI cases.

> So just to be certain here the "Computed clock" column above would be the
> pixel clock frequency?

it is

Maxime

-- 
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 05/12] media: ov5640: Compute the clock rate at runtime
  2019-02-22 15:04         ` Maxime Ripard
@ 2019-02-25  9:21           ` Jacopo Mondi
  2019-02-25 12:15             ` Jacopo Mondi
  2019-02-25 13:06             ` Maxime Ripard
  0 siblings, 2 replies; 35+ messages in thread
From: Jacopo Mondi @ 2019-02-25  9:21 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Benoit Parrot, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media, Thomas Petazzoni, Mylene Josserand, Hans Verkuil,
	Sakari Ailus, Hugues Fruchet, Loic Poulain, Samuel Bobrowicz,
	Steve Longerbeam, Daniel Mack

[-- Attachment #1: Type: text/plain, Size: 5338 bytes --]

Hello Maxime, Benoit,
  sorry for chiming in, but I'm a bit confused...

On Fri, Feb 22, 2019 at 04:04:21PM +0100, Maxime Ripard wrote:
> On Fri, Feb 22, 2019 at 08:54:56AM -0600, Benoit Parrot wrote:
> > Maxime Ripard <maxime.ripard@bootlin.com> wrote on Fri [2019-Feb-22 15:39:59 +0100]:
> > > On Thu, Feb 21, 2019 at 10:20:20AM -0600, Benoit Parrot wrote:
> > > > Hi Maxime,
> > > >
> > > > A couple of questions,
> > > >
> > > > Maxime Ripard <maxime.ripard@bootlin.com> wrote on Thu [2018-Oct-11 04:21:00 -0500]:
> > > > > The clock rate, while hardcoded until now, is actually a function of the
> > > > > resolution, framerate and bytes per pixel. Now that we have an algorithm to
> > > > > adjust our clock rate, we can select it dynamically when we change the
> > > > > mode.
> > > > >
> > > > > This changes a bit the clock rate being used, with the following effect:
> > > > >
> > > > > +------+------+------+------+-----+-----------------+----------------+-----------+
> > > > > | Hact | Vact | Htot | Vtot | FPS | Hardcoded clock | Computed clock | Deviation |
> > > > > +------+------+------+------+-----+-----------------+----------------+-----------+
> > > > > |  640 |  480 | 1896 | 1080 |  15 |        56000000 |       61430400 | 8.84 %    |
> > > > > |  640 |  480 | 1896 | 1080 |  30 |       112000000 |      122860800 | 8.84 %    |
> > > > > | 1024 |  768 | 1896 | 1080 |  15 |        56000000 |       61430400 | 8.84 %    |
> > > > > | 1024 |  768 | 1896 | 1080 |  30 |       112000000 |      122860800 | 8.84 %    |
> > > > > |  320 |  240 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > > > > |  320 |  240 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > > > > |  176 |  144 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > > > > |  176 |  144 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > > > > |  720 |  480 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > > > > |  720 |  480 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > > > > |  720 |  576 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > > > > |  720 |  576 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > > > > | 1280 |  720 | 1892 |  740 |  15 |        42000000 |       42002400 | 0.01 %    |
> > > > > | 1280 |  720 | 1892 |  740 |  30 |        84000000 |       84004800 | 0.01 %    |
> > > > > | 1920 | 1080 | 2500 | 1120 |  15 |        84000000 |       84000000 | 0.00 %    |
> > > > > | 1920 | 1080 | 2500 | 1120 |  30 |       168000000 |      168000000 | 0.00 %    |
> > > > > | 2592 | 1944 | 2844 | 1944 |  15 |        84000000 |      165862080 | 49.36 %   |
> > > > > +------+------+------+------+-----+-----------------+----------------+-----------+
> > > >
> > > > Is the computed clock above the same for both parallel and CSI2?
> > > >
> > > > I want to add controls for PIXEL_RATE and LINK_FREQ, would you have any
> > > > quick pointer on taking the computed clock and translating that into the
> > > > PIXEL_RATE and LINK_FREQ values?
> > > >
> > > > I am trying to use this sensor with TI CAL driver which at the moment uses
> > > > the PIXEL_RATE values in order to compute ths_settle and ths_term values
> > > > needed to program the DPHY properly. This is similar in behavior as the way
> > > > omap3isp relies on this info as well.
> > >
> > > I haven't looked that much into the csi-2 case, but the pixel rate
> > > should be the same at least.
> >
> > I'll have to study the way the computed clock is actually calculated for
> > either case, but if they yield the same number then I would be surprised
> > that the pixel rate would be the same as in parallel mode you get 8 data
> > bits per clock whereas in CSI2 using 2 data lanes you get 4 data bits per
> > clock.
>
> The bus rate will be different, but the pixel rate is the same: you
> have as many pixels per frames and as many frames per seconds in the
> parallel and CSI cases.
>

I agree with that, but..

> > So just to be certain here the "Computed clock" column above would be the
> > pixel clock frequency?
>
> it is

...it seems to me the Computed clock column is actually the "byte clock".

From a simple calculation for the 640x480@15FPS case:
"Computed clock" = 1896 * 1080 * 15 * 2 = 61430400

While, in my understanding, the pixel clock would just be
pixel_clock = HTOT * VTOT * FPS = 1896 * 1080 * 15 = 30715200

So I suspect the "* 2" there is the number of bytes per pixel.

That would match what's also reported here
file:///home/jmondi/project/renesas/linux/linux-build/Documentation/output/media/kapi/csi2.html?highlight=link_freq

Where:
link_freq = (pixel_rate * bpp) / (2 * nr_lanes)

So if I were to calculate PIXEL_RATE and LINK_FREQ in this driver,
that would be:
PIXEL_RATE = mode->vtot * mode->htot * ov5640_framerates[sensor->current_fr];
LINK_FREQ = PIXEL_RATE * 16 / ( 2 * sensor->ep.bus.mipi_csi2.num_data_lanes);
(assuming, as the driver does now, all formats have 16bpp)

Does this match your understanding as well?

Thanks
  j

>
> Maxime
>
> --
> Maxime Ripard, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com



[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 05/12] media: ov5640: Compute the clock rate at runtime
  2019-02-25  9:21           ` Jacopo Mondi
@ 2019-02-25 12:15             ` Jacopo Mondi
  2019-02-25 13:06             ` Maxime Ripard
  1 sibling, 0 replies; 35+ messages in thread
From: Jacopo Mondi @ 2019-02-25 12:15 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Benoit Parrot, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media, Thomas Petazzoni, Mylene Josserand, Hans Verkuil,
	Sakari Ailus, Hugues Fruchet, Loic Poulain, Samuel Bobrowicz,
	Steve Longerbeam, Daniel Mack

[-- Attachment #1: Type: text/plain, Size: 5706 bytes --]

Sorry again,

On Mon, Feb 25, 2019 at 10:21:51AM +0100, Jacopo Mondi wrote:
> Hello Maxime, Benoit,
>   sorry for chiming in, but I'm a bit confused...
>
> On Fri, Feb 22, 2019 at 04:04:21PM +0100, Maxime Ripard wrote:
> > On Fri, Feb 22, 2019 at 08:54:56AM -0600, Benoit Parrot wrote:
> > > Maxime Ripard <maxime.ripard@bootlin.com> wrote on Fri [2019-Feb-22 15:39:59 +0100]:
> > > > On Thu, Feb 21, 2019 at 10:20:20AM -0600, Benoit Parrot wrote:
> > > > > Hi Maxime,
> > > > >
> > > > > A couple of questions,
> > > > >
> > > > > Maxime Ripard <maxime.ripard@bootlin.com> wrote on Thu [2018-Oct-11 04:21:00 -0500]:
> > > > > > The clock rate, while hardcoded until now, is actually a function of the
> > > > > > resolution, framerate and bytes per pixel. Now that we have an algorithm to
> > > > > > adjust our clock rate, we can select it dynamically when we change the
> > > > > > mode.
> > > > > >
> > > > > > This changes a bit the clock rate being used, with the following effect:
> > > > > >
> > > > > > +------+------+------+------+-----+-----------------+----------------+-----------+
> > > > > > | Hact | Vact | Htot | Vtot | FPS | Hardcoded clock | Computed clock | Deviation |
> > > > > > +------+------+------+------+-----+-----------------+----------------+-----------+
> > > > > > |  640 |  480 | 1896 | 1080 |  15 |        56000000 |       61430400 | 8.84 %    |
> > > > > > |  640 |  480 | 1896 | 1080 |  30 |       112000000 |      122860800 | 8.84 %    |
> > > > > > | 1024 |  768 | 1896 | 1080 |  15 |        56000000 |       61430400 | 8.84 %    |
> > > > > > | 1024 |  768 | 1896 | 1080 |  30 |       112000000 |      122860800 | 8.84 %    |
> > > > > > |  320 |  240 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > > > > > |  320 |  240 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > > > > > |  176 |  144 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > > > > > |  176 |  144 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > > > > > |  720 |  480 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > > > > > |  720 |  480 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > > > > > |  720 |  576 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > > > > > |  720 |  576 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > > > > > | 1280 |  720 | 1892 |  740 |  15 |        42000000 |       42002400 | 0.01 %    |
> > > > > > | 1280 |  720 | 1892 |  740 |  30 |        84000000 |       84004800 | 0.01 %    |
> > > > > > | 1920 | 1080 | 2500 | 1120 |  15 |        84000000 |       84000000 | 0.00 %    |
> > > > > > | 1920 | 1080 | 2500 | 1120 |  30 |       168000000 |      168000000 | 0.00 %    |
> > > > > > | 2592 | 1944 | 2844 | 1944 |  15 |        84000000 |      165862080 | 49.36 %   |
> > > > > > +------+------+------+------+-----+-----------------+----------------+-----------+
> > > > >
> > > > > Is the computed clock above the same for both parallel and CSI2?
> > > > >
> > > > > I want to add controls for PIXEL_RATE and LINK_FREQ, would you have any
> > > > > quick pointer on taking the computed clock and translating that into the
> > > > > PIXEL_RATE and LINK_FREQ values?
> > > > >
> > > > > I am trying to use this sensor with TI CAL driver which at the moment uses
> > > > > the PIXEL_RATE values in order to compute ths_settle and ths_term values
> > > > > needed to program the DPHY properly. This is similar in behavior as the way
> > > > > omap3isp relies on this info as well.
> > > >
> > > > I haven't looked that much into the csi-2 case, but the pixel rate
> > > > should be the same at least.
> > >
> > > I'll have to study the way the computed clock is actually calculated for
> > > either case, but if they yield the same number then I would be surprised
> > > that the pixel rate would be the same as in parallel mode you get 8 data
> > > bits per clock whereas in CSI2 using 2 data lanes you get 4 data bits per
> > > clock.
> >
> > The bus rate will be different, but the pixel rate is the same: you
> > have as many pixels per frames and as many frames per seconds in the
> > parallel and CSI cases.
> >
>
> I agree with that, but..
>
> > > So just to be certain here the "Computed clock" column above would be the
> > > pixel clock frequency?
> >
> > it is
>
> ...it seems to me the Computed clock column is actually the "byte clock".
>
> From a simple calculation for the 640x480@15FPS case:
> "Computed clock" = 1896 * 1080 * 15 * 2 = 61430400
>
> While, in my understanding, the pixel clock would just be
> pixel_clock = HTOT * VTOT * FPS = 1896 * 1080 * 15 = 30715200
>
> So I suspect the "* 2" there is the number of bytes per pixel.
>
> That would match what's also reported here
> file:///home/jmondi/project/renesas/linux/linux-build/Documentation/output/media/kapi/csi2.html?highlight=link_freq
>

Of course that is a link to the local copy of the documentation,
what I actually meant to link was:
https://www.kernel.org/doc/html/latest/media/kapi/csi2.html?highlight=link_freq

Sorry!
   j


> Where:
> link_freq = (pixel_rate * bpp) / (2 * nr_lanes)
>
> So if I were to calculate PIXEL_RATE and LINK_FREQ in this driver,
> that would be:
> PIXEL_RATE = mode->vtot * mode->htot * ov5640_framerates[sensor->current_fr];
> LINK_FREQ = PIXEL_RATE * 16 / ( 2 * sensor->ep.bus.mipi_csi2.num_data_lanes);
> (assuming, as the driver does now, all formats have 16bpp)
>
> Does this match your understanding as well?
>
> Thanks
>   j
>
> >
> > Maxime
> >
> > --
> > Maxime Ripard, Bootlin
> > Embedded Linux and Kernel engineering
> > https://bootlin.com
>
>



[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4 05/12] media: ov5640: Compute the clock rate at runtime
  2019-02-25  9:21           ` Jacopo Mondi
  2019-02-25 12:15             ` Jacopo Mondi
@ 2019-02-25 13:06             ` Maxime Ripard
  1 sibling, 0 replies; 35+ messages in thread
From: Maxime Ripard @ 2019-02-25 13:06 UTC (permalink / raw)
  To: Jacopo Mondi
  Cc: Benoit Parrot, Mauro Carvalho Chehab, Laurent Pinchart,
	linux-media, Thomas Petazzoni, Mylene Josserand, Hans Verkuil,
	Sakari Ailus, Hugues Fruchet, Loic Poulain, Samuel Bobrowicz,
	Steve Longerbeam, Daniel Mack

[-- Attachment #1: Type: text/plain, Size: 5606 bytes --]

On Mon, Feb 25, 2019 at 10:21:51AM +0100, Jacopo Mondi wrote:
> Hello Maxime, Benoit,
>   sorry for chiming in, but I'm a bit confused...
> 
> On Fri, Feb 22, 2019 at 04:04:21PM +0100, Maxime Ripard wrote:
> > On Fri, Feb 22, 2019 at 08:54:56AM -0600, Benoit Parrot wrote:
> > > Maxime Ripard <maxime.ripard@bootlin.com> wrote on Fri [2019-Feb-22 15:39:59 +0100]:
> > > > On Thu, Feb 21, 2019 at 10:20:20AM -0600, Benoit Parrot wrote:
> > > > > Hi Maxime,
> > > > >
> > > > > A couple of questions,
> > > > >
> > > > > Maxime Ripard <maxime.ripard@bootlin.com> wrote on Thu [2018-Oct-11 04:21:00 -0500]:
> > > > > > The clock rate, while hardcoded until now, is actually a function of the
> > > > > > resolution, framerate and bytes per pixel. Now that we have an algorithm to
> > > > > > adjust our clock rate, we can select it dynamically when we change the
> > > > > > mode.
> > > > > >
> > > > > > This changes a bit the clock rate being used, with the following effect:
> > > > > >
> > > > > > +------+------+------+------+-----+-----------------+----------------+-----------+
> > > > > > | Hact | Vact | Htot | Vtot | FPS | Hardcoded clock | Computed clock | Deviation |
> > > > > > +------+------+------+------+-----+-----------------+----------------+-----------+
> > > > > > |  640 |  480 | 1896 | 1080 |  15 |        56000000 |       61430400 | 8.84 %    |
> > > > > > |  640 |  480 | 1896 | 1080 |  30 |       112000000 |      122860800 | 8.84 %    |
> > > > > > | 1024 |  768 | 1896 | 1080 |  15 |        56000000 |       61430400 | 8.84 %    |
> > > > > > | 1024 |  768 | 1896 | 1080 |  30 |       112000000 |      122860800 | 8.84 %    |
> > > > > > |  320 |  240 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > > > > > |  320 |  240 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > > > > > |  176 |  144 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > > > > > |  176 |  144 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > > > > > |  720 |  480 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > > > > > |  720 |  480 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > > > > > |  720 |  576 | 1896 |  984 |  15 |        56000000 |       55969920 | 0.05 %    |
> > > > > > |  720 |  576 | 1896 |  984 |  30 |       112000000 |      111939840 | 0.05 %    |
> > > > > > | 1280 |  720 | 1892 |  740 |  15 |        42000000 |       42002400 | 0.01 %    |
> > > > > > | 1280 |  720 | 1892 |  740 |  30 |        84000000 |       84004800 | 0.01 %    |
> > > > > > | 1920 | 1080 | 2500 | 1120 |  15 |        84000000 |       84000000 | 0.00 %    |
> > > > > > | 1920 | 1080 | 2500 | 1120 |  30 |       168000000 |      168000000 | 0.00 %    |
> > > > > > | 2592 | 1944 | 2844 | 1944 |  15 |        84000000 |      165862080 | 49.36 %   |
> > > > > > +------+------+------+------+-----+-----------------+----------------+-----------+
> > > > >
> > > > > Is the computed clock above the same for both parallel and CSI2?
> > > > >
> > > > > I want to add controls for PIXEL_RATE and LINK_FREQ, would you have any
> > > > > quick pointer on taking the computed clock and translating that into the
> > > > > PIXEL_RATE and LINK_FREQ values?
> > > > >
> > > > > I am trying to use this sensor with TI CAL driver which at the moment uses
> > > > > the PIXEL_RATE values in order to compute ths_settle and ths_term values
> > > > > needed to program the DPHY properly. This is similar in behavior as the way
> > > > > omap3isp relies on this info as well.
> > > >
> > > > I haven't looked that much into the csi-2 case, but the pixel rate
> > > > should be the same at least.
> > >
> > > I'll have to study the way the computed clock is actually calculated for
> > > either case, but if they yield the same number then I would be surprised
> > > that the pixel rate would be the same as in parallel mode you get 8 data
> > > bits per clock whereas in CSI2 using 2 data lanes you get 4 data bits per
> > > clock.
> >
> > The bus rate will be different, but the pixel rate is the same: you
> > have as many pixels per frames and as many frames per seconds in the
> > parallel and CSI cases.
> >
> 
> I agree with that, but..
> 
> > > So just to be certain here the "Computed clock" column above would be the
> > > pixel clock frequency?
> >
> > it is
> 
> ...it seems to me the Computed clock column is actually the "byte clock".
> 
> From a simple calculation for the 640x480@15FPS case:
> "Computed clock" = 1896 * 1080 * 15 * 2 = 61430400
> 
> While, in my understanding, the pixel clock would just be
> pixel_clock = HTOT * VTOT * FPS = 1896 * 1080 * 15 = 30715200
> 
> So I suspect the "* 2" there is the number of bytes per pixel.
> 
> That would match what's also reported here
> file:///home/jmondi/project/renesas/linux/linux-build/Documentation/output/media/kapi/csi2.html?highlight=link_freq
> 
> Where:
> link_freq = (pixel_rate * bpp) / (2 * nr_lanes)
> 
> So if I were to calculate PIXEL_RATE and LINK_FREQ in this driver,
> that would be:
> PIXEL_RATE = mode->vtot * mode->htot * ov5640_framerates[sensor->current_fr];
> LINK_FREQ = PIXEL_RATE * 16 / ( 2 * sensor->ep.bus.mipi_csi2.num_data_lanes);
> (assuming, as the driver does now, all formats have 16bpp)
> 
> Does this match your understanding as well?

You're totally right, sorry about that :)

Maxime

-- 
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2019-02-25 13:06 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-10-11  9:20 [PATCH v4 00/12] media: ov5640: Misc cleanup and improvements Maxime Ripard
2018-10-11  9:20 ` [PATCH v4 01/12] media: ov5640: Adjust the clock based on the expected rate Maxime Ripard
2018-10-15 14:05   ` Hugues FRUCHET
2018-10-16 16:54   ` jacopo mondi
2018-10-17 17:54     ` Sam Bobrowicz
2018-10-17 19:51       ` jacopo mondi
2018-10-18  9:31         ` Maxime Ripard
2018-10-18 10:03           ` jacopo mondi
2018-10-18 20:25             ` Samuel Bobrowicz
2018-10-18 20:15         ` Samuel Bobrowicz
2018-10-18  9:29     ` Maxime Ripard
2018-10-18 13:46       ` jacopo mondi
2018-10-18 16:56         ` Maxime Ripard
2018-10-11  9:20 ` [PATCH v4 02/12] media: ov5640: Remove the clocks registers initialization Maxime Ripard
2018-10-11  9:20 ` [PATCH v4 03/12] media: ov5640: Remove redundant defines Maxime Ripard
2018-10-11  9:20 ` [PATCH v4 04/12] media: ov5640: Remove redundant register setup Maxime Ripard
2018-10-11  9:21 ` [PATCH v4 05/12] media: ov5640: Compute the clock rate at runtime Maxime Ripard
2019-02-21 16:20   ` Benoit Parrot
2019-02-22 14:39     ` Maxime Ripard
2019-02-22 14:54       ` Benoit Parrot
2019-02-22 15:04         ` Maxime Ripard
2019-02-25  9:21           ` Jacopo Mondi
2019-02-25 12:15             ` Jacopo Mondi
2019-02-25 13:06             ` Maxime Ripard
2018-10-11  9:21 ` [PATCH v4 06/12] media: ov5640: Remove pixel clock rates Maxime Ripard
2018-10-11  9:21 ` [PATCH v4 07/12] media: ov5640: Enhance FPS handling Maxime Ripard
2018-10-11  9:21 ` [PATCH v4 08/12] media: ov5640: Make the return rate type more explicit Maxime Ripard
2018-10-11  9:21 ` [PATCH v4 09/12] media: ov5640: Make the FPS clamping / rounding more extendable Maxime Ripard
2018-10-11  9:21 ` [PATCH v4 10/12] media: ov5640: Add 60 fps support Maxime Ripard
2018-10-11  9:21 ` [PATCH v4 11/12] media: ov5640: Remove duplicate auto-exposure setup Maxime Ripard
2018-10-11  9:21 ` [PATCH v4 12/12] ov5640: Enforce a mode change when changing the framerate Maxime Ripard
2018-10-15 13:57   ` Hugues FRUCHET
2018-10-16  7:10     ` Maxime Ripard
2018-10-16  8:45       ` Hugues FRUCHET
2018-10-16 15:57 ` [PATCH v4 00/12] media: ov5640: Misc cleanup and improvements jacopo mondi

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.