Merge branch 'upstream' of git://lost.foo-projects.org/~ahkok/git/netdev-2.6 into...
[linux-2.6] / 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 : + OF dependencies should be removed.
21                + This driver should be merged with the CyberVision driver. The
22                  CyberVision is a Zorro III implementation of the S3Trio64 chip.
23
24 */
25
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 <asm/io.h>
40 #include <asm/prom.h>
41 #include <asm/pci-bridge.h>
42 #include <linux/pci.h>
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 static struct display disp;
60 static struct fb_info fb_info;
61 static struct { u_char red, green, blue, pad; } palette[256];
62 static char s3trio_name[16] = "S3Trio ";
63 static char *s3trio_base;
64
65 static struct fb_fix_screeninfo fb_fix;
66 static struct fb_var_screeninfo fb_var = { 0, };
67
68
69     /*
70      *  Interface used by the world
71      */
72
73 static void __init s3triofb_of_init(struct device_node *dp);
74 static int s3trio_get_fix(struct fb_fix_screeninfo *fix, int con,
75                           struct fb_info *info);
76 static int s3trio_get_var(struct fb_var_screeninfo *var, int con,
77                           struct fb_info *info);
78 static int s3trio_set_var(struct fb_var_screeninfo *var, int con,
79                           struct fb_info *info);
80 static int s3trio_get_cmap(struct fb_cmap *cmap, int kspc, int con,
81                            struct fb_info *info);
82 static int s3trio_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
83                          u_int transp, struct fb_info *info);
84 static int s3trio_pan_display(struct fb_var_screeninfo *var, int con,
85                               struct fb_info *info);
86 static void s3triofb_blank(int blank, struct fb_info *info);
87
88     /*
89      *  Interface to the low level console driver
90      */
91
92 int s3triofb_init(void);
93 static int s3triofbcon_switch(int con, struct fb_info *info);
94 static int s3triofbcon_updatevar(int con, struct fb_info *info);
95
96     /*
97      *  Text console acceleration
98      */
99
100 #ifdef FBCON_HAS_CFB8
101 static struct display_switch fbcon_trio8;
102 #endif
103
104     /*
105      *    Accelerated Functions used by the low level console driver
106      */
107
108 static void Trio_WaitQueue(u_short fifo);
109 static void Trio_WaitBlit(void);
110 static void Trio_BitBLT(u_short curx, u_short cury, u_short destx,
111                         u_short desty, u_short width, u_short height,
112                         u_short mode);
113 static void Trio_RectFill(u_short x, u_short y, u_short width, u_short height,
114                           u_short mode, u_short color);
115 static void Trio_MoveCursor(u_short x, u_short y);
116
117
118     /*
119      *  Internal routines
120      */
121
122 static int s3trio_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
123                          u_int *transp, struct fb_info *info);
124
125 static struct fb_ops s3trio_ops = {
126         .owner =        THIS_MODULE,
127         .fb_get_fix =   s3trio_get_fix,
128         .fb_get_var =   s3trio_get_var,
129         .fb_set_var =   s3trio_set_var,
130         .fb_get_cmap =  s3trio_get_cmap,
131         .fb_set_cmap =  gen_set_cmap,
132         .fb_setcolreg = s3trio_setcolreg,
133         .fb_pan_display =s3trio_pan_display,
134         .fb_blank =     s3triofb_blank,
135 };
136
137     /*
138      *  Get the Fixed Part of the Display
139      */
140
141 static int s3trio_get_fix(struct fb_fix_screeninfo *fix, int con,
142                           struct fb_info *info)
143 {
144     memcpy(fix, &fb_fix, sizeof(fb_fix));
145     return 0;
146 }
147
148
149     /*
150      *  Get the User Defined Part of the Display
151      */
152
153 static int s3trio_get_var(struct fb_var_screeninfo *var, int con,
154                           struct fb_info *info)
155 {
156     memcpy(var, &fb_var, sizeof(fb_var));
157     return 0;
158 }
159
160
161     /*
162      *  Set the User Defined Part of the Display
163      */
164
165 static int s3trio_set_var(struct fb_var_screeninfo *var, int con,
166                           struct fb_info *info)
167 {
168     if (var->xres > fb_var.xres || var->yres > fb_var.yres ||
169         var->bits_per_pixel > fb_var.bits_per_pixel )
170         /* || var->nonstd || var->vmode != FB_VMODE_NONINTERLACED) */
171         return -EINVAL;
172     if (var->xres_virtual > fb_var.xres_virtual) {
173         outw(IO_OUT16VAL((var->xres_virtual /8) & 0xff, 0x13), 0x3d4);
174         outw(IO_OUT16VAL(((var->xres_virtual /8 ) & 0x300) >> 3, 0x51), 0x3d4);
175         fb_var.xres_virtual = var->xres_virtual;
176         fb_fix.line_length = var->xres_virtual;
177     }
178     fb_var.yres_virtual = var->yres_virtual;
179     memcpy(var, &fb_var, sizeof(fb_var));
180     return 0;
181 }
182
183
184     /*
185      *  Pan or Wrap the Display
186      *
187      *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
188      */
189
190 static int s3trio_pan_display(struct fb_var_screeninfo *var, int con,
191                               struct fb_info *info)
192 {
193     unsigned int base;
194
195     if (var->xoffset > (var->xres_virtual - var->xres))
196         return -EINVAL;
197     if (var->yoffset > (var->yres_virtual - var->yres))
198         return -EINVAL;
199
200     fb_var.xoffset = var->xoffset;
201     fb_var.yoffset = var->yoffset;
202
203     base = var->yoffset * fb_fix.line_length + var->xoffset;
204
205     outw(IO_OUT16VAL((base >> 8) & 0xff, 0x0c),0x03D4);
206     outw(IO_OUT16VAL(base  & 0xff, 0x0d),0x03D4);
207     outw(IO_OUT16VAL((base >> 16) & 0xf, 0x69),0x03D4);
208     return 0;
209 }
210
211
212     /*
213      *  Get the Colormap
214      */
215
216 static int s3trio_get_cmap(struct fb_cmap *cmap, int kspc, int con,
217                            struct fb_info *info)
218 {
219     if (con == info->currcon) /* current console? */
220         return fb_get_cmap(cmap, kspc, s3trio_getcolreg, info);
221     else if (fb_display[con].cmap.len) /* non default colormap? */
222         fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
223     else
224         fb_copy_cmap(fb_default_cmap(1 << fb_display[con].var.bits_per_pixel),
225                      cmap, kspc ? 0 : 2);
226     return 0;
227 }
228
229 int __init s3triofb_init(void)
230 {
231         struct device_node *dp;
232
233         dp = find_devices("S3Trio");
234         if (dp != 0)
235             s3triofb_of_init(dp);
236         return 0;
237 }
238
239 void __init s3trio_resetaccel(void){
240
241
242 #define EC01_ENH_ENB    0x0005
243 #define EC01_LAW_ENB    0x0010
244 #define EC01_MMIO_ENB   0x0020
245
246 #define EC00_RESET      0x8000
247 #define EC00_ENABLE     0x4000
248 #define MF_MULT_MISC    0xE000
249 #define SRC_FOREGROUND  0x0020
250 #define SRC_BACKGROUND  0x0000
251 #define MIX_SRC                 0x0007
252 #define MF_T_CLIP       0x1000
253 #define MF_L_CLIP       0x2000
254 #define MF_B_CLIP       0x3000
255 #define MF_R_CLIP       0x4000
256 #define MF_PIX_CONTROL  0xA000
257 #define MFA_SRC_FOREGR_MIX      0x0000
258 #define MF_PIX_CONTROL  0xA000
259
260         outw(EC00_RESET,  0x42e8);
261         inw(  0x42e8);
262         outw(EC00_ENABLE,  0x42e8);
263         inw(  0x42e8);
264         outw(EC01_ENH_ENB | EC01_LAW_ENB,
265                    0x4ae8);
266         outw(MF_MULT_MISC,  0xbee8); /* 16 bit I/O registers */
267
268         /* Now set some basic accelerator registers */
269         Trio_WaitQueue(0x0400);
270         outw(SRC_FOREGROUND | MIX_SRC, 0xbae8);
271         outw(SRC_BACKGROUND | MIX_SRC,  0xb6e8);/* direct color*/
272         outw(MF_T_CLIP | 0, 0xbee8 );     /* clip virtual area  */
273         outw(MF_L_CLIP | 0, 0xbee8 );
274         outw(MF_R_CLIP | (640 - 1), 0xbee8);
275         outw(MF_B_CLIP | (480 - 1),  0xbee8);
276         Trio_WaitQueue(0x0400);
277         outw(0xffff,  0xaae8);       /* Enable all planes */
278         outw(0xffff, 0xaae8);       /* Enable all planes */
279         outw( MF_PIX_CONTROL | MFA_SRC_FOREGR_MIX,  0xbee8);
280 }
281
282 int __init s3trio_init(struct device_node *dp){
283
284     u_char bus, dev;
285     unsigned int t32;
286     unsigned short cmd;
287
288         pci_device_loc(dp,&bus,&dev);
289                 pcibios_read_config_dword(bus, dev, PCI_VENDOR_ID, &t32);
290                 if(t32 == (PCI_DEVICE_ID_S3_TRIO << 16) + PCI_VENDOR_ID_S3) {
291                         pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_0, &t32);
292                         pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_1, &t32);
293                         pcibios_read_config_word(bus, dev, PCI_COMMAND,&cmd);
294
295                         pcibios_write_config_word(bus, dev, PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
296
297                         pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_0,0xffffffff);
298                         pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_0, &t32);
299
300 /* This is a gross hack as OF only maps enough memory for the framebuffer and
301    we want to use MMIO too. We should find out which chunk of address space
302    we can use here */
303                         pcibios_write_config_dword(bus,dev,PCI_BASE_ADDRESS_0,0xc6000000);
304
305                         /* unlock s3 */
306
307                         outb(0x01, 0x3C3);
308
309                         outb(inb(0x03CC) | 1, 0x3c2);
310
311                         outw(IO_OUT16VAL(0x48, 0x38),0x03D4);
312                         outw(IO_OUT16VAL(0xA0, 0x39),0x03D4);
313                         outb(0x33,0x3d4);
314                         outw(IO_OUT16VAL((inb(0x3d5) & ~(0x2 | 0x10 |  0x40)) |
315                                           0x20, 0x33), 0x3d4);
316
317                         outw(IO_OUT16VAL(0x6, 0x8), 0x3c4);
318
319                         /* switch to MMIO only mode */
320
321                         outb(0x58, 0x3d4);
322                         outw(IO_OUT16VAL(inb(0x3d5) | 3 | 0x10, 0x58), 0x3d4);
323                         outw(IO_OUT16VAL(8, 0x53), 0x3d4);
324
325                         /* switch off I/O accesses */
326
327 #if 0
328                         pcibios_write_config_word(bus, dev, PCI_COMMAND,
329                                         PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
330 #endif
331                         return 1;
332                 }
333
334         return 0;
335 }
336
337
338     /*
339      *  Initialisation
340      *  We heavily rely on OF for the moment. This needs fixing.
341      */
342
343 static void __init s3triofb_of_init(struct device_node *dp)
344 {
345     int i, *pp, len;
346     unsigned long address, size;
347     u_long *CursorBase;
348
349     strncat(s3trio_name, dp->name, sizeof(s3trio_name));
350     s3trio_name[sizeof(s3trio_name)-1] = '\0';
351     strcpy(fb_fix.id, s3trio_name);
352
353     if((pp = (int *)get_property(dp, "vendor-id", &len)) != NULL
354         && *pp!=PCI_VENDOR_ID_S3) {
355         printk("%s: can't find S3 Trio board\n", dp->full_name);
356         return;
357     }
358
359     if((pp = (int *)get_property(dp, "device-id", &len)) != NULL
360         && *pp!=PCI_DEVICE_ID_S3_TRIO) {
361         printk("%s: can't find S3 Trio board\n", dp->full_name);
362         return;
363     }
364
365     if ((pp = (int *)get_property(dp, "depth", &len)) != NULL
366         && len == sizeof(int) && *pp != 8) {
367         printk("%s: can't use depth = %d\n", dp->full_name, *pp);
368         return;
369     }
370     if ((pp = (int *)get_property(dp, "width", &len)) != NULL
371         && len == sizeof(int))
372         fb_var.xres = fb_var.xres_virtual = *pp;
373     if ((pp = (int *)get_property(dp, "height", &len)) != NULL
374         && len == sizeof(int))
375         fb_var.yres = fb_var.yres_virtual = *pp;
376     if ((pp = (int *)get_property(dp, "linebytes", &len)) != NULL
377         && len == sizeof(int))
378         fb_fix.line_length = *pp;
379     else
380         fb_fix.line_length = fb_var.xres_virtual;
381     fb_fix.smem_len = fb_fix.line_length*fb_var.yres;
382
383     address = 0xc6000000;
384     size = 64*1024*1024;
385     if (!request_mem_region(address, size, "S3triofb"))
386         return;
387
388     s3trio_init(dp);
389     s3trio_base = ioremap(address, size);
390     fb_fix.smem_start = address;
391     fb_fix.type = FB_TYPE_PACKED_PIXELS;
392     fb_fix.type_aux = 0;
393     fb_fix.accel = FB_ACCEL_S3_TRIO64;
394     fb_fix.mmio_start = address+0x1000000;
395     fb_fix.mmio_len = 0x1000000;
396
397     fb_fix.xpanstep = 1;
398     fb_fix.ypanstep = 1;
399
400     s3trio_resetaccel();
401
402     mem_out8(0x30, s3trio_base+0x1008000 + 0x03D4);
403     mem_out8(0x2d, s3trio_base+0x1008000 + 0x03D4);
404     mem_out8(0x2e, s3trio_base+0x1008000 + 0x03D4);
405
406     mem_out8(0x50, s3trio_base+0x1008000 + 0x03D4);
407
408     /* disable HW cursor */
409
410     mem_out8(0x39, s3trio_base+0x1008000 + 0x03D4);
411     mem_out8(0xa0, s3trio_base+0x1008000 + 0x03D5);
412
413     mem_out8(0x45, s3trio_base+0x1008000 + 0x03D4);
414     mem_out8(0, s3trio_base+0x1008000 + 0x03D5);
415
416     mem_out8(0x4e, s3trio_base+0x1008000 + 0x03D4);
417     mem_out8(0, s3trio_base+0x1008000 + 0x03D5);
418
419     mem_out8(0x4f, s3trio_base+0x1008000 + 0x03D4);
420     mem_out8(0, s3trio_base+0x1008000 + 0x03D5);
421
422     /* init HW cursor */
423
424     CursorBase = (u_long *)(s3trio_base + 2*1024*1024 - 0x400);
425         for (i = 0; i < 8; i++) {
426                 *(CursorBase  +(i*4)) = 0xffffff00;
427                 *(CursorBase+1+(i*4)) = 0xffff0000;
428                 *(CursorBase+2+(i*4)) = 0xffff0000;
429                 *(CursorBase+3+(i*4)) = 0xffff0000;
430         }
431         for (i = 8; i < 64; i++) {
432                 *(CursorBase  +(i*4)) = 0xffff0000;
433                 *(CursorBase+1+(i*4)) = 0xffff0000;
434                 *(CursorBase+2+(i*4)) = 0xffff0000;
435                 *(CursorBase+3+(i*4)) = 0xffff0000;
436         }
437
438
439     mem_out8(0x4c, s3trio_base+0x1008000 + 0x03D4);
440     mem_out8(((2*1024 - 1)&0xf00)>>8, s3trio_base+0x1008000 + 0x03D5);
441
442     mem_out8(0x4d, s3trio_base+0x1008000 + 0x03D4);
443     mem_out8((2*1024 - 1) & 0xff, s3trio_base+0x1008000 + 0x03D5);
444
445     mem_out8(0x45, s3trio_base+0x1008000 + 0x03D4);
446     mem_in8(s3trio_base+0x1008000 + 0x03D4);
447
448     mem_out8(0x4a, s3trio_base+0x1008000 + 0x03D4);
449     mem_out8(0x80, s3trio_base+0x1008000 + 0x03D5);
450     mem_out8(0x80, s3trio_base+0x1008000 + 0x03D5);
451     mem_out8(0x80, s3trio_base+0x1008000 + 0x03D5);
452
453     mem_out8(0x4b, s3trio_base+0x1008000 + 0x03D4);
454     mem_out8(0x00, s3trio_base+0x1008000 + 0x03D5);
455     mem_out8(0x00, s3trio_base+0x1008000 + 0x03D5);
456     mem_out8(0x00, s3trio_base+0x1008000 + 0x03D5);
457
458     mem_out8(0x45, s3trio_base+0x1008000 + 0x03D4);
459     mem_out8(0, s3trio_base+0x1008000 + 0x03D5);
460
461     /* setup default color table */
462
463         for(i = 0; i < 16; i++) {
464                 int j = color_table[i];
465                 palette[i].red=default_red[j];
466                 palette[i].green=default_grn[j];
467                 palette[i].blue=default_blu[j];
468         }
469
470     s3trio_setcolreg(255, 56, 100, 160, 0, NULL /* not used */);
471     s3trio_setcolreg(254, 0, 0, 0, 0, NULL /* not used */);
472     memset((char *)s3trio_base, 0, 640*480);
473
474 #if 0
475     Trio_RectFill(0, 0, 90, 90, 7, 1);
476 #endif
477
478     fb_fix.visual = FB_VISUAL_PSEUDOCOLOR ;
479     fb_var.xoffset = fb_var.yoffset = 0;
480     fb_var.bits_per_pixel = 8;
481     fb_var.grayscale = 0;
482     fb_var.red.offset = fb_var.green.offset = fb_var.blue.offset = 0;
483     fb_var.red.length = fb_var.green.length = fb_var.blue.length = 8;
484     fb_var.red.msb_right = fb_var.green.msb_right = fb_var.blue.msb_right = 0;
485     fb_var.transp.offset = fb_var.transp.length = fb_var.transp.msb_right = 0;
486     fb_var.nonstd = 0;
487     fb_var.activate = 0;
488     fb_var.height = fb_var.width = -1;
489     fb_var.accel_flags = FB_ACCELF_TEXT;
490 #warning FIXME: always obey fb_var.accel_flags
491     fb_var.pixclock = 1;
492     fb_var.left_margin = fb_var.right_margin = 0;
493     fb_var.upper_margin = fb_var.lower_margin = 0;
494     fb_var.hsync_len = fb_var.vsync_len = 0;
495     fb_var.sync = 0;
496     fb_var.vmode = FB_VMODE_NONINTERLACED;
497
498     disp.var = fb_var;
499     disp.cmap.start = 0;
500     disp.cmap.len = 0;
501     disp.cmap.red = disp.cmap.green = disp.cmap.blue = disp.cmap.transp = NULL;
502     disp.visual = fb_fix.visual;
503     disp.type = fb_fix.type;
504     disp.type_aux = fb_fix.type_aux;
505     disp.ypanstep = 0;
506     disp.ywrapstep = 0;
507     disp.line_length = fb_fix.line_length;
508     disp.can_soft_blank = 1;
509     disp.inverse = 0;
510 #ifdef FBCON_HAS_CFB8
511     if (fb_var.accel_flags & FB_ACCELF_TEXT)
512         disp.dispsw = &fbcon_trio8;
513     else
514         disp.dispsw = &fbcon_cfb8;
515 #else
516     disp.dispsw = &fbcon_dummy;
517 #endif
518     disp.scrollmode = fb_var.accel_flags & FB_ACCELF_TEXT ? 0 : SCROLL_YREDRAW;
519
520     strcpy(fb_info.modename, "Trio64 ");
521     strncat(fb_info.modename, dp->full_name, sizeof(fb_info.modename));
522     fb_info.currcon = -1;
523     fb_info.fbops = &s3trio_ops;
524     fb_info.screen_base = s3trio_base;  
525 #if 0
526     fb_info.fbvar_num = 1;
527     fb_info.fbvar = &fb_var;
528 #endif
529     fb_info.disp = &disp;
530     fb_info.fontname[0] = '\0';
531     fb_info.changevar = NULL;
532     fb_info.switch_con = &s3triofbcon_switch;
533     fb_info.updatevar = &s3triofbcon_updatevar;
534 #if 0
535     fb_info.setcmap = &s3triofbcon_setcmap;
536 #endif
537
538     fb_info.flags = FBINFO_FLAG_DEFAULT;
539     if (register_framebuffer(&fb_info) < 0)
540         return;
541
542     printk("fb%d: S3 Trio frame buffer device on %s\n",
543            fb_info.node, dp->full_name);
544 }
545
546
547 static int s3triofbcon_switch(int con, struct fb_info *info)
548 {
549     /* Do we have to save the colormap? */
550     if (fb_display[info->currcon].cmap.len)
551         fb_get_cmap(&fb_display[info->currcon].cmap, 1, s3trio_getcolreg, info);
552
553     info->currcon = con;
554     /* Install new colormap */
555     do_install_cmap(con,info);
556     return 0;
557 }
558
559     /*
560      *  Update the `var' structure (called by fbcon.c)
561      */
562
563 static int s3triofbcon_updatevar(int con, struct fb_info *info)
564 {
565     /* Nothing */
566     return 0;
567 }
568
569     /*
570      *  Blank the display.
571      */
572
573 static int s3triofb_blank(int blank, struct fb_info *info)
574 {
575     unsigned char x;
576
577     mem_out8(0x1, s3trio_base+0x1008000 + 0x03c4);
578     x = mem_in8(s3trio_base+0x1008000 + 0x03c5);
579     mem_out8((x & (~0x20)) | (blank << 5), s3trio_base+0x1008000 + 0x03c5);
580     return 0;   
581 }
582
583     /*
584      *  Read a single color register and split it into
585      *  colors/transparent. Return != 0 for invalid regno.
586      */
587
588 static int s3trio_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
589                          u_int *transp, struct fb_info *info)
590 {
591     if (regno > 255)
592         return 1;
593     *red = (palette[regno].red << 8) | palette[regno].red;
594     *green = (palette[regno].green << 8) | palette[regno].green;
595     *blue = (palette[regno].blue << 8) | palette[regno].blue;
596     *transp = 0;
597     return 0;
598 }
599
600
601     /*
602      *  Set a single color register. Return != 0 for invalid regno.
603      */
604
605 static int s3trio_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
606                             u_int transp, struct fb_info *info)
607 {
608     if (regno > 255)
609         return 1;
610
611     red >>= 8;
612     green >>= 8;
613     blue >>= 8;
614     palette[regno].red = red;
615     palette[regno].green = green;
616     palette[regno].blue = blue;
617
618     mem_out8(regno,s3trio_base+0x1008000 + 0x3c8);
619     mem_out8((red & 0xff) >> 2,s3trio_base+0x1008000 + 0x3c9);
620     mem_out8((green & 0xff) >> 2,s3trio_base+0x1008000 + 0x3c9);
621     mem_out8((blue & 0xff) >> 2,s3trio_base+0x1008000 + 0x3c9);
622
623     return 0;
624 }
625
626 static void Trio_WaitQueue(u_short fifo) {
627
628         u_short status;
629
630         do
631         {
632                 status = mem_in16(s3trio_base + 0x1000000 + 0x9AE8);
633         }  while (!(status & fifo));
634
635 }
636
637 static void Trio_WaitBlit(void) {
638
639         u_short status;
640
641         do
642         {
643                 status = mem_in16(s3trio_base + 0x1000000 + 0x9AE8);
644         }  while (status & 0x200);
645
646 }
647
648 static void Trio_BitBLT(u_short curx, u_short cury, u_short destx,
649                         u_short desty, u_short width, u_short height,
650                         u_short mode) {
651
652         u_short blitcmd = 0xc011;
653
654         /* Set drawing direction */
655         /* -Y, X maj, -X (default) */
656
657         if (curx > destx)
658                 blitcmd |= 0x0020;  /* Drawing direction +X */
659         else {
660                 curx  += (width - 1);
661                 destx += (width - 1);
662         }
663
664         if (cury > desty)
665                 blitcmd |= 0x0080;  /* Drawing direction +Y */
666         else {
667                 cury  += (height - 1);
668                 desty += (height - 1);
669         }
670
671         Trio_WaitQueue(0x0400);
672
673         outw(0xa000,  0xBEE8);
674         outw(0x60 | mode,  0xBAE8);
675
676         outw(curx,  0x86E8);
677         outw(cury,  0x82E8);
678
679         outw(destx,  0x8EE8);
680         outw(desty,  0x8AE8);
681
682         outw(height - 1,  0xBEE8);
683         outw(width - 1,  0x96E8);
684
685         outw(blitcmd,  0x9AE8);
686
687 }
688
689 static void Trio_RectFill(u_short x, u_short y, u_short width, u_short height,
690                           u_short mode, u_short color) {
691
692         u_short blitcmd = 0x40b1;
693
694         Trio_WaitQueue(0x0400);
695
696         outw(0xa000,  0xBEE8);
697         outw((0x20 | mode),  0xBAE8);
698         outw(0xe000,  0xBEE8);
699         outw(color,  0xA6E8);
700         outw(x,  0x86E8);
701         outw(y,  0x82E8);
702         outw((height - 1), 0xBEE8);
703         outw((width - 1), 0x96E8);
704         outw(blitcmd,  0x9AE8);
705
706 }
707
708
709 static void Trio_MoveCursor(u_short x, u_short y) {
710
711         mem_out8(0x39, s3trio_base + 0x1008000 + 0x3d4);
712         mem_out8(0xa0, s3trio_base + 0x1008000 + 0x3d5);
713
714         mem_out8(0x46, s3trio_base + 0x1008000 + 0x3d4);
715         mem_out8((x & 0x0700) >> 8, s3trio_base + 0x1008000 + 0x3d5);
716         mem_out8(0x47, s3trio_base + 0x1008000 + 0x3d4);
717         mem_out8(x & 0x00ff, s3trio_base + 0x1008000 + 0x3d5);
718
719         mem_out8(0x48, s3trio_base + 0x1008000 + 0x3d4);
720         mem_out8((y & 0x0700) >> 8, s3trio_base + 0x1008000 + 0x3d5);
721         mem_out8(0x49, s3trio_base + 0x1008000 + 0x3d4);
722         mem_out8(y & 0x00ff, s3trio_base + 0x1008000 + 0x3d5);
723
724 }
725
726
727     /*
728      *  Text console acceleration
729      */
730
731 #ifdef FBCON_HAS_CFB8
732 static void fbcon_trio8_bmove(struct display *p, int sy, int sx, int dy,
733                               int dx, int height, int width)
734 {
735     sx *= 8; dx *= 8; width *= 8;
736     Trio_BitBLT((u_short)sx, (u_short)(sy*fontheight(p)), (u_short)dx,
737                  (u_short)(dy*fontheight(p)), (u_short)width,
738                  (u_short)(height*fontheight(p)), (u_short)S3_NEW);
739 }
740
741 static void fbcon_trio8_clear(struct vc_data *conp, struct display *p, int sy,
742                               int sx, int height, int width)
743 {
744     unsigned char bg;
745
746     sx *= 8; width *= 8;
747     bg = attr_bgcol_ec(p,conp);
748     Trio_RectFill((u_short)sx,
749                    (u_short)(sy*fontheight(p)),
750                    (u_short)width,
751                    (u_short)(height*fontheight(p)),
752                    (u_short)S3_NEW,
753                    (u_short)bg);
754 }
755
756 static void fbcon_trio8_putc(struct vc_data *conp, struct display *p, int c,
757                              int yy, int xx)
758 {
759     Trio_WaitBlit();
760     fbcon_cfb8_putc(conp, p, c, yy, xx);
761 }
762
763 static void fbcon_trio8_putcs(struct vc_data *conp, struct display *p,
764                               const unsigned short *s, int count, int yy, int xx)
765 {
766     Trio_WaitBlit();
767     fbcon_cfb8_putcs(conp, p, s, count, yy, xx);
768 }
769
770 static void fbcon_trio8_revc(struct display *p, int xx, int yy)
771 {
772     Trio_WaitBlit();
773     fbcon_cfb8_revc(p, xx, yy);
774 }
775
776 static struct display_switch fbcon_trio8 = {
777    .setup =             fbcon_cfb8_setup,
778    .bmove =             fbcon_trio8_bmove,
779    .clear =             fbcon_trio8_clear,
780    .putc =              fbcon_trio8_putc,
781    .putcs =             fbcon_trio8_putcs,
782    .revc =              fbcon_trio8_revc,
783    .clear_margins =     fbcon_cfb8_clear_margins,
784    .fontwidthmask =     FONTWIDTH(8)
785 };
786 #endif
787
788 MODULE_LICENSE("GPL");