[PATCH v4 4/8] phy: rockchip: samsung-hdptx: Handle uncommitted PHY config changes
Andy Yan
andy.yan at rock-chips.com
Thu Jul 23 04:50:31 PDT 2026
Hello,
On 6/12/26 07:45, Cristian Ciocaltea wrote:
> Any changes to the PHY link rate and/or color depth done via the HDMI
> PHY configuration API are not immediately programmed into the hardware,
> but are delayed until the PHY usage count gets incremented from 0 to 1,
> that is when it is powered on or when the PLL clock exposed through
> the CCF API is prepared, whichever comes first.
>
> Since the clock might remain in prepared state after subsequent PHY
> config changes, the programming can also be triggered via
> clk_ops.set_rate(). However, from the clock consumer perspective (i.e.
> VOP2 display controller), the (pixel) clock rate doesn't vary with bpc,
> as that is handled internally by the PHY and reflected in the TDMS
> character rate only.
>
> As a consequence, changing the bpc while preserving the modeline may
> lead to out-of-sync issues between CCF and HDMI PHY config state,
> because the .set_rate() callback is not invoked when clock rate remains
> constant. This may also happen when the PHY PLL has been pre-programmed
> by an external entity, e.g. the bootloader, which is actually a
> regression introduced by the recent FRL patches.
>
> Introduce a pll_config_dirty flag to keep track of uncommitted PHY
> config changes and use it in clk_ops.determine_rate() to invalidate the
> current clock rate (as known by CCF) and, consequently, ensure those
> changes are programmed into hardware via clk_ops.set_rate().
>
> Moreover, proceed with a similar fix in phy_ops.power_on() callback, to
> handle the scenario where the CCF API is not used due to operating in
> FRL mode, while the clock is still in a prepared state and thus
> preventing rk_hdptx_phy_consumer_get() to apply the updated PHY
> configuration.
>
> Fixes: de5dba833118 ("phy: rockchip: samsung-hdptx: Add HDMI 2.1 FRL support")
> Fixes: 9d0ec51d7c22 ("phy: rockchip: samsung-hdptx: Add high color depth management")
> Tested-by: Thomas Niederprüm <dubito at online.de>
> Tested-by: Simon Wright <simon at symple.nz>
> Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea at collabora.com>
Reviewed-by: Andy Yan <andy.yan at rock-chips.com>
> ---
> drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c | 84 +++++++++++++----------
> 1 file changed, 48 insertions(+), 36 deletions(-)
>
> diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
> index b210c1a88b25..25bd821cd039 100644
> --- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
> +++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
> @@ -413,6 +413,7 @@ struct rk_hdptx_phy {
>
> /* clk provider */
> struct clk_hw hw;
> + bool pll_config_dirty;
> bool restrict_rate_change;
>
> atomic_t usage_count;
> @@ -1260,13 +1261,19 @@ static int rk_hdptx_tmds_ropll_cmn_config(struct rk_hdptx_phy *hdptx)
>
> static int rk_hdptx_pll_cmn_config(struct rk_hdptx_phy *hdptx)
> {
> + int ret;
> +
> if (hdptx->hdmi_cfg.rate <= HDMI20_MAX_RATE)
> - return rk_hdptx_tmds_ropll_cmn_config(hdptx);
> + ret = rk_hdptx_tmds_ropll_cmn_config(hdptx);
> + else if (hdptx->hdmi_cfg.rate == FRL_8G4L_RATE)
> + ret = rk_hdptx_frl_lcpll_ropll_cmn_config(hdptx);
> + else
> + ret = rk_hdptx_frl_lcpll_cmn_config(hdptx);
>
> - if (hdptx->hdmi_cfg.rate == FRL_8G4L_RATE)
> - return rk_hdptx_frl_lcpll_ropll_cmn_config(hdptx);
> + if (!ret)
> + hdptx->pll_config_dirty = false;
>
> - return rk_hdptx_frl_lcpll_cmn_config(hdptx);
> + return ret;
> }
>
> static int rk_hdptx_frl_lcpll_mode_config(struct rk_hdptx_phy *hdptx)
> @@ -1347,25 +1354,22 @@ static int rk_hdptx_phy_consumer_get(struct rk_hdptx_phy *hdptx)
> return 0;
>
> ret = regmap_read(hdptx->grf, GRF_HDPTX_STATUS, &status);
> - if (ret)
> - goto dec_usage;
> -
> - if (status & HDPTX_O_PLL_LOCK_DONE)
> - dev_warn(hdptx->dev, "PLL locked by unknown consumer!\n");
> + if (ret) {
> + atomic_dec(&hdptx->usage_count);
> + return ret;
> + }
>
> if (mode == PHY_MODE_DP) {
> rk_hdptx_dp_reset(hdptx);
> } else {
> - ret = rk_hdptx_pll_cmn_config(hdptx);
> - if (ret)
> - goto dec_usage;
> + /*
> + * Ignore PLL config errors at this point as pll_config_dirty
> + * was not reset and, therefore, operation will be retried.
> + */
> + rk_hdptx_pll_cmn_config(hdptx);
> }
>
> return 0;
> -
> -dec_usage:
> - atomic_dec(&hdptx->usage_count);
> - return ret;
> }
>
> static int rk_hdptx_phy_consumer_put(struct rk_hdptx_phy *hdptx, bool force)
> @@ -1700,13 +1704,18 @@ static int rk_hdptx_phy_power_on(struct phy *phy)
> if (ret)
> rk_hdptx_phy_consumer_put(hdptx, true);
> } else {
> - regmap_write(hdptx->grf, GRF_HDPTX_CON0,
> - HDPTX_MODE_SEL << 16 | FIELD_PREP(HDPTX_MODE_SEL, 0x0));
> + if (hdptx->pll_config_dirty)
> + ret = rk_hdptx_pll_cmn_config(hdptx);
>
> - if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
> - ret = rk_hdptx_frl_lcpll_mode_config(hdptx);
> - else
> - ret = rk_hdptx_tmds_ropll_mode_config(hdptx);
> + if (!ret) {
> + regmap_write(hdptx->grf, GRF_HDPTX_CON0,
> + HDPTX_MODE_SEL << 16 | FIELD_PREP(HDPTX_MODE_SEL, 0x0));
> +
> + if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
> + ret = rk_hdptx_frl_lcpll_mode_config(hdptx);
> + else
> + ret = rk_hdptx_tmds_ropll_mode_config(hdptx);
> + }
>
> if (ret)
> rk_hdptx_phy_consumer_put(hdptx, true);
> @@ -2081,7 +2090,10 @@ static int rk_hdptx_phy_configure(struct phy *phy, union phy_configure_opts *opt
> dev_err(hdptx->dev, "invalid hdmi params for phy configure\n");
> } else {
> hdptx->restrict_rate_change = true;
> - dev_dbg(hdptx->dev, "%s rate=%llu bpc=%u\n", __func__,
> + hdptx->pll_config_dirty = true;
> +
> + dev_dbg(hdptx->dev, "%s %s rate=%llu bpc=%u\n", __func__,
> + hdptx->hdmi_cfg.mode ? "FRL" : "TMDS",
> hdptx->hdmi_cfg.rate, hdptx->hdmi_cfg.bpc);
> }
>
> @@ -2323,8 +2335,19 @@ static int rk_hdptx_phy_clk_determine_rate(struct clk_hw *hw,
> {
> struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw);
>
> - if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
> - return hdptx->hdmi_cfg.rate;
> + /*
> + * Invalidate current clock rate to ensure rk_hdptx_phy_clk_set_rate()
> + * will be invoked to commit PLL configuration.
> + */
> + if (hdptx->pll_config_dirty) {
> + req->rate = 0;
> + return 0;
> + }
> +
> + if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL) {
> + req->rate = hdptx->hdmi_cfg.rate;
> + return 0;
> + }
>
> /*
> * FIXME: Temporarily allow altering TMDS char rate via CCF.
> @@ -2356,17 +2379,6 @@ static int rk_hdptx_phy_clk_set_rate(struct clk_hw *hw, unsigned long rate,
> unsigned long parent_rate)
> {
> struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw);
> - unsigned long long link_rate = rate;
> -
> - if (hdptx->hdmi_cfg.mode != PHY_HDMI_MODE_FRL)
> - link_rate = DIV_ROUND_CLOSEST_ULL(rate * hdptx->hdmi_cfg.bpc, 8);
> -
> - /* Revert any unlikely link rate change since determine_rate() */
> - if (hdptx->hdmi_cfg.rate != link_rate) {
> - dev_warn(hdptx->dev, "Reverting unexpected rate change from %llu to %llu\n",
> - link_rate, hdptx->hdmi_cfg.rate);
> - hdptx->hdmi_cfg.rate = link_rate;
> - }
>
> /*
> * The link rate would be normally programmed in HW during
>
More information about the Linux-rockchip
mailing list