Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6de1b143 authored by Sakari Ailus's avatar Sakari Ailus Committed by Mauro Carvalho Chehab
Browse files

[media] smiapp-pll: Try other pre-pll divisors



The smiapp pll calculator assumed that the minimum pre-pll divisor was
perfect. That may not always be the case, so let's try the others, too.
Typically there are just a few alternatives.

Signed-off-by: default avatarSakari Ailus <sakari.ailus@iki.fi>
Acked-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent c2ebca00
Loading
Loading
Loading
Loading
+73 −58
Original line number Diff line number Diff line
@@ -58,7 +58,7 @@ static int bounds_check(struct device *dev, uint32_t val,
	if (val >= min && val <= max)
		return 0;

	dev_warn(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max);
	dev_dbg(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max);

	return -EINVAL;
}
@@ -87,14 +87,14 @@ static void print_pll(struct device *dev, struct smiapp_pll *pll)
	dev_dbg(dev, "vt_pix_clk_freq_hz \t%d\n", pll->vt_pix_clk_freq_hz);
}

int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
			 struct smiapp_pll *pll)
static int __smiapp_pll_calculate(struct device *dev,
				  struct smiapp_pll_limits *limits,
				  struct smiapp_pll *pll, uint32_t mul,
				  uint32_t div, uint32_t lane_op_clock_ratio)
{
	uint32_t sys_div;
	uint32_t best_pix_div = INT_MAX >> 1;
	uint32_t vt_op_binning_div;
	uint32_t lane_op_clock_ratio;
	uint32_t mul, div;
	uint32_t more_mul_min, more_mul_max;
	uint32_t more_mul_factor;
	uint32_t min_vt_div, max_vt_div, vt_div;
@@ -102,54 +102,6 @@ int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
	unsigned int i;
	int rval;

	if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE)
		lane_op_clock_ratio = pll->lanes;
	else
		lane_op_clock_ratio = 1;
	dev_dbg(dev, "lane_op_clock_ratio: %d\n", lane_op_clock_ratio);

	dev_dbg(dev, "binning: %dx%d\n", pll->binning_horizontal,
		pll->binning_vertical);

	/* CSI transfers 2 bits per clock per lane; thus times 2 */
	pll->pll_op_clk_freq_hz = pll->link_freq * 2
		* (pll->lanes / lane_op_clock_ratio);

	/* Figure out limits for pre-pll divider based on extclk */
	dev_dbg(dev, "min / max pre_pll_clk_div: %d / %d\n",
		limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div);
	limits->max_pre_pll_clk_div =
		min_t(uint16_t, limits->max_pre_pll_clk_div,
		      clk_div_even(pll->ext_clk_freq_hz /
				   limits->min_pll_ip_freq_hz));
	limits->min_pre_pll_clk_div =
		max_t(uint16_t, limits->min_pre_pll_clk_div,
		      clk_div_even_up(
			      DIV_ROUND_UP(pll->ext_clk_freq_hz,
					   limits->max_pll_ip_freq_hz)));
	dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %d / %d\n",
		limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div);

	i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz);
	mul = div_u64(pll->pll_op_clk_freq_hz, i);
	div = pll->ext_clk_freq_hz / i;
	dev_dbg(dev, "mul %d / div %d\n", mul, div);

	limits->min_pre_pll_clk_div =
		max_t(uint16_t, limits->min_pre_pll_clk_div,
		      clk_div_even_up(
			      DIV_ROUND_UP(mul * pll->ext_clk_freq_hz,
					   limits->max_pll_op_freq_hz)));
	dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %d / %d\n",
		limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div);

	if (limits->min_pre_pll_clk_div > limits->max_pre_pll_clk_div) {
		dev_err(dev, "unable to compute pre_pll divisor\n");
		return -EINVAL;
	}

	pll->pre_pll_clk_div = limits->min_pre_pll_clk_div;

	/*
	 * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be
	 * too high.
@@ -193,7 +145,7 @@ int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
		more_mul_min);

	if (more_mul_min > more_mul_max) {
		dev_warn(dev,
		dev_dbg(dev,
			"unable to compute more_mul_min and more_mul_max\n");
		return -EINVAL;
	}
@@ -209,7 +161,7 @@ int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,

	dev_dbg(dev, "final more_mul: %d\n", i);
	if (i > more_mul_max) {
		dev_warn(dev, "final more_mul is bad, max %d\n", more_mul_max);
		dev_dbg(dev, "final more_mul is bad, max %d\n", more_mul_max);
		return -EINVAL;
	}

@@ -354,8 +306,6 @@ int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
	pll->pixel_rate_csi =
		pll->op_pix_clk_freq_hz * lane_op_clock_ratio;

	print_pll(dev, pll);

	rval = bounds_check(dev, pll->pre_pll_clk_div,
			    limits->min_pre_pll_clk_div,
			    limits->max_pre_pll_clk_div, "pre_pll_clk_div");
@@ -411,6 +361,71 @@ int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,

	return rval;
}

int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
			 struct smiapp_pll *pll)
{
	uint32_t lane_op_clock_ratio;
	uint32_t mul, div;
	unsigned int i;
	int rval = -EINVAL;

	if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE)
		lane_op_clock_ratio = pll->lanes;
	else
		lane_op_clock_ratio = 1;
	dev_dbg(dev, "lane_op_clock_ratio: %d\n", lane_op_clock_ratio);

	dev_dbg(dev, "binning: %dx%d\n", pll->binning_horizontal,
		pll->binning_vertical);

	/* CSI transfers 2 bits per clock per lane; thus times 2 */
	pll->pll_op_clk_freq_hz = pll->link_freq * 2
		* (pll->lanes / lane_op_clock_ratio);

	/* Figure out limits for pre-pll divider based on extclk */
	dev_dbg(dev, "min / max pre_pll_clk_div: %d / %d\n",
		limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div);
	limits->max_pre_pll_clk_div =
		min_t(uint16_t, limits->max_pre_pll_clk_div,
		      clk_div_even(pll->ext_clk_freq_hz /
				   limits->min_pll_ip_freq_hz));
	limits->min_pre_pll_clk_div =
		max_t(uint16_t, limits->min_pre_pll_clk_div,
		      clk_div_even_up(
			      DIV_ROUND_UP(pll->ext_clk_freq_hz,
					   limits->max_pll_ip_freq_hz)));
	dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %d / %d\n",
		limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div);

	i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz);
	mul = div_u64(pll->pll_op_clk_freq_hz, i);
	div = pll->ext_clk_freq_hz / i;
	dev_dbg(dev, "mul %d / div %d\n", mul, div);

	limits->min_pre_pll_clk_div =
		max_t(uint16_t, limits->min_pre_pll_clk_div,
		      clk_div_even_up(
			      DIV_ROUND_UP(mul * pll->ext_clk_freq_hz,
					   limits->max_pll_op_freq_hz)));
	dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %d / %d\n",
		limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div);

	for (pll->pre_pll_clk_div = limits->min_pre_pll_clk_div;
	     pll->pre_pll_clk_div <= limits->max_pre_pll_clk_div;
	     pll->pre_pll_clk_div += 2 - (pll->pre_pll_clk_div & 1)) {
		rval = __smiapp_pll_calculate(dev, limits, pll, mul, div,
					      lane_op_clock_ratio);
		if (rval)
			continue;

		print_pll(dev, pll);
		return 0;
	}

	dev_info(dev, "unable to compute pre_pll divisor\n");
	return rval;
}
EXPORT_SYMBOL_GPL(smiapp_pll_calculate);

MODULE_AUTHOR("Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>");