[PATCH V5 4/5] phy: freescale: fsl-samsung-hdmi: Use closest divider

Adam Ford aford173 at gmail.com
Wed Sep 4 07:14:08 PDT 2024


On Wed, Sep 4, 2024 at 8:54 AM Frieder Schrempf
<frieder.schrempf at kontron.de> wrote:
>
> On 04.09.24 3:40 PM, Frieder Schrempf wrote:
> > On 04.09.24 4:32 AM, Adam Ford wrote:
> >> 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>
> >> ---
> >> V5:  No Change
> >> V4:  New to series
> >> ---
> >>  drivers/phy/freescale/phy-fsl-samsung-hdmi.c | 40 +++++++++++++++-----
> >>  1 file changed, 31 insertions(+), 9 deletions(-)
> >>
> >> diff --git a/drivers/phy/freescale/phy-fsl-samsung-hdmi.c b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
> >> index 8f2c0082aa12..56b08e684179 100644
> >> --- a/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
> >> +++ b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
> >> @@ -550,7 +550,7 @@ static unsigned long phy_clk_recalc_rate(struct clk_hw *hw,
> >>  static long phy_clk_round_rate(struct clk_hw *hw,
> >>                             unsigned long rate, unsigned long *parent_rate)
> >>  {
> >> -    u32 int_div_clk;
> >> +    u32 int_div_clk, delta_int, delta_frac;
> >>      int i;
> >>      u16 m;
> >>      u8 p, s;
> >> @@ -563,6 +563,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,15 +580,21 @@ 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;
> >> +    /* Calculate the differences and use the closest one */
> >> +    delta_frac = (rate - phy_pll_cfg[i].pixclk);
> >> +    delta_int = (rate - int_div_clk);
> >> +
> >> +    if (delta_int < delta_frac)
> >> +            return int_div_clk;
> >> +    else
> >> +            return phy_pll_cfg[i].pixclk;
>
> I would also prefer to use a helper for calculating the closest rate.
> Something like:
>
> static u32 fsl_samsung_hdmi_phy_get_closest_rate(unsigned long rate,
>                                                  u32 int_div_clk,
>                                                  u32 frac_div_clk)
> {
>         if ((rate - int_div_clk) < (rate - frac_div_clk))
>                 return int_div_clk;
>
>         return frac_div_clk;
> }

I like this idea.  As Dominique noted, the int_div_clk might be
greater than rate, so I'll add abs() here when I simplify this.
>
> This can be used above:
>
> return fsl_samsung_hdmi_phy_get_closest_rate(rate, int_div_clk,
> phy_pll_cfg[i].pixclk);
>
> And also below in phy_clk_set_rate():
>
> 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];
>

I like this too.

adam
>
> >>  }
> >>
> >>  static int phy_clk_set_rate(struct clk_hw *hw,
> >>                          unsigned long rate, unsigned long parent_rate)
> >>  {
> >>      struct fsl_samsung_hdmi_phy *phy = to_fsl_samsung_hdmi_phy(hw);
> >> -    u32 int_div_clk;
> >> +    u32 int_div_clk, delta_int, delta_frac;
> >>      int i;
> >>      u16 m;
> >>      u8 p, s;
> >> @@ -602,19 +609,34 @@ static int phy_clk_set_rate(struct clk_hw *hw,
> >>              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;
> >
> >                              ^ unneeded whitespace (comment belongs to
> > patch 3/5)
> >
> >> +            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];
> >>      }
> >>
> >> +    /* Calculate the differences for each clock against the requested value */
> >> +    delta_frac = (rate - phy_pll_cfg[i].pixclk);
> >> +    delta_int = (rate - int_div_clk);
> >> +
> >> +    /* Use the value closest to the desired */
> >> +    if (delta_int < delta_frac)
> >> +            phy->cur_cfg  = &calculated_phy_pll_cfg;
> >
> >                              ^ unneeded whitespace
> >
> > Are you sure that this is correct? The calculated_phy_pll_cfg is only
> > set above if there is an exact match for the integer divider. I don't
> > think it contains the data you expect here, or am I missing something?
> >
> >> +    else
> >> +            phy->cur_cfg = &phy_pll_cfg[i];
> >> +done:
> >>      return fsl_samsung_hdmi_phy_configure(phy, phy->cur_cfg);
> >>  }
> >>



More information about the linux-phy mailing list