drivers/media/i2c/ccs-pll.c

Source file repositories/reference/linux-study-clean/drivers/media/i2c/ccs-pll.c

File Facts

System
Linux kernel
Corpus path
drivers/media/i2c/ccs-pll.c
Extension
.c
Size
27069 bytes
Lines
911
Domain
Driver Families
Bucket
drivers/media
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

if (pix_div * sys_div <= best_pix_div) {
			best_pix_div = pix_div;
			best_div = pix_div * sys_div;
		}
	}
	if (best_pix_div == SHRT_MAX >> 1)
		return -EINVAL;

	pll_bk->sys_clk_div = best_div / best_pix_div;
	pll_bk->pix_clk_div = best_pix_div;

	pll_bk->sys_clk_freq_hz =
		pll_fr->pll_op_clk_freq_hz / pll_bk->sys_clk_div;
	pll_bk->pix_clk_freq_hz =
		pll_bk->sys_clk_freq_hz / pll_bk->pix_clk_div;

	pll->pixel_rate_pixel_array =
		pll_bk->pix_clk_freq_hz * pll->vt_lanes;

	return 0;
}

static int ccs_pll_calculate_vt_tree(struct device *dev,
				     const struct ccs_pll_limits *lim,
				     struct ccs_pll *pll)
{
	const struct ccs_pll_branch_limits_fr *lim_fr = &lim->vt_fr;
	struct ccs_pll_branch_fr *pll_fr = &pll->vt_fr;
	u16 min_pre_pll_clk_div = lim_fr->min_pre_pll_clk_div;
	u16 max_pre_pll_clk_div = lim_fr->max_pre_pll_clk_div;
	u32 pre_mul, pre_div;

	pre_div = gcd(pll->pixel_rate_csi,
		      pll->ext_clk_freq_hz * pll->vt_lanes);
	pre_mul = pll->pixel_rate_csi / pre_div;
	pre_div = pll->ext_clk_freq_hz * pll->vt_lanes / pre_div;

	/* Make sure PLL input frequency is within limits */
	max_pre_pll_clk_div =
		min_t(u16, max_pre_pll_clk_div,
		      DIV_ROUND_UP(pll->ext_clk_freq_hz,
				   lim_fr->min_pll_ip_clk_freq_hz));

	min_pre_pll_clk_div = max_t(u16, min_pre_pll_clk_div,
				    pll->ext_clk_freq_hz /
				    lim_fr->max_pll_ip_clk_freq_hz);
	if (!(pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER))
		min_pre_pll_clk_div = clk_div_even(min_pre_pll_clk_div);

	dev_dbg(dev, "vt min/max_pre_pll_clk_div: %u,%u\n",
		min_pre_pll_clk_div, max_pre_pll_clk_div);

	for (pll_fr->pre_pll_clk_div = min_pre_pll_clk_div;
	     pll_fr->pre_pll_clk_div <= max_pre_pll_clk_div;
	     pll_fr->pre_pll_clk_div +=
		     (pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER) ? 1 :
		     2 - (pll_fr->pre_pll_clk_div & 1)) {
		u32 mul, div;
		int rval;

		div = gcd(pre_mul * pll_fr->pre_pll_clk_div, pre_div);
		mul = pre_mul * pll_fr->pre_pll_clk_div / div;
		div = pre_div / div;

		dev_dbg(dev, "vt pre-div/mul/div: %u,%u,%u\n",
			pll_fr->pre_pll_clk_div, mul, div);

		rval = __ccs_pll_calculate_vt_tree(dev, lim, pll,
						   mul, div);
		if (rval)
			continue;

		rval = check_fr_bounds(dev, lim, pll, PLL_VT);
		if (rval)
			continue;

		rval = check_bk_bounds(dev, lim, pll, PLL_VT);
		if (rval)
			continue;

		return 0;
	}

	dev_dbg(dev, "unable to compute VT pre_pll divisor\n");
	return -EINVAL;
}

static int
ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim,
		     const struct ccs_pll_branch_limits_bk *op_lim_bk,

Annotation

Implementation Notes