make oldconfig will rebuild these...
[linux-2.4.21-pre4.git] / drivers / video / S3triofb.c
1 /*
2  *  linux/drivers/video/S3Triofb.c -- Open Firmware based frame buffer device
3  *
4  *      Copyright (C) 1997 Peter De Schrijver
5  *
6  *  This driver is partly based on the PowerMac console driver:
7  *
8  *      Copyright (C) 1996 Paul Mackerras
9  *
10  *  and on the Open Firmware based frame buffer device:
11  *
12  *      Copyright (C) 1997 Geert Uytterhoeven
13  *
14  *  This file is subject to the terms and conditions of the GNU General Public
15  *  License. See the file COPYING in the main directory of this archive for
16  *  more details.
17  */
18
19 /*
20         Bugs : + This driver should be merged with the CyberVision driver. The
21                  CyberVision is a Zorro III implementation of the S3Trio64 chip.
22
23 */
24
25 #include <linux/config.h>
26 #include <linux/kernel.h>
27 #include <linux/module.h>
28 #include <linux/errno.h>
29 #include <linux/string.h>
30 #include <linux/mm.h>
31 #include <linux/tty.h>
32 #include <linux/slab.h>
33 #include <linux/vmalloc.h>
34 #include <linux/delay.h>
35 #include <linux/interrupt.h>
36 #include <linux/fb.h>
37 #include <linux/init.h>
38 #include <linux/selection.h>
39 #include <linux/pci.h>
40 #ifdef CONFIG_FB_COMPAT_XPMAC
41 #include <asm/vc_ioctl.h>
42 #endif
43
44 #include <video/fbcon.h>
45 #include <video/fbcon-cfb8.h>
46 #include <video/s3blit.h>
47
48
49 #define mem_in8(addr)           in_8((void *)(addr))
50 #define mem_in16(addr)          in_le16((void *)(addr))
51 #define mem_in32(addr)          in_le32((void *)(addr))
52
53 #define mem_out8(val, addr)     out_8((void *)(addr), val)
54 #define mem_out16(val, addr)    out_le16((void *)(addr), val)
55 #define mem_out32(val, addr)    out_le32((void *)(addr), val)
56
57 #define IO_OUT16VAL(v, r)       (((v) << 8) | (r))
58
59
60 static int currcon = 0;
61 static int disabled;
62 static struct display disp;
63 static struct fb_info fb_info;
64 static struct { u_char red, green, blue, pad; } palette[256];
65 static char s3trio_name[16] = "S3Trio ";
66 static char *s3trio_base;
67
68 static struct fb_fix_screeninfo fb_fix;
69 static struct fb_var_screeninfo S3triofb_default_var = {
70         640, 480, 640, 480, 0, 0, 8, 0,
71         {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
72         0, 0, -1, FB_ACCELF_TEXT, 39722, 40, 24, 32, 11, 96, 2,
73         FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT,
74         FB_VMODE_NONINTERLACED
75 };
76 static struct fb_var_screeninfo fb_var = { 0,};
77
78     /*
79      *  Interface used by the world
80      */
81
82 static void __init s3triofb_pci_init(struct pci_dev *dp);
83 static int s3trio_get_fix(struct fb_fix_screeninfo *fix, int con,
84                           struct fb_info *info);
85 static int s3trio_get_var(struct fb_var_screeninfo *var, int con,
86                           struct fb_info *info);
87 static int s3trio_set_var(struct fb_var_screeninfo *var, int con,
88                           struct fb_info *info);
89 static int s3trio_get_cmap(struct fb_cmap *cmap, int kspc, int con,
90                            struct fb_info *info);
91 static int s3trio_set_cmap(struct fb_cmap *cmap, int kspc, int con,
92                            struct fb_info *info);
93 static int s3trio_pan_display(struct fb_var_screeninfo *var, int con,
94                               struct fb_info *info);
95
96
97     /*
98      *  Interface to the low level console driver
99      */
100
101 int s3triofb_init(void);
102 static int s3triofbcon_switch(int con, struct fb_info *info);
103 static int s3triofbcon_updatevar(int con, struct fb_info *info);
104 static void s3triofbcon_blank(int blank, struct fb_info *info);
105 #if 0
106 static int s3triofbcon_setcmap(struct fb_cmap *cmap, int con);
107 #endif
108
109     /*
110      *  Text console acceleration
111      */
112
113 #ifdef FBCON_HAS_CFB8
114 static struct display_switch fbcon_trio8;
115 #endif
116
117     /*
118      *    Accelerated Functions used by the low level console driver
119      */
120
121 static void Trio_WaitQueue(u_short fifo);
122 static void Trio_WaitBlit(void);
123 static void Trio_BitBLT(u_short curx, u_short cury, u_short destx,
124                         u_short desty, u_short width, u_short height,
125                         u_short mode);
126 static void Trio_RectFill(u_short x, u_short y, u_short width, u_short height,
127                           u_short mode, u_short color);
128 static void Trio_MoveCursor(u_short x, u_short y);
129
130
131     /*
132      *  Internal routines
133      */
134
135 static int s3trio_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
136                          u_int *transp, struct fb_info *info);
137 static int s3trio_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
138                          u_int transp, struct fb_info *info);
139 static void do_install_cmap(int con, struct fb_info *info);
140
141
142 static struct fb_ops s3trio_ops = {
143         owner:          THIS_MODULE,
144         fb_get_fix:     s3trio_get_fix,
145         fb_get_var:     s3trio_get_var,
146         fb_set_var:     s3trio_set_var,
147         fb_get_cmap:    s3trio_get_cmap,
148         fb_set_cmap:    s3trio_set_cmap,
149         fb_pan_display: s3trio_pan_display,
150 };
151
152     /*
153      *  Get the Fixed Part of the Display
154      */
155
156 static int s3trio_get_fix(struct fb_fix_screeninfo *fix, int con,
157                           struct fb_info *info)
158 {
159     memcpy(fix, &fb_fix, sizeof(fb_fix));
160     return 0;
161 }
162
163
164     /*
165      *  Get the User Defined Part of the Display
166      */
167
168 static int s3trio_get_var(struct fb_var_screeninfo *var, int con,
169                           struct fb_info *info)
170 {
171     memcpy(var, &fb_var, sizeof(fb_var));
172     return 0;
173 }
174
175
176     /*
177      *  Set the User Defined Part of the Display
178      */
179
180 static int s3trio_set_var(struct fb_var_screeninfo *var, int con,
181                           struct fb_info *info)
182 {
183     if (var->xres > fb_var.xres || var->yres > fb_var.yres ||
184         var->bits_per_pixel > fb_var.bits_per_pixel )
185         /* || var->nonstd || var->vmode != FB_VMODE_NONINTERLACED) */
186         return -EINVAL;
187     if (var->xres_virtual > fb_var.xres_virtual) {
188         outw(IO_OUT16VAL((var->xres_virtual /8) & 0xff, 0x13), 0x3d4);
189         outw(IO_OUT16VAL(((var->xres_virtual /8 ) & 0x300) >> 3, 0x51), 0x3d4);
190         fb_var.xres_virtual = var->xres_virtual;
191         fb_fix.line_length = var->xres_virtual;
192     }
193     fb_var.yres_virtual = var->yres_virtual;
194     memcpy(var, &fb_var, sizeof(fb_var));
195     return 0;
196 }
197
198
199     /*
200      *  Pan or Wrap the Display
201      *
202      *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
203      */
204
205 static int s3trio_pan_display(struct fb_var_screeninfo *var, int con,
206                               struct fb_info *info)
207 {
208     unsigned int base;
209
210     if (var->xoffset > (var->xres_virtual - var->xres))
211         return -EINVAL;
212     if (var->yoffset > (var->yres_virtual - var->yres))
213         return -EINVAL;
214
215     fb_var.xoffset = var->xoffset;
216     fb_var.yoffset = var->yoffset;
217
218     base = var->yoffset * fb_fix.line_length + var->xoffset;
219
220     outw(IO_OUT16VAL((base >> 8) & 0xff, 0x0c),0x03D4);
221     outw(IO_OUT16VAL(base  & 0xff, 0x0d),0x03D4);
222     outw(IO_OUT16VAL((base >> 16) & 0xf, 0x69),0x03D4);
223     return 0;
224 }
225
226
227     /*
228      *  Get the Colormap
229      */
230
231 static int s3trio_get_cmap(struct fb_cmap *cmap, int kspc, int con,
232                            struct fb_info *info)
233 {
234     if (con == currcon) /* current console? */
235         return fb_get_cmap(cmap, kspc, s3trio_getcolreg, info);
236     else if (fb_display[con].cmap.len) /* non default colormap? */
237         fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
238     else
239         fb_copy_cmap(fb_default_cmap(1 << fb_display[con].var.bits_per_pixel),
240                      cmap, kspc ? 0 : 2);
241     return 0;
242 }
243
244     /*
245      *  Set the Colormap
246      */
247
248 static int s3trio_set_cmap(struct fb_cmap *cmap, int kspc, int con,
249                            struct fb_info *info)
250 {
251     int err;
252
253
254     if (!fb_display[con].cmap.len) {    /* no colormap allocated? */
255         if ((err = fb_alloc_cmap(&fb_display[con].cmap,
256                                  1<<fb_display[con].var.bits_per_pixel, 0)))
257             return err;
258     }
259     if (con == currcon)                 /* current console? */
260         return fb_set_cmap(cmap, kspc, s3trio_setcolreg, info);
261     else
262         fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
263     return 0;
264 }
265
266 int __init s3triofb_setup(char *options) {
267         char *this_opt;
268
269         if (!options || !*options)
270                 return 0;
271
272         while ((this_opt = strsep(&options, ",")) != NULL) {
273                 if (!*this_opt)
274                         continue;
275
276                 if (!strcmp(this_opt, "disabled"))
277                         disabled = 1;
278         }
279         return 0;
280 }
281
282 int __init s3triofb_init(void)
283 {
284         struct pci_dev *dp = NULL;
285
286         if (disabled)
287                 return -ENXIO;
288
289         dp = pci_find_device(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_TRIO, dp);
290         if ((dp != 0) && ((dp->class >> 16) == PCI_BASE_CLASS_DISPLAY)) {
291                 s3triofb_pci_init(dp);
292                 return 0;
293         } else
294                 return -ENODEV;
295 }
296
297 static void __exit s3triofb_exit(void)
298 {
299     unregister_framebuffer(&fb_info);
300     iounmap(s3trio_base);
301     /* XXX unshare VGA regions */
302 }
303
304 void __init s3trio_resetaccel(void){
305
306
307 #define EC01_ENH_ENB    0x0005
308 #define EC01_LAW_ENB    0x0010
309 #define EC01_MMIO_ENB   0x0020
310
311 #define EC00_RESET      0x8000
312 #define EC00_ENABLE     0x4000
313 #define MF_MULT_MISC    0xE000
314 #define SRC_FOREGROUND  0x0020
315 #define SRC_BACKGROUND  0x0000
316 #define MIX_SRC                 0x0007
317 #define MF_T_CLIP       0x1000
318 #define MF_L_CLIP       0x2000
319 #define MF_B_CLIP       0x3000
320 #define MF_R_CLIP       0x4000
321 #define MF_PIX_CONTROL  0xA000
322 #define MFA_SRC_FOREGR_MIX      0x0000
323 #define MF_PIX_CONTROL  0xA000
324
325         outw(EC00_RESET,  0x42e8);
326         inw(  0x42e8);
327         outw(EC00_ENABLE,  0x42e8);
328         inw(  0x42e8);
329         outw(EC01_ENH_ENB | EC01_LAW_ENB,
330                    0x4ae8);
331         outw(MF_MULT_MISC,  0xbee8); /* 16 bit I/O registers */
332
333         /* Now set some basic accelerator registers */
334         Trio_WaitQueue(0x0400);
335         outw(SRC_FOREGROUND | MIX_SRC, 0xbae8);
336         outw(SRC_BACKGROUND | MIX_SRC,  0xb6e8);/* direct color*/
337         outw(MF_T_CLIP | 0, 0xbee8 );     /* clip virtual area  */
338         outw(MF_L_CLIP | 0, 0xbee8 );
339         outw(MF_R_CLIP | (640 - 1), 0xbee8);
340         outw(MF_B_CLIP | (480 - 1),  0xbee8);
341         Trio_WaitQueue(0x0400);
342         outw(0xffff,  0xaae8);       /* Enable all planes */
343         outw(0xffff, 0xaae8);       /* Enable all planes */
344         outw( MF_PIX_CONTROL | MFA_SRC_FOREGR_MIX,  0xbee8);
345 }
346
347 int __init s3trio_init(struct pci_dev *dp)
348 {
349         /* unlock s3 */
350         outb(0x01, 0x3C3);
351         outb(inb(0x03CC) | 1, 0x3c2);
352         outw(IO_OUT16VAL(0x48, 0x38),0x03D4);
353         outw(IO_OUT16VAL(0xA0, 0x39),0x03D4);
354         outb(0x33,0x3d4);
355         outw(IO_OUT16VAL((inb(0x3d5) & ~(0x2 | 0x10 |  0x40)) |
356                           0x20, 0x33), 0x3d4);
357
358         outw(IO_OUT16VAL(0x6, 0x8), 0x3c4);
359         printk("S3trio: unlocked s3\n");
360
361         /* switch to MMIO only mode */
362         outb(0x58, 0x3d4);
363         outw(IO_OUT16VAL(inb(0x3d5) | 3 | 0x10, 0x58), 0x3d4);
364         outw(IO_OUT16VAL(8, 0x53), 0x3d4);
365         printk("S3trio: switched to mmio only mode\n");
366
367         return 1;
368 }
369
370
371     /*
372      *  Initialisation
373      */
374
375 static void __init s3triofb_pci_init(struct pci_dev *dp)
376 {
377     int i;
378     unsigned long address, size;
379     u_long *CursorBase;
380     u16 cmd;
381
382     strcpy(fb_fix.id, s3trio_name);
383
384     fb_var = S3triofb_default_var;
385     fb_fix.line_length = fb_var.xres_virtual;
386     fb_fix.smem_len = fb_fix.line_length*fb_var.yres;
387
388     /* This driver cannot cope if the firmware has not initialised the
389      * device.  So, if the device isn't enabled we simply return
390      */
391     pci_read_config_word(dp, PCI_COMMAND, &cmd);
392     if (!(cmd & PCI_COMMAND_MEMORY)) {
393             printk(KERN_NOTICE "S3trio: card was not initialised by firmware\n");
394             return;
395     }
396
397     /* Enable it anyway */
398     if (pci_enable_device(dp)) {
399             printk(KERN_ERR "S3trio: failed to enable PCI device\n");
400             return;
401     }
402
403     /* There is only one memory region and it covers the mmio and fb areas */
404     address = pci_resource_start(dp, 0);
405     size    = pci_resource_len(dp, 0); /* size = 64*1024*1024; */
406     if (!request_mem_region(address, size, "S3triofb")) {
407         printk("S3trio: failed to allocate memory region\n");
408         return;
409     }
410
411     s3trio_init(dp);
412     s3trio_base = ioremap(address, size);
413     fb_fix.smem_start = address;
414     fb_fix.type = FB_TYPE_PACKED_PIXELS;
415     fb_fix.type_aux = 0;
416     fb_fix.accel = FB_ACCEL_S3_TRIO64;
417     fb_fix.mmio_start = address+0x1000000;
418     fb_fix.mmio_len = 0x1000000;
419
420     fb_fix.xpanstep = 1;
421     fb_fix.ypanstep = 1;
422
423     s3trio_resetaccel();
424
425     mem_out8(0x30, s3trio_base+0x1008000 + 0x03D4);
426     mem_out8(0x2d, s3trio_base+0x1008000 + 0x03D4);
427     mem_out8(0x2e, s3trio_base+0x1008000 + 0x03D4);
428
429     mem_out8(0x50, s3trio_base+0x1008000 + 0x03D4);
430
431     /* disable HW cursor */
432
433     mem_out8(0x39, s3trio_base+0x1008000 + 0x03D4);
434     mem_out8(0xa0, s3trio_base+0x1008000 + 0x03D5);
435
436     mem_out8(0x45, s3trio_base+0x1008000 + 0x03D4);
437     mem_out8(0, s3trio_base+0x1008000 + 0x03D5);
438
439     mem_out8(0x4e, s3trio_base+0x1008000 + 0x03D4);
440     mem_out8(0, s3trio_base+0x1008000 + 0x03D5);
441
442     mem_out8(0x4f, s3trio_base+0x1008000 + 0x03D4);
443     mem_out8(0, s3trio_base+0x1008000 + 0x03D5);
444
445     /* init HW cursor */
446
447     CursorBase = (u_long *)(s3trio_base + 2*1024*1024 - 0x400);
448         for (i = 0; i < 8; i++) {
449                 *(CursorBase  +(i*4)) = 0xffffff00;
450                 *(CursorBase+1+(i*4)) = 0xffff0000;
451                 *(CursorBase+2+(i*4)) = 0xffff0000;
452                 *(CursorBase+3+(i*4)) = 0xffff0000;
453         }
454         for (i = 8; i < 64; i++) {
455                 *(CursorBase  +(i*4)) = 0xffff0000;
456                 *(CursorBase+1+(i*4)) = 0xffff0000;
457                 *(CursorBase+2+(i*4)) = 0xffff0000;
458                 *(CursorBase+3+(i*4)) = 0xffff0000;
459         }
460
461
462     mem_out8(0x4c, s3trio_base+0x1008000 + 0x03D4);
463     mem_out8(((2*1024 - 1)&0xf00)>>8, s3trio_base+0x1008000 + 0x03D5);
464
465     mem_out8(0x4d, s3trio_base+0x1008000 + 0x03D4);
466     mem_out8((2*1024 - 1) & 0xff, s3trio_base+0x1008000 + 0x03D5);
467
468     mem_out8(0x45, s3trio_base+0x1008000 + 0x03D4);
469     mem_in8(s3trio_base+0x1008000 + 0x03D4);
470
471     mem_out8(0x4a, s3trio_base+0x1008000 + 0x03D4);
472     mem_out8(0x80, s3trio_base+0x1008000 + 0x03D5);
473     mem_out8(0x80, s3trio_base+0x1008000 + 0x03D5);
474     mem_out8(0x80, s3trio_base+0x1008000 + 0x03D5);
475
476     mem_out8(0x4b, s3trio_base+0x1008000 + 0x03D4);
477     mem_out8(0x00, s3trio_base+0x1008000 + 0x03D5);
478     mem_out8(0x00, s3trio_base+0x1008000 + 0x03D5);
479     mem_out8(0x00, s3trio_base+0x1008000 + 0x03D5);
480
481     mem_out8(0x45, s3trio_base+0x1008000 + 0x03D4);
482     mem_out8(0, s3trio_base+0x1008000 + 0x03D5);
483
484     /* setup default color table */
485
486         for(i = 0; i < 16; i++) {
487                 int j = color_table[i];
488                 palette[i].red=default_red[j];
489                 palette[i].green=default_grn[j];
490                 palette[i].blue=default_blu[j];
491         }
492
493     s3trio_setcolreg(255, 56, 100, 160, 0, NULL /* not used */);
494     s3trio_setcolreg(254, 0, 0, 0, 0, NULL /* not used */);
495     memset((char *)s3trio_base, 0, 640*480);
496
497 #if 0
498     Trio_RectFill(0, 0, 90, 90, 7, 1);
499 #endif
500
501     fb_fix.visual = FB_VISUAL_PSEUDOCOLOR ;
502     fb_var.xoffset = fb_var.yoffset = 0;
503     fb_var.bits_per_pixel = 8;
504     fb_var.grayscale = 0;
505     fb_var.red.offset = fb_var.green.offset = fb_var.blue.offset = 0;
506     fb_var.red.length = fb_var.green.length = fb_var.blue.length = 8;
507     fb_var.red.msb_right = fb_var.green.msb_right = fb_var.blue.msb_right = 0;
508     fb_var.transp.offset = fb_var.transp.length = fb_var.transp.msb_right = 0;
509     fb_var.nonstd = 0;
510     fb_var.activate = 0;
511     fb_var.height = fb_var.width = -1;
512     fb_var.accel_flags = FB_ACCELF_TEXT;
513 #warning FIXME: always obey fb_var.accel_flags
514     fb_var.pixclock = 1;
515     fb_var.left_margin = fb_var.right_margin = 0;
516     fb_var.upper_margin = fb_var.lower_margin = 0;
517     fb_var.hsync_len = fb_var.vsync_len = 0;
518     fb_var.sync = 0;
519     fb_var.vmode = FB_VMODE_NONINTERLACED;
520
521     disp.var = fb_var;
522     disp.cmap.start = 0;
523     disp.cmap.len = 0;
524     disp.cmap.red = disp.cmap.green = disp.cmap.blue = disp.cmap.transp = NULL;
525     disp.screen_base = s3trio_base;
526     disp.visual = fb_fix.visual;
527     disp.type = fb_fix.type;
528     disp.type_aux = fb_fix.type_aux;
529     disp.ypanstep = 0;
530     disp.ywrapstep = 0;
531     disp.line_length = fb_fix.line_length;
532     disp.can_soft_blank = 1;
533     disp.inverse = 0;
534 #ifdef FBCON_HAS_CFB8
535     if (fb_var.accel_flags & FB_ACCELF_TEXT)
536         disp.dispsw = &fbcon_trio8;
537     else
538         disp.dispsw = &fbcon_cfb8;
539 #else
540     disp.dispsw = &fbcon_dummy;
541 #endif
542     disp.scrollmode = fb_var.accel_flags & FB_ACCELF_TEXT ? 0 : SCROLL_YREDRAW;
543
544     strcpy(fb_info.modename, "Trio64");
545     fb_info.node = -1;
546     fb_info.fbops = &s3trio_ops;
547 #if 0
548     fb_info.fbvar_num = 1;
549     fb_info.fbvar = &fb_var;
550 #endif
551     fb_info.disp = &disp;
552     fb_info.fontname[0] = '\0';
553     fb_info.changevar = NULL;
554     fb_info.switch_con = &s3triofbcon_switch;
555     fb_info.updatevar = &s3triofbcon_updatevar;
556     fb_info.blank = &s3triofbcon_blank;
557 #if 0
558     fb_info.setcmap = &s3triofbcon_setcmap;
559 #endif
560
561 #ifdef CONFIG_FB_COMPAT_XPMAC
562     if (!console_fb_info) {
563         display_info.height = fb_var.yres;
564         display_info.width = fb_var.xres;
565         display_info.depth = 8;
566         display_info.pitch = fb_fix.line_length;
567         display_info.mode = 0;
568         strncpy(display_info.name, dp->name, sizeof(display_info.name));
569         display_info.fb_address = (unsigned long)fb_fix.smem_start;
570         display_info.disp_reg_address = address + 0x1008000;
571         display_info.cmap_adr_address = address + 0x1008000 + 0x3c8;
572         display_info.cmap_data_address = address + 0x1008000 + 0x3c9;
573         console_fb_info = &fb_info;
574     }
575 #endif /* CONFIG_FB_COMPAT_XPMAC) */
576
577     fb_info.flags = FBINFO_FLAG_DEFAULT;
578     if (register_framebuffer(&fb_info) < 0)
579         return;
580
581     printk("fb%d: S3 Trio frame buffer device on %s\n",
582            GET_FB_IDX(fb_info.node), dp->name);
583 }
584
585
586 static int s3triofbcon_switch(int con, struct fb_info *info)
587 {
588     /* Do we have to save the colormap? */
589     if (fb_display[currcon].cmap.len)
590         fb_get_cmap(&fb_display[currcon].cmap, 1, s3trio_getcolreg, info);
591
592     currcon = con;
593     /* Install new colormap */
594     do_install_cmap(con,info);
595     return 0;
596 }
597
598     /*
599      *  Update the `var' structure (called by fbcon.c)
600      */
601
602 static int s3triofbcon_updatevar(int con, struct fb_info *info)
603 {
604     /* Nothing */
605     return 0;
606 }
607
608     /*
609      *  Blank the display.
610      */
611
612 static void s3triofbcon_blank(int blank, struct fb_info *info)
613 {
614     unsigned char x;
615
616     mem_out8(0x1, s3trio_base+0x1008000 + 0x03c4);
617     x = mem_in8(s3trio_base+0x1008000 + 0x03c5);
618     mem_out8((x & (~0x20)) | (blank << 5), s3trio_base+0x1008000 + 0x03c5);
619 }
620
621     /*
622      *  Set the colormap
623      */
624
625 #if 0
626 static int s3triofbcon_setcmap(struct fb_cmap *cmap, int con)
627 {
628     return(s3trio_set_cmap(cmap, 1, con, &fb_info));
629 }
630 #endif
631
632
633     /*
634      *  Read a single color register and split it into
635      *  colors/transparent. Return != 0 for invalid regno.
636      */
637
638 static int s3trio_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
639                          u_int *transp, struct fb_info *info)
640 {
641     if (regno > 255)
642         return 1;
643     *red = (palette[regno].red << 8) | palette[regno].red;
644     *green = (palette[regno].green << 8) | palette[regno].green;
645     *blue = (palette[regno].blue << 8) | palette[regno].blue;
646     *transp = 0;
647     return 0;
648 }
649
650
651     /*
652      *  Set a single color register. Return != 0 for invalid regno.
653      */
654
655 static int s3trio_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
656                             u_int transp, struct fb_info *info)
657 {
658     if (regno > 255)
659         return 1;
660
661     red >>= 8;
662     green >>= 8;
663     blue >>= 8;
664     palette[regno].red = red;
665     palette[regno].green = green;
666     palette[regno].blue = blue;
667
668     mem_out8(regno,s3trio_base+0x1008000 + 0x3c8);
669     mem_out8((red & 0xff) >> 2,s3trio_base+0x1008000 + 0x3c9);
670     mem_out8((green & 0xff) >> 2,s3trio_base+0x1008000 + 0x3c9);
671     mem_out8((blue & 0xff) >> 2,s3trio_base+0x1008000 + 0x3c9);
672
673     return 0;
674 }
675
676
677 static void do_install_cmap(int con, struct fb_info *info)
678 {
679     if (con != currcon)
680         return;
681     if (fb_display[con].cmap.len)
682         fb_set_cmap(&fb_display[con].cmap, 1, s3trio_setcolreg, &fb_info);
683     else
684         fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel), 1,
685                     s3trio_setcolreg, &fb_info);
686 }
687
688 static void Trio_WaitQueue(u_short fifo) {
689
690         u_short status;
691
692         do
693         {
694                 status = mem_in16(s3trio_base + 0x1000000 + 0x9AE8);
695         }  while (!(status & fifo));
696
697 }
698
699 static void Trio_WaitBlit(void) {
700
701         u_short status;
702
703         do
704         {
705                 status = mem_in16(s3trio_base + 0x1000000 + 0x9AE8);
706         }  while (status & 0x200);
707
708 }
709
710 static void Trio_BitBLT(u_short curx, u_short cury, u_short destx,
711                         u_short desty, u_short width, u_short height,
712                         u_short mode) {
713
714         u_short blitcmd = 0xc011;
715
716         /* Set drawing direction */
717         /* -Y, X maj, -X (default) */
718
719         if (curx > destx)
720                 blitcmd |= 0x0020;  /* Drawing direction +X */
721         else {
722                 curx  += (width - 1);
723                 destx += (width - 1);
724         }
725
726         if (cury > desty)
727                 blitcmd |= 0x0080;  /* Drawing direction +Y */
728         else {
729                 cury  += (height - 1);
730                 desty += (height - 1);
731         }
732
733         Trio_WaitQueue(0x0400);
734
735         outw(0xa000,  0xBEE8);
736         outw(0x60 | mode,  0xBAE8);
737
738         outw(curx,  0x86E8);
739         outw(cury,  0x82E8);
740
741         outw(destx,  0x8EE8);
742         outw(desty,  0x8AE8);
743
744         outw(height - 1,  0xBEE8);
745         outw(width - 1,  0x96E8);
746
747         outw(blitcmd,  0x9AE8);
748
749 }
750
751 static void Trio_RectFill(u_short x, u_short y, u_short width, u_short height,
752                           u_short mode, u_short color) {
753
754         u_short blitcmd = 0x40b1;
755
756         Trio_WaitQueue(0x0400);
757
758         outw(0xa000,  0xBEE8);
759         outw((0x20 | mode),  0xBAE8);
760         outw(0xe000,  0xBEE8);
761         outw(color,  0xA6E8);
762         outw(x,  0x86E8);
763         outw(y,  0x82E8);
764         outw((height - 1), 0xBEE8);
765         outw((width - 1), 0x96E8);
766         outw(blitcmd,  0x9AE8);
767
768 }
769
770
771 static void Trio_MoveCursor(u_short x, u_short y) {
772
773         mem_out8(0x39, s3trio_base + 0x1008000 + 0x3d4);
774         mem_out8(0xa0, s3trio_base + 0x1008000 + 0x3d5);
775
776         mem_out8(0x46, s3trio_base + 0x1008000 + 0x3d4);
777         mem_out8((x & 0x0700) >> 8, s3trio_base + 0x1008000 + 0x3d5);
778         mem_out8(0x47, s3trio_base + 0x1008000 + 0x3d4);
779         mem_out8(x & 0x00ff, s3trio_base + 0x1008000 + 0x3d5);
780
781         mem_out8(0x48, s3trio_base + 0x1008000 + 0x3d4);
782         mem_out8((y & 0x0700) >> 8, s3trio_base + 0x1008000 + 0x3d5);
783         mem_out8(0x49, s3trio_base + 0x1008000 + 0x3d4);
784         mem_out8(y & 0x00ff, s3trio_base + 0x1008000 + 0x3d5);
785
786 }
787
788
789     /*
790      *  Text console acceleration
791      */
792
793 #ifdef FBCON_HAS_CFB8
794 static void fbcon_trio8_bmove(struct display *p, int sy, int sx, int dy,
795                               int dx, int height, int width)
796 {
797     sx *= 8; dx *= 8; width *= 8;
798     Trio_BitBLT((u_short)sx, (u_short)(sy*fontheight(p)), (u_short)dx,
799                  (u_short)(dy*fontheight(p)), (u_short)width,
800                  (u_short)(height*fontheight(p)), (u_short)S3_NEW);
801 }
802
803 static void fbcon_trio8_clear(struct vc_data *conp, struct display *p, int sy,
804                               int sx, int height, int width)
805 {
806     unsigned char bg;
807
808     sx *= 8; width *= 8;
809     bg = attr_bgcol_ec(p,conp);
810     Trio_RectFill((u_short)sx,
811                    (u_short)(sy*fontheight(p)),
812                    (u_short)width,
813                    (u_short)(height*fontheight(p)),
814                    (u_short)S3_NEW,
815                    (u_short)bg);
816 }
817
818 static void fbcon_trio8_putc(struct vc_data *conp, struct display *p, int c,
819                              int yy, int xx)
820 {
821     Trio_WaitBlit();
822     fbcon_cfb8_putc(conp, p, c, yy, xx);
823 }
824
825 static void fbcon_trio8_putcs(struct vc_data *conp, struct display *p,
826                               const unsigned short *s, int count, int yy, int xx)
827 {
828     Trio_WaitBlit();
829     fbcon_cfb8_putcs(conp, p, s, count, yy, xx);
830 }
831
832 static void fbcon_trio8_revc(struct display *p, int xx, int yy)
833 {
834     Trio_WaitBlit();
835     fbcon_cfb8_revc(p, xx, yy);
836 }
837
838 static struct display_switch fbcon_trio8 = {
839    setup:               fbcon_cfb8_setup,
840    bmove:               fbcon_trio8_bmove,
841    clear:               fbcon_trio8_clear,
842    putc:                fbcon_trio8_putc,
843    putcs:               fbcon_trio8_putcs,
844    revc:                fbcon_trio8_revc,
845    clear_margins:       fbcon_cfb8_clear_margins,
846    fontwidthmask:       FONTWIDTH(8)
847 };
848 #endif
849
850 #ifdef MODULE
851 module_init(s3triofb_init);
852 MODULE_LICENSE("GPL");
853 #endif
854 module_exit(s3triofb_exit);