File: /usr/src/linux/drivers/video/virgefb.c
1 /*
2 * linux/drivers/video/virgefb.c -- CyberVision64/3D frame buffer device
3 *
4 * Copyright (C) 1997 André Heynatz
5 *
6 *
7 * This file is based on the CyberVision frame buffer device (cyberfb.c):
8 *
9 * Copyright (C) 1996 Martin Apel
10 * Geert Uytterhoeven
11 *
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.
15 */
16
17 #undef VIRGEFBDEBUG
18
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/slab.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>
36
37 #include <video/s3blit.h>
38 #include <video/fbcon.h>
39 #include <video/fbcon-cfb8.h>
40 #include <video/fbcon-cfb16.h>
41
42
43 #ifdef VIRGEFBDEBUG
44 #define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
45 #else
46 #define DPRINTK(fmt, args...)
47 #endif
48
49 #if 1
50 #define vgawb_3d(reg,dat) \
51 if (cv3d_on_zorro2) { \
52 *((unsigned char volatile *)((Cyber_vcode_switch_base) + 0x04)) = \
53 (0x01 & 0xffff); asm volatile ("nop"); \
54 } \
55 (*((unsigned char *)(CyberVGARegs + (reg ^ 3))) = dat); \
56 if (cv3d_on_zorro2) { \
57 *((unsigned char volatile *)((Cyber_vcode_switch_base) + 0x04)) = \
58 (0x02 & 0xffff); asm volatile ("nop"); \
59 }
60 #define vgaww_3d(reg,dat) \
61 (*((unsigned word *)(CyberVGARegs + (reg ^ 2))) = swab16(dat))
62 #define vgawl_3d(reg,dat) \
63 (*((unsigned long *)(CyberVGARegs + reg)) = swab32(dat))
64 #else
65 /*
66 * Dunno why this doesn't work at the moment - we'll have to look at
67 * it later.
68 */
69 #define vgawb_3d(reg,dat) \
70 (*((unsigned char *)(CyberRegs + 0x8000 + reg)) = dat)
71 #define vgaww_3d(reg,dat) \
72 (*((unsigned word *)(CyberRegs + 0x8000 + reg)) = dat)
73 #define vgawl_3d(reg,dat) \
74 (*((unsigned long *)(CyberRegs + 0x8000 + reg)) = dat)
75 #endif
76
77 /*
78 * We asume P5 mapped the big-endian version of these registers.
79 */
80 #define wb_3d(reg,dat) \
81 (*((unsigned char volatile *)(CyberRegs + reg)) = dat)
82 #define ww_3d(reg,dat) \
83 (*((unsigned word volatile *)(CyberRegs + reg)) = dat)
84 #define wl_3d(reg,dat) \
85 (*((unsigned long volatile *)(CyberRegs + reg)) = dat)
86 #define rl_3d(reg) \
87 (*((unsigned long volatile *)(CyberRegs + reg)))
88
89 #define Select_Zorro2_FrameBuffer(flag) \
90 do { \
91 *((unsigned char volatile *)((Cyber_vcode_switch_base) + 0x08)) = \
92 ((flag * 0x40) & 0xffff); asm volatile ("nop"); \
93 } while (0)
94 /*
95 * may be needed when we initialize the board?
96 * 8bit: flag = 2, 16 bit: flag = 1, 24/32bit: flag = 0
97 * _when_ the board is initialized, depth doesnt matter, we allways write
98 * to the same address, aperture seems not to matter on Z2.
99 */
100
101 struct virgefb_par {
102 int xres;
103 int yres;
104 int bpp;
105 int accel;
106 };
107
108 static struct virgefb_par current_par;
109
110 static int current_par_valid = 0;
111 static int currcon = 0;
112
113 static struct display disp;
114 static struct fb_info fb_info;
115
116 static union {
117 #ifdef FBCON_HAS_CFB16
118 u16 cfb16[16];
119 #endif
120 } fbcon_cmap;
121
122 /*
123 * Switch for Chipset Independency
124 */
125
126 static struct fb_hwswitch {
127
128 /* Initialisation */
129
130 int (*init)(void);
131
132 /* Display Control */
133
134 int (*encode_fix)(struct fb_fix_screeninfo *fix, struct virgefb_par *par);
135 int (*decode_var)(struct fb_var_screeninfo *var, struct virgefb_par *par);
136 int (*encode_var)(struct fb_var_screeninfo *var, struct virgefb_par *par);
137 int (*getcolreg)(u_int regno, u_int *red, u_int *green, u_int *blue,
138 u_int *transp, struct fb_info *info);
139 int (*setcolreg)(u_int regno, u_int red, u_int green, u_int blue,
140 u_int transp, struct fb_info *info);
141 void (*blank)(int blank);
142 } *fbhw;
143
144 static int blit_maybe_busy = 0;
145
146 /*
147 * Frame Buffer Name
148 */
149
150 static char virgefb_name[16] = "Cybervision/3D";
151
152
153 /*
154 * Cybervision Graphics Board
155 */
156
157 #define VIRGE8_WIDTH 1152
158 #define VIRGE8_HEIGHT 886
159 #define VIRGE8_PIXCLOCK 12500 /* ++Geert: Just a guess */
160
161 #if 1
162 #define VIRGE16_WIDTH 800
163 #define VIRGE16_HEIGHT 600
164 #endif
165 #define VIRGE16_PIXCLOCK 25000 /* ++Geert: Just a guess */
166
167
168 static unsigned char Cyber_colour_table [256][3];
169 static unsigned long CyberMem;
170 static unsigned long CyberSize;
171 static volatile char *CyberRegs;
172 static volatile unsigned long CyberVGARegs; /* ++Andre: for CV64/3D, see macros at the beginning */
173 static unsigned long CyberMem_phys;
174 static unsigned long CyberRegs_phys;
175 static unsigned long Cyber_register_base;
176 static unsigned long Cyber_vcode_switch_base;
177 static unsigned char cv3d_on_zorro2;
178
179 #define CYBMEM_OFFSET_8 0x800000 /* offsets from start of video - */
180 #define CYBMEM_OFFSET_16 0x400000 /* ram to appropriate aperture */
181
182 /*
183 * Predefined Video Modes
184 */
185
186 static struct {
187 const char *name;
188 struct fb_var_screeninfo var;
189 } virgefb_predefined[] __initdata = {
190 {
191 "640x480-8", { /* Cybervision 8 bpp */
192 640, 480, 640, 480, 0, 0, 8, 0,
193 {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
194 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
195 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
196 }
197 }, {
198 "800x600-8", { /* Cybervision 8 bpp */
199 800, 600, 800, 600, 0, 0, 8, 0,
200 {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
201 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
202 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
203 }
204 }, {
205 "1024x768-8", { /* Cybervision 8 bpp */
206 1024, 768, 1024, 768, 0, 0, 8, 0,
207 {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
208 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
209 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
210 }
211 }, {
212 "1152x886-8", { /* Cybervision 8 bpp */
213 1152, 886, 1152, 886, 0, 0, 8, 0,
214 {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
215 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
216 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
217 }
218 }, {
219 "1280x1024-8", { /* Cybervision 8 bpp */
220 1280, 1024, 1280, 1024, 0, 0, 8, 0,
221 {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
222 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
223 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
224 }
225 }, {
226 "1600x1200-8", { /* Cybervision 8 bpp */
227 1600, 1200, 1600, 1200, 0, 0, 8, 0,
228 {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
229 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
230 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
231 }
232 }, {
233 "640x480-16", { /* Cybervision 16 bpp */
234 640, 480, 640, 480, 0, 0, 16, 0,
235 {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
236 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE16_PIXCLOCK, 64, 96, 35, 12, 112, 2,
237 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
238 }
239 }, {
240 "800x600-16", { /* Cybervision 16 bpp */
241 800, 600, 800, 600, 0, 0, 16, 0,
242 {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
243 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE16_PIXCLOCK, 64, 96, 35, 12, 112, 2,
244 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
245 }
246 }, {
247 "1024x768-16", { /* Cybervision 16 bpp */
248 1024, 768, 1024, 768, 0, 0, 16, 0,
249 {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
250 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE16_PIXCLOCK, 64, 96, 35, 12, 112, 2,
251 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
252 }
253 }, {
254 "1152x886-16", { /* Cybervision 16 bpp */
255 1152, 886, 1152, 886, 0, 0, 16, 0,
256 {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
257 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE16_PIXCLOCK, 64, 96, 35, 12, 112, 2,
258 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
259 }
260 }, {
261 "1280x1024-16", { /* Cybervision 16 bpp */
262 1280, 1024, 1280, 1024, 0, 0, 16, 0,
263 {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
264 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE16_PIXCLOCK, 64, 96, 35, 12, 112, 2,
265 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
266 }
267 }, {
268 "1600x1200-16", { /* Cybervision 16 bpp */
269 1600, 1200, 1600, 1200, 0, 0, 16, 0,
270 {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
271 0, 0, -1, -1, FB_ACCELF_TEXT, VIRGE16_PIXCLOCK, 64, 96, 35, 12, 112, 2,
272 FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
273 }
274 }
275 };
276
277
278 #define NUM_TOTAL_MODES ARRAY_SIZE(virgefb_predefined)
279
280
281 static int Cyberfb_inverse = 0;
282
283 /*
284 * Some default modes
285 */
286
287 #define VIRGE8_DEFMODE (1)
288 #define VIRGE16_DEFMODE (7)
289
290 static struct fb_var_screeninfo virgefb_default;
291
292
293 /*
294 * Interface used by the world
295 */
296
297 int virgefb_setup(char*);
298
299 static int virgefb_get_fix(struct fb_fix_screeninfo *fix, int con, struct
300 fb_info *info);
301 static int virgefb_get_var(struct fb_var_screeninfo *var, int con, struct
302 fb_info *info);
303 static int virgefb_set_var(struct fb_var_screeninfo *var, int con, struct
304 fb_info *info);
305 static int virgefb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
306 struct fb_info *info);
307 static int virgefb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
308 struct fb_info *info);
309
310
311 /*
312 * Interface to the low level console driver
313 */
314
315 int virgefb_init(void);
316 static int Cyberfb_switch(int con, struct fb_info *info);
317 static int Cyberfb_updatevar(int con, struct fb_info *info);
318 static void Cyberfb_blank(int blank, struct fb_info *info);
319
320
321 /*
322 * Text console acceleration
323 */
324
325 #ifdef FBCON_HAS_CFB8
326 static struct display_switch fbcon_virge8;
327 #endif
328
329 #ifdef FBCON_HAS_CFB16
330 static struct display_switch fbcon_virge16;
331 #endif
332
333 /*
334 * Hardware Specific Routines
335 */
336
337 static int Cyber_init(void);
338 static int Cyber_encode_fix(struct fb_fix_screeninfo *fix,
339 struct virgefb_par *par);
340 static int Cyber_decode_var(struct fb_var_screeninfo *var,
341 struct virgefb_par *par);
342 static int Cyber_encode_var(struct fb_var_screeninfo *var,
343 struct virgefb_par *par);
344 static int Cyber_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
345 u_int *transp, struct fb_info *info);
346 static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
347 u_int transp, struct fb_info *info);
348 static void Cyber_blank(int blank);
349
350
351 /*
352 * Internal routines
353 */
354
355 static void virgefb_get_par(struct virgefb_par *par);
356 static void virgefb_set_par(struct virgefb_par *par);
357 static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive);
358 static void do_install_cmap(int con, struct fb_info *info);
359 static void virgefb_set_disp(int con, struct fb_info *info);
360 static int get_video_mode(const char *name);
361
362
363 /* -------------------- Hardware specific routines ------------------------- */
364
365
366 /*
367 * Initialization
368 *
369 * Set the default video mode for this chipset. If a video mode was
370 * specified on the command line, it will override the default mode.
371 */
372
373 static int Cyber_init(void)
374 {
375 int i;
376
377 for (i = 0; i < 256; i++)
378 {
379 Cyber_colour_table [i][0] = i;
380 Cyber_colour_table [i][1] = i;
381 Cyber_colour_table [i][2] = i;
382 }
383
384 /*
385 * Just clear the thing for the biggest mode.
386 *
387 * ++Andre, TODO: determine size first, then clear all memory
388 * (the 3D penguin might need texture memory :-) )
389 */
390
391 if (cv3d_on_zorro2) {
392 CyberSize = 0x00380000; /* 3.5 MB , we need some space for the registers? */
393 } else {
394 CyberSize = 0x00400000; /* 4 MB */
395 }
396
397 memset ((char*)CyberMem, 0, CyberSize);
398
399 /* Disable hardware cursor */
400 vgawb_3d(0x3c8, 255);
401 vgawb_3d(0x3c9, 56);
402 vgawb_3d(0x3c9, 100);
403 vgawb_3d(0x3c9, 160);
404
405 vgawb_3d(0x3c8, 254);
406 vgawb_3d(0x3c9, 0);
407 vgawb_3d(0x3c9, 0);
408 vgawb_3d(0x3c9, 0);
409
410 /* Disable hardware cursor */
411 vgawb_3d(S3_CRTC_ADR, S3_REG_LOCK2);
412 vgawb_3d(S3_CRTC_DATA, 0xa0);
413 vgawb_3d(S3_CRTC_ADR, S3_HGC_MODE);
414 vgawb_3d(S3_CRTC_DATA, 0x00);
415 vgawb_3d(S3_CRTC_ADR, S3_HWGC_DX);
416 vgawb_3d(S3_CRTC_DATA, 0x00);
417 vgawb_3d(S3_CRTC_ADR, S3_HWGC_DY);
418 vgawb_3d(S3_CRTC_DATA, 0x00);
419
420 return 0; /* TODO: hardware cursor for CV64/3D */
421 }
422
423
424 /*
425 * This function should fill in the `fix' structure based on the
426 * values in the `par' structure.
427 */
428
429 static int Cyber_encode_fix(struct fb_fix_screeninfo *fix,
430 struct virgefb_par *par)
431 {
432 memset(fix, 0, sizeof(struct fb_fix_screeninfo));
433 strcpy(fix->id, virgefb_name);
434 if (cv3d_on_zorro2) {
435 fix->smem_start = CyberMem_phys;
436 } else {
437 switch (par->bpp) {
438 case 8:
439 fix->smem_start = (CyberMem_phys + CYBMEM_OFFSET_8);
440 break;
441 case 16:
442 fix->smem_start = (CyberMem_phys + CYBMEM_OFFSET_16);
443 break;
444 }
445 }
446 fix->smem_len = CyberSize;
447 fix->mmio_start = CyberRegs_phys;
448 fix->mmio_len = 0x10000; /* TODO: verify this for the CV64/3D */
449
450 fix->type = FB_TYPE_PACKED_PIXELS;
451 fix->type_aux = 0;
452 if (par->bpp == 8)
453 fix->visual = FB_VISUAL_PSEUDOCOLOR;
454 else
455 fix->visual = FB_VISUAL_TRUECOLOR;
456
457 fix->xpanstep = 0;
458 fix->ypanstep = 0;
459 fix->ywrapstep = 0;
460 fix->line_length = 0;
461 fix->accel = FB_ACCEL_S3_VIRGE;
462 return(0);
463 }
464
465
466 /*
467 * Get the video params out of `var'. If a value doesn't fit, round
468 * it up, if it's too big, return -EINVAL.
469 */
470
471 static int Cyber_decode_var(struct fb_var_screeninfo *var,
472 struct virgefb_par *par)
473 {
474 #if 1
475 par->xres = var->xres;
476 par->yres = var->yres;
477 par->bpp = var->bits_per_pixel;
478 if (var->accel_flags & FB_ACCELF_TEXT)
479 par->accel = FB_ACCELF_TEXT;
480 else
481 par->accel = 0;
482 #else
483 if (Cyberfb_Cyber8) {
484 par->xres = VIRGE8_WIDTH;
485 par->yres = VIRGE8_HEIGHT;
486 par->bpp = 8;
487 } else {
488 par->xres = VIRGE16_WIDTH;
489 par->yres = VIRGE16_HEIGHT;
490 par->bpp = 16;
491 }
492 #endif
493 return(0);
494 }
495
496
497 /*
498 * Fill the `var' structure based on the values in `par' and maybe
499 * other values read out of the hardware.
500 */
501
502 static int Cyber_encode_var(struct fb_var_screeninfo *var,
503 struct virgefb_par *par)
504 {
505 memset(var, 0, sizeof(struct fb_var_screeninfo));
506 var->xres = par->xres;
507 var->yres = par->yres;
508 var->xres_virtual = par->xres;
509 var->yres_virtual = par->yres;
510 var->xoffset = 0;
511 var->yoffset = 0;
512
513 var->bits_per_pixel = par->bpp;
514 var->grayscale = 0;
515
516 switch (var->bits_per_pixel) {
517 case 8: /* CLUT */
518 var->red.offset = 0;
519 var->red.length = 6;
520 var->red.msb_right = 0;
521 var->blue = var->green = var->red;
522 break;
523 case 16: /* RGB 565 */
524 var->red.offset = 11;
525 var->red.length = 5;
526 var->green.offset = 5;
527 var->green.length = 6;
528 var->blue.offset = 0;
529 var->blue.length = 5;
530 var->transp.offset = 0;
531 var->transp.length = 0;
532 break;
533 }
534 var->red.msb_right = 0;
535 var->green.msb_right = 0;
536 var->blue.msb_right = 0;
537 var->transp.msb_right = 0;
538
539 var->nonstd = 0;
540 var->activate = 0;
541
542 var->height = -1;
543 var->width = -1;
544
545 var->accel_flags = (par->accel &&
546 ((par->bpp == 8) || (par->bpp == 16))) ? FB_ACCELF_TEXT : 0;
547
548 /* printk("CV64/3D : %s\n",(var->accel_flags ? "accel" : "no accel")); */
549
550 var->vmode = FB_VMODE_NONINTERLACED;
551
552 /* Dummy values */
553
554 if (par->bpp == 8)
555 var->pixclock = VIRGE8_PIXCLOCK;
556 else
557 var->pixclock = VIRGE16_PIXCLOCK;
558 var->sync = 0;
559 var->left_margin = 64;
560 var->right_margin = 96;
561 var->upper_margin = 35;
562 var->lower_margin = 12;
563 var->hsync_len = 112;
564 var->vsync_len = 2;
565
566 return(0);
567 }
568
569
570 /*
571 * Set a single color register. The values supplied are already
572 * rounded down to the hardware's capabilities (according to the
573 * entries in the var structure). Return != 0 for invalid regno.
574 */
575
576 static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
577 u_int transp, struct fb_info *info)
578 {
579 if (((current_par.bpp==8) && (regno>255)) ||
580 ((current_par.bpp!=8) && (regno>15)))
581 return (1);
582
583 if (((current_par.bpp==8) && (regno<256)) || ((current_par.bpp==16) &&(regno<16))) {
584 Cyber_colour_table [regno][0] = red >> 10;
585 Cyber_colour_table [regno][1] = green >> 10;
586 Cyber_colour_table [regno][2] = blue >> 10;
587 }
588
589 switch (current_par.bpp) {
590 #ifdef FBCON_HAS_CFB8
591 case 8:
592 vgawb_3d(0x3c8, (unsigned char) regno);
593 vgawb_3d(0x3c9, ((unsigned char) (red >> 10)));
594 vgawb_3d(0x3c9, ((unsigned char) (green >> 10)));
595 vgawb_3d(0x3c9, ((unsigned char) (blue >> 10)));
596 break;
597 #endif
598 #ifdef FBCON_HAS_CFB16
599 case 16:
600 fbcon_cmap.cfb16[regno] =
601 ((red & 0xf800) |
602 ((green & 0xfc00) >> 5) |
603 ((blue & 0xf800) >> 11));
604 break;
605 #endif
606 }
607 return (0);
608 }
609
610
611 /*
612 * Read a single color register and split it into
613 * colors/transparent. Return != 0 for invalid regno.
614 */
615
616 static int Cyber_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
617 u_int *transp, struct fb_info *info)
618 {
619 int t;
620
621 if (regno > 255)
622 return (1);
623
624 if (((current_par.bpp==8) && (regno<256)) || ((current_par.bpp==16) && (regno<16))) {
625
626 t = Cyber_colour_table [regno][0];
627 *red = (t<<10) | (t<<4) | (t>>2);
628 t = Cyber_colour_table [regno][1];
629 *green = (t<<10) | (t<<4) | (t>>2);
630 t = Cyber_colour_table [regno][2];
631 *blue = (t<<10) | (t<<4) | (t>>2);
632 }
633 *transp = 0;
634 return (0);
635 }
636
637
638 /*
639 * (Un)Blank the screen
640 */
641
642 void Cyber_blank(int blank)
643 {
644 int i;
645
646 if (blank)
647 {
648 for (i = 0; i < 256; i++)
649 {
650 vgawb_3d(0x3c8, (unsigned char) i);
651 vgawb_3d(0x3c9, 0);
652 vgawb_3d(0x3c9, 0);
653 vgawb_3d(0x3c9, 0);
654 }
655 }
656 else
657 {
658 for (i = 0; i < 256; i++)
659 {
660 vgawb_3d(0x3c8, (unsigned char) i);
661 vgawb_3d(0x3c9, Cyber_colour_table[i][0]);
662 vgawb_3d(0x3c9, Cyber_colour_table[i][1]);
663 vgawb_3d(0x3c9, Cyber_colour_table[i][2]);
664 }
665 }
666 }
667
668 /*
669 * CV3D low-level support
670 */
671
672 #define Cyber3D_WaitQueue(v) \
673 { \
674 do { \
675 while ((rl_3d(0x8504) & 0x1f00) < (((v)+2) << 8)); \
676 } \
677 while (0); \
678 }
679
680 static inline void Cyber3D_WaitBusy(void)
681 {
682 unsigned long status;
683
684 do {
685 mb();
686 status = rl_3d(0x8504);
687 } while (!(status & (1 << 13)));
688 blit_maybe_busy = 0;
689 }
690
691 #define S3V_BITBLT (0x0 << 27)
692 #define S3V_RECTFILL (0x2 << 27)
693 #define S3V_AUTOEXE 0x01
694 #define S3V_HWCLIP 0x02
695 #define S3V_DRAW 0x20
696 #define S3V_DST_8BPP 0x00
697 #define S3V_DST_16BPP 0x04
698 #define S3V_DST_24BPP 0x08
699 #define S3V_MONO_PAT 0x100
700
701 #define S3V_BLT_COPY (0xcc<<17)
702 #define S3V_BLT_CLEAR (0x00<<17)
703 #define S3V_BLT_SET (0xff<<17)
704
705 /*
706 * BitBLT - Through the Plane
707 */
708
709 static void Cyber3D_BitBLT(u_short curx, u_short cury, u_short destx,
710 u_short desty, u_short width, u_short height, u_short depth)
711 {
712 unsigned int blitcmd = S3V_BITBLT | S3V_DRAW | S3V_BLT_COPY;
713
714 switch (depth) {
715 #ifdef FBCON_HAS_CFB8
716 case 8 :
717 blitcmd |= S3V_DST_8BPP;
718 break;
719 #endif
720 #ifdef FBCON_HAS_CFB16
721 case 16 :
722 blitcmd |= S3V_DST_16BPP;
723 break;
724 #endif
725 }
726
727 /* Set drawing direction */
728 /* -Y, X maj, -X (default) */
729 if (curx > destx)
730 {
731 blitcmd |= (1 << 25); /* Drawing direction +X */
732 }
733 else
734 {
735 curx += (width - 1);
736 destx += (width - 1);
737 }
738
739 if (cury > desty)
740 {
741 blitcmd |= (1 << 26); /* Drawing direction +Y */
742 }
743 else
744 {
745 cury += (height - 1);
746 desty += (height - 1);
747 }
748
749 if (blit_maybe_busy)
750 Cyber3D_WaitBusy();
751 blit_maybe_busy = 1;
752
753 wl_3d(0xa4f4, 1); /* pattern fb color */
754
755 wl_3d(0xa4e8, ~0); /* mono pat 0 */
756 wl_3d(0xa4ec, ~0); /* mono pat 1 */
757
758 wl_3d(0xa504, ((width << 16) | height)); /* rwidth_height */
759 wl_3d(0xa508, ((curx << 16) | cury)); /* rsrc_xy */
760 wl_3d(0xa50c, ((destx << 16) | desty)); /* rdest_xy */
761
762 wl_3d(0xa500, blitcmd); /* GO! */
763 }
764
765 /*
766 * Rectangle Fill Solid
767 */
768
769 static void Cyber3D_RectFill(u_short x, u_short y, u_short width,
770 u_short height, u_short color, u_short depth)
771 {
772 unsigned int tmp;
773 unsigned int blitcmd = S3V_RECTFILL | S3V_DRAW |
774 S3V_BLT_CLEAR | S3V_MONO_PAT | (1 << 26) | (1 << 25);
775
776 if (blit_maybe_busy)
777 Cyber3D_WaitBusy();
778 blit_maybe_busy = 1;
779
780 switch (depth) {
781 #ifdef FBCON_HAS_CFB8
782 case 8 :
783 blitcmd |= S3V_DST_8BPP;
784 break;
785 #endif
786 #ifdef FBCON_HAS_CFB16
787 case 16 :
788 blitcmd |= S3V_DST_16BPP;
789 break;
790 #endif
791 }
792
793 tmp = color & 0xff;
794 wl_3d(0xa4f4, tmp);
795
796 wl_3d(0xa504, ((width << 16) | height)); /* rwidth_height */
797 wl_3d(0xa50c, ((x << 16) | y)); /* rdest_xy */
798
799 wl_3d(0xa500, blitcmd); /* GO! */
800 }
801
802
803 /**************************************************************
804 * Move cursor to x, y
805 */
806
807 #if 0
808 static void Cyber_MoveCursor (u_short x, u_short y)
809 {
810 printk(KERN_DEBUG "Yuck .... MoveCursor on a 3D\n");
811 return;
812 }
813 #endif
814
815 /* -------------------- Interfaces to hardware functions -------------------- */
816
817
818 static struct fb_hwswitch Cyber_switch = {
819 Cyber_init, Cyber_encode_fix, Cyber_decode_var, Cyber_encode_var,
820 Cyber_getcolreg, Cyber_setcolreg, Cyber_blank
821 };
822
823
824 /* -------------------- Generic routines ------------------------------------ */
825
826
827 /*
828 * Fill the hardware's `par' structure.
829 */
830
831 static void virgefb_get_par(struct virgefb_par *par)
832 {
833 if (current_par_valid)
834 {
835 *par = current_par;
836 }
837 else
838 {
839 fbhw->decode_var(&virgefb_default, par);
840 }
841 }
842
843
844 static void virgefb_set_par(struct virgefb_par *par)
845 {
846 current_par = *par;
847 current_par_valid = 1;
848 }
849
850
851 static void virge_set_video(struct fb_var_screeninfo *var)
852 {
853 /* Set clipping rectangle to current screen size */
854
855 unsigned int clip;
856
857 clip = ((0 << 16) | (var->xres - 1));
858 wl_3d(0xa4dc, clip);
859 clip = ((0 << 16) | (var->yres - 1));
860 wl_3d(0xa4e0, clip);
861 }
862
863
864 static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive)
865 {
866 int err, activate;
867 struct virgefb_par par;
868
869 if ((err = fbhw->decode_var(var, &par)))
870 return(err);
871 activate = var->activate;
872 if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW && isactive)
873 virgefb_set_par(&par);
874 fbhw->encode_var(var, &par);
875 var->activate = activate;
876
877 virge_set_video(var);
878 return 0;
879 }
880
881
882 static void do_install_cmap(int con, struct fb_info *info)
883 {
884 if (con != currcon)
885 return;
886 if (fb_display[con].cmap.len)
887 fb_set_cmap(&fb_display[con].cmap, 1, fbhw->setcolreg, info);
888 else
889 fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
890 1, fbhw->setcolreg, info);
891 }
892
893 /*
894 * Get the Fixed Part of the Display
895 */
896
897 static int virgefb_get_fix(struct fb_fix_screeninfo *fix, int con,
898 struct fb_info *info)
899 {
900 struct virgefb_par par;
901 int error = 0;
902
903 if (con == -1)
904 virgefb_get_par(&par);
905 else
906 error = fbhw->decode_var(&fb_display[con].var, &par);
907 return(error ? error : fbhw->encode_fix(fix, &par));
908 }
909
910
911 /*
912 * Get the User Defined Part of the Display
913 */
914
915 static int virgefb_get_var(struct fb_var_screeninfo *var, int con,
916 struct fb_info *info)
917 {
918 struct virgefb_par par;
919 int error = 0;
920
921 if (con == -1)
922 {
923 virgefb_get_par(&par);
924 error = fbhw->encode_var(var, &par);
925 disp.var = *var; /* ++Andre: don't know if this is the right place */
926 }
927 else
928 {
929 *var = fb_display[con].var;
930 }
931
932 return(error);
933 }
934
935
936 static void virgefb_set_disp(int con, struct fb_info *info)
937 {
938 struct fb_fix_screeninfo fix;
939 struct display *display;
940
941 if (con >= 0)
942 display = &fb_display[con];
943 else
944 display = &disp; /* used during initialization */
945
946 virgefb_get_fix(&fix, con, info);
947 if (con == -1)
948 con = 0;
949 if (cv3d_on_zorro2) {
950 display->screen_base = (char*) CyberMem;
951 } else {
952 switch (display->var.bits_per_pixel) {
953 case 8:
954 display->screen_base = (char*) (CyberMem + CYBMEM_OFFSET_8);
955 break;
956 case 16:
957 display->screen_base = (char*) (CyberMem + CYBMEM_OFFSET_16);
958 break;
959 }
960 }
961 display->visual = fix.visual;
962 display->type = fix.type;
963 display->type_aux = fix.type_aux;
964 display->ypanstep = fix.ypanstep;
965 display->ywrapstep = fix.ywrapstep;
966 display->can_soft_blank = 1;
967 display->inverse = Cyberfb_inverse;
968 switch (display->var.bits_per_pixel) {
969 #ifdef FBCON_HAS_CFB8
970 case 8:
971 if (display->var.accel_flags & FB_ACCELF_TEXT) {
972 display->dispsw = &fbcon_virge8;
973 #warning FIXME: We should reinit the graphics engine here
974 } else
975 display->dispsw = &fbcon_cfb8;
976 break;
977 #endif
978 #ifdef FBCON_HAS_CFB16
979 case 16:
980 if (display->var.accel_flags & FB_ACCELF_TEXT) {
981 display->dispsw = &fbcon_virge16;
982 } else
983 display->dispsw = &fbcon_cfb16;
984 display->dispsw_data = &fbcon_cmap.cfb16;
985 break;
986 #endif
987 default:
988 display->dispsw = &fbcon_dummy;
989 break;
990 }
991 }
992
993
994 /*
995 * Set the User Defined Part of the Display
996 */
997
998 static int virgefb_set_var(struct fb_var_screeninfo *var, int con,
999 struct fb_info *info)
1000 {
1001 int err, oldxres, oldyres, oldvxres, oldvyres, oldbpp, oldaccel;
1002
1003 if ((err = do_fb_set_var(var, con == currcon)))
1004 return(err);
1005 if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
1006 oldxres = fb_display[con].var.xres;
1007 oldyres = fb_display[con].var.yres;
1008 oldvxres = fb_display[con].var.xres_virtual;
1009 oldvyres = fb_display[con].var.yres_virtual;
1010 oldbpp = fb_display[con].var.bits_per_pixel;
1011 oldaccel = fb_display[con].var.accel_flags;
1012 fb_display[con].var = *var;
1013 if (oldxres != var->xres || oldyres != var->yres ||
1014 oldvxres != var->xres_virtual ||
1015 oldvyres != var->yres_virtual ||
1016 oldbpp != var->bits_per_pixel ||
1017 oldaccel != var->accel_flags) {
1018 virgefb_set_disp(con, info);
1019 (*fb_info.changevar)(con);
1020 fb_alloc_cmap(&fb_display[con].cmap, 0, 0);
1021 do_install_cmap(con, info);
1022 }
1023 }
1024 var->activate = 0;
1025 return(0);
1026 }
1027
1028
1029 /*
1030 * Get the Colormap
1031 */
1032
1033 static int virgefb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
1034 struct fb_info *info)
1035 {
1036 if (con == currcon) /* current console? */
1037 return(fb_get_cmap(cmap, kspc, fbhw->getcolreg, info));
1038 else if (fb_display[con].cmap.len) /* non default colormap? */
1039 fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
1040 else
1041 fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
1042 cmap, kspc ? 0 : 2);
1043 return(0);
1044 }
1045
1046
1047 /*
1048 * Set the Colormap
1049 */
1050
1051 static int virgefb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
1052 struct fb_info *info)
1053 {
1054 int err;
1055
1056 if (!fb_display[con].cmap.len) { /* no colormap allocated? */
1057 if ((err = fb_alloc_cmap(&fb_display[con].cmap,
1058 1<<fb_display[con].var.bits_per_pixel, 0)))
1059 return(err);
1060 }
1061 if (con == currcon) /* current console? */
1062 return(fb_set_cmap(cmap, kspc, fbhw->setcolreg, info));
1063 else
1064 fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
1065 return(0);
1066 }
1067
1068
1069 static struct fb_ops virgefb_ops = {
1070 owner: THIS_MODULE,
1071 fb_get_fix: virgefb_get_fix,
1072 fb_get_var: virgefb_get_var,
1073 fb_set_var: virgefb_set_var,
1074 fb_get_cmap: virgefb_get_cmap,
1075 fb_set_cmap: virgefb_set_cmap,
1076 };
1077
1078
1079 int __init virgefb_setup(char *options)
1080 {
1081 char *this_opt;
1082
1083 fb_info.fontname[0] = '\0';
1084
1085 if (!options || !*options)
1086 return 0;
1087
1088 for (this_opt = strtok(options, ","); this_opt; this_opt = strtok(NULL, ","))
1089 if (!strcmp(this_opt, "inverse")) {
1090 Cyberfb_inverse = 1;
1091 fb_invert_cmaps();
1092 } else if (!strncmp(this_opt, "font:", 5))
1093 strcpy(fb_info.fontname, this_opt+5);
1094 else if (!strcmp (this_opt, "virge8")){
1095 virgefb_default = virgefb_predefined[VIRGE8_DEFMODE].var;
1096 }
1097 else if (!strcmp (this_opt, "virge16")){
1098 virgefb_default = virgefb_predefined[VIRGE16_DEFMODE].var;
1099 }
1100 else
1101 get_video_mode(this_opt);
1102
1103 DPRINTK("default mode: xres=%d, yres=%d, bpp=%d\n",virgefb_default.xres,
1104 virgefb_default.yres,
1105 virgefb_default.bits_per_pixel);
1106 return 0;
1107 }
1108
1109
1110 /*
1111 * Initialization
1112 */
1113
1114 int __init virgefb_init(void)
1115 {
1116 struct virgefb_par par;
1117 unsigned long board_addr, ramsize;
1118 struct zorro_dev *z = NULL;
1119
1120 while ((z = zorro_find_device(ZORRO_PROD_PHASE5_CYBERVISION64_3D, z))) {
1121 board_addr = z->resource.start;
1122 if (board_addr < 0x01000000) {
1123 /*
1124 * Ok we got the board running in Z2 space.
1125 */
1126 CyberRegs_phys = (unsigned long)(board_addr + 0x003e0000);
1127 CyberMem_phys = board_addr;
1128 ramsize = 0x00380000;
1129 } else {
1130 CyberRegs_phys = board_addr + 0x05000000;
1131 CyberMem_phys = board_addr + 0x04000000; /* was 0x04800000 */
1132 ramsize = 0x00400000;
1133 }
1134 if (!request_mem_region(CyberRegs_phys, 0x10000, "S3 ViRGE"))
1135 continue;
1136 if (!request_mem_region(CyberMem_phys, ramsize, "RAM")) {
1137 release_mem_region(CyberRegs_phys, 0x10000);
1138 continue;
1139 }
1140
1141 if (board_addr < 0x01000000) {
1142 /*
1143 * Ok we got the board running in Z2 space.
1144 */
1145
1146 CyberMem = ZTWO_VADDR(CyberMem_phys);
1147 CyberVGARegs = (unsigned long)
1148 ZTWO_VADDR(board_addr + 0x003c0000);
1149 CyberRegs = (unsigned char *)ZTWO_VADDR(CyberRegs_phys);
1150 Cyber_register_base = (unsigned long)
1151 ZTWO_VADDR(board_addr + 0x003c8000);
1152 Cyber_vcode_switch_base = (unsigned long)
1153 ZTWO_VADDR(board_addr + 0x003a0000);
1154 cv3d_on_zorro2 = 1;
1155 printk(KERN_INFO "CV3D detected running in Z2 mode.\n");
1156 } else {
1157 CyberVGARegs = (unsigned long)ioremap(board_addr+0x0c000000, 0x00010000);
1158 CyberRegs = ioremap(CyberRegs_phys, 0x00010000);
1159 CyberMem = (unsigned long)ioremap(CyberMem_phys, 0x01000000); /* was 0x00400000 */
1160 cv3d_on_zorro2 = 0;
1161 printk(KERN_INFO "CV3D detected running in Z3 mode.\n");
1162 }
1163
1164 fbhw = &Cyber_switch;
1165
1166 strcpy(fb_info.modename, virgefb_name);
1167 fb_info.changevar = NULL;
1168 fb_info.node = -1;
1169 fb_info.fbops = &virgefb_ops;
1170 fb_info.disp = &disp;
1171 fb_info.switch_con = &Cyberfb_switch;
1172 fb_info.updatevar = &Cyberfb_updatevar;
1173 fb_info.blank = &Cyberfb_blank;
1174 fb_info.flags = FBINFO_FLAG_DEFAULT;
1175
1176 fbhw->init();
1177 fbhw->decode_var(&virgefb_default, &par);
1178 fbhw->encode_var(&virgefb_default, &par);
1179
1180 do_fb_set_var(&virgefb_default, 1);
1181 virgefb_get_var(&fb_display[0].var, -1, &fb_info);
1182 virgefb_set_disp(-1, &fb_info);
1183 do_install_cmap(0, &fb_info);
1184
1185 if (register_framebuffer(&fb_info) < 0) {
1186 printk(KERN_ERR "virgefb.c: register_framebuffer failed\n");
1187 return -EINVAL;
1188 }
1189
1190 printk(KERN_INFO "fb%d: %s frame buffer device, using %ldK of "
1191 "video memory\n", GET_FB_IDX(fb_info.node),
1192 fb_info.modename, CyberSize>>10);
1193
1194 /* TODO: This driver cannot be unloaded yet */
1195 MOD_INC_USE_COUNT;
1196 return 0;
1197 }
1198 return -ENODEV;
1199 }
1200
1201
1202 static int Cyberfb_switch(int con, struct fb_info *info)
1203 {
1204 /* Do we have to save the colormap? */
1205 if (fb_display[currcon].cmap.len)
1206 fb_get_cmap(&fb_display[currcon].cmap, 1, fbhw->getcolreg,
1207 info);
1208
1209 do_fb_set_var(&fb_display[con].var, 1);
1210 currcon = con;
1211 /* Install new colormap */
1212 do_install_cmap(con, info);
1213 return(0);
1214 }
1215
1216
1217 /*
1218 * Update the `var' structure (called by fbcon.c)
1219 *
1220 * This call looks only at yoffset and the FB_VMODE_YWRAP flag in `var'.
1221 * Since it's called by a kernel driver, no range checking is done.
1222 */
1223
1224 static int Cyberfb_updatevar(int con, struct fb_info *info)
1225 {
1226 return(0);
1227 }
1228
1229
1230 /*
1231 * Blank the display.
1232 */
1233
1234 static void Cyberfb_blank(int blank, struct fb_info *info)
1235 {
1236 fbhw->blank(blank);
1237 }
1238
1239
1240 /*
1241 * Get a Video Mode
1242 */
1243
1244 static int __init get_video_mode(const char *name)
1245 {
1246 int i;
1247
1248 for (i = 0; i < NUM_TOTAL_MODES; i++) {
1249 if (!strcmp(name, virgefb_predefined[i].name)) {
1250 virgefb_default = virgefb_predefined[i].var;
1251 return(i);
1252 }
1253 }
1254 /* ++Andre: set virgefb default mode */
1255 virgefb_default = virgefb_predefined[VIRGE8_DEFMODE].var;
1256 return(0);
1257 }
1258
1259
1260 /*
1261 * Text console acceleration
1262 */
1263
1264 #ifdef FBCON_HAS_CFB8
1265 static void fbcon_virge8_bmove(struct display *p, int sy, int sx, int dy,
1266 int dx, int height, int width)
1267 {
1268 sx *= 8; dx *= 8; width *= 8;
1269 Cyber3D_BitBLT((u_short)sx, (u_short)(sy*fontheight(p)), (u_short)dx,
1270 (u_short)(dy*fontheight(p)), (u_short)width,
1271 (u_short)(height*fontheight(p)), 8);
1272 }
1273
1274 static void fbcon_virge8_clear(struct vc_data *conp, struct display *p, int sy,
1275 int sx, int height, int width)
1276 {
1277 unsigned char bg;
1278
1279 sx *= 8; width *= 8;
1280 bg = attr_bgcol_ec(p,conp);
1281 Cyber3D_RectFill((u_short)sx, (u_short)(sy*fontheight(p)),
1282 (u_short)width, (u_short)(height*fontheight(p)),
1283 (u_short)bg, 8);
1284 }
1285
1286 static void fbcon_virge8_putc(struct vc_data *conp, struct display *p, int c, int yy,
1287 int xx)
1288 {
1289 if (blit_maybe_busy)
1290 Cyber3D_WaitBusy();
1291 fbcon_cfb8_putc(conp, p, c, yy, xx);
1292 }
1293
1294 static void fbcon_virge8_putcs(struct vc_data *conp, struct display *p,
1295 const unsigned short *s, int count, int yy, int xx)
1296 {
1297 if (blit_maybe_busy)
1298 Cyber3D_WaitBusy();
1299 fbcon_cfb8_putcs(conp, p, s, count, yy, xx);
1300 }
1301
1302 static void fbcon_virge8_revc(struct display *p, int xx, int yy)
1303 {
1304 if (blit_maybe_busy)
1305 Cyber3D_WaitBusy();
1306 fbcon_cfb8_revc(p, xx, yy);
1307 }
1308
1309 static void fbcon_virge8_clear_margins(struct vc_data *conp, struct display *p,
1310 int bottom_only)
1311 {
1312 if (blit_maybe_busy)
1313 Cyber3D_WaitBusy();
1314 fbcon_cfb8_clear_margins(conp, p, bottom_only);
1315 }
1316
1317 static struct display_switch fbcon_virge8 = {
1318 setup: fbcon_cfb8_setup,
1319 bmove: fbcon_virge8_bmove,
1320 clear: fbcon_virge8_clear,
1321 putc: fbcon_virge8_putc,
1322 putcs: fbcon_virge8_putcs,
1323 revc: fbcon_virge8_revc,
1324 clear_margins: fbcon_virge8_clear_margins,
1325 fontwidthmask: FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
1326 };
1327 #endif
1328
1329 #ifdef FBCON_HAS_CFB16
1330 static void fbcon_virge16_bmove(struct display *p, int sy, int sx, int dy,
1331 int dx, int height, int width)
1332 {
1333 sx *= 8; dx *= 8; width *= 8;
1334 Cyber3D_BitBLT((u_short)sx, (u_short)(sy*fontheight(p)), (u_short)dx,
1335 (u_short)(dy*fontheight(p)), (u_short)width,
1336 (u_short)(height*fontheight(p)), 16);
1337 }
1338
1339 static void fbcon_virge16_clear(struct vc_data *conp, struct display *p, int sy,
1340 int sx, int height, int width)
1341 {
1342 unsigned char bg;
1343
1344 sx *= 8; width *= 8;
1345 bg = attr_bgcol_ec(p,conp);
1346 Cyber3D_RectFill((u_short)sx, (u_short)(sy*fontheight(p)),
1347 (u_short)width, (u_short)(height*fontheight(p)),
1348 (u_short)bg, 16);
1349 }
1350
1351 static void fbcon_virge16_putc(struct vc_data *conp, struct display *p, int c, int yy,
1352 int xx)
1353 {
1354 if (blit_maybe_busy)
1355 Cyber3D_WaitBusy();
1356 fbcon_cfb16_putc(conp, p, c, yy, xx);
1357 }
1358
1359 static void fbcon_virge16_putcs(struct vc_data *conp, struct display *p,
1360 const unsigned short *s, int count, int yy, int xx)
1361 {
1362 if (blit_maybe_busy)
1363 Cyber3D_WaitBusy();
1364 fbcon_cfb16_putcs(conp, p, s, count, yy, xx);
1365 }
1366
1367 static void fbcon_virge16_revc(struct display *p, int xx, int yy)
1368 {
1369 if (blit_maybe_busy)
1370 Cyber3D_WaitBusy();
1371 fbcon_cfb16_revc(p, xx, yy);
1372 }
1373
1374 static void fbcon_virge16_clear_margins(struct vc_data *conp, struct display *p,
1375 int bottom_only)
1376 {
1377 if (blit_maybe_busy)
1378 Cyber3D_WaitBusy();
1379 fbcon_cfb16_clear_margins(conp, p, bottom_only);
1380 }
1381
1382 static struct display_switch fbcon_virge16 = {
1383 setup: fbcon_cfb16_setup,
1384 bmove: fbcon_virge16_bmove,
1385 clear: fbcon_virge16_clear,
1386 putc: fbcon_virge16_putc,
1387 putcs: fbcon_virge16_putcs,
1388 revc: fbcon_virge16_revc,
1389 clear_margins: fbcon_virge16_clear_margins,
1390 fontwidthmask: FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
1391 };
1392 #endif
1393
1394 #ifdef MODULE
1395 MODULE_LICENSE("GPL");
1396
1397 int init_module(void)
1398 {
1399 return virgefb_init();
1400 }
1401
1402 void cleanup_module(void)
1403 {
1404 /* Not reached because the usecount will never be
1405 decremented to zero */
1406 unregister_framebuffer(&fb_info);
1407 /* TODO: clean up ... */
1408 }
1409 #endif /* MODULE */
1410