blob: ee9ce4af58b53ea2e4f3ecea79a2bee415d7938a [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>
29#include <linux/interrupt.h>
30#include <linux/seq_file.h>
31#include <linux/clk.h>
32
33#include <plat/display.h>
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +000034#include <plat/clock.h>
Tomi Valkeinen559d6702009-11-03 11:23:50 +020035#include "dss.h"
36
Tomi Valkeinen559d6702009-11-03 11:23:50 +020037#define DSS_SZ_REGS SZ_512
38
39struct dss_reg {
40 u16 idx;
41};
42
43#define DSS_REG(idx) ((const struct dss_reg) { idx })
44
45#define DSS_REVISION DSS_REG(0x0000)
46#define DSS_SYSCONFIG DSS_REG(0x0010)
47#define DSS_SYSSTATUS DSS_REG(0x0014)
48#define DSS_IRQSTATUS DSS_REG(0x0018)
49#define DSS_CONTROL DSS_REG(0x0040)
50#define DSS_SDI_CONTROL DSS_REG(0x0044)
51#define DSS_PLL_CONTROL DSS_REG(0x0048)
52#define DSS_SDI_STATUS DSS_REG(0x005C)
53
54#define REG_GET(idx, start, end) \
55 FLD_GET(dss_read_reg(idx), start, end)
56
57#define REG_FLD_MOD(idx, val, start, end) \
58 dss_write_reg(idx, FLD_MOD(dss_read_reg(idx), val, start, end))
59
60static struct {
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +000061 struct platform_device *pdev;
Tomi Valkeinen559d6702009-11-03 11:23:50 +020062 void __iomem *base;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +000063 int ctx_id;
Tomi Valkeinen559d6702009-11-03 11:23:50 +020064
65 struct clk *dpll4_m4_ck;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +000066 struct clk *dss_ick;
67 struct clk *dss1_fck;
68 struct clk *dss2_fck;
69 struct clk *dss_54m_fck;
70 struct clk *dss_96m_fck;
71 unsigned num_clks_enabled;
Tomi Valkeinen559d6702009-11-03 11:23:50 +020072
73 unsigned long cache_req_pck;
74 unsigned long cache_prate;
75 struct dss_clock_info cache_dss_cinfo;
76 struct dispc_clock_info cache_dispc_cinfo;
77
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +020078 enum dss_clk_source dsi_clk_source;
79 enum dss_clk_source dispc_clk_source;
80
Tomi Valkeinen559d6702009-11-03 11:23:50 +020081 u32 ctx[DSS_SZ_REGS / sizeof(u32)];
82} dss;
83
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +000084static void dss_clk_enable_all_no_ctx(void);
85static void dss_clk_disable_all_no_ctx(void);
86static void dss_clk_enable_no_ctx(enum dss_clock clks);
87static void dss_clk_disable_no_ctx(enum dss_clock clks);
88
Tomi Valkeinen559d6702009-11-03 11:23:50 +020089static int _omap_dss_wait_reset(void);
90
91static inline void dss_write_reg(const struct dss_reg idx, u32 val)
92{
93 __raw_writel(val, dss.base + idx.idx);
94}
95
96static inline u32 dss_read_reg(const struct dss_reg idx)
97{
98 return __raw_readl(dss.base + idx.idx);
99}
100
101#define SR(reg) \
102 dss.ctx[(DSS_##reg).idx / sizeof(u32)] = dss_read_reg(DSS_##reg)
103#define RR(reg) \
104 dss_write_reg(DSS_##reg, dss.ctx[(DSS_##reg).idx / sizeof(u32)])
105
106void dss_save_context(void)
107{
108 if (cpu_is_omap24xx())
109 return;
110
111 SR(SYSCONFIG);
112 SR(CONTROL);
113
114#ifdef CONFIG_OMAP2_DSS_SDI
115 SR(SDI_CONTROL);
116 SR(PLL_CONTROL);
117#endif
118}
119
120void dss_restore_context(void)
121{
122 if (_omap_dss_wait_reset())
123 DSSERR("DSS not coming out of reset after sleep\n");
124
125 RR(SYSCONFIG);
126 RR(CONTROL);
127
128#ifdef CONFIG_OMAP2_DSS_SDI
129 RR(SDI_CONTROL);
130 RR(PLL_CONTROL);
131#endif
132}
133
134#undef SR
135#undef RR
136
137void dss_sdi_init(u8 datapairs)
138{
139 u32 l;
140
141 BUG_ON(datapairs > 3 || datapairs < 1);
142
143 l = dss_read_reg(DSS_SDI_CONTROL);
144 l = FLD_MOD(l, 0xf, 19, 15); /* SDI_PDIV */
145 l = FLD_MOD(l, datapairs-1, 3, 2); /* SDI_PRSEL */
146 l = FLD_MOD(l, 2, 1, 0); /* SDI_BWSEL */
147 dss_write_reg(DSS_SDI_CONTROL, l);
148
149 l = dss_read_reg(DSS_PLL_CONTROL);
150 l = FLD_MOD(l, 0x7, 25, 22); /* SDI_PLL_FREQSEL */
151 l = FLD_MOD(l, 0xb, 16, 11); /* SDI_PLL_REGN */
152 l = FLD_MOD(l, 0xb4, 10, 1); /* SDI_PLL_REGM */
153 dss_write_reg(DSS_PLL_CONTROL, l);
154}
155
156int dss_sdi_enable(void)
157{
158 unsigned long timeout;
159
160 dispc_pck_free_enable(1);
161
162 /* Reset SDI PLL */
163 REG_FLD_MOD(DSS_PLL_CONTROL, 1, 18, 18); /* SDI_PLL_SYSRESET */
164 udelay(1); /* wait 2x PCLK */
165
166 /* Lock SDI PLL */
167 REG_FLD_MOD(DSS_PLL_CONTROL, 1, 28, 28); /* SDI_PLL_GOBIT */
168
169 /* Waiting for PLL lock request to complete */
170 timeout = jiffies + msecs_to_jiffies(500);
171 while (dss_read_reg(DSS_SDI_STATUS) & (1 << 6)) {
172 if (time_after_eq(jiffies, timeout)) {
173 DSSERR("PLL lock request timed out\n");
174 goto err1;
175 }
176 }
177
178 /* Clearing PLL_GO bit */
179 REG_FLD_MOD(DSS_PLL_CONTROL, 0, 28, 28);
180
181 /* Waiting for PLL to lock */
182 timeout = jiffies + msecs_to_jiffies(500);
183 while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 5))) {
184 if (time_after_eq(jiffies, timeout)) {
185 DSSERR("PLL lock timed out\n");
186 goto err1;
187 }
188 }
189
190 dispc_lcd_enable_signal(1);
191
192 /* Waiting for SDI reset to complete */
193 timeout = jiffies + msecs_to_jiffies(500);
194 while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 2))) {
195 if (time_after_eq(jiffies, timeout)) {
196 DSSERR("SDI reset timed out\n");
197 goto err2;
198 }
199 }
200
201 return 0;
202
203 err2:
204 dispc_lcd_enable_signal(0);
205 err1:
206 /* Reset SDI PLL */
207 REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */
208
209 dispc_pck_free_enable(0);
210
211 return -ETIMEDOUT;
212}
213
214void dss_sdi_disable(void)
215{
216 dispc_lcd_enable_signal(0);
217
218 dispc_pck_free_enable(0);
219
220 /* Reset SDI PLL */
221 REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */
222}
223
224void dss_dump_clocks(struct seq_file *s)
225{
226 unsigned long dpll4_ck_rate;
227 unsigned long dpll4_m4_ck_rate;
228
229 dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
230
231 dpll4_ck_rate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
232 dpll4_m4_ck_rate = clk_get_rate(dss.dpll4_m4_ck);
233
234 seq_printf(s, "- DSS -\n");
235
236 seq_printf(s, "dpll4_ck %lu\n", dpll4_ck_rate);
237
Kishore Yac01bb72010-04-25 16:27:19 +0530238 if (cpu_is_omap3630())
239 seq_printf(s, "dss1_alwon_fclk = %lu / %lu = %lu\n",
240 dpll4_ck_rate,
241 dpll4_ck_rate / dpll4_m4_ck_rate,
242 dss_clk_get_rate(DSS_CLK_FCK1));
243 else
244 seq_printf(s, "dss1_alwon_fclk = %lu / %lu * 2 = %lu\n",
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200245 dpll4_ck_rate,
246 dpll4_ck_rate / dpll4_m4_ck_rate,
247 dss_clk_get_rate(DSS_CLK_FCK1));
248
249 dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
250}
251
252void dss_dump_regs(struct seq_file *s)
253{
254#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r))
255
256 dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
257
258 DUMPREG(DSS_REVISION);
259 DUMPREG(DSS_SYSCONFIG);
260 DUMPREG(DSS_SYSSTATUS);
261 DUMPREG(DSS_IRQSTATUS);
262 DUMPREG(DSS_CONTROL);
263 DUMPREG(DSS_SDI_CONTROL);
264 DUMPREG(DSS_PLL_CONTROL);
265 DUMPREG(DSS_SDI_STATUS);
266
267 dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
268#undef DUMPREG
269}
270
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200271void dss_select_dispc_clk_source(enum dss_clk_source clk_src)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200272{
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200273 int b;
274
275 BUG_ON(clk_src != DSS_SRC_DSI1_PLL_FCLK &&
276 clk_src != DSS_SRC_DSS1_ALWON_FCLK);
277
278 b = clk_src == DSS_SRC_DSS1_ALWON_FCLK ? 0 : 1;
279
Tomi Valkeinene406f902010-06-09 15:28:12 +0300280 if (clk_src == DSS_SRC_DSI1_PLL_FCLK)
281 dsi_wait_dsi1_pll_active();
282
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200283 REG_FLD_MOD(DSS_CONTROL, b, 0, 0); /* DISPC_CLK_SWITCH */
284
285 dss.dispc_clk_source = clk_src;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200286}
287
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200288void dss_select_dsi_clk_source(enum dss_clk_source clk_src)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200289{
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200290 int b;
291
292 BUG_ON(clk_src != DSS_SRC_DSI2_PLL_FCLK &&
293 clk_src != DSS_SRC_DSS1_ALWON_FCLK);
294
295 b = clk_src == DSS_SRC_DSS1_ALWON_FCLK ? 0 : 1;
296
Tomi Valkeinene406f902010-06-09 15:28:12 +0300297 if (clk_src == DSS_SRC_DSI2_PLL_FCLK)
298 dsi_wait_dsi2_pll_active();
299
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200300 REG_FLD_MOD(DSS_CONTROL, b, 1, 1); /* DSI_CLK_SWITCH */
301
302 dss.dsi_clk_source = clk_src;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200303}
304
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200305enum dss_clk_source dss_get_dispc_clk_source(void)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200306{
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200307 return dss.dispc_clk_source;
308}
309
310enum dss_clk_source dss_get_dsi_clk_source(void)
311{
312 return dss.dsi_clk_source;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200313}
314
315/* calculate clock rates using dividers in cinfo */
316int dss_calc_clock_rates(struct dss_clock_info *cinfo)
317{
318 unsigned long prate;
319
Kishore Yac01bb72010-04-25 16:27:19 +0530320 if (cinfo->fck_div > (cpu_is_omap3630() ? 32 : 16) ||
321 cinfo->fck_div == 0)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200322 return -EINVAL;
323
324 prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
325
326 cinfo->fck = prate / cinfo->fck_div;
327
328 return 0;
329}
330
331int dss_set_clock_div(struct dss_clock_info *cinfo)
332{
333 unsigned long prate;
334 int r;
335
336 if (cpu_is_omap34xx()) {
337 prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
338 DSSDBG("dpll4_m4 = %ld\n", prate);
339
340 r = clk_set_rate(dss.dpll4_m4_ck, prate / cinfo->fck_div);
341 if (r)
342 return r;
343 }
344
345 DSSDBG("fck = %ld (%d)\n", cinfo->fck, cinfo->fck_div);
346
347 return 0;
348}
349
350int dss_get_clock_div(struct dss_clock_info *cinfo)
351{
352 cinfo->fck = dss_clk_get_rate(DSS_CLK_FCK1);
353
354 if (cpu_is_omap34xx()) {
355 unsigned long prate;
356 prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
Kishore Yac01bb72010-04-25 16:27:19 +0530357 if (cpu_is_omap3630())
358 cinfo->fck_div = prate / (cinfo->fck);
359 else
360 cinfo->fck_div = prate / (cinfo->fck / 2);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200361 } else {
362 cinfo->fck_div = 0;
363 }
364
365 return 0;
366}
367
368unsigned long dss_get_dpll4_rate(void)
369{
370 if (cpu_is_omap34xx())
371 return clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
372 else
373 return 0;
374}
375
376int dss_calc_clock_div(bool is_tft, unsigned long req_pck,
377 struct dss_clock_info *dss_cinfo,
378 struct dispc_clock_info *dispc_cinfo)
379{
380 unsigned long prate;
381 struct dss_clock_info best_dss;
382 struct dispc_clock_info best_dispc;
383
384 unsigned long fck;
385
386 u16 fck_div;
387
388 int match = 0;
389 int min_fck_per_pck;
390
391 prate = dss_get_dpll4_rate();
392
393 fck = dss_clk_get_rate(DSS_CLK_FCK1);
394 if (req_pck == dss.cache_req_pck &&
395 ((cpu_is_omap34xx() && prate == dss.cache_prate) ||
396 dss.cache_dss_cinfo.fck == fck)) {
397 DSSDBG("dispc clock info found from cache.\n");
398 *dss_cinfo = dss.cache_dss_cinfo;
399 *dispc_cinfo = dss.cache_dispc_cinfo;
400 return 0;
401 }
402
403 min_fck_per_pck = CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK;
404
405 if (min_fck_per_pck &&
406 req_pck * min_fck_per_pck > DISPC_MAX_FCK) {
407 DSSERR("Requested pixel clock not possible with the current "
408 "OMAP2_DSS_MIN_FCK_PER_PCK setting. Turning "
409 "the constraint off.\n");
410 min_fck_per_pck = 0;
411 }
412
413retry:
414 memset(&best_dss, 0, sizeof(best_dss));
415 memset(&best_dispc, 0, sizeof(best_dispc));
416
417 if (cpu_is_omap24xx()) {
418 struct dispc_clock_info cur_dispc;
419 /* XXX can we change the clock on omap2? */
420 fck = dss_clk_get_rate(DSS_CLK_FCK1);
421 fck_div = 1;
422
423 dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc);
424 match = 1;
425
426 best_dss.fck = fck;
427 best_dss.fck_div = fck_div;
428
429 best_dispc = cur_dispc;
430
431 goto found;
432 } else if (cpu_is_omap34xx()) {
Kishore Yac01bb72010-04-25 16:27:19 +0530433 for (fck_div = (cpu_is_omap3630() ? 32 : 16);
434 fck_div > 0; --fck_div) {
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200435 struct dispc_clock_info cur_dispc;
436
Kishore Yac01bb72010-04-25 16:27:19 +0530437 if (cpu_is_omap3630())
438 fck = prate / fck_div;
439 else
440 fck = prate / fck_div * 2;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200441
442 if (fck > DISPC_MAX_FCK)
443 continue;
444
445 if (min_fck_per_pck &&
446 fck < req_pck * min_fck_per_pck)
447 continue;
448
449 match = 1;
450
451 dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc);
452
453 if (abs(cur_dispc.pck - req_pck) <
454 abs(best_dispc.pck - req_pck)) {
455
456 best_dss.fck = fck;
457 best_dss.fck_div = fck_div;
458
459 best_dispc = cur_dispc;
460
461 if (cur_dispc.pck == req_pck)
462 goto found;
463 }
464 }
465 } else {
466 BUG();
467 }
468
469found:
470 if (!match) {
471 if (min_fck_per_pck) {
472 DSSERR("Could not find suitable clock settings.\n"
473 "Turning FCK/PCK constraint off and"
474 "trying again.\n");
475 min_fck_per_pck = 0;
476 goto retry;
477 }
478
479 DSSERR("Could not find suitable clock settings.\n");
480
481 return -EINVAL;
482 }
483
484 if (dss_cinfo)
485 *dss_cinfo = best_dss;
486 if (dispc_cinfo)
487 *dispc_cinfo = best_dispc;
488
489 dss.cache_req_pck = req_pck;
490 dss.cache_prate = prate;
491 dss.cache_dss_cinfo = best_dss;
492 dss.cache_dispc_cinfo = best_dispc;
493
494 return 0;
495}
496
497
498
499static irqreturn_t dss_irq_handler_omap2(int irq, void *arg)
500{
501 dispc_irq_handler();
502
503 return IRQ_HANDLED;
504}
505
506static irqreturn_t dss_irq_handler_omap3(int irq, void *arg)
507{
508 u32 irqstatus;
509
510 irqstatus = dss_read_reg(DSS_IRQSTATUS);
511
512 if (irqstatus & (1<<0)) /* DISPC_IRQ */
513 dispc_irq_handler();
514#ifdef CONFIG_OMAP2_DSS_DSI
515 if (irqstatus & (1<<1)) /* DSI_IRQ */
516 dsi_irq_handler();
517#endif
518
519 return IRQ_HANDLED;
520}
521
522static int _omap_dss_wait_reset(void)
523{
Tomi Valkeinen24be78b2010-01-07 14:19:48 +0200524 int t = 0;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200525
526 while (REG_GET(DSS_SYSSTATUS, 0, 0) == 0) {
Tomi Valkeinen24be78b2010-01-07 14:19:48 +0200527 if (++t > 1000) {
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200528 DSSERR("soft reset failed\n");
529 return -ENODEV;
530 }
Tomi Valkeinen24be78b2010-01-07 14:19:48 +0200531 udelay(1);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200532 }
533
534 return 0;
535}
536
537static int _omap_dss_reset(void)
538{
539 /* Soft reset */
540 REG_FLD_MOD(DSS_SYSCONFIG, 1, 1, 1);
541 return _omap_dss_wait_reset();
542}
543
544void dss_set_venc_output(enum omap_dss_venc_type type)
545{
546 int l = 0;
547
548 if (type == OMAP_DSS_VENC_TYPE_COMPOSITE)
549 l = 0;
550 else if (type == OMAP_DSS_VENC_TYPE_SVIDEO)
551 l = 1;
552 else
553 BUG();
554
555 /* venc out selection. 0 = comp, 1 = svideo */
556 REG_FLD_MOD(DSS_CONTROL, l, 6, 6);
557}
558
559void dss_set_dac_pwrdn_bgz(bool enable)
560{
561 REG_FLD_MOD(DSS_CONTROL, enable, 5, 5); /* DAC Power-Down Control */
562}
563
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +0000564static int dss_init(bool skip_init)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200565{
566 int r;
567 u32 rev;
Senthilvadivu Guruswamyea9da362011-01-24 06:22:04 +0000568 struct resource *dss_mem;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200569
Senthilvadivu Guruswamyea9da362011-01-24 06:22:04 +0000570 dss_mem = platform_get_resource(dss.pdev, IORESOURCE_MEM, 0);
571 if (!dss_mem) {
572 DSSERR("can't get IORESOURCE_MEM DSS\n");
573 r = -EINVAL;
574 goto fail0;
575 }
576 dss.base = ioremap(dss_mem->start, resource_size(dss_mem));
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200577 if (!dss.base) {
578 DSSERR("can't ioremap DSS\n");
579 r = -ENOMEM;
580 goto fail0;
581 }
582
583 if (!skip_init) {
584 /* disable LCD and DIGIT output. This seems to fix the synclost
585 * problem that we get, if the bootloader starts the DSS and
586 * the kernel resets it */
587 omap_writel(omap_readl(0x48050440) & ~0x3, 0x48050440);
588
589 /* We need to wait here a bit, otherwise we sometimes start to
590 * get synclost errors, and after that only power cycle will
591 * restore DSS functionality. I have no idea why this happens.
592 * And we have to wait _before_ resetting the DSS, but after
593 * enabling clocks.
594 */
595 msleep(50);
596
597 _omap_dss_reset();
598 }
599
600 /* autoidle */
601 REG_FLD_MOD(DSS_SYSCONFIG, 1, 0, 0);
602
603 /* Select DPLL */
604 REG_FLD_MOD(DSS_CONTROL, 0, 0, 0);
605
606#ifdef CONFIG_OMAP2_DSS_VENC
607 REG_FLD_MOD(DSS_CONTROL, 1, 4, 4); /* venc dac demen */
608 REG_FLD_MOD(DSS_CONTROL, 1, 3, 3); /* venc clock 4x enable */
609 REG_FLD_MOD(DSS_CONTROL, 0, 2, 2); /* venc clock mode = normal */
610#endif
611
612 r = request_irq(INT_24XX_DSS_IRQ,
613 cpu_is_omap24xx()
614 ? dss_irq_handler_omap2
615 : dss_irq_handler_omap3,
616 0, "OMAP DSS", NULL);
617
618 if (r < 0) {
619 DSSERR("omap2 dss: request_irq failed\n");
620 goto fail1;
621 }
622
623 if (cpu_is_omap34xx()) {
624 dss.dpll4_m4_ck = clk_get(NULL, "dpll4_m4_ck");
625 if (IS_ERR(dss.dpll4_m4_ck)) {
626 DSSERR("Failed to get dpll4_m4_ck\n");
627 r = PTR_ERR(dss.dpll4_m4_ck);
628 goto fail2;
629 }
630 }
631
Tomi Valkeinence619e12010-03-12 12:46:05 +0200632 dss.dsi_clk_source = DSS_SRC_DSS1_ALWON_FCLK;
633 dss.dispc_clk_source = DSS_SRC_DSS1_ALWON_FCLK;
634
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200635 dss_save_context();
636
637 rev = dss_read_reg(DSS_REVISION);
638 printk(KERN_INFO "OMAP DSS rev %d.%d\n",
639 FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
640
641 return 0;
642
643fail2:
644 free_irq(INT_24XX_DSS_IRQ, NULL);
645fail1:
646 iounmap(dss.base);
647fail0:
648 return r;
649}
650
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +0000651static void dss_exit(void)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200652{
653 if (cpu_is_omap34xx())
654 clk_put(dss.dpll4_m4_ck);
655
656 free_irq(INT_24XX_DSS_IRQ, NULL);
657
658 iounmap(dss.base);
659}
660
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000661/* CONTEXT */
662static int dss_get_ctx_id(void)
663{
664 struct omap_display_platform_data *pdata = dss.pdev->dev.platform_data;
665 int r;
666
667 if (!pdata->board_data->get_last_off_on_transaction_id)
668 return 0;
669 r = pdata->board_data->get_last_off_on_transaction_id(&dss.pdev->dev);
670 if (r < 0) {
671 dev_err(&dss.pdev->dev, "getting transaction ID failed, "
672 "will force context restore\n");
673 r = -1;
674 }
675 return r;
676}
677
678int dss_need_ctx_restore(void)
679{
680 int id = dss_get_ctx_id();
681
682 if (id < 0 || id != dss.ctx_id) {
683 DSSDBG("ctx id %d -> id %d\n",
684 dss.ctx_id, id);
685 dss.ctx_id = id;
686 return 1;
687 } else {
688 return 0;
689 }
690}
691
692static void save_all_ctx(void)
693{
694 DSSDBG("save context\n");
695
696 dss_clk_enable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK1);
697
698 dss_save_context();
699 dispc_save_context();
700#ifdef CONFIG_OMAP2_DSS_DSI
701 dsi_save_context();
702#endif
703
704 dss_clk_disable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK1);
705}
706
707static void restore_all_ctx(void)
708{
709 DSSDBG("restore context\n");
710
711 dss_clk_enable_all_no_ctx();
712
713 dss_restore_context();
714 dispc_restore_context();
715#ifdef CONFIG_OMAP2_DSS_DSI
716 dsi_restore_context();
717#endif
718
719 dss_clk_disable_all_no_ctx();
720}
721
722static int dss_get_clock(struct clk **clock, const char *clk_name)
723{
724 struct clk *clk;
725
726 clk = clk_get(&dss.pdev->dev, clk_name);
727
728 if (IS_ERR(clk)) {
729 DSSERR("can't get clock %s", clk_name);
730 return PTR_ERR(clk);
731 }
732
733 *clock = clk;
734
735 DSSDBG("clk %s, rate %ld\n", clk_name, clk_get_rate(clk));
736
737 return 0;
738}
739
740static int dss_get_clocks(void)
741{
742 int r;
743
744 dss.dss_ick = NULL;
745 dss.dss1_fck = NULL;
746 dss.dss2_fck = NULL;
747 dss.dss_54m_fck = NULL;
748 dss.dss_96m_fck = NULL;
749
750 r = dss_get_clock(&dss.dss_ick, "ick");
751 if (r)
752 goto err;
753
754 r = dss_get_clock(&dss.dss1_fck, "dss1_fck");
755 if (r)
756 goto err;
757
758 r = dss_get_clock(&dss.dss2_fck, "dss2_fck");
759 if (r)
760 goto err;
761
762 r = dss_get_clock(&dss.dss_54m_fck, "tv_fck");
763 if (r)
764 goto err;
765
766 r = dss_get_clock(&dss.dss_96m_fck, "video_fck");
767 if (r)
768 goto err;
769
770 return 0;
771
772err:
773 if (dss.dss_ick)
774 clk_put(dss.dss_ick);
775 if (dss.dss1_fck)
776 clk_put(dss.dss1_fck);
777 if (dss.dss2_fck)
778 clk_put(dss.dss2_fck);
779 if (dss.dss_54m_fck)
780 clk_put(dss.dss_54m_fck);
781 if (dss.dss_96m_fck)
782 clk_put(dss.dss_96m_fck);
783
784 return r;
785}
786
787static void dss_put_clocks(void)
788{
789 if (dss.dss_96m_fck)
790 clk_put(dss.dss_96m_fck);
791 clk_put(dss.dss_54m_fck);
792 clk_put(dss.dss1_fck);
793 clk_put(dss.dss2_fck);
794 clk_put(dss.dss_ick);
795}
796
797unsigned long dss_clk_get_rate(enum dss_clock clk)
798{
799 switch (clk) {
800 case DSS_CLK_ICK:
801 return clk_get_rate(dss.dss_ick);
802 case DSS_CLK_FCK1:
803 return clk_get_rate(dss.dss1_fck);
804 case DSS_CLK_FCK2:
805 return clk_get_rate(dss.dss2_fck);
806 case DSS_CLK_54M:
807 return clk_get_rate(dss.dss_54m_fck);
808 case DSS_CLK_96M:
809 return clk_get_rate(dss.dss_96m_fck);
810 }
811
812 BUG();
813 return 0;
814}
815
816static unsigned count_clk_bits(enum dss_clock clks)
817{
818 unsigned num_clks = 0;
819
820 if (clks & DSS_CLK_ICK)
821 ++num_clks;
822 if (clks & DSS_CLK_FCK1)
823 ++num_clks;
824 if (clks & DSS_CLK_FCK2)
825 ++num_clks;
826 if (clks & DSS_CLK_54M)
827 ++num_clks;
828 if (clks & DSS_CLK_96M)
829 ++num_clks;
830
831 return num_clks;
832}
833
834static void dss_clk_enable_no_ctx(enum dss_clock clks)
835{
836 unsigned num_clks = count_clk_bits(clks);
837
838 if (clks & DSS_CLK_ICK)
839 clk_enable(dss.dss_ick);
840 if (clks & DSS_CLK_FCK1)
841 clk_enable(dss.dss1_fck);
842 if (clks & DSS_CLK_FCK2)
843 clk_enable(dss.dss2_fck);
844 if (clks & DSS_CLK_54M)
845 clk_enable(dss.dss_54m_fck);
846 if (clks & DSS_CLK_96M)
847 clk_enable(dss.dss_96m_fck);
848
849 dss.num_clks_enabled += num_clks;
850}
851
852void dss_clk_enable(enum dss_clock clks)
853{
854 bool check_ctx = dss.num_clks_enabled == 0;
855
856 dss_clk_enable_no_ctx(clks);
857
858 if (check_ctx && cpu_is_omap34xx() && dss_need_ctx_restore())
859 restore_all_ctx();
860}
861
862static void dss_clk_disable_no_ctx(enum dss_clock clks)
863{
864 unsigned num_clks = count_clk_bits(clks);
865
866 if (clks & DSS_CLK_ICK)
867 clk_disable(dss.dss_ick);
868 if (clks & DSS_CLK_FCK1)
869 clk_disable(dss.dss1_fck);
870 if (clks & DSS_CLK_FCK2)
871 clk_disable(dss.dss2_fck);
872 if (clks & DSS_CLK_54M)
873 clk_disable(dss.dss_54m_fck);
874 if (clks & DSS_CLK_96M)
875 clk_disable(dss.dss_96m_fck);
876
877 dss.num_clks_enabled -= num_clks;
878}
879
880void dss_clk_disable(enum dss_clock clks)
881{
882 if (cpu_is_omap34xx()) {
883 unsigned num_clks = count_clk_bits(clks);
884
885 BUG_ON(dss.num_clks_enabled < num_clks);
886
887 if (dss.num_clks_enabled == num_clks)
888 save_all_ctx();
889 }
890
891 dss_clk_disable_no_ctx(clks);
892}
893
894static void dss_clk_enable_all_no_ctx(void)
895{
896 enum dss_clock clks;
897
898 clks = DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_FCK2 | DSS_CLK_54M;
899 if (cpu_is_omap34xx())
900 clks |= DSS_CLK_96M;
901 dss_clk_enable_no_ctx(clks);
902}
903
904static void dss_clk_disable_all_no_ctx(void)
905{
906 enum dss_clock clks;
907
908 clks = DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_FCK2 | DSS_CLK_54M;
909 if (cpu_is_omap34xx())
910 clks |= DSS_CLK_96M;
911 dss_clk_disable_no_ctx(clks);
912}
913
914#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
915/* CLOCKS */
916static void core_dump_clocks(struct seq_file *s)
917{
918 int i;
919 struct clk *clocks[5] = {
920 dss.dss_ick,
921 dss.dss1_fck,
922 dss.dss2_fck,
923 dss.dss_54m_fck,
924 dss.dss_96m_fck
925 };
926
927 seq_printf(s, "- CORE -\n");
928
929 seq_printf(s, "internal clk count\t\t%u\n", dss.num_clks_enabled);
930
931 for (i = 0; i < 5; i++) {
932 if (!clocks[i])
933 continue;
934 seq_printf(s, "%-15s\t%lu\t%d\n",
935 clocks[i]->name,
936 clk_get_rate(clocks[i]),
937 clocks[i]->usecount);
938 }
939}
940#endif /* defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) */
941
942/* DEBUGFS */
943#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
944void dss_debug_dump_clocks(struct seq_file *s)
945{
946 core_dump_clocks(s);
947 dss_dump_clocks(s);
948 dispc_dump_clocks(s);
949#ifdef CONFIG_OMAP2_DSS_DSI
950 dsi_dump_clocks(s);
951#endif
952}
953#endif
954
955
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +0000956/* DSS HW IP initialisation */
957static int omap_dsshw_probe(struct platform_device *pdev)
958{
959 int r;
960 int skip_init = 0;
961
962 dss.pdev = pdev;
963
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000964 r = dss_get_clocks();
965 if (r)
966 goto err_clocks;
967
968 dss_clk_enable_all_no_ctx();
969
970 dss.ctx_id = dss_get_ctx_id();
971 DSSDBG("initial ctx id %u\n", dss.ctx_id);
972
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +0000973#ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT
974 /* DISPC_CONTROL */
975 if (omap_readl(0x48050440) & 1) /* LCD enabled? */
976 skip_init = 1;
977#endif
978
979 r = dss_init(skip_init);
980 if (r) {
981 DSSERR("Failed to initialize DSS\n");
982 goto err_dss;
983 }
984
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000985 dss_clk_disable_all_no_ctx();
986 return 0;
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +0000987
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000988err_dss:
989 dss_clk_disable_all_no_ctx();
990 dss_put_clocks();
991err_clocks:
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +0000992 return r;
993}
994
995static int omap_dsshw_remove(struct platform_device *pdev)
996{
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000997
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +0000998 dss_exit();
999
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +00001000 /*
1001 * As part of hwmod changes, DSS is not the only controller of dss
1002 * clocks; hwmod framework itself will also enable clocks during hwmod
1003 * init for dss, and autoidle is set in h/w for DSS. Hence, there's no
1004 * need to disable clocks if their usecounts > 1.
1005 */
1006 WARN_ON(dss.num_clks_enabled > 0);
1007
1008 dss_put_clocks();
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001009 return 0;
1010}
1011
1012static struct platform_driver omap_dsshw_driver = {
1013 .probe = omap_dsshw_probe,
1014 .remove = omap_dsshw_remove,
1015 .driver = {
1016 .name = "omapdss_dss",
1017 .owner = THIS_MODULE,
1018 },
1019};
1020
1021int dss_init_platform_driver(void)
1022{
1023 return platform_driver_register(&omap_dsshw_driver);
1024}
1025
1026void dss_uninit_platform_driver(void)
1027{
1028 return platform_driver_unregister(&omap_dsshw_driver);
1029}