Linux 2.4.0-test10pre4
[davej-history.git] / drivers / video / virgefb.c
blobed7bad2b17abc34ac98780b401297fac293090cd
1 /*
2 * linux/drivers/video/virgefb.c -- CyberVision64/3D frame buffer device
4 * Copyright (C) 1997 André Heynatz
7 * This file is based on the CyberVision frame buffer device (cyberfb.c):
9 * Copyright (C) 1996 Martin Apel
10 * Geert Uytterhoeven
12 * This file is subject to the terms and conditions of the GNU General Public
13 * License. See the file COPYING in the main directory of this archive
14 * for more details.
17 #undef VIRGEFBDEBUG
19 #include <linux/module.h>
20 #include <linux/kernel.h>
21 #include <linux/errno.h>
22 #include <linux/string.h>
23 #include <linux/mm.h>
24 #include <linux/tty.h>
25 #include <linux/malloc.h>
26 #include <linux/delay.h>
27 #include <linux/zorro.h>
28 #include <linux/fb.h>
29 #include <linux/init.h>
30 #include <asm/uaccess.h>
31 #include <asm/system.h>
32 #include <asm/irq.h>
33 #include <asm/pgtable.h>
34 #include <asm/amigahw.h>
35 #include <asm/io.h>
37 #include <video/s3blit.h>
38 #include <video/fbcon.h>
39 #include <video/fbcon-cfb8.h>
40 #include <video/fbcon-cfb16.h>
43 #ifdef VIRGEFBDEBUG
44 #define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
45 #else
46 #define DPRINTK(fmt, args...)
47 #endif
49 #define arraysize(x) (sizeof(x)/sizeof(*(x)))
51 #if 1
52 #define vgawb_3d(reg,dat) \
53 if (cv3d_on_zorro2) { \
54 *((unsigned char volatile *)((Cyber_vcode_switch_base) + 0x04)) = \
55 (0x01 & 0xffff); asm volatile ("nop"); \
56 } \
57 (*((unsigned char *)(CyberVGARegs + (reg ^ 3))) = dat); \
58 if (cv3d_on_zorro2) { \
59 *((unsigned char volatile *)((Cyber_vcode_switch_base) + 0x04)) = \
60 (0x02 & 0xffff); asm volatile ("nop"); \
62 #define vgaww_3d(reg,dat) \
63 (*((unsigned word *)(CyberVGARegs + (reg ^ 2))) = swab16(dat))
64 #define vgawl_3d(reg,dat) \
65 (*((unsigned long *)(CyberVGARegs + reg)) = swab32(dat))
66 #else
68 * Dunno why this doesn't work at the moment - we'll have to look at
69 * it later.
71 #define vgawb_3d(reg,dat) \
72 (*((unsigned char *)(CyberRegs + 0x8000 + reg)) = dat)
73 #define vgaww_3d(reg,dat) \
74 (*((unsigned word *)(CyberRegs + 0x8000 + reg)) = dat)
75 #define vgawl_3d(reg,dat) \
76 (*((unsigned long *)(CyberRegs + 0x8000 + reg)) = dat)
77 #endif
80 * We asume P5 mapped the big-endian version of these registers.
82 #define wb_3d(reg,dat) \
83 (*((unsigned char volatile *)(CyberRegs + reg)) = dat)
84 #define ww_3d(reg,dat) \
85 (*((unsigned word volatile *)(CyberRegs + reg)) = dat)
86 #define wl_3d(reg,dat) \
87 (*((unsigned long volatile *)(CyberRegs + reg)) = dat)
88 #define rl_3d(reg) \
89 (*((unsigned long volatile *)(CyberRegs + reg)))
91 #define Select_Zorro2_FrameBuffer(flag) \
92 do { \
93 *((unsigned char volatile *)((Cyber_vcode_switch_base) + 0x08)) = \
94 ((flag * 0x40) & 0xffff); asm volatile ("nop"); \
95 } while (0)
97 * may be needed when we initialize the board?
98 * 8bit: flag = 2, 16 bit: flag = 1, 24/32bit: flag = 0
99 * _when_ the board is initialized, depth doesnt matter, we allways write
100 * to the same address, aperture seems not to matter on Z2.
103 struct virgefb_par {
104 int xres;
105 int yres;
106 int bpp;
107 int accel;
110 static struct virgefb_par current_par;
112 static int current_par_valid = 0;
113 static int currcon = 0;
115 static struct display disp;
116 static struct fb_info fb_info;
118 static union {
119 #ifdef FBCON_HAS_CFB16
120 u16 cfb16[16];
121 #endif
122 } fbcon_cmap;
125 * Switch for Chipset Independency
128 static struct fb_hwswitch {
130 /* Initialisation */
132 int (*init)(void);
134 /* Display Control */
136 int (*encode_fix)(struct fb_fix_screeninfo *fix, struct virgefb_par *par);
137 int (*decode_var)(struct fb_var_screeninfo *var, struct virgefb_par *par);
138 int (*encode_var)(struct fb_var_screeninfo *var, struct virgefb_par *par);
139 int (*getcolreg)(u_int regno, u_int *red, u_int *green, u_int *blue,
140 u_int *transp, struct fb_info *info);
141 int (*setcolreg)(u_int regno, u_int red, u_int green, u_int blue,
142 u_int transp, struct fb_info *info);
143 void (*blank)(int blank);
144 } *fbhw;
146 static int blit_maybe_busy = 0;
149 * Frame Buffer Name
152 static char virgefb_name[16] = "Cybervision/3D";
156 * Cybervision Graphics Board
159 #define VIRGE8_WIDTH 1152
160 #define VIRGE8_HEIGHT 886
161 #define VIRGE8_PIXCLOCK 12500 /* ++Geert: Just a guess */
163 #if 1
164 #define VIRGE16_WIDTH 800
165 #define VIRGE16_HEIGHT 600
166 #endif
167 #define VIRGE16_PIXCLOCK 25000 /* ++Geert: Just a guess */
170 static unsigned char Cyber_colour_table [256][3];
171 static unsigned long CyberMem;
172 static unsigned long CyberSize;
173 static volatile char *CyberRegs;
174 static volatile unsigned long CyberVGARegs; /* ++Andre: for CV64/3D, see macros at the beginning */
175 static unsigned long CyberMem_phys;
176 static unsigned long CyberRegs_phys;
177 static unsigned long Cyber_register_base;
178 static unsigned long Cyber_vcode_switch_base;
179 static unsigned char cv3d_on_zorro2;
181 #define CYBMEM_OFFSET_8 0x800000 /* offsets from start of video - */
182 #define CYBMEM_OFFSET_16 0x400000 /* ram to appropriate aperture */
185 * Predefined Video Modes
188 static struct {
189 const char *name;
190 struct fb_var_screeninfo var;
191 } virgefb_predefined[] __initdata = {
193 "640x480-8", { /* Cybervision 8 bpp */
194 640, 480, 640, 480, 0, 0, 8, 0,
195 {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
196 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
197 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
199 }, {
200 "800x600-8", { /* Cybervision 8 bpp */
201 800, 600, 800, 600, 0, 0, 8, 0,
202 {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
203 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
204 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
206 }, {
207 "1024x768-8", { /* Cybervision 8 bpp */
208 1024, 768, 1024, 768, 0, 0, 8, 0,
209 {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
210 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
211 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
213 }, {
214 "1152x886-8", { /* Cybervision 8 bpp */
215 1152, 886, 1152, 886, 0, 0, 8, 0,
216 {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
217 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
218 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
220 }, {
221 "1280x1024-8", { /* Cybervision 8 bpp */
222 1280, 1024, 1280, 1024, 0, 0, 8, 0,
223 {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
224 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
225 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
227 }, {
228 "1600x1200-8", { /* Cybervision 8 bpp */
229 1600, 1200, 1600, 1200, 0, 0, 8, 0,
230 {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
231 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
232 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
234 }, {
235 "640x480-16", { /* Cybervision 16 bpp */
236 640, 480, 640, 480, 0, 0, 16, 0,
237 {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
238 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE16_PIXCLOCK, 64, 96, 35, 12, 112, 2,
239 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
241 }, {
242 "800x600-16", { /* Cybervision 16 bpp */
243 800, 600, 800, 600, 0, 0, 16, 0,
244 {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
245 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE16_PIXCLOCK, 64, 96, 35, 12, 112, 2,
246 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
248 }, {
249 "1024x768-16", { /* Cybervision 16 bpp */
250 1024, 768, 1024, 768, 0, 0, 16, 0,
251 {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
252 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE16_PIXCLOCK, 64, 96, 35, 12, 112, 2,
253 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
255 }, {
256 "1152x886-16", { /* Cybervision 16 bpp */
257 1152, 886, 1152, 886, 0, 0, 16, 0,
258 {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
259 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE16_PIXCLOCK, 64, 96, 35, 12, 112, 2,
260 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
262 }, {
263 "1280x1024-16", { /* Cybervision 16 bpp */
264 1280, 1024, 1280, 1024, 0, 0, 16, 0,
265 {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
266 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE16_PIXCLOCK, 64, 96, 35, 12, 112, 2,
267 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
269 }, {
270 "1600x1200-16", { /* Cybervision 16 bpp */
271 1600, 1200, 1600, 1200, 0, 0, 16, 0,
272 {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
273 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE16_PIXCLOCK, 64, 96, 35, 12, 112, 2,
274 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
280 #define NUM_TOTAL_MODES arraysize(virgefb_predefined)
283 static int Cyberfb_inverse = 0;
286 * Some default modes
289 #define VIRGE8_DEFMODE (1)
290 #define VIRGE16_DEFMODE (7)
292 static struct fb_var_screeninfo virgefb_default;
296 * Interface used by the world
299 int virgefb_setup(char*);
301 static int virgefb_get_fix(struct fb_fix_screeninfo *fix, int con, struct
302 fb_info *info);
303 static int virgefb_get_var(struct fb_var_screeninfo *var, int con, struct
304 fb_info *info);
305 static int virgefb_set_var(struct fb_var_screeninfo *var, int con, struct
306 fb_info *info);
307 static int virgefb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
308 struct fb_info *info);
309 static int virgefb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
310 struct fb_info *info);
314 * Interface to the low level console driver
317 int virgefb_init(void);
318 static int Cyberfb_switch(int con, struct fb_info *info);
319 static int Cyberfb_updatevar(int con, struct fb_info *info);
320 static void Cyberfb_blank(int blank, struct fb_info *info);
324 * Text console acceleration
327 #ifdef FBCON_HAS_CFB8
328 static struct display_switch fbcon_virge8;
329 #endif
331 #ifdef FBCON_HAS_CFB16
332 static struct display_switch fbcon_virge16;
333 #endif
336 * Hardware Specific Routines
339 static int Cyber_init(void);
340 static int Cyber_encode_fix(struct fb_fix_screeninfo *fix,
341 struct virgefb_par *par);
342 static int Cyber_decode_var(struct fb_var_screeninfo *var,
343 struct virgefb_par *par);
344 static int Cyber_encode_var(struct fb_var_screeninfo *var,
345 struct virgefb_par *par);
346 static int Cyber_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
347 u_int *transp, struct fb_info *info);
348 static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
349 u_int transp, struct fb_info *info);
350 static void Cyber_blank(int blank);
354 * Internal routines
357 static void virgefb_get_par(struct virgefb_par *par);
358 static void virgefb_set_par(struct virgefb_par *par);
359 static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive);
360 static void do_install_cmap(int con, struct fb_info *info);
361 static void virgefb_set_disp(int con, struct fb_info *info);
362 static int get_video_mode(const char *name);
365 /* -------------------- Hardware specific routines ------------------------- */
369 * Initialization
371 * Set the default video mode for this chipset. If a video mode was
372 * specified on the command line, it will override the default mode.
375 static int Cyber_init(void)
377 int i;
379 for (i = 0; i < 256; i++)
381 Cyber_colour_table [i][0] = i;
382 Cyber_colour_table [i][1] = i;
383 Cyber_colour_table [i][2] = i;
387 * Just clear the thing for the biggest mode.
389 * ++Andre, TODO: determine size first, then clear all memory
390 * (the 3D penguin might need texture memory :-) )
393 if (cv3d_on_zorro2) {
394 CyberSize = 0x00380000; /* 3.5 MB , we need some space for the registers? */
395 } else {
396 CyberSize = 0x00400000; /* 4 MB */
399 memset ((char*)CyberMem, 0, CyberSize);
401 /* Disable hardware cursor */
402 vgawb_3d(0x3c8, 255);
403 vgawb_3d(0x3c9, 56);
404 vgawb_3d(0x3c9, 100);
405 vgawb_3d(0x3c9, 160);
407 vgawb_3d(0x3c8, 254);
408 vgawb_3d(0x3c9, 0);
409 vgawb_3d(0x3c9, 0);
410 vgawb_3d(0x3c9, 0);
412 /* Disable hardware cursor */
413 vgawb_3d(S3_CRTC_ADR, S3_REG_LOCK2);
414 vgawb_3d(S3_CRTC_DATA, 0xa0);
415 vgawb_3d(S3_CRTC_ADR, S3_HGC_MODE);
416 vgawb_3d(S3_CRTC_DATA, 0x00);
417 vgawb_3d(S3_CRTC_ADR, S3_HWGC_DX);
418 vgawb_3d(S3_CRTC_DATA, 0x00);
419 vgawb_3d(S3_CRTC_ADR, S3_HWGC_DY);
420 vgawb_3d(S3_CRTC_DATA, 0x00);
422 return 0; /* TODO: hardware cursor for CV64/3D */
427 * This function should fill in the `fix' structure based on the
428 * values in the `par' structure.
431 static int Cyber_encode_fix(struct fb_fix_screeninfo *fix,
432 struct virgefb_par *par)
434 memset(fix, 0, sizeof(struct fb_fix_screeninfo));
435 strcpy(fix->id, virgefb_name);
436 if (cv3d_on_zorro2) {
437 fix->smem_start = CyberMem_phys;
438 } else {
439 switch (par->bpp) {
440 case 8:
441 fix->smem_start = (CyberMem_phys + CYBMEM_OFFSET_8);
442 break;
443 case 16:
444 fix->smem_start = (CyberMem_phys + CYBMEM_OFFSET_16);
445 break;
448 fix->smem_len = CyberSize;
449 fix->mmio_start = CyberRegs_phys;
450 fix->mmio_len = 0x10000; /* TODO: verify this for the CV64/3D */
452 fix->type = FB_TYPE_PACKED_PIXELS;
453 fix->type_aux = 0;
454 if (par->bpp == 8)
455 fix->visual = FB_VISUAL_PSEUDOCOLOR;
456 else
457 fix->visual = FB_VISUAL_TRUECOLOR;
459 fix->xpanstep = 0;
460 fix->ypanstep = 0;
461 fix->ywrapstep = 0;
462 fix->line_length = 0;
463 fix->accel = FB_ACCEL_S3_VIRGE;
464 return(0);
469 * Get the video params out of `var'. If a value doesn't fit, round
470 * it up, if it's too big, return -EINVAL.
473 static int Cyber_decode_var(struct fb_var_screeninfo *var,
474 struct virgefb_par *par)
476 #if 1
477 par->xres = var->xres;
478 par->yres = var->yres;
479 par->bpp = var->bits_per_pixel;
480 if (var->accel_flags & FB_ACCELF_TEXT)
481 par->accel = FB_ACCELF_TEXT;
482 else
483 par->accel = 0;
484 #else
485 if (Cyberfb_Cyber8) {
486 par->xres = VIRGE8_WIDTH;
487 par->yres = VIRGE8_HEIGHT;
488 par->bpp = 8;
489 } else {
490 par->xres = VIRGE16_WIDTH;
491 par->yres = VIRGE16_HEIGHT;
492 par->bpp = 16;
494 #endif
495 return(0);
500 * Fill the `var' structure based on the values in `par' and maybe
501 * other values read out of the hardware.
504 static int Cyber_encode_var(struct fb_var_screeninfo *var,
505 struct virgefb_par *par)
507 memset(var, 0, sizeof(struct fb_var_screeninfo));
508 var->xres = par->xres;
509 var->yres = par->yres;
510 var->xres_virtual = par->xres;
511 var->yres_virtual = par->yres;
512 var->xoffset = 0;
513 var->yoffset = 0;
515 var->bits_per_pixel = par->bpp;
516 var->grayscale = 0;
518 switch (var->bits_per_pixel) {
519 case 8: /* CLUT */
520 var->red.offset = 0;
521 var->red.length = 6;
522 var->red.msb_right = 0;
523 var->blue = var->green = var->red;
524 break;
525 case 16: /* RGB 565 */
526 var->red.offset = 11;
527 var->red.length = 5;
528 var->green.offset = 5;
529 var->green.length = 6;
530 var->blue.offset = 0;
531 var->blue.length = 5;
532 var->transp.offset = 0;
533 var->transp.length = 0;
534 break;
536 var->red.msb_right = 0;
537 var->green.msb_right = 0;
538 var->blue.msb_right = 0;
539 var->transp.msb_right = 0;
541 var->nonstd = 0;
542 var->activate = 0;
544 var->height = -1;
545 var->width = -1;
547 var->accel_flags = (par->accel &&
548 ((par->bpp == 8) || (par->bpp == 16))) ? FB_ACCELF_TEXT : 0;
550 /* printk("CV64/3D : %s\n",(var->accel_flags ? "accel" : "no accel")); */
552 var->vmode = FB_VMODE_NONINTERLACED;
554 /* Dummy values */
556 if (par->bpp == 8)
557 var->pixclock = VIRGE8_PIXCLOCK;
558 else
559 var->pixclock = VIRGE16_PIXCLOCK;
560 var->sync = 0;
561 var->left_margin = 64;
562 var->right_margin = 96;
563 var->upper_margin = 35;
564 var->lower_margin = 12;
565 var->hsync_len = 112;
566 var->vsync_len = 2;
568 return(0);
573 * Set a single color register. The values supplied are already
574 * rounded down to the hardware's capabilities (according to the
575 * entries in the var structure). Return != 0 for invalid regno.
578 static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
579 u_int transp, struct fb_info *info)
581 if (((current_par.bpp==8) && (regno>255)) ||
582 ((current_par.bpp!=8) && (regno>15)))
583 return (1);
585 if (((current_par.bpp==8) && (regno<256)) || ((current_par.bpp==16) &&(regno<16))) {
586 Cyber_colour_table [regno][0] = red >> 10;
587 Cyber_colour_table [regno][1] = green >> 10;
588 Cyber_colour_table [regno][2] = blue >> 10;
591 switch (current_par.bpp) {
592 #ifdef FBCON_HAS_CFB8
593 case 8:
594 vgawb_3d(0x3c8, (unsigned char) regno);
595 vgawb_3d(0x3c9, ((unsigned char) (red >> 10)));
596 vgawb_3d(0x3c9, ((unsigned char) (green >> 10)));
597 vgawb_3d(0x3c9, ((unsigned char) (blue >> 10)));
598 break;
599 #endif
600 #ifdef FBCON_HAS_CFB16
601 case 16:
602 fbcon_cmap.cfb16[regno] =
603 ((red & 0xf800) |
604 ((green & 0xfc00) >> 5) |
605 ((blue & 0xf800) >> 11));
606 break;
607 #endif
609 return (0);
614 * Read a single color register and split it into
615 * colors/transparent. Return != 0 for invalid regno.
618 static int Cyber_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
619 u_int *transp, struct fb_info *info)
621 int t;
623 if (regno > 255)
624 return (1);
626 if (((current_par.bpp==8) && (regno<256)) || ((current_par.bpp==16) && (regno<16))) {
628 t = Cyber_colour_table [regno][0];
629 *red = (t<<10) | (t<<4) | (t>>2);
630 t = Cyber_colour_table [regno][1];
631 *green = (t<<10) | (t<<4) | (t>>2);
632 t = Cyber_colour_table [regno][2];
633 *blue = (t<<10) | (t<<4) | (t>>2);
635 *transp = 0;
636 return (0);
641 * (Un)Blank the screen
644 void Cyber_blank(int blank)
646 int i;
648 if (blank)
650 for (i = 0; i < 256; i++)
652 vgawb_3d(0x3c8, (unsigned char) i);
653 vgawb_3d(0x3c9, 0);
654 vgawb_3d(0x3c9, 0);
655 vgawb_3d(0x3c9, 0);
658 else
660 for (i = 0; i < 256; i++)
662 vgawb_3d(0x3c8, (unsigned char) i);
663 vgawb_3d(0x3c9, Cyber_colour_table[i][0]);
664 vgawb_3d(0x3c9, Cyber_colour_table[i][1]);
665 vgawb_3d(0x3c9, Cyber_colour_table[i][2]);
671 * CV3D low-level support
674 #define Cyber3D_WaitQueue(v) \
676 do { \
677 while ((rl_3d(0x8504) & 0x1f00) < (((v)+2) << 8)); \
679 while (0); \
682 static inline void Cyber3D_WaitBusy(void)
684 unsigned long status;
686 do {
687 mb();
688 status = rl_3d(0x8504);
689 } while (!(status & (1 << 13)));
690 blit_maybe_busy = 0;
693 #define S3V_BITBLT (0x0 << 27)
694 #define S3V_RECTFILL (0x2 << 27)
695 #define S3V_AUTOEXE 0x01
696 #define S3V_HWCLIP 0x02
697 #define S3V_DRAW 0x20
698 #define S3V_DST_8BPP 0x00
699 #define S3V_DST_16BPP 0x04
700 #define S3V_DST_24BPP 0x08
701 #define S3V_MONO_PAT 0x100
703 #define S3V_BLT_COPY (0xcc<<17)
704 #define S3V_BLT_CLEAR (0x00<<17)
705 #define S3V_BLT_SET (0xff<<17)
708 * BitBLT - Through the Plane
711 static void Cyber3D_BitBLT(u_short curx, u_short cury, u_short destx,
712 u_short desty, u_short width, u_short height, u_short depth)
714 unsigned int blitcmd = S3V_BITBLT | S3V_DRAW | S3V_BLT_COPY;
716 switch (depth) {
717 #ifdef FBCON_HAS_CFB8
718 case 8 :
719 blitcmd |= S3V_DST_8BPP;
720 break;
721 #endif
722 #ifdef FBCON_HAS_CFB16
723 case 16 :
724 blitcmd |= S3V_DST_16BPP;
725 break;
726 #endif
729 /* Set drawing direction */
730 /* -Y, X maj, -X (default) */
731 if (curx > destx)
733 blitcmd |= (1 << 25); /* Drawing direction +X */
735 else
737 curx += (width - 1);
738 destx += (width - 1);
741 if (cury > desty)
743 blitcmd |= (1 << 26); /* Drawing direction +Y */
745 else
747 cury += (height - 1);
748 desty += (height - 1);
751 if (blit_maybe_busy)
752 Cyber3D_WaitBusy();
753 blit_maybe_busy = 1;
755 wl_3d(0xa4f4, 1); /* pattern fb color */
757 wl_3d(0xa4e8, ~0); /* mono pat 0 */
758 wl_3d(0xa4ec, ~0); /* mono pat 1 */
760 wl_3d(0xa504, ((width << 16) | height)); /* rwidth_height */
761 wl_3d(0xa508, ((curx << 16) | cury)); /* rsrc_xy */
762 wl_3d(0xa50c, ((destx << 16) | desty)); /* rdest_xy */
764 wl_3d(0xa500, blitcmd); /* GO! */
768 * Rectangle Fill Solid
771 static void Cyber3D_RectFill(u_short x, u_short y, u_short width,
772 u_short height, u_short color, u_short depth)
774 unsigned int tmp;
775 unsigned int blitcmd = S3V_RECTFILL | S3V_DRAW |
776 S3V_BLT_CLEAR | S3V_MONO_PAT | (1 << 26) | (1 << 25);
778 if (blit_maybe_busy)
779 Cyber3D_WaitBusy();
780 blit_maybe_busy = 1;
782 switch (depth) {
783 #ifdef FBCON_HAS_CFB8
784 case 8 :
785 blitcmd |= S3V_DST_8BPP;
786 break;
787 #endif
788 #ifdef FBCON_HAS_CFB16
789 case 16 :
790 blitcmd |= S3V_DST_16BPP;
791 break;
792 #endif
795 tmp = color & 0xff;
796 wl_3d(0xa4f4, tmp);
798 wl_3d(0xa504, ((width << 16) | height)); /* rwidth_height */
799 wl_3d(0xa50c, ((x << 16) | y)); /* rdest_xy */
801 wl_3d(0xa500, blitcmd); /* GO! */
805 /**************************************************************
806 * Move cursor to x, y
809 #if 0
810 static void Cyber_MoveCursor (u_short x, u_short y)
812 printk(KERN_DEBUG "Yuck .... MoveCursor on a 3D\n");
813 return;
815 #endif
817 /* -------------------- Interfaces to hardware functions -------------------- */
820 static struct fb_hwswitch Cyber_switch = {
821 Cyber_init, Cyber_encode_fix, Cyber_decode_var, Cyber_encode_var,
822 Cyber_getcolreg, Cyber_setcolreg, Cyber_blank
826 /* -------------------- Generic routines ------------------------------------ */
830 * Fill the hardware's `par' structure.
833 static void virgefb_get_par(struct virgefb_par *par)
835 if (current_par_valid)
837 *par = current_par;
839 else
841 fbhw->decode_var(&virgefb_default, par);
846 static void virgefb_set_par(struct virgefb_par *par)
848 current_par = *par;
849 current_par_valid = 1;
853 static void virge_set_video(struct fb_var_screeninfo *var)
855 /* Set clipping rectangle to current screen size */
857 unsigned int clip;
859 clip = ((0 << 16) | (var->xres - 1));
860 wl_3d(0xa4dc, clip);
861 clip = ((0 << 16) | (var->yres - 1));
862 wl_3d(0xa4e0, clip);
866 static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive)
868 int err, activate;
869 struct virgefb_par par;
871 if ((err = fbhw->decode_var(var, &par)))
872 return(err);
873 activate = var->activate;
874 if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW && isactive)
875 virgefb_set_par(&par);
876 fbhw->encode_var(var, &par);
877 var->activate = activate;
879 virge_set_video(var);
880 return 0;
884 static void do_install_cmap(int con, struct fb_info *info)
886 if (con != currcon)
887 return;
888 if (fb_display[con].cmap.len)
889 fb_set_cmap(&fb_display[con].cmap, 1, fbhw->setcolreg, info);
890 else
891 fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
892 1, fbhw->setcolreg, info);
896 * Get the Fixed Part of the Display
899 static int virgefb_get_fix(struct fb_fix_screeninfo *fix, int con,
900 struct fb_info *info)
902 struct virgefb_par par;
903 int error = 0;
905 if (con == -1)
906 virgefb_get_par(&par);
907 else
908 error = fbhw->decode_var(&fb_display[con].var, &par);
909 return(error ? error : fbhw->encode_fix(fix, &par));
914 * Get the User Defined Part of the Display
917 static int virgefb_get_var(struct fb_var_screeninfo *var, int con,
918 struct fb_info *info)
920 struct virgefb_par par;
921 int error = 0;
923 if (con == -1)
925 virgefb_get_par(&par);
926 error = fbhw->encode_var(var, &par);
927 disp.var = *var; /* ++Andre: don't know if this is the right place */
929 else
931 *var = fb_display[con].var;
934 return(error);
938 static void virgefb_set_disp(int con, struct fb_info *info)
940 struct fb_fix_screeninfo fix;
941 struct display *display;
943 if (con >= 0)
944 display = &fb_display[con];
945 else
946 display = &disp; /* used during initialization */
948 virgefb_get_fix(&fix, con, info);
949 if (con == -1)
950 con = 0;
951 if (cv3d_on_zorro2) {
952 display->screen_base = (char*) CyberMem;
953 } else {
954 switch (display->var.bits_per_pixel) {
955 case 8:
956 display->screen_base = (char*) (CyberMem + CYBMEM_OFFSET_8);
957 break;
958 case 16:
959 display->screen_base = (char*) (CyberMem + CYBMEM_OFFSET_16);
960 break;
963 display->visual = fix.visual;
964 display->type = fix.type;
965 display->type_aux = fix.type_aux;
966 display->ypanstep = fix.ypanstep;
967 display->ywrapstep = fix.ywrapstep;
968 display->can_soft_blank = 1;
969 display->inverse = Cyberfb_inverse;
970 switch (display->var.bits_per_pixel) {
971 #ifdef FBCON_HAS_CFB8
972 case 8:
973 if (display->var.accel_flags & FB_ACCELF_TEXT) {
974 display->dispsw = &fbcon_virge8;
975 #warning FIXME: We should reinit the graphics engine here
976 } else
977 display->dispsw = &fbcon_cfb8;
978 break;
979 #endif
980 #ifdef FBCON_HAS_CFB16
981 case 16:
982 if (display->var.accel_flags & FB_ACCELF_TEXT) {
983 display->dispsw = &fbcon_virge16;
984 } else
985 display->dispsw = &fbcon_cfb16;
986 display->dispsw_data = &fbcon_cmap.cfb16;
987 break;
988 #endif
989 default:
990 display->dispsw = &fbcon_dummy;
991 break;
997 * Set the User Defined Part of the Display
1000 static int virgefb_set_var(struct fb_var_screeninfo *var, int con,
1001 struct fb_info *info)
1003 int err, oldxres, oldyres, oldvxres, oldvyres, oldbpp, oldaccel;
1005 if ((err = do_fb_set_var(var, con == currcon)))
1006 return(err);
1007 if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
1008 oldxres = fb_display[con].var.xres;
1009 oldyres = fb_display[con].var.yres;
1010 oldvxres = fb_display[con].var.xres_virtual;
1011 oldvyres = fb_display[con].var.yres_virtual;
1012 oldbpp = fb_display[con].var.bits_per_pixel;
1013 oldaccel = fb_display[con].var.accel_flags;
1014 fb_display[con].var = *var;
1015 if (oldxres != var->xres || oldyres != var->yres ||
1016 oldvxres != var->xres_virtual ||
1017 oldvyres != var->yres_virtual ||
1018 oldbpp != var->bits_per_pixel ||
1019 oldaccel != var->accel_flags) {
1020 virgefb_set_disp(con, info);
1021 (*fb_info.changevar)(con);
1022 fb_alloc_cmap(&fb_display[con].cmap, 0, 0);
1023 do_install_cmap(con, info);
1026 var->activate = 0;
1027 return(0);
1032 * Get the Colormap
1035 static int virgefb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
1036 struct fb_info *info)
1038 if (con == currcon) /* current console? */
1039 return(fb_get_cmap(cmap, kspc, fbhw->getcolreg, info));
1040 else if (fb_display[con].cmap.len) /* non default colormap? */
1041 fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
1042 else
1043 fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
1044 cmap, kspc ? 0 : 2);
1045 return(0);
1050 * Set the Colormap
1053 static int virgefb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
1054 struct fb_info *info)
1056 int err;
1058 if (!fb_display[con].cmap.len) { /* no colormap allocated? */
1059 if ((err = fb_alloc_cmap(&fb_display[con].cmap,
1060 1<<fb_display[con].var.bits_per_pixel, 0)))
1061 return(err);
1063 if (con == currcon) /* current console? */
1064 return(fb_set_cmap(cmap, kspc, fbhw->setcolreg, info));
1065 else
1066 fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
1067 return(0);
1071 static struct fb_ops virgefb_ops = {
1072 owner: THIS_MODULE,
1073 fb_get_fix: virgefb_get_fix,
1074 fb_get_var: virgefb_get_var,
1075 fb_set_var: virgefb_set_var,
1076 fb_get_cmap: virgefb_get_cmap,
1077 fb_set_cmap: virgefb_set_cmap,
1081 int __init virgefb_setup(char *options)
1083 char *this_opt;
1085 fb_info.fontname[0] = '\0';
1087 if (!options || !*options)
1088 return 0;
1090 for (this_opt = strtok(options, ","); this_opt; this_opt = strtok(NULL, ","))
1091 if (!strcmp(this_opt, "inverse")) {
1092 Cyberfb_inverse = 1;
1093 fb_invert_cmaps();
1094 } else if (!strncmp(this_opt, "font:", 5))
1095 strcpy(fb_info.fontname, this_opt+5);
1096 else if (!strcmp (this_opt, "virge8")){
1097 virgefb_default = virgefb_predefined[VIRGE8_DEFMODE].var;
1099 else if (!strcmp (this_opt, "virge16")){
1100 virgefb_default = virgefb_predefined[VIRGE16_DEFMODE].var;
1102 else
1103 get_video_mode(this_opt);
1105 DPRINTK("default mode: xres=%d, yres=%d, bpp=%d\n",virgefb_default.xres,
1106 virgefb_default.yres,
1107 virgefb_default.bits_per_pixel);
1108 return 0;
1113 * Initialization
1116 int __init virgefb_init(void)
1118 struct virgefb_par par;
1119 unsigned long board_addr, ramsize;
1120 struct zorro_dev *z = NULL;
1122 while ((z = zorro_find_device(ZORRO_PROD_PHASE5_CYBERVISION64_3D, z))) {
1123 board_addr = z->resource.start;
1124 if (board_addr < 0x01000000) {
1126 * Ok we got the board running in Z2 space.
1128 CyberRegs_phys = (unsigned long)(board_addr + 0x003e0000);
1129 CyberMem_phys = board_addr;
1130 ramsize = 0x00380000;
1131 } else {
1132 CyberRegs_phys = board_addr + 0x05000000;
1133 CyberMem_phys = board_addr + 0x04000000; /* was 0x04800000 */
1134 ramsize = 0x00400000;
1136 if (!request_mem_region(CyberRegs_phys, 0x10000, "S3 ViRGE"))
1137 continue;
1138 if (!request_mem_region(CyberMem_phys, ramsize, "RAM")) {
1139 release_mem_region(CyberRegs_phys, 0x10000);
1140 continue;
1143 if (board_addr < 0x01000000) {
1145 * Ok we got the board running in Z2 space.
1148 CyberMem = ZTWO_VADDR(CyberMem_phys);
1149 CyberVGARegs = (unsigned long) \
1150 ZTWO_VADDR(board_addr + 0x003c0000);
1151 CyberRegs = (unsigned char *)ZTWO_VADDR(CyberRegs_phys);
1152 Cyber_register_base = (unsigned long) \
1153 ZTWO_VADDR(board_addr + 0x003c8000);
1154 Cyber_vcode_switch_base = (unsigned long) \
1155 ZTWO_VADDR(board_addr + 0x003a0000);
1156 cv3d_on_zorro2 = 1;
1157 printk(KERN_INFO "CV3D detected running in Z2 mode.\n");
1158 } else {
1159 CyberVGARegs = (unsigned long)ioremap(board_addr+0x0c000000, 0x00010000);
1160 CyberRegs = ioremap(CyberRegs_phys, 0x00010000);
1161 CyberMem = (unsigned long)ioremap(CyberMem_phys, 0x01000000); /* was 0x00400000 */
1162 cv3d_on_zorro2 = 0;
1163 printk(KERN_INFO "CV3D detected running in Z3 mode.\n");
1166 fbhw = &Cyber_switch;
1168 strcpy(fb_info.modename, virgefb_name);
1169 fb_info.changevar = NULL;
1170 fb_info.node = -1;
1171 fb_info.fbops = &virgefb_ops;
1172 fb_info.disp = &disp;
1173 fb_info.switch_con = &Cyberfb_switch;
1174 fb_info.updatevar = &Cyberfb_updatevar;
1175 fb_info.blank = &Cyberfb_blank;
1176 fb_info.flags = FBINFO_FLAG_DEFAULT;
1178 fbhw->init();
1179 fbhw->decode_var(&virgefb_default, &par);
1180 fbhw->encode_var(&virgefb_default, &par);
1182 do_fb_set_var(&virgefb_default, 1);
1183 virgefb_get_var(&fb_display[0].var, -1, &fb_info);
1184 virgefb_set_disp(-1, &fb_info);
1185 do_install_cmap(0, &fb_info);
1187 if (register_framebuffer(&fb_info) < 0) {
1188 printk(KERN_ERR "virgefb.c: register_framebuffer failed\n");
1189 return -EINVAL;
1192 printk(KERN_INFO "fb%d: %s frame buffer device, using %ldK of "
1193 "video memory\n", GET_FB_IDX(fb_info.node),
1194 fb_info.modename, CyberSize>>10);
1196 /* TODO: This driver cannot be unloaded yet */
1197 MOD_INC_USE_COUNT;
1198 return 0;
1200 return -ENODEV;
1204 static int Cyberfb_switch(int con, struct fb_info *info)
1206 /* Do we have to save the colormap? */
1207 if (fb_display[currcon].cmap.len)
1208 fb_get_cmap(&fb_display[currcon].cmap, 1, fbhw->getcolreg,
1209 info);
1211 do_fb_set_var(&fb_display[con].var, 1);
1212 currcon = con;
1213 /* Install new colormap */
1214 do_install_cmap(con, info);
1215 return(0);
1220 * Update the `var' structure (called by fbcon.c)
1222 * This call looks only at yoffset and the FB_VMODE_YWRAP flag in `var'.
1223 * Since it's called by a kernel driver, no range checking is done.
1226 static int Cyberfb_updatevar(int con, struct fb_info *info)
1228 return(0);
1233 * Blank the display.
1236 static void Cyberfb_blank(int blank, struct fb_info *info)
1238 fbhw->blank(blank);
1243 * Get a Video Mode
1246 static int __init get_video_mode(const char *name)
1248 int i;
1250 for (i = 0; i < NUM_TOTAL_MODES; i++) {
1251 if (!strcmp(name, virgefb_predefined[i].name)) {
1252 virgefb_default = virgefb_predefined[i].var;
1253 return(i);
1256 /* ++Andre: set virgefb default mode */
1257 virgefb_default = virgefb_predefined[VIRGE8_DEFMODE].var;
1258 return(0);
1263 * Text console acceleration
1266 #ifdef FBCON_HAS_CFB8
1267 static void fbcon_virge8_bmove(struct display *p, int sy, int sx, int dy,
1268 int dx, int height, int width)
1270 sx *= 8; dx *= 8; width *= 8;
1271 Cyber3D_BitBLT((u_short)sx, (u_short)(sy*fontheight(p)), (u_short)dx,
1272 (u_short)(dy*fontheight(p)), (u_short)width,
1273 (u_short)(height*fontheight(p)), 8);
1276 static void fbcon_virge8_clear(struct vc_data *conp, struct display *p, int sy,
1277 int sx, int height, int width)
1279 unsigned char bg;
1281 sx *= 8; width *= 8;
1282 bg = attr_bgcol_ec(p,conp);
1283 Cyber3D_RectFill((u_short)sx, (u_short)(sy*fontheight(p)),
1284 (u_short)width, (u_short)(height*fontheight(p)),
1285 (u_short)bg, 8);
1288 static void fbcon_virge8_putc(struct vc_data *conp, struct display *p, int c, int yy,
1289 int xx)
1291 if (blit_maybe_busy)
1292 Cyber3D_WaitBusy();
1293 fbcon_cfb8_putc(conp, p, c, yy, xx);
1296 static void fbcon_virge8_putcs(struct vc_data *conp, struct display *p,
1297 const unsigned short *s, int count, int yy, int xx)
1299 if (blit_maybe_busy)
1300 Cyber3D_WaitBusy();
1301 fbcon_cfb8_putcs(conp, p, s, count, yy, xx);
1304 static void fbcon_virge8_revc(struct display *p, int xx, int yy)
1306 if (blit_maybe_busy)
1307 Cyber3D_WaitBusy();
1308 fbcon_cfb8_revc(p, xx, yy);
1311 static void fbcon_virge8_clear_margins(struct vc_data *conp, struct display *p,
1312 int bottom_only)
1314 if (blit_maybe_busy)
1315 Cyber3D_WaitBusy();
1316 fbcon_cfb8_clear_margins(conp, p, bottom_only);
1319 static struct display_switch fbcon_virge8 = {
1320 setup: fbcon_cfb8_setup,
1321 bmove: fbcon_virge8_bmove,
1322 clear: fbcon_virge8_clear,
1323 putc: fbcon_virge8_putc,
1324 putcs: fbcon_virge8_putcs,
1325 revc: fbcon_virge8_revc,
1326 clear_margins: fbcon_virge8_clear_margins,
1327 fontwidthmask: FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
1329 #endif
1331 #ifdef FBCON_HAS_CFB16
1332 static void fbcon_virge16_bmove(struct display *p, int sy, int sx, int dy,
1333 int dx, int height, int width)
1335 sx *= 8; dx *= 8; width *= 8;
1336 Cyber3D_BitBLT((u_short)sx, (u_short)(sy*fontheight(p)), (u_short)dx,
1337 (u_short)(dy*fontheight(p)), (u_short)width,
1338 (u_short)(height*fontheight(p)), 16);
1341 static void fbcon_virge16_clear(struct vc_data *conp, struct display *p, int sy,
1342 int sx, int height, int width)
1344 unsigned char bg;
1346 sx *= 8; width *= 8;
1347 bg = attr_bgcol_ec(p,conp);
1348 Cyber3D_RectFill((u_short)sx, (u_short)(sy*fontheight(p)),
1349 (u_short)width, (u_short)(height*fontheight(p)),
1350 (u_short)bg, 16);
1353 static void fbcon_virge16_putc(struct vc_data *conp, struct display *p, int c, int yy,
1354 int xx)
1356 if (blit_maybe_busy)
1357 Cyber3D_WaitBusy();
1358 fbcon_cfb16_putc(conp, p, c, yy, xx);
1361 static void fbcon_virge16_putcs(struct vc_data *conp, struct display *p,
1362 const unsigned short *s, int count, int yy, int xx)
1364 if (blit_maybe_busy)
1365 Cyber3D_WaitBusy();
1366 fbcon_cfb16_putcs(conp, p, s, count, yy, xx);
1369 static void fbcon_virge16_revc(struct display *p, int xx, int yy)
1371 if (blit_maybe_busy)
1372 Cyber3D_WaitBusy();
1373 fbcon_cfb16_revc(p, xx, yy);
1376 static void fbcon_virge16_clear_margins(struct vc_data *conp, struct display *p,
1377 int bottom_only)
1379 if (blit_maybe_busy)
1380 Cyber3D_WaitBusy();
1381 fbcon_cfb16_clear_margins(conp, p, bottom_only);
1384 static struct display_switch fbcon_virge16 = {
1385 setup: fbcon_cfb16_setup,
1386 bmove: fbcon_virge16_bmove,
1387 clear: fbcon_virge16_clear,
1388 putc: fbcon_virge16_putc,
1389 putcs: fbcon_virge16_putcs,
1390 revc: fbcon_virge16_revc,
1391 clear_margins: fbcon_virge16_clear_margins,
1392 fontwidthmask: FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
1394 #endif
1396 #ifdef MODULE
1397 int init_module(void)
1399 return virgefb_init();
1402 void cleanup_module(void)
1404 /* Not reached because the usecount will never be
1405 decremented to zero */
1406 unregister_framebuffer(&fb_info);
1407 /* TODO: clean up ... */
1409 #endif /* MODULE */