Import changeset
[linux-flexiantxendom0-3.2.10.git] / drivers / video / g364fb.c
1 /* $Id: g364fb.c,v 1.3 1998/08/28 22:43:00 tsbogend Exp $
2  *
3  * linux/drivers/video/g364fb.c -- Mips Magnum frame buffer device
4  *
5  * (C) 1998 Thomas Bogendoerfer
6  *
7  *  This driver is based on tgafb.c
8  *
9  *      Copyright (C) 1997 Geert Uytterhoeven 
10  *      Copyright (C) 1995  Jay Estabrook
11  *
12  *  This file is subject to the terms and conditions of the GNU General Public
13  *  License. See the file COPYING in the main directory of this archive for
14  *  more details.
15  */
16
17 #include <linux/module.h>
18 #include <linux/sched.h>
19 #include <linux/kernel.h>
20 #include <linux/errno.h>
21 #include <linux/string.h>
22 #include <linux/mm.h>
23 #include <linux/tty.h>
24 #include <linux/malloc.h>
25 #include <linux/vmalloc.h>
26 #include <linux/delay.h>
27 #include <linux/interrupt.h>
28 #include <linux/fb.h>
29 #include <linux/init.h>
30 #include <linux/pci.h>
31 #include <linux/selection.h>
32 #include <linux/console.h>
33 #include <asm/io.h>
34 #include <asm/jazz.h>
35
36 #include <video/fbcon.h>
37 #include <video/fbcon-cfb8.h>
38
39 /* 
40  * Various defines for the G364
41  */
42 #define G364_MEM_BASE   0xe4400000
43 #define G364_PORT_BASE  0xe4000000
44 #define ID_REG          0xe4000000      /* Read only */
45 #define BOOT_REG        0xe4080000
46 #define TIMING_REG      0xe4080108      /* to 0x080170 - DON'T TOUCH! */
47 #define DISPLAY_REG     0xe4080118
48 #define VDISPLAY_REG    0xe4080150
49 #define MASK_REG        0xe4080200
50 #define CTLA_REG        0xe4080300
51 #define CURS_TOGGLE     0x800000
52 #define BIT_PER_PIX     0x700000        /* bits 22 to 20 of Control A */
53 #define DELAY_SAMPLE    0x080000
54 #define PORT_INTER      0x040000
55 #define PIX_PIPE_DEL    0x030000        /* bits 17 and 16 of Control A */
56 #define PIX_PIPE_DEL2   0x008000        /* same as above - don't ask me why */
57 #define TR_CYCLE_TOG    0x004000
58 #define VRAM_ADR_INC    0x003000        /* bits 13 and 12 of Control A */
59 #define BLANK_OFF       0x000800
60 #define FORCE_BLANK     0x000400
61 #define BLK_FUN_SWTCH   0x000200
62 #define BLANK_IO        0x000100
63 #define BLANK_LEVEL     0x000080
64 #define A_VID_FORM      0x000040
65 #define D_SYNC_FORM     0x000020
66 #define FRAME_FLY_PAT   0x000010
67 #define OP_MODE         0x000008
68 #define INTL_STAND      0x000004
69 #define SCRN_FORM       0x000002
70 #define ENABLE_VTG      0x000001        
71 #define TOP_REG         0xe4080400
72 #define CURS_PAL_REG    0xe4080508      /* to 0x080518 */
73 #define CHKSUM_REG      0xe4080600      /* to 0x080610 - unused */
74 #define CURS_POS_REG    0xe4080638
75 #define CLR_PAL_REG     0xe4080800      /* to 0x080ff8 */
76 #define CURS_PAT_REG    0xe4081000      /* to 0x081ff8 */
77 #define MON_ID_REG      0xe4100000      /* unused */
78 #define RESET_REG       0xe4180000      /* Write only */
79
80 static int currcon = 0;
81 static struct display disp;
82 static struct fb_info fb_info;
83 static struct { u_char red, green, blue, pad; } palette[256];
84
85 static struct fb_fix_screeninfo fb_fix = { { "G364 8plane", } };
86 static struct fb_var_screeninfo fb_var = { 0, };
87
88
89 /*
90  *  Interface used by the world
91  */
92 static int g364fb_get_fix(struct fb_fix_screeninfo *fix, int con,
93                           struct fb_info *info);
94 static int g364fb_get_var(struct fb_var_screeninfo *var, int con,
95                           struct fb_info *info);
96 static int g364fb_set_var(struct fb_var_screeninfo *var, int con,
97                           struct fb_info *info);
98 static int g364fb_pan_display(struct fb_var_screeninfo *var, int con,
99                               struct fb_info *info);
100 static int g364fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
101                            struct fb_info *info);
102 static int g364fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
103                            struct fb_info *info);
104
105
106 /*
107  *  Interface to the low level console driver
108  */
109 int g364fb_init(void);
110 static int g364fbcon_switch(int con, struct fb_info *info);
111 static int g364fbcon_updatevar(int con, struct fb_info *info);
112 static void g364fbcon_blank(int blank, struct fb_info *info);
113
114
115 /*
116  *  Internal routines
117  */
118 static int g364fb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
119                             u_int *transp, struct fb_info *info);
120 static int g364fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
121                             u_int transp, struct fb_info *info);
122 static void do_install_cmap(int con, struct fb_info *info);
123
124
125 static struct fb_ops g364fb_ops = {
126         owner:          THIS_MODULE,
127         fb_get_fix:     g364fb_get_fix,
128         fb_get_var:     g364fb_get_var,
129         fb_set_var:     g364fb_set_var,
130         fb_get_cmap:    g364fb_get_cmap,
131         fb_set_cmap:    g364fb_set_cmap,
132         fb_pan_display: g364fb_pan_display,
133 };
134
135
136 void fbcon_g364fb_cursor(struct display *p, int mode, int x, int y)
137 {
138     switch (mode) {
139      case CM_ERASE:
140         *(unsigned int *) CTLA_REG |= CURS_TOGGLE;
141         break;
142
143      case CM_MOVE:
144      case CM_DRAW:
145         *(unsigned int *) CTLA_REG &= ~CURS_TOGGLE;
146         *(unsigned int *) CURS_POS_REG = ((x * fontwidth(p)) << 12) | ((y * fontheight(p))-p->var.yoffset);
147         break;
148     }
149 }
150
151
152 static struct display_switch fbcon_g364cfb8 = {
153     setup:              fbcon_cfb8_setup,
154     bmove:              fbcon_cfb8_bmove,
155     clear:              fbcon_cfb8_clear,
156     putc:               fbcon_cfb8_putc,
157     putcs:              fbcon_cfb8_putcs,
158     revc:               fbcon_cfb8_revc,
159     cursor:             fbcon_g364fb_cursor,
160     clear_margins:      fbcon_cfb8_clear_margins,
161     fontwidthmask:      FONTWIDTH(8)
162 };
163
164 /*
165  *  Get the Fixed Part of the Display
166  */
167 static int g364fb_get_fix(struct fb_fix_screeninfo *fix, int con,
168                           struct fb_info *info)
169 {
170     memcpy(fix, &fb_fix, sizeof(fb_fix));
171     return 0;
172 }
173
174
175 /*
176  *  Get the User Defined Part of the Display
177  */
178 static int g364fb_get_var(struct fb_var_screeninfo *var, int con,
179                           struct fb_info *info)
180 {
181     memcpy(var, &fb_var, sizeof(fb_var));
182     return 0;
183 }
184
185
186 /*
187  *  Set the User Defined Part of the Display
188  */
189 static int g364fb_set_var(struct fb_var_screeninfo *var, int con,
190                           struct fb_info *info)
191 {
192     struct display *display;
193     int oldbpp = -1, err;
194
195     if (con >= 0)
196         display = &fb_display[con];
197     else
198         display = &disp;        /* used during initialization */
199
200     if (var->xres > fb_var.xres || var->yres > fb_var.yres ||
201         var->xres_virtual > fb_var.xres_virtual ||
202         var->yres_virtual > fb_var.yres_virtual ||
203         var->bits_per_pixel > fb_var.bits_per_pixel ||
204         var->nonstd ||
205         (var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
206         return -EINVAL;
207     memcpy(var, &fb_var, sizeof(fb_var));
208     
209     if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
210         oldbpp = display->var.bits_per_pixel;
211         display->var = *var;
212         *(unsigned int *)TOP_REG = var->yoffset * var->xres;    
213     }
214     if (oldbpp != var->bits_per_pixel) {
215         if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
216             return err;
217         do_install_cmap(con, info);
218     }
219     return 0;
220 }
221
222
223 /*
224  *  Pan or Wrap the Display
225  *
226  *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
227  */
228 static int g364fb_pan_display(struct fb_var_screeninfo *var, int con,
229                               struct fb_info *info)
230 {
231     if (var->xoffset || var->yoffset+var->yres > var->yres_virtual)
232         return -EINVAL;
233     
234     *(unsigned int *)TOP_REG = var->yoffset * var->xres;
235     return 0;
236 }
237
238 /*
239  *  Get the Colormap
240  */
241 static int g364fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
242                            struct fb_info *info)
243 {
244     if (con == currcon) /* current console? */
245         return fb_get_cmap(cmap, kspc, g364fb_getcolreg, info);
246     else if (fb_display[con].cmap.len) /* non default colormap? */
247         fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
248     else
249         fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
250                      cmap, kspc ? 0 : 2);
251     return 0;
252 }
253
254 /*
255  *  Set the Colormap
256  */
257 static int g364fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
258                            struct fb_info *info)
259 {
260     int err;
261
262     if (!fb_display[con].cmap.len) {    /* no colormap allocated? */
263         if ((err = fb_alloc_cmap(&fb_display[con].cmap,
264                                  1<<fb_display[con].var.bits_per_pixel, 0)))
265             return err;
266     }
267     if (con == currcon) {               /* current console? */
268         return fb_set_cmap(cmap, kspc, g364fb_setcolreg, info);
269     } else
270         fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
271     return 0;
272 }
273
274
275 /*
276  *  Initialisation
277  */
278 int __init g364fb_init(void)
279 {
280     int i,j;
281     volatile unsigned int *pal_ptr = (volatile unsigned int *) CLR_PAL_REG;
282     volatile unsigned int *curs_pal_ptr = (volatile unsigned int *) CURS_PAL_REG;
283     unsigned int xres, yres;
284     int mem;
285
286     /* TBD: G364 detection */
287     
288     /* get the resolution set by ARC console */
289     *(volatile unsigned int *)CTLA_REG &= ~ENABLE_VTG;
290     xres = (*((volatile unsigned int*)DISPLAY_REG) & 0x00ffffff) * 4;
291     yres = (*((volatile unsigned int*)VDISPLAY_REG) & 0x00ffffff) / 2;
292     *(volatile unsigned int *)CTLA_REG |= ENABLE_VTG;    
293
294     /* initialise color palette */
295     for (i = 0; i < 16; i++) {
296         j = color_table[i];
297         palette[i].red=default_red[j];
298         palette[i].green=default_grn[j];
299         palette[i].blue=default_blu[j];
300         pal_ptr[i << 1] = (palette[i].red << 16) | (palette[i].green << 8) | palette[i].blue;
301     }
302     
303     /* setup cursor */
304     curs_pal_ptr[0] |= 0x00ffffff;
305     curs_pal_ptr[2] |= 0x00ffffff;
306     curs_pal_ptr[4] |= 0x00ffffff;
307
308     /*
309      * first set the whole cursor to transparent
310      */
311     for (i = 0; i < 512; i++)
312         *(unsigned short *)(CURS_PAT_REG+i*8) = 0;
313
314     /*
315      * switch the last two lines to cursor palette 3
316      * we assume here, that FONTSIZE_X is 8
317      */
318     *(unsigned short *)(CURS_PAT_REG + 14*64) = 0xffff;
319     *(unsigned short *)(CURS_PAT_REG + 15*64) = 0xffff;                     
320
321     fb_var.bits_per_pixel = 8;
322     fb_var.xres = fb_var.xres_virtual = xres;
323     fb_var.yres = yres;
324
325     fb_fix.line_length = (xres / 8) * fb_var.bits_per_pixel;
326     fb_fix.smem_start = 0x40000000; /* physical address */
327     /* get size of video memory; this is special for the JAZZ hardware */
328     mem = (r4030_read_reg32(JAZZ_R4030_CONFIG) >> 8) & 3;
329     fb_fix.smem_len = (1 << (mem*2)) * 512 * 1024;
330     fb_fix.type = FB_TYPE_PACKED_PIXELS;
331     fb_fix.type_aux = 0;
332     fb_fix.visual = FB_VISUAL_PSEUDOCOLOR;    
333     fb_fix.xpanstep = 0;
334     fb_fix.ypanstep = 1;
335     fb_fix.ywrapstep = 0;
336     fb_fix.mmio_start = 0;
337     fb_fix.mmio_len = 0;
338     fb_fix.accel = FB_ACCEL_NONE;
339     
340     fb_var.yres_virtual = fb_fix.smem_len / xres;
341     fb_var.xoffset = fb_var.yoffset = 0;
342     fb_var.grayscale = 0;
343
344     fb_var.red.offset = 0;
345     fb_var.green.offset = 0;
346     fb_var.blue.offset = 0;
347
348     fb_var.red.length = fb_var.green.length = fb_var.blue.length = 8;
349     fb_var.red.msb_right = fb_var.green.msb_right = fb_var.blue.msb_right = 0;
350     fb_var.transp.offset = fb_var.transp.length = fb_var.transp.msb_right = 0;
351     fb_var.nonstd = 0;
352     fb_var.activate = 0;
353     fb_var.height = fb_var.width = -1;
354     fb_var.accel_flags = 0;
355     fb_var.pixclock = 39722;
356     fb_var.left_margin = 40;
357     fb_var.right_margin = 24;
358     fb_var.upper_margin = 32;
359     fb_var.lower_margin = 11;
360     fb_var.hsync_len = 96;
361     fb_var.vsync_len = 2;
362     fb_var.sync = 0;
363     fb_var.vmode = FB_VMODE_NONINTERLACED;
364
365     disp.var = fb_var;
366     disp.cmap.start = 0;
367     disp.cmap.len = 0;
368     disp.cmap.red = disp.cmap.green = disp.cmap.blue = disp.cmap.transp = NULL;
369     disp.screen_base = (char *)G364_MEM_BASE; /* virtual kernel address */
370     disp.visual = fb_fix.visual;
371     disp.type = fb_fix.type;
372     disp.type_aux = fb_fix.type_aux;
373     disp.ypanstep = fb_fix.ypanstep;
374     disp.ywrapstep = fb_fix.ywrapstep;
375     disp.line_length = fb_fix.line_length;
376     disp.can_soft_blank = 1;
377     disp.inverse = 0;
378     disp.dispsw = &fbcon_g364cfb8;
379
380     strcpy(fb_info.modename, fb_fix.id);
381     fb_info.node = -1;
382     fb_info.fbops = &g364fb_ops;
383     fb_info.disp = &disp;
384     fb_info.fontname[0] = '\0';
385     fb_info.changevar = NULL;
386     fb_info.switch_con = &g364fbcon_switch;
387     fb_info.updatevar = &g364fbcon_updatevar;
388     fb_info.blank = &g364fbcon_blank;
389     fb_info.flags = FBINFO_FLAG_DEFAULT;
390
391     g364fb_set_var(&fb_var, -1, &fb_info);
392
393     if (register_framebuffer(&fb_info) < 0)
394         return -EINVAL;
395
396     printk("fb%d: %s frame buffer device\n", GET_FB_IDX(fb_info.node),
397            fb_fix.id);
398     return 0;
399 }
400
401
402 static int g364fbcon_switch(int con, struct fb_info *info)
403 {
404     /* Do we have to save the colormap? */
405     if (fb_display[currcon].cmap.len)
406         fb_get_cmap(&fb_display[currcon].cmap, 1, g364fb_getcolreg, info);
407
408     currcon = con;
409     /* Install new colormap */
410     do_install_cmap(con, info);
411     g364fbcon_updatevar(con, info);    
412     return 0;
413 }
414
415 /*
416  *  Update the `var' structure (called by fbcon.c)
417  */
418 static int g364fbcon_updatevar(int con, struct fb_info *info)
419 {
420     if (con == currcon) {
421         struct fb_var_screeninfo *var = &fb_display[currcon].var;
422
423         /* hardware scrolling */
424         *(unsigned int *)TOP_REG = var->yoffset * var->xres;    
425     }
426     return 0;
427 }
428
429 /*
430  *  Blank the display.
431  */
432 static void g364fbcon_blank(int blank, struct fb_info *info)
433 {
434     if (blank)
435         *(unsigned int *) CTLA_REG |= FORCE_BLANK;      
436     else
437         *(unsigned int *) CTLA_REG &= ~FORCE_BLANK;     
438 }
439
440 /*
441  *  Read a single color register and split it into
442  *  colors/transparent. Return != 0 for invalid regno.
443  */
444 static int g364fb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
445                             u_int *transp, struct fb_info *info)
446 {
447     if (regno > 255)
448         return 1;
449     *red = (palette[regno].red << 8) | palette[regno].red;
450     *green = (palette[regno].green << 8) | palette[regno].green;
451     *blue = (palette[regno].blue << 8) | palette[regno].blue;
452     *transp = 0;
453     return 0;
454 }
455
456 /*
457  *  Set a single color register. Return != 0 for invalid regno.
458  */
459 static int g364fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
460                             u_int transp, struct fb_info *info)
461 {
462     volatile unsigned int *ptr = (volatile unsigned int *) CLR_PAL_REG;
463
464     if (regno > 255)
465         return 1;
466
467     red >>= 8;
468     green >>= 8;
469     blue >>=8;
470     palette[regno].red = red;
471     palette[regno].green = green;
472     palette[regno].blue = blue;
473     
474     ptr[regno << 1] = (red << 16) | (green << 8) | blue;
475
476     return 0;
477 }
478
479
480 static void do_install_cmap(int con, struct fb_info *info)
481 {
482     if (con != currcon)
483         return;
484     if (fb_display[con].cmap.len)
485         fb_set_cmap(&fb_display[con].cmap, 1, g364fb_setcolreg, info);
486     else
487         fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), 1,
488                     g364fb_setcolreg, info);
489 }