0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/device.h>
0009 #include <linux/gcd.h>
0010 #include <linux/kernel.h>
0011 #include <linux/lcm.h>
0012 #include <linux/module.h>
0013
0014 #include "aptina-pll.h"
0015
0016 int aptina_pll_calculate(struct device *dev,
0017 const struct aptina_pll_limits *limits,
0018 struct aptina_pll *pll)
0019 {
0020 unsigned int mf_min;
0021 unsigned int mf_max;
0022 unsigned int p1_min;
0023 unsigned int p1_max;
0024 unsigned int p1;
0025 unsigned int div;
0026
0027 dev_dbg(dev, "PLL: ext clock %u pix clock %u\n",
0028 pll->ext_clock, pll->pix_clock);
0029
0030 if (pll->ext_clock < limits->ext_clock_min ||
0031 pll->ext_clock > limits->ext_clock_max) {
0032 dev_err(dev, "pll: invalid external clock frequency.\n");
0033 return -EINVAL;
0034 }
0035
0036 if (pll->pix_clock == 0 || pll->pix_clock > limits->pix_clock_max) {
0037 dev_err(dev, "pll: invalid pixel clock frequency.\n");
0038 return -EINVAL;
0039 }
0040
0041
0042 div = gcd(pll->pix_clock, pll->ext_clock);
0043 pll->m = pll->pix_clock / div;
0044 div = pll->ext_clock / div;
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056 mf_min = DIV_ROUND_UP(limits->m_min, pll->m);
0057 mf_min = max(mf_min, limits->out_clock_min /
0058 (pll->ext_clock / limits->n_min * pll->m));
0059 mf_min = max(mf_min, limits->n_min * limits->p1_min / div);
0060 mf_max = limits->m_max / pll->m;
0061 mf_max = min(mf_max, limits->out_clock_max /
0062 (pll->ext_clock / limits->n_max * pll->m));
0063 mf_max = min(mf_max, DIV_ROUND_UP(limits->n_max * limits->p1_max, div));
0064
0065 dev_dbg(dev, "pll: mf min %u max %u\n", mf_min, mf_max);
0066 if (mf_min > mf_max) {
0067 dev_err(dev, "pll: no valid combined N*P1 divisor.\n");
0068 return -EINVAL;
0069 }
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123 if (limits->p1_min == 0) {
0124 dev_err(dev, "pll: P1 minimum value must be >0.\n");
0125 return -EINVAL;
0126 }
0127
0128 p1_min = max(limits->p1_min, DIV_ROUND_UP(limits->out_clock_min * div,
0129 pll->ext_clock * pll->m));
0130 p1_max = min(limits->p1_max, limits->out_clock_max * div /
0131 (pll->ext_clock * pll->m));
0132
0133 for (p1 = p1_max & ~1; p1 >= p1_min; p1 -= 2) {
0134 unsigned int mf_inc = p1 / gcd(div, p1);
0135 unsigned int mf_high;
0136 unsigned int mf_low;
0137
0138 mf_low = roundup(max(mf_min, DIV_ROUND_UP(pll->ext_clock * p1,
0139 limits->int_clock_max * div)), mf_inc);
0140 mf_high = min(mf_max, pll->ext_clock * p1 /
0141 (limits->int_clock_min * div));
0142
0143 if (mf_low > mf_high)
0144 continue;
0145
0146 pll->n = div * mf_low / p1;
0147 pll->m *= mf_low;
0148 pll->p1 = p1;
0149 dev_dbg(dev, "PLL: N %u M %u P1 %u\n", pll->n, pll->m, pll->p1);
0150 return 0;
0151 }
0152
0153 dev_err(dev, "pll: no valid N and P1 divisors found.\n");
0154 return -EINVAL;
0155 }
0156 EXPORT_SYMBOL_GPL(aptina_pll_calculate);
0157
0158 MODULE_DESCRIPTION("Aptina PLL Helpers");
0159 MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
0160 MODULE_LICENSE("GPL v2");