1
2
3
4
5
6
7
8#include <linux/device.h>
9#include <linux/gcd.h>
10#include <linux/kernel.h>
11#include <linux/lcm.h>
12#include <linux/module.h>
13
14#include "aptina-pll.h"
15
16int aptina_pll_calculate(struct device *dev,
17 const struct aptina_pll_limits *limits,
18 struct aptina_pll *pll)
19{
20 unsigned int mf_min;
21 unsigned int mf_max;
22 unsigned int p1_min;
23 unsigned int p1_max;
24 unsigned int p1;
25 unsigned int div;
26
27 dev_dbg(dev, "PLL: ext clock %u pix clock %u\n",
28 pll->ext_clock, pll->pix_clock);
29
30 if (pll->ext_clock < limits->ext_clock_min ||
31 pll->ext_clock > limits->ext_clock_max) {
32 dev_err(dev, "pll: invalid external clock frequency.\n");
33 return -EINVAL;
34 }
35
36 if (pll->pix_clock == 0 || pll->pix_clock > limits->pix_clock_max) {
37 dev_err(dev, "pll: invalid pixel clock frequency.\n");
38 return -EINVAL;
39 }
40
41
42 div = gcd(pll->pix_clock, pll->ext_clock);
43 pll->m = pll->pix_clock / div;
44 div = pll->ext_clock / div;
45
46
47
48
49
50
51
52
53
54
55
56 mf_min = DIV_ROUND_UP(limits->m_min, pll->m);
57 mf_min = max(mf_min, limits->out_clock_min /
58 (pll->ext_clock / limits->n_min * pll->m));
59 mf_min = max(mf_min, limits->n_min * limits->p1_min / div);
60 mf_max = limits->m_max / pll->m;
61 mf_max = min(mf_max, limits->out_clock_max /
62 (pll->ext_clock / limits->n_max * pll->m));
63 mf_max = min(mf_max, DIV_ROUND_UP(limits->n_max * limits->p1_max, div));
64
65 dev_dbg(dev, "pll: mf min %u max %u\n", mf_min, mf_max);
66 if (mf_min > mf_max) {
67 dev_err(dev, "pll: no valid combined N*P1 divisor.\n");
68 return -EINVAL;
69 }
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123 if (limits->p1_min == 0) {
124 dev_err(dev, "pll: P1 minimum value must be >0.\n");
125 return -EINVAL;
126 }
127
128 p1_min = max(limits->p1_min, DIV_ROUND_UP(limits->out_clock_min * div,
129 pll->ext_clock * pll->m));
130 p1_max = min(limits->p1_max, limits->out_clock_max * div /
131 (pll->ext_clock * pll->m));
132
133 for (p1 = p1_max & ~1; p1 >= p1_min; p1 -= 2) {
134 unsigned int mf_inc = p1 / gcd(div, p1);
135 unsigned int mf_high;
136 unsigned int mf_low;
137
138 mf_low = roundup(max(mf_min, DIV_ROUND_UP(pll->ext_clock * p1,
139 limits->int_clock_max * div)), mf_inc);
140 mf_high = min(mf_max, pll->ext_clock * p1 /
141 (limits->int_clock_min * div));
142
143 if (mf_low > mf_high)
144 continue;
145
146 pll->n = div * mf_low / p1;
147 pll->m *= mf_low;
148 pll->p1 = p1;
149 dev_dbg(dev, "PLL: N %u M %u P1 %u\n", pll->n, pll->m, pll->p1);
150 return 0;
151 }
152
153 dev_err(dev, "pll: no valid N and P1 divisors found.\n");
154 return -EINVAL;
155}
156EXPORT_SYMBOL_GPL(aptina_pll_calculate);
157
158MODULE_DESCRIPTION("Aptina PLL Helpers");
159MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
160MODULE_LICENSE("GPL v2");
161