make oldconfig will rebuild these...
[linux-2.4.21-pre4.git] / drivers / video / platinumfb.c
1 /*
2  *  platinumfb.c -- frame buffer device for the PowerMac 'platinum' display
3  *
4  *  Copyright (C) 1998 Franz Sirl
5  *
6  *  Frame buffer structure from:
7  *    drivers/video/controlfb.c -- frame buffer device for
8  *    Apple 'control' display chip.
9  *    Copyright (C) 1998 Dan Jacobowitz
10  *
11  *  Hardware information from:
12  *    platinum.c: Console support for PowerMac "platinum" display adaptor.
13  *    Copyright (C) 1996 Paul Mackerras and Mark Abene
14  *
15  *  This file is subject to the terms and conditions of the GNU General Public
16  *  License. See the file COPYING in the main directory of this archive for
17  *  more details.
18  */
19
20 #include <linux/config.h>
21 #include <linux/module.h>
22 #include <linux/kernel.h>
23 #include <linux/errno.h>
24 #include <linux/string.h>
25 #include <linux/mm.h>
26 #include <linux/tty.h>
27 #include <linux/slab.h>
28 #include <linux/vmalloc.h>
29 #include <linux/delay.h>
30 #include <linux/interrupt.h>
31 #include <linux/fb.h>
32 #include <linux/selection.h>
33 #include <linux/init.h>
34 #include <linux/pci.h>
35 #include <linux/nvram.h>
36 #ifdef CONFIG_FB_COMPAT_XPMAC
37 #include <asm/vc_ioctl.h>
38 #endif
39 #include <asm/io.h>
40 #include <asm/prom.h>
41 #include <asm/pgtable.h>
42
43 #include <video/fbcon.h>
44 #include <video/fbcon-cfb8.h>
45 #include <video/fbcon-cfb16.h>
46 #include <video/fbcon-cfb32.h>
47 #include <video/macmodes.h>
48
49 #include "platinumfb.h"
50
51 static char fontname[40] __initdata = { 0 };
52
53 static int currcon = 0;
54
55 static int default_vmode = VMODE_NVRAM;
56 static int default_cmode = CMODE_NVRAM;
57
58 struct fb_par_platinum {
59         int     vmode, cmode;
60         int     xres, yres;
61         int     vxres, vyres;
62         int     xoffset, yoffset;
63 };
64
65 struct fb_info_platinum {
66         struct fb_info                  fb_info;
67         struct display                  disp;
68         struct fb_par_platinum          default_par;
69         struct fb_par_platinum          current_par;
70
71         struct {
72                 __u8 red, green, blue;
73         }                               palette[256];
74         
75         volatile struct cmap_regs       *cmap_regs;
76         unsigned long                   cmap_regs_phys;
77         
78         volatile struct platinum_regs   *platinum_regs;
79         unsigned long                   platinum_regs_phys;
80         
81         __u8                            *frame_buffer;
82         volatile __u8                   *base_frame_buffer;
83         unsigned long                   frame_buffer_phys;
84         
85         unsigned long                   total_vram;
86         int                             clktype;
87         int                             dactype;
88
89         union {
90 #ifdef FBCON_HAS_CFB16
91                 u16 cfb16[16];
92 #endif
93 #ifdef FBCON_HAS_CFB32
94                 u32 cfb32[16];
95 #endif
96         } fbcon_cmap;
97 };
98
99 /*
100  * Frame buffer device API
101  */
102
103 static int platinum_get_fix(struct fb_fix_screeninfo *fix, int con,
104                             struct fb_info *fb);
105 static int platinum_get_var(struct fb_var_screeninfo *var, int con,
106                             struct fb_info *fb);
107 static int platinum_set_var(struct fb_var_screeninfo *var, int con,
108                             struct fb_info *fb);
109 static int platinum_get_cmap(struct fb_cmap *cmap, int kspc, int con,
110                              struct fb_info *info);
111 static int platinum_set_cmap(struct fb_cmap *cmap, int kspc, int con,
112                              struct fb_info *info);
113
114
115 /*
116  * Interface to the low level console driver
117  */
118
119 static int platinum_switch(int con, struct fb_info *fb);
120 static int platinum_updatevar(int con, struct fb_info *fb);
121 static void platinum_blank(int blank, struct fb_info *fb);
122
123
124 /*
125  * internal functions
126  */
127
128 static void platinum_of_init(struct device_node *dp);
129 static inline int platinum_vram_reqd(const struct fb_info_platinum* info,
130                                         int video_mode,
131                                         int color_mode);
132 static int read_platinum_sense(struct fb_info_platinum *info);
133 static void set_platinum_clock(struct fb_info_platinum *info);
134 static void platinum_set_par(const struct fb_par_platinum *par, struct fb_info_platinum *info);
135 static int platinum_par_to_var(struct fb_var_screeninfo *var,
136                                const struct fb_par_platinum *par,
137                                const struct fb_info_platinum *info);
138 static int platinum_var_to_par(const struct fb_var_screeninfo *var,
139                                struct fb_par_platinum *par,
140                                const struct fb_info_platinum *info);
141 static int platinum_encode_fix(struct fb_fix_screeninfo *fix,
142                                const struct fb_par_platinum *par,
143                                const struct fb_info_platinum *info);
144 static void platinum_set_dispsw(struct display *disp,
145                                 struct fb_info_platinum *info, int cmode,
146                                 int accel);
147 static int platinum_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
148                               u_int *transp, struct fb_info *fb);
149 static int platinum_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
150                               u_int transp, struct fb_info *fb);
151 static void do_install_cmap(int con, struct fb_info *info);
152
153
154 /*
155  * Interface used by the world
156  */
157
158 int platinum_init(void);
159 int platinum_setup(char*);
160
161 static struct fb_ops platinumfb_ops = {
162         owner:          THIS_MODULE,
163         fb_get_fix:     platinum_get_fix,
164         fb_get_var:     platinum_get_var,
165         fb_set_var:     platinum_set_var,
166         fb_get_cmap:    platinum_get_cmap,
167         fb_set_cmap:    platinum_set_cmap,
168 };
169
170 static int platinum_get_fix(struct fb_fix_screeninfo *fix, int con,
171                             struct fb_info *fb)
172 {
173         const struct fb_info_platinum *info = (struct fb_info_platinum *)fb;
174         struct fb_par_platinum par;
175
176         if (con == -1)
177                 par = info->default_par;
178         else
179                 platinum_var_to_par(&fb_display[con].var, &par, info);
180
181         platinum_encode_fix(fix, &par, info);
182         return 0;
183 }
184
185 static int platinum_get_var(struct fb_var_screeninfo *var, int con,
186                             struct fb_info *fb)
187 {
188         const struct fb_info_platinum *info = (struct fb_info_platinum *)fb;
189
190         if (con == -1)
191                 platinum_par_to_var(var, &info->default_par, info);
192         else
193                 *var = fb_display[con].var;
194
195         return 0;
196 }
197
198 static void platinum_set_dispsw(struct display *disp,
199                                 struct fb_info_platinum *info, int cmode,
200                                 int accel)
201 {
202         switch(cmode) {
203 #ifdef FBCON_HAS_CFB8
204             case CMODE_8:
205                 disp->dispsw = &fbcon_cfb8;
206                 break;
207 #endif
208 #ifdef FBCON_HAS_CFB16
209             case CMODE_16:
210                 disp->dispsw = &fbcon_cfb16;
211                 disp->dispsw_data = info->fbcon_cmap.cfb16;
212                 break;
213 #endif
214 #ifdef FBCON_HAS_CFB32
215             case CMODE_32:
216                 disp->dispsw = &fbcon_cfb32;
217                 disp->dispsw_data = info->fbcon_cmap.cfb32;
218                 break;
219 #endif
220             default:
221                 disp->dispsw = &fbcon_dummy;
222                 break;
223         }
224 }
225
226 static int platinum_set_var(struct fb_var_screeninfo *var, int con,
227                             struct fb_info *fb)
228 {
229         struct fb_info_platinum *info = (struct fb_info_platinum *) fb;
230         struct fb_par_platinum par;
231         struct display *display;
232         int oldxres, oldyres, oldvxres, oldvyres, oldbpp, err;
233         int activate = var->activate;
234         struct platinum_regvals *init;
235
236         display = (con >= 0) ? &fb_display[con] : fb->disp;
237
238         if((err = platinum_var_to_par(var, &par, info))) {
239                 printk(KERN_ERR "platinum_set_var: error calling platinum_var_to_par: %d.\n", err);
240                 return err;
241         }
242         
243         platinum_par_to_var(var, &par, info);
244
245         if ((activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) {
246                 printk(KERN_INFO "platinum_set_var: Not activating.\n");
247                 return 0;
248         }
249
250         init = platinum_reg_init[par.vmode-1];
251
252         oldxres = display->var.xres;
253         oldyres = display->var.yres;
254         oldvxres = display->var.xres_virtual;
255         oldvyres = display->var.yres_virtual;
256         oldbpp = display->var.bits_per_pixel;
257         display->var = *var;
258
259         if (oldxres != var->xres || oldyres != var->yres ||
260             oldvxres != var->xres_virtual || oldyres != var->yres_virtual ||
261             oldbpp != var->bits_per_pixel) {
262             struct fb_fix_screeninfo fix;
263
264             platinum_encode_fix(&fix, &par, info);
265             display->screen_base = (char *) info->frame_buffer + init->fb_offset + 0x20;
266             display->visual = fix.visual;
267             display->type = fix.type;
268             display->type_aux = fix.type_aux;
269             display->ypanstep = fix.ypanstep;
270             display->ywrapstep = fix.ywrapstep;
271             display->line_length = fix.line_length;
272             display->can_soft_blank = 1;
273             display->inverse = 0;
274             platinum_set_dispsw(display, info, par.cmode, 0);
275             display->scrollmode = SCROLL_YREDRAW;
276             if (info->fb_info.changevar)
277               (*info->fb_info.changevar)(con);
278         }
279
280         if (!info->fb_info.display_fg ||
281             info->fb_info.display_fg->vc_num == con)
282                 platinum_set_par(&par, info);
283
284         if (oldbpp != var->bits_per_pixel) {
285             if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
286               return err;
287             do_install_cmap(con, &info->fb_info);
288         }
289
290         return 0;
291 }
292
293 static int platinum_get_cmap(struct fb_cmap *cmap, int kspc, int con,
294                              struct fb_info *info)
295 {
296         if (!info->display_fg ||
297             info->display_fg->vc_num == con)    /* current console? */
298                 return fb_get_cmap(cmap, kspc, platinum_getcolreg, info);
299         if (fb_display[con].cmap.len)   /* non default colormap? */
300                 fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
301         else {
302                 int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
303                 fb_copy_cmap(fb_default_cmap(size), cmap, kspc ? 0 : 2);
304         }
305         return 0;
306 }
307
308 static int platinum_set_cmap(struct fb_cmap *cmap, int kspc, int con,
309                              struct fb_info *info)
310 {
311         int err;
312         struct display *disp;
313
314         if (con >= 0)
315                 disp = &fb_display[con];
316         else
317                 disp = info->disp;
318         if (!disp->cmap.len) {     /* no colormap allocated? */
319                 int size = disp->var.bits_per_pixel == 16 ? 32 : 256;
320                 if ((err = fb_alloc_cmap(&disp->cmap, size, 0)))
321                         return err;
322         }
323
324         if (!info->display_fg ||
325             info->display_fg->vc_num == con)    /* current console? */
326                 return fb_set_cmap(cmap, kspc, platinum_setcolreg, info);
327         else
328                 fb_copy_cmap(cmap, &disp->cmap, kspc ? 0 : 1);
329         return 0;
330 }
331
332 static int platinum_switch(int con, struct fb_info *fb)
333 {
334         struct fb_info_platinum *info = (struct fb_info_platinum *) fb;
335         struct fb_par_platinum par;
336
337         if (fb_display[currcon].cmap.len)
338                 fb_get_cmap(&fb_display[currcon].cmap, 1, platinum_getcolreg,
339                             fb);
340         currcon = con;
341
342         platinum_var_to_par(&fb_display[con].var, &par, info);
343         platinum_set_par(&par, info);
344         platinum_set_dispsw(&fb_display[con], info, par.cmode, 0);
345         do_install_cmap(con, fb);
346
347         return 1;
348 }
349
350 static int platinum_updatevar(int con, struct fb_info *fb)
351 {
352         printk(KERN_ERR "platinum_updatevar is doing nothing yet.\n");
353         return 0;
354 }
355
356 static void platinum_blank(int blank,  struct fb_info *fb)
357 {
358 /*
359  *  Blank the screen if blank_mode != 0, else unblank. If blank == NULL
360  *  then the caller blanks by setting the CLUT (Color Look Up Table) to all
361  *  black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due
362  *  to e.g. a video mode which doesn't support it. Implements VESA suspend
363  *  and powerdown modes on hardware that supports disabling hsync/vsync:
364  *    blank_mode == 2: suspend vsync
365  *    blank_mode == 3: suspend hsync
366  *    blank_mode == 4: powerdown
367  */
368 /* [danj] I think there's something fishy about those constants... */
369 /*
370         struct fb_info_platinum *info = (struct fb_info_platinum *) fb;
371         int     ctrl;
372
373         ctrl = ld_le32(&info->platinum_regs->ctrl.r) | 0x33;
374         if (blank)
375                 --blank_mode;
376         if (blank & VESA_VSYNC_SUSPEND)
377                 ctrl &= ~3;
378         if (blank & VESA_HSYNC_SUSPEND)
379                 ctrl &= ~0x30;
380         out_le32(&info->platinum_regs->ctrl.r, ctrl);
381 */
382 /* TODO: Figure out how the heck to powerdown this thing! */
383     return;
384 }
385
386 static int platinum_getcolreg(u_int regno, u_int *red, u_int *green,
387                               u_int *blue, u_int *transp, struct fb_info *fb)
388 {
389         struct fb_info_platinum *info = (struct fb_info_platinum *) fb;
390
391         if (regno > 255)
392                 return 1;
393
394         *red = (info->palette[regno].red<<8) | info->palette[regno].red;
395         *green = (info->palette[regno].green<<8) | info->palette[regno].green;
396         *blue = (info->palette[regno].blue<<8) | info->palette[regno].blue;
397         *transp = 0;
398         return 0;
399 }
400
401 static int platinum_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
402                               u_int transp, struct fb_info *fb)
403 {
404         struct fb_info_platinum *info = (struct fb_info_platinum *) fb;
405         volatile struct cmap_regs *cmap_regs = info->cmap_regs;
406
407         if (regno > 255)
408                 return 1;
409
410         red >>= 8;
411         green >>= 8;
412         blue >>= 8;
413
414         info->palette[regno].red = red;
415         info->palette[regno].green = green;
416         info->palette[regno].blue = blue;
417
418         out_8(&cmap_regs->addr, regno);         /* tell clut what addr to fill  */
419         out_8(&cmap_regs->lut, red);            /* send one color channel at    */
420         out_8(&cmap_regs->lut, green);          /* a time...                    */
421         out_8(&cmap_regs->lut, blue);
422
423         if(regno < 16) {
424 #ifdef FBCON_HAS_CFB16
425                 info->fbcon_cmap.cfb16[regno] = (regno << 10) | (regno << 5) | (regno << 0);
426 #endif
427 #ifdef FBCON_HAS_CFB32
428                 info->fbcon_cmap.cfb32[regno] = (regno << 24) | (regno << 16) | (regno << 8) | regno;
429 #endif
430         }
431         return 0;
432 }
433
434 static void do_install_cmap(int con, struct fb_info *info)
435 {
436         if (con != currcon)
437                 return;
438         if (fb_display[con].cmap.len)
439                 fb_set_cmap(&fb_display[con].cmap, 1, platinum_setcolreg,
440                             info);
441         else {
442                 int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
443                 fb_set_cmap(fb_default_cmap(size), 1, platinum_setcolreg,
444                             info);
445         }
446 }
447
448 static inline int platinum_vram_reqd(const struct fb_info_platinum *info, int video_mode, int color_mode)
449 {
450         unsigned int pitch = 
451                (vmode_attrs[video_mode-1].hres * (1<<color_mode) + 0x20);
452         fixup_pitch(pitch, info, color_mode);
453         return vmode_attrs[video_mode-1].vres * pitch;
454 }
455
456 #define STORE_D2(a, d) { \
457         out_8(&cmap_regs->addr, (a+32)); \
458         out_8(&cmap_regs->d2, (d)); \
459 }
460
461 static void set_platinum_clock(struct fb_info_platinum *info)
462 {
463         volatile struct cmap_regs *cmap_regs = info->cmap_regs;
464         struct platinum_regvals *init;
465
466         init = platinum_reg_init[info->current_par.vmode-1];
467
468         STORE_D2(6, 0xc6);
469         out_8(&cmap_regs->addr,3+32);
470
471         if (in_8(&cmap_regs->d2) == 2) {
472                 STORE_D2(7, init->clock_params[info->clktype][0]);
473                 STORE_D2(8, init->clock_params[info->clktype][1]);
474                 STORE_D2(3, 3);
475         } else {
476                 STORE_D2(4, init->clock_params[info->clktype][0]);
477                 STORE_D2(5, init->clock_params[info->clktype][1]);
478                 STORE_D2(3, 2);
479         }
480
481         __delay(5000);
482         STORE_D2(9, 0xa6);
483 }
484
485
486 /* Now how about actually saying, Make it so! */
487 /* Some things in here probably don't need to be done each time. */
488 static void platinum_set_par(const struct fb_par_platinum *par, struct fb_info_platinum *info)
489 {
490         volatile struct platinum_regs   *platinum_regs = info->platinum_regs;
491         volatile struct cmap_regs       *cmap_regs = info->cmap_regs;
492         struct platinum_regvals         *init;
493         int                             i;
494         int                             vmode, cmode, pitch;
495         
496         info->current_par = *par;
497
498         vmode = par->vmode;
499         cmode = par->cmode;
500
501         init = platinum_reg_init[vmode - 1];
502
503         /* Initialize display timing registers */
504         out_be32(&platinum_regs->reg[24].r, 7); /* turn display off */
505
506         for (i = 0; i < 26; ++i)
507                 out_be32(&platinum_regs->reg[i+32].r, init->regs[i]);
508
509         out_be32(&platinum_regs->reg[26+32].r, (info->total_vram == 0x100000 ?
510                                                 init->offset[cmode] + 4 - cmode :
511                                                 init->offset[cmode]));
512         out_be32(&platinum_regs->reg[16].r, (unsigned) info->frame_buffer_phys+init->fb_offset+0x10);
513         pitch = init->pitch[cmode];
514         fixup_pitch(pitch, info, cmode);
515         out_be32(&platinum_regs->reg[18].r, pitch);
516         out_be32(&platinum_regs->reg[19].r, (info->total_vram == 0x100000 ?
517                                              init->mode[cmode+1] :
518                                              init->mode[cmode]));
519         out_be32(&platinum_regs->reg[20].r, (info->total_vram == 0x100000 ? 0x11 : 0x1011));
520         out_be32(&platinum_regs->reg[21].r, 0x100);
521         out_be32(&platinum_regs->reg[22].r, 1);
522         out_be32(&platinum_regs->reg[23].r, 1);
523         out_be32(&platinum_regs->reg[26].r, 0xc00);
524         out_be32(&platinum_regs->reg[27].r, 0x235);
525         /* out_be32(&platinum_regs->reg[27].r, 0x2aa); */
526
527         STORE_D2(0, (info->total_vram == 0x100000 ?
528                      init->dacula_ctrl[cmode] & 0xf :
529                      init->dacula_ctrl[cmode]));
530         STORE_D2(1, 4);
531         STORE_D2(2, 0);
532
533         set_platinum_clock(info);
534
535         out_be32(&platinum_regs->reg[24].r, 0); /* turn display on */
536
537 #ifdef CONFIG_FB_COMPAT_XPMAC
538         if (console_fb_info == &info->fb_info) {
539                 display_info.height = par->yres;
540                 display_info.width = par->xres;
541                 display_info.depth = ( (cmode == CMODE_32) ? 32 :
542                                       ((cmode == CMODE_16) ? 16 : 8));
543                 display_info.pitch = vmode_attrs[vmode-1].hres * (1<<cmode) + 0x20;
544                 fixup_pitch(display_info.pitch, info, cmode);
545                 display_info.mode = vmode;
546                 strncpy(display_info.name, "platinum",
547                         sizeof(display_info.name));
548                 display_info.fb_address = info->frame_buffer_phys + init->fb_offset + 0x20;
549                 display_info.cmap_adr_address = info->cmap_regs_phys;
550                 display_info.cmap_data_address = info->cmap_regs_phys + 0x30;
551                 display_info.disp_reg_address = info->platinum_regs_phys;
552                 
553         }
554 #endif /* CONFIG_FB_COMPAT_XPMAC */
555 }
556
557
558 static int __init init_platinum(struct fb_info_platinum *info)
559 {
560         struct fb_var_screeninfo var;
561         struct display *disp;
562         int sense;
563         int j,k;
564
565         sense = read_platinum_sense(info);
566         printk(KERN_INFO "Monitor sense value = 0x%x, ", sense);
567
568 #ifdef CONFIG_NVRAM
569         if (default_vmode == VMODE_NVRAM) {
570                 default_vmode = nvram_read_byte(NV_VMODE);
571                 if (default_vmode <= 0 || default_vmode > VMODE_MAX ||
572                     !platinum_reg_init[default_vmode-1])
573                         default_vmode = VMODE_CHOOSE;
574         }
575         if (default_cmode == CMODE_NVRAM)
576                 default_cmode = nvram_read_byte(NV_CMODE);
577 #endif
578         if (default_vmode == VMODE_CHOOSE)
579                 default_vmode = mac_map_monitor_sense(sense);
580         if (default_vmode <= 0 || default_vmode > VMODE_MAX)
581                 default_vmode = VMODE_640_480_60;
582         if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
583                 default_cmode = CMODE_8;
584         /*
585          * Reduce the pixel size if we don't have enough VRAM.
586          */
587         while(default_cmode > CMODE_8 && platinum_vram_reqd(info, default_vmode, default_cmode)
588             > info->total_vram)
589                 default_cmode--;
590
591         printk("using video mode %d and color mode %d.\n", default_vmode, default_cmode);
592
593         mac_vmode_to_var(default_vmode, default_cmode, &var);
594
595         if (platinum_var_to_par(&var, &info->default_par, info)) {
596                 printk(KERN_ERR "platinumfb: can't set default video mode\n");
597                 return 0;
598         }
599
600         disp = &info->disp;
601
602         strcpy(info->fb_info.modename, "platinum");
603         info->fb_info.node = -1;
604         info->fb_info.fbops = &platinumfb_ops;
605         info->fb_info.disp = disp;
606         strcpy(info->fb_info.fontname, fontname);
607         info->fb_info.changevar = NULL;
608         info->fb_info.switch_con = &platinum_switch;
609         info->fb_info.updatevar = &platinum_updatevar;
610         info->fb_info.blank = &platinum_blank;
611         info->fb_info.flags = FBINFO_FLAG_DEFAULT;
612
613         for (j = 0; j < 16; j++) {
614                 k = color_table[j];
615                 info->palette[j].red = default_red[k];
616                 info->palette[j].green = default_grn[k];
617                 info->palette[j].blue = default_blu[k];
618         }
619         platinum_set_var(&var, -1, &info->fb_info);
620
621         if (register_framebuffer(&info->fb_info) < 0)
622                 return 0;
623
624         printk(KERN_INFO "fb%d: platinum frame buffer device\n",
625                GET_FB_IDX(info->fb_info.node));
626
627         return 1;
628 }
629
630 int __init platinum_init(void)
631 {
632         struct device_node *dp;
633
634         dp = find_devices("platinum");
635         if (dp != 0)
636                 platinum_of_init(dp);
637         return 0;
638 }
639
640 #ifdef __powerpc__
641 #define invalidate_cache(addr) \
642         asm volatile("eieio; dcbf 0,%1" \
643         : "=m" (*(addr)) : "r" (addr) : "memory");
644 #else
645 #define invalidate_cache(addr)
646 #endif
647
648 static void __init platinum_of_init(struct device_node *dp)
649 {
650         struct fb_info_platinum *info;
651         unsigned long           addr, size;
652         volatile __u8           *fbuffer;
653         int                     i, bank0, bank1, bank2, bank3;
654
655         if(dp->n_addrs != 2) {
656                 printk(KERN_ERR "expecting 2 address for platinum (got %d)", dp->n_addrs);
657                 return;
658         }
659
660         info = kmalloc(sizeof(*info), GFP_ATOMIC);
661         if (info == 0)
662                 return;
663         memset(info, 0, sizeof(*info));
664
665         /* Map in frame buffer and registers */
666         for (i = 0; i < dp->n_addrs; ++i) {
667                 addr = dp->addrs[i].address;
668                 size = dp->addrs[i].size;
669                 /* Let's assume we can request either all or nothing */
670                 if (!request_mem_region(addr, size, "platinumfb")) {
671                         kfree(info);
672                         return;
673                 }
674                 if (size >= 0x400000) {
675                         /* frame buffer - map only 4MB */
676                         info->frame_buffer_phys = addr;
677                         info->frame_buffer = __ioremap(addr, 0x400000, _PAGE_WRITETHRU);
678                         info->base_frame_buffer = info->frame_buffer;
679                 } else {
680                         /* registers */
681                         info->platinum_regs_phys = addr;
682                         info->platinum_regs = ioremap(addr, size);
683                 }
684         }
685
686         info->cmap_regs_phys = 0xf301b000;      /* XXX not in prom? */
687         request_mem_region(info->cmap_regs_phys, 0x1000, "platinumfb cmap");
688         info->cmap_regs = ioremap(info->cmap_regs_phys, 0x1000);
689
690         /* Grok total video ram */
691         out_be32(&info->platinum_regs->reg[16].r, (unsigned)info->frame_buffer_phys);
692         out_be32(&info->platinum_regs->reg[20].r, 0x1011);      /* select max vram */
693         out_be32(&info->platinum_regs->reg[24].r, 0);   /* switch in vram */
694
695         fbuffer = info->base_frame_buffer;
696         fbuffer[0x100000] = 0x34;
697         fbuffer[0x100008] = 0x0;
698         invalidate_cache(&fbuffer[0x100000]);
699         fbuffer[0x200000] = 0x56;
700         fbuffer[0x200008] = 0x0;
701         invalidate_cache(&fbuffer[0x200000]);
702         fbuffer[0x300000] = 0x78;
703         fbuffer[0x300008] = 0x0;
704         invalidate_cache(&fbuffer[0x300000]);
705         bank0 = 1; /* builtin 1MB vram, always there */
706         bank1 = fbuffer[0x100000] == 0x34;
707         bank2 = fbuffer[0x200000] == 0x56;
708         bank3 = fbuffer[0x300000] == 0x78;
709         info->total_vram = (bank0 + bank1 + bank2 + bank3) * 0x100000;
710         printk(KERN_INFO "Total VRAM = %dMB %d%d%d%d\n", (int) (info->total_vram / 1024 / 1024), bank3, bank2, bank1, bank0);
711
712         /*
713          * Try to determine whether we have an old or a new DACula.
714          */
715         out_8(&info->cmap_regs->addr, 0x40);
716         info->dactype = in_8(&info->cmap_regs->d2);
717         switch (info->dactype) {
718         case 0x3c:
719                 info->clktype = 1;
720                 break;
721         case 0x84:
722                 info->clktype = 0;
723                 break;
724         default:
725                 info->clktype = 0;
726                 printk(KERN_INFO "Unknown DACula type: %x\n", info->dactype);
727                 break;
728         }
729
730         if (!init_platinum(info)) {
731                 kfree(info);
732                 return;
733         }
734
735 #ifdef CONFIG_FB_COMPAT_XPMAC
736         if (!console_fb_info)
737                 console_fb_info = &info->fb_info;
738 #endif
739 }
740
741 /*
742  * Get the monitor sense value.
743  * Note that this can be called before calibrate_delay,
744  * so we can't use udelay.
745  */
746 static int read_platinum_sense(struct fb_info_platinum *info)
747 {
748         volatile struct platinum_regs *platinum_regs = info->platinum_regs;
749         int sense;
750
751         out_be32(&platinum_regs->reg[23].r, 7); /* turn off drivers */
752         __delay(2000);
753         sense = (~in_be32(&platinum_regs->reg[23].r) & 7) << 8;
754
755         /* drive each sense line low in turn and collect the other 2 */
756         out_be32(&platinum_regs->reg[23].r, 3); /* drive A low */
757         __delay(2000);
758         sense |= (~in_be32(&platinum_regs->reg[23].r) & 3) << 4;
759         out_be32(&platinum_regs->reg[23].r, 5); /* drive B low */
760         __delay(2000);
761         sense |= (~in_be32(&platinum_regs->reg[23].r) & 4) << 1;
762         sense |= (~in_be32(&platinum_regs->reg[23].r) & 1) << 2;
763         out_be32(&platinum_regs->reg[23].r, 6); /* drive C low */
764         __delay(2000);
765         sense |= (~in_be32(&platinum_regs->reg[23].r) & 6) >> 1;
766
767         out_be32(&platinum_regs->reg[23].r, 7); /* turn off drivers */
768
769         return sense;
770 }
771
772 /* This routine takes a user-supplied var, and picks the best vmode/cmode from it. */
773 static int platinum_var_to_par(const struct fb_var_screeninfo *var, 
774                                struct fb_par_platinum *par,
775                                const struct fb_info_platinum *info)
776 {
777         if(mac_var_to_vmode(var, &par->vmode, &par->cmode) != 0) {
778                 printk(KERN_ERR "platinum_var_to_par: mac_var_to_vmode unsuccessful.\n");
779                 printk(KERN_ERR "platinum_var_to_par: var->xres = %d\n", var->xres);
780                 printk(KERN_ERR "platinum_var_to_par: var->yres = %d\n", var->yres);
781                 printk(KERN_ERR "platinum_var_to_par: var->xres_virtual = %d\n", var->xres_virtual);
782                 printk(KERN_ERR "platinum_var_to_par: var->yres_virtual = %d\n", var->yres_virtual);
783                 printk(KERN_ERR "platinum_var_to_par: var->bits_per_pixel = %d\n", var->bits_per_pixel);
784                 printk(KERN_ERR "platinum_var_to_par: var->pixclock = %d\n", var->pixclock);
785                 printk(KERN_ERR "platinum_var_to_par: var->vmode = %d\n", var->vmode);
786                 return -EINVAL;
787         }
788
789         if(!platinum_reg_init[par->vmode-1]) {
790                 printk(KERN_ERR "platinum_var_to_par, vmode %d not valid.\n", par->vmode);
791                 return -EINVAL;
792         }
793
794         if (platinum_vram_reqd(info, par->vmode, par->cmode) > info->total_vram) {
795                 printk(KERN_ERR "platinum_var_to_par, not enough ram for vmode %d, cmode %d.\n", par->vmode, par->cmode);
796                 return -EINVAL;
797         }
798
799         par->xres = vmode_attrs[par->vmode-1].hres;
800         par->yres = vmode_attrs[par->vmode-1].vres;
801         par->xoffset = 0;
802         par->yoffset = 0;
803         par->vxres = par->xres;
804         par->vyres = par->yres;
805         
806         return 0;
807 }
808
809 static int platinum_par_to_var(struct fb_var_screeninfo *var,
810                                const struct fb_par_platinum *par,
811                                const struct fb_info_platinum *info)
812 {
813         return mac_vmode_to_var(par->vmode, par->cmode, var);
814 }
815
816 static int platinum_encode_fix(struct fb_fix_screeninfo *fix,
817                                const struct fb_par_platinum *par,
818                                const struct fb_info_platinum *info)
819 {
820         struct platinum_regvals *init;
821
822         init = platinum_reg_init[par->vmode-1];
823
824         memset(fix, 0, sizeof(*fix));
825         strcpy(fix->id, "platinum");
826         fix->smem_start = (info->frame_buffer_phys) + init->fb_offset + 0x20;
827         fix->smem_len = (u32) info->total_vram;
828         fix->mmio_start = (info->platinum_regs_phys);
829         fix->mmio_len = 0x1000;
830         fix->type = FB_TYPE_PACKED_PIXELS;
831         fix->type_aux = 0;
832         fix->ywrapstep = 0;
833         fix->xpanstep = 0;
834         fix->ypanstep = 0;
835         fix->visual = (par->cmode == CMODE_8) ?
836                 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
837         fix->line_length = vmode_attrs[par->vmode-1].hres * (1<<par->cmode) + 0x20;
838         fixup_pitch(fix->line_length, info, par->cmode);
839         
840         return 0;
841 }
842
843
844 /* 
845  * Parse user speficied options (`video=platinumfb:')
846  */
847 int __init platinum_setup(char *options)
848 {
849         char *this_opt;
850
851         if (!options || !*options)
852                 return 0;
853
854         while ((this_opt = strsep(&options, ",")) != NULL) {
855                 if (!strncmp(this_opt, "font:", 5)) {
856                         char *p;
857                         int i;
858
859                         p = this_opt + 5;
860                         for (i = 0; i < sizeof(fontname) - 1; i++)
861                                 if (!*p || *p == ' ' || *p == ',')
862                                         break;
863                         memcpy(fontname, this_opt + 5, i);
864                         fontname[i] = 0;
865                 }
866                 if (!strncmp(this_opt, "vmode:", 6)) {
867                         int vmode = simple_strtoul(this_opt+6, NULL, 0);
868                 if (vmode > 0 && vmode <= VMODE_MAX)
869                         default_vmode = vmode;
870                 } else if (!strncmp(this_opt, "cmode:", 6)) {
871                         int depth = simple_strtoul(this_opt+6, NULL, 0);
872                         switch (depth) {
873                          case 0:
874                          case 8:
875                             default_cmode = CMODE_8;
876                             break;
877                          case 15:
878                          case 16:
879                             default_cmode = CMODE_16;
880                             break;
881                          case 24:
882                          case 32:
883                             default_cmode = CMODE_32;
884                             break;
885                         }
886                 }
887         }
888         return 0;
889 }
890
891 MODULE_LICENSE("GPL");