]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
drm/tegra: dc: rgb: Allow changing PLLD rate on Tegra30+
authorDmitry Osipenko <digetx@gmail.com>
Wed, 29 Sep 2021 22:28:05 +0000 (01:28 +0300)
committerSasha Levin <sashal@kernel.org>
Tue, 26 Mar 2024 22:21:24 +0000 (18:21 -0400)
[ Upstream commit 0c921b6d4ba06bc899fd84d3ce1c1afd3d00bc1c ]

Asus Transformer TF700T is a Tegra30 tablet device which uses RGB->DSI
bridge that requires a precise clock rate in order to operate properly.
Tegra30 has a dedicated PLL for each display controller, hence the PLL
rate can be changed freely. Allow PLL rate changes on Tegra30+ for RGB
output. Configure the clock rate before display controller is enabled
since DC itself may be running off this PLL and it's not okay to change
the rate of the active PLL that doesn't support dynamic frequency
switching since hardware will hang.

Tested-by: Maxim Schwalm <maxim.schwalm@gmail.com> #TF700T
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
Stable-dep-of: bc456b5d93db ("drm/tegra: rgb: Fix some error handling paths in tegra_dc_rgb_probe()")
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/gpu/drm/tegra/dc.c
drivers/gpu/drm/tegra/dc.h
drivers/gpu/drm/tegra/rgb.c

index abb409b08bc6452792b22f6f23792d30f84c8640..f44a64e896587526a08eb37bf3578aaee0daac92 100644 (file)
@@ -1762,10 +1762,9 @@ int tegra_dc_state_setup_clock(struct tegra_dc *dc,
        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);
@@ -1796,11 +1795,6 @@ static void tegra_dc_commit_state(struct tegra_dc *dc,
        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)
@@ -2002,6 +1996,9 @@ static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
        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);
@@ -2076,8 +2073,11 @@ static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
        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);
@@ -2685,6 +2685,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = {
        .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 = {
@@ -2707,6 +2708,7 @@ 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 = {
@@ -2729,6 +2731,7 @@ 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 = {
@@ -2751,6 +2754,7 @@ 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 = {
@@ -2773,6 +2777,7 @@ 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[] = {
@@ -2823,6 +2828,7 @@ static const struct tegra_dc_soc_info tegra186_dc_soc_info = {
        .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[] = {
@@ -2873,6 +2879,7 @@ static const struct tegra_dc_soc_info tegra194_dc_soc_info = {
        .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[] = {
index 40378308d527a1369ed0f19e644e506a8b45e145..c9c4c45c05183aa0be81fee0d2257685be4fb183 100644 (file)
@@ -76,6 +76,7 @@ struct tegra_dc_soc_info {
        bool has_win_b_vfilter_mem_client;
        bool has_win_c_without_vert_filter;
        bool plane_tiled_memory_bandwidth_x2;
+       bool has_pll_d2_out0;
 };
 
 struct tegra_dc {
index 606c78a2b988f1756048baae69734962250e52b2..a84aee2f4acd0ceeeaa9187d4d53dbce6efdaf7a 100644 (file)
@@ -17,6 +17,8 @@ struct tegra_rgb {
        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;
 };
@@ -123,6 +125,18 @@ static void tegra_rgb_encoder_enable(struct drm_encoder *encoder)
        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,
@@ -151,8 +165,17 @@ tegra_rgb_encoder_atomic_check(struct drm_encoder *encoder,
         * 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);
@@ -210,6 +233,22 @@ int tegra_dc_rgb_probe(struct tegra_dc *dc)
                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;
@@ -217,9 +256,15 @@ int tegra_dc_rgb_probe(struct tegra_dc *dc)
 
 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;