diff options
author | Imre Deak <imre.deak@solidboot.com> | 2007-07-17 07:05:59 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-17 13:23:14 -0400 |
commit | f5c125a0fc44e513c3d51d179cb8a9ccaf589b7b (patch) | |
tree | 507019a730c458c52c896ef92e57ec3b9fea4fe0 /drivers/video/omap | |
parent | e6b4573c563966f3b22aa07d2c7b554a551eb0dc (diff) |
OMAP: add TI OMAP2 external LCD controller support - RFBI
- Adds support for Texas Instruments OMAP2 processors boards connected with
external LCD controller through "Remote framebuffer Interface"
Signed-off-by: Trilok Soni <soni.trilok@gmail.com>
Cc: Tony Lindgren <tony@atomide.com>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/video/omap')
-rw-r--r-- | drivers/video/omap/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/omap/rfbi.c | 588 |
2 files changed, 589 insertions, 0 deletions
diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile index 5289bf91c2c9..1e8dd3110c8d 100644 --- a/drivers/video/omap/Makefile +++ b/drivers/video/omap/Makefile | |||
@@ -10,6 +10,7 @@ objs-y$(CONFIG_ARCH_OMAP1) += lcdc.o | |||
10 | objs-y$(CONFIG_ARCH_OMAP2) += dispc.o | 10 | objs-y$(CONFIG_ARCH_OMAP2) += dispc.o |
11 | 11 | ||
12 | objs-$(CONFIG_ARCH_OMAP1)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += sossi.o | 12 | objs-$(CONFIG_ARCH_OMAP1)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += sossi.o |
13 | objs-$(CONFIG_ARCH_OMAP2)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += rfbi.o | ||
13 | 14 | ||
14 | omapfb-objs := $(objs-yy) | 15 | omapfb-objs := $(objs-yy) |
15 | 16 | ||
diff --git a/drivers/video/omap/rfbi.c b/drivers/video/omap/rfbi.c new file mode 100644 index 000000000000..2b4269813b22 --- /dev/null +++ b/drivers/video/omap/rfbi.c | |||
@@ -0,0 +1,588 @@ | |||
1 | /* | ||
2 | * OMAP2 Remote Frame Buffer Interface support | ||
3 | * | ||
4 | * Copyright (C) 2005 Nokia Corporation | ||
5 | * Author: Juha Yrjölä <juha.yrjola@nokia.com> | ||
6 | * Imre Deak <imre.deak@nokia.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but | ||
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
16 | * General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License along | ||
19 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
20 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
21 | */ | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/i2c.h> | ||
25 | #include <linux/err.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/clk.h> | ||
28 | #include <linux/io.h> | ||
29 | |||
30 | #include <asm/arch/omapfb.h> | ||
31 | |||
32 | #include "dispc.h" | ||
33 | |||
34 | /* To work around an RFBI transfer rate limitation */ | ||
35 | #define OMAP_RFBI_RATE_LIMIT 1 | ||
36 | |||
37 | #define RFBI_BASE 0x48050800 | ||
38 | #define RFBI_REVISION 0x0000 | ||
39 | #define RFBI_SYSCONFIG 0x0010 | ||
40 | #define RFBI_SYSSTATUS 0x0014 | ||
41 | #define RFBI_CONTROL 0x0040 | ||
42 | #define RFBI_PIXEL_CNT 0x0044 | ||
43 | #define RFBI_LINE_NUMBER 0x0048 | ||
44 | #define RFBI_CMD 0x004c | ||
45 | #define RFBI_PARAM 0x0050 | ||
46 | #define RFBI_DATA 0x0054 | ||
47 | #define RFBI_READ 0x0058 | ||
48 | #define RFBI_STATUS 0x005c | ||
49 | #define RFBI_CONFIG0 0x0060 | ||
50 | #define RFBI_ONOFF_TIME0 0x0064 | ||
51 | #define RFBI_CYCLE_TIME0 0x0068 | ||
52 | #define RFBI_DATA_CYCLE1_0 0x006c | ||
53 | #define RFBI_DATA_CYCLE2_0 0x0070 | ||
54 | #define RFBI_DATA_CYCLE3_0 0x0074 | ||
55 | #define RFBI_VSYNC_WIDTH 0x0090 | ||
56 | #define RFBI_HSYNC_WIDTH 0x0094 | ||
57 | |||
58 | #define DISPC_BASE 0x48050400 | ||
59 | #define DISPC_CONTROL 0x0040 | ||
60 | |||
61 | static struct { | ||
62 | u32 base; | ||
63 | void (*lcdc_callback)(void *data); | ||
64 | void *lcdc_callback_data; | ||
65 | unsigned long l4_khz; | ||
66 | int bits_per_cycle; | ||
67 | struct omapfb_device *fbdev; | ||
68 | struct clk *dss_ick; | ||
69 | struct clk *dss1_fck; | ||
70 | unsigned tearsync_pin_cnt; | ||
71 | unsigned tearsync_mode; | ||
72 | } rfbi; | ||
73 | |||
74 | static inline void rfbi_write_reg(int idx, u32 val) | ||
75 | { | ||
76 | __raw_writel(val, rfbi.base + idx); | ||
77 | } | ||
78 | |||
79 | static inline u32 rfbi_read_reg(int idx) | ||
80 | { | ||
81 | return __raw_readl(rfbi.base + idx); | ||
82 | } | ||
83 | |||
84 | static int rfbi_get_clocks(void) | ||
85 | { | ||
86 | if (IS_ERR((rfbi.dss_ick = clk_get(rfbi.fbdev->dev, "dss_ick")))) { | ||
87 | dev_err(rfbi.fbdev->dev, "can't get dss_ick"); | ||
88 | return PTR_ERR(rfbi.dss_ick); | ||
89 | } | ||
90 | |||
91 | if (IS_ERR((rfbi.dss1_fck = clk_get(rfbi.fbdev->dev, "dss1_fck")))) { | ||
92 | dev_err(rfbi.fbdev->dev, "can't get dss1_fck"); | ||
93 | clk_put(rfbi.dss_ick); | ||
94 | return PTR_ERR(rfbi.dss1_fck); | ||
95 | } | ||
96 | |||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | static void rfbi_put_clocks(void) | ||
101 | { | ||
102 | clk_put(rfbi.dss1_fck); | ||
103 | clk_put(rfbi.dss_ick); | ||
104 | } | ||
105 | |||
106 | static void rfbi_enable_clocks(int enable) | ||
107 | { | ||
108 | if (enable) { | ||
109 | clk_enable(rfbi.dss_ick); | ||
110 | clk_enable(rfbi.dss1_fck); | ||
111 | } else { | ||
112 | clk_disable(rfbi.dss1_fck); | ||
113 | clk_disable(rfbi.dss_ick); | ||
114 | } | ||
115 | } | ||
116 | |||
117 | |||
118 | #ifdef VERBOSE | ||
119 | static void rfbi_print_timings(void) | ||
120 | { | ||
121 | u32 l; | ||
122 | u32 time; | ||
123 | |||
124 | l = rfbi_read_reg(RFBI_CONFIG0); | ||
125 | time = 1000000000 / rfbi.l4_khz; | ||
126 | if (l & (1 << 4)) | ||
127 | time *= 2; | ||
128 | |||
129 | dev_dbg(rfbi.fbdev->dev, "Tick time %u ps\n", time); | ||
130 | l = rfbi_read_reg(RFBI_ONOFF_TIME0); | ||
131 | dev_dbg(rfbi.fbdev->dev, | ||
132 | "CSONTIME %d, CSOFFTIME %d, WEONTIME %d, WEOFFTIME %d, " | ||
133 | "REONTIME %d, REOFFTIME %d\n", | ||
134 | l & 0x0f, (l >> 4) & 0x3f, (l >> 10) & 0x0f, (l >> 14) & 0x3f, | ||
135 | (l >> 20) & 0x0f, (l >> 24) & 0x3f); | ||
136 | |||
137 | l = rfbi_read_reg(RFBI_CYCLE_TIME0); | ||
138 | dev_dbg(rfbi.fbdev->dev, | ||
139 | "WECYCLETIME %d, RECYCLETIME %d, CSPULSEWIDTH %d, " | ||
140 | "ACCESSTIME %d\n", | ||
141 | (l & 0x3f), (l >> 6) & 0x3f, (l >> 12) & 0x3f, | ||
142 | (l >> 22) & 0x3f); | ||
143 | } | ||
144 | #else | ||
145 | static void rfbi_print_timings(void) {} | ||
146 | #endif | ||
147 | |||
148 | static void rfbi_set_timings(const struct extif_timings *t) | ||
149 | { | ||
150 | u32 l; | ||
151 | |||
152 | BUG_ON(!t->converted); | ||
153 | |||
154 | rfbi_enable_clocks(1); | ||
155 | rfbi_write_reg(RFBI_ONOFF_TIME0, t->tim[0]); | ||
156 | rfbi_write_reg(RFBI_CYCLE_TIME0, t->tim[1]); | ||
157 | |||
158 | l = rfbi_read_reg(RFBI_CONFIG0); | ||
159 | l &= ~(1 << 4); | ||
160 | l |= (t->tim[2] ? 1 : 0) << 4; | ||
161 | rfbi_write_reg(RFBI_CONFIG0, l); | ||
162 | |||
163 | rfbi_print_timings(); | ||
164 | rfbi_enable_clocks(0); | ||
165 | } | ||
166 | |||
167 | static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div) | ||
168 | { | ||
169 | *clk_period = 1000000000 / rfbi.l4_khz; | ||
170 | *max_clk_div = 2; | ||
171 | } | ||
172 | |||
173 | static int ps_to_rfbi_ticks(int time, int div) | ||
174 | { | ||
175 | unsigned long tick_ps; | ||
176 | int ret; | ||
177 | |||
178 | /* Calculate in picosecs to yield more exact results */ | ||
179 | tick_ps = 1000000000 / (rfbi.l4_khz) * div; | ||
180 | |||
181 | ret = (time + tick_ps - 1) / tick_ps; | ||
182 | |||
183 | return ret; | ||
184 | } | ||
185 | |||
186 | #ifdef OMAP_RFBI_RATE_LIMIT | ||
187 | static unsigned long rfbi_get_max_tx_rate(void) | ||
188 | { | ||
189 | unsigned long l4_rate, dss1_rate; | ||
190 | int min_l4_ticks = 0; | ||
191 | int i; | ||
192 | |||
193 | /* According to TI this can't be calculated so make the | ||
194 | * adjustments for a couple of known frequencies and warn for | ||
195 | * others. | ||
196 | */ | ||
197 | static const struct { | ||
198 | unsigned long l4_clk; /* HZ */ | ||
199 | unsigned long dss1_clk; /* HZ */ | ||
200 | unsigned long min_l4_ticks; | ||
201 | } ftab[] = { | ||
202 | { 55, 132, 7, }, /* 7.86 MPix/s */ | ||
203 | { 110, 110, 12, }, /* 9.16 MPix/s */ | ||
204 | { 110, 132, 10, }, /* 11 Mpix/s */ | ||
205 | { 120, 120, 10, }, /* 12 Mpix/s */ | ||
206 | { 133, 133, 10, }, /* 13.3 Mpix/s */ | ||
207 | }; | ||
208 | |||
209 | l4_rate = rfbi.l4_khz / 1000; | ||
210 | dss1_rate = clk_get_rate(rfbi.dss1_fck) / 1000000; | ||
211 | |||
212 | for (i = 0; i < ARRAY_SIZE(ftab); i++) { | ||
213 | /* Use a window instead of an exact match, to account | ||
214 | * for different DPLL multiplier / divider pairs. | ||
215 | */ | ||
216 | if (abs(ftab[i].l4_clk - l4_rate) < 3 && | ||
217 | abs(ftab[i].dss1_clk - dss1_rate) < 3) { | ||
218 | min_l4_ticks = ftab[i].min_l4_ticks; | ||
219 | break; | ||
220 | } | ||
221 | } | ||
222 | if (i == ARRAY_SIZE(ftab)) { | ||
223 | /* Can't be sure, return anyway the maximum not | ||
224 | * rate-limited. This might cause a problem only for the | ||
225 | * tearing synchronisation. | ||
226 | */ | ||
227 | dev_err(rfbi.fbdev->dev, | ||
228 | "can't determine maximum RFBI transfer rate\n"); | ||
229 | return rfbi.l4_khz * 1000; | ||
230 | } | ||
231 | return rfbi.l4_khz * 1000 / min_l4_ticks; | ||
232 | } | ||
233 | #else | ||
234 | static int rfbi_get_max_tx_rate(void) | ||
235 | { | ||
236 | return rfbi.l4_khz * 1000; | ||
237 | } | ||
238 | #endif | ||
239 | |||
240 | |||
241 | static int rfbi_convert_timings(struct extif_timings *t) | ||
242 | { | ||
243 | u32 l; | ||
244 | int reon, reoff, weon, weoff, cson, csoff, cs_pulse; | ||
245 | int actim, recyc, wecyc; | ||
246 | int div = t->clk_div; | ||
247 | |||
248 | if (div <= 0 || div > 2) | ||
249 | return -1; | ||
250 | |||
251 | /* Make sure that after conversion it still holds that: | ||
252 | * weoff > weon, reoff > reon, recyc >= reoff, wecyc >= weoff, | ||
253 | * csoff > cson, csoff >= max(weoff, reoff), actim > reon | ||
254 | */ | ||
255 | weon = ps_to_rfbi_ticks(t->we_on_time, div); | ||
256 | weoff = ps_to_rfbi_ticks(t->we_off_time, div); | ||
257 | if (weoff <= weon) | ||
258 | weoff = weon + 1; | ||
259 | if (weon > 0x0f) | ||
260 | return -1; | ||
261 | if (weoff > 0x3f) | ||
262 | return -1; | ||
263 | |||
264 | reon = ps_to_rfbi_ticks(t->re_on_time, div); | ||
265 | reoff = ps_to_rfbi_ticks(t->re_off_time, div); | ||
266 | if (reoff <= reon) | ||
267 | reoff = reon + 1; | ||
268 | if (reon > 0x0f) | ||
269 | return -1; | ||
270 | if (reoff > 0x3f) | ||
271 | return -1; | ||
272 | |||
273 | cson = ps_to_rfbi_ticks(t->cs_on_time, div); | ||
274 | csoff = ps_to_rfbi_ticks(t->cs_off_time, div); | ||
275 | if (csoff <= cson) | ||
276 | csoff = cson + 1; | ||
277 | if (csoff < max(weoff, reoff)) | ||
278 | csoff = max(weoff, reoff); | ||
279 | if (cson > 0x0f) | ||
280 | return -1; | ||
281 | if (csoff > 0x3f) | ||
282 | return -1; | ||
283 | |||
284 | l = cson; | ||
285 | l |= csoff << 4; | ||
286 | l |= weon << 10; | ||
287 | l |= weoff << 14; | ||
288 | l |= reon << 20; | ||
289 | l |= reoff << 24; | ||
290 | |||
291 | t->tim[0] = l; | ||
292 | |||
293 | actim = ps_to_rfbi_ticks(t->access_time, div); | ||
294 | if (actim <= reon) | ||
295 | actim = reon + 1; | ||
296 | if (actim > 0x3f) | ||
297 | return -1; | ||
298 | |||
299 | wecyc = ps_to_rfbi_ticks(t->we_cycle_time, div); | ||
300 | if (wecyc < weoff) | ||
301 | wecyc = weoff; | ||
302 | if (wecyc > 0x3f) | ||
303 | return -1; | ||
304 | |||
305 | recyc = ps_to_rfbi_ticks(t->re_cycle_time, div); | ||
306 | if (recyc < reoff) | ||
307 | recyc = reoff; | ||
308 | if (recyc > 0x3f) | ||
309 | return -1; | ||
310 | |||
311 | cs_pulse = ps_to_rfbi_ticks(t->cs_pulse_width, div); | ||
312 | if (cs_pulse > 0x3f) | ||
313 | return -1; | ||
314 | |||
315 | l = wecyc; | ||
316 | l |= recyc << 6; | ||
317 | l |= cs_pulse << 12; | ||
318 | l |= actim << 22; | ||
319 | |||
320 | t->tim[1] = l; | ||
321 | |||
322 | t->tim[2] = div - 1; | ||
323 | |||
324 | t->converted = 1; | ||
325 | |||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | static int rfbi_setup_tearsync(unsigned pin_cnt, | ||
330 | unsigned hs_pulse_time, unsigned vs_pulse_time, | ||
331 | int hs_pol_inv, int vs_pol_inv, int extif_div) | ||
332 | { | ||
333 | int hs, vs; | ||
334 | int min; | ||
335 | u32 l; | ||
336 | |||
337 | if (pin_cnt != 1 && pin_cnt != 2) | ||
338 | return -EINVAL; | ||
339 | |||
340 | hs = ps_to_rfbi_ticks(hs_pulse_time, 1); | ||
341 | vs = ps_to_rfbi_ticks(vs_pulse_time, 1); | ||
342 | if (hs < 2) | ||
343 | return -EDOM; | ||
344 | if (pin_cnt == 2) | ||
345 | min = 2; | ||
346 | else | ||
347 | min = 4; | ||
348 | if (vs < min) | ||
349 | return -EDOM; | ||
350 | if (vs == hs) | ||
351 | return -EINVAL; | ||
352 | rfbi.tearsync_pin_cnt = pin_cnt; | ||
353 | dev_dbg(rfbi.fbdev->dev, | ||
354 | "setup_tearsync: pins %d hs %d vs %d hs_inv %d vs_inv %d\n", | ||
355 | pin_cnt, hs, vs, hs_pol_inv, vs_pol_inv); | ||
356 | |||
357 | rfbi_enable_clocks(1); | ||
358 | rfbi_write_reg(RFBI_HSYNC_WIDTH, hs); | ||
359 | rfbi_write_reg(RFBI_VSYNC_WIDTH, vs); | ||
360 | |||
361 | l = rfbi_read_reg(RFBI_CONFIG0); | ||
362 | if (hs_pol_inv) | ||
363 | l &= ~(1 << 21); | ||
364 | else | ||
365 | l |= 1 << 21; | ||
366 | if (vs_pol_inv) | ||
367 | l &= ~(1 << 20); | ||
368 | else | ||
369 | l |= 1 << 20; | ||
370 | rfbi_enable_clocks(0); | ||
371 | |||
372 | return 0; | ||
373 | } | ||
374 | |||
375 | static int rfbi_enable_tearsync(int enable, unsigned line) | ||
376 | { | ||
377 | u32 l; | ||
378 | |||
379 | dev_dbg(rfbi.fbdev->dev, "tearsync %d line %d mode %d\n", | ||
380 | enable, line, rfbi.tearsync_mode); | ||
381 | if (line > (1 << 11) - 1) | ||
382 | return -EINVAL; | ||
383 | |||
384 | rfbi_enable_clocks(1); | ||
385 | l = rfbi_read_reg(RFBI_CONFIG0); | ||
386 | l &= ~(0x3 << 2); | ||
387 | if (enable) { | ||
388 | rfbi.tearsync_mode = rfbi.tearsync_pin_cnt; | ||
389 | l |= rfbi.tearsync_mode << 2; | ||
390 | } else | ||
391 | rfbi.tearsync_mode = 0; | ||
392 | rfbi_write_reg(RFBI_CONFIG0, l); | ||
393 | rfbi_write_reg(RFBI_LINE_NUMBER, line); | ||
394 | rfbi_enable_clocks(0); | ||
395 | |||
396 | return 0; | ||
397 | } | ||
398 | |||
399 | static void rfbi_write_command(const void *buf, unsigned int len) | ||
400 | { | ||
401 | rfbi_enable_clocks(1); | ||
402 | if (rfbi.bits_per_cycle == 16) { | ||
403 | const u16 *w = buf; | ||
404 | BUG_ON(len & 1); | ||
405 | for (; len; len -= 2) | ||
406 | rfbi_write_reg(RFBI_CMD, *w++); | ||
407 | } else { | ||
408 | const u8 *b = buf; | ||
409 | BUG_ON(rfbi.bits_per_cycle != 8); | ||
410 | for (; len; len--) | ||
411 | rfbi_write_reg(RFBI_CMD, *b++); | ||
412 | } | ||
413 | rfbi_enable_clocks(0); | ||
414 | } | ||
415 | |||
416 | static void rfbi_read_data(void *buf, unsigned int len) | ||
417 | { | ||
418 | rfbi_enable_clocks(1); | ||
419 | if (rfbi.bits_per_cycle == 16) { | ||
420 | u16 *w = buf; | ||
421 | BUG_ON(len & ~1); | ||
422 | for (; len; len -= 2) { | ||
423 | rfbi_write_reg(RFBI_READ, 0); | ||
424 | *w++ = rfbi_read_reg(RFBI_READ); | ||
425 | } | ||
426 | } else { | ||
427 | u8 *b = buf; | ||
428 | BUG_ON(rfbi.bits_per_cycle != 8); | ||
429 | for (; len; len--) { | ||
430 | rfbi_write_reg(RFBI_READ, 0); | ||
431 | *b++ = rfbi_read_reg(RFBI_READ); | ||
432 | } | ||
433 | } | ||
434 | rfbi_enable_clocks(0); | ||
435 | } | ||
436 | |||
437 | static void rfbi_write_data(const void *buf, unsigned int len) | ||
438 | { | ||
439 | rfbi_enable_clocks(1); | ||
440 | if (rfbi.bits_per_cycle == 16) { | ||
441 | const u16 *w = buf; | ||
442 | BUG_ON(len & 1); | ||
443 | for (; len; len -= 2) | ||
444 | rfbi_write_reg(RFBI_PARAM, *w++); | ||
445 | } else { | ||
446 | const u8 *b = buf; | ||
447 | BUG_ON(rfbi.bits_per_cycle != 8); | ||
448 | for (; len; len--) | ||
449 | rfbi_write_reg(RFBI_PARAM, *b++); | ||
450 | } | ||
451 | rfbi_enable_clocks(0); | ||
452 | } | ||
453 | |||
454 | static void rfbi_transfer_area(int width, int height, | ||
455 | void (callback)(void * data), void *data) | ||
456 | { | ||
457 | u32 w; | ||
458 | |||
459 | BUG_ON(callback == NULL); | ||
460 | |||
461 | rfbi_enable_clocks(1); | ||
462 | omap_dispc_set_lcd_size(width, height); | ||
463 | |||
464 | rfbi.lcdc_callback = callback; | ||
465 | rfbi.lcdc_callback_data = data; | ||
466 | |||
467 | rfbi_write_reg(RFBI_PIXEL_CNT, width * height); | ||
468 | |||
469 | w = rfbi_read_reg(RFBI_CONTROL); | ||
470 | w |= 1; /* enable */ | ||
471 | if (!rfbi.tearsync_mode) | ||
472 | w |= 1 << 4; /* internal trigger, reset by HW */ | ||
473 | rfbi_write_reg(RFBI_CONTROL, w); | ||
474 | |||
475 | omap_dispc_enable_lcd_out(1); | ||
476 | } | ||
477 | |||
478 | static inline void _stop_transfer(void) | ||
479 | { | ||
480 | u32 w; | ||
481 | |||
482 | w = rfbi_read_reg(RFBI_CONTROL); | ||
483 | rfbi_write_reg(RFBI_CONTROL, w & ~(1 << 0)); | ||
484 | rfbi_enable_clocks(0); | ||
485 | } | ||
486 | |||
487 | static void rfbi_dma_callback(void *data) | ||
488 | { | ||
489 | _stop_transfer(); | ||
490 | rfbi.lcdc_callback(rfbi.lcdc_callback_data); | ||
491 | } | ||
492 | |||
493 | static void rfbi_set_bits_per_cycle(int bpc) | ||
494 | { | ||
495 | u32 l; | ||
496 | |||
497 | rfbi_enable_clocks(1); | ||
498 | l = rfbi_read_reg(RFBI_CONFIG0); | ||
499 | l &= ~(0x03 << 0); | ||
500 | |||
501 | switch (bpc) { | ||
502 | case 8: | ||
503 | break; | ||
504 | case 16: | ||
505 | l |= 3; | ||
506 | break; | ||
507 | default: | ||
508 | BUG(); | ||
509 | } | ||
510 | rfbi_write_reg(RFBI_CONFIG0, l); | ||
511 | rfbi.bits_per_cycle = bpc; | ||
512 | rfbi_enable_clocks(0); | ||
513 | } | ||
514 | |||
515 | static int rfbi_init(struct omapfb_device *fbdev) | ||
516 | { | ||
517 | u32 l; | ||
518 | int r; | ||
519 | |||
520 | rfbi.fbdev = fbdev; | ||
521 | rfbi.base = io_p2v(RFBI_BASE); | ||
522 | |||
523 | if ((r = rfbi_get_clocks()) < 0) | ||
524 | return r; | ||
525 | rfbi_enable_clocks(1); | ||
526 | |||
527 | rfbi.l4_khz = clk_get_rate(rfbi.dss_ick) / 1000; | ||
528 | |||
529 | /* Reset */ | ||
530 | rfbi_write_reg(RFBI_SYSCONFIG, 1 << 1); | ||
531 | while (!(rfbi_read_reg(RFBI_SYSSTATUS) & (1 << 0))); | ||
532 | |||
533 | l = rfbi_read_reg(RFBI_SYSCONFIG); | ||
534 | /* Enable autoidle and smart-idle */ | ||
535 | l |= (1 << 0) | (2 << 3); | ||
536 | rfbi_write_reg(RFBI_SYSCONFIG, l); | ||
537 | |||
538 | /* 16-bit interface, ITE trigger mode, 16-bit data */ | ||
539 | l = (0x03 << 0) | (0x00 << 2) | (0x01 << 5) | (0x02 << 7); | ||
540 | l |= (0 << 9) | (1 << 20) | (1 << 21); | ||
541 | rfbi_write_reg(RFBI_CONFIG0, l); | ||
542 | |||
543 | rfbi_write_reg(RFBI_DATA_CYCLE1_0, 0x00000010); | ||
544 | |||
545 | l = rfbi_read_reg(RFBI_CONTROL); | ||
546 | /* Select CS0, clear bypass mode */ | ||
547 | l = (0x01 << 2); | ||
548 | rfbi_write_reg(RFBI_CONTROL, l); | ||
549 | |||
550 | if ((r = omap_dispc_request_irq(rfbi_dma_callback, NULL)) < 0) { | ||
551 | dev_err(fbdev->dev, "can't get DISPC irq\n"); | ||
552 | rfbi_enable_clocks(0); | ||
553 | return r; | ||
554 | } | ||
555 | |||
556 | l = rfbi_read_reg(RFBI_REVISION); | ||
557 | pr_info("omapfb: RFBI version %d.%d initialized\n", | ||
558 | (l >> 4) & 0x0f, l & 0x0f); | ||
559 | |||
560 | rfbi_enable_clocks(0); | ||
561 | |||
562 | return 0; | ||
563 | } | ||
564 | |||
565 | static void rfbi_cleanup(void) | ||
566 | { | ||
567 | omap_dispc_free_irq(); | ||
568 | rfbi_put_clocks(); | ||
569 | } | ||
570 | |||
571 | const struct lcd_ctrl_extif omap2_ext_if = { | ||
572 | .init = rfbi_init, | ||
573 | .cleanup = rfbi_cleanup, | ||
574 | .get_clk_info = rfbi_get_clk_info, | ||
575 | .get_max_tx_rate = rfbi_get_max_tx_rate, | ||
576 | .set_bits_per_cycle = rfbi_set_bits_per_cycle, | ||
577 | .convert_timings = rfbi_convert_timings, | ||
578 | .set_timings = rfbi_set_timings, | ||
579 | .write_command = rfbi_write_command, | ||
580 | .read_data = rfbi_read_data, | ||
581 | .write_data = rfbi_write_data, | ||
582 | .transfer_area = rfbi_transfer_area, | ||
583 | .setup_tearsync = rfbi_setup_tearsync, | ||
584 | .enable_tearsync = rfbi_enable_tearsync, | ||
585 | |||
586 | .max_transmit_size = (u32) ~0, | ||
587 | }; | ||
588 | |||