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