[PATCH] powerpc: update iSeries viocd and viotape device-tree
[linux-2.6] / drivers / video / pmagb-b-fb.c
1 /*
2  *      linux/drivers/video/pmagb-b-fb.c
3  *
4  *      PMAGB-B TURBOchannel Smart Frame Buffer (SFB) card support,
5  *      derived from:
6  *      "HP300 Topcat framebuffer support (derived from macfb of all things)
7  *      Phil Blundell <philb@gnu.org> 1998", the original code can be
8  *      found in the file hpfb.c in the same directory.
9  *
10  *      DECstation related code Copyright (C) 1999, 2000, 2001 by
11  *      Michael Engel <engel@unix-ag.org>,
12  *      Karsten Merker <merker@linuxtag.org> and
13  *      Harald Koerfgen.
14  *      Copyright (c) 2005  Maciej W. Rozycki
15  *
16  *      This file is subject to the terms and conditions of the GNU General
17  *      Public License.  See the file COPYING in the main directory of this
18  *      archive for more details.
19  */
20
21 #include <linux/compiler.h>
22 #include <linux/delay.h>
23 #include <linux/errno.h>
24 #include <linux/fb.h>
25 #include <linux/init.h>
26 #include <linux/kernel.h>
27 #include <linux/module.h>
28 #include <linux/types.h>
29
30 #include <asm/io.h>
31 #include <asm/system.h>
32
33 #include <asm/dec/tc.h>
34
35 #include <video/pmagb-b-fb.h>
36
37
38 struct pmagbbfb_par {
39         struct fb_info *next;
40         volatile void __iomem *mmio;
41         volatile void __iomem *smem;
42         volatile u32 __iomem *sfb;
43         volatile u32 __iomem *dac;
44         unsigned int osc0;
45         unsigned int osc1;
46         int slot;
47 };
48
49
50 static struct fb_info *root_pmagbbfb_dev;
51
52 static struct fb_var_screeninfo pmagbbfb_defined __initdata = {
53         .bits_per_pixel = 8,
54         .red.length     = 8,
55         .green.length   = 8,
56         .blue.length    = 8,
57         .activate       = FB_ACTIVATE_NOW,
58         .height         = -1,
59         .width          = -1,
60         .accel_flags    = FB_ACCEL_NONE,
61         .sync           = FB_SYNC_ON_GREEN,
62         .vmode          = FB_VMODE_NONINTERLACED,
63 };
64
65 static struct fb_fix_screeninfo pmagbbfb_fix __initdata = {
66         .id             = "PMAGB-BA",
67         .smem_len       = (2048 * 1024),
68         .type           = FB_TYPE_PACKED_PIXELS,
69         .visual         = FB_VISUAL_PSEUDOCOLOR,
70         .mmio_len       = PMAGB_B_FBMEM,
71 };
72
73
74 static inline void sfb_write(struct pmagbbfb_par *par, unsigned int reg, u32 v)
75 {
76         writel(v, par->sfb + reg / 4);
77 }
78
79 static inline u32 sfb_read(struct pmagbbfb_par *par, unsigned int reg)
80 {
81         return readl(par->sfb + reg / 4);
82 }
83
84 static inline void dac_write(struct pmagbbfb_par *par, unsigned int reg, u8 v)
85 {
86         writeb(v, par->dac + reg / 4);
87 }
88
89 static inline u8 dac_read(struct pmagbbfb_par *par, unsigned int reg)
90 {
91         return readb(par->dac + reg / 4);
92 }
93
94 static inline void gp0_write(struct pmagbbfb_par *par, u32 v)
95 {
96         writel(v, par->mmio + PMAGB_B_GP0);
97 }
98
99
100 /*
101  * Set the palette.
102  */
103 static int pmagbbfb_setcolreg(unsigned int regno, unsigned int red,
104                               unsigned int green, unsigned int blue,
105                               unsigned int transp, struct fb_info *info)
106 {
107         struct pmagbbfb_par *par = info->par;
108
109         BUG_ON(regno >= info->cmap.len);
110
111         red   >>= 8;    /* The cmap fields are 16 bits    */
112         green >>= 8;    /* wide, but the hardware colormap */
113         blue  >>= 8;    /* registers are only 8 bits wide */
114
115         mb();
116         dac_write(par, BT459_ADDR_LO, regno);
117         dac_write(par, BT459_ADDR_HI, 0x00);
118         wmb();
119         dac_write(par, BT459_CMAP, red);
120         wmb();
121         dac_write(par, BT459_CMAP, green);
122         wmb();
123         dac_write(par, BT459_CMAP, blue);
124
125         return 0;
126 }
127
128 static struct fb_ops pmagbbfb_ops = {
129         .owner          = THIS_MODULE,
130         .fb_setcolreg   = pmagbbfb_setcolreg,
131         .fb_fillrect    = cfb_fillrect,
132         .fb_copyarea    = cfb_copyarea,
133         .fb_imageblit   = cfb_imageblit,
134 };
135
136
137 /*
138  * Turn the hardware cursor off.
139  */
140 static void __init pmagbbfb_erase_cursor(struct fb_info *info)
141 {
142         struct pmagbbfb_par *par = info->par;
143
144         mb();
145         dac_write(par, BT459_ADDR_LO, 0x00);
146         dac_write(par, BT459_ADDR_HI, 0x03);
147         wmb();
148         dac_write(par, BT459_DATA, 0x00);
149 }
150
151 /*
152  * Set up screen parameters.
153  */
154 static void __init pmagbbfb_screen_setup(struct fb_info *info)
155 {
156         struct pmagbbfb_par *par = info->par;
157
158         info->var.xres = ((sfb_read(par, SFB_REG_VID_HOR) >>
159                            SFB_VID_HOR_PIX_SHIFT) & SFB_VID_HOR_PIX_MASK) * 4;
160         info->var.xres_virtual = info->var.xres;
161         info->var.yres = (sfb_read(par, SFB_REG_VID_VER) >>
162                           SFB_VID_VER_SL_SHIFT) & SFB_VID_VER_SL_MASK;
163         info->var.yres_virtual = info->var.yres;
164         info->var.left_margin = ((sfb_read(par, SFB_REG_VID_HOR) >>
165                                   SFB_VID_HOR_BP_SHIFT) &
166                                  SFB_VID_HOR_BP_MASK) * 4;
167         info->var.right_margin = ((sfb_read(par, SFB_REG_VID_HOR) >>
168                                    SFB_VID_HOR_FP_SHIFT) &
169                                   SFB_VID_HOR_FP_MASK) * 4;
170         info->var.upper_margin = (sfb_read(par, SFB_REG_VID_VER) >>
171                                   SFB_VID_VER_BP_SHIFT) & SFB_VID_VER_BP_MASK;
172         info->var.lower_margin = (sfb_read(par, SFB_REG_VID_VER) >>
173                                   SFB_VID_VER_FP_SHIFT) & SFB_VID_VER_FP_MASK;
174         info->var.hsync_len = ((sfb_read(par, SFB_REG_VID_HOR) >>
175                                 SFB_VID_HOR_SYN_SHIFT) &
176                                SFB_VID_HOR_SYN_MASK) * 4;
177         info->var.vsync_len = (sfb_read(par, SFB_REG_VID_VER) >>
178                                SFB_VID_VER_SYN_SHIFT) & SFB_VID_VER_SYN_MASK;
179
180         info->fix.line_length = info->var.xres;
181 };
182
183 /*
184  * Determine oscillator configuration.
185  */
186 static void __init pmagbbfb_osc_setup(struct fb_info *info)
187 {
188         static unsigned int pmagbbfb_freqs[] __initdata = {
189                 130808, 119843, 104000, 92980, 74367, 72800,
190                 69197, 66000, 65000, 50350, 36000, 32000, 25175
191         };
192         struct pmagbbfb_par *par = info->par;
193         u32 count0 = 8, count1 = 8, counttc = 16 * 256 + 8;
194         u32 freq0, freq1, freqtc = get_tc_speed() / 250;
195         int i, j;
196
197         gp0_write(par, 0);                              /* select Osc0 */
198         for (j = 0; j < 16; j++) {
199                 mb();
200                 sfb_write(par, SFB_REG_TCCLK_COUNT, 0);
201                 mb();
202                 for (i = 0; i < 100; i++) {     /* nominally max. 20.5us */
203                         if (sfb_read(par, SFB_REG_TCCLK_COUNT) == 0)
204                                 break;
205                         udelay(1);
206                 }
207                 count0 += sfb_read(par, SFB_REG_VIDCLK_COUNT);
208         }
209
210         gp0_write(par, 1);                              /* select Osc1 */
211         for (j = 0; j < 16; j++) {
212                 mb();
213                 sfb_write(par, SFB_REG_TCCLK_COUNT, 0);
214
215                 for (i = 0; i < 100; i++) {     /* nominally max. 20.5us */
216                         if (sfb_read(par, SFB_REG_TCCLK_COUNT) == 0)
217                                 break;
218                         udelay(1);
219                 }
220                 count1 += sfb_read(par, SFB_REG_VIDCLK_COUNT);
221         }
222
223         freq0 = (freqtc * count0 + counttc / 2) / counttc;
224         par->osc0 = freq0;
225         if (freq0 >= pmagbbfb_freqs[0] - (pmagbbfb_freqs[0] + 32) / 64 &&
226             freq0 <= pmagbbfb_freqs[0] + (pmagbbfb_freqs[0] + 32) / 64)
227                 par->osc0 = pmagbbfb_freqs[0];
228
229         freq1 = (par->osc0 * count1 + count0 / 2) / count0;
230         par->osc1 = freq1;
231         for (i = 0; i < ARRAY_SIZE(pmagbbfb_freqs); i++)
232                 if (freq1 >= pmagbbfb_freqs[i] -
233                              (pmagbbfb_freqs[i] + 128) / 256 &&
234                     freq1 <= pmagbbfb_freqs[i] +
235                              (pmagbbfb_freqs[i] + 128) / 256) {
236                         par->osc1 = pmagbbfb_freqs[i];
237                         break;
238                 }
239
240         if (par->osc0 - par->osc1 <= (par->osc0 + par->osc1 + 256) / 512 ||
241             par->osc1 - par->osc0 <= (par->osc0 + par->osc1 + 256) / 512)
242                 par->osc1 = 0;
243
244         gp0_write(par, par->osc1 != 0);                 /* reselect OscX */
245
246         info->var.pixclock = par->osc1 ?
247                              (1000000000 + par->osc1 / 2) / par->osc1 :
248                              (1000000000 + par->osc0 / 2) / par->osc0;
249 };
250
251
252 static int __init pmagbbfb_init_one(int slot)
253 {
254         char freq0[12], freq1[12];
255         struct fb_info *info;
256         struct pmagbbfb_par *par;
257         unsigned long base_addr;
258         u32 vid_base;
259
260         info = framebuffer_alloc(sizeof(struct pmagbbfb_par), NULL);
261         if (!info)
262                 return -ENOMEM;
263
264         par = info->par;
265         par->slot = slot;
266         claim_tc_card(par->slot);
267
268         base_addr = get_tc_base_addr(par->slot);
269
270         par->next = root_pmagbbfb_dev;
271         root_pmagbbfb_dev = info;
272
273         if (fb_alloc_cmap(&info->cmap, 256, 0) < 0)
274                 goto err_alloc;
275
276         info->fbops = &pmagbbfb_ops;
277         info->fix = pmagbbfb_fix;
278         info->var = pmagbbfb_defined;
279         info->flags = FBINFO_DEFAULT;
280
281         /* MMIO mapping setup.  */
282         info->fix.mmio_start = base_addr;
283         par->mmio = ioremap_nocache(info->fix.mmio_start, info->fix.mmio_len);
284         if (!par->mmio)
285                 goto err_cmap;
286         par->sfb = par->mmio + PMAGB_B_SFB;
287         par->dac = par->mmio + PMAGB_B_BT459;
288
289         /* Frame buffer mapping setup.  */
290         info->fix.smem_start = base_addr + PMAGB_B_FBMEM;
291         par->smem = ioremap_nocache(info->fix.smem_start, info->fix.smem_len);
292         if (!par->smem)
293                 goto err_mmio_map;
294         vid_base = sfb_read(par, SFB_REG_VID_BASE);
295         info->screen_base = (void __iomem *)par->smem + vid_base * 0x1000;
296         info->screen_size = info->fix.smem_len - 2 * vid_base * 0x1000;
297
298         pmagbbfb_erase_cursor(info);
299         pmagbbfb_screen_setup(info);
300         pmagbbfb_osc_setup(info);
301
302         if (register_framebuffer(info) < 0)
303                 goto err_smem_map;
304
305         snprintf(freq0, sizeof(freq0), "%u.%03uMHz",
306                  par->osc0 / 1000, par->osc0 % 1000);
307         snprintf(freq1, sizeof(freq1), "%u.%03uMHz",
308                  par->osc1 / 1000, par->osc1 % 1000);
309
310         pr_info("fb%d: %s frame buffer device in slot %d\n",
311                 info->node, info->fix.id, par->slot);
312         pr_info("fb%d: Osc0: %s, Osc1: %s, Osc%u selected\n",
313                 info->node, freq0, par->osc1 ? freq1 : "disabled",
314                 par->osc1 != 0);
315
316         return 0;
317
318
319 err_smem_map:
320         iounmap(par->smem);
321
322 err_mmio_map:
323         iounmap(par->mmio);
324
325 err_cmap:
326         fb_dealloc_cmap(&info->cmap);
327
328 err_alloc:
329         root_pmagbbfb_dev = par->next;
330         release_tc_card(par->slot);
331         framebuffer_release(info);
332         return -ENXIO;
333 }
334
335 static void __exit pmagbbfb_exit_one(void)
336 {
337         struct fb_info *info = root_pmagbbfb_dev;
338         struct pmagbbfb_par *par = info->par;
339
340         unregister_framebuffer(info);
341         iounmap(par->smem);
342         iounmap(par->mmio);
343         fb_dealloc_cmap(&info->cmap);
344         root_pmagbbfb_dev = par->next;
345         release_tc_card(par->slot);
346         framebuffer_release(info);
347 }
348
349
350 /*
351  * Initialise the framebuffer.
352  */
353 static int __init pmagbbfb_init(void)
354 {
355         int count = 0;
356         int slot;
357
358         if (fb_get_options("pmagbbfb", NULL))
359                 return -ENXIO;
360
361         while ((slot = search_tc_card("PMAGB-BA")) >= 0) {
362                 if (pmagbbfb_init_one(slot) < 0)
363                         break;
364                 count++;
365         }
366         return (count > 0) ? 0 : -ENXIO;
367 }
368
369 static void __exit pmagbbfb_exit(void)
370 {
371         while (root_pmagbbfb_dev)
372                 pmagbbfb_exit_one();
373 }
374
375
376 module_init(pmagbbfb_init);
377 module_exit(pmagbbfb_exit);
378
379 MODULE_LICENSE("GPL");