aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video
diff options
context:
space:
mode:
authorTomi Valkeinen <tomi.valkeinen@ti.com>2014-10-22 07:21:59 -0400
committerTomi Valkeinen <tomi.valkeinen@ti.com>2014-11-12 06:40:23 -0500
commit0a20170aa5ef1c4935663d88cbfc17b682326fc0 (patch)
tree1058ec9c4816ab53c8f8b5683d3f50e57ab28668 /drivers/video
parentf76b178a78e3aae0e88d88fb4113c3778ff4df70 (diff)
OMAPDSS: Add common PLL code
OMAP DSS currently contains two different PLLs: DSI PLL (Type A PLL) and HDMI PLL (Type B PLL). When DRA7 support is added, we will also support Video PLLs (Type A). The driver currently handles all PLLs totally separately. This patch adds common DSS PLL code, which a) lets us have common code for the PLLs b) lets the users of the PLLs use a common API, instead of DSI API or HDMI API. Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/fbdev/omap2/dss/Makefile2
-rw-r--r--drivers/video/fbdev/omap2/dss/dss.h88
-rw-r--r--drivers/video/fbdev/omap2/dss/pll.c378
3 files changed, 467 insertions, 1 deletions
diff --git a/drivers/video/fbdev/omap2/dss/Makefile b/drivers/video/fbdev/omap2/dss/Makefile
index 245f933060ee..2ea9d382354c 100644
--- a/drivers/video/fbdev/omap2/dss/Makefile
+++ b/drivers/video/fbdev/omap2/dss/Makefile
@@ -2,7 +2,7 @@ obj-$(CONFIG_OMAP2_DSS_INIT) += omapdss-boot-init.o
2obj-$(CONFIG_OMAP2_DSS) += omapdss.o 2obj-$(CONFIG_OMAP2_DSS) += omapdss.o
3# Core DSS files 3# Core DSS files
4omapdss-y := core.o dss.o dss_features.o dispc.o dispc_coefs.o display.o \ 4omapdss-y := core.o dss.o dss_features.o dispc.o dispc_coefs.o display.o \
5 output.o dss-of.o 5 output.o dss-of.o pll.o
6# DSS compat layer files 6# DSS compat layer files
7omapdss-y += manager.o manager-sysfs.o overlay.o overlay-sysfs.o apply.o \ 7omapdss-y += manager.o manager-sysfs.o overlay.o overlay-sysfs.o apply.o \
8 dispc-compat.o display-sysfs.o 8 dispc-compat.o display-sysfs.o
diff --git a/drivers/video/fbdev/omap2/dss/dss.h b/drivers/video/fbdev/omap2/dss/dss.h
index 4c268985235c..8563e2bc702c 100644
--- a/drivers/video/fbdev/omap2/dss/dss.h
+++ b/drivers/video/fbdev/omap2/dss/dss.h
@@ -100,6 +100,69 @@ enum dss_writeback_channel {
100 DSS_WB_LCD3_MGR = 7, 100 DSS_WB_LCD3_MGR = 7,
101}; 101};
102 102
103struct dss_pll;
104
105#define DSS_PLL_MAX_HSDIVS 4
106
107/*
108 * Type-A PLLs: clkout[]/mX[] refer to hsdiv outputs m4, m5, m6, m7.
109 * Type-B PLLs: clkout[0] refers to m2.
110 */
111struct dss_pll_clock_info {
112 /* rates that we get with dividers below */
113 unsigned long fint;
114 unsigned long clkdco;
115 unsigned long clkout[DSS_PLL_MAX_HSDIVS];
116
117 /* dividers */
118 u16 n;
119 u16 m;
120 u32 mf;
121 u16 mX[DSS_PLL_MAX_HSDIVS];
122 u16 sd;
123};
124
125struct dss_pll_ops {
126 int (*enable)(struct dss_pll *pll);
127 void (*disable)(struct dss_pll *pll);
128 int (*set_config)(struct dss_pll *pll,
129 const struct dss_pll_clock_info *cinfo);
130};
131
132struct dss_pll_hw {
133 unsigned n_max;
134 unsigned m_min;
135 unsigned m_max;
136 unsigned mX_max;
137
138 unsigned long fint_min, fint_max;
139 unsigned long clkdco_min, clkdco_low, clkdco_max;
140
141 u8 n_msb, n_lsb;
142 u8 m_msb, m_lsb;
143 u8 mX_msb[DSS_PLL_MAX_HSDIVS], mX_lsb[DSS_PLL_MAX_HSDIVS];
144
145 bool has_stopmode;
146 bool has_freqsel;
147 bool has_selfreqdco;
148 bool has_refsel;
149};
150
151struct dss_pll {
152 const char *name;
153
154 struct clk *clkin;
155 struct regulator *regulator;
156
157 void __iomem *base;
158
159 const struct dss_pll_hw *hw;
160
161 const struct dss_pll_ops *ops;
162
163 struct dss_pll_clock_info cinfo;
164};
165
103struct dispc_clock_info { 166struct dispc_clock_info {
104 /* rates that we get with dividers below */ 167 /* rates that we get with dividers below */
105 unsigned long lck; 168 unsigned long lck;
@@ -435,4 +498,29 @@ static inline void dss_collect_irq_stats(u32 irqstatus, unsigned *irq_arr)
435} 498}
436#endif 499#endif
437 500
501/* PLL */
502typedef bool (*dss_pll_calc_func)(int n, int m, unsigned long fint,
503 unsigned long clkdco, void *data);
504typedef bool (*dss_hsdiv_calc_func)(int m_dispc, unsigned long dispc,
505 void *data);
506
507int dss_pll_register(struct dss_pll *pll);
508void dss_pll_unregister(struct dss_pll *pll);
509struct dss_pll *dss_pll_find(const char *name);
510int dss_pll_enable(struct dss_pll *pll);
511void dss_pll_disable(struct dss_pll *pll);
512int dss_pll_set_config(struct dss_pll *pll,
513 const struct dss_pll_clock_info *cinfo);
514
515bool dss_pll_hsdiv_calc(const struct dss_pll *pll, unsigned long clkdco,
516 unsigned long out_min, unsigned long out_max,
517 dss_hsdiv_calc_func func, void *data);
518bool dss_pll_calc(const struct dss_pll *pll, unsigned long clkin,
519 unsigned long pll_min, unsigned long pll_max,
520 dss_pll_calc_func func, void *data);
521int dss_pll_write_config_type_a(struct dss_pll *pll,
522 const struct dss_pll_clock_info *cinfo);
523int dss_pll_write_config_type_b(struct dss_pll *pll,
524 const struct dss_pll_clock_info *cinfo);
525
438#endif 526#endif
diff --git a/drivers/video/fbdev/omap2/dss/pll.c b/drivers/video/fbdev/omap2/dss/pll.c
new file mode 100644
index 000000000000..50bc62c5d367
--- /dev/null
+++ b/drivers/video/fbdev/omap2/dss/pll.c
@@ -0,0 +1,378 @@
1/*
2 * Copyright (C) 2014 Texas Instruments Incorporated
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License along with
14 * this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#define DSS_SUBSYS_NAME "PLL"
18
19#include <linux/clk.h>
20#include <linux/io.h>
21#include <linux/kernel.h>
22#include <linux/regulator/consumer.h>
23#include <linux/sched.h>
24
25#include <video/omapdss.h>
26
27#include "dss.h"
28
29#define PLL_CONTROL 0x0000
30#define PLL_STATUS 0x0004
31#define PLL_GO 0x0008
32#define PLL_CONFIGURATION1 0x000C
33#define PLL_CONFIGURATION2 0x0010
34#define PLL_CONFIGURATION3 0x0014
35#define PLL_SSC_CONFIGURATION1 0x0018
36#define PLL_SSC_CONFIGURATION2 0x001C
37#define PLL_CONFIGURATION4 0x0020
38
39static struct dss_pll *dss_plls[4];
40
41int dss_pll_register(struct dss_pll *pll)
42{
43 int i;
44
45 for (i = 0; i < ARRAY_SIZE(dss_plls); ++i) {
46 if (!dss_plls[i]) {
47 dss_plls[i] = pll;
48 return 0;
49 }
50 }
51
52 return -EBUSY;
53}
54
55void dss_pll_unregister(struct dss_pll *pll)
56{
57 int i;
58
59 for (i = 0; i < ARRAY_SIZE(dss_plls); ++i) {
60 if (dss_plls[i] == pll) {
61 dss_plls[i] = NULL;
62 return;
63 }
64 }
65}
66
67struct dss_pll *dss_pll_find(const char *name)
68{
69 int i;
70
71 for (i = 0; i < ARRAY_SIZE(dss_plls); ++i) {
72 if (dss_plls[i] && strcmp(dss_plls[i]->name, name) == 0)
73 return dss_plls[i];
74 }
75
76 return NULL;
77}
78
79int dss_pll_enable(struct dss_pll *pll)
80{
81 int r;
82
83 r = clk_prepare_enable(pll->clkin);
84 if (r)
85 return r;
86
87 if (pll->regulator) {
88 r = regulator_enable(pll->regulator);
89 if (r)
90 goto err_reg;
91 }
92
93 r = pll->ops->enable(pll);
94 if (r)
95 goto err_enable;
96
97 return 0;
98
99err_enable:
100 regulator_disable(pll->regulator);
101err_reg:
102 clk_disable_unprepare(pll->clkin);
103 return r;
104}
105
106void dss_pll_disable(struct dss_pll *pll)
107{
108 pll->ops->disable(pll);
109
110 if (pll->regulator)
111 regulator_disable(pll->regulator);
112
113 clk_disable_unprepare(pll->clkin);
114
115 memset(&pll->cinfo, 0, sizeof(pll->cinfo));
116}
117
118int dss_pll_set_config(struct dss_pll *pll, const struct dss_pll_clock_info *cinfo)
119{
120 int r;
121
122 r = pll->ops->set_config(pll, cinfo);
123 if (r)
124 return r;
125
126 pll->cinfo = *cinfo;
127
128 return 0;
129}
130
131bool dss_pll_hsdiv_calc(const struct dss_pll *pll, unsigned long clkdco,
132 unsigned long out_min, unsigned long out_max,
133 dss_hsdiv_calc_func func, void *data)
134{
135 const struct dss_pll_hw *hw = pll->hw;
136 int m, m_start, m_stop;
137 unsigned long out;
138
139 out_min = out_min ? out_min : 1;
140 out_max = out_max ? out_max : ULONG_MAX;
141
142 m_start = max(DIV_ROUND_UP(clkdco, out_max), 1ul);
143
144 m_stop = min((unsigned)(clkdco / out_min), hw->mX_max);
145
146 for (m = m_start; m <= m_stop; ++m) {
147 out = clkdco / m;
148
149 if (func(m, out, data))
150 return true;
151 }
152
153 return false;
154}
155
156bool dss_pll_calc(const struct dss_pll *pll, unsigned long clkin,
157 unsigned long pll_min, unsigned long pll_max,
158 dss_pll_calc_func func, void *data)
159{
160 const struct dss_pll_hw *hw = pll->hw;
161 int n, n_start, n_stop;
162 int m, m_start, m_stop;
163 unsigned long fint, clkdco;
164 unsigned long pll_hw_max;
165 unsigned long fint_hw_min, fint_hw_max;
166
167 pll_hw_max = hw->clkdco_max;
168
169 fint_hw_min = hw->fint_min;
170 fint_hw_max = hw->fint_max;
171
172 n_start = max(DIV_ROUND_UP(clkin, fint_hw_max), 1ul);
173 n_stop = min((unsigned)(clkin / fint_hw_min), hw->n_max);
174
175 pll_max = pll_max ? pll_max : ULONG_MAX;
176
177 for (n = n_start; n <= n_stop; ++n) {
178 fint = clkin / n;
179
180 m_start = max(DIV_ROUND_UP(DIV_ROUND_UP(pll_min, fint), 2),
181 1ul);
182 m_stop = min3((unsigned)(pll_max / fint / 2),
183 (unsigned)(pll_hw_max / fint / 2),
184 hw->m_max);
185
186 for (m = m_start; m <= m_stop; ++m) {
187 clkdco = 2 * m * fint;
188
189 if (func(n, m, fint, clkdco, data))
190 return true;
191 }
192 }
193
194 return false;
195}
196
197static int wait_for_bit_change(void __iomem *reg, int bitnum, int value)
198{
199 unsigned long timeout;
200 ktime_t wait;
201 int t;
202
203 /* first busyloop to see if the bit changes right away */
204 t = 100;
205 while (t-- > 0) {
206 if (FLD_GET(readl_relaxed(reg), bitnum, bitnum) == value)
207 return value;
208 }
209
210 /* then loop for 500ms, sleeping for 1ms in between */
211 timeout = jiffies + msecs_to_jiffies(500);
212 while (time_before(jiffies, timeout)) {
213 if (FLD_GET(readl_relaxed(reg), bitnum, bitnum) == value)
214 return value;
215
216 wait = ns_to_ktime(1000 * 1000);
217 set_current_state(TASK_UNINTERRUPTIBLE);
218 schedule_hrtimeout(&wait, HRTIMER_MODE_REL);
219 }
220
221 return !value;
222}
223
224static int dss_wait_hsdiv_ack(struct dss_pll *pll, u32 hsdiv_ack_mask)
225{
226 int t = 100;
227
228 while (t-- > 0) {
229 u32 v = readl_relaxed(pll->base + PLL_STATUS);
230 v &= hsdiv_ack_mask;
231 if (v == hsdiv_ack_mask)
232 return 0;
233 }
234
235 return -ETIMEDOUT;
236}
237
238int dss_pll_write_config_type_a(struct dss_pll *pll,
239 const struct dss_pll_clock_info *cinfo)
240{
241 const struct dss_pll_hw *hw = pll->hw;
242 void __iomem *base = pll->base;
243 int r = 0;
244 u32 l;
245
246 l = 0;
247 if (hw->has_stopmode)
248 l = FLD_MOD(l, 1, 0, 0); /* PLL_STOPMODE */
249 l = FLD_MOD(l, cinfo->n - 1, hw->n_msb, hw->n_lsb); /* PLL_REGN */
250 l = FLD_MOD(l, cinfo->m, hw->m_msb, hw->m_lsb); /* PLL_REGM */
251 /* M4 */
252 l = FLD_MOD(l, cinfo->mX[0] ? cinfo->mX[0] - 1 : 0,
253 hw->mX_msb[0], hw->mX_lsb[0]);
254 /* M5 */
255 l = FLD_MOD(l, cinfo->mX[1] ? cinfo->mX[1] - 1 : 0,
256 hw->mX_msb[1], hw->mX_lsb[1]);
257 writel_relaxed(l, base + PLL_CONFIGURATION1);
258
259 l = 0;
260 /* M6 */
261 l = FLD_MOD(l, cinfo->mX[2] ? cinfo->mX[2] - 1 : 0,
262 hw->mX_msb[2], hw->mX_lsb[2]);
263 /* M7 */
264 l = FLD_MOD(l, cinfo->mX[3] ? cinfo->mX[3] - 1 : 0,
265 hw->mX_msb[3], hw->mX_lsb[3]);
266 writel_relaxed(l, base + PLL_CONFIGURATION3);
267
268 l = readl_relaxed(base + PLL_CONFIGURATION2);
269 if (hw->has_freqsel) {
270 u32 f = cinfo->fint < 1000000 ? 0x3 :
271 cinfo->fint < 1250000 ? 0x4 :
272 cinfo->fint < 1500000 ? 0x5 :
273 cinfo->fint < 1750000 ? 0x6 :
274 0x7;
275
276 l = FLD_MOD(l, f, 4, 1); /* PLL_FREQSEL */
277 } else if (hw->has_selfreqdco) {
278 u32 f = cinfo->clkdco < hw->clkdco_low ? 0x2 : 0x4;
279
280 l = FLD_MOD(l, f, 3, 1); /* PLL_SELFREQDCO */
281 }
282 l = FLD_MOD(l, 1, 13, 13); /* PLL_REFEN */
283 l = FLD_MOD(l, 0, 14, 14); /* PHY_CLKINEN */
284 l = FLD_MOD(l, 0, 16, 16); /* M4_CLOCK_EN */
285 l = FLD_MOD(l, 0, 18, 18); /* M5_CLOCK_EN */
286 l = FLD_MOD(l, 1, 20, 20); /* HSDIVBYPASS */
287 if (hw->has_refsel)
288 l = FLD_MOD(l, 3, 22, 21); /* REFSEL = sysclk */
289 l = FLD_MOD(l, 0, 23, 23); /* M6_CLOCK_EN */
290 l = FLD_MOD(l, 0, 25, 25); /* M7_CLOCK_EN */
291 writel_relaxed(l, base + PLL_CONFIGURATION2);
292
293 writel_relaxed(1, base + PLL_GO); /* PLL_GO */
294
295 if (wait_for_bit_change(base + PLL_GO, 0, 0) != 0) {
296 DSSERR("DSS DPLL GO bit not going down.\n");
297 r = -EIO;
298 goto err;
299 }
300
301 if (wait_for_bit_change(base + PLL_STATUS, 1, 1) != 1) {
302 DSSERR("cannot lock DSS DPLL\n");
303 r = -EIO;
304 goto err;
305 }
306
307 l = readl_relaxed(base + PLL_CONFIGURATION2);
308 l = FLD_MOD(l, 1, 14, 14); /* PHY_CLKINEN */
309 l = FLD_MOD(l, cinfo->mX[0] ? 1 : 0, 16, 16); /* M4_CLOCK_EN */
310 l = FLD_MOD(l, cinfo->mX[1] ? 1 : 0, 18, 18); /* M5_CLOCK_EN */
311 l = FLD_MOD(l, 0, 20, 20); /* HSDIVBYPASS */
312 l = FLD_MOD(l, cinfo->mX[2] ? 1 : 0, 23, 23); /* M6_CLOCK_EN */
313 l = FLD_MOD(l, cinfo->mX[3] ? 1 : 0, 25, 25); /* M7_CLOCK_EN */
314 writel_relaxed(l, base + PLL_CONFIGURATION2);
315
316 r = dss_wait_hsdiv_ack(pll,
317 (cinfo->mX[0] ? BIT(7) : 0) |
318 (cinfo->mX[1] ? BIT(8) : 0) |
319 (cinfo->mX[2] ? BIT(10) : 0) |
320 (cinfo->mX[3] ? BIT(11) : 0));
321 if (r) {
322 DSSERR("failed to enable HSDIV clocks\n");
323 goto err;
324 }
325
326err:
327 return r;
328}
329
330int dss_pll_write_config_type_b(struct dss_pll *pll,
331 const struct dss_pll_clock_info *cinfo)
332{
333 const struct dss_pll_hw *hw = pll->hw;
334 void __iomem *base = pll->base;
335 u32 l;
336
337 l = 0;
338 l = FLD_MOD(l, cinfo->m, 20, 9); /* PLL_REGM */
339 l = FLD_MOD(l, cinfo->n - 1, 8, 1); /* PLL_REGN */
340 writel_relaxed(l, base + PLL_CONFIGURATION1);
341
342 l = readl_relaxed(base + PLL_CONFIGURATION2);
343 l = FLD_MOD(l, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */
344 l = FLD_MOD(l, 0x1, 13, 13); /* PLL_REFEN */
345 l = FLD_MOD(l, 0x0, 14, 14); /* PHY_CLKINEN */
346 if (hw->has_refsel)
347 l = FLD_MOD(l, 0x3, 22, 21); /* REFSEL = SYSCLK */
348
349 /* PLL_SELFREQDCO */
350 if (cinfo->clkdco > hw->clkdco_low)
351 l = FLD_MOD(l, 0x4, 3, 1);
352 else
353 l = FLD_MOD(l, 0x2, 3, 1);
354 writel_relaxed(l, base + PLL_CONFIGURATION2);
355
356 l = readl_relaxed(base + PLL_CONFIGURATION3);
357 l = FLD_MOD(l, cinfo->sd, 17, 10); /* PLL_REGSD */
358 writel_relaxed(l, base + PLL_CONFIGURATION3);
359
360 l = readl_relaxed(base + PLL_CONFIGURATION4);
361 l = FLD_MOD(l, cinfo->mX[0], 24, 18); /* PLL_REGM2 */
362 l = FLD_MOD(l, cinfo->mf, 17, 0); /* PLL_REGM_F */
363 writel_relaxed(l, base + PLL_CONFIGURATION4);
364
365 writel_relaxed(1, base + PLL_GO); /* PLL_GO */
366
367 if (wait_for_bit_change(base + PLL_GO, 0, 0) != 0) {
368 DSSERR("DSS DPLL GO bit not going down.\n");
369 return -EIO;
370 }
371
372 if (wait_for_bit_change(base + PLL_STATUS, 1, 1) != 1) {
373 DSSERR("cannot lock DSS DPLL\n");
374 return -ETIMEDOUT;
375 }
376
377 return 0;
378}