Hi all, Dne sobota, 24. februar 2018 ob 22:45:41 CET je Jernej Skrabec napisal(a): > While A83T HDMI PHY seems to be just customized Synopsys HDMI PHY, H3 > HDMI PHY is completely custom PHY. > > However, they still have many things in common like clock and reset > setup, setting sync polarity and more. > > Add support for H3 HDMI PHY variant. > > While documentation exists for this PHY variant, it doesn't go in great > details. Because of that, almost all settings are copied from BSP linux > 4.4. Interestingly, those settings are slightly different to those found > in a older BSP with Linux 3.4. For now, no user visible difference was > found between them. > > Signed-off-by: Jernej Skrabec <jernej.skrabec@xxxxxxxx> > --- > drivers/gpu/drm/sun4i/Makefile | 1 + > drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h | 6 + > drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 263 > ++++++++++++++++++++++++++++- drivers/gpu/drm/sun4i/sun8i_hdmi_phy_clk.c | > 130 ++++++++++++++ > 4 files changed, 397 insertions(+), 3 deletions(-) > create mode 100644 drivers/gpu/drm/sun4i/sun8i_hdmi_phy_clk.c > [...] > diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy_clk.c > b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy_clk.c new file mode 100644 > index 000000000000..3c34ec5ff4af > --- /dev/null > +++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy_clk.c > @@ -0,0 +1,130 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright (C) 2018 Jernej Skrabec <jernej.skrabec@xxxxxxxx> > + */ > + > +#include <linux/clk-provider.h> > + > +#include "sun8i_dw_hdmi.h" > + > +struct sun8i_phy_clk { > + struct clk_hw hw; > + struct sun8i_hdmi_phy *phy; > +}; > + > +static inline struct sun8i_phy_clk *hw_to_phy_clk(struct clk_hw *hw) > +{ > + return container_of(hw, struct sun8i_phy_clk, hw); > +} > + > +static int sun8i_phy_clk_determine_rate(struct clk_hw *hw, > + struct clk_rate_request *req) > +{ > + unsigned long rate = req->rate; > + unsigned long best_rate = 0; > + struct clk_hw *parent; > + int best_div = 1; > + int i; > + > + parent = clk_hw_get_parent(hw); > + > + for (i = 1; i <= 16; i++) { > + unsigned long ideal = rate * i; > + unsigned long rounded; > + > + rounded = clk_hw_round_rate(parent, ideal); > + > + if (rounded == ideal) { > + best_rate = rounded; > + best_div = i; > + break; > + } > + > + if (abs(rate - rounded) < abs(rate - best_rate / best_div)) { Here is a bug. Above line should be: if (abs(rate - rounded / i) < abs(rate - best_rate / best_div)) { I guess this could solve the issue described in cover letter. Best regards, Jernej > + best_rate = rounded; > + best_div = i; > + } > + } > + > + req->rate = best_rate / best_div; > + req->best_parent_rate = best_rate; > + req->best_parent_hw = parent; > + > + return 0; > +} > + > +static unsigned long sun8i_phy_clk_recalc_rate(struct clk_hw *hw, > + unsigned long parent_rate) > +{ > + struct sun8i_phy_clk *priv = hw_to_phy_clk(hw); > + u32 reg; > + > + regmap_read(priv->phy->regs, SUN8I_HDMI_PHY_PLL_CFG2_REG, ®); > + reg = ((reg >> SUN8I_HDMI_PHY_PLL_CFG2_PREDIV_SHIFT) & > + SUN8I_HDMI_PHY_PLL_CFG2_PREDIV_MSK) + 1; > + > + return parent_rate / reg; > +} > + > +static int sun8i_phy_clk_set_rate(struct clk_hw *hw, unsigned long rate, > + unsigned long parent_rate) > +{ > + struct sun8i_phy_clk *priv = hw_to_phy_clk(hw); > + unsigned long best_rate = 0; > + u8 best_m = 0, m; > + > + for (m = 1; m <= 16; m++) { > + unsigned long tmp_rate = parent_rate / m; > + > + if (tmp_rate > rate) > + continue; > + > + if (!best_rate || > + (rate - tmp_rate) < (rate - best_rate)) { > + best_rate = tmp_rate; > + best_m = m; > + } > + } > + > + regmap_update_bits(priv->phy->regs, SUN8I_HDMI_PHY_PLL_CFG2_REG, > + SUN8I_HDMI_PHY_PLL_CFG2_PREDIV_MSK, > + SUN8I_HDMI_PHY_PLL_CFG2_PREDIV(best_m)); > + > + return 0; > +} > + > +static const struct clk_ops sun8i_phy_clk_ops = { > + .determine_rate = sun8i_phy_clk_determine_rate, > + .recalc_rate = sun8i_phy_clk_recalc_rate, > + .set_rate = sun8i_phy_clk_set_rate, > +}; > + > +int sun8i_phy_clk_create(struct sun8i_hdmi_phy *phy, struct device *dev) > +{ > + struct clk_init_data init; > + struct sun8i_phy_clk *priv; > + const char *parents[1]; > + > + parents[0] = __clk_get_name(phy->clk_pll0); > + if (!parents[0]) > + return -ENODEV; > + > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + init.name = "hdmi-phy-clk"; > + init.ops = &sun8i_phy_clk_ops; > + init.parent_names = parents; > + init.num_parents = 1; > + init.flags = CLK_SET_RATE_PARENT; > + > + priv->phy = phy; > + priv->hw.init = &init; > + > + phy->clk_phy = devm_clk_register(dev, &priv->hw); > + if (IS_ERR(phy->clk_phy)) > + return PTR_ERR(phy->clk_phy); > + > + return 0; > +} > -- > 2.16.2 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html