xref: /openbmc/linux/drivers/media/i2c/ccs-pll.c (revision f25d3962ac8f23ab4871cef1d79e10a8c34f7908)
19e05bbacSSakari Ailus // SPDX-License-Identifier: GPL-2.0-only
29e05bbacSSakari Ailus /*
39e05bbacSSakari Ailus  * drivers/media/i2c/ccs-pll.c
49e05bbacSSakari Ailus  *
59e05bbacSSakari Ailus  * Generic MIPI CCS/SMIA/SMIA++ PLL calculator
69e05bbacSSakari Ailus  *
79e05bbacSSakari Ailus  * Copyright (C) 2020 Intel Corporation
89e05bbacSSakari Ailus  * Copyright (C) 2011--2012 Nokia Corporation
97389d01cSSakari Ailus  * Contact: Sakari Ailus <sakari.ailus@linux.intel.com>
109e05bbacSSakari Ailus  */
119e05bbacSSakari Ailus 
129e05bbacSSakari Ailus #include <linux/device.h>
139e05bbacSSakari Ailus #include <linux/gcd.h>
149e05bbacSSakari Ailus #include <linux/lcm.h>
159e05bbacSSakari Ailus #include <linux/module.h>
169e05bbacSSakari Ailus 
179e05bbacSSakari Ailus #include "ccs-pll.h"
189e05bbacSSakari Ailus 
199e05bbacSSakari Ailus /* Return an even number or one. */
209e05bbacSSakari Ailus static inline uint32_t clk_div_even(uint32_t a)
219e05bbacSSakari Ailus {
229e05bbacSSakari Ailus 	return max_t(uint32_t, 1, a & ~1);
239e05bbacSSakari Ailus }
249e05bbacSSakari Ailus 
259e05bbacSSakari Ailus /* Return an even number or one. */
269e05bbacSSakari Ailus static inline uint32_t clk_div_even_up(uint32_t a)
279e05bbacSSakari Ailus {
289e05bbacSSakari Ailus 	if (a == 1)
299e05bbacSSakari Ailus 		return 1;
309e05bbacSSakari Ailus 	return (a + 1) & ~1;
319e05bbacSSakari Ailus }
329e05bbacSSakari Ailus 
339e05bbacSSakari Ailus static inline uint32_t is_one_or_even(uint32_t a)
349e05bbacSSakari Ailus {
359e05bbacSSakari Ailus 	if (a == 1)
369e05bbacSSakari Ailus 		return 1;
379e05bbacSSakari Ailus 	if (a & 1)
389e05bbacSSakari Ailus 		return 0;
399e05bbacSSakari Ailus 
409e05bbacSSakari Ailus 	return 1;
419e05bbacSSakari Ailus }
429e05bbacSSakari Ailus 
43482e75e7SSakari Ailus static inline uint32_t one_or_more(uint32_t a)
44482e75e7SSakari Ailus {
45482e75e7SSakari Ailus 	return a ?: 1;
46482e75e7SSakari Ailus }
47482e75e7SSakari Ailus 
489e05bbacSSakari Ailus static int bounds_check(struct device *dev, uint32_t val,
49*f25d3962SSakari Ailus 			uint32_t min, uint32_t max, const char *prefix,
50*f25d3962SSakari Ailus 			char *str)
519e05bbacSSakari Ailus {
529e05bbacSSakari Ailus 	if (val >= min && val <= max)
539e05bbacSSakari Ailus 		return 0;
549e05bbacSSakari Ailus 
55*f25d3962SSakari Ailus 	dev_dbg(dev, "%s_%s out of bounds: %d (%d--%d)\n", prefix,
56*f25d3962SSakari Ailus 		str, val, min, max);
579e05bbacSSakari Ailus 
589e05bbacSSakari Ailus 	return -EINVAL;
599e05bbacSSakari Ailus }
609e05bbacSSakari Ailus 
61fadfe884SSakari Ailus #define PLL_OP 1
62fadfe884SSakari Ailus #define PLL_VT 2
63fadfe884SSakari Ailus 
64fadfe884SSakari Ailus static const char *pll_string(unsigned int which)
65fadfe884SSakari Ailus {
66fadfe884SSakari Ailus 	switch (which) {
67fadfe884SSakari Ailus 	case PLL_OP:
68fadfe884SSakari Ailus 		return "op";
69fadfe884SSakari Ailus 	case PLL_VT:
70fadfe884SSakari Ailus 		return "vt";
71fadfe884SSakari Ailus 	}
72fadfe884SSakari Ailus 
73fadfe884SSakari Ailus 	return NULL;
74fadfe884SSakari Ailus }
75fadfe884SSakari Ailus 
76fadfe884SSakari Ailus #define PLL_FL(f) CCS_PLL_FLAG_##f
77fadfe884SSakari Ailus 
789e05bbacSSakari Ailus static void print_pll(struct device *dev, struct ccs_pll *pll)
799e05bbacSSakari Ailus {
80fadfe884SSakari Ailus 	const struct {
81fadfe884SSakari Ailus 		struct ccs_pll_branch_fr *fr;
82fadfe884SSakari Ailus 		struct ccs_pll_branch_bk *bk;
83fadfe884SSakari Ailus 		unsigned int which;
84fadfe884SSakari Ailus 	} branches[] = {
85fadfe884SSakari Ailus 		{ &pll->vt_fr, &pll->vt_bk, PLL_VT },
86*f25d3962SSakari Ailus 		{ &pll->op_fr, &pll->op_bk, PLL_OP }
87fadfe884SSakari Ailus 	}, *br;
88fadfe884SSakari Ailus 	unsigned int i;
899e05bbacSSakari Ailus 
90fadfe884SSakari Ailus 	dev_dbg(dev, "ext_clk_freq_hz\t\t%u\n", pll->ext_clk_freq_hz);
91fadfe884SSakari Ailus 
92fadfe884SSakari Ailus 	for (i = 0, br = branches; i < ARRAY_SIZE(branches); i++, br++) {
93fadfe884SSakari Ailus 		const char *s = pll_string(br->which);
94fadfe884SSakari Ailus 
95fadfe884SSakari Ailus 		if (br->which == PLL_VT) {
96fadfe884SSakari Ailus 			dev_dbg(dev, "%s_pre_pll_clk_div\t\t%u\n",  s,
97fadfe884SSakari Ailus 				br->fr->pre_pll_clk_div);
98fadfe884SSakari Ailus 			dev_dbg(dev, "%s_pll_multiplier\t\t%u\n",  s,
99fadfe884SSakari Ailus 				br->fr->pll_multiplier);
100fadfe884SSakari Ailus 
101fadfe884SSakari Ailus 			dev_dbg(dev, "%s_pll_ip_clk_freq_hz\t%u\n", s,
102fadfe884SSakari Ailus 				br->fr->pll_ip_clk_freq_hz);
103fadfe884SSakari Ailus 			dev_dbg(dev, "%s_pll_op_clk_freq_hz\t%u\n", s,
104fadfe884SSakari Ailus 				br->fr->pll_op_clk_freq_hz);
1059e05bbacSSakari Ailus 		}
106fadfe884SSakari Ailus 
107fadfe884SSakari Ailus 		if (!(pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) ||
108fadfe884SSakari Ailus 		    br->which == PLL_VT) {
109fadfe884SSakari Ailus 			dev_dbg(dev, "%s_sys_clk_div\t\t%u\n",  s,
110fadfe884SSakari Ailus 				br->bk->sys_clk_div);
111fadfe884SSakari Ailus 			dev_dbg(dev, "%s_pix_clk_div\t\t%u\n", s,
112fadfe884SSakari Ailus 				br->bk->pix_clk_div);
113fadfe884SSakari Ailus 
114fadfe884SSakari Ailus 			dev_dbg(dev, "%s_sys_clk_freq_hz\t%u\n", s,
115fadfe884SSakari Ailus 				br->bk->sys_clk_freq_hz);
116fadfe884SSakari Ailus 			dev_dbg(dev, "%s_pix_clk_freq_hz\t%u\n", s,
117fadfe884SSakari Ailus 				br->bk->pix_clk_freq_hz);
118fadfe884SSakari Ailus 		}
119fadfe884SSakari Ailus 	}
120fadfe884SSakari Ailus 
121fadfe884SSakari Ailus 	dev_dbg(dev, "flags%s%s%s%s%s%s\n",
122fadfe884SSakari Ailus 		pll->flags & PLL_FL(LANE_SPEED_MODEL) ? " lane-speed" : "",
123fadfe884SSakari Ailus 		pll->flags & PLL_FL(LINK_DECOUPLED) ? " link-decoupled" : "",
124fadfe884SSakari Ailus 		pll->flags & PLL_FL(EXT_IP_PLL_DIVIDER) ?
125fadfe884SSakari Ailus 		" ext-ip-pll-divider" : "",
126fadfe884SSakari Ailus 		pll->flags & PLL_FL(FLEXIBLE_OP_PIX_CLK_DIV) ?
127fadfe884SSakari Ailus 		" flexible-op-pix-div" : "",
128fadfe884SSakari Ailus 		pll->flags & PLL_FL(FIFO_DERATING) ? " fifo-derating" : "",
129fadfe884SSakari Ailus 		pll->flags & PLL_FL(FIFO_OVERRATING) ? " fifo-overrating" : "");
1309e05bbacSSakari Ailus }
1319e05bbacSSakari Ailus 
132*f25d3962SSakari Ailus static int check_fr_bounds(struct device *dev,
133415ddd99SSakari Ailus 			   const struct ccs_pll_limits *lim,
134*f25d3962SSakari Ailus 			   struct ccs_pll *pll, unsigned int which)
1359e05bbacSSakari Ailus {
136*f25d3962SSakari Ailus 	const struct ccs_pll_branch_limits_fr *lim_fr;
137*f25d3962SSakari Ailus 	struct ccs_pll_branch_fr *pll_fr;
138*f25d3962SSakari Ailus 	const char *s = pll_string(which);
1399e05bbacSSakari Ailus 	int rval;
1409e05bbacSSakari Ailus 
141*f25d3962SSakari Ailus 	if (which == PLL_OP) {
142*f25d3962SSakari Ailus 		lim_fr = &lim->op_fr;
143*f25d3962SSakari Ailus 		pll_fr = &pll->op_fr;
144*f25d3962SSakari Ailus 	} else {
145*f25d3962SSakari Ailus 		lim_fr = &lim->vt_fr;
146*f25d3962SSakari Ailus 		pll_fr = &pll->vt_fr;
147*f25d3962SSakari Ailus 	}
1489e05bbacSSakari Ailus 
149*f25d3962SSakari Ailus 	rval = bounds_check(dev, pll_fr->pre_pll_clk_div,
150*f25d3962SSakari Ailus 			    lim_fr->min_pre_pll_clk_div,
151*f25d3962SSakari Ailus 			    lim_fr->max_pre_pll_clk_div, s, "pre_pll_clk_div");
152*f25d3962SSakari Ailus 
153*f25d3962SSakari Ailus 	if (!rval)
154*f25d3962SSakari Ailus 		rval = bounds_check(dev, pll_fr->pll_ip_clk_freq_hz,
155*f25d3962SSakari Ailus 				    lim_fr->min_pll_ip_clk_freq_hz,
156*f25d3962SSakari Ailus 				    lim_fr->max_pll_ip_clk_freq_hz,
157*f25d3962SSakari Ailus 				    s, "pll_ip_clk_freq_hz");
158*f25d3962SSakari Ailus 	if (!rval)
159*f25d3962SSakari Ailus 		rval = bounds_check(dev, pll_fr->pll_multiplier,
160*f25d3962SSakari Ailus 				    lim_fr->min_pll_multiplier,
161*f25d3962SSakari Ailus 				    lim_fr->max_pll_multiplier,
162*f25d3962SSakari Ailus 				    s, "pll_multiplier");
163*f25d3962SSakari Ailus 	if (!rval)
164*f25d3962SSakari Ailus 		rval = bounds_check(dev, pll_fr->pll_op_clk_freq_hz,
165*f25d3962SSakari Ailus 				    lim_fr->min_pll_op_clk_freq_hz,
166*f25d3962SSakari Ailus 				    lim_fr->max_pll_op_clk_freq_hz,
167*f25d3962SSakari Ailus 				    s, "pll_op_clk_freq_hz");
168*f25d3962SSakari Ailus 
1699e05bbacSSakari Ailus 	return rval;
170*f25d3962SSakari Ailus }
1719e05bbacSSakari Ailus 
172*f25d3962SSakari Ailus static int check_bk_bounds(struct device *dev,
173*f25d3962SSakari Ailus 			   const struct ccs_pll_limits *lim,
174*f25d3962SSakari Ailus 			   struct ccs_pll *pll, unsigned int which)
175*f25d3962SSakari Ailus {
176*f25d3962SSakari Ailus 	const struct ccs_pll_branch_limits_bk *lim_bk;
177*f25d3962SSakari Ailus 	struct ccs_pll_branch_bk *pll_bk;
178*f25d3962SSakari Ailus 	const char *s = pll_string(which);
179*f25d3962SSakari Ailus 	int rval;
1809e05bbacSSakari Ailus 
181*f25d3962SSakari Ailus 	if (which == PLL_OP) {
182*f25d3962SSakari Ailus 		if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS)
183*f25d3962SSakari Ailus 			return 0;
184*f25d3962SSakari Ailus 
185*f25d3962SSakari Ailus 		lim_bk = &lim->op_bk;
186*f25d3962SSakari Ailus 		pll_bk = &pll->op_bk;
187*f25d3962SSakari Ailus 	} else {
188*f25d3962SSakari Ailus 		lim_bk = &lim->vt_bk;
189*f25d3962SSakari Ailus 		pll_bk = &pll->vt_bk;
190*f25d3962SSakari Ailus 	}
191*f25d3962SSakari Ailus 
192*f25d3962SSakari Ailus 	rval = bounds_check(dev, pll_bk->sys_clk_div,
193*f25d3962SSakari Ailus 			    lim_bk->min_sys_clk_div,
194*f25d3962SSakari Ailus 			    lim_bk->max_sys_clk_div, s, "op_sys_clk_div");
195*f25d3962SSakari Ailus 	if (!rval)
196*f25d3962SSakari Ailus 		rval = bounds_check(dev, pll_bk->sys_clk_freq_hz,
197*f25d3962SSakari Ailus 				    lim_bk->min_sys_clk_freq_hz,
198*f25d3962SSakari Ailus 				    lim_bk->max_sys_clk_freq_hz,
199*f25d3962SSakari Ailus 				    s, "sys_clk_freq_hz");
200*f25d3962SSakari Ailus 	if (!rval)
201*f25d3962SSakari Ailus 		rval = bounds_check(dev, pll_bk->sys_clk_div,
202*f25d3962SSakari Ailus 				    lim_bk->min_sys_clk_div,
203*f25d3962SSakari Ailus 				    lim_bk->max_sys_clk_div,
204*f25d3962SSakari Ailus 				    s, "sys_clk_div");
205*f25d3962SSakari Ailus 	if (!rval)
206*f25d3962SSakari Ailus 		rval = bounds_check(dev, pll_bk->pix_clk_freq_hz,
207*f25d3962SSakari Ailus 				    lim_bk->min_pix_clk_freq_hz,
208*f25d3962SSakari Ailus 				    lim_bk->max_pix_clk_freq_hz,
209*f25d3962SSakari Ailus 				    s, "pix_clk_freq_hz");
210*f25d3962SSakari Ailus 
211*f25d3962SSakari Ailus 	return rval;
212*f25d3962SSakari Ailus }
213*f25d3962SSakari Ailus 
214*f25d3962SSakari Ailus static int check_ext_bounds(struct device *dev, struct ccs_pll *pll)
215*f25d3962SSakari Ailus {
21638c94eb8SSakari Ailus 	if (!(pll->flags & CCS_PLL_FLAG_FIFO_DERATING) &&
21738c94eb8SSakari Ailus 	    pll->pixel_rate_pixel_array > pll->pixel_rate_csi) {
21838c94eb8SSakari Ailus 		dev_dbg(dev, "device does not support derating\n");
21938c94eb8SSakari Ailus 		return -EINVAL;
22038c94eb8SSakari Ailus 	}
22138c94eb8SSakari Ailus 
22238c94eb8SSakari Ailus 	if (!(pll->flags & CCS_PLL_FLAG_FIFO_OVERRATING) &&
22338c94eb8SSakari Ailus 	    pll->pixel_rate_pixel_array < pll->pixel_rate_csi) {
22438c94eb8SSakari Ailus 		dev_dbg(dev, "device does not support overrating\n");
22538c94eb8SSakari Ailus 		return -EINVAL;
22638c94eb8SSakari Ailus 	}
22738c94eb8SSakari Ailus 
228*f25d3962SSakari Ailus 	return 0;
2299e05bbacSSakari Ailus }
2309e05bbacSSakari Ailus 
2318030aa4fSSakari Ailus #define CPHY_CONST		7
2328030aa4fSSakari Ailus #define DPHY_CONST		16
2338030aa4fSSakari Ailus #define PHY_CONST_DIV		16
2348030aa4fSSakari Ailus 
2353e2db036SSakari Ailus static void
236a38836b2SSakari Ailus ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim,
2373e2db036SSakari Ailus 		     const struct ccs_pll_branch_limits_bk *op_lim_bk,
2383e2db036SSakari Ailus 		     struct ccs_pll *pll, struct ccs_pll_branch_fr *pll_fr,
2393e2db036SSakari Ailus 		     struct ccs_pll_branch_bk *op_pll_bk, bool cphy,
2403e2db036SSakari Ailus 		     uint32_t phy_const)
2413e2db036SSakari Ailus {
2423e2db036SSakari Ailus 	uint32_t sys_div;
2433e2db036SSakari Ailus 	uint32_t best_pix_div = INT_MAX >> 1;
2443e2db036SSakari Ailus 	uint32_t vt_op_binning_div;
2453e2db036SSakari Ailus 	uint32_t min_vt_div, max_vt_div, vt_div;
2463e2db036SSakari Ailus 	uint32_t min_sys_div, max_sys_div;
2473e2db036SSakari Ailus 
248a38836b2SSakari Ailus 	if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS)
249a38836b2SSakari Ailus 		goto out_calc_pixel_rate;
250a38836b2SSakari Ailus 
2513e2db036SSakari Ailus 	/*
25238c94eb8SSakari Ailus 	 * Find out whether a sensor supports derating. If it does not, VT and
25338c94eb8SSakari Ailus 	 * OP domains are required to run at the same pixel rate.
25438c94eb8SSakari Ailus 	 */
25538c94eb8SSakari Ailus 	if (!(pll->flags & CCS_PLL_FLAG_FIFO_DERATING)) {
25638c94eb8SSakari Ailus 		min_vt_div =
25738c94eb8SSakari Ailus 			op_pll_bk->sys_clk_div * op_pll_bk->pix_clk_div
25838c94eb8SSakari Ailus 			* pll->vt_lanes * phy_const
25938c94eb8SSakari Ailus 			/ pll->op_lanes / PHY_CONST_DIV;
26038c94eb8SSakari Ailus 	} else {
26138c94eb8SSakari Ailus 		/*
2623e2db036SSakari Ailus 		 * Some sensors perform analogue binning and some do this
2633e2db036SSakari Ailus 		 * digitally. The ones doing this digitally can be roughly be
2643e2db036SSakari Ailus 		 * found out using this formula. The ones doing this digitally
2653e2db036SSakari Ailus 		 * should run at higher clock rate, so smaller divisor is used
2663e2db036SSakari Ailus 		 * on video timing side.
2673e2db036SSakari Ailus 		 */
2683e2db036SSakari Ailus 		if (lim->min_line_length_pck_bin > lim->min_line_length_pck
2693e2db036SSakari Ailus 		    / pll->binning_horizontal)
2703e2db036SSakari Ailus 			vt_op_binning_div = pll->binning_horizontal;
2713e2db036SSakari Ailus 		else
2723e2db036SSakari Ailus 			vt_op_binning_div = 1;
2733e2db036SSakari Ailus 		dev_dbg(dev, "vt_op_binning_div: %u\n", vt_op_binning_div);
2743e2db036SSakari Ailus 
2753e2db036SSakari Ailus 		/*
2763e2db036SSakari Ailus 		 * Profile 2 supports vt_pix_clk_div E [4, 10]
2773e2db036SSakari Ailus 		 *
2783e2db036SSakari Ailus 		 * Horizontal binning can be used as a base for difference in
2793e2db036SSakari Ailus 		 * divisors. One must make sure that horizontal blanking is
2803e2db036SSakari Ailus 		 * enough to accommodate the CSI-2 sync codes.
2813e2db036SSakari Ailus 		 *
2823e2db036SSakari Ailus 		 * Take scaling factor and number of VT lanes into account as well.
2833e2db036SSakari Ailus 		 *
2843e2db036SSakari Ailus 		 * Find absolute limits for the factor of vt divider.
2853e2db036SSakari Ailus 		 */
2863e2db036SSakari Ailus 		dev_dbg(dev, "scale_m: %u\n", pll->scale_m);
28738c94eb8SSakari Ailus 		min_vt_div =
28838c94eb8SSakari Ailus 			DIV_ROUND_UP(pll->bits_per_pixel
28938c94eb8SSakari Ailus 				     * op_pll_bk->sys_clk_div * pll->scale_n
29038c94eb8SSakari Ailus 				     * pll->vt_lanes * phy_const,
29138c94eb8SSakari Ailus 				     (pll->flags &
29238c94eb8SSakari Ailus 				      CCS_PLL_FLAG_LANE_SPEED_MODEL ?
2933e2db036SSakari Ailus 				      pll->csi2.lanes : 1)
2943e2db036SSakari Ailus 				     * vt_op_binning_div * pll->scale_m
2953e2db036SSakari Ailus 				     * PHY_CONST_DIV);
29638c94eb8SSakari Ailus 	}
2973e2db036SSakari Ailus 
2983e2db036SSakari Ailus 	/* Find smallest and biggest allowed vt divisor. */
2993e2db036SSakari Ailus 	dev_dbg(dev, "min_vt_div: %u\n", min_vt_div);
3003e2db036SSakari Ailus 	min_vt_div = max(min_vt_div,
3013e2db036SSakari Ailus 			 DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz,
3023e2db036SSakari Ailus 				      lim->vt_bk.max_pix_clk_freq_hz));
3033e2db036SSakari Ailus 	dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %u\n",
3043e2db036SSakari Ailus 		min_vt_div);
3053e2db036SSakari Ailus 	min_vt_div = max_t(uint32_t, min_vt_div,
3063e2db036SSakari Ailus 			   lim->vt_bk.min_pix_clk_div
3073e2db036SSakari Ailus 			   * lim->vt_bk.min_sys_clk_div);
3083e2db036SSakari Ailus 	dev_dbg(dev, "min_vt_div: min_vt_clk_div: %u\n", min_vt_div);
3093e2db036SSakari Ailus 
3103e2db036SSakari Ailus 	max_vt_div = lim->vt_bk.max_sys_clk_div * lim->vt_bk.max_pix_clk_div;
3113e2db036SSakari Ailus 	dev_dbg(dev, "max_vt_div: %u\n", max_vt_div);
3123e2db036SSakari Ailus 	max_vt_div = min(max_vt_div,
3133e2db036SSakari Ailus 			 DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz,
3143e2db036SSakari Ailus 				      lim->vt_bk.min_pix_clk_freq_hz));
3153e2db036SSakari Ailus 	dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %u\n",
3163e2db036SSakari Ailus 		max_vt_div);
3173e2db036SSakari Ailus 
3183e2db036SSakari Ailus 	/*
3193e2db036SSakari Ailus 	 * Find limitsits for sys_clk_div. Not all values are possible
3203e2db036SSakari Ailus 	 * with all values of pix_clk_div.
3213e2db036SSakari Ailus 	 */
3223e2db036SSakari Ailus 	min_sys_div = lim->vt_bk.min_sys_clk_div;
3233e2db036SSakari Ailus 	dev_dbg(dev, "min_sys_div: %u\n", min_sys_div);
3243e2db036SSakari Ailus 	min_sys_div = max(min_sys_div,
3253e2db036SSakari Ailus 			  DIV_ROUND_UP(min_vt_div,
3263e2db036SSakari Ailus 				       lim->vt_bk.max_pix_clk_div));
3273e2db036SSakari Ailus 	dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %u\n", min_sys_div);
3283e2db036SSakari Ailus 	min_sys_div = max(min_sys_div,
3293e2db036SSakari Ailus 			  pll_fr->pll_op_clk_freq_hz
3303e2db036SSakari Ailus 			  / lim->vt_bk.max_sys_clk_freq_hz);
3313e2db036SSakari Ailus 	dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %u\n", min_sys_div);
3323e2db036SSakari Ailus 	min_sys_div = clk_div_even_up(min_sys_div);
3333e2db036SSakari Ailus 	dev_dbg(dev, "min_sys_div: one or even: %u\n", min_sys_div);
3343e2db036SSakari Ailus 
3353e2db036SSakari Ailus 	max_sys_div = lim->vt_bk.max_sys_clk_div;
3363e2db036SSakari Ailus 	dev_dbg(dev, "max_sys_div: %u\n", max_sys_div);
3373e2db036SSakari Ailus 	max_sys_div = min(max_sys_div,
3383e2db036SSakari Ailus 			  DIV_ROUND_UP(max_vt_div,
3393e2db036SSakari Ailus 				       lim->vt_bk.min_pix_clk_div));
3403e2db036SSakari Ailus 	dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %u\n", max_sys_div);
3413e2db036SSakari Ailus 	max_sys_div = min(max_sys_div,
3423e2db036SSakari Ailus 			  DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz,
3433e2db036SSakari Ailus 				       lim->vt_bk.min_pix_clk_freq_hz));
3443e2db036SSakari Ailus 	dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %u\n", max_sys_div);
3453e2db036SSakari Ailus 
3463e2db036SSakari Ailus 	/*
3473e2db036SSakari Ailus 	 * Find pix_div such that a legal pix_div * sys_div results
3483e2db036SSakari Ailus 	 * into a value which is not smaller than div, the desired
3493e2db036SSakari Ailus 	 * divisor.
3503e2db036SSakari Ailus 	 */
3513e2db036SSakari Ailus 	for (vt_div = min_vt_div; vt_div <= max_vt_div;
3523e2db036SSakari Ailus 	     vt_div += 2 - (vt_div & 1)) {
3533e2db036SSakari Ailus 		for (sys_div = min_sys_div;
3543e2db036SSakari Ailus 		     sys_div <= max_sys_div;
3553e2db036SSakari Ailus 		     sys_div += 2 - (sys_div & 1)) {
3563e2db036SSakari Ailus 			uint16_t pix_div = DIV_ROUND_UP(vt_div, sys_div);
3573e2db036SSakari Ailus 			uint16_t rounded_div;
3583e2db036SSakari Ailus 
3593e2db036SSakari Ailus 			if (pix_div < lim->vt_bk.min_pix_clk_div
3603e2db036SSakari Ailus 			    || pix_div > lim->vt_bk.max_pix_clk_div) {
3613e2db036SSakari Ailus 				dev_dbg(dev,
3623e2db036SSakari Ailus 					"pix_div %u too small or too big (%u--%u)\n",
3633e2db036SSakari Ailus 					pix_div,
3643e2db036SSakari Ailus 					lim->vt_bk.min_pix_clk_div,
3653e2db036SSakari Ailus 					lim->vt_bk.max_pix_clk_div);
3663e2db036SSakari Ailus 				continue;
3673e2db036SSakari Ailus 			}
3683e2db036SSakari Ailus 
3693e2db036SSakari Ailus 			rounded_div = roundup(vt_div, best_pix_div);
3703e2db036SSakari Ailus 
3713e2db036SSakari Ailus 			/* Check if this one is better. */
3723e2db036SSakari Ailus 			if (pix_div * sys_div <= rounded_div)
3733e2db036SSakari Ailus 				best_pix_div = pix_div;
3743e2db036SSakari Ailus 
3753e2db036SSakari Ailus 			/* Bail out if we've already found the best value. */
3763e2db036SSakari Ailus 			if (vt_div == rounded_div)
3773e2db036SSakari Ailus 				break;
3783e2db036SSakari Ailus 		}
3793e2db036SSakari Ailus 		if (best_pix_div < INT_MAX >> 1)
3803e2db036SSakari Ailus 			break;
3813e2db036SSakari Ailus 	}
3823e2db036SSakari Ailus 
3833e2db036SSakari Ailus 	pll->vt_bk.sys_clk_div = DIV_ROUND_UP(vt_div, best_pix_div);
3843e2db036SSakari Ailus 	pll->vt_bk.pix_clk_div = best_pix_div;
3853e2db036SSakari Ailus 
3863e2db036SSakari Ailus 	pll->vt_bk.sys_clk_freq_hz =
3873e2db036SSakari Ailus 		pll_fr->pll_op_clk_freq_hz / pll->vt_bk.sys_clk_div;
3883e2db036SSakari Ailus 	pll->vt_bk.pix_clk_freq_hz =
3893e2db036SSakari Ailus 		pll->vt_bk.sys_clk_freq_hz / pll->vt_bk.pix_clk_div;
390a38836b2SSakari Ailus 
391a38836b2SSakari Ailus out_calc_pixel_rate:
392a38836b2SSakari Ailus 	pll->pixel_rate_pixel_array =
393a38836b2SSakari Ailus 		pll->vt_bk.pix_clk_freq_hz * pll->vt_lanes;
3943e2db036SSakari Ailus }
3953e2db036SSakari Ailus 
3969e05bbacSSakari Ailus /*
3979e05bbacSSakari Ailus  * Heuristically guess the PLL tree for a given common multiplier and
3989e05bbacSSakari Ailus  * divisor. Begin with the operational timing and continue to video
3999e05bbacSSakari Ailus  * timing once operational timing has been verified.
4009e05bbacSSakari Ailus  *
4019e05bbacSSakari Ailus  * @mul is the PLL multiplier and @div is the common divisor
4029e05bbacSSakari Ailus  * (pre_pll_clk_div and op_sys_clk_div combined). The final PLL
4039e05bbacSSakari Ailus  * multiplier will be a multiple of @mul.
4049e05bbacSSakari Ailus  *
4059e05bbacSSakari Ailus  * @return Zero on success, error code on error.
4069e05bbacSSakari Ailus  */
4079e05bbacSSakari Ailus static int
408a38836b2SSakari Ailus ccs_pll_calculate_op(struct device *dev, const struct ccs_pll_limits *lim,
409415ddd99SSakari Ailus 		     const struct ccs_pll_branch_limits_fr *op_lim_fr,
410415ddd99SSakari Ailus 		     const struct ccs_pll_branch_limits_bk *op_lim_bk,
411415ddd99SSakari Ailus 		     struct ccs_pll *pll, struct ccs_pll_branch_fr *op_pll_fr,
412415ddd99SSakari Ailus 		     struct ccs_pll_branch_bk *op_pll_bk, uint32_t mul,
4138030aa4fSSakari Ailus 		     uint32_t div, uint32_t l, bool cphy, uint32_t phy_const)
4149e05bbacSSakari Ailus {
4159e05bbacSSakari Ailus 	/*
4169e05bbacSSakari Ailus 	 * Higher multipliers (and divisors) are often required than
4179e05bbacSSakari Ailus 	 * necessitated by the external clock and the output clocks.
4189e05bbacSSakari Ailus 	 * There are limits for all values in the clock tree. These
4199e05bbacSSakari Ailus 	 * are the minimum and maximum multiplier for mul.
4209e05bbacSSakari Ailus 	 */
4219e05bbacSSakari Ailus 	uint32_t more_mul_min, more_mul_max;
4229e05bbacSSakari Ailus 	uint32_t more_mul_factor;
423e583e654SSakari Ailus 	uint32_t i;
4249e05bbacSSakari Ailus 
4259e05bbacSSakari Ailus 	/*
4269e05bbacSSakari Ailus 	 * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be
4279e05bbacSSakari Ailus 	 * too high.
4289e05bbacSSakari Ailus 	 */
429415ddd99SSakari Ailus 	dev_dbg(dev, "op_pre_pll_clk_div %u\n", op_pll_fr->pre_pll_clk_div);
4309e05bbacSSakari Ailus 
4319e05bbacSSakari Ailus 	/* Don't go above max pll multiplier. */
432415ddd99SSakari Ailus 	more_mul_max = op_lim_fr->max_pll_multiplier / mul;
433415ddd99SSakari Ailus 	dev_dbg(dev, "more_mul_max: max_op_pll_multiplier check: %u\n",
4349e05bbacSSakari Ailus 		more_mul_max);
4359e05bbacSSakari Ailus 	/* Don't go above max pll op frequency. */
4369e05bbacSSakari Ailus 	more_mul_max =
4379e05bbacSSakari Ailus 		min_t(uint32_t,
4389e05bbacSSakari Ailus 		      more_mul_max,
439415ddd99SSakari Ailus 		      op_lim_fr->max_pll_op_clk_freq_hz
440ae502e08SSakari Ailus 		      / (pll->ext_clk_freq_hz /
441ae502e08SSakari Ailus 			 op_pll_fr->pre_pll_clk_div * mul));
442415ddd99SSakari Ailus 	dev_dbg(dev, "more_mul_max: max_pll_op_clk_freq_hz check: %u\n",
4439e05bbacSSakari Ailus 		more_mul_max);
4449e05bbacSSakari Ailus 	/* Don't go above the division capability of op sys clock divider. */
4459e05bbacSSakari Ailus 	more_mul_max = min(more_mul_max,
446415ddd99SSakari Ailus 			   op_lim_bk->max_sys_clk_div * op_pll_fr->pre_pll_clk_div
4479e05bbacSSakari Ailus 			   / div);
4489e05bbacSSakari Ailus 	dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %u\n",
4499e05bbacSSakari Ailus 		more_mul_max);
450c64cf71dSSakari Ailus 	/* Ensure we won't go above max_pll_multiplier. */
45182ab97c8SSakari Ailus 	more_mul_max = min(more_mul_max, op_lim_fr->max_pll_multiplier / mul);
4529e05bbacSSakari Ailus 	dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %u\n",
4539e05bbacSSakari Ailus 		more_mul_max);
4549e05bbacSSakari Ailus 
455415ddd99SSakari Ailus 	/* Ensure we won't go below min_pll_op_clk_freq_hz. */
456415ddd99SSakari Ailus 	more_mul_min = DIV_ROUND_UP(op_lim_fr->min_pll_op_clk_freq_hz,
457415ddd99SSakari Ailus 				    pll->ext_clk_freq_hz /
458415ddd99SSakari Ailus 				    op_pll_fr->pre_pll_clk_div * mul);
459415ddd99SSakari Ailus 	dev_dbg(dev, "more_mul_min: min_op_pll_op_clk_freq_hz check: %u\n",
4609e05bbacSSakari Ailus 		more_mul_min);
4619e05bbacSSakari Ailus 	/* Ensure we won't go below min_pll_multiplier. */
4629e05bbacSSakari Ailus 	more_mul_min = max(more_mul_min,
463415ddd99SSakari Ailus 			   DIV_ROUND_UP(op_lim_fr->min_pll_multiplier, mul));
464415ddd99SSakari Ailus 	dev_dbg(dev, "more_mul_min: min_op_pll_multiplier check: %u\n",
4659e05bbacSSakari Ailus 		more_mul_min);
4669e05bbacSSakari Ailus 
4679e05bbacSSakari Ailus 	if (more_mul_min > more_mul_max) {
4689e05bbacSSakari Ailus 		dev_dbg(dev,
4699e05bbacSSakari Ailus 			"unable to compute more_mul_min and more_mul_max\n");
4709e05bbacSSakari Ailus 		return -EINVAL;
4719e05bbacSSakari Ailus 	}
4729e05bbacSSakari Ailus 
473415ddd99SSakari Ailus 	more_mul_factor = lcm(div, op_pll_fr->pre_pll_clk_div) / div;
4749e05bbacSSakari Ailus 	dev_dbg(dev, "more_mul_factor: %u\n", more_mul_factor);
475415ddd99SSakari Ailus 	more_mul_factor = lcm(more_mul_factor, op_lim_bk->min_sys_clk_div);
4769e05bbacSSakari Ailus 	dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n",
4779e05bbacSSakari Ailus 		more_mul_factor);
4789e05bbacSSakari Ailus 	i = roundup(more_mul_min, more_mul_factor);
4799e05bbacSSakari Ailus 	if (!is_one_or_even(i))
4809e05bbacSSakari Ailus 		i <<= 1;
4819e05bbacSSakari Ailus 
4829e05bbacSSakari Ailus 	dev_dbg(dev, "final more_mul: %u\n", i);
4839e05bbacSSakari Ailus 	if (i > more_mul_max) {
4849e05bbacSSakari Ailus 		dev_dbg(dev, "final more_mul is bad, max %u\n", more_mul_max);
4859e05bbacSSakari Ailus 		return -EINVAL;
4869e05bbacSSakari Ailus 	}
4879e05bbacSSakari Ailus 
488415ddd99SSakari Ailus 	op_pll_fr->pll_multiplier = mul * i;
489415ddd99SSakari Ailus 	op_pll_bk->sys_clk_div = div * i / op_pll_fr->pre_pll_clk_div;
490415ddd99SSakari Ailus 	dev_dbg(dev, "op_sys_clk_div: %u\n", op_pll_bk->sys_clk_div);
4919e05bbacSSakari Ailus 
492415ddd99SSakari Ailus 	op_pll_fr->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz
493415ddd99SSakari Ailus 		/ op_pll_fr->pre_pll_clk_div;
4949e05bbacSSakari Ailus 
495415ddd99SSakari Ailus 	op_pll_fr->pll_op_clk_freq_hz = op_pll_fr->pll_ip_clk_freq_hz
496415ddd99SSakari Ailus 		* op_pll_fr->pll_multiplier;
4979e05bbacSSakari Ailus 
498c4c0b222SSakari Ailus 	if (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL)
499cac8f5d2SSakari Ailus 		op_pll_bk->pix_clk_div = pll->bits_per_pixel
5008030aa4fSSakari Ailus 			* pll->op_lanes * phy_const
5018030aa4fSSakari Ailus 			/ PHY_CONST_DIV / pll->csi2.lanes / l;
502c4c0b222SSakari Ailus 	else
5038030aa4fSSakari Ailus 		op_pll_bk->pix_clk_div =
5048030aa4fSSakari Ailus 			pll->bits_per_pixel * phy_const / PHY_CONST_DIV / l;
505c4c0b222SSakari Ailus 
506415ddd99SSakari Ailus 	op_pll_bk->pix_clk_freq_hz =
507415ddd99SSakari Ailus 		op_pll_bk->sys_clk_freq_hz / op_pll_bk->pix_clk_div;
508c4c0b222SSakari Ailus 
509cac8f5d2SSakari Ailus 	dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll_bk->pix_clk_div);
510cac8f5d2SSakari Ailus 
511a38836b2SSakari Ailus 	return 0;
5129e05bbacSSakari Ailus }
5139e05bbacSSakari Ailus 
514415ddd99SSakari Ailus int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
5159e05bbacSSakari Ailus 		      struct ccs_pll *pll)
5169e05bbacSSakari Ailus {
517415ddd99SSakari Ailus 	const struct ccs_pll_branch_limits_fr *op_lim_fr = &lim->vt_fr;
518415ddd99SSakari Ailus 	const struct ccs_pll_branch_limits_bk *op_lim_bk = &lim->op_bk;
519415ddd99SSakari Ailus 	struct ccs_pll_branch_fr *op_pll_fr = &pll->vt_fr;
520415ddd99SSakari Ailus 	struct ccs_pll_branch_bk *op_pll_bk = &pll->op_bk;
5218030aa4fSSakari Ailus 	bool cphy = pll->bus_type == CCS_PLL_BUS_TYPE_CSI2_CPHY;
5228030aa4fSSakari Ailus 	uint32_t phy_const = cphy ? CPHY_CONST : DPHY_CONST;
523415ddd99SSakari Ailus 	uint16_t min_op_pre_pll_clk_div;
524415ddd99SSakari Ailus 	uint16_t max_op_pre_pll_clk_div;
5259e05bbacSSakari Ailus 	uint32_t mul, div;
526c4c0b222SSakari Ailus 	uint32_t l = (!pll->op_bits_per_lane ||
527c4c0b222SSakari Ailus 		      pll->op_bits_per_lane >= pll->bits_per_pixel) ? 1 : 2;
528e583e654SSakari Ailus 	uint32_t i;
5299e05bbacSSakari Ailus 	int rval = -EINVAL;
5309e05bbacSSakari Ailus 
531cac8f5d2SSakari Ailus 	if (!(pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL)) {
532cac8f5d2SSakari Ailus 		pll->op_lanes = 1;
533cac8f5d2SSakari Ailus 		pll->vt_lanes = 1;
534cac8f5d2SSakari Ailus 	}
5359490a227SSakari Ailus 
536d7172c0eSSakari Ailus 	if (!pll->op_lanes || !pll->vt_lanes || !pll->bits_per_pixel ||
537d7172c0eSSakari Ailus 	    !pll->ext_clk_freq_hz || !pll->link_freq || !pll->scale_m ||
538d7172c0eSSakari Ailus 	    !op_lim_fr->min_pll_ip_clk_freq_hz ||
539d7172c0eSSakari Ailus 	    !op_lim_fr->max_pll_ip_clk_freq_hz ||
540d7172c0eSSakari Ailus 	    !op_lim_fr->min_pll_op_clk_freq_hz ||
541d7172c0eSSakari Ailus 	    !op_lim_fr->max_pll_op_clk_freq_hz ||
542d7172c0eSSakari Ailus 	    !op_lim_bk->max_sys_clk_div || !op_lim_fr->max_pll_multiplier)
543d7172c0eSSakari Ailus 		return -EINVAL;
544d7172c0eSSakari Ailus 
5459490a227SSakari Ailus 	/*
5469490a227SSakari Ailus 	 * Make sure op_pix_clk_div will be integer --- unless flexible
5479490a227SSakari Ailus 	 * op_pix_clk_div is supported
5489490a227SSakari Ailus 	 */
5499490a227SSakari Ailus 	if (!(pll->flags & CCS_PLL_FLAG_FLEXIBLE_OP_PIX_CLK_DIV) &&
5509490a227SSakari Ailus 	    (pll->bits_per_pixel * pll->op_lanes) % (pll->csi2.lanes * l)) {
5519490a227SSakari Ailus 		dev_dbg(dev, "op_pix_clk_div not an integer (bpp %u, op lanes %u, lanes %u, l %u)\n",
5529490a227SSakari Ailus 			pll->bits_per_pixel, pll->op_lanes, pll->csi2.lanes, l);
5539490a227SSakari Ailus 		return -EINVAL;
5549490a227SSakari Ailus 	}
5559490a227SSakari Ailus 
556cac8f5d2SSakari Ailus 	dev_dbg(dev, "vt_lanes: %u\n", pll->vt_lanes);
557cac8f5d2SSakari Ailus 	dev_dbg(dev, "op_lanes: %u\n", pll->op_lanes);
558cac8f5d2SSakari Ailus 
5599e05bbacSSakari Ailus 	if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) {
5609e05bbacSSakari Ailus 		/*
5619e05bbacSSakari Ailus 		 * If there's no OP PLL at all, use the VT values
5629e05bbacSSakari Ailus 		 * instead. The OP values are ignored for the rest of
5639e05bbacSSakari Ailus 		 * the PLL calculation.
5649e05bbacSSakari Ailus 		 */
565415ddd99SSakari Ailus 		op_lim_fr = &lim->vt_fr;
566415ddd99SSakari Ailus 		op_lim_bk = &lim->vt_bk;
567415ddd99SSakari Ailus 		op_pll_bk = &pll->vt_bk;
5689e05bbacSSakari Ailus 	}
5699e05bbacSSakari Ailus 
5709e05bbacSSakari Ailus 	dev_dbg(dev, "binning: %ux%u\n", pll->binning_horizontal,
5719e05bbacSSakari Ailus 		pll->binning_vertical);
5729e05bbacSSakari Ailus 
5739e05bbacSSakari Ailus 	switch (pll->bus_type) {
57447b6eaf3SSakari Ailus 	case CCS_PLL_BUS_TYPE_CSI2_DPHY:
5759e05bbacSSakari Ailus 		/* CSI transfers 2 bits per clock per lane; thus times 2 */
576cab27256SSakari Ailus 		op_pll_bk->sys_clk_freq_hz = pll->link_freq * 2
577cac8f5d2SSakari Ailus 			* (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
578ae502e08SSakari Ailus 			   1 : pll->csi2.lanes);
5799e05bbacSSakari Ailus 		break;
5808030aa4fSSakari Ailus 	case CCS_PLL_BUS_TYPE_CSI2_CPHY:
5818030aa4fSSakari Ailus 		op_pll_bk->sys_clk_freq_hz =
5828030aa4fSSakari Ailus 			pll->link_freq
5838030aa4fSSakari Ailus 			* (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
5848030aa4fSSakari Ailus 			   1 : pll->csi2.lanes);
5858030aa4fSSakari Ailus 		break;
5869e05bbacSSakari Ailus 	default:
5879e05bbacSSakari Ailus 		return -EINVAL;
5889e05bbacSSakari Ailus 	}
5899e05bbacSSakari Ailus 
590cac8f5d2SSakari Ailus 	pll->pixel_rate_csi =
5918030aa4fSSakari Ailus 		div_u64((uint64_t)op_pll_bk->sys_clk_freq_hz
592cac8f5d2SSakari Ailus 			* (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
5938030aa4fSSakari Ailus 			   pll->csi2.lanes : 1) * PHY_CONST_DIV,
5948030aa4fSSakari Ailus 			phy_const * pll->bits_per_pixel * l);
595cac8f5d2SSakari Ailus 
596415ddd99SSakari Ailus 	/* Figure out limits for OP pre-pll divider based on extclk */
597415ddd99SSakari Ailus 	dev_dbg(dev, "min / max op_pre_pll_clk_div: %u / %u\n",
598415ddd99SSakari Ailus 		op_lim_fr->min_pre_pll_clk_div, op_lim_fr->max_pre_pll_clk_div);
599415ddd99SSakari Ailus 	max_op_pre_pll_clk_div =
600415ddd99SSakari Ailus 		min_t(uint16_t, op_lim_fr->max_pre_pll_clk_div,
6019e05bbacSSakari Ailus 		      clk_div_even(pll->ext_clk_freq_hz /
602415ddd99SSakari Ailus 				   op_lim_fr->min_pll_ip_clk_freq_hz));
603415ddd99SSakari Ailus 	min_op_pre_pll_clk_div =
604415ddd99SSakari Ailus 		max_t(uint16_t, op_lim_fr->min_pre_pll_clk_div,
6059e05bbacSSakari Ailus 		      clk_div_even_up(
6069e05bbacSSakari Ailus 			      DIV_ROUND_UP(pll->ext_clk_freq_hz,
607415ddd99SSakari Ailus 					   op_lim_fr->max_pll_ip_clk_freq_hz)));
608415ddd99SSakari Ailus 	dev_dbg(dev, "pre-pll check: min / max op_pre_pll_clk_div: %u / %u\n",
609415ddd99SSakari Ailus 		min_op_pre_pll_clk_div, max_op_pre_pll_clk_div);
6109e05bbacSSakari Ailus 
611cab27256SSakari Ailus 	i = gcd(op_pll_bk->sys_clk_freq_hz, pll->ext_clk_freq_hz);
612cab27256SSakari Ailus 	mul = op_pll_bk->sys_clk_freq_hz / i;
6139e05bbacSSakari Ailus 	div = pll->ext_clk_freq_hz / i;
6149e05bbacSSakari Ailus 	dev_dbg(dev, "mul %u / div %u\n", mul, div);
6159e05bbacSSakari Ailus 
616415ddd99SSakari Ailus 	min_op_pre_pll_clk_div =
617415ddd99SSakari Ailus 		max_t(uint16_t, min_op_pre_pll_clk_div,
6189e05bbacSSakari Ailus 		      clk_div_even_up(
619482e75e7SSakari Ailus 			      mul /
620482e75e7SSakari Ailus 			      one_or_more(
621482e75e7SSakari Ailus 				      DIV_ROUND_UP(op_lim_fr->max_pll_op_clk_freq_hz,
622482e75e7SSakari Ailus 						   pll->ext_clk_freq_hz))));
623415ddd99SSakari Ailus 	dev_dbg(dev, "pll_op check: min / max op_pre_pll_clk_div: %u / %u\n",
624415ddd99SSakari Ailus 		min_op_pre_pll_clk_div, max_op_pre_pll_clk_div);
6259e05bbacSSakari Ailus 
626415ddd99SSakari Ailus 	for (op_pll_fr->pre_pll_clk_div = min_op_pre_pll_clk_div;
627415ddd99SSakari Ailus 	     op_pll_fr->pre_pll_clk_div <= max_op_pre_pll_clk_div;
6284e1e8d24SSakari Ailus 	     op_pll_fr->pre_pll_clk_div +=
6294e1e8d24SSakari Ailus 		     (pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER) ? 1 :
6304e1e8d24SSakari Ailus 		     2 - (op_pll_fr->pre_pll_clk_div & 1)) {
631a38836b2SSakari Ailus 		rval = ccs_pll_calculate_op(dev, lim, op_lim_fr, op_lim_bk, pll,
6328030aa4fSSakari Ailus 					    op_pll_fr, op_pll_bk, mul, div, l,
6338030aa4fSSakari Ailus 					    cphy, phy_const);
6349e05bbacSSakari Ailus 		if (rval)
6359e05bbacSSakari Ailus 			continue;
6369e05bbacSSakari Ailus 
637*f25d3962SSakari Ailus 		rval = check_fr_bounds(dev, lim, pll, PLL_VT);
638*f25d3962SSakari Ailus 		if (rval)
639*f25d3962SSakari Ailus 			continue;
640*f25d3962SSakari Ailus 
641*f25d3962SSakari Ailus 		rval = check_bk_bounds(dev, lim, pll, PLL_OP);
642*f25d3962SSakari Ailus 		if (rval)
643*f25d3962SSakari Ailus 			continue;
644*f25d3962SSakari Ailus 
645a38836b2SSakari Ailus 		ccs_pll_calculate_vt(dev, lim, op_lim_bk, pll, op_pll_fr,
646a38836b2SSakari Ailus 				     op_pll_bk, cphy, phy_const);
647a38836b2SSakari Ailus 
648*f25d3962SSakari Ailus 		rval = check_bk_bounds(dev, lim, pll, PLL_VT);
649*f25d3962SSakari Ailus 		if (rval)
650*f25d3962SSakari Ailus 			continue;
651*f25d3962SSakari Ailus 		rval = check_ext_bounds(dev, pll);
652a38836b2SSakari Ailus 		if (rval)
653a38836b2SSakari Ailus 			continue;
654a38836b2SSakari Ailus 
6559e05bbacSSakari Ailus 		print_pll(dev, pll);
656a38836b2SSakari Ailus 
6579e05bbacSSakari Ailus 		return 0;
6589e05bbacSSakari Ailus 	}
6599e05bbacSSakari Ailus 
6609e05bbacSSakari Ailus 	dev_dbg(dev, "unable to compute pre_pll divisor\n");
6619e05bbacSSakari Ailus 
6629e05bbacSSakari Ailus 	return rval;
6639e05bbacSSakari Ailus }
6649e05bbacSSakari Ailus EXPORT_SYMBOL_GPL(ccs_pll_calculate);
6659e05bbacSSakari Ailus 
6667389d01cSSakari Ailus MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
6679e05bbacSSakari Ailus MODULE_DESCRIPTION("Generic MIPI CCS/SMIA/SMIA++ PLL calculator");
668b3c0115eSSakari Ailus MODULE_LICENSE("GPL v2");
669