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. */
clk_div_even(u32 a)208a75e8dcSSakari Ailus static inline u32 clk_div_even(u32 a)
219e05bbacSSakari Ailus {
228a75e8dcSSakari Ailus return max_t(u32, 1, a & ~1);
239e05bbacSSakari Ailus }
249e05bbacSSakari Ailus
259e05bbacSSakari Ailus /* Return an even number or one. */
clk_div_even_up(u32 a)268a75e8dcSSakari Ailus static inline u32 clk_div_even_up(u32 a)
279e05bbacSSakari Ailus {
289e05bbacSSakari Ailus if (a == 1)
299e05bbacSSakari Ailus return 1;
309e05bbacSSakari Ailus return (a + 1) & ~1;
319e05bbacSSakari Ailus }
329e05bbacSSakari Ailus
is_one_or_even(u32 a)338a75e8dcSSakari Ailus static inline u32 is_one_or_even(u32 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
one_or_more(u32 a)438a75e8dcSSakari Ailus static inline u32 one_or_more(u32 a)
44482e75e7SSakari Ailus {
45482e75e7SSakari Ailus return a ?: 1;
46482e75e7SSakari Ailus }
47482e75e7SSakari Ailus
bounds_check(struct device * dev,u32 val,u32 min,u32 max,const char * prefix,char * str)488a75e8dcSSakari Ailus static int bounds_check(struct device *dev, u32 val,
498a75e8dcSSakari Ailus u32 min, u32 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
pll_string(unsigned int which)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
print_pll(struct device * dev,struct ccs_pll * pll)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
122bd189aacSSakari Ailus dev_dbg(dev, "pixel rate in pixel array:\t%u\n",
123bd189aacSSakari Ailus pll->pixel_rate_pixel_array);
124bd189aacSSakari Ailus dev_dbg(dev, "pixel rate on CSI-2 bus:\t%u\n",
125bd189aacSSakari Ailus pll->pixel_rate_csi);
126bd189aacSSakari Ailus
127900c33e8SSakari Ailus dev_dbg(dev, "flags%s%s%s%s%s%s%s%s%s\n",
128fadfe884SSakari Ailus pll->flags & PLL_FL(LANE_SPEED_MODEL) ? " lane-speed" : "",
129fadfe884SSakari Ailus pll->flags & PLL_FL(LINK_DECOUPLED) ? " link-decoupled" : "",
130fadfe884SSakari Ailus pll->flags & PLL_FL(EXT_IP_PLL_DIVIDER) ?
131fadfe884SSakari Ailus " ext-ip-pll-divider" : "",
132fadfe884SSakari Ailus pll->flags & PLL_FL(FLEXIBLE_OP_PIX_CLK_DIV) ?
133fadfe884SSakari Ailus " flexible-op-pix-div" : "",
134fadfe884SSakari Ailus pll->flags & PLL_FL(FIFO_DERATING) ? " fifo-derating" : "",
1356c7469e4SSakari Ailus pll->flags & PLL_FL(FIFO_OVERRATING) ? " fifo-overrating" : "",
136900c33e8SSakari Ailus pll->flags & PLL_FL(DUAL_PLL) ? " dual-pll" : "",
137900c33e8SSakari Ailus pll->flags & PLL_FL(OP_SYS_DDR) ? " op-sys-ddr" : "",
138900c33e8SSakari Ailus pll->flags & PLL_FL(OP_PIX_DDR) ? " op-pix-ddr" : "");
139900c33e8SSakari Ailus }
140900c33e8SSakari Ailus
op_sys_ddr(u32 flags)1418a75e8dcSSakari Ailus static u32 op_sys_ddr(u32 flags)
142900c33e8SSakari Ailus {
143900c33e8SSakari Ailus return flags & CCS_PLL_FLAG_OP_SYS_DDR ? 1 : 0;
144900c33e8SSakari Ailus }
145900c33e8SSakari Ailus
op_pix_ddr(u32 flags)1468a75e8dcSSakari Ailus static u32 op_pix_ddr(u32 flags)
147900c33e8SSakari Ailus {
148900c33e8SSakari Ailus return flags & CCS_PLL_FLAG_OP_PIX_DDR ? 1 : 0;
1499e05bbacSSakari Ailus }
1509e05bbacSSakari Ailus
check_fr_bounds(struct device * dev,const struct ccs_pll_limits * lim,struct ccs_pll * pll,unsigned int which)151f25d3962SSakari Ailus static int check_fr_bounds(struct device *dev,
152415ddd99SSakari Ailus const struct ccs_pll_limits *lim,
153f25d3962SSakari Ailus struct ccs_pll *pll, unsigned int which)
1549e05bbacSSakari Ailus {
155f25d3962SSakari Ailus const struct ccs_pll_branch_limits_fr *lim_fr;
156f25d3962SSakari Ailus struct ccs_pll_branch_fr *pll_fr;
157f25d3962SSakari Ailus const char *s = pll_string(which);
1589e05bbacSSakari Ailus int rval;
1599e05bbacSSakari Ailus
160f25d3962SSakari Ailus if (which == PLL_OP) {
161f25d3962SSakari Ailus lim_fr = &lim->op_fr;
162f25d3962SSakari Ailus pll_fr = &pll->op_fr;
163f25d3962SSakari Ailus } else {
164f25d3962SSakari Ailus lim_fr = &lim->vt_fr;
165f25d3962SSakari Ailus pll_fr = &pll->vt_fr;
166f25d3962SSakari Ailus }
1679e05bbacSSakari Ailus
168f25d3962SSakari Ailus rval = bounds_check(dev, pll_fr->pre_pll_clk_div,
169f25d3962SSakari Ailus lim_fr->min_pre_pll_clk_div,
170f25d3962SSakari Ailus lim_fr->max_pre_pll_clk_div, s, "pre_pll_clk_div");
171f25d3962SSakari Ailus
172f25d3962SSakari Ailus if (!rval)
173f25d3962SSakari Ailus rval = bounds_check(dev, pll_fr->pll_ip_clk_freq_hz,
174f25d3962SSakari Ailus lim_fr->min_pll_ip_clk_freq_hz,
175f25d3962SSakari Ailus lim_fr->max_pll_ip_clk_freq_hz,
176f25d3962SSakari Ailus s, "pll_ip_clk_freq_hz");
177f25d3962SSakari Ailus if (!rval)
178f25d3962SSakari Ailus rval = bounds_check(dev, pll_fr->pll_multiplier,
179f25d3962SSakari Ailus lim_fr->min_pll_multiplier,
180f25d3962SSakari Ailus lim_fr->max_pll_multiplier,
181f25d3962SSakari Ailus s, "pll_multiplier");
182f25d3962SSakari Ailus if (!rval)
183f25d3962SSakari Ailus rval = bounds_check(dev, pll_fr->pll_op_clk_freq_hz,
184f25d3962SSakari Ailus lim_fr->min_pll_op_clk_freq_hz,
185f25d3962SSakari Ailus lim_fr->max_pll_op_clk_freq_hz,
186f25d3962SSakari Ailus s, "pll_op_clk_freq_hz");
187f25d3962SSakari Ailus
1889e05bbacSSakari Ailus return rval;
189f25d3962SSakari Ailus }
1909e05bbacSSakari Ailus
check_bk_bounds(struct device * dev,const struct ccs_pll_limits * lim,struct ccs_pll * pll,unsigned int which)191f25d3962SSakari Ailus static int check_bk_bounds(struct device *dev,
192f25d3962SSakari Ailus const struct ccs_pll_limits *lim,
193f25d3962SSakari Ailus struct ccs_pll *pll, unsigned int which)
194f25d3962SSakari Ailus {
195f25d3962SSakari Ailus const struct ccs_pll_branch_limits_bk *lim_bk;
196f25d3962SSakari Ailus struct ccs_pll_branch_bk *pll_bk;
197f25d3962SSakari Ailus const char *s = pll_string(which);
198f25d3962SSakari Ailus int rval;
1999e05bbacSSakari Ailus
200f25d3962SSakari Ailus if (which == PLL_OP) {
201f25d3962SSakari Ailus if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS)
202f25d3962SSakari Ailus return 0;
203f25d3962SSakari Ailus
204f25d3962SSakari Ailus lim_bk = &lim->op_bk;
205f25d3962SSakari Ailus pll_bk = &pll->op_bk;
206f25d3962SSakari Ailus } else {
207f25d3962SSakari Ailus lim_bk = &lim->vt_bk;
208f25d3962SSakari Ailus pll_bk = &pll->vt_bk;
209f25d3962SSakari Ailus }
210f25d3962SSakari Ailus
211f25d3962SSakari Ailus rval = bounds_check(dev, pll_bk->sys_clk_div,
212f25d3962SSakari Ailus lim_bk->min_sys_clk_div,
213f25d3962SSakari Ailus lim_bk->max_sys_clk_div, s, "op_sys_clk_div");
214f25d3962SSakari Ailus if (!rval)
215f25d3962SSakari Ailus rval = bounds_check(dev, pll_bk->sys_clk_freq_hz,
216f25d3962SSakari Ailus lim_bk->min_sys_clk_freq_hz,
217f25d3962SSakari Ailus lim_bk->max_sys_clk_freq_hz,
218f25d3962SSakari Ailus s, "sys_clk_freq_hz");
219f25d3962SSakari Ailus if (!rval)
220f25d3962SSakari Ailus rval = bounds_check(dev, pll_bk->sys_clk_div,
221f25d3962SSakari Ailus lim_bk->min_sys_clk_div,
222f25d3962SSakari Ailus lim_bk->max_sys_clk_div,
223f25d3962SSakari Ailus s, "sys_clk_div");
224f25d3962SSakari Ailus if (!rval)
225f25d3962SSakari Ailus rval = bounds_check(dev, pll_bk->pix_clk_freq_hz,
226f25d3962SSakari Ailus lim_bk->min_pix_clk_freq_hz,
227f25d3962SSakari Ailus lim_bk->max_pix_clk_freq_hz,
228f25d3962SSakari Ailus s, "pix_clk_freq_hz");
229f25d3962SSakari Ailus
230f25d3962SSakari Ailus return rval;
231f25d3962SSakari Ailus }
232f25d3962SSakari Ailus
check_ext_bounds(struct device * dev,struct ccs_pll * pll)233f25d3962SSakari Ailus static int check_ext_bounds(struct device *dev, struct ccs_pll *pll)
234f25d3962SSakari Ailus {
23538c94eb8SSakari Ailus if (!(pll->flags & CCS_PLL_FLAG_FIFO_DERATING) &&
23638c94eb8SSakari Ailus pll->pixel_rate_pixel_array > pll->pixel_rate_csi) {
23738c94eb8SSakari Ailus dev_dbg(dev, "device does not support derating\n");
23838c94eb8SSakari Ailus return -EINVAL;
23938c94eb8SSakari Ailus }
24038c94eb8SSakari Ailus
24138c94eb8SSakari Ailus if (!(pll->flags & CCS_PLL_FLAG_FIFO_OVERRATING) &&
24238c94eb8SSakari Ailus pll->pixel_rate_pixel_array < pll->pixel_rate_csi) {
24338c94eb8SSakari Ailus dev_dbg(dev, "device does not support overrating\n");
24438c94eb8SSakari Ailus return -EINVAL;
24538c94eb8SSakari Ailus }
24638c94eb8SSakari Ailus
247f25d3962SSakari Ailus return 0;
2489e05bbacSSakari Ailus }
2499e05bbacSSakari Ailus
2509ec6e5b1SSakari Ailus static void
ccs_pll_find_vt_sys_div(struct device * dev,const struct ccs_pll_limits * lim,struct ccs_pll * pll,struct ccs_pll_branch_fr * pll_fr,u16 min_vt_div,u16 max_vt_div,u16 * min_sys_div,u16 * max_sys_div)2519ec6e5b1SSakari Ailus ccs_pll_find_vt_sys_div(struct device *dev, const struct ccs_pll_limits *lim,
2529ec6e5b1SSakari Ailus struct ccs_pll *pll, struct ccs_pll_branch_fr *pll_fr,
2538a75e8dcSSakari Ailus u16 min_vt_div, u16 max_vt_div,
2548a75e8dcSSakari Ailus u16 *min_sys_div, u16 *max_sys_div)
2559ec6e5b1SSakari Ailus {
2569ec6e5b1SSakari Ailus /*
2579ec6e5b1SSakari Ailus * Find limits for sys_clk_div. Not all values are possible with all
2589ec6e5b1SSakari Ailus * values of pix_clk_div.
2599ec6e5b1SSakari Ailus */
2609ec6e5b1SSakari Ailus *min_sys_div = lim->vt_bk.min_sys_clk_div;
2619ec6e5b1SSakari Ailus dev_dbg(dev, "min_sys_div: %u\n", *min_sys_div);
2628a75e8dcSSakari Ailus *min_sys_div = max_t(u16, *min_sys_div,
2639ec6e5b1SSakari Ailus DIV_ROUND_UP(min_vt_div,
2649ec6e5b1SSakari Ailus lim->vt_bk.max_pix_clk_div));
2659ec6e5b1SSakari Ailus dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %u\n", *min_sys_div);
2668a75e8dcSSakari Ailus *min_sys_div = max_t(u16, *min_sys_div,
2679ec6e5b1SSakari Ailus pll_fr->pll_op_clk_freq_hz
2689ec6e5b1SSakari Ailus / lim->vt_bk.max_sys_clk_freq_hz);
2699ec6e5b1SSakari Ailus dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %u\n", *min_sys_div);
2709ec6e5b1SSakari Ailus *min_sys_div = clk_div_even_up(*min_sys_div);
2719ec6e5b1SSakari Ailus dev_dbg(dev, "min_sys_div: one or even: %u\n", *min_sys_div);
2729ec6e5b1SSakari Ailus
2739ec6e5b1SSakari Ailus *max_sys_div = lim->vt_bk.max_sys_clk_div;
2749ec6e5b1SSakari Ailus dev_dbg(dev, "max_sys_div: %u\n", *max_sys_div);
2758a75e8dcSSakari Ailus *max_sys_div = min_t(u16, *max_sys_div,
2769ec6e5b1SSakari Ailus DIV_ROUND_UP(max_vt_div,
2779ec6e5b1SSakari Ailus lim->vt_bk.min_pix_clk_div));
2789ec6e5b1SSakari Ailus dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %u\n", *max_sys_div);
2798a75e8dcSSakari Ailus *max_sys_div = min_t(u16, *max_sys_div,
2809ec6e5b1SSakari Ailus DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz,
2819ec6e5b1SSakari Ailus lim->vt_bk.min_pix_clk_freq_hz));
2829ec6e5b1SSakari Ailus dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %u\n", *max_sys_div);
2839ec6e5b1SSakari Ailus }
2849ec6e5b1SSakari Ailus
2858030aa4fSSakari Ailus #define CPHY_CONST 7
2868030aa4fSSakari Ailus #define DPHY_CONST 16
2878030aa4fSSakari Ailus #define PHY_CONST_DIV 16
2888030aa4fSSakari Ailus
2896c7469e4SSakari Ailus static inline int
__ccs_pll_calculate_vt_tree(struct device * dev,const struct ccs_pll_limits * lim,struct ccs_pll * pll,u32 mul,u32 div)2906c7469e4SSakari Ailus __ccs_pll_calculate_vt_tree(struct device *dev,
2916c7469e4SSakari Ailus const struct ccs_pll_limits *lim,
2928a75e8dcSSakari Ailus struct ccs_pll *pll, u32 mul, u32 div)
2936c7469e4SSakari Ailus {
2946c7469e4SSakari Ailus const struct ccs_pll_branch_limits_fr *lim_fr = &lim->vt_fr;
2956c7469e4SSakari Ailus const struct ccs_pll_branch_limits_bk *lim_bk = &lim->vt_bk;
2966c7469e4SSakari Ailus struct ccs_pll_branch_fr *pll_fr = &pll->vt_fr;
2976c7469e4SSakari Ailus struct ccs_pll_branch_bk *pll_bk = &pll->vt_bk;
2988a75e8dcSSakari Ailus u32 more_mul;
299*697bef6cSSakari Ailus u16 best_pix_div = SHRT_MAX >> 1, best_div = lim_bk->max_sys_clk_div;
3008a75e8dcSSakari Ailus u16 vt_div, min_sys_div, max_sys_div, sys_div;
3016c7469e4SSakari Ailus
3026c7469e4SSakari Ailus pll_fr->pll_ip_clk_freq_hz =
3036c7469e4SSakari Ailus pll->ext_clk_freq_hz / pll_fr->pre_pll_clk_div;
3046c7469e4SSakari Ailus
3056c7469e4SSakari Ailus dev_dbg(dev, "vt_pll_ip_clk_freq_hz %u\n", pll_fr->pll_ip_clk_freq_hz);
3066c7469e4SSakari Ailus
3076c7469e4SSakari Ailus more_mul = one_or_more(DIV_ROUND_UP(lim_fr->min_pll_op_clk_freq_hz,
3086c7469e4SSakari Ailus pll_fr->pll_ip_clk_freq_hz * mul));
3096c7469e4SSakari Ailus
3106c7469e4SSakari Ailus dev_dbg(dev, "more_mul: %u\n", more_mul);
3116c7469e4SSakari Ailus more_mul *= DIV_ROUND_UP(lim_fr->min_pll_multiplier, mul * more_mul);
3126c7469e4SSakari Ailus dev_dbg(dev, "more_mul2: %u\n", more_mul);
3136c7469e4SSakari Ailus
3146c7469e4SSakari Ailus pll_fr->pll_multiplier = mul * more_mul;
3156c7469e4SSakari Ailus
3166c7469e4SSakari Ailus if (pll_fr->pll_multiplier * pll_fr->pll_ip_clk_freq_hz >
3176c7469e4SSakari Ailus lim_fr->max_pll_op_clk_freq_hz)
3186c7469e4SSakari Ailus return -EINVAL;
3196c7469e4SSakari Ailus
3206c7469e4SSakari Ailus pll_fr->pll_op_clk_freq_hz =
3216c7469e4SSakari Ailus pll_fr->pll_ip_clk_freq_hz * pll_fr->pll_multiplier;
3226c7469e4SSakari Ailus
3236c7469e4SSakari Ailus vt_div = div * more_mul;
3246c7469e4SSakari Ailus
3256c7469e4SSakari Ailus ccs_pll_find_vt_sys_div(dev, lim, pll, pll_fr, vt_div, vt_div,
3266c7469e4SSakari Ailus &min_sys_div, &max_sys_div);
3276c7469e4SSakari Ailus
3286c7469e4SSakari Ailus max_sys_div = (vt_div & 1) ? 1 : max_sys_div;
3296c7469e4SSakari Ailus
3306c7469e4SSakari Ailus dev_dbg(dev, "vt min/max_sys_div: %u,%u\n", min_sys_div, max_sys_div);
3316c7469e4SSakari Ailus
3326c7469e4SSakari Ailus for (sys_div = min_sys_div; sys_div <= max_sys_div;
3336c7469e4SSakari Ailus sys_div += 2 - (sys_div & 1)) {
3348a75e8dcSSakari Ailus u16 pix_div;
3356c7469e4SSakari Ailus
3366c7469e4SSakari Ailus if (vt_div % sys_div)
3376c7469e4SSakari Ailus continue;
3386c7469e4SSakari Ailus
3396c7469e4SSakari Ailus pix_div = vt_div / sys_div;
3406c7469e4SSakari Ailus
3416c7469e4SSakari Ailus if (pix_div < lim_bk->min_pix_clk_div ||
3426c7469e4SSakari Ailus pix_div > lim_bk->max_pix_clk_div) {
3436c7469e4SSakari Ailus dev_dbg(dev,
3446c7469e4SSakari Ailus "pix_div %u too small or too big (%u--%u)\n",
3456c7469e4SSakari Ailus pix_div,
3466c7469e4SSakari Ailus lim_bk->min_pix_clk_div,
3476c7469e4SSakari Ailus lim_bk->max_pix_clk_div);
3486c7469e4SSakari Ailus continue;
3496c7469e4SSakari Ailus }
3506c7469e4SSakari Ailus
351b41f2708SSakari Ailus dev_dbg(dev, "sys/pix/best_pix: %u,%u,%u\n", sys_div, pix_div,
352b41f2708SSakari Ailus best_pix_div);
353b41f2708SSakari Ailus
354b41f2708SSakari Ailus if (pix_div * sys_div <= best_pix_div) {
3556c7469e4SSakari Ailus best_pix_div = pix_div;
3566c7469e4SSakari Ailus best_div = pix_div * sys_div;
3576c7469e4SSakari Ailus }
3586c7469e4SSakari Ailus }
3596c7469e4SSakari Ailus if (best_pix_div == SHRT_MAX >> 1)
3606c7469e4SSakari Ailus return -EINVAL;
3616c7469e4SSakari Ailus
3626c7469e4SSakari Ailus pll_bk->sys_clk_div = best_div / best_pix_div;
3636c7469e4SSakari Ailus pll_bk->pix_clk_div = best_pix_div;
3646c7469e4SSakari Ailus
3656c7469e4SSakari Ailus pll_bk->sys_clk_freq_hz =
3666c7469e4SSakari Ailus pll_fr->pll_op_clk_freq_hz / pll_bk->sys_clk_div;
3676c7469e4SSakari Ailus pll_bk->pix_clk_freq_hz =
3686c7469e4SSakari Ailus pll_bk->sys_clk_freq_hz / pll_bk->pix_clk_div;
3696c7469e4SSakari Ailus
3706c7469e4SSakari Ailus pll->pixel_rate_pixel_array =
3716c7469e4SSakari Ailus pll_bk->pix_clk_freq_hz * pll->vt_lanes;
3726c7469e4SSakari Ailus
3736c7469e4SSakari Ailus return 0;
3746c7469e4SSakari Ailus }
3756c7469e4SSakari Ailus
ccs_pll_calculate_vt_tree(struct device * dev,const struct ccs_pll_limits * lim,struct ccs_pll * pll)3766c7469e4SSakari Ailus static int ccs_pll_calculate_vt_tree(struct device *dev,
3776c7469e4SSakari Ailus const struct ccs_pll_limits *lim,
3786c7469e4SSakari Ailus struct ccs_pll *pll)
3796c7469e4SSakari Ailus {
3806c7469e4SSakari Ailus const struct ccs_pll_branch_limits_fr *lim_fr = &lim->vt_fr;
3816c7469e4SSakari Ailus struct ccs_pll_branch_fr *pll_fr = &pll->vt_fr;
3828a75e8dcSSakari Ailus u16 min_pre_pll_clk_div = lim_fr->min_pre_pll_clk_div;
3838a75e8dcSSakari Ailus u16 max_pre_pll_clk_div = lim_fr->max_pre_pll_clk_div;
3848a75e8dcSSakari Ailus u32 pre_mul, pre_div;
3856c7469e4SSakari Ailus
3866c7469e4SSakari Ailus pre_div = gcd(pll->pixel_rate_csi,
3876c7469e4SSakari Ailus pll->ext_clk_freq_hz * pll->vt_lanes);
3886c7469e4SSakari Ailus pre_mul = pll->pixel_rate_csi / pre_div;
3896c7469e4SSakari Ailus pre_div = pll->ext_clk_freq_hz * pll->vt_lanes / pre_div;
3906c7469e4SSakari Ailus
3916c7469e4SSakari Ailus /* Make sure PLL input frequency is within limits */
3926c7469e4SSakari Ailus max_pre_pll_clk_div =
3938a75e8dcSSakari Ailus min_t(u16, max_pre_pll_clk_div,
3946c7469e4SSakari Ailus DIV_ROUND_UP(pll->ext_clk_freq_hz,
3956c7469e4SSakari Ailus lim_fr->min_pll_ip_clk_freq_hz));
3966c7469e4SSakari Ailus
3978a75e8dcSSakari Ailus min_pre_pll_clk_div = max_t(u16, min_pre_pll_clk_div,
3986c7469e4SSakari Ailus pll->ext_clk_freq_hz /
3996c7469e4SSakari Ailus lim_fr->max_pll_ip_clk_freq_hz);
4006c7469e4SSakari Ailus
4016c7469e4SSakari Ailus dev_dbg(dev, "vt min/max_pre_pll_clk_div: %u,%u\n",
4026c7469e4SSakari Ailus min_pre_pll_clk_div, max_pre_pll_clk_div);
4036c7469e4SSakari Ailus
4046c7469e4SSakari Ailus for (pll_fr->pre_pll_clk_div = min_pre_pll_clk_div;
4056c7469e4SSakari Ailus pll_fr->pre_pll_clk_div <= max_pre_pll_clk_div;
4066c7469e4SSakari Ailus pll_fr->pre_pll_clk_div +=
4076c7469e4SSakari Ailus (pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER) ? 1 :
4086c7469e4SSakari Ailus 2 - (pll_fr->pre_pll_clk_div & 1)) {
4098a75e8dcSSakari Ailus u32 mul, div;
4106c7469e4SSakari Ailus int rval;
4116c7469e4SSakari Ailus
4126c7469e4SSakari Ailus div = gcd(pre_mul * pll_fr->pre_pll_clk_div, pre_div);
4136c7469e4SSakari Ailus mul = pre_mul * pll_fr->pre_pll_clk_div / div;
4146c7469e4SSakari Ailus div = pre_div / div;
4156c7469e4SSakari Ailus
4166c7469e4SSakari Ailus dev_dbg(dev, "vt pre-div/mul/div: %u,%u,%u\n",
4176c7469e4SSakari Ailus pll_fr->pre_pll_clk_div, mul, div);
4186c7469e4SSakari Ailus
4196c7469e4SSakari Ailus rval = __ccs_pll_calculate_vt_tree(dev, lim, pll,
4206c7469e4SSakari Ailus mul, div);
4216c7469e4SSakari Ailus if (rval)
4226c7469e4SSakari Ailus continue;
4236c7469e4SSakari Ailus
4246c7469e4SSakari Ailus rval = check_fr_bounds(dev, lim, pll, PLL_VT);
4256c7469e4SSakari Ailus if (rval)
4266c7469e4SSakari Ailus continue;
4276c7469e4SSakari Ailus
4286c7469e4SSakari Ailus rval = check_bk_bounds(dev, lim, pll, PLL_VT);
4296c7469e4SSakari Ailus if (rval)
4306c7469e4SSakari Ailus continue;
4316c7469e4SSakari Ailus
4326c7469e4SSakari Ailus return 0;
4336c7469e4SSakari Ailus }
4346c7469e4SSakari Ailus
4356c7469e4SSakari Ailus return -EINVAL;
4366c7469e4SSakari Ailus }
4376c7469e4SSakari Ailus
4383e2db036SSakari Ailus static void
ccs_pll_calculate_vt(struct device * dev,const struct ccs_pll_limits * lim,const struct ccs_pll_branch_limits_bk * op_lim_bk,struct ccs_pll * pll,struct ccs_pll_branch_fr * pll_fr,struct ccs_pll_branch_bk * op_pll_bk,bool cphy,u32 phy_const)439a38836b2SSakari Ailus ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim,
4403e2db036SSakari Ailus const struct ccs_pll_branch_limits_bk *op_lim_bk,
4413e2db036SSakari Ailus struct ccs_pll *pll, struct ccs_pll_branch_fr *pll_fr,
4423e2db036SSakari Ailus struct ccs_pll_branch_bk *op_pll_bk, bool cphy,
4438a75e8dcSSakari Ailus u32 phy_const)
4443e2db036SSakari Ailus {
4458a75e8dcSSakari Ailus u16 sys_div;
4468a75e8dcSSakari Ailus u16 best_pix_div = SHRT_MAX >> 1;
4478a75e8dcSSakari Ailus u16 vt_op_binning_div;
4488a75e8dcSSakari Ailus u16 min_vt_div, max_vt_div, vt_div;
4498a75e8dcSSakari Ailus u16 min_sys_div, max_sys_div;
4503e2db036SSakari Ailus
451a38836b2SSakari Ailus if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS)
452a38836b2SSakari Ailus goto out_calc_pixel_rate;
453a38836b2SSakari Ailus
4543e2db036SSakari Ailus /*
45538c94eb8SSakari Ailus * Find out whether a sensor supports derating. If it does not, VT and
45638c94eb8SSakari Ailus * OP domains are required to run at the same pixel rate.
45738c94eb8SSakari Ailus */
45838c94eb8SSakari Ailus if (!(pll->flags & CCS_PLL_FLAG_FIFO_DERATING)) {
45938c94eb8SSakari Ailus min_vt_div =
46038c94eb8SSakari Ailus op_pll_bk->sys_clk_div * op_pll_bk->pix_clk_div
461900c33e8SSakari Ailus * pll->vt_lanes * phy_const / pll->op_lanes
462900c33e8SSakari Ailus / (PHY_CONST_DIV << op_pix_ddr(pll->flags));
46338c94eb8SSakari Ailus } else {
46438c94eb8SSakari Ailus /*
4653e2db036SSakari Ailus * Some sensors perform analogue binning and some do this
4663e2db036SSakari Ailus * digitally. The ones doing this digitally can be roughly be
4673e2db036SSakari Ailus * found out using this formula. The ones doing this digitally
4683e2db036SSakari Ailus * should run at higher clock rate, so smaller divisor is used
4693e2db036SSakari Ailus * on video timing side.
4703e2db036SSakari Ailus */
4713e2db036SSakari Ailus if (lim->min_line_length_pck_bin > lim->min_line_length_pck
4723e2db036SSakari Ailus / pll->binning_horizontal)
4733e2db036SSakari Ailus vt_op_binning_div = pll->binning_horizontal;
4743e2db036SSakari Ailus else
4753e2db036SSakari Ailus vt_op_binning_div = 1;
4763e2db036SSakari Ailus dev_dbg(dev, "vt_op_binning_div: %u\n", vt_op_binning_div);
4773e2db036SSakari Ailus
4783e2db036SSakari Ailus /*
4793e2db036SSakari Ailus * Profile 2 supports vt_pix_clk_div E [4, 10]
4803e2db036SSakari Ailus *
4813e2db036SSakari Ailus * Horizontal binning can be used as a base for difference in
4823e2db036SSakari Ailus * divisors. One must make sure that horizontal blanking is
4833e2db036SSakari Ailus * enough to accommodate the CSI-2 sync codes.
4843e2db036SSakari Ailus *
4853e2db036SSakari Ailus * Take scaling factor and number of VT lanes into account as well.
4863e2db036SSakari Ailus *
4873e2db036SSakari Ailus * Find absolute limits for the factor of vt divider.
4883e2db036SSakari Ailus */
4893e2db036SSakari Ailus dev_dbg(dev, "scale_m: %u\n", pll->scale_m);
49038c94eb8SSakari Ailus min_vt_div =
49138c94eb8SSakari Ailus DIV_ROUND_UP(pll->bits_per_pixel
49238c94eb8SSakari Ailus * op_pll_bk->sys_clk_div * pll->scale_n
49338c94eb8SSakari Ailus * pll->vt_lanes * phy_const,
49438c94eb8SSakari Ailus (pll->flags &
49538c94eb8SSakari Ailus CCS_PLL_FLAG_LANE_SPEED_MODEL ?
4963e2db036SSakari Ailus pll->csi2.lanes : 1)
4973e2db036SSakari Ailus * vt_op_binning_div * pll->scale_m
498900c33e8SSakari Ailus * PHY_CONST_DIV << op_pix_ddr(pll->flags));
49938c94eb8SSakari Ailus }
5003e2db036SSakari Ailus
5013e2db036SSakari Ailus /* Find smallest and biggest allowed vt divisor. */
5023e2db036SSakari Ailus dev_dbg(dev, "min_vt_div: %u\n", min_vt_div);
5038a75e8dcSSakari Ailus min_vt_div = max_t(u16, min_vt_div,
5043e2db036SSakari Ailus DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz,
5053e2db036SSakari Ailus lim->vt_bk.max_pix_clk_freq_hz));
5063e2db036SSakari Ailus dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %u\n",
5073e2db036SSakari Ailus min_vt_div);
5088a75e8dcSSakari Ailus min_vt_div = max_t(u16, min_vt_div, lim->vt_bk.min_pix_clk_div
5093e2db036SSakari Ailus * lim->vt_bk.min_sys_clk_div);
5103e2db036SSakari Ailus dev_dbg(dev, "min_vt_div: min_vt_clk_div: %u\n", min_vt_div);
5113e2db036SSakari Ailus
5123e2db036SSakari Ailus max_vt_div = lim->vt_bk.max_sys_clk_div * lim->vt_bk.max_pix_clk_div;
5133e2db036SSakari Ailus dev_dbg(dev, "max_vt_div: %u\n", max_vt_div);
5148a75e8dcSSakari Ailus max_vt_div = min_t(u16, max_vt_div,
5153e2db036SSakari Ailus DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz,
5163e2db036SSakari Ailus lim->vt_bk.min_pix_clk_freq_hz));
5173e2db036SSakari Ailus dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %u\n",
5183e2db036SSakari Ailus max_vt_div);
5193e2db036SSakari Ailus
5209ec6e5b1SSakari Ailus ccs_pll_find_vt_sys_div(dev, lim, pll, pll_fr, min_vt_div,
5219ec6e5b1SSakari Ailus max_vt_div, &min_sys_div, &max_sys_div);
5223e2db036SSakari Ailus
5233e2db036SSakari Ailus /*
5243e2db036SSakari Ailus * Find pix_div such that a legal pix_div * sys_div results
5253e2db036SSakari Ailus * into a value which is not smaller than div, the desired
5263e2db036SSakari Ailus * divisor.
5273e2db036SSakari Ailus */
52836154b68SSakari Ailus for (vt_div = min_vt_div; vt_div <= max_vt_div; vt_div++) {
5298a75e8dcSSakari Ailus u16 __max_sys_div = vt_div & 1 ? 1 : max_sys_div;
53036154b68SSakari Ailus
53136154b68SSakari Ailus for (sys_div = min_sys_div; sys_div <= __max_sys_div;
5323e2db036SSakari Ailus sys_div += 2 - (sys_div & 1)) {
5338a75e8dcSSakari Ailus u16 pix_div;
5348a75e8dcSSakari Ailus u16 rounded_div;
5353e2db036SSakari Ailus
53636154b68SSakari Ailus pix_div = DIV_ROUND_UP(vt_div, sys_div);
53736154b68SSakari Ailus
5383e2db036SSakari Ailus if (pix_div < lim->vt_bk.min_pix_clk_div
5393e2db036SSakari Ailus || pix_div > lim->vt_bk.max_pix_clk_div) {
5403e2db036SSakari Ailus dev_dbg(dev,
5413e2db036SSakari Ailus "pix_div %u too small or too big (%u--%u)\n",
5423e2db036SSakari Ailus pix_div,
5433e2db036SSakari Ailus lim->vt_bk.min_pix_clk_div,
5443e2db036SSakari Ailus lim->vt_bk.max_pix_clk_div);
5453e2db036SSakari Ailus continue;
5463e2db036SSakari Ailus }
5473e2db036SSakari Ailus
5483e2db036SSakari Ailus rounded_div = roundup(vt_div, best_pix_div);
5493e2db036SSakari Ailus
5503e2db036SSakari Ailus /* Check if this one is better. */
5513e2db036SSakari Ailus if (pix_div * sys_div <= rounded_div)
5523e2db036SSakari Ailus best_pix_div = pix_div;
5533e2db036SSakari Ailus
5543e2db036SSakari Ailus /* Bail out if we've already found the best value. */
5553e2db036SSakari Ailus if (vt_div == rounded_div)
5563e2db036SSakari Ailus break;
5573e2db036SSakari Ailus }
558594f1e93SSakari Ailus if (best_pix_div < SHRT_MAX >> 1)
5593e2db036SSakari Ailus break;
5603e2db036SSakari Ailus }
5613e2db036SSakari Ailus
5623e2db036SSakari Ailus pll->vt_bk.sys_clk_div = DIV_ROUND_UP(vt_div, best_pix_div);
5633e2db036SSakari Ailus pll->vt_bk.pix_clk_div = best_pix_div;
5643e2db036SSakari Ailus
5653e2db036SSakari Ailus pll->vt_bk.sys_clk_freq_hz =
5663e2db036SSakari Ailus pll_fr->pll_op_clk_freq_hz / pll->vt_bk.sys_clk_div;
5673e2db036SSakari Ailus pll->vt_bk.pix_clk_freq_hz =
5683e2db036SSakari Ailus pll->vt_bk.sys_clk_freq_hz / pll->vt_bk.pix_clk_div;
569a38836b2SSakari Ailus
570a38836b2SSakari Ailus out_calc_pixel_rate:
571a38836b2SSakari Ailus pll->pixel_rate_pixel_array =
572a38836b2SSakari Ailus pll->vt_bk.pix_clk_freq_hz * pll->vt_lanes;
5733e2db036SSakari Ailus }
5743e2db036SSakari Ailus
5759e05bbacSSakari Ailus /*
5769e05bbacSSakari Ailus * Heuristically guess the PLL tree for a given common multiplier and
5779e05bbacSSakari Ailus * divisor. Begin with the operational timing and continue to video
5789e05bbacSSakari Ailus * timing once operational timing has been verified.
5799e05bbacSSakari Ailus *
5809e05bbacSSakari Ailus * @mul is the PLL multiplier and @div is the common divisor
5819e05bbacSSakari Ailus * (pre_pll_clk_div and op_sys_clk_div combined). The final PLL
5829e05bbacSSakari Ailus * multiplier will be a multiple of @mul.
5839e05bbacSSakari Ailus *
5849e05bbacSSakari Ailus * @return Zero on success, error code on error.
5859e05bbacSSakari Ailus */
5869e05bbacSSakari Ailus static int
ccs_pll_calculate_op(struct device * dev,const struct ccs_pll_limits * lim,const struct ccs_pll_branch_limits_fr * op_lim_fr,const struct ccs_pll_branch_limits_bk * op_lim_bk,struct ccs_pll * pll,struct ccs_pll_branch_fr * op_pll_fr,struct ccs_pll_branch_bk * op_pll_bk,u32 mul,u32 div,u32 op_sys_clk_freq_hz_sdr,u32 l,bool cphy,u32 phy_const)587a38836b2SSakari Ailus ccs_pll_calculate_op(struct device *dev, const struct ccs_pll_limits *lim,
588415ddd99SSakari Ailus const struct ccs_pll_branch_limits_fr *op_lim_fr,
589415ddd99SSakari Ailus const struct ccs_pll_branch_limits_bk *op_lim_bk,
590415ddd99SSakari Ailus struct ccs_pll *pll, struct ccs_pll_branch_fr *op_pll_fr,
5918a75e8dcSSakari Ailus struct ccs_pll_branch_bk *op_pll_bk, u32 mul,
5928a75e8dcSSakari Ailus u32 div, u32 op_sys_clk_freq_hz_sdr, u32 l,
5938a75e8dcSSakari Ailus bool cphy, u32 phy_const)
5949e05bbacSSakari Ailus {
5959e05bbacSSakari Ailus /*
5969e05bbacSSakari Ailus * Higher multipliers (and divisors) are often required than
5979e05bbacSSakari Ailus * necessitated by the external clock and the output clocks.
5989e05bbacSSakari Ailus * There are limits for all values in the clock tree. These
5999e05bbacSSakari Ailus * are the minimum and maximum multiplier for mul.
6009e05bbacSSakari Ailus */
6018a75e8dcSSakari Ailus u32 more_mul_min, more_mul_max;
6028a75e8dcSSakari Ailus u32 more_mul_factor;
6038a75e8dcSSakari Ailus u32 i;
6049e05bbacSSakari Ailus
6059e05bbacSSakari Ailus /*
6069e05bbacSSakari Ailus * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be
6079e05bbacSSakari Ailus * too high.
6089e05bbacSSakari Ailus */
609415ddd99SSakari Ailus dev_dbg(dev, "op_pre_pll_clk_div %u\n", op_pll_fr->pre_pll_clk_div);
6109e05bbacSSakari Ailus
6119e05bbacSSakari Ailus /* Don't go above max pll multiplier. */
612415ddd99SSakari Ailus more_mul_max = op_lim_fr->max_pll_multiplier / mul;
613415ddd99SSakari Ailus dev_dbg(dev, "more_mul_max: max_op_pll_multiplier check: %u\n",
6149e05bbacSSakari Ailus more_mul_max);
6159e05bbacSSakari Ailus /* Don't go above max pll op frequency. */
6169e05bbacSSakari Ailus more_mul_max =
6178a75e8dcSSakari Ailus min_t(u32,
6189e05bbacSSakari Ailus more_mul_max,
619415ddd99SSakari Ailus op_lim_fr->max_pll_op_clk_freq_hz
620ae502e08SSakari Ailus / (pll->ext_clk_freq_hz /
621ae502e08SSakari Ailus op_pll_fr->pre_pll_clk_div * mul));
622415ddd99SSakari Ailus dev_dbg(dev, "more_mul_max: max_pll_op_clk_freq_hz check: %u\n",
6239e05bbacSSakari Ailus more_mul_max);
6249e05bbacSSakari Ailus /* Don't go above the division capability of op sys clock divider. */
6259e05bbacSSakari Ailus more_mul_max = min(more_mul_max,
626415ddd99SSakari Ailus op_lim_bk->max_sys_clk_div * op_pll_fr->pre_pll_clk_div
6279e05bbacSSakari Ailus / div);
6289e05bbacSSakari Ailus dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %u\n",
6299e05bbacSSakari Ailus more_mul_max);
630c64cf71dSSakari Ailus /* Ensure we won't go above max_pll_multiplier. */
63182ab97c8SSakari Ailus more_mul_max = min(more_mul_max, op_lim_fr->max_pll_multiplier / mul);
6329e05bbacSSakari Ailus dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %u\n",
6339e05bbacSSakari Ailus more_mul_max);
6349e05bbacSSakari Ailus
635415ddd99SSakari Ailus /* Ensure we won't go below min_pll_op_clk_freq_hz. */
636415ddd99SSakari Ailus more_mul_min = DIV_ROUND_UP(op_lim_fr->min_pll_op_clk_freq_hz,
637415ddd99SSakari Ailus pll->ext_clk_freq_hz /
638415ddd99SSakari Ailus op_pll_fr->pre_pll_clk_div * mul);
639415ddd99SSakari Ailus dev_dbg(dev, "more_mul_min: min_op_pll_op_clk_freq_hz check: %u\n",
6409e05bbacSSakari Ailus more_mul_min);
6419e05bbacSSakari Ailus /* Ensure we won't go below min_pll_multiplier. */
6429e05bbacSSakari Ailus more_mul_min = max(more_mul_min,
643415ddd99SSakari Ailus DIV_ROUND_UP(op_lim_fr->min_pll_multiplier, mul));
644415ddd99SSakari Ailus dev_dbg(dev, "more_mul_min: min_op_pll_multiplier check: %u\n",
6459e05bbacSSakari Ailus more_mul_min);
6469e05bbacSSakari Ailus
6479e05bbacSSakari Ailus if (more_mul_min > more_mul_max) {
6489e05bbacSSakari Ailus dev_dbg(dev,
6499e05bbacSSakari Ailus "unable to compute more_mul_min and more_mul_max\n");
6509e05bbacSSakari Ailus return -EINVAL;
6519e05bbacSSakari Ailus }
6529e05bbacSSakari Ailus
653415ddd99SSakari Ailus more_mul_factor = lcm(div, op_pll_fr->pre_pll_clk_div) / div;
6549e05bbacSSakari Ailus dev_dbg(dev, "more_mul_factor: %u\n", more_mul_factor);
655415ddd99SSakari Ailus more_mul_factor = lcm(more_mul_factor, op_lim_bk->min_sys_clk_div);
6569e05bbacSSakari Ailus dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n",
6579e05bbacSSakari Ailus more_mul_factor);
6589e05bbacSSakari Ailus i = roundup(more_mul_min, more_mul_factor);
6599e05bbacSSakari Ailus if (!is_one_or_even(i))
6609e05bbacSSakari Ailus i <<= 1;
6619e05bbacSSakari Ailus
6629e05bbacSSakari Ailus dev_dbg(dev, "final more_mul: %u\n", i);
6639e05bbacSSakari Ailus if (i > more_mul_max) {
6649e05bbacSSakari Ailus dev_dbg(dev, "final more_mul is bad, max %u\n", more_mul_max);
6659e05bbacSSakari Ailus return -EINVAL;
6669e05bbacSSakari Ailus }
6679e05bbacSSakari Ailus
668415ddd99SSakari Ailus op_pll_fr->pll_multiplier = mul * i;
669415ddd99SSakari Ailus op_pll_bk->sys_clk_div = div * i / op_pll_fr->pre_pll_clk_div;
670415ddd99SSakari Ailus dev_dbg(dev, "op_sys_clk_div: %u\n", op_pll_bk->sys_clk_div);
6719e05bbacSSakari Ailus
672415ddd99SSakari Ailus op_pll_fr->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz
673415ddd99SSakari Ailus / op_pll_fr->pre_pll_clk_div;
6749e05bbacSSakari Ailus
675415ddd99SSakari Ailus op_pll_fr->pll_op_clk_freq_hz = op_pll_fr->pll_ip_clk_freq_hz
676415ddd99SSakari Ailus * op_pll_fr->pll_multiplier;
6779e05bbacSSakari Ailus
678c4c0b222SSakari Ailus if (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL)
679900c33e8SSakari Ailus op_pll_bk->pix_clk_div =
680900c33e8SSakari Ailus (pll->bits_per_pixel
681900c33e8SSakari Ailus * pll->op_lanes * (phy_const << op_sys_ddr(pll->flags))
682900c33e8SSakari Ailus / PHY_CONST_DIV / pll->csi2.lanes / l)
683900c33e8SSakari Ailus >> op_pix_ddr(pll->flags);
684c4c0b222SSakari Ailus else
6858030aa4fSSakari Ailus op_pll_bk->pix_clk_div =
686900c33e8SSakari Ailus (pll->bits_per_pixel
687900c33e8SSakari Ailus * (phy_const << op_sys_ddr(pll->flags))
688900c33e8SSakari Ailus / PHY_CONST_DIV / l) >> op_pix_ddr(pll->flags);
689c4c0b222SSakari Ailus
690415ddd99SSakari Ailus op_pll_bk->pix_clk_freq_hz =
691900c33e8SSakari Ailus (op_sys_clk_freq_hz_sdr >> op_pix_ddr(pll->flags))
692900c33e8SSakari Ailus / op_pll_bk->pix_clk_div;
693900c33e8SSakari Ailus op_pll_bk->sys_clk_freq_hz =
694900c33e8SSakari Ailus op_sys_clk_freq_hz_sdr >> op_sys_ddr(pll->flags);
695c4c0b222SSakari Ailus
696cac8f5d2SSakari Ailus dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll_bk->pix_clk_div);
697cac8f5d2SSakari Ailus
698a38836b2SSakari Ailus return 0;
6999e05bbacSSakari Ailus }
7009e05bbacSSakari Ailus
ccs_pll_calculate(struct device * dev,const struct ccs_pll_limits * lim,struct ccs_pll * pll)701415ddd99SSakari Ailus int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
7029e05bbacSSakari Ailus struct ccs_pll *pll)
7039e05bbacSSakari Ailus {
7046c7469e4SSakari Ailus const struct ccs_pll_branch_limits_fr *op_lim_fr;
7056c7469e4SSakari Ailus const struct ccs_pll_branch_limits_bk *op_lim_bk;
7066c7469e4SSakari Ailus struct ccs_pll_branch_fr *op_pll_fr;
7076c7469e4SSakari Ailus struct ccs_pll_branch_bk *op_pll_bk;
7088030aa4fSSakari Ailus bool cphy = pll->bus_type == CCS_PLL_BUS_TYPE_CSI2_CPHY;
7098a75e8dcSSakari Ailus u32 phy_const = cphy ? CPHY_CONST : DPHY_CONST;
7108a75e8dcSSakari Ailus u32 op_sys_clk_freq_hz_sdr;
7118a75e8dcSSakari Ailus u16 min_op_pre_pll_clk_div;
7128a75e8dcSSakari Ailus u16 max_op_pre_pll_clk_div;
7138a75e8dcSSakari Ailus u32 mul, div;
7148a75e8dcSSakari Ailus u32 l = (!pll->op_bits_per_lane ||
715c4c0b222SSakari Ailus pll->op_bits_per_lane >= pll->bits_per_pixel) ? 1 : 2;
7168a75e8dcSSakari Ailus u32 i;
7179e05bbacSSakari Ailus int rval = -EINVAL;
7189e05bbacSSakari Ailus
719cac8f5d2SSakari Ailus if (!(pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL)) {
720cac8f5d2SSakari Ailus pll->op_lanes = 1;
721cac8f5d2SSakari Ailus pll->vt_lanes = 1;
722cac8f5d2SSakari Ailus }
7239490a227SSakari Ailus
7246c7469e4SSakari Ailus if (pll->flags & CCS_PLL_FLAG_DUAL_PLL) {
7256c7469e4SSakari Ailus op_lim_fr = &lim->op_fr;
7266c7469e4SSakari Ailus op_lim_bk = &lim->op_bk;
7276c7469e4SSakari Ailus op_pll_fr = &pll->op_fr;
7286c7469e4SSakari Ailus op_pll_bk = &pll->op_bk;
7296c7469e4SSakari Ailus } else if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) {
7306c7469e4SSakari Ailus /*
7316c7469e4SSakari Ailus * If there's no OP PLL at all, use the VT values
7326c7469e4SSakari Ailus * instead. The OP values are ignored for the rest of
7336c7469e4SSakari Ailus * the PLL calculation.
7346c7469e4SSakari Ailus */
7356c7469e4SSakari Ailus op_lim_fr = &lim->vt_fr;
7366c7469e4SSakari Ailus op_lim_bk = &lim->vt_bk;
7376c7469e4SSakari Ailus op_pll_fr = &pll->vt_fr;
7386c7469e4SSakari Ailus op_pll_bk = &pll->vt_bk;
7396c7469e4SSakari Ailus } else {
7406c7469e4SSakari Ailus op_lim_fr = &lim->vt_fr;
7416c7469e4SSakari Ailus op_lim_bk = &lim->op_bk;
7426c7469e4SSakari Ailus op_pll_fr = &pll->vt_fr;
7436c7469e4SSakari Ailus op_pll_bk = &pll->op_bk;
7446c7469e4SSakari Ailus }
7456c7469e4SSakari Ailus
746d7172c0eSSakari Ailus if (!pll->op_lanes || !pll->vt_lanes || !pll->bits_per_pixel ||
747d7172c0eSSakari Ailus !pll->ext_clk_freq_hz || !pll->link_freq || !pll->scale_m ||
748d7172c0eSSakari Ailus !op_lim_fr->min_pll_ip_clk_freq_hz ||
749d7172c0eSSakari Ailus !op_lim_fr->max_pll_ip_clk_freq_hz ||
750d7172c0eSSakari Ailus !op_lim_fr->min_pll_op_clk_freq_hz ||
751d7172c0eSSakari Ailus !op_lim_fr->max_pll_op_clk_freq_hz ||
752d7172c0eSSakari Ailus !op_lim_bk->max_sys_clk_div || !op_lim_fr->max_pll_multiplier)
753d7172c0eSSakari Ailus return -EINVAL;
754d7172c0eSSakari Ailus
7559490a227SSakari Ailus /*
7569490a227SSakari Ailus * Make sure op_pix_clk_div will be integer --- unless flexible
7579490a227SSakari Ailus * op_pix_clk_div is supported
7589490a227SSakari Ailus */
7599490a227SSakari Ailus if (!(pll->flags & CCS_PLL_FLAG_FLEXIBLE_OP_PIX_CLK_DIV) &&
760900c33e8SSakari Ailus (pll->bits_per_pixel * pll->op_lanes) %
761900c33e8SSakari Ailus (pll->csi2.lanes * l << op_pix_ddr(pll->flags))) {
7629490a227SSakari Ailus dev_dbg(dev, "op_pix_clk_div not an integer (bpp %u, op lanes %u, lanes %u, l %u)\n",
7639490a227SSakari Ailus pll->bits_per_pixel, pll->op_lanes, pll->csi2.lanes, l);
7649490a227SSakari Ailus return -EINVAL;
7659490a227SSakari Ailus }
7669490a227SSakari Ailus
767cac8f5d2SSakari Ailus dev_dbg(dev, "vt_lanes: %u\n", pll->vt_lanes);
768cac8f5d2SSakari Ailus dev_dbg(dev, "op_lanes: %u\n", pll->op_lanes);
769cac8f5d2SSakari Ailus
7709e05bbacSSakari Ailus dev_dbg(dev, "binning: %ux%u\n", pll->binning_horizontal,
7719e05bbacSSakari Ailus pll->binning_vertical);
7729e05bbacSSakari Ailus
7739e05bbacSSakari Ailus switch (pll->bus_type) {
77447b6eaf3SSakari Ailus case CCS_PLL_BUS_TYPE_CSI2_DPHY:
7758030aa4fSSakari Ailus case CCS_PLL_BUS_TYPE_CSI2_CPHY:
776ff474accSSakari Ailus op_sys_clk_freq_hz_sdr = pll->link_freq * 2
7778030aa4fSSakari Ailus * (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
7788030aa4fSSakari Ailus 1 : pll->csi2.lanes);
7798030aa4fSSakari Ailus break;
7809e05bbacSSakari Ailus default:
7819e05bbacSSakari Ailus return -EINVAL;
7829e05bbacSSakari Ailus }
7839e05bbacSSakari Ailus
784cac8f5d2SSakari Ailus pll->pixel_rate_csi =
785900c33e8SSakari Ailus div_u64((uint64_t)op_sys_clk_freq_hz_sdr
786cac8f5d2SSakari Ailus * (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
7878030aa4fSSakari Ailus pll->csi2.lanes : 1) * PHY_CONST_DIV,
7888030aa4fSSakari Ailus phy_const * pll->bits_per_pixel * l);
789cac8f5d2SSakari Ailus
790415ddd99SSakari Ailus /* Figure out limits for OP pre-pll divider based on extclk */
791415ddd99SSakari Ailus dev_dbg(dev, "min / max op_pre_pll_clk_div: %u / %u\n",
792415ddd99SSakari Ailus op_lim_fr->min_pre_pll_clk_div, op_lim_fr->max_pre_pll_clk_div);
793415ddd99SSakari Ailus max_op_pre_pll_clk_div =
7948a75e8dcSSakari Ailus min_t(u16, op_lim_fr->max_pre_pll_clk_div,
7959e05bbacSSakari Ailus clk_div_even(pll->ext_clk_freq_hz /
796415ddd99SSakari Ailus op_lim_fr->min_pll_ip_clk_freq_hz));
797415ddd99SSakari Ailus min_op_pre_pll_clk_div =
7988a75e8dcSSakari Ailus max_t(u16, op_lim_fr->min_pre_pll_clk_div,
7999e05bbacSSakari Ailus clk_div_even_up(
8009e05bbacSSakari Ailus DIV_ROUND_UP(pll->ext_clk_freq_hz,
801415ddd99SSakari Ailus op_lim_fr->max_pll_ip_clk_freq_hz)));
802415ddd99SSakari Ailus dev_dbg(dev, "pre-pll check: min / max op_pre_pll_clk_div: %u / %u\n",
803415ddd99SSakari Ailus min_op_pre_pll_clk_div, max_op_pre_pll_clk_div);
8049e05bbacSSakari Ailus
805900c33e8SSakari Ailus i = gcd(op_sys_clk_freq_hz_sdr,
806900c33e8SSakari Ailus pll->ext_clk_freq_hz << op_pix_ddr(pll->flags));
807900c33e8SSakari Ailus mul = op_sys_clk_freq_hz_sdr / i;
808900c33e8SSakari Ailus div = (pll->ext_clk_freq_hz << op_pix_ddr(pll->flags)) / i;
8099e05bbacSSakari Ailus dev_dbg(dev, "mul %u / div %u\n", mul, div);
8109e05bbacSSakari Ailus
811415ddd99SSakari Ailus min_op_pre_pll_clk_div =
8128a75e8dcSSakari Ailus max_t(u16, min_op_pre_pll_clk_div,
8139e05bbacSSakari Ailus clk_div_even_up(
814482e75e7SSakari Ailus mul /
815482e75e7SSakari Ailus one_or_more(
816482e75e7SSakari Ailus DIV_ROUND_UP(op_lim_fr->max_pll_op_clk_freq_hz,
817482e75e7SSakari Ailus pll->ext_clk_freq_hz))));
818415ddd99SSakari Ailus dev_dbg(dev, "pll_op check: min / max op_pre_pll_clk_div: %u / %u\n",
819415ddd99SSakari Ailus min_op_pre_pll_clk_div, max_op_pre_pll_clk_div);
8209e05bbacSSakari Ailus
821415ddd99SSakari Ailus for (op_pll_fr->pre_pll_clk_div = min_op_pre_pll_clk_div;
822415ddd99SSakari Ailus op_pll_fr->pre_pll_clk_div <= max_op_pre_pll_clk_div;
8234e1e8d24SSakari Ailus op_pll_fr->pre_pll_clk_div +=
8244e1e8d24SSakari Ailus (pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER) ? 1 :
8254e1e8d24SSakari Ailus 2 - (op_pll_fr->pre_pll_clk_div & 1)) {
826a38836b2SSakari Ailus rval = ccs_pll_calculate_op(dev, lim, op_lim_fr, op_lim_bk, pll,
827900c33e8SSakari Ailus op_pll_fr, op_pll_bk, mul, div,
828900c33e8SSakari Ailus op_sys_clk_freq_hz_sdr, l, cphy,
829900c33e8SSakari Ailus phy_const);
8309e05bbacSSakari Ailus if (rval)
8319e05bbacSSakari Ailus continue;
8329e05bbacSSakari Ailus
833b41f2708SSakari Ailus rval = check_fr_bounds(dev, lim, pll,
834b41f2708SSakari Ailus pll->flags & CCS_PLL_FLAG_DUAL_PLL ?
835b41f2708SSakari Ailus PLL_OP : PLL_VT);
836f25d3962SSakari Ailus if (rval)
837f25d3962SSakari Ailus continue;
838f25d3962SSakari Ailus
839f25d3962SSakari Ailus rval = check_bk_bounds(dev, lim, pll, PLL_OP);
840f25d3962SSakari Ailus if (rval)
841f25d3962SSakari Ailus continue;
842f25d3962SSakari Ailus
8436c7469e4SSakari Ailus if (pll->flags & CCS_PLL_FLAG_DUAL_PLL)
8446c7469e4SSakari Ailus break;
8456c7469e4SSakari Ailus
846a38836b2SSakari Ailus ccs_pll_calculate_vt(dev, lim, op_lim_bk, pll, op_pll_fr,
847a38836b2SSakari Ailus op_pll_bk, cphy, phy_const);
848a38836b2SSakari Ailus
849f25d3962SSakari Ailus rval = check_bk_bounds(dev, lim, pll, PLL_VT);
850f25d3962SSakari Ailus if (rval)
851f25d3962SSakari Ailus continue;
852f25d3962SSakari Ailus rval = check_ext_bounds(dev, pll);
853a38836b2SSakari Ailus if (rval)
854a38836b2SSakari Ailus continue;
855a38836b2SSakari Ailus
8566c7469e4SSakari Ailus break;
8579e05bbacSSakari Ailus }
8589e05bbacSSakari Ailus
8596c7469e4SSakari Ailus if (rval) {
8609e05bbacSSakari Ailus dev_dbg(dev, "unable to compute pre_pll divisor\n");
8619e05bbacSSakari Ailus
8629e05bbacSSakari Ailus return rval;
8639e05bbacSSakari Ailus }
8646c7469e4SSakari Ailus
8656c7469e4SSakari Ailus if (pll->flags & CCS_PLL_FLAG_DUAL_PLL) {
8666c7469e4SSakari Ailus rval = ccs_pll_calculate_vt_tree(dev, lim, pll);
8676c7469e4SSakari Ailus
8686c7469e4SSakari Ailus if (rval)
8696c7469e4SSakari Ailus return rval;
8706c7469e4SSakari Ailus }
8716c7469e4SSakari Ailus
8726c7469e4SSakari Ailus print_pll(dev, pll);
8736c7469e4SSakari Ailus
8746c7469e4SSakari Ailus return 0;
8756c7469e4SSakari Ailus }
8769e05bbacSSakari Ailus EXPORT_SYMBOL_GPL(ccs_pll_calculate);
8779e05bbacSSakari Ailus
8787389d01cSSakari Ailus MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
8799e05bbacSSakari Ailus MODULE_DESCRIPTION("Generic MIPI CCS/SMIA/SMIA++ PLL calculator");
880ca59318bSSakari Ailus MODULE_LICENSE("GPL");
881