Re: [PATCH v2 05/16] drm: rcar-du: lvds: D3/E3 support

2018-09-18 Thread Ulrich Hecht
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

2018-09-17 Thread jacopo mondi
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

2018-09-17 Thread Laurent Pinchart
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

2018-09-14 Thread Laurent Pinchart
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
-