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 <laurent.pinchart+rene...@ideasonboard.com>
> Tested-by: Jacopo Mondi <jacopo+rene...@jmondi.org>

please add my
Reviewed-by: Jacopo Mondi <jacopo+rene...@jmondi.org>

> ---
> 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_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_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 < 39000000)
> +             val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M;
> +     else if (freq < 61000000)
> +             val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M;
> +     else if (freq < 121000000)
> +             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 < 42000000)
> +             val = LVDPLLCR_PLLDIVCNT_42M;
> +     else if (freq < 85000000)
> +             val = LVDPLLCR_PLLDIVCNT_85M;
> +     else if (freq < 128000000)
> +             val = LVDPLLCR_PLLDIVCNT_128M;
>       else
> -             return LVDPLLCR_PLLDIVCNT_148M;
> +             val = LVDPLLCR_PLLDIVCNT_148M;
> +
> +     rcar_lvds_write(lvds, LVDPLLCR, val);
>  }
>
> +struct pll_info {
> +     unsigned long diff;
> +     unsigned int pll_m;
> +     unsigned int pll_n;
> +     unsigned int pll_e;
> +     unsigned int div;
> +     u32 clksel;
> +};
> +
> +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
> +      * 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 < 12000000 || fin > 192000000)
> +             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, 24000000));
> +     m_max = min_t(unsigned int, 8, fin / 12000000);
> +
> +     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(900000000, fpfd));
> +             n_max = min_t(unsigned int, 120, 1800000000 / fpfd);
> +
> +             for (n = n_min; n < n_max; ++n) {
> +                     unsigned long fvco;
> +                     unsigned int e_min;
> +                     unsigned int e;
> +
> +                     /*
> +                      * The output frequency is limited to 1039.5 MHz,
> +                      * limiting again the allowed values for the
> +                      * post-divider E (normal value 1, 2 or 4).
> +                      *
> +                      * Fout = Fvco / E
> +                      */
> +                     fvco = fpfd * n;
> +                     e_min = fvco > 1039500000 ? 1 : 0;
> +
> +                     for (e = e_min; e < 3; ++e) {
> +                             unsigned long fout;
> +                             unsigned long diff;
> +                             unsigned int div;
> +
> +                             /*
> +                              * Finally we have a programable divider after
> +                              * the PLL, followed by a an optional fixed /7
> +                              * divider.
> +                              */
> +                             fout = fvco / (1 << e) / 7;
> +                             div = DIV_ROUND_CLOSEST(fout, target);
> +                             diff = abs(fout / div - target);
> +
> +                             if (diff < pll->diff) {
> +                                     pll->diff = diff;
> +                                     pll->pll_m = m;
> +                                     pll->pll_n = n;
> +                                     pll->pll_e = e;
> +                                     pll->div = div;
> +                                     pll->clksel = clksel;
> +
> +                                     if (diff == 0)
> +                                             goto done;
> +                             }
> +                     }
> +             }
> +     }
> +
> +done:
> +     output = fin * pll->pll_n / pll->pll_m / (1 << pll->pll_e)
> +            / 7 / pll->div;
> +     error = (long)(output - target) * 10000 / (long)target;
> +
> +     dev_dbg(lvds->dev,
> +             "%pC %lu Hz -> Fout %lu Hz (target %lu Hz, error %d.%02u%%), 
> PLL M/N/E/DIV %u/%u/%u/%u\n",
> +             clk, fin, output, target, error / 100,
> +             error < 0 ? -error % 100 : error % 100,
> +             pll->pll_m, pll->pll_n, pll->pll_e, pll->div);
> +}
> +
> +static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, unsigned int 
> freq)
> +{
> +     struct pll_info pll = { .diff = (unsigned long)-1 };
> +     u32 lvdpllcr;
> +
> +     rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.dotclkin[0], freq, &pll,
> +                              LVDPLLCR_CKSEL_DU_DOTCLKIN(0));
> +     rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.dotclkin[1], freq, &pll,
> +                              LVDPLLCR_CKSEL_DU_DOTCLKIN(1));
> +     rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.extal, freq, &pll,
> +                              LVDPLLCR_CKSEL_EXTAL);
> +
> +     lvdpllcr = LVDPLLCR_PLLON | pll.clksel | LVDPLLCR_CLKOUT
> +              | LVDPLLCR_PLLN(pll.pll_n - 1) | LVDPLLCR_PLLM(pll.pll_m - 1);
> +
> +     if (pll.pll_e > 0)
> +             lvdpllcr |= LVDPLLCR_STP_CLKOUTE | LVDPLLCR_OUTCLKSEL
> +                      |  LVDPLLCR_PLLE(pll.pll_e - 1);
> +
> +     rcar_lvds_write(lvds, LVDPLLCR, lvdpllcr);
> +
> +     if (pll.div > 1)
> +             rcar_lvds_write(lvds, LVDDIV, LVDDIV_DIVSEL |
> +                             LVDDIV_DIVRESET | LVDDIV_DIV(pll.div - 1));
> +     else
> +             rcar_lvds_write(lvds, LVDDIV, 0);
> +}
> +
> +/* 
> -----------------------------------------------------------------------------
> + * Bridge
> + */
> +
>  static void rcar_lvds_enable(struct drm_bridge *bridge)
>  {
>       struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
> @@ -164,14 +351,13 @@ static void rcar_lvds_enable(struct drm_bridge *bridge)
>        * do we get a state pointer?
>        */
>       struct drm_crtc *crtc = lvds->bridge.encoder->crtc;
> -     u32 lvdpllcr;
>       u32 lvdhcr;
>       u32 lvdcr0;
>       int ret;
>
>       WARN_ON(lvds->enabled);
>
> -     ret = clk_prepare_enable(lvds->clock);
> +     ret = clk_prepare_enable(lvds->clocks.mod);
>       if (ret < 0)
>               return;
>
> @@ -196,12 +382,13 @@ static void rcar_lvds_enable(struct drm_bridge *bridge)
>
>       rcar_lvds_write(lvds, LVDCHCR, lvdhcr);
>
> +     if (lvds->info->quirks & RCAR_LVDS_QUIRK_DUAL_LINK) {
> +             /* Disable dual-link mode. */
> +             rcar_lvds_write(lvds, LVDSTRIPE, 0);
> +     }
> +
>       /* PLL clock configuration. */
> -     if (lvds->info->quirks & RCAR_LVDS_QUIRK_GEN2_PLLCR)
> -             lvdpllcr = rcar_lvds_lvdpllcr_gen2(mode->clock);
> -     else
> -             lvdpllcr = rcar_lvds_lvdpllcr_gen3(mode->clock);
> -     rcar_lvds_write(lvds, LVDPLLCR, lvdpllcr);
> +     lvds->info->pll_setup(lvds, mode->clock * 1000);
>
>       /* Set the LVDS mode and select the input. */
>       lvdcr0 = lvds->mode << LVDCR0_LVMD_SHIFT;
> @@ -220,11 +407,16 @@ static void rcar_lvds_enable(struct drm_bridge *bridge)
>               rcar_lvds_write(lvds, LVDCR0, lvdcr0);
>       }
>
> -     /* Turn the PLL on. */
> -     lvdcr0 |= LVDCR0_PLLON;
> -     rcar_lvds_write(lvds, LVDCR0, lvdcr0);
> +     if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) {
> +             /*
> +              * Turn the PLL on (simple PLL only, extended PLL is fully
> +              * controlled through LVDPLLCR).
> +              */
> +             lvdcr0 |= LVDCR0_PLLON;
> +             rcar_lvds_write(lvds, LVDCR0, lvdcr0);
> +     }
>
> -     if (lvds->info->gen > 2) {
> +     if (lvds->info->quirks & RCAR_LVDS_QUIRK_PWD) {
>               /* Set LVDS normal mode. */
>               lvdcr0 |= LVDCR0_PWD;
>               rcar_lvds_write(lvds, LVDCR0, lvdcr0);
> @@ -236,8 +428,10 @@ static void rcar_lvds_enable(struct drm_bridge *bridge)
>               rcar_lvds_write(lvds, LVDCR0, lvdcr0);
>       }
>
> -     /* Wait for the startup delay. */
> -     usleep_range(100, 150);
> +     if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) {
> +             /* Wait for the PLL startup delay (simple PLL only). */
> +             usleep_range(100, 150);
> +     }
>
>       /* Turn the output on. */
>       lvdcr0 |= LVDCR0_LVRES;
> @@ -264,8 +458,9 @@ static void rcar_lvds_disable(struct drm_bridge *bridge)
>
>       rcar_lvds_write(lvds, LVDCR0, 0);
>       rcar_lvds_write(lvds, LVDCR1, 0);
> +     rcar_lvds_write(lvds, LVDPLLCR, 0);
>
> -     clk_disable_unprepare(lvds->clock);
> +     clk_disable_unprepare(lvds->clocks.mod);
>
>       lvds->enabled = false;
>  }
> @@ -446,6 +641,60 @@ static int rcar_lvds_parse_dt(struct rcar_lvds *lvds)
>       return ret;
>  }
>
> +static struct clk *rcar_lvds_get_clock(struct rcar_lvds *lvds, const char 
> *name,
> +                                    bool optional)
> +{
> +     struct clk *clk;
> +
> +     clk = devm_clk_get(lvds->dev, name);
> +     if (!IS_ERR(clk))
> +             return clk;
> +
> +     if (PTR_ERR(clk) == -ENOENT && optional)
> +             return NULL;
> +
> +     if (PTR_ERR(clk) != -EPROBE_DEFER)
> +             dev_err(lvds->dev, "failed to get %s clock\n",
> +                     name ? name : "module");
> +
> +     return clk;
> +}
> +
> +static int rcar_lvds_get_clocks(struct rcar_lvds *lvds)
> +{
> +     lvds->clocks.mod = rcar_lvds_get_clock(lvds, NULL, false);
> +     if (IS_ERR(lvds->clocks.mod))
> +             return PTR_ERR(lvds->clocks.mod);
> +
> +     /*
> +      * LVDS encoders without an extended PLL have no external clock inputs.
> +      */
> +     if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL))
> +             return 0;
> +
> +     lvds->clocks.extal = rcar_lvds_get_clock(lvds, "extal", true);
> +     if (IS_ERR(lvds->clocks.extal))
> +             return PTR_ERR(lvds->clocks.extal);
> +
> +     lvds->clocks.dotclkin[0] = rcar_lvds_get_clock(lvds, "dclkin.0", true);
> +     if (IS_ERR(lvds->clocks.dotclkin[0]))
> +             return PTR_ERR(lvds->clocks.dotclkin[0]);
> +
> +     lvds->clocks.dotclkin[1] = rcar_lvds_get_clock(lvds, "dclkin.1", true);
> +     if (IS_ERR(lvds->clocks.dotclkin[1]))
> +             return PTR_ERR(lvds->clocks.dotclkin[1]);
> +
> +     /* At least one input to the PLL must be available. */
> +     if (!lvds->clocks.extal && !lvds->clocks.dotclkin[0] &&
> +         !lvds->clocks.dotclkin[1]) {
> +             dev_err(lvds->dev,
> +                     "no input clock (extal, dclkin.0 or dclkin.1)\n");
> +             return -EINVAL;
> +     }
> +
> +     return 0;
> +}
> +
>  static int rcar_lvds_probe(struct platform_device *pdev)
>  {
>       struct rcar_lvds *lvds;
> @@ -475,11 +724,9 @@ static int rcar_lvds_probe(struct platform_device *pdev)
>       if (IS_ERR(lvds->mmio))
>               return PTR_ERR(lvds->mmio);
>
> -     lvds->clock = devm_clk_get(&pdev->dev, NULL);
> -     if (IS_ERR(lvds->clock)) {
> -             dev_err(&pdev->dev, "failed to get clock\n");
> -             return PTR_ERR(lvds->clock);
> -     }
> +     ret = rcar_lvds_get_clocks(lvds);
> +     if (ret < 0)
> +             return ret;
>
>       drm_bridge_add(&lvds->bridge);
>
> @@ -497,21 +744,39 @@ static int rcar_lvds_remove(struct platform_device 
> *pdev)
>
>  static const struct rcar_lvds_device_info rcar_lvds_gen2_info = {
>       .gen = 2,
> -     .quirks = RCAR_LVDS_QUIRK_GEN2_PLLCR,
> +     .pll_setup = rcar_lvds_pll_setup_gen2,
>  };
>
>  static const struct rcar_lvds_device_info rcar_lvds_r8a7790_info = {
>       .gen = 2,
> -     .quirks = RCAR_LVDS_QUIRK_GEN2_PLLCR | RCAR_LVDS_QUIRK_LANES,
> +     .quirks = RCAR_LVDS_QUIRK_LANES,
> +     .pll_setup = rcar_lvds_pll_setup_gen2,
>  };
>
>  static const struct rcar_lvds_device_info rcar_lvds_gen3_info = {
>       .gen = 3,
> +     .quirks = RCAR_LVDS_QUIRK_PWD,
> +     .pll_setup = rcar_lvds_pll_setup_gen3,
>  };
>
>  static const struct rcar_lvds_device_info rcar_lvds_r8a77970_info = {
>       .gen = 3,
> -     .quirks = RCAR_LVDS_QUIRK_GEN2_PLLCR | RCAR_LVDS_QUIRK_GEN3_LVEN,
> +     .quirks = RCAR_LVDS_QUIRK_PWD | RCAR_LVDS_QUIRK_GEN3_LVEN,
> +     .pll_setup = rcar_lvds_pll_setup_gen2,
> +};
> +
> +static const struct rcar_lvds_device_info rcar_lvds_r8a77990_info = {
> +     .gen = 3,
> +     .quirks = RCAR_LVDS_QUIRK_GEN3_LVEN | RCAR_LVDS_QUIRK_EXT_PLL
> +             | RCAR_LVDS_QUIRK_DUAL_LINK,
> +     .pll_setup = rcar_lvds_pll_setup_d3_e3,
> +};
> +
> +static const struct rcar_lvds_device_info rcar_lvds_r8a77995_info = {
> +     .gen = 3,
> +     .quirks = RCAR_LVDS_QUIRK_GEN3_LVEN | RCAR_LVDS_QUIRK_PWD
> +             | RCAR_LVDS_QUIRK_EXT_PLL | RCAR_LVDS_QUIRK_DUAL_LINK,
> +     .pll_setup = rcar_lvds_pll_setup_d3_e3,
>  };
>
>  static const struct of_device_id rcar_lvds_of_table[] = {
> @@ -523,6 +788,8 @@ static const struct of_device_id rcar_lvds_of_table[] = {
>       { .compatible = "renesas,r8a7796-lvds", .data = &rcar_lvds_gen3_info },
>       { .compatible = "renesas,r8a77970-lvds", .data = 
> &rcar_lvds_r8a77970_info },
>       { .compatible = "renesas,r8a77980-lvds", .data = &rcar_lvds_gen3_info },
> +     { .compatible = "renesas,r8a77990-lvds", .data = 
> &rcar_lvds_r8a77990_info },
> +     { .compatible = "renesas,r8a77995-lvds", .data = 
> &rcar_lvds_r8a77995_info },
>       { }
>  };
>
> diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h 
> b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h
> index 4870f50d9bec..87149f2f8056 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h
> +++ b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h
> @@ -18,7 +18,7 @@
>  #define LVDCR0_PLLON                 (1 << 4)
>  #define LVDCR0_PWD                   (1 << 2)                /* Gen3 only */
>  #define LVDCR0_BEN                   (1 << 2)                /* Gen2 only */
> -#define LVDCR0_LVEN                  (1 << 1)                /* Gen2 only */
> +#define LVDCR0_LVEN                  (1 << 1)
>  #define LVDCR0_LVRES                 (1 << 0)
>
>  #define LVDCR1                               0x0004
> @@ -27,21 +27,36 @@
>  #define LVDCR1_CLKSTBY                       (3 << 0)
>
>  #define LVDPLLCR                     0x0008
> +/* Gen2 & V3M */
>  #define LVDPLLCR_CEEN                        (1 << 14)
>  #define LVDPLLCR_FBEN                        (1 << 13)
>  #define LVDPLLCR_COSEL                       (1 << 12)
> -/* Gen2 */
>  #define LVDPLLCR_PLLDLYCNT_150M              (0x1bf << 0)
>  #define LVDPLLCR_PLLDLYCNT_121M              (0x22c << 0)
>  #define LVDPLLCR_PLLDLYCNT_60M               (0x77b << 0)
>  #define LVDPLLCR_PLLDLYCNT_38M               (0x69a << 0)
>  #define LVDPLLCR_PLLDLYCNT_MASK              (0x7ff << 0)
> -/* Gen3 */
> +/* Gen3 but V3M,D3 and E3 */
>  #define LVDPLLCR_PLLDIVCNT_42M               (0x014cb << 0)
>  #define LVDPLLCR_PLLDIVCNT_85M               (0x00a45 << 0)
>  #define LVDPLLCR_PLLDIVCNT_128M              (0x006c3 << 0)
>  #define LVDPLLCR_PLLDIVCNT_148M              (0x046c1 << 0)
>  #define LVDPLLCR_PLLDIVCNT_MASK              (0x7ffff << 0)
> +/* D3 and E3 */
> +#define LVDPLLCR_PLLON                       (1 << 22)
> +#define LVDPLLCR_PLLSEL_PLL0         (0 << 20)
> +#define LVDPLLCR_PLLSEL_LVX          (1 << 20)
> +#define LVDPLLCR_PLLSEL_PLL1         (2 << 20)
> +#define LVDPLLCR_CKSEL_LVX           (1 << 17)
> +#define LVDPLLCR_CKSEL_EXTAL         (3 << 17)
> +#define LVDPLLCR_CKSEL_DU_DOTCLKIN(n)        ((5 + (n) * 2) << 17)
> +#define LVDPLLCR_OCKSEL                      (1 << 16)
> +#define LVDPLLCR_STP_CLKOUTE         (1 << 14)
> +#define LVDPLLCR_OUTCLKSEL           (1 << 12)
> +#define LVDPLLCR_CLKOUT                      (1 << 11)
> +#define LVDPLLCR_PLLE(n)             ((n) << 10)
> +#define LVDPLLCR_PLLN(n)             ((n) << 3)
> +#define LVDPLLCR_PLLM(n)             ((n) << 0)
>
>  #define LVDCTRCR                     0x000c
>  #define LVDCTRCR_CTR3SEL_ZERO                (0 << 12)
> @@ -71,4 +86,26 @@
>  #define LVDCHCR_CHSEL_CH(n, c)               ((((c) - (n)) & 3) << ((n) * 4))
>  #define LVDCHCR_CHSEL_MASK(n)                (3 << ((n) * 4))
>
> +/* All registers below are specific to D3 and E3 */
> +#define LVDSTRIPE                    0x0014
> +#define LVDSTRIPE_ST_TRGSEL_DISP     (0 << 2)
> +#define LVDSTRIPE_ST_TRGSEL_HSYNC_R  (1 << 2)
> +#define LVDSTRIPE_ST_TRGSEL_HSYNC_F  (2 << 2)
> +#define LVDSTRIPE_ST_SWAP            (1 << 1)
> +#define LVDSTRIPE_ST_ON                      (1 << 0)
> +
> +#define LVDSCR                               0x0018
> +#define LVDSCR_DEPTH(n)                      (((n) - 1) << 29)
> +#define LVDSCR_BANDSET                       (1 << 28)
> +#define LVDSCR_TWGCNT(n)             ((((n) - 256) / 16) << 24)
> +#define LVDSCR_SDIV(n)                       ((n) << 22)
> +#define LVDSCR_MODE                  (1 << 21)
> +#define LVDSCR_RSTN                  (1 << 20)
> +
> +#define LVDDIV                               0x001c
> +#define LVDDIV_DIVSEL                        (1 << 8)
> +#define LVDDIV_DIVRESET                      (1 << 7)
> +#define LVDDIV_DIVSTP                        (1 << 6)
> +#define LVDDIV_DIV(n)                        ((n) << 0)
> +
>  #endif /* __RCAR_LVDS_REGS_H__ */
> --
> Regards,
>
> Laurent Pinchart
>

Attachment: signature.asc
Description: PGP signature

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to