Convert the limited MIPI clock calculations to a full range of settings
based on math including H/W limitation validation.
Since the required DSI division setting must be specified from external
sources before calculations, expose a new API to set it.

Signed-off-by: Chris Brandt <chris.bra...@renesas.com>
Signed-off-by: hienhuynh <hien.huynh...@renesas.com>
Signed-off-by: Nghia Vo <nghia.vo...@renesas.com>

---
v1->v2:
- Remove unnecessary parentheses
- Add target argument to new API
- DPI mode has more restrictions on DIV_A and DIV_B
---
 drivers/clk/renesas/rzg2l-cpg.c | 129 +++++++++++++++++++++++++++++---
 include/linux/clk/renesas.h     |   4 +
 2 files changed, 123 insertions(+), 10 deletions(-)

diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c
index 187233302818..33d799a2e8ac 100644
--- a/drivers/clk/renesas/rzg2l-cpg.c
+++ b/drivers/clk/renesas/rzg2l-cpg.c
@@ -74,6 +74,22 @@
 #define MSTOP_OFF(conf)                FIELD_GET(GENMASK(31, 16), (conf))
 #define MSTOP_MASK(conf)       FIELD_GET(GENMASK(15, 0), (conf))
 
+#define PLL5_FOUTVCO_MIN       800000000
+#define PLL5_FOUTVCO_MAX       3000000000
+#define PLL5_POSTDIV_MIN       1
+#define PLL5_POSTDIV_MAX       7
+#define PLL5_POSTDIV_DEF       1
+#define PLL5_REFDIV_MIN                1
+#define PLL5_REFDIV_MAX                2
+#define PLL5_REFDIV_DEF                1
+#define PLL5_INTIN_MIN         20
+#define PLL5_INTIN_MAX         320
+#define PLL5_INTIN_DEF         125
+#define PLL5_FRACIN_DEF                0
+
+#define PLL5_TARGET_DPI                0
+#define PLL5_TARGET_DSI                1
+
 /**
  * struct clk_hw_data - clock hardware data
  * @hw: clock hw
@@ -129,6 +145,12 @@ struct rzg2l_pll5_param {
        u8 pl5_spread;
 };
 
+/* PLL5 output will be used for DPI or MIPI-DSI */
+static int dsi_div_target = PLL5_TARGET_DPI;
+
+/* Required division ratio for MIPI D-PHY clock changes depending on 
resolution and lanes. */
+static int dsi_div_ab;
+
 struct rzg2l_pll5_mux_dsi_div_param {
        u8 clksrc;
        u8 dsi_div_a;
@@ -557,24 +579,102 @@ rzg2l_cpg_sd_mux_clk_register(const struct cpg_core_clk 
*core,
 }
 
 static unsigned long
-rzg2l_cpg_get_foutpostdiv_rate(struct rzg2l_pll5_param *params,
+rzg2l_cpg_get_foutpostdiv_rate(struct rzg2l_cpg_priv *priv,
+                              struct rzg2l_pll5_param *params,
                               unsigned long rate)
 {
        unsigned long foutpostdiv_rate, foutvco_rate;
+       u8 div = 1;
+       bool found = 0;
+       int a, b;
+
+       if (priv->mux_dsi_div_params.clksrc)
+               div = 2;
+
+       /* Calculate the DIV_DSI_A and DIV_DSI_B based on the final DIV DSI */
+       for (a = 0; a < 4; a++) {
+
+               if (dsi_div_target == PLL5_TARGET_DPI && a == 0)
+                       continue;       /* 1/1 div not supported for DIV_DSI_A 
for DPI */
+
+               for (b = 0; b < 16; b++) {
+
+                       if (dsi_div_target == PLL5_TARGET_DPI && b != 0)
+                               continue;       /* Only 1/1 div supported for 
DIV_DSI_B in DPI */
+
+                       if (((1 << a) * (b + 1)) == dsi_div_ab) {
+                               priv->mux_dsi_div_params.dsi_div_a = a;
+                               priv->mux_dsi_div_params.dsi_div_b = b;
+
+                               goto found_dsi_div;
+                       }
+               }
+       }
+
+found_dsi_div:
+       /*
+        * Below conditions must be set for PLL5 parameters:
+        * - REFDIV must be between 1 and 2.
+        * - POSTDIV1/2 must be between 1 and 7.
+        * - INTIN must be between 20 and 320.
+        * - FOUTVCO must be between 800MHz and 3000MHz.
+        */
+       for (params->pl5_postdiv1 = PLL5_POSTDIV_MIN;
+            params->pl5_postdiv1 < PLL5_POSTDIV_MAX + 1;
+            params->pl5_postdiv1++) {
+               for (params->pl5_postdiv2 = PLL5_POSTDIV_MIN;
+                    params->pl5_postdiv2 < PLL5_POSTDIV_MAX + 1;
+                    params->pl5_postdiv2++) {
+                       foutvco_rate = rate * ((1 << 
priv->mux_dsi_div_params.dsi_div_a) *
+                                              
(priv->mux_dsi_div_params.dsi_div_b + 1)) *
+                                             div * params->pl5_postdiv1 * 
params->pl5_postdiv2;
+                       if (foutvco_rate < PLL5_FOUTVCO_MIN + 1 ||
+                           foutvco_rate > PLL5_FOUTVCO_MAX - 1)
+                               continue;
+
+                       for (params->pl5_refdiv = PLL5_REFDIV_MIN;
+                            params->pl5_refdiv < PLL5_REFDIV_MAX + 1;
+                            params->pl5_refdiv++) {
+                               params->pl5_intin = (foutvco_rate * 
params->pl5_refdiv) /
+                                                   (EXTAL_FREQ_IN_MEGA_HZ * 
MEGA);
+                               if (params->pl5_intin < PLL5_INTIN_MIN + 1 ||
+                                   params->pl5_intin > PLL5_INTIN_MAX - 1)
+                                       continue;
+                               params->pl5_fracin = div_u64(((u64)
+                                                    (foutvco_rate * 
params->pl5_refdiv) %
+                                                    (EXTAL_FREQ_IN_MEGA_HZ * 
MEGA)) << 24,
+                                                    EXTAL_FREQ_IN_MEGA_HZ * 
MEGA);
+                               found = 1;
+                               goto found_clk;
+                       }
+               }
+       }
+
+found_clk:
+       if (!found) {
+               params->pl5_intin = PLL5_INTIN_DEF;
+               params->pl5_fracin = PLL5_FRACIN_DEF;
+               params->pl5_refdiv = PLL5_REFDIV_DEF;
+               params->pl5_postdiv1 = PLL5_POSTDIV_DEF;
+               params->pl5_postdiv2 = PLL5_POSTDIV_DEF;
+       }
 
-       params->pl5_intin = rate / MEGA;
-       params->pl5_fracin = div_u64(((u64)rate % MEGA) << 24, MEGA);
-       params->pl5_refdiv = 2;
-       params->pl5_postdiv1 = 1;
-       params->pl5_postdiv2 = 1;
        params->pl5_spread = 0x16;
 
        foutvco_rate = div_u64(mul_u32_u32(EXTAL_FREQ_IN_MEGA_HZ * MEGA,
-                                          (params->pl5_intin << 24) + 
params->pl5_fracin),
-                              params->pl5_refdiv) >> 24;
+                      (params->pl5_intin << 24) + params->pl5_fracin),
+                      params->pl5_refdiv) >> 24;
        foutpostdiv_rate = DIV_ROUND_CLOSEST_ULL(foutvco_rate,
                                                 params->pl5_postdiv1 * 
params->pl5_postdiv2);
 
+       /* If foutvco is above 1.5GHz, change parent and recalculate */
+       if (priv->mux_dsi_div_params.clksrc && foutvco_rate > 1500000000) {
+               priv->mux_dsi_div_params.clksrc = 0;
+               dsi_div_ab *= 2;
+               dsi_div_target = PLL5_TARGET_DSI;       /* Assume MIPI-DSI */
+               return rzg2l_cpg_get_foutpostdiv_rate(priv, params, rate);
+       }
+
        return foutpostdiv_rate;
 }
 
@@ -607,7 +707,7 @@ static unsigned long rzg2l_cpg_get_vclk_parent_rate(struct 
clk_hw *hw,
        struct rzg2l_pll5_param params;
        unsigned long parent_rate;
 
-       parent_rate = rzg2l_cpg_get_foutpostdiv_rate(&params, rate);
+       parent_rate = rzg2l_cpg_get_foutpostdiv_rate(priv, &params, rate);
 
        if (priv->mux_dsi_div_params.clksrc)
                parent_rate /= 2;
@@ -626,6 +726,13 @@ static int rzg2l_cpg_dsi_div_determine_rate(struct clk_hw 
*hw,
        return 0;
 }
 
+void rzg2l_cpg_dsi_div_set_divider(int divider, int target)
+{
+       dsi_div_ab = divider;
+       dsi_div_target = target;
+}
+EXPORT_SYMBOL_GPL(rzg2l_cpg_dsi_div_set_divider);
+
 static int rzg2l_cpg_dsi_div_set_rate(struct clk_hw *hw,
                                      unsigned long rate,
                                      unsigned long parent_rate)
@@ -859,7 +966,7 @@ static int rzg2l_cpg_sipll5_set_rate(struct clk_hw *hw,
 
        vclk_rate = rzg2l_cpg_get_vclk_rate(hw, rate);
        sipll5->foutpostdiv_rate =
-               rzg2l_cpg_get_foutpostdiv_rate(&params, vclk_rate);
+               rzg2l_cpg_get_foutpostdiv_rate(priv, &params, vclk_rate);
 
        /* Put PLL5 into standby mode */
        writel(CPG_SIPLL5_STBY_RESETB_WEN, priv->base + CPG_SIPLL5_STBY);
@@ -949,6 +1056,8 @@ rzg2l_cpg_sipll5_register(const struct cpg_core_clk *core,
        priv->mux_dsi_div_params.clksrc = 1; /* Use clk src 1 for DSI */
        priv->mux_dsi_div_params.dsi_div_a = 1; /* Divided by 2 */
        priv->mux_dsi_div_params.dsi_div_b = 2; /* Divided by 3 */
+       dsi_div_ab = (1 << priv->mux_dsi_div_params.dsi_div_a) *
+                    (priv->mux_dsi_div_params.dsi_div_b + 1);
 
        return clk_hw->clk;
 }
diff --git a/include/linux/clk/renesas.h b/include/linux/clk/renesas.h
index 0ebbe2f0b45e..0cdbd3922cf4 100644
--- a/include/linux/clk/renesas.h
+++ b/include/linux/clk/renesas.h
@@ -33,3 +33,7 @@ void cpg_mssr_detach_dev(struct generic_pm_domain *unused, 
struct device *dev);
 #define cpg_mssr_detach_dev    NULL
 #endif
 #endif
+
+#ifdef CONFIG_CLK_RZG2L
+void rzg2l_cpg_dsi_div_set_divider(int divider, int target);
+#endif
-- 
2.50.1

Reply via email to