Re: [PATCH v2 05/16] drm: rcar-du: lvds: D3/E3 support
Thank you for your patch! > On September 14, 2018 at 11:10 AM Laurent Pinchart > wrote: > > > The LVDS encoders in the D3 and E3 SoCs differ significantly from those > in the other R-Car Gen3 family members: > > - The LVDS PLL architecture is more complex and requires computing PLL > parameters manually. > - The PLL uses external clocks as inputs, which need to be retrieved > from DT. > - In addition to the different PLL setup, the startup sequence has > changed *again* (seems someone had trouble making his/her mind). > > Supporting all this requires DT bindings extensions for external clocks, > brand new PLL setup code, and a few quirks to handle the differences in > the startup sequence. > > The implementation doesn't support all hardware features yet, namely > > - Using the LV[01] clocks generated by the CPG as PLL input. > - Providing the LVDS PLL clock to the DU for use with the RGB output. > > Those features can be added later when the need will arise. > > Signed-off-by: Laurent Pinchart > Tested-by: Jacopo Mondi > --- > Changes since v1: > > - Don't compile-out debug code based on DEBUG and CONFIG_DYNAMIC_DEBUG > - Test all three input clocks (DOTCLKIN[01] and EXTAL) and pick the best > one > --- > drivers/gpu/drm/rcar-du/rcar_lvds.c | 355 > +++ > drivers/gpu/drm/rcar-du/rcar_lvds_regs.h | 43 +++- > 2 files changed, 351 insertions(+), 47 deletions(-) > > diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c > b/drivers/gpu/drm/rcar-du/rcar_lvds.c > index ce0eb68c3416..23e7743144c8 100644 > --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c > +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c > @@ -24,6 +24,8 @@ > > #include "rcar_lvds_regs.h" > > +struct rcar_lvds; > + > /* Keep in sync with the LVDCR0.LVMD hardware register values. */ > enum rcar_lvds_mode { > RCAR_LVDS_MODE_JEIDA = 0, > @@ -31,14 +33,16 @@ enum rcar_lvds_mode { > RCAR_LVDS_MODE_VESA = 4, > }; > > -#define RCAR_LVDS_QUIRK_LANES(1 << 0)/* LVDS lanes 1 and 3 > inverted */ > -#define RCAR_LVDS_QUIRK_GEN2_PLLCR (1 << 1) /* LVDPLLCR has gen2 layout */ > -#define RCAR_LVDS_QUIRK_GEN3_LVEN (1 << 2) /* LVEN bit needs to be set */ > - /* on R8A77970/R8A7799x */ > +#define RCAR_LVDS_QUIRK_LANESBIT(0) /* LVDS lanes 1 and 3 > inverted */ > +#define RCAR_LVDS_QUIRK_GEN3_LVENBIT(1) /* LVEN bit needs to be set on > R8A77970/R8A7799x */ > +#define RCAR_LVDS_QUIRK_PWD BIT(2) /* PWD bit available (all of > Gen3 but E3) */ > +#define RCAR_LVDS_QUIRK_EXT_PLL BIT(3) /* Has extended PLL */ > +#define RCAR_LVDS_QUIRK_DUAL_LINKBIT(4) /* Supports dual-link operation > */ > > struct rcar_lvds_device_info { > unsigned int gen; > unsigned int quirks; > + void (*pll_setup)(struct rcar_lvds *lvds, unsigned int freq); > }; > > struct rcar_lvds { > @@ -52,7 +56,11 @@ struct rcar_lvds { > struct drm_panel *panel; > > void __iomem *mmio; > - struct clk *clock; > + struct { > + struct clk *mod;/* CPG module clock */ > + struct clk *extal; /* External clock */ > + struct clk *dotclkin[2];/* External DU clocks */ > + } clocks; > bool enabled; > > struct drm_display_mode display_mode; > @@ -128,33 +136,212 @@ static const struct drm_connector_funcs > rcar_lvds_conn_funcs = { > }; > > /* > - > - * Bridge > + * PLL Setup > */ > > -static u32 rcar_lvds_lvdpllcr_gen2(unsigned int freq) > +static void rcar_lvds_pll_setup_gen2(struct rcar_lvds *lvds, unsigned int > freq) > { > - if (freq < 39000) > - return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M; > - else if (freq < 61000) > - return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M; > - else if (freq < 121000) > - return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M; > + u32 val; > + > + if (freq < 3900) > + val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M; > + else if (freq < 6100) > + val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M; > + else if (freq < 12100) > + val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M; > else > - return LVDPLLCR_PLLDLYCNT_150M; > + val = LVDPLLCR_PLLDLYCNT_150M; > + > + rcar_lvds_write(lvds, LVDPLLCR, val); > } > > -static u32 rcar_lvds_lvdpllcr_gen3(unsigned int freq) > +static void rcar_lvds_pll_setup_gen3(struct rcar_lvds *lvds, unsigned int > freq) > { > - if (freq < 42000) > - return LVDPLLCR_PLLDIVCNT_42M; > - else if (freq < 85000) > - return LVDPLLCR_PLLDIVCNT_85M; > - else if (freq < 128000) > - return
Re: [PATCH v2 05/16] drm: rcar-du: lvds: D3/E3 support
Hi Laurent, On Fri, Sep 14, 2018 at 12:10:35PM +0300, Laurent Pinchart wrote: > The LVDS encoders in the D3 and E3 SoCs differ significantly from those > in the other R-Car Gen3 family members: > > - The LVDS PLL architecture is more complex and requires computing PLL > parameters manually. > - The PLL uses external clocks as inputs, which need to be retrieved > from DT. > - In addition to the different PLL setup, the startup sequence has > changed *again* (seems someone had trouble making his/her mind). > > Supporting all this requires DT bindings extensions for external clocks, > brand new PLL setup code, and a few quirks to handle the differences in > the startup sequence. > > The implementation doesn't support all hardware features yet, namely > > - Using the LV[01] clocks generated by the CPG as PLL input. > - Providing the LVDS PLL clock to the DU for use with the RGB output. > > Those features can be added later when the need will arise. > > Signed-off-by: Laurent Pinchart > Tested-by: Jacopo Mondi please add my Reviewed-by: Jacopo Mondi > --- > Changes since v1: > > - Don't compile-out debug code based on DEBUG and CONFIG_DYNAMIC_DEBUG > - Test all three input clocks (DOTCLKIN[01] and EXTAL) and pick the best > one > --- > drivers/gpu/drm/rcar-du/rcar_lvds.c | 355 > +++ > drivers/gpu/drm/rcar-du/rcar_lvds_regs.h | 43 +++- > 2 files changed, 351 insertions(+), 47 deletions(-) > > diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c > b/drivers/gpu/drm/rcar-du/rcar_lvds.c > index ce0eb68c3416..23e7743144c8 100644 > --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c > +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c > @@ -24,6 +24,8 @@ > > #include "rcar_lvds_regs.h" > > +struct rcar_lvds; > + > /* Keep in sync with the LVDCR0.LVMD hardware register values. */ > enum rcar_lvds_mode { > RCAR_LVDS_MODE_JEIDA = 0, > @@ -31,14 +33,16 @@ enum rcar_lvds_mode { > RCAR_LVDS_MODE_VESA = 4, > }; > > -#define RCAR_LVDS_QUIRK_LANES(1 << 0)/* LVDS lanes 1 and 3 > inverted */ > -#define RCAR_LVDS_QUIRK_GEN2_PLLCR (1 << 1) /* LVDPLLCR has gen2 layout */ > -#define RCAR_LVDS_QUIRK_GEN3_LVEN (1 << 2) /* LVEN bit needs to be set */ > - /* on R8A77970/R8A7799x */ > +#define RCAR_LVDS_QUIRK_LANESBIT(0) /* LVDS lanes 1 and 3 > inverted */ > +#define RCAR_LVDS_QUIRK_GEN3_LVENBIT(1) /* LVEN bit needs to be set on > R8A77970/R8A7799x */ > +#define RCAR_LVDS_QUIRK_PWD BIT(2) /* PWD bit available (all of > Gen3 but E3) */ > +#define RCAR_LVDS_QUIRK_EXT_PLL BIT(3) /* Has extended PLL */ > +#define RCAR_LVDS_QUIRK_DUAL_LINKBIT(4) /* Supports dual-link operation > */ > > struct rcar_lvds_device_info { > unsigned int gen; > unsigned int quirks; > + void (*pll_setup)(struct rcar_lvds *lvds, unsigned int freq); > }; > > struct rcar_lvds { > @@ -52,7 +56,11 @@ struct rcar_lvds { > struct drm_panel *panel; > > void __iomem *mmio; > - struct clk *clock; > + struct { > + struct clk *mod;/* CPG module clock */ > + struct clk *extal; /* External clock */ > + struct clk *dotclkin[2];/* External DU clocks */ > + } clocks; > bool enabled; > > struct drm_display_mode display_mode; > @@ -128,33 +136,212 @@ static const struct drm_connector_funcs > rcar_lvds_conn_funcs = { > }; > > /* > - > - * Bridge > + * PLL Setup > */ > > -static u32 rcar_lvds_lvdpllcr_gen2(unsigned int freq) > +static void rcar_lvds_pll_setup_gen2(struct rcar_lvds *lvds, unsigned int > freq) > { > - if (freq < 39000) > - return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M; > - else if (freq < 61000) > - return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M; > - else if (freq < 121000) > - return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M; > + u32 val; > + > + if (freq < 3900) > + val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M; > + else if (freq < 6100) > + val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M; > + else if (freq < 12100) > + val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M; > else > - return LVDPLLCR_PLLDLYCNT_150M; > + val = LVDPLLCR_PLLDLYCNT_150M; > + > + rcar_lvds_write(lvds, LVDPLLCR, val); > } > > -static u32 rcar_lvds_lvdpllcr_gen3(unsigned int freq) > +static void rcar_lvds_pll_setup_gen3(struct rcar_lvds *lvds, unsigned int > freq) > { > - if (freq < 42000) > - return LVDPLLCR_PLLDIVCNT_42M; > - else if (freq < 85000) > - return LVDPLLCR_PLLDIVCNT_85M; > - else if (freq < 128000) > - return
Re: [PATCH v2 05/16] drm: rcar-du: lvds: D3/E3 support
Hi Ulrich, On Monday, 17 September 2018 13:53:58 EEST Ulrich Hecht wrote: > > On September 14, 2018 at 11:10 AM Laurent Pinchart wrote: > > > > The LVDS encoders in the D3 and E3 SoCs differ significantly from those > > in the other R-Car Gen3 family members: > > > > - The LVDS PLL architecture is more complex and requires computing PLL > > parameters manually. > > > > - The PLL uses external clocks as inputs, which need to be retrieved > > from DT. > > > > - In addition to the different PLL setup, the startup sequence has > > changed *again* (seems someone had trouble making his/her mind). > > > > Supporting all this requires DT bindings extensions for external clocks, > > brand new PLL setup code, and a few quirks to handle the differences in > > the startup sequence. > > > > The implementation doesn't support all hardware features yet, namely > > > > - Using the LV[01] clocks generated by the CPG as PLL input. > > - Providing the LVDS PLL clock to the DU for use with the RGB output. > > > > Those features can be added later when the need will arise. > > > > Signed-off-by: Laurent Pinchart > > > > Tested-by: Jacopo Mondi > > --- > > Changes since v1: > > > > - Don't compile-out debug code based on DEBUG and CONFIG_DYNAMIC_DEBUG > > - Test all three input clocks (DOTCLKIN[01] and EXTAL) and pick the best > > one > > > > --- > > > > drivers/gpu/drm/rcar-du/rcar_lvds.c | 355 ++ > > drivers/gpu/drm/rcar-du/rcar_lvds_regs.h | 43 +++- > > 2 files changed, 351 insertions(+), 47 deletions(-) > > > > diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c > > b/drivers/gpu/drm/rcar-du/rcar_lvds.c index ce0eb68c3416..23e7743144c8 > > 100644 > > --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c > > +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c [snip] > > +static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk > > *clk, > > +unsigned long target, struct pll_info *pll, > > +u32 clksel) > > +{ > > + unsigned long output; > > + unsigned long fin; > > + unsigned int m_min; > > + unsigned int m_max; > > + unsigned int m; > > + int error; > > + > > + if (!clk) > > + return; > > + > > + /* > > +* The LVDS PLL is made of a pre-divider and a multiplier (strangerly > > strangely Will be fixed in v2. > > +* enough called M and N respectively), followed by a post-divider E. > > +* > > +* ,-. ,-. ,-. ,-. > > +* Fin --> | 1/M | -Fpdf-> | PFD | --> | VCO | -Fvco-> | 1/E | --> Fout > > +* `-' ,-> | | `-' | `-' > > +* | `-' | > > +* | ,-. | > > +* ` | 1/N | <---' > > +* `-' > > +* > > +* The clock output by the PLL is then further divided by a > > programmable > > +* divider DIV to achieve the desired target frequency. Finally, an > > +* optional fixed /7 divider is used to convert the bit clock to a > > pixel > > +* clock (as LVDS transmits 7 bits per lane per clock sample). > > +* > > +* ,---. ,-. |\ > > +* Fout --> | 1/DIV | --> | 1/7 | --> | | > > +* `---' | `-' | | --> dot clock > > +* `> | | > > +*|/ > > +* > > +* The /7 divider is optional when the LVDS PLL is used to generate a > > +* dot clock for the DU RGB output, without using the LVDS encoder. We > > +* don't support this configuration yet. > > +* > > +* The PLL allowed input frequency range is 12 MHz to 192 MHz. > > +*/ > > + > > + fin = clk_get_rate(clk); > > + if (fin < 1200 || fin > 19200) > > + return; > > + > > + /* > > +* The comparison frequency range is 12 MHz to 24 MHz, which limits the > > +* allowed values for the pre-divider M (normal range 1-8). > > +* > > +* Fpfd = Fin / M > > +*/ > > + m_min = max_t(unsigned int, 1, DIV_ROUND_UP(fin, 2400)); > > + m_max = min_t(unsigned int, 8, fin / 1200); > > + > > + for (m = m_min; m <= m_max; ++m) { > > + unsigned long fpfd; > > + unsigned int n_min; > > + unsigned int n_max; > > + unsigned int n; > > + > > + /* > > +* The VCO operating range is 900 Mhz to 1800 MHz, which limits > > +* the allowed values for the multiplier N (normal range > > +* 60-120). > > +* > > +* Fvco = Fin * N / M > > +*/ > > + fpfd = fin / m; > > + n_min = max_t(unsigned int, 60, DIV_ROUND_UP(9, fpfd)); > > + n_max = min_t(unsigned int, 120, 18 / fpfd); > > + > > + for (n = n_min; n < n_max; ++n) { > > +
[PATCH v2 05/16] drm: rcar-du: lvds: D3/E3 support
The LVDS encoders in the D3 and E3 SoCs differ significantly from those in the other R-Car Gen3 family members: - The LVDS PLL architecture is more complex and requires computing PLL parameters manually. - The PLL uses external clocks as inputs, which need to be retrieved from DT. - In addition to the different PLL setup, the startup sequence has changed *again* (seems someone had trouble making his/her mind). Supporting all this requires DT bindings extensions for external clocks, brand new PLL setup code, and a few quirks to handle the differences in the startup sequence. The implementation doesn't support all hardware features yet, namely - Using the LV[01] clocks generated by the CPG as PLL input. - Providing the LVDS PLL clock to the DU for use with the RGB output. Those features can be added later when the need will arise. Signed-off-by: Laurent Pinchart Tested-by: Jacopo Mondi --- Changes since v1: - Don't compile-out debug code based on DEBUG and CONFIG_DYNAMIC_DEBUG - Test all three input clocks (DOTCLKIN[01] and EXTAL) and pick the best one --- drivers/gpu/drm/rcar-du/rcar_lvds.c | 355 +++ drivers/gpu/drm/rcar-du/rcar_lvds_regs.h | 43 +++- 2 files changed, 351 insertions(+), 47 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c index ce0eb68c3416..23e7743144c8 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c @@ -24,6 +24,8 @@ #include "rcar_lvds_regs.h" +struct rcar_lvds; + /* Keep in sync with the LVDCR0.LVMD hardware register values. */ enum rcar_lvds_mode { RCAR_LVDS_MODE_JEIDA = 0, @@ -31,14 +33,16 @@ enum rcar_lvds_mode { RCAR_LVDS_MODE_VESA = 4, }; -#define RCAR_LVDS_QUIRK_LANES (1 << 0)/* LVDS lanes 1 and 3 inverted */ -#define RCAR_LVDS_QUIRK_GEN2_PLLCR (1 << 1)/* LVDPLLCR has gen2 layout */ -#define RCAR_LVDS_QUIRK_GEN3_LVEN (1 << 2) /* LVEN bit needs to be set */ - /* on R8A77970/R8A7799x */ +#define RCAR_LVDS_QUIRK_LANES BIT(0) /* LVDS lanes 1 and 3 inverted */ +#define RCAR_LVDS_QUIRK_GEN3_LVEN BIT(1) /* LVEN bit needs to be set on R8A77970/R8A7799x */ +#define RCAR_LVDS_QUIRK_PWDBIT(2) /* PWD bit available (all of Gen3 but E3) */ +#define RCAR_LVDS_QUIRK_EXT_PLLBIT(3) /* Has extended PLL */ +#define RCAR_LVDS_QUIRK_DUAL_LINK BIT(4) /* Supports dual-link operation */ struct rcar_lvds_device_info { unsigned int gen; unsigned int quirks; + void (*pll_setup)(struct rcar_lvds *lvds, unsigned int freq); }; struct rcar_lvds { @@ -52,7 +56,11 @@ struct rcar_lvds { struct drm_panel *panel; void __iomem *mmio; - struct clk *clock; + struct { + struct clk *mod;/* CPG module clock */ + struct clk *extal; /* External clock */ + struct clk *dotclkin[2];/* External DU clocks */ + } clocks; bool enabled; struct drm_display_mode display_mode; @@ -128,33 +136,212 @@ static const struct drm_connector_funcs rcar_lvds_conn_funcs = { }; /* - - * Bridge + * PLL Setup */ -static u32 rcar_lvds_lvdpllcr_gen2(unsigned int freq) +static void rcar_lvds_pll_setup_gen2(struct rcar_lvds *lvds, unsigned int freq) { - if (freq < 39000) - return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M; - else if (freq < 61000) - return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M; - else if (freq < 121000) - return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M; + u32 val; + + if (freq < 3900) + val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M; + else if (freq < 6100) + val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M; + else if (freq < 12100) + val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M; else - return LVDPLLCR_PLLDLYCNT_150M; + val = LVDPLLCR_PLLDLYCNT_150M; + + rcar_lvds_write(lvds, LVDPLLCR, val); } -static u32 rcar_lvds_lvdpllcr_gen3(unsigned int freq) +static void rcar_lvds_pll_setup_gen3(struct rcar_lvds *lvds, unsigned int freq) { - if (freq < 42000) - return LVDPLLCR_PLLDIVCNT_42M; - else if (freq < 85000) - return LVDPLLCR_PLLDIVCNT_85M; - else if (freq < 128000) - return LVDPLLCR_PLLDIVCNT_128M; + u32 val; + + if (freq < 4200) + val = LVDPLLCR_PLLDIVCNT_42M; + else if (freq < 8500) + val = LVDPLLCR_PLLDIVCNT_85M; + else if (freq < 12800) + val = LVDPLLCR_PLLDIVCNT_128M; else -