2 * File: drivers/video/omap/omap1/sossi.c
4 * OMAP1 Special OptimiSed Screen Interface support
6 * Copyright (C) 2004-2005 Nokia Corporation
7 * Author: Juha Yrjölä <juha.yrjola@nokia.com>
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.
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.
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.
24 #include <linux/config.h>
25 #include <linux/module.h>
27 #include <asm/hardware/clock.h>
32 #define MODULE_NAME "omapfb-sossi"
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
45 #define DMA_GSCR 0xfffedc04
46 #define DMA_LCD_CCR 0xfffee3c2
47 #define DMA_LCD_CTRL 0xfffee3c4
48 #define DMA_LCD_LCH_CTRL 0xfffee3ea
50 #define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args)
52 static int sossi_base = IO_ADDRESS(OMAP_SOSSI_BASE);
54 static inline u32 sossi_read_reg(int reg)
56 return readl(sossi_base + reg);
59 static inline u16 sossi_read_reg16(int reg)
61 return readw(sossi_base + reg);
64 static inline u8 sossi_read_reg8(int reg)
66 return readb(sossi_base + reg);
69 static inline void sossi_write_reg(int reg, u32 value)
71 writel(value, sossi_base + reg);
74 static inline void sossi_write_reg16(int reg, u16 value)
76 writew(value, sossi_base + reg);
79 static inline void sossi_write_reg8(int reg, u8 value)
81 writeb(value, sossi_base + reg);
84 static void sossi_set_bits(int reg, u32 bits)
86 sossi_write_reg(reg, sossi_read_reg(reg) | bits);
89 static void sossi_clear_bits(int reg, u32 bits)
91 sossi_write_reg(reg, sossi_read_reg(reg) & ~bits);
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)
98 static struct clk *dpll_clk;
104 dpll_clk = clk_get(NULL, "ck_dpll1");
105 BUG_ON(dpll_clk == NULL);
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);
114 l |= CONF_MOD_SOSSI_CLK_EN_R;
115 omap_writel(l, MOD_CONF_CTRL_1);
117 omap_writel(omap_readl(ARM_IDLECT2) | (1 << 11), ARM_IDLECT2);
118 omap_writel(omap_readl(ARM_IDLECT1) | (1 << 6), ARM_IDLECT1);
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 */
126 sossi_write_reg(SOSSI_INIT2_REG, l);
128 sossi_write_reg(SOSSI_ID_REG, 0);
129 l = sossi_read_reg(SOSSI_ID_REG);
130 k = sossi_read_reg(SOSSI_ID_REG);
132 if (l != 0x55555555 || k != 0xaaaaaaaa) {
133 pr_err("Invalid SoSSI sync pattern: %08x, %08x\n", l, k);
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);
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);
149 static unsigned long get_sossi_clk_rate(int div)
151 return (clk_get_rate(dpll_clk)) / div;
154 static unsigned long get_sossi_clk_period(int div)
157 return 1000000000 / (get_sossi_clk_rate(div) / 1000);
160 static int ns_to_sossi_ticks(int time, int div)
162 unsigned long tick_ps;
164 /* Calculate in picosecs to yield more exact results */
165 tick_ps = get_sossi_clk_period(div);
167 return (time * 1000 + tick_ps - 1) / tick_ps;
170 static int set_timings(int div, int tw0, int tw1)
174 if (tw1 * 1000 > 64 * get_sossi_clk_period(div))
176 if (tw0 * 1000 > 16 * get_sossi_clk_period(div))
179 l = omap_readl(MOD_CONF_CTRL_1);
181 l |= (div - 1) << 17;
182 omap_writel(l, MOD_CONF_CTRL_1);
184 tw0 = ns_to_sossi_ticks(tw0, div) - 1;
185 tw1 = ns_to_sossi_ticks(tw1, div) - 1;
191 printk("Using TW0 = %d, TW1 = %d, div = %d, period = %d ps\n",
192 tw0, tw1, div, get_sossi_clk_period(div));
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);
202 static struct sossi {
206 void sossi_set_timings(int min_time, int min_tw0, int min_tw1)
210 for (div = 1; div <= 8; div++) {
211 if (min_time * 1000 > get_sossi_clk_period(div))
213 if (set_timings(div, min_tw0, min_tw1) == 0)
217 pr_err("DPLL frequency too high for SoSSI\n");
222 void sossi_set_xfer_params(int bus_pick_count, int bus_pick_width)
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);
231 void sossi_start_transfer(void)
234 sossi_clear_bits(SOSSI_INIT2_REG, 1 << 4);
236 sossi_clear_bits(SOSSI_INIT1_REG, 1 << 30);
237 /* FIXME: locking? */
240 void sossi_stop_transfer(void)
243 sossi_set_bits(SOSSI_INIT2_REG, 1 << 4);
245 sossi_set_bits(SOSSI_INIT1_REG, 1 << 30);
246 /* FIXME: locking? */
249 static void send_data(const void *data, unsigned int len)
252 sossi_write_reg(SOSSI_FIFO_REG, *(const u32 *) data);
257 sossi_write_reg16(SOSSI_FIFO_REG, *(const u16 *) data);
262 sossi_write_reg8(SOSSI_FIFO_REG, *(const u8 *) data);
268 static void set_cycles(unsigned int len)
270 int nr_cycles = len / (sossi.bus_pick_width / 8);
272 sossi_clear_bits(SOSSI_INIT1_REG, 0x3ffff);
273 sossi_set_bits(SOSSI_INIT1_REG, (nr_cycles - 1) & 0x3ffff);
276 void sossi_send_cmd(const void *data, unsigned int len)
278 sossi_clear_bits(SOSSI_INIT1_REG, 1 << 18);
280 send_data(data, len);
283 void sossi_send_data(const void *data, unsigned int len)
285 sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
287 send_data(data, len);
290 void sossi_prepare_dma_transfer(unsigned int count)
292 sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
296 void sossi_send_data_const32(u32 data, unsigned int count)
298 sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
299 set_cycles(count * 4);
301 sossi_write_reg(SOSSI_FIFO_REG, data);
306 void sossi_set_tearing(int mode, int hs_counter, int detect_limit,
307 int vs_counter, int vs_detect_limit, int flags)
311 l |= vs_counter << 30;
312 if (flags & SOSSI_FLAG_HS_INVERTED)
314 if (flags & SOSSI_FLAG_VS_INVERTED)
317 l |= hs_counter << 15;
318 l |= vs_detect_limit << 3;
320 sossi_write_reg(SOSSI_TEARING_REG, l);
323 void sossi_read_data(void *data, unsigned int len)
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);
330 *(u32 *) data = sossi_read_reg(SOSSI_FIFO_REG);
335 *(u16 *) data = sossi_read_reg16(SOSSI_FIFO_REG);
340 *(u8 *) data = sossi_read_reg8(SOSSI_FIFO_REG);