diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c index 80dd896861bc..2feb46f6d4e5 100644 --- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c +++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c @@ -402,6 +402,7 @@ struct rk_hdptx_phy { /* clk provider */ struct clk_hw hw; unsigned long rate; + bool restrict_rate_change; atomic_t usage_count; @@ -1758,10 +1759,12 @@ static int rk_hdptx_phy_configure(struct phy *phy, union phy_configure_opts *opt if (mode != PHY_MODE_DP) { ret = rk_hdptx_phy_verify_hdmi_config(hdptx, &opts->hdmi); - if (ret) + if (ret) { dev_err(hdptx->dev, "invalid hdmi params for phy configure\n"); - else + } else { hdptx->hdmi_cfg = opts->hdmi; + hdptx->restrict_rate_change = true; + } return ret; } @@ -1848,20 +1851,31 @@ static unsigned long rk_hdptx_phy_clk_recalc_rate(struct clk_hw *hw, static long rk_hdptx_phy_clk_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { - int i; + struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw); - if (rate > HDMI20_MAX_RATE) - return rate; + /* + * FIXME: Temporarily allow altering TMDS char rate via CCF. + * To be dropped as soon as the RK DW HDMI QP bridge driver + * switches to make use of phy_configure(). + */ + if (!hdptx->restrict_rate_change && rate != hdptx->hdmi_cfg.tmds_char_rate) { + struct phy_configure_opts_hdmi hdmi = { + .tmds_char_rate = rate, + }; + int ret = rk_hdptx_phy_verify_hdmi_config(hdptx, &hdmi); - for (i = 0; i < ARRAY_SIZE(ropll_tmds_cfg); i++) - if (rate == ropll_tmds_cfg[i].rate) - break; + if (ret) + return ret; - if (i == ARRAY_SIZE(ropll_tmds_cfg) && - !rk_hdptx_phy_clk_pll_calc(rate, NULL)) - return -EINVAL; + hdptx->hdmi_cfg = hdmi; + } - return rate; + /* + * The TMDS char rate shall be adjusted via phy_configure() only, + * hence ensure rk_hdptx_phy_clk_set_rate() won't be invoked with + * a different rate argument. + */ + return hdptx->hdmi_cfg.tmds_char_rate; } static int rk_hdptx_phy_clk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -1869,6 +1883,20 @@ static int rk_hdptx_phy_clk_set_rate(struct clk_hw *hw, unsigned long rate, { struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw); + /* Revert any unlikely TMDS char rate change since round_rate() */ + if (hdptx->hdmi_cfg.tmds_char_rate != rate) { + dev_warn(hdptx->dev, "Reverting unexpected rate change from %lu to %llu\n", + rate, hdptx->hdmi_cfg.tmds_char_rate); + hdptx->hdmi_cfg.tmds_char_rate = rate; + } + + /* + * The TMDS char rate would be normally programmed in HW during + * phy_ops.power_on() or clk_ops.prepare() callbacks, but it might + * happen that the former gets fired too late, i.e. after this call, + * while the latter being executed only once, i.e. when clock remains + * in the prepared state during rate changes. + */ return rk_hdptx_ropll_tmds_cmn_config(hdptx, rate); }