diff options
author | Sakari Ailus <sakari.ailus@iki.fi> | 2012-10-22 15:27:27 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-10-29 07:49:30 -0400 |
commit | 6de1b143a45d3c4b8ad277b484e0d14df0d30d63 (patch) | |
tree | 7776cc0963b1f3793713f7d07e4411990d9c1ba1 | |
parent | c2ebca00324ce7b89f756d5e182a5b3db938a4c9 (diff) |
[media] smiapp-pll: Try other pre-pll divisors
The smiapp pll calculator assumed that the minimum pre-pll divisor was
perfect. That may not always be the case, so let's try the others, too.
Typically there are just a few alternatives.
Signed-off-by: Sakari Ailus <sakari.ailus@iki.fi>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/i2c/smiapp-pll.c | 131 |
1 files changed, 73 insertions, 58 deletions
diff --git a/drivers/media/i2c/smiapp-pll.c b/drivers/media/i2c/smiapp-pll.c index e92dc46e6221..d7e347594e19 100644 --- a/drivers/media/i2c/smiapp-pll.c +++ b/drivers/media/i2c/smiapp-pll.c | |||
@@ -58,7 +58,7 @@ static int bounds_check(struct device *dev, uint32_t val, | |||
58 | if (val >= min && val <= max) | 58 | if (val >= min && val <= max) |
59 | return 0; | 59 | return 0; |
60 | 60 | ||
61 | dev_warn(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max); | 61 | dev_dbg(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max); |
62 | 62 | ||
63 | return -EINVAL; | 63 | return -EINVAL; |
64 | } | 64 | } |
@@ -87,14 +87,14 @@ static void print_pll(struct device *dev, struct smiapp_pll *pll) | |||
87 | dev_dbg(dev, "vt_pix_clk_freq_hz \t%d\n", pll->vt_pix_clk_freq_hz); | 87 | dev_dbg(dev, "vt_pix_clk_freq_hz \t%d\n", pll->vt_pix_clk_freq_hz); |
88 | } | 88 | } |
89 | 89 | ||
90 | int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, | 90 | static int __smiapp_pll_calculate(struct device *dev, |
91 | struct smiapp_pll *pll) | 91 | struct smiapp_pll_limits *limits, |
92 | struct smiapp_pll *pll, uint32_t mul, | ||
93 | uint32_t div, uint32_t lane_op_clock_ratio) | ||
92 | { | 94 | { |
93 | uint32_t sys_div; | 95 | uint32_t sys_div; |
94 | uint32_t best_pix_div = INT_MAX >> 1; | 96 | uint32_t best_pix_div = INT_MAX >> 1; |
95 | uint32_t vt_op_binning_div; | 97 | 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; | 98 | uint32_t more_mul_min, more_mul_max; |
99 | uint32_t more_mul_factor; | 99 | uint32_t more_mul_factor; |
100 | uint32_t min_vt_div, max_vt_div, vt_div; | 100 | uint32_t min_vt_div, max_vt_div, vt_div; |
@@ -102,54 +102,6 @@ int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, | |||
102 | unsigned int i; | 102 | unsigned int i; |
103 | int rval; | 103 | int rval; |
104 | 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_up( | ||
128 | DIV_ROUND_UP(pll->ext_clk_freq_hz, | ||
129 | limits->max_pll_ip_freq_hz))); | ||
130 | dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %d / %d\n", | ||
131 | limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); | ||
132 | |||
133 | i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz); | ||
134 | mul = div_u64(pll->pll_op_clk_freq_hz, i); | ||
135 | div = pll->ext_clk_freq_hz / i; | ||
136 | dev_dbg(dev, "mul %d / div %d\n", mul, div); | ||
137 | |||
138 | limits->min_pre_pll_clk_div = | ||
139 | max_t(uint16_t, limits->min_pre_pll_clk_div, | ||
140 | clk_div_even_up( | ||
141 | DIV_ROUND_UP(mul * pll->ext_clk_freq_hz, | ||
142 | limits->max_pll_op_freq_hz))); | ||
143 | dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %d / %d\n", | ||
144 | limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); | ||
145 | |||
146 | if (limits->min_pre_pll_clk_div > limits->max_pre_pll_clk_div) { | ||
147 | dev_err(dev, "unable to compute pre_pll divisor\n"); | ||
148 | return -EINVAL; | ||
149 | } | ||
150 | |||
151 | pll->pre_pll_clk_div = limits->min_pre_pll_clk_div; | ||
152 | |||
153 | /* | 105 | /* |
154 | * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be | 106 | * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be |
155 | * too high. | 107 | * too high. |
@@ -193,8 +145,8 @@ int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, | |||
193 | more_mul_min); | 145 | more_mul_min); |
194 | 146 | ||
195 | if (more_mul_min > more_mul_max) { | 147 | if (more_mul_min > more_mul_max) { |
196 | dev_warn(dev, | 148 | dev_dbg(dev, |
197 | "unable to compute more_mul_min and more_mul_max\n"); | 149 | "unable to compute more_mul_min and more_mul_max\n"); |
198 | return -EINVAL; | 150 | return -EINVAL; |
199 | } | 151 | } |
200 | 152 | ||
@@ -209,7 +161,7 @@ int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, | |||
209 | 161 | ||
210 | dev_dbg(dev, "final more_mul: %d\n", i); | 162 | dev_dbg(dev, "final more_mul: %d\n", i); |
211 | if (i > more_mul_max) { | 163 | if (i > more_mul_max) { |
212 | dev_warn(dev, "final more_mul is bad, max %d\n", more_mul_max); | 164 | dev_dbg(dev, "final more_mul is bad, max %d\n", more_mul_max); |
213 | return -EINVAL; | 165 | return -EINVAL; |
214 | } | 166 | } |
215 | 167 | ||
@@ -354,8 +306,6 @@ int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, | |||
354 | pll->pixel_rate_csi = | 306 | pll->pixel_rate_csi = |
355 | pll->op_pix_clk_freq_hz * lane_op_clock_ratio; | 307 | pll->op_pix_clk_freq_hz * lane_op_clock_ratio; |
356 | 308 | ||
357 | print_pll(dev, pll); | ||
358 | |||
359 | rval = bounds_check(dev, pll->pre_pll_clk_div, | 309 | rval = bounds_check(dev, pll->pre_pll_clk_div, |
360 | limits->min_pre_pll_clk_div, | 310 | limits->min_pre_pll_clk_div, |
361 | limits->max_pre_pll_clk_div, "pre_pll_clk_div"); | 311 | limits->max_pre_pll_clk_div, "pre_pll_clk_div"); |
@@ -411,6 +361,71 @@ int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, | |||
411 | 361 | ||
412 | return rval; | 362 | return rval; |
413 | } | 363 | } |
364 | |||
365 | int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, | ||
366 | struct smiapp_pll *pll) | ||
367 | { | ||
368 | uint32_t lane_op_clock_ratio; | ||
369 | uint32_t mul, div; | ||
370 | unsigned int i; | ||
371 | int rval = -EINVAL; | ||
372 | |||
373 | if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE) | ||
374 | lane_op_clock_ratio = pll->lanes; | ||
375 | else | ||
376 | lane_op_clock_ratio = 1; | ||
377 | dev_dbg(dev, "lane_op_clock_ratio: %d\n", lane_op_clock_ratio); | ||
378 | |||
379 | dev_dbg(dev, "binning: %dx%d\n", pll->binning_horizontal, | ||
380 | pll->binning_vertical); | ||
381 | |||
382 | /* CSI transfers 2 bits per clock per lane; thus times 2 */ | ||
383 | pll->pll_op_clk_freq_hz = pll->link_freq * 2 | ||
384 | * (pll->lanes / lane_op_clock_ratio); | ||
385 | |||
386 | /* Figure out limits for pre-pll divider based on extclk */ | ||
387 | dev_dbg(dev, "min / max pre_pll_clk_div: %d / %d\n", | ||
388 | limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); | ||
389 | limits->max_pre_pll_clk_div = | ||
390 | min_t(uint16_t, limits->max_pre_pll_clk_div, | ||
391 | clk_div_even(pll->ext_clk_freq_hz / | ||
392 | limits->min_pll_ip_freq_hz)); | ||
393 | limits->min_pre_pll_clk_div = | ||
394 | max_t(uint16_t, limits->min_pre_pll_clk_div, | ||
395 | clk_div_even_up( | ||
396 | DIV_ROUND_UP(pll->ext_clk_freq_hz, | ||
397 | limits->max_pll_ip_freq_hz))); | ||
398 | dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %d / %d\n", | ||
399 | limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); | ||
400 | |||
401 | i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz); | ||
402 | mul = div_u64(pll->pll_op_clk_freq_hz, i); | ||
403 | div = pll->ext_clk_freq_hz / i; | ||
404 | dev_dbg(dev, "mul %d / div %d\n", mul, div); | ||
405 | |||
406 | limits->min_pre_pll_clk_div = | ||
407 | max_t(uint16_t, limits->min_pre_pll_clk_div, | ||
408 | clk_div_even_up( | ||
409 | DIV_ROUND_UP(mul * pll->ext_clk_freq_hz, | ||
410 | limits->max_pll_op_freq_hz))); | ||
411 | dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %d / %d\n", | ||
412 | limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); | ||
413 | |||
414 | for (pll->pre_pll_clk_div = limits->min_pre_pll_clk_div; | ||
415 | pll->pre_pll_clk_div <= limits->max_pre_pll_clk_div; | ||
416 | pll->pre_pll_clk_div += 2 - (pll->pre_pll_clk_div & 1)) { | ||
417 | rval = __smiapp_pll_calculate(dev, limits, pll, mul, div, | ||
418 | lane_op_clock_ratio); | ||
419 | if (rval) | ||
420 | continue; | ||
421 | |||
422 | print_pll(dev, pll); | ||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | dev_info(dev, "unable to compute pre_pll divisor\n"); | ||
427 | return rval; | ||
428 | } | ||
414 | EXPORT_SYMBOL_GPL(smiapp_pll_calculate); | 429 | EXPORT_SYMBOL_GPL(smiapp_pll_calculate); |
415 | 430 | ||
416 | MODULE_AUTHOR("Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>"); | 431 | MODULE_AUTHOR("Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>"); |