make oldconfig will rebuild these...
[linux-2.4.21-pre4.git] / drivers / video / valkyriefb.c
1 /*
2  *  valkyriefb.c -- frame buffer device for the PowerMac 'valkyrie' display
3  *
4  *  Created 8 August 1998 by 
5  *  Martin Costabel <costabel@wanadoo.fr> and Kevin Schoedel
6  *
7  *  Vmode-switching changes and vmode 15/17 modifications created 29 August
8  *  1998 by Barry K. Nathan <barryn@pobox.com>.
9  *
10  *  Ported to m68k Macintosh by David Huggins-Daines <dhd@debian.org>
11  *
12  *  Derived directly from:
13  *
14  *   controlfb.c -- frame buffer device for the PowerMac 'control' display
15  *   Copyright (C) 1998 Dan Jacobowitz <dan@debian.org>
16  *
17  *   pmc-valkyrie.c -- Console support for PowerMac "valkyrie" display adaptor.
18  *   Copyright (C) 1997 Paul Mackerras.
19  *
20  *  and indirectly:
21  *
22  *  Frame buffer structure from:
23  *    drivers/video/chipsfb.c -- frame buffer device for
24  *    Chips & Technologies 65550 chip.
25  *
26  *    Copyright (C) 1998 Paul Mackerras
27  *
28  *    This file is derived from the Powermac "chips" driver:
29  *    Copyright (C) 1997 Fabio Riccardi.
30  *    And from the frame buffer device for Open Firmware-initialized devices:
31  *    Copyright (C) 1997 Geert Uytterhoeven.
32  *
33  *  Hardware information from:
34  *    control.c: Console support for PowerMac "control" display adaptor.
35  *    Copyright (C) 1996 Paul Mackerras
36  *
37  *  This file is subject to the terms and conditions of the GNU General Public
38  *  License. See the file COPYING in the main directory of this archive for
39  *  more details.
40  */
41
42 #include <linux/config.h>
43 #include <linux/module.h>
44 #include <linux/kernel.h>
45 #include <linux/errno.h>
46 #include <linux/string.h>
47 #include <linux/mm.h>
48 #include <linux/tty.h>
49 #include <linux/slab.h>
50 #include <linux/vmalloc.h>
51 #include <linux/delay.h>
52 #include <linux/interrupt.h>
53 #include <linux/fb.h>
54 #include <linux/selection.h>
55 #include <linux/init.h>
56 #include <linux/pci.h>
57 #include <linux/nvram.h>
58 #ifdef CONFIG_FB_COMPAT_XPMAC
59 #include <asm/vc_ioctl.h>
60 #endif
61 #include <linux/adb.h>
62 #include <linux/cuda.h>
63 #include <asm/io.h>
64 #ifdef CONFIG_MAC
65 #include <asm/bootinfo.h>
66 #include <asm/macintosh.h>
67 #else
68 #include <asm/prom.h>
69 #endif
70 #include <asm/pgtable.h>
71
72 #include <video/fbcon.h>
73 #include <video/fbcon-cfb8.h>
74 #include <video/fbcon-cfb16.h>
75 #include <video/macmodes.h>
76
77 #include "valkyriefb.h"
78
79 static int can_soft_blank = 1;
80
81 #ifdef CONFIG_MAC
82 /* We don't yet have functions to read the PRAM... perhaps we can
83    adapt them from the PPC code? */
84 static int default_vmode = VMODE_640_480_67;
85 static int default_cmode = CMODE_8;
86 #else
87 static int default_vmode = VMODE_NVRAM;
88 static int default_cmode = CMODE_NVRAM;
89 #endif
90 static char fontname[40] __initdata = { 0 };
91
92 static int currcon = 0;
93 static int switching = 0;
94
95 struct fb_par_valkyrie {
96         int     vmode, cmode;
97         int     xres, yres;
98         int     vxres, vyres;
99         int     xoffset, yoffset;
100 };
101
102 struct fb_info_valkyrie {
103         struct fb_info                  info;
104         struct fb_fix_screeninfo        fix;
105         struct fb_var_screeninfo        var;
106         struct display                  disp;
107         struct fb_par_valkyrie          par;
108         struct {
109             __u8 red, green, blue;
110         }                       palette[256];
111         
112         struct cmap_regs        *cmap_regs;
113         unsigned long           cmap_regs_phys;
114         
115         struct valkyrie_regs    *valkyrie_regs;
116         unsigned long           valkyrie_regs_phys;
117         
118         __u8                    *frame_buffer;
119         unsigned long           frame_buffer_phys;
120         
121         int                     sense;
122         unsigned long           total_vram;
123 #ifdef FBCON_HAS_CFB16
124         u16 fbcon_cfb16_cmap[16];
125 #endif
126 };
127
128 /*
129  * Exported functions
130  */
131 int valkyriefb_init(void);
132 int valkyriefb_setup(char*);
133
134 static int valkyrie_get_fix(struct fb_fix_screeninfo *fix, int con,
135                          struct fb_info *info);
136 static int valkyrie_get_var(struct fb_var_screeninfo *var, int con,
137                          struct fb_info *info);
138 static int valkyrie_set_var(struct fb_var_screeninfo *var, int con,
139                          struct fb_info *info);
140 static int valkyrie_get_cmap(struct fb_cmap *cmap, int kspc, int con,
141                           struct fb_info *info);
142 static int valkyrie_set_cmap(struct fb_cmap *cmap, int kspc, int con,
143                           struct fb_info *info);
144
145 static int read_valkyrie_sense(struct fb_info_valkyrie *p);
146 static inline int valkyrie_vram_reqd(int video_mode, int color_mode);
147 static void set_valkyrie_clock(unsigned char *params);
148 static void valkyrie_set_par(const struct fb_par_valkyrie *p, struct fb_info_valkyrie *info);
149 static inline int valkyrie_par_to_var(struct fb_par_valkyrie *par, struct fb_var_screeninfo *var);
150 static int valkyrie_var_to_par(struct fb_var_screeninfo *var,
151         struct fb_par_valkyrie *par, const struct fb_info *fb_info);
152
153 static void valkyrie_init_info(struct fb_info *info, struct fb_info_valkyrie *p);
154 static void valkyrie_par_to_display(struct fb_par_valkyrie *par,
155   struct display *disp, struct fb_fix_screeninfo *fix, struct fb_info_valkyrie *p);
156 static void valkyrie_init_display(struct display *disp);
157 static void valkyrie_par_to_fix(struct fb_par_valkyrie *par, struct fb_fix_screeninfo *fix,
158         struct fb_info_valkyrie *p);
159 static void valkyrie_init_fix(struct fb_fix_screeninfo *fix, struct fb_info_valkyrie *p);
160
161 static struct fb_ops valkyriefb_ops = {
162         owner:          THIS_MODULE,
163         fb_get_fix:     valkyrie_get_fix,
164         fb_get_var:     valkyrie_get_var,
165         fb_set_var:     valkyrie_set_var,
166         fb_get_cmap:    valkyrie_get_cmap,
167         fb_set_cmap:    valkyrie_set_cmap,
168 };
169
170 static int valkyriefb_getcolreg(u_int regno, u_int *red, u_int *green,
171                              u_int *blue, u_int *transp, struct fb_info *info);
172 static int valkyriefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
173                              u_int transp, struct fb_info *info);
174 static void do_install_cmap(int con, struct fb_info *info);
175
176 static int valkyrie_get_fix(struct fb_fix_screeninfo *fix, int con,
177                          struct fb_info *info)
178 {
179         struct fb_info_valkyrie *cp = (struct fb_info_valkyrie *) info;
180
181         *fix = cp->fix;
182         return 0;
183 }
184
185 static int valkyrie_get_var(struct fb_var_screeninfo *var, int con,
186                          struct fb_info *info)
187 {
188         struct fb_info_valkyrie *cp = (struct fb_info_valkyrie *) info;
189
190         *var = cp->var;
191         return 0;
192 }
193
194 /* Sets everything according to var */
195 static int valkyrie_set_var(struct fb_var_screeninfo *var, int con,
196                          struct fb_info *info)
197 {
198         struct fb_info_valkyrie *p = (struct fb_info_valkyrie *) info;
199         struct display *disp;
200         struct fb_par_valkyrie par;
201         int depthchange, err;
202
203         disp = (con >= 0) ? &fb_display[con] : &p->disp;
204         if ((err = valkyrie_var_to_par(var, &par, info))) {
205                  /* printk (KERN_ERR "Error in valkyrie_set_var, calling valkyrie_var_to_par: %d.\n", err); */
206                 return err;
207         }
208         
209         if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) {
210                 /* printk(KERN_ERR "Not activating, in valkyrie_set_var.\n"); */
211                 valkyrie_par_to_var(&par, var);
212                 return 0;
213         }
214
215         /*
216          * I know, we want to use fb_display[con], but grab certain info
217          * from p->var instead.
218          */
219 #define DIRTY(x) (p->var.x != var->x)
220         depthchange = DIRTY(bits_per_pixel);
221         /* adding "&& !DIRTY(pixclock)" corrects vmode-switching problems */
222         if(!DIRTY(xres) && !DIRTY(yres) && !DIRTY(xres_virtual) &&
223            !DIRTY(yres_virtual) && !DIRTY(bits_per_pixel) && !DIRTY(pixclock)) {
224                 valkyrie_par_to_var(&par, var);
225                 p->var = disp->var = *var;
226                 return 0;
227         }
228
229         p->par = par;
230         valkyrie_par_to_var(&par, var);
231         p->var = *var;
232         valkyrie_par_to_fix(&par, &p->fix, p);
233         valkyrie_par_to_display(&par, disp, &p->fix, p);
234         p->disp = *disp;
235         
236         if (info->changevar && !switching) {
237                 /* Don't want to do this if just switching consoles. */
238                 (*info->changevar)(con);
239         }
240         if (con == currcon)
241                 valkyrie_set_par(&par, p);
242         if (depthchange)
243                 if ((err = fb_alloc_cmap(&disp->cmap, 0, 0)))
244                         return err;
245         if (depthchange || switching)
246                 do_install_cmap(con, info);
247         return 0;
248 }
249
250 static int valkyrie_get_cmap(struct fb_cmap *cmap, int kspc, int con,
251                           struct fb_info *info)
252 {
253         if (con == currcon)     {
254                 /* current console? */
255                 return fb_get_cmap(cmap, kspc, valkyriefb_getcolreg, info);
256         }
257         if (fb_display[con].cmap.len) { /* non default colormap? */
258                 fb_copy_cmap(&fb_display[con].cmap, cmap, kspc? 0: 2);
259         }
260         else {
261                 int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
262                 fb_copy_cmap(fb_default_cmap(size), cmap, kspc ? 0 : 2);
263         }
264         return 0;
265 }
266
267 static int valkyrie_set_cmap(struct fb_cmap *cmap, int kspc, int con,
268                          struct fb_info *info)
269 {
270         struct display *disp = &fb_display[con];
271         int err;
272
273         if (disp->cmap.len == 0) {
274                 int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
275                 err = fb_alloc_cmap(&disp->cmap, size, 0);
276                 if (err) {
277                         return err;
278                 }
279         }
280
281         if (con == currcon) {
282                 return fb_set_cmap(cmap, kspc, valkyriefb_setcolreg, info);
283         }
284         fb_copy_cmap(cmap, &disp->cmap, kspc ? 0 : 1);
285         return 0;
286 }
287
288 static int valkyriefb_switch(int con, struct fb_info *fb)
289 {
290         struct fb_info_valkyrie *info = (struct fb_info_valkyrie *) fb;
291         struct fb_par_valkyrie par;
292
293         if (fb_display[currcon].cmap.len)
294                 fb_get_cmap(&fb_display[currcon].cmap, 1, valkyriefb_getcolreg,
295                             fb);
296         currcon = con;
297 #if 1
298         valkyrie_var_to_par(&fb_display[currcon].var, &par, fb);
299         valkyrie_set_par(&par, info);
300         do_install_cmap(con, fb);
301 #else
302         /* I see no reason not to do this.  Minus info->changevar(). */
303         /* DOH.  This makes valkyrie_set_var compare, you guessed it, */
304         /* fb_display[con].var (first param), and fb_display[con].var! */
305         /* Perhaps I just fixed that... */
306         switching = 1;
307         valkyrie_set_var(&fb_display[con].var, con, info);
308         switching = 0;
309 #endif
310         return 0;
311 }
312
313 static int valkyriefb_updatevar(int con, struct fb_info *info)
314 {
315         return 0;
316 }
317
318 static void valkyriefb_blank(int blank_mode, struct fb_info *info)
319 {
320 /*
321  *  Blank the screen if blank_mode != 0, else unblank. If blank_mode == NULL
322  *  then the caller blanks by setting the CLUT (Color Look Up Table) to all
323  *  black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due
324  *  to e.g. a video mode which doesn't support it. Implements VESA suspend
325  *  and powerdown modes on hardware that supports disabling hsync/vsync:
326  *    blank_mode == 2: suspend vsync
327  *    blank_mode == 3: suspend hsync
328  *    blank_mode == 4: powerdown
329  */
330         struct fb_info_valkyrie *p = (struct fb_info_valkyrie *) info;
331         struct valkyrie_regvals *init;
332         unsigned char vmode;
333
334         if (p->disp.can_soft_blank
335          && ((vmode = p->par.vmode) > 0)
336          && (vmode <= VMODE_MAX)
337          && ((init = valkyrie_reg_init[vmode - 1]) != NULL)) {
338                 if (blank_mode)
339                         --blank_mode;
340                 switch (blank_mode) {
341                 default:        /* unblank */
342                         out_8(&p->valkyrie_regs->mode.r, init->mode);
343                         break;
344                 case VESA_VSYNC_SUSPEND:
345                 case VESA_HSYNC_SUSPEND:
346                         /*
347                          * [kps] Value extracted from MacOS. I don't know
348                          * whether this bit disables hsync or vsync, or
349                          * whether the hardware can do the other as well.
350                          */
351                         out_8(&p->valkyrie_regs->mode.r, init->mode | 0x40);
352                         break;
353                 case VESA_POWERDOWN:
354                         out_8(&p->valkyrie_regs->mode.r, 0x66);
355                         break;
356                 }
357         }
358 }
359
360 static int valkyriefb_getcolreg(u_int regno, u_int *red, u_int *green,
361                              u_int *blue, u_int *transp, struct fb_info *info)
362 {
363         struct fb_info_valkyrie *p = (struct fb_info_valkyrie *) info;
364
365         if (regno > 255)
366                 return 1;
367         *red = (p->palette[regno].red<<8) | p->palette[regno].red;
368         *green = (p->palette[regno].green<<8) | p->palette[regno].green;
369         *blue = (p->palette[regno].blue<<8) | p->palette[regno].blue;
370
371         return 0;
372 }
373
374 static int valkyriefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
375                              u_int transp, struct fb_info *info)
376 {
377         struct fb_info_valkyrie *p = (struct fb_info_valkyrie *) info;
378         volatile struct cmap_regs *cmap_regs = p->cmap_regs;
379
380
381         if (regno > 255)
382                 return 1;
383         red >>= 8;
384         green >>= 8;
385         blue >>= 8;
386         p->palette[regno].red = red;
387         p->palette[regno].green = green;
388         p->palette[regno].blue = blue;
389
390         /* tell clut which address to fill */
391         out_8(&p->cmap_regs->addr, regno);
392         udelay(1);
393         /* send one color channel at a time */
394         out_8(&cmap_regs->lut, red);
395         out_8(&cmap_regs->lut, green);
396         out_8(&cmap_regs->lut, blue);
397
398         if (regno < 16) {
399 #ifdef FBCON_HAS_CFB16
400                 p->fbcon_cfb16_cmap[regno] = (regno << 10) | (regno << 5) | regno;
401 #endif
402         }
403
404         return 0;
405 }
406
407 static void do_install_cmap(int con, struct fb_info *info)
408 {
409         if (con != currcon)
410                 return;
411         if (fb_display[con].cmap.len) {
412                 fb_set_cmap(&fb_display[con].cmap, 1, valkyriefb_setcolreg,
413                             info);
414         }
415         else {
416                 int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
417                 fb_set_cmap(fb_default_cmap(size), 1, valkyriefb_setcolreg,
418                             info);
419         }
420 }
421
422 #ifdef CONFIG_FB_COMPAT_XPMAC
423 extern struct vc_mode display_info;
424 extern struct fb_info *console_fb_info;
425 #endif /* CONFIG_FB_COMPAT_XPMAC */
426
427 static int valkyrie_vram_reqd(int video_mode, int color_mode)
428 {
429         int pitch;
430         
431         if ((pitch = valkyrie_reg_init[video_mode-1]->pitch[color_mode]) == 0)
432                 pitch = 2 * valkyrie_reg_init[video_mode-1]->pitch[0];
433         return valkyrie_reg_init[video_mode-1]->vres * pitch;
434 }
435
436 static void set_valkyrie_clock(unsigned char *params)
437 {
438         struct adb_request req;
439         int i;
440
441 #ifdef CONFIG_ADB_CUDA
442         for (i = 0; i < 3; ++i) {
443                 cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
444                              0x50, i + 1, params[i]);
445                 while (!req.complete)
446                         cuda_poll();
447         }
448 #endif
449 }
450
451 static void __init init_valkyrie(struct fb_info_valkyrie *p)
452 {
453         struct fb_par_valkyrie *par = &p->par;
454         struct fb_var_screeninfo var;
455         int j, k;
456
457         p->sense = read_valkyrie_sense(p);
458         printk(KERN_INFO "Monitor sense value = 0x%x, ", p->sense);
459
460 #ifdef CONFIG_NVRAM
461         /* Try to pick a video mode out of NVRAM if we have one. */
462         if (default_vmode == VMODE_NVRAM) {
463                 default_vmode = nvram_read_byte(NV_VMODE);
464                 if (default_vmode <= 0
465                  || default_vmode > VMODE_MAX
466                  || !valkyrie_reg_init[default_vmode - 1])
467                         default_vmode = VMODE_CHOOSE;
468         }
469         if (default_cmode == CMODE_NVRAM)
470                 default_cmode = nvram_read_byte(NV_CMODE);
471 #endif
472         if (default_vmode == VMODE_CHOOSE)
473                 default_vmode = mac_map_monitor_sense(p->sense);
474         if (!valkyrie_reg_init[default_vmode - 1])
475                 default_vmode = VMODE_640_480_67;
476
477         /*
478          * Reduce the pixel size if we don't have enough VRAM or bandwitdh.
479          */
480         if (default_cmode < CMODE_8
481          || default_cmode > CMODE_16
482          || valkyrie_reg_init[default_vmode-1]->pitch[default_cmode] == 0
483          || valkyrie_vram_reqd(default_vmode, default_cmode) > p->total_vram)
484                 default_cmode = CMODE_8;
485         
486         printk(KERN_INFO "using video mode %d and color mode %d.\n", default_vmode, default_cmode);
487
488         mac_vmode_to_var(default_vmode, default_cmode, &var);
489         if (valkyrie_var_to_par(&var, par, &p->info)) {
490             printk(KERN_ERR "valkyriefb: can't set default video mode\n");
491             return ;
492         }
493         
494         valkyrie_init_fix(&p->fix, p);
495         valkyrie_par_to_fix(&p->par, &p->fix, p);
496         valkyrie_par_to_var(&p->par, &p->var);
497         valkyrie_init_display(&p->disp);
498         valkyrie_par_to_display(&p->par, &p->disp, &p->fix, p);
499         valkyrie_init_info(&p->info, p);
500
501         /* Initialize colormap */
502         for (j = 0; j < 16; j++) {
503                 k = color_table[j];
504                 p->palette[j].red = default_red[k];
505                 p->palette[j].green = default_grn[k];
506                 p->palette[j].blue = default_blu[k];
507         }
508         
509         valkyrie_set_var (&var, -1, &p->info);
510
511         if (register_framebuffer(&p->info) < 0) {
512                 kfree(p);
513                 return;
514         }
515         
516         printk(KERN_INFO "fb%d: valkyrie frame buffer device\n", GET_FB_IDX(p->info.node));     
517 }
518
519 static void valkyrie_set_par(const struct fb_par_valkyrie *par,
520                              struct fb_info_valkyrie *p)
521 {
522         struct valkyrie_regvals *init;
523         volatile struct valkyrie_regs *valkyrie_regs = p->valkyrie_regs;
524         int vmode, cmode;
525         
526         vmode = par->vmode;
527         cmode = par->cmode;
528         
529         if (vmode <= 0
530          || vmode > VMODE_MAX
531          || (init = valkyrie_reg_init[vmode - 1]) == NULL)
532                 panic("valkyrie: display mode %d not supported", vmode);
533
534         /* Reset the valkyrie */
535         out_8(&valkyrie_regs->status.r, 0);
536         udelay(100);
537
538         /* Initialize display timing registers */
539         out_8(&valkyrie_regs->mode.r, init->mode | 0x80);
540         out_8(&valkyrie_regs->depth.r, cmode + 3);
541         set_valkyrie_clock(init->clock_params);
542         udelay(100);
543
544         /* Turn on display */
545         out_8(&valkyrie_regs->mode.r, init->mode);
546
547 #ifdef CONFIG_FB_COMPAT_XPMAC
548         /* And let the world know the truth. */
549         if (!console_fb_info || console_fb_info == &p->info) {
550                 display_info.height = p->var.yres;
551                 display_info.width = p->var.xres;
552                 display_info.depth = (cmode == CMODE_16) ? 16 : 8;
553                 display_info.pitch = p->fix.line_length;
554                 display_info.mode = vmode;
555                 strncpy(display_info.name, "valkyrie",
556                         sizeof(display_info.name));
557                 display_info.fb_address = p->frame_buffer_phys + 0x1000;
558                 display_info.cmap_adr_address = p->cmap_regs_phys;
559                 display_info.cmap_data_address = p->cmap_regs_phys + 8;
560                 display_info.disp_reg_address = p->valkyrie_regs_phys;
561                 console_fb_info = &p->info;
562         }
563 #endif /* CONFIG_FB_COMPAT_XPMAC */
564 }
565
566 int __init valkyriefb_init(void)
567 {
568         struct fb_info_valkyrie *p;
569         unsigned long frame_buffer_phys, cmap_regs_phys, flags;
570
571 #ifdef CONFIG_MAC
572         if (!MACH_IS_MAC)
573                 return 0;
574         if (!(mac_bi_data.id == MAC_MODEL_Q630
575               /* I'm not sure about this one */
576             || mac_bi_data.id == MAC_MODEL_P588))
577                 return 0;
578
579         /* Hardcoded addresses... welcome to 68k Macintosh country :-) */
580         frame_buffer_phys = 0xf9000000;
581         cmap_regs_phys = 0x50f24000;
582         flags = IOMAP_NOCACHE_SER; /* IOMAP_WRITETHROUGH?? */
583 #else /* ppc (!CONFIG_MAC) */
584         struct device_node *dp;
585
586         dp = find_devices("valkyrie");
587         if (dp == 0)
588                 return 0;
589
590         if(dp->n_addrs != 1) {
591                 printk(KERN_ERR "expecting 1 address for valkyrie (got %d)", dp->n_addrs);
592                 return 0;
593         }       
594
595         frame_buffer_phys = dp->addrs[0].address;
596         cmap_regs_phys = dp->addrs[0].address+0x304000;
597         flags = _PAGE_WRITETHRU;
598 #endif /* ppc (!CONFIG_MAC) */
599
600         p = kmalloc(sizeof(*p), GFP_ATOMIC);
601         if (p == 0)
602                 return 0;
603         memset(p, 0, sizeof(*p));
604
605         /* Map in frame buffer and registers */
606         if (!request_mem_region(frame_buffer_phys, 0x100000, "valkyriefb")) {
607                 kfree(p);
608                 return 0;
609         }
610         p->total_vram = 0x100000;
611         p->frame_buffer_phys  = frame_buffer_phys;
612         p->frame_buffer = __ioremap(frame_buffer_phys, p->total_vram, flags);
613         p->cmap_regs_phys = cmap_regs_phys;
614         p->cmap_regs = ioremap(p->cmap_regs_phys, 0x1000);
615         p->valkyrie_regs_phys = cmap_regs_phys+0x6000;
616         p->valkyrie_regs = ioremap(p->valkyrie_regs_phys, 0x1000);
617         init_valkyrie(p);
618         return 0;
619 }
620
621 /*
622  * Get the monitor sense value.
623  */
624 static int read_valkyrie_sense(struct fb_info_valkyrie *p)
625 {
626         int sense, in;
627
628     out_8(&p->valkyrie_regs->msense.r, 0);   /* release all lines */
629     __delay(20000);
630     sense = ((in = in_8(&p->valkyrie_regs->msense.r)) & 0x70) << 4;
631     /* drive each sense line low in turn and collect the other 2 */
632     out_8(&p->valkyrie_regs->msense.r, 4);   /* drive A low */
633     __delay(20000);
634     sense |= ((in = in_8(&p->valkyrie_regs->msense.r)) & 0x30);
635     out_8(&p->valkyrie_regs->msense.r, 2);   /* drive B low */
636     __delay(20000);
637     sense |= ((in = in_8(&p->valkyrie_regs->msense.r)) & 0x40) >> 3;
638         sense |= (in & 0x10) >> 2;
639     out_8(&p->valkyrie_regs->msense.r, 1);   /* drive C low */
640     __delay(20000);
641     sense |= ((in = in_8(&p->valkyrie_regs->msense.r)) & 0x60) >> 5;
642
643     out_8(&p->valkyrie_regs->msense.r, 7);
644
645         return sense;
646 }
647
648 /*
649  * This routine takes a user-supplied var,
650  * and picks the best vmode/cmode from it.
651  */
652 static int valkyrie_var_to_par(struct fb_var_screeninfo *var,
653         struct fb_par_valkyrie *par, const struct fb_info *fb_info)
654
655 /* [bkn] I did a major overhaul of this function.
656  *
657  * Much of the old code was "swiped by jonh from atyfb.c". Because
658  * macmodes has mac_var_to_vmode, I felt that it would be better to
659  * rework this function to use that, instead of reinventing the wheel to
660  * add support for vmode 17. This was reinforced by the fact that
661  * the previously swiped atyfb.c code is no longer there.
662  *
663  * So, I swiped and adapted platinum_var_to_par (from platinumfb.c), replacing
664  * most, but not all, of the old code in the process. One side benefit of
665  * swiping the platinumfb code is that we now have more comprehensible error
666  * messages when a vmode/cmode switch fails. (Most of the error messages are
667  * platinumfb.c, but I added two of my own, and I also changed some commas
668  * into colons to make the messages more consistent with other Linux error
669  * messages.) In addition, I think the new code *might* fix some vmode-
670  * switching oddities, but I'm not sure.
671  *
672  * There may be some more opportunities for cleanup in here, but this is a
673  * good start...
674  */
675
676 {
677         int bpp = var->bits_per_pixel;
678         struct valkyrie_regvals *init;
679         struct fb_info_valkyrie *p = (struct fb_info_valkyrie *) fb_info;
680
681
682         if(mac_var_to_vmode(var, &par->vmode, &par->cmode) != 0) {
683                 printk(KERN_ERR "valkyrie_var_to_par: %dx%dx%d unsuccessful.\n",var->xres,var->yres,var->bits_per_pixel);
684                 return -EINVAL;
685         }
686
687         /* Check if we know about the wanted video mode */
688         if(!valkyrie_reg_init[par->vmode-1]) {
689                 printk(KERN_ERR "valkyrie_var_to_par: vmode %d not valid.\n", par->vmode);
690                 return -EINVAL;
691         }
692
693         par->xres = var->xres;
694         par->yres = var->yres;
695         par->xoffset = 0;
696         par->yoffset = 0;
697         par->vxres = par->xres;
698         par->vyres = par->yres;
699         
700         if (var->xres_virtual > var->xres || var->yres_virtual > var->yres
701                 || var->xoffset != 0 || var->yoffset != 0) {
702                 return -EINVAL;
703         }
704
705         if (bpp <= 8)
706                 par->cmode = CMODE_8;
707         else if (bpp <= 16)
708                 par->cmode = CMODE_16;
709         else {
710                 printk(KERN_ERR "valkyrie_var_to_par: cmode %d not supported.\n", par->cmode);
711                 return -EINVAL;
712         }
713
714         init = valkyrie_reg_init[par->vmode-1];
715         if (init->pitch[par->cmode] == 0) {
716                 printk(KERN_ERR "valkyrie_var_to_par: vmode %d does not support cmode %d.\n", par->vmode, par->cmode);
717                 return -EINVAL;
718         }
719
720         if (valkyrie_vram_reqd(par->vmode, par->cmode) > p->total_vram) {
721                 printk(KERN_ERR "valkyrie_var_to_par: not enough ram for vmode %d, cmode %d.\n", par->vmode, par->cmode);
722                 return -EINVAL;
723         }
724
725         return 0;
726 }
727
728 static int valkyrie_par_to_var(struct fb_par_valkyrie *par, struct fb_var_screeninfo *var)
729 {
730         return mac_vmode_to_var(par->vmode, par->cmode, var);
731 }
732
733 static void valkyrie_init_fix(struct fb_fix_screeninfo *fix, struct fb_info_valkyrie *p)
734 {
735         memset(fix, 0, sizeof(*fix));
736         strcpy(fix->id, "valkyrie");
737         fix->mmio_start = p->valkyrie_regs_phys;
738         fix->mmio_len = sizeof(struct valkyrie_regs);
739         fix->type = FB_TYPE_PACKED_PIXELS;
740         
741         fix->type_aux = 0;
742         fix->ywrapstep = 0;
743         fix->ypanstep = 0;
744         fix->xpanstep = 0;
745         
746 }
747
748 /* Fix must already be inited above */
749 static void valkyrie_par_to_fix(struct fb_par_valkyrie *par,
750         struct fb_fix_screeninfo *fix,
751         struct fb_info_valkyrie *p)
752 {
753         fix->smem_start = p->frame_buffer_phys + 0x1000;
754 #if 1
755         fix->smem_len = valkyrie_vram_reqd(par->vmode, par->cmode);
756 #else
757         fix->smem_len = p->total_vram;
758 #endif
759         fix->visual = (par->cmode == CMODE_8) ?
760                 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
761         fix->line_length = par->vxres << par->cmode;
762                 /* ywrapstep, xpanstep, ypanstep */
763 }
764
765 static void valkyrie_init_display(struct display *disp)
766 {
767         memset(disp, 0, sizeof(*disp));
768         disp->type = /* fix->type */ FB_TYPE_PACKED_PIXELS;
769         disp->can_soft_blank = can_soft_blank;
770         disp->scrollmode = SCROLL_YREDRAW;
771 }
772
773 static void valkyrie_par_to_display(struct fb_par_valkyrie *par,
774   struct display *disp, struct fb_fix_screeninfo *fix, struct fb_info_valkyrie *p)
775 {
776         disp->var = p->var;
777         disp->screen_base = (char *) p->frame_buffer + 0x1000;
778         disp->visual = fix->visual;
779         disp->line_length = fix->line_length;
780
781         if(disp->scrollmode != SCROLL_YREDRAW) {
782                 printk(KERN_ERR "Scroll mode not YREDRAW in valkyrie_par_to_display\n");
783                 disp->scrollmode = SCROLL_YREDRAW;
784         }
785         switch (par->cmode) {
786 #ifdef FBCON_HAS_CFB8
787                 case CMODE_8:
788                         disp->dispsw = &fbcon_cfb8;
789                         break;
790 #endif
791 #ifdef FBCON_HAS_CFB16
792                 case CMODE_16:
793                         disp->dispsw = &fbcon_cfb16;
794                         disp->dispsw_data = p->fbcon_cfb16_cmap;
795                         break;
796 #endif
797                 default:
798                         disp->dispsw = &fbcon_dummy;
799                         break;
800         }
801 }
802
803 static void __init valkyrie_init_info(struct fb_info *info, struct fb_info_valkyrie *p)
804 {
805         strcpy(info->modename, p->fix.id);
806         info->node = -1;        /* ??? danj */
807         info->fbops = &valkyriefb_ops;
808         info->disp = &p->disp;
809         strcpy(info->fontname, fontname);
810         info->changevar = NULL;
811         info->switch_con = &valkyriefb_switch;
812         info->updatevar = &valkyriefb_updatevar;
813         info->blank = &valkyriefb_blank;
814         info->flags = FBINFO_FLAG_DEFAULT;
815 }
816
817
818 /*
819  * Parse user speficied options (`video=valkyriefb:')
820  */
821 int __init valkyriefb_setup(char *options)
822 {
823         char *this_opt;
824
825         if (!options || !*options)
826                 return 0;
827
828         while ((this_opt = strsep(&options, ",")) != NULL) {
829                 if (!strncmp(this_opt, "font:", 5)) {
830                         char *p;
831                         int i;
832
833                         p = this_opt + 5;
834                         for (i = 0; i < sizeof(fontname) - 1; i++)
835                                 if (!*p || *p == ' ' || *p == ',')
836                                         break;
837                         memcpy(fontname, this_opt + 5, i);
838                         fontname[i] = 0;
839                 }
840                 else if (!strncmp(this_opt, "vmode:", 6)) {
841                         int vmode = simple_strtoul(this_opt+6, NULL, 0);
842                 if (vmode > 0 && vmode <= VMODE_MAX)
843                                 default_vmode = vmode;
844                 }
845                 else if (!strncmp(this_opt, "cmode:", 6)) {
846                         int depth = simple_strtoul(this_opt+6, NULL, 0);
847                         switch (depth) {
848                          case 8:
849                             default_cmode = CMODE_8;
850                             break;
851                          case 15:
852                          case 16:
853                             default_cmode = CMODE_16;
854                             break;
855                         }
856                 }
857                 /* XXX - remove these options once blanking has been tested */
858                 else if (!strncmp(this_opt, "noblank", 7)) {
859                         can_soft_blank = 0;
860                 }
861                 else if (!strncmp(this_opt, "blank", 5)) {
862                         can_soft_blank = 1;
863                 }
864         }
865         return 0;
866 }
867
868 MODULE_LICENSE("GPL");