blob: dc57100cc43dc407a145b42f735ebba29d70a673 [file] [log] [blame]
Tomi Valkeinen559d6702009-11-03 11:23:50 +02001/*
2 * linux/drivers/video/omap2/dss/dss.c
3 *
4 * Copyright (C) 2009 Nokia Corporation
5 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
6 *
7 * Some code and ideas taken from drivers/video/omap/ driver
8 * by Imre Deak.
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License version 2 as published by
12 * the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * more details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23#define DSS_SUBSYS_NAME "DSS"
24
25#include <linux/kernel.h>
26#include <linux/io.h>
27#include <linux/err.h>
28#include <linux/delay.h>
Tomi Valkeinen559d6702009-11-03 11:23:50 +020029#include <linux/seq_file.h>
30#include <linux/clk.h>
31
32#include <plat/display.h>
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +000033#include <plat/clock.h>
Tomi Valkeinen559d6702009-11-03 11:23:50 +020034#include "dss.h"
35
Tomi Valkeinen559d6702009-11-03 11:23:50 +020036#define DSS_SZ_REGS SZ_512
37
38struct dss_reg {
39 u16 idx;
40};
41
42#define DSS_REG(idx) ((const struct dss_reg) { idx })
43
44#define DSS_REVISION DSS_REG(0x0000)
45#define DSS_SYSCONFIG DSS_REG(0x0010)
46#define DSS_SYSSTATUS DSS_REG(0x0014)
47#define DSS_IRQSTATUS DSS_REG(0x0018)
48#define DSS_CONTROL DSS_REG(0x0040)
49#define DSS_SDI_CONTROL DSS_REG(0x0044)
50#define DSS_PLL_CONTROL DSS_REG(0x0048)
51#define DSS_SDI_STATUS DSS_REG(0x005C)
52
53#define REG_GET(idx, start, end) \
54 FLD_GET(dss_read_reg(idx), start, end)
55
56#define REG_FLD_MOD(idx, val, start, end) \
57 dss_write_reg(idx, FLD_MOD(dss_read_reg(idx), val, start, end))
58
59static struct {
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +000060 struct platform_device *pdev;
Tomi Valkeinen559d6702009-11-03 11:23:50 +020061 void __iomem *base;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +000062 int ctx_id;
Tomi Valkeinen559d6702009-11-03 11:23:50 +020063
64 struct clk *dpll4_m4_ck;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +000065 struct clk *dss_ick;
Archit Tanejac7642f62011-01-31 16:27:45 +000066 struct clk *dss_fck;
67 struct clk *dss_sys_clk;
68 struct clk *dss_tv_fck;
69 struct clk *dss_video_fck;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +000070 unsigned num_clks_enabled;
Tomi Valkeinen559d6702009-11-03 11:23:50 +020071
72 unsigned long cache_req_pck;
73 unsigned long cache_prate;
74 struct dss_clock_info cache_dss_cinfo;
75 struct dispc_clock_info cache_dispc_cinfo;
76
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +020077 enum dss_clk_source dsi_clk_source;
78 enum dss_clk_source dispc_clk_source;
79
Tomi Valkeinen559d6702009-11-03 11:23:50 +020080 u32 ctx[DSS_SZ_REGS / sizeof(u32)];
81} dss;
82
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +000083static void dss_clk_enable_all_no_ctx(void);
84static void dss_clk_disable_all_no_ctx(void);
85static void dss_clk_enable_no_ctx(enum dss_clock clks);
86static void dss_clk_disable_no_ctx(enum dss_clock clks);
87
Tomi Valkeinen559d6702009-11-03 11:23:50 +020088static int _omap_dss_wait_reset(void);
89
90static inline void dss_write_reg(const struct dss_reg idx, u32 val)
91{
92 __raw_writel(val, dss.base + idx.idx);
93}
94
95static inline u32 dss_read_reg(const struct dss_reg idx)
96{
97 return __raw_readl(dss.base + idx.idx);
98}
99
100#define SR(reg) \
101 dss.ctx[(DSS_##reg).idx / sizeof(u32)] = dss_read_reg(DSS_##reg)
102#define RR(reg) \
103 dss_write_reg(DSS_##reg, dss.ctx[(DSS_##reg).idx / sizeof(u32)])
104
105void dss_save_context(void)
106{
107 if (cpu_is_omap24xx())
108 return;
109
110 SR(SYSCONFIG);
111 SR(CONTROL);
112
113#ifdef CONFIG_OMAP2_DSS_SDI
114 SR(SDI_CONTROL);
115 SR(PLL_CONTROL);
116#endif
117}
118
119void dss_restore_context(void)
120{
121 if (_omap_dss_wait_reset())
122 DSSERR("DSS not coming out of reset after sleep\n");
123
124 RR(SYSCONFIG);
125 RR(CONTROL);
126
127#ifdef CONFIG_OMAP2_DSS_SDI
128 RR(SDI_CONTROL);
129 RR(PLL_CONTROL);
130#endif
131}
132
133#undef SR
134#undef RR
135
136void dss_sdi_init(u8 datapairs)
137{
138 u32 l;
139
140 BUG_ON(datapairs > 3 || datapairs < 1);
141
142 l = dss_read_reg(DSS_SDI_CONTROL);
143 l = FLD_MOD(l, 0xf, 19, 15); /* SDI_PDIV */
144 l = FLD_MOD(l, datapairs-1, 3, 2); /* SDI_PRSEL */
145 l = FLD_MOD(l, 2, 1, 0); /* SDI_BWSEL */
146 dss_write_reg(DSS_SDI_CONTROL, l);
147
148 l = dss_read_reg(DSS_PLL_CONTROL);
149 l = FLD_MOD(l, 0x7, 25, 22); /* SDI_PLL_FREQSEL */
150 l = FLD_MOD(l, 0xb, 16, 11); /* SDI_PLL_REGN */
151 l = FLD_MOD(l, 0xb4, 10, 1); /* SDI_PLL_REGM */
152 dss_write_reg(DSS_PLL_CONTROL, l);
153}
154
155int dss_sdi_enable(void)
156{
157 unsigned long timeout;
158
159 dispc_pck_free_enable(1);
160
161 /* Reset SDI PLL */
162 REG_FLD_MOD(DSS_PLL_CONTROL, 1, 18, 18); /* SDI_PLL_SYSRESET */
163 udelay(1); /* wait 2x PCLK */
164
165 /* Lock SDI PLL */
166 REG_FLD_MOD(DSS_PLL_CONTROL, 1, 28, 28); /* SDI_PLL_GOBIT */
167
168 /* Waiting for PLL lock request to complete */
169 timeout = jiffies + msecs_to_jiffies(500);
170 while (dss_read_reg(DSS_SDI_STATUS) & (1 << 6)) {
171 if (time_after_eq(jiffies, timeout)) {
172 DSSERR("PLL lock request timed out\n");
173 goto err1;
174 }
175 }
176
177 /* Clearing PLL_GO bit */
178 REG_FLD_MOD(DSS_PLL_CONTROL, 0, 28, 28);
179
180 /* Waiting for PLL to lock */
181 timeout = jiffies + msecs_to_jiffies(500);
182 while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 5))) {
183 if (time_after_eq(jiffies, timeout)) {
184 DSSERR("PLL lock timed out\n");
185 goto err1;
186 }
187 }
188
189 dispc_lcd_enable_signal(1);
190
191 /* Waiting for SDI reset to complete */
192 timeout = jiffies + msecs_to_jiffies(500);
193 while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 2))) {
194 if (time_after_eq(jiffies, timeout)) {
195 DSSERR("SDI reset timed out\n");
196 goto err2;
197 }
198 }
199
200 return 0;
201
202 err2:
203 dispc_lcd_enable_signal(0);
204 err1:
205 /* Reset SDI PLL */
206 REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */
207
208 dispc_pck_free_enable(0);
209
210 return -ETIMEDOUT;
211}
212
213void dss_sdi_disable(void)
214{
215 dispc_lcd_enable_signal(0);
216
217 dispc_pck_free_enable(0);
218
219 /* Reset SDI PLL */
220 REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */
221}
222
223void dss_dump_clocks(struct seq_file *s)
224{
225 unsigned long dpll4_ck_rate;
226 unsigned long dpll4_m4_ck_rate;
227
Archit Taneja6af9cd12011-01-31 16:27:44 +0000228 dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200229
230 dpll4_ck_rate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
231 dpll4_m4_ck_rate = clk_get_rate(dss.dpll4_m4_ck);
232
233 seq_printf(s, "- DSS -\n");
234
235 seq_printf(s, "dpll4_ck %lu\n", dpll4_ck_rate);
236
Kishore Yac01bb72010-04-25 16:27:19 +0530237 if (cpu_is_omap3630())
238 seq_printf(s, "dss1_alwon_fclk = %lu / %lu = %lu\n",
239 dpll4_ck_rate,
240 dpll4_ck_rate / dpll4_m4_ck_rate,
Archit Taneja6af9cd12011-01-31 16:27:44 +0000241 dss_clk_get_rate(DSS_CLK_FCK));
Kishore Yac01bb72010-04-25 16:27:19 +0530242 else
243 seq_printf(s, "dss1_alwon_fclk = %lu / %lu * 2 = %lu\n",
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200244 dpll4_ck_rate,
245 dpll4_ck_rate / dpll4_m4_ck_rate,
Archit Taneja6af9cd12011-01-31 16:27:44 +0000246 dss_clk_get_rate(DSS_CLK_FCK));
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200247
Archit Taneja6af9cd12011-01-31 16:27:44 +0000248 dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200249}
250
251void dss_dump_regs(struct seq_file *s)
252{
253#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r))
254
Archit Taneja6af9cd12011-01-31 16:27:44 +0000255 dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200256
257 DUMPREG(DSS_REVISION);
258 DUMPREG(DSS_SYSCONFIG);
259 DUMPREG(DSS_SYSSTATUS);
260 DUMPREG(DSS_IRQSTATUS);
261 DUMPREG(DSS_CONTROL);
262 DUMPREG(DSS_SDI_CONTROL);
263 DUMPREG(DSS_PLL_CONTROL);
264 DUMPREG(DSS_SDI_STATUS);
265
Archit Taneja6af9cd12011-01-31 16:27:44 +0000266 dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200267#undef DUMPREG
268}
269
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200270void dss_select_dispc_clk_source(enum dss_clk_source clk_src)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200271{
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200272 int b;
273
274 BUG_ON(clk_src != DSS_SRC_DSI1_PLL_FCLK &&
275 clk_src != DSS_SRC_DSS1_ALWON_FCLK);
276
277 b = clk_src == DSS_SRC_DSS1_ALWON_FCLK ? 0 : 1;
278
Tomi Valkeinene406f902010-06-09 15:28:12 +0300279 if (clk_src == DSS_SRC_DSI1_PLL_FCLK)
280 dsi_wait_dsi1_pll_active();
281
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200282 REG_FLD_MOD(DSS_CONTROL, b, 0, 0); /* DISPC_CLK_SWITCH */
283
284 dss.dispc_clk_source = clk_src;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200285}
286
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200287void dss_select_dsi_clk_source(enum dss_clk_source clk_src)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200288{
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200289 int b;
290
291 BUG_ON(clk_src != DSS_SRC_DSI2_PLL_FCLK &&
292 clk_src != DSS_SRC_DSS1_ALWON_FCLK);
293
294 b = clk_src == DSS_SRC_DSS1_ALWON_FCLK ? 0 : 1;
295
Tomi Valkeinene406f902010-06-09 15:28:12 +0300296 if (clk_src == DSS_SRC_DSI2_PLL_FCLK)
297 dsi_wait_dsi2_pll_active();
298
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200299 REG_FLD_MOD(DSS_CONTROL, b, 1, 1); /* DSI_CLK_SWITCH */
300
301 dss.dsi_clk_source = clk_src;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200302}
303
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200304enum dss_clk_source dss_get_dispc_clk_source(void)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200305{
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200306 return dss.dispc_clk_source;
307}
308
309enum dss_clk_source dss_get_dsi_clk_source(void)
310{
311 return dss.dsi_clk_source;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200312}
313
314/* calculate clock rates using dividers in cinfo */
315int dss_calc_clock_rates(struct dss_clock_info *cinfo)
316{
317 unsigned long prate;
318
Kishore Yac01bb72010-04-25 16:27:19 +0530319 if (cinfo->fck_div > (cpu_is_omap3630() ? 32 : 16) ||
320 cinfo->fck_div == 0)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200321 return -EINVAL;
322
323 prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
324
325 cinfo->fck = prate / cinfo->fck_div;
326
327 return 0;
328}
329
330int dss_set_clock_div(struct dss_clock_info *cinfo)
331{
332 unsigned long prate;
333 int r;
334
335 if (cpu_is_omap34xx()) {
336 prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
337 DSSDBG("dpll4_m4 = %ld\n", prate);
338
339 r = clk_set_rate(dss.dpll4_m4_ck, prate / cinfo->fck_div);
340 if (r)
341 return r;
342 }
343
344 DSSDBG("fck = %ld (%d)\n", cinfo->fck, cinfo->fck_div);
345
346 return 0;
347}
348
349int dss_get_clock_div(struct dss_clock_info *cinfo)
350{
Archit Taneja6af9cd12011-01-31 16:27:44 +0000351 cinfo->fck = dss_clk_get_rate(DSS_CLK_FCK);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200352
353 if (cpu_is_omap34xx()) {
354 unsigned long prate;
355 prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
Kishore Yac01bb72010-04-25 16:27:19 +0530356 if (cpu_is_omap3630())
357 cinfo->fck_div = prate / (cinfo->fck);
358 else
359 cinfo->fck_div = prate / (cinfo->fck / 2);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200360 } else {
361 cinfo->fck_div = 0;
362 }
363
364 return 0;
365}
366
367unsigned long dss_get_dpll4_rate(void)
368{
369 if (cpu_is_omap34xx())
370 return clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
371 else
372 return 0;
373}
374
375int dss_calc_clock_div(bool is_tft, unsigned long req_pck,
376 struct dss_clock_info *dss_cinfo,
377 struct dispc_clock_info *dispc_cinfo)
378{
379 unsigned long prate;
380 struct dss_clock_info best_dss;
381 struct dispc_clock_info best_dispc;
382
383 unsigned long fck;
384
385 u16 fck_div;
386
387 int match = 0;
388 int min_fck_per_pck;
389
390 prate = dss_get_dpll4_rate();
391
Archit Taneja6af9cd12011-01-31 16:27:44 +0000392 fck = dss_clk_get_rate(DSS_CLK_FCK);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200393 if (req_pck == dss.cache_req_pck &&
394 ((cpu_is_omap34xx() && prate == dss.cache_prate) ||
395 dss.cache_dss_cinfo.fck == fck)) {
396 DSSDBG("dispc clock info found from cache.\n");
397 *dss_cinfo = dss.cache_dss_cinfo;
398 *dispc_cinfo = dss.cache_dispc_cinfo;
399 return 0;
400 }
401
402 min_fck_per_pck = CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK;
403
404 if (min_fck_per_pck &&
405 req_pck * min_fck_per_pck > DISPC_MAX_FCK) {
406 DSSERR("Requested pixel clock not possible with the current "
407 "OMAP2_DSS_MIN_FCK_PER_PCK setting. Turning "
408 "the constraint off.\n");
409 min_fck_per_pck = 0;
410 }
411
412retry:
413 memset(&best_dss, 0, sizeof(best_dss));
414 memset(&best_dispc, 0, sizeof(best_dispc));
415
416 if (cpu_is_omap24xx()) {
417 struct dispc_clock_info cur_dispc;
418 /* XXX can we change the clock on omap2? */
Archit Taneja6af9cd12011-01-31 16:27:44 +0000419 fck = dss_clk_get_rate(DSS_CLK_FCK);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200420 fck_div = 1;
421
422 dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc);
423 match = 1;
424
425 best_dss.fck = fck;
426 best_dss.fck_div = fck_div;
427
428 best_dispc = cur_dispc;
429
430 goto found;
431 } else if (cpu_is_omap34xx()) {
Kishore Yac01bb72010-04-25 16:27:19 +0530432 for (fck_div = (cpu_is_omap3630() ? 32 : 16);
433 fck_div > 0; --fck_div) {
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200434 struct dispc_clock_info cur_dispc;
435
Kishore Yac01bb72010-04-25 16:27:19 +0530436 if (cpu_is_omap3630())
437 fck = prate / fck_div;
438 else
439 fck = prate / fck_div * 2;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200440
441 if (fck > DISPC_MAX_FCK)
442 continue;
443
444 if (min_fck_per_pck &&
445 fck < req_pck * min_fck_per_pck)
446 continue;
447
448 match = 1;
449
450 dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc);
451
452 if (abs(cur_dispc.pck - req_pck) <
453 abs(best_dispc.pck - req_pck)) {
454
455 best_dss.fck = fck;
456 best_dss.fck_div = fck_div;
457
458 best_dispc = cur_dispc;
459
460 if (cur_dispc.pck == req_pck)
461 goto found;
462 }
463 }
464 } else {
465 BUG();
466 }
467
468found:
469 if (!match) {
470 if (min_fck_per_pck) {
471 DSSERR("Could not find suitable clock settings.\n"
472 "Turning FCK/PCK constraint off and"
473 "trying again.\n");
474 min_fck_per_pck = 0;
475 goto retry;
476 }
477
478 DSSERR("Could not find suitable clock settings.\n");
479
480 return -EINVAL;
481 }
482
483 if (dss_cinfo)
484 *dss_cinfo = best_dss;
485 if (dispc_cinfo)
486 *dispc_cinfo = best_dispc;
487
488 dss.cache_req_pck = req_pck;
489 dss.cache_prate = prate;
490 dss.cache_dss_cinfo = best_dss;
491 dss.cache_dispc_cinfo = best_dispc;
492
493 return 0;
494}
495
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200496static int _omap_dss_wait_reset(void)
497{
Tomi Valkeinen24be78b2010-01-07 14:19:48 +0200498 int t = 0;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200499
500 while (REG_GET(DSS_SYSSTATUS, 0, 0) == 0) {
Tomi Valkeinen24be78b2010-01-07 14:19:48 +0200501 if (++t > 1000) {
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200502 DSSERR("soft reset failed\n");
503 return -ENODEV;
504 }
Tomi Valkeinen24be78b2010-01-07 14:19:48 +0200505 udelay(1);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200506 }
507
508 return 0;
509}
510
511static int _omap_dss_reset(void)
512{
513 /* Soft reset */
514 REG_FLD_MOD(DSS_SYSCONFIG, 1, 1, 1);
515 return _omap_dss_wait_reset();
516}
517
518void dss_set_venc_output(enum omap_dss_venc_type type)
519{
520 int l = 0;
521
522 if (type == OMAP_DSS_VENC_TYPE_COMPOSITE)
523 l = 0;
524 else if (type == OMAP_DSS_VENC_TYPE_SVIDEO)
525 l = 1;
526 else
527 BUG();
528
529 /* venc out selection. 0 = comp, 1 = svideo */
530 REG_FLD_MOD(DSS_CONTROL, l, 6, 6);
531}
532
533void dss_set_dac_pwrdn_bgz(bool enable)
534{
535 REG_FLD_MOD(DSS_CONTROL, enable, 5, 5); /* DAC Power-Down Control */
536}
537
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +0000538static int dss_init(bool skip_init)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200539{
540 int r;
541 u32 rev;
Senthilvadivu Guruswamyea9da362011-01-24 06:22:04 +0000542 struct resource *dss_mem;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200543
Senthilvadivu Guruswamyea9da362011-01-24 06:22:04 +0000544 dss_mem = platform_get_resource(dss.pdev, IORESOURCE_MEM, 0);
545 if (!dss_mem) {
546 DSSERR("can't get IORESOURCE_MEM DSS\n");
547 r = -EINVAL;
548 goto fail0;
549 }
550 dss.base = ioremap(dss_mem->start, resource_size(dss_mem));
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200551 if (!dss.base) {
552 DSSERR("can't ioremap DSS\n");
553 r = -ENOMEM;
554 goto fail0;
555 }
556
557 if (!skip_init) {
558 /* disable LCD and DIGIT output. This seems to fix the synclost
559 * problem that we get, if the bootloader starts the DSS and
560 * the kernel resets it */
561 omap_writel(omap_readl(0x48050440) & ~0x3, 0x48050440);
562
563 /* We need to wait here a bit, otherwise we sometimes start to
564 * get synclost errors, and after that only power cycle will
565 * restore DSS functionality. I have no idea why this happens.
566 * And we have to wait _before_ resetting the DSS, but after
567 * enabling clocks.
568 */
569 msleep(50);
570
571 _omap_dss_reset();
572 }
573
574 /* autoidle */
575 REG_FLD_MOD(DSS_SYSCONFIG, 1, 0, 0);
576
577 /* Select DPLL */
578 REG_FLD_MOD(DSS_CONTROL, 0, 0, 0);
579
580#ifdef CONFIG_OMAP2_DSS_VENC
581 REG_FLD_MOD(DSS_CONTROL, 1, 4, 4); /* venc dac demen */
582 REG_FLD_MOD(DSS_CONTROL, 1, 3, 3); /* venc clock 4x enable */
583 REG_FLD_MOD(DSS_CONTROL, 0, 2, 2); /* venc clock mode = normal */
584#endif
585
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200586 if (cpu_is_omap34xx()) {
587 dss.dpll4_m4_ck = clk_get(NULL, "dpll4_m4_ck");
588 if (IS_ERR(dss.dpll4_m4_ck)) {
589 DSSERR("Failed to get dpll4_m4_ck\n");
590 r = PTR_ERR(dss.dpll4_m4_ck);
archit tanejaaffe3602011-02-23 08:41:03 +0000591 goto fail1;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200592 }
593 }
594
Tomi Valkeinence619e12010-03-12 12:46:05 +0200595 dss.dsi_clk_source = DSS_SRC_DSS1_ALWON_FCLK;
596 dss.dispc_clk_source = DSS_SRC_DSS1_ALWON_FCLK;
597
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200598 dss_save_context();
599
600 rev = dss_read_reg(DSS_REVISION);
601 printk(KERN_INFO "OMAP DSS rev %d.%d\n",
602 FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
603
604 return 0;
605
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200606fail1:
607 iounmap(dss.base);
608fail0:
609 return r;
610}
611
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +0000612static void dss_exit(void)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200613{
614 if (cpu_is_omap34xx())
615 clk_put(dss.dpll4_m4_ck);
616
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200617 iounmap(dss.base);
618}
619
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000620/* CONTEXT */
621static int dss_get_ctx_id(void)
622{
623 struct omap_display_platform_data *pdata = dss.pdev->dev.platform_data;
624 int r;
625
626 if (!pdata->board_data->get_last_off_on_transaction_id)
627 return 0;
628 r = pdata->board_data->get_last_off_on_transaction_id(&dss.pdev->dev);
629 if (r < 0) {
630 dev_err(&dss.pdev->dev, "getting transaction ID failed, "
631 "will force context restore\n");
632 r = -1;
633 }
634 return r;
635}
636
637int dss_need_ctx_restore(void)
638{
639 int id = dss_get_ctx_id();
640
641 if (id < 0 || id != dss.ctx_id) {
642 DSSDBG("ctx id %d -> id %d\n",
643 dss.ctx_id, id);
644 dss.ctx_id = id;
645 return 1;
646 } else {
647 return 0;
648 }
649}
650
651static void save_all_ctx(void)
652{
653 DSSDBG("save context\n");
654
Archit Taneja6af9cd12011-01-31 16:27:44 +0000655 dss_clk_enable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK);
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000656
657 dss_save_context();
658 dispc_save_context();
659#ifdef CONFIG_OMAP2_DSS_DSI
660 dsi_save_context();
661#endif
662
Archit Taneja6af9cd12011-01-31 16:27:44 +0000663 dss_clk_disable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK);
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000664}
665
666static void restore_all_ctx(void)
667{
668 DSSDBG("restore context\n");
669
670 dss_clk_enable_all_no_ctx();
671
672 dss_restore_context();
673 dispc_restore_context();
674#ifdef CONFIG_OMAP2_DSS_DSI
675 dsi_restore_context();
676#endif
677
678 dss_clk_disable_all_no_ctx();
679}
680
681static int dss_get_clock(struct clk **clock, const char *clk_name)
682{
683 struct clk *clk;
684
685 clk = clk_get(&dss.pdev->dev, clk_name);
686
687 if (IS_ERR(clk)) {
688 DSSERR("can't get clock %s", clk_name);
689 return PTR_ERR(clk);
690 }
691
692 *clock = clk;
693
694 DSSDBG("clk %s, rate %ld\n", clk_name, clk_get_rate(clk));
695
696 return 0;
697}
698
699static int dss_get_clocks(void)
700{
701 int r;
702
703 dss.dss_ick = NULL;
Archit Tanejac7642f62011-01-31 16:27:45 +0000704 dss.dss_fck = NULL;
705 dss.dss_sys_clk = NULL;
706 dss.dss_tv_fck = NULL;
707 dss.dss_video_fck = NULL;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000708
709 r = dss_get_clock(&dss.dss_ick, "ick");
710 if (r)
711 goto err;
712
Archit Tanejac7642f62011-01-31 16:27:45 +0000713 r = dss_get_clock(&dss.dss_fck, "fck");
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000714 if (r)
715 goto err;
716
Archit Tanejac7642f62011-01-31 16:27:45 +0000717 r = dss_get_clock(&dss.dss_sys_clk, "sys_clk");
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000718 if (r)
719 goto err;
720
Archit Tanejac7642f62011-01-31 16:27:45 +0000721 r = dss_get_clock(&dss.dss_tv_fck, "tv_clk");
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000722 if (r)
723 goto err;
724
Archit Tanejac7642f62011-01-31 16:27:45 +0000725 r = dss_get_clock(&dss.dss_video_fck, "video_clk");
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000726 if (r)
727 goto err;
728
729 return 0;
730
731err:
732 if (dss.dss_ick)
733 clk_put(dss.dss_ick);
Archit Tanejac7642f62011-01-31 16:27:45 +0000734 if (dss.dss_fck)
735 clk_put(dss.dss_fck);
736 if (dss.dss_sys_clk)
737 clk_put(dss.dss_sys_clk);
738 if (dss.dss_tv_fck)
739 clk_put(dss.dss_tv_fck);
740 if (dss.dss_video_fck)
741 clk_put(dss.dss_video_fck);
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000742
743 return r;
744}
745
746static void dss_put_clocks(void)
747{
Archit Tanejac7642f62011-01-31 16:27:45 +0000748 if (dss.dss_video_fck)
749 clk_put(dss.dss_video_fck);
750 clk_put(dss.dss_tv_fck);
751 clk_put(dss.dss_fck);
752 clk_put(dss.dss_sys_clk);
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000753 clk_put(dss.dss_ick);
754}
755
756unsigned long dss_clk_get_rate(enum dss_clock clk)
757{
758 switch (clk) {
759 case DSS_CLK_ICK:
760 return clk_get_rate(dss.dss_ick);
Archit Taneja6af9cd12011-01-31 16:27:44 +0000761 case DSS_CLK_FCK:
Archit Tanejac7642f62011-01-31 16:27:45 +0000762 return clk_get_rate(dss.dss_fck);
Archit Taneja6af9cd12011-01-31 16:27:44 +0000763 case DSS_CLK_SYSCK:
Archit Tanejac7642f62011-01-31 16:27:45 +0000764 return clk_get_rate(dss.dss_sys_clk);
Archit Taneja6af9cd12011-01-31 16:27:44 +0000765 case DSS_CLK_TVFCK:
Archit Tanejac7642f62011-01-31 16:27:45 +0000766 return clk_get_rate(dss.dss_tv_fck);
Archit Taneja6af9cd12011-01-31 16:27:44 +0000767 case DSS_CLK_VIDFCK:
Archit Tanejac7642f62011-01-31 16:27:45 +0000768 return clk_get_rate(dss.dss_video_fck);
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000769 }
770
771 BUG();
772 return 0;
773}
774
775static unsigned count_clk_bits(enum dss_clock clks)
776{
777 unsigned num_clks = 0;
778
779 if (clks & DSS_CLK_ICK)
780 ++num_clks;
Archit Taneja6af9cd12011-01-31 16:27:44 +0000781 if (clks & DSS_CLK_FCK)
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000782 ++num_clks;
Archit Taneja6af9cd12011-01-31 16:27:44 +0000783 if (clks & DSS_CLK_SYSCK)
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000784 ++num_clks;
Archit Taneja6af9cd12011-01-31 16:27:44 +0000785 if (clks & DSS_CLK_TVFCK)
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000786 ++num_clks;
Archit Taneja6af9cd12011-01-31 16:27:44 +0000787 if (clks & DSS_CLK_VIDFCK)
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000788 ++num_clks;
789
790 return num_clks;
791}
792
793static void dss_clk_enable_no_ctx(enum dss_clock clks)
794{
795 unsigned num_clks = count_clk_bits(clks);
796
797 if (clks & DSS_CLK_ICK)
798 clk_enable(dss.dss_ick);
Archit Taneja6af9cd12011-01-31 16:27:44 +0000799 if (clks & DSS_CLK_FCK)
Archit Tanejac7642f62011-01-31 16:27:45 +0000800 clk_enable(dss.dss_fck);
Archit Taneja6af9cd12011-01-31 16:27:44 +0000801 if (clks & DSS_CLK_SYSCK)
Archit Tanejac7642f62011-01-31 16:27:45 +0000802 clk_enable(dss.dss_sys_clk);
Archit Taneja6af9cd12011-01-31 16:27:44 +0000803 if (clks & DSS_CLK_TVFCK)
Archit Tanejac7642f62011-01-31 16:27:45 +0000804 clk_enable(dss.dss_tv_fck);
Archit Taneja6af9cd12011-01-31 16:27:44 +0000805 if (clks & DSS_CLK_VIDFCK)
Archit Tanejac7642f62011-01-31 16:27:45 +0000806 clk_enable(dss.dss_video_fck);
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000807
808 dss.num_clks_enabled += num_clks;
809}
810
811void dss_clk_enable(enum dss_clock clks)
812{
813 bool check_ctx = dss.num_clks_enabled == 0;
814
815 dss_clk_enable_no_ctx(clks);
816
817 if (check_ctx && cpu_is_omap34xx() && dss_need_ctx_restore())
818 restore_all_ctx();
819}
820
821static void dss_clk_disable_no_ctx(enum dss_clock clks)
822{
823 unsigned num_clks = count_clk_bits(clks);
824
825 if (clks & DSS_CLK_ICK)
826 clk_disable(dss.dss_ick);
Archit Taneja6af9cd12011-01-31 16:27:44 +0000827 if (clks & DSS_CLK_FCK)
Archit Tanejac7642f62011-01-31 16:27:45 +0000828 clk_disable(dss.dss_fck);
Archit Taneja6af9cd12011-01-31 16:27:44 +0000829 if (clks & DSS_CLK_SYSCK)
Archit Tanejac7642f62011-01-31 16:27:45 +0000830 clk_disable(dss.dss_sys_clk);
Archit Taneja6af9cd12011-01-31 16:27:44 +0000831 if (clks & DSS_CLK_TVFCK)
Archit Tanejac7642f62011-01-31 16:27:45 +0000832 clk_disable(dss.dss_tv_fck);
Archit Taneja6af9cd12011-01-31 16:27:44 +0000833 if (clks & DSS_CLK_VIDFCK)
Archit Tanejac7642f62011-01-31 16:27:45 +0000834 clk_disable(dss.dss_video_fck);
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000835
836 dss.num_clks_enabled -= num_clks;
837}
838
839void dss_clk_disable(enum dss_clock clks)
840{
841 if (cpu_is_omap34xx()) {
842 unsigned num_clks = count_clk_bits(clks);
843
844 BUG_ON(dss.num_clks_enabled < num_clks);
845
846 if (dss.num_clks_enabled == num_clks)
847 save_all_ctx();
848 }
849
850 dss_clk_disable_no_ctx(clks);
851}
852
853static void dss_clk_enable_all_no_ctx(void)
854{
855 enum dss_clock clks;
856
Archit Taneja6af9cd12011-01-31 16:27:44 +0000857 clks = DSS_CLK_ICK | DSS_CLK_FCK | DSS_CLK_SYSCK | DSS_CLK_TVFCK;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000858 if (cpu_is_omap34xx())
Archit Taneja6af9cd12011-01-31 16:27:44 +0000859 clks |= DSS_CLK_VIDFCK;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000860 dss_clk_enable_no_ctx(clks);
861}
862
863static void dss_clk_disable_all_no_ctx(void)
864{
865 enum dss_clock clks;
866
Archit Taneja6af9cd12011-01-31 16:27:44 +0000867 clks = DSS_CLK_ICK | DSS_CLK_FCK | DSS_CLK_SYSCK | DSS_CLK_TVFCK;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000868 if (cpu_is_omap34xx())
Archit Taneja6af9cd12011-01-31 16:27:44 +0000869 clks |= DSS_CLK_VIDFCK;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000870 dss_clk_disable_no_ctx(clks);
871}
872
873#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
874/* CLOCKS */
875static void core_dump_clocks(struct seq_file *s)
876{
877 int i;
878 struct clk *clocks[5] = {
879 dss.dss_ick,
Archit Tanejac7642f62011-01-31 16:27:45 +0000880 dss.dss_fck,
881 dss.dss_sys_clk,
882 dss.dss_tv_fck,
883 dss.dss_video_fck
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000884 };
885
886 seq_printf(s, "- CORE -\n");
887
888 seq_printf(s, "internal clk count\t\t%u\n", dss.num_clks_enabled);
889
890 for (i = 0; i < 5; i++) {
891 if (!clocks[i])
892 continue;
893 seq_printf(s, "%-15s\t%lu\t%d\n",
894 clocks[i]->name,
895 clk_get_rate(clocks[i]),
896 clocks[i]->usecount);
897 }
898}
899#endif /* defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) */
900
901/* DEBUGFS */
902#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
903void dss_debug_dump_clocks(struct seq_file *s)
904{
905 core_dump_clocks(s);
906 dss_dump_clocks(s);
907 dispc_dump_clocks(s);
908#ifdef CONFIG_OMAP2_DSS_DSI
909 dsi_dump_clocks(s);
910#endif
911}
912#endif
913
914
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +0000915/* DSS HW IP initialisation */
916static int omap_dsshw_probe(struct platform_device *pdev)
917{
918 int r;
919 int skip_init = 0;
920
921 dss.pdev = pdev;
922
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000923 r = dss_get_clocks();
924 if (r)
925 goto err_clocks;
926
927 dss_clk_enable_all_no_ctx();
928
929 dss.ctx_id = dss_get_ctx_id();
930 DSSDBG("initial ctx id %u\n", dss.ctx_id);
931
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +0000932#ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT
933 /* DISPC_CONTROL */
934 if (omap_readl(0x48050440) & 1) /* LCD enabled? */
935 skip_init = 1;
936#endif
937
938 r = dss_init(skip_init);
939 if (r) {
940 DSSERR("Failed to initialize DSS\n");
941 goto err_dss;
942 }
943
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000944 dss_clk_disable_all_no_ctx();
945 return 0;
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +0000946
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000947err_dss:
948 dss_clk_disable_all_no_ctx();
949 dss_put_clocks();
950err_clocks:
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +0000951 return r;
952}
953
954static int omap_dsshw_remove(struct platform_device *pdev)
955{
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000956
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +0000957 dss_exit();
958
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000959 /*
960 * As part of hwmod changes, DSS is not the only controller of dss
961 * clocks; hwmod framework itself will also enable clocks during hwmod
962 * init for dss, and autoidle is set in h/w for DSS. Hence, there's no
963 * need to disable clocks if their usecounts > 1.
964 */
965 WARN_ON(dss.num_clks_enabled > 0);
966
967 dss_put_clocks();
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +0000968 return 0;
969}
970
971static struct platform_driver omap_dsshw_driver = {
972 .probe = omap_dsshw_probe,
973 .remove = omap_dsshw_remove,
974 .driver = {
975 .name = "omapdss_dss",
976 .owner = THIS_MODULE,
977 },
978};
979
980int dss_init_platform_driver(void)
981{
982 return platform_driver_register(&omap_dsshw_driver);
983}
984
985void dss_uninit_platform_driver(void)
986{
987 return platform_driver_unregister(&omap_dsshw_driver);
988}