Merge branch 'master' into for_paulus
[linux-drm-fsl-dcu.git] / drivers / video / retz3fb.c
1 /*
2  * Linux/drivers/video/retz3fb.c -- RetinaZ3 frame buffer device
3  *
4  *    Copyright (C) 1997 Jes Sorensen
5  *
6  * This file is based on the CyberVision64 frame buffer device and
7  * the generic Cirrus Logic driver.
8  *
9  * cyberfb.c: Copyright (C) 1996 Martin Apel,
10  *                               Geert Uytterhoeven
11  * clgen.c:   Copyright (C) 1996 Frank Neumann
12  *
13  * History:
14  *   - 22 Jan 97: Initial work
15  *   - 14 Feb 97: Screen initialization works somewhat, still only
16  *                8-bit packed pixel is supported.
17  *
18  * This file is subject to the terms and conditions of the GNU General Public
19  * License.  See the file COPYING in the main directory of this archive
20  * for more details.
21  */
22
23 #include <linux/module.h>
24 #include <linux/kernel.h>
25 #include <linux/errno.h>
26 #include <linux/string.h>
27 #include <linux/mm.h>
28 #include <linux/slab.h>
29 #include <linux/delay.h>
30 #include <linux/fb.h>
31 #include <linux/zorro.h>
32 #include <linux/init.h>
33
34 #include <asm/uaccess.h>
35 #include <asm/system.h>
36 #include <asm/irq.h>
37 #include <asm/pgtable.h>
38 #include <asm/io.h>
39
40 #include <video/fbcon.h>
41 #include <video/fbcon-cfb8.h>
42 #include <video/fbcon-cfb16.h>
43
44 #include "retz3fb.h"
45
46 /* #define DEBUG if(1) */
47 #define DEBUG if(0)
48
49 /*
50  * Reserve space for one pattern line.
51  *
52  * For the time being we only support 4MB boards!
53  */
54
55 #define PAT_MEM_SIZE 16*3
56 #define PAT_MEM_OFF  (4*1024*1024 - PAT_MEM_SIZE)
57
58 struct retz3fb_par {
59         int xres;
60         int yres;
61         int xres_vir;
62         int yres_vir;
63         int xoffset;
64         int yoffset;
65         int bpp;
66
67         struct fb_bitfield red;
68         struct fb_bitfield green;
69         struct fb_bitfield blue;
70         struct fb_bitfield transp;
71
72         int pixclock;
73         int left_margin;        /* time from sync to picture    */
74         int right_margin;       /* time from picture to sync    */
75         int upper_margin;       /* time from sync to picture    */
76         int lower_margin;
77         int hsync_len;  /* length of horizontal sync    */
78         int vsync_len;  /* length of vertical sync      */
79         int vmode;
80
81         int accel;
82 };
83
84 struct display_data {
85         long h_total;           /* Horizontal Total */
86         long h_sstart;          /* Horizontal Sync Start */
87         long h_sstop;           /* Horizontal Sync Stop */
88         long h_bstart;          /* Horizontal Blank Start */
89         long h_bstop;           /* Horizontal Blank Stop */
90         long h_dispend;         /* Horizontal Display End */
91         long v_total;           /* Vertical Total */
92         long v_sstart;          /* Vertical Sync Start */
93         long v_sstop;           /* Vertical Sync Stop */
94         long v_bstart;          /* Vertical Blank Start */
95         long v_bstop;           /* Vertical Blank Stop */
96         long v_dispend;         /* Horizontal Display End */
97 };
98
99 struct retz3_fb_info {
100         struct fb_info info;
101         unsigned char *base;
102         unsigned char *fbmem;
103         unsigned long fbsize;
104         volatile unsigned char *regs;
105         unsigned long physfbmem;
106         unsigned long physregs;
107         int current_par_valid; /* set to 0 by memset */
108         int blitbusy;
109         struct display disp;
110         struct retz3fb_par current_par;
111         unsigned char color_table [256][3];
112 };
113
114
115 static char fontname[40] __initdata = { 0 };
116
117 #define retz3info(info) ((struct retz3_fb_info *)(info))
118 #define fbinfo(info) ((struct fb_info *)(info))
119
120
121 /*
122  *    Frame Buffer Name
123  */
124
125 static char retz3fb_name[16] = "RetinaZ3";
126
127
128 /*
129  * A small info on how to convert XFree86 timing values into fb
130  * timings - by Frank Neumann:
131  *
132 An XFree86 mode line consists of the following fields:
133  "800x600"     50      800  856  976 1040    600  637  643  666
134  < name >     DCF       HR  SH1  SH2  HFL     VR  SV1  SV2  VFL
135
136 The fields in the fb_var_screeninfo structure are:
137         unsigned long pixclock;         * pixel clock in ps (pico seconds) *
138         unsigned long left_margin;      * time from sync to picture    *
139         unsigned long right_margin;     * time from picture to sync    *
140         unsigned long upper_margin;     * time from sync to picture    *
141         unsigned long lower_margin;
142         unsigned long hsync_len;        * length of horizontal sync    *
143         unsigned long vsync_len;        * length of vertical sync      *
144
145 1) Pixelclock:
146    xfree: in MHz
147    fb: In Picoseconds (ps)
148
149    pixclock = 1000000 / DCF
150
151 2) horizontal timings:
152    left_margin = HFL - SH2
153    right_margin = SH1 - HR
154    hsync_len = SH2 - SH1
155
156 3) vertical timings:
157    upper_margin = VFL - SV2
158    lower_margin = SV1 - VR
159    vsync_len = SV2 - SV1
160
161 Good examples for VESA timings can be found in the XFree86 source tree,
162 under "programs/Xserver/hw/xfree86/doc/modeDB.txt".
163 */
164
165 /*
166  *    Predefined Video Modes
167  */
168
169 static struct {
170     const char *name;
171     struct fb_var_screeninfo var;
172 } retz3fb_predefined[] __initdata = {
173     /*
174      * NB: it is very important to adjust the pixel-clock to the color-depth.
175      */
176
177     {
178         "640x480", {            /* 640x480, 8 bpp */
179             640, 480, 640, 480, 0, 0, 8, 0,
180             {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
181             0, 0, -1, -1, FB_ACCEL_NONE, 39722, 48, 16, 33, 10, 96, 2,
182             FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,FB_VMODE_NONINTERLACED
183         }
184     },
185     /*
186      ModeLine "800x600" 36 800 824 896 1024 600 601 603 625
187               < name > DCF HR  SH1 SH2  HFL VR  SV1 SV2 VFL
188      */
189     {
190         "800x600", {            /* 800x600, 8 bpp */
191             800, 600, 800, 600, 0, 0, 8, 0,
192             {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
193             0, 0, -1, -1, FB_ACCELF_TEXT, 27778, 64, 24, 22, 1, 120, 2,
194             FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
195         }
196     },
197     {
198         "800x600-60", {         /* 800x600, 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, 25000, 88, 40, 23, 1, 128, 4,
202             FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
203         }
204     },
205     {
206         "800x600-70", {         /* 800x600, 8 bpp */
207             800, 600, 800, 600, 0, 0, 8, 0,
208             {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
209             0, 0, -1, -1, FB_ACCELF_TEXT, 22272, 40, 24, 15, 9, 144, 12,
210             FB_SYNC_COMP_HIGH_ACT, FB_VMODE_NONINTERLACED
211         }
212     },
213     /*
214       ModeLine "1024x768i" 45 1024 1064 1224 1264 768 777 785 817 interlace
215                < name >   DCF HR  SH1  SH2  HFL  VR  SV1 SV2 VFL
216      */
217     {
218         "1024x768i", {          /* 1024x768, 8 bpp, interlaced */
219             1024, 768, 1024, 768, 0, 0, 8, 0,
220             {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
221             0, 0, -1, -1, FB_ACCELF_TEXT, 22222, 40, 40, 32, 9, 160, 8,
222             FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_INTERLACED
223         }
224     },
225     {
226         "1024x768", {
227             1024, 768, 1024, 768, 0, 0, 8, 0, 
228             {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, 
229             0, 0, -1, -1, FB_ACCEL_NONE, 12500, 92, 112, 31, 2, 204, 4,
230             FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
231          }
232     },
233     {
234         "640x480-16", {         /* 640x480, 16 bpp */
235             640, 480, 640, 480, 0, 0, 16, 0,
236             {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
237             0, 0, -1, -1, 0, 38461/2, 28, 32, 12, 10, 96, 2,
238             FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,FB_VMODE_NONINTERLACED
239         }
240     },
241     {
242         "640x480-24", {         /* 640x480, 24 bpp */
243             640, 480, 640, 480, 0, 0, 24, 0,
244             {8, 8, 8}, {8, 8, 8}, {8, 8, 8}, {0, 0, 0},
245             0, 0, -1, -1, 0, 38461/3, 28, 32, 12, 10, 96, 2,
246             FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,FB_VMODE_NONINTERLACED
247         }
248     },
249 };
250
251
252 #define NUM_TOTAL_MODES    ARRAY_SIZE(retz3fb_predefined)
253
254 static struct fb_var_screeninfo retz3fb_default;
255
256 static int z3fb_inverse = 0;
257 static int z3fb_mode __initdata = 0;
258
259
260 /*
261  *    Interface used by the world
262  */
263
264 int retz3fb_setup(char *options);
265
266 static int retz3fb_get_fix(struct fb_fix_screeninfo *fix, int con,
267                            struct fb_info *info);
268 static int retz3fb_get_var(struct fb_var_screeninfo *var, int con,
269                            struct fb_info *info);
270 static int retz3fb_set_var(struct fb_var_screeninfo *var, int con,
271                            struct fb_info *info);
272 static int retz3fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
273                             struct fb_info *info);
274 static int retz3fb_setcolreg(unsigned int regno, unsigned int red,
275                              unsigned int green, unsigned int blue,
276                              unsigned int transp, struct fb_info *info);
277 static int retz3fb_blank(int blank, struct fb_info *info);
278
279
280 /*
281  *    Interface to the low level console driver
282  */
283
284 int retz3fb_init(void);
285 static int z3fb_switch(int con, struct fb_info *info);
286 static int z3fb_updatevar(int con, struct fb_info *info);
287
288
289 /*
290  *    Text console acceleration
291  */
292
293 #ifdef FBCON_HAS_CFB8
294 static struct display_switch fbcon_retz3_8;
295 #endif
296
297
298 /*
299  *    Accelerated Functions used by the low level console driver
300  */
301
302 static void retz3_bitblt(struct display *p,
303                          unsigned short curx, unsigned short cury, unsigned
304                          short destx, unsigned short desty, unsigned short
305                          width, unsigned short height, unsigned short cmd,
306                          unsigned short mask);
307
308 /*
309  *   Hardware Specific Routines
310  */
311
312 static int retz3_encode_fix(struct fb_info *info,
313                             struct fb_fix_screeninfo *fix,
314                             struct retz3fb_par *par);
315 static int retz3_decode_var(struct fb_var_screeninfo *var,
316                             struct retz3fb_par *par);
317 static int retz3_encode_var(struct fb_var_screeninfo *var,
318                             struct retz3fb_par *par);
319 static int retz3_getcolreg(unsigned int regno, unsigned int *red,
320                            unsigned int *green, unsigned int *blue,
321                            unsigned int *transp, struct fb_info *info);
322
323 /*
324  *    Internal routines
325  */
326
327 static void retz3fb_get_par(struct fb_info *info, struct retz3fb_par *par);
328 static void retz3fb_set_par(struct fb_info *info, struct retz3fb_par *par);
329 static int do_fb_set_var(struct fb_info *info,
330                          struct fb_var_screeninfo *var, int isactive);
331 static void retz3fb_set_disp(int con, struct fb_info *info);
332 static int get_video_mode(const char *name);
333
334
335 /* -------------------- Hardware specific routines ------------------------- */
336
337 static unsigned short find_fq(unsigned int freq)
338 {
339         unsigned long f;
340         long tmp;
341         long prev = 0x7fffffff;
342         long n2, n1 = 3;
343         unsigned long m;
344         unsigned short res = 0;
345
346         if (freq <= 31250000)
347                 n2 = 3;
348         else if (freq <= 62500000)
349                 n2 = 2;
350         else if (freq <= 125000000)
351                 n2 = 1;
352         else if (freq <= 250000000)
353                 n2 = 0;
354         else
355                 return 0;
356
357
358         do {
359                 f = freq >> (10 - n2);
360
361                 m = (f * n1) / (14318180/1024);
362
363                 if (m > 129)
364                         break;
365
366                 tmp =  (((m * 14318180) >> n2) / n1) - freq;
367                 if (tmp < 0)
368                         tmp = -tmp;
369
370                 if (tmp < prev) {
371                         prev = tmp;
372                         res = (((n2 << 5) | (n1-2)) << 8) | (m-2);
373                 }
374
375         } while ( (++n1) <= 21);
376
377         return res;
378 }
379
380
381 static int retz3_set_video(struct fb_info *info,
382                            struct fb_var_screeninfo *var,
383                            struct retz3fb_par *par)
384 {
385         volatile unsigned char *regs = retz3info(info)->regs;
386         unsigned int freq;
387
388         int xres, hfront, hsync, hback;
389         int yres, vfront, vsync, vback;
390         unsigned char tmp;
391         unsigned short best_freq;
392         struct display_data data;
393
394         short clocksel = 0; /* Apparantly this is always zero */
395
396         int bpp = var->bits_per_pixel;
397
398         /*
399          * XXX
400          */
401         if (bpp == 24)
402                 return 0;
403
404         if ((bpp != 8) && (bpp != 16) && (bpp != 24))
405                 return -EFAULT;
406
407         par->xoffset = 0;
408         par->yoffset = 0;
409
410         xres   = var->xres * bpp / 4;
411         hfront = var->right_margin * bpp / 4;
412         hsync  = var->hsync_len * bpp / 4;
413         hback  = var->left_margin * bpp / 4;
414
415         if (var->vmode & FB_VMODE_DOUBLE)
416         {
417                 yres = var->yres * 2;
418                 vfront = var->lower_margin * 2;
419                 vsync  = var->vsync_len * 2;
420                 vback  = var->upper_margin * 2;
421         }
422         else if (var->vmode & FB_VMODE_INTERLACED)
423         {
424                 yres   = (var->yres + 1) / 2;
425                 vfront = (var->lower_margin + 1) / 2;
426                 vsync  = (var->vsync_len + 1) / 2;
427                 vback  = (var->upper_margin + 1) / 2;
428         }
429         else
430         {
431                 yres   = var->yres; /* -1 ? */
432                 vfront = var->lower_margin;
433                 vsync  = var->vsync_len;
434                 vback  = var->upper_margin;
435         }
436
437         data.h_total    = (hback / 8) + (xres / 8)
438                         + (hfront / 8) + (hsync / 8) - 1 /* + 1 */;
439         data.h_dispend  = ((xres + bpp - 1)/ 8) - 1;
440         data.h_bstart   = xres / 8 - 1 /* + 1 */;
441
442         data.h_bstop    = data.h_total+1 + 2 + 1;
443         data.h_sstart   = (xres / 8) + (hfront / 8) + 1;
444         data.h_sstop    = (xres / 8) + (hfront / 8) + (hsync / 8) + 1;
445
446         data.v_total    = yres + vfront + vsync + vback - 1;
447
448         data.v_dispend  = yres - 1;
449         data.v_bstart   = yres - 1;
450
451         data.v_bstop    = data.v_total;
452         data.v_sstart   = yres + vfront - 1 - 2;
453         data.v_sstop    = yres + vfront + vsync - 1;
454
455 #if 0 /* testing */
456
457         printk("HBS: %i\n", data.h_bstart);
458         printk("HSS: %i\n", data.h_sstart);
459         printk("HSE: %i\n", data.h_sstop);
460         printk("HBE: %i\n", data.h_bstop);
461         printk("HT: %i\n", data.h_total);
462
463         printk("hsync: %i\n", hsync);
464         printk("hfront: %i\n", hfront);
465         printk("hback: %i\n", hback);
466
467         printk("VBS: %i\n", data.v_bstart);
468         printk("VSS: %i\n", data.v_sstart);
469         printk("VSE: %i\n", data.v_sstop);
470         printk("VBE: %i\n", data.v_bstop);
471         printk("VT: %i\n", data.v_total);
472
473         printk("vsync: %i\n", vsync);
474         printk("vfront: %i\n", vfront);
475         printk("vback: %i\n", vback);
476 #endif
477
478         if (data.v_total >= 1024)
479                 printk(KERN_ERR "MAYDAY: v_total >= 1024; bailing out!\n");
480
481         reg_w(regs, GREG_MISC_OUTPUT_W, 0xe3 | ((clocksel & 3) * 0x04));
482         reg_w(regs, GREG_FEATURE_CONTROL_W, 0x00);
483
484         seq_w(regs, SEQ_RESET, 0x00);
485         seq_w(regs, SEQ_RESET, 0x03);   /* reset sequencer logic */
486
487         /*
488          * CLOCKING_MODE bits:
489          * 2: This one is only set for certain text-modes, wonder if
490          *    it may be for EGA-lines? (it was referred to as CLKDIV2)
491          * (The CL drivers sets it to 0x21 with the comment:
492          *  FullBandwidth (video off) and 8/9 dot clock)
493          */
494         seq_w(regs, SEQ_CLOCKING_MODE, 0x01 | 0x00 /* 0x08 */);
495
496         seq_w(regs, SEQ_MAP_MASK, 0x0f);        /* enable writing to plane 0-3 */
497         seq_w(regs, SEQ_CHAR_MAP_SELECT, 0x00); /* doesn't matter in gfx-mode */
498         seq_w(regs, SEQ_MEMORY_MODE, 0x06); /* CL driver says 0x0e for 256 col mode*/
499         seq_w(regs, SEQ_RESET, 0x01);
500         seq_w(regs, SEQ_RESET, 0x03);
501
502         seq_w(regs, SEQ_EXTENDED_ENABLE, 0x05);
503
504         seq_w(regs, SEQ_CURSOR_CONTROL, 0x00);  /* disable cursor */
505         seq_w(regs, SEQ_PRIM_HOST_OFF_HI, 0x00);
506         seq_w(regs, SEQ_PRIM_HOST_OFF_HI, 0x00);
507         seq_w(regs, SEQ_LINEAR_0, 0x4a);
508         seq_w(regs, SEQ_LINEAR_1, 0x00);
509
510         seq_w(regs, SEQ_SEC_HOST_OFF_HI, 0x00);
511         seq_w(regs, SEQ_SEC_HOST_OFF_LO, 0x00);
512         seq_w(regs, SEQ_EXTENDED_MEM_ENA, 0x3 | 0x4 | 0x10 | 0x40);
513
514         /*
515          * The lower 4 bits (0-3) are used to set the font-width for
516          * text-mode - DON'T try to set this for gfx-mode.
517          */
518         seq_w(regs, SEQ_EXT_CLOCK_MODE, 0x10);
519         seq_w(regs, SEQ_EXT_VIDEO_ADDR, 0x03);
520
521         /*
522          * Extended Pixel Control:
523          * bit 0:   text-mode=0, gfx-mode=1 (Graphics Byte ?)
524          * bit 1: (Packed/Nibble Pixel Format ?)
525          * bit 4-5: depth, 0=1-8bpp, 1=9-16bpp, 2=17-24bpp
526          */
527         seq_w(regs, SEQ_EXT_PIXEL_CNTL, 0x01 | (((bpp / 8) - 1) << 4));
528
529         seq_w(regs, SEQ_BUS_WIDTH_FEEDB, 0x04);
530         seq_w(regs, SEQ_COLOR_EXP_WFG, 0x01);
531         seq_w(regs, SEQ_COLOR_EXP_WBG, 0x00);
532         seq_w(regs, SEQ_EXT_RW_CONTROL, 0x00);
533         seq_w(regs, SEQ_MISC_FEATURE_SEL, (0x51 | (clocksel & 8)));
534         seq_w(regs, SEQ_COLOR_KEY_CNTL, 0x40);
535         seq_w(regs, SEQ_COLOR_KEY_MATCH0, 0x00);
536         seq_w(regs, SEQ_COLOR_KEY_MATCH1, 0x00);
537         seq_w(regs, SEQ_COLOR_KEY_MATCH2, 0x00);
538         seq_w(regs, SEQ_CRC_CONTROL, 0x00);
539         seq_w(regs, SEQ_PERF_SELECT, 0x10);
540         seq_w(regs, SEQ_ACM_APERTURE_1, 0x00);
541         seq_w(regs, SEQ_ACM_APERTURE_2, 0x30);
542         seq_w(regs, SEQ_ACM_APERTURE_3, 0x00);
543         seq_w(regs, SEQ_MEMORY_MAP_CNTL, 0x03);
544
545
546         /* unlock register CRT0..CRT7 */
547         crt_w(regs, CRT_END_VER_RETR, (data.v_sstop & 0x0f) | 0x20);
548
549         /* Zuerst zu schreibende Werte nur per printk ausgeben */
550         DEBUG printk("CRT_HOR_TOTAL: %ld\n", data.h_total);
551         crt_w(regs, CRT_HOR_TOTAL, data.h_total & 0xff);
552
553         DEBUG printk("CRT_HOR_DISP_ENA_END: %ld\n", data.h_dispend);
554         crt_w(regs, CRT_HOR_DISP_ENA_END, (data.h_dispend) & 0xff);
555
556         DEBUG printk("CRT_START_HOR_BLANK: %ld\n", data.h_bstart);
557         crt_w(regs, CRT_START_HOR_BLANK, data.h_bstart & 0xff);
558
559         DEBUG printk("CRT_END_HOR_BLANK: 128+%ld\n", data.h_bstop % 32);
560         crt_w(regs, CRT_END_HOR_BLANK,  0x80 | (data.h_bstop & 0x1f));
561
562         DEBUG printk("CRT_START_HOR_RETR: %ld\n", data.h_sstart);
563         crt_w(regs, CRT_START_HOR_RETR, data.h_sstart & 0xff);
564
565         tmp = (data.h_sstop & 0x1f);
566         if (data.h_bstop & 0x20)
567                 tmp |= 0x80;
568         DEBUG printk("CRT_END_HOR_RETR: %d\n", tmp);
569         crt_w(regs, CRT_END_HOR_RETR, tmp);
570
571         DEBUG printk("CRT_VER_TOTAL: %ld\n", data.v_total & 0xff);
572         crt_w(regs, CRT_VER_TOTAL, (data.v_total & 0xff));
573
574         tmp = 0x10;  /* LineCompare bit #9 */
575         if (data.v_total & 256)
576                 tmp |= 0x01;
577         if (data.v_dispend & 256)
578                 tmp |= 0x02;
579         if (data.v_sstart & 256)
580                 tmp |= 0x04;
581         if (data.v_bstart & 256)
582                 tmp |= 0x08;
583         if (data.v_total & 512)
584                 tmp |= 0x20;
585         if (data.v_dispend & 512)
586                 tmp |= 0x40;
587         if (data.v_sstart & 512)
588                 tmp |= 0x80;
589         DEBUG printk("CRT_OVERFLOW: %d\n", tmp);
590         crt_w(regs, CRT_OVERFLOW, tmp);
591
592         crt_w(regs, CRT_PRESET_ROW_SCAN, 0x00); /* not CL !!! */
593
594         tmp = 0x40; /* LineCompare bit #8 */
595         if (data.v_bstart & 512)
596                 tmp |= 0x20;
597         if (var->vmode & FB_VMODE_DOUBLE)
598                 tmp |= 0x80;
599         DEBUG printk("CRT_MAX_SCAN_LINE: %d\n", tmp);
600         crt_w(regs, CRT_MAX_SCAN_LINE, tmp);
601
602         crt_w(regs, CRT_CURSOR_START, 0x00);
603         crt_w(regs, CRT_CURSOR_END, 8 & 0x1f); /* font height */
604
605         crt_w(regs, CRT_START_ADDR_HIGH, 0x00);
606         crt_w(regs, CRT_START_ADDR_LOW, 0x00);
607
608         crt_w(regs, CRT_CURSOR_LOC_HIGH, 0x00);
609         crt_w(regs, CRT_CURSOR_LOC_LOW, 0x00);
610
611         DEBUG printk("CRT_START_VER_RETR: %ld\n", data.v_sstart & 0xff);
612         crt_w(regs, CRT_START_VER_RETR, (data.v_sstart & 0xff));
613
614 #if 1
615         /* 5 refresh cycles per scanline */
616         DEBUG printk("CRT_END_VER_RETR: 64+32+%ld\n", data.v_sstop % 16);
617         crt_w(regs, CRT_END_VER_RETR, ((data.v_sstop & 0x0f) | 0x40 | 0x20));
618 #else
619         DEBUG printk("CRT_END_VER_RETR: 128+32+%ld\n", data.v_sstop % 16);
620         crt_w(regs, CRT_END_VER_RETR, ((data.v_sstop & 0x0f) | 128 | 32));
621 #endif
622         DEBUG printk("CRT_VER_DISP_ENA_END: %ld\n", data.v_dispend & 0xff);
623         crt_w(regs, CRT_VER_DISP_ENA_END, (data.v_dispend & 0xff));
624
625         DEBUG printk("CRT_START_VER_BLANK: %ld\n", data.v_bstart & 0xff);
626         crt_w(regs, CRT_START_VER_BLANK, (data.v_bstart & 0xff));
627
628         DEBUG printk("CRT_END_VER_BLANK: %ld\n", data.v_bstop & 0xff);
629         crt_w(regs, CRT_END_VER_BLANK, (data.v_bstop & 0xff));
630
631         DEBUG printk("CRT_MODE_CONTROL: 0xe3\n");
632         crt_w(regs, CRT_MODE_CONTROL, 0xe3);
633
634         DEBUG printk("CRT_LINE_COMPARE: 0xff\n");
635         crt_w(regs, CRT_LINE_COMPARE, 0xff);
636
637         tmp = (var->xres_virtual / 8) * (bpp / 8);
638         crt_w(regs, CRT_OFFSET, tmp);
639
640         crt_w(regs, CRT_UNDERLINE_LOC, 0x07); /* probably font-height - 1 */
641
642         tmp = 0x20;                     /* Enable extended end bits */
643         if (data.h_total & 0x100)
644                 tmp |= 0x01;
645         if ((data.h_dispend) & 0x100)
646                 tmp |= 0x02;
647         if (data.h_bstart & 0x100)
648                 tmp |= 0x04;
649         if (data.h_sstart & 0x100)
650                 tmp |= 0x08;
651         if (var->vmode & FB_VMODE_INTERLACED)
652                 tmp |= 0x10;
653         DEBUG printk("CRT_EXT_HOR_TIMING1: %d\n", tmp);
654         crt_w(regs, CRT_EXT_HOR_TIMING1, tmp);
655
656         tmp = 0x00;
657         if (((var->xres_virtual / 8) * (bpp / 8)) & 0x100)
658                 tmp |= 0x10;
659         crt_w(regs, CRT_EXT_START_ADDR, tmp);
660
661         tmp = 0x00;
662         if (data.h_total & 0x200)
663                 tmp |= 0x01;
664         if ((data.h_dispend) & 0x200)
665                 tmp |= 0x02;
666         if (data.h_bstart & 0x200)
667                 tmp |= 0x04;
668         if (data.h_sstart & 0x200)
669                 tmp |= 0x08;
670         tmp |= ((data.h_bstop & 0xc0) >> 2);
671         tmp |= ((data.h_sstop & 0x60) << 1);
672         crt_w(regs, CRT_EXT_HOR_TIMING2, tmp);
673         DEBUG printk("CRT_EXT_HOR_TIMING2: %d\n", tmp);
674
675         tmp = 0x10;                     /* Line compare bit 10 */
676         if (data.v_total & 0x400)
677                 tmp |= 0x01;
678         if ((data.v_dispend) & 0x400)
679                 tmp |= 0x02;
680         if (data.v_bstart & 0x400)
681                 tmp |= 0x04;
682         if (data.v_sstart & 0x400)
683                 tmp |= 0x08;
684         tmp |= ((data.v_bstop & 0x300) >> 3);
685         if (data.v_sstop & 0x10)
686                 tmp |= 0x80;
687         crt_w(regs, CRT_EXT_VER_TIMING, tmp);
688         DEBUG printk("CRT_EXT_VER_TIMING: %d\n", tmp);
689
690         crt_w(regs, CRT_MONITOR_POWER, 0x00);
691
692         /*
693          * Convert from ps to Hz.
694          */
695         freq = 2000000000 / var->pixclock;
696         freq = freq * 500;
697
698         best_freq = find_fq(freq);
699         pll_w(regs, 0x02, best_freq);
700         best_freq = find_fq(61000000);
701         pll_w(regs, 0x0a, best_freq);
702         pll_w(regs, 0x0e, 0x22);
703
704         gfx_w(regs, GFX_SET_RESET, 0x00);
705         gfx_w(regs, GFX_ENABLE_SET_RESET, 0x00);
706         gfx_w(regs, GFX_COLOR_COMPARE, 0x00);
707         gfx_w(regs, GFX_DATA_ROTATE, 0x00);
708         gfx_w(regs, GFX_READ_MAP_SELECT, 0x00);
709         gfx_w(regs, GFX_GRAPHICS_MODE, 0x00);
710         gfx_w(regs, GFX_MISC, 0x05);
711         gfx_w(regs, GFX_COLOR_XCARE, 0x0f);
712         gfx_w(regs, GFX_BITMASK, 0xff);
713
714         reg_r(regs, ACT_ADDRESS_RESET);
715         attr_w(regs, ACT_PALETTE0 , 0x00);
716         attr_w(regs, ACT_PALETTE1 , 0x01);
717         attr_w(regs, ACT_PALETTE2 , 0x02);
718         attr_w(regs, ACT_PALETTE3 , 0x03);
719         attr_w(regs, ACT_PALETTE4 , 0x04);
720         attr_w(regs, ACT_PALETTE5 , 0x05);
721         attr_w(regs, ACT_PALETTE6 , 0x06);
722         attr_w(regs, ACT_PALETTE7 , 0x07);
723         attr_w(regs, ACT_PALETTE8 , 0x08);
724         attr_w(regs, ACT_PALETTE9 , 0x09);
725         attr_w(regs, ACT_PALETTE10, 0x0a);
726         attr_w(regs, ACT_PALETTE11, 0x0b);
727         attr_w(regs, ACT_PALETTE12, 0x0c);
728         attr_w(regs, ACT_PALETTE13, 0x0d);
729         attr_w(regs, ACT_PALETTE14, 0x0e);
730         attr_w(regs, ACT_PALETTE15, 0x0f);
731         reg_r(regs, ACT_ADDRESS_RESET);
732
733         attr_w(regs, ACT_ATTR_MODE_CNTL, 0x09); /* 0x01 for CL */
734
735         attr_w(regs, ACT_OVERSCAN_COLOR, 0x00);
736         attr_w(regs, ACT_COLOR_PLANE_ENA, 0x0f);
737         attr_w(regs, ACT_HOR_PEL_PANNING, 0x00);
738         attr_w(regs, ACT_COLOR_SELECT, 0x00);
739
740         reg_r(regs, ACT_ADDRESS_RESET);
741         reg_w(regs, ACT_DATA, 0x20);
742
743         reg_w(regs, VDAC_MASK, 0xff);
744
745         /*
746          * Extended palette addressing ???
747          */
748         switch (bpp){
749         case 8:
750                 reg_w(regs, 0x83c6, 0x00);
751                 break;
752         case 16:
753                 reg_w(regs, 0x83c6, 0x60);
754                 break;
755         case 24:
756                 reg_w(regs, 0x83c6, 0xe0);
757                 break;
758         default:
759                 printk(KERN_INFO "Illegal color-depth: %i\n", bpp);
760         }
761
762         reg_w(regs, VDAC_ADDRESS, 0x00);
763
764         seq_w(regs, SEQ_MAP_MASK, 0x0f );
765
766         return 0;
767 }
768
769
770 /*
771  *    This function should fill in the `fix' structure based on the
772  *    values in the `par' structure.
773  */
774
775 static int retz3_encode_fix(struct fb_info *info,
776                             struct fb_fix_screeninfo *fix,
777                             struct retz3fb_par *par)
778 {
779         struct retz3_fb_info *zinfo = retz3info(info);
780
781         memset(fix, 0, sizeof(struct fb_fix_screeninfo));
782         strcpy(fix->id, retz3fb_name);
783         fix->smem_start = zinfo->physfbmem;
784         fix->smem_len = zinfo->fbsize;
785         fix->mmio_start = zinfo->physregs;
786         fix->mmio_len = 0x00c00000;
787
788         fix->type = FB_TYPE_PACKED_PIXELS;
789         fix->type_aux = 0;
790         if (par->bpp == 8)
791                 fix->visual = FB_VISUAL_PSEUDOCOLOR;
792         else
793                 fix->visual = FB_VISUAL_TRUECOLOR;
794
795         fix->xpanstep = 0;
796         fix->ypanstep = 0;
797         fix->ywrapstep = 0;
798         fix->line_length = 0;
799
800         fix->accel = FB_ACCEL_NCR_77C32BLT;
801
802         return 0;
803 }
804
805
806 /*
807  *    Get the video params out of `var'. If a value doesn't fit, round
808  *    it up, if it's too big, return -EINVAL.
809  */
810
811 static int retz3_decode_var(struct fb_var_screeninfo *var,
812                             struct retz3fb_par *par)
813 {
814         par->xres = var->xres;
815         par->yres = var->yres;
816         par->xres_vir = var->xres_virtual;
817         par->yres_vir = var->yres_virtual;
818         par->bpp = var->bits_per_pixel;
819         par->pixclock = var->pixclock;
820         par->vmode = var->vmode;
821
822         par->red = var->red;
823         par->green = var->green;
824         par->blue = var->blue;
825         par->transp = var->transp;
826
827         par->left_margin = var->left_margin;
828         par->right_margin = var->right_margin;
829         par->upper_margin = var->upper_margin;
830         par->lower_margin = var->lower_margin;
831         par->hsync_len = var->hsync_len;
832         par->vsync_len = var->vsync_len;
833
834         if (var->accel_flags & FB_ACCELF_TEXT)
835             par->accel = FB_ACCELF_TEXT;
836         else
837             par->accel = 0;
838
839         return 0;
840 }
841
842
843 /*
844  *    Fill the `var' structure based on the values in `par' and maybe
845  *    other values read out of the hardware.
846  */
847
848 static int retz3_encode_var(struct fb_var_screeninfo *var,
849                             struct retz3fb_par *par)
850 {
851         memset(var, 0, sizeof(struct fb_var_screeninfo));
852         var->xres = par->xres;
853         var->yres = par->yres;
854         var->xres_virtual = par->xres_vir;
855         var->yres_virtual = par->yres_vir;
856         var->xoffset = 0;
857         var->yoffset = 0;
858
859         var->bits_per_pixel = par->bpp;
860         var->grayscale = 0;
861
862         var->red = par->red;
863         var->green = par->green;
864         var->blue = par->blue;
865         var->transp = par->transp;
866
867         var->nonstd = 0;
868         var->activate = 0;
869
870         var->height = -1;
871         var->width = -1;
872
873         var->accel_flags = (par->accel && par->bpp == 8) ? FB_ACCELF_TEXT : 0;
874
875         var->pixclock = par->pixclock;
876
877         var->sync = 0;                          /* ??? */
878         var->left_margin = par->left_margin;
879         var->right_margin = par->right_margin;
880         var->upper_margin = par->upper_margin;
881         var->lower_margin = par->lower_margin;
882         var->hsync_len = par->hsync_len;
883         var->vsync_len = par->vsync_len;
884
885         var->vmode = par->vmode;
886         return 0;
887 }
888
889
890 /*
891  *    Set a single color register. Return != 0 for invalid regno.
892  */
893
894 static int retz3fb_setcolreg(unsigned int regno, unsigned int red,
895                              unsigned int green, unsigned int blue,
896                              unsigned int transp, struct fb_info *info)
897 {
898         struct retz3_fb_info *zinfo = retz3info(info);
899         volatile unsigned char *regs = zinfo->regs;
900
901         /* We'll get to this */
902
903         if (regno > 255)
904                 return 1;
905
906         red >>= 10;
907         green >>= 10;
908         blue >>= 10;
909
910         zinfo->color_table[regno][0] = red;
911         zinfo->color_table[regno][1] = green;
912         zinfo->color_table[regno][2] = blue;
913
914         reg_w(regs, VDAC_ADDRESS_W, regno);
915         reg_w(regs, VDAC_DATA, red);
916         reg_w(regs, VDAC_DATA, green);
917         reg_w(regs, VDAC_DATA, blue);
918
919         return 0;
920 }
921
922
923 /*
924  *    Read a single color register and split it into
925  *    colors/transparent. Return != 0 for invalid regno.
926  */
927
928 static int retz3_getcolreg(unsigned int regno, unsigned int *red,
929                            unsigned int *green, unsigned int *blue,
930                            unsigned int *transp, struct fb_info *info)
931 {
932         struct retz3_fb_info *zinfo = retz3info(info);
933         int t;
934
935         if (regno > 255)
936                 return 1;
937         t       = zinfo->color_table[regno][0];
938         *red    = (t<<10) | (t<<4) | (t>>2);
939         t       = zinfo->color_table[regno][1];
940         *green  = (t<<10) | (t<<4) | (t>>2);
941         t       = zinfo->color_table[regno][2];
942         *blue   = (t<<10) | (t<<4) | (t>>2);
943         *transp = 0;
944         return 0;
945 }
946
947
948 static inline void retz3_busy(struct display *p)
949 {
950         struct retz3_fb_info *zinfo = retz3info(p->fb_info);
951         volatile unsigned char *acm = zinfo->base + ACM_OFFSET;
952         unsigned char blt_status;
953
954         if (zinfo->blitbusy) {
955                 do{
956                         blt_status = *((acm) + (ACM_START_STATUS + 2));
957                 }while ((blt_status & 1) == 0);
958                 zinfo->blitbusy = 0;
959         }
960 }
961
962
963 static void retz3_bitblt (struct display *p,
964                           unsigned short srcx, unsigned short srcy,
965                           unsigned short destx, unsigned short desty,
966                           unsigned short width, unsigned short height,
967                           unsigned short cmd, unsigned short mask)
968 {
969         struct fb_var_screeninfo *var = &p->var;
970         struct retz3_fb_info *zinfo = retz3info(p->fb_info);
971         volatile unsigned long *acm = (unsigned long *)(zinfo->base + ACM_OFFSET);
972         unsigned long *pattern = (unsigned long *)(zinfo->fbmem + PAT_MEM_OFF);
973
974         unsigned short mod;
975         unsigned long tmp;
976         unsigned long pat, src, dst;
977
978         int i, xres_virtual = var->xres_virtual;
979         short bpp = (var->bits_per_pixel & 0xff);
980
981         if (bpp < 8)
982                 bpp = 8;
983
984         tmp = mask | (mask << 16);
985
986         retz3_busy(p);
987
988         i = 0;
989         do{
990                 *pattern++ = tmp;
991         }while(i++ < bpp/4);
992
993         tmp = cmd << 8;
994         *(acm + ACM_RASTEROP_ROTATION/4) = tmp;
995
996         mod = 0xc0c2;
997
998         pat = 8 * PAT_MEM_OFF;
999         dst = bpp * (destx + desty * xres_virtual);
1000
1001         /*
1002          * Source is not set for clear.
1003          */
1004         if ((cmd != Z3BLTclear) && (cmd != Z3BLTset)) {
1005                 src = bpp * (srcx + srcy * xres_virtual);
1006
1007                 if (destx > srcx) {
1008                         mod &= ~0x8000;
1009                         src += bpp * (width - 1);
1010                         dst += bpp * (width - 1);
1011                         pat += bpp * 2;
1012                 }
1013                 if (desty > srcy) {
1014                         mod &= ~0x4000;
1015                         src += bpp * (height - 1) * xres_virtual;
1016                         dst += bpp * (height - 1) * xres_virtual;
1017                         pat += bpp * 4;
1018                 }
1019
1020                 *(acm + ACM_SOURCE/4) = cpu_to_le32(src);
1021         }
1022
1023         *(acm + ACM_PATTERN/4) = cpu_to_le32(pat);
1024
1025         *(acm + ACM_DESTINATION/4) = cpu_to_le32(dst);
1026
1027         tmp = mod << 16;
1028         *(acm + ACM_CONTROL/4) = tmp;
1029
1030         tmp  = width | (height << 16);
1031
1032         *(acm + ACM_BITMAP_DIMENSION/4) = cpu_to_le32(tmp);
1033
1034         *(((volatile unsigned char *)acm) + ACM_START_STATUS) = 0x00;
1035         *(((volatile unsigned char *)acm) + ACM_START_STATUS) = 0x01;
1036         zinfo->blitbusy = 1;
1037 }
1038
1039 #if 0
1040 /*
1041  * Move cursor to x, y
1042  */
1043 static void retz3_MoveCursor (unsigned short x, unsigned short y)
1044 {
1045         /* Guess we gotta deal with the cursor at some point */
1046 }
1047 #endif
1048
1049
1050 /*
1051  *    Fill the hardware's `par' structure.
1052  */
1053
1054 static void retz3fb_get_par(struct fb_info *info, struct retz3fb_par *par)
1055 {
1056         struct retz3_fb_info *zinfo = retz3info(info);
1057
1058         if (zinfo->current_par_valid)
1059                 *par = zinfo->current_par;
1060         else
1061                 retz3_decode_var(&retz3fb_default, par);
1062 }
1063
1064
1065 static void retz3fb_set_par(struct fb_info *info, struct retz3fb_par *par)
1066 {
1067         struct retz3_fb_info *zinfo = retz3info(info);
1068
1069         zinfo->current_par = *par;
1070         zinfo->current_par_valid = 1;
1071 }
1072
1073
1074 static int do_fb_set_var(struct fb_info *info,
1075                          struct fb_var_screeninfo *var, int isactive)
1076 {
1077         int err, activate;
1078         struct retz3fb_par par;
1079         struct retz3_fb_info *zinfo = retz3info(info);
1080
1081         if ((err = retz3_decode_var(var, &par)))
1082                 return err;
1083         activate = var->activate;
1084
1085         /* XXX ... what to do about isactive ? */
1086
1087         if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW && isactive)
1088                 retz3fb_set_par(info, &par);
1089         retz3_encode_var(var, &par);
1090         var->activate = activate;
1091
1092         retz3_set_video(info, var, &zinfo->current_par);
1093
1094         return 0;
1095 }
1096
1097 /*
1098  *    Get the Fixed Part of the Display
1099  */
1100
1101 static int retz3fb_get_fix(struct fb_fix_screeninfo *fix, int con,
1102                            struct fb_info *info)
1103 {
1104         struct retz3fb_par par;
1105         int error = 0;
1106
1107         if (con == -1)
1108                 retz3fb_get_par(info, &par);
1109         else
1110                 error = retz3_decode_var(&fb_display[con].var, &par);
1111         return(error ? error : retz3_encode_fix(info, fix, &par));
1112 }
1113
1114
1115 /*
1116  *    Get the User Defined Part of the Display
1117  */
1118
1119 static int retz3fb_get_var(struct fb_var_screeninfo *var, int con,
1120                            struct fb_info *info)
1121 {
1122         struct retz3fb_par par;
1123         int error = 0;
1124
1125         if (con == -1) {
1126                 retz3fb_get_par(info, &par);
1127                 error = retz3_encode_var(var, &par);
1128         } else
1129                 *var = fb_display[con].var;
1130         return error;
1131 }
1132
1133
1134 static void retz3fb_set_disp(int con, struct fb_info *info)
1135 {
1136         struct fb_fix_screeninfo fix;
1137         struct display *display;
1138         struct retz3_fb_info *zinfo = retz3info(info);
1139
1140         if (con >= 0)
1141                 display = &fb_display[con];
1142         else
1143                 display = &zinfo->disp; /* used during initialization */
1144
1145         retz3fb_get_fix(&fix, con, info);
1146
1147         if (con == -1)
1148                 con = 0;
1149
1150         display->visual = fix.visual;
1151         display->type = fix.type;
1152         display->type_aux = fix.type_aux;
1153         display->ypanstep = fix.ypanstep;
1154         display->ywrapstep = fix.ywrapstep;
1155         display->can_soft_blank = 1;
1156         display->inverse = z3fb_inverse;
1157
1158         /*
1159          * This seems to be about 20% faster.
1160          */
1161         display->scrollmode = SCROLL_YREDRAW;
1162
1163         switch (display->var.bits_per_pixel) {
1164 #ifdef FBCON_HAS_CFB8
1165         case 8:
1166                 if (display->var.accel_flags & FB_ACCELF_TEXT) {
1167                     display->dispsw = &fbcon_retz3_8;
1168                     retz3_set_video(info, &display->var, &zinfo->current_par);
1169                 } else
1170                     display->dispsw = &fbcon_cfb8;
1171                 break;
1172 #endif
1173 #ifdef FBCON_HAS_CFB16
1174         case 16:
1175                 display->dispsw = &fbcon_cfb16;
1176                 break;
1177 #endif
1178         default:
1179                 display->dispsw = &fbcon_dummy;
1180                 break;
1181         }
1182 }
1183
1184
1185 /*
1186  *    Set the User Defined Part of the Display
1187  */
1188
1189 static int retz3fb_set_var(struct fb_var_screeninfo *var, int con,
1190                            struct fb_info *info)
1191 {
1192         int err, oldxres, oldyres, oldvxres, oldvyres, oldbpp, oldaccel;
1193         struct display *display;
1194         struct retz3_fb_info *zinfo = retz3info(info);
1195
1196         if (con >= 0)
1197                 display = &fb_display[con];
1198         else
1199                 display = &zinfo->disp; /* used during initialization */
1200
1201         if ((err = do_fb_set_var(info, var, con == info->currcon)))
1202                 return err;
1203         if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
1204                 oldxres = display->var.xres;
1205                 oldyres = display->var.yres;
1206                 oldvxres = display->var.xres_virtual;
1207                 oldvyres = display->var.yres_virtual;
1208                 oldbpp = display->var.bits_per_pixel;
1209                 oldaccel = display->var.accel_flags;
1210                 display->var = *var;
1211
1212                 if (oldxres != var->xres || oldyres != var->yres ||
1213                     oldvxres != var->xres_virtual ||
1214                     oldvyres != var->yres_virtual ||
1215                     oldbpp != var->bits_per_pixel ||
1216                     oldaccel != var->accel_flags) {
1217
1218                         struct fb_fix_screeninfo fix;
1219                         retz3fb_get_fix(&fix, con, info);
1220
1221                         display->visual = fix.visual;
1222                         display->type = fix.type;
1223                         display->type_aux = fix.type_aux;
1224                         display->ypanstep = fix.ypanstep;
1225                         display->ywrapstep = fix.ywrapstep;
1226                         display->line_length = fix.line_length;
1227                         display->can_soft_blank = 1;
1228                         display->inverse = z3fb_inverse;
1229                         switch (display->var.bits_per_pixel) {
1230 #ifdef FBCON_HAS_CFB8
1231                         case 8:
1232                                 if (var->accel_flags & FB_ACCELF_TEXT) {
1233                                         display->dispsw = &fbcon_retz3_8;
1234                                 } else
1235                                         display->dispsw = &fbcon_cfb8;
1236                                 break;
1237 #endif
1238 #ifdef FBCON_HAS_CFB16
1239                         case 16:
1240                                 display->dispsw = &fbcon_cfb16;
1241                                 break;
1242 #endif
1243                         default:
1244                                 display->dispsw = &fbcon_dummy;
1245                                 break;
1246                         }
1247                         /*
1248                          * We still need to find a way to tell the X
1249                          * server that the video mem has been fiddled with
1250                          * so it redraws the entire screen when switching
1251                          * between X and a text console.
1252                          */
1253                         retz3_set_video(info, var, &zinfo->current_par);
1254
1255                         if (info->changevar)
1256                                 (*info->changevar)(con);
1257                 }
1258
1259                 if (oldbpp != var->bits_per_pixel) {
1260                         if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
1261                                 return err;
1262                         do_install_cmap(con, info);
1263                 }
1264         }
1265         return 0;
1266 }
1267
1268
1269 /*
1270  *    Get the Colormap
1271  */
1272
1273 static int retz3fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
1274                             struct fb_info *info)
1275 {
1276         if (con == info->currcon) /* current console? */
1277                 return(fb_get_cmap(cmap, kspc, retz3_getcolreg, info));
1278         else if (fb_display[con].cmap.len) /* non default colormap? */
1279                 fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
1280         else
1281                 fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
1282                              cmap, kspc ? 0 : 2);
1283         return 0;
1284 }
1285
1286 /*
1287  *    Blank the display.
1288  */
1289
1290 static int retz3fb_blank(int blank, struct fb_info *info)
1291 {
1292         struct retz3_fb_info *zinfo = retz3info(info);
1293         volatile unsigned char *regs = retz3info(info)->regs;
1294         short i;
1295
1296         if (blank)
1297                 for (i = 0; i < 256; i++){
1298                         reg_w(regs, VDAC_ADDRESS_W, i);
1299                         reg_w(regs, VDAC_DATA, 0);
1300                         reg_w(regs, VDAC_DATA, 0);
1301                         reg_w(regs, VDAC_DATA, 0);
1302                 }
1303         else
1304                 for (i = 0; i < 256; i++){
1305                         reg_w(regs, VDAC_ADDRESS_W, i);
1306                         reg_w(regs, VDAC_DATA, zinfo->color_table[i][0]);
1307                         reg_w(regs, VDAC_DATA, zinfo->color_table[i][1]);
1308                         reg_w(regs, VDAC_DATA, zinfo->color_table[i][2]);
1309                 }
1310         return 0;
1311 }
1312
1313 static struct fb_ops retz3fb_ops = {
1314         .owner =        THIS_MODULE,
1315         .fb_get_fix =   retz3fb_get_fix,
1316         .fb_get_var =   retz3fb_get_var,
1317         .fb_set_var =   retz3fb_set_var,
1318         .fb_get_cmap =  retz3fb_get_cmap,
1319         .fb_set_cmap =  gen_set_cmap,
1320         .fb_setcolreg = retz3fb_setcolreg,
1321         .fb_blank =     retz3fb_blank,
1322 };
1323
1324 int __init retz3fb_setup(char *options)
1325 {
1326         char *this_opt;
1327
1328         if (!options || !*options)
1329                 return 0;
1330
1331         while ((this_opt = strsep(&options, ",")) != NULL) {
1332                 if (!*this_opt)
1333                         continue;
1334                 if (!strcmp(this_opt, "inverse")) {
1335                         z3fb_inverse = 1;
1336                         fb_invert_cmaps();
1337                 } else if (!strncmp(this_opt, "font:", 5)) {
1338                         strlcpy(fontname, this_opt+5, sizeof(fontname));
1339                 } else
1340                         z3fb_mode = get_video_mode(this_opt);
1341         }
1342         return 0;
1343 }
1344
1345
1346 /*
1347  *    Initialization
1348  */
1349
1350 int __init retz3fb_init(void)
1351 {
1352         unsigned long board_addr, board_size;
1353         struct zorro_dev *z = NULL;
1354         volatile unsigned char *regs;
1355         struct retz3fb_par par;
1356         struct retz3_fb_info *zinfo;
1357         struct fb_info *fb_info;
1358         short i;
1359         int res = -ENXIO;
1360
1361         while ((z = zorro_find_device(ZORRO_PROD_MACROSYSTEMS_RETINA_Z3, z))) {
1362                 board_addr = z->resource.start;
1363                 board_size = z->resource.end-z->resource.start+1;
1364                 if (!request_mem_region(board_addr, 0x0c00000,
1365                                         "ncr77c32blt")) {
1366                         continue;
1367                 if (!request_mem_region(board_addr+VIDEO_MEM_OFFSET,
1368                                         0x00400000, "RAM"))
1369                         release_mem_region(board_addr, 0x00c00000);
1370                         continue;
1371                 }
1372                 if (!(zinfo = kmalloc(sizeof(struct retz3_fb_info),
1373                                       GFP_KERNEL)))
1374                         return -ENOMEM;
1375                 memset(zinfo, 0, sizeof(struct retz3_fb_info));
1376
1377                 zinfo->base = ioremap(board_addr, board_size);
1378                 zinfo->regs = zinfo->base;
1379                 zinfo->fbmem = zinfo->base + VIDEO_MEM_OFFSET;
1380                 /* Get memory size - for now we asume it's a 4MB board */
1381                 zinfo->fbsize = 0x00400000; /* 4 MB */
1382                 zinfo->physregs = board_addr;
1383                 zinfo->physfbmem = board_addr + VIDEO_MEM_OFFSET;
1384
1385                 fb_info = fbinfo(zinfo);
1386
1387                 for (i = 0; i < 256; i++){
1388                         for (i = 0; i < 256; i++){
1389                                 zinfo->color_table[i][0] = i;
1390                                 zinfo->color_table[i][1] = i;
1391                                 zinfo->color_table[i][2] = i;
1392                         }
1393                 }
1394
1395                 regs = zinfo->regs;
1396                 /* Disable hardware cursor */
1397                 seq_w(regs, SEQ_CURSOR_Y_INDEX, 0x00);
1398
1399                 retz3fb_setcolreg (255, 56<<8, 100<<8, 160<<8, 0, fb_info);
1400                 retz3fb_setcolreg (254, 0, 0, 0, 0, fb_info);
1401
1402                 strcpy(fb_info->modename, retz3fb_name);
1403                 fb_info->changevar = NULL;
1404                 fb_info->fbops = &retz3fb_ops;
1405                 fb_info->screen_base = zinfo->fbmem;
1406                 fb_info->disp = &zinfo->disp;
1407                 fb_info->currcon = -1;
1408                 fb_info->switch_con = &z3fb_switch;
1409                 fb_info->updatevar = &z3fb_updatevar;
1410                 fb_info->flags = FBINFO_FLAG_DEFAULT;
1411                 strlcpy(fb_info->fontname, fontname, sizeof(fb_info->fontname));
1412
1413                 if (z3fb_mode == -1)
1414                         retz3fb_default = retz3fb_predefined[0].var;
1415
1416                 retz3_decode_var(&retz3fb_default, &par);
1417                 retz3_encode_var(&retz3fb_default, &par);
1418
1419                 do_fb_set_var(fb_info, &retz3fb_default, 0);
1420                 retz3fb_get_var(&zinfo->disp.var, -1, fb_info);
1421
1422                 retz3fb_set_disp(-1, fb_info);
1423
1424                 do_install_cmap(0, fb_info);
1425
1426                 if (register_framebuffer(fb_info) < 0) {
1427                         iounmap(zinfo->base);
1428                         return -EINVAL;
1429                 }
1430
1431                 printk(KERN_INFO "fb%d: %s frame buffer device, using %ldK of "
1432                        "video memory\n", fb_info->node,
1433                        fb_info->modename, zinfo->fbsize>>10);
1434
1435                 /* FIXME: This driver cannot be unloaded yet */
1436                 res = 0;
1437         }
1438         return res;
1439 }
1440
1441
1442 static int z3fb_switch(int con, struct fb_info *info)
1443 {
1444         /* Do we have to save the colormap? */
1445         if (fb_display[info->currcon].cmap.len)
1446                 fb_get_cmap(&fb_display[info->currcon].cmap, 1,
1447                             retz3_getcolreg, info);
1448
1449         do_fb_set_var(info, &fb_display[con].var, 1);
1450         info->currcon = con;
1451         /* Install new colormap */
1452         do_install_cmap(con, info);
1453         return 0;
1454 }
1455
1456
1457 /*
1458  *    Update the `var' structure (called by fbcon.c)
1459  *
1460  *    This call looks only at yoffset and the FB_VMODE_YWRAP flag in `var'.
1461  *    Since it's called by a kernel driver, no range checking is done.
1462  */
1463
1464 static int z3fb_updatevar(int con, struct fb_info *info)
1465 {
1466         return 0;
1467 }
1468
1469 /*
1470  *    Get a Video Mode
1471  */
1472
1473 static int __init get_video_mode(const char *name)
1474 {
1475         short i;
1476
1477         for (i = 0; i < NUM_TOTAL_MODES; i++)
1478                 if (!strcmp(name, retz3fb_predefined[i].name)){
1479                         retz3fb_default = retz3fb_predefined[i].var;
1480                         return i;
1481                 }
1482         return -1;
1483 }
1484
1485
1486 #ifdef MODULE
1487 MODULE_LICENSE("GPL");
1488
1489 int init_module(void)
1490 {
1491         return retz3fb_init();
1492 }
1493 #endif
1494
1495
1496 /*
1497  *  Text console acceleration
1498  */
1499
1500 #ifdef FBCON_HAS_CFB8
1501 static void retz3_8_bmove(struct display *p, int sy, int sx,
1502                           int dy, int dx, int height, int width)
1503 {
1504         int fontwidth = fontwidth(p);
1505
1506         sx *= fontwidth;
1507         dx *= fontwidth;
1508         width *= fontwidth;
1509
1510         retz3_bitblt(p,
1511                      (unsigned short)sx,
1512                      (unsigned short)(sy*fontheight(p)),
1513                      (unsigned short)dx,
1514                      (unsigned short)(dy*fontheight(p)),
1515                      (unsigned short)width,
1516                      (unsigned short)(height*fontheight(p)),
1517                      Z3BLTcopy,
1518                      0xffff);
1519 }
1520
1521 static void retz3_8_clear(struct vc_data *conp, struct display *p,
1522                           int sy, int sx, int height, int width)
1523 {
1524         unsigned short col;
1525         int fontwidth = fontwidth(p);
1526
1527         sx *= fontwidth;
1528         width *= fontwidth;
1529
1530         col = attr_bgcol_ec(p, conp);
1531         col &= 0xff;
1532         col |= (col << 8);
1533
1534         retz3_bitblt(p,
1535                      (unsigned short)sx,
1536                      (unsigned short)(sy*fontheight(p)),
1537                      (unsigned short)sx,
1538                      (unsigned short)(sy*fontheight(p)),
1539                      (unsigned short)width,
1540                      (unsigned short)(height*fontheight(p)),
1541                      Z3BLTset,
1542                      col);
1543 }
1544
1545
1546 static void retz3_putc(struct vc_data *conp, struct display *p, int c,
1547                        int yy, int xx)
1548 {
1549         retz3_busy(p);
1550         fbcon_cfb8_putc(conp, p, c, yy, xx);
1551 }
1552
1553
1554 static void retz3_putcs(struct vc_data *conp, struct display *p,
1555                         const unsigned short *s, int count,
1556                         int yy, int xx)
1557 {
1558         retz3_busy(p);
1559         fbcon_cfb8_putcs(conp, p, s, count, yy, xx);
1560 }
1561
1562
1563 static void retz3_revc(struct display *p, int xx, int yy)
1564 {
1565         retz3_busy(p);
1566         fbcon_cfb8_revc(p, xx, yy);
1567 }
1568
1569
1570 static void retz3_clear_margins(struct vc_data* conp, struct display* p,
1571                                 int bottom_only)
1572 {
1573         retz3_busy(p);
1574         fbcon_cfb8_clear_margins(conp, p, bottom_only);
1575 }
1576
1577
1578 static struct display_switch fbcon_retz3_8 = {
1579         .setup          = fbcon_cfb8_setup,
1580         .bmove          = retz3_8_bmove,
1581         .clear          = retz3_8_clear,
1582         .putc           = retz3_putc,
1583         .putcs          = retz3_putcs,
1584         .revc           = retz3_revc,
1585         .clear_margins  = retz3_clear_margins,
1586         .fontwidthmask  = FONTWIDTH(8)
1587 };
1588 #endif