18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * drivers/media/i2c/smiapp-pll.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Generic driver for SMIA/SMIA++ compliant camera modules
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2011--2012 Nokia Corporation
88c2ecf20Sopenharmony_ci * Contact: Sakari Ailus <sakari.ailus@iki.fi>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/device.h>
128c2ecf20Sopenharmony_ci#include <linux/gcd.h>
138c2ecf20Sopenharmony_ci#include <linux/lcm.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "smiapp-pll.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/* Return an even number or one. */
198c2ecf20Sopenharmony_cistatic inline uint32_t clk_div_even(uint32_t a)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	return max_t(uint32_t, 1, a & ~1);
228c2ecf20Sopenharmony_ci}
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/* Return an even number or one. */
258c2ecf20Sopenharmony_cistatic inline uint32_t clk_div_even_up(uint32_t a)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	if (a == 1)
288c2ecf20Sopenharmony_ci		return 1;
298c2ecf20Sopenharmony_ci	return (a + 1) & ~1;
308c2ecf20Sopenharmony_ci}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic inline uint32_t is_one_or_even(uint32_t a)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	if (a == 1)
358c2ecf20Sopenharmony_ci		return 1;
368c2ecf20Sopenharmony_ci	if (a & 1)
378c2ecf20Sopenharmony_ci		return 0;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	return 1;
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic int bounds_check(struct device *dev, uint32_t val,
438c2ecf20Sopenharmony_ci			uint32_t min, uint32_t max, char *str)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	if (val >= min && val <= max)
468c2ecf20Sopenharmony_ci		return 0;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	return -EINVAL;
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic void print_pll(struct device *dev, struct smiapp_pll *pll)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	dev_dbg(dev, "pre_pll_clk_div\t%u\n",  pll->pre_pll_clk_div);
568c2ecf20Sopenharmony_ci	dev_dbg(dev, "pll_multiplier \t%u\n",  pll->pll_multiplier);
578c2ecf20Sopenharmony_ci	if (!(pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS)) {
588c2ecf20Sopenharmony_ci		dev_dbg(dev, "op_sys_clk_div \t%u\n", pll->op.sys_clk_div);
598c2ecf20Sopenharmony_ci		dev_dbg(dev, "op_pix_clk_div \t%u\n", pll->op.pix_clk_div);
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci	dev_dbg(dev, "vt_sys_clk_div \t%u\n",  pll->vt.sys_clk_div);
628c2ecf20Sopenharmony_ci	dev_dbg(dev, "vt_pix_clk_div \t%u\n",  pll->vt.pix_clk_div);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	dev_dbg(dev, "ext_clk_freq_hz \t%u\n", pll->ext_clk_freq_hz);
658c2ecf20Sopenharmony_ci	dev_dbg(dev, "pll_ip_clk_freq_hz \t%u\n", pll->pll_ip_clk_freq_hz);
668c2ecf20Sopenharmony_ci	dev_dbg(dev, "pll_op_clk_freq_hz \t%u\n", pll->pll_op_clk_freq_hz);
678c2ecf20Sopenharmony_ci	if (!(pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS)) {
688c2ecf20Sopenharmony_ci		dev_dbg(dev, "op_sys_clk_freq_hz \t%u\n",
698c2ecf20Sopenharmony_ci			pll->op.sys_clk_freq_hz);
708c2ecf20Sopenharmony_ci		dev_dbg(dev, "op_pix_clk_freq_hz \t%u\n",
718c2ecf20Sopenharmony_ci			pll->op.pix_clk_freq_hz);
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci	dev_dbg(dev, "vt_sys_clk_freq_hz \t%u\n", pll->vt.sys_clk_freq_hz);
748c2ecf20Sopenharmony_ci	dev_dbg(dev, "vt_pix_clk_freq_hz \t%u\n", pll->vt.pix_clk_freq_hz);
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int check_all_bounds(struct device *dev,
788c2ecf20Sopenharmony_ci			    const struct smiapp_pll_limits *limits,
798c2ecf20Sopenharmony_ci			    const struct smiapp_pll_branch_limits *op_limits,
808c2ecf20Sopenharmony_ci			    struct smiapp_pll *pll,
818c2ecf20Sopenharmony_ci			    struct smiapp_pll_branch *op_pll)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	int rval;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	rval = bounds_check(dev, pll->pll_ip_clk_freq_hz,
868c2ecf20Sopenharmony_ci			    limits->min_pll_ip_freq_hz,
878c2ecf20Sopenharmony_ci			    limits->max_pll_ip_freq_hz,
888c2ecf20Sopenharmony_ci			    "pll_ip_clk_freq_hz");
898c2ecf20Sopenharmony_ci	if (!rval)
908c2ecf20Sopenharmony_ci		rval = bounds_check(
918c2ecf20Sopenharmony_ci			dev, pll->pll_multiplier,
928c2ecf20Sopenharmony_ci			limits->min_pll_multiplier, limits->max_pll_multiplier,
938c2ecf20Sopenharmony_ci			"pll_multiplier");
948c2ecf20Sopenharmony_ci	if (!rval)
958c2ecf20Sopenharmony_ci		rval = bounds_check(
968c2ecf20Sopenharmony_ci			dev, pll->pll_op_clk_freq_hz,
978c2ecf20Sopenharmony_ci			limits->min_pll_op_freq_hz, limits->max_pll_op_freq_hz,
988c2ecf20Sopenharmony_ci			"pll_op_clk_freq_hz");
998c2ecf20Sopenharmony_ci	if (!rval)
1008c2ecf20Sopenharmony_ci		rval = bounds_check(
1018c2ecf20Sopenharmony_ci			dev, op_pll->sys_clk_div,
1028c2ecf20Sopenharmony_ci			op_limits->min_sys_clk_div, op_limits->max_sys_clk_div,
1038c2ecf20Sopenharmony_ci			"op_sys_clk_div");
1048c2ecf20Sopenharmony_ci	if (!rval)
1058c2ecf20Sopenharmony_ci		rval = bounds_check(
1068c2ecf20Sopenharmony_ci			dev, op_pll->sys_clk_freq_hz,
1078c2ecf20Sopenharmony_ci			op_limits->min_sys_clk_freq_hz,
1088c2ecf20Sopenharmony_ci			op_limits->max_sys_clk_freq_hz,
1098c2ecf20Sopenharmony_ci			"op_sys_clk_freq_hz");
1108c2ecf20Sopenharmony_ci	if (!rval)
1118c2ecf20Sopenharmony_ci		rval = bounds_check(
1128c2ecf20Sopenharmony_ci			dev, op_pll->pix_clk_freq_hz,
1138c2ecf20Sopenharmony_ci			op_limits->min_pix_clk_freq_hz,
1148c2ecf20Sopenharmony_ci			op_limits->max_pix_clk_freq_hz,
1158c2ecf20Sopenharmony_ci			"op_pix_clk_freq_hz");
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	/*
1188c2ecf20Sopenharmony_ci	 * If there are no OP clocks, the VT clocks are contained in
1198c2ecf20Sopenharmony_ci	 * the OP clock struct.
1208c2ecf20Sopenharmony_ci	 */
1218c2ecf20Sopenharmony_ci	if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS)
1228c2ecf20Sopenharmony_ci		return rval;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (!rval)
1258c2ecf20Sopenharmony_ci		rval = bounds_check(
1268c2ecf20Sopenharmony_ci			dev, pll->vt.sys_clk_freq_hz,
1278c2ecf20Sopenharmony_ci			limits->vt.min_sys_clk_freq_hz,
1288c2ecf20Sopenharmony_ci			limits->vt.max_sys_clk_freq_hz,
1298c2ecf20Sopenharmony_ci			"vt_sys_clk_freq_hz");
1308c2ecf20Sopenharmony_ci	if (!rval)
1318c2ecf20Sopenharmony_ci		rval = bounds_check(
1328c2ecf20Sopenharmony_ci			dev, pll->vt.pix_clk_freq_hz,
1338c2ecf20Sopenharmony_ci			limits->vt.min_pix_clk_freq_hz,
1348c2ecf20Sopenharmony_ci			limits->vt.max_pix_clk_freq_hz,
1358c2ecf20Sopenharmony_ci			"vt_pix_clk_freq_hz");
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	return rval;
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci/*
1418c2ecf20Sopenharmony_ci * Heuristically guess the PLL tree for a given common multiplier and
1428c2ecf20Sopenharmony_ci * divisor. Begin with the operational timing and continue to video
1438c2ecf20Sopenharmony_ci * timing once operational timing has been verified.
1448c2ecf20Sopenharmony_ci *
1458c2ecf20Sopenharmony_ci * @mul is the PLL multiplier and @div is the common divisor
1468c2ecf20Sopenharmony_ci * (pre_pll_clk_div and op_sys_clk_div combined). The final PLL
1478c2ecf20Sopenharmony_ci * multiplier will be a multiple of @mul.
1488c2ecf20Sopenharmony_ci *
1498c2ecf20Sopenharmony_ci * @return Zero on success, error code on error.
1508c2ecf20Sopenharmony_ci */
1518c2ecf20Sopenharmony_cistatic int __smiapp_pll_calculate(
1528c2ecf20Sopenharmony_ci	struct device *dev, const struct smiapp_pll_limits *limits,
1538c2ecf20Sopenharmony_ci	const struct smiapp_pll_branch_limits *op_limits,
1548c2ecf20Sopenharmony_ci	struct smiapp_pll *pll, struct smiapp_pll_branch *op_pll, uint32_t mul,
1558c2ecf20Sopenharmony_ci	uint32_t div, uint32_t lane_op_clock_ratio)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	uint32_t sys_div;
1588c2ecf20Sopenharmony_ci	uint32_t best_pix_div = INT_MAX >> 1;
1598c2ecf20Sopenharmony_ci	uint32_t vt_op_binning_div;
1608c2ecf20Sopenharmony_ci	/*
1618c2ecf20Sopenharmony_ci	 * Higher multipliers (and divisors) are often required than
1628c2ecf20Sopenharmony_ci	 * necessitated by the external clock and the output clocks.
1638c2ecf20Sopenharmony_ci	 * There are limits for all values in the clock tree. These
1648c2ecf20Sopenharmony_ci	 * are the minimum and maximum multiplier for mul.
1658c2ecf20Sopenharmony_ci	 */
1668c2ecf20Sopenharmony_ci	uint32_t more_mul_min, more_mul_max;
1678c2ecf20Sopenharmony_ci	uint32_t more_mul_factor;
1688c2ecf20Sopenharmony_ci	uint32_t min_vt_div, max_vt_div, vt_div;
1698c2ecf20Sopenharmony_ci	uint32_t min_sys_div, max_sys_div;
1708c2ecf20Sopenharmony_ci	unsigned int i;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	/*
1738c2ecf20Sopenharmony_ci	 * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be
1748c2ecf20Sopenharmony_ci	 * too high.
1758c2ecf20Sopenharmony_ci	 */
1768c2ecf20Sopenharmony_ci	dev_dbg(dev, "pre_pll_clk_div %u\n", pll->pre_pll_clk_div);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	/* Don't go above max pll multiplier. */
1798c2ecf20Sopenharmony_ci	more_mul_max = limits->max_pll_multiplier / mul;
1808c2ecf20Sopenharmony_ci	dev_dbg(dev, "more_mul_max: max_pll_multiplier check: %u\n",
1818c2ecf20Sopenharmony_ci		more_mul_max);
1828c2ecf20Sopenharmony_ci	/* Don't go above max pll op frequency. */
1838c2ecf20Sopenharmony_ci	more_mul_max =
1848c2ecf20Sopenharmony_ci		min_t(uint32_t,
1858c2ecf20Sopenharmony_ci		      more_mul_max,
1868c2ecf20Sopenharmony_ci		      limits->max_pll_op_freq_hz
1878c2ecf20Sopenharmony_ci		      / (pll->ext_clk_freq_hz / pll->pre_pll_clk_div * mul));
1888c2ecf20Sopenharmony_ci	dev_dbg(dev, "more_mul_max: max_pll_op_freq_hz check: %u\n",
1898c2ecf20Sopenharmony_ci		more_mul_max);
1908c2ecf20Sopenharmony_ci	/* Don't go above the division capability of op sys clock divider. */
1918c2ecf20Sopenharmony_ci	more_mul_max = min(more_mul_max,
1928c2ecf20Sopenharmony_ci			   op_limits->max_sys_clk_div * pll->pre_pll_clk_div
1938c2ecf20Sopenharmony_ci			   / div);
1948c2ecf20Sopenharmony_ci	dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %u\n",
1958c2ecf20Sopenharmony_ci		more_mul_max);
1968c2ecf20Sopenharmony_ci	/* Ensure we won't go above min_pll_multiplier. */
1978c2ecf20Sopenharmony_ci	more_mul_max = min(more_mul_max,
1988c2ecf20Sopenharmony_ci			   DIV_ROUND_UP(limits->max_pll_multiplier, mul));
1998c2ecf20Sopenharmony_ci	dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %u\n",
2008c2ecf20Sopenharmony_ci		more_mul_max);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	/* Ensure we won't go below min_pll_op_freq_hz. */
2038c2ecf20Sopenharmony_ci	more_mul_min = DIV_ROUND_UP(limits->min_pll_op_freq_hz,
2048c2ecf20Sopenharmony_ci				    pll->ext_clk_freq_hz / pll->pre_pll_clk_div
2058c2ecf20Sopenharmony_ci				    * mul);
2068c2ecf20Sopenharmony_ci	dev_dbg(dev, "more_mul_min: min_pll_op_freq_hz check: %u\n",
2078c2ecf20Sopenharmony_ci		more_mul_min);
2088c2ecf20Sopenharmony_ci	/* Ensure we won't go below min_pll_multiplier. */
2098c2ecf20Sopenharmony_ci	more_mul_min = max(more_mul_min,
2108c2ecf20Sopenharmony_ci			   DIV_ROUND_UP(limits->min_pll_multiplier, mul));
2118c2ecf20Sopenharmony_ci	dev_dbg(dev, "more_mul_min: min_pll_multiplier check: %u\n",
2128c2ecf20Sopenharmony_ci		more_mul_min);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if (more_mul_min > more_mul_max) {
2158c2ecf20Sopenharmony_ci		dev_dbg(dev,
2168c2ecf20Sopenharmony_ci			"unable to compute more_mul_min and more_mul_max\n");
2178c2ecf20Sopenharmony_ci		return -EINVAL;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div;
2218c2ecf20Sopenharmony_ci	dev_dbg(dev, "more_mul_factor: %u\n", more_mul_factor);
2228c2ecf20Sopenharmony_ci	more_mul_factor = lcm(more_mul_factor, op_limits->min_sys_clk_div);
2238c2ecf20Sopenharmony_ci	dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n",
2248c2ecf20Sopenharmony_ci		more_mul_factor);
2258c2ecf20Sopenharmony_ci	i = roundup(more_mul_min, more_mul_factor);
2268c2ecf20Sopenharmony_ci	if (!is_one_or_even(i))
2278c2ecf20Sopenharmony_ci		i <<= 1;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	dev_dbg(dev, "final more_mul: %u\n", i);
2308c2ecf20Sopenharmony_ci	if (i > more_mul_max) {
2318c2ecf20Sopenharmony_ci		dev_dbg(dev, "final more_mul is bad, max %u\n", more_mul_max);
2328c2ecf20Sopenharmony_ci		return -EINVAL;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	pll->pll_multiplier = mul * i;
2368c2ecf20Sopenharmony_ci	op_pll->sys_clk_div = div * i / pll->pre_pll_clk_div;
2378c2ecf20Sopenharmony_ci	dev_dbg(dev, "op_sys_clk_div: %u\n", op_pll->sys_clk_div);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	pll->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz
2408c2ecf20Sopenharmony_ci		/ pll->pre_pll_clk_div;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	pll->pll_op_clk_freq_hz = pll->pll_ip_clk_freq_hz
2438c2ecf20Sopenharmony_ci		* pll->pll_multiplier;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	/* Derive pll_op_clk_freq_hz. */
2468c2ecf20Sopenharmony_ci	op_pll->sys_clk_freq_hz =
2478c2ecf20Sopenharmony_ci		pll->pll_op_clk_freq_hz / op_pll->sys_clk_div;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	op_pll->pix_clk_div = pll->bits_per_pixel;
2508c2ecf20Sopenharmony_ci	dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll->pix_clk_div);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	op_pll->pix_clk_freq_hz =
2538c2ecf20Sopenharmony_ci		op_pll->sys_clk_freq_hz / op_pll->pix_clk_div;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) {
2568c2ecf20Sopenharmony_ci		/* No OP clocks --- VT clocks are used instead. */
2578c2ecf20Sopenharmony_ci		goto out_skip_vt_calc;
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	/*
2618c2ecf20Sopenharmony_ci	 * Some sensors perform analogue binning and some do this
2628c2ecf20Sopenharmony_ci	 * digitally. The ones doing this digitally can be roughly be
2638c2ecf20Sopenharmony_ci	 * found out using this formula. The ones doing this digitally
2648c2ecf20Sopenharmony_ci	 * should run at higher clock rate, so smaller divisor is used
2658c2ecf20Sopenharmony_ci	 * on video timing side.
2668c2ecf20Sopenharmony_ci	 */
2678c2ecf20Sopenharmony_ci	if (limits->min_line_length_pck_bin > limits->min_line_length_pck
2688c2ecf20Sopenharmony_ci	    / pll->binning_horizontal)
2698c2ecf20Sopenharmony_ci		vt_op_binning_div = pll->binning_horizontal;
2708c2ecf20Sopenharmony_ci	else
2718c2ecf20Sopenharmony_ci		vt_op_binning_div = 1;
2728c2ecf20Sopenharmony_ci	dev_dbg(dev, "vt_op_binning_div: %u\n", vt_op_binning_div);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	/*
2758c2ecf20Sopenharmony_ci	 * Profile 2 supports vt_pix_clk_div E [4, 10]
2768c2ecf20Sopenharmony_ci	 *
2778c2ecf20Sopenharmony_ci	 * Horizontal binning can be used as a base for difference in
2788c2ecf20Sopenharmony_ci	 * divisors. One must make sure that horizontal blanking is
2798c2ecf20Sopenharmony_ci	 * enough to accommodate the CSI-2 sync codes.
2808c2ecf20Sopenharmony_ci	 *
2818c2ecf20Sopenharmony_ci	 * Take scaling factor into account as well.
2828c2ecf20Sopenharmony_ci	 *
2838c2ecf20Sopenharmony_ci	 * Find absolute limits for the factor of vt divider.
2848c2ecf20Sopenharmony_ci	 */
2858c2ecf20Sopenharmony_ci	dev_dbg(dev, "scale_m: %u\n", pll->scale_m);
2868c2ecf20Sopenharmony_ci	min_vt_div = DIV_ROUND_UP(op_pll->pix_clk_div * op_pll->sys_clk_div
2878c2ecf20Sopenharmony_ci				  * pll->scale_n,
2888c2ecf20Sopenharmony_ci				  lane_op_clock_ratio * vt_op_binning_div
2898c2ecf20Sopenharmony_ci				  * pll->scale_m);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	/* Find smallest and biggest allowed vt divisor. */
2928c2ecf20Sopenharmony_ci	dev_dbg(dev, "min_vt_div: %u\n", min_vt_div);
2938c2ecf20Sopenharmony_ci	min_vt_div = max(min_vt_div,
2948c2ecf20Sopenharmony_ci			 DIV_ROUND_UP(pll->pll_op_clk_freq_hz,
2958c2ecf20Sopenharmony_ci				      limits->vt.max_pix_clk_freq_hz));
2968c2ecf20Sopenharmony_ci	dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %u\n",
2978c2ecf20Sopenharmony_ci		min_vt_div);
2988c2ecf20Sopenharmony_ci	min_vt_div = max_t(uint32_t, min_vt_div,
2998c2ecf20Sopenharmony_ci			   limits->vt.min_pix_clk_div
3008c2ecf20Sopenharmony_ci			   * limits->vt.min_sys_clk_div);
3018c2ecf20Sopenharmony_ci	dev_dbg(dev, "min_vt_div: min_vt_clk_div: %u\n", min_vt_div);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	max_vt_div = limits->vt.max_sys_clk_div * limits->vt.max_pix_clk_div;
3048c2ecf20Sopenharmony_ci	dev_dbg(dev, "max_vt_div: %u\n", max_vt_div);
3058c2ecf20Sopenharmony_ci	max_vt_div = min(max_vt_div,
3068c2ecf20Sopenharmony_ci			 DIV_ROUND_UP(pll->pll_op_clk_freq_hz,
3078c2ecf20Sopenharmony_ci				      limits->vt.min_pix_clk_freq_hz));
3088c2ecf20Sopenharmony_ci	dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %u\n",
3098c2ecf20Sopenharmony_ci		max_vt_div);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	/*
3128c2ecf20Sopenharmony_ci	 * Find limitsits for sys_clk_div. Not all values are possible
3138c2ecf20Sopenharmony_ci	 * with all values of pix_clk_div.
3148c2ecf20Sopenharmony_ci	 */
3158c2ecf20Sopenharmony_ci	min_sys_div = limits->vt.min_sys_clk_div;
3168c2ecf20Sopenharmony_ci	dev_dbg(dev, "min_sys_div: %u\n", min_sys_div);
3178c2ecf20Sopenharmony_ci	min_sys_div = max(min_sys_div,
3188c2ecf20Sopenharmony_ci			  DIV_ROUND_UP(min_vt_div,
3198c2ecf20Sopenharmony_ci				       limits->vt.max_pix_clk_div));
3208c2ecf20Sopenharmony_ci	dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %u\n", min_sys_div);
3218c2ecf20Sopenharmony_ci	min_sys_div = max(min_sys_div,
3228c2ecf20Sopenharmony_ci			  pll->pll_op_clk_freq_hz
3238c2ecf20Sopenharmony_ci			  / limits->vt.max_sys_clk_freq_hz);
3248c2ecf20Sopenharmony_ci	dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %u\n", min_sys_div);
3258c2ecf20Sopenharmony_ci	min_sys_div = clk_div_even_up(min_sys_div);
3268c2ecf20Sopenharmony_ci	dev_dbg(dev, "min_sys_div: one or even: %u\n", min_sys_div);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	max_sys_div = limits->vt.max_sys_clk_div;
3298c2ecf20Sopenharmony_ci	dev_dbg(dev, "max_sys_div: %u\n", max_sys_div);
3308c2ecf20Sopenharmony_ci	max_sys_div = min(max_sys_div,
3318c2ecf20Sopenharmony_ci			  DIV_ROUND_UP(max_vt_div,
3328c2ecf20Sopenharmony_ci				       limits->vt.min_pix_clk_div));
3338c2ecf20Sopenharmony_ci	dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %u\n", max_sys_div);
3348c2ecf20Sopenharmony_ci	max_sys_div = min(max_sys_div,
3358c2ecf20Sopenharmony_ci			  DIV_ROUND_UP(pll->pll_op_clk_freq_hz,
3368c2ecf20Sopenharmony_ci				       limits->vt.min_pix_clk_freq_hz));
3378c2ecf20Sopenharmony_ci	dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %u\n", max_sys_div);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	/*
3408c2ecf20Sopenharmony_ci	 * Find pix_div such that a legal pix_div * sys_div results
3418c2ecf20Sopenharmony_ci	 * into a value which is not smaller than div, the desired
3428c2ecf20Sopenharmony_ci	 * divisor.
3438c2ecf20Sopenharmony_ci	 */
3448c2ecf20Sopenharmony_ci	for (vt_div = min_vt_div; vt_div <= max_vt_div;
3458c2ecf20Sopenharmony_ci	     vt_div += 2 - (vt_div & 1)) {
3468c2ecf20Sopenharmony_ci		for (sys_div = min_sys_div;
3478c2ecf20Sopenharmony_ci		     sys_div <= max_sys_div;
3488c2ecf20Sopenharmony_ci		     sys_div += 2 - (sys_div & 1)) {
3498c2ecf20Sopenharmony_ci			uint16_t pix_div = DIV_ROUND_UP(vt_div, sys_div);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci			if (pix_div < limits->vt.min_pix_clk_div
3528c2ecf20Sopenharmony_ci			    || pix_div > limits->vt.max_pix_clk_div) {
3538c2ecf20Sopenharmony_ci				dev_dbg(dev,
3548c2ecf20Sopenharmony_ci					"pix_div %u too small or too big (%u--%u)\n",
3558c2ecf20Sopenharmony_ci					pix_div,
3568c2ecf20Sopenharmony_ci					limits->vt.min_pix_clk_div,
3578c2ecf20Sopenharmony_ci					limits->vt.max_pix_clk_div);
3588c2ecf20Sopenharmony_ci				continue;
3598c2ecf20Sopenharmony_ci			}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci			/* Check if this one is better. */
3628c2ecf20Sopenharmony_ci			if (pix_div * sys_div
3638c2ecf20Sopenharmony_ci			    <= roundup(min_vt_div, best_pix_div))
3648c2ecf20Sopenharmony_ci				best_pix_div = pix_div;
3658c2ecf20Sopenharmony_ci		}
3668c2ecf20Sopenharmony_ci		if (best_pix_div < INT_MAX >> 1)
3678c2ecf20Sopenharmony_ci			break;
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	pll->vt.sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div);
3718c2ecf20Sopenharmony_ci	pll->vt.pix_clk_div = best_pix_div;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	pll->vt.sys_clk_freq_hz =
3748c2ecf20Sopenharmony_ci		pll->pll_op_clk_freq_hz / pll->vt.sys_clk_div;
3758c2ecf20Sopenharmony_ci	pll->vt.pix_clk_freq_hz =
3768c2ecf20Sopenharmony_ci		pll->vt.sys_clk_freq_hz / pll->vt.pix_clk_div;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ciout_skip_vt_calc:
3798c2ecf20Sopenharmony_ci	pll->pixel_rate_csi =
3808c2ecf20Sopenharmony_ci		op_pll->pix_clk_freq_hz * lane_op_clock_ratio;
3818c2ecf20Sopenharmony_ci	pll->pixel_rate_pixel_array = pll->vt.pix_clk_freq_hz;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	return check_all_bounds(dev, limits, op_limits, pll, op_pll);
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ciint smiapp_pll_calculate(struct device *dev,
3878c2ecf20Sopenharmony_ci			 const struct smiapp_pll_limits *limits,
3888c2ecf20Sopenharmony_ci			 struct smiapp_pll *pll)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	const struct smiapp_pll_branch_limits *op_limits = &limits->op;
3918c2ecf20Sopenharmony_ci	struct smiapp_pll_branch *op_pll = &pll->op;
3928c2ecf20Sopenharmony_ci	uint16_t min_pre_pll_clk_div;
3938c2ecf20Sopenharmony_ci	uint16_t max_pre_pll_clk_div;
3948c2ecf20Sopenharmony_ci	uint32_t lane_op_clock_ratio;
3958c2ecf20Sopenharmony_ci	uint32_t mul, div;
3968c2ecf20Sopenharmony_ci	unsigned int i;
3978c2ecf20Sopenharmony_ci	int rval = -EINVAL;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) {
4008c2ecf20Sopenharmony_ci		/*
4018c2ecf20Sopenharmony_ci		 * If there's no OP PLL at all, use the VT values
4028c2ecf20Sopenharmony_ci		 * instead. The OP values are ignored for the rest of
4038c2ecf20Sopenharmony_ci		 * the PLL calculation.
4048c2ecf20Sopenharmony_ci		 */
4058c2ecf20Sopenharmony_ci		op_limits = &limits->vt;
4068c2ecf20Sopenharmony_ci		op_pll = &pll->vt;
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE)
4108c2ecf20Sopenharmony_ci		lane_op_clock_ratio = pll->csi2.lanes;
4118c2ecf20Sopenharmony_ci	else
4128c2ecf20Sopenharmony_ci		lane_op_clock_ratio = 1;
4138c2ecf20Sopenharmony_ci	dev_dbg(dev, "lane_op_clock_ratio: %u\n", lane_op_clock_ratio);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	dev_dbg(dev, "binning: %ux%u\n", pll->binning_horizontal,
4168c2ecf20Sopenharmony_ci		pll->binning_vertical);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	switch (pll->bus_type) {
4198c2ecf20Sopenharmony_ci	case SMIAPP_PLL_BUS_TYPE_CSI2:
4208c2ecf20Sopenharmony_ci		/* CSI transfers 2 bits per clock per lane; thus times 2 */
4218c2ecf20Sopenharmony_ci		pll->pll_op_clk_freq_hz = pll->link_freq * 2
4228c2ecf20Sopenharmony_ci			* (pll->csi2.lanes / lane_op_clock_ratio);
4238c2ecf20Sopenharmony_ci		break;
4248c2ecf20Sopenharmony_ci	case SMIAPP_PLL_BUS_TYPE_PARALLEL:
4258c2ecf20Sopenharmony_ci		pll->pll_op_clk_freq_hz = pll->link_freq * pll->bits_per_pixel
4268c2ecf20Sopenharmony_ci			/ DIV_ROUND_UP(pll->bits_per_pixel,
4278c2ecf20Sopenharmony_ci				       pll->parallel.bus_width);
4288c2ecf20Sopenharmony_ci		break;
4298c2ecf20Sopenharmony_ci	default:
4308c2ecf20Sopenharmony_ci		return -EINVAL;
4318c2ecf20Sopenharmony_ci	}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	/* Figure out limits for pre-pll divider based on extclk */
4348c2ecf20Sopenharmony_ci	dev_dbg(dev, "min / max pre_pll_clk_div: %u / %u\n",
4358c2ecf20Sopenharmony_ci		limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div);
4368c2ecf20Sopenharmony_ci	max_pre_pll_clk_div =
4378c2ecf20Sopenharmony_ci		min_t(uint16_t, limits->max_pre_pll_clk_div,
4388c2ecf20Sopenharmony_ci		      clk_div_even(pll->ext_clk_freq_hz /
4398c2ecf20Sopenharmony_ci				   limits->min_pll_ip_freq_hz));
4408c2ecf20Sopenharmony_ci	min_pre_pll_clk_div =
4418c2ecf20Sopenharmony_ci		max_t(uint16_t, limits->min_pre_pll_clk_div,
4428c2ecf20Sopenharmony_ci		      clk_div_even_up(
4438c2ecf20Sopenharmony_ci			      DIV_ROUND_UP(pll->ext_clk_freq_hz,
4448c2ecf20Sopenharmony_ci					   limits->max_pll_ip_freq_hz)));
4458c2ecf20Sopenharmony_ci	dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %u / %u\n",
4468c2ecf20Sopenharmony_ci		min_pre_pll_clk_div, max_pre_pll_clk_div);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz);
4498c2ecf20Sopenharmony_ci	mul = div_u64(pll->pll_op_clk_freq_hz, i);
4508c2ecf20Sopenharmony_ci	div = pll->ext_clk_freq_hz / i;
4518c2ecf20Sopenharmony_ci	dev_dbg(dev, "mul %u / div %u\n", mul, div);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	min_pre_pll_clk_div =
4548c2ecf20Sopenharmony_ci		max_t(uint16_t, min_pre_pll_clk_div,
4558c2ecf20Sopenharmony_ci		      clk_div_even_up(
4568c2ecf20Sopenharmony_ci			      DIV_ROUND_UP(mul * pll->ext_clk_freq_hz,
4578c2ecf20Sopenharmony_ci					   limits->max_pll_op_freq_hz)));
4588c2ecf20Sopenharmony_ci	dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %u / %u\n",
4598c2ecf20Sopenharmony_ci		min_pre_pll_clk_div, max_pre_pll_clk_div);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	for (pll->pre_pll_clk_div = min_pre_pll_clk_div;
4628c2ecf20Sopenharmony_ci	     pll->pre_pll_clk_div <= max_pre_pll_clk_div;
4638c2ecf20Sopenharmony_ci	     pll->pre_pll_clk_div += 2 - (pll->pre_pll_clk_div & 1)) {
4648c2ecf20Sopenharmony_ci		rval = __smiapp_pll_calculate(dev, limits, op_limits, pll,
4658c2ecf20Sopenharmony_ci					      op_pll, mul, div,
4668c2ecf20Sopenharmony_ci					      lane_op_clock_ratio);
4678c2ecf20Sopenharmony_ci		if (rval)
4688c2ecf20Sopenharmony_ci			continue;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci		print_pll(dev, pll);
4718c2ecf20Sopenharmony_ci		return 0;
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	dev_dbg(dev, "unable to compute pre_pll divisor\n");
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	return rval;
4778c2ecf20Sopenharmony_ci}
4788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(smiapp_pll_calculate);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sakari Ailus <sakari.ailus@iki.fi>");
4818c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Generic SMIA/SMIA++ PLL calculator");
4828c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
483