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