]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/video/omap/sossi.c
ba26512a504af6e3f2da4fcef63b602c1c26bfb7
[linux-2.6-omap-h63xx.git] / drivers / video / omap / sossi.c
1 /*
2  * File: drivers/video/omap/omap1/sossi.c
3  *
4  * OMAP1 Special OptimiSed Screen Interface support
5  *
6  * Copyright (C) 2004-2005 Nokia Corporation
7  * Author: Juha Yrjölä <juha.yrjola@nokia.com>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU General Public License as published by the
11  * Free Software Foundation; either version 2 of the License, or (at your
12  * option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  */
23
24 #include <linux/config.h>
25 #include <linux/module.h>
26 #include <linux/mm.h>
27 #include <linux/clk.h>
28
29 #include <asm/io.h>
30
31 #include "sossi.h"
32
33 #define MODULE_NAME             "omapfb-sossi"
34
35 #define OMAP_SOSSI_BASE         0xfffbac00
36 #define SOSSI_ID_REG            0x00
37 #define SOSSI_INIT1_REG         0x04
38 #define SOSSI_INIT2_REG         0x08
39 #define SOSSI_INIT3_REG         0x0c
40 #define SOSSI_FIFO_REG          0x10
41 #define SOSSI_REOTABLE_REG      0x14
42 #define SOSSI_TEARING_REG       0x18
43 #define SOSSI_INIT1B_REG        0x1c
44 #define SOSSI_FIFOB_REG         0x20
45
46 #define DMA_GSCR          0xfffedc04
47 #define DMA_LCD_CCR       0xfffee3c2
48 #define DMA_LCD_CTRL      0xfffee3c4
49 #define DMA_LCD_LCH_CTRL  0xfffee3ea
50
51 #define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args)
52
53 static int sossi_base = IO_ADDRESS(OMAP_SOSSI_BASE);
54
55 static inline u32 sossi_read_reg(int reg)
56 {
57         return readl(sossi_base + reg);
58 }
59
60 static inline u16 sossi_read_reg16(int reg)
61 {
62         return readw(sossi_base + reg);
63 }
64
65 static inline u8 sossi_read_reg8(int reg)
66 {
67         return readb(sossi_base + reg);
68 }
69
70 static inline void sossi_write_reg(int reg, u32 value)
71 {
72         writel(value, sossi_base + reg);
73 }
74
75 static inline void sossi_write_reg16(int reg, u16 value)
76 {
77         writew(value, sossi_base + reg);
78 }
79
80 static inline void sossi_write_reg8(int reg, u8 value)
81 {
82         writeb(value, sossi_base + reg);
83 }
84
85 static void sossi_set_bits(int reg, u32 bits)
86 {
87         sossi_write_reg(reg, sossi_read_reg(reg) | bits);
88 }
89
90 static void sossi_clear_bits(int reg, u32 bits)
91 {
92         sossi_write_reg(reg, sossi_read_reg(reg) & ~bits);
93 }
94
95 #define MOD_CONF_CTRL_1   0xfffe1110
96 #define CONF_SOSSI_RESET_R      (1 << 23)
97 #define CONF_MOD_SOSSI_CLK_EN_R (1 << 16)
98
99 static struct clk *dpll_clk;
100
101 int sossi_init(void)
102 {
103         u32 l, k;
104
105         dpll_clk = clk_get(NULL, "ck_dpll1");
106         BUG_ON(dpll_clk == NULL);
107
108         /* Reset and enable the SoSSI module */
109         l = omap_readl(MOD_CONF_CTRL_1);
110         l |= CONF_SOSSI_RESET_R;
111         omap_writel(l, MOD_CONF_CTRL_1);
112         l &= ~CONF_SOSSI_RESET_R;
113         omap_writel(l, MOD_CONF_CTRL_1);
114
115         l |= CONF_MOD_SOSSI_CLK_EN_R;
116         omap_writel(l, MOD_CONF_CTRL_1);
117
118         omap_writel(omap_readl(ARM_IDLECT2) | (1 << 11), ARM_IDLECT2);
119         omap_writel(omap_readl(ARM_IDLECT1) | (1 << 6), ARM_IDLECT1);
120
121         l = sossi_read_reg(SOSSI_INIT2_REG);
122         /* Enable and reset the SoSSI block */
123         l |= (1 << 0) | (1 << 1);
124         sossi_write_reg(SOSSI_INIT2_REG, l);
125         /* Take SoSSI out of reset */
126         l &= ~(1 << 1);
127         sossi_write_reg(SOSSI_INIT2_REG, l);
128
129         sossi_write_reg(SOSSI_ID_REG, 0);
130         l = sossi_read_reg(SOSSI_ID_REG);
131         k = sossi_read_reg(SOSSI_ID_REG);
132
133         if (l != 0x55555555 || k != 0xaaaaaaaa) {
134                 pr_err("Invalid SoSSI sync pattern: %08x, %08x\n", l, k);
135                 return -ENODEV;
136         }
137         l = sossi_read_reg(SOSSI_ID_REG); /* Component code */
138         l = sossi_read_reg(SOSSI_ID_REG);
139         pr_info(KERN_INFO MODULE_NAME ": version %d.%d initialized\n",
140                         l >> 16, l & 0xffff);
141
142         l = sossi_read_reg(SOSSI_INIT1_REG);
143         l |= (1 << 19); /* DMA_MODE */
144         l &= ~(1 << 31); /* REORDERING */
145         sossi_write_reg(SOSSI_INIT1_REG, l);
146
147         return 0;
148 }
149
150 static unsigned long get_sossi_clk_rate(int div)
151 {
152         return (clk_get_rate(dpll_clk)) / div;
153 }
154
155 static unsigned long get_sossi_clk_period(int div)
156 {
157         /* In picoseconds */
158         return 1000000000 / (get_sossi_clk_rate(div) / 1000);
159 }
160
161 static int ns_to_sossi_ticks(int time, int div)
162 {
163         unsigned long tick_ps;
164
165         /* Calculate in picosecs to yield more exact results */
166         tick_ps = get_sossi_clk_period(div);
167
168         return (time * 1000 + tick_ps - 1) / tick_ps;
169 }
170
171 static int set_timings(int div, int tw0, int tw1)
172 {
173         u32 l;
174
175         if (tw1 * 1000 > 64 * get_sossi_clk_period(div))
176                 return -1;
177         if (tw0 * 1000 > 16 * get_sossi_clk_period(div))
178                 return -1;
179
180         l = omap_readl(MOD_CONF_CTRL_1);
181         l &= ~(7 << 17);
182         l |= (div - 1) << 17;
183         omap_writel(l, MOD_CONF_CTRL_1);
184
185         tw0 = ns_to_sossi_ticks(tw0, div) - 1;
186         tw1 = ns_to_sossi_ticks(tw1, div) - 1;
187         if (tw0 < 0)
188                 tw0 = 0;
189         if (tw1 < 0)
190                 tw1 = 0;
191 #if 0
192         printk("Using TW0 = %d, TW1 = %d, div = %d, period = %d ps\n",
193                tw0, tw1, div, get_sossi_clk_period(div));
194 #endif
195         l = sossi_read_reg(SOSSI_INIT1_REG);
196         l &= ~((0x0f << 20) | (0x3f << 24));
197         l |= ((tw0 & 0x0f) << 20) | ((tw1 & 0x3f) << 24);
198         sossi_write_reg(SOSSI_INIT1_REG, l);
199
200         return 0;
201 }
202
203 static struct sossi {
204         int bus_pick_width;
205 } sossi;
206
207 void sossi_set_timings(int min_time, int min_tw0, int min_tw1)
208 {
209         int div;
210
211         for (div = 1; div <= 8; div++) {
212                 if (min_time * 1000 > get_sossi_clk_period(div))
213                         continue;
214                 if (set_timings(div, min_tw0, min_tw1) == 0)
215                         break;
216         }
217         if (div == 9) {
218                 pr_err("DPLL frequency too high for SoSSI\n");
219                 BUG();
220         }
221 }
222
223 void sossi_set_xfer_params(int bus_pick_count, int bus_pick_width)
224 {
225         u32 l;
226
227         sossi.bus_pick_width = bus_pick_width;
228         l = ((bus_pick_count - 1) << 5) | ((bus_pick_width - 1) & 0x1f);
229         sossi_write_reg(SOSSI_INIT3_REG, l);
230 }
231
232 void sossi_start_transfer(void)
233 {
234         /* WE */
235         sossi_clear_bits(SOSSI_INIT2_REG, 1 << 4);
236         /* CS active low */
237         sossi_clear_bits(SOSSI_INIT1_REG, 1 << 30);
238         /* FIXME: locking? */
239 }
240
241 void sossi_stop_transfer(void)
242 {
243         /* WE */
244         sossi_set_bits(SOSSI_INIT2_REG, 1 << 4);
245         /* CS active low */
246         sossi_set_bits(SOSSI_INIT1_REG, 1 << 30);
247         /* FIXME: locking? */
248 }
249
250 static void send_data(const void *data, unsigned int len)
251 {
252         while (len >= 4) {
253                 sossi_write_reg(SOSSI_FIFO_REG, *(const u32 *) data);
254                 len -= 4;
255                 data += 4;
256         }
257         while (len >= 2) {
258                 sossi_write_reg16(SOSSI_FIFO_REG, *(const u16 *) data);
259                 len -= 2;
260                 data += 2;
261         }
262         while (len) {
263                 sossi_write_reg8(SOSSI_FIFO_REG, *(const u8 *) data);
264                 len--;
265                 data++;
266         }
267 }
268
269 static void set_cycles(unsigned int len)
270 {
271         int nr_cycles = len / (sossi.bus_pick_width / 8);
272
273         sossi_clear_bits(SOSSI_INIT1_REG, 0x3ffff);
274         sossi_set_bits(SOSSI_INIT1_REG, (nr_cycles - 1) & 0x3ffff);
275 }
276
277 void sossi_send_cmd(const void *data, unsigned int len)
278 {
279         sossi_clear_bits(SOSSI_INIT1_REG, 1 << 18);
280         set_cycles(len);
281         send_data(data, len);
282 }
283
284 void sossi_send_data(const void *data, unsigned int len)
285 {
286         sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
287         set_cycles(len);
288         send_data(data, len);
289 }
290
291 void sossi_prepare_dma_transfer(unsigned int count)
292 {
293         sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
294         set_cycles(count);
295 }
296
297 void sossi_send_data_const32(u32 data, unsigned int count)
298 {
299         sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
300         set_cycles(count * 4);
301         while (count > 0) {
302                 sossi_write_reg(SOSSI_FIFO_REG, data);
303                 count--;
304         }
305 }
306
307 void sossi_set_tearing(int mode, int hs_counter, int detect_limit,
308                        int vs_counter, int vs_detect_limit, int flags)
309 {
310         u32 l = 0;
311
312         l |= vs_counter << 30;
313         if (flags & SOSSI_FLAG_HS_INVERTED)
314                 l |= 1 << 29;
315         if (flags & SOSSI_FLAG_VS_INVERTED)
316                 l |= 1 << 28;
317         l |= mode << 26;
318         l |= hs_counter << 15;
319         l |= vs_detect_limit << 3;
320         l |= detect_limit;
321         sossi_write_reg(SOSSI_TEARING_REG, l);
322 }
323
324 void sossi_read_data(void *data, unsigned int len)
325 {
326         /* Before reading we must check if some writings are going on */
327         while (!(sossi_read_reg(SOSSI_INIT2_REG) & (1 << 3)));
328         sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
329         set_cycles(len);
330         while (len >= 4) {
331                 *(u32 *) data = sossi_read_reg(SOSSI_FIFO_REG);
332                 len -= 4;
333                 data += 4;
334         }
335         while (len >= 2) {
336                 *(u16 *) data = sossi_read_reg16(SOSSI_FIFO_REG);
337                 len -= 2;
338                 data += 2;
339         }
340         while (len) {
341                 *(u8 *) data = sossi_read_reg8(SOSSI_FIFO_REG);
342                 len--;
343                 data++;
344         }
345 }