[PATCH V6 4/5] phy: freescale: fsl-samsung-hdmi: Use closest divider
Adam Ford
aford173 at gmail.com
Wed Sep 4 16:30:32 PDT 2024
Currently, if the clock values cannot be set to the exact rate,
the round_rate and set_rate functions use the closest value found in
the look-up-table. In preparation of removing values from the LUT
that can be calculated evenly with the integer calculator, it's
necessary to ensure to check both the look-up-table and the integer
divider clock values to get the closest values to the requested
value. It does this by measuring the difference between the
requested clock value and the closest value in both integer divider
calucator and the fractional clock look-up-table.
Which ever has the smallest difference between them is returned as
the cloesest rate.
Signed-off-by: Adam Ford <aford173 at gmail.com>
Signed-off-by: Dominique Martinet <dominique.martinet at atmark-techno.com>
---
V6: Simplify the calculation of the closest rate and fix
a situation where the integer divider values may not be properly
setup before they are used.
Fixup some comments
V5: No Change
V4: New to series
---
drivers/phy/freescale/phy-fsl-samsung-hdmi.c | 46 ++++++++++++++------
1 file changed, 33 insertions(+), 13 deletions(-)
diff --git a/drivers/phy/freescale/phy-fsl-samsung-hdmi.c b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
index 4b13e386e5ba..9a21dbbf1a82 100644
--- a/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
+++ b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
@@ -547,6 +547,16 @@ static unsigned long phy_clk_recalc_rate(struct clk_hw *hw,
return phy->cur_cfg->pixclk;
}
+static u32 fsl_samsung_hdmi_phy_get_closest_rate(unsigned long rate,
+ u32 int_div_clk, u32 frac_div_clk)
+{
+ /* The int_div_clk may be greater than rate, so cast it and use ABS */
+ if (abs((long)rate - (long)int_div_clk) < (rate - frac_div_clk))
+ return int_div_clk;
+
+ return frac_div_clk;
+}
+
static long phy_clk_round_rate(struct clk_hw *hw,
unsigned long rate, unsigned long *parent_rate)
{
@@ -563,6 +573,7 @@ static long phy_clk_round_rate(struct clk_hw *hw,
for (i = ARRAY_SIZE(phy_pll_cfg) - 1; i >= 0; i--)
if (phy_pll_cfg[i].pixclk <= rate)
break;
+
/* If the rate is an exact match, return it now */
if (rate == phy_pll_cfg[i].pixclk)
return phy_pll_cfg[i].pixclk;
@@ -579,8 +590,7 @@ static long phy_clk_round_rate(struct clk_hw *hw,
if (int_div_clk == rate)
return int_div_clk;
- /* Fall back to the closest value in the LUT */
- return phy_pll_cfg[i].pixclk;
+ return fsl_samsung_hdmi_phy_get_closest_rate(rate, int_div_clk, phy_pll_cfg[i].pixclk);
}
static int phy_clk_set_rate(struct clk_hw *hw,
@@ -594,27 +604,37 @@ static int phy_clk_set_rate(struct clk_hw *hw,
/* If the integer divider works, just use it */
int_div_clk = fsl_samsung_hdmi_phy_find_pms(rate * 5, &p, &m, &s) / 5;
+ calculated_phy_pll_cfg.pixclk = int_div_clk;
+ calculated_phy_pll_cfg.pll_div_regs[0] = FIELD_PREP(REG01_PMS_P_MASK, p);
+ calculated_phy_pll_cfg.pll_div_regs[1] = m;
+ calculated_phy_pll_cfg.pll_div_regs[2] = FIELD_PREP(REG03_PMS_S_MASK, s-1);
+ phy->cur_cfg = &calculated_phy_pll_cfg;
if (int_div_clk == rate) {
dev_dbg(phy->dev, "fsl_samsung_hdmi_phy: using integer divider\n");
- calculated_phy_pll_cfg.pixclk = int_div_clk;
- calculated_phy_pll_cfg.pll_div_regs[0] = FIELD_PREP(REG01_PMS_P_MASK, p);
- calculated_phy_pll_cfg.pll_div_regs[1] = m;
- calculated_phy_pll_cfg.pll_div_regs[2] = FIELD_PREP(REG03_PMS_S_MASK, s-1);
- /* pll_div_regs 3-6 are fixed and pre-defined already */
- phy->cur_cfg = &calculated_phy_pll_cfg;
+ goto done;
} else {
/* Otherwise, search the LUT */
- dev_dbg(phy->dev, "fsl_samsung_hdmi_phy: using fractional divider\n");
- for (i = ARRAY_SIZE(phy_pll_cfg) - 1; i >= 0; i--)
- if (phy_pll_cfg[i].pixclk <= rate)
+ for (i = ARRAY_SIZE(phy_pll_cfg) - 1; i >= 0; i--) {
+ if (phy_pll_cfg[i].pixclk == rate) {
+ dev_dbg(phy->dev, "fsl_samsung_hdmi_phy: using fractional divider\n");
+ phy->cur_cfg = &phy_pll_cfg[i];
+ goto done;
+ }
+
+ if (phy_pll_cfg[i].pixclk < rate)
break;
+ }
if (i < 0)
return -EINVAL;
-
- phy->cur_cfg = &phy_pll_cfg[i];
}
+ if (fsl_samsung_hdmi_phy_get_closest_rate(rate, int_div_clk,
+ phy_pll_cfg[i].pixclk) == int_div_clk)
+ phy->cur_cfg = &calculated_phy_pll_cfg;
+ else
+ phy->cur_cfg = &phy_pll_cfg[i];
+done:
return fsl_samsung_hdmi_phy_configure(phy, phy->cur_cfg);
}
--
2.43.0
More information about the linux-phy
mailing list