drivers/gpu/drm/sun4i/sun4i_tcon_dclk.c

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

File Facts

System
Linux kernel
Corpus path
drivers/gpu/drm/sun4i/sun4i_tcon_dclk.c
Extension
.c
Size
4593 bytes
Lines
209
Domain
Driver Families
Bucket
drivers/gpu
Inferred role
Driver Families: exported/initcall integration point
Status
integration 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 sun4i_dclk {
	struct clk_hw		hw;
	struct regmap		*regmap;
	struct sun4i_tcon	*tcon;
};

static inline struct sun4i_dclk *hw_to_dclk(struct clk_hw *hw)
{
	return container_of(hw, struct sun4i_dclk, hw);
}

static void sun4i_dclk_disable(struct clk_hw *hw)
{
	struct sun4i_dclk *dclk = hw_to_dclk(hw);

	regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG,
			   BIT(SUN4I_TCON0_DCLK_GATE_BIT), 0);
}

static int sun4i_dclk_enable(struct clk_hw *hw)
{
	struct sun4i_dclk *dclk = hw_to_dclk(hw);

	return regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG,
				  BIT(SUN4I_TCON0_DCLK_GATE_BIT),
				  BIT(SUN4I_TCON0_DCLK_GATE_BIT));
}

static int sun4i_dclk_is_enabled(struct clk_hw *hw)
{
	struct sun4i_dclk *dclk = hw_to_dclk(hw);
	u32 val;

	regmap_read(dclk->regmap, SUN4I_TCON0_DCLK_REG, &val);

	return val & BIT(SUN4I_TCON0_DCLK_GATE_BIT);
}

static unsigned long sun4i_dclk_recalc_rate(struct clk_hw *hw,
					    unsigned long parent_rate)
{
	struct sun4i_dclk *dclk = hw_to_dclk(hw);
	u32 val;

	regmap_read(dclk->regmap, SUN4I_TCON0_DCLK_REG, &val);

	val >>= SUN4I_TCON0_DCLK_DIV_SHIFT;
	val &= (1 << SUN4I_TCON0_DCLK_DIV_WIDTH) - 1;

	if (!val)
		val = 1;

	return parent_rate / val;
}

static int sun4i_dclk_determine_rate(struct clk_hw *hw,
				     struct clk_rate_request *req)
{
	struct sun4i_dclk *dclk = hw_to_dclk(hw);
	struct sun4i_tcon *tcon = dclk->tcon;
	unsigned long best_parent = 0;
	u8 best_div = 1;
	int i;

	for (i = tcon->dclk_min_div; i <= tcon->dclk_max_div; i++) {
		u64 ideal = (u64)req->rate * i;
		unsigned long rounded;

		/*
		 * ideal has overflowed the max value that can be stored in an
		 * unsigned long, and every clk operation we might do on a
		 * truncated u64 value will give us incorrect results.
		 * Let's just stop there since bigger dividers will result in
		 * the same overflow issue.
		 */
		if (ideal > ULONG_MAX)
			goto out;

		rounded = clk_hw_round_rate(clk_hw_get_parent(hw),
					    ideal);

		if (rounded == ideal) {
			best_parent = rounded;
			best_div = i;
			goto out;
		}

		if (abs(req->rate - rounded / i) <
		    abs(req->rate - best_parent / best_div)) {
			best_parent = rounded;

Annotation

Implementation Notes