Hi Jacopo, On Wed, Nov 14, 2018 at 08:48:47PM +0100, jacopo mondi wrote: > On Tue, Nov 13, 2018 at 02:03:15PM +0100, 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 > > As you've squashed in my MIPI CSI-2 fixes, do you think it is > appropriate adding my So-b tag here? Yeah, I'll add your co-developped-by tag as well, if that's ok. > > +/* > > + * 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. > > + */ > > I forgot to fix this comment in my patches you squahed in here (the > value now ranges from 1 to 16 Ok. > > +#define OV5640_SYSDIV_MIN 1 > > +#define OV5640_SYSDIV_MAX 16 > > + > > +/* > > + * 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 > > +#define OV5640_PLL_CTRL3_PLL_ROOT_DIV_2 BIT(4) > > + > > +/* > > + * We only supports 8-bit formats at the moment > > + */ > > +#define OV5640_BIT_DIV 2 > > +#define OV5640_PLL_CTRL0_MIPI_MODE_8BIT 0x08 > > + > > +/* > > + * 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 > > +#define OV5640_PLL_SYS_ROOT_DIVIDER_BYPASS 0x00 > > + > > +static unsigned long ov5640_compute_sys_clk(struct ov5640_dev *sensor, > > + u8 pll_prediv, u8 pll_mult, > > + u8 sysdiv) > > +{ > > + unsigned long sysclk = sensor->xclk_freq / pll_prediv * pll_mult; > > + > > + /* PLL1 output cannot exceed 1GHz. */ > > + if (sysclk / 1000000 > 1000) > > + return 0; > > + > > + return sysclk / sysdiv; > > +} > > That's better, but Sam's version is even better. I plan to pick some > part of his patch and apply them on top of this (for this and a few > other things I'm pointing out a here below). How would you like to > have those updates? Incremental patches on top of this series once it > gets merged (and it can now be merged, since it works for both CSI-2 > and DVP), or would you like to receive those patches, squash them in > and re-send? The first solution would be better, having to constantly piling up patches in a series is a very efficient way to not get anything merged. > > + > > +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); > > + > > + /* > > + * We have reached the maximum allowed PLL1 output, > > + * increase sysdiv. > > + */ > > + if (!rate) > > + break; > > + > > + /* > > + * Prefer rates above the expected clock rate than > > + * below, even if that means being less precise. > > + */ > > + 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; > > +} > > + > > +/* > > + * ov5640_set_mipi_pclk() - Calculate the clock tree configuration values > > + * for the MIPI CSI-2 output. > > + * > > + * @rate: The requested bandwidth in bytes per second. > > + * It is calculated as: HTOT * VTOT * FPS * bpp > > + * > > Almost. This is actually the bandwidth per lane. My bad, I need to update > this whole comment block. Can you send a patch? I'll squash it in. > > + * This function use the requested bandwidth to calculate: > > + * - sample_rate = bandwidth / bpp; > > + * - mipi_clk = bandwidth / num_lanes / 2; ( / 2 for CSI-2 DDR) > > + * > > + * The bandwidth corresponds to the SYSCLK frequency generated by the > > + * PLL pre-divider, the PLL multiplier and the SYS divider (see the clock > > + * tree documented here above). > > + * > > + * From the SYSCLK frequency, the MIPI CSI-2 clock tree generates the > > + * pixel clock and the MIPI BIT clock as follows: > > + * > > + * MIPI_BIT_CLK = SYSCLK / MIPI_DIV / 2; > > + * PIXEL_CLK = SYSCLK / PLL_RDVI / BIT_DIVIDER / PCLK_DIV / MIPI_DIV > > + * > > + * with this fixed parameters: > > + * PLL_RDIV = 2; > > + * BIT_DIVIDER = 2; (MIPI_BIT_MODE == 8 ? 2 : 2,5); > > + * PCLK_DIV = 1; > > + * > > + * With these values we have: > > + * > > + * pixel_clock = bandwidth / bpp > > + * = bandwidth / 4 / MIPI_DIV; > > + * > > + * And so we can calculate MIPI_DIV as: > > + * MIPI_DIV = bpp / 4; > > + */ > > +static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor, > > + unsigned long rate) > > +{ > > + const struct ov5640_mode_info *mode = sensor->current_mode; > > + u8 mipi_div = OV5640_MIPI_DIV; > > + u8 prediv, mult, sysdiv; > > + int ret; > > + > > + /* FIXME: > > + * Practical experience shows we get a correct frame rate by > > + * halving the bandwidth rate by 2, to slow down SYSCLK frequency. > > + * Divide both SYSCLK and MIPI_DIV by two (with OV5640_MIPI_DIV > > + * currently fixed at value '2', while according to the above > > + * formula it should have been = bpp / 4 = 4). > > + * > > + * So that: > > + * pixel_clock = bandwidth / 2 / bpp > > + * = bandwidth / 2 / 4 / MIPI_DIV; > > + * MIPI_DIV = bpp / 4 / 2; > > + */ > > + rate /= 2; > > + > > + /* FIXME: > > + * High resolution modes (1280x720, 1920x1080) requires an higher > > + * clock speed. Half the MIPI_DIVIDER value to double the output > > + * pixel clock and MIPI_CLK speeds. > > + */ > > + if (mode->hact > 1024) > > + mipi_div /= 2; > > Sam patches are better here. He found out the reason for this, as > 'high resolutions modes' use the scaler, and a ration between pixel > clock and scaler clock has to be respected. My patches you squahsed in > here use this rather sub-optimal "hact > 1024" check, I would rather use his > "does the mode use the scaler?" way instead. That's something else > that could be squashed in a forthcoming version, or fixed with > incremental patches. > > > + > > + ov5640_calc_sys_clk(sensor, rate, &prediv, &mult, &sysdiv); > > + > > + ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0, > > + 0x0f, OV5640_PLL_CTRL0_MIPI_MODE_8BIT); > > + > > + ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1, > > + 0xff, sysdiv << 4 | mipi_div); > > + 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, OV5640_PLL_CTRL3_PLL_ROOT_DIV_2 | prediv); > > + if (ret) > > + return ret; > > + > > + return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, > > + 0x30, OV5640_PLL_SYS_ROOT_DIVIDER_BYPASS); > > Again, there might be something to bring in from Sam patches here > (programming register 0x4837 with the pixel clock in particular). > Again, let me know how would you like to receive updates. > > > +} > > + > > +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; > > +} > > This function is now called from a single place, maybe you want to > remove it and inline its code. I still find it easier to understand, and the compiler will probbaly end up inlining it anyway. Thanks! Maxime -- Maxime Ripard, Bootlin Embedded Linux and Kernel engineering https://bootlin.com