Import 2.4.0-test5pre6
[davej-history.git] / drivers / video / controlfb.c
blob10453891d9878d68d85e3e0a6e12da52a1e25113
1 /*
2 * controlfb.c -- frame buffer device for the PowerMac 'control' display
4 * Created 12 July 1998 by Dan Jacobowitz <dan@debian.org>
5 * Copyright (C) 1998 Dan Jacobowitz
7 * Frame buffer structure from:
8 * drivers/video/chipsfb.c -- frame buffer device for
9 * Chips & Technologies 65550 chip.
11 * Copyright (C) 1998 Paul Mackerras
13 * This file is derived from the Powermac "chips" driver:
14 * Copyright (C) 1997 Fabio Riccardi.
15 * And from the frame buffer device for Open Firmware-initialized devices:
16 * Copyright (C) 1997 Geert Uytterhoeven.
18 * Hardware information from:
19 * control.c: Console support for PowerMac "control" display adaptor.
20 * Copyright (C) 1996 Paul Mackerras
22 * This file is subject to the terms and conditions of the GNU General Public
23 * License. See the file COPYING in the main directory of this archive for
24 * more details.
27 #include <linux/config.h>
28 #include <linux/module.h>
29 #include <linux/kernel.h>
30 #include <linux/errno.h>
31 #include <linux/string.h>
32 #include <linux/mm.h>
33 #include <linux/tty.h>
34 #include <linux/malloc.h>
35 #include <linux/vmalloc.h>
36 #include <linux/delay.h>
37 #include <linux/interrupt.h>
38 #include <linux/fb.h>
39 #include <linux/selection.h>
40 #include <linux/init.h>
41 #include <linux/pci.h>
42 #include <linux/nvram.h>
43 #ifdef CONFIG_FB_COMPAT_XPMAC
44 #include <asm/vc_ioctl.h>
45 #endif
46 #include <linux/adb.h>
47 #include <linux/cuda.h>
48 #include <asm/io.h>
49 #include <asm/prom.h>
50 #include <asm/pgtable.h>
52 #include <video/fbcon.h>
53 #include <video/fbcon-cfb8.h>
54 #include <video/fbcon-cfb16.h>
55 #include <video/fbcon-cfb32.h>
56 #include <video/macmodes.h>
58 #include "controlfb.h"
60 struct fb_par_control {
61 int vmode, cmode;
62 int xres, yres;
63 int vxres, vyres;
64 int xoffset, yoffset;
67 #define DIRTY(z) ((x)->z != (y)->z)
68 static inline int PAR_EQUAL(struct fb_par_control *x, struct fb_par_control *y)
70 return (!DIRTY(vmode) && !DIRTY(cmode) && !DIRTY(xres)
71 && !DIRTY(yres) && !DIRTY(vxres) && !DIRTY(vyres)
72 && !DIRTY(xoffset) && !DIRTY(yoffset));
74 static inline int VAR_MATCH(struct fb_var_screeninfo *x, struct fb_var_screeninfo *y)
76 return (!DIRTY(bits_per_pixel) && !DIRTY(xres)
77 && !DIRTY(yres) && !DIRTY(xres_virtual)
78 && !DIRTY(yres_virtual));
81 struct fb_info_control {
82 struct fb_info info;
83 /* struct fb_fix_screeninfo fix;
84 struct fb_var_screeninfo var;*/
85 struct display display;
86 struct fb_par_control par;
87 struct {
88 __u8 red, green, blue;
89 } palette[256];
91 struct cmap_regs *cmap_regs;
92 unsigned long cmap_regs_phys;
94 struct control_regs *control_regs;
95 unsigned long control_regs_phys;
97 __u8 *frame_buffer;
98 unsigned long frame_buffer_phys;
100 int sense, control_use_bank2;
101 unsigned long total_vram;
102 union {
103 #ifdef FBCON_HAS_CFB16
104 u16 cfb16[16];
105 #endif
106 #ifdef FBCON_HAS_CFB32
107 u32 cfb32[16];
108 #endif
109 } fbcon_cmap;
112 /******************** Prototypes for exported functions ********************/
113 static int control_get_fix(struct fb_fix_screeninfo *fix, int con,
114 struct fb_info *info);
115 static int control_get_var(struct fb_var_screeninfo *var, int con,
116 struct fb_info *info);
117 static int control_set_var(struct fb_var_screeninfo *var, int con,
118 struct fb_info *info);
119 static int control_pan_display(struct fb_var_screeninfo *var, int con,
120 struct fb_info *info);
121 static int control_get_cmap(struct fb_cmap *cmap, int kspc, int con,
122 struct fb_info *info);
123 static int control_set_cmap(struct fb_cmap *cmap, int kspc, int con,
124 struct fb_info *info);
127 static int controlfb_getcolreg(u_int regno, u_int *red, u_int *green,
128 u_int *blue, u_int *transp, struct fb_info *info);
129 static int controlfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
130 u_int transp, struct fb_info *info);
132 /******************** Prototypes for internal functions ********************/
133 static void control_par_to_fix(struct fb_par_control *par, struct fb_fix_screeninfo *fix,
134 struct fb_info_control *p);
135 static void do_install_cmap(int con, struct fb_info *info);
136 static void control_set_dispsw(struct display *disp, int cmode, struct fb_info_control *p);
138 /************************* Internal variables *****************************/
139 static int currcon = 0;
140 static int par_set = 0;
141 static char fontname[40] __initdata = { 0 };
142 static int default_vmode = VMODE_NVRAM;
143 static int default_cmode = CMODE_NVRAM;
146 * Exported functions
148 int control_init(void);
149 #ifdef CONFIG_FB_OF
150 void control_of_init(struct device_node *dp);
151 #endif
152 void control_setup(char *);
154 static int read_control_sense(struct fb_info_control *p);
155 static inline int control_vram_reqd(int video_mode, int color_mode);
156 static void set_control_clock(unsigned char *params);
157 static void control_set_hardware(struct fb_info_control *p, struct fb_par_control *par);
158 static inline void control_par_to_var(struct fb_par_control *par, struct fb_var_screeninfo *var);
159 static int control_var_to_par(struct fb_var_screeninfo *var,
160 struct fb_par_control *par, const struct fb_info *fb_info);
162 static void control_init_info(struct fb_info *info, struct fb_info_control *p);
163 static void control_par_to_display(struct fb_par_control *par,
164 struct display *disp, struct fb_fix_screeninfo *fix, struct fb_info_control *p);
166 static int controlfb_updatevar(int con, struct fb_info *info);
168 static struct fb_ops controlfb_ops = {
169 owner: THIS_MODULE,
170 fb_get_fix: control_get_fix,
171 fb_get_var: control_get_var,
172 fb_set_var: control_set_var,
173 fb_get_cmap: control_get_cmap,
174 fb_set_cmap: control_set_cmap,
175 fb_pan_display: control_pan_display,
180 /******************** The functions for controlfb_ops ********************/
182 #ifdef MODULE
183 int init_module(void)
185 struct device_node *dp;
187 printk("Loading...\n");
188 dp = find_devices("control");
189 if (dp != 0)
190 control_of_init(dp);
191 else
192 printk("Failed.\n");
193 printk("Done.\n");
196 void cleanup_module(void)
199 #endif
201 /*********** Providing our information to the user ************/
203 static int control_get_fix(struct fb_fix_screeninfo *fix, int con,
204 struct fb_info *info)
206 struct fb_info_control *p = (struct fb_info_control *) info;
208 if(!par_set)
209 printk(KERN_ERR "control_get_fix called with unset par!\n");
210 if(con == -1) {
211 control_par_to_fix(&p->par, fix, p);
212 } else {
213 struct fb_par_control par;
215 control_var_to_par(&fb_display[con].var, &par, info);
216 control_par_to_fix(&par, fix, p);
218 return 0;
221 static int control_get_var(struct fb_var_screeninfo *var, int con,
222 struct fb_info *info)
224 struct fb_info_control *p = (struct fb_info_control *) info;
226 if(!par_set)
227 printk(KERN_ERR "control_get_var called with unset par!\n");
228 if(con == -1) {
229 control_par_to_var(&p->par, var);
230 } else {
231 *var = fb_display[con].var;
233 return 0;
236 /* Sets everything according to var */
237 /* No longer safe for use in console switching */
238 static int control_set_var(struct fb_var_screeninfo *var, int con,
239 struct fb_info *info)
241 struct fb_info_control *p = (struct fb_info_control *) info;
242 struct display *disp;
243 struct fb_par_control par;
244 int depthchange, err;
245 int activate = var->activate;
247 disp = (con >= 0) ? &fb_display[con] : info->disp;
249 if((err = control_var_to_par(var, &par, info))) {
250 printk (KERN_ERR "control_set_var: error calling control_var_to_par: %d.\n", err);
251 return err;
254 control_par_to_var(&par, var);
256 if ((activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
257 return 0;
259 /* I know, we want to use fb_display[con], but grab certain info from p->var instead. */
260 /* [above no longer true] */
261 depthchange = (disp->var.bits_per_pixel != var->bits_per_pixel);
262 if(!VAR_MATCH(&disp->var, var)) {
263 struct fb_fix_screeninfo fix;
264 control_par_to_fix(&par, &fix, p);
265 control_par_to_display(&par, disp, &fix, p);
266 if(info->changevar)
267 (*info->changevar)(con);
268 } else
269 disp->var = *var;
270 /*p->disp = *disp;*/
273 if(con == currcon || con == -1) {
274 control_set_hardware(p, &par);
276 if(depthchange) {
277 if((err = fb_alloc_cmap(&disp->cmap, 0, 0)))
278 return err;
279 do_install_cmap(con, info);
281 return 0;
284 static int control_pan_display(struct fb_var_screeninfo *var, int con,
285 struct fb_info *info)
287 struct fb_info_control *p = (struct fb_info_control *)info;
288 struct fb_par_control *par = &p->par;
290 if (var->xoffset != 0 || var->yoffset+var->yres > var->yres_virtual)
291 return -EINVAL;
292 fb_display[con].var.yoffset = par->yoffset = var->yoffset;
293 if(con == currcon)
294 out_le32(&p->control_regs->start_addr.r,
295 par->yoffset * (par->vxres << par->cmode));
296 return 0;
299 static int control_get_cmap(struct fb_cmap *cmap, int kspc, int con,
300 struct fb_info *info)
302 if (con == currcon) /* current console? */
303 return fb_get_cmap(cmap, kspc, controlfb_getcolreg, info);
304 if (fb_display[con].cmap.len) /* non default colormap? */
305 fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0: 2);
306 else {
307 int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
308 fb_copy_cmap(fb_default_cmap(size), cmap, kspc ? 0 : 2);
310 return 0;
313 static int control_set_cmap(struct fb_cmap *cmap, int kspc, int con,
314 struct fb_info *info)
316 struct display *disp = &fb_display[con];
317 int err;
319 if (disp->cmap.len == 0) {
320 int size = disp->var.bits_per_pixel == 16 ? 32 : 256;
321 err = fb_alloc_cmap(&disp->cmap, size, 0);
322 if (err)
323 return err;
325 if (con == currcon)
326 return fb_set_cmap(cmap, kspc, controlfb_setcolreg, info);
327 fb_copy_cmap(cmap, &disp->cmap, kspc ? 0 : 1);
328 return 0;
331 /******************** End of controlfb_ops implementation ********************/
332 /* (new one that is) */
335 static int controlfb_switch(int con, struct fb_info *info)
337 struct fb_info_control *p = (struct fb_info_control *)info;
338 struct fb_par_control par;
339 int oldcon = currcon;
341 if (fb_display[currcon].cmap.len)
342 fb_get_cmap(&fb_display[currcon].cmap, 1, controlfb_getcolreg,
343 info);
344 currcon = con;
346 control_var_to_par(&fb_display[con].var, &par, info);
347 control_set_hardware(p, &par);
348 control_set_dispsw(&fb_display[con], par.cmode, p);
350 if(fb_display[oldcon].var.yoffset != fb_display[con].var.yoffset)
351 controlfb_updatevar(con, info);
353 do_install_cmap(con, info);
354 return 1;
357 static int controlfb_updatevar(int con, struct fb_info *info)
359 struct fb_info_control *p = (struct fb_info_control *)info;
361 if(con != currcon)
362 return 0;
363 /* imsttfb blanks the unused bottom of the screen here...hmm. */
364 out_le32(&p->control_regs->start_addr.r,
365 fb_display[con].var.yoffset * fb_display[con].line_length);
367 return 0;
370 static void controlfb_blank(int blank_mode, struct fb_info *info)
373 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
374 * then the caller blanks by setting the CLUT (Color Look Up Table) to all
375 * black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due
376 * to e.g. a video mode which doesn't support it. Implements VESA suspend
377 * and powerdown modes on hardware that supports disabling hsync/vsync:
378 * blank_mode == 2: suspend vsync
379 * blank_mode == 3: suspend hsync
380 * blank_mode == 4: powerdown
382 /* A blank_mode of 1+VESA_NO_BLANKING or 1+VESA_POWERDOWN act alike... */
383 struct fb_info_control *p = (struct fb_info_control *) info;
384 int ctrl;
386 if(blank_mode == 1+VESA_NO_BLANKING)
387 blank_mode = 1+VESA_POWERDOWN;
388 ctrl = ld_le32(&p->control_regs->ctrl.r) | 0x33;
389 if (blank_mode)
390 --blank_mode;
391 if (blank_mode & VESA_VSYNC_SUSPEND)
392 ctrl &= ~3;
393 if (blank_mode & VESA_HSYNC_SUSPEND)
394 ctrl &= ~0x30;
395 out_le32(&p->control_regs->ctrl.r, ctrl);
397 /* TODO: Figure out how the heck to powerdown this thing! */
399 return;
402 static int controlfb_getcolreg(u_int regno, u_int *red, u_int *green,
403 u_int *blue, u_int *transp, struct fb_info *info)
405 struct fb_info_control *p = (struct fb_info_control *) info;
407 if (regno > 255)
408 return 1;
409 *red = (p->palette[regno].red<<8) | p->palette[regno].red;
410 *green = (p->palette[regno].green<<8) | p->palette[regno].green;
411 *blue = (p->palette[regno].blue<<8) | p->palette[regno].blue;
412 *transp = 0;
413 return 0;
416 static int controlfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
417 u_int transp, struct fb_info *info)
419 struct fb_info_control *p = (struct fb_info_control *) info;
420 u_int i;
421 __u8 r, g, b;
423 if (regno > 255)
424 return 1;
426 r = red >> 8;
427 g = green >> 8;
428 b = blue >> 8;
430 p->palette[regno].red = r;
431 p->palette[regno].green = g;
432 p->palette[regno].blue = b;
434 out_8(&p->cmap_regs->addr, regno); /* tell clut what addr to fill */
435 out_8(&p->cmap_regs->lut, r); /* send one color channel at */
436 out_8(&p->cmap_regs->lut, g); /* a time... */
437 out_8(&p->cmap_regs->lut, b);
439 if (regno < 16)
440 switch (p->par.cmode) {
441 #ifdef FBCON_HAS_CFB16
442 case CMODE_16:
443 p->fbcon_cmap.cfb16[regno] = (regno << 10) | (regno << 5) | regno;
444 break;
445 #endif
446 #ifdef FBCON_HAS_CFB32
447 case CMODE_32:
448 i = (regno << 8) | regno;
449 p->fbcon_cmap.cfb32[regno] = (i << 16) | i;
450 break;
451 #endif
453 return 0;
456 static void do_install_cmap(int con, struct fb_info *info)
458 if (con != currcon)
459 return;
460 if (fb_display[con].cmap.len)
461 fb_set_cmap(&fb_display[con].cmap, 1, controlfb_setcolreg,
462 info);
463 else {
464 int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
465 fb_set_cmap(fb_default_cmap(size), 1, controlfb_setcolreg,
466 info);
470 #ifdef CONFIG_FB_COMPAT_XPMAC
471 extern struct vc_mode display_info;
472 extern struct fb_info *console_fb_info;
473 #endif /* CONFIG_FB_COMPAT_XPMAC */
475 static inline int control_vram_reqd(int video_mode, int color_mode)
477 return (control_reg_init[video_mode-1]->vres
478 * control_reg_init[video_mode-1]->hres << color_mode)
479 + control_reg_init[video_mode-1]->offset[color_mode];
482 static void set_control_clock(unsigned char *params)
484 struct adb_request req;
485 int i;
487 for (i = 0; i < 3; ++i) {
488 cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
489 0x50, i + 1, params[i]);
490 while (!req.complete)
491 cuda_poll();
496 static void __init init_control(struct fb_info_control *p)
498 struct fb_par_control parstruct;
499 struct fb_par_control *par = &parstruct;
500 struct fb_var_screeninfo var;
502 p->sense = read_control_sense(p);
503 printk(KERN_INFO "Monitor sense value = 0x%x, ", p->sense);
504 /* Try to pick a video mode out of NVRAM if we have one. */
505 if (default_vmode == VMODE_NVRAM) {
506 par->vmode = nvram_read_byte(NV_VMODE);
507 if(par->vmode <= 0 || par->vmode > VMODE_MAX || !control_reg_init[par->vmode - 1])
508 par->vmode = VMODE_CHOOSE;
509 if(par->vmode == VMODE_CHOOSE)
510 par->vmode = mac_map_monitor_sense(p->sense);
511 if(!control_reg_init[par->vmode - 1])
512 par->vmode = VMODE_640_480_60;
513 } else
514 par->vmode=default_vmode;
516 if (default_cmode == CMODE_NVRAM){
517 par->cmode = nvram_read_byte(NV_CMODE);
518 if(par->cmode < CMODE_8 || par->cmode > CMODE_32)
519 par->cmode = CMODE_8;}
520 else
521 par->cmode=default_cmode;
523 * Reduce the pixel size if we don't have enough VRAM.
525 while(par->cmode > CMODE_8 && control_vram_reqd(par->vmode, par->cmode) > p->total_vram)
526 par->cmode--;
528 printk("using video mode %d and color mode %d.\n", par->vmode, par->cmode);
530 par->vxres = par->xres = control_reg_init[par->vmode - 1]->hres;
531 par->yres = control_reg_init[par->vmode - 1]->vres;
532 par->vyres = p->total_vram / (par->vxres << par->cmode);
533 par->xoffset = par->yoffset = 0;
535 control_init_info(&p->info, p);
537 par_set = 1; /* Debug */
539 control_par_to_var(par, &var);
540 var.activate = FB_ACTIVATE_NOW;
541 control_set_var(&var, -1, &p->info);
543 p->info.flags = FBINFO_FLAG_DEFAULT;
544 if (register_framebuffer(&p->info) < 0) {
545 kfree(p);
546 return;
549 printk(KERN_INFO "fb%d: control display adapter\n", GET_FB_IDX(p->info.node));
552 #define STORE_D2(a,d) \
553 out_8(&p->cmap_regs->addr, (a)); \
554 out_8(&p->cmap_regs->d2, (d))
556 /* Now how about actually saying, Make it so! */
557 /* Some things in here probably don't need to be done each time. */
558 static void control_set_hardware(struct fb_info_control *p, struct fb_par_control *par)
560 struct control_regvals *init;
561 volatile struct preg *rp;
562 int flags, ctrl, i;
563 int vmode, cmode;
565 if(PAR_EQUAL(&p->par, par))
566 return;
568 p->par = *par;
570 vmode = p->par.vmode;
571 cmode = p->par.cmode;
573 init = control_reg_init[vmode - 1];
575 if (control_vram_reqd(vmode, cmode) > 0x200000)
576 flags = 0x51;
577 else if (p->control_use_bank2)
578 flags = 0x39;
579 else
580 flags = 0x31;
581 if (vmode >= VMODE_1280_960_75 && cmode >= CMODE_16)
582 ctrl = 0x7f;
583 else
584 ctrl = 0x3b;
586 /* Initialize display timing registers */
587 out_le32(&p->control_regs->ctrl.r, 0x43b);
589 set_control_clock(init->clock_params);
591 STORE_D2(0x20, init->radacal_ctrl[cmode]);
592 STORE_D2(0x21, p->control_use_bank2 ? 0 : 1);
593 STORE_D2(0x10, 0);
594 STORE_D2(0x11, 0);
596 rp = &p->control_regs->vswin;
597 for (i = 0; i < 16; ++i, ++rp)
598 out_le32(&rp->r, init->regs[i]);
600 out_le32(&p->control_regs->pitch.r, par->vxres << cmode);
601 out_le32(&p->control_regs->mode.r, init->mode[cmode]);
602 out_le32(&p->control_regs->flags.r, flags);
603 out_le32(&p->control_regs->start_addr.r,
604 par->yoffset * (par->vxres << cmode));
605 out_le32(&p->control_regs->reg18.r, 0x1e5);
606 out_le32(&p->control_regs->reg19.r, 0);
608 for (i = 0; i < 16; ++i) {
609 controlfb_setcolreg(color_table[i], default_red[i]<<8,
610 default_grn[i]<<8, default_blu[i]<<8,
611 0, (struct fb_info *)p);
613 /* Does the above need to be here each time? -- danj */
615 /* Turn on display */
616 out_le32(&p->control_regs->ctrl.r, ctrl);
618 #ifdef CONFIG_FB_COMPAT_XPMAC
619 /* And let the world know the truth. */
620 if (!console_fb_info || console_fb_info == &p->info) {
621 display_info.height = p->par.yres;
622 display_info.width = p->par.xres;
623 display_info.depth = (cmode == CMODE_32) ? 32 :
624 ((cmode == CMODE_16) ? 16 : 8);
625 display_info.pitch = p->par.vxres << p->par.cmode;
626 display_info.mode = vmode;
627 strncpy(display_info.name, "control",
628 sizeof(display_info.name));
629 display_info.fb_address = p->frame_buffer_phys
630 + control_reg_init[vmode-1]->offset[cmode];
631 display_info.cmap_adr_address = p->cmap_regs_phys;
632 display_info.cmap_data_address = p->cmap_regs_phys + 0x30;
633 display_info.disp_reg_address = p->control_regs_phys;
634 console_fb_info = &p->info;
636 #endif /* CONFIG_FB_COMPAT_XPMAC */
639 int __init control_init(void)
641 #ifndef CONFIG_FB_OF
642 struct device_node *dp;
644 dp = find_devices("control");
645 if (dp != 0)
646 control_of_init(dp);
647 #endif /* CONFIG_FB_OF */
648 return 0;
651 void __init control_of_init(struct device_node *dp)
653 struct fb_info_control *p;
654 unsigned long addr, size;
655 int i, bank1, bank2;
657 if(dp->n_addrs != 2) {
658 printk(KERN_ERR "expecting 2 address for control (got %d)", dp->n_addrs);
659 return;
661 p = kmalloc(sizeof(*p), GFP_ATOMIC);
662 if (p == 0)
663 return;
664 memset(p, 0, sizeof(*p));
666 /* Map in frame buffer and registers */
667 for (i = 0; i < dp->n_addrs; ++i) {
668 addr = dp->addrs[i].address;
669 size = dp->addrs[i].size;
670 if (size >= 0x800000) {
671 /* use the big-endian aperture (??) */
672 addr += 0x800000;
673 /* map at most 8MB for the frame buffer */
674 p->frame_buffer_phys = addr;
675 p->frame_buffer = __ioremap(addr, 0x800000, _PAGE_WRITETHRU);
676 } else {
677 p->control_regs_phys = addr;
678 p->control_regs = ioremap(addr, size);
681 p->cmap_regs_phys = 0xf301b000; /* XXX not in prom? */
682 p->cmap_regs = ioremap(p->cmap_regs_phys, 0x1000);
684 /* Work out which banks of VRAM we have installed. */
685 /* According to Andrew Fyfe <bandr@best.com>, the VRAM behaves like so: */
686 /* afyfe: observations from an 8500:
687 * - with 2M vram in bank 1, it appears at offsets 0, 2M and 4M
688 * - with 2M vram in bank 2, it appears only at offset 6M
689 * - with 4M vram, it appears only as a 4M block at offset 0.
692 /* We know there is something at 2M if there is something at 0M. */
693 out_8(&p->frame_buffer[0x200000], 0xa5);
694 out_8(&p->frame_buffer[0x200001], 0x38);
695 asm volatile("eieio; dcbi 0,%0" : : "r" (&p->frame_buffer[0x200000]) : "memory" );
697 out_8(&p->frame_buffer[0], 0x5a);
698 out_8(&p->frame_buffer[1], 0xc7);
699 asm volatile("eieio; dcbi 0,%0" : : "r" (&p->frame_buffer[0]) : "memory" );
701 bank1 = (in_8(&p->frame_buffer[0x000000]) == 0x5a)
702 && (in_8(&p->frame_buffer[0x000001]) == 0xc7);
703 bank2 = (in_8(&p->frame_buffer[0x200000]) == 0xa5)
704 && (in_8(&p->frame_buffer[0x200001]) == 0x38);
706 if(bank2 && !bank1)
707 printk(KERN_INFO "controlfb: Found memory at 2MB but not at 0! Please contact dan@debian.org\n");
709 if(!bank1) {
710 out_8(&p->frame_buffer[0x600000], 0xa5);
711 out_8(&p->frame_buffer[0x600001], 0x38);
712 asm volatile("eieio; dcbi 0,%0" : : "r" (&p->frame_buffer[0x600000]) : "memory" );
713 bank2 = (in_8(&p->frame_buffer[0x600000]) == 0xa5)
714 && (in_8(&p->frame_buffer[0x600001]) == 0x38);
715 /* If we don't have bank 1 installed, we hope we have bank 2 :-) */
716 p->control_use_bank2 = 1;
717 p->frame_buffer += 0x600000;
718 p->frame_buffer_phys += 0x600000;
721 p->total_vram = (bank1 + bank2) * 0x200000;
723 printk(KERN_INFO "controlfb: Memory bank 1 %s, bank 2 %s, total VRAM %dMB\n",
724 bank1 ? "present" : "absent", bank2 ? "present" : "absent",
725 2 * (bank1 + bank2));
727 init_control(p);
731 * Get the monitor sense value.
732 * Note that this can be called before calibrate_delay,
733 * so we can't use udelay.
735 static int read_control_sense(struct fb_info_control *p)
737 int sense;
739 out_le32(&p->control_regs->mon_sense.r, 7); /* drive all lines high */
740 __delay(200);
741 out_le32(&p->control_regs->mon_sense.r, 077); /* turn off drivers */
742 __delay(2000);
743 sense = (in_le32(&p->control_regs->mon_sense.r) & 0x1c0) << 2;
745 /* drive each sense line low in turn and collect the other 2 */
746 out_le32(&p->control_regs->mon_sense.r, 033); /* drive A low */
747 __delay(2000);
748 sense |= (in_le32(&p->control_regs->mon_sense.r) & 0xc0) >> 2;
749 out_le32(&p->control_regs->mon_sense.r, 055); /* drive B low */
750 __delay(2000);
751 sense |= ((in_le32(&p->control_regs->mon_sense.r) & 0x100) >> 5)
752 | ((in_le32(&p->control_regs->mon_sense.r) & 0x40) >> 4);
753 out_le32(&p->control_regs->mon_sense.r, 066); /* drive C low */
754 __delay(2000);
755 sense |= (in_le32(&p->control_regs->mon_sense.r) & 0x180) >> 7;
757 out_le32(&p->control_regs->mon_sense.r, 077); /* turn off drivers */
759 return sense;
762 /*********************** Various translation functions ***********************/
763 #if 1
764 /* This routine takes a user-supplied var, and picks the best vmode/cmode from it. */
765 static int control_var_to_par(struct fb_var_screeninfo *var,
766 struct fb_par_control *par, const struct fb_info *fb_info)
768 int xres = var->xres;
769 int yres = var->yres;
770 int bpp = var->bits_per_pixel;
771 struct fb_info_control *p = (struct fb_info_control *) fb_info;
774 * Get the video params out of 'var'. If a value doesn't fit, round it up,
775 * if it's too big, return -EINVAL.
777 * Suggestion: Round up in the following order: bits_per_pixel, xres,
778 * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
779 * bitfields, horizontal timing, vertical timing.
781 /* swiped by jonh from atyfb.c */
782 if (xres <= 640 && yres <= 480)
783 par->vmode = VMODE_640_480_67; /* 640x480, 67Hz */
784 else if (xres <= 640 && yres <= 870)
785 par->vmode = VMODE_640_870_75P; /* 640x870, 75Hz (portrait) */
786 else if (xres <= 800 && yres <= 600)
787 par->vmode = VMODE_800_600_75; /* 800x600, 75Hz */
788 else if (xres <= 832 && yres <= 624)
789 par->vmode = VMODE_832_624_75; /* 832x624, 75Hz */
790 else if (xres <= 1024 && yres <= 768)
791 par->vmode = VMODE_1024_768_75; /* 1024x768, 75Hz */
792 else if (xres <= 1152 && yres <= 870)
793 par->vmode = VMODE_1152_870_75; /* 1152x870, 75Hz */
794 else if (xres <= 1280 && yres <= 960)
795 par->vmode = VMODE_1280_960_75; /* 1280x960, 75Hz */
796 else if (xres <= 1280 && yres <= 1024)
797 par->vmode = VMODE_1280_1024_75; /* 1280x1024, 75Hz */
798 else {
799 printk(KERN_ERR "Bad x/y res in var_to_par\n");
800 return -EINVAL;
803 xres = control_reg_init[par->vmode-1]->hres;
804 yres = control_reg_init[par->vmode-1]->vres;
806 par->xres = xres;
807 par->yres = yres;
809 if (var->xres_virtual <= xres)
810 par->vxres = xres;
811 else if(var->xres_virtual > xres) {
812 par->vxres = xres;
813 } else /* NotReached at present */
814 par->vxres = (var->xres_virtual+7) & ~7;
816 if (var->yres_virtual <= yres)
817 par->vyres = yres;
818 else
819 par->vyres = var->yres_virtual;
821 if (var->xoffset > 0 || var->yoffset+yres > par->vyres) {
822 printk(KERN_ERR "Bad offsets in var_to_par\n");
823 return -EINVAL;
826 par->xoffset = (var->xoffset+7) & ~7;
827 par->yoffset = var->yoffset;
830 if (bpp <= 8)
831 par->cmode = CMODE_8;
832 else if (bpp <= 16)
833 par->cmode = CMODE_16;
834 else if (bpp <= 32)
835 par->cmode = CMODE_32;
836 else {
837 printk(KERN_ERR "Bad bpp in var_to_par\n");
838 return -EINVAL;
841 if (control_vram_reqd(par->vmode, par->cmode) > p->total_vram) {
842 printk(KERN_ERR "Too much VRAM required for vmode %d cmode %d.\n", par->vmode, par->cmode);
843 return -EINVAL;
846 /* Check if we know about the wanted video mode */
847 if (control_reg_init[par->vmode - 1] == NULL) {
848 printk(KERN_ERR "init is null in control_var_to_par().\n");
849 /* I'm not sure if control has any specific requirements -- */
850 /* if we have a regvals struct, we're good to go? */
851 return -EINVAL;
854 return 0;
856 #else
857 /* This routine takes a user-supplied var, and picks the best vmode/cmode from it. */
858 static int control_var_to_par(struct fb_var_screeninfo *var,
859 struct fb_par_control *par, const struct fb_info *fb_info)
861 struct fb_info_control *p = (struct fb_info_control *) fb_info;
863 if(mac_var_to_vmode(var, &par->vmode, &par->cmode) != 0)
864 return -EINVAL;
865 par->xres = par->vxres = vmode_attrs[par->vmode - 1].hres;
866 par->yres = par->vyres = vmode_attrs[par->vmode - 1].vres;
867 par->xoffset = par->yoffset = 0;
869 if (control_vram_reqd(par->vmode, par->cmode) > p->total_vram)
870 return -EINVAL;
872 /* Check if we know about the wanted video mode */
873 if(!control_reg_init[par->vmode-1]) {
874 /* I'm not sure if control has any specific requirements -- */
875 /* if we have a regvals struct, we're good to go? */
876 return -EINVAL;
878 return 0;
880 #endif
882 /*********** Convert hardware data in par to an fb_var_screeninfo ***********/
884 static void control_par_to_var(struct fb_par_control *par, struct fb_var_screeninfo *var)
886 struct control_regints *rv;
888 rv = (struct control_regints *) control_reg_init[par->vmode - 1]->regs;
890 memset(var, 0, sizeof(*var));
891 var->xres = control_reg_init[par->vmode - 1]->hres;
892 var->yres = control_reg_init[par->vmode - 1]->vres;
893 var->xres_virtual = par->vxres;
894 var->yres_virtual = par->vyres;
895 var->xoffset = par->xoffset;
896 var->yoffset = par->yoffset;
897 var->grayscale = 0;
899 if(par->cmode != CMODE_8 && par->cmode != CMODE_16 && par->cmode != CMODE_32) {
900 printk(KERN_ERR "Bad color mode in control_par_to_var()!\n");
901 par->cmode = CMODE_8;
903 switch(par->cmode) {
904 case CMODE_8:
905 var->bits_per_pixel = 8;
906 var->red.offset = 0;
907 var->red.length = 8;
908 var->green.offset = 0;
909 var->green.length = 8;
910 var->blue.offset = 0;
911 var->blue.length = 8;
912 var->transp.offset = 0;
913 var->transp.length = 0;
914 break;
915 case CMODE_16: /* RGB 555 */
916 var->bits_per_pixel = 16;
917 var->red.offset = 10;
918 var->red.length = 5;
919 var->green.offset = 5;
920 var->green.length = 5;
921 var->blue.offset = 0;
922 var->blue.length = 5;
923 var->transp.offset = 0;
924 var->transp.length = 0;
925 break;
926 case CMODE_32: /* RGB 888 */
927 var->bits_per_pixel = 32;
928 var->red.offset = 16;
929 var->red.length = 8;
930 var->green.offset = 8;
931 var->green.length = 8;
932 var->blue.offset = 0;
933 var->blue.length = 8;
934 var->transp.offset = 24;
935 var->transp.length = 8;
936 break;
938 var->red.msb_right = 0;
939 var->green.msb_right = 0;
940 var->blue.msb_right = 0;
941 var->transp.msb_right = 0;
942 var->nonstd = 0;
943 var->activate = 0;
944 var->height = -1;
945 var->width = -1;
946 var->vmode = FB_VMODE_NONINTERLACED;
948 var->left_margin = (rv->heblank - rv->hesync)
949 << ((par->vmode > 18) ? 2 : 1);
950 var->right_margin = (rv->hssync - rv->hsblank)
951 << ((par->vmode > 18) ? 2 : 1);
952 var->hsync_len = (rv->hperiod + 2 - rv->hssync + rv->hesync)
953 << ((par->vmode > 18) ? 2 : 1);
955 var->upper_margin = (rv->veblank - rv->vesync) >> 1;
956 var->lower_margin = (rv->vssync - rv->vsblank) >> 1;
957 var->vsync_len = (rv->vperiod - rv->vssync + rv->vesync) >> 1;
959 /* Acording to macmodes.c... */
960 if((par->vmode >= 9 && par->vmode <= 12) ||
961 (par->vmode >= 16 && par->vmode <= 18) ||
962 (par->vmode == 20))
964 var->sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT;
965 } else {
966 var->sync = 0; /* I suppose */
969 /* The reason these are both here: with my revised margin calculations, */
970 /* these SHOULD both give the same answer for each mode. Some day I */
971 /* will sit down and check the rest. Works perfectly for vmode 13. */
973 #if 0
974 /* jonh's pixclocks...*/
975 /* no long long support in the kernel :-( */
976 /* this splittig trick will work if xres > 232 */
977 var->pixclock = 1000000000/
978 (var->left_margin+var->xres+var->right_margin+var->hsync_len);
979 var->pixclock *= 1000;
980 var->pixclock /= vmode_attrs[par->vmode-1].vfreq*
981 (var->upper_margin+var->yres+var->lower_margin+var->vsync_len);
982 #else
983 /* danj's */
984 /* 10^12 * clock_params[0] / (3906400 * clock_params[1] * 2^clock_params[2]) */
985 /* (10^12 * clock_params[0] / (3906400 * clock_params[1])) >> clock_params[2] */
986 /* (255990.17 * clock_params[0] / clock_params[1]) >> clock_params[2] */
987 var->pixclock = 255990 * control_reg_init[par->vmode-1]->clock_params[0];
988 var->pixclock /= control_reg_init[par->vmode-1]->clock_params[1];
989 var->pixclock >>= control_reg_init[par->vmode-1]->clock_params[2];
990 #endif
993 static void control_par_to_fix(struct fb_par_control *par, struct fb_fix_screeninfo *fix,
994 struct fb_info_control *p)
996 memset(fix, 0, sizeof(*fix));
997 strcpy(fix->id, "control");
998 fix->mmio_start = p->control_regs_phys;
999 fix->mmio_len = sizeof(struct control_regs);
1000 fix->type = FB_TYPE_PACKED_PIXELS;
1002 fix->ypanstep = 1;
1004 fix->type_aux = 0;
1005 fix->ywrapstep = 0;
1006 fix->ypanstep = 0;
1007 fix->xpanstep = 0;
1010 fix->smem_start = (p->frame_buffer_phys
1011 + control_reg_init[par->vmode-1]->offset[par->cmode]);
1012 fix->smem_len = p->total_vram - control_reg_init[par->vmode-1]->offset[par->cmode];
1013 fix->visual = (par->cmode == CMODE_8) ?
1014 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
1015 fix->line_length = par->vxres << par->cmode;
1018 /* We never initialize any display except for p->disp.
1019 And p->disp is already memset to 0. So no memset here.
1020 [Found by Takashi Oe]
1022 static void control_par_to_display(struct fb_par_control *par,
1023 struct display *disp, struct fb_fix_screeninfo *fix, struct fb_info_control *p)
1025 /* memset(disp, 0, sizeof(*disp)); */
1026 disp->type = fix->type;
1027 disp->can_soft_blank = 1;
1028 disp->scrollmode = SCROLL_YNOMOVE | SCROLL_YNOPARTIAL;
1029 disp->ypanstep = fix->ypanstep;
1030 disp->ywrapstep = fix->ywrapstep;
1031 #if 0
1032 disp->type_aux = fix->type_aux;
1033 disp->cmap.red = NULL; /* ??? danj */
1034 disp->cmap.green = NULL;
1035 disp->cmap.blue = NULL;
1036 disp->cmap.transp = NULL;
1037 /* Yeah, I realize I just set 0 = 0. */
1038 #endif
1040 control_par_to_var(par, &disp->var);
1041 disp->screen_base = (char *) p->frame_buffer
1042 + control_reg_init[par->vmode-1]->offset[par->cmode];
1043 disp->visual = fix->visual;
1044 disp->line_length = fix->line_length;
1045 control_set_dispsw(disp, par->cmode, p);
1048 static void control_cfb16_revc(struct display *p, int xx, int yy)
1050 u8 *dest;
1051 int bytes = p->next_line, rows;
1053 dest = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p)*2;
1054 for (rows = fontheight(p); rows--; dest += bytes) {
1055 switch (fontwidth(p)) {
1056 case 16:
1057 ((u32 *)dest)[6] ^= 0x3def3def; ((u32 *)dest)[7] ^= 0x3def3def;
1058 /* FALL THROUGH */
1059 case 12:
1060 ((u32 *)dest)[4] ^= 0x3def3def; ((u32 *)dest)[5] ^= 0x3def3def;
1061 /* FALL THROUGH */
1062 case 8:
1063 ((u32 *)dest)[2] ^= 0x3def3def; ((u32 *)dest)[3] ^= 0x3def3def;
1064 /* FALL THROUGH */
1065 case 4:
1066 ((u32 *)dest)[0] ^= 0x3def3def; ((u32 *)dest)[1] ^= 0x3def3def;
1071 static void control_cfb32_revc(struct display *p, int xx, int yy)
1073 u8 *dest;
1074 int bytes = p->next_line, rows;
1076 dest = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p) * 4;
1077 for (rows = fontheight(p); rows--; dest += bytes) {
1078 switch (fontwidth(p)) {
1079 case 16:
1080 ((u32 *)dest)[12] ^= 0x0f0f0f0f; ((u32 *)dest)[13] ^= 0x0f0f0f0f;
1081 ((u32 *)dest)[14] ^= 0x0f0f0f0f; ((u32 *)dest)[15] ^= 0x0f0f0f0f;
1082 /* FALL THROUGH */
1083 case 12:
1084 ((u32 *)dest)[8] ^= 0x0f0f0f0f; ((u32 *)dest)[9] ^= 0x0f0f0f0f;
1085 ((u32 *)dest)[10] ^= 0x0f0f0f0f; ((u32 *)dest)[11] ^= 0x0f0f0f0f;
1086 /* FALL THROUGH */
1087 case 8:
1088 ((u32 *)dest)[4] ^= 0x0f0f0f0f; ((u32 *)dest)[5] ^= 0x0f0f0f0f;
1089 ((u32 *)dest)[6] ^= 0x0f0f0f0f; ((u32 *)dest)[7] ^= 0x0f0f0f0f;
1090 /* FALL THROUGH */
1091 case 4:
1092 ((u32 *)dest)[0] ^= 0x0f0f0f0f; ((u32 *)dest)[1] ^= 0x0f0f0f0f;
1093 ((u32 *)dest)[2] ^= 0x0f0f0f0f; ((u32 *)dest)[3] ^= 0x0f0f0f0f;
1094 /* FALL THROUGH */
1099 static struct display_switch control_cfb16 = {
1100 setup: fbcon_cfb16_setup,
1101 bmove: fbcon_cfb16_bmove,
1102 clear: fbcon_cfb16_clear,
1103 putc: fbcon_cfb16_putc,
1104 putcs: fbcon_cfb16_putcs,
1105 revc: control_cfb16_revc,
1106 clear_margins: fbcon_cfb16_clear_margins,
1107 fontwidthmask: FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
1110 static struct display_switch control_cfb32 = {
1111 setup: fbcon_cfb32_setup,
1112 bmove: fbcon_cfb32_bmove,
1113 clear: fbcon_cfb32_clear,
1114 putc: fbcon_cfb32_putc,
1115 putcs: fbcon_cfb32_putcs,
1116 revc: control_cfb32_revc,
1117 clear_margins: fbcon_cfb32_clear_margins,
1118 fontwidthmask: FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
1122 static void control_set_dispsw(struct display *disp, int cmode, struct fb_info_control *p)
1124 switch (cmode) {
1125 #ifdef FBCON_HAS_CFB8
1126 case CMODE_8:
1127 disp->dispsw = &fbcon_cfb8;
1128 break;
1129 #endif
1130 #ifdef FBCON_HAS_CFB16
1131 case CMODE_16:
1132 disp->dispsw = &control_cfb16;
1133 disp->dispsw_data = p->fbcon_cmap.cfb16;
1134 break;
1135 #endif
1136 #ifdef FBCON_HAS_CFB32
1137 case CMODE_32:
1138 disp->dispsw = &control_cfb32;
1139 disp->dispsw_data = p->fbcon_cmap.cfb32;
1140 break;
1141 #endif
1142 default:
1143 disp->dispsw = &fbcon_dummy;
1144 break;
1148 static void control_init_info(struct fb_info *info, struct fb_info_control *p)
1150 strcpy(info->modename, "control");
1151 info->node = -1; /* ??? danj */
1152 info->fbops = &controlfb_ops;
1153 info->disp = &p->display;
1154 strcpy(info->fontname, fontname);
1155 info->changevar = NULL;
1156 info->switch_con = &controlfb_switch;
1157 info->updatevar = &controlfb_updatevar;
1158 info->blank = &controlfb_blank;
1161 /* Parse user speficied options (`video=controlfb:') */
1162 void __init control_setup(char *options)
1164 char *this_opt;
1166 if (!options || !*options)
1167 return;
1169 for (this_opt = strtok(options, ","); this_opt;
1170 this_opt = strtok(NULL, ",")) {
1171 if (!strncmp(this_opt, "font:", 5)) {
1172 char *p;
1173 int i;
1175 p = this_opt +5;
1176 for (i = 0; i < sizeof(fontname) - 1; i++)
1177 if (!*p || *p == ' ' || *p == ',')
1178 break;
1179 memcpy(fontname, this_opt + 5, i);
1180 fontname[i] = 0;
1182 if (!strncmp(this_opt, "vmode:", 6)) {
1183 int vmode = simple_strtoul(this_opt+6, NULL, 0);
1184 if (vmode > 0 && vmode <= VMODE_MAX)
1185 default_vmode = vmode;
1186 } else if (!strncmp(this_opt, "cmode:", 6)) {
1187 int depth = simple_strtoul(this_opt+6, NULL, 0);
1188 switch (depth) {
1189 case CMODE_8:
1190 case CMODE_16:
1191 case CMODE_32:
1192 default_cmode = depth;
1193 break;
1194 case 8:
1195 default_cmode = CMODE_8;
1196 break;
1197 case 15:
1198 case 16:
1199 default_cmode = CMODE_16;
1200 break;
1201 case 24:
1202 case 32:
1203 default_cmode = CMODE_32;
1204 break;
1210 #if 0
1211 static int controlfb_pan_display(struct fb_var_screeninfo *var,
1212 struct controlfb_par *par,
1213 const struct fb_info *fb_info)
1216 * Pan (or wrap, depending on the `vmode' field) the display using the
1217 * `xoffset' and `yoffset' fields of the `var' structure.
1218 * If the values don't fit, return -EINVAL.
1221 FUNCID;
1223 return 0;
1226 #endif