xref: /openbmc/linux/drivers/media/i2c/ccs-pll.c (revision 900c33e86e4b53e96e6ea10e9737870e03911a66)
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,
49f25d3962SSakari Ailus 			uint32_t min, uint32_t max, const char *prefix,
50f25d3962SSakari Ailus 			char *str)
519e05bbacSSakari Ailus {
529e05bbacSSakari Ailus 	if (val >= min && val <= max)
539e05bbacSSakari Ailus 		return 0;
549e05bbacSSakari Ailus 
55f25d3962SSakari Ailus 	dev_dbg(dev, "%s_%s out of bounds: %d (%d--%d)\n", prefix,
56f25d3962SSakari 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 },
86f25d3962SSakari 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 
956c7469e4SSakari Ailus 		if (pll->flags & CCS_PLL_FLAG_DUAL_PLL ||
966c7469e4SSakari Ailus 		    br->which == PLL_VT) {
97fadfe884SSakari Ailus 			dev_dbg(dev, "%s_pre_pll_clk_div\t\t%u\n",  s,
98fadfe884SSakari Ailus 				br->fr->pre_pll_clk_div);
99fadfe884SSakari Ailus 			dev_dbg(dev, "%s_pll_multiplier\t\t%u\n",  s,
100fadfe884SSakari Ailus 				br->fr->pll_multiplier);
101fadfe884SSakari Ailus 
102fadfe884SSakari Ailus 			dev_dbg(dev, "%s_pll_ip_clk_freq_hz\t%u\n", s,
103fadfe884SSakari Ailus 				br->fr->pll_ip_clk_freq_hz);
104fadfe884SSakari Ailus 			dev_dbg(dev, "%s_pll_op_clk_freq_hz\t%u\n", s,
105fadfe884SSakari Ailus 				br->fr->pll_op_clk_freq_hz);
1069e05bbacSSakari Ailus 		}
107fadfe884SSakari Ailus 
108fadfe884SSakari Ailus 		if (!(pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) ||
109fadfe884SSakari Ailus 		    br->which == PLL_VT) {
110fadfe884SSakari Ailus 			dev_dbg(dev, "%s_sys_clk_div\t\t%u\n",  s,
111fadfe884SSakari Ailus 				br->bk->sys_clk_div);
112fadfe884SSakari Ailus 			dev_dbg(dev, "%s_pix_clk_div\t\t%u\n", s,
113fadfe884SSakari Ailus 				br->bk->pix_clk_div);
114fadfe884SSakari Ailus 
115fadfe884SSakari Ailus 			dev_dbg(dev, "%s_sys_clk_freq_hz\t%u\n", s,
116fadfe884SSakari Ailus 				br->bk->sys_clk_freq_hz);
117fadfe884SSakari Ailus 			dev_dbg(dev, "%s_pix_clk_freq_hz\t%u\n", s,
118fadfe884SSakari Ailus 				br->bk->pix_clk_freq_hz);
119fadfe884SSakari Ailus 		}
120fadfe884SSakari Ailus 	}
121fadfe884SSakari Ailus 
122*900c33e8SSakari Ailus 	dev_dbg(dev, "flags%s%s%s%s%s%s%s%s%s\n",
123fadfe884SSakari Ailus 		pll->flags & PLL_FL(LANE_SPEED_MODEL) ? " lane-speed" : "",
124fadfe884SSakari Ailus 		pll->flags & PLL_FL(LINK_DECOUPLED) ? " link-decoupled" : "",
125fadfe884SSakari Ailus 		pll->flags & PLL_FL(EXT_IP_PLL_DIVIDER) ?
126fadfe884SSakari Ailus 		" ext-ip-pll-divider" : "",
127fadfe884SSakari Ailus 		pll->flags & PLL_FL(FLEXIBLE_OP_PIX_CLK_DIV) ?
128fadfe884SSakari Ailus 		" flexible-op-pix-div" : "",
129fadfe884SSakari Ailus 		pll->flags & PLL_FL(FIFO_DERATING) ? " fifo-derating" : "",
1306c7469e4SSakari Ailus 		pll->flags & PLL_FL(FIFO_OVERRATING) ? " fifo-overrating" : "",
131*900c33e8SSakari Ailus 		pll->flags & PLL_FL(DUAL_PLL) ? " dual-pll" : "",
132*900c33e8SSakari Ailus 		pll->flags & PLL_FL(OP_SYS_DDR) ? " op-sys-ddr" : "",
133*900c33e8SSakari Ailus 		pll->flags & PLL_FL(OP_PIX_DDR) ? " op-pix-ddr" : "");
134*900c33e8SSakari Ailus }
135*900c33e8SSakari Ailus 
136*900c33e8SSakari Ailus static uint32_t op_sys_ddr(uint32_t flags)
137*900c33e8SSakari Ailus {
138*900c33e8SSakari Ailus 	return flags & CCS_PLL_FLAG_OP_SYS_DDR ? 1 : 0;
139*900c33e8SSakari Ailus }
140*900c33e8SSakari Ailus 
141*900c33e8SSakari Ailus static uint32_t op_pix_ddr(uint32_t flags)
142*900c33e8SSakari Ailus {
143*900c33e8SSakari Ailus 	return flags & CCS_PLL_FLAG_OP_PIX_DDR ? 1 : 0;
1449e05bbacSSakari Ailus }
1459e05bbacSSakari Ailus 
146f25d3962SSakari Ailus static int check_fr_bounds(struct device *dev,
147415ddd99SSakari Ailus 			   const struct ccs_pll_limits *lim,
148f25d3962SSakari Ailus 			   struct ccs_pll *pll, unsigned int which)
1499e05bbacSSakari Ailus {
150f25d3962SSakari Ailus 	const struct ccs_pll_branch_limits_fr *lim_fr;
151f25d3962SSakari Ailus 	struct ccs_pll_branch_fr *pll_fr;
152f25d3962SSakari Ailus 	const char *s = pll_string(which);
1539e05bbacSSakari Ailus 	int rval;
1549e05bbacSSakari Ailus 
155f25d3962SSakari Ailus 	if (which == PLL_OP) {
156f25d3962SSakari Ailus 		lim_fr = &lim->op_fr;
157f25d3962SSakari Ailus 		pll_fr = &pll->op_fr;
158f25d3962SSakari Ailus 	} else {
159f25d3962SSakari Ailus 		lim_fr = &lim->vt_fr;
160f25d3962SSakari Ailus 		pll_fr = &pll->vt_fr;
161f25d3962SSakari Ailus 	}
1629e05bbacSSakari Ailus 
163f25d3962SSakari Ailus 	rval = bounds_check(dev, pll_fr->pre_pll_clk_div,
164f25d3962SSakari Ailus 			    lim_fr->min_pre_pll_clk_div,
165f25d3962SSakari Ailus 			    lim_fr->max_pre_pll_clk_div, s, "pre_pll_clk_div");
166f25d3962SSakari Ailus 
167f25d3962SSakari Ailus 	if (!rval)
168f25d3962SSakari Ailus 		rval = bounds_check(dev, pll_fr->pll_ip_clk_freq_hz,
169f25d3962SSakari Ailus 				    lim_fr->min_pll_ip_clk_freq_hz,
170f25d3962SSakari Ailus 				    lim_fr->max_pll_ip_clk_freq_hz,
171f25d3962SSakari Ailus 				    s, "pll_ip_clk_freq_hz");
172f25d3962SSakari Ailus 	if (!rval)
173f25d3962SSakari Ailus 		rval = bounds_check(dev, pll_fr->pll_multiplier,
174f25d3962SSakari Ailus 				    lim_fr->min_pll_multiplier,
175f25d3962SSakari Ailus 				    lim_fr->max_pll_multiplier,
176f25d3962SSakari Ailus 				    s, "pll_multiplier");
177f25d3962SSakari Ailus 	if (!rval)
178f25d3962SSakari Ailus 		rval = bounds_check(dev, pll_fr->pll_op_clk_freq_hz,
179f25d3962SSakari Ailus 				    lim_fr->min_pll_op_clk_freq_hz,
180f25d3962SSakari Ailus 				    lim_fr->max_pll_op_clk_freq_hz,
181f25d3962SSakari Ailus 				    s, "pll_op_clk_freq_hz");
182f25d3962SSakari Ailus 
1839e05bbacSSakari Ailus 	return rval;
184f25d3962SSakari Ailus }
1859e05bbacSSakari Ailus 
186f25d3962SSakari Ailus static int check_bk_bounds(struct device *dev,
187f25d3962SSakari Ailus 			   const struct ccs_pll_limits *lim,
188f25d3962SSakari Ailus 			   struct ccs_pll *pll, unsigned int which)
189f25d3962SSakari Ailus {
190f25d3962SSakari Ailus 	const struct ccs_pll_branch_limits_bk *lim_bk;
191f25d3962SSakari Ailus 	struct ccs_pll_branch_bk *pll_bk;
192f25d3962SSakari Ailus 	const char *s = pll_string(which);
193f25d3962SSakari Ailus 	int rval;
1949e05bbacSSakari Ailus 
195f25d3962SSakari Ailus 	if (which == PLL_OP) {
196f25d3962SSakari Ailus 		if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS)
197f25d3962SSakari Ailus 			return 0;
198f25d3962SSakari Ailus 
199f25d3962SSakari Ailus 		lim_bk = &lim->op_bk;
200f25d3962SSakari Ailus 		pll_bk = &pll->op_bk;
201f25d3962SSakari Ailus 	} else {
202f25d3962SSakari Ailus 		lim_bk = &lim->vt_bk;
203f25d3962SSakari Ailus 		pll_bk = &pll->vt_bk;
204f25d3962SSakari Ailus 	}
205f25d3962SSakari Ailus 
206f25d3962SSakari Ailus 	rval = bounds_check(dev, pll_bk->sys_clk_div,
207f25d3962SSakari Ailus 			    lim_bk->min_sys_clk_div,
208f25d3962SSakari Ailus 			    lim_bk->max_sys_clk_div, s, "op_sys_clk_div");
209f25d3962SSakari Ailus 	if (!rval)
210f25d3962SSakari Ailus 		rval = bounds_check(dev, pll_bk->sys_clk_freq_hz,
211f25d3962SSakari Ailus 				    lim_bk->min_sys_clk_freq_hz,
212f25d3962SSakari Ailus 				    lim_bk->max_sys_clk_freq_hz,
213f25d3962SSakari Ailus 				    s, "sys_clk_freq_hz");
214f25d3962SSakari Ailus 	if (!rval)
215f25d3962SSakari Ailus 		rval = bounds_check(dev, pll_bk->sys_clk_div,
216f25d3962SSakari Ailus 				    lim_bk->min_sys_clk_div,
217f25d3962SSakari Ailus 				    lim_bk->max_sys_clk_div,
218f25d3962SSakari Ailus 				    s, "sys_clk_div");
219f25d3962SSakari Ailus 	if (!rval)
220f25d3962SSakari Ailus 		rval = bounds_check(dev, pll_bk->pix_clk_freq_hz,
221f25d3962SSakari Ailus 				    lim_bk->min_pix_clk_freq_hz,
222f25d3962SSakari Ailus 				    lim_bk->max_pix_clk_freq_hz,
223f25d3962SSakari Ailus 				    s, "pix_clk_freq_hz");
224f25d3962SSakari Ailus 
225f25d3962SSakari Ailus 	return rval;
226f25d3962SSakari Ailus }
227f25d3962SSakari Ailus 
228f25d3962SSakari Ailus static int check_ext_bounds(struct device *dev, struct ccs_pll *pll)
229f25d3962SSakari Ailus {
23038c94eb8SSakari Ailus 	if (!(pll->flags & CCS_PLL_FLAG_FIFO_DERATING) &&
23138c94eb8SSakari Ailus 	    pll->pixel_rate_pixel_array > pll->pixel_rate_csi) {
23238c94eb8SSakari Ailus 		dev_dbg(dev, "device does not support derating\n");
23338c94eb8SSakari Ailus 		return -EINVAL;
23438c94eb8SSakari Ailus 	}
23538c94eb8SSakari Ailus 
23638c94eb8SSakari Ailus 	if (!(pll->flags & CCS_PLL_FLAG_FIFO_OVERRATING) &&
23738c94eb8SSakari Ailus 	    pll->pixel_rate_pixel_array < pll->pixel_rate_csi) {
23838c94eb8SSakari Ailus 		dev_dbg(dev, "device does not support overrating\n");
23938c94eb8SSakari Ailus 		return -EINVAL;
24038c94eb8SSakari Ailus 	}
24138c94eb8SSakari Ailus 
242f25d3962SSakari Ailus 	return 0;
2439e05bbacSSakari Ailus }
2449e05bbacSSakari Ailus 
2459ec6e5b1SSakari Ailus static void
2469ec6e5b1SSakari Ailus ccs_pll_find_vt_sys_div(struct device *dev, const struct ccs_pll_limits *lim,
2479ec6e5b1SSakari Ailus 			struct ccs_pll *pll, struct ccs_pll_branch_fr *pll_fr,
2489ec6e5b1SSakari Ailus 			uint16_t min_vt_div, uint16_t max_vt_div,
2499ec6e5b1SSakari Ailus 			uint16_t *min_sys_div, uint16_t *max_sys_div)
2509ec6e5b1SSakari Ailus {
2519ec6e5b1SSakari Ailus 	/*
2529ec6e5b1SSakari Ailus 	 * Find limits for sys_clk_div. Not all values are possible with all
2539ec6e5b1SSakari Ailus 	 * values of pix_clk_div.
2549ec6e5b1SSakari Ailus 	 */
2559ec6e5b1SSakari Ailus 	*min_sys_div = lim->vt_bk.min_sys_clk_div;
2569ec6e5b1SSakari Ailus 	dev_dbg(dev, "min_sys_div: %u\n", *min_sys_div);
2579ec6e5b1SSakari Ailus 	*min_sys_div = max_t(uint16_t, *min_sys_div,
2589ec6e5b1SSakari Ailus 			     DIV_ROUND_UP(min_vt_div,
2599ec6e5b1SSakari Ailus 					  lim->vt_bk.max_pix_clk_div));
2609ec6e5b1SSakari Ailus 	dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %u\n", *min_sys_div);
2619ec6e5b1SSakari Ailus 	*min_sys_div = max_t(uint16_t, *min_sys_div,
2629ec6e5b1SSakari Ailus 			     pll_fr->pll_op_clk_freq_hz
2639ec6e5b1SSakari Ailus 			     / lim->vt_bk.max_sys_clk_freq_hz);
2649ec6e5b1SSakari Ailus 	dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %u\n", *min_sys_div);
2659ec6e5b1SSakari Ailus 	*min_sys_div = clk_div_even_up(*min_sys_div);
2669ec6e5b1SSakari Ailus 	dev_dbg(dev, "min_sys_div: one or even: %u\n", *min_sys_div);
2679ec6e5b1SSakari Ailus 
2689ec6e5b1SSakari Ailus 	*max_sys_div = lim->vt_bk.max_sys_clk_div;
2699ec6e5b1SSakari Ailus 	dev_dbg(dev, "max_sys_div: %u\n", *max_sys_div);
2709ec6e5b1SSakari Ailus 	*max_sys_div = min_t(uint16_t, *max_sys_div,
2719ec6e5b1SSakari Ailus 			     DIV_ROUND_UP(max_vt_div,
2729ec6e5b1SSakari Ailus 					  lim->vt_bk.min_pix_clk_div));
2739ec6e5b1SSakari Ailus 	dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %u\n", *max_sys_div);
2749ec6e5b1SSakari Ailus 	*max_sys_div = min_t(uint16_t, *max_sys_div,
2759ec6e5b1SSakari Ailus 			     DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz,
2769ec6e5b1SSakari Ailus 					  lim->vt_bk.min_pix_clk_freq_hz));
2779ec6e5b1SSakari Ailus 	dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %u\n", *max_sys_div);
2789ec6e5b1SSakari Ailus }
2799ec6e5b1SSakari Ailus 
2808030aa4fSSakari Ailus #define CPHY_CONST		7
2818030aa4fSSakari Ailus #define DPHY_CONST		16
2828030aa4fSSakari Ailus #define PHY_CONST_DIV		16
2838030aa4fSSakari Ailus 
2846c7469e4SSakari Ailus static inline int
2856c7469e4SSakari Ailus __ccs_pll_calculate_vt_tree(struct device *dev,
2866c7469e4SSakari Ailus 			    const struct ccs_pll_limits *lim,
2876c7469e4SSakari Ailus 			    struct ccs_pll *pll, uint32_t mul, uint32_t div)
2886c7469e4SSakari Ailus {
2896c7469e4SSakari Ailus 	const struct ccs_pll_branch_limits_fr *lim_fr = &lim->vt_fr;
2906c7469e4SSakari Ailus 	const struct ccs_pll_branch_limits_bk *lim_bk = &lim->vt_bk;
2916c7469e4SSakari Ailus 	struct ccs_pll_branch_fr *pll_fr = &pll->vt_fr;
2926c7469e4SSakari Ailus 	struct ccs_pll_branch_bk *pll_bk = &pll->vt_bk;
2936c7469e4SSakari Ailus 	uint32_t more_mul;
2946c7469e4SSakari Ailus 	uint16_t best_pix_div = SHRT_MAX >> 1, best_div;
2956c7469e4SSakari Ailus 	uint16_t vt_div, min_sys_div, max_sys_div, sys_div;
2966c7469e4SSakari Ailus 
2976c7469e4SSakari Ailus 	pll_fr->pll_ip_clk_freq_hz =
2986c7469e4SSakari Ailus 		pll->ext_clk_freq_hz / pll_fr->pre_pll_clk_div;
2996c7469e4SSakari Ailus 
3006c7469e4SSakari Ailus 	dev_dbg(dev, "vt_pll_ip_clk_freq_hz %u\n", pll_fr->pll_ip_clk_freq_hz);
3016c7469e4SSakari Ailus 
3026c7469e4SSakari Ailus 	more_mul = one_or_more(DIV_ROUND_UP(lim_fr->min_pll_op_clk_freq_hz,
3036c7469e4SSakari Ailus 					    pll_fr->pll_ip_clk_freq_hz * mul));
3046c7469e4SSakari Ailus 
3056c7469e4SSakari Ailus 	dev_dbg(dev, "more_mul: %u\n", more_mul);
3066c7469e4SSakari Ailus 	more_mul *= DIV_ROUND_UP(lim_fr->min_pll_multiplier, mul * more_mul);
3076c7469e4SSakari Ailus 	dev_dbg(dev, "more_mul2: %u\n", more_mul);
3086c7469e4SSakari Ailus 
3096c7469e4SSakari Ailus 	pll_fr->pll_multiplier = mul * more_mul;
3106c7469e4SSakari Ailus 
3116c7469e4SSakari Ailus 	if (pll_fr->pll_multiplier * pll_fr->pll_ip_clk_freq_hz >
3126c7469e4SSakari Ailus 	    lim_fr->max_pll_op_clk_freq_hz)
3136c7469e4SSakari Ailus 		return -EINVAL;
3146c7469e4SSakari Ailus 
3156c7469e4SSakari Ailus 	pll_fr->pll_op_clk_freq_hz =
3166c7469e4SSakari Ailus 		pll_fr->pll_ip_clk_freq_hz * pll_fr->pll_multiplier;
3176c7469e4SSakari Ailus 
3186c7469e4SSakari Ailus 	vt_div = div * more_mul;
3196c7469e4SSakari Ailus 
3206c7469e4SSakari Ailus 	ccs_pll_find_vt_sys_div(dev, lim, pll, pll_fr, vt_div, vt_div,
3216c7469e4SSakari Ailus 				&min_sys_div, &max_sys_div);
3226c7469e4SSakari Ailus 
3236c7469e4SSakari Ailus 	max_sys_div = (vt_div & 1) ? 1 : max_sys_div;
3246c7469e4SSakari Ailus 
3256c7469e4SSakari Ailus 	dev_dbg(dev, "vt min/max_sys_div: %u,%u\n", min_sys_div, max_sys_div);
3266c7469e4SSakari Ailus 
3276c7469e4SSakari Ailus 	for (sys_div = min_sys_div; sys_div <= max_sys_div;
3286c7469e4SSakari Ailus 	     sys_div += 2 - (sys_div & 1)) {
3296c7469e4SSakari Ailus 		uint16_t pix_div;
3306c7469e4SSakari Ailus 
3316c7469e4SSakari Ailus 		if (vt_div % sys_div)
3326c7469e4SSakari Ailus 			continue;
3336c7469e4SSakari Ailus 
3346c7469e4SSakari Ailus 		pix_div = vt_div / sys_div;
3356c7469e4SSakari Ailus 
3366c7469e4SSakari Ailus 		if (pix_div < lim_bk->min_pix_clk_div ||
3376c7469e4SSakari Ailus 		    pix_div > lim_bk->max_pix_clk_div) {
3386c7469e4SSakari Ailus 			dev_dbg(dev,
3396c7469e4SSakari Ailus 				"pix_div %u too small or too big (%u--%u)\n",
3406c7469e4SSakari Ailus 				pix_div,
3416c7469e4SSakari Ailus 				lim_bk->min_pix_clk_div,
3426c7469e4SSakari Ailus 				lim_bk->max_pix_clk_div);
3436c7469e4SSakari Ailus 			continue;
3446c7469e4SSakari Ailus 		}
3456c7469e4SSakari Ailus 
346b41f2708SSakari Ailus 		dev_dbg(dev, "sys/pix/best_pix: %u,%u,%u\n", sys_div, pix_div,
347b41f2708SSakari Ailus 			best_pix_div);
348b41f2708SSakari Ailus 
349b41f2708SSakari Ailus 		if (pix_div * sys_div <= best_pix_div) {
3506c7469e4SSakari Ailus 			best_pix_div = pix_div;
3516c7469e4SSakari Ailus 			best_div = pix_div * sys_div;
3526c7469e4SSakari Ailus 		}
3536c7469e4SSakari Ailus 	}
3546c7469e4SSakari Ailus 	if (best_pix_div == SHRT_MAX >> 1)
3556c7469e4SSakari Ailus 		return -EINVAL;
3566c7469e4SSakari Ailus 
3576c7469e4SSakari Ailus 	pll_bk->sys_clk_div = best_div / best_pix_div;
3586c7469e4SSakari Ailus 	pll_bk->pix_clk_div = best_pix_div;
3596c7469e4SSakari Ailus 
3606c7469e4SSakari Ailus 	pll_bk->sys_clk_freq_hz =
3616c7469e4SSakari Ailus 		pll_fr->pll_op_clk_freq_hz / pll_bk->sys_clk_div;
3626c7469e4SSakari Ailus 	pll_bk->pix_clk_freq_hz =
3636c7469e4SSakari Ailus 		pll_bk->sys_clk_freq_hz / pll_bk->pix_clk_div;
3646c7469e4SSakari Ailus 
3656c7469e4SSakari Ailus 	pll->pixel_rate_pixel_array =
3666c7469e4SSakari Ailus 		pll_bk->pix_clk_freq_hz * pll->vt_lanes;
3676c7469e4SSakari Ailus 
3686c7469e4SSakari Ailus 	return 0;
3696c7469e4SSakari Ailus }
3706c7469e4SSakari Ailus 
3716c7469e4SSakari Ailus static int ccs_pll_calculate_vt_tree(struct device *dev,
3726c7469e4SSakari Ailus 				     const struct ccs_pll_limits *lim,
3736c7469e4SSakari Ailus 				     struct ccs_pll *pll)
3746c7469e4SSakari Ailus {
3756c7469e4SSakari Ailus 	const struct ccs_pll_branch_limits_fr *lim_fr = &lim->vt_fr;
3766c7469e4SSakari Ailus 	struct ccs_pll_branch_fr *pll_fr = &pll->vt_fr;
3776c7469e4SSakari Ailus 	uint16_t min_pre_pll_clk_div = lim_fr->min_pre_pll_clk_div;
3786c7469e4SSakari Ailus 	uint16_t max_pre_pll_clk_div = lim_fr->max_pre_pll_clk_div;
3796c7469e4SSakari Ailus 	uint32_t pre_mul, pre_div;
3806c7469e4SSakari Ailus 
3816c7469e4SSakari Ailus 	pre_div = gcd(pll->pixel_rate_csi,
3826c7469e4SSakari Ailus 		      pll->ext_clk_freq_hz * pll->vt_lanes);
3836c7469e4SSakari Ailus 	pre_mul = pll->pixel_rate_csi / pre_div;
3846c7469e4SSakari Ailus 	pre_div = pll->ext_clk_freq_hz * pll->vt_lanes / pre_div;
3856c7469e4SSakari Ailus 
3866c7469e4SSakari Ailus 	/* Make sure PLL input frequency is within limits */
3876c7469e4SSakari Ailus 	max_pre_pll_clk_div =
3886c7469e4SSakari Ailus 		min_t(uint16_t, max_pre_pll_clk_div,
3896c7469e4SSakari Ailus 		      DIV_ROUND_UP(pll->ext_clk_freq_hz,
3906c7469e4SSakari Ailus 				   lim_fr->min_pll_ip_clk_freq_hz));
3916c7469e4SSakari Ailus 
3926c7469e4SSakari Ailus 	min_pre_pll_clk_div = max_t(uint16_t, min_pre_pll_clk_div,
3936c7469e4SSakari Ailus 				    pll->ext_clk_freq_hz /
3946c7469e4SSakari Ailus 				    lim_fr->max_pll_ip_clk_freq_hz);
3956c7469e4SSakari Ailus 
3966c7469e4SSakari Ailus 	dev_dbg(dev, "vt min/max_pre_pll_clk_div: %u,%u\n",
3976c7469e4SSakari Ailus 		min_pre_pll_clk_div, max_pre_pll_clk_div);
3986c7469e4SSakari Ailus 
3996c7469e4SSakari Ailus 	for (pll_fr->pre_pll_clk_div = min_pre_pll_clk_div;
4006c7469e4SSakari Ailus 	     pll_fr->pre_pll_clk_div <= max_pre_pll_clk_div;
4016c7469e4SSakari Ailus 	     pll_fr->pre_pll_clk_div +=
4026c7469e4SSakari Ailus 		     (pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER) ? 1 :
4036c7469e4SSakari Ailus 		     2 - (pll_fr->pre_pll_clk_div & 1)) {
4046c7469e4SSakari Ailus 		uint32_t mul, div;
4056c7469e4SSakari Ailus 		int rval;
4066c7469e4SSakari Ailus 
4076c7469e4SSakari Ailus 		div = gcd(pre_mul * pll_fr->pre_pll_clk_div, pre_div);
4086c7469e4SSakari Ailus 		mul = pre_mul * pll_fr->pre_pll_clk_div / div;
4096c7469e4SSakari Ailus 		div = pre_div / div;
4106c7469e4SSakari Ailus 
4116c7469e4SSakari Ailus 		dev_dbg(dev, "vt pre-div/mul/div: %u,%u,%u\n",
4126c7469e4SSakari Ailus 			pll_fr->pre_pll_clk_div, mul, div);
4136c7469e4SSakari Ailus 
4146c7469e4SSakari Ailus 		rval = __ccs_pll_calculate_vt_tree(dev, lim, pll,
4156c7469e4SSakari Ailus 						   mul, div);
4166c7469e4SSakari Ailus 		if (rval)
4176c7469e4SSakari Ailus 			continue;
4186c7469e4SSakari Ailus 
4196c7469e4SSakari Ailus 		rval = check_fr_bounds(dev, lim, pll, PLL_VT);
4206c7469e4SSakari Ailus 		if (rval)
4216c7469e4SSakari Ailus 			continue;
4226c7469e4SSakari Ailus 
4236c7469e4SSakari Ailus 		rval = check_bk_bounds(dev, lim, pll, PLL_VT);
4246c7469e4SSakari Ailus 		if (rval)
4256c7469e4SSakari Ailus 			continue;
4266c7469e4SSakari Ailus 
4276c7469e4SSakari Ailus 		return 0;
4286c7469e4SSakari Ailus 	}
4296c7469e4SSakari Ailus 
4306c7469e4SSakari Ailus 	return -EINVAL;
4316c7469e4SSakari Ailus }
4326c7469e4SSakari Ailus 
4333e2db036SSakari Ailus static void
434a38836b2SSakari Ailus ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim,
4353e2db036SSakari Ailus 		     const struct ccs_pll_branch_limits_bk *op_lim_bk,
4363e2db036SSakari Ailus 		     struct ccs_pll *pll, struct ccs_pll_branch_fr *pll_fr,
4373e2db036SSakari Ailus 		     struct ccs_pll_branch_bk *op_pll_bk, bool cphy,
4383e2db036SSakari Ailus 		     uint32_t phy_const)
4393e2db036SSakari Ailus {
440594f1e93SSakari Ailus 	uint16_t sys_div;
441594f1e93SSakari Ailus 	uint16_t best_pix_div = SHRT_MAX >> 1;
442594f1e93SSakari Ailus 	uint16_t vt_op_binning_div;
443594f1e93SSakari Ailus 	uint16_t min_vt_div, max_vt_div, vt_div;
444594f1e93SSakari Ailus 	uint16_t min_sys_div, max_sys_div;
4453e2db036SSakari Ailus 
446a38836b2SSakari Ailus 	if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS)
447a38836b2SSakari Ailus 		goto out_calc_pixel_rate;
448a38836b2SSakari Ailus 
4493e2db036SSakari Ailus 	/*
45038c94eb8SSakari Ailus 	 * Find out whether a sensor supports derating. If it does not, VT and
45138c94eb8SSakari Ailus 	 * OP domains are required to run at the same pixel rate.
45238c94eb8SSakari Ailus 	 */
45338c94eb8SSakari Ailus 	if (!(pll->flags & CCS_PLL_FLAG_FIFO_DERATING)) {
45438c94eb8SSakari Ailus 		min_vt_div =
45538c94eb8SSakari Ailus 			op_pll_bk->sys_clk_div * op_pll_bk->pix_clk_div
456*900c33e8SSakari Ailus 			* pll->vt_lanes * phy_const / pll->op_lanes
457*900c33e8SSakari Ailus 			/ (PHY_CONST_DIV << op_pix_ddr(pll->flags));
45838c94eb8SSakari Ailus 	} else {
45938c94eb8SSakari Ailus 		/*
4603e2db036SSakari Ailus 		 * Some sensors perform analogue binning and some do this
4613e2db036SSakari Ailus 		 * digitally. The ones doing this digitally can be roughly be
4623e2db036SSakari Ailus 		 * found out using this formula. The ones doing this digitally
4633e2db036SSakari Ailus 		 * should run at higher clock rate, so smaller divisor is used
4643e2db036SSakari Ailus 		 * on video timing side.
4653e2db036SSakari Ailus 		 */
4663e2db036SSakari Ailus 		if (lim->min_line_length_pck_bin > lim->min_line_length_pck
4673e2db036SSakari Ailus 		    / pll->binning_horizontal)
4683e2db036SSakari Ailus 			vt_op_binning_div = pll->binning_horizontal;
4693e2db036SSakari Ailus 		else
4703e2db036SSakari Ailus 			vt_op_binning_div = 1;
4713e2db036SSakari Ailus 		dev_dbg(dev, "vt_op_binning_div: %u\n", vt_op_binning_div);
4723e2db036SSakari Ailus 
4733e2db036SSakari Ailus 		/*
4743e2db036SSakari Ailus 		 * Profile 2 supports vt_pix_clk_div E [4, 10]
4753e2db036SSakari Ailus 		 *
4763e2db036SSakari Ailus 		 * Horizontal binning can be used as a base for difference in
4773e2db036SSakari Ailus 		 * divisors. One must make sure that horizontal blanking is
4783e2db036SSakari Ailus 		 * enough to accommodate the CSI-2 sync codes.
4793e2db036SSakari Ailus 		 *
4803e2db036SSakari Ailus 		 * Take scaling factor and number of VT lanes into account as well.
4813e2db036SSakari Ailus 		 *
4823e2db036SSakari Ailus 		 * Find absolute limits for the factor of vt divider.
4833e2db036SSakari Ailus 		 */
4843e2db036SSakari Ailus 		dev_dbg(dev, "scale_m: %u\n", pll->scale_m);
48538c94eb8SSakari Ailus 		min_vt_div =
48638c94eb8SSakari Ailus 			DIV_ROUND_UP(pll->bits_per_pixel
48738c94eb8SSakari Ailus 				     * op_pll_bk->sys_clk_div * pll->scale_n
48838c94eb8SSakari Ailus 				     * pll->vt_lanes * phy_const,
48938c94eb8SSakari Ailus 				     (pll->flags &
49038c94eb8SSakari Ailus 				      CCS_PLL_FLAG_LANE_SPEED_MODEL ?
4913e2db036SSakari Ailus 				      pll->csi2.lanes : 1)
4923e2db036SSakari Ailus 				     * vt_op_binning_div * pll->scale_m
493*900c33e8SSakari Ailus 				     * PHY_CONST_DIV << op_pix_ddr(pll->flags));
49438c94eb8SSakari Ailus 	}
4953e2db036SSakari Ailus 
4963e2db036SSakari Ailus 	/* Find smallest and biggest allowed vt divisor. */
4973e2db036SSakari Ailus 	dev_dbg(dev, "min_vt_div: %u\n", min_vt_div);
498594f1e93SSakari Ailus 	min_vt_div = max_t(uint16_t, min_vt_div,
4993e2db036SSakari Ailus 			   DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz,
5003e2db036SSakari Ailus 					lim->vt_bk.max_pix_clk_freq_hz));
5013e2db036SSakari Ailus 	dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %u\n",
5023e2db036SSakari Ailus 		min_vt_div);
503594f1e93SSakari Ailus 	min_vt_div = max_t(uint16_t, min_vt_div, lim->vt_bk.min_pix_clk_div
5043e2db036SSakari Ailus 						 * lim->vt_bk.min_sys_clk_div);
5053e2db036SSakari Ailus 	dev_dbg(dev, "min_vt_div: min_vt_clk_div: %u\n", min_vt_div);
5063e2db036SSakari Ailus 
5073e2db036SSakari Ailus 	max_vt_div = lim->vt_bk.max_sys_clk_div * lim->vt_bk.max_pix_clk_div;
5083e2db036SSakari Ailus 	dev_dbg(dev, "max_vt_div: %u\n", max_vt_div);
509594f1e93SSakari Ailus 	max_vt_div = min_t(uint16_t, max_vt_div,
5103e2db036SSakari Ailus 			   DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz,
5113e2db036SSakari Ailus 				      lim->vt_bk.min_pix_clk_freq_hz));
5123e2db036SSakari Ailus 	dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %u\n",
5133e2db036SSakari Ailus 		max_vt_div);
5143e2db036SSakari Ailus 
5159ec6e5b1SSakari Ailus 	ccs_pll_find_vt_sys_div(dev, lim, pll, pll_fr, min_vt_div,
5169ec6e5b1SSakari Ailus 				max_vt_div, &min_sys_div, &max_sys_div);
5173e2db036SSakari Ailus 
5183e2db036SSakari Ailus 	/*
5193e2db036SSakari Ailus 	 * Find pix_div such that a legal pix_div * sys_div results
5203e2db036SSakari Ailus 	 * into a value which is not smaller than div, the desired
5213e2db036SSakari Ailus 	 * divisor.
5223e2db036SSakari Ailus 	 */
52336154b68SSakari Ailus 	for (vt_div = min_vt_div; vt_div <= max_vt_div; vt_div++) {
52436154b68SSakari Ailus 		uint16_t __max_sys_div = vt_div & 1 ? 1 : max_sys_div;
52536154b68SSakari Ailus 
52636154b68SSakari Ailus 		for (sys_div = min_sys_div; sys_div <= __max_sys_div;
5273e2db036SSakari Ailus 		     sys_div += 2 - (sys_div & 1)) {
52836154b68SSakari Ailus 			uint16_t pix_div;
5293e2db036SSakari Ailus 			uint16_t rounded_div;
5303e2db036SSakari Ailus 
53136154b68SSakari Ailus 			pix_div = DIV_ROUND_UP(vt_div, sys_div);
53236154b68SSakari Ailus 
5333e2db036SSakari Ailus 			if (pix_div < lim->vt_bk.min_pix_clk_div
5343e2db036SSakari Ailus 			    || pix_div > lim->vt_bk.max_pix_clk_div) {
5353e2db036SSakari Ailus 				dev_dbg(dev,
5363e2db036SSakari Ailus 					"pix_div %u too small or too big (%u--%u)\n",
5373e2db036SSakari Ailus 					pix_div,
5383e2db036SSakari Ailus 					lim->vt_bk.min_pix_clk_div,
5393e2db036SSakari Ailus 					lim->vt_bk.max_pix_clk_div);
5403e2db036SSakari Ailus 				continue;
5413e2db036SSakari Ailus 			}
5423e2db036SSakari Ailus 
5433e2db036SSakari Ailus 			rounded_div = roundup(vt_div, best_pix_div);
5443e2db036SSakari Ailus 
5453e2db036SSakari Ailus 			/* Check if this one is better. */
5463e2db036SSakari Ailus 			if (pix_div * sys_div <= rounded_div)
5473e2db036SSakari Ailus 				best_pix_div = pix_div;
5483e2db036SSakari Ailus 
5493e2db036SSakari Ailus 			/* Bail out if we've already found the best value. */
5503e2db036SSakari Ailus 			if (vt_div == rounded_div)
5513e2db036SSakari Ailus 				break;
5523e2db036SSakari Ailus 		}
553594f1e93SSakari Ailus 		if (best_pix_div < SHRT_MAX >> 1)
5543e2db036SSakari Ailus 			break;
5553e2db036SSakari Ailus 	}
5563e2db036SSakari Ailus 
5573e2db036SSakari Ailus 	pll->vt_bk.sys_clk_div = DIV_ROUND_UP(vt_div, best_pix_div);
5583e2db036SSakari Ailus 	pll->vt_bk.pix_clk_div = best_pix_div;
5593e2db036SSakari Ailus 
5603e2db036SSakari Ailus 	pll->vt_bk.sys_clk_freq_hz =
5613e2db036SSakari Ailus 		pll_fr->pll_op_clk_freq_hz / pll->vt_bk.sys_clk_div;
5623e2db036SSakari Ailus 	pll->vt_bk.pix_clk_freq_hz =
5633e2db036SSakari Ailus 		pll->vt_bk.sys_clk_freq_hz / pll->vt_bk.pix_clk_div;
564a38836b2SSakari Ailus 
565a38836b2SSakari Ailus out_calc_pixel_rate:
566a38836b2SSakari Ailus 	pll->pixel_rate_pixel_array =
567a38836b2SSakari Ailus 		pll->vt_bk.pix_clk_freq_hz * pll->vt_lanes;
5683e2db036SSakari Ailus }
5693e2db036SSakari Ailus 
5709e05bbacSSakari Ailus /*
5719e05bbacSSakari Ailus  * Heuristically guess the PLL tree for a given common multiplier and
5729e05bbacSSakari Ailus  * divisor. Begin with the operational timing and continue to video
5739e05bbacSSakari Ailus  * timing once operational timing has been verified.
5749e05bbacSSakari Ailus  *
5759e05bbacSSakari Ailus  * @mul is the PLL multiplier and @div is the common divisor
5769e05bbacSSakari Ailus  * (pre_pll_clk_div and op_sys_clk_div combined). The final PLL
5779e05bbacSSakari Ailus  * multiplier will be a multiple of @mul.
5789e05bbacSSakari Ailus  *
5799e05bbacSSakari Ailus  * @return Zero on success, error code on error.
5809e05bbacSSakari Ailus  */
5819e05bbacSSakari Ailus static int
582a38836b2SSakari Ailus ccs_pll_calculate_op(struct device *dev, const struct ccs_pll_limits *lim,
583415ddd99SSakari Ailus 		     const struct ccs_pll_branch_limits_fr *op_lim_fr,
584415ddd99SSakari Ailus 		     const struct ccs_pll_branch_limits_bk *op_lim_bk,
585415ddd99SSakari Ailus 		     struct ccs_pll *pll, struct ccs_pll_branch_fr *op_pll_fr,
586415ddd99SSakari Ailus 		     struct ccs_pll_branch_bk *op_pll_bk, uint32_t mul,
587*900c33e8SSakari Ailus 		     uint32_t div, uint32_t op_sys_clk_freq_hz_sdr, uint32_t l,
588*900c33e8SSakari Ailus 		     bool cphy, uint32_t phy_const)
5899e05bbacSSakari Ailus {
5909e05bbacSSakari Ailus 	/*
5919e05bbacSSakari Ailus 	 * Higher multipliers (and divisors) are often required than
5929e05bbacSSakari Ailus 	 * necessitated by the external clock and the output clocks.
5939e05bbacSSakari Ailus 	 * There are limits for all values in the clock tree. These
5949e05bbacSSakari Ailus 	 * are the minimum and maximum multiplier for mul.
5959e05bbacSSakari Ailus 	 */
5969e05bbacSSakari Ailus 	uint32_t more_mul_min, more_mul_max;
5979e05bbacSSakari Ailus 	uint32_t more_mul_factor;
598e583e654SSakari Ailus 	uint32_t i;
5999e05bbacSSakari Ailus 
6009e05bbacSSakari Ailus 	/*
6019e05bbacSSakari Ailus 	 * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be
6029e05bbacSSakari Ailus 	 * too high.
6039e05bbacSSakari Ailus 	 */
604415ddd99SSakari Ailus 	dev_dbg(dev, "op_pre_pll_clk_div %u\n", op_pll_fr->pre_pll_clk_div);
6059e05bbacSSakari Ailus 
6069e05bbacSSakari Ailus 	/* Don't go above max pll multiplier. */
607415ddd99SSakari Ailus 	more_mul_max = op_lim_fr->max_pll_multiplier / mul;
608415ddd99SSakari Ailus 	dev_dbg(dev, "more_mul_max: max_op_pll_multiplier check: %u\n",
6099e05bbacSSakari Ailus 		more_mul_max);
6109e05bbacSSakari Ailus 	/* Don't go above max pll op frequency. */
6119e05bbacSSakari Ailus 	more_mul_max =
6129e05bbacSSakari Ailus 		min_t(uint32_t,
6139e05bbacSSakari Ailus 		      more_mul_max,
614415ddd99SSakari Ailus 		      op_lim_fr->max_pll_op_clk_freq_hz
615ae502e08SSakari Ailus 		      / (pll->ext_clk_freq_hz /
616ae502e08SSakari Ailus 			 op_pll_fr->pre_pll_clk_div * mul));
617415ddd99SSakari Ailus 	dev_dbg(dev, "more_mul_max: max_pll_op_clk_freq_hz check: %u\n",
6189e05bbacSSakari Ailus 		more_mul_max);
6199e05bbacSSakari Ailus 	/* Don't go above the division capability of op sys clock divider. */
6209e05bbacSSakari Ailus 	more_mul_max = min(more_mul_max,
621415ddd99SSakari Ailus 			   op_lim_bk->max_sys_clk_div * op_pll_fr->pre_pll_clk_div
6229e05bbacSSakari Ailus 			   / div);
6239e05bbacSSakari Ailus 	dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %u\n",
6249e05bbacSSakari Ailus 		more_mul_max);
625c64cf71dSSakari Ailus 	/* Ensure we won't go above max_pll_multiplier. */
62682ab97c8SSakari Ailus 	more_mul_max = min(more_mul_max, op_lim_fr->max_pll_multiplier / mul);
6279e05bbacSSakari Ailus 	dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %u\n",
6289e05bbacSSakari Ailus 		more_mul_max);
6299e05bbacSSakari Ailus 
630415ddd99SSakari Ailus 	/* Ensure we won't go below min_pll_op_clk_freq_hz. */
631415ddd99SSakari Ailus 	more_mul_min = DIV_ROUND_UP(op_lim_fr->min_pll_op_clk_freq_hz,
632415ddd99SSakari Ailus 				    pll->ext_clk_freq_hz /
633415ddd99SSakari Ailus 				    op_pll_fr->pre_pll_clk_div * mul);
634415ddd99SSakari Ailus 	dev_dbg(dev, "more_mul_min: min_op_pll_op_clk_freq_hz check: %u\n",
6359e05bbacSSakari Ailus 		more_mul_min);
6369e05bbacSSakari Ailus 	/* Ensure we won't go below min_pll_multiplier. */
6379e05bbacSSakari Ailus 	more_mul_min = max(more_mul_min,
638415ddd99SSakari Ailus 			   DIV_ROUND_UP(op_lim_fr->min_pll_multiplier, mul));
639415ddd99SSakari Ailus 	dev_dbg(dev, "more_mul_min: min_op_pll_multiplier check: %u\n",
6409e05bbacSSakari Ailus 		more_mul_min);
6419e05bbacSSakari Ailus 
6429e05bbacSSakari Ailus 	if (more_mul_min > more_mul_max) {
6439e05bbacSSakari Ailus 		dev_dbg(dev,
6449e05bbacSSakari Ailus 			"unable to compute more_mul_min and more_mul_max\n");
6459e05bbacSSakari Ailus 		return -EINVAL;
6469e05bbacSSakari Ailus 	}
6479e05bbacSSakari Ailus 
648415ddd99SSakari Ailus 	more_mul_factor = lcm(div, op_pll_fr->pre_pll_clk_div) / div;
6499e05bbacSSakari Ailus 	dev_dbg(dev, "more_mul_factor: %u\n", more_mul_factor);
650415ddd99SSakari Ailus 	more_mul_factor = lcm(more_mul_factor, op_lim_bk->min_sys_clk_div);
6519e05bbacSSakari Ailus 	dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n",
6529e05bbacSSakari Ailus 		more_mul_factor);
6539e05bbacSSakari Ailus 	i = roundup(more_mul_min, more_mul_factor);
6549e05bbacSSakari Ailus 	if (!is_one_or_even(i))
6559e05bbacSSakari Ailus 		i <<= 1;
6569e05bbacSSakari Ailus 
6579e05bbacSSakari Ailus 	dev_dbg(dev, "final more_mul: %u\n", i);
6589e05bbacSSakari Ailus 	if (i > more_mul_max) {
6599e05bbacSSakari Ailus 		dev_dbg(dev, "final more_mul is bad, max %u\n", more_mul_max);
6609e05bbacSSakari Ailus 		return -EINVAL;
6619e05bbacSSakari Ailus 	}
6629e05bbacSSakari Ailus 
663415ddd99SSakari Ailus 	op_pll_fr->pll_multiplier = mul * i;
664415ddd99SSakari Ailus 	op_pll_bk->sys_clk_div = div * i / op_pll_fr->pre_pll_clk_div;
665415ddd99SSakari Ailus 	dev_dbg(dev, "op_sys_clk_div: %u\n", op_pll_bk->sys_clk_div);
6669e05bbacSSakari Ailus 
667415ddd99SSakari Ailus 	op_pll_fr->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz
668415ddd99SSakari Ailus 		/ op_pll_fr->pre_pll_clk_div;
6699e05bbacSSakari Ailus 
670415ddd99SSakari Ailus 	op_pll_fr->pll_op_clk_freq_hz = op_pll_fr->pll_ip_clk_freq_hz
671415ddd99SSakari Ailus 		* op_pll_fr->pll_multiplier;
6729e05bbacSSakari Ailus 
673c4c0b222SSakari Ailus 	if (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL)
674*900c33e8SSakari Ailus 		op_pll_bk->pix_clk_div =
675*900c33e8SSakari Ailus 			(pll->bits_per_pixel
676*900c33e8SSakari Ailus 			 * pll->op_lanes * (phy_const << op_sys_ddr(pll->flags))
677*900c33e8SSakari Ailus 			 / PHY_CONST_DIV / pll->csi2.lanes / l)
678*900c33e8SSakari Ailus 			>> op_pix_ddr(pll->flags);
679c4c0b222SSakari Ailus 	else
6808030aa4fSSakari Ailus 		op_pll_bk->pix_clk_div =
681*900c33e8SSakari Ailus 			(pll->bits_per_pixel
682*900c33e8SSakari Ailus 			 * (phy_const << op_sys_ddr(pll->flags))
683*900c33e8SSakari Ailus 			 / PHY_CONST_DIV / l) >> op_pix_ddr(pll->flags);
684c4c0b222SSakari Ailus 
685415ddd99SSakari Ailus 	op_pll_bk->pix_clk_freq_hz =
686*900c33e8SSakari Ailus 		(op_sys_clk_freq_hz_sdr >> op_pix_ddr(pll->flags))
687*900c33e8SSakari Ailus 		/ op_pll_bk->pix_clk_div;
688*900c33e8SSakari Ailus 	op_pll_bk->sys_clk_freq_hz =
689*900c33e8SSakari Ailus 		op_sys_clk_freq_hz_sdr >> op_sys_ddr(pll->flags);
690c4c0b222SSakari Ailus 
691cac8f5d2SSakari Ailus 	dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll_bk->pix_clk_div);
692cac8f5d2SSakari Ailus 
693a38836b2SSakari Ailus 	return 0;
6949e05bbacSSakari Ailus }
6959e05bbacSSakari Ailus 
696415ddd99SSakari Ailus int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
6979e05bbacSSakari Ailus 		      struct ccs_pll *pll)
6989e05bbacSSakari Ailus {
6996c7469e4SSakari Ailus 	const struct ccs_pll_branch_limits_fr *op_lim_fr;
7006c7469e4SSakari Ailus 	const struct ccs_pll_branch_limits_bk *op_lim_bk;
7016c7469e4SSakari Ailus 	struct ccs_pll_branch_fr *op_pll_fr;
7026c7469e4SSakari Ailus 	struct ccs_pll_branch_bk *op_pll_bk;
7038030aa4fSSakari Ailus 	bool cphy = pll->bus_type == CCS_PLL_BUS_TYPE_CSI2_CPHY;
7048030aa4fSSakari Ailus 	uint32_t phy_const = cphy ? CPHY_CONST : DPHY_CONST;
705*900c33e8SSakari Ailus 	uint32_t op_sys_clk_freq_hz_sdr;
706415ddd99SSakari Ailus 	uint16_t min_op_pre_pll_clk_div;
707415ddd99SSakari Ailus 	uint16_t max_op_pre_pll_clk_div;
7089e05bbacSSakari Ailus 	uint32_t mul, div;
709c4c0b222SSakari Ailus 	uint32_t l = (!pll->op_bits_per_lane ||
710c4c0b222SSakari Ailus 		      pll->op_bits_per_lane >= pll->bits_per_pixel) ? 1 : 2;
711e583e654SSakari Ailus 	uint32_t i;
7129e05bbacSSakari Ailus 	int rval = -EINVAL;
7139e05bbacSSakari Ailus 
714cac8f5d2SSakari Ailus 	if (!(pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL)) {
715cac8f5d2SSakari Ailus 		pll->op_lanes = 1;
716cac8f5d2SSakari Ailus 		pll->vt_lanes = 1;
717cac8f5d2SSakari Ailus 	}
7189490a227SSakari Ailus 
7196c7469e4SSakari Ailus 	if (pll->flags & CCS_PLL_FLAG_DUAL_PLL) {
7206c7469e4SSakari Ailus 		op_lim_fr = &lim->op_fr;
7216c7469e4SSakari Ailus 		op_lim_bk = &lim->op_bk;
7226c7469e4SSakari Ailus 		op_pll_fr = &pll->op_fr;
7236c7469e4SSakari Ailus 		op_pll_bk = &pll->op_bk;
7246c7469e4SSakari Ailus 	} else if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) {
7256c7469e4SSakari Ailus 		/*
7266c7469e4SSakari Ailus 		 * If there's no OP PLL at all, use the VT values
7276c7469e4SSakari Ailus 		 * instead. The OP values are ignored for the rest of
7286c7469e4SSakari Ailus 		 * the PLL calculation.
7296c7469e4SSakari Ailus 		 */
7306c7469e4SSakari Ailus 		op_lim_fr = &lim->vt_fr;
7316c7469e4SSakari Ailus 		op_lim_bk = &lim->vt_bk;
7326c7469e4SSakari Ailus 		op_pll_fr = &pll->vt_fr;
7336c7469e4SSakari Ailus 		op_pll_bk = &pll->vt_bk;
7346c7469e4SSakari Ailus 	} else {
7356c7469e4SSakari Ailus 		op_lim_fr = &lim->vt_fr;
7366c7469e4SSakari Ailus 		op_lim_bk = &lim->op_bk;
7376c7469e4SSakari Ailus 		op_pll_fr = &pll->vt_fr;
7386c7469e4SSakari Ailus 		op_pll_bk = &pll->op_bk;
7396c7469e4SSakari Ailus 	}
7406c7469e4SSakari Ailus 
741d7172c0eSSakari Ailus 	if (!pll->op_lanes || !pll->vt_lanes || !pll->bits_per_pixel ||
742d7172c0eSSakari Ailus 	    !pll->ext_clk_freq_hz || !pll->link_freq || !pll->scale_m ||
743d7172c0eSSakari Ailus 	    !op_lim_fr->min_pll_ip_clk_freq_hz ||
744d7172c0eSSakari Ailus 	    !op_lim_fr->max_pll_ip_clk_freq_hz ||
745d7172c0eSSakari Ailus 	    !op_lim_fr->min_pll_op_clk_freq_hz ||
746d7172c0eSSakari Ailus 	    !op_lim_fr->max_pll_op_clk_freq_hz ||
747d7172c0eSSakari Ailus 	    !op_lim_bk->max_sys_clk_div || !op_lim_fr->max_pll_multiplier)
748d7172c0eSSakari Ailus 		return -EINVAL;
749d7172c0eSSakari Ailus 
7509490a227SSakari Ailus 	/*
7519490a227SSakari Ailus 	 * Make sure op_pix_clk_div will be integer --- unless flexible
7529490a227SSakari Ailus 	 * op_pix_clk_div is supported
7539490a227SSakari Ailus 	 */
7549490a227SSakari Ailus 	if (!(pll->flags & CCS_PLL_FLAG_FLEXIBLE_OP_PIX_CLK_DIV) &&
755*900c33e8SSakari Ailus 	    (pll->bits_per_pixel * pll->op_lanes) %
756*900c33e8SSakari Ailus 	    (pll->csi2.lanes * l << op_pix_ddr(pll->flags))) {
7579490a227SSakari Ailus 		dev_dbg(dev, "op_pix_clk_div not an integer (bpp %u, op lanes %u, lanes %u, l %u)\n",
7589490a227SSakari Ailus 			pll->bits_per_pixel, pll->op_lanes, pll->csi2.lanes, l);
7599490a227SSakari Ailus 		return -EINVAL;
7609490a227SSakari Ailus 	}
7619490a227SSakari Ailus 
762cac8f5d2SSakari Ailus 	dev_dbg(dev, "vt_lanes: %u\n", pll->vt_lanes);
763cac8f5d2SSakari Ailus 	dev_dbg(dev, "op_lanes: %u\n", pll->op_lanes);
764cac8f5d2SSakari Ailus 
7659e05bbacSSakari Ailus 	dev_dbg(dev, "binning: %ux%u\n", pll->binning_horizontal,
7669e05bbacSSakari Ailus 		pll->binning_vertical);
7679e05bbacSSakari Ailus 
7689e05bbacSSakari Ailus 	switch (pll->bus_type) {
76947b6eaf3SSakari Ailus 	case CCS_PLL_BUS_TYPE_CSI2_DPHY:
7709e05bbacSSakari Ailus 		/* CSI transfers 2 bits per clock per lane; thus times 2 */
771*900c33e8SSakari Ailus 		op_sys_clk_freq_hz_sdr = pll->link_freq * 2
772cac8f5d2SSakari Ailus 			* (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
773ae502e08SSakari Ailus 			   1 : pll->csi2.lanes);
7749e05bbacSSakari Ailus 		break;
7758030aa4fSSakari Ailus 	case CCS_PLL_BUS_TYPE_CSI2_CPHY:
776*900c33e8SSakari Ailus 		op_sys_clk_freq_hz_sdr =
7778030aa4fSSakari Ailus 			pll->link_freq
7788030aa4fSSakari Ailus 			* (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
7798030aa4fSSakari Ailus 			   1 : pll->csi2.lanes);
7808030aa4fSSakari Ailus 		break;
7819e05bbacSSakari Ailus 	default:
7829e05bbacSSakari Ailus 		return -EINVAL;
7839e05bbacSSakari Ailus 	}
7849e05bbacSSakari Ailus 
785cac8f5d2SSakari Ailus 	pll->pixel_rate_csi =
786*900c33e8SSakari Ailus 		div_u64((uint64_t)op_sys_clk_freq_hz_sdr
787cac8f5d2SSakari Ailus 			* (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
7888030aa4fSSakari Ailus 			   pll->csi2.lanes : 1) * PHY_CONST_DIV,
7898030aa4fSSakari Ailus 			phy_const * pll->bits_per_pixel * l);
790cac8f5d2SSakari Ailus 
791415ddd99SSakari Ailus 	/* Figure out limits for OP pre-pll divider based on extclk */
792415ddd99SSakari Ailus 	dev_dbg(dev, "min / max op_pre_pll_clk_div: %u / %u\n",
793415ddd99SSakari Ailus 		op_lim_fr->min_pre_pll_clk_div, op_lim_fr->max_pre_pll_clk_div);
794415ddd99SSakari Ailus 	max_op_pre_pll_clk_div =
795415ddd99SSakari Ailus 		min_t(uint16_t, op_lim_fr->max_pre_pll_clk_div,
7969e05bbacSSakari Ailus 		      clk_div_even(pll->ext_clk_freq_hz /
797415ddd99SSakari Ailus 				   op_lim_fr->min_pll_ip_clk_freq_hz));
798415ddd99SSakari Ailus 	min_op_pre_pll_clk_div =
799415ddd99SSakari Ailus 		max_t(uint16_t, op_lim_fr->min_pre_pll_clk_div,
8009e05bbacSSakari Ailus 		      clk_div_even_up(
8019e05bbacSSakari Ailus 			      DIV_ROUND_UP(pll->ext_clk_freq_hz,
802415ddd99SSakari Ailus 					   op_lim_fr->max_pll_ip_clk_freq_hz)));
803415ddd99SSakari Ailus 	dev_dbg(dev, "pre-pll check: min / max op_pre_pll_clk_div: %u / %u\n",
804415ddd99SSakari Ailus 		min_op_pre_pll_clk_div, max_op_pre_pll_clk_div);
8059e05bbacSSakari Ailus 
806*900c33e8SSakari Ailus 	i = gcd(op_sys_clk_freq_hz_sdr,
807*900c33e8SSakari Ailus 		pll->ext_clk_freq_hz << op_pix_ddr(pll->flags));
808*900c33e8SSakari Ailus 	mul = op_sys_clk_freq_hz_sdr / i;
809*900c33e8SSakari Ailus 	div = (pll->ext_clk_freq_hz << op_pix_ddr(pll->flags)) / i;
8109e05bbacSSakari Ailus 	dev_dbg(dev, "mul %u / div %u\n", mul, div);
8119e05bbacSSakari Ailus 
812415ddd99SSakari Ailus 	min_op_pre_pll_clk_div =
813415ddd99SSakari Ailus 		max_t(uint16_t, min_op_pre_pll_clk_div,
8149e05bbacSSakari Ailus 		      clk_div_even_up(
815482e75e7SSakari Ailus 			      mul /
816482e75e7SSakari Ailus 			      one_or_more(
817482e75e7SSakari Ailus 				      DIV_ROUND_UP(op_lim_fr->max_pll_op_clk_freq_hz,
818482e75e7SSakari Ailus 						   pll->ext_clk_freq_hz))));
819415ddd99SSakari Ailus 	dev_dbg(dev, "pll_op check: min / max op_pre_pll_clk_div: %u / %u\n",
820415ddd99SSakari Ailus 		min_op_pre_pll_clk_div, max_op_pre_pll_clk_div);
8219e05bbacSSakari Ailus 
822415ddd99SSakari Ailus 	for (op_pll_fr->pre_pll_clk_div = min_op_pre_pll_clk_div;
823415ddd99SSakari Ailus 	     op_pll_fr->pre_pll_clk_div <= max_op_pre_pll_clk_div;
8244e1e8d24SSakari Ailus 	     op_pll_fr->pre_pll_clk_div +=
8254e1e8d24SSakari Ailus 		     (pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER) ? 1 :
8264e1e8d24SSakari Ailus 		     2 - (op_pll_fr->pre_pll_clk_div & 1)) {
827a38836b2SSakari Ailus 		rval = ccs_pll_calculate_op(dev, lim, op_lim_fr, op_lim_bk, pll,
828*900c33e8SSakari Ailus 					    op_pll_fr, op_pll_bk, mul, div,
829*900c33e8SSakari Ailus 					    op_sys_clk_freq_hz_sdr, l, cphy,
830*900c33e8SSakari Ailus 					    phy_const);
8319e05bbacSSakari Ailus 		if (rval)
8329e05bbacSSakari Ailus 			continue;
8339e05bbacSSakari Ailus 
834b41f2708SSakari Ailus 		rval = check_fr_bounds(dev, lim, pll,
835b41f2708SSakari Ailus 				       pll->flags & CCS_PLL_FLAG_DUAL_PLL ?
836b41f2708SSakari Ailus 				       PLL_OP : PLL_VT);
837f25d3962SSakari Ailus 		if (rval)
838f25d3962SSakari Ailus 			continue;
839f25d3962SSakari Ailus 
840f25d3962SSakari Ailus 		rval = check_bk_bounds(dev, lim, pll, PLL_OP);
841f25d3962SSakari Ailus 		if (rval)
842f25d3962SSakari Ailus 			continue;
843f25d3962SSakari Ailus 
8446c7469e4SSakari Ailus 		if (pll->flags & CCS_PLL_FLAG_DUAL_PLL)
8456c7469e4SSakari Ailus 			break;
8466c7469e4SSakari Ailus 
847a38836b2SSakari Ailus 		ccs_pll_calculate_vt(dev, lim, op_lim_bk, pll, op_pll_fr,
848a38836b2SSakari Ailus 				     op_pll_bk, cphy, phy_const);
849a38836b2SSakari Ailus 
850f25d3962SSakari Ailus 		rval = check_bk_bounds(dev, lim, pll, PLL_VT);
851f25d3962SSakari Ailus 		if (rval)
852f25d3962SSakari Ailus 			continue;
853f25d3962SSakari Ailus 		rval = check_ext_bounds(dev, pll);
854a38836b2SSakari Ailus 		if (rval)
855a38836b2SSakari Ailus 			continue;
856a38836b2SSakari Ailus 
8576c7469e4SSakari Ailus 		break;
8589e05bbacSSakari Ailus 	}
8599e05bbacSSakari Ailus 
8606c7469e4SSakari Ailus 	if (rval) {
8619e05bbacSSakari Ailus 		dev_dbg(dev, "unable to compute pre_pll divisor\n");
8629e05bbacSSakari Ailus 
8639e05bbacSSakari Ailus 		return rval;
8649e05bbacSSakari Ailus 	}
8656c7469e4SSakari Ailus 
8666c7469e4SSakari Ailus 	if (pll->flags & CCS_PLL_FLAG_DUAL_PLL) {
8676c7469e4SSakari Ailus 		rval = ccs_pll_calculate_vt_tree(dev, lim, pll);
8686c7469e4SSakari Ailus 
8696c7469e4SSakari Ailus 		if (rval)
8706c7469e4SSakari Ailus 			return rval;
8716c7469e4SSakari Ailus 	}
8726c7469e4SSakari Ailus 
8736c7469e4SSakari Ailus 	print_pll(dev, pll);
8746c7469e4SSakari Ailus 
8756c7469e4SSakari Ailus 	return 0;
8766c7469e4SSakari Ailus }
8779e05bbacSSakari Ailus EXPORT_SYMBOL_GPL(ccs_pll_calculate);
8789e05bbacSSakari Ailus 
8797389d01cSSakari Ailus MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
8809e05bbacSSakari Ailus MODULE_DESCRIPTION("Generic MIPI CCS/SMIA/SMIA++ PLL calculator");
881b3c0115eSSakari Ailus MODULE_LICENSE("GPL v2");
882