blob: 8238a7b60761be27c599cb7164da2dfefc6a6fec [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
Tomi Valkeinena0b38cc2011-05-11 14:05:07 +030032#include <video/omapdss.h>
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +000033#include <plat/clock.h>
Tomi Valkeinen559d6702009-11-03 11:23:50 +020034#include "dss.h"
Tomi Valkeinen6ec549e2011-02-24 14:18:50 +020035#include "dss_features.h"
Tomi Valkeinen559d6702009-11-03 11:23:50 +020036
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;
Archit Tanejac7642f62011-01-31 16:27:45 +000067 struct clk *dss_fck;
68 struct clk *dss_sys_clk;
69 struct clk *dss_tv_fck;
70 struct clk *dss_video_fck;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +000071 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;
Taneja, Architea751592011-03-08 05:50:35 -060080 enum dss_clk_source lcd_clk_source[MAX_DSS_LCD_MANAGERS];
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +020081
Tomi Valkeinen559d6702009-11-03 11:23:50 +020082 u32 ctx[DSS_SZ_REGS / sizeof(u32)];
83} dss;
84
Taneja, Archit235e7db2011-03-14 23:28:21 -050085static const char * const dss_generic_clk_source_names[] = {
86 [DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC] = "DSI_PLL_HSDIV_DISPC",
87 [DSS_CLK_SRC_DSI_PLL_HSDIV_DSI] = "DSI_PLL_HSDIV_DSI",
88 [DSS_CLK_SRC_FCK] = "DSS_FCK",
Archit Taneja067a57e2011-03-02 11:57:25 +053089};
90
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +000091static void dss_clk_enable_all_no_ctx(void);
92static void dss_clk_disable_all_no_ctx(void);
93static void dss_clk_enable_no_ctx(enum dss_clock clks);
94static void dss_clk_disable_no_ctx(enum dss_clock clks);
95
Tomi Valkeinen559d6702009-11-03 11:23:50 +020096static int _omap_dss_wait_reset(void);
97
98static inline void dss_write_reg(const struct dss_reg idx, u32 val)
99{
100 __raw_writel(val, dss.base + idx.idx);
101}
102
103static inline u32 dss_read_reg(const struct dss_reg idx)
104{
105 return __raw_readl(dss.base + idx.idx);
106}
107
108#define SR(reg) \
109 dss.ctx[(DSS_##reg).idx / sizeof(u32)] = dss_read_reg(DSS_##reg)
110#define RR(reg) \
111 dss_write_reg(DSS_##reg, dss.ctx[(DSS_##reg).idx / sizeof(u32)])
112
113void dss_save_context(void)
114{
115 if (cpu_is_omap24xx())
116 return;
117
118 SR(SYSCONFIG);
119 SR(CONTROL);
120
Tomi Valkeinen6ec549e2011-02-24 14:18:50 +0200121 if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) &
122 OMAP_DISPLAY_TYPE_SDI) {
123 SR(SDI_CONTROL);
124 SR(PLL_CONTROL);
125 }
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200126}
127
128void dss_restore_context(void)
129{
130 if (_omap_dss_wait_reset())
131 DSSERR("DSS not coming out of reset after sleep\n");
132
133 RR(SYSCONFIG);
134 RR(CONTROL);
135
Tomi Valkeinen6ec549e2011-02-24 14:18:50 +0200136 if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) &
137 OMAP_DISPLAY_TYPE_SDI) {
138 RR(SDI_CONTROL);
139 RR(PLL_CONTROL);
140 }
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200141}
142
143#undef SR
144#undef RR
145
146void dss_sdi_init(u8 datapairs)
147{
148 u32 l;
149
150 BUG_ON(datapairs > 3 || datapairs < 1);
151
152 l = dss_read_reg(DSS_SDI_CONTROL);
153 l = FLD_MOD(l, 0xf, 19, 15); /* SDI_PDIV */
154 l = FLD_MOD(l, datapairs-1, 3, 2); /* SDI_PRSEL */
155 l = FLD_MOD(l, 2, 1, 0); /* SDI_BWSEL */
156 dss_write_reg(DSS_SDI_CONTROL, l);
157
158 l = dss_read_reg(DSS_PLL_CONTROL);
159 l = FLD_MOD(l, 0x7, 25, 22); /* SDI_PLL_FREQSEL */
160 l = FLD_MOD(l, 0xb, 16, 11); /* SDI_PLL_REGN */
161 l = FLD_MOD(l, 0xb4, 10, 1); /* SDI_PLL_REGM */
162 dss_write_reg(DSS_PLL_CONTROL, l);
163}
164
165int dss_sdi_enable(void)
166{
167 unsigned long timeout;
168
169 dispc_pck_free_enable(1);
170
171 /* Reset SDI PLL */
172 REG_FLD_MOD(DSS_PLL_CONTROL, 1, 18, 18); /* SDI_PLL_SYSRESET */
173 udelay(1); /* wait 2x PCLK */
174
175 /* Lock SDI PLL */
176 REG_FLD_MOD(DSS_PLL_CONTROL, 1, 28, 28); /* SDI_PLL_GOBIT */
177
178 /* Waiting for PLL lock request to complete */
179 timeout = jiffies + msecs_to_jiffies(500);
180 while (dss_read_reg(DSS_SDI_STATUS) & (1 << 6)) {
181 if (time_after_eq(jiffies, timeout)) {
182 DSSERR("PLL lock request timed out\n");
183 goto err1;
184 }
185 }
186
187 /* Clearing PLL_GO bit */
188 REG_FLD_MOD(DSS_PLL_CONTROL, 0, 28, 28);
189
190 /* Waiting for PLL to lock */
191 timeout = jiffies + msecs_to_jiffies(500);
192 while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 5))) {
193 if (time_after_eq(jiffies, timeout)) {
194 DSSERR("PLL lock timed out\n");
195 goto err1;
196 }
197 }
198
199 dispc_lcd_enable_signal(1);
200
201 /* Waiting for SDI reset to complete */
202 timeout = jiffies + msecs_to_jiffies(500);
203 while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 2))) {
204 if (time_after_eq(jiffies, timeout)) {
205 DSSERR("SDI reset timed out\n");
206 goto err2;
207 }
208 }
209
210 return 0;
211
212 err2:
213 dispc_lcd_enable_signal(0);
214 err1:
215 /* Reset SDI PLL */
216 REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */
217
218 dispc_pck_free_enable(0);
219
220 return -ETIMEDOUT;
221}
222
223void dss_sdi_disable(void)
224{
225 dispc_lcd_enable_signal(0);
226
227 dispc_pck_free_enable(0);
228
229 /* Reset SDI PLL */
230 REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */
231}
232
Archit Taneja067a57e2011-03-02 11:57:25 +0530233const char *dss_get_generic_clk_source_name(enum dss_clk_source clk_src)
234{
Taneja, Archit235e7db2011-03-14 23:28:21 -0500235 return dss_generic_clk_source_names[clk_src];
Archit Taneja067a57e2011-03-02 11:57:25 +0530236}
237
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200238void dss_dump_clocks(struct seq_file *s)
239{
240 unsigned long dpll4_ck_rate;
241 unsigned long dpll4_m4_ck_rate;
Tomi Valkeinen0acf6592011-03-14 07:28:57 -0500242 const char *fclk_name, *fclk_real_name;
243 unsigned long fclk_rate;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200244
Archit Taneja6af9cd12011-01-31 16:27:44 +0000245 dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200246
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200247 seq_printf(s, "- DSS -\n");
248
Tomi Valkeinen0acf6592011-03-14 07:28:57 -0500249 fclk_name = dss_get_generic_clk_source_name(DSS_CLK_SRC_FCK);
250 fclk_real_name = dss_feat_get_clk_source_name(DSS_CLK_SRC_FCK);
251 fclk_rate = dss_clk_get_rate(DSS_CLK_FCK);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200252
Tomi Valkeinen0acf6592011-03-14 07:28:57 -0500253 if (dss.dpll4_m4_ck) {
254 dpll4_ck_rate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
255 dpll4_m4_ck_rate = clk_get_rate(dss.dpll4_m4_ck);
256
257 seq_printf(s, "dpll4_ck %lu\n", dpll4_ck_rate);
258
Murthy, Raghuveer2de11082011-03-14 07:28:58 -0500259 if (cpu_is_omap3630() || cpu_is_omap44xx())
Tomi Valkeinen0acf6592011-03-14 07:28:57 -0500260 seq_printf(s, "%s (%s) = %lu / %lu = %lu\n",
261 fclk_name, fclk_real_name,
262 dpll4_ck_rate,
263 dpll4_ck_rate / dpll4_m4_ck_rate,
264 fclk_rate);
265 else
266 seq_printf(s, "%s (%s) = %lu / %lu * 2 = %lu\n",
267 fclk_name, fclk_real_name,
268 dpll4_ck_rate,
269 dpll4_ck_rate / dpll4_m4_ck_rate,
270 fclk_rate);
271 } else {
272 seq_printf(s, "%s (%s) = %lu\n",
273 fclk_name, fclk_real_name,
274 fclk_rate);
275 }
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200276
Archit Taneja6af9cd12011-01-31 16:27:44 +0000277 dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200278}
279
280void dss_dump_regs(struct seq_file *s)
281{
282#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r))
283
Archit Taneja6af9cd12011-01-31 16:27:44 +0000284 dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200285
286 DUMPREG(DSS_REVISION);
287 DUMPREG(DSS_SYSCONFIG);
288 DUMPREG(DSS_SYSSTATUS);
289 DUMPREG(DSS_IRQSTATUS);
290 DUMPREG(DSS_CONTROL);
Tomi Valkeinen6ec549e2011-02-24 14:18:50 +0200291
292 if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) &
293 OMAP_DISPLAY_TYPE_SDI) {
294 DUMPREG(DSS_SDI_CONTROL);
295 DUMPREG(DSS_PLL_CONTROL);
296 DUMPREG(DSS_SDI_STATUS);
297 }
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200298
Archit Taneja6af9cd12011-01-31 16:27:44 +0000299 dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200300#undef DUMPREG
301}
302
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200303void dss_select_dispc_clk_source(enum dss_clk_source clk_src)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200304{
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200305 int b;
Taneja, Architea751592011-03-08 05:50:35 -0600306 u8 start, end;
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200307
Taneja, Archit66534e82011-03-08 05:50:34 -0600308 switch (clk_src) {
309 case DSS_CLK_SRC_FCK:
310 b = 0;
311 break;
312 case DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC:
313 b = 1;
Archit Taneja1bb47832011-02-24 14:17:30 +0530314 dsi_wait_pll_hsdiv_dispc_active();
Taneja, Archit66534e82011-03-08 05:50:34 -0600315 break;
316 default:
317 BUG();
318 }
Tomi Valkeinene406f902010-06-09 15:28:12 +0300319
Taneja, Architea751592011-03-08 05:50:35 -0600320 dss_feat_get_reg_field(FEAT_REG_DISPC_CLK_SWITCH, &start, &end);
321
322 REG_FLD_MOD(DSS_CONTROL, b, start, end); /* DISPC_CLK_SWITCH */
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200323
324 dss.dispc_clk_source = clk_src;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200325}
326
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200327void dss_select_dsi_clk_source(enum dss_clk_source clk_src)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200328{
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200329 int b;
330
Taneja, Archit66534e82011-03-08 05:50:34 -0600331 switch (clk_src) {
332 case DSS_CLK_SRC_FCK:
333 b = 0;
334 break;
335 case DSS_CLK_SRC_DSI_PLL_HSDIV_DSI:
336 b = 1;
Archit Taneja1bb47832011-02-24 14:17:30 +0530337 dsi_wait_pll_hsdiv_dsi_active();
Taneja, Archit66534e82011-03-08 05:50:34 -0600338 break;
339 default:
340 BUG();
341 }
Tomi Valkeinene406f902010-06-09 15:28:12 +0300342
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200343 REG_FLD_MOD(DSS_CONTROL, b, 1, 1); /* DSI_CLK_SWITCH */
344
345 dss.dsi_clk_source = clk_src;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200346}
347
Taneja, Architea751592011-03-08 05:50:35 -0600348void dss_select_lcd_clk_source(enum omap_channel channel,
349 enum dss_clk_source clk_src)
350{
351 int b, ix, pos;
352
353 if (!dss_has_feature(FEAT_LCD_CLK_SRC))
354 return;
355
356 switch (clk_src) {
357 case DSS_CLK_SRC_FCK:
358 b = 0;
359 break;
360 case DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC:
361 BUG_ON(channel != OMAP_DSS_CHANNEL_LCD);
362 b = 1;
363 dsi_wait_pll_hsdiv_dispc_active();
364 break;
365 default:
366 BUG();
367 }
368
369 pos = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 12;
370 REG_FLD_MOD(DSS_CONTROL, b, pos, pos); /* LCDx_CLK_SWITCH */
371
372 ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 1;
373 dss.lcd_clk_source[ix] = clk_src;
374}
375
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200376enum dss_clk_source dss_get_dispc_clk_source(void)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200377{
Tomi Valkeinen2f18c4d2010-01-08 18:00:36 +0200378 return dss.dispc_clk_source;
379}
380
381enum dss_clk_source dss_get_dsi_clk_source(void)
382{
383 return dss.dsi_clk_source;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200384}
385
Taneja, Architea751592011-03-08 05:50:35 -0600386enum dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel)
387{
Archit Taneja89976f22011-03-31 13:23:35 +0530388 if (dss_has_feature(FEAT_LCD_CLK_SRC)) {
389 int ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 1;
390 return dss.lcd_clk_source[ix];
391 } else {
392 /* LCD_CLK source is the same as DISPC_FCLK source for
393 * OMAP2 and OMAP3 */
394 return dss.dispc_clk_source;
395 }
Taneja, Architea751592011-03-08 05:50:35 -0600396}
397
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200398/* calculate clock rates using dividers in cinfo */
399int dss_calc_clock_rates(struct dss_clock_info *cinfo)
400{
Tomi Valkeinen0acf6592011-03-14 07:28:57 -0500401 if (dss.dpll4_m4_ck) {
402 unsigned long prate;
Murthy, Raghuveer2de11082011-03-14 07:28:58 -0500403 u16 fck_div_max = 16;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200404
Murthy, Raghuveer2de11082011-03-14 07:28:58 -0500405 if (cpu_is_omap3630() || cpu_is_omap44xx())
406 fck_div_max = 32;
407
408 if (cinfo->fck_div > fck_div_max || cinfo->fck_div == 0)
Tomi Valkeinen0acf6592011-03-14 07:28:57 -0500409 return -EINVAL;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200410
Tomi Valkeinen0acf6592011-03-14 07:28:57 -0500411 prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200412
Tomi Valkeinen0acf6592011-03-14 07:28:57 -0500413 cinfo->fck = prate / cinfo->fck_div;
414 } else {
415 if (cinfo->fck_div != 0)
416 return -EINVAL;
417 cinfo->fck = dss_clk_get_rate(DSS_CLK_FCK);
418 }
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200419
420 return 0;
421}
422
423int dss_set_clock_div(struct dss_clock_info *cinfo)
424{
Tomi Valkeinen0acf6592011-03-14 07:28:57 -0500425 if (dss.dpll4_m4_ck) {
426 unsigned long prate;
427 int r;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200428
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200429 prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
430 DSSDBG("dpll4_m4 = %ld\n", prate);
431
432 r = clk_set_rate(dss.dpll4_m4_ck, prate / cinfo->fck_div);
433 if (r)
434 return r;
Tomi Valkeinen0acf6592011-03-14 07:28:57 -0500435 } else {
436 if (cinfo->fck_div != 0)
437 return -EINVAL;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200438 }
439
440 DSSDBG("fck = %ld (%d)\n", cinfo->fck, cinfo->fck_div);
441
442 return 0;
443}
444
445int dss_get_clock_div(struct dss_clock_info *cinfo)
446{
Archit Taneja6af9cd12011-01-31 16:27:44 +0000447 cinfo->fck = dss_clk_get_rate(DSS_CLK_FCK);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200448
Tomi Valkeinen0acf6592011-03-14 07:28:57 -0500449 if (dss.dpll4_m4_ck) {
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200450 unsigned long prate;
Tomi Valkeinen0acf6592011-03-14 07:28:57 -0500451
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200452 prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
Tomi Valkeinen0acf6592011-03-14 07:28:57 -0500453
Murthy, Raghuveer2de11082011-03-14 07:28:58 -0500454 if (cpu_is_omap3630() || cpu_is_omap44xx())
Kishore Yac01bb72010-04-25 16:27:19 +0530455 cinfo->fck_div = prate / (cinfo->fck);
456 else
457 cinfo->fck_div = prate / (cinfo->fck / 2);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200458 } else {
459 cinfo->fck_div = 0;
460 }
461
462 return 0;
463}
464
465unsigned long dss_get_dpll4_rate(void)
466{
Tomi Valkeinen0acf6592011-03-14 07:28:57 -0500467 if (dss.dpll4_m4_ck)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200468 return clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
469 else
470 return 0;
471}
472
473int dss_calc_clock_div(bool is_tft, unsigned long req_pck,
474 struct dss_clock_info *dss_cinfo,
475 struct dispc_clock_info *dispc_cinfo)
476{
477 unsigned long prate;
478 struct dss_clock_info best_dss;
479 struct dispc_clock_info best_dispc;
480
Archit Taneja819d8072011-03-01 11:54:00 +0530481 unsigned long fck, max_dss_fck;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200482
Murthy, Raghuveer2de11082011-03-14 07:28:58 -0500483 u16 fck_div, fck_div_max = 16;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200484
485 int match = 0;
486 int min_fck_per_pck;
487
488 prate = dss_get_dpll4_rate();
489
Taneja, Archit31ef8232011-03-14 23:28:22 -0500490 max_dss_fck = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK);
Archit Taneja819d8072011-03-01 11:54:00 +0530491
Archit Taneja6af9cd12011-01-31 16:27:44 +0000492 fck = dss_clk_get_rate(DSS_CLK_FCK);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200493 if (req_pck == dss.cache_req_pck &&
494 ((cpu_is_omap34xx() && prate == dss.cache_prate) ||
495 dss.cache_dss_cinfo.fck == fck)) {
496 DSSDBG("dispc clock info found from cache.\n");
497 *dss_cinfo = dss.cache_dss_cinfo;
498 *dispc_cinfo = dss.cache_dispc_cinfo;
499 return 0;
500 }
501
502 min_fck_per_pck = CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK;
503
504 if (min_fck_per_pck &&
Archit Taneja819d8072011-03-01 11:54:00 +0530505 req_pck * min_fck_per_pck > max_dss_fck) {
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200506 DSSERR("Requested pixel clock not possible with the current "
507 "OMAP2_DSS_MIN_FCK_PER_PCK setting. Turning "
508 "the constraint off.\n");
509 min_fck_per_pck = 0;
510 }
511
512retry:
513 memset(&best_dss, 0, sizeof(best_dss));
514 memset(&best_dispc, 0, sizeof(best_dispc));
515
Murthy, Raghuveer2de11082011-03-14 07:28:58 -0500516 if (dss.dpll4_m4_ck == NULL) {
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200517 struct dispc_clock_info cur_dispc;
518 /* XXX can we change the clock on omap2? */
Archit Taneja6af9cd12011-01-31 16:27:44 +0000519 fck = dss_clk_get_rate(DSS_CLK_FCK);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200520 fck_div = 1;
521
522 dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc);
523 match = 1;
524
525 best_dss.fck = fck;
526 best_dss.fck_div = fck_div;
527
528 best_dispc = cur_dispc;
529
530 goto found;
Murthy, Raghuveer2de11082011-03-14 07:28:58 -0500531 } else {
532 if (cpu_is_omap3630() || cpu_is_omap44xx())
533 fck_div_max = 32;
534
535 for (fck_div = fck_div_max; fck_div > 0; --fck_div) {
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200536 struct dispc_clock_info cur_dispc;
537
Murthy, Raghuveer2de11082011-03-14 07:28:58 -0500538 if (fck_div_max == 32)
Kishore Yac01bb72010-04-25 16:27:19 +0530539 fck = prate / fck_div;
540 else
541 fck = prate / fck_div * 2;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200542
Archit Taneja819d8072011-03-01 11:54:00 +0530543 if (fck > max_dss_fck)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200544 continue;
545
546 if (min_fck_per_pck &&
547 fck < req_pck * min_fck_per_pck)
548 continue;
549
550 match = 1;
551
552 dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc);
553
554 if (abs(cur_dispc.pck - req_pck) <
555 abs(best_dispc.pck - req_pck)) {
556
557 best_dss.fck = fck;
558 best_dss.fck_div = fck_div;
559
560 best_dispc = cur_dispc;
561
562 if (cur_dispc.pck == req_pck)
563 goto found;
564 }
565 }
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200566 }
567
568found:
569 if (!match) {
570 if (min_fck_per_pck) {
571 DSSERR("Could not find suitable clock settings.\n"
572 "Turning FCK/PCK constraint off and"
573 "trying again.\n");
574 min_fck_per_pck = 0;
575 goto retry;
576 }
577
578 DSSERR("Could not find suitable clock settings.\n");
579
580 return -EINVAL;
581 }
582
583 if (dss_cinfo)
584 *dss_cinfo = best_dss;
585 if (dispc_cinfo)
586 *dispc_cinfo = best_dispc;
587
588 dss.cache_req_pck = req_pck;
589 dss.cache_prate = prate;
590 dss.cache_dss_cinfo = best_dss;
591 dss.cache_dispc_cinfo = best_dispc;
592
593 return 0;
594}
595
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200596static int _omap_dss_wait_reset(void)
597{
Tomi Valkeinen24be78b2010-01-07 14:19:48 +0200598 int t = 0;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200599
600 while (REG_GET(DSS_SYSSTATUS, 0, 0) == 0) {
Tomi Valkeinen24be78b2010-01-07 14:19:48 +0200601 if (++t > 1000) {
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200602 DSSERR("soft reset failed\n");
603 return -ENODEV;
604 }
Tomi Valkeinen24be78b2010-01-07 14:19:48 +0200605 udelay(1);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200606 }
607
608 return 0;
609}
610
611static int _omap_dss_reset(void)
612{
613 /* Soft reset */
614 REG_FLD_MOD(DSS_SYSCONFIG, 1, 1, 1);
615 return _omap_dss_wait_reset();
616}
617
618void dss_set_venc_output(enum omap_dss_venc_type type)
619{
620 int l = 0;
621
622 if (type == OMAP_DSS_VENC_TYPE_COMPOSITE)
623 l = 0;
624 else if (type == OMAP_DSS_VENC_TYPE_SVIDEO)
625 l = 1;
626 else
627 BUG();
628
629 /* venc out selection. 0 = comp, 1 = svideo */
630 REG_FLD_MOD(DSS_CONTROL, l, 6, 6);
631}
632
633void dss_set_dac_pwrdn_bgz(bool enable)
634{
635 REG_FLD_MOD(DSS_CONTROL, enable, 5, 5); /* DAC Power-Down Control */
636}
637
Mythri P K7ed024a2011-03-09 16:31:38 +0530638void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select hdmi)
639{
640 REG_FLD_MOD(DSS_CONTROL, hdmi, 15, 15); /* VENC_HDMI_SWITCH */
641}
642
Tomi Valkeinen42c9dee2011-03-02 12:29:27 +0200643static int dss_init(void)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200644{
645 int r;
646 u32 rev;
Senthilvadivu Guruswamyea9da362011-01-24 06:22:04 +0000647 struct resource *dss_mem;
Tomi Valkeinen0acf6592011-03-14 07:28:57 -0500648 struct clk *dpll4_m4_ck;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200649
Senthilvadivu Guruswamyea9da362011-01-24 06:22:04 +0000650 dss_mem = platform_get_resource(dss.pdev, IORESOURCE_MEM, 0);
651 if (!dss_mem) {
652 DSSERR("can't get IORESOURCE_MEM DSS\n");
653 r = -EINVAL;
654 goto fail0;
655 }
656 dss.base = ioremap(dss_mem->start, resource_size(dss_mem));
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200657 if (!dss.base) {
658 DSSERR("can't ioremap DSS\n");
659 r = -ENOMEM;
660 goto fail0;
661 }
662
Tomi Valkeinen42c9dee2011-03-02 12:29:27 +0200663 /* disable LCD and DIGIT output. This seems to fix the synclost
664 * problem that we get, if the bootloader starts the DSS and
665 * the kernel resets it */
666 omap_writel(omap_readl(0x48050440) & ~0x3, 0x48050440);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200667
Tomi Valkeinen42c9dee2011-03-02 12:29:27 +0200668 /* We need to wait here a bit, otherwise we sometimes start to
669 * get synclost errors, and after that only power cycle will
670 * restore DSS functionality. I have no idea why this happens.
671 * And we have to wait _before_ resetting the DSS, but after
672 * enabling clocks.
673 */
674 msleep(50);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200675
Tomi Valkeinen42c9dee2011-03-02 12:29:27 +0200676 _omap_dss_reset();
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200677
678 /* autoidle */
679 REG_FLD_MOD(DSS_SYSCONFIG, 1, 0, 0);
680
681 /* Select DPLL */
682 REG_FLD_MOD(DSS_CONTROL, 0, 0, 0);
683
684#ifdef CONFIG_OMAP2_DSS_VENC
685 REG_FLD_MOD(DSS_CONTROL, 1, 4, 4); /* venc dac demen */
686 REG_FLD_MOD(DSS_CONTROL, 1, 3, 3); /* venc clock 4x enable */
687 REG_FLD_MOD(DSS_CONTROL, 0, 2, 2); /* venc clock mode = normal */
688#endif
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200689 if (cpu_is_omap34xx()) {
Tomi Valkeinen0acf6592011-03-14 07:28:57 -0500690 dpll4_m4_ck = clk_get(NULL, "dpll4_m4_ck");
691 if (IS_ERR(dpll4_m4_ck)) {
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200692 DSSERR("Failed to get dpll4_m4_ck\n");
Tomi Valkeinen0acf6592011-03-14 07:28:57 -0500693 r = PTR_ERR(dpll4_m4_ck);
archit tanejaaffe3602011-02-23 08:41:03 +0000694 goto fail1;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200695 }
Murthy, Raghuveer2de11082011-03-14 07:28:58 -0500696 } else if (cpu_is_omap44xx()) {
697 dpll4_m4_ck = clk_get(NULL, "dpll_per_m5x2_ck");
698 if (IS_ERR(dpll4_m4_ck)) {
699 DSSERR("Failed to get dpll4_m4_ck\n");
700 r = PTR_ERR(dpll4_m4_ck);
701 goto fail1;
702 }
Tomi Valkeinen0acf6592011-03-14 07:28:57 -0500703 } else { /* omap24xx */
704 dpll4_m4_ck = NULL;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200705 }
706
Tomi Valkeinen0acf6592011-03-14 07:28:57 -0500707 dss.dpll4_m4_ck = dpll4_m4_ck;
708
Archit Taneja88134fa2011-01-06 10:44:10 +0530709 dss.dsi_clk_source = DSS_CLK_SRC_FCK;
710 dss.dispc_clk_source = DSS_CLK_SRC_FCK;
Taneja, Architea751592011-03-08 05:50:35 -0600711 dss.lcd_clk_source[0] = DSS_CLK_SRC_FCK;
712 dss.lcd_clk_source[1] = DSS_CLK_SRC_FCK;
Tomi Valkeinence619e12010-03-12 12:46:05 +0200713
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200714 dss_save_context();
715
716 rev = dss_read_reg(DSS_REVISION);
717 printk(KERN_INFO "OMAP DSS rev %d.%d\n",
718 FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
719
720 return 0;
721
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200722fail1:
723 iounmap(dss.base);
724fail0:
725 return r;
726}
727
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +0000728static void dss_exit(void)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200729{
Tomi Valkeinen0acf6592011-03-14 07:28:57 -0500730 if (dss.dpll4_m4_ck)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200731 clk_put(dss.dpll4_m4_ck);
732
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200733 iounmap(dss.base);
734}
735
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000736/* CONTEXT */
737static int dss_get_ctx_id(void)
738{
739 struct omap_display_platform_data *pdata = dss.pdev->dev.platform_data;
740 int r;
741
742 if (!pdata->board_data->get_last_off_on_transaction_id)
743 return 0;
744 r = pdata->board_data->get_last_off_on_transaction_id(&dss.pdev->dev);
745 if (r < 0) {
746 dev_err(&dss.pdev->dev, "getting transaction ID failed, "
747 "will force context restore\n");
748 r = -1;
749 }
750 return r;
751}
752
753int dss_need_ctx_restore(void)
754{
755 int id = dss_get_ctx_id();
756
757 if (id < 0 || id != dss.ctx_id) {
758 DSSDBG("ctx id %d -> id %d\n",
759 dss.ctx_id, id);
760 dss.ctx_id = id;
761 return 1;
762 } else {
763 return 0;
764 }
765}
766
767static void save_all_ctx(void)
768{
769 DSSDBG("save context\n");
770
Archit Taneja6af9cd12011-01-31 16:27:44 +0000771 dss_clk_enable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK);
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000772
773 dss_save_context();
774 dispc_save_context();
775#ifdef CONFIG_OMAP2_DSS_DSI
776 dsi_save_context();
777#endif
778
Archit Taneja6af9cd12011-01-31 16:27:44 +0000779 dss_clk_disable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK);
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000780}
781
782static void restore_all_ctx(void)
783{
784 DSSDBG("restore context\n");
785
786 dss_clk_enable_all_no_ctx();
787
788 dss_restore_context();
789 dispc_restore_context();
790#ifdef CONFIG_OMAP2_DSS_DSI
791 dsi_restore_context();
792#endif
793
794 dss_clk_disable_all_no_ctx();
795}
796
797static int dss_get_clock(struct clk **clock, const char *clk_name)
798{
799 struct clk *clk;
800
801 clk = clk_get(&dss.pdev->dev, clk_name);
802
803 if (IS_ERR(clk)) {
804 DSSERR("can't get clock %s", clk_name);
805 return PTR_ERR(clk);
806 }
807
808 *clock = clk;
809
810 DSSDBG("clk %s, rate %ld\n", clk_name, clk_get_rate(clk));
811
812 return 0;
813}
814
815static int dss_get_clocks(void)
816{
817 int r;
Semwal, Sumita1a0dcc2011-03-01 02:42:14 -0600818 struct omap_display_platform_data *pdata = dss.pdev->dev.platform_data;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000819
820 dss.dss_ick = NULL;
Archit Tanejac7642f62011-01-31 16:27:45 +0000821 dss.dss_fck = NULL;
822 dss.dss_sys_clk = NULL;
823 dss.dss_tv_fck = NULL;
824 dss.dss_video_fck = NULL;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000825
826 r = dss_get_clock(&dss.dss_ick, "ick");
827 if (r)
828 goto err;
829
Archit Tanejac7642f62011-01-31 16:27:45 +0000830 r = dss_get_clock(&dss.dss_fck, "fck");
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000831 if (r)
832 goto err;
833
Semwal, Sumita1a0dcc2011-03-01 02:42:14 -0600834 if (!pdata->opt_clock_available) {
835 r = -ENODEV;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000836 goto err;
Semwal, Sumita1a0dcc2011-03-01 02:42:14 -0600837 }
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000838
Semwal, Sumita1a0dcc2011-03-01 02:42:14 -0600839 if (pdata->opt_clock_available("sys_clk")) {
840 r = dss_get_clock(&dss.dss_sys_clk, "sys_clk");
841 if (r)
842 goto err;
843 }
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000844
Semwal, Sumita1a0dcc2011-03-01 02:42:14 -0600845 if (pdata->opt_clock_available("tv_clk")) {
846 r = dss_get_clock(&dss.dss_tv_fck, "tv_clk");
847 if (r)
848 goto err;
849 }
850
851 if (pdata->opt_clock_available("video_clk")) {
852 r = dss_get_clock(&dss.dss_video_fck, "video_clk");
853 if (r)
854 goto err;
855 }
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000856
857 return 0;
858
859err:
860 if (dss.dss_ick)
861 clk_put(dss.dss_ick);
Archit Tanejac7642f62011-01-31 16:27:45 +0000862 if (dss.dss_fck)
863 clk_put(dss.dss_fck);
864 if (dss.dss_sys_clk)
865 clk_put(dss.dss_sys_clk);
866 if (dss.dss_tv_fck)
867 clk_put(dss.dss_tv_fck);
868 if (dss.dss_video_fck)
869 clk_put(dss.dss_video_fck);
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000870
871 return r;
872}
873
874static void dss_put_clocks(void)
875{
Archit Tanejac7642f62011-01-31 16:27:45 +0000876 if (dss.dss_video_fck)
877 clk_put(dss.dss_video_fck);
Semwal, Sumita1a0dcc2011-03-01 02:42:14 -0600878 if (dss.dss_tv_fck)
879 clk_put(dss.dss_tv_fck);
880 if (dss.dss_sys_clk)
881 clk_put(dss.dss_sys_clk);
Archit Tanejac7642f62011-01-31 16:27:45 +0000882 clk_put(dss.dss_fck);
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000883 clk_put(dss.dss_ick);
884}
885
886unsigned long dss_clk_get_rate(enum dss_clock clk)
887{
888 switch (clk) {
889 case DSS_CLK_ICK:
890 return clk_get_rate(dss.dss_ick);
Archit Taneja6af9cd12011-01-31 16:27:44 +0000891 case DSS_CLK_FCK:
Archit Tanejac7642f62011-01-31 16:27:45 +0000892 return clk_get_rate(dss.dss_fck);
Archit Taneja6af9cd12011-01-31 16:27:44 +0000893 case DSS_CLK_SYSCK:
Archit Tanejac7642f62011-01-31 16:27:45 +0000894 return clk_get_rate(dss.dss_sys_clk);
Archit Taneja6af9cd12011-01-31 16:27:44 +0000895 case DSS_CLK_TVFCK:
Archit Tanejac7642f62011-01-31 16:27:45 +0000896 return clk_get_rate(dss.dss_tv_fck);
Archit Taneja6af9cd12011-01-31 16:27:44 +0000897 case DSS_CLK_VIDFCK:
Archit Tanejac7642f62011-01-31 16:27:45 +0000898 return clk_get_rate(dss.dss_video_fck);
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000899 }
900
901 BUG();
902 return 0;
903}
904
905static unsigned count_clk_bits(enum dss_clock clks)
906{
907 unsigned num_clks = 0;
908
909 if (clks & DSS_CLK_ICK)
910 ++num_clks;
Archit Taneja6af9cd12011-01-31 16:27:44 +0000911 if (clks & DSS_CLK_FCK)
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000912 ++num_clks;
Archit Taneja6af9cd12011-01-31 16:27:44 +0000913 if (clks & DSS_CLK_SYSCK)
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000914 ++num_clks;
Archit Taneja6af9cd12011-01-31 16:27:44 +0000915 if (clks & DSS_CLK_TVFCK)
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000916 ++num_clks;
Archit Taneja6af9cd12011-01-31 16:27:44 +0000917 if (clks & DSS_CLK_VIDFCK)
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000918 ++num_clks;
919
920 return num_clks;
921}
922
923static void dss_clk_enable_no_ctx(enum dss_clock clks)
924{
925 unsigned num_clks = count_clk_bits(clks);
926
927 if (clks & DSS_CLK_ICK)
928 clk_enable(dss.dss_ick);
Archit Taneja6af9cd12011-01-31 16:27:44 +0000929 if (clks & DSS_CLK_FCK)
Archit Tanejac7642f62011-01-31 16:27:45 +0000930 clk_enable(dss.dss_fck);
Semwal, Sumita1a0dcc2011-03-01 02:42:14 -0600931 if ((clks & DSS_CLK_SYSCK) && dss.dss_sys_clk)
Archit Tanejac7642f62011-01-31 16:27:45 +0000932 clk_enable(dss.dss_sys_clk);
Semwal, Sumita1a0dcc2011-03-01 02:42:14 -0600933 if ((clks & DSS_CLK_TVFCK) && dss.dss_tv_fck)
Archit Tanejac7642f62011-01-31 16:27:45 +0000934 clk_enable(dss.dss_tv_fck);
Semwal, Sumita1a0dcc2011-03-01 02:42:14 -0600935 if ((clks & DSS_CLK_VIDFCK) && dss.dss_video_fck)
Archit Tanejac7642f62011-01-31 16:27:45 +0000936 clk_enable(dss.dss_video_fck);
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000937
938 dss.num_clks_enabled += num_clks;
939}
940
941void dss_clk_enable(enum dss_clock clks)
942{
943 bool check_ctx = dss.num_clks_enabled == 0;
944
945 dss_clk_enable_no_ctx(clks);
946
Tomi Valkeinen85604b02011-03-03 13:16:23 +0200947 /*
948 * HACK: On omap4 the registers may not be accessible right after
949 * enabling the clocks. At some point this will be handled by
950 * pm_runtime, but for the time begin this should make things work.
951 */
952 if (cpu_is_omap44xx() && check_ctx)
953 udelay(10);
954
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000955 if (check_ctx && cpu_is_omap34xx() && dss_need_ctx_restore())
956 restore_all_ctx();
957}
958
959static void dss_clk_disable_no_ctx(enum dss_clock clks)
960{
961 unsigned num_clks = count_clk_bits(clks);
962
963 if (clks & DSS_CLK_ICK)
964 clk_disable(dss.dss_ick);
Archit Taneja6af9cd12011-01-31 16:27:44 +0000965 if (clks & DSS_CLK_FCK)
Archit Tanejac7642f62011-01-31 16:27:45 +0000966 clk_disable(dss.dss_fck);
Semwal, Sumita1a0dcc2011-03-01 02:42:14 -0600967 if ((clks & DSS_CLK_SYSCK) && dss.dss_sys_clk)
Archit Tanejac7642f62011-01-31 16:27:45 +0000968 clk_disable(dss.dss_sys_clk);
Semwal, Sumita1a0dcc2011-03-01 02:42:14 -0600969 if ((clks & DSS_CLK_TVFCK) && dss.dss_tv_fck)
Archit Tanejac7642f62011-01-31 16:27:45 +0000970 clk_disable(dss.dss_tv_fck);
Semwal, Sumita1a0dcc2011-03-01 02:42:14 -0600971 if ((clks & DSS_CLK_VIDFCK) && dss.dss_video_fck)
Archit Tanejac7642f62011-01-31 16:27:45 +0000972 clk_disable(dss.dss_video_fck);
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000973
974 dss.num_clks_enabled -= num_clks;
975}
976
977void dss_clk_disable(enum dss_clock clks)
978{
979 if (cpu_is_omap34xx()) {
980 unsigned num_clks = count_clk_bits(clks);
981
982 BUG_ON(dss.num_clks_enabled < num_clks);
983
984 if (dss.num_clks_enabled == num_clks)
985 save_all_ctx();
986 }
987
988 dss_clk_disable_no_ctx(clks);
989}
990
991static void dss_clk_enable_all_no_ctx(void)
992{
993 enum dss_clock clks;
994
Archit Taneja6af9cd12011-01-31 16:27:44 +0000995 clks = DSS_CLK_ICK | DSS_CLK_FCK | DSS_CLK_SYSCK | DSS_CLK_TVFCK;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000996 if (cpu_is_omap34xx())
Archit Taneja6af9cd12011-01-31 16:27:44 +0000997 clks |= DSS_CLK_VIDFCK;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +0000998 dss_clk_enable_no_ctx(clks);
999}
1000
1001static void dss_clk_disable_all_no_ctx(void)
1002{
1003 enum dss_clock clks;
1004
Archit Taneja6af9cd12011-01-31 16:27:44 +00001005 clks = DSS_CLK_ICK | DSS_CLK_FCK | DSS_CLK_SYSCK | DSS_CLK_TVFCK;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +00001006 if (cpu_is_omap34xx())
Archit Taneja6af9cd12011-01-31 16:27:44 +00001007 clks |= DSS_CLK_VIDFCK;
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +00001008 dss_clk_disable_no_ctx(clks);
1009}
1010
1011#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
1012/* CLOCKS */
1013static void core_dump_clocks(struct seq_file *s)
1014{
1015 int i;
1016 struct clk *clocks[5] = {
1017 dss.dss_ick,
Archit Tanejac7642f62011-01-31 16:27:45 +00001018 dss.dss_fck,
1019 dss.dss_sys_clk,
1020 dss.dss_tv_fck,
1021 dss.dss_video_fck
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +00001022 };
1023
1024 seq_printf(s, "- CORE -\n");
1025
1026 seq_printf(s, "internal clk count\t\t%u\n", dss.num_clks_enabled);
1027
1028 for (i = 0; i < 5; i++) {
1029 if (!clocks[i])
1030 continue;
1031 seq_printf(s, "%-15s\t%lu\t%d\n",
1032 clocks[i]->name,
1033 clk_get_rate(clocks[i]),
1034 clocks[i]->usecount);
1035 }
1036}
1037#endif /* defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) */
1038
1039/* DEBUGFS */
1040#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
1041void dss_debug_dump_clocks(struct seq_file *s)
1042{
1043 core_dump_clocks(s);
1044 dss_dump_clocks(s);
1045 dispc_dump_clocks(s);
1046#ifdef CONFIG_OMAP2_DSS_DSI
1047 dsi_dump_clocks(s);
1048#endif
1049}
1050#endif
1051
1052
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001053/* DSS HW IP initialisation */
1054static int omap_dsshw_probe(struct platform_device *pdev)
1055{
1056 int r;
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001057
1058 dss.pdev = pdev;
1059
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +00001060 r = dss_get_clocks();
1061 if (r)
1062 goto err_clocks;
1063
1064 dss_clk_enable_all_no_ctx();
1065
1066 dss.ctx_id = dss_get_ctx_id();
1067 DSSDBG("initial ctx id %u\n", dss.ctx_id);
1068
Tomi Valkeinen42c9dee2011-03-02 12:29:27 +02001069 r = dss_init();
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001070 if (r) {
1071 DSSERR("Failed to initialize DSS\n");
1072 goto err_dss;
1073 }
1074
Tomi Valkeinen587b5e82011-03-02 12:47:54 +02001075 r = dpi_init();
1076 if (r) {
1077 DSSERR("Failed to initialize DPI\n");
1078 goto err_dpi;
1079 }
1080
1081 r = sdi_init();
1082 if (r) {
1083 DSSERR("Failed to initialize SDI\n");
1084 goto err_sdi;
1085 }
1086
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +00001087 dss_clk_disable_all_no_ctx();
1088 return 0;
Tomi Valkeinen587b5e82011-03-02 12:47:54 +02001089err_sdi:
1090 dpi_exit();
1091err_dpi:
1092 dss_exit();
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +00001093err_dss:
1094 dss_clk_disable_all_no_ctx();
1095 dss_put_clocks();
1096err_clocks:
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001097 return r;
1098}
1099
1100static int omap_dsshw_remove(struct platform_device *pdev)
1101{
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +00001102
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001103 dss_exit();
1104
Senthilvadivu Guruswamy8b9cb3a2011-01-24 06:21:58 +00001105 /*
1106 * As part of hwmod changes, DSS is not the only controller of dss
1107 * clocks; hwmod framework itself will also enable clocks during hwmod
1108 * init for dss, and autoidle is set in h/w for DSS. Hence, there's no
1109 * need to disable clocks if their usecounts > 1.
1110 */
1111 WARN_ON(dss.num_clks_enabled > 0);
1112
1113 dss_put_clocks();
Senthilvadivu Guruswamy96c401b2011-01-24 06:21:57 +00001114 return 0;
1115}
1116
1117static struct platform_driver omap_dsshw_driver = {
1118 .probe = omap_dsshw_probe,
1119 .remove = omap_dsshw_remove,
1120 .driver = {
1121 .name = "omapdss_dss",
1122 .owner = THIS_MODULE,
1123 },
1124};
1125
1126int dss_init_platform_driver(void)
1127{
1128 return platform_driver_register(&omap_dsshw_driver);
1129}
1130
1131void dss_uninit_platform_driver(void)
1132{
1133 return platform_driver_unregister(&omap_dsshw_driver);
1134}