diff options
author | Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | 2011-12-07 11:45:25 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-05-14 07:57:02 -0400 |
commit | cf1c5fae5f8a28d478b7177a2d83e42d25eab072 (patch) | |
tree | e8a6ddfe3775a74413db9c5d674137a74e7e0a18 | |
parent | a6d7a62dcd1fccb3140100551b205315491eadc5 (diff) |
[media] smiapp: Generic SMIA++/SMIA PLL calculator
Calculate PLL configuration based on input data: sensor configuration, board
properties and sensor-specific limits.
[mchehab@redhat.com: Fix a Kconfig conflict affecting APTINA_PLL]
Signed-off-by: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/video/Kconfig | 3 | ||||
-rw-r--r-- | drivers/media/video/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/video/smiapp-pll.c | 417 | ||||
-rw-r--r-- | drivers/media/video/smiapp-pll.h | 103 |
4 files changed, 525 insertions, 0 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index b09982ee21d..d3e879f6431 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig | |||
@@ -472,6 +472,9 @@ comment "Camera sensor devices" | |||
472 | config VIDEO_APTINA_PLL | 472 | config VIDEO_APTINA_PLL |
473 | tristate | 473 | tristate |
474 | 474 | ||
475 | config VIDEO_SMIAPP_PLL | ||
476 | tristate | ||
477 | |||
475 | config VIDEO_OV7670 | 478 | config VIDEO_OV7670 |
476 | tristate "OmniVision OV7670 sensor support" | 479 | tristate "OmniVision OV7670 sensor support" |
477 | depends on I2C && VIDEO_V4L2 | 480 | depends on I2C && VIDEO_V4L2 |
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 1f5e36ceb94..4e6c100cf58 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile | |||
@@ -82,6 +82,8 @@ obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o | |||
82 | obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o | 82 | obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o |
83 | obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o | 83 | obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o |
84 | 84 | ||
85 | obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o | ||
86 | |||
85 | obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o | 87 | obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o |
86 | obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o | 88 | obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o |
87 | obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o | 89 | obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o |
diff --git a/drivers/media/video/smiapp-pll.c b/drivers/media/video/smiapp-pll.c new file mode 100644 index 00000000000..a416e27a428 --- /dev/null +++ b/drivers/media/video/smiapp-pll.c | |||
@@ -0,0 +1,417 @@ | |||
1 | /* | ||
2 | * drivers/media/video/smiapp-pll.c | ||
3 | * | ||
4 | * Generic driver for SMIA/SMIA++ compliant camera modules | ||
5 | * | ||
6 | * Copyright (C) 2011--2012 Nokia Corporation | ||
7 | * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * version 2 as published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but | ||
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
16 | * General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
21 | * 02110-1301 USA | ||
22 | * | ||
23 | */ | ||
24 | |||
25 | #include <linux/gcd.h> | ||
26 | #include <linux/lcm.h> | ||
27 | #include <linux/module.h> | ||
28 | |||
29 | #include "smiapp-pll.h" | ||
30 | |||
31 | /* Return an even number or one. */ | ||
32 | static inline uint32_t clk_div_even(uint32_t a) | ||
33 | { | ||
34 | return max_t(uint32_t, 1, a & ~1); | ||
35 | } | ||
36 | |||
37 | /* Return an even number or one. */ | ||
38 | static inline uint32_t clk_div_even_up(uint32_t a) | ||
39 | { | ||
40 | if (a == 1) | ||
41 | return 1; | ||
42 | return (a + 1) & ~1; | ||
43 | } | ||
44 | |||
45 | static inline uint32_t is_one_or_even(uint32_t a) | ||
46 | { | ||
47 | if (a == 1) | ||
48 | return 1; | ||
49 | if (a & 1) | ||
50 | return 0; | ||
51 | |||
52 | return 1; | ||
53 | } | ||
54 | |||
55 | static int bounds_check(struct device *dev, uint32_t val, | ||
56 | uint32_t min, uint32_t max, char *str) | ||
57 | { | ||
58 | if (val >= min && val <= max) | ||
59 | return 0; | ||
60 | |||
61 | dev_warn(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max); | ||
62 | |||
63 | return -EINVAL; | ||
64 | } | ||
65 | |||
66 | static void print_pll(struct device *dev, struct smiapp_pll *pll) | ||
67 | { | ||
68 | dev_dbg(dev, "pre_pll_clk_div\t%d\n", pll->pre_pll_clk_div); | ||
69 | dev_dbg(dev, "pll_multiplier \t%d\n", pll->pll_multiplier); | ||
70 | if (pll->flags != SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { | ||
71 | dev_dbg(dev, "op_sys_clk_div \t%d\n", pll->op_sys_clk_div); | ||
72 | dev_dbg(dev, "op_pix_clk_div \t%d\n", pll->op_pix_clk_div); | ||
73 | } | ||
74 | dev_dbg(dev, "vt_sys_clk_div \t%d\n", pll->vt_sys_clk_div); | ||
75 | dev_dbg(dev, "vt_pix_clk_div \t%d\n", pll->vt_pix_clk_div); | ||
76 | |||
77 | dev_dbg(dev, "ext_clk_freq_hz \t%d\n", pll->ext_clk_freq_hz); | ||
78 | dev_dbg(dev, "pll_ip_clk_freq_hz \t%d\n", pll->pll_ip_clk_freq_hz); | ||
79 | dev_dbg(dev, "pll_op_clk_freq_hz \t%d\n", pll->pll_op_clk_freq_hz); | ||
80 | if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { | ||
81 | dev_dbg(dev, "op_sys_clk_freq_hz \t%d\n", | ||
82 | pll->op_sys_clk_freq_hz); | ||
83 | dev_dbg(dev, "op_pix_clk_freq_hz \t%d\n", | ||
84 | pll->op_pix_clk_freq_hz); | ||
85 | } | ||
86 | dev_dbg(dev, "vt_sys_clk_freq_hz \t%d\n", pll->vt_sys_clk_freq_hz); | ||
87 | dev_dbg(dev, "vt_pix_clk_freq_hz \t%d\n", pll->vt_pix_clk_freq_hz); | ||
88 | } | ||
89 | |||
90 | int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, | ||
91 | struct smiapp_pll *pll) | ||
92 | { | ||
93 | uint32_t sys_div; | ||
94 | uint32_t best_pix_div = INT_MAX >> 1; | ||
95 | uint32_t vt_op_binning_div; | ||
96 | uint32_t lane_op_clock_ratio; | ||
97 | uint32_t mul, div; | ||
98 | uint32_t more_mul_min, more_mul_max; | ||
99 | uint32_t more_mul_factor; | ||
100 | uint32_t min_vt_div, max_vt_div, vt_div; | ||
101 | uint32_t min_sys_div, max_sys_div; | ||
102 | unsigned int i; | ||
103 | int rval; | ||
104 | |||
105 | if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE) | ||
106 | lane_op_clock_ratio = pll->lanes; | ||
107 | else | ||
108 | lane_op_clock_ratio = 1; | ||
109 | dev_dbg(dev, "lane_op_clock_ratio: %d\n", lane_op_clock_ratio); | ||
110 | |||
111 | dev_dbg(dev, "binning: %dx%d\n", pll->binning_horizontal, | ||
112 | pll->binning_vertical); | ||
113 | |||
114 | /* CSI transfers 2 bits per clock per lane; thus times 2 */ | ||
115 | pll->pll_op_clk_freq_hz = pll->link_freq * 2 | ||
116 | * (pll->lanes / lane_op_clock_ratio); | ||
117 | |||
118 | /* Figure out limits for pre-pll divider based on extclk */ | ||
119 | dev_dbg(dev, "min / max pre_pll_clk_div: %d / %d\n", | ||
120 | limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); | ||
121 | limits->max_pre_pll_clk_div = | ||
122 | min_t(uint16_t, limits->max_pre_pll_clk_div, | ||
123 | clk_div_even(pll->ext_clk_freq_hz / | ||
124 | limits->min_pll_ip_freq_hz)); | ||
125 | limits->min_pre_pll_clk_div = | ||
126 | max_t(uint16_t, limits->min_pre_pll_clk_div, | ||
127 | clk_div_even(pll->ext_clk_freq_hz / | ||
128 | limits->max_pll_ip_freq_hz)); | ||
129 | dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %d / %d\n", | ||
130 | limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); | ||
131 | |||
132 | i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz); | ||
133 | mul = div_u64(pll->pll_op_clk_freq_hz, i); | ||
134 | div = pll->ext_clk_freq_hz / i; | ||
135 | dev_dbg(dev, "mul %d / div %d\n", mul, div); | ||
136 | |||
137 | limits->min_pre_pll_clk_div = | ||
138 | max_t(uint16_t, limits->min_pre_pll_clk_div, | ||
139 | clk_div_even_up( | ||
140 | DIV_ROUND_UP(mul * pll->ext_clk_freq_hz, | ||
141 | limits->max_pll_op_freq_hz))); | ||
142 | dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %d / %d\n", | ||
143 | limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); | ||
144 | |||
145 | if (limits->min_pre_pll_clk_div > limits->max_pre_pll_clk_div) { | ||
146 | dev_err(dev, "unable to compute pre_pll divisor\n"); | ||
147 | return -EINVAL; | ||
148 | } | ||
149 | |||
150 | pll->pre_pll_clk_div = limits->min_pre_pll_clk_div; | ||
151 | |||
152 | /* | ||
153 | * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be | ||
154 | * too high. | ||
155 | */ | ||
156 | dev_dbg(dev, "pre_pll_clk_div %d\n", pll->pre_pll_clk_div); | ||
157 | |||
158 | /* Don't go above max pll multiplier. */ | ||
159 | more_mul_max = limits->max_pll_multiplier / mul; | ||
160 | dev_dbg(dev, "more_mul_max: max_pll_multiplier check: %d\n", | ||
161 | more_mul_max); | ||
162 | /* Don't go above max pll op frequency. */ | ||
163 | more_mul_max = | ||
164 | min_t(int, | ||
165 | more_mul_max, | ||
166 | limits->max_pll_op_freq_hz | ||
167 | / (pll->ext_clk_freq_hz / pll->pre_pll_clk_div * mul)); | ||
168 | dev_dbg(dev, "more_mul_max: max_pll_op_freq_hz check: %d\n", | ||
169 | more_mul_max); | ||
170 | /* Don't go above the division capability of op sys clock divider. */ | ||
171 | more_mul_max = min(more_mul_max, | ||
172 | limits->max_op_sys_clk_div * pll->pre_pll_clk_div | ||
173 | / div); | ||
174 | dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %d\n", | ||
175 | more_mul_max); | ||
176 | /* Ensure we won't go above min_pll_multiplier. */ | ||
177 | more_mul_max = min(more_mul_max, | ||
178 | DIV_ROUND_UP(limits->max_pll_multiplier, mul)); | ||
179 | dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %d\n", | ||
180 | more_mul_max); | ||
181 | |||
182 | /* Ensure we won't go below min_pll_op_freq_hz. */ | ||
183 | more_mul_min = DIV_ROUND_UP(limits->min_pll_op_freq_hz, | ||
184 | pll->ext_clk_freq_hz / pll->pre_pll_clk_div | ||
185 | * mul); | ||
186 | dev_dbg(dev, "more_mul_min: min_pll_op_freq_hz check: %d\n", | ||
187 | more_mul_min); | ||
188 | /* Ensure we won't go below min_pll_multiplier. */ | ||
189 | more_mul_min = max(more_mul_min, | ||
190 | DIV_ROUND_UP(limits->min_pll_multiplier, mul)); | ||
191 | dev_dbg(dev, "more_mul_min: min_pll_multiplier check: %d\n", | ||
192 | more_mul_min); | ||
193 | |||
194 | if (more_mul_min > more_mul_max) { | ||
195 | dev_warn(dev, | ||
196 | "unable to compute more_mul_min and more_mul_max"); | ||
197 | return -EINVAL; | ||
198 | } | ||
199 | |||
200 | more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div; | ||
201 | dev_dbg(dev, "more_mul_factor: %d\n", more_mul_factor); | ||
202 | more_mul_factor = lcm(more_mul_factor, limits->min_op_sys_clk_div); | ||
203 | dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n", | ||
204 | more_mul_factor); | ||
205 | i = roundup(more_mul_min, more_mul_factor); | ||
206 | if (!is_one_or_even(i)) | ||
207 | i <<= 1; | ||
208 | |||
209 | dev_dbg(dev, "final more_mul: %d\n", i); | ||
210 | if (i > more_mul_max) { | ||
211 | dev_warn(dev, "final more_mul is bad, max %d", more_mul_max); | ||
212 | return -EINVAL; | ||
213 | } | ||
214 | |||
215 | pll->pll_multiplier = mul * i; | ||
216 | pll->op_sys_clk_div = div * i / pll->pre_pll_clk_div; | ||
217 | dev_dbg(dev, "op_sys_clk_div: %d\n", pll->op_sys_clk_div); | ||
218 | |||
219 | pll->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz | ||
220 | / pll->pre_pll_clk_div; | ||
221 | |||
222 | pll->pll_op_clk_freq_hz = pll->pll_ip_clk_freq_hz | ||
223 | * pll->pll_multiplier; | ||
224 | |||
225 | /* Derive pll_op_clk_freq_hz. */ | ||
226 | pll->op_sys_clk_freq_hz = | ||
227 | pll->pll_op_clk_freq_hz / pll->op_sys_clk_div; | ||
228 | |||
229 | pll->op_pix_clk_div = pll->bits_per_pixel; | ||
230 | dev_dbg(dev, "op_pix_clk_div: %d\n", pll->op_pix_clk_div); | ||
231 | |||
232 | pll->op_pix_clk_freq_hz = | ||
233 | pll->op_sys_clk_freq_hz / pll->op_pix_clk_div; | ||
234 | |||
235 | /* | ||
236 | * Some sensors perform analogue binning and some do this | ||
237 | * digitally. The ones doing this digitally can be roughly be | ||
238 | * found out using this formula. The ones doing this digitally | ||
239 | * should run at higher clock rate, so smaller divisor is used | ||
240 | * on video timing side. | ||
241 | */ | ||
242 | if (limits->min_line_length_pck_bin > limits->min_line_length_pck | ||
243 | / pll->binning_horizontal) | ||
244 | vt_op_binning_div = pll->binning_horizontal; | ||
245 | else | ||
246 | vt_op_binning_div = 1; | ||
247 | dev_dbg(dev, "vt_op_binning_div: %d\n", vt_op_binning_div); | ||
248 | |||
249 | /* | ||
250 | * Profile 2 supports vt_pix_clk_div E [4, 10] | ||
251 | * | ||
252 | * Horizontal binning can be used as a base for difference in | ||
253 | * divisors. One must make sure that horizontal blanking is | ||
254 | * enough to accommodate the CSI-2 sync codes. | ||
255 | * | ||
256 | * Take scaling factor into account as well. | ||
257 | * | ||
258 | * Find absolute limits for the factor of vt divider. | ||
259 | */ | ||
260 | dev_dbg(dev, "scale_m: %d\n", pll->scale_m); | ||
261 | min_vt_div = DIV_ROUND_UP(pll->op_pix_clk_div * pll->op_sys_clk_div | ||
262 | * pll->scale_n, | ||
263 | lane_op_clock_ratio * vt_op_binning_div | ||
264 | * pll->scale_m); | ||
265 | |||
266 | /* Find smallest and biggest allowed vt divisor. */ | ||
267 | dev_dbg(dev, "min_vt_div: %d\n", min_vt_div); | ||
268 | min_vt_div = max(min_vt_div, | ||
269 | DIV_ROUND_UP(pll->pll_op_clk_freq_hz, | ||
270 | limits->max_vt_pix_clk_freq_hz)); | ||
271 | dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %d\n", | ||
272 | min_vt_div); | ||
273 | min_vt_div = max_t(uint32_t, min_vt_div, | ||
274 | limits->min_vt_pix_clk_div | ||
275 | * limits->min_vt_sys_clk_div); | ||
276 | dev_dbg(dev, "min_vt_div: min_vt_clk_div: %d\n", min_vt_div); | ||
277 | |||
278 | max_vt_div = limits->max_vt_sys_clk_div * limits->max_vt_pix_clk_div; | ||
279 | dev_dbg(dev, "max_vt_div: %d\n", max_vt_div); | ||
280 | max_vt_div = min(max_vt_div, | ||
281 | DIV_ROUND_UP(pll->pll_op_clk_freq_hz, | ||
282 | limits->min_vt_pix_clk_freq_hz)); | ||
283 | dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %d\n", | ||
284 | max_vt_div); | ||
285 | |||
286 | /* | ||
287 | * Find limitsits for sys_clk_div. Not all values are possible | ||
288 | * with all values of pix_clk_div. | ||
289 | */ | ||
290 | min_sys_div = limits->min_vt_sys_clk_div; | ||
291 | dev_dbg(dev, "min_sys_div: %d\n", min_sys_div); | ||
292 | min_sys_div = max(min_sys_div, | ||
293 | DIV_ROUND_UP(min_vt_div, | ||
294 | limits->max_vt_pix_clk_div)); | ||
295 | dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %d\n", min_sys_div); | ||
296 | min_sys_div = max(min_sys_div, | ||
297 | pll->pll_op_clk_freq_hz | ||
298 | / limits->max_vt_sys_clk_freq_hz); | ||
299 | dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %d\n", min_sys_div); | ||
300 | min_sys_div = clk_div_even_up(min_sys_div); | ||
301 | dev_dbg(dev, "min_sys_div: one or even: %d\n", min_sys_div); | ||
302 | |||
303 | max_sys_div = limits->max_vt_sys_clk_div; | ||
304 | dev_dbg(dev, "max_sys_div: %d\n", max_sys_div); | ||
305 | max_sys_div = min(max_sys_div, | ||
306 | DIV_ROUND_UP(max_vt_div, | ||
307 | limits->min_vt_pix_clk_div)); | ||
308 | dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %d\n", max_sys_div); | ||
309 | max_sys_div = min(max_sys_div, | ||
310 | DIV_ROUND_UP(pll->pll_op_clk_freq_hz, | ||
311 | limits->min_vt_pix_clk_freq_hz)); | ||
312 | dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %d\n", max_sys_div); | ||
313 | |||
314 | /* | ||
315 | * Find pix_div such that a legal pix_div * sys_div results | ||
316 | * into a value which is not smaller than div, the desired | ||
317 | * divisor. | ||
318 | */ | ||
319 | for (vt_div = min_vt_div; vt_div <= max_vt_div; | ||
320 | vt_div += 2 - (vt_div & 1)) { | ||
321 | for (sys_div = min_sys_div; | ||
322 | sys_div <= max_sys_div; | ||
323 | sys_div += 2 - (sys_div & 1)) { | ||
324 | int pix_div = DIV_ROUND_UP(vt_div, sys_div); | ||
325 | |||
326 | if (pix_div < limits->min_vt_pix_clk_div | ||
327 | || pix_div > limits->max_vt_pix_clk_div) { | ||
328 | dev_dbg(dev, | ||
329 | "pix_div %d too small or too big (%d--%d)\n", | ||
330 | pix_div, | ||
331 | limits->min_vt_pix_clk_div, | ||
332 | limits->max_vt_pix_clk_div); | ||
333 | continue; | ||
334 | } | ||
335 | |||
336 | /* Check if this one is better. */ | ||
337 | if (pix_div * sys_div | ||
338 | <= roundup(min_vt_div, best_pix_div)) | ||
339 | best_pix_div = pix_div; | ||
340 | } | ||
341 | if (best_pix_div < INT_MAX >> 1) | ||
342 | break; | ||
343 | } | ||
344 | |||
345 | pll->vt_sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div); | ||
346 | pll->vt_pix_clk_div = best_pix_div; | ||
347 | |||
348 | pll->vt_sys_clk_freq_hz = | ||
349 | pll->pll_op_clk_freq_hz / pll->vt_sys_clk_div; | ||
350 | pll->vt_pix_clk_freq_hz = | ||
351 | pll->vt_sys_clk_freq_hz / pll->vt_pix_clk_div; | ||
352 | |||
353 | pll->pixel_rate_csi = | ||
354 | pll->op_pix_clk_freq_hz * lane_op_clock_ratio; | ||
355 | |||
356 | print_pll(dev, pll); | ||
357 | |||
358 | rval = bounds_check(dev, pll->pre_pll_clk_div, | ||
359 | limits->min_pre_pll_clk_div, | ||
360 | limits->max_pre_pll_clk_div, "pre_pll_clk_div"); | ||
361 | if (!rval) | ||
362 | rval = bounds_check( | ||
363 | dev, pll->pll_ip_clk_freq_hz, | ||
364 | limits->min_pll_ip_freq_hz, limits->max_pll_ip_freq_hz, | ||
365 | "pll_ip_clk_freq_hz"); | ||
366 | if (!rval) | ||
367 | rval = bounds_check( | ||
368 | dev, pll->pll_multiplier, | ||
369 | limits->min_pll_multiplier, limits->max_pll_multiplier, | ||
370 | "pll_multiplier"); | ||
371 | if (!rval) | ||
372 | rval = bounds_check( | ||
373 | dev, pll->pll_op_clk_freq_hz, | ||
374 | limits->min_pll_op_freq_hz, limits->max_pll_op_freq_hz, | ||
375 | "pll_op_clk_freq_hz"); | ||
376 | if (!rval) | ||
377 | rval = bounds_check( | ||
378 | dev, pll->op_sys_clk_div, | ||
379 | limits->min_op_sys_clk_div, limits->max_op_sys_clk_div, | ||
380 | "op_sys_clk_div"); | ||
381 | if (!rval) | ||
382 | rval = bounds_check( | ||
383 | dev, pll->op_pix_clk_div, | ||
384 | limits->min_op_pix_clk_div, limits->max_op_pix_clk_div, | ||
385 | "op_pix_clk_div"); | ||
386 | if (!rval) | ||
387 | rval = bounds_check( | ||
388 | dev, pll->op_sys_clk_freq_hz, | ||
389 | limits->min_op_sys_clk_freq_hz, | ||
390 | limits->max_op_sys_clk_freq_hz, | ||
391 | "op_sys_clk_freq_hz"); | ||
392 | if (!rval) | ||
393 | rval = bounds_check( | ||
394 | dev, pll->op_pix_clk_freq_hz, | ||
395 | limits->min_op_pix_clk_freq_hz, | ||
396 | limits->max_op_pix_clk_freq_hz, | ||
397 | "op_pix_clk_freq_hz"); | ||
398 | if (!rval) | ||
399 | rval = bounds_check( | ||
400 | dev, pll->vt_sys_clk_freq_hz, | ||
401 | limits->min_vt_sys_clk_freq_hz, | ||
402 | limits->max_vt_sys_clk_freq_hz, | ||
403 | "vt_sys_clk_freq_hz"); | ||
404 | if (!rval) | ||
405 | rval = bounds_check( | ||
406 | dev, pll->vt_pix_clk_freq_hz, | ||
407 | limits->min_vt_pix_clk_freq_hz, | ||
408 | limits->max_vt_pix_clk_freq_hz, | ||
409 | "vt_pix_clk_freq_hz"); | ||
410 | |||
411 | return rval; | ||
412 | } | ||
413 | EXPORT_SYMBOL_GPL(smiapp_pll_calculate); | ||
414 | |||
415 | MODULE_AUTHOR("Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>"); | ||
416 | MODULE_DESCRIPTION("Generic SMIA/SMIA++ PLL calculator"); | ||
417 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/media/video/smiapp-pll.h b/drivers/media/video/smiapp-pll.h new file mode 100644 index 00000000000..9eab63f23af --- /dev/null +++ b/drivers/media/video/smiapp-pll.h | |||
@@ -0,0 +1,103 @@ | |||
1 | /* | ||
2 | * drivers/media/video/smiapp-pll.h | ||
3 | * | ||
4 | * Generic driver for SMIA/SMIA++ compliant camera modules | ||
5 | * | ||
6 | * Copyright (C) 2012 Nokia Corporation | ||
7 | * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * version 2 as published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but | ||
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
16 | * General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
21 | * 02110-1301 USA | ||
22 | * | ||
23 | */ | ||
24 | |||
25 | #ifndef SMIAPP_PLL_H | ||
26 | #define SMIAPP_PLL_H | ||
27 | |||
28 | #include <linux/device.h> | ||
29 | |||
30 | struct smiapp_pll { | ||
31 | uint8_t lanes; | ||
32 | uint8_t binning_horizontal; | ||
33 | uint8_t binning_vertical; | ||
34 | uint8_t scale_m; | ||
35 | uint8_t scale_n; | ||
36 | uint8_t bits_per_pixel; | ||
37 | uint16_t flags; | ||
38 | uint32_t link_freq; | ||
39 | |||
40 | uint16_t pre_pll_clk_div; | ||
41 | uint16_t pll_multiplier; | ||
42 | uint16_t op_sys_clk_div; | ||
43 | uint16_t op_pix_clk_div; | ||
44 | uint16_t vt_sys_clk_div; | ||
45 | uint16_t vt_pix_clk_div; | ||
46 | |||
47 | uint32_t ext_clk_freq_hz; | ||
48 | uint32_t pll_ip_clk_freq_hz; | ||
49 | uint32_t pll_op_clk_freq_hz; | ||
50 | uint32_t op_sys_clk_freq_hz; | ||
51 | uint32_t op_pix_clk_freq_hz; | ||
52 | uint32_t vt_sys_clk_freq_hz; | ||
53 | uint32_t vt_pix_clk_freq_hz; | ||
54 | |||
55 | uint32_t pixel_rate_csi; | ||
56 | }; | ||
57 | |||
58 | struct smiapp_pll_limits { | ||
59 | /* Strict PLL limits */ | ||
60 | uint32_t min_ext_clk_freq_hz; | ||
61 | uint32_t max_ext_clk_freq_hz; | ||
62 | uint16_t min_pre_pll_clk_div; | ||
63 | uint16_t max_pre_pll_clk_div; | ||
64 | uint32_t min_pll_ip_freq_hz; | ||
65 | uint32_t max_pll_ip_freq_hz; | ||
66 | uint16_t min_pll_multiplier; | ||
67 | uint16_t max_pll_multiplier; | ||
68 | uint32_t min_pll_op_freq_hz; | ||
69 | uint32_t max_pll_op_freq_hz; | ||
70 | |||
71 | uint16_t min_vt_sys_clk_div; | ||
72 | uint16_t max_vt_sys_clk_div; | ||
73 | uint32_t min_vt_sys_clk_freq_hz; | ||
74 | uint32_t max_vt_sys_clk_freq_hz; | ||
75 | uint16_t min_vt_pix_clk_div; | ||
76 | uint16_t max_vt_pix_clk_div; | ||
77 | uint32_t min_vt_pix_clk_freq_hz; | ||
78 | uint32_t max_vt_pix_clk_freq_hz; | ||
79 | |||
80 | uint16_t min_op_sys_clk_div; | ||
81 | uint16_t max_op_sys_clk_div; | ||
82 | uint32_t min_op_sys_clk_freq_hz; | ||
83 | uint32_t max_op_sys_clk_freq_hz; | ||
84 | uint16_t min_op_pix_clk_div; | ||
85 | uint16_t max_op_pix_clk_div; | ||
86 | uint32_t min_op_pix_clk_freq_hz; | ||
87 | uint32_t max_op_pix_clk_freq_hz; | ||
88 | |||
89 | /* Other relevant limits */ | ||
90 | uint32_t min_line_length_pck_bin; | ||
91 | uint32_t min_line_length_pck; | ||
92 | }; | ||
93 | |||
94 | /* op pix clock is for all lanes in total normally */ | ||
95 | #define SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0) | ||
96 | #define SMIAPP_PLL_FLAG_NO_OP_CLOCKS (1 << 1) | ||
97 | |||
98 | struct device; | ||
99 | |||
100 | int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, | ||
101 | struct smiapp_pll *pll); | ||
102 | |||
103 | #endif /* SMIAPP_PLL_H */ | ||