There is a workaround in which the tegra rgb driver initializes the tegra dc pclk to 0 so that it will skip setting the parent clk rate. The relevant commits: 3cebae6737b100323baca21de6bce6647249e778 76d59ed049197bdaaa24c0e061a105af6ce74457 A more recent commit sets the rate of the dc clk itself: 39e08affecf0998be1b01f4752016e33fa98eb9a This doesn't make sense because it always sets the dc clk to 0. Is this intended behavior or does it just happen to be working for the current tegra 2 boards in the mainline kernel? For context I am running the kernel on a tegra 2 based Galaxy Tab 10.1. The display panel driver is out of tree. This panel has very low clock rate tolerances so it must be driven at a rate very close to the required specification (68.75Mhz). pll_p is not adequate to drive this panel so pll_d must be used. Another issue is the current workaround is always forcing the disp1 clk to zero. The Galaxy Tab 10.1 locks up completely when calling clk_set_rate with a clk rate of 0 whether the parent is pll_p or pll_d. This patch adds a flag single_display_pll to mark that the device has only one display pll. This replaces the the pclk = 0 workaround. There is a comment in rgb.c about using the shift clock divider for tegra 2 but the divider is not set due to the has_nvdisplay flag. This patch also sets the shift clock divider based on the single_display_pll flag. A change I'm uncertain about. In tegra_dc_commit_state() the dc clock is now being set to the display panel rate rather than zero. I don't have any other tegra devices to test with. So I don't know if this breaks other devices. Previously the code was trying to set the clock rate to zero anyways. Signed-off-by: ryang <decatf@xxxxxxxxx> --- drivers/gpu/drm/tegra/dc.c | 9 +++++++-- drivers/gpu/drm/tegra/dc.h | 1 + drivers/gpu/drm/tegra/rgb.c | 1 - 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 965088afcfad..03f1ad630254 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1664,7 +1664,7 @@ static void tegra_dc_commit_state(struct tegra_dc *dc, * which is shared with other peripherals. Changing the clock rate * should therefore be avoided. */ - if (state->pclk > 0) { + if (!dc->soc->single_display_pll) { err = clk_set_rate(state->clk, state->pclk); if (err < 0) dev_err(dc->dev, @@ -1676,7 +1676,7 @@ static void tegra_dc_commit_state(struct tegra_dc *dc, state->div); DRM_DEBUG_KMS("pclk: %lu\n", state->pclk); - if (!dc->soc->has_nvdisplay) { + if (!dc->soc->has_nvdisplay || dc->soc->single_display_pll) { value = SHIFT_CLK_DIVIDER(state->div) | PIXEL_CLK_DIVIDER_PCD1; tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL); } @@ -2108,6 +2108,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = { .modifiers = tegra20_modifiers, .has_win_a_without_filters = true, .has_win_c_without_vert_filter = true, + .single_display_pll = true, }; static const struct tegra_dc_soc_info tegra30_dc_soc_info = { @@ -2127,6 +2128,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = { .modifiers = tegra20_modifiers, .has_win_a_without_filters = false, .has_win_c_without_vert_filter = false, + .single_display_pll = false, }; static const struct tegra_dc_soc_info tegra114_dc_soc_info = { @@ -2146,6 +2148,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = { .modifiers = tegra20_modifiers, .has_win_a_without_filters = false, .has_win_c_without_vert_filter = false, + .single_display_pll = false, }; static const struct tegra_dc_soc_info tegra124_dc_soc_info = { @@ -2165,6 +2168,7 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = { .modifiers = tegra124_modifiers, .has_win_a_without_filters = false, .has_win_c_without_vert_filter = false, + .single_display_pll = false, }; static const struct tegra_dc_soc_info tegra210_dc_soc_info = { @@ -2184,6 +2188,7 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = { .modifiers = tegra124_modifiers, .has_win_a_without_filters = false, .has_win_c_without_vert_filter = false, + .single_display_pll = false, }; static const struct tegra_windowgroup_soc tegra186_dc_wgrps[] = { diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index e96f582ca692..abe75d4ad8bd 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -69,6 +69,7 @@ struct tegra_dc_soc_info { const u64 *modifiers; bool has_win_a_without_filters; bool has_win_c_without_vert_filter; + bool single_display_pll; }; struct tegra_dc { diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c index 28a78d3120bc..53a872a03dea 100644 --- a/drivers/gpu/drm/tegra/rgb.c +++ b/drivers/gpu/drm/tegra/rgb.c @@ -196,7 +196,6 @@ tegra_rgb_encoder_atomic_check(struct drm_encoder *encoder, * matched sufficiently close that the panel will still work). */ 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); -- 2.17.1