]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
video: tegra20: dc: configure behavior if PLLD/D2 is used
authorSvyatoslav Ryhel <clamor95@gmail.com>
Tue, 23 Jan 2024 17:16:23 +0000 (19:16 +0200)
committerAnatolij Gustschin <agust@denx.de>
Sun, 21 Apr 2024 07:07:01 +0000 (09:07 +0200)
If DISP1 is a PLLD/D2 child, it cannot go over 370MHz. The cause
of this is not quite clear. This can be overcomed by further
halving the PLLD/D2 if the target parent rate is over 800MHz.
This way DISP1 and DSI clocks will have the same frequency. The
shift divider in this case has to be calculated from the
original PLLD/D2 frequency and is passed from the DSI driver.

Tested-by: Andreas Westman Dorcsak <hedmoo@yahoo.com> # ASUS Grouper E1565
Tested-by: Ion Agorria <ion@agorria.com> # HTC One X
Tested-by: Svyatoslav Ryhel <clamor95@gmail.com> # Nvidia Tegratab T114
Tested-by: Jonas Schwöbel <jonasschwoebel@yahoo.de> # Microsoft Surface 2
Signed-off-by: Jonas Schwöbel <jonasschwoebel@yahoo.de>
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Acked-by: Thierry Reding <treding@nvidia.com>
drivers/video/tegra20/tegra-dc.c
drivers/video/tegra20/tegra-dc.h
drivers/video/tegra20/tegra-dsi.c

index cc4b5b70015037969ef42cdcb2699573f5a6c015..2c9017807f2afb16cd582cd238ba7085f78e219e 100644 (file)
@@ -46,6 +46,7 @@ struct tegra_lcd_priv {
        fdt_addr_t frame_buffer;        /* Address of frame buffer */
        unsigned pixel_clock;           /* Pixel clock in Hz */
        int dc_clk[2];                  /* Contains clk and its parent */
+       ulong scdiv;                    /* Clock divider used by disp_clk_ctrl */
        bool rotation;                  /* 180 degree panel turn */
        bool pipe;                      /* DC controller: 0 for A, 1 for B */
 };
@@ -124,8 +125,6 @@ static int update_display_mode(struct tegra_lcd_priv *priv)
        struct dc_disp_reg *disp = &priv->dc->disp;
        struct display_timing *dt = &priv->timing;
        unsigned long val;
-       unsigned long rate;
-       unsigned long div;
 
        writel(0x0, &disp->disp_timing_opt);
 
@@ -148,21 +147,11 @@ static int update_display_mode(struct tegra_lcd_priv *priv)
                writel(val, &disp->disp_interface_ctrl);
        }
 
-       /*
-        * The pixel clock divider is in 7.1 format (where the bottom bit
-        * represents 0.5). Here we calculate the divider needed to get from
-        * the display clock (typically 600MHz) to the pixel clock. We round
-        * up or down as requried.
-        */
-       rate = clock_get_periph_rate(priv->dc_clk[0], priv->dc_clk[1]);
-       div = ((rate * 2 + priv->pixel_clock / 2) / priv->pixel_clock) - 2;
-       debug("Display clock %lu, divider %lu\n", rate, div);
-
        if (priv->soc->has_rgb)
                writel(0x00010001, &disp->shift_clk_opt);
 
        val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT;
-       val |= div << SHIFT_CLK_DIVIDER_SHIFT;
+       val |= priv->scdiv << SHIFT_CLK_DIVIDER_SHIFT;
        writel(val, &disp->disp_clk_ctrl);
 
        return 0;
@@ -312,6 +301,17 @@ static int tegra_display_probe(struct tegra_lcd_priv *priv,
                rate /= 2;
 #endif
 
+       /*
+        * The pixel clock divider is in 7.1 format (where the bottom bit
+        * represents 0.5). Here we calculate the divider needed to get from
+        * the display clock (typically 600MHz) to the pixel clock. We round
+        * up or down as required.
+        */
+       if (!priv->scdiv)
+               priv->scdiv = ((rate * 2 + priv->pixel_clock / 2)
+                                               / priv->pixel_clock) - 2;
+       debug("Display clock %lu, divider %lu\n", rate, priv->scdiv);
+
        /*
         * HOST1X is init by default at 150MHz with PLLC as parent
         */
@@ -373,6 +373,14 @@ static int tegra_lcd_probe(struct udevice *dev)
                }
        }
 
+       /* Get shift clock divider from Tegra DSI if used */
+       if (!strcmp(priv->panel->name, TEGRA_DSI_A) ||
+           !strcmp(priv->panel->name, TEGRA_DSI_B)) {
+               struct tegra_dc_plat *dc_plat = dev_get_plat(priv->panel);
+
+               priv->scdiv = dc_plat->scdiv;
+       }
+
        if (tegra_display_probe(priv, (void *)plat->base)) {
                debug("%s: Failed to probe display driver\n", __func__);
                return -1;
index 75fc0fa4de4f78b2f6f9e0fcb7c13263b4967d17..05042dab1c6c908fbd91cd7833da543012c5a4aa 100644 (file)
@@ -23,6 +23,7 @@ struct tegra_dc_plat {
        struct udevice *dev;            /* Display controller device */
        struct dc_ctlr *dc;             /* Display controller regmap */
        bool pipe;                      /* DC number: 0 for A, 1 for B */
+       ulong scdiv;                    /* Shift clock divider */
 };
 
 /* This holds information about a window which can be displayed */
index 72b91ed26bfb1e5fb3f5b1e3965614914b82cd14..de225ed3766ab01737a2b75972a82cb87e725370 100644 (file)
@@ -743,6 +743,7 @@ static int tegra_dsi_panel_timings(struct udevice *dev,
 static void tegra_dsi_init_clocks(struct udevice *dev)
 {
        struct tegra_dsi_priv *priv = dev_get_priv(dev);
+       struct tegra_dc_plat *dc_plat = dev_get_plat(dev);
        struct mipi_dsi_device *device = &priv->device;
        unsigned int mul, div;
        unsigned long bclk, plld;
@@ -754,6 +755,19 @@ static void tegra_dsi_init_clocks(struct udevice *dev)
 
        plld = DIV_ROUND_UP(bclk * 8, USEC_PER_SEC);
 
+       dc_plat->scdiv = ((plld * USEC_PER_SEC +
+               priv->timing.pixelclock.typ / 2) /
+               priv->timing.pixelclock.typ) - 2;
+
+       /*
+        * BUG: If DISP1 is a PLLD/D2 child, it cannot go over 370MHz. The
+        * cause of this is not quite clear. This can be overcomed by
+        * halving the PLLD/D2 if the target rate is > 800MHz. This way
+        * DISP1 and DSI clocks will be equal.
+        */
+       if (plld > 800)
+               plld /= 2;
+
        switch (clock_get_osc_freq()) {
        case CLOCK_OSC_FREQ_12_0: /* OSC is 12Mhz */
        case CLOCK_OSC_FREQ_48_0: /* OSC is 48Mhz */