return 0;
 }
 
-static void tegra_dc_commit_state(struct tegra_dc *dc,
-                                 struct tegra_dc_state *state)
+static void tegra_dc_set_clock_rate(struct tegra_dc *dc,
+                                   struct tegra_dc_state *state)
 {
-       u32 value;
        int err;
 
        err = clk_set_parent(dc->clk, state->clk);
        DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk),
                      state->div);
        DRM_DEBUG_KMS("pclk: %lu\n", state->pclk);
-
-       if (!dc->soc->has_nvdisplay) {
-               value = SHIFT_CLK_DIVIDER(state->div) | PIXEL_CLK_DIVIDER_PCD1;
-               tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
-       }
 }
 
 static void tegra_dc_stop(struct tegra_dc *dc)
        u32 value;
        int err;
 
+       /* apply PLL changes */
+       tegra_dc_set_clock_rate(dc, crtc_state);
+
        err = host1x_client_resume(&dc->client);
        if (err < 0) {
                dev_err(dc->dev, "failed to resume: %d\n", err);
        else
                tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR);
 
-       /* apply PLL and pixel clock changes */
-       tegra_dc_commit_state(dc, crtc_state);
+       /* apply pixel clock changes */
+       if (!dc->soc->has_nvdisplay) {
+               value = SHIFT_CLK_DIVIDER(crtc_state->div) | PIXEL_CLK_DIVIDER_PCD1;
+               tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
+       }
 
        /* program display mode */
        tegra_dc_set_timings(dc, mode);
        .has_win_b_vfilter_mem_client = true,
        .has_win_c_without_vert_filter = true,
        .plane_tiled_memory_bandwidth_x2 = false,
+       .has_pll_d2_out0 = false,
 };
 
 static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
        .has_win_b_vfilter_mem_client = true,
        .has_win_c_without_vert_filter = false,
        .plane_tiled_memory_bandwidth_x2 = true,
+       .has_pll_d2_out0 = true,
 };
 
 static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
        .has_win_b_vfilter_mem_client = false,
        .has_win_c_without_vert_filter = false,
        .plane_tiled_memory_bandwidth_x2 = true,
+       .has_pll_d2_out0 = true,
 };
 
 static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
        .has_win_b_vfilter_mem_client = false,
        .has_win_c_without_vert_filter = false,
        .plane_tiled_memory_bandwidth_x2 = false,
+       .has_pll_d2_out0 = true,
 };
 
 static const struct tegra_dc_soc_info tegra210_dc_soc_info = {
        .has_win_b_vfilter_mem_client = false,
        .has_win_c_without_vert_filter = false,
        .plane_tiled_memory_bandwidth_x2 = false,
+       .has_pll_d2_out0 = true,
 };
 
 static const struct tegra_windowgroup_soc tegra186_dc_wgrps[] = {
        .wgrps = tegra186_dc_wgrps,
        .num_wgrps = ARRAY_SIZE(tegra186_dc_wgrps),
        .plane_tiled_memory_bandwidth_x2 = false,
+       .has_pll_d2_out0 = false,
 };
 
 static const struct tegra_windowgroup_soc tegra194_dc_wgrps[] = {
        .wgrps = tegra194_dc_wgrps,
        .num_wgrps = ARRAY_SIZE(tegra194_dc_wgrps),
        .plane_tiled_memory_bandwidth_x2 = false,
+       .has_pll_d2_out0 = false,
 };
 
 static const struct of_device_id tegra_dc_of_match[] = {
 
        struct tegra_output output;
        struct tegra_dc *dc;
 
+       struct clk *pll_d_out0;
+       struct clk *pll_d2_out0;
        struct clk *clk_parent;
        struct clk *clk;
 };
        tegra_dc_commit(rgb->dc);
 }
 
+static bool tegra_rgb_pll_rate_change_allowed(struct tegra_rgb *rgb)
+{
+       if (!rgb->pll_d2_out0)
+               return false;
+
+       if (!clk_is_match(rgb->clk_parent, rgb->pll_d_out0) &&
+           !clk_is_match(rgb->clk_parent, rgb->pll_d2_out0))
+               return false;
+
+       return true;
+}
+
 static int
 tegra_rgb_encoder_atomic_check(struct drm_encoder *encoder,
                               struct drm_crtc_state *crtc_state,
         * and hope that the desired frequency can be matched (or at least
         * matched sufficiently close that the panel will still work).
         */
-       div = ((clk_get_rate(rgb->clk) * 2) / pclk) - 2;
-       pclk = 0;
+       if (tegra_rgb_pll_rate_change_allowed(rgb)) {
+               /*
+                * Set display controller clock to x2 of PCLK in order to
+                * produce higher resolution pulse positions.
+                */
+               div = 2;
+               pclk *= 2;
+       } else {
+               div = ((clk_get_rate(rgb->clk) * 2) / pclk) - 2;
+               pclk = 0;
+       }
 
        err = tegra_dc_state_setup_clock(dc, crtc_state, rgb->clk_parent,
                                         pclk, div);
                return err;
        }
 
+       rgb->pll_d_out0 = clk_get_sys(NULL, "pll_d_out0");
+       if (IS_ERR(rgb->pll_d_out0)) {
+               err = PTR_ERR(rgb->pll_d_out0);
+               dev_err(dc->dev, "failed to get pll_d_out0: %d\n", err);
+               return err;
+       }
+
+       if (dc->soc->has_pll_d2_out0) {
+               rgb->pll_d2_out0 = clk_get_sys(NULL, "pll_d2_out0");
+               if (IS_ERR(rgb->pll_d2_out0)) {
+                       err = PTR_ERR(rgb->pll_d2_out0);
+                       dev_err(dc->dev, "failed to get pll_d2_out0: %d\n", err);
+                       return err;
+               }
+       }
+
        dc->rgb = &rgb->output;
 
        return 0;
 
 int tegra_dc_rgb_remove(struct tegra_dc *dc)
 {
+       struct tegra_rgb *rgb;
+
        if (!dc->rgb)
                return 0;
 
+       rgb = to_rgb(dc->rgb);
+       clk_put(rgb->pll_d2_out0);
+       clk_put(rgb->pll_d_out0);
+
        tegra_output_remove(dc->rgb);
        dc->rgb = NULL;