Message ID | 20181011092107.30715-2-maxime.ripard@bootlin.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | media: ov5640: Misc cleanup and improvements | expand |
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.
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 >
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 > >
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 > > >
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
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
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
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
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
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 >>>>
> 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
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)) { /*
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(-)