drivers/gpu/drm/sun4i/sun8i_hdmi_phy_clk.c

Source file repositories/reference/linux-study-clean/drivers/gpu/drm/sun4i/sun8i_hdmi_phy_clk.c

File Facts

System
Linux kernel
Corpus path
drivers/gpu/drm/sun4i/sun8i_hdmi_phy_clk.c
Extension
.c
Size
4012 bytes
Lines
179
Domain
Driver Families
Bucket
drivers/gpu
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

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 *best_parent = NULL;
	struct clk_hw *parent;
	int best_div = 1;
	int i, p;

	for (p = 0; p < clk_hw_get_num_parents(hw); p++) {
		parent = clk_hw_get_parent_by_index(hw, p);
		if (!parent)
			continue;

		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;
				best_parent = parent;
				break;
			}

			if (!best_rate ||
			    abs(rate - rounded / i) <
			    abs(rate - best_rate / best_div)) {
				best_rate = rounded;
				best_div = i;
				best_parent = parent;
			}
		}

		if (best_rate / best_div == rate)
			break;
	}

	req->rate = best_rate / best_div;
	req->best_parent_rate = best_rate;
	req->best_parent_hw = best_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 = ((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;
		}
	}

Annotation

Implementation Notes