@@ -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;
@@ -1759,10 +1760,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;
}
@@ -1849,21 +1852,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)
{
- u32 bit_rate = rate / 100;
- 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 (bit_rate == ropll_tmds_cfg[i].bit_rate)
- break;
+ if (ret)
+ return ret;
- if (i == ARRAY_SIZE(ropll_tmds_cfg) &&
- !rk_hdptx_phy_clk_pll_calc(bit_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,
@@ -1871,6 +1884,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 / 100);
}
Although, in theory, the clock provider functionality could be enabled as a standalone driver feature, in practice it is unlikely that it would be ever needed separately from the common PHY related features, i.e. making use of the PHY PLL as an alternative and more accurate clock source for display modes handling. Which means the PLL will be always programmed according to the TMDS char rate set via the HDMI PHY configuration API. Currently it's possible to freely adjust the rate via the clock API as well, that is through clk_set_rate(). Making the clock read-only is not feasible since we need to ensure any rate update done via the PHY configuration API has been actually programmed into the hardware before CCF accesses it. This would be normally done during phy_ops.power_on() or clk_ops.prepare() callbacks, but it might happen that the former gets fired too late and the latter only once, hence we need to keep handle it via clk_ops.set_rate() as a fallback approach. Prevent changing the TMDS character rate via CCF by letting rk_hdptx_phy_clk_round_rate() always return the value set via phy_configure(). To avoid breaking existing users, i.e. RK DW HDMI QP bridge driver, until the switch to the HDMI PHY config based approach is completed, introduce a temporary exception to the rule, toggled via the new ->restrict_rate_change flag, which indicates whether phy_configure() has been called or not. Additionally, revert any unlikely rate change that might have occurred between the calls to ->round_rate() and ->set_rate(). Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com> --- drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c | 53 +++++++++++++++++------ 1 file changed, 40 insertions(+), 13 deletions(-)