]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/video/omap/sossi.c
Merge with mainline
[linux-2.6-omap-h63xx.git] / drivers / video / omap / sossi.c
1 /*
2  * File: drivers/video/omap_new/omapfb_main.c
3  *
4  * Special optimiSed Screen Interface driver for TI OMAP boards
5  *
6  * Copyright (C) 2004 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/io.h>
28
29 #include "sossi.h"
30
31 #define OMAP_SOSSI_BASE         0xfffbac00
32 #define SOSSI_ID_REG            0x00
33 #define SOSSI_INIT1_REG         0x04
34 #define SOSSI_INIT2_REG         0x08
35 #define SOSSI_INIT3_REG         0x0c
36 #define SOSSI_FIFO_REG          0x10
37 #define SOSSI_REOTABLE_REG      0x14
38 #define SOSSI_TEARING_REG       0x18
39 #define SOSSI_INIT1B_REG        0x1c
40 #define SOSSI_FIFOB_REG         0x20
41
42 #define DMA_GSCR          0xfffedc04
43 #define DMA_LCD_CCR       0xfffee3c2
44 #define DMA_LCD_CTRL      0xfffee3c4
45 #define DMA_LCD_LCH_CTRL  0xfffee3ea
46
47 static int sossi_base = IO_ADDRESS(OMAP_SOSSI_BASE);
48
49 static inline u32 sossi_read_reg(int reg)
50 {
51         return readl(sossi_base + reg);
52 }
53
54 static inline u16 sossi_read_reg16(int reg)
55 {
56         return readw(sossi_base + reg);
57 }
58
59 static inline u8 sossi_read_reg8(int reg)
60 {
61         return readb(sossi_base + reg);
62 }
63
64 static inline void sossi_write_reg(int reg, u32 value)
65 {
66         writel(value, sossi_base + reg);
67 }
68
69 static inline void sossi_write_reg16(int reg, u16 value)
70 {
71         writew(value, sossi_base + reg);
72 }
73
74 static inline void sossi_write_reg8(int reg, u8 value)
75 {
76         writeb(value, sossi_base + reg);
77 }
78
79 static void sossi_set_bits(int reg, u32 bits)
80 {
81         sossi_write_reg(reg, sossi_read_reg(reg) | bits);
82 }
83
84 static void sossi_clear_bits(int reg, u32 bits)
85 {
86         sossi_write_reg(reg, sossi_read_reg(reg) & ~bits);
87 }
88
89 #if 1
90 void sossi_dump(void)
91 {
92         printk("  INIT1:    0x%08x\n", sossi_read_reg(SOSSI_INIT1_REG));
93         printk("  INIT2:    0x%08x\n", sossi_read_reg(SOSSI_INIT2_REG));
94         printk("  INIT3:    0x%08x\n", sossi_read_reg(SOSSI_INIT3_REG));
95         printk("  TEARING:  0x%08x\n", sossi_read_reg(SOSSI_TEARING_REG));
96         printk("  INIT1B:   0x%08x\n", sossi_read_reg(SOSSI_INIT1B_REG));
97 }
98 #endif
99
100 static void sossi_dma_init(void)
101 {
102         /* OMAP3.1 mapping disable */
103         omap_writel(omap_readl(DMA_GSCR) | (1 << 3), DMA_GSCR);
104         /* Logical channel type to b0100 */
105         omap_writew(omap_readw(DMA_LCD_LCH_CTRL) | (1 << 2), DMA_LCD_LCH_CTRL);
106         /* LCD_DMA dest port to 1 */
107         omap_writew(omap_readw(DMA_LCD_CTRL) | (1 << 8), DMA_LCD_CTRL);
108         /* LCD_CCR OMAP31 comp mode */
109         omap_writew(omap_readw(DMA_LCD_CCR) | (1 << 10), DMA_LCD_CCR);
110 }
111
112 #define MOD_CONF_CTRL_1   0xfffe1110
113 #define CONF_SOSSI_RESET_R      (1 << 23)
114 #define CONF_MOD_SOSSI_CLK_EN_R (1 << 16)
115
116 int sossi_init(void)
117 {
118         u32 l, k;
119
120         /* Reset and enable the SoSSI module */
121         l = omap_readl(MOD_CONF_CTRL_1);
122         l |= CONF_SOSSI_RESET_R;
123         omap_writel(l, MOD_CONF_CTRL_1);
124         l &= ~CONF_SOSSI_RESET_R;
125         omap_writel(l, MOD_CONF_CTRL_1);
126
127         l |= CONF_MOD_SOSSI_CLK_EN_R;
128         /* FIXME: Hardcode divide ratio 3 */
129         l |= 2 << 17;
130         omap_writel(l, MOD_CONF_CTRL_1);
131
132         omap_writel(omap_readl(ARM_IDLECT2) | (1 << 11), ARM_IDLECT2);
133         omap_writel(omap_readl(ARM_IDLECT1) | (1 << 6), ARM_IDLECT1);
134
135         sossi_dma_init();
136
137         l = sossi_read_reg(SOSSI_INIT2_REG);
138         /* Enable and reset the SoSSI block */
139         l |= (1 << 0) | (1 << 1);
140         sossi_write_reg(SOSSI_INIT2_REG, l);
141         /* Take SoSSI out of reset */
142         l &= ~(1 << 1);
143         sossi_write_reg(SOSSI_INIT2_REG, l);
144
145         sossi_write_reg(SOSSI_ID_REG, 0);
146         l = sossi_read_reg(SOSSI_ID_REG);
147         k = sossi_read_reg(SOSSI_ID_REG);
148         
149         if (l != 0x55555555 || k != 0xaaaaaaaa) {
150                 printk(KERN_ERR "Invalid SoSSI sync pattern: %08x, %08x\n", l, k);
151                 return -ENODEV;
152         }
153         l = sossi_read_reg(SOSSI_ID_REG); /* Component code */
154         l = sossi_read_reg(SOSSI_ID_REG);
155         printk(KERN_INFO "SoSSI rev. %d.%d initialized\n", l >> 16, l & 0xffff);
156
157         l = sossi_read_reg(SOSSI_INIT1_REG);
158         l |= (1 << 19); /* DMA_MODE */
159         l &= ~(1 << 31); /* REORDERING */
160         sossi_write_reg(SOSSI_INIT1_REG, l);
161
162         return 0;
163 }
164
165 static void set_timings(int tw0, int tw1)
166 {
167         u32 l;
168
169         l = sossi_read_reg(SOSSI_INIT1_REG);
170         l &= ~((0x0f << 20) | (0x3f << 24));
171         l |= ((tw0 & 0x0f) << 20) | ((tw1 & 0x3f) << 24);
172         sossi_write_reg(SOSSI_INIT1_REG, l);
173 }
174
175 static struct sossi {
176         int bus_pick_width;
177 } sossi;
178
179 void sossi_set_xfer_params(int tw0, int tw1, int bus_pick_count, int bus_pick_width)
180 {
181         u32 l;
182
183         set_timings(tw0, tw1);  
184         sossi.bus_pick_width = bus_pick_width;
185         l = ((bus_pick_count - 1) << 5) | ((bus_pick_width - 1) & 0x1f);
186         sossi_write_reg(SOSSI_INIT3_REG, l);
187 }
188
189 void sossi_start_transfer(void)
190 {
191         /* WE */
192         sossi_clear_bits(SOSSI_INIT2_REG, 1 << 4);
193         /* CS active low */
194         sossi_clear_bits(SOSSI_INIT1_REG, 1 << 30);
195         /* FIXME: locking? */
196 }
197
198 void sossi_stop_transfer(void)
199 {
200         /* WE */
201         sossi_set_bits(SOSSI_INIT2_REG, 1 << 4);
202         /* CS active low */
203         sossi_set_bits(SOSSI_INIT1_REG, 1 << 30);
204         /* FIXME: locking? */
205 }
206
207 static void send_data(const void *data, unsigned int len)
208 {
209         while (len >= 4) {
210                 sossi_write_reg(SOSSI_FIFO_REG, *(const u32 *) data);
211                 len -= 4;
212                 data += 4;
213         }
214         while (len >= 2) {
215                 sossi_write_reg16(SOSSI_FIFO_REG, *(const u16 *) data);
216                 len -= 2;
217                 data += 2;
218         }
219         while (len) {
220                 sossi_write_reg8(SOSSI_FIFO_REG, *(const u8 *) data);
221                 len--;
222                 data++;
223         }
224 }
225
226 static void set_cycles(unsigned int len)
227 {
228         int nr_cycles = len / (sossi.bus_pick_width / 8);
229
230         sossi_clear_bits(SOSSI_INIT1_REG, 0x3ffff);
231         sossi_set_bits(SOSSI_INIT1_REG, (nr_cycles - 1) & 0x3ffff);
232 }
233
234 void sossi_send_cmd(const void *data, unsigned int len)
235 {
236         sossi_clear_bits(SOSSI_INIT1_REG, 1 << 18);
237         set_cycles(len);
238         send_data(data, len);
239 }
240
241 void sossi_send_data(const void *data, unsigned int len)
242 {
243         sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
244         set_cycles(len);
245         send_data(data, len);
246 }
247
248 void sossi_prepare_dma_transfer(unsigned int count)
249 {
250         sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
251         set_cycles(count);
252 }
253
254 void sossi_send_data_const32(u32 data, unsigned int count)
255 {
256         sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
257         set_cycles(count * 4);
258         while (count > 0) {
259                 sossi_write_reg(SOSSI_FIFO_REG, data);
260                 count--;
261         }
262 }
263
264 void sossi_set_tearing(int mode, int hs_counter, int detect_limit,
265                        int vs_counter, int vs_detect_limit, int flags)
266 {
267         u32 l = 0;
268
269         l |= vs_counter << 30;
270         if (flags & SOSSI_FLAG_HS_INVERTED)
271                 l |= 1 << 29;
272         if (flags & SOSSI_FLAG_VS_INVERTED)
273                 l |= 1 << 28;
274         l |= mode << 26;
275         l |= hs_counter << 15;
276         l |= vs_detect_limit << 3;
277         l |= detect_limit;
278         sossi_write_reg(SOSSI_TEARING_REG, l);
279 }
280
281 void sossi_read_data(void *data, unsigned int len)
282 {
283         /* Before reading we must check if some writings are going on */
284         while (!(sossi_read_reg(SOSSI_INIT2_REG) & (1 << 3)));
285         sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
286         set_cycles(len);
287         while (len >= 4) {
288                 *(u32 *) data = sossi_read_reg(SOSSI_FIFO_REG);
289                 len -= 4;
290                 data += 4;
291         }
292         while (len >= 2) {
293                 *(u16 *) data = sossi_read_reg16(SOSSI_FIFO_REG);
294                 len -= 2;
295                 data += 2;
296         }
297         while (len) {
298                 *(u8 *) data = sossi_read_reg8(SOSSI_FIFO_REG);
299                 len--;
300                 data++;
301         }
302 }