drivers/gpu/drm/mcde/mcde_clk_div.c

Source file repositories/reference/linux-study-clean/drivers/gpu/drm/mcde/mcde_clk_div.c

File Facts

System
Linux kernel
Corpus path
drivers/gpu/drm/mcde/mcde_clk_div.c
Extension
.c
Size
4742 bytes
Lines
197
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 mcde_clk_div {
	struct clk_hw hw;
	struct mcde *mcde;
	u32 cr;
	u32 cr_div;
};

static int mcde_clk_div_enable(struct clk_hw *hw)
{
	struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
	struct mcde *mcde = cdiv->mcde;
	u32 val;

	spin_lock(&mcde->fifo_crx1_lock);
	val = readl(mcde->regs + cdiv->cr);
	/*
	 * Select the PLL72 (LCD) clock as parent
	 * FIXME: implement other parents.
	 */
	val &= ~MCDE_CRX1_CLKSEL_MASK;
	val |= MCDE_CRX1_CLKSEL_CLKPLL72 << MCDE_CRX1_CLKSEL_SHIFT;
	/* Internal clock */
	val |= MCDE_CRA1_CLKTYPE_TVXCLKSEL1;

	/* Clear then set the divider */
	val &= ~(MCDE_CRX1_BCD | MCDE_CRX1_PCD_MASK);
	val |= cdiv->cr_div;

	writel(val, mcde->regs + cdiv->cr);
	spin_unlock(&mcde->fifo_crx1_lock);

	return 0;
}

static int mcde_clk_div_choose_div(struct clk_hw *hw, unsigned long rate,
				   unsigned long *prate, bool set_parent)
{
	int best_div = 1, div;
	struct clk_hw *parent = clk_hw_get_parent(hw);
	unsigned long best_prate = 0;
	unsigned long best_diff = ~0ul;
	int max_div = (1 << MCDE_CRX1_PCD_BITS) - 1;

	for (div = 1; div < max_div; div++) {
		unsigned long this_prate, div_rate, diff;

		if (set_parent)
			this_prate = clk_hw_round_rate(parent, rate * div);
		else
			this_prate = *prate;
		div_rate = DIV_ROUND_UP_ULL(this_prate, div);
		diff = abs(rate - div_rate);

		if (diff < best_diff) {
			best_div = div;
			best_diff = diff;
			best_prate = this_prate;
		}
	}

	*prate = best_prate;
	return best_div;
}

static int mcde_clk_div_determine_rate(struct clk_hw *hw,
				       struct clk_rate_request *req)
{
	int div = mcde_clk_div_choose_div(hw, req->rate,
					  &req->best_parent_rate, true);

	req->rate = DIV_ROUND_UP_ULL(req->best_parent_rate, div);

	return 0;
}

static unsigned long mcde_clk_div_recalc_rate(struct clk_hw *hw,
					       unsigned long prate)
{
	struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
	struct mcde *mcde = cdiv->mcde;
	u32 cr;
	int div;

	/*
	 * If the MCDE is not powered we can't access registers.
	 * It will come up with 0 in the divider register bits, which
	 * means "divide by 2".
	 */
	if (!regulator_is_enabled(mcde->epod))
		return DIV_ROUND_UP_ULL(prate, 2);

Annotation

Implementation Notes