/*
         * On D3/E3 the dot clock is provided by the LVDS encoder attached to
-        * the DU channel. We need to enable its clock output explicitly if
-        * the LVDS output is disabled.
+        * the DU channel. We need to enable its clock output explicitly before
+        * starting the CRTC, as the bridge hasn't been enabled by the atomic
+        * helpers yet.
         */
-       if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index) &&
-           rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0)) {
+       if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index)) {
+               bool dot_clk_only = rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0);
                struct drm_bridge *bridge = rcdu->lvds[rcrtc->index];
                const struct drm_display_mode *mode =
                        &crtc->state->adjusted_mode;
 
-               rcar_lvds_pclk_enable(bridge, mode->clock * 1000);
+               rcar_lvds_pclk_enable(bridge, mode->clock * 1000, dot_clk_only);
        }
 
        /*
        rcar_du_crtc_stop(rcrtc);
        rcar_du_crtc_put(rcrtc);
 
-       if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index) &&
-           rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0)) {
+       if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index)) {
+               bool dot_clk_only = rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0);
                struct drm_bridge *bridge = rcdu->lvds[rcrtc->index];
 
                /*
                 * Disable the LVDS clock output, see
-                * rcar_du_crtc_atomic_enable().
+                * rcar_du_crtc_atomic_enable(). When the LVDS output is used,
+                * this also disables the LVDS encoder.
                 */
-               rcar_lvds_pclk_disable(bridge);
+               rcar_lvds_pclk_disable(bridge, dot_clk_only);
        }
 
        if ((rcdu->info->dsi_clk_mask & BIT(rcrtc->index)) &&
                 * Disable the DSI clock output, see
                 * rcar_du_crtc_atomic_enable().
                 */
-
                rcar_mipi_dsi_pclk_disable(bridge);
        }
 
 
                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, bool dot_clock_only)
+static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds,
+                                     unsigned int freq, bool dot_clock_only)
 {
        struct pll_info pll = { .diff = (unsigned long)-1 };
        u32 lvdpllcr;
                rcar_lvds_write(lvds, LVDDIV, 0);
 }
 
-static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, unsigned int freq)
-{
-       __rcar_lvds_pll_setup_d3_e3(lvds, freq, false);
-}
-
 /* -----------------------------------------------------------------------------
  * Enable/disable
  */
        /*
         * PLL clock configuration on all instances but the companion in
         * dual-link mode.
+        *
+        * The extended PLL has been turned on by an explicit call to
+        * rcar_lvds_pclk_enable() from the DU driver.
         */
-       if (lvds->link_type == RCAR_LVDS_SINGLE_LINK || lvds->companion) {
+       if ((lvds->link_type == RCAR_LVDS_SINGLE_LINK || lvds->companion) &&
+           !(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) {
                const struct drm_crtc_state *crtc_state =
                        drm_atomic_get_new_crtc_state(state, crtc);
                const struct drm_display_mode *mode =
        rcar_lvds_write(lvds, LVDCR0, lvdcr0);
 }
 
+static void rcar_lvds_disable(struct drm_bridge *bridge)
+{
+       struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
+       u32 lvdcr0;
+
+       /*
+        * Clear the LVDCR0 bits in the order specified by the hardware
+        * documentation, ending with a write of 0 to the full register to
+        * clear all remaining bits.
+        */
+       lvdcr0 = rcar_lvds_read(lvds, LVDCR0);
+
+       lvdcr0 &= ~LVDCR0_LVRES;
+       rcar_lvds_write(lvds, LVDCR0, lvdcr0);
+
+       if (lvds->info->quirks & RCAR_LVDS_QUIRK_GEN3_LVEN) {
+               lvdcr0 &= ~LVDCR0_LVEN;
+               rcar_lvds_write(lvds, LVDCR0, lvdcr0);
+       }
+
+       if (lvds->info->quirks & RCAR_LVDS_QUIRK_PWD) {
+               lvdcr0 &= ~LVDCR0_PWD;
+               rcar_lvds_write(lvds, LVDCR0, lvdcr0);
+       }
+
+       if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) {
+               lvdcr0 &= ~LVDCR0_PLLON;
+               rcar_lvds_write(lvds, LVDCR0, lvdcr0);
+       }
+
+       rcar_lvds_write(lvds, LVDCR0, 0);
+       rcar_lvds_write(lvds, LVDCR1, 0);
+
+       /* The extended PLL is turned off in rcar_lvds_pclk_disable(). */
+       if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL))
+               rcar_lvds_write(lvds, LVDPLLCR, 0);
+
+       /* Disable the companion LVDS encoder in dual-link mode. */
+       if (lvds->link_type != RCAR_LVDS_SINGLE_LINK && lvds->companion)
+               rcar_lvds_disable(lvds->companion);
+
+       pm_runtime_put_sync(lvds->dev);
+}
+
 /* -----------------------------------------------------------------------------
  * Clock - D3/E3 only
  */
 
-int rcar_lvds_pclk_enable(struct drm_bridge *bridge, unsigned long freq)
+int rcar_lvds_pclk_enable(struct drm_bridge *bridge, unsigned long freq,
+                         bool dot_clk_only)
 {
        struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
        int ret;
        if (ret)
                return ret;
 
-       __rcar_lvds_pll_setup_d3_e3(lvds, freq, true);
+       rcar_lvds_pll_setup_d3_e3(lvds, freq, dot_clk_only);
 
        return 0;
 }
 EXPORT_SYMBOL_GPL(rcar_lvds_pclk_enable);
 
-void rcar_lvds_pclk_disable(struct drm_bridge *bridge)
+void rcar_lvds_pclk_disable(struct drm_bridge *bridge, bool dot_clk_only)
 {
        struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
 
 
        dev_dbg(lvds->dev, "disabling LVDS PLL\n");
 
+       if (!dot_clk_only)
+               rcar_lvds_disable(bridge);
+
        rcar_lvds_write(lvds, LVDPLLCR, 0);
 
        pm_runtime_put_sync(lvds->dev);
                                     struct drm_bridge_state *old_bridge_state)
 {
        struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
-       u32 lvdcr0;
 
        /*
-        * Clear the LVDCR0 bits in the order specified by the hardware
-        * documentation, ending with a write of 0 to the full register to
-        * clear all remaining bits.
+        * For D3 and E3, disabling the LVDS encoder before the DU would stall
+        * the DU, causing a vblank wait timeout when stopping the DU. This has
+        * been traced to clearing the LVEN bit, but the exact reason is
+        * unknown. Keep the encoder enabled, it will be disabled by an explicit
+        * call to rcar_lvds_pclk_disable() from the DU driver.
+        *
+        * We could clear the LVRES bit already to disable the LVDS output, but
+        * that's likely pointless.
         */
-       lvdcr0 = rcar_lvds_read(lvds, LVDCR0);
-
-       lvdcr0 &= ~LVDCR0_LVRES;
-       rcar_lvds_write(lvds, LVDCR0, lvdcr0);
-
-       if (lvds->info->quirks & RCAR_LVDS_QUIRK_GEN3_LVEN) {
-               lvdcr0 &= ~LVDCR0_LVEN;
-               rcar_lvds_write(lvds, LVDCR0, lvdcr0);
-       }
-
-       if (lvds->info->quirks & RCAR_LVDS_QUIRK_PWD) {
-               lvdcr0 &= ~LVDCR0_PWD;
-               rcar_lvds_write(lvds, LVDCR0, lvdcr0);
-       }
-
-       if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) {
-               lvdcr0 &= ~LVDCR0_PLLON;
-               rcar_lvds_write(lvds, LVDCR0, lvdcr0);
-       }
-
-       rcar_lvds_write(lvds, LVDCR0, 0);
-       rcar_lvds_write(lvds, LVDCR1, 0);
-       rcar_lvds_write(lvds, LVDPLLCR, 0);
-
-       /* Disable the companion LVDS encoder in dual-link mode. */
-       if (lvds->link_type != RCAR_LVDS_SINGLE_LINK && lvds->companion)
-               rcar_lvds_atomic_disable(lvds->companion, old_bridge_state);
+       if (lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)
+               return;
 
-       pm_runtime_put_sync(lvds->dev);
+       rcar_lvds_disable(bridge);
 }
 
 static bool rcar_lvds_mode_fixup(struct drm_bridge *bridge,
        .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[] = {