Commit | Line | Data |
---|---|---|
1802d0be | 1 | // SPDX-License-Identifier: GPL-2.0-only |
cf1c5fae | 2 | /* |
9e05bbac | 3 | * drivers/media/i2c/ccs-pll.c |
cf1c5fae | 4 | * |
9e05bbac | 5 | * Generic MIPI CCS/SMIA/SMIA++ PLL calculator |
cf1c5fae | 6 | * |
9e05bbac | 7 | * Copyright (C) 2020 Intel Corporation |
cf1c5fae | 8 | * Copyright (C) 2011--2012 Nokia Corporation |
7389d01c | 9 | * Contact: Sakari Ailus <sakari.ailus@linux.intel.com> |
cf1c5fae SA |
10 | */ |
11 | ||
8c20ee6e | 12 | #include <linux/device.h> |
cf1c5fae SA |
13 | #include <linux/gcd.h> |
14 | #include <linux/lcm.h> | |
15 | #include <linux/module.h> | |
16 | ||
9e05bbac | 17 | #include "ccs-pll.h" |
cf1c5fae SA |
18 | |
19 | /* Return an even number or one. */ | |
8a75e8dc | 20 | static inline u32 clk_div_even(u32 a) |
cf1c5fae | 21 | { |
8a75e8dc | 22 | return max_t(u32, 1, a & ~1); |
cf1c5fae SA |
23 | } |
24 | ||
25 | /* Return an even number or one. */ | |
8a75e8dc | 26 | static inline u32 clk_div_even_up(u32 a) |
cf1c5fae SA |
27 | { |
28 | if (a == 1) | |
29 | return 1; | |
30 | return (a + 1) & ~1; | |
31 | } | |
32 | ||
8a75e8dc | 33 | static inline u32 is_one_or_even(u32 a) |
cf1c5fae SA |
34 | { |
35 | if (a == 1) | |
36 | return 1; | |
37 | if (a & 1) | |
38 | return 0; | |
39 | ||
40 | return 1; | |
41 | } | |
42 | ||
8a75e8dc | 43 | static inline u32 one_or_more(u32 a) |
482e75e7 SA |
44 | { |
45 | return a ?: 1; | |
46 | } | |
47 | ||
8a75e8dc SA |
48 | static int bounds_check(struct device *dev, u32 val, |
49 | u32 min, u32 max, const char *prefix, | |
f25d3962 | 50 | char *str) |
cf1c5fae SA |
51 | { |
52 | if (val >= min && val <= max) | |
53 | return 0; | |
54 | ||
f25d3962 SA |
55 | dev_dbg(dev, "%s_%s out of bounds: %d (%d--%d)\n", prefix, |
56 | str, val, min, max); | |
cf1c5fae SA |
57 | |
58 | return -EINVAL; | |
59 | } | |
60 | ||
fadfe884 SA |
61 | #define PLL_OP 1 |
62 | #define PLL_VT 2 | |
63 | ||
64 | static const char *pll_string(unsigned int which) | |
cf1c5fae | 65 | { |
fadfe884 SA |
66 | switch (which) { |
67 | case PLL_OP: | |
68 | return "op"; | |
69 | case PLL_VT: | |
70 | return "vt"; | |
cf1c5fae | 71 | } |
fadfe884 SA |
72 | |
73 | return NULL; | |
74 | } | |
75 | ||
76 | #define PLL_FL(f) CCS_PLL_FLAG_##f | |
77 | ||
78 | static void print_pll(struct device *dev, struct ccs_pll *pll) | |
79 | { | |
80 | const struct { | |
81 | struct ccs_pll_branch_fr *fr; | |
82 | struct ccs_pll_branch_bk *bk; | |
83 | unsigned int which; | |
84 | } branches[] = { | |
85 | { &pll->vt_fr, &pll->vt_bk, PLL_VT }, | |
f25d3962 | 86 | { &pll->op_fr, &pll->op_bk, PLL_OP } |
fadfe884 SA |
87 | }, *br; |
88 | unsigned int i; | |
89 | ||
90 | dev_dbg(dev, "ext_clk_freq_hz\t\t%u\n", pll->ext_clk_freq_hz); | |
91 | ||
92 | for (i = 0, br = branches; i < ARRAY_SIZE(branches); i++, br++) { | |
93 | const char *s = pll_string(br->which); | |
94 | ||
6c7469e4 SA |
95 | if (pll->flags & CCS_PLL_FLAG_DUAL_PLL || |
96 | br->which == PLL_VT) { | |
fadfe884 SA |
97 | dev_dbg(dev, "%s_pre_pll_clk_div\t\t%u\n", s, |
98 | br->fr->pre_pll_clk_div); | |
99 | dev_dbg(dev, "%s_pll_multiplier\t\t%u\n", s, | |
100 | br->fr->pll_multiplier); | |
101 | ||
102 | dev_dbg(dev, "%s_pll_ip_clk_freq_hz\t%u\n", s, | |
103 | br->fr->pll_ip_clk_freq_hz); | |
104 | dev_dbg(dev, "%s_pll_op_clk_freq_hz\t%u\n", s, | |
105 | br->fr->pll_op_clk_freq_hz); | |
106 | } | |
107 | ||
108 | if (!(pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) || | |
109 | br->which == PLL_VT) { | |
110 | dev_dbg(dev, "%s_sys_clk_div\t\t%u\n", s, | |
111 | br->bk->sys_clk_div); | |
112 | dev_dbg(dev, "%s_pix_clk_div\t\t%u\n", s, | |
113 | br->bk->pix_clk_div); | |
114 | ||
115 | dev_dbg(dev, "%s_sys_clk_freq_hz\t%u\n", s, | |
116 | br->bk->sys_clk_freq_hz); | |
117 | dev_dbg(dev, "%s_pix_clk_freq_hz\t%u\n", s, | |
118 | br->bk->pix_clk_freq_hz); | |
119 | } | |
cf1c5fae | 120 | } |
fadfe884 | 121 | |
bd189aac SA |
122 | dev_dbg(dev, "pixel rate in pixel array:\t%u\n", |
123 | pll->pixel_rate_pixel_array); | |
124 | dev_dbg(dev, "pixel rate on CSI-2 bus:\t%u\n", | |
125 | pll->pixel_rate_csi); | |
126 | ||
900c33e8 | 127 | dev_dbg(dev, "flags%s%s%s%s%s%s%s%s%s\n", |
fadfe884 SA |
128 | pll->flags & PLL_FL(LANE_SPEED_MODEL) ? " lane-speed" : "", |
129 | pll->flags & PLL_FL(LINK_DECOUPLED) ? " link-decoupled" : "", | |
130 | pll->flags & PLL_FL(EXT_IP_PLL_DIVIDER) ? | |
131 | " ext-ip-pll-divider" : "", | |
132 | pll->flags & PLL_FL(FLEXIBLE_OP_PIX_CLK_DIV) ? | |
133 | " flexible-op-pix-div" : "", | |
134 | pll->flags & PLL_FL(FIFO_DERATING) ? " fifo-derating" : "", | |
6c7469e4 | 135 | pll->flags & PLL_FL(FIFO_OVERRATING) ? " fifo-overrating" : "", |
900c33e8 SA |
136 | pll->flags & PLL_FL(DUAL_PLL) ? " dual-pll" : "", |
137 | pll->flags & PLL_FL(OP_SYS_DDR) ? " op-sys-ddr" : "", | |
138 | pll->flags & PLL_FL(OP_PIX_DDR) ? " op-pix-ddr" : ""); | |
139 | } | |
140 | ||
8a75e8dc | 141 | static u32 op_sys_ddr(u32 flags) |
900c33e8 SA |
142 | { |
143 | return flags & CCS_PLL_FLAG_OP_SYS_DDR ? 1 : 0; | |
144 | } | |
145 | ||
8a75e8dc | 146 | static u32 op_pix_ddr(u32 flags) |
900c33e8 SA |
147 | { |
148 | return flags & CCS_PLL_FLAG_OP_PIX_DDR ? 1 : 0; | |
cf1c5fae SA |
149 | } |
150 | ||
f25d3962 SA |
151 | static int check_fr_bounds(struct device *dev, |
152 | const struct ccs_pll_limits *lim, | |
153 | struct ccs_pll *pll, unsigned int which) | |
c859470a | 154 | { |
f25d3962 SA |
155 | const struct ccs_pll_branch_limits_fr *lim_fr; |
156 | struct ccs_pll_branch_fr *pll_fr; | |
157 | const char *s = pll_string(which); | |
c859470a SA |
158 | int rval; |
159 | ||
f25d3962 SA |
160 | if (which == PLL_OP) { |
161 | lim_fr = &lim->op_fr; | |
162 | pll_fr = &pll->op_fr; | |
163 | } else { | |
164 | lim_fr = &lim->vt_fr; | |
165 | pll_fr = &pll->vt_fr; | |
166 | } | |
167 | ||
168 | rval = bounds_check(dev, pll_fr->pre_pll_clk_div, | |
169 | lim_fr->min_pre_pll_clk_div, | |
170 | lim_fr->max_pre_pll_clk_div, s, "pre_pll_clk_div"); | |
171 | ||
c859470a | 172 | if (!rval) |
f25d3962 SA |
173 | rval = bounds_check(dev, pll_fr->pll_ip_clk_freq_hz, |
174 | lim_fr->min_pll_ip_clk_freq_hz, | |
175 | lim_fr->max_pll_ip_clk_freq_hz, | |
176 | s, "pll_ip_clk_freq_hz"); | |
c859470a | 177 | if (!rval) |
f25d3962 SA |
178 | rval = bounds_check(dev, pll_fr->pll_multiplier, |
179 | lim_fr->min_pll_multiplier, | |
180 | lim_fr->max_pll_multiplier, | |
181 | s, "pll_multiplier"); | |
c859470a | 182 | if (!rval) |
f25d3962 SA |
183 | rval = bounds_check(dev, pll_fr->pll_op_clk_freq_hz, |
184 | lim_fr->min_pll_op_clk_freq_hz, | |
185 | lim_fr->max_pll_op_clk_freq_hz, | |
186 | s, "pll_op_clk_freq_hz"); | |
63516b55 | 187 | |
f25d3962 SA |
188 | return rval; |
189 | } | |
190 | ||
191 | static int check_bk_bounds(struct device *dev, | |
192 | const struct ccs_pll_limits *lim, | |
193 | struct ccs_pll *pll, unsigned int which) | |
194 | { | |
195 | const struct ccs_pll_branch_limits_bk *lim_bk; | |
196 | struct ccs_pll_branch_bk *pll_bk; | |
197 | const char *s = pll_string(which); | |
198 | int rval; | |
199 | ||
200 | if (which == PLL_OP) { | |
201 | if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) | |
202 | return 0; | |
63516b55 | 203 | |
f25d3962 SA |
204 | lim_bk = &lim->op_bk; |
205 | pll_bk = &pll->op_bk; | |
206 | } else { | |
207 | lim_bk = &lim->vt_bk; | |
208 | pll_bk = &pll->vt_bk; | |
209 | } | |
210 | ||
211 | rval = bounds_check(dev, pll_bk->sys_clk_div, | |
212 | lim_bk->min_sys_clk_div, | |
213 | lim_bk->max_sys_clk_div, s, "op_sys_clk_div"); | |
214 | if (!rval) | |
215 | rval = bounds_check(dev, pll_bk->sys_clk_freq_hz, | |
216 | lim_bk->min_sys_clk_freq_hz, | |
217 | lim_bk->max_sys_clk_freq_hz, | |
218 | s, "sys_clk_freq_hz"); | |
c859470a | 219 | if (!rval) |
f25d3962 SA |
220 | rval = bounds_check(dev, pll_bk->sys_clk_div, |
221 | lim_bk->min_sys_clk_div, | |
222 | lim_bk->max_sys_clk_div, | |
223 | s, "sys_clk_div"); | |
c859470a | 224 | if (!rval) |
f25d3962 SA |
225 | rval = bounds_check(dev, pll_bk->pix_clk_freq_hz, |
226 | lim_bk->min_pix_clk_freq_hz, | |
227 | lim_bk->max_pix_clk_freq_hz, | |
228 | s, "pix_clk_freq_hz"); | |
229 | ||
230 | return rval; | |
231 | } | |
c859470a | 232 | |
f25d3962 SA |
233 | static int check_ext_bounds(struct device *dev, struct ccs_pll *pll) |
234 | { | |
38c94eb8 SA |
235 | if (!(pll->flags & CCS_PLL_FLAG_FIFO_DERATING) && |
236 | pll->pixel_rate_pixel_array > pll->pixel_rate_csi) { | |
237 | dev_dbg(dev, "device does not support derating\n"); | |
238 | return -EINVAL; | |
239 | } | |
240 | ||
241 | if (!(pll->flags & CCS_PLL_FLAG_FIFO_OVERRATING) && | |
242 | pll->pixel_rate_pixel_array < pll->pixel_rate_csi) { | |
243 | dev_dbg(dev, "device does not support overrating\n"); | |
244 | return -EINVAL; | |
245 | } | |
246 | ||
f25d3962 | 247 | return 0; |
c859470a SA |
248 | } |
249 | ||
9ec6e5b1 SA |
250 | static void |
251 | ccs_pll_find_vt_sys_div(struct device *dev, const struct ccs_pll_limits *lim, | |
252 | struct ccs_pll *pll, struct ccs_pll_branch_fr *pll_fr, | |
8a75e8dc SA |
253 | u16 min_vt_div, u16 max_vt_div, |
254 | u16 *min_sys_div, u16 *max_sys_div) | |
9ec6e5b1 SA |
255 | { |
256 | /* | |
257 | * Find limits for sys_clk_div. Not all values are possible with all | |
258 | * values of pix_clk_div. | |
259 | */ | |
260 | *min_sys_div = lim->vt_bk.min_sys_clk_div; | |
261 | dev_dbg(dev, "min_sys_div: %u\n", *min_sys_div); | |
8a75e8dc | 262 | *min_sys_div = max_t(u16, *min_sys_div, |
9ec6e5b1 SA |
263 | DIV_ROUND_UP(min_vt_div, |
264 | lim->vt_bk.max_pix_clk_div)); | |
265 | dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %u\n", *min_sys_div); | |
8a75e8dc | 266 | *min_sys_div = max_t(u16, *min_sys_div, |
9ec6e5b1 SA |
267 | pll_fr->pll_op_clk_freq_hz |
268 | / lim->vt_bk.max_sys_clk_freq_hz); | |
269 | dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %u\n", *min_sys_div); | |
270 | *min_sys_div = clk_div_even_up(*min_sys_div); | |
271 | dev_dbg(dev, "min_sys_div: one or even: %u\n", *min_sys_div); | |
272 | ||
273 | *max_sys_div = lim->vt_bk.max_sys_clk_div; | |
274 | dev_dbg(dev, "max_sys_div: %u\n", *max_sys_div); | |
8a75e8dc | 275 | *max_sys_div = min_t(u16, *max_sys_div, |
9ec6e5b1 SA |
276 | DIV_ROUND_UP(max_vt_div, |
277 | lim->vt_bk.min_pix_clk_div)); | |
278 | dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %u\n", *max_sys_div); | |
8a75e8dc | 279 | *max_sys_div = min_t(u16, *max_sys_div, |
9ec6e5b1 SA |
280 | DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz, |
281 | lim->vt_bk.min_pix_clk_freq_hz)); | |
282 | dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %u\n", *max_sys_div); | |
283 | } | |
284 | ||
8030aa4f SA |
285 | #define CPHY_CONST 7 |
286 | #define DPHY_CONST 16 | |
287 | #define PHY_CONST_DIV 16 | |
288 | ||
6c7469e4 SA |
289 | static inline int |
290 | __ccs_pll_calculate_vt_tree(struct device *dev, | |
291 | const struct ccs_pll_limits *lim, | |
8a75e8dc | 292 | struct ccs_pll *pll, u32 mul, u32 div) |
6c7469e4 SA |
293 | { |
294 | const struct ccs_pll_branch_limits_fr *lim_fr = &lim->vt_fr; | |
295 | const struct ccs_pll_branch_limits_bk *lim_bk = &lim->vt_bk; | |
296 | struct ccs_pll_branch_fr *pll_fr = &pll->vt_fr; | |
297 | struct ccs_pll_branch_bk *pll_bk = &pll->vt_bk; | |
8a75e8dc SA |
298 | u32 more_mul; |
299 | u16 best_pix_div = SHRT_MAX >> 1, best_div; | |
300 | u16 vt_div, min_sys_div, max_sys_div, sys_div; | |
6c7469e4 SA |
301 | |
302 | pll_fr->pll_ip_clk_freq_hz = | |
303 | pll->ext_clk_freq_hz / pll_fr->pre_pll_clk_div; | |
304 | ||
305 | dev_dbg(dev, "vt_pll_ip_clk_freq_hz %u\n", pll_fr->pll_ip_clk_freq_hz); | |
306 | ||
307 | more_mul = one_or_more(DIV_ROUND_UP(lim_fr->min_pll_op_clk_freq_hz, | |
308 | pll_fr->pll_ip_clk_freq_hz * mul)); | |
309 | ||
310 | dev_dbg(dev, "more_mul: %u\n", more_mul); | |
311 | more_mul *= DIV_ROUND_UP(lim_fr->min_pll_multiplier, mul * more_mul); | |
312 | dev_dbg(dev, "more_mul2: %u\n", more_mul); | |
313 | ||
314 | pll_fr->pll_multiplier = mul * more_mul; | |
315 | ||
316 | if (pll_fr->pll_multiplier * pll_fr->pll_ip_clk_freq_hz > | |
317 | lim_fr->max_pll_op_clk_freq_hz) | |
318 | return -EINVAL; | |
319 | ||
320 | pll_fr->pll_op_clk_freq_hz = | |
321 | pll_fr->pll_ip_clk_freq_hz * pll_fr->pll_multiplier; | |
322 | ||
323 | vt_div = div * more_mul; | |
324 | ||
325 | ccs_pll_find_vt_sys_div(dev, lim, pll, pll_fr, vt_div, vt_div, | |
326 | &min_sys_div, &max_sys_div); | |
327 | ||
328 | max_sys_div = (vt_div & 1) ? 1 : max_sys_div; | |
329 | ||
330 | dev_dbg(dev, "vt min/max_sys_div: %u,%u\n", min_sys_div, max_sys_div); | |
331 | ||
332 | for (sys_div = min_sys_div; sys_div <= max_sys_div; | |
333 | sys_div += 2 - (sys_div & 1)) { | |
8a75e8dc | 334 | u16 pix_div; |
6c7469e4 SA |
335 | |
336 | if (vt_div % sys_div) | |
337 | continue; | |
338 | ||
339 | pix_div = vt_div / sys_div; | |
340 | ||
341 | if (pix_div < lim_bk->min_pix_clk_div || | |
342 | pix_div > lim_bk->max_pix_clk_div) { | |
343 | dev_dbg(dev, | |
344 | "pix_div %u too small or too big (%u--%u)\n", | |
345 | pix_div, | |
346 | lim_bk->min_pix_clk_div, | |
347 | lim_bk->max_pix_clk_div); | |
348 | continue; | |
349 | } | |
350 | ||
b41f2708 SA |
351 | dev_dbg(dev, "sys/pix/best_pix: %u,%u,%u\n", sys_div, pix_div, |
352 | best_pix_div); | |
353 | ||
354 | if (pix_div * sys_div <= best_pix_div) { | |
6c7469e4 SA |
355 | best_pix_div = pix_div; |
356 | best_div = pix_div * sys_div; | |
357 | } | |
358 | } | |
359 | if (best_pix_div == SHRT_MAX >> 1) | |
360 | return -EINVAL; | |
361 | ||
362 | pll_bk->sys_clk_div = best_div / best_pix_div; | |
363 | pll_bk->pix_clk_div = best_pix_div; | |
364 | ||
365 | pll_bk->sys_clk_freq_hz = | |
366 | pll_fr->pll_op_clk_freq_hz / pll_bk->sys_clk_div; | |
367 | pll_bk->pix_clk_freq_hz = | |
368 | pll_bk->sys_clk_freq_hz / pll_bk->pix_clk_div; | |
369 | ||
370 | pll->pixel_rate_pixel_array = | |
371 | pll_bk->pix_clk_freq_hz * pll->vt_lanes; | |
372 | ||
373 | return 0; | |
374 | } | |
375 | ||
376 | static int ccs_pll_calculate_vt_tree(struct device *dev, | |
377 | const struct ccs_pll_limits *lim, | |
378 | struct ccs_pll *pll) | |
379 | { | |
380 | const struct ccs_pll_branch_limits_fr *lim_fr = &lim->vt_fr; | |
381 | struct ccs_pll_branch_fr *pll_fr = &pll->vt_fr; | |
8a75e8dc SA |
382 | u16 min_pre_pll_clk_div = lim_fr->min_pre_pll_clk_div; |
383 | u16 max_pre_pll_clk_div = lim_fr->max_pre_pll_clk_div; | |
384 | u32 pre_mul, pre_div; | |
6c7469e4 SA |
385 | |
386 | pre_div = gcd(pll->pixel_rate_csi, | |
387 | pll->ext_clk_freq_hz * pll->vt_lanes); | |
388 | pre_mul = pll->pixel_rate_csi / pre_div; | |
389 | pre_div = pll->ext_clk_freq_hz * pll->vt_lanes / pre_div; | |
390 | ||
391 | /* Make sure PLL input frequency is within limits */ | |
392 | max_pre_pll_clk_div = | |
8a75e8dc | 393 | min_t(u16, max_pre_pll_clk_div, |
6c7469e4 SA |
394 | DIV_ROUND_UP(pll->ext_clk_freq_hz, |
395 | lim_fr->min_pll_ip_clk_freq_hz)); | |
396 | ||
8a75e8dc | 397 | min_pre_pll_clk_div = max_t(u16, min_pre_pll_clk_div, |
6c7469e4 SA |
398 | pll->ext_clk_freq_hz / |
399 | lim_fr->max_pll_ip_clk_freq_hz); | |
400 | ||
401 | dev_dbg(dev, "vt min/max_pre_pll_clk_div: %u,%u\n", | |
402 | min_pre_pll_clk_div, max_pre_pll_clk_div); | |
403 | ||
404 | for (pll_fr->pre_pll_clk_div = min_pre_pll_clk_div; | |
405 | pll_fr->pre_pll_clk_div <= max_pre_pll_clk_div; | |
406 | pll_fr->pre_pll_clk_div += | |
407 | (pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER) ? 1 : | |
408 | 2 - (pll_fr->pre_pll_clk_div & 1)) { | |
8a75e8dc | 409 | u32 mul, div; |
6c7469e4 SA |
410 | int rval; |
411 | ||
412 | div = gcd(pre_mul * pll_fr->pre_pll_clk_div, pre_div); | |
413 | mul = pre_mul * pll_fr->pre_pll_clk_div / div; | |
414 | div = pre_div / div; | |
415 | ||
416 | dev_dbg(dev, "vt pre-div/mul/div: %u,%u,%u\n", | |
417 | pll_fr->pre_pll_clk_div, mul, div); | |
418 | ||
419 | rval = __ccs_pll_calculate_vt_tree(dev, lim, pll, | |
420 | mul, div); | |
421 | if (rval) | |
422 | continue; | |
423 | ||
424 | rval = check_fr_bounds(dev, lim, pll, PLL_VT); | |
425 | if (rval) | |
426 | continue; | |
427 | ||
428 | rval = check_bk_bounds(dev, lim, pll, PLL_VT); | |
429 | if (rval) | |
430 | continue; | |
431 | ||
432 | return 0; | |
433 | } | |
434 | ||
435 | return -EINVAL; | |
436 | } | |
437 | ||
3e2db036 | 438 | static void |
a38836b2 SA |
439 | ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim, |
440 | const struct ccs_pll_branch_limits_bk *op_lim_bk, | |
441 | struct ccs_pll *pll, struct ccs_pll_branch_fr *pll_fr, | |
442 | struct ccs_pll_branch_bk *op_pll_bk, bool cphy, | |
8a75e8dc | 443 | u32 phy_const) |
cf1c5fae | 444 | { |
8a75e8dc SA |
445 | u16 sys_div; |
446 | u16 best_pix_div = SHRT_MAX >> 1; | |
447 | u16 vt_op_binning_div; | |
448 | u16 min_vt_div, max_vt_div, vt_div; | |
449 | u16 min_sys_div, max_sys_div; | |
cf1c5fae | 450 | |
a38836b2 SA |
451 | if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) |
452 | goto out_calc_pixel_rate; | |
453 | ||
cf1c5fae | 454 | /* |
38c94eb8 SA |
455 | * Find out whether a sensor supports derating. If it does not, VT and |
456 | * OP domains are required to run at the same pixel rate. | |
cf1c5fae | 457 | */ |
38c94eb8 SA |
458 | if (!(pll->flags & CCS_PLL_FLAG_FIFO_DERATING)) { |
459 | min_vt_div = | |
460 | op_pll_bk->sys_clk_div * op_pll_bk->pix_clk_div | |
900c33e8 SA |
461 | * pll->vt_lanes * phy_const / pll->op_lanes |
462 | / (PHY_CONST_DIV << op_pix_ddr(pll->flags)); | |
38c94eb8 SA |
463 | } else { |
464 | /* | |
465 | * Some sensors perform analogue binning and some do this | |
466 | * digitally. The ones doing this digitally can be roughly be | |
467 | * found out using this formula. The ones doing this digitally | |
468 | * should run at higher clock rate, so smaller divisor is used | |
469 | * on video timing side. | |
470 | */ | |
471 | if (lim->min_line_length_pck_bin > lim->min_line_length_pck | |
472 | / pll->binning_horizontal) | |
473 | vt_op_binning_div = pll->binning_horizontal; | |
474 | else | |
475 | vt_op_binning_div = 1; | |
476 | dev_dbg(dev, "vt_op_binning_div: %u\n", vt_op_binning_div); | |
477 | ||
478 | /* | |
479 | * Profile 2 supports vt_pix_clk_div E [4, 10] | |
480 | * | |
481 | * Horizontal binning can be used as a base for difference in | |
482 | * divisors. One must make sure that horizontal blanking is | |
483 | * enough to accommodate the CSI-2 sync codes. | |
484 | * | |
485 | * Take scaling factor and number of VT lanes into account as well. | |
486 | * | |
487 | * Find absolute limits for the factor of vt divider. | |
488 | */ | |
489 | dev_dbg(dev, "scale_m: %u\n", pll->scale_m); | |
490 | min_vt_div = | |
491 | DIV_ROUND_UP(pll->bits_per_pixel | |
492 | * op_pll_bk->sys_clk_div * pll->scale_n | |
493 | * pll->vt_lanes * phy_const, | |
494 | (pll->flags & | |
495 | CCS_PLL_FLAG_LANE_SPEED_MODEL ? | |
496 | pll->csi2.lanes : 1) | |
497 | * vt_op_binning_div * pll->scale_m | |
900c33e8 | 498 | * PHY_CONST_DIV << op_pix_ddr(pll->flags)); |
38c94eb8 | 499 | } |
cf1c5fae SA |
500 | |
501 | /* Find smallest and biggest allowed vt divisor. */ | |
c37f9bf9 | 502 | dev_dbg(dev, "min_vt_div: %u\n", min_vt_div); |
8a75e8dc | 503 | min_vt_div = max_t(u16, min_vt_div, |
594f1e93 SA |
504 | DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz, |
505 | lim->vt_bk.max_pix_clk_freq_hz)); | |
c37f9bf9 | 506 | dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %u\n", |
cf1c5fae | 507 | min_vt_div); |
8a75e8dc SA |
508 | min_vt_div = max_t(u16, min_vt_div, lim->vt_bk.min_pix_clk_div |
509 | * lim->vt_bk.min_sys_clk_div); | |
c37f9bf9 | 510 | dev_dbg(dev, "min_vt_div: min_vt_clk_div: %u\n", min_vt_div); |
cf1c5fae | 511 | |
415ddd99 | 512 | max_vt_div = lim->vt_bk.max_sys_clk_div * lim->vt_bk.max_pix_clk_div; |
c37f9bf9 | 513 | dev_dbg(dev, "max_vt_div: %u\n", max_vt_div); |
8a75e8dc | 514 | max_vt_div = min_t(u16, max_vt_div, |
594f1e93 | 515 | DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz, |
415ddd99 | 516 | lim->vt_bk.min_pix_clk_freq_hz)); |
c37f9bf9 | 517 | dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %u\n", |
cf1c5fae SA |
518 | max_vt_div); |
519 | ||
9ec6e5b1 SA |
520 | ccs_pll_find_vt_sys_div(dev, lim, pll, pll_fr, min_vt_div, |
521 | max_vt_div, &min_sys_div, &max_sys_div); | |
cf1c5fae SA |
522 | |
523 | /* | |
524 | * Find pix_div such that a legal pix_div * sys_div results | |
525 | * into a value which is not smaller than div, the desired | |
526 | * divisor. | |
527 | */ | |
36154b68 | 528 | for (vt_div = min_vt_div; vt_div <= max_vt_div; vt_div++) { |
8a75e8dc | 529 | u16 __max_sys_div = vt_div & 1 ? 1 : max_sys_div; |
36154b68 SA |
530 | |
531 | for (sys_div = min_sys_div; sys_div <= __max_sys_div; | |
cf1c5fae | 532 | sys_div += 2 - (sys_div & 1)) { |
8a75e8dc SA |
533 | u16 pix_div; |
534 | u16 rounded_div; | |
cf1c5fae | 535 | |
36154b68 SA |
536 | pix_div = DIV_ROUND_UP(vt_div, sys_div); |
537 | ||
415ddd99 SA |
538 | if (pix_div < lim->vt_bk.min_pix_clk_div |
539 | || pix_div > lim->vt_bk.max_pix_clk_div) { | |
cf1c5fae | 540 | dev_dbg(dev, |
c37f9bf9 | 541 | "pix_div %u too small or too big (%u--%u)\n", |
cf1c5fae | 542 | pix_div, |
415ddd99 SA |
543 | lim->vt_bk.min_pix_clk_div, |
544 | lim->vt_bk.max_pix_clk_div); | |
cf1c5fae SA |
545 | continue; |
546 | } | |
547 | ||
9c1a0d9e SA |
548 | rounded_div = roundup(vt_div, best_pix_div); |
549 | ||
cf1c5fae | 550 | /* Check if this one is better. */ |
9c1a0d9e | 551 | if (pix_div * sys_div <= rounded_div) |
cf1c5fae | 552 | best_pix_div = pix_div; |
9c1a0d9e SA |
553 | |
554 | /* Bail out if we've already found the best value. */ | |
555 | if (vt_div == rounded_div) | |
556 | break; | |
cf1c5fae | 557 | } |
594f1e93 | 558 | if (best_pix_div < SHRT_MAX >> 1) |
cf1c5fae SA |
559 | break; |
560 | } | |
561 | ||
9454432a | 562 | pll->vt_bk.sys_clk_div = DIV_ROUND_UP(vt_div, best_pix_div); |
415ddd99 | 563 | pll->vt_bk.pix_clk_div = best_pix_div; |
cf1c5fae | 564 | |
415ddd99 | 565 | pll->vt_bk.sys_clk_freq_hz = |
3e2db036 | 566 | pll_fr->pll_op_clk_freq_hz / pll->vt_bk.sys_clk_div; |
415ddd99 SA |
567 | pll->vt_bk.pix_clk_freq_hz = |
568 | pll->vt_bk.sys_clk_freq_hz / pll->vt_bk.pix_clk_div; | |
a38836b2 SA |
569 | |
570 | out_calc_pixel_rate: | |
571 | pll->pixel_rate_pixel_array = | |
572 | pll->vt_bk.pix_clk_freq_hz * pll->vt_lanes; | |
3e2db036 SA |
573 | } |
574 | ||
575 | /* | |
576 | * Heuristically guess the PLL tree for a given common multiplier and | |
577 | * divisor. Begin with the operational timing and continue to video | |
578 | * timing once operational timing has been verified. | |
579 | * | |
580 | * @mul is the PLL multiplier and @div is the common divisor | |
581 | * (pre_pll_clk_div and op_sys_clk_div combined). The final PLL | |
582 | * multiplier will be a multiple of @mul. | |
583 | * | |
584 | * @return Zero on success, error code on error. | |
585 | */ | |
586 | static int | |
a38836b2 SA |
587 | ccs_pll_calculate_op(struct device *dev, const struct ccs_pll_limits *lim, |
588 | const struct ccs_pll_branch_limits_fr *op_lim_fr, | |
589 | const struct ccs_pll_branch_limits_bk *op_lim_bk, | |
590 | struct ccs_pll *pll, struct ccs_pll_branch_fr *op_pll_fr, | |
8a75e8dc SA |
591 | struct ccs_pll_branch_bk *op_pll_bk, u32 mul, |
592 | u32 div, u32 op_sys_clk_freq_hz_sdr, u32 l, | |
593 | bool cphy, u32 phy_const) | |
3e2db036 SA |
594 | { |
595 | /* | |
596 | * Higher multipliers (and divisors) are often required than | |
597 | * necessitated by the external clock and the output clocks. | |
598 | * There are limits for all values in the clock tree. These | |
599 | * are the minimum and maximum multiplier for mul. | |
600 | */ | |
8a75e8dc SA |
601 | u32 more_mul_min, more_mul_max; |
602 | u32 more_mul_factor; | |
603 | u32 i; | |
3e2db036 SA |
604 | |
605 | /* | |
606 | * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be | |
607 | * too high. | |
608 | */ | |
609 | dev_dbg(dev, "op_pre_pll_clk_div %u\n", op_pll_fr->pre_pll_clk_div); | |
610 | ||
611 | /* Don't go above max pll multiplier. */ | |
612 | more_mul_max = op_lim_fr->max_pll_multiplier / mul; | |
613 | dev_dbg(dev, "more_mul_max: max_op_pll_multiplier check: %u\n", | |
614 | more_mul_max); | |
615 | /* Don't go above max pll op frequency. */ | |
616 | more_mul_max = | |
8a75e8dc | 617 | min_t(u32, |
3e2db036 SA |
618 | more_mul_max, |
619 | op_lim_fr->max_pll_op_clk_freq_hz | |
620 | / (pll->ext_clk_freq_hz / | |
621 | op_pll_fr->pre_pll_clk_div * mul)); | |
622 | dev_dbg(dev, "more_mul_max: max_pll_op_clk_freq_hz check: %u\n", | |
623 | more_mul_max); | |
624 | /* Don't go above the division capability of op sys clock divider. */ | |
625 | more_mul_max = min(more_mul_max, | |
626 | op_lim_bk->max_sys_clk_div * op_pll_fr->pre_pll_clk_div | |
627 | / div); | |
628 | dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %u\n", | |
629 | more_mul_max); | |
630 | /* Ensure we won't go above max_pll_multiplier. */ | |
631 | more_mul_max = min(more_mul_max, op_lim_fr->max_pll_multiplier / mul); | |
632 | dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %u\n", | |
633 | more_mul_max); | |
634 | ||
635 | /* Ensure we won't go below min_pll_op_clk_freq_hz. */ | |
636 | more_mul_min = DIV_ROUND_UP(op_lim_fr->min_pll_op_clk_freq_hz, | |
637 | pll->ext_clk_freq_hz / | |
638 | op_pll_fr->pre_pll_clk_div * mul); | |
639 | dev_dbg(dev, "more_mul_min: min_op_pll_op_clk_freq_hz check: %u\n", | |
640 | more_mul_min); | |
641 | /* Ensure we won't go below min_pll_multiplier. */ | |
642 | more_mul_min = max(more_mul_min, | |
643 | DIV_ROUND_UP(op_lim_fr->min_pll_multiplier, mul)); | |
644 | dev_dbg(dev, "more_mul_min: min_op_pll_multiplier check: %u\n", | |
645 | more_mul_min); | |
646 | ||
647 | if (more_mul_min > more_mul_max) { | |
648 | dev_dbg(dev, | |
649 | "unable to compute more_mul_min and more_mul_max\n"); | |
650 | return -EINVAL; | |
651 | } | |
652 | ||
653 | more_mul_factor = lcm(div, op_pll_fr->pre_pll_clk_div) / div; | |
654 | dev_dbg(dev, "more_mul_factor: %u\n", more_mul_factor); | |
655 | more_mul_factor = lcm(more_mul_factor, op_lim_bk->min_sys_clk_div); | |
656 | dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n", | |
657 | more_mul_factor); | |
658 | i = roundup(more_mul_min, more_mul_factor); | |
659 | if (!is_one_or_even(i)) | |
660 | i <<= 1; | |
661 | ||
662 | dev_dbg(dev, "final more_mul: %u\n", i); | |
663 | if (i > more_mul_max) { | |
664 | dev_dbg(dev, "final more_mul is bad, max %u\n", more_mul_max); | |
665 | return -EINVAL; | |
666 | } | |
667 | ||
668 | op_pll_fr->pll_multiplier = mul * i; | |
669 | op_pll_bk->sys_clk_div = div * i / op_pll_fr->pre_pll_clk_div; | |
670 | dev_dbg(dev, "op_sys_clk_div: %u\n", op_pll_bk->sys_clk_div); | |
671 | ||
672 | op_pll_fr->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz | |
673 | / op_pll_fr->pre_pll_clk_div; | |
674 | ||
675 | op_pll_fr->pll_op_clk_freq_hz = op_pll_fr->pll_ip_clk_freq_hz | |
676 | * op_pll_fr->pll_multiplier; | |
677 | ||
678 | if (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL) | |
900c33e8 SA |
679 | op_pll_bk->pix_clk_div = |
680 | (pll->bits_per_pixel | |
681 | * pll->op_lanes * (phy_const << op_sys_ddr(pll->flags)) | |
682 | / PHY_CONST_DIV / pll->csi2.lanes / l) | |
683 | >> op_pix_ddr(pll->flags); | |
3e2db036 SA |
684 | else |
685 | op_pll_bk->pix_clk_div = | |
900c33e8 SA |
686 | (pll->bits_per_pixel |
687 | * (phy_const << op_sys_ddr(pll->flags)) | |
688 | / PHY_CONST_DIV / l) >> op_pix_ddr(pll->flags); | |
3e2db036 SA |
689 | |
690 | op_pll_bk->pix_clk_freq_hz = | |
900c33e8 SA |
691 | (op_sys_clk_freq_hz_sdr >> op_pix_ddr(pll->flags)) |
692 | / op_pll_bk->pix_clk_div; | |
693 | op_pll_bk->sys_clk_freq_hz = | |
694 | op_sys_clk_freq_hz_sdr >> op_sys_ddr(pll->flags); | |
3e2db036 SA |
695 | |
696 | dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll_bk->pix_clk_div); | |
697 | ||
a38836b2 | 698 | return 0; |
cf1c5fae | 699 | } |
6de1b143 | 700 | |
415ddd99 | 701 | int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim, |
9e05bbac | 702 | struct ccs_pll *pll) |
6de1b143 | 703 | { |
6c7469e4 SA |
704 | const struct ccs_pll_branch_limits_fr *op_lim_fr; |
705 | const struct ccs_pll_branch_limits_bk *op_lim_bk; | |
706 | struct ccs_pll_branch_fr *op_pll_fr; | |
707 | struct ccs_pll_branch_bk *op_pll_bk; | |
8030aa4f | 708 | bool cphy = pll->bus_type == CCS_PLL_BUS_TYPE_CSI2_CPHY; |
8a75e8dc SA |
709 | u32 phy_const = cphy ? CPHY_CONST : DPHY_CONST; |
710 | u32 op_sys_clk_freq_hz_sdr; | |
711 | u16 min_op_pre_pll_clk_div; | |
712 | u16 max_op_pre_pll_clk_div; | |
713 | u32 mul, div; | |
714 | u32 l = (!pll->op_bits_per_lane || | |
715 | pll->op_bits_per_lane >= pll->bits_per_pixel) ? 1 : 2; | |
716 | u32 i; | |
6de1b143 SA |
717 | int rval = -EINVAL; |
718 | ||
cac8f5d2 SA |
719 | if (!(pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL)) { |
720 | pll->op_lanes = 1; | |
721 | pll->vt_lanes = 1; | |
722 | } | |
9490a227 | 723 | |
6c7469e4 SA |
724 | if (pll->flags & CCS_PLL_FLAG_DUAL_PLL) { |
725 | op_lim_fr = &lim->op_fr; | |
726 | op_lim_bk = &lim->op_bk; | |
727 | op_pll_fr = &pll->op_fr; | |
728 | op_pll_bk = &pll->op_bk; | |
729 | } else if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) { | |
730 | /* | |
731 | * If there's no OP PLL at all, use the VT values | |
732 | * instead. The OP values are ignored for the rest of | |
733 | * the PLL calculation. | |
734 | */ | |
735 | op_lim_fr = &lim->vt_fr; | |
736 | op_lim_bk = &lim->vt_bk; | |
737 | op_pll_fr = &pll->vt_fr; | |
738 | op_pll_bk = &pll->vt_bk; | |
739 | } else { | |
740 | op_lim_fr = &lim->vt_fr; | |
741 | op_lim_bk = &lim->op_bk; | |
742 | op_pll_fr = &pll->vt_fr; | |
743 | op_pll_bk = &pll->op_bk; | |
744 | } | |
745 | ||
d7172c0e SA |
746 | if (!pll->op_lanes || !pll->vt_lanes || !pll->bits_per_pixel || |
747 | !pll->ext_clk_freq_hz || !pll->link_freq || !pll->scale_m || | |
748 | !op_lim_fr->min_pll_ip_clk_freq_hz || | |
749 | !op_lim_fr->max_pll_ip_clk_freq_hz || | |
750 | !op_lim_fr->min_pll_op_clk_freq_hz || | |
751 | !op_lim_fr->max_pll_op_clk_freq_hz || | |
752 | !op_lim_bk->max_sys_clk_div || !op_lim_fr->max_pll_multiplier) | |
753 | return -EINVAL; | |
754 | ||
9490a227 SA |
755 | /* |
756 | * Make sure op_pix_clk_div will be integer --- unless flexible | |
757 | * op_pix_clk_div is supported | |
758 | */ | |
759 | if (!(pll->flags & CCS_PLL_FLAG_FLEXIBLE_OP_PIX_CLK_DIV) && | |
900c33e8 SA |
760 | (pll->bits_per_pixel * pll->op_lanes) % |
761 | (pll->csi2.lanes * l << op_pix_ddr(pll->flags))) { | |
9490a227 SA |
762 | dev_dbg(dev, "op_pix_clk_div not an integer (bpp %u, op lanes %u, lanes %u, l %u)\n", |
763 | pll->bits_per_pixel, pll->op_lanes, pll->csi2.lanes, l); | |
764 | return -EINVAL; | |
765 | } | |
766 | ||
cac8f5d2 SA |
767 | dev_dbg(dev, "vt_lanes: %u\n", pll->vt_lanes); |
768 | dev_dbg(dev, "op_lanes: %u\n", pll->op_lanes); | |
769 | ||
c37f9bf9 | 770 | dev_dbg(dev, "binning: %ux%u\n", pll->binning_horizontal, |
6de1b143 SA |
771 | pll->binning_vertical); |
772 | ||
f5984bbd | 773 | switch (pll->bus_type) { |
47b6eaf3 | 774 | case CCS_PLL_BUS_TYPE_CSI2_DPHY: |
8030aa4f | 775 | case CCS_PLL_BUS_TYPE_CSI2_CPHY: |
ff474acc | 776 | op_sys_clk_freq_hz_sdr = pll->link_freq * 2 |
8030aa4f SA |
777 | * (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ? |
778 | 1 : pll->csi2.lanes); | |
779 | break; | |
f5984bbd SA |
780 | default: |
781 | return -EINVAL; | |
782 | } | |
6de1b143 | 783 | |
cac8f5d2 | 784 | pll->pixel_rate_csi = |
900c33e8 | 785 | div_u64((uint64_t)op_sys_clk_freq_hz_sdr |
8030aa4f SA |
786 | * (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ? |
787 | pll->csi2.lanes : 1) * PHY_CONST_DIV, | |
788 | phy_const * pll->bits_per_pixel * l); | |
cac8f5d2 | 789 | |
415ddd99 SA |
790 | /* Figure out limits for OP pre-pll divider based on extclk */ |
791 | dev_dbg(dev, "min / max op_pre_pll_clk_div: %u / %u\n", | |
792 | op_lim_fr->min_pre_pll_clk_div, op_lim_fr->max_pre_pll_clk_div); | |
793 | max_op_pre_pll_clk_div = | |
8a75e8dc | 794 | min_t(u16, op_lim_fr->max_pre_pll_clk_div, |
6de1b143 | 795 | clk_div_even(pll->ext_clk_freq_hz / |
415ddd99 SA |
796 | op_lim_fr->min_pll_ip_clk_freq_hz)); |
797 | min_op_pre_pll_clk_div = | |
8a75e8dc | 798 | max_t(u16, op_lim_fr->min_pre_pll_clk_div, |
6de1b143 SA |
799 | clk_div_even_up( |
800 | DIV_ROUND_UP(pll->ext_clk_freq_hz, | |
415ddd99 SA |
801 | op_lim_fr->max_pll_ip_clk_freq_hz))); |
802 | dev_dbg(dev, "pre-pll check: min / max op_pre_pll_clk_div: %u / %u\n", | |
803 | min_op_pre_pll_clk_div, max_op_pre_pll_clk_div); | |
6de1b143 | 804 | |
900c33e8 SA |
805 | i = gcd(op_sys_clk_freq_hz_sdr, |
806 | pll->ext_clk_freq_hz << op_pix_ddr(pll->flags)); | |
807 | mul = op_sys_clk_freq_hz_sdr / i; | |
808 | div = (pll->ext_clk_freq_hz << op_pix_ddr(pll->flags)) / i; | |
c37f9bf9 | 809 | dev_dbg(dev, "mul %u / div %u\n", mul, div); |
6de1b143 | 810 | |
415ddd99 | 811 | min_op_pre_pll_clk_div = |
8a75e8dc | 812 | max_t(u16, min_op_pre_pll_clk_div, |
6de1b143 | 813 | clk_div_even_up( |
482e75e7 SA |
814 | mul / |
815 | one_or_more( | |
816 | DIV_ROUND_UP(op_lim_fr->max_pll_op_clk_freq_hz, | |
817 | pll->ext_clk_freq_hz)))); | |
415ddd99 SA |
818 | dev_dbg(dev, "pll_op check: min / max op_pre_pll_clk_div: %u / %u\n", |
819 | min_op_pre_pll_clk_div, max_op_pre_pll_clk_div); | |
820 | ||
821 | for (op_pll_fr->pre_pll_clk_div = min_op_pre_pll_clk_div; | |
822 | op_pll_fr->pre_pll_clk_div <= max_op_pre_pll_clk_div; | |
4e1e8d24 SA |
823 | op_pll_fr->pre_pll_clk_div += |
824 | (pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER) ? 1 : | |
825 | 2 - (op_pll_fr->pre_pll_clk_div & 1)) { | |
a38836b2 | 826 | rval = ccs_pll_calculate_op(dev, lim, op_lim_fr, op_lim_bk, pll, |
900c33e8 SA |
827 | op_pll_fr, op_pll_bk, mul, div, |
828 | op_sys_clk_freq_hz_sdr, l, cphy, | |
829 | phy_const); | |
a38836b2 SA |
830 | if (rval) |
831 | continue; | |
832 | ||
b41f2708 SA |
833 | rval = check_fr_bounds(dev, lim, pll, |
834 | pll->flags & CCS_PLL_FLAG_DUAL_PLL ? | |
835 | PLL_OP : PLL_VT); | |
f25d3962 SA |
836 | if (rval) |
837 | continue; | |
838 | ||
839 | rval = check_bk_bounds(dev, lim, pll, PLL_OP); | |
840 | if (rval) | |
841 | continue; | |
842 | ||
6c7469e4 SA |
843 | if (pll->flags & CCS_PLL_FLAG_DUAL_PLL) |
844 | break; | |
845 | ||
a38836b2 SA |
846 | ccs_pll_calculate_vt(dev, lim, op_lim_bk, pll, op_pll_fr, |
847 | op_pll_bk, cphy, phy_const); | |
848 | ||
f25d3962 SA |
849 | rval = check_bk_bounds(dev, lim, pll, PLL_VT); |
850 | if (rval) | |
851 | continue; | |
852 | rval = check_ext_bounds(dev, pll); | |
6de1b143 SA |
853 | if (rval) |
854 | continue; | |
855 | ||
6c7469e4 SA |
856 | break; |
857 | } | |
858 | ||
859 | if (rval) { | |
860 | dev_dbg(dev, "unable to compute pre_pll divisor\n"); | |
a38836b2 | 861 | |
6c7469e4 | 862 | return rval; |
6de1b143 SA |
863 | } |
864 | ||
6c7469e4 SA |
865 | if (pll->flags & CCS_PLL_FLAG_DUAL_PLL) { |
866 | rval = ccs_pll_calculate_vt_tree(dev, lim, pll); | |
b08726bf | 867 | |
6c7469e4 SA |
868 | if (rval) |
869 | return rval; | |
870 | } | |
871 | ||
872 | print_pll(dev, pll); | |
873 | ||
874 | return 0; | |
6de1b143 | 875 | } |
9e05bbac | 876 | EXPORT_SYMBOL_GPL(ccs_pll_calculate); |
cf1c5fae | 877 | |
7389d01c | 878 | MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>"); |
9e05bbac | 879 | MODULE_DESCRIPTION("Generic MIPI CCS/SMIA/SMIA++ PLL calculator"); |
ca59318b | 880 | MODULE_LICENSE("GPL"); |