iowrite32(data, lvds->mmio + reg);
 }
 
-static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
-                                struct rcar_du_crtc *rcrtc)
+static void rcar_du_lvdsenc_start_gen2(struct rcar_du_lvdsenc *lvds,
+                                      struct rcar_du_crtc *rcrtc)
 {
        const struct drm_display_mode *mode = &rcrtc->crtc.mode;
        unsigned int freq = mode->clock;
        u32 lvdcr0;
-       u32 lvdhcr;
        u32 pllcr;
-       int ret;
-
-       if (lvds->enabled)
-               return 0;
-
-       ret = clk_prepare_enable(lvds->clock);
-       if (ret < 0)
-               return ret;
 
        /* PLL clock configuration */
        if (freq < 39000)
 
        rcar_lvds_write(lvds, LVDPLLCR, pllcr);
 
-       /* Hardcode the channels and control signals routing for now.
-        *
-        * HSYNC -> CTRL0
-        * VSYNC -> CTRL1
-        * DISP  -> CTRL2
-        * 0     -> CTRL3
-        */
-       rcar_lvds_write(lvds, LVDCTRCR, LVDCTRCR_CTR3SEL_ZERO |
-                       LVDCTRCR_CTR2SEL_DISP | LVDCTRCR_CTR1SEL_VSYNC |
-                       LVDCTRCR_CTR0SEL_HSYNC);
-
-       if (rcar_du_needs(lvds->dev, RCAR_DU_QUIRK_LVDS_LANES))
-               lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 3)
-                      | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 1);
-       else
-               lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 1)
-                      | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 3);
-
-       rcar_lvds_write(lvds, LVDCHCR, lvdhcr);
-
        /* Select the input, hardcode mode 0, enable LVDS operation and turn
         * bias circuitry on.
         */
        rcar_lvds_write(lvds, LVDCR0, lvdcr0);
 
        /* Turn all the channels on. */
-       rcar_lvds_write(lvds, LVDCR1, LVDCR1_CHSTBY(3) | LVDCR1_CHSTBY(2) |
-                       LVDCR1_CHSTBY(1) | LVDCR1_CHSTBY(0) | LVDCR1_CLKSTBY);
+       rcar_lvds_write(lvds, LVDCR1,
+                       LVDCR1_CHSTBY_GEN2(3) | LVDCR1_CHSTBY_GEN2(2) |
+                       LVDCR1_CHSTBY_GEN2(1) | LVDCR1_CHSTBY_GEN2(0) |
+                       LVDCR1_CLKSTBY_GEN2);
 
        /* Turn the PLL on, wait for the startup delay, and turn the output
         * on.
 
        lvdcr0 |= LVDCR0_LVRES;
        rcar_lvds_write(lvds, LVDCR0, lvdcr0);
+}
+
+static void rcar_du_lvdsenc_start_gen3(struct rcar_du_lvdsenc *lvds,
+                                      struct rcar_du_crtc *rcrtc)
+{
+       const struct drm_display_mode *mode = &rcrtc->crtc.mode;
+       unsigned int freq = mode->clock;
+       u32 lvdcr0;
+       u32 pllcr;
+
+       /* PLL clock configuration */
+       if (freq < 42000)
+               pllcr = LVDPLLCR_PLLDIVCNT_42M;
+       else if (freq < 85000)
+               pllcr = LVDPLLCR_PLLDIVCNT_85M;
+       else if (freq < 128000)
+               pllcr = LVDPLLCR_PLLDIVCNT_128M;
+       else
+               pllcr = LVDPLLCR_PLLDIVCNT_148M;
+
+       rcar_lvds_write(lvds, LVDPLLCR, pllcr);
+
+       /* Turn the PLL on, set it to LVDS normal mode, wait for the startup
+        * delay and turn the output on.
+        */
+       lvdcr0 = LVDCR0_PLLON;
+       rcar_lvds_write(lvds, LVDCR0, lvdcr0);
+
+       lvdcr0 |= LVDCR0_PWD;
+       rcar_lvds_write(lvds, LVDCR0, lvdcr0);
+
+       usleep_range(100, 150);
+
+       lvdcr0 |= LVDCR0_LVRES;
+       rcar_lvds_write(lvds, LVDCR0, lvdcr0);
+
+       /* Turn all the channels on. */
+       rcar_lvds_write(lvds, LVDCR1,
+                       LVDCR1_CHSTBY_GEN3(3) | LVDCR1_CHSTBY_GEN3(2) |
+                       LVDCR1_CHSTBY_GEN3(1) | LVDCR1_CHSTBY_GEN3(0) |
+                       LVDCR1_CLKSTBY_GEN3);
+}
+
+static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
+                                struct rcar_du_crtc *rcrtc)
+{
+       u32 lvdhcr;
+       int ret;
+
+       if (lvds->enabled)
+               return 0;
+
+       ret = clk_prepare_enable(lvds->clock);
+       if (ret < 0)
+               return ret;
+
+       /* Hardcode the channels and control signals routing for now.
+        *
+        * HSYNC -> CTRL0
+        * VSYNC -> CTRL1
+        * DISP  -> CTRL2
+        * 0     -> CTRL3
+        */
+       rcar_lvds_write(lvds, LVDCTRCR, LVDCTRCR_CTR3SEL_ZERO |
+                       LVDCTRCR_CTR2SEL_DISP | LVDCTRCR_CTR1SEL_VSYNC |
+                       LVDCTRCR_CTR0SEL_HSYNC);
+
+       if (rcar_du_needs(lvds->dev, RCAR_DU_QUIRK_LVDS_LANES))
+               lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 3)
+                      | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 1);
+       else
+               lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 1)
+                      | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 3);
+
+       rcar_lvds_write(lvds, LVDCHCR, lvdhcr);
+
+       /* Perform generation-specific initialization. */
+       if (lvds->dev->info->gen < 3)
+               rcar_du_lvdsenc_start_gen2(lvds, rcrtc);
+       else
+               rcar_du_lvdsenc_start_gen3(lvds, rcrtc);
 
        lvds->enabled = true;
+
        return 0;
 }
 
 void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds,
                                  struct drm_display_mode *mode)
 {
-       /* The internal LVDS encoder has a clock frequency operating range of
-        * 30MHz to 150MHz. Clamp the clock accordingly.
+       struct rcar_du_device *rcdu = lvds->dev;
+
+       /* The internal LVDS encoder has a restricted clock frequency operating
+        * range (30MHz to 150MHz on Gen2, 25.175MHz to 148.5MHz on Gen3). Clamp
+        * the clock accordingly.
         */
-       mode->clock = clamp(mode->clock, 30000, 150000);
+       if (rcdu->info->gen < 3)
+               mode->clock = clamp(mode->clock, 30000, 150000);
+       else
+               mode->clock = clamp(mode->clock, 25175, 148500);
 }
 
 static int rcar_du_lvdsenc_get_resources(struct rcar_du_lvdsenc *lvds,
 
 /*
  * rcar_lvds_regs.h  --  R-Car LVDS Interface Registers Definitions
  *
- * Copyright (C) 2013 Renesas Electronics Corporation
+ * Copyright (C) 2013-2015 Renesas Electronics Corporation
  *
  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  *
 
 #define LVDCR0                         0x0000
 #define LVDCR0_DUSEL                   (1 << 15)
-#define LVDCR0_DMD                     (1 << 12)
+#define LVDCR0_DMD                     (1 << 12)               /* Gen2 only */
 #define LVDCR0_LVMD_MASK               (0xf << 8)
 #define LVDCR0_LVMD_SHIFT              8
 #define LVDCR0_PLLON                   (1 << 4)
-#define LVDCR0_BEN                     (1 << 2)
-#define LVDCR0_LVEN                    (1 << 1)
+#define LVDCR0_PWD                     (1 << 2)                /* Gen3 only */
+#define LVDCR0_BEN                     (1 << 2)                /* Gen2 only */
+#define LVDCR0_LVEN                    (1 << 1)                /* Gen2 only */
 #define LVDCR0_LVRES                   (1 << 0)
 
 #define LVDCR1                         0x0004
-#define LVDCR1_CKSEL                   (1 << 15)
-#define LVDCR1_CHSTBY(n)               (3 << (2 + (n) * 2))
-#define LVDCR1_CLKSTBY                 (3 << 0)
+#define LVDCR1_CKSEL                   (1 << 15)               /* Gen2 only */
+#define LVDCR1_CHSTBY_GEN2(n)          (3 << (2 + (n) * 2))    /* Gen2 only */
+#define LVDCR1_CHSTBY_GEN3(n)          (1 << (2 + (n) * 2))    /* Gen3 only */
+#define LVDCR1_CLKSTBY_GEN2            (3 << 0)                /* Gen2 only */
+#define LVDCR1_CLKSTBY_GEN3            (1 << 0)                /* Gen3 only */
 
 #define LVDPLLCR                       0x0008
 #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 */
+#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)
 
 #define LVDCTRCR                       0x000c
 #define LVDCTRCR_CTR3SEL_ZERO          (0 << 12)