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 <[email protected]>
> 
> 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

Attachment: signature.asc
Description: PGP signature

Reply via email to