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